diff options
author | Jordan Bayles <jophba@chromium.org> | 2021-07-16 13:19:50 -0700 |
---|---|---|
committer | Openscreen LUCI CQ <openscreen-scoped@luci-project-accounts.iam.gserviceaccount.com> | 2021-07-16 21:22:59 +0000 |
commit | 4f20671877f4b0d515641cbb566c4c0fe4a2c947 (patch) | |
tree | 6f28d4c5753b4ee1d7dc446474bb0f17b1194d4c /cast | |
parent | d29ea545e02197c5166c0e0c76b3c181b3e3b370 (diff) | |
download | openscreen-4f20671877f4b0d515641cbb566c4c0fe4a2c947.tar.gz |
[Cast Streaming] Add codec parameter
This patch adds a new codec parameter to the OFFER/ANSWER exchange. This
field respects the format laid out in RFC 6381, and is a strictly
optional field that allows senders to provide more information about a
given codec configuration.
Bug: b/184429130
Change-Id: Ibd537f05e579b3bb2a488712b688edb407784841
Reviewed-on: https://chromium-review.googlesource.com/c/openscreen/+/2984452
Commit-Queue: Jordan Bayles <jophba@chromium.org>
Reviewed-by: mark a. foltz <mfoltz@chromium.org>
Diffstat (limited to 'cast')
-rw-r--r-- | cast/protocol/castv2/streaming_examples/offer.json | 21 | ||||
-rw-r--r-- | cast/protocol/castv2/streaming_schema.json | 1 | ||||
-rw-r--r-- | cast/streaming/capture_configs.h | 13 | ||||
-rw-r--r-- | cast/streaming/offer_messages.cc | 64 | ||||
-rw-r--r-- | cast/streaming/offer_messages.h | 30 | ||||
-rw-r--r-- | cast/streaming/offer_messages_unittest.cc | 74 | ||||
-rw-r--r-- | cast/streaming/receiver_session.cc | 16 | ||||
-rw-r--r-- | cast/streaming/receiver_session.h | 14 | ||||
-rw-r--r-- | cast/streaming/receiver_session_unittest.cc | 81 | ||||
-rw-r--r-- | cast/streaming/sender_session.cc | 6 | ||||
-rw-r--r-- | cast/streaming/sender_session_unittest.cc | 21 |
11 files changed, 306 insertions, 35 deletions
diff --git a/cast/protocol/castv2/streaming_examples/offer.json b/cast/protocol/castv2/streaming_examples/offer.json index 928c7344..b6162112 100644 --- a/cast/protocol/castv2/streaming_examples/offer.json +++ b/cast/protocol/castv2/streaming_examples/offer.json @@ -40,6 +40,25 @@ { "aesIvMask": "64A6AAC2821880145271BB15B0188821", "aesKey": "65386FD9BCC30BC7FB6A4DD1D3B0FA5E", + "codecName": "h264", + "codecParameter": "avc1.4D4028", + "index": 2, + "maxBitRate": 4000000, + "maxFrameRate": "25", + "receiverRtcpEventLog": false, + "renderMode": "video", + "resolutions": [{"height": 720, "width": 1280}], + "rtpExtensions": ["adaptive_playout_delay"], + "rtpPayloadType": 97, + "rtpProfile": "cast", + "ssrc": 748229, + "targetDelay": 400, + "timeBase": "1/90000", + "type": "video_source" + }, + { + "aesIvMask": "64A6AAC2821880145271BB15B0188821", + "aesKey": "65386FD9BCC30BC7FB6A4DD1D3B0FA5E", "codecName": "vp9", "index": 2, "maxBitRate": 5000000, @@ -75,6 +94,6 @@ } ] }, - "seqNum": 0, + "seqNum": 123, "type": "OFFER" } diff --git a/cast/protocol/castv2/streaming_schema.json b/cast/protocol/castv2/streaming_schema.json index 6a1a0db5..4c78d526 100644 --- a/cast/protocol/castv2/streaming_schema.json +++ b/cast/protocol/castv2/streaming_schema.json @@ -28,6 +28,7 @@ "index": {"type": "integer", "minimum": 0}, "type": {"type": "string", "enum": ["audio_source", "video_source"]}, "codecName": {"type": "string", "enum": ["aac", "opus", "h264", "vp8", "hevc", "vp9", "av1"]}, + "codecParameter": {"type": "string"}, "rtpProfile": {"type": "string", "enum": ["cast"]}, "rtpPayloadType": {"type": "integer", "minimum": 96, "maximum": 127}, "ssrc": {"$ref": "#/definitions/ssrc"}, diff --git a/cast/streaming/capture_configs.h b/cast/streaming/capture_configs.h index a5367e18..56b15898 100644 --- a/cast/streaming/capture_configs.h +++ b/cast/streaming/capture_configs.h @@ -35,6 +35,11 @@ struct AudioCaptureConfig { // Target playout delay in milliseconds. std::chrono::milliseconds target_playout_delay = kDefaultTargetPlayoutDelay; + + // The codec parameter for this configuration. Honors the format laid out + // in RFC 6381: https://datatracker.ietf.org/doc/html/rfc6381 + // NOTE: the "profiles" parameter is not supported in our implementation. + std::string codec_parameter; }; // A configuration set that can be used by the sender to capture video, as @@ -62,6 +67,14 @@ struct VideoCaptureConfig { // Target playout delay in milliseconds. std::chrono::milliseconds target_playout_delay = kDefaultTargetPlayoutDelay; + + // The codec parameter for this configuration. Honors the format laid out + // in RFC 6381: https://datatracker.ietf.org/doc/html/rfc6381. + // VP8 and VP9 codec parameter versions are defined here: + // https://developer.mozilla.org/en-US/docs/Web/Media/Formats/codecs_parameter#webm + // https://www.webmproject.org/vp9/mp4/#codecs-parameter-string + // NOTE: the "profiles" parameter is not supported in our implementation. + std::string codec_parameter; }; } // namespace cast diff --git a/cast/streaming/offer_messages.cc b/cast/streaming/offer_messages.cc index eeefcc5d..a162f09f 100644 --- a/cast/streaming/offer_messages.cc +++ b/cast/streaming/offer_messages.cc @@ -33,6 +33,45 @@ constexpr char kAudioSourceType[] = "audio_source"; constexpr char kVideoSourceType[] = "video_source"; constexpr char kStreamType[] = "type"; +bool CodecParameterIsValid(VideoCodec codec, + const std::string& codec_parameter) { + if (codec_parameter.empty()) { + return true; + } + switch (codec) { + case VideoCodec::kVp8: + return absl::StartsWith(codec_parameter, "vp08"); + case VideoCodec::kVp9: + return absl::StartsWith(codec_parameter, "vp09"); + case VideoCodec::kAv1: + return absl::StartsWith(codec_parameter, "av01"); + case VideoCodec::kHevc: + return absl::StartsWith(codec_parameter, "hev1"); + case VideoCodec::kH264: + return absl::StartsWith(codec_parameter, "avc1"); + case VideoCodec::kNotSpecified: + return false; + } + OSP_NOTREACHED(); +} + +bool CodecParameterIsValid(AudioCodec codec, + const std::string& codec_parameter) { + if (codec_parameter.empty()) { + return true; + } + switch (codec) { + case AudioCodec::kAac: + return absl::StartsWith(codec_parameter, "mp4a."); + + // Opus doesn't use codec parameters. + case AudioCodec::kOpus: // fallthrough + case AudioCodec::kNotSpecified: + return false; + } + OSP_NOTREACHED(); +} + EnumNameTable<CastMode, 2> kCastModeNames{ {{"mirroring", CastMode::kMirroring}, {"remoting", CastMode::kRemoting}}}; @@ -175,14 +214,10 @@ Error Stream::TryParse(const Json::Value& value, } } - if (!json::TryParseBool(value["receiverRtcpEventLog"], - &out->receiver_rtcp_event_log)) { - out->receiver_rtcp_event_log = false; - } - if (!json::TryParseString(value["receiverRtcpDscp"], - &out->receiver_rtcp_dscp)) { - out->receiver_rtcp_dscp = {}; - } + json::TryParseBool(value["receiverRtcpEventLog"], + &out->receiver_rtcp_event_log); + json::TryParseString(value["receiverRtcpDscp"], &out->receiver_rtcp_dscp); + json::TryParseString(value["codecParameter"], &out->codec_parameter); return Error::None(); } @@ -207,6 +242,7 @@ Json::Value Stream::ToJson() const { root["receiverRtcpEventLog"] = receiver_rtcp_event_log; root["receiverRtcpDscp"] = receiver_rtcp_dscp; root["timeBase"] = "1/" + std::to_string(rtp_timebase); + root["codecParameter"] = codec_parameter; return root; } @@ -235,6 +271,12 @@ Error AudioStream::TryParse(const Json::Value& value, AudioStream* out) { "Codec is not known, can't use stream"); } out->codec = codec.value(); + if (!CodecParameterIsValid(codec.value(), out->stream.codec_parameter)) { + return Error(Error::Code::kInvalidCodecParameter, + StringPrintf("Invalid audio codec parameter (%s for codec %s)", + out->stream.codec_parameter.c_str(), + CodecToString(codec.value()))); + } return Error::None(); } @@ -268,6 +310,12 @@ Error VideoStream::TryParse(const Json::Value& value, VideoStream* out) { "Codec is not known, can't use stream"); } out->codec = codec.value(); + if (!CodecParameterIsValid(codec.value(), out->stream.codec_parameter)) { + return Error(Error::Code::kInvalidCodecParameter, + StringPrintf("Invalid video codec parameter (%s for codec %s)", + out->stream.codec_parameter.c_str(), + CodecToString(codec.value()))); + } out->max_frame_rate = SimpleFraction{kDefaultMaxFrameRate, 1}; std::string raw_max_frame_rate; diff --git a/cast/streaming/offer_messages.h b/cast/streaming/offer_messages.h index c2be5bfa..765bda2a 100644 --- a/cast/streaming/offer_messages.h +++ b/cast/streaming/offer_messages.h @@ -65,9 +65,13 @@ struct Stream { // must be converted to a 16 digit byte array. std::array<uint8_t, 16> aes_key = {}; std::array<uint8_t, 16> aes_iv_mask = {}; - bool receiver_rtcp_event_log = {}; - std::string receiver_rtcp_dscp = {}; + bool receiver_rtcp_event_log = false; + std::string receiver_rtcp_dscp; int rtp_timebase = 0; + + // The codec parameter field honors the format laid out in RFC 6381: + // https://datatracker.ietf.org/doc/html/rfc6381. + std::string codec_parameter; }; struct AudioStream { @@ -75,8 +79,8 @@ struct AudioStream { Json::Value ToJson() const; bool IsValid() const; - Stream stream = {}; - AudioCodec codec; + Stream stream; + AudioCodec codec = AudioCodec::kNotSpecified; int bit_rate = 0; }; @@ -86,15 +90,15 @@ struct VideoStream { Json::Value ToJson() const; bool IsValid() const; - Stream stream = {}; - VideoCodec codec; + Stream stream; + VideoCodec codec = VideoCodec::kNotSpecified; SimpleFraction max_frame_rate; int max_bit_rate = 0; - std::string protection = {}; - std::string profile = {}; - std::string level = {}; - std::vector<Resolution> resolutions = {}; - std::string error_recovery_mode = {}; + std::string protection; + std::string profile; + std::string level; + std::vector<Resolution> resolutions; + std::string error_recovery_mode; }; struct Offer { @@ -105,8 +109,8 @@ struct Offer { bool IsValid() const; CastMode cast_mode = CastMode::kMirroring; - std::vector<AudioStream> audio_streams = {}; - std::vector<VideoStream> video_streams = {}; + std::vector<AudioStream> audio_streams; + std::vector<VideoStream> video_streams; }; } // namespace cast diff --git a/cast/streaming/offer_messages_unittest.cc b/cast/streaming/offer_messages_unittest.cc index 3bad54d0..62685e4d 100644 --- a/cast/streaming/offer_messages_unittest.cc +++ b/cast/streaming/offer_messages_unittest.cc @@ -463,6 +463,80 @@ TEST(OfferTest, ErrorOnMissingVideoStreamMandatoryField) { })"); } +TEST(OfferTest, ValidatesCodecParameterFormat) { + ExpectFailureOnParse(R"({ + "castMode": "mirroring", + "supportedStreams": [{ + "index": 2, + "type": "audio_source", + "codecName": "aac", + "codecParameter": "vp08.123.332", + "rtpProfile": "cast", + "rtpPayloadType": 96, + "ssrc": 19088743, + "bitRate": 124000, + "timeBase": "1/10000000", + "channels": 2, + "aesKey": "51027e4e2347cbcb49d57ef10177aebc", + "aesIvMask": "7f12a19be62a36c04ae4116caaeff6d1" + }] + })"); + + ExpectFailureOnParse(R"({ + "castMode": "mirroring", + "supportedStreams": [{ + "index": 2, + "type": "video_source", + "codecName": "vp8", + "codecParameter": "vp09.11.23", + "rtpProfile": "cast", + "rtpPayloadType": 100, + "ssrc": 19088743, + "timeBase": "1/48000", + "resolutions": [], + "maxBitRate": 10000, + "aesKey": "51027e4e2347cbcb49d57ef10177aebc" + }] + })"); + + const ErrorOr<Json::Value> audio_root = json::Parse(R"({ + "castMode": "mirroring", + "supportedStreams": [{ + "index": 2, + "type": "audio_source", + "codecName": "aac", + "codecParameter": "mp4a.12", + "rtpProfile": "cast", + "rtpPayloadType": 96, + "ssrc": 19088743, + "bitRate": 124000, + "timeBase": "1/10000000", + "channels": 2, + "aesKey": "51027e4e2347cbcb49d57ef10177aebc", + "aesIvMask": "7f12a19be62a36c04ae4116caaeff6d1" + }] + })"); + ASSERT_TRUE(audio_root.is_value()) << audio_root.error(); + + const ErrorOr<Json::Value> video_root = json::Parse(R"({ + "castMode": "mirroring", + "supportedStreams": [{ + "index": 2, + "type": "video_source", + "codecName": "vp9", + "codecParameter": "vp09.11.23", + "rtpProfile": "cast", + "rtpPayloadType": 100, + "ssrc": 19088743, + "timeBase": "1/48000", + "resolutions": [], + "maxBitRate": 10000, + "aesKey": "51027e4e2347cbcb49d57ef10177aebc" + }] + })"); + ASSERT_TRUE(video_root.is_value()) << video_root.error(); +} + TEST(OfferTest, CanParseValidButMinimalVideoOffer) { ErrorOr<Json::Value> root = json::Parse(R"({ "castMode": "mirroring", diff --git a/cast/streaming/receiver_session.cc b/cast/streaming/receiver_session.cc index 8082af8e..b55784b2 100644 --- a/cast/streaming/receiver_session.cc +++ b/cast/streaming/receiver_session.cc @@ -29,10 +29,14 @@ namespace { template <typename Stream, typename Codec> std::unique_ptr<Stream> SelectStream( const std::vector<Codec>& preferred_codecs, + ReceiverSession::Client* client, const std::vector<Stream>& offered_streams) { for (auto codec : preferred_codecs) { for (const Stream& offered_stream : offered_streams) { - if (offered_stream.codec == codec) { + if (offered_stream.codec == codec && + (offered_stream.stream.codec_parameter.empty() || + client->SupportsCodecParameter( + offered_stream.stream.codec_parameter))) { OSP_VLOG << "Selected " << CodecToString(codec) << " as codec for streaming"; return std::make_unique<Stream>(offered_stream); @@ -378,11 +382,11 @@ void ReceiverSession::SelectStreams(const Offer& offer, if (offer.cast_mode == CastMode::kMirroring) { if (!offer.audio_streams.empty() && !preferences_.audio_codecs.empty()) { properties->selected_audio = - SelectStream(preferences_.audio_codecs, offer.audio_streams); + SelectStream(preferences_.audio_codecs, client_, offer.audio_streams); } if (!offer.video_streams.empty() && !preferences_.video_codecs.empty()) { properties->selected_video = - SelectStream(preferences_.video_codecs, offer.video_streams); + SelectStream(preferences_.video_codecs, client_, offer.video_streams); } } else { OSP_DCHECK(offer.cast_mode == CastMode::kRemoting); @@ -459,7 +463,8 @@ ReceiverSession::ConfiguredReceivers ReceiverSession::SpawnReceivers( properties.selected_audio->stream.channels, properties.selected_audio->bit_rate, properties.selected_audio->stream.rtp_timebase, - properties.selected_audio->stream.target_delay}; + properties.selected_audio->stream.target_delay, + properties.selected_audio->stream.codec_parameter}; } VideoCaptureConfig video_config; @@ -471,7 +476,8 @@ ReceiverSession::ConfiguredReceivers ReceiverSession::SpawnReceivers( properties.selected_video->max_frame_rate, properties.selected_video->max_bit_rate, properties.selected_video->resolutions, - properties.selected_video->stream.target_delay}; + properties.selected_video->stream.target_delay, + properties.selected_video->stream.codec_parameter}; } return ConfiguredReceivers{ diff --git a/cast/streaming/receiver_session.h b/cast/streaming/receiver_session.h index d928da5e..caf271b6 100644 --- a/cast/streaming/receiver_session.h +++ b/cast/streaming/receiver_session.h @@ -115,6 +115,20 @@ class ReceiverSession final : public Environment::SocketSubscriber { // of reported here. virtual void OnError(const ReceiverSession* session, Error error) = 0; + // Called to verify whether a given codec parameter is supported by + // this client. If not overriden, this always assumes true. + // This method is used only for secondary matching, e.g. + // if you don't add VideoCodec::kHevc to the VideoCaptureConfig, then + // supporting codec parameter "hev1.1.6.L153.B0" does not matter. + // + // The codec parameter support callback is optional, however if provided + // then any offered streams that have a non-empty codec parameter field must + // match. If a stream does not have a codec parameter, this callback will + // not be called. + virtual bool SupportsCodecParameter(const std::string& parameter) { + return true; + } + protected: virtual ~Client(); }; diff --git a/cast/streaming/receiver_session_unittest.cc b/cast/streaming/receiver_session_unittest.cc index b8d64afa..098695a8 100644 --- a/cast/streaming/receiver_session_unittest.cc +++ b/cast/streaming/receiver_session_unittest.cc @@ -78,6 +78,26 @@ constexpr char kValidOfferMessage[] = R"({ ] }, { + "index": 31339, + "type": "video_source", + "codecName": "hevc", + "codecParameter": "hev1.1.6.L150.B0", + "rtpProfile": "cast", + "rtpPayloadType": 127, + "ssrc": 19088746, + "maxFrameRate": "120", + "timeBase": "1/90000", + "maxBitRate": 5000000, + "aesKey": "040d756791711fd3adb939066e6d8690", + "aesIvMask": "9ff0f022a959150e70a2d05a6c184aed", + "resolutions": [ + { + "width": 1920, + "height": 1080 + } + ] + }, + { "index": 1337, "type": "audio_source", "codecName": "opus", @@ -307,6 +327,10 @@ class FakeClient : public ReceiverSession::Client { (const ReceiverSession*, ReceiversDestroyingReason), (override)); MOCK_METHOD(void, OnError, (const ReceiverSession*, Error error), (override)); + MOCK_METHOD(bool, + SupportsCodecParameter, + (const std::string& parameter), + (override)); }; void ExpectIsErrorAnswerMessage(const ErrorOr<Json::Value>& message_or_error) { @@ -452,6 +476,63 @@ TEST_F(ReceiverSessionTest, CanNegotiateWithCustomCodecPreferences) { message_port_->ReceiveMessage(kValidOfferMessage); } +TEST_F(ReceiverSessionTest, RejectsStreamWithUnsupportedCodecParameter) { + ReceiverSession::Preferences preferences({VideoCodec::kHevc}, + {AudioCodec::kOpus}); + EXPECT_CALL(client_, SupportsCodecParameter(_)).WillRepeatedly(Return(false)); + ReceiverSession session(&client_, environment_.get(), message_port_.get(), + preferences); + InSequence s; + EXPECT_CALL(client_, OnNegotiated(&session, _)) + .WillOnce([](const ReceiverSession* session_, + ReceiverSession::ConfiguredReceivers cr) { + EXPECT_FALSE(cr.video_receiver); + }); + EXPECT_CALL(client_, OnReceiversDestroying( + &session, ReceiverSession::Client::kEndOfSession)); + message_port_->ReceiveMessage(kValidOfferMessage); +} + +TEST_F(ReceiverSessionTest, AcceptsStreamWithNoCodecParameter) { + ReceiverSession::Preferences preferences( + {VideoCodec::kHevc, VideoCodec::kVp9}, {AudioCodec::kOpus}); + EXPECT_CALL(client_, SupportsCodecParameter(_)).WillRepeatedly(Return(false)); + + ReceiverSession session(&client_, environment_.get(), message_port_.get(), + std::move(preferences)); + InSequence s; + EXPECT_CALL(client_, OnNegotiated(&session, _)) + .WillOnce([](const ReceiverSession* session_, + ReceiverSession::ConfiguredReceivers cr) { + EXPECT_TRUE(cr.video_receiver); + EXPECT_EQ(cr.video_config.codec, VideoCodec::kVp9); + }); + EXPECT_CALL(client_, OnReceiversDestroying( + &session, ReceiverSession::Client::kEndOfSession)); + message_port_->ReceiveMessage(kValidOfferMessage); +} + +TEST_F(ReceiverSessionTest, AcceptsStreamWithMatchingParameter) { + ReceiverSession::Preferences preferences({VideoCodec::kHevc}, + {AudioCodec::kOpus}); + EXPECT_CALL(client_, SupportsCodecParameter(_)) + .WillRepeatedly( + [](const std::string& param) { return param == "hev1.1.6.L150.B0"; }); + + ReceiverSession session(&client_, environment_.get(), message_port_.get(), + std::move(preferences)); + InSequence s; + EXPECT_CALL(client_, OnNegotiated(&session, _)) + .WillOnce([](const ReceiverSession* session_, + ReceiverSession::ConfiguredReceivers cr) { + EXPECT_TRUE(cr.video_receiver); + EXPECT_EQ(cr.video_config.codec, VideoCodec::kHevc); + }); + EXPECT_CALL(client_, OnReceiversDestroying( + &session, ReceiverSession::Client::kEndOfSession)); + message_port_->ReceiveMessage(kValidOfferMessage); +} + TEST_F(ReceiverSessionTest, CanNegotiateWithLimits) { std::vector<ReceiverSession::AudioLimits> audio_limits = { {false, AudioCodec::kOpus, 48001, 2, 32001, 32002, milliseconds(3001)}}; diff --git a/cast/streaming/sender_session.cc b/cast/streaming/sender_session.cc index f59f0d70..cae6f3e4 100644 --- a/cast/streaming/sender_session.cc +++ b/cast/streaming/sender_session.cc @@ -44,7 +44,8 @@ AudioStream CreateStream(int index, GenerateRandomBytes16(), false /* receiver_rtcp_event_log */, {} /* receiver_rtcp_dscp */, - config.sample_rate}, + config.sample_rate, + config.codec_parameter}, config.codec, std::max(config.bit_rate, kDefaultAudioMinBitRate)}; } @@ -64,7 +65,8 @@ VideoStream CreateStream(int index, GenerateRandomBytes16(), false /* receiver_rtcp_event_log */, {} /* receiver_rtcp_dscp */, - kRtpVideoTimebase}, + kRtpVideoTimebase, + config.codec_parameter}, config.codec, config.max_frame_rate, (config.max_bit_rate >= kDefaultVideoMinBitRate) diff --git a/cast/streaming/sender_session_unittest.cc b/cast/streaming/sender_session_unittest.cc index bfe4cfab..227e68e7 100644 --- a/cast/streaming/sender_session_unittest.cc +++ b/cast/streaming/sender_session_unittest.cc @@ -111,15 +111,20 @@ const AudioCaptureConfig kAudioCaptureConfigInvalidChannels{ }; const AudioCaptureConfig kAudioCaptureConfigValid{ - AudioCodec::kOpus, 5 /* channels */, 32000 /* bit_rate */, - 44000 /* sample_rate */ -}; + AudioCodec::kAac, + 5 /* channels */, + 32000 /* bit_rate */, + 44000 /* sample_rate */, + std::chrono::milliseconds(300), + "mp4a.40.5"}; const VideoCaptureConfig kVideoCaptureConfigMissingResolutions{ VideoCodec::kHevc, {60, 1}, 300000 /* max_bit_rate */, - std::vector<Resolution>{}}; + std::vector<Resolution>{}, + std::chrono::milliseconds(500), + "hev1.1.6.L150.B0"}; const VideoCaptureConfig kVideoCaptureConfigInvalid{ VideoCodec::kHevc, @@ -131,7 +136,9 @@ const VideoCaptureConfig kVideoCaptureConfigValid{ VideoCodec::kHevc, {60, 1}, 300000 /* max_bit_rate */, - std::vector<Resolution>{Resolution{1280, 720}, Resolution{1920, 1080}}}; + std::vector<Resolution>{Resolution{1280, 720}, Resolution{1920, 1080}}, + std::chrono::milliseconds(250), + "hev1.1.6.L150.B0"}; const VideoCaptureConfig kVideoCaptureConfigValidSimplest{ VideoCodec::kHevc, @@ -358,13 +365,14 @@ TEST_F(SenderSessionTest, SendsOfferMessage) { EXPECT_EQ(2u, streams.size()); const Json::Value& audio_stream = streams[0]; - EXPECT_EQ("opus", audio_stream["codecName"].asString()); + EXPECT_EQ("aac", audio_stream["codecName"].asString()); EXPECT_EQ(0, audio_stream["index"].asInt()); EXPECT_EQ(32u, audio_stream["aesKey"].asString().length()); EXPECT_EQ(32u, audio_stream["aesIvMask"].asString().length()); EXPECT_EQ(5, audio_stream["channels"].asInt()); EXPECT_LT(0u, audio_stream["ssrc"].asUInt()); EXPECT_EQ(127, audio_stream["rtpPayloadType"].asInt()); + EXPECT_EQ("mp4a.40.5", audio_stream["codecParameter"].asString()); const Json::Value& video_stream = streams[1]; EXPECT_EQ("hevc", video_stream["codecName"].asString()); @@ -374,6 +382,7 @@ TEST_F(SenderSessionTest, SendsOfferMessage) { EXPECT_EQ(1, video_stream["channels"].asInt()); EXPECT_LT(0u, video_stream["ssrc"].asUInt()); EXPECT_EQ(96, video_stream["rtpPayloadType"].asInt()); + EXPECT_EQ("hev1.1.6.L150.B0", video_stream["codecParameter"].asString()); } TEST_F(SenderSessionTest, HandlesValidAnswer) { |