summaryrefslogtreecommitdiff
path: root/src/libffmpeg/mpeg_parser.c
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 /src/libffmpeg/mpeg_parser.c
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
Diffstat (limited to 'src/libffmpeg/mpeg_parser.c')
-rw-r--r--src/libffmpeg/mpeg_parser.c297
1 files changed, 297 insertions, 0 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;
+}