[ANNOUNCE] vdr-xine-0.7.10 plugin

Message ID 458326A2.5070205@gmx.de
State New
Headers

Commit Message

Reinhard Nissl Dec. 15, 2006, 10:50 p.m. UTC
  Hi,

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).

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. 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).

Bye.
  

Comments

Darren Salt Dec. 15, 2006, 11:27 p.m. UTC | #1
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.)
  

Patch

--- xine-cvs/xine-lib/src/libmpeg2/decode.c	2006-12-09 22:04:30.000000000 +0100
+++ xine-lib/src/libmpeg2/decode.c	2006-12-15 23:24:46.000000000 +0100
@@ -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, &current, 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]);
+    }
   }
 }
--- xine-cvs/xine-lib/src/libmpeg2/mpeg2.h	2005-04-11 20:29:46.000000000 +0200
+++ xine-lib/src/libmpeg2/mpeg2.h	2006-12-15 22:37:06.000000000 +0100
@@ -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 */