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/mp2t/mp2t_stream_parser.h"
20#include "testing/gtest/include/gtest/gtest.h"
21
22namespace media {
23namespace mp2t {
24
25namespace {
26
27bool IsMonotonic(const StreamParser::BufferQueue& buffers) {
28  if (buffers.empty())
29    return true;
30
31  StreamParser::BufferQueue::const_iterator it1 = buffers.begin();
32  StreamParser::BufferQueue::const_iterator it2 = ++it1;
33  for ( ; it2 != buffers.end(); ++it1, ++it2) {
34    if ((*it2)->GetDecodeTimestamp() < (*it1)->GetDecodeTimestamp())
35      return false;
36  }
37  return true;
38}
39
40bool IsAlmostEqual(DecodeTimestamp t0, DecodeTimestamp t1) {
41  base::TimeDelta kMaxDeviation = base::TimeDelta::FromMilliseconds(5);
42  base::TimeDelta diff = t1 - t0;
43  return (diff >= -kMaxDeviation && diff <= kMaxDeviation);
44}
45
46}  // namespace
47
48class Mp2tStreamParserTest : public testing::Test {
49 public:
50  Mp2tStreamParserTest()
51      : segment_count_(0),
52        config_count_(0),
53        audio_frame_count_(0),
54        video_frame_count_(0),
55        audio_min_dts_(kNoDecodeTimestamp()),
56        audio_max_dts_(kNoDecodeTimestamp()),
57        video_min_dts_(kNoDecodeTimestamp()),
58        video_max_dts_(kNoDecodeTimestamp()) {
59    bool has_sbr = false;
60    parser_.reset(new Mp2tStreamParser(has_sbr));
61  }
62
63 protected:
64  scoped_ptr<Mp2tStreamParser> parser_;
65  int segment_count_;
66  int config_count_;
67  int audio_frame_count_;
68  int video_frame_count_;
69  DecodeTimestamp audio_min_dts_;
70  DecodeTimestamp audio_max_dts_;
71  DecodeTimestamp video_min_dts_;
72  DecodeTimestamp video_max_dts_;
73
74  void ResetStats() {
75    segment_count_ = 0;
76    config_count_ = 0;
77    audio_frame_count_ = 0;
78    video_frame_count_ = 0;
79    audio_min_dts_ = kNoDecodeTimestamp();
80    audio_max_dts_ = kNoDecodeTimestamp();
81    video_min_dts_ = kNoDecodeTimestamp();
82    video_max_dts_ = kNoDecodeTimestamp();
83  }
84
85  bool AppendData(const uint8* data, size_t length) {
86    return parser_->Parse(data, length);
87  }
88
89  bool AppendDataInPieces(const uint8* data, size_t length, size_t piece_size) {
90    const uint8* start = data;
91    const uint8* end = data + length;
92    while (start < end) {
93      size_t append_size = std::min(piece_size,
94                                    static_cast<size_t>(end - start));
95      if (!AppendData(start, append_size))
96        return false;
97      start += append_size;
98    }
99    return true;
100  }
101
102  void OnInit(bool init_ok,
103              const StreamParser::InitParameters& params) {
104    DVLOG(1) << "OnInit: ok=" << init_ok
105             << ", dur=" << params.duration.InMilliseconds()
106             << ", autoTimestampOffset=" << params.auto_update_timestamp_offset;
107  }
108
109  bool OnNewConfig(const AudioDecoderConfig& ac,
110                   const VideoDecoderConfig& vc,
111                   const StreamParser::TextTrackConfigMap& tc) {
112    DVLOG(1) << "OnNewConfig: audio=" << ac.IsValidConfig()
113             << ", video=" << vc.IsValidConfig();
114    // Test streams have both audio and video, verify the configs are valid.
115    config_count_++;
116    EXPECT_TRUE(ac.IsValidConfig());
117    EXPECT_TRUE(vc.IsValidConfig());
118    return true;
119  }
120
121
122  void DumpBuffers(const std::string& label,
123                   const StreamParser::BufferQueue& buffers) {
124    DVLOG(2) << "DumpBuffers: " << label << " size " << buffers.size();
125    for (StreamParser::BufferQueue::const_iterator buf = buffers.begin();
126         buf != buffers.end(); buf++) {
127      DVLOG(3) << "  n=" << buf - buffers.begin()
128               << ", size=" << (*buf)->data_size()
129               << ", dur=" << (*buf)->duration().InMilliseconds();
130    }
131  }
132
133  bool OnNewBuffers(const StreamParser::BufferQueue& audio_buffers,
134                    const StreamParser::BufferQueue& video_buffers,
135                    const StreamParser::TextBufferQueueMap& text_map) {
136    EXPECT_GT(config_count_, 0);
137    DumpBuffers("audio_buffers", audio_buffers);
138    DumpBuffers("video_buffers", video_buffers);
139
140    // TODO(wolenetz/acolwell): Add text track support to more MSE parsers. See
141    // http://crbug.com/336926.
142    if (!text_map.empty())
143      return false;
144
145    // Verify monotonicity.
146    if (!IsMonotonic(video_buffers))
147      return false;
148    if (!IsMonotonic(audio_buffers))
149      return false;
150
151    if (!video_buffers.empty()) {
152      DecodeTimestamp first_dts = video_buffers.front()->GetDecodeTimestamp();
153      DecodeTimestamp last_dts = video_buffers.back()->GetDecodeTimestamp();
154      if (video_max_dts_ != kNoDecodeTimestamp() && first_dts < video_max_dts_)
155        return false;
156      if (video_min_dts_ == kNoDecodeTimestamp())
157        video_min_dts_ = first_dts;
158      video_max_dts_ = last_dts;
159    }
160    if (!audio_buffers.empty()) {
161      DecodeTimestamp first_dts = audio_buffers.front()->GetDecodeTimestamp();
162      DecodeTimestamp last_dts = audio_buffers.back()->GetDecodeTimestamp();
163      if (audio_max_dts_ != kNoDecodeTimestamp() && first_dts < audio_max_dts_)
164        return false;
165      if (audio_min_dts_ == kNoDecodeTimestamp())
166        audio_min_dts_ = first_dts;
167      audio_max_dts_ = last_dts;
168    }
169
170    audio_frame_count_ += audio_buffers.size();
171    video_frame_count_ += video_buffers.size();
172    return true;
173  }
174
175  void OnKeyNeeded(const std::string& type,
176                   const std::vector<uint8>& init_data) {
177    NOTREACHED() << "OnKeyNeeded not expected in the Mpeg2 TS parser";
178  }
179
180  void OnNewSegment() {
181    DVLOG(1) << "OnNewSegment";
182    segment_count_++;
183  }
184
185  void OnEndOfSegment() {
186    NOTREACHED() << "OnEndOfSegment not expected in the Mpeg2 TS parser";
187  }
188
189  void InitializeParser() {
190    parser_->Init(
191        base::Bind(&Mp2tStreamParserTest::OnInit,
192                   base::Unretained(this)),
193        base::Bind(&Mp2tStreamParserTest::OnNewConfig,
194                   base::Unretained(this)),
195        base::Bind(&Mp2tStreamParserTest::OnNewBuffers,
196                   base::Unretained(this)),
197        true,
198        base::Bind(&Mp2tStreamParserTest::OnKeyNeeded,
199                   base::Unretained(this)),
200        base::Bind(&Mp2tStreamParserTest::OnNewSegment,
201                   base::Unretained(this)),
202        base::Bind(&Mp2tStreamParserTest::OnEndOfSegment,
203                   base::Unretained(this)),
204        LogCB());
205  }
206
207  bool ParseMpeg2TsFile(const std::string& filename, int append_bytes) {
208    scoped_refptr<DecoderBuffer> buffer = ReadTestDataFile(filename);
209    EXPECT_TRUE(AppendDataInPieces(buffer->data(),
210                                   buffer->data_size(),
211                                   append_bytes));
212    return true;
213  }
214};
215
216TEST_F(Mp2tStreamParserTest, UnalignedAppend17) {
217  // Test small, non-segment-aligned appends.
218  InitializeParser();
219  ParseMpeg2TsFile("bear-1280x720.ts", 17);
220  parser_->Flush();
221  EXPECT_EQ(video_frame_count_, 82);
222  // This stream has no mid-stream configuration change.
223  EXPECT_EQ(config_count_, 1);
224  EXPECT_EQ(segment_count_, 1);
225}
226
227TEST_F(Mp2tStreamParserTest, UnalignedAppend512) {
228  // Test small, non-segment-aligned appends.
229  InitializeParser();
230  ParseMpeg2TsFile("bear-1280x720.ts", 512);
231  parser_->Flush();
232  EXPECT_EQ(video_frame_count_, 82);
233  // This stream has no mid-stream configuration change.
234  EXPECT_EQ(config_count_, 1);
235  EXPECT_EQ(segment_count_, 1);
236}
237
238TEST_F(Mp2tStreamParserTest, AppendAfterFlush512) {
239  InitializeParser();
240  ParseMpeg2TsFile("bear-1280x720.ts", 512);
241  parser_->Flush();
242  EXPECT_EQ(video_frame_count_, 82);
243  EXPECT_EQ(config_count_, 1);
244  EXPECT_EQ(segment_count_, 1);
245
246  ResetStats();
247  ParseMpeg2TsFile("bear-1280x720.ts", 512);
248  parser_->Flush();
249  EXPECT_EQ(video_frame_count_, 82);
250  EXPECT_EQ(config_count_, 1);
251  EXPECT_EQ(segment_count_, 1);
252}
253
254TEST_F(Mp2tStreamParserTest, TimestampWrapAround) {
255  // "bear-1280x720_ptswraparound.ts" has been transcoded
256  // from bear-1280x720.mp4 by applying a time offset of 95442s
257  // (close to 2^33 / 90000) which results in timestamps wrap around
258  // in the Mpeg2 TS stream.
259  InitializeParser();
260  ParseMpeg2TsFile("bear-1280x720_ptswraparound.ts", 512);
261  parser_->Flush();
262  EXPECT_EQ(video_frame_count_, 82);
263
264  EXPECT_TRUE(IsAlmostEqual(video_min_dts_,
265                            DecodeTimestamp::FromSecondsD(95443.376)));
266  EXPECT_TRUE(IsAlmostEqual(video_max_dts_,
267                            DecodeTimestamp::FromSecondsD(95446.079)));
268
269  // Note: for audio, AdtsStreamParser considers only the PTS (which is then
270  // used as the DTS).
271  // TODO(damienv): most of the time, audio streams just have PTS. Here, only
272  // the first PES packet has a DTS, all the other PES packets have PTS only.
273  // Reconsider the expected value for |audio_min_dts_| if DTS are used as part
274  // of the ADTS stream parser.
275  //
276  // Note: the last pts for audio is 95445.931 but this PES packet includes
277  // 9 ADTS frames with 1 AAC frame in each ADTS frame.
278  // So the PTS of the last AAC frame is:
279  // 95445.931 + 8 * (1024 / 44100) = 95446.117
280  EXPECT_TRUE(IsAlmostEqual(audio_min_dts_,
281                            DecodeTimestamp::FromSecondsD(95443.400)));
282  EXPECT_TRUE(IsAlmostEqual(audio_max_dts_,
283                            DecodeTimestamp::FromSecondsD(95446.117)));
284}
285
286}  // namespace mp2t
287}  // namespace media
288