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