diff --git a/CHANGELOG.md b/CHANGELOG.md index 4054ac6516..a695ec1277 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,9 @@ ### NEXT +- Bump up Meson from 1.5.0 to 1.9.1 ([PR #1634](https://github.com/versatica/mediasoup/pull/1634)). - `SeqManager`: Fix, properly account out of order drops until an input is forwarded ([#1635](https://github.com/versatica/mediasoup/pull/1635)), thanks to @pnts-se-whereby for reporting. +- `RtpParameters`: Add `msid` optional field ([PR #1634](https://github.com/versatica/mediasoup/pull/1634)). ### 3.19.6 diff --git a/node/src/ortc.ts b/node/src/ortc.ts index 9581b1a932..d89fbcd484 100644 --- a/node/src/ortc.ts +++ b/node/src/ortc.ts @@ -140,6 +140,11 @@ export function validateRtpParameters(params: RtpParameters): void { params.rtcp = {}; } + // msid is optional. + if (params.msid && typeof params.msid !== 'string') { + throw new TypeError('params.msid is not a string'); + } + validateRtcpParameters(params.rtcp); } @@ -454,6 +459,7 @@ export function getConsumableRtpParameters( headerExtensions: [], encodings: [], rtcp: {}, + msid: undefined, }; for (const codec of params.codecs) { @@ -542,6 +548,8 @@ export function getConsumableRtpParameters( reducedSize: true, }; + consumableParams.msid = params.msid; + return consumableParams; } @@ -600,6 +608,7 @@ export function getConsumerRtpParameters({ headerExtensions: [], encodings: [], rtcp: consumableRtpParameters.rtcp, + msid: consumableRtpParameters.msid, }; for (const capCodec of remoteRtpCapabilities.codecs!) { @@ -790,6 +799,7 @@ export function getPipeConsumerRtpParameters({ headerExtensions: [], encodings: [], rtcp: consumableRtpParameters.rtcp, + msid: consumableRtpParameters.msid, }; const consumableCodecs = diff --git a/node/src/rtpParametersFbsUtils.ts b/node/src/rtpParametersFbsUtils.ts index 9f2fcbea2f..e489bb257e 100644 --- a/node/src/rtpParametersFbsUtils.ts +++ b/node/src/rtpParametersFbsUtils.ts @@ -119,14 +119,15 @@ export function serializeRtpParameters( ); const midOffset = builder.createString(rtpParameters.mid); + const msidOffset = builder.createString(rtpParameters.msid); FbsRtpParameters.startRtpParameters(builder); FbsRtpParameters.addMid(builder, midOffset); FbsRtpParameters.addCodecs(builder, codecsOffset); - FbsRtpParameters.addHeaderExtensions(builder, headerExtensionsOffset); FbsRtpParameters.addEncodings(builder, encodingsOffset); FbsRtpParameters.addRtcp(builder, rtcpOffset); + FbsRtpParameters.addMsid(builder, msidOffset); return FbsRtpParameters.endRtpParameters(builder); } @@ -494,12 +495,12 @@ export function parseRtpEncodingParameters( ): RtpEncodingParameters { return { ssrc: data.ssrc() ?? undefined, - rid: data.rid() ?? undefined, + rid: data.rid() || undefined, codecPayloadType: data.codecPayloadType() !== null ? data.codecPayloadType()! : undefined, rtx: data.rtx() ? { ssrc: data.rtx()!.ssrc() } : undefined, dtx: data.dtx(), - scalabilityMode: data.scalabilityMode() ?? undefined, + scalabilityMode: data.scalabilityMode() || undefined, maxBitrate: data.maxBitrate() !== null ? data.maxBitrate()! : undefined, }; } @@ -533,16 +534,17 @@ export function parseRtpParameters(data: FbsRtpParameters): RtpParameters { const fbsRtcp = data.rtcp()!; rtcp = { - cname: fbsRtcp.cname() ?? undefined, + cname: fbsRtcp.cname() || undefined, reducedSize: fbsRtcp.reducedSize(), }; } return { - mid: data.mid() ?? undefined, + mid: data.mid() || undefined, codecs, headerExtensions, encodings, rtcp, + msid: data.msid() || undefined, }; } diff --git a/node/src/rtpParametersTypes.ts b/node/src/rtpParametersTypes.ts index a68bce3451..5131dd514e 100644 --- a/node/src/rtpParametersTypes.ts +++ b/node/src/rtpParametersTypes.ts @@ -204,6 +204,12 @@ export type RtpParameters = { * Parameters used for RTCP. */ rtcp?: RtcpParameters; + /** + * MSID (WebRTC MediaStream Identification). + * + * @see https://datatracker.ietf.org/doc/html/rfc8830 + */ + msid?: string; }; /** diff --git a/node/src/rtpStreamStatsFbsUtils.ts b/node/src/rtpStreamStatsFbsUtils.ts index 96d941e739..90f45d415d 100644 --- a/node/src/rtpStreamStatsFbsUtils.ts +++ b/node/src/rtpStreamStatsFbsUtils.ts @@ -66,7 +66,7 @@ function parseBaseStreamStats( timestamp: Number(binary.timestamp()), ssrc: binary.ssrc(), rtxSsrc: binary.rtxSsrc() ?? undefined, - rid: binary.rid() ?? undefined, + rid: binary.rid() || undefined, kind: binary.kind() === FbsRtpParameters.MediaKind.AUDIO ? 'audio' : 'video', mimeType: binary.mimeType()!, diff --git a/node/src/test/test-Consumer.ts b/node/src/test/test-Consumer.ts index 0d444ab8a6..b486dc6309 100644 --- a/node/src/test/test-Consumer.ts +++ b/node/src/test/test-Consumer.ts @@ -85,6 +85,7 @@ const ctx: TestContext = { rtcp: { cname: 'FOOBAR', }, + msid: '1111-1111-1111-1111 2222-2222-2222-2222', }, appData: { foo: 1, bar: '2' }, }), @@ -294,6 +295,9 @@ test('transport.consume() succeeds', async () => { }, rtcpFeedback: [], }); + expect(audioConsumer.rtpParameters.msid).toBe( + '1111-1111-1111-1111 2222-2222-2222-2222' + ); expect(audioConsumer.type).toBe('simple'); expect(audioConsumer.paused).toBe(false); expect(audioConsumer.producerPaused).toBe(false); @@ -380,6 +384,7 @@ test('transport.consume() succeeds', async () => { parameters: { apt: 103 }, rtcpFeedback: [], }); + expect(videoConsumer.rtpParameters.msid).toBe(undefined); expect(videoConsumer.type).toBe('simulcast'); expect(videoConsumer.paused).toBe(true); expect(videoConsumer.producerPaused).toBe(true); @@ -654,6 +659,9 @@ test('consumer.dump() succeeds', async () => { ssrc: audioConsumer.rtpParameters.encodings![0]!.ssrc, }), ]); + expect(dump1.rtpParameters.msid).toBe( + '1111-1111-1111-1111 2222-2222-2222-2222' + ); expect(dump1.type).toBe('simple'); expect(Array.isArray(dump1.consumableRtpEncodings)).toBe(true); expect(dump1.consumableRtpEncodings!.length).toBe(1); @@ -734,6 +742,7 @@ test('consumer.dump() succeeds', async () => { scalabilityMode: 'L4T5', }, ]); + expect(dump2.rtpParameters.msid).toBe(undefined); expect(Array.isArray(dump2.consumableRtpEncodings)).toBe(true); expect(dump2.consumableRtpEncodings!.length).toBe(4); expect(dump2.consumableRtpEncodings![0]).toEqual( diff --git a/node/src/test/test-PipeTransport.ts b/node/src/test/test-PipeTransport.ts index e62891181c..560221bcc8 100644 --- a/node/src/test/test-PipeTransport.ts +++ b/node/src/test/test-PipeTransport.ts @@ -106,6 +106,7 @@ const ctx: TestContext = { rtcp: { cname: 'FOOBAR', }, + msid: 'aaaa-bbbb', }, appData: { foo: 'bar2' }, }), @@ -854,6 +855,7 @@ test('transport.consume() for a pipe Producer succeeds', async () => { expect(typeof videoConsumer.rtpParameters.encodings![0]!.rtx?.ssrc).toBe( 'number' ); + expect(videoConsumer.rtpParameters.msid).toBe('aaaa-bbbb'); expect(videoConsumer.type).toBe('simulcast'); expect(videoConsumer.paused).toBe(false); expect(videoConsumer.producerPaused).toBe(false); diff --git a/rust/benches/producer.rs b/rust/benches/producer.rs index 249bf75118..b7511418b2 100644 --- a/rust/benches/producer.rs +++ b/rust/benches/producer.rs @@ -123,6 +123,7 @@ fn audio_producer_options() -> ProducerOptions { cname: Some("audio-1".to_string()), ..RtcpParameters::default() }, + msid: None, }, ) } @@ -202,6 +203,7 @@ fn video_producer_options() -> ProducerOptions { cname: Some("video-1".to_string()), ..RtcpParameters::default() }, + msid: None, }, ) } diff --git a/rust/src/ortc.rs b/rust/src/ortc.rs index 2bb53bef16..42b507a0f0 100644 --- a/rust/src/ortc.rs +++ b/rust/src/ortc.rs @@ -711,6 +711,8 @@ pub(crate) fn get_consumable_rtp_parameters( reduced_size: true, }; + consumable_params.msid.clone_from(¶ms.msid); + consumable_params } @@ -753,6 +755,7 @@ pub(crate) fn get_consumer_rtp_parameters( ) -> Result { let mut consumer_params = RtpParameters { rtcp: consumable_rtp_parameters.rtcp.clone(), + msid: consumable_rtp_parameters.msid.clone(), ..RtpParameters::default() }; @@ -933,6 +936,7 @@ pub(crate) fn get_pipe_consumer_rtp_parameters( header_extensions: vec![], encodings: vec![], rtcp: consumable_rtp_parameters.rtcp.clone(), + msid: consumable_rtp_parameters.msid.clone(), }; for codec in &consumable_rtp_parameters.codecs { diff --git a/rust/src/ortc/tests.rs b/rust/src/ortc/tests.rs index 5db5086c29..f6fa7c2f7d 100644 --- a/rust/src/ortc/tests.rs +++ b/rust/src/ortc/tests.rs @@ -235,6 +235,7 @@ fn get_producer_rtp_parameters_mapping_get_consumable_rtp_parameters_get_consume cname: Some("qwerty1234".to_string()), ..RtcpParameters::default() }, + msid: None, }; let rtp_mapping = @@ -696,6 +697,7 @@ fn get_producer_rtp_parameters_mapping_unsupported() { cname: Some("qwerty1234".to_string()), ..RtcpParameters::default() }, + msid: None, }; assert!(matches!( diff --git a/rust/src/rtp_parameters.rs b/rust/src/rtp_parameters.rs index 8382956933..95a282c1ba 100644 --- a/rust/src/rtp_parameters.rs +++ b/rust/src/rtp_parameters.rs @@ -125,6 +125,7 @@ impl<'a> TryFromFbs<'a> for RtpParameters { .map(|cname| cname.to_string()), reduced_size: rtp_parameters.rtcp()?.reduced_size()?, }, + msid: rtp_parameters.msid()?.map(|msid| msid.to_string()), }) } } @@ -354,6 +355,7 @@ impl ToFbs for RtpParameters { cname: self.rtcp.cname.clone(), reduced_size: self.rtcp.reduced_size, }), + msid: self.msid.clone(), } } } diff --git a/rust/tests/integration/consumer.rs b/rust/tests/integration/consumer.rs index 7b8accf844..3dbd4e51ce 100644 --- a/rust/tests/integration/consumer.rs +++ b/rust/tests/integration/consumer.rs @@ -108,6 +108,7 @@ fn audio_producer_options() -> ProducerOptions { cname: Some("FOOBAR".to_string()), ..RtcpParameters::default() }, + msid: Some("1111-1111-1111-1111 2222-2222-2222-2222".to_string()), }, ); @@ -186,6 +187,7 @@ fn video_producer_options() -> ProducerOptions { cname: Some("FOOBAR".to_string()), ..RtcpParameters::default() }, + msid: None, }, ); @@ -456,6 +458,10 @@ fn consume_succeeds() { rtcp_feedback: vec![], }] ); + assert_eq!( + audio_consumer.rtp_parameters().msid, + Some("1111-1111-1111-1111 2222-2222-2222-2222".to_string()) + ); assert_eq!(audio_consumer.r#type(), ConsumerType::Simple); assert!(!audio_consumer.paused()); assert!(!audio_consumer.producer_paused()); @@ -554,6 +560,7 @@ fn consume_succeeds() { }, ] ); + assert_eq!(video_consumer.rtp_parameters().msid, None); assert_eq!(video_consumer.r#type(), ConsumerType::Simulcast); assert!(video_consumer.paused()); assert!(video_consumer.producer_paused()); diff --git a/rust/tests/integration/pipe_transport.rs b/rust/tests/integration/pipe_transport.rs index 0d3aa45242..5705ea6c66 100644 --- a/rust/tests/integration/pipe_transport.rs +++ b/rust/tests/integration/pipe_transport.rs @@ -83,6 +83,7 @@ fn audio_producer_options() -> ProducerOptions { cname: Some("FOOBAR".to_string()), ..RtcpParameters::default() }, + msid: None, }, ); @@ -142,6 +143,7 @@ fn video_producer_options() -> ProducerOptions { cname: Some("FOOBAR".to_string()), ..RtcpParameters::default() }, + msid: Some("aaaa-bbbb".to_string()), }, ); @@ -1023,6 +1025,10 @@ fn consume_for_pipe_producer_succeeds() { assert_eq!(video_consumer.rtp_parameters().encodings.len(), 1); assert!(video_consumer.rtp_parameters().encodings[0].ssrc.is_some()); assert!(video_consumer.rtp_parameters().encodings[0].rtx.is_some()); + assert_eq!( + video_consumer.rtp_parameters().msid, + Some("aaaa-bbbb".to_string()) + ); assert_eq!(video_consumer.r#type(), ConsumerType::Simulcast); assert!(!video_consumer.paused()); assert!(video_consumer.producer_paused()); diff --git a/rust/tests/integration/producer.rs b/rust/tests/integration/producer.rs index 6dba387418..583b3590f4 100644 --- a/rust/tests/integration/producer.rs +++ b/rust/tests/integration/producer.rs @@ -97,6 +97,7 @@ fn audio_producer_options() -> ProducerOptions { cname: Some("audio-1".to_string()), ..RtcpParameters::default() }, + msid: None, }, ); @@ -172,6 +173,7 @@ fn video_producer_options() -> ProducerOptions { cname: Some("video-1".to_string()), ..RtcpParameters::default() }, + msid: None, }, ); @@ -415,6 +417,7 @@ fn produce_wrong_arguments() { let produce_result = transport_1 .produce(ProducerOptions::new(MediaKind::Video, { let mut parameters = RtpParameters::default(); + parameters.codecs = vec![ RtpCodecParameters::Video { mime_type: MimeTypeVideo::H264, @@ -441,6 +444,7 @@ fn produce_wrong_arguments() { cname: Some("qwerty".to_string()), ..RtcpParameters::default() }; + parameters })) .await; @@ -453,6 +457,7 @@ fn produce_wrong_arguments() { let produce_result = transport_1 .produce(ProducerOptions::new(MediaKind::Video, { let mut parameters = RtpParameters::default(); + parameters.codecs = vec![ RtpCodecParameters::Video { mime_type: MimeTypeVideo::H264, @@ -484,6 +489,7 @@ fn produce_wrong_arguments() { cname: Some("video-1".to_string()), ..RtcpParameters::default() }; + parameters })) .await; @@ -506,6 +512,7 @@ fn produce_unsupported_codecs() { transport_1 .produce(ProducerOptions::new(MediaKind::Audio, { let mut parameters = RtpParameters::default(); + parameters.codecs = vec![RtpCodecParameters::Audio { mime_type: MimeTypeAudio::Isac, payload_type: 108, @@ -523,6 +530,7 @@ fn produce_unsupported_codecs() { cname: Some("audio".to_string()), ..RtcpParameters::default() }; + parameters })) .await, @@ -534,6 +542,7 @@ fn produce_unsupported_codecs() { let produce_result = transport_1 .produce(ProducerOptions::new(MediaKind::Video, { let mut parameters = RtpParameters::default(); + parameters.codecs = vec![ RtpCodecParameters::Video { mime_type: MimeTypeVideo::H264, @@ -561,6 +570,7 @@ fn produce_unsupported_codecs() { rtx: Some(RtpEncodingParametersRtx { ssrc: 6667 }), ..RtpEncodingParameters::default() }]; + parameters })) .await; @@ -587,6 +597,7 @@ fn produce_already_used_mid_ssrc() { let produce_result = transport_1 .produce(ProducerOptions::new(MediaKind::Audio, { let mut parameters = RtpParameters::default(); + parameters.mid = Some("AUDIO".to_string()); parameters.codecs = vec![RtpCodecParameters::Audio { mime_type: MimeTypeAudio::Opus, @@ -605,6 +616,7 @@ fn produce_already_used_mid_ssrc() { cname: Some("audio-2".to_string()), ..RtcpParameters::default() }; + parameters })) .await; @@ -622,6 +634,7 @@ fn produce_already_used_mid_ssrc() { let produce_result = transport_2 .produce(ProducerOptions::new(MediaKind::Video, { let mut parameters = RtpParameters::default(); + parameters.mid = Some("VIDEO2".to_string()); parameters.codecs = vec![RtpCodecParameters::Video { mime_type: MimeTypeVideo::Vp8, @@ -635,6 +648,7 @@ fn produce_already_used_mid_ssrc() { rtx: Some(RtpEncodingParametersRtx { ssrc: 6667 }), ..RtpEncodingParameters::default() }]; + parameters })) .await; @@ -652,6 +666,7 @@ fn produce_no_mid_single_encoding_without_rid_or_ssrc() { let produce_result = transport_1 .produce(ProducerOptions::new(MediaKind::Audio, { let mut parameters = RtpParameters::default(); + parameters.codecs = vec![RtpCodecParameters::Audio { mime_type: MimeTypeAudio::Opus, payload_type: 111, @@ -666,6 +681,7 @@ fn produce_no_mid_single_encoding_without_rid_or_ssrc() { cname: Some("audio-2".to_string()), ..RtcpParameters::default() }; + parameters })) .await; diff --git a/rust/types/src/rtp_parameters.rs b/rust/types/src/rtp_parameters.rs index f56e3045a5..11607d8218 100644 --- a/rust/types/src/rtp_parameters.rs +++ b/rust/types/src/rtp_parameters.rs @@ -725,6 +725,10 @@ pub struct RtpParameters { pub encodings: Vec, /// Parameters used for RTCP. pub rtcp: RtcpParameters, + /// MSID (WebRTC MediaStream Identification) as defined in + /// https://datatracker.ietf.org/doc/html/rfc8830 + #[serde(skip_serializing_if = "Option::is_none")] + pub msid: Option, } /// Single value used in RTP codec parameters. diff --git a/worker/fbs/rtpParameters.fbs b/worker/fbs/rtpParameters.fbs index 44512f7a4a..115f901c4b 100644 --- a/worker/fbs/rtpParameters.fbs +++ b/worker/fbs/rtpParameters.fbs @@ -108,6 +108,7 @@ table RtpParameters { header_extensions: [RtpHeaderExtensionParameters] (required); encodings: [RtpEncodingParameters] (required); rtcp: RtcpParameters (required); + msid: string; } table CodecMapping { diff --git a/worker/include/RTC/RtpDictionaries.hpp b/worker/include/RTC/RtpDictionaries.hpp index ae1b6af789..b03219049c 100644 --- a/worker/include/RTC/RtpDictionaries.hpp +++ b/worker/include/RTC/RtpDictionaries.hpp @@ -274,6 +274,7 @@ namespace RTC std::vector encodings; std::vector headerExtensions; RtcpParameters rtcp; + std::string msid; }; } // namespace RTC diff --git a/worker/src/RTC/RtpDictionaries/RtpParameters.cpp b/worker/src/RTC/RtpDictionaries/RtpParameters.cpp index e61924942c..6bef90b31e 100644 --- a/worker/src/RTC/RtpDictionaries/RtpParameters.cpp +++ b/worker/src/RTC/RtpDictionaries/RtpParameters.cpp @@ -105,11 +105,6 @@ namespace RTC if (data->mid()) { this->mid = data->mid()->str(); - - if (this->mid.empty()) - { - MS_THROW_TYPE_ERROR("empty mid"); - } } this->codecs.reserve(data->codecs()->size()); @@ -149,6 +144,12 @@ namespace RTC // This may throw. this->rtcp = RTC::RtcpParameters(data->rtcp()); + // msid is optional. + if (data->msid()) + { + this->msid = data->msid()->str(); + } + // Validate RTP parameters. ValidateCodecs(); ValidateEncodings(); @@ -192,7 +193,7 @@ namespace RTC rtcp = this->rtcp.FillBuffer(builder); return FBS::RtpParameters::CreateRtpParametersDirect( - builder, mid.c_str(), &codecs, &headerExtensions, &encodings, rtcp); + builder, this->mid.c_str(), &codecs, &headerExtensions, &encodings, rtcp, this->msid.c_str()); } const RTC::RtpCodecParameters* RtpParameters::GetCodecForEncoding(RtpEncodingParameters& encoding) const diff --git a/worker/tasks.py b/worker/tasks.py index 18797df73d..d6fde90fec 100644 --- a/worker/tasks.py +++ b/worker/tasks.py @@ -51,7 +51,7 @@ NUM_CORES = len(os.sched_getaffinity(0)) if hasattr(os, 'sched_getaffinity') else os.cpu_count(); PYTHON = os.getenv('PYTHON') or sys.executable; MESON = os.getenv('MESON') or f'{PIP_MESON_NINJA_DIR}/bin/meson'; -MESON_VERSION = os.getenv('MESON_VERSION') or '1.5.0'; +MESON_VERSION = os.getenv('MESON_VERSION') or '1.9.1'; # MESON_ARGS can be used to provide extra configuration parameters to meson, # such as adding defines or changing optimization options. For instance, use # `MESON_ARGS="-Dms_log_trace=true -Dms_log_file_line=true" npm i` to compile