blob: fba9e05964828900a839662853cc606406e3f759 [file] [log] [blame]
// Copyright 2014 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 "chrome/browser/media/cast_transport_host_filter.h"
#include <utility>
#include "base/task/post_task.h"
#include "base/threading/thread_task_runner_handle.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/cast_messages.h"
#include "components/net_log/chrome_net_log.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/common/service_manager_connection.h"
#include "media/cast/net/cast_transport.h"
#include "media/cast/net/udp_transport_impl.h"
#include "mojo/public/cpp/bindings/interface_request.h"
#include "net/url_request/url_request_context.h"
#include "services/device/public/mojom/constants.mojom.h"
#include "services/device/public/mojom/wake_lock_provider.mojom.h"
#include "services/service_manager/public/cpp/connector.h"
namespace {
// The interval for CastTransport and/or CastRemotingSender to send
// Frame/PacketEvents to renderer process for logging.
constexpr base::TimeDelta kSendEventsInterval = base::TimeDelta::FromSeconds(1);
class TransportClient : public media::cast::CastTransport::Client {
public:
TransportClient(int32_t channel_id,
cast::CastTransportHostFilter* cast_transport_host_filter)
: channel_id_(channel_id),
cast_transport_host_filter_(cast_transport_host_filter) {}
void OnStatusChanged(media::cast::CastTransportStatus status) final;
void OnLoggingEventsReceived(
std::unique_ptr<std::vector<media::cast::FrameEvent>> frame_events,
std::unique_ptr<std::vector<media::cast::PacketEvent>> packet_events)
final;
void ProcessRtpPacket(std::unique_ptr<media::cast::Packet> packet) final;
private:
const int32_t channel_id_;
cast::CastTransportHostFilter* const cast_transport_host_filter_;
DISALLOW_COPY_AND_ASSIGN(TransportClient);
};
void TransportClient::OnStatusChanged(media::cast::CastTransportStatus status) {
cast_transport_host_filter_->Send(
new CastMsg_NotifyStatusChange(channel_id_, status));
}
void TransportClient::OnLoggingEventsReceived(
std::unique_ptr<std::vector<media::cast::FrameEvent>> frame_events,
std::unique_ptr<std::vector<media::cast::PacketEvent>> packet_events) {
if (frame_events->empty() && packet_events->empty())
return;
cast_transport_host_filter_->Send(
new CastMsg_RawEvents(channel_id_, *packet_events, *frame_events));
}
void TransportClient::ProcessRtpPacket(
std::unique_ptr<media::cast::Packet> packet) {
cast_transport_host_filter_->Send(
new CastMsg_ReceivedPacket(channel_id_, *packet));
}
class RtcpClient : public media::cast::RtcpObserver {
public:
RtcpClient(
int32_t channel_id,
uint32_t rtp_sender_ssrc,
base::WeakPtr<cast::CastTransportHostFilter> cast_transport_host_filter)
: channel_id_(channel_id),
rtp_sender_ssrc_(rtp_sender_ssrc),
cast_transport_host_filter_(cast_transport_host_filter) {}
void OnReceivedCastMessage(
const media::cast::RtcpCastMessage& cast_message) override {
if (cast_transport_host_filter_)
cast_transport_host_filter_->Send(new CastMsg_RtcpCastMessage(
channel_id_, rtp_sender_ssrc_, cast_message));
}
void OnReceivedRtt(base::TimeDelta round_trip_time) override {
if (cast_transport_host_filter_)
cast_transport_host_filter_->Send(
new CastMsg_Rtt(channel_id_, rtp_sender_ssrc_, round_trip_time));
}
void OnReceivedPli() override {
if (cast_transport_host_filter_)
cast_transport_host_filter_->Send(
new CastMsg_Pli(channel_id_, rtp_sender_ssrc_));
}
private:
const int32_t channel_id_;
const uint32_t rtp_sender_ssrc_;
const base::WeakPtr<cast::CastTransportHostFilter>
cast_transport_host_filter_;
DISALLOW_COPY_AND_ASSIGN(RtcpClient);
};
void CastBindConnectorRequest(
service_manager::mojom::ConnectorRequest connector_request) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(content::ServiceManagerConnection::GetForProcess());
content::ServiceManagerConnection::GetForProcess()
->GetConnector()
->BindConnectorRequest(std::move(connector_request));
}
} // namespace
namespace cast {
CastTransportHostFilter::CastTransportHostFilter(Profile* profile)
: BrowserMessageFilter(CastMsgStart),
url_request_context_getter_(profile->GetRequestContext()),
weak_factory_(this) {}
CastTransportHostFilter::~CastTransportHostFilter() {}
void CastTransportHostFilter::OnStatusChanged(
int32_t channel_id,
media::cast::CastTransportStatus status) {
Send(new CastMsg_NotifyStatusChange(channel_id, status));
}
bool CastTransportHostFilter::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(CastTransportHostFilter, message)
IPC_MESSAGE_HANDLER(CastHostMsg_New, OnNew)
IPC_MESSAGE_HANDLER(CastHostMsg_Delete, OnDelete)
IPC_MESSAGE_HANDLER(CastHostMsg_InitializeStream, OnInitializeStream)
IPC_MESSAGE_HANDLER(CastHostMsg_InsertFrame, OnInsertFrame)
IPC_MESSAGE_HANDLER(CastHostMsg_SendSenderReport,
OnSendSenderReport)
IPC_MESSAGE_HANDLER(CastHostMsg_ResendFrameForKickstart,
OnResendFrameForKickstart)
IPC_MESSAGE_HANDLER(CastHostMsg_CancelSendingFrames,
OnCancelSendingFrames)
IPC_MESSAGE_HANDLER(CastHostMsg_AddValidRtpReceiver, OnAddValidRtpReceiver)
IPC_MESSAGE_HANDLER(CastHostMsg_InitializeRtpReceiverRtcpBuilder,
OnInitializeRtpReceiverRtcpBuilder)
IPC_MESSAGE_HANDLER(CastHostMsg_AddCastFeedback, OnAddCastFeedback)
IPC_MESSAGE_HANDLER(CastHostMsg_AddPli, OnAddPli)
IPC_MESSAGE_HANDLER(CastHostMsg_AddRtcpEvents, OnAddRtcpEvents)
IPC_MESSAGE_HANDLER(CastHostMsg_AddRtpReceiverReport,
OnAddRtpReceiverReport)
IPC_MESSAGE_HANDLER(CastHostMsg_SendRtcpFromRtpReceiver,
OnSendRtcpFromRtpReceiver)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
void CastTransportHostFilter::OnNew(int32_t channel_id,
const net::IPEndPoint& local_end_point,
const net::IPEndPoint& remote_end_point,
const base::DictionaryValue& options) {
if (id_map_.IsEmpty()) {
DVLOG(1) << ("Preventing the application from being suspended while one or "
"more transports are active for Cast Streaming.");
GetWakeLock()->RequestWakeLock();
}
if (id_map_.Lookup(channel_id)) {
id_map_.Remove(channel_id);
}
auto udp_transport = std::make_unique<media::cast::UdpTransportImpl>(
url_request_context_getter_->GetURLRequestContext()->net_log(),
base::ThreadTaskRunnerHandle::Get(), local_end_point, remote_end_point,
base::BindRepeating(&CastTransportHostFilter::OnStatusChanged,
weak_factory_.GetWeakPtr(), channel_id));
udp_transport->SetUdpOptions(options);
std::unique_ptr<media::cast::CastTransport> transport =
media::cast::CastTransport::Create(
base::DefaultTickClock::GetInstance(), kSendEventsInterval,
std::make_unique<TransportClient>(channel_id, this),
std::move(udp_transport), base::ThreadTaskRunnerHandle::Get());
transport->SetOptions(options);
id_map_.AddWithID(std::move(transport), channel_id);
}
void CastTransportHostFilter::OnDelete(int32_t channel_id) {
media::cast::CastTransport* transport = id_map_.Lookup(channel_id);
if (transport) {
id_map_.Remove(channel_id);
} else {
DVLOG(1) << "CastTransportHostFilter::Delete called "
<< "on non-existing channel";
}
// Delete all existing remoting senders for this channel.
const auto entries = stream_id_map_.equal_range(channel_id);
for (auto it = entries.first; it != entries.second; ++it) {
if (remoting_sender_map_.Lookup(it->second)) {
DVLOG(3) << "Delete CastRemotingSender for stream: " << it->second;
remoting_sender_map_.Remove(it->second);
}
}
stream_id_map_.erase(channel_id);
if (id_map_.IsEmpty()) {
DVLOG(1)
<< ("Releasing the block on application suspension since no transports "
"are active anymore for Cast Streaming.");
GetWakeLock()->CancelWakeLock();
}
}
void CastTransportHostFilter::OnInitializeStream(
int32_t channel_id,
const media::cast::CastTransportRtpConfig& config) {
media::cast::CastTransport* transport = id_map_.Lookup(channel_id);
if (transport) {
if (config.rtp_payload_type == media::cast::RtpPayloadType::REMOTE_AUDIO ||
config.rtp_payload_type == media::cast::RtpPayloadType::REMOTE_VIDEO) {
// Create CastRemotingSender for this RTP stream.
remoting_sender_map_.AddWithID(
std::make_unique<mirroring::CastRemotingSender>(
transport, config, kSendEventsInterval,
base::BindRepeating(
&CastTransportHostFilter::OnCastRemotingSenderEvents,
weak_factory_.GetWeakPtr(), channel_id)),
config.rtp_stream_id);
DVLOG(3) << "Create CastRemotingSender for stream: "
<< config.rtp_stream_id;
stream_id_map_.insert(std::make_pair(channel_id, config.rtp_stream_id));
} else {
transport->InitializeStream(
config, std::make_unique<RtcpClient>(channel_id, config.ssrc,
weak_factory_.GetWeakPtr()));
}
} else {
DVLOG(1) << "CastTransportHostFilter::OnInitializeStream on non-existing "
"channel";
}
}
void CastTransportHostFilter::OnInsertFrame(
int32_t channel_id,
uint32_t ssrc,
const media::cast::EncodedFrame& frame) {
media::cast::CastTransport* transport = id_map_.Lookup(channel_id);
if (transport) {
transport->InsertFrame(ssrc, frame);
} else {
DVLOG(1)
<< "CastTransportHostFilter::OnInsertFrame on non-existing channel";
}
}
void CastTransportHostFilter::OnCancelSendingFrames(
int32_t channel_id,
uint32_t ssrc,
const std::vector<media::cast::FrameId>& frame_ids) {
media::cast::CastTransport* transport = id_map_.Lookup(channel_id);
if (transport) {
transport->CancelSendingFrames(ssrc, frame_ids);
} else {
DVLOG(1)
<< "CastTransportHostFilter::OnCancelSendingFrames "
<< "on non-existing channel";
}
}
void CastTransportHostFilter::OnResendFrameForKickstart(
int32_t channel_id,
uint32_t ssrc,
media::cast::FrameId frame_id) {
media::cast::CastTransport* transport = id_map_.Lookup(channel_id);
if (transport) {
transport->ResendFrameForKickstart(ssrc, frame_id);
} else {
DVLOG(1)
<< "CastTransportHostFilter::OnResendFrameForKickstart "
<< "on non-existing channel";
}
}
void CastTransportHostFilter::OnSendSenderReport(
int32_t channel_id,
uint32_t ssrc,
base::TimeTicks current_time,
media::cast::RtpTimeTicks current_time_as_rtp_timestamp) {
media::cast::CastTransport* transport = id_map_.Lookup(channel_id);
if (transport) {
transport->SendSenderReport(ssrc, current_time,
current_time_as_rtp_timestamp);
} else {
DVLOG(1)
<< "CastTransportHostFilter::OnSendSenderReport "
<< "on non-existing channel";
}
}
void CastTransportHostFilter::OnAddValidRtpReceiver(
int32_t channel_id,
uint32_t rtp_sender_ssrc,
uint32_t rtp_receiver_ssrc) {
media::cast::CastTransport* transport = id_map_.Lookup(channel_id);
if (transport) {
transport->AddValidRtpReceiver(rtp_sender_ssrc, rtp_receiver_ssrc);
} else {
DVLOG(1) << "CastTransportHostFilter::OnAddValidRtpReceiver "
<< "on non-existing channel";
}
}
void CastTransportHostFilter::OnInitializeRtpReceiverRtcpBuilder(
int32_t channel_id,
uint32_t rtp_receiver_ssrc,
const media::cast::RtcpTimeData& time_data) {
media::cast::CastTransport* transport = id_map_.Lookup(channel_id);
if (transport) {
transport->InitializeRtpReceiverRtcpBuilder(rtp_receiver_ssrc, time_data);
} else {
DVLOG(1) << "CastTransportHostFilter::OnInitializeRtpReceiverRtcpBuilder "
<< "on non-existing channel";
}
}
void CastTransportHostFilter::OnAddCastFeedback(
int32_t channel_id,
const media::cast::RtcpCastMessage& cast_message,
base::TimeDelta target_delay) {
media::cast::CastTransport* transport = id_map_.Lookup(channel_id);
if (transport) {
transport->AddCastFeedback(cast_message, target_delay);
} else {
DVLOG(1) << "CastTransportHostFilter::OnAddCastFeedback "
<< "on non-existing channel";
}
}
void CastTransportHostFilter::OnAddPli(
int32_t channel_id,
const media::cast::RtcpPliMessage& pli_message) {
media::cast::CastTransport* transport = id_map_.Lookup(channel_id);
if (transport) {
transport->AddPli(pli_message);
} else {
DVLOG(1) << "CastTransportHostFilter::OnAddPli on non-existing channel";
}
}
void CastTransportHostFilter::OnAddRtcpEvents(
int32_t channel_id,
const media::cast::ReceiverRtcpEventSubscriber::RtcpEvents& rtcp_events) {
media::cast::CastTransport* transport = id_map_.Lookup(channel_id);
if (transport) {
transport->AddRtcpEvents(rtcp_events);
} else {
DVLOG(1) << "CastTransportHostFilter::OnAddRtcpEvents "
<< "on non-existing channel";
}
}
void CastTransportHostFilter::OnAddRtpReceiverReport(
int32_t channel_id,
const media::cast::RtcpReportBlock& rtp_receiver_report_block) {
media::cast::CastTransport* transport = id_map_.Lookup(channel_id);
if (transport) {
transport->AddRtpReceiverReport(rtp_receiver_report_block);
} else {
DVLOG(1) << "CastTransportHostFilter::OnAddRtpReceiverReport "
<< "on non-existing channel";
}
}
void CastTransportHostFilter::OnSendRtcpFromRtpReceiver(int32_t channel_id) {
media::cast::CastTransport* transport = id_map_.Lookup(channel_id);
if (transport) {
transport->SendRtcpFromRtpReceiver();
} else {
DVLOG(1)
<< "CastTransportHostFilter::OnSendRtcpFromRtpReceiver "
<< "on non-existing channel";
}
}
void CastTransportHostFilter::OnCastRemotingSenderEvents(
int32_t channel_id,
const std::vector<media::cast::FrameEvent>& events) {
if (events.empty())
return;
// PacketEvents can only come from CastTransport via CastTransport::Client
// interface.
Send(new CastMsg_RawEvents(channel_id,
std::vector<media::cast::PacketEvent>(), events));
}
device::mojom::WakeLock* CastTransportHostFilter::GetWakeLock() {
// Here is a lazy binding, and will not reconnect after connection error.
if (wake_lock_)
return wake_lock_.get();
device::mojom::WakeLockRequest request = mojo::MakeRequest(&wake_lock_);
DCHECK(content::ServiceManagerConnection::GetForProcess());
service_manager::mojom::ConnectorRequest connector_request;
auto connector = service_manager::Connector::Create(&connector_request);
base::PostTaskWithTraits(
FROM_HERE, {content::BrowserThread::UI},
base::BindOnce(&CastBindConnectorRequest, std::move(connector_request)));
device::mojom::WakeLockProviderPtr wake_lock_provider;
connector->BindInterface(device::mojom::kServiceName,
mojo::MakeRequest(&wake_lock_provider));
wake_lock_provider->GetWakeLockWithoutContext(
device::mojom::WakeLockType::kPreventAppSuspension,
device::mojom::WakeLockReason::kOther,
"Cast is streaming content to a remote receiver", std::move(request));
return wake_lock_.get();
}
void CastTransportHostFilter::InitializeNoOpWakeLockForTesting() {
// Initializes |wake_lock_| to make GetWakeLock() short-circuit out of its
// own lazy initialization process.
mojo::MakeRequest(&wake_lock_);
}
} // namespace cast