I demand that Reinhard Nissl may or may not have written...
> Darren Salt wrote:
>>> Cool -- are you relying on XINE_STREAM_INFO_VIDEO_AFD?
>> Yes.
>>> My implementation in libmpeg2 is not perfect. It lacks stacking (AFD
>>> information may be valid for a sequence, group or frame, i. e. the AFD
>>> for a sequence could be overridden for a single frame duration by a frame
>>> AFD) and cannot detect XINE_VIDEO_AFD_NOT_PRESENT.
>> My code is currently broken wrt some transitions and when paused, but this
>> could be an effect of breakage in your code. (I'm attaching the patch for
>> reference.)
>>> Would you provide me a sample stream which contains some transitions
>>> between different XINE_VIDEO_AFD_* values?
>> <URL:http://zap.tartarus.org/~ds/001.vdr> is a short clip with one
>> transition and a corresponding aspect change - AFD 14 to AFD 9, 16:9 to
>> 4:3.
> The attached patch against xine-lib-cvs/src/libmpeg2 fixes the above
> mentioned issues (and contains the vdr-xine changes, too).
There's still a pause problem (<URL:http://zap.tartarus.org/~ds/002.vdr>).
That may be caused by my code, though I don't see how when shoot-and-protect
isn't enabled.
> decode.c contains two fprintf() debug statements: one reports the detected
> AFD value and whether it was detected at [S]equence, [G]roup or [P]icture
> level.
Early results show BBC, ITV and Five using picture, and Channel 4 using
sequence.
> The other statement reports AFD changes.
> Be aware that AFD changes are detected in decoding order of the pictures.
> Further changes would be necessary to have them reported in display order
> (see comment in decode.c).
Rounding to group /may/ be sufficient - sloppiness with the changes isn't
unknown, although some of that may be due to set-top box manufacturers
cutting corners.
(I'll be putting this patch or a followup patch in my .debs.)
@@ -87,6 +87,13 @@ void mpeg2_init (mpeg2dec_t * mpeg2dec,
mpeg2dec->code = 0xb4;
mpeg2dec->seek_mode = 0;
+ /* initialize AFD storage */
+ mpeg2dec->afd_stack[0] = -1;
+ mpeg2dec->afd_stack[1] = -1;
+ mpeg2dec->afd_stack[2] = -1;
+ mpeg2dec->afd_index = -1;
+ mpeg2dec->afd_value = -2;
+
memset (mpeg2dec->picture, 0, sizeof (picture_t));
/* initialize substructures */
@@ -236,7 +243,7 @@ static void remember_metainfo (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;
@@ -303,6 +310,10 @@ static inline int parse_chunk (mpeg2dec_
abort();
}
+ /* reset picture AFD data and set index to picture */
+ mpeg2dec->afd_stack[2] = -1;
+ mpeg2dec->afd_index = 2;
+
mpeg2dec->is_frame_needed=0;
if (!picture->second_field) {
@@ -393,6 +404,17 @@ static inline int parse_chunk (mpeg2dec_
/* abort(); */
break;
}
+
+ /* reset squence, group and picture AFD data and set index to sequence */
+ mpeg2dec->afd_stack[0] = -1;
+ mpeg2dec->afd_stack[1] = -1;
+ mpeg2dec->afd_stack[2] = -1;
+ mpeg2dec->afd_index = 0;
+
+ /* 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 ) {
@@ -457,6 +479,12 @@ static inline int parse_chunk (mpeg2dec_
printf ("libmpeg2: bad group of pictures\n");
abort();
}
+
+ /* reset group and picture AFD data and set index to group */
+ mpeg2dec->afd_stack[1] = -1;
+ mpeg2dec->afd_stack[2] = -1;
+ mpeg2dec->afd_index = 1;
+
default:
if ((code >= 0xb9) && (code != 0xe4)) {
printf("Not multiplexed? 0x%x\n",code);
@@ -464,6 +492,22 @@ static inline int parse_chunk (mpeg2dec_
if (code >= 0xb0)
break;
+ /* check for AFD change once per picture */
+ if (0 <= mpeg2dec->afd_index && mpeg2dec->afd_index <= 2) {
+ int afd = (mpeg2dec->afd_stack[2] == -1) ? (mpeg2dec->afd_stack[1] == -1) ? mpeg2dec->afd_stack[0] : mpeg2dec->afd_stack[1] : mpeg2dec->afd_stack[2];
+ mpeg2dec->afd_index = -1;
+ /* 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. */
+ if (mpeg2dec->afd_value != afd) {
+ mpeg2dec->afd_value = afd;
+ _x_stream_info_set(mpeg2dec->stream, XINE_STREAM_INFO_VIDEO_AFD, mpeg2dec->afd_value);
+fprintf(stderr, "AFD changed to: %d\n", afd);
+ }
+ }
+
if (!(mpeg2dec->in_slice)) {
mpeg2dec->in_slice = 1;
@@ -574,45 +618,135 @@ static inline int parse_chunk (mpeg2dec_
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;
- shift = mpeg2dec->shift;
- chunk_ptr = mpeg2dec->chunk_ptr;
- limit = current + (mpeg2dec->chunk_buffer + BUFFER_SIZE - chunk_ptr);
+ /* 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;
+ }
+
+ limit = current + (mpeg2dec->chunk_buffer + BUFFER_SIZE - mpeg2dec->chunk_ptr);
if (limit > end)
limit = end;
+#if 0
+ {
+ uint32_t shift = mpeg2dec->shift;
+ uint8_t * chunk_ptr = mpeg2dec->chunk_ptr;
- while (1) {
+ 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;
+ uint8_t 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;
+ mpeg2dec->chunk_ptr = mpeg2dec->chunk_buffer;
+ mpeg2dec->shift = 0xffffff00;
+ return current;
}
- mpeg2dec->code = byte;
- mpeg2dec->chunk_size = chunk_ptr - mpeg2dec->chunk_buffer - 3;
+ }
+#else
+ {
+ uint8_t * data = current;
+ int found = find_start_code(mpeg2dec, ¤t, limit);
+ int 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;
- mpeg2dec->shift = 0xffffff00;
return current;
}
+#endif
}
int mpeg2_decode_data (mpeg2dec_t * mpeg2dec, uint8_t * current, uint8_t * end,
@@ -633,12 +767,12 @@ int mpeg2_decode_data (mpeg2dec_t * mpeg
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)
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 +939,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 +949,7 @@ void mpeg2_find_sequence_header (mpeg2de
current = copy_chunk (mpeg2dec, current, end);
if (current == NULL)
return ;
+ next_code = mpeg2dec->code;
/* printf ("looking for sequence header... %02x\n", code); */
@@ -825,6 +960,11 @@ void mpeg2_find_sequence_header (mpeg2de
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) {
@@ -919,8 +1059,10 @@ static void process_userdata(mpeg2dec_t
/* 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);
-
+ if (0 <= mpeg2dec->afd_index && mpeg2dec->afd_index <= 2)
+ {
+ mpeg2dec->afd_stack[mpeg2dec->afd_index] = (buffer[4] & 0x40) ? (buffer[5] & 0x0f) : -1;
+fprintf(stderr, "AFD detected at %c %d\n", "SGP"[mpeg2dec->afd_index], mpeg2dec->afd_stack[mpeg2dec->afd_index]);
+ }
}
}
@@ -57,6 +57,14 @@ typedef struct mpeg2dec_s {
int force_aspect;
int force_pan_scan;
+ /* AFD data can be found after a sequence, group or picture start code */
+ /* afd_stack[] stores the AFD value and afd_index specifies at which */
+ /* array element the data has to be written to: 0, 1, or 2 */
+ int afd_stack[3];
+ int afd_index;
+ /* afd_value stores the last determined AFD data for change detection */
+ int afd_value;
+
xine_stream_t *stream;
/* a spu decoder for possible closed captions */