mp4_stream_parser_unittest.cc revision 010d83a9304c5a91596085d917d248abff47903a
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
23using base::TimeDelta;
24
25namespace media {
26namespace mp4 {
27
28// TODO(xhwang): Figure out the init data type appropriately once it's spec'ed.
29static const char kMp4InitDataType[] = "video/mp4";
30
31class 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, const StreamParser::InitParameters& params) {
62    DVLOG(1) << "InitF: ok=" << init_ok
63             << ", dur=" << params.duration.InMilliseconds()
64             << ", autoTimestampOffset=" << params.auto_update_timestamp_offset;
65  }
66
67  bool NewConfigF(const AudioDecoderConfig& ac,
68                  const VideoDecoderConfig& vc,
69                  const StreamParser::TextTrackConfigMap& tc) {
70    DVLOG(1) << "NewConfigF: audio=" << ac.IsValidConfig()
71             << ", video=" << vc.IsValidConfig();
72    configs_received_ = true;
73    return true;
74  }
75
76
77  void DumpBuffers(const std::string& label,
78                   const StreamParser::BufferQueue& buffers) {
79    DVLOG(2) << "DumpBuffers: " << label << " size " << buffers.size();
80    for (StreamParser::BufferQueue::const_iterator buf = buffers.begin();
81         buf != buffers.end(); buf++) {
82      DVLOG(3) << "  n=" << buf - buffers.begin()
83               << ", size=" << (*buf)->data_size()
84               << ", dur=" << (*buf)->duration().InMilliseconds();
85    }
86  }
87
88  bool NewBuffersF(const StreamParser::BufferQueue& audio_buffers,
89                   const StreamParser::BufferQueue& video_buffers,
90                   const StreamParser::TextBufferQueueMap& text_map) {
91    DumpBuffers("audio_buffers", audio_buffers);
92    DumpBuffers("video_buffers", video_buffers);
93
94    // TODO(wolenetz/acolwell): Add text track support to more MSE parsers. See
95    // http://crbug.com/336926.
96    if (!text_map.empty())
97      return false;
98
99    return true;
100  }
101
102  void KeyNeededF(const std::string& type,
103                  const std::vector<uint8>& init_data) {
104    DVLOG(1) << "KeyNeededF: " << init_data.size();
105    EXPECT_EQ(kMp4InitDataType, type);
106    EXPECT_FALSE(init_data.empty());
107  }
108
109  void NewSegmentF() {
110    DVLOG(1) << "NewSegmentF";
111  }
112
113  void EndOfSegmentF() {
114    DVLOG(1) << "EndOfSegmentF()";
115  }
116
117  void InitializeParser() {
118    parser_->Init(
119        base::Bind(&MP4StreamParserTest::InitF, base::Unretained(this)),
120        base::Bind(&MP4StreamParserTest::NewConfigF, base::Unretained(this)),
121        base::Bind(&MP4StreamParserTest::NewBuffersF, base::Unretained(this)),
122        true,
123        base::Bind(&MP4StreamParserTest::KeyNeededF, base::Unretained(this)),
124        base::Bind(&MP4StreamParserTest::NewSegmentF, base::Unretained(this)),
125        base::Bind(&MP4StreamParserTest::EndOfSegmentF,
126                   base::Unretained(this)),
127        LogCB());
128  }
129
130  bool ParseMP4File(const std::string& filename, int append_bytes) {
131    InitializeParser();
132
133    scoped_refptr<DecoderBuffer> buffer = ReadTestDataFile(filename);
134    EXPECT_TRUE(AppendDataInPieces(buffer->data(),
135                                   buffer->data_size(),
136                                   append_bytes));
137    return true;
138  }
139};
140
141TEST_F(MP4StreamParserTest, UnalignedAppend) {
142  // Test small, non-segment-aligned appends (small enough to exercise
143  // incremental append system)
144  ParseMP4File("bear-1280x720-av_frag.mp4", 512);
145}
146
147TEST_F(MP4StreamParserTest, BytewiseAppend) {
148  // Ensure no incremental errors occur when parsing
149  ParseMP4File("bear-1280x720-av_frag.mp4", 1);
150}
151
152TEST_F(MP4StreamParserTest, MultiFragmentAppend) {
153  // Large size ensures multiple fragments are appended in one call (size is
154  // larger than this particular test file)
155  ParseMP4File("bear-1280x720-av_frag.mp4", 768432);
156}
157
158TEST_F(MP4StreamParserTest, Flush) {
159  // Flush while reading sample data, then start a new stream.
160  InitializeParser();
161
162  scoped_refptr<DecoderBuffer> buffer =
163      ReadTestDataFile("bear-1280x720-av_frag.mp4");
164  EXPECT_TRUE(AppendDataInPieces(buffer->data(), 65536, 512));
165  parser_->Flush();
166  EXPECT_TRUE(AppendDataInPieces(buffer->data(),
167                                 buffer->data_size(),
168                                 512));
169}
170
171TEST_F(MP4StreamParserTest, Reinitialization) {
172  InitializeParser();
173
174  scoped_refptr<DecoderBuffer> buffer =
175      ReadTestDataFile("bear-1280x720-av_frag.mp4");
176  EXPECT_TRUE(AppendDataInPieces(buffer->data(),
177                                 buffer->data_size(),
178                                 512));
179  EXPECT_TRUE(AppendDataInPieces(buffer->data(),
180                                 buffer->data_size(),
181                                 512));
182}
183
184TEST_F(MP4StreamParserTest, MPEG2_AAC_LC) {
185  std::set<int> audio_object_types;
186  audio_object_types.insert(kISO_13818_7_AAC_LC);
187  parser_.reset(new MP4StreamParser(audio_object_types, false));
188  ParseMP4File("bear-mpeg2-aac-only_frag.mp4", 512);
189}
190
191// Test that a moov box is not always required after Flush() is called.
192TEST_F(MP4StreamParserTest, NoMoovAfterFlush) {
193  InitializeParser();
194
195  scoped_refptr<DecoderBuffer> buffer =
196      ReadTestDataFile("bear-1280x720-av_frag.mp4");
197  EXPECT_TRUE(AppendDataInPieces(buffer->data(),
198                                 buffer->data_size(),
199                                 512));
200  parser_->Flush();
201
202  const int kFirstMoofOffset = 1307;
203  EXPECT_TRUE(AppendDataInPieces(buffer->data() + kFirstMoofOffset,
204                                 buffer->data_size() - kFirstMoofOffset,
205                                 512));
206}
207
208// Test an invalid file where there are encrypted samples, but
209// SampleAuxiliaryInformation{Sizes|Offsets}Box (saiz|saio) are missing.
210// The parser should fail instead of crash. See http://crbug.com/361347
211TEST_F(MP4StreamParserTest, MissingSampleAuxInfo) {
212  InitializeParser();
213
214  scoped_refptr<DecoderBuffer> buffer =
215      ReadTestDataFile("bear-1280x720-a_frag-cenc_missing-saiz-saio.mp4");
216  EXPECT_FALSE(AppendDataInPieces(buffer->data(), buffer->data_size(), 512));
217}
218
219// Test a file where all video samples start with an Access Unit
220// Delimiter (AUD) NALU.
221TEST_F(MP4StreamParserTest, VideoSamplesStartWithAUDs) {
222  ParseMP4File("bear-1280x720-av_with-aud-nalus_frag.mp4", 512);
223}
224
225// TODO(strobe): Create and test media which uses CENC auxiliary info stored
226// inside a private box
227
228}  // namespace mp4
229}  // namespace media
230