Skip to content

Commit aba56f0

Browse files
committed
open(): major changes
- `mode` input argument is now required - removed automatic conversion of ununsed `rate_in` and `rate` to related ffmpeg options - validates conditionally required arguments - validates conditionally invalid arguments
1 parent 2843d95 commit aba56f0

1 file changed

Lines changed: 97 additions & 88 deletions

File tree

src/ffmpegio/__init__.py

Lines changed: 97 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ def __getattr__(name):
7878

7979
def open(
8080
url_fg,
81-
mode="",
81+
mode,
8282
rate_in=None,
8383
shape_in=None,
8484
dtype_in=None,
@@ -219,111 +219,120 @@ def open(
219219
is_fg = kwds.get("f_in", None) == "lavfi"
220220
url_fg = (url_fg,)
221221

222-
audio = "a" in mode
223-
video = "v" in mode
224-
read = "r" in mode
225-
write = "w" in mode
226-
filter = "f" in mode
227-
# backwards = "b" in mode
228222
unk = set(mode) - set("avrwf")
229223
if unk:
230-
raise Exception(
224+
raise ValueError(
231225
f"Invalid FFmpeg streaming mode: {mode}. Unknown mode {unk} specified."
232226
)
233227

234-
if read + write + filter > 1:
235-
raise Exception(
236-
f"Invalid FFmpeg streaming mode: {mode}. Only 1 of 'rwf' may be specified."
228+
read = "r" in mode
229+
write = "w" in mode
230+
filter = "f" in mode
231+
232+
if read + write + filter != 1:
233+
raise ValueError(
234+
f"Invalid FFmpeg streaming mode argument: {mode}. It must contain one and only one of 'rwf'."
237235
)
238236

239-
if (read or write or filter):
240-
# convert unused rate argument to ffmpeg option
241-
if read and rate_in is not None:
242-
kwds['r_in' if video else 'ar_in'] = rate_in
243-
rate_in = None
244-
elif write and rate is not None:
245-
kwds['r' if video else 'ar'] = rate
246-
rate = None
237+
audio = sum(1 for m in mode if m == "a")
238+
video = sum(1 for m in mode if m == "v")
239+
240+
if audio + video == 0:
241+
raise ValueError(
242+
f"Invalid FFmpeg streaming mode argument: {mode}. Stream type not specified. Mode must contain 'v' or 'a' at least once."
243+
)
244+
245+
if read:
246+
vars = []
247+
if rate_in is not None:
248+
vars.append("rate_in")
249+
if rate is not None:
250+
vars.append("rate")
251+
if len(vars):
252+
vars = ", ".join(vars)
253+
raise ValueError(
254+
f"Invalid argument for a read stream: {vars}. To change rate, use FFmpeg 'r' argument for video stream or 'ar' argument for audio stream."
255+
)
256+
vars = []
257+
if shape_in is not None:
258+
vars.append("shape_in")
259+
if shape is not None:
260+
vars.append("shape")
261+
if len(vars):
262+
vars = ", ".join(vars)
263+
raise ValueError(
264+
f"Invalid argument for a read stream: {vars}. To change shape, use FFmpeg 's' argument for video frame or 'ac' for the number of audio channels."
265+
)
266+
267+
if dtype_in is not None:
268+
raise ValueError("Invalid argument for a read stream: dtype_in.")
247269
else:
248-
# auto-detect operation
249-
if rate_in is None:
250-
read = True
251-
elif rate is None:
252-
write = True
253-
else:
254-
filter = True
255-
256-
# auto-detect type
257-
if not (audio or video):
258-
if is_fg:
270+
if audio + video > 1:
259271
raise ValueError(
260-
"media type must be specified to read from an Input filtergraph"
272+
f"Too many streams specified: {mode}. A {'write' if write else 'filter'} stream can only process one stream at a time."
261273
)
262-
elif read:
263-
for url in url_fg:
264-
try:
265-
info = probe.streams_basic(url, entries=("codec_type",))
266-
except:
267-
raise ValueError(f"cannot auto-detect media type of {url}")
268-
for inf in info:
269-
t = inf["codec_type"]
270-
if t == "video" and not video:
271-
video = True
272-
elif t == "audio" and not audio:
273-
audio = True
274-
if video and audio:
275-
break
276-
277-
else:
278-
if shape_in is not None:
279-
audio = len(shape_in) < 2
280-
elif shape is not None:
281-
audio = len(shape) < 2
282-
else:
283-
# TODO identify based on file extension
284-
raise ValueError(f"cannot auto-detect media type")
285-
video = not audio
286-
elif read:
287-
# if audio or video is set multiple times, use avi reader
288-
if audio and not video:
289-
video = audio and sum((1 for m in mode if m == "a")) > 1
290-
elif video and not audio:
291-
audio = video and sum((1 for m in mode if m == "v")) > 1
292-
elif write and is_fg:
293-
ValueError("Cannot write to a filtergraph.")
274+
275+
if write:
276+
if is_fg:
277+
ValueError("Cannot write to a filtergraph.")
278+
if rate_in is None:
279+
raise ValueError(
280+
"Missing required argument: rate_in. A write stream must specify the rate of the input media stream."
281+
)
282+
if rate is not None:
283+
raise ValueError(
284+
"Invalid argument for a write stream: rate. To change rate, use FFmpeg 'r' argument for video stream or 'ar' argument for audio stream."
285+
)
286+
if shape is not None:
287+
raise ValueError(
288+
"Invalid argument for a read stream: shape. To change shape, use FFmpeg 's' argument for video frame or 'ac' for the number of audio channels."
289+
)
290+
else: # if filter
291+
vars = []
292+
if rate_in is None:
293+
vars.append("rate_in")
294+
if rate is None:
295+
vars.append("rate")
296+
if len(vars):
297+
vars = ", ".join(vars)
298+
raise ValueError(
299+
f"Missing required arguments: {vars}. A filter stream must specify the rates of both the input and output media streams."
300+
)
294301

295302
try:
296-
StreamClass = {
297-
1: {
298-
0: _streams.SimpleAudioReader,
299-
1: _streams.SimpleAudioWriter,
300-
2: _streams.SimpleAudioFilter,
301-
},
302-
2: {
303-
0: _streams.SimpleVideoReader,
304-
1: _streams.SimpleVideoWriter,
305-
2: _streams.SimpleVideoFilter,
306-
},
307-
3: {
308-
0: _streams.AviMediaReader,
309-
},
310-
}[audio + 2 * video][write + 2 * filter]
303+
StreamClass = (
304+
{
305+
0: {
306+
0: _streams.SimpleAudioReader,
307+
1: _streams.SimpleAudioWriter,
308+
2: _streams.SimpleAudioFilter,
309+
},
310+
1: {
311+
0: _streams.SimpleVideoReader,
312+
1: _streams.SimpleVideoWriter,
313+
2: _streams.SimpleVideoFilter,
314+
},
315+
}[video][write + 2 * filter]
316+
if audio + video == 1
317+
else _streams.AviMediaReader
318+
)
311319
except:
312-
raise Exception(f"Invalid/unsupported FFmpeg streaming mode: {mode}.")
320+
raise ValueError(f"Invalid/unsupported FFmpeg streaming mode: {mode}.")
313321

314322
if len(url_fg) > 1 and not StreamClass.multi_read:
315-
raise Exception(f'Multi-input streaming is not supported in "{mode}" mode')
323+
raise ValueError(f'Multi-input streaming is not supported in "{mode}" mode')
316324

317325
# add other info to the arguments
318326
args = (*url_fg,) if read else (*url_fg, rate_in)
319-
for k, v in (
320-
("dtype_in", dtype_in),
321-
("shape_in", shape_in),
322-
("rate", rate),
323-
("shape", shape),
324-
):
325-
if v is not None:
326-
kwds[k] = v
327+
if not read:
328+
for k, v in (
329+
("dtype_in", dtype_in),
330+
("shape_in", shape_in),
331+
("rate", rate),
332+
("shape", shape),
333+
):
334+
if v is not None:
335+
kwds[k] = v
327336

328337
# instantiate the streaming object
329338
return StreamClass(*args, **kwds)

0 commit comments

Comments
 (0)