diff options
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.patch | 598 |
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 |