aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJordan Bayles <jophba@chromium.org>2021-07-16 13:19:50 -0700
committerOpenscreen LUCI CQ <openscreen-scoped@luci-project-accounts.iam.gserviceaccount.com>2021-07-16 21:22:59 +0000
commit4f20671877f4b0d515641cbb566c4c0fe4a2c947 (patch)
tree6f28d4c5753b4ee1d7dc446474bb0f17b1194d4c
parentd29ea545e02197c5166c0e0c76b3c181b3e3b370 (diff)
downloadopenscreen-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>
-rw-r--r--cast/protocol/castv2/streaming_examples/offer.json21
-rw-r--r--cast/protocol/castv2/streaming_schema.json1
-rw-r--r--cast/streaming/capture_configs.h13
-rw-r--r--cast/streaming/offer_messages.cc64
-rw-r--r--cast/streaming/offer_messages.h30
-rw-r--r--cast/streaming/offer_messages_unittest.cc74
-rw-r--r--cast/streaming/receiver_session.cc16
-rw-r--r--cast/streaming/receiver_session.h14
-rw-r--r--cast/streaming/receiver_session_unittest.cc81
-rw-r--r--cast/streaming/sender_session.cc6
-rw-r--r--cast/streaming/sender_session_unittest.cc21
-rw-r--r--platform/base/error.cc2
-rw-r--r--platform/base/error.h1
13 files changed, 309 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) {
diff --git a/platform/base/error.cc b/platform/base/error.cc
index fa891335..a9a146e3 100644
--- a/platform/base/error.cc
+++ b/platform/base/error.cc
@@ -254,6 +254,8 @@ std::ostream& operator<<(std::ostream& os, const Error::Code& code) {
return os << "ProcessReceivedRecordFailure";
case Error::Code::kUnknownCodec:
return os << "UnknownCodec";
+ case Error::Code::kInvalidCodecParameter:
+ return os << "InvalidCodecParameter";
case Error::Code::kSocketFailure:
return os << "SocketFailure";
case Error::Code::kUnencryptedOffer:
diff --git a/platform/base/error.h b/platform/base/error.h
index 69a8a89e..2f9216f2 100644
--- a/platform/base/error.h
+++ b/platform/base/error.h
@@ -186,6 +186,7 @@ class Error {
// Cast streaming errors
kTypeError,
kUnknownCodec,
+ kInvalidCodecParameter,
kSocketFailure,
kUnencryptedOffer,
kRemotingNotSupported,