Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / media / formats / webm / webm_cluster_parser_unittest.cc
index 8b32f5d..55dc791 100644 (file)
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include <algorithm>
+#include <cstdlib>
 
 #include "base/bind.h"
 #include "base/logging.h"
@@ -19,16 +20,35 @@ using ::testing::_;
 
 namespace media {
 
+typedef WebMTracksParser::TextTracks TextTracks;
+
 enum {
   kTimecodeScale = 1000000,  // Timecode scale for millisecond timestamps.
   kAudioTrackNum = 1,
   kVideoTrackNum = 2,
   kTextTrackNum = 3,
+  kTestAudioFrameDefaultDurationInMs = 13,
+  kTestVideoFrameDefaultDurationInMs = 17
 };
 
+COMPILE_ASSERT(
+    static_cast<int>(kTestAudioFrameDefaultDurationInMs) !=
+        static_cast<int>(WebMClusterParser::kDefaultAudioBufferDurationInMs),
+    test_default_is_same_as_estimation_fallback_audio_duration);
+COMPILE_ASSERT(
+    static_cast<int>(kTestVideoFrameDefaultDurationInMs) !=
+        static_cast<int>(WebMClusterParser::kDefaultVideoBufferDurationInMs),
+    test_default_is_same_as_estimation_fallback_video_duration);
+
 struct BlockInfo {
   int track_num;
   int timestamp;
+
+  // Negative value is allowed only for block groups (not simple blocks) and
+  // directs CreateCluster() to exclude BlockDuration entry from the cluster for
+  // this BlockGroup. The absolute value is used for parser verification.
+  // For simple blocks, this value must be non-negative, and is used only for
+  // parser verification.
   int duration;
   bool use_simple_block;
 };
@@ -57,13 +77,20 @@ static scoped_ptr<Cluster> CreateCluster(int timecode,
   for (int i = 0; i < block_count; i++) {
     uint8 data[] = { 0x00 };
     if (block_info[i].use_simple_block) {
+      CHECK_GE(block_info[i].duration, 0);
       cb.AddSimpleBlock(block_info[i].track_num,
                         block_info[i].timestamp,
                         0, data, sizeof(data));
       continue;
     }
 
-    CHECK_GE(block_info[i].duration, 0);
+    if (block_info[i].duration < 0) {
+      cb.AddBlockGroupWithoutBlockDuration(block_info[i].track_num,
+                                           block_info[i].timestamp,
+                                           0, data, sizeof(data));
+      continue;
+    }
+
     cb.AddBlockGroup(block_info[i].track_num,
                      block_info[i].timestamp,
                      block_info[i].duration,
@@ -90,6 +117,14 @@ static bool VerifyBuffers(const WebMClusterParser::BufferQueue& audio_buffers,
                           const WebMClusterParser::BufferQueue& text_buffers,
                           const BlockInfo* block_info,
                           int block_count) {
+  int buffer_count = audio_buffers.size() + video_buffers.size() +
+      text_buffers.size();
+  if (block_count != buffer_count) {
+    DVLOG(1) << __FUNCTION__ << " : block_count (" << block_count
+             << ") mismatches buffer_count (" << buffer_count << ")";
+    return false;
+  }
+
   size_t audio_offset = 0;
   size_t video_offset = 0;
   size_t text_offset = 0;
@@ -115,13 +150,18 @@ static bool VerifyBuffers(const WebMClusterParser::BufferQueue& audio_buffers,
       return false;
     }
 
-    if (*offset >= buffers->size())
+    if (*offset >= buffers->size()) {
+      DVLOG(1) << __FUNCTION__ << " : Too few buffers (" << buffers->size()
+               << ") for track_num (" << block_info[i].track_num
+               << "), expected at least " << *offset + 1 << " buffers";
       return false;
+    }
 
     scoped_refptr<StreamParserBuffer> buffer = (*buffers)[(*offset)++];
 
     EXPECT_EQ(block_info[i].timestamp, buffer->timestamp().InMilliseconds());
-    EXPECT_EQ(block_info[i].duration, buffer->duration().InMilliseconds());
+    EXPECT_EQ(std::abs(block_info[i].duration),
+              buffer->duration().InMilliseconds());
     EXPECT_EQ(expected_type, buffer->type());
     EXPECT_EQ(block_info[i].track_num, buffer->track_id());
   }
@@ -171,7 +211,8 @@ static bool VerifyTextBuffers(
 
     const scoped_refptr<StreamParserBuffer> buffer = *buffer_iter++;
     EXPECT_EQ(block_info.timestamp, buffer->timestamp().InMilliseconds());
-    EXPECT_EQ(block_info.duration, buffer->duration().InMilliseconds());
+    EXPECT_EQ(std::abs(block_info.duration),
+              buffer->duration().InMilliseconds());
     EXPECT_EQ(DemuxerStream::TEXT, buffer->type());
     EXPECT_EQ(text_track_num, buffer->track_id());
   }
@@ -203,16 +244,131 @@ class WebMClusterParserTest : public testing::Test {
                                       kNoTimestamp(),
                                       kVideoTrackNum,
                                       kNoTimestamp(),
-                                      WebMTracksParser::TextTracks(),
+                                      TextTracks(),
                                       std::set<int64>(),
                                       std::string(),
                                       std::string(),
                                       LogCB())) {}
 
  protected:
+  void ResetParserToHaveDefaultDurations() {
+    base::TimeDelta default_audio_duration = base::TimeDelta::FromMilliseconds(
+        kTestAudioFrameDefaultDurationInMs);
+    base::TimeDelta default_video_duration = base::TimeDelta::FromMilliseconds(
+        kTestVideoFrameDefaultDurationInMs);
+    ASSERT_GE(default_audio_duration, base::TimeDelta());
+    ASSERT_GE(default_video_duration, base::TimeDelta());
+    ASSERT_NE(kNoTimestamp(), default_audio_duration);
+    ASSERT_NE(kNoTimestamp(), default_video_duration);
+
+    parser_.reset(new WebMClusterParser(kTimecodeScale,
+                                        kAudioTrackNum,
+                                        default_audio_duration,
+                                        kVideoTrackNum,
+                                        default_video_duration,
+                                        TextTracks(),
+                                        std::set<int64>(),
+                                        std::string(),
+                                        std::string(),
+                                        LogCB()));
+  }
+
   scoped_ptr<WebMClusterParser> parser_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(WebMClusterParserTest);
 };
 
+TEST_F(WebMClusterParserTest, HeldBackBufferHoldsBackAllTracks) {
+  // If a buffer is missing duration and is being held back, then all other
+  // tracks' buffers that have same or higher (decode) timestamp should be held
+  // back too to keep the timestamps emitted for a cluster monotonically
+  // non-decreasing and in same order as parsed.
+  InSequence s;
+
+  // Reset the parser to have 3 tracks: text, video (no default frame duration),
+  // and audio (with a default frame duration).
+  TextTracks text_tracks;
+  text_tracks.insert(std::make_pair(TextTracks::key_type(kTextTrackNum),
+                                    TextTrackConfig(kTextSubtitles, "", "",
+                                                    "")));
+  base::TimeDelta default_audio_duration =
+      base::TimeDelta::FromMilliseconds(kTestAudioFrameDefaultDurationInMs);
+  ASSERT_GE(default_audio_duration, base::TimeDelta());
+  ASSERT_NE(kNoTimestamp(), default_audio_duration);
+  parser_.reset(new WebMClusterParser(kTimecodeScale,
+                                      kAudioTrackNum,
+                                      default_audio_duration,
+                                      kVideoTrackNum,
+                                      kNoTimestamp(),
+                                      text_tracks,
+                                      std::set<int64>(),
+                                      std::string(),
+                                      std::string(),
+                                      LogCB()));
+
+  const BlockInfo kBlockInfo[] = {
+    { kVideoTrackNum, 0, 33, true },
+    { kAudioTrackNum, 0, 23, false },
+    { kTextTrackNum, 10, 42, false },
+    { kAudioTrackNum, 23, kTestAudioFrameDefaultDurationInMs, true },
+    { kVideoTrackNum, 33, 33, true },
+    { kAudioTrackNum, 36, kTestAudioFrameDefaultDurationInMs, true },
+    { kVideoTrackNum, 66, 33, true },
+    { kAudioTrackNum, 70, kTestAudioFrameDefaultDurationInMs, true },
+    { kAudioTrackNum, 83, kTestAudioFrameDefaultDurationInMs, true },
+  };
+
+  const int kExpectedBuffersOnPartialCluster[] = {
+    0,  // Video simple block without DefaultDuration should be held back
+    0,  // Audio buffer ready, but not emitted because its TS >= held back video
+    0,  // Text buffer ready, but not emitted because its TS >= held back video
+    0,  // 2nd audio buffer ready, also not emitted for same reason as first
+    4,  // All previous buffers emitted, 2nd video held back with no duration
+    4,  // 2nd video still has no duration, 3rd audio ready but not emitted
+    6,  // All previous buffers emitted, 3rd video held back with no duration
+    6,  // 3rd video still has no duration, 4th audio ready but not emitted
+    9,  // Cluster end emits all buffers and 3rd video's duration is estimated
+  };
+
+  ASSERT_EQ(arraysize(kBlockInfo), arraysize(kExpectedBuffersOnPartialCluster));
+  int block_count = arraysize(kBlockInfo);
+
+  // Iteratively create a cluster containing the first N+1 blocks and parse all
+  // but the last byte of the cluster (except when N==|block_count|, just parse
+  // the whole cluster). Verify that the corresponding entry in
+  // |kExpectedBuffersOnPartialCluster| identifies the exact subset of
+  // |kBlockInfo| returned by the parser.
+  for (int i = 0; i < block_count; ++i) {
+    if (i > 0)
+      parser_->Reset();
+    // Since we don't know exactly the offsets of each block in the full
+    // cluster, build a cluster with exactly one additional block so that
+    // parse of all but one byte should deterministically parse all but the
+    // last full block. Don't |exceed block_count| blocks though.
+    int blocks_in_cluster = std::min(i + 2, block_count);
+    scoped_ptr<Cluster> cluster(CreateCluster(0, kBlockInfo,
+                                              blocks_in_cluster));
+    // Parse all but the last byte unless we need to parse the full cluster.
+    bool parse_full_cluster = i == (block_count - 1);
+    int result = parser_->Parse(cluster->data(), parse_full_cluster ?
+                                cluster->size() : cluster->size() - 1);
+    if (parse_full_cluster) {
+      DVLOG(1) << "Verifying parse result of full cluster of "
+               << blocks_in_cluster << " blocks";
+      EXPECT_EQ(cluster->size(), result);
+    } else {
+      DVLOG(1) << "Verifying parse result of cluster of "
+               << blocks_in_cluster << " blocks with last block incomplete";
+      EXPECT_GT(cluster->size(), result);
+      EXPECT_LT(0, result);
+    }
+
+    EXPECT_TRUE(VerifyBuffers(parser_, kBlockInfo,
+                              kExpectedBuffersOnPartialCluster[i]));
+  }
+}
+
 TEST_F(WebMClusterParserTest, Reset) {
   InSequence s;
 
@@ -337,7 +493,7 @@ TEST_F(WebMClusterParserTest, IgnoredTracks) {
                                       kNoTimestamp(),
                                       kVideoTrackNum,
                                       kNoTimestamp(),
-                                      WebMTracksParser::TextTracks(),
+                                      TextTracks(),
                                       ignored_tracks,
                                       std::string(),
                                       std::string(),
@@ -371,7 +527,6 @@ TEST_F(WebMClusterParserTest, IgnoredTracks) {
 }
 
 TEST_F(WebMClusterParserTest, ParseTextTracks) {
-  typedef WebMTracksParser::TextTracks TextTracks;
   TextTracks text_tracks;
 
   text_tracks.insert(std::make_pair(TextTracks::key_type(kTextTrackNum),
@@ -409,8 +564,7 @@ TEST_F(WebMClusterParserTest, ParseTextTracks) {
 }
 
 TEST_F(WebMClusterParserTest, TextTracksSimpleBlock) {
-  typedef WebMTracksParser::TextTracks TextTracks;
-  WebMTracksParser::TextTracks text_tracks;
+  TextTracks text_tracks;
 
   text_tracks.insert(std::make_pair(TextTracks::key_type(kTextTrackNum),
                                     TextTrackConfig(kTextSubtitles, "", "",
@@ -440,7 +594,6 @@ TEST_F(WebMClusterParserTest, TextTracksSimpleBlock) {
 }
 
 TEST_F(WebMClusterParserTest, ParseMultipleTextTracks) {
-  typedef WebMTracksParser::TextTracks TextTracks;
   TextTracks text_tracks;
 
   const int kSubtitleTextTrackNum = kTextTrackNum;
@@ -489,7 +642,7 @@ TEST_F(WebMClusterParserTest, ParseMultipleTextTracks) {
            text_map.begin();
        itr != text_map.end();
        ++itr) {
-    const WebMTracksParser::TextTracks::const_iterator find_result =
+    const TextTracks::const_iterator find_result =
         text_tracks.find(itr->first);
     ASSERT_TRUE(find_result != text_tracks.end());
     ASSERT_TRUE(VerifyTextBuffers(parser_, kInputBlockInfo, input_block_count,
@@ -505,7 +658,7 @@ TEST_F(WebMClusterParserTest, ParseEncryptedBlock) {
                                       kNoTimestamp(),
                                       kVideoTrackNum,
                                       kNoTimestamp(),
-                                      WebMTracksParser::TextTracks(),
+                                      TextTracks(),
                                       std::set<int64>(),
                                       std::string(),
                                       "video_key_id",
@@ -526,7 +679,7 @@ TEST_F(WebMClusterParserTest, ParseBadEncryptedBlock) {
                                       kNoTimestamp(),
                                       kVideoTrackNum,
                                       kNoTimestamp(),
-                                      WebMTracksParser::TextTracks(),
+                                      TextTracks(),
                                       std::set<int64>(),
                                       std::string(),
                                       "video_key_id",
@@ -552,4 +705,253 @@ TEST_F(WebMClusterParserTest, ParseInvalidUnknownButActuallyZeroSizedCluster) {
   EXPECT_EQ(-1, parser_->Parse(kBuffer, sizeof(kBuffer)));
 }
 
+TEST_F(WebMClusterParserTest, ParseInvalidTextBlockGroupWithoutDuration) {
+  // Text track frames must have explicitly specified BlockGroup BlockDurations.
+  TextTracks text_tracks;
+
+  text_tracks.insert(std::make_pair(TextTracks::key_type(kTextTrackNum),
+                                    TextTrackConfig(kTextSubtitles, "", "",
+                                                    "")));
+
+  parser_.reset(new WebMClusterParser(kTimecodeScale,
+                                      kAudioTrackNum,
+                                      kNoTimestamp(),
+                                      kVideoTrackNum,
+                                      kNoTimestamp(),
+                                      text_tracks,
+                                      std::set<int64>(),
+                                      std::string(),
+                                      std::string(),
+                                      LogCB()));
+
+  const BlockInfo kBlockInfo[] = {
+    { kTextTrackNum,  33, -42, false },
+  };
+  int block_count = arraysize(kBlockInfo);
+  scoped_ptr<Cluster> cluster(CreateCluster(0, kBlockInfo, block_count));
+  int result = parser_->Parse(cluster->data(), cluster->size());
+  EXPECT_LT(result, 0);
+}
+
+TEST_F(WebMClusterParserTest, ParseWithDefaultDurationsSimpleBlocks) {
+  InSequence s;
+  ResetParserToHaveDefaultDurations();
+
+  EXPECT_LT(kTestAudioFrameDefaultDurationInMs, 23);
+  EXPECT_LT(kTestVideoFrameDefaultDurationInMs, 33);
+
+  const BlockInfo kBlockInfo[] = {
+    { kAudioTrackNum, 0, kTestAudioFrameDefaultDurationInMs, true },
+    { kAudioTrackNum, 23, kTestAudioFrameDefaultDurationInMs, true },
+    { kVideoTrackNum, 33, kTestVideoFrameDefaultDurationInMs, true },
+    { kAudioTrackNum, 46, kTestAudioFrameDefaultDurationInMs, true },
+    { kVideoTrackNum, 67, kTestVideoFrameDefaultDurationInMs, true },
+    { kAudioTrackNum, 69, kTestAudioFrameDefaultDurationInMs, true },
+    { kVideoTrackNum, 100, kTestVideoFrameDefaultDurationInMs, true },
+  };
+
+  int block_count = arraysize(kBlockInfo);
+  scoped_ptr<Cluster> cluster(CreateCluster(0, kBlockInfo, block_count));
+
+  // Send slightly less than the full cluster so all but the last block is
+  // parsed. Though all the blocks are simple blocks, none should be held aside
+  // for duration estimation prior to end of cluster detection because all the
+  // tracks have DefaultDurations.
+  int result = parser_->Parse(cluster->data(), cluster->size() - 1);
+  EXPECT_GT(result, 0);
+  EXPECT_LT(result, cluster->size());
+  ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo, block_count - 1));
+
+  parser_->Reset();
+
+  // Now parse a whole cluster to verify that all the blocks will get parsed.
+  result = parser_->Parse(cluster->data(), cluster->size());
+  EXPECT_EQ(cluster->size(), result);
+  ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo, block_count));
+}
+
+TEST_F(WebMClusterParserTest, ParseWithoutAnyDurationsSimpleBlocks) {
+  InSequence s;
+
+  // Absent DefaultDuration information, SimpleBlock durations are derived from
+  // inter-buffer track timestamp delta if within the cluster, and are estimated
+  // as the lowest non-zero duration seen so far if the last buffer in the track
+  // in the cluster (independently for each track in the cluster).
+  const BlockInfo kBlockInfo1[] = {
+    { kAudioTrackNum, 0, 23, true },
+    { kAudioTrackNum, 23, 22, true },
+    { kVideoTrackNum, 33, 33, true },
+    { kAudioTrackNum, 45, 23, true },
+    { kVideoTrackNum, 66, 34, true },
+    { kAudioTrackNum, 68, 22, true },  // Estimated from minimum audio dur
+    { kVideoTrackNum, 100, 33, true },  // Estimated from minimum video dur
+  };
+
+  int block_count1 = arraysize(kBlockInfo1);
+  scoped_ptr<Cluster> cluster1(CreateCluster(0, kBlockInfo1, block_count1));
+
+  // Send slightly less than the first full cluster so all but the last video
+  // block is parsed. Verify the last fully parsed audio and video buffer are
+  // both missing from the result (parser should hold them aside for duration
+  // estimation prior to end of cluster detection in the absence of
+  // DefaultDurations.)
+  int result = parser_->Parse(cluster1->data(), cluster1->size() - 1);
+  EXPECT_GT(result, 0);
+  EXPECT_LT(result, cluster1->size());
+  ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo1, block_count1 - 3));
+  EXPECT_EQ(3UL, parser_->GetAudioBuffers().size());
+  EXPECT_EQ(1UL, parser_->GetVideoBuffers().size());
+
+  parser_->Reset();
+
+  // Now parse the full first cluster and verify all the blocks are parsed.
+  result = parser_->Parse(cluster1->data(), cluster1->size());
+  EXPECT_EQ(cluster1->size(), result);
+  ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo1, block_count1));
+
+  // Verify that the estimated frame duration is tracked across clusters for
+  // each track.
+  const BlockInfo kBlockInfo2[] = {
+    { kAudioTrackNum, 200, 22, true },  // Estimate carries over across clusters
+    { kVideoTrackNum, 201, 33, true },  // Estimate carries over across clusters
+  };
+
+  int block_count2 = arraysize(kBlockInfo2);
+  scoped_ptr<Cluster> cluster2(CreateCluster(0, kBlockInfo2, block_count2));
+  result = parser_->Parse(cluster2->data(), cluster2->size());
+  EXPECT_EQ(cluster2->size(), result);
+  ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo2, block_count2));
+}
+
+TEST_F(WebMClusterParserTest, ParseWithoutAnyDurationsBlockGroups) {
+  InSequence s;
+
+  // Absent DefaultDuration and BlockDuration information, BlockGroup block
+  // durations are derived from inter-buffer track timestamp delta if within the
+  // cluster, and are estimated as the lowest non-zero duration seen so far if
+  // the last buffer in the track in the cluster (independently for each track
+  // in the cluster).
+    const BlockInfo kBlockInfo1[] = {
+    { kAudioTrackNum, 0, -23, false },
+    { kAudioTrackNum, 23, -22, false },
+    { kVideoTrackNum, 33, -33, false },
+    { kAudioTrackNum, 45, -23, false },
+    { kVideoTrackNum, 66, -34, false },
+    { kAudioTrackNum, 68, -22, false },  // Estimated from minimum audio dur
+    { kVideoTrackNum, 100, -33, false },  // Estimated from minimum video dur
+  };
+
+  int block_count1 = arraysize(kBlockInfo1);
+  scoped_ptr<Cluster> cluster1(CreateCluster(0, kBlockInfo1, block_count1));
+
+  // Send slightly less than the first full cluster so all but the last video
+  // block is parsed. Verify the last fully parsed audio and video buffer are
+  // both missing from the result (parser should hold them aside for duration
+  // estimation prior to end of cluster detection in the absence of
+  // DefaultDurations.)
+  int result = parser_->Parse(cluster1->data(), cluster1->size() - 1);
+  EXPECT_GT(result, 0);
+  EXPECT_LT(result, cluster1->size());
+  ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo1, block_count1 - 3));
+  EXPECT_EQ(3UL, parser_->GetAudioBuffers().size());
+  EXPECT_EQ(1UL, parser_->GetVideoBuffers().size());
+
+  parser_->Reset();
+
+  // Now parse the full first cluster and verify all the blocks are parsed.
+  result = parser_->Parse(cluster1->data(), cluster1->size());
+  EXPECT_EQ(cluster1->size(), result);
+  ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo1, block_count1));
+
+  // Verify that the estimated frame duration is tracked across clusters for
+  // each track.
+  const BlockInfo kBlockInfo2[] = {
+    { kAudioTrackNum, 200, -22, false },
+    { kVideoTrackNum, 201, -33, false },
+  };
+
+  int block_count2 = arraysize(kBlockInfo2);
+  scoped_ptr<Cluster> cluster2(CreateCluster(0, kBlockInfo2, block_count2));
+  result = parser_->Parse(cluster2->data(), cluster2->size());
+  EXPECT_EQ(cluster2->size(), result);
+  ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo2, block_count2));
+}
+
+// TODO(wolenetz): Is parser behavior correct? See http://crbug.com/363433.
+TEST_F(WebMClusterParserTest,
+       ParseWithDefaultDurationsBlockGroupsWithoutDurations) {
+  InSequence s;
+  ResetParserToHaveDefaultDurations();
+
+  EXPECT_LT(kTestAudioFrameDefaultDurationInMs, 23);
+  EXPECT_LT(kTestVideoFrameDefaultDurationInMs, 33);
+
+  const BlockInfo kBlockInfo[] = {
+    { kAudioTrackNum, 0, -kTestAudioFrameDefaultDurationInMs, false },
+    { kAudioTrackNum, 23, -kTestAudioFrameDefaultDurationInMs, false },
+    { kVideoTrackNum, 33, -kTestVideoFrameDefaultDurationInMs, false },
+    { kAudioTrackNum, 46, -kTestAudioFrameDefaultDurationInMs, false },
+    { kVideoTrackNum, 67, -kTestVideoFrameDefaultDurationInMs, false },
+    { kAudioTrackNum, 69, -kTestAudioFrameDefaultDurationInMs, false },
+    { kVideoTrackNum, 100, -kTestVideoFrameDefaultDurationInMs, false },
+  };
+
+  int block_count = arraysize(kBlockInfo);
+  scoped_ptr<Cluster> cluster(CreateCluster(0, kBlockInfo, block_count));
+
+  // Send slightly less than the full cluster so all but the last block is
+  // parsed. None should be held aside for duration estimation prior to end of
+  // cluster detection because all the tracks have DefaultDurations.
+  int result = parser_->Parse(cluster->data(), cluster->size() - 1);
+  EXPECT_GT(result, 0);
+  EXPECT_LT(result, cluster->size());
+  ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo, block_count - 1));
+
+  parser_->Reset();
+
+  // Now parse a whole cluster to verify that all the blocks will get parsed.
+  result = parser_->Parse(cluster->data(), cluster->size());
+  EXPECT_EQ(cluster->size(), result);
+  ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo, block_count));
+}
+
+TEST_F(WebMClusterParserTest,
+       ParseDegenerateClusterYieldsHardcodedEstimatedDurations) {
+  const BlockInfo kBlockInfo[] = {
+    {
+      kAudioTrackNum,
+      0,
+      WebMClusterParser::kDefaultAudioBufferDurationInMs,
+      true
+    }, {
+      kVideoTrackNum,
+      0,
+      WebMClusterParser::kDefaultVideoBufferDurationInMs,
+      true
+    },
+  };
+
+  int block_count = arraysize(kBlockInfo);
+  scoped_ptr<Cluster> cluster(CreateCluster(0, kBlockInfo, block_count));
+  int result = parser_->Parse(cluster->data(), cluster->size());
+  EXPECT_EQ(cluster->size(), result);
+  ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo, block_count));
+}
+
+TEST_F(WebMClusterParserTest,
+       ParseDegenerateClusterWithDefaultDurationsYieldsDefaultDurations) {
+  ResetParserToHaveDefaultDurations();
+
+  const BlockInfo kBlockInfo[] = {
+    { kAudioTrackNum, 0, kTestAudioFrameDefaultDurationInMs, true },
+    { kVideoTrackNum, 0, kTestVideoFrameDefaultDurationInMs, true },
+  };
+
+  int block_count = arraysize(kBlockInfo);
+  scoped_ptr<Cluster> cluster(CreateCluster(0, kBlockInfo, block_count));
+  int result = parser_->Parse(cluster->data(), cluster->size());
+  EXPECT_EQ(cluster->size(), result);
+  ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo, block_count));
+}
+
 }  // namespace media