Skip to content

Commit 8a46fe4

Browse files
authored
Merge pull request #6 from membraneframework/enforce-transcoding
Add option to enforce transcoding
2 parents 231fe90 + e9af5aa commit 8a46fe4

File tree

6 files changed

+125
-26
lines changed

6 files changed

+125
-26
lines changed

Diff for: README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ The package can be installed by adding `membrane_transcoder_plugin` to your list
1717
```elixir
1818
def deps do
1919
[
20-
{:membrane_transcoder_plugin, "~> 0.1.2"}
20+
{:membrane_transcoder_plugin, "~> 0.1.3"}
2121
]
2222
end
2323
```

Diff for: lib/transcoder.ex

+36-7
Original file line numberDiff line numberDiff line change
@@ -55,15 +55,31 @@ defmodule Membrane.Transcoder do
5555
accepted_format: format when Audio.is_audio_format(format) or Video.is_video_format(format)
5656

5757
def_options output_stream_format: [
58-
spec: stream_format() | stream_format_module() | stream_format_resolver(),
58+
spec:
59+
stream_format()
60+
| stream_format_module()
61+
| stream_format_resolver(),
5962
description: """
6063
An option specifying desired output format.
64+
6165
Can be either:
6266
* a struct being a Membrane stream format,
6367
* a module in which Membrane stream format struct is defined,
6468
* a function which receives input stream format as an input argument
6569
and is supposed to return the desired output stream format or its module.
6670
"""
71+
],
72+
enforce_transcoding?: [
73+
spec: boolean() | (stream_format() -> boolean()),
74+
default: false,
75+
description: """
76+
If set to `true`, the input media stream will be decoded and encoded, even
77+
if the input stream format and the output stream format are the same type.
78+
79+
Can be either:
80+
* a boolean,
81+
* a function that receives the input stream format and returns a boolean.
82+
"""
6783
]
6884

6985
@impl true
@@ -78,7 +94,9 @@ defmodule Membrane.Transcoder do
7894
state =
7995
opts
8096
|> Map.from_struct()
81-
|> Map.put(:input_stream_format, nil)
97+
|> Map.merge(%{
98+
input_stream_format: nil
99+
})
82100

83101
{[spec: spec], state}
84102
end
@@ -94,9 +112,18 @@ defmodule Membrane.Transcoder do
94112
%{state | input_stream_format: format}
95113
|> resolve_output_stream_format()
96114

115+
state =
116+
with %{enforce_transcoding?: f} when is_function(f) <- state do
117+
%{state | enforce_transcoding?: f.(format)}
118+
end
119+
97120
spec =
98121
get_child(:forwarding_filter)
99-
|> plug_transcoding(format, state.output_stream_format)
122+
|> plug_transcoding(
123+
format,
124+
state.output_stream_format,
125+
state.enforce_transcoding?
126+
)
100127
|> get_child(:output_funnel)
101128

102129
{[spec: spec], state}
@@ -139,13 +166,15 @@ defmodule Membrane.Transcoder do
139166
end
140167
end
141168

142-
defp plug_transcoding(builder, input_format, output_format)
169+
defp plug_transcoding(builder, input_format, output_format, enforce_transcoding?)
143170
when Audio.is_audio_format(input_format) do
144-
builder |> Audio.plug_audio_transcoding(input_format, output_format)
171+
builder
172+
|> Audio.plug_audio_transcoding(input_format, output_format, enforce_transcoding?)
145173
end
146174

147-
defp plug_transcoding(builder, input_format, output_format)
175+
defp plug_transcoding(builder, input_format, output_format, enforce_transcoding?)
148176
when Video.is_video_format(input_format) do
149-
builder |> Video.plug_video_transcoding(input_format, output_format)
177+
builder
178+
|> Video.plug_video_transcoding(input_format, output_format, enforce_transcoding?)
150179
end
151180
end

Diff for: lib/transcoder/audio.ex

