Home | History | Annotate | Download | only in mp2t
      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 
     22 namespace media {
     23 namespace mp2t {
     24 
     25 namespace {
     26 
     27 bool 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 
     40 bool 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 
     48 class 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 
    216 TEST_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 
    227 TEST_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 
    238 TEST_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 
    254 TEST_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