Skip to content
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

Remove implementation option, VC refactor #57

Merged
merged 16 commits into from
Nov 17, 2022
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 3 additions & 22 deletions lib/membrane/video_compositor.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,8 @@ defmodule Membrane.VideoCompositor do

use Membrane.Filter
alias Membrane.RawVideo
alias Membrane.VideoCompositor.Implementations

def_options implementation: [
type: :atom,
spec: Implementations.implementation_t() | {:mock, module()},
description: "Implementation of video composer."
],
caps: [

def_options caps: [
type: RawVideo,
description: "Struct with video width, height, framerate and pixel format."
]
Expand All @@ -41,7 +35,7 @@ defmodule Membrane.VideoCompositor do

@impl true
def handle_init(options) do
compositor_module = determine_compositor_module(options.implementation)
compositor_module = Membrane.VideoCompositor.Wgpu
WojciechBarczynski marked this conversation as resolved.
Show resolved Hide resolved

{:ok, internal_state} = compositor_module.init(options.caps)

Expand Down Expand Up @@ -161,17 +155,4 @@ defmodule Membrane.VideoCompositor do
Map.to_list(pads)
|> Enum.all?(fn {ref, pad} -> ref == :output or pad.end_of_stream? end)
end

defp determine_compositor_module(implementation) do
case implementation do
{:mock, module} ->
module

implementation ->
case Implementations.get_implementation_module(implementation) do
{:ok, module} -> module
{:error, error} -> raise error
end
end
end
end
76 changes: 0 additions & 76 deletions lib/membrane/video_compositor/frame_compositor.ex

This file was deleted.

33 changes: 0 additions & 33 deletions lib/membrane/video_compositor/implementations.ex

This file was deleted.

82 changes: 69 additions & 13 deletions lib/membrane/video_compositor/wgpu/wgpu.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,30 @@ defmodule Membrane.VideoCompositor.Wgpu do
@moduledoc """
This module implements video composition in wgpu
"""
WojciechBarczynski marked this conversation as resolved.
Show resolved Hide resolved
@behaviour Membrane.VideoCompositor.FrameCompositor

alias Membrane.VideoCompositor.Common.{RawVideo, VideoProperties}
alias Membrane.VideoCompositor.Common
WojciechBarczynski marked this conversation as resolved.
Show resolved Hide resolved
alias Membrane.VideoCompositor.Wgpu.Native

@impl true
@type id_t() :: non_neg_integer()
@type internal_state_t() :: any()
@type error_t() :: any()
@type frame_t() :: binary()
@type pts_t() :: Membrane.Time.t()
@type frame_with_pts_t :: {binary(), pts_t()}

@spec init(Membrane.RawVideo.t()) :: {:error, any} | {:ok, any}
def init(output_caps) do
{:ok, output_caps} = RawVideo.from_membrane_raw_video(output_caps)
{:ok, output_caps} = Common.RawVideo.from_membrane_raw_video(output_caps)
Native.init(output_caps)
end

@impl true
@doc """
Uploads a frame to the compositor.

If all videos have provided input frames with a current enough pts, this will also render and return a composed frame.
"""
@spec upload_frame(internal_state_t(), id_t(), frame_with_pts_t()) ::
{:ok | {:ok, frame_with_pts_t()}, internal_state_t()}
WojciechBarczynski marked this conversation as resolved.
Show resolved Hide resolved
def upload_frame(state, id, {frame, pts}) do
case Native.upload_frame(state, id, frame, pts) do
:ok ->
Expand All @@ -27,44 +39,88 @@ defmodule Membrane.VideoCompositor.Wgpu do
end
end

@impl true
@doc """
Forcibly renders the composed frame, even if we are still waiting for some frames to arrive
"""
@spec force_render(internal_state :: internal_state_t) ::
{{:ok, merged_frames :: frame_with_pts_t()}, internal_state_t} | {:error, error_t()}

def force_render(state) do
case Native.force_render(state) do
{:ok, frame} -> {{:ok, frame}, state}
{:error, reason} -> raise "Error while force rendering, reason: #{inspect(reason)}"
end
end

@impl true
@doc """
Registers a new input video with the given numerical `id`.

Provided `id` should be unique within all previous ones, otherwise the compositor may or may not replace
the old video with this id with a new one.
`x` and `y` are pixel coordinates specifying where the top-left corner of the video should be.
`z` must be a float between 0.0 and 1.0, and it determines which videos are drawn in front of others.
A video with a higher `z` coordinate will cover videos with lower `z` coordinates.
"""
@spec add_video(
internal_state :: internal_state_t,
id :: id_t(),
input_caps :: Membrane.RawVideo.t(),
position :: {x :: non_neg_integer(), y :: non_neg_integer()},
z :: float(),
scale :: float()
) :: {:ok, internal_state_t} | {:error, error_t()}
def add_video(state, id, input_caps, {x, y}, z \\ 0.0, scale \\ 1.0) do
{:ok, input_caps} = RawVideo.from_membrane_raw_video(input_caps)
properties = VideoProperties.from_tuple({x, y, z, scale})
{:ok, input_caps} = Common.RawVideo.from_membrane_raw_video(input_caps)
properties = Common.VideoProperties.from_tuple({x, y, z, scale})

case Native.add_video(state, id, input_caps, properties) do
:ok -> {:ok, state}
{:error, reason} -> raise "Error while adding a video, reason: #{inspect(reason)}"
end
end

@impl true
@doc """
`x` and `y` are pixel coordinates specifying where the top-left corner of the video should be.
`z` must be a float between 0.0 and 1.0, and it determines which videos are drawn in front of others.
A video with a higher `z` coordinate will cover videos with lower `z` coordinates.
"""
@spec set_properties(
internal_state :: internal_state_t,
id :: id_t(),
position :: {x :: non_neg_integer(), y :: non_neg_integer()},
z :: float(),
scale :: float()
) :: {:ok, internal_state_t} | {:error, error_t()}
def set_properties(state, id, {x, y}, z \\ 0.0, scale \\ 1.0) do
properties = VideoProperties.from_tuple({x, y, z, scale})
properties = Common.VideoProperties.from_tuple({x, y, z, scale})

case Native.set_properties(state, id, properties) do
:ok -> {:ok, state}
{:error, reason} -> raise "Error while setting video properties, reason: #{inspect(reason)}"
end
end

@impl true
@doc """
If the video doesn't exist this will return an error.
"""
@spec remove_video(
internal_state :: internal_state_t,
id :: id_t()
) :: {:ok, internal_state_t} | {:error, error_t()}
def remove_video(state, id) do
case Native.remove_video(state, id) do
:ok -> {:ok, state}
{:error, reason} -> raise "Error while removing a video, reason: #{inspect(reason)}"
end
end

@impl true
@doc """
Send an end of stream to a video with the given `id`.

This causes the video to be deleted after it's enqueued frames are used up.
"""
@spec send_end_of_stream(internal_state_t(), id_t()) ::
{:ok, internal_state_t()} | {:error, error_t()}
def send_end_of_stream(state, id) do
case Native.send_end_of_stream(state, id) do
:ok ->
Expand Down
1 change: 0 additions & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ defmodule Membrane.VideoCompositor.Mixfile do
{:membrane_file_plugin, "~> 0.12.0", only: :test},
{:membrane_h264_ffmpeg_plugin, "~> 0.21.0", only: :test},
{:membrane_raw_video_parser_plugin, "~> 0.8.0", only: :test},
{:membrane_video_compositor_plugin_utility, path: "./utility", only: :test},
{:membrane_video_compositor_plugin_pipeline, path: "./pipeline", only: :test},
# Development
{:ex_doc, ">= 0.0.0", only: :dev, runtime: false},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,20 +33,14 @@ defmodule Membrane.VideoCompositor.Pipeline.Utility.Options do
"""
@type encoder_t :: Membrane.Filter.t() | nil

@typedoc """
Atom describing FrameComposer implementation
"""
@type implementation_t :: atom()

@type t() :: %__MODULE__{
inputs: inputs_t(),
output: output_t(),
caps: caps_t(),
compositor: compositor_t(),
implementation: implementation_t(),
decoder: decoder_t(),
encoder: encoder_t()
}
@enforce_keys [:inputs, :output, :caps]
defstruct [:inputs, :output, :caps, :compositor, :implementation, :decoder, :encoder]
defstruct [:inputs, :output, :caps, :compositor, :decoder, :encoder]
end
Loading