Packages
membrane_h264_ffmpeg_plugin
0.12.2
0.32.7
0.32.6
0.32.5
0.32.4
0.32.3
0.32.2
0.32.1
0.32.0
0.31.8
0.31.7
0.31.6
0.31.5
0.31.4
0.31.3
0.31.2
0.31.1
0.31.0
0.30.1
0.30.0
0.29.0
0.28.2
0.28.1
0.28.0
0.27.0
0.26.2
0.26.1
0.26.0
0.25.4
0.25.3
0.25.2
0.25.1
0.25.0
0.24.0
0.23.0
0.22.1
0.22.0
0.21.6
0.21.5
0.21.4
0.21.3
0.21.2
0.21.1
0.21.0
0.20.1
0.20.0
0.19.0
0.18.0
0.17.0
0.16.3
0.16.2
0.16.1
0.16.0
0.15.0
0.14.0
0.13.3
0.13.2
0.13.1
0.13.0
0.12.2
0.12.1
0.12.0
0.11.0
0.10.0
0.9.0
0.8.0
0.7.0
0.6.0
0.5.0
Membrane H264 decoder and encoder based on FFmpeg and x264
Current section
Files
Jump to
Current section
Files
lib/membrane_h264_ffmpeg/parser/nalu.ex
defmodule Membrane.H264.FFmpeg.Parser.NALu do
@moduledoc false
# See https://yumichan.net/video-processing/video-compression/introduction-to-h264-nal-unit/
@nalu_types %{
0 => :unspecified,
1 => :non_idr,
2 => :part_a,
3 => :part_b,
4 => :part_c,
5 => :idr,
6 => :sei,
7 => :sps,
8 => :pps,
9 => :aud,
10 => :end_of_seq,
11 => :end_of_stream,
12 => :filler_data,
13 => :sps_extension,
14 => :prefix_nal_unit,
15 => :subset_sps,
(16..18) => :reserved,
19 => :auxiliary_non_part,
20 => :extension,
(21..23) => :reserved,
(24..31) => :unspecified
}
|> Enum.flat_map(fn
{k, v} when is_integer(k) -> [{k, v}]
{k, v} -> Enum.map(k, &{&1, v})
end)
|> Map.new()
@spec parse(binary) :: {list, %{h264: any}}
def parse(access_unit) do
{nalus, au_info} =
access_unit
|> extract_nalus()
|> Enum.map_reduce(%{key_frame?: false}, &parse_nalu(&1, &2, access_unit))
nalus =
nalus
|> List.update_at(0, &put_in(&1, [:metadata, :h264, :new_access_unit], au_info))
|> List.update_at(-1, &put_in(&1, [:metadata, :h264, :end_access_unit], true))
{nalus, %{h264: au_info}}
end
defp extract_nalus(access_unit) do
access_unit
|> :binary.matches([<<0, 0, 0, 1>>, <<0, 0, 1>>])
|> Enum.chunk_every(2, 1, [{byte_size(access_unit), nil}])
|> Enum.map(fn [{from, prefix_len}, {to, _}] ->
len = to - from
%{prefixed_poslen: {from, len}, unprefixed_poslen: {from + prefix_len, len - prefix_len}}
end)
end
defp parse_nalu(nalu, access_unit_info, access_unit) do
<<0::1, _nal_ref_idc::unsigned-integer-size(2), nal_unit_type::unsigned-integer-size(5),
_rest::bitstring>> = :binary.part(access_unit, nalu.unprefixed_poslen)
type = @nalu_types |> Map.fetch!(nal_unit_type)
new_au_info = if type == :idr, do: %{key_frame?: true}, else: %{}
nalu = Map.put(nalu, :metadata, %{h264: %{type: type}})
{nalu, Map.merge(access_unit_info, new_au_info)}
end
end