defmodule Membrane.Element.IVF.Serializer do @moduledoc """ Serializes video stream into IVF format. """ use Membrane.Filter use Membrane.Log alias Membrane.Element.IVF alias Membrane.{Buffer, RemoteStream} alias Membrane.{VP9, VP8} def_options width: [spec: [integer], description: "width of frame"], height: [spec: [integer], description: "height of frame"], scale: [spec: [integer], default: 1, description: "scale"], rate: [spec: [integer], default: 1_000_000, description: "rate"], frame_count: [spec: [integer], default: 0, description: "number of frames"] def_input_pad :input, caps: {RemoteStream, content_format: one_of([VP9, VP8]), type: :packetized}, demand_unit: :buffers def_output_pad :output, caps: :any defmodule State do @moduledoc false defstruct [:width, :height, :timebase, :first_frame, :frame_count] end @impl true def handle_init(options) do use Ratio {:ok, %State{ width: options.width, height: options.height, timebase: options.scale <|> options.rate, frame_count: options.frame_count, first_frame: true }} end @impl true def handle_demand(:output, size, :buffers, _ctx, state) do {{:ok, demand: {:input, size}}, state} end @impl true def handle_process(:input, buffer, ctx, state) do %Buffer{payload: frame, metadata: %{timestamp: timestamp}} = buffer ivf_frame = IVF.Headers.create_ivf_frame_header(byte_size(frame), timestamp, state.timebase) <> frame ivf_file_header = if state.first_frame, do: IVF.Headers.create_ivf_header( state.width, state.height, state.timebase, state.frame_count, ctx.pads.input.caps ) ivf_buffer = (ivf_file_header || "") <> ivf_frame {{:ok, buffer: {:output, %Buffer{buffer | payload: ivf_buffer}}, redemand: :output}, %State{state | first_frame: false}} end end