@@ -4255,17 +4255,26 @@ def test_cpu_fallback_is_on_cuda(self, video, expected_pix_fmt):
42554255 assert frame .pix_fmt == expected_pix_fmt
42564256 assert all (plane .device .type == "cuda" for plane in frame .planes )
42574257
4258- @pytest .mark .parametrize ("pix_fmt" , ("pal8" , "gbrpf32le" ))
4259- def test_planes_of_non_viewable_format (self , tmp_path , pix_fmt ):
4258+ @pytest .mark .parametrize (
4259+ "pix_fmt, codec, container" ,
4260+ (
4261+ ("pal8" , "rawvideo" , "nut" ),
4262+ # Before FFmpeg 8 the nut muxer has no rawvideo tag for the float
4263+ # formats and silently writes a bogus one, so the file reads back as
4264+ # rgb555le. EXR in mkv stores gbrpf32le properly on all versions.
4265+ ("gbrpf32le" , "exr" , "mkv" ),
4266+ ),
4267+ )
4268+ def test_planes_of_non_viewable_format (self , tmp_path , pix_fmt , codec , container ):
42604269 # Palettised and float formats can't be handed out as views. Everything
42614270 # *but* the planes still works, which is what lets a caller check
42624271 # pix_fmt before reaching for them.
4263- path = tmp_path / f"{ pix_fmt } .nut "
4272+ path = tmp_path / f"{ pix_fmt } .{ container } "
42644273 subprocess .run (
42654274 [
42664275 "ffmpeg" , "-y" , "-hide_banner" , "-loglevel" , "error" ,
42674276 "-f" , "lavfi" , "-i" , "testsrc2=size=64x48:rate=10:duration=1" ,
4268- "-c:v" , "rawvideo" , "-pix_fmt" , pix_fmt , str (path ),
4277+ "-c:v" , codec , "-pix_fmt" , pix_fmt , str (path ),
42694278 ],
42704279 check = True ,
42714280 ) # fmt: skip
@@ -4759,6 +4768,47 @@ def test_seek_on_every_source_kind(self, make_source):
47594768 assert got .pts_seconds == expected .pts_seconds == seconds
47604769 assert_frames_equal (got .data , expected .data )
47614770
4771+ # ===== stream_index =====
4772+
4773+ @pytest .mark .parametrize ("stream_index" , (None , 0 , 3 ))
4774+ def test_stream_index (self , stream_index ):
4775+ # nasa_13013.mp4 has two video streams, 0 and 3, of different sizes,
4776+ # and 3 is the best one, i.e. the one used when nothing is requested.
4777+ demuxer = Demuxer (NASA_VIDEO .path , stream_index = stream_index )
4778+ decoder = PacketDecoder (demuxer )
4779+ converter = ColorConverter ()
4780+ got = [
4781+ converter .convert (raw_frame )
4782+ for raw_frame in itertools .islice (
4783+ self ._decode (decoder , self ._demux (demuxer )), 10
4784+ )
4785+ ]
4786+
4787+ expected = VideoDecoder (NASA_VIDEO .path , stream_index = stream_index )[:10 ]
4788+
4789+ assert len (got ) == len (expected ) == 10
4790+ for got_frame , expected_data in zip (got , expected ):
4791+ assert_frames_equal (got_frame .data , expected_data )
4792+
4793+ @pytest .mark .parametrize ("stream_index" , (1 , 4 )) # the mp4's aac streams
4794+ def test_audio_stream_index_raises (self , stream_index ):
4795+ with pytest .raises (RuntimeError , match = "is not a video stream.*'audio'" ):
4796+ Demuxer (NASA_VIDEO .path , stream_index = stream_index )
4797+
4798+ def test_audio_only_file_raises (self ):
4799+ with pytest .raises (RuntimeError , match = "No valid video stream found" ):
4800+ Demuxer (NASA_AUDIO_MP3 .path )
4801+
4802+ def test_non_video_stream_index_raises (self ):
4803+ # Stream 2 of the mp4 is a subtitle stream.
4804+ with pytest .raises (RuntimeError , match = "is not a video stream.*'subtitle'" ):
4805+ Demuxer (NASA_VIDEO .path , stream_index = 2 )
4806+
4807+ @pytest .mark .parametrize ("stream_index" , (- 1 , 6 , 1000 ))
4808+ def test_invalid_stream_index_raises (self , stream_index ):
4809+ with pytest .raises (RuntimeError , match = "is not a valid stream" ):
4810+ Demuxer (NASA_VIDEO .path , stream_index = stream_index )
4811+
47624812 def test_bad_source_type_raises (self ):
47634813 with pytest .raises (TypeError , match = "Unknown source type" ):
47644814 Demuxer (123 )
0 commit comments