Packages
membrane_rtp_plugin
0.22.0
0.31.5
0.31.4
0.31.3
0.31.2
0.31.1
0.31.0
0.30.0
0.29.1
0.29.0
0.28.0
0.27.1
0.27.0
0.26.0
0.25.0
0.24.1
0.24.0
0.23.2
0.23.1
0.23.0
0.22.1
0.22.0
0.21.0
0.20.0
0.19.1
0.19.0
0.18.0
0.17.1
0.17.0
0.16.0
0.15.0
0.15.0-rc.1
0.14.0
0.13.0
0.12.2
0.12.1
0.12.0
0.11.0
0.10.0
0.9.0
0.8.2
0.8.1
0.8.0
0.7.1-alpha.3
0.7.1-alpha.2
0.7.0-alpha.2
0.7.0-alpha.1
0.7.0-alpha
0.6.1
0.6.0
0.5.1
0.5.0
0.4.0-alpha
Membrane Multimedia Framework plugin for RTP
Current section
Files
Jump to
Current section
Files
lib/membrane/rtp/parser.ex
defmodule Membrane.RTP.Parser do
@moduledoc """
Identifies RTP/RTCP packets, then tries to parse RTP packet (parsing header and preparing payload)
and forwards RTCP packet to `:rtcp_output` pad unchanged.
## Encrypted packets
In case of SRTP/SRTCP the parser tries to parse just the header of the RTP packet as the packet's payload
is encrypted and must be passed as a whole to the decryptor. The whole packet remains unchanged but
the parsed header gets attached to `Membrane.Buffer`'s metadata.
SRTP is treated the same as RTCP and all packets gets forwarded to `:rtcp_output` pad.
## Parsed packets
In both cases, encrypted and unencryptd, parsed header is put into the metadata field in `Membrane.Buffer` under `:rtp` key.
with the following metadata `:timestamp`, `:sequence_number`, `:ssrc`, `:payload_type`,
`:marker`, `:extension`. See `Membrane.RTP.Header` for their meaning and specifications.
"""
use Membrane.Filter
require Membrane.Logger
alias Membrane.Buffer
alias Membrane.{RemoteStream, RTCP, RTCPEvent, RTP}
@metadata_fields [
:timestamp,
:sequence_number,
:ssrc,
:csrcs,
:payload_type,
:marker,
:extensions
]
def_options secure?: [
type: :boolean,
default: false,
description: """
Specifies whether Parser should expect packets that are encrypted or not.
Requires adding [srtp](https://github.com/membraneframework/elixir_libsrtp) dependency to work.
"""
]
def_input_pad :input,
accepted_format:
%RemoteStream{type: :packetized, content_format: cf} when cf in [nil, RTP, RTCP],
demand_mode: :auto
def_output_pad :output, accepted_format: RTP, demand_mode: :auto
def_output_pad :rtcp_output,
mode: :push,
accepted_format: %RemoteStream{content_format: RTCP, type: :packetized},
availability: :on_request
@impl true
def handle_init(_ctx, opts) do
{[], %{rtcp_output_pad: nil, secure?: opts.secure?}}
end
@impl true
def handle_stream_format(:input, _stream_format, _ctx, state) do
{[stream_format: {:output, %RTP{}}], state}
end
@impl true
def handle_pad_added(Pad.ref(:rtcp_output, _ref) = pad, %{playback_state: :playing}, state) do
actions = [stream_format: {pad, %RemoteStream{content_format: RTCP, type: :packetized}}]
{actions, %{state | rtcp_output_pad: pad}}
end
@impl true
def handle_pad_added(Pad.ref(:rtcp_output, _ref) = pad, _ctx, state) do
{[], %{state | rtcp_output_pad: pad}}
end
@impl true
def handle_playing(_ctx, %{rtcp_output_pad: pad} = state) when pad != nil do
actions = [stream_format: {pad, %RemoteStream{content_format: RTCP, type: :packetized}}]
{actions, state}
end
@impl true
def handle_playing(ctx, state) do
super(ctx, state)
end
@impl true
def handle_process(:input, %Buffer{payload: payload, metadata: metadata} = buffer, _ctx, state) do
with :rtp <- RTP.Packet.identify(payload),
{:ok,
%{packet: packet, padding_size: padding_size, total_header_size: total_header_size}} <-
RTP.Packet.parse(payload, state.secure?) do
%RTP.Packet{payload: payload, header: header} = packet
rtp =
header
|> Map.take(@metadata_fields)
|> Map.merge(%{padding_size: padding_size, total_header_size: total_header_size})
metadata = Map.put(metadata, :rtp, rtp)
{[buffer: {:output, %Buffer{payload: payload, metadata: metadata}}], state}
else
:rtcp ->
case state.rtcp_output_pad do
nil -> {[], state}
pad -> {[buffer: {pad, buffer}], state}
end
{:error, reason} ->
Membrane.Logger.debug("""
Couldn't parse rtp packet:
#{inspect(payload, limit: :infinity)}
Reason: #{inspect(reason)}. Ignoring packet.
""")
{[], state}
end
end
@impl true
def handle_event(:output, %RTCPEvent{} = event, _ctx, state) do
case state.rtcp_output_pad do
nil ->
{[], state}
pad ->
{[event: {pad, event}], state}
end
end
@impl true
def handle_event(pad, event, ctx, state), do: super(pad, event, ctx, state)
end