summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThibaut Mattern <tmattern@users.sourceforge.net>2004-07-18 00:50:02 +0000
committerThibaut Mattern <tmattern@users.sourceforge.net>2004-07-18 00:50:02 +0000
commit53c8bea4d9de3dea4c11e1fbedd24e5ba055a08d (patch)
tree9fc1c83dbfda6aae84f0d4563224127301001597
parentcf502f850beaf31c6a0a32649ce53ab1cc998949 (diff)
downloadxine-lib-53c8bea4d9de3dea4c11e1fbedd24e5ba055a08d.tar.gz
xine-lib-53c8bea4d9de3dea4c11e1fbedd24e5ba055a08d.tar.bz2
better mpeg-es parser.
Fix "hurry_up" behavior (keep the metronom happy). Enable DR1 for the mpeg12 decoder. Remove all the mpeg-es parsing from here, use the new parser instead. Handle frame format changes (width, height and aspect ratio) Tested with all my mpeg streams, and with some DVDs with still menus. Enjoy ;) CVS patchset: 6805 CVS date: 2004/07/18 00:50:02
-rw-r--r--src/libffmpeg/mpeg_parser.c297
-rw-r--r--src/libffmpeg/mpeg_parser.h71
-rw-r--r--src/libffmpeg/video_decoder.c376
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;
}