1
0
mirror of https://github.com/VDR4Arch/vdr.git synced 2023-10-10 13:36:52 +02:00

Improved editing TS recordings

This commit is contained in:
Klaus Schmidinger 2012-11-18 12:19:51 +01:00
parent 5b4e1fa793
commit cca2cd35ad
36 changed files with 850 additions and 228 deletions

17
HISTORY
View File

@ -7272,7 +7272,7 @@ Video Disk Recorder Revision History
".keep" to prevent a directory from being deleted when it is empty. Currently the ".keep" to prevent a directory from being deleted when it is empty. Currently the
only file name that is ignored is ".sort". 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 - 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). 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 - 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 of the list of marks the returned pointer might have pointed to a totally different
mark. Besides, the return value was never actually used. 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

20
MANUAL
View File

@ -367,13 +367,13 @@ Version 1.6
- 7, 9 Jump back and forward between editing marks. Replay goes into still - 7, 9 Jump back and forward between editing marks. Replay goes into still
mode after jumping to a mark. mode after jumping to a mark.
- 8 Positions replay at a point 3 seconds before the current or next - 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. - 2 Start the actual cutting process.
Editing marks are represented by black, vertical lines in the progress display. 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. 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 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 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 (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 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. "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 6,...) is an "end" mark. Inserting or toggling a mark on or off automatically
adjusts the sequence to the right side of that mark. 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 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. 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 Currently editing marks can only be set at I-frames, which typically appear
every 12th frame. So editing can be done with a resolution of roughly half every half of a second to a second. A "begin" mark marks the first frame of
a second. A "start" mark marks the first frame of a resulting video a resulting video sequence, and an "end" mark marks the last frame of that
sequence, and an "end" mark marks the last frame of that sequence. 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 An edited recording (indicated by the '%' character) will never be deleted
automatically in case the disk runs full (no matter what "lifetime" it has). automatically in case the disk runs full (no matter what "lifetime" it has).

654
cutter.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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" #include "cutter.h"
@ -13,17 +13,261 @@
#include "remux.h" #include "remux.h"
#include "videodir.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 -------------------------------------------------------- // --- cCuttingThread --------------------------------------------------------
class cCuttingThread : public cThread { class cCuttingThread : public cThread {
private: private:
const char *error; const char *error;
bool isPesRecording; bool isPesRecording;
double framesPerSecond;
cUnbufferedFile *fromFile, *toFile; cUnbufferedFile *fromFile, *toFile;
cFileName *fromFileName, *toFileName; cFileName *fromFileName, *toFileName;
cIndexFile *fromIndex, *toIndex; cIndexFile *fromIndex, *toIndex;
cMarks fromMarks, toMarks; cMarks fromMarks, toMarks;
int numSequences;
off_t maxVideoFileSize; 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: protected:
virtual void Action(void); virtual void Action(void);
public: public:
@ -41,16 +285,25 @@ cCuttingThread::cCuttingThread(const char *FromFileName, const char *ToFileName)
fromIndex = toIndex = NULL; fromIndex = toIndex = NULL;
cRecording Recording(FromFileName); cRecording Recording(FromFileName);
isPesRecording = Recording.IsPesRecording(); isPesRecording = Recording.IsPesRecording();
if (fromMarks.Load(FromFileName, Recording.FramesPerSecond(), isPesRecording) && fromMarks.Count()) { framesPerSecond = Recording.FramesPerSecond();
fromFileName = new cFileName(FromFileName, false, true, isPesRecording); suspensionLogged = false;
toFileName = new cFileName(ToFileName, true, true, isPesRecording); fileSize = 0;
fromIndex = new cIndexFile(FromFileName, false, isPesRecording); ptsFixer.Setup(framesPerSecond);
toIndex = new cIndexFile(ToFileName, true, isPesRecording); if (fromMarks.Load(FromFileName, framesPerSecond, isPesRecording) && fromMarks.Count()) {
toMarks.Load(ToFileName, Recording.FramesPerSecond(), isPesRecording); // doesn't actually load marks, just sets the file name numSequences = fromMarks.GetNumSequences();
maxVideoFileSize = MEGABYTE(Setup.MaxVideoFileSize); if (numSequences > 0) {
if (isPesRecording && maxVideoFileSize > MEGABYTE(MAXVIDEOFILESIZEPES)) fromFileName = new cFileName(FromFileName, false, true, isPesRecording);
maxVideoFileSize = MEGABYTE(MAXVIDEOFILESIZEPES); toFileName = new cFileName(ToFileName, true, true, isPesRecording);
Start(); 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 else
esyslog("no editing marks found for %s", FromFileName); esyslog("no editing marks found for %s", FromFileName);
@ -65,168 +318,235 @@ cCuttingThread::~cCuttingThread()
delete toIndex; 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) void cCuttingThread::Action(void)
{ {
cMark *Mark = fromMarks.First(); if (cMark *BeginMark = fromMarks.GetNextBegin()) {
if (Mark) {
fromFile = fromFileName->Open(); fromFile = fromFileName->Open();
toFile = toFileName->Open(); toFile = toFileName->Open();
if (!fromFile || !toFile) if (!fromFile || !toFile)
return; return;
fromFile->SetReadAhead(MEGABYTE(20)); int LastEndIndex = -1;
int Index = Mark->Position(); while (BeginMark && Running()) {
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;
// Suspend cutting if we have severe throughput problems: // Suspend cutting if we have severe throughput problems:
if (Throttled()) {
if (cIoThrottle::Engaged()) {
if (!suspensionLogged) {
dsyslog("suspending cutter thread");
suspensionLogged = true;
}
cCondWait::SleepMs(100); cCondWait::SleepMs(100);
continue; continue;
} }
else if (suspensionLogged) {
dsyslog("resuming cutter thread");
suspensionLogged = false;
}
// Make sure there is enough disk space: // Make sure there is enough disk space:
AssertFreeDiskSpace(-1); AssertFreeDiskSpace(-1);
// Determine the actual begin and end marks, skipping any marks at the same position:
// Read one frame: cMark *EndMark = fromMarks.GetNextEnd(BeginMark);
// Process the current sequence:
if (fromIndex->Get(Index++, &FileNumber, &FileOffset, &Independent, &Length)) { int EndIndex = EndMark ? EndMark->Position() : fromIndex->Last() + 1;
if (FileNumber != CurrentFileNumber) { int NextBeginIndex = -1;
fromFile = fromFileName->SetOffset(FileNumber, FileOffset); if (EndMark) {
if (fromFile) if (cMark *NextBeginMark = fromMarks.GetNextBegin(EndMark))
fromFile->SetReadAhead(MEGABYTE(20)); NextBeginIndex = NextBeginMark->Position();
CurrentFileNumber = FileNumber; }
} if (!ProcessSequence(LastEndIndex, BeginMark->Position(), EndIndex, NextBeginIndex))
if (fromFile) { break;
int len = ReadFrame(fromFile, buffer, Length, sizeof(buffer)); if (!EndMark)
if (len < 0) { break; // reached EOF
error = "ReadFrame"; LastEndIndex = EndIndex;
// Switch to the next sequence:
BeginMark = fromMarks.GetNextBegin(EndMark);
if (BeginMark) {
// Split edited files:
if (Setup.SplitEditedFiles) {
if (!SwitchFile(true))
break; break;
}
if (len != Length) {
CurrentFileNumber = 0; // this re-syncs in case the frame was larger than the buffer
Length = len;
}
} }
else {
error = "fromFile";
break;
}
}
else {
// Error, unless we're past the last cut-in and there's no cut-out
if (Mark || LastMark)
error = "index";
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";
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(); Recordings.TouchUpdate();
@ -255,7 +575,7 @@ bool cCutter::Start(const char *FileName)
cMarks FromMarks; cMarks FromMarks;
FromMarks.Load(FileName, Recording.FramesPerSecond(), Recording.IsPesRecording()); 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); Recording.SetStartTime(Recording.Start() + (int(First->Position() / Recording.FramesPerSecond() + 30) / 60) * 60);
const char *evn = Recording.PrefixFileName('%'); const char *evn = Recording.PrefixFileName('%');
@ -343,13 +663,17 @@ bool CutRecording(const char *FileName)
if (Recording.Name()) { if (Recording.Name()) {
cMarks Marks; cMarks Marks;
if (Marks.Load(FileName, Recording.FramesPerSecond(), Recording.IsPesRecording()) && Marks.Count()) { if (Marks.Load(FileName, Recording.FramesPerSecond(), Recording.IsPesRecording()) && Marks.Count()) {
if (cCutter::Start(FileName)) { if (Marks.GetNumSequences()) {
while (cCutter::Active()) if (cCutter::Start(FileName)) {
cCondWait::SleepMs(CUTTINGCHECKINTERVAL); while (cCutter::Active())
return true; cCondWait::SleepMs(CUTTINGCHECKINTERVAL);
return true;
}
else
fprintf(stderr, "can't start editing process\n");
} }
else else
fprintf(stderr, "can't start editing process\n"); fprintf(stderr, "'%s' has no editing sequences\n", FileName);
} }
else else
fprintf(stderr, "'%s' has no editing marks\n", FileName); fprintf(stderr, "'%s' has no editing marks\n", FileName);

4
menu.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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" #include "menu.h"
@ -4796,6 +4796,8 @@ void cReplayControl::EditCut(void)
if (!cCutter::Active()) { if (!cCutter::Active()) {
if (!marks.Count()) if (!marks.Count())
Skins.Message(mtError, tr("No editing marks defined!")); 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)) else if (!cCutter::Start(fileName))
Skins.Message(mtError, tr("Can't start editing process!")); Skins.Message(mtError, tr("Can't start editing process!"));
else else

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 1.7.0\n" "Project-Id-Version: VDR 1.7.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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" "PO-Revision-Date: 2008-10-16 11:16-0400\n"
"Last-Translator: Osama Alrawab <alrawab@hotmail.com>\n" "Last-Translator: Osama Alrawab <alrawab@hotmail.com>\n"
"Language-Team: Arabic <ar@li.org>\n" "Language-Team: Arabic <ar@li.org>\n"
@ -1241,6 +1241,9 @@ msgstr "اقفز الى "
msgid "No editing marks defined!" msgid "No editing marks defined!"
msgstr "لاتوجد علامات تعديل معرفة" msgstr "لاتوجد علامات تعديل معرفة"
msgid "No editing sequences defined!"
msgstr ""
msgid "Can't start editing process!" msgid "Can't start editing process!"
msgstr "لا يمكن البدء فى عملية التعديل" msgstr "لا يمكن البدء فى عملية التعديل"

View File

@ -10,7 +10,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 1.6.0\n" "Project-Id-Version: VDR 1.6.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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" "PO-Revision-Date: 2008-03-02 19:02+0100\n"
"Last-Translator: Luca Olivetti <luca@ventoso.org>\n" "Last-Translator: Luca Olivetti <luca@ventoso.org>\n"
"Language-Team: Catalan <vdr@linuxtv.org>\n" "Language-Team: Catalan <vdr@linuxtv.org>\n"
@ -1216,6 +1216,9 @@ msgstr "Salta a:"
msgid "No editing marks defined!" msgid "No editing marks defined!"
msgstr "No hi ha marques d'edició definides" msgstr "No hi ha marques d'edició definides"
msgid "No editing sequences defined!"
msgstr ""
msgid "Can't start editing process!" msgid "Can't start editing process!"
msgstr "No puc iniciar el procés d'edició!" msgstr "No puc iniciar el procés d'edició!"

View File

@ -9,7 +9,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 1.7.14\n" "Project-Id-Version: VDR 1.7.14\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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" "PO-Revision-Date: 2010-05-06 11:00+0200\n"
"Last-Translator: Radek Šťastný <dedkus@gmail.com>\n" "Last-Translator: Radek Šťastný <dedkus@gmail.com>\n"
"Language-Team: Czech <vdr@linuxtv.org>\n" "Language-Team: Czech <vdr@linuxtv.org>\n"
@ -1215,6 +1215,9 @@ msgstr "Skok: "
msgid "No editing marks defined!" msgid "No editing marks defined!"
msgstr "Nejsou definovány editační značky!" msgstr "Nejsou definovány editační značky!"
msgid "No editing sequences defined!"
msgstr ""
msgid "Can't start editing process!" msgid "Can't start editing process!"
msgstr "Nelze začít editační proces!" msgstr "Nelze začít editační proces!"

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 1.6.0\n" "Project-Id-Version: VDR 1.6.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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" "PO-Revision-Date: 2007-08-12 14:17+0200\n"
"Last-Translator: Mogens Elneff <mogens@elneff.dk>\n" "Last-Translator: Mogens Elneff <mogens@elneff.dk>\n"
"Language-Team: Danish <vdr@linuxtv.org>\n" "Language-Team: Danish <vdr@linuxtv.org>\n"
@ -1213,6 +1213,9 @@ msgstr "Hop: "
msgid "No editing marks defined!" msgid "No editing marks defined!"
msgstr "Der er ikke sat nogen redigeringsmærker!" msgstr "Der er ikke sat nogen redigeringsmærker!"
msgid "No editing sequences defined!"
msgstr ""
msgid "Can't start editing process!" msgid "Can't start editing process!"
msgstr "Kan ikke starte redigeringsprocessen!" msgstr "Kan ikke starte redigeringsprocessen!"

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 1.6.0\n" "Project-Id-Version: VDR 1.6.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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" "PO-Revision-Date: 2010-01-16 16:46+0100\n"
"Last-Translator: Klaus Schmidinger <kls@tvdr.de>\n" "Last-Translator: Klaus Schmidinger <kls@tvdr.de>\n"
"Language-Team: German <vdr@linuxtv.org>\n" "Language-Team: German <vdr@linuxtv.org>\n"
@ -1213,6 +1213,9 @@ msgstr "Springen: "
msgid "No editing marks defined!" msgid "No editing marks defined!"
msgstr "Keine Schnittmarken gesetzt!" msgstr "Keine Schnittmarken gesetzt!"
msgid "No editing sequences defined!"
msgstr "Keine Schnittsequenzen definiert!"
msgid "Can't start editing process!" msgid "Can't start editing process!"
msgstr "Schnitt kann nicht gestartet werden!" msgstr "Schnitt kann nicht gestartet werden!"

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 1.6.0\n" "Project-Id-Version: VDR 1.6.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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" "PO-Revision-Date: 2007-08-12 14:17+0200\n"
"Last-Translator: Dimitrios Dimitrakos <mail@dimitrios.de>\n" "Last-Translator: Dimitrios Dimitrakos <mail@dimitrios.de>\n"
"Language-Team: Greek <vdr@linuxtv.org>\n" "Language-Team: Greek <vdr@linuxtv.org>\n"
@ -1213,6 +1213,9 @@ msgstr "
msgid "No editing marks defined!" msgid "No editing marks defined!"
msgstr "ÄÝí Ý÷ïõí ïñéóôåß óçìåßá åðåîåñãáóßáò" msgstr "ÄÝí Ý÷ïõí ïñéóôåß óçìåßá åðåîåñãáóßáò"
msgid "No editing sequences defined!"
msgstr ""
msgid "Can't start editing process!" msgid "Can't start editing process!"
msgstr "Áäõíáìßá åêêßíçóçò ôçò åðåîåñãáóßáò!" msgstr "Áäõíáìßá åêêßíçóçò ôçò åðåîåñãáóßáò!"

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 1.6.0\n" "Project-Id-Version: VDR 1.6.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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" "PO-Revision-Date: 2008-03-02 19:02+0100\n"
"Last-Translator: Luca Olivetti <luca@ventoso.org>\n" "Last-Translator: Luca Olivetti <luca@ventoso.org>\n"
"Language-Team: Spanish <vdr@linuxtv.org>\n" "Language-Team: Spanish <vdr@linuxtv.org>\n"
@ -1214,6 +1214,9 @@ msgstr "Saltar: "
msgid "No editing marks defined!" msgid "No editing marks defined!"
msgstr "¡No se definieron marcas de edición!" msgstr "¡No se definieron marcas de edición!"
msgid "No editing sequences defined!"
msgstr ""
msgid "Can't start editing process!" msgid "Can't start editing process!"
msgstr "¡No se puede iniciar el proceso de edición!" msgstr "¡No se puede iniciar el proceso de edición!"

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 1.6.0\n" "Project-Id-Version: VDR 1.6.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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" "PO-Revision-Date: 2007-08-12 14:17+0200\n"
"Last-Translator: Arthur Konovalov <artlov@gmail.com>\n" "Last-Translator: Arthur Konovalov <artlov@gmail.com>\n"
"Language-Team: Estonian <vdr@linuxtv.org>\n" "Language-Team: Estonian <vdr@linuxtv.org>\n"
@ -1213,6 +1213,9 @@ msgstr "Hüpe: "
msgid "No editing marks defined!" msgid "No editing marks defined!"
msgstr "Redigeerimise markerid puuduvad!" msgstr "Redigeerimise markerid puuduvad!"
msgid "No editing sequences defined!"
msgstr ""
msgid "Can't start editing process!" msgid "Can't start editing process!"
msgstr "Redigeerimise start nurjus!" msgstr "Redigeerimise start nurjus!"

View File

@ -10,7 +10,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 1.6.0\n" "Project-Id-Version: VDR 1.6.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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" "PO-Revision-Date: 2007-08-15 15:52+0200\n"
"Last-Translator: Rolf Ahrenberg <rahrenbe@cc.hut.fi>\n" "Last-Translator: Rolf Ahrenberg <rahrenbe@cc.hut.fi>\n"
"Language-Team: Finnish <vdr@linuxtv.org>\n" "Language-Team: Finnish <vdr@linuxtv.org>\n"
@ -1216,6 +1216,9 @@ msgstr "Siirry: "
msgid "No editing marks defined!" msgid "No editing marks defined!"
msgstr "Muokkausmerkinnät puuttuvat!" msgstr "Muokkausmerkinnät puuttuvat!"
msgid "No editing sequences defined!"
msgstr ""
msgid "Can't start editing process!" msgid "Can't start editing process!"
msgstr "Muokkauksen aloitus epäonnistui!" msgstr "Muokkauksen aloitus epäonnistui!"

View File

@ -13,7 +13,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 1.6.0\n" "Project-Id-Version: VDR 1.6.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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" "PO-Revision-Date: 2008-02-27 18:14+0100\n"
"Last-Translator: Jean-Claude Repetto <jc@repetto.org>\n" "Last-Translator: Jean-Claude Repetto <jc@repetto.org>\n"
"Language-Team: French <vdr@linuxtv.org>\n" "Language-Team: French <vdr@linuxtv.org>\n"
@ -1219,6 +1219,9 @@ msgstr "Acc
msgid "No editing marks defined!" msgid "No editing marks defined!"
msgstr "Pas de marques d'édition définies !" msgstr "Pas de marques d'édition définies !"
msgid "No editing sequences defined!"
msgstr ""
msgid "Can't start editing process!" msgid "Can't start editing process!"
msgstr "Impossible de commencer le montage !" msgstr "Impossible de commencer le montage !"

View File

@ -9,7 +9,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 1.6.0\n" "Project-Id-Version: VDR 1.6.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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" "PO-Revision-Date: 2008-03-17 19:00+0100\n"
"Last-Translator: Adrian Caval <anrxc@sysphere.org>\n" "Last-Translator: Adrian Caval <anrxc@sysphere.org>\n"
"Language-Team: Croatian <vdr@linuxtv.org>\n" "Language-Team: Croatian <vdr@linuxtv.org>\n"
@ -1215,6 +1215,9 @@ msgstr "Sko
msgid "No editing marks defined!" msgid "No editing marks defined!"
msgstr "Nijedna toèka rezanja nije odreðena!" msgstr "Nijedna toèka rezanja nije odreðena!"
msgid "No editing sequences defined!"
msgstr ""
msgid "Can't start editing process!" msgid "Can't start editing process!"
msgstr "Ne mogu zapoèeti ureðivanje!" msgstr "Ne mogu zapoèeti ureðivanje!"

View File

@ -10,7 +10,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 1.6.0\n" "Project-Id-Version: VDR 1.6.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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" "PO-Revision-Date: 2012-01-02 11:54+0200\n"
"Last-Translator: István Füley <ifuley@tigercomp.ro>\n" "Last-Translator: István Füley <ifuley@tigercomp.ro>\n"
"Language-Team: Hungarian <vdr@linuxtv.org>\n" "Language-Team: Hungarian <vdr@linuxtv.org>\n"
@ -1217,6 +1217,9 @@ msgstr "Ugr
msgid "No editing marks defined!" msgid "No editing marks defined!"
msgstr "Nincs vágópont kijelölve" msgstr "Nincs vágópont kijelölve"
msgid "No editing sequences defined!"
msgstr ""
msgid "Can't start editing process!" msgid "Can't start editing process!"
msgstr "A vágás nem indítható!" msgstr "A vágás nem indítható!"

View File

@ -11,7 +11,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 1.6.0\n" "Project-Id-Version: VDR 1.6.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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" "PO-Revision-Date: 2012-06-06 22:50+0100\n"
"Last-Translator: Diego Pierotto <vdr-italian@tiscali.it>\n" "Last-Translator: Diego Pierotto <vdr-italian@tiscali.it>\n"
"Language-Team: Italian <vdr@linuxtv.org>\n" "Language-Team: Italian <vdr@linuxtv.org>\n"
@ -1220,6 +1220,9 @@ msgstr "Vai a: "
msgid "No editing marks defined!" msgid "No editing marks defined!"
msgstr "Nessun marcatore di modifica definito!" msgstr "Nessun marcatore di modifica definito!"
msgid "No editing sequences defined!"
msgstr ""
msgid "Can't start editing process!" msgid "Can't start editing process!"
msgstr "Impossibile avviare il processo di modifica!" msgstr "Impossibile avviare il processo di modifica!"

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 1.7.16\n" "Project-Id-Version: VDR 1.7.16\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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" "PO-Revision-Date: 2010-10-30 11:55+0200\n"
"Last-Translator: Valdemaras Pipiras <varas@ambernet.lt>\n" "Last-Translator: Valdemaras Pipiras <varas@ambernet.lt>\n"
"Language-Team: Lithuanian <vdr@linuxtv.org>\n" "Language-Team: Lithuanian <vdr@linuxtv.org>\n"
@ -1213,6 +1213,9 @@ msgstr "Peršokti: "
msgid "No editing marks defined!" msgid "No editing marks defined!"
msgstr "Nenustatytos koregavimo žymės!" msgstr "Nenustatytos koregavimo žymės!"
msgid "No editing sequences defined!"
msgstr ""
msgid "Can't start editing process!" msgid "Can't start editing process!"
msgstr "Negali pradėti koregavimo!" msgstr "Negali pradėti koregavimo!"

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR-1.7.14\n" "Project-Id-Version: VDR-1.7.14\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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" "PO-Revision-Date: 2010-03-11 00:54+0100\n"
"Last-Translator: Dimitar Petrovski <dimeptr@gmail.com>\n" "Last-Translator: Dimitar Petrovski <dimeptr@gmail.com>\n"
"Language-Team: Macedonian <en@li.org>\n" "Language-Team: Macedonian <en@li.org>\n"
@ -1214,6 +1214,9 @@ msgstr "Скокни:"
msgid "No editing marks defined!" msgid "No editing marks defined!"
msgstr "Нема одредено ознаки за сечење!" msgstr "Нема одредено ознаки за сечење!"
msgid "No editing sequences defined!"
msgstr ""
msgid "Can't start editing process!" msgid "Can't start editing process!"
msgstr "Не може да почне уредување!" msgstr "Не може да почне уредување!"

View File

@ -11,7 +11,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 1.6.0\n" "Project-Id-Version: VDR 1.6.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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" "PO-Revision-Date: 2008-02-26 17:20+0100\n"
"Last-Translator: Johan Schuring <johan.schuring@vetteblei.nl>\n" "Last-Translator: Johan Schuring <johan.schuring@vetteblei.nl>\n"
"Language-Team: Dutch <vdr@linuxtv.org>\n" "Language-Team: Dutch <vdr@linuxtv.org>\n"
@ -1217,6 +1217,9 @@ msgstr "Springen: "
msgid "No editing marks defined!" msgid "No editing marks defined!"
msgstr "Geen bewerkingsmarkeringen gedefinieerd!" msgstr "Geen bewerkingsmarkeringen gedefinieerd!"
msgid "No editing sequences defined!"
msgstr ""
msgid "Can't start editing process!" msgid "Can't start editing process!"
msgstr "Kan niet beginnen met bewerken!" msgstr "Kan niet beginnen met bewerken!"

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 1.6.0\n" "Project-Id-Version: VDR 1.6.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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" "PO-Revision-Date: 2007-08-12 14:17+0200\n"
"Last-Translator: Truls Slevigen <truls@slevigen.no>\n" "Last-Translator: Truls Slevigen <truls@slevigen.no>\n"
"Language-Team: Norwegian Nynorsk <vdr@linuxtv.org>\n" "Language-Team: Norwegian Nynorsk <vdr@linuxtv.org>\n"
@ -1214,6 +1214,9 @@ msgstr "Hopp: "
msgid "No editing marks defined!" msgid "No editing marks defined!"
msgstr "" msgstr ""
msgid "No editing sequences defined!"
msgstr ""
msgid "Can't start editing process!" msgid "Can't start editing process!"
msgstr "Kan ikke starte redigeringsprosessen!" msgstr "Kan ikke starte redigeringsprosessen!"

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 1.6.0\n" "Project-Id-Version: VDR 1.6.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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" "PO-Revision-Date: 2008-03-09 12:59+0100\n"
"Last-Translator: Michael Rakowski <mrak@gmx.de>\n" "Last-Translator: Michael Rakowski <mrak@gmx.de>\n"
"Language-Team: Polish <vdr@linuxtv.org>\n" "Language-Team: Polish <vdr@linuxtv.org>\n"
@ -1214,6 +1214,9 @@ msgstr "Skok: "
msgid "No editing marks defined!" msgid "No editing marks defined!"
msgstr "Nie zdefiniowano znaczników monta¿u!" msgstr "Nie zdefiniowano znaczników monta¿u!"
msgid "No editing sequences defined!"
msgstr ""
msgid "Can't start editing process!" msgid "Can't start editing process!"
msgstr "Nie mo¿na uruchomiæ procesu edycji!" msgstr "Nie mo¿na uruchomiæ procesu edycji!"

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 1.7.15\n" "Project-Id-Version: VDR 1.7.15\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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" "PO-Revision-Date: 2010-03-28 22:49+0100\n"
"Last-Translator: Cris Silva <hudokkow@gmail.com>\n" "Last-Translator: Cris Silva <hudokkow@gmail.com>\n"
"Language-Team: Portuguese <vdr@linuxtv.org>\n" "Language-Team: Portuguese <vdr@linuxtv.org>\n"
@ -1214,6 +1214,9 @@ msgstr "Saltar: "
msgid "No editing marks defined!" msgid "No editing marks defined!"
msgstr "Marcas de edição não foram definidas!" msgstr "Marcas de edição não foram definidas!"
msgid "No editing sequences defined!"
msgstr ""
msgid "Can't start editing process!" msgid "Can't start editing process!"
msgstr "Impossível iniciar processo de edição!" msgstr "Impossível iniciar processo de edição!"

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 1.7.12\n" "Project-Id-Version: VDR 1.7.12\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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" "PO-Revision-Date: 2012-11-05 01:28+0100\n"
"Last-Translator: Lucian Muresan <lucianm@users.sourceforge.net>\n" "Last-Translator: Lucian Muresan <lucianm@users.sourceforge.net>\n"
"Language-Team: Romanian <vdr@linuxtv.org>\n" "Language-Team: Romanian <vdr@linuxtv.org>\n"
@ -1216,6 +1216,9 @@ msgstr "Salt la: "
msgid "No editing marks defined!" msgid "No editing marks defined!"
msgstr "Nu s-au pus marcaje de montaj pentru această înregistrare" msgstr "Nu s-au pus marcaje de montaj pentru această înregistrare"
msgid "No editing sequences defined!"
msgstr ""
msgid "Can't start editing process!" msgid "Can't start editing process!"
msgstr "Nu pot porni montajul înregistrării!" msgstr "Nu pot porni montajul înregistrării!"

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 1.6.0\n" "Project-Id-Version: VDR 1.6.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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" "PO-Revision-Date: 2008-12-15 14:37+0100\n"
"Last-Translator: Oleg Roitburd <oleg@roitburd.de>\n" "Last-Translator: Oleg Roitburd <oleg@roitburd.de>\n"
"Language-Team: Russian <vdr@linuxtv.org>\n" "Language-Team: Russian <vdr@linuxtv.org>\n"
@ -1214,6 +1214,9 @@ msgstr "
msgid "No editing marks defined!" msgid "No editing marks defined!"
msgstr "½Õ ×ÐÔÐÝë ÜÕâÚØ ÔÛï ÜÞÝâÐÖÐ!" msgstr "½Õ ×ÐÔÐÝë ÜÕâÚØ ÔÛï ÜÞÝâÐÖÐ!"
msgid "No editing sequences defined!"
msgstr ""
msgid "Can't start editing process!" msgid "Can't start editing process!"
msgstr "½ÕÒÞ×ÜÞÖÝÞ ÝÐçÐâì ÜÞÝâÐÖ ×ÐßØáØ!" msgstr "½ÕÒÞ×ÜÞÖÝÞ ÝÐçÐâì ÜÞÝâÐÖ ×ÐßØáØ!"

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 1.7.16\n" "Project-Id-Version: VDR 1.7.16\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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" "PO-Revision-Date: 2011-02-15 16:29+0100\n"
"Last-Translator: Milan Hrala <hrala.milan@gmail.com>\n" "Last-Translator: Milan Hrala <hrala.milan@gmail.com>\n"
"Language-Team: Slovak <vdr@linuxtv.org>\n" "Language-Team: Slovak <vdr@linuxtv.org>\n"
@ -1213,6 +1213,9 @@ msgstr "Skok: "
msgid "No editing marks defined!" msgid "No editing marks defined!"
msgstr "Nie sú urèené znaèky úprav!" msgstr "Nie sú urèené znaèky úprav!"
msgid "No editing sequences defined!"
msgstr ""
msgid "Can't start editing process!" msgid "Can't start editing process!"
msgstr "Nemô¾e zaèa» spracovanie úprav!" msgstr "Nemô¾e zaèa» spracovanie úprav!"

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 1.6.0\n" "Project-Id-Version: VDR 1.6.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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" "PO-Revision-Date: 2008-02-28 19:44+0100\n"
"Last-Translator: Matjaz Thaler <matjaz.thaler@guest.arnes.si>\n" "Last-Translator: Matjaz Thaler <matjaz.thaler@guest.arnes.si>\n"
"Language-Team: Slovenian <vdr@linuxtv.org>\n" "Language-Team: Slovenian <vdr@linuxtv.org>\n"
@ -1214,6 +1214,9 @@ msgstr "Sko
msgid "No editing marks defined!" msgid "No editing marks defined!"
msgstr "Nobena prekinitvena toèka ni definirana!" msgstr "Nobena prekinitvena toèka ni definirana!"
msgid "No editing sequences defined!"
msgstr ""
msgid "Can't start editing process!" msgid "Can't start editing process!"
msgstr "Ne morem zaèeti urejanja!" msgstr "Ne morem zaèeti urejanja!"

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 1.7.1\n" "Project-Id-Version: VDR 1.7.1\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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" "PO-Revision-Date: 2011-01-09 15:57+0100\n"
"Last-Translator: Milan Cvijanoviæ <elcom_cvijo@hotmail.com>\n" "Last-Translator: Milan Cvijanoviæ <elcom_cvijo@hotmail.com>\n"
"Language-Team: Serbian <vdr@linuxtv.org>\n" "Language-Team: Serbian <vdr@linuxtv.org>\n"
@ -1239,6 +1239,9 @@ msgstr "Sko
msgid "No editing marks defined!" msgid "No editing marks defined!"
msgstr "Nijedna taèka rezanja nije odreðena!" msgstr "Nijedna taèka rezanja nije odreðena!"
msgid "No editing sequences defined!"
msgstr ""
msgid "Can't start editing process!" msgid "Can't start editing process!"
msgstr "Ne mogu zapoèeti ureðivanje!" msgstr "Ne mogu zapoèeti ureðivanje!"

View File

@ -10,7 +10,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 1.6.0\n" "Project-Id-Version: VDR 1.6.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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" "PO-Revision-Date: 2008-03-12 18:25+0100\n"
"Last-Translator: Magnus Andersson <svankan@bahnhof.se>\n" "Last-Translator: Magnus Andersson <svankan@bahnhof.se>\n"
"Language-Team: Swedish <vdr@linuxtv.org>\n" "Language-Team: Swedish <vdr@linuxtv.org>\n"
@ -1216,6 +1216,9 @@ msgstr "Hopp: "
msgid "No editing marks defined!" msgid "No editing marks defined!"
msgstr "Det finns inga redigeringsmärken" msgstr "Det finns inga redigeringsmärken"
msgid "No editing sequences defined!"
msgstr ""
msgid "Can't start editing process!" msgid "Can't start editing process!"
msgstr "Kan inte starta redigering!" msgstr "Kan inte starta redigering!"

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 1.6.0\n" "Project-Id-Version: VDR 1.6.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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" "PO-Revision-Date: 2008-02-28 00:33+0100\n"
"Last-Translator: Oktay Yolgeçen <oktay_73@yahoo.de>\n" "Last-Translator: Oktay Yolgeçen <oktay_73@yahoo.de>\n"
"Language-Team: Turkish <vdr@linuxtv.org>\n" "Language-Team: Turkish <vdr@linuxtv.org>\n"
@ -1213,6 +1213,9 @@ msgstr "Atla: "
msgid "No editing marks defined!" msgid "No editing marks defined!"
msgstr "Kesim iþaretleri belirtilmemiþ!" msgstr "Kesim iþaretleri belirtilmemiþ!"
msgid "No editing sequences defined!"
msgstr ""
msgid "Can't start editing process!" msgid "Can't start editing process!"
msgstr "Kesim baþlatýlamýyor!" msgstr "Kesim baþlatýlamýyor!"

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 1.7.7\n" "Project-Id-Version: VDR 1.7.7\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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" "PO-Revision-Date: 2010-04-25 16:35+0200\n"
"Last-Translator: Yarema aka Knedlyk <yupadmin@gmail.com>\n" "Last-Translator: Yarema aka Knedlyk <yupadmin@gmail.com>\n"
"Language-Team: Ukrainian <vdr@linuxtv.org>\n" "Language-Team: Ukrainian <vdr@linuxtv.org>\n"
@ -1213,6 +1213,9 @@ msgstr "Перейти: "
msgid "No editing marks defined!" msgid "No editing marks defined!"
msgstr "Не задано міток для монтажу!" msgstr "Не задано міток для монтажу!"
msgid "No editing sequences defined!"
msgstr ""
msgid "Can't start editing process!" msgid "Can't start editing process!"
msgstr "Неможливо почати монтаж запису!" msgstr "Неможливо почати монтаж запису!"

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: VDR 1.6.0\n" "Project-Id-Version: VDR 1.6.0\n"
"Report-Msgid-Bugs-To: <vdr-bugs@tvdr.de>\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" "PO-Revision-Date: 2009-09-23 23:50+0800\n"
"Last-Translator: Nan Feng <nfgx@21cn.com>\n" "Last-Translator: Nan Feng <nfgx@21cn.com>\n"
"Language-Team: Chinese (simplified) <vdr@linuxtv.org>\n" "Language-Team: Chinese (simplified) <vdr@linuxtv.org>\n"
@ -1216,6 +1216,9 @@ msgstr "跳过: "
msgid "No editing marks defined!" msgid "No editing marks defined!"
msgstr "无编辑标记定义!" msgstr "无编辑标记定义!"
msgid "No editing sequences defined!"
msgstr ""
msgid "Can't start editing process!" msgid "Can't start editing process!"
msgstr "不能开始编辑处理" msgstr "不能开始编辑处理"

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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" #include "recording.h"
@ -1456,6 +1456,54 @@ cMark *cMarks::GetNext(int Position)
return NULL; 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 ------------------------------------------------- // --- cRecordingUserCommand -------------------------------------------------
const char *cRecordingUserCommand::command = NULL; const char *cRecordingUserCommand::command = NULL;

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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 #ifndef __RECORDING_H
@ -238,6 +238,19 @@ public:
cMark *Get(int Position); cMark *Get(int Position);
cMark *GetPrev(int Position); cMark *GetPrev(int Position);
cMark *GetNext(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" #define RUC_BEFORERECORDING "before"

112
remux.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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" #include "remux.h"
@ -114,6 +114,32 @@ void cRemux::SetBrokenLink(uchar *Data, int Length)
// --- Some TS handling tools ------------------------------------------------ // --- 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) int64_t TsGetPts(const uchar *p, int l)
{ {
// Find the first packet with a PTS and use it: // Find the first packet with a PTS and use it:
@ -127,25 +153,75 @@ int64_t TsGetPts(const uchar *p, int l)
return -1; return -1;
} }
void TsSetTeiOnBrokenPackets(uchar *p, int l) int64_t TsGetDts(const uchar *p, int l)
{ {
bool Processed[MAXPID] = { false }; // Find the first packet with a DTS and use it:
while (l >= TS_SIZE) { while (l > 0) {
if (*p != TS_SYNC_BYTE) const uchar *d = p;
break; if (TsPayloadStart(d) && TsGetPayload(&d) && PesHasDts(d))
int Pid = TsPid(p); return PesGetDts(d);
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);
}
}
l -= TS_SIZE;
p += TS_SIZE; 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 ------------------------------------------------------------
@ -1395,7 +1471,7 @@ int cFrameDetector::Analyze(const uchar *Data, int Length)
} }
} }
else // audio 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); dbgframes("\nDelta = %d FPS = %5.2f FPPU = %d NF = %d\n", Delta, framesPerSecond, framesPerPayloadUnit, numPtsValues + 1);
synced = true; synced = true;
parser->SetDebug(false); parser->SetDebug(false);

66
remux.h
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * 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 #ifndef __REMUX_H
@ -52,6 +52,11 @@ public:
#define PATPID 0x0000 // PAT PID (constant 0) #define PATPID 0x0000 // PAT PID (constant 0)
#define MAXPID 0x2000 // for arrays that use a PID as the index #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) inline bool TsHasPayload(const uchar *p)
{ {
return p[3] & TS_PAYLOAD_EXISTS; return p[3] & TS_PAYLOAD_EXISTS;
@ -82,6 +87,16 @@ inline bool TsIsScrambled(const uchar *p)
return p[3] & TS_SCRAMBLING_CONTROL; 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) inline int TsPayloadOffset(const uchar *p)
{ {
int o = TsHasAdaptationField(p) ? p[4] + 5 : 4; 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; 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. // The following functions all take a pointer to a sequence of complete TS packets.
int64_t TsGetPts(const uchar *p, int l); 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: // Some PES handling tools:
// The following functions that take a pointer to PES data all assume that // 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; 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) inline int64_t PesGetPts(const uchar *p)
{ {
return ((((int64_t)p[ 9]) & 0x0E) << 29) | return ((((int64_t)p[ 9]) & 0x0E) << 29) |
@ -151,6 +187,28 @@ inline int64_t PesGetPts(const uchar *p)
((((int64_t)p[13]) & 0xFE) >> 1); ((((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: // A transprent TS payload handler:
class cTsPayload { class cTsPayload {