diff --git a/packager/app/test/testdata/bear-320x240-vp9-cenc-golden.mp4 b/packager/app/test/testdata/bear-320x240-vp9-cenc-golden.mp4 index 0d8f9a29b5..d7b8add6ea 100644 Binary files a/packager/app/test/testdata/bear-320x240-vp9-cenc-golden.mp4 and b/packager/app/test/testdata/bear-320x240-vp9-cenc-golden.mp4 differ diff --git a/packager/app/test/testdata/bear-320x240-vp9-cenc-golden.mpd b/packager/app/test/testdata/bear-320x240-vp9-cenc-golden.mpd index 5f8e5dae39..6b89c5ccc3 100644 --- a/packager/app/test/testdata/bear-320x240-vp9-cenc-golden.mpd +++ b/packager/app/test/testdata/bear-320x240-vp9-cenc-golden.mpd @@ -1,15 +1,15 @@ - + - - + + AAAAMHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABAxMjM0NTY3ODkwMTIzNDU2 output_video.mp4 - + diff --git a/packager/app/test/testdata/bear-320x240-vp9-golden.webm b/packager/app/test/testdata/bear-320x240-vp9-golden.webm index 9e34a62ab2..d387a02576 100644 Binary files a/packager/app/test/testdata/bear-320x240-vp9-golden.webm and b/packager/app/test/testdata/bear-320x240-vp9-golden.webm differ diff --git a/packager/app/test/testdata/bear-320x240-vp9-webm-golden.mpd b/packager/app/test/testdata/bear-320x240-vp9-webm-golden.mpd index b9116d2a8a..8ee2342618 100644 --- a/packager/app/test/testdata/bear-320x240-vp9-webm-golden.mpd +++ b/packager/app/test/testdata/bear-320x240-vp9-webm-golden.mpd @@ -1,11 +1,11 @@ - + - - + + output_video.webm - + diff --git a/packager/app/test/testdata/bear-640x360-vp8-cenc-golden.mp4 b/packager/app/test/testdata/bear-640x360-vp8-cenc-golden.mp4 index e28348623b..3423a75b26 100644 Binary files a/packager/app/test/testdata/bear-640x360-vp8-cenc-golden.mp4 and b/packager/app/test/testdata/bear-640x360-vp8-cenc-golden.mp4 differ diff --git a/packager/app/test/testdata/bear-640x360-vp8-cenc-golden.mpd b/packager/app/test/testdata/bear-640x360-vp8-cenc-golden.mpd index 99d07c1d71..18372ecbfe 100644 --- a/packager/app/test/testdata/bear-640x360-vp8-cenc-golden.mpd +++ b/packager/app/test/testdata/bear-640x360-vp8-cenc-golden.mpd @@ -1,15 +1,15 @@ - + - + AAAAMHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABAxMjM0NTY3ODkwMTIzNDU2 output_video.mp4 - + diff --git a/packager/app/test/testdata/bear-640x360-vp8-golden.webm b/packager/app/test/testdata/bear-640x360-vp8-golden.webm index 330354f702..0de835fd4a 100644 Binary files a/packager/app/test/testdata/bear-640x360-vp8-golden.webm and b/packager/app/test/testdata/bear-640x360-vp8-golden.webm differ diff --git a/packager/app/test/testdata/bear-640x360-vp8-webm-golden.mpd b/packager/app/test/testdata/bear-640x360-vp8-webm-golden.mpd index ce5f1adbef..38e0b57c58 100644 --- a/packager/app/test/testdata/bear-640x360-vp8-webm-golden.mpd +++ b/packager/app/test/testdata/bear-640x360-vp8-webm-golden.mpd @@ -1,11 +1,11 @@ - + - + output_video.webm - + diff --git a/packager/media/formats/webm/opus_packet_builder.cc b/packager/media/formats/webm/opus_packet_builder.cc deleted file mode 100644 index e555c8be14..0000000000 --- a/packager/media/formats/webm/opus_packet_builder.cc +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright (c) 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "packager/base/logging.h" -#include "packager/media/formats/webm/opus_packet_builder.h" -#include "packager/media/formats/webm/webm_cluster_parser.h" - -namespace edash_packager { -namespace media { - -OpusPacket::OpusPacket(uint8_t config, uint8_t frame_count, bool is_VBR) { - DCHECK_GE(config, 0); - DCHECK_LT(config, kNumPossibleOpusConfigs); - DCHECK_GE(frame_count, kMinOpusPacketFrameCount); - DCHECK_LE(frame_count, kMaxOpusPacketFrameCount); - - duration_ms_ = frame_count * - WebMClusterParser::kOpusFrameDurationsMu[config] / - static_cast(1000); - - uint8_t frame_count_code; - uint8_t frame_count_byte; - - if (frame_count == 1) { - frame_count_code = 0; - } else if (frame_count == 2) { - frame_count_code = is_VBR ? 2 : 1; - } else { - frame_count_code = 3; - frame_count_byte = (is_VBR ? 1 << 7 : 0) | frame_count; - } - - // All opus packets must have TOC byte. - uint8_t opus_toc_byte = (config << 3) | frame_count_code; - data_.push_back(opus_toc_byte); - - // For code 3 packets, the number of frames is signaled in the "frame - // count byte". - if (frame_count_code == 3) { - data_.push_back(frame_count_byte); - } - - // Packet will only conform to layout specification for the TOC byte - // and optional frame count bytes appended above. This last byte - // is purely dummy padding where frame size data or encoded data might - // otherwise start. - data_.push_back(static_cast(0)); -} - -OpusPacket::~OpusPacket() { -} - -const uint8_t* OpusPacket::data() const { - return &(data_[0]); -} - -int OpusPacket::size() const { - return data_.size(); -} - -double OpusPacket::duration_ms() const { - return duration_ms_; -} - -ScopedVector BuildAllOpusPackets() { - ScopedVector opus_packets; - - for (int frame_count = kMinOpusPacketFrameCount; - frame_count <= kMaxOpusPacketFrameCount; frame_count++) { - for (int opus_config_num = 0; opus_config_num < kNumPossibleOpusConfigs; - opus_config_num++) { - bool is_VBR = false; - opus_packets.push_back( - new OpusPacket(opus_config_num, frame_count, is_VBR)); - - if (frame_count >= 2) { - // Add another packet with VBR flag toggled. For frame counts >= 2, - // VBR triggers changes to packet framing. - is_VBR = true; - opus_packets.push_back( - new OpusPacket(opus_config_num, frame_count, is_VBR)); - } - } - } - - return opus_packets.Pass(); -} - -} // namespace media -} // namespace edash_packager diff --git a/packager/media/formats/webm/opus_packet_builder.h b/packager/media/formats/webm/opus_packet_builder.h deleted file mode 100644 index 78dcda59c1..0000000000 --- a/packager/media/formats/webm/opus_packet_builder.h +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MEDIA_FORMATS_WEBM_OPUS_PACKET_BUILDER_H_ -#define MEDIA_FORMATS_WEBM_OPUS_PACKET_BUILDER_H_ - -#include - -#include "packager/base/memory/scoped_ptr.h" -#include "packager/base/memory/scoped_vector.h" - -namespace edash_packager { -namespace media { - -// From Opus RFC. See https://tools.ietf.org/html/rfc6716#page-14 -enum OpusConstants { - kNumPossibleOpusConfigs = 32, - kMinOpusPacketFrameCount = 1, - kMaxOpusPacketFrameCount = 48 -}; - -class OpusPacket { - public: - OpusPacket(uint8_t config, uint8_t frame_count, bool is_VBR); - ~OpusPacket(); - - const uint8_t* data() const; - int size() const; - double duration_ms() const; - - private: - std::vector data_; - double duration_ms_; - - DISALLOW_COPY_AND_ASSIGN(OpusPacket); -}; - -// Builds an exhaustive collection of Opus packet configurations. -ScopedVector BuildAllOpusPackets(); - -} // namespace media -} // namespace edash_packager - -#endif // MEDIA_FORMATS_WEBM_OPUS_PACKET_BUILDER_H_ diff --git a/packager/media/formats/webm/webm.gyp b/packager/media/formats/webm/webm.gyp index a01626bc43..0508b31fbc 100644 --- a/packager/media/formats/webm/webm.gyp +++ b/packager/media/formats/webm/webm.gyp @@ -69,8 +69,6 @@ 'cluster_builder.h', 'encrypted_segmenter_unittest.cc', 'multi_segment_segmenter_unittest.cc', - 'opus_packet_builder.cc', - 'opus_packet_builder.h', 'segmenter_test_base.cc', 'segmenter_test_base.h', 'single_segment_segmenter_unittest.cc', diff --git a/packager/media/formats/webm/webm_cluster_parser.cc b/packager/media/formats/webm/webm_cluster_parser.cc index f5cfc1be9a..bcfb77a1ac 100644 --- a/packager/media/formats/webm/webm_cluster_parser.cc +++ b/packager/media/formats/webm/webm_cluster_parser.cc @@ -17,36 +17,12 @@ #include "packager/media/formats/webm/webm_crypto_helpers.h" #include "packager/media/formats/webm/webm_webvtt_parser.h" -// Logs only while |count| < |max|, increments |count| for each log, and warns -// in the log if |count| has just reached |max|. -#define LIMITED_LOG(level, count, max) \ - LOG_IF(level, (count) < (max)) \ - << (((count) + 1 == (max)) \ - ? "(Log limit reached. Further similar entries " \ - "may be suppressed): " \ - : "") -#define LIMITED_DLOG(level, count, max) \ - DLOG_IF(level, (count) < (max)) \ - << (((count) + 1 == (max)) \ - ? "(Log limit reached. Further similar entries " \ - "may be suppressed): " \ - : "") - namespace edash_packager { namespace media { namespace { const int64_t kMicrosecondsPerMillisecond = 1000; -enum { - // Limits the number of LOG() calls in the path of reading encoded - // duration to avoid spamming for corrupted data. - kMaxDurationErrorLogs = 10, - // Limits the number of LOG() calls warning the user that buffer - // durations have been estimated. - kMaxDurationEstimateLogs = 10, -}; - // Helper function used to inspect block data to determine if the // block is a keyframe. // |data| contains the bytes in the block. @@ -73,11 +49,6 @@ bool IsKeyframe(bool is_video, } // namespace -const uint16_t 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_t timecode_scale, scoped_refptr audio_stream_info, @@ -169,100 +140,6 @@ int WebMClusterParser::Parse(const uint8_t* buf, int size) { return result; } -int64_t WebMClusterParser::TryGetEncodedAudioDuration( - const uint8_t* data, - int size) { - - // Duration is currently read assuming the *entire* stream is unencrypted. - // The special "Signal Byte" prepended to Blocks in encrypted streams is - // assumed to not be present. - // TODO: Consider parsing "Signal Byte" for encrypted streams to return - // duration for any unencrypted blocks. - - DCHECK(audio_stream_info_); - if (audio_stream_info_->codec() == kCodecOpus) { - return ReadOpusDuration(data, size); - } - - // TODO: Implement duration reading for Vorbis. See motivations in - // http://crbug.com/396634. - - return kNoTimestamp; -} - -int64_t WebMClusterParser::ReadOpusDuration(const uint8_t* data, int size) { - // Masks and constants for Opus packets. See - // https://tools.ietf.org/html/rfc6716#page-14 - static const uint8_t kTocConfigMask = 0xf8; - static const uint8_t kTocFrameCountCodeMask = 0x03; - static const uint8_t kFrameCountMask = 0x3f; - static const int64_t kPacketDurationMaxMs = 120000; - - if (size < 1) { - LIMITED_DLOG(INFO, num_duration_errors_, kMaxDurationErrorLogs) - << "Invalid zero-byte Opus packet; demuxed block duration may be " - "imprecise."; - 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) { - LIMITED_DLOG(INFO, num_duration_errors_, kMaxDurationErrorLogs) - << "Second byte missing from 'Code 3' Opus packet; demuxed block " - "duration may be imprecise."; - return kNoTimestamp; - } - - frame_count = data[1] & kFrameCountMask; - - if (frame_count == 0) { - LIMITED_DLOG(INFO, num_duration_errors_, kMaxDurationErrorLogs) - << "Illegal 'Code 3' Opus packet with frame count zero; demuxed " - "block duration may be imprecise."; - return kNoTimestamp; - } - - break; - default: - LIMITED_DLOG(INFO, num_duration_errors_, kMaxDurationErrorLogs) - << "Unexpected Opus frame count type: " << frame_count_type << "; " - << "demuxed block duration may be imprecise."; - return kNoTimestamp; - } - - int opusConfig = (data[0] & kTocConfigMask) >> 3; - CHECK_GE(opusConfig, 0); - CHECK_LT(opusConfig, static_cast(arraysize(kOpusFrameDurationsMu))); - - DCHECK_GT(frame_count, 0); - int64_t duration = kOpusFrameDurationsMu[opusConfig] * frame_count; - - if (duration > kPacketDurationMaxMs * 1000) { - // Intentionally allowing packet to pass through for now. Decoder should - // either handle or fail gracefully. LOG as breadcrumbs in case - // things go sideways. - LIMITED_DLOG(INFO, num_duration_errors_, kMaxDurationErrorLogs) - << "Warning, demuxed Opus packet with encoded duration: " - << duration / 1000 << "ms. Should be no greater than " - << kPacketDurationMaxMs << "ms."; - } - - return duration; -} - WebMParserClient* WebMClusterParser::OnListStart(int id) { if (id == kWebMIdCluster) { cluster_timecode_ = -1; @@ -451,13 +328,9 @@ bool WebMClusterParser::OnBlock(bool is_simple_block, Track* track = NULL; StreamType stream_type = kStreamAudio; std::string encryption_key_id; - int64_t 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_; @@ -529,49 +402,13 @@ bool WebMClusterParser::OnBlock(bool is_simple_block, &side_data[0], side_data.size(), true); } + buffer->set_dts(timestamp); buffer->set_pts(timestamp); if (cluster_start_time_ == kNoTimestamp) cluster_start_time_ = timestamp; - - int64_t block_duration_time_delta = kNoTimestamp; - if (block_duration >= 0) { - block_duration_time_delta = block_duration * timecode_multiplier_; - } - - // Prefer encoded duration over BlockGroup->BlockDuration or - // TrackEntry->DefaultDuration when available. This layering violation is a - // workaround for http://crbug.com/396634, decreasing the likelihood of - // fall-back to rough estimation techniques for Blocks that lack a - // BlockDuration at the end of a cluster. Duration estimation may still apply - // in cases of encryption and codecs for which we do not extract encoded - // duration. Estimates are applied as Block Timecode deltas, or once the whole - // stream is parsed in the case of the last Block in the stream. See - // Track::EmitBuffer and ApplyDurationEstimateIfNeeded(). - if (encoded_duration != kNoTimestamp) { - DCHECK(encoded_duration != kInfiniteDuration); - DCHECK(encoded_duration > 0); - buffer->set_duration(encoded_duration); - - DVLOG(3) << __FUNCTION__ << " : " - << "Using encoded duration " << encoded_duration; - - if (block_duration_time_delta != kNoTimestamp) { - int64_t duration_difference = - block_duration_time_delta - encoded_duration; - - const auto kWarnDurationDiff = timecode_multiplier_ * 2; - if (duration_difference > kWarnDurationDiff) { - LIMITED_DLOG(INFO, num_duration_errors_, kMaxDurationErrorLogs) - << "BlockDuration (" << block_duration_time_delta / 1000 - << "ms) differs significantly from encoded duration (" - << encoded_duration / 1000 << "ms)."; - } - } - } else if (block_duration_time_delta != kNoTimestamp) { - buffer->set_duration(block_duration_time_delta); - } else { - buffer->set_duration(track->default_duration()); - } + buffer->set_duration(block_duration > 0 + ? (block_duration * timecode_multiplier_) + : kNoTimestamp); if (!init_cb_.is_null() && !initialized_) { std::vector> streams; @@ -679,16 +516,14 @@ void WebMClusterParser::Track::ApplyDurationEstimateIfNeeded() { int64_t estimated_duration = GetDurationEstimate(); last_added_buffer_missing_duration_->set_duration(estimated_duration); - LIMITED_LOG(INFO, num_duration_estimates_, kMaxDurationEstimateLogs) - << "Estimating WebM block duration to be " << estimated_duration / 1000 - << "ms for the last (Simple)Block in the Cluster for this Track. Use " - "BlockGroups with BlockDurations at the end of each Track in a " - "Cluster to avoid estimation."; + VLOG(1) << "Track " << track_num_ << ": Estimating WebM block duration to be " + << estimated_duration / 1000 + << "ms for the last (Simple)Block in the Cluster for this Track. Use " + "BlockGroups with BlockDurations at the end of each Track in a " + "Cluster to avoid estimation."; - DVLOG(2) << __FUNCTION__ << " new dur : ts " - << last_added_buffer_missing_duration_->pts() - << " dur " - << last_added_buffer_missing_duration_->duration() + DVLOG(2) << " new dur : ts " << last_added_buffer_missing_duration_->pts() + << " dur " << last_added_buffer_missing_duration_->duration() << " kf " << last_added_buffer_missing_duration_->is_key_frame() << " size " << last_added_buffer_missing_duration_->data_size(); @@ -702,7 +537,6 @@ void WebMClusterParser::Track::Reset() { last_added_buffer_missing_duration_ = NULL; } - bool WebMClusterParser::Track::EmitBufferHelp( const scoped_refptr& buffer) { DCHECK(!last_added_buffer_missing_duration_.get()); @@ -739,20 +573,25 @@ bool WebMClusterParser::Track::EmitBufferHelp( } int64_t WebMClusterParser::Track::GetDurationEstimate() { - int64_t duration = estimated_next_frame_duration_; - if (duration != kNoTimestamp) { - DVLOG(3) << __FUNCTION__ << " : using estimated duration"; + int64_t duration = kNoTimestamp; + if (default_duration_ != kNoTimestamp) { + duration = default_duration_; + DVLOG(3) << __FUNCTION__ << " : using track default duration " << duration; + } else if (estimated_next_frame_duration_ != kNoTimestamp) { + duration = estimated_next_frame_duration_; + DVLOG(3) << __FUNCTION__ << " : using estimated duration " << duration; } else { - DVLOG(3) << __FUNCTION__ << " : using hardcoded default duration"; if (is_video_) { duration = kDefaultVideoBufferDurationInMs * kMicrosecondsPerMillisecond; } else { duration = kDefaultAudioBufferDurationInMs * kMicrosecondsPerMillisecond; } + DVLOG(3) << __FUNCTION__ << " : using hardcoded default duration " + << duration; } - DCHECK(duration > 0); - DCHECK(duration != kNoTimestamp); + DCHECK_GT(duration, 0); + DCHECK_NE(duration, kNoTimestamp); return duration; } diff --git a/packager/media/formats/webm/webm_cluster_parser.h b/packager/media/formats/webm/webm_cluster_parser.h index 3bcb0aa63c..ee27bb928a 100644 --- a/packager/media/formats/webm/webm_cluster_parser.h +++ b/packager/media/formats/webm/webm_cluster_parser.h @@ -32,12 +32,6 @@ class WebMClusterParser : public WebMParserClient { kDefaultVideoBufferDurationInMs = 63 }; - /// Opus packets encode the duration and other parameters in the 5 most - /// significant bits of the first byte. The index in this array corresponds - /// to the duration of each frame of the packet in microseconds. See - /// https://tools.ietf.org/html/rfc6716#page-14 - static const uint16_t kOpusFrameDurationsMu[]; - private: // Helper class that manages per-track state. class Track { @@ -56,19 +50,16 @@ class WebMClusterParser : public WebMParserClient { // duration, saves |buffer| into |last_added_buffer_missing_duration_|. bool EmitBuffer(const scoped_refptr& buffer); - // If |last_added_buffer_missing_duration_| is set, updates its duration to - // be non-kNoTimestamp value of |estimated_next_frame_duration_| or a - // hard-coded default, then emits it and unsets - // |last_added_buffer_missing_duration_|. (This method helps stream parser - // emit all buffers in a media segment before signaling end of segment.) + // If |last_added_buffer_missing_duration_| is set, estimate the duration + // for this buffer using helper function GetDurationEstimate() then emits it + // and unsets |last_added_buffer_missing_duration_| (This method helps + // stream parser emit all buffers in a media segment). void ApplyDurationEstimateIfNeeded(); // Clears all buffer state, including any possibly held-aside buffer that // was missing duration. void Reset(); - int64_t default_duration() const { return default_duration_; } - private: // Helper that sanity-checks |buffer| duration, updates // |estimated_next_frame_duration_|, and emits |buffer|. @@ -76,28 +67,24 @@ class WebMClusterParser : public WebMParserClient { // emitted. Returns true otherwise. bool EmitBufferHelp(const scoped_refptr& buffer); - // Helper that calculates the buffer duration to use in + // Helper function that calculates the buffer duration to use in // ApplyDurationEstimateIfNeeded(). int64_t GetDurationEstimate(); - // Counts the number of estimated durations used in this track. Used to - // prevent log spam for LOG()s about estimated duration. - int num_duration_estimates_ = 0; - int track_num_; bool is_video_; - // Parsed track buffers, each with duration and in (decode) timestamp order, - // that have not yet been emitted. Note that up to one additional buffer - // missing duration may be tracked by |last_added_buffer_missing_duration_|. + // Holding the sample that is missing duration. The duration will be + // computed from the difference in timestamp when next sample arrives; or + // estimated if it is the last sample in this track. scoped_refptr last_added_buffer_missing_duration_; // If kNoTimestamp, then |estimated_next_frame_duration_| will be used. int64_t default_duration_; - // If kNoTimestamp, then a default value will be used. This estimate is the - // maximum duration seen so far for this track, and is used only if - // |default_duration_| is kNoTimestamp. + // If kNoTimestamp, then a hardcoded default value will be used. This + // estimate is the maximum duration seen so far for this track, and is used + // only if |default_duration_| is kNoTimestamp. int64_t estimated_next_frame_duration_; MediaParser::NewSampleCB new_sample_cb_; @@ -169,22 +156,9 @@ class WebMClusterParser : public WebMParserClient { // if that track num is not a text track. Track* FindTextTrack(int track_num); - // Attempts to read the duration from the encoded audio data, returning as - // kNoTimestamp if duration cannot be retrieved. - // Avoid calling if encrypted; may produce unexpected output. See - // implementation for supported codecs. - int64_t TryGetEncodedAudioDuration(const uint8_t* data, int size); + // Multiplier used to convert timecodes into microseconds. + double timecode_multiplier_; - // Reads Opus packet header to determine packet duration. Duration returned - // as kNoTimestamp upon failure to read duration from packet. - int64_t ReadOpusDuration(const uint8_t* data, int size); - - // Tracks the number of LOGs made in process of reading encoded duration. - // Useful to prevent log spam. - int num_duration_errors_ = 0; - - double timecode_multiplier_; // Multiplier used to convert timecodes into - // microseconds. scoped_refptr audio_stream_info_; scoped_refptr video_stream_info_; std::set ignored_tracks_; @@ -221,7 +195,7 @@ class WebMClusterParser : public WebMParserClient { Track video_; TextTrackMap text_track_map_; - DISALLOW_IMPLICIT_CONSTRUCTORS(WebMClusterParser); + DISALLOW_COPY_AND_ASSIGN(WebMClusterParser); }; } // namespace media diff --git a/packager/media/formats/webm/webm_cluster_parser_unittest.cc b/packager/media/formats/webm/webm_cluster_parser_unittest.cc index caaa9ca30e..a167e8d7cf 100644 --- a/packager/media/formats/webm/webm_cluster_parser_unittest.cc +++ b/packager/media/formats/webm/webm_cluster_parser_unittest.cc @@ -18,7 +18,6 @@ #include "packager/media/base/decrypt_config.h" #include "packager/media/base/timestamp.h" #include "packager/media/formats/webm/cluster_builder.h" -#include "packager/media/formats/webm/opus_packet_builder.h" #include "packager/media/formats/webm/webm_constants.h" using ::testing::HasSubstr; @@ -475,15 +474,16 @@ TEST_F(WebMClusterParserTest, TracksWithSampleMissingDuration) { const int kExpectedVideoEstimationInMs = 33; const BlockInfo kBlockInfo[] = { - {kVideoTrackNum, 0, 33, true, NULL, 0}, + // Note that for simple blocks, duration is not encoded. + {kVideoTrackNum, 0, 0, true, NULL, 0}, {kAudioTrackNum, 0, 23, false, NULL, 0}, {kTextTrackNum, 10, 42, false, NULL, 0}, - {kAudioTrackNum, 23, kTestAudioFrameDefaultDurationInMs, true, NULL, 0}, - {kVideoTrackNum, 33, 33, true, NULL, 0}, - {kAudioTrackNum, 36, kTestAudioFrameDefaultDurationInMs, true, NULL, 0}, - {kVideoTrackNum, 66, kExpectedVideoEstimationInMs, true, NULL, 0}, - {kAudioTrackNum, 70, kTestAudioFrameDefaultDurationInMs, true, NULL, 0}, - {kAudioTrackNum, 83, kTestAudioFrameDefaultDurationInMs, true, NULL, 0}, + {kAudioTrackNum, 23, 0, true, NULL, 0}, + {kVideoTrackNum, 33, 0, true, NULL, 0}, + {kAudioTrackNum, 36, 0, true, NULL, 0}, + {kVideoTrackNum, 66, 0, true, NULL, 0}, + {kAudioTrackNum, 70, 0, true, NULL, 0}, + {kAudioTrackNum, 83, 0, true, NULL, 0}, }; // Samples are not emitted in the same order as |kBlockInfo| due to missing of @@ -491,24 +491,24 @@ TEST_F(WebMClusterParserTest, TracksWithSampleMissingDuration) { const BlockInfo kExpectedBlockInfo[] = { {kAudioTrackNum, 0, 23, false, NULL, 0}, {kTextTrackNum, 10, 42, false, NULL, 0}, - {kAudioTrackNum, 23, kTestAudioFrameDefaultDurationInMs, true, NULL, 0}, {kVideoTrackNum, 0, 33, true, NULL, 0}, - {kAudioTrackNum, 36, kTestAudioFrameDefaultDurationInMs, true, NULL, 0}, + {kAudioTrackNum, 23, 13, true, NULL, 0}, {kVideoTrackNum, 33, 33, true, NULL, 0}, - {kAudioTrackNum, 70, kTestAudioFrameDefaultDurationInMs, true, NULL, 0}, + {kAudioTrackNum, 36, 34, true, NULL, 0}, + {kAudioTrackNum, 70, 13, true, NULL, 0}, {kAudioTrackNum, 83, kTestAudioFrameDefaultDurationInMs, true, NULL, 0}, {kVideoTrackNum, 66, kExpectedVideoEstimationInMs, true, NULL, 0}, }; const int kExpectedBuffersOnPartialCluster[] = { - 0, // Video simple block without DefaultDuration should be held back - 1, // Audio buffer ready + 0, // Video simple block without duration should be held back + 1, // 1st audio buffer ready 2, // Text buffer ready - 3, // 2nd audio buffer ready - 4, // 1st video emitted, 2nd video held back with no duration - 5, // 3rd audio ready - 6, // 2nd video emitted, 3rd video held back with no duration - 7, // 4th audio ready - 8, // 5th audio ready + 2, // Audio simple block without duration should be held back + 3, // 1st video emitted, 2nd video held back with no duration + 4, // 2rd audio ready, 3rd audio held back with no duration + 5, // 2nd video emitted, 3rd video held back with no duration + 6, // 3th audio ready, 4th audio held back with no duration + 7, // 4th audio ready, 5th audio held back with no duration }; ASSERT_EQ(arraysize(kBlockInfo), arraysize(kExpectedBuffersOnPartialCluster)); @@ -531,9 +531,11 @@ TEST_F(WebMClusterParserTest, TracksWithSampleMissingDuration) { VerifyBuffers(kExpectedBlockInfo, kExpectedBuffersOnPartialCluster[i])); } - // The last (3rd) video is emitted on flush with duration estimated. + // The last audio (5th) and the last video (3rd) are emitted on flush with + // duration estimated - estimated to be default duration if it is specified, + // otherwise estimated from earlier frames. parser_->Flush(); - EXPECT_TRUE(VerifyBuffers(&kExpectedBlockInfo[block_count - 1], 1)); + EXPECT_TRUE(VerifyBuffers(&kExpectedBlockInfo[block_count - 2], 2)); } TEST_F(WebMClusterParserTest, Reset) { @@ -875,11 +877,12 @@ TEST_F(WebMClusterParserTest, ParseWithDefaultDurationsSimpleBlocks) { EXPECT_LT(kTestVideoFrameDefaultDurationInMs, 33); const BlockInfo kBlockInfo[] = { - {kAudioTrackNum, 0, kTestAudioFrameDefaultDurationInMs, true, NULL, 0}, - {kAudioTrackNum, 23, kTestAudioFrameDefaultDurationInMs, true, NULL, 0}, - {kVideoTrackNum, 33, kTestVideoFrameDefaultDurationInMs, true, NULL, 0}, - {kAudioTrackNum, 46, kTestAudioFrameDefaultDurationInMs, true, NULL, 0}, - {kVideoTrackNum, 67, kTestVideoFrameDefaultDurationInMs, true, NULL, 0}, + // Note that for simple blocks, duration is not encoded. + {kAudioTrackNum, 0, 23, true, NULL, 0}, + {kAudioTrackNum, 23, 23, true, NULL, 0}, + {kVideoTrackNum, 33, 34, true, NULL, 0}, + {kAudioTrackNum, 46, 23, true, NULL, 0}, + {kVideoTrackNum, 67, 33, true, NULL, 0}, {kAudioTrackNum, 69, kTestAudioFrameDefaultDurationInMs, true, NULL, 0}, {kVideoTrackNum, 100, kTestVideoFrameDefaultDurationInMs, true, NULL, 0}, }; @@ -887,21 +890,17 @@ TEST_F(WebMClusterParserTest, ParseWithDefaultDurationsSimpleBlocks) { int block_count = arraysize(kBlockInfo); scoped_ptr 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(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()); + // Now parse a whole cluster to verify that all the blocks will get parsed + // and the last audio and video are held back due to no duration. + // The durations for all blocks are calculated to be the timestamp difference + // with the next block. + int result = parser_->Parse(cluster->data(), cluster->size()); EXPECT_EQ(cluster->size(), result); - ASSERT_TRUE(VerifyBuffers(kBlockInfo, block_count)); + ASSERT_TRUE(VerifyBuffers(kBlockInfo, block_count - 2)); + // The last audio and video are emitted on flush wiht duration estimated - + // estimated to be default_duration since it is present. + parser_->Flush(); + ASSERT_TRUE(VerifyBuffers(&kBlockInfo[block_count - 2], 2)); } TEST_F(WebMClusterParserTest, ParseWithoutAnyDurationsSimpleBlocks) { @@ -1008,49 +1007,6 @@ TEST_F(WebMClusterParserTest, ParseWithoutAnyDurationsBlockGroups) { ASSERT_TRUE(VerifyBuffers(kBlockInfo2, block_count2)); } -// TODO: 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, NULL, 0}, - {kAudioTrackNum, 23, -kTestAudioFrameDefaultDurationInMs, false, NULL, 0}, - {kVideoTrackNum, 33, -kTestVideoFrameDefaultDurationInMs, false, NULL, 0}, - {kAudioTrackNum, 46, -kTestAudioFrameDefaultDurationInMs, false, NULL, 0}, - {kVideoTrackNum, 67, -kTestVideoFrameDefaultDurationInMs, false, NULL, 0}, - {kAudioTrackNum, 69, -kTestAudioFrameDefaultDurationInMs, false, NULL, 0}, - {kVideoTrackNum, - 100, - -kTestVideoFrameDefaultDurationInMs, - false, - NULL, - 0}, - }; - - int block_count = arraysize(kBlockInfo); - scoped_ptr 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()); - parser_->Flush(); - ASSERT_TRUE(VerifyBuffers(kBlockInfo, block_count - 1)); - - // 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); - parser_->Flush(); - ASSERT_TRUE(VerifyBuffers(kBlockInfo, block_count)); -} - TEST_F(WebMClusterParserTest, ParseDegenerateClusterYieldsHardcodedEstimatedDurations) { const BlockInfo kBlockInfo[] = { @@ -1092,99 +1048,5 @@ TEST_F(WebMClusterParserTest, ASSERT_TRUE(VerifyBuffers(kBlockInfo, block_count)); } -TEST_F(WebMClusterParserTest, ReadOpusDurationsSimpleBlockAtEndOfCluster) { - int loop_count = 0; - for (const auto* packet_ptr : BuildAllOpusPackets()) { - InSequence s; - - // Get a new parser each iteration to prevent exceeding the log cap. - parser_.reset(CreateParserWithKeyIdsAndAudioCodec( - std::string(), std::string(), kCodecOpus)); - - const BlockInfo kBlockInfo[] = {{kAudioTrackNum, - 0, - packet_ptr->duration_ms(), - true, // Make it a SimpleBlock. - packet_ptr->data(), - packet_ptr->size()}}; - - int block_count = arraysize(kBlockInfo); - scoped_ptr cluster(CreateCluster(0, kBlockInfo, block_count)); - - int result = parser_->Parse(cluster->data(), cluster->size()); - EXPECT_EQ(cluster->size(), result); - ASSERT_TRUE(VerifyBuffers(kBlockInfo, block_count)); - - loop_count++; - } - - // Test should minimally cover all the combinations of config and frame count. - ASSERT_GE(loop_count, kNumPossibleOpusConfigs * kMaxOpusPacketFrameCount); -} - -TEST_F(WebMClusterParserTest, PreferOpusDurationsOverBlockDurations) { - int loop_count = 0; - for (const auto* packet_ptr : BuildAllOpusPackets()) { - InSequence s; - - // Get a new parser each iteration to prevent exceeding the log cap. - parser_.reset(CreateParserWithKeyIdsAndAudioCodec( - std::string(), std::string(), kCodecOpus)); - - // Setting BlockDuration != Opus duration to see which one the parser uses. - int block_duration_ms = packet_ptr->duration_ms() + 10; - BlockInfo block_infos[] = {{kAudioTrackNum, - 0, - static_cast(block_duration_ms), - false, // Not a SimpleBlock. - packet_ptr->data(), - packet_ptr->size()}}; - - int block_count = arraysize(block_infos); - scoped_ptr cluster(CreateCluster(0, block_infos, block_count)); - int result = parser_->Parse(cluster->data(), cluster->size()); - EXPECT_EQ(cluster->size(), result); - - // BlockInfo duration will be used to verify buffer duration, so changing - // duration to be that of the Opus packet to verify it was preferred. - block_infos[0].duration = packet_ptr->duration_ms(); - - ASSERT_TRUE(VerifyBuffers(block_infos, block_count)); - - loop_count++; - } - - // Test should minimally cover all the combinations of config and frame count. - ASSERT_GE(loop_count, kNumPossibleOpusConfigs * kMaxOpusPacketFrameCount); -} - -// Tests that BlockDuration is used to set duration on buffer rather than -// encoded duration in Opus packet (or hard coded duration estimates). Encoded -// Opus duration is usually preferred but cannot be known when encrypted. -TEST_F(WebMClusterParserTest, DontReadEncodedDurationWhenEncrypted) { - // Non-empty dummy value signals encryption is active for audio. - std::string audio_encryption_id("audio_key_id"); - - // Reset parser to expect Opus codec audio and use audio encryption key id. - parser_.reset(CreateParserWithKeyIdsAndAudioCodec(audio_encryption_id, - std::string(), kCodecOpus)); - - // Single Block with BlockDuration and encrypted data. - const BlockInfo kBlockInfo[] = {{kAudioTrackNum, - 0, - kTestAudioFrameDefaultDurationInMs, - false, // Not a SimpleBlock - kEncryptedFrame, // Encrypted frame data - arraysize(kEncryptedFrame)}}; - - int block_count = arraysize(kBlockInfo); - scoped_ptr cluster(CreateCluster(0, kBlockInfo, block_count)); - int result = parser_->Parse(cluster->data(), cluster->size()); - EXPECT_EQ(cluster->size(), result); - - // Will verify that duration of buffer matches that of BlockDuration. - ASSERT_TRUE(VerifyBuffers(kBlockInfo, block_count)); -} - } // namespace media } // namespace edash_packager