Permalink
Browse files

DTVRecorder: buffer up each payload until we know if the payload is a

keyframe.

Especially with H.264, we can be several packets into a payload before we
know if it is a keyframe or just a frame or something else.  Always buffer
the packets of a payload until we know what we are doing with the payload.

If we need to switch ringbuffers, we now do it so the new file will start at
the beginning of the payload holding a keyframe.  Each file will also start
with a PAT/PMT if the "recorder" calls for it.
(cherry picked from commit 87a15e1)

Signed-off-by: Taylor Ralph <tralph@mythtv.org>
  • Loading branch information...
1 parent 947bb60 commit 71ad653b9a7ba593eb5307d4cd88a81e4d9387a4 @jpoet jpoet committed with tralph Mar 3, 2013
View
15 mythtv/libs/libmythtv/asirecorder.cpp
@@ -62,6 +62,14 @@ void ASIRecorder::SetOption(const QString &name, int value)
DTVRecorder::SetOption(name, value);
}
+void ASIRecorder::StartNewFile(void)
+{
+ // Make sure the first things in the file are a PAT & PMT
+ HandleSingleProgramPAT(_stream_data->PATSingleProgram(), true);
+ HandleSingleProgramPMT(_stream_data->PMTSingleProgram(), true);
+}
+
+
void ASIRecorder::run(void)
{
if (!Open())
@@ -99,12 +107,7 @@ void ASIRecorder::run(void)
if (m_channel && (m_channel->GetSIStandard() == "dvb"))
_stream_data->AddListeningPID(DVB_TDT_PID);
- // Make sure the first things in the file are a PAT & PMT
- bool tmp = _wait_for_keyframe_option;
- _wait_for_keyframe_option = false;
- HandleSingleProgramPAT(_stream_data->PATSingleProgram());
- HandleSingleProgramPMT(_stream_data->PMTSingleProgram());
- _wait_for_keyframe_option = tmp;
+ StartNewFile();
_stream_data->AddAVListener(this);
_stream_data->AddWritingListener(this);
View
1 mythtv/libs/libmythtv/asirecorder.h
@@ -68,6 +68,7 @@ class ASIRecorder : public DTVRecorder
bool Open(void);
bool IsOpen(void) const;
void Close(void);
+ void StartNewFile(void);
private:
ASIChannel *m_channel;
View
14 mythtv/libs/libmythtv/cetonrecorder.cpp
@@ -46,6 +46,13 @@ void CetonRecorder::Close(void)
LOG(VB_RECORD, LOG_INFO, LOC + "Close() -- end");
}
+void CetonRecorder::StartNewFile(void)
+{
+ // Make sure the first things in the file are a PAT & PMT
+ HandleSingleProgramPAT(_stream_data->PATSingleProgram(), true);
+ HandleSingleProgramPMT(_stream_data->PMTSingleProgram(), true);
+}
+
void CetonRecorder::run(void)
{
LOG(VB_RECORD, LOG_INFO, LOC + "run -- begin");
@@ -65,12 +72,7 @@ void CetonRecorder::run(void)
recordingWait.wakeAll();
}
- // Make sure the first things in the file are a PAT & PMT
- bool tmp = _wait_for_keyframe_option;
- _wait_for_keyframe_option = false;
- HandleSingleProgramPAT(_stream_data->PATSingleProgram());
- HandleSingleProgramPMT(_stream_data->PMTSingleProgram());
- _wait_for_keyframe_option = tmp;
+ StartNewFile();
_stream_data->AddAVListener(this);
_stream_data->AddWritingListener(this);
View
1 mythtv/libs/libmythtv/cetonrecorder.h
@@ -26,6 +26,7 @@ class CetonRecorder : public DTVRecorder
bool Open(void);
void Close(void);
+ void StartNewFile(void);
bool IsOpen(void) const { return _stream_handler; }
View
303 mythtv/libs/libmythtv/dtvrecorder.cpp
@@ -85,7 +85,12 @@ DTVRecorder::DTVRecorder(TVRec *rec) :
{
SetPositionMapType(MARK_GOP_BYFRAME);
_payload_buffer.reserve(TSPacket::kSize * (50 + 1));
+
ResetForNewFile();
+
+ memset(_stream_id, 0, sizeof(_stream_id));
+ memset(_pid_status, 0, sizeof(_pid_status));
+ memset(_continuity_counter, 0xff, sizeof(_continuity_counter));
}
DTVRecorder::~DTVRecorder()
@@ -145,14 +150,7 @@ void DTVRecorder::SetOptionsFromProfile(RecordingProfile *profile,
void DTVRecorder::FinishRecording(void)
{
if (ringBuffer)
- {
- if (!_payload_buffer.empty())
- {
- ringBuffer->Write(&_payload_buffer[0], _payload_buffer.size());
- _payload_buffer.clear();
- }
ringBuffer->WriterFlush();
- }
if (curRecording)
{
@@ -167,7 +165,7 @@ void DTVRecorder::ResetForNewFile(void)
LOG(VB_RECORD, LOG_INFO, LOC + "ResetForNewFile(void)");
QMutexLocker locker(&positionMapLock);
- // _first_keyframe, _seen_psp and m_h264_parser should
+ // _seen_psp and m_h264_parser should
// not be reset here. This will only be called just as
// we're seeing the first packet of a new keyframe for
// writing to the new file and anything that makes the
@@ -176,7 +174,7 @@ void DTVRecorder::ResetForNewFile(void)
// -- Daniel Kristjansson 2011-02-26
_start_code = 0xffffffff;
- //_first_keyframe
+ _first_keyframe = -1;
_has_written_other_keyframe = false;
_last_keyframe_seen = 0;
_last_gop_seen = 0;
@@ -187,18 +185,13 @@ void DTVRecorder::ResetForNewFile(void)
//_recording
_error = QString();
- memset(_stream_id, 0, sizeof(_stream_id));
- memset(_pid_status, 0, sizeof(_pid_status));
- memset(_continuity_counter, 0xff, sizeof(_continuity_counter));
-
_progressive_sequence = 0;
_repeat_pict = 0;
- _pes_synced = false;
+ //_pes_synced
//_seen_sps
positionMap.clear();
positionMapDelta.clear();
- _payload_buffer.clear();
locker.unlock();
ClearStatistics();
@@ -209,9 +202,9 @@ void DTVRecorder::ClearStatistics(void)
RecorderBase::ClearStatistics();
memset(_ts_count, 0, sizeof(_ts_count));
- for (int i = 0; i < 256; i++)
+ for (int i = 0; i < 256; ++i)
_ts_last[i] = -1LL;
- for (int i = 0; i < 256; i++)
+ for (int i = 0; i < 256; ++i)
_ts_first[i] = -1LL;
//_ts_first_dt -- doesn't need to be cleared only used if _ts_first>=0
_packet_count.fetchAndStoreRelaxed(0);
@@ -264,58 +257,62 @@ void DTVRecorder::SetStreamData(void)
_stream_data->SetDesiredProgram(_stream_data->DesiredProgram());
}
-void DTVRecorder::BufferedWrite(const TSPacket &tspacket)
+void DTVRecorder::BufferedWrite(const TSPacket &tspacket, bool insert)
{
- // delay until first GOP to avoid decoder crash on res change
- if (_wait_for_keyframe_option && _first_keyframe<0)
- return;
-
- if (curRecording && timeOfFirstDataIsSet.testAndSetRelaxed(0,1))
+ if (!insert) // PAT/PMT may need inserted in front of any buffered data
{
- QMutexLocker locker(&statisticsLock);
- timeOfFirstData = mythCurrentDateTime();
- timeOfLatestData = mythCurrentDateTime();
- timeOfLatestDataTimer.start();
- }
+ // delay until first GOP to avoid decoder crash on res change
+ if (!_buffer_packets && _wait_for_keyframe_option &&
+ _first_keyframe < 0)
+ return;
- int val = timeOfLatestDataCount.fetchAndAddRelaxed(1);
- int thresh = timeOfLatestDataPacketInterval.fetchAndAddRelaxed(0);
- if (val > thresh)
- {
- QMutexLocker locker(&statisticsLock);
- uint elapsed = timeOfLatestDataTimer.restart();
- int interval = thresh;
- if (elapsed > kTimeOfLatestDataIntervalTarget + 250)
- interval = timeOfLatestDataPacketInterval
- .fetchAndStoreRelaxed(thresh * 4 / 5);
- else if (elapsed + 250 < kTimeOfLatestDataIntervalTarget)
- interval = timeOfLatestDataPacketInterval
- .fetchAndStoreRelaxed(thresh * 9 / 8);
-
- timeOfLatestDataCount.fetchAndStoreRelaxed(1);
- timeOfLatestData = mythCurrentDateTime();
-
- LOG(VB_RECORD, LOG_DEBUG, LOC + QString("Updating timeOfLatestData ") +
- QString("elapsed(%1) interval(%2)")
- .arg(elapsed).arg(interval));
- }
+ if (curRecording && timeOfFirstDataIsSet.testAndSetRelaxed(0,1))
+ {
+ QMutexLocker locker(&statisticsLock);
+ timeOfFirstData = mythCurrentDateTime();
+ timeOfLatestData = mythCurrentDateTime();
+ timeOfLatestDataTimer.start();
+ }
- // Do we have to buffer the packet for exact keyframe detection?
- if (_buffer_packets)
- {
- int idx = _payload_buffer.size();
- _payload_buffer.resize(idx + TSPacket::kSize);
- memcpy(&_payload_buffer[idx], tspacket.data(), TSPacket::kSize);
- return;
- }
+ int val = timeOfLatestDataCount.fetchAndAddRelaxed(1);
+ int thresh = timeOfLatestDataPacketInterval.fetchAndAddRelaxed(0);
+ if (val > thresh)
+ {
+ QMutexLocker locker(&statisticsLock);
+ uint elapsed = timeOfLatestDataTimer.restart();
+ int interval = thresh;
+ if (elapsed > kTimeOfLatestDataIntervalTarget + 250)
+ interval = timeOfLatestDataPacketInterval
+ .fetchAndStoreRelaxed(thresh * 4 / 5);
+ else if (elapsed + 250 < kTimeOfLatestDataIntervalTarget)
+ interval = timeOfLatestDataPacketInterval
+ .fetchAndStoreRelaxed(thresh * 9 / 8);
+
+ timeOfLatestDataCount.fetchAndStoreRelaxed(1);
+ timeOfLatestData = mythCurrentDateTime();
+
+ LOG(VB_RECORD, LOG_DEBUG, LOC +
+ QString("Updating timeOfLatestData elapsed(%1) interval(%2)")
+ .arg(elapsed).arg(interval));
+ }
- // We are free to write the packet, but if we have buffered packet[s]
- // we have to write them first...
- if (!_payload_buffer.empty())
- {
- if (ringBuffer)
- ringBuffer->Write(&_payload_buffer[0], _payload_buffer.size());
- _payload_buffer.clear();
+ // Do we have to buffer the packet for exact keyframe detection?
+ if (_buffer_packets)
+ {
+ int idx = _payload_buffer.size();
+ _payload_buffer.resize(idx + TSPacket::kSize);
+ memcpy(&_payload_buffer[idx], tspacket.data(), TSPacket::kSize);
+ return;
+ }
+
+ // We are free to write the packet, but if we have buffered packet[s]
+ // we have to write them first...
+ if (!_payload_buffer.empty())
+ {
+ if (ringBuffer)
+ ringBuffer->Write(&_payload_buffer[0], _payload_buffer.size());
+ _payload_buffer.clear();
+ }
}
if (ringBuffer)
@@ -393,12 +390,11 @@ static const uint frameRateMap[16] = {
*/
bool DTVRecorder::FindMPEG2Keyframes(const TSPacket* tspacket)
{
- bool haveBufferedData = !_payload_buffer.empty();
if (!tspacket->HasPayload()) // no payload to scan
- return !haveBufferedData;
+ return _first_keyframe >= 0;
if (!ringBuffer)
- return !haveBufferedData;
+ return _first_keyframe >= 0;
// if packet contains start of PES packet, start
// looking for first byte of MPEG start code (3 bytes 0 0 1)
@@ -526,15 +522,34 @@ bool DTVRecorder::FindMPEG2Keyframes(const TSPacket* tspacket)
if (hasKeyFrame)
{
+ LOG(VB_RECORD, LOG_DEBUG, LOC + QString
+ ("Keyframe @ %1 + %2 = %3")
+ .arg(ringBuffer->GetWritePosition())
+ .arg(_payload_buffer.size())
+ .arg(ringBuffer->GetWritePosition() + _payload_buffer.size()));
+
_last_keyframe_seen = _frames_seen_count;
- HandleKeyframe(_frames_written_count, TSPacket::kSize);
+ HandleKeyframe(0);
}
if (hasFrame)
{
+ LOG(VB_RECORD, LOG_DEBUG, LOC + QString
+ ("Frame @ %1 + %2 = %3")
+ .arg(ringBuffer->GetWritePosition())
+ .arg(_payload_buffer.size())
+ .arg(ringBuffer->GetWritePosition() + _payload_buffer.size()));
+
+ _buffer_packets = false; // We now know if it is a keyframe, or not
_frames_seen_count++;
- if (!_wait_for_keyframe_option || _first_keyframe>=0)
+ if (!_wait_for_keyframe_option || _first_keyframe >= 0)
_frames_written_count++;
+ else
+ {
+ /* Found a frame that is not a keyframe, and we want to
+ * start on a keyframe */
+ _payload_buffer.clear();
+ }
}
if ((aspectRatio > 0) && (aspectRatio != m_videoAspect))
@@ -558,7 +573,7 @@ bool DTVRecorder::FindMPEG2Keyframes(const TSPacket* tspacket)
FrameRateChange(frameRate, _frames_written_count);
}
- return hasKeyFrame || (_payload_buffer.size() >= (188*50));
+ return _first_keyframe >= 0;
}
void DTVRecorder::HandleTimestamps(int stream_id, int64_t pts, int64_t dts)
@@ -652,7 +667,7 @@ bool DTVRecorder::FindAudioKeyframes(const TSPacket*)
if (1 == (_frames_seen_count & 0x7))
{
_last_keyframe_seen = _frames_seen_count;
- HandleKeyframe(_frames_written_count);
+ HandleKeyframe(_payload_buffer.size());
hasKeyFrame = true;
}
@@ -680,7 +695,7 @@ bool DTVRecorder::FindOtherKeyframes(const TSPacket *tspacket)
_frames_written_count++;
_last_keyframe_seen = _frames_seen_count;
- HandleKeyframe(_frames_written_count);
+ HandleKeyframe(_payload_buffer.size());
_has_written_other_keyframe = true;
@@ -716,24 +731,22 @@ void DTVRecorder::SetNextRecording(const ProgramInfo *progInf, RingBuffer *rb)
* \brief This save the current frame to the position maps
* and handles ringbuffer switching.
*/
-void DTVRecorder::HandleKeyframe(uint64_t frameNum, int64_t extra)
+void DTVRecorder::HandleKeyframe(int64_t extra)
{
if (!ringBuffer)
return;
-#if 0
- unsigned long long frameNum = _frames_written_count;
-#endif
+ // Perform ringbuffer switch if needed.
+ CheckForRingBufferSwitch();
+ uint64_t frameNum = _frames_written_count;
_first_keyframe = (_first_keyframe < 0) ? frameNum : _first_keyframe;
// Add key frame to position map
positionMapLock.lock();
if (!positionMap.contains(frameNum))
{
- long long startpos = ringBuffer->GetWritePosition();
- // FIXME: handle keyframes with start code spanning over two ts packets
- startpos += _payload_buffer.size() - extra;
+ int64_t startpos = ringBuffer->GetWritePosition() + extra;
// Don't put negative offsets into the database, they get munged into
// MAX_INT64 - offset, which is an exceedingly large number, and
@@ -745,9 +758,6 @@ void DTVRecorder::HandleKeyframe(uint64_t frameNum, int64_t extra)
}
}
positionMapLock.unlock();
-
- // Perform ringbuffer switch if needed.
- CheckForRingBufferSwitch();
}
/** \fn DTVRecorder::FindH264Keyframes(const TSPacket*)
@@ -757,16 +767,15 @@ void DTVRecorder::HandleKeyframe(uint64_t frameNum, int64_t extra)
*/
bool DTVRecorder::FindH264Keyframes(const TSPacket *tspacket)
{
+ if (!tspacket->HasPayload()) // no payload to scan
+ return _first_keyframe >= 0;
+
if (!ringBuffer)
{
LOG(VB_GENERAL, LOG_ERR, LOC + "FindH264Keyframes: No ringbuffer");
- return false;
+ return _first_keyframe >= 0;
}
- bool haveBufferedData = !_payload_buffer.empty();
- if (!tspacket->HasPayload()) // no payload to scan
- return !haveBufferedData;
-
const bool payloadStart = tspacket->PayloadStart();
if (payloadStart)
{
@@ -785,7 +794,7 @@ bool DTVRecorder::FindH264Keyframes(const TSPacket *tspacket)
// scan for PES packets and H.264 NAL units
uint i = tspacket->AFCOffset();
- for (; i < TSPacket::kSize; i++)
+ for (; i < TSPacket::kSize; ++i)
{
// special handling required when a new PES packet begins
if (payloadStart && !_pes_synced)
@@ -853,10 +862,9 @@ bool DTVRecorder::FindH264Keyframes(const TSPacket *tspacket)
// scan for a NAL unit start code
- uint32_t bytes_used = m_h264_parser.addBytes(
- tspacket->data() + i, TSPacket::kSize - i,
- ringBuffer->GetWritePosition() + _payload_buffer.size()
- );
+ uint32_t bytes_used = m_h264_parser.addBytes
+ (tspacket->data() + i, TSPacket::kSize - i,
+ ringBuffer->GetWritePosition());
i += (bytes_used - 1);
if (m_h264_parser.stateChanged())
@@ -874,19 +882,40 @@ bool DTVRecorder::FindH264Keyframes(const TSPacket *tspacket)
frameRate = m_h264_parser.frameRate();
}
}
- } // for (; i < TSPacket::kSize; i++)
+ } // for (; i < TSPacket::kSize; ++i)
if (hasKeyFrame)
{
+ LOG(VB_RECORD, LOG_DEBUG, LOC + QString
+ ("Keyframe @ %1 + %2 = %3 AU %4")
+ .arg(ringBuffer->GetWritePosition())
+ .arg(_payload_buffer.size())
+ .arg(ringBuffer->GetWritePosition() + _payload_buffer.size())
+ .arg(m_h264_parser.keyframeAUstreamOffset()));
+
_last_keyframe_seen = _frames_seen_count;
HandleH264Keyframe();
}
if (hasFrame)
{
+ LOG(VB_RECORD, LOG_DEBUG, LOC + QString
+ ("Frame @ %1 + %2 = %3 AU %4")
+ .arg(ringBuffer->GetWritePosition())
+ .arg(_payload_buffer.size())
+ .arg(ringBuffer->GetWritePosition() + _payload_buffer.size())
+ .arg(m_h264_parser.keyframeAUstreamOffset()));
+
+ _buffer_packets = false; // We now know if this is a keyframe
_frames_seen_count++;
if (!_wait_for_keyframe_option || _first_keyframe >= 0)
_frames_written_count++;
+ else
+ {
+ /* Found a frame that is not a keyframe, and we want to
+ * start on a keyframe */
+ _payload_buffer.clear();
+ }
}
if ((aspectRatio > 0) && (aspectRatio != m_videoAspect))
@@ -904,7 +933,6 @@ bool DTVRecorder::FindH264Keyframes(const TSPacket *tspacket)
if (frameRate != 0 && frameRate != m_frameRate)
{
-
LOG(VB_RECORD, LOG_INFO, LOC +
QString("FindH264Keyframes: timescale: %1, tick: %2, framerate: %3")
.arg( m_h264_parser.GetTimeScale() )
@@ -914,7 +942,7 @@ bool DTVRecorder::FindH264Keyframes(const TSPacket *tspacket)
FrameRateChange(frameRate, _frames_written_count);
}
- return hasKeyFrame || (_payload_buffer.size() >= (188*50));
+ return _seen_sps;
}
/** \fn DTVRecorder::HandleH264Keyframe(void)
@@ -923,21 +951,28 @@ bool DTVRecorder::FindH264Keyframes(const TSPacket *tspacket)
*/
void DTVRecorder::HandleH264Keyframe(void)
{
- unsigned long long frameNum = _frames_written_count;
+ // Perform ringbuffer switch if needed.
+ CheckForRingBufferSwitch();
- _first_keyframe = (_first_keyframe < 0) ? frameNum : _first_keyframe;
+ uint64_t startpos;
+ uint64_t frameNum = _frames_written_count;
+
+ if (_first_keyframe < 0)
+ {
+ _first_keyframe = frameNum;
+ startpos = 0;
+ }
+ else
+ startpos = m_h264_parser.keyframeAUstreamOffset();
// Add key frame to position map
positionMapLock.lock();
if (!positionMap.contains(frameNum))
{
- positionMapDelta[frameNum] = m_h264_parser.keyframeAUstreamOffset();
- positionMap[frameNum] = m_h264_parser.keyframeAUstreamOffset();
+ positionMapDelta[frameNum] = startpos;
+ positionMap[frameNum] = startpos;
}
positionMapLock.unlock();
-
- // Perform ringbuffer switch if needed.
- CheckForRingBufferSwitch();
}
void DTVRecorder::FindPSKeyFrames(const uint8_t *buffer, uint len)
@@ -1057,7 +1092,7 @@ void DTVRecorder::FindPSKeyFrames(const uint8_t *buffer, uint len)
if (hasKeyFrame)
{
_last_keyframe_seen = _frames_seen_count;
- HandleKeyframe(_frames_written_count, bufptr - bufstart);
+ HandleKeyframe(_payload_buffer.size() - (bufptr - bufstart));
}
if ((aspectRatio > 0) && (aspectRatio != m_videoAspect))
@@ -1141,8 +1176,9 @@ void DTVRecorder::HandlePAT(const ProgramAssociationTable *_pat)
if (!pmtpid)
{
- LOG(VB_RECORD, LOG_ERR, LOC + "SetPAT(): "
- "Ignoring PAT not containing our desired program...");
+ LOG(VB_RECORD, LOG_ERR, LOC +
+ QString("SetPAT(): Ignoring PAT not containing our desired "
+ "program (%1)...").arg(progNum));
return;
}
@@ -1154,7 +1190,7 @@ void DTVRecorder::HandlePAT(const ProgramAssociationTable *_pat)
delete oldpat;
// Listen for the other PMTs for faster channel switching
- for (uint i = 0; _input_pat && (i < _input_pat->ProgramCount()); i++)
+ for (uint i = 0; _input_pat && (i < _input_pat->ProgramCount()); ++i)
{
uint pmt_pid = _input_pat->ProgramPID(i);
if (!_stream_data->IsListeningPID(pmt_pid))
@@ -1175,7 +1211,7 @@ void DTVRecorder::HandlePMT(uint progNum, const ProgramMapTable *_pmt)
QString sistandard = GetSIStandard();
bool has_no_av = true;
- for (uint i = 0; i < _input_pmt->StreamCount() && has_no_av; i++)
+ for (uint i = 0; i < _input_pmt->StreamCount() && has_no_av; ++i)
{
has_no_av &= !_input_pmt->IsVideo(i, sistandard);
has_no_av &= !_input_pmt->IsAudio(i, sistandard);
@@ -1187,7 +1223,8 @@ void DTVRecorder::HandlePMT(uint progNum, const ProgramMapTable *_pmt)
}
}
-void DTVRecorder::HandleSingleProgramPAT(ProgramAssociationTable *pat)
+void DTVRecorder::HandleSingleProgramPAT(ProgramAssociationTable *pat,
+ bool insert)
{
if (!pat)
{
@@ -1202,11 +1239,11 @@ void DTVRecorder::HandleSingleProgramPAT(ProgramAssociationTable *pat)
pat->tsheader()->SetContinuityCounter(next_cc);
pat->GetAsTSPackets(_scratch, next_cc);
- for (uint i = 0; i < _scratch.size(); i++)
- DTVRecorder::BufferedWrite(_scratch[i]);
+ for (uint i = 0; i < _scratch.size(); ++i)
+ DTVRecorder::BufferedWrite(_scratch[i], insert);
}
-void DTVRecorder::HandleSingleProgramPMT(ProgramMapTable *pmt)
+void DTVRecorder::HandleSingleProgramPMT(ProgramMapTable *pmt, bool insert)
{
if (!pmt)
{
@@ -1215,7 +1252,7 @@ void DTVRecorder::HandleSingleProgramPMT(ProgramMapTable *pmt)
}
// collect stream types for H.264 (MPEG-4 AVC) keyframe detection
- for (uint i = 0; i < pmt->StreamCount(); i++)
+ for (uint i = 0; i < pmt->StreamCount(); ++i)
_stream_id[pmt->StreamPID(i)] = pmt->StreamType(i);
if (!ringBuffer)
@@ -1225,8 +1262,8 @@ void DTVRecorder::HandleSingleProgramPMT(ProgramMapTable *pmt)
pmt->tsheader()->SetContinuityCounter(next_cc);
pmt->GetAsTSPackets(_scratch, next_cc);
- for (uint i = 0; i < _scratch.size(); i++)
- DTVRecorder::BufferedWrite(_scratch[i]);
+ for (uint i = 0; i < _scratch.size(); ++i)
+ DTVRecorder::BufferedWrite(_scratch[i], insert);
}
bool DTVRecorder::ProcessTSPacket(const TSPacket &tspacket)
@@ -1252,7 +1289,8 @@ bool DTVRecorder::ProcessTSPacket(const TSPacket &tspacket)
// Only create fake keyframe[s] if there are no audio/video streams
if (_input_pmt && _has_no_av)
{
- _buffer_packets = !FindOtherKeyframes(&tspacket);
+ FindOtherKeyframes(&tspacket);
+ _buffer_packets = false;
}
else
{
@@ -1276,17 +1314,20 @@ bool DTVRecorder::ProcessVideoTSPacket(const TSPacket &tspacket)
uint streamType = _stream_id[tspacket.PID()];
- // Check for keyframes and count frames
- if (streamType == StreamID::H264Video)
+ if (tspacket.HasPayload() && tspacket.PayloadStart())
{
- _buffer_packets = !FindH264Keyframes(&tspacket);
- if (_wait_for_keyframe_option && !_seen_sps)
- return true;
+ // buffer packets until we know if this is a keyframe
+ _buffer_packets = true;
}
+
+ // Check for keyframes and count frames
+ if (streamType == StreamID::H264Video)
+ FindH264Keyframes(&tspacket);
+ else if (streamType != 0)
+ FindMPEG2Keyframes(&tspacket);
else
- {
- _buffer_packets = !FindMPEG2Keyframes(&tspacket);
- }
+ LOG(VB_RECORD, LOG_ERR, LOC +
+ "ProcessVideoTSPacket: unknown stream type!");
return ProcessAVTSPacket(tspacket);
}
@@ -1296,13 +1337,23 @@ bool DTVRecorder::ProcessAudioTSPacket(const TSPacket &tspacket)
if (!ringBuffer)
return true;
- _buffer_packets = !FindAudioKeyframes(&tspacket);
+ if (tspacket.HasPayload() && tspacket.PayloadStart())
+ {
+ // buffer packets until we know if this is a keyframe
+ _buffer_packets = true;
+ }
+
+ FindAudioKeyframes(&tspacket);
return ProcessAVTSPacket(tspacket);
}
/// Common code for processing either audio or video packets
bool DTVRecorder::ProcessAVTSPacket(const TSPacket &tspacket)
{
+ // Sync recording start to first keyframe
+ if (!_buffer_packets && _wait_for_keyframe_option && _first_keyframe < 0)
+ return true;
+
const uint pid = tspacket.PID();
if (pid != 0x1fff)
@@ -1320,10 +1371,6 @@ bool DTVRecorder::ProcessAVTSPacket(const TSPacket &tspacket)
.arg(erate,5,'f',2));
}
- // Sync recording start to first keyframe
- if (_wait_for_keyframe_option && _first_keyframe < 0)
- return true;
-
// Sync streams to the first Payload Unit Start Indicator
// _after_ first keyframe iff _wait_for_keyframe_option is true
if (!(_pid_status[pid] & kPayloadStartSeen) && tspacket.HasPayload())
View
8 mythtv/libs/libmythtv/dtvrecorder.h
@@ -66,8 +66,8 @@ class DTVRecorder :
void HandleEncryptionStatus(uint /*pnum*/, bool /*encrypted*/) { }
// MPEG Single Program Stream Listener
- void HandleSingleProgramPAT(ProgramAssociationTable *pat);
- void HandleSingleProgramPMT(ProgramMapTable *pmt);
+ void HandleSingleProgramPAT(ProgramAssociationTable *pat, bool insert);
+ void HandleSingleProgramPMT(ProgramMapTable *pmt, bool insert);
// ATSC Main
void HandleSTT(const SystemTimeTable*) { UpdateCAMTimeOffset(); }
@@ -93,10 +93,10 @@ class DTVRecorder :
void FinishRecording(void);
void ResetForNewFile(void);
- void HandleKeyframe(uint64_t frameNum, int64_t extra = 0);
+ void HandleKeyframe(int64_t extra);
void HandleTimestamps(int stream_id, int64_t pts, int64_t dts);
- void BufferedWrite(const TSPacket &tspacket);
+ void BufferedWrite(const TSPacket &tspacket, bool insert = false);
// MPEG TS "audio only" support
bool FindAudioKeyframes(const TSPacket *tspacket);
View
14 mythtv/libs/libmythtv/dvbrecorder.cpp
@@ -72,6 +72,13 @@ void DVBRecorder::Close(void)
LOG(VB_RECORD, LOG_INFO, LOC + "Close() -- end");
}
+void DVBRecorder::StartNewFile(void)
+{
+ // Make sure the first things in the file are a PAT & PMT
+ HandleSingleProgramPAT(_stream_data->PATSingleProgram(), true);
+ HandleSingleProgramPMT(_stream_data->PMTSingleProgram(), true);
+}
+
void DVBRecorder::run(void)
{
if (!Open())
@@ -92,12 +99,7 @@ void DVBRecorder::run(void)
if (_channel && (_channel->GetSIStandard() == "dvb"))
_stream_data->AddListeningPID(DVB_TDT_PID);
- // Make sure the first things in the file are a PAT & PMT
- bool tmp = _wait_for_keyframe_option;
- _wait_for_keyframe_option = false;
- HandleSingleProgramPAT(_stream_data->PATSingleProgram());
- HandleSingleProgramPMT(_stream_data->PMTSingleProgram());
- _wait_for_keyframe_option = tmp;
+ StartNewFile();
_stream_data->AddAVListener(this);
_stream_data->AddWritingListener(this);
View
1 mythtv/libs/libmythtv/dvbrecorder.h
@@ -28,6 +28,7 @@ class DVBRecorder : public DTVRecorder
bool Open(void);
bool IsOpen(void) const;
void Close(void);
+ void StartNewFile(void);
private:
bool PauseAndWait(int timeout = 100);
View
14 mythtv/libs/libmythtv/hdhrrecorder.cpp
@@ -47,6 +47,13 @@ void HDHRRecorder::Close(void)
LOG(VB_RECORD, LOG_INFO, LOC + "Close() -- end");
}
+void HDHRRecorder::StartNewFile(void)
+{
+ // Make sure the first things in the file are a PAT & PMT
+ HandleSingleProgramPAT(_stream_data->PATSingleProgram(), true);
+ HandleSingleProgramPMT(_stream_data->PMTSingleProgram(), true);
+}
+
void HDHRRecorder::run(void)
{
LOG(VB_RECORD, LOG_INFO, LOC + "run -- begin");
@@ -66,12 +73,7 @@ void HDHRRecorder::run(void)
recordingWait.wakeAll();
}
- // Make sure the first things in the file are a PAT & PMT
- bool tmp = _wait_for_keyframe_option;
- _wait_for_keyframe_option = false;
- HandleSingleProgramPAT(_stream_data->PATSingleProgram());
- HandleSingleProgramPMT(_stream_data->PMTSingleProgram());
- _wait_for_keyframe_option = tmp;
+ StartNewFile();
_stream_data->AddAVListener(this);
_stream_data->AddWritingListener(this);
View
1 mythtv/libs/libmythtv/hdhrrecorder.h
@@ -26,6 +26,7 @@ class HDHRRecorder : public DTVRecorder
bool Open(void);
bool IsOpen(void) const { return _stream_handler; }
void Close(void);
+ void StartNewFile(void);
QString GetSIStandard(void) const;
View
8 mythtv/libs/libmythtv/mpeg/mpegstreamdata.cpp
@@ -836,7 +836,7 @@ void MPEGStreamData::ProcessPAT(const ProgramAssociationTable *pat)
QMutexLocker locker(&_listener_lock);
ProgramAssociationTable *pat_sp = PATSingleProgram();
for (uint i = 0; i < _mpeg_sp_listeners.size(); i++)
- _mpeg_sp_listeners[i]->HandleSingleProgramPAT(pat_sp);
+ _mpeg_sp_listeners[i]->HandleSingleProgramPAT(pat_sp, false);
}
}
@@ -871,7 +871,7 @@ void MPEGStreamData::ProcessPMT(const ProgramMapTable *pmt)
QMutexLocker locker(&_listener_lock);
ProgramMapTable *pmt_sp = PMTSingleProgram();
for (uint i = 0; i < _mpeg_sp_listeners.size(); i++)
- _mpeg_sp_listeners[i]->HandleSingleProgramPMT(pmt_sp);
+ _mpeg_sp_listeners[i]->HandleSingleProgramPMT(pmt_sp, false);
}
}
@@ -982,15 +982,15 @@ void MPEGStreamData::HandleTSTables(const TSPacket* tspacket)
QMutexLocker locker(&_listener_lock);
ProgramAssociationTable *pat_sp = PATSingleProgram();
for (uint i = 0; i < _mpeg_sp_listeners.size(); i++)
- _mpeg_sp_listeners[i]->HandleSingleProgramPAT(pat_sp);
+ _mpeg_sp_listeners[i]->HandleSingleProgramPAT(pat_sp, false);
}
if (TableID::PMT == psip->TableID() &&
tspacket->PID() == _pid_pmt_single_program)
{
QMutexLocker locker(&_listener_lock);
ProgramMapTable *pmt_sp = PMTSingleProgram();
for (uint i = 0; i < _mpeg_sp_listeners.size(); i++)
- _mpeg_sp_listeners[i]->HandleSingleProgramPMT(pmt_sp);
+ _mpeg_sp_listeners[i]->HandleSingleProgramPMT(pmt_sp, false);
}
DONE_WITH_PSIP_PACKET(); // already parsed this table, toss it.
}
View
5 mythtv/libs/libmythtv/mpeg/streamlisteners.h
@@ -95,8 +95,9 @@ class MPEGSingleProgramStreamListener
protected:
virtual ~MPEGSingleProgramStreamListener() {}
public:
- virtual void HandleSingleProgramPAT(ProgramAssociationTable*) = 0;
- virtual void HandleSingleProgramPMT(ProgramMapTable*) = 0;
+ virtual void HandleSingleProgramPAT(ProgramAssociationTable*,
+ bool insert) = 0;
+ virtual void HandleSingleProgramPMT(ProgramMapTable*, bool insert) = 0;
};
class ATSCMainStreamListener
View
9 mythtv/libs/libmythtv/mpegrecorder.cpp
@@ -929,9 +929,8 @@ void MpegRecorder::run(void)
_stream_data->AddWritingListener(this);
// Make sure the first things in the file are a PAT & PMT
- _wait_for_keyframe_option = false;
- HandleSingleProgramPAT(_stream_data->PATSingleProgram());
- HandleSingleProgramPMT(_stream_data->PMTSingleProgram());
+ HandleSingleProgramPAT(_stream_data->PATSingleProgram(), true);
+ HandleSingleProgramPMT(_stream_data->PMTSingleProgram(), true);
_wait_for_keyframe_option = true;
}
@@ -1256,8 +1255,8 @@ void MpegRecorder::RestartEncoding(void)
_stream_data->PMTSingleProgram())
{
_wait_for_keyframe_option = false;
- HandleSingleProgramPAT(_stream_data->PATSingleProgram());
- HandleSingleProgramPMT(_stream_data->PMTSingleProgram());
+ HandleSingleProgramPAT(_stream_data->PATSingleProgram(), false);
+ HandleSingleProgramPMT(_stream_data->PMTSingleProgram(), false);
}
if (driver == "hdpvr") // HD-PVR will sometimes reset to defaults

0 comments on commit 71ad653

Please sign in to comment.