@@ -844,7 +844,8 @@ FrameBatchOutput SingleStreamDecoder::getFramesPlayedAt(
844844
845845FrameBatchOutput SingleStreamDecoder::getFramesPlayedInRange (
846846 double startSeconds,
847- double stopSeconds) {
847+ double stopSeconds,
848+ std::optional<double > fps) {
848849 validateActiveStream (AVMEDIA_TYPE_VIDEO );
849850 const auto & streamMetadata =
850851 containerMetadata_.allStreamMetadata [activeStreamIndex_];
@@ -906,36 +907,77 @@ FrameBatchOutput SingleStreamDecoder::getFramesPlayedInRange(
906907 std::to_string (maxSeconds.value ()) + " )." );
907908 }
908909
909- // Note that we look at nextPts for a frame, and not its pts or duration.
910- // Our abstract player displays frames starting at the pts for that frame
911- // until the pts for the next frame. There are two consequences:
912- //
913- // 1. We ignore the duration for a frame. A frame is played until the
914- // next frame replaces it. This model is robust to durations being 0 or
915- // incorrect; our source of truth is the pts for frames. If duration is
916- // accurate, the nextPts for a frame would be equivalent to pts +
917- // duration.
918- // 2. In order to establish if the start of an interval maps to a
919- // particular frame, we need to figure out if it is ordered after the
920- // frame's pts, but before the next frames's pts.
921-
922- int64_t startFrameIndex = secondsToIndexLowerBound (startSeconds);
923- int64_t stopFrameIndex = secondsToIndexUpperBound (stopSeconds);
924- int64_t numFrames = stopFrameIndex - startFrameIndex;
910+ // Resample frames to match the target frame rate
911+ if (fps.has_value ()) {
912+ TORCH_CHECK (
913+ fps.value () > 0 ,
914+ " fps must be positive, got " + std::to_string (fps.value ()));
925915
926- FrameBatchOutput frameBatchOutput (
927- numFrames,
928- resizedOutputDims_.value_or (metadataDims_),
929- videoStreamOptions.device );
930- for (int64_t i = startFrameIndex, f = 0 ; i < stopFrameIndex; ++i, ++f) {
931- FrameOutput frameOutput =
932- getFrameAtIndexInternal (i, frameBatchOutput.data [f]);
933- frameBatchOutput.ptsSeconds [f] = frameOutput.ptsSeconds ;
934- frameBatchOutput.durationSeconds [f] = frameOutput.durationSeconds ;
935- }
936- frameBatchOutput.data = maybePermuteHWC2CHW (frameBatchOutput.data );
916+ // TODO: add an early break if requested fps is the same as the current fps
937917
938- return frameBatchOutput;
918+ double fpsVal = fps.value ();
919+ double frameDurationSeconds = 1.0 / fpsVal;
920+
921+ double product = (stopSeconds - startSeconds) * fpsVal;
922+ int64_t numOutputFrames = static_cast <int64_t >(std::round (product));
923+
924+ FrameBatchOutput frameBatchOutput (
925+ numOutputFrames,
926+ resizedOutputDims_.value_or (metadataDims_),
927+ videoStreamOptions.device );
928+
929+ // Decode frames, reusing already-decoded frames for duplicates
930+ int64_t lastDecodedSourceIndex = -1 ;
931+
932+ for (int64_t i = 0 ; i < numOutputFrames; ++i) {
933+ double targetPtsSeconds = startSeconds + i * frameDurationSeconds;
934+ int64_t sourceIdx = secondsToIndexLowerBound (targetPtsSeconds);
935+
936+ if (sourceIdx == lastDecodedSourceIndex && lastDecodedSourceIndex >= 0 ) {
937+ frameBatchOutput.data [i].copy_ (frameBatchOutput.data [i - 1 ]);
938+ } else {
939+ getFrameAtIndexInternal (sourceIdx, frameBatchOutput.data [i]);
940+ lastDecodedSourceIndex = sourceIdx;
941+ }
942+
943+ frameBatchOutput.ptsSeconds [i] = targetPtsSeconds;
944+ frameBatchOutput.durationSeconds [i] = frameDurationSeconds;
945+ }
946+
947+ frameBatchOutput.data = maybePermuteHWC2CHW (frameBatchOutput.data );
948+ return frameBatchOutput;
949+ } else {
950+ // Note that we look at nextPts for a frame, and not its pts or duration.
951+ // Our abstract player displays frames starting at the pts for that frame
952+ // until the pts for the next frame. There are two consequences:
953+ //
954+ // 1. We ignore the duration for a frame. A frame is played until the
955+ // next frame replaces it. This model is robust to durations being 0 or
956+ // incorrect; our source of truth is the pts for frames. If duration is
957+ // accurate, the nextPts for a frame would be equivalent to pts +
958+ // duration.
959+ // 2. In order to establish if the start of an interval maps to a
960+ // particular frame, we need to figure out if it is ordered after the
961+ // frame's pts, but before the next frames's pts.
962+
963+ int64_t startFrameIndex = secondsToIndexLowerBound (startSeconds);
964+ int64_t stopFrameIndex = secondsToIndexUpperBound (stopSeconds);
965+ int64_t numFrames = stopFrameIndex - startFrameIndex;
966+
967+ FrameBatchOutput frameBatchOutput (
968+ numFrames,
969+ resizedOutputDims_.value_or (metadataDims_),
970+ videoStreamOptions.device );
971+ for (int64_t i = startFrameIndex, f = 0 ; i < stopFrameIndex; ++i, ++f) {
972+ FrameOutput frameOutput =
973+ getFrameAtIndexInternal (i, frameBatchOutput.data [f]);
974+ frameBatchOutput.ptsSeconds [f] = frameOutput.ptsSeconds ;
975+ frameBatchOutput.durationSeconds [f] = frameOutput.durationSeconds ;
976+ }
977+ frameBatchOutput.data = maybePermuteHWC2CHW (frameBatchOutput.data );
978+
979+ return frameBatchOutput;
980+ }
939981}
940982
941983// Note [Audio Decoding Design]
0 commit comments