diff --git a/ffmpeg/_run.py b/ffmpeg/_run.py index 3167634..afc504d 100644 --- a/ffmpeg/_run.py +++ b/ffmpeg/_run.py @@ -84,7 +84,7 @@ def _allocate_filter_stream_names(filter_nodes, outgoing_edge_maps, stream_name_ stream_count = 0 for upstream_node in filter_nodes: outgoing_edge_map = outgoing_edge_maps[upstream_node] - for upstream_label, downstreams in list(outgoing_edge_map.items()): + for upstream_label, downstreams in sorted(outgoing_edge_map.items()): if len(downstreams) > 1: # TODO: automatically insert `splits` ahead of time via graph transformation. raise ValueError( diff --git a/ffmpeg/dag.py b/ffmpeg/dag.py index bb39823..9564d7f 100644 --- a/ffmpeg/dag.py +++ b/ffmpeg/dag.py @@ -100,7 +100,7 @@ def get_incoming_edges(downstream_node, incoming_edge_map): def get_outgoing_edges(upstream_node, outgoing_edge_map): edges = [] - for upstream_label, downstream_infos in list(outgoing_edge_map.items()): + for upstream_label, downstream_infos in sorted(outgoing_edge_map.items()): for downstream_info in downstream_infos: downstream_node, downstream_label, downstream_selector = downstream_info edges += [ diff --git a/ffmpeg/tests/test_ffmpeg.py b/ffmpeg/tests/test_ffmpeg.py index 9763812..b40d624 100644 --- a/ffmpeg/tests/test_ffmpeg.py +++ b/ffmpeg/tests/test_ffmpeg.py @@ -716,3 +716,51 @@ def test__probe__exception(): def test__probe__extra_args(): data = ffmpeg.probe(TEST_INPUT_FILE1, show_frames=None) assert set(data.keys()) == {'format', 'streams', 'frames'} + +def get_filter_complex_input(flt, name): + m = re.search(r'\[([^]]+)\]{}(?=[[;]|$)'.format(name), flt) + if m: + return m.group(1) + else: + return None + +def get_filter_complex_outputs(flt, name): + m = re.search(r'(^|[];]){}((\[[^]]+\])+)(?=;|$)'.format(name), flt) + if m: + return m.group(2)[1:-1].split('][') + else: + return None + +def test__get_filter_complex_input(): + assert get_filter_complex_input("", "scale") is None + assert get_filter_complex_input("scale", "scale") is None + assert get_filter_complex_input("scale[s3][s4];etc", "scale") is None + assert get_filter_complex_input("[s2]scale", "scale") == "s2" + assert get_filter_complex_input("[s2]scale;etc", "scale") == "s2" + assert get_filter_complex_input("[s2]scale[s3][s4];etc", "scale") == "s2" + +def test__get_filter_complex_outputs(): + assert get_filter_complex_outputs("", "scale") is None + assert get_filter_complex_outputs("scale", "scale") is None + assert get_filter_complex_outputs("scalex[s0][s1]", "scale") is None + assert get_filter_complex_outputs("scale[s0][s1]", "scale") == ['s0', 's1'] + assert get_filter_complex_outputs("[s5]scale[s0][s1]", "scale") == ['s0', 's1'] + assert get_filter_complex_outputs("[s5]scale[s1][s0]", "scale") == ['s1', 's0'] + assert get_filter_complex_outputs("[s5]scale[s1]", "scale") == ['s1'] + assert get_filter_complex_outputs("[s5]scale[s1];x", "scale") == ['s1'] + assert get_filter_complex_outputs("y;[s5]scale[s1];x", "scale") == ['s1'] + +def test__multi_output_edge_label_order(): + scale2ref = ffmpeg.filter_multi_output([ffmpeg.input('x'), ffmpeg.input('y')], 'scale2ref') + out = ( + ffmpeg.merge_outputs( + scale2ref[1].filter('scale').output('a'), + scale2ref[10000].filter('hflip').output('b') + ) + ) + + args = out.get_args() + flt_cmpl = args[args.index('-filter_complex')+1] + out1, out2 = get_filter_complex_outputs(flt_cmpl, 'scale2ref') + assert out1 == get_filter_complex_input(flt_cmpl, 'scale') + assert out2 == get_filter_complex_input(flt_cmpl, 'hflip')