Skip to content

Commit 07a2abd

Browse files
authored
Merge pull request #11 from membraneframework/transcoding_policy
transcoding_policy option
2 parents d490293 + 407d663 commit 07a2abd

File tree

6 files changed

+100
-45
lines changed

6 files changed

+100
-45
lines changed

README.md

Lines changed: 1 addition & 1 deletion
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.2.2"}
20+
{:membrane_transcoder_plugin, "~> 0.3.0"}
2121
]
2222
end
2323
```

lib/transcoder.ex

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -80,16 +80,35 @@ defmodule Membrane.Transcoder do
8080
and is supposed to return the desired output stream format or its module.
8181
"""
8282
],
83-
force_transcoding?: [
84-
spec: boolean() | (stream_format() -> boolean()),
85-
default: false,
83+
transcoding_policy: [
84+
spec:
85+
:always
86+
| :if_needed
87+
| :never
88+
| (stream_format() -> :always | :if_needed | :never),
89+
default: :if_needed,
8690
description: """
87-
If set to `true`, the input media stream will be decoded and encoded, even
88-
if the input stream format and the output stream format are the same type.
91+
Specifies, when transcoding should be applied.
8992
9093
Can be either:
91-
* a boolean,
92-
* a function that receives the input stream format and returns a boolean.
94+
* an atom: `:always`, `:if_needed` (default) or `:never`,
95+
* a function that receives the input stream format and returns either `:always`,
96+
`:if_needed` or `:never`.
97+
98+
If set to `:always`, the input media stream will be decoded and encoded, even
99+
if the input stream format and the output stream format are the same type.
100+
101+
If set to `:if_needed`, the input media stream will be transcoded only if the input
102+
stream format and the output stream format are different types.
103+
This is the default behavior.
104+
105+
If set to `:never`, the input media stream won't be neither decoded nor encoded.
106+
Changing alignment, encapsulation or stream structure is still possible. This option
107+
is helpful when you want to ensure that #{inspect(__MODULE__)} will not use too much
108+
of resources, e.g. CPU or memory.
109+
110+
If the transition from the input stream format to the output stream format is not
111+
possible without decoding or encoding the stream, an error will be raised.
93112
"""
94113
],
95114
assumed_input_stream_format: [
@@ -152,16 +171,16 @@ defmodule Membrane.Transcoder do
152171
|> resolve_output_stream_format()
153172

154173
state =
155-
with %{force_transcoding?: f} when is_function(f) <- state do
156-
%{state | force_transcoding?: f.(format)}
174+
with %{transcoding_policy: f} when is_function(f) <- state do
175+
%{state | transcoding_policy: f.(format)}
157176
end
158177

159178
spec =
160179
get_child(:connector)
161180
|> plug_transcoding(
162181
format,
163182
state.output_stream_format,
164-
state.force_transcoding?
183+
state.transcoding_policy
165184
)
166185
|> get_child(:output_funnel)
167186

@@ -203,15 +222,15 @@ defmodule Membrane.Transcoder do
203222
end
204223
end
205224

206-
defp plug_transcoding(builder, input_format, output_format, force_transcoding?)
225+
defp plug_transcoding(builder, input_format, output_format, transcoding_policy)
207226
when Audio.is_audio_format(input_format) do
208227
builder
209-
|> Audio.plug_audio_transcoding(input_format, output_format, force_transcoding?)
228+
|> Audio.plug_audio_transcoding(input_format, output_format, transcoding_policy)
210229
end
211230

212-
defp plug_transcoding(builder, input_format, output_format, force_transcoding?)
231+
defp plug_transcoding(builder, input_format, output_format, transcoding_policy)
213232
when Video.is_video_format(input_format) do
214233
builder
215-
|> Video.plug_video_transcoding(input_format, output_format, force_transcoding?)
234+
|> Video.plug_video_transcoding(input_format, output_format, transcoding_policy)
216235
end
217236
end

lib/transcoder/audio.ex

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,17 +52,18 @@ defmodule Membrane.Transcoder.Audio do
5252
audio_stream_format(),
5353
boolean()
5454
) :: ChildrenSpec.builder()
55-
def plug_audio_transcoding(builder, input_format, output_format, force_transcoding?)
55+
def plug_audio_transcoding(builder, input_format, output_format, transcoding_policy)
5656
when is_audio_format(input_format) and is_audio_format(output_format) do
57-
do_plug_audio_transcoding(builder, input_format, output_format, force_transcoding?)
57+
do_plug_audio_transcoding(builder, input_format, output_format, transcoding_policy)
5858
end
5959

6060
defp do_plug_audio_transcoding(
6161
builder,
6262
%format_module{},
6363
%format_module{},
64-
false = _force_transcoding?
65-
) do
64+
transcoding_policy
65+
)
66+
when transcoding_policy in [:if_needed, :never] do
6667
Membrane.Logger.debug("""
6768
This bin will only forward buffers, as the input stream format is the same as the output stream format.
6869
""")
@@ -74,12 +75,20 @@ defmodule Membrane.Transcoder.Audio do
7475
builder,
7576
%RemoteStream{content_format: Opus},
7677
%Opus{},
77-
false = _force_transcoding?
78-
) do
78+
transcoding_policy
79+
)
80+
when transcoding_policy in [:if_needed, :never] do
7981
builder |> child(:opus_parser, Opus.Parser)
8082
end
8183

82-
defp do_plug_audio_transcoding(builder, input_format, output_format, _force_transcoding?) do
84+
defp do_plug_audio_transcoding(_builder, input_format, output_format, :never) do
85+
raise """
86+
Cannot convert input format #{inspect(input_format)} to output format #{inspect(output_format)} \
87+
with :transcoding_policy option set to :never.
88+
"""
89+
end
90+
91+
defp do_plug_audio_transcoding(builder, input_format, output_format, _transcoding_policy) do
8392
builder
8493
|> maybe_plug_parser(input_format)
8594
|> maybe_plug_decoder(input_format)

lib/transcoder/video.ex

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,30 +19,22 @@ defmodule Membrane.Transcoder.Video do
1919
video_stream_format(),
2020
boolean()
2121
) :: ChildrenSpec.builder()
22-
def plug_video_transcoding(builder, input_format, output_format, force_transcoding?)
22+
def plug_video_transcoding(builder, input_format, output_format, transcoding_policy)
2323
when is_video_format(input_format) and is_video_format(output_format) do
24-
do_plug_video_transcoding(builder, input_format, output_format, force_transcoding?)
24+
do_plug_video_transcoding(builder, input_format, output_format, transcoding_policy)
2525
end
2626

27-
defp do_plug_video_transcoding(
28-
builder,
29-
%H264{},
30-
%H264{} = output_format,
31-
false = _force_transcoding?
32-
) do
27+
defp do_plug_video_transcoding(builder, %H264{}, %H264{} = output_format, transcoding_policy)
28+
when transcoding_policy in [:if_needed, :never] do
3329
builder
3430
|> child(:h264_parser, %H264.Parser{
3531
output_stream_structure: stream_structure_type(output_format),
3632
output_alignment: output_format.alignment
3733
})
3834
end
3935

40-
defp do_plug_video_transcoding(
41-
builder,
42-
%H265{},
43-
%H265{} = output_format,
44-
false = _force_transcoding?
45-
) do
36+
defp do_plug_video_transcoding(builder, %H265{}, %H265{} = output_format, transcoding_policy)
37+
when transcoding_policy in [:if_needed, :never] do
4638
builder
4739
|> child(:h265_parser, %H265.Parser{
4840
output_stream_structure: stream_structure_type(output_format),
@@ -54,16 +46,24 @@ defmodule Membrane.Transcoder.Video do
5446
builder,
5547
%format_module{},
5648
%format_module{},
57-
false = _force_transcoding?
58-
) do
49+
transcoding_policy
50+
)
51+
when transcoding_policy in [:if_needed, :never] do
5952
Membrane.Logger.debug("""
6053
This bin will only forward buffers, as the input stream format is the same type as the output stream format.
6154
""")
6255

6356
builder
6457
end
6558

66-
defp do_plug_video_transcoding(builder, input_format, output_format, _force_transcoding?) do
59+
defp do_plug_video_transcoding(_builder, input_format, output_format, :never) do
60+
raise """
61+
Cannot convert input format #{inspect(input_format)} to output format #{inspect(output_format)} \
62+
with :transcoding_policy option set to :never.
63+
"""
64+
end
65+
66+
defp do_plug_video_transcoding(builder, input_format, output_format, _transcoding_policy) do
6767
builder
6868
|> maybe_plug_parser_and_decoder(input_format)
6969
|> maybe_plug_encoder_and_parser(output_format)

mix.exs

Lines changed: 1 addition & 1 deletion
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.2.2"
4+
@version "0.3.0"
55
@github_url "https://github.com/membraneframework/membrane_transcoder_plugin"
66

77
def project do

test/integration_test.exs

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,20 @@ defmodule Membrane.Transcoder.IntegrationTest do
7676
do: {[stream_format: {:output, state.format}], state}
7777
end
7878

79-
test "if encoder and decoder are spawned or not, depending on the value of `force_transcoding?` option" do
79+
test "if encoder and decoder are spawned or not, depending on the value of `transcoding_policy` option" do
8080
for format <- [%AAC{channels: 1}, %H264{alignment: :au, stream_structure: :annexb}],
81-
force_transcoding? <- [true, false] do
81+
transcoding_policy <- [:always, :if_needed, :never] do
82+
output_format =
83+
case format do
84+
%H264{} -> %H264{format | stream_structure: :avc1}
85+
%AAC{} -> format
86+
end
87+
8288
spec =
8389
child(:source, %FormatSource{format: format})
8490
|> child(:transcoder, %Membrane.Transcoder{
85-
output_stream_format: format,
86-
force_transcoding?: force_transcoding?
91+
output_stream_format: output_format,
92+
transcoding_policy: transcoding_policy
8793
})
8894
|> child(:sink, Testing.Sink)
8995

@@ -98,7 +104,7 @@ defmodule Membrane.Transcoder.IntegrationTest do
98104
|> Enum.each(fn child_name ->
99105
get_child_result = Testing.Pipeline.get_child_pid(pipeline, [:transcoder, child_name])
100106

101-
if force_transcoding? do
107+
if transcoding_policy == :always do
102108
assert {:ok, child_pid} = get_child_result
103109
assert is_pid(child_pid)
104110
else
@@ -109,4 +115,25 @@ defmodule Membrane.Transcoder.IntegrationTest do
109115
Testing.Pipeline.terminate(pipeline)
110116
end
111117
end
118+
119+
test "if transcoder raises when `transcoding_policy` is set to `:never` and formats don't match" do
120+
spec =
121+
child(:source, %FormatSource{format: %H264{alignment: :au, stream_structure: :annexb}})
122+
|> child(:transcoder, %Membrane.Transcoder{
123+
output_stream_format: VP8,
124+
transcoding_policy: :never
125+
})
126+
|> child(:sink, Testing.Sink)
127+
128+
{:ok, supervisor, pipeline} = Testing.Pipeline.start(spec: [])
129+
supervisor_ref = Process.monitor(supervisor)
130+
pipeline_ref = Process.monitor(pipeline)
131+
132+
Testing.Pipeline.execute_actions(pipeline, spec: spec)
133+
134+
assert_receive {:DOWN, ^pipeline_ref, :process, _pid,
135+
{:membrane_child_crash, :transcoder, {%RuntimeError{}, _stacktrace}}}
136+
137+
assert_receive {:DOWN, ^supervisor_ref, :process, _pid, _reason}
138+
end
112139
end

0 commit comments

Comments
 (0)