diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2021-09-01 11:08:40 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2021-10-01 12:16:21 +0000 |
commit | 03c549e0392f92c02536d3f86d5e1d8dfa3435ac (patch) | |
tree | fe49d170a929b34ba82cd10db1a0bd8e3760fa4b /chromium/third_party/openscreen | |
parent | 5d013f5804a0d91fcf6c626b2d6fb6eca5c845b0 (diff) | |
download | qtwebengine-chromium-03c549e0392f92c02536d3f86d5e1d8dfa3435ac.tar.gz |
BASELINE: Update Chromium to 91.0.4472.160
Change-Id: I0def1f08a2412aeed79a9ab95dd50eb5c3f65f31
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/third_party/openscreen')
77 files changed, 1588 insertions, 845 deletions
diff --git a/chromium/third_party/openscreen/src/DEPS b/chromium/third_party/openscreen/src/DEPS index 7fbb8aeab51..9c0499ea839 100644 --- a/chromium/third_party/openscreen/src/DEPS +++ b/chromium/third_party/openscreen/src/DEPS @@ -92,7 +92,7 @@ deps = { # python ./src/util/generate_build_files.py gn 'third_party/boringssl/src': { 'url' : Var('boringssl_git') + '/boringssl.git' + - '@' + '78987bb7bb4764ca3a8b08b0a6f7bd14b53c3e4f', + '@' + 'f6bd54efbcafcf4625ce99b5f702dc4850b0ca50', 'condition': 'not build_with_chromium', }, diff --git a/chromium/third_party/openscreen/src/cast/cast_core/api/bindings/api_bindings.proto b/chromium/third_party/openscreen/src/cast/cast_core/api/bindings/api_bindings.proto index 23e734dea3e..78da4236c7f 100644 --- a/chromium/third_party/openscreen/src/cast/cast_core/api/bindings/api_bindings.proto +++ b/chromium/third_party/openscreen/src/cast/cast_core/api/bindings/api_bindings.proto @@ -1,3 +1,8 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// **** DO NOT EDIT - this .proto was automatically generated. **** syntax = "proto3"; package cast.bindings; @@ -11,6 +16,8 @@ message ApiBinding { string before_load_script = 1; } +message GetAllRequest {} + message GetAllResponse { repeated ApiBinding bindings = 1; } @@ -19,3 +26,5 @@ message ConnectRequest { string port_name = 1; cast.web.MessagePortDescriptor port = 2; } + +message ConnectResponse {} diff --git a/chromium/third_party/openscreen/src/cast/cast_core/api/common/application_config.proto b/chromium/third_party/openscreen/src/cast/cast_core/api/common/application_config.proto index 6104e96e7b3..cb426824c83 100644 --- a/chromium/third_party/openscreen/src/cast/cast_core/api/common/application_config.proto +++ b/chromium/third_party/openscreen/src/cast/cast_core/api/common/application_config.proto @@ -1,3 +1,8 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// **** DO NOT EDIT - this .proto was automatically generated. **** syntax = "proto3"; package cast.common; diff --git a/chromium/third_party/openscreen/src/cast/cast_core/api/common/runtime_metadata.proto b/chromium/third_party/openscreen/src/cast/cast_core/api/common/runtime_metadata.proto index 00079f65618..38693389a2a 100644 --- a/chromium/third_party/openscreen/src/cast/cast_core/api/common/runtime_metadata.proto +++ b/chromium/third_party/openscreen/src/cast/cast_core/api/common/runtime_metadata.proto @@ -1,3 +1,8 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// **** DO NOT EDIT - this .proto was automatically generated. **** syntax = "proto3"; package cast.common; @@ -51,9 +56,6 @@ message RuntimeCapabilities { ApplicationCapabilities native_application_capabilities = 2; } - // Flags if heartbeat is supported. - bool heartbeat_supported = 3; - // Flags if metrics recording is supported. bool metrics_recorder_supported = 4; } diff --git a/chromium/third_party/openscreen/src/cast/cast_core/api/common/service_info.proto b/chromium/third_party/openscreen/src/cast/cast_core/api/common/service_info.proto index 012414e889a..e8dc7dd1bbb 100644 --- a/chromium/third_party/openscreen/src/cast/cast_core/api/common/service_info.proto +++ b/chromium/third_party/openscreen/src/cast/cast_core/api/common/service_info.proto @@ -1,3 +1,8 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// **** DO NOT EDIT - this .proto was automatically generated. **** syntax = "proto3"; package cast.common; diff --git a/chromium/third_party/openscreen/src/cast/cast_core/api/core/cast_core_service.proto b/chromium/third_party/openscreen/src/cast/cast_core/api/core/cast_core_service.proto index 5a9e8271e5f..ffd564d43a8 100644 --- a/chromium/third_party/openscreen/src/cast/cast_core/api/core/cast_core_service.proto +++ b/chromium/third_party/openscreen/src/cast/cast_core/api/core/cast_core_service.proto @@ -1,10 +1,13 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// **** DO NOT EDIT - this .proto was automatically generated. **** syntax = "proto3"; package cast.core; -import "google/protobuf/empty.proto"; import "cast/cast_core/api/common/runtime_metadata.proto"; -import "cast/cast_core/api/common/service_info.proto"; option optimize_for = LITE_RUNTIME; @@ -16,42 +19,24 @@ service CastCoreService { // Unregisters a Cast Runtime. Usually called by platform. rpc UnregisterRuntime(UnregisterRuntimeRequest) returns (UnregisterRuntimeResponse); - - // Called by the Runtime when it starts up. - rpc RuntimeStarted(RuntimeStartedNotification) - returns (google.protobuf.Empty); - - // Called when the runtime is shutdown. May be called for an active Cast - // session. - rpc RuntimeStopped(RuntimeStoppedNotification) - returns (google.protobuf.Empty); } message RegisterRuntimeRequest { - // Platform-generated runtime ID associated with this runtime. Uniqueness is - // guaranteed by the CastCore service. - string runtime_id = 1; + // DEPRECATED. + string runtime_id = 1 [deprecated = true]; // Metadata about the runtime. cast.common.RuntimeMetadata runtime_metadata = 2; } -message RegisterRuntimeResponse {} - -message UnregisterRuntimeRequest { - // Runtime ID. +message RegisterRuntimeResponse { + // A randomly generated runtime ID. Cast Core will use this ID to reference a + // particular Runtime. string runtime_id = 1; } -message UnregisterRuntimeResponse {} - -message RuntimeStartedNotification { +message UnregisterRuntimeRequest { // Runtime ID. string runtime_id = 1; - // Runtime service info. - cast.common.ServiceInfo runtime_service_info = 2; } -message RuntimeStoppedNotification { - // Runtime ID. - string runtime_id = 1; -} +message UnregisterRuntimeResponse {} diff --git a/chromium/third_party/openscreen/src/cast/cast_core/api/metrics/metrics_recorder.proto b/chromium/third_party/openscreen/src/cast/cast_core/api/metrics/metrics_recorder.proto index 30227d3b566..c6654abe5fa 100644 --- a/chromium/third_party/openscreen/src/cast/cast_core/api/metrics/metrics_recorder.proto +++ b/chromium/third_party/openscreen/src/cast/cast_core/api/metrics/metrics_recorder.proto @@ -1,20 +1,25 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// **** DO NOT EDIT - this .proto was automatically generated. **** syntax = "proto3"; package cast.metrics; -import "google/protobuf/empty.proto"; - option optimize_for = LITE_RUNTIME; service MetricsRecorderService { // Record a set of|Event| - rpc Record(RecordRequest) returns (google.protobuf.Empty); + rpc Record(RecordRequest) returns (RecordResponse); } message RecordRequest { repeated Event event = 1; } +message RecordResponse {} + // This repliciates the Fuchsia approach to Cast metrics; for documentation on // event structure, refer to // fuchsia.googlesource.com/fuchsia/+/master/sdk/fidl/fuchsia.legacymetrics/event.fidl @@ -28,7 +33,7 @@ message Event { message UserActionEvent { string name = 1; - optional int64 time = 2; + int64 time = 2; } message Histogram { @@ -45,5 +50,5 @@ message HistogramBucket { message ImplementationDefinedEvent { bytes data = 1; - optional string name = 2; + string name = 2; } diff --git a/chromium/third_party/openscreen/src/cast/cast_core/api/platform/platform_service.proto b/chromium/third_party/openscreen/src/cast/cast_core/api/platform/platform_service.proto index 858339bf707..9369ab93cad 100644 --- a/chromium/third_party/openscreen/src/cast/cast_core/api/platform/platform_service.proto +++ b/chromium/third_party/openscreen/src/cast/cast_core/api/platform/platform_service.proto @@ -1,7 +1,14 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// **** DO NOT EDIT - this .proto was automatically generated. **** syntax = "proto3"; package cast.platform; +import "cast/cast_core/api/common/service_info.proto"; + option optimize_for = LITE_RUNTIME; // Platform service. Implemented and hosted by Platform. @@ -18,7 +25,10 @@ service PlatformService { } message StartRuntimeRequest { + // Cast Runtime ID assigned in CastCoreService.RegisterRuntime. string runtime_id = 1; + // gRPC endpoint Cast Runtime must run on. + cast.common.ServiceInfo runtime_service_info = 2; } message StartRuntimeResponse {} diff --git a/chromium/third_party/openscreen/src/cast/cast_core/api/runtime/cast_audio_decoder_service.proto b/chromium/third_party/openscreen/src/cast/cast_core/api/runtime/cast_audio_channel_service.proto index a051edbaa2f..900e9c3b74e 100644 --- a/chromium/third_party/openscreen/src/cast/cast_core/api/runtime/cast_audio_decoder_service.proto +++ b/chromium/third_party/openscreen/src/cast/cast_core/api/runtime/cast_audio_channel_service.proto @@ -1,12 +1,183 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// **** DO NOT EDIT - this .proto was automatically generated. **** syntax = "proto3"; package cast.media; import "google/protobuf/duration.proto"; -import "google/protobuf/empty.proto"; option optimize_for = LITE_RUNTIME; +// Cast audio service hosted by Cast Core. +// +// It defines a state machine with the following states: +// - Uninitialized +// - Playing +// - Stopped +// - Paused +// +// Note that the received ordering between different RPC calls is not +// guaranteed to match the sent order. +service CastAudioChannelService { + // Initializes the service and places the pipeline into the 'Stopped' state. + // This must be the first call received by the server, and no other calls + // may be sent prior to receiving this call's response. + rpc Initialize(InitializeRequest) returns (InitializeResponse); + + // Returns the minimum buffering delay (min_delay) required by Cast. This is + // a constant value and only needs to be queried once for each service. + // During a StartRequest or ResumeRequest, the system timestamp must be + // greater than this delay and the current time in order for the buffer to be + // successfully rendered on remote devices. + rpc GetMinimumBufferDelay(GetMinimumBufferDelayRequest) + returns (GetMinimumBufferDelayResponse); + + // Update the pipeline state. + // + // StartRequest: + // Places pipeline into 'Playing' state. Playback will start at the + // specified buffer and system timestamp. + // + // May only be called in the 'Stopped' state, and following this call the + // state machine will be in the 'Playing' state. + // + // StopRequest + // Stops media playback and drops all pushed buffers which have not yet been + // played. + // + // May only be called in the 'Playing' or 'Paused' states, and following + // this call the state machine will be in the 'Stopped' state. + // + // PauseRequest + // Pauses media playback. + // + // May only be called in the 'Playing' state, and following this call the + // state machine will be in the 'Paused' state. + // + // ResumeRequest + // Resumes media playback at the specified buffer and system timestamp. + // + // May only be called in the 'Paused' state, and following this call the + // state machine will be in the 'Playing'' state. + // + // TimestampUpdateRequest + // Sends a timestamp update for a specified buffer for audio + // synchronization. This should be called when operating in + // CAST_AUDIO_DECODER_MODE_MULTIROOM_ONLY when the runtime has detected a + // discrepancy in the system clock or pipeline delay from the original + // playback schedule. See example below: + // + // Assume all buffers have duration of 100us. + // + // StartRequest(id=1, system_timestamp=0); + // -> Cast expects id=1 to play at 0, id=2 at 100us, id=3 at 200 us... + // + // TimestampUpdateRequest(id=4, system_timestamp=405us); + // -> Cast expects id=4 to play at 405, id=5 at 505us, id=6 at 605 us... + // + // May be called from any state. + // + // A state transition may only occur after a successful PushBuffer() + // call has been made with a valid configuration. + rpc StateChange(StateChangeRequest) returns (StateChangeResponse); + + // Sets the volume multiplier for this audio stream. + // The multiplier is in the range [0.0, 1.0]. If not called, a default + // multiplier of 1.0 is assumed. + // + // May be called in any state, and following this call the state machine + // will be in the same state. + rpc SetVolume(SetVolumeRequest) returns (SetVolumeResponse); + + // Sets the playback rate for this audio stream. + // + // May be called in any state, and following this call the state machine + // will be in the same state. + rpc SetPlaybackRate(SetPlaybackRateRequest) returns (SetPlaybackRateResponse); + + // Sends decoded bits and responses to the audio service. The client must + // wait for a response from the server before sending another + // PushBufferRequest. + // + // May only be called in the 'Playing' or 'Paused' states, and following + // this call the state machine will remain the same state. + // + rpc PushBuffer(PushBufferRequest) returns (PushBufferResponse); + + // Returns the current media time that has been rendered. + rpc GetMediaTime(GetMediaTimeRequest) returns (GetMediaTimeResponse); +} + +message InitializeRequest { + // Cast session ID. + string cast_session_id = 1; + + // Configures how the server should operate. + CastAudioDecoderMode mode = 2; +} + +message InitializeResponse {} + +message GetMinimumBufferDelayRequest {} + +message GetMinimumBufferDelayResponse { + // The minimum buffering delay in microseconds. + int64 delay_micros = 1; +} + +message StateChangeRequest { + oneof request { + StartRequest start = 1; + StopRequest stop = 2; + PauseRequest pause = 3; + ResumeRequest resume = 4; + TimestampUpdateRequest timestamp_update = 5; + } +} + +message StateChangeResponse { + // Pipeline state after state change. + PipelineState state = 1; +} + +message SetVolumeRequest { + // The multiplier is in the range [0.0, 1.0]. + float multiplier = 1; +} + +message SetVolumeResponse {} + +message SetPlaybackRateRequest { + // Playback rate greater than 0. + double rate = 1; +} + +message SetPlaybackRateResponse {} + +message PushBufferRequest { + AudioDecoderBuffer buffer = 1; + + // Audio configuration for this buffer and all subsequent buffers. This + // field must be populated for the first request or if there is an audio + // configuration change. + AudioConfiguration audio_config = 2; +} + +message PushBufferResponse { + // The total number of decoded bytes. + int64 decoded_bytes = 1; +} + +message GetMediaTimeRequest {} + +message GetMediaTimeResponse { + // The current media time that has been rendered. + MediaTime media_time = 1; +} + enum PipelineState { PIPELINE_STATE_UNINITIALIZED = 0; PIPELINE_STATE_STOPPED = 1; @@ -137,19 +308,6 @@ message TimestampInfo { int64 buffer_id = 2; } -message InitializeRequest { - // Cast session ID. - string cast_session_id = 1; - - // Configures how the server should operate. - CastAudioDecoderMode mode = 2; -} - -message GetMinimumBufferingDelayResponse { - // The minimum buffering delay in microseconds. - int64 delay_micros = 1; -} - message StartRequest { // The start presentation timestamp in microseconds. int64 pts_micros = 1; @@ -174,143 +332,3 @@ message ResumeRequest { message TimestampUpdateRequest { TimestampInfo timestamp_info = 1; } - -message StateChangeRequest { - oneof request { - StartRequest start = 1; - StopRequest stop = 2; - PauseRequest pause = 3; - ResumeRequest resume = 4; - TimestampUpdateRequest timestamp_update = 5; - } -} - -message StateChangeResponse { - // Pipeline state after state change. - PipelineState state = 1; -} - -message PushBufferRequest { - AudioDecoderBuffer buffer = 1; - - // Audio configuration for this buffer and all subsequent buffers. This - // field must be populated for the first request or if there is an audio - // configuration change. - AudioConfiguration audio_config = 2; -} - -message PushBufferResponse { - // The total number of decoded bytes. - int64 decoded_bytes = 1; -} - -message SetVolumeRequest { - // The multiplier is in the range [0.0, 1.0]. - float multiplier = 1; -} -message SetPlaybackRateRequest { - // Playback rate greater than 0. - double rate = 1; -} - -message GetMediaTimeResponse { - // The current media time that has been rendered. - MediaTime media_time = 1; -} - -// Cast audio service hosted by Cast Core. -// -// It defines a state machine with the following states: -// - Uninitialized -// - Playing -// - Stopped -// - Paused -// -// Note that the received ordering between different RPC calls is not -// guaranteed to match the sent order. -service CastRuntimeAudioChannel { - // Initializes the service and places the pipeline into the 'Stopped' state. - // This must be the first call received by the server, and no other calls - // may be sent prior to receiving this call's response. - rpc Initialize(InitializeRequest) returns (google.protobuf.Empty); - - // Returns the minimum buffering delay (min_delay) required by Cast. This is - // a constant value and only needs to be queried once for each service. - // During a StartRequest or ResumeRequest, the system timestamp must be - // greater than this delay and the current time in order for the buffer to be - // successfully rendered on remote devices. - rpc GetMinimumBufferDelay(google.protobuf.Empty) - returns (GetMinimumBufferingDelayResponse); - - // Update the pipeline state. - // - // StartRequest: - // Places pipeline into 'Playing' state. Playback will start at the - // specified buffer and system timestamp. - // - // May only be called in the 'Stopped' state, and following this call the - // state machine will be in the 'Playing' state. - // - // StopRequest - // Stops media playback and drops all pushed buffers which have not yet been - // played. - // - // May only be called in the 'Playing' or 'Paused' states, and following - // this call the state machine will be in the 'Stopped' state. - // - // PauseRequest - // Pauses media playback. - // - // May only be called in the 'Playing' state, and following this call the - // state machine will be in the 'Paused' state. - // - // ResumeRequest - // Resumes media playback at the specified buffer and system timestamp. - // - // May only be called in the 'Paused' state, and following this call the - // state machine will be in the 'Playing'' state. - // - // TimestampUpdateRequest - // Sends a timestamp update for a specified buffer for audio - // synchronization. This should be called when operating in - // CAST_AUDIO_DECODER_MODE_MULTIROOM_ONLY when the runtime has detected a - // discrepancy in the system clock or pipeline delay from the original - // playback schedule. See example below: - // - // Assume all buffers have duration of 100us. - // - // StartRequest(id=1, system_timestamp=0); - // -> Cast expects id=1 to play at 0, id=2 at 100us, id=3 at 200 us... - // - // TimestampUpdateRequest(id=4, system_timestamp=405us); - // -> Cast expects id=4 to play at 405, id=5 at 505us, id=6 at 605 us... - // - // May be called from any state. - // - // A state transition may only occur after a successful PushBuffer() - // call has been made with a valid configuration. - rpc StateChange(StateChangeRequest) returns (StateChangeResponse); - - // Sets the volume multiplier for this audio stream. - // The multiplier is in the range [0.0, 1.0]. If not called, a default - // multiplier of 1.0 is assumed. - // - // May be called in any state, and following this call the state machine - // will be in the same state. - rpc SetVolume(SetVolumeRequest) returns (google.protobuf.Empty); - - // Sets the playback rate for this audio stream. - // - // May be called in any state, and following this call the state machine - // will be in the same state. - rpc SetPlayback(SetPlaybackRateRequest) returns (google.protobuf.Empty); - - // Streams decoded bits and responses to the audio service. - // - // May only be called in the 'Playing' or 'Paused' states, and following - // this call the state machine will remain the same state. - rpc PushBuffer(stream PushBufferRequest) returns (PushBufferResponse); - - // Returns the current media time that has been rendered. - rpc GetMediaTime(google.protobuf.Empty) returns (GetMediaTimeResponse); -} diff --git a/chromium/third_party/openscreen/src/cast/cast_core/api/runtime/runtime_service.proto b/chromium/third_party/openscreen/src/cast/cast_core/api/runtime/runtime_service.proto index cc30fdaf81b..b3010c6d5e8 100644 --- a/chromium/third_party/openscreen/src/cast/cast_core/api/runtime/runtime_service.proto +++ b/chromium/third_party/openscreen/src/cast/cast_core/api/runtime/runtime_service.proto @@ -1,9 +1,13 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// **** DO NOT EDIT - this .proto was automatically generated. **** syntax = "proto3"; package cast.runtime; import "google/protobuf/duration.proto"; -import "google/protobuf/empty.proto"; import "cast/cast_core/api/common/service_info.proto"; option optimize_for = LITE_RUNTIME; @@ -13,8 +17,8 @@ option optimize_for = LITE_RUNTIME; // This service is called by CastCore after Runtime starts up. service RuntimeService { // Launches a Cast application. The application must connect to the - // CoreApplicationService based on cast_protocol and - // core_application_endpoint, and provide its endpoint. + // CoreApplicationService via core_application_service_info and start its + // RuntimeApplicationService on runtime_applicationn_service_info. rpc LaunchApplication(LaunchApplicationRequest) returns (LaunchApplicationResponse); @@ -33,16 +37,11 @@ service RuntimeService { // Provides information need by the runtime to start recording metrics via // the core. rpc StartMetricsRecorder(StartMetricsRecorderRequest) - returns (google.protobuf.Empty); + returns (StartMetricsRecorderResponse); // Stops the metrics recorder, which may also attempt to flush. - rpc StopMetricsRecorder(google.protobuf.Empty) - returns (google.protobuf.Empty); -} - -message StartMetricsRecorderRequest { - // Metrics service info. - cast.common.ServiceInfo metrics_recorder_service_info = 1; + rpc StopMetricsRecorder(StopMetricsRecorderRequest) + returns (StopMetricsRecorderResponse); } message LaunchApplicationRequest { @@ -51,6 +50,9 @@ message LaunchApplicationRequest { // Cast session id used to setup a connection and pull the config from core // application service. string cast_session_id = 2; + // RuntimeApplication service info. The endpoint is generated by Cast Core and + // must be used by the Runtime to bind the RuntimeApplication service. + cast.common.ServiceInfo runtime_application_service_info = 3; } // Returned by the runtime in response to a launch application request. @@ -77,3 +79,14 @@ message HeartbeatRequest { } message HeartbeatResponse {} + +message StartMetricsRecorderRequest { + // Metrics service info. + cast.common.ServiceInfo metrics_recorder_service_info = 1; +} + +message StartMetricsRecorderResponse {} + +message StopMetricsRecorderRequest {} + +message StopMetricsRecorderResponse {} diff --git a/chromium/third_party/openscreen/src/cast/cast_core/api/v2/cast_message.proto b/chromium/third_party/openscreen/src/cast/cast_core/api/v2/cast_message.proto index c429cf95454..62e7b1047e3 100644 --- a/chromium/third_party/openscreen/src/cast/cast_core/api/v2/cast_message.proto +++ b/chromium/third_party/openscreen/src/cast/cast_core/api/v2/cast_message.proto @@ -1,3 +1,8 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// **** DO NOT EDIT - this .proto was automatically generated. **** syntax = "proto3"; package cast.v2; @@ -5,7 +10,7 @@ package cast.v2; option optimize_for = LITE_RUNTIME; // Cast V2 request definition. -message CastMessage { +message CastMessageRequest { // Cast sender ID; distinct from virtual connection source ID. string sender_id = 1; // Cast namespace. diff --git a/chromium/third_party/openscreen/src/cast/cast_core/api/v2/core_application_service.proto b/chromium/third_party/openscreen/src/cast/cast_core/api/v2/core_application_service.proto index 86b729ab322..de4f2b02ec5 100644 --- a/chromium/third_party/openscreen/src/cast/cast_core/api/v2/core_application_service.proto +++ b/chromium/third_party/openscreen/src/cast/cast_core/api/v2/core_application_service.proto @@ -1,8 +1,12 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// **** DO NOT EDIT - this .proto was automatically generated. **** syntax = "proto3"; package cast.v2; -import "google/protobuf/empty.proto"; import "cast/cast_core/api/bindings/api_bindings.proto"; import "cast/cast_core/api/common/application_config.proto"; import "cast/cast_core/api/common/service_info.proto"; @@ -21,13 +25,14 @@ service CoreApplicationService { rpc GetConfig(GetConfigRequest) returns (GetConfigResponse); // Send a Cast V2 message to core application. - rpc SendCastMessage(CastMessage) returns (CastMessageResponse); + rpc SendCastMessage(CastMessageRequest) returns (CastMessageResponse); // Notifies Cast Core on the application state changes. The callback must be // called by the Runtime whenever the internal state of the application // changes. Cast Core may discard any resources associated with the // application upon failures. - rpc OnApplicationStatus(ApplicationStatus) returns (google.protobuf.Empty); + rpc SetApplicationStatus(ApplicationStatusRequest) + returns (ApplicationStatusResponse); // Posts messages between MessagePorts. MessagePorts are connected using other // services (e.g. ApiBindings), then registered with the @@ -35,17 +40,17 @@ service CoreApplicationService { rpc PostMessage(cast.web.Message) returns (cast.web.MessagePortStatus); // Gets the list of bindings to early-inject into javascript at page load. - rpc GetAll(google.protobuf.Empty) returns (cast.bindings.GetAllResponse); + rpc GetAll(cast.bindings.GetAllRequest) + returns (cast.bindings.GetAllResponse); // Connects to a binding returned by GetAll. - rpc Connect(cast.bindings.ConnectRequest) returns (google.protobuf.Empty); + rpc Connect(cast.bindings.ConnectRequest) + returns (cast.bindings.ConnectResponse); } message GetConfigRequest { // Cast session ID. string cast_session_id = 1; - // RuntimeApplication service info. - cast.common.ServiceInfo runtime_application_service_info = 2; } message GetConfigResponse { @@ -58,7 +63,7 @@ message GetConfigResponse { } // Contains information about an application status in the runtime. -message ApplicationStatus { +message ApplicationStatusRequest { // The Cast session ID whose application status changed. string cast_session_id = 1; @@ -89,3 +94,5 @@ message ApplicationStatus { // |stop_reason| is HTTP_ERROR. int32 http_response_code = 4; } + +message ApplicationStatusResponse {} diff --git a/chromium/third_party/openscreen/src/cast/cast_core/api/v2/runtime_application_service.proto b/chromium/third_party/openscreen/src/cast/cast_core/api/v2/runtime_application_service.proto index e8bc21bab29..292dbaa9ffb 100644 --- a/chromium/third_party/openscreen/src/cast/cast_core/api/v2/runtime_application_service.proto +++ b/chromium/third_party/openscreen/src/cast/cast_core/api/v2/runtime_application_service.proto @@ -1,8 +1,12 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// **** DO NOT EDIT - this .proto was automatically generated. **** syntax = "proto3"; package cast.v2; -import "google/protobuf/empty.proto"; import "cast/cast_core/api/v2/cast_message.proto"; import "cast/cast_core/api/v2/url_rewrite.proto"; import "cast/cast_core/api/web/message_channel.proto"; @@ -14,16 +18,8 @@ option optimize_for = LITE_RUNTIME; // This service is implemented by the Runtime and represents services // specific to a Cast application. service RuntimeApplicationService { - // Notifies the runtime that a new Cast V2 virtual connection has been opened. - rpc OnVirtualConnectionOpen(VirtualConnectionInfo) - returns (google.protobuf.Empty); - - // Notifies the runtime that a Cast V2 virtual connection has been closed. - rpc OnVirtualConnectionClosed(VirtualConnectionInfo) - returns (google.protobuf.Empty); - // Sends a Cast message to the runtime. - rpc SendCastMessage(CastMessage) returns (CastMessageResponse); + rpc SendCastMessage(CastMessageRequest) returns (CastMessageResponse); // Set the URL rewrite rules that the Runtime will use to contact the MSP // This is called when the rewrite rules are changed @@ -44,16 +40,3 @@ message SetUrlRewriteRulesRequest { } message SetUrlRewriteRulesResponse {} - -// Request by the sender to open or close a virtual connection to the Cast -// runtime. -message VirtualConnectionInfo { - // The source of the virtual connection request. Connections from the - // sender platform use an id of 'sender-0' and connections from applications - // use a unique ID. - string source_id = 1; - // The destination of the connection request. Connections to the Cast - // receiver platform use an id of 'receiver-0' and connections to applications - // use the Cast session id. - string destination_id = 2; -} diff --git a/chromium/third_party/openscreen/src/cast/cast_core/api/v2/url_rewrite.proto b/chromium/third_party/openscreen/src/cast/cast_core/api/v2/url_rewrite.proto index 94d7f3b511d..6f637c8da70 100644 --- a/chromium/third_party/openscreen/src/cast/cast_core/api/v2/url_rewrite.proto +++ b/chromium/third_party/openscreen/src/cast/cast_core/api/v2/url_rewrite.proto @@ -1,3 +1,8 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// **** DO NOT EDIT - this .proto was automatically generated. **** syntax = "proto3"; package cast.v2; diff --git a/chromium/third_party/openscreen/src/cast/cast_core/api/web/message_channel.proto b/chromium/third_party/openscreen/src/cast/cast_core/api/web/message_channel.proto index df4ea15d2d0..4e3f1deda04 100644 --- a/chromium/third_party/openscreen/src/cast/cast_core/api/web/message_channel.proto +++ b/chromium/third_party/openscreen/src/cast/cast_core/api/web/message_channel.proto @@ -1,3 +1,8 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// **** DO NOT EDIT - this .proto was automatically generated. **** syntax = "proto3"; package cast.web; diff --git a/chromium/third_party/openscreen/src/cast/common/certificate/cast_cert_validator_internal.cc b/chromium/third_party/openscreen/src/cast/common/certificate/cast_cert_validator_internal.cc index 350aca6ef4d..764ac3e4610 100644 --- a/chromium/third_party/openscreen/src/cast/common/certificate/cast_cert_validator_internal.cc +++ b/chromium/third_party/openscreen/src/cast/common/certificate/cast_cert_validator_internal.cc @@ -6,12 +6,16 @@ #include <openssl/asn1.h> #include <openssl/evp.h> +#include <openssl/mem.h> +#include <openssl/ossl_typ.h> #include <openssl/x509.h> #include <openssl/x509v3.h> #include <time.h> #include <chrono> +#include <memory> #include <string> +#include <utility> #include <vector> #include "cast/common/certificate/types.h" @@ -24,6 +28,14 @@ namespace { constexpr static int32_t kMinRsaModulusLengthBits = 2048; +// TODO(davidben): Switch this to bssl::UniquePtr after +// https://boringssl-review.googlesource.com/c/boringssl/+/46105 lands. +struct FreeNameConstraints { + void operator()(NAME_CONSTRAINTS* nc) { NAME_CONSTRAINTS_free(nc); } +}; +using NameConstraintsPtr = + std::unique_ptr<NAME_CONSTRAINTS, FreeNameConstraints>; + // Stores intermediate state while attempting to find a valid certificate chain // from a set of trusted certificates to a target certificate. Together, a // sequence of these forms a certificate chain to be verified as well as a stack @@ -59,23 +71,23 @@ bool CertInPath(X509_NAME* name, return false; } -// Parse the data in |time| at |index| as a two-digit ascii number. -uint8_t ParseAsn1TimeDoubleDigit(ASN1_GENERALIZEDTIME* time, int index) { - return (time->data[index] - '0') * 10 + (time->data[index + 1] - '0'); +// Parse the data in |time| at |index| as a two-digit ascii number. Note this +// function assumes the caller already did a bounds check and checked the inputs +// are digits. +uint8_t ParseAsn1TimeDoubleDigit(absl::string_view time, size_t index) { + OSP_DCHECK_LT(index + 1, time.size()); + OSP_DCHECK('0' <= time[index] && time[index] <= '9'); + OSP_DCHECK('0' <= time[index + 1] && time[index + 1] <= '9'); + return (time[index] - '0') * 10 + (time[index + 1] - '0'); } bssl::UniquePtr<BASIC_CONSTRAINTS> GetConstraints(X509* issuer) { - const int basic_constraints_index = - X509_get_ext_by_NID(issuer, NID_basic_constraints, -1); - if (basic_constraints_index == -1) { - return nullptr; - } - - X509_EXTENSION* const basic_constraints_extension = - X509_get_ext(issuer, basic_constraints_index); + // TODO(davidben): This and other |X509_get_ext_d2i| are missing + // error-handling for syntax errors in certificates. See BoringSSL + // documentation for the calling convention. return bssl::UniquePtr<BASIC_CONSTRAINTS>{ reinterpret_cast<BASIC_CONSTRAINTS*>( - X509V3_EXT_d2i(basic_constraints_extension))}; + X509_get_ext_d2i(issuer, NID_basic_constraints, nullptr, nullptr))}; } Error::Code VerifyCertTime(X509* cert, const DateTime& time) { @@ -96,18 +108,8 @@ bool VerifyPublicKeyLength(EVP_PKEY* public_key) { } bssl::UniquePtr<ASN1_BIT_STRING> GetKeyUsage(X509* cert) { - int pos = X509_get_ext_by_NID(cert, NID_key_usage, -1); - if (pos == -1) { - return nullptr; - } - X509_EXTENSION* key_usage = X509_get_ext(cert, pos); - const uint8_t* value = key_usage->value->data; - ASN1_BIT_STRING* key_usage_bit_string = nullptr; - if (!d2i_ASN1_BIT_STRING(&key_usage_bit_string, &value, - key_usage->value->length)) { - return nullptr; - } - return bssl::UniquePtr<ASN1_BIT_STRING>{key_usage_bit_string}; + return bssl::UniquePtr<ASN1_BIT_STRING>{reinterpret_cast<ASN1_BIT_STRING*>( + X509_get_ext_d2i(cert, NID_key_usage, nullptr, nullptr))}; } Error::Code VerifyCertificateChain(const std::vector<CertPathStep>& path, @@ -116,13 +118,14 @@ Error::Code VerifyCertificateChain(const std::vector<CertPathStep>& path, // Default max path length is the number of intermediate certificates. int max_pathlen = path.size() - 2; - std::vector<NAME_CONSTRAINTS*> path_name_constraints; + std::vector<NameConstraintsPtr> path_name_constraints; Error::Code error = Error::Code::kNone; uint32_t i = step_index; for (; i < path.size() - 1; ++i) { X509* subject = path[i + 1].cert; X509* issuer = path[i].cert; bool is_root = (i == step_index); + bool issuer_is_self_issued = false; if (!is_root) { if ((error = VerifyCertTime(issuer, time)) != Error::Code::kNone) { return error; @@ -134,10 +137,10 @@ Error::Code VerifyCertificateChain(const std::vector<CertPathStep>& path, } --max_pathlen; } else { - issuer->ex_flags |= EXFLAG_SI; + issuer_is_self_issued = true; } } else { - issuer->ex_flags |= EXFLAG_SI; + issuer_is_self_issued = true; } bssl::UniquePtr<ASN1_BIT_STRING> key_usage = GetKeyUsage(issuer); @@ -172,7 +175,9 @@ Error::Code VerifyCertificateChain(const std::vector<CertPathStep>& path, } } - if (X509_ALGOR_cmp(issuer->sig_alg, issuer->cert_info->signature) != 0) { + const X509_ALGOR* issuer_sig_alg; + X509_get0_signature(nullptr, &issuer_sig_alg, issuer); + if (X509_ALGOR_cmp(issuer_sig_alg, X509_get0_tbs_sigalg(issuer)) != 0) { return Error::Code::kErrCertsVerifyGeneric; } @@ -183,47 +188,39 @@ Error::Code VerifyCertificateChain(const std::vector<CertPathStep>& path, // NOTE: (!self-issued || target) -> verify name constraints. Target case // is after the loop. - const bool is_self_issued = issuer->ex_flags & EXFLAG_SI; - if (!is_self_issued) { - for (NAME_CONSTRAINTS* name_constraints : path_name_constraints) { - if (NAME_CONSTRAINTS_check(subject, name_constraints) != X509_V_OK) { + if (!issuer_is_self_issued) { + for (const auto& name_constraints : path_name_constraints) { + if (NAME_CONSTRAINTS_check(subject, name_constraints.get()) != + X509_V_OK) { return Error::Code::kErrCertsVerifyGeneric; } } } - if (issuer->nc) { - path_name_constraints.push_back(issuer->nc); - } else { - const int index = X509_get_ext_by_NID(issuer, NID_name_constraints, -1); - if (index != -1) { - X509_EXTENSION* ext = X509_get_ext(issuer, index); - auto* nc = reinterpret_cast<NAME_CONSTRAINTS*>(X509V3_EXT_d2i(ext)); - if (nc) { - issuer->nc = nc; - path_name_constraints.push_back(nc); - } else { - return Error::Code::kErrCertsVerifyGeneric; - } - } + int critical; + NameConstraintsPtr nc{reinterpret_cast<NAME_CONSTRAINTS*>( + X509_get_ext_d2i(issuer, NID_name_constraints, &critical, nullptr))}; + if (!nc && critical != -1) { + // X509_get_ext_d2i's error handling is a little confusing. See + // https://boringssl.googlesource.com/boringssl/+/215f4a0287/include/openssl/x509.h#1384 + // https://boringssl.googlesource.com/boringssl/+/215f4a0287/include/openssl/x509v3.h#651 + return Error::Code::kErrCertsVerifyGeneric; + } + if (nc) { + path_name_constraints.push_back(std::move(nc)); } // Check that any policy mappings present are _not_ the anyPolicy OID. Even // though we don't otherwise handle policies, this is required by RFC 5280 // 6.1.4(a). - const int policy_mappings_index = - X509_get_ext_by_NID(issuer, NID_policy_mappings, -1); - if (policy_mappings_index != -1) { - X509_EXTENSION* policy_mappings_extension = - X509_get_ext(issuer, policy_mappings_index); - auto* policy_mappings = reinterpret_cast<POLICY_MAPPINGS*>( - X509V3_EXT_d2i(policy_mappings_extension)); - const uint32_t policy_mapping_count = - sk_POLICY_MAPPING_num(policy_mappings); + // + // TODO(davidben): Switch to bssl::UniquePtr once + // https://boringssl-review.googlesource.com/c/boringssl/+/46104 has landed. + auto* policy_mappings = reinterpret_cast<POLICY_MAPPINGS*>( + X509_get_ext_d2i(issuer, NID_policy_mappings, nullptr, nullptr)); + if (policy_mappings) { const ASN1_OBJECT* any_policy = OBJ_nid2obj(NID_any_policy); - for (uint32_t i = 0; i < policy_mapping_count; ++i) { - POLICY_MAPPING* policy_mapping = - sk_POLICY_MAPPING_value(policy_mappings, i); + for (const POLICY_MAPPING* policy_mapping : policy_mappings) { const bool either_matches = ((OBJ_cmp(policy_mapping->issuerDomainPolicy, any_policy) == 0) || (OBJ_cmp(policy_mapping->subjectDomainPolicy, any_policy) == 0)); @@ -242,8 +239,8 @@ Error::Code VerifyCertificateChain(const std::vector<CertPathStep>& path, int extension_count = X509_get_ext_count(issuer); for (int i = 0; i < extension_count; ++i) { X509_EXTENSION* extension = X509_get_ext(issuer, i); - if (extension->critical > 0) { - const int nid = OBJ_obj2nid(extension->object); + if (X509_EXTENSION_get_critical(extension)) { + const int nid = OBJ_obj2nid(X509_EXTENSION_get_object(extension)); if (nid != NID_name_constraints && nid != NID_basic_constraints && nid != NID_key_usage) { return Error::Code::kErrCertsVerifyGeneric; @@ -251,7 +248,7 @@ Error::Code VerifyCertificateChain(const std::vector<CertPathStep>& path, } } - int nid = OBJ_obj2nid(subject->sig_alg->algorithm); + int nid = X509_get_signature_nid(subject); const EVP_MD* digest; switch (nid) { case NID_sha1WithRSAEncryption: @@ -269,18 +266,24 @@ Error::Code VerifyCertificateChain(const std::vector<CertPathStep>& path, default: return Error::Code::kErrCertsVerifyGeneric; } + uint8_t* tbs = nullptr; + int tbs_len = i2d_X509_tbs(subject, &tbs); + if (tbs_len < 0) { + return Error::Code::kErrCertsVerifyGeneric; + } + bssl::UniquePtr<uint8_t> free_tbs{tbs}; + const ASN1_BIT_STRING* signature; + X509_get0_signature(&signature, nullptr, subject); if (!VerifySignedData( - digest, public_key.get(), - {subject->cert_info->enc.enc, - static_cast<uint32_t>(subject->cert_info->enc.len)}, - {subject->signature->data, - static_cast<uint32_t>(subject->signature->length)})) { + digest, public_key.get(), {tbs, static_cast<uint32_t>(tbs_len)}, + {ASN1_STRING_get0_data(signature), + static_cast<uint32_t>(ASN1_STRING_length(signature))})) { return Error::Code::kErrCertsVerifyGeneric; } } // NOTE: Other half of ((!self-issued || target) -> check name constraints). - for (NAME_CONSTRAINTS* name_constraints : path_name_constraints) { - if (NAME_CONSTRAINTS_check(path.back().cert, name_constraints) != + for (const auto& name_constraints : path_name_constraints) { + if (NAME_CONSTRAINTS_check(path.back().cert, name_constraints.get()) != X509_V_OK) { return Error::Code::kErrCertsVerifyGeneric; } @@ -302,24 +305,27 @@ bool ParseAsn1GeneralizedTime(ASN1_GENERALIZEDTIME* time, DateTime* out) { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, }; - if (time->length != 15) { + absl::string_view time_str{ + reinterpret_cast<const char*>(ASN1_STRING_get0_data(time)), + static_cast<size_t>(ASN1_STRING_length(time))}; + if (time_str.size() != 15) { return false; } - if (time->data[14] != 'Z') { + if (time_str[14] != 'Z') { return false; } - for (int i = 0; i < 14; ++i) { - if (time->data[i] < '0' || time->data[i] > '9') { + for (size_t i = 0; i < 14; ++i) { + if (time_str[i] < '0' || time_str[i] > '9') { return false; } } - out->year = ParseAsn1TimeDoubleDigit(time, 0) * 100 + - ParseAsn1TimeDoubleDigit(time, 2); - out->month = ParseAsn1TimeDoubleDigit(time, 4); - out->day = ParseAsn1TimeDoubleDigit(time, 6); - out->hour = ParseAsn1TimeDoubleDigit(time, 8); - out->minute = ParseAsn1TimeDoubleDigit(time, 10); - out->second = ParseAsn1TimeDoubleDigit(time, 12); + out->year = ParseAsn1TimeDoubleDigit(time_str, 0) * 100 + + ParseAsn1TimeDoubleDigit(time_str, 2); + out->month = ParseAsn1TimeDoubleDigit(time_str, 4); + out->day = ParseAsn1TimeDoubleDigit(time_str, 6); + out->hour = ParseAsn1TimeDoubleDigit(time_str, 8); + out->minute = ParseAsn1TimeDoubleDigit(time_str, 10); + out->second = ParseAsn1TimeDoubleDigit(time_str, 12); if (out->month == 0 || out->month > 12) { return false; } @@ -350,18 +356,15 @@ bool ParseAsn1GeneralizedTime(ASN1_GENERALIZEDTIME* time, DateTime* out) { bool GetCertValidTimeRange(X509* cert, DateTime* not_before, DateTime* not_after) { - ASN1_GENERALIZEDTIME* not_before_asn1 = ASN1_TIME_to_generalizedtime( - cert->cert_info->validity->notBefore, nullptr); - ASN1_GENERALIZEDTIME* not_after_asn1 = ASN1_TIME_to_generalizedtime( - cert->cert_info->validity->notAfter, nullptr); + bssl::UniquePtr<ASN1_GENERALIZEDTIME> not_before_asn1{ + ASN1_TIME_to_generalizedtime(X509_get0_notBefore(cert), nullptr)}; + bssl::UniquePtr<ASN1_GENERALIZEDTIME> not_after_asn1{ + ASN1_TIME_to_generalizedtime(X509_get0_notAfter(cert), nullptr)}; if (!not_before_asn1 || !not_after_asn1) { return false; } - bool times_valid = ParseAsn1GeneralizedTime(not_before_asn1, not_before) && - ParseAsn1GeneralizedTime(not_after_asn1, not_after); - ASN1_GENERALIZEDTIME_free(not_before_asn1); - ASN1_GENERALIZEDTIME_free(not_after_asn1); - return times_valid; + return ParseAsn1GeneralizedTime(not_before_asn1.get(), not_before) && + ParseAsn1GeneralizedTime(not_after_asn1.get(), not_after); } // static @@ -428,8 +431,9 @@ Error FindCertificatePath(const std::vector<std::string>& der_certs, OSP_DVLOG << "FindCertificatePath: Failed with invalid public key length"; return Error::Code::kErrCertsVerifyGeneric; } - if (X509_ALGOR_cmp(target_cert.get()->sig_alg, - target_cert.get()->cert_info->signature) != 0) { + const X509_ALGOR* sig_alg; + X509_get0_signature(nullptr, &sig_alg, target_cert.get()); + if (X509_ALGOR_cmp(sig_alg, X509_get0_tbs_sigalg(target_cert.get())) != 0) { return Error::Code::kErrCertsVerifyGeneric; } bssl::UniquePtr<ASN1_BIT_STRING> key_usage = GetKeyUsage(target_cert.get()); diff --git a/chromium/third_party/openscreen/src/cast/common/certificate/cast_crl.cc b/chromium/third_party/openscreen/src/cast/common/certificate/cast_crl.cc index c7635a4fe2a..aa269df5bd0 100644 --- a/chromium/third_party/openscreen/src/cast/common/certificate/cast_crl.cc +++ b/chromium/third_party/openscreen/src/cast/common/certificate/cast_crl.cc @@ -107,14 +107,15 @@ bool VerifyCRL(const Crl& crl, // (excluding trust anchor). No intermediates are provided above, so this // just amounts to |signer_cert| vs. |not_after_seconds|. *overall_not_after = not_after; - ASN1_GENERALIZEDTIME* not_after_asn1 = ASN1_TIME_to_generalizedtime( - result_path.target_cert->cert_info->validity->notAfter, nullptr); + bssl::UniquePtr<ASN1_GENERALIZEDTIME> not_after_asn1{ + ASN1_TIME_to_generalizedtime( + X509_get0_notAfter(result_path.target_cert.get()), nullptr)}; if (!not_after_asn1) { return false; } DateTime cert_not_after; - bool time_valid = ParseAsn1GeneralizedTime(not_after_asn1, &cert_not_after); - ASN1_GENERALIZEDTIME_free(not_after_asn1); + bool time_valid = + ParseAsn1GeneralizedTime(not_after_asn1.get(), &cert_not_after); if (!time_valid) { return false; } @@ -199,7 +200,7 @@ bool CastCRL::CheckRevocation(const std::vector<X509*>& trusted_chain, // Only Google generated device certificates will be revoked by range. // These will always be less than 64 bits in length. ErrorOr<uint64_t> maybe_serial = - ParseDerUint64(subordinate->cert_info->serialNumber); + ParseDerUint64(X509_get0_serialNumber(subordinate)); if (!maybe_serial) { continue; } diff --git a/chromium/third_party/openscreen/src/cast/common/discovery/e2e_test/tests.cc b/chromium/third_party/openscreen/src/cast/common/discovery/e2e_test/tests.cc index 9a02053b7a4..7c29441829a 100644 --- a/chromium/third_party/openscreen/src/cast/common/discovery/e2e_test/tests.cc +++ b/chromium/third_party/openscreen/src/cast/common/discovery/e2e_test/tests.cc @@ -143,7 +143,7 @@ class DiscoveryE2ETest : public testing::Test { // Sleep to let any packets clear off the network before further tests. std::this_thread::sleep_for(milliseconds(500)); - PlatformClientPosix::Create(milliseconds(50), milliseconds(50)); + PlatformClientPosix::Create(milliseconds(50)); task_runner_ = PlatformClientPosix::GetInstance()->GetTaskRunner(); } diff --git a/chromium/third_party/openscreen/src/cast/protocol/castv2/streaming_examples/answer.json b/chromium/third_party/openscreen/src/cast/protocol/castv2/streaming_examples/answer.json index 97a3e59aaae..73c45ea26f4 100644 --- a/chromium/third_party/openscreen/src/cast/protocol/castv2/streaming_examples/answer.json +++ b/chromium/third_party/openscreen/src/cast/protocol/castv2/streaming_examples/answer.json @@ -12,8 +12,7 @@ "maxSampleRate": 96000, "maxChannels": 5, "minBitRate": 32000, - "maxBitRate": 320000, - "maxDelay": 4000 + "maxBitRate": 320000 }, "video": { "maxPixelsPerSecond": 62208000, diff --git a/chromium/third_party/openscreen/src/cast/protocol/castv2/streaming_schema.json b/chromium/third_party/openscreen/src/cast/protocol/castv2/streaming_schema.json index 2c2fc59acf2..392d135cd43 100644 --- a/chromium/third_party/openscreen/src/cast/protocol/castv2/streaming_schema.json +++ b/chromium/third_party/openscreen/src/cast/protocol/castv2/streaming_schema.json @@ -110,7 +110,7 @@ "maxBitRate": {"type": "integer", "minimum": 32000, "maximum": 320000}, "maxDelay": {"$ref": "#/definitions/delay"} }, - "required": ["maxSampleRate", "maxChannels", "maxBitRate", "maxDelay"] + "required": ["maxSampleRate", "maxChannels", "maxBitRate"] }, "video_constraints": { "properties": { @@ -122,10 +122,8 @@ "maxDelay": {"$ref": "#/definitions/delay"} }, "required": [ - "maxPixelsPerSecond", "maxDimensions", - "maxBitRate", - "maxDelay" + "maxBitRate" ] }, "constraints": { diff --git a/chromium/third_party/openscreen/src/cast/standalone_receiver/cast_service.cc b/chromium/third_party/openscreen/src/cast/standalone_receiver/cast_service.cc index e4a5b531c80..75790197a3d 100644 --- a/chromium/third_party/openscreen/src/cast/standalone_receiver/cast_service.cc +++ b/chromium/third_party/openscreen/src/cast/standalone_receiver/cast_service.cc @@ -38,8 +38,7 @@ discovery::Config MakeDiscoveryConfig(const InterfaceInfo& interface) { discovery::Config::NetworkInfo::kNoAddressFamily; if (interface.GetIpAddressV4()) { supported_address_families |= discovery::Config::NetworkInfo::kUseIpV4; - } - if (interface.GetIpAddressV6()) { + } else if (interface.GetIpAddressV6()) { supported_address_families |= discovery::Config::NetworkInfo::kUseIpV6; } config.network_info.push_back({interface, supported_address_families}); @@ -84,6 +83,7 @@ CastService::CastService(TaskRunner* task_runner, info.unique_id = HexEncode(interface.hardware_address); info.friendly_name = friendly_name; info.model_name = model_name; + info.capabilities = kHasVideoOutput | kHasAudioOutput; Error error = discovery_publisher_->Register(info); if (!error.ok()) { OnFatalError(std::move(error)); diff --git a/chromium/third_party/openscreen/src/cast/standalone_receiver/main.cc b/chromium/third_party/openscreen/src/cast/standalone_receiver/main.cc index 1263f39cee2..9e305c8bd3c 100644 --- a/chromium/third_party/openscreen/src/cast/standalone_receiver/main.cc +++ b/chromium/third_party/openscreen/src/cast/standalone_receiver/main.cc @@ -230,7 +230,7 @@ int RunStandaloneReceiver(int argc, char* argv[]) { } auto* const task_runner = new TaskRunnerImpl(&Clock::now); - PlatformClientPosix::Create(milliseconds(50), milliseconds(50), + PlatformClientPosix::Create(milliseconds(50), std::unique_ptr<TaskRunnerImpl>(task_runner)); RunCastService(task_runner, interface, std::move(creds.value()), friendly_name, model_name, discovery_enabled); diff --git a/chromium/third_party/openscreen/src/cast/standalone_sender/looping_file_cast_agent.cc b/chromium/third_party/openscreen/src/cast/standalone_sender/looping_file_cast_agent.cc index 1a90cbb2cab..8c77577b655 100644 --- a/chromium/third_party/openscreen/src/cast/standalone_sender/looping_file_cast_agent.cc +++ b/chromium/third_party/openscreen/src/cast/standalone_sender/looping_file_cast_agent.cc @@ -268,17 +268,28 @@ void LoopingFileCastAgent::OnRemoteMessagingOpened(bool success) { void LoopingFileCastAgent::CreateAndStartSession() { TRACE_DEFAULT_SCOPED(TraceCategory::kStandaloneSender); + OSP_DCHECK(remote_connection_.has_value()); environment_ = std::make_unique<Environment>(&Clock::now, task_runner_, IPEndpoint{}); - OSP_DCHECK(remote_connection_.has_value()); - current_session_ = std::make_unique<SenderSession>( - connection_settings_->receiver_endpoint.address, this, environment_.get(), - &message_port_, remote_connection_->local_id, - remote_connection_->peer_id); + + SenderSession::Configuration config{ + connection_settings_->receiver_endpoint.address, + this, + environment_.get(), + &message_port_, + remote_connection_->local_id, + remote_connection_->peer_id, + connection_settings_->use_android_rtp_hack}; + current_session_ = std::make_unique<SenderSession>(std::move(config)); OSP_DCHECK(!message_port_.client_sender_id().empty()); AudioCaptureConfig audio_config; + // Opus does best at 192kbps, so we cap that here. + audio_config.bit_rate = 192 * 1000; VideoCaptureConfig video_config; + // The video config is allowed to use whatever is left over after audio. + video_config.max_bit_rate = + connection_settings_->max_bitrate - audio_config.bit_rate; // Use default display resolution of 1080P. video_config.resolutions.emplace_back(DisplayResolution{}); @@ -299,11 +310,9 @@ void LoopingFileCastAgent::OnNegotiated( return; } - OSP_LOG_INFO << "Streaming to " << connection_settings_->receiver_endpoint - << "..."; file_sender_ = std::make_unique<LoopingFileSender>( - environment_.get(), connection_settings_->path_to_file.c_str(), senders, - connection_settings_->max_bitrate); + environment_.get(), connection_settings_->path_to_file.c_str(), session, + std::move(senders), connection_settings_->max_bitrate); } void LoopingFileCastAgent::OnError(const SenderSession* session, Error error) { diff --git a/chromium/third_party/openscreen/src/cast/standalone_sender/looping_file_sender.cc b/chromium/third_party/openscreen/src/cast/standalone_sender/looping_file_sender.cc index 67aff23b20c..9fae843963b 100644 --- a/chromium/third_party/openscreen/src/cast/standalone_sender/looping_file_sender.cc +++ b/chromium/third_party/openscreen/src/cast/standalone_sender/looping_file_sender.cc @@ -11,11 +11,12 @@ namespace cast { LoopingFileSender::LoopingFileSender(Environment* environment, const char* path, + const SenderSession* session, SenderSession::ConfiguredSenders senders, int max_bitrate) : env_(environment), path_(path), - packet_router_(env_), + session_(session), max_bitrate_(max_bitrate), audio_encoder_(senders.audio_sender->config().channels, StreamingOpusEncoder::kDefaultCastAudioFramesPerSecond, @@ -51,7 +52,7 @@ void LoopingFileSender::UpdateEncoderBitrates() { } void LoopingFileSender::ControlForNetworkCongestion() { - bandwidth_estimate_ = packet_router_.ComputeNetworkBandwidth(); + bandwidth_estimate_ = session_->GetEstimatedNetworkBandwidth(); if (bandwidth_estimate_ > 0) { // Don't ever try to use *all* of the network bandwidth! However, don't go // below the absolute minimum requirement either. diff --git a/chromium/third_party/openscreen/src/cast/standalone_sender/looping_file_sender.h b/chromium/third_party/openscreen/src/cast/standalone_sender/looping_file_sender.h index 4f6c5d90715..e55a4a7e0c3 100644 --- a/chromium/third_party/openscreen/src/cast/standalone_sender/looping_file_sender.h +++ b/chromium/third_party/openscreen/src/cast/standalone_sender/looping_file_sender.h @@ -24,6 +24,7 @@ class LoopingFileSender final : public SimulatedAudioCapturer::Client, public: LoopingFileSender(Environment* environment, const char* path, + const SenderSession* session, SenderSession::ConfiguredSenders senders, int max_bitrate); @@ -59,11 +60,12 @@ class LoopingFileSender final : public SimulatedAudioCapturer::Client, // The path to the media file to stream over and over. const char* const path_; - // The packet router allows both the Audio Sender and the Video Sender to - // share the same UDP socket. - SenderPacketRouter packet_router_; + // Session to query for bandwidth information. + const SenderSession* session_; + + // User provided maximum bitrate (from command line argument). + const int max_bitrate_; - const int max_bitrate_; // Passed by the user on the command line. int bandwidth_estimate_ = 0; int bandwidth_being_utilized_; diff --git a/chromium/third_party/openscreen/src/cast/standalone_sender/main.cc b/chromium/third_party/openscreen/src/cast/standalone_sender/main.cc index da7ed24c71e..75b505534a4 100644 --- a/chromium/third_party/openscreen/src/cast/standalone_sender/main.cc +++ b/chromium/third_party/openscreen/src/cast/standalone_sender/main.cc @@ -174,7 +174,7 @@ int StandaloneSenderMain(int argc, char* argv[]) { #endif auto* const task_runner = new TaskRunnerImpl(&Clock::now); - PlatformClientPosix::Create(milliseconds(50), milliseconds(50), + PlatformClientPosix::Create(milliseconds(50), std::unique_ptr<TaskRunnerImpl>(task_runner)); IPEndpoint remote_endpoint = ParseAsEndpoint(iface_or_endpoint); diff --git a/chromium/third_party/openscreen/src/cast/standalone_sender/streaming_vp8_encoder.cc b/chromium/third_party/openscreen/src/cast/standalone_sender/streaming_vp8_encoder.cc index 066e37f5ae1..8b8e18dc5f6 100644 --- a/chromium/third_party/openscreen/src/cast/standalone_sender/streaming_vp8_encoder.cc +++ b/chromium/third_party/openscreen/src/cast/standalone_sender/streaming_vp8_encoder.cc @@ -226,6 +226,7 @@ void StreamingVp8Encoder::ProcessWorkUnitsUntilTimeToQuit() { static_cast<WorkUnit&>(work_unit) = std::move(encode_queue_.front()); encode_queue_.pop(); force_key_frame = needs_key_frame_; + needs_key_frame_ = false; target_bitrate = target_bitrate_; } diff --git a/chromium/third_party/openscreen/src/cast/streaming/BUILD.gn b/chromium/third_party/openscreen/src/cast/streaming/BUILD.gn index 04424cf4154..9f7d72524f4 100644 --- a/chromium/third_party/openscreen/src/cast/streaming/BUILD.gn +++ b/chromium/third_party/openscreen/src/cast/streaming/BUILD.gn @@ -112,6 +112,7 @@ source_set("sender") { "bandwidth_estimator.h", "compound_rtcp_parser.cc", "compound_rtcp_parser.h", + "remoting_capabilities.h", "rtp_packetizer.cc", "rtp_packetizer.h", "sender.cc", diff --git a/chromium/third_party/openscreen/src/cast/streaming/answer_messages.cc b/chromium/third_party/openscreen/src/cast/streaming/answer_messages.cc index ed308260e8b..906e8901ed2 100644 --- a/chromium/third_party/openscreen/src/cast/streaming/answer_messages.cc +++ b/chromium/third_party/openscreen/src/cast/streaming/answer_messages.cc @@ -210,10 +210,15 @@ bool AudioConstraints::ParseAndValidate(const Json::Value& root, if (!json::ParseAndValidateInt(root[kMaxSampleRate], &(out->max_sample_rate)) || !json::ParseAndValidateInt(root[kMaxChannels], &(out->max_channels)) || - !json::ParseAndValidateInt(root[kMaxBitRate], &(out->max_bit_rate)) || - !json::ParseAndValidateMilliseconds(root[kMaxDelay], &(out->max_delay))) { + !json::ParseAndValidateInt(root[kMaxBitRate], &(out->max_bit_rate))) { return false; } + + std::chrono::milliseconds max_delay; + if (json::ParseAndValidateMilliseconds(root[kMaxDelay], &max_delay)) { + out->max_delay = max_delay; + } + if (!json::ParseAndValidateInt(root[kMinBitRate], &(out->min_bit_rate))) { out->min_bit_rate = kDefaultAudioMinBitRate; } @@ -227,7 +232,9 @@ Json::Value AudioConstraints::ToJson() const { root[kMaxChannels] = max_channels; root[kMinBitRate] = min_bit_rate; root[kMaxBitRate] = max_bit_rate; - root[kMaxDelay] = Json::Value::Int64(max_delay.count()); + if (max_delay.has_value()) { + root[kMaxDelay] = Json::Value::Int64(max_delay->count()); + } return root; } @@ -262,16 +269,25 @@ Json::Value Dimensions::ToJson() const { // static bool VideoConstraints::ParseAndValidate(const Json::Value& root, VideoConstraints* out) { - if (!json::ParseAndValidateDouble(root[kMaxPixelsPerSecond], - &(out->max_pixels_per_second)) || - !Dimensions::ParseAndValidate(root[kMaxDimensions], + if (!Dimensions::ParseAndValidate(root[kMaxDimensions], &(out->max_dimensions)) || !json::ParseAndValidateInt(root[kMaxBitRate], &(out->max_bit_rate)) || - !json::ParseAndValidateMilliseconds(root[kMaxDelay], &(out->max_delay)) || !ParseOptional<Dimensions>(root[kMinDimensions], &(out->min_dimensions))) { return false; } + + std::chrono::milliseconds max_delay; + if (json::ParseAndValidateMilliseconds(root[kMaxDelay], &max_delay)) { + out->max_delay = max_delay; + } + + double max_pixels_per_second; + if (json::ParseAndValidateDouble(root[kMaxPixelsPerSecond], + &max_pixels_per_second)) { + out->max_pixels_per_second = max_pixels_per_second; + } + if (!json::ParseAndValidateInt(root[kMinBitRate], &(out->min_bit_rate))) { out->min_bit_rate = kDefaultVideoMinBitRate; } @@ -280,7 +296,8 @@ bool VideoConstraints::ParseAndValidate(const Json::Value& root, bool VideoConstraints::IsValid() const { return max_pixels_per_second > 0 && min_bit_rate > 0 && - max_bit_rate > min_bit_rate && max_delay.count() > 0 && + max_bit_rate > min_bit_rate && + (!max_delay.has_value() || max_delay->count() > 0) && max_dimensions.IsValid() && (!min_dimensions.has_value() || min_dimensions->IsValid()) && max_dimensions.frame_rate.numerator > 0; @@ -289,14 +306,20 @@ bool VideoConstraints::IsValid() const { Json::Value VideoConstraints::ToJson() const { OSP_DCHECK(IsValid()); Json::Value root; - root[kMaxPixelsPerSecond] = max_pixels_per_second; - if (min_dimensions.has_value()) { - root[kMinDimensions] = min_dimensions->ToJson(); - } root[kMaxDimensions] = max_dimensions.ToJson(); root[kMinBitRate] = min_bit_rate; root[kMaxBitRate] = max_bit_rate; - root[kMaxDelay] = Json::Value::Int64(max_delay.count()); + if (max_pixels_per_second.has_value()) { + root[kMaxPixelsPerSecond] = max_pixels_per_second.value(); + } + + if (min_dimensions.has_value()) { + root[kMinDimensions] = min_dimensions->ToJson(); + } + + if (max_delay.has_value()) { + root[kMaxDelay] = Json::Value::Int64(max_delay->count()); + } return root; } @@ -346,9 +369,11 @@ bool DisplayDescription::IsValid() const { if (aspect_ratio.has_value() && !aspect_ratio->IsValid()) { return false; } + if (dimensions.has_value() && !dimensions->IsValid()) { return false; } + // Sender behavior is undefined if the aspect ratio is fixed but no // dimensions or aspect ratio are provided. if (aspect_ratio_constraint.has_value() && diff --git a/chromium/third_party/openscreen/src/cast/streaming/answer_messages.h b/chromium/third_party/openscreen/src/cast/streaming/answer_messages.h index 4298913b6dc..1f62706a1a8 100644 --- a/chromium/third_party/openscreen/src/cast/streaming/answer_messages.h +++ b/chromium/third_party/openscreen/src/cast/streaming/answer_messages.h @@ -41,10 +41,9 @@ struct AudioConstraints { int max_sample_rate = 0; int max_channels = 0; - // Technically optional, sender will assume 32kbps if omitted. - int min_bit_rate = 0; + int min_bit_rate = 0; // optional int max_bit_rate = 0; - std::chrono::milliseconds max_delay = {}; + absl::optional<std::chrono::milliseconds> max_delay = {}; }; struct Dimensions { @@ -62,13 +61,12 @@ struct VideoConstraints { Json::Value ToJson() const; bool IsValid() const; - double max_pixels_per_second = {}; + absl::optional<double> max_pixels_per_second = {}; absl::optional<Dimensions> min_dimensions = {}; Dimensions max_dimensions = {}; - // Technically optional, sender will assume 300kbps if omitted. - int min_bit_rate = 0; + int min_bit_rate = 0; // optional int max_bit_rate = 0; - std::chrono::milliseconds max_delay = {}; + absl::optional<std::chrono::milliseconds> max_delay = {}; }; struct Constraints { diff --git a/chromium/third_party/openscreen/src/cast/streaming/capture_recommendations.cc b/chromium/third_party/openscreen/src/cast/streaming/capture_recommendations.cc index 94ac47de66d..b30b5dc10d0 100644 --- a/chromium/third_party/openscreen/src/cast/streaming/capture_recommendations.cc +++ b/chromium/third_party/openscreen/src/cast/streaming/capture_recommendations.cc @@ -83,7 +83,9 @@ void ApplyConstraints(const Constraints& constraints, Recommendations* recommendations) { // Audio has no fields in the display description, so we can safely // ignore the current recommendations when setting values here. - recommendations->audio.max_delay = constraints.audio.max_delay; + if (constraints.audio.max_delay.has_value()) { + recommendations->audio.max_delay = constraints.audio.max_delay.value(); + } recommendations->audio.max_channels = constraints.audio.max_channels; recommendations->audio.max_sample_rate = constraints.audio.max_sample_rate; @@ -93,9 +95,15 @@ void ApplyConstraints(const Constraints& constraints, // With video, we take the intersection of values of the constraints and // the display description. - recommendations->video.max_delay = constraints.video.max_delay; - recommendations->video.max_pixels_per_second = - constraints.video.max_pixels_per_second; + if (constraints.video.max_delay.has_value()) { + recommendations->video.max_delay = constraints.video.max_delay.value(); + } + + if (constraints.video.max_pixels_per_second.has_value()) { + recommendations->video.max_pixels_per_second = + constraints.video.max_pixels_per_second.value(); + } + recommendations->video.bit_rate_limits = BitRateLimits{std::max(constraints.video.min_bit_rate, recommendations->video.bit_rate_limits.minimum), diff --git a/chromium/third_party/openscreen/src/cast/streaming/capture_recommendations.h b/chromium/third_party/openscreen/src/cast/streaming/capture_recommendations.h index 94855c7c669..ccb2475b167 100644 --- a/chromium/third_party/openscreen/src/cast/streaming/capture_recommendations.h +++ b/chromium/third_party/openscreen/src/cast/streaming/capture_recommendations.h @@ -29,7 +29,7 @@ namespace capture_recommendations { // Default maximum delay for both audio and video. Used if the sender fails // to provide any constraints. -constexpr std::chrono::milliseconds kDefaultMaxDelayMs(4000); +constexpr std::chrono::milliseconds kDefaultMaxDelayMs(400); // Bit rate limits, used for both audio and video streams. struct BitRateLimits { diff --git a/chromium/third_party/openscreen/src/cast/streaming/capture_recommendations_unittest.cc b/chromium/third_party/openscreen/src/cast/streaming/capture_recommendations_unittest.cc index 8e9765af99e..4f76b9d9ca6 100644 --- a/chromium/third_party/openscreen/src/cast/streaming/capture_recommendations_unittest.cc +++ b/chromium/third_party/openscreen/src/cast/streaming/capture_recommendations_unittest.cc @@ -16,9 +16,9 @@ namespace capture_recommendations { namespace { constexpr Recommendations kDefaultRecommendations{ - Audio{BitRateLimits{32000, 256000}, milliseconds(4000), 2, 48000, 16000}, + Audio{BitRateLimits{32000, 256000}, milliseconds(400), 2, 48000, 16000}, Video{BitRateLimits{300000, 1920 * 1080 * 30}, Resolution{320, 240, 30}, - Resolution{1920, 1080, 30}, false, milliseconds(4000), + Resolution{1920, 1080, 30}, false, milliseconds(400), 1920 * 1080 * 30 / 8}}; constexpr DisplayDescription kEmptyDescription{}; diff --git a/chromium/third_party/openscreen/src/cast/streaming/constants.h b/chromium/third_party/openscreen/src/cast/streaming/constants.h index 1075a8171fb..ae1e8aa4658 100644 --- a/chromium/third_party/openscreen/src/cast/streaming/constants.h +++ b/chromium/third_party/openscreen/src/cast/streaming/constants.h @@ -67,10 +67,13 @@ constexpr int kDefaultAudioChannels = 2; // Codecs known and understood by cast senders and receivers. Note: receivers // are required to implement the following codecs to be Cast V2 compliant: H264, -// VP8, AAC, Opus. Senders have to implement at least one codec for audio and -// video to start a session. -enum class AudioCodec { kAac, kOpus }; -enum class VideoCodec { kH264, kVp8, kHevc, kVp9 }; +// VP8, AAC, Opus. Senders have to implement at least one codec from this +// list for audio or video to start a session. +// |kNotSpecified| is used in remoting to indicate that the stream is being +// remoted and is not specified as part of the OFFER message (indicated as +// "REMOTE_AUDIO" or "REMOTE_VIDEO"). +enum class AudioCodec { kAac, kOpus, kNotSpecified }; +enum class VideoCodec { kH264, kVp8, kHevc, kVp9, kNotSpecified }; } // namespace cast } // namespace openscreen diff --git a/chromium/third_party/openscreen/src/cast/streaming/message_fields.cc b/chromium/third_party/openscreen/src/cast/streaming/message_fields.cc index f199ab8d539..9eb40bf3746 100644 --- a/chromium/third_party/openscreen/src/cast/streaming/message_fields.cc +++ b/chromium/third_party/openscreen/src/cast/streaming/message_fields.cc @@ -14,14 +14,17 @@ namespace openscreen { namespace cast { namespace { -constexpr EnumNameTable<AudioCodec, 2> kAudioCodecNames{ - {{"aac", AudioCodec::kAac}, {"opus", AudioCodec::kOpus}}}; +constexpr EnumNameTable<AudioCodec, 3> kAudioCodecNames{ + {{"aac", AudioCodec::kAac}, + {"opus", AudioCodec::kOpus}, + {"REMOTE_AUDIO", AudioCodec::kNotSpecified}}}; -constexpr EnumNameTable<VideoCodec, 4> kVideoCodecNames{ +constexpr EnumNameTable<VideoCodec, 5> kVideoCodecNames{ {{"h264", VideoCodec::kH264}, {"vp8", VideoCodec::kVp8}, {"hevc", VideoCodec::kHevc}, - {"vp9", VideoCodec::kVp9}}}; + {"vp9", VideoCodec::kVp9}, + {"REMOTE_VIDEO", VideoCodec::kNotSpecified}}}; } // namespace diff --git a/chromium/third_party/openscreen/src/cast/streaming/offer_messages.cc b/chromium/third_party/openscreen/src/cast/streaming/offer_messages.cc index dfc5a15ebb4..cea500cd8e7 100644 --- a/chromium/third_party/openscreen/src/cast/streaming/offer_messages.cc +++ b/chromium/third_party/openscreen/src/cast/streaming/offer_messages.cc @@ -120,12 +120,10 @@ ErrorOr<Stream> ParseStream(const Json::Value& value, Stream::Type type) { return ssrc.error(); } auto aes_key = ParseAesHexBytes(value, "aesKey"); - if (!aes_key) { - return aes_key.error(); - } auto aes_iv_mask = ParseAesHexBytes(value, "aesIvMask"); - if (!aes_iv_mask) { - return aes_iv_mask.error(); + if (!aes_key || !aes_iv_mask) { + return Error(Error::Code::kUnencryptedOffer, + "Offer stream must have both a valid aesKey and aesIvMask"); } auto rtp_timebase = ParseRtpTimebase(value, "timeBase"); if (!rtp_timebase) { diff --git a/chromium/third_party/openscreen/src/cast/streaming/offer_messages_unittest.cc b/chromium/third_party/openscreen/src/cast/streaming/offer_messages_unittest.cc index 3976d46b3e9..a2117f671c9 100644 --- a/chromium/third_party/openscreen/src/cast/streaming/offer_messages_unittest.cc +++ b/chromium/third_party/openscreen/src/cast/streaming/offer_messages_unittest.cc @@ -86,10 +86,16 @@ constexpr char kValidOffer[] = R"({ ] })"; -void ExpectFailureOnParse(absl::string_view body) { +void ExpectFailureOnParse( + absl::string_view body, + absl::optional<Error::Code> expected = absl::nullopt) { ErrorOr<Json::Value> root = json::Parse(body); ASSERT_TRUE(root.is_value()) << root.error(); - EXPECT_TRUE(Offer::Parse(std::move(root.value())).is_error()); + ErrorOr<Offer> error_or_offer = Offer::Parse(std::move(root.value())); + EXPECT_TRUE(error_or_offer.is_error()); + if (expected) { + EXPECT_EQ(expected, error_or_offer.error().code()); + } } void ExpectEqualsValidOffer(const Offer& offer) { @@ -183,8 +189,8 @@ TEST(OfferTest, ErrorOnEmptyOffer) { } TEST(OfferTest, ErrorOnMissingMandatoryFields) { - // It's okay if castMode is omitted, but if supportedStreams isanne // - // omitted we should fail here. + // It's okay if castMode is omitted, but if supportedStreams is omitted we + // should fail here. ExpectFailureOnParse(R"({ "castMode": "mirroring" })"); @@ -493,5 +499,96 @@ TEST(OfferTest, ToJsonFailsWithInvalidStreams) { EXPECT_TRUE(video_stream_invalid.ToJson().is_error()); } +TEST(OfferTest, FailsIfUnencrypted) { + // Video stream missing AES fields. + ExpectFailureOnParse(R"({ + "castMode": "mirroring", + "supportedStreams": [{ + "index": 2, + "type": "video_source", + "codecName": "vp8", + "rtpProfile": "cast", + "rtpPayloadType": 100, + "ssrc": 19088743, + "timeBase": "1/48000", + "resolutions": [], + "maxBitRate": 10000, + "aesIvMask": "7f12a19be62a36c04ae4116caaeff6d1" + }] + })", + Error::Code::kUnencryptedOffer); + + ExpectFailureOnParse(R"({ + "castMode": "mirroring", + "supportedStreams": [{ + "index": 2, + "type": "video_source", + "codecName": "vp8", + "rtpProfile": "cast", + "rtpPayloadType": 100, + "ssrc": 19088743, + "timeBase": "1/48000", + "resolutions": [], + "maxBitRate": 10000, + "aesKey": "51027e4e2347cbcb49d57ef10177aebc" + }] + })", + Error::Code::kUnencryptedOffer); + + // Audio stream missing AES fields. + ExpectFailureOnParse(R"({ + "castMode": "mirroring", + "supportedStreams": [{ + "index": 2, + "type": "audio_source", + "codecName": "opus", + "rtpProfile": "cast", + "rtpPayloadType": 96, + "ssrc": 19088743, + "bitRate": 124000, + "timeBase": "1/48000", + "channels": 2, + "aesIvMask": "7f12a19be62a36c04ae4116caaeff6d1" + }] + })", + Error::Code::kUnencryptedOffer); + + ExpectFailureOnParse(R"({ + "castMode": "mirroring", + "supportedStreams": [{ + "index": 2, + "type": "audio_source", + "codecName": "opus", + "rtpProfile": "cast", + "rtpPayloadType": 96, + "ssrc": 19088743, + "bitRate": 124000, + "timeBase": "1/48000", + "channels": 2, + "aesKey": "51027e4e2347cbcb49d57ef10177aebc" + }] + })", + Error::Code::kUnencryptedOffer); + + // And finally, fields provided but not properly formatted. + ExpectFailureOnParse(R"({ + "castMode": "mirroring", + "supportedStreams": [{ + "index": 2, + "type": "audio_source", + "codecName": "opus", + "rtpProfile": "cast", + "rtpPayloadType": 96, + "ssrc": 19088743, + "bitRate": 124000, + "timeBase": "1/48000", + "channels": 2, + "aesKey": "51027e4e2347$bcb49d57ef10177aebc", + "aesIvMask": "7f12a19be62a36c04ae4116caaeff6d1" + }] + })", + Error::Code::kUnencryptedOffer); +} + } // namespace cast } // namespace openscreen diff --git a/chromium/third_party/openscreen/src/cast/streaming/receiver_message.cc b/chromium/third_party/openscreen/src/cast/streaming/receiver_message.cc index e2739220184..1ce675b753c 100644 --- a/chromium/third_party/openscreen/src/cast/streaming/receiver_message.cc +++ b/chromium/third_party/openscreen/src/cast/streaming/receiver_message.cc @@ -27,6 +27,18 @@ EnumNameTable<ReceiverMessage::Type, 5> kMessageTypeNames{ {"CAPABILITIES_RESPONSE", ReceiverMessage::Type::kCapabilitiesResponse}, {"RPC", ReceiverMessage::Type::kRpc}}}; +EnumNameTable<MediaCapability, 9> kMediaCapabilityNames{{ + {"audio", MediaCapability::kAudio}, + {"aac", MediaCapability::kAac}, + {"opus", MediaCapability::kOpus}, + {"video", MediaCapability::kVideo}, + {"4k", MediaCapability::k4k}, + {"h264", MediaCapability::kH264}, + {"vp8", MediaCapability::kVp8}, + {"vp9", MediaCapability::kVp9}, + {"hevc", MediaCapability::kHevc}, +}}; + ReceiverMessage::Type GetMessageType(const Json::Value& root) { std::string type; if (!json::ParseAndValidateString(root[kMessageType], &type)) { @@ -39,6 +51,22 @@ ReceiverMessage::Type GetMessageType(const Json::Value& root) { return parsed.value(ReceiverMessage::Type::kUnknown); } +bool ParseAndValidateCapability(const Json::Value& value, + MediaCapability* out) { + std::string c; + if (!json::ParseAndValidateString(value, &c)) { + return false; + } + + const ErrorOr<MediaCapability> capability = GetEnum(kMediaCapabilityNames, c); + if (capability.is_error()) { + return false; + } + + *out = capability.value(); + return true; +} + } // namespace // static @@ -77,14 +105,14 @@ ErrorOr<ReceiverCapability> ReceiverCapability::Parse( remoting_version = ReceiverCapability::kRemotingVersionUnknown; } - std::vector<std::string> media_capabilities; - if (!json::ParseAndValidateStringArray(value["mediaCaps"], - &media_capabilities)) { + std::vector<MediaCapability> capabilities; + if (!json::ParseAndValidateArray<MediaCapability>( + value["mediaCaps"], ParseAndValidateCapability, &capabilities)) { return Error(Error::Code::kJsonParseError, "Failed to parse media capabilities"); } - return ReceiverCapability{remoting_version, std::move(media_capabilities)}; + return ReceiverCapability{remoting_version, std::move(capabilities)}; } Json::Value ReceiverCapability::ToJson() const { @@ -92,7 +120,7 @@ Json::Value ReceiverCapability::ToJson() const { root["remoting"] = remoting_version; Json::Value capabilities(Json::ValueType::arrayValue); for (const auto& capability : media_capabilities) { - capabilities.append(capability); + capabilities.append(GetEnumName(kMediaCapabilityNames, capability).value()); } root["mediaCaps"] = std::move(capabilities); return root; @@ -181,9 +209,10 @@ ErrorOr<ReceiverMessage> ReceiverMessage::Parse(const Json::Value& value) { } break; case Type::kRpc: { - std::string rpc; - if (json::ParseAndValidateString(value[kRpcMessageBody], &rpc) && - base64::Decode(rpc, &rpc)) { + std::string encoded_rpc; + std::vector<uint8_t> rpc; + if (json::ParseAndValidateString(value[kRpcMessageBody], &encoded_rpc) && + base64::Decode(encoded_rpc, &rpc)) { message.body = std::move(rpc); message.valid = true; } @@ -232,7 +261,8 @@ ErrorOr<Json::Value> ReceiverMessage::ToJson() const { // NOTE: RPC messages do NOT have a result field. case ReceiverMessage::Type::kRpc: - root[kRpcMessageBody] = base64::Encode(absl::get<std::string>(body)); + root[kRpcMessageBody] = + base64::Encode(absl::get<std::vector<uint8_t>>(body)); break; default: diff --git a/chromium/third_party/openscreen/src/cast/streaming/receiver_message.h b/chromium/third_party/openscreen/src/cast/streaming/receiver_message.h index 59aa9750c49..b231149a745 100644 --- a/chromium/third_party/openscreen/src/cast/streaming/receiver_message.h +++ b/chromium/third_party/openscreen/src/cast/streaming/receiver_message.h @@ -29,6 +29,18 @@ struct ReceiverWifiStatus { std::vector<int32_t> wifi_speed; }; +enum class MediaCapability { + kAudio, + kAac, + kOpus, + kVideo, + k4k, + kH264, + kVp8, + kVp9, + kHevc +}; + struct ReceiverCapability { static constexpr int kRemotingVersionUnknown = -1; @@ -39,7 +51,7 @@ struct ReceiverCapability { int remoting_version = kRemotingVersionUnknown; // Set of capabilities (e.g., ac3, 4k, hevc, vp9, dolby_vision, etc.). - std::vector<std::string> media_capabilities; + std::vector<MediaCapability> media_capabilities; }; struct ReceiverError { @@ -84,7 +96,7 @@ struct ReceiverMessage { absl::variant<absl::monostate, Answer, - std::string, + std::vector<uint8_t>, // Binary-encoded RPC message. ReceiverWifiStatus, ReceiverCapability, ReceiverError> diff --git a/chromium/third_party/openscreen/src/cast/streaming/remoting.proto b/chromium/third_party/openscreen/src/cast/streaming/remoting.proto index 0ce73012fba..bd00dd5ce6d 100644 --- a/chromium/third_party/openscreen/src/cast/streaming/remoting.proto +++ b/chromium/third_party/openscreen/src/cast/streaming/remoting.proto @@ -238,10 +238,15 @@ message VideoDecoderConfig { optional bytes extra_data = 9; } -message PipelineDecoderInfo { +message AudioDecoderInfo { reserved 3; - reserved "has_decrypting_demuxer_stream"; - optional string decoder_name = 1; + optional int64 decoder_type = 1; + optional bool is_platform_decoder = 2; +}; + +message VideoDecoderInfo { + reserved 3; + optional int64 decoder_type = 1; optional bool is_platform_decoder = 2; }; @@ -253,8 +258,8 @@ message PipelineStatistics { optional int64 audio_memory_usage = 5; optional int64 video_memory_usage = 6; optional int64 video_frame_duration_average_usec = 7; - optional PipelineDecoderInfo audio_decoder_info = 8; - optional PipelineDecoderInfo video_decoder_info = 9; + optional AudioDecoderInfo audio_decoder_info = 8; + optional VideoDecoderInfo video_decoder_info = 9; }; message AcquireDemuxer { diff --git a/chromium/third_party/openscreen/src/cast/streaming/remoting_capabilities.h b/chromium/third_party/openscreen/src/cast/streaming/remoting_capabilities.h new file mode 100644 index 00000000000..6957028ffc1 --- /dev/null +++ b/chromium/third_party/openscreen/src/cast/streaming/remoting_capabilities.h @@ -0,0 +1,60 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CAST_STREAMING_REMOTING_CAPABILITIES_H_ +#define CAST_STREAMING_REMOTING_CAPABILITIES_H_ + +#include <string> +#include <vector> + +namespace openscreen { +namespace cast { + +// Audio capabilities are how receivers indicate support for remoting codecs-- +// as remoting does not include the actual codec in the OFFER message. +enum class AudioCapability { + // The "baseline set" is used in Chrome to check support for a wide + // variety of audio codecs in media/remoting/renderer_controller.cc, including + // but not limited to MP3, PCM, Ogg Vorbis, and FLAC. + kBaselineSet, + kAac, + kOpus, +}; + +// Similar to audio capabilities, video capabilities are how the receiver +// indicates support for certain video codecs, as well as support for streaming +// 4k content. It is assumed by the sender that the receiver can support 4k +// on all supported codecs. +enum class VideoCapability { + // |kSupports4k| indicates that the receiver wants and can support 4k remoting + // content--both decoding/rendering and either a native 4k display or + // downscaling to the display's native resolution. + // TODO(issuetracker.google.com/184429130): |kSupports4k| is not super helpful + // for enabling 4k support, as receivers may not support 4k for all types of + // content. + kSupports4k, + kH264, + kVp8, + kVp9, + kHevc +}; + +// This class is similar to the RemotingSinkMetadata in Chrome, however +// it is focused around our needs and is not mojom-based. This contains +// a rough set of capabilities of the receiver to give the sender an idea of +// what features are suppported for remoting. +// TODO(issuetracker.google.com/184189100): this object should be expanded to +// allow more specific constraint tracking. +struct RemotingCapabilities { + // Receiver audio-specific capabilities. + std::vector<AudioCapability> audio; + + // Receiver video-specific capabilities. + std::vector<VideoCapability> video; +}; + +} // namespace cast +} // namespace openscreen + +#endif // CAST_STREAMING_REMOTING_CAPABILITIES_H_ diff --git a/chromium/third_party/openscreen/src/cast/streaming/rpc_broker.cc b/chromium/third_party/openscreen/src/cast/streaming/rpc_broker.cc index 6e79a4dd687..58379d79593 100644 --- a/chromium/third_party/openscreen/src/cast/streaming/rpc_broker.cc +++ b/chromium/third_party/openscreen/src/cast/streaming/rpc_broker.cc @@ -4,6 +4,8 @@ #include "cast/streaming/rpc_broker.h" +#include <memory> +#include <string> #include <utility> #include "util/osp_logging.h" @@ -35,6 +37,11 @@ std::ostream& operator<<(std::ostream& out, const RpcMessage& message) { } // namespace +constexpr RpcBroker::Handle RpcBroker::kInvalidHandle; +constexpr RpcBroker::Handle RpcBroker::kAcquireRendererHandle; +constexpr RpcBroker::Handle RpcBroker::kAcquireDemuxerHandle; +constexpr RpcBroker::Handle RpcBroker::kFirstHandle; + RpcBroker::RpcBroker(SendMessageCallback send_message_cb) : next_handle_(kFirstHandle), send_message_cb_(std::move(send_message_cb)) {} @@ -61,22 +68,29 @@ void RpcBroker::UnregisterMessageReceiverCallback(RpcBroker::Handle handle) { receive_callbacks_.erase_key(handle); } -void RpcBroker::ProcessMessageFromRemote(const RpcMessage& message) { - OSP_DVLOG << "received message: " << message; - const auto entry = receive_callbacks_.find(message.handle()); +void RpcBroker::ProcessMessageFromRemote(const uint8_t* message, + std::size_t message_len) { + auto rpc = std::make_unique<RpcMessage>(); + if (!rpc->ParseFromArray(message, message_len)) { + OSP_LOG_WARN << "Failed to parse RPC message from remote: " << message; + return; + } + OSP_DVLOG << "Received RPC message: " << *rpc; + + const auto entry = receive_callbacks_.find(rpc->handle()); if (entry == receive_callbacks_.end()) { - OSP_DVLOG << "unregistered handle: " << message.handle(); + OSP_DVLOG << "Dropping message due to unregistered handle: " + << rpc->handle(); return; } - entry->second(message); + entry->second(std::move(rpc)); } -void RpcBroker::SendMessageToRemote(const RpcMessage& message) { - OSP_DVLOG << "sending message message: " << message; - std::vector<uint8_t> serialized_message(message.ByteSizeLong()); - OSP_CHECK(message.SerializeToArray(serialized_message.data(), - serialized_message.size())); - send_message_cb_(std::move(serialized_message)); +void RpcBroker::SendMessageToRemote(const RpcMessage& rpc) { + OSP_DVLOG << "Sending RPC message: " << rpc; + std::vector<uint8_t> message(rpc.ByteSizeLong()); + rpc.SerializeToArray(message.data(), message.size()); + send_message_cb_(std::move(message)); } bool RpcBroker::IsRegisteredForTesting(RpcBroker::Handle handle) { diff --git a/chromium/third_party/openscreen/src/cast/streaming/rpc_broker.h b/chromium/third_party/openscreen/src/cast/streaming/rpc_broker.h index 596aba1387f..5865a86bf3a 100644 --- a/chromium/third_party/openscreen/src/cast/streaming/rpc_broker.h +++ b/chromium/third_party/openscreen/src/cast/streaming/rpc_broker.h @@ -5,6 +5,9 @@ #ifndef CAST_STREAMING_RPC_BROKER_H_ #define CAST_STREAMING_RPC_BROKER_H_ +#include <memory> +#include <string> +#include <utility> #include <vector> #include "cast/streaming/remoting.pb.h" @@ -28,7 +31,8 @@ namespace cast { class RpcBroker { public: using Handle = int; - using ReceiveMessageCallback = std::function<void(const RpcMessage&)>; + using ReceiveMessageCallback = + std::function<void(std::unique_ptr<RpcMessage>)>; using SendMessageCallback = std::function<void(std::vector<uint8_t>)>; explicit RpcBroker(SendMessageCallback send_message_cb); @@ -53,14 +57,23 @@ class RpcBroker { void UnregisterMessageReceiverCallback(Handle handle); // Distributes an incoming RPC message to the registered (if any) component. - void ProcessMessageFromRemote(const RpcMessage& message); + // The |serialized_message| should be already base64-decoded and ready for + // deserialization by protobuf. + void ProcessMessageFromRemote(const uint8_t* message, + std::size_t message_len); - // Executes the |send_message_cb_| using |message|. - void SendMessageToRemote(const RpcMessage& message); + // Executes the |send_message_cb_| using |rpc|. + void SendMessageToRemote(const RpcMessage& rpc); // Checks if the handle is registered for receiving messages. Test-only. bool IsRegisteredForTesting(Handle handle); + // Consumers of RPCBroker may set the send message callback post-hoc + // in order to simulate different scenarios. + void set_send_message_cb_for_testing(SendMessageCallback cb) { + send_message_cb_ = std::move(cb); + } + // Predefined invalid handle value for RPC message. static constexpr Handle kInvalidHandle = -1; diff --git a/chromium/third_party/openscreen/src/cast/streaming/rpc_broker_unittest.cc b/chromium/third_party/openscreen/src/cast/streaming/rpc_broker_unittest.cc index 5dacfbb2964..3f13f9eab51 100644 --- a/chromium/third_party/openscreen/src/cast/streaming/rpc_broker_unittest.cc +++ b/chromium/third_party/openscreen/src/cast/streaming/rpc_broker_unittest.cc @@ -5,6 +5,8 @@ #include "cast/streaming/rpc_broker.h" #include <memory> +#include <string> +#include <utility> #include <vector> #include "cast/streaming/remoting.pb.h" @@ -17,13 +19,12 @@ using testing::Return; namespace openscreen { namespace cast { - namespace { class FakeMessager { public: - void OnReceivedRpc(const RpcMessage& message) { - received_rpc_ = message; + void OnReceivedRpc(std::unique_ptr<RpcMessage> message) { + received_rpc_ = std::move(message); received_count_++; } @@ -33,7 +34,7 @@ class FakeMessager { } int received_count() const { return received_count_; } - const RpcMessage& received_rpc() const { return received_rpc_; } + const RpcMessage& received_rpc() const { return *received_rpc_; } int sent_count() const { return sent_count_; } const RpcMessage& sent_rpc() const { return sent_rpc_; } @@ -42,7 +43,7 @@ class FakeMessager { RpcBroker::Handle handle() { return handle_; } private: - RpcMessage received_rpc_; + std::unique_ptr<RpcMessage> received_rpc_; int received_count_ = 0; RpcMessage sent_rpc_; @@ -67,11 +68,18 @@ class RpcBrokerTest : public testing::Test { const auto handle = rpc_broker_->GetUniqueHandle(); fake_messager_->set_handle(handle); rpc_broker_->RegisterMessageReceiverCallback( - handle, [p = fake_messager_.get()](const RpcMessage& message) { - p->OnReceivedRpc(message); + handle, + [p = fake_messager_.get()](std::unique_ptr<RpcMessage> message) { + p->OnReceivedRpc(std::move(message)); }); } + void ProcessMessage(const RpcMessage& rpc) { + std::vector<uint8_t> message(rpc.ByteSizeLong()); + rpc.SerializeToArray(message.data(), message.size()); + rpc_broker_->ProcessMessageFromRemote(message.data(), message.size()); + } + std::unique_ptr<FakeMessager> fake_messager_; std::unique_ptr<RpcBroker> rpc_broker_; }; @@ -79,14 +87,14 @@ class RpcBrokerTest : public testing::Test { TEST_F(RpcBrokerTest, TestProcessMessageFromRemoteRegistered) { RpcMessage rpc; rpc.set_handle(fake_messager_->handle()); - rpc_broker_->ProcessMessageFromRemote(rpc); + ProcessMessage(rpc); ASSERT_EQ(1, fake_messager_->received_count()); } TEST_F(RpcBrokerTest, TestProcessMessageFromRemoteUnregistered) { RpcMessage rpc; rpc_broker_->UnregisterMessageReceiverCallback(fake_messager_->handle()); - rpc_broker_->ProcessMessageFromRemote(rpc); + ProcessMessage(rpc); ASSERT_EQ(0, fake_messager_->received_count()); } @@ -119,7 +127,7 @@ TEST_F(RpcBrokerTest, ProcessMessageWithRegisteredHandle) { sent_rpc.set_handle(fake_messager_->handle()); sent_rpc.set_proc(RpcMessage::RPC_R_SETVOLUME); sent_rpc.set_double_value(3.4); - rpc_broker_->ProcessMessageFromRemote(sent_rpc); + ProcessMessage(sent_rpc); // Checks if received message is identical to the one sent earlier. ASSERT_EQ(1, fake_messager_->received_count()); @@ -136,7 +144,7 @@ TEST_F(RpcBrokerTest, ProcessMessageWithUnregisteredHandle) { sent_rpc.set_handle(different_handle); sent_rpc.set_proc(RpcMessage::RPC_R_SETVOLUME); sent_rpc.set_double_value(4.5); - rpc_broker_->ProcessMessageFromRemote(sent_rpc); + ProcessMessage(sent_rpc); // We shouldn't have gotten the message since the handle is different. ASSERT_EQ(0, fake_messager_->received_count()); diff --git a/chromium/third_party/openscreen/src/cast/streaming/rtp_defines.cc b/chromium/third_party/openscreen/src/cast/streaming/rtp_defines.cc index d64773d5c34..e07b590f6d0 100644 --- a/chromium/third_party/openscreen/src/cast/streaming/rtp_defines.cc +++ b/chromium/third_party/openscreen/src/cast/streaming/rtp_defines.cc @@ -4,15 +4,53 @@ #include "cast/streaming/rtp_defines.h" +#include "util/osp_logging.h" + namespace openscreen { namespace cast { -RtpPayloadType GetPayloadType(AudioCodec codec) { - return RtpPayloadType::kAudioHackForAndroidTV; +RtpPayloadType GetPayloadType(AudioCodec codec, bool use_android_rtp_hack) { + if (use_android_rtp_hack) { + return codec == AudioCodec::kNotSpecified + ? RtpPayloadType::kAudioVarious + : RtpPayloadType::kAudioHackForAndroidTV; + } + + switch (codec) { + case AudioCodec::kAac: + return RtpPayloadType::kAudioAac; + case AudioCodec::kOpus: + return RtpPayloadType::kAudioOpus; + case AudioCodec::kNotSpecified: + return RtpPayloadType::kAudioVarious; + default: + OSP_NOTREACHED(); + } } -RtpPayloadType GetPayloadType(VideoCodec codec) { - return RtpPayloadType::kVideoHackForAndroidTV; +RtpPayloadType GetPayloadType(VideoCodec codec, bool use_android_rtp_hack) { + if (use_android_rtp_hack) { + return codec == VideoCodec::kNotSpecified + ? RtpPayloadType::kVideoVarious + : RtpPayloadType::kVideoHackForAndroidTV; + } + switch (codec) { + // VP8 and VP9 share the same payload type. + case VideoCodec::kVp9: + case VideoCodec::kVp8: + return RtpPayloadType::kVideoVp8; + + // H264 and HEVC/H265 share the same payload type. + case VideoCodec::kHevc: // fallthrough + case VideoCodec::kH264: + return RtpPayloadType::kVideoH264; + + case VideoCodec::kNotSpecified: + return RtpPayloadType::kVideoVarious; + + default: + OSP_NOTREACHED(); + } } bool IsRtpPayloadType(uint8_t raw_byte) { diff --git a/chromium/third_party/openscreen/src/cast/streaming/rtp_defines.h b/chromium/third_party/openscreen/src/cast/streaming/rtp_defines.h index 82c91c31a7d..1f9cc43fdce 100644 --- a/chromium/third_party/openscreen/src/cast/streaming/rtp_defines.h +++ b/chromium/third_party/openscreen/src/cast/streaming/rtp_defines.h @@ -98,20 +98,17 @@ enum class RtpPayloadType : uint8_t { // video to be 96; regardless of the codecs actually being used. This is // definitely out-of-spec, and inconsistent with the audio versus video range // of values, but must be taken into account for backwards-compatibility. - // TODO(crbug.com/1127978): RTP payload types need to represent actual type, - // as well as have options for new codecs like VP9. kAudioHackForAndroidTV = 127, kVideoHackForAndroidTV = 96, }; -// NOTE: currently we match the legacy Chrome sender's behavior of always -// sending the audio and video hacks for AndroidTV, however we should migrate -// to using proper rtp payload types. New payload types for new codecs, such -// as VP9, should also be defined. -// TODO(crbug.com/1127978): RTP payload types need to represent actual type, -// as well as have options for new codecs like VP9. -RtpPayloadType GetPayloadType(AudioCodec codec); -RtpPayloadType GetPayloadType(VideoCodec codec); +// Setting |use_android_rtp_hack| to true means that we match the legacy Chrome +// sender's behavior of always sending the audio and video hacks for AndroidTV, +// as some legacy android receivers require these. +// TODO(issuetracker.google.com/184438154): we need to figure out what receivers +// need this still, if any. The hack should be removed when possible. +RtpPayloadType GetPayloadType(AudioCodec codec, bool use_android_rtp_hack); +RtpPayloadType GetPayloadType(VideoCodec codec, bool use_android_rtp_hack); // Returns true if the |raw_byte| can be type-casted to a RtpPayloadType, and is // also not RtpPayloadType::kNull. The caller should mask the byte, to select diff --git a/chromium/third_party/openscreen/src/cast/streaming/sender_message.cc b/chromium/third_party/openscreen/src/cast/streaming/sender_message.cc index 9c2b538875f..2983aa2f480 100644 --- a/chromium/third_party/openscreen/src/cast/streaming/sender_message.cc +++ b/chromium/third_party/openscreen/src/cast/streaming/sender_message.cc @@ -59,9 +59,10 @@ ErrorOr<SenderMessage> SenderMessage::Parse(const Json::Value& value) { } } else if (message.type == SenderMessage::Type::kRpc) { std::string rpc_body; + std::vector<uint8_t> rpc; if (json::ParseAndValidateString(value[kRpcMessageBody], &rpc_body) && - base64::Decode(rpc_body, &rpc_body)) { - message.body = rpc_body; + base64::Decode(rpc_body, &rpc)) { + message.body = rpc; message.valid = true; } } else if (message.type == SenderMessage::Type::kGetStatus || @@ -90,7 +91,8 @@ ErrorOr<Json::Value> SenderMessage::ToJson() const { break; case SenderMessage::Type::kRpc: - root[kRpcMessageBody] = base64::Encode(absl::get<std::string>(body)); + root[kRpcMessageBody] = + base64::Encode(absl::get<std::vector<uint8_t>>(body)); break; case SenderMessage::Type::kGetCapabilities: // fallthrough diff --git a/chromium/third_party/openscreen/src/cast/streaming/sender_message.h b/chromium/third_party/openscreen/src/cast/streaming/sender_message.h index c016b31c515..2c45032c66d 100644 --- a/chromium/third_party/openscreen/src/cast/streaming/sender_message.h +++ b/chromium/third_party/openscreen/src/cast/streaming/sender_message.h @@ -44,7 +44,11 @@ struct SenderMessage { Type type = Type::kUnknown; int32_t sequence_number = -1; bool valid = false; - absl::variant<absl::monostate, Offer, std::string> body; + absl::variant<absl::monostate, + std::vector<uint8_t>, // Binary-encoded RPC message. + Offer, + std::string> + body; }; } // namespace cast diff --git a/chromium/third_party/openscreen/src/cast/streaming/sender_packet_router.h b/chromium/third_party/openscreen/src/cast/streaming/sender_packet_router.h index a6c161a91f8..c86cb2a5e6e 100644 --- a/chromium/third_party/openscreen/src/cast/streaming/sender_packet_router.h +++ b/chromium/third_party/openscreen/src/cast/streaming/sender_packet_router.h @@ -146,7 +146,7 @@ class SenderPacketRouter : public BandwidthEstimator, // a burst-send. void ScheduleNextBurst(); - // Performs a burst-send of packets. This is called whevener the Alarm fires. + // Performs a burst-send of packets. This is called whenever the Alarm fires. void SendBurstOfPackets(); // Send an RTCP packet from each Sender that has one ready, and return the diff --git a/chromium/third_party/openscreen/src/cast/streaming/sender_session.cc b/chromium/third_party/openscreen/src/cast/streaming/sender_session.cc index 7a54c7e1ffd..47782412a55 100644 --- a/chromium/third_party/openscreen/src/cast/streaming/sender_session.cc +++ b/chromium/third_party/openscreen/src/cast/streaming/sender_session.cc @@ -24,18 +24,23 @@ #include "util/json/json_helpers.h" #include "util/json/json_serialization.h" #include "util/osp_logging.h" +#include "util/stringprintf.h" namespace openscreen { namespace cast { namespace { -AudioStream CreateStream(int index, const AudioCaptureConfig& config) { +constexpr int kSupportedRemotingVersion = 3; + +AudioStream CreateStream(int index, + const AudioCaptureConfig& config, + bool use_android_rtp_hack) { return AudioStream{ Stream{index, Stream::Type::kAudioSource, config.channels, - GetPayloadType(config.codec), + GetPayloadType(config.codec, use_android_rtp_hack), GenerateSsrc(true /*high_priority*/), config.target_playout_delay, GenerateRandomBytes16(), @@ -53,7 +58,9 @@ Resolution ToResolution(const DisplayResolution& display_resolution) { return Resolution{display_resolution.width, display_resolution.height}; } -VideoStream CreateStream(int index, const VideoCaptureConfig& config) { +VideoStream CreateStream(int index, + const VideoCaptureConfig& config, + bool use_android_rtp_hack) { std::vector<Resolution> resolutions; std::transform(config.resolutions.begin(), config.resolutions.end(), std::back_inserter(resolutions), ToResolution); @@ -63,7 +70,7 @@ VideoStream CreateStream(int index, const VideoCaptureConfig& config) { Stream{index, Stream::Type::kVideoSource, kVideoStreamChannelCount, - GetPayloadType(config.codec), + GetPayloadType(config.codec, use_android_rtp_hack), GenerateSsrc(false /*high_priority*/), config.target_playout_delay, GenerateRandomBytes16(), @@ -89,21 +96,50 @@ VideoStream CreateStream(int index, const VideoCaptureConfig& config) { template <typename S, typename C> void CreateStreamList(int offset_index, const std::vector<C>& configs, + bool use_android_rtp_hack, std::vector<S>* out) { out->reserve(configs.size()); for (size_t i = 0; i < configs.size(); ++i) { - out->emplace_back(CreateStream(i + offset_index, configs[i])); + out->emplace_back( + CreateStream(i + offset_index, configs[i], use_android_rtp_hack)); } } -Offer CreateOffer(const std::vector<AudioCaptureConfig>& audio_configs, - const std::vector<VideoCaptureConfig>& video_configs) { +Offer CreateMirroringOffer(const std::vector<AudioCaptureConfig>& audio_configs, + const std::vector<VideoCaptureConfig>& video_configs, + bool use_android_rtp_hack) { Offer offer; + offer.cast_mode = CastMode::kMirroring; // NOTE here: IDs will always follow the pattern: // [0.. audio streams... N - 1][N.. video streams.. K] - CreateStreamList(0, audio_configs, &offer.audio_streams); - CreateStreamList(audio_configs.size(), video_configs, &offer.video_streams); + CreateStreamList(0, audio_configs, use_android_rtp_hack, + &offer.audio_streams); + CreateStreamList(audio_configs.size(), video_configs, use_android_rtp_hack, + &offer.video_streams); + + return offer; +} + +Offer CreateRemotingOffer(const AudioCaptureConfig& audio_config, + const VideoCaptureConfig& video_config, + bool use_android_rtp_hack) { + Offer offer; + offer.cast_mode = CastMode::kRemoting; + + AudioStream audio_stream = + CreateStream(0, audio_config, use_android_rtp_hack); + audio_stream.codec = AudioCodec::kNotSpecified; + audio_stream.stream.rtp_payload_type = + GetPayloadType(AudioCodec::kNotSpecified, use_android_rtp_hack); + offer.audio_streams.push_back(std::move(audio_stream)); + + VideoStream video_stream = + CreateStream(1, video_config, use_android_rtp_hack); + video_stream.codec = VideoCodec::kNotSpecified; + video_stream.stream.rtp_payload_type = + GetPayloadType(VideoCodec::kNotSpecified, use_android_rtp_hack); + offer.video_streams.push_back(std::move(video_stream)); return offer; } @@ -136,31 +172,71 @@ bool AreAllValid(const std::vector<AudioCaptureConfig>& audio_configs, IsValidVideoCaptureConfig); } +RemotingCapabilities ToCapabilities(const ReceiverCapability& capability) { + RemotingCapabilities out; + for (MediaCapability c : capability.media_capabilities) { + switch (c) { + case MediaCapability::kAudio: + out.audio.push_back(AudioCapability::kBaselineSet); + break; + case MediaCapability::kAac: + out.audio.push_back(AudioCapability::kAac); + break; + case MediaCapability::kOpus: + out.audio.push_back(AudioCapability::kOpus); + break; + case MediaCapability::k4k: + out.video.push_back(VideoCapability::kSupports4k); + break; + case MediaCapability::kH264: + out.video.push_back(VideoCapability::kH264); + break; + case MediaCapability::kVp8: + out.video.push_back(VideoCapability::kVp8); + break; + case MediaCapability::kVp9: + out.video.push_back(VideoCapability::kVp9); + break; + case MediaCapability::kHevc: + out.video.push_back(VideoCapability::kHevc); + break; + case MediaCapability::kVideo: + // noop, as "video" is ignored by Chrome remoting. + break; + + default: + OSP_NOTREACHED(); + } + } + return out; +} + } // namespace SenderSession::Client::~Client() = default; -SenderSession::SenderSession(IPAddress remote_address, - Client* const client, - Environment* environment, - MessagePort* message_port, - std::string message_source_id, - std::string message_destination_id) - : remote_address_(remote_address), - client_(client), - environment_(environment), +SenderSession::SenderSession(Configuration config) + : config_(config), messager_( - message_port, - std::move(message_source_id), - std::move(message_destination_id), + config_.message_port, + config_.message_source_id, + config_.message_destination_id, [this](Error error) { OSP_DLOG_WARN << "SenderSession message port error: " << error; - client_->OnError(this, error); + config_.client->OnError(this, error); }, - environment->task_runner()), - packet_router_(environment_) { - OSP_DCHECK(client_); - OSP_DCHECK(environment_); + config_.environment->task_runner()), + packet_router_(config_.environment) { + OSP_DCHECK(config_.client); + OSP_DCHECK(config_.environment); + + // We may or may not do remoting this session, however our RPC handler + // is not negotiation-specific and registering on construction here allows us + // to record any unexpected RPC messages. + messager_.SetHandler(ReceiverMessage::Type::kRpc, + [this](ReceiverMessage message) { + this->OnRpcMessage(std::move(message)); + }); } SenderSession::~SenderSession() = default; @@ -176,9 +252,45 @@ Error SenderSession::Negotiate(std::vector<AudioCaptureConfig> audio_configs, return Error(Error::Code::kParameterInvalid, "Invalid configs provided."); } - Offer offer = CreateOffer(audio_configs, video_configs); - current_negotiation_ = std::unique_ptr<Negotiation>(new Negotiation{ - offer, std::move(audio_configs), std::move(video_configs)}); + Offer offer = CreateMirroringOffer(audio_configs, video_configs, + config_.use_android_rtp_hack); + return StartNegotiation(std::move(audio_configs), std::move(video_configs), + std::move(offer)); +} + +Error SenderSession::NegotiateRemoting(AudioCaptureConfig audio_config, + VideoCaptureConfig video_config) { + // Remoting requires both an audio and a video configuration. + if (!IsValidAudioCaptureConfig(audio_config) || + !IsValidVideoCaptureConfig(video_config)) { + return Error(Error::Code::kParameterInvalid, + "Passed invalid audio or video config."); + } + + Offer offer = CreateRemotingOffer(audio_config, video_config, + config_.use_android_rtp_hack); + return StartNegotiation({audio_config}, {video_config}, std::move(offer)); +} + +int SenderSession::GetEstimatedNetworkBandwidth() const { + return packet_router_.ComputeNetworkBandwidth(); +} + +void SenderSession::ResetState() { + state_ = State::kIdle; + current_negotiation_.reset(); + current_audio_sender_.reset(); + current_video_sender_.reset(); + broker_.reset(); +} + +Error SenderSession::StartNegotiation( + std::vector<AudioCaptureConfig> audio_configs, + std::vector<VideoCaptureConfig> video_configs, + Offer offer) { + current_negotiation_ = + std::unique_ptr<InProcessNegotiation>(new InProcessNegotiation{ + offer, std::move(audio_configs), std::move(video_configs)}); return messager_.SendRequest( SenderMessage{SenderMessage::Type::kOffer, ++current_sequence_number_, @@ -188,28 +300,128 @@ Error SenderSession::Negotiate(std::vector<AudioCaptureConfig> audio_configs, } void SenderSession::OnAnswer(ReceiverMessage message) { - OSP_LOG_WARN << "Message sn: " << message.sequence_number - << ", current: " << current_sequence_number_; if (!message.valid) { - if (absl::holds_alternative<ReceiverError>(message.body)) { - client_->OnError( - this, Error(Error::Code::kParameterInvalid, - absl::get<ReceiverError>(message.body).description)); - } else { - client_->OnError(this, Error(Error::Code::kJsonParseError, - "Received invalid answer message")); - } + HandleErrorMessage(message, "Invalid answer response message"); return; } + // There isn't an obvious way to tell from the Answer whether it is mirroring + // or remoting specific--the only clues are in the original offer message. const Answer& answer = absl::get<Answer>(message.body); - ConfiguredSenders senders = SpawnSenders(answer); + if (current_negotiation_->offer.cast_mode == CastMode::kMirroring) { + ConfiguredSenders senders = SpawnSenders(answer); + // If we didn't select any senders, the negotiation was unsuccessful. + if (senders.audio_sender == nullptr && senders.video_sender == nullptr) { + return; + } + + state_ = State::kMirroring; + config_.client->OnNegotiated( + this, std::move(senders), + capture_recommendations::GetRecommendations(answer)); + } else { + state_ = State::kRemoting; + + // We don't want to spawn senders yet, since we don't know what the + // receiver's capabilities are. So, we cache the Answer until the + // capabilites request is completed. + current_negotiation_->answer = answer; + const Error result = messager_.SendRequest( + SenderMessage{SenderMessage::Type::kGetCapabilities, + ++current_sequence_number_, true}, + ReceiverMessage::Type::kCapabilitiesResponse, + [this](ReceiverMessage message) { OnCapabilitiesResponse(message); }); + if (!result.ok()) { + config_.client->OnError( + this, Error(Error::Code::kNegotiationFailure, + "Failed to set a GET_CAPABILITIES request")); + } + } +} + +void SenderSession::OnCapabilitiesResponse(ReceiverMessage message) { + if (!current_negotiation_ || !current_negotiation_->answer.IsValid()) { + OSP_LOG_INFO + << "Received a capabilities response, but not negotiating anything."; + return; + } + + if (!message.valid) { + HandleErrorMessage( + message, + "Bad CAPABILITIES_RESPONSE, assuming remoting is not supported"); + return; + } + + const ReceiverCapability& caps = absl::get<ReceiverCapability>(message.body); + int remoting_version = caps.remoting_version; + // If not set, we assume it is version 1. + if (remoting_version == ReceiverCapability::kRemotingVersionUnknown) { + remoting_version = 1; + } + + if (remoting_version > kSupportedRemotingVersion) { + std::string message = StringPrintf( + "Receiver is using too new of a version for remoting (%d > %d)", + remoting_version, kSupportedRemotingVersion); + config_.client->OnError( + this, Error(Error::Code::kRemotingNotSupported, std::move(message))); + return; + } + + ConfiguredSenders senders = SpawnSenders(current_negotiation_->answer); // If we didn't select any senders, the negotiation was unsuccessful. if (senders.audio_sender == nullptr && senders.video_sender == nullptr) { + config_.client->OnError(this, + Error(Error::Code::kNegotiationFailure, + "Failed to negotiate a remoting session.")); return; } - client_->OnNegotiated(this, std::move(senders), - capture_recommendations::GetRecommendations(answer)); + broker_ = std::make_unique<RpcBroker>([this](std::vector<uint8_t> message) { + Error error = this->messager_.SendOutboundMessage(SenderMessage{ + SenderMessage::Type::kRpc, ++(this->current_sequence_number_), true, + std::move(message)}); + + if (!error.ok()) { + OSP_LOG_WARN << "Failed to send RPC message: " << error; + } + }); + + config_.client->OnRemotingNegotiated( + this, RemotingNegotiation{std::move(senders), ToCapabilities(caps), + broker_.get()}); +} + +void SenderSession::OnRpcMessage(ReceiverMessage message) { + if (!broker_) { + OSP_LOG_INFO << "Received an RPC message without having an RPCBroker."; + return; + } + + if (!message.valid) { + HandleErrorMessage( + message, + "Bad RPC message. This may or may not represent a serious problem"); + return; + } + + const auto& body = absl::get<std::vector<uint8_t>>(message.body); + broker_->ProcessMessageFromRemote(body.data(), body.size()); +} + +void SenderSession::HandleErrorMessage(ReceiverMessage message, + const std::string& text) { + OSP_DCHECK(!message.valid); + if (absl::holds_alternative<ReceiverError>(message.body)) { + const ReceiverError& error = absl::get<ReceiverError>(message.body); + std::string error_text = + StringPrintf("%s. Error code: %d, description: %s", text.c_str(), + error.code, error.description.c_str()); + config_.client->OnError( + this, Error(Error::Code::kParameterInvalid, std::move(error_text))); + } else { + config_.client->OnError(this, Error(Error::Code::kJsonParseError, text)); + } } std::unique_ptr<Sender> SenderSession::CreateSender(Ssrc receiver_ssrc, @@ -225,7 +437,7 @@ std::unique_ptr<Sender> SenderSession::CreateSender(Ssrc receiver_ssrc, stream.aes_iv_mask, /* is_pli_enabled*/ true}; - return std::make_unique<Sender>(environment_, &packet_router_, + return std::make_unique<Sender>(config_.environment, &packet_router_, std::move(config), type); } @@ -235,7 +447,8 @@ void SenderSession::SpawnAudioSender(ConfiguredSenders* senders, int config_index) { const AudioCaptureConfig& config = current_negotiation_->audio_configs[config_index]; - const RtpPayloadType payload_type = GetPayloadType(config.codec); + const RtpPayloadType payload_type = + GetPayloadType(config.codec, config_.use_android_rtp_hack); for (const AudioStream& stream : current_negotiation_->offer.audio_streams) { if (stream.stream.index == send_index) { current_audio_sender_ = @@ -253,7 +466,8 @@ void SenderSession::SpawnVideoSender(ConfiguredSenders* senders, int config_index) { const VideoCaptureConfig& config = current_negotiation_->video_configs[config_index]; - const RtpPayloadType payload_type = GetPayloadType(config.codec); + const RtpPayloadType payload_type = + GetPayloadType(config.codec, config_.use_android_rtp_hack); for (const VideoStream& stream : current_negotiation_->offer.video_streams) { if (stream.stream.index == send_index) { current_video_sender_ = @@ -272,8 +486,10 @@ SenderSession::ConfiguredSenders SenderSession::SpawnSenders( // Although we already have a message port set up with the TLS // address of the receiver, we don't know where to send the separate UDP // stream until we get the ANSWER message here. - environment_->set_remote_endpoint( - IPEndpoint{remote_address_, static_cast<uint16_t>(answer.udp_port)}); + config_.environment->set_remote_endpoint(IPEndpoint{ + config_.remote_address, static_cast<uint16_t>(answer.udp_port)}); + OSP_LOG_INFO << "Streaming to " << config_.environment->remote_endpoint() + << "..."; ConfiguredSenders senders; for (size_t i = 0; i < answer.send_indexes.size(); ++i) { diff --git a/chromium/third_party/openscreen/src/cast/streaming/sender_session.h b/chromium/third_party/openscreen/src/cast/streaming/sender_session.h index 86ab4d5a649..5865668a31d 100644 --- a/chromium/third_party/openscreen/src/cast/streaming/sender_session.h +++ b/chromium/third_party/openscreen/src/cast/streaming/sender_session.h @@ -13,7 +13,10 @@ #include "cast/common/public/message_port.h" #include "cast/streaming/answer_messages.h" #include "cast/streaming/capture_configs.h" +#include "cast/streaming/capture_recommendations.h" #include "cast/streaming/offer_messages.h" +#include "cast/streaming/remoting_capabilities.h" +#include "cast/streaming/rpc_broker.h" #include "cast/streaming/sender.h" #include "cast/streaming/sender_packet_router.h" #include "cast/streaming/session_config.h" @@ -24,10 +27,6 @@ namespace openscreen { namespace cast { -namespace capture_recommendations { -struct Recommendations; -} - class Environment; class Sender; @@ -49,8 +48,28 @@ class SenderSession final { VideoCaptureConfig video_config; }; - // The embedder should provide a client for handling the negotiation. - // When the negotiation is complete, the OnNegotiated callback is called. + // This struct contains all of the information necessary to begin remoting + // after we receive the capabilities from the receiver. + // TODO(issuetracker.google.com/184189241): capture recommendations should be + // exposed as part of the remoting negotiation. + struct RemotingNegotiation { + ConfiguredSenders senders; + + // The capabilities reported by the connected receiver. NOTE: SenderSession + // reports the capabilities as-is from the Receiver, so clients concerned + // about legacy devices, such as pre-1.27 Earth receivers should do + // a version check when using these capabilities to offer remoting. + RemotingCapabilities capabilities; + + // The RPC broker to be used for subscribing to remoting proto messages. + RpcBroker* broker; + }; + + // The embedder should provide a client for handling negotiation events. + // The client is required to implement a mirorring handler, and may choose + // to provide a remoting negotiation if it supports remoting. + // When the negotiation is complete, the appropriate |On*Negotiated| handler + // is called. class Client { public: // Called when a new set of senders has been negotiated. This may be @@ -63,6 +82,12 @@ class SenderSession final { ConfiguredSenders senders, capture_recommendations::Recommendations capture_recommendations) = 0; + // Called when a new set of remoting senders has been negotiated. Since + // remoting is an optional feature, the default behavior here is to leave + // this method unhandled. + virtual void OnRemotingNegotiated(const SenderSession* session, + RemotingNegotiation negotiation) {} + // Called whenever an error occurs. Ends the ongoing session, and the caller // must call Negotiate() again if they wish to re-establish streaming. virtual void OnError(const SenderSession* session, Error error) = 0; @@ -71,6 +96,35 @@ class SenderSession final { virtual ~Client(); }; + // The configuration information required to set up the session. + struct Configuration { + // The remote address of the receiver to connect to. NOTE: we do eventually + // set the remote endpoint on the |environment| object, but only after + // getting the port information from a successful ANSWER message. + IPAddress remote_address; + + // The client for notifying of successful negotiations and errors. Required. + Client* const client; + + // The cast environment used to access operating system resources, such + // as the UDP socket for RTP/RTCP messaging. Required. + Environment* environment; + + // The message port used to send streaming control protocol messages. + MessagePort* message_port; + + // The message source identifier (e.g. this sender). + std::string message_source_id; + + // The message destination identifier (e.g. the receiver we are connected + // to). + std::string message_destination_id; + + // Whether or not the android RTP value hack should be used (for legacy + // android devices). + bool use_android_rtp_hack = true; + }; + // The SenderSession assumes that the passed in client, environment, and // message port persist for at least the lifetime of the SenderSession. If // one of these classes needs to be reset, a new SenderSession should be @@ -80,37 +134,80 @@ class SenderSession final { // ID, respectively, to use when sending or receiving control messages (e.g., // OFFERs or ANSWERs) over the |message_port|. |message_port|'s SetClient() // method will be called. - SenderSession(IPAddress remote_address, - Client* const client, - Environment* environment, - MessagePort* message_port, - std::string message_source_id, - std::string message_destination_id); + explicit SenderSession(Configuration config); SenderSession(const SenderSession&) = delete; SenderSession(SenderSession&&) noexcept = delete; SenderSession& operator=(const SenderSession&) = delete; SenderSession& operator=(SenderSession&&) = delete; ~SenderSession(); - // Starts an OFFER/ANSWER exchange with the already configured receiver - // over the message port. The caller should assume any configured senders - // become invalid when calling this method. + // Starts a mirroring OFFER/ANSWER exchange with the already configured + // receiver over the message port. The caller should assume any configured + // senders become invalid when calling this method. Error Negotiate(std::vector<AudioCaptureConfig> audio_configs, std::vector<VideoCaptureConfig> video_configs); + // Remoting negotiation is actually very similar to mirroring negotiation-- + // an OFFER/ANSWER exchange still occurs, however only one audio and video + // codec should be presented based on the encoding of the media element that + // should be remoted. Note: the codec fields in |audio_config| and + // |video_config| are ignored in favor of |kRemote|. + Error NegotiateRemoting(AudioCaptureConfig audio_config, + VideoCaptureConfig video_config); + + // Get the current network usage (in bits per second). This includes all + // senders managed by this session, and is a best guess based on receiver + // feedback. Embedders may use this information to throttle capture devices. + int GetEstimatedNetworkBandwidth() const; + private: // We store the current negotiation, so that when we get an answer from the // receiver we can line up the selected streams with the original // configuration. - struct Negotiation { + struct InProcessNegotiation { + // The offer, which should always be valid if we have an in process + // negotiation. Offer offer; + // The configs used to derive the offer. std::vector<AudioCaptureConfig> audio_configs; std::vector<VideoCaptureConfig> video_configs; + + // The answer message for this negotiation, which may be invalid if we + // haven't received an answer yet. + Answer answer; + }; + + // The state of the session. + enum class State { + // Not sending content--may be in the middle of negotiation, or just + // waiting. + kIdle, + + // Currently mirroring content to a receiver. + kMirroring, + + // Currently remoting content to a receiver. + kRemoting }; + // Reset the state and tear down the current negotiation/negotiated mirroring + // or remoting session. After reset, the SenderSession is still connected to + // the same |remote_address_|, and the |packet_router_| and sequence number + // will be unchanged. + void ResetState(); + + // Uses the passed in configs and offer to send an OFFER/ANSWER negotiation + // and cache the new InProcessNavigation. + Error StartNegotiation(std::vector<AudioCaptureConfig> audio_configs, + std::vector<VideoCaptureConfig> video_configs, + Offer offer); + // Specific message type handler methods. void OnAnswer(ReceiverMessage message); + void OnCapabilitiesResponse(ReceiverMessage message); + void OnRpcMessage(ReceiverMessage message); + void HandleErrorMessage(ReceiverMessage message, const std::string& text); // Used by SpawnSenders to generate a sender for a specific stream. std::unique_ptr<Sender> CreateSender(Ssrc receiver_ssrc, @@ -130,18 +227,15 @@ class SenderSession final { // Spawn a set of configured senders from the currently stored negotiation. ConfiguredSenders SpawnSenders(const Answer& answer); - // The remote address of the receiver we are communicating with. Used - // for both TLS and UDP traffic. - const IPAddress remote_address_; + // This session's configuration. + Configuration config_; - // The embedder is expected to provide us a client for notifications about - // negotiations and errors, a valid cast environment, and a messaging - // port for communicating to the Receiver over TLS. - Client* const client_; - Environment* const environment_; + // The session messager, which uses the message port for sending control + // messages. For message formats, see + // cast/protocol/castv2/streaming_schema.json. SenderSessionMessager messager_; - // The packet router used for messaging across all senders. + // The packet router used for RTP/RTCP messaging across all senders. SenderPacketRouter packet_router_; // Each negotiation has its own sequence number, and the receiver replies @@ -151,12 +245,22 @@ class SenderSession final { // The current negotiation. If present, we are expected an ANSWER from // the receiver. If not present, any provided ANSWERS are rejected. - std::unique_ptr<Negotiation> current_negotiation_; + std::unique_ptr<InProcessNegotiation> current_negotiation_; + + // The current state of the session. Note that the state is intentionally + // limited. |kMirroring| or |kRemoting| means that we are either starting + // a negotiation or actively sending to a receiver. + State state_ = State::kIdle; // If the negotiation has succeeded, we store the current audio and video // senders used for this session. Either or both may be nullptr. std::unique_ptr<Sender> current_audio_sender_; std::unique_ptr<Sender> current_video_sender_; + + // If remoting, we store the RpcBroker used by the embedder to send RPC + // messages from the remoting protobuf specification. For more information, + // see //cast/streaming/remoting.proto. + std::unique_ptr<RpcBroker> broker_; }; // namespace cast } // namespace cast diff --git a/chromium/third_party/openscreen/src/cast/streaming/sender_session_unittest.cc b/chromium/third_party/openscreen/src/cast/streaming/sender_session_unittest.cc index 2ea2073b1cb..f8d4f24ed94 100644 --- a/chromium/third_party/openscreen/src/cast/streaming/sender_session_unittest.cc +++ b/chromium/third_party/openscreen/src/cast/streaming/sender_session_unittest.cc @@ -96,6 +96,15 @@ constexpr char kErrorAnswerMessage[] = R"({ } })"; +constexpr char kCapabilitiesResponse[] = R"({ + "seqNum": 2, + "result": "ok", + "type": "CAPABILITIES_RESPONSE", + "capabilities": { + "mediaCaps": ["video", "vp8", "audio", "aac"] + } +})"; + const AudioCaptureConfig kAudioCaptureConfigInvalidChannels{ AudioCodec::kAac, -1 /* channels */, 44000 /* bit_rate */, 96000 /* sample_rate */ @@ -132,6 +141,10 @@ class FakeClient : public SenderSession::Client { SenderSession::ConfiguredSenders, capture_recommendations::Recommendations), (override)); + MOCK_METHOD(void, + OnRemotingNegotiated, + (const SenderSession*, SenderSession::RemotingNegotiation), + (override)); MOCK_METHOD(void, OnError, (const SenderSession*, Error error), (override)); }; @@ -153,19 +166,32 @@ class SenderSessionTest : public ::testing::Test { void SetUp() { message_port_ = std::make_unique<SimpleMessagePort>("receiver-12345"); environment_ = MakeEnvironment(); - session_ = std::make_unique<SenderSession>( - IPAddress::kV4LoopbackAddress(), &client_, environment_.get(), - message_port_.get(), "sender-12345", "receiver-12345"); + + SenderSession::Configuration config{IPAddress::kV4LoopbackAddress(), + &client_, + environment_.get(), + message_port_.get(), + "sender-12345", + "receiver-12345", + /* use_android_rtp_hack */ true}; + session_ = std::make_unique<SenderSession>(std::move(config)); } - std::string NegotiateOfferAndConstructAnswer() { + void NegotiateMirroringWithValidConfigs() { const Error error = session_->Negotiate( std::vector<AudioCaptureConfig>{kAudioCaptureConfigValid}, std::vector<VideoCaptureConfig>{kVideoCaptureConfigValid}); - if (!error.ok()) { - return {}; - } + ASSERT_TRUE(error.ok()); + } + + void NegotiateRemotingWithValidConfigs() { + const Error error = session_->NegotiateRemoting(kAudioCaptureConfigValid, + kVideoCaptureConfigValid); + ASSERT_TRUE(error.ok()); + } + // Answers require specific fields from the original offer to be valid. + std::string ConstructAnswerFromOffer(CastMode mode) { const auto& messages = message_port_->posted_messages(); if (messages.size() != 1) { return {}; @@ -200,14 +226,16 @@ class SenderSessionTest : public ::testing::Test { "seqNum": %d, "result": "ok", "answer": { - "castMode": "mirroring", + "castMode": "%s", "udpPort": 1234, "sendIndexes": [%d, %d], "ssrcs": [%d, %d] } })"; - return StringPrintf(kAnswerTemplate, offer["seqNum"].asInt(), audio_index, - video_index, audio_ssrc + 1, video_ssrc + 1); + return StringPrintf(kAnswerTemplate, offer["seqNum"].asInt(), + mode == CastMode::kMirroring ? "mirroring" : "remoting", + audio_index, video_index, audio_ssrc + 1, + video_ssrc + 1); } protected: @@ -344,14 +372,16 @@ TEST_F(SenderSessionTest, SendsOfferMessage) { } TEST_F(SenderSessionTest, HandlesValidAnswer) { - std::string answer = NegotiateOfferAndConstructAnswer(); + NegotiateMirroringWithValidConfigs(); + std::string answer = ConstructAnswerFromOffer(CastMode::kMirroring); EXPECT_CALL(client_, OnNegotiated(session_.get(), _, _)); message_port_->ReceiveMessage(answer); } TEST_F(SenderSessionTest, HandlesInvalidNamespace) { - std::string answer = NegotiateOfferAndConstructAnswer(); + NegotiateMirroringWithValidConfigs(); + std::string answer = ConstructAnswerFromOffer(CastMode::kMirroring); message_port_->ReceiveMessage("random-namespace", answer); } @@ -454,5 +484,66 @@ TEST_F(SenderSessionTest, DoesNotCrashOnMessagePortError) { message_port_->ReceiveError(Error(Error::Code::kUnknownError)); } +TEST_F(SenderSessionTest, ReportsZeroBandwidthWhenNoPacketsSent) { + // TODO(issuetracker.google.com/183996645): As part of end to end testing, + // we need to ensure that we are providing reasonable network bandwidth + // measurements. + EXPECT_EQ(0, session_->GetEstimatedNetworkBandwidth()); +} + +TEST_F(SenderSessionTest, ComplainsIfInvalidAudioCaptureConfigRemoting) { + const Error error = session_->NegotiateRemoting( + kAudioCaptureConfigInvalidChannels, kVideoCaptureConfigValid); + + EXPECT_EQ(error.code(), Error::Code::kParameterInvalid); +} + +TEST_F(SenderSessionTest, ComplainsIfInvalidVideoCaptureConfigRemoting) { + const Error error = session_->NegotiateRemoting(kAudioCaptureConfigValid, + kVideoCaptureConfigInvalid); + EXPECT_EQ(error.code(), Error::Code::kParameterInvalid); +} + +TEST_F(SenderSessionTest, ComplainsIfMissingResolutionsRemoting) { + const Error error = session_->NegotiateRemoting( + kAudioCaptureConfigValid, kVideoCaptureConfigMissingResolutions); + EXPECT_EQ(error.code(), Error::Code::kParameterInvalid); +} + +TEST_F(SenderSessionTest, HandlesValidAnswerRemoting) { + NegotiateRemotingWithValidConfigs(); + std::string answer = ConstructAnswerFromOffer(CastMode::kRemoting); + + EXPECT_CALL(client_, OnRemotingNegotiated(session_.get(), _)); + message_port_->ReceiveMessage(answer); + message_port_->ReceiveMessage(kCapabilitiesResponse); +} + +TEST_F(SenderSessionTest, SuccessfulRemotingNegotiationYieldsValidObject) { + NegotiateRemotingWithValidConfigs(); + std::string answer = ConstructAnswerFromOffer(CastMode::kRemoting); + + SenderSession::RemotingNegotiation negotiation; + EXPECT_CALL(client_, OnRemotingNegotiated(session_.get(), _)) + .WillOnce(testing::SaveArg<1>(&negotiation)); + message_port_->ReceiveMessage(answer); + message_port_->ReceiveMessage(kCapabilitiesResponse); + + // The capabilities should match the values in |kCapabilitiesResponse|. + EXPECT_THAT(negotiation.capabilities.audio, + testing::ElementsAre(AudioCapability::kBaselineSet, + AudioCapability::kAac)); + + // The "video" capability is ignored since it means nothing. + EXPECT_THAT(negotiation.capabilities.video, + testing::ElementsAre(VideoCapability::kVp8)); + + // The broker is tested elsewhere, but we can sanity check that we got a valid + // one here. + EXPECT_TRUE(negotiation.broker); + const RpcBroker::Handle handle = negotiation.broker->GetUniqueHandle(); + EXPECT_NE(RpcBroker::kInvalidHandle, handle); +} + } // namespace cast } // namespace openscreen diff --git a/chromium/third_party/openscreen/src/cast/streaming/session_messager.cc b/chromium/third_party/openscreen/src/cast/streaming/session_messager.cc index 31e634d9561..126f721857f 100644 --- a/chromium/third_party/openscreen/src/cast/streaming/session_messager.cc +++ b/chromium/third_party/openscreen/src/cast/streaming/session_messager.cc @@ -21,8 +21,7 @@ void ReplyIfTimedOut( ReceiverMessage::Type reply_type, std::vector<std::pair<int, SenderSessionMessager::ReplyCallback>>* replies) { - auto it = replies->begin(); - for (; it != replies->end(); ++it) { + for (auto it = replies->begin(); it != replies->end(); ++it) { if (it->first == sequence_number) { OSP_DVLOG << "Replying with empty message due to timeout for sequence number: " @@ -108,6 +107,8 @@ Error SenderSessionMessager::SendRequest(SenderMessage message, return error; } + OSP_DCHECK(awaiting_replies_.find(message.sequence_number) == + awaiting_replies_.end()); awaiting_replies_.emplace_back(message.sequence_number, std::move(cb)); task_runner_->PostTaskWithDelay( [self = weak_factory_.GetWeakPtr(), reply_type, @@ -179,8 +180,12 @@ void SenderSessionMessager::OnMessage(const std::string& source_id, return; } - it->second(receiver_message.value({})); - awaiting_replies_.erase(it); + it->second(std::move(receiver_message.value({}))); + + // Calling the function callback may result in the checksum of the pointed + // to object to change, so calling erase() on the iterator after executing + // second() may result in a segfault. + awaiting_replies_.erase_key(sequence_number); } } @@ -202,7 +207,7 @@ void ReceiverSessionMessager::SetHandler(SenderMessage::Type type, Error ReceiverSessionMessager::SendMessage(ReceiverMessage message) { if (sender_session_id_.empty()) { return Error(Error::Code::kInitializationFailure, - "Tried to send a message without receving one first"); + "Tried to send a message without receiving one first"); } const auto namespace_ = (message.type == ReceiverMessage::Type::kRpc) diff --git a/chromium/third_party/openscreen/src/cast/streaming/session_messager.h b/chromium/third_party/openscreen/src/cast/streaming/session_messager.h index 99b458ff56b..044ff794a64 100644 --- a/chromium/third_party/openscreen/src/cast/streaming/session_messager.h +++ b/chromium/third_party/openscreen/src/cast/streaming/session_messager.h @@ -38,9 +38,9 @@ class SessionMessager : public MessagePort::Client { protected: // Barebones message sending method shared by both children. - Error SendMessage(const std::string& destination_id, - const std::string& namespace_, - const Json::Value& message_root); + [[nodiscard]] Error SendMessage(const std::string& destination_id, + const std::string& namespace_, + const Json::Value& message_root); // Used to report errors in subclasses. void ReportError(Error error); diff --git a/chromium/third_party/openscreen/src/cast/streaming/session_messager_unittest.cc b/chromium/third_party/openscreen/src/cast/streaming/session_messager_unittest.cc index 6f5dd3eaca2..21d702809c1 100644 --- a/chromium/third_party/openscreen/src/cast/streaming/session_messager_unittest.cc +++ b/chromium/third_party/openscreen/src/cast/streaming/session_messager_unittest.cc @@ -125,24 +125,26 @@ class SessionMessagerTest : public ::testing::Test { }; TEST_F(SessionMessagerTest, RpcMessaging) { - ASSERT_TRUE(sender_messager_ - .SendOutboundMessage(SenderMessage{ - SenderMessage::Type::kRpc, 123, true /* valid */, - std::string("all your base are belong to us")}) - .ok()); + static const std::vector<uint8_t> kSenderMessage{1, 2, 3, 4, 5}; + static const std::vector<uint8_t> kReceiverResponse{6, 7, 8, 9}; + ASSERT_TRUE( + sender_messager_ + .SendOutboundMessage(SenderMessage{SenderMessage::Type::kRpc, 123, + true /* valid */, kSenderMessage}) + .ok()); ASSERT_EQ(1u, message_store_.sender_messages.size()); ASSERT_TRUE(message_store_.receiver_messages.empty()); EXPECT_EQ(SenderMessage::Type::kRpc, message_store_.sender_messages[0].type); ASSERT_TRUE(message_store_.sender_messages[0].valid); - EXPECT_EQ("all your base are belong to us", - absl::get<std::string>(message_store_.sender_messages[0].body)); + EXPECT_EQ(kSenderMessage, absl::get<std::vector<uint8_t>>( + message_store_.sender_messages[0].body)); message_store_.sender_messages.clear(); ASSERT_TRUE( receiver_messager_ .SendMessage(ReceiverMessage{ReceiverMessage::Type::kRpc, 123, - true /* valid */, std::string("nuh uh")}) + true /* valid */, kReceiverResponse}) .ok()); ASSERT_TRUE(message_store_.sender_messages.empty()); @@ -150,8 +152,8 @@ TEST_F(SessionMessagerTest, RpcMessaging) { EXPECT_EQ(ReceiverMessage::Type::kRpc, message_store_.receiver_messages[0].type); EXPECT_TRUE(message_store_.receiver_messages[0].valid); - EXPECT_EQ("nuh uh", - absl::get<std::string>(message_store_.receiver_messages[0].body)); + EXPECT_EQ(kReceiverResponse, absl::get<std::vector<uint8_t>>( + message_store_.receiver_messages[0].body)); } TEST_F(SessionMessagerTest, StatusMessaging) { @@ -207,7 +209,9 @@ TEST_F(SessionMessagerTest, CapabilitiesMessaging) { ASSERT_TRUE(receiver_messager_ .SendMessage(ReceiverMessage{ ReceiverMessage::Type::kCapabilitiesResponse, 1337, - true /* valid */, ReceiverCapability{47, {"ac3", "4k"}}}) + true /* valid */, + ReceiverCapability{ + 47, {MediaCapability::kAac, MediaCapability::k4k}}}) .ok()); ASSERT_TRUE(message_store_.sender_messages.empty()); @@ -219,7 +223,8 @@ TEST_F(SessionMessagerTest, CapabilitiesMessaging) { const auto& capability = absl::get<ReceiverCapability>(message_store_.receiver_messages[0].body); EXPECT_EQ(47, capability.remoting_version); - EXPECT_THAT(capability.media_capabilities, ElementsAre("ac3", "4k")); + EXPECT_THAT(capability.media_capabilities, + ElementsAre(MediaCapability::kAac, MediaCapability::k4k)); } TEST_F(SessionMessagerTest, OfferAnswerMessaging) { diff --git a/chromium/third_party/openscreen/src/discovery/dnssd/impl/dns_data_graph.cc b/chromium/third_party/openscreen/src/discovery/dnssd/impl/dns_data_graph.cc index 9e6f166ed33..079dbf7d79f 100644 --- a/chromium/third_party/openscreen/src/discovery/dnssd/impl/dns_data_graph.cc +++ b/chromium/third_party/openscreen/src/discovery/dnssd/impl/dns_data_graph.cc @@ -252,7 +252,10 @@ Error DnsDataGraphImpl::Node::ApplyDataRecordChange(MdnsRecord record, if (record.dns_type() == DnsType::kPTR) { child_name = absl::get<PtrRecordRdata>(record.rdata()).ptr_domain(); - it = std::find(records_.begin(), records_.end(), record); + it = std::find_if(records_.begin(), records_.end(), + [record](const MdnsRecord& rhs) { + return record.IsReannouncementOf(rhs); + }); } else { if (record.dns_type() == DnsType::kSRV) { child_name = absl::get<SrvRecordRdata>(record.rdata()).target(); diff --git a/chromium/third_party/openscreen/src/discovery/dnssd/impl/dns_data_graph_unittest.cc b/chromium/third_party/openscreen/src/discovery/dnssd/impl/dns_data_graph_unittest.cc index 0af3aa19dbb..a32b942c240 100644 --- a/chromium/third_party/openscreen/src/discovery/dnssd/impl/dns_data_graph_unittest.cc +++ b/chromium/third_party/openscreen/src/discovery/dnssd/impl/dns_data_graph_unittest.cc @@ -236,6 +236,21 @@ TEST_F(DnsDataGraphTests, DeleteFailsForNonExistingRecord) { EXPECT_EQ(graph_->GetTrackedDomainCount(), size_t{2}); } +TEST_F(DnsDataGraphTests, DeleteSucceedsForRecordWithDifferentTtl) { + const MdnsRecord ptr = GetFakePtrRecord(primary_domain_); + TriggerRecordCreationWithCallback(ptr, primary_domain_); + + EXPECT_CALL(callbacks_, OnStopTracking(primary_domain_)); + const MdnsRecord new_ptr(ptr.name(), ptr.dns_type(), ptr.dns_class(), + ptr.record_type(), + ptr.ttl() + std::chrono::seconds(10), ptr.rdata()); + const auto result = + ApplyDataRecordChange(new_ptr, RecordChangedEvent::kExpired); + + EXPECT_TRUE(result.ok()); + EXPECT_EQ(graph_->GetTrackedDomainCount(), size_t{1}); +} + TEST_F(DnsDataGraphTests, UpdateEndpointsWorksAsExpected) { auto ptr = GetFakePtrRecord(primary_domain_); auto srv = GetFakeSrvRecord(primary_domain_, secondary_domain_); diff --git a/chromium/third_party/openscreen/src/discovery/dnssd/impl/service_instance.cc b/chromium/third_party/openscreen/src/discovery/dnssd/impl/service_instance.cc index e241e473506..7d4b014fd7e 100644 --- a/chromium/third_party/openscreen/src/discovery/dnssd/impl/service_instance.cc +++ b/chromium/third_party/openscreen/src/discovery/dnssd/impl/service_instance.cc @@ -23,8 +23,14 @@ ServiceInstance::ServiceInstance(TaskRunner* task_runner, config, network_info)), network_config_(network_info.interface.index, - network_info.interface.GetIpAddressV4(), - network_info.interface.GetIpAddressV6()) { + (network_info.supported_address_families & + Config::NetworkInfo::kUseIpV4) + ? network_info.interface.GetIpAddressV4() + : IPAddress{}, + (network_info.supported_address_families & + Config::NetworkInfo::kUseIpV6) + ? network_info.interface.GetIpAddressV6() + : IPAddress{}) { const Config::NetworkInfo::AddressFamilies supported_address_families = network_info.supported_address_families; diff --git a/chromium/third_party/openscreen/src/discovery/mdns/mdns_records.cc b/chromium/third_party/openscreen/src/discovery/mdns/mdns_records.cc index 4768e2e4fc8..1b7b441b228 100644 --- a/chromium/third_party/openscreen/src/discovery/mdns/mdns_records.cc +++ b/chromium/third_party/openscreen/src/discovery/mdns/mdns_records.cc @@ -606,9 +606,7 @@ bool MdnsRecord::IsValidConfig(const DomainName& name, } bool MdnsRecord::operator==(const MdnsRecord& rhs) const { - return dns_type_ == rhs.dns_type_ && dns_class_ == rhs.dns_class_ && - record_type_ == rhs.record_type_ && ttl_ == rhs.ttl_ && - name_ == rhs.name_ && rdata_ == rhs.rdata_; + return IsReannouncementOf(rhs) && ttl_ == rhs.ttl_; } bool MdnsRecord::operator!=(const MdnsRecord& rhs) const { @@ -654,6 +652,12 @@ bool MdnsRecord::operator>=(const MdnsRecord& rhs) const { return !(*this < rhs); } +bool MdnsRecord::IsReannouncementOf(const MdnsRecord& rhs) const { + return dns_type_ == rhs.dns_type_ && dns_class_ == rhs.dns_class_ && + record_type_ == rhs.record_type_ && name_ == rhs.name_ && + rdata_ == rhs.rdata_; +} + size_t MdnsRecord::MaxWireSize() const { auto wire_size_visitor = [](auto&& arg) { return arg.MaxWireSize(); }; // NAME size, 2-byte TYPE, 2-byte CLASS, 4-byte TTL, RDATA size diff --git a/chromium/third_party/openscreen/src/discovery/mdns/mdns_records.h b/chromium/third_party/openscreen/src/discovery/mdns/mdns_records.h index de37932ce01..ce1be4f6571 100644 --- a/chromium/third_party/openscreen/src/discovery/mdns/mdns_records.h +++ b/chromium/third_party/openscreen/src/discovery/mdns/mdns_records.h @@ -475,6 +475,9 @@ class MdnsRecord { bool operator<=(const MdnsRecord& other) const; bool operator>=(const MdnsRecord& other) const; + // While not being "equivalent", a record could be said to be an update of + // a different record if it is the same, excepting TTL. + bool IsReannouncementOf(const MdnsRecord& other) const; size_t MaxWireSize() const; const DomainName& name() const { return name_; } DnsType dns_type() const { return dns_type_; } diff --git a/chromium/third_party/openscreen/src/osp/demo/osp_demo.cc b/chromium/third_party/openscreen/src/osp/demo/osp_demo.cc index 443536f3340..952a925f46d 100644 --- a/chromium/third_party/openscreen/src/osp/demo/osp_demo.cc +++ b/chromium/third_party/openscreen/src/osp/demo/osp_demo.cc @@ -644,8 +644,7 @@ int main(int argc, char** argv) { // TODO(jophba): Mac on Mojave hangs on this command forever. openscreen::SetLogFifoOrDie(log_filename); - PlatformClientPosix::Create(std::chrono::milliseconds(50), - std::chrono::milliseconds(50)); + PlatformClientPosix::Create(std::chrono::milliseconds(50)); if (is_receiver_demo) { OSP_LOG_INFO << "Running publisher demo..."; diff --git a/chromium/third_party/openscreen/src/osp/impl/discovery/mdns/mdns_demo.cc b/chromium/third_party/openscreen/src/osp/impl/discovery/mdns/mdns_demo.cc index 628609957e6..1fc1513ec8c 100644 --- a/chromium/third_party/openscreen/src/osp/impl/discovery/mdns/mdns_demo.cc +++ b/chromium/third_party/openscreen/src/osp/impl/discovery/mdns/mdns_demo.cc @@ -361,8 +361,7 @@ int main(int argc, char** argv) { openscreen::osp::ServiceMap services; openscreen::osp::g_services = &services; - PlatformClientPosix::Create(std::chrono::milliseconds(50), - std::chrono::milliseconds(50)); + PlatformClientPosix::Create(std::chrono::milliseconds(50)); openscreen::osp::BrowseDemo( PlatformClientPosix::GetInstance()->GetTaskRunner(), labels[0], labels[1], diff --git a/chromium/third_party/openscreen/src/platform/base/error.cc b/chromium/third_party/openscreen/src/platform/base/error.cc index 58d81e76dd3..fa891335c5f 100644 --- a/chromium/third_party/openscreen/src/platform/base/error.cc +++ b/chromium/third_party/openscreen/src/platform/base/error.cc @@ -256,6 +256,12 @@ std::ostream& operator<<(std::ostream& os, const Error::Code& code) { return os << "UnknownCodec"; case Error::Code::kSocketFailure: return os << "SocketFailure"; + case Error::Code::kUnencryptedOffer: + return os << "UnencryptedOffer"; + case Error::Code::kRemotingNotSupported: + return os << "RemotingNotSupported"; + case Error::Code::kNegotiationFailure: + return os << "NegotiationFailure"; case Error::Code::kNone: break; } diff --git a/chromium/third_party/openscreen/src/platform/base/error.h b/chromium/third_party/openscreen/src/platform/base/error.h index dc4c3a7f874..69a8a89ec40 100644 --- a/chromium/third_party/openscreen/src/platform/base/error.h +++ b/chromium/third_party/openscreen/src/platform/base/error.h @@ -186,7 +186,13 @@ class Error { // Cast streaming errors kTypeError, kUnknownCodec, - kSocketFailure + kSocketFailure, + kUnencryptedOffer, + kRemotingNotSupported, + + // A negotiation failure means that the current negotiation must be + // restarted by the sender. + kNegotiationFailure, }; Error(); diff --git a/chromium/third_party/openscreen/src/platform/impl/platform_client_posix.cc b/chromium/third_party/openscreen/src/platform/impl/platform_client_posix.cc index d1415258bd7..328b8bfa8eb 100644 --- a/chromium/third_party/openscreen/src/platform/impl/platform_client_posix.cc +++ b/chromium/third_party/openscreen/src/platform/impl/platform_client_posix.cc @@ -5,6 +5,7 @@ #include "platform/impl/platform_client_posix.h" #include <functional> +#include <utility> #include <vector> #include "platform/impl/udp_socket_reader_posix.h" @@ -16,18 +17,14 @@ PlatformClientPosix* PlatformClientPosix::instance_ = nullptr; // static void PlatformClientPosix::Create(Clock::duration networking_operation_timeout, - Clock::duration networking_loop_interval, std::unique_ptr<TaskRunnerImpl> task_runner) { SetInstance(new PlatformClientPosix(networking_operation_timeout, - networking_loop_interval, std::move(task_runner))); } // static -void PlatformClientPosix::Create(Clock::duration networking_operation_timeout, - Clock::duration networking_loop_interval) { - SetInstance(new PlatformClientPosix(networking_operation_timeout, - networking_loop_interval)); +void PlatformClientPosix::Create(Clock::duration networking_operation_timeout) { + SetInstance(new PlatformClientPosix(networking_operation_timeout)); } // static @@ -67,7 +64,7 @@ PlatformClientPosix::~PlatformClientPosix() { } OSP_DVLOG << "Shutting down network operations..."; - networking_loop_.RequestStopSoon(); + networking_loop_running_.store(false); networking_loop_thread_.join(); OSP_DVLOG << "\tNetwork operation shutdown complete!"; } @@ -79,27 +76,21 @@ void PlatformClientPosix::SetInstance(PlatformClientPosix* instance) { } PlatformClientPosix::PlatformClientPosix( - Clock::duration networking_operation_timeout, - Clock::duration networking_loop_interval) - : networking_loop_(networking_operations(), - networking_operation_timeout, - networking_loop_interval), - task_runner_(new TaskRunnerImpl(Clock::now)), - networking_loop_thread_(&OperationLoop::RunUntilStopped, - &networking_loop_), + Clock::duration networking_operation_timeout) + : task_runner_(new TaskRunnerImpl(Clock::now)), + networking_loop_timeout_(networking_operation_timeout), + networking_loop_thread_(&PlatformClientPosix::RunNetworkLoopUntilStopped, + this), task_runner_thread_( std::thread(&TaskRunnerImpl::RunUntilStopped, task_runner_.get())) {} PlatformClientPosix::PlatformClientPosix( Clock::duration networking_operation_timeout, - Clock::duration networking_loop_interval, std::unique_ptr<TaskRunnerImpl> task_runner) - : networking_loop_(networking_operations(), - networking_operation_timeout, - networking_loop_interval), - task_runner_(std::move(task_runner)), - networking_loop_thread_(&OperationLoop::RunUntilStopped, - &networking_loop_) {} + : task_runner_(std::move(task_runner)), + networking_loop_timeout_(networking_operation_timeout), + networking_loop_thread_(&PlatformClientPosix::RunNetworkLoopUntilStopped, + this) {} SocketHandleWaiterPosix* PlatformClientPosix::socket_handle_waiter() { std::call_once(waiter_initialization_, [this]() { @@ -109,20 +100,14 @@ SocketHandleWaiterPosix* PlatformClientPosix::socket_handle_waiter() { return waiter_.get(); } -void PlatformClientPosix::PerformSocketHandleWaiterActions( - Clock::duration timeout) { - if (!waiter_created_.load()) { - return; +void PlatformClientPosix::RunNetworkLoopUntilStopped() { + while (networking_loop_running_.load()) { + if (!waiter_created_.load()) { + std::this_thread::sleep_for(networking_loop_timeout_); + continue; + } + socket_handle_waiter()->ProcessHandles(networking_loop_timeout_); } - - socket_handle_waiter()->ProcessHandles(timeout); -} - -std::vector<std::function<void(Clock::duration)>> -PlatformClientPosix::networking_operations() { - return {[this](Clock::duration timeout) { - PerformSocketHandleWaiterActions(timeout); - }}; } } // namespace openscreen diff --git a/chromium/third_party/openscreen/src/platform/impl/platform_client_posix.h b/chromium/third_party/openscreen/src/platform/impl/platform_client_posix.h index 6bda424b168..bb320f553b2 100644 --- a/chromium/third_party/openscreen/src/platform/impl/platform_client_posix.h +++ b/chromium/third_party/openscreen/src/platform/impl/platform_client_posix.h @@ -9,6 +9,7 @@ #include <memory> #include <mutex> #include <thread> +#include <vector> #include "absl/types/optional.h" #include "platform/api/time.h" @@ -16,7 +17,6 @@ #include "platform/impl/socket_handle_waiter_posix.h" #include "platform/impl/task_runner.h" #include "platform/impl/tls_data_router_posix.h" -#include "util/operation_loop.h" namespace openscreen { @@ -41,20 +41,19 @@ class PlatformClientPosix { // |networking_loop_interval| sets the minimum amount of time that should pass // between iterations of the loop used to handle networking operations. Higher // values will result in less time being spent on these operations, but also - // potentially less performant networking operations. + // less performant networking operations. Be careful setting values larger + // than a few hundred microseconds. // // |networking_operation_timeout| sets how much time may be spent on a // single networking operation type. // // |task_runner| is a client-provided TaskRunner implementation. static void Create(Clock::duration networking_operation_timeout, - Clock::duration networking_loop_interval, std::unique_ptr<TaskRunnerImpl> task_runner); // Initializes the platform implementation and creates a new TaskRunner (which // starts a new thread). - static void Create(Clock::duration networking_operation_timeout, - Clock::duration networking_loop_interval); + static void Create(Clock::duration networking_operation_timeout); // Shuts down and deletes the PlatformClient instance currently stored as a // singleton. This method is expected to be called before program exit. After @@ -83,25 +82,15 @@ class PlatformClientPosix { static void SetInstance(PlatformClientPosix* client); private: - PlatformClientPosix(Clock::duration networking_operation_timeout, - Clock::duration networking_loop_interval); + explicit PlatformClientPosix(Clock::duration networking_operation_timeout); PlatformClientPosix(Clock::duration networking_operation_timeout, - Clock::duration networking_loop_interval, std::unique_ptr<TaskRunnerImpl> task_runner); // This method is thread-safe. SocketHandleWaiterPosix* socket_handle_waiter(); - // Helper functions to use when creating and calling the OperationLoop used - // for the networking thread. - void PerformSocketHandleWaiterActions(Clock::duration timeout); - void PerformTlsDataRouterActions(Clock::duration timeout); - std::vector<std::function<void(Clock::duration)>> networking_operations(); - - // Instance objects with threads are created at object-creation time. - // NOTE: Delayed instantiation of networking_loop_ may be useful in future. - OperationLoop networking_loop_; + void RunNetworkLoopUntilStopped(); std::unique_ptr<TaskRunnerImpl> task_runner_; @@ -109,6 +98,10 @@ class PlatformClientPosix { std::atomic_bool waiter_created_{false}; std::atomic_bool tls_data_router_created_{false}; + // Parameters for networking loop. + std::atomic_bool networking_loop_running_{true}; + Clock::duration networking_loop_timeout_; + // Flags used to ensure that initialization of below instance objects occurs // only once across all threads. std::once_flag waiter_initialization_; diff --git a/chromium/third_party/openscreen/src/third_party/boringssl/BUILD.generated.gni b/chromium/third_party/openscreen/src/third_party/boringssl/BUILD.generated.gni index f96352f79b7..aee38c2ea0a 100644 --- a/chromium/third_party/openscreen/src/third_party/boringssl/BUILD.generated.gni +++ b/chromium/third_party/openscreen/src/third_party/boringssl/BUILD.generated.gni @@ -49,6 +49,7 @@ crypto_sources = [ "src/crypto/bio/printf.c", "src/crypto/bio/socket.c", "src/crypto/bio/socket_helper.c", + "src/crypto/blake2/blake2.c", "src/crypto/bn_extra/bn_asn1.c", "src/crypto/bn_extra/convert.c", "src/crypto/buf/buf.c", @@ -78,20 +79,23 @@ crypto_sources = [ "src/crypto/conf/internal.h", "src/crypto/cpu-aarch64-fuchsia.c", "src/crypto/cpu-aarch64-linux.c", + "src/crypto/cpu-aarch64-win.c", "src/crypto/cpu-arm-linux.c", "src/crypto/cpu-arm-linux.h", "src/crypto/cpu-arm.c", "src/crypto/cpu-intel.c", "src/crypto/cpu-ppc64le.c", "src/crypto/crypto.c", + "src/crypto/curve25519/curve25519.c", + "src/crypto/curve25519/curve25519_tables.h", + "src/crypto/curve25519/internal.h", "src/crypto/curve25519/spake25519.c", - "src/crypto/dh/check.c", - "src/crypto/dh/dh.c", - "src/crypto/dh/dh_asn1.c", - "src/crypto/dh/params.c", + "src/crypto/dh_extra/dh_asn1.c", + "src/crypto/dh_extra/params.c", "src/crypto/digest_extra/digest_extra.c", "src/crypto/dsa/dsa.c", "src/crypto/dsa/dsa_asn1.c", + "src/crypto/dsa/internal.h", "src/crypto/ec_extra/ec_asn1.c", "src/crypto/ec_extra/ec_derive.c", "src/crypto/ec_extra/hash_to_curve.c", @@ -132,16 +136,20 @@ crypto_sources = [ "src/crypto/fipsmodule/ec/internal.h", "src/crypto/fipsmodule/ec/p256-x86_64-table.h", "src/crypto/fipsmodule/ec/p256-x86_64.h", + "src/crypto/fipsmodule/ec/p256_table.h", "src/crypto/fipsmodule/fips_shared_support.c", "src/crypto/fipsmodule/is_fips.c", "src/crypto/fipsmodule/md5/internal.h", "src/crypto/fipsmodule/modes/internal.h", + "src/crypto/fipsmodule/rand/fork_detect.h", "src/crypto/fipsmodule/rand/getrandom_fillin.h", "src/crypto/fipsmodule/rand/internal.h", "src/crypto/fipsmodule/rsa/internal.h", "src/crypto/fipsmodule/sha/internal.h", "src/crypto/fipsmodule/tls/internal.h", "src/crypto/hkdf/hkdf.c", + "src/crypto/hpke/hpke.c", + "src/crypto/hpke/internal.h", "src/crypto/hrss/hrss.c", "src/crypto/hrss/internal.h", "src/crypto/internal.h", @@ -174,6 +182,7 @@ crypto_sources = [ "src/crypto/rand_extra/deterministic.c", "src/crypto/rand_extra/forkunsafe.c", "src/crypto/rand_extra/fuchsia.c", + "src/crypto/rand_extra/passive.c", "src/crypto/rand_extra/rand_extra.c", "src/crypto/rand_extra/windows.c", "src/crypto/rc4/rc4.c", @@ -190,6 +199,7 @@ crypto_sources = [ "src/crypto/trust_token/internal.h", "src/crypto/trust_token/pmbtoken.c", "src/crypto/trust_token/trust_token.c", + "src/crypto/trust_token/voprf.c", "src/crypto/x509/a_digest.c", "src/crypto/x509/a_sign.c", "src/crypto/x509/a_strex.c", @@ -271,18 +281,13 @@ crypto_sources = [ "src/crypto/x509v3/v3_pci.c", "src/crypto/x509v3/v3_pcia.c", "src/crypto/x509v3/v3_pcons.c", - "src/crypto/x509v3/v3_pku.c", "src/crypto/x509v3/v3_pmaps.c", "src/crypto/x509v3/v3_prn.c", "src/crypto/x509v3/v3_purp.c", "src/crypto/x509v3/v3_skey.c", - "src/crypto/x509v3/v3_sxnet.c", "src/crypto/x509v3/v3_utl.c", - "src/third_party/fiat/curve25519.c", "src/third_party/fiat/curve25519_32.h", "src/third_party/fiat/curve25519_64.h", - "src/third_party/fiat/curve25519_tables.h", - "src/third_party/fiat/internal.h", "src/third_party/fiat/p256_32.h", "src/third_party/fiat/p256_64.h", ] @@ -297,6 +302,7 @@ crypto_headers = [ "src/include/openssl/base.h", "src/include/openssl/base64.h", "src/include/openssl/bio.h", + "src/include/openssl/blake2.h", "src/include/openssl/blowfish.h", "src/include/openssl/bn.h", "src/include/openssl/buf.h", @@ -409,6 +415,27 @@ ssl_headers = [ "src/include/openssl/tls1.h", ] +tool_sources = [ + "src/tool/args.cc", + "src/tool/ciphers.cc", + "src/tool/client.cc", + "src/tool/const.cc", + "src/tool/digest.cc", + "src/tool/fd.cc", + "src/tool/file.cc", + "src/tool/generate_ed25519.cc", + "src/tool/genrsa.cc", + "src/tool/internal.h", + "src/tool/pkcs12.cc", + "src/tool/rand.cc", + "src/tool/server.cc", + "src/tool/sign.cc", + "src/tool/speed.cc", + "src/tool/tool.cc", + "src/tool/transport_common.cc", + "src/tool/transport_common.h", +] + crypto_sources_ios_aarch64 = [ "ios-aarch64/crypto/chacha/chacha-armv8.S", "ios-aarch64/crypto/fipsmodule/aesv8-armx64.S", @@ -548,6 +575,19 @@ crypto_sources_mac_x86_64 = [ "mac-x86_64/crypto/test/trampoline-x86_64.S", ] +crypto_sources_win_aarch64 = [ + "win-aarch64/crypto/chacha/chacha-armv8.S", + "win-aarch64/crypto/fipsmodule/aesv8-armx64.S", + "win-aarch64/crypto/fipsmodule/armv8-mont.S", + "win-aarch64/crypto/fipsmodule/ghash-neon-armv8.S", + "win-aarch64/crypto/fipsmodule/ghashv8-armx64.S", + "win-aarch64/crypto/fipsmodule/sha1-armv8.S", + "win-aarch64/crypto/fipsmodule/sha256-armv8.S", + "win-aarch64/crypto/fipsmodule/sha512-armv8.S", + "win-aarch64/crypto/fipsmodule/vpaes-armv8.S", + "win-aarch64/crypto/test/trampoline-armv8.S", +] + crypto_sources_win_x86 = [ "win-x86/crypto/chacha/chacha-x86.asm", "win-x86/crypto/fipsmodule/aesni-x86.asm", diff --git a/chromium/third_party/openscreen/src/third_party/boringssl/BUILD.generated_tests.gni b/chromium/third_party/openscreen/src/third_party/boringssl/BUILD.generated_tests.gni index 3e8ff75219c..29fc7c071f3 100644 --- a/chromium/third_party/openscreen/src/third_party/boringssl/BUILD.generated_tests.gni +++ b/chromium/third_party/openscreen/src/third_party/boringssl/BUILD.generated_tests.gni @@ -31,6 +31,7 @@ crypto_test_sources = [ "src/crypto/asn1/asn1_test.cc", "src/crypto/base64/base64_test.cc", "src/crypto/bio/bio_test.cc", + "src/crypto/blake2/blake2_test.cc", "src/crypto/buf/buf_test.cc", "src/crypto/bytestring/bytestring_test.cc", "src/crypto/chacha/chacha_test.cc", @@ -40,10 +41,11 @@ crypto_test_sources = [ "src/crypto/compiler_test.cc", "src/crypto/constant_time_test.cc", "src/crypto/cpu-arm-linux_test.cc", + "src/crypto/crypto_test.cc", "src/crypto/curve25519/ed25519_test.cc", "src/crypto/curve25519/spake25519_test.cc", "src/crypto/curve25519/x25519_test.cc", - "src/crypto/dh/dh_test.cc", + "src/crypto/dh_extra/dh_test.cc", "src/crypto/digest_extra/digest_test.cc", "src/crypto/dsa/dsa_test.cc", "src/crypto/ecdh_extra/ecdh_test.cc", @@ -60,9 +62,11 @@ crypto_test_sources = [ "src/crypto/fipsmodule/md5/md5_test.cc", "src/crypto/fipsmodule/modes/gcm_test.cc", "src/crypto/fipsmodule/rand/ctrdrbg_test.cc", + "src/crypto/fipsmodule/rand/fork_detect_test.cc", "src/crypto/fipsmodule/sha/sha_test.cc", "src/crypto/hkdf/hkdf_test.cc", "src/crypto/hmac_extra/hmac_test.cc", + "src/crypto/hpke/hpke_test.cc", "src/crypto/hrss/hrss_test.cc", "src/crypto/impl_dispatch_test.cc", "src/crypto/lhash/lhash_test.cc", @@ -91,12 +95,14 @@ crypto_test_sources = [ ] crypto_test_data = [ + "src/crypto/blake2/blake2b256_tests.txt", "src/crypto/cipher_extra/test/aes_128_cbc_sha1_tls_implicit_iv_tests.txt", "src/crypto/cipher_extra/test/aes_128_cbc_sha1_tls_tests.txt", "src/crypto/cipher_extra/test/aes_128_cbc_sha256_tls_tests.txt", "src/crypto/cipher_extra/test/aes_128_ccm_bluetooth_8_tests.txt", "src/crypto/cipher_extra/test/aes_128_ccm_bluetooth_tests.txt", "src/crypto/cipher_extra/test/aes_128_ctr_hmac_sha256.txt", + "src/crypto/cipher_extra/test/aes_128_gcm_randnonce_tests.txt", "src/crypto/cipher_extra/test/aes_128_gcm_siv_tests.txt", "src/crypto/cipher_extra/test/aes_128_gcm_tests.txt", "src/crypto/cipher_extra/test/aes_192_gcm_tests.txt", @@ -105,6 +111,7 @@ crypto_test_data = [ "src/crypto/cipher_extra/test/aes_256_cbc_sha256_tls_tests.txt", "src/crypto/cipher_extra/test/aes_256_cbc_sha384_tls_tests.txt", "src/crypto/cipher_extra/test/aes_256_ctr_hmac_sha256.txt", + "src/crypto/cipher_extra/test/aes_256_gcm_randnonce_tests.txt", "src/crypto/cipher_extra/test/aes_256_gcm_siv_tests.txt", "src/crypto/cipher_extra/test/aes_256_gcm_tests.txt", "src/crypto/cipher_extra/test/chacha20_poly1305_tests.txt", @@ -140,15 +147,46 @@ crypto_test_data = [ "src/crypto/fipsmodule/modes/gcm_tests.txt", "src/crypto/fipsmodule/rand/ctrdrbg_vectors.txt", "src/crypto/hmac_extra/hmac_tests.txt", + "src/crypto/hpke/hpke_test_vectors.txt", "src/crypto/poly1305/poly1305_tests.txt", "src/crypto/siphash/siphash_tests.txt", - "src/crypto/x509/many_constraints.pem", - "src/crypto/x509/many_names1.pem", - "src/crypto/x509/many_names2.pem", - "src/crypto/x509/many_names3.pem", - "src/crypto/x509/some_names1.pem", - "src/crypto/x509/some_names2.pem", - "src/crypto/x509/some_names3.pem", + "src/crypto/x509/test/basic_constraints_ca.pem", + "src/crypto/x509/test/basic_constraints_ca_pathlen_0.pem", + "src/crypto/x509/test/basic_constraints_ca_pathlen_1.pem", + "src/crypto/x509/test/basic_constraints_ca_pathlen_10.pem", + "src/crypto/x509/test/basic_constraints_leaf.pem", + "src/crypto/x509/test/basic_constraints_none.pem", + "src/crypto/x509/test/invalid_extension_intermediate.pem", + "src/crypto/x509/test/invalid_extension_intermediate_authority_key_identifier.pem", + "src/crypto/x509/test/invalid_extension_intermediate_basic_constraints.pem", + "src/crypto/x509/test/invalid_extension_intermediate_ext_key_usage.pem", + "src/crypto/x509/test/invalid_extension_intermediate_key_usage.pem", + "src/crypto/x509/test/invalid_extension_intermediate_name_constraints.pem", + "src/crypto/x509/test/invalid_extension_intermediate_subject_alt_name.pem", + "src/crypto/x509/test/invalid_extension_intermediate_subject_key_identifier.pem", + "src/crypto/x509/test/invalid_extension_leaf.pem", + "src/crypto/x509/test/invalid_extension_leaf_authority_key_identifier.pem", + "src/crypto/x509/test/invalid_extension_leaf_basic_constraints.pem", + "src/crypto/x509/test/invalid_extension_leaf_ext_key_usage.pem", + "src/crypto/x509/test/invalid_extension_leaf_key_usage.pem", + "src/crypto/x509/test/invalid_extension_leaf_name_constraints.pem", + "src/crypto/x509/test/invalid_extension_leaf_subject_alt_name.pem", + "src/crypto/x509/test/invalid_extension_leaf_subject_key_identifier.pem", + "src/crypto/x509/test/invalid_extension_root.pem", + "src/crypto/x509/test/invalid_extension_root_authority_key_identifier.pem", + "src/crypto/x509/test/invalid_extension_root_basic_constraints.pem", + "src/crypto/x509/test/invalid_extension_root_ext_key_usage.pem", + "src/crypto/x509/test/invalid_extension_root_key_usage.pem", + "src/crypto/x509/test/invalid_extension_root_name_constraints.pem", + "src/crypto/x509/test/invalid_extension_root_subject_alt_name.pem", + "src/crypto/x509/test/invalid_extension_root_subject_key_identifier.pem", + "src/crypto/x509/test/many_constraints.pem", + "src/crypto/x509/test/many_names1.pem", + "src/crypto/x509/test/many_names2.pem", + "src/crypto/x509/test/many_names3.pem", + "src/crypto/x509/test/some_names1.pem", + "src/crypto/x509/test/some_names2.pem", + "src/crypto/x509/test/some_names3.pem", "src/third_party/wycheproof_testvectors/aes_cbc_pkcs5_test.txt", "src/third_party/wycheproof_testvectors/aes_cmac_test.txt", "src/third_party/wycheproof_testvectors/aes_gcm_siv_test.txt", diff --git a/chromium/third_party/openscreen/src/util/BUILD.gn b/chromium/third_party/openscreen/src/util/BUILD.gn index 3e915eb93c9..3f97e09e334 100644 --- a/chromium/third_party/openscreen/src/util/BUILD.gn +++ b/chromium/third_party/openscreen/src/util/BUILD.gn @@ -51,8 +51,6 @@ source_set("util") { "json/json_serialization.h", "json/json_value.cc", "json/json_value.h", - "operation_loop.cc", - "operation_loop.h", "osp_logging.h", "saturate_cast.h", "simple_fraction.cc", @@ -101,6 +99,7 @@ source_set("unittests") { "alarm_unittest.cc", "base64_unittest.cc", "big_endian_unittest.cc", + "crypto/certificate_utils_unittest.cc", "crypto/random_bytes_unittest.cc", "crypto/rsa_private_key_unittest.cc", "crypto/secure_hash_unittest.cc", @@ -111,7 +110,6 @@ source_set("unittests") { "json/json_helpers_unittest.cc", "json/json_serialization_unittest.cc", "json/json_value_unittest.cc", - "operation_loop_unittest.cc", "saturate_cast_unittest.cc", "simple_fraction_unittest.cc", "stringprintf_unittest.cc", @@ -127,12 +125,6 @@ source_set("unittests") { sources += [ "trace_logging_unittest.cc" ] } - # TODO(http://issuetracker.google.com/159955844): test fails on fieldtrial - # bot, needs to be diagnosed and reenabled in Chrome. - if (!build_with_chromium) { - sources += [ "crypto/certificate_utils_unittest.cc" ] - } - deps = [ ":util", "../platform:test", diff --git a/chromium/third_party/openscreen/src/util/base64.cc b/chromium/third_party/openscreen/src/util/base64.cc index 06e120e06d2..64e34175c7d 100644 --- a/chromium/third_party/openscreen/src/util/base64.cc +++ b/chromium/third_party/openscreen/src/util/base64.cc @@ -6,6 +6,10 @@ #include <stddef.h> +#include <string> +#include <utility> +#include <vector> + #include "third_party/modp_b64/modp_b64.h" #include "util/osp_logging.h" #include "util/std_util.h" @@ -33,20 +37,18 @@ std::string Encode(absl::string_view input) { return out; } -bool Decode(absl::string_view input, std::string* output) { - std::string out; - out.resize(modp_b64_decode_len(input.size())); +bool Decode(absl::string_view input, std::vector<uint8_t>* output) { + std::vector<uint8_t> out(modp_b64_decode_len(input.size())); - // We don't null terminate the result since it is binary data. - const size_t output_size = - modp_b64_decode(data(out), input.data(), input.size()); + const size_t output_size = modp_b64_decode( + reinterpret_cast<char*>(out.data()), input.data(), input.size()); if (output_size == MODP_B64_ERROR) { return false; } // The output size from decode_len is generally larger than needed. out.resize(output_size); - output->swap(out); + *output = std::move(out); return true; } diff --git a/chromium/third_party/openscreen/src/util/base64.h b/chromium/third_party/openscreen/src/util/base64.h index b24c3b3fc47..a7af9ecaa2b 100644 --- a/chromium/third_party/openscreen/src/util/base64.h +++ b/chromium/third_party/openscreen/src/util/base64.h @@ -6,6 +6,7 @@ #define UTIL_BASE64_H_ #include <string> +#include <vector> #include "absl/strings/string_view.h" #include "absl/types/span.h" @@ -23,7 +24,7 @@ std::string Encode(absl::string_view input); // Decodes the base64 input string. Returns true if successful and false // otherwise. The output string is only modified if successful. The decoding can // be done in-place. -bool Decode(absl::string_view input, std::string* output); +bool Decode(absl::string_view input, std::vector<uint8_t>* output); } // namespace base64 } // namespace openscreen diff --git a/chromium/third_party/openscreen/src/util/base64_unittest.cc b/chromium/third_party/openscreen/src/util/base64_unittest.cc index 28d4fb1dee2..873b5656e48 100644 --- a/chromium/third_party/openscreen/src/util/base64_unittest.cc +++ b/chromium/third_party/openscreen/src/util/base64_unittest.cc @@ -4,6 +4,9 @@ #include "util/base64.h" +#include <string> +#include <vector> + #include "gtest/gtest.h" namespace openscreen { @@ -14,13 +17,21 @@ namespace { constexpr char kText[] = "hello world"; constexpr char kBase64Text[] = "aGVsbG8gd29ybGQ="; +// More sophisticated comparisons here, such as EXPECT_STREQ, may +// cause memory failures on some platforms (e.g. ASAN) due to mismatched +// lengths. +void CheckEquals(const char* expected, const std::vector<uint8_t>& actual) { + EXPECT_EQ(0, std::memcmp(actual.data(), expected, actual.size())); +} + void CheckEncodeDecode(const char* to_encode, const char* encode_expected) { std::string encoded = Encode(to_encode); EXPECT_EQ(encode_expected, encoded); - std::string decoded; + std::vector<uint8_t> decoded; EXPECT_TRUE(Decode(encoded, &decoded)); - EXPECT_EQ(to_encode, decoded); + + CheckEquals(to_encode, decoded); } } // namespace @@ -52,8 +63,9 @@ TEST(Base64Test, InPlace) { text = Encode(text); EXPECT_EQ(kBase64Text, text); - EXPECT_TRUE(Decode(text, &text)); - EXPECT_EQ(text, kText); + std::vector<uint8_t> out; + EXPECT_TRUE(Decode(text, &out)); + CheckEquals(kText, out); } } // namespace base64 diff --git a/chromium/third_party/openscreen/src/util/crypto/certificate_utils.cc b/chromium/third_party/openscreen/src/util/crypto/certificate_utils.cc index a9b7d9adb0c..f5018042c98 100644 --- a/chromium/third_party/openscreen/src/util/crypto/certificate_utils.cc +++ b/chromium/third_party/openscreen/src/util/crypto/certificate_utils.cc @@ -218,25 +218,28 @@ ErrorOr<bssl::UniquePtr<EVP_PKEY>> ImportRSAPrivateKey( } std::string GetSpkiTlv(X509* cert) { - int len = i2d_X509_PUBKEY(cert->cert_info->key, nullptr); + X509_PUBKEY* key = X509_get_X509_PUBKEY(cert); + int len = i2d_X509_PUBKEY(key, nullptr); if (len <= 0) { return {}; } std::string x(len, 0); uint8_t* data = reinterpret_cast<uint8_t*>(&x[0]); - if (!i2d_X509_PUBKEY(cert->cert_info->key, &data)) { + if (!i2d_X509_PUBKEY(key, &data)) { return {}; } return x; } -ErrorOr<uint64_t> ParseDerUint64(ASN1_INTEGER* asn1int) { - if (asn1int->length > 8 || asn1int->length == 0) { +ErrorOr<uint64_t> ParseDerUint64(const ASN1_INTEGER* asn1int) { + const uint8_t* data = ASN1_STRING_get0_data(asn1int); + int length = ASN1_STRING_length(asn1int); + if (length > 8 || length <= 0) { return Error::Code::kParameterInvalid; } uint64_t result = 0; - for (int i = 0; i < asn1int->length; ++i) { - result = (result << 8) | asn1int->data[i]; + for (int i = 0; i < length; ++i) { + result = (result << 8) | data[i]; } return result; } diff --git a/chromium/third_party/openscreen/src/util/crypto/certificate_utils.h b/chromium/third_party/openscreen/src/util/crypto/certificate_utils.h index 22da0330a0b..6d9a08fd35e 100644 --- a/chromium/third_party/openscreen/src/util/crypto/certificate_utils.h +++ b/chromium/third_party/openscreen/src/util/crypto/certificate_utils.h @@ -52,7 +52,7 @@ ErrorOr<bssl::UniquePtr<EVP_PKEY>> ImportRSAPrivateKey( std::string GetSpkiTlv(X509* cert); -ErrorOr<uint64_t> ParseDerUint64(ASN1_INTEGER* asn1int); +ErrorOr<uint64_t> ParseDerUint64(const ASN1_INTEGER* asn1int); } // namespace openscreen diff --git a/chromium/third_party/openscreen/src/util/operation_loop.cc b/chromium/third_party/openscreen/src/util/operation_loop.cc deleted file mode 100644 index a20f1447c23..00000000000 --- a/chromium/third_party/openscreen/src/util/operation_loop.cc +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. -#include "util/operation_loop.h" - -#include <algorithm> - -#include "util/osp_logging.h" - -namespace openscreen { - -OperationLoop::OperationLoop(std::vector<OperationWithTimeout> operations, - Clock::duration timeout, - Clock::duration min_loop_execution_time) - : perform_all_operations_min_execution_time_(min_loop_execution_time), - operation_timeout_(timeout), - operations_(operations) { - OSP_DCHECK(operations_.size()); -} - -void OperationLoop::RunUntilStopped() { - OSP_CHECK(!is_running_.exchange(true)); - - while (is_running_.load()) { - PerformAllOperations(); - } -} - -void OperationLoop::RequestStopSoon() { - { - std::unique_lock<std::mutex> lock(wait_mutex_); - is_running_.store(false); - } - - perform_all_operations_waiter_.notify_all(); -} - -void OperationLoop::PerformAllOperations() { - auto start_time = Clock::now(); - - for (OperationWithTimeout operation : operations_) { - if (is_running_.load()) { - operation(operation_timeout_); - } - } - - const auto duration = Clock::now() - start_time; - const auto remaining_duration = - perform_all_operations_min_execution_time_ - duration; - if (remaining_duration > Clock::duration{0} && is_running_.load()) { - std::unique_lock<std::mutex> lock(wait_mutex_); - perform_all_operations_waiter_.wait_for( - lock, remaining_duration, [this]() { return !is_running_.load(); }); - } -} - -} // namespace openscreen diff --git a/chromium/third_party/openscreen/src/util/operation_loop.h b/chromium/third_party/openscreen/src/util/operation_loop.h deleted file mode 100644 index ddb7846f4be..00000000000 --- a/chromium/third_party/openscreen/src/util/operation_loop.h +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UTIL_OPERATION_LOOP_H_ -#define UTIL_OPERATION_LOOP_H_ - -#include <atomic> -#include <condition_variable> -#include <functional> -#include <vector> - -#include "platform/api/time.h" -#include "platform/base/macros.h" - -namespace openscreen { - -class OperationLoop { - public: - using OperationWithTimeout = std::function<void(Clock::duration)>; - - // Creates a new OperationLoop from a variable number of operations. The - // provided functions will be called repeatedly, at a minimum interval equal - // to min_loop_execution_time, and are expected to exit after the time period - // provided to their call has passed. This is because some operations may not - // be safe to be interrupted from this class. - // NOTE: If n operations are provided with operation timeout T, each iteration - // of the operation loop may take as long as n * T, and will not exit after - // min_loop_execution_time has elapsed. In order to avoid this behavior, the - // caller can set min_loop_execution_time = n * T. - // - // operations = Functions to execute repeatedly. All functions are expected to - // be valid the duration of this object's lifetime. - // timeout = Timeout for each individual function above. - // min_loop_execution_time = Minimum time that OperationLoop should wait - // before successive calls to members of the - // provided operations vector. - OperationLoop(std::vector<OperationWithTimeout> operations, - Clock::duration timeout, - Clock::duration min_loop_execution_time); - - // Runs the PerformAllOperations function in a loop until the below - // RequestStopSoon function is called. - void RunUntilStopped(); - - // Signals for the RunUntilStopped loop to cease running. - void RequestStopSoon(); - - OSP_DISALLOW_COPY_AND_ASSIGN(OperationLoop); - - private: - // Performs all operations which have been provided to this instance. - void PerformAllOperations(); - - const Clock::duration perform_all_operations_min_execution_time_; - - const Clock::duration operation_timeout_; - - // Used to wait in PerformAllOperations() if not enough time has elapsed. - std::condition_variable perform_all_operations_waiter_; - - // Mutex used by the above condition_variable. - std::mutex wait_mutex_; - - // Represents whether this instance is currently "running". - std::atomic_bool is_running_{false}; - - // Operations currently being run by this object. - const std::vector<OperationWithTimeout> operations_; -}; - -} // namespace openscreen - -#endif // UTIL_OPERATION_LOOP_H_ diff --git a/chromium/third_party/openscreen/src/util/operation_loop_unittest.cc b/chromium/third_party/openscreen/src/util/operation_loop_unittest.cc deleted file mode 100644 index 4b4ea5df671..00000000000 --- a/chromium/third_party/openscreen/src/util/operation_loop_unittest.cc +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "util/operation_loop.h" - -#include <atomic> -#include <chrono> -#include <thread> - -#include "gtest/gtest.h" - -namespace openscreen { - -TEST(OperationsLoopTest, PerformAllOperationsWaits) { - constexpr Clock::duration kTimeout{0}; - constexpr Clock::duration kMinRuntime{500}; - - const auto start_time = Clock::now(); - std::atomic<Clock::time_point> last_run{start_time}; - std::atomic<Clock::time_point> current_run{start_time}; - std::function<void(Clock::duration)> test_function = - [last = &last_run, current = ¤t_run](Clock::duration timeout) { - last->store(current->load()); - current->store(Clock::now()); - }; - OperationLoop loop({test_function}, kTimeout, kMinRuntime); - - std::thread run_loop([&loop]() { loop.RunUntilStopped(); }); - - while (last_run.load() == start_time) { - } - - loop.RequestStopSoon(); - run_loop.join(); - - EXPECT_GE( - std::chrono::nanoseconds(current_run.load() - last_run.load()).count(), - std::chrono::nanoseconds(kMinRuntime).count()); -} - -} // namespace openscreen |