Home | History | Annotate | Download | only in demuxer_bench
      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.
      4 //
      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.
      8 
      9 #include <iostream>
     10 
     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"
     21 
     22 namespace switches {
     23 const char kEnableBitstreamConverter[] = "enable-bitstream-converter";
     24 }  // namespace switches
     25 
     26 class DemuxerHostImpl : public media::DemuxerHost {
     27  public:
     28   DemuxerHostImpl() {}
     29   virtual ~DemuxerHostImpl() {}
     30 
     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 {}
     36 
     37   // DemuxerHost implementation.
     38   virtual void SetDuration(base::TimeDelta duration) OVERRIDE {}
     39   virtual void OnDemuxerError(media::PipelineStatus error) OVERRIDE {}
     40 
     41  private:
     42   DISALLOW_COPY_AND_ASSIGN(DemuxerHostImpl);
     43 };
     44 
     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());
     49 }
     50 
     51 static void NeedKey(const std::string& type, scoped_ptr<uint8[]> init_data,
     52              int init_data_size) {
     53   LOG(INFO) << "File is encrypted.";
     54 }
     55 
     56 typedef std::vector<media::DemuxerStream* > Streams;
     57 
     58 // Simulates playback reading requirements by reading from each stream
     59 // present in |demuxer| in as-close-to-monotonically-increasing timestamp order.
     60 class StreamReader {
     61  public:
     62   StreamReader(media::Demuxer* demuxer, bool enable_bitstream_converter);
     63   ~StreamReader();
     64 
     65   // Performs a single step read.
     66   void Read();
     67 
     68   // Returns true when all streams have reached end of stream.
     69   bool IsDone();
     70 
     71   int number_of_streams() { return streams_.size(); }
     72   const Streams& streams() { return streams_; }
     73   const std::vector<int>& counts() { return counts_; }
     74 
     75  private:
     76   void OnReadDone(base::MessageLoop* message_loop,
     77                   bool* end_of_stream,
     78                   base::TimeDelta* timestamp,
     79                   media::DemuxerStream::Status status,
     80                   const scoped_refptr<media::DecoderBuffer>& buffer);
     81   int GetNextStreamIndexToRead();
     82 
     83   Streams streams_;
     84   std::vector<bool> end_of_stream_;
     85   std::vector<base::TimeDelta> last_read_timestamp_;
     86   std::vector<int> counts_;
     87 
     88   DISALLOW_COPY_AND_ASSIGN(StreamReader);
     89 };
     90 
     91 StreamReader::StreamReader(media::Demuxer* demuxer,
     92                            bool enable_bitstream_converter) {
     93   media::DemuxerStream* stream =
     94       demuxer->GetStream(media::DemuxerStream::AUDIO);
     95   if (stream) {
     96     streams_.push_back(stream);
     97     end_of_stream_.push_back(false);
     98     last_read_timestamp_.push_back(media::kNoTimestamp());
     99     counts_.push_back(0);
    100   }
    101 
    102   stream = demuxer->GetStream(media::DemuxerStream::VIDEO);
    103   if (stream) {
    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);
    108 
    109     if (enable_bitstream_converter)
    110       stream->EnableBitstreamConverter();
    111   }
    112 }
    113 
    114 StreamReader::~StreamReader() {}
    115 
    116 void StreamReader::Read() {
    117   int index = GetNextStreamIndexToRead();
    118   bool end_of_stream = false;
    119   base::TimeDelta timestamp;
    120 
    121   streams_[index]->Read(base::Bind(
    122       &StreamReader::OnReadDone, base::Unretained(this),
    123       base::MessageLoop::current(), &end_of_stream, &timestamp));
    124   base::MessageLoop::current()->Run();
    125 
    126   CHECK(end_of_stream || timestamp != media::kNoTimestamp());
    127   end_of_stream_[index] = end_of_stream;
    128   last_read_timestamp_[index] = timestamp;
    129   counts_[index]++;
    130 }
    131 
    132 bool StreamReader::IsDone() {
    133   for (size_t i = 0; i < end_of_stream_.size(); ++i) {
    134     if (!end_of_stream_[i])
    135       return false;
    136   }
    137   return true;
    138 }
    139 
    140 void StreamReader::OnReadDone(
    141     base::MessageLoop* message_loop,
    142     bool* end_of_stream,
    143     base::TimeDelta* timestamp,
    144     media::DemuxerStream::Status status,
    145     const scoped_refptr<media::DecoderBuffer>& buffer) {
    146   CHECK_EQ(status, media::DemuxerStream::kOk);
    147   CHECK(buffer.get());
    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());
    151 }
    152 
    153 int StreamReader::GetNextStreamIndexToRead() {
    154   int index = -1;
    155   for (int i = 0; i < number_of_streams(); ++i) {
    156     // Ignore streams at EOS.
    157     if (end_of_stream_[i])
    158       continue;
    159 
    160     // Use a stream if it hasn't been read from yet.
    161     if (last_read_timestamp_[i] == media::kNoTimestamp())
    162       return i;
    163 
    164     if (index < 0 ||
    165         last_read_timestamp_[i] < last_read_timestamp_[index]) {
    166       index = i;
    167     }
    168   }
    169   CHECK_GE(index, 0) << "Couldn't find a stream to read";
    170   return index;
    171 }
    172 
    173 int main(int argc, char** argv) {
    174   base::AtExitManager at_exit;
    175   media::InitializeMediaLibraryForTesting();
    176 
    177   CommandLine::Init(argc, argv);
    178   CommandLine* cmd_line = CommandLine::ForCurrentProcess();
    179 
    180   if (cmd_line->GetArgs().empty()) {
    181     std::cerr << "Usage: " << argv[0] << " [file]\n\n"
    182               << "Options:\n"
    183               << "  --" << switches::kEnableBitstreamConverter
    184               << " Enables H.264 Annex B bitstream conversion"
    185               << std::endl;
    186     return 1;
    187   }
    188 
    189   base::MessageLoop message_loop;
    190   DemuxerHostImpl demuxer_host;
    191   base::FilePath file_path(cmd_line->GetArgs()[0]);
    192 
    193   // Setup.
    194   media::FileDataSource data_source;
    195   CHECK(data_source.Initialize(file_path));
    196 
    197   media::FFmpegNeedKeyCB need_key_cb = base::Bind(&NeedKey);
    198   media::FFmpegDemuxer demuxer(message_loop.message_loop_proxy(),
    199                                &data_source,
    200                                need_key_cb,
    201                                new media::MediaLog());
    202 
    203   demuxer.Initialize(&demuxer_host, base::Bind(
    204       &QuitLoopWithStatus, &message_loop));
    205   message_loop.Run();
    206 
    207   StreamReader stream_reader(
    208       &demuxer, cmd_line->HasSwitch(switches::kEnableBitstreamConverter));
    209 
    210   // Benchmark.
    211   base::TimeTicks start = base::TimeTicks::HighResNow();
    212   while (!stream_reader.IsDone()) {
    213     stream_reader.Read();
    214   }
    215   base::TimeTicks end = base::TimeTicks::HighResNow();
    216 
    217   // Results.
    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 << ": ";
    222 
    223     if (stream->type() == media::DemuxerStream::AUDIO) {
    224       std::cout << "audio";
    225     } else if (stream->type() == media::DemuxerStream::VIDEO) {
    226       std::cout << "video";
    227     } else {
    228       std::cout << "unknown";
    229     }
    230 
    231     std::cout << ", " << stream_reader.counts()[i] << " packets" << std::endl;
    232   }
    233 
    234   // Teardown.
    235   demuxer.Stop(base::Bind(
    236       &QuitLoopWithStatus, &message_loop, media::PIPELINE_OK));
    237   message_loop.Run();
    238 
    239   return 0;
    240 }
    241