lib/membrane_mp4/depayloader/aac.ex

defmodule Membrane.MP4.Depayloader.AAC do
  @moduledoc """
  Depayloads AAC stream.
  """
  use Membrane.Filter

  alias Membrane.AAC

  def_input_pad :input,
    demand_unit: :buffers,
    demand_mode: :auto,
    accepted_format: Membrane.MP4.Payload

  def_output_pad :output,
    demand_mode: :auto,
    accepted_format: AAC

  @impl true
  def handle_init(_ctx, _opts) do
    {[], %{}}
  end

  @impl true
  def handle_stream_format(:input, stream_format, _ctx, state) do
    content = stream_format.content
    esds_content = get_esds_content(content.esds)

    stream_format = %Membrane.AAC{
      channels: content.channels,
      encapsulation: :none,
      mpeg_version: 2,
      profile: esds_content.profile,
      sample_rate: content.sample_rate,
      samples_per_frame: esds_content.samples_per_frame
    }

    {[stream_format: {:output, stream_format}], state}
  end

  defp get_esds_content(esds) do
    stream_priority = 0

    {esds, <<>>} = unpack_esds_section(esds, 3)
    <<_elementary_stream_id::16, ^stream_priority, rest::binary>> = esds

    {section_4, esds_section_6} = unpack_esds_section(rest, 4)
    {<<2>>, <<>>} = unpack_esds_section(esds_section_6, 6)

    # 64 = mpeg4-audio
    object_type_id = 64
    # 5 = audio
    stream_type = 5
    upstream_flag = 0
    reserved_flag_set_to_1 = 1
    buffer_size = 0

    <<^object_type_id, ^stream_type::6, ^upstream_flag::1, ^reserved_flag_set_to_1::1,
      ^buffer_size::24, _max_bit_rate::32, _avg_bit_rate::32, esds_section_5::binary>> = section_4

    {section_5, <<>>} = unpack_esds_section(esds_section_5, 5)

    depends_on_core_coder = 0
    extension_flag = 0

    <<aot_id::5, _frequency_id::4, _channel_config_id::4, frame_length_id::1,
      ^depends_on_core_coder::1, ^extension_flag::1>> = section_5

    %{
      profile: AAC.aot_id_to_profile(aot_id),
      samples_per_frame: AAC.frame_length_id_to_samples_per_frame(frame_length_id)
    }
  end

  defp unpack_esds_section(section, section_no) do
    type_tag = <<128, 128, 128>>

    <<^section_no::8-integer, ^type_tag::binary-size(3), payload_size::8-integer, rest::binary>> =
      section

    <<payload::binary-size(payload_size), rest::binary>> = rest
    {payload, rest}
  end

  @impl true
  def handle_process(:input, buffer, _ctx, state) do
    {[buffer: {:output, buffer}], state}
  end
end