@@ -236,67 +236,86 @@ void FFmpegDemuxer::Flush() {
236
236
avformat_flush (fmtc);
237
237
}
238
238
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
+
242
269
if (!is_seekable) {
243
270
cerr << " Seek isn't supported for this input." << endl;
244
271
return false ;
245
272
}
246
273
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
+ }
261
280
262
281
// Seek for single frame;
263
- auto seek_frame = [&](SeekContext const & seek_ctx, int flags) {
282
+ auto seek_frame = [&](SeekContext const & seek_ctx, int flags) {
264
283
bool seek_backward = false ;
284
+ int64_t timestamp = 0 ;
265
285
int ret = 0 ;
266
286
267
287
switch (seek_ctx.crit ) {
268
288
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,
273
292
seek_backward ? AVSEEK_FLAG_BACKWARD | flags : flags);
274
- if (ret < 0 )
275
- throw runtime_error (" Error seeking for frame: " + AvErrorToString (ret));
276
293
break ;
277
294
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,
282
298
seek_backward ? AVSEEK_FLAG_BACKWARD | flags : flags);
283
299
break ;
284
300
default :
285
301
throw runtime_error (" Invalid seek mode" );
286
302
}
287
- return ;
303
+
304
+ if (ret < 0 ) {
305
+ throw runtime_error (" Error seeking for frame: " + AvErrorToString (ret));
306
+ }
288
307
};
289
308
290
309
// 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) {
292
311
int64_t target_ts = 0 ;
293
312
294
313
switch (seek_ctx.crit ) {
295
314
case BY_NUMBER:
296
- target_ts = ts_from_num (seek_ctx.seek_frame );
315
+ target_ts = TsFromFrameNumber (seek_ctx.seek_frame );
297
316
break ;
298
317
case BY_TIMESTAMP:
299
- target_ts = ts_from_time (seek_ctx.seek_frame );
318
+ target_ts = TsFromTime (seek_ctx.seek_frame );
300
319
break ;
301
320
default :
302
321
throw runtime_error (" Invalid seek criteria" );
@@ -312,17 +331,18 @@ bool FFmpegDemuxer::Seek(SeekContext &seekCtx, uint8_t *&pVideo,
312
331
};
313
332
};
314
333
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) {
319
337
// Repetititive seek until seek condition is satisfied;
320
338
SeekContext tmp_ctx (seek_ctx.seek_frame );
321
339
seek_frame (tmp_ctx, AVSEEK_FLAG_ANY);
322
340
323
341
int condition = 0 ;
324
342
do {
325
- Demux (pVideo, rVideoBytes, pkt_data, ppSEI, pSEIBytes);
343
+ if (!Demux (pVideo, rVideoBytes, pkt_data, ppSEI, pSEIBytes)) {
344
+ break ;
345
+ }
326
346
condition = is_seek_done (pkt_data, seek_ctx);
327
347
328
348
// We've gone too far and need to seek backwards;
@@ -341,11 +361,9 @@ bool FFmpegDemuxer::Seek(SeekContext &seekCtx, uint8_t *&pVideo,
341
361
};
342
362
343
363
// 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);
349
367
350
368
Demux (pVideo, rVideoBytes, pkt_data, ppSEI, pSEIBytes);
351
369
seek_ctx.out_frame_pts = pkt_data.pts ;
0 commit comments