mirror of
https://github.com/VDR4Arch/vdr.git
synced 2023-10-10 13:36:52 +02:00
Improved cutting MPEG-2 video
This commit is contained in:
parent
db98ecf54e
commit
abde1d04ae
@ -3024,6 +3024,7 @@ S
|
|||||||
for dropped B-frames
|
for dropped B-frames
|
||||||
for simplifying calculating the PTS offset in cPtsFixer::Fix() and fixing the overflow
|
for simplifying calculating the PTS offset in cPtsFixer::Fix() and fixing the overflow
|
||||||
handling of PCR values
|
handling of PCR values
|
||||||
|
for improving cutting MPEG-2 video
|
||||||
|
|
||||||
Peter Münster <pmlists@free.fr>
|
Peter Münster <pmlists@free.fr>
|
||||||
for fixing 'make install' to not overwrite existing configuration files
|
for fixing 'make install' to not overwrite existing configuration files
|
||||||
|
1
HISTORY
1
HISTORY
@ -7530,3 +7530,4 @@ Video Disk Recorder Revision History
|
|||||||
- The SVDRP command NEWT no longer checks whether a timer with the given data already
|
- The SVDRP command NEWT no longer checks whether a timer with the given data already
|
||||||
exists (suggested by Malte Forkel).
|
exists (suggested by Malte Forkel).
|
||||||
- Implemented scaling of SPU bitmaps (thanks to Johann Friedrichs).
|
- Implemented scaling of SPU bitmaps (thanks to Johann Friedrichs).
|
||||||
|
- Improved cutting MPEG-2 video (thanks to Sören Moch).
|
||||||
|
437
cutter.c
437
cutter.c
@ -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.21 2012/12/02 14:30:55 kls Exp $
|
* $Id: cutter.c 2.22 2013/01/20 12:04:07 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "cutter.h"
|
#include "cutter.h"
|
||||||
@ -104,167 +104,121 @@ void cPacketStorage::Flush(int Pid, uchar *Data, int &Length, int MaxLength)
|
|||||||
buffers[Pid]->Flush(Data, Length, MaxLength);
|
buffers[Pid]->Flush(Data, Length, MaxLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- cDanglingPacketStripper -----------------------------------------------
|
// --- cMpeg2Fixer --------------------------------------------------------
|
||||||
|
|
||||||
class cDanglingPacketStripper {
|
class cMpeg2Fixer : private cTsPayload {
|
||||||
private:
|
private:
|
||||||
bool processed[MAXPID];
|
bool FindHeader(uint32_t Code, const char *Header);
|
||||||
cPatPmtParser patPmtParser;
|
|
||||||
public:
|
public:
|
||||||
cDanglingPacketStripper(void);
|
cMpeg2Fixer(uchar *Data, int Length, int Vpid);
|
||||||
bool Process(uchar *Data, int Length, int64_t FirstPts);
|
void SetBrokenLink(void);
|
||||||
///< Scans the frame given in Data and hides the payloads of any TS packets
|
void SetClosedGop(void);
|
||||||
///< that either didn't start within this frame, or have a PTS that is
|
int GetTref(void);
|
||||||
///< before FirstPts. The TS packets in question are not physically removed
|
void AdjGopTime(int Offset, int FramesPerSecond);
|
||||||
///< from Data in order to keep any frame counts and PCR timestamps intact.
|
void AdjTref(int TrefOffset);
|
||||||
///< Returns true if any dangling packets have been found.
|
|
||||||
};
|
};
|
||||||
|
|
||||||
cDanglingPacketStripper::cDanglingPacketStripper(void)
|
cMpeg2Fixer::cMpeg2Fixer(uchar *Data, int Length, int Vpid)
|
||||||
{
|
{
|
||||||
memset(processed, 0x00, sizeof(processed));
|
// Go to first video packet:
|
||||||
}
|
for (; Length > 0; Data += TS_SIZE, Length -= TS_SIZE) {
|
||||||
|
if (TsPid(Data) == Vpid) {
|
||||||
bool cDanglingPacketStripper::Process(uchar *Data, int Length, int64_t FirstPts)
|
Setup(Data, Length, Vpid);
|
||||||
{
|
break;
|
||||||
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 (patPmtParser.IsPmtPid(Pid))
|
|
||||||
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;
|
|
||||||
|
bool cMpeg2Fixer::FindHeader(uint32_t Code, const char *Header)
|
||||||
|
{
|
||||||
|
Reset();
|
||||||
|
if (Find(Code))
|
||||||
|
return true;
|
||||||
|
esyslog("ERROR: %s header not found!", Header);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cMpeg2Fixer::SetBrokenLink(void)
|
||||||
|
{
|
||||||
|
if (!FindHeader(0x000001B8, "GOP"))
|
||||||
|
return;
|
||||||
|
SkipBytes(3);
|
||||||
|
uchar b = GetByte();
|
||||||
|
if (!(b & 0x40)) { // GOP not closed
|
||||||
|
b |= 0x20;
|
||||||
|
SetByte(b, GetLastIndex());
|
||||||
}
|
}
|
||||||
return Found;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- cPtsFixer -------------------------------------------------------------
|
void cMpeg2Fixer::SetClosedGop(void)
|
||||||
|
|
||||||
class cPtsFixer {
|
|
||||||
private:
|
|
||||||
int delta; // time between two frames
|
|
||||||
int64_t deltaDts; // the difference between two consecutive DTS values (may differ from 'delta' in case of multiple fields per frame)
|
|
||||||
int64_t lastPts; // the video PTS of the last frame (in display order)
|
|
||||||
int64_t lastDts; // the last video DTS value seen
|
|
||||||
int64_t offset; // offset to add to all timestamps
|
|
||||||
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;
|
if (!FindHeader(0x000001B8, "GOP"))
|
||||||
deltaDts = 0;
|
return;
|
||||||
lastPts = -1;
|
SkipBytes(3);
|
||||||
lastDts = -1;
|
uchar b = GetByte();
|
||||||
offset = -1;
|
b |= 0x40;
|
||||||
fixCounters = false;
|
SetByte(b, GetLastIndex());
|
||||||
memset(counter, 0x00, sizeof(counter));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void cPtsFixer::Setup(double FramesPerSecond)
|
int cMpeg2Fixer::GetTref(void)
|
||||||
{
|
{
|
||||||
delta = int(round(PTSTICKS / FramesPerSecond));
|
if (!FindHeader(0x00000100, "picture"))
|
||||||
|
return 0;
|
||||||
|
int Tref = GetByte() << 2;
|
||||||
|
Tref |= GetByte() >> 6;
|
||||||
|
return Tref;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cPtsFixer::Fix(uchar *Data, int Length, bool CutIn)
|
void cMpeg2Fixer::AdjGopTime(int Offset, int FramesPerSecond)
|
||||||
{
|
{
|
||||||
if (!patPmtParser.Vpid()) {
|
if (!FindHeader(0x000001B8, "GOP"))
|
||||||
if (!patPmtParser.ParsePatPmt(Data, Length))
|
return;
|
||||||
|
uchar Byte1 = GetByte();
|
||||||
|
int Index1 = GetLastIndex();
|
||||||
|
uchar Byte2 = GetByte();
|
||||||
|
int Index2 = GetLastIndex();
|
||||||
|
uchar Byte3 = GetByte();
|
||||||
|
int Index3 = GetLastIndex();
|
||||||
|
uchar Byte4 = GetByte();
|
||||||
|
int Index4 = GetLastIndex();
|
||||||
|
uchar Frame = ((Byte3 & 0x1F) << 1) | (Byte4 >> 7);
|
||||||
|
uchar Sec = ((Byte2 & 0x07) << 3) | (Byte3 >> 5);
|
||||||
|
uchar Min = ((Byte1 & 0x03) << 4) | (Byte2 >> 4);
|
||||||
|
uchar Hour = ((Byte1 & 0x7C) >> 2);
|
||||||
|
int GopTime = ((Hour * 60 + Min) * 60 + Sec) * FramesPerSecond + Frame;
|
||||||
|
if (GopTime) { // do not fix when zero
|
||||||
|
GopTime += Offset;
|
||||||
|
if (GopTime < 0)
|
||||||
|
GopTime += 24 * 60 * 60 * FramesPerSecond;
|
||||||
|
Frame = GopTime % FramesPerSecond;
|
||||||
|
GopTime = GopTime / FramesPerSecond;
|
||||||
|
Sec = GopTime % 60;
|
||||||
|
GopTime = GopTime / 60;
|
||||||
|
Min = GopTime % 60;
|
||||||
|
GopTime = GopTime / 60;
|
||||||
|
Hour = GopTime % 24;
|
||||||
|
SetByte((Byte1 & 0x80) | (Hour << 2) | (Min >> 4), Index1);
|
||||||
|
SetByte(((Min & 0x0F) << 4) | 0x08 | (Sec >> 3), Index2);
|
||||||
|
SetByte(((Sec & 0x07) << 3) | (Frame >> 1), Index3);
|
||||||
|
SetByte((Byte4 & 0x7F) | ((Frame & 0x01) << 7), Index4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cMpeg2Fixer::AdjTref(int TrefOffset)
|
||||||
|
{
|
||||||
|
Reset();
|
||||||
|
if (!Find(0x00000100)) {
|
||||||
|
esyslog("ERROR: Picture header not found!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Determine the PTS offset at the beginning of each sequence (except the first one):
|
int Tref = GetByte() << 2;
|
||||||
if (CutIn && lastPts >= 0) {
|
int Index1 = GetLastIndex();
|
||||||
int64_t Pts = TsGetPts(Data, Length);
|
uchar Byte2 = GetByte();
|
||||||
if (Pts >= 0)
|
int Index2 = GetLastIndex();
|
||||||
offset = (lastPts + delta - Pts) & MAX33BIT; // offset is calculated so that Pts + offset results in lastPts + delta
|
Tref |= Byte2 >> 6;
|
||||||
fixCounters = true;
|
Tref -= TrefOffset;
|
||||||
}
|
SetByte(Tref >> 2, Index1);
|
||||||
// Keep track of the highest video PTS:
|
SetByte((Tref << 6) | (Byte2 & 0x3F), Index2);
|
||||||
bool GotPts = false;
|
|
||||||
int64_t PrevDts = lastDts;
|
|
||||||
uchar *p = Data;
|
|
||||||
int len = Length;
|
|
||||||
while (len >= TS_SIZE && *p == TS_SYNC_BYTE) {
|
|
||||||
int Pid = TsPid(p);
|
|
||||||
if (Pid == patPmtParser.Vpid()) {
|
|
||||||
if (!GotPts) { // in case of multiple fields per frame, the offset is calculated only with the first one
|
|
||||||
int64_t Pts = TsGetPts(p, TS_SIZE);
|
|
||||||
if (Pts >= 0) {
|
|
||||||
if (offset >= 0)
|
|
||||||
Pts = PtsAdd(Pts, offset); // offset is taken into account here, to make lastPts have the "new" value already!
|
|
||||||
if (lastPts < 0 || PtsDiff(lastPts, Pts) > 0)
|
|
||||||
lastPts = Pts;
|
|
||||||
}
|
|
||||||
GotPts = true;
|
|
||||||
}
|
|
||||||
if (!CutIn) {
|
|
||||||
int64_t Dts = TsGetDts(p, TS_SIZE);
|
|
||||||
if (Dts >= 0) {
|
|
||||||
if (offset >= 0)
|
|
||||||
Dts = PtsAdd(Dts, offset); // offset is taken into account here, to make lastDts have the "new" value already!
|
|
||||||
deltaDts = PtsDiff(PrevDts, Dts);
|
|
||||||
PrevDts = Dts;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Adjust the TS continuity counter:
|
|
||||||
if (fixCounters) {
|
|
||||||
if (TsHasPayload(p))
|
|
||||||
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) {
|
|
||||||
if (CutIn) {
|
|
||||||
lastDts = PtsAdd(lastDts, deltaDts);
|
|
||||||
TsSetDts(p, TS_SIZE, lastDts);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lastDts = PrevDts;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- cCuttingThread --------------------------------------------------------
|
// --- cCuttingThread --------------------------------------------------------
|
||||||
@ -281,16 +235,27 @@ private:
|
|||||||
int numSequences;
|
int numSequences;
|
||||||
off_t maxVideoFileSize;
|
off_t maxVideoFileSize;
|
||||||
off_t fileSize;
|
off_t fileSize;
|
||||||
cPtsFixer ptsFixer;
|
|
||||||
bool suspensionLogged;
|
bool suspensionLogged;
|
||||||
|
int sequence; // cutting sequence
|
||||||
|
int delta; // time between two frames (PTS ticks)
|
||||||
|
int64_t lastVidPts; // the video PTS of the last frame (in display order)
|
||||||
|
bool multiFramePkt; // multiple frames within one PES packet
|
||||||
|
int64_t firstPts; // first valid PTS, for dangling packet stripping
|
||||||
|
int64_t offset; // offset to add to all timestamps
|
||||||
|
int tRefOffset; // number of stripped frames in GOP
|
||||||
|
uchar counter[MAXPID]; // the TS continuity counter for each PID
|
||||||
|
bool keepPkt[MAXPID]; // flag for each PID to keep packets, for dangling packet stripping
|
||||||
|
int numIFrames; // number of I-frames without pending packets
|
||||||
|
cPatPmtParser patPmtParser;
|
||||||
bool Throttled(void);
|
bool Throttled(void);
|
||||||
bool SwitchFile(bool Force = false);
|
bool SwitchFile(bool Force = false);
|
||||||
bool LoadFrame(int Index, uchar *Buffer, bool &Independent, int &Length);
|
bool LoadFrame(int Index, uchar *Buffer, bool &Independent, int &Length);
|
||||||
bool FramesAreEqual(int Index1, int Index2);
|
bool FramesAreEqual(int Index1, int Index2);
|
||||||
void GetPendingPackets(uchar *Buffer, int &Length, int Index, int64_t LastPts);
|
void GetPendingPackets(uchar *Buffer, int &Length, int Index);
|
||||||
// Gather all non-video TS packets from Index upward that either belong to
|
// 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,
|
// payloads that started before Index, or have a PTS that is before lastVidPts,
|
||||||
// and add them to the end of the given Data.
|
// and add them to the end of the given Data.
|
||||||
|
bool FixFrame(uchar *Data, int &Length, bool Independent, int Index, bool CutIn, bool CutOut);
|
||||||
bool ProcessSequence(int LastEndIndex, int BeginIndex, int EndIndex, int NextBeginIndex);
|
bool ProcessSequence(int LastEndIndex, int BeginIndex, int EndIndex, int NextBeginIndex);
|
||||||
protected:
|
protected:
|
||||||
virtual void Action(void);
|
virtual void Action(void);
|
||||||
@ -312,7 +277,15 @@ cCuttingThread::cCuttingThread(const char *FromFileName, const char *ToFileName)
|
|||||||
framesPerSecond = Recording.FramesPerSecond();
|
framesPerSecond = Recording.FramesPerSecond();
|
||||||
suspensionLogged = false;
|
suspensionLogged = false;
|
||||||
fileSize = 0;
|
fileSize = 0;
|
||||||
ptsFixer.Setup(framesPerSecond);
|
sequence = 0;
|
||||||
|
delta = int(round(PTSTICKS / framesPerSecond));
|
||||||
|
lastVidPts = -1;
|
||||||
|
multiFramePkt = false;
|
||||||
|
firstPts = -1;
|
||||||
|
offset = 0;
|
||||||
|
tRefOffset = 0;
|
||||||
|
memset(counter, 0x00, sizeof(counter));
|
||||||
|
numIFrames = 0;
|
||||||
if (fromMarks.Load(FromFileName, framesPerSecond, isPesRecording) && fromMarks.Count()) {
|
if (fromMarks.Load(FromFileName, framesPerSecond, isPesRecording) && fromMarks.Count()) {
|
||||||
numSequences = fromMarks.GetNumSequences();
|
numSequences = fromMarks.GetNumSequences();
|
||||||
if (numSequences > 0) {
|
if (numSequences > 0) {
|
||||||
@ -414,35 +387,28 @@ bool cCuttingThread::FramesAreEqual(int Index1, int Index2)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cCuttingThread::GetPendingPackets(uchar *Data, int &Length, int Index, int64_t LastPts)
|
void cCuttingThread::GetPendingPackets(uchar *Data, int &Length, int Index)
|
||||||
{
|
{
|
||||||
bool Processed[MAXPID] = { false };
|
bool Processed[MAXPID] = { false };
|
||||||
int NumIndependentFrames = 0;
|
|
||||||
cPatPmtParser PatPmtParser;
|
|
||||||
cPacketStorage PacketStorage;
|
cPacketStorage PacketStorage;
|
||||||
for (; NumIndependentFrames < 2; Index++) {
|
int64_t LastPts = lastVidPts + delta;// adding one frame length to fully cover the very last frame
|
||||||
|
Processed[patPmtParser.Vpid()] = true; // we only want non-video packets
|
||||||
|
for (int NumIndependentFrames = 0; NumIndependentFrames < 2; Index++) {
|
||||||
uchar Buffer[MAXFRAMESIZE];
|
uchar Buffer[MAXFRAMESIZE];
|
||||||
bool Independent;
|
bool Independent;
|
||||||
int len;
|
int len;
|
||||||
if (LoadFrame(Index, Buffer, Independent, len)) {
|
if (LoadFrame(Index, Buffer, Independent, len)) {
|
||||||
if (Independent)
|
if (Independent)
|
||||||
NumIndependentFrames++;
|
NumIndependentFrames++;
|
||||||
uchar *p = Buffer;
|
for (uchar *p = Buffer; len >= TS_SIZE && *p == TS_SYNC_BYTE; len -= TS_SIZE, p += TS_SIZE) {
|
||||||
while (len >= TS_SIZE && *p == TS_SYNC_BYTE) {
|
|
||||||
int Pid = TsPid(p);
|
int Pid = TsPid(p);
|
||||||
if (Pid == PATPID)
|
if (Pid != PATPID && !patPmtParser.IsPmtPid(Pid) && !Processed[Pid]) {
|
||||||
PatPmtParser.ParsePat(p, TS_SIZE);
|
|
||||||
else if (PatPmtParser.IsPmtPid(Pid)) {
|
|
||||||
PatPmtParser.ParsePmt(p, TS_SIZE);
|
|
||||||
Processed[PatPmtParser.Vpid()] = true; // we only want non-video packets
|
|
||||||
}
|
|
||||||
else if (!Processed[Pid]) {
|
|
||||||
int64_t Pts = TsGetPts(p, TS_SIZE);
|
int64_t Pts = TsGetPts(p, TS_SIZE);
|
||||||
if (Pts >= 0) {
|
if (Pts >= 0) {
|
||||||
int64_t d = PtsDiff(LastPts, Pts);
|
int64_t d = PtsDiff(LastPts, Pts);
|
||||||
if (d <= 0) // Pts is before or at LastPts
|
if (d < 0) // Pts is before LastPts
|
||||||
PacketStorage.Flush(Pid, Data, Length, MAXFRAMESIZE);
|
PacketStorage.Flush(Pid, Data, Length, MAXFRAMESIZE);
|
||||||
if (d >= 0) { // Pts is at or after LastPts
|
else { // Pts is at or after LastPts
|
||||||
NumIndependentFrames = 0; // we search until we find two consecutive I-frames without any more pending packets
|
NumIndependentFrames = 0; // we search until we find two consecutive I-frames without any more pending packets
|
||||||
Processed[Pid] = true;
|
Processed[Pid] = true;
|
||||||
}
|
}
|
||||||
@ -450,8 +416,6 @@ void cCuttingThread::GetPendingPackets(uchar *Data, int &Length, int Index, int6
|
|||||||
if (!Processed[Pid])
|
if (!Processed[Pid])
|
||||||
PacketStorage.Append(Pid, p, TS_SIZE);
|
PacketStorage.Append(Pid, p, TS_SIZE);
|
||||||
}
|
}
|
||||||
len -= TS_SIZE;
|
|
||||||
p += TS_SIZE;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -459,59 +423,140 @@ void cCuttingThread::GetPendingPackets(uchar *Data, int &Length, int Index, int6
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool cCuttingThread::FixFrame(uchar *Data, int &Length, bool Independent, int Index, bool CutIn, bool CutOut)
|
||||||
|
{
|
||||||
|
if (!patPmtParser.Vpid()) {
|
||||||
|
if (!patPmtParser.ParsePatPmt(Data, Length))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (CutIn) {
|
||||||
|
sequence++;
|
||||||
|
memset(keepPkt, false, sizeof(keepPkt));
|
||||||
|
numIFrames = 0;
|
||||||
|
firstPts = TsGetPts(Data, Length);
|
||||||
|
// Determine the PTS offset at the beginning of each sequence (except the first one):
|
||||||
|
if (sequence != 1) {
|
||||||
|
if (firstPts >= 0)
|
||||||
|
offset = (lastVidPts + delta - firstPts) & MAX33BIT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (CutOut)
|
||||||
|
GetPendingPackets(Data, Length, Index + 1);
|
||||||
|
if (Independent) {
|
||||||
|
numIFrames++;
|
||||||
|
tRefOffset = 0;
|
||||||
|
}
|
||||||
|
// Fix MPEG-2:
|
||||||
|
if (patPmtParser.Vtype() == 2) {
|
||||||
|
cMpeg2Fixer Mpeg2fixer(Data, Length, patPmtParser.Vpid());
|
||||||
|
if (CutIn) {
|
||||||
|
if (sequence == 1 || multiFramePkt)
|
||||||
|
Mpeg2fixer.SetBrokenLink();
|
||||||
|
else {
|
||||||
|
Mpeg2fixer.SetClosedGop();
|
||||||
|
tRefOffset = Mpeg2fixer.GetTref();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (tRefOffset)
|
||||||
|
Mpeg2fixer.AdjTref(tRefOffset);
|
||||||
|
if (sequence > 1 && Independent)
|
||||||
|
Mpeg2fixer.AdjGopTime((offset - MAX33BIT) / delta, round(framesPerSecond));
|
||||||
|
}
|
||||||
|
bool DeletedFrame = false;
|
||||||
|
bool GotVidPts = false;
|
||||||
|
bool StripVideo = sequence > 1 && tRefOffset;
|
||||||
|
uchar *p;
|
||||||
|
int len;
|
||||||
|
for (p = Data, len = Length; len >= TS_SIZE && *p == TS_SYNC_BYTE; p += TS_SIZE, len -= TS_SIZE) {
|
||||||
|
int Pid = TsPid(p);
|
||||||
|
// Strip dangling packets:
|
||||||
|
if (numIFrames < 2 && Pid != PATPID && !patPmtParser.IsPmtPid(Pid)) {
|
||||||
|
if (Pid != patPmtParser.Vpid() || StripVideo) {
|
||||||
|
int64_t Pts = TsGetPts(p, TS_SIZE);
|
||||||
|
if (Pts >= 0)
|
||||||
|
keepPkt[Pid] = PtsDiff(firstPts, Pts) >= 0; // Pts is at or after FirstPts
|
||||||
|
if (!keepPkt[Pid]) {
|
||||||
|
TsHidePayload(p);
|
||||||
|
numIFrames = 0; // we search until we find two consecutive I-frames without any more dangling packets
|
||||||
|
if (Pid == patPmtParser.Vpid())
|
||||||
|
DeletedFrame = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Adjust the TS continuity counter:
|
||||||
|
if (sequence > 1) {
|
||||||
|
if (TsHasPayload(p))
|
||||||
|
counter[Pid] = (counter[Pid] + 1) & TS_CONT_CNT_MASK;
|
||||||
|
TsSetContinuityCounter(p, counter[Pid]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
counter[Pid] = TsGetContinuityCounter(p); // collect initial counters
|
||||||
|
// Adjust PTS:
|
||||||
|
int64_t Pts = TsGetPts(p, TS_SIZE);
|
||||||
|
if (Pts >= 0) {
|
||||||
|
if (sequence > 1) {
|
||||||
|
Pts = PtsAdd(Pts, offset);
|
||||||
|
TsSetPts(p, TS_SIZE, Pts);
|
||||||
|
}
|
||||||
|
// Keep track of the highest video PTS - in case of multiple fields per frame, take the first one
|
||||||
|
if (!GotVidPts && Pid == patPmtParser.Vpid()) {
|
||||||
|
GotVidPts = true;
|
||||||
|
if (lastVidPts < 0 || PtsDiff(lastVidPts, Pts) > 0)
|
||||||
|
lastVidPts = Pts;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Adjust DTS:
|
||||||
|
if (sequence > 1) {
|
||||||
|
int64_t Dts = TsGetDts(p, TS_SIZE);
|
||||||
|
if (Dts >= 0) {
|
||||||
|
Dts = PtsAdd(Dts, offset);
|
||||||
|
if (CutIn)
|
||||||
|
Dts = PtsAdd(Dts, tRefOffset * delta);
|
||||||
|
TsSetDts(p, TS_SIZE, Dts);
|
||||||
|
}
|
||||||
|
int64_t Pcr = TsGetPcr(p);
|
||||||
|
if (Pcr >= 0) {
|
||||||
|
Pcr = Pcr + offset * PCRFACTOR;
|
||||||
|
if (Pcr > MAX27MHZ)
|
||||||
|
Pcr -= MAX27MHZ + 1;
|
||||||
|
TsSetPcr(p, Pcr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!DeletedFrame && !GotVidPts) {
|
||||||
|
// Adjust PTS for multiple frames within a single PES packet:
|
||||||
|
lastVidPts = (lastVidPts + delta) & MAX33BIT;
|
||||||
|
multiFramePkt = true;
|
||||||
|
}
|
||||||
|
return DeletedFrame;
|
||||||
|
}
|
||||||
|
|
||||||
bool cCuttingThread::ProcessSequence(int LastEndIndex, int BeginIndex, int EndIndex, int NextBeginIndex)
|
bool cCuttingThread::ProcessSequence(int LastEndIndex, int BeginIndex, int EndIndex, int NextBeginIndex)
|
||||||
{
|
{
|
||||||
// Check for seamless connections:
|
// Check for seamless connections:
|
||||||
bool SeamlessBegin = LastEndIndex >= 0 && FramesAreEqual(LastEndIndex, BeginIndex);
|
bool SeamlessBegin = LastEndIndex >= 0 && FramesAreEqual(LastEndIndex, BeginIndex);
|
||||||
bool SeamlessEnd = NextBeginIndex >= 0 && FramesAreEqual(EndIndex, NextBeginIndex);
|
bool SeamlessEnd = NextBeginIndex >= 0 && FramesAreEqual(EndIndex, NextBeginIndex);
|
||||||
// Process all frames from BeginIndex (included) to EndIndex (excluded):
|
// 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++) {
|
for (int Index = BeginIndex; Running() && Index < EndIndex; Index++) {
|
||||||
uchar Buffer[MAXFRAMESIZE];
|
uchar Buffer[MAXFRAMESIZE];
|
||||||
bool Independent;
|
bool Independent;
|
||||||
int Length;
|
int Length;
|
||||||
if (LoadFrame(Index, Buffer, Independent, Length)) {
|
if (LoadFrame(Index, Buffer, Independent, Length)) {
|
||||||
|
bool CutIn = !SeamlessBegin && Index == BeginIndex;
|
||||||
|
bool CutOut = !SeamlessEnd && Index == EndIndex - 1;
|
||||||
|
bool DeletedFrame = false;
|
||||||
if (!isPesRecording) {
|
if (!isPesRecording) {
|
||||||
int64_t Pts = TsGetPts(Buffer, Length);
|
DeletedFrame = FixFrame(Buffer, Length, Independent, Index, CutIn, CutOut);
|
||||||
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:
|
else if (CutIn)
|
||||||
if (!SeamlessBegin) {
|
|
||||||
if (isPesRecording) {
|
|
||||||
if (Index == BeginIndex)
|
|
||||||
cRemux::SetBrokenLink(Buffer, Length);
|
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:
|
// Every file shall start with an independent frame:
|
||||||
if (Independent) {
|
if (Independent) {
|
||||||
NumIndependentFrames++;
|
|
||||||
if (!SwitchFile())
|
if (!SwitchFile())
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Write index:
|
// Write index:
|
||||||
if (!toIndex->Write(Independent, toFileName->Number(), fileSize)) {
|
if (!DeletedFrame && !toIndex->Write(Independent, toFileName->Number(), fileSize)) {
|
||||||
error = "toIndex";
|
error = "toIndex";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
4
remux.h
4
remux.h
@ -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.36 2012/11/19 10:22:28 kls Exp $
|
* $Id: remux.h 2.37 2013/01/20 11:43:59 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __REMUX_H
|
#ifndef __REMUX_H
|
||||||
@ -217,6 +217,8 @@ private:
|
|||||||
int length;
|
int length;
|
||||||
int pid;
|
int pid;
|
||||||
int index; // points to the next byte to process
|
int index; // points to the next byte to process
|
||||||
|
protected:
|
||||||
|
void Reset(void) { index = 0; }
|
||||||
public:
|
public:
|
||||||
cTsPayload(void);
|
cTsPayload(void);
|
||||||
cTsPayload(uchar *Data, int Length, int Pid = -1);
|
cTsPayload(uchar *Data, int Length, int Pid = -1);
|
||||||
|
Loading…
Reference in New Issue
Block a user