diff options
Diffstat (limited to 'remux.c')
-rw-r--r-- | remux.c | 539 |
1 files changed, 493 insertions, 46 deletions
@@ -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.5 2001/06/24 16:37:23 kls Exp $ */ /* The calling interface of the 'cRemux::Process()' function is defined @@ -62,17 +66,390 @@ */ #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 + +// Start codes: +#define SC_PICTURE 0x00 // "picture header" + +#define MAXNONUSEFULDATA (10*1024*1024) + +class cTS2PES { +private: + int size; + int found; + int count; + uint8_t *buf; + uint8_t cid; + uint8_t audioCid; + 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, uint8_t AudioCid = 0x00); + ~cTS2PES(); + void ts_to_pes(const uint8_t *Buf); // don't need count (=188) + void Clear(void); + }; + +uint8_t cTS2PES::headr[] = { 0x00, 0x00, 0x01 }; -cRemux::cRemux(void) +cTS2PES::cTS2PES(uint8_t *ResultBuffer, int *ResultCount, int Size, uint8_t AudioCid) { + resultBuffer = ResultBuffer; + resultCount = ResultCount; + size = Size; + audioCid = AudioCid; + + if (!(buf = new uint8_t[size])) + esyslog(LOG_ERR, "Not enough memory for ts_transform"); + + reset_ipack(); +} + +cTS2PES::~cTS2PES() +{ + delete buf; +} + +void cTS2PES::Clear(void) +{ + reset_ipack(); +} + +void cTS2PES::store(uint8_t *Data, int Count) +{ + if (*resultCount + Count > RESULTBUFFERSIZE) { + esyslog(LOG_ERR, "ERROR: result buffer overflow (%d + %d > %d)", *resultCount, Count, RESULTBUFFERSIZE); + Count = RESULTBUFFERSIZE - *resultCount; + } + 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] = (AUDIO_STREAM_S <= cid && cid <= AUDIO_STREAM_E && audioCid) ? audioCid : 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; + } + + 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(int VPid, int APid1, int APid2, int DPid1, int DPid2, bool ExitOnFailure) +{ + vPid = VPid; + aPid1 = APid1; + aPid2 = APid2; + dPid1 = DPid1; + dPid2 = DPid2; + exitOnFailure = ExitOnFailure; synced = false; + skipped = 0; + resultCount = resultDelivered = 0; + vTS2PES = new cTS2PES(resultBuffer, &resultCount, IPACKS); + aTS2PES1 = new cTS2PES(resultBuffer, &resultCount, IPACKS, 0xC0); + aTS2PES2 = aPid2 ? new cTS2PES(resultBuffer, &resultCount, IPACKS, 0xC1) : NULL; + dTS2PES1 = dPid1 ? new cTS2PES(resultBuffer, &resultCount, IPACKS) : NULL; + //XXX don't yet know how to tell apart primary and secondary DD data... + dTS2PES2 = /*XXX dPid2 ? new cTS2PES(resultBuffer, &resultCount, IPACKS) : XXX*/ NULL; } cRemux::~cRemux() { + delete vTS2PES; + delete aTS2PES1; + delete aTS2PES2; + delete dTS2PES1; + delete dTS2PES2; +} + +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 +481,140 @@ 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) +void cRemux::SetAudioPid(int APid) { - int Skip = 0; + aPid1 = APid; + vTS2PES->Clear(); + aTS2PES1->Clear(); + resultCount = resultDelivered = 0; +} - PictureType = NO_PICTURE; +const uchar *cRemux::Process(const uchar *Data, int &Count, int &Result, uchar *PictureType) +{ + uchar dummyPictureType; + if (!PictureType) + PictureType = &dummyPictureType; - 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]) { - case SC_VIDEO: +/*XXX + // test recording the raw TS: + Result = Count; + *PictureType = I_FRAME; + return Data; +XXX*/ + + // 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; + int pid = GetPid(Data + i + 1); + if (Data[i + 3] & 0x10) { // got payload + if (pid == vPid) vTS2PES->ts_to_pes(Data + i); + else if (pid == aPid1) aTS2PES1->ts_to_pes(Data + i); + else if (pid == aPid2 && aTS2PES2) aTS2PES2->ts_to_pes(Data + i); + else if (pid == dPid1 && dTS2PES1) dTS2PES1->ts_to_pes(Data + i); + else if (pid == dPid2 && dTS2PES2) dTS2PES2->ts_to_pes(Data + i); + } + 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 > MAXNONUSEFULDATA) { + 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 VIDEO_STREAM_S ... VIDEO_STREAM_E: { 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! + case PRIVATE_STREAM1: + case AUDIO_STREAM_S ... AUDIO_STREAM_E: + { + 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 - |