Update webm_cluster_parser to emit samples
Change-Id: I02436cfcb53b96210d6f683227cdabb994f4c01f
This commit is contained in:
parent
732e06fde0
commit
5a4234f4da
|
@ -9,7 +9,7 @@
|
||||||
#include "packager/base/logging.h"
|
#include "packager/base/logging.h"
|
||||||
#include "packager/base/sys_byteorder.h"
|
#include "packager/base/sys_byteorder.h"
|
||||||
#include "packager/media/base/decrypt_config.h"
|
#include "packager/media/base/decrypt_config.h"
|
||||||
#include "packager/media/base/timestamp_constants.h"
|
#include "packager/media/base/timestamp.h"
|
||||||
#include "packager/media/filters/webvtt_util.h"
|
#include "packager/media/filters/webvtt_util.h"
|
||||||
#include "packager/media/formats/webm/webm_constants.h"
|
#include "packager/media/formats/webm/webm_constants.h"
|
||||||
#include "packager/media/formats/webm/webm_crypto_helpers.h"
|
#include "packager/media/formats/webm/webm_crypto_helpers.h"
|
||||||
|
@ -30,6 +30,10 @@
|
||||||
"may be suppressed): " \
|
"may be suppressed): " \
|
||||||
: "")
|
: "")
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
const int64_t kMicrosecondsPerMillisecond = 1000;
|
||||||
|
} // namespace
|
||||||
|
|
||||||
namespace edash_packager {
|
namespace edash_packager {
|
||||||
namespace media {
|
namespace media {
|
||||||
|
|
||||||
|
@ -50,29 +54,29 @@ enum {
|
||||||
WebMClusterParser::WebMClusterParser(
|
WebMClusterParser::WebMClusterParser(
|
||||||
int64_t timecode_scale,
|
int64_t timecode_scale,
|
||||||
int audio_track_num,
|
int audio_track_num,
|
||||||
base::TimeDelta audio_default_duration,
|
int64_t audio_default_duration,
|
||||||
int video_track_num,
|
int video_track_num,
|
||||||
base::TimeDelta video_default_duration,
|
int64_t video_default_duration,
|
||||||
const WebMTracksParser::TextTracks& text_tracks,
|
const WebMTracksParser::TextTracks& text_tracks,
|
||||||
const std::set<int64_t>& ignored_tracks,
|
const std::set<int64_t>& ignored_tracks,
|
||||||
const std::string& audio_encryption_key_id,
|
const std::string& audio_encryption_key_id,
|
||||||
const std::string& video_encryption_key_id,
|
const std::string& video_encryption_key_id,
|
||||||
const AudioCodec audio_codec)
|
const AudioCodec audio_codec,
|
||||||
|
const MediaParser::NewSampleCB& new_sample_cb)
|
||||||
: timecode_multiplier_(timecode_scale / 1000.0),
|
: timecode_multiplier_(timecode_scale / 1000.0),
|
||||||
ignored_tracks_(ignored_tracks),
|
ignored_tracks_(ignored_tracks),
|
||||||
audio_encryption_key_id_(audio_encryption_key_id),
|
audio_encryption_key_id_(audio_encryption_key_id),
|
||||||
video_encryption_key_id_(video_encryption_key_id),
|
video_encryption_key_id_(video_encryption_key_id),
|
||||||
audio_codec_(audio_codec),
|
audio_codec_(audio_codec),
|
||||||
parser_(kWebMIdCluster, this),
|
parser_(kWebMIdCluster, this),
|
||||||
cluster_start_time_(kNoTimestamp()),
|
cluster_start_time_(kNoTimestamp),
|
||||||
audio_(audio_track_num, false, audio_default_duration),
|
audio_(audio_track_num, false, audio_default_duration, new_sample_cb),
|
||||||
video_(video_track_num, true, video_default_duration),
|
video_(video_track_num, true, video_default_duration, new_sample_cb) {
|
||||||
ready_buffer_upper_bound_(kNoDecodeTimestamp()) {
|
|
||||||
for (WebMTracksParser::TextTracks::const_iterator it = text_tracks.begin();
|
for (WebMTracksParser::TextTracks::const_iterator it = text_tracks.begin();
|
||||||
it != text_tracks.end();
|
it != text_tracks.end();
|
||||||
++it) {
|
++it) {
|
||||||
text_track_map_.insert(
|
text_track_map_.insert(std::make_pair(
|
||||||
std::make_pair(it->first, Track(it->first, false, kNoTimestamp())));
|
it->first, Track(it->first, false, kNoTimestamp, new_sample_cb)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,21 +85,15 @@ WebMClusterParser::~WebMClusterParser() {}
|
||||||
void WebMClusterParser::Reset() {
|
void WebMClusterParser::Reset() {
|
||||||
last_block_timecode_ = -1;
|
last_block_timecode_ = -1;
|
||||||
cluster_timecode_ = -1;
|
cluster_timecode_ = -1;
|
||||||
cluster_start_time_ = kNoTimestamp();
|
cluster_start_time_ = kNoTimestamp;
|
||||||
cluster_ended_ = false;
|
cluster_ended_ = false;
|
||||||
parser_.Reset();
|
parser_.Reset();
|
||||||
audio_.Reset();
|
audio_.Reset();
|
||||||
video_.Reset();
|
video_.Reset();
|
||||||
ResetTextTracks();
|
ResetTextTracks();
|
||||||
ready_buffer_upper_bound_ = kNoDecodeTimestamp();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int WebMClusterParser::Parse(const uint8_t* buf, int size) {
|
int WebMClusterParser::Parse(const uint8_t* buf, int size) {
|
||||||
audio_.ClearReadyBuffers();
|
|
||||||
video_.ClearReadyBuffers();
|
|
||||||
ClearTextTrackReadyBuffers();
|
|
||||||
ready_buffer_upper_bound_ = kNoDecodeTimestamp();
|
|
||||||
|
|
||||||
int result = parser_.Parse(buf, size);
|
int result = parser_.Parse(buf, size);
|
||||||
|
|
||||||
if (result < 0) {
|
if (result < 0) {
|
||||||
|
@ -105,16 +103,18 @@ int WebMClusterParser::Parse(const uint8_t* buf, int size) {
|
||||||
|
|
||||||
cluster_ended_ = parser_.IsParsingComplete();
|
cluster_ended_ = parser_.IsParsingComplete();
|
||||||
if (cluster_ended_) {
|
if (cluster_ended_) {
|
||||||
|
audio_.ApplyDurationEstimateIfNeeded();
|
||||||
|
video_.ApplyDurationEstimateIfNeeded();
|
||||||
|
|
||||||
// If there were no buffers in this cluster, set the cluster start time to
|
// If there were no buffers in this cluster, set the cluster start time to
|
||||||
// be the |cluster_timecode_|.
|
// be the |cluster_timecode_|.
|
||||||
if (cluster_start_time_ == kNoTimestamp()) {
|
if (cluster_start_time_ == kNoTimestamp) {
|
||||||
// If the cluster did not even have a |cluster_timecode_|, signal parse
|
// If the cluster did not even have a |cluster_timecode_|, signal parse
|
||||||
// error.
|
// error.
|
||||||
if (cluster_timecode_ < 0)
|
if (cluster_timecode_ < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
cluster_start_time_ = base::TimeDelta::FromMicroseconds(
|
cluster_start_time_ = cluster_timecode_ * timecode_multiplier_;
|
||||||
cluster_timecode_ * timecode_multiplier_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset the parser if we're done parsing so that
|
// Reset the parser if we're done parsing so that
|
||||||
|
@ -129,40 +129,7 @@ int WebMClusterParser::Parse(const uint8_t* buf, int size) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
const WebMClusterParser::BufferQueue& WebMClusterParser::GetAudioBuffers() {
|
int64_t WebMClusterParser::TryGetEncodedAudioDuration(
|
||||||
if (ready_buffer_upper_bound_ == kNoDecodeTimestamp())
|
|
||||||
UpdateReadyBuffers();
|
|
||||||
|
|
||||||
return audio_.ready_buffers();
|
|
||||||
}
|
|
||||||
|
|
||||||
const WebMClusterParser::BufferQueue& WebMClusterParser::GetVideoBuffers() {
|
|
||||||
if (ready_buffer_upper_bound_ == kNoDecodeTimestamp())
|
|
||||||
UpdateReadyBuffers();
|
|
||||||
|
|
||||||
return video_.ready_buffers();
|
|
||||||
}
|
|
||||||
|
|
||||||
const WebMClusterParser::TextBufferQueueMap&
|
|
||||||
WebMClusterParser::GetTextBuffers() {
|
|
||||||
if (ready_buffer_upper_bound_ == kNoDecodeTimestamp())
|
|
||||||
UpdateReadyBuffers();
|
|
||||||
|
|
||||||
// Translate our |text_track_map_| into |text_buffers_map_|, inserting rows in
|
|
||||||
// the output only for non-empty ready_buffer() queues in |text_track_map_|.
|
|
||||||
text_buffers_map_.clear();
|
|
||||||
for (TextTrackMap::const_iterator itr = text_track_map_.begin();
|
|
||||||
itr != text_track_map_.end();
|
|
||||||
++itr) {
|
|
||||||
const BufferQueue& text_buffers = itr->second.ready_buffers();
|
|
||||||
if (!text_buffers.empty())
|
|
||||||
text_buffers_map_.insert(std::make_pair(itr->first, text_buffers));
|
|
||||||
}
|
|
||||||
|
|
||||||
return text_buffers_map_;
|
|
||||||
}
|
|
||||||
|
|
||||||
base::TimeDelta WebMClusterParser::TryGetEncodedAudioDuration(
|
|
||||||
const uint8_t* data,
|
const uint8_t* data,
|
||||||
int size) {
|
int size) {
|
||||||
|
|
||||||
|
@ -179,24 +146,22 @@ base::TimeDelta WebMClusterParser::TryGetEncodedAudioDuration(
|
||||||
// TODO(wolenetz/chcunningham): Implement duration reading for Vorbis. See
|
// TODO(wolenetz/chcunningham): Implement duration reading for Vorbis. See
|
||||||
// motivations in http://crbug.com/396634.
|
// motivations in http://crbug.com/396634.
|
||||||
|
|
||||||
return kNoTimestamp();
|
return kNoTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
base::TimeDelta WebMClusterParser::ReadOpusDuration(const uint8_t* data,
|
int64_t WebMClusterParser::ReadOpusDuration(const uint8_t* data, int size) {
|
||||||
int size) {
|
|
||||||
// Masks and constants for Opus packets. See
|
// Masks and constants for Opus packets. See
|
||||||
// https://tools.ietf.org/html/rfc6716#page-14
|
// https://tools.ietf.org/html/rfc6716#page-14
|
||||||
static const uint8_t kTocConfigMask = 0xf8;
|
static const uint8_t kTocConfigMask = 0xf8;
|
||||||
static const uint8_t kTocFrameCountCodeMask = 0x03;
|
static const uint8_t kTocFrameCountCodeMask = 0x03;
|
||||||
static const uint8_t kFrameCountMask = 0x3f;
|
static const uint8_t kFrameCountMask = 0x3f;
|
||||||
static const base::TimeDelta kPacketDurationMax =
|
static const int64_t kPacketDurationMax = 120;
|
||||||
base::TimeDelta::FromMilliseconds(120);
|
|
||||||
|
|
||||||
if (size < 1) {
|
if (size < 1) {
|
||||||
LIMITED_DLOG(INFO, num_duration_errors_, kMaxDurationErrorLogs)
|
LIMITED_DLOG(INFO, num_duration_errors_, kMaxDurationErrorLogs)
|
||||||
<< "Invalid zero-byte Opus packet; demuxed block duration may be "
|
<< "Invalid zero-byte Opus packet; demuxed block duration may be "
|
||||||
"imprecise.";
|
"imprecise.";
|
||||||
return kNoTimestamp();
|
return kNoTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Frame count type described by last 2 bits of Opus TOC byte.
|
// Frame count type described by last 2 bits of Opus TOC byte.
|
||||||
|
@ -217,7 +182,7 @@ base::TimeDelta WebMClusterParser::ReadOpusDuration(const uint8_t* data,
|
||||||
LIMITED_DLOG(INFO, num_duration_errors_, kMaxDurationErrorLogs)
|
LIMITED_DLOG(INFO, num_duration_errors_, kMaxDurationErrorLogs)
|
||||||
<< "Second byte missing from 'Code 3' Opus packet; demuxed block "
|
<< "Second byte missing from 'Code 3' Opus packet; demuxed block "
|
||||||
"duration may be imprecise.";
|
"duration may be imprecise.";
|
||||||
return kNoTimestamp();
|
return kNoTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
frame_count = data[1] & kFrameCountMask;
|
frame_count = data[1] & kFrameCountMask;
|
||||||
|
@ -226,7 +191,7 @@ base::TimeDelta WebMClusterParser::ReadOpusDuration(const uint8_t* data,
|
||||||
LIMITED_DLOG(INFO, num_duration_errors_, kMaxDurationErrorLogs)
|
LIMITED_DLOG(INFO, num_duration_errors_, kMaxDurationErrorLogs)
|
||||||
<< "Illegal 'Code 3' Opus packet with frame count zero; demuxed "
|
<< "Illegal 'Code 3' Opus packet with frame count zero; demuxed "
|
||||||
"block duration may be imprecise.";
|
"block duration may be imprecise.";
|
||||||
return kNoTimestamp();
|
return kNoTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -234,7 +199,7 @@ base::TimeDelta WebMClusterParser::ReadOpusDuration(const uint8_t* data,
|
||||||
LIMITED_DLOG(INFO, num_duration_errors_, kMaxDurationErrorLogs)
|
LIMITED_DLOG(INFO, num_duration_errors_, kMaxDurationErrorLogs)
|
||||||
<< "Unexpected Opus frame count type: " << frame_count_type << "; "
|
<< "Unexpected Opus frame count type: " << frame_count_type << "; "
|
||||||
<< "demuxed block duration may be imprecise.";
|
<< "demuxed block duration may be imprecise.";
|
||||||
return kNoTimestamp();
|
return kNoTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
int opusConfig = (data[0] & kTocConfigMask) >> 3;
|
int opusConfig = (data[0] & kTocConfigMask) >> 3;
|
||||||
|
@ -242,8 +207,7 @@ base::TimeDelta WebMClusterParser::ReadOpusDuration(const uint8_t* data,
|
||||||
CHECK_LT(opusConfig, static_cast<int>(arraysize(kOpusFrameDurationsMu)));
|
CHECK_LT(opusConfig, static_cast<int>(arraysize(kOpusFrameDurationsMu)));
|
||||||
|
|
||||||
DCHECK_GT(frame_count, 0);
|
DCHECK_GT(frame_count, 0);
|
||||||
base::TimeDelta duration = base::TimeDelta::FromMicroseconds(
|
int64_t duration = kOpusFrameDurationsMu[opusConfig] * frame_count;
|
||||||
kOpusFrameDurationsMu[opusConfig] * frame_count);
|
|
||||||
|
|
||||||
if (duration > kPacketDurationMax) {
|
if (duration > kPacketDurationMax) {
|
||||||
// Intentionally allowing packet to pass through for now. Decoder should
|
// Intentionally allowing packet to pass through for now. Decoder should
|
||||||
|
@ -251,8 +215,8 @@ base::TimeDelta WebMClusterParser::ReadOpusDuration(const uint8_t* data,
|
||||||
// things go sideways.
|
// things go sideways.
|
||||||
LIMITED_DLOG(INFO, num_duration_errors_, kMaxDurationErrorLogs)
|
LIMITED_DLOG(INFO, num_duration_errors_, kMaxDurationErrorLogs)
|
||||||
<< "Warning, demuxed Opus packet with encoded duration: "
|
<< "Warning, demuxed Opus packet with encoded duration: "
|
||||||
<< duration.InMilliseconds() << "ms. Should be no greater than "
|
<< duration << "ms. Should be no greater than "
|
||||||
<< kPacketDurationMax.InMilliseconds() << "ms.";
|
<< kPacketDurationMax << "ms.";
|
||||||
}
|
}
|
||||||
|
|
||||||
return duration;
|
return duration;
|
||||||
|
@ -261,7 +225,7 @@ base::TimeDelta WebMClusterParser::ReadOpusDuration(const uint8_t* data,
|
||||||
WebMParserClient* WebMClusterParser::OnListStart(int id) {
|
WebMParserClient* WebMClusterParser::OnListStart(int id) {
|
||||||
if (id == kWebMIdCluster) {
|
if (id == kWebMIdCluster) {
|
||||||
cluster_timecode_ = -1;
|
cluster_timecode_ = -1;
|
||||||
cluster_start_time_ = kNoTimestamp();
|
cluster_start_time_ = kNoTimestamp;
|
||||||
} else if (id == kWebMIdBlockGroup) {
|
} else if (id == kWebMIdBlockGroup) {
|
||||||
block_data_.reset();
|
block_data_.reset();
|
||||||
block_data_size_ = -1;
|
block_data_size_ = -1;
|
||||||
|
@ -444,9 +408,9 @@ bool WebMClusterParser::OnBlock(bool is_simple_block,
|
||||||
}
|
}
|
||||||
|
|
||||||
Track* track = NULL;
|
Track* track = NULL;
|
||||||
StreamParserBuffer::Type buffer_type = DemuxerStream::AUDIO;
|
StreamType stream_type = kStreamAudio;
|
||||||
std::string encryption_key_id;
|
std::string encryption_key_id;
|
||||||
base::TimeDelta encoded_duration = kNoTimestamp();
|
int64_t encoded_duration = kNoTimestamp;
|
||||||
if (track_num == audio_.track_num()) {
|
if (track_num == audio_.track_num()) {
|
||||||
track = &audio_;
|
track = &audio_;
|
||||||
encryption_key_id = audio_encryption_key_id_;
|
encryption_key_id = audio_encryption_key_id_;
|
||||||
|
@ -456,7 +420,7 @@ bool WebMClusterParser::OnBlock(bool is_simple_block,
|
||||||
} else if (track_num == video_.track_num()) {
|
} else if (track_num == video_.track_num()) {
|
||||||
track = &video_;
|
track = &video_;
|
||||||
encryption_key_id = video_encryption_key_id_;
|
encryption_key_id = video_encryption_key_id_;
|
||||||
buffer_type = DemuxerStream::VIDEO;
|
stream_type = kStreamVideo;
|
||||||
} else if (ignored_tracks_.find(track_num) != ignored_tracks_.end()) {
|
} else if (ignored_tracks_.find(track_num) != ignored_tracks_.end()) {
|
||||||
return true;
|
return true;
|
||||||
} else if (Track* const text_track = FindTextTrack(track_num)) {
|
} else if (Track* const text_track = FindTextTrack(track_num)) {
|
||||||
|
@ -465,7 +429,7 @@ bool WebMClusterParser::OnBlock(bool is_simple_block,
|
||||||
if (block_duration < 0) // not specified
|
if (block_duration < 0) // not specified
|
||||||
return false;
|
return false;
|
||||||
track = text_track;
|
track = text_track;
|
||||||
buffer_type = DemuxerStream::TEXT;
|
stream_type = kStreamText;
|
||||||
} else {
|
} else {
|
||||||
LOG(ERROR) << "Unexpected track number " << track_num;
|
LOG(ERROR) << "Unexpected track number " << track_num;
|
||||||
return false;
|
return false;
|
||||||
|
@ -473,11 +437,10 @@ bool WebMClusterParser::OnBlock(bool is_simple_block,
|
||||||
|
|
||||||
last_block_timecode_ = timecode;
|
last_block_timecode_ = timecode;
|
||||||
|
|
||||||
base::TimeDelta timestamp = base::TimeDelta::FromMicroseconds(
|
int64_t timestamp = (cluster_timecode_ + timecode) * timecode_multiplier_;
|
||||||
(cluster_timecode_ + timecode) * timecode_multiplier_);
|
|
||||||
|
|
||||||
scoped_refptr<StreamParserBuffer> buffer;
|
scoped_refptr<MediaSample> buffer;
|
||||||
if (buffer_type != DemuxerStream::TEXT) {
|
if (stream_type != kStreamText) {
|
||||||
// The first bit of the flags is set when a SimpleBlock contains only
|
// 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
|
// keyframes. If this is a Block, then inspection of the payload is
|
||||||
// necessary to determine whether it contains a keyframe or not.
|
// necessary to determine whether it contains a keyframe or not.
|
||||||
|
@ -499,16 +462,13 @@ bool WebMClusterParser::OnBlock(bool is_simple_block,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(wolenetz/acolwell): Validate and use a common cross-parser TrackId
|
buffer = MediaSample::CopyFrom(data + data_offset, size - data_offset,
|
||||||
// type with remapped bytestream track numbers and allow multiple tracks as
|
additional, additional_size, is_keyframe);
|
||||||
// applicable. See https://crbug.com/341581.
|
|
||||||
buffer = StreamParserBuffer::CopyFrom(
|
|
||||||
data + data_offset, size - data_offset,
|
|
||||||
additional, additional_size,
|
|
||||||
is_keyframe, buffer_type, track_num);
|
|
||||||
|
|
||||||
if (decrypt_config)
|
if (decrypt_config) {
|
||||||
buffer->set_decrypt_config(decrypt_config.Pass());
|
// TODO(kqyang): Decrypt it if it is encrypted.
|
||||||
|
buffer->set_is_encrypted(true);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
std::string id, settings, content;
|
std::string id, settings, content;
|
||||||
WebMWebVTTParser::Parse(data, size, &id, &settings, &content);
|
WebMWebVTTParser::Parse(data, size, &id, &settings, &content);
|
||||||
|
@ -518,25 +478,18 @@ bool WebMClusterParser::OnBlock(bool is_simple_block,
|
||||||
settings.begin(), settings.end(),
|
settings.begin(), settings.end(),
|
||||||
&side_data);
|
&side_data);
|
||||||
|
|
||||||
// TODO(wolenetz/acolwell): Validate and use a common cross-parser TrackId
|
buffer = MediaSample::CopyFrom(
|
||||||
// type with remapped bytestream track numbers and allow multiple tracks as
|
reinterpret_cast<const uint8_t*>(content.data()), content.length(),
|
||||||
// applicable. See https://crbug.com/341581.
|
&side_data[0], side_data.size(), true);
|
||||||
buffer = StreamParserBuffer::CopyFrom(
|
|
||||||
reinterpret_cast<const uint8_t*>(content.data()),
|
|
||||||
content.length(),
|
|
||||||
&side_data[0],
|
|
||||||
side_data.size(),
|
|
||||||
true, buffer_type, track_num);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer->set_timestamp(timestamp);
|
buffer->set_pts(timestamp);
|
||||||
if (cluster_start_time_ == kNoTimestamp())
|
if (cluster_start_time_ == kNoTimestamp)
|
||||||
cluster_start_time_ = timestamp;
|
cluster_start_time_ = timestamp;
|
||||||
|
|
||||||
base::TimeDelta block_duration_time_delta = kNoTimestamp();
|
int64_t block_duration_time_delta = kNoTimestamp;
|
||||||
if (block_duration >= 0) {
|
if (block_duration >= 0) {
|
||||||
block_duration_time_delta = base::TimeDelta::FromMicroseconds(
|
block_duration_time_delta = block_duration * timecode_multiplier_;
|
||||||
block_duration * timecode_multiplier_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prefer encoded duration over BlockGroup->BlockDuration or
|
// Prefer encoded duration over BlockGroup->BlockDuration or
|
||||||
|
@ -550,126 +503,77 @@ bool WebMClusterParser::OnBlock(bool is_simple_block,
|
||||||
// as Block Timecode deltas, or once the whole cluster is parsed in the case
|
// as Block Timecode deltas, or once the whole cluster is parsed in the case
|
||||||
// of the last Block in the cluster. See Track::AddBuffer and
|
// of the last Block in the cluster. See Track::AddBuffer and
|
||||||
// ApplyDurationEstimateIfNeeded().
|
// ApplyDurationEstimateIfNeeded().
|
||||||
if (encoded_duration != kNoTimestamp()) {
|
if (encoded_duration != kNoTimestamp) {
|
||||||
DCHECK(encoded_duration != kInfiniteDuration());
|
DCHECK(encoded_duration != kInfiniteDuration);
|
||||||
DCHECK(encoded_duration > base::TimeDelta());
|
DCHECK(encoded_duration > 0);
|
||||||
buffer->set_duration(encoded_duration);
|
buffer->set_duration(encoded_duration);
|
||||||
|
|
||||||
DVLOG(3) << __FUNCTION__ << " : "
|
DVLOG(3) << __FUNCTION__ << " : "
|
||||||
<< "Using encoded duration " << encoded_duration.InSecondsF();
|
<< "Using encoded duration " << encoded_duration;
|
||||||
|
|
||||||
if (block_duration_time_delta != kNoTimestamp()) {
|
if (block_duration_time_delta != kNoTimestamp) {
|
||||||
base::TimeDelta duration_difference =
|
int64_t duration_difference =
|
||||||
block_duration_time_delta - encoded_duration;
|
block_duration_time_delta - encoded_duration;
|
||||||
|
|
||||||
const auto kWarnDurationDiff =
|
const auto kWarnDurationDiff = timecode_multiplier_ * 2;
|
||||||
base::TimeDelta::FromMicroseconds(timecode_multiplier_ * 2);
|
if (duration_difference > kWarnDurationDiff) {
|
||||||
if (duration_difference.magnitude() > kWarnDurationDiff) {
|
|
||||||
LIMITED_DLOG(INFO, num_duration_errors_, kMaxDurationErrorLogs)
|
LIMITED_DLOG(INFO, num_duration_errors_, kMaxDurationErrorLogs)
|
||||||
<< "BlockDuration (" << block_duration_time_delta.InMilliseconds()
|
<< "BlockDuration (" << block_duration_time_delta
|
||||||
<< "ms) differs significantly from encoded duration ("
|
<< "ms) differs significantly from encoded duration ("
|
||||||
<< encoded_duration.InMilliseconds() << "ms).";
|
<< encoded_duration << "ms).";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (block_duration_time_delta != kNoTimestamp()) {
|
} else if (block_duration_time_delta != kNoTimestamp) {
|
||||||
buffer->set_duration(block_duration_time_delta);
|
buffer->set_duration(block_duration_time_delta);
|
||||||
} else {
|
} else {
|
||||||
DCHECK_NE(buffer_type, DemuxerStream::TEXT);
|
|
||||||
buffer->set_duration(track->default_duration());
|
buffer->set_duration(track->default_duration());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (discard_padding != 0) {
|
|
||||||
buffer->set_discard_padding(std::make_pair(
|
|
||||||
base::TimeDelta(),
|
|
||||||
base::TimeDelta::FromMicroseconds(discard_padding / 1000)));
|
|
||||||
}
|
|
||||||
|
|
||||||
return track->AddBuffer(buffer);
|
return track->AddBuffer(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
WebMClusterParser::Track::Track(int track_num,
|
WebMClusterParser::Track::Track(int track_num,
|
||||||
bool is_video,
|
bool is_video,
|
||||||
base::TimeDelta default_duration)
|
int64_t default_duration,
|
||||||
|
const MediaParser::NewSampleCB& new_sample_cb)
|
||||||
: track_num_(track_num),
|
: track_num_(track_num),
|
||||||
is_video_(is_video),
|
is_video_(is_video),
|
||||||
default_duration_(default_duration),
|
default_duration_(default_duration),
|
||||||
estimated_next_frame_duration_(kNoTimestamp()) {
|
estimated_next_frame_duration_(kNoTimestamp),
|
||||||
DCHECK(default_duration_ == kNoTimestamp() ||
|
new_sample_cb_(new_sample_cb) {
|
||||||
default_duration_ > base::TimeDelta());
|
DCHECK(default_duration_ == kNoTimestamp || default_duration_ > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
WebMClusterParser::Track::~Track() {}
|
WebMClusterParser::Track::~Track() {}
|
||||||
|
|
||||||
DecodeTimestamp WebMClusterParser::Track::GetReadyUpperBound() {
|
|
||||||
DCHECK(ready_buffers_.empty());
|
|
||||||
if (last_added_buffer_missing_duration_.get())
|
|
||||||
return last_added_buffer_missing_duration_->GetDecodeTimestamp();
|
|
||||||
|
|
||||||
return DecodeTimestamp::FromPresentationTime(base::TimeDelta::Max());
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebMClusterParser::Track::ExtractReadyBuffers(
|
|
||||||
const DecodeTimestamp before_timestamp) {
|
|
||||||
DCHECK(ready_buffers_.empty());
|
|
||||||
DCHECK(DecodeTimestamp() <= before_timestamp);
|
|
||||||
DCHECK(kNoDecodeTimestamp() != before_timestamp);
|
|
||||||
|
|
||||||
if (buffers_.empty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (buffers_.back()->GetDecodeTimestamp() < before_timestamp) {
|
|
||||||
// All of |buffers_| are ready.
|
|
||||||
ready_buffers_.swap(buffers_);
|
|
||||||
DVLOG(3) << __FUNCTION__ << " : " << track_num_ << " All "
|
|
||||||
<< ready_buffers_.size() << " are ready: before upper bound ts "
|
|
||||||
<< before_timestamp.InSecondsF();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Not all of |buffers_| are ready yet. Move any that are ready to
|
|
||||||
// |ready_buffers_|.
|
|
||||||
while (true) {
|
|
||||||
const scoped_refptr<StreamParserBuffer>& buffer = buffers_.front();
|
|
||||||
if (buffer->GetDecodeTimestamp() >= before_timestamp)
|
|
||||||
break;
|
|
||||||
ready_buffers_.push_back(buffer);
|
|
||||||
buffers_.pop_front();
|
|
||||||
DCHECK(!buffers_.empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
DVLOG(3) << __FUNCTION__ << " : " << track_num_ << " Only "
|
|
||||||
<< ready_buffers_.size() << " ready, " << buffers_.size()
|
|
||||||
<< " at or after upper bound ts " << before_timestamp.InSecondsF();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WebMClusterParser::Track::AddBuffer(
|
bool WebMClusterParser::Track::AddBuffer(
|
||||||
const scoped_refptr<StreamParserBuffer>& buffer) {
|
const scoped_refptr<MediaSample>& buffer) {
|
||||||
DVLOG(2) << "AddBuffer() : " << track_num_
|
DVLOG(2) << "AddBuffer() : " << track_num_
|
||||||
<< " ts " << buffer->timestamp().InSecondsF()
|
<< " ts " << buffer->pts()
|
||||||
<< " dur " << buffer->duration().InSecondsF()
|
<< " dur " << buffer->duration()
|
||||||
<< " kf " << buffer->is_key_frame()
|
<< " kf " << buffer->is_key_frame()
|
||||||
<< " size " << buffer->data_size();
|
<< " size " << buffer->data_size();
|
||||||
|
|
||||||
if (last_added_buffer_missing_duration_.get()) {
|
if (last_added_buffer_missing_duration_.get()) {
|
||||||
base::TimeDelta derived_duration =
|
int64_t derived_duration =
|
||||||
buffer->timestamp() - last_added_buffer_missing_duration_->timestamp();
|
buffer->pts() - last_added_buffer_missing_duration_->pts();
|
||||||
last_added_buffer_missing_duration_->set_duration(derived_duration);
|
last_added_buffer_missing_duration_->set_duration(derived_duration);
|
||||||
|
|
||||||
DVLOG(2) << "AddBuffer() : applied derived duration to held-back buffer : "
|
DVLOG(2) << "AddBuffer() : applied derived duration to held-back buffer : "
|
||||||
<< " ts "
|
<< " ts "
|
||||||
<< last_added_buffer_missing_duration_->timestamp().InSecondsF()
|
<< last_added_buffer_missing_duration_->pts()
|
||||||
<< " dur "
|
<< " dur "
|
||||||
<< last_added_buffer_missing_duration_->duration().InSecondsF()
|
<< last_added_buffer_missing_duration_->duration()
|
||||||
<< " kf " << last_added_buffer_missing_duration_->is_key_frame()
|
<< " kf " << last_added_buffer_missing_duration_->is_key_frame()
|
||||||
<< " size " << last_added_buffer_missing_duration_->data_size();
|
<< " size " << last_added_buffer_missing_duration_->data_size();
|
||||||
scoped_refptr<StreamParserBuffer> updated_buffer =
|
scoped_refptr<MediaSample> updated_buffer =
|
||||||
last_added_buffer_missing_duration_;
|
last_added_buffer_missing_duration_;
|
||||||
last_added_buffer_missing_duration_ = NULL;
|
last_added_buffer_missing_duration_ = NULL;
|
||||||
if (!QueueBuffer(updated_buffer))
|
if (!QueueBuffer(updated_buffer))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buffer->duration() == kNoTimestamp()) {
|
if (buffer->duration() == kNoTimestamp) {
|
||||||
last_added_buffer_missing_duration_ = buffer;
|
last_added_buffer_missing_duration_ = buffer;
|
||||||
DVLOG(2) << "AddBuffer() : holding back buffer that is missing duration";
|
DVLOG(2) << "AddBuffer() : holding back buffer that is missing duration";
|
||||||
return true;
|
return true;
|
||||||
|
@ -682,46 +586,37 @@ void WebMClusterParser::Track::ApplyDurationEstimateIfNeeded() {
|
||||||
if (!last_added_buffer_missing_duration_.get())
|
if (!last_added_buffer_missing_duration_.get())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
base::TimeDelta estimated_duration = GetDurationEstimate();
|
int64_t estimated_duration = GetDurationEstimate();
|
||||||
last_added_buffer_missing_duration_->set_duration(estimated_duration);
|
last_added_buffer_missing_duration_->set_duration(estimated_duration);
|
||||||
|
|
||||||
if (is_video_) {
|
if (is_video_) {
|
||||||
// Exposing estimation so splicing/overlap frame processing can make
|
// Exposing estimation so splicing/overlap frame processing can make
|
||||||
// informed decisions downstream.
|
// informed decisions downstream.
|
||||||
// TODO(chcunningham): Set this for audio as well in later change where
|
// TODO(kqyang): Should we wait for the next cluster to set the duration?
|
||||||
// audio is switched to max estimation and splicing is disabled.
|
// last_added_buffer_missing_duration_->set_is_duration_estimated(true);
|
||||||
last_added_buffer_missing_duration_->set_is_duration_estimated(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LIMITED_LOG(INFO, num_duration_estimates_, kMaxDurationEstimateLogs)
|
LIMITED_LOG(INFO, num_duration_estimates_, kMaxDurationEstimateLogs)
|
||||||
<< "Estimating WebM block duration to be "
|
<< "Estimating WebM block duration to be "
|
||||||
<< estimated_duration.InMilliseconds()
|
<< estimated_duration
|
||||||
<< "ms for the last (Simple)Block in the Cluster for this Track. Use "
|
<< "ms for the last (Simple)Block in the Cluster for this Track. Use "
|
||||||
"BlockGroups with BlockDurations at the end of each Track in a "
|
"BlockGroups with BlockDurations at the end of each Track in a "
|
||||||
"Cluster to avoid estimation.";
|
"Cluster to avoid estimation.";
|
||||||
|
|
||||||
DVLOG(2) << __FUNCTION__ << " new dur : ts "
|
DVLOG(2) << __FUNCTION__ << " new dur : ts "
|
||||||
<< last_added_buffer_missing_duration_->timestamp().InSecondsF()
|
<< last_added_buffer_missing_duration_->pts()
|
||||||
<< " dur "
|
<< " dur "
|
||||||
<< last_added_buffer_missing_duration_->duration().InSecondsF()
|
<< last_added_buffer_missing_duration_->duration()
|
||||||
<< " kf " << last_added_buffer_missing_duration_->is_key_frame()
|
<< " kf " << last_added_buffer_missing_duration_->is_key_frame()
|
||||||
<< " size " << last_added_buffer_missing_duration_->data_size();
|
<< " size " << last_added_buffer_missing_duration_->data_size();
|
||||||
|
|
||||||
// Don't use the applied duration as a future estimation (don't use
|
// Don't use the applied duration as a future estimation (don't use
|
||||||
// QueueBuffer() here.)
|
// QueueBuffer() here.)
|
||||||
buffers_.push_back(last_added_buffer_missing_duration_);
|
new_sample_cb_.Run(track_num_, last_added_buffer_missing_duration_);
|
||||||
last_added_buffer_missing_duration_ = NULL;
|
last_added_buffer_missing_duration_ = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebMClusterParser::Track::ClearReadyBuffers() {
|
|
||||||
// Note that |buffers_| are kept and |estimated_next_frame_duration_| is not
|
|
||||||
// reset here.
|
|
||||||
ready_buffers_.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebMClusterParser::Track::Reset() {
|
void WebMClusterParser::Track::Reset() {
|
||||||
ClearReadyBuffers();
|
|
||||||
buffers_.clear();
|
|
||||||
last_added_buffer_missing_duration_ = NULL;
|
last_added_buffer_missing_duration_ = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -749,19 +644,12 @@ bool WebMClusterParser::Track::IsKeyframe(const uint8_t* data, int size) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WebMClusterParser::Track::QueueBuffer(
|
bool WebMClusterParser::Track::QueueBuffer(
|
||||||
const scoped_refptr<StreamParserBuffer>& buffer) {
|
const scoped_refptr<MediaSample>& buffer) {
|
||||||
DCHECK(!last_added_buffer_missing_duration_.get());
|
DCHECK(!last_added_buffer_missing_duration_.get());
|
||||||
|
|
||||||
// WebMClusterParser::OnBlock() gives LOG and parse error on decreasing
|
int64_t duration = buffer->duration();
|
||||||
// block timecode detection within a cluster. Therefore, we should not see
|
if (duration < 0 || duration == kNoTimestamp) {
|
||||||
// those here.
|
LOG(ERROR) << "Invalid buffer duration: " << duration;
|
||||||
DecodeTimestamp previous_buffers_timestamp = buffers_.empty() ?
|
|
||||||
DecodeTimestamp() : buffers_.back()->GetDecodeTimestamp();
|
|
||||||
CHECK(previous_buffers_timestamp <= buffer->GetDecodeTimestamp());
|
|
||||||
|
|
||||||
base::TimeDelta duration = buffer->duration();
|
|
||||||
if (duration < base::TimeDelta() || duration == kNoTimestamp()) {
|
|
||||||
LOG(ERROR) << "Invalid buffer duration: " << duration.InSecondsF();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -774,9 +662,9 @@ bool WebMClusterParser::Track::QueueBuffer(
|
||||||
// the over-estimated duration of the previous frame.
|
// the over-estimated duration of the previous frame.
|
||||||
// TODO(chcunningham): Use max for audio and disable splicing whenever
|
// TODO(chcunningham): Use max for audio and disable splicing whenever
|
||||||
// estimated buffers are encountered.
|
// estimated buffers are encountered.
|
||||||
if (duration > base::TimeDelta()) {
|
if (duration > 0) {
|
||||||
base::TimeDelta orig_duration_estimate = estimated_next_frame_duration_;
|
int64_t orig_duration_estimate = estimated_next_frame_duration_;
|
||||||
if (estimated_next_frame_duration_ == kNoTimestamp()) {
|
if (estimated_next_frame_duration_ == kNoTimestamp) {
|
||||||
estimated_next_frame_duration_ = duration;
|
estimated_next_frame_duration_ = duration;
|
||||||
} else if (is_video_) {
|
} else if (is_video_) {
|
||||||
estimated_next_frame_duration_ =
|
estimated_next_frame_duration_ =
|
||||||
|
@ -792,45 +680,33 @@ bool WebMClusterParser::Track::QueueBuffer(
|
||||||
<< " -> "
|
<< " -> "
|
||||||
<< estimated_next_frame_duration_
|
<< estimated_next_frame_duration_
|
||||||
<< " at timestamp: "
|
<< " at timestamp: "
|
||||||
<< buffer->GetDecodeTimestamp().InSecondsF();
|
<< buffer->dts();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buffers_.push_back(buffer);
|
new_sample_cb_.Run(track_num_, buffer);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
base::TimeDelta WebMClusterParser::Track::GetDurationEstimate() {
|
int64_t WebMClusterParser::Track::GetDurationEstimate() {
|
||||||
base::TimeDelta duration = estimated_next_frame_duration_;
|
int64_t duration = estimated_next_frame_duration_;
|
||||||
if (duration != kNoTimestamp()) {
|
if (duration != kNoTimestamp) {
|
||||||
DVLOG(3) << __FUNCTION__ << " : using estimated duration";
|
DVLOG(3) << __FUNCTION__ << " : using estimated duration";
|
||||||
} else {
|
} else {
|
||||||
DVLOG(3) << __FUNCTION__ << " : using hardcoded default duration";
|
DVLOG(3) << __FUNCTION__ << " : using hardcoded default duration";
|
||||||
if (is_video_) {
|
if (is_video_) {
|
||||||
duration = base::TimeDelta::FromMilliseconds(
|
duration = kDefaultVideoBufferDurationInMs * kMicrosecondsPerMillisecond;
|
||||||
kDefaultVideoBufferDurationInMs);
|
|
||||||
} else {
|
} else {
|
||||||
duration = base::TimeDelta::FromMilliseconds(
|
duration = kDefaultAudioBufferDurationInMs * kMicrosecondsPerMillisecond;
|
||||||
kDefaultAudioBufferDurationInMs);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DCHECK(duration > base::TimeDelta());
|
DCHECK(duration > 0);
|
||||||
DCHECK(duration != kNoTimestamp());
|
DCHECK(duration != kNoTimestamp);
|
||||||
return duration;
|
return duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebMClusterParser::ClearTextTrackReadyBuffers() {
|
|
||||||
text_buffers_map_.clear();
|
|
||||||
for (TextTrackMap::iterator it = text_track_map_.begin();
|
|
||||||
it != text_track_map_.end();
|
|
||||||
++it) {
|
|
||||||
it->second.ClearReadyBuffers();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void WebMClusterParser::ResetTextTracks() {
|
void WebMClusterParser::ResetTextTracks() {
|
||||||
ClearTextTrackReadyBuffers();
|
|
||||||
for (TextTrackMap::iterator it = text_track_map_.begin();
|
for (TextTrackMap::iterator it = text_track_map_.begin();
|
||||||
it != text_track_map_.end();
|
it != text_track_map_.end();
|
||||||
++it) {
|
++it) {
|
||||||
|
@ -838,37 +714,6 @@ void WebMClusterParser::ResetTextTracks() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebMClusterParser::UpdateReadyBuffers() {
|
|
||||||
DCHECK(ready_buffer_upper_bound_ == kNoDecodeTimestamp());
|
|
||||||
DCHECK(text_buffers_map_.empty());
|
|
||||||
|
|
||||||
if (cluster_ended_) {
|
|
||||||
audio_.ApplyDurationEstimateIfNeeded();
|
|
||||||
video_.ApplyDurationEstimateIfNeeded();
|
|
||||||
// Per OnBlock(), all text buffers should already have valid durations, so
|
|
||||||
// there is no need to call ApplyDurationEstimateIfNeeded() on text tracks
|
|
||||||
// here.
|
|
||||||
ready_buffer_upper_bound_ =
|
|
||||||
DecodeTimestamp::FromPresentationTime(base::TimeDelta::Max());
|
|
||||||
DCHECK(ready_buffer_upper_bound_ == audio_.GetReadyUpperBound());
|
|
||||||
DCHECK(ready_buffer_upper_bound_ == video_.GetReadyUpperBound());
|
|
||||||
} else {
|
|
||||||
ready_buffer_upper_bound_ = std::min(audio_.GetReadyUpperBound(),
|
|
||||||
video_.GetReadyUpperBound());
|
|
||||||
DCHECK(DecodeTimestamp() <= ready_buffer_upper_bound_);
|
|
||||||
DCHECK(kNoDecodeTimestamp() != ready_buffer_upper_bound_);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare each track's ready buffers for retrieval.
|
|
||||||
audio_.ExtractReadyBuffers(ready_buffer_upper_bound_);
|
|
||||||
video_.ExtractReadyBuffers(ready_buffer_upper_bound_);
|
|
||||||
for (TextTrackMap::iterator itr = text_track_map_.begin();
|
|
||||||
itr != text_track_map_.end();
|
|
||||||
++itr) {
|
|
||||||
itr->second.ExtractReadyBuffers(ready_buffer_upper_bound_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
WebMClusterParser::Track*
|
WebMClusterParser::Track*
|
||||||
WebMClusterParser::FindTextTrack(int track_num) {
|
WebMClusterParser::FindTextTrack(int track_num) {
|
||||||
const TextTrackMap::iterator it = text_track_map_.find(track_num);
|
const TextTrackMap::iterator it = text_track_map_.find(track_num);
|
||||||
|
|
|
@ -11,9 +11,8 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "packager/base/memory/scoped_ptr.h"
|
#include "packager/base/memory/scoped_ptr.h"
|
||||||
#include "packager/media/base/audio_decoder_config.h"
|
#include "packager/media/base/media_parser.h"
|
||||||
#include "packager/media/base/stream_parser.h"
|
#include "packager/media/base/media_sample.h"
|
||||||
#include "packager/media/base/stream_parser_buffer.h"
|
|
||||||
#include "packager/media/formats/webm/webm_parser.h"
|
#include "packager/media/formats/webm/webm_parser.h"
|
||||||
#include "packager/media/formats/webm/webm_tracks_parser.h"
|
#include "packager/media/formats/webm/webm_tracks_parser.h"
|
||||||
|
|
||||||
|
@ -22,10 +21,6 @@ namespace media {
|
||||||
|
|
||||||
class WebMClusterParser : public WebMParserClient {
|
class WebMClusterParser : public WebMParserClient {
|
||||||
public:
|
public:
|
||||||
typedef StreamParser::TrackId TrackId;
|
|
||||||
typedef std::deque<scoped_refptr<StreamParserBuffer> > BufferQueue;
|
|
||||||
typedef std::map<TrackId, const BufferQueue> TextBufferQueueMap;
|
|
||||||
|
|
||||||
// Numbers chosen to estimate the duration of a buffer if none is set and
|
// Numbers chosen to estimate the duration of a buffer if none is set and
|
||||||
// there is not enough information to get a better estimate.
|
// there is not enough information to get a better estimate.
|
||||||
enum {
|
enum {
|
||||||
|
@ -49,29 +44,18 @@ class WebMClusterParser : public WebMParserClient {
|
||||||
public:
|
public:
|
||||||
Track(int track_num,
|
Track(int track_num,
|
||||||
bool is_video,
|
bool is_video,
|
||||||
base::TimeDelta default_duration);
|
int64_t default_duration,
|
||||||
|
const MediaParser::NewSampleCB& new_sample_cb);
|
||||||
~Track();
|
~Track();
|
||||||
|
|
||||||
int track_num() const { return track_num_; }
|
int track_num() const { return track_num_; }
|
||||||
|
|
||||||
// If a buffer is currently held aside pending duration calculation, returns
|
|
||||||
// its decode timestamp. Otherwise, returns kInfiniteDuration().
|
|
||||||
DecodeTimestamp GetReadyUpperBound();
|
|
||||||
|
|
||||||
// Prepares |ready_buffers_| for retrieval. Prior to calling,
|
|
||||||
// |ready_buffers_| must be empty. Moves all |buffers_| with decode
|
|
||||||
// timestamp before |before_timestamp| to |ready_buffers_|, preserving their
|
|
||||||
// order.
|
|
||||||
void ExtractReadyBuffers(const DecodeTimestamp before_timestamp);
|
|
||||||
|
|
||||||
const BufferQueue& ready_buffers() const { return ready_buffers_; }
|
|
||||||
|
|
||||||
// If |last_added_buffer_missing_duration_| is set, updates its duration
|
// If |last_added_buffer_missing_duration_| is set, updates its duration
|
||||||
// relative to |buffer|'s timestamp, and adds it to |buffers_| and unsets
|
// relative to |buffer|'s timestamp, and adds it to |buffers_| and unsets
|
||||||
// |last_added_buffer_missing_duration_|. Then, if |buffer| is missing
|
// |last_added_buffer_missing_duration_|. Then, if |buffer| is missing
|
||||||
// duration, saves |buffer| into |last_added_buffer_missing_duration_|, or
|
// duration, saves |buffer| into |last_added_buffer_missing_duration_|, or
|
||||||
// otherwise adds |buffer| to |buffers_|.
|
// otherwise adds |buffer| to |buffers_|.
|
||||||
bool AddBuffer(const scoped_refptr<StreamParserBuffer>& buffer);
|
bool AddBuffer(const scoped_refptr<MediaSample>& buffer);
|
||||||
|
|
||||||
// If |last_added_buffer_missing_duration_| is set, updates its duration to
|
// If |last_added_buffer_missing_duration_| is set, updates its duration to
|
||||||
// be non-kNoTimestamp() value of |estimated_next_frame_duration_| or a
|
// be non-kNoTimestamp() value of |estimated_next_frame_duration_| or a
|
||||||
|
@ -80,14 +64,8 @@ class WebMClusterParser : public WebMParserClient {
|
||||||
// emit all buffers in a media segment before signaling end of segment.)
|
// emit all buffers in a media segment before signaling end of segment.)
|
||||||
void ApplyDurationEstimateIfNeeded();
|
void ApplyDurationEstimateIfNeeded();
|
||||||
|
|
||||||
// Clears |ready_buffers_| (use ExtractReadyBuffers() to fill it again).
|
|
||||||
// Leaves as-is |buffers_| and any possibly held-aside buffer that is
|
|
||||||
// missing duration.
|
|
||||||
void ClearReadyBuffers();
|
|
||||||
|
|
||||||
// Clears all buffer state, including any possibly held-aside buffer that
|
// Clears all buffer state, including any possibly held-aside buffer that
|
||||||
// was missing duration, and all contents of |buffers_| and
|
// was missing duration, and all contents of |buffers_|.
|
||||||
// |ready_buffers_|.
|
|
||||||
void Reset();
|
void Reset();
|
||||||
|
|
||||||
// Helper function used to inspect block data to determine if the
|
// Helper function used to inspect block data to determine if the
|
||||||
|
@ -96,18 +74,18 @@ class WebMClusterParser : public WebMParserClient {
|
||||||
// |size| indicates the number of bytes in |data|.
|
// |size| indicates the number of bytes in |data|.
|
||||||
bool IsKeyframe(const uint8_t* data, int size) const;
|
bool IsKeyframe(const uint8_t* data, int size) const;
|
||||||
|
|
||||||
base::TimeDelta default_duration() const { return default_duration_; }
|
int64_t default_duration() const { return default_duration_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Helper that sanity-checks |buffer| duration, updates
|
// Helper that sanity-checks |buffer| duration, updates
|
||||||
// |estimated_next_frame_duration_|, and adds |buffer| to |buffers_|.
|
// |estimated_next_frame_duration_|, and adds |buffer| to |buffers_|.
|
||||||
// Returns false if |buffer| failed sanity check and therefore was not added
|
// Returns false if |buffer| failed sanity check and therefore was not added
|
||||||
// to |buffers_|. Returns true otherwise.
|
// to |buffers_|. Returns true otherwise.
|
||||||
bool QueueBuffer(const scoped_refptr<StreamParserBuffer>& buffer);
|
bool QueueBuffer(const scoped_refptr<MediaSample>& buffer);
|
||||||
|
|
||||||
// Helper that calculates the buffer duration to use in
|
// Helper that calculates the buffer duration to use in
|
||||||
// ApplyDurationEstimateIfNeeded().
|
// ApplyDurationEstimateIfNeeded().
|
||||||
base::TimeDelta GetDurationEstimate();
|
int64_t GetDurationEstimate();
|
||||||
|
|
||||||
// Counts the number of estimated durations used in this track. Used to
|
// Counts the number of estimated durations used in this track. Used to
|
||||||
// prevent log spam for LOG()s about estimated duration.
|
// prevent log spam for LOG()s about estimated duration.
|
||||||
|
@ -120,26 +98,19 @@ class WebMClusterParser : public WebMParserClient {
|
||||||
// that have not yet been extracted into |ready_buffers_|. Note that up to
|
// that have not yet been extracted into |ready_buffers_|. Note that up to
|
||||||
// one additional buffer missing duration may be tracked by
|
// one additional buffer missing duration may be tracked by
|
||||||
// |last_added_buffer_missing_duration_|.
|
// |last_added_buffer_missing_duration_|.
|
||||||
BufferQueue buffers_;
|
scoped_refptr<MediaSample> last_added_buffer_missing_duration_;
|
||||||
scoped_refptr<StreamParserBuffer> last_added_buffer_missing_duration_;
|
|
||||||
|
|
||||||
// Buffers in (decode) timestamp order that were previously parsed into and
|
|
||||||
// extracted from |buffers_|. Buffers are moved from |buffers_| to
|
|
||||||
// |ready_buffers_| by ExtractReadyBuffers() if they are below a specified
|
|
||||||
// upper bound timestamp. Track users can therefore extract only those
|
|
||||||
// parsed buffers which are "ready" for emission (all before some maximum
|
|
||||||
// timestamp).
|
|
||||||
BufferQueue ready_buffers_;
|
|
||||||
|
|
||||||
// If kNoTimestamp(), then |estimated_next_frame_duration_| will be used.
|
// If kNoTimestamp(), then |estimated_next_frame_duration_| will be used.
|
||||||
base::TimeDelta default_duration_;
|
int64_t default_duration_;
|
||||||
|
|
||||||
// If kNoTimestamp(), then a default value will be used. This estimate is
|
// If kNoTimestamp(), then a default value will be used. This estimate is
|
||||||
// the maximum (for video), or minimum (for audio) duration seen so far for
|
// the maximum (for video), or minimum (for audio) duration seen so far for
|
||||||
// this track, and is used only if |default_duration_| is kNoTimestamp().
|
// this track, and is used only if |default_duration_| is kNoTimestamp().
|
||||||
// TODO(chcunningham): Use maximum for audio too, adding checks to disable
|
// TODO(chcunningham): Use maximum for audio too, adding checks to disable
|
||||||
// splicing when these estimates are observed in SourceBufferStream.
|
// splicing when these estimates are observed in SourceBufferStream.
|
||||||
base::TimeDelta estimated_next_frame_duration_;
|
int64_t estimated_next_frame_duration_;
|
||||||
|
|
||||||
|
MediaParser::NewSampleCB new_sample_cb_;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::map<int, Track> TextTrackMap;
|
typedef std::map<int, Track> TextTrackMap;
|
||||||
|
@ -147,14 +118,15 @@ class WebMClusterParser : public WebMParserClient {
|
||||||
public:
|
public:
|
||||||
WebMClusterParser(int64_t timecode_scale,
|
WebMClusterParser(int64_t timecode_scale,
|
||||||
int audio_track_num,
|
int audio_track_num,
|
||||||
base::TimeDelta audio_default_duration,
|
int64_t audio_default_duration,
|
||||||
int video_track_num,
|
int video_track_num,
|
||||||
base::TimeDelta video_default_duration,
|
int64_t video_default_duration,
|
||||||
const WebMTracksParser::TextTracks& text_tracks,
|
const WebMTracksParser::TextTracks& text_tracks,
|
||||||
const std::set<int64_t>& ignored_tracks,
|
const std::set<int64_t>& ignored_tracks,
|
||||||
const std::string& audio_encryption_key_id,
|
const std::string& audio_encryption_key_id,
|
||||||
const std::string& video_encryption_key_id,
|
const std::string& video_encryption_key_id,
|
||||||
const AudioCodec audio_codec);
|
const AudioCodec audio_codec,
|
||||||
|
const MediaParser::NewSampleCB& new_sample_cb);
|
||||||
~WebMClusterParser() override;
|
~WebMClusterParser() override;
|
||||||
|
|
||||||
// Resets the parser state so it can accept a new cluster.
|
// Resets the parser state so it can accept a new cluster.
|
||||||
|
@ -167,35 +139,7 @@ class WebMClusterParser : public WebMParserClient {
|
||||||
// Returns the number of bytes parsed on success.
|
// Returns the number of bytes parsed on success.
|
||||||
int Parse(const uint8_t* buf, int size);
|
int Parse(const uint8_t* buf, int size);
|
||||||
|
|
||||||
base::TimeDelta cluster_start_time() const { return cluster_start_time_; }
|
int64_t cluster_start_time() const { return cluster_start_time_; }
|
||||||
|
|
||||||
// Get the current ready buffers resulting from Parse().
|
|
||||||
// If the parse reached the end of cluster and the last buffer was held aside
|
|
||||||
// due to missing duration, the buffer is given an estimated duration and
|
|
||||||
// included in the result.
|
|
||||||
// Otherwise, if there are is a buffer held aside due to missing duration for
|
|
||||||
// any of the tracks, no buffers with same or greater (decode) timestamp will
|
|
||||||
// be included in the buffers.
|
|
||||||
// The returned deques are cleared by Parse() or Reset() and updated by the
|
|
||||||
// next calls to Get{Audio,Video}Buffers().
|
|
||||||
// If no Parse() or Reset() has occurred since the last call to Get{Audio,
|
|
||||||
// Video,Text}Buffers(), then the previous BufferQueue& is returned again
|
|
||||||
// without any recalculation.
|
|
||||||
const BufferQueue& GetAudioBuffers();
|
|
||||||
const BufferQueue& GetVideoBuffers();
|
|
||||||
|
|
||||||
// Constructs and returns a subset of |text_track_map_| containing only
|
|
||||||
// tracks with non-empty buffer queues produced by the last Parse() and
|
|
||||||
// filtered to exclude any buffers that have (decode) timestamp same or
|
|
||||||
// greater than the lowest (decode) timestamp across all tracks of any buffer
|
|
||||||
// held aside due to missing duration (unless the end of cluster has been
|
|
||||||
// reached).
|
|
||||||
// The returned map is cleared by Parse() or Reset() and updated by the next
|
|
||||||
// call to GetTextBuffers().
|
|
||||||
// If no Parse() or Reset() has occurred since the last call to
|
|
||||||
// GetTextBuffers(), then the previous TextBufferQueueMap& is returned again
|
|
||||||
// without any recalculation.
|
|
||||||
const TextBufferQueueMap& GetTextBuffers();
|
|
||||||
|
|
||||||
// Returns true if the last Parse() call stopped at the end of a cluster.
|
// Returns true if the last Parse() call stopped at the end of a cluster.
|
||||||
bool cluster_ended() const { return cluster_ended_; }
|
bool cluster_ended() const { return cluster_ended_; }
|
||||||
|
@ -228,22 +172,6 @@ class WebMClusterParser : public WebMParserClient {
|
||||||
// Resets the Track objects associated with each text track.
|
// Resets the Track objects associated with each text track.
|
||||||
void ResetTextTracks();
|
void ResetTextTracks();
|
||||||
|
|
||||||
// Clears the the ready buffers associated with each text track.
|
|
||||||
void ClearTextTrackReadyBuffers();
|
|
||||||
|
|
||||||
// Helper method for Get{Audio,Video,Text}Buffers() that recomputes
|
|
||||||
// |ready_buffer_upper_bound_| and calls ExtractReadyBuffers() on each track.
|
|
||||||
// If |cluster_ended_| is true, first applies duration estimate if needed for
|
|
||||||
// |audio_| and |video_| and sets |ready_buffer_upper_bound_| to
|
|
||||||
// kInfiniteDuration(). Otherwise, sets |ready_buffer_upper_bound_| to the
|
|
||||||
// minimum upper bound across |audio_| and |video_|. (Text tracks can have no
|
|
||||||
// buffers missing duration, so they are not involved in calculating the upper
|
|
||||||
// bound.)
|
|
||||||
// Parse() or Reset() must be called between calls to UpdateReadyBuffers() to
|
|
||||||
// clear each track's ready buffers and to reset |ready_buffer_upper_bound_|
|
|
||||||
// to kNoDecodeTimestamp().
|
|
||||||
void UpdateReadyBuffers();
|
|
||||||
|
|
||||||
// Search for the indicated track_num among the text tracks. Returns NULL
|
// Search for the indicated track_num among the text tracks. Returns NULL
|
||||||
// if that track num is not a text track.
|
// if that track num is not a text track.
|
||||||
Track* FindTextTrack(int track_num);
|
Track* FindTextTrack(int track_num);
|
||||||
|
@ -256,11 +184,11 @@ class WebMClusterParser : public WebMParserClient {
|
||||||
// Cluster we parse, so we can't simply use the delta of the first Block in
|
// Cluster we parse, so we can't simply use the delta of the first Block in
|
||||||
// the next Cluster). Avoid calling if encrypted; may produce unexpected
|
// the next Cluster). Avoid calling if encrypted; may produce unexpected
|
||||||
// output. See implementation for supported codecs.
|
// output. See implementation for supported codecs.
|
||||||
base::TimeDelta TryGetEncodedAudioDuration(const uint8_t* data, int size);
|
int64_t TryGetEncodedAudioDuration(const uint8_t* data, int size);
|
||||||
|
|
||||||
// Reads Opus packet header to determine packet duration. Duration returned
|
// Reads Opus packet header to determine packet duration. Duration returned
|
||||||
// as TimeDelta or kNoTimestamp() upon failure to read duration from packet.
|
// as TimeDelta or kNoTimestamp() upon failure to read duration from packet.
|
||||||
base::TimeDelta ReadOpusDuration(const uint8_t* data, int size);
|
int64_t ReadOpusDuration(const uint8_t* data, int size);
|
||||||
|
|
||||||
// Tracks the number of LOGs made in process of reading encoded
|
// Tracks the number of LOGs made in process of reading encoded
|
||||||
// duration. Useful to prevent log spam.
|
// duration. Useful to prevent log spam.
|
||||||
|
@ -290,26 +218,13 @@ class WebMClusterParser : public WebMParserClient {
|
||||||
bool discard_padding_set_ = false;
|
bool discard_padding_set_ = false;
|
||||||
|
|
||||||
int64_t cluster_timecode_ = -1;
|
int64_t cluster_timecode_ = -1;
|
||||||
base::TimeDelta cluster_start_time_;
|
int64_t cluster_start_time_;
|
||||||
bool cluster_ended_ = false;
|
bool cluster_ended_ = false;
|
||||||
|
|
||||||
Track audio_;
|
Track audio_;
|
||||||
Track video_;
|
Track video_;
|
||||||
TextTrackMap text_track_map_;
|
TextTrackMap text_track_map_;
|
||||||
|
|
||||||
// Subset of |text_track_map_| maintained by GetTextBuffers(), and cleared by
|
|
||||||
// ClearTextTrackReadyBuffers(). Callers of GetTextBuffers() get a const-ref
|
|
||||||
// to this member.
|
|
||||||
TextBufferQueueMap text_buffers_map_;
|
|
||||||
|
|
||||||
// Limits the range of buffers returned by Get{Audio,Video,Text}Buffers() to
|
|
||||||
// this exclusive upper bound. Set to kNoDecodeTimestamp(), meaning not yet
|
|
||||||
// calculated, by Reset() and Parse(). If kNoDecodeTimestamp(), then
|
|
||||||
// Get{Audio,Video,Text}Buffers() will calculate it to be the minimum (decode)
|
|
||||||
// timestamp across all tracks' |last_buffer_missing_duration_|, or
|
|
||||||
// kInfiniteDuration() if no buffers are currently missing duration.
|
|
||||||
DecodeTimestamp ready_buffer_upper_bound_;
|
|
||||||
|
|
||||||
DISALLOW_IMPLICIT_CONSTRUCTORS(WebMClusterParser);
|
DISALLOW_IMPLICIT_CONSTRUCTORS(WebMClusterParser);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -15,9 +15,8 @@
|
||||||
#include "packager/base/bind.h"
|
#include "packager/base/bind.h"
|
||||||
#include "packager/base/logging.h"
|
#include "packager/base/logging.h"
|
||||||
#include "packager/base/strings/string_number_conversions.h"
|
#include "packager/base/strings/string_number_conversions.h"
|
||||||
#include "packager/media/base/audio_decoder_config.h"
|
|
||||||
#include "packager/media/base/decrypt_config.h"
|
#include "packager/media/base/decrypt_config.h"
|
||||||
#include "packager/media/base/timestamp_constants.h"
|
#include "packager/media/base/timestamp.h"
|
||||||
#include "packager/media/formats/webm/cluster_builder.h"
|
#include "packager/media/formats/webm/cluster_builder.h"
|
||||||
#include "packager/media/formats/webm/opus_packet_builder.h"
|
#include "packager/media/formats/webm/opus_packet_builder.h"
|
||||||
#include "packager/media/formats/webm/webm_constants.h"
|
#include "packager/media/formats/webm/webm_constants.h"
|
||||||
|
@ -29,10 +28,15 @@ using ::testing::StrictMock;
|
||||||
using ::testing::Mock;
|
using ::testing::Mock;
|
||||||
using ::testing::_;
|
using ::testing::_;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
const int64_t kMicrosecondsPerMillisecond = 1000;
|
||||||
|
} // namespace
|
||||||
|
|
||||||
namespace edash_packager {
|
namespace edash_packager {
|
||||||
namespace media {
|
namespace media {
|
||||||
|
|
||||||
typedef WebMTracksParser::TextTracks TextTracks;
|
typedef WebMTracksParser::TextTracks TextTracks;
|
||||||
|
typedef std::map<uint32_t, BufferQueue> TextBufferQueueMap;
|
||||||
|
|
||||||
// Matchers for verifying common media log entry strings.
|
// Matchers for verifying common media log entry strings.
|
||||||
MATCHER_P(OpusPacketDurationTooHigh, actual_duration_ms, "") {
|
MATCHER_P(OpusPacketDurationTooHigh, actual_duration_ms, "") {
|
||||||
|
@ -115,9 +119,11 @@ const BlockInfo kDefaultBlockInfo[] = {
|
||||||
const uint8_t kEncryptedFrame[] = {
|
const uint8_t kEncryptedFrame[] = {
|
||||||
// Block is encrypted
|
// Block is encrypted
|
||||||
0x01,
|
0x01,
|
||||||
|
|
||||||
// IV
|
// IV
|
||||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
|
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
||||||
|
// Some dummy encrypted data
|
||||||
|
0x01,
|
||||||
|
};
|
||||||
|
|
||||||
scoped_ptr<Cluster> CreateCluster(int timecode,
|
scoped_ptr<Cluster> CreateCluster(int timecode,
|
||||||
const BlockInfo* block_info,
|
const BlockInfo* block_info,
|
||||||
|
@ -125,7 +131,8 @@ scoped_ptr<Cluster> CreateCluster(int timecode,
|
||||||
ClusterBuilder cb;
|
ClusterBuilder cb;
|
||||||
cb.SetClusterTimecode(0);
|
cb.SetClusterTimecode(0);
|
||||||
|
|
||||||
uint8_t kDefaultBlockData[] = { 0x00 };
|
// Default block data for audio, video and text.
|
||||||
|
uint8_t kDefaultBlockData[] = {0x00, 0x0A, 0x01, 0x0D, 0x02};
|
||||||
|
|
||||||
for (int i = 0; i < block_count; i++) {
|
for (int i = 0; i < block_count; i++) {
|
||||||
const uint8_t* data;
|
const uint8_t* data;
|
||||||
|
@ -171,16 +178,16 @@ scoped_ptr<Cluster> CreateEncryptedCluster(int bytes_to_write) {
|
||||||
return cb.Finish();
|
return cb.Finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VerifyBuffers(const WebMClusterParser::BufferQueue& audio_buffers,
|
bool VerifyBuffersHelper(const BufferQueue& audio_buffers,
|
||||||
const WebMClusterParser::BufferQueue& video_buffers,
|
const BufferQueue& video_buffers,
|
||||||
const WebMClusterParser::BufferQueue& text_buffers,
|
const BufferQueue& text_buffers,
|
||||||
const BlockInfo* block_info,
|
const BlockInfo* block_info,
|
||||||
int block_count) {
|
int block_count) {
|
||||||
int buffer_count = audio_buffers.size() + video_buffers.size() +
|
int buffer_count = audio_buffers.size() + video_buffers.size() +
|
||||||
text_buffers.size();
|
text_buffers.size();
|
||||||
if (block_count != buffer_count) {
|
if (block_count != buffer_count) {
|
||||||
DVLOG(1) << __FUNCTION__ << " : block_count (" << block_count
|
LOG(ERROR) << __FUNCTION__ << " : block_count (" << block_count
|
||||||
<< ") mismatches buffer_count (" << buffer_count << ")";
|
<< ") mismatches buffer_count (" << buffer_count << ")";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,73 +195,48 @@ bool VerifyBuffers(const WebMClusterParser::BufferQueue& audio_buffers,
|
||||||
size_t video_offset = 0;
|
size_t video_offset = 0;
|
||||||
size_t text_offset = 0;
|
size_t text_offset = 0;
|
||||||
for (int i = 0; i < block_count; i++) {
|
for (int i = 0; i < block_count; i++) {
|
||||||
const WebMClusterParser::BufferQueue* buffers = NULL;
|
const BufferQueue* buffers = NULL;
|
||||||
size_t* offset;
|
size_t* offset;
|
||||||
StreamParserBuffer::Type expected_type = DemuxerStream::UNKNOWN;
|
|
||||||
|
|
||||||
if (block_info[i].track_num == kAudioTrackNum) {
|
if (block_info[i].track_num == kAudioTrackNum) {
|
||||||
buffers = &audio_buffers;
|
buffers = &audio_buffers;
|
||||||
offset = &audio_offset;
|
offset = &audio_offset;
|
||||||
expected_type = DemuxerStream::AUDIO;
|
|
||||||
} else if (block_info[i].track_num == kVideoTrackNum) {
|
} else if (block_info[i].track_num == kVideoTrackNum) {
|
||||||
buffers = &video_buffers;
|
buffers = &video_buffers;
|
||||||
offset = &video_offset;
|
offset = &video_offset;
|
||||||
expected_type = DemuxerStream::VIDEO;
|
|
||||||
} else if (block_info[i].track_num == kTextTrackNum) {
|
} else if (block_info[i].track_num == kTextTrackNum) {
|
||||||
buffers = &text_buffers;
|
buffers = &text_buffers;
|
||||||
offset = &text_offset;
|
offset = &text_offset;
|
||||||
expected_type = DemuxerStream::TEXT;
|
|
||||||
} else {
|
} else {
|
||||||
LOG(ERROR) << "Unexpected track number " << block_info[i].track_num;
|
LOG(ERROR) << "Unexpected track number " << block_info[i].track_num;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*offset >= buffers->size()) {
|
if (*offset >= buffers->size()) {
|
||||||
DVLOG(1) << __FUNCTION__ << " : Too few buffers (" << buffers->size()
|
LOG(ERROR) << __FUNCTION__ << " : Too few buffers (" << buffers->size()
|
||||||
<< ") for track_num (" << block_info[i].track_num
|
<< ") for track_num (" << block_info[i].track_num
|
||||||
<< "), expected at least " << *offset + 1 << " buffers";
|
<< "), expected at least " << *offset + 1 << " buffers";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
scoped_refptr<StreamParserBuffer> buffer = (*buffers)[(*offset)++];
|
scoped_refptr<MediaSample> buffer = (*buffers)[(*offset)++];
|
||||||
|
|
||||||
EXPECT_EQ(block_info[i].timestamp, buffer->timestamp().InMilliseconds());
|
EXPECT_EQ(block_info[i].timestamp * kMicrosecondsPerMillisecond,
|
||||||
EXPECT_EQ(std::abs(block_info[i].duration),
|
buffer->pts());
|
||||||
buffer->duration().InMillisecondsF());
|
EXPECT_EQ(std::abs(block_info[i].duration) * kMicrosecondsPerMillisecond,
|
||||||
EXPECT_EQ(expected_type, buffer->type());
|
buffer->duration());
|
||||||
EXPECT_EQ(block_info[i].track_num, buffer->track_id());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VerifyBuffers(const scoped_ptr<WebMClusterParser>& parser,
|
bool VerifyTextBuffers(const BlockInfo* block_info_ptr,
|
||||||
const BlockInfo* block_info,
|
|
||||||
int block_count) {
|
|
||||||
const WebMClusterParser::TextBufferQueueMap& text_map =
|
|
||||||
parser->GetTextBuffers();
|
|
||||||
const WebMClusterParser::BufferQueue* text_buffers;
|
|
||||||
const WebMClusterParser::BufferQueue no_text_buffers;
|
|
||||||
if (!text_map.empty())
|
|
||||||
text_buffers = &(text_map.rbegin()->second);
|
|
||||||
else
|
|
||||||
text_buffers = &no_text_buffers;
|
|
||||||
|
|
||||||
return VerifyBuffers(parser->GetAudioBuffers(),
|
|
||||||
parser->GetVideoBuffers(),
|
|
||||||
*text_buffers,
|
|
||||||
block_info,
|
|
||||||
block_count);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool VerifyTextBuffers(const scoped_ptr<WebMClusterParser>& parser,
|
|
||||||
const BlockInfo* block_info_ptr,
|
|
||||||
int block_count,
|
int block_count,
|
||||||
int text_track_num,
|
int text_track_num,
|
||||||
const WebMClusterParser::BufferQueue& text_buffers) {
|
const BufferQueue& text_buffers) {
|
||||||
const BlockInfo* const block_info_end = block_info_ptr + block_count;
|
const BlockInfo* const block_info_end = block_info_ptr + block_count;
|
||||||
|
|
||||||
typedef WebMClusterParser::BufferQueue::const_iterator TextBufferIter;
|
typedef BufferQueue::const_iterator TextBufferIter;
|
||||||
TextBufferIter buffer_iter = text_buffers.begin();
|
TextBufferIter buffer_iter = text_buffers.begin();
|
||||||
const TextBufferIter buffer_end = text_buffers.end();
|
const TextBufferIter buffer_end = text_buffers.end();
|
||||||
|
|
||||||
|
@ -267,30 +249,19 @@ bool VerifyTextBuffers(const scoped_ptr<WebMClusterParser>& parser,
|
||||||
EXPECT_FALSE(block_info.use_simple_block);
|
EXPECT_FALSE(block_info.use_simple_block);
|
||||||
EXPECT_FALSE(buffer_iter == buffer_end);
|
EXPECT_FALSE(buffer_iter == buffer_end);
|
||||||
|
|
||||||
const scoped_refptr<StreamParserBuffer> buffer = *buffer_iter++;
|
const scoped_refptr<MediaSample> buffer = *buffer_iter++;
|
||||||
EXPECT_EQ(block_info.timestamp, buffer->timestamp().InMilliseconds());
|
EXPECT_EQ(block_info.timestamp * kMicrosecondsPerMillisecond,
|
||||||
EXPECT_EQ(std::abs(block_info.duration),
|
buffer->pts());
|
||||||
buffer->duration().InMillisecondsF());
|
EXPECT_EQ(std::abs(block_info.duration) * kMicrosecondsPerMillisecond,
|
||||||
EXPECT_EQ(DemuxerStream::TEXT, buffer->type());
|
buffer->duration());
|
||||||
EXPECT_EQ(text_track_num, buffer->track_id());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPECT_TRUE(buffer_iter == buffer_end);
|
EXPECT_TRUE(buffer_iter == buffer_end);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VerifyEncryptedBuffer(scoped_refptr<StreamParserBuffer> buffer) {
|
void VerifyEncryptedBuffer(scoped_refptr<MediaSample> buffer) {
|
||||||
EXPECT_TRUE(buffer->decrypt_config());
|
EXPECT_TRUE(buffer->is_encrypted());
|
||||||
EXPECT_EQ(static_cast<unsigned long>(DecryptConfig::kDecryptionKeySize),
|
|
||||||
buffer->decrypt_config()->iv().length());
|
|
||||||
}
|
|
||||||
|
|
||||||
void AppendToEnd(const WebMClusterParser::BufferQueue& src,
|
|
||||||
WebMClusterParser::BufferQueue* dest) {
|
|
||||||
for (WebMClusterParser::BufferQueue::const_iterator itr = src.begin();
|
|
||||||
itr != src.end(); ++itr) {
|
|
||||||
dest->push_back(*itr);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
@ -301,23 +272,43 @@ class WebMClusterParserTest : public testing::Test {
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void ResetParserToHaveDefaultDurations() {
|
void ResetParserToHaveDefaultDurations() {
|
||||||
base::TimeDelta default_audio_duration = base::TimeDelta::FromMilliseconds(
|
int64_t default_audio_duration =
|
||||||
kTestAudioFrameDefaultDurationInMs);
|
kTestAudioFrameDefaultDurationInMs * kMicrosecondsPerMillisecond;
|
||||||
base::TimeDelta default_video_duration = base::TimeDelta::FromMilliseconds(
|
int64_t default_video_duration =
|
||||||
kTestVideoFrameDefaultDurationInMs);
|
kTestVideoFrameDefaultDurationInMs * kMicrosecondsPerMillisecond;
|
||||||
ASSERT_GE(default_audio_duration, base::TimeDelta());
|
ASSERT_GE(default_audio_duration, 0);
|
||||||
ASSERT_GE(default_video_duration, base::TimeDelta());
|
ASSERT_GE(default_video_duration, 0);
|
||||||
ASSERT_NE(kNoTimestamp(), default_audio_duration);
|
ASSERT_NE(kNoTimestamp, default_audio_duration);
|
||||||
ASSERT_NE(kNoTimestamp(), default_video_duration);
|
ASSERT_NE(kNoTimestamp, default_video_duration);
|
||||||
|
|
||||||
parser_.reset(CreateParserWithDefaultDurationsAndOptionalTextTracks(
|
parser_.reset(CreateParserWithDefaultDurationsAndOptionalTextTracks(
|
||||||
default_audio_duration, default_video_duration));
|
default_audio_duration, default_video_duration));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool NewSampleEvent(uint32_t track_id,
|
||||||
|
const scoped_refptr<MediaSample>& sample) {
|
||||||
|
switch (track_id) {
|
||||||
|
case kAudioTrackNum:
|
||||||
|
audio_buffers_.push_back(sample);
|
||||||
|
break;
|
||||||
|
case kVideoTrackNum:
|
||||||
|
video_buffers_.push_back(sample);
|
||||||
|
break;
|
||||||
|
case kTextTrackNum:
|
||||||
|
case kTextTrackNum + 1:
|
||||||
|
text_buffers_map_[track_id].push_back(sample);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG(ERROR) << "Unexpected track number " << track_id;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Helper that hard-codes some non-varying constructor parameters.
|
// Helper that hard-codes some non-varying constructor parameters.
|
||||||
WebMClusterParser* CreateParserHelper(
|
WebMClusterParser* CreateParserHelper(
|
||||||
base::TimeDelta audio_default_duration,
|
int64_t audio_default_duration,
|
||||||
base::TimeDelta video_default_duration,
|
int64_t video_default_duration,
|
||||||
const WebMTracksParser::TextTracks& text_tracks,
|
const WebMTracksParser::TextTracks& text_tracks,
|
||||||
const std::set<int64_t>& ignored_tracks,
|
const std::set<int64_t>& ignored_tracks,
|
||||||
const std::string& audio_encryption_key_id,
|
const std::string& audio_encryption_key_id,
|
||||||
|
@ -326,12 +317,14 @@ class WebMClusterParserTest : public testing::Test {
|
||||||
return new WebMClusterParser(
|
return new WebMClusterParser(
|
||||||
kTimecodeScale, kAudioTrackNum, audio_default_duration, kVideoTrackNum,
|
kTimecodeScale, kAudioTrackNum, audio_default_duration, kVideoTrackNum,
|
||||||
video_default_duration, text_tracks, ignored_tracks,
|
video_default_duration, text_tracks, ignored_tracks,
|
||||||
audio_encryption_key_id, video_encryption_key_id, audio_codec);
|
audio_encryption_key_id, video_encryption_key_id, audio_codec,
|
||||||
|
base::Bind(&WebMClusterParserTest::NewSampleEvent,
|
||||||
|
base::Unretained(this)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a default version of the parser for test.
|
// Create a default version of the parser for test.
|
||||||
WebMClusterParser* CreateDefaultParser() {
|
WebMClusterParser* CreateDefaultParser() {
|
||||||
return CreateParserHelper(kNoTimestamp(), kNoTimestamp(), TextTracks(),
|
return CreateParserHelper(kNoTimestamp, kNoTimestamp, TextTracks(),
|
||||||
std::set<int64_t>(), std::string(), std::string(),
|
std::set<int64_t>(), std::string(), std::string(),
|
||||||
kUnknownAudioCodec);
|
kUnknownAudioCodec);
|
||||||
}
|
}
|
||||||
|
@ -339,8 +332,8 @@ class WebMClusterParserTest : public testing::Test {
|
||||||
// Create a parser for test with custom audio and video default durations, and
|
// Create a parser for test with custom audio and video default durations, and
|
||||||
// optionally custom text tracks.
|
// optionally custom text tracks.
|
||||||
WebMClusterParser* CreateParserWithDefaultDurationsAndOptionalTextTracks(
|
WebMClusterParser* CreateParserWithDefaultDurationsAndOptionalTextTracks(
|
||||||
base::TimeDelta audio_default_duration,
|
int64_t audio_default_duration,
|
||||||
base::TimeDelta video_default_duration,
|
int64_t video_default_duration,
|
||||||
const WebMTracksParser::TextTracks& text_tracks = TextTracks()) {
|
const WebMTracksParser::TextTracks& text_tracks = TextTracks()) {
|
||||||
return CreateParserHelper(audio_default_duration, video_default_duration,
|
return CreateParserHelper(audio_default_duration, video_default_duration,
|
||||||
text_tracks, std::set<int64_t>(), std::string(),
|
text_tracks, std::set<int64_t>(), std::string(),
|
||||||
|
@ -350,7 +343,7 @@ class WebMClusterParserTest : public testing::Test {
|
||||||
// Create a parser for test with custom ignored tracks.
|
// Create a parser for test with custom ignored tracks.
|
||||||
WebMClusterParser* CreateParserWithIgnoredTracks(
|
WebMClusterParser* CreateParserWithIgnoredTracks(
|
||||||
std::set<int64_t>& ignored_tracks) {
|
std::set<int64_t>& ignored_tracks) {
|
||||||
return CreateParserHelper(kNoTimestamp(), kNoTimestamp(), TextTracks(),
|
return CreateParserHelper(kNoTimestamp, kNoTimestamp, TextTracks(),
|
||||||
ignored_tracks, std::string(), std::string(),
|
ignored_tracks, std::string(), std::string(),
|
||||||
kUnknownAudioCodec);
|
kUnknownAudioCodec);
|
||||||
}
|
}
|
||||||
|
@ -360,22 +353,31 @@ class WebMClusterParserTest : public testing::Test {
|
||||||
const std::string& audio_encryption_key_id,
|
const std::string& audio_encryption_key_id,
|
||||||
const std::string& video_encryption_key_id,
|
const std::string& video_encryption_key_id,
|
||||||
const AudioCodec audio_codec) {
|
const AudioCodec audio_codec) {
|
||||||
return CreateParserHelper(kNoTimestamp(), kNoTimestamp(), TextTracks(),
|
return CreateParserHelper(kNoTimestamp, kNoTimestamp, TextTracks(),
|
||||||
std::set<int64_t>(), audio_encryption_key_id,
|
std::set<int64_t>(), audio_encryption_key_id,
|
||||||
video_encryption_key_id, audio_codec);
|
video_encryption_key_id, audio_codec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool VerifyBuffers(const BlockInfo* block_info, int block_count) {
|
||||||
|
bool result = VerifyBuffersHelper(audio_buffers_, video_buffers_,
|
||||||
|
text_buffers_map_[kTextTrackNum],
|
||||||
|
block_info, block_count);
|
||||||
|
audio_buffers_.clear();
|
||||||
|
video_buffers_.clear();
|
||||||
|
text_buffers_map_.clear();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
scoped_ptr<WebMClusterParser> parser_;
|
scoped_ptr<WebMClusterParser> parser_;
|
||||||
|
BufferQueue audio_buffers_;
|
||||||
|
BufferQueue video_buffers_;
|
||||||
|
TextBufferQueueMap text_buffers_map_;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DISALLOW_COPY_AND_ASSIGN(WebMClusterParserTest);
|
DISALLOW_COPY_AND_ASSIGN(WebMClusterParserTest);
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(WebMClusterParserTest, HeldBackBufferHoldsBackAllTracks) {
|
TEST_F(WebMClusterParserTest, TracksWithSampleMissingDuration) {
|
||||||
// If a buffer is missing duration and is being held back, then all other
|
|
||||||
// tracks' buffers that have same or higher (decode) timestamp should be held
|
|
||||||
// back too to keep the timestamps emitted for a cluster monotonically
|
|
||||||
// non-decreasing and in same order as parsed.
|
|
||||||
InSequence s;
|
InSequence s;
|
||||||
|
|
||||||
// Reset the parser to have 3 tracks: text, video (no default frame duration),
|
// Reset the parser to have 3 tracks: text, video (no default frame duration),
|
||||||
|
@ -384,12 +386,12 @@ TEST_F(WebMClusterParserTest, HeldBackBufferHoldsBackAllTracks) {
|
||||||
text_tracks.insert(std::make_pair(TextTracks::key_type(kTextTrackNum),
|
text_tracks.insert(std::make_pair(TextTracks::key_type(kTextTrackNum),
|
||||||
TextTrackConfig(kTextSubtitles, "", "",
|
TextTrackConfig(kTextSubtitles, "", "",
|
||||||
"")));
|
"")));
|
||||||
base::TimeDelta default_audio_duration =
|
int64_t default_audio_duration = kTestAudioFrameDefaultDurationInMs;
|
||||||
base::TimeDelta::FromMilliseconds(kTestAudioFrameDefaultDurationInMs);
|
ASSERT_GE(default_audio_duration, 0);
|
||||||
ASSERT_GE(default_audio_duration, base::TimeDelta());
|
ASSERT_NE(kNoTimestamp, default_audio_duration);
|
||||||
ASSERT_NE(kNoTimestamp(), default_audio_duration);
|
|
||||||
parser_.reset(CreateParserWithDefaultDurationsAndOptionalTextTracks(
|
parser_.reset(CreateParserWithDefaultDurationsAndOptionalTextTracks(
|
||||||
default_audio_duration, kNoTimestamp(), text_tracks));
|
default_audio_duration * kMicrosecondsPerMillisecond, kNoTimestamp,
|
||||||
|
text_tracks));
|
||||||
|
|
||||||
const int kExpectedVideoEstimationInMs = 33;
|
const int kExpectedVideoEstimationInMs = 33;
|
||||||
|
|
||||||
|
@ -405,15 +407,28 @@ TEST_F(WebMClusterParserTest, HeldBackBufferHoldsBackAllTracks) {
|
||||||
{kAudioTrackNum, 83, kTestAudioFrameDefaultDurationInMs, true, NULL, 0},
|
{kAudioTrackNum, 83, kTestAudioFrameDefaultDurationInMs, true, NULL, 0},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Samples are not emitted in the same order as |kBlockInfo| due to missing of
|
||||||
|
// duration in some samples.
|
||||||
|
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},
|
||||||
|
{kVideoTrackNum, 33, 33, true, NULL, 0},
|
||||||
|
{kAudioTrackNum, 70, kTestAudioFrameDefaultDurationInMs, true, NULL, 0},
|
||||||
|
{kVideoTrackNum, 66, kExpectedVideoEstimationInMs, true, NULL, 0},
|
||||||
|
{kAudioTrackNum, 83, kTestAudioFrameDefaultDurationInMs, true, NULL, 0},
|
||||||
|
};
|
||||||
const int kExpectedBuffersOnPartialCluster[] = {
|
const int kExpectedBuffersOnPartialCluster[] = {
|
||||||
0, // Video simple block without DefaultDuration should be held back
|
0, // Video simple block without DefaultDuration should be held back
|
||||||
0, // Audio buffer ready, but not emitted because its TS >= held back video
|
1, // Audio buffer ready
|
||||||
0, // Text buffer ready, but not emitted because its TS >= held back video
|
2, // Text buffer ready
|
||||||
0, // 2nd audio buffer ready, also not emitted for same reason as first
|
3, // 2nd audio buffer ready
|
||||||
4, // All previous buffers emitted, 2nd video held back with no duration
|
4, // 1st video emitted, 2nd video held back with no duration
|
||||||
4, // 2nd video still has no duration, 3rd audio ready but not emitted
|
5, // 3rd audio ready
|
||||||
6, // All previous buffers emitted, 3rd video held back with no duration
|
6, // 2nd video emitted, 3rd video held back with no duration
|
||||||
6, // 3rd video still has no duration, 4th audio ready but not emitted
|
7, // 4th audio ready
|
||||||
9, // Cluster end emits all buffers and 3rd video's duration is estimated
|
9, // Cluster end emits all buffers and 3rd video's duration is estimated
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -451,8 +466,8 @@ TEST_F(WebMClusterParserTest, HeldBackBufferHoldsBackAllTracks) {
|
||||||
EXPECT_LT(0, result);
|
EXPECT_LT(0, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPECT_TRUE(VerifyBuffers(parser_, kBlockInfo,
|
EXPECT_TRUE(
|
||||||
kExpectedBuffersOnPartialCluster[i]));
|
VerifyBuffers(kExpectedBlockInfo, kExpectedBuffersOnPartialCluster[i]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -468,13 +483,13 @@ TEST_F(WebMClusterParserTest, Reset) {
|
||||||
EXPECT_GT(result, 0);
|
EXPECT_GT(result, 0);
|
||||||
EXPECT_LT(result, cluster->size());
|
EXPECT_LT(result, cluster->size());
|
||||||
|
|
||||||
ASSERT_TRUE(VerifyBuffers(parser_, kDefaultBlockInfo, block_count - 1));
|
ASSERT_TRUE(VerifyBuffers(kDefaultBlockInfo, block_count - 1));
|
||||||
parser_->Reset();
|
parser_->Reset();
|
||||||
|
|
||||||
// Now parse a whole cluster to verify that all the blocks will get parsed.
|
// Now parse a whole cluster to verify that all the blocks will get parsed.
|
||||||
result = parser_->Parse(cluster->data(), cluster->size());
|
result = parser_->Parse(cluster->data(), cluster->size());
|
||||||
EXPECT_EQ(cluster->size(), result);
|
EXPECT_EQ(cluster->size(), result);
|
||||||
ASSERT_TRUE(VerifyBuffers(parser_, kDefaultBlockInfo, block_count));
|
ASSERT_TRUE(VerifyBuffers(kDefaultBlockInfo, block_count));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WebMClusterParserTest, ParseClusterWithSingleCall) {
|
TEST_F(WebMClusterParserTest, ParseClusterWithSingleCall) {
|
||||||
|
@ -483,16 +498,16 @@ TEST_F(WebMClusterParserTest, ParseClusterWithSingleCall) {
|
||||||
|
|
||||||
int result = parser_->Parse(cluster->data(), cluster->size());
|
int result = parser_->Parse(cluster->data(), cluster->size());
|
||||||
EXPECT_EQ(cluster->size(), result);
|
EXPECT_EQ(cluster->size(), result);
|
||||||
ASSERT_TRUE(VerifyBuffers(parser_, kDefaultBlockInfo, block_count));
|
ASSERT_TRUE(VerifyBuffers(kDefaultBlockInfo, block_count));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WebMClusterParserTest, ParseClusterWithMultipleCalls) {
|
TEST_F(WebMClusterParserTest, ParseClusterWithMultipleCalls) {
|
||||||
int block_count = arraysize(kDefaultBlockInfo);
|
int block_count = arraysize(kDefaultBlockInfo);
|
||||||
scoped_ptr<Cluster> cluster(CreateCluster(0, kDefaultBlockInfo, block_count));
|
scoped_ptr<Cluster> cluster(CreateCluster(0, kDefaultBlockInfo, block_count));
|
||||||
|
|
||||||
WebMClusterParser::BufferQueue audio_buffers;
|
BufferQueue audio_buffers;
|
||||||
WebMClusterParser::BufferQueue video_buffers;
|
BufferQueue video_buffers;
|
||||||
const WebMClusterParser::BufferQueue no_text_buffers;
|
const BufferQueue no_text_buffers;
|
||||||
|
|
||||||
const uint8_t* data = cluster->data();
|
const uint8_t* data = cluster->data();
|
||||||
int size = cluster->size();
|
int size = cluster->size();
|
||||||
|
@ -511,17 +526,12 @@ TEST_F(WebMClusterParserTest, ParseClusterWithMultipleCalls) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
AppendToEnd(parser_->GetAudioBuffers(), &audio_buffers);
|
|
||||||
AppendToEnd(parser_->GetVideoBuffers(), &video_buffers);
|
|
||||||
|
|
||||||
parse_size = default_parse_size;
|
parse_size = default_parse_size;
|
||||||
|
|
||||||
data += result;
|
data += result;
|
||||||
size -= result;
|
size -= result;
|
||||||
}
|
}
|
||||||
ASSERT_TRUE(VerifyBuffers(audio_buffers, video_buffers,
|
ASSERT_TRUE(VerifyBuffers(kDefaultBlockInfo, block_count));
|
||||||
no_text_buffers, kDefaultBlockInfo,
|
|
||||||
block_count));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify that both BlockGroups with the BlockDuration before the Block
|
// Verify that both BlockGroups with the BlockDuration before the Block
|
||||||
|
@ -552,7 +562,7 @@ TEST_F(WebMClusterParserTest, ParseBlockGroup) {
|
||||||
|
|
||||||
int result = parser_->Parse(kClusterData, kClusterSize);
|
int result = parser_->Parse(kClusterData, kClusterSize);
|
||||||
EXPECT_EQ(kClusterSize, result);
|
EXPECT_EQ(kClusterSize, result);
|
||||||
ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo, block_count));
|
ASSERT_TRUE(VerifyBuffers(kBlockInfo, block_count));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WebMClusterParserTest, ParseSimpleBlockAndBlockGroupMixture) {
|
TEST_F(WebMClusterParserTest, ParseSimpleBlockAndBlockGroupMixture) {
|
||||||
|
@ -568,7 +578,7 @@ TEST_F(WebMClusterParserTest, ParseSimpleBlockAndBlockGroupMixture) {
|
||||||
|
|
||||||
int result = parser_->Parse(cluster->data(), cluster->size());
|
int result = parser_->Parse(cluster->data(), cluster->size());
|
||||||
EXPECT_EQ(cluster->size(), result);
|
EXPECT_EQ(cluster->size(), result);
|
||||||
ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo, block_count));
|
ASSERT_TRUE(VerifyBuffers(kBlockInfo, block_count));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WebMClusterParserTest, IgnoredTracks) {
|
TEST_F(WebMClusterParserTest, IgnoredTracks) {
|
||||||
|
@ -601,7 +611,7 @@ TEST_F(WebMClusterParserTest, IgnoredTracks) {
|
||||||
|
|
||||||
int result = parser_->Parse(cluster->data(), cluster->size());
|
int result = parser_->Parse(cluster->data(), cluster->size());
|
||||||
EXPECT_EQ(cluster->size(), result);
|
EXPECT_EQ(cluster->size(), result);
|
||||||
ASSERT_TRUE(VerifyBuffers(parser_, kOutputBlockInfo, output_block_count));
|
ASSERT_TRUE(VerifyBuffers(kOutputBlockInfo, output_block_count));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WebMClusterParserTest, ParseTextTracks) {
|
TEST_F(WebMClusterParserTest, ParseTextTracks) {
|
||||||
|
@ -612,7 +622,7 @@ TEST_F(WebMClusterParserTest, ParseTextTracks) {
|
||||||
"")));
|
"")));
|
||||||
|
|
||||||
parser_.reset(CreateParserWithDefaultDurationsAndOptionalTextTracks(
|
parser_.reset(CreateParserWithDefaultDurationsAndOptionalTextTracks(
|
||||||
kNoTimestamp(), kNoTimestamp(), text_tracks));
|
kNoTimestamp, kNoTimestamp, text_tracks));
|
||||||
|
|
||||||
const BlockInfo kInputBlockInfo[] = {
|
const BlockInfo kInputBlockInfo[] = {
|
||||||
{kAudioTrackNum, 0, 23, true, NULL, 0},
|
{kAudioTrackNum, 0, 23, true, NULL, 0},
|
||||||
|
@ -630,7 +640,7 @@ TEST_F(WebMClusterParserTest, ParseTextTracks) {
|
||||||
|
|
||||||
int result = parser_->Parse(cluster->data(), cluster->size());
|
int result = parser_->Parse(cluster->data(), cluster->size());
|
||||||
EXPECT_EQ(cluster->size(), result);
|
EXPECT_EQ(cluster->size(), result);
|
||||||
ASSERT_TRUE(VerifyBuffers(parser_, kInputBlockInfo, input_block_count));
|
ASSERT_TRUE(VerifyBuffers(kInputBlockInfo, input_block_count));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WebMClusterParserTest, TextTracksSimpleBlock) {
|
TEST_F(WebMClusterParserTest, TextTracksSimpleBlock) {
|
||||||
|
@ -641,7 +651,7 @@ TEST_F(WebMClusterParserTest, TextTracksSimpleBlock) {
|
||||||
"")));
|
"")));
|
||||||
|
|
||||||
parser_.reset(CreateParserWithDefaultDurationsAndOptionalTextTracks(
|
parser_.reset(CreateParserWithDefaultDurationsAndOptionalTextTracks(
|
||||||
kNoTimestamp(), kNoTimestamp(), text_tracks));
|
kNoTimestamp, kNoTimestamp, text_tracks));
|
||||||
|
|
||||||
const BlockInfo kInputBlockInfo[] = {
|
const BlockInfo kInputBlockInfo[] = {
|
||||||
{ kTextTrackNum, 33, 42, true },
|
{ kTextTrackNum, 33, 42, true },
|
||||||
|
@ -670,7 +680,7 @@ TEST_F(WebMClusterParserTest, ParseMultipleTextTracks) {
|
||||||
"")));
|
"")));
|
||||||
|
|
||||||
parser_.reset(CreateParserWithDefaultDurationsAndOptionalTextTracks(
|
parser_.reset(CreateParserWithDefaultDurationsAndOptionalTextTracks(
|
||||||
kNoTimestamp(), kNoTimestamp(), text_tracks));
|
kNoTimestamp, kNoTimestamp, text_tracks));
|
||||||
|
|
||||||
const BlockInfo kInputBlockInfo[] = {
|
const BlockInfo kInputBlockInfo[] = {
|
||||||
{kAudioTrackNum, 0, 23, true, NULL, 0},
|
{kAudioTrackNum, 0, 23, true, NULL, 0},
|
||||||
|
@ -690,16 +700,12 @@ TEST_F(WebMClusterParserTest, ParseMultipleTextTracks) {
|
||||||
int result = parser_->Parse(cluster->data(), cluster->size());
|
int result = parser_->Parse(cluster->data(), cluster->size());
|
||||||
EXPECT_EQ(cluster->size(), result);
|
EXPECT_EQ(cluster->size(), result);
|
||||||
|
|
||||||
const WebMClusterParser::TextBufferQueueMap& text_map =
|
for (TextBufferQueueMap::const_iterator itr = text_buffers_map_.begin();
|
||||||
parser_->GetTextBuffers();
|
itr != text_buffers_map_.end(); ++itr) {
|
||||||
for (WebMClusterParser::TextBufferQueueMap::const_iterator itr =
|
|
||||||
text_map.begin();
|
|
||||||
itr != text_map.end();
|
|
||||||
++itr) {
|
|
||||||
const TextTracks::const_iterator find_result =
|
const TextTracks::const_iterator find_result =
|
||||||
text_tracks.find(itr->first);
|
text_tracks.find(itr->first);
|
||||||
ASSERT_TRUE(find_result != text_tracks.end());
|
ASSERT_TRUE(find_result != text_tracks.end());
|
||||||
ASSERT_TRUE(VerifyTextBuffers(parser_, kInputBlockInfo, input_block_count,
|
ASSERT_TRUE(VerifyTextBuffers(kInputBlockInfo, input_block_count,
|
||||||
itr->first, itr->second));
|
itr->first, itr->second));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -712,14 +718,14 @@ TEST_F(WebMClusterParserTest, ParseEncryptedBlock) {
|
||||||
|
|
||||||
int result = parser_->Parse(cluster->data(), cluster->size());
|
int result = parser_->Parse(cluster->data(), cluster->size());
|
||||||
EXPECT_EQ(cluster->size(), result);
|
EXPECT_EQ(cluster->size(), result);
|
||||||
ASSERT_EQ(1UL, parser_->GetVideoBuffers().size());
|
ASSERT_EQ(1UL, video_buffers_.size());
|
||||||
scoped_refptr<StreamParserBuffer> buffer = parser_->GetVideoBuffers()[0];
|
scoped_refptr<MediaSample> buffer = video_buffers_[0];
|
||||||
VerifyEncryptedBuffer(buffer);
|
VerifyEncryptedBuffer(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WebMClusterParserTest, ParseBadEncryptedBlock) {
|
TEST_F(WebMClusterParserTest, ParseBadEncryptedBlock) {
|
||||||
scoped_ptr<Cluster> cluster(
|
scoped_ptr<Cluster> cluster(
|
||||||
CreateEncryptedCluster(sizeof(kEncryptedFrame) - 1));
|
CreateEncryptedCluster(sizeof(kEncryptedFrame) - 2));
|
||||||
|
|
||||||
parser_.reset(CreateParserWithKeyIdsAndAudioCodec(
|
parser_.reset(CreateParserWithKeyIdsAndAudioCodec(
|
||||||
std::string(), "video_key_id", kUnknownAudioCodec));
|
std::string(), "video_key_id", kUnknownAudioCodec));
|
||||||
|
@ -753,7 +759,7 @@ TEST_F(WebMClusterParserTest, ParseInvalidTextBlockGroupWithoutDuration) {
|
||||||
"")));
|
"")));
|
||||||
|
|
||||||
parser_.reset(CreateParserWithDefaultDurationsAndOptionalTextTracks(
|
parser_.reset(CreateParserWithDefaultDurationsAndOptionalTextTracks(
|
||||||
kNoTimestamp(), kNoTimestamp(), text_tracks));
|
kNoTimestamp, kNoTimestamp, text_tracks));
|
||||||
|
|
||||||
const BlockInfo kBlockInfo[] = {
|
const BlockInfo kBlockInfo[] = {
|
||||||
{ kTextTrackNum, 33, -42, false },
|
{ kTextTrackNum, 33, -42, false },
|
||||||
|
@ -791,14 +797,14 @@ TEST_F(WebMClusterParserTest, ParseWithDefaultDurationsSimpleBlocks) {
|
||||||
int result = parser_->Parse(cluster->data(), cluster->size() - 1);
|
int result = parser_->Parse(cluster->data(), cluster->size() - 1);
|
||||||
EXPECT_GT(result, 0);
|
EXPECT_GT(result, 0);
|
||||||
EXPECT_LT(result, cluster->size());
|
EXPECT_LT(result, cluster->size());
|
||||||
ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo, block_count - 1));
|
ASSERT_TRUE(VerifyBuffers(kBlockInfo, block_count - 1));
|
||||||
|
|
||||||
parser_->Reset();
|
parser_->Reset();
|
||||||
|
|
||||||
// Now parse a whole cluster to verify that all the blocks will get parsed.
|
// Now parse a whole cluster to verify that all the blocks will get parsed.
|
||||||
result = parser_->Parse(cluster->data(), cluster->size());
|
result = parser_->Parse(cluster->data(), cluster->size());
|
||||||
EXPECT_EQ(cluster->size(), result);
|
EXPECT_EQ(cluster->size(), result);
|
||||||
ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo, block_count));
|
ASSERT_TRUE(VerifyBuffers(kBlockInfo, block_count));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WebMClusterParserTest, ParseWithoutAnyDurationsSimpleBlocks) {
|
TEST_F(WebMClusterParserTest, ParseWithoutAnyDurationsSimpleBlocks) {
|
||||||
|
@ -809,7 +815,7 @@ TEST_F(WebMClusterParserTest, ParseWithoutAnyDurationsSimpleBlocks) {
|
||||||
// last block in a cluster is estimated independently for each track in the
|
// last block in a cluster is estimated independently for each track in the
|
||||||
// cluster. For video tracks we use the maximum seen so far. For audio we use
|
// cluster. For video tracks we use the maximum seen so far. For audio we use
|
||||||
// the the minimum.
|
// the the minimum.
|
||||||
// TODO(chcunningham): Move audio over to use the maximum.
|
// TODO: Move audio over to use the maximum.
|
||||||
|
|
||||||
const int kExpectedAudioEstimationInMs = 22;
|
const int kExpectedAudioEstimationInMs = 22;
|
||||||
const int kExpectedVideoEstimationInMs = 34;
|
const int kExpectedVideoEstimationInMs = 34;
|
||||||
|
@ -834,16 +840,16 @@ TEST_F(WebMClusterParserTest, ParseWithoutAnyDurationsSimpleBlocks) {
|
||||||
int result = parser_->Parse(cluster1->data(), cluster1->size() - 1);
|
int result = parser_->Parse(cluster1->data(), cluster1->size() - 1);
|
||||||
EXPECT_GT(result, 0);
|
EXPECT_GT(result, 0);
|
||||||
EXPECT_LT(result, cluster1->size());
|
EXPECT_LT(result, cluster1->size());
|
||||||
ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo1, block_count1 - 3));
|
EXPECT_EQ(3UL, audio_buffers_.size());
|
||||||
EXPECT_EQ(3UL, parser_->GetAudioBuffers().size());
|
EXPECT_EQ(1UL, video_buffers_.size());
|
||||||
EXPECT_EQ(1UL, parser_->GetVideoBuffers().size());
|
ASSERT_TRUE(VerifyBuffers(kBlockInfo1, block_count1 - 3));
|
||||||
|
|
||||||
parser_->Reset();
|
parser_->Reset();
|
||||||
|
|
||||||
// Now parse the full first cluster and verify all the blocks are parsed.
|
// Now parse the full first cluster and verify all the blocks are parsed.
|
||||||
result = parser_->Parse(cluster1->data(), cluster1->size());
|
result = parser_->Parse(cluster1->data(), cluster1->size());
|
||||||
EXPECT_EQ(cluster1->size(), result);
|
EXPECT_EQ(cluster1->size(), result);
|
||||||
ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo1, block_count1));
|
ASSERT_TRUE(VerifyBuffers(kBlockInfo1, block_count1));
|
||||||
|
|
||||||
// Verify that the estimated frame duration is tracked across clusters for
|
// Verify that the estimated frame duration is tracked across clusters for
|
||||||
// each track.
|
// each track.
|
||||||
|
@ -858,7 +864,7 @@ TEST_F(WebMClusterParserTest, ParseWithoutAnyDurationsSimpleBlocks) {
|
||||||
scoped_ptr<Cluster> cluster2(CreateCluster(0, kBlockInfo2, block_count2));
|
scoped_ptr<Cluster> cluster2(CreateCluster(0, kBlockInfo2, block_count2));
|
||||||
result = parser_->Parse(cluster2->data(), cluster2->size());
|
result = parser_->Parse(cluster2->data(), cluster2->size());
|
||||||
EXPECT_EQ(cluster2->size(), result);
|
EXPECT_EQ(cluster2->size(), result);
|
||||||
ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo2, block_count2));
|
ASSERT_TRUE(VerifyBuffers(kBlockInfo2, block_count2));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WebMClusterParserTest, ParseWithoutAnyDurationsBlockGroups) {
|
TEST_F(WebMClusterParserTest, ParseWithoutAnyDurationsBlockGroups) {
|
||||||
|
@ -894,16 +900,16 @@ TEST_F(WebMClusterParserTest, ParseWithoutAnyDurationsBlockGroups) {
|
||||||
int result = parser_->Parse(cluster1->data(), cluster1->size() - 1);
|
int result = parser_->Parse(cluster1->data(), cluster1->size() - 1);
|
||||||
EXPECT_GT(result, 0);
|
EXPECT_GT(result, 0);
|
||||||
EXPECT_LT(result, cluster1->size());
|
EXPECT_LT(result, cluster1->size());
|
||||||
ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo1, block_count1 - 3));
|
EXPECT_EQ(3UL, audio_buffers_.size());
|
||||||
EXPECT_EQ(3UL, parser_->GetAudioBuffers().size());
|
EXPECT_EQ(1UL, video_buffers_.size());
|
||||||
EXPECT_EQ(1UL, parser_->GetVideoBuffers().size());
|
ASSERT_TRUE(VerifyBuffers(kBlockInfo1, block_count1 - 3));
|
||||||
|
|
||||||
parser_->Reset();
|
parser_->Reset();
|
||||||
|
|
||||||
// Now parse the full first cluster and verify all the blocks are parsed.
|
// Now parse the full first cluster and verify all the blocks are parsed.
|
||||||
result = parser_->Parse(cluster1->data(), cluster1->size());
|
result = parser_->Parse(cluster1->data(), cluster1->size());
|
||||||
EXPECT_EQ(cluster1->size(), result);
|
EXPECT_EQ(cluster1->size(), result);
|
||||||
ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo1, block_count1));
|
ASSERT_TRUE(VerifyBuffers(kBlockInfo1, block_count1));
|
||||||
|
|
||||||
// Verify that the estimated frame duration is tracked across clusters for
|
// Verify that the estimated frame duration is tracked across clusters for
|
||||||
// each track.
|
// each track.
|
||||||
|
@ -916,7 +922,7 @@ TEST_F(WebMClusterParserTest, ParseWithoutAnyDurationsBlockGroups) {
|
||||||
scoped_ptr<Cluster> cluster2(CreateCluster(0, kBlockInfo2, block_count2));
|
scoped_ptr<Cluster> cluster2(CreateCluster(0, kBlockInfo2, block_count2));
|
||||||
result = parser_->Parse(cluster2->data(), cluster2->size());
|
result = parser_->Parse(cluster2->data(), cluster2->size());
|
||||||
EXPECT_EQ(cluster2->size(), result);
|
EXPECT_EQ(cluster2->size(), result);
|
||||||
ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo2, block_count2));
|
ASSERT_TRUE(VerifyBuffers(kBlockInfo2, block_count2));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(wolenetz): Is parser behavior correct? See http://crbug.com/363433.
|
// TODO(wolenetz): Is parser behavior correct? See http://crbug.com/363433.
|
||||||
|
@ -952,14 +958,14 @@ TEST_F(WebMClusterParserTest,
|
||||||
int result = parser_->Parse(cluster->data(), cluster->size() - 1);
|
int result = parser_->Parse(cluster->data(), cluster->size() - 1);
|
||||||
EXPECT_GT(result, 0);
|
EXPECT_GT(result, 0);
|
||||||
EXPECT_LT(result, cluster->size());
|
EXPECT_LT(result, cluster->size());
|
||||||
ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo, block_count - 1));
|
ASSERT_TRUE(VerifyBuffers(kBlockInfo, block_count - 1));
|
||||||
|
|
||||||
parser_->Reset();
|
parser_->Reset();
|
||||||
|
|
||||||
// Now parse a whole cluster to verify that all the blocks will get parsed.
|
// Now parse a whole cluster to verify that all the blocks will get parsed.
|
||||||
result = parser_->Parse(cluster->data(), cluster->size());
|
result = parser_->Parse(cluster->data(), cluster->size());
|
||||||
EXPECT_EQ(cluster->size(), result);
|
EXPECT_EQ(cluster->size(), result);
|
||||||
ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo, block_count));
|
ASSERT_TRUE(VerifyBuffers(kBlockInfo, block_count));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WebMClusterParserTest,
|
TEST_F(WebMClusterParserTest,
|
||||||
|
@ -982,7 +988,7 @@ TEST_F(WebMClusterParserTest,
|
||||||
scoped_ptr<Cluster> cluster(CreateCluster(0, kBlockInfo, block_count));
|
scoped_ptr<Cluster> cluster(CreateCluster(0, kBlockInfo, block_count));
|
||||||
int result = parser_->Parse(cluster->data(), cluster->size());
|
int result = parser_->Parse(cluster->data(), cluster->size());
|
||||||
EXPECT_EQ(cluster->size(), result);
|
EXPECT_EQ(cluster->size(), result);
|
||||||
ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo, block_count));
|
ASSERT_TRUE(VerifyBuffers(kBlockInfo, block_count));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WebMClusterParserTest,
|
TEST_F(WebMClusterParserTest,
|
||||||
|
@ -998,7 +1004,7 @@ TEST_F(WebMClusterParserTest,
|
||||||
scoped_ptr<Cluster> cluster(CreateCluster(0, kBlockInfo, block_count));
|
scoped_ptr<Cluster> cluster(CreateCluster(0, kBlockInfo, block_count));
|
||||||
int result = parser_->Parse(cluster->data(), cluster->size());
|
int result = parser_->Parse(cluster->data(), cluster->size());
|
||||||
EXPECT_EQ(cluster->size(), result);
|
EXPECT_EQ(cluster->size(), result);
|
||||||
ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo, block_count));
|
ASSERT_TRUE(VerifyBuffers(kBlockInfo, block_count));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WebMClusterParserTest, ReadOpusDurationsSimpleBlockAtEndOfCluster) {
|
TEST_F(WebMClusterParserTest, ReadOpusDurationsSimpleBlockAtEndOfCluster) {
|
||||||
|
@ -1022,7 +1028,7 @@ TEST_F(WebMClusterParserTest, ReadOpusDurationsSimpleBlockAtEndOfCluster) {
|
||||||
|
|
||||||
int result = parser_->Parse(cluster->data(), cluster->size());
|
int result = parser_->Parse(cluster->data(), cluster->size());
|
||||||
EXPECT_EQ(cluster->size(), result);
|
EXPECT_EQ(cluster->size(), result);
|
||||||
ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo, block_count));
|
ASSERT_TRUE(VerifyBuffers(kBlockInfo, block_count));
|
||||||
|
|
||||||
loop_count++;
|
loop_count++;
|
||||||
}
|
}
|
||||||
|
@ -1040,9 +1046,11 @@ TEST_F(WebMClusterParserTest, PreferOpusDurationsOverBlockDurations) {
|
||||||
parser_.reset(CreateParserWithKeyIdsAndAudioCodec(
|
parser_.reset(CreateParserWithKeyIdsAndAudioCodec(
|
||||||
std::string(), std::string(), kCodecOpus));
|
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,
|
BlockInfo block_infos[] = {{kAudioTrackNum,
|
||||||
0,
|
0,
|
||||||
block_duration_ms,
|
static_cast<double>(block_duration_ms),
|
||||||
false, // Not a SimpleBlock.
|
false, // Not a SimpleBlock.
|
||||||
packet_ptr->data(),
|
packet_ptr->data(),
|
||||||
packet_ptr->size()}};
|
packet_ptr->size()}};
|
||||||
|
@ -1056,7 +1064,7 @@ TEST_F(WebMClusterParserTest, PreferOpusDurationsOverBlockDurations) {
|
||||||
// duration to be that of the Opus packet to verify it was preferred.
|
// duration to be that of the Opus packet to verify it was preferred.
|
||||||
block_infos[0].duration = packet_ptr->duration_ms();
|
block_infos[0].duration = packet_ptr->duration_ms();
|
||||||
|
|
||||||
ASSERT_TRUE(VerifyBuffers(parser_, block_infos, block_count));
|
ASSERT_TRUE(VerifyBuffers(block_infos, block_count));
|
||||||
|
|
||||||
loop_count++;
|
loop_count++;
|
||||||
}
|
}
|
||||||
|
@ -1090,7 +1098,7 @@ TEST_F(WebMClusterParserTest, DontReadEncodedDurationWhenEncrypted) {
|
||||||
EXPECT_EQ(cluster->size(), result);
|
EXPECT_EQ(cluster->size(), result);
|
||||||
|
|
||||||
// Will verify that duration of buffer matches that of BlockDuration.
|
// Will verify that duration of buffer matches that of BlockDuration.
|
||||||
ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo, block_count));
|
ASSERT_TRUE(VerifyBuffers(kBlockInfo, block_count));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace media
|
} // namespace media
|
||||||
|
|
Loading…
Reference in New Issue