+17-6
Original file line numberDiff line numberDiff line change
@@ -32,26 +32,37 @@ defmodule Membrane.Transcoder.Audio do
3232
@spec plug_audio_transcoding(
3333
ChildrenSpec.builder(),
3434
audio_stream_format() | RemoteStream.t(),
35-
audio_stream_format()
35+
audio_stream_format(),
36+
boolean()
3637
) :: ChildrenSpec.builder()
37-
def plug_audio_transcoding(builder, input_format, output_format)
38+
def plug_audio_transcoding(builder, input_format, output_format, enforce_transcoding?)
3839
when is_audio_format(input_format) and is_audio_format(output_format) do
39-
do_plug_audio_transcoding(builder, input_format, output_format)
40+
do_plug_audio_transcoding(builder, input_format, output_format, enforce_transcoding?)
4041
end
4142

42-
defp do_plug_audio_transcoding(builder, %format_module{}, %format_module{}) do
43+
defp do_plug_audio_transcoding(
44+
builder,
45+
%format_module{},
46+
%format_module{},
47+
false = _enforce_transcoding?
48+
) do
4349
Membrane.Logger.debug("""
4450
This bin will only forward buffers, as the input stream format is the same as the output stream format.
4551
""")
4652

4753
builder
4854
end
4955

50-
defp do_plug_audio_transcoding(builder, %RemoteStream{content_format: Opus}, %Opus{}) do
56+
defp do_plug_audio_transcoding(
57+
builder,
58+
%RemoteStream{content_format: Opus},
59+
%Opus{},
60+
false = _enforce_transcoding?
61+
) do
5162
builder |> child(:opus_parser, Opus.Parser)
5263
end
5364

54-
defp do_plug_audio_transcoding(builder, input_format, output_format) do
65+
defp do_plug_audio_transcoding(builder, input_format, output_format, _enforce_transcoding?) do
5566
builder
5667
|> maybe_plug_parser(input_format)
5768
|> maybe_plug_decoder(input_format)

Diff for: lib/transcoder/video.ex

+17-6
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,20 @@ defmodule Membrane.Transcoder.Video do
1616
@spec plug_video_transcoding(
1717
ChildrenSpec.builder(),
1818
video_stream_format(),
19-
video_stream_format()
19+
video_stream_format(),
20+
boolean()
2021
) :: ChildrenSpec.builder()
21-
def plug_video_transcoding(builder, input_format, output_format)
22+
def plug_video_transcoding(builder, input_format, output_format, enforce_transcoding?)
2223
when is_video_format(input_format) and is_video_format(output_format) do
23-
do_plug_video_transcoding(builder, input_format, output_format)
24+
do_plug_video_transcoding(builder, input_format, output_format, enforce_transcoding?)
2425
end
2526

26-
defp do_plug_video_transcoding(builder, %h26x{}, %h26x{} = output_format)
27+
defp do_plug_video_transcoding(
28+
builder,
29+
%h26x{},
30+
%h26x{} = output_format,
31+
false = _enforce_transcoding?
32+
)
2733
when h26x in [H264, H265] do
2834
parser =
2935
h26x
@@ -36,15 +42,20 @@ defmodule Membrane.Transcoder.Video do
3642
builder |> child(:h264_parser, parser)
3743
end
3844

39-
defp do_plug_video_transcoding(builder, %format_module{}, %format_module{}) do
45+
defp do_plug_video_transcoding(
46+
builder,
47+
%format_module{},
48+
%format_module{},
49+
false = _enforce_transcoding?
50+
) do
4051
Membrane.Logger.debug("""
4152
This bin will only forward buffers, as the input stream format is the same type as the output stream format.
4253
""")
4354

4455
builder
4556
end
4657

47-
defp do_plug_video_transcoding(builder, input_format, output_format) do
58+
defp do_plug_video_transcoding(builder, input_format, output_format, _enforce_transcoding?) do
4859
builder
4960
|> maybe_plug_parser_and_decoder(input_format)
5061
|> maybe_plug_encoder_and_parser(output_format)

