1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153 |
- // Copyright 2021 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 <fuchsia/media/audio/cpp/fidl_test_base.h>
- #include <fuchsia/media/cpp/fidl_test_base.h>
- #include <lib/fidl/cpp/binding.h>
- #include "base/containers/queue.h"
- #include "base/fuchsia/fuchsia_logging.h"
- #include "base/logging.h"
- #include "base/test/bind.h"
- #include "base/test/task_environment.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/passthrough_sysmem_buffer_stream.h"
- #include "media/fuchsia/common/sysmem_client.h"
- #include "testing/gtest/include/gtest/gtest.h"
- #include "third_party/abseil-cpp/absl/types/optional.h"
- namespace {
- constexpr int kDefaultSampleRate = 48000;
- constexpr base::TimeDelta kPacketDuration = base::Milliseconds(20);
- constexpr base::TimeDelta kMinLeadTime = base::Milliseconds(100);
- constexpr base::TimeDelta kMaxLeadTime = base::Milliseconds(500);
- const base::TimeDelta kTimeStep = base::Milliseconds(2);
- class TestDemuxerStream : public media::DemuxerStream {
- public:
- struct ReadResult {
- explicit ReadResult(scoped_refptr<media::DecoderBuffer> buffer)
- : buffer(buffer) {}
- explicit ReadResult(const media::AudioDecoderConfig& config)
- : config(config) {}
- absl::optional<media::AudioDecoderConfig> config;
- scoped_refptr<media::DecoderBuffer> buffer;
- };
- explicit TestDemuxerStream(const media::AudioDecoderConfig& config)
- : config_(config) {}
- ~TestDemuxerStream() override {}
- void QueueReadResult(ReadResult read_result) {
- CHECK(read_result.config.has_value() == !read_result.buffer);
- read_queue_.push(std::move(read_result));
- SatisfyRead();
- }
- void DiscardQueueAndAbortRead() {
- while (!read_queue_.empty())
- read_queue_.pop();
- if (read_cb_)
- std::move(read_cb_).Run(kAborted, nullptr);
- }
- bool is_read_pending() const { return !!read_cb_; }
- // DemuxerStream implementation.
- void Read(ReadCB read_cb) override {
- read_cb_ = std::move(read_cb);
- SatisfyRead();
- }
- media::AudioDecoderConfig audio_decoder_config() override { return config_; }
- media::VideoDecoderConfig video_decoder_config() override {
- NOTREACHED();
- return media::VideoDecoderConfig();
- }
- Type type() const override { return AUDIO; }
- media::StreamLiveness liveness() const override {
- return media::StreamLiveness::kRecorded;
- }
- bool SupportsConfigChanges() override { return true; }
- private:
- void SatisfyRead() {
- if (read_queue_.empty() || !read_cb_)
- return;
- auto result = std::move(read_queue_.front());
- read_queue_.pop();
- Status status;
- if (result.config) {
- config_ = result.config.value();
- status = kConfigChanged;
- } else {
- status = kOk;
- }
- std::move(read_cb_).Run(status, result.buffer);
- }
- media::AudioDecoderConfig config_;
- ReadCB read_cb_;
- base::queue<ReadResult> read_queue_;
- };
- class TestStreamSink : public fuchsia::media::testing::StreamSink_TestBase {
- public:
- TestStreamSink(
- std::vector<zx::vmo> buffers,
- fuchsia::media::AudioStreamType stream_type,
- std::unique_ptr<fuchsia::media::Compression> compression,
- fidl::InterfaceRequest<fuchsia::media::StreamSink> stream_sink_request)
- : binding_(this, std::move(stream_sink_request)),
- buffers_(std::move(buffers)),
- stream_type_(std::move(stream_type)),
- compression_(std::move(compression)) {}
- const std::vector<zx::vmo>& buffers() const { return buffers_; }
- const fuchsia::media::AudioStreamType& stream_type() const {
- return stream_type_;
- }
- const fuchsia::media::Compression* compression() const {
- return compression_.get();
- }
- std::vector<fuchsia::media::StreamPacket>* received_packets() {
- return &received_packets_;
- }
- std::vector<fuchsia::media::StreamPacket>* discarded_packets() {
- return &discarded_packets_;
- }
- bool received_end_of_stream() const { return received_end_of_stream_; }
- // fuchsia::media::StreamSink overrides.
- void SendPacket(fuchsia::media::StreamPacket packet,
- SendPacketCallback callback) override {
- EXPECT_FALSE(received_end_of_stream_);
- received_packets_.push_back(std::move(packet));
- callback();
- }
- void EndOfStream() override {
- EXPECT_FALSE(received_end_of_stream_);
- received_end_of_stream_ = true;
- }
- void DiscardAllPackets(DiscardAllPacketsCallback callback) override {
- DiscardAllPacketsNoReply();
- received_end_of_stream_ = false;
- callback();
- }
- void DiscardAllPacketsNoReply() override {
- std::move(std::begin(received_packets_), std::end(received_packets_),
- std::back_inserter(discarded_packets_));
- received_packets_.clear();
- }
- // Other methods are not expected to be called.
- void NotImplemented_(const std::string& name) final {
- FAIL() << ": " << name;
- }
- private:
- fidl::Binding<fuchsia::media::StreamSink> binding_;
- std::vector<zx::vmo> buffers_;
- fuchsia::media::AudioStreamType stream_type_;
- std::unique_ptr<fuchsia::media::Compression> compression_;
- std::vector<fuchsia::media::StreamPacket> received_packets_;
- std::vector<fuchsia::media::StreamPacket> discarded_packets_;
- bool received_end_of_stream_ = false;
- };
- class TestAudioConsumer
- : public fuchsia::media::testing::AudioConsumer_TestBase,
- public fuchsia::media::audio::testing::VolumeControl_TestBase {
- public:
- explicit TestAudioConsumer(
- fidl::InterfaceRequest<fuchsia::media::AudioConsumer> request)
- : binding_(this, std::move(request)), volume_control_binding_(this) {}
- std::unique_ptr<TestStreamSink> TakeStreamSink() {
- return std::move(stream_sink_);
- }
- std::unique_ptr<TestStreamSink> WaitStreamSinkConnected() {
- if (!stream_sink_) {
- base::RunLoop run_loop;
- wait_stream_sink_created_loop_ = &run_loop;
- run_loop.Run();
- wait_stream_sink_created_loop_ = nullptr;
- }
- EXPECT_TRUE(stream_sink_);
- return TakeStreamSink();
- }
- void WaitStarted() {
- if (started_)
- return;
- base::RunLoop run_loop;
- wait_started_loop_ = &run_loop;
- run_loop.Run();
- wait_started_loop_ = nullptr;
- EXPECT_TRUE(started_);
- }
- void UpdateStatus(absl::optional<base::TimeTicks> reference_time,
- absl::optional<base::TimeDelta> media_time) {
- fuchsia::media::AudioConsumerStatus status;
- if (reference_time) {
- CHECK(media_time);
- fuchsia::media::TimelineFunction timeline;
- timeline.reference_time = reference_time->ToZxTime();
- timeline.subject_time = media_time->ToZxDuration();
- timeline.reference_delta = 1000;
- timeline.subject_delta = static_cast<int>(playback_rate_ * 1000.0);
- status.set_presentation_timeline(std::move(timeline));
- }
- status.set_min_lead_time(kMinLeadTime.ToZxDuration());
- status.set_max_lead_time(kMaxLeadTime.ToZxDuration());
- status_update_ = std::move(status);
- if (status_callback_)
- CallStatusCallback();
- }
- void SignalEndOfStream() { binding_.events().OnEndOfStream(); }
- bool started() const { return started_; }
- base::TimeDelta start_media_time() const { return start_media_time_; }
- float playback_rate() const { return playback_rate_; }
- float volume() const { return volume_; }
- // fuchsia::media::AudioConsumer overrides.
- void CreateStreamSink(
- std::vector<zx::vmo> buffers,
- fuchsia::media::AudioStreamType stream_type,
- std::unique_ptr<fuchsia::media::Compression> compression,
- fidl::InterfaceRequest<fuchsia::media::StreamSink> stream_sink_request)
- override {
- create_stream_sink_called_ = true;
- stream_sink_ = std::make_unique<TestStreamSink>(
- std::move(buffers), std::move(stream_type), std::move(compression),
- std::move(stream_sink_request));
- if (wait_stream_sink_created_loop_)
- wait_stream_sink_created_loop_->Quit();
- }
- void Start(fuchsia::media::AudioConsumerStartFlags flags,
- int64_t reference_time,
- int64_t media_time) override {
- EXPECT_TRUE(create_stream_sink_called_);
- EXPECT_FALSE(started_);
- EXPECT_EQ(reference_time, fuchsia::media::NO_TIMESTAMP);
- started_ = true;
- start_media_time_ = base::TimeDelta::FromZxDuration(media_time);
- if (wait_started_loop_)
- wait_started_loop_->Quit();
- }
- void Stop() override {
- EXPECT_TRUE(started_);
- started_ = false;
- }
- void SetRate(float rate) override { playback_rate_ = rate; }
- void BindVolumeControl(
- fidl::InterfaceRequest<fuchsia::media::audio::VolumeControl>
- volume_control_request) override {
- volume_control_binding_.Bind(std::move(volume_control_request));
- }
- void WatchStatus(WatchStatusCallback callback) override {
- EXPECT_FALSE(!!status_callback_);
- status_callback_ = std::move(callback);
- if (status_update_) {
- CallStatusCallback();
- }
- }
- // fuchsia::media::audio::VolumeControl overrides.
- void SetVolume(float volume) override { volume_ = volume; }
- // Other methods are not expected to be called.
- void NotImplemented_(const std::string& name) final {
- FAIL() << ": " << name;
- }
- private:
- void CallStatusCallback() {
- EXPECT_TRUE(status_callback_);
- EXPECT_TRUE(status_update_);
- std::move(status_callback_)(std::move(status_update_.value()));
- status_callback_ = {};
- status_update_ = absl::nullopt;
- }
- fidl::Binding<fuchsia::media::AudioConsumer> binding_;
- fidl::Binding<fuchsia::media::audio::VolumeControl> volume_control_binding_;
- std::unique_ptr<TestStreamSink> stream_sink_;
- base::RunLoop* wait_stream_sink_created_loop_ = nullptr;
- base::RunLoop* wait_started_loop_ = nullptr;
- bool create_stream_sink_called_ = false;
- WatchStatusCallback status_callback_;
- absl::optional<fuchsia::media::AudioConsumerStatus> status_update_;
- bool started_ = false;
- base::TimeDelta start_media_time_;
- float playback_rate_ = 1.0;
- float volume_ = 1.0;
- };
- class TestRendererClient : public media::RendererClient {
- public:
- TestRendererClient() = default;
- ~TestRendererClient() {
- EXPECT_EQ(expected_error_, media::PIPELINE_OK);
- EXPECT_FALSE(expect_eos_);
- }
- void ExpectError(media::PipelineStatus expected_error) {
- EXPECT_EQ(expected_error_, media::PIPELINE_OK);
- expected_error_ = expected_error;
- }
- void ExpectEos() {
- EXPECT_FALSE(expect_eos_);
- expect_eos_ = true;
- }
- media::BufferingState buffering_state() const { return buffering_state_; }
- absl::optional<media::AudioDecoderConfig> last_config_change() const {
- return last_config_change_;
- }
- // RendererClient implementation.
- void OnError(media::PipelineStatus status) override {
- EXPECT_EQ(status, expected_error_);
- EXPECT_FALSE(have_error_);
- have_error_ = true;
- expected_error_ = media::PIPELINE_OK;
- }
- void OnFallback(media::PipelineStatus status) override {}
- void OnEnded() override {
- EXPECT_TRUE(expect_eos_);
- expect_eos_ = false;
- }
- void OnStatisticsUpdate(const media::PipelineStatistics& stats) override {
- bytes_decoded_ += stats.audio_bytes_decoded;
- }
- void OnBufferingStateChange(
- media::BufferingState state,
- media::BufferingStateChangeReason reason) override {
- buffering_state_ = state;
- }
- void OnWaiting(media::WaitingReason reason) override {}
- void OnAudioConfigChange(const media::AudioDecoderConfig& config) override {
- last_config_change_ = config;
- }
- void OnVideoConfigChange(const media::VideoDecoderConfig& config) override {
- FAIL();
- }
- void OnVideoNaturalSizeChange(const gfx::Size& size) override { FAIL(); }
- void OnVideoOpacityChange(bool opaque) override { FAIL(); }
- void OnVideoFrameRateChange(absl::optional<int> fps) override { FAIL(); }
- private:
- media::PipelineStatus expected_error_ = media::PIPELINE_OK;
- bool have_error_ = false;
- bool expect_eos_ = false;
- media::BufferingState buffering_state_ = media::BUFFERING_HAVE_NOTHING;
- size_t bytes_decoded_ = 0;
- absl::optional<media::AudioDecoderConfig> last_config_change_;
- };
- // media::SysmemBufferStream that asynchronously decouples buffer production
- // from buffer consumption. Used to simulate stream decryptor.
- class AsyncSysmemBufferStream : public media::SysmemBufferStream {
- public:
- AsyncSysmemBufferStream();
- ~AsyncSysmemBufferStream() override;
- AsyncSysmemBufferStream(const AsyncSysmemBufferStream&) = delete;
- AsyncSysmemBufferStream& operator=(const AsyncSysmemBufferStream&) = delete;
- // media::SysmemBufferStream implementation:
- void Initialize(Sink* sink,
- size_t min_buffer_size,
- size_t min_buffer_count) override;
- void EnqueueBuffer(scoped_refptr<media::DecoderBuffer> buffer) override;
- void Reset() override;
- private:
- void DoEnqueueBuffer(scoped_refptr<media::DecoderBuffer> buffer);
- media::SysmemAllocatorClient sysmem_allocator_;
- media::PassthroughSysmemBufferStream passthrough_stream_;
- bool is_at_end_of_stream_ = false;
- base::WeakPtrFactory<AsyncSysmemBufferStream> weak_factory_{this};
- };
- AsyncSysmemBufferStream::AsyncSysmemBufferStream()
- : sysmem_allocator_("AsyncSysmemBufferStream"),
- passthrough_stream_(&sysmem_allocator_) {}
- AsyncSysmemBufferStream::~AsyncSysmemBufferStream() = default;
- void AsyncSysmemBufferStream::Initialize(Sink* sink,
- size_t min_buffer_size,
- size_t min_buffer_count) {
- passthrough_stream_.Initialize(sink, min_buffer_size, min_buffer_count);
- }
- void AsyncSysmemBufferStream::EnqueueBuffer(
- scoped_refptr<media::DecoderBuffer> buffer) {
- if (buffer->end_of_stream()) {
- EXPECT_FALSE(is_at_end_of_stream_);
- is_at_end_of_stream_ = true;
- }
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(&AsyncSysmemBufferStream::DoEnqueueBuffer,
- weak_factory_.GetWeakPtr(), std::move(buffer)));
- }
- void AsyncSysmemBufferStream::Reset() {
- passthrough_stream_.Reset();
- // Drop pending DoEnqueueBuffer tasks.
- weak_factory_.InvalidateWeakPtrs();
- is_at_end_of_stream_ = false;
- }
- void AsyncSysmemBufferStream::DoEnqueueBuffer(
- scoped_refptr<media::DecoderBuffer> buffer) {
- passthrough_stream_.EnqueueBuffer(std::move(buffer));
- }
- class TestFuchsiaCdmContext : public media::CdmContext,
- public media::FuchsiaCdmContext {
- public:
- // CdmContext overrides.
- media::FuchsiaCdmContext* GetFuchsiaCdmContext() override { return this; }
- // media::FuchsiaCdmContext implementation.
- std::unique_ptr<media::SysmemBufferStream> CreateStreamDecryptor(
- bool secure_mode) override {
- ++num_decryptors_;
- return std::make_unique<AsyncSysmemBufferStream>();
- }
- size_t num_decryptors() { return num_decryptors_; }
- private:
- size_t num_decryptors_ = 0;
- };
- } // namespace
- class WebEngineAudioRendererTestBase : public testing::Test {
- public:
- WebEngineAudioRendererTestBase() = default;
- ~WebEngineAudioRendererTestBase() override = default;
- void CreateUninitializedRenderer();
- void InitializeRenderer();
- void CreateAndInitializeRenderer();
- void ProduceDemuxerPacket(base::TimeDelta duration);
- void FillDemuxerStream(base::TimeDelta end_pos);
- void FillBuffer();
- void StartPlayback(base::TimeDelta start_time = base::TimeDelta());
- void CheckGetWallClockTimes(absl::optional<base::TimeDelta> media_timestamp,
- base::TimeTicks expected_wall_clock,
- bool is_time_moving);
- void TestPcmStream(media::SampleFormat sample_format,
- size_t bytes_per_sample_input,
- fuchsia::media::AudioSampleFormat fuchsia_sample_format,
- size_t bytes_per_sample_output);
- // Starts playback from `start_time` at the specified `playback_rate` and
- // verifies that the clock works correctly.
- void StartPlaybackAndVerifyClock(base::TimeDelta start_time,
- float playback_rate);
- // Returns initial config for the `demuxer_stream_`.
- virtual media::AudioDecoderConfig GetStreamConfig() = 0;
- protected:
- base::test::SingleThreadTaskEnvironment task_environment_{
- base::test::SingleThreadTaskEnvironment::MainThreadType::IO,
- base::test::TaskEnvironment::TimeSource::MOCK_TIME};
- TestFuchsiaCdmContext cdm_context_;
- std::unique_ptr<TestAudioConsumer> audio_consumer_;
- std::unique_ptr<TestStreamSink> stream_sink_;
- std::unique_ptr<TestDemuxerStream> demuxer_stream_;
- TestRendererClient client_;
- std::unique_ptr<media::AudioRenderer> audio_renderer_;
- media::TimeSource* time_source_;
- base::TimeDelta demuxer_stream_pos_;
- };
- void WebEngineAudioRendererTestBase::CreateUninitializedRenderer() {
- fidl::InterfaceHandle<fuchsia::media::AudioConsumer> audio_consumer_handle;
- audio_consumer_ =
- std::make_unique<TestAudioConsumer>(audio_consumer_handle.NewRequest());
- audio_renderer_ = std::make_unique<WebEngineAudioRenderer>(
- /*media_log=*/nullptr, std::move(audio_consumer_handle));
- time_source_ = audio_renderer_->GetTimeSource();
- }
- void WebEngineAudioRendererTestBase::InitializeRenderer() {
- if (!demuxer_stream_) {
- demuxer_stream_ = std::make_unique<TestDemuxerStream>(GetStreamConfig());
- }
- base::RunLoop run_loop;
- media::PipelineStatus pipeline_status;
- audio_renderer_->Initialize(
- demuxer_stream_.get(), &cdm_context_, &client_,
- base::BindLambdaForTesting(
- [&run_loop, &pipeline_status](media::PipelineStatus s) {
- pipeline_status = s;
- run_loop.Quit();
- }));
- run_loop.Run();
- ASSERT_EQ(pipeline_status, media::PIPELINE_OK);
- audio_consumer_->UpdateStatus(absl::nullopt, absl::nullopt);
- task_environment_.RunUntilIdle();
- }
- void WebEngineAudioRendererTestBase::CreateAndInitializeRenderer() {
- CreateUninitializedRenderer();
- InitializeRenderer();
- }
- void WebEngineAudioRendererTestBase::ProduceDemuxerPacket(
- base::TimeDelta duration) {
- // Create a dummy packet that contains just 1 byte.
- const size_t kBufferSize = 1;
- scoped_refptr<media::DecoderBuffer> buffer =
- new media::DecoderBuffer(kBufferSize);
- buffer->set_timestamp(demuxer_stream_pos_);
- buffer->set_duration(duration);
- demuxer_stream_pos_ += duration;
- demuxer_stream_->QueueReadResult(TestDemuxerStream::ReadResult(buffer));
- }
- void WebEngineAudioRendererTestBase::FillDemuxerStream(
- base::TimeDelta end_pos) {
- EXPECT_LT(demuxer_stream_pos_, end_pos);
- while (demuxer_stream_pos_ < end_pos) {
- ProduceDemuxerPacket(kPacketDuration);
- }
- }
- void WebEngineAudioRendererTestBase::FillBuffer() {
- if (!stream_sink_) {
- stream_sink_ = audio_consumer_->WaitStreamSinkConnected();
- }
- // The renderer expects one extra packet after reaching kMinLeadTime to get
- // to the media::BUFFERING_HAVE_ENOUGH state.
- const size_t kNumPackets = kMinLeadTime / kPacketDuration + 1;
- for (size_t i = 0; i < kNumPackets; ++i) {
- ProduceDemuxerPacket(kPacketDuration);
- }
- task_environment_.RunUntilIdle();
- // Renderer should not start reading demuxer untile StartPlaying() is
- // called.
- EXPECT_EQ(client_.buffering_state(), media::BUFFERING_HAVE_NOTHING);
- EXPECT_EQ(stream_sink_->received_packets()->size(), 0U);
- // Start playback. The renderer should push queued packets to the
- // AudioConsumer and updated buffering state.
- audio_renderer_->StartPlaying();
- task_environment_.RunUntilIdle();
- EXPECT_EQ(stream_sink_->received_packets()->size(), kNumPackets);
- EXPECT_EQ(client_.buffering_state(), media::BUFFERING_HAVE_ENOUGH);
- }
- void WebEngineAudioRendererTestBase::StartPlayback(base::TimeDelta start_time) {
- EXPECT_FALSE(audio_consumer_->started());
- time_source_->SetMediaTime(start_time);
- ASSERT_NO_FATAL_FAILURE(FillBuffer());
- time_source_->StartTicking();
- task_environment_.RunUntilIdle();
- EXPECT_TRUE(audio_consumer_->started());
- EXPECT_EQ(audio_consumer_->start_media_time(), start_time);
- audio_consumer_->UpdateStatus(base::TimeTicks::Now(), start_time);
- task_environment_.RunUntilIdle();
- }
- void WebEngineAudioRendererTestBase::CheckGetWallClockTimes(
- absl::optional<base::TimeDelta> media_timestamp,
- base::TimeTicks expected_wall_clock,
- bool is_time_moving) {
- std::vector<base::TimeDelta> media_timestamps;
- if (media_timestamp)
- media_timestamps.push_back(media_timestamp.value());
- std::vector<base::TimeTicks> wall_clock;
- bool result = time_source_->GetWallClockTimes(media_timestamps, &wall_clock);
- EXPECT_EQ(wall_clock[0], expected_wall_clock);
- EXPECT_EQ(result, is_time_moving);
- }
- void WebEngineAudioRendererTestBase::StartPlaybackAndVerifyClock(
- base::TimeDelta start_time,
- float playback_rate) {
- time_source_->SetMediaTime(start_time);
- time_source_->SetPlaybackRate(playback_rate);
- demuxer_stream_pos_ = start_time;
- ASSERT_NO_FATAL_FAILURE(FillBuffer());
- EXPECT_FALSE(audio_consumer_->started());
- time_source_->StartTicking();
- task_environment_.RunUntilIdle();
- EXPECT_TRUE(audio_consumer_->started());
- // Start position should be reported before updated status is received.
- EXPECT_EQ(time_source_->CurrentMediaTime(), start_time);
- task_environment_.FastForwardBy(kTimeStep);
- EXPECT_EQ(time_source_->CurrentMediaTime(), start_time);
- CheckGetWallClockTimes(absl::nullopt, base::TimeTicks(), false);
- CheckGetWallClockTimes(start_time + kTimeStep,
- base::TimeTicks::Now() + kTimeStep, false);
- // MediaTime will start moving once AudioConsumer updates timeline.
- const base::TimeDelta kStartDelay = base::Milliseconds(3);
- base::TimeTicks start_wall_clock = base::TimeTicks::Now() + kStartDelay;
- audio_consumer_->UpdateStatus(start_wall_clock, start_time);
- task_environment_.RunUntilIdle();
- EXPECT_EQ(time_source_->CurrentMediaTime(),
- start_time - kStartDelay * playback_rate);
- task_environment_.FastForwardBy(kTimeStep);
- EXPECT_EQ(time_source_->CurrentMediaTime(),
- start_time + (-kStartDelay + kTimeStep) * playback_rate);
- CheckGetWallClockTimes(absl::nullopt, base::TimeTicks::Now(), true);
- CheckGetWallClockTimes(start_time + kTimeStep,
- start_wall_clock + kTimeStep / playback_rate, true);
- CheckGetWallClockTimes(start_time + 2 * kTimeStep,
- start_wall_clock + 2.0 * kTimeStep / playback_rate,
- true);
- }
- void WebEngineAudioRendererTestBase::TestPcmStream(
- media::SampleFormat sample_format,
- size_t bytes_per_sample_input,
- fuchsia::media::AudioSampleFormat fuchsia_sample_format,
- size_t bytes_per_sample_output) {
- media::AudioDecoderConfig config(
- media::AudioCodec::kPCM, sample_format, media::CHANNEL_LAYOUT_STEREO,
- kDefaultSampleRate, {}, media::EncryptionScheme::kUnencrypted);
- demuxer_stream_ = std::make_unique<TestDemuxerStream>(config);
- ASSERT_NO_FATAL_FAILURE(CreateAndInitializeRenderer());
- stream_sink_ = audio_consumer_->WaitStreamSinkConnected();
- EXPECT_EQ(stream_sink_->stream_type().sample_format, fuchsia_sample_format);
- // Create a dummy packet that contains 1 sample.
- const size_t kNumSamples = 10;
- const size_t kChannels = 2;
- size_t input_buffer_size = kNumSamples * kChannels * bytes_per_sample_input;
- scoped_refptr<media::DecoderBuffer> buffer =
- new media::DecoderBuffer(input_buffer_size);
- buffer->set_timestamp(demuxer_stream_pos_);
- buffer->set_duration(kPacketDuration);
- for (size_t i = 0; i < input_buffer_size; ++i) {
- buffer->writable_data()[i] = i;
- }
- demuxer_stream_->QueueReadResult(TestDemuxerStream::ReadResult(buffer));
- // Start playback. The renderer will process the packet queued above.
- audio_renderer_->StartPlaying();
- task_environment_.RunUntilIdle();
- ASSERT_EQ(stream_sink_->received_packets()->size(), 1U);
- auto packet = stream_sink_->received_packets()->at(0);
- // Read and verify packet content
- size_t output_size = kNumSamples * kChannels * bytes_per_sample_output;
- EXPECT_EQ(packet.payload_size, output_size);
- uint8_t data[output_size];
- zx_status_t result = stream_sink_->buffers()[packet.payload_buffer_id].read(
- data, 0, output_size);
- ZX_CHECK(result == ZX_OK, result);
- for (size_t i = 0; i < output_size; ++i) {
- size_t pos_within_sample = i % bytes_per_sample_output;
- uint8_t expected_value =
- (pos_within_sample < bytes_per_sample_input)
- ? (i / bytes_per_sample_output * bytes_per_sample_input +
- pos_within_sample)
- : 0;
- EXPECT_EQ(data[i], expected_value);
- }
- }
- struct RendererTestConfig {
- bool simulate_fuchsia_cdm;
- };
- class WebEngineAudioRendererTest
- : public WebEngineAudioRendererTestBase,
- public testing::WithParamInterface<RendererTestConfig> {
- public:
- media::AudioDecoderConfig GetStreamConfig() final {
- auto encryption_scheme = GetParam().simulate_fuchsia_cdm
- ? media::EncryptionScheme::kCenc
- : media::EncryptionScheme::kUnencrypted;
- return media::AudioDecoderConfig(
- media::AudioCodec::kPCM, media::kSampleFormatF32,
- media::CHANNEL_LAYOUT_MONO, kDefaultSampleRate, {}, encryption_scheme);
- }
- };
- // Run all WebEngineAudioRendererTests with CDM enabled and disabled.
- INSTANTIATE_TEST_SUITE_P(Unencrypted,
- WebEngineAudioRendererTest,
- testing::Values(RendererTestConfig{false}));
- INSTANTIATE_TEST_SUITE_P(Encrypted,
- WebEngineAudioRendererTest,
- testing::Values(RendererTestConfig{true}));
- TEST_P(WebEngineAudioRendererTest, Initialize) {
- ASSERT_NO_FATAL_FAILURE(CreateAndInitializeRenderer());
- }
- TEST_P(WebEngineAudioRendererTest, InitializeAndBuffer) {
- ASSERT_NO_FATAL_FAILURE(CreateAndInitializeRenderer());
- ASSERT_NO_FATAL_FAILURE(FillBuffer());
- // Extra packets should be sent to AudioConsumer immediately.
- stream_sink_->received_packets()->clear();
- ProduceDemuxerPacket(base::Milliseconds(10));
- task_environment_.RunUntilIdle();
- EXPECT_EQ(stream_sink_->received_packets()->size(), 1U);
- }
- TEST_P(WebEngineAudioRendererTest, StartPlaybackBeforeStreamSinkConnected) {
- ASSERT_NO_FATAL_FAILURE(CreateAndInitializeRenderer());
- // Start playing immediately after initialization. The renderer should wait
- // for buffers to be allocated before it starts reading from the demuxer.
- audio_renderer_->StartPlaying();
- ProduceDemuxerPacket(base::Milliseconds(10));
- task_environment_.RunUntilIdle();
- stream_sink_ = audio_consumer_->WaitStreamSinkConnected();
- task_environment_.RunUntilIdle();
- EXPECT_EQ(stream_sink_->received_packets()->size(), 1U);
- }
- TEST_P(WebEngineAudioRendererTest, StartTicking) {
- ASSERT_NO_FATAL_FAILURE(CreateAndInitializeRenderer());
- ASSERT_NO_FATAL_FAILURE(StartPlaybackAndVerifyClock(
- /*start_pos=*/base::Milliseconds(123),
- /*playback_rate=*/1.0));
- }
- TEST_P(WebEngineAudioRendererTest, StartTickingRate1_5) {
- ASSERT_NO_FATAL_FAILURE(CreateAndInitializeRenderer());
- ASSERT_NO_FATAL_FAILURE(StartPlaybackAndVerifyClock(
- /*start_pos=*/base::Milliseconds(123),
- /*playback_rate=*/1.5));
- }
- TEST_P(WebEngineAudioRendererTest, StartTickingRate0_5) {
- ASSERT_NO_FATAL_FAILURE(CreateAndInitializeRenderer());
- ASSERT_NO_FATAL_FAILURE(StartPlaybackAndVerifyClock(
- /*start_pos=*/base::Milliseconds(123),
- /*playback_rate=*/0.5));
- }
- // Verify that the renderer doesn't send packets more than kMaxLeadTime ahead of
- // time.
- TEST_P(WebEngineAudioRendererTest, MaxLeadTime) {
- ASSERT_NO_FATAL_FAILURE(CreateAndInitializeRenderer());
- ASSERT_NO_FATAL_FAILURE(FillBuffer());
- // Queue packets up to kMaxLeadTime with 10 extra packets. The Renderer
- // shouldn't read these extra packets at the end.
- FillDemuxerStream(kMaxLeadTime + kPacketDuration * 10);
- task_environment_.RunUntilIdle();
- // Verify that the renderer has filled the buffer to kMaxLeadTime.
- size_t expected_packets = kMaxLeadTime / kPacketDuration;
- EXPECT_EQ(expected_packets, stream_sink_->received_packets()->size());
- }
- TEST_P(WebEngineAudioRendererTest, Seek) {
- ASSERT_NO_FATAL_FAILURE(CreateAndInitializeRenderer());
- constexpr base::TimeDelta kStartPos = base::TimeDelta();
- ASSERT_NO_FATAL_FAILURE(StartPlayback(kStartPos));
- task_environment_.FastForwardBy(kTimeStep);
- time_source_->StopTicking();
- demuxer_stream_->DiscardQueueAndAbortRead();
- // Media time should be stopped after StopTicking().
- EXPECT_EQ(time_source_->CurrentMediaTime(), kStartPos + kTimeStep);
- task_environment_.FastForwardBy(kTimeStep);
- EXPECT_EQ(time_source_->CurrentMediaTime(), kStartPos + kTimeStep);
- // Flush the renderer.
- base::RunLoop run_loop;
- audio_renderer_->Flush(run_loop.QuitClosure());
- run_loop.Run();
- // Restart playback from a new position.
- const base::TimeDelta kSeekPos = base::Milliseconds(123);
- ASSERT_NO_FATAL_FAILURE(StartPlaybackAndVerifyClock(kSeekPos,
- /*playback_rate=*/1.0));
- ProduceDemuxerPacket(kPacketDuration);
- // Verify that old packets were discarded and StreamSink started received
- // packets at the correct position.
- EXPECT_GT(stream_sink_->discarded_packets()->size(), 0u);
- EXPECT_EQ(stream_sink_->received_packets()->at(0).pts,
- kSeekPos.ToZxDuration());
- }
- TEST_F(WebEngineAudioRendererTest, PcmU8Stream) {
- TestPcmStream(media::kSampleFormatU8, 1,
- fuchsia::media::AudioSampleFormat::UNSIGNED_8, 1);
- }
- TEST_F(WebEngineAudioRendererTest, PcmS16Stream) {
- TestPcmStream(media::kSampleFormatS16, 2,
- fuchsia::media::AudioSampleFormat::SIGNED_16, 2);
- }
- TEST_F(WebEngineAudioRendererTest, PcmS24Stream) {
- TestPcmStream(media::kSampleFormatS24, 3,
- fuchsia::media::AudioSampleFormat::SIGNED_24_IN_32, 4);
- }
- TEST_F(WebEngineAudioRendererTest, PcmF32Stream) {
- TestPcmStream(media::kSampleFormatF32, 4,
- fuchsia::media::AudioSampleFormat::FLOAT, 4);
- }
- struct ConfigChangeTestConfig {
- bool encrypted_head;
- bool encrypted_tail;
- };
- class WebEngineAudioRendererConfgChangeTest
- : public WebEngineAudioRendererTestBase,
- public testing::WithParamInterface<ConfigChangeTestConfig> {
- media::AudioDecoderConfig GetStreamConfig() final {
- auto encryption_scheme = GetParam().encrypted_head
- ? media::EncryptionScheme::kCenc
- : media::EncryptionScheme::kUnencrypted;
- return media::AudioDecoderConfig(
- media::AudioCodec::kPCM, media::kSampleFormatF32,
- media::CHANNEL_LAYOUT_MONO, kDefaultSampleRate, {}, encryption_scheme);
- }
- };
- // Run all WebEngineAudioRendererTests with CDM enabled and disabled.
- INSTANTIATE_TEST_SUITE_P(ConfigChange,
- WebEngineAudioRendererConfgChangeTest,
- testing::Values(ConfigChangeTestConfig{false, false},
- ConfigChangeTestConfig{false, true},
- ConfigChangeTestConfig{true, false},
- ConfigChangeTestConfig{true, true}));
- TEST_P(WebEngineAudioRendererConfgChangeTest, ConfigChange) {
- ASSERT_NO_FATAL_FAILURE(CreateAndInitializeRenderer());
- ASSERT_NO_FATAL_FAILURE(StartPlayback());
- const auto kConfigChangePos = base::Seconds(1);
- // Queue packets up to kConfigChangePos.
- FillDemuxerStream(kConfigChangePos);
- // Verify that decryptor was initialized only if the beginning of the stream
- // is encrypted.
- EXPECT_EQ(cdm_context_.num_decryptors(), GetParam().encrypted_head ? 1U : 0U);
- // New Config
- const size_t kNewSampleRate = 44100;
- const std::vector<uint8_t> kArbitraryExtraData = {1, 2, 3};
- auto mew_encryption_scheme = GetParam().encrypted_tail
- ? media::EncryptionScheme::kCenc
- : media::EncryptionScheme::kUnencrypted;
- media::AudioDecoderConfig updated_config(
- media::AudioCodec::kOpus, media::kSampleFormatF32,
- media::CHANNEL_LAYOUT_STEREO, kNewSampleRate, kArbitraryExtraData,
- mew_encryption_scheme);
- demuxer_stream_->QueueReadResult(
- TestDemuxerStream::ReadResult(updated_config));
- // Queue one more packet with the new config.
- ProduceDemuxerPacket(kPacketDuration * 2);
- task_environment_.FastForwardBy(kConfigChangePos);
- // The renderer should have created new StreamSink when config was changed.
- auto new_stream_sink = audio_consumer_->WaitStreamSinkConnected();
- ASSERT_TRUE(new_stream_sink);
- task_environment_.RunUntilIdle();
- // Verify that decryptor was re-created if the stream is encrypted after
- // the config change.
- EXPECT_EQ(cdm_context_.num_decryptors(),
- (GetParam().encrypted_head ? 1U : 0U) +
- (GetParam().encrypted_tail ? 1U : 0U));
- ASSERT_TRUE(client_.last_config_change().has_value());
- EXPECT_TRUE(client_.last_config_change()->Matches(updated_config));
- EXPECT_EQ(stream_sink_->stream_type().channels, 1U);
- EXPECT_EQ(stream_sink_->stream_type().frames_per_second,
- static_cast<uint32_t>(kDefaultSampleRate));
- EXPECT_EQ(stream_sink_->received_packets()->size(),
- kConfigChangePos / kPacketDuration);
- EXPECT_EQ(new_stream_sink->stream_type().channels,
- static_cast<uint32_t>(updated_config.channels()));
- EXPECT_EQ(new_stream_sink->stream_type().frames_per_second, kNewSampleRate);
- EXPECT_TRUE(new_stream_sink->compression());
- EXPECT_EQ(new_stream_sink->compression()->type,
- fuchsia::media::AUDIO_ENCODING_OPUS);
- EXPECT_EQ(new_stream_sink->compression()->parameters, kArbitraryExtraData);
- EXPECT_EQ(new_stream_sink->received_packets()->size(), 1U);
- EXPECT_EQ(new_stream_sink->received_packets()->at(0).pts,
- kConfigChangePos.ToZxDuration());
- }
- TEST_P(WebEngineAudioRendererTest, UpdateTimeline) {
- ASSERT_NO_FATAL_FAILURE(CreateAndInitializeRenderer());
- ASSERT_NO_FATAL_FAILURE(StartPlayback());
- FillDemuxerStream(base::Seconds(2));
- const auto kTimelineChangePos = base::Seconds(1);
- task_environment_.FastForwardBy(kTimelineChangePos);
- // Shift the timeline by 2ms.
- const auto kMediaDelta = base::Milliseconds(2);
- audio_consumer_->UpdateStatus(base::TimeTicks::Now(),
- kTimelineChangePos + kMediaDelta);
- task_environment_.RunUntilIdle();
- EXPECT_EQ(time_source_->CurrentMediaTime(), kTimelineChangePos + kMediaDelta);
- task_environment_.FastForwardBy(kTimeStep);
- EXPECT_EQ(time_source_->CurrentMediaTime(),
- kTimelineChangePos + kMediaDelta + kTimeStep);
- }
- TEST_P(WebEngineAudioRendererTest, PauseAndResume) {
- ASSERT_NO_FATAL_FAILURE(CreateAndInitializeRenderer());
- ASSERT_NO_FATAL_FAILURE(StartPlayback());
- const auto kPauseTimestamp = base::Seconds(1);
- const auto kStreamLength = base::Seconds(2);
- FillDemuxerStream(kStreamLength);
- task_environment_.FastForwardBy(kPauseTimestamp);
- // Pause playback by setting playback rate to 0.0.
- time_source_->SetPlaybackRate(0.0);
- task_environment_.RunUntilIdle();
- EXPECT_EQ(audio_consumer_->playback_rate(), 0.0);
- task_environment_.FastForwardBy(kTimeStep);
- audio_consumer_->UpdateStatus(base::TimeTicks::Now(), kPauseTimestamp);
- task_environment_.RunUntilIdle();
- const size_t kExpectedQueuedPackets =
- (kPauseTimestamp + kMaxLeadTime) / kPacketDuration + 1;
- EXPECT_EQ(stream_sink_->received_packets()->size(), kExpectedQueuedPackets);
- EXPECT_EQ(time_source_->CurrentMediaTime(), kPauseTimestamp);
- // Keep the stream paused for 10 seconds. The Renderer should not be sending
- // new packets
- task_environment_.FastForwardBy(base::Seconds(10));
- EXPECT_EQ(stream_sink_->received_packets()->size(), kExpectedQueuedPackets);
- EXPECT_EQ(time_source_->CurrentMediaTime(), kPauseTimestamp);
- // Resume playback.
- time_source_->SetPlaybackRate(1.0);
- task_environment_.RunUntilIdle();
- EXPECT_EQ(audio_consumer_->playback_rate(), 1.0);
- audio_consumer_->UpdateStatus(base::TimeTicks::Now(), kPauseTimestamp);
- task_environment_.RunUntilIdle();
- EXPECT_EQ(time_source_->CurrentMediaTime(), kPauseTimestamp);
- // The renderer should start sending packets again.
- task_environment_.FastForwardBy(kPacketDuration);
- EXPECT_EQ(stream_sink_->received_packets()->size(),
- kExpectedQueuedPackets + 1);
- EXPECT_EQ(time_source_->CurrentMediaTime(),
- kPauseTimestamp + kPacketDuration);
- }
- // Verify that end-of-stream is handled correctly when the renderer is buffered.
- TEST_P(WebEngineAudioRendererTest, EndOfStreamBuffered) {
- ASSERT_NO_FATAL_FAILURE(CreateAndInitializeRenderer());
- ASSERT_NO_FATAL_FAILURE(StartPlayback());
- const auto kStreamLength = base::Seconds(1);
- FillDemuxerStream(kStreamLength);
- demuxer_stream_->QueueReadResult(
- TestDemuxerStream::ReadResult(media::DecoderBuffer::CreateEOSBuffer()));
- // Queue second EOS buffer. The renderer should not read it.
- demuxer_stream_->QueueReadResult(
- TestDemuxerStream::ReadResult(media::DecoderBuffer::CreateEOSBuffer()));
- task_environment_.FastForwardBy(kStreamLength);
- EXPECT_EQ(stream_sink_->received_packets()->size(),
- kStreamLength / kPacketDuration);
- EXPECT_TRUE(stream_sink_->received_end_of_stream());
- client_.ExpectEos();
- audio_consumer_->SignalEndOfStream();
- task_environment_.RunUntilIdle();
- }
- // Verifies that buffering state is updated after reaching EOS. See
- // https://crbug.com/1162503 .
- TEST_P(WebEngineAudioRendererTest, EndOfStreamWhenBuffering) {
- ASSERT_NO_FATAL_FAILURE(CreateAndInitializeRenderer());
- stream_sink_ = audio_consumer_->WaitStreamSinkConnected();
- // Produce stream shorter than kMinLeadTime.
- const auto kStreamLength = kMinLeadTime / 2;
- FillDemuxerStream(kStreamLength);
- demuxer_stream_->QueueReadResult(
- TestDemuxerStream::ReadResult(media::DecoderBuffer::CreateEOSBuffer()));
- task_environment_.RunUntilIdle();
- // Start playback. The renderer should push queued packets to the
- // AudioConsumer and updated buffering state when it reaches EOS.
- audio_renderer_->StartPlaying();
- task_environment_.RunUntilIdle();
- EXPECT_EQ(client_.buffering_state(), media::BUFFERING_HAVE_ENOUGH);
- EXPECT_TRUE(stream_sink_->received_end_of_stream());
- }
- TEST_P(WebEngineAudioRendererTest, EndOfStreamStart) {
- ASSERT_NO_FATAL_FAILURE(CreateAndInitializeRenderer());
- stream_sink_ = audio_consumer_->WaitStreamSinkConnected();
- // Queue EOS without any preceding packets.
- demuxer_stream_->QueueReadResult(
- TestDemuxerStream::ReadResult(media::DecoderBuffer::CreateEOSBuffer()));
- task_environment_.RunUntilIdle();
- // Start playback. The renderer should push queued packets to the
- // AudioConsumer and updated buffering state when it reaches EOS.
- audio_renderer_->StartPlaying();
- task_environment_.RunUntilIdle();
- EXPECT_EQ(client_.buffering_state(), media::BUFFERING_HAVE_ENOUGH);
- EXPECT_TRUE(stream_sink_->received_end_of_stream());
- }
- TEST_P(WebEngineAudioRendererTest, SetVolume) {
- ASSERT_NO_FATAL_FAILURE(CreateAndInitializeRenderer());
- audio_renderer_->SetVolume(0.5);
- task_environment_.RunUntilIdle();
- EXPECT_EQ(audio_consumer_->volume(), 0.5);
- }
- TEST_P(WebEngineAudioRendererTest, SetVolumeBeforeInitialize) {
- ASSERT_NO_FATAL_FAILURE(CreateUninitializedRenderer());
- // SetVolume() may be called before media::AudioRenderer is initialized. It
- // should still be handled.
- audio_renderer_->SetVolume(0.5);
- ASSERT_NO_FATAL_FAILURE(InitializeRenderer());
- EXPECT_EQ(audio_consumer_->volume(), 0.5);
- }
- // Verify that the case when StartTicking() is called shortly after
- // StartPlaying() is handled correctly. AudioConsumer::Start() should be sent
- // only after CreateStreamSink(). See crbug.com/1219147 .
- TEST_P(WebEngineAudioRendererTest, PlaybackBeforeSinkCreation) {
- demuxer_stream_ = std::make_unique<TestDemuxerStream>(GetStreamConfig());
- const auto kStreamLength = base::Milliseconds(100);
- FillDemuxerStream(kStreamLength);
- demuxer_stream_->QueueReadResult(
- TestDemuxerStream::ReadResult(media::DecoderBuffer::CreateEOSBuffer()));
- ASSERT_NO_FATAL_FAILURE(CreateAndInitializeRenderer());
- // Call StartTicking() shortly after StartPlayback(). At this point sysmem
- // buffer allocation hasn't been complete, so AudioConsumer::Start() should be
- // delayed until the buffer are allocated.
- audio_renderer_->StartPlaying();
- time_source_->StartTicking();
- // Wait until the stream is started. Start() should be called only after
- // StreamSink() is connected and the packets are buffered.
- audio_consumer_->WaitStarted();
- stream_sink_ = audio_consumer_->TakeStreamSink();
- EXPECT_GT(stream_sink_->received_packets()->size(), 0U);
- EXPECT_FALSE(stream_sink_->received_end_of_stream());
- }
|