summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authoracolwell@chromium.org <acolwell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-12-21 18:33:24 +0000
committeracolwell@chromium.org <acolwell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-12-21 18:33:24 +0000
commit3123ed71dc2fa410b25048f4d2033cbc11727909 (patch)
tree77108ce719916dcaeb24997c727568b39d345e58 /media
parent8f12b197f302f6bc97d07ec93741df0e67c98238 (diff)
downloadchromium_src-3123ed71dc2fa410b25048f4d2033cbc11727909.zip
chromium_src-3123ed71dc2fa410b25048f4d2033cbc11727909.tar.gz
chromium_src-3123ed71dc2fa410b25048f4d2033cbc11727909.tar.bz2
Update WebMClusterParser so it can detect keyframes in BlockGroups
BUG=166990 TEST=Covered by existing ChunkDemuxer tests. Review URL: https://chromiumcodereview.appspot.com/11618050 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@174415 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r--media/filters/chunk_demuxer_unittest.cc34
-rw-r--r--media/webm/cluster_builder.cc4
-rw-r--r--media/webm/webm_cluster_parser.cc53
-rw-r--r--media/webm/webm_cluster_parser.h16
4 files changed, 86 insertions, 21 deletions
diff --git a/media/filters/chunk_demuxer_unittest.cc b/media/filters/chunk_demuxer_unittest.cc
index fc83690..32e068c 100644
--- a/media/filters/chunk_demuxer_unittest.cc
+++ b/media/filters/chunk_demuxer_unittest.cc
@@ -30,6 +30,14 @@ static const uint8 kTracksHeader[] = {
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // tracks(size = 0)
};
+// WebM Block bytes that represent a VP8 keyframe.
+static const uint8 kVP8Keyframe[] = {
+ 0x010, 0x00, 0x00, 0x9d, 0x01, 0x2a, 0x00, 0x10, 0x00, 0x10, 0x00
+};
+
+// WebM Block bytes that represent a VP8 interframe.
+static const uint8 kVP8Interframe[] = { 0x11, 0x00, 0x00 };
+
static const int kTracksHeaderSize = sizeof(kTracksHeader);
static const int kTracksSizeOffset = 4;
@@ -484,6 +492,15 @@ class ChunkDemuxerTest : public testing::Test {
return GenerateCluster(timecode, timecode, block_count);
}
+ void AddVideoBlockGroup(ClusterBuilder* cb, int track_num, int64 timecode,
+ int duration, int flags) {
+ const uint8* data =
+ (flags & kWebMFlagKeyframe) != 0 ? kVP8Keyframe : kVP8Interframe;
+ int size = (flags & kWebMFlagKeyframe) != 0 ? sizeof(kVP8Keyframe) :
+ sizeof(kVP8Interframe);
+ cb->AddBlockGroup(track_num, timecode, duration, flags, data, size);
+ }
+
scoped_ptr<Cluster> GenerateCluster(int first_audio_timecode,
int first_video_timecode,
int block_count) {
@@ -527,11 +544,11 @@ class ChunkDemuxerTest : public testing::Test {
if (audio_timecode <= video_timecode) {
cb.AddBlockGroup(kAudioTrackNum, audio_timecode, kAudioBlockDuration,
kWebMFlagKeyframe, data.get(), size);
- cb.AddBlockGroup(kVideoTrackNum, video_timecode, kVideoBlockDuration,
- video_flag, data.get(), size);
+ AddVideoBlockGroup(&cb, kVideoTrackNum, video_timecode,
+ kVideoBlockDuration, video_flag);
} else {
- cb.AddBlockGroup(kVideoTrackNum, video_timecode, kVideoBlockDuration,
- video_flag, data.get(), size);
+ AddVideoBlockGroup(&cb, kVideoTrackNum, video_timecode,
+ kVideoBlockDuration, video_flag);
cb.AddBlockGroup(kAudioTrackNum, audio_timecode, kAudioBlockDuration,
kWebMFlagKeyframe, data.get(), size);
}
@@ -560,8 +577,13 @@ class ChunkDemuxerTest : public testing::Test {
// Make the last block a BlockGroup so that it doesn't get delayed by the
// block duration calculation logic.
- cb.AddBlockGroup(track_number, timecode, block_duration,
- kWebMFlagKeyframe, data.get(), size);
+ if (track_number == kVideoTrackNum) {
+ AddVideoBlockGroup(&cb, track_number, timecode, block_duration,
+ kWebMFlagKeyframe);
+ } else {
+ cb.AddBlockGroup(track_number, timecode, block_duration,
+ kWebMFlagKeyframe, data.get(), size);
+ }
return cb.Finish();
}
diff --git a/media/webm/cluster_builder.cc b/media/webm/cluster_builder.cc
index 1c377a6..b06da84 100644
--- a/media/webm/cluster_builder.cc
+++ b/media/webm/cluster_builder.cc
@@ -100,6 +100,10 @@ void ClusterBuilder::AddBlockGroup(int track_num, int64 timecode, int duration,
UpdateUInt64(block_group_offset + kBlockGroupBlockSizeOffset, block_size);
buf += sizeof(kBlockGroupHeader);
+ // Make sure the 4 most-significant bits are 0.
+ // http://www.matroska.org/technical/specs/index.html#block_structure
+ flags &= 0x0f;
+
WriteBlock(buf, track_num, timecode, flags, data, size);
bytes_used_ += bytes_needed;
diff --git a/media/webm/webm_cluster_parser.cc b/media/webm/webm_cluster_parser.cc
index 4027f11..32ace0e 100644
--- a/media/webm/webm_cluster_parser.cc
+++ b/media/webm/webm_cluster_parser.cc
@@ -38,8 +38,8 @@ WebMClusterParser::WebMClusterParser(
cluster_timecode_(-1),
cluster_start_time_(kNoTimestamp()),
cluster_ended_(false),
- audio_(audio_track_num),
- video_(video_track_num),
+ audio_(audio_track_num, false),
+ video_(video_track_num, true),
log_cb_(log_cb) {
}
@@ -111,7 +111,7 @@ bool WebMClusterParser::OnListEnd(int id) {
return false;
}
- bool result = ParseBlock(block_data_.get(), block_data_size_,
+ bool result = ParseBlock(false, block_data_.get(), block_data_size_,
block_duration_);
block_data_.reset();
block_data_size_ = -1;
@@ -134,7 +134,8 @@ bool WebMClusterParser::OnUInt(int id, int64 val) {
return true;
}
-bool WebMClusterParser::ParseBlock(const uint8* buf, int size, int duration) {
+bool WebMClusterParser::ParseBlock(bool is_simple_block, const uint8* buf,
+ int size, int duration) {
if (size < 4)
return false;
@@ -161,12 +162,13 @@ bool WebMClusterParser::ParseBlock(const uint8* buf, int size, int duration) {
const uint8* frame_data = buf + 4;
int frame_size = size - (frame_data - buf);
- return OnBlock(track_num, timecode, duration, flags, frame_data, frame_size);
+ return OnBlock(is_simple_block, track_num, timecode, duration, flags,
+ frame_data, frame_size);
}
bool WebMClusterParser::OnBinary(int id, const uint8* data, int size) {
if (id == kWebMIdSimpleBlock)
- return ParseBlock(data, size, -1);
+ return ParseBlock(true, data, size, -1);
if (id != kWebMIdBlock)
return true;
@@ -182,7 +184,8 @@ bool WebMClusterParser::OnBinary(int id, const uint8* data, int size) {
return true;
}
-bool WebMClusterParser::OnBlock(int track_num, int timecode,
+bool WebMClusterParser::OnBlock(bool is_simple_block, int track_num,
+ int timecode,
int block_duration,
int flags,
const uint8* data, int size) {
@@ -222,9 +225,13 @@ bool WebMClusterParser::OnBlock(int track_num, int timecode,
base::TimeDelta timestamp = base::TimeDelta::FromMicroseconds(
(cluster_timecode_ + timecode) * timecode_multiplier_);
- // The first bit of the flags is set when the block contains only keyframes.
+ // The first bit of the flags is set when a SimpleBlock contains only
+ // keyframes. If this is a Block, then inspection of the payload is
+ // necessary to determine whether it contains a keyframe or not.
// http://www.matroska.org/technical/specs/index.html
- bool is_keyframe = (flags & 0x80) != 0;
+ bool is_keyframe =
+ is_simple_block ? (flags & 0x80) != 0 : track->IsKeyframe(data, size);
+
scoped_refptr<StreamParserBuffer> buffer =
StreamParserBuffer::CopyFrom(data, size, is_keyframe);
@@ -279,8 +286,9 @@ bool WebMClusterParser::OnBlock(int track_num, int timecode,
return track->AddBuffer(buffer);
}
-WebMClusterParser::Track::Track(int track_num)
- : track_num_(track_num) {
+WebMClusterParser::Track::Track(int track_num, bool is_video)
+ : track_num_(track_num),
+ is_video_(is_video) {
}
WebMClusterParser::Track::~Track() {}
@@ -301,4 +309,27 @@ void WebMClusterParser::Track::Reset() {
buffers_.clear();
}
+bool WebMClusterParser::Track::IsKeyframe(const uint8* data, int size) const {
+ // For now, assume that all blocks are keyframes for datatypes other than
+ // video. This is a valid assumption for Vorbis, WebVTT, & Opus.
+ if (!is_video_)
+ return true;
+
+ // Make sure the block is big enough for the minimal keyframe header size.
+ if (size < 7)
+ return false;
+
+ // The LSb of the first byte must be a 0 for a keyframe.
+ // http://tools.ietf.org/html/rfc6386 Section 19.1
+ if ((data[0] & 0x01) != 0)
+ return false;
+
+ // Verify VP8 keyframe startcode.
+ // http://tools.ietf.org/html/rfc6386 Section 19.1
+ if (data[3] != 0x9d || data[4] != 0x01 || data[5] != 0x2a)
+ return false;
+
+ return true;
+}
+
} // namespace media
diff --git a/media/webm/webm_cluster_parser.h b/media/webm/webm_cluster_parser.h
index f31a0cb..c387984 100644
--- a/media/webm/webm_cluster_parser.h
+++ b/media/webm/webm_cluster_parser.h
@@ -49,7 +49,7 @@ class MEDIA_EXPORT WebMClusterParser : public WebMParserClient {
// Helper class that manages per-track state.
class Track {
public:
- explicit Track(int track_num);
+ Track(int track_num, bool is_video);
~Track();
int track_num() const { return track_num_; }
@@ -60,9 +60,16 @@ class MEDIA_EXPORT WebMClusterParser : public WebMParserClient {
// Clears all buffer state.
void Reset();
+ // Helper function used to inspect block data to determine if the
+ // block is a keyframe.
+ // |data| contains the bytes in the block.
+ // |size| indicates the number of bytes in |data|.
+ bool IsKeyframe(const uint8* data, int size) const;
+
private:
int track_num_;
BufferQueue buffers_;
+ bool is_video_;
};
// WebMParserClient methods.
@@ -71,9 +78,10 @@ class MEDIA_EXPORT WebMClusterParser : public WebMParserClient {
virtual bool OnUInt(int id, int64 val) OVERRIDE;
virtual bool OnBinary(int id, const uint8* data, int size) OVERRIDE;
- bool ParseBlock(const uint8* buf, int size, int duration);
- bool OnBlock(int track_num, int timecode, int duration, int flags,
- const uint8* data, int size);
+ bool ParseBlock(bool is_simple_block, const uint8* buf, int size,
+ int duration);
+ bool OnBlock(bool is_simple_block, int track_num, int timecode, int duration,
+ int flags, const uint8* data, int size);
double timecode_multiplier_; // Multiplier used to convert timecodes into
// microseconds.