defmodule Membrane.AAC.FDK.Decoder do @moduledoc """ Element for decoding AAC audio to raw data in S16LE format. """ use Membrane.Filter alias __MODULE__.Native alias Membrane.Buffer alias Membrane.Caps.Audio.Raw use Bunch def_input_pad :input, caps: :any, demand_unit: :buffers def_output_pad :output, caps: {Raw, format: :s16le} @impl true def handle_init(_) do {:ok, %{native: nil}} end @impl true def handle_stopped_to_prepared(_ctx, state) do with {:ok, native} <- Native.create() do {:ok, %{state | native: native}} else {:error, reason} -> {{:error, reason}, state} end end @impl true def handle_prepared_to_stopped(_ctx, state) do {:ok, %{state | native: nil}} end @impl true def handle_demand(:output, size, :buffers, _ctx, state) do {{:ok, demand: {:input, size}}, state} end @impl true def handle_demand(:output, _size, :bytes, _ctx, state) do {{:ok, demand: :input}, state} end @impl true def handle_caps(:input, _caps, _ctx, state) do {:ok, state} end # Handles parsing buffer payload to raw audio frames. # # The flow is as follows: # 1. Fill the native buffer using `Native.fill` with input buffer content # 2. Natively decode audio frames using `Native.decode_frame`. # Since the input buffer can contain more than one frame, # we're calling `decode_frame` until it returns `:not_enough_bits` # to ensure we're emptying the whole native buffer. # 3. Set output caps based on the stream metadata. # This should execute only once when output caps are not specified yet, # since they should stay consistent for the whole stream. # 4. In case an unhandled error is returned during this flow, returns error message. @impl true def handle_process(:input, %Buffer{payload: payload}, ctx, state) do with :ok <- Native.fill(payload, state.native), {:ok, decoded_frames} <- decode_buffer(payload, state.native), {:ok, caps_action} <- get_caps_if_needed(ctx.pads.output.caps, state) do buffer_actions = [buffer: {:output, decoded_frames}] {{:ok, caps_action ++ buffer_actions ++ [redemand: :output]}, state} else {:error, reason} -> {{:error, reason}, state} end end defp decode_buffer(payload, native, acc \\ []) do case Native.decode_frame(payload, native) do {:ok, decoded_frame} -> # Accumulate decoded frames decode_buffer(payload, native, [%Buffer{payload: decoded_frame} | acc]) {:error, :not_enough_bits} -> # Means that we've parsed the whole buffer. {:ok, Enum.reverse(acc)} {:error, reason} -> {:error, reason} end end defp get_caps_if_needed(nil, state) do {:ok, {_frame_size, sample_rate, channels}} = Native.get_metadata(state.native) {:ok, caps: {:output, %Raw{format: :s16le, sample_rate: sample_rate, channels: channels}}} end defp get_caps_if_needed(_, _), do: {:ok, []} end