Index: media/formats/webm/webm_cluster_parser.cc |
diff --git a/media/formats/webm/webm_cluster_parser.cc b/media/formats/webm/webm_cluster_parser.cc |
index 6784776a0e029e0c21d168391532087b8a43fa6b..4e82c81272395e0ef77d6d912a93237557fdf728 100644 |
--- a/media/formats/webm/webm_cluster_parser.cc |
+++ b/media/formats/webm/webm_cluster_parser.cc |
@@ -17,6 +17,11 @@ |
namespace media { |
+const uint16 WebMClusterParser::kOpusFrameDurationsMu[] = { |
+ 10000, 20000, 40000, 60000, 10000, 20000, 40000, 60000, 10000, 20000, 40000, |
+ 60000, 10000, 20000, 10000, 20000, 2500, 5000, 10000, 20000, 2500, 5000, |
+ 10000, 20000, 2500, 5000, 10000, 20000, 2500, 5000, 10000, 20000}; |
+ |
WebMClusterParser::WebMClusterParser( |
int64 timecode_scale, |
int audio_track_num, |
@@ -27,11 +32,13 @@ WebMClusterParser::WebMClusterParser( |
const std::set<int64>& ignored_tracks, |
const std::string& audio_encryption_key_id, |
const std::string& video_encryption_key_id, |
+ const AudioCodec audio_codec, |
const LogCB& log_cb) |
: timecode_multiplier_(timecode_scale / 1000.0), |
ignored_tracks_(ignored_tracks), |
audio_encryption_key_id_(audio_encryption_key_id), |
video_encryption_key_id_(video_encryption_key_id), |
+ audio_codec_(audio_codec), |
parser_(kWebMIdCluster, this), |
last_block_timecode_(-1), |
block_data_size_(-1), |
@@ -140,6 +147,67 @@ WebMClusterParser::GetTextBuffers() { |
return text_buffers_map_; |
} |
+base::TimeDelta WebMClusterParser::TryGetEncodedAudioDuration(const uint8* data, |
+ int size) { |
+ if (audio_codec_ == kCodecOpus) { |
+ return ReadOpusDuration(data, size); |
+ } |
+ // TODO(wolenetz/chcunningham): Implement duration reading for Vorbis. |
wolenetz
2015/02/03 22:47:02
nits: let's start a new or reference an existing c
chcunningham
2015/02/05 02:48:21
Done.
|
+ |
+ return kNoTimestamp(); |
+} |
+ |
+base::TimeDelta WebMClusterParser::ReadOpusDuration(const uint8* data, |
+ int size) { |
+ // Masks for Opus TOC and Frame Count bytes. See http://goo.gl/2RmoxA |
wolenetz
2015/02/03 22:47:01
ditto full URL... sorry
chcunningham
2015/02/05 02:48:21
Done.
|
+ static const uint8 kTocConfigMask = 0xf8; |
wolenetz
2015/02/03 22:47:01
ditto: _t
chcunningham
2015/02/05 02:48:21
Done.
|
+ static const uint8 kTocFrameCountCodeMask = 0x03; |
+ static const uint8 kFrameCountMask = 0x3f; |
+ static const int kFrameCountMin = 1; |
+ static const int kFrameCountMax = 48; |
+ |
+ if (size < 1) { |
+ MEDIA_LOG(log_cb_) << "Invalid zero-byte Opus packet."; |
+ return kNoTimestamp(); |
+ } |
+ |
+ // Frame count type described by last 2 bits of Opus TOC byte. |
+ int frame_count_type = data[0] & kTocFrameCountCodeMask; |
+ |
+ int frame_count = 0; |
+ switch (frame_count_type) { |
+ case 0: |
+ frame_count = 1; |
+ break; |
+ case 1: |
+ case 2: |
+ frame_count = 2; |
+ break; |
+ case 3: |
+ // Type 3 indicates an arbitrary frame count described in the next byte. |
+ if (size < 2) { |
+ MEDIA_LOG(log_cb_) << "Second byte missing from 'Code 3' Opus packet."; |
+ return kNoTimestamp(); |
+ } |
+ frame_count = data[1] & kFrameCountMask; |
+ DCHECK_GE(frame_count, 1); |
+ if (frame_count < kFrameCountMin || frame_count > kFrameCountMax) { |
+ MEDIA_LOG(log_cb_) << "Illegal Opus packet frame_count: " << frame_count |
wolenetz
2015/02/03 22:47:01
nit: comment that we are explicitly allowing these
wolenetz
2015/02/03 22:47:01
nit: s/frame_count:/frame count:/
chcunningham
2015/02/05 02:48:21
Done.
chcunningham
2015/02/05 02:48:21
Done.
|
+ << " Should be in range [" << kFrameCountMin << ", " |
+ << kFrameCountMax << "]"; |
+ } |
+ break; |
+ default: |
+ MEDIA_LOG(log_cb_) << "Unexpected Opus frame count type: " |
+ << frame_count_type; |
+ return kNoTimestamp(); |
+ } |
+ |
+ int opusConfig = (data[0] & kTocConfigMask) >> 3; |
+ return base::TimeDelta::FromMicroseconds(kOpusFrameDurationsMu[opusConfig] * |
wolenetz
2015/02/03 22:47:01
nit: kFrameCountMax is lower for longer frame size
chcunningham
2015/02/05 02:48:21
Done.
|
+ frame_count); |
+} |
+ |
WebMParserClient* WebMClusterParser::OnListStart(int id) { |
if (id == kWebMIdCluster) { |
cluster_timecode_ = -1; |
@@ -324,9 +392,13 @@ bool WebMClusterParser::OnBlock(bool is_simple_block, int track_num, |
Track* track = NULL; |
StreamParserBuffer::Type buffer_type = DemuxerStream::AUDIO; |
std::string encryption_key_id; |
+ base::TimeDelta encoded_duration = kNoTimestamp(); |
if (track_num == audio_.track_num()) { |
track = &audio_; |
encryption_key_id = audio_encryption_key_id_; |
+ if (encryption_key_id.empty()) { |
+ encoded_duration = TryGetEncodedAudioDuration(data, size); |
+ } |
} else if (track_num == video_.track_num()) { |
track = &video_; |
encryption_key_id = video_encryption_key_id_; |
@@ -407,9 +479,42 @@ bool WebMClusterParser::OnBlock(bool is_simple_block, int track_num, |
if (cluster_start_time_ == kNoTimestamp()) |
cluster_start_time_ = timestamp; |
+ base::TimeDelta block_duration_time_delta = kNoTimestamp(); |
if (block_duration >= 0) { |
- buffer->set_duration(base::TimeDelta::FromMicroseconds( |
- block_duration * timecode_multiplier_)); |
+ block_duration_time_delta = base::TimeDelta::FromMicroseconds( |
+ block_duration * timecode_multiplier_); |
+ } |
+ |
+ // Prefer encoded duration when available. This layering violatiaon is a |
wolenetz
2015/02/03 22:47:01
nit: s/when available/over BlockGroup->BlockDurati
wolenetz
2015/02/03 22:47:02
nit: s/violatiaon/violation/
chcunningham
2015/02/05 02:48:21
Done. Oh, and I found a sublime text spell checker
chcunningham
2015/02/05 02:48:22
Done.
wolenetz
2015/02/05 23:04:59
Please share link offline with me :)
|
+ // workaround for http://crbug.com/396634, decreasing the likelihood of |
+ // fallback to rough estimation techniques for Blocks that lack a |
+ // BlockDuration at the end of a cluster. Cross cluster durations are not |
+ // feasabile given flexibility of cluster ordering and MSE APIs. Duration |
wolenetz
2015/02/03 22:47:01
nit: s/feasabile/feasible/
chcunningham
2015/02/05 02:48:21
Done.
|
+ // estimation may still apply in cases of encryption and unsupported codecs. |
wolenetz
2015/02/03 22:47:01
nit: s/unsupported codecs/codecs for which we do n
chcunningham
2015/02/05 02:48:21
Done.
|
+ // Estimates are applied at the end of parsing once the whole cluster is |
+ // parsed. See ApplyDurationEstimateIfNeeded for more on estimation. |
wolenetz
2015/02/03 22:47:01
nit: add ()
chcunningham
2015/02/05 02:48:21
Done.
|
+ if (encoded_duration != kNoTimestamp()) { |
+ DCHECK(encoded_duration != kInfiniteDuration()); |
+ DCHECK(encoded_duration > base::TimeDelta()); |
+ buffer->set_duration(encoded_duration); |
+ |
+ DVLOG(3) << __FUNCTION__ << " : " |
+ << "Using encoded duartion " << encoded_duration.InSecondsF(); |
wolenetz
2015/02/03 22:47:01
nit: s/duartion/duration/
chcunningham
2015/02/05 02:48:21
Done.
|
+ |
+ if (block_duration_time_delta != kNoTimestamp()) { |
+ base::TimeDelta duration_difference = |
+ block_duration_time_delta - encoded_duration; |
+ |
+ const auto kWarnDurationDiff = base::TimeDelta::FromMilliseconds(10); |
wolenetz
2015/02/03 22:47:01
Hmmm. 10ms seems a little arbitrary (and big).
Con
chcunningham
2015/02/05 02:48:21
Done.
|
+ if (duration_difference.magnitude() > kWarnDurationDiff) { |
+ MEDIA_LOG(log_cb_) << "BlockDuration " |
+ << "(" << block_duration_time_delta << ") " |
wolenetz
2015/02/03 22:47:01
nit: Units. Is this in secondsF? (use .InSecondsF(
chcunningham
2015/02/05 02:48:21
It is in secondsF. I can be explicit, but I think
wolenetz
2015/02/05 23:04:59
Oh. I didn't realize that. And it comes with nice
|
+ << "differs signifcantly from encoded duration " |
wolenetz
2015/02/03 22:47:01
nit:s/signifcantly/significantly/
chcunningham
2015/02/05 02:48:21
Done.
|
+ << "(" << encoded_duration << ")."; |
wolenetz
2015/02/03 22:47:01
nit ditto: InSecondsF()
chcunningham
2015/02/05 02:48:21
See other reply
wolenetz
2015/02/05 23:04:59
Acknowledged.
|
+ } |
+ } |
+ } else if (block_duration_time_delta != kNoTimestamp()) { |
+ buffer->set_duration(block_duration_time_delta); |
} else { |
DCHECK_NE(buffer_type, DemuxerStream::TEXT); |
buffer->set_duration(track->default_duration()); |