diff options
author | acolwell@chromium.org <acolwell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-12-21 18:33:24 +0000 |
---|---|---|
committer | acolwell@chromium.org <acolwell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-12-21 18:33:24 +0000 |
commit | 3123ed71dc2fa410b25048f4d2033cbc11727909 (patch) | |
tree | 77108ce719916dcaeb24997c727568b39d345e58 /media | |
parent | 8f12b197f302f6bc97d07ec93741df0e67c98238 (diff) | |
download | chromium_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.cc | 34 | ||||
-rw-r--r-- | media/webm/cluster_builder.cc | 4 | ||||
-rw-r--r-- | media/webm/webm_cluster_parser.cc | 53 | ||||
-rw-r--r-- | media/webm/webm_cluster_parser.h | 16 |
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. |