PyNvVideoCodec 2.0.0__cp313-cp313-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. PyNvVideoCodec/PyNvVideoCodec_121.cp313-win_amd64.pyd +0 -0
  2. PyNvVideoCodec/PyNvVideoCodec_130.cp313-win_amd64.pyd +0 -0
  3. PyNvVideoCodec/VersionCheck.cp313-win_amd64.pyd +0 -0
  4. PyNvVideoCodec/__init__.py +237 -0
  5. PyNvVideoCodec/avcodec-58.dll +0 -0
  6. PyNvVideoCodec/avcodec-61.dll +0 -0
  7. PyNvVideoCodec/avcodec.dll +0 -0
  8. PyNvVideoCodec/avdevice-58.dll +0 -0
  9. PyNvVideoCodec/avdevice-61.dll +0 -0
  10. PyNvVideoCodec/avdevice.dll +0 -0
  11. PyNvVideoCodec/avfilter-10.dll +0 -0
  12. PyNvVideoCodec/avfilter-7.dll +0 -0
  13. PyNvVideoCodec/avfilter.dll +0 -0
  14. PyNvVideoCodec/avformat-58.dll +0 -0
  15. PyNvVideoCodec/avformat-61.dll +0 -0
  16. PyNvVideoCodec/avformat.dll +0 -0
  17. PyNvVideoCodec/avutil-56.dll +0 -0
  18. PyNvVideoCodec/avutil-59.dll +0 -0
  19. PyNvVideoCodec/avutil.dll +0 -0
  20. PyNvVideoCodec/decoders/SimpleDecoder.py +225 -0
  21. PyNvVideoCodec/decoders/ThreadedDecoder.py +119 -0
  22. PyNvVideoCodec/decoders/__init__.py +22 -0
  23. PyNvVideoCodec/swresample-3.dll +0 -0
  24. PyNvVideoCodec/swresample-5.dll +0 -0
  25. PyNvVideoCodec/swresample.dll +0 -0
  26. PyNvVideoCodec/swscale-5.dll +0 -0
  27. PyNvVideoCodec/swscale-8.dll +0 -0
  28. PyNvVideoCodec/swscale.dll +0 -0
  29. PyNvVideoCodec/transcoder/Transcoder.py +55 -0
  30. PyNvVideoCodec/transcoder/__init__.py +22 -0
  31. PyNvVideoCodec-2.0.0.data/data/external/ffmpeg/readme.txt +10 -0
  32. PyNvVideoCodec-2.0.0.data/data/external/ffmpeg/src/ffmpeg-7.0.2.tar.xz +0 -0
  33. PyNvVideoCodec-2.0.0.dist-info/METADATA +77 -0
  34. PyNvVideoCodec-2.0.0.dist-info/RECORD +64 -0
  35. PyNvVideoCodec-2.0.0.dist-info/WHEEL +5 -0
  36. PyNvVideoCodec-2.0.0.dist-info/licenses/LICENSE.txt +22 -0
  37. PyNvVideoCodec-2.0.0.dist-info/licenses/NOTICES.txt +167 -0
  38. PyNvVideoCodec-2.0.0.dist-info/top_level.txt +3 -0
  39. benchmarks/cached_decoder_benchmark.py +635 -0
  40. benchmarks/frame_sampling_benchmark.py +610 -0
  41. benchmarks/segmented_transcode_benchmark.py +1072 -0
  42. samples/Decode.py +194 -0
  43. samples/DecodeMultiprocessing.py +162 -0
  44. samples/DecodePerf.py +215 -0
  45. samples/DecodeSEIMsgExtraction.py +338 -0
  46. samples/DemuxFromByteArray.py +185 -0
  47. samples/Encode.py +189 -0
  48. samples/EncodeFromCPUBuffer.py +220 -0
  49. samples/EncodeMultiprocessing.py +268 -0
  50. samples/EncodePerf.py +380 -0
  51. samples/EncodeReconfigure.py +201 -0
  52. samples/EncodeSEIMsgInsertion.py +189 -0
  53. samples/ObjectDetection.py +218 -0
  54. samples/SimpleDecoder/EnsembleApp.py +416 -0
  55. samples/SimpleDecoder/SubsamplingAndReconfigure.py +341 -0
  56. samples/SimpleDecoder/fileLists.txt +4 -0
  57. samples/SimpleDecoder/segments.txt +3 -0
  58. samples/SimpleDecoder/timeListInSeconds.txt +5 -0
  59. samples/SimpleDecoder/transcode_config.json +6 -0
  60. samples/Utils.py +290 -0
  61. samples/coco_classes.pickle +0 -0
  62. samples/encode_config.json +16 -0
  63. samples/encode_config_lowlatency.json +11 -0
  64. samples/encode_config_perf.json +9 -0
