2222.. code-block::
2323
2424 Demuxer -> PacketDecoder -> ColorConverter
25- Packet DecodedFrame RGB Frame
25+ Packet RawFrame RGB Frame
2626
2727The blocks are passive: they never create threads, and they release the GIL.
2828You decide how they are composed, on which threads, and where to stop. Below
6565#
6666# ``PacketDecoder`` and ``ColorConverter`` both accept ``device="cuda"``:
6767# decoding then runs on NVDEC and the color conversion on the GPU, and the
68- # frames never leave the device. Demuxing always happens on the CPU.
68+ # frames never leave the device. Demuxing always happens on the CPU. Left
69+ # unspecified, ``device`` is the current default device.
6970from torchcodec .decoders ._blocks import ColorConverter , Demuxer , PacketDecoder
7071
7172demuxer = Demuxer (video_path )
7475
7576frames = []
7677for packet in demuxer :
77- for decoded_frame in packet_decoder .decode (packet ):
78- frames .append (color_converter .convert (decoded_frame ))
79- for decoded_frame in packet_decoder .drain ():
80- frames .append (color_converter .convert (decoded_frame ))
78+ for raw_frame in packet_decoder .decode (packet ):
79+ frames .append (color_converter .convert (raw_frame ))
80+ for raw_frame in packet_decoder .drain ():
81+ frames .append (color_converter .convert (raw_frame ))
8182
8283print (f"{ len (frames )} frames, { frames [0 ].data .shape = } , "
8384 f"{ frames [0 ].pts_seconds = } , { frames [0 ].data .device = } " )
@@ -104,9 +105,9 @@ def decode(packet_decoder, packets):
104105 yield from packet_decoder .drain ()
105106
106107
107- def color_convert (color_converter , decoded_frames ):
108- for decoded_frame in decoded_frames :
109- yield color_converter .convert (decoded_frame )
108+ def color_convert (color_converter , raw_frames ):
109+ for raw_frame in raw_frames :
110+ yield color_converter .convert (raw_frame )
110111
111112
112113def prefetch (upstream , buffer_size = 8 ):
@@ -143,8 +144,8 @@ def convert_on_own_thread():
143144 demuxer = Demuxer (video_path )
144145 packet_decoder = PacketDecoder (demuxer , device = device )
145146 color_converter = ColorConverter (device = device )
146- decoded_frames = prefetch (decode (packet_decoder , demux (demuxer )))
147- return color_convert (color_converter , decoded_frames )
147+ raw_frames = prefetch (decode (packet_decoder , demux (demuxer )))
148+ return color_convert (color_converter , raw_frames )
148149
149150
150151def demux_on_own_thread ():
@@ -197,13 +198,12 @@ def demux_on_own_thread():
197198# Raw frames
198199# ----------
199200#
200- # Color conversion is optional. A ``DecodedFrame `` can hand out the decoder's
201- # own planes as tensor views, with no copy and no conversion.
201+ # Color conversion is optional. A ``RawFrame `` can hand out the decoder's own
202+ # planes as tensor views, with no copy and no conversion.
202203demuxer = Demuxer (video_path )
203204packet_decoder = PacketDecoder (demuxer , device = device )
204- decoded_frame = next (decode (packet_decoder , demux (demuxer )))
205+ raw_frame = next (decode (packet_decoder , demux (demuxer )))
205206
206- raw_frame = decoded_frame .materialize ()
207207Y , U , V = raw_frame .planes
208208print (f"{ raw_frame .pix_fmt = } , { raw_frame .bit_depth = } , "
209209 f"{ raw_frame .colorspace = } , { raw_frame .color_range = } " )
@@ -215,8 +215,9 @@ def demux_on_own_thread():
215215# views over a single plane. Writing through them is visible downstream.
216216#
217217# Being the decoder's own planes, they are also never rotated - a video whose
218- # container asks for a rotation gives you the samples as they were encoded.
219- # ``ColorConverter`` applies the rotation for you.
218+ # container asks for a rotation gives you the samples as they were encoded, and
219+ # ``raw_frame.rotation_degrees`` tells you what to apply. ``ColorConverter``
220+ # applies it for you.
220221#
221222# So we can do the color conversion ourselves. Here it's plain PyTorch ops -
222223# it could just as well be a Triton or CUDA kernel, fused with whatever your
@@ -246,7 +247,7 @@ def upsample(plane):
246247
247248
248249ours = yuv420_to_rgb (Y , U , V )
249- reference = ColorConverter (device = device ).convert (decoded_frame ).data
250+ reference = ColorConverter (device = device ).convert (raw_frame ).data
250251print (f"{ ours .shape = } , mean abs diff vs ColorConverter: "
251252 f"{ (ours .float () - reference .float ()).abs ().mean ():.2f} " )
252253
@@ -273,9 +274,8 @@ def upsample(plane):
273274
274275hdr_demuxer = Demuxer (hdr_video_path )
275276hdr_packet_decoder = PacketDecoder (hdr_demuxer , device = device )
276- hdr_frame = next (decode (hdr_packet_decoder , demux (hdr_demuxer )))
277+ hdr_raw = next (decode (hdr_packet_decoder , demux (hdr_demuxer )))
277278
278- hdr_raw = hdr_frame .materialize ()
279279hdr_Y = hdr_raw .planes [0 ]
280280print (f"{ hdr_raw .pix_fmt = } , { hdr_raw .bit_depth = } , "
281281 f"{ hdr_raw .colorspace = } , { hdr_Y .dtype = } " )
0 commit comments