• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
MP4StreamParserTest()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 
AppendData(const uint8 * data,size_t length)44   bool AppendData(const uint8* data, size_t length) {
45     return parser_->Parse(data, length);
46   }
47 
AppendDataInPieces(const uint8 * data,size_t length,size_t piece_size)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 
InitF(bool init_ok,base::TimeDelta duration)61   void InitF(bool init_ok, base::TimeDelta duration) {
62     DVLOG(1) << "InitF: ok=" << init_ok
63              << ", dur=" << duration.InMilliseconds();
64   }
65 
NewConfigF(const AudioDecoderConfig & ac,const VideoDecoderConfig & vc,const StreamParser::TextTrackConfigMap & tc)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 
DumpBuffers(const std::string & label,const StreamParser::BufferQueue & buffers)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 
NewBuffersF(const StreamParser::BufferQueue & audio_buffers,const StreamParser::BufferQueue & video_buffers)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 
KeyNeededF(const std::string & type,const std::vector<uint8> & init_data)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 
NewSegmentF()101   void NewSegmentF() {
102     DVLOG(1) << "NewSegmentF";
103   }
104 
EndOfSegmentF()105   void EndOfSegmentF() {
106     DVLOG(1) << "EndOfSegmentF()";
107   }
108 
InitializeParser()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 
ParseMP4File(const std::string & filename,int append_bytes)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 
TEST_F(MP4StreamParserTest,UnalignedAppend)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 
TEST_F(MP4StreamParserTest,BytewiseAppend)139 TEST_F(MP4StreamParserTest, BytewiseAppend) {
140   // Ensure no incremental errors occur when parsing
141   ParseMP4File("bear-1280x720-av_frag.mp4", 1);
142 }
143 
TEST_F(MP4StreamParserTest,MultiFragmentAppend)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 
TEST_F(MP4StreamParserTest,Flush)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 
TEST_F(MP4StreamParserTest,Reinitialization)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 
TEST_F(MP4StreamParserTest,MPEG2_AAC_LC)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.
TEST_F(MP4StreamParserTest,NoMoovAfterFlush)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