diff --git a/linphone/mediastreamer2/build/win32native/alldescs.h b/linphone/mediastreamer2/build/win32native/alldescs.h index 84fe06067ea4acfa2b1950eb3c6ac3c9aa1ba4f1..a83db96eead2eb4276d538b73e2097d2e71daa84 100755 --- a/linphone/mediastreamer2/build/win32native/alldescs.h +++ b/linphone/mediastreamer2/build/win32native/alldescs.h @@ -29,6 +29,8 @@ extern MSFilterDesc ms_snow_enc_desc; extern MSFilterDesc ms_snow_dec_desc; extern MSFilterDesc ms_theora_enc_desc; extern MSFilterDesc ms_theora_dec_desc; +extern MSFilterDesc ms_mjpeg_enc_desc; +extern MSFilterDesc ms_mjpeg_dec_desc; extern MSFilterDesc ms_size_conv_desc; extern MSFilterDesc ms_pix_conv_desc; extern MSFilterDesc ms_join_desc; @@ -69,6 +71,8 @@ MSFilterDesc * ms_filter_descs[]={ &ms_snow_dec_desc, &ms_theora_enc_desc, &ms_theora_dec_desc, +&ms_mjpeg_enc_desc, +&ms_mjpeg_dec_desc, &ms_size_conv_desc, &ms_pix_conv_desc, &ms_join_desc, diff --git a/linphone/mediastreamer2/include/mediastreamer2/allfilters.h b/linphone/mediastreamer2/include/mediastreamer2/allfilters.h index 36436656464543cd7e4100a03ff00dc482334392..eb6440506fc829d95764d56905dea5958fc6cf00 100644 --- a/linphone/mediastreamer2/include/mediastreamer2/allfilters.h +++ b/linphone/mediastreamer2/include/mediastreamer2/allfilters.h @@ -86,7 +86,9 @@ typedef enum MSFilterId{ MS_DSCAP_ID, MS_AQ_READ_ID, MS_AQ_WRITE_ID, - MS_EQUALIZER_ID + MS_EQUALIZER_ID, + MS_JPEG_DEC_ID, + MS_JPEG_ENC_ID, } MSFilterId; diff --git a/linphone/mediastreamer2/src/videodec.c b/linphone/mediastreamer2/src/videodec.c index fb01d656e0f90cb55ab8802063950fe4fa8e686e..f324ea2696440bb60fc475a50eec30ab45c24efb 100644 --- a/linphone/mediastreamer2/src/videodec.c +++ b/linphone/mediastreamer2/src/videodec.c @@ -212,6 +212,301 @@ static mblk_t * parse_snow_header(DecState *s,mblk_t *inm){ } } +struct jpeghdr { + //unsigned int tspec:8; /* type-specific field */ + unsigned int off:32; /* fragment byte offset */ + uint8_t type; /* id of jpeg decoder params */ + uint8_t q; /* quantization factor (or table id) */ + uint8_t width; /* frame width in 8 pixel blocks */ + uint8_t height; /* frame height in 8 pixel blocks */ +}; + +struct jpeghdr_rst { + uint16_t dri; + unsigned int f:1; + unsigned int l:1; + unsigned int count:14; +}; + +struct jpeghdr_qtable { + uint8_t mbz; + uint8_t precision; + uint16_t length; +}; + + +u_char lum_dc_codelens[] = { + 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, +}; + +u_char lum_dc_symbols[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, +}; + +u_char lum_ac_codelens[] = { + 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d, +}; + +u_char lum_ac_symbols[] = { + 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, +}; + +u_char chm_dc_codelens[] = { + 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, +}; + +u_char chm_dc_symbols[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, +}; + +u_char chm_ac_codelens[] = { + 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77, +}; + +u_char chm_ac_symbols[] = { + 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, +}; + +u_char * +MakeQuantHeader(u_char *p, u_char *qt, int tableNo, int table_len) +{ + *p++ = 0xff; + *p++ = 0xdb; /* DQT */ + *p++ = 0; /* length msb */ + *p++ = table_len+3; /* length lsb */ + *p++ = tableNo; + memcpy(p, qt, table_len); + return (p + table_len); +} + +u_char * +MakeHuffmanHeader(u_char *p, u_char *codelens, int ncodes, + u_char *symbols, int nsymbols, int tableNo, + int tableClass) +{ + *p++ = 0xff; + *p++ = 0xc4; /* DHT */ + *p++ = 0; /* length msb */ + *p++ = 3 + ncodes + nsymbols; /* length lsb */ + *p++ = (tableClass << 4) | tableNo; + memcpy(p, codelens, ncodes); + p += ncodes; + memcpy(p, symbols, nsymbols); + p += nsymbols; + return (p); +} + +u_char * +MakeDRIHeader(u_char *p, u_short dri) { + *p++ = 0xff; + *p++ = 0xdd; /* DRI */ + *p++ = 0x0; /* length msb */ + *p++ = 4; /* length lsb */ + *p++ = dri >> 8; /* dri msb */ + *p++ = dri & 0xff; /* dri lsb */ + return (p); +} + +/* + * Arguments: + * type, width, height: as supplied in RTP/JPEG header + * lqt, cqt: quantization tables as either derived from + * the Q field using MakeTables() or as specified + * in section 4.2. + * dri: restart interval in MCUs, or 0 if no restarts. + * + * p: pointer to return area + * + * Return value: + * The length of the generated headers. + * + * Generate a frame and scan headers that can be prepended to the + * RTP/JPEG data payload to produce a JPEG compressed image in + * interchange format (except for possible trailing garbage and + * absence of an EOI marker to terminate the scan). + */ +int MakeHeaders(u_char *p, int type, int w, int h, u_char *lqt, + u_char *cqt, unsigned table_len, u_short dri) +{ + u_char *start = p; + + /* convert from blocks to pixels */ + w <<= 3; + h <<= 3; + + *p++ = 0xff; + *p++ = 0xd8; /* SOI */ + + if (table_len>64) + { + p = MakeQuantHeader(p, lqt, 0, table_len/2); + p = MakeQuantHeader(p, cqt, 1, table_len/2); + } + else + { + p = MakeQuantHeader(p, lqt, 0, table_len); + //p = MakeQuantHeader(p, lqt, 1, table_len); + } + if (dri != 0) + p = MakeDRIHeader(p, dri); + + *p++ = 0xff; + *p++ = 0xc0; /* SOF */ + *p++ = 0; /* length msb */ + *p++ = 17; /* length lsb */ + *p++ = 8; /* 8-bit precision */ + *p++ = h >> 8; /* height msb */ + *p++ = h; /* height lsb */ + *p++ = w >> 8; /* width msb */ + *p++ = w; /* wudth lsb */ + *p++ = 3; /* number of components */ + *p++ = 0; /* comp 0 */ + if (type == 0) + *p++ = 0x21; /* hsamp = 2, vsamp = 1 */ + else + *p++ = 0x22; /* hsamp = 2, vsamp = 2 */ + *p++ = 0; /* quant table 0 */ + *p++ = 1; /* comp 1 */ + *p++ = 0x11; /* hsamp = 1, vsamp = 1 */ + *p++ = table_len <= 64 ? 0x00 : 0x01; //1 /* quant table 1 */ + *p++ = 2; /* comp 2 */ + *p++ = 0x11; /* hsamp = 1, vsamp = 1 */ + *p++ = table_len <= 64 ? 0x00 : 0x01; //1; /* quant table 1 */ + p = MakeHuffmanHeader(p, lum_dc_codelens, + sizeof(lum_dc_codelens), + lum_dc_symbols, + sizeof(lum_dc_symbols), 0, 0); + p = MakeHuffmanHeader(p, lum_ac_codelens, + sizeof(lum_ac_codelens), + lum_ac_symbols, + sizeof(lum_ac_symbols), 0, 1); + p = MakeHuffmanHeader(p, chm_dc_codelens, + sizeof(chm_dc_codelens), + chm_dc_symbols, + sizeof(chm_dc_symbols), 1, 0); + p = MakeHuffmanHeader(p, chm_ac_codelens, + sizeof(chm_ac_codelens), + chm_ac_symbols, + sizeof(chm_ac_symbols), 1, 1); + + *p++ = 0xff; + *p++ = 0xda; /* SOS */ + *p++ = 0; /* length msb */ + *p++ = 12; /* length lsb */ + *p++ = 3; /* 3 components */ + *p++ = 0; /* comp 0 */ + *p++ = 0; /* huffman table 0 */ + *p++ = 1; /* comp 1 */ + *p++ = 0x11; /* huffman table 1 */ + *p++ = 2; /* comp 2 */ + *p++ = 0x11; /* huffman table 1 */ + *p++ = 0; /* first DCT coeff */ + *p++ = 63; /* last DCT coeff */ + *p++ = 0; /* sucessive approx. */ + + return (p - start); +}; + +void MakeTables(int q, u_char *lqt, u_char *cqt); + +mblk_t * +read_rfc2435_header(DecState *s,mblk_t *inm) +{ + if (msgdsize(inm) >= sizeof(struct jpeghdr)) { + struct jpeghdr *hdr = (struct jpeghdr *)inm->b_rptr; + uint32_t off = ntohl(*(uint32_t*)inm->b_rptr); + uint16_t dri=0; + uint16_t table_len=0; + int len=0; + + mblk_t *headers=NULL; + + inm->b_rptr += sizeof(struct jpeghdr); + if (hdr->type>63){ + struct jpeghdr_rst *rsthdr = (struct jpeghdr_rst *)inm->b_rptr; + dri = ntohs(rsthdr->dri); + inm->b_rptr += sizeof(struct jpeghdr_rst); + } + + if (off==0){ + if (hdr->q>=128){ + inm->b_rptr++; /* MBZ */ + inm->b_rptr++; /* Precision */ + table_len = ntohs(*((uint16_t*)(inm->b_rptr))); + inm->b_rptr++; /* len */ + inm->b_rptr++; /* len */ + headers = allocb(495 + table_len*2 + (dri > 0 ? 6 : 0), 0); + len = MakeHeaders(headers->b_rptr, hdr->type, hdr->width, hdr->height, + inm->b_rptr, inm->b_rptr+table_len/2, table_len, dri); + inm->b_rptr += table_len; + headers->b_wptr += len; + }else{ + uint8_t lqt_cqt[128]; + MakeTables(hdr->q, lqt_cqt, lqt_cqt+64); + table_len=128; + headers = allocb(495 + table_len + (dri > 0 ? 6 : 0), 0); + len = MakeHeaders(headers->b_rptr, hdr->type, hdr->width, hdr->height, + lqt_cqt, lqt_cqt+64, table_len, dri); + headers->b_wptr += len; + } + } + + if (headers!=NULL) + { + /* prepend headers to JPEG RTP data */ + if (mblk_get_marker_info(inm)) + mblk_set_marker_info(headers, TRUE); + headers->b_cont=inm; + msgpullup(headers, -1); + return headers; + } + } else { + freemsg(inm); + inm=NULL; + } + return inm; +} + static mblk_t *get_as_yuvmsg(MSFilter *f, DecState *s, AVFrame *orig){ AVCodecContext *ctx=&s->av_context; @@ -243,6 +538,7 @@ static void dec_process_frame(MSFilter *f, mblk_t *inm){ if (f->desc->id==MS_H263_DEC_ID) inm=skip_rfc2429_header(inm); else if (f->desc->id==MS_H263_OLD_DEC_ID) inm=skip_rfc2190_header(inm); else if (s->codec==CODEC_ID_SNOW && s->input==NULL) inm=parse_snow_header(s,inm); + else if (s->codec==CODEC_ID_MJPEG && f->desc->id==MS_JPEG_DEC_ID) inm=read_rfc2435_header(s,inm); if (inm){ /* accumulate the video packet until we have the rtp markbit*/ if (s->input==NULL){ @@ -345,12 +641,28 @@ MSFilterDesc ms_mpeg4_dec_desc={ methods }; +MSFilterDesc ms_jpeg_dec_desc={ + MS_JPEG_DEC_ID, + "MSJpegDec", + N_("A RTP/JPEG decoder using ffmpeg library"), + MS_FILTER_DECODER, + "JPEG", + 1, + 1, + dec_mjpeg_init, + dec_preprocess, + dec_process, + dec_postprocess, + dec_uninit, + methods +}; + MSFilterDesc ms_mjpeg_dec_desc={ MS_MJPEG_DEC_ID, "MSMJpegDec", N_("A MJPEG decoder using ffmpeg library"), MS_FILTER_DECODER, - "JPEG", + "MJPEG", 1, 1, dec_mjpeg_init, @@ -428,12 +740,28 @@ MSFilterDesc ms_mpeg4_dec_desc={ .methods= methods }; +MSFilterDesc ms_jpeg_dec_desc={ + .id=MS_JPEG_DEC_ID, + .name="MSJpegDec", + .text="A RTP/MJEPG decoder using ffmpeg library", + .category=MS_FILTER_DECODER, + .enc_fmt="JPEG", + .ninputs=1, + .noutputs=1, + .init=dec_mjpeg_init, + .preprocess=dec_preprocess, + .process=dec_process, + .postprocess=dec_postprocess, + .uninit=dec_uninit, + .methods= methods +}; + MSFilterDesc ms_mjpeg_dec_desc={ .id=MS_MJPEG_DEC_ID, .name="MSMJpegDec", .text="A MJEPG decoder using ffmpeg library", .category=MS_FILTER_DECODER, - .enc_fmt="JPEG", + .enc_fmt="MJPEG", .ninputs=1, .noutputs=1, .init=dec_mjpeg_init, @@ -465,5 +793,9 @@ MSFilterDesc ms_snow_dec_desc={ MS_FILTER_DESC_EXPORT(ms_mpeg4_dec_desc) MS_FILTER_DESC_EXPORT(ms_h263_dec_desc) MS_FILTER_DESC_EXPORT(ms_h263_old_dec_desc) -MS_FILTER_DESC_EXPORT(ms_mjpeg_dec_desc) MS_FILTER_DESC_EXPORT(ms_snow_dec_desc) + +/* decode JPEG image with RTP/jpeg headers */ +MS_FILTER_DESC_EXPORT(ms_jpeg_dec_desc) +/* decode JPEG image with jpeg headers */ +MS_FILTER_DESC_EXPORT(ms_mjpeg_dec_desc) diff --git a/linphone/mediastreamer2/src/videoenc.c b/linphone/mediastreamer2/src/videoenc.c index 67ddb88ad3cc578094edb52123d349be2661282f..64437a753831726d8c79b5ab35e0ae7be4b74bbb 100644 --- a/linphone/mediastreamer2/src/videoenc.c +++ b/linphone/mediastreamer2/src/videoenc.c @@ -196,9 +196,26 @@ static void enc_snow_init(MSFilter *f){ enc_init(f,CODEC_ID_SNOW); } +static void enc_mjpeg_init(MSFilter *f){ + enc_init(f,CODEC_ID_MJPEG); +} + static void prepare(EncState *s){ AVCodecContext *c=&s->av_context; avcodec_get_context_defaults(c); + if (s->codec==CODEC_ID_MJPEG) + { + ms_message("Codec bitrate set to %i",c->bit_rate); + c->width = s->vsize.width; + c->height = s->vsize.height; + c->time_base.num = 1; + c->time_base.den = (int)s->fps; + c->gop_size=(int)s->fps*5; /*emit I frame every 5 seconds*/ + c->pix_fmt=PIX_FMT_YUVJ420P; + s->comp_buf=allocb(c->bit_rate*2,0); + return; + } + /* put codec parameters */ c->bit_rate=(float)s->maxbr*0.7; c->bit_rate_tolerance=s->fps!=1?(float)c->bit_rate/(s->fps-1):c->bit_rate; @@ -286,6 +303,8 @@ static void enc_preprocess(MSFilter *f){ prepare_mpeg4(s); else if (s->codec==CODEC_ID_SNOW){ /**/ + }else if (s->codec==CODEC_ID_MJPEG){ + /**/ }else { ms_error("Unsupported codec id %i",s->codec); return; @@ -440,6 +459,268 @@ static uint8_t *get_psc(uint8_t *begin,uint8_t *end, int packet_size){ return ret; } +/* + * Table K.1 from JPEG spec. + */ +static const int jpeg_luma_quantizer[64] = { + 16, 11, 10, 16, 24, 40, 51, 61, + 12, 12, 14, 19, 26, 58, 60, 55, + 14, 13, 16, 24, 40, 57, 69, 56, + 14, 17, 22, 29, 51, 87, 80, 62, + 18, 22, 37, 56, 68, 109, 103, 77, + 24, 35, 55, 64, 81, 104, 113, 92, + 49, 64, 78, 87, 103, 121, 120, 101, + 72, 92, 95, 98, 112, 100, 103, 99 +}; + +/* + * Table K.2 from JPEG spec. + */ +static const int jpeg_chroma_quantizer[64] = { + 17, 18, 24, 47, 99, 99, 99, 99, + 18, 21, 26, 66, 99, 99, 99, 99, + 24, 26, 56, 99, 99, 99, 99, 99, + 47, 66, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99 +}; + +/* + * Call MakeTables with the Q factor and two u_char[64] return arrays + */ +void +MakeTables(int q, u_char *lqt, u_char *cqt) +{ + int i; + int factor = q; + + if (q < 1) factor = 1; + if (q > 99) factor = 99; + if (q < 50) + q = 5000 / factor; + else + q = 200 - factor*2; + + for (i=0; i < 64; i++) { + int lq = (jpeg_luma_quantizer[i] * q + 50) / 100; + int cq = (jpeg_chroma_quantizer[i] * q + 50) / 100; + + /* Limit the quantizers to 1 <= q <= 255 */ + if (lq < 1) lq = 1; + else if (lq > 255) lq = 255; + lqt[i] = lq; + + if (cq < 1) cq = 1; + else if (cq > 255) cq = 255; + cqt[i] = cq; + } +} + + +struct jpeghdr { + //unsigned int tspec:8; /* type-specific field */ + unsigned int off:32; /* fragment byte offset */ + uint8_t type; /* id of jpeg decoder params */ + uint8_t q; /* quantization factor (or table id) */ + uint8_t width; /* frame width in 8 pixel blocks */ + uint8_t height; /* frame height in 8 pixel blocks */ +}; + +struct jpeghdr_rst { + uint16_t dri; + unsigned int f:1; + unsigned int l:1; + unsigned int count:14; +}; + +struct jpeghdr_qtable { + uint8_t mbz; + uint8_t precision; + uint16_t length; +}; + +#define RTP_JPEG_RESTART 0x40 + +/* Procedure SendFrame: + * + * Arguments: + * start_seq: The sequence number for the first packet of the current + * frame. + * ts: RTP timestamp for the current frame + * ssrc: RTP SSRC value + * jpeg_data: Huffman encoded JPEG scan data + * len: Length of the JPEG scan data + * type: The value the RTP/JPEG type field should be set to + * typespec: The value the RTP/JPEG type-specific field should be set + * to + * width: The width in pixels of the JPEG image + * height: The height in pixels of the JPEG image + * dri: The number of MCUs between restart markers (or 0 if there + * are no restart markers in the data + * q: The Q factor of the data, to be specified using the Independent + * JPEG group's algorithm if 1 <= q <= 99, specified explicitly + * with lqt and cqt if q >= 128, or undefined otherwise. + * lqt: The quantization table for the luminance channel if q >= 128 + * cqt: The quantization table for the chrominance channels if + * q >= 128 + * + * Return value: + * the sequence number to be sent for the first packet of the next + * frame. + * + * The following are assumed to be defined: + * + * PACKET_SIZE - The size of the outgoing packet + * send_packet(u_int8 *data, int len) - Sends the packet to the network + */ + +void mjpeg_fragment_and_send(MSFilter *f,EncState *s,mblk_t *frame, uint32_t timestamp, + uint8_t type, uint8_t typespec, int dri, + uint8_t q, mblk_t *lqt, mblk_t *cqt) { + struct jpeghdr jpghdr; + struct jpeghdr_rst rsthdr; + struct jpeghdr_qtable qtblhdr; + int bytes_left = msgdsize(frame); + int data_len; + + mblk_t *packet; + + /* Initialize JPEG header + */ + //jpghdr.tspec = typespec; + jpghdr.off = 0; + jpghdr.type = type | ((dri != 0) ? RTP_JPEG_RESTART : 0); + jpghdr.q = q; + jpghdr.width = s->vsize.width / 8; + jpghdr.height = s->vsize.height / 8; + + /* Initialize DRI header + */ + if (dri != 0) { + rsthdr.dri = htons(dri); + rsthdr.f = 1; /* This code does not align RIs */ + rsthdr.l = 1; + rsthdr.count = 0x3fff; + } + + /* Initialize quantization table header + */ + if (q >= 128) { + qtblhdr.mbz = 0; + qtblhdr.precision = 0; /* This code uses 8 bit tables only */ + qtblhdr.length = htons(msgdsize(lqt)+msgdsize(cqt)); /* 2 64-byte tables */ + } + + while (bytes_left > 0) { + packet = allocb(s->mtu, 0); + + jpghdr.off = htonl(jpghdr.off); + memcpy(packet->b_wptr, &jpghdr, sizeof(jpghdr)); + jpghdr.off = ntohl(jpghdr.off); + packet->b_wptr += sizeof(jpghdr); + + if (dri != 0) { + memcpy(packet->b_wptr, &rsthdr, sizeof(rsthdr)); + packet->b_wptr += sizeof(rsthdr); + } + + if (q >= 128 && jpghdr.off == 0) { + memcpy(packet->b_wptr, &qtblhdr, sizeof(qtblhdr)); + packet->b_wptr += sizeof(qtblhdr); + if (msgdsize(lqt)){ + memcpy(packet->b_wptr, lqt->b_rptr, msgdsize(lqt)); + packet->b_wptr += msgdsize(lqt); + } + if (msgdsize(cqt)){ + memcpy(packet->b_wptr, cqt->b_rptr, msgdsize(cqt)); + packet->b_wptr += msgdsize(cqt); + } + } + + data_len = s->mtu - (packet->b_wptr - packet->b_rptr); + if (data_len >= bytes_left) { + data_len = bytes_left; + mblk_set_marker_info(packet,TRUE); + } + + memcpy(packet->b_wptr, frame->b_rptr + jpghdr.off, data_len); + packet->b_wptr=packet->b_wptr + data_len; + + mblk_set_timestamp_info(packet,timestamp); + ms_queue_put(f->outputs[0],packet); + + jpghdr.off += data_len; + bytes_left -= data_len; + } +} + +static int find_marker(uint8_t **pbuf_ptr, uint8_t *buf_end){ + + uint8_t *buf_ptr; + unsigned int v, v2; + int val; + + buf_ptr = *pbuf_ptr; + while (buf_ptr < buf_end) { + v = *buf_ptr++; + v2 = *buf_ptr; + if ((v == 0xff) && (v2 >= 0xc0) && (v2 <= 0xfe) && buf_ptr < buf_end) { + val = *buf_ptr++; + *pbuf_ptr = buf_ptr; + return val; + } + } + val = -1; + return val; +} + +mblk_t *skip_jpeg_headers(mblk_t *full_frame, mblk_t **lqt, mblk_t **cqt){ + int err; + uint8_t *pbuf_ptr=full_frame->b_rptr; + uint8_t *buf_end=full_frame->b_wptr; + + ms_message("image size: %i)", buf_end-pbuf_ptr); + + *lqt=NULL; + *cqt=NULL; + + err = find_marker(&pbuf_ptr, buf_end); + while (err!=-1) + { + ms_message("marker found: %x (offset from beginning%i)", err, pbuf_ptr-full_frame->b_rptr); + if (err==0xdb) + { + /* copy DQT table */ + int len = ntohs(*(uint16_t*)(pbuf_ptr)); + if (*lqt==NULL) + { + mblk_t *_lqt = allocb(len-3, 0); + memcpy(_lqt->b_rptr, pbuf_ptr+3, len-3); + _lqt->b_wptr += len-3; + *lqt = _lqt; + //*cqt = dupb(*lqt); + } + else + { + mblk_t *_cqt = allocb(len-3, 0); + memcpy(_cqt->b_rptr, pbuf_ptr+3, len-3); + _cqt->b_wptr += len-3; + *cqt = _cqt; + } + } + if (err==0xda) + { + uint16_t *bistream=(uint16_t *)pbuf_ptr; + uint16_t len = ntohs(*bistream); + full_frame->b_rptr = pbuf_ptr+len; + } + err = find_marker(&pbuf_ptr, buf_end); + } + return full_frame; +} + static void split_and_send(MSFilter *f, EncState *s, mblk_t *frame){ uint8_t *lastpsc; uint8_t *psc; @@ -450,6 +731,20 @@ static void split_and_send(MSFilter *f, EncState *s, mblk_t *frame){ mpeg4_fragment_and_send(f,s,frame,timestamp); return; } + else if (s->codec==CODEC_ID_MJPEG) + { + mblk_t *lqt=NULL; + mblk_t *cqt=NULL; + skip_jpeg_headers(frame, &lqt, &cqt); + mjpeg_fragment_and_send(f,s,frame,timestamp, + 1, /* 420? */ + 0, + 0, /* dri ?*/ + 255, /* q */ + lqt, + cqt); + return; + } ms_debug("processing frame of size %i",frame->b_wptr-frame->b_rptr); if (f->desc->id==MS_H263_ENC_ID){ @@ -662,6 +957,22 @@ MSFilterDesc ms_snow_enc_desc={ methods }; +MSFilterDesc ms_mjpeg_enc_desc={ + MS_JPEG_ENC_ID, + "MSJpegEnc", + N_("A RTP/MJPEG encoder using ffmpeg library."), + MS_FILTER_ENCODER, + "JPEG", + 1, /*MS_YUV420P is assumed on this input */ + 1, + enc_mjpeg_init, + enc_preprocess, + enc_process, + enc_postprocess, + enc_uninit, + methods +}; + #else MSFilterDesc ms_h263_enc_desc={ @@ -732,6 +1043,22 @@ MSFilterDesc ms_snow_enc_desc={ .methods=methods }; +MSFilterDesc ms_mjpeg_enc_desc={ + .id=MS_MJPEG_ENC_ID, + .name="MSMJpegEnc", + .text=N_("A MJPEG encoder using ffmpeg library."), + .category=MS_FILTER_ENCODER, + .enc_fmt="JPEG", + .ninputs=1, /*MS_YUV420P is assumed on this input */ + .noutputs=1, + .init=enc_mjpeg_init, + .preprocess=enc_preprocess, + .process=enc_process, + .postprocess=enc_postprocess, + .uninit=enc_uninit, + .methods=methods +}; + #endif void __register_ffmpeg_encoders_if_possible(void){ @@ -744,5 +1071,9 @@ void __register_ffmpeg_encoders_if_possible(void){ } if (avcodec_find_encoder(CODEC_ID_SNOW)) ms_filter_register(&ms_snow_enc_desc); + if (avcodec_find_encoder(CODEC_ID_MJPEG)) + { + ms_filter_register(&ms_mjpeg_enc_desc); + } }