summaryrefslogtreecommitdiff
path: root/remux.c
diff options
context:
space:
mode:
Diffstat (limited to 'remux.c')
-rw-r--r--remux.c499
1 files changed, 455 insertions, 44 deletions
diff --git a/remux.c b/remux.c
index e48296d8..6212ff07 100644
--- a/remux.c
+++ b/remux.c
@@ -4,7 +4,11 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: remux.c 1.1 2001/03/31 08:42:17 kls Exp $
+ * The parts of this code that implement cTS2PES have been taken from
+ * the Linux DVB driver's 'tuxplayer' example and were rewritten to suit
+ * VDR's needs.
+ *
+ * $Id: remux.c 1.2 2001/05/27 10:27:15 kls Exp $
*/
/* The calling interface of the 'cRemux::Process()' function is defined
@@ -62,17 +66,364 @@
*/
#include "remux.h"
+#include "thread.h"
#include "tools.h"
-#if defined(REMUX_NONE)
+// --- cTS2PES ---------------------------------------------------------------
+
+#include <netinet/in.h>
+
+//XXX TODO: these should really be available in some driver header file!
+#define PROG_STREAM_MAP 0xBC
+#ifndef PRIVATE_STREAM1
+#define PRIVATE_STREAM1 0xBD
+#endif
+#define PADDING_STREAM 0xBE
+#ifndef PRIVATE_STREAM2
+#define PRIVATE_STREAM2 0xBF
+#endif
+#define AUDIO_STREAM_S 0xC0
+#define AUDIO_STREAM_E 0xDF
+#define VIDEO_STREAM_S 0xE0
+#define VIDEO_STREAM_E 0xEF
+#define ECM_STREAM 0xF0
+#define EMM_STREAM 0xF1
+#define DSM_CC_STREAM 0xF2
+#define ISO13522_STREAM 0xF3
+#define PROG_STREAM_DIR 0xFF
+
+//pts_dts flags
+#define PTS_ONLY 0x80
+
+#define TS_SIZE 188
+#define PAY_START 0x40
+#define PID_MASK_HI 0x1F
+//flags
+#define ADAPT_FIELD 0x20
+//XXX TODO
+
+#define MAX_PLENGTH 0xFFFF
+#define MMAX_PLENGTH (4*MAX_PLENGTH)
+
+#define IPACKS 2048
+
+class cTS2PES {
+private:
+ int size;
+ int found;
+ int count;
+ uint8_t *buf;
+ uint8_t cid;
+ int plength;
+ uint8_t plen[2];
+ uint8_t flag1;
+ uint8_t flag2;
+ uint8_t hlength;
+ int mpeg;
+ uint8_t check;
+ int which;
+ bool done;
+ uint8_t *resultBuffer;
+ int *resultCount;
+ static uint8_t headr[];
+ void store(uint8_t *Data, int Count);
+ void reset_ipack(void);
+ void send_ipack(void);
+ void write_ipack(const uint8_t *Data, int Count);
+ void instant_repack(const uint8_t *Buf, int Count);
+public:
+ cTS2PES(uint8_t *ResultBuffer, int *ResultCount, int Size);
+ ~cTS2PES();
+ void ts_to_pes(const uint8_t *Buf); // don't need count (=188)
+ };
+
+uint8_t cTS2PES::headr[] = { 0x00, 0x00, 0x01 };
+
+cTS2PES::cTS2PES(uint8_t *ResultBuffer, int *ResultCount, int Size)
+{
+ resultBuffer = ResultBuffer;
+ resultCount = ResultCount;
+ size = Size;
+
+ if (!(buf = new uint8_t[size]))
+ esyslog(LOG_ERR, "Not enough memory for ts_transform");
+
+ reset_ipack();
+}
+
+cTS2PES::~cTS2PES()
+{
+ delete buf;
+}
+
+void cTS2PES::store(uint8_t *Data, int Count)
+{
+ //XXX overflow check???
+ memcpy(resultBuffer + *resultCount, Data, Count);
+ *resultCount += Count;
+}
+
+void cTS2PES::reset_ipack(void)
+{
+ found = 0;
+ cid = 0;
+ plength = 0;
+ flag1 = 0;
+ flag2 = 0;
+ hlength = 0;
+ mpeg = 0;
+ check = 0;
+ which = 0;
+ done = false;
+ count = 0;
+}
+
+void cTS2PES::send_ipack(void)
+{
+ if (count < 10)
+ return;
+ buf[3] = cid;
+ buf[4] = (uint8_t)(((count - 6) & 0xFF00) >> 8);
+ buf[5] = (uint8_t)((count - 6) & 0x00FF);
+ store(buf, count);
+
+ switch (mpeg) {
+ case 2:
+ buf[6] = 0x80;
+ buf[7] = 0x00;
+ buf[8] = 0x00;
+ count = 9;
+ break;
+ case 1:
+ buf[6] = 0x0F;
+ count = 7;
+ break;
+ }
+}
+
+void cTS2PES::write_ipack(const uint8_t *Data, int Count)
+{
+ if (count < 6) {
+ memcpy(buf, headr, 3);
+ count = 6;
+ }
+
+ if (count + Count < size) {
+ memcpy(buf + count, Data, Count);
+ count += Count;
+ }
+ else {
+ int rest = size - count;
+ memcpy(buf + count, Data, rest);
+ count += rest;
+ send_ipack();
+ if (Count - rest > 0)
+ write_ipack(Data + rest, Count - rest);
+ }
+}
+
+void cTS2PES::instant_repack(const uint8_t *Buf, int Count)
+{
+ int c = 0;
+
+ while (c < Count && (mpeg == 0 || (mpeg == 1 && found < 7) || (mpeg == 2 && found < 9)) && (found < 5 || !done)) {
+ switch (found ) {
+ case 0:
+ case 1:
+ if (Buf[c] == 0x00)
+ found++;
+ else
+ found = 0;
+ c++;
+ break;
+ case 2:
+ if (Buf[c] == 0x01)
+ found++;
+ else if (Buf[c] != 0)
+ found = 0;
+ c++;
+ break;
+ case 3:
+ cid = 0;
+ switch (Buf[c]) {
+ case PROG_STREAM_MAP:
+ case PRIVATE_STREAM2:
+ case PROG_STREAM_DIR:
+ case ECM_STREAM :
+ case EMM_STREAM :
+ case PADDING_STREAM :
+ case DSM_CC_STREAM :
+ case ISO13522_STREAM:
+ done = true;
+ case PRIVATE_STREAM1:
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ found++;
+ cid = Buf[c++];
+ break;
+ default:
+ found = 0;
+ break;
+ }
+ break;
+ case 4:
+ if (Count - c > 1) {
+ unsigned short *pl = (unsigned short *)(Buf + c);
+ plength = ntohs(*pl);
+ c += 2;
+ found += 2;
+ }
+ else {
+ plen[0] = Buf[c];
+ found++;
+ return;
+ }
+ break;
+ case 5: {
+ plen[1] = Buf[c++];
+ unsigned short *pl = (unsigned short *)plen;
+ plength = ntohs(*pl);
+ found++;
+ }
+ break;
+ case 6:
+ if (!done) {
+ flag1 = Buf[c++];
+ found++;
+ if ((flag1 & 0xC0) == 0x80 )
+ mpeg = 2;
+ else {
+ esyslog(LOG_INFO, "ERROR: can't record MPEG1!");
+ hlength = 0;
+ which = 0;
+ mpeg = 1;
+ flag2 = 0;
+ }
+ }
+ break;
+ case 7:
+ if (!done && mpeg == 2) {
+ flag2 = Buf[c++];
+ found++;
+ }
+ break;
+ case 8:
+ if (!done && mpeg == 2) {
+ hlength = Buf[c++];
+ found++;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (!plength)
+ plength = MMAX_PLENGTH - 6;
+
+ if (done || ((mpeg == 2 && found >= 9) || (mpeg == 1 && found >= 7))) {
+ switch (cid) {
+ case AUDIO_STREAM_S ... AUDIO_STREAM_E:
+ case VIDEO_STREAM_S ... VIDEO_STREAM_E:
+ case PRIVATE_STREAM1:
+
+ if (mpeg == 2 && found == 9) {
+ write_ipack(&flag1, 1);
+ write_ipack(&flag2, 1);
+ write_ipack(&hlength, 1);
+ }
+
+ if (mpeg == 2 && (flag2 & PTS_ONLY) && found < 14) {
+ while (c < Count && found < 14) {
+ write_ipack(Buf + c, 1);
+ c++;
+ found++;
+ }
+ if (c == Count)
+ return;
+ }
-cRemux::cRemux(void)
+ while (c < Count && found < plength + 6) {
+ int l = Count - c;
+ if (l + found > plength + 6)
+ l = plength + 6 - found;
+ write_ipack(Buf + c, l);
+ found += l;
+ c += l;
+ }
+
+ break;
+ }
+
+ if (done) {
+ if (found + Count - c < plength + 6) {
+ found += Count - c;
+ c = Count;
+ }
+ else {
+ c += plength + 6 - found;
+ found = plength + 6;
+ }
+ }
+
+ if (plength && found == plength + 6) {
+ send_ipack();
+ reset_ipack();
+ if (c < Count)
+ instant_repack(Buf + c, Count - c);
+ }
+ }
+ return;
+}
+
+void cTS2PES::ts_to_pes(const uint8_t *Buf) // don't need count (=188)
+{
+ if (!Buf)
+ return;
+
+ if (Buf[1] & PAY_START) {
+ if (plength == MMAX_PLENGTH - 6 && found > 6) {
+ plength = found - 6;
+ found = 0;
+ send_ipack();
+ reset_ipack();
+ }
+ }
+
+ uint8_t off = 0;
+
+ if (Buf[3] & ADAPT_FIELD) { // adaptation field?
+ off = Buf[4] + 1;
+ if (off + 4 > 187)
+ return;
+ }
+
+ instant_repack(Buf + 4 + off, TS_SIZE - 4 - off);
+}
+
+// --- cRemux ----------------------------------------------------------------
+
+cRemux::cRemux(dvb_pid_t VPid, dvb_pid_t APid, bool ExitOnFailure)
{
+ vPid = VPid;
+ aPid = APid;
+ exitOnFailure = ExitOnFailure;
synced = false;
+ skipped = 0;
+ resultCount = resultDelivered = 0;
+ vTS2PES = new cTS2PES(resultBuffer, &resultCount, IPACKS);
+ aTS2PES = new cTS2PES(resultBuffer, &resultCount, IPACKS);
}
cRemux::~cRemux()
{
+ delete vTS2PES;
+ delete aTS2PES;
+}
+
+int cRemux::GetPid(const uchar *Data)
+{
+ return (((uint16_t)Data[0] & PID_MASK_HI) << 8) | (Data[1] & 0xFF);
}
int cRemux::GetPacketLength(const uchar *Data, int Count, int Offset)
@@ -104,70 +455,130 @@ int cRemux::ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &Pic
return -1;
}
-const uchar *cRemux::Process(const uchar *Data, int &Count, int &Result, uchar &PictureType)
+const uchar *cRemux::Process(const uchar *Data, int &Count, int &Result, uchar *PictureType)
{
- int Skip = 0;
+ uchar dummyPictureType;
+ if (!PictureType)
+ PictureType = &dummyPictureType;
- PictureType = NO_PICTURE;
+/*XXX
+ // test recording the raw TS:
+ Result = Count;
+ *PictureType = I_FRAME;
+ return Data;
+XXX*/
- if (Count >= MINVIDEODATA) {
- for (int i = 0; i < Count; i++) {
- if (Data[i] == 0 && Data[i + 1] == 0 && Data[i + 2] == 1) {
- switch (Data[i + 3]) {
+ // Remove any previously delivered data from the result buffer:
+
+ if (resultDelivered) {
+ if (resultDelivered < resultCount)
+ memmove(resultBuffer, resultBuffer + resultDelivered, resultCount - resultDelivered);
+ resultCount -= resultDelivered;
+ resultDelivered = 0;
+ }
+
+ // Convert incoming TS data into multiplexed PES:
+
+ int used = 0;
+ for (int i = 0; i < Count; i += TS_SIZE) {
+ if (Count - i < TS_SIZE)
+ break;
+ dvb_pid_t pid = GetPid(Data + i + 1);
+ if (Data[i + 3] & 0x10) { // got payload
+ if (pid == vPid)
+ vTS2PES->ts_to_pes(Data + i);
+ else if (pid == aPid)
+ aTS2PES->ts_to_pes(Data + i);
+ }
+ used += TS_SIZE;
+ if (resultCount > (int)sizeof(resultBuffer) / 2)
+ break;
+ }
+ Count = used;
+
+/*XXX
+ // test recording without determining the real frame borders:
+ *PictureType = I_FRAME;
+ Result = resultDelivered = resultCount;
+ return Result ? resultBuffer : NULL;
+XXX*/
+
+ // Check if we're getting anywhere here:
+
+ if (!synced && skipped >= 0) {
+ if (skipped > 1024*1024) {
+ esyslog(LOG_ERR, "ERROR: no useful data seen within %d byte of video stream", skipped);
+ skipped = -1;
+ if (exitOnFailure)
+ cThread::EmergencyExit(true);
+ }
+ else
+ skipped += Count;
+ }
+
+ // Check for frame borders:
+
+ *PictureType = NO_PICTURE;
+
+ if (resultCount >= MINVIDEODATA) {
+ for (int i = 0; i < resultCount; i++) {
+ if (resultBuffer[i] == 0 && resultBuffer[i + 1] == 0 && resultBuffer[i + 2] == 1) {
+ switch (resultBuffer[i + 3]) {
case SC_VIDEO:
{
uchar pt = NO_PICTURE;
- int l = ScanVideoPacket(Data, Count, i, pt);
- if (l < 0) {
- if (Skip < Count)
- Count = Skip;
+ int l = ScanVideoPacket(resultBuffer, resultCount, i, pt);
+ if (l < 0)
return NULL; // no useful data found, wait for more
- }
if (pt != NO_PICTURE) {
if (pt < I_FRAME || B_FRAME < pt) {
esyslog(LOG_ERR, "ERROR: unknown picture type '%d'", pt);
}
- else if (PictureType == NO_PICTURE) {
- if (!synced) {
- if (pt == I_FRAME) {
- Skip = i;
- synced = true;
- }
- else {
- i += l;
- Skip = i;
- break;
- }
+ else if (!synced) {
+ if (pt == I_FRAME) {
+ resultDelivered = i; // will drop everything before this position
+ synced = true;
+ }
+ else {
+ resultDelivered = i + l; // will drop everything before and including this packet
+ return NULL;
}
- if (synced)
- PictureType = pt;
- }
- else {
- Count = i;
- Result = i - Skip;
- return Data + Skip;
}
}
- else if (!synced) {
- i += l;
- Skip = i;
- break;
+ if (synced) {
+ *PictureType = pt;
+ Result = l;
+ const uchar *p = resultBuffer + resultDelivered;
+ resultDelivered += l;
+ return p;
+ }
+ else {
+ resultDelivered = i + l; // will drop everything before and including this packet
+ return NULL;
}
- i += l - 1; // -1 to compensate for i++ in the loop!
}
break;
case SC_AUDIO:
- i += GetPacketLength(Data, Count, i) - 1; // -1 to compensate for i++ in the loop!
+ {
+ int l = GetPacketLength(resultBuffer, resultCount, i);
+ if (l < 0)
+ return NULL; // no useful data found, wait for more
+ if (synced) {
+ Result = l;
+ const uchar *p = resultBuffer + resultDelivered;
+ resultDelivered += l;
+ return p;
+ }
+ else {
+ resultDelivered = i + l; // will drop everything before and including this packet
+ return NULL;
+ }
+ }
break;
}
}
}
}
- if (Skip < Count)
- Count = Skip;
return NULL; // no useful data found, wait for more
}
-#elif defined(REMUX_TEST)
-#endif
-