mpeg-audio-frame-parser/lib/mpeg_audio_frame_parser/frame.ex

218 lines
8.2 KiB
Elixir

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
with version_id = parse_version(header),
layer = parse_layer(header),
crc_protection = parse_crc_protection(header),
bitrate = parse_bitrate(header),
sample_rate = parse_sample_rate(header),
padding = parse_padding(header),
valid = valid?(version_id, layer, bitrate, sample_rate),
frame_length = frame_length(version_id, layer, bitrate, sample_rate, padding)
do
%Frame{
data: header,
version_id: version_id,
layer: layer,
crc_protection: crc_protection,
bitrate: bitrate,
sample_rate: sample_rate,
padding: padding,
valid: valid,
length: frame_length,
}
end
end
def add_bytes(frame, packet) do
limit = frame.length - byte_size(frame.data) |> max(0)
{:ok, bytes, rest} = split_packet(packet, limit)
{:ok, %{frame | data: frame.data <> bytes}, rest}
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(_, :layer1), do: 384
defp samples_per_frame(_, :layer2), do: 1152
defp samples_per_frame(:version1, :layer3), do: 1152
defp samples_per_frame(_, :layer3), do: 576
defp samples_per_frame(_, _), do: 0
defp valid?(version_id, layer, bitrate, sample_rate)
when version_id != :reserved
and layer != :reserved
and bitrate != :bad
and sample_rate != :bad
do
true
end
defp valid?(_version_id, _layer, _bitrate, _sample_rate), do: false
defp frame_length(version_id, layer, bitrate, sample_rate, padding)
when is_integer(bitrate)
and is_integer(sample_rate)
do
bits_per_frame = samples_per_frame(version_id, layer) / 8
(bits_per_frame * (bitrate * 1000) / sample_rate + padding)
|> trunc
end
defp frame_length(_version_id, _layer, _bitrate, _sample_rate, _padding), do: 0
end