Home

Dokumentation

Impressum

Dokumentation VDR
 

Main Page   Class Hierarchy   Alphabetical List   Data Structures   File List   Data Fields   Globals  

remux.c

Go to the documentation of this file.
00001 /*
00002  * remux.c: A streaming MPEG2 remultiplexer
00003  *
00004  * See the main source file 'vdr.c' for copyright information and
00005  * how to reach the author.
00006  *
00007  * The parts of this code that implement cTS2PES have been taken from
00008  * the Linux DVB driver's 'tuxplayer' example and were rewritten to suit
00009  * VDR's needs.
00010  *
00011  * $Id: remux.c 1.12 2002/10/12 13:33:54 kls Exp $
00012  */
00013 
00014 /* The calling interface of the 'cRemux::Process()' function is defined
00015    as follows:
00016 
00017    'Data' points to a chunk of data that consists of 'Count' bytes.
00018    The 'Process' function shall try to remultiplex as much of the
00019    data as possible and return a pointer to the resulting buffer.
00020    That buffer typically is different from the incoming 'Data',
00021    but in the simplest case (when 'Process' does nothing) might
00022    as well point to the original 'Data'. When returning, 'Count'
00023    shall be set to the number of bytes that have been processed
00024    (i.e. have been taken from 'Data'), while 'Result' indicates
00025    how many bytes the returned buffer contains. 'PictureType' shall
00026    be set to NO_PICTURE if the returned data does not start a new
00027    picture, or one of I_FRAME, P_FRAME or B_FRAME if a new picture
00028    starting point has been found. This also means that the returned
00029    data buffer may contain at most one entire video frame, because
00030    the next frame must be returned with its own value for 'PictureType'.
00031 
00032    'Process' shall do it's best to keep the latency time as short
00033    as possible in order to allow a quick start of VDR's "Transfer
00034    mode" (displaying the signal of one DVB card on another card).
00035    In order to do that, this function may decide to first pass
00036    through the incoming data (almost) unprocessed, and make
00037    actual processing kick in after a few seconds (if that is at
00038    all possible for the algorithm). This may result in a non-
00039    optimal stream at the beginning, which won't matter for normal
00040    recordings but may make switching through encrypted channels
00041    in "Transfer mode" faster.
00042 
00043    In the resulting data stream, a new packet shall always be started
00044    when a frame border is encountered. VDR needs this in order to
00045    be able to detect and store the frame indexes, and to easily
00046    display single frames in fast forward/back mode. The very first
00047    data block returned shall be the starting point of an I_FRAME.
00048    Everything before that shall be silently dropped.
00049 
00050    If the incoming data is not enough to do remultiplexing, a value
00051    of NULL shall be returned ('Result' has no meaning then). This
00052    will tell the caller to wait for more data to be presented in
00053    the next call. If NULL is returned and 'Count' is not 0, the
00054    caller shall remove 'Count' bytes from the beginning of 'Data'
00055    before the next call. This is the way 'Process' indicates that
00056    it must skip that data.
00057 
00058    Any data that is not used during this call will appear at the
00059    beginning of the incoming 'Data' buffer at the next call, plus
00060    any new data that has become available.
00061 
00062    It is guaranteed that the caller will completely process any
00063    returned data before the next call to 'Process'. That way, 'Process'
00064    can dynamically allocate its return buffer and be sure the caller
00065    doesn't keep any pointers into that buffer.
00066 */
00067 
00068 #include "remux.h"
00069 #include <stdlib.h>
00070 #include "thread.h"
00071 #include "tools.h"
00072 
00073 // --- cTS2PES ---------------------------------------------------------------
00074 
00075 #include <netinet/in.h>
00076 
00077 //XXX TODO: these should really be available in some driver header file!
00078 #define PROG_STREAM_MAP  0xBC
00079 #ifndef PRIVATE_STREAM1
00080 #define PRIVATE_STREAM1  0xBD
00081 #endif
00082 #define PADDING_STREAM   0xBE
00083 #ifndef PRIVATE_STREAM2
00084 #define PRIVATE_STREAM2  0xBF
00085 #endif
00086 #define AUDIO_STREAM_S   0xC0
00087 #define AUDIO_STREAM_E   0xDF
00088 #define VIDEO_STREAM_S   0xE0
00089 #define VIDEO_STREAM_E   0xEF
00090 #define ECM_STREAM       0xF0
00091 #define EMM_STREAM       0xF1
00092 #define DSM_CC_STREAM    0xF2
00093 #define ISO13522_STREAM  0xF3
00094 #define PROG_STREAM_DIR  0xFF
00095 
00096 //pts_dts flags
00097 #define PTS_ONLY         0x80
00098 
00099 #define TS_SIZE        188
00100 #define PAY_START      0x40
00101 #define PID_MASK_HI    0x1F
00102 //flags
00103 #define ADAPT_FIELD    0x20
00104 //XXX TODO
00105 
00106 #define MAX_PLENGTH 0xFFFF
00107 #define MMAX_PLENGTH (4*MAX_PLENGTH)
00108 
00109 #define IPACKS 2048
00110 
00111 // Start codes:
00112 #define SC_PICTURE 0x00  // "picture header"
00113 
00114 #define MAXNONUSEFULDATA (10*1024*1024)
00115 
00116 class cTS2PES {
00117 private:
00118   int size;
00119   int found;
00120   int count;
00121   uint8_t *buf;
00122   uint8_t cid;
00123   uint8_t audioCid;
00124   int plength;
00125   uint8_t plen[2];
00126   uint8_t flag1;
00127   uint8_t flag2;
00128   uint8_t hlength;
00129   int mpeg;
00130   uint8_t check;
00131   int which;
00132   bool done;
00133   uint8_t *resultBuffer;
00134   int *resultCount;
00135   static uint8_t headr[];
00136   void store(uint8_t *Data, int Count);
00137   void reset_ipack(void);
00138   void send_ipack(void);
00139   void write_ipack(const uint8_t *Data, int Count);
00140   void instant_repack(const uint8_t *Buf, int Count);
00141 public:
00142   cTS2PES(uint8_t *ResultBuffer, int *ResultCount, int Size, uint8_t AudioCid = 0x00);
00143   ~cTS2PES();
00144   void ts_to_pes(const uint8_t *Buf); // don't need count (=188)
00145   void Clear(void);
00146   };
00147 
00148 uint8_t cTS2PES::headr[] = { 0x00, 0x00, 0x01 };
00149 
00150 cTS2PES::cTS2PES(uint8_t *ResultBuffer, int *ResultCount, int Size, uint8_t AudioCid)
00151 {
00152   resultBuffer = ResultBuffer;
00153   resultCount = ResultCount;
00154   size = Size;
00155   audioCid = AudioCid;
00156 
00157   if (!(buf = MALLOC(uint8_t, size)))
00158      esyslog("Not enough memory for ts_transform");
00159 
00160   reset_ipack();
00161 }
00162 
00163 cTS2PES::~cTS2PES()
00164 {
00165   free(buf);
00166 }
00167 
00168 void cTS2PES::Clear(void)
00169 {
00170   reset_ipack();
00171 }
00172 
00173 void cTS2PES::store(uint8_t *Data, int Count)
00174 {
00175   if (*resultCount + Count > RESULTBUFFERSIZE) {
00176      esyslog("ERROR: result buffer overflow (%d + %d > %d)", *resultCount, Count, RESULTBUFFERSIZE);
00177      Count = RESULTBUFFERSIZE - *resultCount;
00178      }
00179   memcpy(resultBuffer + *resultCount, Data, Count);
00180   *resultCount += Count;
00181 }
00182 
00183 void cTS2PES::reset_ipack(void)
00184 {
00185   found = 0;
00186   cid = 0;
00187   plength = 0;
00188   flag1 = 0;
00189   flag2 = 0;
00190   hlength = 0;
00191   mpeg = 0;
00192   check = 0;
00193   which = 0;
00194   done = false;
00195   count = 0;
00196 }
00197 
00198 void cTS2PES::send_ipack(void)
00199 {
00200   if (count < 10)
00201      return;
00202   buf[3] = (AUDIO_STREAM_S <= cid && cid <= AUDIO_STREAM_E && audioCid) ? audioCid : cid;
00203   buf[4] = (uint8_t)(((count - 6) & 0xFF00) >> 8);
00204   buf[5] = (uint8_t)((count - 6) & 0x00FF);
00205   store(buf, count);
00206 
00207   switch (mpeg) {
00208     case 2:
00209             buf[6] = 0x80;
00210             buf[7] = 0x00;
00211             buf[8] = 0x00;
00212             count = 9;
00213             break;
00214     case 1:
00215             buf[6] = 0x0F;
00216             count = 7;
00217             break;
00218     }
00219 }
00220 
00221 void cTS2PES::write_ipack(const uint8_t *Data, int Count)
00222 {
00223   if (count < 6) {
00224      memcpy(buf, headr, 3);
00225      count = 6;
00226      }
00227 
00228   if (count + Count < size) {
00229      memcpy(buf + count, Data, Count);
00230      count += Count;
00231      }
00232   else {
00233      int rest = size - count;
00234      memcpy(buf + count, Data, rest);
00235      count += rest;
00236      send_ipack();
00237      if (Count - rest > 0)
00238         write_ipack(Data + rest, Count - rest);
00239      }
00240 }
00241 
00242 void cTS2PES::instant_repack(const uint8_t *Buf, int Count)
00243 {
00244   int c = 0;
00245 
00246   while (c < Count && (mpeg == 0 || (mpeg == 1 && found < 7) || (mpeg == 2 && found < 9)) && (found < 5 || !done)) {
00247         switch (found ) {
00248           case 0:
00249           case 1:
00250                   if (Buf[c] == 0x00)
00251                      found++;
00252                   else
00253                      found = 0;
00254                   c++;
00255                   break;
00256           case 2:
00257                   if (Buf[c] == 0x01)
00258                      found++;
00259                   else if (Buf[c] != 0)
00260                      found = 0;
00261                   c++;
00262                   break;
00263           case 3:
00264                   cid = 0;
00265                   switch (Buf[c]) {
00266                     case PROG_STREAM_MAP:
00267                     case PRIVATE_STREAM2:
00268                     case PROG_STREAM_DIR:
00269                     case ECM_STREAM     :
00270                     case EMM_STREAM     :
00271                     case PADDING_STREAM :
00272                     case DSM_CC_STREAM  :
00273                     case ISO13522_STREAM:
00274                          done = true;
00275                     case PRIVATE_STREAM1:
00276                     case VIDEO_STREAM_S ... VIDEO_STREAM_E:
00277                     case AUDIO_STREAM_S ... AUDIO_STREAM_E:
00278                          found++;
00279                          cid = Buf[c++];
00280                          break;
00281                     default:
00282                          found = 0;
00283                          break;
00284                     }
00285                   break;
00286           case 4:
00287                   if (Count - c > 1) {
00288                      unsigned short *pl = (unsigned short *)(Buf + c);
00289                      plength = ntohs(*pl);
00290                      c += 2;
00291                      found += 2;
00292                      }
00293                   else {
00294                      plen[0] = Buf[c];
00295                      found++;
00296                      return;
00297                      }
00298                   break;
00299           case 5: {
00300                     plen[1] = Buf[c++];
00301                     unsigned short *pl = (unsigned short *)plen;
00302                     plength = ntohs(*pl);
00303                     found++;
00304                   }
00305                   break;
00306           case 6:
00307                   if (!done) {
00308                      flag1 = Buf[c++];
00309                      found++;
00310                      if ((flag1 & 0xC0) == 0x80 )
00311                         mpeg = 2;
00312                      else {
00313                         esyslog("ERROR: error in data stream!");
00314                         hlength = 0;
00315                         which = 0;
00316                         mpeg = 1;
00317                         flag2 = 0;
00318                         }
00319                      }
00320                   break;
00321           case 7:
00322                   if (!done && mpeg == 2) {
00323                      flag2 = Buf[c++];
00324                      found++;
00325                      }
00326                   break;
00327           case 8:
00328                   if (!done && mpeg == 2) {
00329                      hlength = Buf[c++];
00330                      found++;
00331                      }
00332                   break;
00333           default:
00334                   break;
00335           }
00336         }
00337 
00338   if (!plength)
00339      plength = MMAX_PLENGTH - 6;
00340 
00341   if (done || ((mpeg == 2 && found >= 9) || (mpeg == 1 && found >= 7))) {
00342      switch (cid) {
00343        case AUDIO_STREAM_S ... AUDIO_STREAM_E:
00344        case VIDEO_STREAM_S ... VIDEO_STREAM_E:
00345        case PRIVATE_STREAM1:
00346 
00347             if (mpeg == 2 && found == 9) {
00348                write_ipack(&flag1, 1);
00349                write_ipack(&flag2, 1);
00350                write_ipack(&hlength, 1);
00351                }
00352 
00353             if (mpeg == 2 && (flag2 & PTS_ONLY) && found < 14) {
00354                while (c < Count && found < 14) {
00355                      write_ipack(Buf + c, 1);
00356                      c++;
00357                      found++;
00358                      }
00359                if (c == Count)
00360                   return;
00361                }
00362 
00363             while (c < Count && found < plength + 6) {
00364                   int l = Count - c;
00365                   if (l + found > plength + 6)
00366                      l = plength + 6 - found;
00367                   write_ipack(Buf + c, l);
00368                   found += l;
00369                   c += l;
00370                   }
00371 
00372             break;
00373        }
00374 
00375      if (done) {
00376         if (found + Count - c < plength + 6) {
00377            found += Count - c;
00378            c = Count;
00379            }
00380         else {
00381            c += plength + 6 - found;
00382            found = plength + 6;
00383            }
00384         }
00385 
00386      if (plength && found == plength + 6) {
00387         send_ipack();
00388         reset_ipack();
00389         if (c < Count)
00390            instant_repack(Buf + c, Count - c);
00391         }
00392      }
00393   return;
00394 }
00395 
00396 void cTS2PES::ts_to_pes(const uint8_t *Buf) // don't need count (=188)
00397 {
00398   if (!Buf)
00399      return;
00400 
00401   if (Buf[1] & PAY_START) {
00402      if (plength == MMAX_PLENGTH - 6 && found > 6) {
00403         plength = found - 6;
00404         found = 0;
00405         send_ipack();
00406         reset_ipack();
00407         }
00408      }
00409 
00410   uint8_t off = 0;
00411 
00412   if (Buf[3] & ADAPT_FIELD) {  // adaptation field?
00413      off = Buf[4] + 1;
00414      if (off + 4 > 187)
00415         return;
00416      }
00417 
00418   instant_repack(Buf + 4 + off, TS_SIZE - 4 - off);
00419 }
00420 
00421 // --- cRemux ----------------------------------------------------------------
00422 
00423 cRemux::cRemux(int VPid, int APid1, int APid2, int DPid1, int DPid2, bool ExitOnFailure)
00424 {
00425   vPid = VPid;
00426   aPid1 = APid1;
00427   aPid2 = APid2;
00428   dPid1 = DPid1;
00429   dPid2 = DPid2;
00430   exitOnFailure = ExitOnFailure;
00431   synced = false;
00432   skipped = 0;
00433   resultCount = resultDelivered = 0;
00434   vTS2PES  =         new cTS2PES(resultBuffer, &resultCount, IPACKS);
00435   aTS2PES1 =         new cTS2PES(resultBuffer, &resultCount, IPACKS, 0xC0);
00436   aTS2PES2 = aPid2 ? new cTS2PES(resultBuffer, &resultCount, IPACKS, 0xC1) : NULL;
00437   dTS2PES1 = dPid1 ? new cTS2PES(resultBuffer, &resultCount, IPACKS)       : NULL;
00438   //XXX don't yet know how to tell apart primary and secondary DD data...
00439   dTS2PES2 = /*XXX dPid2 ? new cTS2PES(resultBuffer, &resultCount, IPACKS) : XXX*/ NULL;
00440 }
00441 
00442 cRemux::~cRemux()
00443 {
00444   delete vTS2PES;
00445   delete aTS2PES1;
00446   delete aTS2PES2;
00447   delete dTS2PES1;
00448   delete dTS2PES2;
00449 }
00450 
00451 int cRemux::GetPid(const uchar *Data)
00452 {
00453   return (((uint16_t)Data[0] & PID_MASK_HI) << 8) | (Data[1] & 0xFF);
00454 }
00455 
00456 int cRemux::GetPacketLength(const uchar *Data, int Count, int Offset)
00457 {
00458   // Returns the entire length of the packet starting at offset, or -1 in case of error.
00459   return (Offset + 5 < Count) ? (Data[Offset + 4] << 8) + Data[Offset + 5] + 6 : -1;
00460 }
00461 
00462 int cRemux::ScanVideoPacket(const uchar *Data, int Count, int Offset, uchar &PictureType)
00463 {
00464   // Scans the video packet starting at Offset and returns its length.
00465   // If the return value is -1 the packet was not completely in the buffer.
00466 
00467   int Length = GetPacketLength(Data, Count, Offset);
00468   if (Length > 0 && Offset + Length <= Count) {
00469      int i = Offset + 8; // the minimum length of the video packet header
00470      i += Data[i] + 1;   // possible additional header bytes
00471      for (; i < Offset + Length; i++) {
00472          if (Data[i] == 0 && Data[i + 1] == 0 && Data[i + 2] == 1) {
00473             switch (Data[i + 3]) {
00474               case SC_PICTURE: PictureType = (Data[i + 5] >> 3) & 0x07;
00475                                return Length;
00476               }
00477             }
00478          }
00479      PictureType = NO_PICTURE;
00480      return Length;
00481      }
00482   return -1;
00483 }
00484 
00485 #define TS_SYNC_BYTE 0x47
00486 
00487 uchar *cRemux::Process(const uchar *Data, int &Count, int &Result, uchar *PictureType)
00488 {
00489   uchar dummyPictureType;
00490   if (!PictureType)
00491      PictureType = &dummyPictureType;
00492 
00493 /*XXX
00494   // test recording the raw TS:
00495   Result = Count;
00496   *PictureType = I_FRAME;
00497   return Data;
00498 XXX*/
00499 
00500   // Remove any previously delivered data from the result buffer:
00501 
00502   if (resultDelivered) {
00503      if (resultDelivered < resultCount)
00504         memmove(resultBuffer, resultBuffer + resultDelivered, resultCount - resultDelivered);
00505      resultCount -= resultDelivered;
00506      resultDelivered = 0;
00507      }
00508 
00509   int used = 0;
00510 
00511   // Make sure we are looking at a TS packet:
00512 
00513   while (Count > TS_SIZE) {
00514         if (Data[0] == TS_SYNC_BYTE && Data[TS_SIZE] == TS_SYNC_BYTE)
00515            break;
00516         Data++;
00517         Count--;
00518         used++;
00519         }
00520   if (used)
00521      esyslog("ERROR: skipped %d byte to sync on TS packet", used);
00522 
00523   // Convert incoming TS data into multiplexed PES:
00524 
00525   for (int i = 0; i < Count; i += TS_SIZE) {
00526       if (Count - i < TS_SIZE)
00527          break;
00528       if (Data[i] != TS_SYNC_BYTE)
00529          break;
00530       int pid = GetPid(Data + i + 1);
00531       if (Data[i + 3] & 0x10) { // got payload
00532          if      (pid == vPid)              vTS2PES->ts_to_pes(Data + i);
00533          else if (pid == aPid1)             aTS2PES1->ts_to_pes(Data + i);
00534          else if (pid == aPid2 && aTS2PES2) aTS2PES2->ts_to_pes(Data + i);
00535          else if (pid == dPid1 && dTS2PES1) dTS2PES1->ts_to_pes(Data + i);
00536          else if (pid == dPid2 && dTS2PES2) dTS2PES2->ts_to_pes(Data + i);
00537          }
00538       used += TS_SIZE;
00539       if (resultCount > (int)sizeof(resultBuffer) / 2)
00540          break;
00541       }
00542   Count = used;
00543 
00544 /*XXX
00545   // test recording without determining the real frame borders:
00546   *PictureType = I_FRAME;
00547   Result = resultDelivered = resultCount;
00548   return Result ? resultBuffer : NULL;
00549 XXX*/
00550 
00551   // Special VPID case to enable recording radio channels:
00552 
00553   if (vPid == 0 || vPid == 1 || vPid == 0x1FFF) {
00554      // XXX actually '0' should be enough, but '1' must be used with encrypted channels (driver bug?)
00555      // XXX also allowing 0x1FFF to not break Michael Paar's original patch,
00556      // XXX but it would probably be best to only use '0'
00557      *PictureType = I_FRAME;
00558      Result = resultDelivered = resultCount;
00559      return Result ? resultBuffer : NULL;
00560      }
00561 
00562   // Check if we're getting anywhere here:
00563 
00564   if (!synced && skipped >= 0) {
00565      if (skipped > MAXNONUSEFULDATA) {
00566         esyslog("ERROR: no useful data seen within %d byte of video stream", skipped);
00567         skipped = -1;
00568         if (exitOnFailure)
00569            cThread::EmergencyExit(true);
00570         }
00571      else
00572         skipped += Count;
00573      }
00574 
00575   // Check for frame borders:
00576 
00577   *PictureType = NO_PICTURE;
00578 
00579   if (resultCount >= MINVIDEODATA) {
00580      for (int i = 0; i < resultCount; i++) {
00581          if (resultBuffer[i] == 0 && resultBuffer[i + 1] == 0 && resultBuffer[i + 2] == 1) {
00582             switch (resultBuffer[i + 3]) {
00583               case VIDEO_STREAM_S ... VIDEO_STREAM_E:
00584                    {
00585                      uchar pt = NO_PICTURE;
00586                      int l = ScanVideoPacket(resultBuffer, resultCount, i, pt);
00587                      if (l < 0)
00588                         return NULL; // no useful data found, wait for more
00589                      if (pt != NO_PICTURE) {
00590                         if (pt < I_FRAME || B_FRAME < pt)
00591                            esyslog("ERROR: unknown picture type '%d'", pt);
00592                         else if (!synced) {
00593                            if (pt == I_FRAME) {
00594                               resultDelivered = i; // will drop everything before this position
00595                               synced = true;
00596                               }
00597                            else {
00598                               resultDelivered = i + l; // will drop everything before and including this packet
00599                               return NULL;
00600                               }
00601                            }
00602                         }
00603                      if (synced) {
00604                         *PictureType = pt;
00605                         Result = l;
00606                         uchar *p = resultBuffer + resultDelivered;
00607                         resultDelivered += l;
00608                         return p;
00609                         }
00610                      else {
00611                         resultDelivered = i + l; // will drop everything before and including this packet
00612                         return NULL;
00613                         }
00614                    }
00615                    break;
00616               case PRIVATE_STREAM1:
00617               case AUDIO_STREAM_S ... AUDIO_STREAM_E:
00618                    {
00619                      int l = GetPacketLength(resultBuffer, resultCount, i);
00620                      if (l < 0)
00621                         return NULL; // no useful data found, wait for more
00622                      if (synced) {
00623                         Result = l;
00624                         uchar *p = resultBuffer + resultDelivered;
00625                         resultDelivered += l;
00626                         return p;
00627                         }
00628                      else {
00629                         resultDelivered = i + l; // will drop everything before and including this packet
00630                         return NULL;
00631                         }
00632                    }
00633                    break;
00634               }
00635             }
00636          }
00637      }
00638   return NULL; // no useful data found, wait for more
00639 }
00640 

Generated on Wed Feb 5 23:30:11 2003 for VDR by doxygen1.3-rc2