/***************************************************************************** * * This program is free software ; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * $Id: mjpeg.c 603 2006-01-19 13:00:33Z picard $ * * The Core Pocket Media Player * Copyright (c) 2004-2005 Gabor Kovacs * ****************************************************************************/ #include "../common/common.h" #include "mjpeg.h" #define WEBMJEG_READSIZE 2048 #define VLC_BITS 9 typedef struct blockindex { uint8_t quant; uint8_t comp; uint8_t ac; uint8_t dc; } blockindex; typedef struct mjpeg { codecidct Codec; bool_t error; int sos_no; int restart_interval; int restart_count; idct_block_t* blockptr; // bitstream bitstream s; vlc* v[2][4]; int vsize[2][4]; uint8_t quant[4][64]; int hblock[4]; int vblock[4]; int compid[4]; int quantidx[4]; int Ss; int Se; int Al; int Ah; int blocks; int mb_width; int mb_height; int last_dc[4]; int comp; bool_t progressive; int startstate; blockindex *indexend; blockindex index[4*2*2]; uint8_t zigzag[64]; IDCT_BLOCK_DECLARE } mjpeg; static const uint8_t zigzag[64] = { 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63 }; static const uint8_t dc_luminance[] = { 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }; static const uint8_t dc_chrominance[] = { 0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }; static const uint8_t ac_luminance[] = { 0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 125, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa }; static const uint8_t ac_chrominance[] = { 0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 119, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa }; static const uint8_t* const default_dht[4] = { dc_luminance, dc_chrominance, ac_luminance, ac_chrominance }; DECLARE_BITINIT DECLARE_BITLOAD #undef bitload_pos #define bitload_pos(p, bitpos) \ bitpos -= 8; \ if (bitpos >= 0) { \ int bits = (p)->bits; \ const uint8_t* bitptr = (p)->bitptr; \ do { \ bits = (bits << 8) | *bitptr; \ if (*bitptr++ == 0xFF) \ { \ while (*bitptr==0xFF && bitptr<(p)->bitend) \ bitptr++; \ if (*bitptr==0) \ ++bitptr; \ } \ bitpos -= 8; \ } while (bitpos>=0); \ (p)->bits = bits; \ (p)->bitptr = bitptr; \ } \ bitpos += 8; static NOINLINE int NotSupported(mjpeg* p) { if (!p->error) { p->error = 1; ShowError(p->Codec.Node.Class,MJPEG_ID,MJPEG_NOT_SUPPORTED); } return ERR_NOT_SUPPORTED; } static NOINLINE bool_t VLC(vlc** tab, int* size, const uint8_t *data) { uint32_t huffcode[256]; uint16_t huffdata[256]; int i,j,k,n,code; code = 0; k = 0; for (i=1;i<=16;i++) { n = data[i]; for (j=0;js,16) - 2; while (len >= 65 && !biteof(&p->s)) { int comp,i; bitload(&p->s); if (bitget(&p->s,4) != 0) return NotSupported(p); // 16bit prec comp = bitget(&p->s, 4); if (comp >= 4) return ERR_INVALID_DATA; for (i=0;i<64;i++) { bitload(&p->s); p->quant[comp][i] = (uint8_t)bitget(&p->s,8); } len -= 65; } return ERR_NONE; } static int DHT(mjpeg* p) { uint8_t data[17+256]; int len = bitget(&p->s,16)-2; while (len>=17 && !biteof(&p->s)) { int n,i; int comp,idx; bitload(&p->s); comp = bitget(&p->s,4); if (comp >= 2) break; idx = bitget(&p->s,4); if (idx >= 4) break; n = 0; for (i=1;i<=16;i++) { bitload(&p->s); data[i] = (uint8_t) bitget(&p->s,8); n += data[i]; } len -= 17+n; if (len<0 || n>256) break; for (i=0;is); data[17+i] = (uint8_t) bitget(&p->s,8); } if (!VLC(&p->v[comp][idx],&p->vsize[comp][idx],data)) return ERR_NONE; } return ERR_NONE; } static int DRI(mjpeg* p) { if (bitget(&p->s,16) == 4) { bitload(&p->s); p->restart_interval = bitget(&p->s,16); } return ERR_NONE; } static int SOF(mjpeg* p,bool_t progressive) { int len,i,width,height,mode; p->progressive = progressive; len = bitget(&p->s,16); bitload(&p->s); if (bitget(&p->s,8) != 8) // 8bit/component return NotSupported(p); height = bitget(&p->s,16); bitload(&p->s); width = bitget(&p->s,16); p->comp = bitget(&p->s,8); if (p->comp != 3 && p->comp != 1) // 3 or 1 components return NotSupported(p); if (len != 8+3*p->comp) return ERR_INVALID_DATA; p->blocks = 0; for (i=0;icomp;i++) { bitload(&p->s); p->compid[i] = bitget(&p->s,8); p->hblock[i] = bitget(&p->s,4); p->vblock[i] = bitget(&p->s,4); p->quantidx[i] = bitget(&p->s,8); if (p->quantidx[i] >= 4) return ERR_INVALID_DATA; if (i>0 && (p->hblock[i]!=1 || p->vblock[i]!=1)) return NotSupported(p); p->blocks += p->hblock[i] * p->vblock[i]; } p->indexend = p->index + p->blocks; switch (16*p->vblock[0]+p->hblock[0]) { case 0x11: mode = PF_YUV444; p->mb_width = (width + 7) >> 3; p->mb_height = (height + 7) >> 3; break; case 0x12: mode = PF_YUV422; p->mb_width = (width + 15) >> 4; p->mb_height = (height + 7) >> 3; break; case 0x22: mode = PF_YUV420; p->mb_width = (width + 15) >> 4; p->mb_height = (height + 15) >> 4; break; default: return NotSupported(p); } return CodecIDCTSetFormat(&p->Codec,mode|PF_YUV_PC,width,height,width,height,p->Codec.In.Format.Format.Video.Aspect); } static NOINLINE void scan(mjpeg* p) { idct* IDCT = p->Codec.IDCT.Ptr; int x,y,bitpos; blockindex* i; p->restart_count = p->restart_interval; bitpos = p->s.bitpos; for (y=0;ymb_height;++y) for (x=0;xmb_width;++x) { IDCT->Process(IDCT,x,y); for (i=p->index;i!=p->indexend;i++) { int len,val,n; idct_block_t* block = p->blockptr; uint8_t* quant = p->quant[i->quant]; vlc* tab = p->v[0][i->dc]; if (biteof(&p->s)) break; ClearBlock(block); vlcget2_pos(val,tab,&p->s,bitpos,n,255,VLC_BITS); if (val) { bitload_pos(&p->s,bitpos); bitgetx_pos(&p->s,bitpos,val,n); } val = val * quant[0] + p->last_dc[i->comp]; p->last_dc[i->comp] = val; block[0] = (idct_block_t)val; tab = p->v[1][i->ac]; len = 1; for (;;) { vlcget2_pos(val,tab,&p->s,bitpos,n,255,VLC_BITS); if (!val) break; if (val == 0xF0) { len += 16; if (len>=64) break; } else { n = val >> 4; val &= 15; len += n; bitload_pos(&p->s,bitpos); bitgetx_pos(&p->s,bitpos,val,n); n = len & 63; val *= quant[n]; n = p->zigzag[n]; block[n] = (idct_block_t)val; if (++len>=64) break; } } IDCT->Intra8x8(IDCT,block,len,IDCTSCAN_ZIGZAG); } if (p->comp == 1) { idct_block_t* block = p->blockptr; ClearBlock(block); block[0] = 1024; IDCT->Intra8x8(IDCT,block,1,IDCTSCAN_ZIGZAG); ClearBlock(block); block[0] = 1024; IDCT->Intra8x8(IDCT,block,1,IDCTSCAN_ZIGZAG); } if (p->restart_interval && !--p->restart_count) { p->restart_count = p->restart_interval; p->last_dc[0] = p->last_dc[1] = p->last_dc[2] = 1024; bitbytealign_pos(&p->s,bitpos); bitflush_pos(&p->s,bitpos,16); bitload_pos(&p->s,bitpos); } } p->s.bitpos = bitpos; } static int SOS(mjpeg* p) { const uint8_t *pos; int i,j,comp,len,result; len = bitget(&p->s,16); comp = bitget(&p->s,8); if (len != 6+2*comp) return ERR_INVALID_DATA; if (p->comp != comp) return NotSupported(p); j=0; for (i=0;is); if (p->compid[i] != bitget(&p->s,8)) return NotSupported(p); dc = bitget(&p->s,4); ac = bitget(&p->s,4); if (dc >= 4 || ac >= 4) return ERR_INVALID_DATA; for (n=p->hblock[i]*p->vblock[i];n;--n,++j) { p->index[j].quant=(uint8_t)p->quantidx[i]; p->index[j].comp=(uint8_t)i; p->index[j].ac=(uint8_t)ac; p->index[j].dc=(uint8_t)dc; } } bitload(&p->s); p->Ss = bitget(&p->s,8); // Ss p->Se = bitget(&p->s,8); // Se bitload(&p->s); p->Ah = bitget(&p->s,4); // Ah p->Al = bitget(&p->s,4); // Al p->last_dc[0] = p->last_dc[1] = p->last_dc[2] = 1024; if (!p->sos_no) { result = p->Codec.IDCT.Ptr->FrameStart(p->Codec.IDCT.Ptr,0,NULL,0,-1,-1,0,0); if (result != ERR_NONE) return result; p->Codec.Show = 0; } pos = bitbytepos(&p->s); bitinit(&p->s,pos,bitendptr(&p->s)-pos); ++p->sos_no; scan(p); return ERR_NONE; } static int APP(mjpeg* p) { int n = bitget(&p->s,16); if (n>6) bitflush(&p->s,n*8-16); return ERR_NONE; } static int Frame(mjpeg* p, const uint8_t* ptr, int num) { int Result = ERR_NONE; int code,v; if (p->Codec.State.DropLevel) return p->Codec.IDCT.Ptr->Null(p->Codec.IDCT.Ptr,NULL,0); p->sos_no = 0; p->progressive = 0; do { for (;;) { if (num < 2) { code = 0xD9; break; } v = *ptr++; --num; if ((v == 0xFF) && (*ptr >= 0xC0) && (*ptr <= 0xFE)) { code = *ptr++; --num; break; } } if (code == 0xD9) // end of image break; bitinit(&p->s,ptr,num); bitload(&p->s); switch(code) { case 0xD8: // start of image p->restart_interval = 0; break; case 0xDD: // define restart interval Result = DRI(p); break; case 0xDB: // define quantization tables Result = DQT(p); break; case 0xC4: // define huffman tables Result = DHT(p); break; case 0xC0: // baseline, huffman Result = SOF(p,0); break; case 0xC2: // progressive, huffman if (!p->error) { p->error = 1; ShowError(p->Codec.Node.Class,MJPEG_ID,MJPEG_PROGRESSIVE); } Result = ERR_NOT_SUPPORTED; //todo... Result = SOF(p,1); break; case 0xDA: // start of scan if (!p->sos_no || p->progressive) Result = SOS(p); continue; case 0xE0: case 0xE1: case 0xE2: case 0xE3: case 0xE4: case 0xE5: case 0xE6: case 0xE7: case 0xE8: case 0xE9: case 0xEA: case 0xEB: case 0xEC: case 0xED: case 0xEE: case 0xEF: Result = APP(p); break; case 0xC1: // extended sequential, huffman case 0xC3: // lossless, huffman case 0xC5: // differential sequential, huffman case 0xC6: // differential progressive, huffman case 0xC7: // differential lossless, huffman case 0xC9: // extended sequential, arithmetic case 0xCA: // progressive, arithmetic case 0xCB: // lossless, arithmetic case 0xCD: // differential sequential, arithmetic case 0xCE: // differential progressive, arithmetic case 0xCF: // differential lossless, arithmetic case 0xC8: // reserved Result = NotSupported(p); break; } v = bitbytepos(&p->s) - ptr; ptr += v; num -= v; } while (Result == ERR_NONE); if (p->sos_no) p->Codec.IDCT.Ptr->FrameEnd(p->Codec.IDCT.Ptr); return Result; } static int Flush(mjpeg* p) { p->startstate = 0; return ERR_NONE; } static bool_t FindNext(mjpeg* p) { const uint8_t* Ptr = p->Codec.Buffer.Data; int Len = p->Codec.Buffer.WritePos; int Pos = p->Codec.FrameEnd; for (;Posstartstate) { case -1: Valid = 0; break; case 1: Valid = ch == 0xD8; break; case 3: Valid = (ch >= 0xC0) && (ch <= 0xFE); break; default: Valid = ch == 0xFF; break; } if (!Valid) p->startstate = 0; else if (++p->startstate == 4) { p->Codec.FrameEnd = Pos-3; p->startstate = -1; return 1; } } p->Codec.FrameEnd = Pos; return 0; } static int UpdateInput(mjpeg* p) { int i,j; int Result = ERR_NONE; p->error = 0; IDCT_BLOCK_PREPARE(p,p->blockptr); memcpy(p->zigzag,zigzag,sizeof(zigzag)); for (i=0;i<2;++i) for (j=0;j<4;++j) vlcdone(&p->v[i][j],&p->vsize[i][j]); if (p->Codec.In.Format.Type == PACKET_VIDEO) { Result = CodecIDCTSetFormat(&p->Codec,PF_YUV422|PF_YUV_PC,p->Codec.In.Format.Format.Video.Width,p->Codec.In.Format.Format.Video.Height, p->Codec.In.Format.Format.Video.Width,p->Codec.In.Format.Format.Video.Height, p->Codec.In.Format.Format.Video.Aspect); if (Result == ERR_NONE) { for (i=0;i<2;++i) for (j=0;j<2;++j) if (!VLC(&p->v[i][j],&p->vsize[i][j],default_dht[i*2+j])) Result = ERR_OUT_OF_MEMORY; if (p->Codec.In.Format.PacketRate.Num>0) p->Codec.FrameTime = Scale(TICKSPERSEC,p->Codec.In.Format.PacketRate.Den,p->Codec.In.Format.PacketRate.Num); else p->Codec.FrameTime = 0; } } return Result; } static int Create(mjpeg* p) { p->Codec.MinCount = 1; p->Codec.Frame = (codecidctframe)Frame; p->Codec.UpdateInput = (nodefunc)UpdateInput; p->Codec.FindNext = (codecidctnext)FindNext; p->Codec.Flush = (nodefunc)Flush; return ERR_NONE; } static const nodedef MJPEG = { sizeof(mjpeg), MJPEG_ID, CODECIDCT_CLASS, PRI_DEFAULT, (nodecreate)Create, NULL, }; //------------------------------------------------------------------------------------------- static const nodedef JPEG = { 0, // parent size JPEG_ID, RAWIMAGE_CLASS, PRI_DEFAULT, }; //--------------------------------------------------------------------------------------------- typedef struct webmjpeg { format_base Format; } webmjpeg; static int WEBMJPEGInit(rawaudio* p) { format_stream* s = Format_AddStream(&p->Format,sizeof(format_stream)); if (s) { PacketFormatClear(&s->Format); s->Format.Type = PACKET_VIDEO; s->Format.Format.Video.Pixel.Flags = PF_FOURCC|PF_FRAGMENTED; s->Format.Format.Video.Pixel.FourCC = FOURCC('J','P','E','G'); s->PacketBurst = 1; s->Fragmented = 1; s->DisableDrop = 1; Format_PrepairStream(&p->Format,s); } p->Format.HeaderLoaded = 1; p->Format.ProcessMinBuffer = 0; p->Format.ReadSize = WEBMJEG_READSIZE; return ERR_NONE; } static int WEBMJPEGPacket(rawaudio* p, format_reader* Reader, format_packet* Packet) { format_stream* Stream = p->Format.Streams[0]; if (Reader->FilePos<=0) Packet->RefTime = 0; else Packet->RefTime = TIME_UNKNOWN; Packet->Data = Reader->ReadAsRef(Reader,-WEBMJEG_READSIZE); Packet->Stream = Stream; if (Stream->LastTime < TIME_UNKNOWN) Stream->LastTime = TIME_UNKNOWN; return ERR_NONE; } static int WEBMJPEGSeek(rawaudio* p, tick_t Time, filepos_t FilePos,bool_t PrevKey) { if (Time==0 || FilePos==0) p->Format.Reader->Seek(p->Format.Reader,0,SEEK_SET); return ERR_NONE; } static int WEBMJPEGCreate(rawaudio* p) { p->Format.Init = (fmtfunc)WEBMJPEGInit; p->Format.Seek = (fmtseek)WEBMJPEGSeek; p->Format.ReadPacket = (fmtreadpacket)WEBMJPEGPacket; return ERR_NONE; } static const nodedef WEBMJPEG = { sizeof(webmjpeg), WEB_MJPEG_ID, FORMATBASE_CLASS, PRI_DEFAULT, (nodecreate)WEBMJPEGCreate, NULL, }; //--------------------------------------------------------------------------------------------- void MJPEG_Init() { NodeRegisterClass(&MJPEG); NodeRegisterClass(&JPEG); NodeRegisterClass(&WEBMJPEG); } void MJPEG_Done() { NodeUnRegisterClass(MJPEG_ID); NodeUnRegisterClass(JPEG_ID); NodeUnRegisterClass(WEB_MJPEG_ID); }