From 70340520422bd57265c125c9114bf0804a0fcfb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Reinhard=20Ni=C3=9Fl?= Date: Wed, 11 Apr 2007 22:47:14 +0200 Subject: Improve MPEG2 detection to have it ready for correct aspect ratio determination. The current code detects MPEG2 when parsing the sequence extention which appears only in MPEG2 streams. But this is to late for correct aspect ratio determination which happens earlier and therefore assumes MPEG1. The result is a totally wrong aspect ratio. To fix this issue, the next start code (which is already available at that time) is passed to parse_chunk() too, which can then be used to detect a MPEG2 extension start code early enough before the aspect ratio is determined. --- src/libmpeg2/decode.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/libmpeg2/decode.c b/src/libmpeg2/decode.c index a2bb868df..3b19feda1 100644 --- a/src/libmpeg2/decode.c +++ b/src/libmpeg2/decode.c @@ -236,7 +236,7 @@ static void remember_metainfo (mpeg2dec_t *mpeg2dec) { } static inline int parse_chunk (mpeg2dec_t * mpeg2dec, int code, - uint8_t * buffer) + uint8_t * buffer, int next_code) { picture_t * picture; int is_frame_done; @@ -393,6 +393,11 @@ static inline int parse_chunk (mpeg2dec_t * mpeg2dec, int code, /* abort(); */ break; } + + /* according to ISO/IEC 13818-2, an extension start code will follow. + * Otherwise the stream follows ISO/IEC 11172-2 which means MPEG1 */ + picture->mpeg1 = (next_code != 0xb5); + if (mpeg2dec->force_aspect) picture->aspect_ratio_information = mpeg2dec->force_aspect; if (mpeg2dec->is_sequence_needed ) { @@ -638,7 +643,7 @@ int mpeg2_decode_data (mpeg2dec_t * mpeg2dec, uint8_t * current, uint8_t * end, current = copy_chunk (mpeg2dec, current, end); if (current == NULL) break; - ret += parse_chunk (mpeg2dec, code, mpeg2dec->chunk_buffer); + ret += parse_chunk (mpeg2dec, code, mpeg2dec->chunk_buffer, mpeg2dec->code); } libmpeg2_accel_frame_completion(&mpeg2dec->accel, mpeg2dec->frame_format, @@ -805,7 +810,7 @@ void mpeg2_close (mpeg2dec_t * mpeg2dec) void mpeg2_find_sequence_header (mpeg2dec_t * mpeg2dec, uint8_t * current, uint8_t * end){ - uint8_t code; + uint8_t code, next_code; picture_t *picture = mpeg2dec->picture; mpeg2dec->seek_mode = 1; @@ -815,6 +820,7 @@ void mpeg2_find_sequence_header (mpeg2dec_t * mpeg2dec, current = copy_chunk (mpeg2dec, current, end); if (current == NULL) return ; + next_code = mpeg2dec->code; /* printf ("looking for sequence header... %02x\n", code); */ @@ -825,6 +831,11 @@ void mpeg2_find_sequence_header (mpeg2dec_t * mpeg2dec, printf ("libmpeg2: bad sequence header\n"); continue; } + + /* according to ISO/IEC 13818-2, an extension start code will follow. + * Otherwise the stream follows ISO/IEC 11172-2 which means MPEG1 */ + picture->mpeg1 = (next_code != 0xb5); + if (mpeg2dec->force_aspect) picture->aspect_ratio_information = mpeg2dec->force_aspect; if (mpeg2dec->is_sequence_needed) { -- cgit v1.2.3 From 47d88e8447b5b86f7eca03d302f2d17d2a4c9c26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Reinhard=20Ni=C3=9Fl?= Date: Wed, 11 Apr 2007 23:04:11 +0200 Subject: Detect absence of AFD and report only changes. The current code cannot detect the absence of AFD once it has been seen in the stream. As AFD can appear in user data after sequence, group or picture start codes, the idea is to reset the stored AFD value when processing the sequence start code. In the case where AFD is seen in user data, it is stored internally, to have it ready when the first slice is processed. At least at that time, AFD data has been seen and can be analyzed for changes. At any change, the AFD value will then be stored into a stream property. Doing this only for changes avoids locks while writing the same value over and over to the stream's property. --- src/libmpeg2/decode.c | 25 ++++++++++++++++++++----- src/libmpeg2/mpeg2.h | 6 ++++++ 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/libmpeg2/decode.c b/src/libmpeg2/decode.c index 3b19feda1..3233fb9b4 100644 --- a/src/libmpeg2/decode.c +++ b/src/libmpeg2/decode.c @@ -87,6 +87,10 @@ void mpeg2_init (mpeg2dec_t * mpeg2dec, mpeg2dec->code = 0xb4; mpeg2dec->seek_mode = 0; + /* initialize AFD storage */ + mpeg2dec->afd_value_seen = XINE_VIDEO_AFD_NOT_PRESENT; + mpeg2dec->afd_value_reported = (XINE_VIDEO_AFD_NOT_PRESENT - 1); + memset (mpeg2dec->picture, 0, sizeof (picture_t)); /* initialize substructures */ @@ -394,6 +398,9 @@ static inline int parse_chunk (mpeg2dec_t * mpeg2dec, int code, break; } + /* reset AFD value to detect absence */ + mpeg2dec->afd_value_seen = XINE_VIDEO_AFD_NOT_PRESENT; + /* according to ISO/IEC 13818-2, an extension start code will follow. * Otherwise the stream follows ISO/IEC 11172-2 which means MPEG1 */ picture->mpeg1 = (next_code != 0xb5); @@ -469,6 +476,18 @@ static inline int parse_chunk (mpeg2dec_t * mpeg2dec, int code, if (code >= 0xb0) break; + /* check for AFD change once per picture */ + if (mpeg2dec->afd_value_reported != mpeg2dec->afd_value_seen) { + /* AFD data should better be stored in current_frame to have it */ + /* ready and synchronous with other data like width or height. */ + /* An AFD change should then be detected when a new frame is emitted */ + /* from the decoder to report the AFD change in display order and not */ + /* in decoding order like it happens below for now. */ + _x_stream_info_set(mpeg2dec->stream, XINE_STREAM_INFO_VIDEO_AFD, mpeg2dec->afd_value_seen); +fprintf(stderr, "AFD changed from %d to %d\n", mpeg2dec->afd_value_reported, mpeg2dec->afd_value_seen); + mpeg2dec->afd_value_reported = mpeg2dec->afd_value_seen; + } + if (!(mpeg2dec->in_slice)) { mpeg2dec->in_slice = 1; @@ -929,9 +948,5 @@ static void process_userdata(mpeg2dec_t *mpeg2dec, uint8_t *buffer) } /* check Active Format Description ETSI TS 101 154 V1.5.1 */ else if (buffer[0] == 0x44 && buffer[1] == 0x54 && buffer[2] == 0x47 && buffer[3] == 0x31) - { - int afd = (buffer[4] & 0x40) ? (buffer[5] & 0x0f) : -1; - _x_stream_info_set(mpeg2dec->stream, XINE_STREAM_INFO_VIDEO_AFD, afd); - - } + mpeg2dec->afd_value_seen = (buffer[4] & 0x40) ? (buffer[5] & 0x0f) : XINE_VIDEO_AFD_NOT_PRESENT; } diff --git a/src/libmpeg2/mpeg2.h b/src/libmpeg2/mpeg2.h index 788fa823c..253f300a2 100644 --- a/src/libmpeg2/mpeg2.h +++ b/src/libmpeg2/mpeg2.h @@ -57,6 +57,12 @@ typedef struct mpeg2dec_s { int force_aspect; int force_pan_scan; + /* AFD data can be found after a sequence, group or picture start code */ + /* and will be stored in afd_value_seen. Later it will be transfered to */ + /* a stream property and stored into afd_value_reported to detect changes */ + int afd_value_seen; + int afd_value_reported; + xine_stream_t *stream; /* a spu decoder for possible closed captions */ -- cgit v1.2.3 From 5e58a3c2f6a02dbd198369d1132366c49f5345fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Reinhard=20Ni=C3=9Fl?= Date: Wed, 11 Apr 2007 23:27:25 +0200 Subject: Special handling of sequence end code to improve still frames. The current code emits a frame when a non slice start code is seen. For still frames, this is typically a sequence end code. But the current code doesn't call parse_chunk() immediately because it waits for a further start code to determine the chunk of data to pass to parse_chunk(). But there isn't such a further start code for still frames after the sequence end code and thus, the still frame will not be emitted. As sequence end code is the only start code which has no data according to the MPEG specification, let's use this information to call parse_chunk() immediately. --- src/libmpeg2/decode.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/libmpeg2/decode.c b/src/libmpeg2/decode.c index 3233fb9b4..c3ba6ea4d 100644 --- a/src/libmpeg2/decode.c +++ b/src/libmpeg2/decode.c @@ -606,6 +606,18 @@ static inline uint8_t * copy_chunk (mpeg2dec_t * mpeg2dec, uint8_t * limit; uint8_t byte; + /* sequence end code 0xb7 doesn't have any data and there might be the case + * that no start code will follow this code for quite some time (e. g. in case + * of a still image. + * Therefore, return immediately with a chunk_size of 0. Setting code to 0xb4 + * will eat up any trailing garbage next time. + */ + if (mpeg2dec->code == 0xb7) { + mpeg2dec->code = 0xb4; + mpeg2dec->chunk_size = 0; + return current; + } + shift = mpeg2dec->shift; chunk_ptr = mpeg2dec->chunk_ptr; limit = current + (mpeg2dec->chunk_buffer + BUFFER_SIZE - chunk_ptr); @@ -657,7 +669,7 @@ int mpeg2_decode_data (mpeg2dec_t * mpeg2dec, uint8_t * current, uint8_t * end, if (pts) mpeg2dec->pts = pts; - while (current != end) { + while (current != end || mpeg2dec->code == 0xb7) { code = mpeg2dec->code; current = copy_chunk (mpeg2dec, current, end); if (current == NULL) -- cgit v1.2.3 From a76aacd69fa3512c3c566c93248bc6c41bb00e97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Reinhard=20Ni=C3=9Fl?= Date: Thu, 12 Apr 2007 00:02:45 +0200 Subject: Speed up start code scanning. The current code implements hardware (a shift register) in software just to find the byte pattern 00 00 01 xx, which causes remarkable CPU load on less powerful machines. The new approach uses memchr() to find the 01 in the buffer, which most often hits a start code. memchr() seems to be even faster then implementing a real pattern search (i. e. by just looking at every third byte to find 01). The new implementation causes significantly fewer CPU load on less powerful machines. --- src/libmpeg2/decode.c | 99 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 72 insertions(+), 27 deletions(-) diff --git a/src/libmpeg2/decode.c b/src/libmpeg2/decode.c index c3ba6ea4d..2b2426950 100644 --- a/src/libmpeg2/decode.c +++ b/src/libmpeg2/decode.c @@ -598,13 +598,63 @@ fprintf(stderr, "AFD changed from %d to %d\n", mpeg2dec->afd_value_reported, mpe return is_frame_done; } +static inline int find_start_code (mpeg2dec_t * mpeg2dec, + uint8_t ** current, uint8_t * limit) +{ + uint8_t * p; + + if (*current >= limit) + return 0; + if (mpeg2dec->shift == 0x00000100) + return 1; + + mpeg2dec->shift = (mpeg2dec->shift | *(*current)++) << 8; + + if (*current >= limit) + return 0; + if (mpeg2dec->shift == 0x00000100) + return 1; + + mpeg2dec->shift = (mpeg2dec->shift | *(*current)++) << 8; + + if (*current >= limit) + return 0; + if (mpeg2dec->shift == 0x00000100) + return 1; + + limit--; + + if (*current >= limit) { + mpeg2dec->shift = (mpeg2dec->shift | *(*current)++) << 8; + return 0; + } + + p = *current; + + while (p < limit && (p = (uint8_t *)memchr(p, 0x01, limit - p))) { + if (p[-2] || p[-1]) + p += 3; + else { + *current = ++p; + return 1; + } + } + + *current = ++limit; + p = limit - 3; + mpeg2dec->shift = (mpeg2dec->shift | *p++) << 8; + mpeg2dec->shift = (mpeg2dec->shift | *p++) << 8; + mpeg2dec->shift = (mpeg2dec->shift | *p++) << 8; + + return 0; +} + static inline uint8_t * copy_chunk (mpeg2dec_t * mpeg2dec, uint8_t * current, uint8_t * end) { - uint32_t shift; - uint8_t * chunk_ptr; uint8_t * limit; - uint8_t byte; + uint8_t * data = current; + int found, bite; /* sequence end code 0xb7 doesn't have any data and there might be the case * that no start code will follow this code for quite some time (e. g. in case @@ -618,37 +668,32 @@ static inline uint8_t * copy_chunk (mpeg2dec_t * mpeg2dec, return current; } - shift = mpeg2dec->shift; - chunk_ptr = mpeg2dec->chunk_ptr; - limit = current + (mpeg2dec->chunk_buffer + BUFFER_SIZE - chunk_ptr); + limit = current + (mpeg2dec->chunk_buffer + BUFFER_SIZE - mpeg2dec->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) { - mpeg2dec->chunk_ptr = chunk_ptr; - mpeg2dec->shift = shift; - return NULL; - } else { - /* we filled the chunk buffer without finding a start code */ - mpeg2dec->code = 0xb4; /* sequence_error_code */ - mpeg2dec->chunk_ptr = mpeg2dec->chunk_buffer; - return current; - } - } - mpeg2dec->code = byte; - mpeg2dec->chunk_size = chunk_ptr - mpeg2dec->chunk_buffer - 3; + found = find_start_code(mpeg2dec, ¤t, limit); + bite = current - data; + if (bite) { + xine_fast_memcpy(mpeg2dec->chunk_ptr, data, bite); + mpeg2dec->chunk_ptr += bite; + } + + if (found) { + mpeg2dec->code = *current++; + mpeg2dec->chunk_size = mpeg2dec->chunk_ptr - mpeg2dec->chunk_buffer - 3; mpeg2dec->chunk_ptr = mpeg2dec->chunk_buffer; mpeg2dec->shift = 0xffffff00; return current; } + + if (current == end) + return NULL; + + /* we filled the chunk buffer without finding a start code */ + mpeg2dec->code = 0xb4; /* sequence_error_code */ + mpeg2dec->chunk_ptr = mpeg2dec->chunk_buffer; + return current; } int mpeg2_decode_data (mpeg2dec_t * mpeg2dec, uint8_t * current, uint8_t * end, -- cgit v1.2.3