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/Encode.py ADDED
@@ -0,0 +1,189 @@
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 PyNvVideoCodec as nvc
25
+ import json
26
+ import argparse
27
+ from pathlib import Path
28
+ import io
29
+ import os
30
+ import sys
31
+ from os.path import join, dirname, abspath
32
+
33
+ # Add the parent directory to Python path in a way that works from any working directory
34
+ current_dir = dirname(abspath(__file__))
35
+ parent_dir = dirname(current_dir)
36
+ if parent_dir not in sys.path:
37
+ sys.path.append(parent_dir)
38
+
39
+ from Utils import AppFrame
40
+ from Utils import FetchCPUFrame
41
+ from Utils import FetchGPUFrame
42
+
43
+
44
+ def encode(gpu_id, dec_file_path, enc_file_path, width, height, fmt, config_params, frame_count=1000):
45
+ """
46
+ Encode frames using CUDA device buffers as input.
47
+
48
+ This function reads image data from a file and loads it to CUDA input buffers using
49
+ FetchGPUFrame(). The encoder copies the CUDA buffers and submits them to NVENC hardware
50
+ for encoding. Video memory buffer is allocated to get the NVENC hardware output.
51
+ The output is copied from video memory to host memory for file storage.
52
+
53
+ Parameters:
54
+ gpu_id (int): Ordinal of GPU to use
55
+ dec_file_path (str): Path to file to be decoded
56
+ enc_file_path (str): Path to output file for encoded frames
57
+ width (int): Width of encoded frame
58
+ height (int): Height of encoded frame
59
+ fmt (str): Surface format string in uppercase (e.g., "NV12")
60
+ config_params (dict): Key-value pairs providing fine-grained control on encoding
61
+ frame_count (int, optional): Number of frames to encode. If None, 0, or negative, encode all frames from input.
62
+
63
+ Returns:
64
+ None
65
+
66
+ Example:
67
+ >>> encode(0, "input.yuv", "output.h264", 1920, 1080, "NV12", {"codec": "h264"})
68
+ Encode 1080p NV12 raw YUV into elementary bitstream using H.264 codec
69
+ """
70
+ try:
71
+ caps = nvc.GetEncoderCaps(codec=config_params["codec"])
72
+ if "num_encoder_engines" in caps:
73
+ print(f"Number of NVENCs: {caps['num_encoder_engines']}")
74
+
75
+ with open(dec_file_path, "rb") as decFile, open(enc_file_path, "wb") as encFile:
76
+ config_params["gpu_id"] = gpu_id
77
+ nvenc = nvc.CreateEncoder(width, height, fmt, False, **config_params)
78
+ input_frame_list = [AppFrame(width, height, fmt) for _ in range(1, 5)]
79
+
80
+ # Calculate total available frames from file size
81
+ file_size = os.path.getsize(dec_file_path)
82
+ available_frames = file_size // input_frame_list[0].frameSize
83
+
84
+ if frame_count is None or frame_count == 0:
85
+ print(f"Encoding all available frames")
86
+ frame_count = available_frames
87
+ elif frame_count < 0 or frame_count > available_frames:
88
+ print(f"Warning: Invalid frame count {frame_count}. Encoding all available frames.")
89
+ frame_count = available_frames
90
+
91
+ for input_gpu_frame in FetchGPUFrame(
92
+ input_frame_list,
93
+ FetchCPUFrame(decFile, input_frame_list[0].frameSize),
94
+ frame_count
95
+ ):
96
+ bitstream = nvenc.Encode(input_gpu_frame)
97
+ bitstream = bytearray(bitstream)
98
+ encFile.write(bitstream)
99
+ bitstream = nvenc.EndEncode() # flush encoder queue
100
+ bitstream = bytearray(bitstream)
101
+ encFile.write(bitstream)
102
+ except nvc.PyNvVCExceptionUnsupported as e:
103
+ print(f"CreateEncoder failure: {e}")
104
+ except Exception as e:
105
+ print(f"An unexpected error occurred: {e}")
106
+
107
+
108
+ if __name__ == "__main__":
109
+ parser = argparse.ArgumentParser(
110
+ description="This sample application illustrates encoding of frames using CUDA device buffers as input."
111
+ )
112
+
113
+ parser.add_argument(
114
+ "-g", "--gpu_id",
115
+ type=int,
116
+ default=0,
117
+ help="Check nvidia-smi for available GPUs. Default: 0"
118
+ )
119
+ parser.add_argument(
120
+ "-i", "--raw_file_path",
121
+ type=Path,
122
+ required=True,
123
+ help="Raw video file (read from)"
124
+ )
125
+ parser.add_argument(
126
+ "-o", "--encoded_file_path",
127
+ type=Path,
128
+ help="Encoded video file (write to). Default: <input_file_name>.<codec>"
129
+ )
130
+ parser.add_argument(
131
+ "-s", "--size",
132
+ type=str,
133
+ required=True,
134
+ help="widthxheight of raw frame (e.g., 1920x1080)"
135
+ )
136
+ parser.add_argument(
137
+ "-if", "--format",
138
+ type=str,
139
+ default="NV12",
140
+ help="Format of input file. (default: NV12)"
141
+ )
142
+ parser.add_argument(
143
+ "-c", "--codec",
144
+ type=str,
145
+ default="h264",
146
+ help="Video codec. Default is h264. (other options: hevc, av1)"
147
+ )
148
+ parser.add_argument(
149
+ "-json", "--config_file",
150
+ type=str,
151
+ default=join(current_dir, "encode_config.json"),
152
+ help="Path of JSON config file (default: encode_config.json in the same directory)"
153
+ )
154
+ parser.add_argument(
155
+ "-f", "--frame_count",
156
+ type=int,
157
+ default=0,
158
+ help="Number of frames to encode (0 for all frames)"
159
+ )
160
+
161
+ args = parser.parse_args()
162
+ config = {}
163
+
164
+ # Set default encoded_file_path if not provided
165
+ if args.encoded_file_path is None:
166
+ input_path = Path(args.raw_file_path)
167
+ args.encoded_file_path = input_path.with_suffix(f'.{args.codec}')
168
+
169
+ if args.config_file:
170
+ with open(args.config_file) as jsonFile:
171
+ config = json.loads(jsonFile.read())
172
+ config["preset"] = config["preset"].upper()
173
+
174
+ args.codec = args.codec.lower()
175
+ args.format = args.format.upper()
176
+ config["codec"] = args.codec
177
+ width, height = map(int, args.size.split("x"))
178
+
179
+ encode(
180
+ args.gpu_id,
181
+ args.raw_file_path.as_posix(),
182
+ args.encoded_file_path.as_posix(),
183
+ width,
184
+ height,
185
+ args.format,
186
+ config,
187
+ args.frame_count
188
+ )
189
+ print(f"Encoded frames written to {args.encoded_file_path}")
@@ -0,0 +1,220 @@
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 PyNvVideoCodec as nvc
25
+ import numpy as np
26
+ import json
27
+ import argparse
28
+ from pathlib import Path
29
+ import os
30
+ from os.path import join, dirname, abspath
31
+ import sys
32
+
33
+ # Add the parent directory to Python path in a way that works from any working directory
34
+ current_dir = dirname(abspath(__file__))
35
+ parent_dir = dirname(current_dir)
36
+ if parent_dir not in sys.path:
37
+ sys.path.append(parent_dir)
38
+
39
+
40
+ def GetFrameSize(width, height, surface_format):
41
+ """
42
+ Calculate the size of a frame in bytes based on its dimensions and format.
43
+
44
+ Parameters:
45
+ width (int): Width of the frame
46
+ height (int): Height of the frame
47
+ surface_format (str): Format of the surface (e.g., "NV12", "ARGB", "YUV444")
48
+
49
+ Returns:
50
+ int: Size of the frame in bytes
51
+ """
52
+ if surface_format in ("ARGB", "ABGR"):
53
+ return width * height * 4
54
+ elif surface_format == "YUV444":
55
+ return width * height * 3
56
+ elif surface_format == "YUV420":
57
+ return int(width * height * 3 / 2)
58
+ elif surface_format == "P010":
59
+ return int(width * height * 3 / 2 * 2)
60
+ elif surface_format == "YUV444_16BIT":
61
+ return int(width * height * 3 * 2)
62
+ else:
63
+ return int(width * height * 3 / 2)
64
+
65
+
66
+ def FetchCPUFrame(dec_file, frame_size):
67
+ """
68
+ Generator function to fetch frames from a file.
69
+
70
+ Parameters:
71
+ dec_file: File object to read from
72
+ frame_size (int): Size of each frame in bytes
73
+
74
+ Yields:
75
+ numpy.ndarray: Frame data as a numpy array
76
+ """
77
+ yield np.fromfile(dec_file, np.uint8, count=frame_size)
78
+
79
+
80
+ def encode(gpu_id, dec_file_path, enc_file_path, width, height, fmt, config_params, frame_count=None):
81
+ """
82
+ Encode frames using host memory buffers as input.
83
+
84
+ This function reads image data from a file and copies it to CUDA buffers for encoding.
85
+ The encoder submits the data to NVENC hardware for encoding. Video memory buffer is
86
+ allocated to get the NVENC hardware output. The output is copied from video memory
87
+ to host memory for file storage.
88
+
89
+ Parameters:
90
+ gpu_id (int): Ordinal of GPU to use.
91
+ dec_file_path (str): Path to file to be decoded
92
+ enc_file_path (str): Path to output file for encoded frames
93
+ width (int): Width of encoded frame
94
+ height (int): Height of encoded frame
95
+ fmt (str): Surface format string in uppercase (e.g., "NV12")
96
+ config_params (dict): Key-value pairs providing fine-grained control on encoding
97
+ frame_count (int, optional): Number of frames to encode. If None, 0 or negative, encodes all frames from input.
98
+
99
+ Returns:
100
+ None
101
+
102
+ Example:
103
+ >>> encode(0, "input.yuv", "output.h264", 1920, 1080, "NV12", 1, {"codec": "h264"})
104
+ Encode 1080p NV12 raw YUV into elementary bitstream using H.264 codec
105
+ """
106
+ try:
107
+ frame_size = GetFrameSize(width, height, fmt)
108
+ with open(dec_file_path, "rb") as dec_file, open(enc_file_path, "wb") as enc_file:
109
+ config_params["gpu_id"] = gpu_id
110
+ nvenc = nvc.CreateEncoder(width, height, fmt, True, **config_params) # create encoder object
111
+
112
+ # Calculate total available frames from file size
113
+ file_size = os.path.getsize(dec_file_path)
114
+ available_frames = file_size // frame_size
115
+
116
+ if frame_count is None or frame_count == 0:
117
+ print(f"Encoding all available frames")
118
+ frame_count = available_frames
119
+ elif frame_count < 0 or frame_count > available_frames:
120
+ print(f"Warning: Invalid frame count {frame_count}. Encoding all available frames.")
121
+ frame_count = available_frames
122
+
123
+ for _ in range(frame_count):
124
+ chunk = np.fromfile(dec_file, np.uint8, count=frame_size)
125
+ if chunk.size != 0:
126
+ bitstream = nvenc.Encode(chunk)
127
+ enc_file.write(bytearray(bitstream))
128
+
129
+ # Flush encoder queue
130
+ bitstream = nvenc.EndEncode()
131
+ enc_file.write(bytearray(bitstream))
132
+ print(f"Encoded {frame_count} frames. Output file: {enc_file_path}")
133
+ except nvc.PyNvVCExceptionUnsupported as e:
134
+ print(f"CreateEncoder failure: {e}")
135
+ except Exception as e:
136
+ print(f"An unexpected error occurred: {e}")
137
+
138
+
139
+ if __name__ == "__main__":
140
+ parser = argparse.ArgumentParser(
141
+ description="This sample application illustrates encoding of frames using host memory buffers as input."
142
+ )
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", "--raw_file_path",
152
+ type=Path,
153
+ required=True,
154
+ help="Raw video file (read from)"
155
+ )
156
+ parser.add_argument(
157
+ "-o", "--encoded_file_path",
158
+ type=Path,
159
+ help="Encoded video file (write to). Default: <raw_file_path>.<codec>"
160
+ )
161
+ parser.add_argument(
162
+ "-s", "--size",
163
+ type=str,
164
+ required=True,
165
+ help="widthxheight of raw frame (e.g., 1920x1080)"
166
+ )
167
+ parser.add_argument(
168
+ "-if", "--format",
169
+ type=str,
170
+ default="NV12",
171
+ help="Format of input file. (default: NV12)"
172
+ )
173
+ parser.add_argument(
174
+ "-c", "--codec",
175
+ type=str,
176
+ default="H264",
177
+ help="Video codec (HEVC, H264, AV1). (default: H264)"
178
+ )
179
+ parser.add_argument(
180
+ "-json", "--config_file",
181
+ type=str,
182
+ default=join(current_dir, "encode_config.json"),
183
+ help="Path of JSON config file (default: encode_config.json in the same directory)"
184
+ )
185
+
186
+ parser.add_argument(
187
+ "-f", "--frame_count",
188
+ type=int,
189
+ default=0,
190
+ help="Number of frames to encode (0 for all frames)"
191
+ )
192
+
193
+ args = parser.parse_args()
194
+ config = {}
195
+
196
+ # Set default encoded_file_path if not provided
197
+ if args.encoded_file_path is None:
198
+ input_path = Path(args.raw_file_path)
199
+ args.encoded_file_path = input_path.with_suffix(f'.{args.codec}')
200
+
201
+ if args.config_file:
202
+ with open(args.config_file) as jsonFile:
203
+ config = json.loads(jsonFile.read())
204
+ config["preset"] = config["preset"].upper()
205
+
206
+ args.codec = args.codec.lower()
207
+ args.format = args.format.upper()
208
+ config["codec"] = args.codec
209
+ width, height = map(int, args.size.split("x"))
210
+
211
+ encode(
212
+ args.gpu_id,
213
+ args.raw_file_path.as_posix(),
214
+ args.encoded_file_path.as_posix(),
215
+ width,
216
+ height,
217
+ args.format,
218
+ config,
219
+ args.frame_count
220
+ )
@@ -0,0 +1,268 @@
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 argparse
25
+ from pathlib import Path
26
+ import pycuda.driver as cuda
27
+ import PyNvVideoCodec as nvc
28
+ from pycuda.autoinit import context
29
+ import mmap
30
+ import json
31
+ import multiprocessing
32
+ from multiprocessing import Process
33
+ import os
34
+ import sys
35
+ from os.path import join, dirname, abspath
36
+
37
+ # Add the parent directory to Python path in a way that works from any working directory
38
+ current_dir = dirname(abspath(__file__))
39
+ parent_dir = dirname(current_dir)
40
+ if parent_dir not in sys.path:
41
+ sys.path.append(parent_dir)
42
+
43
+ from Utils import AppFramePerf
44
+
45
+
46
+ def GetFrameSize(width, height, surface_format):
47
+ """
48
+ Calculate the size of a frame in bytes based on its dimensions and format.
49
+
50
+ Parameters:
51
+ width (int): Width of the frame
52
+ height (int): Height of the frame
53
+ surface_format (str): Format of the surface (e.g., "NV12", "ARGB", "YUV444")
54
+
55
+ Returns:
56
+ int: Size of the frame in bytes
57
+ """
58
+ if surface_format in ("ARGB", "ABGR"):
59
+ return width * height * 4
60
+ elif surface_format == "YUV444":
61
+ return width * height * 3
62
+ elif surface_format == "YUV420":
63
+ return int(width * height * 3 / 2)
64
+ elif surface_format == "P010":
65
+ return int(width * height * 3 / 2 * 2)
66
+ elif surface_format == "YUV444_16BIT":
67
+ return int(width * height * 3 * 2)
68
+ else:
69
+ return int(width * height * 3 / 2)
70
+
71
+
72
+ def encode_process(gpu_id, dec_file_path, width, height, fmt, h, framesize, config_params, frame_count=None):
73
+ """
74
+ Function to encode frames in a separate process.
75
+
76
+ This function demonstrates how to set up and run video encoding in a separate process,
77
+ showing proper handling of CUDA contexts and device memory in a multiprocessing environment.
78
+
79
+ Parameters:
80
+ gpu_id (int): Ordinal of GPU to use
81
+ dec_file_path (str): Path to file to be decoded
82
+ width (int): Width of encoded frame
83
+ height (int): Height of encoded frame
84
+ fmt (str): Surface format string in uppercase (e.g., "NV12")
85
+ h: IPC memory handle for CUDA device memory
86
+ framesize (int): Size of each frame in bytes
87
+ config_params (dict): Key-value pairs providing fine-grained control on encoding
88
+ frame_count (int, optional): Number of frames to encode. If None, 0, or negative, encode all frames from input.
89
+
90
+ Returns:
91
+ None
92
+ """
93
+ try:
94
+ process_id = multiprocessing.current_process().name
95
+ print(f"Starting encode process {process_id}")
96
+
97
+ config_params["gpu_id"] = gpu_id
98
+ nvenc = nvc.CreateEncoder(width, height, fmt, False, **config_params)
99
+ devicedata = cuda.IPCMemoryHandle(h)
100
+ input_gpu_frame = AppFramePerf(width, height, fmt, devicedata, 0)
101
+
102
+ # Calculate total available frames from file size
103
+ file_size = os.path.getsize(dec_file_path)
104
+ available_frames = file_size // framesize
105
+
106
+ if frame_count is None or frame_count == 0:
107
+ print(f"Encoding all available frames")
108
+ frame_count = available_frames
109
+ elif frame_count < 0 or frame_count > available_frames:
110
+ print(f"Warning: Invalid frame count {frame_count}. Process {process_id} will process all available frames.")
111
+ frame_count = available_frames
112
+
113
+ frames_encoded = 0
114
+ for i in range(frame_count):
115
+ input_gpu_frame.gpuAlloc = int(devicedata) + (i * framesize)
116
+ bitstream = nvenc.Encode(input_gpu_frame)
117
+ frames_encoded += 1
118
+
119
+ # Flush encoder queue
120
+ bitstream = nvenc.EndEncode()
121
+ print(f"Process {process_id} completed encoding {frames_encoded} frames")
122
+
123
+ except nvc.PyNvVCExceptionUnsupported as e:
124
+ print(f"CreateEncoder failure in process {process_id}: {e}")
125
+ except Exception as e:
126
+ print(f"An unexpected error occurred in process {process_id}: {e}")
127
+
128
+
129
+ def run_parallel_encode(num_processes, gpu_id, enc_file_path, width, height, fmt, framesize, config, frame_count=None):
130
+ """
131
+ Demonstrates parallel video encoding using multiple processes.
132
+
133
+ This function shows how to:
134
+ 1. Set up shared device memory for multiple encoder processes
135
+ 2. Create and manage multiple encoding processes
136
+ 3. Handle CUDA IPC memory in a multiprocessing context
137
+ 4. Coordinate multiple encoder instances running in parallel
138
+
139
+ Parameters:
140
+ num_processes (int): Number of parallel encoding processes to run
141
+ gpu_id (int): Ordinal of GPU to use
142
+ enc_file_path (str): Path to file to be encoded
143
+ width (int): Width of encoded frame
144
+ height (int): Height of encoded frame
145
+ fmt (str): Surface format string
146
+ framesize (int): Size of each frame in bytes
147
+ config (dict): Encoder configuration parameters
148
+ frame_count (int, optional): Number of frames for each process to encode
149
+ """
150
+ processes = []
151
+
152
+ print(f"Initializing {num_processes} encoder processes")
153
+
154
+ with open(enc_file_path, "rb") as decFile:
155
+ # Cross-platform memory mapping
156
+ if sys.platform == 'win32':
157
+ m = mmap.mmap(decFile.fileno(), 0, access=mmap.ACCESS_READ)
158
+ else:
159
+ m = mmap.mmap(decFile.fileno(), 0, prot=mmap.PROT_READ)
160
+
161
+ hostdata = m.read(m.size())
162
+ devicedata = cuda.mem_alloc(m.size())
163
+ cuda.memcpy_htod(devicedata, hostdata)
164
+ devptrhandle = cuda.mem_get_ipc_handle(devicedata)
165
+
166
+ for i in range(num_processes):
167
+ p = Process(
168
+ target=encode_process,
169
+ args=(
170
+ gpu_id,
171
+ enc_file_path,
172
+ width,
173
+ height,
174
+ fmt,
175
+ devptrhandle,
176
+ framesize,
177
+ config,
178
+ frame_count
179
+ )
180
+ )
181
+ p.start()
182
+ processes.append(p)
183
+
184
+ for p in processes:
185
+ p.join()
186
+
187
+ print("All encoding processes completed")
188
+
189
+
190
+ if __name__ == "__main__":
191
+ parser = argparse.ArgumentParser(
192
+ description="This sample application demonstrates video encoding using multiple processes."
193
+ )
194
+ parser.add_argument(
195
+ "-g", "--gpu_id",
196
+ type=int,
197
+ default=0,
198
+ help="Check nvidia-smi for available GPUs. Default: 0"
199
+ )
200
+ parser.add_argument(
201
+ "-i", "--raw_file_path",
202
+ type=Path,
203
+ required=True,
204
+ help="Raw video file to encode"
205
+ )
206
+ parser.add_argument(
207
+ "-s", "--size",
208
+ type=str,
209
+ required=True,
210
+ help="WidthxHeight of raw frame (e.g., 1920x1080)"
211
+ )
212
+ parser.add_argument(
213
+ "-if", "--format",
214
+ type=str,
215
+ default="NV12",
216
+ help="Format of input file. Default: NV12"
217
+ )
218
+ parser.add_argument(
219
+ "-c", "--codec",
220
+ type=str,
221
+ default="H264",
222
+ help="Video codec (HEVC, H264, AV1). Default: H264"
223
+ )
224
+ parser.add_argument(
225
+ "-json", "--config_file",
226
+ type=str,
227
+ default=join(dirname(__file__), "encode_config_perf.json"),
228
+ help="Path of JSON config file (default: encode_config_perf.json in the same directory)"
229
+ )
230
+ parser.add_argument(
231
+ "-n", "--num_processes",
232
+ type=int,
233
+ default=1,
234
+ help="Number of parallel encoding processes. Default: 1"
235
+ )
236
+ parser.add_argument(
237
+ "-f", "--frame_count",
238
+ type=int,
239
+ help="Number of frames for each process to encode. If not specified, encodes all frames."
240
+ )
241
+
242
+ args = parser.parse_args()
243
+ config = {}
244
+
245
+ if args.config_file:
246
+ with open(args.config_file) as jsonFile:
247
+ config = json.loads(jsonFile.read())
248
+ config["preset"] = config["preset"].upper()
249
+
250
+ args.codec = args.codec.lower()
251
+ args.format = args.format.upper()
252
+ config["codec"] = args.codec
253
+ width, height = map(int, args.size.split("x"))
254
+ framesize = GetFrameSize(width, height, args.format)
255
+
256
+ multiprocessing.set_start_method('spawn')
257
+
258
+ run_parallel_encode(
259
+ args.num_processes,
260
+ args.gpu_id,
261
+ args.raw_file_path.as_posix(),
262
+ width,
263
+ height,
264
+ args.format,
265
+ framesize,
266
+ config,
267
+ args.frame_count
268
+ )