/* * Copyright 2017 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include "pc/rtp_parameters_conversion.h" #include #include "rtc_base/gunit.h" #include "test/gmock.h" using ::testing::UnorderedElementsAre; namespace webrtc { TEST(RtpParametersConversionTest, ToCricketFeedbackParam) { auto result = ToCricketFeedbackParam( {RtcpFeedbackType::CCM, RtcpFeedbackMessageType::FIR}); EXPECT_EQ(cricket::FeedbackParam("ccm", "fir"), result.value()); result = ToCricketFeedbackParam(RtcpFeedback(RtcpFeedbackType::LNTF)); EXPECT_EQ(cricket::FeedbackParam("goog-lntf"), result.value()); result = ToCricketFeedbackParam( {RtcpFeedbackType::NACK, RtcpFeedbackMessageType::GENERIC_NACK}); EXPECT_EQ(cricket::FeedbackParam("nack"), result.value()); result = ToCricketFeedbackParam( {RtcpFeedbackType::NACK, RtcpFeedbackMessageType::PLI}); EXPECT_EQ(cricket::FeedbackParam("nack", "pli"), result.value()); result = ToCricketFeedbackParam(RtcpFeedback(RtcpFeedbackType::REMB)); EXPECT_EQ(cricket::FeedbackParam("goog-remb"), result.value()); result = ToCricketFeedbackParam(RtcpFeedback(RtcpFeedbackType::TRANSPORT_CC)); EXPECT_EQ(cricket::FeedbackParam("transport-cc"), result.value()); } TEST(RtpParametersConversionTest, ToCricketFeedbackParamErrors) { // CCM with missing or invalid message type. auto result = ToCricketFeedbackParam(RtcpFeedback(RtcpFeedbackType::CCM)); EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, result.error().type()); result = ToCricketFeedbackParam( {RtcpFeedbackType::CCM, RtcpFeedbackMessageType::PLI}); EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, result.error().type()); // LNTF with message type (should be left empty). result = ToCricketFeedbackParam( {RtcpFeedbackType::LNTF, RtcpFeedbackMessageType::GENERIC_NACK}); EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, result.error().type()); // NACK with missing or invalid message type. result = ToCricketFeedbackParam(RtcpFeedback(RtcpFeedbackType::NACK)); EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, result.error().type()); result = ToCricketFeedbackParam( {RtcpFeedbackType::NACK, RtcpFeedbackMessageType::FIR}); EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, result.error().type()); // REMB with message type (should be left empty). result = ToCricketFeedbackParam( {RtcpFeedbackType::REMB, RtcpFeedbackMessageType::GENERIC_NACK}); EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, result.error().type()); // TRANSPORT_CC with message type (should be left empty). result = ToCricketFeedbackParam( {RtcpFeedbackType::TRANSPORT_CC, RtcpFeedbackMessageType::FIR}); EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, result.error().type()); } TEST(RtpParametersConversionTest, ToAudioCodec) { RtpCodecParameters codec; codec.name = "AuDiO"; codec.kind = cricket::MEDIA_TYPE_AUDIO; codec.payload_type = 120; codec.clock_rate.emplace(36000); codec.num_channels.emplace(6); codec.parameters["foo"] = "bar"; codec.rtcp_feedback.emplace_back(RtcpFeedbackType::TRANSPORT_CC); auto result = ToCricketCodec(codec); ASSERT_TRUE(result.ok()); EXPECT_EQ("AuDiO", result.value().name); EXPECT_EQ(120, result.value().id); EXPECT_EQ(36000, result.value().clockrate); EXPECT_EQ(6u, result.value().channels); ASSERT_EQ(1u, result.value().params.size()); EXPECT_EQ("bar", result.value().params["foo"]); EXPECT_EQ(1u, result.value().feedback_params.params().size()); EXPECT_TRUE(result.value().feedback_params.Has( cricket::FeedbackParam("transport-cc"))); } TEST(RtpParametersConversionTest, ToVideoCodec) { RtpCodecParameters codec; codec.name = "coolcodec"; codec.kind = cricket::MEDIA_TYPE_VIDEO; codec.payload_type = 101; codec.clock_rate.emplace(90000); codec.parameters["foo"] = "bar"; codec.parameters["PING"] = "PONG"; codec.rtcp_feedback.emplace_back(RtcpFeedbackType::LNTF); codec.rtcp_feedback.emplace_back(RtcpFeedbackType::TRANSPORT_CC); codec.rtcp_feedback.emplace_back(RtcpFeedbackType::NACK, RtcpFeedbackMessageType::PLI); auto result = ToCricketCodec(codec); ASSERT_TRUE(result.ok()); EXPECT_EQ("coolcodec", result.value().name); EXPECT_EQ(101, result.value().id); EXPECT_EQ(90000, result.value().clockrate); ASSERT_EQ(2u, result.value().params.size()); EXPECT_EQ("bar", result.value().params["foo"]); EXPECT_EQ("PONG", result.value().params["PING"]); EXPECT_EQ(3u, result.value().feedback_params.params().size()); EXPECT_TRUE( result.value().feedback_params.Has(cricket::FeedbackParam("goog-lntf"))); EXPECT_TRUE(result.value().feedback_params.Has( cricket::FeedbackParam("transport-cc"))); EXPECT_TRUE(result.value().feedback_params.Has( cricket::FeedbackParam("nack", "pli"))); } // Trying to convert to an AudioCodec if the kind is "video" should fail. TEST(RtpParametersConversionTest, ToCricketCodecInvalidKind) { RtpCodecParameters audio_codec; audio_codec.name = "opus"; audio_codec.kind = cricket::MEDIA_TYPE_VIDEO; audio_codec.payload_type = 111; audio_codec.clock_rate.emplace(48000); audio_codec.num_channels.emplace(2); RtpCodecParameters video_codec; video_codec.name = "VP8"; video_codec.kind = cricket::MEDIA_TYPE_AUDIO; video_codec.payload_type = 102; video_codec.clock_rate.emplace(90000); auto audio_result = ToCricketCodec(audio_codec); EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, audio_result.error().type()); auto video_result = ToCricketCodec(video_codec); EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, video_result.error().type()); // Sanity check that if the kind is correct, the conversion succeeds. audio_codec.kind = cricket::MEDIA_TYPE_AUDIO; video_codec.kind = cricket::MEDIA_TYPE_VIDEO; audio_result = ToCricketCodec(audio_codec); EXPECT_TRUE(audio_result.ok()); video_result = ToCricketCodec(video_codec); EXPECT_TRUE(video_result.ok()); } TEST(RtpParametersConversionTest, ToAudioCodecInvalidParameters) { // Missing channels. RtpCodecParameters codec; codec.name = "opus"; codec.kind = cricket::MEDIA_TYPE_AUDIO; codec.payload_type = 111; codec.clock_rate.emplace(48000); auto result = ToCricketCodec(codec); EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, result.error().type()); // Negative number of channels. codec.num_channels.emplace(-1); result = ToCricketCodec(codec); EXPECT_EQ(RTCErrorType::INVALID_RANGE, result.error().type()); // Missing clock rate. codec.num_channels.emplace(2); codec.clock_rate.reset(); result = ToCricketCodec(codec); EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, result.error().type()); // Negative clock rate. codec.clock_rate.emplace(-48000); result = ToCricketCodec(codec); EXPECT_EQ(RTCErrorType::INVALID_RANGE, result.error().type()); // Sanity check that conversion succeeds if these errors are fixed. codec.clock_rate.emplace(48000); result = ToCricketCodec(codec); EXPECT_TRUE(result.ok()); } TEST(RtpParametersConversionTest, ToVideoCodecInvalidParameters) { // Missing clock rate. RtpCodecParameters codec; codec.name = "VP8"; codec.kind = cricket::MEDIA_TYPE_VIDEO; codec.payload_type = 102; auto result = ToCricketCodec(codec); EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, result.error().type()); // Invalid clock rate. codec.clock_rate.emplace(48000); result = ToCricketCodec(codec); EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, result.error().type()); // Channels set (should be unset). codec.clock_rate.emplace(90000); codec.num_channels.emplace(2); result = ToCricketCodec(codec); EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, result.error().type()); // Sanity check that conversion succeeds if these errors are fixed. codec.num_channels.reset(); result = ToCricketCodec(codec); EXPECT_TRUE(result.ok()); } TEST(RtpParametersConversionTest, ToCricketCodecInvalidPayloadType) { RtpCodecParameters codec; codec.name = "VP8"; codec.kind = cricket::MEDIA_TYPE_VIDEO; codec.clock_rate.emplace(90000); codec.payload_type = -1000; auto result = ToCricketCodec(codec); EXPECT_EQ(RTCErrorType::INVALID_RANGE, result.error().type()); // Max payload type is 127. codec.payload_type = 128; result = ToCricketCodec(codec); EXPECT_EQ(RTCErrorType::INVALID_RANGE, result.error().type()); // Sanity check that conversion succeeds with a valid payload type. codec.payload_type = 127; result = ToCricketCodec(codec); EXPECT_TRUE(result.ok()); } // There are already tests for ToCricketFeedbackParam, but ensure that those // errors are propagated from ToCricketCodec. TEST(RtpParametersConversionTest, ToCricketCodecInvalidRtcpFeedback) { RtpCodecParameters codec; codec.name = "VP8"; codec.kind = cricket::MEDIA_TYPE_VIDEO; codec.clock_rate.emplace(90000); codec.payload_type = 99; codec.rtcp_feedback.emplace_back(RtcpFeedbackType::CCM, RtcpFeedbackMessageType::PLI); auto result = ToCricketCodec(codec); EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, result.error().type()); // Sanity check that conversion succeeds without invalid feedback. codec.rtcp_feedback.clear(); result = ToCricketCodec(codec); EXPECT_TRUE(result.ok()); } TEST(RtpParametersConversionTest, ToCricketCodecs) { std::vector codecs; RtpCodecParameters codec; codec.name = "VP8"; codec.kind = cricket::MEDIA_TYPE_VIDEO; codec.clock_rate.emplace(90000); codec.payload_type = 99; codecs.push_back(codec); codec.name = "VP9"; codec.payload_type = 100; codecs.push_back(codec); auto result = ToCricketCodecs(codecs); ASSERT_TRUE(result.ok()); ASSERT_EQ(2u, result.value().size()); EXPECT_EQ("VP8", result.value()[0].name); EXPECT_EQ(99, result.value()[0].id); EXPECT_EQ("VP9", result.value()[1].name); EXPECT_EQ(100, result.value()[1].id); } TEST(RtpParametersConversionTest, ToCricketCodecsDuplicatePayloadType) { std::vector codecs; RtpCodecParameters codec; codec.name = "VP8"; codec.kind = cricket::MEDIA_TYPE_VIDEO; codec.clock_rate.emplace(90000); codec.payload_type = 99; codecs.push_back(codec); codec.name = "VP9"; codec.payload_type = 99; codecs.push_back(codec); auto result = ToCricketCodecs(codecs); EXPECT_EQ(RTCErrorType::INVALID_PARAMETER, result.error().type()); // Sanity check that this succeeds without the duplicate payload type. codecs[1].payload_type = 120; result = ToCricketCodecs(codecs); EXPECT_TRUE(result.ok()); } TEST(RtpParametersConversionTest, ToCricketStreamParamsVecSimple) { std::vector encodings; RtpEncodingParameters encoding; encoding.ssrc.emplace(0xbaadf00d); encodings.push_back(encoding); auto result = ToCricketStreamParamsVec(encodings); ASSERT_TRUE(result.ok()); ASSERT_EQ(1u, result.value().size()); EXPECT_EQ(1u, result.value()[0].ssrcs.size()); EXPECT_EQ(0xbaadf00d, result.value()[0].first_ssrc()); } // No encodings should be accepted; an endpoint may want to prepare a // decoder/encoder without having something to receive/send yet. TEST(RtpParametersConversionTest, ToCricketStreamParamsVecNoEncodings) { std::vector encodings; auto result = ToCricketStreamParamsVec(encodings); ASSERT_TRUE(result.ok()); EXPECT_EQ(0u, result.value().size()); } // An encoding without SSRCs should be accepted. This could be the case when // SSRCs aren't signaled and payload-type based demuxing is used. TEST(RtpParametersConversionTest, ToCricketStreamParamsVecMissingSsrcs) { std::vector encodings = {{}}; // Creates RtxParameters with empty SSRC. auto result = ToCricketStreamParamsVec(encodings); ASSERT_TRUE(result.ok()); EXPECT_EQ(0u, result.value().size()); } // TODO(deadbeef): Update this test when we support multiple encodings. TEST(RtpParametersConversionTest, ToCricketStreamParamsVecMultipleEncodings) { std::vector encodings = {{}, {}}; auto result = ToCricketStreamParamsVec(encodings); EXPECT_EQ(RTCErrorType::UNSUPPORTED_PARAMETER, result.error().type()); } TEST(RtpParametersConversionTest, ToRtcpFeedback) { absl::optional result = ToRtcpFeedback({"ccm", "fir"}); EXPECT_EQ(RtcpFeedback(RtcpFeedbackType::CCM, RtcpFeedbackMessageType::FIR), *result); result = ToRtcpFeedback(cricket::FeedbackParam("goog-lntf")); EXPECT_EQ(RtcpFeedback(RtcpFeedbackType::LNTF), *result); result = ToRtcpFeedback(cricket::FeedbackParam("nack")); EXPECT_EQ(RtcpFeedback(RtcpFeedbackType::NACK, RtcpFeedbackMessageType::GENERIC_NACK), *result); result = ToRtcpFeedback({"nack", "pli"}); EXPECT_EQ(RtcpFeedback(RtcpFeedbackType::NACK, RtcpFeedbackMessageType::PLI), *result); result = ToRtcpFeedback(cricket::FeedbackParam("goog-remb")); EXPECT_EQ(RtcpFeedback(RtcpFeedbackType::REMB), *result); result = ToRtcpFeedback(cricket::FeedbackParam("transport-cc")); EXPECT_EQ(RtcpFeedback(RtcpFeedbackType::TRANSPORT_CC), *result); } TEST(RtpParametersConversionTest, ToRtcpFeedbackErrors) { // CCM with missing or invalid message type. absl::optional result = ToRtcpFeedback({"ccm", "pli"}); EXPECT_FALSE(result); result = ToRtcpFeedback(cricket::FeedbackParam("ccm")); EXPECT_FALSE(result); // LNTF with message type (should be left empty). result = ToRtcpFeedback({"goog-lntf", "pli"}); EXPECT_FALSE(result); // NACK with missing or invalid message type. result = ToRtcpFeedback({"nack", "fir"}); EXPECT_FALSE(result); // REMB with message type (should be left empty). result = ToRtcpFeedback({"goog-remb", "pli"}); EXPECT_FALSE(result); // TRANSPORT_CC with message type (should be left empty). result = ToRtcpFeedback({"transport-cc", "fir"}); EXPECT_FALSE(result); // Unknown message type. result = ToRtcpFeedback(cricket::FeedbackParam("foo")); EXPECT_FALSE(result); } TEST(RtpParametersConversionTest, ToAudioRtpCodecCapability) { cricket::AudioCodec cricket_codec; cricket_codec.name = "foo"; cricket_codec.id = 50; cricket_codec.clockrate = 22222; cricket_codec.channels = 4; cricket_codec.params["foo"] = "bar"; cricket_codec.feedback_params.Add(cricket::FeedbackParam("transport-cc")); RtpCodecCapability codec = ToRtpCodecCapability(cricket_codec); EXPECT_EQ("foo", codec.name); EXPECT_EQ(cricket::MEDIA_TYPE_AUDIO, codec.kind); EXPECT_EQ(50, codec.preferred_payload_type); EXPECT_EQ(22222, codec.clock_rate); EXPECT_EQ(4, codec.num_channels); ASSERT_EQ(1u, codec.parameters.size()); EXPECT_EQ("bar", codec.parameters["foo"]); EXPECT_EQ(1u, codec.rtcp_feedback.size()); EXPECT_EQ(RtcpFeedback(RtcpFeedbackType::TRANSPORT_CC), codec.rtcp_feedback[0]); } TEST(RtpParametersConversionTest, ToVideoRtpCodecCapability) { cricket::VideoCodec cricket_codec; cricket_codec.name = "VID"; cricket_codec.id = 101; cricket_codec.clockrate = 80000; cricket_codec.params["foo"] = "bar"; cricket_codec.params["ANOTHER"] = "param"; cricket_codec.feedback_params.Add(cricket::FeedbackParam("transport-cc")); cricket_codec.feedback_params.Add(cricket::FeedbackParam("goog-lntf")); cricket_codec.feedback_params.Add({"nack", "pli"}); RtpCodecCapability codec = ToRtpCodecCapability(cricket_codec); EXPECT_EQ("VID", codec.name); EXPECT_EQ(cricket::MEDIA_TYPE_VIDEO, codec.kind); EXPECT_EQ(101, codec.preferred_payload_type); EXPECT_EQ(80000, codec.clock_rate); ASSERT_EQ(2u, codec.parameters.size()); EXPECT_EQ("bar", codec.parameters["foo"]); EXPECT_EQ("param", codec.parameters["ANOTHER"]); EXPECT_EQ(3u, codec.rtcp_feedback.size()); EXPECT_EQ(RtcpFeedback(RtcpFeedbackType::TRANSPORT_CC), codec.rtcp_feedback[0]); EXPECT_EQ(RtcpFeedback(RtcpFeedbackType::LNTF), codec.rtcp_feedback[1]); EXPECT_EQ(RtcpFeedback(RtcpFeedbackType::NACK, RtcpFeedbackMessageType::PLI), codec.rtcp_feedback[2]); } TEST(RtpParametersConversionTest, ToRtpEncodingsWithEmptyStreamParamsVec) { cricket::StreamParamsVec streams; auto rtp_encodings = ToRtpEncodings(streams); ASSERT_EQ(0u, rtp_encodings.size()); } TEST(RtpParametersConversionTest, ToRtpEncodingsWithMultipleStreamParams) { cricket::StreamParamsVec streams; cricket::StreamParams stream1; stream1.ssrcs.push_back(1111u); cricket::StreamParams stream2; stream2.ssrcs.push_back(2222u); streams.push_back(stream1); streams.push_back(stream2); auto rtp_encodings = ToRtpEncodings(streams); ASSERT_EQ(2u, rtp_encodings.size()); EXPECT_EQ(1111u, rtp_encodings[0].ssrc); EXPECT_EQ(2222u, rtp_encodings[1].ssrc); } TEST(RtpParametersConversionTest, ToAudioRtpCodecParameters) { cricket::AudioCodec cricket_codec; cricket_codec.name = "foo"; cricket_codec.id = 50; cricket_codec.clockrate = 22222; cricket_codec.channels = 4; cricket_codec.params["foo"] = "bar"; cricket_codec.feedback_params.Add(cricket::FeedbackParam("transport-cc")); RtpCodecParameters codec = ToRtpCodecParameters(cricket_codec); EXPECT_EQ("foo", codec.name); EXPECT_EQ(cricket::MEDIA_TYPE_AUDIO, codec.kind); EXPECT_EQ(50, codec.payload_type); EXPECT_EQ(22222, codec.clock_rate); EXPECT_EQ(4, codec.num_channels); ASSERT_EQ(1u, codec.parameters.size()); EXPECT_EQ("bar", codec.parameters["foo"]); EXPECT_EQ(1u, codec.rtcp_feedback.size()); EXPECT_EQ(RtcpFeedback(RtcpFeedbackType::TRANSPORT_CC), codec.rtcp_feedback[0]); } TEST(RtpParametersConversionTest, ToVideoRtpCodecParameters) { cricket::VideoCodec cricket_codec; cricket_codec.name = "VID"; cricket_codec.id = 101; cricket_codec.clockrate = 80000; cricket_codec.params["foo"] = "bar"; cricket_codec.params["ANOTHER"] = "param"; cricket_codec.feedback_params.Add(cricket::FeedbackParam("transport-cc")); cricket_codec.feedback_params.Add(cricket::FeedbackParam("goog-lntf")); cricket_codec.feedback_params.Add({"nack", "pli"}); RtpCodecParameters codec = ToRtpCodecParameters(cricket_codec); EXPECT_EQ("VID", codec.name); EXPECT_EQ(cricket::MEDIA_TYPE_VIDEO, codec.kind); EXPECT_EQ(101, codec.payload_type); EXPECT_EQ(80000, codec.clock_rate); ASSERT_EQ(2u, codec.parameters.size()); EXPECT_EQ("bar", codec.parameters["foo"]); EXPECT_EQ("param", codec.parameters["ANOTHER"]); EXPECT_EQ(3u, codec.rtcp_feedback.size()); EXPECT_EQ(RtcpFeedback(RtcpFeedbackType::TRANSPORT_CC), codec.rtcp_feedback[0]); EXPECT_EQ(RtcpFeedback(RtcpFeedbackType::LNTF), codec.rtcp_feedback[1]); EXPECT_EQ(RtcpFeedback(RtcpFeedbackType::NACK, RtcpFeedbackMessageType::PLI), codec.rtcp_feedback[2]); } // An unknown feedback param should just be ignored. TEST(RtpParametersConversionTest, ToRtpCodecCapabilityUnknownFeedbackParam) { cricket::AudioCodec cricket_codec; cricket_codec.name = "foo"; cricket_codec.id = 50; cricket_codec.clockrate = 22222; cricket_codec.channels = 4; cricket_codec.params["foo"] = "bar"; cricket_codec.feedback_params.Add({"unknown", "param"}); cricket_codec.feedback_params.Add(cricket::FeedbackParam("transport-cc")); RtpCodecCapability codec = ToRtpCodecCapability(cricket_codec); ASSERT_EQ(1u, codec.rtcp_feedback.size()); EXPECT_EQ(RtcpFeedback(RtcpFeedbackType::TRANSPORT_CC), codec.rtcp_feedback[0]); } // Most of ToRtpCapabilities is tested by ToRtpCodecCapability, but we need to // test that the result of ToRtpCodecCapability ends up in the result, and that // the "fec" list is assembled correctly. TEST(RtpParametersConversionTest, ToRtpCapabilities) { cricket::VideoCodec vp8; vp8.name = "VP8"; vp8.id = 101; vp8.clockrate = 90000; cricket::VideoCodec red; red.name = "red"; red.id = 102; red.clockrate = 90000; cricket::VideoCodec ulpfec; ulpfec.name = "ulpfec"; ulpfec.id = 103; ulpfec.clockrate = 90000; cricket::VideoCodec flexfec; flexfec.name = "flexfec-03"; flexfec.id = 102; flexfec.clockrate = 90000; cricket::VideoCodec rtx; rtx.name = "rtx"; rtx.id = 104; rtx.params.insert({"apt", "101"}); cricket::VideoCodec rtx2; rtx2.name = "rtx"; rtx2.id = 105; rtx2.params.insert({"apt", "109"}); RtpCapabilities capabilities = ToRtpCapabilities( {vp8, ulpfec, rtx, rtx2}, {{"uri", 1}, {"uri2", 3}}); ASSERT_EQ(3u, capabilities.codecs.size()); EXPECT_EQ("VP8", capabilities.codecs[0].name); EXPECT_EQ("ulpfec", capabilities.codecs[1].name); EXPECT_EQ("rtx", capabilities.codecs[2].name); EXPECT_EQ(0u, capabilities.codecs[2].parameters.size()); ASSERT_EQ(2u, capabilities.header_extensions.size()); EXPECT_EQ("uri", capabilities.header_extensions[0].uri); EXPECT_EQ(1, capabilities.header_extensions[0].preferred_id); EXPECT_EQ("uri2", capabilities.header_extensions[1].uri); EXPECT_EQ(3, capabilities.header_extensions[1].preferred_id); EXPECT_EQ(0u, capabilities.fec.size()); capabilities = ToRtpCapabilities( {vp8, red, ulpfec, rtx}, cricket::RtpHeaderExtensions()); EXPECT_EQ(4u, capabilities.codecs.size()); EXPECT_THAT( capabilities.fec, UnorderedElementsAre(FecMechanism::RED, FecMechanism::RED_AND_ULPFEC)); capabilities = ToRtpCapabilities( {vp8, red, flexfec}, cricket::RtpHeaderExtensions()); EXPECT_EQ(3u, capabilities.codecs.size()); EXPECT_THAT(capabilities.fec, UnorderedElementsAre(FecMechanism::RED, FecMechanism::FLEXFEC)); } TEST(RtpParametersConversionTest, ToRtpParameters) { cricket::VideoCodec vp8; vp8.name = "VP8"; vp8.id = 101; vp8.clockrate = 90000; cricket::VideoCodec red; red.name = "red"; red.id = 102; red.clockrate = 90000; cricket::VideoCodec ulpfec; ulpfec.name = "ulpfec"; ulpfec.id = 103; ulpfec.clockrate = 90000; cricket::StreamParamsVec streams; cricket::StreamParams stream; stream.ssrcs.push_back(1234u); streams.push_back(stream); RtpParameters rtp_parameters = ToRtpParameters( {vp8, red, ulpfec}, {{"uri", 1}, {"uri2", 3}}, streams); ASSERT_EQ(3u, rtp_parameters.codecs.size()); EXPECT_EQ("VP8", rtp_parameters.codecs[0].name); EXPECT_EQ("red", rtp_parameters.codecs[1].name); EXPECT_EQ("ulpfec", rtp_parameters.codecs[2].name); ASSERT_EQ(2u, rtp_parameters.header_extensions.size()); EXPECT_EQ("uri", rtp_parameters.header_extensions[0].uri); EXPECT_EQ(1, rtp_parameters.header_extensions[0].id); EXPECT_EQ("uri2", rtp_parameters.header_extensions[1].uri); EXPECT_EQ(3, rtp_parameters.header_extensions[1].id); ASSERT_EQ(1u, rtp_parameters.encodings.size()); EXPECT_EQ(1234u, rtp_parameters.encodings[0].ssrc); } } // namespace webrtc