From 53c8bea4d9de3dea4c11e1fbedd24e5ba055a08d Mon Sep 17 00:00:00 2001 From: Thibaut Mattern Date: Sun, 18 Jul 2004 00:50:02 +0000 Subject: 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 --- src/libffmpeg/mpeg_parser.c | 297 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 297 insertions(+) create mode 100644 src/libffmpeg/mpeg_parser.c (limited to 'src/libffmpeg/mpeg_parser.c') 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; +} -- cgit v1.2.3