diff options
| author | Carl Hetherington <cth@carlh.net> | 2026-02-21 02:37:42 +0100 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2026-02-21 02:37:42 +0100 |
| commit | c2adc362e4ce0a64d7e7a1c6b634b024d862c8c1 (patch) | |
| tree | 1cce02f73de9aed44e25add73606b59485d1dbea | |
| parent | 6c562ac01da72e8b296a360b50642175b6430821 (diff) | |
Handle FFmpeg content with only subtitles enabled better.
Previously if some FFmpeg content was set to use only its subtitles, and
the first subtitle was 1 minute in to the content, the content would be
selected for pass() until its first minute had been scanned.
This could be slow, and incorrect as on the way to this 1 minute we want
to look at other content.
Here we store the PTS of dropped packets to use as a fallback position
when there is no video or audio enabled.
| -rw-r--r-- | src/lib/ffmpeg_decoder.cc | 40 | ||||
| -rw-r--r-- | src/lib/ffmpeg_decoder.h | 3 |
2 files changed, 43 insertions, 0 deletions
diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc index ec85bb56c..294db777d 100644 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@ -102,6 +102,8 @@ FFmpegDecoder::FFmpegDecoder (shared_ptr<const Film> film, shared_ptr<const FFmp for (auto i: c->ffmpeg_audio_streams()) { _next_time[i] = boost::optional<dcpomatic::ContentTime>(); } + + _dropped_time.resize(_format_context->nb_streams); } @@ -240,6 +242,9 @@ FFmpegDecoder::pass () decode_and_process_subtitle_packet (packet); } else if (audio) { decode_and_process_audio_packet (packet); + } else { + DCPOMATIC_ASSERT(static_cast<int>(_dropped_time.size()) > si); + _dropped_time[si] = dcpomatic::ContentTime::from_seconds(packet->pts * av_q2d(_format_context->streams[si]->time_base) + _pts_offset.seconds()); } if (_have_current_subtitle && _current_subtitle_to && position() > *_current_subtitle_to) { @@ -466,6 +471,10 @@ FFmpegDecoder::seek (ContentTime time, bool accurate) for (auto& i: _next_time) { i.second = boost::optional<dcpomatic::ContentTime>(); } + + for (auto& dropped: _dropped_time) { + dropped = boost::none; + } } @@ -846,3 +855,34 @@ FFmpegDecoder::process_ass_subtitle (string ass, ContentTime from) only_text()->emit_plain_start (from, i); } } + + +ContentTime +FFmpegDecoder::position() const +{ + optional<ContentTime> pos; + auto f = film(); + + if (video && !video->ignore() && (!pos || video->position(f).get_value_or(ContentTime()) < *pos)) { + pos = video->position(f); + } + + if (audio && !audio->ignore() && (!pos || audio->position(f).get_value_or(ContentTime()) < *pos)) { + pos = audio->position(f); + } + + if (!pos) { + for (auto dropped: _dropped_time) { + if (dropped) { + if (pos) { + pos = std::min(*pos, *dropped); + } else { + pos = *dropped; + } + } + } + } + + return pos.get_value_or(ContentTime()); +} + diff --git a/src/lib/ffmpeg_decoder.h b/src/lib/ffmpeg_decoder.h index bd4b74f88..022c5625b 100644 --- a/src/lib/ffmpeg_decoder.h +++ b/src/lib/ffmpeg_decoder.h @@ -53,6 +53,7 @@ public: bool pass () override; void seek (dcpomatic::ContentTime time, bool) override; + dcpomatic::ContentTime position() const override; private: friend struct ::ffmpeg_pts_offset_test; @@ -102,4 +103,6 @@ private: }; FlushState _flush_state = FlushState::CODECS; + + std::vector<boost::optional<dcpomatic::ContentTime>> _dropped_time; }; |
