From ebcf2908e18aae2b2638b97356c65474e5b33db4 Mon Sep 17 00:00:00 2001 From: Guenter Bartsch Date: Mon, 19 Aug 2002 17:40:41 +0000 Subject: try to make ffmpeg handle mpeg streams, try to handle buffers that contain more than one frame CVS patchset: 2486 CVS date: 2002/08/19 17:40:41 --- src/libffmpeg/xine_decoder.c | 448 ++++++++++++++++++++++++++++++++----------- 1 file changed, 333 insertions(+), 115 deletions(-) diff --git a/src/libffmpeg/xine_decoder.c b/src/libffmpeg/xine_decoder.c index 89c446231..470cba149 100644 --- a/src/libffmpeg/xine_decoder.c +++ b/src/libffmpeg/xine_decoder.c @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * - * $Id: xine_decoder.c,v 1.50 2002/07/30 20:13:09 miguelfreitas Exp $ + * $Id: xine_decoder.c,v 1.51 2002/08/19 17:40:41 guenter Exp $ * * xine decoder plugin using ffmpeg * @@ -46,9 +46,12 @@ #define LOG */ +#define SLICE_BUFFER_SIZE (1194 * 1024) + typedef struct ff_decoder_s { video_decoder_t video_decoder; + xine_t *xine; vo_instance_t *video_out; int video_step; int decoder_ok; @@ -62,6 +65,16 @@ typedef struct ff_decoder_s { AVPicture av_picture; AVCodecContext context; + + /* mpeg sequence header parsing, stolen from libmpeg2 */ + + uint32_t shift; + uint8_t *chunk_buffer; + uint8_t *chunk_ptr; + uint8_t code; + + int is_continous; + } ff_decoder_t; #define VIDEOBUFSIZE 128*1024 @@ -82,7 +95,175 @@ static int ff_can_handle (video_decoder_t *this_gen, int buf_type) { buf_type == BUF_VIDEO_RV10 || /* PIX_FMT_YUV410P must be supported to enable svq1 */ /* buf_type == BUF_VIDEO_SORENSON_V1 || */ - buf_type == BUF_VIDEO_JPEG); + buf_type == BUF_VIDEO_JPEG || + buf_type == BUF_VIDEO_MPEG); +} + +static void init_codec (ff_decoder_t *this, AVCodec *codec) { + + /* force (width % 8 == 0), otherwise there will be + * display problems with Xv. + */ + this->bih.biWidth = (this->bih.biWidth + 7) & (~7); + + memset(&this->context, 0, sizeof(this->context)); + this->context.width = this->bih.biWidth; + this->context.height = this->bih.biHeight; + + if (avcodec_open (&this->context, codec) < 0) { + printf ("ffmpeg: couldn't open decoder\n"); + return; + } + + this->decoder_ok = 1; + this->video_out->open (this->video_out); + + /* needed to play streams generated by MS ISO MPEG4 codec. + Michael Niedermayer explained: + M$ "ISO MPEG4" uses illegal vlc code combinations, a ISO MPEG4 compliant + decoder which support error resilience should handle them like errors. + */ + if (this->illegal_vlc) + this->context.error_resilience=-1; + + if (this->buf) + free (this->buf); + + this->buf = xine_xmalloc (VIDEOBUFSIZE); + this->bufsize = VIDEOBUFSIZE; + + this->skipframes = 0; +} + +static void find_sequence_header (ff_decoder_t *this, + uint8_t * current, uint8_t * end){ + + uint8_t code; + + if (this->decoder_ok) + return; + + while (current != end) { + + uint32_t shift; + uint8_t *chunk_ptr; + uint8_t *limit; + uint8_t byte; + + code = this->code; + + /* copy chunk */ + + shift = this->shift; + chunk_ptr = this->chunk_ptr; + limit = current + (this->chunk_buffer + SLICE_BUFFER_SIZE - chunk_ptr); + if (limit > end) + limit = end; + + while (1) { + + byte = *current++; + if (shift != 0x00000100) { + shift = (shift | byte) << 8; + *chunk_ptr++ = byte; + if (current < limit) + continue; + if (current == end) { + this->chunk_ptr = chunk_ptr; + this->shift = shift; + current = 0; + break; + } else { + /* we filled the chunk buffer without finding a start code */ + this->code = 0xb4; /* sequence_error_code */ + this->chunk_ptr = this->chunk_buffer; + break; + } + } + this->code = byte; + this->chunk_ptr = this->chunk_buffer; + this->shift = 0xffffff00; + break; + } + + if (current == NULL) + return ; + +#ifdef LOG + printf ("ffmpeg: looking for sequence header... %02x\n", code); +#endif + + /* mpeg2_stats (code, this->chunk_buffer); */ + + if (code == 0xb3) { /* sequence_header_code */ + + AVCodec *codec = NULL; + int width, height, frame_rate_code; + +#ifdef LOG + printf ("ffmpeg: found sequence header !\n"); +#endif + + height = (this->chunk_buffer[0] << 16) | (this->chunk_buffer[1] << 8) + | this->chunk_buffer[2]; + + width = ((height >> 12) + 15) & ~15; + height = ((height & 0xfff) + 15) & ~15; + + xine_log (this->xine, XINE_LOG_FORMAT, + "ffmpeg: frame size is %d x %d\n", + width, height); + + this->bih.biWidth = width; + this->bih.biHeight = height; + + frame_rate_code = this->chunk_buffer[3] & 15; + + switch (frame_rate_code) { + case 1: /* 23.976 fps */ + this->video_step = 3913; + break; + case 2: /* 24 fps */ + this->video_step = 3750; + break; + case 3: /* 25 fps */ + this->video_step = 3600; + break; + case 4: /* 29.97 fps */ + this->video_step = 3003; + break; + case 5: /* 30 fps */ + this->video_step = 3000; + break; + case 6: /* 50 fps */ + this->video_step = 1800; + break; + case 7: /* 59.94 fps */ + this->video_step = 1525; + break; + case 8: /* 60 fps */ + this->video_step = 1509; + break; + default: + printf ("ffmpeg: invalid/unknown frame rate code : %d \n", + frame_rate_code); + this->video_step = 3000; + } + + /* + * init codec + */ + + codec = avcodec_find_decoder (CODEC_ID_MPEG1VIDEO); + if (!codec) { + printf ("avcodec_find_decoder (CODEC_ID_MPEG1VIDEO) failed.\n"); + abort(); + } + + this->is_continous = 1; + init_codec (this, codec); + } + } } static void ff_init (video_decoder_t *this_gen, vo_instance_t *video_out) { @@ -92,6 +273,12 @@ static void ff_init (video_decoder_t *this_gen, vo_instance_t *video_out) { this->video_out = video_out; this->decoder_ok = 0; this->buf = NULL; + + this->shift = 0xffffff00; + this->code = 0xb4; + this->chunk_ptr = this->chunk_buffer; + + this->is_continous = 0; } static void ff_decode_data (video_decoder_t *this_gen, buf_element_t *buf) { @@ -103,8 +290,12 @@ static void ff_decode_data (video_decoder_t *this_gen, buf_element_t *buf) { buf->type, buf, buf->decoder_flags); #endif - if (buf->decoder_flags & BUF_FLAG_PREVIEW) + if (buf->decoder_flags & BUF_FLAG_PREVIEW) { + if ( (buf->type & 0xFFFF0000) == BUF_VIDEO_MPEG ) { + find_sequence_header (this, buf->content, buf->content+buf->size); + } return; + } if (buf->decoder_flags & BUF_FLAG_HEADER) { @@ -194,7 +385,6 @@ static void ff_decode_data (video_decoder_t *this_gen, buf_element_t *buf) { this->buf = malloc( VIDEOBUFSIZE ); this->bufsize = VIDEOBUFSIZE; - this->size = 0; this->skipframes = 0; @@ -214,138 +404,165 @@ static void ff_decode_data (video_decoder_t *this_gen, buf_element_t *buf) { if (buf->decoder_flags & BUF_FLAG_FRAMERATE) this->video_step = buf->decoder_info[0]; - if (buf->decoder_flags & BUF_FLAG_FRAME_END) { + if ( (buf->decoder_flags & BUF_FLAG_FRAME_END) || this->is_continous) { vo_frame_t *img; int got_picture, len, y; uint8_t *dy, *du, *dv, *sy, *su, *sv; + int offset; - /* decode video frame */ + /* decode video frame(s) */ /* skip decoding b frames if too late */ this->context.hurry_up = (this->skipframes > 2) ? 1:0; - len = avcodec_decode_video (&this->context, &this->av_picture, - &got_picture, this->buf, - this->size); -#ifdef ARCH_X86 - emms_c (); + offset = 0; + while (this->size>0) { + len = avcodec_decode_video (&this->context, &this->av_picture, + &got_picture, &this->buf[offset], + this->size); + if (len<0) { + printf ("ffmpeg: error decompressing frame\n"); + this->size=0; + return; + } + + this->size -= len; + offset += len; + + if (!got_picture) { + printf ("ffmpeg: didn't get a picture, got %d bytes left\n", + this->size); + + if (this->size>0) + memmove (this->buf, &this->buf[offset], this->size); + + return; + } + +#ifdef LOG + printf ("ffmpeg: got a picture\n"); #endif - switch(this->context.aspect_ratio_info) { - case FF_ASPECT_SQUARE: - ratio = XINE_ASPECT_RATIO_SQUARE; - break; - case FF_ASPECT_4_3_625: - case FF_ASPECT_4_3_525: - ratio = XINE_ASPECT_RATIO_4_3; - break; - case FF_ASPECT_16_9_625: - case FF_ASPECT_16_9_525: - ratio = XINE_ASPECT_RATIO_ANAMORPHIC; - break; - default: - ratio = XINE_ASPECT_RATIO_DONT_TOUCH; - } +#ifdef ARCH_X86 + emms_c (); +#endif - img = this->video_out->get_frame (this->video_out, - /* this->av_picture.linesize[0], */ - this->bih.biWidth, - this->bih.biHeight, - ratio, - IMGFMT_YV12, - VO_BOTH_FIELDS); - - img->pts = buf->pts; - img->duration = this->video_step; - if (len<0 || this->skipframes) { - if( !this->skipframes ) - printf ("ffmpeg: error decompressing frame\n"); - img->bad_frame = 1; - } else { - img->bad_frame = 0; - - dy = img->base[0]; - du = img->base[1]; - dv = img->base[2]; - sy = this->av_picture.data[0]; - su = this->av_picture.data[1]; - sv = this->av_picture.data[2]; - - for (y=0; ybih.biHeight; y++) { + switch(this->context.aspect_ratio_info) { + case FF_ASPECT_SQUARE: + ratio = XINE_ASPECT_RATIO_SQUARE; + break; + case FF_ASPECT_4_3_625: + case FF_ASPECT_4_3_525: + ratio = XINE_ASPECT_RATIO_4_3; + break; + case FF_ASPECT_16_9_625: + case FF_ASPECT_16_9_525: + ratio = XINE_ASPECT_RATIO_ANAMORPHIC; + break; + default: + ratio = XINE_ASPECT_RATIO_DONT_TOUCH; + } + + img = this->video_out->get_frame (this->video_out, + /* this->av_picture.linesize[0], */ + this->bih.biWidth, + this->bih.biHeight, + ratio, + IMGFMT_YV12, + VO_BOTH_FIELDS); + + img->pts = buf->pts; + buf->pts = 0; + img->duration = this->video_step; + if (len<0 || this->skipframes) { + if( !this->skipframes ) + printf ("ffmpeg: error decompressing frame\n"); + img->bad_frame = 1; + } else { + img->bad_frame = 0; - xine_fast_memcpy (dy, sy, this->bih.biWidth); + dy = img->base[0]; + du = img->base[1]; + dv = img->base[2]; + sy = this->av_picture.data[0]; + su = this->av_picture.data[1]; + sv = this->av_picture.data[2]; - dy += img->pitches[0]; + for (y=0; ybih.biHeight; y++) { + + xine_fast_memcpy (dy, sy, this->bih.biWidth); + + dy += img->pitches[0]; - sy += this->av_picture.linesize[0]; - } + sy += this->av_picture.linesize[0]; + } - for (y=0; y<(this->bih.biHeight/2); y++) { - - if (this->context.pix_fmt != PIX_FMT_YUV444P) { - - xine_fast_memcpy (du, su, this->bih.biWidth/2); - xine_fast_memcpy (dv, sv, this->bih.biWidth/2); - - } else { - - int x; - uint8_t *src; - uint8_t *dst; + for (y=0; y<(this->bih.biHeight/2); y++) { - /* subsample */ - - src = su; dst = du; - for (x=0; x<(this->bih.biWidth/2); x++) { - *dst = *src; - dst++; - src += 2; - } - src = sv; dst = dv; - for (x=0; x<(this->bih.biWidth/2); x++) { - *dst = *src; - dst++; - src += 2; + if (this->context.pix_fmt != PIX_FMT_YUV444P) { + + xine_fast_memcpy (du, su, this->bih.biWidth/2); + xine_fast_memcpy (dv, sv, this->bih.biWidth/2); + + } else { + + int x; + uint8_t *src; + uint8_t *dst; + + /* subsample */ + + src = su; dst = du; + for (x=0; x<(this->bih.biWidth/2); x++) { + *dst = *src; + dst++; + src += 2; + } + src = sv; dst = dv; + for (x=0; x<(this->bih.biWidth/2); x++) { + *dst = *src; + dst++; + src += 2; + } + } - - } - - du += img->pitches[1]; - dv += img->pitches[2]; - if (this->context.pix_fmt != PIX_FMT_YUV420P) { - su += 2*this->av_picture.linesize[1]; - sv += 2*this->av_picture.linesize[2]; - } else { - su += this->av_picture.linesize[1]; - sv += this->av_picture.linesize[2]; + du += img->pitches[1]; + dv += img->pitches[2]; + + if (this->context.pix_fmt != PIX_FMT_YUV420P) { + su += 2*this->av_picture.linesize[1]; + sv += 2*this->av_picture.linesize[2]; + } else { + su += this->av_picture.linesize[1]; + sv += this->av_picture.linesize[2]; + } } - } - if (img->copy) { - int height = img->height; - uint8_t *src[3]; - - src[0] = img->base[0]; - src[1] = img->base[1]; - src[2] = img->base[2]; - - while ((height -= 16) >= 0) { - img->copy(img, src); - src[0] += 16 * img->pitches[0]; - src[1] += 8 * img->pitches[1]; - src[2] += 8 * img->pitches[2]; + if (img->copy) { + int height = img->height; + uint8_t *src[3]; + + src[0] = img->base[0]; + src[1] = img->base[1]; + src[2] = img->base[2]; + + while ((height -= 16) >= 0) { + img->copy(img, src); + src[0] += 16 * img->pitches[0]; + src[1] += 8 * img->pitches[1]; + src[2] += 8 * img->pitches[2]; + } } } - } - this->skipframes = img->draw(img); - if( this->skipframes < 0 ) - this->skipframes = 0; - img->free(img); + this->skipframes = img->draw(img); + if( this->skipframes < 0 ) + this->skipframes = 0; + img->free(img); - this->size = 0; + } } } } @@ -355,10 +572,7 @@ static void ff_flush (video_decoder_t *this_gen) { } static void ff_reset (video_decoder_t *this_gen) { - ff_decoder_t *this = (ff_decoder_t *) this_gen; - - /* invalidate buffer on seek */ - this->size = 0; + /* seems to handle seeking quite nicelly without any code here */ } static void ff_close (video_decoder_t *this_gen) { @@ -441,6 +655,10 @@ video_decoder_t *init_video_decoder_plugin (int iface_version, xine_t *xine) { this->video_decoder.dispose = ff_dispose; this->size = 0; + this->xine = xine; + + this->chunk_buffer = xine_xmalloc (SLICE_BUFFER_SIZE + 4); + this->illegal_vlc = xine->config->register_bool (xine->config, "codec.ffmpeg_illegal_vlc", 1, _("allow illegal vlc codes in mpeg4 streams"), NULL, NULL, NULL); -- cgit v1.2.3