1010
1111#include " libspdl/core/detail/ffmpeg/ctx_utils.h"
1212#include " libspdl/core/detail/ffmpeg/logging.h"
13+ #include " libspdl/core/detail/ffmpeg/rational_utils.h"
1314#include " libspdl/core/detail/tracing.h"
1415
1516#include < glog/logging.h>
@@ -142,6 +143,10 @@ Rational DecoderImpl<media>::get_output_time_base() const {
142143 return {codec_ctx->time_base .num , codec_ctx->time_base .den };
143144}
144145
146+ // For audio and image.
147+ // Note: when decoding audio with timestamp, we rely on `atrim` filter
148+ // for handling timestamp.
149+ // This is handled through high-level Python interface.
145150template <MediaType media>
146151FramesPtr<media> DecoderImpl<media>::decode_and_flush(
147152 PacketsPtr<media> packets,
@@ -161,6 +166,55 @@ FramesPtr<media> DecoderImpl<media>::decode_and_flush(
161166 return ret;
162167}
163168
169+ // Specialization for video.
170+ // For video we want to ensure the half-open range.
171+ // Originally we used `trim` filter like how audio is processed above,
172+ // but this was not properly handling the half-open range, so we have
173+ // specialization for video.
174+ template <>
175+ VideoFramesPtr DecoderImpl<MediaType::Video>::decode_and_flush(
176+ VideoPacketsPtr packets,
177+ int num_frames) {
178+ auto time_base = get_output_time_base ();
179+ auto [has_timestamp, start, end] =
180+ [&]() -> std::tuple<bool , AVRational, AVRational> {
181+ if (packets->timestamp ) {
182+ auto [start_time, end_time] = *packets->timestamp ;
183+ return std::make_tuple (
184+ true ,
185+ av_d2q (start_time, AV_TIME_BASE ),
186+ av_d2q (end_time, AV_TIME_BASE ));
187+ }
188+ return std::make_tuple (false , AVRational{-1 , 0 }, AVRational{1 , 0 });
189+ }();
190+
191+ auto ret = std::make_unique<VideoFrames>(packets->id , get_output_time_base ());
192+ auto gen = decode_packets (
193+ codec_ctx, packets->pkts .get_packets (), filter_graph, true );
194+ int num_yielded = 0 ;
195+ while (gen) {
196+ // For video, we manualy apply timestamps.
197+ auto frame = gen ().release ();
198+ if (has_timestamp && frame) {
199+ auto frame_pts = detail::to_rational (frame->pts , time_base);
200+ if (!is_within_window (frame_pts, start, end)) {
201+ av_frame_free (&frame);
202+ continue ;
203+ }
204+ }
205+ ret->push_back (frame);
206+ num_yielded += 1 ;
207+ if (num_frames > 0 && num_yielded >= num_frames) {
208+ break ;
209+ }
210+ }
211+ return ret;
212+ }
213+
214+ // For audio and image.
215+ // Note: when decoding audio with timestamp, we rely on `atrim` filter
216+ // for handling timestamp.
217+ // This is handled through high-level Python interface.
164218template <MediaType media>
165219FramesPtr<media> DecoderImpl<media>::decode(PacketsPtr<media> packets) {
166220 auto ret =
@@ -173,6 +227,47 @@ FramesPtr<media> DecoderImpl<media>::decode(PacketsPtr<media> packets) {
173227 return ret;
174228}
175229
230+ // Specialization for video.
231+ // For video we want to ensure the half-open range.
232+ // Originally we used `trim` filter like how audio is processed above,
233+ // but this was not properly handling the half-open range, so we have
234+ // specialization for video.
235+ template <>
236+ VideoFramesPtr DecoderImpl<MediaType::Video>::decode(VideoPacketsPtr packets) {
237+ auto ret = std::make_unique<VideoFrames>(packets->id , get_output_time_base ());
238+ auto gen = decode_packets (
239+ codec_ctx, packets->pkts .get_packets (), filter_graph, false );
240+
241+ auto time_base = get_output_time_base ();
242+ AVRational start = {0 , 1 };
243+ AVRational end = {0 , 1 };
244+ bool has_timestamp = false ;
245+
246+ if (packets->timestamp ) {
247+ auto [start_time, end_time] = *packets->timestamp ;
248+ start = av_d2q (start_time, AV_TIME_BASE );
249+ end = av_d2q (end_time, AV_TIME_BASE );
250+ has_timestamp = true ;
251+ }
252+
253+ while (gen) {
254+ auto frame = gen ().release ();
255+
256+ if (has_timestamp && frame) {
257+ auto frame_pts = AVRational{
258+ static_cast <int >(frame->pts * time_base.num ), time_base.den };
259+
260+ if (!is_within_window (frame_pts, start, end)) {
261+ av_frame_free (&frame);
262+ continue ;
263+ }
264+ }
265+
266+ ret->push_back (frame);
267+ }
268+ return ret;
269+ }
270+
176271template <MediaType media>
177272FramesPtr<media> DecoderImpl<media>::flush() {
178273 auto ret = std::make_unique<Frames<media>>(
0 commit comments