From 0ac5373fc78974f5a02930cc365e4868e2e043fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Per=C5=BCy=C5=82o?= Date: Tue, 24 Aug 2021 15:27:27 +0200 Subject: [PATCH 01/20] Initial jitter buffer removal --- lib/membrane/rtcp/receiver.ex | 4 +- lib/membrane/rtp/serializer.ex | 39 +++++++++-- lib/membrane/rtp/session_bin.ex | 96 ++++++++++++++++---------- lib/membrane/rtp/stream_receive_bin.ex | 40 ++++++----- lib/membrane/rtp/stream_send_bin.ex | 20 +++--- 5 files changed, 132 insertions(+), 67 deletions(-) diff --git a/lib/membrane/rtcp/receiver.ex b/lib/membrane/rtcp/receiver.ex index 4d75845e..4f291985 100644 --- a/lib/membrane/rtcp/receiver.ex +++ b/lib/membrane/rtcp/receiver.ex @@ -46,7 +46,9 @@ defmodule Membrane.RTCP.Receiver do @impl true def handle_tick(:report_timer, _ctx, state) do - {{:ok, event: {:output, %RTP.JitterBuffer.StatsRequestEvent{}}}, state} + # TODO: do something with me.... + # {{:ok, event: {:output, %RTP.JitterBuffer.StatsRequestEvent{}}}, state} + {:ok, state} end @impl true diff --git a/lib/membrane/rtp/serializer.ex b/lib/membrane/rtp/serializer.ex index 566ffbe3..b9bcb76b 100644 --- a/lib/membrane/rtp/serializer.ex +++ b/lib/membrane/rtp/serializer.ex @@ -25,6 +25,17 @@ defmodule Membrane.RTP.Serializer do Number of bytes that each packet should be aligned to. Alignment is achieved by adding RTP padding. """ + ], + rewrite_rtp_header?: [ + spec: boolean(), + default: true, + describer: """ + Determines whether packet's header should get rewritten or stay unchanged. + + One may need to rewrite packet'ss header to adjust packet's timestamp + and sequence number so that when previously combined with jitter buffer the outgoing + RTP stream will seem to be continuous. + """ ] defmodule State do @@ -34,6 +45,7 @@ defmodule Membrane.RTP.Serializer do defstruct sequence_number: 0, init_timestamp: 0, any_buffer_sent?: false, + rewrite_rtp_header?: true, stats_acc: %{ clock_rate: 0, timestamp: 0, @@ -46,6 +58,7 @@ defmodule Membrane.RTP.Serializer do sequence_number: non_neg_integer(), init_timestamp: non_neg_integer(), any_buffer_sent?: boolean(), + rewrite_rtp_header?: boolean(), stats_acc: %{} } end @@ -54,7 +67,8 @@ defmodule Membrane.RTP.Serializer do def handle_init(options) do state = %State{ sequence_number: Enum.random(0..@max_seq_num), - init_timestamp: Enum.random(0..@max_timestamp) + init_timestamp: Enum.random(0..@max_timestamp), + rewrite_rtp_header?: options.rewrite_rtp_header? } state = state |> put_in([:stats_acc, :clock_rate], options.clock_rate) @@ -77,16 +91,15 @@ defmodule Membrane.RTP.Serializer do state = update_counters(buffer, state) {rtp_metadata, metadata} = Map.pop(metadata, :rtp, %{}) - %{timestamp: timestamp} = metadata - rtp_offset = timestamp |> Ratio.mult(state.clock_rate) |> Membrane.Time.to_seconds() - rtp_timestamp = rem(state.init_timestamp + rtp_offset, @max_timestamp + 1) + + {rtp_timestamp, sequence_number, state} = maybe_rewrite_params(rtp_metadata, state) header = %RTP.Header{ ssrc: state.ssrc, marker: Map.get(rtp_metadata, :marker, false), payload_type: state.payload_type, timestamp: rtp_timestamp, - sequence_number: state.sequence_number, + sequence_number: sequence_number, csrcs: Map.get(rtp_metadata, :csrcs, []), extension: Map.get(rtp_metadata, :extension) } @@ -125,4 +138,20 @@ defmodule Membrane.RTP.Serializer do ) |> update_in([:stats_acc, :sender_packet_count], &(&1 + 1)) end + + defp maybe_rewrite_params( + %{timestamp: timestamp, sequence_number: sequence_number}, + %{rewrite_rtp_header?: false} = state + ) do + {timestamp, sequence_number, state} + end + + defp maybe_rewrite_params(%{timestamp: timestamp}, %{rewrite_rtp_header?: true} = state) do + rtp_offset = timestamp |> Ratio.mult(state.clock_rate) |> Membrane.Time.to_seconds() + rtp_timestamp = rem(state.init_timestamp + rtp_offset, @max_timestamp + 1) + + state = Map.update!(state, :sequence_number, &rem(&1 + 1, @max_seq_num + 1)) + + {rtp_timestamp, state.sequence_number, state} + end end diff --git a/lib/membrane/rtp/session_bin.ex b/lib/membrane/rtp/session_bin.ex index 7bd05d64..cd39d3b8 100644 --- a/lib/membrane/rtp/session_bin.ex +++ b/lib/membrane/rtp/session_bin.ex @@ -33,6 +33,9 @@ defmodule Membrane.RTP.SessionBin do require Bitwise require Membrane.Logger + # TODO: remove me, just for testing... + @use_jitter_buffer true + @type new_stream_notification_t :: Membrane.RTP.SSRCRouter.new_stream_notification_t() @typedoc """ @@ -132,7 +135,19 @@ defmodule Membrane.RTP.SessionBin do |> Enum.find(&(&1 not in local_ssrcs and &1 not in remote_ssrcs)) end - def_input_pad :input, demand_unit: :buffers, caps: :any, availability: :on_request + def_input_pad :input, + demand_unit: :buffers, + caps: :any, + availability: :on_request, + options: [ + use_payloader?: [ + spec: boolean(), + default: @use_jitter_buffer, + description: """ + Defines whether paylaoder should be used for incoming stream. + """ + ] + ] def_input_pad :rtp_input, demand_unit: :buffers, @@ -151,6 +166,20 @@ defmodule Membrane.RTP.SessionBin do Encoding name determining depayloader which will be used to produce output stream from RTP stream. """ ], + use_depayloader?: [ + spec: boolean(), + default: @use_jitter_buffer, + description: """ + Defines whether the incoming stream should be depayloaded from RTP stream. + """ + ], + use_jitter_buffer?: [ + spec: boolean(), + default: @use_jitter_buffer, + description: """ + Defines whether jitter buffer should be used for the incoming RTP stream. + """ + ], clock_rate: [ spec: integer() | nil, default: nil, @@ -214,6 +243,13 @@ defmodule Membrane.RTP.SessionBin do description: """ Clock rate to use. If not provided, determined from `:payload_type`. """ + ], + use_payloader?: [ + spec: boolean(), + default: @use_jitter_buffer, + description: """ + Defines whether paylaoder should be used for incoming stream. + """ ] ] @@ -307,6 +343,8 @@ defmodule Membrane.RTP.SessionBin do def handle_pad_added(Pad.ref(:output, ssrc) = pad, ctx, state) do %{ encoding: encoding_name, + use_depayloader?: use_depayloader?, + use_jitter_buffer?: use_jitter_buffer?, clock_rate: clock_rate, extensions: extensions, rtcp_fir_interval: fir_interval, @@ -317,22 +355,23 @@ defmodule Membrane.RTP.SessionBin do encoding_name = encoding_name || get_from_register!(:encoding_name, payload_type, state) clock_rate = clock_rate || get_from_register!(:clock_rate, payload_type, state) - depayloader = get_depayloader!(encoding_name, state) + depayloader = if use_depayloader?, do: get_depayloader!(encoding_name, state), else: nil {local_ssrc, state} = add_ssrc(ssrc, state) rtp_stream_name = {:stream_receive_bin, ssrc} new_children = %{ rtp_stream_name => %RTP.StreamReceiveBin{ - filters: filters, - srtp_policies: state.srtp_policies, - secure?: state.secure?, + clock_rate: clock_rate, depayloader: depayloader, + filters: filters, local_ssrc: local_ssrc, remote_ssrc: ssrc, - clock_rate: clock_rate, + rtcp_fir_interval: fir_interval, rtcp_report_interval: state.rtcp_report_interval, - rtcp_fir_interval: fir_interval + secure?: state.secure?, + srtp_policies: state.srtp_policies, + use_jitter_buffer?: use_jitter_buffer? } } @@ -380,11 +419,16 @@ defmodule Membrane.RTP.SessionBin do {:ok, state} else pad = Pad.ref(:rtp_output, ssrc) - %{encoding: encoding_name, clock_rate: clock_rate} = ctx.pads[pad].options + + %{encoding: encoding_name, clock_rate: clock_rate, use_payloader?: use_payloader?} = + ctx.pads[pad].options + payload_type = get_output_payload_type!(ctx, ssrc) encoding_name = encoding_name || get_from_register!(:encoding_name, payload_type, state) clock_rate = clock_rate || get_from_register!(:clock_rate, payload_type, state) - payloader = get_payloader!(encoding_name, state) + payloader = if use_payloader?, do: get_payloader!(encoding_name, state), else: nil + IO.inspect("PAYLOADER #{inspect(payloader)}", label: :payloader) + spec = sent_stream_spec(ssrc, payload_type, payloader, clock_rate, state) state = %{state | senders_ssrcs: MapSet.put(state.senders_ssrcs, ssrc)} @@ -393,46 +437,24 @@ defmodule Membrane.RTP.SessionBin do end defp sent_stream_spec(ssrc, payload_type, payloader, clock_rate, %{ - secure?: true, + secure?: secure?, srtp_policies: policies }) do - children = %{ - {:stream_send_bin, ssrc} => %RTP.StreamSendBin{ - ssrc: ssrc, - payload_type: payload_type, - payloader: payloader, - clock_rate: clock_rate - }, - {:srtp_encryptor, ssrc} => %SRTP.Encryptor{policies: policies} - } + maybe_link_encryptor = &to(&1, {:srtp_encryptor, ssrc}, %SRTP.Encryptor{policies: policies}) links = [ link_bin_input(Pad.ref(:input, ssrc)) - |> to({:stream_send_bin, ssrc}) - |> to({:srtp_encryptor, ssrc}) - |> to_bin_output(Pad.ref(:rtp_output, ssrc)) - ] - - %ParentSpec{children: children, links: links} - end - - defp sent_stream_spec(ssrc, payload_type, payloader, clock_rate, %{secure?: false}) do - children = %{ - {:stream_send_bin, ssrc} => %RTP.StreamSendBin{ + |> to({:stream_send_bin, ssrc}, %RTP.StreamSendBin{ ssrc: ssrc, payload_type: payload_type, payloader: payloader, clock_rate: clock_rate - } - } - - links = [ - link_bin_input(Pad.ref(:input, ssrc)) - |> to({:stream_send_bin, ssrc}) + }) + |> then(if secure?, do: maybe_link_encryptor, else: & &1) |> to_bin_output(Pad.ref(:rtp_output, ssrc)) ] - %ParentSpec{children: children, links: links} + %ParentSpec{links: links} end @impl true diff --git a/lib/membrane/rtp/stream_receive_bin.ex b/lib/membrane/rtp/stream_receive_bin.ex index 0485b876..94a9aaaa 100644 --- a/lib/membrane/rtp/stream_receive_bin.ex +++ b/lib/membrane/rtp/stream_receive_bin.ex @@ -1,6 +1,9 @@ defmodule Membrane.RTP.StreamReceiveBin do @moduledoc """ This bin gets a parsed RTP stream on input and outputs raw media stream. + + If + Its responsibility is to depayload the RTP stream and compensate the jitter. """ @@ -22,7 +25,14 @@ defmodule Membrane.RTP.StreamReceiveBin do spec: [Membrane.RTP.SessionBin.packet_filter_t()], default: [] ], - depayloader: [type: :module], + use_jitter_buffer?: [ + type: :boolean, + default: false + ], + depayloader: [ + spec: module() | nil, + default: nil + ], local_ssrc: [spec: Membrane.RTP.ssrc_t()], remote_ssrc: [spec: Membrane.RTP.ssrc_t()], rtcp_report_interval: [spec: Membrane.Time.t() | nil], @@ -33,32 +43,30 @@ defmodule Membrane.RTP.StreamReceiveBin do @impl true def handle_init(opts) do - children = %{ - rtcp_receiver: %Membrane.RTCP.Receiver{ - local_ssrc: opts.local_ssrc, - remote_ssrc: opts.remote_ssrc, - report_interval: opts.rtcp_report_interval, - fir_interval: opts.rtcp_fir_interval - }, - jitter_buffer: %Membrane.RTP.JitterBuffer{clock_rate: opts.clock_rate}, - depayloader: opts.depayloader - } - maybe_link_decryptor = &to(&1, :decryptor, %Membrane.SRTP.Decryptor{policies: opts.srtp_policies}) + maybe_link_jitter_buffer = + &to(&1, :jitter_buffer, %Membrane.RTP.JitterBuffer{clock_rate: opts.clock_rate}) + + maybe_link_depayloader = &to(&1, :depayloader, opts.depayloader) + links = [ link_bin_input() |> to_filters(opts.filters) - |> to(:rtcp_receiver) - |> to(:jitter_buffer) + |> to(:rtcp_receiver, %Membrane.RTCP.Receiver{ + local_ssrc: opts.local_ssrc, + remote_ssrc: opts.remote_ssrc, + report_interval: opts.rtcp_report_interval, + fir_interval: opts.rtcp_fir_interval + }) + |> then(if opts.use_jitter_buffer?, do: maybe_link_jitter_buffer, else: & &1) |> then(if opts.secure?, do: maybe_link_decryptor, else: & &1) - |> to(:depayloader) + |> then(if opts.depayloader != nil, do: maybe_link_depayloader, else: & &1) |> to_bin_output() ] spec = %ParentSpec{ - children: children, links: links } diff --git a/lib/membrane/rtp/stream_send_bin.ex b/lib/membrane/rtp/stream_send_bin.ex index 92877553..4554930f 100644 --- a/lib/membrane/rtp/stream_send_bin.ex +++ b/lib/membrane/rtp/stream_send_bin.ex @@ -9,24 +9,28 @@ defmodule Membrane.RTP.StreamSendBin do def_output_pad :output, caps: :any, demand_unit: :buffers - def_options payloader: [spec: module], + def_options payloader: [default: nil, spec: module], payload_type: [spec: RTP.payload_type_t()], ssrc: [spec: RTP.ssrc_t()], clock_rate: [spec: RTP.clock_rate_t()] @impl true def handle_init(opts) do - children = [ - payloader: opts.payloader, - serializer: %RTP.Serializer{ + maybe_link_payloader = &to(&1, :payloader, opts.payloader) + + links = [ + link_bin_input() + |> then(if opts.payloader != nil, do: maybe_link_payloader, else: & &1) + |> to(:serializer, %RTP.Serializer{ ssrc: opts.ssrc, payload_type: opts.payload_type, - clock_rate: opts.clock_rate - } + clock_rate: opts.clock_rate, + rewrite_rtp_header?: opts.payloader != nil + }) + |> to_bin_output() ] - links = [link_bin_input() |> to(:payloader) |> to(:serializer) |> to_bin_output()] - spec = %ParentSpec{children: children, links: links} + spec = %ParentSpec{links: links} {{:ok, spec: spec}, %{}} end From 32c774a0437261840867ef4ca571b8614160b374 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Per=C5=BCy=C5=82o?= Date: Thu, 26 Aug 2021 15:08:37 +0200 Subject: [PATCH 02/20] Changed jitter buffer, payloaders and depayloaders optional --- lib/membrane/rtcp/receiver.ex | 4 +-- lib/membrane/rtp/serializer.ex | 32 +++++++++++-------- lib/membrane/rtp/session_bin.ex | 20 +++++------- lib/membrane/rtp/stream_receive_bin.ex | 16 ++++------ lib/membrane/rtp/stream_send_bin.ex | 2 +- test/membrane/rtp/session_bin_test.exs | 10 ++++-- test/membrane/rtp/stream_receive_bin_test.exs | 4 +-- 7 files changed, 43 insertions(+), 45 deletions(-) diff --git a/lib/membrane/rtcp/receiver.ex b/lib/membrane/rtcp/receiver.ex index 4f291985..4d75845e 100644 --- a/lib/membrane/rtcp/receiver.ex +++ b/lib/membrane/rtcp/receiver.ex @@ -46,9 +46,7 @@ defmodule Membrane.RTCP.Receiver do @impl true def handle_tick(:report_timer, _ctx, state) do - # TODO: do something with me.... - # {{:ok, event: {:output, %RTP.JitterBuffer.StatsRequestEvent{}}}, state} - {:ok, state} + {{:ok, event: {:output, %RTP.JitterBuffer.StatsRequestEvent{}}}, state} end @impl true diff --git a/lib/membrane/rtp/serializer.ex b/lib/membrane/rtp/serializer.ex index b9bcb76b..9ca8497c 100644 --- a/lib/membrane/rtp/serializer.ex +++ b/lib/membrane/rtp/serializer.ex @@ -26,15 +26,15 @@ defmodule Membrane.RTP.Serializer do Alignment is achieved by adding RTP padding. """ ], - rewrite_rtp_header?: [ + generate_seq_num_and_timestamp?: [ spec: boolean(), default: true, - describer: """ - Determines whether packet's header should get rewritten or stay unchanged. + description: """ + Determines if sequence number and timestamp should be sequentially generated by the serializer. + If set to false then serializer will take already existing sequence number and timestamp from incoming buffer's metadata. - One may need to rewrite packet'ss header to adjust packet's timestamp - and sequence number so that when previously combined with jitter buffer the outgoing - RTP stream will seem to be continuous. + ## Note + Generation should be used only when incoming buffers are ensured to be ordered. """ ] @@ -45,7 +45,7 @@ defmodule Membrane.RTP.Serializer do defstruct sequence_number: 0, init_timestamp: 0, any_buffer_sent?: false, - rewrite_rtp_header?: true, + generate_seq_num_and_timestamp?: true, stats_acc: %{ clock_rate: 0, timestamp: 0, @@ -58,7 +58,7 @@ defmodule Membrane.RTP.Serializer do sequence_number: non_neg_integer(), init_timestamp: non_neg_integer(), any_buffer_sent?: boolean(), - rewrite_rtp_header?: boolean(), + generate_seq_num_and_timestamp?: boolean(), stats_acc: %{} } end @@ -68,7 +68,7 @@ defmodule Membrane.RTP.Serializer do state = %State{ sequence_number: Enum.random(0..@max_seq_num), init_timestamp: Enum.random(0..@max_timestamp), - rewrite_rtp_header?: options.rewrite_rtp_header? + generate_seq_num_and_timestamp?: options.generate_seq_num_and_timestamp? } state = state |> put_in([:stats_acc, :clock_rate], options.clock_rate) @@ -92,7 +92,7 @@ defmodule Membrane.RTP.Serializer do {rtp_metadata, metadata} = Map.pop(metadata, :rtp, %{}) - {rtp_timestamp, sequence_number, state} = maybe_rewrite_params(rtp_metadata, state) + {rtp_timestamp, sequence_number, state} = maybe_rewrite_params(rtp_metadata, metadata, state) header = %RTP.Header{ ssrc: state.ssrc, @@ -107,7 +107,6 @@ defmodule Membrane.RTP.Serializer do packet = %RTP.Packet{header: header, payload: payload} payload = RTP.Packet.serialize(packet, align_to: state.alignment) buffer = %Buffer{payload: payload, metadata: metadata} - state = Map.update!(state, :sequence_number, &rem(&1 + 1, @max_seq_num + 1)) state = %{ state @@ -141,17 +140,22 @@ defmodule Membrane.RTP.Serializer do defp maybe_rewrite_params( %{timestamp: timestamp, sequence_number: sequence_number}, - %{rewrite_rtp_header?: false} = state + _metadata, + %{generate_seq_num_and_timestamp?: false} = state ) do {timestamp, sequence_number, state} end - defp maybe_rewrite_params(%{timestamp: timestamp}, %{rewrite_rtp_header?: true} = state) do + defp maybe_rewrite_params( + _rtp_metadata, + %{timestamp: timestamp}, + %{generate_seq_num_and_timestamp?: true, sequence_number: seq_num} = state + ) do rtp_offset = timestamp |> Ratio.mult(state.clock_rate) |> Membrane.Time.to_seconds() rtp_timestamp = rem(state.init_timestamp + rtp_offset, @max_timestamp + 1) state = Map.update!(state, :sequence_number, &rem(&1 + 1, @max_seq_num + 1)) - {rtp_timestamp, state.sequence_number, state} + {rtp_timestamp, seq_num, state} end end diff --git a/lib/membrane/rtp/session_bin.ex b/lib/membrane/rtp/session_bin.ex index cd39d3b8..cd14cad6 100644 --- a/lib/membrane/rtp/session_bin.ex +++ b/lib/membrane/rtp/session_bin.ex @@ -33,9 +33,6 @@ defmodule Membrane.RTP.SessionBin do require Bitwise require Membrane.Logger - # TODO: remove me, just for testing... - @use_jitter_buffer true - @type new_stream_notification_t :: Membrane.RTP.SSRCRouter.new_stream_notification_t() @typedoc """ @@ -142,7 +139,7 @@ defmodule Membrane.RTP.SessionBin do options: [ use_payloader?: [ spec: boolean(), - default: @use_jitter_buffer, + default: true, description: """ Defines whether paylaoder should be used for incoming stream. """ @@ -168,16 +165,16 @@ defmodule Membrane.RTP.SessionBin do ], use_depayloader?: [ spec: boolean(), - default: @use_jitter_buffer, + default: true, description: """ Defines whether the incoming stream should be depayloaded from RTP stream. """ ], use_jitter_buffer?: [ spec: boolean(), - default: @use_jitter_buffer, + default: true, description: """ - Defines whether jitter buffer should be used for the incoming RTP stream. + Defines whether jitter buffer should be used for the incoming RTP stream. """ ], clock_rate: [ @@ -246,7 +243,7 @@ defmodule Membrane.RTP.SessionBin do ], use_payloader?: [ spec: boolean(), - default: @use_jitter_buffer, + default: true, description: """ Defines whether paylaoder should be used for incoming stream. """ @@ -362,16 +359,16 @@ defmodule Membrane.RTP.SessionBin do new_children = %{ rtp_stream_name => %RTP.StreamReceiveBin{ - clock_rate: clock_rate, depayloader: depayloader, + jitter_buffer: + if(use_jitter_buffer?, do: %Membrane.RTP.JitterBuffer{clock_rate: clock_rate}, else: nil), filters: filters, local_ssrc: local_ssrc, remote_ssrc: ssrc, rtcp_fir_interval: fir_interval, rtcp_report_interval: state.rtcp_report_interval, secure?: state.secure?, - srtp_policies: state.srtp_policies, - use_jitter_buffer?: use_jitter_buffer? + srtp_policies: state.srtp_policies } } @@ -427,7 +424,6 @@ defmodule Membrane.RTP.SessionBin do encoding_name = encoding_name || get_from_register!(:encoding_name, payload_type, state) clock_rate = clock_rate || get_from_register!(:clock_rate, payload_type, state) payloader = if use_payloader?, do: get_payloader!(encoding_name, state), else: nil - IO.inspect("PAYLOADER #{inspect(payloader)}", label: :payloader) spec = sent_stream_spec(ssrc, payload_type, payloader, clock_rate, state) state = %{state | senders_ssrcs: MapSet.put(state.senders_ssrcs, ssrc)} diff --git a/lib/membrane/rtp/stream_receive_bin.ex b/lib/membrane/rtp/stream_receive_bin.ex index 94a9aaaa..5c3243b6 100644 --- a/lib/membrane/rtp/stream_receive_bin.ex +++ b/lib/membrane/rtp/stream_receive_bin.ex @@ -2,8 +2,6 @@ defmodule Membrane.RTP.StreamReceiveBin do @moduledoc """ This bin gets a parsed RTP stream on input and outputs raw media stream. - If - Its responsibility is to depayload the RTP stream and compensate the jitter. """ @@ -12,8 +10,7 @@ defmodule Membrane.RTP.StreamReceiveBin do alias Membrane.ParentSpec - def_options clock_rate: [type: :integer, spec: Membrane.RTP.clock_rate_t()], - srtp_policies: [ + def_options srtp_policies: [ spec: [ExLibSRTP.Policy.t()], default: [] ], @@ -25,9 +22,9 @@ defmodule Membrane.RTP.StreamReceiveBin do spec: [Membrane.RTP.SessionBin.packet_filter_t()], default: [] ], - use_jitter_buffer?: [ - type: :boolean, - default: false + jitter_buffer: [ + spec: module() | struct() | nil, + default: nil ], depayloader: [ spec: module() | nil, @@ -46,8 +43,7 @@ defmodule Membrane.RTP.StreamReceiveBin do maybe_link_decryptor = &to(&1, :decryptor, %Membrane.SRTP.Decryptor{policies: opts.srtp_policies}) - maybe_link_jitter_buffer = - &to(&1, :jitter_buffer, %Membrane.RTP.JitterBuffer{clock_rate: opts.clock_rate}) + maybe_link_jitter_buffer = &to(&1, :jitter_buffer, opts.jitter_buffer) maybe_link_depayloader = &to(&1, :depayloader, opts.depayloader) @@ -60,7 +56,7 @@ defmodule Membrane.RTP.StreamReceiveBin do report_interval: opts.rtcp_report_interval, fir_interval: opts.rtcp_fir_interval }) - |> then(if opts.use_jitter_buffer?, do: maybe_link_jitter_buffer, else: & &1) + |> then(if opts.jitter_buffer != nil, do: maybe_link_jitter_buffer, else: & &1) |> then(if opts.secure?, do: maybe_link_decryptor, else: & &1) |> then(if opts.depayloader != nil, do: maybe_link_depayloader, else: & &1) |> to_bin_output() diff --git a/lib/membrane/rtp/stream_send_bin.ex b/lib/membrane/rtp/stream_send_bin.ex index 4554930f..3e0837fe 100644 --- a/lib/membrane/rtp/stream_send_bin.ex +++ b/lib/membrane/rtp/stream_send_bin.ex @@ -25,7 +25,7 @@ defmodule Membrane.RTP.StreamSendBin do ssrc: opts.ssrc, payload_type: opts.payload_type, clock_rate: opts.clock_rate, - rewrite_rtp_header?: opts.payloader != nil + generate_seq_num_and_timestamp?: opts.payloader != nil }) |> to_bin_output() ] diff --git a/test/membrane/rtp/session_bin_test.exs b/test/membrane/rtp/session_bin_test.exs index acae7984..d1ec269d 100644 --- a/test/membrane/rtp/session_bin_test.exs +++ b/test/membrane/rtp/session_bin_test.exs @@ -127,9 +127,11 @@ defmodule Membrane.RTP.Session.BinTest do |> to(:rtp), link(:hackney) |> to(:parser) - |> via_in(Pad.ref(:input, options.output.video.ssrc)) + |> via_in(Pad.ref(:input, options.output.video.ssrc), options: [use_payloader?: true]) |> to(:rtp) - |> via_out(Pad.ref(:rtp_output, options.output.video.ssrc), options: [encoding: :H264]) + |> via_out(Pad.ref(:rtp_output, options.output.video.ssrc), + options: [encoding: :H264, use_payloader?: true] + ) |> to(:rtp_sink) ] } @@ -145,7 +147,9 @@ defmodule Membrane.RTP.Session.BinTest do ], links: [ link(:rtp) - |> via_out(Pad.ref(:output, ssrc), options: [rtcp_fir_interval: nil]) + |> via_out(Pad.ref(:output, ssrc), + options: [use_jitter_buffer?: true, use_depayloader?: true, rtcp_fir_interval: nil] + ) |> to({:sink, ssrc}) ] } diff --git a/test/membrane/rtp/stream_receive_bin_test.exs b/test/membrane/rtp/stream_receive_bin_test.exs index 0b00951a..2b6406c6 100644 --- a/test/membrane/rtp/stream_receive_bin_test.exs +++ b/test/membrane/rtp/stream_receive_bin_test.exs @@ -41,9 +41,9 @@ defmodule Membrane.RTP.StreamReceiveBinTest do rtp_parser: RTP.Parser, rtp: %StreamReceiveBin{ depayloader: H264.Depayloader, + jitter_buffer: %Membrane.RTP.JitterBuffer{clock_rate: @h264_clock_rate}, remote_ssrc: @ssrc, local_ssrc: 0, - clock_rate: @h264_clock_rate, rtcp_report_interval: Membrane.Time.seconds(5), rtcp_fir_interval: nil }, @@ -85,9 +85,9 @@ defmodule Membrane.RTP.StreamReceiveBinTest do rtp_parser: RTP.Parser, rtp: %StreamReceiveBin{ depayloader: H264.Depayloader, + jitter_buffer: %Membrane.RTP.JitterBuffer{clock_rate: @h264_clock_rate}, local_ssrc: 0, remote_ssrc: 4_194_443_425, - clock_rate: @h264_clock_rate, rtcp_report_interval: Membrane.Time.seconds(5), rtcp_fir_interval: nil }, From 3aa6fc09215c3f7927c6158c49ea418af274e6ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Per=C5=BCy=C5=82o?= Date: Fri, 27 Aug 2021 09:58:58 +0200 Subject: [PATCH 03/20] Added docs regaring optional payloaders/depayloaders in SessionBin --- lib/membrane/rtp/session_bin.ex | 33 ++++++++++++++++---------- test/membrane/rtp/session_bin_test.exs | 2 +- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/lib/membrane/rtp/session_bin.ex b/lib/membrane/rtp/session_bin.ex index cd14cad6..e20989a8 100644 --- a/lib/membrane/rtp/session_bin.ex +++ b/lib/membrane/rtp/session_bin.ex @@ -20,6 +20,18 @@ defmodule Membrane.RTP.SessionBin do `membrane_rtp_X_plugin` packages, where X stands for codec name. It's enough when such plugin is added to dependencies. + #### Jitter Buffer + Some depayloaders may need the incoming packets to be in a correct order therefore one needs to not only + use depayloader but also include a jitter buffer. It can be specified as pad's option `use_jitter_buffer?` when + adding `Pad.ref(:output, ssrc)` pad, . + + + #### Important note + Payloaders and depayloaders are mostly needed when working with external media sources (in different formats than RTP). + For applications such an SFU it is not needed to either payload or depayload the RTP stream as we are always dealing with RTP format. + In such a case, SessionBin will receive payloaded packets and work as a simple proxy just forwarding the packets (and decrypting them if necessary). + Therefore it is possible to specify in newly added pads if payloaders/depayloaders should be used for the correlated stream. + ## RTCP RTCP packets for inbound stream can be provided either in-band or via a separate `rtp_input` pad instance. Corresponding receiver report packets will be sent back through `rtcp_output` with the same id as `rtp_input` for the RTP stream. @@ -240,13 +252,6 @@ defmodule Membrane.RTP.SessionBin do description: """ Clock rate to use. If not provided, determined from `:payload_type`. """ - ], - use_payloader?: [ - spec: boolean(), - default: true, - description: """ - Defines whether paylaoder should be used for incoming stream. - """ ] ] @@ -408,17 +413,19 @@ defmodule Membrane.RTP.SessionBin do @impl true def handle_pad_added(Pad.ref(name, ssrc), ctx, state) when name in [:input, :rtp_output] do - pads_present? = - Map.has_key?(ctx.pads, Pad.ref(:input, ssrc)) and - Map.has_key?(ctx.pads, Pad.ref(:rtp_output, ssrc)) + input_pad = Pad.ref(:input, ssrc) + output_pad = Pad.ref(:rtp_output, ssrc) + + pads_present? = Enum.all?([input_pad, output_pad], & Map.has_key?(ctx.pads, &1)) if not pads_present? or Map.has_key?(ctx.children, {:stream_send_bin, ssrc}) do {:ok, state} else - pad = Pad.ref(:rtp_output, ssrc) + input_pad = Pad.ref(:input, ssrc) + output_pad = Pad.ref(:rtp_output, ssrc) - %{encoding: encoding_name, clock_rate: clock_rate, use_payloader?: use_payloader?} = - ctx.pads[pad].options + %{use_payloader?: use_payloader?} = ctx.pads[input_pad].options + %{encoding: encoding_name, clock_rate: clock_rate} = ctx.pads[output_pad].options payload_type = get_output_payload_type!(ctx, ssrc) encoding_name = encoding_name || get_from_register!(:encoding_name, payload_type, state) diff --git a/test/membrane/rtp/session_bin_test.exs b/test/membrane/rtp/session_bin_test.exs index d1ec269d..8b71c1fa 100644 --- a/test/membrane/rtp/session_bin_test.exs +++ b/test/membrane/rtp/session_bin_test.exs @@ -130,7 +130,7 @@ defmodule Membrane.RTP.Session.BinTest do |> via_in(Pad.ref(:input, options.output.video.ssrc), options: [use_payloader?: true]) |> to(:rtp) |> via_out(Pad.ref(:rtp_output, options.output.video.ssrc), - options: [encoding: :H264, use_payloader?: true] + options: [encoding: :H264] ) |> to(:rtp_sink) ] From 283d611496fe5d0742cc8910d8acde78de97ec0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Per=C5=BCy=C5=82o?= Date: Tue, 31 Aug 2021 15:26:33 +0200 Subject: [PATCH 04/20] Added separate element for gathering stats about RTP stream --- lib/membrane/rtp/jitter_buffer.ex | 1 + lib/membrane/rtp/random_dropper.ex | 31 +++ lib/membrane/rtp/session_bin.ex | 10 +- lib/membrane/rtp/stats_accumulator.ex | 203 ++++++++++++++++++ lib/membrane/rtp/stream_receive_bin.ex | 6 + test/membrane/rtp/stream_receive_bin_test.exs | 2 + 6 files changed, 251 insertions(+), 2 deletions(-) create mode 100644 lib/membrane/rtp/random_dropper.ex create mode 100644 lib/membrane/rtp/stats_accumulator.ex diff --git a/lib/membrane/rtp/jitter_buffer.ex b/lib/membrane/rtp/jitter_buffer.ex index 11c10cb8..08a35e32 100644 --- a/lib/membrane/rtp/jitter_buffer.ex +++ b/lib/membrane/rtp/jitter_buffer.ex @@ -131,6 +131,7 @@ defmodule Membrane.RTP.JitterBuffer do @impl true def handle_event(:input, %__MODULE__.StatsRequestEvent{}, _ctx, state) do {stats, state} = get_updated_stats(state) + IO.inspect(stats) {{:ok, event: {:input, %__MODULE__.StatsEvent{stats: stats}}}, state} end diff --git a/lib/membrane/rtp/random_dropper.ex b/lib/membrane/rtp/random_dropper.ex new file mode 100644 index 00000000..8a81bc36 --- /dev/null +++ b/lib/membrane/rtp/random_dropper.ex @@ -0,0 +1,31 @@ +defmodule Membrane.RTP.RandomDropper do + use Membrane.Filter + + def_input_pad :input, demand_unit: :buffers, caps: :any + def_output_pad :output, caps: :any + + def_options drop_rate: [ + spec: float(), + default: 0, + description: "Probability for a buffer to get dropped in range (0, 1)" + ] + + @impl true + def handle_init(opts) do + {:ok, %{drop_rate: opts.drop_rate}} + end + + @impl true + def handle_demand(:output, size, :buffers, _ctx, state) do + {{:ok, demand: {:input, size}}, state} + end + + @impl true + def handle_process(:input, buffer, _ctx, state) do + if :rand.uniform() < state.drop_rate do + {{:ok, redemand: :output}, state} + else + {{:ok, buffer: {:output, buffer}, redemand: :output}, state} + end + end +end diff --git a/lib/membrane/rtp/session_bin.ex b/lib/membrane/rtp/session_bin.ex index e20989a8..4361c729 100644 --- a/lib/membrane/rtp/session_bin.ex +++ b/lib/membrane/rtp/session_bin.ex @@ -353,6 +353,9 @@ defmodule Membrane.RTP.SessionBin do packet_filters: filters } = ctx.pads[pad].options + IO.inspect("USING DEPAYLOADER: #{use_depayloader?}") + IO.inspect("USING JITTER BUFFEr: #{use_jitter_buffer?}") + payload_type = Map.fetch!(state.ssrc_pt_mapping, ssrc) encoding_name = encoding_name || get_from_register!(:encoding_name, payload_type, state) @@ -364,6 +367,7 @@ defmodule Membrane.RTP.SessionBin do new_children = %{ rtp_stream_name => %RTP.StreamReceiveBin{ + clock_rate: clock_rate, depayloader: depayloader, jitter_buffer: if(use_jitter_buffer?, do: %Membrane.RTP.JitterBuffer{clock_rate: clock_rate}, else: nil), @@ -371,7 +375,7 @@ defmodule Membrane.RTP.SessionBin do local_ssrc: local_ssrc, remote_ssrc: ssrc, rtcp_fir_interval: fir_interval, - rtcp_report_interval: state.rtcp_report_interval, + rtcp_report_interval: 5 * Membrane.Time.second() || state.rtcp_report_interval, secure?: state.secure?, srtp_policies: state.srtp_policies } @@ -416,7 +420,7 @@ defmodule Membrane.RTP.SessionBin do input_pad = Pad.ref(:input, ssrc) output_pad = Pad.ref(:rtp_output, ssrc) - pads_present? = Enum.all?([input_pad, output_pad], & Map.has_key?(ctx.pads, &1)) + pads_present? = Enum.all?([input_pad, output_pad], &Map.has_key?(ctx.pads, &1)) if not pads_present? or Map.has_key?(ctx.children, {:stream_send_bin, ssrc}) do {:ok, state} @@ -427,6 +431,8 @@ defmodule Membrane.RTP.SessionBin do %{use_payloader?: use_payloader?} = ctx.pads[input_pad].options %{encoding: encoding_name, clock_rate: clock_rate} = ctx.pads[output_pad].options + IO.inspect("USING PAYLOADER: #{use_payloader?}") + payload_type = get_output_payload_type!(ctx, ssrc) encoding_name = encoding_name || get_from_register!(:encoding_name, payload_type, state) clock_rate = clock_rate || get_from_register!(:clock_rate, payload_type, state) diff --git a/lib/membrane/rtp/stats_accumulator.ex b/lib/membrane/rtp/stats_accumulator.ex new file mode 100644 index 00000000..7c349302 --- /dev/null +++ b/lib/membrane/rtp/stats_accumulator.ex @@ -0,0 +1,203 @@ +defmodule Membrane.RTP.StatsAccumulator do + # TODO: support for discarding packets and repairing their sequence numbers + + use Membrane.Filter + + alias Membrane.Time + alias Membrane.Buffer + + @max_seq_num 0xFFFF + @max_dropout 3000 + @max_unordered 3000 + + @max_s24_val 8_388_607 + @min_s24_val -8_388_608 + + def_input_pad :input, demand_unit: :buffers, caps: :any + def_output_pad :output, caps: :any + + def_options clock_rate: [type: :integer, spec: Membrane.RTP.clock_rate_t()] + + defmodule State do + @type t :: %__MODULE__{ + clock_rate: non_neg_integer(), + jitter: float(), + transit: non_neg_integer() | nil, + received: non_neg_integer(), + cycles: non_neg_integer(), + max_seq: non_neg_integer(), + base_seq: non_neg_integer(), + received_prior: non_neg_integer(), + expected_prior: non_neg_integer(), + lost: non_neg_integer(), + fraction_lost: float(), + discarded: non_neg_integer() + } + + @enforce_keys [:clock_rate] + defstruct @enforce_keys ++ + [ + jitter: 0.0, + transit: nil, + received: 0, + cycles: 0, + max_seq: nil, + base_seq: nil, + received_prior: 0, + expected_prior: 0, + lost: 0, + fraction_lost: 0.0, + discarded: 0 + ] + end + + @impl true + def handle_init(opts) do + {:ok, %State{clock_rate: opts.clock_rate}} + end + + @impl true + def handle_demand(:output, size, :buffers, _ctx, state) do + {{:ok, demand: {:input, size}}, state} + end + + @impl true + def handle_process(:input, buffer, _ctx, %State{cycles: cycles, max_seq: max_seq} = state) do + seq_num = buffer.metadata.rtp.sequence_number + max_seq = max_seq || seq_num - 1 + + delta = rem(seq_num - max_seq + @max_seq_num, @max_seq_num) + + cond do + # greater sequence number but within dropout to ensure that it is not from previous cycle + delta < @max_dropout -> + state = + state + |> update_sequence_counters(seq_num, max_seq, cycles) + |> update_received() + |> update_jitter(buffer) + + {{:ok, buffer: {:output, buffer}}, state} + + # the packets is either too old or too new + delta <= @max_seq_num - @max_unordered -> + {:ok, state} + + # packet is old but within dropout threshold + true -> + state = + state + |> update_received() + |> update_jitter(buffer) + + {{:ok, buffer, {:output, buffer}}, update_received(state)} + end + end + + @impl true + def handle_event(:input, %Membrane.RTP.JitterBuffer.StatsRequestEvent{}, _ctx, state) do + %State{ + received: received, + received_prior: received_prior, + expected_prior: expected_prior, + cycles: cycles, + max_seq: max_seq, + jitter: jitter + } = state + + expected = expected_packets(state) + + lost = + if expected > received do + expected - received + else + 0 + end + + expected_interval = expected - expected_prior + received_interval = received - received_prior + + lost_interval = expected_interval - received_interval + + fraction_lost = + if expected_interval == 0 || lost_interval <= 0 do + 0.0 + else + lost_interval / expected_interval + end + + IO.inspect( + "Expected interval #{expected_interval}, Received interval: #{received_interval}, Fraction lost #{fraction_lost}" + ) + + total_lost = + cond do + lost > @max_s24_val -> @max_s24_val + lost < @min_s24_val -> @min_s24_val + true -> lost + end + + state = %State{ + state + | expected_prior: expected, + received_prior: received, + lost: total_lost, + fraction_lost: fraction_lost + } + + stats = + %Membrane.RTP.JitterBuffer.Stats{ + fraction_lost: fraction_lost, + total_lost: total_lost, + highest_seq_num: max_seq + cycles, + interarrival_jitter: jitter + } + |> IO.inspect() + + {{:ok, event: {:input, %Membrane.RTP.JitterBuffer.StatsEvent{stats: stats}}}, state} + end + + @impl true + def handle_event(direction, event, ctx, state), do: super(direction, event, ctx, state) + + defp update_sequence_counters(state, seq_num, max_seq, cycles) do + {max_seq_num, cycles} = + if seq_num < max_seq do + {seq_num, cycles + @max_seq_num} + else + {seq_num, cycles} + end + + %State{state | max_seq: max_seq_num, cycles: cycles, base_seq: state.base_seq || seq_num} + end + + defp update_received(%State{received: received} = state) do + %State{state | received: received + 1} + end + + defp expected_packets(%State{cycles: cycles, max_seq: max_seq, base_seq: base_seq}) do + cycles + max_seq - base_seq + 1 + end + + defp update_jitter(state, %Buffer{metadata: metadata}) do + %State{clock_rate: clock_rate, jitter: last_jitter, transit: last_transit} = state + + # Algorithm from https://tools.ietf.org/html/rfc3550#appendix-A.8 + arrival_ts = Map.get(metadata, :arrival_ts, Time.vm_time()) + buffer_ts = metadata.rtp.timestamp + arrival = arrival_ts |> Time.as_seconds() |> Ratio.mult(clock_rate) |> Ratio.trunc() + transit = arrival - buffer_ts + + {jitter, transit} = + if last_transit == nil do + {last_jitter, transit} + else + d = abs(transit - last_transit) + new_jitter = last_jitter + 1 / 16 * (d - last_jitter) + + {new_jitter, transit} + end + + %State{state | jitter: jitter, transit: transit} + end +end diff --git a/lib/membrane/rtp/stream_receive_bin.ex b/lib/membrane/rtp/stream_receive_bin.ex index 5c3243b6..00588c75 100644 --- a/lib/membrane/rtp/stream_receive_bin.ex +++ b/lib/membrane/rtp/stream_receive_bin.ex @@ -30,6 +30,10 @@ defmodule Membrane.RTP.StreamReceiveBin do spec: module() | nil, default: nil ], + clock_rate: [ + type: :integer, + spec: RTP.clock_rate_t() + ], local_ssrc: [spec: Membrane.RTP.ssrc_t()], remote_ssrc: [spec: Membrane.RTP.ssrc_t()], rtcp_report_interval: [spec: Membrane.Time.t() | nil], @@ -50,12 +54,14 @@ defmodule Membrane.RTP.StreamReceiveBin do links = [ link_bin_input() |> to_filters(opts.filters) + |> to(:dropper, %Membrane.RTP.RandomDropper{drop_rate: 0.05}) |> to(:rtcp_receiver, %Membrane.RTCP.Receiver{ local_ssrc: opts.local_ssrc, remote_ssrc: opts.remote_ssrc, report_interval: opts.rtcp_report_interval, fir_interval: opts.rtcp_fir_interval }) + |> to(:rtcp_stats, %Membrane.RTP.StatsAccumulator{clock_rate: opts.clock_rate}) |> then(if opts.jitter_buffer != nil, do: maybe_link_jitter_buffer, else: & &1) |> then(if opts.secure?, do: maybe_link_decryptor, else: & &1) |> then(if opts.depayloader != nil, do: maybe_link_depayloader, else: & &1) diff --git a/test/membrane/rtp/stream_receive_bin_test.exs b/test/membrane/rtp/stream_receive_bin_test.exs index 2b6406c6..04491409 100644 --- a/test/membrane/rtp/stream_receive_bin_test.exs +++ b/test/membrane/rtp/stream_receive_bin_test.exs @@ -40,6 +40,7 @@ defmodule Membrane.RTP.StreamReceiveBinTest do pcap: %Membrane.Element.Pcap.Source{path: @pcap_file}, rtp_parser: RTP.Parser, rtp: %StreamReceiveBin{ + clock_rate: @h264_clock_rate, depayloader: H264.Depayloader, jitter_buffer: %Membrane.RTP.JitterBuffer{clock_rate: @h264_clock_rate}, remote_ssrc: @ssrc, @@ -84,6 +85,7 @@ defmodule Membrane.RTP.StreamReceiveBinTest do }, rtp_parser: RTP.Parser, rtp: %StreamReceiveBin{ + clock_rate: @h264_clock_rate, depayloader: H264.Depayloader, jitter_buffer: %Membrane.RTP.JitterBuffer{clock_rate: @h264_clock_rate}, local_ssrc: 0, From 3c527d7caff37dbc71ffa43db1558537d77fe384 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Per=C5=BCy=C5=82o?= Date: Wed, 1 Sep 2021 14:55:21 +0200 Subject: [PATCH 05/20] Added packet tracker instead of gathering stats in jitter buffer --- lib/membrane/rtcp/receiver.ex | 9 +- .../receiver_report}/stats.ex | 2 +- .../receiver_report}/stats_event.ex | 4 +- .../receiver_report}/stats_request_event.ex | 2 +- lib/membrane/rtp/jitter_buffer.ex | 97 +------------------ .../rtp/jitter_buffer/buffer_store.ex | 6 +- ...stats_accumulator.ex => packet_tracker.ex} | 89 ++++++++++++----- lib/membrane/rtp/packets_discarded_event.ex | 12 +++ lib/membrane/rtp/packets_dropped_event.ex | 12 --- lib/membrane/rtp/session_bin.ex | 7 +- lib/membrane/rtp/silence_discarder.ex | 6 +- lib/membrane/rtp/stream_receive_bin.ex | 7 +- .../rtp/jitter_buffer/buffer_store_test.exs | 10 -- test/membrane/rtp/jitter_buffer_test.exs | 50 +++++----- test/membrane/rtp/packet_tracker_test.exs | 10 ++ 15 files changed, 135 insertions(+), 188 deletions(-) rename lib/membrane/{rtp/jitter_buffer => rtcp/receiver_report}/stats.ex (90%) rename lib/membrane/{rtp/jitter_buffer => rtcp/receiver_report}/stats_event.ex (64%) rename lib/membrane/{rtp/jitter_buffer => rtcp/receiver_report}/stats_request_event.ex (72%) rename lib/membrane/rtp/{stats_accumulator.ex => packet_tracker.ex} (63%) create mode 100644 lib/membrane/rtp/packets_discarded_event.ex delete mode 100644 lib/membrane/rtp/packets_dropped_event.ex create mode 100644 test/membrane/rtp/packet_tracker_test.exs diff --git a/lib/membrane/rtcp/receiver.ex b/lib/membrane/rtcp/receiver.ex index 4d75845e..cb523538 100644 --- a/lib/membrane/rtcp/receiver.ex +++ b/lib/membrane/rtcp/receiver.ex @@ -7,6 +7,7 @@ defmodule Membrane.RTCP.Receiver do alias Membrane.RTCPEvent alias Membrane.RTCP.{FeedbackPacket, SenderReportPacket} alias Membrane.{RTCP, RTP} + alias Membrane.RTCP.ReceiverReport alias Membrane.Time require Membrane.Logger @@ -46,7 +47,7 @@ defmodule Membrane.RTCP.Receiver do @impl true def handle_tick(:report_timer, _ctx, state) do - {{:ok, event: {:output, %RTP.JitterBuffer.StatsRequestEvent{}}}, state} + {{:ok, event: {:output, %ReceiverReport.StatsRequestEvent{}}}, state} end @impl true @@ -75,12 +76,14 @@ defmodule Membrane.RTCP.Receiver do end @impl true - def handle_event(:output, %RTP.JitterBuffer.StatsEvent{stats: :no_stats}, _ctx, state) do + def handle_event(:output, %ReceiverReport.StatsEvent{stats: :no_stats}, _ctx, state) do {:ok, state} end + # TODO: study why turning on receiver reports causes browsers (at least chrome) to start + # sending x4 more packets @impl true - def handle_event(:output, %RTP.JitterBuffer.StatsEvent{stats: stats}, _ctx, state) do + def handle_event(:output, %ReceiverReport.StatsEvent{stats: stats}, _ctx, state) do now = Time.vm_time() delay_since_sr = now - Map.get(state.sr_info, :arrival_ts, now) diff --git a/lib/membrane/rtp/jitter_buffer/stats.ex b/lib/membrane/rtcp/receiver_report/stats.ex similarity index 90% rename from lib/membrane/rtp/jitter_buffer/stats.ex rename to lib/membrane/rtcp/receiver_report/stats.ex index 9cc226b1..83bcff12 100644 --- a/lib/membrane/rtp/jitter_buffer/stats.ex +++ b/lib/membrane/rtcp/receiver_report/stats.ex @@ -1,4 +1,4 @@ -defmodule Membrane.RTP.JitterBuffer.Stats do +defmodule Membrane.RTCP.ReceiverReport.Stats do @moduledoc """ JitterBuffer stats that can be used for Receiver report generation """ diff --git a/lib/membrane/rtp/jitter_buffer/stats_event.ex b/lib/membrane/rtcp/receiver_report/stats_event.ex similarity index 64% rename from lib/membrane/rtp/jitter_buffer/stats_event.ex rename to lib/membrane/rtcp/receiver_report/stats_event.ex index a06f7981..f4c3fad4 100644 --- a/lib/membrane/rtp/jitter_buffer/stats_event.ex +++ b/lib/membrane/rtcp/receiver_report/stats_event.ex @@ -1,4 +1,4 @@ -defmodule Membrane.RTP.JitterBuffer.StatsEvent do +defmodule Membrane.RTCP.ReceiverReport.StatsEvent do @moduledoc """ Event carrying jitter buffer statistics. """ @@ -8,6 +8,6 @@ defmodule Membrane.RTP.JitterBuffer.StatsEvent do defstruct @enforce_keys @type t :: %__MODULE__{ - stats: Membrane.RTP.JitterBuffer.Stats.t() + stats: Membrane.RTCP.ReceierReport.Stats.t() } end diff --git a/lib/membrane/rtp/jitter_buffer/stats_request_event.ex b/lib/membrane/rtcp/receiver_report/stats_request_event.ex similarity index 72% rename from lib/membrane/rtp/jitter_buffer/stats_request_event.ex rename to lib/membrane/rtcp/receiver_report/stats_request_event.ex index 0a846e6e..7cca3df2 100644 --- a/lib/membrane/rtp/jitter_buffer/stats_request_event.ex +++ b/lib/membrane/rtcp/receiver_report/stats_request_event.ex @@ -1,4 +1,4 @@ -defmodule Membrane.RTP.JitterBuffer.StatsRequestEvent do +defmodule Membrane.RTCP.ReceiverReport.StatsRequestEvent do @moduledoc """ Event to be sent to jitter buffer to request statistics. """ diff --git a/lib/membrane/rtp/jitter_buffer.ex b/lib/membrane/rtp/jitter_buffer.ex index 08a35e32..0adb8254 100644 --- a/lib/membrane/rtp/jitter_buffer.ex +++ b/lib/membrane/rtp/jitter_buffer.ex @@ -7,7 +7,7 @@ defmodule Membrane.RTP.JitterBuffer do use Bunch alias Membrane.{Buffer, RTP, Time} - alias __MODULE__.{BufferStore, Record, Stats} + alias __MODULE__.{BufferStore, Record} @type packet_index :: non_neg_integer() @@ -22,9 +22,6 @@ defmodule Membrane.RTP.JitterBuffer do @default_latency 200 |> Time.milliseconds() - @max_s24_val 8_388_607 - @min_s24_val -8_388_608 - def_options clock_rate: [type: :integer, spec: RTP.clock_rate_t()], latency: [ type: :time, @@ -44,21 +41,14 @@ defmodule Membrane.RTP.JitterBuffer do waiting?: true, max_latency_timer: nil, timestamp_base: nil, - previous_timestamp: -1, - stats_acc: %{expected_prior: 0, received_prior: 0, last_transit: nil, jitter: 0.0} + previous_timestamp: -1 @type t :: %__MODULE__{ store: BufferStore.t(), clock_rate: RTP.clock_rate_t(), latency: Time.t(), waiting?: boolean(), - max_latency_timer: reference, - stats_acc: %{ - expected_prior: non_neg_integer(), - received_prior: non_neg_integer(), - last_transit: non_neg_integer() | nil, - jitter: float() - } + max_latency_timer: reference } end @@ -98,8 +88,6 @@ defmodule Membrane.RTP.JitterBuffer do @impl true def handle_process(:input, buffer, _context, %State{store: store, waiting?: true} = state) do - state = update_jitter(buffer, state) - state = case BufferStore.insert_buffer(store, buffer) do {:ok, result} -> @@ -115,8 +103,6 @@ defmodule Membrane.RTP.JitterBuffer do @impl true def handle_process(:input, buffer, _context, %State{store: store} = state) do - state = update_jitter(buffer, state) - case BufferStore.insert_buffer(store, buffer) do {:ok, result} -> state = %State{state | store: result} @@ -128,18 +114,6 @@ defmodule Membrane.RTP.JitterBuffer do end end - @impl true - def handle_event(:input, %__MODULE__.StatsRequestEvent{}, _ctx, state) do - {stats, state} = get_updated_stats(state) - IO.inspect(stats) - {{:ok, event: {:input, %__MODULE__.StatsEvent{stats: stats}}}, state} - end - - @impl true - def handle_event(:input, %RTP.PacketsDroppedEvent{dropped: dropped}, _ctx, state) do - {:ok, update_in(state, [:store, :received], &(&1 + dropped))} - end - @impl true def handle_event(pad, event, ctx, state), do: super(pad, event, ctx, state) @@ -206,69 +180,4 @@ defmodule Membrane.RTP.JitterBuffer do state = %{state | timestamp_base: timestamp_base, previous_timestamp: rtp_timestamp} {action, state} end - - @spec get_updated_stats(State.t()) :: {Stats.t(), State.t()} - defp get_updated_stats(%State{store: %BufferStore{base_index: nil}} = state) do - {:no_stats, state} - end - - defp get_updated_stats(state) do - %State{store: store, stats_acc: stats_acc} = state - - # Variable names follow algorithm A.3 from RFC3550 (https://tools.ietf.org/html/rfc3550#appendix-A.3) - %BufferStore{base_index: base_seq, end_index: extended_max, received: received} = store - expected = extended_max - base_seq + 1 - lost = expected - received - - capped_lost = - cond do - lost > @max_s24_val -> @max_s24_val - lost < @min_s24_val -> @min_s24_val - true -> lost - end - - expected_interval = expected - stats_acc.expected_prior - received_interval = received - stats_acc.received_prior - lost_interval = expected_interval - received_interval - - fraction = - if expected_interval == 0 || lost_interval <= 0 do - 0.0 - else - lost_interval / expected_interval - end - - stats_acc = %{stats_acc | expected_prior: expected, received_prior: received} - - stats = %Stats{ - fraction_lost: fraction, - total_lost: capped_lost, - highest_seq_num: extended_max, - interarrival_jitter: stats_acc.jitter - } - - {stats, %State{state | stats_acc: stats_acc}} - end - - defp update_jitter(%Buffer{metadata: metadata}, state) do - %State{clock_rate: clock_rate, stats_acc: %{jitter: jitter, last_transit: last_transit}} = - state - - # Algorithm from https://tools.ietf.org/html/rfc3550#appendix-A.8 - arrival_ts = Map.get(metadata, :arrival_ts, Time.vm_time()) - buffer_ts = metadata.rtp.timestamp - arrival = arrival_ts |> Time.as_seconds() |> Ratio.mult(clock_rate) |> Ratio.trunc() - transit = arrival - buffer_ts - - if last_transit == nil do - put_in(state.stats_acc.last_transit, transit) - else - d = abs(transit - last_transit) - new_jitter = jitter + 1 / 16 * (d - jitter) - - state - |> put_in([:stats_acc, :jitter], new_jitter) - |> put_in([:stats_acc, :last_transit], transit) - end - end end diff --git a/lib/membrane/rtp/jitter_buffer/buffer_store.ex b/lib/membrane/rtp/jitter_buffer/buffer_store.ex index 9c419874..e30208cc 100644 --- a/lib/membrane/rtp/jitter_buffer/buffer_store.ex +++ b/lib/membrane/rtp/jitter_buffer/buffer_store.ex @@ -19,8 +19,7 @@ defmodule Membrane.RTP.JitterBuffer.BufferStore do @seq_number_limit 65_536 - defstruct received: 0, - base_index: nil, + defstruct base_index: nil, prev_index: nil, end_index: nil, heap: Heap.new(&Record.rtp_comparator/2), @@ -28,7 +27,6 @@ defmodule Membrane.RTP.JitterBuffer.BufferStore do rollover_count: 0 @type t :: %__MODULE__{ - received: non_neg_integer(), base_index: JitterBuffer.packet_index() | nil, prev_index: JitterBuffer.packet_index() | nil, end_index: JitterBuffer.packet_index() | nil, @@ -56,7 +54,7 @@ defmodule Membrane.RTP.JitterBuffer.BufferStore do """ @spec insert_buffer(t(), Buffer.t()) :: {:ok, t()} | {:error, insert_error()} def insert_buffer(store, %Buffer{metadata: %{rtp: %{sequence_number: seq_num}}} = buffer) do - do_insert_buffer(%__MODULE__{store | received: store.received + 1}, buffer, seq_num) + do_insert_buffer(store, buffer, seq_num) end @spec do_insert_buffer(t(), Buffer.t(), RTP.Header.sequence_number_t()) :: diff --git a/lib/membrane/rtp/stats_accumulator.ex b/lib/membrane/rtp/packet_tracker.ex similarity index 63% rename from lib/membrane/rtp/stats_accumulator.ex rename to lib/membrane/rtp/packet_tracker.ex index 7c349302..4f8c55d1 100644 --- a/lib/membrane/rtp/stats_accumulator.ex +++ b/lib/membrane/rtp/packet_tracker.ex @@ -1,10 +1,14 @@ -defmodule Membrane.RTP.StatsAccumulator do - # TODO: support for discarding packets and repairing their sequence numbers +defmodule Membrane.RTP.PacketTracker do + @moduledoc """ + Module responsible for tracking statistics of RTP packets for a single stream. + Tracker is capable of repairing packets' sequence numbers provided that it has information about how many packets has + been previously discarded. To updated number of discarded packets one should send an event `Membrane.RTP.PacketsDiscarded.t/0`. + """ use Membrane.Filter - alias Membrane.Time - alias Membrane.Buffer + alias Membrane.{Buffer, RTP, Time} + alias Membrane.RTCP.ReceiverReport @max_seq_num 0xFFFF @max_dropout 3000 @@ -16,11 +20,20 @@ defmodule Membrane.RTP.StatsAccumulator do def_input_pad :input, demand_unit: :buffers, caps: :any def_output_pad :output, caps: :any - def_options clock_rate: [type: :integer, spec: Membrane.RTP.clock_rate_t()] + def_options clock_rate: [ + type: :integer, + spec: Membrane.RTP.clock_rate_t() + ], + repair_sequence_numbers?: [ + spec: boolean(), + default: false, + description: "Defines if tracker should try to repair packet's sequence number" + ] defmodule State do @type t :: %__MODULE__{ clock_rate: non_neg_integer(), + repair_sequence_numbers?: boolean(), jitter: float(), transit: non_neg_integer() | nil, received: non_neg_integer(), @@ -34,7 +47,7 @@ defmodule Membrane.RTP.StatsAccumulator do discarded: non_neg_integer() } - @enforce_keys [:clock_rate] + @enforce_keys [:clock_rate, :repair_sequence_numbers?] defstruct @enforce_keys ++ [ jitter: 0.0, @@ -53,7 +66,8 @@ defmodule Membrane.RTP.StatsAccumulator do @impl true def handle_init(opts) do - {:ok, %State{clock_rate: opts.clock_rate}} + {:ok, + %State{clock_rate: opts.clock_rate, repair_sequence_numbers?: opts.repair_sequence_numbers?}} end @impl true @@ -66,7 +80,7 @@ defmodule Membrane.RTP.StatsAccumulator do seq_num = buffer.metadata.rtp.sequence_number max_seq = max_seq || seq_num - 1 - delta = rem(seq_num - max_seq + @max_seq_num, @max_seq_num) + delta = rem(seq_num - max_seq + @max_seq_num, @max_seq_num + 1) cond do # greater sequence number but within dropout to ensure that it is not from previous cycle @@ -77,11 +91,12 @@ defmodule Membrane.RTP.StatsAccumulator do |> update_received() |> update_jitter(buffer) - {{:ok, buffer: {:output, buffer}}, state} + {{:ok, buffer: {:output, repair_sequence_number(buffer, state)}}, state} # the packets is either too old or too new + # TODO: decide if this type of incident should increment the discarded counter delta <= @max_seq_num - @max_unordered -> - {:ok, state} + {{:ok, redemand: :output}, state} # packet is old but within dropout threshold true -> @@ -90,12 +105,12 @@ defmodule Membrane.RTP.StatsAccumulator do |> update_received() |> update_jitter(buffer) - {{:ok, buffer, {:output, buffer}}, update_received(state)} + {{:ok, buffer: {:output, repair_sequence_number(buffer, state)}}, update_received(state)} end end @impl true - def handle_event(:input, %Membrane.RTP.JitterBuffer.StatsRequestEvent{}, _ctx, state) do + def handle_event(:input, %ReceiverReport.StatsRequestEvent{}, _ctx, state) do %State{ received: received, received_prior: received_prior, @@ -126,10 +141,6 @@ defmodule Membrane.RTP.StatsAccumulator do lost_interval / expected_interval end - IO.inspect( - "Expected interval #{expected_interval}, Received interval: #{received_interval}, Fraction lost #{fraction_lost}" - ) - total_lost = cond do lost > @max_s24_val -> @max_s24_val @@ -145,16 +156,25 @@ defmodule Membrane.RTP.StatsAccumulator do fraction_lost: fraction_lost } - stats = - %Membrane.RTP.JitterBuffer.Stats{ - fraction_lost: fraction_lost, - total_lost: total_lost, - highest_seq_num: max_seq + cycles, - interarrival_jitter: jitter - } - |> IO.inspect() + stats = %ReceiverReport.Stats{ + fraction_lost: fraction_lost, + total_lost: total_lost, + highest_seq_num: max_seq + cycles, + # interarrival_jitter: jitter + interarrival_jitter: 0.010 + } - {{:ok, event: {:input, %Membrane.RTP.JitterBuffer.StatsEvent{stats: stats}}}, state} + {{:ok, event: {:input, %ReceiverReport.StatsEvent{stats: stats}}}, state} + end + + @impl true + def handle_event( + :input, + %RTP.PacketsDiscardedEvent{discarded: packets_discarded}, + _ctx, + %State{discarded: discarded} = state + ) do + {:ok, %State{state | discarded: discarded + packets_discarded}} end @impl true @@ -200,4 +220,23 @@ defmodule Membrane.RTP.StatsAccumulator do %State{state | jitter: jitter, transit: transit} end + + defp repair_sequence_number(%Buffer{} = buffer, %State{ + discarded: discarded, + repair_sequence_numbers?: repair? + }) + when not repair? or discarded == 0 do + buffer + end + + # repairs sequence number if there have been any packets discarded by any of previous elements + defp repair_sequence_number( + %Buffer{metadata: %{rtp: %{sequence_number: seq_num}} = metadata} = buffer, + %State{discarded: discarded} + ) do + metadata = + put_in(metadata, [:rtp, :sequence_number], rem(seq_num - discarded, @max_seq_num + 1)) + + %Buffer{buffer | metadata: metadata} + end end diff --git a/lib/membrane/rtp/packets_discarded_event.ex b/lib/membrane/rtp/packets_discarded_event.ex new file mode 100644 index 00000000..c771bf3c --- /dev/null +++ b/lib/membrane/rtp/packets_discarded_event.ex @@ -0,0 +1,12 @@ +defmodule Membrane.RTP.PacketsDiscardedEvent do + @moduledoc """ + Event carrying information about how many packets has been discarded by some element. + """ + @derive Membrane.EventProtocol + + defstruct discarded: 0 + + @type t :: %__MODULE__{ + discarded: non_neg_integer() + } +end diff --git a/lib/membrane/rtp/packets_dropped_event.ex b/lib/membrane/rtp/packets_dropped_event.ex deleted file mode 100644 index f1619b80..00000000 --- a/lib/membrane/rtp/packets_dropped_event.ex +++ /dev/null @@ -1,12 +0,0 @@ -defmodule Membrane.RTP.PacketsDroppedEvent do - @moduledoc """ - Event carrying information about how many packets has been dropped by some element. - """ - @derive Membrane.EventProtocol - - defstruct dropped: 0 - - @type t :: %__MODULE__{ - dropped: non_neg_integer() - } -end diff --git a/lib/membrane/rtp/session_bin.ex b/lib/membrane/rtp/session_bin.ex index 4361c729..11197319 100644 --- a/lib/membrane/rtp/session_bin.ex +++ b/lib/membrane/rtp/session_bin.ex @@ -353,9 +353,6 @@ defmodule Membrane.RTP.SessionBin do packet_filters: filters } = ctx.pads[pad].options - IO.inspect("USING DEPAYLOADER: #{use_depayloader?}") - IO.inspect("USING JITTER BUFFEr: #{use_jitter_buffer?}") - payload_type = Map.fetch!(state.ssrc_pt_mapping, ssrc) encoding_name = encoding_name || get_from_register!(:encoding_name, payload_type, state) @@ -375,7 +372,7 @@ defmodule Membrane.RTP.SessionBin do local_ssrc: local_ssrc, remote_ssrc: ssrc, rtcp_fir_interval: fir_interval, - rtcp_report_interval: 5 * Membrane.Time.second() || state.rtcp_report_interval, + rtcp_report_interval: state.rtcp_report_interval, secure?: state.secure?, srtp_policies: state.srtp_policies } @@ -431,8 +428,6 @@ defmodule Membrane.RTP.SessionBin do %{use_payloader?: use_payloader?} = ctx.pads[input_pad].options %{encoding: encoding_name, clock_rate: clock_rate} = ctx.pads[output_pad].options - IO.inspect("USING PAYLOADER: #{use_payloader?}") - payload_type = get_output_payload_type!(ctx, ssrc) encoding_name = encoding_name || get_from_register!(:encoding_name, payload_type, state) clock_rate = clock_rate || get_from_register!(:clock_rate, payload_type, state) diff --git a/lib/membrane/rtp/silence_discarder.ex b/lib/membrane/rtp/silence_discarder.ex index 7f28658e..d52acc82 100644 --- a/lib/membrane/rtp/silence_discarder.ex +++ b/lib/membrane/rtp/silence_discarder.ex @@ -13,7 +13,7 @@ defmodule Membrane.RTP.SilenceDiscarder do use Membrane.Filter alias Membrane.RTP.Header - alias Membrane.RTP.PacketsDroppedEvent + alias Membrane.RTP.PacketsDiscardedEvent require Membrane.Logger @@ -110,8 +110,8 @@ defmodule Membrane.RTP.SilenceDiscarder do defp stop_dropping(buffer, state) do {{:ok, - event: {:output, %PacketsDroppedEvent{dropped: state.dropped}}, buffer: {:output, buffer}}, - %{state | dropped: 0}} + event: {:output, %PacketsDiscardedEvent{discarded: state.dropped}}, + buffer: {:output, buffer}}, %{state | dropped: 0}} end defp is_silent_packet(_vad_id, _threshold, <<>>), do: false diff --git a/lib/membrane/rtp/stream_receive_bin.ex b/lib/membrane/rtp/stream_receive_bin.ex index 00588c75..f452c0c9 100644 --- a/lib/membrane/rtp/stream_receive_bin.ex +++ b/lib/membrane/rtp/stream_receive_bin.ex @@ -54,14 +54,17 @@ defmodule Membrane.RTP.StreamReceiveBin do links = [ link_bin_input() |> to_filters(opts.filters) - |> to(:dropper, %Membrane.RTP.RandomDropper{drop_rate: 0.05}) |> to(:rtcp_receiver, %Membrane.RTCP.Receiver{ local_ssrc: opts.local_ssrc, remote_ssrc: opts.remote_ssrc, report_interval: opts.rtcp_report_interval, + # report_interval: 5 * Membrane.Time.second(), fir_interval: opts.rtcp_fir_interval }) - |> to(:rtcp_stats, %Membrane.RTP.StatsAccumulator{clock_rate: opts.clock_rate}) + |> to(:tracker, %Membrane.RTP.PacketTracker{ + clock_rate: opts.clock_rate, + repair_sequence_numbers?: true + }) |> then(if opts.jitter_buffer != nil, do: maybe_link_jitter_buffer, else: & &1) |> then(if opts.secure?, do: maybe_link_decryptor, else: & &1) |> then(if opts.depayloader != nil, do: maybe_link_depayloader, else: & &1) diff --git a/test/membrane/rtp/jitter_buffer/buffer_store_test.exs b/test/membrane/rtp/jitter_buffer/buffer_store_test.exs index 7e34fbba..ef0ccc84 100644 --- a/test/membrane/rtp/jitter_buffer/buffer_store_test.exs +++ b/test/membrane/rtp/jitter_buffer/buffer_store_test.exs @@ -48,16 +48,6 @@ defmodule Membrane.RTP.JitterBuffer.BufferStoreTest do assert read_index == @next_index end - test "only changes stats in the BufferStore when duplicate is inserted", %{ - base_store: base_store - } do - buffer = BufferFactory.sample_buffer(@next_index) - {:ok, store} = BufferStore.insert_buffer(base_store, buffer) - assert {:ok, new_store} = BufferStore.insert_buffer(store, buffer) - assert Map.delete(store, :received) == Map.delete(new_store, :received) - assert new_store.received == store.received + 1 - end - test "handles first buffers starting with sequence_number 0" do store = %BufferStore{} buffer_a = BufferFactory.sample_buffer(0) diff --git a/test/membrane/rtp/jitter_buffer_test.exs b/test/membrane/rtp/jitter_buffer_test.exs index ba0f9b4f..442b5d67 100644 --- a/test/membrane/rtp/jitter_buffer_test.exs +++ b/test/membrane/rtp/jitter_buffer_test.exs @@ -43,38 +43,39 @@ defmodule Membrane.RTP.JitterBufferTest do assert BufferStore.dump(new_store) == [] end - test "jitter stats are updated", %{state: state, buffer: buffer} do - ts = ~U[2020-06-19 19:06:00Z] |> DateTime.to_unix() |> Membrane.Time.seconds() + # TODO: change that so that those tests are placed in PacketTracker instead of jitter buffer tests + # test "jitter stats are updated", %{state: state, buffer: buffer} do + # ts = ~U[2020-06-19 19:06:00Z] |> DateTime.to_unix() |> Membrane.Time.seconds() - timestamped_buf = put_in(buffer.metadata[:arrival_ts], ts) - assert {:ok, state} = JitterBuffer.handle_process(:input, timestamped_buf, nil, state) - assert state.stats_acc.jitter == 0.0 + # timestamped_buf = put_in(buffer.metadata[:arrival_ts], ts) + # assert {:ok, state} = JitterBuffer.handle_process(:input, timestamped_buf, nil, state) + # assert state.stats_acc.jitter == 0.0 - assert state.stats_acc.last_transit == - Membrane.Time.to_seconds(ts) * state.clock_rate - - timestamped_buf.metadata.rtp.timestamp + # assert state.stats_acc.last_transit == + # Membrane.Time.to_seconds(ts) * state.clock_rate - + # timestamped_buf.metadata.rtp.timestamp - buffer = BufferFactory.sample_buffer(@base_seq_number + 1) + # buffer = BufferFactory.sample_buffer(@base_seq_number + 1) - arrival_ts_increment = - div(BufferFactory.timestamp_increment(), state.clock_rate) |> Membrane.Time.seconds() + # arrival_ts_increment = + # div(BufferFactory.timestamp_increment(), state.clock_rate) |> Membrane.Time.seconds() - packet_delay = 1 |> Membrane.Time.seconds() + # packet_delay = 1 |> Membrane.Time.seconds() - timestamped_buf = - put_in(buffer.metadata[:arrival_ts], ts + arrival_ts_increment + packet_delay) + # timestamped_buf = + # put_in(buffer.metadata[:arrival_ts], ts + arrival_ts_increment + packet_delay) - assert {:ok, state} = JitterBuffer.handle_process(:input, timestamped_buf, nil, state) + # assert {:ok, state} = JitterBuffer.handle_process(:input, timestamped_buf, nil, state) - # 16 is defined by RFC - assert state.stats_acc.jitter == - Membrane.Time.to_seconds(packet_delay) * state.clock_rate / 16 + # # 16 is defined by RFC + # assert state.stats_acc.jitter == + # Membrane.Time.to_seconds(packet_delay) * state.clock_rate / 16 - assert state.stats_acc.last_transit == - Membrane.Time.to_seconds(ts + arrival_ts_increment + packet_delay) * - state.clock_rate - - timestamped_buf.metadata.rtp.timestamp - end + # assert state.stats_acc.last_transit == + # Membrane.Time.to_seconds(ts + arrival_ts_increment + packet_delay) * + # state.clock_rate - + # timestamped_buf.metadata.rtp.timestamp + # end end describe "When new buffer arrives when not waiting and already pushed some buffer" do @@ -97,8 +98,7 @@ defmodule Membrane.RTP.JitterBufferTest do assert {{:ok, redemand: :output}, new_state} = JitterBuffer.handle_process(:input, late_buffer, nil, state) - # assert nothing changed except for stats - assert %{new_state | stats_acc: state.stats_acc} == state + assert new_state == state end test "adds it and when it fills the gap, returns all buffers in order", %{state: state} do diff --git a/test/membrane/rtp/packet_tracker_test.exs b/test/membrane/rtp/packet_tracker_test.exs new file mode 100644 index 00000000..f6b064bd --- /dev/null +++ b/test/membrane/rtp/packet_tracker_test.exs @@ -0,0 +1,10 @@ +defmodule Membrane.RTP.PacketTrackerTest do + use ExUnit.Case, async: true + use Bunch + + # @seq_number_limit 65_536 + # @base_index 65_505 + # @next_index @base_index + 1 + + # TODO: write tests for the packet tracker gathering stats and repairing packets +end From a3f93fa0a6fb1ab07a3ca10289c4fdf17e674bc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Per=C5=BCy=C5=82o?= Date: Mon, 6 Sep 2021 15:13:43 +0200 Subject: [PATCH 06/20] Added tests for packet tracker and some docs --- lib/membrane/rtp/jitter_buffer.ex | 3 +- .../rtp/jitter_buffer/buffer_store.ex | 8 +-- lib/membrane/rtp/packet_tracker.ex | 31 ++++---- lib/membrane/rtp/random_dropper.ex | 31 -------- lib/membrane/rtp/session_bin.ex | 46 +++++------- lib/membrane/rtp/stream_receive_bin.ex | 1 - lib/membrane/rtp/stream_send_bin.ex | 3 + test/membrane/rtp/jitter_buffer_test.exs | 37 +--------- test/membrane/rtp/packet_tracker_test.exs | 71 +++++++++++++++++++ 9 files changed, 111 insertions(+), 120 deletions(-) delete mode 100644 lib/membrane/rtp/random_dropper.ex diff --git a/lib/membrane/rtp/jitter_buffer.ex b/lib/membrane/rtp/jitter_buffer.ex index 0adb8254..a14a0ddb 100644 --- a/lib/membrane/rtp/jitter_buffer.ex +++ b/lib/membrane/rtp/jitter_buffer.ex @@ -64,8 +64,7 @@ defmodule Membrane.RTP.JitterBuffer do @impl true def handle_start_of_stream(:input, _context, state) do Process.send_after( - self(), - :initial_latency_passed, + self(), :initial_latency_passed, state.latency |> Time.to_milliseconds() ) diff --git a/lib/membrane/rtp/jitter_buffer/buffer_store.ex b/lib/membrane/rtp/jitter_buffer/buffer_store.ex index e30208cc..ce2730cb 100644 --- a/lib/membrane/rtp/jitter_buffer/buffer_store.ex +++ b/lib/membrane/rtp/jitter_buffer/buffer_store.ex @@ -19,15 +19,13 @@ defmodule Membrane.RTP.JitterBuffer.BufferStore do @seq_number_limit 65_536 - defstruct base_index: nil, - prev_index: nil, + defstruct prev_index: nil, end_index: nil, heap: Heap.new(&Record.rtp_comparator/2), set: MapSet.new(), rollover_count: 0 @type t :: %__MODULE__{ - base_index: JitterBuffer.packet_index() | nil, prev_index: JitterBuffer.packet_index() | nil, end_index: JitterBuffer.packet_index() | nil, heap: Heap.t(), @@ -61,12 +59,12 @@ defmodule Membrane.RTP.JitterBuffer.BufferStore do {:ok, t()} | {:error, insert_error()} defp do_insert_buffer(%__MODULE__{prev_index: nil} = store, buffer, 0) do store = add_record(store, Record.new(buffer, @seq_number_limit)) - {:ok, %__MODULE__{store | prev_index: @seq_number_limit - 1, base_index: 0}} + {:ok, %__MODULE__{store | prev_index: @seq_number_limit - 1}} end defp do_insert_buffer(%__MODULE__{prev_index: nil} = store, buffer, seq_num) do store = add_record(store, Record.new(buffer, seq_num)) - {:ok, %__MODULE__{store | prev_index: seq_num - 1, base_index: seq_num}} + {:ok, %__MODULE__{store | prev_index: seq_num - 1}} end defp do_insert_buffer( diff --git a/lib/membrane/rtp/packet_tracker.ex b/lib/membrane/rtp/packet_tracker.ex index 4f8c55d1..280bfe58 100644 --- a/lib/membrane/rtp/packet_tracker.ex +++ b/lib/membrane/rtp/packet_tracker.ex @@ -1,9 +1,10 @@ defmodule Membrane.RTP.PacketTracker do @moduledoc """ - Module responsible for tracking statistics of RTP packets for a single stream. + Module responsible for tracking statistics of incoming RTP packets for a single stream. Tracker is capable of repairing packets' sequence numbers provided that it has information about how many packets has - been previously discarded. To updated number of discarded packets one should send an event `Membrane.RTP.PacketsDiscarded.t/0`. + been previously discarded. To updated number of discarded packets one should send an event `Membrane.RTP.PacketsDiscarded.t/0` that will accumulate + the total number of discarded packets and will subtract that number from the packet's sequence number. """ use Membrane.Filter @@ -37,14 +38,14 @@ defmodule Membrane.RTP.PacketTracker do jitter: float(), transit: non_neg_integer() | nil, received: non_neg_integer(), + discarded: non_neg_integer(), cycles: non_neg_integer(), - max_seq: non_neg_integer(), base_seq: non_neg_integer(), + max_seq: non_neg_integer(), received_prior: non_neg_integer(), expected_prior: non_neg_integer(), lost: non_neg_integer(), - fraction_lost: float(), - discarded: non_neg_integer() + fraction_lost: float() } @enforce_keys [:clock_rate, :repair_sequence_numbers?] @@ -53,14 +54,14 @@ defmodule Membrane.RTP.PacketTracker do jitter: 0.0, transit: nil, received: 0, + discarded: 0, cycles: 0, - max_seq: nil, base_seq: nil, + max_seq: nil, received_prior: 0, expected_prior: 0, lost: 0, - fraction_lost: 0.0, - discarded: 0 + fraction_lost: 0.0 ] end @@ -80,7 +81,7 @@ defmodule Membrane.RTP.PacketTracker do seq_num = buffer.metadata.rtp.sequence_number max_seq = max_seq || seq_num - 1 - delta = rem(seq_num - max_seq + @max_seq_num, @max_seq_num + 1) + delta = rem(seq_num - max_seq + @max_seq_num + 1, @max_seq_num + 1) cond do # greater sequence number but within dropout to ensure that it is not from previous cycle @@ -122,12 +123,7 @@ defmodule Membrane.RTP.PacketTracker do expected = expected_packets(state) - lost = - if expected > received do - expected - received - else - 0 - end + lost = max(expected - received, 0) expected_interval = expected - expected_prior received_interval = received - received_prior @@ -160,8 +156,7 @@ defmodule Membrane.RTP.PacketTracker do fraction_lost: fraction_lost, total_lost: total_lost, highest_seq_num: max_seq + cycles, - # interarrival_jitter: jitter - interarrival_jitter: 0.010 + interarrival_jitter: jitter } {{:ok, event: {:input, %ReceiverReport.StatsEvent{stats: stats}}}, state} @@ -235,7 +230,7 @@ defmodule Membrane.RTP.PacketTracker do %State{discarded: discarded} ) do metadata = - put_in(metadata, [:rtp, :sequence_number], rem(seq_num - discarded, @max_seq_num + 1)) + put_in(metadata, [:rtp, :sequence_number], rem(seq_num - discarded + @max_seq_num + 1, @max_seq_num + 1)) %Buffer{buffer | metadata: metadata} end diff --git a/lib/membrane/rtp/random_dropper.ex b/lib/membrane/rtp/random_dropper.ex deleted file mode 100644 index 8a81bc36..00000000 --- a/lib/membrane/rtp/random_dropper.ex +++ /dev/null @@ -1,31 +0,0 @@ -defmodule Membrane.RTP.RandomDropper do - use Membrane.Filter - - def_input_pad :input, demand_unit: :buffers, caps: :any - def_output_pad :output, caps: :any - - def_options drop_rate: [ - spec: float(), - default: 0, - description: "Probability for a buffer to get dropped in range (0, 1)" - ] - - @impl true - def handle_init(opts) do - {:ok, %{drop_rate: opts.drop_rate}} - end - - @impl true - def handle_demand(:output, size, :buffers, _ctx, state) do - {{:ok, demand: {:input, size}}, state} - end - - @impl true - def handle_process(:input, buffer, _ctx, state) do - if :rand.uniform() < state.drop_rate do - {{:ok, redemand: :output}, state} - else - {{:ok, buffer: {:output, buffer}, redemand: :output}, state} - end - end -end diff --git a/lib/membrane/rtp/session_bin.ex b/lib/membrane/rtp/session_bin.ex index 11197319..58d84b18 100644 --- a/lib/membrane/rtp/session_bin.ex +++ b/lib/membrane/rtp/session_bin.ex @@ -22,8 +22,8 @@ defmodule Membrane.RTP.SessionBin do #### Jitter Buffer Some depayloaders may need the incoming packets to be in a correct order therefore one needs to not only - use depayloader but also include a jitter buffer. It can be specified as pad's option `use_jitter_buffer?` when - adding `Pad.ref(:output, ssrc)` pad, . + use depayloader but also include a jitter buffer. It can be specified via pad's option `use_jitter_buffer?` when + adding `Pad.ref(:output, ssrc)` pad. #### Important note @@ -419,12 +419,10 @@ defmodule Membrane.RTP.SessionBin do pads_present? = Enum.all?([input_pad, output_pad], &Map.has_key?(ctx.pads, &1)) + # if one of the pads is missing do nothing and wait for the other pad to be added if not pads_present? or Map.has_key?(ctx.children, {:stream_send_bin, ssrc}) do {:ok, state} else - input_pad = Pad.ref(:input, ssrc) - output_pad = Pad.ref(:rtp_output, ssrc) - %{use_payloader?: use_payloader?} = ctx.pads[input_pad].options %{encoding: encoding_name, clock_rate: clock_rate} = ctx.pads[output_pad].options @@ -433,34 +431,28 @@ defmodule Membrane.RTP.SessionBin do clock_rate = clock_rate || get_from_register!(:clock_rate, payload_type, state) payloader = if use_payloader?, do: get_payloader!(encoding_name, state), else: nil - spec = sent_stream_spec(ssrc, payload_type, payloader, clock_rate, state) + maybe_link_encryptor = + &to(&1, {:srtp_encryptor, ssrc}, %SRTP.Encryptor{policies: state.srtp_policies}) + + links = [ + link_bin_input(input_pad) + |> to({:stream_send_bin, ssrc}, %RTP.StreamSendBin{ + ssrc: ssrc, + payload_type: payload_type, + payloader: payloader, + clock_rate: clock_rate + }) + |> then(if state.secure?, do: maybe_link_encryptor, else: & &1) + |> to_bin_output(output_pad) + ] + + spec = %ParentSpec{links: links} state = %{state | senders_ssrcs: MapSet.put(state.senders_ssrcs, ssrc)} {{:ok, spec: spec}, state} end end - defp sent_stream_spec(ssrc, payload_type, payloader, clock_rate, %{ - secure?: secure?, - srtp_policies: policies - }) do - maybe_link_encryptor = &to(&1, {:srtp_encryptor, ssrc}, %SRTP.Encryptor{policies: policies}) - - links = [ - link_bin_input(Pad.ref(:input, ssrc)) - |> to({:stream_send_bin, ssrc}, %RTP.StreamSendBin{ - ssrc: ssrc, - payload_type: payload_type, - payloader: payloader, - clock_rate: clock_rate - }) - |> then(if secure?, do: maybe_link_encryptor, else: & &1) - |> to_bin_output(Pad.ref(:rtp_output, ssrc)) - ] - - %ParentSpec{links: links} - end - @impl true def handle_pad_removed(Pad.ref(:rtp_input, ref), ctx, state) do children = diff --git a/lib/membrane/rtp/stream_receive_bin.ex b/lib/membrane/rtp/stream_receive_bin.ex index f452c0c9..9048e1ba 100644 --- a/lib/membrane/rtp/stream_receive_bin.ex +++ b/lib/membrane/rtp/stream_receive_bin.ex @@ -58,7 +58,6 @@ defmodule Membrane.RTP.StreamReceiveBin do local_ssrc: opts.local_ssrc, remote_ssrc: opts.remote_ssrc, report_interval: opts.rtcp_report_interval, - # report_interval: 5 * Membrane.Time.second(), fir_interval: opts.rtcp_fir_interval }) |> to(:tracker, %Membrane.RTP.PacketTracker{ diff --git a/lib/membrane/rtp/stream_send_bin.ex b/lib/membrane/rtp/stream_send_bin.ex index 3e0837fe..48932958 100644 --- a/lib/membrane/rtp/stream_send_bin.ex +++ b/lib/membrane/rtp/stream_send_bin.ex @@ -21,6 +21,9 @@ defmodule Membrane.RTP.StreamSendBin do links = [ link_bin_input() |> then(if opts.payloader != nil, do: maybe_link_payloader, else: & &1) + # TODO: do we event need the serializer in case we don't provide the payloader? + # its main responsibility previously was to rewrite sequence numbers to match the outgoing packets + # and to generate some stats |> to(:serializer, %RTP.Serializer{ ssrc: opts.ssrc, payload_type: opts.payload_type, diff --git a/test/membrane/rtp/jitter_buffer_test.exs b/test/membrane/rtp/jitter_buffer_test.exs index 442b5d67..1ea8309f 100644 --- a/test/membrane/rtp/jitter_buffer_test.exs +++ b/test/membrane/rtp/jitter_buffer_test.exs @@ -9,7 +9,6 @@ defmodule Membrane.RTP.JitterBufferTest do setup_all do buffer = BufferFactory.sample_buffer(@base_seq_number) - # {:ok, store} = BufferStore.insert_buffer(%BufferStore{}, buffer) state = %State{ clock_rate: BufferFactory.clock_rate(), store: %BufferStore{}, @@ -42,40 +41,6 @@ defmodule Membrane.RTP.JitterBufferTest do assert {%Record{buffer: ^buffer}, new_store} = BufferStore.shift(store) assert BufferStore.dump(new_store) == [] end - - # TODO: change that so that those tests are placed in PacketTracker instead of jitter buffer tests - # test "jitter stats are updated", %{state: state, buffer: buffer} do - # ts = ~U[2020-06-19 19:06:00Z] |> DateTime.to_unix() |> Membrane.Time.seconds() - - # timestamped_buf = put_in(buffer.metadata[:arrival_ts], ts) - # assert {:ok, state} = JitterBuffer.handle_process(:input, timestamped_buf, nil, state) - # assert state.stats_acc.jitter == 0.0 - - # assert state.stats_acc.last_transit == - # Membrane.Time.to_seconds(ts) * state.clock_rate - - # timestamped_buf.metadata.rtp.timestamp - - # buffer = BufferFactory.sample_buffer(@base_seq_number + 1) - - # arrival_ts_increment = - # div(BufferFactory.timestamp_increment(), state.clock_rate) |> Membrane.Time.seconds() - - # packet_delay = 1 |> Membrane.Time.seconds() - - # timestamped_buf = - # put_in(buffer.metadata[:arrival_ts], ts + arrival_ts_increment + packet_delay) - - # assert {:ok, state} = JitterBuffer.handle_process(:input, timestamped_buf, nil, state) - - # # 16 is defined by RFC - # assert state.stats_acc.jitter == - # Membrane.Time.to_seconds(packet_delay) * state.clock_rate / 16 - - # assert state.stats_acc.last_transit == - # Membrane.Time.to_seconds(ts + arrival_ts_increment + packet_delay) * - # state.clock_rate - - # timestamped_buf.metadata.rtp.timestamp - # end end describe "When new buffer arrives when not waiting and already pushed some buffer" do @@ -145,7 +110,7 @@ defmodule Membrane.RTP.JitterBufferTest do describe "When event arrives" do test "dumps store if event was end of stream", %{state: state, buffer: buffer} do - store = %BufferStore{state.store | prev_index: @base_seq_number - 2, base_index: 0} + store = %BufferStore{state.store | prev_index: @base_seq_number - 2} {:ok, store} = BufferStore.insert_buffer(store, buffer) state = %{state | store: store} assert {{:ok, actions}, _state} = JitterBuffer.handle_end_of_stream(:input, nil, state) diff --git a/test/membrane/rtp/packet_tracker_test.exs b/test/membrane/rtp/packet_tracker_test.exs index f6b064bd..eefef1c2 100644 --- a/test/membrane/rtp/packet_tracker_test.exs +++ b/test/membrane/rtp/packet_tracker_test.exs @@ -2,9 +2,80 @@ defmodule Membrane.RTP.PacketTrackerTest do use ExUnit.Case, async: true use Bunch + alias Membrane.RTP.PacketTracker + alias Membrane.RTP.BufferFactory + # @seq_number_limit 65_536 # @base_index 65_505 # @next_index @base_index + 1 + @base_seq_number BufferFactory.base_seq_number() + @max_seq_number 0xFFFF # TODO: write tests for the packet tracker gathering stats and repairing packets + + + + describe "PacketTracer should" do + setup do + buffer = BufferFactory.sample_buffer(@base_seq_number) + state = %PacketTracker.State{ + clock_rate: BufferFactory.clock_rate(), + repair_sequence_numbers?: true + } + + [state: state, buffer: buffer] + end + + test "update stats accordingly when receiving new buffers", %{state: state, buffer: buffer} do + ts = ~U[2020-06-19 19:06:00Z] |> DateTime.to_unix() |> Membrane.Time.seconds() + + timestamped_buf = put_in(buffer.metadata[:arrival_ts], ts) + assert {{:ok, _actions}, state} = PacketTracker.handle_process(:input, timestamped_buf, nil, state) + assert state.jitter == 0.0 + + assert state.transit == + Membrane.Time.to_seconds(ts) * state.clock_rate - + timestamped_buf.metadata.rtp.timestamp + + buffer = BufferFactory.sample_buffer(@base_seq_number + 1) + + arrival_ts_increment = + div(BufferFactory.timestamp_increment(), state.clock_rate) |> Membrane.Time.seconds() + + packet_delay = 1 |> Membrane.Time.seconds() + + timestamped_buf = + put_in(buffer.metadata[:arrival_ts], ts + arrival_ts_increment + packet_delay) + + assert {{:ok, _actions}, state} = PacketTracker.handle_process(:input, timestamped_buf, nil, state) + + # 16 is defined by RFC + assert state.jitter == + Membrane.Time.to_seconds(packet_delay) * state.clock_rate / 16 + + assert state.transit == + Membrane.Time.to_seconds(ts + arrival_ts_increment + packet_delay) * + state.clock_rate - + timestamped_buf.metadata.rtp.timestamp + end + + test "update packet's sequence number if there have been discarded packets", %{state: state} do + state = %PacketTracker.State{state | discarded: 10} + + # in sequence number range + buffer = BufferFactory.sample_buffer(100) + assert {{:ok, [buffer: {:output, buffer}]}, state} = PacketTracker.handle_process(:input, buffer, nil, state) + assert buffer.metadata.rtp.sequence_number == 90 + + # border case where sequence number over rolled + buffer = BufferFactory.sample_buffer(5) + assert {{:ok, [buffer: {:output, buffer}]}, state} = PacketTracker.handle_process(:input, buffer, nil, state) + assert buffer.metadata.rtp.sequence_number == @max_seq_number - 5 + 1 + + state = %PacketTracker.State{state | repair_sequence_numbers?: false} + buffer = BufferFactory.sample_buffer(100) + assert {{:ok, [buffer: {:output, buffer}]}, _state} = PacketTracker.handle_process(:input, buffer, nil, state) + assert buffer.metadata.rtp.sequence_number == 100 + end + end end From 23add82e9850ffee2da8ad111c0eb6b0ca7209bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Per=C5=BCy=C5=82o?= Date: Mon, 6 Sep 2021 15:37:08 +0200 Subject: [PATCH 07/20] Applied credo suggestions and formated code --- lib/membrane/rtp/jitter_buffer.ex | 5 +++-- lib/membrane/rtp/packet_tracker.ex | 8 ++++++- test/membrane/rtp/jitter_buffer_test.exs | 1 + test/membrane/rtp/packet_tracker_test.exs | 26 +++++++++++++++++------ 4 files changed, 30 insertions(+), 10 deletions(-) diff --git a/lib/membrane/rtp/jitter_buffer.ex b/lib/membrane/rtp/jitter_buffer.ex index a14a0ddb..0d8c2771 100644 --- a/lib/membrane/rtp/jitter_buffer.ex +++ b/lib/membrane/rtp/jitter_buffer.ex @@ -6,7 +6,7 @@ defmodule Membrane.RTP.JitterBuffer do use Membrane.Log use Bunch - alias Membrane.{Buffer, RTP, Time} + alias Membrane.{RTP, Time} alias __MODULE__.{BufferStore, Record} @type packet_index :: non_neg_integer() @@ -64,7 +64,8 @@ defmodule Membrane.RTP.JitterBuffer do @impl true def handle_start_of_stream(:input, _context, state) do Process.send_after( - self(), :initial_latency_passed, + self(), + :initial_latency_passed, state.latency |> Time.to_milliseconds() ) diff --git a/lib/membrane/rtp/packet_tracker.ex b/lib/membrane/rtp/packet_tracker.ex index 280bfe58..874af470 100644 --- a/lib/membrane/rtp/packet_tracker.ex +++ b/lib/membrane/rtp/packet_tracker.ex @@ -32,6 +32,8 @@ defmodule Membrane.RTP.PacketTracker do ] defmodule State do + @moduledoc false + @type t :: %__MODULE__{ clock_rate: non_neg_integer(), repair_sequence_numbers?: boolean(), @@ -230,7 +232,11 @@ defmodule Membrane.RTP.PacketTracker do %State{discarded: discarded} ) do metadata = - put_in(metadata, [:rtp, :sequence_number], rem(seq_num - discarded + @max_seq_num + 1, @max_seq_num + 1)) + put_in( + metadata, + [:rtp, :sequence_number], + rem(seq_num - discarded + @max_seq_num + 1, @max_seq_num + 1) + ) %Buffer{buffer | metadata: metadata} end diff --git a/test/membrane/rtp/jitter_buffer_test.exs b/test/membrane/rtp/jitter_buffer_test.exs index 1ea8309f..9b3ed6a0 100644 --- a/test/membrane/rtp/jitter_buffer_test.exs +++ b/test/membrane/rtp/jitter_buffer_test.exs @@ -9,6 +9,7 @@ defmodule Membrane.RTP.JitterBufferTest do setup_all do buffer = BufferFactory.sample_buffer(@base_seq_number) + state = %State{ clock_rate: BufferFactory.clock_rate(), store: %BufferStore{}, diff --git a/test/membrane/rtp/packet_tracker_test.exs b/test/membrane/rtp/packet_tracker_test.exs index eefef1c2..e47f094b 100644 --- a/test/membrane/rtp/packet_tracker_test.exs +++ b/test/membrane/rtp/packet_tracker_test.exs @@ -13,11 +13,10 @@ defmodule Membrane.RTP.PacketTrackerTest do # TODO: write tests for the packet tracker gathering stats and repairing packets - - describe "PacketTracer should" do setup do buffer = BufferFactory.sample_buffer(@base_seq_number) + state = %PacketTracker.State{ clock_rate: BufferFactory.clock_rate(), repair_sequence_numbers?: true @@ -30,7 +29,10 @@ defmodule Membrane.RTP.PacketTrackerTest do ts = ~U[2020-06-19 19:06:00Z] |> DateTime.to_unix() |> Membrane.Time.seconds() timestamped_buf = put_in(buffer.metadata[:arrival_ts], ts) - assert {{:ok, _actions}, state} = PacketTracker.handle_process(:input, timestamped_buf, nil, state) + + assert {{:ok, _actions}, state} = + PacketTracker.handle_process(:input, timestamped_buf, nil, state) + assert state.jitter == 0.0 assert state.transit == @@ -47,7 +49,8 @@ defmodule Membrane.RTP.PacketTrackerTest do timestamped_buf = put_in(buffer.metadata[:arrival_ts], ts + arrival_ts_increment + packet_delay) - assert {{:ok, _actions}, state} = PacketTracker.handle_process(:input, timestamped_buf, nil, state) + assert {{:ok, _actions}, state} = + PacketTracker.handle_process(:input, timestamped_buf, nil, state) # 16 is defined by RFC assert state.jitter == @@ -64,17 +67,26 @@ defmodule Membrane.RTP.PacketTrackerTest do # in sequence number range buffer = BufferFactory.sample_buffer(100) - assert {{:ok, [buffer: {:output, buffer}]}, state} = PacketTracker.handle_process(:input, buffer, nil, state) + + assert {{:ok, [buffer: {:output, buffer}]}, state} = + PacketTracker.handle_process(:input, buffer, nil, state) + assert buffer.metadata.rtp.sequence_number == 90 # border case where sequence number over rolled buffer = BufferFactory.sample_buffer(5) - assert {{:ok, [buffer: {:output, buffer}]}, state} = PacketTracker.handle_process(:input, buffer, nil, state) + + assert {{:ok, [buffer: {:output, buffer}]}, state} = + PacketTracker.handle_process(:input, buffer, nil, state) + assert buffer.metadata.rtp.sequence_number == @max_seq_number - 5 + 1 state = %PacketTracker.State{state | repair_sequence_numbers?: false} buffer = BufferFactory.sample_buffer(100) - assert {{:ok, [buffer: {:output, buffer}]}, _state} = PacketTracker.handle_process(:input, buffer, nil, state) + + assert {{:ok, [buffer: {:output, buffer}]}, _state} = + PacketTracker.handle_process(:input, buffer, nil, state) + assert buffer.metadata.rtp.sequence_number == 100 end end From 8663527ddbcd0d479ad06f50ffc9a5809ef84333 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Per=C5=BCy=C5=82o?= Date: Mon, 4 Oct 2021 16:47:19 +0200 Subject: [PATCH 08/20] Moved payloading and depayloading to bins --- lib/membrane/rtp/depayloader_bin.ex | 38 +++++++ ...t_tracker.ex => inbound_packet_tracker.ex} | 2 +- lib/membrane/rtp/outband_packet_tracker.ex | 94 ++++++++++++++++ lib/membrane/rtp/payloader_bin.ex | 46 ++++++++ lib/membrane/rtp/serializer.ex | 100 ++++-------------- lib/membrane/rtp/session_bin.ex | 16 +-- lib/membrane/rtp/stream_receive_bin.ex | 22 ++-- lib/membrane/rtp/stream_send_bin.ex | 24 +++-- ...st.exs => inbound_packet_tracker_test.exs} | 22 ++-- test/membrane/rtp/session_bin_test.exs | 2 +- test/membrane/rtp/stream_receive_bin_test.exs | 2 - 11 files changed, 237 insertions(+), 131 deletions(-) create mode 100644 lib/membrane/rtp/depayloader_bin.ex rename lib/membrane/rtp/{packet_tracker.ex => inbound_packet_tracker.ex} (99%) create mode 100644 lib/membrane/rtp/outband_packet_tracker.ex create mode 100644 lib/membrane/rtp/payloader_bin.ex rename test/membrane/rtp/{packet_tracker_test.exs => inbound_packet_tracker_test.exs} (77%) diff --git a/lib/membrane/rtp/depayloader_bin.ex b/lib/membrane/rtp/depayloader_bin.ex new file mode 100644 index 00000000..eb4a4ef5 --- /dev/null +++ b/lib/membrane/rtp/depayloader_bin.ex @@ -0,0 +1,38 @@ +defmodule Membrane.RTP.DepayloaderBin do + @moduledoc """ + Modules responsible for reordering incoming RTP packets using a jitter buffer + to later depayload packet's payload from RTP format. + """ + + use Membrane.Bin + + alias Membrane.RTP.JitterBuffer + + def_options depayloader: [ + spec: module(), + description: "Depayloader module that should be used for incoming stream" + ], + clock_rate: [ + spec: RTP.clock_rate_t() + ] + + def_input_pad :input, + caps: RTP, + demand_unit: :buffers + + def_output_pad :output, + caps: RTP, + demand_unit: :buffers + + @impl true + def handle_init(opts) do + links = [ + link_bin_input() + |> to(:jitter_buffer, %JitterBuffer{clock_rate: opts.clock_rate}) + |> to(:depayloader, opts.depayloader) + |> to_bin_output() + ] + + {{:ok, spec: %ParentSpec{links: links}}, %{}} + end +end diff --git a/lib/membrane/rtp/packet_tracker.ex b/lib/membrane/rtp/inbound_packet_tracker.ex similarity index 99% rename from lib/membrane/rtp/packet_tracker.ex rename to lib/membrane/rtp/inbound_packet_tracker.ex index 874af470..0e932530 100644 --- a/lib/membrane/rtp/packet_tracker.ex +++ b/lib/membrane/rtp/inbound_packet_tracker.ex @@ -1,4 +1,4 @@ -defmodule Membrane.RTP.PacketTracker do +defmodule Membrane.RTP.InboundPacketTracker do @moduledoc """ Module responsible for tracking statistics of incoming RTP packets for a single stream. diff --git a/lib/membrane/rtp/outband_packet_tracker.ex b/lib/membrane/rtp/outband_packet_tracker.ex new file mode 100644 index 00000000..ae96fa43 --- /dev/null +++ b/lib/membrane/rtp/outband_packet_tracker.ex @@ -0,0 +1,94 @@ +defmodule Membrane.RTP.OutbandPacketTracker do + @moduledoc """ + Tracks statistics of outband packets. + """ + use Membrane.Filter + + alias Membrane.{Buffer, RTP, RemoteStream, Payload, Time} + + def_input_pad :input, + caps: {RemoteStream, type: :packetized, content_format: one_of([nil, RTP])}, + demand_unit: :buffers + + def_output_pad :output, + caps: {RemoteStream, type: :packetized, content_format: one_of([nil, RTP])} + + def_options ssrc: [spec: RTP.ssrc_t()], + payload_type: [spec: RTP.payload_type_t()], + clock_rate: [spec: RTP.clock_rate_t()], + alignment: [ + default: 1, + spec: pos_integer(), + description: """ + Number of bytes that each packet should be aligned to. + Alignment is achieved by adding RTP padding. + """ + ] + + defmodule State do + @moduledoc false + use Bunch.Access + + @type t :: %__MODULE__{ + any_buffer_sent?: boolean(), + stats_acc: %{} + } + + defstruct any_buffer_sent?: false, + stats_acc: %{ + clock_rate: 0, + timestamp: 0, + rtp_timestamp: 0, + sender_packet_count: 0, + sender_octet_count: 0 + } + end + + @impl true + def handle_init(options) do + state = %State{} + + state = state |> put_in([:stats_acc, :clock_rate], options.clock_rate) + {:ok, Map.merge(Map.from_struct(options), state)} + end + + @impl true + def handle_demand(:output, size, :buffers, _ctx, state) do + {{:ok, demand: {:input, size}}, state} + end + + @impl true + def handle_process(:input, %Buffer{} = buffer, _ctx, state) do + state = update_stats(buffer, state) + + {{:ok, buffer: {:output, buffer}}, state} + end + + @impl true + def handle_other(:send_stats, _ctx, state) do + stats = get_stats(state) + state = %{state | any_buffer_sent?: false} + {{:ok, notify: {:outband_stats, stats}}, state} + end + + @spec get_stats(State.t()) :: map() | :no_stats + defp get_stats(%State{any_buffer_sent?: false}), do: :no_stats + defp get_stats(%State{stats_acc: stats}), do: stats + + defp update_stats(%Buffer{payload: payload, metadata: metadata}, state) do + %{ + sender_octet_count: octet_count, + sender_packet_count: packet_count + } = state.stats_acc + + updated_stats = %{ + clock_rate: state.stats_acc.clock_rate, + sender_octet_count: octet_count + Payload.size(payload), + sender_packet_count: packet_count + 1, + timestamp: Time.vm_time(), + rtp_timestamp: metadata.rtp.timestamp + } + + Map.put(state, :stats_acc, updated_stats) + end +end diff --git a/lib/membrane/rtp/payloader_bin.ex b/lib/membrane/rtp/payloader_bin.ex new file mode 100644 index 00000000..3790a9be --- /dev/null +++ b/lib/membrane/rtp/payloader_bin.ex @@ -0,0 +1,46 @@ +defmodule Membrane.RTP.PayloaderBin do + @moduledoc """ + Module responsible for payloading a stream to RTP format and preparing RTP headers. + """ + + use Membrane.Bin + + alias Membrane.ParentSpec + + def_input_pad :input, demand_unit: :buffers, caps: :any + + def_output_pad :output, caps: :any, demand_unit: :buffers + + def_options payloader: [ + spec: module(), + description: "Payloader module used for payloading a stream to RTP format" + ], + ssrc: [spec: RTP.ssrc_t()], + payload_type: [spec: RTP.payload_type_t()], + clock_rate: [spec: RTP.clock_rate_t()], + alignment: [ + default: 1, + spec: pos_integer(), + description: """ + Number of bytes that each packet should be aligned to. + Alignment is achieved by adding RTP padding. + """ + ] + + @impl true + def handle_init(opts) do + links = [ + link_bin_input() + |> to(:payloader, opts.payloader) + |> to(:serializer, %Membrane.RTP.Serializer{ + ssrc: opts.ssrc, + payload_type: opts.payload_type, + clock_rate: opts.clock_rate, + alignment: opts.alignment + }) + |> to_bin_output() + ] + + {{:ok, spec: %ParentSpec{links: links}}, %{}} + end +end diff --git a/lib/membrane/rtp/serializer.ex b/lib/membrane/rtp/serializer.ex index 9ca8497c..48be1879 100644 --- a/lib/membrane/rtp/serializer.ex +++ b/lib/membrane/rtp/serializer.ex @@ -7,7 +7,7 @@ defmodule Membrane.RTP.Serializer do """ use Membrane.Filter - alias Membrane.{Buffer, RTP, RemoteStream, Payload, Time} + alias Membrane.{Buffer, RTP, RemoteStream} @max_seq_num 65_535 @max_timestamp 0xFFFFFFFF @@ -25,17 +25,6 @@ defmodule Membrane.RTP.Serializer do Number of bytes that each packet should be aligned to. Alignment is achieved by adding RTP padding. """ - ], - generate_seq_num_and_timestamp?: [ - spec: boolean(), - default: true, - description: """ - Determines if sequence number and timestamp should be sequentially generated by the serializer. - If set to false then serializer will take already existing sequence number and timestamp from incoming buffer's metadata. - - ## Note - Generation should be used only when incoming buffers are ensured to be ordered. - """ ] defmodule State do @@ -43,23 +32,11 @@ defmodule Membrane.RTP.Serializer do use Bunch.Access defstruct sequence_number: 0, - init_timestamp: 0, - any_buffer_sent?: false, - generate_seq_num_and_timestamp?: true, - stats_acc: %{ - clock_rate: 0, - timestamp: 0, - rtp_timestamp: 0, - sender_packet_count: 0, - sender_octet_count: 0 - } + init_timestamp: 0 @type t :: %__MODULE__{ sequence_number: non_neg_integer(), - init_timestamp: non_neg_integer(), - any_buffer_sent?: boolean(), - generate_seq_num_and_timestamp?: boolean(), - stats_acc: %{} + init_timestamp: non_neg_integer() } end @@ -67,11 +44,9 @@ defmodule Membrane.RTP.Serializer do def handle_init(options) do state = %State{ sequence_number: Enum.random(0..@max_seq_num), - init_timestamp: Enum.random(0..@max_timestamp), - generate_seq_num_and_timestamp?: options.generate_seq_num_and_timestamp? + init_timestamp: Enum.random(0..@max_timestamp) } - state = state |> put_in([:stats_acc, :clock_rate], options.clock_rate) {:ok, Map.merge(Map.from_struct(options), state)} end @@ -87,75 +62,40 @@ defmodule Membrane.RTP.Serializer do end @impl true - def handle_process(:input, %Buffer{payload: payload, metadata: metadata} = buffer, _ctx, state) do - state = update_counters(buffer, state) - + def handle_process(:input, %Buffer{payload: payload, metadata: metadata}, _ctx, state) do {rtp_metadata, metadata} = Map.pop(metadata, :rtp, %{}) - {rtp_timestamp, sequence_number, state} = maybe_rewrite_params(rtp_metadata, metadata, state) + rtp_offset = + rtp_metadata + |> buffer_timestamp(metadata) + |> Ratio.mult(state.clock_rate) + |> Membrane.Time.to_seconds() + + rtp_timestamp = rem(state.init_timestamp + rtp_offset, @max_timestamp + 1) + + state = Map.update!(state, :sequence_number, &rem(&1 + 1, @max_seq_num + 1)) header = %RTP.Header{ ssrc: state.ssrc, marker: Map.get(rtp_metadata, :marker, false), payload_type: state.payload_type, timestamp: rtp_timestamp, - sequence_number: sequence_number, + sequence_number: state.sequence_number, csrcs: Map.get(rtp_metadata, :csrcs, []), extension: Map.get(rtp_metadata, :extension) } packet = %RTP.Packet{header: header, payload: payload} payload = RTP.Packet.serialize(packet, align_to: state.alignment) - buffer = %Buffer{payload: payload, metadata: metadata} - state = %{ - state - | any_buffer_sent?: true, - stats_acc: %{state.stats_acc | timestamp: Time.vm_time(), rtp_timestamp: rtp_timestamp} + buffer = %Buffer{ + payload: payload, + metadata: Map.put(metadata, :rtp, %{timestamp: rtp_timestamp}) } {{:ok, buffer: {:output, buffer}}, state} end - @impl true - def handle_other(:send_stats, _ctx, state) do - stats = get_stats(state) - state = %{state | any_buffer_sent?: false} - {{:ok, notify: {:serializer_stats, stats}}, state} - end - - @spec get_stats(State.t()) :: %{} | :no_stats - defp get_stats(%State{any_buffer_sent?: false}), do: :no_stats - - defp get_stats(%State{stats_acc: stats}), do: stats - - defp update_counters(%Buffer{payload: payload}, state) do - state - |> update_in( - [:stats_acc, :sender_octet_count], - &(&1 + Payload.size(payload)) - ) - |> update_in([:stats_acc, :sender_packet_count], &(&1 + 1)) - end - - defp maybe_rewrite_params( - %{timestamp: timestamp, sequence_number: sequence_number}, - _metadata, - %{generate_seq_num_and_timestamp?: false} = state - ) do - {timestamp, sequence_number, state} - end - - defp maybe_rewrite_params( - _rtp_metadata, - %{timestamp: timestamp}, - %{generate_seq_num_and_timestamp?: true, sequence_number: seq_num} = state - ) do - rtp_offset = timestamp |> Ratio.mult(state.clock_rate) |> Membrane.Time.to_seconds() - rtp_timestamp = rem(state.init_timestamp + rtp_offset, @max_timestamp + 1) - - state = Map.update!(state, :sequence_number, &rem(&1 + 1, @max_seq_num + 1)) - - {rtp_timestamp, seq_num, state} - end + def buffer_timestamp(%{rtp: %{timestamp: timestamp}}, _metadata), do: timestamp + def buffer_timestamp(_rtp_metadata, %{timestamp: timestamp}), do: timestamp end diff --git a/lib/membrane/rtp/session_bin.ex b/lib/membrane/rtp/session_bin.ex index 58d84b18..c0bce23c 100644 --- a/lib/membrane/rtp/session_bin.ex +++ b/lib/membrane/rtp/session_bin.ex @@ -180,13 +180,9 @@ defmodule Membrane.RTP.SessionBin do default: true, description: """ Defines whether the incoming stream should be depayloaded from RTP stream. - """ - ], - use_jitter_buffer?: [ - spec: boolean(), - default: true, - description: """ - Defines whether jitter buffer should be used for the incoming RTP stream. + + Depayloading stream goes through `Membrane.RTP.DepayloaderBin` which besides creating a depayloader instance will + also use a jitter buffer. """ ], clock_rate: [ @@ -346,7 +342,6 @@ defmodule Membrane.RTP.SessionBin do %{ encoding: encoding_name, use_depayloader?: use_depayloader?, - use_jitter_buffer?: use_jitter_buffer?, clock_rate: clock_rate, extensions: extensions, rtcp_fir_interval: fir_interval, @@ -354,10 +349,11 @@ defmodule Membrane.RTP.SessionBin do } = ctx.pads[pad].options payload_type = Map.fetch!(state.ssrc_pt_mapping, ssrc) - encoding_name = encoding_name || get_from_register!(:encoding_name, payload_type, state) clock_rate = clock_rate || get_from_register!(:clock_rate, payload_type, state) + depayloader = if use_depayloader?, do: get_depayloader!(encoding_name, state), else: nil + {local_ssrc, state} = add_ssrc(ssrc, state) rtp_stream_name = {:stream_receive_bin, ssrc} @@ -366,8 +362,6 @@ defmodule Membrane.RTP.SessionBin do rtp_stream_name => %RTP.StreamReceiveBin{ clock_rate: clock_rate, depayloader: depayloader, - jitter_buffer: - if(use_jitter_buffer?, do: %Membrane.RTP.JitterBuffer{clock_rate: clock_rate}, else: nil), filters: filters, local_ssrc: local_ssrc, remote_ssrc: ssrc, diff --git a/lib/membrane/rtp/stream_receive_bin.ex b/lib/membrane/rtp/stream_receive_bin.ex index 9048e1ba..eea2444f 100644 --- a/lib/membrane/rtp/stream_receive_bin.ex +++ b/lib/membrane/rtp/stream_receive_bin.ex @@ -22,18 +22,11 @@ defmodule Membrane.RTP.StreamReceiveBin do spec: [Membrane.RTP.SessionBin.packet_filter_t()], default: [] ], - jitter_buffer: [ - spec: module() | struct() | nil, - default: nil - ], - depayloader: [ - spec: module() | nil, - default: nil - ], clock_rate: [ type: :integer, spec: RTP.clock_rate_t() ], + depayloader: [spec: module() | nil], local_ssrc: [spec: Membrane.RTP.ssrc_t()], remote_ssrc: [spec: Membrane.RTP.ssrc_t()], rtcp_report_interval: [spec: Membrane.Time.t() | nil], @@ -47,9 +40,11 @@ defmodule Membrane.RTP.StreamReceiveBin do maybe_link_decryptor = &to(&1, :decryptor, %Membrane.SRTP.Decryptor{policies: opts.srtp_policies}) - maybe_link_jitter_buffer = &to(&1, :jitter_buffer, opts.jitter_buffer) - - maybe_link_depayloader = &to(&1, :depayloader, opts.depayloader) + maybe_link_depayloader_bin = + &to(&1, :depayloader, %Membrane.RTP.DepayloaderBin{ + depayloader: opts.depayloader, + clock_rate: opts.clock_rate + }) links = [ link_bin_input() @@ -60,13 +55,12 @@ defmodule Membrane.RTP.StreamReceiveBin do report_interval: opts.rtcp_report_interval, fir_interval: opts.rtcp_fir_interval }) - |> to(:tracker, %Membrane.RTP.PacketTracker{ + |> to(:tracker, %Membrane.RTP.InboundPacketTracker{ clock_rate: opts.clock_rate, repair_sequence_numbers?: true }) - |> then(if opts.jitter_buffer != nil, do: maybe_link_jitter_buffer, else: & &1) |> then(if opts.secure?, do: maybe_link_decryptor, else: & &1) - |> then(if opts.depayloader != nil, do: maybe_link_depayloader, else: & &1) + |> then(if opts.depayloader, do: maybe_link_depayloader_bin, else: & &1) |> to_bin_output() ] diff --git a/lib/membrane/rtp/stream_send_bin.ex b/lib/membrane/rtp/stream_send_bin.ex index 48932958..a40e7bcc 100644 --- a/lib/membrane/rtp/stream_send_bin.ex +++ b/lib/membrane/rtp/stream_send_bin.ex @@ -16,19 +16,21 @@ defmodule Membrane.RTP.StreamSendBin do @impl true def handle_init(opts) do - maybe_link_payloader = &to(&1, :payloader, opts.payloader) + maybe_link_payloader_bin = + &to(&1, :payloader, %RTP.PayloaderBin{ + payloader: opts.payloader, + ssrc: opts.ssrc, + clock_rate: opts.clock_rate, + payload_type: opts.payload_type + }) links = [ link_bin_input() - |> then(if opts.payloader != nil, do: maybe_link_payloader, else: & &1) - # TODO: do we event need the serializer in case we don't provide the payloader? - # its main responsibility previously was to rewrite sequence numbers to match the outgoing packets - # and to generate some stats - |> to(:serializer, %RTP.Serializer{ + |> then(if opts.payloader != nil, do: maybe_link_payloader_bin, else: & &1) + |> to(:packet_tracker, %RTP.OutbandPacketTracker{ ssrc: opts.ssrc, payload_type: opts.payload_type, - clock_rate: opts.clock_rate, - generate_seq_num_and_timestamp?: opts.payloader != nil + clock_rate: opts.clock_rate }) |> to_bin_output() ] @@ -39,11 +41,11 @@ defmodule Membrane.RTP.StreamSendBin do @impl true def handle_other(:send_stats, _ctx, state) do - {{:ok, forward: {:serializer, :send_stats}}, state} + {{:ok, forward: {:packet_tracker, :send_stats}}, state} end @impl true - def handle_notification({:serializer_stats, stats}, :serializer, _ctx, state) do - {{:ok, notify: {:serializer_stats, stats}}, state} + def handle_notification({:outbound_stats, stats}, :packet_tracker, _ctx, state) do + {{:ok, notify: {:outbound_stats, stats}}, state} end end diff --git a/test/membrane/rtp/packet_tracker_test.exs b/test/membrane/rtp/inbound_packet_tracker_test.exs similarity index 77% rename from test/membrane/rtp/packet_tracker_test.exs rename to test/membrane/rtp/inbound_packet_tracker_test.exs index e47f094b..10fcbae2 100644 --- a/test/membrane/rtp/packet_tracker_test.exs +++ b/test/membrane/rtp/inbound_packet_tracker_test.exs @@ -1,8 +1,8 @@ -defmodule Membrane.RTP.PacketTrackerTest do +defmodule Membrane.RTP.InboundPacketTrackerTest do use ExUnit.Case, async: true use Bunch - alias Membrane.RTP.PacketTracker + alias Membrane.RTP.InboundPacketTracker alias Membrane.RTP.BufferFactory # @seq_number_limit 65_536 @@ -13,11 +13,11 @@ defmodule Membrane.RTP.PacketTrackerTest do # TODO: write tests for the packet tracker gathering stats and repairing packets - describe "PacketTracer should" do + describe "PacketTracker should" do setup do buffer = BufferFactory.sample_buffer(@base_seq_number) - state = %PacketTracker.State{ + state = %InboundPacketTracker.State{ clock_rate: BufferFactory.clock_rate(), repair_sequence_numbers?: true } @@ -31,7 +31,7 @@ defmodule Membrane.RTP.PacketTrackerTest do timestamped_buf = put_in(buffer.metadata[:arrival_ts], ts) assert {{:ok, _actions}, state} = - PacketTracker.handle_process(:input, timestamped_buf, nil, state) + InboundPacketTracker.handle_process(:input, timestamped_buf, nil, state) assert state.jitter == 0.0 @@ -50,7 +50,7 @@ defmodule Membrane.RTP.PacketTrackerTest do put_in(buffer.metadata[:arrival_ts], ts + arrival_ts_increment + packet_delay) assert {{:ok, _actions}, state} = - PacketTracker.handle_process(:input, timestamped_buf, nil, state) + InboundPacketTracker.handle_process(:input, timestamped_buf, nil, state) # 16 is defined by RFC assert state.jitter == @@ -63,13 +63,13 @@ defmodule Membrane.RTP.PacketTrackerTest do end test "update packet's sequence number if there have been discarded packets", %{state: state} do - state = %PacketTracker.State{state | discarded: 10} + state = %InboundPacketTracker.State{state | discarded: 10} # in sequence number range buffer = BufferFactory.sample_buffer(100) assert {{:ok, [buffer: {:output, buffer}]}, state} = - PacketTracker.handle_process(:input, buffer, nil, state) + InboundPacketTracker.handle_process(:input, buffer, nil, state) assert buffer.metadata.rtp.sequence_number == 90 @@ -77,15 +77,15 @@ defmodule Membrane.RTP.PacketTrackerTest do buffer = BufferFactory.sample_buffer(5) assert {{:ok, [buffer: {:output, buffer}]}, state} = - PacketTracker.handle_process(:input, buffer, nil, state) + InboundPacketTracker.handle_process(:input, buffer, nil, state) assert buffer.metadata.rtp.sequence_number == @max_seq_number - 5 + 1 - state = %PacketTracker.State{state | repair_sequence_numbers?: false} + state = %InboundPacketTracker.State{state | repair_sequence_numbers?: false} buffer = BufferFactory.sample_buffer(100) assert {{:ok, [buffer: {:output, buffer}]}, _state} = - PacketTracker.handle_process(:input, buffer, nil, state) + InboundPacketTracker.handle_process(:input, buffer, nil, state) assert buffer.metadata.rtp.sequence_number == 100 end diff --git a/test/membrane/rtp/session_bin_test.exs b/test/membrane/rtp/session_bin_test.exs index 8b71c1fa..4849fa7b 100644 --- a/test/membrane/rtp/session_bin_test.exs +++ b/test/membrane/rtp/session_bin_test.exs @@ -148,7 +148,7 @@ defmodule Membrane.RTP.Session.BinTest do links: [ link(:rtp) |> via_out(Pad.ref(:output, ssrc), - options: [use_jitter_buffer?: true, use_depayloader?: true, rtcp_fir_interval: nil] + options: [use_depayloader?: true, rtcp_fir_interval: nil] ) |> to({:sink, ssrc}) ] diff --git a/test/membrane/rtp/stream_receive_bin_test.exs b/test/membrane/rtp/stream_receive_bin_test.exs index 04491409..38d669c8 100644 --- a/test/membrane/rtp/stream_receive_bin_test.exs +++ b/test/membrane/rtp/stream_receive_bin_test.exs @@ -42,7 +42,6 @@ defmodule Membrane.RTP.StreamReceiveBinTest do rtp: %StreamReceiveBin{ clock_rate: @h264_clock_rate, depayloader: H264.Depayloader, - jitter_buffer: %Membrane.RTP.JitterBuffer{clock_rate: @h264_clock_rate}, remote_ssrc: @ssrc, local_ssrc: 0, rtcp_report_interval: Membrane.Time.seconds(5), @@ -87,7 +86,6 @@ defmodule Membrane.RTP.StreamReceiveBinTest do rtp: %StreamReceiveBin{ clock_rate: @h264_clock_rate, depayloader: H264.Depayloader, - jitter_buffer: %Membrane.RTP.JitterBuffer{clock_rate: @h264_clock_rate}, local_ssrc: 0, remote_ssrc: 4_194_443_425, rtcp_report_interval: Membrane.Time.seconds(5), From 9471fdd010b76f461fec92e1fe7630944b123bb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Per=C5=BCy=C5=82o?= Date: Tue, 5 Oct 2021 10:11:58 +0200 Subject: [PATCH 09/20] Removed old comments --- lib/membrane/rtcp/receiver.ex | 2 -- test/membrane/rtp/inbound_packet_tracker_test.exs | 7 +------ 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/lib/membrane/rtcp/receiver.ex b/lib/membrane/rtcp/receiver.ex index cb523538..6717f965 100644 --- a/lib/membrane/rtcp/receiver.ex +++ b/lib/membrane/rtcp/receiver.ex @@ -80,8 +80,6 @@ defmodule Membrane.RTCP.Receiver do {:ok, state} end - # TODO: study why turning on receiver reports causes browsers (at least chrome) to start - # sending x4 more packets @impl true def handle_event(:output, %ReceiverReport.StatsEvent{stats: stats}, _ctx, state) do now = Time.vm_time() diff --git a/test/membrane/rtp/inbound_packet_tracker_test.exs b/test/membrane/rtp/inbound_packet_tracker_test.exs index 10fcbae2..36e42d40 100644 --- a/test/membrane/rtp/inbound_packet_tracker_test.exs +++ b/test/membrane/rtp/inbound_packet_tracker_test.exs @@ -5,15 +5,10 @@ defmodule Membrane.RTP.InboundPacketTrackerTest do alias Membrane.RTP.InboundPacketTracker alias Membrane.RTP.BufferFactory - # @seq_number_limit 65_536 - # @base_index 65_505 - # @next_index @base_index + 1 @base_seq_number BufferFactory.base_seq_number() @max_seq_number 0xFFFF - # TODO: write tests for the packet tracker gathering stats and repairing packets - - describe "PacketTracker should" do + describe "InboundPacketTracker should" do setup do buffer = BufferFactory.sample_buffer(@base_seq_number) From 414f008614a6086d5b5e0542e52d639908f72080 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Per=C5=BCy=C5=82o?= Date: Tue, 5 Oct 2021 10:15:05 +0200 Subject: [PATCH 10/20] Fixed docs --- lib/membrane/rtcp/receiver_report/stats_event.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/membrane/rtcp/receiver_report/stats_event.ex b/lib/membrane/rtcp/receiver_report/stats_event.ex index f4c3fad4..ae32e929 100644 --- a/lib/membrane/rtcp/receiver_report/stats_event.ex +++ b/lib/membrane/rtcp/receiver_report/stats_event.ex @@ -1,6 +1,6 @@ defmodule Membrane.RTCP.ReceiverReport.StatsEvent do @moduledoc """ - Event carrying jitter buffer statistics. + Event carrying statistics for a receiver report. """ @derive Membrane.EventProtocol From 6cfb748d91422c05cea0ab3163185b387d2f7585 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Per=C5=BCy=C5=82o?= Date: Tue, 5 Oct 2021 10:21:43 +0200 Subject: [PATCH 11/20] Fix credo --- lib/membrane/rtp/serializer.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/membrane/rtp/serializer.ex b/lib/membrane/rtp/serializer.ex index 48be1879..28b020f2 100644 --- a/lib/membrane/rtp/serializer.ex +++ b/lib/membrane/rtp/serializer.ex @@ -96,6 +96,6 @@ defmodule Membrane.RTP.Serializer do {{:ok, buffer: {:output, buffer}}, state} end - def buffer_timestamp(%{rtp: %{timestamp: timestamp}}, _metadata), do: timestamp - def buffer_timestamp(_rtp_metadata, %{timestamp: timestamp}), do: timestamp + defp buffer_timestamp(%{rtp: %{timestamp: timestamp}}, _metadata), do: timestamp + defp buffer_timestamp(_rtp_metadata, %{timestamp: timestamp}), do: timestamp end From dc5c075738fe5a23d57defa0b13ad608cd1453d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Per=C5=BCy=C5=82o?= Date: Mon, 11 Oct 2021 10:45:59 +0200 Subject: [PATCH 12/20] Fixed missing rtp header when serializing packet without using payloader --- ..._tracker.ex => outbound_packet_tracker.ex} | 37 ++++++++++++++++--- lib/membrane/rtp/serializer.ex | 2 +- lib/membrane/rtp/stream_receive_bin.ex | 2 +- lib/membrane/rtp/stream_send_bin.ex | 7 +++- 4 files changed, 38 insertions(+), 10 deletions(-) rename lib/membrane/rtp/{outband_packet_tracker.ex => outbound_packet_tracker.ex} (62%) diff --git a/lib/membrane/rtp/outband_packet_tracker.ex b/lib/membrane/rtp/outbound_packet_tracker.ex similarity index 62% rename from lib/membrane/rtp/outband_packet_tracker.ex rename to lib/membrane/rtp/outbound_packet_tracker.ex index ae96fa43..633e8194 100644 --- a/lib/membrane/rtp/outband_packet_tracker.ex +++ b/lib/membrane/rtp/outbound_packet_tracker.ex @@ -1,21 +1,36 @@ -defmodule Membrane.RTP.OutbandPacketTracker do +defmodule Membrane.RTP.OutboundPacketTracker do @moduledoc """ Tracks statistics of outband packets. + + Besides tracking statistics, tracker can also serialize packet's header and payload stored inside an incoming buffer into + a a proper RTP packet. """ use Membrane.Filter - alias Membrane.{Buffer, RTP, RemoteStream, Payload, Time} + alias Membrane.{Buffer, RTP, Payload, Time} def_input_pad :input, - caps: {RemoteStream, type: :packetized, content_format: one_of([nil, RTP])}, + caps: :any, demand_unit: :buffers def_output_pad :output, - caps: {RemoteStream, type: :packetized, content_format: one_of([nil, RTP])} + caps: :any def_options ssrc: [spec: RTP.ssrc_t()], payload_type: [spec: RTP.payload_type_t()], clock_rate: [spec: RTP.clock_rate_t()], + serialize_packets?: [ + spec: boolean(), + default: false, + description: """ + Decides if incoming buffers should get serialized to RTP packets. + If set to true then the filter assumes that buffer's metadata has proper header information under `:rtp` key + and buffer's payload is a proper RTP payload. + + Packet serialization may be necessary when there is no payloading process applied and we receive buffers with + parsed header and proper payload and we need to concatenate them. + """ + ], alignment: [ default: 1, spec: pos_integer(), @@ -35,6 +50,7 @@ defmodule Membrane.RTP.OutbandPacketTracker do } defstruct any_buffer_sent?: false, + serialize_packets?: false, stats_acc: %{ clock_rate: 0, timestamp: 0, @@ -46,7 +62,7 @@ defmodule Membrane.RTP.OutbandPacketTracker do @impl true def handle_init(options) do - state = %State{} + state = %State{serialize_packets?: options.serialize_packets?} state = state |> put_in([:stats_acc, :clock_rate], options.clock_rate) {:ok, Map.merge(Map.from_struct(options), state)} @@ -61,7 +77,7 @@ defmodule Membrane.RTP.OutbandPacketTracker do def handle_process(:input, %Buffer{} = buffer, _ctx, state) do state = update_stats(buffer, state) - {{:ok, buffer: {:output, buffer}}, state} + {{:ok, buffer: {:output, process_buffer(buffer, state)}}, state} end @impl true @@ -91,4 +107,13 @@ defmodule Membrane.RTP.OutbandPacketTracker do Map.put(state, :stats_acc, updated_stats) end + + defp process_buffer(buffer, %{serialize_packets?: true, ssrc: ssrc}) do + header = struct(RTP.Header, %{buffer.metadata.rtp | ssrc: ssrc}) + payload = RTP.Packet.serialize(%RTP.Packet{header: header, payload: buffer.payload}) + + %Buffer{buffer | payload: payload} + end + + defp process_buffer(buffer, _state), do: buffer end diff --git a/lib/membrane/rtp/serializer.ex b/lib/membrane/rtp/serializer.ex index 28b020f2..96e69ff2 100644 --- a/lib/membrane/rtp/serializer.ex +++ b/lib/membrane/rtp/serializer.ex @@ -96,6 +96,6 @@ defmodule Membrane.RTP.Serializer do {{:ok, buffer: {:output, buffer}}, state} end - defp buffer_timestamp(%{rtp: %{timestamp: timestamp}}, _metadata), do: timestamp + defp buffer_timestamp(%{timestamp: timestamp}, _metadata), do: timestamp defp buffer_timestamp(_rtp_metadata, %{timestamp: timestamp}), do: timestamp end diff --git a/lib/membrane/rtp/stream_receive_bin.ex b/lib/membrane/rtp/stream_receive_bin.ex index eea2444f..ce521fda 100644 --- a/lib/membrane/rtp/stream_receive_bin.ex +++ b/lib/membrane/rtp/stream_receive_bin.ex @@ -55,7 +55,7 @@ defmodule Membrane.RTP.StreamReceiveBin do report_interval: opts.rtcp_report_interval, fir_interval: opts.rtcp_fir_interval }) - |> to(:tracker, %Membrane.RTP.InboundPacketTracker{ + |> to(:packet_tracker, %Membrane.RTP.InboundPacketTracker{ clock_rate: opts.clock_rate, repair_sequence_numbers?: true }) diff --git a/lib/membrane/rtp/stream_send_bin.ex b/lib/membrane/rtp/stream_send_bin.ex index a40e7bcc..dc659dd3 100644 --- a/lib/membrane/rtp/stream_send_bin.ex +++ b/lib/membrane/rtp/stream_send_bin.ex @@ -16,6 +16,8 @@ defmodule Membrane.RTP.StreamSendBin do @impl true def handle_init(opts) do + use_payloader = !is_nil(opts.payloader) + maybe_link_payloader_bin = &to(&1, :payloader, %RTP.PayloaderBin{ payloader: opts.payloader, @@ -26,8 +28,9 @@ defmodule Membrane.RTP.StreamSendBin do links = [ link_bin_input() - |> then(if opts.payloader != nil, do: maybe_link_payloader_bin, else: & &1) - |> to(:packet_tracker, %RTP.OutbandPacketTracker{ + |> then(if use_payloader, do: maybe_link_payloader_bin, else: & &1) + |> to(:packet_tracker, %RTP.OutboundPacketTracker{ + serialize_packets?: not use_payloader, ssrc: opts.ssrc, payload_type: opts.payload_type, clock_rate: opts.clock_rate From 7630987604428bf71282837f54b69dda37dffb11 Mon Sep 17 00:00:00 2001 From: AndrzejPodo Date: Thu, 14 Oct 2021 13:39:06 +0200 Subject: [PATCH 13/20] migrated to pts, dts - tests are passing --- lib/membrane/rtp/jitter_buffer.ex | 5 ++--- lib/membrane/rtp/serializer.ex | 10 +++++----- lib/membrane/rtp/vad.ex | 2 +- mix.exs | 15 ++++++++++++--- mix.lock | 6 +++--- test/membrane/rtp/jitter_buffer/pipeline_test.exs | 2 +- .../jitter_buffer/timestamps_calculation_test.exs | 2 +- test/support/buffer_factory.ex | 5 ++--- 8 files changed, 27 insertions(+), 20 deletions(-) diff --git a/lib/membrane/rtp/jitter_buffer.ex b/lib/membrane/rtp/jitter_buffer.ex index ca82b663..35e9e864 100644 --- a/lib/membrane/rtp/jitter_buffer.ex +++ b/lib/membrane/rtp/jitter_buffer.ex @@ -198,9 +198,8 @@ defmodule Membrane.RTP.JitterBuffer do :current -> timestamp_base end - timestamp = Ratio.div((rtp_timestamp - timestamp_base) * Time.second(), state.clock_rate) - buffer = Bunch.Struct.put_in(buffer, [:metadata, :timestamp], timestamp) - action = {:buffer, {:output, buffer}} + timestamp = div((rtp_timestamp - timestamp_base) * Time.second(), state.clock_rate) + action = {:buffer, {:output, %{buffer | pts: timestamp}}} state = %{state | timestamp_base: timestamp_base, previous_timestamp: rtp_timestamp} {action, state} end diff --git a/lib/membrane/rtp/serializer.ex b/lib/membrane/rtp/serializer.ex index 96e69ff2..92ce6581 100644 --- a/lib/membrane/rtp/serializer.ex +++ b/lib/membrane/rtp/serializer.ex @@ -62,12 +62,12 @@ defmodule Membrane.RTP.Serializer do end @impl true - def handle_process(:input, %Buffer{payload: payload, metadata: metadata}, _ctx, state) do - {rtp_metadata, metadata} = Map.pop(metadata, :rtp, %{}) + def handle_process(:input, buffer, _ctx, state) do + {rtp_metadata, metadata} = Map.pop(buffer.metadata, :rtp, %{}) rtp_offset = rtp_metadata - |> buffer_timestamp(metadata) + |> buffer_timestamp(Buffer.get_dts_or_pts(buffer)) |> Ratio.mult(state.clock_rate) |> Membrane.Time.to_seconds() @@ -85,7 +85,7 @@ defmodule Membrane.RTP.Serializer do extension: Map.get(rtp_metadata, :extension) } - packet = %RTP.Packet{header: header, payload: payload} + packet = %RTP.Packet{header: header, payload: buffer.payload} payload = RTP.Packet.serialize(packet, align_to: state.alignment) buffer = %Buffer{ @@ -97,5 +97,5 @@ defmodule Membrane.RTP.Serializer do end defp buffer_timestamp(%{timestamp: timestamp}, _metadata), do: timestamp - defp buffer_timestamp(_rtp_metadata, %{timestamp: timestamp}), do: timestamp + defp buffer_timestamp(_rtp_metadata, timestamp), do: timestamp end diff --git a/lib/membrane/rtp/vad.ex b/lib/membrane/rtp/vad.ex index f039a158..20dd8871 100644 --- a/lib/membrane/rtp/vad.ex +++ b/lib/membrane/rtp/vad.ex @@ -91,7 +91,7 @@ defmodule Membrane.RTP.VAD do <<_id::4, _len::4, _v::1, level::7, _rest::binary-size(2)>> = buffer.metadata.rtp.extension.data - state = %{state | current_timestamp: buffer.metadata.timestamp} + state = %{state | current_timestamp: buffer.pts} state = filter_old_audio_levels(state) state = add_new_audio_level(state, level) audio_levels_vad = get_audio_levels_vad(state) diff --git a/mix.exs b/mix.exs index 8636fb40..cbcdcd1a 100644 --- a/mix.exs +++ b/mix.exs @@ -73,11 +73,20 @@ defmodule Membrane.RTP.Plugin.MixProject do defp deps do [ - {:membrane_core, "~> 0.7.0"}, + {:membrane_core, + github: "membraneframework/membrane-core", + branch: "add-timestamps-to-buffer", + override: true}, {:membrane_rtp_format, "~> 0.3.1"}, - {:membrane_rtp_h264_plugin, "~> 0.5.0", only: :test}, + {:membrane_rtp_h264_plugin, + github: "membraneframework/membrane_rtp_h264_plugin", + branch: "migration-to-buffer-with-pts-dts", + only: :test}, {:membrane_rtp_mpegaudio_plugin, "~> 0.6.0", only: :test}, - {:membrane_h264_ffmpeg_plugin, "~> 0.12.0", only: :test}, + {:membrane_h264_ffmpeg_plugin, + github: "membraneframework/membrane_h264_ffmpeg_plugin", + branch: "migration-to-buffer-with-pts-dts", + only: :test}, {:membrane_element_pcap, github: "membraneframework/membrane-element-pcap", only: :test}, {:membrane_element_udp, "~> 0.5.0", only: :test}, {:membrane_hackney_plugin, "~> 0.5.0", only: :test}, diff --git a/mix.lock b/mix.lock index cf88a866..e9d5a75c 100644 --- a/mix.lock +++ b/mix.lock @@ -26,14 +26,14 @@ "membrane_caps_video_h264": {:hex, :membrane_caps_video_h264, "0.2.0", "ecfe3a83b5e349b6ca56980c543cf3bf2275ccfb7769126fe62798f08fd7a2fe", [:mix], [], "hexpm", "36e447b8fd42cd4ad79bfa4dbf2e7f2ff3a9030c0ffa85ba951d21868a36376b"}, "membrane_caps_video_raw": {:hex, :membrane_caps_video_raw, "0.1.0", "6aa751b0c338ea6672540b7ec7ad2be0d23bad931b8a8776757da9b279070a3b", [:mix], [], "hexpm", "3f60d65189bd9e3b0ab77e0ebf2e0c1b04d0fd6f67c546fc1d54d9958c362ce4"}, "membrane_common_c": {:hex, :membrane_common_c, "0.9.0", "696c631acd34e3e7eea66e1da84fc6b37e51465f44b193f2fead3c2bcad59aed", [:mix], [{:membrane_core, "~> 0.7.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:shmex, "~> 0.4.0", [hex: :shmex, repo: "hexpm", optional: false]}, {:unifex, "~> 0.7.0", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "5f94b0a9667a988e8abe3b863d902e6cf6ea7e2047820855a3d863ef82b3c190"}, - "membrane_core": {:hex, :membrane_core, "0.7.0", "fc773f3b79ad3b5631c3a0754c429b333a4e024989aa4a63e49aa796d2fa1bd8", [:mix], [{:bunch, "~> 1.3", [hex: :bunch, repo: "hexpm", optional: false]}, {:qex, "~> 0.3", [hex: :qex, repo: "hexpm", optional: false]}, {:ratio, "~> 2.0", [hex: :ratio, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "4d04d42712d5548cf22993d85a7fdee9f4b6dfe31500ed299ed692fabdbc9824"}, + "membrane_core": {:git, "https://github.com/membraneframework/membrane-core.git", "845564cc22b7cf1bb0bfb570e58b1cc9e3546bc2", [branch: "add-timestamps-to-buffer"]}, "membrane_element_pcap": {:git, "https://github.com/membraneframework/membrane-element-pcap.git", "82a562f497d659bbaa4f081c074c02839a60cda7", []}, "membrane_element_udp": {:hex, :membrane_element_udp, "0.5.1", "98092b8243821c2d260995067e4e65fe4585d2558b54bc4d0d5daefacf886f7f", [:mix], [{:membrane_core, "~> 0.7.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:mockery, "~> 2.3.0", [hex: :mockery, repo: "hexpm", optional: false]}], "hexpm", "fe1a4e16181928f22e462486879e6306e438bad94e34b52ae49b88101f267269"}, - "membrane_h264_ffmpeg_plugin": {:hex, :membrane_h264_ffmpeg_plugin, "0.12.0", "e5b7b41ef439a95edb880279c283ac72d84972a096c96f6164c436d7cb153855", [:mix], [{:bunch, "~> 1.3.0", [hex: :bunch, repo: "hexpm", optional: false]}, {:membrane_caps_video_h264, "~> 0.2.0", [hex: :membrane_caps_video_h264, repo: "hexpm", optional: false]}, {:membrane_caps_video_raw, "~> 0.1.0", [hex: :membrane_caps_video_raw, repo: "hexpm", optional: false]}, {:membrane_common_c, "~> 0.9.0", [hex: :membrane_common_c, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.7.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:ratio, "~> 2.4.0", [hex: :ratio, repo: "hexpm", optional: false]}, {:unifex, "~> 0.7.0", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "d285e7ab0b43802f47129c76dd4447158eefa01563a4120746accb3ce7fb4ace"}, + "membrane_h264_ffmpeg_plugin": {:git, "https://github.com/membraneframework/membrane_h264_ffmpeg_plugin.git", "bdd07c35719c0b1f224569df8668738ee156d1d0", [branch: "migration-to-buffer-with-pts-dts"]}, "membrane_hackney_plugin": {:hex, :membrane_hackney_plugin, "0.5.0", "b0cc617458b73ad9cfe3fbb921e7484b1be6eb982961231627a4c3d4d5ad0307", [:mix], [{:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.7.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:mockery, "~> 2.3", [hex: :mockery, repo: "hexpm", optional: false]}], "hexpm", "b301e5ba91573fff62257209a48a75335877b96534d47e581fd088f5f987c67b"}, "membrane_remote_stream_format": {:hex, :membrane_remote_stream_format, "0.1.0", "2cedb6facd7f4185fc7c0e7c83a614483f98ef1e8d36f4ed5939c935768eb95d", [:mix], [], "hexpm", "a6f01aa51766fe92e12d2cb720176fcff6d2485fb563e51002d8f9ef9a90f894"}, "membrane_rtp_format": {:hex, :membrane_rtp_format, "0.3.1", "c6920fdb58660d90e264fcf3587dbb5994fb2f3643d234e59608c82598baebaf", [:mix], [], "hexpm", "d225f453715d165ded7dcb287c45360a4d653ac4a6d3ff70b4acdd244c45fa84"}, - "membrane_rtp_h264_plugin": {:hex, :membrane_rtp_h264_plugin, "0.5.1", "983e650b414926f482021eef5a9fa89e31e9dbc07c08d8ad6e3f99c15ae9c0ed", [:mix], [{:bunch, "~> 1.2", [hex: :bunch, repo: "hexpm", optional: false]}, {:membrane_caps_video_h264, "~> 0.2.0", [hex: :membrane_caps_video_h264, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.7.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_rtp_format, "~> 0.3.0", [hex: :membrane_rtp_format, repo: "hexpm", optional: false]}], "hexpm", "9ea174bb18e8156e30d5b537575c4aae70208931708ce1ecbbbe8e93406d43d8"}, + "membrane_rtp_h264_plugin": {:git, "https://github.com/membraneframework/membrane_rtp_h264_plugin.git", "4595164c67bab2e9f3c7ee757724c49ba184f9b1", [branch: "migration-to-buffer-with-pts-dts"]}, "membrane_rtp_mpegaudio_plugin": {:hex, :membrane_rtp_mpegaudio_plugin, "0.6.1", "3f55b15122fb00f8e803351c8b5cb473b7213dea8b077196ad9e5890ddc228b8", [:mix], [{:membrane_caps_audio_mpeg, "~> 0.2.0", [hex: :membrane_caps_audio_mpeg, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.7.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_rtp_format, "~> 0.3.0", [hex: :membrane_rtp_format, repo: "hexpm", optional: false]}], "hexpm", "00382506414afbe51d5dc76fb2b74231cf080977a7312f0f0cc100bf5d9361e2"}, "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"}, diff --git a/test/membrane/rtp/jitter_buffer/pipeline_test.exs b/test/membrane/rtp/jitter_buffer/pipeline_test.exs index 0ac5c575..da5dd9ed 100644 --- a/test/membrane/rtp/jitter_buffer/pipeline_test.exs +++ b/test/membrane/rtp/jitter_buffer/pipeline_test.exs @@ -128,7 +128,7 @@ defmodule Membrane.RTP.JitterBuffer.PipelineTest do pipeline, :sink, %Membrane.Buffer{ - metadata: %{rtp: %{sequence_number: ^seq_num, timestamp: _}}, + metadata: %{rtp: %{sequence_number: ^seq_num}}, payload: _ }, timeout diff --git a/test/membrane/rtp/jitter_buffer/timestamps_calculation_test.exs b/test/membrane/rtp/jitter_buffer/timestamps_calculation_test.exs index 70bb2543..14ca56f3 100644 --- a/test/membrane/rtp/jitter_buffer/timestamps_calculation_test.exs +++ b/test/membrane/rtp/jitter_buffer/timestamps_calculation_test.exs @@ -14,7 +14,7 @@ defmodule Membrane.RTP.JitterBuffer.TimestampsCalculationTest do end defp buffers_timestamps(buffers) do - Enum.map(buffers, & &1.metadata.timestamp) + Enum.map(buffers, & &1.pts) end defp process_and_receive_buffer_timestamps(buffers, state) do diff --git a/test/support/buffer_factory.ex b/test/support/buffer_factory.ex index ad5ea39a..4144d46f 100644 --- a/test/support/buffer_factory.ex +++ b/test/support/buffer_factory.ex @@ -22,13 +22,12 @@ defmodule Membrane.RTP.BufferFactory do %Buffer{ payload: <<0, 255>>, + pts: div(seq_num_offset * @timestamp_increment * Membrane.Time.second(), @clock_rate), metadata: %{ rtp: %{ timestamp: seq_num_offset * @timestamp_increment, sequence_number: seq_num - }, - timestamp: - Ratio.div(seq_num_offset * @timestamp_increment * Membrane.Time.second(), @clock_rate) + } } } end From 7ee449b6620ff7b11809b6b67eae0c3c2e9fcd0c Mon Sep 17 00:00:00 2001 From: AndrzejPodo Date: Tue, 19 Oct 2021 18:58:48 +0200 Subject: [PATCH 14/20] serializer should add pts --- lib/membrane/rtp/serializer.ex | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/membrane/rtp/serializer.ex b/lib/membrane/rtp/serializer.ex index 92ce6581..87f3cf4c 100644 --- a/lib/membrane/rtp/serializer.ex +++ b/lib/membrane/rtp/serializer.ex @@ -89,6 +89,7 @@ defmodule Membrane.RTP.Serializer do payload = RTP.Packet.serialize(packet, align_to: state.alignment) buffer = %Buffer{ + pts: Buffer.get_dts_or_pts(buffer), payload: payload, metadata: Map.put(metadata, :rtp, %{timestamp: rtp_timestamp}) } From 2507d99249b326974ea6fe73ecc88084d8b3b93c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20B=C5=82aszk=C3=B3w?= Date: Mon, 25 Oct 2021 15:30:40 +0200 Subject: [PATCH 15/20] Update deps in mix.lock --- mix.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/mix.lock b/mix.lock index e9d5a75c..0dfdfc92 100644 --- a/mix.lock +++ b/mix.lock @@ -4,36 +4,36 @@ "bunch_native": {:hex, :bunch_native, "0.4.0", "9214f73b753c7c4201fc0a1145d9720a15e45effa02d9eea8237d98ae53b36e5", [:mix], [{:bundlex, "~> 0.5.0", [hex: :bundlex, repo: "hexpm", optional: false]}], "hexpm", "4bf7e84250614994383870092e883bc8b7e213c3854506b1a03493bd9a6a1ba2"}, "bundlex": {:hex, :bundlex, "0.5.0", "2be5374bd58fcdccb0e08fe5b6839baa081eb2ee73b85958122ec78160e19aa4", [:mix], [{:bunch, "~> 1.0", [hex: :bunch, repo: "hexpm", optional: false]}, {:qex, "~> 0.5", [hex: :qex, repo: "hexpm", optional: false]}, {:secure_random, "~> 0.5", [hex: :secure_random, repo: "hexpm", optional: false]}], "hexpm", "c459b5f19d759821dfd269abd03259dc0411d02ec1efdd350b7e15a3021f1aa9"}, "bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm", "7af5c7e09fe1d40f76c8e4f9dd2be7cebd83909f31fee7cd0e9eadc567da8353"}, - "certifi": {:hex, :certifi, "2.6.1", "dbab8e5e155a0763eea978c913ca280a6b544bfa115633fa20249c3d396d9493", [:rebar3], [], "hexpm", "524c97b4991b3849dd5c17a631223896272c6b0af446778ba4675a1dff53bb7e"}, + "certifi": {:hex, :certifi, "2.8.0", "d4fb0a6bb20b7c9c3643e22507e42f356ac090a1dcea9ab99e27e0376d695eba", [:rebar3], [], "hexpm", "6ac7efc1c6f8600b08d625292d4bbf584e14847ce1b6b5c44d983d273e1097ea"}, "coerce": {:hex, :coerce, "1.0.1", "211c27386315dc2894ac11bc1f413a0e38505d808153367bd5c6e75a4003d096", [:mix], [], "hexpm", "b44a691700f7a1a15b4b7e2ff1fa30bebd669929ac8aa43cffe9e2f8bf051cf1"}, "credo": {:hex, :credo, "1.5.6", "e04cc0fdc236fefbb578e0c04bd01a471081616e741d386909e527ac146016c6", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "4b52a3e558bd64e30de62a648518a5ea2b6e3e5d2b164ef5296244753fc7eb17"}, "dialyxir": {:hex, :dialyxir, "1.1.0", "c5aab0d6e71e5522e77beff7ba9e08f8e02bad90dfbeffae60eaf0cb47e29488", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "07ea8e49c45f15264ebe6d5b93799d4dd56a44036cf42d0ad9c960bc266c0b9a"}, - "earmark_parser": {:hex, :earmark_parser, "1.4.15", "b29e8e729f4aa4a00436580dcc2c9c5c51890613457c193cc8525c388ccb2f06", [:mix], [], "hexpm", "044523d6438ea19c1b8ec877ec221b008661d3c27e3b848f4c879f500421ca5c"}, + "earmark_parser": {:hex, :earmark_parser, "1.4.16", "607709303e1d4e3e02f1444df0c821529af1c03b8578dfc81bb9cf64553d02b9", [:mix], [], "hexpm", "69fcf696168f5a274dd012e3e305027010658b2d1630cef68421d6baaeaccead"}, "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, - "ex_doc": {:hex, :ex_doc, "0.25.1", "4b736fa38dc76488a937e5ef2944f5474f3eff921de771b25371345a8dc810bc", [:mix], [{:earmark_parser, "~> 1.4.0", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "3200b0a69ddb2028365281fbef3753ea9e728683863d8cdaa96580925c891f67"}, + "ex_doc": {:hex, :ex_doc, "0.25.5", "ac3c5425a80b4b7c4dfecdf51fa9c23a44877124dd8ca34ee45ff608b1c6deb9", [:mix], [{:earmark_parser, "~> 1.4.0", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "688cfa538cdc146bc4291607764a7f1fcfa4cce8009ecd62de03b27197528350"}, "ex_libsrtp": {:hex, :ex_libsrtp, "0.3.0", "964cf207fc44357b9ef103ade78e99004de70df8ee44bf1c8f6a18051d0d3667", [:mix], [{:bunch, "~> 1.3", [hex: :bunch, repo: "hexpm", optional: false]}, {:unifex, "~> 0.7.0", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "62c2c40f6481ee670c864d26aa6496ad851c9b694bbd5367111bc93f3be2e44a"}, - "excoveralls": {:hex, :excoveralls, "0.14.2", "f9f5fd0004d7bbeaa28ea9606251bb643c313c3d60710bad1f5809c845b748f0", [:mix], [{:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "ca6fd358621cb4d29311b29d4732c4d47dac70e622850979bc54ed9a3e50f3e1"}, + "excoveralls": {:hex, :excoveralls, "0.14.4", "295498f1ae47bdc6dce59af9a585c381e1aefc63298d48172efaaa90c3d251db", [:mix], [{:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "e3ab02f2df4c1c7a519728a6f0a747e71d7d6e846020aae338173619217931c1"}, "expcap": {:git, "https://github.com/Hajto/expcap.git", "0608702b7e22c1f3206014dbe3e1d313cdc17f1c", []}, "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"}, - "hackney": {:hex, :hackney, "1.17.4", "99da4674592504d3fb0cfef0db84c3ba02b4508bae2dff8c0108baa0d6e0977c", [:rebar3], [{:certifi, "~>2.6.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "de16ff4996556c8548d512f4dbe22dd58a587bf3332e7fd362430a7ef3986b16"}, + "hackney": {:hex, :hackney, "1.18.0", "c4443d960bb9fba6d01161d01cd81173089686717d9490e5d3606644c48d121f", [:rebar3], [{:certifi, "~>2.8.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "9afcda620704d720db8c6a3123e9848d09c87586dc1c10479c42627b905b5c5e"}, "heap": {:hex, :heap, "2.0.2", "d98cb178286cfeb5edbcf17785e2d20af73ca57b5a2cf4af584118afbcf917eb", [:mix], [], "hexpm", "ba9ea2fe99eb4bcbd9a8a28eaf71cbcac449ca1d8e71731596aace9028c9d429"}, "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, "jason": {:hex, :jason, "1.2.2", "ba43e3f2709fd1aa1dce90aaabfd039d000469c05c56f0b8e31978e03fa39052", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "18a228f5f0058ee183f29f9eae0805c6e59d61c3b006760668d8d18ff0d12179"}, "makeup": {:hex, :makeup, "1.0.5", "d5a830bc42c9800ce07dd97fa94669dfb93d3bf5fcf6ea7a0c67b2e0e4a7f26c", [:mix], [{:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cfa158c02d3f5c0c665d0af11512fed3fba0144cf1aadee0f2ce17747fba2ca9"}, - "makeup_elixir": {:hex, :makeup_elixir, "0.15.1", "b5888c880d17d1cc3e598f05cdb5b5a91b7b17ac4eaf5f297cb697663a1094dd", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.1", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "db68c173234b07ab2a07f645a5acdc117b9f99d69ebf521821d89690ae6c6ec8"}, + "makeup_elixir": {:hex, :makeup_elixir, "0.15.2", "dc72dfe17eb240552857465cc00cce390960d9a0c055c4ccd38b70629227e97c", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.1", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "fd23ae48d09b32eff49d4ced2b43c9f086d402ee4fd4fcb2d7fad97fa8823e75"}, "makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"}, "membrane_caps_audio_mpeg": {:hex, :membrane_caps_audio_mpeg, "0.2.0", "9cf9a63f03e25b31cf31445325aa68e60a07d36ee1e759caa1422fa45df49367", [:mix], [], "hexpm", "f7a80e4841d46164c148be880932ac7425329f4bcc32eb36ad2e47eafe5f23e4"}, "membrane_caps_video_h264": {:hex, :membrane_caps_video_h264, "0.2.0", "ecfe3a83b5e349b6ca56980c543cf3bf2275ccfb7769126fe62798f08fd7a2fe", [:mix], [], "hexpm", "36e447b8fd42cd4ad79bfa4dbf2e7f2ff3a9030c0ffa85ba951d21868a36376b"}, "membrane_caps_video_raw": {:hex, :membrane_caps_video_raw, "0.1.0", "6aa751b0c338ea6672540b7ec7ad2be0d23bad931b8a8776757da9b279070a3b", [:mix], [], "hexpm", "3f60d65189bd9e3b0ab77e0ebf2e0c1b04d0fd6f67c546fc1d54d9958c362ce4"}, "membrane_common_c": {:hex, :membrane_common_c, "0.9.0", "696c631acd34e3e7eea66e1da84fc6b37e51465f44b193f2fead3c2bcad59aed", [:mix], [{:membrane_core, "~> 0.7.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:shmex, "~> 0.4.0", [hex: :shmex, repo: "hexpm", optional: false]}, {:unifex, "~> 0.7.0", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "5f94b0a9667a988e8abe3b863d902e6cf6ea7e2047820855a3d863ef82b3c190"}, - "membrane_core": {:git, "https://github.com/membraneframework/membrane-core.git", "845564cc22b7cf1bb0bfb570e58b1cc9e3546bc2", [branch: "add-timestamps-to-buffer"]}, + "membrane_core": {:git, "https://github.com/membraneframework/membrane-core.git", "cf2509127c1724ab04461952502d8025ff252222", [branch: "add-timestamps-to-buffer"]}, "membrane_element_pcap": {:git, "https://github.com/membraneframework/membrane-element-pcap.git", "82a562f497d659bbaa4f081c074c02839a60cda7", []}, "membrane_element_udp": {:hex, :membrane_element_udp, "0.5.1", "98092b8243821c2d260995067e4e65fe4585d2558b54bc4d0d5daefacf886f7f", [:mix], [{:membrane_core, "~> 0.7.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:mockery, "~> 2.3.0", [hex: :mockery, repo: "hexpm", optional: false]}], "hexpm", "fe1a4e16181928f22e462486879e6306e438bad94e34b52ae49b88101f267269"}, - "membrane_h264_ffmpeg_plugin": {:git, "https://github.com/membraneframework/membrane_h264_ffmpeg_plugin.git", "bdd07c35719c0b1f224569df8668738ee156d1d0", [branch: "migration-to-buffer-with-pts-dts"]}, + "membrane_h264_ffmpeg_plugin": {:git, "https://github.com/membraneframework/membrane_h264_ffmpeg_plugin.git", "570d1a0a6153eeda855db0ddc4243831909d4502", [branch: "migration-to-buffer-with-pts-dts"]}, "membrane_hackney_plugin": {:hex, :membrane_hackney_plugin, "0.5.0", "b0cc617458b73ad9cfe3fbb921e7484b1be6eb982961231627a4c3d4d5ad0307", [:mix], [{:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.7.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:mockery, "~> 2.3", [hex: :mockery, repo: "hexpm", optional: false]}], "hexpm", "b301e5ba91573fff62257209a48a75335877b96534d47e581fd088f5f987c67b"}, "membrane_remote_stream_format": {:hex, :membrane_remote_stream_format, "0.1.0", "2cedb6facd7f4185fc7c0e7c83a614483f98ef1e8d36f4ed5939c935768eb95d", [:mix], [], "hexpm", "a6f01aa51766fe92e12d2cb720176fcff6d2485fb563e51002d8f9ef9a90f894"}, "membrane_rtp_format": {:hex, :membrane_rtp_format, "0.3.1", "c6920fdb58660d90e264fcf3587dbb5994fb2f3643d234e59608c82598baebaf", [:mix], [], "hexpm", "d225f453715d165ded7dcb287c45360a4d653ac4a6d3ff70b4acdd244c45fa84"}, - "membrane_rtp_h264_plugin": {:git, "https://github.com/membraneframework/membrane_rtp_h264_plugin.git", "4595164c67bab2e9f3c7ee757724c49ba184f9b1", [branch: "migration-to-buffer-with-pts-dts"]}, + "membrane_rtp_h264_plugin": {:git, "https://github.com/membraneframework/membrane_rtp_h264_plugin.git", "05b06e54beb5c0cb18f492a886ffc812e03ee4e6", [branch: "migration-to-buffer-with-pts-dts"]}, "membrane_rtp_mpegaudio_plugin": {:hex, :membrane_rtp_mpegaudio_plugin, "0.6.1", "3f55b15122fb00f8e803351c8b5cb473b7213dea8b077196ad9e5890ddc228b8", [:mix], [{:membrane_caps_audio_mpeg, "~> 0.2.0", [hex: :membrane_caps_audio_mpeg, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.7.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_rtp_format, "~> 0.3.0", [hex: :membrane_rtp_format, repo: "hexpm", optional: false]}], "hexpm", "00382506414afbe51d5dc76fb2b74231cf080977a7312f0f0cc100bf5d9361e2"}, "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"}, From d224f6407b80b3a00eed4458d57a2ea6cb775b08 Mon Sep 17 00:00:00 2001 From: AndrzejPodo Date: Fri, 12 Nov 2021 12:26:38 +0100 Subject: [PATCH 16/20] use pts if there is no timestamp in metadata --- lib/membrane/rtp/serializer.ex | 8 ++++---- mix.exs | 2 +- mix.lock | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/membrane/rtp/serializer.ex b/lib/membrane/rtp/serializer.ex index 87f3cf4c..e760046a 100644 --- a/lib/membrane/rtp/serializer.ex +++ b/lib/membrane/rtp/serializer.ex @@ -67,7 +67,7 @@ defmodule Membrane.RTP.Serializer do rtp_offset = rtp_metadata - |> buffer_timestamp(Buffer.get_dts_or_pts(buffer)) + |> buffer_timestamp(buffer.pts) |> Ratio.mult(state.clock_rate) |> Membrane.Time.to_seconds() @@ -89,9 +89,9 @@ defmodule Membrane.RTP.Serializer do payload = RTP.Packet.serialize(packet, align_to: state.alignment) buffer = %Buffer{ - pts: Buffer.get_dts_or_pts(buffer), - payload: payload, - metadata: Map.put(metadata, :rtp, %{timestamp: rtp_timestamp}) + buffer + | payload: payload, + metadata: Map.put(metadata, :rtp, %{timestamp: rtp_timestamp}) } {{:ok, buffer: {:output, buffer}}, state} diff --git a/mix.exs b/mix.exs index cbcdcd1a..b6d36166 100644 --- a/mix.exs +++ b/mix.exs @@ -85,7 +85,7 @@ defmodule Membrane.RTP.Plugin.MixProject do {:membrane_rtp_mpegaudio_plugin, "~> 0.6.0", only: :test}, {:membrane_h264_ffmpeg_plugin, github: "membraneframework/membrane_h264_ffmpeg_plugin", - branch: "migration-to-buffer-with-pts-dts", + branch: "decoder-adds-pts-and-dts", only: :test}, {:membrane_element_pcap, github: "membraneframework/membrane-element-pcap", only: :test}, {:membrane_element_udp, "~> 0.5.0", only: :test}, diff --git a/mix.lock b/mix.lock index 0dfdfc92..6deb236b 100644 --- a/mix.lock +++ b/mix.lock @@ -29,11 +29,11 @@ "membrane_core": {:git, "https://github.com/membraneframework/membrane-core.git", "cf2509127c1724ab04461952502d8025ff252222", [branch: "add-timestamps-to-buffer"]}, "membrane_element_pcap": {:git, "https://github.com/membraneframework/membrane-element-pcap.git", "82a562f497d659bbaa4f081c074c02839a60cda7", []}, "membrane_element_udp": {:hex, :membrane_element_udp, "0.5.1", "98092b8243821c2d260995067e4e65fe4585d2558b54bc4d0d5daefacf886f7f", [:mix], [{:membrane_core, "~> 0.7.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:mockery, "~> 2.3.0", [hex: :mockery, repo: "hexpm", optional: false]}], "hexpm", "fe1a4e16181928f22e462486879e6306e438bad94e34b52ae49b88101f267269"}, - "membrane_h264_ffmpeg_plugin": {:git, "https://github.com/membraneframework/membrane_h264_ffmpeg_plugin.git", "570d1a0a6153eeda855db0ddc4243831909d4502", [branch: "migration-to-buffer-with-pts-dts"]}, + "membrane_h264_ffmpeg_plugin": {:git, "https://github.com/membraneframework/membrane_h264_ffmpeg_plugin.git", "fad46ef728e6492d49619394dd1d85f20510daa7", [branch: "decoder-adds-pts-and-dts"]}, "membrane_hackney_plugin": {:hex, :membrane_hackney_plugin, "0.5.0", "b0cc617458b73ad9cfe3fbb921e7484b1be6eb982961231627a4c3d4d5ad0307", [:mix], [{:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.7.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:mockery, "~> 2.3", [hex: :mockery, repo: "hexpm", optional: false]}], "hexpm", "b301e5ba91573fff62257209a48a75335877b96534d47e581fd088f5f987c67b"}, "membrane_remote_stream_format": {:hex, :membrane_remote_stream_format, "0.1.0", "2cedb6facd7f4185fc7c0e7c83a614483f98ef1e8d36f4ed5939c935768eb95d", [:mix], [], "hexpm", "a6f01aa51766fe92e12d2cb720176fcff6d2485fb563e51002d8f9ef9a90f894"}, "membrane_rtp_format": {:hex, :membrane_rtp_format, "0.3.1", "c6920fdb58660d90e264fcf3587dbb5994fb2f3643d234e59608c82598baebaf", [:mix], [], "hexpm", "d225f453715d165ded7dcb287c45360a4d653ac4a6d3ff70b4acdd244c45fa84"}, - "membrane_rtp_h264_plugin": {:git, "https://github.com/membraneframework/membrane_rtp_h264_plugin.git", "05b06e54beb5c0cb18f492a886ffc812e03ee4e6", [branch: "migration-to-buffer-with-pts-dts"]}, + "membrane_rtp_h264_plugin": {:git, "https://github.com/membraneframework/membrane_rtp_h264_plugin.git", "c68dbeda6565549f9ad18a13b861bb943e5b39e7", [branch: "migration-to-buffer-with-pts-dts"]}, "membrane_rtp_mpegaudio_plugin": {:hex, :membrane_rtp_mpegaudio_plugin, "0.6.1", "3f55b15122fb00f8e803351c8b5cb473b7213dea8b077196ad9e5890ddc228b8", [:mix], [{:membrane_caps_audio_mpeg, "~> 0.2.0", [hex: :membrane_caps_audio_mpeg, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.7.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_rtp_format, "~> 0.3.0", [hex: :membrane_rtp_format, repo: "hexpm", optional: false]}], "hexpm", "00382506414afbe51d5dc76fb2b74231cf080977a7312f0f0cc100bf5d9361e2"}, "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"}, From bc3326b6dd710cf45f4a3d77d83ad63fba9c0c89 Mon Sep 17 00:00:00 2001 From: AndrzejPodo Date: Tue, 30 Nov 2021 15:06:32 +0100 Subject: [PATCH 17/20] update deps --- mix.exs | 10 ++-------- mix.lock | 23 +++++++++++------------ 2 files changed, 13 insertions(+), 20 deletions(-) diff --git a/mix.exs b/mix.exs index b6d36166..91c5b360 100644 --- a/mix.exs +++ b/mix.exs @@ -73,20 +73,14 @@ defmodule Membrane.RTP.Plugin.MixProject do defp deps do [ - {:membrane_core, - github: "membraneframework/membrane-core", - branch: "add-timestamps-to-buffer", - override: true}, + {:membrane_core, "~> 0.8.0", override: true}, {:membrane_rtp_format, "~> 0.3.1"}, {:membrane_rtp_h264_plugin, github: "membraneframework/membrane_rtp_h264_plugin", branch: "migration-to-buffer-with-pts-dts", only: :test}, {:membrane_rtp_mpegaudio_plugin, "~> 0.6.0", only: :test}, - {:membrane_h264_ffmpeg_plugin, - github: "membraneframework/membrane_h264_ffmpeg_plugin", - branch: "decoder-adds-pts-and-dts", - only: :test}, + {:membrane_h264_ffmpeg_plugin, "~> 0.15.0", only: :test}, {:membrane_element_pcap, github: "membraneframework/membrane-element-pcap", only: :test}, {:membrane_element_udp, "~> 0.5.0", only: :test}, {:membrane_hackney_plugin, "~> 0.5.0", only: :test}, diff --git a/mix.lock b/mix.lock index 6deb236b..da124592 100644 --- a/mix.lock +++ b/mix.lock @@ -2,15 +2,15 @@ "bimap": {:hex, :bimap, "1.1.1", "783a5f31423ffd608040985de1fc9bc17bd73c813fe79f3b3576ff574ad81d1c", [:mix], [], "hexpm", "861b630d470934ad4c5063ed795f865409473c41d80d9b897ae07f11049045ab"}, "bunch": {:hex, :bunch, "1.3.0", "51b4423088b7fb9e21eae6d6bc5e5d219d955ea5556fbd6130bfb6213df4be32", [:mix], [], "hexpm", "9ad233a2bacc0dae8aa6553a9b9057f27446443b1c5903c3479b6f9f3820ce2d"}, "bunch_native": {:hex, :bunch_native, "0.4.0", "9214f73b753c7c4201fc0a1145d9720a15e45effa02d9eea8237d98ae53b36e5", [:mix], [{:bundlex, "~> 0.5.0", [hex: :bundlex, repo: "hexpm", optional: false]}], "hexpm", "4bf7e84250614994383870092e883bc8b7e213c3854506b1a03493bd9a6a1ba2"}, - "bundlex": {:hex, :bundlex, "0.5.0", "2be5374bd58fcdccb0e08fe5b6839baa081eb2ee73b85958122ec78160e19aa4", [:mix], [{:bunch, "~> 1.0", [hex: :bunch, repo: "hexpm", optional: false]}, {:qex, "~> 0.5", [hex: :qex, repo: "hexpm", optional: false]}, {:secure_random, "~> 0.5", [hex: :secure_random, repo: "hexpm", optional: false]}], "hexpm", "c459b5f19d759821dfd269abd03259dc0411d02ec1efdd350b7e15a3021f1aa9"}, + "bundlex": {:hex, :bundlex, "0.5.1", "a164ba822102476db7a11db8bb7e8adca0630bdff448e00e5ba1138e3df27839", [:mix], [{:bunch, "~> 1.0", [hex: :bunch, repo: "hexpm", optional: false]}, {:qex, "~> 0.5", [hex: :qex, repo: "hexpm", optional: false]}, {:secure_random, "~> 0.5", [hex: :secure_random, repo: "hexpm", optional: false]}], "hexpm", "b3348db967dfa880c9e3d311af7e366ab515202429264334baeeaef04fa7a4e2"}, "bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm", "7af5c7e09fe1d40f76c8e4f9dd2be7cebd83909f31fee7cd0e9eadc567da8353"}, "certifi": {:hex, :certifi, "2.8.0", "d4fb0a6bb20b7c9c3643e22507e42f356ac090a1dcea9ab99e27e0376d695eba", [:rebar3], [], "hexpm", "6ac7efc1c6f8600b08d625292d4bbf584e14847ce1b6b5c44d983d273e1097ea"}, "coerce": {:hex, :coerce, "1.0.1", "211c27386315dc2894ac11bc1f413a0e38505d808153367bd5c6e75a4003d096", [:mix], [], "hexpm", "b44a691700f7a1a15b4b7e2ff1fa30bebd669929ac8aa43cffe9e2f8bf051cf1"}, - "credo": {:hex, :credo, "1.5.6", "e04cc0fdc236fefbb578e0c04bd01a471081616e741d386909e527ac146016c6", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "4b52a3e558bd64e30de62a648518a5ea2b6e3e5d2b164ef5296244753fc7eb17"}, + "credo": {:hex, :credo, "1.6.1", "7dc76dcdb764a4316c1596804c48eada9fff44bd4b733a91ccbf0c0f368be61e", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "698607fb5993720c7e93d2d8e76f2175bba024de964e160e2f7151ef3ab82ac5"}, "dialyxir": {:hex, :dialyxir, "1.1.0", "c5aab0d6e71e5522e77beff7ba9e08f8e02bad90dfbeffae60eaf0cb47e29488", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "07ea8e49c45f15264ebe6d5b93799d4dd56a44036cf42d0ad9c960bc266c0b9a"}, - "earmark_parser": {:hex, :earmark_parser, "1.4.16", "607709303e1d4e3e02f1444df0c821529af1c03b8578dfc81bb9cf64553d02b9", [:mix], [], "hexpm", "69fcf696168f5a274dd012e3e305027010658b2d1630cef68421d6baaeaccead"}, + "earmark_parser": {:hex, :earmark_parser, "1.4.17", "6f3c7e94170377ba45241d394389e800fb15adc5de51d0a3cd52ae766aafd63f", [:mix], [], "hexpm", "f93ac89c9feca61c165b264b5837bf82344d13bebc634cd575cb711e2e342023"}, "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, - "ex_doc": {:hex, :ex_doc, "0.25.5", "ac3c5425a80b4b7c4dfecdf51fa9c23a44877124dd8ca34ee45ff608b1c6deb9", [:mix], [{:earmark_parser, "~> 1.4.0", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "688cfa538cdc146bc4291607764a7f1fcfa4cce8009ecd62de03b27197528350"}, + "ex_doc": {:hex, :ex_doc, "0.26.0", "1922164bac0b18b02f84d6f69cab1b93bc3e870e2ad18d5dacb50a9e06b542a3", [:mix], [{:earmark_parser, "~> 1.4.0", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "2775d66e494a9a48355db7867478ffd997864c61c65a47d31c4949459281c78d"}, "ex_libsrtp": {:hex, :ex_libsrtp, "0.3.0", "964cf207fc44357b9ef103ade78e99004de70df8ee44bf1c8f6a18051d0d3667", [:mix], [{:bunch, "~> 1.3", [hex: :bunch, repo: "hexpm", optional: false]}, {:unifex, "~> 0.7.0", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "62c2c40f6481ee670c864d26aa6496ad851c9b694bbd5367111bc93f3be2e44a"}, "excoveralls": {:hex, :excoveralls, "0.14.4", "295498f1ae47bdc6dce59af9a585c381e1aefc63298d48172efaaa90c3d251db", [:mix], [{:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "e3ab02f2df4c1c7a519728a6f0a747e71d7d6e846020aae338173619217931c1"}, "expcap": {:git, "https://github.com/Hajto/expcap.git", "0608702b7e22c1f3206014dbe3e1d313cdc17f1c", []}, @@ -25,29 +25,28 @@ "membrane_caps_audio_mpeg": {:hex, :membrane_caps_audio_mpeg, "0.2.0", "9cf9a63f03e25b31cf31445325aa68e60a07d36ee1e759caa1422fa45df49367", [:mix], [], "hexpm", "f7a80e4841d46164c148be880932ac7425329f4bcc32eb36ad2e47eafe5f23e4"}, "membrane_caps_video_h264": {:hex, :membrane_caps_video_h264, "0.2.0", "ecfe3a83b5e349b6ca56980c543cf3bf2275ccfb7769126fe62798f08fd7a2fe", [:mix], [], "hexpm", "36e447b8fd42cd4ad79bfa4dbf2e7f2ff3a9030c0ffa85ba951d21868a36376b"}, "membrane_caps_video_raw": {:hex, :membrane_caps_video_raw, "0.1.0", "6aa751b0c338ea6672540b7ec7ad2be0d23bad931b8a8776757da9b279070a3b", [:mix], [], "hexpm", "3f60d65189bd9e3b0ab77e0ebf2e0c1b04d0fd6f67c546fc1d54d9958c362ce4"}, - "membrane_common_c": {:hex, :membrane_common_c, "0.9.0", "696c631acd34e3e7eea66e1da84fc6b37e51465f44b193f2fead3c2bcad59aed", [:mix], [{:membrane_core, "~> 0.7.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:shmex, "~> 0.4.0", [hex: :shmex, repo: "hexpm", optional: false]}, {:unifex, "~> 0.7.0", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "5f94b0a9667a988e8abe3b863d902e6cf6ea7e2047820855a3d863ef82b3c190"}, - "membrane_core": {:git, "https://github.com/membraneframework/membrane-core.git", "cf2509127c1724ab04461952502d8025ff252222", [branch: "add-timestamps-to-buffer"]}, - "membrane_element_pcap": {:git, "https://github.com/membraneframework/membrane-element-pcap.git", "82a562f497d659bbaa4f081c074c02839a60cda7", []}, + "membrane_common_c": {:hex, :membrane_common_c, "0.10.0", "9db4fa2964039e3cd518b333d27ff7492c9746cd6af5ebd217a101fa1bccbdcd", [:mix], [{:membrane_core, "~> 0.8.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:shmex, "~> 0.4.0", [hex: :shmex, repo: "hexpm", optional: false]}, {:unifex, "~> 0.7.0", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "bb3312f1b25d9eae3226b49d1f0daa6535fe38ac72d8742adb60b4373c531566"}, + "membrane_core": {:hex, :membrane_core, "0.8.1", "33df0e4c76c05a2be57042893b05516886462baefdf95b7299e4d2f5db32dea3", [:mix], [{:bunch, "~> 1.3", [hex: :bunch, repo: "hexpm", optional: false]}, {:qex, "~> 0.3", [hex: :qex, repo: "hexpm", optional: false]}, {:ratio, "~> 2.0", [hex: :ratio, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "5574db35f08de1e95648f9c3acce38c5f318216c5a5ae5e3f253f7edad8fdc1b"}, + "membrane_element_pcap": {:git, "https://github.com/membraneframework/membrane-element-pcap.git", "e9d9c47bc1ed5897250e6463710b4adb0d76c4ed", []}, "membrane_element_udp": {:hex, :membrane_element_udp, "0.5.1", "98092b8243821c2d260995067e4e65fe4585d2558b54bc4d0d5daefacf886f7f", [:mix], [{:membrane_core, "~> 0.7.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:mockery, "~> 2.3.0", [hex: :mockery, repo: "hexpm", optional: false]}], "hexpm", "fe1a4e16181928f22e462486879e6306e438bad94e34b52ae49b88101f267269"}, - "membrane_h264_ffmpeg_plugin": {:git, "https://github.com/membraneframework/membrane_h264_ffmpeg_plugin.git", "fad46ef728e6492d49619394dd1d85f20510daa7", [branch: "decoder-adds-pts-and-dts"]}, + "membrane_h264_ffmpeg_plugin": {:hex, :membrane_h264_ffmpeg_plugin, "0.15.0", "717ffd95f250eedf4f5f66c0f2850a88b6682c54b3a0dfa32181478a4da19fda", [:mix], [{:bunch, "~> 1.3.0", [hex: :bunch, repo: "hexpm", optional: false]}, {:membrane_caps_video_h264, "~> 0.2.0", [hex: :membrane_caps_video_h264, repo: "hexpm", optional: false]}, {:membrane_caps_video_raw, "~> 0.1.0", [hex: :membrane_caps_video_raw, repo: "hexpm", optional: false]}, {:membrane_common_c, "~> 0.10.0", [hex: :membrane_common_c, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.8.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:ratio, "~> 2.4.0", [hex: :ratio, repo: "hexpm", optional: false]}, {:unifex, "~> 0.7.0", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "e70a81e6af2cfbb211893003c2ea92053ad90cebda4392a42363de719cf5ed89"}, "membrane_hackney_plugin": {:hex, :membrane_hackney_plugin, "0.5.0", "b0cc617458b73ad9cfe3fbb921e7484b1be6eb982961231627a4c3d4d5ad0307", [:mix], [{:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.7.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:mockery, "~> 2.3", [hex: :mockery, repo: "hexpm", optional: false]}], "hexpm", "b301e5ba91573fff62257209a48a75335877b96534d47e581fd088f5f987c67b"}, - "membrane_remote_stream_format": {:hex, :membrane_remote_stream_format, "0.1.0", "2cedb6facd7f4185fc7c0e7c83a614483f98ef1e8d36f4ed5939c935768eb95d", [:mix], [], "hexpm", "a6f01aa51766fe92e12d2cb720176fcff6d2485fb563e51002d8f9ef9a90f894"}, "membrane_rtp_format": {:hex, :membrane_rtp_format, "0.3.1", "c6920fdb58660d90e264fcf3587dbb5994fb2f3643d234e59608c82598baebaf", [:mix], [], "hexpm", "d225f453715d165ded7dcb287c45360a4d653ac4a6d3ff70b4acdd244c45fa84"}, "membrane_rtp_h264_plugin": {:git, "https://github.com/membraneframework/membrane_rtp_h264_plugin.git", "c68dbeda6565549f9ad18a13b861bb943e5b39e7", [branch: "migration-to-buffer-with-pts-dts"]}, "membrane_rtp_mpegaudio_plugin": {:hex, :membrane_rtp_mpegaudio_plugin, "0.6.1", "3f55b15122fb00f8e803351c8b5cb473b7213dea8b077196ad9e5890ddc228b8", [:mix], [{:membrane_caps_audio_mpeg, "~> 0.2.0", [hex: :membrane_caps_audio_mpeg, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.7.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_rtp_format, "~> 0.3.0", [hex: :membrane_rtp_format, repo: "hexpm", optional: false]}], "hexpm", "00382506414afbe51d5dc76fb2b74231cf080977a7312f0f0cc100bf5d9361e2"}, "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"}, "mockery": {:hex, :mockery, "2.3.1", "a02fd60b10ac9ed37a7a2ecf6786c1f1dd5c75d2b079a60594b089fba32dc087", [:mix], [], "hexpm", "1d0971d88ebf084e962da3f2cfee16f0ea8e04ff73a7710428500d4500b947fa"}, - "nimble_parsec": {:hex, :nimble_parsec, "1.1.0", "3a6fca1550363552e54c216debb6a9e95bd8d32348938e13de5eda962c0d7f89", [:mix], [], "hexpm", "08eb32d66b706e913ff748f11694b17981c0b04a33ef470e33e11b3d3ac8f54b"}, + "nimble_parsec": {:hex, :nimble_parsec, "1.2.0", "b44d75e2a6542dcb6acf5d71c32c74ca88960421b6874777f79153bbbbd7dccc", [:mix], [], "hexpm", "52b2871a7515a5ac49b00f214e4165a40724cf99798d8e4a65e4fd64ebd002c1"}, "numbers": {:hex, :numbers, "5.2.4", "f123d5bb7f6acc366f8f445e10a32bd403c8469bdbce8ce049e1f0972b607080", [:mix], [{:coerce, "~> 1.0", [hex: :coerce, repo: "hexpm", optional: false]}, {:decimal, "~> 1.9 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "eeccf5c61d5f4922198395bf87a465b6f980b8b862dd22d28198c5e6fab38582"}, "parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"}, "pkt": {:git, "https://github.com/msantos/pkt.git", "8c826b25098b06db8c24805fb1da2d65c14abe06", []}, - "qex": {:hex, :qex, "0.5.0", "5a3a9becf67d4006377c4c247ffdaaa8ae5b3634a0caadb788dc24d6125068f4", [:mix], [], "hexpm", "4ad6f6421163cd8204509a119a5c9813cbb969cfb8d802a9dc49b968bffbac2a"}, + "qex": {:hex, :qex, "0.5.1", "0d82c0f008551d24fffb99d97f8299afcb8ea9cf99582b770bd004ed5af63fd6", [:mix], [], "hexpm", "935a39fdaf2445834b95951456559e9dc2063d0a055742c558a99987b38d6bab"}, "ratio": {:hex, :ratio, "2.4.2", "c8518f3536d49b1b00d88dd20d49f8b11abb7819638093314a6348139f14f9f9", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}, {:numbers, "~> 5.2.0", [hex: :numbers, repo: "hexpm", optional: false]}], "hexpm", "441ef6f73172a3503de65ccf1769030997b0d533b1039422f1e5e0e0b4cbf89e"}, "secure_random": {:hex, :secure_random, "0.5.1", "c5532b37c89d175c328f5196a0c2a5680b15ebce3e654da37129a9fe40ebf51b", [:mix], [], "hexpm", "1b9754f15e3940a143baafd19da12293f100044df69ea12db5d72878312ae6ab"}, "shmex": {:hex, :shmex, "0.4.0", "8a074a984bbd45808d80eddfa0f037d7dfb4d4046e6566cd40adf41e13decf74", [:mix], [{:bunch_native, "~> 0.4.0", [hex: :bunch_native, repo: "hexpm", optional: false]}, {:bundlex, "~> 0.5.0", [hex: :bundlex, repo: "hexpm", optional: false]}], "hexpm", "1018c4eca1db5352ed4dec4813e1ee212b98fc851e1edd302906892538bbfaa8"}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"}, - "telemetry": {:hex, :telemetry, "0.4.3", "a06428a514bdbc63293cd9a6263aad00ddeb66f608163bdec7c8995784080818", [:rebar3], [], "hexpm", "eb72b8365ffda5bed68a620d1da88525e326cb82a75ee61354fc24b844768041"}, + "telemetry": {:hex, :telemetry, "1.0.0", "0f453a102cdf13d506b7c0ab158324c337c41f1cc7548f0bc0e130bbf0ae9452", [:rebar3], [], "hexpm", "73bc09fa59b4a0284efb4624335583c528e07ec9ae76aca96ea0673850aec57a"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, "unifex": {:hex, :unifex, "0.7.0", "43b876f19a6f9e1849762d90ac2242698a8318199e4b6b92d18274873c733bec", [:mix], [{:bunch, "~> 1.0", [hex: :bunch, repo: "hexpm", optional: false]}, {:bundlex, "~> 0.5.0", [hex: :bundlex, repo: "hexpm", optional: false]}, {:shmex, "~> 0.4.0", [hex: :shmex, repo: "hexpm", optional: false]}], "hexpm", "dac9366611d82d647df5c604638caac0c26951d62892e19cacff3bf7d37556f8"}, } From 583a5561af7eb1a3ab4c078fb0edffe885d888c3 Mon Sep 17 00:00:00 2001 From: AndrzejPodo Date: Wed, 1 Dec 2021 12:49:20 +0100 Subject: [PATCH 18/20] fix after merge --- lib/membrane/rtp/outbound_packet_tracker.ex | 2 +- lib/membrane/rtp/serializer.ex | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/membrane/rtp/outbound_packet_tracker.ex b/lib/membrane/rtp/outbound_packet_tracker.ex index bf6a8237..9aff978d 100644 --- a/lib/membrane/rtp/outbound_packet_tracker.ex +++ b/lib/membrane/rtp/outbound_packet_tracker.ex @@ -73,7 +73,7 @@ defmodule Membrane.RTP.OutboundPacketTracker do align_to: state.alignment ) - buffer = %Buffer{payload: payload, metadata: metadata} + buffer = %Buffer{buffer | payload: payload, metadata: metadata} {{:ok, buffer: {:output, buffer}}, state} end diff --git a/lib/membrane/rtp/serializer.ex b/lib/membrane/rtp/serializer.ex index 9b02a9e4..f8980cf4 100644 --- a/lib/membrane/rtp/serializer.ex +++ b/lib/membrane/rtp/serializer.ex @@ -100,8 +100,9 @@ defmodule Membrane.RTP.Serializer do } buffer = %Membrane.Buffer{ - metadata: Map.put(metadata, :rtp, header), - payload: buffer.payload + buffer + | metadata: Map.put(metadata, :rtp, header), + payload: buffer.payload } {{:ok, buffer: {:output, buffer}}, state} From 082e2d77183f07563b8ff62b413d6282b3ab7c45 Mon Sep 17 00:00:00 2001 From: AndrzejPodo Date: Wed, 1 Dec 2021 15:37:48 +0100 Subject: [PATCH 19/20] bump membrane_rtp_h264_plugin --- lib/membrane/rtp/serializer.ex | 2 +- mix.exs | 5 +---- mix.lock | 2 +- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/lib/membrane/rtp/serializer.ex b/lib/membrane/rtp/serializer.ex index f8980cf4..1f0287c3 100644 --- a/lib/membrane/rtp/serializer.ex +++ b/lib/membrane/rtp/serializer.ex @@ -9,7 +9,7 @@ defmodule Membrane.RTP.Serializer do """ use Membrane.Filter - alias Membrane.{Buffer, RTP, RemoteStream} + alias Membrane.{RTP, RemoteStream} @max_seq_num 65_535 @max_timestamp 0xFFFFFFFF diff --git a/mix.exs b/mix.exs index 4c11f9c2..ac657fe7 100644 --- a/mix.exs +++ b/mix.exs @@ -75,10 +75,7 @@ defmodule Membrane.RTP.Plugin.MixProject do [ {:membrane_core, "~> 0.8.0", override: true}, {:membrane_rtp_format, "~> 0.3.1"}, - {:membrane_rtp_h264_plugin, - github: "membraneframework/membrane_rtp_h264_plugin", - branch: "migration-to-buffer-with-pts-dts", - only: :test}, + {:membrane_rtp_h264_plugin, "~> 0.7.0"}, {:membrane_rtp_mpegaudio_plugin, "~> 0.7.0", only: :test}, {:membrane_h264_ffmpeg_plugin, "~> 0.15.0", only: :test}, {:membrane_element_pcap, github: "membraneframework/membrane-element-pcap", only: :test}, diff --git a/mix.lock b/mix.lock index b0bce116..57faf6f3 100644 --- a/mix.lock +++ b/mix.lock @@ -32,7 +32,7 @@ "membrane_h264_ffmpeg_plugin": {:hex, :membrane_h264_ffmpeg_plugin, "0.15.0", "717ffd95f250eedf4f5f66c0f2850a88b6682c54b3a0dfa32181478a4da19fda", [:mix], [{:bunch, "~> 1.3.0", [hex: :bunch, repo: "hexpm", optional: false]}, {:membrane_caps_video_h264, "~> 0.2.0", [hex: :membrane_caps_video_h264, repo: "hexpm", optional: false]}, {:membrane_caps_video_raw, "~> 0.1.0", [hex: :membrane_caps_video_raw, repo: "hexpm", optional: false]}, {:membrane_common_c, "~> 0.10.0", [hex: :membrane_common_c, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.8.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:ratio, "~> 2.4.0", [hex: :ratio, repo: "hexpm", optional: false]}, {:unifex, "~> 0.7.0", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "e70a81e6af2cfbb211893003c2ea92053ad90cebda4392a42363de719cf5ed89"}, "membrane_hackney_plugin": {:hex, :membrane_hackney_plugin, "0.6.0", "f495da8f8d3b55035d2f38a58b18d16549df9453b2e88517def98a6414a34655", [:mix], [{:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.8.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:mockery, "~> 2.3", [hex: :mockery, repo: "hexpm", optional: false]}], "hexpm", "16beedf5f829b5ba7aa6850882cbd209b1ff5be2dd84ef675c2a31f48837962f"}, "membrane_rtp_format": {:hex, :membrane_rtp_format, "0.3.1", "c6920fdb58660d90e264fcf3587dbb5994fb2f3643d234e59608c82598baebaf", [:mix], [], "hexpm", "d225f453715d165ded7dcb287c45360a4d653ac4a6d3ff70b4acdd244c45fa84"}, - "membrane_rtp_h264_plugin": {:git, "https://github.com/membraneframework/membrane_rtp_h264_plugin.git", "c68dbeda6565549f9ad18a13b861bb943e5b39e7", [branch: "migration-to-buffer-with-pts-dts"]}, + "membrane_rtp_h264_plugin": {:hex, :membrane_rtp_h264_plugin, "0.7.0", "34e8e51d415f5401873d915235d6330672ff3b55304c02e14f5ed24d0f1bf451", [:mix], [{:bunch, "~> 1.2", [hex: :bunch, repo: "hexpm", optional: false]}, {:membrane_caps_video_h264, "~> 0.2.0", [hex: :membrane_caps_video_h264, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.8.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_rtp_format, "~> 0.3.0", [hex: :membrane_rtp_format, repo: "hexpm", optional: false]}], "hexpm", "9d40c4e703dc2c7a9ea645ed88d5cddde156e459ef3d66e1484fd7b43b5004e4"}, "membrane_rtp_mpegaudio_plugin": {:hex, :membrane_rtp_mpegaudio_plugin, "0.7.0", "bb26b4bd290166af311061eda37188502303887e1cd775183bdf73c6ceba8db7", [:mix], [{:membrane_caps_audio_mpeg, "~> 0.2.0", [hex: :membrane_caps_audio_mpeg, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.8.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_rtp_format, "~> 0.3.0", [hex: :membrane_rtp_format, repo: "hexpm", optional: false]}], "hexpm", "e9ee553a2bab1704acc68b36cd7d099e2126634c6cd9fc7eb5634c656947d3b7"}, "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"}, From 5ff78672e576d0c6f0945c740aca66028ea0bb30 Mon Sep 17 00:00:00 2001 From: AndrzejPodo Date: Mon, 6 Dec 2021 10:55:30 +0100 Subject: [PATCH 20/20] update membrane_rtp_h264_plugin and add tag option to membrane_element_pcap --- mix.exs | 5 +++-- mix.lock | 9 +++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/mix.exs b/mix.exs index ac657fe7..f83e7bfc 100644 --- a/mix.exs +++ b/mix.exs @@ -75,10 +75,11 @@ defmodule Membrane.RTP.Plugin.MixProject do [ {:membrane_core, "~> 0.8.0", override: true}, {:membrane_rtp_format, "~> 0.3.1"}, - {:membrane_rtp_h264_plugin, "~> 0.7.0"}, + {:membrane_rtp_h264_plugin, "~> 0.7.1"}, {:membrane_rtp_mpegaudio_plugin, "~> 0.7.0", only: :test}, {:membrane_h264_ffmpeg_plugin, "~> 0.15.0", only: :test}, - {:membrane_element_pcap, github: "membraneframework/membrane-element-pcap", only: :test}, + {:membrane_element_pcap, + github: "membraneframework/membrane-element-pcap", tag: "v0.4.0", only: :test}, {:membrane_element_udp, "~> 0.6.0", only: :test}, {:membrane_hackney_plugin, "~> 0.6.0", only: :test}, {:ex_libsrtp, "~> 0.3.0", optional: true}, diff --git a/mix.lock b/mix.lock index 57faf6f3..19e349df 100644 --- a/mix.lock +++ b/mix.lock @@ -8,10 +8,11 @@ "coerce": {:hex, :coerce, "1.0.1", "211c27386315dc2894ac11bc1f413a0e38505d808153367bd5c6e75a4003d096", [:mix], [], "hexpm", "b44a691700f7a1a15b4b7e2ff1fa30bebd669929ac8aa43cffe9e2f8bf051cf1"}, "credo": {:hex, :credo, "1.6.1", "7dc76dcdb764a4316c1596804c48eada9fff44bd4b733a91ccbf0c0f368be61e", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "698607fb5993720c7e93d2d8e76f2175bba024de964e160e2f7151ef3ab82ac5"}, "dialyxir": {:hex, :dialyxir, "1.1.0", "c5aab0d6e71e5522e77beff7ba9e08f8e02bad90dfbeffae60eaf0cb47e29488", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "07ea8e49c45f15264ebe6d5b93799d4dd56a44036cf42d0ad9c960bc266c0b9a"}, - "earmark_parser": {:hex, :earmark_parser, "1.4.17", "6f3c7e94170377ba45241d394389e800fb15adc5de51d0a3cd52ae766aafd63f", [:mix], [], "hexpm", "f93ac89c9feca61c165b264b5837bf82344d13bebc634cd575cb711e2e342023"}, + "earmark_parser": {:hex, :earmark_parser, "1.4.18", "e1b2be73eb08a49fb032a0208bf647380682374a725dfb5b9e510def8397f6f2", [:mix], [], "hexpm", "114a0e85ec3cf9e04b811009e73c206394ffecfcc313e0b346de0d557774ee97"}, "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, "ex_doc": {:hex, :ex_doc, "0.26.0", "1922164bac0b18b02f84d6f69cab1b93bc3e870e2ad18d5dacb50a9e06b542a3", [:mix], [{:earmark_parser, "~> 1.4.0", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "2775d66e494a9a48355db7867478ffd997864c61c65a47d31c4949459281c78d"}, "ex_libsrtp": {:hex, :ex_libsrtp, "0.3.0", "964cf207fc44357b9ef103ade78e99004de70df8ee44bf1c8f6a18051d0d3667", [:mix], [{:bunch, "~> 1.3", [hex: :bunch, repo: "hexpm", optional: false]}, {:unifex, "~> 0.7.0", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "62c2c40f6481ee670c864d26aa6496ad851c9b694bbd5367111bc93f3be2e44a"}, + "ex_pcap": {:git, "https://github.com/membraneframework/expcap.git", "07c5bfa25280ea6a28d022d3a206ececf9b9913a", []}, "excoveralls": {:hex, :excoveralls, "0.14.4", "295498f1ae47bdc6dce59af9a585c381e1aefc63298d48172efaaa90c3d251db", [:mix], [{:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "e3ab02f2df4c1c7a519728a6f0a747e71d7d6e846020aae338173619217931c1"}, "expcap": {:git, "https://github.com/Hajto/expcap.git", "0608702b7e22c1f3206014dbe3e1d313cdc17f1c", []}, "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"}, @@ -23,16 +24,16 @@ "makeup_elixir": {:hex, :makeup_elixir, "0.15.2", "dc72dfe17eb240552857465cc00cce390960d9a0c055c4ccd38b70629227e97c", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.1", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "fd23ae48d09b32eff49d4ced2b43c9f086d402ee4fd4fcb2d7fad97fa8823e75"}, "makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"}, "membrane_caps_audio_mpeg": {:hex, :membrane_caps_audio_mpeg, "0.2.0", "9cf9a63f03e25b31cf31445325aa68e60a07d36ee1e759caa1422fa45df49367", [:mix], [], "hexpm", "f7a80e4841d46164c148be880932ac7425329f4bcc32eb36ad2e47eafe5f23e4"}, - "membrane_caps_video_h264": {:hex, :membrane_caps_video_h264, "0.2.0", "ecfe3a83b5e349b6ca56980c543cf3bf2275ccfb7769126fe62798f08fd7a2fe", [:mix], [], "hexpm", "36e447b8fd42cd4ad79bfa4dbf2e7f2ff3a9030c0ffa85ba951d21868a36376b"}, + "membrane_caps_video_h264": {:hex, :membrane_caps_video_h264, "0.2.1", "8545074299dc48d8afe106b0be1ecf3097e997f0e9f32557f435c123135050fe", [:mix], [], "hexpm", "a14a8dcd0d2c205ca2cdbe5c7df24ec3e34a027e1caf511a71ca4c72b371b200"}, "membrane_caps_video_raw": {:hex, :membrane_caps_video_raw, "0.1.0", "6aa751b0c338ea6672540b7ec7ad2be0d23bad931b8a8776757da9b279070a3b", [:mix], [], "hexpm", "3f60d65189bd9e3b0ab77e0ebf2e0c1b04d0fd6f67c546fc1d54d9958c362ce4"}, "membrane_common_c": {:hex, :membrane_common_c, "0.10.0", "9db4fa2964039e3cd518b333d27ff7492c9746cd6af5ebd217a101fa1bccbdcd", [:mix], [{:membrane_core, "~> 0.8.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:shmex, "~> 0.4.0", [hex: :shmex, repo: "hexpm", optional: false]}, {:unifex, "~> 0.7.0", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "bb3312f1b25d9eae3226b49d1f0daa6535fe38ac72d8742adb60b4373c531566"}, "membrane_core": {:hex, :membrane_core, "0.8.1", "33df0e4c76c05a2be57042893b05516886462baefdf95b7299e4d2f5db32dea3", [:mix], [{:bunch, "~> 1.3", [hex: :bunch, repo: "hexpm", optional: false]}, {:qex, "~> 0.3", [hex: :qex, repo: "hexpm", optional: false]}, {:ratio, "~> 2.0", [hex: :ratio, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "5574db35f08de1e95648f9c3acce38c5f318216c5a5ae5e3f253f7edad8fdc1b"}, - "membrane_element_pcap": {:git, "https://github.com/membraneframework/membrane-element-pcap.git", "e9d9c47bc1ed5897250e6463710b4adb0d76c4ed", []}, + "membrane_element_pcap": {:git, "https://github.com/membraneframework/membrane-element-pcap.git", "e9d9c47bc1ed5897250e6463710b4adb0d76c4ed", [tag: "v0.4.0"]}, "membrane_element_udp": {:hex, :membrane_element_udp, "0.6.0", "40174620e1e08bb5e0dc498f5b2aec16eb29b6995127e43d1bd45599147d5d9f", [:mix], [{:membrane_core, "~> 0.8.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:mockery, "~> 2.3.0", [hex: :mockery, repo: "hexpm", optional: false]}], "hexpm", "ce7d9941ec65628bba7e8da4d7414cd588fc40c794a2a609859c85cdfcc718e2"}, "membrane_h264_ffmpeg_plugin": {:hex, :membrane_h264_ffmpeg_plugin, "0.15.0", "717ffd95f250eedf4f5f66c0f2850a88b6682c54b3a0dfa32181478a4da19fda", [:mix], [{:bunch, "~> 1.3.0", [hex: :bunch, repo: "hexpm", optional: false]}, {:membrane_caps_video_h264, "~> 0.2.0", [hex: :membrane_caps_video_h264, repo: "hexpm", optional: false]}, {:membrane_caps_video_raw, "~> 0.1.0", [hex: :membrane_caps_video_raw, repo: "hexpm", optional: false]}, {:membrane_common_c, "~> 0.10.0", [hex: :membrane_common_c, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.8.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:ratio, "~> 2.4.0", [hex: :ratio, repo: "hexpm", optional: false]}, {:unifex, "~> 0.7.0", [hex: :unifex, repo: "hexpm", optional: false]}], "hexpm", "e70a81e6af2cfbb211893003c2ea92053ad90cebda4392a42363de719cf5ed89"}, "membrane_hackney_plugin": {:hex, :membrane_hackney_plugin, "0.6.0", "f495da8f8d3b55035d2f38a58b18d16549df9453b2e88517def98a6414a34655", [:mix], [{:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.8.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:mockery, "~> 2.3", [hex: :mockery, repo: "hexpm", optional: false]}], "hexpm", "16beedf5f829b5ba7aa6850882cbd209b1ff5be2dd84ef675c2a31f48837962f"}, "membrane_rtp_format": {:hex, :membrane_rtp_format, "0.3.1", "c6920fdb58660d90e264fcf3587dbb5994fb2f3643d234e59608c82598baebaf", [:mix], [], "hexpm", "d225f453715d165ded7dcb287c45360a4d653ac4a6d3ff70b4acdd244c45fa84"}, - "membrane_rtp_h264_plugin": {:hex, :membrane_rtp_h264_plugin, "0.7.0", "34e8e51d415f5401873d915235d6330672ff3b55304c02e14f5ed24d0f1bf451", [:mix], [{:bunch, "~> 1.2", [hex: :bunch, repo: "hexpm", optional: false]}, {:membrane_caps_video_h264, "~> 0.2.0", [hex: :membrane_caps_video_h264, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.8.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_rtp_format, "~> 0.3.0", [hex: :membrane_rtp_format, repo: "hexpm", optional: false]}], "hexpm", "9d40c4e703dc2c7a9ea645ed88d5cddde156e459ef3d66e1484fd7b43b5004e4"}, + "membrane_rtp_h264_plugin": {:hex, :membrane_rtp_h264_plugin, "0.7.1", "3efcdd7f00b0e417ceb2fa4bfe2f101dd1f0f62f0e185c3a350817b9ea981a57", [:mix], [{:bunch, "~> 1.2", [hex: :bunch, repo: "hexpm", optional: false]}, {:membrane_caps_video_h264, "~> 0.2.0", [hex: :membrane_caps_video_h264, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.8.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_rtp_format, "~> 0.3.0", [hex: :membrane_rtp_format, repo: "hexpm", optional: false]}], "hexpm", "ad8d94410d7f464a0cdb26cd8b3f2fd58e619837308cef9ae2e1838a50cb2589"}, "membrane_rtp_mpegaudio_plugin": {:hex, :membrane_rtp_mpegaudio_plugin, "0.7.0", "bb26b4bd290166af311061eda37188502303887e1cd775183bdf73c6ceba8db7", [:mix], [{:membrane_caps_audio_mpeg, "~> 0.2.0", [hex: :membrane_caps_audio_mpeg, repo: "hexpm", optional: false]}, {:membrane_core, "~> 0.8.0", [hex: :membrane_core, repo: "hexpm", optional: false]}, {:membrane_rtp_format, "~> 0.3.0", [hex: :membrane_rtp_format, repo: "hexpm", optional: false]}], "hexpm", "e9ee553a2bab1704acc68b36cd7d099e2126634c6cd9fc7eb5634c656947d3b7"}, "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},