bitstreamout 0.70 and vdr 1.3.22
Commit Message
Hi,
Dr. Werner Fink wrote:
>>I've just had a closer look into xine's sources (demux_mpeg_block.c) and
>>it seems that my substream header is wrong most of the time.
>>
>>Byte 0: substream id => correct
>>Byte 1: number of AC3 frames starting in this PES packet => wrong
>>Byte 2+3: offset to first AC3 frame relative to PES payload => wrong
Reading xine's source documenation again, it looks like I've interpreted
it wrong. I've no idea why offset should be 5 for the first AC3 frame in
a DVD's PES packet!?
Anyway, source documentation doesn't match the code and actually they
ignore offset completely and rely on "number of AC3 frames" to assign
PTS to the correct AC3 frame.
> Hmmm ..this is what I've from http://mpucoder.kewlhair.com/DVD/
>
> [...]
>
> Audio Substream Headers
> -----------------------
> All substreams in private stream 1 begin with the substream number.
> The header described here immediately follows the substream number.
> This header is not part of either MPEG or the respective audio
> format standard. These are unique to DVD.
>
> All methods
> -----------
> +-------------------+-------------------------------------------+
> |byte 0 | byte 1 byte 2 |
> |-------------------+-------------------------------------------+
> |7 6 5 4 3 2 1 0 | 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 |
> |-------------------+-------------------------------------------+
> | FrmCnt | FirstAccUnit |
> | number of frames | offset to frame which corresponds to PTS |
> | which begin in | value offset 0 is the last byte of |
> | this packet | FirstAccUnit. The value 0000 indicates |
> | | there is no first access unit |
> +-------------------+-------------------------------------------+
>
> AC3 specific
> ------------
> AC3 has no additional information which is DVD specific.
>
>
> example
> -------
>
> +--------+-------+------------------------------------------------------------+
> | offset | value | meaning |
> +--------+-------+------------------------------------------------------------+
> | 01f | 80h | AC3 substream number (audio stream 0) |
> | 020 | 02 | this packet contains the beginning of 2 audio frames |
> | 021 | 0001 | the frame corresponding to PTS begins at packet offset 023 |
> | 023 | 0B77 | AC3 frame begins here (0B77 = AC3 sync word) |
> +--------+-------+------------------------------------------------------------+
>
> [...]
>
>>As cDolbyRepacker assures that 1 frame is put into 1 PES packet, byte 1
>>should be 1 and byte 3 should be 4.
>
> Byte 1 (or in the description above byte 0) should have 1, but the
> byte 3 and 4 (or in the description above byte 1 and 2) should have
> exactly 1 because the next byte is the first of an AC3 frame.
> Don't ask me why the specs writer do begin to count with 0 as normal
> programmers do :^)
>
> I've added a check now for the value 0 (simply adding 1 and checking
> for the magic AC3 bytes 0x0b 0x77 at position 1), this because with
> a real value 0 there is no beginning AC3 frame after the sub audio
> header.
>
>>But what about extremely large AC3 frames, e. g. 1920 words = 3840
>>bytes. By assuring that PES packets don't get larger than 2048 bytes it
>>can happen that such a PES packet contains just the "middle" of an AC3
>>frame. How should bytes 1 to 3 be set in such a case?
>
> This should also work, because then you have to set 0 for the offset
> and it is very unlikly to find a 0x0b 0x77 within the middle of an
> AC3 data frame.
Thank you very much for this explanation. The attached patch against
VDR-1.3.22 fixes all known issues of cDolbyRepacker so far.
Bye.
@@ -46,6 +46,8 @@ private:
int fragmentTodo;
uchar pesHeader[6 + 3 + 255 + 4 + 4];
int pesHeaderLen;
+ uchar pesHeaderBackup[6 + 3 + 255];
+ int pesHeaderBackupLen;
uchar chk1;
uchar chk2;
int ac3todo;
@@ -57,8 +59,8 @@ private:
get_length,
output_packet
} state;
- void ResetPesHeader(void);
- void AppendSubStreamID(void);
+ void ResetPesHeader(bool ContinuationFrame = false);
+ void AppendSubStreamID(bool ContinuationFrame = false);
bool FinishRemainder(cRingBufferLinear *ResultBuffer, const uchar *const Data, const int Todo, int &Done, int &Bite);
bool StartNewPacket(cRingBufferLinear *ResultBuffer, const uchar *const Data, const int Todo, int &Done, int &Bite);
public:
@@ -103,23 +105,26 @@ cDolbyRepacker::cDolbyRepacker(void)
Reset();
}
-void cDolbyRepacker::AppendSubStreamID(void)
+void cDolbyRepacker::AppendSubStreamID(bool ContinuationFrame)
{
if (subStreamId) {
pesHeader[pesHeaderLen++] = subStreamId;
+ // number of ac3 frames "starting" in this packet (1 by design).
+ pesHeader[pesHeaderLen++] = 0x01;
+ // offset to start of first ac3 frame (0 means "no ac3 frame starting"
+ // so 1 (by design) addresses the first byte after the next two bytes).
pesHeader[pesHeaderLen++] = 0x00;
- pesHeader[pesHeaderLen++] = 0x00;
- pesHeader[pesHeaderLen++] = 0x00;
+ pesHeader[pesHeaderLen++] = (ContinuationFrame ? 0x00 : 0x01);
}
}
-void cDolbyRepacker::ResetPesHeader(void)
+void cDolbyRepacker::ResetPesHeader(bool ContinuationFrame)
{
pesHeader[6] = 0x80;
pesHeader[7] = 0x00;
pesHeader[8] = 0x00;
pesHeaderLen = 9;
- AppendSubStreamID();
+ AppendSubStreamID(ContinuationFrame);
}
void cDolbyRepacker::Reset(void)
@@ -131,6 +136,7 @@ void cDolbyRepacker::Reset(void)
chk2 = 0;
fragmentLen = 0;
fragmentTodo = 0;
+ pesHeaderBackupLen = 0;
}
bool cDolbyRepacker::FinishRemainder(cRingBufferLinear *ResultBuffer, const uchar *const Data, const int Todo, int &Done, int &Bite)
@@ -229,12 +235,17 @@ int cDolbyRepacker::Put(cRingBufferLinea
if ((Data[6] & 0xC0) != 0x80)
return 0;
+ // backup PES header
+ if (Data[6] != 0x80 || Data[7] != 0x00 || Data[8] != 0x00) {
+ pesHeaderBackupLen = 6 + 3 + Data[8];
+ memcpy(pesHeaderBackup, Data, pesHeaderBackupLen);
+ }
+
// skip PES header
int done = 6 + 3 + Data[8];
int todo = Count - done;
const uchar *data = Data + done;
- bool headerCopied = false;
-
+
// look for 0x0B 0x77 <chk1> <chk2> <frameSize>
while (todo > 0) {
switch (state) {
@@ -242,10 +253,10 @@ int cDolbyRepacker::Put(cRingBufferLinea
if (*data == 0x0B) {
++(int &)state;
// copy header information once for later use
- if (!headerCopied) {
- headerCopied = true;
- pesHeaderLen = 6 + 3 + Data[8];
- memcpy(pesHeader, Data, pesHeaderLen);
+ if (pesHeaderBackupLen > 0) {
+ pesHeaderLen = pesHeaderBackupLen;
+ pesHeaderBackupLen = 0;
+ memcpy(pesHeader, pesHeaderBackup, pesHeaderLen);
AppendSubStreamID();
}
}
@@ -279,9 +290,8 @@ int cDolbyRepacker::Put(cRingBufferLinea
ac3todo = 2 * frameSizes[*data];
// frameSizeCode was invalid => restart searching
if (ac3todo <= 0) {
- // reset PES header instead of using/copying a wrong one
+ // reset PES header instead of using a wrong one
ResetPesHeader();
- headerCopied = true;
if (chk1 == 0x0B) {
if (chk2 == 0x77) {
state = store_chk1;
@@ -320,8 +330,8 @@ int cDolbyRepacker::Put(cRingBufferLinea
// start a new packet
if (!StartNewPacket(ResultBuffer, data, todo, done, bite))
return done;
- // prepare for next packet
- ResetPesHeader();
+ // prepare for next (continuation) packet
+ ResetPesHeader(state == output_packet);
}
data += bite;
done += bite;