218 lines
8.2 KiB
Elixir
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
|