defmodule MPEGAudioFrameParser.Frame do defstruct version_id: nil, crc_protection: false, bitrate: nil, layer: nil, sample_rate: nil, padding: 0, channel_mode: nil, data: <<>>, length: 0, valid: false alias MPEGAudioFrameParser.Frame require Logger @sync_word 0b11111111111 @header_length 32 def from_header(header) when is_binary(header) and bit_size(header) == @header_length do frame = %Frame{data: header} |> Map.put(:version_id, parse_version(header)) |> Map.put(:layer, parse_layer(header)) |> Map.put(:crc_protection, parse_crc_protection(header)) |> Map.put(:bitrate, parse_bitrate(header)) |> Map.put(:sample_rate, parse_sample_rate(header)) |> Map.put(:padding, parse_padding(header)) %{frame | valid: header_valid?(frame), length: frame_length(frame)} end def header_valid?(%Frame{version_id: version_id, layer: layer, bitrate: bitrate, sample_rate: sample_rate}) when version_id != :reserved and layer != :reserved and bitrate != :bad and sample_rate != :bad do true end def header_valid?(%Frame{}), do: false def frame_length(%Frame{bitrate: bitrate, sample_rate: sample_rate} = frame) when is_integer(bitrate) and is_integer(sample_rate) do bits_per_frame = samples_per_frame(frame) / 8 (bits_per_frame * (frame.bitrate * 1000) / frame.sample_rate + frame.padding) |> trunc end def frame_length(%Frame{}), do: 0 def add_bytes(frame, packet) do limit = bytes_missing(frame) {:ok, bytes, rest} = split_packet(packet, limit) {:ok, %{frame | data: frame.data <> bytes}, rest} end def bytes_missing(frame) do (frame_length(frame) - byte_size(frame.data)) |> max(0) end # Private Functions defp split_packet(packet, limit) do bytes_available = byte_size(packet) bytes_to_take = min(bytes_available, limit) bytes_to_leave = bytes_available - bytes_to_take part1 = :binary.part(packet, {0, bytes_to_take}) part2 = :binary.part(packet, {bytes_available, -bytes_to_leave}) {:ok, part1, part2} end defp parse_version(<<@sync_word::size(11), bits::size(2), _::bits>>), do: version_atom(bits) defp version_atom(0b11), do: :version1 defp version_atom(0b10), do: :version2 defp version_atom(0b00), do: :"version2.5" defp version_atom(0b01), do: :reserved defp parse_layer(<<@sync_word::size(11), _::size(2), bits::size(2), _::bits>>), do: layer_atom(bits) defp layer_atom(0b11), do: :layer1 defp layer_atom(0b10), do: :layer2 defp layer_atom(0b01), do: :layer3 defp layer_atom(0b00), do: :reserved defp parse_crc_protection(<<@sync_word::size(11), _::size(4), 0b0::size(1), _::bits>>), do: false defp parse_crc_protection(<<@sync_word::size(11), _::size(4), 0b1::size(1), _::bits>>), do: true defp parse_bitrate(<<@sync_word::size(11), version_bits::size(2), layer_bits::size(2), _::size(1), bitrate_bits::size(4), _::bits>>) do version_atom = version_atom(version_bits) layer_atom = layer_atom(layer_bits) case {version_atom, layer_atom, bitrate_bits} do # V1, L1 {:version1, :layer1, 0b0001} -> 32 {:version1, :layer1, 0b0010} -> 64 {:version1, :layer1, 0b0011} -> 96 {:version1, :layer1, 0b0100} -> 128 {:version1, :layer1, 0b0101} -> 160 {:version1, :layer1, 0b0110} -> 192 {:version1, :layer1, 0b0111} -> 224 {:version1, :layer1, 0b1000} -> 256 {:version1, :layer1, 0b1001} -> 288 {:version1, :layer1, 0b1010} -> 320 {:version1, :layer1, 0b1011} -> 352 {:version1, :layer1, 0b1100} -> 384 {:version1, :layer1, 0b1101} -> 416 {:version1, :layer1, 0b1110} -> 448 # V1, L2 {:version1, :layer2, 0b0001} -> 32 {:version1, :layer2, 0b0010} -> 48 {:version1, :layer2, 0b0011} -> 56 {:version1, :layer2, 0b0100} -> 64 {:version1, :layer2, 0b0101} -> 80 {:version1, :layer2, 0b0110} -> 96 {:version1, :layer2, 0b0111} -> 112 {:version1, :layer2, 0b1000} -> 128 {:version1, :layer2, 0b1001} -> 160 {:version1, :layer2, 0b1010} -> 192 {:version1, :layer2, 0b1011} -> 224 {:version1, :layer2, 0b1100} -> 256 {:version1, :layer2, 0b1101} -> 320 {:version1, :layer2, 0b1110} -> 384 # V1, L3 {:version1, :layer3, 0b0001} -> 32 {:version1, :layer3, 0b0010} -> 40 {:version1, :layer3, 0b0011} -> 48 {:version1, :layer3, 0b0100} -> 56 {:version1, :layer3, 0b0101} -> 64 {:version1, :layer3, 0b0110} -> 80 {:version1, :layer3, 0b0111} -> 96 {:version1, :layer3, 0b1000} -> 112 {:version1, :layer3, 0b1001} -> 128 {:version1, :layer3, 0b1010} -> 160 {:version1, :layer3, 0b1011} -> 192 {:version1, :layer3, 0b1100} -> 224 {:version1, :layer3, 0b1101} -> 256 {:version1, :layer3, 0b1110} -> 320 # V2, L1 {version, :layer1, 0b0001} when version in [:version2, :"version2.5"] -> 32 {version, :layer1, 0b0010} when version in [:version2, :"version2.5"] -> 48 {version, :layer1, 0b0011} when version in [:version2, :"version2.5"] -> 56 {version, :layer1, 0b0100} when version in [:version2, :"version2.5"] -> 64 {version, :layer1, 0b0101} when version in [:version2, :"version2.5"] -> 80 {version, :layer1, 0b0110} when version in [:version2, :"version2.5"] -> 96 {version, :layer1, 0b0111} when version in [:version2, :"version2.5"] -> 112 {version, :layer1, 0b1000} when version in [:version2, :"version2.5"] -> 128 {version, :layer1, 0b1001} when version in [:version2, :"version2.5"] -> 144 {version, :layer1, 0b1010} when version in [:version2, :"version2.5"] -> 160 {version, :layer1, 0b1011} when version in [:version2, :"version2.5"] -> 176 {version, :layer1, 0b1100} when version in [:version2, :"version2.5"] -> 192 {version, :layer1, 0b1101} when version in [:version2, :"version2.5"] -> 224 {version, :layer1, 0b1110} when version in [:version2, :"version2.5"] -> 256 # V2, L2/L3 {version, _, 0b0001} when version in [:version2, :"version2.5"] -> 8 {version, _, 0b0010} when version in [:version2, :"version2.5"] -> 16 {version, _, 0b0011} when version in [:version2, :"version2.5"] -> 24 {version, _, 0b0100} when version in [:version2, :"version2.5"] -> 32 {version, _, 0b0101} when version in [:version2, :"version2.5"] -> 40 {version, _, 0b0110} when version in [:version2, :"version2.5"] -> 48 {version, _, 0b0111} when version in [:version2, :"version2.5"] -> 56 {version, _, 0b1000} when version in [:version2, :"version2.5"] -> 64 {version, _, 0b1001} when version in [:version2, :"version2.5"] -> 80 {version, _, 0b1010} when version in [:version2, :"version2.5"] -> 96 {version, _, 0b1011} when version in [:version2, :"version2.5"] -> 112 {version, _, 0b1100} when version in [:version2, :"version2.5"] -> 128 {version, _, 0b1101} when version in [:version2, :"version2.5"] -> 144 {version, _, 0b1110} when version in [:version2, :"version2.5"] -> 160 _ -> :bad end end defp parse_sample_rate(<<@sync_word::size(11), version_bits::size(2), _::size(7), sample_rate_bits::size(2), _::bits>>) do version_atom = version_atom(version_bits) case {version_atom, sample_rate_bits} do {:version1, 0b00} -> 44100 {:version1, 0b01} -> 48000 {:version1, 0b10} -> 32000 {:version2, 0b00} -> 22050 {:version2, 0b01} -> 24000 {:version2, 0b10} -> 16000 {:"version2.5", 0b00} -> 11025 {:"version2.5", 0b01} -> 12000 {:"version2.5", 0b10} -> 8000 _ -> :bad end end defp parse_padding(<<@sync_word::size(11), _::size(11), padding_bit::size(1), _::bits>>), do: padding_bit defp samples_per_frame(%Frame{layer: :layer1}), do: 384 defp samples_per_frame(%Frame{layer: :layer2}), do: 1152 defp samples_per_frame(%Frame{layer: :layer3, version_id: :version1}), do: 1152 defp samples_per_frame(%Frame{layer: :layer3, version_id: _}), do: 576 defp samples_per_frame(%Frame{}), do: 0 end