defmodule Membrane.AAC.Parser.Esds do @moduledoc """ Utility functions for parsing and generating `esds` atom. It's structure is defined in ISO/IEC 14496-1. """ alias Membrane.AAC @spec generate_esds(AAC.t(), Membrane.Element.state()) :: binary() def generate_esds(stream_format, state) do aot_id = Membrane.AAC.profile_to_aot_id(stream_format.profile) frequency_id = Membrane.AAC.sample_rate_to_sampling_frequency_id(stream_format.sample_rate) channel_config_id = Membrane.AAC.channels_to_channel_config_id(stream_format.channels) frame_length_id = Membrane.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: <>, else: <<>> section5 = <> |> generate_esds_section(5) # 64 = mpeg4-audio object_type_id = 64 # 5 = audio stream_type = 5 upstream_flag = 0 reserved_flag_set_to_1 = 1 buffer_size = 0 section4 = <> |> generate_esds_section(4) section6 = <<2>> |> generate_esds_section(6) elementary_stream_id = 1 stream_priority = 0 <> |> generate_esds_section(3) end defp generate_esds_section(payload, section_no) do type_tag = <<128, 128, 128>> <> end @spec parse_esds(binary()) :: %{ profile: AAC.profile(), mpeg_version: 4, samples_per_frame: AAC.samples_per_frame() } def parse_esds(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 <> = section_5 custom_frequency_length = if frequency_id == 15, do: 24, else: 0 <<_maybe_custom_frequency::integer-size(custom_frequency_length), _channel_config_id::4, frame_length_id::1, ^depends_on_core_coder::1, ^extension_flag::1>> = section_5_rest %{ profile: AAC.aot_id_to_profile(aot_id), mpeg_version: 4, 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 <> = rest {payload, rest} end end