@@ -1159,7 +1159,10 @@ def decode(self, source=None) -> torch.Tensor:
11591159 "format" , ("mov" , "mp4" , "mkv" , pytest .param ("webm" , marks = pytest .mark .slow ))
11601160 )
11611161 @pytest .mark .parametrize ("method" , ("to_file" , "to_tensor" , "to_file_like" ))
1162- def test_video_encoder_round_trip (self , tmp_path , format , method ):
1162+ @pytest .mark .parametrize (
1163+ "device" , ("cpu" , pytest .param ("cuda" , marks = pytest .mark .needs_cuda ))
1164+ )
1165+ def test_video_encoder_round_trip (self , tmp_path , format , method , device ):
11631166 # Test that decode(encode(decode(frames))) == decode(frames)
11641167 ffmpeg_version = get_ffmpeg_major_version ()
11651168 # In FFmpeg6, the default codec's best pixel format is lossy for all container formats but webm.
@@ -1174,9 +1177,10 @@ def test_video_encoder_round_trip(self, tmp_path, format, method):
11741177 pytest .skip ("Codec for webm is not available in this FFmpeg installation." )
11751178 source_frames = self .decode (TEST_SRC_2_720P .path ).data
11761179
1180+ # Frame rate is fixed with num frames decoded
11771181 params = dict (
1178- frame_rate = 30 , crf = 0
1179- ) # Frame rate is fixed with num frames decoded
1182+ frame_rate = 30 , crf = 0 , device = device
1183+ )
11801184 if method == "to_file" :
11811185 encoded_path = str (tmp_path / f"encoder_output.{ format } " )
11821186 encode_video_to_file (
@@ -1207,9 +1211,10 @@ def test_video_encoder_round_trip(self, tmp_path, format, method):
12071211
12081212 # If FFmpeg selects a codec or pixel format that does lossy encoding, assert 99% of pixels
12091213 # are within a higher tolerance.
1210- if ffmpeg_version == 6 :
1211- assert_close = partial (assert_tensor_close_on_at_least , percentage = 99 )
1214+ if ffmpeg_version == 6 or device == "cuda" :
12121215 atol = 15
1216+ percentage = 98
1217+ assert_close = partial (assert_tensor_close_on_at_least , percentage = percentage )
12131218 else :
12141219 assert_close = torch .testing .assert_close
12151220 atol = 2
@@ -1230,7 +1235,10 @@ def test_video_encoder_round_trip(self, tmp_path, format, method):
12301235 ),
12311236 )
12321237 @pytest .mark .parametrize ("method" , ("to_tensor" , "to_file_like" ))
1233- def test_against_to_file (self , tmp_path , format , method ):
1238+ @pytest .mark .parametrize (
1239+ "device" , ("cpu" , pytest .param ("cuda" , marks = pytest .mark .needs_cuda ))
1240+ )
1241+ def test_against_to_file (self , tmp_path , format , method , device ):
12341242 # Test that to_file, to_tensor, and to_file_like produce the same results
12351243 ffmpeg_version = get_ffmpeg_major_version ()
12361244 if format == "webm" and (
@@ -1239,7 +1247,7 @@ def test_against_to_file(self, tmp_path, format, method):
12391247 pytest .skip ("Codec for webm is not available in this FFmpeg installation." )
12401248
12411249 source_frames = self .decode (TEST_SRC_2_720P .path ).data
1242- params = dict (frame_rate = 30 , crf = 0 )
1250+ params = dict (frame_rate = 30 , crf = 0 , device = device )
12431251
12441252 encoded_file = tmp_path / f"output.{ format } "
12451253 encode_video_to_file (frames = source_frames , filename = str (encoded_file ), ** params )
@@ -1313,10 +1321,22 @@ def test_video_encoder_against_ffmpeg_cli(self, tmp_path, format, device):
13131321 str (frame_rate ),
13141322 "-i" ,
13151323 temp_raw_path ,
1324+ ]
1325+
1326+ # Use NVENC encoder when device is CUDA and format has an NVENC codec
1327+ if device == "cuda" :
1328+ if format in ("mp4" , "mov" , "mkv" ):
1329+ ffmpeg_cmd .extend (["-c:v" , "h264_nvenc" ])
1330+ elif format == "webm" :
1331+ ffmpeg_cmd .extend (["-c:v" , "vp9_nvenc" ]) # Use NVENC for VP9
1332+ # TODO-VideoEncoder: formats "flv", "avi" should also use respective NVENC codecs,
1333+ # but do not auto select them.
1334+
1335+ ffmpeg_cmd .extend ([
13161336 "-crf" ,
13171337 str (crf ),
13181338 ffmpeg_encoded_path ,
1319- ]
1339+ ])
13201340 subprocess .run (ffmpeg_cmd , check = True )
13211341
13221342 # Encode with our video encoder
@@ -1347,7 +1367,10 @@ def test_video_encoder_against_ffmpeg_cli(self, tmp_path, format, device):
13471367 ff_frame , enc_frame , percentage = percentage , atol = 2
13481368 )
13491369
1350- def test_to_file_like_custom_file_object (self ):
1370+ @pytest .mark .parametrize (
1371+ "device" , ("cpu" , pytest .param ("cuda" , marks = pytest .mark .needs_cuda ))
1372+ )
1373+ def test_to_file_like_custom_file_object (self , device ):
13511374 """Test to_file_like with a custom file-like object that implements write and seek."""
13521375
13531376 class CustomFileObject :
@@ -1366,32 +1389,54 @@ def get_encoded_data(self):
13661389 source_frames = self .decode (TEST_SRC_2_720P .path ).data
13671390 file_like = CustomFileObject ()
13681391 encode_video_to_file_like (
1369- source_frames , frame_rate = 30 , crf = 0 , format = "mp4" , file_like = file_like
1392+ source_frames , frame_rate = 30 , crf = 0 , format = "mp4" , file_like = file_like , device = device
13701393 )
13711394 decoded_samples = self .decode (file_like .get_encoded_data ())
13721395
1373- torch .testing .assert_close (
1396+ ffmpeg_version = get_ffmpeg_major_version ()
1397+ if device == "cuda" :
1398+ atol = 15
1399+ percentage = 98
1400+ assert_close = partial (assert_tensor_close_on_at_least , percentage = percentage )
1401+ else :
1402+ assert_close = torch .testing .assert_close
1403+ atol = 2
1404+
1405+ assert_close (
13741406 decoded_samples .data ,
13751407 source_frames ,
1376- atol = 2 ,
1408+ atol = atol ,
13771409 rtol = 0 ,
13781410 )
13791411
1380- def test_to_file_like_real_file (self , tmp_path ):
1412+ @pytest .mark .parametrize (
1413+ "device" , ("cpu" , pytest .param ("cuda" , marks = pytest .mark .needs_cuda ))
1414+ )
1415+ def test_to_file_like_real_file (self , tmp_path , device ):
13811416 """Test to_file_like with a real file opened in binary write mode."""
13821417 source_frames = self .decode (TEST_SRC_2_720P .path ).data
13831418 file_path = tmp_path / "test_file_like.mp4"
13841419
13851420 with open (file_path , "wb" ) as file_like :
13861421 encode_video_to_file_like (
1387- source_frames , frame_rate = 30 , crf = 0 , format = "mp4" , file_like = file_like
1422+ source_frames , frame_rate = 30 , crf = 0 , format = "mp4" , file_like = file_like , device = device
13881423 )
13891424 decoded_samples = self .decode (str (file_path ))
13901425
1391- torch .testing .assert_close (
1426+ # Use adaptive tolerance based on device and FFmpeg version, consistent with test_video_encoder_round_trip
1427+ ffmpeg_version = get_ffmpeg_major_version ()
1428+ if device == "cuda" :
1429+ atol = 15
1430+ percentage = 98
1431+ assert_close = partial (assert_tensor_close_on_at_least , percentage = percentage )
1432+ else :
1433+ assert_close = torch .testing .assert_close
1434+ atol = 2
1435+
1436+ assert_close (
13921437 decoded_samples .data ,
13931438 source_frames ,
1394- atol = 2 ,
1439+ atol = atol ,
13951440 rtol = 0 ,
13961441 )
13971442
0 commit comments