samples/Decode.py ADDED
@@ -0,0 +1,194 @@
1
+ # This copyright notice applies to this file only
2
+ #
3
+ # SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
4
+ # SPDX-License-Identifier: MIT
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining a
7
+ # copy of this software and associated documentation files (the "Software"),
8
+ # to deal in the Software without restriction, including without limitation
9
+ # the rights to use, copy, modify, merge, publish, distribute, sublicense,
10
+ # and/or sell copies of the Software, and to permit persons to whom the
11
+ # Software is furnished to do so, subject to the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be included in
14
+ # all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19
+ # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22
+ # DEALINGS IN THE SOFTWARE.
23
+
24
+ import sys
25
+ import argparse
26
+ import numpy as np
27
+ from pathlib import Path
28
+ import pycuda.driver as cuda
29
+ import PyNvVideoCodec as nvc
30
+ from os.path import join, dirname, abspath, splitext
31
+
32
+ # Add the parent directory to Python path in a way that works from any working directory
33
+ current_dir = dirname(abspath(__file__))
34
+ parent_dir = dirname(current_dir)
35
+ if parent_dir not in sys.path:
36
+ sys.path.append(parent_dir)
37
+
38
+ from Utils import cast_address_to_1d_bytearray
39
+
40
+ def decode(gpu_id, enc_file_path, dec_file_path, use_device_memory, decode_latency,frame_count=None):
41
+ """
42
+ Function to decode media file and write raw frames into an output file.
43
+
44
+ This function will read a media file and split it into chunks of data (packets).
45
+ A Packet contains elementary bitstream belonging to one frame and conforms to annex.b standard.
46
+ Packet is sent to decoder for parsing and hardware accelerated decoding. Decoder returns list of raw YUV
47
+ frames which can be iterated upon.
48
+
49
+ Parameters:
50
+ gpu_id (int): Ordinal of GPU to use
51
+ enc_file_path (str): Path to file to be decoded
52
+ dec_file_path (str): Path to output file into which raw frames are stored
53
+ use_device_memory (int): If set to 1, output decoded frame is CUDeviceptr wrapped in CUDA Array Interface
54
+ frame_count (int, optional): Maximum number of frames to decode. If None, 0, or negative, decode all frames.
55
+
56
+ Example:
57
+ >>> decode(0, "path/to/input/media/file","path/to/output/yuv", 1)
58
+ Function to decode media file and write raw frames into an output file.
59
+ """
60
+ # Validate frame count if provided
61
+ if frame_count is None or frame_count <= 0:
62
+ print(f"Decoding all available frames")
63
+ frame_count = None # Reset to None to decode all frames
64
+
65
+ cuda_ctx = None
66
+ try:
67
+ device_id = gpu_id
68
+ cuda_device = cuda.Device(device_id) # pyright: ignore[reportAttributeAccessIssue]
69
+ cuda_ctx = cuda_device.retain_primary_context()
70
+ cuda_ctx.push()
71
+ cuda_stream = cuda.Stream()
72
+ nv_dmx = nvc.CreateDemuxer(filename=enc_file_path)
73
+
74
+ caps = nvc.GetDecoderCaps(
75
+ gpuid=gpu_id,
76
+ codec=nv_dmx.GetNvCodecId(),
77
+ chromaformat=nv_dmx.ChromaFormat(),
78
+ bitdepth=nv_dmx.BitDepth()
79
+ )
80
+ if "num_decoder_engines" in caps:
81
+ print("Number of NVDECs:", caps["num_decoder_engines"])
82
+
83
+ nv_dec = nvc.CreateDecoder(gpuid=gpu_id,
84
+ codec=nv_dmx.GetNvCodecId(),
85
+ cudacontext=cuda_ctx.handle,
86
+ cudastream=cuda_stream.handle,
87
+ usedevicememory=use_device_memory,
88
+ latency=decode_latency)
89
+
90
+
91
+ decoded_frame_size = 0
92
+ raw_frame = None
93
+
94
+ seq_triggered = False
95
+ # printing out FPS and pixel format of the stream for convenience
96
+ print("FPS =", nv_dmx.FrameRate())
97
+ # open the file to be decoded in write mode
98
+ with open(dec_file_path, "wb") as decFile:
99
+ # demuxer can be iterated, fetch the packet from demuxer
100
+ frames_decoded = 0
101
+ for packet in nv_dmx:
102
+ # Decode returns a list of packets, range of this list is from [0, size of (decode picture buffer)]
103
+ # size of (decode picture buffer) depends on GPU, fur Turing series its 8
104
+ if decode_latency == nvc.DisplayDecodeLatencyType.LOW or decode_latency == nvc.DisplayDecodeLatencyType.ZERO:
105
+ # Set when the packet contains exactly one frame or one field bitstream data, parser will trigger decode callback immediately when this flag is set.
106
+ packet.decode_flag = nvc.VideoPacketFlag.ENDOFPICTURE
107
+
108
+ for decoded_frame in nv_dec.Decode(packet):
109
+ # 'decoded_frame' contains list of views implementing cuda array interface
110
+ # for nv12, it would contain 2 views for each plane and two planes would be contiguous
111
+ if not seq_triggered:
112
+ decoded_frame_size = nv_dec.GetFrameSize()
113
+ raw_frame = np.ndarray(shape=decoded_frame_size, dtype=np.uint8)
114
+ seq_triggered = True
115
+
116
+ luma_base_addr = decoded_frame.GetPtrToPlane(0)
117
+ if use_device_memory:
118
+ cuda.memcpy_dtoh(raw_frame, luma_base_addr)
119
+ bits = bytearray(raw_frame)
120
+ decFile.write(bits)
121
+ else:
122
+ new_array = cast_address_to_1d_bytearray(base_address=luma_base_addr, size=decoded_frame.framesize())
123
+ decFile.write(bytearray(new_array))
124
+
125
+ frames_decoded += 1
126
+ if frame_count is not None and frame_count > 0 and frames_decoded >= frame_count:
127
+ print(f"Reached requested frame count: {frame_count}")
128
+ return
129
+
130
+ if frame_count is not None and frame_count > 0 and frames_decoded < frame_count:
131
+ print(f"Video ended before reaching requested frame count. Decoded {frames_decoded} frames")
132
+ except nvc.PyNvVCExceptionUnsupported as e:
133
+ print(f"CreateDecoder failure: {e}")
134
+ except Exception as e:
135
+ print(f"An unexpected error occurred: {e}")
136
+ finally:
137
+ if cuda_ctx is not None:
138
+ cuda_ctx.pop()
139
+
140
+ if __name__ == "__main__":
141
+ parser = argparse.ArgumentParser(
142
+ description="This sample application illustrates the demuxing and decoding of a media file."
143
+ )
144
+ parser.add_argument(
145
+ "-g", "--gpu_id",
146
+ type=int,
147
+ default=0,
148
+ help="Check nvidia-smi for available GPUs. Default: 0"
149
+ )
150
+ parser.add_argument(
151
+ "-i", "--encoded_file_path",
152
+ type=Path,
153
+ required=True,
154
+ help="Encoded video file (read from)"
155
+ )
156
+ parser.add_argument(
157
+ "-o", "--raw_file_path",
158
+ type=Path,
159
+ help="Raw NV12 video file (write to). Default: <input_file_name>.yuv"
160
+ )
161
+ parser.add_argument(
162
+ "-d", "--use_device_memory",
163
+ type=int,
164
+ default=1,
165
+ help="Decoder output surface is in device memory (1) else in host memory (0) (default: 1)"
166
+ )
167
+ parser.add_argument(
168
+ "-f",
169
+ "--frame_count", type=int, help="Maximum number of frames to decode")
170
+ parser.add_argument(
171
+ "-dl", "--decode_latency", type=int, default=0, help=""
172
+ "0 - Decoder input and output have a latency of 4 frames, output in display order"
173
+ "1 - Decoder input and output have a latency of 0 frames, output in display order"
174
+ "2 - Decoder input and output have a latency of 0 frames, output in decode order",
175
+ )
176
+ args = parser.parse_args()
177
+ decode_latency = nvc.DisplayDecodeLatencyType.NATIVE
178
+ if args.decode_latency == 1:
179
+ decode_latency = nvc.DisplayDecodeLatencyType.LOW
180
+ elif args.decode_latency == 2:
181
+ decode_latency = nvc.DisplayDecodeLatencyType.ZERO
182
+
183
+
184
+ # Set default raw_file_path if not provided
185
+ if args.raw_file_path is None:
186
+ input_path = Path(args.encoded_file_path)
187
+ args.raw_file_path = input_path.with_suffix('.yuv')
188
+
189
+ decode(args.gpu_id, args.encoded_file_path.as_posix(),
190
+ args.raw_file_path.as_posix(),
191
+ args.use_device_memory,
192
+ decode_latency,
193
+ args.frame_count)
194
+ print(f"Decoded frames written to {args.raw_file_path}")
@@ -0,0 +1,162 @@
1
+ # This copyright notice applies to this file only
2
+ #
3
+ # SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
4
+ # SPDX-License-Identifier: MIT
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining a
7
+ # copy of this software and associated documentation files (the "Software"),
8
+ # to deal in the Software without restriction, including without limitation
9
+ # the rights to use, copy, modify, merge, publish, distribute, sublicense,
10
+ # and/or sell copies of the Software, and to permit persons to whom the
11
+ # Software is furnished to do so, subject to the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be included in
14
+ # all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19
+ # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22
+ # DEALINGS IN THE SOFTWARE.
23
+
24
+ import sys
25
+ import os
26
+ import argparse
27
+ from pathlib import Path
28
+ import pycuda.driver as cuda
29
+ import PyNvVideoCodec as nvc
30
+ from multiprocessing import Process
31
+ import multiprocessing
32
+ from os.path import join, dirname, abspath
33
+ import pycuda.autoinit
34
+
35
+ # Add the parent directory to Python path in a way that works from any working directory
36
+ current_dir = dirname(abspath(__file__))
37
+ parent_dir = dirname(current_dir)
38
+ if parent_dir not in sys.path:
39
+ sys.path.append(parent_dir)
40
+
41
+ def decode_process(gpu_id, enc_file_path, frame_count=None):
42
+ """
43
+ Function to decode media file in a separate process.
44
+
45
+ Parameters:
46
+ gpu_id (int): Ordinal of GPU to use
47
+ enc_file_path (str): Path to file to be decoded
48
+ frame_count (int, optional): Maximum number of frames to decode. If None, 0, or negative, decode all frames.
49
+ """
50
+ # Validate frame count if provided
51
+ if frame_count is None or frame_count <= 0:
52
+ print(f"Decoding all available frames")
53
+ frame_count = None # Reset to None to decode all frames
54
+
55
+ cuda_ctx = None
56
+
57
+ try:
58
+ device_id = gpu_id
59
+ cuda_device = cuda.Device(device_id) # pyright: ignore[reportAttributeAccessIssue]
60
+ cuda_ctx = cuda_device.retain_primary_context()
61
+ cuda_ctx.push()
62
+ cuda_stream = cuda.Stream()
63
+ nv_dmx = nvc.CreateDemuxer(filename=enc_file_path)
64
+ nv_dec = nvc.CreateDecoder(
65
+ gpuid=gpu_id,
66
+ codec=nv_dmx.GetNvCodecId(),
67
+ cudacontext=cuda_ctx.handle,
68
+ cudastream=cuda_stream.handle,
69
+ usedevicememory=True
70
+ )
71
+
72
+ num_decoded_frames = 0
73
+ for packet in nv_dmx:
74
+ for _ in nv_dec.Decode(packet):
75
+ num_decoded_frames += 1
76
+ cuda_stream.synchronize()
77
+
78
+ if frame_count is not None and frame_count > 0 and num_decoded_frames >= frame_count:
79
+ print(f"Process {multiprocessing.current_process().name} reached requested frame count: {frame_count}")
80
+ break
81
+
82
+ if frame_count is not None and frame_count > 0 and num_decoded_frames >= frame_count:
83
+ break
84
+
85
+ if frame_count is not None and frame_count > 0 and num_decoded_frames < frame_count:
86
+ print(f"Video ended before reaching requested frame count for process {multiprocessing.current_process().name}. Decoded {num_decoded_frames} frames")
87
+
88
+ print(f"Process {multiprocessing.current_process().name} completed decoding {num_decoded_frames} frames")
89
+
90
+ except nvc.PyNvVCExceptionUnsupported as e:
91
+ print(f"CreateDecoder failure: {e}")
92
+ except Exception as e:
93
+ print(f"An unexpected error occurred: {e}")
94
+ finally:
95
+ if cuda_ctx is not None:
96
+ cuda_ctx.pop()
97
+
98
+
99
+ def run_parallel_decode(num_processes, gpu_id, enc_file_path, frame_count=None):
100
+ """
101
+ This function demonstrates parallel video decoding using multiple processes.
102
+
103
+ The application creates multiple python processes and runs a different decoding session on each process.
104
+ Each process decodes the same input file independently.
105
+
106
+ Parameters:
107
+ num_processes (int): Number of parallel decoding processes to run
108
+ gpu_id (int): Ordinal of GPU to use
109
+ enc_file_path (str): Path to file to be decoded
110
+ frame_count (int, optional): Maximum number of frames to decode per process
111
+ """
112
+ multiprocessing.set_start_method('spawn')
113
+ processes = []
114
+
115
+ for i in range(num_processes):
116
+ p = Process(target=decode_process, args=(gpu_id, enc_file_path, frame_count))
117
+ p.start()
118
+ processes.append(p)
119
+ print(f"Started decode process {i+1}/{num_processes}")
120
+
121
+ for p in processes:
122
+ p.join()
123
+
124
+ print(f"All {num_processes} decode processes completed")
125
+
126
+
127
+ if __name__ == "__main__":
128
+ parser = argparse.ArgumentParser(
129
+ description="This sample application demonstrates parallel video decoding using multiple processes."
130
+ )
131
+ parser.add_argument(
132
+ "-g", "--gpu_id",
133
+ type=int,
134
+ default=0,
135
+ help="Check nvidia-smi for available GPUs. Default: 0"
136
+ )
137
+ parser.add_argument(
138
+ "-i", "--encoded_file_path",
139
+ type=Path,
140
+ required=True,
141
+ help="Encoded video file (read from)"
142
+ )
143
+ parser.add_argument(
144
+ "-n", "--num_processes",
145
+ default=1,
146
+ type=int,
147
+ help="Number of parallel decode processes to run. (default: 1)"
148
+ )
149
+ parser.add_argument(
150
+ "-f", "--frame_count",
151
+ type=int,
152
+ help="Maximum number of frames to decode per process. If not provided, all frames will be decoded."
153
+ )
154
+
155
+ args = parser.parse_args()
156
+
157
+ run_parallel_decode(
158
+ args.num_processes,
159
+ args.gpu_id,
160
+ args.encoded_file_path.as_posix(),
161
+ args.frame_count
162
+ )
samples/DecodePerf.py ADDED
@@ -0,0 +1,215 @@
1
+ # This copyright notice applies to this file only
2
+ #
3
+ # SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
4
+ # SPDX-License-Identifier: MIT
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining a
7
+ # copy of this software and associated documentation files (the "Software"),
8
+ # to deal in the Software without restriction, including without limitation
9
+ # the rights to use, copy, modify, merge, publish, distribute, sublicense,
10
+ # and/or sell copies of the Software, and to permit persons to whom the
11
+ # Software is furnished to do so, subject to the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be included in
14
+ # all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19
+ # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22
+ # DEALINGS IN THE SOFTWARE.
23
+
24
+ from struct import pack
25
+ import sys
26
+ import os
27
+ import argparse
28
+ from pathlib import Path
29
+ from enum import Enum
30
+ import pycuda.driver as cuda
31
+ import PyNvVideoCodec as nvc
32
+ import numpy as np
33
+ from pycuda.compiler import SourceModule
34
+ import time
35
+ import pycuda.autoinit
36
+ import threading
37
+ from os.path import join, dirname, abspath
38
+
39
+ # Add the parent directory to Python path in a way that works from any working directory
40
+ current_dir = dirname(abspath(__file__))
41
+ parent_dir = dirname(current_dir)
42
+ if parent_dir not in sys.path:
43
+ sys.path.append(parent_dir)
44
+
45
+ # Global variables for tracking performance metrics
46
+ total_fps = 0
47
+ num_frames = 0
48
+
49
+
50
+ def decode_parallel(cuda_ctx, cuda_stream, gpu_id, enc_file_path, frame_count=None):
51
+ """
52
+ Function to decode media file and measure performance in a thread.
53
+
54
+ Parameters:
55
+ cuda_ctx: CUDA context for the thread
56
+ cuda_stream: CUDA stream for the thread
57
+ gpu_id (int): Ordinal of GPU to use [Parameter not in use]
58
+ enc_file_path (str): Path to file to be decoded
59
+ frame_count (int, optional): Maximum number of frames to decode. If None, 0, or negative, decode all frames.
60
+
61
+ Returns:
62
+ None
63
+ """
64
+ global total_fps, num_frames
65
+
66
+ cuda_ctx.push()
67
+
68
+ try:
69
+ if frame_count is None or frame_count <= 0:
70
+ print(f"Decoding all available frames")
71
+ frame_count = None # Reset to None to decode all frames
72
+ nv_dmx = nvc.CreateDemuxer(filename=enc_file_path)
73
+ nv_dec = nvc.CreateDecoder(
74
+ gpuid=gpu_id,
75
+ codec=nv_dmx.GetNvCodecId(),
76
+ cudacontext=cuda_ctx.handle,
77
+ cudastream=cuda_stream.handle,
78
+ usedevicememory=True,
79
+ bWaitForSessionWarmUp=True
80
+ )
81
+
82
+ start = time.perf_counter()
83
+ num_decoded_frames = 0
84
+
85
+ for packet in nv_dmx:
86
+ for _ in nv_dec.Decode(packet):
87
+ num_decoded_frames += 1
88
+ if frame_count is not None and frame_count > 0 and num_decoded_frames >= frame_count:
89
+ print(f"Thread {threading.current_thread().name} reached requested frame count: {frame_count}")
90
+ break
91
+
92
+ if frame_count is not None and frame_count > 0 and num_decoded_frames >= frame_count:
93
+ break
94
+
95
+ if frame_count is not None and frame_count > 0 and num_decoded_frames < frame_count:
96
+ print(f"Video ended before reaching requested frame count for thread {threading.current_thread().name}. Decoded {num_decoded_frames} frames")
97
+
98
+ # Synchronize stream after all decoding is done
99
+ cuda_stream.synchronize()
100
+
101
+ # Calculate accurate elapsed time by subtracting initialization time
102
+ elapsed_time = time.perf_counter() - start
103
+ init_time = nv_dec.GetSessionInitTime()
104
+ init_time /= 1000.00 # Convert to seconds
105
+ elapsed_time -= init_time
106
+
107
+ print(f"Thread {threading.current_thread().name} decoded {num_decoded_frames} frames in {elapsed_time:.2f} seconds")
108
+
109
+ # Update performance metrics with thread-safe operation
110
+ with threading.Lock():
111
+ global total_fps, num_frames
112
+ total_fps += (num_decoded_frames / elapsed_time)
113
+ num_frames += num_decoded_frames
114
+
115
+ except nvc.PyNvVCExceptionUnsupported as e:
116
+ print(f"CreateDecoder failure: {e}")
117
+ except Exception as e:
118
+ print(f"An unexpected error occurred: {e}")
119
+ finally:
120
+ cuda_ctx.pop()
121
+
122
+
123
+ def run_parallel_decode(N, gpu_id, enc_file_path, frame_count=None):
124
+ """
125
+ This function demonstrates decoding using multiple threads.
126
+
127
+ The application creates multiple python threads and runs a different decoding session on each thread.
128
+ The number of sessions can be controlled by the CLI option "--num_threads".
129
+ The application supports decoding frames in device memory and provides detailed performance metrics.
130
+
131
+ Parameters:
132
+ N (int): Number of parallel decoding sessions to run
133
+ gpu_id (int): Ordinal of GPU to use [Parameter not in use]
134
+ enc_file_path (str): Path to file to be decoded
135
+
136
+ Returns:
137
+ None
138
+ """
139
+ global total_fps, num_frames
140
+ total_fps = 0
141
+ num_frames = 0
142
+ threads = []
143
+
144
+ try:
145
+ cuda_device = cuda.Device(gpu_id)
146
+ cuda_ctx = cuda_device.make_context()
147
+ cuda_stream = cuda.Stream()
148
+ nvc.PyNvDecoder.SetSessionCount(N)
149
+
150
+ for i in range(0, N):
151
+ t = threading.Thread(
152
+ target=decode_parallel,
153
+ args=(cuda_ctx, cuda_stream, gpu_id, enc_file_path, frame_count)
154
+ )
155
+ print(f"Started decode thread {i+1}/{N}")
156
+ t.start()
157
+ threads.append(t)
158
+
159
+
160
+ for t in threads:
161
+ t.join()
162
+
163
+ cuda_ctx.pop()
164
+ cuda_ctx.detach()
165
+
166
+ # Print performance summary
167
+ print("\n--- Performance Summary ---")
168
+ print(f"Number of threads: {N}")
169
+ print(f"Total frames decoded: {num_frames}")
170
+ print(f"Total FPS: {total_fps:.2f}")
171
+ print(f"Average FPS per thread: {total_fps/N:.2f}")
172
+
173
+ except Exception as e:
174
+ print(f"Error in parallel decode: {e}")
175
+ if 'cuda_ctx' in locals():
176
+ cuda_ctx.pop()
177
+ cuda_ctx.detach()
178
+
179
+
180
+ if __name__ == "__main__":
181
+ parser = argparse.ArgumentParser(
182
+ description="This sample application demonstrates video decoding using multiple threads."
183
+ )
184
+ parser.add_argument(
185
+ "-g", "--gpu_id",
186
+ type=int,
187
+ default=0,
188
+ help="Check nvidia-smi for available GPUs. Default: 0"
189
+ )
190
+ parser.add_argument(
191
+ "-i", "--encoded_file_path",
192
+ type=Path,
193
+ required=True,
194
+ help="Encoded video file (read from)"
195
+ )
196
+ parser.add_argument(
197
+ "-n", "--num_threads",
198
+ default=1,
199
+ type=int,
200
+ help="Number of parallel decode threads to run. Default: 1"
201
+ )
202
+ parser.add_argument(
203
+ "-f", "--frame_count",
204
+ type=int,
205
+ help="Maximum number of frames to decode. If not provided, all frames will be decoded."
206
+ )
207
+
208
+ args = parser.parse_args()
209
+
210
+ run_parallel_decode(
211
+ args.num_threads,
212
+ args.gpu_id,
213
+ args.encoded_file_path.as_posix(),
214
+ args.frame_count
215
+ )