Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/internal_modules/roc_pipeline/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ SenderSinkConfig::SenderSinkConfig()
: input_sample_spec(DefaultSampleSpec)
, payload_type(rtp::PayloadType_L16_Stereo)
, packet_length(DefaultPacketLength)
, packet_mtu(0)
, enable_timing(false)
, enable_auto_duration(false)
, enable_auto_cts(false)
Expand Down
6 changes: 6 additions & 0 deletions src/internal_modules/roc_pipeline/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,14 @@ struct SenderSinkConfig {
unsigned payload_type;

//! Packet length, in nanoseconds.
//! If zero and packet_mtu is set, derived automatically from packet_mtu.
core::nanoseconds_t packet_length;

//! Maximum packet size (MTU), in bytes.
//! When non-zero and packet_length is zero, packet_length is auto-derived
//! so that the resulting packet fits within this MTU.
size_t packet_mtu;

//! FEC writer parameters.
fec::WriterConfig fec_writer;

Expand Down
56 changes: 55 additions & 1 deletion src/internal_modules/roc_pipeline/sender_session.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,60 @@ bool SenderSession::create_transport_pipeline(SenderEndpoint* source_endpoint,
return false;
}

// Derive packet_length from packet_mtu if packet_length was not explicitly set.
// We use the encoding's sample spec to compute how many samples fit in the MTU.
core::nanoseconds_t effective_packet_length = sink_config_.packet_length;
if (sink_config_.packet_mtu > 0 && sink_config_.packet_length == 0) {
const size_t rtp_header_size = 64; // conservative RTP+FEC header estimate
if (sink_config_.packet_mtu > rtp_header_size) {
const size_t payload_bytes = sink_config_.packet_mtu - rtp_header_size;
// bytes -> samples per channel (PCM s16 = 2 bytes per sample per channel)
const size_t bytes_per_sample =
pkt_encoding->sample_spec.stream_timestamp_2_bytes(1);
const size_t samples_per_chan =
bytes_per_sample > 0 ? payload_bytes / bytes_per_sample : 0;
if (samples_per_chan > 0) {
effective_packet_length =
pkt_encoding->sample_spec.samples_per_chan_2_ns(samples_per_chan);
}
}
if (effective_packet_length <= 0) {
roc_log(LogError,
"sender session: can't derive packet length from mtu=%lu,"
" mtu is too small",
(unsigned long)sink_config_.packet_mtu);
return false;
}
roc_log(LogDebug,
"sender session: derived packet_length=%.3fms from packet_mtu=%lu",
(double)effective_packet_length / core::Millisecond,
(unsigned long)sink_config_.packet_mtu);
} else if (sink_config_.packet_length == 0) {
effective_packet_length = DefaultPacketLength;
}
// If both are set, warn and pick the smaller one.
if (sink_config_.packet_mtu > 0 && sink_config_.packet_length > 0) {
const size_t rtp_header_size = 64;
if (sink_config_.packet_mtu > rtp_header_size) {
const size_t payload_bytes = sink_config_.packet_mtu - rtp_header_size;
const size_t bytes_per_sample =
pkt_encoding->sample_spec.stream_timestamp_2_bytes(1);
const size_t samples_per_chan =
bytes_per_sample > 0 ? payload_bytes / bytes_per_sample : 0;
if (samples_per_chan > 0) {
const core::nanoseconds_t mtu_length =
pkt_encoding->sample_spec.samples_per_chan_2_ns(samples_per_chan);
if (mtu_length < effective_packet_length) {
roc_log(LogWarn,
"sender session: both --packet-len and --packet-mtu set;"
" using smaller value derived from mtu=%.3fms",
(double)mtu_length / core::Millisecond);
effective_packet_length = mtu_length;
}
}
}
}

// First part of pipeline: chained packet writers from packetizer to endpoint.
// Packetizer writes packet to this pipeline, and it the end it writes
// packets into endpoint outbound writers.
Expand Down Expand Up @@ -133,7 +187,7 @@ bool SenderSession::create_transport_pipeline(SenderEndpoint* source_endpoint,

packetizer_.reset(new (packetizer_) audio::Packetizer(
*pkt_writer, source_endpoint->outbound_composer(), *sequencer_,
*payload_encoder_, packet_factory_, sink_config_.packet_length, in_spec));
*payload_encoder_, packet_factory_, effective_packet_length, in_spec));
if (!packetizer_ || !packetizer_->is_valid()) {
return false;
}
Expand Down
6 changes: 6 additions & 0 deletions src/tools/roc_send/cmdline.ggo
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ section "Options"
option "packet-len" - "Outgoing packet length, TIME units"
string optional

option "packet-mtu" - "Maximum packet size (MTU), SIZE units; auto-derives packet length when --packet-len is not set"
typestr="SIZE" string optional

option "packet-encoding" - "RTP payload type and encoding for outgoing packets, in form PT:FORMAT/RATE/CHANNELS, e.g. 96:s16/48000/stereo"
typestr="PT_SPEC" string optional

option "frame-len" - "Duration of the internal frames, TIME units"
typestr="TIME" string optional

Expand Down
43 changes: 43 additions & 0 deletions src/tools/roc_send/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "roc_node/context.h"
#include "roc_node/sender.h"
#include "roc_pipeline/sender_sink.h"
#include "roc_rtp/encoding.h"
#include "roc_sndio/backend_dispatcher.h"
#include "roc_sndio/backend_map.h"
#include "roc_sndio/print_supported.h"
Expand Down Expand Up @@ -113,6 +114,31 @@ int main(int argc, char** argv) {
}
}

if (args.packet_mtu_given) {
if (!core::parse_size(args.packet_mtu_arg, sender_config.packet_mtu)) {
roc_log(LogError, "invalid --packet-mtu: bad format");
return 1;
}
if (sender_config.packet_mtu == 0) {
roc_log(LogError, "invalid --packet-mtu: should be > 0");
return 1;
}
// Signal to the pipeline to derive packet_length from MTU.
if (!args.packet_len_given) {
sender_config.packet_length = 0;
}
}

if (args.packet_encoding_given) {
rtp::Encoding enc;
if (!rtp::parse_encoding(args.packet_encoding_arg, enc)) {
roc_log(LogError, "invalid --packet-encoding: bad format,"
" expected PT:FORMAT/RATE/CHANNELS, e.g. 96:s16/48000/stereo");
return 1;
}
sender_config.payload_type = enc.payload_type;
}

if (args.source_given) {
address::EndpointUri source_endpoint(heap_arena);
if (!address::parse_endpoint_uri(
Expand Down Expand Up @@ -268,6 +294,23 @@ int main(int argc, char** argv) {
return 1;
}

if (args.packet_encoding_given) {
rtp::Encoding enc;
if (!rtp::parse_encoding(args.packet_encoding_arg, enc)) {
roc_log(LogError, "invalid --packet-encoding");
return 1;
}
// Only register if it's not a built-in payload type.
if (!context.encoding_map().find_by_pt(enc.payload_type)) {
if (!context.encoding_map().add_encoding(enc)) {
roc_log(LogError,
"can't register --packet-encoding: payload type %u already exists",
enc.payload_type);
return 1;
}
}
}

sndio::BackendDispatcher backend_dispatcher(context.arena());
if (args.list_supported_given) {
if (!address::print_supported(context.arena())) {
Expand Down
Loading