Version 1.3.19

- Making sure at least the default skin is available at program start in case a
  plugin needs to issue an error message (thanks to Achim Tuffentshammer for
  reporting a crash in such a case). Also checking if there is a current skin
  in cSkins::Message().
- Completed the Finnish OSD texts and fixed internationalization of the text
  for "Setup/DVB/Audio language(s)" (thanks to Rolf Ahrenberg).
- Completed the Estonian OSD texts and switched to iso8859-13 character set
  (thanks to Arthur Konovalov).
- Made cCondWait::SleepMs() sleep at least 3ms to avoid a possible busy wait.
- Fixed canceling the LIRC thread (thanks to Marco Schlüßler for pointing out
  this one).
- The "Green" button in the "Main" menu is now always "Audio", since the audio
  channel might be changed even if there is only one actual audio PID.
- Fixed handling the '-E' option which was broken in version 1.3.18 (thanks to
  Christian Jacobsen for reporting this one).
- Added 'channels.conf.terr' entries for Mainz (thanks to Michael Heyse).
- Implemented cDolbyRepacker for better handling of Dolby Digital PES packets
  (thanks to Reinhard Nissl).
- Fixed playing files with PES packets longer than 2048 byte through the full
  featured DVB card (thanks to Marco Kremer for reporting this one and providing
  a test sample).
- Recording and Transfer Mode now handle more than 2 audio PIDs. For this the
  interfaces of the following functions have been changed:
  cTransferControl::cTransferControl()
  cTransfer::cTransfer()
  cRecorder::cRecorder()
  cReceiver::cReceiver()
  cRemux::cRemux()
- Fixed a possible race condition in cDevice::Action() and cTSBuffer::Action()
  (thanks to Stefan Huelswitt).
- Extended some buffer sizes to allow handling HDTV streams (thanks to Reinhard
  Nissl).
- Added 'channels.conf.terr' entries for Düsseldorf and Köln (thanks to Walter Koch).
- Falling back to 'stereo' when switching channels in case the user had switched
  to 'left' or 'right' (suggested by Rolf Groppe).
- Completed the Danish OSD texts (thanks to Mogens Elneff).
- Recording and Transfer Mode can now handle up to 8 Dolby Digital tracks (thanks
  to Marco Schlüßler for a patch that implements substream handling into
  cDevice::PlayPesPacket(), and Reinhard Nissl for adding substream handling to
  cDolbyRepacker).
- Added PlayPes(NULL, 0) to cTransfer::Action() when clearing the transfer buffer
  to avoid overflows (thanks to Marco Schlüßler for pointing this out).
This commit is contained in:
Klaus Schmidinger
2005-01-23 18:00:00 +01:00
parent fb5cccb2df
commit e36fe18c48
37 changed files with 19664 additions and 222 deletions

412
remux.c
View File

