Skip to content
This repository was archived by the owner on Jun 10, 2024. It is now read-only.

Commit e7ea766

Browse files
committed
Cleaning up seek code.
Replacing pts calculation based on duration with that based on fps for higher precision
1 parent ec453ad commit e7ea766

File tree

7 files changed

+180
-112
lines changed

7 files changed

+180
-112
lines changed

PyNvCodec/TC/inc/FFmpegDemuxer.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,10 @@ class DllExport FFmpegDemuxer
222222

223223
double GetTimebase() const;
224224

225+
int64_t TsFromTime(double ts_sec);
226+
227+
int64_t TsFromFrameNumber(int64_t frame_num);
228+
225229
uint32_t GetVideoStreamIndex() const;
226230

227231
AVPixelFormat GetPixelFormat() const;

PyNvCodec/TC/inc/Tasks.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,8 @@ class DllExport DemuxFrame final : public Task
221221
DemuxFrame& operator=(const DemuxFrame& other) = delete;
222222

223223
void GetParams(struct MuxingParams& params) const;
224+
int64_t TsFromTime(double ts_sec);
225+
int64_t TsFromFrameNumber(int64_t frame_num);
224226
void Flush();
225227
TaskExecStatus Run() final;
226228
~DemuxFrame() final;

PyNvCodec/TC/src/FFmpegDemuxer.cpp

Lines changed: 60 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -236,67 +236,86 @@ void FFmpegDemuxer::Flush() {
236236
avformat_flush(fmtc);
237237
}
238238

