mirror of
				https://github.com/vdr-projects/vdr.git
				synced 2025-03-01 10:50:46 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			639 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			639 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * remux.c: A streaming MPEG2 remultiplexer
 | |
|  *
 | |
|  * See the main source file 'vdr.c' for copyright information and
 | |
|  * how to reach the author.
 | |
|  *
 | |
|  * 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.22 2004/11/16 16:49:03 kls Exp $
 | |
|  */
 | |
| 
 | |
| #include "remux.h"
 | |
| #include <stdlib.h>
 | |
| #include "thread.h"
 | |
| #include "tools.h"
 | |
| 
 | |
| // --- 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 PID_MASK_HI    0x1F
 | |
| #define CONT_CNT_MASK  0x0F
 | |
| 
 | |
| // Flags:
 | |
| #define PAY_START      0x40
 | |
| #define TS_ERROR       0x80
 | |
| #define ADAPT_FIELD    0x20
 | |
| 
 | |
| #define MAX_PLENGTH  0xFFFF          // the maximum PES packet length (theoretically)
 | |
| #define MMAX_PLENGTH (8*MAX_PLENGTH) // some stations send PES packets that are extremely large, e.g. DVB-T in Finland
 | |
| 
 | |
| #define IPACKS 2048
 | |
| 
 | |
| // Start codes:
 | |
| #define SC_PICTURE 0x00  // "picture header"
 | |
| 
 | |
| #define MAXNONUSEFULDATA (10*1024*1024)
 | |
| #define MAXNUMUPTERRORS  10
 | |
| 
 | |
| 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;
 | |
|   cRingBufferLinear *resultBuffer;
 | |
|   int tsErrors;
 | |
|   int ccErrors;
 | |
|   int ccCounter;
 | |
|   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(cRingBufferLinear *ResultBuffer, 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 };
 | |
| 
 | |
| cTS2PES::cTS2PES(cRingBufferLinear *ResultBuffer, int Size, uint8_t AudioCid)
 | |
| {
 | |
|   resultBuffer = ResultBuffer;
 | |
|   size = Size;
 | |
|   audioCid = AudioCid;
 | |
| 
 | |
|   tsErrors = 0;
 | |
|   ccErrors = 0;
 | |
|   ccCounter = -1;
 | |
| 
 | |
|   if (!(buf = MALLOC(uint8_t, size)))
 | |
|      esyslog("Not enough memory for ts_transform");
 | |
| 
 | |
|   reset_ipack();
 | |
| }
 | |
| 
 | |
| cTS2PES::~cTS2PES()
 | |
| {
 | |
|   if (tsErrors || ccErrors)
 | |
|      dsyslog("cTS2PES got %d TS errors, %d TS continuity errors", tsErrors, ccErrors);
 | |
|   free(buf);
 | |
| }
 | |
| 
 | |
| void cTS2PES::Clear(void)
 | |
| {
 | |
|   reset_ipack();
 | |
| }
 | |
| 
 | |
| void cTS2PES::store(uint8_t *Data, int Count)
 | |
