Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "media/formats/webm/webm_cluster_parser.h" | 5 #include "media/formats/webm/webm_cluster_parser.h" |
| 6 | 6 |
| 7 #include <vector> | 7 #include <vector> |
| 8 | 8 |
| 9 #include "base/logging.h" | 9 #include "base/logging.h" |
| 10 #include "base/sys_byteorder.h" | 10 #include "base/sys_byteorder.h" |
| 11 #include "media/base/buffers.h" | 11 #include "media/base/buffers.h" |
| 12 #include "media/base/decrypt_config.h" | 12 #include "media/base/decrypt_config.h" |
| 13 #include "media/filters/webvtt_util.h" | 13 #include "media/filters/webvtt_util.h" |
| 14 #include "media/formats/webm/webm_constants.h" | 14 #include "media/formats/webm/webm_constants.h" |
| 15 #include "media/formats/webm/webm_crypto_helpers.h" | 15 #include "media/formats/webm/webm_crypto_helpers.h" |
| 16 #include "media/formats/webm/webm_webvtt_parser.h" | 16 #include "media/formats/webm/webm_webvtt_parser.h" |
| 17 | 17 |
| 18 namespace media { | 18 namespace media { |
| 19 | 19 |
| 20 const uint16 WebMClusterParser::kOpusFrameDurationsMu[] = { | |
| 21 10000, 20000, 40000, 60000, 10000, 20000, 40000, 60000, 10000, 20000, 40000, | |
| 22 60000, 10000, 20000, 10000, 20000, 2500, 5000, 10000, 20000, 2500, 5000, | |
| 23 10000, 20000, 2500, 5000, 10000, 20000, 2500, 5000, 10000, 20000}; | |
| 24 | |
| 20 WebMClusterParser::WebMClusterParser( | 25 WebMClusterParser::WebMClusterParser( |
| 21 int64 timecode_scale, | 26 int64 timecode_scale, |
| 22 int audio_track_num, | 27 int audio_track_num, |
| 23 base::TimeDelta audio_default_duration, | 28 base::TimeDelta audio_default_duration, |
| 24 int video_track_num, | 29 int video_track_num, |
| 25 base::TimeDelta video_default_duration, | 30 base::TimeDelta video_default_duration, |
| 26 const WebMTracksParser::TextTracks& text_tracks, | 31 const WebMTracksParser::TextTracks& text_tracks, |
| 27 const std::set<int64>& ignored_tracks, | 32 const std::set<int64>& ignored_tracks, |
| 28 const std::string& audio_encryption_key_id, | 33 const std::string& audio_encryption_key_id, |
| 29 const std::string& video_encryption_key_id, | 34 const std::string& video_encryption_key_id, |
| 35 const AudioCodec audio_codec, | |
| 30 const LogCB& log_cb) | 36 const LogCB& log_cb) |
| 31 : timecode_multiplier_(timecode_scale / 1000.0), | 37 : timecode_multiplier_(timecode_scale / 1000.0), |
| 32 ignored_tracks_(ignored_tracks), | 38 ignored_tracks_(ignored_tracks), |
| 33 audio_encryption_key_id_(audio_encryption_key_id), | 39 audio_encryption_key_id_(audio_encryption_key_id), |
| 34 video_encryption_key_id_(video_encryption_key_id), | 40 video_encryption_key_id_(video_encryption_key_id), |
| 41 audio_codec_(audio_codec), | |
| 35 parser_(kWebMIdCluster, this), | 42 parser_(kWebMIdCluster, this), |
| 36 last_block_timecode_(-1), | 43 last_block_timecode_(-1), |
| 37 block_data_size_(-1), | 44 block_data_size_(-1), |
| 38 block_duration_(-1), | 45 block_duration_(-1), |
| 39 block_add_id_(-1), | 46 block_add_id_(-1), |
| 40 block_additional_data_size_(0), | 47 block_additional_data_size_(0), |
| 41 discard_padding_(-1), | 48 discard_padding_(-1), |
| 42 cluster_timecode_(-1), | 49 cluster_timecode_(-1), |
| 43 cluster_start_time_(kNoTimestamp()), | 50 cluster_start_time_(kNoTimestamp()), |
| 44 cluster_ended_(false), | 51 cluster_ended_(false), |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 133 itr != text_track_map_.end(); | 140 itr != text_track_map_.end(); |
| 134 ++itr) { | 141 ++itr) { |
| 135 const BufferQueue& text_buffers = itr->second.ready_buffers(); | 142 const BufferQueue& text_buffers = itr->second.ready_buffers(); |
| 136 if (!text_buffers.empty()) | 143 if (!text_buffers.empty()) |
| 137 text_buffers_map_.insert(std::make_pair(itr->first, text_buffers)); | 144 text_buffers_map_.insert(std::make_pair(itr->first, text_buffers)); |
| 138 } | 145 } |
| 139 | 146 |
| 140 return text_buffers_map_; | 147 return text_buffers_map_; |
| 141 } | 148 } |
| 142 | 149 |
| 150 base::TimeDelta WebMClusterParser::TryGetEncodedAudioDuration(const uint8* data, | |
| 151 int size) { | |
| 152 if (audio_codec_ == kCodecOpus) { | |
| 153 return ReadOpusDuration(data, size); | |
| 154 } | |
| 155 // 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.
| |
| 156 | |
| 157 return kNoTimestamp(); | |
| 158 } | |
| 159 | |
| 160 base::TimeDelta WebMClusterParser::ReadOpusDuration(const uint8* data, | |
| 161 int size) { | |
| 162 // 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.
| |
| 163 static const uint8 kTocConfigMask = 0xf8; | |
|
wolenetz
2015/02/03 22:47:01
ditto: _t
chcunningham
2015/02/05 02:48:21
Done.
| |
| 164 static const uint8 kTocFrameCountCodeMask = 0x03; | |
| 165 static const uint8 kFrameCountMask = 0x3f; | |
| 166 static const int kFrameCountMin = 1; | |
| 167 static const int kFrameCountMax = 48; | |
| 168 | |
| 169 if (size < 1) { | |
| 170 MEDIA_LOG(log_cb_) << "Invalid zero-byte Opus packet."; | |
| 171 return kNoTimestamp(); | |
| 172 } | |
| 173 | |
| 174 // Frame count type described by last 2 bits of Opus TOC byte. | |
| 175 int frame_count_type = data[0] & kTocFrameCountCodeMask; | |
| 176 | |
| 177 int frame_count = 0; | |
| 178 switch (frame_count_type) { | |
| 179 case 0: | |
| 180 frame_count = 1; | |
| 181 break; | |
| 182 case 1: | |
| 183 case 2: | |
| 184 frame_count = 2; | |
| 185 break; | |
| 186 case 3: | |
| 187 // Type 3 indicates an arbitrary frame count described in the next byte. | |
| 188 if (size < 2) { | |
| 189 MEDIA_LOG(log_cb_) << "Second byte missing from 'Code 3' Opus packet."; | |
| 190 return kNoTimestamp(); | |
| 191 } | |
| 192 frame_count = data[1] & kFrameCountMask; | |
| 193 DCHECK_GE(frame_count, 1); | |
| 194 if (frame_count < kFrameCountMin || frame_count > kFrameCountMax) { | |
| 195 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.
| |
| 196 << " Should be in range [" << kFrameCountMin << ", " | |
| 197 << kFrameCountMax << "]"; | |
| 198 } | |
| 199 break; | |
| 200 default: | |
| 201 MEDIA_LOG(log_cb_) << "Unexpected Opus frame count type: " | |
| 202 << frame_count_type; | |
| 203 return kNoTimestamp(); | |
| 204 } | |
| 205 | |
| 206 int opusConfig = (data[0] & kTocConfigMask) >> 3; | |
| 207 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.
| |
| 208 frame_count); | |
| 209 } | |
| 210 | |
| 143 WebMParserClient* WebMClusterParser::OnListStart(int id) { | 211 WebMParserClient* WebMClusterParser::OnListStart(int id) { |
| 144 if (id == kWebMIdCluster) { | 212 if (id == kWebMIdCluster) { |
| 145 cluster_timecode_ = -1; | 213 cluster_timecode_ = -1; |
| 146 cluster_start_time_ = kNoTimestamp(); | 214 cluster_start_time_ = kNoTimestamp(); |
| 147 } else if (id == kWebMIdBlockGroup) { | 215 } else if (id == kWebMIdBlockGroup) { |
| 148 block_data_.reset(); | 216 block_data_.reset(); |
| 149 block_data_size_ = -1; | 217 block_data_size_ = -1; |
| 150 block_duration_ = -1; | 218 block_duration_ = -1; |
| 151 discard_padding_ = -1; | 219 discard_padding_ = -1; |
| 152 discard_padding_set_ = false; | 220 discard_padding_set_ = false; |
| (...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 317 | 385 |
| 318 if (last_block_timecode_ != -1 && timecode < last_block_timecode_) { | 386 if (last_block_timecode_ != -1 && timecode < last_block_timecode_) { |
| 319 MEDIA_LOG(log_cb_) | 387 MEDIA_LOG(log_cb_) |
| 320 << "Got a block with a timecode before the previous block."; | 388 << "Got a block with a timecode before the previous block."; |
| 321 return false; | 389 return false; |
| 322 } | 390 } |
| 323 | 391 |
| 324 Track* track = NULL; | 392 Track* track = NULL; |
| 325 StreamParserBuffer::Type buffer_type = DemuxerStream::AUDIO; | 393 StreamParserBuffer::Type buffer_type = DemuxerStream::AUDIO; |
| 326 std::string encryption_key_id; | 394 std::string encryption_key_id; |
| 395 base::TimeDelta encoded_duration = kNoTimestamp(); | |
| 327 if (track_num == audio_.track_num()) { | 396 if (track_num == audio_.track_num()) { |
| 328 track = &audio_; | 397 track = &audio_; |
| 329 encryption_key_id = audio_encryption_key_id_; | 398 encryption_key_id = audio_encryption_key_id_; |
| 399 if (encryption_key_id.empty()) { | |
| 400 encoded_duration = TryGetEncodedAudioDuration(data, size); | |
| 401 } | |
| 330 } else if (track_num == video_.track_num()) { | 402 } else if (track_num == video_.track_num()) { |
| 331 track = &video_; | 403 track = &video_; |
| 332 encryption_key_id = video_encryption_key_id_; | 404 encryption_key_id = video_encryption_key_id_; |
| 333 buffer_type = DemuxerStream::VIDEO; | 405 buffer_type = DemuxerStream::VIDEO; |
| 334 } else if (ignored_tracks_.find(track_num) != ignored_tracks_.end()) { | 406 } else if (ignored_tracks_.find(track_num) != ignored_tracks_.end()) { |
| 335 return true; | 407 return true; |
| 336 } else if (Track* const text_track = FindTextTrack(track_num)) { | 408 } else if (Track* const text_track = FindTextTrack(track_num)) { |
| 337 if (is_simple_block) // BlockGroup is required for WebVTT cues | 409 if (is_simple_block) // BlockGroup is required for WebVTT cues |
| 338 return false; | 410 return false; |
| 339 if (block_duration < 0) // not specified | 411 if (block_duration < 0) // not specified |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 400 content.length(), | 472 content.length(), |
| 401 &side_data[0], | 473 &side_data[0], |
| 402 side_data.size(), | 474 side_data.size(), |
| 403 true, buffer_type, track_num); | 475 true, buffer_type, track_num); |
| 404 } | 476 } |
| 405 | 477 |
| 406 buffer->set_timestamp(timestamp); | 478 buffer->set_timestamp(timestamp); |
| 407 if (cluster_start_time_ == kNoTimestamp()) | 479 if (cluster_start_time_ == kNoTimestamp()) |
| 408 cluster_start_time_ = timestamp; | 480 cluster_start_time_ = timestamp; |
| 409 | 481 |
| 482 base::TimeDelta block_duration_time_delta = kNoTimestamp(); | |
| 410 if (block_duration >= 0) { | 483 if (block_duration >= 0) { |
| 411 buffer->set_duration(base::TimeDelta::FromMicroseconds( | 484 block_duration_time_delta = base::TimeDelta::FromMicroseconds( |
| 412 block_duration * timecode_multiplier_)); | 485 block_duration * timecode_multiplier_); |
| 486 } | |
| 487 | |
| 488 // 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 :)
| |
| 489 // workaround for http://crbug.com/396634, decreasing the likelihood of | |
| 490 // fallback to rough estimation techniques for Blocks that lack a | |
| 491 // BlockDuration at the end of a cluster. Cross cluster durations are not | |
| 492 // 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.
| |
| 493 // 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.
| |
| 494 // Estimates are applied at the end of parsing once the whole cluster is | |
| 495 // parsed. See ApplyDurationEstimateIfNeeded for more on estimation. | |
|
wolenetz
2015/02/03 22:47:01
nit: add ()
chcunningham
2015/02/05 02:48:21
Done.
| |
| 496 if (encoded_duration != kNoTimestamp()) { | |
| 497 DCHECK(encoded_duration != kInfiniteDuration()); | |
| 498 DCHECK(encoded_duration > base::TimeDelta()); | |
| 499 buffer->set_duration(encoded_duration); | |
| 500 | |
| 501 DVLOG(3) << __FUNCTION__ << " : " | |
| 502 << "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.
| |
| 503 | |
| 504 if (block_duration_time_delta != kNoTimestamp()) { | |
| 505 base::TimeDelta duration_difference = | |
| 506 block_duration_time_delta - encoded_duration; | |
| 507 | |
| 508 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.
| |
| 509 if (duration_difference.magnitude() > kWarnDurationDiff) { | |
| 510 MEDIA_LOG(log_cb_) << "BlockDuration " | |
| 511 << "(" << 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
| |
| 512 << "differs signifcantly from encoded duration " | |
|
wolenetz
2015/02/03 22:47:01
nit:s/signifcantly/significantly/
chcunningham
2015/02/05 02:48:21
Done.
| |
| 513 << "(" << 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.
| |
| 514 } | |
| 515 } | |
| 516 } else if (block_duration_time_delta != kNoTimestamp()) { | |
| 517 buffer->set_duration(block_duration_time_delta); | |
| 413 } else { | 518 } else { |
| 414 DCHECK_NE(buffer_type, DemuxerStream::TEXT); | 519 DCHECK_NE(buffer_type, DemuxerStream::TEXT); |
| 415 buffer->set_duration(track->default_duration()); | 520 buffer->set_duration(track->default_duration()); |
| 416 } | 521 } |
| 417 | 522 |
| 418 if (discard_padding != 0) { | 523 if (discard_padding != 0) { |
| 419 buffer->set_discard_padding(std::make_pair( | 524 buffer->set_discard_padding(std::make_pair( |
| 420 base::TimeDelta(), | 525 base::TimeDelta(), |
| 421 base::TimeDelta::FromMicroseconds(discard_padding / 1000))); | 526 base::TimeDelta::FromMicroseconds(discard_padding / 1000))); |
| 422 } | 527 } |
| (...skipping 255 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 678 WebMClusterParser::FindTextTrack(int track_num) { | 783 WebMClusterParser::FindTextTrack(int track_num) { |
| 679 const TextTrackMap::iterator it = text_track_map_.find(track_num); | 784 const TextTrackMap::iterator it = text_track_map_.find(track_num); |
| 680 | 785 |
| 681 if (it == text_track_map_.end()) | 786 if (it == text_track_map_.end()) |
| 682 return NULL; | 787 return NULL; |
| 683 | 788 |
| 684 return &it->second; | 789 return &it->second; |
| 685 } | 790 } |
| 686 | 791 |
| 687 } // namespace media | 792 } // namespace media |
| OLD | NEW |