summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'media-video/vdr/files/vdr-2.6.1_naludump.patch')
-rw-r--r--media-video/vdr/files/vdr-2.6.1_naludump.patch598
1 files changed, 598 insertions, 0 deletions
diff --git a/media-video/vdr/files/vdr-2.6.1_naludump.patch b/media-video/vdr/files/vdr-2.6.1_naludump.patch
new file mode 100644
index 000000000000..efea3a1d74f7
--- /dev/null
+++ b/media-video/vdr/files/vdr-2.6.1_naludump.patch
@@ -0,0 +1,598 @@
+diff -a -U 2 -r a/config.c b/config.c
+--- a/config.c
++++ b/config.c
+@@ -469,4 +469,5 @@
+ SplitEditedFiles = 0;
+ DelTimeshiftRec = 0;
++ DumpNaluFill = 0;
+ MinEventTimeout = 30;
+ MinUserInactivity = 300;
+@@ -697,4 +698,5 @@
+ else if (!strcasecmp(Name, "SplitEditedFiles")) SplitEditedFiles = atoi(Value);
+ else if (!strcasecmp(Name, "DelTimeshiftRec")) DelTimeshiftRec = atoi(Value);
++ else if (!strcasecmp(Name, "DumpNaluFill")) DumpNaluFill = atoi(Value);
+ else if (!strcasecmp(Name, "MinEventTimeout")) MinEventTimeout = atoi(Value);
+ else if (!strcasecmp(Name, "MinUserInactivity")) MinUserInactivity = atoi(Value);
+@@ -829,4 +831,5 @@
+ Store("SplitEditedFiles", SplitEditedFiles);
+ Store("DelTimeshiftRec", DelTimeshiftRec);
++ Store("DumpNaluFill", DumpNaluFill);
+ Store("MinEventTimeout", MinEventTimeout);
+ Store("MinUserInactivity", MinUserInactivity);
+diff -a -U 2 -r a/config.h b/config.h
+--- a/config.h
++++ b/config.h
+@@ -341,4 +341,5 @@
+ int SplitEditedFiles;
+ int DelTimeshiftRec;
++ int DumpNaluFill;
+ int MinEventTimeout, MinUserInactivity;
+ time_t NextWakeupTime;
+diff -a -U 2 -r a/menu.c b/menu.c
+--- a/menu.c
++++ b/menu.c
+@@ -4185,4 +4185,5 @@
+ Add(new cMenuEditBoolItem(tr("Setup.Recording$Split edited files"), &data.SplitEditedFiles));
+ Add(new cMenuEditStraItem(tr("Setup.Recording$Delete timeshift recording"),&data.DelTimeshiftRec, 3, delTimeshiftRecTexts));
++ Add(new cMenuEditBoolItem(tr("Setup.Recording$Dump NALU Fill data"), &data.DumpNaluFill));
+ }
+
+diff -a -U 2 -r a/recorder.c b/recorder.c
+--- a/recorder.c
++++ b/recorder.c
+@@ -195,4 +195,12 @@
+ }
+ frameDetector = new cFrameDetector(Pid, Type);
++ if ( Type == 0x1B // MPEG4 video
++ && (Setup.DumpNaluFill ? (strstr(FileName, "NALUKEEP") == NULL) : (strstr(FileName, "NALUDUMP") != NULL))) { // MPEG4
++ isyslog("Starting NALU fill dumper");
++ naluStreamProcessor = new cNaluStreamProcessor();
++ naluStreamProcessor->SetPid(Pid);
++ }
++ else
++ naluStreamProcessor = NULL;
+ index = NULL;
+ fileSize = 0;
+@@ -217,4 +225,10 @@
+ {
+ Detach();
++ if (naluStreamProcessor) {
++ long long int TotalPackets = naluStreamProcessor->GetTotalPackets();
++ long long int DroppedPackets = naluStreamProcessor->GetDroppedPackets();
++ isyslog("NALU fill dumper: %lld of %lld packets dropped, %lli%%", DroppedPackets, TotalPackets, TotalPackets ? DroppedPackets*100/TotalPackets : 0);
++ delete naluStreamProcessor;
++ }
+ delete index;
+ delete fileName;
+@@ -357,10 +371,32 @@
+ t.Set(MAXBROKENTIMEOUT);
+ }
+- if (recordFile->Write(b, Count) < 0) {
+- LOG_ERROR_STR(fileName->Name());
+- break;
++ if (naluStreamProcessor) {
++ naluStreamProcessor->PutBuffer(b, Count);
++ bool Fail = false;
++ while (true) {
++ int OutLength = 0;
++ uchar *OutData = naluStreamProcessor->GetBuffer(OutLength);
++ if (!OutData || OutLength <= 0)
++ break;
++ if (recordFile->Write(OutData, OutLength) < 0) {
++ LOG_ERROR_STR(fileName->Name());
++ Fail = true;
++ break;
++ }
++ HandleErrors();
++ fileSize += OutLength;
++ }
++ if (Fail)
++ break;
++ }
++ else {
++ if (recordFile->Write(b, Count) < 0) {
++ LOG_ERROR_STR(fileName->Name());
++ break;
++ }
++ HandleErrors();
++ fileSize += Count;
+ }
+- HandleErrors();
+- fileSize += Count;
++
+ }
+ }
+diff -a -U 2 -r a/recorder.h b/recorder.h
+--- a/recorder.h
++++ b/recorder.h C2022-01-08 17:51:00.397595525 +0100
+@@ -27,4 +27,5 @@
+ cFrameDetector *frameDetector;
+ cPatPmtGenerator patPmtGenerator;
++ cNaluStreamProcessor *naluStreamProcessor;
+ cFileName *fileName;
+ cRecordingInfo *recordingInfo;
+diff -a -U 2 -r a/remux.c b/remux.c
+--- a/remux.c
++++ b/remux.c
+@@ -357,4 +357,40 @@
+ }
+
++void TsExtendAdaptionField(unsigned char *Packet, int ToLength)
++{
++ // Hint: ExtenAdaptionField(p, TsPayloadOffset(p) - 4) is a null operation
++
++ int Offset = TsPayloadOffset(Packet); // First byte after existing adaption field
++
++ if (ToLength <= 0)
++ {
++ // Remove adaption field
++ Packet[3] = Packet[3] & ~TS_ADAPT_FIELD_EXISTS;
++ return;
++ }
++
++ // Set adaption field present
++ Packet[3] = Packet[3] | TS_ADAPT_FIELD_EXISTS;
++
++ // Set new length of adaption field:
++ Packet[4] = ToLength <= TS_SIZE-4 ? ToLength-1 : TS_SIZE-4-1;
++
++ if (Packet[4] == TS_SIZE-4-1)
++ {
++ // No more payload, remove payload flag
++ Packet[3] = Packet[3] & ~TS_PAYLOAD_EXISTS;
++ }
++
++ int NewPayload = TsPayloadOffset(Packet); // First byte after new adaption field
++
++ // Fill new adaption field
++ if (Offset == 4 && Offset < NewPayload)
++ Offset++; // skip adaptation_field_length
++ if (Offset == 5 && Offset < NewPayload)
++ Packet[Offset++] = 0; // various flags set to 0
++ while (Offset < NewPayload)
++ Packet[Offset++] = 0xff; // stuffing byte
++}
++
+ // --- cPatPmtGenerator ------------------------------------------------------
+
+@@ -1765,2 +1801,343 @@
+ return Processed;
+ }
++
++// --- cNaluDumper ---------------------------------------------------------
++
++cNaluDumper::cNaluDumper()
++{
++ LastContinuityOutput = -1;
++ reset();
++}
++
++void cNaluDumper::reset()
++{
++ LastContinuityInput = -1;
++ ContinuityOffset = 0;
++ PesId = -1;
++ PesOffset = 0;
++ NaluFillState = NALU_NONE;
++ NaluOffset = 0;
++ History = 0xffffffff;
++ DropAllPayload = false;
++}
++
++void cNaluDumper::ProcessPayload(unsigned char *Payload, int size, bool PayloadStart, sPayloadInfo &Info)
++{
++ Info.DropPayloadStartBytes = 0;
++ Info.DropPayloadEndBytes = 0;
++ int LastKeepByte = -1;
++
++ if (PayloadStart)
++ {
++ History = 0xffffffff;
++ PesId = -1;
++ NaluFillState = NALU_NONE;
++ }
++
++ for (int i=0; i<size; i++) {
++ History = (History << 8) | Payload[i];
++
++ PesOffset++;
++ NaluOffset++;
++
++ bool DropByte = false;
++
++ if (History >= 0x00000180 && History <= 0x000001FF)
++ {
++ // Start of PES packet
++ PesId = History & 0xff;
++ PesOffset = 0;
++ NaluFillState = NALU_NONE;
++ }
++ else if (PesId >= 0xe0 && PesId <= 0xef // video stream
++ && History >= 0x00000100 && History <= 0x0000017F) // NALU start code
++ {
++ int NaluId = History & 0xff;
++ NaluOffset = 0;
++ NaluFillState = ((NaluId & 0x1f) == 0x0c) ? NALU_FILL : NALU_NONE;
++ }
++
++ if (PesId >= 0xe0 && PesId <= 0xef // video stream
++ && PesOffset >= 1 && PesOffset <= 2)
++ {
++ Payload[i] = 0; // Zero out PES length field
++ }
++
++ if (NaluFillState == NALU_FILL && NaluOffset > 0) // Within NALU fill data
++ {
++ // We expect a series of 0xff bytes terminated by a single 0x80 byte.
++
++ if (Payload[i] == 0xFF)
++ {
++ DropByte = true;
++ }
++ else if (Payload[i] == 0x80)
++ {
++ NaluFillState = NALU_TERM; // Last byte of NALU fill, next byte sets NaluFillEnd=true
++ DropByte = true;
++ }
++ else // Invalid NALU fill
++ {
++ dsyslog("cNaluDumper: Unexpected NALU fill data: %02x", Payload[i]);
++ NaluFillState = NALU_END;
++ if (LastKeepByte == -1)
++ {
++ // Nalu fill from beginning of packet until last byte
++ // packet start needs to be dropped
++ Info.DropPayloadStartBytes = i;
++ }
++ }
++ }
++ else if (NaluFillState == NALU_TERM) // Within NALU fill data
++ {
++ // We are after the terminating 0x80 byte
++ NaluFillState = NALU_END;
++ if (LastKeepByte == -1)
++ {
++ // Nalu fill from beginning of packet until last byte
++ // packet start needs to be dropped
++ Info.DropPayloadStartBytes = i;
++ }
++ }
++
++ if (!DropByte)
++ LastKeepByte = i; // Last useful byte
++ }
++
++ Info.DropAllPayloadBytes = (LastKeepByte == -1);
++ Info.DropPayloadEndBytes = size-1-LastKeepByte;
++}
++
++bool cNaluDumper::ProcessTSPacket(unsigned char *Packet)
++{
++ bool HasAdaption = TsHasAdaptationField(Packet);
++ bool HasPayload = TsHasPayload(Packet);
++
++ // Check continuity:
++ int ContinuityInput = TsContinuityCounter(Packet);
++ if (LastContinuityInput >= 0)
++ {
++ int NewContinuityInput = HasPayload ? (LastContinuityInput + 1) & TS_CONT_CNT_MASK : LastContinuityInput;
++ int Offset = (NewContinuityInput - ContinuityInput) & TS_CONT_CNT_MASK;
++ if (Offset > 0)
++ dsyslog("cNaluDumper: TS continuity offset %i", Offset);
++ if (Offset > ContinuityOffset)
++ ContinuityOffset = Offset; // max if packets get dropped, otherwise always the current one.
++ }
++ LastContinuityInput = ContinuityInput;
++
++ if (HasPayload) {
++ sPayloadInfo Info;
++ int Offset = TsPayloadOffset(Packet);
++ ProcessPayload(Packet + Offset, TS_SIZE - Offset, TsPayloadStart(Packet), Info);
++
++ if (DropAllPayload && !Info.DropAllPayloadBytes)
++ {
++ // Return from drop packet mode to normal mode
++ DropAllPayload = false;
++
++ // Does the packet start with some remaining NALU fill data?
++ if (Info.DropPayloadStartBytes > 0)
++ {
++ // Add these bytes as stuffing to the adaption field.
++
++ // Sample payload layout:
++ // FF FF FF FF FF 80 00 00 01 xx xx xx xx
++ // ^DropPayloadStartBytes
++
++ TsExtendAdaptionField(Packet, Offset - 4 + Info.DropPayloadStartBytes);
++ }
++ }
++
++ bool DropThisPayload = DropAllPayload;
++
++ if (!DropAllPayload && Info.DropPayloadEndBytes > 0) // Payload ends with 0xff NALU Fill
++ {
++ // Last packet of useful data
++ // Do early termination of NALU fill data
++ Packet[TS_SIZE-1] = 0x80;
++ DropAllPayload = true;
++ // Drop all packets AFTER this one
++
++ // Since we already wrote the 0x80, we have to make sure that
++ // as soon as we stop dropping packets, any beginning NALU fill of next
++ // packet gets dumped. (see DropPayloadStartBytes above)
++ }
++
++ if (DropThisPayload && HasAdaption)
++ {
++ // Drop payload data, but keep adaption field data
++ TsExtendAdaptionField(Packet, TS_SIZE-4);
++ DropThisPayload = false;
++ }
++
++ if (DropThisPayload)
++ {
++ return true; // Drop packet
++ }
++ }
++
++ // Fix Continuity Counter and reproduce incoming offsets:
++ int NewContinuityOutput = TsHasPayload(Packet) ? (LastContinuityOutput + 1) & TS_CONT_CNT_MASK : LastContinuityOutput;
++ NewContinuityOutput = (NewContinuityOutput + ContinuityOffset) & TS_CONT_CNT_MASK;
++ TsSetContinuityCounter(Packet, NewContinuityOutput);
++ LastContinuityOutput = NewContinuityOutput;
++ ContinuityOffset = 0;
++
++ return false; // Keep packet
++}
++
++// --- cNaluStreamProcessor ---------------------------------------------------------
++
++cNaluStreamProcessor::cNaluStreamProcessor()
++{
++ pPatPmtParser = NULL;
++ vpid = -1;
++ data = NULL;
++ length = 0;
++ tempLength = 0;
++ tempLengthAtEnd = false;
++ TotalPackets = 0;
++ DroppedPackets = 0;
++}
++
++void cNaluStreamProcessor::PutBuffer(uchar *Data, int Length)
++{
++ if (length > 0)
++ esyslog("cNaluStreamProcessor::PutBuffer: New data before old data was processed!");
++
++ data = Data;
++ length = Length;
++}
++
++uchar* cNaluStreamProcessor::GetBuffer(int &OutLength)
++{
++ if (length <= 0)
++ {
++ // Need more data - quick exit
++ OutLength = 0;
++ return NULL;
++ }
++ if (tempLength > 0) // Data in temp buffer?
++ {
++ if (tempLengthAtEnd) // Data is at end, copy to beginning
++ {
++ // Overlapping src and dst!
++ for (int i=0; i<tempLength; i++)
++ tempBuffer[i] = tempBuffer[TS_SIZE-tempLength+i];
++ }
++ // Normalize TempBuffer fill
++ if (tempLength < TS_SIZE && length > 0)
++ {
++ int Size = min(TS_SIZE-tempLength, length);
++ memcpy(tempBuffer+tempLength, data, Size);
++ data += Size;
++ length -= Size;
++ tempLength += Size;
++ }
++ if (tempLength < TS_SIZE)
++ {
++ // All incoming data buffered, but need more data
++ tempLengthAtEnd = false;
++ OutLength = 0;
++ return NULL;
++ }
++ // Now: TempLength==TS_SIZE
++ if (tempBuffer[0] != TS_SYNC_BYTE)
++ {
++ // Need to sync on TS within temp buffer
++ int Skipped = 1;
++ while (Skipped < TS_SIZE && (tempBuffer[Skipped] != TS_SYNC_BYTE || (Skipped < length && data[Skipped] != TS_SYNC_BYTE)))
++ Skipped++;
++ esyslog("ERROR: skipped %d bytes to sync on start of TS packet", Skipped);
++ // Pass through skipped bytes
++ tempLengthAtEnd = true;
++ tempLength = TS_SIZE - Skipped; // may be 0, thats ok
++ OutLength = Skipped;
++ return tempBuffer;
++ }
++ // Now: TempBuffer is a TS packet
++ int Pid = TsPid(tempBuffer);
++ if (pPatPmtParser)
++ {
++ if (Pid == 0)
++ pPatPmtParser->ParsePat(tempBuffer, TS_SIZE);
++ else if (pPatPmtParser->IsPmtPid(Pid))
++ pPatPmtParser->ParsePmt(tempBuffer, TS_SIZE);
++ }
++
++ TotalPackets++;
++ bool Drop = false;
++ if (Pid == vpid || (pPatPmtParser && Pid == pPatPmtParser->Vpid() && pPatPmtParser->Vtype() == 0x1B))
++ Drop = NaluDumper.ProcessTSPacket(tempBuffer);
++ if (!Drop)
++ {
++ // Keep this packet, then continue with new data
++ tempLength = 0;
++ OutLength = TS_SIZE;
++ return tempBuffer;
++ }
++ // Drop TempBuffer
++ DroppedPackets++;
++ tempLength = 0;
++ }
++ // Now: TempLength==0, just process data/length
++
++ // Pointer to processed data / length:
++ uchar *Out = data;
++ uchar *OutEnd = Out;
++
++ while (length >= TS_SIZE)
++ {
++ if (data[0] != TS_SYNC_BYTE) {
++ int Skipped = 1;
++ while (Skipped < length && (data[Skipped] != TS_SYNC_BYTE || (length - Skipped > TS_SIZE && data[Skipped + TS_SIZE] != TS_SYNC_BYTE)))
++ Skipped++;
++ esyslog("ERROR: skipped %d bytes to sync on start of TS packet", Skipped);
++
++ // Pass through skipped bytes
++ if (OutEnd != data)
++ memcpy(OutEnd, data, Skipped);
++ OutEnd += Skipped;
++ continue;
++ }
++ // Now: Data starts with complete TS packet
++
++ int Pid = TsPid(data);
++ if (pPatPmtParser)
++ {
++ if (Pid == 0)
++ pPatPmtParser->ParsePat(data, TS_SIZE);
++ else if (pPatPmtParser->IsPmtPid(Pid))
++ pPatPmtParser->ParsePmt(data, TS_SIZE);
++ }
++
++ TotalPackets++;
++ bool Drop = false;
++ if (Pid == vpid || (pPatPmtParser && Pid == pPatPmtParser->Vpid() && pPatPmtParser->Vtype() == 0x1B))
++ Drop = NaluDumper.ProcessTSPacket(data);
++ if (!Drop)
++ {
++ if (OutEnd != data)
++ memcpy(OutEnd, data, TS_SIZE);
++ OutEnd += TS_SIZE;
++ }
++ else
++ {
++ DroppedPackets++;
++ }
++ data += TS_SIZE;
++ length -= TS_SIZE;
++ }
++ // Now: Less than a packet remains.
++ if (length > 0)
++ {
++ // copy remains into temp buffer
++ memcpy(tempBuffer, data, length);
++ tempLength = length;
++ tempLengthAtEnd = false;
++ length = 0;
++ }
++ OutLength = (OutEnd - Out);
++ return OutLength > 0 ? Out : NULL;
++}
+diff -a -U 2 -r a/remux.h b/remux.h
+--- a/remux.h
++++ b/remux.h
+@@ -65,4 +65,9 @@
+ }
+
++inline bool TsSetPayload(const uchar *p)
++{
++ return p[3] & TS_PAYLOAD_EXISTS;
++}
++
+ inline bool TsHasAdaptationField(const uchar *p)
+ {
+@@ -156,4 +161,5 @@
+ void TsSetPts(uchar *p, int l, int64_t Pts);
+ void TsSetDts(uchar *p, int l, int64_t Dts);
++void TsExtendAdaptionField(unsigned char *Packet, int ToLength);
+
+ // Some PES handling tools:
+@@ -550,3 +556,77 @@
+ };
+
++
++#define PATCH_NALUDUMP 100
++
++class cNaluDumper {
++ unsigned int History;
++
++ int LastContinuityInput;
++ int LastContinuityOutput;
++ int ContinuityOffset;
++
++ bool DropAllPayload;
++
++ int PesId;
++ int PesOffset;
++
++ int NaluOffset;
++
++ enum eNaluFillState {
++ NALU_NONE=0, // currently not NALU fill stream
++ NALU_FILL, // Within NALU fill stream, 0xff bytes and NALU start code in byte 0
++ NALU_TERM, // Within NALU fill stream, read 0x80 terminating byte
++ NALU_END // Beyond end of NALU fill stream, expecting 0x00 0x00 0x01 now
++ };
++
++ eNaluFillState NaluFillState;
++
++ struct sPayloadInfo {
++ int DropPayloadStartBytes;
++ int DropPayloadEndBytes;
++ bool DropAllPayloadBytes;
++ };
++
++public:
++ cNaluDumper();
++
++ void reset();
++
++ // Single packet interface:
++ bool ProcessTSPacket(unsigned char *Packet);
++
++private:
++ void ProcessPayload(unsigned char *Payload, int size, bool PayloadStart, sPayloadInfo &Info);
++};
++
++class cNaluStreamProcessor {
++ //Buffer stream interface:
++ int vpid;
++ uchar *data;
++ int length;
++ uchar tempBuffer[TS_SIZE];
++ int tempLength;
++ bool tempLengthAtEnd;
++ cPatPmtParser *pPatPmtParser;
++ cNaluDumper NaluDumper;
++
++ long long int TotalPackets;
++ long long int DroppedPackets;
++public:
++ cNaluStreamProcessor();
++
++ void SetPid(int VPid) { vpid = VPid; }
++ void SetPatPmtParser(cPatPmtParser *_pPatPmtParser) { pPatPmtParser = _pPatPmtParser; }
++ // Set either a PID or set a pointer to an PatPmtParser that will detect _one_ PID
++
++ void PutBuffer(uchar *Data, int Length);
++ // Add new data to be processed. Data must be valid until Get() returns NULL.
++ uchar* GetBuffer(int &OutLength);
++ // Returns filtered data, or NULL/0 to indicate that all data from Put() was processed
++ // or buffered.
++
++ long long int GetTotalPackets() { return TotalPackets; }
++ long long int GetDroppedPackets() { return DroppedPackets; }
++};
++
+ #endif // __REMUX_H