@@ -8,14 +8,296 @@
* the Linux DVB driver's 'tuxplayer' example and were rewritten to suit
* VDR's needs.
*
* $Id: remux.c 1.23 2004/12/18 13:15:02 kls Exp $
* 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.27 2005/01/23 12:56:39 kls Exp $
*/
#include "remux.h"
#include <stdlib.h>
#include "channels.h"
#include "thread.h"
#include "tools.h"
// --- cRepacker -------------------------------------------------------------
class cRepacker {
protected:
uint8_t subStreamId;
public:
cRepacker(void) { subStreamId = 0; }
virtual ~cRepacker() {}
virtual int Put(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count) = 0;
virtual int BreakAt(const uchar *Data, int Count) = 0;
void SetSubStreamId(uint8_t SubStreamId) { subStreamId = SubStreamId; }
};
// --- cDolbyRepacker --------------------------------------------------------
class cDolbyRepacker : public cRepacker {
private:
static int frameSizes[];
uchar fragmentData[6 + 65535];
int fragmentLen;
uchar pesHeader[6 + 3 + 255 + 4 + 4];
int pesHeaderLen;
uchar chk1;
uchar chk2;
int ac3todo;
enum {
find_0b,
find_77,
store_chk1,
store_chk2,
get_length
} state;
void Reset(void);
public:
cDolbyRepacker(void);
virtual int Put(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count);
virtual int BreakAt(const uchar *Data, int Count);
};
int cDolbyRepacker::frameSizes[] = {
// fs = 48 kHz
64, 64, 80, 80, 96, 96, 112, 112, 128, 128, 160, 160, 192, 192, 224, 224,
256, 256, 320, 320, 384, 384, 448, 448, 512, 512, 640, 640, 768, 768, 896, 896,
1024, 1024, 1152, 1152, 1280, 1280, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// fs = 44.1 kHz
69, 70, 87, 88, 104, 105, 121, 122, 139, 140, 174, 175, 208, 209, 243, 244,
278, 279, 348, 349, 417, 418, 487, 488, 557, 558, 696, 697, 835, 836, 975, 976,
1114, 1115, 1253, 1254, 1393, 1394, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// fs = 32 kHz
96, 96, 120, 120, 144, 144, 168, 168, 192, 192, 240, 240, 288, 288, 336, 336,
384, 384, 480, 480, 576, 576, 672, 672, 768, 768, 960, 960, 1152, 1152, 1344, 1344,
1536, 1536, 1728, 1728, 1920, 1920, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
//
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
cDolbyRepacker::cDolbyRepacker(void)
{
pesHeader[0] = 0x00;
pesHeader[1] = 0x00;
pesHeader[2] = 0x01;
pesHeader[3] = 0xBD;
pesHeader[4] = 0x00;
pesHeader[5] = 0x00;
Reset();
}
void cDolbyRepacker::Reset()
{
state = find_0b;
pesHeader[6] = 0x80;
pesHeader[7] = 0x00;
pesHeader[8] = 0x00;
pesHeaderLen = 9;
ac3todo = 0;
chk1 = 0;
chk2 = 0;
fragmentLen = 0;
}
int cDolbyRepacker::Put(cRingBufferLinear *ResultBuffer, const uchar *Data, int Count)
{
// check for MPEG 2
if ((Data[6] & 0xC0) != 0x80)
return 0;
// copy header information for later use
if (Data[6] != 0x80 || Data[7] != 0x00 || Data[8] != 0x00) {
pesHeaderLen = Data[8] + 6 + 3;
memcpy(pesHeader, Data, pesHeaderLen);
}
const uchar *data = Data + pesHeaderLen;
int done = pesHeaderLen;
int todo = Count - done;
// finish remainder of ac3 frame
if (ac3todo > 0) {
int bite;
// enough data available to put PES packet into buffer?
if (ac3todo <= todo) {
// output a previous fragment first
if (fragmentLen > 0) {
bite = fragmentLen;
int n = ResultBuffer->Put(fragmentData, bite);
if (bite != n) {
Reset();
return done;
}
fragmentLen = 0;
}
bite = ac3todo;
int n = ResultBuffer->Put(data, bite);
if (bite != n) {
Reset();
return done + n;
}
}
else {
// copy the fragment into separate buffer for later processing
bite = todo;
if (fragmentLen + bite > (int)sizeof(fragmentData)) {
Reset();
return done;
}
memcpy(fragmentData + fragmentLen, data, bite);
fragmentLen += bite;
}
data += bite;
done += bite;
todo -= bite;
ac3todo -= bite;
}
// look for 0x0B 0x77 <chk1> <chk2> <frameSize>
while (todo > 0) {
switch (state) {
case find_0b:
if (*data == 0x0B)
++(int &)state;
data++;
done++;
todo--;
continue;
case find_77:
if (*data != 0x77) {
state = find_0b;
continue;
}
data++;
done++;
todo--;
++(int &)state;
continue;
case store_chk1:
chk1 = *data++;
done++;
todo--;
++(int &)state;
continue;
case store_chk2:
chk2 = *data++;
done++;
todo--;
++(int &)state;
continue;
case get_length: {
ac3todo = 2 * frameSizes[*data];
// frameSizeCode was invalid => restart searching
if (ac3todo <= 0) {
if (chk1 == 0x0B) {
if (chk2 == 0x77) {
state = store_chk1;
continue;
}
if (chk2 == 0x0B) {
state = find_77;
continue;
}
state = find_0b;
continue;
}
if (chk2 == 0x0B) {
state = find_77;
continue;
}
state = find_0b;
continue;
}
// adjust PES packet length and output packet
if (subStreamId) {
pesHeader[pesHeaderLen++] = subStreamId;
pesHeader[pesHeaderLen++] = 0x00;
pesHeader[pesHeaderLen++] = 0x00;
pesHeader[pesHeaderLen++] = 0x00;
}
int packetLen = pesHeaderLen - 6 + ac3todo;
pesHeader[4] = packetLen >> 8;
pesHeader[5] = packetLen & 0xFF;
pesHeader[pesHeaderLen + 0] = 0x0B;
pesHeader[pesHeaderLen + 1] = 0x77;
pesHeader[pesHeaderLen + 2] = chk1;
pesHeader[pesHeaderLen + 3] = chk2;
ac3todo -= 4;
int bite = pesHeaderLen + 4;
// enough data available to put PES packet into buffer?
if (ac3todo <= todo) {
int n = ResultBuffer->Put(pesHeader, bite);
if (bite != n) {
Reset();
return done;
}
bite = ac3todo;
n = ResultBuffer->Put(data, bite);
if (bite != n) {
Reset();
return done + n;
}
}
else {
// copy the fragment into separate buffer for later processing
if (fragmentLen + bite > (int)sizeof(fragmentData)) {
Reset();
return done;
}
memcpy(fragmentData + fragmentLen, pesHeader, bite);
fragmentLen += bite;
bite = todo;
if (fragmentLen + bite > (int)sizeof(fragmentData)) {
Reset();
return done;
}
memcpy(fragmentData + fragmentLen, data, bite);
fragmentLen += bite;
}
data += bite;
done += bite;
todo -= bite;
ac3todo -= bite;
// prepare for next packet
pesHeader[6] = 0x80;
pesHeader[7] = 0x00;
pesHeader[8] = 0x00;
pesHeaderLen = 9;
state = find_0b;
}
}
}
return Count;
}
int cDolbyRepacker::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;
// break after fragment tail?
if (ac3todo > 0)
return headerLen + ac3todo;
// enough data for test?
if (Count < headerLen + 5)
return -1;
const uchar *data = Data + headerLen;
// break after ac3 frame?
if (data[0] == 0x0B && data[1] == 0x77 && frameSizes[data[4]] > 0)
return headerLen + frameSizes[data[4]];
return -1;
}
// --- cTS2PES ---------------------------------------------------------------
#include <netinet/in.h>
@@ -52,7 +334,7 @@
#define ADAPT_FIELD 0x20
#define MAX_PLENGTH 0xFFFF // the maximum PES packet length (theoretically)
#define MMAX_PLENGTH (8*MAX_PLENGTH) // some stations send PES packets that are extremely large, e.g. DVB-T in Finland
#define MMAX_PLENGTH (64*MAX_PLENGTH) // some stations send PES packets that are extremely large, e.g. DVB-T in Finland or HDTV 1920x1080
#define IPACKS 2048
@@ -64,12 +346,14 @@
class cTS2PES {
private:
int pid;
int size;
int found;
int count;
uint8_t *buf;
uint8_t cid;
uint8_t audioCid;
uint8_t subStreamId;
int plength;
uint8_t plen[2];
uint8_t flag1;
@@ -83,6 +367,7 @@ private:
int tsErrors;
int ccErrors;
int ccCounter;
cRepacker *repacker;
static uint8_t headr[];
void store(uint8_t *Data, int Count);
void reset_ipack(void);
@@ -90,19 +375,25 @@ private:
void write_ipack(const uint8_t *Data, int Count);
void instant_repack(const uint8_t *Buf, int Count);
public:
cTS2PES(cRingBufferLinear *ResultBuffer, int Size, uint8_t AudioCid = 0x00);
cTS2PES(int Pid, cRingBufferLinear *ResultBuffer, int Size, uint8_t AudioCid = 0x00, uint8_t SubStreamId = 0x00, cRepacker *Repacker = NULL);
~cTS2PES();
int Pid(void) { return pid; }
void ts_to_pes(const uint8_t *Buf); // don't need count (=188)
void Clear(void);
};
uint8_t cTS2PES::headr[] = { 0x00, 0x00, 0x01 };
cTS2PES::cTS2PES(cRingBufferLinear *ResultBuffer, int Size, uint8_t AudioCid)
cTS2PES::cTS2PES(int Pid, cRingBufferLinear *ResultBuffer, int Size, uint8_t AudioCid, uint8_t SubStreamId, cRepacker *Repacker)
{
pid = Pid;
resultBuffer = ResultBuffer;
size = Size;
audioCid = AudioCid;
subStreamId = SubStreamId;
repacker = Repacker;
if (repacker)
repacker->SetSubStreamId(subStreamId);
tsErrors = 0;
ccErrors = 0;
@@ -119,6 +410,7 @@ cTS2PES::~cTS2PES()
if (tsErrors || ccErrors)
dsyslog("cTS2PES got %d TS errors, %d TS continuity errors", tsErrors, ccErrors);
free(buf);
delete repacker;
}
void cTS2PES::Clear(void)
@@ -128,7 +420,7 @@ void cTS2PES::Clear(void)
void cTS2PES::store(uint8_t *Data, int Count)
{
int n = resultBuffer->Put(Data, 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);
}
@@ -178,17 +470,48 @@ void cTS2PES::write_ipack(const uint8_t *Data, int Count)
count = 6;
}
if (count + Count < size) {
memcpy(buf + count, Data, Count);
count += Count;
// determine amount of data to process
int bite = Count;
if (count + bite > size)
bite = size - count;
if (repacker) {
int breakAt = repacker->BreakAt(buf, count);
// avoid memcpy of data after break location
if (0 <= breakAt && breakAt < count + bite) {
bite = breakAt - count;
if (bite < 0) // should never happen
bite = 0;
}
}
else {
int rest = size - count;
memcpy(buf + count, Data, rest);
count += rest;
memcpy(buf + count, Data, bite);
count += bite;
if (repacker) {
// determine break location
int breakAt = repacker->BreakAt(buf, count);
if (breakAt > size) // won't fit into packet?
breakAt = -1;
if (breakAt > count) // not enough data?
breakAt = -1;
// push out data before break location
if (breakAt > 0) {
// adjust bite if above memcpy was to large
bite -= count - breakAt;
count = breakAt;
send_ipack();
// recurse for data after break location
if (Count - bite > 0)
write_ipack(Data + bite, Count - bite);
}
}
// push out data when buffer is full
if (count >= size) {
send_ipack();
if (Count - rest > 0)
write_ipack(Data + rest, Count - rest);
// recurse for remaining data
if (Count - bite > 0)
write_ipack(Data + bite, Count - bite);
}
}
@@ -395,35 +718,42 @@ void cTS2PES::ts_to_pes(const uint8_t *Buf) // don't need count (=188)
#define RESULTBUFFERSIZE KILOBYTE(256)
cRemux::cRemux(int VPid, int APid1, int APid2, int DPid1, int DPid2, bool ExitOnFailure)
cRemux::cRemux(int VPid, const int *APids, const int *DPids, const int *SPids, bool ExitOnFailure)
{
vPid = VPid;
aPid1 = APid1;
aPid2 = APid2;
dPid1 = DPid1;
dPid2 = DPid2;
exitOnFailure = ExitOnFailure;
isRadio = VPid == 0 || VPid == 1 || VPid == 0x1FFF;
numUPTerrors = 0;
synced = false;
skipped = 0;
numTracks = 0;
resultSkipped = 0;
resultBuffer = new cRingBufferLinear(RESULTBUFFERSIZE, IPACKS, false, "Result");
resultBuffer->SetTimeouts(0, 100);
vTS2PES = new cTS2PES(resultBuffer, IPACKS);
aTS2PES1 = new cTS2PES(resultBuffer, IPACKS, 0xC0);
aTS2PES2 = aPid2 ? new cTS2PES(resultBuffer, IPACKS, 0xC1) : NULL;
dTS2PES1 = dPid1 ? new cTS2PES(resultBuffer, IPACKS) : NULL;
//XXX don't yet know how to tell apart primary and secondary DD data...
dTS2PES2 = /*XXX dPid2 ? new cTS2PES(resultBuffer, IPACKS) : XXX*/ NULL;
if (VPid)
ts2pes[numTracks++] = new cTS2PES(VPid, resultBuffer, IPACKS);
if (APids) {
int n = 0;
while (*APids && numTracks < MAXTRACKS && n < MAXAPIDS)
ts2pes[numTracks++] = new cTS2PES(*APids++, resultBuffer, IPACKS, 0xC0 + n++);
}
if (DPids) {
int n = 0;
while (*DPids && numTracks < MAXTRACKS && n < MAXDPIDS)
ts2pes[numTracks++] = new cTS2PES(*DPids++, resultBuffer, IPACKS, 0x00, 0x80 + n++, new cDolbyRepacker);
}
/* future...
if (SPids) {
int n = 0;
while (*SPids && numTracks < MAXTRACKS && n < MAXSPIDS)
ts2pes[numTracks++] = new cTS2PES(*SPids++, resultBuffer, IPACKS, 0x00, 0x28 + n++);
}
*/
}
cRemux::~cRemux()
{
delete vTS2PES;
delete aTS2PES1;
delete aTS2PES2;
delete dTS2PES1;
delete dTS2PES2;
for (int t = 0; t < numTracks; t++)
delete ts2pes[t];
delete resultBuffer;
}
@@ -495,11 +825,12 @@ int cRemux::Put(const uchar *Data, int Count)
break; // A cTS2PES might write one full packet and also a small rest
int pid = GetPid(Data + i + 1);
if (Data[i + 3] & 0x10) { // got payload
if (pid == vPid) vTS2PES->ts_to_pes(Data + i);
else if (pid == aPid1) aTS2PES1->ts_to_pes(Data + i);
else if (pid == aPid2 && aTS2PES2) aTS2PES2->ts_to_pes(Data + i);
else if (pid == dPid1 && dTS2PES1) dTS2PES1->ts_to_pes(Data + i);
else if (pid == dPid2 && dTS2PES2) dTS2PES2->ts_to_pes(Data + i);
for (int t = 0; t < numTracks; t++) {
if (ts2pes[t]->Pid() == pid) {
ts2pes[t]->ts_to_pes(Data + i);
break;
}
}
}
used += TS_SIZE;
}
@@ -537,7 +868,7 @@ uchar *cRemux::Get(int &Count, uchar *PictureType)
// Special VPID case to enable recording radio channels:
if (vPid == 0 || vPid == 1 || vPid == 0x1FFF) {
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'
@@ -615,11 +946,8 @@ void cRemux::Del(int Count)
void cRemux::Clear(void)
{
if (vTS2PES) vTS2PES->Clear();
if (aTS2PES1) aTS2PES1->Clear();
if (aTS2PES2) aTS2PES2->Clear();
if (dTS2PES1) dTS2PES1->Clear();
if (dTS2PES2) dTS2PES2->Clear();
for (int t = 0; t < numTracks; t++)
ts2pes[t]->Clear();
resultBuffer->Clear();
}