Home | History | Annotate | Download | only in base
      1 // Copyright 2013 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 #include "base/at_exit.h"
      6 #include "base/bind.h"
      7 #include "base/message_loop/message_loop.h"
      8 #include "base/strings/string_number_conversions.h"
      9 #include "base/time/time.h"
     10 #include "media/base/media.h"
     11 #include "media/base/media_log.h"
     12 #include "media/base/test_data_util.h"
     13 #include "media/filters/ffmpeg_demuxer.h"
     14 #include "media/filters/file_data_source.h"
     15 #include "testing/gtest/include/gtest/gtest.h"
     16 #include "testing/perf/perf_test.h"
     17 
     18 namespace media {
     19 
     20 static const int kBenchmarkIterations = 500;
     21 
     22 class DemuxerHostImpl : public media::DemuxerHost {
     23  public:
     24   DemuxerHostImpl() {}
     25   virtual ~DemuxerHostImpl() {}
     26 
     27   // DataSourceHost implementation.
     28   virtual void SetTotalBytes(int64 total_bytes) OVERRIDE {}
     29   virtual void AddBufferedByteRange(int64 start, int64 end) OVERRIDE {}
     30   virtual void AddBufferedTimeRange(base::TimeDelta start,
     31                                     base::TimeDelta end) OVERRIDE {}
     32 
     33   // DemuxerHost implementation.
     34   virtual void SetDuration(base::TimeDelta duration) OVERRIDE {}
     35   virtual void OnDemuxerError(media::PipelineStatus error) OVERRIDE {}
     36   virtual void AddTextStream(media::DemuxerStream* text_stream,
     37                              const media::TextTrackConfig& config) OVERRIDE {}
     38   virtual void RemoveTextStream(media::DemuxerStream* text_stream) OVERRIDE {}
     39 
     40  private:
     41   DISALLOW_COPY_AND_ASSIGN(DemuxerHostImpl);
     42 };
     43 
     44 static void QuitLoopWithStatus(base::MessageLoop* message_loop,
     45                         media::PipelineStatus status) {
     46   CHECK_EQ(status, media::PIPELINE_OK);
     47   message_loop->PostTask(FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
     48 }
     49 
     50 static void NeedKey(const std::string& type,
     51                     const std::vector<uint8>& init_data) {
     52   VLOG(0) << "File is encrypted.";
     53 }
     54 
     55 typedef std::vector<media::DemuxerStream* > Streams;
     56 
     57 // Simulates playback reading requirements by reading from each stream
     58 // present in |demuxer| in as-close-to-monotonically-increasing timestamp order.
     59 class StreamReader {
     60  public:
     61   StreamReader(media::Demuxer* demuxer, bool enable_bitstream_converter);
     62   ~StreamReader();
     63 
     64   // Performs a single step read.
     65   void Read();
     66 
     67   // Returns true when all streams have reached end of stream.
     68   bool IsDone();
     69 
     70   int number_of_streams() { return static_cast<int>(streams_.size()); }
     71   const Streams& streams() { return streams_; }
     72   const std::vector<int>& counts() { return counts_; }
     73 
     74  private:
     75   void OnReadDone(base::MessageLoop* message_loop,
     76                   bool* end_of_stream,
     77                   base::TimeDelta* timestamp,
     78                   media::DemuxerStream::Status status,
     79                   const scoped_refptr<media::DecoderBuffer>& buffer);
     80   int GetNextStreamIndexToRead();
     81 
     82   Streams streams_;
     83   std::vector<bool> end_of_stream_;
     84   std::vector<base::TimeDelta> last_read_timestamp_;
     85   std::vector<int> counts_;
     86 
     87   DISALLOW_COPY_AND_ASSIGN(StreamReader);
     88 };
     89 
     90 StreamReader::StreamReader(media::Demuxer* demuxer,
     91                            bool enable_bitstream_converter) {
     92   media::DemuxerStream* stream =
     93       demuxer->GetStream(media::DemuxerStream::AUDIO);
     94   if (stream) {
     95     streams_.push_back(stream);
     96     end_of_stream_.push_back(false);
     97     last_read_timestamp_.push_back(media::kNoTimestamp());
     98     counts_.push_back(0);
     99   }
    100 
    101   stream = demuxer->GetStream(media::DemuxerStream::VIDEO);
    102   if (stream) {
    103     streams_.push_back(stream);
    104     end_of_stream_.push_back(false);
    105     last_read_timestamp_.push_back(media::kNoTimestamp());
    106     counts_.push_back(0);
    107 
    108     if (enable_bitstream_converter)
    109       stream->EnableBitstreamConverter();
    110   }
    111 }
    112 
    113 StreamReader::~StreamReader() {}
    114 
    115 void StreamReader::Read() {
    116   int index = GetNextStreamIndexToRead();
    117   bool end_of_stream = false;
    118   base::TimeDelta timestamp;
    119 
    120   streams_[index]->Read(base::Bind(
    121       &StreamReader::OnReadDone, base::Unretained(this),
    122       base::MessageLoop::current(), &end_of_stream, &timestamp));
    123   base::MessageLoop::current()->Run();
    124 
    125   CHECK(end_of_stream || timestamp != media::kNoTimestamp());
    126   end_of_stream_[index] = end_of_stream;
    127   last_read_timestamp_[index] = timestamp;
    128   counts_[index]++;
    129 }
    130 
    131 bool StreamReader::IsDone() {
    132   for (size_t i = 0; i < end_of_stream_.size(); ++i) {
    133     if (!end_of_stream_[i])
    134       return false;
    135   }
    136   return true;
    137 }
    138 
    139 void StreamReader::OnReadDone(
    140     base::MessageLoop* message_loop,
    141     bool* end_of_stream,
    142     base::TimeDelta* timestamp,
    143     media::DemuxerStream::Status status,
    144     const scoped_refptr<media::DecoderBuffer>& buffer) {
    145   CHECK_EQ(status, media::DemuxerStream::kOk);
    146   CHECK(buffer.get());
    147   *end_of_stream = buffer->end_of_stream();
    148   *timestamp = *end_of_stream ? media::kNoTimestamp() : buffer->timestamp();
    149   message_loop->PostTask(FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
    150 }
    151 
    152 int StreamReader::GetNextStreamIndexToRead() {
    153   int index = -1;
    154   for (int i = 0; i < number_of_streams(); ++i) {
    155     // Ignore streams at EOS.
    156     if (end_of_stream_[i])
    157       continue;
    158 
    159     // Use a stream if it hasn't been read from yet.
    160     if (last_read_timestamp_[i] == media::kNoTimestamp())
    161       return i;
    162 
    163     if (index < 0 ||
    164         last_read_timestamp_[i] < last_read_timestamp_[index]) {
    165       index = i;
    166     }
    167   }
    168   CHECK_GE(index, 0) << "Couldn't find a stream to read";
    169   return index;
    170 }
    171 
    172 static void RunDemuxerBenchmark(const std::string& filename) {
    173   base::FilePath file_path(GetTestDataFilePath(filename));
    174   double total_time = 0.0;
    175   for (int i = 0; i < kBenchmarkIterations; ++i) {
    176     // Setup.
    177     base::MessageLoop message_loop;
    178     DemuxerHostImpl demuxer_host;
    179     FileDataSource data_source;
    180     ASSERT_TRUE(data_source.Initialize(file_path));
    181 
    182     Demuxer::NeedKeyCB need_key_cb = base::Bind(&NeedKey);
    183     FFmpegDemuxer demuxer(message_loop.message_loop_proxy(),
    184                           &data_source,
    185                           need_key_cb,
    186                           new MediaLog());
    187 
    188     demuxer.Initialize(&demuxer_host,
    189                        base::Bind(&QuitLoopWithStatus, &message_loop),
    190                        false);
    191     message_loop.Run();
    192     StreamReader stream_reader(&demuxer, false);
    193 
    194     // Benchmark.
    195     base::TimeTicks start = base::TimeTicks::HighResNow();
    196     while (!stream_reader.IsDone()) {
    197       stream_reader.Read();
    198     }
    199     base::TimeTicks end = base::TimeTicks::HighResNow();
    200     total_time += (end - start).InSecondsF();
    201     demuxer.Stop(base::Bind(
    202         &QuitLoopWithStatus, &message_loop, PIPELINE_OK));
    203     message_loop.Run();
    204   }
    205 
    206   perf_test::PrintResult("demuxer_bench",
    207                          "",
    208                          filename,
    209                          kBenchmarkIterations / total_time,
    210                          "runs/s",
    211                          true);
    212 }
    213 
    214 TEST(DemuxerPerfTest, Demuxer) {
    215   RunDemuxerBenchmark("bear.ogv");
    216   RunDemuxerBenchmark("bear-640x360.webm");
    217   RunDemuxerBenchmark("sfx_s16le.wav");
    218 #if defined(USE_PROPRIETARY_CODECS)
    219   RunDemuxerBenchmark("bear-1280x720.mp4");
    220   RunDemuxerBenchmark("sfx.mp3");
    221 #endif
    222 #if defined(OS_CHROMEOS)
    223   RunDemuxerBenchmark("bear.flac");
    224 #endif
    225 #if defined(USE_PROPRIETARY_CODECS) && defined(OS_CHROMEOS)
    226   RunDemuxerBenchmark("bear.avi");
    227 #endif
    228 }
    229 
    230 }  // namespace media
    231