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