lib/turbojpeg/sink.ex

defmodule Turbojpeg.Sink do
  @moduledoc """
  Element responsible for converting YUV binary data to jpeg image format using `turbojpeg`.
  """

  use Membrane.Sink

  alias Membrane.{Buffer, RawVideo}

  def_input_pad :input,
    flow_control: :auto,
    accepted_format: %RawVideo{pixel_format: pix_fmt} when pix_fmt in [:I420, :I422, :I444]

  def_options filename: [
                spec: binary(),
                description: "File to write the jpeg data"
              ],
              quality: [
                spec: non_neg_integer(),
                default: 75,
                description: "Jpeg encoding quality"
              ]

  @impl true
  def handle_init(_ctx, options) do
    state =
      options
      |> Map.from_struct()
      |> Map.merge(%{
        height: nil,
        width: nil,
        format: nil
      })

    {[], state}
  end

  @impl true
  def handle_stream_format(
        :input,
        %RawVideo{width: width, height: height, pixel_format: pix_fmt},
        _ctx,
        state
      ) do
    {[], %{state | width: width, height: height, format: pix_fmt}}
  end

  @impl true
  def handle_buffer(:input, %Buffer{payload: payload}, _ctx, state) do
    with {:ok, data} <-
           Turbojpeg.yuv_to_jpeg(payload, state.width, state.height, state.quality, state.format),
         :ok <- File.write(state.filename, data) do
      {[], state}
    else
      error ->
        raise """
        could not create a JPEG file
        #{inspect(error)}
        """
    end
  end
end