Current section
Files
Jump to
Current section
Files
lib/muxer/h264_parser.ex
defmodule Membrane.MPEGTS.Muxer.H264Parser do
@moduledoc false
alias Membrane.H264.AUSplitter
alias Membrane.H264.NALuParser
alias Membrane.H26x.NALuSplitter
@aud <<0x00, 0x00, 0x00, 0x01, 0x09, 0x16>>
@type t :: %{
nalu_splitter: NALuSplitter.t(),
nalu_parser: Membrane.H26x.NALuParser.t(),
au_splitter: AUSplitter.t()
}
@doc """
Returns a new instance of the H264 Parser.
"""
@spec new() :: t()
def new() do
%{
nalu_splitter: NALuSplitter.new(),
nalu_parser: NALuParser.new(),
au_splitter: AUSplitter.new()
}
end
@doc """
Parses an incoming H264 payload and returns a list of access units
along the updated state of the parser.
Please note that the parser is allowed to buffer incoming data.
You can call: ``#{inspect(__MODULE__)}.flush/1` to flush out the buffers data (e.g.
when the stream ends)
"""
@spec parse(binary(), t()) :: {[binary()], t()}
def parse(payload, state) do
{nalu_payloads, nalu_splitter} = NALuSplitter.split(payload, state.nalu_splitter)
{nalus, nalu_parser} = NALuParser.parse_nalus(nalu_payloads, state.nalu_parser)
{aus, au_splitter} = AUSplitter.split(nalus, state.au_splitter)
aus = join_nalus_in_aus(aus)
{aus,
%{state | nalu_splitter: nalu_splitter, nalu_parser: nalu_parser, au_splitter: au_splitter}}
end
@doc """
Flushes out the data buffered in the internal state of the parser.
"""
@spec flush(t()) :: {[binary()], t()}
def flush(state) do
{nalu_payloads, nalu_splitter} = NALuSplitter.split(<<>>, true, state.nalu_splitter)
{nalus, nalu_parser} = NALuParser.parse_nalus(nalu_payloads, state.nalu_parser)
{aus, au_splitter} = AUSplitter.split(nalus, true, state.au_splitter)
aus = join_nalus_in_aus(aus)
{aus,
%{state | nalu_splitter: nalu_splitter, nalu_parser: nalu_parser, au_splitter: au_splitter}}
end
@doc """
Adds an Access Unit Delimeter NAL unit if it's not present at
the beginning of the binary.
Please note that this function assumes that the provided payload
is a single Access Unit.
"""
@spec maybe_add_aud(binary()) :: binary()
def maybe_add_aud(au) do
if starts_with_aud(au), do: au, else: @aud <> au
end
defp starts_with_aud(@aud <> _rest), do: true
defp starts_with_aud(_payload), do: false
defp join_nalus_in_aus(aus) do
annexb_prefix = <<0, 0, 0, 1>>
Enum.map(aus, fn au ->
%{
payload: Enum.map_join(au, &(annexb_prefix <> &1.payload)),
is_keyframe: Enum.any?(au, &(&1.type == :idr))
}
end)
end
end