Skip to content

Commit

Permalink
Merge pull request #24 from membraneframework/decoder-configuration-r…
Browse files Browse the repository at this point in the history
…ecord-parsing

Add support for remote stream's decoder configuration parsing
  • Loading branch information
Qizot committed Jun 1, 2023
2 parents 4d27b9b + 236fbf3 commit 13384f4
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 9 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ The package can be installed by adding `membrane_h264_plugin` to your list of de
```elixir
def deps do
[
{:membrane_h264_plugin, "~> 0.2.0"}
{:membrane_h264_plugin, "~> 0.3.0"}
]
end
```
Expand Down
44 changes: 37 additions & 7 deletions lib/membrane_h264_plugin/parser.ex
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ defmodule Membrane.H264.Parser do
alias Membrane.{Buffer, H264, RemoteStream}
alias Membrane.H264.Parser.{AUSplitter, Format, NALuParser, NALuSplitter}

alias __MODULE__.DecoderConfigurationRecord

def_input_pad :input,
demand_unit: :buffers,
demand_mode: :auto,
Expand Down Expand Up @@ -89,28 +91,56 @@ defmodule Membrane.H264.Parser do
profile: nil,
previous_timestamps: {nil, nil},
framerate: opts.framerate,
au_counter: 0
au_counter: 0,
frame_prefix: <<>>,
parameter_sets_present?: byte_size(opts.sps) > 0 or byte_size(opts.pps) > 0
}

{[], state}
end

@impl true
def handle_stream_format(:input, stream_format, _ctx, state) do
mode =
state =
case stream_format do
%RemoteStream{type: :bytestream} -> :bytestream
%H264.RemoteStream{alignment: :nalu} -> :nalu_aligned
%H264.RemoteStream{alignment: :au} -> :au_aligned
%RemoteStream{type: :bytestream} ->
%{state | mode: :bytestream}

%H264.RemoteStream{alignment: alignment, decoder_configuration_record: dcr} ->
mode =
case alignment do
:nalu -> :nalu_aligned
:au -> :au_aligned
end

frame_prefix =
if dcr do
if state.parameter_sets_present? do
raise "Parameter sets were already provided as the options to the parser and parameter sets from the decoder configuration record could overwrite them."
end

{:ok, %{sps: sps, pps: pps}} = DecoderConfigurationRecord.parse(dcr)

Enum.concat([[<<>>], sps, pps]) |> Enum.join(<<0, 0, 1>>)
else
<<>>
end

%{state | mode: mode, frame_prefix: frame_prefix}
end

state = %{state | mode: mode}
{[], state}
end

@impl true
def handle_process(:input, %Membrane.Buffer{} = buffer, _ctx, state) do
{nalus_payloads_list, nalu_splitter} = NALuSplitter.split(buffer.payload, state.nalu_splitter)
{payload, state} =
case state.frame_prefix do
<<>> -> {buffer.payload, state}
prefix -> {prefix <> buffer.payload, %{state | frame_prefix: <<>>}}
end

{nalus_payloads_list, nalu_splitter} = NALuSplitter.split(payload, state.nalu_splitter)

{nalus_payloads_list, nalu_splitter} =
if state.mode != :bytestream do
Expand Down
62 changes: 62 additions & 0 deletions lib/membrane_h264_plugin/parser/decoder_configuration_record.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
defmodule Membrane.H264.Parser.DecoderConfigurationRecord do
@moduledoc """
Utility functions for parsing AVC Configuration Record.
The structure of the record is described in section 5.2.4.1.1 of MPEG-4 part 15 (ISO/IEC 14496-15).
"""
@enforce_keys [
:sps,
:pps,
:avc_profile_indication,
:avc_level,
:profile_compatibility,
:length_size_minus_one
]
defstruct @enforce_keys

@typedoc "Structure representing the Decoder Configuartion Record"
@type t() :: %__MODULE__{
sps: [binary()],
pps: [binary()],
avc_profile_indication: non_neg_integer(),
profile_compatibility: non_neg_integer(),
avc_level: non_neg_integer(),
length_size_minus_one: non_neg_integer()
}

@doc """
Parses the DCR.
"""
@spec parse(binary()) :: {:ok, t()} | {:error, any()}
def parse(
<<1::8, avc_profile_indication::8, profile_compatibility::8, avc_level::8, 0b111111::6,
length_size_minus_one::2, 0b111::3, rest::bitstring>>
) do
{sps, rest} = parse_sps(rest)
{pps, _rest} = parse_pps(rest)

%__MODULE__{
sps: sps,
pps: pps,
avc_profile_indication: avc_profile_indication,
profile_compatibility: profile_compatibility,
avc_level: avc_level,
length_size_minus_one: length_size_minus_one
}
|> then(&{:ok, &1})
end

def parse(_data), do: {:error, :unknown_pattern}

defp parse_sps(<<num_of_sps::5, rest::bitstring>>) do
do_parse_array(num_of_sps, rest)
end

defp parse_pps(<<num_of_pps::8, rest::bitstring>>), do: do_parse_array(num_of_pps, rest)

defp do_parse_array(amount, rest, acc \\ [])
defp do_parse_array(0, rest, acc), do: {Enum.reverse(acc), rest}

defp do_parse_array(remaining, <<size::16, data::binary-size(size), rest::bitstring>>, acc),
do: do_parse_array(remaining - 1, rest, [data | acc])
end
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
defmodule Membrane.H264.TODO.Mixfile do
use Mix.Project

@version "0.2.0"
@version "0.3.0"
@github_url "https://github.com/membraneframework-labs/membrane_h264_plugin"

def project do
Expand Down

0 comments on commit 13384f4

Please sign in to comment.