summaryrefslogtreecommitdiff
path: root/remux.c
diff options
context:
space:
mode:
authorKlaus Schmidinger <kls (at) cadsoft (dot) de>2005-08-07 18:00:00 +0200
committerKlaus Schmidinger <kls (at) cadsoft (dot) de>2005-08-07 18:00:00 +0200
commit4c5de2888331fb4372eba0b0f4364fd9a0197b17 (patch)
treefbacaa13bb20870db03ad49516713c3f00ec3b0e /remux.c
parentfef3aa3a7fff0e3b4745532a6b1a157b45cb9643 (diff)
downloadvdr-patch-lnbsharing-4c5de2888331fb4372eba0b0f4364fd9a0197b17.tar.gz
vdr-patch-lnbsharing-4c5de2888331fb4372eba0b0f4364fd9a0197b17.tar.bz2
Version 1.3.28vdr-1.3.28
- Added a sleep in cDvbPlayer::Action() in case there is no data to send to the device, which avoids a busy loop on very fast machines (thanks to Martin Wache). - Modified the description of cDevice::Poll() to avoid misunderstandings. - Updated Croatian language texts (thanks to Drazen Dupor). - cDvbPlayer::Goto() now appends a Sequence End Code to get the image shown immediately with softdevices (thanks to Reinhard Nissl). - Reactivated cVideoRepacker in remux.c after some fixes (thanks to Reinhard Nissl). - Removed the fix for handling VPS timers, so that they only record if the event they are assigned to actually has the given VPS time. This has caused repeating VPS timers to stop recording prematurely. - Avoiding duplicate components in EPG events when reading epg.data or in the PUTE SVDRP command (thanks to Olaf Titz for reporting this one). - Added the command line options '--lirc', '--rcu' and '--no-kbd' to allow setting the remote control at runtime (based on a patch by Darren Salt). - Now checking whether timers or channels are currently being edited via the menu before making changes through SVDRP (thanks to Andreas Brugger for reporting a problem with this). - Files and directories are now created with rights according to the shell's umask settings (thanks to Andreas Brachold). - Fixed the cChannel copy constructor (thanks to Marcel Wiesweg for pointing out a problem with it). - Fixed an out-of-bounds memory access with audio language ids (thanks to Matthias Lenk for reporting, and Udo Richter for suggesting a fix). - Updated the Finnish OSD texts (thanks to Rolf Ahrenberg). - Added missing storing of the MenuScrollPage parameter (thanks to Frank Krömmelbein). - Added cRemux::SetTimeouts() for better use of cRemux in a single thread (thanks to Udo Richter for reporting a problem with this). - Modified cEITScanner::Process() so that it uses the primary device if it is replaying and is the only device that provides the given transponder, and that a forced EPG scan works even if EPG scan timeout is set to 0 (thanks to Bernhard Stegmaier for reporting a problem with this). - Fixed cDvbSpuBitmap::putPixel() (thanks to Reinhard Nissl). - Fixed setting system time to avoid time jumps in case of faulty data (thanks to Andreas Böttger). - Fixed a memory leak in the SVDRP command LSTE (thanks to Stefan Huelswitt).
Diffstat (limited to 'remux.c')
-rw-r--r--remux.c241
1 files changed, 181 insertions, 60 deletions
diff --git a/remux.c b/remux.c
index c4e7b20..4a8682b 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.35 2005/06/19 10:17:00 kls Exp $
+ * $Id: remux.c 1.36 2005/07/30 10:23:00 kls Exp $
*/
#include "remux.h"
@@ -26,17 +26,92 @@ 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 int Put(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count) = 0;
+ 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)
+{
+ if (Count < 7)
+ return -1; // too short
+
+ if ((Data[6] & 0xC0) == 0x80) { // MPEG 2
+ if (Count < 9)
+ return -1; // too short
+
+ PesPayloadOffset = 6 + 3 + Data[8];
+ if (Count < PesPayloadOffset)
+ return -1; // too short
+
+ if (ContinuationHeader)
+ *ContinuationHeader = ((Data[6] == 0x80) && !Data[7] && !Data[8]);
+
+ return 2; // MPEG 2
+ }
+
+ // check for MPEG 1 ...
+ PesPayloadOffset = 6;
+
+ // skip up to 16 stuffing bytes
+ for (int i = 0; i < 16; i++) {
+ if (Data[PesPayloadOffset] != 0xFF)
+ break;
+
+ if (Count <= ++PesPayloadOffset)
+ return -1; // too short
+ }
+
+ // skip STD_buffer_scale/size
+ if ((Data[PesPayloadOffset] & 0xC0) == 0x40) {
+ PesPayloadOffset += 2;
+
+ if (Count <= PesPayloadOffset)
+ return -1; // too short
+ }
+
+ if (ContinuationHeader)
+ *ContinuationHeader = false;
+
+ if ((Data[PesPayloadOffset] & 0xF0) == 0x20) {
+ // skip PTS only
+ PesPayloadOffset += 5;
+ }
+ else if ((Data[PesPayloadOffset] & 0xF0) == 0x30) {
+ // skip PTS and DTS
+ PesPayloadOffset += 10;
+ }
+ else if (Data[PesPayloadOffset] == 0x0F) {
+ // continuation header
+ PesPayloadOffset++;
+
+ if (ContinuationHeader)
+ *ContinuationHeader = true;
+ }
+ else
+ return 0; // unknown
+
+ if (Count < PesPayloadOffset)
+ return -1; // too short
+
+ return 1; // MPEG 1
+}
+
// --- cVideoRepacker --------------------------------------------------------
class cVideoRepacker : public cRepacker {
@@ -61,7 +136,7 @@ private:
public:
cVideoRepacker(void);
virtual void Reset(void);
- virtual int Put(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count);
+ virtual void Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count);
virtual int BreakAt(const uchar *Data, int Count);
virtual int QuerySnoopSize() { return 4; }
};
@@ -95,7 +170,7 @@ bool cVideoRepacker::PushOutPacket(cRingBufferLinear *ResultBuffer, const uchar
// to strip off any partially contained start code.
int Bite = fragmentLen + (Count >= 0 ? 0 : Count);
// put data into result buffer
- int n = ResultBuffer->Put(fragmentData, Bite);
+ int n = Put(ResultBuffer, fragmentData, Bite);
if (n != Bite) {
Reset();
return false;
@@ -110,7 +185,7 @@ bool cVideoRepacker::PushOutPacket(cRingBufferLinear *ResultBuffer, const uchar
// to strip off any partially contained start code.
int Bite = pesHeaderLen + (Count >= 0 ? 0 : Count);
// put data into result buffer
- int n = ResultBuffer->Put(pesHeader, Bite);
+ int n = Put(ResultBuffer, pesHeader, Bite);
if (n != Bite) {
Reset();
return false;
@@ -122,7 +197,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 = ResultBuffer->Put(Data, Bite);
+ int n = Put(ResultBuffer, Data, Bite);
if (n != Bite) {
Reset();
return false;
@@ -132,23 +207,29 @@ bool cVideoRepacker::PushOutPacket(cRingBufferLinear *ResultBuffer, const uchar
return true;
}
-int cVideoRepacker::Put(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count)
+void cVideoRepacker::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;
-
- // check for MPEG 2
- 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);
+ int pesPayloadOffset = 0;
+ bool continuationHeader = false;
+ int mpegLevel = AnalyzePesHeader(Data, Count, pesPayloadOffset, &continuationHeader);
+ if (mpegLevel <= 0) {
+ DroppedData("cVideoRepacker: no valid PES packet header found", Count);
+ return;
}
+ if (!continuationHeader) {
+ // backup PES header
+ pesHeaderBackupLen = pesPayloadOffset;
+ memcpy(pesHeaderBackup, Data, pesHeaderBackupLen);
+ }
// skip PES header
- int done = 6 + 3 + Data[8];
+ int done = pesPayloadOffset;
int todo = Count - done;
const uchar *data = Data + done;
// remember start of the data
@@ -191,15 +272,17 @@ int cVideoRepacker::Put(cRingBufferLinear *ResultBuffer, const uchar *Data, int
// 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))
- return done - 3;
+ if (!PushOutPacket(ResultBuffer, payload, data - 3 - payload)) {
+ DroppedData("cVideoRepacker: result buffer overflow", Count - (done - 3));
+ return;
+ }
// go on with syncing to the next picture
state = syncing;
}
if (state == syncing) {
// report that syncing dropped some bytes
- if (skippedBytes > 4)
- esyslog("cVideoRepacker: skipped %d bytes to sync on next picture", skippedBytes - 4);
+ if (skippedBytes > SkippedBytesLimit)
+ esyslog("cVideoRepacker: skipped %d bytes to sync on next picture", skippedBytes - SkippedBytesLimit);
skippedBytes = 0;
// if there is a PES header available, then use it ...
if (pesHeaderBackupLen > 0) {
@@ -222,9 +305,14 @@ int cVideoRepacker::Put(cRingBufferLinear *ResultBuffer, const uchar *Data, int
pesHeader[pesHeaderLen++] = Data[3]; // video stream ID
pesHeader[pesHeaderLen++] = 0x00; // length still unknown
pesHeader[pesHeaderLen++] = 0x00; // length still unknown
- pesHeader[pesHeaderLen++] = 0x80;
- pesHeader[pesHeaderLen++] = 0x00;
- pesHeader[pesHeaderLen++] = 0x00;
+
+ if (mpegLevel == 2) {
+ pesHeader[pesHeaderLen++] = 0x80;
+ pesHeader[pesHeaderLen++] = 0x00;
+ pesHeader[pesHeaderLen++] = 0x00;
+ }
+ else
+ pesHeader[pesHeaderLen++] = 0x0F;
}
// append the first three bytes of the start code
pesHeader[pesHeaderLen++] = 0x00;
@@ -299,8 +387,10 @@ int cVideoRepacker::Put(cRingBufferLinear *ResultBuffer, const uchar *Data, int
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))
- return done;
+ if (!PushOutPacket(ResultBuffer, payload, bite)) {
+ DroppedData("cVideoRepacker: result buffer overflow", Count - done);
+ return;
+ }
// create a continuation PES header
pesHeaderLen = 0;
pesHeader[pesHeaderLen++] = 0x00;
@@ -309,9 +399,15 @@ int cVideoRepacker::Put(cRingBufferLinear *ResultBuffer, const uchar *Data, int
pesHeader[pesHeaderLen++] = Data[3]; // video stream ID
pesHeader[pesHeaderLen++] = 0x00; // length still unknown
pesHeader[pesHeaderLen++] = 0x00; // length still unknown
- pesHeader[pesHeaderLen++] = 0x80;
- pesHeader[pesHeaderLen++] = 0x00;
- pesHeader[pesHeaderLen++] = 0x00;
+
+ if (mpegLevel == 2) {
+ 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
@@ -344,22 +440,20 @@ int cVideoRepacker::Put(cRingBufferLinear *ResultBuffer, const uchar *Data, int
fragmentLen += bite;
}
}
- // we've eaten the whole packet ;-)
- return Count;
+ // report that syncing dropped some bytes
+ if (skippedBytes > SkippedBytesLimit) {
+ esyslog("cVideoRepacker: skipped %d bytes while syncing on next picture", skippedBytes - SkippedBytesLimit);
+ skippedBytes = SkippedBytesLimit;
+ }
}
int cVideoRepacker::BreakAt(const uchar *Data, int Count)
{
- // enough data for test?
- if (Count < 6 + 3)
- return -1;
- // check for MPEG 2
- if ((Data[6] & 0xC0) != 0x80)
- return -1;
- int headerLen = Data[8] + 6 + 3;
- // enough data for test?
- if (Count < headerLen)
- return -1;
+ int PesPayloadOffset = 0;
+
+ if (AnalyzePesHeader(Data, Count, PesPayloadOffset) <= 0)
+ return -1; // not enough data for test
+
// just detect end of picture
if (state == scanPicture) {
// setup local scanner
@@ -368,7 +462,7 @@ int cVideoRepacker::BreakAt(const uchar *Data, int Count)
localStart = 0;
}
// start where we've stopped at the last run
- const uchar *data = Data + headerLen + localStart;
+ const uchar *data = Data + PesPayloadOffset + localStart;
const uchar *limit = Data + Count;
// scan data
while (data < limit) {
@@ -386,7 +480,7 @@ int cVideoRepacker::BreakAt(const uchar *Data, int Count)
}
}
// just fill up packet and append next start code
- return headerLen + packetTodo + 4;
+ return PesPayloadOffset + packetTodo + 4;
}
// --- cDolbyRepacker --------------------------------------------------------
@@ -412,6 +506,7 @@ private:
get_length,
output_packet
} state;
+ 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);
@@ -419,7 +514,7 @@ private:
public:
cDolbyRepacker(void);
virtual void Reset(void);
- virtual int Put(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count);
+ virtual void Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count);
virtual int BreakAt(const uchar *Data, int Count);
};
@@ -490,6 +585,7 @@ void cDolbyRepacker::Reset(void)
fragmentLen = 0;
fragmentTodo = 0;
pesHeaderBackupLen = 0;
+ skippedBytes = 0;
}
bool cDolbyRepacker::FinishRemainder(cRingBufferLinear *ResultBuffer, const uchar *const Data, const int Todo, int &Done, int &Bite)
@@ -499,7 +595,7 @@ bool cDolbyRepacker::FinishRemainder(cRingBufferLinear *ResultBuffer, const ucha
// output a previous fragment first
if (fragmentLen > 0) {
Bite = fragmentLen;
- int n = ResultBuffer->Put(fragmentData, Bite);
+ int n = Put(ResultBuffer, fragmentData, Bite);
if (Bite != n) {
Reset();
return false;
@@ -507,7 +603,7 @@ bool cDolbyRepacker::FinishRemainder(cRingBufferLinear *ResultBuffer, const ucha
fragmentLen = 0;
}
Bite = fragmentTodo;
- int n = ResultBuffer->Put(Data, Bite);
+ int n = Put(ResultBuffer, Data, Bite);
if (Bite != n) {
Reset();
Done += n;
@@ -543,13 +639,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 = ResultBuffer->Put(pesHeader, Bite);
+ int n = Put(ResultBuffer, pesHeader, Bite);
if (Bite != n) {
Reset();
return false;
}
Bite = packetLen - pesHeaderLen;
- n = ResultBuffer->Put(Data, Bite);
+ n = Put(ResultBuffer, Data, Bite);
if (Bite != n) {
Reset();
Done += n;
@@ -582,11 +678,16 @@ bool cDolbyRepacker::StartNewPacket(cRingBufferLinear *ResultBuffer, const uchar
return true;
}
-int cDolbyRepacker::Put(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count)
+void cDolbyRepacker::Repack(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count)
{
+ // synchronisation is detected some bytes after frame start.
+ const int SkippedBytesLimit = 4;
+
// check for MPEG 2
- if ((Data[6] & 0xC0) != 0x80)
- return 0;
+ if ((Data[6] & 0xC0) != 0x80) {
+ DroppedData("cDolbyRepacker: MPEG 2 PES header expected", Count);
+ return;
+ }
// backup PES header
if (Data[6] != 0x80 || Data[7] != 0x00 || Data[8] != 0x00) {
@@ -616,6 +717,7 @@ int cDolbyRepacker::Put(cRingBufferLinear *ResultBuffer, const uchar *Data, int
data++;
done++;
todo--;
+ skippedBytes++; // collect number of skipped bytes while syncing
continue;
case find_77:
if (*data != 0x77) {
@@ -625,18 +727,21 @@ int cDolbyRepacker::Put(cRingBufferLinear *ResultBuffer, const uchar *Data, int
data++;
done++;
todo--;
+ skippedBytes++; // collect number of skipped bytes while syncing
++(int &)state;
continue;
case store_chk1:
chk1 = *data++;
done++;
todo--;
+ skippedBytes++; // collect number of skipped bytes while syncing
++(int &)state;
continue;
case store_chk2:
chk2 = *data++;
done++;
todo--;
+ skippedBytes++; // collect number of skipped bytes while syncing
++(int &)state;
continue;
case get_length:
@@ -664,6 +769,10 @@ int cDolbyRepacker::Put(cRingBufferLinear *ResultBuffer, const uchar *Data, int
state = find_0b;
continue;
}
+ // report that syncing dropped some bytes
+ if (skippedBytes > SkippedBytesLimit)
+ esyslog("cDolbyRepacker: skipped %d bytes to sync on next AC3 frame", skippedBytes - SkippedBytesLimit);
+ skippedBytes = 0;
// append read data to header for common output processing
pesHeader[pesHeaderLen++] = 0x0B;
pesHeader[pesHeaderLen++] = 0x77;
@@ -676,13 +785,17 @@ int cDolbyRepacker::Put(cRingBufferLinear *ResultBuffer, const uchar *Data, int
int bite = 0;
// finish remainder of ac3 frame?
if (fragmentTodo > 0) {
- if (!FinishRemainder(ResultBuffer, data, todo, done, bite))
- return done;
+ if (!FinishRemainder(ResultBuffer, data, todo, done, bite)) {
+ DroppedData("cDolbyRepacker: result buffer overflow", Count - done);
+ return;
+ }
}
else {
// start a new packet
- if (!StartNewPacket(ResultBuffer, data, todo, done, bite))
- return done;
+ if (!StartNewPacket(ResultBuffer, data, todo, done, bite)) {
+ DroppedData("cDolbyRepacker: result buffer overflow", Count - done);
+ return;
+ }
// prepare for next (continuation) packet
ResetPesHeader(state == output_packet);
}
@@ -693,7 +806,11 @@ int cDolbyRepacker::Put(cRingBufferLinear *ResultBuffer, const uchar *Data, int
}
}
}
- return Count;
+ // report that syncing dropped some bytes
+ if (skippedBytes > SkippedBytesLimit) {
+ esyslog("cDolbyRepacker: skipped %d bytes while syncing on next AC3 frame", skippedBytes - 4);
+ skippedBytes = SkippedBytesLimit;
+ }
}
int cDolbyRepacker::BreakAt(const uchar *Data, int Count)
@@ -845,9 +962,13 @@ void cTS2PES::Clear(void)
void cTS2PES::store(uint8_t *Data, int Count)
{
- int n = repacker ? repacker->Put(resultBuffer, Data, Count) : resultBuffer->Put(Data, Count);
- if (n != Count)
- esyslog("ERROR: result buffer overflow, dropped %d out of %d byte", Count - n, 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);
+ }
}
void cTS2PES::reset_ipack(void)
@@ -867,7 +988,7 @@ void cTS2PES::reset_ipack(void)
void cTS2PES::send_ipack(void)
{
- if (count < 10)
+ if (count <= ((mpeg == 2) ? 9 : 7)) // skip empty packets
return;
buf[3] = (AUDIO_STREAM_S <= cid && cid <= AUDIO_STREAM_E && audioCid) ? audioCid : cid;
buf[4] = (uint8_t)(((count - 6) & 0xFF00) >> 8);
@@ -1155,7 +1276,7 @@ cRemux::cRemux(int VPid, const int *APids, const int *DPids, const int *SPids, b
resultBuffer = new cRingBufferLinear(RESULTBUFFERSIZE, IPACKS, false, "Result");
resultBuffer->SetTimeouts(0, 100);
if (VPid)
-//#define TEST_cVideoRepacker
+#define TEST_cVideoRepacker
#ifdef TEST_cVideoRepacker
ts2pes[numTracks++] = new cTS2PES(VPid, resultBuffer, IPACKS, 0x00, 0x00, new cVideoRepacker);
#else