summaryrefslogtreecommitdiff
path: root/remux.c
diff options
context:
space:
mode:
authorKlaus Schmidinger <kls (at) cadsoft (dot) de>2008-09-06 18:00:00 +0200
committerKlaus Schmidinger <kls (at) cadsoft (dot) de>2008-09-06 18:00:00 +0200
commitc848ab793a302dc067663ec4a06395745e443c9d (patch)
treea6c65facbf68864b9523152560a4ae23a6ad2f16 /remux.c
parent771986b89fc19b4ae65179ccf7dd8082512f8b7d (diff)
downloadvdr-patch-lnbsharing-c848ab793a302dc067663ec4a06395745e443c9d.tar.gz
vdr-patch-lnbsharing-c848ab793a302dc067663ec4a06395745e443c9d.tar.bz2
Version 1.7.1vdr-1.7.1
- 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. - Updated the Italian OSD texts (thanks to Diego Pierotto). - Removed obsolete $(NCURSESLIB) from the Makefile. - Implemented handling the standard component descriptor for AC3 (stream=4), as it will soon be used by the German ARD channels (thanks to Michael Pennewiß for advance information about this change). The previously used "Premiere pseudo standard" (stream=2, type=5) still works, but has apparently been wrongfully used by broadcasters from the beginning. - Added missing description of the 'S' channel parameter to vdr.5 (reported by Reinhard Nissl). - The SVDRP signon message now indicates the character encoding in use, as in "220 video SVDRP VideoDiskRecorder 1.7.1; Fri May 2 16:17:10 2008; ISO-8859-1". This may be useful for instance for external tools that provide EPG data, so that they can correctly encode the strings. - No longer calling FcFini() to avoid problems with older (broken) versions of fontconfig (suggested by Edgar Toernig). - 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 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. - Updated the sources.conf file (thanks to Oleg Roitburd). - Fixed a possible integer overflow in GetAbsTime() (thanks to Alexander Rieger). - Fixed a problem with calling isyslog() from within the SignalHandler() (thanks to Udo Richter). - Replaced the Finnish language code "smi" with "suo" (thanks to Rolf Ahrenberg). - Fixed wrong value for TableIdBAT in libsi/si.h (thanks to Winfried Köhler). - Errors in config files no longer keep VDR from starting. - Removed unneeded include files <linux/dvb/dmx.h> und <time.h> from remux.h (reported by Tobias Grimm).
Diffstat (limited to 'remux.c')
-rw-r--r--remux.c477
1 files changed, 473 insertions, 4 deletions
diff --git a/remux.c b/remux.c
index 6b42209..8312a74 100644
--- a/remux.c
+++ b/remux.c
@@ -11,15 +11,24 @@
* The cRepacker family's code was originally written by Reinhard Nissl <rnissl@gmx.de>,
* and adapted to the VDR coding style by Klaus.Schmidinger@cadsoft.de.
*
- * $Id: remux.c 2.0 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 <stdlib.h>
#include "channels.h"
+#include "device.h"
+#include "libsi/si.h"
+#include "libsi/section.h"
+#include "libsi/descriptor.h"
#include "shutdown.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)
{
if (Count < 7)
@@ -1413,7 +1422,6 @@ int cDolbyRepacker::BreakAt(const uchar *Data, int Count)
//pts_dts flags
#define PTS_ONLY 0x80
-#define TS_SIZE 188
#define PID_MASK_HI 0x1F
#define CONT_CNT_MASK 0x0F
@@ -2007,8 +2015,6 @@ int cRemux::ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &Pic
return -1;
}
-#define TS_SYNC_BYTE 0x47
-
int cRemux::Put(const uchar *Data, int Count)
{
int used = 0;
@@ -2182,3 +2188,466 @@ void cRemux::SetBrokenLink(uchar *Data, int Length)
else
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);
+}