summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CONTRIBUTORS2
-rw-r--r--HISTORY5
-rw-r--r--recorder.c5
-rw-r--r--remux.c129
-rw-r--r--remux.h23
5 files changed, 112 insertions, 52 deletions
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index 032b7f5d..0df0bb24 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -2421,6 +2421,8 @@ Timo Helkio <timolavi@mbnet.fi>
Derek Kelly (user.vdr@gmail.com)
for fixing handling the 'new' indicator in the recordings menu for TS recordings
+ for reporting a problem with HD NTSC broadcasts that split frames over several payload
+ units
Marcel Unbehaun <frostworks@gmx.de>
for adding cRecordingInfo::GetEvent()
diff --git a/HISTORY b/HISTORY
index 8e8ec2f7..53e5b2e8 100644
--- a/HISTORY
+++ b/HISTORY
@@ -5979,7 +5979,7 @@ Video Disk Recorder Revision History
cDevice class reimplements PlayTs() or PlayPes(), it also needs to make sure this
new function works as expected.
-2009-03-13: Version 1.7.5
+2009-03-27: Version 1.7.5
- Fixed a hangup when replaying a TS recording with subtitles activated (reported
by Timo Helkio).
@@ -6000,3 +6000,6 @@ Video Disk Recorder Revision History
+ The full-featured DVB cards need an improved firmware in order to return
proper STC values in trick modes (thanks to Oliver Endriss for enhancing the
av7110 firmware).
+- Adapted cFrameDetector::Analyze() to HD NTSC broadcasts that split frames over
+ several payload units (thanks to Derek Kelly for reporting this and helping in
+ testing).
diff --git a/recorder.c b/recorder.c
index 6bf299eb..d8f22ce1 100644
--- a/recorder.c
+++ b/recorder.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: recorder.c 2.2 2009/01/23 16:44:29 kls Exp $
+ * $Id: recorder.c 2.3 2009/03/20 15:49:02 kls Exp $
*/
#include "recorder.h"
@@ -113,7 +113,6 @@ void cRecorder::Receive(uchar *Data, int Length)
void cRecorder::Action(void)
{
time_t t = time(NULL);
- bool Synced = false;
bool InfoWritten = false;
while (Running()) {
int r;
@@ -123,7 +122,7 @@ void cRecorder::Action(void)
if (Count) {
if (!Running() && frameDetector->IndependentFrame()) // finish the recording before the next independent frame
break;
- if (Synced |= frameDetector->IndependentFrame()) { // start with first independent frame
+ if (frameDetector->Synced()) {
if (!InfoWritten) {
if (recordingInfo.Read()) {
if (frameDetector->FramesPerSecond() > 0 && recordingInfo.FramesPerSecond() != frameDetector->FramesPerSecond()) {
diff --git a/remux.c b/remux.c
index a22c024b..2a1f7a94 100644
--- a/remux.c
+++ b/remux.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: remux.c 2.14 2009/03/08 12:12:17 kls Exp $
+ * $Id: remux.c 2.15 2009/03/27 13:32:11 kls Exp $
*/
#include "remux.h"
@@ -676,15 +676,25 @@ cFrameDetector::cFrameDetector(int Pid, int Type)
{
pid = Pid;
type = Type;
+ synced = false;
newFrame = independentFrame = false;
- lastPts = 0;
+ numPtsValues = 0;
+ numIFrames = 0;
isVideo = type == 0x02 || type == 0x1B; // MPEG 2 or MPEG 4
frameDuration = 0;
- framesPerPayloadUnit = 0;
+ framesInPayloadUnit = framesPerPayloadUnit = 0;
+ payloadUnitOfFrame = 0;
scanning = false;
scanner = 0;
}
+static int CmpUint32(const void *p1, const void *p2)
+{
+ if (*(uint32_t *)p1 < *(uint32_t *)p2) return -1;
+ if (*(uint32_t *)p1 > *(uint32_t *)p2) return 1;
+ return 0;
+}
+
int cFrameDetector::Analyze(const uchar *Data, int Length)
{
newFrame = independentFrame = false;
@@ -692,31 +702,47 @@ int cFrameDetector::Analyze(const uchar *Data, int Length)
if (TsHasPayload(Data) && !TsIsScrambled(Data) && TsPid(Data) == pid) {
if (TsPayloadStart(Data)) {
if (!frameDuration) {
- const uchar *Pes = Data + TsPayloadOffset(Data);
- if (PesHasPts(Pes)) {
- int64_t Pts = PesGetPts(Pes);
- if (Pts < lastPts) { // avoid wrapping
- lastPts = 0;
- framesPerPayloadUnit = 0;
+ // frame duration unknown, so collect a sequenece of PTS values:
+ if (numPtsValues < MaxPtsValues && numIFrames < 2) { // collect a sequence containing at least two I-frames
+ const uchar *Pes = Data + TsPayloadOffset(Data);
+ if (PesHasPts(Pes)) {
+ ptsValues[numPtsValues] = PesGetPts(Pes);
+ // check for rollover:
+ if (numPtsValues && ptsValues[numPtsValues - 1] > 0xF0000000 && ptsValues[numPtsValues] < 0x10000000) {
+ dbgframes("#");
+ numPtsValues = 0;
+ numIFrames = 0;
+ }
+ else
+ numPtsValues++;
}
- if ((!lastPts || !framesPerPayloadUnit) && Pts != lastPts)
- lastPts = Pts;
- else {
- int64_t Delta = Pts - lastPts;
- if (isVideo) {
- if (Delta % 3600 == 0)
- frameDuration = 3600; // PAL, 25 fps
- else if (Delta % 3003 == 0)
- frameDuration = 3003; // NTSC, 29.97 fps
- else {
- frameDuration = 3600; // unknown, assuming 25 fps
- dsyslog("unknown frame duration, assuming 25 fps (PTS: %lld - %lld = %lld FPPU = %d)\n", Pts, lastPts, Delta, framesPerPayloadUnit);
- }
+ }
+ else {
+ // find the smallest PTS delta:
+ qsort(ptsValues, numPtsValues, sizeof(uint32_t), CmpUint32);
+ numPtsValues--;
+ for (int i = 0; i < numPtsValues; i++)
+ ptsValues[i] = ptsValues[i + 1] - ptsValues[i];
+ qsort(ptsValues, numPtsValues, sizeof(uint32_t), CmpUint32);
+ uint32_t Delta = ptsValues[0];
+ // determine frame info:
+ if (isVideo) {
+ if (Delta % 3600 == 0)
+ frameDuration = 3600; // PAL, 25 fps
+ else if (Delta % 3003 == 0)
+ frameDuration = 3003; // NTSC, 29.97 fps
+ else if (Delta == 1501) {
+ frameDuration = 3003; // NTSC, 29.97 fps
+ framesPerPayloadUnit = -2;
+ }
+ else {
+ frameDuration = 3600; // unknown, assuming 25 fps
+ dsyslog("unknown frame duration (%d), assuming 25 fps", Delta);
}
- else // audio
- frameDuration = Delta; // PTS of audio frames is always increasing
- dbgframes("PTS: %lld - %lld = %lld -> FD = %d FPS = %5.2f FPPU = %d\n", Pts, lastPts, Delta, frameDuration, 90000.0 / frameDuration, framesPerPayloadUnit);
}
+ else // audio
+ frameDuration = Delta; // PTS of audio frames is always increasing
+ dbgframes("\nframe duration = %d FPS = %5.2f FPPU = %d\n", frameDuration, 90000.0 / frameDuration, framesPerPayloadUnit);
}
}
scanner = 0;
@@ -724,24 +750,31 @@ int cFrameDetector::Analyze(const uchar *Data, int Length)
}
if (scanning) {
int PayloadOffset = TsPayloadOffset(Data);
- if (TsPayloadStart(Data))
+ if (TsPayloadStart(Data)) {
PayloadOffset += PesPayloadOffset(Data + PayloadOffset);
+ if (!framesPerPayloadUnit)
+ framesPerPayloadUnit = framesInPayloadUnit;
+ if (DebugFrames && !synced)
+ dbgframes("/");
+ }
for (int i = PayloadOffset; i < TS_SIZE; i++) {
scanner <<= 8;
scanner |= Data[i];
switch (type) {
case 0x02: // MPEG 2 video
if (scanner == 0x00000100) { // Picture Start Code
- if (frameDuration) {
- newFrame = true;
- independentFrame = ((Data[i + 2] >> 3) & 0x07) == 1; // I-Frame
- if (framesPerPayloadUnit == 1) {
+ newFrame = true;
+ independentFrame = ((Data[i + 2] >> 3) & 0x07) == 1; // I-Frame
+ if (synced) {
+ if (framesPerPayloadUnit <= 1) {
scanning = false;
return TS_SIZE;
}
}
else {
- framesPerPayloadUnit++;
+ framesInPayloadUnit++;
+ if (independentFrame)
+ numIFrames++;
dbgframes("%d ", (Data[i + 2] >> 3) & 0x07);
}
scanner = 0;
@@ -749,16 +782,25 @@ int cFrameDetector::Analyze(const uchar *Data, int Length)
break;
case 0x1B: // MPEG 4 video
if (scanner == 0x00000109) { // Access Unit Delimiter
- if (frameDuration) {
- newFrame = true;
- independentFrame = Data[i + 1] == 0x10;
- if (framesPerPayloadUnit == 1) {
+ newFrame = true;
+ independentFrame = Data[i + 1] == 0x10;
+ if (synced) {
+ if (framesPerPayloadUnit < 0) {
+ payloadUnitOfFrame = (payloadUnitOfFrame + 1) % -framesPerPayloadUnit;
+ if (payloadUnitOfFrame != 0 && independentFrame)
+ payloadUnitOfFrame = 0;
+ if (payloadUnitOfFrame)
+ newFrame = false;
+ }
+ if (framesPerPayloadUnit <= 1) {
scanning = false;
return TS_SIZE;
}
}
else {
- framesPerPayloadUnit++;
+ framesInPayloadUnit++;
+ if (independentFrame)
+ numIFrames++;
dbgframes("%02X ", Data[i + 1]);
}
scanner = 0;
@@ -766,18 +808,23 @@ int cFrameDetector::Analyze(const uchar *Data, int Length)
break;
case 0x04: // MPEG audio
case 0x06: // AC3 audio
- if (frameDuration) {
- newFrame = true;
- independentFrame = true;
+ newFrame = true;
+ independentFrame = true;
+ if (synced)
scanning = false;
+ else {
+ framesInPayloadUnit = 1;
+ numIFrames++;
}
- else
- framesPerPayloadUnit = 1;
break;
default: esyslog("ERROR: unknown stream type %d (PID %d) in frame detector", type, pid);
pid = 0; // let's just ignore any further data
}
}
+ if (!synced && frameDuration && independentFrame) {
+ synced = true;
+ dbgframes("*");
+ }
}
}
return TS_SIZE;
diff --git a/remux.h b/remux.h
index b41b4c8d..ccb38821 100644
--- a/remux.h
+++ b/remux.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: remux.h 2.8 2009/03/08 12:05:12 kls Exp $
+ * $Id: remux.h 2.9 2009/03/27 13:38:59 kls Exp $
*/
#ifndef __REMUX_H
@@ -267,14 +267,22 @@ void PesDump(const char *Name, const u_char *Data, int Length);
class cFrameDetector {
private:
+ enum { MaxPtsValues = 150 };
int pid;
int type;
+ bool synced;
bool newFrame;
bool independentFrame;
- int64_t lastPts;
+ uint32_t ptsValues[MaxPtsValues]; // 32 bit is enough - we only need the delta
+ int numPtsValues;
+ int numIFrames;
bool isVideo;
int frameDuration;
- int framesPerPayloadUnit;
+ int framesInPayloadUnit;
+ int framesPerPayloadUnit; // Some broadcasters send one frame per payload unit (== 1),
+ // some put an entire GOP into one payload unit (> 1), and
+ // some spread a single frame over several payload units (< 0).
+ int payloadUnitOfFrame;
bool scanning;
uint32_t scanner;
public:
@@ -282,10 +290,11 @@ public:
int Analyze(const uchar *Data, int Length);
///< Analyzes the TS packets pointed to by Data. Length is the number of
///< bytes Data points to, and must be a multiple of 188.
- ///< Returns the number of bytes that have been analyzed and may be written
- ///< to the recording file. If the return value is 0, the data was not
- ///< sufficient for analyzing and Analyze() needs to be called again with
- ///< more actual data.
+ ///< Returns the number of bytes that have been analyzed.
+ ///< If the return value is 0, the data was not sufficient for analyzing and
+ ///< Analyze() needs to be called again with more actual data.
+ bool Synced(void) { return synced; }
+ ///< Returns true if the frame detector has synced on the data stream.
bool NewFrame(void) { return newFrame; }
///< Returns true if the data given to the last call to Analyze() started a
///< new frame.