ac3 of old (pre1.3.19) cannot detected/decoded with ffmpeg

Message ID 200509112052.28353.stefan@lucke.in-berlin.de
State New
Headers

Commit Message

Stefan Lucke Sept. 11, 2005, 6:52 p.m. UTC
  Hi,

software decoding/detection of old ac3 streams seems to be impossible
with ffmpeg. To fix this I made a modification so that recognition works.

Can someone with more ac3 stream knowledge can have a look at this
diff ? This vdr-1.3.22 apply with some offset to vdr-1.3.32 and still works.
  

Comments

Reinhard Nissl Sept. 11, 2005, 9:53 p.m. UTC | #1
Hi,

Stefan Lucke wrote:

> software decoding/detection of old ac3 streams seems to be impossible
> with ffmpeg. To fix this I made a modification so that recognition works.
> 
> Can someone with more ac3 stream knowledge can have a look at this
> diff ? This vdr-1.3.22 apply with some offset to vdr-1.3.32 and still works.
 >
> +                        Length += 4;
> +                        *e++ = 0x80; //substream id
> +                        *e++ = 0x00; // nr of ac3 frames (sea AppendSubStream
> +                        *e++ = 0x00;
> +                        *e++ = 0x00; //??

The first byte (byte 0) is correct: 0x80 means the first dolby track.

Then, to be precise, byte 1 specifies the number of AC3 frames starting 
in this packet. It must at least be 0x01, even if there is no AC3 frame 
starting (see below), i. e. the packet contains just continuation data 
of the current large AC3 frame which was started in a previous packet.

Byte 2 and 3 specifiy the offset in bytes from the last byte of the 
substream header to the first byte of a starting AC3 frame in this 
packet. I. e., 0x00 0x01 specifies, that byte 4 (the first byte 
following the substream header) will be 0x0b (the first byte of the 
syncword, which starts a new AC3 frame). A value of 0x00 0x00 means, 
that no AC3 frame starts in this packet (i. e., just continuation data, 
see above).

So, to create a proper substream header, one must "read" the stream, 
find sync words, use the frame size and calculate frame count and 
offset, which is a not a trivial task.

A further problem may arise by enlarging the packets to be able to 
append the substream header: VDR's packets should never be larger than 
IPACKS (= 2048 bytes). This means, each packet which is larger than 2044 
bytes needs to be split into two packets to enforce this rule.

Maybe the code of cDolbyRepacker could be reused to transform these old 
streams into the new format on the fly. Just feed it's Repack() method 
with the original PES packets and you'll find the repacked packets in 
the specified result buffer. BTW: whenever a DeviceClear() happens, 
cDolbyRepacker should be Reset(), too.

Bye.
  

Patch

diff -U 3 vdr-1.3.22/device.c vdr-1.3.22.jarada/device.c
--- vdr-1.3.22/device.c	2005-02-27 14:55:15.000000000 +0100
+++ vdr-1.3.22.jarada/device.c	2005-04-29 12:56:59.000000000 +0200
@@ -895,12 +905,41 @@ 
   return -1;
 }
 
