summaryrefslogtreecommitdiff
path: root/remux.c
diff options
context:
space:
mode:
authorKlaus Schmidinger <kls (at) cadsoft (dot) de>2005-08-28 18:00:00 +0200
committerKlaus Schmidinger <kls (at) cadsoft (dot) de>2005-08-28 18:00:00 +0200
commitad40eaa28e6e9f0fa594937453b5ae53b88dce75 (patch)
tree6bd222711a6a20d4977d572dcab2bb526c27d743 /remux.c
parentdab203efe9e24f1dade33ee1da6a39b26f8501f0 (diff)
downloadvdr-patch-lnbsharing-ad40eaa28e6e9f0fa594937453b5ae53b88dce75.tar.gz
vdr-patch-lnbsharing-ad40eaa28e6e9f0fa594937453b5ae53b88dce75.tar.bz2
Version 1.3.31vdr-1.3.31
- 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). - Modified handling of audio packets for radio channels in remux.c (thanks to Reinhard Nissl). - Updated the Danish OSD texts (thanks to Mogens Elneff). - Fixed the EPG scan, so that it doesn't use the primary device if that is currently in Transfer-Mode from itself (thanks to Marcus Hilbrich for a bug report that lead to this). - Removed the TUNER_LOCK_TIMEOUT in cDevice::AttachReceiver() since it caused more trouble than it fixed. - Fixed detecting short channel names for "Kabel Deutschland", who uses a comma as delimiter (thanks to Marco Schlüßler). - Moved cMenuEditTimer and cMenuEvent to menu.h so that plugins can use it (suggested by Thomas Günther). - The new static function cString::sprintf() can be used to easily create a formatted string. - Plugins can now implement their own SVDRP commands (based on a patch from Hardy Flor). See PLUGINS.html, section "SVDRP commands" for details. The SVDRP commands of a plugin are accessed through the new SVDRP command PLUG. See PLUGINS/src/svdrpdemo for an example of how to use this feature. - The new SVDRP command PLAY can be used to start replaying a recording (thanks to Hardy Flor). - The new SVDRP command EDIT can be used to start the editing process of a recording (based on the CUTR patch by Harald Milz).
Diffstat (limited to 'remux.c')
-rw-r--r--remux.c749
1 files changed, 581 insertions, 168 deletions
diff --git a/remux.c b/remux.c
index c980bd8..1028050 100644
--- a/remux.c
+++ b/remux.c
@@ -8,10 +8,10 @@
* the Linux DVB driver's 'tuxplayer' example and were rewritten to suit
* VDR's needs.
*
- * The cDolbyRepacker code was originally written by Reinhard Nissl <rnissl@gmx.de>,
+ * The cRepacker family's 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.42 2005/08/28 11:46:44 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,50 @@ 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:
+ bool initiallySyncing;
+ 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) { initiallySyncing = true; maxPacketSize = 6 + 65535; subStreamId = 0; }
+ virtual ~cRepacker() {}
+ virtual void Reset(void) { /* initiallySyncing = true; */ }
+ 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 +131,25 @@ 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)
{
+ cRepacker::Reset();
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 +157,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 +176,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 +196,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 +204,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 +242,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;
}
@@ -264,16 +295,14 @@ void cVideoRepacker::Repack(cRingBufferLinear *ResultBuffer, const uchar *Data,
// the byte count get's negative then the current buffer ends in a
// partitial start code that must be stripped off, as it shall be put
// in the next packet.
- if (!PushOutPacket(ResultBuffer, payload, data - 3 - payload)) {
- DroppedData("cVideoRepacker: result buffer overflow", Count - (done - 3));
- return;
- }
+ PushOutPacket(ResultBuffer, payload, data - 3 - payload);
// go on with syncing to the next picture
state = syncing;
}
if (state == syncing) {
- // report that syncing dropped some bytes
- if (skippedBytes > SkippedBytesLimit)
+ if (initiallySyncing) // omit report for the typical initial case
+ initiallySyncing = false;
+ else if (skippedBytes > SkippedBytesLimit) // report that syncing dropped some bytes
esyslog("cVideoRepacker: skipped %d bytes to sync on next picture", skippedBytes - SkippedBytesLimit);
skippedBytes = 0;
// if there is a PES header available, then use it ...
@@ -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;
@@ -379,10 +408,7 @@ void cVideoRepacker::Repack(cRingBufferLinear *ResultBuffer, const uchar *Data,
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("cVideoRepacker: result buffer overflow", Count - done);
- return;
- }
+ PushOutPacket(ResultBuffer, payload, bite);
// create a continuation PES header
pesHeaderLen = 0;
pesHeader[pesHeaderLen++] = 0x00;
@@ -392,7 +418,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;
@@ -434,7 +460,8 @@ void cVideoRepacker::Repack(cRingBufferLinear *ResultBuffer, const uchar *Data,
}
// report that syncing dropped some bytes
if (skippedBytes > SkippedBytesLimit) {
- esyslog("cVideoRepacker: skipped %d bytes while syncing on next picture", skippedBytes - SkippedBytesLimit);
+ if (!initiallySyncing) // omit report for the typical initial case
+ esyslog("cVideoRepacker: skipped %d bytes while syncing on next picture", skippedBytes - SkippedBytesLimit);
skippedBytes = SkippedBytesLimit;
}
}
@@ -443,7 +470,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 +502,381 @@ int cVideoRepacker::BreakAt(const uchar *Data, int Count)
return PesPayloadOffset + packetTodo + 4;
}
+// --- cAudioRepacker --------------------------------------------------------
+
+class cAudioRepacker : public cCommonRepacker {
+private:
+ static int bitRates[2][3][16];
+ enum eState {
+ syncing,
+ scanFrame
+ } state;
+ int frameTodo;
+ int frameSize;
+ static bool IsValidAudioHeader(uint32_t Header, bool Mpeg2, 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[2][3][16] = { // all values are specified as kbits/s
+ // MPEG 1, Layer I
+ 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, -1,
+ // MPEG 1, Layer II
+ 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, -1,
+ // MPEG 1, Layer III
+ 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, -1,
+ // MPEG 2, Layer I
+ 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, -1,
+ // MPEG 2, Layer II/III
+ 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1,
+ // MPEG 2, Layer II/III
+ 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -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, bool Mpeg2, 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 && !Mpeg2) // reserved in MPEG 1
+ 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[2][4] = { // all values are specified in Hz
+ // MPEG 1
+ 44100, 48000, 32000, -1,
+ // MPEG 2
+ 22050, 24000, 16000, -1
+ };
+
+ static int slots_per_frame[2][3] = {
+ // MPEG 1, Layer I, II, III
+ 12, 144, 144,
+ // MPEG 2, Layer I, II, III
+ 12, 144, 72
+ };
+
+ int mpegIndex = 1 - id;
+ int layerIndex = 3 - layer;
+
+ // Layer I (i. e., layerIndex == 0) has a larger slot size
+ int slotSize = (layerIndex == 0) ? 4 : 1; // bytes
+
+ int br = 1000 * bitRates[mpegIndex][layerIndex][bitrate_index]; // bits/s
+ int sf = samplingFrequencies[mpegIndex][sampling_frequency];
+
+ int N = slots_per_frame[mpegIndex][layerIndex] * br / sf; // slots
+
+ *FrameSize = (N + padding_bit) * slotSize; // 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--;
+ if (frameTodo == 0 && state == scanFrame) {
+ // the current audio frame is is done now. So push out the packet to
+ // start a new packet for the next audio frame.
+ PushOutPacket(ResultBuffer, payload, data - payload);
+ // go on with syncing to the next audio frame
+ state = syncing;
+ }
+ }
+ // did we reach an audio frame header?
+ scanner <<= 8;
+ scanner |= *data;
+ if ((scanner & 0xFFF00000) == 0xFFF00000) {
+ if (frameTodo <= 0 && IsValidAudioHeader(scanner, mpegLevel == phMPEG2, &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.
+ PushOutPacket(ResultBuffer, payload, data - 3 - payload);
+ // go on with syncing to the next audio frame
+ state = syncing;
+ }
+ if (state == syncing) {
+ if (initiallySyncing) // omit report for the typical initial case
+ initiallySyncing = false;
+ else if (skippedBytes > SkippedBytesLimit) // report that syncing dropped some bytes
+ 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;
+ // maximum we can hold in one PES packet
+ packetTodo = maxPacketSize - pesHeaderLen;
+ // expected remainder of audio frame: so far we have read 3 bytes from the frame header
+ frameTodo = frameSize - 3;
+ // 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.
+ PushOutPacket(ResultBuffer, payload, bite);
+ // 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) {
+ if (!initiallySyncing) // omit report for the typical initial case
+ 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;
+
+ ePesHeader MpegLevel = AnalyzePesHeader(Data, Count, PesPayloadOffset);
+ if (MpegLevel <= 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, MpegLevel == phMPEG2))
+ return data - Data;
+ }
+ }
+ // just fill up packet and append next audio frame header
+ return packetRemainder;
+}
+
// --- cDolbyRepacker --------------------------------------------------------
class cDolbyRepacker : public cRepacker {
@@ -501,8 +903,8 @@ private:
int skippedBytes;
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);
+ bool FinishRemainder(cRingBufferLinear *ResultBuffer, const uchar *const Data, const int Todo, int &Bite);
+ bool StartNewPacket(cRingBufferLinear *ResultBuffer, const uchar *const Data, const int Todo, int &Bite);
public:
cDolbyRepacker(void);
virtual void Reset(void);
@@ -569,6 +971,7 @@ void cDolbyRepacker::ResetPesHeader(bool ContinuationFrame)
void cDolbyRepacker::Reset(void)
{
+ cRepacker::Reset();
ResetPesHeader();
state = find_0b;
ac3todo = 0;
@@ -580,26 +983,24 @@ void cDolbyRepacker::Reset(void)
skippedBytes = 0;
}
-bool cDolbyRepacker::FinishRemainder(cRingBufferLinear *ResultBuffer, const uchar *const Data, const int Todo, int &Done, int &Bite)
+bool cDolbyRepacker::FinishRemainder(cRingBufferLinear *ResultBuffer, const uchar *const Data, const int Todo, int &Bite)
{
+ bool success = true;
// enough data available to put PES packet into buffer?
if (fragmentTodo <= Todo) {
// output a previous fragment first
if (fragmentLen > 0) {
Bite = fragmentLen;
- int n = Put(ResultBuffer, fragmentData, Bite);
- if (Bite != n) {
- Reset();
- return false;
- }
+ int n = Put(ResultBuffer, fragmentData, Bite, fragmentLen + fragmentTodo);
+ if (Bite != n)
+ success = false;
fragmentLen = 0;
}
Bite = fragmentTodo;
- int n = Put(ResultBuffer, Data, Bite);
- if (Bite != n) {
- Reset();
- Done += n;
- return false;
+ if (success) {
+ int n = Put(ResultBuffer, Data, Bite, Bite);
+ if (Bite != n)
+ success = false;
}
fragmentTodo = 0;
// ac3 frame completely processed?
@@ -609,19 +1010,16 @@ bool cDolbyRepacker::FinishRemainder(cRingBufferLinear *ResultBuffer, const ucha
else {
// copy the fragment into separate buffer for later processing
Bite = Todo;
- if (fragmentLen + Bite > (int)sizeof(fragmentData)) {
- Reset();
- return false;
- }
memcpy(fragmentData + fragmentLen, Data, Bite);
fragmentLen += Bite;
fragmentTodo -= Bite;
}
- return true;
+ return success;
}
-bool cDolbyRepacker::StartNewPacket(cRingBufferLinear *ResultBuffer, const uchar *const Data, const int Todo, int &Done, int &Bite)
+bool cDolbyRepacker::StartNewPacket(cRingBufferLinear *ResultBuffer, const uchar *const Data, const int Todo, int &Bite)
{
+ bool success = true;
int packetLen = pesHeaderLen + ac3todo;
// limit packet to maximum size
if (packetLen > maxPacketSize)
@@ -631,17 +1029,14 @@ 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);
- if (Bite != n) {
- Reset();
- return false;
- }
+ int n = Put(ResultBuffer, pesHeader, Bite, packetLen);
+ if (Bite != n)
+ success = false;
Bite = packetLen - pesHeaderLen;
- n = Put(ResultBuffer, Data, Bite);
- if (Bite != n) {
- Reset();
- Done += n;
- return false;
+ if (success) {
+ n = Put(ResultBuffer, Data, Bite, Bite);
+ if (Bite != n)
+ success = false;
}
// ac3 frame completely processed?
if (Bite >= ac3todo)
@@ -650,24 +1045,16 @@ bool cDolbyRepacker::StartNewPacket(cRingBufferLinear *ResultBuffer, const uchar
else {
fragmentTodo = packetLen;
// copy the pesheader into separate buffer for later processing
- if (fragmentLen + Bite > (int)sizeof(fragmentData)) {
- Reset();
- return false;
- }
memcpy(fragmentData + fragmentLen, pesHeader, Bite);
fragmentLen += Bite;
fragmentTodo -= Bite;
// copy the fragment into separate buffer for later processing
Bite = Todo;
- if (fragmentLen + Bite > (int)sizeof(fragmentData)) {
- Reset();
- return false;
- }
memcpy(fragmentData + fragmentLen, Data, Bite);
fragmentLen += Bite;
fragmentTodo -= Bite;
}
- return true;
+ return success;
}
void cDolbyRepacker::Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count)
@@ -761,8 +1148,9 @@ void cDolbyRepacker::Repack(cRingBufferLinear *ResultBuffer, const uchar *Data,
state = find_0b;
continue;
}
- // report that syncing dropped some bytes
- if (skippedBytes > SkippedBytesLimit)
+ if (initiallySyncing) // omit report for the typical initial case
+ initiallySyncing = false;
+ else if (skippedBytes > SkippedBytesLimit) // report that syncing dropped some bytes
esyslog("cDolbyRepacker: skipped %d bytes to sync on next AC3 frame", skippedBytes - SkippedBytesLimit);
skippedBytes = 0;
// append read data to header for common output processing
@@ -776,18 +1164,11 @@ void cDolbyRepacker::Repack(cRingBufferLinear *ResultBuffer, const uchar *Data,
case output_packet: {
int bite = 0;
// finish remainder of ac3 frame?
- if (fragmentTodo > 0) {
- if (!FinishRemainder(ResultBuffer, data, todo, done, bite)) {
- DroppedData("cDolbyRepacker: result buffer overflow", Count - done);
- return;
- }
- }
+ if (fragmentTodo > 0)
+ FinishRemainder(ResultBuffer, data, todo, bite);
else {
// start a new packet
- if (!StartNewPacket(ResultBuffer, data, todo, done, bite)) {
- DroppedData("cDolbyRepacker: result buffer overflow", Count - done);
- return;
- }
+ StartNewPacket(ResultBuffer, data, todo, bite);
// prepare for next (continuation) packet
ResetPesHeader(state == output_packet);
}
@@ -800,7 +1181,8 @@ void cDolbyRepacker::Repack(cRingBufferLinear *ResultBuffer, const uchar *Data,
}
// report that syncing dropped some bytes
if (skippedBytes > SkippedBytesLimit) {
- esyslog("cDolbyRepacker: skipped %d bytes while syncing on next AC3 frame", skippedBytes - 4);
+ if (!initiallySyncing) // omit report for the typical initial case
+ esyslog("cDolbyRepacker: skipped %d bytes while syncing on next AC3 frame", skippedBytes - 4);
skippedBytes = SkippedBytesLimit;
}
}
@@ -890,7 +1272,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 +1339,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 +1353,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 +1438,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 +1484,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 +1497,54 @@ 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 +1555,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 +1567,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 +1686,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 +1735,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;
@@ -1409,19 +1822,6 @@ uchar *cRemux::Get(int &Count, uchar *PictureType)
return resultBuffer->Get(Count);
#endif
- // Special VPID case to enable recording radio channels:
-
- if (isRadio) {
- // XXX actually '0' should be enough, but '1' must be used with encrypted channels (driver bug?)
- // XXX also allowing 0x1FFF to not break Michael Paar's original patch,
- // XXX but it would probably be best to only use '0'
- // Force syncing of radio channels to avoid "no useful data" error
- synced = true;
- if (PictureType)
- *PictureType = I_FRAME;
- return resultBuffer->Get(Count);
- }
-
// Check for frame borders:
if (PictureType)
@@ -1466,6 +1866,18 @@ uchar *cRemux::Get(int &Count, uchar *PictureType)
l = GetPacketLength(data, resultCount, i);
if (l < 0)
return resultData;
+ if (isRadio) {
+ if (!synced) {
+ if (PictureType)
+ *PictureType = I_FRAME;
+ resultSkipped = i; // will drop everything before this position
+ synced = true;
+ }
+ else if (Count)
+ return resultData;
+ else if (PictureType)
+ *PictureType = I_FRAME;
+ }
}
if (synced) {
if (!Count)
@@ -1499,8 +1911,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;