| {
 | |
|   int n = resultBuffer->Put(Data, Count);
 | |
|   if (n != Count)
 | |
|      esyslog("ERROR: result buffer overflow, dropped %d out of %d byte", Count - n, 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 {
 | |
|                         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 == 1 && found == 7)
 | |
|                write_ipack(&flag1, 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) {
 | |
|         if (plength == MMAX_PLENGTH - 6)
 | |
|            esyslog("ERROR: PES packet length overflow in remuxer (stream corruption)");
 | |
|         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] & TS_ERROR)
 | |
|      tsErrors++;
 | |
|   if ((Buf[3] ^ ccCounter) & CONT_CNT_MASK) {
 | |
|      // This should check duplicates and packets which do not increase the counter.
 | |
|      // But as the errors usually come in bursts this should be enough to
 | |
|      // show you there is something wrong with signal quality.
 | |
|      if (ccCounter != -1 && ((Buf[3] ^ (ccCounter + 1)) & CONT_CNT_MASK)) {
 | |
|         ccErrors++;
 | |
|         // Enable this if you are having problems with signal quality.
 | |
|         // These are the errors I used to get with Nova-T when antenna
 | |
|         // was not positioned correcly (not transport errors). //tvr
 | |
|         //dsyslog("TS continuity error (%d)", ccCounter);
 | |
|         }
 | |
|      ccCounter = Buf[3] & CONT_CNT_MASK;
 | |
|      }
 | |
| 
 | |
|   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 ----------------------------------------------------------------
 | |
| 
 | |
| #define RESULTBUFFERSIZE KILOBYTE(256)
 | |
| 
 | |
| 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;
 | |
|   numUPTerrors = 0;
 | |
|   synced = false;
 | |
|   skipped = 0;
 | |
|   resultSkipped = 0;
 | |
|   resultBuffer = new cRingBufferLinear(RESULTBUFFERSIZE, IPACKS, false, "Result");
 | |
|   resultBuffer->SetTimeouts(0, 100);
 | |
|   vTS2PES  =         new cTS2PES(resultBuffer, IPACKS);
 | |
|   aTS2PES1 =         new cTS2PES(resultBuffer, IPACKS, 0xC0);
 | |
|   aTS2PES2 = aPid2 ? new cTS2PES(resultBuffer, IPACKS, 0xC1) : NULL;
 | |
|   dTS2PES1 = dPid1 ? new cTS2PES(resultBuffer, IPACKS)       : NULL;
 | |
|   //XXX don't yet know how to tell apart primary and secondary DD data...
 | |
|   dTS2PES2 = /*XXX dPid2 ? new cTS2PES(resultBuffer, IPACKS) : XXX*/ NULL;
 | |
| }
 | |
| 
 | |
| cRemux::~cRemux()
 | |
| {
 | |
|   delete vTS2PES;
 | |
|   delete aTS2PES1;
 | |
|   delete aTS2PES2;
 | |
|   delete dTS2PES1;
 | |
|   delete dTS2PES2;
 | |
|   delete resultBuffer;
 | |
| }
 | |
| 
 | |
| 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)
 | |
| {
 | |
|   // Returns the length of the packet starting at Offset, or -1 if Count is
 | |
|   // too small to contain the entire packet.
 | |
|   int Length = (Offset + 5 < Count) ? (Data[Offset + 4] << 8) + Data[Offset + 5] + 6 : -1;
 | |
|   if (Length > 0 && Offset + Length <= Count)
 | |
|      return Length;
 | |
|   return -1;
 | |
| }
 | |
| 
 | |
| int cRemux::ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &PictureType)
 | |
| {
 | |
|   // Scans the video packet starting at Offset and returns its length.
 | |
|   // If the return value is -1 the packet was not completely in the buffer.
 | |
|   int Length = GetPacketLength(Data, Count, Offset);
 | |
|   if (Length > 0) {
 | |
|      if (Length >= 8) {
 | |
|         int i = Offset + 8; // the minimum length of the video packet header
 | |
|         i += Data[i] + 1;   // possible additional header bytes
 | |
|         for (; i < Offset + Length - 5; i++) {
 | |
|             if (Data[i] == 0 && Data[i + 1] == 0 && Data[i + 2] == 1) {
 | |
|                switch (Data[i + 3]) {
 | |
|                  case SC_PICTURE: PictureType = (Data[i + 5] >> 3) & 0x07;
 | |
|                                   return Length;
 | |
|                  }
 | |
|                }
 | |
|             }
 | |
|         }
 | |
|      PictureType = NO_PICTURE;
 | |
|      return Length;
 | |
|      }
 | |
|   return -1;
 | |
| }
 | |
| 
 | |
| #define TS_SYNC_BYTE 0x47
 | |
| 
 | |
| int cRemux::Put(const uchar *Data, int Count)
 | |
