1 // Copyright 2013 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
9 #include "base/at_exit.h"
10 #include "base/functional/bind.h"
11 #include "base/run_loop.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/task/single_thread_task_runner.h"
14 #include "base/test/task_environment.h"
15 #include "base/time/time.h"
16 #include "build/build_config.h"
17 #include "media/base/media.h"
18 #include "media/base/media_tracks.h"
19 #include "media/base/media_util.h"
20 #include "media/base/test_data_util.h"
21 #include "media/base/timestamp_constants.h"
22 #include "media/filters/ffmpeg_demuxer.h"
23 #include "media/filters/file_data_source.h"
24 #include "media/media_buildflags.h"
25 #include "testing/gtest/include/gtest/gtest.h"
26 #include "testing/perf/perf_result_reporter.h"
30 static const int kBenchmarkIterations = 100;
32 class DemuxerHostImpl : public media::DemuxerHost {
34 DemuxerHostImpl() = default;
36 DemuxerHostImpl(const DemuxerHostImpl&) = delete;
37 DemuxerHostImpl& operator=(const DemuxerHostImpl&) = delete;
39 ~DemuxerHostImpl() override = default;
41 // DemuxerHost implementation.
42 void OnBufferedTimeRangesChanged(
43 const Ranges<base::TimeDelta>& ranges) override {}
44 void SetDuration(base::TimeDelta duration) override {}
45 void OnDemuxerError(media::PipelineStatus error) override {}
48 static void QuitLoopWithStatus(base::OnceClosure quit_cb,
49 media::PipelineStatus status) {
50 CHECK_EQ(status, media::PIPELINE_OK);
51 std::move(quit_cb).Run();
54 static void OnEncryptedMediaInitData(EmeInitDataType init_data_type,
55 const std::vector<uint8_t>& init_data) {
56 DVLOG(1) << "File is encrypted.";
59 static void OnMediaTracksUpdated(std::unique_ptr<MediaTracks> tracks) {
60 DVLOG(1) << "Got media tracks info, tracks = " << tracks->tracks().size();
63 typedef std::vector<media::DemuxerStream*> Streams;
65 // Simulates playback reading requirements by reading from each stream
66 // present in |demuxer| in as-close-to-monotonically-increasing timestamp order.
69 StreamReader(media::Demuxer* demuxer, bool enable_bitstream_converter);
71 StreamReader(const StreamReader&) = delete;
72 StreamReader& operator=(const StreamReader&) = delete;
76 // Performs a single step read.
79 // Returns true when all streams have reached end of stream.
82 int number_of_streams() { return static_cast<int>(streams_.size()); }
83 const Streams& streams() { return streams_; }
84 const std::vector<int>& counts() { return counts_; }
87 void OnReadDone(scoped_refptr<base::SingleThreadTaskRunner> task_runner,
88 base::OnceClosure quit_when_idle_closure,
90 base::TimeDelta* timestamp,
91 media::DemuxerStream::Status status,
92 DemuxerStream::DecoderBufferVector buffers);
93 int GetNextStreamIndexToRead();
96 std::vector<bool> end_of_stream_;
97 std::vector<base::TimeDelta> last_read_timestamp_;
98 std::vector<int> counts_;
101 StreamReader::StreamReader(media::Demuxer* demuxer,
102 bool enable_bitstream_converter) {
103 std::vector<media::DemuxerStream*> streams = demuxer->GetAllStreams();
104 for (auto* stream : streams) {
105 streams_.push_back(stream);
106 end_of_stream_.push_back(false);
107 last_read_timestamp_.push_back(media::kNoTimestamp);
108 counts_.push_back(0);
109 if (enable_bitstream_converter && stream->type() == DemuxerStream::VIDEO)
110 stream->EnableBitstreamConverter();
114 StreamReader::~StreamReader() = default;
116 void StreamReader::Read() {
117 int index = GetNextStreamIndexToRead();
118 bool end_of_stream = false;
119 base::TimeDelta timestamp;
121 base::RunLoop run_loop;
122 streams_[index]->Read(
123 1, base::BindOnce(&StreamReader::OnReadDone, base::Unretained(this),
124 base::SingleThreadTaskRunner::GetCurrentDefault(),
125 run_loop.QuitWhenIdleClosure(), &end_of_stream,
129 CHECK(end_of_stream || timestamp != media::kNoTimestamp);
130 end_of_stream_[index] = end_of_stream;
131 last_read_timestamp_[index] = timestamp;
135 bool StreamReader::IsDone() {
136 for (size_t i = 0; i < end_of_stream_.size(); ++i) {
137 if (!end_of_stream_[i])
143 void StreamReader::OnReadDone(
144 scoped_refptr<base::SingleThreadTaskRunner> task_runner,
145 base::OnceClosure quit_when_idle_closure,
147 base::TimeDelta* timestamp,
148 media::DemuxerStream::Status status,
149 DemuxerStream::DecoderBufferVector buffers) {
150 CHECK_EQ(status, media::DemuxerStream::kOk);
151 CHECK_EQ(buffers.size(), 1u) << "StreamReader only reads a single-buffer.";
152 scoped_refptr<DecoderBuffer> buffer = std::move(buffers[0]);
153 *end_of_stream = buffer->end_of_stream();
154 *timestamp = *end_of_stream ? media::kNoTimestamp : buffer->timestamp();
155 task_runner->PostTask(FROM_HERE, std::move(quit_when_idle_closure));
158 int StreamReader::GetNextStreamIndexToRead() {
160 for (int i = 0; i < number_of_streams(); ++i) {
161 // Ignore streams at EOS.
162 if (end_of_stream_[i])
165 // Use a stream if it hasn't been read from yet.
166 if (last_read_timestamp_[i] == media::kNoTimestamp)
169 if (index < 0 || last_read_timestamp_[i] < last_read_timestamp_[index]) {
173 CHECK_GE(index, 0) << "Couldn't find a stream to read";
177 static void RunDemuxerBenchmark(const std::string& filename) {
178 base::FilePath file_path(GetTestDataFilePath(filename));
179 base::TimeDelta total_time;
180 NullMediaLog media_log_;
181 for (int i = 0; i < kBenchmarkIterations; ++i) {
183 base::test::TaskEnvironment task_environment_;
184 DemuxerHostImpl demuxer_host;
185 FileDataSource data_source;
186 ASSERT_TRUE(data_source.Initialize(file_path));
188 Demuxer::EncryptedMediaInitDataCB encrypted_media_init_data_cb =
189 base::BindRepeating(&OnEncryptedMediaInitData);
190 Demuxer::MediaTracksUpdatedCB tracks_updated_cb =
191 base::BindRepeating(&OnMediaTracksUpdated);
192 FFmpegDemuxer demuxer(base::SingleThreadTaskRunner::GetCurrentDefault(),
193 &data_source, encrypted_media_init_data_cb,
194 tracks_updated_cb, &media_log_, true);
197 base::RunLoop run_loop;
198 demuxer.Initialize(&demuxer_host, base::BindOnce(&QuitLoopWithStatus,
199 run_loop.QuitClosure()));
203 StreamReader stream_reader(&demuxer, false);
206 base::TimeTicks start = base::TimeTicks::Now();
207 while (!stream_reader.IsDone())
208 stream_reader.Read();
209 total_time += base::TimeTicks::Now() - start;
211 base::RunLoop().RunUntilIdle();
214 perf_test::PerfResultReporter reporter("demuxer_bench", filename);
215 reporter.RegisterImportantMetric("", "runs/s");
216 reporter.AddResult("", kBenchmarkIterations / total_time.InSecondsF());
219 class DemuxerPerfTest : public testing::TestWithParam<const char*> {};
221 TEST_P(DemuxerPerfTest, Demuxer) {
222 RunDemuxerBenchmark(GetParam());
225 static const char* kDemuxerTestFiles[] {
226 "bear.ogv", "bear-640x360.webm", "sfx.mp3",
227 #if BUILDFLAG(USE_PROPRIETARY_CODECS)
232 // For simplicity we only test containers with above 2% daily usage as measured
233 // by the Media.DetectedContainer histogram.
234 INSTANTIATE_TEST_SUITE_P(
237 testing::ValuesIn(kDemuxerTestFiles));