Make webm parser compile and unittest pass

Change-Id: Ib93fbb3eba6f0638f3aa57a9a47e73e8738792d1
This commit is contained in:
KongQun Yang 2015-10-14 16:12:10 -07:00
parent 5a4234f4da
commit 845766f69a
16 changed files with 240 additions and 232 deletions

View File

@ -69,6 +69,8 @@
'stream_info.h', 'stream_info.h',
'text_track.h', 'text_track.h',
'timestamp.h', 'timestamp.h',
'text_track_config.cc',
'text_track_config.h',
'video_stream_info.cc', 'video_stream_info.cc',
'video_stream_info.h', 'video_stream_info.h',
'widevine_key_source.cc', 'widevine_key_source.cc',

View File

@ -19,6 +19,7 @@ enum StreamType {
kStreamUnknown = 0, kStreamUnknown = 0,
kStreamAudio, kStreamAudio,
kStreamVideo, kStreamVideo,
kStreamText,
}; };
/// Abstract class holds stream information. /// Abstract class holds stream information.

View File

@ -5,7 +5,6 @@
#include "packager/media/formats/webm/cluster_builder.h" #include "packager/media/formats/webm/cluster_builder.h"
#include "packager/base/logging.h" #include "packager/base/logging.h"
#include "packager/media/base/data_buffer.h"
#include "packager/media/formats/webm/webm_constants.h" #include "packager/media/formats/webm/webm_constants.h"
namespace edash_packager { namespace edash_packager {

View File

@ -0,0 +1,69 @@
# Copyright 2015 Google Inc. All rights reserved.
#
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file or at
# https://developers.google.com/open-source/licenses/bsd
{
'includes': [
'../../../common.gypi',
],
'targets': [
{
'target_name': 'webm',
'type': '<(component)',
'sources': [
'webm_audio_client.cc',
'webm_audio_client.h',
'webm_cluster_parser.cc',
'webm_cluster_parser.h',
'webm_constants.cc',
'webm_constants.h',
'webm_content_encodings.cc',
'webm_content_encodings.h',
'webm_content_encodings_client.cc',
'webm_content_encodings_client.h',
'webm_crypto_helpers.cc',
'webm_crypto_helpers.h',
'webm_info_parser.cc',
'webm_info_parser.h',
'webm_parser.cc',
'webm_parser.h',
'webm_stream_parser.cc',
'webm_stream_parser.h',
'webm_tracks_parser.cc',
'webm_tracks_parser.h',
'webm_video_client.cc',
'webm_video_client.h',
'webm_webvtt_parser.cc',
'webm_webvtt_parser.h'
],
'dependencies': [
'../../base/media_base.gyp:base',
],
},
{
'target_name': 'webm_unittest',
'type': '<(gtest_target_type)',
'sources': [
'cluster_builder.cc',
'cluster_builder.h',
'opus_packet_builder.cc',
'opus_packet_builder.h',
'tracks_builder.cc',
'tracks_builder.h',
'webm_cluster_parser_unittest.cc',
'webm_content_encodings_client_unittest.cc',
'webm_parser_unittest.cc',
'webm_tracks_parser_unittest.cc',
'webm_webvtt_parser_unittest.cc',
],
'dependencies': [
'../../../testing/gtest.gyp:gtest',
'../../../testing/gmock.gyp:gmock',
'../../test/media_test.gyp:media_test_support',
'webm',
]
},
],
}

View File

@ -4,10 +4,14 @@
#include "packager/media/formats/webm/webm_audio_client.h" #include "packager/media/formats/webm/webm_audio_client.h"
#include "packager/media/base/audio_decoder_config.h" #include "packager/base/logging.h"
#include "packager/media/base/channel_layout.h"
#include "packager/media/formats/webm/webm_constants.h" #include "packager/media/formats/webm/webm_constants.h"
namespace {
// Timestamps are represented in double in WebM. Convert to uint64_t in us.
const uint32_t kWebMTimeScale = 1000000u;
} // namespace
namespace edash_packager { namespace edash_packager {
namespace media { namespace media {
@ -24,16 +28,12 @@ void WebMAudioClient::Reset() {
output_samples_per_second_ = -1; output_samples_per_second_ = -1;
} }
bool WebMAudioClient::InitializeConfig( scoped_refptr<AudioStreamInfo> WebMAudioClient::GetAudioStreamInfo(
int64_t track_num,
const std::string& codec_id, const std::string& codec_id,
const std::vector<uint8_t>& codec_private, const std::vector<uint8_t>& codec_private,
int64_t seek_preroll, const std::string& language,
int64_t codec_delay, bool is_encrypted) {
bool is_encrypted,
AudioDecoderConfig* config) {
DCHECK(config);
SampleFormat sample_format = kSampleFormatPlanarF32;
AudioCodec audio_codec = kUnknownAudioCodec; AudioCodec audio_codec = kUnknownAudioCodec;
if (codec_id == "A_VORBIS") { if (codec_id == "A_VORBIS") {
audio_codec = kCodecVorbis; audio_codec = kCodecVorbis;
@ -41,32 +41,21 @@ bool WebMAudioClient::InitializeConfig(
audio_codec = kCodecOpus; audio_codec = kCodecOpus;
} else { } else {
LOG(ERROR) << "Unsupported audio codec_id " << codec_id; LOG(ERROR) << "Unsupported audio codec_id " << codec_id;
return false; return scoped_refptr<AudioStreamInfo>();
} }
if (samples_per_second_ <= 0) if (samples_per_second_ <= 0)
return false; return scoped_refptr<AudioStreamInfo>();
// Set channel layout default if a Channels element was not present. // Set channel layout default if a Channels element was not present.
if (channels_ == -1) if (channels_ == -1)
channels_ = 1; channels_ = 1;
ChannelLayout channel_layout = GuessChannelLayout(channels_); uint32_t sampling_frequency = samples_per_second_;
if (channel_layout == CHANNEL_LAYOUT_UNSUPPORTED) {
LOG(ERROR) << "Unsupported channel count " << channels_;
return false;
}
int samples_per_second = samples_per_second_;
if (output_samples_per_second_ > 0)
samples_per_second = output_samples_per_second_;
// Always use 48kHz for OPUS. See the "Input Sample Rate" section of the // Always use 48kHz for OPUS. See the "Input Sample Rate" section of the
// spec: http://tools.ietf.org/html/draft-terriberry-oggopus-01#page-11 // spec: http://tools.ietf.org/html/draft-terriberry-oggopus-01#page-11
if (audio_codec == kCodecOpus) { if (audio_codec == kCodecOpus) {
samples_per_second = 48000; sampling_frequency = 48000;
sample_format = kSampleFormatF32;
} }
const uint8_t* extra_data = NULL; const uint8_t* extra_data = NULL;
@ -76,27 +65,12 @@ bool WebMAudioClient::InitializeConfig(
extra_data_size = codec_private.size(); extra_data_size = codec_private.size();
} }
// Convert |codec_delay| from nanoseconds into frames. const uint32_t kSampleSizeInBits = 4u;
int codec_delay_in_frames = 0; return scoped_refptr<AudioStreamInfo>(new AudioStreamInfo(
if (codec_delay != -1) { track_num, kWebMTimeScale, 0, audio_codec,
codec_delay_in_frames = AudioStreamInfo::GetCodecString(audio_codec, 0), language,
0.5 + kSampleSizeInBits, channels_, sampling_frequency, extra_data,
samples_per_second * (static_cast<double>(codec_delay) / extra_data_size, is_encrypted));
base::Time::kNanosecondsPerSecond);
}
config->Initialize(
audio_codec,
sample_format,
channel_layout,
samples_per_second,
extra_data,
extra_data_size,
is_encrypted,
base::TimeDelta::FromMicroseconds(
(seek_preroll != -1 ? seek_preroll : 0) / 1000),
codec_delay_in_frames);
return config->IsValidConfig();
} }
bool WebMAudioClient::OnUInt(int id, int64_t val) { bool WebMAudioClient::OnUInt(int id, int64_t val) {

View File

@ -8,6 +8,8 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "packager/base/memory/scoped_ptr.h"
#include "packager/media/base/audio_stream_info.h"
#include "packager/media/formats/webm/webm_parser.h" #include "packager/media/formats/webm/webm_parser.h"
namespace edash_packager { namespace edash_packager {
@ -23,18 +25,18 @@ class WebMAudioClient : public WebMParserClient {
// Reset this object's state so it can process a new audio track element. // Reset this object's state so it can process a new audio track element.
void Reset(); void Reset();
// Initialize |config| with the data in |codec_id|, |codec_private|, // Create an AudioStreamInfo with the data in |track_num|, |codec_id|,
// |is_encrypted| and the fields parsed from the last audio track element this // |codec_private|, |is_encrypted| and the fields parsed from the last audio
// object was used to parse. // track element this object was used to parse.
// Returns true if |config| was successfully initialized. // Returns an AudioStreamInfo scoped_refptr if successful.
// Returns false if there was unexpected values in the provided parameters or // Returns an empty scoped_refptr if there was unexpected values in the
// audio track element fields. // provided parameters or audio track element fields.
bool InitializeConfig(const std::string& codec_id, scoped_refptr<AudioStreamInfo> GetAudioStreamInfo(
const std::vector<uint8_t>& codec_private, int64_t track_num,
const int64_t seek_preroll, const std::string& codec_id,
const int64_t codec_delay, const std::vector<uint8_t>& codec_private,
bool is_encrypted, const std::string& language,
AudioDecoderConfig* config); bool is_encrypted);
private: private:
// WebMParserClient implementation. // WebMParserClient implementation.

View File

@ -54,9 +54,9 @@ bool WebMCreateDecryptConfig(const uint8_t* data,
} }
decrypt_config->reset(new DecryptConfig( decrypt_config->reset(new DecryptConfig(
std::string(reinterpret_cast<const char*>(key_id), key_id_size), std::vector<uint8_t>(key_id, key_id + key_id_size),
counter_block, std::vector<uint8_t>(counter_block.begin(), counter_block.end()),
std::vector<SubsampleEntry>())); frame_offset, std::vector<SubsampleEntry>()));
*data_offset = frame_offset; *data_offset = frame_offset;
return true; return true;

View File

@ -9,7 +9,7 @@
#include "packager/base/macros.h" #include "packager/base/macros.h"
#include "packager/base/memory/scoped_ptr.h" #include "packager/base/memory/scoped_ptr.h"
#include "packager/media/base/decoder_buffer.h" #include "packager/media/base/decrypt_config.h"
namespace edash_packager { namespace edash_packager {
namespace media { namespace media {

View File

@ -9,7 +9,7 @@
#include "packager/base/callback.h" #include "packager/base/callback.h"
#include "packager/base/callback_helpers.h" #include "packager/base/callback_helpers.h"
#include "packager/base/logging.h" #include "packager/base/logging.h"
#include "packager/media/base/timestamp_constants.h" #include "packager/media/base/timestamp.h"
#include "packager/media/formats/webm/webm_cluster_parser.h" #include "packager/media/formats/webm/webm_cluster_parser.h"
#include "packager/media/formats/webm/webm_constants.h" #include "packager/media/formats/webm/webm_constants.h"
#include "packager/media/formats/webm/webm_content_encodings.h" #include "packager/media/formats/webm/webm_content_encodings.h"
@ -29,29 +29,18 @@ WebMStreamParser::~WebMStreamParser() {
void WebMStreamParser::Init( void WebMStreamParser::Init(
const InitCB& init_cb, const InitCB& init_cb,
const NewConfigCB& config_cb, const NewSampleCB& new_sample_cb,
const NewBuffersCB& new_buffers_cb, KeySource* decryption_key_source) {
bool ignore_text_tracks,
const EncryptedMediaInitDataCB& encrypted_media_init_data_cb,
const NewMediaSegmentCB& new_segment_cb,
const base::Closure& end_of_segment_cb) {
DCHECK_EQ(state_, kWaitingForInit); DCHECK_EQ(state_, kWaitingForInit);
DCHECK(init_cb_.is_null()); DCHECK(init_cb_.is_null());
DCHECK(!init_cb.is_null()); DCHECK(!init_cb.is_null());
DCHECK(!config_cb.is_null()); DCHECK(!new_sample_cb.is_null());
DCHECK(!new_buffers_cb.is_null());
DCHECK(!encrypted_media_init_data_cb.is_null());
DCHECK(!new_segment_cb.is_null());
DCHECK(!end_of_segment_cb.is_null());
ChangeState(kParsingHeaders); ChangeState(kParsingHeaders);
init_cb_ = init_cb; init_cb_ = init_cb;
config_cb_ = config_cb; new_sample_cb_ = new_sample_cb;
new_buffers_cb_ = new_buffers_cb; decryption_key_source_ = decryption_key_source;
ignore_text_tracks_ = ignore_text_tracks; ignore_text_tracks_ = true;
encrypted_media_init_data_cb_ = encrypted_media_init_data_cb;
new_segment_cb_ = new_segment_cb;
end_of_segment_cb_ = end_of_segment_cb;
} }
void WebMStreamParser::Flush() { void WebMStreamParser::Flush() {
@ -62,7 +51,6 @@ void WebMStreamParser::Flush() {
cluster_parser_->Reset(); cluster_parser_->Reset();
if (state_ == kParsingClusters) { if (state_ == kParsingClusters) {
ChangeState(kParsingHeaders); ChangeState(kParsingHeaders);
end_of_segment_cb_.Run();
} }
} }
@ -158,7 +146,6 @@ int WebMStreamParser::ParseInfoAndTracks(const uint8_t* data, int size) {
return -1; return -1;
} }
ChangeState(kParsingClusters); ChangeState(kParsingClusters);
new_segment_cb_.Run();
return 0; return 0;
break; break;
case kWebMIdSegment: case kWebMIdSegment:
@ -196,38 +183,22 @@ int WebMStreamParser::ParseInfoAndTracks(const uint8_t* data, int size) {
bytes_parsed += result; bytes_parsed += result;
double timecode_scale_in_us = info_parser.timecode_scale() / 1000.0; double timecode_scale_in_us = info_parser.timecode_scale() / 1000.0;
InitParameters params(kInfiniteDuration()); int64_t duration_in_us = info_parser.duration() * timecode_scale_in_us;
if (info_parser.duration() > 0) { std::vector<scoped_refptr<StreamInfo>> streams;
int64_t duration_in_us = info_parser.duration() * timecode_scale_in_us; scoped_refptr<AudioStreamInfo> audio_stream_info =
params.duration = base::TimeDelta::FromMicroseconds(duration_in_us); tracks_parser.audio_stream_info();
} streams.push_back(tracks_parser.audio_stream_info());
streams.back()->set_duration(duration_in_us);
params.timeline_offset = info_parser.date_utc(); if (streams.back()->is_encrypted())
if (unknown_segment_size_ && (info_parser.duration() <= 0) &&
!info_parser.date_utc().is_null()) {
params.liveness = DemuxerStream::LIVENESS_LIVE;
} else if (info_parser.duration() >= 0) {
params.liveness = DemuxerStream::LIVENESS_RECORDED;
} else {
params.liveness = DemuxerStream::LIVENESS_UNKNOWN;
}
const AudioDecoderConfig& audio_config = tracks_parser.audio_decoder_config();
if (audio_config.is_encrypted())
OnEncryptedMediaInitData(tracks_parser.audio_encryption_key_id()); OnEncryptedMediaInitData(tracks_parser.audio_encryption_key_id());
const VideoDecoderConfig& video_config = tracks_parser.video_decoder_config(); streams.push_back(tracks_parser.video_stream_info());
if (video_config.is_encrypted()) streams.back()->set_duration(duration_in_us);
if (streams.back()->is_encrypted())
OnEncryptedMediaInitData(tracks_parser.video_encryption_key_id()); OnEncryptedMediaInitData(tracks_parser.video_encryption_key_id());
if (!config_cb_.Run(audio_config, init_cb_.Run(streams);
video_config,
tracks_parser.text_tracks())) {
DVLOG(1) << "New config data isn't allowed.";
return -1;
}
cluster_parser_.reset(new WebMClusterParser( cluster_parser_.reset(new WebMClusterParser(
info_parser.timecode_scale(), tracks_parser.audio_track_num(), info_parser.timecode_scale(), tracks_parser.audio_track_num(),
@ -236,10 +207,8 @@ int WebMStreamParser::ParseInfoAndTracks(const uint8_t* data, int size) {
tracks_parser.GetVideoDefaultDuration(timecode_scale_in_us), tracks_parser.GetVideoDefaultDuration(timecode_scale_in_us),
tracks_parser.text_tracks(), tracks_parser.ignored_tracks(), tracks_parser.text_tracks(), tracks_parser.ignored_tracks(),
tracks_parser.audio_encryption_key_id(), tracks_parser.audio_encryption_key_id(),
tracks_parser.video_encryption_key_id(), audio_config.codec()); tracks_parser.video_encryption_key_id(), audio_stream_info->codec(),
new_sample_cb_));
if (!init_cb_.is_null())
base::ResetAndReturn(&init_cb_).Run(params);
return bytes_parsed; return bytes_parsed;
} }
@ -252,29 +221,16 @@ int WebMStreamParser::ParseCluster(const uint8_t* data, int size) {
if (bytes_parsed < 0) if (bytes_parsed < 0)
return bytes_parsed; return bytes_parsed;
const BufferQueue& audio_buffers = cluster_parser_->GetAudioBuffers();
const BufferQueue& video_buffers = cluster_parser_->GetVideoBuffers();
const TextBufferQueueMap& text_map = cluster_parser_->GetTextBuffers();
bool cluster_ended = cluster_parser_->cluster_ended(); bool cluster_ended = cluster_parser_->cluster_ended();
if ((!audio_buffers.empty() || !video_buffers.empty() ||
!text_map.empty()) &&
!new_buffers_cb_.Run(audio_buffers, video_buffers, text_map)) {
return -1;
}
if (cluster_ended) { if (cluster_ended) {
ChangeState(kParsingHeaders); ChangeState(kParsingHeaders);
end_of_segment_cb_.Run();
} }
return bytes_parsed; return bytes_parsed;
} }
void WebMStreamParser::OnEncryptedMediaInitData(const std::string& key_id) { void WebMStreamParser::OnEncryptedMediaInitData(const std::string& key_id) {
std::vector<uint8_t> key_id_vector(key_id.begin(), key_id.end()); NOTIMPLEMENTED() << "WebM decryption is not implemented yet.";
encrypted_media_init_data_cb_.Run(EmeInitDataType::WEBM, key_id_vector);
} }
} // namespace media } // namespace media

View File

@ -7,29 +7,23 @@
#include "packager/base/callback_forward.h" #include "packager/base/callback_forward.h"
#include "packager/base/memory/ref_counted.h" #include "packager/base/memory/ref_counted.h"
#include "packager/media/base/audio_decoder_config.h"
#include "packager/media/base/byte_queue.h" #include "packager/media/base/byte_queue.h"
#include "packager/media/base/stream_parser.h" #include "packager/media/base/media_parser.h"
#include "packager/media/base/video_decoder_config.h"
namespace edash_packager { namespace edash_packager {
namespace media { namespace media {
class WebMClusterParser; class WebMClusterParser;
class WebMStreamParser : public StreamParser { class WebMStreamParser : public MediaParser {
public: public:
WebMStreamParser(); WebMStreamParser();
~WebMStreamParser() override; ~WebMStreamParser() override;
// StreamParser implementation. // StreamParser implementation.
void Init(const InitCB& init_cb, void Init(const InitCB& init_cb,
const NewConfigCB& config_cb, const NewSampleCB& new_sample_cb,
const NewBuffersCB& new_buffers_cb, KeySource* decryption_key_source) override;
bool ignore_text_tracks,
const EncryptedMediaInitDataCB& encrypted_media_init_data_cb,
const NewMediaSegmentCB& new_segment_cb,
const base::Closure& end_of_segment_cb) override;
void Flush() override; void Flush() override;
bool Parse(const uint8_t* buf, int size) override; bool Parse(const uint8_t* buf, int size) override;
@ -67,13 +61,9 @@ class WebMStreamParser : public StreamParser {
State state_; State state_;
InitCB init_cb_; InitCB init_cb_;
NewConfigCB config_cb_; NewSampleCB new_sample_cb_;
NewBuffersCB new_buffers_cb_; KeySource* decryption_key_source_;
bool ignore_text_tracks_; bool ignore_text_tracks_;
EncryptedMediaInitDataCB encrypted_media_init_data_cb_;
NewMediaSegmentCB new_segment_cb_;
base::Closure end_of_segment_cb_;
bool unknown_segment_size_; bool unknown_segment_size_;

View File

@ -7,7 +7,7 @@
#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/base/strings/string_util.h" #include "packager/base/strings/string_util.h"
#include "packager/media/base/timestamp_constants.h" #include "packager/media/base/timestamp.h"
#include "packager/media/formats/webm/webm_constants.h" #include "packager/media/formats/webm/webm_constants.h"
#include "packager/media/formats/webm/webm_content_encodings.h" #include "packager/media/formats/webm/webm_content_encodings.h"
@ -30,19 +30,19 @@ static TextKind CodecIdToTextKind(const std::string& codec_id) {
return kTextNone; return kTextNone;
} }
static base::TimeDelta PrecisionCappedDefaultDuration( static int64_t PrecisionCappedDefaultDuration(
const double timecode_scale_in_us, const double timecode_scale_in_us,
const int64_t duration_in_ns) { const int64_t duration_in_ns) {
if (duration_in_ns <= 0) if (duration_in_ns <= 0)
return kNoTimestamp(); return kNoTimestamp;
int64_t mult = duration_in_ns / 1000; int64_t mult = duration_in_ns / 1000;
mult /= timecode_scale_in_us; mult /= timecode_scale_in_us;
if (mult == 0) if (mult == 0)
return kNoTimestamp(); return kNoTimestamp;
mult = static_cast<double>(mult) * timecode_scale_in_us; mult = static_cast<double>(mult) * timecode_scale_in_us;
return base::TimeDelta::FromMicroseconds(mult); return mult;
} }
WebMTracksParser::WebMTracksParser(bool ignore_text_tracks) WebMTracksParser::WebMTracksParser(bool ignore_text_tracks)
@ -70,10 +70,10 @@ int WebMTracksParser::Parse(const uint8_t* buf, int size) {
track_language_.clear(); track_language_.clear();
audio_track_num_ = -1; audio_track_num_ = -1;
audio_default_duration_ = -1; audio_default_duration_ = -1;
audio_decoder_config_ = AudioDecoderConfig(); audio_stream_info_ = nullptr;
video_track_num_ = -1; video_track_num_ = -1;
video_default_duration_ = -1; video_default_duration_ = -1;
video_decoder_config_ = VideoDecoderConfig(); video_stream_info_ = nullptr;
text_tracks_.clear(); text_tracks_.clear();
ignored_tracks_.clear(); ignored_tracks_.clear();
@ -87,13 +87,13 @@ int WebMTracksParser::Parse(const uint8_t* buf, int size) {
return parser.IsParsingComplete() ? result : 0; return parser.IsParsingComplete() ? result : 0;
} }
base::TimeDelta WebMTracksParser::GetAudioDefaultDuration( int64_t WebMTracksParser::GetAudioDefaultDuration(
const double timecode_scale_in_us) const { const double timecode_scale_in_us) const {
return PrecisionCappedDefaultDuration(timecode_scale_in_us, return PrecisionCappedDefaultDuration(timecode_scale_in_us,
audio_default_duration_); audio_default_duration_);
} }
base::TimeDelta WebMTracksParser::GetVideoDefaultDuration( int64_t WebMTracksParser::GetVideoDefaultDuration(
const double timecode_scale_in_us) const { const double timecode_scale_in_us) const {
return PrecisionCappedDefaultDuration(timecode_scale_in_us, return PrecisionCappedDefaultDuration(timecode_scale_in_us,
video_default_duration_); video_default_duration_);
@ -201,12 +201,12 @@ bool WebMTracksParser::OnListEnd(int id) {
} }
audio_default_duration_ = default_duration_; audio_default_duration_ = default_duration_;
DCHECK(!audio_decoder_config_.IsValidConfig()); DCHECK(!audio_stream_info_);
if (!audio_client_.InitializeConfig( audio_stream_info_ = audio_client_.GetAudioStreamInfo(
codec_id_, codec_private_, seek_preroll_, codec_delay_, audio_track_num_, codec_id_, codec_private_, track_language_,
!audio_encryption_key_id_.empty(), &audio_decoder_config_)) { !audio_encryption_key_id_.empty());
if (!audio_stream_info_)
return false; return false;
}
} else { } else {
DLOG(INFO) << "Ignoring audio track " << track_num_; DLOG(INFO) << "Ignoring audio track " << track_num_;
ignored_tracks_.insert(track_num_); ignored_tracks_.insert(track_num_);
@ -223,12 +223,12 @@ bool WebMTracksParser::OnListEnd(int id) {
} }
video_default_duration_ = default_duration_; video_default_duration_ = default_duration_;
DCHECK(!video_decoder_config_.IsValidConfig()); DCHECK(!video_stream_info_);
if (!video_client_.InitializeConfig( video_stream_info_ = video_client_.GetVideoStreamInfo(
codec_id_, codec_private_, !video_encryption_key_id_.empty(), video_track_num_, codec_id_, codec_private_,
&video_decoder_config_)) { !video_encryption_key_id_.empty());
if (!video_stream_info_)
return false; return false;
}
} else { } else {
DLOG(INFO) << "Ignoring video track " << track_num_; DLOG(INFO) << "Ignoring video track " << track_num_;
ignored_tracks_.insert(track_num_); ignored_tracks_.insert(track_num_);

View File

@ -13,9 +13,9 @@
#include "packager/base/compiler_specific.h" #include "packager/base/compiler_specific.h"
#include "packager/base/memory/scoped_ptr.h" #include "packager/base/memory/scoped_ptr.h"
#include "packager/base/time/time.h" #include "packager/base/time/time.h"
#include "packager/media/base/audio_decoder_config.h" #include "packager/media/base/audio_stream_info.h"
#include "packager/media/base/text_track_config.h" #include "packager/media/base/text_track_config.h"
#include "packager/media/base/video_decoder_config.h" #include "packager/media/base/video_stream_info.h"
#include "packager/media/formats/webm/webm_audio_client.h" #include "packager/media/formats/webm/webm_audio_client.h"
#include "packager/media/formats/webm/webm_content_encodings_client.h" #include "packager/media/formats/webm/webm_content_encodings_client.h"
#include "packager/media/formats/webm/webm_parser.h" #include "packager/media/formats/webm/webm_parser.h"
@ -44,10 +44,8 @@ class WebMTracksParser : public WebMParserClient {
// video track, returns that value converted from ns to base::TimeDelta with // video track, returns that value converted from ns to base::TimeDelta with
// precision not greater than |timecode_scale_in_us|. Defaults to // precision not greater than |timecode_scale_in_us|. Defaults to
// kNoTimestamp(). // kNoTimestamp().
base::TimeDelta GetAudioDefaultDuration( int64_t GetAudioDefaultDuration(const double timecode_scale_in_us) const;
const double timecode_scale_in_us) const; int64_t GetVideoDefaultDuration(const double timecode_scale_in_us) const;
base::TimeDelta GetVideoDefaultDuration(
const double timecode_scale_in_us) const;
const std::set<int64_t>& ignored_tracks() const { return ignored_tracks_; } const std::set<int64_t>& ignored_tracks() const { return ignored_tracks_; }
@ -55,16 +53,16 @@ class WebMTracksParser : public WebMParserClient {
return audio_encryption_key_id_; return audio_encryption_key_id_;
} }
const AudioDecoderConfig& audio_decoder_config() { scoped_refptr<AudioStreamInfo> audio_stream_info() {
return audio_decoder_config_; return audio_stream_info_;
} }
const std::string& video_encryption_key_id() const { const std::string& video_encryption_key_id() const {
return video_encryption_key_id_; return video_encryption_key_id_;
} }
const VideoDecoderConfig& video_decoder_config() { scoped_refptr<VideoStreamInfo> video_stream_info() {
return video_decoder_config_; return video_stream_info_;
} }
typedef std::map<int, TextTrackConfig> TextTracks; typedef std::map<int, TextTrackConfig> TextTracks;
@ -104,10 +102,10 @@ class WebMTracksParser : public WebMParserClient {
std::string video_encryption_key_id_; std::string video_encryption_key_id_;
WebMAudioClient audio_client_; WebMAudioClient audio_client_;
AudioDecoderConfig audio_decoder_config_; scoped_refptr<AudioStreamInfo> audio_stream_info_;
WebMVideoClient video_client_; WebMVideoClient video_client_;
VideoDecoderConfig video_decoder_config_; scoped_refptr<VideoStreamInfo> video_stream_info_;
DISALLOW_COPY_AND_ASSIGN(WebMTracksParser); DISALLOW_COPY_AND_ASSIGN(WebMTracksParser);
}; };

View File

@ -8,8 +8,7 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include "packager/base/logging.h" #include "packager/base/logging.h"
#include "packager/media/base/channel_layout.h" #include "packager/media/base/timestamp.h"
#include "packager/media/base/timestamp_constants.h"
#include "packager/media/formats/webm/tracks_builder.h" #include "packager/media/formats/webm/tracks_builder.h"
#include "packager/media/formats/webm/webm_constants.h" #include "packager/media/formats/webm/webm_constants.h"
@ -140,20 +139,22 @@ TEST_F(WebMTracksParserTest, AudioVideoDefaultDurationUnset) {
EXPECT_LE(0, result); EXPECT_LE(0, result);
EXPECT_EQ(static_cast<int>(buf.size()), result); EXPECT_EQ(static_cast<int>(buf.size()), result);
EXPECT_EQ(kNoTimestamp(), EXPECT_EQ(kNoTimestamp,
parser->GetAudioDefaultDuration(kDefaultTimecodeScaleInUs)); parser->GetAudioDefaultDuration(kDefaultTimecodeScaleInUs));
EXPECT_EQ(kNoTimestamp(), EXPECT_EQ(kNoTimestamp,
parser->GetVideoDefaultDuration(kDefaultTimecodeScaleInUs)); parser->GetVideoDefaultDuration(kDefaultTimecodeScaleInUs));
const VideoDecoderConfig& video_config = parser->video_decoder_config(); scoped_refptr<VideoStreamInfo> video_stream_info =
EXPECT_TRUE(video_config.IsValidConfig()); parser->video_stream_info();
EXPECT_EQ(320, video_config.coded_size().width()); EXPECT_TRUE(video_stream_info);
EXPECT_EQ(240, video_config.coded_size().height()); EXPECT_EQ(320u, video_stream_info->width());
EXPECT_EQ(240u, video_stream_info->height());
const AudioDecoderConfig& audio_config = parser->audio_decoder_config(); scoped_refptr<AudioStreamInfo> audio_stream_info =
EXPECT_TRUE(audio_config.IsValidConfig()); parser->audio_stream_info();
EXPECT_EQ(CHANNEL_LAYOUT_STEREO, audio_config.channel_layout()); EXPECT_TRUE(audio_stream_info);
EXPECT_EQ(8000, audio_config.samples_per_second()); EXPECT_EQ(2u, audio_stream_info->num_channels());
EXPECT_EQ(8000u, audio_stream_info->sampling_frequency());
} }
TEST_F(WebMTracksParserTest, AudioVideoDefaultDurationSet) { TEST_F(WebMTracksParserTest, AudioVideoDefaultDurationSet) {
@ -169,14 +170,12 @@ TEST_F(WebMTracksParserTest, AudioVideoDefaultDurationSet) {
EXPECT_LE(0, result); EXPECT_LE(0, result);
EXPECT_EQ(static_cast<int>(buf.size()), result); EXPECT_EQ(static_cast<int>(buf.size()), result);
EXPECT_EQ(base::TimeDelta::FromMicroseconds(12000), EXPECT_EQ(12000, parser->GetAudioDefaultDuration(kDefaultTimecodeScaleInUs));
parser->GetAudioDefaultDuration(kDefaultTimecodeScaleInUs)); EXPECT_EQ(985000,
EXPECT_EQ(base::TimeDelta::FromMicroseconds(985000),
parser->GetVideoDefaultDuration(5000.0)); // 5 ms resolution parser->GetVideoDefaultDuration(5000.0)); // 5 ms resolution
EXPECT_EQ(kNoTimestamp(), parser->GetAudioDefaultDuration(12346.0)); EXPECT_EQ(kNoTimestamp, parser->GetAudioDefaultDuration(12346.0));
EXPECT_EQ(base::TimeDelta::FromMicroseconds(12345), EXPECT_EQ(12345, parser->GetAudioDefaultDuration(12345.0));
parser->GetAudioDefaultDuration(12345.0)); EXPECT_EQ(12003,
EXPECT_EQ(base::TimeDelta::FromMicroseconds(12003),
parser->GetAudioDefaultDuration(1000.3)); // 1.0003 ms resolution parser->GetAudioDefaultDuration(1000.3)); // 1.0003 ms resolution
} }

View File

@ -4,9 +4,25 @@
#include "packager/media/formats/webm/webm_video_client.h" #include "packager/media/formats/webm/webm_video_client.h"
#include "packager/media/base/video_decoder_config.h" #include "packager/base/logging.h"
#include "packager/media/formats/webm/webm_constants.h" #include "packager/media/formats/webm/webm_constants.h"
namespace {
// Timestamps are represented in double in WebM. Convert to uint64_t in us.
const uint32_t kWebMTimeScale = 1000000u;
int64_t GetGreatestCommonDivisor(int64_t a, int64_t b) {
while (b) {
int64_t temp = b;
b = a % b;
a = temp;
}
return a;
}
} // namespace
namespace edash_packager { namespace edash_packager {
namespace media { namespace media {
@ -30,31 +46,23 @@ void WebMVideoClient::Reset() {
alpha_mode_ = -1; alpha_mode_ = -1;
} }
bool WebMVideoClient::InitializeConfig( scoped_refptr<VideoStreamInfo> WebMVideoClient::GetVideoStreamInfo(
int64_t track_num,
const std::string& codec_id, const std::string& codec_id,
const std::vector<uint8_t>& codec_private, const std::vector<uint8_t>& codec_private,
bool is_encrypted, bool is_encrypted) {
VideoDecoderConfig* config) {
DCHECK(config);
VideoCodec video_codec = kUnknownVideoCodec; VideoCodec video_codec = kUnknownVideoCodec;
VideoCodecProfile profile = VIDEO_CODEC_PROFILE_UNKNOWN;
if (codec_id == "V_VP8") { if (codec_id == "V_VP8") {
video_codec = kCodecVP8; video_codec = kCodecVP8;
profile = VP8PROFILE_ANY;
} else if (codec_id == "V_VP9") { } else if (codec_id == "V_VP9") {
video_codec = kCodecVP9; video_codec = kCodecVP9;
profile = VP9PROFILE_ANY;
} else { } else {
LOG(ERROR) << "Unsupported video codec_id " << codec_id; LOG(ERROR) << "Unsupported video codec_id " << codec_id;
return false; return scoped_refptr<VideoStreamInfo>();
} }
VideoPixelFormat format =
(alpha_mode_ == 1) ? PIXEL_FORMAT_YV12A : PIXEL_FORMAT_YV12;
if (pixel_width_ <= 0 || pixel_height_ <= 0) if (pixel_width_ <= 0 || pixel_height_ <= 0)
return false; return scoped_refptr<VideoStreamInfo>();
// Set crop and display unit defaults if these elements are not present. // Set crop and display unit defaults if these elements are not present.
if (crop_bottom_ == -1) if (crop_bottom_ == -1)
@ -72,23 +80,28 @@ bool WebMVideoClient::InitializeConfig(
if (display_unit_ == -1) if (display_unit_ == -1)
display_unit_ = 0; display_unit_ = 0;
gfx::Size coded_size(pixel_width_, pixel_height_); uint16_t width_after_crop = pixel_width_ - (crop_left_ + crop_right_);
gfx::Rect visible_rect(crop_top_, crop_left_, uint16_t height_after_crop = pixel_height_ - (crop_top_ + crop_bottom_);
pixel_width_ - (crop_left_ + crop_right_),
pixel_height_ - (crop_top_ + crop_bottom_));
if (display_unit_ == 0) { if (display_unit_ == 0) {
if (display_width_ <= 0) if (display_width_ <= 0)
display_width_ = visible_rect.width(); display_width_ = width_after_crop;
if (display_height_ <= 0) if (display_height_ <= 0)
display_height_ = visible_rect.height(); display_height_ = height_after_crop;
} else if (display_unit_ == 3) { } else if (display_unit_ == 3) {
if (display_width_ <= 0 || display_height_ <= 0) if (display_width_ <= 0 || display_height_ <= 0)
return false; return scoped_refptr<VideoStreamInfo>();
} else { } else {
LOG(ERROR) << "Unsupported display unit type " << display_unit_; LOG(ERROR) << "Unsupported display unit type " << display_unit_;
return false; return scoped_refptr<VideoStreamInfo>();
} }
gfx::Size natural_size = gfx::Size(display_width_, display_height_); // Calculate sample aspect ratio.
int64_t sar_x = display_width_ * height_after_crop;
int64_t sar_y = display_height_ * width_after_crop;
int64_t gcd = GetGreatestCommonDivisor(sar_x, sar_y);
sar_x /= gcd;
sar_y /= gcd;
const uint8_t* extra_data = NULL; const uint8_t* extra_data = NULL;
size_t extra_data_size = 0; size_t extra_data_size = 0;
if (codec_private.size() > 0) { if (codec_private.size() > 0) {
@ -96,10 +109,11 @@ bool WebMVideoClient::InitializeConfig(
extra_data_size = codec_private.size(); extra_data_size = codec_private.size();
} }
config->Initialize(video_codec, profile, format, COLOR_SPACE_HD_REC709, return scoped_refptr<VideoStreamInfo>(
coded_size, visible_rect, natural_size, extra_data, new VideoStreamInfo(track_num, kWebMTimeScale, 0, video_codec,
extra_data_size, is_encrypted); VideoStreamInfo::GetCodecString(video_codec, 0, 0, 0),
return config->IsValidConfig(); "", width_after_crop, height_after_crop, sar_x, sar_y,
0, 0, extra_data, extra_data_size, is_encrypted));
} }
bool WebMVideoClient::OnUInt(int id, int64_t val) { bool WebMVideoClient::OnUInt(int id, int64_t val) {

View File

@ -8,6 +8,8 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "packager/base/memory/scoped_ptr.h"
#include "packager/media/base/video_stream_info.h"
#include "packager/media/formats/webm/webm_parser.h" #include "packager/media/formats/webm/webm_parser.h"
namespace edash_packager { namespace edash_packager {
@ -23,17 +25,17 @@ class WebMVideoClient : public WebMParserClient {
// Reset this object's state so it can process a new video track element. // Reset this object's state so it can process a new video track element.
void Reset(); void Reset();
// Initialize |config| with the data in |codec_id|, |codec_private|, // Create a VideoStreamInfo with the data in |track_num|, |codec_id|,
// |is_encrypted| and the fields parsed from the last video track element this // |codec_private|, |is_encrypted| and the fields parsed from the last video
// object was used to parse. // track element this object was used to parse.
// Returns true if |config| was successfully initialized. // Returns a VideoStreamInfo scoped_refptr if successful.
// Returns false if there was unexpected values in the provided parameters or // Returns an empty scoped_refptr if there was unexpected values in the
// video track element fields. The contents of |config| are undefined in this // provided parameters or video track element fields.
// case and should not be relied upon. scoped_refptr<VideoStreamInfo> GetVideoStreamInfo(
bool InitializeConfig(const std::string& codec_id, int64_t track_num,
const std::vector<uint8_t>& codec_private, const std::string& codec_id,
bool is_encrypted, const std::vector<uint8_t>& codec_private,
VideoDecoderConfig* config); bool is_encrypted);
private: private:
// WebMParserClient implementation. // WebMParserClient implementation.

View File

@ -38,6 +38,7 @@
'media/formats/mp2t/mp2t.gyp:mp2t', 'media/formats/mp2t/mp2t.gyp:mp2t',
'media/formats/mp4/mp4.gyp:mp4', 'media/formats/mp4/mp4.gyp:mp4',
'media/formats/mpeg/mpeg.gyp:mpeg', 'media/formats/mpeg/mpeg.gyp:mpeg',
'media/formats/webm/webm.gyp:webm',
'media/formats/wvm/wvm.gyp:wvm', 'media/formats/wvm/wvm.gyp:wvm',
'mpd/mpd.gyp:mpd_builder', 'mpd/mpd.gyp:mpd_builder',
'third_party/boringssl/boringssl.gyp:boringssl', 'third_party/boringssl/boringssl.gyp:boringssl',
@ -97,6 +98,7 @@
'media/filters/filters.gyp:filters_unittest', 'media/filters/filters.gyp:filters_unittest',
'media/formats/mp2t/mp2t.gyp:mp2t_unittest', 'media/formats/mp2t/mp2t.gyp:mp2t_unittest',
'media/formats/mp4/mp4.gyp:mp4_unittest', 'media/formats/mp4/mp4.gyp:mp4_unittest',
'media/formats/webm/webm.gyp:webm_unittest',
'media/formats/wvm/wvm.gyp:wvm_unittest', 'media/formats/wvm/wvm.gyp:wvm_unittest',
'mpd/mpd.gyp:mpd_unittest', 'mpd/mpd.gyp:mpd_unittest',
'packager_test', 'packager_test',