summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKlaus Schmidinger <vdr@tvdr.de>2005-08-26 13:34:07 +0200
committerKlaus Schmidinger <vdr@tvdr.de>2005-08-26 13:34:07 +0200
commit449ffebcac1e857b4c11f60b3c310ac337a4abc8 (patch)
treeafa6d71296927d6e33d4c25ed9c34983b9715815
parented807972ac8aadd42a72f09a89bfba81be2644d7 (diff)
downloadvdr-449ffebcac1e857b4c11f60b3c310ac337a4abc8.tar.gz
vdr-449ffebcac1e857b4c11f60b3c310ac337a4abc8.tar.bz2
Implemented cAudioRepacker
-rw-r--r--CONTRIBUTORS1
-rw-r--r--HISTORY3
-rw-r--r--remux.c601
-rw-r--r--remux.h11
4 files changed, 519 insertions, 97 deletions
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index ff546dd4..9af0b378 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -976,6 +976,7 @@ Reinhard Nissl <rnissl@gmx.de>
for making cDvbPlayer::Goto() append a Sequence End Code to get the image shown
immediately with softdevices
for fixing cDvbSpuBitmap::putPixel()
+ for implementing cAudioRepacker in remux.c
Richard Robson <richard_robson@beeb.net>
for reporting freezing replay if a timer starts while in Transfer Mode from the
diff --git a/HISTORY b/HISTORY
index f6227105..7cbc3179 100644
--- a/HISTORY
+++ b/HISTORY
@@ -3722,3 +3722,6 @@ Video Disk Recorder Revision History
- Added missing German OSD texts for 'Audio language'.
- The Setup/CICAM menu now only contains the devices that actually have a CI and
dynamically detects the number of slots a CI provides.
+- Implemented cAudioRepacker for better handling of audio PES packets (thanks to
+ Reinhard Nissl).
+
diff --git a/remux.c b/remux.c
index c980bd83..66477f12 100644
--- a/remux.c
+++ b/remux.c
@@ -11,7 +11,7 @@
* The cDolbyRepacker code was originally written by Reinhard Nissl <rnissl@gmx.de>,
* and adapted to the VDR coding style by Klaus.Schmidinger@cadsoft.de.
*
- * $Id: remux.c 1.37 2005/08/21 08:58:58 kls Exp $
+ * $Id: remux.c 1.38 2005/08/26 13:34:07 kls Exp $
*/
#include "remux.h"
@@ -20,49 +20,23 @@
#include "thread.h"
#include "tools.h"
-// --- cRepacker -------------------------------------------------------------
-
-class cRepacker {
-protected:
- int maxPacketSize;
- uint8_t subStreamId;
- static void DroppedData(const char *Reason, int Count) { esyslog("%s (dropped %d bytes)", Reason, Count); }
- static int Put(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count)
- {
- int n = ResultBuffer->Put(Data, Count);
- if (n != Count)
- esyslog("ERROR: result buffer overflow, dropped %d out of %d byte", Count - n, Count);
- return n;
- }
- static int AnalyzePesHeader(const uchar *Data, int Count, int &PesPayloadOffset, bool *ContinuationHeader = 0);
-public:
- cRepacker(void) { maxPacketSize = 6 + 65535; subStreamId = 0; }
- virtual ~cRepacker() {}
- virtual void Reset(void) {}
- virtual void Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count) = 0;
- virtual int BreakAt(const uchar *Data, int Count) = 0;
- virtual int QuerySnoopSize(void) { return 0; }
- void SetMaxPacketSize(int MaxPacketSize) { maxPacketSize = MaxPacketSize; }
- void SetSubStreamId(uint8_t SubStreamId) { subStreamId = SubStreamId; }
- };
-
-int cRepacker::AnalyzePesHeader(const uchar *Data, int Count, int &PesPayloadOffset, bool *ContinuationHeader)
+ePesHeader AnalyzePesHeader(const uchar *Data, int Count, int &PesPayloadOffset, bool *ContinuationHeader)
{
if (Count < 7)
- return -1; // too short
+ return phNeedMoreData; // too short
if ((Data[6] & 0xC0) == 0x80) { // MPEG 2
if (Count < 9)
- return -1; // too short
+ return phNeedMoreData; // too short
PesPayloadOffset = 6 + 3 + Data[8];
if (Count < PesPayloadOffset)
- return -1; // too short
+ return phNeedMoreData; // too short
if (ContinuationHeader)
*ContinuationHeader = ((Data[6] == 0x80) && !Data[7] && !Data[8]);
- return 2; // MPEG 2
+ return phMPEG2; // MPEG 2
}
// check for MPEG 1 ...
@@ -74,7 +48,7 @@ int cRepacker::AnalyzePesHeader(const uchar *Data, int Count, int &PesPayloadOff
break;
if (Count <= ++PesPayloadOffset)
- return -1; // too short
+ return phNeedMoreData; // too short
}
// skip STD_buffer_scale/size
@@ -82,7 +56,7 @@ int cRepacker::AnalyzePesHeader(const uchar *Data, int Count, int &PesPayloadOff
PesPayloadOffset += 2;
if (Count <= PesPayloadOffset)
- return -1; // too short
+ return phNeedMoreData; // too short
}
if (ContinuationHeader)
@@ -104,18 +78,49 @@ int cRepacker::AnalyzePesHeader(const uchar *Data, int Count, int &PesPayloadOff
*ContinuationHeader = true;
}
else
- return 0; // unknown
+ return phInvalid; // unknown
if (Count < PesPayloadOffset)
- return -1; // too short
+ return phNeedMoreData; // too short
- return 1; // MPEG 1
+ return phMPEG1; // MPEG 1
}
-// --- cVideoRepacker --------------------------------------------------------
+// --- cRepacker -------------------------------------------------------------
-class cVideoRepacker : public cRepacker {
-private:
+class cRepacker {
+protected:
+ int maxPacketSize;
+ uint8_t subStreamId;
+ static void DroppedData(const char *Reason, int Count) { esyslog("%s (dropped %d bytes)", Reason, Count); }
+public:
+ static int Put(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count, int CapacityNeeded);
+ cRepacker(void) { maxPacketSize = 6 + 65535; subStreamId = 0; }
+ virtual ~cRepacker() {}
+ virtual void Reset(void) {}
+ virtual void Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count) = 0;
+ virtual int BreakAt(const uchar *Data, int Count) = 0;
+ virtual int QuerySnoopSize(void) { return 0; }
+ void SetMaxPacketSize(int MaxPacketSize) { maxPacketSize = MaxPacketSize; }
+ void SetSubStreamId(uint8_t SubStreamId) { subStreamId = SubStreamId; }
+ };
+
+int cRepacker::Put(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count, int CapacityNeeded)
+{
+ if (CapacityNeeded >= Count && ResultBuffer->Free() < CapacityNeeded) {
+ esyslog("ERROR: possible result buffer overflow, dropped %d out of %d byte", CapacityNeeded, CapacityNeeded);
+ return 0;
+ }
+ int n = ResultBuffer->Put(Data, Count);
+ if (n != Count)
+ esyslog("ERROR: result buffer overflow, dropped %d out of %d byte", Count - n, Count);
+ return n;
+}
+
+// --- cCommonRepacker --------------------------------------------------------
+
+class cCommonRepacker : public cRepacker {
+protected:
int skippedBytes;
int packetTodo;
uchar fragmentData[6 + 65535 + 3];
@@ -125,40 +130,24 @@ private:
uchar pesHeaderBackup[6 + 3 + 255];
int pesHeaderBackupLen;
uint32_t scanner;
- enum eState {
- syncing,
- findPicture,
- scanPicture
- } state;
uint32_t localScanner;
int localStart;
bool PushOutPacket(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count);
-public:
- cVideoRepacker(void);
- virtual void Reset(void);
- virtual void Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count);
- virtual int BreakAt(const uchar *Data, int Count);
virtual int QuerySnoopSize() { return 4; }
+ virtual void Reset(void);
};
-cVideoRepacker::cVideoRepacker(void)
-{
- Reset();
-}
-
-void cVideoRepacker::Reset(void)
+void cCommonRepacker::Reset(void)
{
skippedBytes = 0;
packetTodo = maxPacketSize - 6 - 3;
fragmentLen = 0;
pesHeaderLen = 0;
pesHeaderBackupLen = 0;
- scanner = 0xFFFFFFFF;
- state = syncing;
localStart = -1;
}
-bool cVideoRepacker::PushOutPacket(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count)
+bool cCommonRepacker::PushOutPacket(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count)
{
// enter packet length into PES header ...
if (fragmentLen > 0) { // ... which is contained in the fragment buffer
@@ -166,11 +155,17 @@ bool cVideoRepacker::PushOutPacket(cRingBufferLinear *ResultBuffer, const uchar
int PacketLen = fragmentLen + Count - 6;
fragmentData[ 4 ] = PacketLen >> 8;
fragmentData[ 5 ] = PacketLen & 0xFF;
+ // just skip packets with no payload
+ int PesPayloadOffset = 0;
+ if (AnalyzePesHeader(fragmentData, fragmentLen, PesPayloadOffset) <= phInvalid)
+ esyslog("cCommonRepacker: invalid PES packet encountered in fragment buffer!");
+ else if (6 + PacketLen <= PesPayloadOffset)
+ return true; // skip empty packet
// amount of data to put into result buffer: a negative Count value means
// to strip off any partially contained start code.
int Bite = fragmentLen + (Count >= 0 ? 0 : Count);
// put data into result buffer
- int n = Put(ResultBuffer, fragmentData, Bite);
+ int n = Put(ResultBuffer, fragmentData, Bite, 6 + PacketLen);
fragmentLen = 0;
if (n != Bite)
return false;
@@ -179,11 +174,17 @@ bool cVideoRepacker::PushOutPacket(cRingBufferLinear *ResultBuffer, const uchar
int PacketLen = pesHeaderLen + Count - 6;
pesHeader[ 4 ] = PacketLen >> 8;
pesHeader[ 5 ] = PacketLen & 0xFF;
+ // just skip packets with no payload
+ int PesPayloadOffset = 0;
+ if (AnalyzePesHeader(pesHeader, pesHeaderLen, PesPayloadOffset) <= phInvalid)
+ esyslog("cCommonRepacker: invalid PES packet encountered in header buffer!");
+ else if (6 + PacketLen <= PesPayloadOffset)
+ return true; // skip empty packet
// amount of data to put into result buffer: a negative Count value means
// to strip off any partially contained start code.
int Bite = pesHeaderLen + (Count >= 0 ? 0 : Count);
// put data into result buffer
- int n = Put(ResultBuffer, pesHeader, Bite);
+ int n = Put(ResultBuffer, pesHeader, Bite, 6 + PacketLen);
pesHeaderLen = 0;
if (n != Bite)
return false;
@@ -193,7 +194,7 @@ bool cVideoRepacker::PushOutPacket(cRingBufferLinear *ResultBuffer, const uchar
// amount of data to put into result buffer
int Bite = Count;
// put data into result buffer
- int n = Put(ResultBuffer, Data, Bite);
+ int n = Put(ResultBuffer, Data, Bite, Bite);
if (n != Bite)
return false;
}
@@ -201,6 +202,34 @@ bool cVideoRepacker::PushOutPacket(cRingBufferLinear *ResultBuffer, const uchar
return true;
}
+// --- cVideoRepacker --------------------------------------------------------
+
+class cVideoRepacker : public cCommonRepacker {
+private:
+ enum eState {
+ syncing,
+ findPicture,
+ scanPicture
+ } state;
+public:
+ cVideoRepacker(void);
+ virtual void Reset(void);
+ virtual void Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count);
+ virtual int BreakAt(const uchar *Data, int Count);
+ };
+
+cVideoRepacker::cVideoRepacker(void)
+{
+ Reset();
+}
+
+void cVideoRepacker::Reset(void)
+{
+ cCommonRepacker::Reset();
+ scanner = 0xFFFFFFFF;
+ state = syncing;
+}
+
void cVideoRepacker::Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count)
{
// synchronisation is detected some bytes after frame start.
@@ -211,8 +240,8 @@ void cVideoRepacker::Repack(cRingBufferLinear *ResultBuffer, const uchar *Data,
int pesPayloadOffset = 0;
bool continuationHeader = false;
- int mpegLevel = AnalyzePesHeader(Data, Count, pesPayloadOffset, &continuationHeader);
- if (mpegLevel <= 0) {
+ ePesHeader mpegLevel = AnalyzePesHeader(Data, Count, pesPayloadOffset, &continuationHeader);
+ if (mpegLevel <= phInvalid) {
DroppedData("cVideoRepacker: no valid PES packet header found", Count);
return;
}
@@ -298,7 +327,7 @@ void cVideoRepacker::Repack(cRingBufferLinear *ResultBuffer, const uchar *Data,
pesHeader[pesHeaderLen++] = 0x00; // length still unknown
pesHeader[pesHeaderLen++] = 0x00; // length still unknown
- if (mpegLevel == 2) {
+ if (mpegLevel == phMPEG2) {
pesHeader[pesHeaderLen++] = 0x80;
pesHeader[pesHeaderLen++] = 0x00;
pesHeader[pesHeaderLen++] = 0x00;
@@ -392,7 +421,7 @@ void cVideoRepacker::Repack(cRingBufferLinear *ResultBuffer, const uchar *Data,
pesHeader[pesHeaderLen++] = 0x00; // length still unknown
pesHeader[pesHeaderLen++] = 0x00; // length still unknown
- if (mpegLevel == 2) {
+ if (mpegLevel == phMPEG2) {
pesHeader[pesHeaderLen++] = 0x80;
pesHeader[pesHeaderLen++] = 0x00;
pesHeader[pesHeaderLen++] = 0x00;
@@ -443,7 +472,7 @@ int cVideoRepacker::BreakAt(const uchar *Data, int Count)
{
int PesPayloadOffset = 0;
- if (AnalyzePesHeader(Data, Count, PesPayloadOffset) <= 0)
+ if (AnalyzePesHeader(Data, Count, PesPayloadOffset) <= phInvalid)
return -1; // not enough data for test
// just detect end of picture
@@ -475,6 +504,355 @@ int cVideoRepacker::BreakAt(const uchar *Data, int Count)
return PesPayloadOffset + packetTodo + 4;
}
+// --- cAudioRepacker --------------------------------------------------------
+
+class cAudioRepacker : public cCommonRepacker {
+private:
+ static int bitRates[];
+ enum eState {
+ syncing,
+ scanFrame
+ } state;
+ int frameTodo;
+ int frameSize;
+ bool IsValidAudioHeader(uint32_t Header, int *FrameSize = NULL);
+public:
+ cAudioRepacker(void);
+ virtual void Reset(void);
+ virtual void Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count);
+ virtual int BreakAt(const uchar *Data, int Count);
+ };
+
+int cAudioRepacker::bitRates[] = { // all values are specified as kbits/s
+ // Layer I
+ 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, -1,
+ // Layer II
+ 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, -1,
+ // Layer III
+ 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, -1
+ };
+
+cAudioRepacker::cAudioRepacker(void)
+{
+ Reset();
+}
+
+void cAudioRepacker::Reset(void)
+{
+ cCommonRepacker::Reset();
+ scanner = 0;
+ state = syncing;
+ frameTodo = 0;
+ frameSize = 0;
+}
+
+bool cAudioRepacker::IsValidAudioHeader(uint32_t Header, int *FrameSize)
+{
+ int syncword = (Header & 0xFFF00000) >> 20;
+ int id = (Header & 0x00080000) >> 19;
+ int layer = (Header & 0x00060000) >> 17;
+//int protection_bit = (Header & 0x00010000) >> 16;
+ int bitrate_index = (Header & 0x0000F000) >> 12;
+ int sampling_frequency = (Header & 0x00000C00) >> 10;
+ int padding_bit = (Header & 0x00000200) >> 9;
+//int private_bit = (Header & 0x00000100) >> 8;
+//int mode = (Header & 0x000000C0) >> 6;
+//int mode_extension = (Header & 0x00000030) >> 4;
+//int copyright = (Header & 0x00000008) >> 3;
+//int orignal_copy = (Header & 0x00000004) >> 2;
+ int emphasis = (Header & 0x00000003);
+
+ if (syncword != 0xFFF)
+ return false;
+
+ if (id == 0) // reserved
+ return false;
+
+ if (layer == 0) // reserved
+ return false;
+
+ if (bitrate_index == 0xF) // forbidden
+ return false;
+
+ if (sampling_frequency == 3) // reserved
+ return false;
+
+ if (emphasis == 2) // reserved
+ return false;
+
+ if (FrameSize) {
+ if (bitrate_index == 0)
+ *FrameSize = 0;
+ else {
+ static int samplingFrequencies[] = { 44100, 48000, 32000 }; // Hz
+
+ int br = 1000 * bitRates[ (3 - layer) * 0x10 + bitrate_index ]; // bits/s
+ int sf = samplingFrequencies[sampling_frequency];
+
+ bool layerI = (layer == 3);
+
+ int N = (layerI ? 12 : 144) * br / sf; // slots
+
+ *FrameSize = (N + padding_bit) * (layerI ? 4 : 1); // bytes
+ }
+ }
+
+ return true;
+}
+
+void cAudioRepacker::Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count)
+{
+ // synchronisation is detected some bytes after frame start.
+ const int SkippedBytesLimit = 4;
+
+ // reset local scanner
+ localStart = -1;
+
+ int pesPayloadOffset = 0;
+ bool continuationHeader = false;
+ ePesHeader mpegLevel = AnalyzePesHeader(Data, Count, pesPayloadOffset, &continuationHeader);
+ if (mpegLevel <= phInvalid) {
+ DroppedData("cAudioRepacker: no valid PES packet header found", Count);
+ return;
+ }
+ if (!continuationHeader) {
+ // backup PES header
+ pesHeaderBackupLen = pesPayloadOffset;
+ memcpy(pesHeaderBackup, Data, pesHeaderBackupLen);
+ }
+
+ // skip PES header
+ int done = pesPayloadOffset;
+ int todo = Count - done;
+ const uchar *data = Data + done;
+ // remember start of the data
+ const uchar *payload = data;
+
+ while (todo > 0) {
+ // collect number of skipped bytes while syncing
+ if (state <= syncing)
+ skippedBytes++;
+ else if (frameTodo > 0)
+ frameTodo--;
+ // did we reach an audio frame header?
+ scanner <<= 8;
+ scanner |= *data;
+ if ((scanner & 0xFFF00000) == 0xFFF00000) {
+ if (frameTodo <= 0 && IsValidAudioHeader(scanner, &frameSize)) {
+ if (state == scanFrame) {
+ // As a new audio frame starts here, the previous one is done. So push
+ // out the packet to start a new packet for the next audio frame. If
+ // the byte count gets negative then the current buffer ends in a
+ // partitial audio frame header that must be stripped off, as it shall
+ // be put in the next packet.
+ if (!PushOutPacket(ResultBuffer, payload, data - 3 - payload)) {
+ DroppedData("cAudioRepacker: result buffer overflow", Count - (done - 3));
+ return;
+ }
+ // go on with syncing to the next audio frame
+ state = syncing;
+ }
+ if (state == syncing) {
+ // report that syncing dropped some bytes
+ if (skippedBytes > SkippedBytesLimit)
+ esyslog("cAudioRepacker: skipped %d bytes to sync on next audio frame", skippedBytes - SkippedBytesLimit);
+ skippedBytes = 0;
+ // if there is a PES header available, then use it ...
+ if (pesHeaderBackupLen > 0) {
+ // ISO 13818-1 says:
+ // In the case of audio, if a PTS is present in a PES packet header
+ // it shall refer to the access unit commencing in the PES packet. An
+ // audio access unit commences in a PES packet if the first byte of
+ // the audio access unit is present in the PES packet.
+ memcpy(pesHeader, pesHeaderBackup, pesHeaderBackupLen);
+ pesHeaderLen = pesHeaderBackupLen;
+ pesHeaderBackupLen = 0;
+ }
+ else {
+ // ... otherwise create a continuation PES header
+ pesHeaderLen = 0;
+ pesHeader[pesHeaderLen++] = 0x00;
+ pesHeader[pesHeaderLen++] = 0x00;
+ pesHeader[pesHeaderLen++] = 0x01;
+ pesHeader[pesHeaderLen++] = Data[3]; // audio stream ID
+ pesHeader[pesHeaderLen++] = 0x00; // length still unknown
+ pesHeader[pesHeaderLen++] = 0x00; // length still unknown
+
+ if (mpegLevel == phMPEG2) {
+ pesHeader[pesHeaderLen++] = 0x80;
+ pesHeader[pesHeaderLen++] = 0x00;
+ pesHeader[pesHeaderLen++] = 0x00;
+ }
+ else
+ pesHeader[pesHeaderLen++] = 0x0F;
+ }
+ // append the first three bytes of the audio frame header
+ pesHeader[pesHeaderLen++] = 0xFF;
+ pesHeader[pesHeaderLen++] = (scanner >> 16) & 0xFF;
+ pesHeader[pesHeaderLen++] = (scanner >> 8) & 0xFF;
+ // the next packet's payload will begin with the fourth byte of
+ // the audio frame header (= the actual byte)
+ payload = data;
+ // as there is no length information available, assume the
+ // maximum we can hold in one PES packet
+ packetTodo = maxPacketSize - pesHeaderLen;
+ // setup expected audio frame size to omit false audio header detection
+ frameTodo = frameSize;
+ // go on with collecting the frame's data
+ ((int &)state)++;
+ }
+ }
+ }
+ data++;
+ done++;
+ todo--;
+ // do we have to start a new packet as there is no more space left?
+ if (--packetTodo <= 0) {
+ // We connot start a new packet here if the current might end in an audio
+ // frame header and this header shall possibly be put in the next packet. So
+ // overfill the current packet until we can safely detect that we won't
+ // break an audio frame header into pieces:
+ //
+ // A) the last four bytes were an audio frame header.
+ // B) the last three bytes introduce an audio frame header.
+ // C) the last two bytes introduce an audio frame header.
+ // D) the last byte introduces an audio frame header.
+ //
+ // Todo : Data : Rule : Result
+ // -----:-------------------------------:------:-------
+ // : XX XX FF Fz zz zz|YY YY YY YY : :
+ // 0 : ^^| : A : push
+ // -----:-------------------------------:------:-------
+ // : XX XX XX FF Fz zz|zz YY YY YY : :
+ // 0 : ^^| : B : wait
+ // -1 : |^^ : A : push
+ // -----:-------------------------------:------:-------
+ // : XX XX XX XX FF Fz|zz zz YY YY : :
+ // 0 : ^^| : C : wait
+ // -1 : |^^ : B : wait
+ // -2 : | ^^ : A : push
+ // -----:-------------------------------:------:-------
+ // : XX XX XX XX XX FF|Fz zz zz YY : :
+ // 0 : ^^| : D : wait
+ // -1 : |^^ : C : wait
+ // -2 : | ^^ : B : wait
+ // -3 : | ^^ : A : push
+ // -----:-------------------------------:------:-------
+ bool A = ((scanner & 0xFFF00000) == 0xFFF00000);
+ bool B = ((scanner & 0xFFF000) == 0xFFF000);
+ bool C = ((scanner & 0xFFF0) == 0xFFF0);
+ bool D = ((scanner & 0xFF) == 0xFF);
+ if (A || (!B && !C && !D)) {
+ // Actually we cannot push out an overfull packet. So we'll have to
+ // adjust the byte count and payload start as necessary. If the byte
+ // count gets negative we'll have to append the excess from fragment's
+ // tail to the next PES header.
+ int bite = data + packetTodo - payload;
+ const uchar *excessData = fragmentData + fragmentLen + bite;
+ // A negative byte count means to drop some bytes from the current
+ // fragment's tail, to not exceed the maximum packet size.
+ if (!PushOutPacket(ResultBuffer, payload, bite)) {
+ DroppedData("cAudioRepacker: result buffer overflow", Count - done);
+ return;
+ }
+ // create a continuation PES header
+ pesHeaderLen = 0;
+ pesHeader[pesHeaderLen++] = 0x00;
+ pesHeader[pesHeaderLen++] = 0x00;
+ pesHeader[pesHeaderLen++] = 0x01;
+ pesHeader[pesHeaderLen++] = Data[3]; // audio stream ID
+ pesHeader[pesHeaderLen++] = 0x00; // length still unknown
+ pesHeader[pesHeaderLen++] = 0x00; // length still unknown
+
+ if (mpegLevel == phMPEG2) {
+ pesHeader[pesHeaderLen++] = 0x80;
+ pesHeader[pesHeaderLen++] = 0x00;
+ pesHeader[pesHeaderLen++] = 0x00;
+ }
+ else
+ pesHeader[pesHeaderLen++] = 0x0F;
+
+ // copy any excess data
+ while (bite++ < 0) {
+ // append the excess data here
+ pesHeader[pesHeaderLen++] = *excessData++;
+ packetTodo++;
+ }
+ // the next packet's payload will begin here
+ payload = data + packetTodo;
+ // as there is no length information available, assume the
+ // maximum we can hold in one PES packet
+ packetTodo += maxPacketSize - pesHeaderLen;
+ }
+ }
+ }
+ // The packet is done. Now store any remaining data into fragment buffer
+ // if we are no longer syncing.
+ if (state != syncing) {
+ // append the PES header ...
+ int bite = pesHeaderLen;
+ pesHeaderLen = 0;
+ if (bite > 0) {
+ memcpy(fragmentData + fragmentLen, pesHeader, bite);
+ fragmentLen += bite;
+ }
+ // append payload. It may contain part of an audio frame header at it's
+ // end, which will be removed when the next packet gets processed.
+ bite = data - payload;
+ if (bite > 0) {
+ memcpy(fragmentData + fragmentLen, payload, bite);
+ fragmentLen += bite;
+ }
+ }
+ // report that syncing dropped some bytes
+ if (skippedBytes > SkippedBytesLimit) {
+ esyslog("cAudioRepacker: skipped %d bytes while syncing on next audio frame", skippedBytes - SkippedBytesLimit);
+ skippedBytes = SkippedBytesLimit;
+ }
+}
+
+int cAudioRepacker::BreakAt(const uchar *Data, int Count)
+{
+ int PesPayloadOffset = 0;
+
+ if (AnalyzePesHeader(Data, Count, PesPayloadOffset) <= phInvalid)
+ return -1; // not enough data for test
+
+ // determine amount of data to fill up packet and to append next audio frame header
+ int packetRemainder = PesPayloadOffset + packetTodo + 4;
+
+ // just detect end of an audio frame
+ if (state == scanFrame) {
+ // when remaining audio frame size is known, then omit scanning
+ if (frameTodo > 0) {
+ // determine amount of data to fill up audio frame and to append next audio frame header
+ int remaining = PesPayloadOffset + frameTodo + 4;
+ if (remaining < packetRemainder)
+ return remaining;
+ return packetRemainder;
+ }
+ // setup local scanner
+ if (localStart < 0) {
+ localScanner = scanner;
+ localStart = 0;
+ }
+ // start where we've stopped at the last run
+ const uchar *data = Data + PesPayloadOffset + localStart;
+ const uchar *limit = Data + Count;
+ // scan data
+ while (data < limit) {
+ localStart++;
+ localScanner <<= 8;
+ localScanner |= *data++;
+ // check whether the next audio frame follows
+ if ((localScanner & 0xFFF00000) == 0xFFF00000 && IsValidAudioHeader(localScanner))
+ return data - Data;
+ }
+ }
+ // just fill up packet and append next audio frame header
+ return packetRemainder;
+}
+
// --- cDolbyRepacker --------------------------------------------------------
class cDolbyRepacker : public cRepacker {
@@ -587,7 +965,7 @@ bool cDolbyRepacker::FinishRemainder(cRingBufferLinear *ResultBuffer, const ucha
// output a previous fragment first
if (fragmentLen > 0) {
Bite = fragmentLen;
- int n = Put(ResultBuffer, fragmentData, Bite);
+ int n = Put(ResultBuffer, fragmentData, Bite, fragmentLen + fragmentTodo);
if (Bite != n) {
Reset();
return false;
@@ -595,7 +973,7 @@ bool cDolbyRepacker::FinishRemainder(cRingBufferLinear *ResultBuffer, const ucha
fragmentLen = 0;
}
Bite = fragmentTodo;
- int n = Put(ResultBuffer, Data, Bite);
+ int n = Put(ResultBuffer, Data, Bite, Bite);
if (Bite != n) {
Reset();
Done += n;
@@ -631,13 +1009,13 @@ bool cDolbyRepacker::StartNewPacket(cRingBufferLinear *ResultBuffer, const uchar
Bite = pesHeaderLen;
// enough data available to put PES packet into buffer?
if (packetLen - pesHeaderLen <= Todo) {
- int n = Put(ResultBuffer, pesHeader, Bite);
+ int n = Put(ResultBuffer, pesHeader, Bite, packetLen);
if (Bite != n) {
Reset();
return false;
}
Bite = packetLen - pesHeaderLen;
- n = Put(ResultBuffer, Data, Bite);
+ n = Put(ResultBuffer, Data, Bite, Bite);
if (Bite != n) {
Reset();
Done += n;
@@ -890,7 +1268,8 @@ private:
uint8_t hlength;
int mpeg;
uint8_t check;
- int which;
+ int mpeg1_required;
+ int mpeg1_stuffing;
bool done;
cRingBufferLinear *resultBuffer;
int tsErrors;
@@ -956,11 +1335,8 @@ void cTS2PES::store(uint8_t *Data, int Count)
{
if (repacker)
repacker->Repack(resultBuffer, Data, Count);
- else {
- int n = resultBuffer->Put(Data, Count);
- if (n != Count)
- esyslog("ERROR: result buffer overflow, dropped %d out of %d byte", Count - n, Count);
- }
+ else
+ cRepacker::Put(resultBuffer, Data, Count, Count);
}
void cTS2PES::reset_ipack(void)
@@ -973,7 +1349,8 @@ void cTS2PES::reset_ipack(void)
hlength = 0;
mpeg = 0;
check = 0;
- which = 0;
+ mpeg1_required = 0;
+ mpeg1_stuffing = 0;
done = false;
count = 0;
}
@@ -1057,7 +1434,7 @@ void cTS2PES::instant_repack(const uint8_t *Buf, int Count)
{
int c = 0;
- while (c < Count && (mpeg == 0 || (mpeg == 1 && found < 7) || (mpeg == 2 && found < 9)) && (found < 5 || !done)) {
+ while (c < Count && (mpeg == 0 || (mpeg == 1 && found < mpeg1_required) || (mpeg == 2 && found < 9)) && (found < 5 || !done)) {
switch (found ) {
case 0:
case 1:
@@ -1103,6 +1480,7 @@ void cTS2PES::instant_repack(const uint8_t *Buf, int Count)
plength = ntohs(*pl);
c += 2;
found += 2;
+ mpeg1_stuffing = 0;
}
else {
plen[0] = Buf[c];
@@ -1115,32 +1493,53 @@ void cTS2PES::instant_repack(const uint8_t *Buf, int Count)
unsigned short *pl = (unsigned short *)plen;
plength = ntohs(*pl);
found++;
+ mpeg1_stuffing = 0;
}
break;
case 6:
if (!done) {
flag1 = Buf[c++];
found++;
- if ((flag1 & 0xC0) == 0x80 )
- mpeg = 2;
- else {
- hlength = 0;
- which = 0;
- mpeg = 1;
- flag2 = 0;
+ if (mpeg1_stuffing == 0) { // first stuffing iteration: determine MPEG level
+ if ((flag1 & 0xC0) == 0x80)
+ mpeg = 2;
+ else
+ mpeg = 1;
+ mpeg1_required = 7;
+ }
+ if (mpeg == 1) {
+ if (flag1 == 0xFF) { // MPEG1 stuffing
+ if (++mpeg1_stuffing > 16)
+ found = 0; // invalid MPEG1 header
+ else { // ignore stuffing
+ found--;
+ if (plength > 0)
+ plength--;
+ }
+ }
+ else if ((flag1 & 0xC0) == 0x40) // STD_buffer_scale/size
+ mpeg1_required += 2;
+ else if (flag1 != 0x0F && (flag1 & 0xF0) != 0x20 && (flag1 & 0xF0) != 0x30)
+ found = 0; // invalid MPEG1 header
+ else {
+ flag2 = 0;
+ hlength = 0;
+ }
}
}
break;
case 7:
- if (!done && mpeg == 2) {
+ if (!done && (mpeg == 2 || mpeg1_required > 7)) {
flag2 = Buf[c++];
found++;
}
break;
case 8:
- if (!done && mpeg == 2) {
+ if (!done && (mpeg == 2 || mpeg1_required > 7)) {
hlength = Buf[c++];
found++;
+ if (mpeg == 1 && hlength != 0x0F && (hlength & 0xF0) != 0x20 && (hlength & 0xF0) != 0x30)
+ found = 0; // invalid MPEG1 header
}
break;
default:
@@ -1151,7 +1550,7 @@ void cTS2PES::instant_repack(const uint8_t *Buf, int Count)
if (!plength)
plength = MMAX_PLENGTH - 6;
- if (done || ((mpeg == 2 && found >= 9) || (mpeg == 1 && found >= 7))) {
+ if (done || ((mpeg == 2 && found >= 9) || (mpeg == 1 && found >= mpeg1_required))) {
switch (cid) {
case AUDIO_STREAM_S ... AUDIO_STREAM_E:
case VIDEO_STREAM_S ... VIDEO_STREAM_E:
@@ -1163,8 +1562,13 @@ void cTS2PES::instant_repack(const uint8_t *Buf, int Count)
write_ipack(&hlength, 1);
}
- if (mpeg == 1 && found == 7)
+ if (mpeg == 1 && found == mpeg1_required) {
write_ipack(&flag1, 1);
+ if (mpeg1_required > 7) {
+ write_ipack(&flag2, 1);
+ write_ipack(&hlength, 1);
+ }
+ }
if (mpeg == 2 && (flag2 & PTS_ONLY) && found < 14) {
while (c < Count && found < 14) {
@@ -1277,7 +1681,12 @@ cRemux::cRemux(int VPid, const int *APids, const int *DPids, const int *SPids, b
if (APids) {
int n = 0;
while (*APids && numTracks < MAXTRACKS && n < MAXAPIDS)
+#define TEST_cAudioRepacker
+#ifdef TEST_cAudioRepacker
+ ts2pes[numTracks++] = new cTS2PES(*APids++, resultBuffer, IPACKS, 0xC0 + n++, 0x00, new cAudioRepacker);
+#else
ts2pes[numTracks++] = new cTS2PES(*APids++, resultBuffer, IPACKS, 0xC0 + n++);
+#endif
}
if (DPids) {
int n = 0;
@@ -1321,10 +1730,9 @@ int cRemux::ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &Pic
// If the return value is -1 the packet was not completely in the buffer.
int Length = GetPacketLength(Data, Count, Offset);
if (Length > 0) {
- if (Length >= 8) {
- int i = Offset + 8; // the minimum length of the video packet header
- i += Data[i] + 1; // possible additional header bytes
- for (; i < Offset + Length - 5; i++) {
+ int PesPayloadOffset = 0;
+ if (AnalyzePesHeader(Data + Offset, Length, PesPayloadOffset) >= phMPEG1) {
+ for (int i = Offset + PesPayloadOffset; i < Offset + Length - 5; i++) {
if (Data[i] == 0 && Data[i + 1] == 0 && Data[i + 2] == 1) {
switch (Data[i + 3]) {
case SC_PICTURE: PictureType = (Data[i + 5] >> 3) & 0x07;
@@ -1499,8 +1907,9 @@ void cRemux::Clear(void)
void cRemux::SetBrokenLink(uchar *Data, int Length)
{
- if (Length > 9 && Data[0] == 0 && Data[1] == 0 && Data[2] == 1 && (Data[3] & 0xF0) == VIDEO_STREAM_S) {
- for (int i = Data[8] + 9; i < Length - 7; i++) { // +9 to skip video packet header
+ int PesPayloadOffset = 0;
+ if (AnalyzePesHeader(Data, Length, PesPayloadOffset) >= phMPEG1 && (Data[3] & 0xF0) == VIDEO_STREAM_S) {
+ for (int i = PesPayloadOffset; i < Length - 7; i++) {
if (Data[i] == 0 && Data[i + 1] == 0 && Data[i + 2] == 1 && Data[i + 3] == 0xB8) {
if (!(Data[i + 7] & 0x40)) // set flag only if GOP is not closed
Data[i + 7] |= 0x20;
diff --git a/remux.h b/remux.h
index 0cd570e0..ab296553 100644
--- a/remux.h
+++ b/remux.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: remux.h 1.14 2005/08/07 10:28:07 kls Exp $
+ * $Id: remux.h 1.15 2005/08/26 13:22:19 kls Exp $
*/
#ifndef __REMUX_H
@@ -15,6 +15,15 @@
#include "ringbuffer.h"
#include "tools.h"
+enum ePesHeader {
+ phNeedMoreData = -1,
+ phInvalid = 0,
+ phMPEG1 = 1,
+ phMPEG2 = 2
+ };
+
+ePesHeader AnalyzePesHeader(const uchar *Data, int Count, int &PesPayloadOffset, bool *ContinuationHeader = NULL);
+
// Picture types:
#define NO_PICTURE 0
#define I_FRAME 1