blob: 468d2cb5ab4fc6a5e909e963791f0cc8775ee399 [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 "media/mojo/services/mojo_renderer_service.h"
#include <memory>
#include <utility>
#include "base/bind.h"
#include "base/memory/ptr_util.h"
#include "media/base/cdm_context.h"
#include "media/base/media_url_demuxer.h"
#include "media/base/renderer.h"
#include "media/mojo/common/media_type_converters.h"
#include "media/mojo/services/media_resource_shim.h"
#include "media/mojo/services/mojo_cdm_service_context.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace media {
// Time interval to update media time.
const int kTimeUpdateIntervalMs = 50;
// static
mojo::SelfOwnedReceiverRef<mojom::Renderer> MojoRendererService::Create(
MojoCdmServiceContext* mojo_cdm_service_context,
std::unique_ptr<media::Renderer> renderer,
mojo::PendingReceiver<mojom::Renderer> receiver) {
MojoRendererService* service =
new MojoRendererService(mojo_cdm_service_context, std::move(renderer));
mojo::SelfOwnedReceiverRef<mojom::Renderer> self_owned_receiver =
mojo::MakeSelfOwnedReceiver<mojom::Renderer>(base::WrapUnique(service),
std::move(receiver));
return self_owned_receiver;
}
MojoRendererService::MojoRendererService(
MojoCdmServiceContext* mojo_cdm_service_context,
std::unique_ptr<media::Renderer> renderer)
: mojo_cdm_service_context_(mojo_cdm_service_context),
state_(STATE_UNINITIALIZED),
playback_rate_(0),
renderer_(std::move(renderer)) {
DVLOG(1) << __func__;
DCHECK(renderer_);
weak_this_ = weak_factory_.GetWeakPtr();
}
MojoRendererService::~MojoRendererService() = default;
void MojoRendererService::Initialize(
mojo::PendingAssociatedRemote<mojom::RendererClient> client,
absl::optional<std::vector<mojo::PendingRemote<mojom::DemuxerStream>>>
streams,
mojom::MediaUrlParamsPtr media_url_params,
InitializeCallback callback) {
DVLOG(1) << __func__;
DCHECK_EQ(state_, STATE_UNINITIALIZED);
client_.Bind(std::move(client));
state_ = STATE_INITIALIZING;
if (!media_url_params) {
DCHECK(streams.has_value());
media_resource_ = std::make_unique<MediaResourceShim>(
std::move(*streams),
base::BindOnce(&MojoRendererService::OnAllStreamsReady, weak_this_,
std::move(callback)));
return;
}
DCHECK(!media_url_params->media_url.is_empty());
media_resource_ = std::make_unique<MediaUrlDemuxer>(
nullptr, media_url_params->media_url, media_url_params->site_for_cookies,
media_url_params->top_frame_origin, media_url_params->allow_credentials,
media_url_params->is_hls);
renderer_->Initialize(
media_resource_.get(), this,
base::BindOnce(&MojoRendererService::OnRendererInitializeDone, weak_this_,
std::move(callback)));
}
void MojoRendererService::Flush(FlushCallback callback) {
DVLOG(2) << __func__;
DCHECK_EQ(state_, STATE_PLAYING);
state_ = STATE_FLUSHING;
CancelPeriodicMediaTimeUpdates();
renderer_->Flush(base::BindOnce(&MojoRendererService::OnFlushCompleted,
weak_this_, std::move(callback)));
}
void MojoRendererService::StartPlayingFrom(base::TimeDelta time_delta) {
DVLOG(2) << __func__ << ": " << time_delta;
renderer_->StartPlayingFrom(time_delta);
SchedulePeriodicMediaTimeUpdates();
}
void MojoRendererService::SetPlaybackRate(double playback_rate) {
DVLOG(2) << __func__ << ": " << playback_rate;
DCHECK(state_ == STATE_PLAYING || state_ == STATE_ERROR);
playback_rate_ = playback_rate;
renderer_->SetPlaybackRate(playback_rate);
}
void MojoRendererService::SetVolume(float volume) {
renderer_->SetVolume(volume);
}
void MojoRendererService::SetCdm(
const absl::optional<base::UnguessableToken>& cdm_id,
SetCdmCallback callback) {
if (cdm_context_ref_) {
DVLOG(1) << "Switching CDM not supported";
std::move(callback).Run(false);
return;
}
if (!mojo_cdm_service_context_) {
DVLOG(1) << "CDM service context not available.";
std::move(callback).Run(false);
return;
}
if (!cdm_id) {
DVLOG(1) << "The CDM ID is invalid.";
std::move(callback).Run(false);
return;
}
auto cdm_context_ref =
mojo_cdm_service_context_->GetCdmContextRef(cdm_id.value());
if (!cdm_context_ref) {
DVLOG(1) << "CdmContextRef not found for CDM ID: " << cdm_id.value();
std::move(callback).Run(false);
return;
}
// |cdm_context_ref_| must be kept as long as |cdm_context| is used by the
// |renderer_|.
cdm_context_ref_ = std::move(cdm_context_ref);
auto* cdm_context = cdm_context_ref_->GetCdmContext();
DCHECK(cdm_context);
renderer_->SetCdm(cdm_context,
base::BindOnce(&MojoRendererService::OnCdmAttached,
weak_this_, std::move(callback)));
}
void MojoRendererService::OnError(PipelineStatus error) {
DVLOG(1) << __func__ << "(" << error << ")";
state_ = STATE_ERROR;
StatusCode status_code = PipelineStatusToStatusCode(error);
auto status = Status(status_code, PipelineStatusToString(error));
client_->OnError(status);
}
void MojoRendererService::OnEnded() {
DVLOG(1) << __func__;
CancelPeriodicMediaTimeUpdates();
client_->OnEnded();
}
void MojoRendererService::OnStatisticsUpdate(const PipelineStatistics& stats) {
DVLOG(3) << __func__;
client_->OnStatisticsUpdate(stats);
}
void MojoRendererService::OnBufferingStateChange(
BufferingState state,
BufferingStateChangeReason reason) {
DVLOG(2) << __func__ << "(" << state << ", " << reason << ")";
client_->OnBufferingStateChange(state, reason);
}
void MojoRendererService::OnWaiting(WaitingReason reason) {
DVLOG(1) << __func__;
client_->OnWaiting(reason);
}
void MojoRendererService::OnAudioConfigChange(
const AudioDecoderConfig& config) {
DVLOG(2) << __func__ << "(" << config.AsHumanReadableString() << ")";
client_->OnAudioConfigChange(config);
}
void MojoRendererService::OnVideoConfigChange(
const VideoDecoderConfig& config) {
DVLOG(2) << __func__ << "(" << config.AsHumanReadableString() << ")";
client_->OnVideoConfigChange(config);
}
void MojoRendererService::OnVideoNaturalSizeChange(const gfx::Size& size) {
DVLOG(2) << __func__ << "(" << size.ToString() << ")";
client_->OnVideoNaturalSizeChange(size);
}
void MojoRendererService::OnVideoOpacityChange(bool opaque) {
DVLOG(2) << __func__ << "(" << opaque << ")";
client_->OnVideoOpacityChange(opaque);
}
void MojoRendererService::OnVideoFrameRateChange(absl::optional<int> fps) {
DVLOG(2) << __func__ << "(" << (fps ? *fps : -1) << ")";
// TODO(liberato): plumb to |client_|.
}
void MojoRendererService::OnAllStreamsReady(
base::OnceCallback<void(bool)> callback) {
DCHECK_EQ(state_, STATE_INITIALIZING);
renderer_->Initialize(
media_resource_.get(), this,
base::BindOnce(&MojoRendererService::OnRendererInitializeDone, weak_this_,
std::move(callback)));
}
void MojoRendererService::OnRendererInitializeDone(
base::OnceCallback<void(bool)> callback,
PipelineStatus status) {
DVLOG(1) << __func__;
DCHECK_EQ(state_, STATE_INITIALIZING);
if (status != PIPELINE_OK) {
state_ = STATE_ERROR;
std::move(callback).Run(false);
return;
}
state_ = STATE_PLAYING;
std::move(callback).Run(true);
}
void MojoRendererService::UpdateMediaTime(bool force) {
const base::TimeDelta media_time = renderer_->GetMediaTime();
if (!force && media_time == last_media_time_)
return;
base::TimeDelta max_time = media_time;
// Allow some slop to account for delays in scheduling time update tasks.
if (time_update_timer_.IsRunning() && (playback_rate_ > 0))
max_time += base::Milliseconds(2 * kTimeUpdateIntervalMs);
client_->OnTimeUpdate(media_time, max_time, base::TimeTicks::Now());
last_media_time_ = media_time;
}
void MojoRendererService::CancelPeriodicMediaTimeUpdates() {
DVLOG(2) << __func__;
time_update_timer_.Stop();
UpdateMediaTime(false);
}
void MojoRendererService::SchedulePeriodicMediaTimeUpdates() {
DVLOG(2) << __func__;
UpdateMediaTime(true);
time_update_timer_.Start(
FROM_HERE, base::Milliseconds(kTimeUpdateIntervalMs),
base::BindRepeating(&MojoRendererService::UpdateMediaTime, weak_this_,
false));
}
void MojoRendererService::OnFlushCompleted(FlushCallback callback) {
DVLOG(1) << __func__;
DCHECK_EQ(state_, STATE_FLUSHING);
state_ = STATE_PLAYING;
std::move(callback).Run();
}
void MojoRendererService::OnCdmAttached(base::OnceCallback<void(bool)> callback,
bool success) {
DVLOG(1) << __func__ << "(" << success << ")";
if (!success)
cdm_context_ref_.reset();
std::move(callback).Run(success);
}
} // namespace media