• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 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/formats/mp4/es_descriptor.h"
20 #include "media/formats/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         lower_bound_(
36             DecodeTimestamp::FromPresentationTime(base::TimeDelta::Max())) {
37     std::set<int> audio_object_types;
38     audio_object_types.insert(kISO_14496_3);
39     parser_.reset(new MP4StreamParser(audio_object_types, false));
40   }
41 
42  protected:
43   scoped_ptr<MP4StreamParser> parser_;
44   bool configs_received_;
45   DecodeTimestamp lower_bound_;
46 
AppendData(const uint8 * data,size_t length)47   bool AppendData(const uint8* data, size_t length) {
48     return parser_->Parse(data, length);
49   }
50 
AppendDataInPieces(const uint8 * data,size_t length,size_t piece_size)51   bool AppendDataInPieces(const uint8* data, size_t length, size_t piece_size) {
52     const uint8* start = data;
53     const uint8* end = data + length;
54     while (start < end) {
55       size_t append_size = std::min(piece_size,
56                                     static_cast<size_t>(end - start));
57       if (!AppendData(start, append_size))
58         return false;
59       start += append_size;
60     }
61     return true;
62   }
63 
InitF(bool init_ok,const StreamParser::InitParameters & params)64   void InitF(bool init_ok, const StreamParser::InitParameters& params) {
65     DVLOG(1) << "InitF: ok=" << init_ok
66              << ", dur=" << params.duration.InMilliseconds()
67              << ", autoTimestampOffset=" << params.auto_update_timestamp_offset;
68   }
69 
NewConfigF(const AudioDecoderConfig & ac,const VideoDecoderConfig & vc,const StreamParser::TextTrackConfigMap & tc)70   bool NewConfigF(const AudioDecoderConfig& ac,
71                   const VideoDecoderConfig& vc,
72                   const StreamParser::TextTrackConfigMap& tc) {
73     DVLOG(1) << "NewConfigF: audio=" << ac.IsValidConfig()
74              << ", video=" << vc.IsValidConfig();
75     configs_received_ = true;
76     return true;
77   }
78 
DumpBuffers(const std::string & label,const StreamParser::BufferQueue & buffers)79   void DumpBuffers(const std::string& label,
80                    const StreamParser::BufferQueue& buffers) {
81     DVLOG(2) << "DumpBuffers: " << label << " size " << buffers.size();
82     for (StreamParser::BufferQueue::const_iterator buf = buffers.begin();
83          buf != buffers.end(); buf++) {
84       DVLOG(3) << "  n=" << buf - buffers.begin()
85                << ", size=" << (*buf)->data_size()
86                << ", dur=" << (*buf)->duration().InMilliseconds();
87     }
88   }
89 
NewBuffersF(const StreamParser::BufferQueue & audio_buffers,const StreamParser::BufferQueue & video_buffers,const StreamParser::TextBufferQueueMap & text_map)90   bool NewBuffersF(const StreamParser::BufferQueue& audio_buffers,
91                    const StreamParser::BufferQueue& video_buffers,
92                    const StreamParser::TextBufferQueueMap& text_map) {
93     DumpBuffers("audio_buffers", audio_buffers);
94     DumpBuffers("video_buffers", video_buffers);
95 
96     // TODO(wolenetz/acolwell): Add text track support to more MSE parsers. See
97     // http://crbug.com/336926.
98     if (!text_map.empty())
99       return false;
100 
101     // Find the second highest timestamp so that we know what the
102     // timestamps on the next set of buffers must be >= than.
103     DecodeTimestamp audio = !audio_buffers.empty() ?
104         audio_buffers.back()->GetDecodeTimestamp() : kNoDecodeTimestamp();
105     DecodeTimestamp video = !video_buffers.empty() ?
106         video_buffers.back()->GetDecodeTimestamp() : kNoDecodeTimestamp();
107     DecodeTimestamp second_highest_timestamp =
108         (audio == kNoDecodeTimestamp() ||
109          (video != kNoDecodeTimestamp() && audio > video)) ? video : audio;
110 
111     DCHECK(second_highest_timestamp != kNoDecodeTimestamp());
112 
113     if (lower_bound_ != kNoDecodeTimestamp() &&
114         second_highest_timestamp < lower_bound_) {
115       return false;
116     }
117 
118     lower_bound_ = second_highest_timestamp;
119     return true;
120   }
121 
KeyNeededF(const std::string & type,const std::vector<uint8> & init_data)122   void KeyNeededF(const std::string& type,
123                   const std::vector<uint8>& init_data) {
124     DVLOG(1) << "KeyNeededF: " << init_data.size();
125     EXPECT_EQ(kMp4InitDataType, type);
126     EXPECT_FALSE(init_data.empty());
127   }
128 
NewSegmentF()129   void NewSegmentF() {
130     DVLOG(1) << "NewSegmentF";
131     lower_bound_ = kNoDecodeTimestamp();
132   }
133 
EndOfSegmentF()134   void EndOfSegmentF() {
135     DVLOG(1) << "EndOfSegmentF()";
136     lower_bound_ =
137         DecodeTimestamp::FromPresentationTime(base::TimeDelta::Max());
138   }
139 
InitializeParser()140   void InitializeParser() {
141     parser_->Init(
142         base::Bind(&MP4StreamParserTest::InitF, base::Unretained(this)),
143         base::Bind(&MP4StreamParserTest::NewConfigF, base::Unretained(this)),
144         base::Bind(&MP4StreamParserTest::NewBuffersF, base::Unretained(this)),
145         true,
146         base::Bind(&MP4StreamParserTest::KeyNeededF, base::Unretained(this)),
147         base::Bind(&MP4StreamParserTest::NewSegmentF, base::Unretained(this)),
148         base::Bind(&MP4StreamParserTest::EndOfSegmentF,
149                    base::Unretained(this)),
150         LogCB());
151   }
152 
ParseMP4File(const std::string & filename,int append_bytes)153   bool ParseMP4File(const std::string& filename, int append_bytes) {
154     InitializeParser();
155 
156     scoped_refptr<DecoderBuffer> buffer = ReadTestDataFile(filename);
157     EXPECT_TRUE(AppendDataInPieces(buffer->data(),
158                                    buffer->data_size(),
159                                    append_bytes));
160     return true;
161   }
162 };
163 
TEST_F(MP4StreamParserTest,UnalignedAppend)164 TEST_F(MP4StreamParserTest, UnalignedAppend) {
165   // Test small, non-segment-aligned appends (small enough to exercise
166   // incremental append system)
167   ParseMP4File("bear-1280x720-av_frag.mp4", 512);
168 }
169 
TEST_F(MP4StreamParserTest,BytewiseAppend)170 TEST_F(MP4StreamParserTest, BytewiseAppend) {
171   // Ensure no incremental errors occur when parsing
172   ParseMP4File("bear-1280x720-av_frag.mp4", 1);
173 }
174 
TEST_F(MP4StreamParserTest,MultiFragmentAppend)175 TEST_F(MP4StreamParserTest, MultiFragmentAppend) {
176   // Large size ensures multiple fragments are appended in one call (size is
177   // larger than this particular test file)
178   ParseMP4File("bear-1280x720-av_frag.mp4", 768432);
179 }
180 
TEST_F(MP4StreamParserTest,Flush)181 TEST_F(MP4StreamParserTest, Flush) {
182   // Flush while reading sample data, then start a new stream.
183   InitializeParser();
184 
185   scoped_refptr<DecoderBuffer> buffer =
186       ReadTestDataFile("bear-1280x720-av_frag.mp4");
187   EXPECT_TRUE(AppendDataInPieces(buffer->data(), 65536, 512));
188   parser_->Flush();
189   EXPECT_TRUE(AppendDataInPieces(buffer->data(),
190                                  buffer->data_size(),
191                                  512));
192 }
193 
TEST_F(MP4StreamParserTest,Reinitialization)194 TEST_F(MP4StreamParserTest, Reinitialization) {
195   InitializeParser();
196 
197   scoped_refptr<DecoderBuffer> buffer =
198       ReadTestDataFile("bear-1280x720-av_frag.mp4");
199   EXPECT_TRUE(AppendDataInPieces(buffer->data(),
200                                  buffer->data_size(),
201                                  512));
202   EXPECT_TRUE(AppendDataInPieces(buffer->data(),
203                                  buffer->data_size(),
204                                  512));
205 }
206 
TEST_F(MP4StreamParserTest,MPEG2_AAC_LC)207 TEST_F(MP4StreamParserTest, MPEG2_AAC_LC) {
208   std::set<int> audio_object_types;
209   audio_object_types.insert(kISO_13818_7_AAC_LC);
210   parser_.reset(new MP4StreamParser(audio_object_types, false));
211   ParseMP4File("bear-mpeg2-aac-only_frag.mp4", 512);
212 }
213 
214 // Test that a moov box is not always required after Flush() is called.
TEST_F(MP4StreamParserTest,NoMoovAfterFlush)215 TEST_F(MP4StreamParserTest, NoMoovAfterFlush) {
216   InitializeParser();
217 
218   scoped_refptr<DecoderBuffer> buffer =
219       ReadTestDataFile("bear-1280x720-av_frag.mp4");
220   EXPECT_TRUE(AppendDataInPieces(buffer->data(),
221                                  buffer->data_size(),
222                                  512));
223   parser_->Flush();
224 
225   const int kFirstMoofOffset = 1307;
226   EXPECT_TRUE(AppendDataInPieces(buffer->data() + kFirstMoofOffset,
227                                  buffer->data_size() - kFirstMoofOffset,
228                                  512));
229 }
230 
231 // Test an invalid file where there are encrypted samples, but
232 // SampleAuxiliaryInformation{Sizes|Offsets}Box (saiz|saio) are missing.
233 // The parser should fail instead of crash. See http://crbug.com/361347
TEST_F(MP4StreamParserTest,MissingSampleAuxInfo)234 TEST_F(MP4StreamParserTest, MissingSampleAuxInfo) {
235   InitializeParser();
236 
237   scoped_refptr<DecoderBuffer> buffer =
238       ReadTestDataFile("bear-1280x720-a_frag-cenc_missing-saiz-saio.mp4");
239   EXPECT_FALSE(AppendDataInPieces(buffer->data(), buffer->data_size(), 512));
240 }
241 
242 // Test a file where all video samples start with an Access Unit
243 // Delimiter (AUD) NALU.
TEST_F(MP4StreamParserTest,VideoSamplesStartWithAUDs)244 TEST_F(MP4StreamParserTest, VideoSamplesStartWithAUDs) {
245   ParseMP4File("bear-1280x720-av_with-aud-nalus_frag.mp4", 512);
246 }
247 
248 // TODO(strobe): Create and test media which uses CENC auxiliary info stored
249 // inside a private box
250 
251 }  // namespace mp4
252 }  // namespace media
253