diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/libffmpeg/mpeg_parser.c | 297 | ||||
-rw-r--r-- | src/libffmpeg/mpeg_parser.h | 71 | ||||
-rw-r--r-- | src/libffmpeg/video_decoder.c | 376 |
3 files changed, 501 insertions, 243 deletions
diff --git a/src/libffmpeg/mpeg_parser.c b/src/libffmpeg/mpeg_parser.c new file mode 100644 index 000000000..6baf40b71 --- /dev/null +++ b/src/libffmpeg/mpeg_parser.c @@ -0,0 +1,297 @@ +/* + * Copyright (C) 2001-2004 the xine project + * + * This file is part of xine, a free video player. + * + * xine 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. + * + * xine 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 + * + * Simple MPEG-ES parser/framer by Thibaut Mattern (tmattern@noos.fr) + * based on libmpeg2 decoder. + * + * $Id: mpeg_parser.c,v 1.1 2004/07/18 00:50:02 tmattern Exp $ + */ +#define LOG_MODULE "mpeg_parser" +#define LOG_VERBOSE +/* +#define LOG +*/ +#include "mpeg_parser.h" +#include "xine_internal.h" + +/* mpeg frame rate table from lavc */ +static const int frame_rate_tab[][2] = { + { 0, 0}, + {24000, 1001}, + { 24, 1}, + { 25, 1}, + {30000, 1001}, + { 30, 1}, + { 50, 1}, + {60000, 1001}, + { 60, 1}, + /* Xing's 15fps: (9) */ + { 15, 1}, + /* libmpeg3's "Unofficial economy rates": (10-13) */ + { 5, 1}, + { 10, 1}, + { 12, 1}, + { 15, 1}, + { 0, 0}, +}; + +void mpeg_parser_init (mpeg_parser_t *parser) +{ + mpeg_parser_reset(parser); +} + +void mpeg_parser_reset (mpeg_parser_t *parser) +{ + parser->shift = 0xffffff00; + parser->is_sequence_needed = 1; + parser->in_slice = 0; + parser->chunk_ptr = parser->chunk_buffer; + parser->chunk_start = parser->chunk_buffer; + parser->buffer_size = 0; + parser->code = 0xb4; + parser->picture_coding_type = 0; + parser->width = 0; + parser->height = 0; + parser->rate_code = 0; + parser->aspect_ratio_info = 0; + parser->frame_duration = 0; + parser->is_mpeg1 = 0; + parser->has_sequence = 0; + parser->frame_aspect_ratio = 0.0; +} + +static void parse_header_picture (mpeg_parser_t *parser, uint8_t * buffer) +{ + parser->picture_coding_type = (buffer [1] >> 3) & 7; +} + +static double get_aspect_ratio(mpeg_parser_t *parser) +{ + double ratio; + double mpeg1_pel_ratio[16] = {1.0 /* forbidden */, + 1.0, 0.6735, 0.7031, 0.7615, 0.8055, 0.8437, 0.8935, 0.9157, + 0.9815, 1.0255, 1.0695, 1.0950, 1.1575, 1.2015, 1.0 /*reserved*/ }; + + if( !parser->is_mpeg1 ) { + /* these hardcoded values are defined on mpeg2 standard for + * aspect ratio. other values are reserved or forbidden. */ + switch (parser->aspect_ratio_info) { + case 2: + ratio = 4.0 / 3.0; + break; + case 3: + ratio = 16.0 / 9.0; + break; + case 4: + ratio = 2.11 / 1.0; + break; + case 1: + default: + ratio = (double)parser->width / (double)parser->height; + break; + } + } else { + /* mpeg1 constants refer to pixel aspect ratio */ + ratio = (double)parser->width / (double)parser->height; + ratio /= mpeg1_pel_ratio[parser->aspect_ratio_info]; + } + + return ratio; +} + +static int parse_chunk (mpeg_parser_t *parser, int code, uint8_t *buffer, int len) +{ + int is_frame_done; + int next_code = parser->code; + + /* wait for sequence_header_code */ + if (parser->is_sequence_needed) { + if (code != 0xb3) { + lprintf("waiting for sequence header\n"); + parser->chunk_ptr = parser->chunk_buffer; + return 0; + } + } + + is_frame_done = parser->in_slice && ((!next_code) || (next_code == 0xb7)); + + if (is_frame_done) + parser->in_slice = 0; + + switch (code) { + case 0x00: /* picture_start_code */ + + parse_header_picture (parser, buffer); + + parser->in_slice = 1; + + switch (parser->picture_coding_type) { + case B_TYPE: + lprintf ("B-Frame\n"); + break; + + case P_TYPE: + lprintf ("P-Frame\n"); + break; + + case I_TYPE: + lprintf ("I-Frame\n"); + break; + } + break; + + case 0xb2: /* user data code */ + /* process_userdata(mpeg2dec, buffer); */ + break; + + case 0xb3: /* sequence_header_code */ + { + int value; + if (parser->is_sequence_needed) { + parser->is_sequence_needed = 0; + } + value = (buffer[0] << 16) | + (buffer[1] << 8) | + buffer[2]; + parser->width = ((value >> 12) + 15) & ~15; + parser->height = ((value & 0xfff) + 15) & ~15; + + parser->rate_code = buffer[3] & 15; + parser->aspect_ratio_info = buffer[3] >> 4; + + if (parser->rate_code < sizeof(frame_rate_tab)) { + parser->frame_duration = 90000; + parser->frame_duration *= frame_rate_tab[parser->rate_code][1]; + parser->frame_duration /= frame_rate_tab[parser->rate_code][0]; + } else { + printf ("invalid/unknown frame rate code : %d \n", + parser->rate_code); + parser->frame_duration = 0; + } + + parser->has_sequence = 1; + parser->is_mpeg1 = 1; + } + break; + + case 0xb5: /* extension_start_code */ + switch (buffer[0] & 0xf0) { + case 0x10: /* sequence extension */ + parser->is_mpeg1 = 0; + } + + default: + if (code >= 0xb9) + lprintf ("stream not demultiplexed ?\n"); + + if (code >= 0xb0) + break; + } + return is_frame_done; +} + +static inline uint8_t *copy_chunk (mpeg_parser_t *parser, + uint8_t *current, uint8_t *end) +{ + uint32_t shift; + uint8_t *chunk_ptr; + uint8_t *limit; + uint8_t byte; + + shift = parser->shift; + chunk_ptr = parser->chunk_ptr; + parser->chunk_start = chunk_ptr; + + limit = current + (parser->chunk_buffer + BUFFER_SIZE - chunk_ptr); + if (limit > end) + limit = end; + + while (1) { + + byte = *current++; + *chunk_ptr++ = byte; + if (shift != 0x00000100) { + shift = (shift | byte) << 8; + if (current < limit) + continue; + if (current == end) { + parser->chunk_ptr = chunk_ptr; + parser->shift = shift; + lprintf("Need more bytes\n"); + return NULL; + } else { + /* we filled the chunk buffer without finding a start code */ + parser->code = 0xb4; /* sequence_error_code */ + parser->chunk_ptr = parser->chunk_buffer; + return current; + } + } + lprintf("New chunk: 0x%2X\n", byte); + parser->chunk_ptr = chunk_ptr; + parser->shift = 0xffffff00; + parser->code = byte; + return current; + } +} + + +uint8_t *mpeg_parser_decode_data (mpeg_parser_t *parser, + uint8_t *current, uint8_t *end, + int *flush) +{ + int ret; + uint8_t code; + + ret = 0; + *flush = 0; + + while (current != end) { + if (parser->chunk_ptr == parser->chunk_buffer) { + /* write the beginning of the chunk */ + parser->chunk_buffer[0] = 0x00; + parser->chunk_buffer[1] = 0x00; + parser->chunk_buffer[2] = 0x01; + parser->chunk_buffer[3] = parser->code; + parser->chunk_ptr += 4; + parser->has_sequence = 0; + } + + code = parser->code; + + current = copy_chunk (parser, current, end); + if (current == NULL) + return NULL; + ret = parse_chunk (parser, code, parser->chunk_start, + parser->chunk_ptr - parser->chunk_start - 4); + if (ret == 1) { + if (parser->has_sequence) { + parser->frame_aspect_ratio = get_aspect_ratio(parser); + } + parser->buffer_size = parser->chunk_ptr - parser->chunk_buffer - 4; + parser->chunk_ptr = parser->chunk_buffer; + + if (parser->code == 0xb7) /* sequence end, maybe a still menu */ + *flush = 1; + + return current; + } + } + + return NULL; +} diff --git a/src/libffmpeg/mpeg_parser.h b/src/libffmpeg/mpeg_parser.h new file mode 100644 index 000000000..24bbfcbbb --- /dev/null +++ b/src/libffmpeg/mpeg_parser.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2001-2004 the xine project + * + * This file is part of xine, a free video player. + * + * xine 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. + * + * xine 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 + * + * Simple MPEG-ES parser/framer by Thibaut Mattern (tmattern@noos.fr) + * based on libmpeg2 decoder. + * + * $Id: mpeg_parser.h,v 1.1 2004/07/18 00:50:02 tmattern Exp $ + */ + #include <inttypes.h> + +#define BUFFER_SIZE (1194 * 1024) /* libmpeg2's buffer size */ + +/* picture coding type (mpeg2 header) */ +#define I_TYPE 1 +#define P_TYPE 2 +#define B_TYPE 3 +#define D_TYPE 4 + +typedef struct mpeg_parser_s { + uint32_t shift; + int is_sequence_needed; + uint8_t chunk_buffer[BUFFER_SIZE]; + uint8_t *chunk_ptr; + uint8_t *chunk_start; + int buffer_size; + uint8_t code; + uint8_t picture_coding_type; + int rate_code; + int aspect_ratio_info; + int in_slice; + + /* public properties */ + int is_mpeg1; + int has_sequence; + int width; + int height; + int frame_duration; + double frame_aspect_ratio; + +} mpeg_parser_t; + +/* parser initialization */ +void mpeg_parser_init (mpeg_parser_t *parser); + +/* read a frame + * return a pointer to the first byte of the next frame + * or NULL if more bytes are needed + * *flush is set to 1 if the decoder must be flushed (needed for still menus) + */ +uint8_t *mpeg_parser_decode_data (mpeg_parser_t *parser, + uint8_t *current, uint8_t *end, + int *flush); + +/* reset the parser */ +void mpeg_parser_reset (mpeg_parser_t *parser); diff --git a/src/libffmpeg/video_decoder.c b/src/libffmpeg/video_decoder.c index b7684a615..934b42556 100644 --- a/src/libffmpeg/video_decoder.c +++ b/src/libffmpeg/video_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: video_decoder.c,v 1.21 2004/07/01 20:56:15 jstembridge Exp $ + * $Id: video_decoder.c,v 1.22 2004/07/18 00:50:02 tmattern Exp $ * * xine video decoder plugin using ffmpeg * @@ -40,12 +40,12 @@ /* #define LOG */ - #include "xine_internal.h" #include "bswap.h" #include "buffer.h" #include "xineutils.h" #include "xine_decoder.h" +#include "mpeg_parser.h" #include "libavcodec/libpostproc/postprocess.h" @@ -77,32 +77,26 @@ struct ff_video_decoder_s { int decoder_ok; xine_bmiheader bih; - unsigned char *buf; + unsigned char *buf; int bufsize; int size; int skipframes; int slice_offset_size; - AVFrame *av_frame; - AVCodecContext *context; - AVCodec *codec; - - int pp_available; - int pp_quality; - int pp_flags; - pp_context_t *pp_context; - pp_mode_t *pp_mode; - - /* mpeg sequence header parsing, stolen from libmpeg2 */ - - uint32_t shift; - uint8_t *chunk_buffer; - uint8_t *chunk_ptr; - int chunk_size; - uint8_t code; + AVFrame *av_frame; + AVCodecContext *context; + AVCodec *codec; - int is_continous; + int pp_available; + int pp_quality; + int pp_flags; + pp_context_t *pp_context; + pp_mode_t *pp_mode; + + /* mpeg-es parsing */ + mpeg_parser_t mpeg_parser; + int is_mpeg12; double aspect_ratio; int frame_flags; @@ -116,7 +110,7 @@ struct ff_video_decoder_s { #ifdef ENABLE_DIRECT_RENDERING /* called from ffmpeg to do direct rendering method 1 */ static int get_buffer(AVCodecContext *context, AVFrame *av_frame){ - ff_video_decoder_t * this = (ff_video_decoder_t *)context->opaque; + ff_video_decoder_t *this = (ff_video_decoder_t *)context->opaque; vo_frame_t *img; int width = context->width; int height = context->height; @@ -169,7 +163,7 @@ static void release_buffer(struct AVCodecContext *context, AVFrame *av_frame){ av_frame->data[0]= NULL; av_frame->data[1]= NULL; av_frame->data[2]= NULL; - + img->free(img); av_frame->opaque = NULL; @@ -208,9 +202,9 @@ static void init_video_codec (ff_video_decoder_t *this, xine_bmiheader *bih) { /* Some codecs (eg rv10) copy flags in init so it's necessary to set * this flag here in case we are going to use direct rendering */ - if(this->codec->capabilities & CODEC_CAP_DR1) + if(this->codec->capabilities & CODEC_CAP_DR1) { this->context->flags |= CODEC_FLAG_EMU_EDGE; - + } if (avcodec_open (this->context, this->codec) < 0) { xprintf (this->stream->xine, XINE_VERBOSITY_LOG, _("ffmpeg_video_dec: couldn't open decoder\n")); @@ -250,8 +244,7 @@ static void init_video_codec (ff_video_decoder_t *this, xine_bmiheader *bih) { } else { this->output_format = XINE_IMGFMT_YV12; #ifdef ENABLE_DIRECT_RENDERING - if( this->context->pix_fmt == PIX_FMT_YUV420P && - this->codec->capabilities & CODEC_CAP_DR1 ) { + if( this->codec->capabilities & CODEC_CAP_DR1 ) { this->context->get_buffer = get_buffer; this->context->release_buffer = release_buffer; xprintf(this->stream->xine, XINE_VERBOSITY_LOG, @@ -327,193 +320,52 @@ static void init_postprocess (ff_video_decoder_t *this) { pp_change_quality(this); } +static int ff_handle_mpeg_sequence(ff_video_decoder_t *this, mpeg_parser_t *parser) { -/* code adapted from libmpeg2 */ -static inline uint8_t *ff_mpeg_copy_chunk (ff_video_decoder_t *this, - uint8_t *current, uint8_t *end) { - uint32_t shift; - uint8_t *chunk_ptr; - uint8_t *limit; - uint8_t byte; - - 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) { - - if (byte == 0x00) { - lprintf("start code found\n"); - this->chunk_size = chunk_ptr - this->chunk_buffer - 3; - this->chunk_buffer[0] = 0x00; - this->chunk_buffer[1] = 0x00; - this->chunk_buffer[2] = 0x01; - this->chunk_buffer[3] = this->code; - this->chunk_ptr = this->chunk_buffer + 4; - this->shift = 0xffffff00; - - this->code = byte; - return current; - } - } - shift = (shift | byte) << 8; - *chunk_ptr++ = byte; - if (current < limit) - continue; - if (current == end) { - lprintf("need more bytes\n"); - this->chunk_ptr = chunk_ptr; - this->shift = shift; - return NULL; - } else { - lprintf("buffer full\n"); + /* + * init codec + */ + if (!this->decoder_ok) { + _x_meta_info_set(this->stream, XINE_META_INFO_VIDEOCODEC, + "mpeg-1 (ffmpeg)"); - this->chunk_size = this->chunk_ptr - this->chunk_buffer; - - /* we filled the chunk buffer without finding a start code */ - this->chunk_buffer[0] = 0x00; - this->chunk_buffer[1] = 0x00; - this->chunk_buffer[2] = 0x01; - this->chunk_buffer[3] = this->code; - - this->chunk_ptr = this->chunk_buffer + 4; - this->code = 0xb4; /* sequence_error_code */ - return current; + this->codec = avcodec_find_decoder (CODEC_ID_MPEG1VIDEO); + if (!this->codec) { + xprintf (this->stream->xine, XINE_VERBOSITY_LOG, + _("avcodec_find_decoder (CODEC_ID_MPEG1VIDEO) failed.\n")); + _x_abort(); } - } -} - - -static void ff_find_sequence_header (ff_video_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 ; - - lprintf ("looking for sequence header... %02x\n", code); + init_video_codec (this, NULL); + } - /* mpeg2_stats (code, this->chunk_buffer); */ - - if (code == 0xb3) { /* sequence_header_code */ - - int width, height, frame_rate_code; - - lprintf ("found sequence header !\n"); - - 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; - - 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 = 3754; /* actually it's 3753.75 */ - 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 = 1502; /* actually it's 1501.5 */ - break; - case 8: /* 60 fps */ - this->video_step = 1500; - break; - default: - xprintf(this->stream->xine, XINE_VERBOSITY_LOG, - _("ffmpeg_video_dec: invalid/unknown frame rate code: %d \n"), - frame_rate_code); - this->video_step = 0; - } - - _x_meta_info_set(this->stream, XINE_META_INFO_VIDEOCODEC, - "mpeg-1 (ffmpeg)"); - - /* - * init codec - */ - - this->codec = avcodec_find_decoder (CODEC_ID_MPEG1VIDEO); - if (!this->codec) { - xprintf (this->stream->xine, XINE_VERBOSITY_LOG, - _("avcodec_find_decoder (CODEC_ID_MPEG1VIDEO) failed.\n")); - _x_abort(); - } - - this->is_continous = 1; - init_video_codec (this, NULL); - } + /* frame format change */ + if ((parser->width != this->bih.biWidth) || + (parser->height != this->bih.biHeight) || + (parser->frame_aspect_ratio != this->aspect_ratio)) { + xine_event_t event; + xine_format_change_data_t data; + + this->bih.biWidth = parser->width; + this->bih.biHeight = parser->height; + this->aspect_ratio = parser->frame_aspect_ratio; + _x_stream_info_set(this->stream, XINE_STREAM_INFO_VIDEO_WIDTH, this->bih.biWidth); + _x_stream_info_set(this->stream, XINE_STREAM_INFO_VIDEO_HEIGHT, this->bih.biHeight); + _x_stream_info_set(this->stream, XINE_STREAM_INFO_VIDEO_RATIO, this->aspect_ratio * 10000); + + event.type = XINE_EVENT_FRAME_FORMAT_CHANGE; + event.stream = this->stream; + event.data = &data; + event.data_length = sizeof(data); + data.width = this->bih.biWidth; + data.height = this->bih.biHeight; + data.aspect = this->aspect_ratio; + data.pan_scan = 0; + xine_event_send(this->stream, &event); } + this->video_step = this->mpeg_parser.frame_duration; + + return 1; } static void ff_convert_frame(ff_video_decoder_t *this, vo_frame_t *img) { @@ -864,14 +716,12 @@ static void ff_decode_data (video_decoder_t *this_gen, buf_element_t *buf) { buf->type, buf->size, buf->decoder_flags); codec_type = buf->type & 0xFFFF0000; + if (codec_type == BUF_VIDEO_MPEG) + this->is_mpeg12 = 1; if (buf->decoder_flags & BUF_FLAG_PREVIEW) { lprintf ("preview\n"); - - if ( (buf->type & 0xFFFF0000) == BUF_VIDEO_MPEG ) { - ff_find_sequence_header (this, buf->content, buf->content + buf->size); - } return; } @@ -933,9 +783,10 @@ static void ff_decode_data (video_decoder_t *this_gen, buf_element_t *buf) { } else if (buf->decoder_flags & BUF_FLAG_SPECIAL) { /* take care of all the various types of special buffers */ - + lprintf("BUF_FLAG_SPECIAL\n"); if (buf->decoder_info[1] == BUF_SPECIAL_STSD_ATOM) { + lprintf("BUF_SPECIAL_STSD_ATOM\n"); this->context->extradata_size = buf->decoder_info[2]; this->context->extradata = xine_xmalloc(buf->decoder_info[2]); memcpy(this->context->extradata, buf->decoder_info_ptr[2], @@ -946,6 +797,7 @@ static void ff_decode_data (video_decoder_t *this_gen, buf_element_t *buf) { palette_entry_t *demuxer_palette; AVPaletteControl *decoder_palette; + lprintf("BUF_SPECIAL_PALETTE\n"); decoder_palette = (AVPaletteControl *)this->context->palctrl; demuxer_palette = (palette_entry_t *)buf->decoder_info_ptr[2]; @@ -959,6 +811,7 @@ static void ff_decode_data (video_decoder_t *this_gen, buf_element_t *buf) { } else if (buf->decoder_info[1] == BUF_SPECIAL_RV_CHUNK_TABLE) { + lprintf("BUF_SPECIAL_RV_CHUNK_TABLE\n"); this->context->slice_count = buf->decoder_info[2]+1; if(this->context->slice_count > this->slice_offset_size) { @@ -971,12 +824,12 @@ static void ff_decode_data (video_decoder_t *this_gen, buf_element_t *buf) { this->context->slice_offset[i] = ((uint32_t *) buf->decoder_info_ptr[2])[(2*i)+1]; + } else { + return; /* do not send special data to the decoder */ } - } else { - if (((this->size == 0) && (buf->decoder_flags & BUF_FLAG_FRAME_END)) || - (this->is_continous)) { + (this->is_mpeg12)) { /* buf contains a full frame */ /* no memcpy needed */ ffbuf = buf->content; @@ -1004,15 +857,18 @@ static void ff_decode_data (video_decoder_t *this_gen, buf_element_t *buf) { if (buf->decoder_flags & BUF_FLAG_FRAME_START) this->pts = buf->pts; + else if (this->is_mpeg12 && buf->pts) + this->pts = buf->pts; + + if ((this->decoder_ok && this->size) || this->is_mpeg12) { - if (this->decoder_ok && this->size) { - - if ( (buf->decoder_flags & BUF_FLAG_FRAME_END) || this->is_continous ) { + if ( (buf->decoder_flags & BUF_FLAG_FRAME_END) || this->is_mpeg12 ) { vo_frame_t *img; int free_img; int got_picture, len; int offset; + int flush = 0; /* decode video frame(s) */ @@ -1037,10 +893,10 @@ static void ff_decode_data (video_decoder_t *this_gen, buf_element_t *buf) { } /* skip decoding b frames if too late */ - this->context->hurry_up = (this->skipframes > 2) ? 1:0; + this->context->hurry_up = (this->skipframes > 0); offset = 0; - while (this->size > 0) { + while ((this->size > 0) || (flush == 1)){ /* DV frames can be completely skipped */ if( codec_type == BUF_VIDEO_DV && this->skipframes ) { @@ -1048,22 +904,43 @@ static void ff_decode_data (video_decoder_t *this_gen, buf_element_t *buf) { got_picture = 1; } else { - if (this->is_continous) { + if (this->is_mpeg12) { uint8_t *current; + int next_flush; got_picture = 0; - current = ff_mpeg_copy_chunk (this, ffbuf + offset, ffbuf + offset + this->size); + if (!flush) { + current = mpeg_parser_decode_data(&this->mpeg_parser, + ffbuf + offset, ffbuf + offset + this->size, + &next_flush); + } else { + current = ffbuf + offset + this->size; /* end of the buffer */ + next_flush = 0; + } if (current == NULL) { lprintf("current == NULL\n"); - len = this->size; - } else { - lprintf("avcodec_decode_video: size=%d\n", this->chunk_size); + return; + } else { + + if (this->mpeg_parser.has_sequence) { + ff_handle_mpeg_sequence(this, &this->mpeg_parser); + } + + if (flush) { + lprintf("flush lavc buffers\n"); + /* hack: ffmpeg outputs the last frame if size=0 */ + this->mpeg_parser.buffer_size = 0; + } + lprintf("avcodec_decode_video: size=%d\n", this->mpeg_parser.buffer_size); len = avcodec_decode_video (this->context, this->av_frame, - &got_picture, this->chunk_buffer, - this->chunk_size); - lprintf("avcodec_decode_video: decoded_size=%d\n", len); + &got_picture, this->mpeg_parser.chunk_buffer, + this->mpeg_parser.buffer_size); + lprintf("avcodec_decode_video: decoded_size=%d, got_picture=%d\n", + len, got_picture); len = current - ffbuf - offset; lprintf("avcodec_decode_video: consumed_size=%d\n", len); + + flush = next_flush; } } else { @@ -1087,7 +964,7 @@ static void ff_decode_data (video_decoder_t *this_gen, buf_element_t *buf) { "ffmpeg_video_dec: didn't get a picture, %d bytes left\n", this->size); - if (!this->is_continous) { + if (!this->is_mpeg12) { if (this->size > 0) { ff_check_bufsize(this, offset + this->size); memmove (this->buf, &ffbuf[offset], this->size); @@ -1095,6 +972,20 @@ static void ff_decode_data (video_decoder_t *this_gen, buf_element_t *buf) { } return; } else { + if (this->context->hurry_up) { + /* skipped frame, output a bad frame */ + img = this->stream->video_out->get_frame (this->stream->video_out, + this->bih.biWidth, + this->bih.biHeight, + this->aspect_ratio, + this->output_format, + VO_BOTH_FIELDS|this->frame_flags); + img->pts = 0; + img->duration = this->video_step; + img->bad_frame = 1; + this->skipframes = img->draw(img, this->stream); + img->free(img); + } continue; } } @@ -1121,10 +1012,9 @@ static void ff_decode_data (video_decoder_t *this_gen, buf_element_t *buf) { free_img = 0; } - if (len<0 || this->skipframes) { - if( !this->skipframes ) - xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, - "ffmpeg_video_dec: error decompressing frame\n"); + if (len < 0) { + xprintf (this->stream->xine, XINE_VERBOSITY_DEBUG, + "ffmpeg_video_dec: error decompressing frame\n"); img->bad_frame = 1; } else { img->bad_frame = 0; @@ -1169,7 +1059,6 @@ static void ff_decode_data (video_decoder_t *this_gen, buf_element_t *buf) { img->free(img); } } - } } @@ -1183,13 +1072,19 @@ static void ff_reset (video_decoder_t *this_gen) { lprintf ("ff_reset\n"); this->size = 0; + if(this->context) avcodec_flush_buffers(this->context); + + if (this->is_mpeg12) + mpeg_parser_reset(&this->mpeg_parser); } static void ff_discontinuity (video_decoder_t *this_gen) { + ff_video_decoder_t *this = (ff_video_decoder_t *) this_gen; lprintf ("ff_discontinuity\n"); + this->pts = 0; } static void ff_dispose (video_decoder_t *this_gen) { @@ -1233,7 +1128,6 @@ static void ff_dispose (video_decoder_t *this_gen) { if(this->pp_mode) pp_free_mode(this->pp_mode); - free (this->chunk_buffer); free (this_gen); } @@ -1259,21 +1153,17 @@ static video_decoder_t *ff_video_open_plugin (video_decoder_class_t *class_gen, this->context = avcodec_alloc_context(); this->context->opaque = this; - this->chunk_buffer = xine_xmalloc (SLICE_BUFFER_SIZE + 4); - this->decoder_ok = 0; this->buf = NULL; - this->shift = 0xffffff00; - this->code = 0xb4; - this->chunk_ptr = this->chunk_buffer; - - this->is_continous = 0; + this->is_mpeg12 = 0; this->aspect_ratio = 0; this->pp_quality = 0; this->pp_context = NULL; this->pp_mode = NULL; + + mpeg_parser_init(&this->mpeg_parser); return &this->video_decoder; } |