PyNvVideoCodec 2.0.4__cp314-cp314-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 (65) hide show
  1. PyNvVideoCodec/PyNvVideoCodec_121.cp314-win_amd64.pyd +0 -0
  2. PyNvVideoCodec/PyNvVideoCodec_130.cp314-win_amd64.pyd +0 -0
  3. PyNvVideoCodec/VersionCheck.cp314-win_amd64.pyd +0 -0
  4. PyNvVideoCodec/__init__.py +246 -0
  5. PyNvVideoCodec/avcodec-62.dll +0 -0
  6. PyNvVideoCodec/avdevice-62.dll +0 -0
  7. PyNvVideoCodec/avfilter-11.dll +0 -0
  8. PyNvVideoCodec/avformat-62.dll +0 -0
  9. PyNvVideoCodec/avutil-60.dll +0 -0
  10. PyNvVideoCodec/decoders/SimpleDecoder.py +230 -0
  11. PyNvVideoCodec/decoders/ThreadedDecoder.py +129 -0
  12. PyNvVideoCodec/decoders/__init__.py +22 -0
  13. PyNvVideoCodec/swresample-6.dll +0 -0
  14. PyNvVideoCodec/swscale-9.dll +0 -0
  15. PyNvVideoCodec/transcoder/Transcoder.py +55 -0
  16. PyNvVideoCodec/transcoder/__init__.py +22 -0
  17. benchmarks/cached_decoder_benchmark.py +813 -0
  18. benchmarks/frame_sampling_benchmark.py +765 -0
  19. benchmarks/requirements.txt +47 -0
  20. benchmarks/segmented_transcode_benchmark.py +1546 -0
  21. pynvvideocodec-2.0.4.data/data/external/ffmpeg/readme.txt +15 -0
  22. pynvvideocodec-2.0.4.data/data/external/ffmpeg/src/ffmpeg-8.1.1.tar.xz +0 -0
  23. pynvvideocodec-2.0.4.dist-info/METADATA +84 -0
  24. pynvvideocodec-2.0.4.dist-info/RECORD +65 -0
  25. pynvvideocodec-2.0.4.dist-info/WHEEL +5 -0
  26. pynvvideocodec-2.0.4.dist-info/licenses/LICENSE.txt +22 -0
  27. pynvvideocodec-2.0.4.dist-info/licenses/NOTICES.txt +167 -0
  28. pynvvideocodec-2.0.4.dist-info/top_level.txt +3 -0
  29. samples/advanced/decode.py +173 -0
  30. samples/advanced/decode_from_memory_buffer.py +204 -0
  31. samples/advanced/decode_perf.py +299 -0
  32. samples/advanced/decode_reconfigure.py +184 -0
  33. samples/advanced/decode_sei_msg.py +193 -0
  34. samples/advanced/decode_with_cuda_control.py +214 -0
  35. samples/advanced/decode_with_low_latency.py +183 -0
  36. samples/advanced/encode_config.json +9 -0
  37. samples/advanced/encode_config_lowlatency.json +11 -0
  38. samples/advanced/encode_perf.py +338 -0
  39. samples/advanced/encode_reconfigure.py +211 -0
  40. samples/advanced/encode_sei_msg.py +188 -0
  41. samples/advanced/simple_decode_stats.py +179 -0
  42. samples/advanced/transcode.py +288 -0
  43. samples/basic/create_video_segments.py +187 -0
  44. samples/basic/encode.py +347 -0
  45. samples/basic/encode_config.json +9 -0
  46. samples/basic/segments.txt +3 -0
  47. samples/basic/simple_decode_sampling.py +182 -0
  48. samples/basic/transcode_config.json +6 -0
  49. samples/jupyter/ReconfigureDecoder.png +0 -0
  50. samples/jupyter/SimpleDecoderAPIs.png +0 -0
  51. samples/jupyter/ThreadedDecoder.png +0 -0
  52. samples/jupyter/coco_classes.pickle +0 -0
  53. samples/jupyter/object_detection_tutorial.ipynb +762 -0
  54. samples/jupyter/simple_decode_tutorial.ipynb +651 -0
  55. samples/requirements.txt +78 -0
  56. samples/utils/Utils.py +405 -0
  57. samples/utils/__init__.py +36 -0
  58. samples/utils/decode_parser.py +642 -0
  59. samples/utils/decode_stats_writer.py +177 -0
  60. samples/utils/encode_parallel_utils.py +174 -0
  61. samples/utils/encode_parser.py +357 -0
  62. samples/utils/frame_utils.py +89 -0
  63. samples/utils/sei_message_parser.py +223 -0
  64. samples/utils/sei_parser.py +223 -0
  65. samples/utils/transcode_parser.py +218 -0
