summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoretobi <git@e-tobi.net>2011-10-02 21:10:29 (GMT)
committeretobi <git@e-tobi.net>2011-10-02 21:10:29 (GMT)
commit0e7880576c0dcf84eaa53cd9c73bc2b5bf1e8d01 (patch)
tree1f4e839ce18099b540143bfacfe1cedc537b48d6
downloadvdr-checkts-master.tar.gz
vdr-checkts-master.tar.bz2
Initial commitHEAD0.1master
-rw-r--r--.gitignore2
-rw-r--r--COPYRIGHT29
-rw-r--r--Makefile16
-rw-r--r--README37
-rw-r--r--patch/0001-Record-TS-continuity-errors.patch190
-rw-r--r--tsstreamerrorcounter.cc44
-rw-r--r--tsstreamerrorcounter.h31
-rw-r--r--vdr-checkts.cc91
8 files changed, 440 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b546d6f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+*.o
+vdr-checkts
diff --git a/COPYRIGHT b/COPYRIGHT
new file mode 100644
index 0000000..9b9da23
--- /dev/null
+++ b/COPYRIGHT
@@ -0,0 +1,29 @@
+Copyright 2011 Tobias Grimm <vdr@e-tobi.net> All rights reserved.
+
+Software License Agreement (BSD License)
+
+Redistribution and use of this software in source and binary forms, with or
+without modification, are permitted provided that the following conditions
+are met:
+
+ * Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ * Neither the name of the author/copyright holder nor the names of contributors may
+ be used to endorse or promote products derived from this software without specific
+ prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..ee08a42
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,16 @@
+SOURCES=vdr-checkts.cc tsstreamerrorcounter.cc
+OBJECTS=$(SOURCES:.cc=.o)
+EXECUTABLE=vdr-checkts
+CC=g++
+CFLAGS=-c -Wall
+
+all: $(SOURCES) $(EXECUTABLE)
+
+$(EXECUTABLE): $(OBJECTS)
+ $(CC) $(LDFLAGS) $(OBJECTS) -o $@
+
+.cpp.o:
+ $(CC) $(CFLAGS) $< -o $@
+
+clean:
+ rm -rf *.o $(EXECUTABLE)
diff --git a/README b/README
new file mode 100644
index 0000000..45f2f08
--- /dev/null
+++ b/README
@@ -0,0 +1,37 @@
+vdr-checkts
+===========
+
+Copyright 2011 Tobias Grimm <vdr@e-tobi.net>
+
+vdr-checkts is released under the terms of the BSD License - see COPYRIGHT
+
+
+What's this about?
+------------------
+
+vdr-checkts parses VDR recordings (only TS-recordings / vdr 1.7.x) and
+records any discontinuities in the transport stream by checking the
+continuity counter.
+
+Ideally there will be no discontinuities and vdr-checkts returns with
+exit code 0. If continuity errors were found it returns with exit code 1
+and prints the number of errors.
+
+
+How to use?
+-----------
+
+Usage: vdr-checkts [OPTIONS] RECORDING_DIR
+
+ -m errors, --max-errors=ERROR Stop scanning if error count reaches ERROR
+ -h, --help print this help and exit
+
+
+What else?
+----------
+
+I've also include a patch for VDR 1.7.21 which will make VDR check for
+continuity errors while recording.
+
+The number of continuity errors is written to the info file in the field
+tagged with 'R'.
diff --git a/patch/0001-Record-TS-continuity-errors.patch b/patch/0001-Record-TS-continuity-errors.patch
new file mode 100644
index 0000000..c49cf5c
--- /dev/null
+++ b/patch/0001-Record-TS-continuity-errors.patch
@@ -0,0 +1,190 @@
+From 2e70ffdb5e8a7e06d48a5426c798fdae6df5abcf Mon Sep 17 00:00:00 2001
+From: etobi <git@e-tobi.net>
+Date: Sun, 2 Oct 2011 20:27:37 +0200
+Subject: [PATCH] Record TS continuity errors
+
+---
+ recorder.c | 11 +++++++++++
+ recording.c | 9 +++++++++
+ recording.h | 3 +++
+ remux.c | 41 +++++++++++++++++++++++++++++++++++++++++
+ remux.h | 25 +++++++++++++++++++++++++
+ 5 files changed, 89 insertions(+), 0 deletions(-)
+
+diff --git a/recorder.c b/recorder.c
+index a6cab47..182fb09 100644
+--- a/recorder.c
++++ b/recorder.c
+@@ -119,6 +119,7 @@ void cRecorder::Action(void)
+ time_t t = time(NULL);
+ bool InfoWritten = false;
+ bool FirstIframeSeen = false;
++ cTsStreamErrorCounter TsStreamErrorCounter;
+ while (Running()) {
+ int r;
+ uchar *b = ringBuffer->Get(r);
+@@ -154,6 +155,16 @@ void cRecorder::Action(void)
+ fileSize += TS_SIZE;
+ }
+ }
++ TsStreamErrorCounter.CheckTsPackets(b, Count);
++ if (InfoWritten && TsStreamErrorCounter.ErrorCount() > 0) {
++ cRecordingInfo RecordingInfo(recordingName);
++ if (RecordingInfo.Read()) {
++ RecordingInfo.SetErrorCount(RecordingInfo.ErrorCount() + TsStreamErrorCounter.ErrorCount());
++ RecordingInfo.Write();
++ Recordings.UpdateByName(recordingName);
++ TsStreamErrorCounter.ResetErrorCount();
++ }
++ }
+ if (recordFile->Write(b, Count) < 0) {
+ LOG_ERROR_STR(fileName->Name());
+ break;
+diff --git a/recording.c b/recording.c
+index bea7eb6..47fd2c2 100644
+--- a/recording.c
++++ b/recording.c
+@@ -321,6 +321,7 @@ cRecordingInfo::cRecordingInfo(const cChannel *Channel, const cEvent *Event)
+ event = ownEvent ? ownEvent : Event;
+ aux = NULL;
+ framesPerSecond = DEFAULTFRAMESPERSECOND;
++ errorCount = 0;
+ priority = MAXPRIORITY;
+ lifetime = MAXLIFETIME;
+ fileName = NULL;
+@@ -414,6 +415,11 @@ void cRecordingInfo::SetFramesPerSecond(double FramesPerSecond)
+ framesPerSecond = FramesPerSecond;
+ }
+
++void cRecordingInfo::SetErrorCount(int ErrorCount)
++{
++ errorCount = ErrorCount;
++}
++
+ bool cRecordingInfo::Read(FILE *f)
+ {
+ if (ownEvent) {
+@@ -453,6 +459,8 @@ bool cRecordingInfo::Read(FILE *f)
+ break;
+ case 'F': framesPerSecond = atof(t);
+ break;
++ case 'R': errorCount = atoi(t);
++ break;
+ case 'L': lifetime = atoi(t);
+ break;
+ case 'P': priority = atoi(t);
+@@ -479,6 +487,7 @@ bool cRecordingInfo::Write(FILE *f, const char *Prefix) const
+ fprintf(f, "%sC %s%s%s\n", Prefix, *channelID.ToString(), channelName ? " " : "", channelName ? channelName : "");
+ event->Dump(f, Prefix, true);
+ fprintf(f, "%sF %.10g\n", Prefix, framesPerSecond);
++ fprintf(f, "%sR %d\n", Prefix, errorCount);
+ fprintf(f, "%sP %d\n", Prefix, priority);
+ fprintf(f, "%sL %d\n", Prefix, lifetime);
+ if (aux)
+diff --git a/recording.h b/recording.h
+index 37979ec..be03ecb 100644
+--- a/recording.h
++++ b/recording.h
+@@ -55,6 +55,7 @@ private:
+ cEvent *ownEvent;
+ char *aux;
+ double framesPerSecond;
++ int errorCount;
+ int priority;
+ int lifetime;
+ char *fileName;
+@@ -75,6 +76,8 @@ public:
+ const char *Aux(void) const { return aux; }
+ double FramesPerSecond(void) const { return framesPerSecond; }
+ void SetFramesPerSecond(double FramesPerSecond);
++ int ErrorCount(void) const { return errorCount; }
++ void SetErrorCount(int ErrorCount);
+ bool Write(FILE *f, const char *Prefix = "") const;
+ bool Read(void);
+ bool Write(void) const;
+diff --git a/remux.c b/remux.c
+index 78ab294..eb6b0d9 100644
+--- a/remux.c
++++ b/remux.c
+@@ -1029,3 +1029,44 @@ int cFrameDetector::Analyze(const uchar *Data, int Length)
+ }
+ return Processed;
+ }
++
++// --- cTsStreamErrorCounter -------------------------------------------------
++
++cTsStreamErrorCounter::cTsStreamErrorCounter(void)
++{
++ errorCount = 0;
++ for(int i=0; i < MAX_PIDS; i++) pids[i] = -1;
++ memset(counters, 0, sizeof(counters));
++}
++
++void cTsStreamErrorCounter::CheckTsPackets(uchar* Data, int Length)
++{
++ while (Length >= TS_SIZE) {
++ int pid = ((Data[1] & 0x1F) << 8) + Data[2];
++ char ctr = (Data[3] & 0x0F);
++ char adaption = (Data[3] & 0x30) >> 4;
++
++ if (adaption != 2)
++ CheckTsPacketContinuity(pid, ctr);
++
++ Length -= TS_SIZE;
++ Data += TS_SIZE;
++ }
++}
++
++void cTsStreamErrorCounter::CheckTsPacketContinuity(int pid, int counter)
++{
++ for (int i=0; i < MAX_PIDS; i++) {
++ if (pids[i] == -1) {
++ pids[i] = pid;
++ counters[i] = counter;
++ break;
++ }
++ if (pids[i] == pid) {
++ if (((counter - counters[i]) & 0x0F) != 1)
++ errorCount++;
++ counters[i] = counter;
++ break;
++ }
++ }
++}
+diff --git a/remux.h b/remux.h
+index b882279..72545e7 100644
+--- a/remux.h
++++ b/remux.h
+@@ -390,4 +390,29 @@ public:
+ ///< available.
+ };
+
++
++// TS stream error counter:
++// Count (continuity) errors in a TS packet stream
++
++#define MAX_PIDS 20
++
++class cTsStreamErrorCounter {
++private:
++ int errorCount;
++ int pids[MAX_PIDS];
++ uchar counters[MAX_PIDS];
++public:
++ cTsStreamErrorCounter(void);
++ void CheckTsPackets(uchar* Data, int Length);
++ ///< Check for errors in the TS packets pointed to by Data. Length is the
++ ///< number of bytes Data points to, and must be a multiple of 188.
++ int ErrorCount(void) const { return errorCount; };
++ ///< Return the number of errors found in the analyzed TS packet stream.
++ ///< Right now, only continuity errors are reported.
++ void ResetErrorCount(void) { errorCount = 0; };
++ ///< Reset error count to 0
++private:
++ void CheckTsPacketContinuity(int pid, int counter);
++ };
++
+ #endif // __REMUX_H
+--
+1.7.6.3
+
diff --git a/tsstreamerrorcounter.cc b/tsstreamerrorcounter.cc
new file mode 100644
index 0000000..8cb5771
--- /dev/null
+++ b/tsstreamerrorcounter.cc
@@ -0,0 +1,44 @@
+#include "string.h"
+#include <stdio.h>
+#include "tsstreamerrorcounter.h"
+
+// --- cTsStreamErrorCounter -------------------------------------------------
+
+cTsStreamErrorCounter::cTsStreamErrorCounter(void)
+{
+ errorCount = 0;
+ for(int i=0; i < MAX_PIDS; i++) pids[i] = -1;
+ memset(counters, 0, sizeof(counters));
+}
+
+void cTsStreamErrorCounter::CheckTsPackets(uchar* Data, int Length)
+{
+ while (Length >= TS_SIZE) {
+ int pid = ((Data[1] & 0x1F) << 8) + Data[2];
+ char ctr = (Data[3] & 0x0F);
+ char adaption = (Data[3] & 0x30) >> 4;
+
+ if (adaption != 2)
+ CheckTsPacketContinuity(pid, ctr);
+
+ Length -= TS_SIZE;
+ Data += TS_SIZE;
+ }
+}
+
+void cTsStreamErrorCounter::CheckTsPacketContinuity(int pid, int counter)
+{
+ for (int i=0; i < MAX_PIDS; i++) {
+ if (pids[i] == -1) {
+ pids[i] = pid;
+ counters[i] = counter;
+ break;
+ }
+ if (pids[i] == pid) {
+ if (((counter - counters[i]) & 0x0F) != 1)
+ errorCount++;
+ counters[i] = counter;
+ break;
+ }
+ }
+}
diff --git a/tsstreamerrorcounter.h b/tsstreamerrorcounter.h
new file mode 100644
index 0000000..4f21fbc
--- /dev/null
+++ b/tsstreamerrorcounter.h
@@ -0,0 +1,31 @@
+#ifndef __TSCHECKER_H
+#define __TSCHECKER_H
+
+#define TS_SYNC_BYTE 0x47
+#define TS_SIZE 188
+
+// TS stream error counter:
+// Count (continuity) errors in a TS packet stream
+
+#define MAX_PIDS 20
+typedef unsigned char uchar;
+
+class cTsStreamErrorCounter {
+private:
+ int errorCount;
+ int pids[MAX_PIDS];
+ uchar counters[MAX_PIDS];
+public:
+ cTsStreamErrorCounter(void);
+ void CheckTsPackets(uchar* Data, int Length);
+ ///< Check for errors in the TS packets pointed to by Data. Length is the
+ ///< number of bytes Data points to, and must be a multiple of 188.
+ int ErrorCount(void) const { return errorCount; };
+ ///< Return the number of errors found in the analyzed TS packet stream.
+ ///< Right now, only continuity errors are reported.
+private:
+ void CheckTsPacketContinuity(int pid, int counter);
+ };
+
+
+#endif // __TSCHECKER_H
diff --git a/vdr-checkts.cc b/vdr-checkts.cc
new file mode 100644
index 0000000..45ca51c
--- /dev/null
+++ b/vdr-checkts.cc
@@ -0,0 +1,91 @@
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <limits.h>
+#include "tsstreamerrorcounter.h"
+
+#define TS_READ_BUFFER_SIZE 50
+
+int maxErrors = 0;
+const char* recordingDir;
+
+void displayHelp(void)
+{
+ printf("Usage: vdr-checkts [OPTIONS] RECORDING_DIR\n\n"
+ " -m errors, --max-errors=ERROR Stop scanning if error count reaches ERROR\n"
+ " -h, --help print this help and exit\n"
+ "\n"
+ );
+}
+
+int processCliArguments(int argc, char *argv[])
+{
+ static struct option long_options[] = {
+ { "max-errors", required_argument, NULL, 'm' },
+ { "help", no_argument, NULL, 'h' },
+ { NULL, no_argument, NULL, 0 }
+ };
+
+ int c;
+ while ((c = getopt_long(argc, argv, "m:h", long_options, NULL)) != -1) {
+ switch (c) {
+ case 'm': maxErrors = strtol(optarg, (char**) NULL, 10);
+ if (errno != 0 || maxErrors <= 0) {
+ fprintf(stderr, "Invalid max error argument: %s\n", optarg);
+ return -2;
+ }
+ break;
+ case 'h': displayHelp();
+ return -1;
+ }
+ }
+ if (optind != argc - 1)
+ {
+ displayHelp();
+ return -1;
+ }
+ recordingDir = argv[optind];
+ return 0;
+}
+
+int checkRecording(void)
+{
+ int fileNumber = 1;
+ cTsStreamErrorCounter tsStreamErrorCounter;
+ while(!maxErrors || tsStreamErrorCounter.ErrorCount() < maxErrors) {
+ char fileName[PATH_MAX];
+
+ sprintf(fileName, "%s/%.5d.ts", recordingDir, fileNumber);
+
+ FILE *file = fopen64(fileName, "rb");
+ if (file) {
+ unsigned char buffer[TS_READ_BUFFER_SIZE * TS_SIZE];
+ int read;
+ while (true) {
+ read = fread(buffer, TS_SIZE, TS_READ_BUFFER_SIZE, file);
+ if (read <= 0) break;
+ tsStreamErrorCounter.CheckTsPackets(buffer, read * TS_SIZE);
+ if (maxErrors && tsStreamErrorCounter.ErrorCount() >= maxErrors)
+ break;
+ }
+ }
+ else
+ break;
+ fclose(file);
+ fileNumber++;
+ }
+
+ printf("Errors: %d\n", tsStreamErrorCounter.ErrorCount());
+ return tsStreamErrorCounter.ErrorCount();
+}
+
+int main(int argc, char *argv[])
+{
+ if (int res = processCliArguments(argc, argv) != 0) return res;
+
+ if (checkRecording() == 0)
+ return 0;
+ else
+ return 1;
+}