| // Copyright 2016 The Fuchsia 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 "src/media/playback/mediaplayer/test/fakes/fake_audio_renderer.h" |
| |
| #include <lib/async/cpp/task.h> |
| #include <lib/async/default.h> |
| #include <lib/zx/clock.h> |
| |
| #include <iomanip> |
| #include <iostream> |
| #include <limits> |
| |
| namespace media_player { |
| namespace test { |
| |
| FakeAudioRenderer::FakeAudioRenderer() |
| : dispatcher_(async_get_default_dispatcher()), binding_(this) {} |
| |
| FakeAudioRenderer::~FakeAudioRenderer() {} |
| |
| void FakeAudioRenderer::Bind(fidl::InterfaceRequest<fuchsia::media::AudioRenderer> request) { |
| binding_.Bind(std::move(request)); |
| } |
| |
| void FakeAudioRenderer::SetPcmStreamType(fuchsia::media::AudioStreamType format) { |
| format_ = format; |
| } |
| |
| void FakeAudioRenderer::AddPayloadBuffer(uint32_t id, zx::vmo payload_buffer) { |
| FX_DCHECK(id == 0) << "Only ID 0 is currently supported."; |
| vmo_mapper_.Map(std::move(payload_buffer), 0, 0, ZX_VM_PERM_READ); |
| } |
| |
| void FakeAudioRenderer::RemovePayloadBuffer(uint32_t id) { FX_NOTIMPLEMENTED(); } |
| |
| void FakeAudioRenderer::SetPtsUnits(uint32_t tick_per_second_numerator, |
| uint32_t tick_per_second_denominator) { |
| pts_rate_ = media::TimelineRate(tick_per_second_numerator, tick_per_second_denominator); |
| } |
| |
| void FakeAudioRenderer::SetPtsContinuityThreshold(float threshold_seconds) { |
| threshold_seconds_ = threshold_seconds; |
| } |
| |
| void FakeAudioRenderer::SetReferenceClock(zx::clock ref_clock) { FX_NOTIMPLEMENTED(); } |
| |
| void FakeAudioRenderer::GetReferenceClock(GetReferenceClockCallback callback) { |
| callback(zx::clock(ZX_HANDLE_INVALID)); |
| FX_NOTIMPLEMENTED(); |
| } |
| |
| void FakeAudioRenderer::SendPacket(fuchsia::media::StreamPacket packet, |
| SendPacketCallback callback) { |
| packets_received_++; |
| |
| if (dump_packets_) { |
| std::cerr << "{ " << packet.pts << ", " << packet.payload_size << ", 0x" << std::hex |
| << std::setw(16) << std::setfill('0') |
| << PacketInfo::Hash( |
| reinterpret_cast<uint8_t*>(vmo_mapper_.start()) + packet.payload_offset, |
| packet.payload_size) |
| << std::dec << " },\n"; |
| } |
| |
| if (!packet_expecters_.empty()) { |
| bool expecter_ok = false; |
| |
| for (auto& expecter : packet_expecters_) { |
| if (expecter.IsExpected(packet, vmo_mapper_.start())) { |
| expecter_ok = true; |
| } |
| } |
| |
| if (!expecter_ok) { |
| FX_LOGS(ERROR) << "supplied packet doesn't match expected packet info"; |
| expected_ = false; |
| } |
| } |
| |
| packet_queue_.push(std::make_pair(packet, std::move(callback))); |
| |
| if (packet_queue_.size() == 1) { |
| MaybeScheduleRetirement(); |
| } |
| } |
| |
| void FakeAudioRenderer::SendPacketNoReply(fuchsia::media::StreamPacket packet) { |
| SendPacket(std::move(packet), []() {}); |
| } |
| |
| void FakeAudioRenderer::EndOfStream() { FX_NOTIMPLEMENTED(); } |
| |
| void FakeAudioRenderer::DiscardAllPackets(DiscardAllPacketsCallback callback) { |
| while (!packet_queue_.empty()) { |
| packet_queue_.front().second(); |
| packet_queue_.pop(); |
| } |
| |
| callback(); |
| } |
| |
| void FakeAudioRenderer::DiscardAllPacketsNoReply() { |
| DiscardAllPackets([]() {}); |
| } |
| |
| void FakeAudioRenderer::Play(int64_t reference_time, int64_t media_time, PlayCallback callback) { |
| if (vmo_mapper_.start() == nullptr) { |
| FX_LOGS(ERROR) << "Play called with no buffer added"; |
| expected_ = false; |
| binding_.Unbind(); |
| return; |
| } |
| |
| if (reference_time == fuchsia::media::NO_TIMESTAMP) { |
| reference_time = zx::clock::get_monotonic().get(); |
| } |
| |
| if (media_time == fuchsia::media::NO_TIMESTAMP) { |
| if (restart_media_time_ != fuchsia::media::NO_TIMESTAMP) { |
| media_time = restart_media_time_; |
| } else if (packet_queue_.empty()) { |
| media_time = 0; |
| } else { |
| media_time = packet_queue_.front().first.pts; |
| } |
| } |
| |
| callback(reference_time, media_time); |
| |
| timeline_function_ = media::TimelineFunction(media_time, reference_time, |
| pts_rate_ / media::TimelineRate::NsPerSecond); |
| |
| MaybeScheduleRetirement(); |
| } |
| |
| void FakeAudioRenderer::PlayNoReply(int64_t reference_time, int64_t media_time) { |
| Play(reference_time, media_time, [](int64_t reference_time, int64_t media_time) {}); |
| } |
| |
| void FakeAudioRenderer::Pause(PauseCallback callback) { |
| if (vmo_mapper_.start() == nullptr) { |
| FX_LOGS(ERROR) << "Pause called with no buffer added"; |
| expected_ = false; |
| binding_.Unbind(); |
| return; |
| } |
| |
| int64_t reference_time = zx::clock::get_monotonic().get(); |
| int64_t media_time = timeline_function_(reference_time); |
| timeline_function_ = media::TimelineFunction(media_time, reference_time, 0, 1); |
| callback(reference_time, media_time); |
| } |
| |
| void FakeAudioRenderer::PauseNoReply() { |
| Pause([](int64_t reference_time, int64_t media_time) {}); |
| } |
| |
| void FakeAudioRenderer::BindGainControl( |
| fidl::InterfaceRequest<fuchsia::media::audio::GainControl> request) { |
| FX_NOTIMPLEMENTED(); |
| } |
| |
| void FakeAudioRenderer::EnableMinLeadTimeEvents(bool enabled) { |
| if (enabled) { |
| binding_.events().OnMinLeadTimeChanged(min_lead_time_ns_); |
| } |
| } |
| |
| void FakeAudioRenderer::GetMinLeadTime(GetMinLeadTimeCallback callback) { |
| callback(min_lead_time_ns_); |
| } |
| |
| void FakeAudioRenderer::SetGain(float gain_db) { gain_ = gain_db; } |
| |
| void FakeAudioRenderer::SetMute(bool muted) { mute_ = muted; } |
| |
| void FakeAudioRenderer::MaybeScheduleRetirement() { |
| if (retain_packets_ || !progressing() || packet_queue_.empty()) { |
| return; |
| } |
| |
| int64_t packet_pts = packet_queue_.front().first.pts; |
| int64_t reference_time = timeline_function_.ApplyInverse(packet_pts); |
| if (packet_pts == delay_packet_retirement_pts_) { |
| reference_time += ZX_SEC(1); |
| } |
| |
| async::PostTaskForTime( |
| dispatcher_, |
| [this]() { |
| if (!progressing() || packet_queue_.empty()) { |
| return; |
| } |
| |
| int64_t reference_time = timeline_function_.ApplyInverse(packet_queue_.front().first.pts); |
| |
| if (reference_time <= zx::clock::get_monotonic().get()) { |
| packet_queue_.front().second(); |
| packet_queue_.pop(); |
| } |
| |
| MaybeScheduleRetirement(); |
| }, |
| zx::time(reference_time)); |
| } |
| |
| FakeAudioRenderer::PacketExpecter::PacketExpecter(const std::vector<PacketInfo>&& info) |
| : info_(std::move(info)), iter_(info_.begin()) {} |
| |
| bool FakeAudioRenderer::PacketExpecter::IsExpected(const fuchsia::media::StreamPacket& packet, |
| const void* start) { |
| if (iter_ == info_.end()) { |
| return false; |
| } |
| |
| if (iter_->pts() != packet.pts || iter_->size() != packet.payload_size || |
| iter_->hash() != |
| PacketInfo::Hash(reinterpret_cast<const uint8_t*>(start) + packet.payload_offset, |
| packet.payload_size)) { |
| } |
| |
| ++iter_; |
| return true; |
| } |
| |
| } // namespace test |
| } // namespace media_player |