239-
bool FFmpegDemuxer::Seek(SeekContext &seekCtx, uint8_t *&pVideo,
240-
size_t &rVideoBytes, PacketData &pktData,
241-
uint8_t **ppSEI, size_t *pSEIBytes) {
239+
int64_t FFmpegDemuxer::TsFromTime(double ts_sec)
240+
{
241+
/* Internal timestamp representation is integer, so multiply to AV_TIME_BASE
242+
* and switch to fixed point precision arithmetics; */
243+
auto const ts_tbu = lround(ts_sec * AV_TIME_BASE);
244+
245+
// Rescale the timestamp to value represented in stream base units;
246+
AVRational factor;
247+
factor.num = 1;
248+
factor.den = AV_TIME_BASE;
249+
return av_rescale_q(ts_tbu, factor, fmtc->streams[videoStream]->time_base);
250+
}
251+
252+
int64_t FFmpegDemuxer::TsFromFrameNumber(int64_t frame_num)
253+
{
254+
auto const ts_sec = (double)frame_num / GetFramerate();
255+
return TsFromTime(ts_sec);
256+
}
257+
258+
bool FFmpegDemuxer::Seek(SeekContext& seekCtx, uint8_t*& pVideo,
259+
size_t& rVideoBytes, PacketData& pktData,
260+
uint8_t** ppSEI, size_t* pSEIBytes)
261+
{
262+
/* !!! IMPORTANT !!!
263+
* Across this function packet decode timestamp (DTS) values are used to
264+
* compare given timestamp against. This is done for reason. DTS values shall
265+
* monotonically increase during the course of decoding unlike PTS velues
266+
* which may be affected by frame reordering due to B frames presence.
267+
*/
268+
242269
if (!is_seekable) {
243270
cerr << "Seek isn't supported for this input." << endl;
244271
return false;
245272
}
246273

247-
// Convert timestamp in time units to timestamp in stream base units;
248-
auto ts_from_time = [&](double ts_sec) {
249-
auto const ts_tbu = (int64_t)(ts_sec * AV_TIME_BASE);
250-
AVRational factor;
251-
factor.num = 1;
252-
factor.den = AV_TIME_BASE;
253-
return av_rescale_q(ts_tbu, factor, fmtc->streams[videoStream]->time_base);
254-
};
255-
256-
// Convert frame number to timestamp;
257-
auto ts_from_num = [&](int64_t frame_num) {
258-
auto const ts_sec = (double)seekCtx.seek_frame / GetFramerate();
259-
return ts_from_time(ts_sec);
260-
};
274+
if (IsVFR() && (BY_NUMBER == seekCtx.crit)) {
275+
cerr << "Can't seek by frame number in VFR sequences. Seek by timestamp "
276+
"instead."
277+
<< endl;
278+
return false;
279+
}
261280

262281
// Seek for single frame;
263-
auto seek_frame = [&](SeekContext const &seek_ctx, int flags) {
282+
auto seek_frame = [&](SeekContext const& seek_ctx, int flags) {
264283
bool seek_backward = false;
284+
int64_t timestamp = 0;
265285
int ret = 0;
266286

267287
switch (seek_ctx.crit) {
268288
case BY_NUMBER:
269-
seek_backward =
270-
last_packet_data.dts > seek_ctx.seek_frame * pktDst.duration;
271-
ret = av_seek_frame(fmtc, GetVideoStreamIndex(),
272-
ts_from_num(seek_ctx.seek_frame),
289+
timestamp = TsFromFrameNumber(seek_ctx.seek_frame);
290+
seek_backward = last_packet_data.dts > timestamp;
291+
ret = av_seek_frame(fmtc, GetVideoStreamIndex(), timestamp,
273292
seek_backward ? AVSEEK_FLAG_BACKWARD | flags : flags);
274-
if (ret < 0)
275-
throw runtime_error("Error seeking for frame: " + AvErrorToString(ret));
276293
break;
277294
case BY_TIMESTAMP:
278-
seek_backward =
279-
last_packet_data.dts > seek_ctx.seek_frame;
280-
ret = av_seek_frame(fmtc, GetVideoStreamIndex(),
281-
ts_from_time(seek_ctx.seek_frame),
295+
timestamp = TsFromTime(seek_ctx.seek_frame);
296+
seek_backward = last_packet_data.dts > timestamp;
297+
ret = av_seek_frame(fmtc, GetVideoStreamIndex(), timestamp,
282298
seek_backward ? AVSEEK_FLAG_BACKWARD | flags : flags);
283299
break;
284300
default:
285301
throw runtime_error("Invalid seek mode");
286302
}
287-
return;
303+
304+
if (ret < 0) {
305+
throw runtime_error("Error seeking for frame: " + AvErrorToString(ret));
306+
}
288307
};
289308

290309
// Check if frame satisfies seek conditions;
291-
auto is_seek_done = [&](PacketData &pkt_data, SeekContext const &seek_ctx) {
310+
auto is_seek_done = [&](PacketData& pkt_data, SeekContext const& seek_ctx) {
292311
int64_t target_ts = 0;
293312

294313
switch (seek_ctx.crit) {
295314
case BY_NUMBER:
296-
target_ts = ts_from_num(seek_ctx.seek_frame);
315+
target_ts = TsFromFrameNumber(seek_ctx.seek_frame);
297316
break;
298317
case BY_TIMESTAMP:
299-
target_ts = ts_from_time(seek_ctx.seek_frame);
318+
target_ts = TsFromTime(seek_ctx.seek_frame);
300319
break;
301320
default:
302321
throw runtime_error("Invalid seek criteria");
@@ -312,17 +331,18 @@ bool FFmpegDemuxer::Seek(SeekContext &seekCtx, uint8_t *&pVideo,
312331
};
313332
};
314333

315-
// This will seek for exact frame number;
316-
// Note that decoder may not be able to decode such frame;
317-
auto seek_for_exact_frame = [&](PacketData &pkt_data,
318-
SeekContext &seek_ctx) {
334+
/* This will seek for exact frame number;
335+
* Note that decoder may not be able to decode such frame; */
336+
auto seek_for_exact_frame = [&](PacketData& pkt_data, SeekContext& seek_ctx) {
319337
// Repetititive seek until seek condition is satisfied;
320338
SeekContext tmp_ctx(seek_ctx.seek_frame);
321339
seek_frame(tmp_ctx, AVSEEK_FLAG_ANY);
322340

323341
int condition = 0;
324342
do {
325-
Demux(pVideo, rVideoBytes, pkt_data, ppSEI, pSEIBytes);
343+
if (!Demux(pVideo, rVideoBytes, pkt_data, ppSEI, pSEIBytes)) {
344+
break;
345+
}
326346
condition = is_seek_done(pkt_data, seek_ctx);
327347

328348
// We've gone too far and need to seek backwards;
@@ -341,11 +361,9 @@ bool FFmpegDemuxer::Seek(SeekContext &seekCtx, uint8_t *&pVideo,
341361
};
342362

343363
// Seek for closest key frame in the past;
344-
auto seek_for_prev_key_frame = [&](PacketData &pkt_data,
345-
SeekContext &seek_ctx) {
346-
// Repetititive seek until seek condition is satisfied;
347-
auto tmp_ctx = seek_ctx;
348-
seek_frame(tmp_ctx, AVSEEK_FLAG_BACKWARD);
364+
auto seek_for_prev_key_frame = [&](PacketData& pkt_data,
365+
SeekContext& seek_ctx) {
366+
seek_frame(seek_ctx, AVSEEK_FLAG_BACKWARD);
349367

350368
Demux(pVideo, rVideoBytes, pkt_data, ppSEI, pSEIBytes);
351369
seek_ctx.out_frame_pts = pkt_data.pts;

PyNvCodec/TC/src/NvDecoder.cpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ struct Dim {
145145

146146
struct NvDecoderImpl {
147147
bool m_bReconfigExternal = false, m_bReconfigExtPPChange = false,
148-
eos_set = false;
148+
eos_set = false, decoder_recon = false;
149149

150150
unsigned int m_nWidth = 0U, m_nLumaHeight = 0U, m_nChromaHeight = 0U,
151151
m_nNumChromaPlanes = 0U, m_nMaxWidth = 0U, m_nMaxHeight = 0U;
@@ -195,6 +195,7 @@ cudaVideoCodec NvDecoder::GetCodec() const { return p_impl->m_eCodec; }
195195
int NvDecoder::HandleVideoSequence(CUVIDEOFORMAT* pVideoFormat) noexcept
196196
{
197197
try {
198+
p_impl->decoder_recon = true;
198199
CudaCtxPush ctxPush(p_impl->m_cuContext);
199200
CudaStrSync strSync(p_impl->m_cuvidStream);
200201

@@ -753,9 +754,19 @@ bool NvDecoder::DecodeLockSurface(Buffer const* encFrame,
753754
*/
754755
auto ret = false;
755756

756-
// Prepare black packet data in case no frames are decoded yet;
757+
// Prepare blank packet data in case no frames are decoded yet;
757758
memset(&decCtx.out_pdata, 0, sizeof(decCtx.out_pdata));
758759

760+
/* In case decoder was reconfigured by cuvidParseVideoData() call made above,
761+
* some previously decoded frames could have been pushed to decoded frames
762+
* queue. Need to clean them up; */
763+
if (p_impl->decoder_recon) {
764+
p_impl->decoder_recon = false;
765+
while (!p_impl->m_DecFramesCtxQueue.empty()) {
766+
p_impl->m_DecFramesCtxQueue.pop();
767+
}
768+
}
769+
759770
if (!p_impl->m_DecFramesCtxQueue.empty()) {
760771
decCtx = p_impl->m_DecFramesCtxQueue.front();
761772
p_impl->m_DecFramesCtxQueue.pop();

PyNvCodec/TC/src/Tasks.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -875,6 +875,16 @@ DemuxFrame::~DemuxFrame() { delete pImpl; }
875875

876876
void DemuxFrame::Flush() { pImpl->demuxer->Flush(); }
877877

878+
int64_t DemuxFrame::TsFromTime(double ts_sec)
879+
{
880+
return pImpl->demuxer->TsFromTime(ts_sec);
881+
}
882+
883+
int64_t DemuxFrame::TsFromFrameNumber(int64_t frame_num)
884+
{
885+
return pImpl->demuxer->TsFromFrameNumber(frame_num);
886+
}
887+
878888
TaskExecStatus DemuxFrame::Run()
879889
{
880890
NvtxMark tick(GetName());

PyNvCodec/src/PyNvDecoder.cpp

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -469,8 +469,7 @@ bool PyNvDecoder::DecodeSurface(DecodeContext& ctx)
469469
Surface* p_surf = nullptr;
470470
do {
471471
try {
472-
auto const no_eos = true;
473-
p_surf = getDecodedSurfaceFromPacket(nullptr, nullptr, no_eos);
472+
p_surf = getDecodedSurfaceFromPacket(nullptr, nullptr);
474473
} catch (decoder_error& dec_exc) {
475474
dec_error = true;
476475
cerr << dec_exc.what() << endl;
@@ -521,8 +520,7 @@ bool PyNvDecoder::DecodeSurface(DecodeContext& ctx)
521520
ctx.SetOutPacketData(pktDataBuf->GetDataAs<PacketData>());
522521
}
523522

524-
auto is_seek_done = [&](DecodeContext const& ctx, double time_base,
525-
double duration, double pts) {
523+
auto is_seek_done = [&](DecodeContext const& ctx, int64_t pts) {
526524
auto seek_ctx = ctx.GetSeekContext();
527525
if (!seek_ctx)
528526
throw runtime_error("No seek context.");
@@ -531,13 +529,11 @@ bool PyNvDecoder::DecodeSurface(DecodeContext& ctx)
531529

532530
switch (seek_ctx->crit) {
533531
case BY_NUMBER:
534-
seek_pts = seek_ctx->seek_frame * duration;
532+
seek_pts = upDemuxer->TsFromFrameNumber(seek_ctx->seek_frame);
535533
break;
536-
537534
case BY_TIMESTAMP:
538-
seek_pts = seek_ctx->seek_frame / time_base;
535+
seek_pts = upDemuxer->TsFromTime(seek_ctx->seek_frame);
539536
break;
540-
541537
default:
542538
throw runtime_error("Invalid seek criteria.");
543539
break;
@@ -549,22 +545,12 @@ bool PyNvDecoder::DecodeSurface(DecodeContext& ctx)
549545
/* Check if seek is done. */
550546
if (!use_seek) {
551547
loop_end = true;
552-
} else {
553-
MuxingParams params;
554-
upDemuxer->GetParams(params);
555-
556-
if (pktDataBuf) {
557-
auto out_pkt_data = pktDataBuf->GetDataAs<PacketData>();
558-
if (AV_NOPTS_VALUE == out_pkt_data->pts) {
559-
throw runtime_error(
560-
"Decoded frame doesn't have valid PTS, can't seek.");
561-
}
562-
if (!out_pkt_data->duration) {
563-
throw runtime_error("Decoded frames has zero duration, can't seek.");
564-
}
565-
loop_end = is_seek_done(ctx, params.videoContext.timeBase,
566-
out_pkt_data->duration, out_pkt_data->pts);
548+
} else if (pktDataBuf) {
549+
auto out_pkt_data = pktDataBuf->GetDataAs<PacketData>();
550+
if (AV_NOPTS_VALUE == out_pkt_data->pts) {
551+
throw runtime_error("Decoded frame doesn't have PTS, can't seek.");
567552
}
553+
loop_end = is_seek_done(ctx, out_pkt_data->pts);
568554
}
569555

570556
if (dmx_error) {

0 commit comments

Comments
 (0)