| {
 | |
|   int used = 0;
 | |
| 
 | |
|   // Make sure we are looking at a TS packet:
 | |
| 
 | |
|   while (Count > TS_SIZE) {
 | |
|         if (Data[0] == TS_SYNC_BYTE && Data[TS_SIZE] == TS_SYNC_BYTE)
 | |
|            break;
 | |
|         Data++;
 | |
|         Count--;
 | |
|         used++;
 | |
|         }
 | |
|   if (used)
 | |
|      esyslog("ERROR: skipped %d byte to sync on TS packet", used);
 | |
| 
 | |
|   // Convert incoming TS data into multiplexed PES:
 | |
| 
 | |
|   for (int i = 0; i < Count; i += TS_SIZE) {
 | |
|       if (Count - i < TS_SIZE)
 | |
|          break;
 | |
|       if (Data[i] != TS_SYNC_BYTE)
 | |
|          break;
 | |
|       if (resultBuffer->Free() < 2 * IPACKS)
 | |
|          break; // A cTS2PES might write one full packet and also a small rest
 | |
|       int pid = GetPid(Data + i + 1);
 | |
|       if (Data[i + 3] & 0x10) { // got payload
 | |
|          if      (pid == vPid)              vTS2PES->ts_to_pes(Data + i);
 | |
|          else if (pid == aPid1)             aTS2PES1->ts_to_pes(Data + i);
 | |
|          else if (pid == aPid2 && aTS2PES2) aTS2PES2->ts_to_pes(Data + i);
 | |
|          else if (pid == dPid1 && dTS2PES1) dTS2PES1->ts_to_pes(Data + i);
 | |
|          else if (pid == dPid2 && dTS2PES2) dTS2PES2->ts_to_pes(Data + i);
 | |
|          }
 | |
|       used += TS_SIZE;
 | |
|       }
 | |
| 
 | |
|   // Check if we're getting anywhere here:
 | |
|   if (!synced && skipped >= 0) {
 | |
|      if (skipped > MAXNONUSEFULDATA) {
 | |
|         esyslog("ERROR: no useful data seen within %d byte of video stream", skipped);
 | |
|         skipped = -1;
 | |
|         if (exitOnFailure)
 | |
|            cThread::EmergencyExit(true);
 | |
|         }
 | |
|      else
 | |
|         skipped += used;
 | |
|      }
 | |
| 
 | |
|   return used;
 | |
| }
 | |
| 
 | |
| uchar *cRemux::Get(int &Count, uchar *PictureType)
 | |
