123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812 |
- // 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 "fuchsia_web/webengine/renderer/web_engine_audio_renderer.h"
- #include <lib/sys/cpp/component_context.h>
- #include "base/bind.h"
- #include "base/fuchsia/fuchsia_logging.h"
- #include "base/logging.h"
- #include "base/task/sequenced_task_runner.h"
- #include "base/threading/sequenced_task_runner_handle.h"
- #include "base/threading/thread_task_runner_handle.h"
- #include "base/time/time.h"
- #include "media/base/cdm_context.h"
- #include "media/base/decoder_buffer.h"
- #include "media/base/renderer_client.h"
- #include "media/fuchsia/cdm/fuchsia_cdm_context.h"
- #include "media/fuchsia/common/decrypting_sysmem_buffer_stream.h"
- #include "media/fuchsia/common/passthrough_sysmem_buffer_stream.h"
- namespace {
- // nullopt is returned in case the codec is not supported. nullptr is returned
- // for uncompressed PCM streams.
- absl::optional<std::unique_ptr<fuchsia::media::Compression>>
- GetFuchsiaCompressionFromDecoderConfig(media::AudioDecoderConfig config) {
- auto compression = std::make_unique<fuchsia::media::Compression>();
- switch (config.codec()) {
- #if BUILDFLAG(USE_PROPRIETARY_CODECS)
- case media::AudioCodec::kAAC:
- compression->type = fuchsia::media::AUDIO_ENCODING_AAC;
- break;
- #endif // BUILDFLAG(USE_PROPRIETARY_CODECS)
- case media::AudioCodec::kMP3:
- compression->type = fuchsia::media::AUDIO_ENCODING_MP3;
- break;
- case media::AudioCodec::kVorbis:
- compression->type = fuchsia::media::AUDIO_ENCODING_VORBIS;
- break;
- case media::AudioCodec::kOpus:
- compression->type = fuchsia::media::AUDIO_ENCODING_OPUS;
- break;
- case media::AudioCodec::kFLAC:
- compression->type = fuchsia::media::AUDIO_ENCODING_FLAC;
- break;
- case media::AudioCodec::kPCM:
- compression.reset();
- break;
- default:
- return absl::nullopt;
- }
- if (!config.extra_data().empty()) {
- compression->parameters = config.extra_data();
- }
- return std::move(compression);
- }
- absl::optional<fuchsia::media::AudioSampleFormat>
- GetFuchsiaSampleFormatFromSampleFormat(media::SampleFormat sample_format) {
- switch (sample_format) {
- case media::kSampleFormatU8:
- return fuchsia::media::AudioSampleFormat::UNSIGNED_8;
- case media::kSampleFormatS16:
- return fuchsia::media::AudioSampleFormat::SIGNED_16;
- case media::kSampleFormatS24:
- return fuchsia::media::AudioSampleFormat::SIGNED_24_IN_32;
- case media::kSampleFormatF32:
- return fuchsia::media::AudioSampleFormat::FLOAT;
- default:
- return absl::nullopt;
- }
- }
- // Helper that converts a PCM stream in kStreamFormatS24 to the layout
- // expected by AudioConsumer (i.e. SIGNED_24_IN_32).
- scoped_refptr<media::DecoderBuffer> PreparePcm24Buffer(
- scoped_refptr<media::DecoderBuffer> buffer) {
- static_assert(ARCH_CPU_LITTLE_ENDIAN,
- "Only little-endian CPUs are supported.");
- size_t samples = buffer->data_size() / 3;
- scoped_refptr<media::DecoderBuffer> result =
- base::MakeRefCounted<media::DecoderBuffer>(samples * 4);
- for (size_t i = 0; i < samples - 1; ++i) {
- reinterpret_cast<uint32_t*>(result->writable_data())[i] =
- *reinterpret_cast<const uint32_t*>(buffer->data() + i * 3) & 0x00ffffff;
- }
- size_t last_sample = samples - 1;
- reinterpret_cast<uint32_t*>(result->writable_data())[last_sample] =
- buffer->data()[last_sample * 3] |
- (buffer->data()[last_sample * 3 + 1] << 8) |
- (buffer->data()[last_sample * 3 + 2] << 16);
- result->set_timestamp(buffer->timestamp());
- result->set_duration(buffer->duration());
- if (buffer->decrypt_config())
- result->set_decrypt_config(buffer->decrypt_config()->Clone());
- return result;
- }
- } // namespace
- // Size of a single audio buffer: 100kB. It's enough to cover 100ms of PCM at
- // 48kHz, 2 channels, 16 bps.
- constexpr size_t kBufferSize = 100 * 1024;
- // Total number of buffers. 16 is the maximum allowed by AudioConsumer.
- constexpr size_t kNumBuffers = 16;
- WebEngineAudioRenderer::WebEngineAudioRenderer(
- media::MediaLog* media_log,
- fidl::InterfaceHandle<fuchsia::media::AudioConsumer> audio_consumer_handle)
- : audio_consumer_handle_(std::move(audio_consumer_handle)) {
- DETACH_FROM_THREAD(thread_checker_);
- }
- WebEngineAudioRenderer::~WebEngineAudioRenderer() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- }
- void WebEngineAudioRenderer::Initialize(media::DemuxerStream* stream,
- media::CdmContext* cdm_context,
- media::RendererClient* client,
- media::PipelineStatusCallback init_cb) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- DCHECK(!demuxer_stream_);
- DCHECK(!init_cb_);
- init_cb_ = std::move(init_cb);
- cdm_context_ = cdm_context;
- demuxer_stream_ = stream;
- client_ = client;
- audio_consumer_.Bind(std::move(audio_consumer_handle_));
- audio_consumer_.set_error_handler([this](zx_status_t status) {
- ZX_LOG(ERROR, status) << "AudioConsumer disconnected.";
- OnError(media::AUDIO_RENDERER_ERROR);
- });
- UpdateVolume();
- audio_consumer_.events().OnEndOfStream = [this]() { OnEndOfStream(); };
- RequestAudioConsumerStatus();
- InitializeStream();
- // Call `init_cb_`, unless it's been called by OnError().
- if (init_cb_) {
- std::move(init_cb_).Run(media::PIPELINE_OK);
- }
- }
- void WebEngineAudioRenderer::InitializeStream() {
- #if BUILDFLAG(USE_PROPRIETARY_CODECS)
- // AAC streams require bitstream conversion. Without it the demuxer may
- // produce decoded stream without ADTS headers which are required for AAC
- // streams in AudioConsumer.
- // TODO(crbug.com/1120095): Reconsider this logic.
- if (demuxer_stream_->audio_decoder_config().codec() ==
- media::AudioCodec::kAAC) {
- demuxer_stream_->EnableBitstreamConverter();
- }
- #endif // BUILDFLAG(USE_PROPRIETARY_CODECS)
- if (demuxer_stream_->audio_decoder_config().is_encrypted()) {
- if (!cdm_context_) {
- DLOG(ERROR) << "No cdm context for encrypted stream.";
- OnError(media::AUDIO_RENDERER_ERROR);
- return;
- }
- media::FuchsiaCdmContext* fuchsia_cdm =
- cdm_context_->GetFuchsiaCdmContext();
- if (fuchsia_cdm) {
- sysmem_buffer_stream_ = fuchsia_cdm->CreateStreamDecryptor(false);
- } else {
- sysmem_buffer_stream_ =
- std::make_unique<media::DecryptingSysmemBufferStream>(
- &sysmem_allocator_, cdm_context_, media::Decryptor::kAudio);
- }
- } else {
- sysmem_buffer_stream_ =
- std::make_unique<media::PassthroughSysmemBufferStream>(
- &sysmem_allocator_);
- }
- sysmem_buffer_stream_->Initialize(this, kBufferSize, kNumBuffers);
- }
- void WebEngineAudioRenderer::UpdateVolume() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- DCHECK(audio_consumer_);
- if (!volume_control_) {
- audio_consumer_->BindVolumeControl(volume_control_.NewRequest());
- volume_control_.set_error_handler([](zx_status_t status) {
- ZX_LOG(ERROR, status) << "VolumeControl disconnected.";
- });
- }
- volume_control_->SetVolume(volume_);
- }
- void WebEngineAudioRenderer::OnBuffersAcquired(
- std::vector<media::VmoBuffer> buffers,
- const fuchsia::sysmem::SingleBufferSettings&) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- input_buffers_ = std::move(buffers);
- InitializeStreamSink();
- while (!delayed_packets_.empty()) {
- auto packet = std::move(delayed_packets_.front());
- delayed_packets_.pop_front();
- SendInputPacket(std::move(packet));
- }
- if (has_delayed_end_of_stream_) {
- has_delayed_end_of_stream_ = false;
- OnSysmemBufferStreamEndOfStream();
- }
- }
- void WebEngineAudioRenderer::InitializeStreamSink() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- DCHECK(!stream_sink_);
- // Clone |buffers| to pass to StreamSink.
- std::vector<zx::vmo> vmos_for_stream_sink;
- vmos_for_stream_sink.reserve(input_buffers_.size());
- for (media::VmoBuffer& buffer : input_buffers_) {
- vmos_for_stream_sink.push_back(buffer.Duplicate(/*writable=*/false));
- }
- auto config = demuxer_stream_->audio_decoder_config();
- auto compression = GetFuchsiaCompressionFromDecoderConfig(config);
- if (!compression) {
- LOG(ERROR) << "Unsupported audio codec: " << GetCodecName(config.codec());
- OnError(media::AUDIO_RENDERER_ERROR);
- return;
- }
- fuchsia::media::AudioStreamType stream_type;
- stream_type.channels = config.channels();
- stream_type.frames_per_second = config.samples_per_second();
- // Set sample_format for uncompressed streams.
- if (!compression.value()) {
- absl::optional<fuchsia::media::AudioSampleFormat> sample_format =
- GetFuchsiaSampleFormatFromSampleFormat(config.sample_format());
- if (!sample_format) {
- LOG(ERROR) << "Unsupported sample format: "
- << SampleFormatToString(config.sample_format());
- OnError(media::AUDIO_RENDERER_ERROR);
- return;
- }
- stream_type.sample_format = sample_format.value();
- } else {
- // For compressed formats sample format is determined by the decoder, but
- // this field is still required in AudioStreamType.
- stream_type.sample_format = fuchsia::media::AudioSampleFormat::SIGNED_16;
- }
- audio_consumer_->CreateStreamSink(
- std::move(vmos_for_stream_sink), std::move(stream_type),
- std::move(compression).value(), stream_sink_.NewRequest());
- if (GetPlaybackState() == PlaybackState::kStartPending)
- StartAudioConsumer();
- ScheduleReadDemuxerStream();
- }
- media::TimeSource* WebEngineAudioRenderer::GetTimeSource() {
- return this;
- }
- void WebEngineAudioRenderer::Flush(base::OnceClosure callback) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- FlushInternal();
- renderer_started_ = false;
- std::move(callback).Run();
- }
- void WebEngineAudioRenderer::StartPlaying() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- renderer_started_ = true;
- ScheduleReadDemuxerStream();
- }
- void WebEngineAudioRenderer::SetVolume(float volume) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- volume_ = volume;
- if (audio_consumer_)
- UpdateVolume();
- }
- void WebEngineAudioRenderer::SetLatencyHint(
- absl::optional<base::TimeDelta> latency_hint) {
- // TODO(crbug.com/1131116): Implement at some later date after we've vetted
- // the API shape and usefulness outside of fuchsia.
- NOTIMPLEMENTED();
- }
- void WebEngineAudioRenderer::SetPreservesPitch(bool preserves_pitch) {
- NOTIMPLEMENTED();
- }
- void WebEngineAudioRenderer::SetWasPlayedWithUserActivation(
- bool was_played_with_user_activation) {
- NOTIMPLEMENTED();
- }
- void WebEngineAudioRenderer::StartTicking() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- // If StreamSink hasn't been created yet, then delay starting AudioConsumer
- // until StreamSink is created.
- if (!stream_sink_) {
- base::AutoLock lock(timeline_lock_);
- SetPlaybackState(PlaybackState::kStartPending);
- return;
- }
- StartAudioConsumer();
- }
- void WebEngineAudioRenderer::StartAudioConsumer() {
- DCHECK(stream_sink_);
- fuchsia::media::AudioConsumerStartFlags flags{};
- if (demuxer_stream_->liveness() == media::StreamLiveness::kLive) {
- flags = fuchsia::media::AudioConsumerStartFlags::LOW_LATENCY;
- }
- // Stop the AudioConsumer if it's been started.
- switch (GetPlaybackState()) {
- case PlaybackState::kStopped:
- case PlaybackState::kStartPending:
- break;
- case PlaybackState::kStarting:
- case PlaybackState::kPlaying:
- audio_consumer_->Stop();
- break;
- }
- base::TimeDelta media_pos;
- {
- base::AutoLock lock(timeline_lock_);
- media_pos = media_pos_;
- SetPlaybackState(PlaybackState::kStarting);
- }
- audio_consumer_->Start(flags, fuchsia::media::NO_TIMESTAMP,
- media_pos.ToZxDuration());
- }
- void WebEngineAudioRenderer::StopTicking() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- DCHECK(GetPlaybackState() != PlaybackState::kStopped);
- audio_consumer_->Stop();
- base::AutoLock lock(timeline_lock_);
- UpdateTimelineOnStop();
- SetPlaybackState(PlaybackState::kStopped);
- }
- void WebEngineAudioRenderer::SetPlaybackRate(double playback_rate) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- audio_consumer_->SetRate(playback_rate);
- // AudioConsumer will update media timeline asynchronously. That update is
- // processed in OnAudioConsumerStatusChanged(). This might cause the clock to
- // go back. It's not desirable, e.g. because VideoRenderer could drop some
- // video frames that should be shown when the stream is resumed. To avoid this
- // issue update the timeline synchronously. OnAudioConsumerStatusChanged()
- // will still process the update from AudioConsumer to save the position when
- // the stream was actually paused, but that update would not move the clock
- // backward.
- if (playback_rate == 0.0) {
- base::AutoLock lock(timeline_lock_);
- UpdateTimelineOnStop();
- }
- }
- void WebEngineAudioRenderer::SetMediaTime(base::TimeDelta time) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- DCHECK(GetPlaybackState() == PlaybackState::kStopped);
- {
- base::AutoLock lock(timeline_lock_);
- media_pos_ = time;
- // Reset reference timestamp. This is necessary to ensure that the correct
- // value is returned from GetWallClockTimes() until playback is resumed:
- // GetWallClockTimes() is required to return 0 wall clock between
- // SetMediaTime() and StartTicking().
- reference_time_ = base::TimeTicks();
- }
- FlushInternal();
- ScheduleReadDemuxerStream();
- }
- base::TimeDelta WebEngineAudioRenderer::CurrentMediaTime() {
- base::AutoLock lock(timeline_lock_);
- if (!IsTimeMoving())
- return media_pos_;
- return CurrentMediaTimeLocked();
- }
- bool WebEngineAudioRenderer::GetWallClockTimes(
- const std::vector<base::TimeDelta>& media_timestamps,
- std::vector<base::TimeTicks>* wall_clock_times) {
- wall_clock_times->reserve(media_timestamps.size());
- auto now = base::TimeTicks::Now();
- base::AutoLock lock(timeline_lock_);
- const bool is_time_moving = IsTimeMoving();
- if (media_timestamps.empty()) {
- wall_clock_times->push_back(is_time_moving ? now : reference_time_);
- return is_time_moving;
- }
- base::TimeTicks wall_clock_base = is_time_moving ? reference_time_ : now;
- for (base::TimeDelta timestamp : media_timestamps) {
- base::TimeTicks wall_clock_time;
- auto relative_pos = timestamp - media_pos_;
- if (is_time_moving) {
- // See https://fuchsia.dev/reference/fidl/fuchsia.media#formulas .
- relative_pos = relative_pos * reference_delta_ / media_delta_;
- }
- wall_clock_time = wall_clock_base + relative_pos;
- wall_clock_times->push_back(wall_clock_time);
- }
- return is_time_moving;
- }
- WebEngineAudioRenderer::PlaybackState
- WebEngineAudioRenderer::GetPlaybackState() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- return state_;
- }
- void WebEngineAudioRenderer::SetPlaybackState(PlaybackState state) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- state_ = state;
- }
- void WebEngineAudioRenderer::OnError(media::PipelineStatus status) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- audio_consumer_.Unbind();
- stream_sink_.Unbind();
- sysmem_buffer_stream_.reset();
- if (is_demuxer_read_pending_) {
- drop_next_demuxer_read_result_ = true;
- }
- if (init_cb_) {
- std::move(init_cb_).Run(status);
- } else if (client_) {
- client_->OnError(status);
- }
- }
- void WebEngineAudioRenderer::RequestAudioConsumerStatus() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- audio_consumer_->WatchStatus(fit::bind_member(
- this, &WebEngineAudioRenderer::OnAudioConsumerStatusChanged));
- }
- void WebEngineAudioRenderer::OnAudioConsumerStatusChanged(
- fuchsia::media::AudioConsumerStatus status) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- if (status.has_error()) {
- LOG(ERROR) << "fuchsia::media::AudioConsumer reported an error";
- OnError(media::AUDIO_RENDERER_ERROR);
- return;
- }
- bool reschedule_read_timer = false;
- if (status.has_presentation_timeline()) {
- if (GetPlaybackState() != PlaybackState::kStopped) {
- base::AutoLock lock(timeline_lock_);
- if (GetPlaybackState() == PlaybackState::kStarting) {
- SetPlaybackState(PlaybackState::kPlaying);
- }
- reference_time_ = base::TimeTicks::FromZxTime(
- status.presentation_timeline().reference_time);
- media_pos_ = base::TimeDelta::FromZxDuration(
- status.presentation_timeline().subject_time);
- reference_delta_ = status.presentation_timeline().reference_delta;
- media_delta_ = status.presentation_timeline().subject_delta;
- reschedule_read_timer = true;
- }
- }
- if (status.has_min_lead_time()) {
- auto new_min_lead_time =
- base::TimeDelta::FromZxDuration(status.min_lead_time());
- DCHECK(!new_min_lead_time.is_zero());
- if (new_min_lead_time != min_lead_time_) {
- min_lead_time_ = new_min_lead_time;
- reschedule_read_timer = true;
- }
- }
- if (status.has_max_lead_time()) {
- auto new_max_lead_time =
- base::TimeDelta::FromZxDuration(status.max_lead_time());
- DCHECK(!new_max_lead_time.is_zero());
- if (new_max_lead_time != max_lead_time_) {
- max_lead_time_ = new_max_lead_time;
- reschedule_read_timer = true;
- }
- }
- if (reschedule_read_timer) {
- read_timer_.Stop();
- ScheduleReadDemuxerStream();
- }
- RequestAudioConsumerStatus();
- }
- void WebEngineAudioRenderer::ScheduleReadDemuxerStream() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- if (!renderer_started_ || !demuxer_stream_ || read_timer_.IsRunning() ||
- is_demuxer_read_pending_ || is_at_end_of_stream_) {
- return;
- }
- base::TimeDelta next_read_delay;
- if (!last_packet_timestamp_.is_min()) {
- std::vector<base::TimeTicks> wall_clock_times;
- bool is_time_moving =
- GetWallClockTimes({last_packet_timestamp_}, &wall_clock_times);
- base::TimeDelta relative_buffer_pos =
- wall_clock_times[0] - base::TimeTicks::Now();
- // Check if we have buffered more than |max_lead_time_|.
- if (relative_buffer_pos >= max_lead_time_) {
- // If playback is not active then there is no need to buffer more.
- if (!is_time_moving)
- return;
- // If the buffer is larger than |max_lead_time_|, then the next read
- // should be delayed.
- next_read_delay = relative_buffer_pos - max_lead_time_;
- }
- }
- read_timer_.Start(FROM_HERE, next_read_delay,
- base::BindOnce(&WebEngineAudioRenderer::ReadDemuxerStream,
- base::Unretained(this)));
- }
- void WebEngineAudioRenderer::ReadDemuxerStream() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- DCHECK(demuxer_stream_);
- DCHECK(!is_demuxer_read_pending_);
- is_demuxer_read_pending_ = true;
- demuxer_stream_->Read(
- base::BindOnce(&WebEngineAudioRenderer::OnDemuxerStreamReadDone,
- weak_factory_.GetWeakPtr()));
- }
- void WebEngineAudioRenderer::OnDemuxerStreamReadDone(
- media::DemuxerStream::Status read_status,
- scoped_refptr<media::DecoderBuffer> buffer) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- DCHECK(is_demuxer_read_pending_);
- is_demuxer_read_pending_ = false;
- if (drop_next_demuxer_read_result_) {
- drop_next_demuxer_read_result_ = false;
- ScheduleReadDemuxerStream();
- return;
- }
- if (read_status != media::DemuxerStream::kOk) {
- if (read_status == media::DemuxerStream::kError) {
- OnError(media::PIPELINE_ERROR_READ);
- } else if (read_status == media::DemuxerStream::kConfigChanged) {
- stream_sink_.Unbind();
- // Re-initialize the stream for the new config.
- InitializeStream();
- // Continue reading the stream. Decryptor won't finish output buffer
- // initialization until it starts receiving data on the input.
- ScheduleReadDemuxerStream();
- client_->OnAudioConfigChange(demuxer_stream_->audio_decoder_config());
- } else {
- DCHECK_EQ(read_status, media::DemuxerStream::kAborted);
- }
- return;
- }
- if (buffer->end_of_stream()) {
- is_at_end_of_stream_ = true;
- } else {
- if (buffer->data_size() > kBufferSize) {
- DLOG(ERROR) << "Demuxer returned buffer that is too big: "
- << buffer->data_size();
- OnError(media::AUDIO_RENDERER_ERROR);
- return;
- }
- last_packet_timestamp_ = buffer->timestamp();
- if (buffer->duration() != media::kNoTimestamp)
- last_packet_timestamp_ += buffer->duration();
- }
- // Update layout for 24-bit PCM streams.
- if (!buffer->end_of_stream() &&
- demuxer_stream_->audio_decoder_config().codec() ==
- media::AudioCodec::kPCM &&
- demuxer_stream_->audio_decoder_config().sample_format() ==
- media::kSampleFormatS24) {
- buffer = PreparePcm24Buffer(std::move(buffer));
- }
- sysmem_buffer_stream_->EnqueueBuffer(std::move(buffer));
- ScheduleReadDemuxerStream();
- }
- void WebEngineAudioRenderer::SendInputPacket(
- media::StreamProcessorHelper::IoPacket packet) {
- const auto packet_size = packet.size();
- fuchsia::media::StreamPacket stream_packet;
- stream_packet.payload_buffer_id = packet.buffer_index();
- stream_packet.pts = packet.timestamp().ToZxDuration();
- stream_packet.payload_offset = packet.offset();
- stream_packet.payload_size = packet_size;
- stream_sink_->SendPacket(
- std::move(stream_packet),
- [this, packet = std::make_unique<media::StreamProcessorHelper::IoPacket>(
- std::move(packet))]() mutable {
- OnStreamSendDone(std::move(packet));
- });
- // AudioConsumer doesn't report exact time when the data is decoded, but it's
- // safe to report it as decoded right away since the packet is expected to be
- // decoded soon after AudioConsumer receives it.
- media::PipelineStatistics stats;
- stats.audio_bytes_decoded = packet_size;
- client_->OnStatisticsUpdate(stats);
- }
- void WebEngineAudioRenderer::OnStreamSendDone(
- std::unique_ptr<media::StreamProcessorHelper::IoPacket> packet) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- // Check if we need to update buffering state after sending more than
- // |min_lead_time_| to the AudioConsumer.
- if (buffer_state_ == media::BUFFERING_HAVE_NOTHING) {
- std::vector<base::TimeTicks> wall_clock_times;
- GetWallClockTimes({packet->timestamp()}, &wall_clock_times);
- base::TimeDelta relative_buffer_pos =
- wall_clock_times[0] - base::TimeTicks::Now();
- if (relative_buffer_pos >= min_lead_time_)
- SetBufferState(media::BUFFERING_HAVE_ENOUGH);
- }
- ScheduleReadDemuxerStream();
- }
- void WebEngineAudioRenderer::SetBufferState(
- media::BufferingState buffer_state) {
- if (buffer_state != buffer_state_) {
- buffer_state_ = buffer_state;
- client_->OnBufferingStateChange(buffer_state_,
- media::BUFFERING_CHANGE_REASON_UNKNOWN);
- }
- }
- void WebEngineAudioRenderer::FlushInternal() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- DCHECK(GetPlaybackState() == PlaybackState::kStopped || is_at_end_of_stream_);
- if (stream_sink_)
- stream_sink_->DiscardAllPacketsNoReply();
- SetBufferState(media::BUFFERING_HAVE_NOTHING);
- last_packet_timestamp_ = base::TimeDelta::Min();
- read_timer_.Stop();
- is_at_end_of_stream_ = false;
- if (is_demuxer_read_pending_) {
- drop_next_demuxer_read_result_ = true;
- }
- }
- void WebEngineAudioRenderer::OnEndOfStream() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- client_->OnEnded();
- }
- bool WebEngineAudioRenderer::IsTimeMoving() {
- return state_ == PlaybackState::kPlaying && media_delta_ > 0;
- }
- void WebEngineAudioRenderer::UpdateTimelineOnStop() {
- if (!IsTimeMoving())
- return;
- media_pos_ = CurrentMediaTimeLocked();
- reference_time_ = base::TimeTicks::Now();
- media_delta_ = 0;
- }
- base::TimeDelta WebEngineAudioRenderer::CurrentMediaTimeLocked() {
- DCHECK(IsTimeMoving());
- // Calculate media position using formula specified by the TimelineFunction.
- // See https://fuchsia.dev/reference/fidl/fuchsia.media#formulas .
- return media_pos_ + (base::TimeTicks::Now() - reference_time_) *
- media_delta_ / reference_delta_;
- }
- void WebEngineAudioRenderer::OnSysmemBufferStreamBufferCollectionToken(
- fuchsia::sysmem::BufferCollectionTokenPtr token) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- // Drop old buffers.
- input_buffers_.clear();
- stream_sink_.Unbind();
- // Acquire buffers for the new buffer collection.
- input_buffer_collection_ =
- sysmem_allocator_.BindSharedCollection(std::move(token));
- fuchsia::sysmem::BufferCollectionConstraints buffer_constraints =
- media::VmoBuffer::GetRecommendedConstraints(kNumBuffers, kBufferSize,
- /*writable=*/false);
- input_buffer_collection_->Initialize(std::move(buffer_constraints),
- "CrAudioRenderer");
- input_buffer_collection_->AcquireBuffers(base::BindOnce(
- &WebEngineAudioRenderer::OnBuffersAcquired, base::Unretained(this)));
- }
- void WebEngineAudioRenderer::OnSysmemBufferStreamOutputPacket(
- media::StreamProcessorHelper::IoPacket packet) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- if (stream_sink_) {
- SendInputPacket(std::move(packet));
- } else {
- // The packet will be sent after StreamSink is connected.
- delayed_packets_.push_back(std::move(packet));
- }
- ScheduleReadDemuxerStream();
- }
- void WebEngineAudioRenderer::OnSysmemBufferStreamEndOfStream() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- DCHECK(is_at_end_of_stream_);
- // Stream sink is not bound yet, queue EOS request until then.
- if (!stream_sink_) {
- has_delayed_end_of_stream_ = true;
- return;
- }
- stream_sink_->EndOfStream();
- // No more data is going to be buffered. Update buffering state to ensure
- // RendererImpl starts playback in case it was waiting for buffering to
- // finish.
- SetBufferState(media::BUFFERING_HAVE_ENOUGH);
- }
- void WebEngineAudioRenderer::OnSysmemBufferStreamError() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- OnError(media::AUDIO_RENDERER_ERROR);
- }
- void WebEngineAudioRenderer::OnSysmemBufferStreamNoKey() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- client_->OnWaiting(media::WaitingReason::kNoDecryptionKey);
- }
|