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

77 lines
2.3 KiB
Elixir
Raw Normal View History

2018-04-17 09:42:54 +00:00
defmodule MPEGAudioFrameParser.Impl do
alias MPEGAudioFrameParser.Frame
require Logger
@sync_word 0b11111111111
@initial_state %{leftover: <<>>, current_frame: nil, frames: []}
def init() do
{:ok, @initial_state}
end
def add_packet(state, packet) do
process_bytes(state, packet)
end
def pop_frame(%{frames: []} = state) do
{:ok, nil, state}
end
def pop_frame(state) do
{frame, rest} = List.pop_at(state.frames, -1)
{:ok, frame, %{state | frames: rest}}
end
def flush(state) do
{:ok, state.frames, @initial_state}
end
# Private Functions
# Synced, and the current frame is complete:
defp process_bytes(%{current_frame: %Frame{length: frame_length, data: data}} = state, packet)
when frame_length == byte_size(data)
do
frames = [state.current_frame | state.frames]
process_bytes(%{state | current_frame: nil, frames: frames}, packet)
end
2018-04-17 09:42:54 +00:00
# No data left, or not enough to be able to validate next frame. Return:
defp process_bytes(state, packet)
when bit_size(packet) < 32
do
{:ok, %{state | leftover: packet}}
end
# Leftover from previous call available. Prepend to this packet:
defp process_bytes(%{leftover: leftover} = state, packet)
when bit_size(leftover) > 0
do
process_bytes(%{state | leftover: <<>>}, <<leftover::bits, packet::bits>>)
end
# Not synced, found a sync word. Create a new frame struct:
defp process_bytes(%{current_frame: nil} = state, <<@sync_word::size(11), header::size(21), rest::bits>>) do
2018-04-18 19:48:09 +00:00
frame = <<@sync_word::size(11), header::size(21)>> |> Frame.from_header
2018-04-17 09:42:54 +00:00
process_bytes(%{state | current_frame: frame}, rest)
end
# Not synced, no sync word found. Discard a byte:
defp process_bytes(%{current_frame: nil} = state, packet) do
<<_byte, rest::bits>> = packet
process_bytes(state, rest)
end
# Synced, but with an invalid header. Discard a byte:
defp process_bytes(%{current_frame: %Frame{valid: false}} = state, packet) do
2018-04-18 19:48:09 +00:00
<<_byte, rest::bits>> = <<state.current_frame.data, packet::bits>>
2018-04-17 09:42:54 +00:00
process_bytes(%{state | current_frame: nil}, rest)
end
# Synced, current frame not complete and we have bytes available. Add bytes to frame:
2018-04-19 10:03:50 +00:00
defp process_bytes(state, packet) do
2018-04-17 09:42:54 +00:00
{:ok, frame, rest} = Frame.add_bytes(state.current_frame, packet)
process_bytes(%{state | current_frame: frame}, rest)
end
end