| {
 | |
|   // Remove any previously skipped data from the result buffer:
 | |
| 
 | |
|   if (resultSkipped > 0) {
 | |
|      resultBuffer->Del(resultSkipped);
 | |
|      resultSkipped = 0;
 | |
|      }
 | |
| 
 | |
| #if 0
 | |
|   // Test recording without determining the real frame borders:
 | |
|   if (PictureType)
 | |
|      *PictureType = I_FRAME;
 | |
|   return resultBuffer->Get(Count);
 | |
| #endif
 | |
| 
 | |
|   // Special VPID case to enable recording radio channels:
 | |
| 
 | |
|   if (vPid == 0 || vPid == 1 || vPid == 0x1FFF) {
 | |
|      // XXX actually '0' should be enough, but '1' must be used with encrypted channels (driver bug?)
 | |
|      // XXX also allowing 0x1FFF to not break Michael Paar's original patch,
 | |
|      // XXX but it would probably be best to only use '0'
 | |
|      if (PictureType)
 | |
|         *PictureType = I_FRAME;
 | |
|      return resultBuffer->Get(Count);
 | |
|      }
 | |
| 
 | |
|   // Check for frame borders:
 | |
| 
 | |
|   if (PictureType)
 | |
|      *PictureType = NO_PICTURE;
 | |
| 
 | |
|   Count = 0;
 | |
|   uchar *resultData = NULL;
 | |
|   int resultCount = 0;
 | |
|   uchar *data = resultBuffer->Get(resultCount);
 | |
|   if (data) {
 | |
|      for (int i = 0; i < resultCount - 3; i++) {
 | |
|          if (data[i] == 0 && data[i + 1] == 0 && data[i + 2] == 1) {
 | |
|             int l = 0;
 | |
|             uchar StreamType = data[i + 3];
 | |
|             if (VIDEO_STREAM_S <= StreamType && StreamType <= VIDEO_STREAM_E) {
 | |
|                uchar pt = NO_PICTURE;
 | |
|                l = ScanVideoPacket(data, resultCount, i, pt);
 | |
|                if (l < 0)
 | |
|                   return resultData;
 | |
|                if (pt != NO_PICTURE) {
 | |
|                   if (pt < I_FRAME || B_FRAME < pt) {
 | |
|                      esyslog("ERROR: unknown picture type '%d'", pt);
 | |
|                      if (++numUPTerrors > MAXNUMUPTERRORS && exitOnFailure)
 | |
|                         cThread::EmergencyExit(true);
 | |
|                      }
 | |
|                   else if (!synced) {
 | |
|                      if (pt == I_FRAME) {
 | |
|                         if (PictureType)
 | |
|                            *PictureType = pt;
 | |
|                         resultSkipped = i; // will drop everything before this position
 | |
|                         SetBrokenLink(data + i, l);
 | |
|                         synced = true;
 | |
|                         }
 | |
|                      }
 | |
|                   else if (Count)
 | |
|                      return resultData;
 | |
|                   else if (PictureType)
 | |
|                      *PictureType = pt;
 | |
|                   }
 | |
|                }
 | |
|             else { //if (AUDIO_STREAM_S <= StreamType && StreamType <= AUDIO_STREAM_E || StreamType == PRIVATE_STREAM1) {
 | |
|                l = GetPacketLength(data, resultCount, i);
 | |
|                if (l < 0)
 | |
|                   return resultData;
 | |
|                }
 | |
|             if (synced) {
 | |
|                if (!Count)
 | |
|                   resultData = data + i;
 | |
|                Count += l;
 | |
|                }
 | |
|             else
 | |
|                resultSkipped = i + l;
 | |
|             if (l > 0)
 | |
|                i += l - 1; // the loop increments, too
 | |
|             }
 | |
|          }
 | |
|      }
 | |
|   return resultData;
 | |
| }
 | |
| 
 | |
| void cRemux::Del(int Count)
 | |
| {
 | |
|   resultBuffer->Del(Count);
 | |
| }
 | |
| 
 | |
| void cRemux::Clear(void)
 | |
| {
 | |
|   if (vTS2PES)  vTS2PES->Clear();
 | |
|   if (aTS2PES1) aTS2PES1->Clear();
 | |
|   if (aTS2PES2) aTS2PES2->Clear();
 | |
|   if (dTS2PES1) dTS2PES1->Clear();
 | |
|   if (dTS2PES2) dTS2PES2->Clear();
 | |
|   resultBuffer->Clear();
 | |
| }
 | |
| 
 | |
| void cRemux::SetBrokenLink(uchar *Data, int Length)
 | |
| {
 | |
|   if (Length > 9 && Data[0] == 0 && Data[1] == 0 && Data[2] == 1 && (Data[3] & 0xF0) == VIDEO_STREAM_S) {
 | |
|      for (int i = Data[8] + 9; i < Length - 7; i++) { // +9 to skip video packet header
 | |
|          if (Data[i] == 0 && Data[i + 1] == 0 && Data[i + 2] == 1 && Data[i + 3] == 0xB8) {
 | |
|             if (!(Data[i + 7] & 0x40)) // set flag only if GOP is not closed
 | |
|                Data[i + 7] |= 0x20;
 | |
|             return;
 | |
|             }
 | |
|          }
 | |
|      dsyslog("SetBrokenLink: no GOP header found in video packet");
 | |
|      }
 | |
|   else
 | |
|      dsyslog("SetBrokenLink: no video packet in frame");
 | |
| }
 |