+//  0  1  2  3  4  5  6  7  8  9
+// 00 00 01 BD ph pl xx xx hd si
+//
+// ph   packet length high
+// pl   packet length low
+// hd   header length
+// si   substream id
+// 00 00 01 bd 07 fa 84 80 05 23 cc 3d 21 df 0b 77
+// 00 00 01 bd 07 fa 80 00 00 60 6a 67 c9 7f c5 d3
+// 00 00 01 bd 00 20 80 00 05 80 01 00 01 60 6a 67
+// 00 00 01 bd 07 fa 80 00 00 94 6b 98 e8 b3 81 18
+// 00 00 01 bd 00 20 80 00 f9 80 01 00 01 94 6b 98
+// 00 00 01 bd 07 fa 80 00 00 c4 0e ca 39 4a 34 37
+// 00 00 01 bd 00 20 80 00 f9 80 01 00 01 c4 0e ca
+// 00 00 01 bd 03 2c 80 00 00 8d 88 e5 b8 6a c9 c9
+// 00 00 01 bd 00 04 80 00 0f 80 01 00 01 8d 88 e5
+// 00 00 01 bd 07 fa 84 80 05 23 cc 3d 92 5f 0b 77
+// 00 00 01 bd 07 fa 80 00 00 1a bb a5 4f 61 be 7b
+
+// pro7 ac3 2.0:
+// 00 00 01 bd 07 0c 84 81 05 2f 96 ed 89 1f 80 00 00 00 0b 77 2e 8c 1e 30 42 ff 70 80 01 00 00 7e
+// 00 00 01 bd 07 07 80 00 00 80 00 00 00 0b 77 5e c1 1e 30 42 ff 70 80 01 00 00 7e fb fb 86 0a 63
+// 00 00 01 bd 07 07 80 00 00 80 00 00 00 0b 77 bb 8c 1e 30 42 ff 70 80 01 00 00 7e fb ff 86 0d 29
+// 00 00 01 bd 07 07 80 00 00 80 00 00 00 0b 77 8c 6e 1e 30 42 ff 70 80 01 00 00 7e fb f5 86 0b 17
+// 00 00 01 bd 07 07 80 00 00 80 00 00 00 0b 77 fa 28 1e 30 42 ff 70 80 01 00 00 7e fb f5 86 0a c7
+// 00 00 01 bd 07 0c 84 81 05 2f 96 ed f9 9f 80 00 00 00 0b 77 9c c8 1e 30 42 ff 70 80 01 00 00 7e
+
 int cDevice::PlayPesPacket(const uchar *Data, int Length, bool VideoOnly)
 {
   bool FirstLoop = true;
   uchar c = Data[3];
   const uchar *Start = Data;
   const uchar *End = Start + Length;
+  uchar *fixData = NULL;
+
   while (Start < End) {
         int d = End - Start;
         int w = d;
@@ -922,10 +961,40 @@ 
                uchar SubStreamId = Data[PayloadOffset];
                uchar SubStreamType = SubStreamId & 0xF0;
                uchar SubStreamIndex = SubStreamId & 0x1F;
-        
                // Compatibility mode for old VDR recordings, where 0xBD was only AC3:
 pre_1_3_19_PrivateStreamDeteced:
                if (pre_1_3_19_PrivateStream) {
+                  if (FirstLoop) {
+                     if (!fixData && (fixData = (uchar *) malloc (Length+4))) {
+                          uchar       *e = fixData;
+                          const uchar *s = Data;
+                          int         l = Length, i, k;
+
+                        k = (Data[4] << 8) + Data[5];
+                        k += 4;
+                        memcpy (e, s, 8);
+                        e += 8;
+                        s += 8;
+                        l -= 8;
+                        i = (*s) + 1;
+                        memcpy (e, s, i);
+                        e += i;
+                        s += i;
+                        l -= i;
+                        fixData[4] = k >> 8;
+                        fixData[5] = k & 0x00ff;
+                        Length += 4;
+                        *e++ = 0x80; //substream id
+                        *e++ = 0x00; // nr of ac3 frames (sea AppendSubStream
+                        *e++ = 0x00;
+                        *e++ = 0x00; //??
+                        memcpy (e, s, l);
+                        Start = Data = (const uchar *) fixData;
+                        End = Start + Length;
+                        d = End - Start;
+                        w = d;
+                        }
+                     }
                   SubStreamId = c;
                   SubStreamType = 0x80;
                   SubStreamIndex = 0;
@@ -971,10 +1040,17 @@ 
         else {
            if (Start != Data)
               esyslog("ERROR: incomplete PES packet write!");
+           if (fixData)
+              free(fixData);
            return Start == Data ? w : Start - Data;
            }
         FirstLoop = false;
         }
+  if (fixData) {
+    free(fixData);
+    Length -=4;
+  }
+
   return Length;
 }