defmodule Membrane.RTP.VP8.PayloadDescriptor do @moduledoc """ Defines a structure representing VP8 payload descriptor Described in details under following link: https://tools.ietf.org/html/rfc7741#section-4.2 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ |X|R|N|S|R| PID | (REQUIRED) |X|R|N|S|R| PID | (REQUIRED) +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ X: |I|L|T|K| RSV | (OPTIONAL) X: |I|L|T|K| RSV | (OPTIONAL) +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ I: |M| PictureID | (OPTIONAL) I: |M| PictureID | (OPTIONAL) +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ L: | TL0PICIDX | (OPTIONAL) | PictureID | +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ T/K: |TID|Y| KEYIDX | (OPTIONAL) L: | TL0PICIDX | (OPTIONAL) +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ T/K: |TID|Y| KEYIDX | (OPTIONAL) +-+-+-+-+-+-+-+-+ Structure with single octet picture ID on the left and structure with extended picture id on the right (when M bit is set) """ @type first_octet :: binary() @type partition_index :: 0..7 @type picture_id :: 0..32_767 @type tl0picidx :: 0..255 @type bit :: 0..1 @type tid :: 0..3 @type keyidx :: 0..31 @type t :: %__MODULE__{ x: bit(), n: bit(), s: bit(), partition_index: partition_index(), i: bit(), l: bit(), t: bit(), k: bit(), picture_id: picture_id(), tl0picidx: tl0picidx(), tid: tid(), y: bit(), keyidx: keyidx() } @enforce_keys [:x, :n, :s, :partition_index] defstruct @enforce_keys ++ [ i: 0, l: 0, t: 0, k: 0, m: 0, picture_id: 0, tl0picidx: 0, tid: 0, y: 0, keyidx: 0 ] @spec serialize(__MODULE__.t()) :: binary() def serialize(payload_descriptor) do %__MODULE__{ x: x, n: n, s: s, partition_index: partition_index, i: i, l: l, t: t, k: k, m: m, picture_id: picture_id, tl0picidx: tl0picidx, tid: tid, y: y, keyidx: keyidx } = payload_descriptor xnspid = <> iltk = if x == 1, do: <>, else: <<>> picture_id = case {i, m} do {1, 0} -> <> {1, 1} -> <> {0, _} -> <<>> end tl0picidx = if l == 1, do: <>, else: <<>> tidykeyidx = if t == 1 or k == 1, do: <>, else: <<>> xnspid <> iltk <> picture_id <> tl0picidx <> tidykeyidx end @spec parse_payload_descriptor(binary()) :: {:error, :malformed_data | :payload_too_short} | {:ok, {t(), binary()}} def parse_payload_descriptor(payload) when byte_size(payload) <= 1, do: {:error, :payload_too_short} def parse_payload_descriptor( <> ) when byte_size(rest) > 0 do descriptor_acc = %__MODULE__{x: x, n: n, s: s, partition_index: partition_index} with {:ok, {descriptor_acc, rest}} <- get_extended_control_bits(descriptor_acc, rest), {:ok, {descriptor_acc, rest}} <- get_picture_id(descriptor_acc, rest), {:ok, {descriptor_acc, rest}} <- get_temporal_level_zero_index(descriptor_acc, rest), {:ok, {descriptor_acc, rest}} <- get_tidykeyidx(descriptor_acc, rest) do {:ok, {descriptor_acc, rest}} else {:error, reason} -> {:error, reason} end end def parse_payload_descriptor(_payload), do: {:error, :malformed_data} defp get_extended_control_bits(%__MODULE__{x: 0} = descriptor_acc, rest), do: {:ok, {descriptor_acc, rest}} defp get_extended_control_bits(_descriptor_acc, <<_iltk::4, rsv::4, _rest::binary()>>) when rsv > 0, do: {:error, :malformed_data} defp get_extended_control_bits( descriptor_acc, <> ) when byte_size(rest) > 0 do <> = extended_control_bits {:ok, {%__MODULE__{descriptor_acc | i: i, l: l, t: t, k: k}, rest}} end defp get_extended_control_bits(_descriptor_acc, _rest), do: {:error, :payload_too_short} defp get_picture_id(%__MODULE__{i: 0} = descriptor_acc, rest), do: {:ok, {descriptor_acc, rest}} defp get_picture_id(descriptor_acc, <<0::1, picture_id::7, rest::binary()>>) when byte_size(rest) > 0, do: {:ok, {%__MODULE__{descriptor_acc | m: 0, picture_id: picture_id}, rest}} defp get_picture_id(descriptor_acc, <<1::1, picture_id::15, rest::binary()>>) when byte_size(rest) > 0, do: {:ok, {%__MODULE__{descriptor_acc | m: 1, picture_id: picture_id}, rest}} defp get_picture_id(_descriptor_acc, _rest), do: {:error, :payload_too_short} defp get_temporal_level_zero_index(%__MODULE__{l: 0} = descriptor_acc, rest), do: {:ok, {descriptor_acc, rest}} defp get_temporal_level_zero_index(descriptor_acc, <>) when byte_size(rest) > 0, do: {:ok, {%__MODULE__{descriptor_acc | tl0picidx: tl0picidx}, rest}} defp get_tidykeyidx(%__MODULE__{t: t, k: k} = descriptor_acc, rest) when t == 1 or k == 1 do case byte_size(rest) do 1 -> {:error, :payload_to_short} _greater_than_one -> <> = rest {:ok, {%__MODULE__{descriptor_acc | tid: tid, y: y, keyidx: keyidx}, rest}} end end defp get_tidykeyidx(descriptor_acc, rest), do: {:ok, {descriptor_acc, rest}} end