Skip to content

Add multivariant output support to Membrane.Transcoder#21

Open
khamilowicz wants to merge 5 commits into
masterfrom
multivariant-output
Open

Add multivariant output support to Membrane.Transcoder#21
khamilowicz wants to merge 5 commits into
masterfrom
multivariant-output

Conversation

@khamilowicz
Copy link
Copy Markdown
Contributor

@khamilowicz khamilowicz commented May 21, 2026

Summary

  • On-request output padsMembrane.Transcoder now exposes availability: :on_request output pads, each accepting independent output_stream_format, transcoding_policy, and native_acceleration options via via_out. Single-output backward-compatible usage is unchanged.
  • Fan-out via Tee — multi-output mode inserts a Membrane.Tee.Parallel between the connector and per-output transcoding chains, created atomically with all outputs in one spec to avoid the race where the Tee could receive data before any output is connected.
  • Exampleexamples/multivariant_output.exs demonstrates a single H264 input transcoded simultaneously to H264, H265, and VP8 outputs.
  • Tests — 5 new integration tests cover multivariant video and audio scenarios. Content correctness is verified by asserting each multivariant output is byte-identical to the equivalent single-output pipeline run.

Usage

# Multiple outputs
child(:transcoder, Membrane.Transcoder),
get_child(:transcoder)
|> via_out(Pad.ref(:output, 0), options: [output_stream_format: H264])
|> child(:hd_sink, Membrane.File.Sink),
get_child(:transcoder)
|> via_out(Pad.ref(:output, 1), options: [output_stream_format: VP8])
|> child(:vp8_sink, Membrane.File.Sink)

Test plan

  • mix test --exclude vk — 54 tests, 0 failures
  • Single-output backward-compatible usage unchanged (existing test suite covers this)
  • Multivariant output produces byte-identical results to equivalent single-output pipelines
  • Run elixir examples/multivariant_output.exs to verify the example produces three non-empty output files

🤖 Generated with Claude Code

@khamilowicz khamilowicz requested a review from varsill May 21, 2026 12:25
@khamilowicz khamilowicz force-pushed the multivariant-output branch 3 times, most recently from 406c796 to 0eb8420 Compare May 22, 2026 08:33
Comment thread PLAN.prd Outdated
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we want to have this commited

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to update existing examples as well

Comment thread lib/transcoder/audio.ex Outdated
end

defp child_name(nil, base), do: base
defp child_name(suffix, base), do: :"#{suffix}_#{base}"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am slightly worried about creating atoms dynamically since they are not garbage collected. How about making child's name a String?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ChildSpec allows only atoms or tuples as names, I went with tuple {suffix, base}.

Comment thread lib/transcoder/video.ex Outdated

defp maybe_plug_encoder_and_parser(builder, %VP8{}) do
defp maybe_plug_encoder_and_parser(builder, %VP8{}, suffix) do
cpu_quota = :erlang.system_info(:cpu_quota)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I know it's unrelates but it brought my attention :D)
What :cpu_quota would return in contenerized environments (e.g. with docker)?
The problem is that if :cpu_quota doesn't reflect some cgroup limits, then we might end up in a situation that we will try to use all the cores available in the machine, and we will be given only fraction of it, which ends up in thrashing

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I used :logical_processors_online to handle this case.

The output pad is now availability: :on_request, allowing a single
transcoder bin to produce multiple independent output streams each
with their own output_stream_format, transcoding_policy, and
native_acceleration options.

Single-output backward-compatible usage is unchanged — implicit
via_out links inherit bin-level options and use the same internal
child names, so existing tests and code require no modification.

Multi-output pipelines use a Membrane.Tee.Parallel (new dep) placed
between the connector and per-output transcoding chains within a
single atomic spec, avoiding the race where the Tee could receive
data before any output is connected.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@khamilowicz khamilowicz force-pushed the multivariant-output branch 2 times, most recently from 5a6305d to 3b00a93 Compare May 26, 2026 08:55
@khamilowicz khamilowicz requested a review from varsill May 26, 2026 09:06
@khamilowicz khamilowicz force-pushed the multivariant-output branch from 3b00a93 to b29e223 Compare May 26, 2026 09:38
- use strings as children names
- update examples
@khamilowicz khamilowicz force-pushed the multivariant-output branch from b29e223 to be2620a Compare May 26, 2026 09:41
Comment thread test/integration_test.exs Outdated
Comment on lines +334 to +336
tmp = fn -> Path.join(System.tmp_dir!(), "mv_#{:erlang.unique_integer([:positive])}") end
h264_tmp = tmp.()
vp8_tmp = tmp.()
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use %{tmp_dir: tmp_dir} context

@khamilowicz khamilowicz requested a review from varsill May 26, 2026 14:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

2 participants