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