-
Notifications
You must be signed in to change notification settings - Fork 7
Refactor elixir endpoints #118
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: message-endpoint
Are you sure you want to change the base?
Conversation
7cb399c to
e85f6a1
Compare
31ca721 to
88a148a
Compare
88a148a to
e44efe8
Compare
9ac398d to
b729d70
Compare
b729d70 to
4c07d31
Compare
4c07d31 to
a053f6c
Compare
|
@Noarkhh I'd go for a macro that generates push & pull elements ;) |
lib/boombox/server.ex
Outdated
| membrane_source_demand: non_neg_integer(), | ||
| pipeline_supervisor: pid() | nil, | ||
| pipeline: pid() | nil, | ||
| ghosted_client: GenServer.from() | Process.dest() | nil, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This name is a bit ambiguous, maybe client_to_reply or current_client?
python/src/boombox/boombox.py
Outdated
|
|
||
| RELEASES_URL = "https://github.com/membraneframework/boombox/releases" | ||
| PACKAGE_NAME = "boomboxlib" | ||
| PACKAGE_NAME = "boomboxlibb" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
?
python/src/boombox/boombox.py
Outdated
|
|
||
| match self._call(request): | ||
| case Atom("ok"): | ||
| print("uuuu") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
xd
lib/boombox/pipeline.ex
Outdated
| |> Map.update!(:input, &resolve_elixir_endpoint(&1, self())) | ||
| |> Map.update!(:output, &resolve_elixir_endpoint(&1, self())) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
endpoint resolution happens in the internal bin, can we move this there?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not really, because we need to call self() in the parent process, not the bin
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're passing this pid as a parent option. We can pass it to the internal bin as well
examples.livemd
Outdated
| Stream.unfold(%{}, fn _state -> | ||
| {result1, packet1} = Boombox.read(reader1) | ||
| {result2, packet2} = Boombox.read(reader2) | ||
| case {Boombox.read(reader1), Boombox.read(reader2)} do | ||
| {:finished, :finished} -> | ||
| nil | ||
|
|
||
| joined_image = | ||
| Vix.Vips.Operation.join!(packet1.payload, packet2.payload, :VIPS_DIRECTION_HORIZONTAL) | ||
| {{:ok, _packet}, :finished} -> | ||
| Boombox.close(reader1) | ||
| nil | ||
|
|
||
| packet = %Boombox.Packet{ | ||
| pts: max(packet1.pts, packet2.pts), | ||
| payload: joined_image, | ||
| kind: :video | ||
| } | ||
| {:finished, {:ok, _packet}} -> | ||
| Boombox.close(reader2) | ||
| nil | ||
|
|
||
| Boombox.write(writer, packet) | ||
| {{:ok, packet1}, {:ok, packet2}} -> | ||
| joined_image = | ||
| Vix.Vips.Operation.join!(packet1.payload, packet2.payload, :VIPS_DIRECTION_HORIZONTAL) | ||
|
|
||
| if :finished in [result1, result2] do | ||
| if result1 == :ok, do: | ||
| Boombox.close(reader1) | ||
| if result2 == :ok, do: | ||
| Boombox.close(reader2) | ||
| nil | ||
| else | ||
| {nil, %{}} | ||
| packet = %Boombox.Packet{ | ||
| pts: max(packet1.pts, packet2.pts), | ||
| payload: joined_image, | ||
| kind: :video | ||
| } | ||
|
|
||
| Boombox.write(writer, packet) | ||
|
|
||
| {nil, %{}} | ||
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Stream.unfold with an ignored state looks weird. Also, matching on all the possibilities seems redundant. I'd go for
| Stream.unfold(%{}, fn _state -> | |
| {result1, packet1} = Boombox.read(reader1) | |
| {result2, packet2} = Boombox.read(reader2) | |
| case {Boombox.read(reader1), Boombox.read(reader2)} do | |
| {:finished, :finished} -> | |
| nil | |
| joined_image = | |
| Vix.Vips.Operation.join!(packet1.payload, packet2.payload, :VIPS_DIRECTION_HORIZONTAL) | |
| {{:ok, _packet}, :finished} -> | |
| Boombox.close(reader1) | |
| nil | |
| packet = %Boombox.Packet{ | |
| pts: max(packet1.pts, packet2.pts), | |
| payload: joined_image, | |
| kind: :video | |
| } | |
| {:finished, {:ok, _packet}} -> | |
| Boombox.close(reader2) | |
| nil | |
| Boombox.write(writer, packet) | |
| {{:ok, packet1}, {:ok, packet2}} -> | |
| joined_image = | |
| Vix.Vips.Operation.join!(packet1.payload, packet2.payload, :VIPS_DIRECTION_HORIZONTAL) | |
| if :finished in [result1, result2] do | |
| if result1 == :ok, do: | |
| Boombox.close(reader1) | |
| if result2 == :ok, do: | |
| Boombox.close(reader2) | |
| nil | |
| else | |
| {nil, %{}} | |
| packet = %Boombox.Packet{ | |
| pts: max(packet1.pts, packet2.pts), | |
| payload: joined_image, | |
| kind: :video | |
| } | |
| Boombox.write(writer, packet) | |
| {nil, %{}} | |
| end | |
| Stream.repeatedly(fn -> | |
| case {Boombox.read(reader1), Boombox.read(reader2)} do | |
| {{:ok, packet1}, {:ok, packet2}} -> | |
| joined_image = | |
| Vix.Vips.Operation.join!(packet1.payload, packet2.payload, :VIPS_DIRECTION_HORIZONTAL) | |
| packet = %Boombox.Packet{ | |
| pts: max(packet1.pts, packet2.pts), | |
| payload: joined_image, | |
| kind: :video | |
| } | |
| :ok = Boombox.write(writer, packet) | |
| _finished -> | |
| :eos | |
| end | |
| end) | |
| |> Enum.find(& &1 == :eos) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mat-hek it looks good we just need to remember to call Boombox.close()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, missed that. Maybe Boombox.close on already closed boombox could be a noop, so we could just close both boomboxes no matter what?
|
|
||
| alias Boombox.InternalBin.ElixirEndpoints.Sink | ||
|
|
||
| def_input_pad :input, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How many pads of this type can exist? Wouldn't max_instances be applicable here?
examples.livemd
Outdated
| bb_states: %{ | ||
| bb1: %{last_packet: nil, eos: false}, | ||
| bb2: %{last_packet: nil, eos: false} | ||
| }, | ||
| input_boomboxes: %{bb1 => :bb1, bb2 => :bb2}, | ||
| bbs: %{bb1 => :bb1, bb2 => :bb2}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[NIT] I am not 100% encouraged by this change, bbs look quite funny to me :D
examples.livemd
Outdated
| Stream.unfold(%{}, fn _state -> | ||
| {result1, packet1} = Boombox.read(reader1) | ||
| {result2, packet2} = Boombox.read(reader2) | ||
| case {Boombox.read(reader1), Boombox.read(reader2)} do | ||
| {:finished, :finished} -> | ||
| nil | ||
|
|
||
| joined_image = | ||
| Vix.Vips.Operation.join!(packet1.payload, packet2.payload, :VIPS_DIRECTION_HORIZONTAL) | ||
| {{:ok, _packet}, :finished} -> | ||
| Boombox.close(reader1) | ||
| nil | ||
|
|
||
| packet = %Boombox.Packet{ | ||
| pts: max(packet1.pts, packet2.pts), | ||
| payload: joined_image, | ||
| kind: :video | ||
| } | ||
| {:finished, {:ok, _packet}} -> | ||
| Boombox.close(reader2) | ||
| nil | ||
|
|
||
| Boombox.write(writer, packet) | ||
| {{:ok, packet1}, {:ok, packet2}} -> | ||
| joined_image = | ||
| Vix.Vips.Operation.join!(packet1.payload, packet2.payload, :VIPS_DIRECTION_HORIZONTAL) | ||
|
|
||
| if :finished in [result1, result2] do | ||
| if result1 == :ok, do: | ||
| Boombox.close(reader1) | ||
| if result2 == :ok, do: | ||
| Boombox.close(reader2) | ||
| nil | ||
| else | ||
| {nil, %{}} | ||
| packet = %Boombox.Packet{ | ||
| pts: max(packet1.pts, packet2.pts), | ||
| payload: joined_image, | ||
| kind: :video | ||
| } | ||
|
|
||
| Boombox.write(writer, packet) | ||
|
|
||
| {nil, %{}} | ||
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mat-hek it looks good we just need to remember to call Boombox.close()
test/boombox_test.exs
Outdated
| Stream.unfold(:ok, fn | ||
| :ok -> | ||
| {result, packet} = Boombox.read(boombox) | ||
| {packet, result} | ||
|
|
||
| :finished -> | ||
| nil | ||
| case Boombox.read(boombox) do | ||
| {:ok, packet} -> {packet, :ok} | ||
| :finished -> nil | ||
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[NIT] This "state" here is useless so you can remove it
And similarly to the case in examples.livemd, you could use Enum.repeatedly here
python/src/boombox/boombox.py
Outdated
| if not self._response.done(): | ||
| self._response.set_result(response) | ||
| case (Atom("DOWN"), _, Atom("process"), _, Atom("normal")): | ||
| print(self._boombox_mode) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you can remove these :D
|
|
||
| @impl true | ||
| def handle_info(:boombox_demand, _ctx, state) do | ||
| def handle_info({:boombox_demand, consumer}, _ctx, %{consumer: consumer} = state) do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shouldn't we buffer that demand up if consumer is not yet available?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually after looking at the whole module I am not sure if the consumer field in the state is defined anywhere
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the consumer is passed with options, but I agree, this needs better typing
| def_input_pad :input, | ||
| accepted_format: any_of(Membrane.RawAudio, Membrane.RawVideo), | ||
| availability: :on_request, | ||
| flow_control: :auto |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why :auto instead of :push?
7cf42c2 to
ade6ffc
Compare
ade6ffc to
5a236ad
Compare
b46ae53 to
e99f2d6
Compare
When using server-based endpoints (reader, writer, message), the server will now spawn the pipeline directly and communicate with elixir elements directly.
I'm not sure which approach is better for having push and pull elixir source and sink - using defdelegetes and have a common behavior module (the current one) or have a macro that generates push and pull element.