Current section

Files

Jump to
membrane_rtc_engine lib membrane_rtc_engine endpoints webrtc vp8_munger.ex
Raw

lib/membrane_rtc_engine/endpoints/webrtc/vp8_munger.ex

defmodule Membrane.RTC.Engine.Endpoint.WebRTC.VP8Munger do
@moduledoc false
# Module responsible for rewriting VP8 RTP payload fields
# to provide transparent switch between simulcast encodings.
import Bitwise
alias Membrane.Buffer
alias Membrane.RTP.VP8
@type t() :: %__MODULE__{
pic_id_used: boolean(),
last_pic_id: integer(),
pic_id_offset: integer(),
tl0picidx_used: boolean(),
last_tl0picidx: integer(),
tl0picidx_offset: integer(),
keyidx_used: boolean(),
last_keyidx: integer(),
keyidx_offset: integer()
}
defstruct pic_id_used: false,
last_pic_id: 0,
pic_id_offset: 0,
tl0picidx_used: false,
last_tl0picidx: 0,
tl0picidx_offset: 0,
keyidx_used: false,
last_keyidx: 0,
keyidx_offset: 0
@spec new() :: t()
def new() do
%__MODULE__{}
end
@spec init(t(), Membrane.Buffer.t()) :: t()
def init(vp8_munger, buffer) do
{:ok, {payload_descriptor, _payload}} =
VP8.PayloadDescriptor.parse_payload_descriptor(buffer.payload)
%VP8.PayloadDescriptor{
i: i,
keyidx: keyidx,
l: l,
picture_id: pic_id,
k: k,
tl0picidx: tl0picidx
} = payload_descriptor
pic_id_used = i == 1
tl0picidx_used = l == 1
keyidx_used = k == 1
last_pic_id = if pic_id_used, do: pic_id, else: 0
last_tl0picidx = if tl0picidx_used, do: tl0picidx, else: 0
last_keyidx = if keyidx_used, do: keyidx, else: 0
%__MODULE__{
vp8_munger
| pic_id_used: pic_id_used,
last_pic_id: last_pic_id,
tl0picidx_used: tl0picidx_used,
last_tl0picidx: last_tl0picidx,
keyidx_used: keyidx_used,
last_keyidx: last_keyidx
}
end
@spec update(t(), Membrane.Buffer.t()) :: t()
def update(vp8_munger, buffer) do
{:ok, {payload_descriptor, _payload}} =
VP8.PayloadDescriptor.parse_payload_descriptor(buffer.payload)
%VP8.PayloadDescriptor{
keyidx: keyidx,
picture_id: pic_id,
tl0picidx: tl0picidx
} = payload_descriptor
pic_id_offset = if vp8_munger.pic_id_used, do: pic_id - vp8_munger.last_pic_id - 1, else: 0
tl0picidx_offset =
if vp8_munger.tl0picidx_used, do: tl0picidx - vp8_munger.last_tl0picidx - 1, else: 0
keyidx_offset = if vp8_munger.keyidx_used, do: keyidx - vp8_munger.last_keyidx - 1, else: 0
%__MODULE__{
vp8_munger
| pic_id_offset: pic_id_offset,
tl0picidx_offset: tl0picidx_offset,
keyidx_offset: keyidx_offset
}
end
@spec munge(t(), Membrane.Buffer.t()) :: {t(), Membrane.Buffer.t()}
def munge(vp8_munger, %Membrane.Buffer{payload: <<>>} = buffer), do: {vp8_munger, buffer}
def munge(vp8_munger, buffer) do
{:ok, {payload_descriptor, vp8_payload}} =
VP8.PayloadDescriptor.parse_payload_descriptor(buffer.payload)
%VP8.PayloadDescriptor{
keyidx: keyidx,
picture_id: pic_id,
tl0picidx: tl0picidx
} = payload_descriptor
munged_pic_id = rem(pic_id + (1 <<< 15) - vp8_munger.pic_id_offset, 1 <<< 15)
munged_tl0picidx = rem(tl0picidx + (1 <<< 8) - vp8_munger.tl0picidx_offset, 1 <<< 8)
munged_keyidx = rem(keyidx + (1 <<< 5) - vp8_munger.keyidx_offset, 1 <<< 5)
buffer =
%VP8.PayloadDescriptor{
payload_descriptor
| keyidx: munged_keyidx,
picture_id: munged_pic_id,
tl0picidx: munged_tl0picidx
}
|> VP8.PayloadDescriptor.serialize()
|> then(fn munged_payload_descriptor ->
%Buffer{buffer | payload: munged_payload_descriptor <> vp8_payload}
end)
vp8_munger = %__MODULE__{
vp8_munger
| last_pic_id: munged_pic_id,
last_tl0picidx: munged_tl0picidx,
last_keyidx: munged_keyidx
}
{vp8_munger, buffer}
end
end