Home | History | Annotate | Download | only in mp4
      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 #include <algorithm>
      6 #include <string>
      7 
      8 #include "base/bind.h"
      9 #include "base/bind_helpers.h"
     10 #include "base/logging.h"
     11 #include "base/memory/ref_counted.h"
     12 #include "base/time/time.h"
     13 #include "media/base/audio_decoder_config.h"
     14 #include "media/base/decoder_buffer.h"
     15 #include "media/base/stream_parser_buffer.h"
     16 #include "media/base/test_data_util.h"
     17 #include "media/base/text_track_config.h"
     18 #include "media/base/video_decoder_config.h"
     19 #include "media/mp4/es_descriptor.h"
     20 #include "media/mp4/mp4_stream_parser.h"
     21 #include "testing/gtest/include/gtest/gtest.h"
     22 
     23 using base::TimeDelta;
     24 
     25 namespace media {
     26 namespace mp4 {
     27 
     28 // TODO(xhwang): Figure out the init data type appropriately once it's spec'ed.
     29 static const char kMp4InitDataType[] = "video/mp4";
     30 
     31 class MP4StreamParserTest : public testing::Test {
     32  public:
     33   MP4StreamParserTest()
     34       : configs_received_(false) {
     35     std::set<int> audio_object_types;
     36     audio_object_types.insert(kISO_14496_3);
     37     parser_.reset(new MP4StreamParser(audio_object_types, false));
     38   }
     39 
     40  protected:
     41   scoped_ptr<MP4StreamParser> parser_;
     42   bool configs_received_;
     43 
     44   bool AppendData(const uint8* data, size_t length) {
     45     return parser_->Parse(data, length);
     46   }
     47 
     48   bool AppendDataInPieces(const uint8* data, size_t length, size_t piece_size) {
     49     const uint8* start = data;
     50     const uint8* end = data + length;
     51     while (start < end) {
     52       size_t append_size = std::min(piece_size,
     53                                     static_cast<size_t>(end - start));
     54       if (!AppendData(start, append_size))
     55         return false;
     56       start += append_size;
     57     }
     58     return true;
     59   }
     60 
     61   void InitF(bool init_ok, base::TimeDelta duration) {
     62     DVLOG(1) << "InitF: ok=" << init_ok
     63              << ", dur=" << duration.InMilliseconds();
     64   }
     65 
     66   bool NewConfigF(const AudioDecoderConfig& ac,
     67                   const VideoDecoderConfig& vc,
     68                   const StreamParser::TextTrackConfigMap& tc) {
     69     DVLOG(1) << "NewConfigF: audio=" << ac.IsValidConfig()
     70              << ", video=" << vc.IsValidConfig();
     71     configs_received_ = true;
     72     return true;
     73   }
     74 
     75 
     76   void DumpBuffers(const std::string& label,
     77                    const StreamParser::BufferQueue& buffers) {
     78     DVLOG(2) << "DumpBuffers: " << label << " size " << buffers.size();
     79     for (StreamParser::BufferQueue::const_iterator buf = buffers.begin();
     80          buf != buffers.end(); buf++) {
     81       DVLOG(3) << "  n=" << buf - buffers.begin()
     82                << ", size=" << (*buf)->data_size()
     83                << ", dur=" << (*buf)->duration().InMilliseconds();
     84     }
     85   }
     86 
     87   bool NewBuffersF(const StreamParser::BufferQueue& audio_buffers,
     88                    const StreamParser::BufferQueue& video_buffers) {
     89     DumpBuffers("audio_buffers", audio_buffers);
     90     DumpBuffers("video_buffers", video_buffers);
     91     return true;
     92   }
     93 
     94   void KeyNeededF(const std::string& type,
     95                   const std::vector<uint8>& init_data) {
     96     DVLOG(1) << "KeyNeededF: " << init_data.size();
     97     EXPECT_EQ(kMp4InitDataType, type);
     98     EXPECT_FALSE(init_data.empty());
     99   }
    100 
    101   void NewSegmentF() {
    102     DVLOG(1) << "NewSegmentF";
    103   }
    104 
    105   void EndOfSegmentF() {
    106     DVLOG(1) << "EndOfSegmentF()";
    107   }
    108 
    109   void InitializeParser() {
    110     parser_->Init(
    111         base::Bind(&MP4StreamParserTest::InitF, base::Unretained(this)),
    112         base::Bind(&MP4StreamParserTest::NewConfigF, base::Unretained(this)),
    113         base::Bind(&MP4StreamParserTest::NewBuffersF, base::Unretained(this)),
    114         StreamParser::NewTextBuffersCB(),
    115         base::Bind(&MP4StreamParserTest::KeyNeededF, base::Unretained(this)),
    116         base::Bind(&MP4StreamParserTest::NewSegmentF, base::Unretained(this)),
    117         base::Bind(&MP4StreamParserTest::EndOfSegmentF,
    118                    base::Unretained(this)),
    119         LogCB());
    120   }
    121 
    122   bool ParseMP4File(const std::string& filename, int append_bytes) {
    123     InitializeParser();
    124 
    125     scoped_refptr<DecoderBuffer> buffer = ReadTestDataFile(filename);
    126     EXPECT_TRUE(AppendDataInPieces(buffer->data(),
    127                                    buffer->data_size(),
    128                                    append_bytes));
    129     return true;
    130   }
    131 };
    132 
    133 TEST_F(MP4StreamParserTest, UnalignedAppend) {
    134   // Test small, non-segment-aligned appends (small enough to exercise
    135   // incremental append system)
    136   ParseMP4File("bear-1280x720-av_frag.mp4", 512);
    137 }
    138 
    139 TEST_F(MP4StreamParserTest, BytewiseAppend) {
    140   // Ensure no incremental errors occur when parsing
    141   ParseMP4File("bear-1280x720-av_frag.mp4", 1);
    142 }
    143 
    144 TEST_F(MP4StreamParserTest, MultiFragmentAppend) {
    145   // Large size ensures multiple fragments are appended in one call (size is
    146   // larger than this particular test file)
    147   ParseMP4File("bear-1280x720-av_frag.mp4", 768432);
    148 }
    149 
    150 TEST_F(MP4StreamParserTest, Flush) {
    151   // Flush while reading sample data, then start a new stream.
    152   InitializeParser();
    153 
    154   scoped_refptr<DecoderBuffer> buffer =
    155       ReadTestDataFile("bear-1280x720-av_frag.mp4");
    156   EXPECT_TRUE(AppendDataInPieces(buffer->data(), 65536, 512));
    157   parser_->Flush();
    158   EXPECT_TRUE(AppendDataInPieces(buffer->data(),
    159                                  buffer->data_size(),
    160                                  512));
    161 }
    162 
    163 TEST_F(MP4StreamParserTest, Reinitialization) {
    164   InitializeParser();
    165 
    166   scoped_refptr<DecoderBuffer> buffer =
    167       ReadTestDataFile("bear-1280x720-av_frag.mp4");
    168   EXPECT_TRUE(AppendDataInPieces(buffer->data(),
    169                                  buffer->data_size(),
    170                                  512));
    171   EXPECT_TRUE(AppendDataInPieces(buffer->data(),
    172                                  buffer->data_size(),
    173                                  512));
    174 }
    175 
    176 TEST_F(MP4StreamParserTest, MPEG2_AAC_LC) {
    177   std::set<int> audio_object_types;
    178   audio_object_types.insert(kISO_13818_7_AAC_LC);
    179   parser_.reset(new MP4StreamParser(audio_object_types, false));
    180   ParseMP4File("bear-mpeg2-aac-only_frag.mp4", 512);
    181 }
    182 
    183 // Test that a moov box is not always required after Flush() is called.
    184 TEST_F(MP4StreamParserTest, NoMoovAfterFlush) {
    185   InitializeParser();
    186 
    187   scoped_refptr<DecoderBuffer> buffer =
    188       ReadTestDataFile("bear-1280x720-av_frag.mp4");
    189   EXPECT_TRUE(AppendDataInPieces(buffer->data(),
    190                                  buffer->data_size(),
    191                                  512));
    192   parser_->Flush();
    193 
    194   const int kFirstMoofOffset = 1307;
    195   EXPECT_TRUE(AppendDataInPieces(buffer->data() + kFirstMoofOffset,
    196                                  buffer->data_size() - kFirstMoofOffset,
    197                                  512));
    198 }
    199 
    200 // TODO(strobe): Create and test media which uses CENC auxiliary info stored
    201 // inside a private box
    202 
    203 }  // namespace mp4
    204 }  // namespace media
    205