1
0
mirror of https://github.com/VDR4Arch/vdr.git synced 2023-10-10 13:36:52 +02:00

First step towards switching to TS (Transport Stream) as recording format

This commit is contained in:
Klaus Schmidinger 2008-08-15 14:49:34 +02:00
parent 2ee1e61d35
commit d9e56db9fc
19 changed files with 988 additions and 336 deletions

22
HISTORY
View File

@ -5762,7 +5762,7 @@ Video Disk Recorder Revision History
- Increased the time between checking the CAM status to 500ms to avoid problems - Increased the time between checking the CAM status to 500ms to avoid problems
with some CAMs (reported by Arthur Konovalov). with some CAMs (reported by Arthur Konovalov).
2008-05-02: Version 1.7.1 2008-08-15: Version 1.7.1
- Adapted the tuning code to the new DVBFE_SET_DELSYS API (thanks to Reinhard Nissl). - Adapted the tuning code to the new DVBFE_SET_DELSYS API (thanks to Reinhard Nissl).
VDR now uses the driver from http://jusst.de/hg/multiproto_plus. VDR now uses the driver from http://jusst.de/hg/multiproto_plus.
@ -5784,6 +5784,26 @@ Video Disk Recorder Revision History
- Removed the compile time option VFAT to allow users of precompiled binary - Removed the compile time option VFAT to allow users of precompiled binary
distributions to have full control over whether or not to use the --vfat option distributions to have full control over whether or not to use the --vfat option
at runtime (suggested by Michael Nork). at runtime (suggested by Michael Nork).
- First step towards switching to TS (Transport Stream) as recording format:
+ The new function cDevice::PlayTs() is used to play TS packets.
+ The new functions cDevice::PlayTsVideo() and cDevice::PlayTsAudio()
are used to play video and audio TS packets, respectively.
+ The new function cAudio::PlayTs() is used to play audio TS packets.
+ The new class cPatPmtGenerator is used to generate a PAT/PMT pair that precedes
the TS data in Transfer Mode.
+ The new class cPatPmtParser is used by cDevice to parse the PAT/PMT data in a
TS in order to find out which streams it contains.
+ The new class cTsToPes is used to convert TS packets to a PES packet.
+ cTransfer no longer uses cRemux, and doesn't run a separate thread any more.
It just generates a PAT/PMT and sends all received TS packets to the primary
device's PlayTs().
+ Live subtitle display no longer uses a ring buffer and separate thread.
+ cPesAssembler has been removed. Old VDR recordings only contain complete PES
packets.
+ Since a TS needs to have a PAT/PMT, which requires the video stream type to
be explicitly given, the format of the VPID field in the channels.conf file
and the SVDRP commands NEWC/MODC/LSTC has been extended. The video stream type
now follows the VPID and optional PPID, separated by an '=' sign.
2008-05-03: Version 1.6.0-2 2008-05-03: Version 1.6.0-2

