Home | History | Annotate | Download | only in webm
      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 
      7 #include "base/bind.h"
      8 #include "base/logging.h"
      9 #include "media/base/decrypt_config.h"
     10 #include "media/webm/cluster_builder.h"
     11 #include "media/webm/webm_cluster_parser.h"
     12 #include "media/webm/webm_constants.h"
     13 #include "testing/gmock/include/gmock/gmock.h"
     14 #include "testing/gtest/include/gtest/gtest.h"
     15 
     16 using ::testing::InSequence;
     17 using ::testing::Return;
     18 using ::testing::_;
     19 
     20 namespace media {
     21 
     22 enum {
     23   kTimecodeScale = 1000000,  // Timecode scale for millisecond timestamps.
     24   kAudioTrackNum = 1,
     25   kVideoTrackNum = 2,
     26   kTextTrackNum = 3,
     27 };
     28 
     29 struct BlockInfo {
     30   int track_num;
     31   int timestamp;
     32   int duration;
     33   bool use_simple_block;
     34 };
     35 
     36 static const BlockInfo kDefaultBlockInfo[] = {
     37   { kAudioTrackNum, 0, 23, true },
     38   { kAudioTrackNum, 23, 23, true },
     39   { kVideoTrackNum, 33, 34, true },
     40   { kAudioTrackNum, 46, 23, true },
     41   { kVideoTrackNum, 67, 33, false },
     42   { kAudioTrackNum, 69, 23, false },
     43   { kVideoTrackNum, 100, 33, false },
     44 };
     45 
     46 static const uint8 kEncryptedFrame[] = {
     47   0x01,  // Block is encrypted
     48   0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08  // IV
     49 };
     50 
     51 static scoped_ptr<Cluster> CreateCluster(int timecode,
     52                                          const BlockInfo* block_info,
     53                                          int block_count) {
     54   ClusterBuilder cb;
     55   cb.SetClusterTimecode(0);
     56 
     57   for (int i = 0; i < block_count; i++) {
     58     uint8 data[] = { 0x00 };
     59     if (block_info[i].use_simple_block) {
     60       cb.AddSimpleBlock(block_info[i].track_num,
     61                         block_info[i].timestamp,
     62                         0, data, sizeof(data));
     63       continue;
     64     }
     65 
     66     CHECK_GE(block_info[i].duration, 0);
     67     cb.AddBlockGroup(block_info[i].track_num,
     68                      block_info[i].timestamp,
     69                      block_info[i].duration,
     70                      0, data, sizeof(data));
     71   }
     72 
     73   return cb.Finish();
     74 }
     75 
     76 // Creates a Cluster with one encrypted Block. |bytes_to_write| is number of
     77 // bytes of the encrypted frame to write.
     78 static scoped_ptr<Cluster> CreateEncryptedCluster(int bytes_to_write) {
     79   CHECK_GT(bytes_to_write, 0);
     80   CHECK_LE(bytes_to_write, static_cast<int>(sizeof(kEncryptedFrame)));
     81 
     82   ClusterBuilder cb;
     83   cb.SetClusterTimecode(0);
     84   cb.AddSimpleBlock(kVideoTrackNum, 0, 0, kEncryptedFrame, bytes_to_write);
     85   return cb.Finish();
     86 }
     87 
     88 static bool VerifyBuffers(const WebMClusterParser::BufferQueue& audio_buffers,
     89                           const WebMClusterParser::BufferQueue& video_buffers,
     90                           const WebMClusterParser::BufferQueue& text_buffers,
     91                           const BlockInfo* block_info,
     92                           int block_count) {
     93   size_t audio_offset = 0;
     94   size_t video_offset = 0;
     95   size_t text_offset = 0;
     96   for (int i = 0; i < block_count; i++) {
     97     const WebMClusterParser::BufferQueue* buffers = NULL;
     98     size_t* offset;
     99 
    100     if (block_info[i].track_num == kAudioTrackNum) {
    101       buffers = &audio_buffers;
    102       offset = &audio_offset;
    103     } else if (block_info[i].track_num == kVideoTrackNum) {
    104       buffers = &video_buffers;
    105       offset = &video_offset;
    106     } else if (block_info[i].track_num == kTextTrackNum) {
    107       buffers = &text_buffers;
    108       offset = &text_offset;
    109     } else {
    110       LOG(ERROR) << "Unexpected track number " << block_info[i].track_num;
    111       return false;
    112     }
    113 
    114     if (*offset >= buffers->size())
    115       return false;
    116 
    117     scoped_refptr<StreamParserBuffer> buffer = (*buffers)[(*offset)++];
    118 
    119 
    120     EXPECT_EQ(buffer->timestamp().InMilliseconds(), block_info[i].timestamp);
    121 
    122     if (!block_info[i].use_simple_block)
    123       EXPECT_NE(buffer->duration(), kNoTimestamp());
    124 
    125     if (buffer->duration() != kNoTimestamp())
    126       EXPECT_EQ(buffer->duration().InMilliseconds(), block_info[i].duration);
    127   }
    128 
    129   return true;
    130 }
    131 
    132 static bool VerifyBuffers(const scoped_ptr<WebMClusterParser>& parser,
    133                           const BlockInfo* block_info,
    134                           int block_count) {
    135   typedef WebMClusterParser::TextTrackIterator TextTrackIterator;
    136   TextTrackIterator text_it = parser->CreateTextTrackIterator();
    137 
    138   int text_track_num;
    139   const WebMClusterParser::BufferQueue* text_buffers;
    140 
    141   while (text_it(&text_track_num, &text_buffers))
    142     break;
    143 
    144   const WebMClusterParser::BufferQueue no_text_buffers;
    145 
    146   if (text_buffers == NULL)
    147     text_buffers = &no_text_buffers;
    148 
    149   return VerifyBuffers(parser->audio_buffers(),
    150                        parser->video_buffers(),
    151                        *text_buffers,
    152                        block_info,
    153                        block_count);
    154 }
    155 
    156 static bool VerifyTextBuffers(
    157     const scoped_ptr<WebMClusterParser>& parser,
    158     const BlockInfo* block_info_ptr,
    159     int block_count,
    160     int text_track_num,
    161     const WebMClusterParser::BufferQueue& text_buffers) {
    162   const BlockInfo* const block_info_end = block_info_ptr + block_count;
    163 
    164   typedef WebMClusterParser::BufferQueue::const_iterator TextBufferIter;
    165   TextBufferIter buffer_iter = text_buffers.begin();
    166   const TextBufferIter buffer_end = text_buffers.end();
    167 
    168   while (block_info_ptr != block_info_end) {
    169     const BlockInfo& block_info = *block_info_ptr++;
    170 
    171     if (block_info.track_num != text_track_num)
    172       continue;
    173 
    174     EXPECT_FALSE(block_info.use_simple_block);
    175     EXPECT_FALSE(buffer_iter == buffer_end);
    176 
    177     const scoped_refptr<StreamParserBuffer> buffer = *buffer_iter++;
    178     EXPECT_EQ(buffer->timestamp().InMilliseconds(), block_info.timestamp);
    179     EXPECT_EQ(buffer->duration().InMilliseconds(), block_info.duration);
    180   }
    181 
    182   EXPECT_TRUE(buffer_iter == buffer_end);
    183   return true;
    184 }
    185 
    186 static bool VerifyEncryptedBuffer(
    187     scoped_refptr<StreamParserBuffer> buffer) {
    188   EXPECT_TRUE(buffer->decrypt_config());
    189   EXPECT_EQ(static_cast<unsigned long>(DecryptConfig::kDecryptionKeySize),
    190             buffer->decrypt_config()->iv().length());
    191   const uint8* data = buffer->data();
    192   return data[0] & kWebMFlagEncryptedFrame;
    193 }
    194 
    195 static void AppendToEnd(const WebMClusterParser::BufferQueue& src,
    196                         WebMClusterParser::BufferQueue* dest) {
    197   for (WebMClusterParser::BufferQueue::const_iterator itr = src.begin();
    198        itr != src.end(); ++itr) {
    199     dest->push_back(*itr);
    200   }
    201 }
    202 
    203 class WebMClusterParserTest : public testing::Test {
    204  public:
    205   WebMClusterParserTest()
    206       : parser_(new WebMClusterParser(kTimecodeScale,
    207                                       kAudioTrackNum,
    208                                       kVideoTrackNum,
    209                                       WebMTracksParser::TextTracks(),
    210                                       std::set<int64>(),
    211                                       std::string(),
    212                                       std::string(),
    213                                       LogCB())) {}
    214 
    215  protected:
    216   scoped_ptr<WebMClusterParser> parser_;
    217 };
    218 
    219 TEST_F(WebMClusterParserTest, Reset) {
    220   InSequence s;
    221 
    222   int block_count = arraysize(kDefaultBlockInfo);
    223   scoped_ptr<Cluster> cluster(CreateCluster(0, kDefaultBlockInfo, block_count));
    224 
    225   // Send slightly less than the full cluster so all but the last block is
    226   // parsed.
    227   int result = parser_->Parse(cluster->data(), cluster->size() - 1);
    228   EXPECT_GT(result, 0);
    229   EXPECT_LT(result, cluster->size());
    230 
    231   ASSERT_TRUE(VerifyBuffers(parser_, kDefaultBlockInfo, block_count - 1));
    232   parser_->Reset();
    233 
    234   // Now parse a whole cluster to verify that all the blocks will get parsed.
    235   result = parser_->Parse(cluster->data(), cluster->size());
    236   EXPECT_EQ(result, cluster->size());
    237   ASSERT_TRUE(VerifyBuffers(parser_, kDefaultBlockInfo, block_count));
    238 }
    239 
    240 TEST_F(WebMClusterParserTest, ParseClusterWithSingleCall) {
    241   int block_count = arraysize(kDefaultBlockInfo);
    242   scoped_ptr<Cluster> cluster(CreateCluster(0, kDefaultBlockInfo, block_count));
    243 
    244   int result = parser_->Parse(cluster->data(), cluster->size());
    245   EXPECT_EQ(cluster->size(), result);
    246   ASSERT_TRUE(VerifyBuffers(parser_, kDefaultBlockInfo, block_count));
    247 }
    248 
    249 TEST_F(WebMClusterParserTest, ParseClusterWithMultipleCalls) {
    250   int block_count = arraysize(kDefaultBlockInfo);
    251   scoped_ptr<Cluster> cluster(CreateCluster(0, kDefaultBlockInfo, block_count));
    252 
    253   WebMClusterParser::BufferQueue audio_buffers;
    254   WebMClusterParser::BufferQueue video_buffers;
    255   const WebMClusterParser::BufferQueue no_text_buffers;
    256 
    257   const uint8* data = cluster->data();
    258   int size = cluster->size();
    259   int default_parse_size = 3;
    260   int parse_size = std::min(default_parse_size, size);
    261 
    262   while (size > 0) {
    263     int result = parser_->Parse(data, parse_size);
    264     ASSERT_GE(result, 0);
    265     ASSERT_LE(result, parse_size);
    266 
    267     if (result == 0) {
    268       // The parser needs more data so increase the parse_size a little.
    269       parse_size += default_parse_size;
    270       parse_size = std::min(parse_size, size);
    271       continue;
    272     }
    273 
    274     AppendToEnd(parser_->audio_buffers(), &audio_buffers);
    275     AppendToEnd(parser_->video_buffers(), &video_buffers);
    276 
    277     parse_size = default_parse_size;
    278 
    279     data += result;
    280     size -= result;
    281   }
    282   ASSERT_TRUE(VerifyBuffers(audio_buffers, video_buffers,
    283                             no_text_buffers, kDefaultBlockInfo,
    284                             block_count));
    285 }
    286 
    287 // Verify that both BlockGroups with the BlockDuration before the Block
    288 // and BlockGroups with the BlockDuration after the Block are supported
    289 // correctly.
    290 // Note: Raw bytes are use here because ClusterBuilder only generates
    291 // one of these scenarios.
    292 TEST_F(WebMClusterParserTest, ParseBlockGroup) {
    293   const BlockInfo kBlockInfo[] = {
    294     { kAudioTrackNum, 0, 23, false },
    295     { kVideoTrackNum, 33, 34, false },
    296   };
    297   int block_count = arraysize(kBlockInfo);
    298 
    299   const uint8 kClusterData[] = {
    300     0x1F, 0x43, 0xB6, 0x75, 0x9B,  // Cluster(size=27)
    301     0xE7, 0x81, 0x00,  // Timecode(size=1, value=0)
    302     // BlockGroup with BlockDuration before Block.
    303     0xA0, 0x8A,  // BlockGroup(size=10)
    304     0x9B, 0x81, 0x17,  // BlockDuration(size=1, value=23)
    305     0xA1, 0x85, 0x81, 0x00, 0x00, 0x00, 0xaa,  // Block(size=5, track=1, ts=0)
    306     // BlockGroup with BlockDuration after Block.
    307     0xA0, 0x8A,  // BlockGroup(size=10)
    308     0xA1, 0x85, 0x82, 0x00, 0x21, 0x00, 0x55,  // Block(size=5, track=2, ts=33)
    309     0x9B, 0x81, 0x22,  // BlockDuration(size=1, value=34)
    310   };
    311   const int kClusterSize = sizeof(kClusterData);
    312 
    313   int result = parser_->Parse(kClusterData, kClusterSize);
    314   EXPECT_EQ(result, kClusterSize);
    315   ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo, block_count));
    316 }
    317 
    318 TEST_F(WebMClusterParserTest, ParseSimpleBlockAndBlockGroupMixture) {
    319   const BlockInfo kBlockInfo[] = {
    320     { kAudioTrackNum, 0, 23, true },
    321     { kAudioTrackNum, 23, 23, false },
    322     { kVideoTrackNum, 33, 34, true },
    323     { kAudioTrackNum, 46, 23, false },
    324     { kVideoTrackNum, 67, 33, false },
    325   };
    326   int block_count = arraysize(kBlockInfo);
    327   scoped_ptr<Cluster> cluster(CreateCluster(0, kBlockInfo, block_count));
    328 
    329   int result = parser_->Parse(cluster->data(), cluster->size());
    330   EXPECT_EQ(cluster->size(), result);
    331   ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo, block_count));
    332 }
    333 
    334 TEST_F(WebMClusterParserTest, IgnoredTracks) {
    335   std::set<int64> ignored_tracks;
    336   ignored_tracks.insert(kTextTrackNum);
    337 
    338   parser_.reset(new WebMClusterParser(kTimecodeScale,
    339                                       kAudioTrackNum,
    340                                       kVideoTrackNum,
    341                                       WebMTracksParser::TextTracks(),
    342                                       ignored_tracks,
    343                                       std::string(),
    344                                       std::string(),
    345                                       LogCB()));
    346 
    347   const BlockInfo kInputBlockInfo[] = {
    348     { kAudioTrackNum, 0,  23, true },
    349     { kAudioTrackNum, 23, 23, true },
    350     { kVideoTrackNum, 33, 33, true },
    351     { kTextTrackNum,  33, 99, true },
    352     { kAudioTrackNum, 46, 23, true },
    353     { kVideoTrackNum, 67, 33, true },
    354   };
    355   int input_block_count = arraysize(kInputBlockInfo);
    356 
    357   const BlockInfo kOutputBlockInfo[] = {
    358     { kAudioTrackNum, 0,  23, true },
    359     { kAudioTrackNum, 23, 23, true },
    360     { kVideoTrackNum, 33, 33, true },
    361     { kAudioTrackNum, 46, 23, true },
    362     { kVideoTrackNum, 67, 33, true },
    363   };
    364   int output_block_count = arraysize(kOutputBlockInfo);
    365 
    366   scoped_ptr<Cluster> cluster(
    367       CreateCluster(0, kInputBlockInfo, input_block_count));
    368 
    369   int result = parser_->Parse(cluster->data(), cluster->size());
    370   EXPECT_EQ(cluster->size(), result);
    371   ASSERT_TRUE(VerifyBuffers(parser_, kOutputBlockInfo, output_block_count));
    372 }
    373 
    374 TEST_F(WebMClusterParserTest, ParseTextTracks) {
    375   typedef WebMTracksParser::TextTracks TextTracks;
    376   TextTracks text_tracks;
    377   WebMTracksParser::TextTrackInfo text_track_info;
    378 
    379   text_track_info.kind = kTextSubtitles;
    380   text_tracks.insert(std::make_pair(TextTracks::key_type(kTextTrackNum),
    381                                     text_track_info));
    382 
    383   parser_.reset(new WebMClusterParser(kTimecodeScale,
    384                                       kAudioTrackNum,
    385                                       kVideoTrackNum,
    386                                       text_tracks,
    387                                       std::set<int64>(),
    388                                       std::string(),
    389                                       std::string(),
    390                                       LogCB()));
    391 
    392   const BlockInfo kInputBlockInfo[] = {
    393     { kAudioTrackNum, 0,  23, true },
    394     { kAudioTrackNum, 23, 23, true },
    395     { kVideoTrackNum, 33, 33, true },
    396     { kTextTrackNum,  33, 42, false },
    397     { kAudioTrackNum, 46, 23, true },
    398     { kTextTrackNum, 55, 44, false },
    399     { kVideoTrackNum, 67, 33, true },
    400   };
    401   int input_block_count = arraysize(kInputBlockInfo);
    402 
    403   scoped_ptr<Cluster> cluster(
    404       CreateCluster(0, kInputBlockInfo, input_block_count));
    405 
    406   int result = parser_->Parse(cluster->data(), cluster->size());
    407   EXPECT_EQ(cluster->size(), result);
    408   ASSERT_TRUE(VerifyBuffers(parser_, kInputBlockInfo, input_block_count));
    409 }
    410 
    411 TEST_F(WebMClusterParserTest, TextTracksSimpleBlock) {
    412   typedef WebMTracksParser::TextTracks TextTracks;
    413   TextTracks text_tracks;
    414   WebMTracksParser::TextTrackInfo text_track_info;
    415 
    416   text_track_info.kind = kTextSubtitles;
    417   text_tracks.insert(std::make_pair(TextTracks::key_type(kTextTrackNum),
    418                                     text_track_info));
    419 
    420   parser_.reset(new WebMClusterParser(kTimecodeScale,
    421                                       kAudioTrackNum,
    422                                       kVideoTrackNum,
    423                                       text_tracks,
    424                                       std::set<int64>(),
    425                                       std::string(),
    426                                       std::string(),
    427                                       LogCB()));
    428 
    429   const BlockInfo kInputBlockInfo[] = {
    430     { kTextTrackNum,  33, 42, true },
    431   };
    432   int input_block_count = arraysize(kInputBlockInfo);
    433 
    434   scoped_ptr<Cluster> cluster(
    435       CreateCluster(0, kInputBlockInfo, input_block_count));
    436 
    437   int result = parser_->Parse(cluster->data(), cluster->size());
    438   EXPECT_LT(result, 0);
    439 }
    440 
    441 TEST_F(WebMClusterParserTest, ParseMultipleTextTracks) {
    442   typedef WebMTracksParser::TextTracks TextTracks;
    443   TextTracks text_tracks;
    444   WebMTracksParser::TextTrackInfo text_track_info;
    445 
    446   const int kSubtitleTextTrackNum = kTextTrackNum;
    447   const int kCaptionTextTrackNum = kTextTrackNum + 1;
    448 
    449   text_track_info.kind = kTextSubtitles;
    450   text_tracks.insert(std::make_pair(TextTracks::key_type(kSubtitleTextTrackNum),
    451                                     text_track_info));
    452 
    453   text_track_info.kind = kTextCaptions;
    454   text_tracks.insert(std::make_pair(TextTracks::key_type(kCaptionTextTrackNum),
    455                                     text_track_info));
    456 
    457   parser_.reset(new WebMClusterParser(kTimecodeScale,
    458                                       kAudioTrackNum,
    459                                       kVideoTrackNum,
    460                                       text_tracks,
    461                                       std::set<int64>(),
    462                                       std::string(),
    463                                       std::string(),
    464                                       LogCB()));
    465 
    466   const BlockInfo kInputBlockInfo[] = {
    467     { kAudioTrackNum, 0,  23, true },
    468     { kAudioTrackNum, 23, 23, true },
    469     { kVideoTrackNum, 33, 33, true },
    470     { kSubtitleTextTrackNum,  33, 42, false },
    471     { kAudioTrackNum, 46, 23, true },
    472     { kCaptionTextTrackNum, 55, 44, false },
    473     { kVideoTrackNum, 67, 33, true },
    474     { kSubtitleTextTrackNum,  67, 33, false },
    475   };
    476   int input_block_count = arraysize(kInputBlockInfo);
    477 
    478   scoped_ptr<Cluster> cluster(
    479       CreateCluster(0, kInputBlockInfo, input_block_count));
    480 
    481   int result = parser_->Parse(cluster->data(), cluster->size());
    482   EXPECT_EQ(cluster->size(), result);
    483 
    484   WebMClusterParser::TextTrackIterator text_it =
    485       parser_->CreateTextTrackIterator();
    486 
    487   int text_track_num;
    488   const WebMClusterParser::BufferQueue* text_buffers;
    489 
    490   while (text_it(&text_track_num, &text_buffers)) {
    491     const WebMTracksParser::TextTracks::const_iterator find_result =
    492         text_tracks.find(text_track_num);
    493     ASSERT_TRUE(find_result != text_tracks.end());
    494     ASSERT_TRUE(VerifyTextBuffers(parser_, kInputBlockInfo, input_block_count,
    495                                   text_track_num, *text_buffers));
    496   }
    497 }
    498 
    499 TEST_F(WebMClusterParserTest, ParseEncryptedBlock) {
    500   scoped_ptr<Cluster> cluster(CreateEncryptedCluster(sizeof(kEncryptedFrame)));
    501 
    502   parser_.reset(new WebMClusterParser(kTimecodeScale,
    503                                       kAudioTrackNum,
    504                                       kVideoTrackNum,
    505                                       WebMTracksParser::TextTracks(),
    506                                       std::set<int64>(),
    507                                       std::string(),
    508                                       "video_key_id",
    509                                       LogCB()));
    510   int result = parser_->Parse(cluster->data(), cluster->size());
    511   EXPECT_EQ(cluster->size(), result);
    512   ASSERT_EQ(1UL, parser_->video_buffers().size());
    513   scoped_refptr<StreamParserBuffer> buffer = parser_->video_buffers()[0];
    514   EXPECT_TRUE(VerifyEncryptedBuffer(buffer));
    515 }
    516 
    517 TEST_F(WebMClusterParserTest, ParseBadEncryptedBlock) {
    518   scoped_ptr<Cluster> cluster(
    519       CreateEncryptedCluster(sizeof(kEncryptedFrame) - 1));
    520 
    521   parser_.reset(new WebMClusterParser(kTimecodeScale,
    522                                       kAudioTrackNum,
    523                                       kVideoTrackNum,
    524                                       WebMTracksParser::TextTracks(),
    525                                       std::set<int64>(),
    526                                       std::string(),
    527                                       "video_key_id",
    528                                       LogCB()));
    529   int result = parser_->Parse(cluster->data(), cluster->size());
    530   EXPECT_EQ(-1, result);
    531 }
    532 
    533 }  // namespace media
    534