1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 // demuxer_bench is a standalone benchmarking tool for FFmpegDemuxer. It
6 // simulates the reading requirements for playback by reading from the stream
7 // that has the earliest timestamp.
11 #include "base/at_exit.h"
12 #include "base/bind.h"
13 #include "base/command_line.h"
14 #include "base/logging.h"
15 #include "base/message_loop/message_loop.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "media/base/media.h"
18 #include "media/base/media_log.h"
19 #include "media/filters/ffmpeg_demuxer.h"
20 #include "media/filters/file_data_source.h"
23 const char kEnableBitstreamConverter
[] = "enable-bitstream-converter";
24 } // namespace switches
26 class DemuxerHostImpl
: public media::DemuxerHost
{
29 virtual ~DemuxerHostImpl() {}
31 // DataSourceHost implementation.
32 virtual void SetTotalBytes(int64 total_bytes
) OVERRIDE
{}
33 virtual void AddBufferedByteRange(int64 start
, int64 end
) OVERRIDE
{}
34 virtual void AddBufferedTimeRange(base::TimeDelta start
,
35 base::TimeDelta end
) OVERRIDE
{}
37 // DemuxerHost implementation.
38 virtual void SetDuration(base::TimeDelta duration
) OVERRIDE
{}
39 virtual void OnDemuxerError(media::PipelineStatus error
) OVERRIDE
{}
42 DISALLOW_COPY_AND_ASSIGN(DemuxerHostImpl
);
45 void QuitLoopWithStatus(base::MessageLoop
* message_loop
,
46 media::PipelineStatus status
) {
47 CHECK_EQ(status
, media::PIPELINE_OK
);
48 message_loop
->PostTask(FROM_HERE
, base::MessageLoop::QuitWhenIdleClosure());
51 static void NeedKey(const std::string
& type
,
52 const std::vector
<uint8
>& init_data
) {
53 LOG(INFO
) << "File is encrypted.";
56 typedef std::vector
<media::DemuxerStream
* > Streams
;
58 // Simulates playback reading requirements by reading from each stream
59 // present in |demuxer| in as-close-to-monotonically-increasing timestamp order.
62 StreamReader(media::Demuxer
* demuxer
, bool enable_bitstream_converter
);
65 // Performs a single step read.
68 // Returns true when all streams have reached end of stream.
71 int number_of_streams() { return streams_
.size(); }
72 const Streams
& streams() { return streams_
; }
73 const std::vector
<int>& counts() { return counts_
; }
76 void OnReadDone(base::MessageLoop
* message_loop
,
78 base::TimeDelta
* timestamp
,
79 media::DemuxerStream::Status status
,
80 const scoped_refptr
<media::DecoderBuffer
>& buffer
);
81 int GetNextStreamIndexToRead();
84 std::vector
<bool> end_of_stream_
;
85 std::vector
<base::TimeDelta
> last_read_timestamp_
;
86 std::vector
<int> counts_
;
88 DISALLOW_COPY_AND_ASSIGN(StreamReader
);
91 StreamReader::StreamReader(media::Demuxer
* demuxer
,
92 bool enable_bitstream_converter
) {
93 media::DemuxerStream
* stream
=
94 demuxer
->GetStream(media::DemuxerStream::AUDIO
);
96 streams_
.push_back(stream
);
97 end_of_stream_
.push_back(false);
98 last_read_timestamp_
.push_back(media::kNoTimestamp());
102 stream
= demuxer
->GetStream(media::DemuxerStream::VIDEO
);
104 streams_
.push_back(stream
);
105 end_of_stream_
.push_back(false);
106 last_read_timestamp_
.push_back(media::kNoTimestamp());
107 counts_
.push_back(0);
109 if (enable_bitstream_converter
)
110 stream
->EnableBitstreamConverter();
114 StreamReader::~StreamReader() {}
116 void StreamReader::Read() {
117 int index
= GetNextStreamIndexToRead();
118 bool end_of_stream
= false;
119 base::TimeDelta timestamp
;
121 streams_
[index
]->Read(base::Bind(
122 &StreamReader::OnReadDone
, base::Unretained(this),
123 base::MessageLoop::current(), &end_of_stream
, ×tamp
));
124 base::MessageLoop::current()->Run();
126 CHECK(end_of_stream
|| timestamp
!= media::kNoTimestamp());
127 end_of_stream_
[index
] = end_of_stream
;
128 last_read_timestamp_
[index
] = timestamp
;
132 bool StreamReader::IsDone() {
133 for (size_t i
= 0; i
< end_of_stream_
.size(); ++i
) {
134 if (!end_of_stream_
[i
])
140 void StreamReader::OnReadDone(
141 base::MessageLoop
* message_loop
,
143 base::TimeDelta
* timestamp
,
144 media::DemuxerStream::Status status
,
145 const scoped_refptr
<media::DecoderBuffer
>& buffer
) {
146 CHECK_EQ(status
, media::DemuxerStream::kOk
);
148 *end_of_stream
= buffer
->end_of_stream();
149 *timestamp
= *end_of_stream
? media::kNoTimestamp() : buffer
->timestamp();
150 message_loop
->PostTask(FROM_HERE
, base::MessageLoop::QuitWhenIdleClosure());
153 int StreamReader::GetNextStreamIndexToRead() {
155 for (int i
= 0; i
< number_of_streams(); ++i
) {
156 // Ignore streams at EOS.
157 if (end_of_stream_
[i
])
160 // Use a stream if it hasn't been read from yet.
161 if (last_read_timestamp_
[i
] == media::kNoTimestamp())
165 last_read_timestamp_
[i
] < last_read_timestamp_
[index
]) {
169 CHECK_GE(index
, 0) << "Couldn't find a stream to read";
173 int main(int argc
, char** argv
) {
174 base::AtExitManager at_exit
;
175 media::InitializeMediaLibraryForTesting();
177 CommandLine::Init(argc
, argv
);
178 CommandLine
* cmd_line
= CommandLine::ForCurrentProcess();
180 if (cmd_line
->GetArgs().empty()) {
181 std::cerr
<< "Usage: " << argv
[0] << " [file]\n\n"
183 << " --" << switches::kEnableBitstreamConverter
184 << " Enables H.264 Annex B bitstream conversion"
189 base::MessageLoop message_loop
;
190 DemuxerHostImpl demuxer_host
;
191 base::FilePath
file_path(cmd_line
->GetArgs()[0]);
194 media::FileDataSource data_source
;
195 CHECK(data_source
.Initialize(file_path
));
197 media::Demuxer::NeedKeyCB need_key_cb
= base::Bind(&NeedKey
);
198 media::FFmpegDemuxer
demuxer(message_loop
.message_loop_proxy(),
201 new media::MediaLog());
203 demuxer
.Initialize(&demuxer_host
, base::Bind(
204 &QuitLoopWithStatus
, &message_loop
));
207 StreamReader
stream_reader(
208 &demuxer
, cmd_line
->HasSwitch(switches::kEnableBitstreamConverter
));
211 base::TimeTicks start
= base::TimeTicks::HighResNow();
212 while (!stream_reader
.IsDone()) {
213 stream_reader
.Read();
215 base::TimeTicks end
= base::TimeTicks::HighResNow();
218 std::cout
<< "Time: " << (end
- start
).InMillisecondsF() << " ms\n";
219 for (int i
= 0; i
< stream_reader
.number_of_streams(); ++i
) {
220 media::DemuxerStream
* stream
= stream_reader
.streams()[i
];
221 std::cout
<< "Stream #" << i
<< ": ";
223 if (stream
->type() == media::DemuxerStream::AUDIO
) {
224 std::cout
<< "audio";
225 } else if (stream
->type() == media::DemuxerStream::VIDEO
) {
226 std::cout
<< "video";
228 std::cout
<< "unknown";
231 std::cout
<< ", " << stream_reader
.counts()[i
] << " packets" << std::endl
;
235 demuxer
.Stop(base::Bind(
236 &QuitLoopWithStatus
, &message_loop
, media::PIPELINE_OK
));