Commit 73f4320f authored by Xiangjun Zhang's avatar Xiangjun Zhang Committed by Commit Bot

MirroringService: Add OFFER/ANSWER handling and MirrorSettings.

Add implementation to create OFFER message and handle ANSWER response.
Add the MirrorSettings which hold a set of default settings.

Bug: 734672
Change-Id: Ia6b9af5ac66e1f8f9496c0afaa335a22f40bb535
Reviewed-on: https://chromium-review.googlesource.com/1017235
Commit-Queue: Xiangjun Zhang <xjz@chromium.org>
Reviewed-by: default avatarDavid Benjamin <davidben@chromium.org>
Reviewed-by: default avatarYuri Wiitala <miu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#555837}
parent be7af108
...@@ -6,6 +6,7 @@ import("//testing/test.gni") ...@@ -6,6 +6,7 @@ import("//testing/test.gni")
source_set("interface") { source_set("interface") {
sources = [ sources = [
"interface.cc",
"interface.h", "interface.h",
] ]
...@@ -26,6 +27,8 @@ source_set("service") { ...@@ -26,6 +27,8 @@ source_set("service") {
sources = [ sources = [
"message_dispatcher.cc", "message_dispatcher.cc",
"message_dispatcher.h", "message_dispatcher.h",
"mirror_settings.cc",
"mirror_settings.h",
"receiver_response.cc", "receiver_response.cc",
"receiver_response.h", "receiver_response.h",
"rtp_stream.cc", "rtp_stream.cc",
...@@ -46,6 +49,7 @@ source_set("service") { ...@@ -46,6 +49,7 @@ source_set("service") {
deps = [ deps = [
":interface", ":interface",
"//crypto",
"//media", "//media",
"//media/capture/mojom:video_capture", "//media/capture/mojom:video_capture",
"//media/cast:common", "//media/cast:common",
......
include_rules = [ include_rules = [
"+crypto",
"+net", "+net",
"+services/network/public/mojom", "+services/network/public/mojom",
"+services/network/test", "+services/network/test",
......
// Copyright 2018 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 "components/mirroring/service/interface.h"
namespace mirroring {
CastSinkInfo::CastSinkInfo() {}
CastSinkInfo::~CastSinkInfo() {}
CastSinkInfo::CastSinkInfo(const CastSinkInfo& sink_info) = default;
} // namespace mirroring
...@@ -6,7 +6,6 @@ ...@@ -6,7 +6,6 @@
#define COMPONENTS_MIRRORING_SERVICE_INTERFACE_H_ #define COMPONENTS_MIRRORING_SERVICE_INTERFACE_H_
#include <string> #include <string>
#include <vector>
#include "base/callback.h" #include "base/callback.h"
#include "base/values.h" #include "base/values.h"
...@@ -22,14 +21,22 @@ namespace mirroring { ...@@ -22,14 +21,22 @@ namespace mirroring {
// Errors occurred in a mirroring session. // Errors occurred in a mirroring session.
enum SessionError { enum SessionError {
SESSION_START_ERROR, // Error occurred while starting. ANSWER_TIME_OUT, // ANSWER timeout.
AUDIO_CAPTURE_ERROR, // Error occurred in audio capturing. ANSWER_NOT_OK, // Not OK answer response.
VIDEO_CAPTURE_ERROR, // Error occurred in video capturing. ANSWER_MISMATCHED_CAST_MODE, // ANSWER cast mode mismatched.
CAST_STREAMING_ERROR, // Error occurred in cast streaming. ANSWER_MISMATCHED_SSRC_LENGTH, // ANSWER ssrc length mismatched with indexes.
CAST_TRANSPORT_ERROR, // Error occurred in cast transport. ANSWER_SELECT_MULTIPLE_AUDIO, // Multiple audio streams selected by ANSWER.
ANSWER_SELECT_MULTIPLE_VIDEO, // Multiple video streams selected by ANSWER.
ANSWER_SELECT_INVALID_INDEX, // Invalid index was selected.
ANSWER_NO_AUDIO_OR_VIDEO, // ANSWER not select audio or video.
AUDIO_CAPTURE_ERROR, // Error occurred in audio capturing.
VIDEO_CAPTURE_ERROR, // Error occurred in video capturing.
RTP_STREAM_ERROR, // Error reported by RtpStream.
ENCODING_ERROR, // Error occurred in encoding.
CAST_TRANSPORT_ERROR, // Error occurred in cast transport.
}; };
enum SessionType { enum DeviceCapability {
AUDIO_ONLY, AUDIO_ONLY,
VIDEO_ONLY, VIDEO_ONLY,
AUDIO_AND_VIDEO, AUDIO_AND_VIDEO,
...@@ -49,9 +56,20 @@ class CastMessageChannel { ...@@ -49,9 +56,20 @@ class CastMessageChannel {
virtual void Send(const CastMessage& message) = 0; virtual void Send(const CastMessage& message) = 0;
}; };
class SessionClient { struct CastSinkInfo {
CastSinkInfo();
~CastSinkInfo();
CastSinkInfo(const CastSinkInfo& sink_info);
net::IPAddress ip_address;
std::string model_name;
std::string friendly_name;
DeviceCapability capability;
};
class SessionObserver {
public: public:
virtual ~SessionClient() {} virtual ~SessionObserver() {}
// Called when error occurred. The session will be stopped. // Called when error occurred. The session will be stopped.
virtual void OnError(SessionError error) = 0; virtual void OnError(SessionError error) = 0;
...@@ -61,25 +79,19 @@ class SessionClient { ...@@ -61,25 +79,19 @@ class SessionClient {
// Called when the session is stopped. // Called when the session is stopped.
virtual void DidStop() = 0; virtual void DidStop() = 0;
};
class ResourceProvider {
public:
virtual ~ResourceProvider() {}
virtual void GetVideoCaptureHost( virtual void GetVideoCaptureHost(
media::mojom::VideoCaptureHostRequest request) = 0; media::mojom::VideoCaptureHostRequest request) = 0;
virtual void GetNetWorkContext( virtual void GetNetworkContext(
network::mojom::NetworkContextRequest request) = 0; network::mojom::NetworkContextRequest request) = 0;
// TODO(xjz): Add interface to get AudioCaptureHost. // TODO(xjz): Add interface to get AudioCaptureHost.
// TODO(xjz): Add interface for HW encoder profiles query and VEA create // TODO(xjz): Add interface for HW encoder profiles query and VEA create
// support. // support.
// TODO(xjz): Change this with an interface to send/receive messages to/from
// receiver through cast channel, and generate/parse the OFFER/ANSWER message
// in Mirroing service.
using GetAnswerCallback = base::OnceCallback<void(
const media::cast::FrameSenderConfig& audio_config,
const media::cast::FrameSenderConfig& video_config)>;
virtual void DoOfferAnswerExchange(
const std::vector<media::cast::FrameSenderConfig>& audio_configs,
const std::vector<media::cast::FrameSenderConfig>& video_configs,
GetAnswerCallback callback) = 0;
}; };
} // namespace mirroring } // namespace mirroring
......
// Copyright 2018 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 "components/mirroring/service/mirror_settings.h"
#include <algorithm>
using media::cast::FrameSenderConfig;
using media::cast::Codec;
using media::cast::RtpPayloadType;
namespace mirroring {
namespace {
// Starting end-to-end latency for animated content.
constexpr base::TimeDelta kAnimatedPlayoutDelay =
base::TimeDelta::FromMilliseconds(400);
// Minimum end-to-end latency. This allows cast streaming to adaptively lower
// latency in interactive streaming scenarios.
// TODO(miu): This was 120 before stable launch, but we got user feedback that
// this was causing audio drop-outs. So, we need to fix the Cast Streaming
// implementation before lowering this setting.
constexpr base::TimeDelta kMinPlayoutDelay =
base::TimeDelta::FromMilliseconds(400);
// Maximum end-to-end latency.
constexpr base::TimeDelta kMaxPlayoutDelay =
base::TimeDelta::FromMilliseconds(800);
constexpr int kAudioTimebase = 48000;
constexpr int kVidoTimebase = 90000;
constexpr int kAudioChannels = 2;
constexpr int kAudioFramerate = 100; // 100 FPS for 10ms packets.
constexpr int kMinVideoBitrate = 300000;
constexpr int kMaxVideoBitrate = 5000000;
constexpr int kAudioBitrate = 0; // 0 means automatic.
constexpr int kMaxFrameRate = 30; // The maximum frame rate for captures.
constexpr int kMaxWidth = 1920; // Maximum video width in pixels.
constexpr int kMaxHeight = 1080; // Maximum video height in pixels.
constexpr int kMinWidth = 180; // Minimum video frame width in pixels.
constexpr int kMinHeight = 180; // Minimum video frame height in pixels.
} // namespace
MirrorSettings::MirrorSettings()
: min_width_(kMinWidth),
min_height_(kMinHeight),
max_width_(kMaxWidth),
max_height_(kMaxHeight) {}
MirrorSettings::~MirrorSettings() {}
// static
FrameSenderConfig MirrorSettings::GetDefaultAudioConfig(
RtpPayloadType payload_type,
Codec codec) {
FrameSenderConfig config;
config.sender_ssrc = 1;
config.receiver_ssrc = 2;
config.min_playout_delay = kMinPlayoutDelay;
config.max_playout_delay = kMaxPlayoutDelay;
config.animated_playout_delay = kAnimatedPlayoutDelay;
config.rtp_payload_type = payload_type;
config.rtp_timebase = kAudioTimebase;
config.channels = kAudioChannels;
config.min_bitrate = config.max_bitrate = config.start_bitrate =
kAudioBitrate;
config.max_frame_rate = kAudioFramerate; // 10 ms audio frames
config.codec = codec;
return config;
}
// static
FrameSenderConfig MirrorSettings::GetDefaultVideoConfig(
RtpPayloadType payload_type,
Codec codec) {
FrameSenderConfig config;
config.sender_ssrc = 11;
config.receiver_ssrc = 12;
config.min_playout_delay = kMinPlayoutDelay;
config.max_playout_delay = kMaxPlayoutDelay;
config.animated_playout_delay = kAnimatedPlayoutDelay;
config.rtp_payload_type = payload_type;
config.rtp_timebase = kVidoTimebase;
config.channels = 1;
config.min_bitrate = kMinVideoBitrate;
config.max_bitrate = kMaxVideoBitrate;
config.start_bitrate = kMinVideoBitrate;
config.max_frame_rate = kMaxFrameRate;
config.codec = codec;
return config;
}
void MirrorSettings::SetResolutionContraints(int max_width, int max_height) {
max_width_ = std::max(max_width, min_width_);
max_height_ = std::max(max_height, min_height_);
}
} // namespace mirroring
// Copyright 2018 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 COMPONENTS_MIRRORING_SERVICE_MIRROR_SETTINGS_H_
#define COMPONENTS_MIRRORING_SERVICE_MIRROR_SETTINGS_H_
#include "base/time/time.h"
#include "media/cast/cast_config.h"
namespace mirroring {
// Holds the default settings for a mirroring session. This class provides the
// audio/video configs that this sender supports. And also provides the
// audio/video constraints used for capturing.
// TODO(xjz): Add the function to generate the audio/video contraints for
// capturing.
// TODO(xjz): Add setters to the settings that might be overriden by integration
// tests.
class MirrorSettings {
public:
MirrorSettings();
~MirrorSettings();
// Get the audio/video config with given codec.
static media::cast::FrameSenderConfig GetDefaultAudioConfig(
media::cast::RtpPayloadType payload_type,
media::cast::Codec codec);
static media::cast::FrameSenderConfig GetDefaultVideoConfig(
media::cast::RtpPayloadType payload_type,
media::cast::Codec codec);
// Call to override the default resolution settings.
void SetResolutionContraints(int max_width, int max_height);
int max_width() const { return max_width_; }
int max_height() const { return max_height_; }
private:
const int min_width_;
const int min_height_;
int max_width_;
int max_height_;
DISALLOW_COPY_AND_ASSIGN(MirrorSettings);
};
} // namespace mirroring
#endif // COMPONENTS_MIRRORING_SERVICE_MIRROR_SETTINGS_H_
...@@ -8,7 +8,6 @@ ...@@ -8,7 +8,6 @@
#include "base/logging.h" #include "base/logging.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/values.h" #include "base/values.h"
#include "build/build_config.h"
#include "media/base/video_frame.h" #include "media/base/video_frame.h"
#include "media/cast/cast_config.h" #include "media/cast/cast_config.h"
#include "media/cast/sender/audio_sender.h" #include "media/cast/sender/audio_sender.h"
...@@ -30,81 +29,6 @@ constexpr base::TimeDelta kRefreshInterval = ...@@ -30,81 +29,6 @@ constexpr base::TimeDelta kRefreshInterval =
// limit (60 * 250ms = 15 seconds), refresh frame requests will stop being made. // limit (60 * 250ms = 15 seconds), refresh frame requests will stop being made.
constexpr int kMaxConsecutiveRefreshFrames = 60; constexpr int kMaxConsecutiveRefreshFrames = 60;
FrameSenderConfig DefaultOpusConfig() {
FrameSenderConfig config;
config.rtp_payload_type = RtpPayloadType::AUDIO_OPUS;
config.sender_ssrc = 1;
config.receiver_ssrc = 2;
config.rtp_timebase = media::cast::kDefaultAudioSamplingRate;
config.channels = 2;
config.min_bitrate = config.max_bitrate = config.start_bitrate =
media::cast::kDefaultAudioEncoderBitrate;
config.max_frame_rate = 100; // 10 ms audio frames
config.codec = media::cast::CODEC_AUDIO_OPUS;
return config;
}
FrameSenderConfig DefaultVp8Config() {
FrameSenderConfig config;
config.rtp_payload_type = RtpPayloadType::VIDEO_VP8;
config.sender_ssrc = 11;
config.receiver_ssrc = 12;
config.rtp_timebase = media::cast::kVideoFrequency;
config.channels = 1;
config.max_bitrate = media::cast::kDefaultMaxVideoBitrate;
config.min_bitrate = media::cast::kDefaultMinVideoBitrate;
config.max_frame_rate = media::cast::kDefaultMaxFrameRate;
config.codec = media::cast::CODEC_VIDEO_VP8;
return config;
}
FrameSenderConfig DefaultH264Config() {
FrameSenderConfig config;
config.rtp_payload_type = RtpPayloadType::VIDEO_H264;
config.sender_ssrc = 11;
config.receiver_ssrc = 12;
config.rtp_timebase = media::cast::kVideoFrequency;
config.channels = 1;
config.max_bitrate = media::cast::kDefaultMaxVideoBitrate;
config.min_bitrate = media::cast::kDefaultMinVideoBitrate;
config.max_frame_rate = media::cast::kDefaultMaxFrameRate;
config.codec = media::cast::CODEC_VIDEO_H264;
return config;
}
bool IsHardwareVP8EncodingSupported(RtpStreamClient* client) {
// Query for hardware VP8 encoder support.
const std::vector<media::VideoEncodeAccelerator::SupportedProfile>
vea_profiles = client->GetSupportedVideoEncodeAcceleratorProfiles();
for (const auto& vea_profile : vea_profiles) {
if (vea_profile.profile >= media::VP8PROFILE_MIN &&
vea_profile.profile <= media::VP8PROFILE_MAX) {
return true;
}
}
return false;
}
bool IsHardwareH264EncodingSupported(RtpStreamClient* client) {
// Query for hardware H.264 encoder support.
//
// TODO(miu): Look into why H.264 hardware encoder on MacOS is broken.
// http://crbug.com/596674
// TODO(emircan): Look into HW encoder initialization issues on Win.
// https://crbug.com/636064
#if !defined(OS_MACOSX) && !defined(OS_WIN)
const std::vector<media::VideoEncodeAccelerator::SupportedProfile>
vea_profiles = client->GetSupportedVideoEncodeAcceleratorProfiles();
for (const auto& vea_profile : vea_profiles) {
if (vea_profile.profile >= media::H264PROFILE_MIN &&
vea_profile.profile <= media::H264PROFILE_MAX) {
return true;
}
}
#endif // !defined(OS_MACOSX) && !defined(OS_WIN)
return false;
}
} // namespace } // namespace
VideoRtpStream::VideoRtpStream( VideoRtpStream::VideoRtpStream(
...@@ -125,24 +49,6 @@ VideoRtpStream::VideoRtpStream( ...@@ -125,24 +49,6 @@ VideoRtpStream::VideoRtpStream(
VideoRtpStream::~VideoRtpStream() {} VideoRtpStream::~VideoRtpStream() {}
// static
std::vector<FrameSenderConfig> VideoRtpStream::GetSupportedConfigs(
RtpStreamClient* client) {
std::vector<FrameSenderConfig> supported_configs;
// Prefer VP8 over H.264 for hardware encoder.
if (IsHardwareVP8EncodingSupported(client))
supported_configs.push_back(DefaultVp8Config());
if (IsHardwareH264EncodingSupported(client))
supported_configs.push_back(DefaultH264Config());
// Propose the default software VP8 encoder, if no hardware encoders are
// available.
if (supported_configs.empty())
supported_configs.push_back(DefaultVp8Config());
return supported_configs;
}
void VideoRtpStream::InsertVideoFrame( void VideoRtpStream::InsertVideoFrame(
scoped_refptr<media::VideoFrame> video_frame) { scoped_refptr<media::VideoFrame> video_frame) {
DCHECK(client_); DCHECK(client_);
...@@ -205,11 +111,6 @@ AudioRtpStream::AudioRtpStream( ...@@ -205,11 +111,6 @@ AudioRtpStream::AudioRtpStream(
AudioRtpStream::~AudioRtpStream() {} AudioRtpStream::~AudioRtpStream() {}
// static
std::vector<FrameSenderConfig> AudioRtpStream::GetSupportedConfigs() {
return {DefaultOpusConfig()};
}
void AudioRtpStream::InsertAudio(std::unique_ptr<media::AudioBus> audio_bus, void AudioRtpStream::InsertAudio(std::unique_ptr<media::AudioBus> audio_bus,
base::TimeTicks capture_time) { base::TimeTicks capture_time) {
audio_sender_->InsertAudio(std::move(audio_bus), capture_time); audio_sender_->InsertAudio(std::move(audio_bus), capture_time);
......
...@@ -44,10 +44,6 @@ class RtpStreamClient { ...@@ -44,10 +44,6 @@ class RtpStreamClient {
// The following are for hardware video encoding. // The following are for hardware video encoding.
// Query the supported hardware encoding profiles.
virtual media::VideoEncodeAccelerator::SupportedProfiles
GetSupportedVideoEncodeAcceleratorProfiles() = 0;
virtual void CreateVideoEncodeAccelerator( virtual void CreateVideoEncodeAccelerator(
const media::cast::ReceiveVideoEncodeAcceleratorCallback& callback) = 0; const media::cast::ReceiveVideoEncodeAcceleratorCallback& callback) = 0;
...@@ -71,9 +67,6 @@ class VideoRtpStream { ...@@ -71,9 +67,6 @@ class VideoRtpStream {
base::WeakPtr<RtpStreamClient> client); base::WeakPtr<RtpStreamClient> client);
~VideoRtpStream(); ~VideoRtpStream();
static std::vector<media::cast::FrameSenderConfig> GetSupportedConfigs(
RtpStreamClient* client);
// Called by VideoCaptureClient when a video frame is received. // Called by VideoCaptureClient when a video frame is received.
// |video_frame| is required to provide REFERENCE_TIME in the metadata. // |video_frame| is required to provide REFERENCE_TIME in the metadata.
void InsertVideoFrame(scoped_refptr<media::VideoFrame> video_frame); void InsertVideoFrame(scoped_refptr<media::VideoFrame> video_frame);
...@@ -115,8 +108,6 @@ class AudioRtpStream { ...@@ -115,8 +108,6 @@ class AudioRtpStream {
base::WeakPtr<RtpStreamClient> client); base::WeakPtr<RtpStreamClient> client);
~AudioRtpStream(); ~AudioRtpStream();
static std::vector<media::cast::FrameSenderConfig> GetSupportedConfigs();
// Called by AudioCaptureClient when new audio data is available. // Called by AudioCaptureClient when new audio data is available.
void InsertAudio(std::unique_ptr<media::AudioBus> audio_bus, void InsertAudio(std::unique_ptr<media::AudioBus> audio_bus,
base::TimeTicks estimated_capture_time); base::TimeTicks estimated_capture_time);
......
...@@ -43,10 +43,6 @@ class DummyClient final : public RtpStreamClient { ...@@ -43,10 +43,6 @@ class DummyClient final : public RtpStreamClient {
void CreateVideoEncodeMemory( void CreateVideoEncodeMemory(
size_t size, size_t size,
const media::cast::ReceiveVideoEncodeMemoryCallback& callback) override {} const media::cast::ReceiveVideoEncodeMemoryCallback& callback) override {}
media::VideoEncodeAccelerator::SupportedProfiles
GetSupportedVideoEncodeAcceleratorProfiles() override {
return media::VideoEncodeAccelerator::SupportedProfiles();
}
base::WeakPtr<RtpStreamClient> GetWeakPtr() { base::WeakPtr<RtpStreamClient> GetWeakPtr() {
return weak_factory_.GetWeakPtr(); return weak_factory_.GetWeakPtr();
......
This diff is collapsed.
...@@ -8,6 +8,8 @@ ...@@ -8,6 +8,8 @@
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "base/single_thread_task_runner.h" #include "base/single_thread_task_runner.h"
#include "components/mirroring/service/interface.h" #include "components/mirroring/service/interface.h"
#include "components/mirroring/service/message_dispatcher.h"
#include "components/mirroring/service/mirror_settings.h"
#include "components/mirroring/service/rtp_stream.h" #include "components/mirroring/service/rtp_stream.h"
#include "media/cast/cast_environment.h" #include "media/cast/cast_environment.h"
#include "media/cast/net/cast_transport_defines.h" #include "media/cast/net/cast_transport_defines.h"
...@@ -22,20 +24,31 @@ class CastTransport; ...@@ -22,20 +24,31 @@ class CastTransport;
namespace mirroring { namespace mirroring {
struct ReceiverResponse;
class VideoCaptureClient; class VideoCaptureClient;
// Controls a mirroring session, including audio/video capturing and Cast
// Streaming. When constructed, it does OFFER/ANSWER exchange with the mirroring
// receiver. Mirroring starts when the exchange succeeds and stops when this
// class is destructed or error occurs. |observer| will get notified when status
// changes. |outbound_channel| is responsible for sending messages to the
// mirroring receiver through Cast Channel.
class Session final : public RtpStreamClient { class Session final : public RtpStreamClient {
public: public:
Session(SessionType session_type, Session(int32_t session_id,
const net::IPEndPoint& receiver_endpoint, const CastSinkInfo& sink_info,
SessionClient* client); const gfx::Size& max_resolution,
SessionObserver* observer,
ResourceProvider* resource_provider,
CastMessageChannel* outbound_channel);
// TODO(xjz): Add mojom::CastMessageChannelRequest |inbound_channel| to
// receive inbound messages.
~Session() override; ~Session() override;
// RtpStreamClient implemenation. // RtpStreamClient implemenation.
void OnError(const std::string& message) override; void OnError(const std::string& message) override;
void RequestRefreshFrame() override; void RequestRefreshFrame() override;
media::VideoEncodeAccelerator::SupportedProfiles
GetSupportedVideoEncodeAcceleratorProfiles() override;
void CreateVideoEncodeAccelerator( void CreateVideoEncodeAccelerator(
const media::cast::ReceiveVideoEncodeAcceleratorCallback& callback) const media::cast::ReceiveVideoEncodeAcceleratorCallback& callback)
override; override;
...@@ -49,16 +62,23 @@ class Session final : public RtpStreamClient { ...@@ -49,16 +62,23 @@ class Session final : public RtpStreamClient {
std::unique_ptr<std::vector<media::cast::FrameEvent>> frame_events, std::unique_ptr<std::vector<media::cast::FrameEvent>> frame_events,
std::unique_ptr<std::vector<media::cast::PacketEvent>> packet_events); std::unique_ptr<std::vector<media::cast::PacketEvent>> packet_events);
private: // Callback for ANSWER response. If the ANSWER is invalid, |observer_| will
// Callback when OFFER/ANSWER message exchange finishes. Starts a mirroing // get notified with error, and session is stopped. Otherwise, capturing and
// session. // streaming are started with the selected configs.
void StartInternal(const net::IPEndPoint& receiver_endpoint, void OnAnswer(
const media::cast::FrameSenderConfig& audio_config, const std::string& cast_mode,
const media::cast::FrameSenderConfig& video_config); const std::vector<media::cast::FrameSenderConfig>& audio_configs,
const std::vector<media::cast::FrameSenderConfig>& video_configs,
const ReceiverResponse& response);
// Called by |message_dispatcher_| when error occurs while parsing the
// responses.
void OnResponseParsingError(const std::string& error_message);
private:
void StopSession(); void StopSession();
// Notify |client_| that error occurred and close the session. // Notify |observer_| that error occurred and close the session.
void ReportError(SessionError error); void ReportError(SessionError error);
// Callback by Audio/VideoSender to indicate encoder status change. // Callback by Audio/VideoSender to indicate encoder status change.
...@@ -67,12 +87,22 @@ class Session final : public RtpStreamClient { ...@@ -67,12 +87,22 @@ class Session final : public RtpStreamClient {
// Callback by media::cast::VideoSender to set a new target playout delay. // Callback by media::cast::VideoSender to set a new target playout delay.
void SetTargetPlayoutDelay(base::TimeDelta playout_delay); void SetTargetPlayoutDelay(base::TimeDelta playout_delay);
// Callback by |start_timeout_timer_|. media::VideoEncodeAccelerator::SupportedProfiles GetSupportedVeaProfiles();
void OnOfferAnswerExchangeTimeout();
SessionClient* client_ = nullptr; // Create and send OFFER message.
void CreateAndSendOffer();
// Create on StartInternal(). // Provided by Cast Media Route Provider (MRP).
const int32_t session_id_;
const CastSinkInfo sink_info_;
SessionObserver* observer_ = nullptr;
ResourceProvider* resource_provider_ = nullptr;
MirrorSettings mirror_settings_;
MessageDispatcher message_dispatcher_;
// Created after OFFER/ANSWER exchange succeeds.
std::unique_ptr<AudioRtpStream> audio_stream_; std::unique_ptr<AudioRtpStream> audio_stream_;
std::unique_ptr<VideoRtpStream> video_stream_; std::unique_ptr<VideoRtpStream> video_stream_;
std::unique_ptr<VideoCaptureClient> video_capture_client_; std::unique_ptr<VideoCaptureClient> video_capture_client_;
...@@ -81,9 +111,6 @@ class Session final : public RtpStreamClient { ...@@ -81,9 +111,6 @@ class Session final : public RtpStreamClient {
scoped_refptr<base::SingleThreadTaskRunner> audio_encode_thread_ = nullptr; scoped_refptr<base::SingleThreadTaskRunner> audio_encode_thread_ = nullptr;
scoped_refptr<base::SingleThreadTaskRunner> video_encode_thread_ = nullptr; scoped_refptr<base::SingleThreadTaskRunner> video_encode_thread_ = nullptr;
// Fire if the OFFER/ANSWER exchange times out.
base::OneShotTimer start_timeout_timer_;
base::WeakPtrFactory<Session> weak_factory_; base::WeakPtrFactory<Session> weak_factory_;
}; };
......
...@@ -7,13 +7,14 @@ ...@@ -7,13 +7,14 @@
#include "base/bind.h" #include "base/bind.h"
#include "base/callback.h" #include "base/callback.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/run_loop.h" #include "base/run_loop.h"
#include "base/test/scoped_task_environment.h" #include "base/test/scoped_task_environment.h"
#include "base/test/simple_test_tick_clock.h" #include "base/test/simple_test_tick_clock.h"
#include "components/mirroring/service/fake_network_service.h" #include "components/mirroring/service/fake_network_service.h"
#include "components/mirroring/service/fake_video_capture_host.h" #include "components/mirroring/service/fake_video_capture_host.h"
#include "components/mirroring/service/interface.h" #include "components/mirroring/service/interface.h"
#include "components/mirroring/service/mirror_settings.h"
#include "components/mirroring/service/receiver_response.h"
#include "media/cast/test/utility/default_config.h" #include "media/cast/test/utility/default_config.h"
#include "media/cast/test/utility/net_utility.h" #include "media/cast/test/utility/net_utility.h"
#include "mojo/public/cpp/bindings/binding.h" #include "mojo/public/cpp/bindings/binding.h"
...@@ -27,74 +28,126 @@ using media::cast::Packet; ...@@ -27,74 +28,126 @@ using media::cast::Packet;
namespace mirroring { namespace mirroring {
class SessionTest : public SessionClient, public ::testing::Test { const int kSessionId = 5;
class SessionTest : public ResourceProvider,
public SessionObserver,
public CastMessageChannel,
public ::testing::Test {
public: public:
SessionTest() : weak_factory_(this) { SessionTest() : receiver_endpoint_(media::cast::test::GetFreeLocalPort()) {
testing_clock_.Advance(base::TimeTicks::Now() - base::TimeTicks()); testing_clock_.Advance(base::TimeTicks::Now() - base::TimeTicks());
} }
~SessionTest() override { scoped_task_environment_.RunUntilIdle(); } ~SessionTest() override { scoped_task_environment_.RunUntilIdle(); }
// SessionClient implemenation. // SessionObserver implemenation.
MOCK_METHOD1(OnError, void(SessionError)); MOCK_METHOD1(OnError, void(SessionError));
MOCK_METHOD0(DidStart, void()); MOCK_METHOD0(DidStart, void());
MOCK_METHOD0(DidStop, void()); MOCK_METHOD0(DidStop, void());
MOCK_METHOD0(OnOfferAnswerExchange, void());
// ResourceProvider implemenation.
MOCK_METHOD0(OnGetVideoCaptureHost, void()); MOCK_METHOD0(OnGetVideoCaptureHost, void());
MOCK_METHOD0(OnGetNetworkContext, void()); MOCK_METHOD0(OnGetNetworkContext, void());
// Called when sends OFFER message.
MOCK_METHOD0(OnOffer, void());
// CastMessageHandler implementation. For outbound messages.
void Send(const CastMessage& message) {
EXPECT_TRUE(message.message_namespace == kWebRtcNamespace ||
message.message_namespace == kRemotingNamespace);
std::string message_type;
auto* found = message.data.FindKey("type");
if (found && found->is_string())
message_type = found->GetString();
if (message_type == "OFFER") {
auto* found = message.data.FindKey("seqNum");
if (found && found->is_int())
offer_sequence_number_ = found->GetInt();
OnOffer();
}
}
void GetVideoCaptureHost( void GetVideoCaptureHost(
media::mojom::VideoCaptureHostRequest request) override { media::mojom::VideoCaptureHostRequest request) override {
video_host_ = std::make_unique<FakeVideoCaptureHost>(std::move(request)); video_host_ = std::make_unique<FakeVideoCaptureHost>(std::move(request));
OnGetVideoCaptureHost(); OnGetVideoCaptureHost();
} }
void GetNetWorkContext( void GetNetworkContext(
network::mojom::NetworkContextRequest request) override { network::mojom::NetworkContextRequest request) override {
network_context_ = std::make_unique<MockNetworkContext>(std::move(request)); network_context_ = std::make_unique<MockNetworkContext>(std::move(request));
OnGetNetworkContext(); OnGetNetworkContext();
} }
void DoOfferAnswerExchange( void SendAnswer() {
const std::vector<FrameSenderConfig>& audio_configs, FrameSenderConfig config = MirrorSettings::GetDefaultVideoConfig(
const std::vector<FrameSenderConfig>& video_configs, media::cast::RtpPayloadType::VIDEO_VP8,
GetAnswerCallback callback) override { media::cast::Codec::CODEC_VIDEO_VP8);
OnOfferAnswerExchange(); std::vector<FrameSenderConfig> video_configs;
std::move(callback).Run(FrameSenderConfig(), video_configs.emplace_back(config);
media::cast::GetDefaultVideoSenderConfig());
auto answer = std::make_unique<Answer>();
answer->udp_port = receiver_endpoint_.port();
answer->send_indexes.push_back(0);
answer->ssrcs.push_back(32);
answer->cast_mode = "mirroring";
ReceiverResponse response;
response.result = "ok";
response.type = ResponseType::ANSWER;
response.sequence_number = offer_sequence_number_;
response.answer = std::move(answer);
session_->OnAnswer("mirroring", std::vector<FrameSenderConfig>(),
video_configs, response);
} }
protected: protected:
void CreateSession() {
CastSinkInfo sink_info;
sink_info.ip_address = receiver_endpoint_.address();
sink_info.capability = DeviceCapability::AUDIO_AND_VIDEO;
// Expect to receive OFFER message when session is created.
base::RunLoop run_loop;
EXPECT_CALL(*this, OnError(_)).Times(0);
EXPECT_CALL(*this, OnOffer())
.WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
session_ = std::make_unique<Session>(
kSessionId, sink_info, gfx::Size(1920, 1080), this, this, this);
run_loop.Run();
}
base::test::ScopedTaskEnvironment scoped_task_environment_; base::test::ScopedTaskEnvironment scoped_task_environment_;
const net::IPEndPoint receiver_endpoint_;
base::SimpleTestTickClock testing_clock_; base::SimpleTestTickClock testing_clock_;
std::unique_ptr<Session> session_; std::unique_ptr<Session> session_;
std::unique_ptr<FakeVideoCaptureHost> video_host_; std::unique_ptr<FakeVideoCaptureHost> video_host_;
std::unique_ptr<MockNetworkContext> network_context_; std::unique_ptr<MockNetworkContext> network_context_;
base::WeakPtrFactory<SessionTest> weak_factory_; int32_t offer_sequence_number_ = -1;
private: private:
DISALLOW_COPY_AND_ASSIGN(SessionTest); DISALLOW_COPY_AND_ASSIGN(SessionTest);
}; };
TEST_F(SessionTest, Mirroring) { TEST_F(SessionTest, Mirroring) {
// Start a mirroring session. CreateSession();
scoped_task_environment_.RunUntilIdle();
{ {
// Except mirroing session starts after receiving ANSWER message.
base::RunLoop run_loop; base::RunLoop run_loop;
EXPECT_CALL(*this, OnGetVideoCaptureHost()).Times(1); EXPECT_CALL(*this, OnGetVideoCaptureHost()).Times(1);
EXPECT_CALL(*this, OnGetNetworkContext()).Times(1); EXPECT_CALL(*this, OnGetNetworkContext()).Times(1);
EXPECT_CALL(*this, OnOfferAnswerExchange()).Times(1); EXPECT_CALL(*this, OnError(_)).Times(0);
EXPECT_CALL(*this, DidStart()) EXPECT_CALL(*this, DidStart())
.WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit)); .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
session_ = SendAnswer();
std::make_unique<Session>(SessionType::AUDIO_AND_VIDEO,
media::cast::test::GetFreeLocalPort(), this);
run_loop.Run(); run_loop.Run();
} }
scoped_task_environment_.RunUntilIdle(); scoped_task_environment_.RunUntilIdle();
{ {
base::RunLoop run_loop; base::RunLoop run_loop;
// Expect to send out some UDP packets. // Expect to send out some UDP packets.
...@@ -106,7 +159,6 @@ TEST_F(SessionTest, Mirroring) { ...@@ -106,7 +159,6 @@ TEST_F(SessionTest, Mirroring) {
video_host_->SendOneFrame(gfx::Size(64, 32), testing_clock_.NowTicks()); video_host_->SendOneFrame(gfx::Size(64, 32), testing_clock_.NowTicks());
run_loop.Run(); run_loop.Run();
} }
scoped_task_environment_.RunUntilIdle(); scoped_task_environment_.RunUntilIdle();
// Stop the session. // Stop the session.
...@@ -121,4 +173,22 @@ TEST_F(SessionTest, Mirroring) { ...@@ -121,4 +173,22 @@ TEST_F(SessionTest, Mirroring) {
scoped_task_environment_.RunUntilIdle(); scoped_task_environment_.RunUntilIdle();
} }
TEST_F(SessionTest, AnswerTimeout) {
CreateSession();
scoped_task_environment_.RunUntilIdle();
{
// Expect error.
base::RunLoop run_loop;
EXPECT_CALL(*this, OnGetVideoCaptureHost()).Times(0);
EXPECT_CALL(*this, OnGetNetworkContext()).Times(0);
EXPECT_CALL(*this, DidStop()).Times(1);
EXPECT_CALL(*this, OnError(ANSWER_TIME_OUT))
.WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
session_->OnAnswer("mirroring", std::vector<FrameSenderConfig>(),
std::vector<FrameSenderConfig>(), ReceiverResponse());
run_loop.Run();
}
scoped_task_environment_.RunUntilIdle();
}
} // namespace mirroring } // namespace mirroring
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment