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.
- PyNvVideoCodec/PyNvVideoCodec_121.cp313-win_amd64.pyd +0 -0
- PyNvVideoCodec/PyNvVideoCodec_130.cp313-win_amd64.pyd +0 -0
- PyNvVideoCodec/VersionCheck.cp313-win_amd64.pyd +0 -0
- PyNvVideoCodec/__init__.py +237 -0
- PyNvVideoCodec/avcodec-58.dll +0 -0
- PyNvVideoCodec/avcodec-61.dll +0 -0
- PyNvVideoCodec/avcodec.dll +0 -0
- PyNvVideoCodec/avdevice-58.dll +0 -0
- PyNvVideoCodec/avdevice-61.dll +0 -0
- PyNvVideoCodec/avdevice.dll +0 -0
- PyNvVideoCodec/avfilter-10.dll +0 -0
- PyNvVideoCodec/avfilter-7.dll +0 -0
- PyNvVideoCodec/avfilter.dll +0 -0
- PyNvVideoCodec/avformat-58.dll +0 -0
- PyNvVideoCodec/avformat-61.dll +0 -0
- PyNvVideoCodec/avformat.dll +0 -0
- PyNvVideoCodec/avutil-56.dll +0 -0
- PyNvVideoCodec/avutil-59.dll +0 -0
- PyNvVideoCodec/avutil.dll +0 -0
- PyNvVideoCodec/decoders/SimpleDecoder.py +225 -0
- PyNvVideoCodec/decoders/ThreadedDecoder.py +119 -0
- PyNvVideoCodec/decoders/__init__.py +22 -0
- PyNvVideoCodec/swresample-3.dll +0 -0
- PyNvVideoCodec/swresample-5.dll +0 -0
- PyNvVideoCodec/swresample.dll +0 -0
- PyNvVideoCodec/swscale-5.dll +0 -0
- PyNvVideoCodec/swscale-8.dll +0 -0
- PyNvVideoCodec/swscale.dll +0 -0
- PyNvVideoCodec/transcoder/Transcoder.py +55 -0
- PyNvVideoCodec/transcoder/__init__.py +22 -0
- PyNvVideoCodec-2.0.0.data/data/external/ffmpeg/readme.txt +10 -0
- PyNvVideoCodec-2.0.0.data/data/external/ffmpeg/src/ffmpeg-7.0.2.tar.xz +0 -0
- PyNvVideoCodec-2.0.0.dist-info/METADATA +77 -0
- PyNvVideoCodec-2.0.0.dist-info/RECORD +64 -0
- PyNvVideoCodec-2.0.0.dist-info/WHEEL +5 -0
- PyNvVideoCodec-2.0.0.dist-info/licenses/LICENSE.txt +22 -0
- PyNvVideoCodec-2.0.0.dist-info/licenses/NOTICES.txt +167 -0
- PyNvVideoCodec-2.0.0.dist-info/top_level.txt +3 -0
- benchmarks/cached_decoder_benchmark.py +635 -0
- benchmarks/frame_sampling_benchmark.py +610 -0
- benchmarks/segmented_transcode_benchmark.py +1072 -0
- samples/Decode.py +194 -0
- samples/DecodeMultiprocessing.py +162 -0
- samples/DecodePerf.py +215 -0
- samples/DecodeSEIMsgExtraction.py +338 -0
- samples/DemuxFromByteArray.py +185 -0
- samples/Encode.py +189 -0
- samples/EncodeFromCPUBuffer.py +220 -0
- samples/EncodeMultiprocessing.py +268 -0
- samples/EncodePerf.py +380 -0
- samples/EncodeReconfigure.py +201 -0
- samples/EncodeSEIMsgInsertion.py +189 -0
- samples/ObjectDetection.py +218 -0
- samples/SimpleDecoder/EnsembleApp.py +416 -0
- samples/SimpleDecoder/SubsamplingAndReconfigure.py +341 -0
- samples/SimpleDecoder/fileLists.txt +4 -0
- samples/SimpleDecoder/segments.txt +3 -0
- samples/SimpleDecoder/timeListInSeconds.txt +5 -0
- samples/SimpleDecoder/transcode_config.json +6 -0
- samples/Utils.py +290 -0
- samples/coco_classes.pickle +0 -0
- samples/encode_config.json +16 -0
- samples/encode_config_lowlatency.json +11 -0
- 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
|
+
)
|