PyNvVideoCodec 2.0.0__cp312-cp312-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.cp312-win_amd64.pyd +0 -0
- PyNvVideoCodec/PyNvVideoCodec_130.cp312-win_amd64.pyd +0 -0
- PyNvVideoCodec/VersionCheck.cp312-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
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,237 @@
|
|
|
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
|
+
"""
|
|
25
|
+
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
__author__ = "NVIDIA"
|
|
29
|
+
__copyright__ = "Copyright 2025, NVIDIA"
|
|
30
|
+
__credits__ = []
|
|
31
|
+
__license__ = "MIT"
|
|
32
|
+
__version__ = "2.0.0"
|
|
33
|
+
__maintainer__ = "NVIDIA"
|
|
34
|
+
__email__ = "TODO"
|
|
35
|
+
__status__ = "Production"
|
|
36
|
+
|
|
37
|
+
from ast import Str
|
|
38
|
+
import string
|
|
39
|
+
import os
|
|
40
|
+
import platform
|
|
41
|
+
import sys
|
|
42
|
+
import site
|
|
43
|
+
from pathlib import Path
|
|
44
|
+
import importlib.util
|
|
45
|
+
import logging
|
|
46
|
+
|
|
47
|
+
logger = logging.getLogger(__name__)
|
|
48
|
+
logger.setLevel(logging.INFO)
|
|
49
|
+
|
|
50
|
+
def _setup_environment():
|
|
51
|
+
"""Setup environment variables and paths"""
|
|
52
|
+
package_dir = os.path.dirname(os.path.abspath(__file__))
|
|
53
|
+
machine = platform.machine()
|
|
54
|
+
|
|
55
|
+
if platform.system() == 'Windows':
|
|
56
|
+
try:
|
|
57
|
+
# Add CUDA path if available
|
|
58
|
+
cuda_path = os.environ.get('CUDA_PATH')
|
|
59
|
+
if cuda_path:
|
|
60
|
+
dll_paths = [
|
|
61
|
+
os.path.join(cuda_path, "bin"),
|
|
62
|
+
os.path.join(cuda_path, "lib", "x64"),
|
|
63
|
+
package_dir, # Package directory for ffmpeg DLLs
|
|
64
|
+
os.path.join(package_dir, "lib") # Additional lib directory
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
# Add paths to DLL search path
|
|
68
|
+
if hasattr(os, 'add_dll_directory'):
|
|
69
|
+
for path in dll_paths:
|
|
70
|
+
if os.path.exists(path):
|
|
71
|
+
os.add_dll_directory(path)
|
|
72
|
+
logger.debug(f"Added DLL directory: {path}")
|
|
73
|
+
except Exception as e:
|
|
74
|
+
logger.error(f"Error setting up DLL paths: {e}")
|
|
75
|
+
else:
|
|
76
|
+
# Linux: Update LD_LIBRARY_PATH
|
|
77
|
+
lib_paths = [
|
|
78
|
+
os.path.join(package_dir, "lib"),
|
|
79
|
+
"/usr/local/cuda/lib64",
|
|
80
|
+
]
|
|
81
|
+
|
|
82
|
+
# Add architecture-specific paths
|
|
83
|
+
if machine == 'aarch64':
|
|
84
|
+
lib_paths.extend([
|
|
85
|
+
"/usr/lib/aarch64-linux-gnu",
|
|
86
|
+
"/usr/local/cuda/targets/aarch64-linux/lib"
|
|
87
|
+
])
|
|
88
|
+
else: # x86_64
|
|
89
|
+
lib_paths.append("/usr/lib/x86_64-linux-gnu")
|
|
90
|
+
|
|
91
|
+
if "LD_LIBRARY_PATH" in os.environ:
|
|
92
|
+
lib_paths.extend(os.environ["LD_LIBRARY_PATH"].split(":"))
|
|
93
|
+
os.environ["LD_LIBRARY_PATH"] = ":".join(filter(None, lib_paths))
|
|
94
|
+
|
|
95
|
+
def _get_module_path(module_suffix):
|
|
96
|
+
"""Get the appropriate module file path"""
|
|
97
|
+
package_dir = os.path.dirname(os.path.abspath(__file__))
|
|
98
|
+
py_version = f"{sys.version_info.major}{sys.version_info.minor}"
|
|
99
|
+
machine = platform.machine()
|
|
100
|
+
|
|
101
|
+
if platform.system() == "Windows":
|
|
102
|
+
ext = ".pyd"
|
|
103
|
+
possible_names = [
|
|
104
|
+
f"PyNvVideoCodec{module_suffix}.cp{py_version}-win_amd64{ext}"
|
|
105
|
+
]
|
|
106
|
+
else:
|
|
107
|
+
ext = ".so"
|
|
108
|
+
if machine == 'aarch64':
|
|
109
|
+
possible_names = [
|
|
110
|
+
f"PyNvVideoCodec{module_suffix}.cpython-{py_version}-aarch64-linux-gnu{ext}",
|
|
111
|
+
f"PyNvVideoCodec{module_suffix}.cpython-{py_version}-aarch64-linux{ext}",
|
|
112
|
+
f"PyNvVideoCodec{module_suffix}.cp{py_version}-manylinux_2_28_aarch64{ext}"
|
|
113
|
+
]
|
|
114
|
+
else: # x86_64
|
|
115
|
+
possible_names = [
|
|
116
|
+
f"PyNvVideoCodec{module_suffix}.cpython-{py_version}-x86_64-linux-gnu{ext}",
|
|
117
|
+
f"PyNvVideoCodec{module_suffix}.cpython-{py_version}-x86_64-linux{ext}",
|
|
118
|
+
f"PyNvVideoCodec{module_suffix}.cp{py_version}-manylinux_2_28_x86_64{ext}"
|
|
119
|
+
]
|
|
120
|
+
|
|
121
|
+
# Try each possible name
|
|
122
|
+
for name in possible_names:
|
|
123
|
+
path = os.path.join(package_dir, name)
|
|
124
|
+
if os.path.exists(path):
|
|
125
|
+
logger.debug(f"Found module: {path}")
|
|
126
|
+
return path
|
|
127
|
+
|
|
128
|
+
# If no module found, list directory contents for debugging
|
|
129
|
+
logger.error(f"Available files in {package_dir}:")
|
|
130
|
+
try:
|
|
131
|
+
for file in os.listdir(package_dir):
|
|
132
|
+
logger.error(f" {file}")
|
|
133
|
+
except Exception as e:
|
|
134
|
+
logger.error(f"Error listing directory: {e}")
|
|
135
|
+
|
|
136
|
+
# Return the first name for error message
|
|
137
|
+
return os.path.join(package_dir, possible_names[0])
|
|
138
|
+
|
|
139
|
+
def _get_driver_version():
|
|
140
|
+
"""Get the driver version using VersionCheck"""
|
|
141
|
+
try:
|
|
142
|
+
from .VersionCheck import DriverWrapper
|
|
143
|
+
driver = DriverWrapper()
|
|
144
|
+
version = driver.GetDriverVersion()
|
|
145
|
+
logger.info(f"Supported NVENC Driver Version: {version}")
|
|
146
|
+
return version
|
|
147
|
+
except Exception as e:
|
|
148
|
+
logger.error(f"Error getting driver version: {e}")
|
|
149
|
+
raise
|
|
150
|
+
|
|
151
|
+
try:
|
|
152
|
+
# Setup environment
|
|
153
|
+
_setup_environment()
|
|
154
|
+
|
|
155
|
+
# Get driver version
|
|
156
|
+
supportedNvEncVersion = _get_driver_version()
|
|
157
|
+
|
|
158
|
+
# Define driver version thresholds
|
|
159
|
+
NVENC_VER_13 = 13 * 16 # Driver version for NVENC 13
|
|
160
|
+
NVENC_VER_12 = 12 * 16 + 1 # Driver version for NVENC 12.1
|
|
161
|
+
|
|
162
|
+
# Determine which version to use
|
|
163
|
+
if supportedNvEncVersion >= NVENC_VER_13:
|
|
164
|
+
module_suffix = "_130"
|
|
165
|
+
logger.info("Using NVENC 13.0 version")
|
|
166
|
+
elif supportedNvEncVersion >= NVENC_VER_12:
|
|
167
|
+
module_suffix = "_121"
|
|
168
|
+
logger.info("Using NVENC 12.1 version")
|
|
169
|
+
else:
|
|
170
|
+
raise RuntimeError("Driver version is too old")
|
|
171
|
+
|
|
172
|
+
# Get module path
|
|
173
|
+
module_path = _get_module_path(module_suffix)
|
|
174
|
+
if not os.path.exists(module_path):
|
|
175
|
+
raise ImportError(f"Module not found: {module_path}")
|
|
176
|
+
|
|
177
|
+
# Import the module
|
|
178
|
+
spec = importlib.util.spec_from_file_location("_PyNvVideoCodec", module_path)
|
|
179
|
+
if spec is None:
|
|
180
|
+
raise ImportError(f"Could not create module spec for {module_path}")
|
|
181
|
+
|
|
182
|
+
_PyNvVideoCodec = importlib.util.module_from_spec(spec)
|
|
183
|
+
spec.loader.exec_module(_PyNvVideoCodec)
|
|
184
|
+
|
|
185
|
+
# Import all symbols into current namespace
|
|
186
|
+
globals().update({name: getattr(_PyNvVideoCodec, name) for name in dir(_PyNvVideoCodec)
|
|
187
|
+
if not name.startswith('_')})
|
|
188
|
+
|
|
189
|
+
# Import submodules
|
|
190
|
+
from .decoders.SimpleDecoder import SimpleDecoder
|
|
191
|
+
from .decoders.ThreadedDecoder import ThreadedDecoder
|
|
192
|
+
from .transcoder.Transcoder import Transcoder
|
|
193
|
+
|
|
194
|
+
except Exception as e:
|
|
195
|
+
logger.error(f"Error importing PyNvVideoCodec: {e}")
|
|
196
|
+
logger.error(f"System: {platform.system()}")
|
|
197
|
+
logger.error(f"Python version: {sys.version}")
|
|
198
|
+
if platform.system() == "Linux":
|
|
199
|
+
logger.error(f"LD_LIBRARY_PATH: {os.environ.get('LD_LIBRARY_PATH', 'Not set')}")
|
|
200
|
+
raise
|
|
201
|
+
|
|
202
|
+
def tostring(val):
|
|
203
|
+
if type(val) == dict:
|
|
204
|
+
x = dict(map(lambda x: (x[0], tostring(x[1])), val.items()))
|
|
205
|
+
else:
|
|
206
|
+
x = str(val)
|
|
207
|
+
x = x.replace("]", "")
|
|
208
|
+
x = x.replace("[", "")
|
|
209
|
+
return x
|
|
210
|
+
|
|
211
|
+
def format_optional_params(optional_param):
|
|
212
|
+
param = tostring(optional_param)
|
|
213
|
+
|
|
214
|
+
if "slice" in param:
|
|
215
|
+
param["slice_mode"] = param["slice"]["mode"]
|
|
216
|
+
param["slice_data"] = param["slice"]["data"]
|
|
217
|
+
del param["slice"]
|
|
218
|
+
|
|
219
|
+
if "timinginfo" in param:
|
|
220
|
+
param["num_unit_in_ticks"] = param["timinginfo"]["num_unit_in_ticks"]
|
|
221
|
+
param["timescale"] = param["timinginfo"]["timescale"]
|
|
222
|
+
del param["timinginfo"]
|
|
223
|
+
|
|
224
|
+
return param
|
|
225
|
+
|
|
226
|
+
def CreateEncoder(width, height, fmt, usecpuinputbuffer, **kwargs):
|
|
227
|
+
cudacontext = 0
|
|
228
|
+
cudastream = 0
|
|
229
|
+
if "cudacontext" in kwargs:
|
|
230
|
+
cudacontext = int(kwargs["cudacontext"])
|
|
231
|
+
del kwargs["cudacontext"]
|
|
232
|
+
if "cudastream" in kwargs:
|
|
233
|
+
cudastream = int(kwargs["cudastream"])
|
|
234
|
+
del kwargs["cudastream"]
|
|
235
|
+
|
|
236
|
+
optional_args = format_optional_params(kwargs)
|
|
237
|
+
return PyNvEncoder(width, height, fmt, cudacontext, cudastream, usecpuinputbuffer, optional_args)
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,225 @@
|
|
|
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 typing import Union
|
|
25
|
+
# from ._SimpleDecoderImpl import SimpleDecoderImpl
|
|
26
|
+
import PyNvVideoCodec as nvc
|
|
27
|
+
import warnings
|
|
28
|
+
|
|
29
|
+
class SimpleDecoder:
|
|
30
|
+
"""
|
|
31
|
+
A simple class for decoding a video stream and accessing frames in varied ways.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
enc_file_path (str): Encoded file path.
|
|
35
|
+
gpu_id (int) : gpu id on which to decode on.
|
|
36
|
+
cuda_context(pycuda._driver.Context): Cuda context under which the source is decoded
|
|
37
|
+
cuda_stream(pycuda._driver.Stream): Cuda stream used by the decoder
|
|
38
|
+
use_device_memory (bool): If set to 'True' output decoded frame is CUDeviceptr wrapped in CUDA Array Interface else its Host memory.
|
|
39
|
+
max_width(int): maximum width that the decoder must support
|
|
40
|
+
max_height(int): maximum height that the decoder must support
|
|
41
|
+
need_scanned_stream_metadata(bool): If set to 'True', the stream metadata will be collected by going through each of the packets. This
|
|
42
|
+
runs on a different thread and its running time depends on the total size of input stream.
|
|
43
|
+
decoder_cache_size(int): LRU cache size for the number of decoders to cache.
|
|
44
|
+
output_color_type (OutputColorType): Output format type of the decoded frames. By default it is OutputColorType::NATIVE which
|
|
45
|
+
implies decoded output is returned in native format(NV12, YUV444 etc.). Other supported values are OutputColorType::RGB(which implies
|
|
46
|
+
the decoded output is converted to interleaved RGB(HWC)) and OutputColorType::RGBP(which implies decoded output is converted to planar
|
|
47
|
+
RGB(CHW))
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
def __init__(self, enc_file_path,
|
|
51
|
+
gpu_id = 0, cuda_context = 0,
|
|
52
|
+
cuda_stream = 0, use_device_memory = True,
|
|
53
|
+
max_width = 0,
|
|
54
|
+
max_height = 0,
|
|
55
|
+
need_scanned_stream_metadata = 0,
|
|
56
|
+
decoder_cache_size = 4,
|
|
57
|
+
output_color_type = nvc.OutputColorType.NATIVE,
|
|
58
|
+
bWaitForSessionWarmUp = False):
|
|
59
|
+
self.need_scanned_stream_metadata = need_scanned_stream_metadata
|
|
60
|
+
self.simple_decoder = nvc.CreateSimpleDecoder(enc_file_path, gpu_id,
|
|
61
|
+
cuda_context, cuda_stream,
|
|
62
|
+
use_device_memory, max_width,
|
|
63
|
+
max_height, need_scanned_stream_metadata,
|
|
64
|
+
decoder_cache_size, output_color_type,
|
|
65
|
+
bWaitForSessionWarmUp)
|
|
66
|
+
|
|
67
|
+
total_frames = self.__len__()
|
|
68
|
+
if total_frames == 0:
|
|
69
|
+
raise Exception("Elementary streams not supported by Simple Decoder. Invalid input stream.")
|
|
70
|
+
|
|
71
|
+
def __getitem__(self, key: Union[int, slice]):
|
|
72
|
+
"""
|
|
73
|
+
Return frame or batch of frames depending on the key
|
|
74
|
+
Args:
|
|
75
|
+
key (int or slice): The index or range of index to get the frames
|
|
76
|
+
Returns:
|
|
77
|
+
DecodedFrame: Decoded frame(s) based on the key
|
|
78
|
+
"""
|
|
79
|
+
if isinstance(key, int):
|
|
80
|
+
self.validate_index(key)
|
|
81
|
+
return self.simple_decoder[key]
|
|
82
|
+
elif isinstance(key, slice):
|
|
83
|
+
indices = range(key.start, key.stop, key.step)
|
|
84
|
+
idxs = list(iter(indices))
|
|
85
|
+
if not idxs:
|
|
86
|
+
raise ValueError(f"Invalid input.")
|
|
87
|
+
self.validate_index(key.start)
|
|
88
|
+
self.validate_index(key.stop)
|
|
89
|
+
return self.simple_decoder[idxs]
|
|
90
|
+
else:
|
|
91
|
+
raise TypeError(
|
|
92
|
+
f"Unsupported key type: {type(key)}. Supported types are int and slice."
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def __len__(self):
|
|
97
|
+
"""
|
|
98
|
+
Returns the total number of frames in the video
|
|
99
|
+
"""
|
|
100
|
+
stream_meta = self.simple_decoder.get_stream_metadata()
|
|
101
|
+
if stream_meta.num_frames == 0 and self.need_scanned_stream_metadata == True:
|
|
102
|
+
scanned_stream_data = self.simple_decoder.get_scanned_stream_metadata()
|
|
103
|
+
return scanned_stream_data.num_frames
|
|
104
|
+
return stream_meta.num_frames
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def get_batch_frames(self, batch_size):
|
|
108
|
+
"""
|
|
109
|
+
Returns a batch of frames based on the batch size
|
|
110
|
+
Args:
|
|
111
|
+
batch_size(int): Number of frames in the batch
|
|
112
|
+
Returns:
|
|
113
|
+
DecodedFrame[] : A list of decoded frames
|
|
114
|
+
"""
|
|
115
|
+
return self.simple_decoder.get_batch_frames(batch_size)
|
|
116
|
+
|
|
117
|
+
def stop(self):
|
|
118
|
+
return self.simple_decoder.stop()
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def get_batch_frames_by_index(self, indices):
|
|
122
|
+
"""
|
|
123
|
+
Returns a batch of frames based on the indices
|
|
124
|
+
Args:
|
|
125
|
+
indices(list): A list containing the frame indices to be retrieved
|
|
126
|
+
Returns:
|
|
127
|
+
DecodedFrame[] : A list of decoded frames
|
|
128
|
+
"""
|
|
129
|
+
total_frames = self.__len__()
|
|
130
|
+
validated_indices = []
|
|
131
|
+
seen = set()
|
|
132
|
+
for index in indices:
|
|
133
|
+
if 0 <= index < total_frames:
|
|
134
|
+
if index not in seen:
|
|
135
|
+
seen.add(index)
|
|
136
|
+
validated_indices.append(index)
|
|
137
|
+
else:
|
|
138
|
+
warnings.warn(
|
|
139
|
+
f"Skipping index {index}: Out of valid range [0, {total_frames-1}]")
|
|
140
|
+
|
|
141
|
+
if len(indices) > len(validated_indices):
|
|
142
|
+
warnings.warn(
|
|
143
|
+
f"Duplicates are not supported and have been removed. Modified index list: {validated_indices}"
|
|
144
|
+
)
|
|
145
|
+
sorted_validated_indices = sorted(validated_indices)
|
|
146
|
+
sorted_frames = self.simple_decoder.get_batch_frames_by_index(sorted_validated_indices)
|
|
147
|
+
mapping = {val: sorted_frames[idx] for idx, val in enumerate(sorted_validated_indices)}
|
|
148
|
+
reordered_frames = [mapping[val] for val in validated_indices]
|
|
149
|
+
return reordered_frames
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def get_stream_metadata(self):
|
|
153
|
+
"""
|
|
154
|
+
Returns stream metadata
|
|
155
|
+
Returns:
|
|
156
|
+
StreamMetadata : StreamMetadata structure with metadata info
|
|
157
|
+
"""
|
|
158
|
+
stream_meta = self.simple_decoder.get_stream_metadata()
|
|
159
|
+
return stream_meta
|
|
160
|
+
|
|
161
|
+
def get_scanned_stream_metadata(self):
|
|
162
|
+
"""
|
|
163
|
+
Returns stream metadata by performing entitre stream scan.
|
|
164
|
+
Returns:
|
|
165
|
+
ScannedStreamMetadata : ScannedStreamMetadata structure with
|
|
166
|
+
metadata info
|
|
167
|
+
"""
|
|
168
|
+
if self.need_scanned_stream_metadata == 0:
|
|
169
|
+
raise Exception("Invalid call to funtion. Decoder was created with \
|
|
170
|
+
need_scanned_stream_metadata = 0.")
|
|
171
|
+
stream_meta = self.simple_decoder.get_scanned_stream_metadata()
|
|
172
|
+
return stream_meta
|
|
173
|
+
|
|
174
|
+
def seek_to_index(self, index):
|
|
175
|
+
"""
|
|
176
|
+
Moves the demuxer marker to the specified index
|
|
177
|
+
Args:
|
|
178
|
+
index(int): The index to which the marker needs to be set
|
|
179
|
+
"""
|
|
180
|
+
self.validate_index(index)
|
|
181
|
+
return self.simple_decoder.seek_to_index(index)
|
|
182
|
+
|
|
183
|
+
def get_index_from_time_in_seconds(self, time_in_seconds):
|
|
184
|
+
"""
|
|
185
|
+
Gives the frame index corresponding to the time in seconds
|
|
186
|
+
Args:
|
|
187
|
+
timestamp(float): Frame timestamp
|
|
188
|
+
"""
|
|
189
|
+
return self.simple_decoder.get_index_from_time_in_seconds(time_in_seconds)
|
|
190
|
+
|
|
191
|
+
def validate_index(self, index):
|
|
192
|
+
total_frames = self.__len__()
|
|
193
|
+
if not (0 <= index < total_frames):
|
|
194
|
+
raise IndexError(f"Index {index} is out of range [0, {total_frames-1}]")
|
|
195
|
+
|
|
196
|
+
def reconfigure_decoder(self, new_source):
|
|
197
|
+
"""
|
|
198
|
+
Reconfigures the current decoder to use the new source. Internally it
|
|
199
|
+
holds a cache of decoders. The best available is choosen else a new decoder is
|
|
200
|
+
created.
|
|
201
|
+
Args:
|
|
202
|
+
new_source(str): Encoded source
|
|
203
|
+
"""
|
|
204
|
+
return self.simple_decoder.reconfigure_decoder(new_source)
|
|
205
|
+
|
|
206
|
+
def get_session_init_time(self):
|
|
207
|
+
"""
|
|
208
|
+
Returns the session initialization time in milliseconds.
|
|
209
|
+
This is useful for performance measurement.
|
|
210
|
+
|
|
211
|
+
Returns:
|
|
212
|
+
int64_t: Session initialization time in milliseconds
|
|
213
|
+
"""
|
|
214
|
+
return self.simple_decoder.get_session_init_time()
|
|
215
|
+
|
|
216
|
+
@staticmethod
|
|
217
|
+
def set_session_count(count):
|
|
218
|
+
"""
|
|
219
|
+
Sets the number of decoder sessions to be created.
|
|
220
|
+
This is useful for multi-threaded scenarios.
|
|
221
|
+
|
|
222
|
+
Args:
|
|
223
|
+
count (int): Number of decoder sessions
|
|
224
|
+
"""
|
|
225
|
+
nvc.SimpleDecoder.set_session_count(count)
|
|
@@ -0,0 +1,119 @@
|
|
|
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 typing import Union
|
|
25
|
+
#from ._SimpleDecoderImpl import SimpleDecoderImpl
|
|
26
|
+
import PyNvVideoCodec as nvc
|
|
27
|
+
|
|
28
|
+
class ThreadedDecoder():
|
|
29
|
+
"""
|
|
30
|
+
Class for decoding video in background thread. This can be used for linear access in pipeines which are
|
|
31
|
+
CPU bound. In such a pipeline this decoder offes near zero frame fetch latency.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
enc_file_path (str): Encoded file path.
|
|
35
|
+
gpu_id (int) : gpu id on which to decode on.
|
|
36
|
+
cuda_context(pycuda._driver.Context): Cuda context under which the source is decoded
|
|
37
|
+
cuda_stream(pycuda._driver.Stream): Cuda stream used by the decoder
|
|
38
|
+
use_device_memory (bool): If set to 'True' output decoded frame is CUDeviceptr wrapped in CUDA Array Interface else its Host memory.
|
|
39
|
+
max_width(int): maximum width that the decoder must support
|
|
40
|
+
max_height(int): maximum height that the decoder must support
|
|
41
|
+
need_scanned_stream_metadata(bool): If set to 'True', the stream metadata will be collected by going through each of the packets. This
|
|
42
|
+
runs on a different thread and its running time depends on the total size of input stream.
|
|
43
|
+
decoder_cache_size(int): LRU cache size for the number of decoders to cache.
|
|
44
|
+
output_color_type (OutputColorType): Output format type of the decoded frames. By default it is OutputColorType::NATIVE which
|
|
45
|
+
implies decoded output is returned in native format(NV12, YUV444 etc.). Other supported values are OutputColorType::RGB(which implies
|
|
46
|
+
the decoded output is converted to interleaved RGB(HWC)) and OutputColorType::RGBP(which implies decoded output is converted to planar
|
|
47
|
+
RGB(CHW))
|
|
48
|
+
"""
|
|
49
|
+
def __init__(self, enc_file_path, buffer_size,
|
|
50
|
+
gpu_id = 0, cuda_context = 0,
|
|
51
|
+
cuda_stream = 0, use_device_memory = True,
|
|
52
|
+
max_width = 0,
|
|
53
|
+
max_height = 0,
|
|
54
|
+
need_scanned_stream_metadata = 0,
|
|
55
|
+
decoder_cache_size = 4,
|
|
56
|
+
output_color_type = nvc.OutputColorType.NATIVE):
|
|
57
|
+
self.buffer_size = buffer_size
|
|
58
|
+
self.need_scanned_stream_metadata = need_scanned_stream_metadata
|
|
59
|
+
self.threaded_decoder = nvc.CreateThreadedDecoder(enc_file_path, buffer_size, gpu_id,
|
|
60
|
+
cuda_context, cuda_stream,
|
|
61
|
+
use_device_memory, max_width,
|
|
62
|
+
max_height, need_scanned_stream_metadata,
|
|
63
|
+
decoder_cache_size, output_color_type)
|
|
64
|
+
|
|
65
|
+
def __len__(self):
|
|
66
|
+
stream_meta = self.threaded_decoder.get_stream_metadata()
|
|
67
|
+
if stream_meta.num_frames == 0 and self.need_scanned_stream_metadata == True:
|
|
68
|
+
scanned_stream_data = self.threaded_decoder.get_scanned_stream_metadata()
|
|
69
|
+
return scanned_stream_data.num_frames
|
|
70
|
+
return stream_meta.num_frames
|
|
71
|
+
|
|
72
|
+
def get_batch_frames(self, batch_size):
|
|
73
|
+
"""
|
|
74
|
+
Returns a batch of frames based on the batch size
|
|
75
|
+
Args:
|
|
76
|
+
batch_size(int): Number of frames in the batch
|
|
77
|
+
Returns:
|
|
78
|
+
DecodedFrame[] : A list of decoded frames
|
|
79
|
+
"""
|
|
80
|
+
if batch_size > self.buffer_size:
|
|
81
|
+
raise Exception(f"batch_size cannot be greater than buffer_size. \
|
|
82
|
+
Got batch_size = {batch_size} whereas buffer_size = {self.buffer_size}")
|
|
83
|
+
return self.threaded_decoder.get_batch_frames(batch_size)
|
|
84
|
+
|
|
85
|
+
def end(self):
|
|
86
|
+
return self.threaded_decoder.end()
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def get_stream_metadata(self):
|
|
90
|
+
"""
|
|
91
|
+
Returns stream metadata
|
|
92
|
+
Returns:
|
|
93
|
+
StreamMetadata : StreamMetadata structure with metadata info
|
|
94
|
+
"""
|
|
95
|
+
stream_meta = self.threaded_decoder.get_stream_metadata()
|
|
96
|
+
return stream_meta
|
|
97
|
+
|
|
98
|
+
def get_scanned_stream_metadata(self):
|
|
99
|
+
"""
|
|
100
|
+
Returns stream metadata by performing entitre stream scan.
|
|
101
|
+
Returns:
|
|
102
|
+
ScannedStreamMetadata : ScannedStreamMetadata structure with
|
|
103
|
+
metadata info
|
|
104
|
+
"""
|
|
105
|
+
if self.need_scanned_stream_metadata == 0:
|
|
106
|
+
raise Exception("Invalid call to funtion. Decoder was created with \
|
|
107
|
+
need_scanned_stream_metadata = 0.")
|
|
108
|
+
stream_meta = self.threaded_decoder.get_scanned_stream_metadata()
|
|
109
|
+
return stream_meta
|
|
110
|
+
|
|
111
|
+
def reconfigure_decoder(self, new_source):
|
|
112
|
+
"""
|
|
113
|
+
Reconfigures the current decoder to use the new source. Internally it
|
|
114
|
+
holds a cache of decoders. The best available is choosen else a new decoder is
|
|
115
|
+
created.
|
|
116
|
+
Args:
|
|
117
|
+
new_source(str): Encoded source
|
|
118
|
+
"""
|
|
119
|
+
return self.threaded_decoder.reconfigure_decoder(new_source)
|
|
@@ -0,0 +1,22 @@
|
|
|
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.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|