31
audio.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: audio.c 1.5 2006/05/28 15:03:24 kls Exp $ * $Id: audio.c 2.1 2008/07/06 11:42:58 kls Exp $
*/ */
#include "audio.h" #include "audio.h"
@ -32,6 +32,12 @@ void cAudios::PlayAudio(const uchar *Data, int Length, uchar Id)
audio->Play(Data, Length, Id); audio->Play(Data, Length, Id);
} }
void cAudios::PlayTsAudio(const uchar *Data, int Length)
{
for (cAudio *audio = First(); audio; audio = Next(audio))
audio->PlayTs(Data, Length);
}
void cAudios::MuteAudio(bool On) void cAudios::MuteAudio(bool On)
{ {
for (cAudio *audio = First(); audio; audio = Next(audio)) for (cAudio *audio = First(); audio; audio = Next(audio))
@ -86,6 +92,29 @@ void cExternalAudio::Play(const uchar *Data, int Length, uchar Id)
} }
} }
void cExternalAudio::PlayTs(const uchar *Data, int Length)
{
if (command && !mute) {
if (pipe || pipe.Open(command, "w")) {
int written = 0;
while (Length > 0) {
int w = fwrite(Data + written, 1, Length, pipe);
if (w < 0) {
LOG_ERROR;
break;
}
Length -= w;
written += w;
}
}
else {
esyslog("ERROR: can't open pipe to audio command '%s'", command);
free(command);
command = NULL;
}
}
}
void cExternalAudio::Mute(bool On) void cExternalAudio::Mute(bool On)
{ {
mute = On; mute = On;

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: audio.h 1.3 2005/02/12 12:20:19 kls Exp $ * $Id: audio.h 2.1 2008/07/06 11:39:21 kls Exp $
*/ */
#ifndef __AUDIO_H #ifndef __AUDIO_H
@ -24,6 +24,11 @@ public:
///< be copied and processed in a separate thread. The Data is always a ///< be copied and processed in a separate thread. The Data is always a
///< complete PES audio packet. Id indicates the type of audio data this ///< complete PES audio packet. Id indicates the type of audio data this
///< packet holds. ///< packet holds.
virtual void PlayTs(const uchar *Data, int Length) = 0;
///< Plays the given block of audio Data. Must return as soon as possible.
///< If the entire block of data can't be processed immediately, it must
///< be copied and processed in a separate thread. The Data is always a
///< complete TS audio packet.
virtual void Mute(bool On) = 0; virtual void Mute(bool On) = 0;
///< Immediately sets the audio device to be silent (On==true) or to ///< Immediately sets the audio device to be silent (On==true) or to
///< normal replay (On==false). ///< normal replay (On==false).
@ -34,6 +39,7 @@ public:
class cAudios : public cList<cAudio> { class cAudios : public cList<cAudio> {
public: public:
void PlayAudio(const uchar *Data, int Length, uchar Id); void PlayAudio(const uchar *Data, int Length, uchar Id);
void PlayTsAudio(const uchar *Data, int Length);
void MuteAudio(bool On); void MuteAudio(bool On);
void ClearAudio(void); void ClearAudio(void);
}; };
@ -49,6 +55,7 @@ public:
cExternalAudio(const char *Command); cExternalAudio(const char *Command);
virtual ~cExternalAudio(); virtual ~cExternalAudio();
virtual void Play(const uchar *Data, int Length, uchar Id); virtual void Play(const uchar *Data, int Length, uchar Id);
virtual void PlayTs(const uchar *Data, int Length);
virtual void Mute(bool On); virtual void Mute(bool On);
virtual void Clear(void); virtual void Clear(void);
}; };

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: channels.c 2.2 2008/04/12 13:49:12 kls Exp $ * $Id: channels.c 2.3 2008/07/06 12:59:41 kls Exp $
*/ */
#include "channels.h" #include "channels.h"
@ -505,10 +505,10 @@ static int IntArrayToString(char *s, const int *a, int Base = 10, const char n[]
return q - s; return q - s;
} }
void cChannel::SetPids(int Vpid, int Ppid, int *Apids, char ALangs[][MAXLANGCODE2], int *Dpids, char DLangs[][MAXLANGCODE2], int *Spids, char SLangs[][MAXLANGCODE2], int Tpid) void cChannel::SetPids(int Vpid, int Ppid, int Vtype, int *Apids, char ALangs[][MAXLANGCODE2], int *Dpids, char DLangs[][MAXLANGCODE2], int *Spids, char SLangs[][MAXLANGCODE2], int Tpid)
{ {
int mod = CHANNELMOD_NONE; int mod = CHANNELMOD_NONE;
if (vpid != Vpid || ppid != Ppid || tpid != Tpid) if (vpid != Vpid || ppid != Ppid || vtype != Vtype || tpid != Tpid)
mod |= CHANNELMOD_PIDS; mod |= CHANNELMOD_PIDS;
int m = IntArraysDiffer(apids, Apids, alangs, ALangs) | IntArraysDiffer(dpids, Dpids, dlangs, DLangs) | IntArraysDiffer(spids, Spids, slangs, SLangs); int m = IntArraysDiffer(apids, Apids, alangs, ALangs) | IntArraysDiffer(dpids, Dpids, dlangs, DLangs) | IntArraysDiffer(spids, Spids, slangs, SLangs);
if (m & STRDIFF) if (m & STRDIFF)
@ -542,9 +542,10 @@ void cChannel::SetPids(int Vpid, int Ppid, int *Apids, char ALangs[][MAXLANGCODE
q = NewSpidsBuf; q = NewSpidsBuf;
q += IntArrayToString(q, Spids, 10, SLangs); q += IntArrayToString(q, Spids, 10, SLangs);
*q = 0; *q = 0;
dsyslog("changing pids of channel %d from %d+%d:%s:%s:%d to %d+%d:%s:%s:%d", Number(), vpid, ppid, OldApidsBuf, OldSpidsBuf, tpid, Vpid, Ppid, NewApidsBuf, NewSpidsBuf, Tpid); dsyslog("changing pids of channel %d from %d+%d=%d:%s:%s:%d to %d+%d=%d:%s:%s:%d", Number(), vpid, ppid, vtype, OldApidsBuf, OldSpidsBuf, tpid, Vpid, Ppid, Vtype, NewApidsBuf, NewSpidsBuf, Tpid);
vpid = Vpid; vpid = Vpid;
ppid = Ppid; ppid = Ppid;
vtype = Vtype;
for (int i = 0; i < MAXAPIDS; i++) { for (int i = 0; i < MAXAPIDS; i++) {
apids[i] = Apids[i]; apids[i] = Apids[i];
strn0cpy(alangs[i], ALangs[i], MAXLANGCODE2); strn0cpy(alangs[i], ALangs[i], MAXLANGCODE2);
@ -752,6 +753,8 @@ cString cChannel::ToText(const cChannel *Channel)
q += snprintf(q, sizeof(vpidbuf), "%d", Channel->vpid); q += snprintf(q, sizeof(vpidbuf), "%d", Channel->vpid);
if (Channel->ppid && Channel->ppid != Channel->vpid) if (Channel->ppid && Channel->ppid != Channel->vpid)
q += snprintf(q, sizeof(vpidbuf) - (q - vpidbuf), "+%d", Channel->ppid); q += snprintf(q, sizeof(vpidbuf) - (q - vpidbuf), "+%d", Channel->ppid);
if (Channel->vtype)
q += snprintf(q, sizeof(vpidbuf) - (q - vpidbuf), "=%d", Channel->vtype);
*q = 0; *q = 0;
const int BufferSize = (MAXAPIDS + MAXDPIDS) * (5 + 1 + MAXLANGCODE2) + 10; // 5 digits plus delimiting ',' or ';' plus optional '=cod+cod', +10: paranoia const int BufferSize = (MAXAPIDS + MAXDPIDS) * (5 + 1 + MAXLANGCODE2) + 10; // 5 digits plus delimiting ',' or ';' plus optional '=cod+cod', +10: paranoia
char apidbuf[BufferSize]; char apidbuf[BufferSize];
@ -813,22 +816,27 @@ bool cChannel::Parse(const char *s)
tpid = 0; tpid = 0;
} }
vpid = ppid = 0; vpid = ppid = 0;
vtype = 2; // default is MPEG-2
apids[0] = 0; apids[0] = 0;
dpids[0] = 0; dpids[0] = 0;
ok = false; ok = false;
if (parambuf && sourcebuf && vpidbuf && apidbuf) { if (parambuf && sourcebuf && vpidbuf && apidbuf) {
ok = StringToParameters(parambuf) && (source = cSource::FromString(sourcebuf)) >= 0; ok = StringToParameters(parambuf) && (source = cSource::FromString(sourcebuf)) >= 0;
char *p = strchr(vpidbuf, '+'); char *p;
if (p) if ((p = strchr(vpidbuf, '=')) != NULL) {
*p++ = 0;
if (sscanf(p, "%d", &vtype) != 1)
return false;
}
if ((p = strchr(vpidbuf, '+')) != NULL) {
*p++ = 0; *p++ = 0;
if (sscanf(vpidbuf, "%d", &vpid) != 1)
return false;
if (p) {
if (sscanf(p, "%d", &ppid) != 1) if (sscanf(p, "%d", &ppid) != 1)
return false; return false;
} }
else if (sscanf(vpidbuf, "%d", &vpid) != 1)
return false;
if (!ppid)
ppid = vpid; ppid = vpid;
char *dpidbuf = strchr(apidbuf, ';'); char *dpidbuf = strchr(apidbuf, ';');

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: channels.h 2.2 2008/04/12 13:46:50 kls Exp $ * $Id: channels.h 2.3 2008/07/06 11:49:37 kls Exp $
*/ */
#ifndef __CHANNELS_H #ifndef __CHANNELS_H
@ -124,6 +124,7 @@ private:
int srate; int srate;
int vpid; int vpid;
int ppid; int ppid;
int vtype;
int apids[MAXAPIDS + 1]; // list is zero-terminated int apids[MAXAPIDS + 1]; // list is zero-terminated
char alangs[MAXAPIDS][MAXLANGCODE2]; char alangs[MAXAPIDS][MAXLANGCODE2];
int dpids[MAXDPIDS + 1]; // list is zero-terminated int dpids[MAXDPIDS + 1]; // list is zero-terminated
@ -178,6 +179,7 @@ public:
int Srate(void) const { return srate; } int Srate(void) const { return srate; }
int Vpid(void) const { return vpid; } int Vpid(void) const { return vpid; }
int Ppid(void) const { return ppid; } int Ppid(void) const { return ppid; }
int Vtype(void) const { return vtype; }
const int *Apids(void) const { return apids; } const int *Apids(void) const { return apids; }
const int *Dpids(void) const { return dpids; } const int *Dpids(void) const { return dpids; }
const int *Spids(void) const { return spids; } const int *Spids(void) const { return spids; }
@ -225,7 +227,7 @@ public:
void SetId(int Nid, int Tid, int Sid, int Rid = 0); void SetId(int Nid, int Tid, int Sid, int Rid = 0);
void SetName(const char *Name, const char *ShortName, const char *Provider); void SetName(const char *Name, const char *ShortName, const char *Provider);
void SetPortalName(const char *PortalName); void SetPortalName(const char *PortalName);
void SetPids(int Vpid, int Ppid, int *Apids, char ALangs[][MAXLANGCODE2], int *Dpids, char DLangs[][MAXLANGCODE2], int *Spids, char SLangs[][MAXLANGCODE2], int Tpid); void SetPids(int Vpid, int Ppid, int Vtype, int *Apids, char ALangs[][MAXLANGCODE2], int *Dpids, char DLangs[][MAXLANGCODE2], int *Spids, char SLangs[][MAXLANGCODE2], int Tpid);
void SetCaIds(const int *CaIds); // list must be zero-terminated void SetCaIds(const int *CaIds); // list must be zero-terminated
void SetCaDescriptors(int Level); void SetCaDescriptors(int Level);
void SetLinkChannels(cLinkChannels *LinkChannels); void SetLinkChannels(cLinkChannels *LinkChannels);

286
device.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: device.c 2.2 2008/04/12 14:12:14 kls Exp $ * $Id: device.c 2.3 2008/07/06 13:22:21 kls Exp $
*/ */
#include "device.h" #include "device.h"
@ -21,16 +21,9 @@
// --- cLiveSubtitle --------------------------------------------------------- // --- cLiveSubtitle ---------------------------------------------------------
#define LIVESUBTITLEBUFSIZE KILOBYTE(100) class cLiveSubtitle : public cReceiver {
class cLiveSubtitle : public cReceiver, public cThread {
private:
cRingBufferLinear *ringBuffer;
cRemux *remux;
protected: protected:
virtual void Activate(bool On);
virtual void Receive(uchar *Data, int Length); virtual void Receive(uchar *Data, int Length);
virtual void Action(void);
public: public:
cLiveSubtitle(int SPid); cLiveSubtitle(int SPid);
virtual ~cLiveSubtitle(); virtual ~cLiveSubtitle();
@ -38,170 +31,17 @@ public:
cLiveSubtitle::cLiveSubtitle(int SPid) cLiveSubtitle::cLiveSubtitle(int SPid)
:cReceiver(tChannelID(), -1, SPid) :cReceiver(tChannelID(), -1, SPid)
,cThread("live subtitle")
{ {
ringBuffer = new cRingBufferLinear(LIVESUBTITLEBUFSIZE, TS_SIZE * 2, true, "Live Subtitle");
int NoPids = 0;
int SPids[] = { SPid, 0 };
remux = new cRemux(0, &NoPids, &NoPids, SPids);
} }
cLiveSubtitle::~cLiveSubtitle() cLiveSubtitle::~cLiveSubtitle()
{ {
cReceiver::Detach(); cReceiver::Detach();
delete remux;
delete ringBuffer;
}
void cLiveSubtitle::Activate(bool On)
{
if (On)
Start();
else
Cancel(3);
} }
void cLiveSubtitle::Receive(uchar *Data, int Length) void cLiveSubtitle::Receive(uchar *Data, int Length)
{ {
if (Running()) { cDevice::PrimaryDevice()->PlayTs(Data, Length);
int p = ringBuffer->Put(Data, Length);
if (p != Length && Running())
ringBuffer->ReportOverflow(Length - p);
}
}
void cLiveSubtitle::Action(void)
{
while (Running()) {
int Count;
uchar *b = ringBuffer->Get(Count);
if (b) {
Count = remux->Put(b, Count);
if (Count)
ringBuffer->Del(Count);
}
b = remux->Get(Count);
if (b) {
Count = cDevice::PrimaryDevice()->PlaySubtitle(b, Count);
remux->Del(Count);
}
}
}
// --- cPesAssembler ---------------------------------------------------------
class cPesAssembler {
private:
uchar *data;
uint32_t tag;
int length;
int size;
bool Realloc(int Size);
public:
cPesAssembler(void);
~cPesAssembler();
int ExpectedLength(void) { return PacketSize(data); }
static int PacketSize(const uchar *data);
int Length(void) { return length; }
const uchar *Data(void) { return data; } // only valid if Length() >= 4
void Reset(void);
void Put(uchar c);
void Put(const uchar *Data, int Length);
bool IsPes(void);
};
cPesAssembler::cPesAssembler(void)
{
data = NULL;
size = 0;
Reset();
}
cPesAssembler::~cPesAssembler()
{
free(data);
}
void cPesAssembler::Reset(void)
{
tag = 0xFFFFFFFF;
length = 0;
}
bool cPesAssembler::Realloc(int Size)
{
if (Size > size) {
size = max(Size, 2048);
data = (uchar *)realloc(data, size);
if (!data) {
esyslog("ERROR: can't allocate memory for PES assembler");
length = 0;
size = 0;
return false;
}
}
return true;
}
void cPesAssembler::Put(uchar c)
{
if (length < 4) {
tag = (tag << 8) | c;
if ((tag & 0xFFFFFF00) == 0x00000100) {
if (Realloc(4)) {
*(uint32_t *)data = htonl(tag);
length = 4;
}
}
else if (length < 3)
length++;
}
else if (Realloc(length + 1))
data[length++] = c;
}
void cPesAssembler::Put(const uchar *Data, int Length)
{
while (length < 4 && Length > 0) {
Put(*Data++);
Length--;
}
if (Length && Realloc(length + Length)) {
memcpy(data + length, Data, Length);
length += Length;
}
}
int cPesAssembler::PacketSize(const uchar *data)
{
// we need atleast 6 bytes of data here !!!
switch (data[3]) {
default:
case 0x00 ... 0xB8: // video stream start codes
case 0xB9: // Program end
case 0xBC: // Programm stream map
case 0xF0 ... 0xFF: // reserved
return 6;
case 0xBA: // Pack header
if ((data[4] & 0xC0) == 0x40) // MPEG2
return 14;
// to be absolutely correct we would have to add the stuffing bytes
// as well, but at this point we only may have 6 bytes of data avail-
// able. So it's up to the higher level to resync...
//return 14 + (data[13] & 0x07); // add stuffing bytes
else // MPEG1
return 12;
case 0xBB: // System header
case 0xBD: // Private stream1
case 0xBE: // Padding stream
case 0xBF: // Private stream2 (navigation data)
case 0xC0 ... 0xCF: // all the rest (the real packets)
case 0xD0 ... 0xDF:
case 0xE0 ... 0xEF:
return 6 + data[4] * 256 + data[5];
}
} }
// --- cDevice --------------------------------------------------------------- // --- cDevice ---------------------------------------------------------------
@ -241,7 +81,6 @@ cDevice::cDevice(void)
startScrambleDetection = 0; startScrambleDetection = 0;
player = NULL; player = NULL;
pesAssembler = new cPesAssembler;
ClrAvailableTracks(); ClrAvailableTracks();
currentAudioTrack = ttNone; currentAudioTrack = ttNone;
currentAudioTrackMissingCount = 0; currentAudioTrackMissingCount = 0;
@ -265,7 +104,6 @@ cDevice::~cDevice()
DetachAllReceivers(); DetachAllReceivers();
delete liveSubtitle; delete liveSubtitle;
delete dvbSubtitleConverter; delete dvbSubtitleConverter;
delete pesAssembler;
} }
bool cDevice::WaitForAllDevicesReady(int Timeout) bool cDevice::WaitForAllDevicesReady(int Timeout)
@ -1195,7 +1033,6 @@ bool cDevice::AttachPlayer(cPlayer *Player)
Detach(player); Detach(player);
DELETENULL(liveSubtitle); DELETENULL(liveSubtitle);
DELETENULL(dvbSubtitleConverter); DELETENULL(dvbSubtitleConverter);
pesAssembler->Reset();
player = Player; player = Player;
if (!Transferring()) if (!Transferring())
ClrAvailableTracks(false, true); ClrAvailableTracks(false, true);
@ -1256,7 +1093,7 @@ int cDevice::PlaySubtitle(const uchar *Data, int Length)
{ {
if (!dvbSubtitleConverter) if (!dvbSubtitleConverter)
dvbSubtitleConverter = new cDvbSubtitleConverter; dvbSubtitleConverter = new cDvbSubtitleConverter;
return dvbSubtitleConverter->Convert(Data, Length); return dvbSubtitleConverter->ConvertFragments(Data, Length);
} }
int cDevice::PlayPesPacket(const uchar *Data, int Length, bool VideoOnly) int cDevice::PlayPesPacket(const uchar *Data, int Length, bool VideoOnly)
@ -1360,42 +1197,16 @@ pre_1_3_19_PrivateStreamDetected:
int cDevice::PlayPes(const uchar *Data, int Length, bool VideoOnly) int cDevice::PlayPes(const uchar *Data, int Length, bool VideoOnly)
{ {
if (!Data) { if (!Data) {
pesAssembler->Reset();
if (dvbSubtitleConverter) if (dvbSubtitleConverter)
dvbSubtitleConverter->Reset(); dvbSubtitleConverter->Reset();
return 0; return 0;
} }
int Result = 0;
if (pesAssembler->Length()) {
// Make sure we have a complete PES header:
while (pesAssembler->Length() < 6 && Length > 0) {
pesAssembler->Put(*Data++);
Length--;
Result++;
}
if (pesAssembler->Length() < 6)
return Result; // Still no complete PES header - wait for more
int l = pesAssembler->ExpectedLength();
int Rest = min(l - pesAssembler->Length(), Length);
pesAssembler->Put(Data, Rest);
Data += Rest;
Length -= Rest;
Result += Rest;
if (pesAssembler->Length() < l)
return Result; // Still no complete PES packet - wait for more
// Now pesAssembler contains one complete PES packet.
int w = PlayPesPacket(pesAssembler->Data(), pesAssembler->Length(), VideoOnly);
if (w > 0)
pesAssembler->Reset();
return Result > 0 ? Result : w < 0 ? w : 0;
}
int i = 0; int i = 0;
while (i <= Length - 6) { while (i <= Length - 6) {
if (Data[i] == 0x00 && Data[i + 1] == 0x00 && Data[i + 2] == 0x01) { if (Data[i] == 0x00 && Data[i + 1] == 0x00 && Data[i + 2] == 0x01) {
int l = cPesAssembler::PacketSize(&Data[i]); int l = PesLength(Data + i);
if (i + l > Length) { if (i + l > Length) {
// Store incomplete PES packet for later completion: esyslog("ERROR: incomplete PES packet!");
pesAssembler->Put(Data + i, Length - i);
return Length; return Length;
} }
int w = PlayPesPacket(Data + i, l, VideoOnly); int w = PlayPesPacket(Data + i, l, VideoOnly);
@ -1408,10 +1219,91 @@ int cDevice::PlayPes(const uchar *Data, int Length, bool VideoOnly)
i++; i++;
} }
if (i < Length) if (i < Length)
pesAssembler->Put(Data + i, Length - i); esyslog("ERROR: leftover PES data!");
return Length; return Length;
} }
int cDevice::PlayTsVideo(const uchar *Data, int Length)
{
// Video PES has no explicit length, so we can only determine the end of
// a PES packet when the next TS packet that starts a payload comes in:
if (TsPayloadStart(Data)) {
if (const uchar *p = tsToPesVideo.GetPes(Length)) {
int w = PlayVideo(p, Length);
if (w > 0)
tsToPesVideo.Reset();
else
return w;
}
}
tsToPesVideo.PutTs(Data, Length);
return Length;
}
int cDevice::PlayTsAudio(const uchar *Data, int Length)
{
bool PayloadStart = TsPayloadStart(Data);
for (int Pass = 0; Pass < 2; Pass++) {
if (Pass == 0 && !PayloadStart) // if no new payload is started, we can always put the packet into the converter
tsToPesAudio.PutTs(Data, Length);
if (const uchar *p = tsToPesAudio.GetPes(Length)) {
int w = PlayAudio(p, Length, 0);
if (w > 0)
tsToPesAudio.Reset();
else if (PayloadStart)
return w; // must get out the old packet before starting a new one
}
if (Pass == 0 && PayloadStart)
tsToPesAudio.PutTs(Data, Length);
}
return Length;
}
int cDevice::PlayTsSubtitle(const uchar *Data, int Length)
{
if (!dvbSubtitleConverter)
dvbSubtitleConverter = new cDvbSubtitleConverter;
tsToPesSubtitle.PutTs(Data, Length);
if (const uchar *p = tsToPesSubtitle.GetPes(Length)) {
dvbSubtitleConverter->Convert(p, Length);
tsToPesSubtitle.Reset();
}
return Length;
}
//TODO detect and report continuity errors?
int cDevice::PlayTs(const uchar *Data, int Length, bool VideoOnly)
{
if (Length == TS_SIZE) {
if (!TsHasPayload(Data))
return Length; // silently ignore TS packets w/o payload
int PayloadOffset = TsPayloadOffset(Data);
if (PayloadOffset < Length) {
int Pid = TsPid(Data);
if (Pid == 0)
patPmtParser.ParsePat(Data + PayloadOffset, Length - PayloadOffset);
else if (Pid == patPmtParser.PmtPid())
patPmtParser.ParsePmt(Data + PayloadOffset, Length - PayloadOffset);
else if (Pid == patPmtParser.Vpid())
return PlayTsVideo(Data, Length);
else if (Pid == availableTracks[currentAudioTrack].id) {
if (!VideoOnly || HasIBPTrickSpeed()) {
int w = PlayTsAudio(Data, Length);
if (w > 0)
Audios.PlayTsAudio(Data, Length);
return w;
}
}
else if (Pid == availableTracks[currentSubtitleTrack].id) {
if (!VideoOnly || HasIBPTrickSpeed())
return PlayTsSubtitle(Data, Length);
}
return Length;
}
}
return -1;
}
int cDevice::Priority(void) const int cDevice::Priority(void) const
{ {
int priority = IsPrimaryDevice() ? Setup.PrimaryLimit - 1 : DEFAULTPRIORITY; int priority = IsPrimaryDevice() ? Setup.PrimaryLimit - 1 : DEFAULTPRIORITY;
@ -1448,7 +1340,7 @@ void cDevice::Action(void)
uchar *b = NULL; uchar *b = NULL;
if (GetTSPacket(b)) { if (GetTSPacket(b)) {
if (b) { if (b) {
int Pid = (((uint16_t)b[1] & PID_MASK_HI) << 8) | b[2]; int Pid = TsPid(b);
// Check whether the TS packets are scrambled: // Check whether the TS packets are scrambled:
bool DetachReceivers = false; bool DetachReceivers = false;
bool DescramblingOk = false; bool DescramblingOk = false;

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: device.h 2.1 2008/04/12 11:11:23 kls Exp $ * $Id: device.h 2.2 2008/07/06 11:25:42 kls Exp $
*/ */
#ifndef __DEVICE_H #ifndef __DEVICE_H
@ -17,6 +17,7 @@
#include "filter.h" #include "filter.h"
#include "nit.h" #include "nit.h"
#include "pat.h" #include "pat.h"
#include "remux.h"
#include "ringbuffer.h" #include "ringbuffer.h"
#include "sdt.h" #include "sdt.h"
#include "sections.h" #include "sections.h"
@ -30,10 +31,6 @@
#define MAXVOLUME 255 #define MAXVOLUME 255
#define VOLUMEDELTA 5 // used to increase/decrease the volume #define VOLUMEDELTA 5 // used to increase/decrease the volume
#define TS_SIZE 188
#define TS_SYNC_BYTE 0x47
#define PID_MASK_HI 0x1F
enum eSetChannelResult { scrOk, scrNotAvailable, scrNoTransfer, scrFailed }; enum eSetChannelResult { scrOk, scrNotAvailable, scrNoTransfer, scrFailed };
enum ePlayMode { pmNone, // audio/video from decoder enum ePlayMode { pmNone, // audio/video from decoder
@ -89,7 +86,6 @@ struct tTrackId {
class cPlayer; class cPlayer;
class cReceiver; class cReceiver;
class cPesAssembler;
class cLiveSubtitle; class cLiveSubtitle;
/// The cDevice class is the base from which actual devices can be derived. /// The cDevice class is the base from which actual devices can be derived.
@ -477,7 +473,10 @@ public:
private: private:
cPlayer *player; cPlayer *player;
cPesAssembler *pesAssembler; cPatPmtParser patPmtParser;
cTsToPes tsToPesVideo;
cTsToPes tsToPesAudio;
cTsToPes tsToPesSubtitle;
protected: protected:
virtual bool CanReplay(void) const; virtual bool CanReplay(void) const;
///< Returns true if this device can currently start a replay session. ///< Returns true if this device can currently start a replay session.
@ -511,6 +510,33 @@ protected:
///< If VideoOnly is true, only the video will be displayed, ///< If VideoOnly is true, only the video will be displayed,
///< which is necessary for trick modes like 'fast forward'. ///< which is necessary for trick modes like 'fast forward'.
///< Data must point to one single, complete PES packet. ///< Data must point to one single, complete PES packet.
virtual int PlayTsVideo(const uchar *Data, int Length);
///< Plays the given data block as video.
///< Data points to exactly one complete TS packet of the given Length
///< (which is always TS_SIZE).
///< PlayTsVideo() shall process the packet either as a whole (returning
///< a positive number, which needs not necessarily be Length) or not at all
///< (returning 0 or -1 and setting 'errno' to EAGAIN).
///< The default implementation collects all incoming TS payload belonging
///< to one PES packet and calls PlayVideo() with the resulting packet.
virtual int PlayTsAudio(const uchar *Data, int Length);
///< Plays the given data block as audio.
///< Data points to exactly one complete TS packet of the given Length
///< (which is always TS_SIZE).
///< PlayTsAudio() shall process the packet either as a whole (returning
///< a positive number, which needs not necessarily be Length) or not at all
///< (returning 0 or -1 and setting 'errno' to EAGAIN).
///< The default implementation collects all incoming TS payload belonging
///< to one PES packet and calls PlayAudio() with the resulting packet.
virtual int PlayTsSubtitle(const uchar *Data, int Length);
///< Plays the given data block as a subtitle.
///< Data points to exactly one complete TS packet of the given Length
///< (which is always TS_SIZE).
///< PlayTsSubtitle() shall process the packet either as a whole (returning
///< a positive number, which needs not necessarily be Length) or not at all
///< (returning 0 or -1 and setting 'errno' to EAGAIN).
///< The default implementation collects all incoming TS payload belonging
///< to one PES packet and displays the resulting subtitle via the OSD.
public: public:
virtual int64_t GetSTC(void); virtual int64_t GetSTC(void);
///< Gets the current System Time Counter, which can be used to ///< Gets the current System Time Counter, which can be used to
@ -565,6 +591,21 @@ public:
///< to a complete packet with data from the next call to PlayPes(). ///< to a complete packet with data from the next call to PlayPes().
///< That way any functions called from within PlayPes() will be ///< That way any functions called from within PlayPes() will be
///< guaranteed to always receive complete PES packets. ///< guaranteed to always receive complete PES packets.
virtual int PlayTs(const uchar *Data, int Length, bool VideoOnly = false);
///< Plays the given TS packet.
///< If VideoOnly is true, only the video will be displayed,
///< which is necessary for trick modes like 'fast forward'.
///< Data points to a single TS packet, Length is always TS_SIZE (the total
///< size of a single TS packet).
///< A derived device can reimplement this function to handle the
///< TS packets itself. Any packets the derived function can't handle
///< must be sent to the base class function. This applies especially
///< to the PAT/PMT packets.
///< Returns -1 in case of error, otherwise the number of actually
///< processed bytes is returned, which may be less than Length.
///< PlayTs() shall process the packet either as a whole (returning
///< a positive number, which needs not necessarily be Length) or not at all
///< (returning 0 or -1 and setting 'errno' to EAGAIN).
bool Replaying(void) const; bool Replaying(void) const;
///< Returns true if we are currently replaying. ///< Returns true if we are currently replaying.
bool Transferring(void) const; bool Transferring(void) const;

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: dvbdevice.c 2.3 2008/04/19 09:19:08 kls Exp $ * $Id: dvbdevice.c 2.4 2008/07/06 13:58:56 kls Exp $
*/ */
#include "dvbdevice.h" #include "dvbdevice.h"
@ -1236,6 +1236,18 @@ int cDvbDevice::PlayAudio(const uchar *Data, int Length, uchar Id)
return WriteAllOrNothing(fd_audio, Data, Length, 1000, 10); return WriteAllOrNothing(fd_audio, Data, Length, 1000, 10);
} }
int cDvbDevice::PlayTsVideo(const uchar *Data, int Length)
{
Length = TsGetPayload(&Data);
return PlayVideo(Data, Length);
}
int cDvbDevice::PlayTsAudio(const uchar *Data, int Length)
{
Length = TsGetPayload(&Data);
return PlayAudio(Data, Length, 0);
}
bool cDvbDevice::OpenDvr(void) bool cDvbDevice::OpenDvr(void)
{ {
CloseDvr(); CloseDvr();

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: dvbdevice.h 2.1 2008/04/12 11:20:48 kls Exp $ * $Id: dvbdevice.h 2.2 2008/06/01 09:48:04 kls Exp $
*/ */
#ifndef __DVBDEVICE_H #ifndef __DVBDEVICE_H
@ -138,6 +138,8 @@ protected:
virtual bool SetPlayMode(ePlayMode PlayMode); virtual bool SetPlayMode(ePlayMode PlayMode);
virtual int PlayVideo(const uchar *Data, int Length); virtual int PlayVideo(const uchar *Data, int Length);
virtual int PlayAudio(const uchar *Data, int Length, uchar Id); virtual int PlayAudio(const uchar *Data, int Length, uchar Id);
virtual int PlayTsVideo(const uchar *Data, int Length);
virtual int PlayTsAudio(const uchar *Data, int Length);
public: public:
virtual int64_t GetSTC(void); virtual int64_t GetSTC(void);
virtual void TrickSpeed(int Speed); virtual void TrickSpeed(int Speed);

View File

@ -7,7 +7,7 @@
* Original author: Marco Schlüßler <marco@lordzodiac.de> * Original author: Marco Schlüßler <marco@lordzodiac.de>
* With some input from the "subtitle plugin" by Pekka Virtanen <pekka.virtanen@sci.fi> * With some input from the "subtitle plugin" by Pekka Virtanen <pekka.virtanen@sci.fi>
* *
* $Id: dvbsubtitle.c 1.3 2007/11/25 13:33:08 kls Exp $ * $Id: dvbsubtitle.c 2.1 2008/05/25 14:36:24 kls Exp $
*/ */
#include "dvbsubtitle.h" #include "dvbsubtitle.h"
@ -580,12 +580,12 @@ bool cDvbSubtitleAssembler::Realloc(int Size)
unsigned char *cDvbSubtitleAssembler::Get(int &Length) unsigned char *cDvbSubtitleAssembler::Get(int &Length)
{ {
if (length > pos + 5) { if (length > pos + 5) {
Length = (data[pos + 4] << 8) + data[pos + 5] + 6; Length = (data[pos + 4] << 8) + data[pos + 5] + 6;
if (length >= pos + Length) { if (length >= pos + Length) {
unsigned char *result = data + pos; unsigned char *result = data + pos;
pos += Length; pos += Length;
return result; return result;
} }
} }
return NULL; return NULL;
} }
@ -684,10 +684,10 @@ void cDvbSubtitleConverter::Reset(void)
Unlock(); Unlock();
} }
int cDvbSubtitleConverter::Convert(const uchar *Data, int Length) int cDvbSubtitleConverter::ConvertFragments(const uchar *Data, int Length)
{ {
if (Data && Length > 8) { if (Data && Length > 8) {
int PayloadOffset = Data[8] + 9; int PayloadOffset = PesPayloadOffset(Data);
int SubstreamHeaderLength = 4; int SubstreamHeaderLength = 4;
bool ResetSubtitleAssembler = Data[PayloadOffset + 3] == 0x00; bool ResetSubtitleAssembler = Data[PayloadOffset + 3] == 0x00;
@ -699,15 +699,9 @@ int cDvbSubtitleConverter::Convert(const uchar *Data, int Length)
} }
if (Length > PayloadOffset + SubstreamHeaderLength) { if (Length > PayloadOffset + SubstreamHeaderLength) {
int64_t pts = 0; int64_t pts = PesGetPts(Data);
if ((Data[7] & 0x80) && Data[8] >= 5) { if (pts)
pts = (((int64_t)Data[ 9]) & 0x0E) << 29;
pts |= ( (int64_t)Data[10]) << 22;
pts |= (((int64_t)Data[11]) & 0xFE) << 14;
pts |= ( (int64_t)Data[12]) << 7;
pts |= (((int64_t)Data[13]) & 0xFE) >> 1;
dbgconverter("Converter PTS: %lld\n", pts); dbgconverter("Converter PTS: %lld\n", pts);
}
const uchar *data = Data + PayloadOffset + SubstreamHeaderLength; // skip substream header const uchar *data = Data + PayloadOffset + SubstreamHeaderLength; // skip substream header
int length = Length - PayloadOffset - SubstreamHeaderLength; // skip substream header int length = Length - PayloadOffset - SubstreamHeaderLength; // skip substream header
if (ResetSubtitleAssembler) if (ResetSubtitleAssembler)
@ -736,6 +730,40 @@ int cDvbSubtitleConverter::Convert(const uchar *Data, int Length)
return 0; return 0;
} }
int cDvbSubtitleConverter::Convert(const uchar *Data, int Length)
{
if (Data && Length > 8) {
int PayloadOffset = PesPayloadOffset(Data);
if (Length > PayloadOffset) {
int64_t pts = PesGetPts(Data);
if (pts)
dbgconverter("Converter PTS: %lld\n", pts);
const uchar *data = Data + PayloadOffset;
int length = Length - PayloadOffset;
if (length > 3) {
if (data[0] == 0x20 && data[1] == 0x00 && data[2] == 0x0F) {
data += 2;
length -= 2;
}
const uchar *b = data;
while (length > 0) {
if (b[0] == 0x0F) {
int n = ExtractSegment(b, length, pts);
if (n < 0)
break;
b += n;
length -= n;
}
else
break;
}
}
}
return Length;
}
return 0;
}
#define LimitTo32Bit(n) (n & 0x00000000FFFFFFFFL) #define LimitTo32Bit(n) (n & 0x00000000FFFFFFFFL)
#define MAXDELTA 40000 // max. reasonable PTS/STC delta in ms #define MAXDELTA 40000 // max. reasonable PTS/STC delta in ms

View File

@ -6,7 +6,7 @@
* *
* Original author: Marco Schlüßler <marco@lordzodiac.de> * Original author: Marco Schlüßler <marco@lordzodiac.de>
* *
* $Id: dvbsubtitle.h 1.1 2007/10/12 14:27:30 kls Exp $ * $Id: dvbsubtitle.h 2.1 2008/05/25 14:36:52 kls Exp $
*/ */
#ifndef __DVBSUBTITLE_H #ifndef __DVBSUBTITLE_H
@ -17,7 +17,7 @@
#include "tools.h" #include "tools.h"
class cDvbSubtitlePage; class cDvbSubtitlePage;
class cDvbSubtitleAssembler; class cDvbSubtitleAssembler; // for legacy PES recordings
class cDvbSubtitleBitmaps; class cDvbSubtitleBitmaps;
class cDvbSubtitleConverter : public cThread { class cDvbSubtitleConverter : public cThread {
@ -36,6 +36,7 @@ public:
virtual ~cDvbSubtitleConverter(); virtual ~cDvbSubtitleConverter();
void Action(void); void Action(void);
void Reset(void); void Reset(void);
int ConvertFragments(const uchar *Data, int Length); // for legacy PES recordings
int Convert(const uchar *Data, int Length); int Convert(const uchar *Data, int Length);
static void SetupChanged(void); static void SetupChanged(void);
}; };

View File

@ -6,7 +6,7 @@
* the Free Software Foundation; either version 2 of the License, or * * the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. * * (at your option) any later version. *
* * * *
* $Id: util.h 1.7 2006/02/25 10:13:28 kls Exp $ * $Id: util.h 2.1 2008/05/22 10:49:08 kls Exp $
* * * *
***************************************************************************/ ***************************************************************************/
@ -148,9 +148,9 @@ public:
CRC32(const char *d, int len, u_int32_t CRCvalue=0xFFFFFFFF); CRC32(const char *d, int len, u_int32_t CRCvalue=0xFFFFFFFF);
bool isValid() { return crc32(data, length, value) == 0; } bool isValid() { return crc32(data, length, value) == 0; }
static bool isValid(const char *d, int len, u_int32_t CRCvalue=0xFFFFFFFF) { return crc32(d, len, CRCvalue) == 0; } static bool isValid(const char *d, int len, u_int32_t CRCvalue=0xFFFFFFFF) { return crc32(d, len, CRCvalue) == 0; }
static u_int32_t crc32(const char *d, int len, u_int32_t CRCvalue);
protected: protected:
static u_int32_t crc_table[256]; static u_int32_t crc_table[256];
static u_int32_t crc32 (const char *d, int len, u_int32_t CRCvalue);
const char *data; const char *data;
int length; int length;

11
pat.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: pat.c 2.1 2008/04/12 13:34:50 kls Exp $ * $Id: pat.c 2.2 2008/07/06 14:01:32 kls Exp $
*/ */
#include "pat.h" #include "pat.h"
@ -328,7 +328,8 @@ void cPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length
// Scan the stream-specific loop: // Scan the stream-specific loop:
SI::PMT::Stream stream; SI::PMT::Stream stream;
int Vpid = 0; int Vpid = 0;
int Ppid = pmt.getPCRPid(); int Ppid = 0;
int Vtype = 0;
int Apids[MAXAPIDS + 1] = { 0 }; // these lists are zero-terminated int Apids[MAXAPIDS + 1] = { 0 }; // these lists are zero-terminated
int Dpids[MAXDPIDS + 1] = { 0 }; int Dpids[MAXDPIDS + 1] = { 0 };
int Spids[MAXSPIDS + 1] = { 0 }; int Spids[MAXSPIDS + 1] = { 0 };
@ -343,8 +344,10 @@ void cPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length
switch (stream.getStreamType()) { switch (stream.getStreamType()) {
case 1: // STREAMTYPE_11172_VIDEO case 1: // STREAMTYPE_11172_VIDEO
case 2: // STREAMTYPE_13818_VIDEO case 2: // STREAMTYPE_13818_VIDEO
//TODO case 0x1B: // MPEG4 case 0x1B: // MPEG4
Vpid = stream.getPid(); Vpid = stream.getPid();
Ppid = pmt.getPCRPid();
Vtype = stream.getStreamType();
break; break;
case 3: // STREAMTYPE_11172_AUDIO case 3: // STREAMTYPE_11172_AUDIO
case 4: // STREAMTYPE_13818_AUDIO case 4: // STREAMTYPE_13818_AUDIO
@ -440,7 +443,7 @@ void cPatFilter::Process(u_short Pid, u_char Tid, const u_char *Data, int Length
} }
} }
if (Setup.UpdateChannels >= 2) { if (Setup.UpdateChannels >= 2) {
Channel->SetPids(Vpid, Vpid ? Ppid : 0, Apids, ALangs, Dpids, DLangs, Spids, SLangs, Tpid); Channel->SetPids(Vpid, Ppid, Vtype, Apids, ALangs, Dpids, DLangs, Spids, SLangs, Tpid);
Channel->SetCaIds(CaDescriptors->CaIds()); Channel->SetCaIds(CaDescriptors->CaIds());
} }
Channel->SetCaDescriptors(CaDescriptorHandler.AddCaDescriptors(CaDescriptors)); Channel->SetCaDescriptors(CaDescriptorHandler.AddCaDescriptors(CaDescriptors));

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: player.h 1.21 2008/02/16 13:50:11 kls Exp $ * $Id: player.h 2.1 2008/08/15 14:07:48 kls Exp $
*/ */
#ifndef __PLAYER_H #ifndef __PLAYER_H
@ -42,6 +42,10 @@ protected:
// Sends the given PES Data to the device and returns the number of // Sends the given PES Data to the device and returns the number of
// bytes that have actually been accepted by the device (or a // bytes that have actually been accepted by the device (or a
// negative value in case of an error). // negative value in case of an error).
int PlayTs(const uchar *Data, int Length, bool VideoOnly = false) { return device ? device->PlayTs(Data, Length, VideoOnly) : -1; }
// Sends the given TS packet to the device and returns a positive number
// if the packet has been accepted by the device, or a negative value in
// case of an error.
public: public:
cPlayer(ePlayMode PlayMode = pmAudioVideo); cPlayer(ePlayMode PlayMode = pmAudioVideo);
virtual ~cPlayer(); virtual ~cPlayer();

477
remux.c
View File

@ -11,15 +11,24 @@
* The cRepacker family's 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. * and adapted to the VDR coding style by Klaus.Schmidinger@cadsoft.de.
* *
* $Id: remux.c 1.64 2007/11/25 13:56:03 kls Exp $ * $Id: remux.c 2.1 2008/08/15 14:49:34 kls Exp $
*/ */
#include "remux.h" #include "remux.h"
#include <stdlib.h> #include <stdlib.h>
#include "channels.h" #include "channels.h"
#include "device.h"
#include "libsi/si.h"
#include "libsi/section.h"
#include "libsi/descriptor.h"
#include "shutdown.h" #include "shutdown.h"
#include "tools.h" #include "tools.h"
// Set this to 'true' for debug output:
static bool DebugPatPmt = false;
#define dbgpatpmt(a...) if (DebugPatPmt) fprintf(stderr, a)
ePesHeader AnalyzePesHeader(const uchar *Data, int Count, int &PesPayloadOffset, bool *ContinuationHeader) ePesHeader AnalyzePesHeader(const uchar *Data, int Count, int &PesPayloadOffset, bool *ContinuationHeader)
{ {
if (Count < 7) if (Count < 7)
@ -1413,7 +1422,6 @@ int cDolbyRepacker::BreakAt(const uchar *Data, int Count)
//pts_dts flags //pts_dts flags
#define PTS_ONLY 0x80 #define PTS_ONLY 0x80
#define TS_SIZE 188
#define PID_MASK_HI 0x1F #define PID_MASK_HI 0x1F
#define CONT_CNT_MASK 0x0F #define CONT_CNT_MASK 0x0F
@ -2007,8 +2015,6 @@ int cRemux::ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &Pic
return -1; return -1;
} }
#define TS_SYNC_BYTE 0x47
int cRemux::Put(const uchar *Data, int Count) int cRemux::Put(const uchar *Data, int Count)
{ {
int used = 0; int used = 0;
@ -2182,3 +2188,466 @@ void cRemux::SetBrokenLink(uchar *Data, int Length)
else else
dsyslog("SetBrokenLink: no video packet in frame"); dsyslog("SetBrokenLink: no video packet in frame");
} }
// --- cPatPmtGenerator ------------------------------------------------------
cPatPmtGenerator::cPatPmtGenerator(void)
{
numPmtPackets = 0;
patCounter = pmtCounter = 0;
patVersion = pmtVersion = 0;
esInfoLength = NULL;
GeneratePat();
}
void cPatPmtGenerator::IncCounter(int &Counter, uchar *TsPacket)
{
TsPacket[3] = (TsPacket[3] & 0xF0) | Counter;
if (++Counter > 0x0F)
Counter = 0x00;
}
void cPatPmtGenerator::IncVersion(int &Version)
{
if (++Version > 0x1F)
Version = 0x00;
}
void cPatPmtGenerator::IncEsInfoLength(int Length)
{
if (esInfoLength) {
Length += ((*esInfoLength & 0x0F) << 8) | *(esInfoLength + 1);
*esInfoLength = 0xF0 | (Length >> 8);
*(esInfoLength + 1) = Length;
}
}
int cPatPmtGenerator::MakeStream(uchar *Target, uchar Type, int Pid)
{
int i = 0;
Target[i++] = Type; // stream type
Target[i++] = 0xE0 | (Pid >> 8); // dummy (3), pid hi (5)
Target[i++] = Pid; // pid lo
esInfoLength = &Target[i];
Target[i++] = 0xF0; // dummy (4), ES info length hi
Target[i++] = 0x00; // ES info length lo
return i;
}
int cPatPmtGenerator::MakeAC3Descriptor(uchar *Target)
{
int i = 0;
Target[i++] = SI::AC3DescriptorTag;
Target[i++] = 0x01; // length
Target[i++] = 0x00;
IncEsInfoLength(i);
return i;
}
int cPatPmtGenerator::MakeSubtitlingDescriptor(uchar *Target, const char *Language)
{
int i = 0;
Target[i++] = SI::SubtitlingDescriptorTag;
Target[i++] = 0x08; // length
Target[i++] = *Language++;
Target[i++] = *Language++;
Target[i++] = *Language++;
Target[i++] = 0x00; // subtitling type
Target[i++] = 0x00; // composition page id hi
Target[i++] = 0x01; // composition page id lo
Target[i++] = 0x00; // ancillary page id hi
Target[i++] = 0x01; // ancillary page id lo
IncEsInfoLength(i);
return i;
}
int cPatPmtGenerator::MakeLanguageDescriptor(uchar *Target, const char *Language)
{
int i = 0;
Target[i++] = SI::ISO639LanguageDescriptorTag;
Target[i++] = 0x04; // length
Target[i++] = *Language++;
Target[i++] = *Language++;
Target[i++] = *Language++;
Target[i++] = 0x01; // audio type
IncEsInfoLength(i);
return i;
}
int cPatPmtGenerator::MakeCRC(uchar *Target, const uchar *Data, int Length)
{
int crc = SI::CRC32::crc32((const char *)Data, Length, 0xFFFFFFFF);
int i = 0;
Target[i++] = crc >> 24;
Target[i++] = crc >> 16;
Target[i++] = crc >> 8;
Target[i++] = crc;
return i;
}
#define P_TSID 0x8008 // pseudo TS ID
#define P_PNR 0x0084 // pseudo Program Number
#define P_PMT_PID 0x0084 // pseudo PMT pid
void cPatPmtGenerator::GeneratePat(void)
{
memset(pat, 0xFF, sizeof(pat));
uchar *p = pat;
int i = 0;
p[i++] = 0x47; // TS indicator
p[i++] = 0x40; // flags (3), pid hi (5)
p[i++] = 0x00; // pid lo
p[i++] = 0x10; // flags (4), continuity counter (4)
int PayloadStart = i;
p[i++] = 0x00; // table id
p[i++] = 0xB0; // section syntax indicator (1), dummy (3), section length hi (4)
int SectionLength = i;
p[i++] = 0x00; // section length lo (filled in later)
p[i++] = P_TSID >> 8; // TS id hi
p[i++] = P_TSID & 0xFF; // TS id lo
p[i++] = 0xC1 | (patVersion << 1); // dummy (2), version number (5), current/next indicator (1)
p[i++] = 0x00; // section number
p[i++] = 0x00; // last section number
p[i++] = P_PNR >> 8; // program number hi
p[i++] = P_PNR & 0xFF; // program number lo
p[i++] = 0xE0 | (P_PMT_PID >> 8); // dummy (3), PMT pid hi (5)
p[i++] = P_PMT_PID & 0xFF; // PMT pid lo
pat[SectionLength] = i - SectionLength - 1 + 4; // -2 = SectionLength storage, +4 = length of CRC
MakeCRC(pat + i, pat + PayloadStart, i - PayloadStart);
IncVersion(patVersion);
}
void cPatPmtGenerator::GeneratePmt(tChannelID ChannelID)
{
// generate the complete PMT section:
uchar buf[MAX_SECTION_SIZE];
memset(buf, 0xFF, sizeof(buf));
numPmtPackets = 0;
cChannel *Channel = Channels.GetByChannelID(ChannelID);
if (Channel) {
int Vpid = Channel->Vpid();
uchar *p = buf;
int i = 0;
p[i++] = 0x02; // table id
int SectionLength = i;
p[i++] = 0xB0; // section syntax indicator (1), dummy (3), section length hi (4)
p[i++] = 0x00; // section length lo (filled in later)
p[i++] = P_PNR >> 8; // program number hi
p[i++] = P_PNR & 0xFF; // program number lo
p[i++] = 0xC1 | (pmtVersion << 1); // dummy (2), version number (5), current/next indicator (1)
p[i++] = 0x00; // section number
p[i++] = 0x00; // last section number
p[i++] = 0xE0 | (Vpid >> 8); // dummy (3), PCR pid hi (5)
p[i++] = Vpid; // PCR pid lo
p[i++] = 0xF0; // dummy (4), program info length hi (4)
p[i++] = 0x00; // program info length lo
if (Vpid)
i += MakeStream(buf + i, Channel->Vtype(), Vpid);
for (int n = 0; Channel->Apid(n); n++) {
i += MakeStream(buf + i, 0x04, Channel->Apid(n));
const char *Alang = Channel->Alang(n);
i += MakeLanguageDescriptor(buf + i, Alang);
if (Alang[3] == '+')
i += MakeLanguageDescriptor(buf + i, Alang + 3);
}
for (int n = 0; Channel->Dpid(n); n++) {
i += MakeStream(buf + i, 0x06, Channel->Dpid(n));
i += MakeAC3Descriptor(buf + i);
i += MakeLanguageDescriptor(buf + i, Channel->Dlang(n));
}
for (int n = 0; Channel->Spid(n); n++) {
i += MakeStream(buf + i, 0x06, Channel->Spid(n));
i += MakeSubtitlingDescriptor(buf + i, Channel->Slang(n));
}
int sl = i - SectionLength - 2 + 4; // -2 = SectionLength storage, +4 = length of CRC
buf[SectionLength] |= (sl >> 8) & 0x0F;
buf[SectionLength + 1] = sl;
MakeCRC(buf + i, buf, i);
// split the PMT section into several TS packets:
uchar *q = buf;
while (i > 0) {
uchar *p = pmt[numPmtPackets++];
int j = 0;
p[j++] = 0x47; // TS indicator
p[j++] = 0x40 | (P_PNR >> 8); // flags (3), pid hi (5)
p[j++] = P_PNR & 0xFF; // pid lo
p[j++] = 0x10; // flags (4), continuity counter (4)
int l = TS_SIZE - j;
memcpy(p + j, q, l);
q += l;
i -= l;
}
IncVersion(pmtVersion);
}
else
esyslog("ERROR: can't find channel %s", *ChannelID.ToString());
}
uchar *cPatPmtGenerator::GetPat(void)
{
IncCounter(patCounter, pat);
return pat;
}
uchar *cPatPmtGenerator::GetPmt(int &Index)
{
if (Index < numPmtPackets) {
IncCounter(patCounter, pmt[Index]);
return pmt[Index++];
}
return NULL;
}
// --- cPatPmtParser ---------------------------------------------------------
cPatPmtParser::cPatPmtParser(void)
{
pmtSize = 0;
pmtPid = -1;
vpid = vtype = 0;
}
void cPatPmtParser::ParsePat(const uchar *Data, int Length)
{
// The PAT is always assumed to fit into a single TS packet
SI::PAT Pat(Data, false);
if (Pat.CheckCRCAndParse()) {
dbgpatpmt("PAT: TSid = %d, c/n = %d, v = %d, s = %d, ls = %d\n", Pat.getTransportStreamId(), Pat.getCurrentNextIndicator(), Pat.getVersionNumber(), Pat.getSectionNumber(), Pat.getLastSectionNumber());
SI::PAT::Association assoc;
for (SI::Loop::Iterator it; Pat.associationLoop.getNext(assoc, it); ) {
dbgpatpmt(" isNITPid = %d\n", assoc.isNITPid());
if (!assoc.isNITPid()) {
pmtPid = assoc.getPid();
dbgpatpmt(" service id = %d, pid = %d\n", assoc.getServiceId(), assoc.getPid());
}
}
}
else
esyslog("ERROR: can't parse PAT");
}
void cPatPmtParser::ParsePmt(const uchar *Data, int Length)
{
// The PMT may extend over several TS packets, so we need to assemble them
if (pmtSize == 0) {
// this is the first packet
if (SectionLength(Data, Length) > Length) {
if (Length <= int(sizeof(pmt))) {
memcpy(pmt, Data, Length);
pmtSize = Length;
}
else
esyslog("ERROR: PMT packet length too big (%d byte)!", Length);
return;
}
// the packet contains the entire PMT section, so we run into the actual parsing
}
else {
// this is a following packet, so we add it to the pmt storage
if (Length <= int(sizeof(pmt)) - pmtSize) {
memcpy(pmt + pmtSize, Data, Length);
pmtSize += Length;
}
else {
esyslog("ERROR: PMT section length too big (%d byte)!", pmtSize + Length);
pmtSize = 0;
}
if (SectionLength(pmt, pmtSize) > pmtSize)
return; // more packets to come
// the PMT section is now complete, so we run into the actual parsing
Data = pmt;
}
SI::PMT Pmt(Data, false);
if (Pmt.CheckCRCAndParse()) {
dbgpatpmt("PMT: sid = %d, c/n = %d, v = %d, s = %d, ls = %d\n", Pmt.getServiceId(), Pmt.getCurrentNextIndicator(), Pmt.getVersionNumber(), Pmt.getSectionNumber(), Pmt.getLastSectionNumber());
dbgpatpmt(" pcr = %d\n", Pmt.getPCRPid());
cDevice::PrimaryDevice()->ClrAvailableTracks(false, true);
int NumApids = 0;
int NumDpids = 0;
int NumSpids = 0;
SI::PMT::Stream stream;
for (SI::Loop::Iterator it; Pmt.streamLoop.getNext(stream, it); ) {
dbgpatpmt(" stream type = %02X, pid = %d", stream.getStreamType(), stream.getPid());
switch (stream.getStreamType()) {
case 0x02: // STREAMTYPE_13818_VIDEO
case 0x1B: // MPEG4
vpid = stream.getPid();
vtype = stream.getStreamType();
break;
case 0x04: // STREAMTYPE_13818_AUDIO
{
if (NumApids < MAXAPIDS) {
char ALangs[MAXLANGCODE2] = "";
SI::Descriptor *d;
for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); ) {
switch (d->getDescriptorTag()) {
case SI::ISO639LanguageDescriptorTag: {
SI::ISO639LanguageDescriptor *ld = (SI::ISO639LanguageDescriptor *)d;
SI::ISO639LanguageDescriptor::Language l;
char *s = ALangs;
int n = 0;
for (SI::Loop::Iterator it; ld->languageLoop.getNext(l, it); ) {
if (*ld->languageCode != '-') { // some use "---" to indicate "none"
dbgpatpmt(" '%s'", l.languageCode);
if (n > 0)
*s++ = '+';
strn0cpy(s, I18nNormalizeLanguageCode(l.languageCode), MAXLANGCODE1);
s += strlen(s);
if (n++ > 1)
break;
}
}
}
break;
default: ;
}
delete d;
}
cDevice::PrimaryDevice()->SetAvailableTrack(ttAudio, NumApids, stream.getPid(), ALangs);
NumApids++;
}
}
break;
case 0x06: // STREAMTYPE_13818_PES_PRIVATE
{
int dpid = 0;
char lang[MAXLANGCODE1] = "";
SI::Descriptor *d;
for (SI::Loop::Iterator it; (d = stream.streamDescriptors.getNext(it)); ) {
switch (d->getDescriptorTag()) {
case SI::AC3DescriptorTag:
dbgpatpmt(" AC3");
dpid = stream.getPid();
break;
case SI::SubtitlingDescriptorTag:
dbgpatpmt(" subtitling");
if (NumSpids < MAXSPIDS) {
SI::SubtitlingDescriptor *sd = (SI::SubtitlingDescriptor *)d;
SI::SubtitlingDescriptor::Subtitling sub;
char SLangs[MAXLANGCODE2] = "";
char *s = SLangs;
int n = 0;
for (SI::Loop::Iterator it; sd->subtitlingLoop.getNext(sub, it); ) {
if (sub.languageCode[0]) {
dbgpatpmt(" '%s'", sub.languageCode);
if (n > 0)
*s++ = '+';
strn0cpy(s, I18nNormalizeLanguageCode(sub.languageCode), MAXLANGCODE1);
s += strlen(s);
if (n++ > 1)
break;
}
}
cDevice::PrimaryDevice()->SetAvailableTrack(ttSubtitle, NumSpids, stream.getPid(), SLangs);
NumSpids++;
}
break;
case SI::ISO639LanguageDescriptorTag: {
SI::ISO639LanguageDescriptor *ld = (SI::ISO639LanguageDescriptor *)d;
dbgpatpmt(" '%s'", ld->languageCode);
strn0cpy(lang, I18nNormalizeLanguageCode(ld->languageCode), MAXLANGCODE1);
}
break;
default: ;
}
delete d;
}
if (dpid) {
if (NumDpids < MAXDPIDS) {
cDevice::PrimaryDevice()->SetAvailableTrack(ttDolby, NumDpids, dpid, lang);
NumDpids++;
}
}
}
break;
}
dbgpatpmt("\n");
cDevice::PrimaryDevice()->EnsureAudioTrack(true);
cDevice::PrimaryDevice()->EnsureSubtitleTrack();
}
}
else
esyslog("ERROR: can't parse PMT");
pmtSize = 0;
}
// --- cTsToPes --------------------------------------------------------------
cTsToPes::cTsToPes(void)
{
data = NULL;
size = length = 0;
synced = false;
}
cTsToPes::~cTsToPes()
{
free(data);
}
void cTsToPes::PutTs(const uchar *Data, int Length)
{
if (TsPayloadStart(Data))
Reset();
else if (!size)
return; // skip everything before the first payload start
Length = TsGetPayload(&Data);
if (length + Length > size) {
size = max(KILOBYTE(2), length + Length);
data = (uchar *)realloc(data, size);
}
memcpy(data + length, Data, Length);
length += Length;
}
const uchar *cTsToPes::GetPes(int &Length)
{
if (PesLongEnough(length)) {
Length = PesLength(data);
if (Length <= length) {
Length = length; // in case the PES packet has no explicit length, as is the case for video PES
return data;
}
}
return NULL;
}
void cTsToPes::Reset(void)
{
length = 0;
}
// --- Some helper functions for debugging -----------------------------------
void BlockDump(const char *Name, const u_char *Data, int Length)
{
printf("--- %s\n", Name);
for (int i = 0; i < Length; i++) {
if (i && (i % 16) == 0)
printf("\n");
printf(" %02X", Data[i]);
}
printf("\n");
}
void TsDump(const char *Name, const u_char *Data, int Length)
{
printf("%s: %04X", Name, Length);
int n = min(Length, 20);
for (int i = 0; i < n; i++)
printf(" %02X", Data[i]);
if (n < Length) {
printf(" ...");
n = max(n, Length - 10);
for (n = max(n, Length - 10); n < Length; n++)
printf(" %02X", Data[n]);
}
printf("\n");
}
void PesDump(const char *Name, const u_char *Data, int Length)
{
TsDump(Name, Data, Length);
}

191
remux.h
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: remux.h 1.17 2007/09/02 10:19:06 kls Exp $ * $Id: remux.h 2.1 2008/08/15 14:09:16 kls Exp $
*/ */
#ifndef __REMUX_H #ifndef __REMUX_H
@ -12,6 +12,7 @@
#include <time.h> //XXX FIXME: DVB/linux/dvb/dmx.h should include <time.h> itself!!! #include <time.h> //XXX FIXME: DVB/linux/dvb/dmx.h should include <time.h> itself!!!
#include <linux/dvb/dmx.h> #include <linux/dvb/dmx.h>
#include "channels.h"
#include "ringbuffer.h" #include "ringbuffer.h"
#include "tools.h" #include "tools.h"
@ -81,4 +82,192 @@ public:
static int ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &PictureType); static int ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &PictureType);
}; };
// Some TS handling tools.
// The following functions all take a pointer to one complete TS packet.
#define TS_SYNC_BYTE 0x47
#define TS_SIZE 188
#define TS_ADAPT_FIELD_EXISTS 0x20
#define TS_PAYLOAD_EXISTS 0x10
#define TS_CONT_CNT_MASK 0x0F
#define TS_PAYLOAD_START 0x40
#define TS_ERROR 0x80
#define TS_PID_MASK_HI 0x1F
inline int TsHasPayload(const uchar *p)
{
return p[3] & TS_PAYLOAD_EXISTS;
}
inline int TsPayloadStart(const uchar *p)
{
return p[1] & TS_PAYLOAD_START;
}
inline int TsError(const uchar *p)
{
return p[1] & TS_ERROR;
}
inline int TsPid(const uchar *p)
{
return (p[1] & TS_PID_MASK_HI) * 256 + p[2];
}
inline int TsPayloadOffset(const uchar *p)
{
return (p[3] & TS_ADAPT_FIELD_EXISTS) ? p[4] + 5 : 4;
}
inline int TsGetPayload(const uchar **p)
{
int o = TsPayloadOffset(*p);
*p += o;
return TS_SIZE - o;
}
inline int TsContinuityCounter(const uchar *p)
{
return p[3] & TS_CONT_CNT_MASK;
}
// Some PES handling tools:
// The following functions that take a pointer to PES data all assume that
// there is enough data so that PesLongEnough() returns true.
inline bool PesLongEnough(int Length)
{
return Length >= 6;
}
inline int PesLength(const uchar *p)
{
return 6 + p[4] * 256 + p[5];
}
inline int PesPayloadOffset(const uchar *p)
{
return 9 + p[8];
}
inline int64_t PesGetPts(const uchar *p)
{
if ((p[7] & 0x80) && p[8] >= 5) {
return ((((int64_t)p[ 9]) & 0x0E) << 29) |
(( (int64_t)p[10]) << 22) |
((((int64_t)p[11]) & 0xFE) << 14) |
(( (int64_t)p[12]) << 7) |
((((int64_t)p[13]) & 0xFE) >> 1);
}
return 0;
}
// PAT/PMT Generator:
#define MAX_SECTION_SIZE 4096 // maximum size of an SI section
#define MAX_PMT_TS (MAX_SECTION_SIZE / TS_SIZE + 1)
class cPatPmtGenerator {
private:
uchar pat[TS_SIZE]; // the PAT always fits into a single TS packet
uchar pmt[MAX_PMT_TS][TS_SIZE]; // the PMT may well extend over several TS packets
int numPmtPackets;
int patCounter;
int pmtCounter;
int patVersion;
int pmtVersion;
uchar *esInfoLength;
void IncCounter(int &Counter, uchar *TsPacket);
void IncVersion(int &Version);
void IncEsInfoLength(int Length);
protected:
int MakeStream(uchar *Target, uchar Type, int Pid);
int MakeAC3Descriptor(uchar *Target);
int MakeSubtitlingDescriptor(uchar *Target, const char *Language);
int MakeLanguageDescriptor(uchar *Target, const char *Language);
int MakeCRC(uchar *Target, const uchar *Data, int Length);
public:
cPatPmtGenerator(void);
void GeneratePat(void);
///< Generates a PAT section for later use with GetPat().
///< This function is called by default from the constructor.
void GeneratePmt(tChannelID ChannelID);
///< Generates a PMT section for the given ChannelId, for later use
///< with GetPmt().
uchar *GetPat(void);
///< Returns a pointer to the PAT section, which consist of exactly
///< one TS packet.
uchar *GetPmt(int &Index);
///< Returns a pointer to the Index'th TS packet of the PMT section.
///< Index must be initialized to 0 and will be incremented by each
///< call to GetPmt(). Returns NULL is all packets of the PMT section
///< have been fetched..
};
// PAT/PMT Parser:
class cPatPmtParser {
private:
uchar pmt[MAX_SECTION_SIZE];
int pmtSize;
int pmtPid;
int vpid;
int vtype;
protected:
int SectionLength(const uchar *Data, int Length) { return (Length >= 3) ? ((int(Data[1]) & 0x0F) << 8)| Data[2] : 0; }
public:
cPatPmtParser(void);
void ParsePat(const uchar *Data, int Length);
///< Parses the given PAT Data, which is the payload of a single TS packet
///< from the PAT stream. The PAT may consist only of a single TS packet.
void ParsePmt(const uchar *Data, int Length);
///< Parses the given PMT Data, which is the payload of a single TS packet
///< from the PMT stream. The PMT may consist of several TS packets, which
///< are delivered to the parser through several subsequent calls to
///< ParsePmt(). The whole PMT data will be processed once the last packet
///< has been received.
int PmtPid(void) { return pmtPid; }
///< Returns the PMT pid as defined by the current PAT.
///< If no PAT has been received yet, -1 will be returned.
int Vpid(void) { return vpid; }
///< Returns the video pid as defined by the current PMT.
int Vtype(void) { return vtype; }
};
// TS to PES converter:
// Puts together the payload of several TS packets that form one PES
// packet.
class cTsToPes {
private:
uchar *data;
int size;
int length;
bool synced;
public:
cTsToPes(void);
~cTsToPes();
void PutTs(const uchar *Data, int Length);
///< Puts the payload data of the single TS packet at Data into the converter.
///< Length is always 188.
///< If the given TS packet starts a new PES payload packet, the converter
///< will be automatically reset. Any packets before the first one that starts
///< a new PES payload packet will be ignored.
const uchar *GetPes(int &Length);
///< Gets a pointer to the complete PES packet, or NULL if the packet
///< is not complete yet. If the packet is complete, Length will contain
///< the total packet length. The returned pointer is only valid until
///< the next call to PutTs() or Reset(), or until this object is destroyed.
void Reset(void);
///< Resets the converter. This needs to be called after a PES packet has
///< been fetched by a call to GetPes(), and before the next call to
///< PutTs().
};
// Some helper functions for debugging:
void BlockDump(const char *Name, const u_char *Data, int Length);
void TsDump(const char *Name, const u_char *Data, int Length);
void PesDump(const char *Name, const u_char *Data, int Length);
#endif // __REMUX_H #endif // __REMUX_H

View File

@ -4,109 +4,52 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: transfer.c 1.34 2007/01/07 14:45:28 kls Exp $ * $Id: transfer.c 2.1 2008/08/15 14:32:12 kls Exp $
*/ */
#include "transfer.h" #include "transfer.h"
#define TRANSFERBUFSIZE MEGABYTE(2)
#define POLLTIMEOUTS_BEFORE_DEVICECLEAR 6
// --- cTransfer ------------------------------------------------------------- // --- cTransfer -------------------------------------------------------------
cTransfer::cTransfer(tChannelID ChannelID, int VPid, const int *APids, const int *DPids, const int *SPids) cTransfer::cTransfer(tChannelID ChannelID, int VPid, const int *APids, const int *DPids, const int *SPids)
:cReceiver(ChannelID, -1, VPid, APids, Setup.UseDolbyDigital ? DPids : NULL, SPids) :cReceiver(ChannelID, -1, VPid, APids, Setup.UseDolbyDigital ? DPids : NULL, SPids)
,cThread("transfer")
{ {
ringBuffer = new cRingBufferLinear(TRANSFERBUFSIZE, TS_SIZE * 2, true, "Transfer"); patPmtGenerator.GeneratePmt(ChannelID);
remux = new cRemux(VPid, APids, Setup.UseDolbyDigital ? DPids : NULL, SPids);
} }
cTransfer::~cTransfer() cTransfer::~cTransfer()
{ {
cReceiver::Detach(); cReceiver::Detach();
cPlayer::Detach(); cPlayer::Detach();
delete remux;
delete ringBuffer;
} }
void cTransfer::Activate(bool On) void cTransfer::Activate(bool On)
{ {
if (On) if (On) {
Start(); PlayTs(patPmtGenerator.GetPat(), TS_SIZE);
else { int Index = 0;
Cancel(3); while (uchar *pmt = patPmtGenerator.GetPmt(Index))
cPlayer::Detach(); PlayTs(pmt, TS_SIZE);
} }
} }
void cTransfer::Receive(uchar *Data, int Length) void cTransfer::Receive(uchar *Data, int Length)
{ {
if (cPlayer::IsAttached() && Running()) { if (cPlayer::IsAttached()) {
int p = ringBuffer->Put(Data, Length); // Transfer Mode means "live tv", so there's no point in doing any additional
if (p != Length && Running()) // buffering here. The TS packets *must* get through here! However, every
ringBuffer->ReportOverflow(Length - p); // now and then there may be conditions where the packet just can't be
// handled when offered the first time, so that's why we try several times:
for (int i = 0; i < 100; i++) {
if (PlayTs(Data, Length) > 0)
return;
fprintf(stderr, "-");//XXX just for testing - remove when stable
cCondWait::SleepMs(10);
}
esyslog("ERROR: TS packet not accepted in Transfer Mode");
} }
} }
void cTransfer::Action(void)
{
int PollTimeouts = 0;
uchar *p = NULL;
int Result = 0;
while (Running()) {
int Count;
uchar *b = ringBuffer->Get(Count);
if (b) {
if (ringBuffer->Available() > TRANSFERBUFSIZE * 9 / 10) {
// If the buffer runs full, we have no chance of ever catching up
// since the data comes in at the same rate as it goes out (it's "live").
// So let's clear the buffer instead of suffering from permanent
// overflows.
dsyslog("clearing transfer buffer to avoid overflows");
DeviceClear();
ringBuffer->Clear();
remux->Clear();
PlayPes(NULL, 0);
p = NULL;
continue;
}
Count = remux->Put(b, Count);
if (Count)
ringBuffer->Del(Count);
}
if (!p)
p = remux->Get(Result);
if (p) {
cPoller Poller;
if (DevicePoll(Poller, 100)) {
PollTimeouts = 0;
int w = PlayPes(p, Result);
if (w > 0) {
p += w;
Result -= w;
remux->Del(w);
if (Result <= 0)
p = NULL;
}
else if (w < 0 && FATALERRNO)
LOG_ERROR;
}
else {
PollTimeouts++;
if (PollTimeouts == POLLTIMEOUTS_BEFORE_DEVICECLEAR) {
dsyslog("clearing device because of consecutive poll timeouts");
DeviceClear();
ringBuffer->Clear();
remux->Clear();
PlayPes(NULL, 0);
p = NULL;
}
}
}
}
}
// --- cTransferControl ------------------------------------------------------ // --- cTransferControl ------------------------------------------------------
cDevice *cTransferControl::receiverDevice = NULL; cDevice *cTransferControl::receiverDevice = NULL;

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: transfer.h 1.12 2007/01/07 14:45:45 kls Exp $ * $Id: transfer.h 2.1 2008/05/25 12:44:49 kls Exp $
*/ */
#ifndef __TRANSFER_H #ifndef __TRANSFER_H
@ -13,17 +13,13 @@
#include "player.h" #include "player.h"
#include "receiver.h" #include "receiver.h"
#include "remux.h" #include "remux.h"
#include "ringbuffer.h"
#include "thread.h"
class cTransfer : public cReceiver, public cPlayer, public cThread { class cTransfer : public cReceiver, public cPlayer {
private: private:
cRingBufferLinear *ringBuffer; cPatPmtGenerator patPmtGenerator;
cRemux *remux;
protected: protected:
virtual void Activate(bool On); virtual void Activate(bool On);
virtual void Receive(uchar *Data, int Length); virtual void Receive(uchar *Data, int Length);
virtual void Action(void);
public: public:
cTransfer(tChannelID ChannelID, int VPid, const int *APids, const int *DPids, const int *SPids); cTransfer(tChannelID ChannelID, int VPid, const int *APids, const int *DPids, const int *SPids);
virtual ~cTransfer(); virtual ~cTransfer();

8
vdr.5
View File

@ -8,7 +8,7 @@
.\" License as specified in the file COPYING that comes with the .\" License as specified in the file COPYING that comes with the
.\" vdr distribution. .\" vdr distribution.
.\" .\"
.\" $Id: vdr.5 2.3 2008/05/02 13:48:31 kls Exp $ .\" $Id: vdr.5 2.4 2008/07/06 13:00:19 kls Exp $
.\" .\"
.TH vdr 5 "10 Feb 2008" "1.6" "Video Disk Recorder Files" .TH vdr 5 "10 Feb 2008" "1.6" "Video Disk Recorder Files"
.SH NAME .SH NAME
@ -126,7 +126,13 @@ The symbol rate of this channel (DVB-S and DVB-C only).
The video PID (set to '0' for radio channels). The video PID (set to '0' for radio channels).
If this channel uses a separate PCR PID, it follows the VPID, separated by a If this channel uses a separate PCR PID, it follows the VPID, separated by a
plus sign, as in plus sign, as in
.B ...:164+17:... .B ...:164+17:...
If this channel has a video mode other than 0, the mode
follows the pids, separated by an '=' sign, as in
.B ...:164+17=27:...
.TP .TP
.B APID .B APID
The audio PID (either one number, or several, separated by commas). The audio PID (either one number, or several, separated by commas).