Diff for: mix.exs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
defmodule Membrane.Transcoder.Plugin.Mixfile do
22
use Mix.Project
33

4-
@version "0.1.2"
4+
@version "0.1.3"
55
@github_url "https://github.com/membraneframework/membrane_transcoder_plugin"
66

77
def project do

Diff for: test/integration_test.exs

+53-5
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ defmodule Membrane.Transcoder.IntegrationTest do
44
import Membrane.ChildrenSpec
55

66
alias Membrane.{AAC, H264, H265, Opus, RawAudio, RawVideo, VP8}
7-
alias Membrane.Testing.Pipeline
7+
alias Membrane.Testing
88
alias Membrane.Transcoder.Support.Preprocessors
99

1010
@video_inputs [
@@ -36,20 +36,68 @@ defmodule Membrane.Transcoder.IntegrationTest do
3636

3737
Enum.map(@test_cases, fn test_case ->
3838
test "if transcoder support #{inspect(test_case.input_format)} input and #{inspect(test_case.output_format)} output" do
39-
pid = Pipeline.start_link_supervised!()
39+
pid = Testing.Pipeline.start_link_supervised!()
4040

4141
spec =
4242
child(%Membrane.File.Source{
4343
location: Path.join("./test/fixtures", unquote(test_case.input_file))
4444
})
4545
|> then(unquote(test_case.preprocess))
4646
|> child(%Membrane.Transcoder{output_stream_format: unquote(test_case.output_format)})
47-
|> child(:sink, Membrane.Testing.Sink)
47+
|> child(:sink, Testing.Sink)
4848

49-
Pipeline.execute_actions(pid, spec: spec)
49+
Testing.Pipeline.execute_actions(pid, spec: spec)
5050

5151
assert_sink_stream_format(pid, :sink, %unquote(test_case.output_format){})
52-
Pipeline.terminate(pid)
52+
Testing.Pipeline.terminate(pid)
5353
end
5454
end)
55+
56+
defmodule FormatSource do
57+
use Membrane.Source
58+
59+
def_output_pad :output, accepted_format: _any, flow_control: :push
60+
def_options format: []
61+
62+
@impl true
63+
def handle_init(_ctx, opts), do: {[], opts |> Map.from_struct()}
64+
65+
@impl true
66+
def handle_playing(_ctx, state),
67+
do: {[stream_format: {:output, state.format}], state}
68+
end
69+
70+
test "if encoder and decoder are spawned or not, depending on the value of `enforce_transcoding?` option" do
71+
for format <- [%AAC{channels: 1}, %H264{alignment: :au, stream_structure: :annexb}],
72+
enforce_transcoding? <- [true, false] do
73+
spec =
74+
child(:source, %FormatSource{format: format})
75+
|> child(:transcoder, %Membrane.Transcoder{
76+
output_stream_format: format,
77+
enforce_transcoding?: enforce_transcoding?
78+
})
79+
|> child(:sink, Testing.Sink)
80+
81+
pipeline = Testing.Pipeline.start_link_supervised!(spec: spec)
82+
83+
Process.sleep(500)
84+
85+
case format do
86+
%H264{} -> [:h264_encoder, :h264_decoder]
87+
%AAC{} -> [:aac_encoder, :aac_decoder]
88+
end
89+
|> Enum.each(fn child_name ->
90+
get_child_result = Testing.Pipeline.get_child_pid(pipeline, [:transcoder, child_name])
91+
92+
if enforce_transcoding? do
93+
assert {:ok, child_pid} = get_child_result
94+
assert is_pid(child_pid)
95+
else
96+
assert {:error, :child_not_found} = get_child_result
97+
end
98+
end)
99+
100+
Testing.Pipeline.terminate(pipeline)
101+
end
102+
end
55103
end

0 commit comments

Comments
 (0)