diff options
| author | Klaus Schmidinger <vdr@tvdr.de> | 2012-11-18 12:19:51 +0100 | 
|---|---|---|
| committer | Klaus Schmidinger <vdr@tvdr.de> | 2012-11-18 12:19:51 +0100 | 
| commit | cca2cd35ad7ef20ae7d124e06d05e896c4d8f9b6 (patch) | |
| tree | 21b5f90cfe4143d1fc2188663d671b7f92f05670 | |
| parent | 5b4e1fa793506405d0d8bac47a36640a06340c80 (diff) | |
| download | vdr-cca2cd35ad7ef20ae7d124e06d05e896c4d8f9b6.tar.gz vdr-cca2cd35ad7ef20ae7d124e06d05e896c4d8f9b6.tar.bz2 | |
Improved editing TS recordings
| -rw-r--r-- | HISTORY | 17 | ||||
| -rw-r--r-- | MANUAL | 20 | ||||
| -rw-r--r-- | cutter.c | 650 | ||||
| -rw-r--r-- | menu.c | 4 | ||||
| -rw-r--r-- | po/ar.po | 5 | ||||
| -rw-r--r-- | po/ca_ES.po | 5 | ||||
| -rw-r--r-- | po/cs_CZ.po | 5 | ||||
| -rw-r--r-- | po/da_DK.po | 5 | ||||
| -rw-r--r-- | po/de_DE.po | 5 | ||||
| -rw-r--r-- | po/el_GR.po | 5 | ||||
| -rw-r--r-- | po/es_ES.po | 5 | ||||
| -rw-r--r-- | po/et_EE.po | 5 | ||||
| -rw-r--r-- | po/fi_FI.po | 5 | ||||
| -rw-r--r-- | po/fr_FR.po | 5 | ||||
| -rw-r--r-- | po/hr_HR.po | 5 | ||||
| -rw-r--r-- | po/hu_HU.po | 5 | ||||
| -rw-r--r-- | po/it_IT.po | 5 | ||||
| -rw-r--r-- | po/lt_LT.po | 5 | ||||
| -rw-r--r-- | po/mk_MK.po | 5 | ||||
| -rw-r--r-- | po/nl_NL.po | 5 | ||||
| -rw-r--r-- | po/nn_NO.po | 5 | ||||
| -rw-r--r-- | po/pl_PL.po | 5 | ||||
| -rw-r--r-- | po/pt_PT.po | 5 | ||||
| -rw-r--r-- | po/ro_RO.po | 5 | ||||
| -rw-r--r-- | po/ru_RU.po | 5 | ||||
| -rw-r--r-- | po/sk_SK.po | 5 | ||||
| -rw-r--r-- | po/sl_SI.po | 5 | ||||
| -rw-r--r-- | po/sr_SR.po | 5 | ||||
| -rw-r--r-- | po/sv_SE.po | 5 | ||||
| -rw-r--r-- | po/tr_TR.po | 5 | ||||
| -rw-r--r-- | po/uk_UA.po | 5 | ||||
| -rw-r--r-- | po/zh_CN.po | 5 | ||||
| -rw-r--r-- | recording.c | 50 | ||||
| -rw-r--r-- | recording.h | 15 | ||||
| -rw-r--r-- | remux.c | 108 | ||||
| -rw-r--r-- | remux.h | 66 | 
36 files changed, 846 insertions, 224 deletions
| @@ -7272,7 +7272,7 @@ Video Disk Recorder Revision History    ".keep" to prevent a directory from being deleted when it is empty. Currently the    only file name that is ignored is ".sort". -2012-11-12: Version 1.7.32 +2012-11-18: Version 1.7.32  - Pressing the Play key during normal live viewing mode now opens the Recordings menu    if there is no "last viewed" recording (thanks to Alexander Wenzel). @@ -7315,3 +7315,18 @@ Video Disk Recorder Revision History  - The return type of cMarks::Add() has been changed to void, since due to the sorting    of the list of marks the returned pointer might have pointed to a totally different    mark. Besides, the return value was never actually used. +- Improved editing TS recordings by +  + stripping dangling TS packets from the beginning of a sequence +  + including pending TS packets at the end of a sequence +  + fixing all timestamps and continuity counters +  + generating editing marks for the edited version in such a way that each cutting +    point is marked by an "end" and "begin" mark with the same offset +  + no longer generating an editing mark at the "end" of the edited recording (this +    was actually generated at the beginning of the last GOP, so that a subsequent +    edit would have cut off the last GOP) +  + no longer generating any editing marks if the edited recording results on just +    one single sequence +  + ignoring pairs of editing marks that are placed at exactly the same position of +    a recording when actually cutting the recording +  + not doing anything if the editing marks in place would result in the edited +    version being the same as the original recording @@ -367,13 +367,13 @@ Version 1.6    - 7, 9    Jump back and forward between editing marks. Replay goes into still              mode after jumping to a mark.    - 8       Positions replay at a point 3 seconds before the current or next -            "start" mark and starts replay. +            "begin" mark and starts replay.    - 2       Start the actual cutting process.    Editing marks are represented by black, vertical lines in the progress display. -  A small black triangle at the top of the mark means that this is a "start" +  A small black triangle at the top of the mark means that this is a "begin"    mark, and a triangle at the bottom means that this is an "end" mark. -  The cutting process will save all video data between "start" and "end" marks +  The cutting process will save all video data between "begin" and "end" marks    into a new file (the original recording remains untouched). The new file will    have the same name as the original recording, preceded with a '%' character    (imagine the '%' somehow looking like a pair of scissors ;-). Red bars in the @@ -382,7 +382,7 @@ Version 1.6    The video sequences to be saved by the cutting process are determined by an    "even/odd" algorithm. This means that every odd numbered editing mark (i.e. -  1, 3, 5,...) represents a "start" mark, while every even numbered mark (2, 4, +  1, 3, 5,...) represents a "begin" mark, while every even numbered mark (2, 4,    6,...) is an "end" mark. Inserting or toggling a mark on or off automatically    adjusts the sequence to the right side of that mark. @@ -395,11 +395,13 @@ Version 1.6    version of the recording you can use the '8' key to jump to a point just    before the next cut and have a look at the resulting sequence. -  Currently editing marks can only be set at I-frames, which typically is -  every 12th frame. So editing can be done with a resolution of roughly half -  a second. A "start" mark marks the first frame of a resulting video -  sequence, and an "end" mark marks the last frame of that sequence. - +  Currently editing marks can only be set at I-frames, which typically appear +  every half of a second to a second.  A "begin" mark marks the first frame of +  a resulting video sequence, and an "end" mark marks the last frame of that +  sequence.  Note that the actual frame indicated by the an "end" mark will +  not be included in the edited version of the recording. That's because every +  recording (and every sequence of an edited recording) begins with an I-frame +  and ends right before the next I-frame.    An edited recording (indicated by the '%' character) will never be deleted    automatically in case the disk runs full (no matter what "lifetime" it has). @@ -4,7 +4,7 @@   * See the main source file 'vdr.c' for copyright information and   * how to reach the author.   * - * $Id: cutter.c 2.15 2012/10/04 12:19:37 kls Exp $ + * $Id: cutter.c 2.16 2012/11/18 12:09:00 kls Exp $   */  #include "cutter.h" @@ -13,17 +13,261 @@  #include "remux.h"  #include "videodir.h" +// --- cPacketBuffer --------------------------------------------------------- + +class cPacketBuffer { +private: +  uchar *data; +  int size; +  int length; +public: +  cPacketBuffer(void); +  ~cPacketBuffer(); +  void Append(uchar *Data, int Length); +       ///< Appends Length bytes of Data to this packet buffer. +  void Flush(uchar *Data, int &Length, int MaxLength); +       ///< Flushes the content of this packet buffer into the given Data, starting +       ///< at position Length, and clears the buffer afterwards. Length will be +       ///< incremented accordingly. If Length plus the total length of the stored +       ///< packets would exceed MaxLength, nothing is copied. +  }; + +cPacketBuffer::cPacketBuffer(void) +{ +  data = NULL; +  size = length = 0; +} + +cPacketBuffer::~cPacketBuffer() +{ +  free(data); +} + +void cPacketBuffer::Append(uchar *Data, int Length) +{ +  if (length + Length >= size) { +     int NewSize = (length + Length) * 3 / 2; +     if (uchar *p = (uchar *)realloc(data, NewSize)) { +        data = p; +        size = NewSize; +        } +     else +        return; // out of memory +     } +  memcpy(data + length, Data, Length); +  length += Length; +} + +void cPacketBuffer::Flush(uchar *Data, int &Length, int MaxLength) +{ +  if (Data && length > 0 && Length + length <= MaxLength) { +     memcpy(Data + Length, data, length); +     Length += length; +     } +  length = 0; +} + +// --- cPacketStorage -------------------------------------------------------- + +class cPacketStorage { +private: +  cPacketBuffer *buffers[MAXPID]; +public: +  cPacketStorage(void); +  ~cPacketStorage(); +  void Append(int Pid, uchar *Data, int Length); +  void Flush(int Pid, uchar *Data, int &Length, int MaxLength); +  }; + +cPacketStorage::cPacketStorage(void) +{ +  for (int i = 0; i < MAXPID; i++) +      buffers[i] = NULL; +} + +cPacketStorage::~cPacketStorage() +{ +  for (int i = 0; i < MAXPID; i++) +      delete buffers[i]; +} + +void cPacketStorage::Append(int Pid, uchar *Data, int Length) +{ +  if (!buffers[Pid]) +     buffers[Pid] = new cPacketBuffer; +  buffers[Pid]->Append(Data, Length); +} + +void cPacketStorage::Flush(int Pid, uchar *Data, int &Length, int MaxLength) +{ +  if (buffers[Pid]) +     buffers[Pid]->Flush(Data, Length, MaxLength); +} + +// --- cDanglingPacketStripper ----------------------------------------------- + +class cDanglingPacketStripper { +private: +  bool processed[MAXPID]; +  cPatPmtParser patPmtParser; +public: +  cDanglingPacketStripper(void); +  bool Process(uchar *Data, int Length, int64_t FirstPts); +       ///< Scans the frame given in Data and hides the payloads of any TS packets +       ///< that either didn't start within this frame, or have a PTS that is +       ///< before FirstPts. The TS packets in question are not physically removed +       ///< from Data in order to keep any frame counts and PCR timestamps intact. +       ///< Returns true if any dangling packets have been found. +  }; + +cDanglingPacketStripper::cDanglingPacketStripper(void) +{ +  memset(processed, 0x00, sizeof(processed)); +} + +bool cDanglingPacketStripper::Process(uchar *Data, int Length, int64_t FirstPts) +{ +  bool Found = false; +  while (Length >= TS_SIZE && *Data == TS_SYNC_BYTE) { +        int Pid = TsPid(Data); +        if (Pid == PATPID) +           patPmtParser.ParsePat(Data, TS_SIZE); +        else if (Pid == patPmtParser.PmtPid()) +           patPmtParser.ParsePmt(Data, TS_SIZE); +        else { +           int64_t Pts = TsGetPts(Data, TS_SIZE); +           if (Pts >= 0) +              processed[Pid] = PtsDiff(FirstPts, Pts) >= 0; // Pts is at or after FirstPts +           if (!processed[Pid]) { +              TsHidePayload(Data); +              Found = true; +              } +           } +        Length -= TS_SIZE; +        Data += TS_SIZE; +        } +  return Found; +} + +// --- cPtsFixer ------------------------------------------------------------- + +class cPtsFixer { +private: +  int delta; // time between two frames +  int64_t last; // the last (i.e. highest) video PTS value seen +  int64_t offset; // offset to add to PTS values +  bool fixCounters; // controls fixing the TS continuity counters (only from the second CutIn up) +  uchar counter[MAXPID]; // the TS continuity counter for each PID +  cPatPmtParser patPmtParser; +public: +  cPtsFixer(void); +  void Setup(double FramesPerSecond); +  void Fix(uchar *Data, int Length, bool CutIn); +  }; + +cPtsFixer::cPtsFixer(void) +{ +  delta = 0; +  last = -1; +  offset = -1; +  fixCounters = false; +  memset(counter, 0x00, sizeof(counter)); +} + +void cPtsFixer::Setup(double FramesPerSecond) +{ +  delta = int(round(PTSTICKS / FramesPerSecond)); +} + +void cPtsFixer::Fix(uchar *Data, int Length, bool CutIn) +{ +  if (!patPmtParser.Vpid()) { +     if (!patPmtParser.ParsePatPmt(Data, Length)) +        return; +     } +  // Determine the PTS offset at the beginning of each sequence (except the first one): +  if (CutIn && last >= 0) { +     int64_t Pts = TsGetPts(Data, Length); +     if (Pts >= 0) { +        // offset is calculated so that Pts + offset results in last + delta: +        offset = Pts - PtsAdd(last, delta); +        if (offset <= 0) +           offset = -offset; +        else +           offset = MAX33BIT + 1 - offset; +        } +     fixCounters = true; +     } +  // Keep track of the highest video PTS: +  uchar *p = Data; +  int len = Length; +  while (len >= TS_SIZE && *p == TS_SYNC_BYTE) { +        int Pid = TsPid(p); +        if (Pid == patPmtParser.Vpid()) { +           int64_t Pts = PtsAdd(TsGetPts(p, TS_SIZE), offset); // offset is taken into account here, to make last have the "new" value already! +           if (Pts >= 0 && (last < 0 || PtsDiff(last, Pts) > 0)) +              last = Pts; +           } +        // Adjust the TS continuity counter: +        if (fixCounters) { +           counter[Pid] = (counter[Pid] + 1) & TS_CONT_CNT_MASK; +           TsSetContinuityCounter(p, counter[Pid]); +           } +        else +           counter[Pid] = TsGetContinuityCounter(p); // collect initial counters +        p += TS_SIZE; +        len -= TS_SIZE; +        } +  // Apply the PTS offset: +  if (offset > 0) { +     uchar *p = Data; +     int len = Length; +     while (len >= TS_SIZE && *p == TS_SYNC_BYTE) { +           // Adjust the various timestamps: +           int64_t Pts = TsGetPts(p, TS_SIZE); +           if (Pts >= 0) +              TsSetPts(p, TS_SIZE, PtsAdd(Pts, offset)); +           int64_t Dts = TsGetDts(p, TS_SIZE); +           if (Dts >= 0) +              TsSetDts(p, TS_SIZE, PtsAdd(Dts, offset)); +           int64_t Pcr = TsGetPcr(p); +           if (Pcr >= 0) { +              int64_t NewPcr = Pcr + offset * PCRFACTOR; +              if (NewPcr >= MAX27MHZ) +                 NewPcr -= MAX27MHZ + 1; +              TsSetPcr(p, NewPcr); +              } +           p += TS_SIZE; +           len -= TS_SIZE; +           } +     } +} +  // --- cCuttingThread --------------------------------------------------------  class cCuttingThread : public cThread {  private:    const char *error;    bool isPesRecording; +  double framesPerSecond;    cUnbufferedFile *fromFile, *toFile;    cFileName *fromFileName, *toFileName;    cIndexFile *fromIndex, *toIndex;    cMarks fromMarks, toMarks; +  int numSequences;    off_t maxVideoFileSize; +  off_t fileSize; +  cPtsFixer ptsFixer; +  bool suspensionLogged; +  bool Throttled(void); +  bool SwitchFile(bool Force = false); +  bool LoadFrame(int Index, uchar *Buffer, bool &Independent, int &Length); +  bool FramesAreEqual(int Index1, int Index2); +  void GetPendingPackets(uchar *Buffer, int &Length, int Index, int64_t LastPts); +       // Gather all non-video TS packets from Index upward that either belong to +       // payloads that started before Index, or have a PTS that is before LastPts, +       // and add them to the end of the given Data. +  bool ProcessSequence(int LastEndIndex, int BeginIndex, int EndIndex, int NextBeginIndex);  protected:    virtual void Action(void);  public: @@ -41,16 +285,25 @@ cCuttingThread::cCuttingThread(const char *FromFileName, const char *ToFileName)    fromIndex = toIndex = NULL;    cRecording Recording(FromFileName);    isPesRecording = Recording.IsPesRecording(); -  if (fromMarks.Load(FromFileName, Recording.FramesPerSecond(), isPesRecording) && fromMarks.Count()) { -     fromFileName = new cFileName(FromFileName, false, true, isPesRecording); -     toFileName = new cFileName(ToFileName, true, true, isPesRecording); -     fromIndex = new cIndexFile(FromFileName, false, isPesRecording); -     toIndex = new cIndexFile(ToFileName, true, isPesRecording); -     toMarks.Load(ToFileName, Recording.FramesPerSecond(), isPesRecording); // doesn't actually load marks, just sets the file name -     maxVideoFileSize = MEGABYTE(Setup.MaxVideoFileSize); -     if (isPesRecording && maxVideoFileSize > MEGABYTE(MAXVIDEOFILESIZEPES)) -        maxVideoFileSize = MEGABYTE(MAXVIDEOFILESIZEPES); -     Start(); +  framesPerSecond = Recording.FramesPerSecond(); +  suspensionLogged = false; +  fileSize = 0; +  ptsFixer.Setup(framesPerSecond); +  if (fromMarks.Load(FromFileName, framesPerSecond, isPesRecording) && fromMarks.Count()) { +     numSequences = fromMarks.GetNumSequences(); +     if (numSequences > 0) { +        fromFileName = new cFileName(FromFileName, false, true, isPesRecording); +        toFileName = new cFileName(ToFileName, true, true, isPesRecording); +        fromIndex = new cIndexFile(FromFileName, false, isPesRecording); +        toIndex = new cIndexFile(ToFileName, true, isPesRecording); +        toMarks.Load(ToFileName, framesPerSecond, isPesRecording); // doesn't actually load marks, just sets the file name +        maxVideoFileSize = MEGABYTE(Setup.MaxVideoFileSize); +        if (isPesRecording && maxVideoFileSize > MEGABYTE(MAXVIDEOFILESIZEPES)) +           maxVideoFileSize = MEGABYTE(MAXVIDEOFILESIZEPES); +        Start(); +        } +     else +        esyslog("no editing sequences found for %s", FromFileName);       }    else       esyslog("no editing marks found for %s", FromFileName); @@ -65,169 +318,236 @@ cCuttingThread::~cCuttingThread()    delete toIndex;  } +bool cCuttingThread::Throttled(void) +{ +  if (cIoThrottle::Engaged()) { +     if (!suspensionLogged) { +        dsyslog("suspending cutter thread"); +        suspensionLogged = true; +        } +     return true; +     } +  else if (suspensionLogged) { +     dsyslog("resuming cutter thread"); +     suspensionLogged = false; +     } +  return false; +} + +bool cCuttingThread::LoadFrame(int Index, uchar *Buffer, bool &Independent, int &Length) +{ +  uint16_t FileNumber; +  off_t FileOffset; +  if (fromIndex->Get(Index, &FileNumber, &FileOffset, &Independent, &Length)) { +     fromFile = fromFileName->SetOffset(FileNumber, FileOffset); +     if (fromFile) { +        fromFile->SetReadAhead(MEGABYTE(20)); +        int len = ReadFrame(fromFile, Buffer,  Length, MAXFRAMESIZE); +        if (len < 0) +           error = "ReadFrame"; +        else if (len != Length) +           Length = len; +        return error == NULL; +        } +     else +        error = "fromFile"; +     } +  return false; +} + +bool cCuttingThread::SwitchFile(bool Force) +{ +  if (fileSize > maxVideoFileSize || Force) { +     toFile = toFileName->NextFile(); +     if (!toFile) { +        error = "toFile"; +        return false; +        } +     fileSize = 0; +     } +  return true; +} + +bool cCuttingThread::FramesAreEqual(int Index1, int Index2) +{ +  bool Independent; +  uchar Buffer1[MAXFRAMESIZE]; +  uchar Buffer2[MAXFRAMESIZE]; +  int Length1; +  int Length2; +  if (LoadFrame(Index1, Buffer1, Independent, Length1) && LoadFrame(Index2, Buffer2, Independent, Length2)) { +     if (Length1 == Length2) { +        int Diffs = 0; +        for (int i = 0; i < Length1; i++) { +            if (Buffer1[i] != Buffer2[i]) { +               if (Diffs++ > 10) // the continuity counters of the PAT/PMT packets may differ +                  return false; +               } +            } +        return true; +        } +     } +  return false; +} + +void cCuttingThread::GetPendingPackets(uchar *Data, int &Length, int Index, int64_t LastPts) +{ +  bool Processed[MAXPID] = { false }; +  int NumIndependentFrames = 0; +  cPatPmtParser PatPmtParser; +  cPacketStorage PacketStorage; +  for (; NumIndependentFrames < 2; Index++) { +      uchar Buffer[MAXFRAMESIZE]; +      bool Independent; +      int len; +      if (LoadFrame(Index, Buffer, Independent, len)) { +         if (Independent) +            NumIndependentFrames++; +         uchar *p = Buffer; +         while (len >= TS_SIZE && *p == TS_SYNC_BYTE) { +               int Pid = TsPid(p); +               if (Pid == PATPID) +                  PatPmtParser.ParsePat(p, TS_SIZE); +               else if (Pid == PatPmtParser.PmtPid()) +                  PatPmtParser.ParsePmt(p, TS_SIZE); +               else if (!Processed[Pid]) { +                  int64_t Pts = TsGetPts(p, TS_SIZE); +                  if (Pts >= 0) { +                     int64_t d = PtsDiff(LastPts, Pts); +                     if (d <= 0) // Pts is before or at LastPts +                        PacketStorage.Flush(Pid, Data, Length, MAXFRAMESIZE); +                     if (d >= 0) { // Pts is at or after LastPts +                        NumIndependentFrames = 0; // we search until we find two consecutive I-frames without any more pending packets +                        Processed[Pid] = true; +                        } +                     } +                  if (!Processed[Pid]) +                     PacketStorage.Append(Pid, p, TS_SIZE); +                  } +               len -= TS_SIZE; +               p += TS_SIZE; +               } +         } +      else +         break; +      } +} + +bool cCuttingThread::ProcessSequence(int LastEndIndex, int BeginIndex, int EndIndex, int NextBeginIndex) +{ +  // Check for seamless connections: +  bool SeamlessBegin = LastEndIndex >= 0 && FramesAreEqual(LastEndIndex, BeginIndex); +  bool SeamlessEnd = NextBeginIndex >= 0 && FramesAreEqual(EndIndex, NextBeginIndex); +  // Process all frames from BeginIndex (included) to EndIndex (excluded): +  cDanglingPacketStripper DanglingPacketStripper; +  int NumIndependentFrames = 0; +  int64_t FirstPts = -1; +  int64_t LastPts = -1; +  for (int Index = BeginIndex; Running() && Index < EndIndex; Index++) { +      uchar Buffer[MAXFRAMESIZE]; +      bool Independent; +      int Length; +      if (LoadFrame(Index, Buffer, Independent, Length)) { +         if (!isPesRecording) { +            int64_t Pts = TsGetPts(Buffer, Length); +            if (FirstPts < 0) +               FirstPts = Pts; // the PTS of the first frame in the sequence +            else if (LastPts < 0 || PtsDiff(LastPts, Pts) > 0) +               LastPts = Pts; // the PTS of the frame that is displayed as the very last one of the sequence +            } +         // Fixup data at the beginning of the sequence: +         if (!SeamlessBegin) { +            if (isPesRecording) { +               if (Index == BeginIndex) +                  cRemux::SetBrokenLink(Buffer, Length); +               } +            else if (NumIndependentFrames < 2) { +               if (DanglingPacketStripper.Process(Buffer, Length, FirstPts)) +                  NumIndependentFrames = 0; // we search until we find two consecutive I-frames without any more dangling packets +               } +            } +         // Fixup data at the end of the sequence: +         if (!SeamlessEnd) { +            if (Index == EndIndex - 1) { +               if (!isPesRecording) +                  GetPendingPackets(Buffer, Length, EndIndex, LastPts + int(round(PTSTICKS / framesPerSecond))); // adding one frame length to fully cover the very last frame +               } +            } +         // Fixup timestamps and continuity counters: +         if (!isPesRecording) { +            if (numSequences > 1) +              ptsFixer.Fix(Buffer, Length, !SeamlessBegin && Index == BeginIndex); +            } +         // Every file shall start with an independent frame: +         if (Independent) { +            NumIndependentFrames++; +            if (!SwitchFile()) +               return false; +            } +         // Write index: +         if (!toIndex->Write(Independent, toFileName->Number(), fileSize)) { +            error = "toIndex"; +            return false; +            } +         // Write data: +         if (toFile->Write(Buffer, Length) < 0) { +            error = "safe_write"; +            return false; +            } +         fileSize += Length; +         // Generate marks at the editing points in the edited recording: +         if (numSequences > 0 && Index == BeginIndex) { +            if (toMarks.Count() > 0) +               toMarks.Add(toIndex->Last()); +            toMarks.Add(toIndex->Last()); +            toMarks.Save(); +            } +         } +      else +         return false; +      } +  return true; +} +  void cCuttingThread::Action(void)  { -  cMark *Mark = fromMarks.First(); -  if (Mark) { +  if (cMark *BeginMark = fromMarks.GetNextBegin()) {       fromFile = fromFileName->Open();       toFile = toFileName->Open();       if (!fromFile || !toFile)          return; -     fromFile->SetReadAhead(MEGABYTE(20)); -     int Index = Mark->Position(); -     Mark = fromMarks.Next(Mark); -     off_t FileSize = 0; -     int CurrentFileNumber = 0; -     int LastIFrame = 0; -     toMarks.Add(0); -     toMarks.Save(); -     uchar buffer[MAXFRAMESIZE], buffer2[MAXFRAMESIZE]; -     int Length2; -     bool CheckForSeamlessStream = false; -     bool LastMark = false; -     bool cutIn = true; -     bool suspensionLogged = false; -     while (Running()) { -           uint16_t FileNumber; -           off_t FileOffset; -           int Length; -           bool Independent; - +     int LastEndIndex = -1; +     while (BeginMark && Running()) {             // Suspend cutting if we have severe throughput problems: - -           if (cIoThrottle::Engaged()) { -              if (!suspensionLogged) { -                 dsyslog("suspending cutter thread"); -                 suspensionLogged = true; -                 } +           if (Throttled()) {                cCondWait::SleepMs(100);                continue;                } -           else if (suspensionLogged) { -              dsyslog("resuming cutter thread"); -              suspensionLogged = false; -              } -             // Make sure there is enough disk space: -             AssertFreeDiskSpace(-1); - -           // Read one frame: - -           if (fromIndex->Get(Index++, &FileNumber, &FileOffset, &Independent, &Length)) { -              if (FileNumber != CurrentFileNumber) { -                 fromFile = fromFileName->SetOffset(FileNumber, FileOffset); -                 if (fromFile) -                    fromFile->SetReadAhead(MEGABYTE(20)); -                 CurrentFileNumber = FileNumber; -                 } -              if (fromFile) { -                 int len = ReadFrame(fromFile, buffer,  Length, sizeof(buffer)); -                 if (len < 0) { -                    error = "ReadFrame"; -                    break; -                    } -                 if (len != Length) { -                    CurrentFileNumber = 0; // this re-syncs in case the frame was larger than the buffer -                    Length = len; -                    } -                 } -              else { -                 error = "fromFile"; -                 break; -                 } +           // Determine the actual begin and end marks, skipping any marks at the same position: +           cMark *EndMark = fromMarks.GetNextEnd(BeginMark); +           // Process the current sequence: +           int EndIndex = EndMark ? EndMark->Position() : fromIndex->Last() + 1; +           int NextBeginIndex = -1; +           if (EndMark) { +              if (cMark *NextBeginMark = fromMarks.GetNextBegin(EndMark)) +                 NextBeginIndex = NextBeginMark->Position();                } -           else { -              // Error, unless we're past the last cut-in and there's no cut-out -              if (Mark || LastMark) -                 error = "index"; +           if (!ProcessSequence(LastEndIndex, BeginMark->Position(), EndIndex, NextBeginIndex))                break; -              } - -           // Write one frame: - -           if (Independent) { // every file shall start with an independent frame -              if (LastMark) // edited version shall end before next I-frame -                 break; -              if (FileSize > maxVideoFileSize) { -                 toFile = toFileName->NextFile(); -                 if (!toFile) { -                    error = "toFile 1"; +           if (!EndMark) +              break; // reached EOF +           LastEndIndex = EndIndex; +           // Switch to the next sequence: +           BeginMark = fromMarks.GetNextBegin(EndMark); +           if (BeginMark) { +              // Split edited files: +              if (Setup.SplitEditedFiles) { +                 if (!SwitchFile(true))                      break; -                    } -                 FileSize = 0; -                 } -              LastIFrame = 0; -              // Compare the current frame with the previously stored one, to see if this is a seamlessly merged recording of the same stream: -              if (CheckForSeamlessStream) { -                 if (Length == Length2) { -                    int diffs = 0; -                    for (int i = 0; i < Length; i++) { -                        if (buffer[i] != buffer2[i]) { -                           if (diffs++ > 10) -                              break; -                           } -                        } -                    if (diffs < 10) // the continuity counters of the PAT/PMT packets may differ -                       cutIn = false; // it's apparently a seamless stream, so no need for "broken" handling -                    } -                 CheckForSeamlessStream = false; -                 } -              if (cutIn) { -                 if (isPesRecording) -                    cRemux::SetBrokenLink(buffer, Length); -                 else -                    TsSetTeiOnBrokenPackets(buffer, Length); -                 cutIn = false;                   }                } -           if (toFile->Write(buffer, Length) < 0) { -              error = "safe_write"; -              break; -              } -           if (!toIndex->Write(Independent, toFileName->Number(), FileSize)) { -              error = "toIndex"; -              break; -              } -           FileSize += Length; -           if (!LastIFrame) -              LastIFrame = toIndex->Last(); - -           // Check editing marks: - -           if (Mark && Index >= Mark->Position()) { -              Mark = fromMarks.Next(Mark); -              toMarks.Add(LastIFrame); -              if (Mark) -                 toMarks.Add(toIndex->Last() + 1); -              toMarks.Save(); -              if (Mark) { -                 // Read the next frame, for later comparison with the first frame at this mark: -                 if (fromIndex->Get(Index, &FileNumber, &FileOffset, &Independent, &Length2)) { -                    if (FileNumber != CurrentFileNumber) -                       fromFile = fromFileName->SetOffset(FileNumber, FileOffset); -                    if (fromFile) { -                       int len = ReadFrame(fromFile, buffer2, Length2, sizeof(buffer2)); -                       if (len >= 0 && len == Length2) -                          CheckForSeamlessStream = true; -                       } -                    } -                 Index = Mark->Position(); -                 Mark = fromMarks.Next(Mark); -                 CurrentFileNumber = 0; // triggers SetOffset before reading next frame -                 cutIn = true; -                 if (Setup.SplitEditedFiles) { -                    toFile = toFileName->NextFile(); -                    if (!toFile) { -                       error = "toFile 2"; -                       break; -                       } -                    FileSize = 0; -                    } -                 } -              else -                 LastMark = true; -              }             }       Recordings.TouchUpdate();       } @@ -255,7 +575,7 @@ bool cCutter::Start(const char *FileName)       cMarks FromMarks;       FromMarks.Load(FileName, Recording.FramesPerSecond(), Recording.IsPesRecording()); -     if (cMark *First = FromMarks.First()) +     if (cMark *First = FromMarks.GetNextBegin())          Recording.SetStartTime(Recording.Start() + (int(First->Position() / Recording.FramesPerSecond() + 30) / 60) * 60);       const char *evn = Recording.PrefixFileName('%'); @@ -343,13 +663,17 @@ bool CutRecording(const char *FileName)       if (Recording.Name()) {          cMarks Marks;          if (Marks.Load(FileName, Recording.FramesPerSecond(), Recording.IsPesRecording()) && Marks.Count()) { -           if (cCutter::Start(FileName)) { -              while (cCutter::Active()) -                    cCondWait::SleepMs(CUTTINGCHECKINTERVAL); -              return true; +           if (Marks.GetNumSequences()) { +              if (cCutter::Start(FileName)) { +                 while (cCutter::Active()) +                       cCondWait::SleepMs(CUTTINGCHECKINTERVAL); +                 return true; +                 } +              else +                 fprintf(stderr, "can't start editing process\n");                }             else -              fprintf(stderr, "can't start editing process\n"); +              fprintf(stderr, "'%s' has no editing sequences\n", FileName);             }          else             fprintf(stderr, "'%s' has no editing marks\n", FileName); @@ -4,7 +4,7 @@   * See the main source file 'vdr.c' for copyright information and   * how to reach the author.   * - * $Id: menu.c 2.62 2012/10/03 10:14:53 kls Exp $ + * $Id: menu.c 2.63 2012/11/13 11:23:25 kls Exp $   */  #include "menu.h" @@ -4796,6 +4796,8 @@ void cReplayControl::EditCut(void)       if (!cCutter::Active()) {          if (!marks.Count())             Skins.Message(mtError, tr("No editing marks defined!")); +        else if (!marks.GetNumSequences()) +           Skins.Message(mtError, tr("No editing sequences defined!"));          else if (!cCutter::Start(fileName))             Skins.Message(mtError, tr("Can't start editing process!"));          else @@ -7,7 +7,7 @@ msgid ""  msgstr ""  "Project-Id-Version: VDR 1.7.0\n"  "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\n"  "PO-Revision-Date: 2008-10-16 11:16-0400\n"  "Last-Translator: Osama Alrawab <alrawab@hotmail.com>\n"  "Language-Team: Arabic <ar@li.org>\n" @@ -1241,6 +1241,9 @@ msgstr "اقفز الى  "  msgid "No editing marks defined!"  msgstr "لاتوجد علامات تعديل معرفة" +msgid "No editing sequences defined!" +msgstr "" +  msgid "Can't start editing process!"  msgstr "لا يمكن البدء فى عملية التعديل" diff --git a/po/ca_ES.po b/po/ca_ES.po index c67265b1..f08cd57b 100644 --- a/po/ca_ES.po +++ b/po/ca_ES.po @@ -10,7 +10,7 @@ msgid ""  msgstr ""  "Project-Id-Version: VDR 1.6.0\n"  "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\n"  "PO-Revision-Date: 2008-03-02 19:02+0100\n"  "Last-Translator: Luca Olivetti <luca@ventoso.org>\n"  "Language-Team: Catalan <vdr@linuxtv.org>\n" @@ -1216,6 +1216,9 @@ msgstr "Salta a:"  msgid "No editing marks defined!"  msgstr "No hi ha marques d'edici definides" +msgid "No editing sequences defined!" +msgstr "" +  msgid "Can't start editing process!"  msgstr "No puc iniciar el procs d'edici!" diff --git a/po/cs_CZ.po b/po/cs_CZ.po index 55053f95..1a621026 100644 --- a/po/cs_CZ.po +++ b/po/cs_CZ.po @@ -9,7 +9,7 @@ msgid ""  msgstr ""  "Project-Id-Version: VDR 1.7.14\n"  "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\n"  "PO-Revision-Date: 2010-05-06 11:00+0200\n"  "Last-Translator: Radek Šťastný <dedkus@gmail.com>\n"  "Language-Team: Czech <vdr@linuxtv.org>\n" @@ -1215,6 +1215,9 @@ msgstr "Skok: "  msgid "No editing marks defined!"  msgstr "Nejsou definovány editační značky!" +msgid "No editing sequences defined!" +msgstr "" +  msgid "Can't start editing process!"  msgstr "Nelze začít editační proces!" diff --git a/po/da_DK.po b/po/da_DK.po index fac58003..597539ff 100644 --- a/po/da_DK.po +++ b/po/da_DK.po @@ -7,7 +7,7 @@ msgid ""  msgstr ""  "Project-Id-Version: VDR 1.6.0\n"  "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\n"  "PO-Revision-Date: 2007-08-12 14:17+0200\n"  "Last-Translator: Mogens Elneff <mogens@elneff.dk>\n"  "Language-Team: Danish <vdr@linuxtv.org>\n" @@ -1213,6 +1213,9 @@ msgstr "Hop: "  msgid "No editing marks defined!"  msgstr "Der er ikke sat nogen redigeringsmrker!" +msgid "No editing sequences defined!" +msgstr "" +  msgid "Can't start editing process!"  msgstr "Kan ikke starte redigeringsprocessen!" diff --git a/po/de_DE.po b/po/de_DE.po index a321b501..c958758c 100644 --- a/po/de_DE.po +++ b/po/de_DE.po @@ -7,7 +7,7 @@ msgid ""  msgstr ""  "Project-Id-Version: VDR 1.6.0\n"  "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\n"  "PO-Revision-Date: 2010-01-16 16:46+0100\n"  "Last-Translator: Klaus Schmidinger <kls@tvdr.de>\n"  "Language-Team: German <vdr@linuxtv.org>\n" @@ -1213,6 +1213,9 @@ msgstr "Springen: "  msgid "No editing marks defined!"  msgstr "Keine Schnittmarken gesetzt!" +msgid "No editing sequences defined!" +msgstr "Keine Schnittsequenzen definiert!" +  msgid "Can't start editing process!"  msgstr "Schnitt kann nicht gestartet werden!" diff --git a/po/el_GR.po b/po/el_GR.po index 8936408c..1347812d 100644 --- a/po/el_GR.po +++ b/po/el_GR.po @@ -7,7 +7,7 @@ msgid ""  msgstr ""  "Project-Id-Version: VDR 1.6.0\n"  "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\n"  "PO-Revision-Date: 2007-08-12 14:17+0200\n"  "Last-Translator: Dimitrios Dimitrakos <mail@dimitrios.de>\n"  "Language-Team: Greek <vdr@linuxtv.org>\n" @@ -1213,6 +1213,9 @@ msgstr ": "  msgid "No editing marks defined!"  msgstr "    " +msgid "No editing sequences defined!" +msgstr "" +  msgid "Can't start editing process!"  msgstr "   !" diff --git a/po/es_ES.po b/po/es_ES.po index bc80a117..8e76063d 100644 --- a/po/es_ES.po +++ b/po/es_ES.po @@ -8,7 +8,7 @@ msgid ""  msgstr ""  "Project-Id-Version: VDR 1.6.0\n"  "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\n"  "PO-Revision-Date: 2008-03-02 19:02+0100\n"  "Last-Translator: Luca Olivetti <luca@ventoso.org>\n"  "Language-Team: Spanish <vdr@linuxtv.org>\n" @@ -1214,6 +1214,9 @@ msgstr "Saltar: "  msgid "No editing marks defined!"  msgstr "No se definieron marcas de edicin!" +msgid "No editing sequences defined!" +msgstr "" +  msgid "Can't start editing process!"  msgstr "No se puede iniciar el proceso de edicin!" diff --git a/po/et_EE.po b/po/et_EE.po index 0dea9992..12c1f259 100644 --- a/po/et_EE.po +++ b/po/et_EE.po @@ -7,7 +7,7 @@ msgid ""  msgstr ""  "Project-Id-Version: VDR 1.6.0\n"  "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\n"  "PO-Revision-Date: 2007-08-12 14:17+0200\n"  "Last-Translator: Arthur Konovalov <artlov@gmail.com>\n"  "Language-Team: Estonian <vdr@linuxtv.org>\n" @@ -1213,6 +1213,9 @@ msgstr "Hüpe: "  msgid "No editing marks defined!"  msgstr "Redigeerimise markerid puuduvad!" +msgid "No editing sequences defined!" +msgstr "" +  msgid "Can't start editing process!"  msgstr "Redigeerimise start nurjus!" diff --git a/po/fi_FI.po b/po/fi_FI.po index daad22ae..901328ae 100644 --- a/po/fi_FI.po +++ b/po/fi_FI.po @@ -10,7 +10,7 @@ msgid ""  msgstr ""  "Project-Id-Version: VDR 1.6.0\n"  "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-13 13:15+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\n"  "PO-Revision-Date: 2007-08-15 15:52+0200\n"  "Last-Translator: Rolf Ahrenberg <rahrenbe@cc.hut.fi>\n"  "Language-Team: Finnish <vdr@linuxtv.org>\n" @@ -1216,6 +1216,9 @@ msgstr "Siirry: "  msgid "No editing marks defined!"  msgstr "Muokkausmerkinnät puuttuvat!" +msgid "No editing sequences defined!" +msgstr "" +  msgid "Can't start editing process!"  msgstr "Muokkauksen aloitus epäonnistui!" diff --git a/po/fr_FR.po b/po/fr_FR.po index 3ca3c06f..a0995aeb 100644 --- a/po/fr_FR.po +++ b/po/fr_FR.po @@ -13,7 +13,7 @@ msgid ""  msgstr ""  "Project-Id-Version: VDR 1.6.0\n"  "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\n"  "PO-Revision-Date: 2008-02-27 18:14+0100\n"  "Last-Translator: Jean-Claude Repetto <jc@repetto.org>\n"  "Language-Team: French <vdr@linuxtv.org>\n" @@ -1219,6 +1219,9 @@ msgstr "Accs direct : "  msgid "No editing marks defined!"  msgstr "Pas de marques d'dition dfinies !" +msgid "No editing sequences defined!" +msgstr "" +  msgid "Can't start editing process!"  msgstr "Impossible de commencer le montage !" diff --git a/po/hr_HR.po b/po/hr_HR.po index dcb1a217..44cfd09f 100644 --- a/po/hr_HR.po +++ b/po/hr_HR.po @@ -9,7 +9,7 @@ msgid ""  msgstr ""  "Project-Id-Version: VDR 1.6.0\n"  "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\n"  "PO-Revision-Date: 2008-03-17 19:00+0100\n"  "Last-Translator: Adrian Caval <anrxc@sysphere.org>\n"  "Language-Team: Croatian <vdr@linuxtv.org>\n" @@ -1215,6 +1215,9 @@ msgstr "Skoi: "  msgid "No editing marks defined!"  msgstr "Nijedna toka rezanja nije odreena!" +msgid "No editing sequences defined!" +msgstr "" +  msgid "Can't start editing process!"  msgstr "Ne mogu zapoeti ureivanje!" diff --git a/po/hu_HU.po b/po/hu_HU.po index c391b67e..30ab8ab6 100644 --- a/po/hu_HU.po +++ b/po/hu_HU.po @@ -10,7 +10,7 @@ msgid ""  msgstr ""  "Project-Id-Version: VDR 1.6.0\n"  "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\n"  "PO-Revision-Date: 2012-01-02 11:54+0200\n"  "Last-Translator: Istvn Fley <ifuley@tigercomp.ro>\n"  "Language-Team: Hungarian <vdr@linuxtv.org>\n" @@ -1217,6 +1217,9 @@ msgstr "Ugrs ide: "  msgid "No editing marks defined!"  msgstr "Nincs vgpont kijellve" +msgid "No editing sequences defined!" +msgstr "" +  msgid "Can't start editing process!"  msgstr "A vgs nem indthat!" diff --git a/po/it_IT.po b/po/it_IT.po index b63d93d8..1258f23f 100644 --- a/po/it_IT.po +++ b/po/it_IT.po @@ -11,7 +11,7 @@ msgid ""  msgstr ""  "Project-Id-Version: VDR 1.6.0\n"  "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\n"  "PO-Revision-Date: 2012-06-06 22:50+0100\n"  "Last-Translator: Diego Pierotto <vdr-italian@tiscali.it>\n"  "Language-Team: Italian <vdr@linuxtv.org>\n" @@ -1220,6 +1220,9 @@ msgstr "Vai a: "  msgid "No editing marks defined!"  msgstr "Nessun marcatore di modifica definito!" +msgid "No editing sequences defined!" +msgstr "" +  msgid "Can't start editing process!"  msgstr "Impossibile avviare il processo di modifica!" diff --git a/po/lt_LT.po b/po/lt_LT.po index 95f8e831..b53c7627 100644 --- a/po/lt_LT.po +++ b/po/lt_LT.po @@ -7,7 +7,7 @@ msgid ""  msgstr ""  "Project-Id-Version: VDR 1.7.16\n"  "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\n"  "PO-Revision-Date: 2010-10-30 11:55+0200\n"  "Last-Translator: Valdemaras Pipiras <varas@ambernet.lt>\n"  "Language-Team: Lithuanian <vdr@linuxtv.org>\n" @@ -1213,6 +1213,9 @@ msgstr "Peršokti: "  msgid "No editing marks defined!"  msgstr "Nenustatytos koregavimo žymės!" +msgid "No editing sequences defined!" +msgstr "" +  msgid "Can't start editing process!"  msgstr "Negali pradėti koregavimo!" diff --git a/po/mk_MK.po b/po/mk_MK.po index fd16ec88..89e8b739 100644 --- a/po/mk_MK.po +++ b/po/mk_MK.po @@ -7,7 +7,7 @@ msgid ""  msgstr ""  "Project-Id-Version: VDR-1.7.14\n"  "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\n"  "PO-Revision-Date: 2010-03-11 00:54+0100\n"  "Last-Translator: Dimitar Petrovski <dimeptr@gmail.com>\n"  "Language-Team: Macedonian <en@li.org>\n" @@ -1214,6 +1214,9 @@ msgstr "Скокни:"  msgid "No editing marks defined!"  msgstr "Нема одредено ознаки за сечење!" +msgid "No editing sequences defined!" +msgstr "" +  msgid "Can't start editing process!"  msgstr "Не може да почне уредување!" diff --git a/po/nl_NL.po b/po/nl_NL.po index 6c3825d9..5e515c36 100644 --- a/po/nl_NL.po +++ b/po/nl_NL.po @@ -11,7 +11,7 @@ msgid ""  msgstr ""  "Project-Id-Version: VDR 1.6.0\n"  "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\n"  "PO-Revision-Date: 2008-02-26 17:20+0100\n"  "Last-Translator: Johan Schuring <johan.schuring@vetteblei.nl>\n"  "Language-Team: Dutch <vdr@linuxtv.org>\n" @@ -1217,6 +1217,9 @@ msgstr "Springen: "  msgid "No editing marks defined!"  msgstr "Geen bewerkingsmarkeringen gedefinieerd!" +msgid "No editing sequences defined!" +msgstr "" +  msgid "Can't start editing process!"  msgstr "Kan niet beginnen met bewerken!" diff --git a/po/nn_NO.po b/po/nn_NO.po index 41ce5c30..f3662605 100644 --- a/po/nn_NO.po +++ b/po/nn_NO.po @@ -8,7 +8,7 @@ msgid ""  msgstr ""  "Project-Id-Version: VDR 1.6.0\n"  "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\n"  "PO-Revision-Date: 2007-08-12 14:17+0200\n"  "Last-Translator: Truls Slevigen <truls@slevigen.no>\n"  "Language-Team: Norwegian Nynorsk <vdr@linuxtv.org>\n" @@ -1214,6 +1214,9 @@ msgstr "Hopp: "  msgid "No editing marks defined!"  msgstr "" +msgid "No editing sequences defined!" +msgstr "" +  msgid "Can't start editing process!"  msgstr "Kan ikke starte redigeringsprosessen!" diff --git a/po/pl_PL.po b/po/pl_PL.po index f4c18761..fbe8a860 100644 --- a/po/pl_PL.po +++ b/po/pl_PL.po @@ -8,7 +8,7 @@ msgid ""  msgstr ""  "Project-Id-Version: VDR 1.6.0\n"  "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\n"  "PO-Revision-Date: 2008-03-09 12:59+0100\n"  "Last-Translator: Michael Rakowski <mrak@gmx.de>\n"  "Language-Team: Polish <vdr@linuxtv.org>\n" @@ -1214,6 +1214,9 @@ msgstr "Skok: "  msgid "No editing marks defined!"  msgstr "Nie zdefiniowano znacznikw montau!" +msgid "No editing sequences defined!" +msgstr "" +  msgid "Can't start editing process!"  msgstr "Nie mona uruchomi procesu edycji!" diff --git a/po/pt_PT.po b/po/pt_PT.po index 5cfc2037..d69e0690 100644 --- a/po/pt_PT.po +++ b/po/pt_PT.po @@ -8,7 +8,7 @@ msgid ""  msgstr ""  "Project-Id-Version: VDR 1.7.15\n"  "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\n"  "PO-Revision-Date: 2010-03-28 22:49+0100\n"  "Last-Translator: Cris Silva <hudokkow@gmail.com>\n"  "Language-Team: Portuguese <vdr@linuxtv.org>\n" @@ -1214,6 +1214,9 @@ msgstr "Saltar: "  msgid "No editing marks defined!"  msgstr "Marcas de edio no foram definidas!" +msgid "No editing sequences defined!" +msgstr "" +  msgid "Can't start editing process!"  msgstr "Impossvel iniciar processo de edio!" diff --git a/po/ro_RO.po b/po/ro_RO.po index 7db9a15a..03ae8f02 100644 --- a/po/ro_RO.po +++ b/po/ro_RO.po @@ -8,7 +8,7 @@ msgid ""  msgstr ""  "Project-Id-Version: VDR 1.7.12\n"  "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\n"  "PO-Revision-Date: 2012-11-05 01:28+0100\n"  "Last-Translator: Lucian Muresan <lucianm@users.sourceforge.net>\n"  "Language-Team: Romanian <vdr@linuxtv.org>\n" @@ -1216,6 +1216,9 @@ msgstr "Salt la: "  msgid "No editing marks defined!"  msgstr "Nu s-au pus marcaje de montaj pentru această înregistrare" +msgid "No editing sequences defined!" +msgstr "" +  msgid "Can't start editing process!"  msgstr "Nu pot porni montajul înregistrării!" diff --git a/po/ru_RU.po b/po/ru_RU.po index d8e1e8e1..b58fd9a1 100644 --- a/po/ru_RU.po +++ b/po/ru_RU.po @@ -8,7 +8,7 @@ msgid ""  msgstr ""  "Project-Id-Version: VDR 1.6.0\n"  "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\n"  "PO-Revision-Date: 2008-12-15 14:37+0100\n"  "Last-Translator: Oleg Roitburd <oleg@roitburd.de>\n"  "Language-Team: Russian <vdr@linuxtv.org>\n" @@ -1214,6 +1214,9 @@ msgstr ": "  msgid "No editing marks defined!"  msgstr "    !" +msgid "No editing sequences defined!" +msgstr "" +  msgid "Can't start editing process!"  msgstr "   !" diff --git a/po/sk_SK.po b/po/sk_SK.po index 0de2d061..ab5035e8 100644 --- a/po/sk_SK.po +++ b/po/sk_SK.po @@ -7,7 +7,7 @@ msgid ""  msgstr ""  "Project-Id-Version: VDR 1.7.16\n"  "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\n"  "PO-Revision-Date: 2011-02-15 16:29+0100\n"  "Last-Translator: Milan Hrala <hrala.milan@gmail.com>\n"  "Language-Team: Slovak <vdr@linuxtv.org>\n" @@ -1213,6 +1213,9 @@ msgstr "Skok: "  msgid "No editing marks defined!"  msgstr "Nie s uren znaky prav!" +msgid "No editing sequences defined!" +msgstr "" +  msgid "Can't start editing process!"  msgstr "Neme zaa spracovanie prav!" diff --git a/po/sl_SI.po b/po/sl_SI.po index 95568682..9326d858 100644 --- a/po/sl_SI.po +++ b/po/sl_SI.po @@ -8,7 +8,7 @@ msgid ""  msgstr ""  "Project-Id-Version: VDR 1.6.0\n"  "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\n"  "PO-Revision-Date: 2008-02-28 19:44+0100\n"  "Last-Translator: Matjaz Thaler <matjaz.thaler@guest.arnes.si>\n"  "Language-Team: Slovenian <vdr@linuxtv.org>\n" @@ -1214,6 +1214,9 @@ msgstr "Skoi: "  msgid "No editing marks defined!"  msgstr "Nobena prekinitvena toka ni definirana!" +msgid "No editing sequences defined!" +msgstr "" +  msgid "Can't start editing process!"  msgstr "Ne morem zaeti urejanja!" diff --git a/po/sr_SR.po b/po/sr_SR.po index c4149dda..c7b97d57 100644 --- a/po/sr_SR.po +++ b/po/sr_SR.po @@ -7,7 +7,7 @@ msgid ""  msgstr ""  "Project-Id-Version: VDR 1.7.1\n"  "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\n"  "PO-Revision-Date: 2011-01-09 15:57+0100\n"  "Last-Translator: Milan Cvijanovi <elcom_cvijo@hotmail.com>\n"  "Language-Team: Serbian <vdr@linuxtv.org>\n" @@ -1239,6 +1239,9 @@ msgstr "Skoi: "  msgid "No editing marks defined!"  msgstr "Nijedna taka rezanja nije odreena!" +msgid "No editing sequences defined!" +msgstr "" +  msgid "Can't start editing process!"  msgstr "Ne mogu zapoeti ureivanje!" diff --git a/po/sv_SE.po b/po/sv_SE.po index 951dd044..dc4d6208 100644 --- a/po/sv_SE.po +++ b/po/sv_SE.po @@ -10,7 +10,7 @@ msgid ""  msgstr ""  "Project-Id-Version: VDR 1.6.0\n"  "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\n"  "PO-Revision-Date: 2008-03-12 18:25+0100\n"  "Last-Translator: Magnus Andersson <svankan@bahnhof.se>\n"  "Language-Team: Swedish <vdr@linuxtv.org>\n" @@ -1216,6 +1216,9 @@ msgstr "Hopp: "  msgid "No editing marks defined!"  msgstr "Det finns inga redigeringsmrken" +msgid "No editing sequences defined!" +msgstr "" +  msgid "Can't start editing process!"  msgstr "Kan inte starta redigering!" diff --git a/po/tr_TR.po b/po/tr_TR.po index 8b161b3f..1f619441 100644 --- a/po/tr_TR.po +++ b/po/tr_TR.po @@ -7,7 +7,7 @@ msgid ""  msgstr ""  "Project-Id-Version: VDR 1.6.0\n"  "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\n"  "PO-Revision-Date: 2008-02-28 00:33+0100\n"  "Last-Translator: Oktay Yolgeen <oktay_73@yahoo.de>\n"  "Language-Team: Turkish <vdr@linuxtv.org>\n" @@ -1213,6 +1213,9 @@ msgstr "Atla: "  msgid "No editing marks defined!"  msgstr "Kesim iaretleri belirtilmemi!" +msgid "No editing sequences defined!" +msgstr "" +  msgid "Can't start editing process!"  msgstr "Kesim balatlamyor!" diff --git a/po/uk_UA.po b/po/uk_UA.po index 45e57ee5..72fe84c5 100644 --- a/po/uk_UA.po +++ b/po/uk_UA.po @@ -7,7 +7,7 @@ msgid ""  msgstr ""  "Project-Id-Version: VDR 1.7.7\n"  "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\n"  "PO-Revision-Date: 2010-04-25 16:35+0200\n"  "Last-Translator: Yarema aka Knedlyk <yupadmin@gmail.com>\n"  "Language-Team: Ukrainian <vdr@linuxtv.org>\n" @@ -1213,6 +1213,9 @@ msgstr "Перейти: "  msgid "No editing marks defined!"  msgstr "Не задано міток для монтажу!" +msgid "No editing sequences defined!" +msgstr "" +  msgid "Can't start editing process!"  msgstr "Неможливо почати монтаж запису!" diff --git a/po/zh_CN.po b/po/zh_CN.po index 4e2b951f..56b2de0d 100644 --- a/po/zh_CN.po +++ b/po/zh_CN.po @@ -7,7 +7,7 @@ msgid ""  msgstr ""  "Project-Id-Version: VDR 1.6.0\n"  "Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\n" -"POT-Creation-Date: 2012-09-15 14:04+0200\n" +"POT-Creation-Date: 2012-11-18 14:31+0100\n"  "PO-Revision-Date: 2009-09-23 23:50+0800\n"  "Last-Translator: Nan Feng <nfgx@21cn.com>\n"  "Language-Team: Chinese (simplified) <vdr@linuxtv.org>\n" @@ -1216,6 +1216,9 @@ msgstr "跳过: "  msgid "No editing marks defined!"  msgstr "无编辑标记定义!" +msgid "No editing sequences defined!" +msgstr "" +  msgid "Can't start editing process!"  msgstr "不能开始编辑处理" diff --git a/recording.c b/recording.c index 79db1c74..497bf2dd 100644 --- a/recording.c +++ b/recording.c @@ -4,7 +4,7 @@   * See the main source file 'vdr.c' for copyright information and   * how to reach the author.   * - * $Id: recording.c 2.72 2012/11/12 14:51:09 kls Exp $ + * $Id: recording.c 2.73 2012/11/13 13:46:49 kls Exp $   */  #include "recording.h" @@ -1456,6 +1456,54 @@ cMark *cMarks::GetNext(int Position)    return NULL;  } +cMark *cMarks::GetNextBegin(cMark *EndMark) +{ +  cMark *BeginMark = EndMark ? Next(EndMark) : First(); +  if (BeginMark) { +     while (cMark *NextMark = Next(BeginMark)) { +           if (BeginMark->Position() == NextMark->Position()) { // skip Begin/End at the same position +              if (!(BeginMark = Next(NextMark))) +                 break; +              } +           else +              break; +           } +     } +  return BeginMark; +} + +cMark *cMarks::GetNextEnd(cMark *BeginMark) +{ +  if (!BeginMark) +     return NULL; +  cMark *EndMark = Next(BeginMark); +  if (EndMark) { +     while (cMark *NextMark = Next(EndMark)) { +           if (EndMark->Position() == NextMark->Position()) { // skip End/Begin at the same position +              if (!(EndMark = Next(NextMark))) +                 break; +              } +           else +              break; +           } +     } +  return EndMark; +} + +int cMarks::GetNumSequences(void) +{ +  int NumSequences = 0; +  if (cMark *BeginMark = GetNextBegin()) { +     while (cMark *EndMark = GetNextEnd(BeginMark)) { +           NumSequences++; +           BeginMark = GetNextBegin(EndMark); +           } +     if (NumSequences == 0 && BeginMark->Position() > 0) +        NumSequences = 1; // there is only one actual "begin" mark at a non-zero offset, and no actual "end" mark +     } +  return NumSequences; +} +  // --- cRecordingUserCommand -------------------------------------------------  const char *cRecordingUserCommand::command = NULL; diff --git a/recording.h b/recording.h index 7118e783..9ae9b1ec 100644 --- a/recording.h +++ b/recording.h @@ -4,7 +4,7 @@   * See the main source file 'vdr.c' for copyright information and   * how to reach the author.   * - * $Id: recording.h 2.39 2012/11/12 14:51:09 kls Exp $ + * $Id: recording.h 2.40 2012/11/13 11:43:59 kls Exp $   */  #ifndef __RECORDING_H @@ -238,6 +238,19 @@ public:    cMark *Get(int Position);    cMark *GetPrev(int Position);    cMark *GetNext(int Position); +  cMark *GetNextBegin(cMark *EndMark = NULL); +       ///< Returns the next "begin" mark after EndMark, skipping any marks at the +       ///< same position as EndMark. If EndMark is NULL, the first actual "begin" +       ///< will be returned (if any). +  cMark *GetNextEnd(cMark *BeginMark); +       ///< Returns the next "end" mark after BeginMark, skipping any marks at the +       ///< same position as BeginMark. +  int GetNumSequences(void); +       ///< Returns the actual number of sequences to be cut from the recording. +       ///< If there is only one actual "begin" mark, and it is positioned at index +       ///< 0 (the beginning of the recording), and there is no "end" mark, the +       ///< return value is 0, which means that the result is the same as the original +       ///< recording.    };  #define RUC_BEFORERECORDING "before" @@ -4,7 +4,7 @@   * See the main source file 'vdr.c' for copyright information and   * how to reach the author.   * - * $Id: remux.c 2.70 2012/11/13 10:00:00 kls Exp $ + * $Id: remux.c 2.71 2012/11/18 12:18:08 kls Exp $   */  #include "remux.h" @@ -114,6 +114,32 @@ void cRemux::SetBrokenLink(uchar *Data, int Length)  // --- Some TS handling tools ------------------------------------------------ +void TsHidePayload(uchar *p) +{ +  p[1] &= ~TS_PAYLOAD_START; +  p[3] |=  TS_ADAPT_FIELD_EXISTS; +  p[3] &= ~TS_PAYLOAD_EXISTS; +  p[4] = TS_SIZE - 5; +  p[5] = 0x00; +  memset(p + 6, 0xFF, TS_SIZE - 6); +} + +void TsSetPcr(uchar *p, int64_t Pcr) +{ +  if (TsHasAdaptationField(p)) { +     if (p[4] >= 7 && (p[5] & TS_ADAPT_PCR)) { +        int64_t b = Pcr / PCRFACTOR; +        int e = Pcr % PCRFACTOR; +        p[ 6] =  b >> 25; +        p[ 7] =  b >> 17; +        p[ 8] =  b >>  9; +        p[ 9] =  b >>  1; +        p[10] = (b <<  7) | (p[10] & 0x7E) | ((e >> 8) & 0x01); +        p[11] =  e; +        } +     } +} +  int64_t TsGetPts(const uchar *p, int l)  {    // Find the first packet with a PTS and use it: @@ -127,27 +153,77 @@ int64_t TsGetPts(const uchar *p, int l)    return -1;  } -void TsSetTeiOnBrokenPackets(uchar *p, int l) +int64_t TsGetDts(const uchar *p, int l)  { -  bool Processed[MAXPID] = { false }; -  while (l >= TS_SIZE) { -        if (*p != TS_SYNC_BYTE) -           break; -        int Pid = TsPid(p); -        if (!Processed[Pid]) { -           if (!TsPayloadStart(p)) -              p[1] |= TS_ERROR; -           else { -              Processed[Pid] = true; -              int offs = TsPayloadOffset(p); -              cRemux::SetBrokenLink(p + offs, TS_SIZE - offs); -              } +  // Find the first packet with a DTS and use it: +  while (l > 0) { +        const uchar *d = p; +        if (TsPayloadStart(d) && TsGetPayload(&d) && PesHasDts(d)) +           return PesGetDts(d); +        p += TS_SIZE; +        l -= TS_SIZE; +        } +  return -1; +} + +void TsSetPts(uchar *p, int l, int64_t Pts) +{ +  // Find the first packet with a PTS and use it: +  while (l > 0) { +        const uchar *d = p; +        if (TsPayloadStart(d) && TsGetPayload(&d) && PesHasPts(d)) { +           PesSetPts(const_cast<uchar *>(d), Pts); +           return;             } +        p += TS_SIZE;          l -= TS_SIZE; +        } +} + +void TsSetDts(uchar *p, int l, int64_t Dts) +{ +  // Find the first packet with a DTS and use it: +  while (l > 0) { +        const uchar *d = p; +        if (TsPayloadStart(d) && TsGetPayload(&d) && PesHasDts(d)) { +           PesSetDts(const_cast<uchar *>(d), Dts); +           return; +           }          p += TS_SIZE; +        l -= TS_SIZE;          }  } +// --- Some PES handling tools ----------------------------------------------- + +void PesSetPts(uchar *p, int64_t Pts) +{ +  p[ 9] = ((Pts >> 29) & 0x0E) | (p[9] & 0xF1); +  p[10] =   Pts >> 22; +  p[11] = ((Pts >> 14) & 0xFE) | 0x01; +  p[12] =   Pts >>  7; +  p[13] = ((Pts <<  1) & 0xFE) | 0x01; +} + +void PesSetDts(uchar *p, int64_t Dts) +{ +  p[14] = ((Dts >> 29) & 0x0E) | (p[14] & 0xF1); +  p[15] =   Dts >> 22; +  p[16] = ((Dts >> 14) & 0xFE) | 0x01; +  p[17] =   Dts >>  7; +  p[18] = ((Dts <<  1) & 0xFE) | 0x01; +} + +int64_t PtsDiff(int64_t Pts1, int64_t Pts2) +{ +  int64_t d = Pts2 - Pts1; +  if (d > MAX33BIT / 2) +     return d - (MAX33BIT + 1); +  if (d < -MAX33BIT / 2) +     return d + (MAX33BIT + 1); +  return d; +} +  // --- cTsPayload ------------------------------------------------------------  cTsPayload::cTsPayload(void) @@ -1395,7 +1471,7 @@ int cFrameDetector::Analyze(const uchar *Data, int Length)                               }                            }                         else // audio -                          framesPerSecond = 90000.0 / Delta; // PTS of audio frames is always increasing +                          framesPerSecond = double(PTSTICKS) / Delta; // PTS of audio frames is always increasing                         dbgframes("\nDelta = %d  FPS = %5.2f  FPPU = %d NF = %d\n", Delta, framesPerSecond, framesPerPayloadUnit, numPtsValues + 1);                         synced = true;                         parser->SetDebug(false); @@ -4,7 +4,7 @@   * See the main source file 'vdr.c' for copyright information and   * how to reach the author.   * - * $Id: remux.h 2.34 2012/11/06 11:03:06 kls Exp $ + * $Id: remux.h 2.35 2012/11/18 12:17:23 kls Exp $   */  #ifndef __REMUX_H @@ -52,6 +52,11 @@ public:  #define PATPID 0x0000 // PAT PID (constant 0)  #define MAXPID 0x2000 // for arrays that use a PID as the index +#define PTSTICKS  90000 // number of PTS ticks per second +#define PCRFACTOR 300 // conversion from 27MHz PCR extension to 90kHz PCR base +#define MAX33BIT  0x00000001FFFFFFFFLL // max. possible value with 33 bit +#define MAX27MHZ  ((MAX33BIT + 1) * PCRFACTOR - 1) // max. possible PCR value +  inline bool TsHasPayload(const uchar *p)  {    return p[3] & TS_PAYLOAD_EXISTS; @@ -82,6 +87,16 @@ inline bool TsIsScrambled(const uchar *p)    return p[3] & TS_SCRAMBLING_CONTROL;  } +inline uchar TsGetContinuityCounter(const uchar *p) +{ +  return p[3] & TS_CONT_CNT_MASK; +} + +inline void TsSetContinuityCounter(uchar *p, uchar Counter) +{ +  p[3] = (p[3] & ~TS_CONT_CNT_MASK) | (Counter & TS_CONT_CNT_MASK); +} +  inline int TsPayloadOffset(const uchar *p)  {    int o = TsHasAdaptationField(p) ? p[4] + 5 : 4; @@ -103,15 +118,31 @@ inline int TsContinuityCounter(const uchar *p)    return p[3] & TS_CONT_CNT_MASK;  } -inline int TsGetAdaptationField(const uchar *p) +inline int64_t TsGetPcr(const uchar *p)  { -  return TsHasAdaptationField(p) ? p[5] : 0x00; +  if (TsHasAdaptationField(p)) { +     if (p[4] >= 7 && (p[5] & TS_ADAPT_PCR)) { +        return ((((int64_t)p[ 6]) << 25) | +                (((int64_t)p[ 7]) << 17) | +                (((int64_t)p[ 8]) <<  9) | +                (((int64_t)p[ 9]) <<  1) | +                (((int64_t)p[10]) >>  7)) * PCRFACTOR + +               (((((int)p[10]) & 0x01) << 8) | +                ( ((int)p[11]))); +        } +     } +  return -1;  } +void TsHidePayload(uchar *p); +void TsSetPcr(uchar *p, int64_t Pcr); +  // The following functions all take a pointer to a sequence of complete TS packets.  int64_t TsGetPts(const uchar *p, int l); -void TsSetTeiOnBrokenPackets(uchar *p, int l); +int64_t TsGetDts(const uchar *p, int l); +void TsSetPts(uchar *p, int l, int64_t Pts); +void TsSetDts(uchar *p, int l, int64_t Dts);  // Some PES handling tools:  // The following functions that take a pointer to PES data all assume that @@ -142,6 +173,11 @@ inline bool PesHasPts(const uchar *p)    return (p[7] & 0x80) && p[8] >= 5;  } +inline bool PesHasDts(const uchar *p) +{ +  return (p[7] & 0x40) && p[8] >= 10; +} +  inline int64_t PesGetPts(const uchar *p)  {    return ((((int64_t)p[ 9]) & 0x0E) << 29) | @@ -151,6 +187,28 @@ inline int64_t PesGetPts(const uchar *p)           ((((int64_t)p[13]) & 0xFE) >>  1);  } +inline int64_t PesGetDts(const uchar *p) +{ +  return ((((int64_t)p[14]) & 0x0E) << 29) | +         (( (int64_t)p[15])         << 22) | +         ((((int64_t)p[16]) & 0xFE) << 14) | +         (( (int64_t)p[17])         <<  7) | +         ((((int64_t)p[18]) & 0xFE) >>  1); +} + +void PesSetPts(uchar *p, int64_t Pts); +void PesSetDts(uchar *p, int64_t Dts); + +// PTS handling: + +inline int64_t PtsAdd(int64_t Pts1, int64_t Pts2) { return (Pts1 + Pts2) & MAX33BIT; } +       ///< Adds the given PTS values, taking into account the 33bit wrap around. +int64_t PtsDiff(int64_t Pts1, int64_t Pts2); +       ///< Returns the difference between two PTS values. The result of Pts2 - Pts1 +       ///< is the actual number of 90kHz time ticks that pass from Pts1 to Pts2, +       ///< properly taking into account the 33bit wrap around. If Pts2 is "before" +       ///< Pts1, the result is negative. +  // A transprent TS payload handler:  class cTsPayload { | 
