lib/membrane/aac/parser/audio_specific_config.ex

defmodule Membrane.AAC.Parser.AudioSpecificConfig do
  @moduledoc """
  Utility functions for parsing and generating Audio Specific Config.

  It's spec is described in section 1.6.2.1 of ISO/IEC 14496-3.
  """

  alias Membrane.AAC

  @spec generate_audio_specific_config(AAC.t()) :: binary()
  def generate_audio_specific_config(stream_format) do
    aot = AAC.profile_to_aot_id(stream_format.profile)
    frequency_id = AAC.sample_rate_to_sampling_frequency_id(stream_format.sample_rate)
    channel_config_id = AAC.channels_to_channel_config_id(stream_format.channels)
    frame_length_id = AAC.samples_per_frame_to_frame_length_id(stream_format.samples_per_frame)

    depends_on_core_coder = 0
    extension_flag = 0

    custom_frequency = if frequency_id == 15, do: <<stream_format.sample_rate::24>>, else: <<>>

    <<aot::5, frequency_id::4, custom_frequency::binary, channel_config_id::4, frame_length_id::1,
      depends_on_core_coder::1, extension_flag::1>>
  end

  @spec parse_audio_specific_config(binary()) :: AAC.t()
  def parse_audio_specific_config(audio_specific_config) do
    <<profile::5, frequency_id::4, audio_specific_config_rest::bitstring>> = audio_specific_config

    custom_frequency_length = if frequency_id == 15, do: 24, else: 0

    <<custom_frequency::integer-size(custom_frequency_length), channel_config_id::4,
      frame_length_id::1, _rest::bits>> = audio_specific_config_rest

    sample_rate =
      if frequency_id == 15,
        do: custom_frequency,
        else: AAC.sampling_frequency_id_to_sample_rate(frequency_id)

    %AAC{
      profile: AAC.aot_id_to_profile(profile),
      mpeg_version: 4,
      sample_rate: sample_rate,
      channels: AAC.channel_config_id_to_channels(channel_config_id),
      encapsulation: :none,
      samples_per_frame: AAC.frame_length_id_to_samples_per_frame(frame_length_id),
      config: {:audio_specific_config, audio_specific_config}
    }
  end
end