blob: 58a3d327a74081b3d1dfde7a5783470f76b8386d [file] [log] [blame]
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/mirroring/service/remoting_sender.h"
#include <algorithm>
#include "base/containers/heap_array.h"
#include "base/dcheck_is_on.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
#include "base/task/single_thread_task_runner.h"
#include "base/time/default_tick_clock.h"
#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
#include "media/base/decoder_buffer.h"
#include "media/base/media_switches.h"
#include "media/cast/common/openscreen_conversion_helpers.h"
#include "media/cast/common/rtp_time.h"
#include "media/cast/common/sender_encoded_frame.h"
#include "media/cast/constants.h"
#include "media/cast/openscreen/decoder_buffer_reader.h"
#include "media/cast/openscreen/remoting_proto_utils.h"
#include "media/cast/sender/openscreen_frame_sender.h"
#include "third_party/openscreen/src/cast/streaming/public/encoded_frame.h"
#include "third_party/openscreen/src/cast/streaming/public/sender.h"
using Dependency = openscreen::cast::EncodedFrame::Dependency;
namespace mirroring {
namespace {
// UMA histograms for recording the percentage of dropped frames.
constexpr char kHistogramDroppedAudioFrames[] =
"CastStreaming.Sender.Remoting.Audio.PercentDroppedFrames";
constexpr char kHistogramDroppedVideoFrames[] =
"CastStreaming.Sender.Remoting.Video.PercentDroppedFrames";
// UMA histograms for recording when a frame is dropped.
constexpr char kHistogramAudioFrameDropped[] =
"CastStreaming.Sender.Remoting.Audio.FrameDropped";
constexpr char kHistogramVideoFrameDropped[] =
"CastStreaming.Sender.Remoting.Video.FrameDropped";
// Maximum number of consecutive EnqueueFrame() calls that may be made before
// frames begin being dropped.
constexpr int kMaxEnqueueFrameFailures = 3;
} // namespace
class RemotingSender::SenderEncodedFrameFactory {
public:
SenderEncodedFrameFactory(int rtp_timebase,
media::cast::FrameSender& frame_sender,
const base::TickClock& clock)
: rtp_timebase_(rtp_timebase),
frame_sender_(frame_sender),
clock_(clock) {
// TODO(crbug.com/1413561): validate that we can use an arbitrary timebase
// here. Some receivers may require us to use the hardcoded remoting RTP
// timebase.
DCHECK_EQ(rtp_timebase_, media::cast::kRemotingRtpTimebase);
}
std::unique_ptr<media::cast::SenderEncodedFrame> CreateEncodedFrame(
const media::DecoderBuffer& decoder_buffer,
media::cast::FrameId frame_id) {
frames_created_++;
auto remoting_frame = std::make_unique<media::cast::SenderEncodedFrame>();
remoting_frame->frame_id = frame_id;
// DecoderBuffer data must be encoded in a special format.
remoting_frame->data =
media::cast::DecoderBufferToByteArray(decoder_buffer);
if (!decoder_buffer.end_of_stream() && remoting_frame->data.empty()) {
return nullptr;
}
const bool is_key_frame =
!decoder_buffer.end_of_stream() && decoder_buffer.is_key_frame();
remoting_frame->is_key_frame = is_key_frame;
remoting_frame->referenced_frame_id =
is_key_frame ? frame_id : frame_id - 1;
remoting_frame->reference_time = clock_->NowTicks();
remoting_frame->encode_completion_time = remoting_frame->reference_time;
base::TimeTicks last_frame_reference_time;
media::cast::RtpTimeTicks last_frame_rtp_timestamp;
const bool is_first_frame = (frame_id == media::cast::FrameId::first());
if (is_first_frame) {
last_frame_reference_time = remoting_frame->reference_time;
last_frame_rtp_timestamp =
media::cast::RtpTimeTicks() - media::cast::RtpTimeDelta::FromTicks(1);
} else {
last_frame_reference_time = frame_sender_->LastSendTime();
last_frame_rtp_timestamp =
frame_sender_->GetRecordedRtpTimestamp(frame_id - 1);
}
// Ensure each successive frame's RTP timestamp is unique, but otherwise
// just base it on the reference time.
const media::cast::RtpTimeTicks rtp_timestamp =
last_frame_rtp_timestamp +
std::max(media::cast::RtpTimeDelta::FromTicks(1),
media::cast::ToRtpTimeDelta(
remoting_frame->reference_time - last_frame_reference_time,
rtp_timebase_));
remoting_frame->rtp_timestamp = rtp_timestamp;
return remoting_frame;
}
int64_t frames_created() const { return frames_created_; }
private:
// The RTP timebase for this sender, set from the FrameSenderConfig.
const int rtp_timebase_;
// The frame sender for this frame creator.
raw_ref<media::cast::FrameSender> const frame_sender_;
// The clock.
raw_ref<const base::TickClock> const clock_;
// The total number of times CreateEncodedFrame() has been called.
int64_t frames_created_ = 0;
};
RemotingSender::RemotingSender(
scoped_refptr<media::cast::CastEnvironment> cast_environment,
std::unique_ptr<openscreen::cast::Sender> sender,
const media::cast::FrameSenderConfig& config,
mojo::ScopedDataPipeConsumerHandle pipe,
mojo::PendingReceiver<media::mojom::RemotingDataStreamSender> stream_sender,
base::OnceClosure error_callback)
: RemotingSender(cast_environment,
media::cast::FrameSender::Create(cast_environment,
config,
std::move(sender),
*this),
config,
std::move(pipe),
std::move(stream_sender),
std::move(error_callback)) {}
RemotingSender::RemotingSender(
scoped_refptr<media::cast::CastEnvironment> cast_environment,
std::unique_ptr<media::cast::FrameSender> frame_sender,
const media::cast::FrameSenderConfig& config,
mojo::ScopedDataPipeConsumerHandle pipe,
mojo::PendingReceiver<media::mojom::RemotingDataStreamSender> stream_sender,
base::OnceClosure error_callback)
: frame_sender_(std::move(frame_sender)),
clock_(cast_environment->Clock()),
error_callback_(std::move(error_callback)),
decoder_buffer_reader_(std::make_unique<media::cast::DecoderBufferReader>(
base::BindRepeating(&RemotingSender::OnFrameRead,
base::Unretained(this)),
std::move(pipe))),
stream_sender_(this, std::move(stream_sender)),
is_audio_(config.is_audio()),
frame_factory_(
std::make_unique<SenderEncodedFrameFactory>(config.rtp_timebase,
*frame_sender_,
*clock_)) {
stream_sender_.set_disconnect_handler(base::BindOnce(
&RemotingSender::OnRemotingDataStreamError, base::Unretained(this)));
// Eventually calls OnBufferRead().
decoder_buffer_reader_->ReadBufferAsync();
}
RemotingSender::~RemotingSender() {
const int64_t number_of_frames_inserted =
static_cast<int64_t>(next_frame_id_ - media::cast::FrameId::first());
const int64_t number_of_frames_created = frame_factory_->frames_created();
// Record the number of frames dropped during this session.
base::UmaHistogramPercentage(
is_audio_ ? kHistogramDroppedAudioFrames : kHistogramDroppedVideoFrames,
((number_of_frames_created - number_of_frames_inserted) * 100) /
std::max(int64_t{1}, number_of_frames_created));
}
void RemotingSender::SendFrame(media::mojom::DecoderBufferPtr buffer,
SendFrameCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(decoder_buffer_reader_);
if (read_complete_cb_ || next_frame_) {
// This should never occur if the API is being used as intended, as only
// one SendFrame() call should be ongoing at a time.
mojo::ReportBadMessage(
"Multiple calls made to RemotingDataStreamSender::SendFrame()");
return;
}
read_complete_cb_ = std::move(callback);
decoder_buffer_reader_->ProvideBuffer(std::move(buffer));
}
void RemotingSender::CancelInFlightData() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
flow_restart_pending_ = true;
ClearCurrentFrame();
}
int RemotingSender::GetNumberOfFramesInEncoder() const {
NOTREACHED();
}
base::TimeDelta RemotingSender::GetEncoderBacklogDuration() const {
NOTREACHED();
}
void RemotingSender::OnFrameCanceled(media::cast::FrameId frame_id) {
if (next_frame_) {
TrySendFrame();
}
}
void RemotingSender::TrySendFrame() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(next_frame_);
DCHECK(read_complete_cb_);
// If there would be too many frames in-flight, do not proceed.
if (frame_sender_->GetUnacknowledgedFrameCount() >=
media::cast::kMaxUnackedFrames) {
VLOG(1) << "Cannot send frame now because too many frames are in flight.";
return;
}
// Create a new frame and exit early if it's invalid.
auto remoting_frame =
frame_factory_->CreateEncodedFrame(*next_frame_, next_frame_id_);
if (!remoting_frame) {
DLOG(WARNING) << "Invalid buffer received";
ClearCurrentFrame();
return;
}
#if DCHECK_IS_ON()
CHECK_GE(remoting_frame->referenced_frame_id, remoting_frame->frame_id - 1);
if (flow_restart_pending_) {
CHECK(remoting_frame->is_key_frame);
CHECK_EQ(remoting_frame->referenced_frame_id, remoting_frame->frame_id);
} else {
CHECK_GT(remoting_frame->frame_id, media::cast::FrameId::first());
}
#endif // DCHECK_IS_ON()
// Try to enqueue the frame and request a new frame be sent if the operation
// succeeds.
const auto rtp_timestamp = remoting_frame->rtp_timestamp;
const media::cast::CastStreamingFrameDropReason reason =
frame_sender_->EnqueueFrame(std::move(remoting_frame));
if (reason == media::cast::CastStreamingFrameDropReason::kNotDropped) {
DVLOG(1) << "Successfully sent frame " << next_frame_id_ << ".";
flow_restart_pending_ = false;
next_frame_id_++;
consecutive_enqueue_frame_failure_count_ = 0;
ClearCurrentFrame();
return;
} else {
DLOG(WARNING) << "Dropped a frame for reason code="
<< static_cast<int>(reason);
}
// If EnqueueFrame() has been failing repeatedly or the failure was due to the
// formatting of the packet itself, drop it to avoid an infinite loop. Else,
// retry. The "retry" case is the only case that should be hit during regular
// execution of this function.
//
// By only dropping frames in such edge cases, all determinations about what
// frames can / can't be dropped are delegated to the Renderer process and the
// DemuxerStream::Read() function, rather than trying to "guess" here.
if (++consecutive_enqueue_frame_failure_count_ > kMaxEnqueueFrameFailures) {
DLOG(WARNING) << "Dropped frame due to repeated EnqueueFrame() failures.";
ClearCurrentFrame();
} else if (reason ==
media::cast::CastStreamingFrameDropReason::kPayloadTooLarge) {
DLOG(WARNING) << "Dropped frame with invalid formatting.";
ClearCurrentFrame();
} else {
DVLOG(1) << "Failed to send frame " << next_frame_id_ << ". Retrying...";
}
base::UmaHistogramEnumeration(
is_audio_ ? kHistogramAudioFrameDropped : kHistogramVideoFrameDropped,
reason);
TRACE_EVENT_INSTANT2(
"cast.stream",
is_audio_ ? "Remoting Audio Frame Drop" : "Remoting Video Frame Drop",
TRACE_EVENT_SCOPE_THREAD, "rtp_timestamp", rtp_timestamp.lower_32_bits(),
"reason", reason);
}
void RemotingSender::OnFrameRead(scoped_refptr<media::DecoderBuffer> buffer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!next_frame_);
DCHECK(read_complete_cb_);
DCHECK(decoder_buffer_reader_);
next_frame_ = std::move(buffer);
// Legacy receivers expect the first frame to be a keyframe.
if (flow_restart_pending_ && !next_frame_->end_of_stream()) {
next_frame_->set_is_key_frame(true);
}
TrySendFrame();
}
void RemotingSender::OnRemotingDataStreamError() {
// NOTE: This method must be idemptotent as it may be called more than once.
decoder_buffer_reader_.reset();
stream_sender_.reset();
if (!error_callback_.is_null()) {
std::move(error_callback_).Run();
}
}
void RemotingSender::ClearCurrentFrame() {
if (!next_frame_) {
return;
}
DCHECK(read_complete_cb_);
DCHECK(!decoder_buffer_reader_->is_read_pending());
next_frame_.reset();
decoder_buffer_reader_->ReadBufferAsync();
std::move(read_complete_cb_).Run();
}
} // namespace mirroring