diff --git a/test/generate_reference_resources.sh b/test/generate_reference_resources.sh index fba098a7..7428edf8 100755 --- a/test/generate_reference_resources.sh +++ b/test/generate_reference_resources.sh @@ -77,3 +77,22 @@ do python3 "$TORCHCODEC_PATH/test/convert_image_to_tensor.py" "$bmp" rm -f "$bmp" done + +# This video was generated by running the following: +# ffmpeg -f lavfi -i testsrc=duration=10:size=128x128:rate=30 -vf "setpts=PTS*if(gt(N\,30)\,1.5\,1)" -c:v libx264 -bf 0 var_fps_video.mp4 +VIDEO_PATH=$RESOURCES_DIR/var_fps_video.mp4 +STREAMS=(0) + +FRAMES=(0 1 2 3 4 5 10 15 20 25 30 35 40 45 50 55 60) +for stream in "${STREAMS[@]}"; do + for frame in "${FRAMES[@]}"; do + frame_name=$(printf "%06d" "$frame") + ffmpeg -y -i "$VIDEO_PATH" -map 0:"$stream" -vf select="eq(n\,$frame)" -vsync vfr -q:v 2 "$VIDEO_PATH.stream$stream.frame$frame_name.bmp" + done +done + +for bmp in "$RESOURCES_DIR"/*.bmp +do + python3 "$TORCHCODEC_PATH/test/convert_image_to_tensor.py" "$bmp" + rm -f "$bmp" +done \ No newline at end of file diff --git a/test/resources/var_fps_video.mp4 b/test/resources/var_fps_video.mp4 new file mode 100644 index 00000000..529b0407 Binary files /dev/null and b/test/resources/var_fps_video.mp4 differ diff --git a/test/resources/var_fps_video.mp4.stream0.all_frames_info.json b/test/resources/var_fps_video.mp4.stream0.all_frames_info.json new file mode 100644 index 00000000..502477a9 --- /dev/null +++ b/test/resources/var_fps_video.mp4.stream0.all_frames_info.json @@ -0,0 +1,1202 @@ +[ + { + "pts_time": "0.000000", + "duration_time": "0.033333" + }, + { + "pts_time": "0.033333", + "duration_time": "0.033333" + }, + { + "pts_time": "0.066667", + "duration_time": "0.033333" + }, + { + "pts_time": "0.100000", + "duration_time": "0.033333" + }, + { + "pts_time": "0.133333", + "duration_time": "0.033333" + }, + { + "pts_time": "0.166667", + "duration_time": "0.033333" + }, + { + "pts_time": "0.200000", + "duration_time": "0.033333" + }, + { + "pts_time": "0.233333", + "duration_time": "0.033333" + }, + { + "pts_time": "0.266667", + "duration_time": "0.033333" + }, + { + "pts_time": "0.300000", + "duration_time": "0.033333" + }, + { + "pts_time": "0.333333", + "duration_time": "0.033333" + }, + { + "pts_time": "0.366667", + "duration_time": "0.033333" + }, + { + "pts_time": "0.400000", + "duration_time": "0.033333" + }, + { + "pts_time": "0.433333", + "duration_time": "0.033333" + }, + { + "pts_time": "0.466667", + "duration_time": "0.033333" + }, + { + "pts_time": "0.500000", + "duration_time": "0.033333" + }, + { + "pts_time": "0.533333", + "duration_time": "0.033333" + }, + { + "pts_time": "0.566667", + "duration_time": "0.033333" + }, + { + "pts_time": "0.600000", + "duration_time": "0.033333" + }, + { + "pts_time": "0.633333", + "duration_time": "0.033333" + }, + { + "pts_time": "0.666667", + "duration_time": "0.033333" + }, + { + "pts_time": "0.700000", + "duration_time": "0.033333" + }, + { + "pts_time": "0.733333", + "duration_time": "0.033333" + }, + { + "pts_time": "0.766667", + "duration_time": "0.033333" + }, + { + "pts_time": "0.800000", + "duration_time": "0.033333" + }, + { + "pts_time": "0.833333", + "duration_time": "0.033333" + }, + { + "pts_time": "0.866667", + "duration_time": "0.033333" + }, + { + "pts_time": "0.900000", + "duration_time": "0.033333" + }, + { + "pts_time": "0.933333", + "duration_time": "0.033333" + }, + { + "pts_time": "0.966667", + "duration_time": "0.033333" + }, + { + "pts_time": "1.000000", + "duration_time": "0.533333" + }, + { + "pts_time": "1.533333", + "duration_time": "0.066667" + }, + { + "pts_time": "1.600000", + "duration_time": "0.033333" + }, + { + "pts_time": "1.633333", + "duration_time": "0.066667" + }, + { + "pts_time": "1.700000", + "duration_time": "0.033333" + }, + { + "pts_time": "1.733333", + "duration_time": "0.066667" + }, + { + "pts_time": "1.800000", + "duration_time": "0.033333" + }, + { + "pts_time": "1.833333", + "duration_time": "0.066667" + }, + { + "pts_time": "1.900000", + "duration_time": "0.033333" + }, + { + "pts_time": "1.933333", + "duration_time": "0.066667" + }, + { + "pts_time": "2.000000", + "duration_time": "0.033333" + }, + { + "pts_time": "2.033333", + "duration_time": "0.066667" + }, + { + "pts_time": "2.100000", + "duration_time": "0.033333" + }, + { + "pts_time": "2.133333", + "duration_time": "0.066667" + }, + { + "pts_time": "2.200000", + "duration_time": "0.033333" + }, + { + "pts_time": "2.233333", + "duration_time": "0.066667" + }, + { + "pts_time": "2.300000", + "duration_time": "0.033333" + }, + { + "pts_time": "2.333333", + "duration_time": "0.066667" + }, + { + "pts_time": "2.400000", + "duration_time": "0.033333" + }, + { + "pts_time": "2.433333", + "duration_time": "0.066667" + }, + { + "pts_time": "2.500000", + "duration_time": "0.033333" + }, + { + "pts_time": "2.533333", + "duration_time": "0.066667" + }, + { + "pts_time": "2.600000", + "duration_time": "0.033333" + }, + { + "pts_time": "2.633333", + "duration_time": "0.066667" + }, + { + "pts_time": "2.700000", + "duration_time": "0.033333" + }, + { + "pts_time": "2.733333", + "duration_time": "0.066667" + }, + { + "pts_time": "2.800000", + "duration_time": "0.033333" + }, + { + "pts_time": "2.833333", + "duration_time": "0.066667" + }, + { + "pts_time": "2.900000", + "duration_time": "0.033333" + }, + { + "pts_time": "2.933333", + "duration_time": "0.066667" + }, + { + "pts_time": "3.000000", + "duration_time": "0.033333" + }, + { + "pts_time": "3.033333", + "duration_time": "0.066667" + }, + { + "pts_time": "3.100000", + "duration_time": "0.033333" + }, + { + "pts_time": "3.133333", + "duration_time": "0.066667" + }, + { + "pts_time": "3.200000", + "duration_time": "0.033333" + }, + { + "pts_time": "3.233333", + "duration_time": "0.066667" + }, + { + "pts_time": "3.300000", + "duration_time": "0.033333" + }, + { + "pts_time": "3.333333", + "duration_time": "0.066667" + }, + { + "pts_time": "3.400000", + "duration_time": "0.033333" + }, + { + "pts_time": "3.433333", + "duration_time": "0.066667" + }, + { + "pts_time": "3.500000", + "duration_time": "0.033333" + }, + { + "pts_time": "3.533333", + "duration_time": "0.066667" + }, + { + "pts_time": "3.600000", + "duration_time": "0.033333" + }, + { + "pts_time": "3.633333", + "duration_time": "0.066667" + }, + { + "pts_time": "3.700000", + "duration_time": "0.033333" + }, + { + "pts_time": "3.733333", + "duration_time": "0.066667" + }, + { + "pts_time": "3.800000", + "duration_time": "0.033333" + }, + { + "pts_time": "3.833333", + "duration_time": "0.066667" + }, + { + "pts_time": "3.900000", + "duration_time": "0.033333" + }, + { + "pts_time": "3.933333", + "duration_time": "0.066667" + }, + { + "pts_time": "4.000000", + "duration_time": "0.033333" + }, + { + "pts_time": "4.033333", + "duration_time": "0.066667" + }, + { + "pts_time": "4.100000", + "duration_time": "0.033333" + }, + { + "pts_time": "4.133333", + "duration_time": "0.066667" + }, + { + "pts_time": "4.200000", + "duration_time": "0.033333" + }, + { + "pts_time": "4.233333", + "duration_time": "0.066667" + }, + { + "pts_time": "4.300000", + "duration_time": "0.033333" + }, + { + "pts_time": "4.333333", + "duration_time": "0.066667" + }, + { + "pts_time": "4.400000", + "duration_time": "0.033333" + }, + { + "pts_time": "4.433333", + "duration_time": "0.066667" + }, + { + "pts_time": "4.500000", + "duration_time": "0.033333" + }, + { + "pts_time": "4.533333", + "duration_time": "0.066667" + }, + { + "pts_time": "4.600000", + "duration_time": "0.033333" + }, + { + "pts_time": "4.633333", + "duration_time": "0.066667" + }, + { + "pts_time": "4.700000", + "duration_time": "0.033333" + }, + { + "pts_time": "4.733333", + "duration_time": "0.066667" + }, + { + "pts_time": "4.800000", + "duration_time": "0.033333" + }, + { + "pts_time": "4.833333", + "duration_time": "0.066667" + }, + { + "pts_time": "4.900000", + "duration_time": "0.033333" + }, + { + "pts_time": "4.933333", + "duration_time": "0.066667" + }, + { + "pts_time": "5.000000", + "duration_time": "0.033333" + }, + { + "pts_time": "5.033333", + "duration_time": "0.066667" + }, + { + "pts_time": "5.100000", + "duration_time": "0.033333" + }, + { + "pts_time": "5.133333", + "duration_time": "0.066667" + }, + { + "pts_time": "5.200000", + "duration_time": "0.033333" + }, + { + "pts_time": "5.233333", + "duration_time": "0.066667" + }, + { + "pts_time": "5.300000", + "duration_time": "0.033333" + }, + { + "pts_time": "5.333333", + "duration_time": "0.066667" + }, + { + "pts_time": "5.400000", + "duration_time": "0.033333" + }, + { + "pts_time": "5.433333", + "duration_time": "0.066667" + }, + { + "pts_time": "5.500000", + "duration_time": "0.033333" + }, + { + "pts_time": "5.533333", + "duration_time": "0.066667" + }, + { + "pts_time": "5.600000", + "duration_time": "0.033333" + }, + { + "pts_time": "5.633333", + "duration_time": "0.066667" + }, + { + "pts_time": "5.700000", + "duration_time": "0.033333" + }, + { + "pts_time": "5.733333", + "duration_time": "0.066667" + }, + { + "pts_time": "5.800000", + "duration_time": "0.033333" + }, + { + "pts_time": "5.833333", + "duration_time": "0.066667" + }, + { + "pts_time": "5.900000", + "duration_time": "0.033333" + }, + { + "pts_time": "5.933333", + "duration_time": "0.066667" + }, + { + "pts_time": "6.000000", + "duration_time": "0.033333" + }, + { + "pts_time": "6.033333", + "duration_time": "0.066667" + }, + { + "pts_time": "6.100000", + "duration_time": "0.033333" + }, + { + "pts_time": "6.133333", + "duration_time": "0.066667" + }, + { + "pts_time": "6.200000", + "duration_time": "0.033333" + }, + { + "pts_time": "6.233333", + "duration_time": "0.066667" + }, + { + "pts_time": "6.300000", + "duration_time": "0.033333" + }, + { + "pts_time": "6.333333", + "duration_time": "0.066667" + }, + { + "pts_time": "6.400000", + "duration_time": "0.033333" + }, + { + "pts_time": "6.433333", + "duration_time": "0.066667" + }, + { + "pts_time": "6.500000", + "duration_time": "0.033333" + }, + { + "pts_time": "6.533333", + "duration_time": "0.066667" + }, + { + "pts_time": "6.600000", + "duration_time": "0.033333" + }, + { + "pts_time": "6.633333", + "duration_time": "0.066667" + }, + { + "pts_time": "6.700000", + "duration_time": "0.033333" + }, + { + "pts_time": "6.733333", + "duration_time": "0.066667" + }, + { + "pts_time": "6.800000", + "duration_time": "0.033333" + }, + { + "pts_time": "6.833333", + "duration_time": "0.066667" + }, + { + "pts_time": "6.900000", + "duration_time": "0.033333" + }, + { + "pts_time": "6.933333", + "duration_time": "0.066667" + }, + { + "pts_time": "7.000000", + "duration_time": "0.033333" + }, + { + "pts_time": "7.033333", + "duration_time": "0.066667" + }, + { + "pts_time": "7.100000", + "duration_time": "0.033333" + }, + { + "pts_time": "7.133333", + "duration_time": "0.066667" + }, + { + "pts_time": "7.200000", + "duration_time": "0.033333" + }, + { + "pts_time": "7.233333", + "duration_time": "0.066667" + }, + { + "pts_time": "7.300000", + "duration_time": "0.033333" + }, + { + "pts_time": "7.333333", + "duration_time": "0.066667" + }, + { + "pts_time": "7.400000", + "duration_time": "0.033333" + }, + { + "pts_time": "7.433333", + "duration_time": "0.066667" + }, + { + "pts_time": "7.500000", + "duration_time": "0.033333" + }, + { + "pts_time": "7.533333", + "duration_time": "0.066667" + }, + { + "pts_time": "7.600000", + "duration_time": "0.033333" + }, + { + "pts_time": "7.633333", + "duration_time": "0.066667" + }, + { + "pts_time": "7.700000", + "duration_time": "0.033333" + }, + { + "pts_time": "7.733333", + "duration_time": "0.066667" + }, + { + "pts_time": "7.800000", + "duration_time": "0.033333" + }, + { + "pts_time": "7.833333", + "duration_time": "0.066667" + }, + { + "pts_time": "7.900000", + "duration_time": "0.033333" + }, + { + "pts_time": "7.933333", + "duration_time": "0.066667" + }, + { + "pts_time": "8.000000", + "duration_time": "0.033333" + }, + { + "pts_time": "8.033333", + "duration_time": "0.066667" + }, + { + "pts_time": "8.100000", + "duration_time": "0.033333" + }, + { + "pts_time": "8.133333", + "duration_time": "0.066667" + }, + { + "pts_time": "8.200000", + "duration_time": "0.033333" + }, + { + "pts_time": "8.233333", + "duration_time": "0.066667" + }, + { + "pts_time": "8.300000", + "duration_time": "0.033333" + }, + { + "pts_time": "8.333333", + "duration_time": "0.066667" + }, + { + "pts_time": "8.400000", + "duration_time": "0.033333" + }, + { + "pts_time": "8.433333", + "duration_time": "0.066667" + }, + { + "pts_time": "8.500000", + "duration_time": "0.033333" + }, + { + "pts_time": "8.533333", + "duration_time": "0.066667" + }, + { + "pts_time": "8.600000", + "duration_time": "0.033333" + }, + { + "pts_time": "8.633333", + "duration_time": "0.066667" + }, + { + "pts_time": "8.700000", + "duration_time": "0.033333" + }, + { + "pts_time": "8.733333", + "duration_time": "0.066667" + }, + { + "pts_time": "8.800000", + "duration_time": "0.033333" + }, + { + "pts_time": "8.833333", + "duration_time": "0.066667" + }, + { + "pts_time": "8.900000", + "duration_time": "0.033333" + }, + { + "pts_time": "8.933333", + "duration_time": "0.066667" + }, + { + "pts_time": "9.000000", + "duration_time": "0.033333" + }, + { + "pts_time": "9.033333", + "duration_time": "0.066667" + }, + { + "pts_time": "9.100000", + "duration_time": "0.033333" + }, + { + "pts_time": "9.133333", + "duration_time": "0.066667" + }, + { + "pts_time": "9.200000", + "duration_time": "0.033333" + }, + { + "pts_time": "9.233333", + "duration_time": "0.066667" + }, + { + "pts_time": "9.300000", + "duration_time": "0.033333" + }, + { + "pts_time": "9.333333", + "duration_time": "0.066667" + }, + { + "pts_time": "9.400000", + "duration_time": "0.033333" + }, + { + "pts_time": "9.433333", + "duration_time": "0.066667" + }, + { + "pts_time": "9.500000", + "duration_time": "0.033333" + }, + { + "pts_time": "9.533333", + "duration_time": "0.066667" + }, + { + "pts_time": "9.600000", + "duration_time": "0.033333" + }, + { + "pts_time": "9.633333", + "duration_time": "0.066667" + }, + { + "pts_time": "9.700000", + "duration_time": "0.033333" + }, + { + "pts_time": "9.733333", + "duration_time": "0.066667" + }, + { + "pts_time": "9.800000", + "duration_time": "0.033333" + }, + { + "pts_time": "9.833333", + "duration_time": "0.066667" + }, + { + "pts_time": "9.900000", + "duration_time": "0.033333" + }, + { + "pts_time": "9.933333", + "duration_time": "0.066667" + }, + { + "pts_time": "10.000000", + "duration_time": "0.033333" + }, + { + "pts_time": "10.033333", + "duration_time": "0.066667" + }, + { + "pts_time": "10.100000", + "duration_time": "0.033333" + }, + { + "pts_time": "10.133333", + "duration_time": "0.066667" + }, + { + "pts_time": "10.200000", + "duration_time": "0.033333" + }, + { + "pts_time": "10.233333", + "duration_time": "0.066667" + }, + { + "pts_time": "10.300000", + "duration_time": "0.033333" + }, + { + "pts_time": "10.333333", + "duration_time": "0.066667" + }, + { + "pts_time": "10.400000", + "duration_time": "0.033333" + }, + { + "pts_time": "10.433333", + "duration_time": "0.066667" + }, + { + "pts_time": "10.500000", + "duration_time": "0.033333" + }, + { + "pts_time": "10.533333", + "duration_time": "0.066667" + }, + { + "pts_time": "10.600000", + "duration_time": "0.033333" + }, + { + "pts_time": "10.633333", + "duration_time": "0.066667" + }, + { + "pts_time": "10.700000", + "duration_time": "0.033333" + }, + { + "pts_time": "10.733333", + "duration_time": "0.066667" + }, + { + "pts_time": "10.800000", + "duration_time": "0.033333" + }, + { + "pts_time": "10.833333", + "duration_time": "0.066667" + }, + { + "pts_time": "10.900000", + "duration_time": "0.033333" + }, + { + "pts_time": "10.933333", + "duration_time": "0.066667" + }, + { + "pts_time": "11.000000", + "duration_time": "0.033333" + }, + { + "pts_time": "11.033333", + "duration_time": "0.066667" + }, + { + "pts_time": "11.100000", + "duration_time": "0.033333" + }, + { + "pts_time": "11.133333", + "duration_time": "0.066667" + }, + { + "pts_time": "11.200000", + "duration_time": "0.033333" + }, + { + "pts_time": "11.233333", + "duration_time": "0.066667" + }, + { + "pts_time": "11.300000", + "duration_time": "0.033333" + }, + { + "pts_time": "11.333333", + "duration_time": "0.066667" + }, + { + "pts_time": "11.400000", + "duration_time": "0.033333" + }, + { + "pts_time": "11.433333", + "duration_time": "0.066667" + }, + { + "pts_time": "11.500000", + "duration_time": "0.033333" + }, + { + "pts_time": "11.533333", + "duration_time": "0.066667" + }, + { + "pts_time": "11.600000", + "duration_time": "0.033333" + }, + { + "pts_time": "11.633333", + "duration_time": "0.066667" + }, + { + "pts_time": "11.700000", + "duration_time": "0.033333" + }, + { + "pts_time": "11.733333", + "duration_time": "0.066667" + }, + { + "pts_time": "11.800000", + "duration_time": "0.033333" + }, + { + "pts_time": "11.833333", + "duration_time": "0.066667" + }, + { + "pts_time": "11.900000", + "duration_time": "0.033333" + }, + { + "pts_time": "11.933333", + "duration_time": "0.066667" + }, + { + "pts_time": "12.000000", + "duration_time": "0.033333" + }, + { + "pts_time": "12.033333", + "duration_time": "0.066667" + }, + { + "pts_time": "12.100000", + "duration_time": "0.033333" + }, + { + "pts_time": "12.133333", + "duration_time": "0.066667" + }, + { + "pts_time": "12.200000", + "duration_time": "0.033333" + }, + { + "pts_time": "12.233333", + "duration_time": "0.066667" + }, + { + "pts_time": "12.300000", + "duration_time": "0.033333" + }, + { + "pts_time": "12.333333", + "duration_time": "0.066667" + }, + { + "pts_time": "12.400000", + "duration_time": "0.033333" + }, + { + "pts_time": "12.433333", + "duration_time": "0.066667" + }, + { + "pts_time": "12.500000", + "duration_time": "0.033333" + }, + { + "pts_time": "12.533333", + "duration_time": "0.066667" + }, + { + "pts_time": "12.600000", + "duration_time": "0.033333" + }, + { + "pts_time": "12.633333", + "duration_time": "0.066667" + }, + { + "pts_time": "12.700000", + "duration_time": "0.033333" + }, + { + "pts_time": "12.733333", + "duration_time": "0.066667" + }, + { + "pts_time": "12.800000", + "duration_time": "0.033333" + }, + { + "pts_time": "12.833333", + "duration_time": "0.066667" + }, + { + "pts_time": "12.900000", + "duration_time": "0.033333" + }, + { + "pts_time": "12.933333", + "duration_time": "0.066667" + }, + { + "pts_time": "13.000000", + "duration_time": "0.033333" + }, + { + "pts_time": "13.033333", + "duration_time": "0.066667" + }, + { + "pts_time": "13.100000", + "duration_time": "0.033333" + }, + { + "pts_time": "13.133333", + "duration_time": "0.066667" + }, + { + "pts_time": "13.200000", + "duration_time": "0.033333" + }, + { + "pts_time": "13.233333", + "duration_time": "0.066667" + }, + { + "pts_time": "13.300000", + "duration_time": "0.033333" + }, + { + "pts_time": "13.333333", + "duration_time": "0.066667" + }, + { + "pts_time": "13.400000", + "duration_time": "0.033333" + }, + { + "pts_time": "13.433333", + "duration_time": "0.066667" + }, + { + "pts_time": "13.500000", + "duration_time": "0.033333" + }, + { + "pts_time": "13.533333", + "duration_time": "0.066667" + }, + { + "pts_time": "13.600000", + "duration_time": "0.033333" + }, + { + "pts_time": "13.633333", + "duration_time": "0.066667" + }, + { + "pts_time": "13.700000", + "duration_time": "0.033333" + }, + { + "pts_time": "13.733333", + "duration_time": "0.066667" + }, + { + "pts_time": "13.800000", + "duration_time": "0.033333" + }, + { + "pts_time": "13.833333", + "duration_time": "0.066667" + }, + { + "pts_time": "13.900000", + "duration_time": "0.033333" + }, + { + "pts_time": "13.933333", + "duration_time": "0.066667" + }, + { + "pts_time": "14.000000", + "duration_time": "0.033333" + }, + { + "pts_time": "14.033333", + "duration_time": "0.066667" + }, + { + "pts_time": "14.100000", + "duration_time": "0.033333" + }, + { + "pts_time": "14.133333", + "duration_time": "0.066667" + }, + { + "pts_time": "14.200000", + "duration_time": "0.033333" + }, + { + "pts_time": "14.233333", + "duration_time": "0.066667" + }, + { + "pts_time": "14.300000", + "duration_time": "0.033333" + }, + { + "pts_time": "14.333333", + "duration_time": "0.066667" + }, + { + "pts_time": "14.400000", + "duration_time": "0.033333" + }, + { + "pts_time": "14.433333", + "duration_time": "0.066667" + }, + { + "pts_time": "14.500000", + "duration_time": "0.033333" + }, + { + "pts_time": "14.533333", + "duration_time": "0.066667" + }, + { + "pts_time": "14.600000", + "duration_time": "0.033333" + }, + { + "pts_time": "14.633333", + "duration_time": "0.066667" + }, + { + "pts_time": "14.700000", + "duration_time": "0.033333" + }, + { + "pts_time": "14.733333", + "duration_time": "0.066667" + }, + { + "pts_time": "14.800000", + "duration_time": "0.033333" + }, + { + "pts_time": "14.833333", + "duration_time": "0.066667" + }, + { + "pts_time": "14.900000", + "duration_time": "0.033333" + }, + { + "pts_time": "14.933333", + "duration_time": "0.033333" + } +] diff --git a/test/resources/var_fps_video.mp4.stream0.frame000000.pt b/test/resources/var_fps_video.mp4.stream0.frame000000.pt new file mode 100644 index 00000000..a7a834a0 Binary files /dev/null and b/test/resources/var_fps_video.mp4.stream0.frame000000.pt differ diff --git a/test/resources/var_fps_video.mp4.stream0.frame000001.pt b/test/resources/var_fps_video.mp4.stream0.frame000001.pt new file mode 100644 index 00000000..99336d4a Binary files /dev/null and b/test/resources/var_fps_video.mp4.stream0.frame000001.pt differ diff --git a/test/resources/var_fps_video.mp4.stream0.frame000002.pt b/test/resources/var_fps_video.mp4.stream0.frame000002.pt new file mode 100644 index 00000000..301c260e Binary files /dev/null and b/test/resources/var_fps_video.mp4.stream0.frame000002.pt differ diff --git a/test/resources/var_fps_video.mp4.stream0.frame000003.pt b/test/resources/var_fps_video.mp4.stream0.frame000003.pt new file mode 100644 index 00000000..7bcd549a Binary files /dev/null and b/test/resources/var_fps_video.mp4.stream0.frame000003.pt differ diff --git a/test/resources/var_fps_video.mp4.stream0.frame000004.pt b/test/resources/var_fps_video.mp4.stream0.frame000004.pt new file mode 100644 index 00000000..e0649277 Binary files /dev/null and b/test/resources/var_fps_video.mp4.stream0.frame000004.pt differ diff --git a/test/resources/var_fps_video.mp4.stream0.frame000005.pt b/test/resources/var_fps_video.mp4.stream0.frame000005.pt new file mode 100644 index 00000000..1a647182 Binary files /dev/null and b/test/resources/var_fps_video.mp4.stream0.frame000005.pt differ diff --git a/test/resources/var_fps_video.mp4.stream0.frame000010.pt b/test/resources/var_fps_video.mp4.stream0.frame000010.pt new file mode 100644 index 00000000..c5fdf222 Binary files /dev/null and b/test/resources/var_fps_video.mp4.stream0.frame000010.pt differ diff --git a/test/resources/var_fps_video.mp4.stream0.frame000015.pt b/test/resources/var_fps_video.mp4.stream0.frame000015.pt new file mode 100644 index 00000000..50ed25ab Binary files /dev/null and b/test/resources/var_fps_video.mp4.stream0.frame000015.pt differ diff --git a/test/resources/var_fps_video.mp4.stream0.frame000020.pt b/test/resources/var_fps_video.mp4.stream0.frame000020.pt new file mode 100644 index 00000000..75d1714b Binary files /dev/null and b/test/resources/var_fps_video.mp4.stream0.frame000020.pt differ diff --git a/test/resources/var_fps_video.mp4.stream0.frame000025.pt b/test/resources/var_fps_video.mp4.stream0.frame000025.pt new file mode 100644 index 00000000..989f636e Binary files /dev/null and b/test/resources/var_fps_video.mp4.stream0.frame000025.pt differ diff --git a/test/resources/var_fps_video.mp4.stream0.frame000030.pt b/test/resources/var_fps_video.mp4.stream0.frame000030.pt new file mode 100644 index 00000000..817609b1 Binary files /dev/null and b/test/resources/var_fps_video.mp4.stream0.frame000030.pt differ diff --git a/test/resources/var_fps_video.mp4.stream0.frame000035.pt b/test/resources/var_fps_video.mp4.stream0.frame000035.pt new file mode 100644 index 00000000..442c4b4a Binary files /dev/null and b/test/resources/var_fps_video.mp4.stream0.frame000035.pt differ diff --git a/test/resources/var_fps_video.mp4.stream0.frame000040.pt b/test/resources/var_fps_video.mp4.stream0.frame000040.pt new file mode 100644 index 00000000..62acd5c4 Binary files /dev/null and b/test/resources/var_fps_video.mp4.stream0.frame000040.pt differ diff --git a/test/resources/var_fps_video.mp4.stream0.frame000045.pt b/test/resources/var_fps_video.mp4.stream0.frame000045.pt new file mode 100644 index 00000000..0e9a0cff Binary files /dev/null and b/test/resources/var_fps_video.mp4.stream0.frame000045.pt differ diff --git a/test/resources/var_fps_video.mp4.stream0.frame000050.pt b/test/resources/var_fps_video.mp4.stream0.frame000050.pt new file mode 100644 index 00000000..cedb6d21 Binary files /dev/null and b/test/resources/var_fps_video.mp4.stream0.frame000050.pt differ diff --git a/test/resources/var_fps_video.mp4.stream0.frame000055.pt b/test/resources/var_fps_video.mp4.stream0.frame000055.pt new file mode 100644 index 00000000..a57ca9f6 Binary files /dev/null and b/test/resources/var_fps_video.mp4.stream0.frame000055.pt differ diff --git a/test/resources/var_fps_video.mp4.stream0.frame000060.pt b/test/resources/var_fps_video.mp4.stream0.frame000060.pt new file mode 100644 index 00000000..b0643afa Binary files /dev/null and b/test/resources/var_fps_video.mp4.stream0.frame000060.pt differ diff --git a/test/test_variable_fps_video.py b/test/test_variable_fps_video.py new file mode 100644 index 00000000..1597fef4 --- /dev/null +++ b/test/test_variable_fps_video.py @@ -0,0 +1,204 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +import os +import json +import pathlib +import pytest +import numpy as np +import torch + +from torchcodec._core import ( + create_from_file, + add_video_stream, + get_next_frame, + get_frame_at_index, + get_frame_at_pts, + get_frames_in_range, + get_json_metadata, + seek_to_pts, +) + +from torchcodec.decoders import VideoDecoder,VideoStreamMetadata +from torchcodec import Frame, FrameBatch + +from .utils import ( + assert_frames_equal, + cpu_and_cuda, + TestVideo, + TestVideoStreamInfo, + TestFrameInfo, + _get_file_path, + VAR_FPS_VIDEO, +) + +class TestVariableFPSVideoDecoder: + def _check_video_exists(self): + try: + VAR_FPS_VIDEO.path + except FileNotFoundError: + pytest.skip("Variable FPS test video not found") + + @pytest.mark.parametrize("device", cpu_and_cuda()) + def test_basic_decoding(self, device): + self._check_video_exists() + + decoder = VideoDecoder(str(VAR_FPS_VIDEO.path)) + + frame = decoder.get_frame_at(0) + assert isinstance(frame, Frame) + + metadata = decoder.metadata + assert isinstance(metadata, VideoStreamMetadata) + assert metadata.num_frames > 30 + + @pytest.mark.parametrize("device", cpu_and_cuda()) + def test_exact_seeking_mode(self, device): + self._check_video_exists() + + decoder = VideoDecoder(str(VAR_FPS_VIDEO.path), seek_mode="exact") + + test_timestamps = [0.0, 0.5, 1.0, 1.5, 2.0] + for timestamp in test_timestamps: + if timestamp < decoder.metadata.duration_seconds: + frame_batch = decoder.get_frames_played_at(seconds=[timestamp]) + assert isinstance(frame_batch, FrameBatch) + assert abs(frame_batch.pts_seconds[0] - timestamp) <= frame_batch.duration_seconds[0] + + @pytest.mark.parametrize("device", cpu_and_cuda()) + def test_approximate_seeking_mode_behavior(self, device): + """Test behavior in approximate seeking mode (may fail as expected)""" + self._check_video_exists() + + # Create two decoders: one with exact mode, one with approximate mode + decoder_exact = create_from_file(str(VAR_FPS_VIDEO.path), seek_mode="exact") + add_video_stream(decoder_exact, device=device) + + decoder_approx = create_from_file(str(VAR_FPS_VIDEO.path), seek_mode="approximate") + add_video_stream(decoder_approx, device=device) + + metadata = get_json_metadata(decoder_exact) + metadata_dict = json.loads(metadata) + + # Compare seeking in both modes + test_pts = [0.0, 0.5, 1.0, 1.5, 2.0] + differences = [] + + for pts in test_pts: + if pts < metadata_dict["durationSeconds"]: + frame_exact, pts_exact, _ = get_frame_at_pts(decoder_exact, pts) + + try: + frame_approx, pts_approx, _ = get_frame_at_pts(decoder_approx, pts) + + differences.append({ + "seek_pts": pts, + "exact_pts": pts_exact.item(), + "approx_pts": pts_approx.item(), + "frames_match": torch.allclose(frame_exact, frame_approx), + "pts_difference": abs(pts_exact.item() - pts_approx.item()), + "approximate_failed": False + }) + except Exception as e: + differences.append({ + "seek_pts": pts, + "exact_pts": pts_exact.item(), + "error": str(e), + "approximate_failed": True + }) + + # Print differences (useful for debugging) + for diff in differences: + if diff["approximate_failed"]: + print(f"Seeking to {diff['seek_pts']}s failed in approximate mode: {diff['error']}") + else: + print(f"Seeking to {diff['seek_pts']}s: exact={diff['exact_pts']}, " + f"approx={diff['approx_pts']}, diff={diff['pts_difference']}, " + f"frames {'match' if diff['frames_match'] else 'differ'}") + + # No assertion as approximate mode is expected to potentially fail + + @pytest.mark.parametrize("device", cpu_and_cuda()) + def test_frame_timing_pattern(self, device): + self._check_video_exists() + + decoder = create_from_file(str(VAR_FPS_VIDEO.path)) + add_video_stream(decoder, device=device) + + frames_info = [] + frame_index = 0 + + seek_to_pts(decoder, 0.0) + + while True: + try: + frame, pts, duration = get_next_frame(decoder) + frames_info.append({ + "index": frame_index, + "pts": pts.item(), + "duration": duration.item() + }) + frame_index += 1 + except IndexError: + break + + assert len(frames_info) > 30, "Not enough frames to verify variable frame rate" + + intervals_before = [frames_info[i+1]["pts"] - frames_info[i]["pts"] + for i in range(min(30, len(frames_info)-1))] + + intervals_after = [frames_info[i+1]["pts"] - frames_info[i]["pts"] + for i in range(30, min(60, len(frames_info)-1))] + + if len(intervals_after) > 5: + avg_interval_before = sum(intervals_before) / len(intervals_before) + avg_interval_after = sum(intervals_after) / len(intervals_after) + + print(f"Average interval for first 30 frames: {avg_interval_before:.6f}s") + print(f"Average interval for subsequent frames: {avg_interval_after:.6f}s") + + expected_ratio = 0.5 + actual_ratio = avg_interval_before / avg_interval_after + + # Allow for some error + assert abs(actual_ratio - expected_ratio) < 0.2, \ + f"Interval ratio ({actual_ratio:.2f}) differs too much from expected ({expected_ratio})" + + @pytest.mark.parametrize("device", cpu_and_cuda()) + def test_sequential_decoding(self, device): + self._check_video_exists() + + decoder = create_from_file(str(VAR_FPS_VIDEO.path)) + add_video_stream(decoder, device=device) + + seek_to_pts(decoder, 0.0) + + # Decode multiple frames and verify monotonically increasing timestamps + last_pts = -1.0 + for _ in range(50): + try: + frame, pts, duration = get_next_frame(decoder) + current_pts = pts.item() + + assert current_pts > last_pts, \ + f"Frame timestamps not monotonically increasing: current={current_pts}, previous={last_pts}" + + last_pts = current_pts + except IndexError: + break + + @pytest.mark.parametrize("device", cpu_and_cuda()) + def test_frames_in_range(self, device): + self._check_video_exists() + + decoder = VideoDecoder(str(VAR_FPS_VIDEO.path)) + + frame_batch = decoder.get_frames_in_range(0, 10) + assert isinstance(frame_batch, FrameBatch) + assert len(frame_batch) == 10 + + timestamps = frame_batch.pts_seconds.tolist() + assert all(timestamps[i] < timestamps[i+1] for i in range(len(timestamps)-1)) diff --git a/test/utils.py b/test/utils.py index e7ce12e5..3e81183e 100644 --- a/test/utils.py +++ b/test/utils.py @@ -561,3 +561,14 @@ def sample_format(self) -> str: }, }, ) + +VAR_FPS_VIDEO = TestVideo( + filename="var_fps_video.mp4", + default_stream_index=0, + # This metadata is extracted manually. + # $ ffprobe -v error -hide_banner -select_streams v:0 -show_frames -of json test/resources/av1_video.mkv > out.json + stream_infos={ + 0: TestVideoStreamInfo(width=320, height=240, num_color_channels=3), + }, + frames={}, +) \ No newline at end of file