nvidia-codec 0.0.22__tar.gz → 0.0.23__tar.gz
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.
- {nvidia_codec-0.0.22/src/nvidia_codec.egg-info → nvidia_codec-0.0.23}/PKG-INFO +19 -2
- {nvidia_codec-0.0.22 → nvidia_codec-0.0.23}/README.md +18 -1
- {nvidia_codec-0.0.22 → nvidia_codec-0.0.23}/setup.cfg +1 -1
- {nvidia_codec-0.0.22 → nvidia_codec-0.0.23}/src/nvidia_codec/ffmpeg/include/libavformat.py +1 -0
- {nvidia_codec-0.0.22 → nvidia_codec-0.0.23}/src/nvidia_codec/ffmpeg/include/libavutil.py +7 -5
- {nvidia_codec-0.0.22 → nvidia_codec-0.0.23}/src/nvidia_codec/ffmpeg/libavformat.py +0 -10
- {nvidia_codec-0.0.22 → nvidia_codec-0.0.23}/src/nvidia_codec/utils/decode.py +42 -2
- {nvidia_codec-0.0.22 → nvidia_codec-0.0.23}/src/nvidia_codec/utils/demux.py +23 -8
- {nvidia_codec-0.0.22 → nvidia_codec-0.0.23}/src/nvidia_codec/utils/player.py +4 -1
- {nvidia_codec-0.0.22 → nvidia_codec-0.0.23/src/nvidia_codec.egg-info}/PKG-INFO +19 -2
- {nvidia_codec-0.0.22 → nvidia_codec-0.0.23}/tests/test_smoke.py +57 -0
- {nvidia_codec-0.0.22 → nvidia_codec-0.0.23}/LICENSE +0 -0
- {nvidia_codec-0.0.22 → nvidia_codec-0.0.23}/pyproject.toml +0 -0
- {nvidia_codec-0.0.22 → nvidia_codec-0.0.23}/src/nvidia_codec/__init__.py +0 -0
- {nvidia_codec-0.0.22 → nvidia_codec-0.0.23}/src/nvidia_codec/common.py +0 -0
- {nvidia_codec-0.0.22 → nvidia_codec-0.0.23}/src/nvidia_codec/core/__init__.py +0 -0
- {nvidia_codec-0.0.22 → nvidia_codec-0.0.23}/src/nvidia_codec/core/common.py +0 -0
- {nvidia_codec-0.0.22 → nvidia_codec-0.0.23}/src/nvidia_codec/core/cuda.py +0 -0
- {nvidia_codec-0.0.22 → nvidia_codec-0.0.23}/src/nvidia_codec/core/cuviddec.py +0 -0
- {nvidia_codec-0.0.22 → nvidia_codec-0.0.23}/src/nvidia_codec/core/decode.py +0 -0
- {nvidia_codec-0.0.22 → nvidia_codec-0.0.23}/src/nvidia_codec/core/nvcuvid.py +0 -0
- {nvidia_codec-0.0.22 → nvidia_codec-0.0.23}/src/nvidia_codec/ffmpeg/__init__.py +0 -0
- {nvidia_codec-0.0.22 → nvidia_codec-0.0.23}/src/nvidia_codec/ffmpeg/adapt.py +0 -0
- {nvidia_codec-0.0.22 → nvidia_codec-0.0.23}/src/nvidia_codec/ffmpeg/common.py +0 -0
- {nvidia_codec-0.0.22 → nvidia_codec-0.0.23}/src/nvidia_codec/ffmpeg/include/__init__.py +0 -0
- {nvidia_codec-0.0.22 → nvidia_codec-0.0.23}/src/nvidia_codec/ffmpeg/include/libavcodec.py +0 -0
- {nvidia_codec-0.0.22 → nvidia_codec-0.0.23}/src/nvidia_codec/ffmpeg/libavcodec.py +0 -0
- {nvidia_codec-0.0.22 → nvidia_codec-0.0.23}/src/nvidia_codec/ffmpeg/libavutil.py +0 -0
- {nvidia_codec-0.0.22 → nvidia_codec-0.0.23}/src/nvidia_codec/ffmpeg/test.py +0 -0
- {nvidia_codec-0.0.22 → nvidia_codec-0.0.23}/src/nvidia_codec/utils/__init__.py +0 -0
- {nvidia_codec-0.0.22 → nvidia_codec-0.0.23}/src/nvidia_codec/utils/color.py +0 -0
- {nvidia_codec-0.0.22 → nvidia_codec-0.0.23}/src/nvidia_codec/utils/compat.py +0 -0
- {nvidia_codec-0.0.22 → nvidia_codec-0.0.23}/src/nvidia_codec.egg-info/SOURCES.txt +0 -0
- {nvidia_codec-0.0.22 → nvidia_codec-0.0.23}/src/nvidia_codec.egg-info/dependency_links.txt +0 -0
- {nvidia_codec-0.0.22 → nvidia_codec-0.0.23}/src/nvidia_codec.egg-info/requires.txt +0 -0
- {nvidia_codec-0.0.22 → nvidia_codec-0.0.23}/src/nvidia_codec.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nvidia-codec
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.23
|
|
4
4
|
Summary: Pythonic interface to Nvidia Codec
|
|
5
5
|
Home-page: https://github.com/riaqn/python-nvidia-codec
|
|
6
6
|
Author: Zesen Qian
|
|
@@ -124,6 +124,23 @@ Codec support depends on your GPU:
|
|
|
124
124
|
- VPF allocates the bare minimum of resources needed for most decoding tasks. PNC allows the user to specify the amount of resources to be allocated for advanced applications. Users own the resources and decide when and whether to deal with them.
|
|
125
125
|
- Managing resources is not painful: similar to `pycuda`, we shift the burden of managing host/device resources to the Python garbage collector. Resources (such as `Picture` and `Surface`) are automatically freed when the user drops the reference.
|
|
126
126
|
|
|
127
|
+
## Limitations
|
|
128
|
+
|
|
129
|
+
- **One consumer per `Decoder`.** A `Decoder`/`Player` instance serves one
|
|
130
|
+
`frames()`, `screenshots()` or `screenshot()` call at a time. Use separate
|
|
131
|
+
instances (from any threads) for concurrent decodes of the same or different
|
|
132
|
+
files.
|
|
133
|
+
- **No mid-stream resolution or codec changes.** The NVDEC decoder is created
|
|
134
|
+
from the first sequence header; a later sequence with a different coded
|
|
135
|
+
size, chroma format or bit depth raises `decoder already initialized with
|
|
136
|
+
different format`. Split such files per segment.
|
|
137
|
+
- **Timestamp-less elementary streams** (e.g. raw Annex-B `.h264`/`.hevc`)
|
|
138
|
+
carry no pts: `frames()` yields `AV_NOPTS_VALUE` for every frame,
|
|
139
|
+
`pts2time` returns `None`, and `screenshot()` cannot locate a time. Mux
|
|
140
|
+
into a container first.
|
|
141
|
+
- **Embedded cover art** (`attached_pic` streams) is skipped by `Player`
|
|
142
|
+
and reported by `VideoTrack.attached_pic` from `parse()`.
|
|
143
|
+
|
|
127
144
|
## Roadmap
|
|
128
145
|
|
|
129
146
|
- [x] Decoding
|
|
@@ -133,7 +150,7 @@ Codec support depends on your GPU:
|
|
|
133
150
|
- Color Ranges: MPEG (limited), JPEG (full)
|
|
134
151
|
- Color Space: BT.601, BT.709, BT.2020
|
|
135
152
|
- [x] Built-in cropping and scaling via NV decoder
|
|
136
|
-
- [x] Thread-
|
|
153
|
+
- [x] Thread-friendly (one consumer per decoder; decoders are independent)
|
|
137
154
|
- [ ] Encoder
|
|
138
155
|
- [x] PyTorch integration
|
|
139
156
|
|
|
@@ -107,6 +107,23 @@ Codec support depends on your GPU:
|
|
|
107
107
|
- VPF allocates the bare minimum of resources needed for most decoding tasks. PNC allows the user to specify the amount of resources to be allocated for advanced applications. Users own the resources and decide when and whether to deal with them.
|
|
108
108
|
- Managing resources is not painful: similar to `pycuda`, we shift the burden of managing host/device resources to the Python garbage collector. Resources (such as `Picture` and `Surface`) are automatically freed when the user drops the reference.
|
|
109
109
|
|
|
110
|
+
## Limitations
|
|
111
|
+
|
|
112
|
+
- **One consumer per `Decoder`.** A `Decoder`/`Player` instance serves one
|
|
113
|
+
`frames()`, `screenshots()` or `screenshot()` call at a time. Use separate
|
|
114
|
+
instances (from any threads) for concurrent decodes of the same or different
|
|
115
|
+
files.
|
|
116
|
+
- **No mid-stream resolution or codec changes.** The NVDEC decoder is created
|
|
117
|
+
from the first sequence header; a later sequence with a different coded
|
|
118
|
+
size, chroma format or bit depth raises `decoder already initialized with
|
|
119
|
+
different format`. Split such files per segment.
|
|
120
|
+
- **Timestamp-less elementary streams** (e.g. raw Annex-B `.h264`/`.hevc`)
|
|
121
|
+
carry no pts: `frames()` yields `AV_NOPTS_VALUE` for every frame,
|
|
122
|
+
`pts2time` returns `None`, and `screenshot()` cannot locate a time. Mux
|
|
123
|
+
into a container first.
|
|
124
|
+
- **Embedded cover art** (`attached_pic` streams) is skipped by `Player`
|
|
125
|
+
and reported by `VideoTrack.attached_pic` from `parse()`.
|
|
126
|
+
|
|
110
127
|
## Roadmap
|
|
111
128
|
|
|
112
129
|
- [x] Decoding
|
|
@@ -116,7 +133,7 @@ Codec support depends on your GPU:
|
|
|
116
133
|
- Color Ranges: MPEG (limited), JPEG (full)
|
|
117
134
|
- Color Space: BT.601, BT.709, BT.2020
|
|
118
135
|
- [x] Built-in cropping and scaling via NV decoder
|
|
119
|
-
- [x] Thread-
|
|
136
|
+
- [x] Thread-friendly (one consumer per decoder; decoders are independent)
|
|
120
137
|
- [ ] Encoder
|
|
121
138
|
- [x] PyTorch integration
|
|
122
139
|
|
|
@@ -63,10 +63,12 @@ class AVColorRange(TypedCEnumeration(c_int)):
|
|
|
63
63
|
# NB = 3
|
|
64
64
|
|
|
65
65
|
class AVPixelFormat(TypedCEnumeration(c_int)):
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
66
|
+
# values per libavutil/pixfmt.h (ffmpeg 9); no trailing commas — they
|
|
67
|
+
# would make the members 1-tuples, not ints
|
|
68
|
+
NONE = -1
|
|
69
|
+
YUV420P = 0
|
|
70
|
+
RGB24 = 2
|
|
71
|
+
YUV444P = 5
|
|
72
|
+
YUV420P16LE = 45
|
|
71
73
|
YUV444P16LE = 49
|
|
72
74
|
# other omitted
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
# not pyav
|
|
4
4
|
|
|
5
5
|
from ctypes import *
|
|
6
|
-
from fractions import Fraction
|
|
7
6
|
|
|
8
7
|
from .include.libavutil import *
|
|
9
8
|
from .include.libavcodec import *
|
|
@@ -104,15 +103,6 @@ class FormatContext:
|
|
|
104
103
|
return result.contents
|
|
105
104
|
return None
|
|
106
105
|
|
|
107
|
-
def infer_start_time(self):
|
|
108
|
-
# log.warning('infering start time from first packet')
|
|
109
|
-
# in this case, we get the first packet
|
|
110
|
-
pkt = Packet()
|
|
111
|
-
self.read_packet(pkt)
|
|
112
|
-
start_time = pkt.av.pts
|
|
113
|
-
time_base = pkt.av.time_base
|
|
114
|
-
return start_time * Fraction(time_base.num, time_base.den)
|
|
115
|
-
|
|
116
106
|
@property
|
|
117
107
|
def streams(self):
|
|
118
108
|
for i in range(self.av.nb_streams):
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import collections
|
|
4
4
|
from datetime import timedelta
|
|
5
|
+
from fractions import Fraction
|
|
5
6
|
import numpy as np
|
|
6
7
|
from queue import Queue, ShutDown
|
|
7
8
|
import threading
|
|
@@ -9,6 +10,7 @@ import weakref
|
|
|
9
10
|
|
|
10
11
|
from ..ffmpeg.libavcodec import BSFContext
|
|
11
12
|
from ..ffmpeg.libavformat import FormatContext, AVCodecID
|
|
13
|
+
from ..ffmpeg.include.libavcodec import AV_PKT_FLAG_KEY
|
|
12
14
|
from ..ffmpeg.include.libavutil import (
|
|
13
15
|
AV_NOPTS_VALUE,
|
|
14
16
|
AVColorRange,
|
|
@@ -180,6 +182,11 @@ class Decoder(BaseDecoder):
|
|
|
180
182
|
coded_height=track.height,
|
|
181
183
|
)
|
|
182
184
|
|
|
185
|
+
@property
|
|
186
|
+
def duration(self):
|
|
187
|
+
"""Track duration as a timedelta, or None if the container doesn't say."""
|
|
188
|
+
return self.track.duration
|
|
189
|
+
|
|
183
190
|
def pre_decode(self, idx):
|
|
184
191
|
"""NVDEC reclaims slot idx. Clear tracking, wait for consumer if held."""
|
|
185
192
|
self._slots.pop(idx, None)
|
|
@@ -247,8 +254,7 @@ class Decoder(BaseDecoder):
|
|
|
247
254
|
packet/frame traversal. Only if avformat cannot seek the container at all do we
|
|
248
255
|
fall back to the in-memory-index path (then BOF)."""
|
|
249
256
|
try:
|
|
250
|
-
self.
|
|
251
|
-
min_ts=-(2**63), max_ts=target_pts)
|
|
257
|
+
self._native_seek_to_keyframe(target_pts)
|
|
252
258
|
except AVException:
|
|
253
259
|
kts = self._keyframe_before(target_pts)
|
|
254
260
|
if kts is None:
|
|
@@ -258,6 +264,40 @@ class Decoder(BaseDecoder):
|
|
|
258
264
|
self.with_callbacks(lambda: self.send(None), post_decode=lambda pic: None)
|
|
259
265
|
self._recent.clear()
|
|
260
266
|
|
|
267
|
+
def _native_seek_to_keyframe(self, target_pts, max_tries=16):
|
|
268
|
+
"""avformat_seek_file(max_ts=target) positions on the last packet with
|
|
269
|
+
dts <= target. Indexed containers (MP4 / MKV) snap that to a keyframe.
|
|
270
|
+
Index-less ones (MPEG-TS / M2TS) use the generic dts bisection — which
|
|
271
|
+
is NOT keyframe-aware ("FIXME keyframe?" in mpegts.c) — and land
|
|
272
|
+
mid-GOP; NVDEC then discards everything up to the NEXT IDR, so the
|
|
273
|
+
frame we hand back is up to a GOP late. (ffmpeg's CLI papers over this
|
|
274
|
+
with a fixed 3/23 s dts heuristic.)
|
|
275
|
+
|
|
276
|
+
Peek at the landing packet; if it isn't a keyframe, back off by a
|
|
277
|
+
growing number of frame-durations and retry. Landing on an earlier
|
|
278
|
+
keyframe than strictly needed only costs decode-forward time, so
|
|
279
|
+
overshooting backwards is safe. The peek consumes a packet, so the
|
|
280
|
+
final seek re-runs to restore the position. Raises AVException if the
|
|
281
|
+
container can't seek at all (caller falls back to the in-memory index).
|
|
282
|
+
"""
|
|
283
|
+
fc, stream = self.track.fc, self.track.stream
|
|
284
|
+
tb = self.track._time_base
|
|
285
|
+
afr = stream.avg_frame_rate
|
|
286
|
+
fps = Fraction(afr.num, afr.den) if afr.num > 0 and afr.den > 0 else Fraction(25)
|
|
287
|
+
step = max(1, int(1 / (fps * tb))) # one frame, in pts units
|
|
288
|
+
ts = target_pts
|
|
289
|
+
for _ in range(max_tries):
|
|
290
|
+
fc.seek_file(stream, ts, min_ts=-(2**63), max_ts=ts)
|
|
291
|
+
first = next(fc.read_packets(stream), None)
|
|
292
|
+
if first is None or first.av.flags & AV_PKT_FLAG_KEY:
|
|
293
|
+
break # keyframe (or EOF — nothing to fix)
|
|
294
|
+
landed = first.av.dts if first.av.dts != AV_NOPTS_VALUE else first.av.pts
|
|
295
|
+
if landed == AV_NOPTS_VALUE:
|
|
296
|
+
break # can't reason about position
|
|
297
|
+
ts = min(ts, landed) - step
|
|
298
|
+
step *= 2
|
|
299
|
+
fc.seek_file(stream, ts, min_ts=-(2**63), max_ts=ts)
|
|
300
|
+
|
|
261
301
|
def _map_and_convert(self, pic, dtype, pixel_format='rgb'):
|
|
262
302
|
"""Map a Picture (or OwnedPicture) to a tensor. Frees pic and surface.
|
|
263
303
|
Returns (timedelta, frame) on success, (timedelta, exception) on error.
|
|
@@ -10,6 +10,7 @@ from fractions import Fraction
|
|
|
10
10
|
import functools
|
|
11
11
|
|
|
12
12
|
from ..ffmpeg.libavformat import FormatContext, AVMediaType, AVCodecID
|
|
13
|
+
from ..ffmpeg.include.libavformat import AV_DISPOSITION_ATTACHED_PIC
|
|
13
14
|
from ..ffmpeg.include.libavutil import (
|
|
14
15
|
AV_NOPTS_VALUE,
|
|
15
16
|
AV_TIME_BASE,
|
|
@@ -36,6 +37,7 @@ class VideoTrack:
|
|
|
36
37
|
extradata: Codec extradata bytes, or None.
|
|
37
38
|
color_space: AVColorSpace enum value.
|
|
38
39
|
color_range: AVColorRange enum value.
|
|
40
|
+
attached_pic: True if this is embedded cover art, not real video.
|
|
39
41
|
"""
|
|
40
42
|
|
|
41
43
|
def __init__(self, fc, stream):
|
|
@@ -51,20 +53,33 @@ class VideoTrack:
|
|
|
51
53
|
# Time base
|
|
52
54
|
self._time_base = Fraction(stream.time_base.num, stream.time_base.den)
|
|
53
55
|
|
|
54
|
-
# Start time
|
|
56
|
+
# Start time. Lazy containers (MPEG-TS, raw elementary streams) leave
|
|
57
|
+
# both the stream and the format start_time unset until probed;
|
|
58
|
+
# probing buffers the packets it reads, so nothing is lost. (The old
|
|
59
|
+
# path read the first packet directly — which both computed 0 via the
|
|
60
|
+
# packet's unset 0/1 time_base AND swallowed the IDR, dropping the
|
|
61
|
+
# whole first GOP from decode.)
|
|
55
62
|
self._start_time = stream.start_time
|
|
56
63
|
if self._start_time == AV_NOPTS_VALUE:
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
else:
|
|
64
|
+
fc.probe()
|
|
65
|
+
self._start_time = stream.start_time
|
|
66
|
+
if self._start_time == AV_NOPTS_VALUE:
|
|
67
|
+
if fc.av.start_time != AV_NOPTS_VALUE:
|
|
62
68
|
self._start_time = int(
|
|
63
|
-
|
|
69
|
+
fc.av.start_time / AV_TIME_BASE / self._time_base
|
|
64
70
|
)
|
|
71
|
+
else:
|
|
72
|
+
# Timestamp-less stream (e.g. raw Annex-B .h264): packets carry
|
|
73
|
+
# no pts at all; frames() yields AV_NOPTS_VALUE and pts2time
|
|
74
|
+
# returns None. Anchor at 0 so time2pts is at least defined.
|
|
75
|
+
self._start_time = 0
|
|
65
76
|
|
|
66
|
-
# Duration (lazy — probes only if needed)
|
|
67
77
|
|
|
78
|
+
@property
|
|
79
|
+
def attached_pic(self):
|
|
80
|
+
"""True for cover-art / thumbnail "video" streams (AV_DISPOSITION_ATTACHED_PIC):
|
|
81
|
+
a single still image, not a decodable video track."""
|
|
82
|
+
return bool(self.stream.disposition & AV_DISPOSITION_ATTACHED_PIC)
|
|
68
83
|
|
|
69
84
|
@functools.cached_property
|
|
70
85
|
def width(self):
|
|
@@ -22,7 +22,10 @@ class Player(Decoder):
|
|
|
22
22
|
track_idx=None,
|
|
23
23
|
**decoder_kwargs,
|
|
24
24
|
):
|
|
25
|
-
|
|
25
|
+
# Cover art (attached_pic) is a "video" stream to ffmpeg but a single
|
|
26
|
+
# still image; never pick it, and don't count it as ambiguity.
|
|
27
|
+
tracks = [t for t in parse(url)
|
|
28
|
+
if isinstance(t, VideoTrack) and not t.attached_pic]
|
|
26
29
|
if not tracks:
|
|
27
30
|
raise ValueError(f"{url} has no video stream")
|
|
28
31
|
if track_idx is not None:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nvidia-codec
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.23
|
|
4
4
|
Summary: Pythonic interface to Nvidia Codec
|
|
5
5
|
Home-page: https://github.com/riaqn/python-nvidia-codec
|
|
6
6
|
Author: Zesen Qian
|
|
@@ -124,6 +124,23 @@ Codec support depends on your GPU:
|
|
|
124
124
|
- VPF allocates the bare minimum of resources needed for most decoding tasks. PNC allows the user to specify the amount of resources to be allocated for advanced applications. Users own the resources and decide when and whether to deal with them.
|
|
125
125
|
- Managing resources is not painful: similar to `pycuda`, we shift the burden of managing host/device resources to the Python garbage collector. Resources (such as `Picture` and `Surface`) are automatically freed when the user drops the reference.
|
|
126
126
|
|
|
127
|
+
## Limitations
|
|
128
|
+
|
|
129
|
+
- **One consumer per `Decoder`.** A `Decoder`/`Player` instance serves one
|
|
130
|
+
`frames()`, `screenshots()` or `screenshot()` call at a time. Use separate
|
|
131
|
+
instances (from any threads) for concurrent decodes of the same or different
|
|
132
|
+
files.
|
|
133
|
+
- **No mid-stream resolution or codec changes.** The NVDEC decoder is created
|
|
134
|
+
from the first sequence header; a later sequence with a different coded
|
|
135
|
+
size, chroma format or bit depth raises `decoder already initialized with
|
|
136
|
+
different format`. Split such files per segment.
|
|
137
|
+
- **Timestamp-less elementary streams** (e.g. raw Annex-B `.h264`/`.hevc`)
|
|
138
|
+
carry no pts: `frames()` yields `AV_NOPTS_VALUE` for every frame,
|
|
139
|
+
`pts2time` returns `None`, and `screenshot()` cannot locate a time. Mux
|
|
140
|
+
into a container first.
|
|
141
|
+
- **Embedded cover art** (`attached_pic` streams) is skipped by `Player`
|
|
142
|
+
and reported by `VideoTrack.attached_pic` from `parse()`.
|
|
143
|
+
|
|
127
144
|
## Roadmap
|
|
128
145
|
|
|
129
146
|
- [x] Decoding
|
|
@@ -133,7 +150,7 @@ Codec support depends on your GPU:
|
|
|
133
150
|
- Color Ranges: MPEG (limited), JPEG (full)
|
|
134
151
|
- Color Space: BT.601, BT.709, BT.2020
|
|
135
152
|
- [x] Built-in cropping and scaling via NV decoder
|
|
136
|
-
- [x] Thread-
|
|
153
|
+
- [x] Thread-friendly (one consumer per decoder; decoders are independent)
|
|
137
154
|
- [ ] Encoder
|
|
138
155
|
- [x] PyTorch integration
|
|
139
156
|
|
|
@@ -86,6 +86,63 @@ def test_screenshots(clip):
|
|
|
86
86
|
assert frame.shape == (3, H, W)
|
|
87
87
|
|
|
88
88
|
|
|
89
|
+
def test_duration_property(clip):
|
|
90
|
+
with Player(clip) as p:
|
|
91
|
+
assert abs(p.duration.total_seconds() - SECONDS) < 0.2
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def test_cover_art_skipped(clip):
|
|
95
|
+
"""A PNG attached_pic stream must not be selected or count as ambiguity."""
|
|
96
|
+
d = tempfile.mkdtemp(prefix="nvc-smoke-cover-")
|
|
97
|
+
try:
|
|
98
|
+
png = os.path.join(d, "cover.png")
|
|
99
|
+
out = os.path.join(d, "cover.mp4")
|
|
100
|
+
subprocess.run(["ffmpeg", "-v", "error", "-y", "-f", "lavfi",
|
|
101
|
+
"-i", "color=c=red:size=64x64:duration=1", "-frames:v", "1", png],
|
|
102
|
+
check=True, timeout=60)
|
|
103
|
+
subprocess.run(["ffmpeg", "-v", "error", "-y", "-i", clip, "-i", png,
|
|
104
|
+
"-map", "0", "-map", "1", "-c", "copy",
|
|
105
|
+
"-disposition:v:1", "attached_pic", out], check=True, timeout=60)
|
|
106
|
+
vids = [t for t in parse(out) if isinstance(t, VideoTrack)]
|
|
107
|
+
assert [t.attached_pic for t in vids] == [False, True]
|
|
108
|
+
with Player(out) as p:
|
|
109
|
+
assert p.track.index == 0
|
|
110
|
+
n = sum(1 for _ in p.frames(torch.uint8))
|
|
111
|
+
assert n == FPS * SECONDS
|
|
112
|
+
finally:
|
|
113
|
+
shutil.rmtree(d, ignore_errors=True)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def test_mpegts_start_time_and_first_gop(clip):
|
|
117
|
+
"""MPEG-TS leaves start_time unset until probed. Probing must not swallow
|
|
118
|
+
the IDR (which used to drop the first GOP) and start_time must be real."""
|
|
119
|
+
d = tempfile.mkdtemp(prefix="nvc-smoke-ts-")
|
|
120
|
+
try:
|
|
121
|
+
out = os.path.join(d, "clip.ts")
|
|
122
|
+
subprocess.run(["ffmpeg", "-v", "error", "-y", "-i", clip, "-c", "copy", out],
|
|
123
|
+
check=True, timeout=60)
|
|
124
|
+
with Player(out) as p:
|
|
125
|
+
assert p.track._start_time > 0 # ffmpeg's TS muxer starts at 1.4 s
|
|
126
|
+
frames = list(p.frames(torch.uint8))
|
|
127
|
+
assert len(frames) == FPS * SECONDS
|
|
128
|
+
first_t = p.track.pts2time(frames[0][0])
|
|
129
|
+
assert abs(first_t.total_seconds()) < 1 / FPS
|
|
130
|
+
# TS seeks by dts with no keyframe awareness; the decoder must still
|
|
131
|
+
# produce the frame actually on screen at 1.0 s, not the next IDR.
|
|
132
|
+
t, _ = p.screenshot(timedelta(seconds=1), torch.uint8, accurate=True)
|
|
133
|
+
assert abs(t.total_seconds() - 1.0) < 1 / FPS + 1e-3
|
|
134
|
+
pts0, _ = next(iter(p.frames(torch.uint8, start=timedelta(seconds=1))))
|
|
135
|
+
assert abs(p.track.pts2time(pts0).total_seconds() - 1.0) < 1 / FPS + 1e-3
|
|
136
|
+
finally:
|
|
137
|
+
shutil.rmtree(d, ignore_errors=True)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def test_pixel_format_enum_values():
|
|
141
|
+
from nvidia_codec.ffmpeg.include.libavutil import AVPixelFormat
|
|
142
|
+
assert AVPixelFormat.YUV420P == 0 and AVPixelFormat.RGB24 == 2
|
|
143
|
+
assert isinstance(AVPixelFormat.YUV444P, int)
|
|
144
|
+
|
|
145
|
+
|
|
89
146
|
def _has_encoder(name):
|
|
90
147
|
out = subprocess.run(["ffmpeg", "-hide_banner", "-encoders"],
|
|
91
148
|
capture_output=True, text=True, timeout=30).stdout
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|