@@ -0,0 +1,338 @@
1
+ # This copyright notice applies to this file only
2
+ #
3
+ # SPDX-FileCopyrightText: Copyright (c) 2026 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
+ '''
25
+ This sample demonstrates advanced parallel video encoding using multiple threads or processes.
26
+ It shows how to:
27
+ 1. Maximize GPU utilization using multiple encoder instances
28
+ 2. Measure encoding performance (FPS) accurately for different modes
29
+ 3. Scale encoding across available NVENC engines
30
+ 4. Use either threading or multiprocessing for parallelization
31
+ 5. Manage shared resources and cleanup
32
+ 6. Track and report performance metrics
33
+
34
+ The sample implements two parallel execution modes:
35
+ - Thread Mode:
36
+ * Multiple encoders in the same process
37
+ * Shared memory access
38
+ * Lower overhead, simpler synchronization
39
+ * Best for CPU-bound workloads
40
+
41
+ - Process Mode:
42
+ * Multiple encoders in separate processes
43
+ * IPC memory sharing
44
+ * Better isolation and stability
45
+ * Best for GPU-bound workloads
46
+
47
+
48
+ Usage:
49
+ python encode_perf.py -m <mode> -i <input_file> -s <size> [options]
50
+
51
+ Example:
52
+ python encode_perf.py -m threads -i input.yuv -s 1920x1080 -n 4
53
+
54
+ Arguments:
55
+ -m: Execution mode ('threads' or 'processes')
56
+ -i: Raw input video file
57
+ -s: Frame size (e.g., 1920x1080)
58
+ -if: Input format (default: NV12)
59
+ -c: Codec (H264, HEVC, AV1)
60
+ -n: Number of workers (default: 1)
61
+ -f: Frames per worker (default: all)
62
+ -g: GPU ID (default: 0)
63
+ -json: Encoder config file
64
+
65
+ Note: This performance testing app does not write output files.
66
+ It only measures encoding throughput.
67
+
68
+ Note: For optimal performance:
69
+ - Use thread mode for smaller workloads or when CPU is the bottleneck
70
+ - Use process mode for larger workloads or when GPU is the bottleneck
71
+ - Adjust worker count based on available CPU cores and GPU resources
72
+ '''
73
+
74
+ import gc
75
+ import sys
76
+ import atexit
77
+ import os
78
+ import time
79
+ import queue
80
+ import threading
81
+ import multiprocessing
82
+ from multiprocessing import Process
83
+ import pycuda.driver as cuda
84
+ import PyNvVideoCodec as nvc
85
+ from os.path import dirname, abspath
86
+
87
+
88
+ # Add the parent directory to Python path in a way that works from any working directory
89
+ current_dir = dirname(abspath(__file__))
90
+ parent_dir = dirname(current_dir)
91
+ if parent_dir not in sys.path:
92
+ sys.path.append(parent_dir)
93
+
94
+ # Import utilities for encoding
95
+ from utils.Utils import AppFramePerf
96
+ from utils.encode_parser import parse_perf_args, PERF_MAX_FRAMES
97
+ from utils.frame_utils import get_frame_size, create_memory_map, allocate_device_memory
98
+ from utils.encode_parallel_utils import (
99
+ print_performance_summary,
100
+ encode_thread_worker,
101
+ encode_process_worker
102
+ )
103
+
104
+
105
+ # Global variables to track resources for cleanup
106
+ g_devicedata = None
107
+ g_mmap = None
108
+ g_context = None
109
+
110
+
111
+ def cleanup_resources():
112
+ """Clean up global resources including memory map, GPU memory, and CUDA context."""
113
+ global g_devicedata, g_mmap, g_context
114
+
115
+ gc.collect()
116
+
117
+ if g_mmap:
118
+ try:
119
+ g_mmap.close()
120
+ g_mmap = None
121
+ except:
122
+ pass
123
+
124
+ if g_devicedata:
125
+ try:
126
+ g_devicedata.free()
127
+ g_devicedata = None
128
+ except:
129
+ pass
130
+
131
+ if g_context:
132
+ try:
133
+ g_context.detach()
134
+ g_context = None
135
+ except:
136
+ pass
137
+
138
+
139
+ atexit.register(cleanup_resources)
140
+
141
+
142
+ def run_threaded_encode(args):
143
+ """Run encoding with multiple threads using shared device memory."""
144
+ print(f"Initializing {args.num_workers} encode thread{'s' if args.num_workers > 1 else ''}")
145
+
146
+ # Calculate total frames and show warning if needed
147
+ framesize = get_frame_size(args.width, args.height, args.format)
148
+ file_size = os.path.getsize(args.raw_file_path)
149
+ available_frames = file_size // framesize
150
+
151
+ if available_frames > PERF_MAX_FRAMES and args.frame_count == 0:
152
+ print(f"\nWarning: File contains {available_frames} frames. Limiting to {PERF_MAX_FRAMES} frames for performance testing.")
153
+ elif args.frame_count > PERF_MAX_FRAMES:
154
+ print(f"\nWarning: Requested {args.frame_count} frames. Limiting to {PERF_MAX_FRAMES} frames for performance testing.")
155
+
156
+ global g_devicedata, g_mmap, g_context
157
+
158
+ try:
159
+ # Initialize CUDA
160
+ cuda.init()
161
+ cuda_device = cuda.Device(args.gpu_id)
162
+ g_context = cuda_device.make_context()
163
+ shared_stream = cuda.Stream()
164
+
165
+ # Open input file and prepare data
166
+ with open(args.raw_file_path, "rb") as input_file:
167
+ framesize = get_frame_size(args.width, args.height, args.format)
168
+ g_mmap = create_memory_map(input_file)
169
+
170
+ # Calculate frames to process
171
+ frames_to_process = args.frame_count if args.frame_count > 0 else available_frames
172
+ frames_to_process = min(frames_to_process, available_frames, PERF_MAX_FRAMES)
173
+ bytes_to_read = frames_to_process * framesize
174
+
175
+ print(f"File contains {available_frames} frames, processing {frames_to_process} frames")
176
+
177
+ # Read and copy data to GPU
178
+ hostdata = g_mmap.read(bytes_to_read)
179
+ g_devicedata = allocate_device_memory(hostdata)
180
+
181
+ # Create encoders
182
+ encoders = []
183
+ for _ in range(args.num_workers):
184
+ config = args.config.copy()
185
+ encoder = nvc.CreateEncoder(
186
+ args.width, args.height, args.format, False,
187
+ cudacontext=g_context.handle,
188
+ cudastream=shared_stream.handle,
189
+ **config
190
+ )
191
+ encoders.append(encoder)
192
+
193
+ # Run encoding threads
194
+ result_queue = queue.Queue()
195
+ begin = time.perf_counter()
196
+
197
+ threads = []
198
+ for i in range(args.num_workers):
199
+ t = threading.Thread(
200
+ target=encode_thread_worker,
201
+ args=(
202
+ i, args.raw_file_path, encoders[i], g_devicedata,
203
+ framesize, args.width, args.height, args.format,
204
+ args.frame_count, result_queue
205
+ )
206
+ )
207
+ threads.append(t)
208
+ t.start()
209
+
210
+ total_frames = sum(result_queue.get() for _ in range(args.num_workers))
211
+ duration = time.perf_counter() - begin
212
+ for t in threads:
213
+ t.join()
214
+
215
+ print_performance_summary("threads", args.num_workers, total_frames, duration)
216
+
217
+ # Cleanup
218
+ for encoder in encoders:
219
+ del encoder
220
+ encoders.clear()
221
+
222
+ except Exception as e:
223
+ print(f"An error occurred: {e}")
224
+ finally:
225
+ if g_context is not None:
226
+ g_context.pop()
227
+
228
+
229
+ def run_multiprocess_encode(args):
230
+ """Run encoding with multiple processes using IPC memory sharing."""
231
+ print(f"Initializing {args.num_workers} encode process{'es' if args.num_workers > 1 else ''}")
232
+
233
+ # Calculate total frames and show warning if needed
234
+ framesize = get_frame_size(args.width, args.height, args.format)
235
+ file_size = os.path.getsize(args.raw_file_path)
236
+ available_frames = file_size // framesize
237
+
238
+ if available_frames > PERF_MAX_FRAMES and args.frame_count == 0:
239
+ print(f"\nWarning: File contains {available_frames} frames. Limiting to {PERF_MAX_FRAMES} frames for performance testing.")
240
+ elif args.frame_count > PERF_MAX_FRAMES:
241
+ print(f"\nWarning: Requested {args.frame_count} frames. Limiting to {PERF_MAX_FRAMES} frames for performance testing.")
242
+
243
+ framesize = get_frame_size(args.width, args.height, args.format)
244
+
245
+ with open(args.raw_file_path, "rb") as input_file:
246
+ # Calculate frames to process
247
+ file_size = os.path.getsize(args.raw_file_path)
248
+ total_frames = file_size // framesize
249
+ frames_to_process = args.frame_count if args.frame_count > 0 else total_frames
250
+ frames_to_process = min(frames_to_process, total_frames, PERF_MAX_FRAMES)
251
+ bytes_to_read = frames_to_process * framesize
252
+
253
+ print(f"File contains {total_frames} frames, processing {frames_to_process} frames")
254
+
255
+ cuda.init()
256
+ parent_ctx = cuda.Device(args.gpu_id).make_context()
257
+ m = None
258
+ devicedata = None
259
+ ctx_popped_for_workers = False
260
+ try:
261
+ m = create_memory_map(input_file)
262
+ hostdata = m.read(bytes_to_read)
263
+ devicedata = allocate_device_memory(hostdata)
264
+ devptrhandle = cuda.mem_get_ipc_handle(devicedata)
265
+ # Drop parent's CUDA context while workers run. Otherwise the parent and
266
+ # children keep separate contexts resident on the same GPU and Nsight
267
+ # shows heavy GPU_CONTEXT_SWITCH_EVENTS (order-of-magnitude vs thread mode).
268
+ parent_ctx.pop()
269
+ ctx_popped_for_workers = True
270
+
271
+ result_queue = multiprocessing.Queue()
272
+ ready_queue = multiprocessing.Queue()
273
+ go_event = multiprocessing.Event()
274
+
275
+ processes = []
276
+ for i in range(args.num_workers):
277
+ config = args.config.copy()
278
+ p = Process(
279
+ target=encode_process_worker,
280
+ args=(
281
+ i, args.raw_file_path, args.width, args.height,
282
+ args.format, devptrhandle, framesize, config,
283
+ frames_to_process, result_queue, ready_queue, go_event
284
+ )
285
+ )
286
+ processes.append(p)
287
+ p.start()
288
+
289
+ readies = [ready_queue.get() for _ in range(args.num_workers)]
290
+ if not all(ok for _pid, ok in readies):
291
+ for p in processes:
292
+ if p.is_alive():
293
+ p.terminate()
294
+ p.join()
295
+ print("Encoding aborted: setup failed in one or more workers.")
296
+ else:
297
+ begin = time.perf_counter()
298
+ go_event.set()
299
+ # Stop the timer when workers report frame counts, not when processes exit.
300
+ # The child still runs finally (encoder destructor, CUDA context teardown) after
301
+ # result_queue.put; Process.join() waits for that too (~0.3–0.5s+), unlike
302
+ # Thread.join() after put (see thread path above—same get-then-join pattern).
303
+ total_frames = sum(result_queue.get() for _ in range(args.num_workers))
304
+ duration = time.perf_counter() - begin
305
+ for p in processes:
306
+ p.join()
307
+ print_performance_summary("processes", args.num_workers, total_frames, duration)
308
+ finally:
309
+ if ctx_popped_for_workers:
310
+ parent_ctx.push()
311
+ if devicedata is not None:
312
+ devicedata.free()
313
+ if m is not None:
314
+ m.close()
315
+ parent_ctx.pop()
316
+ parent_ctx.detach()
317
+
318
+ print("All encoding processes completed")
319
+
320
+
321
+ if __name__ == "__main__":
322
+ args = parse_perf_args(
323
+ "Parallel video encoding application supporting both multithreading and multiprocessing for performance testing."
324
+ )
325
+
326
+ # Set multiprocessing start method for compatibility
327
+ if args.mode == "process":
328
+ multiprocessing.set_start_method('spawn')
329
+
330
+ try:
331
+ if args.mode == "thread":
332
+ run_threaded_encode(args)
333
+ else: # processes
334
+ run_multiprocess_encode(args)
335
+ except Exception as e:
336
+ print(f"An error occurred: {e}")
337
+ finally:
338
+ cleanup_resources()
@@ -0,0 +1,211 @@
1
+ # This copyright notice applies to this file only
2
+ #
3
+ # SPDX-FileCopyrightText: Copyright (c) 2026 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
+ '''
25
+ This sample demonstrates dynamic encoder reconfiguration for bitrate control.
26
+ It shows how to:
27
+ 1. Change encoder parameters at runtime
28
+ 2. Modify bitrate without resetting the encoder
29
+ 3. Handle VBV (Video Buffer Verifier) parameters
30
+ 4. Maintain encoding quality during changes
31
+
32
+ Note: Bitrate changes occur every 100 frames:
33
+ - Frame 0: Original bitrate
34
+ - Frame 100: Half bitrate
35
+ - Frame 200: Original bitrate
36
+ - And so on...
37
+
38
+ Usage:
39
+ python encode_reconfigure.py -i <input_file> -s <size> [options]
40
+
41
+ Example:
42
+ python encode_reconfigure.py -i input.yuv -s 1920x1080
43
+
44
+ Arguments:
45
+ -i: Raw input video file
46
+ -s: Frame size (e.g., 1920x1080)
47
+ -if: Input format (default: NV12)
48
+ -c: Codec (H264, HEVC, AV1)
49
+ -f: Number of frames (default: all)
50
+ -g: GPU ID (default: 0)
51
+ -json: Encoder config file
52
+
53
+
54
+ '''
55
+
56
+ import PyNvVideoCodec as nvc
57
+ import numpy as np
58
+ import sys
59
+ import os
60
+ from os.path import dirname, abspath
61
+
62
+ # Add the parent directory to Python path in a way that works from any working directory
63
+ current_dir = dirname(abspath(__file__))
64
+ parent_dir = dirname(current_dir)
65
+ if parent_dir not in sys.path:
66
+ sys.path.append(parent_dir)
67
+
68
+ # Import utilities for GPU buffer mode
69
+ try:
70
+ from utils.Utils import AppFrame, FetchCPUFrame, FetchGPUFrame
71
+ GPU_UTILS_AVAILABLE = True
72
+ except ImportError:
73
+ GPU_UTILS_AVAILABLE = False
74
+ print("Warning: Utils module not found. GPU buffer mode will be disabled.")
75
+
76
+ from utils.encode_parser import parse_reconfigure_encode_args
77
+
78
+
79
+ # Constants
80
+ BITRATE_CHANGE_INTERVAL = 100
81
+ BITRATE_REDUCTION_FACTOR = 2
82
+
83
+
84
+ def encode(gpu_id, dec_file_path, enc_file_path, width, height, fmt, config_params, frame_count=0):
85
+ """
86
+ Encode video with dynamic bitrate reconfiguration.
87
+
88
+ This function demonstrates bitrate change at runtime without the need to reset the encoder session.
89
+ The application reduces the bitrate by half and then restores it to the original value after
90
+ every 100 frames.
91
+
92
+ Parameters:
93
+ gpu_id (int): Ordinal of GPU to use [Parameter not in use]
94
+ dec_file_path (str): Path to file to be decoded
95
+ enc_file_path (str): Path to output file for encoded frames
96
+ width (int): Width of encoded frame
97
+ height (int): Height of encoded frame
98
+ fmt (str): Surface format string in uppercase (e.g., "NV12")
99
+ config_params (dict): Key-value pairs providing fine-grained control on encoding
100
+
101
+ Returns:
102
+ None
103
+
104
+ Example:
105
+ >>> encode(0, "input.yuv", "output.h264", 1920, 1080, "NV12", {"codec": "h264"})
106
+ Encode 1080p NV12 raw YUV into elementary bitstream using H.264 codec
107
+ """
108
+ try:
109
+ with open(dec_file_path, "rb") as decFile, open(enc_file_path, "wb") as encFile:
110
+ # Create encoder object
111
+ config_params["gpu_id"] = gpu_id
112
+
113
+ if "tuning_info" in config_params:
114
+ tuning_info_val = config_params['tuning_info']
115
+ if tuning_info_val == "low_latency":
116
+ config_params['bf'] = 0
117
+
118
+ nvenc = nvc.CreateEncoder(width, height, fmt, False, **config_params)
119
+ # Create input frame list
120
+ input_frame_list = [AppFrame(width, height, fmt) for _ in range(1, 5)]
121
+
122
+ # Get initial encoder parameters
123
+ reconf_params = nvenc.GetEncodeReconfigureParams()
124
+ original_avgbitrate = reconf_params.averageBitrate
125
+ if "vbvbufsize" in config_params:
126
+ original_vbvbuffersize = config_params['vbvbufsize']
127
+ else:
128
+ original_vbvbuffersize = int(original_avgbitrate * reconf_params.frameRateDen / reconf_params.frameRateNum)
129
+
130
+ original_vbvinitdelay = original_vbvbuffersize
131
+
132
+
133
+ # Process frames
134
+ # Calculate total available frames from file size
135
+ file_size = os.path.getsize(dec_file_path)
136
+ available_frames = file_size // input_frame_list[0].frameSize
137
+
138
+ # Determine frames to process
139
+ frames_to_process = frame_count if frame_count > 0 else available_frames
140
+ frames_to_process = min(frames_to_process, available_frames)
141
+
142
+ print(f"File contains {available_frames} frames, processing {frames_to_process} frames")
143
+
144
+ for i, input_gpu_frame in enumerate(
145
+ FetchGPUFrame(
146
+ input_frame_list,
147
+ FetchCPUFrame(decFile, input_frame_list[0].frameSize),
148
+ frames_to_process
149
+ )
150
+ ):
151
+ # Reconfigure bitrate every 100 frames
152
+ if i % BITRATE_CHANGE_INTERVAL == 0:
153
+ if i % (BITRATE_CHANGE_INTERVAL * 2) != 0:
154
+ # Reduce bitrate by half
155
+ reconf_params.averageBitrate = int(original_avgbitrate / BITRATE_REDUCTION_FACTOR)
156
+ reconf_params.vbvBufferSize = int(original_vbvbuffersize / BITRATE_REDUCTION_FACTOR)
157
+ reconf_params.vbvInitialDelay = int(original_vbvinitdelay / BITRATE_REDUCTION_FACTOR)
158
+ else:
159
+ # Restore original bitrate
160
+ reconf_params.averageBitrate = original_avgbitrate
161
+ reconf_params.vbvBufferSize = original_vbvbuffersize
162
+ reconf_params.vbvInitialDelay = original_vbvinitdelay
163
+
164
+ nvenc.Reconfigure(reconf_params)
165
+
166
+ # Encode frame
167
+ pic_params = nvc.NV_ENC_PIC_PARAMS()
168
+ pic_params.inputTimeStamp = i
169
+ packets = nvenc.Encode(input_gpu_frame, pic_params)
170
+ encFile.write(
171
+ bytearray(b"".join(bytes(p["data"]) for p in packets))
172
+ )
173
+
174
+ # Flush encoder queue
175
+ flush_packets = nvenc.EndEncode()
176
+ encFile.write(bytearray(b"".join(bytes(p["data"]) for p in flush_packets)))
177
+ print(f"Completed encoding {frames_to_process} frames using CPU buffers")
178
+ print(f"Output file: {enc_file_path}")
179
+
180
+ except nvc.PyNvVCExceptionUnsupported as e:
181
+ print(f"CreateEncoder failure: {e}")
182
+ except Exception as e:
183
+ print(f"An unexpected error occurred: {e}")
184
+
185
+
186
+ if __name__ == "__main__":
187
+ args = parse_reconfigure_encode_args(
188
+ "This sample application demonstrates bitrate change at runtime without resetting the encoder session."
189
+ )
190
+
191
+ print("=" * 60)
192
+ print("Configuration:")
193
+ print(f"Input: {args.raw_file_path}")
194
+ print(f"Output: {args.encoded_file_path}")
195
+ print(f"Size: {args.width}x{args.height}")
196
+ print(f"Format: {args.format}")
197
+ print(f"Codec: {args.codec}")
198
+ print(f"GPU ID: {args.gpu_id}")
199
+ print(f"Frame count: {args.frame_count if args.frame_count > 0 else 'All frames'}")
200
+ print("=" * 60)
201
+
202
+ encode(
203
+ args.gpu_id,
204
+ args.raw_file_path.as_posix(),
205
+ args.encoded_file_path.as_posix(),
206
+ args.width,
207
+ args.height,
208
+ args.format,
209
+ args.config,
210
+ args.frame_count
211
+ )