diff --git a/packager/media/base/media_base.gyp b/packager/media/base/media_base.gyp index 8b6b94e82a..36996adb66 100644 --- a/packager/media/base/media_base.gyp +++ b/packager/media/base/media_base.gyp @@ -69,6 +69,8 @@ 'stream_info.h', 'text_track.h', 'timestamp.h', + 'text_track_config.cc', + 'text_track_config.h', 'video_stream_info.cc', 'video_stream_info.h', 'widevine_key_source.cc', diff --git a/packager/media/base/stream_info.h b/packager/media/base/stream_info.h index 628e80b53b..7c8f7dbf12 100644 --- a/packager/media/base/stream_info.h +++ b/packager/media/base/stream_info.h @@ -19,6 +19,7 @@ enum StreamType { kStreamUnknown = 0, kStreamAudio, kStreamVideo, + kStreamText, }; /// Abstract class holds stream information. diff --git a/packager/media/formats/webm/cluster_builder.cc b/packager/media/formats/webm/cluster_builder.cc index 52283a0d48..7b25e8b091 100644 --- a/packager/media/formats/webm/cluster_builder.cc +++ b/packager/media/formats/webm/cluster_builder.cc @@ -5,7 +5,6 @@ #include "packager/media/formats/webm/cluster_builder.h" #include "packager/base/logging.h" -#include "packager/media/base/data_buffer.h" #include "packager/media/formats/webm/webm_constants.h" namespace edash_packager { diff --git a/packager/media/formats/webm/webm.gyp b/packager/media/formats/webm/webm.gyp new file mode 100644 index 0000000000..aa975cc4ef --- /dev/null +++ b/packager/media/formats/webm/webm.gyp @@ -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', + ] + }, + ], +} diff --git a/packager/media/formats/webm/webm_audio_client.cc b/packager/media/formats/webm/webm_audio_client.cc index 8ca849b34b..ed799aa829 100644 --- a/packager/media/formats/webm/webm_audio_client.cc +++ b/packager/media/formats/webm/webm_audio_client.cc @@ -4,10 +4,14 @@ #include "packager/media/formats/webm/webm_audio_client.h" -#include "packager/media/base/audio_decoder_config.h" -#include "packager/media/base/channel_layout.h" +#include "packager/base/logging.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 media { @@ -24,16 +28,12 @@ void WebMAudioClient::Reset() { output_samples_per_second_ = -1; } -bool WebMAudioClient::InitializeConfig( +scoped_refptr WebMAudioClient::GetAudioStreamInfo( + int64_t track_num, const std::string& codec_id, const std::vector& codec_private, - int64_t seek_preroll, - int64_t codec_delay, - bool is_encrypted, - AudioDecoderConfig* config) { - DCHECK(config); - SampleFormat sample_format = kSampleFormatPlanarF32; - + const std::string& language, + bool is_encrypted) { AudioCodec audio_codec = kUnknownAudioCodec; if (codec_id == "A_VORBIS") { audio_codec = kCodecVorbis; @@ -41,32 +41,21 @@ bool WebMAudioClient::InitializeConfig( audio_codec = kCodecOpus; } else { LOG(ERROR) << "Unsupported audio codec_id " << codec_id; - return false; + return scoped_refptr(); } if (samples_per_second_ <= 0) - return false; + return scoped_refptr(); // Set channel layout default if a Channels element was not present. if (channels_ == -1) channels_ = 1; - ChannelLayout channel_layout = GuessChannelLayout(channels_); - - 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_; - + uint32_t sampling_frequency = samples_per_second_; // 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 if (audio_codec == kCodecOpus) { - samples_per_second = 48000; - sample_format = kSampleFormatF32; + sampling_frequency = 48000; } const uint8_t* extra_data = NULL; @@ -76,27 +65,12 @@ bool WebMAudioClient::InitializeConfig( extra_data_size = codec_private.size(); } - // Convert |codec_delay| from nanoseconds into frames. - int codec_delay_in_frames = 0; - if (codec_delay != -1) { - codec_delay_in_frames = - 0.5 + - samples_per_second * (static_cast(codec_delay) / - 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(); + const uint32_t kSampleSizeInBits = 4u; + return scoped_refptr(new AudioStreamInfo( + track_num, kWebMTimeScale, 0, audio_codec, + AudioStreamInfo::GetCodecString(audio_codec, 0), language, + kSampleSizeInBits, channels_, sampling_frequency, extra_data, + extra_data_size, is_encrypted)); } bool WebMAudioClient::OnUInt(int id, int64_t val) { diff --git a/packager/media/formats/webm/webm_audio_client.h b/packager/media/formats/webm/webm_audio_client.h index 558ae6b470..92b3a21520 100644 --- a/packager/media/formats/webm/webm_audio_client.h +++ b/packager/media/formats/webm/webm_audio_client.h @@ -8,6 +8,8 @@ #include #include +#include "packager/base/memory/scoped_ptr.h" +#include "packager/media/base/audio_stream_info.h" #include "packager/media/formats/webm/webm_parser.h" 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. void Reset(); - // Initialize |config| with the data in |codec_id|, |codec_private|, - // |is_encrypted| and the fields parsed from the last audio track element this - // object was used to parse. - // Returns true if |config| was successfully initialized. - // Returns false if there was unexpected values in the provided parameters or - // audio track element fields. - bool InitializeConfig(const std::string& codec_id, - const std::vector& codec_private, - const int64_t seek_preroll, - const int64_t codec_delay, - bool is_encrypted, - AudioDecoderConfig* config); + // Create an AudioStreamInfo with the data in |track_num|, |codec_id|, + // |codec_private|, |is_encrypted| and the fields parsed from the last audio + // track element this object was used to parse. + // Returns an AudioStreamInfo scoped_refptr if successful. + // Returns an empty scoped_refptr if there was unexpected values in the + // provided parameters or audio track element fields. + scoped_refptr GetAudioStreamInfo( + int64_t track_num, + const std::string& codec_id, + const std::vector& codec_private, + const std::string& language, + bool is_encrypted); private: // WebMParserClient implementation. diff --git a/packager/media/formats/webm/webm_crypto_helpers.cc b/packager/media/formats/webm/webm_crypto_helpers.cc index f53e5118f1..0dc0ee7f06 100644 --- a/packager/media/formats/webm/webm_crypto_helpers.cc +++ b/packager/media/formats/webm/webm_crypto_helpers.cc @@ -54,9 +54,9 @@ bool WebMCreateDecryptConfig(const uint8_t* data, } decrypt_config->reset(new DecryptConfig( - std::string(reinterpret_cast(key_id), key_id_size), - counter_block, - std::vector())); + std::vector(key_id, key_id + key_id_size), + std::vector(counter_block.begin(), counter_block.end()), + frame_offset, std::vector())); *data_offset = frame_offset; return true; diff --git a/packager/media/formats/webm/webm_crypto_helpers.h b/packager/media/formats/webm/webm_crypto_helpers.h index 9c6eb23e93..b3674ca8f4 100644 --- a/packager/media/formats/webm/webm_crypto_helpers.h +++ b/packager/media/formats/webm/webm_crypto_helpers.h @@ -9,7 +9,7 @@ #include "packager/base/macros.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 media { diff --git a/packager/media/formats/webm/webm_stream_parser.cc b/packager/media/formats/webm/webm_stream_parser.cc index 218d33b1d3..54a25d10e5 100644 --- a/packager/media/formats/webm/webm_stream_parser.cc +++ b/packager/media/formats/webm/webm_stream_parser.cc @@ -9,7 +9,7 @@ #include "packager/base/callback.h" #include "packager/base/callback_helpers.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_constants.h" #include "packager/media/formats/webm/webm_content_encodings.h" @@ -29,29 +29,18 @@ WebMStreamParser::~WebMStreamParser() { void WebMStreamParser::Init( const InitCB& init_cb, - const NewConfigCB& config_cb, - const NewBuffersCB& new_buffers_cb, - bool ignore_text_tracks, - const EncryptedMediaInitDataCB& encrypted_media_init_data_cb, - const NewMediaSegmentCB& new_segment_cb, - const base::Closure& end_of_segment_cb) { + const NewSampleCB& new_sample_cb, + KeySource* decryption_key_source) { DCHECK_EQ(state_, kWaitingForInit); DCHECK(init_cb_.is_null()); DCHECK(!init_cb.is_null()); - DCHECK(!config_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()); + DCHECK(!new_sample_cb.is_null()); ChangeState(kParsingHeaders); init_cb_ = init_cb; - config_cb_ = config_cb; - new_buffers_cb_ = new_buffers_cb; - ignore_text_tracks_ = ignore_text_tracks; - encrypted_media_init_data_cb_ = encrypted_media_init_data_cb; - new_segment_cb_ = new_segment_cb; - end_of_segment_cb_ = end_of_segment_cb; + new_sample_cb_ = new_sample_cb; + decryption_key_source_ = decryption_key_source; + ignore_text_tracks_ = true; } void WebMStreamParser::Flush() { @@ -62,7 +51,6 @@ void WebMStreamParser::Flush() { cluster_parser_->Reset(); if (state_ == kParsingClusters) { ChangeState(kParsingHeaders); - end_of_segment_cb_.Run(); } } @@ -158,7 +146,6 @@ int WebMStreamParser::ParseInfoAndTracks(const uint8_t* data, int size) { return -1; } ChangeState(kParsingClusters); - new_segment_cb_.Run(); return 0; break; case kWebMIdSegment: @@ -196,38 +183,22 @@ int WebMStreamParser::ParseInfoAndTracks(const uint8_t* data, int size) { bytes_parsed += result; 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) { - int64_t duration_in_us = info_parser.duration() * timecode_scale_in_us; - params.duration = base::TimeDelta::FromMicroseconds(duration_in_us); - } - - params.timeline_offset = info_parser.date_utc(); - - 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()) + std::vector> streams; + scoped_refptr audio_stream_info = + tracks_parser.audio_stream_info(); + streams.push_back(tracks_parser.audio_stream_info()); + streams.back()->set_duration(duration_in_us); + if (streams.back()->is_encrypted()) OnEncryptedMediaInitData(tracks_parser.audio_encryption_key_id()); - const VideoDecoderConfig& video_config = tracks_parser.video_decoder_config(); - if (video_config.is_encrypted()) + streams.push_back(tracks_parser.video_stream_info()); + streams.back()->set_duration(duration_in_us); + if (streams.back()->is_encrypted()) OnEncryptedMediaInitData(tracks_parser.video_encryption_key_id()); - if (!config_cb_.Run(audio_config, - video_config, - tracks_parser.text_tracks())) { - DVLOG(1) << "New config data isn't allowed."; - return -1; - } + init_cb_.Run(streams); cluster_parser_.reset(new WebMClusterParser( 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.text_tracks(), tracks_parser.ignored_tracks(), tracks_parser.audio_encryption_key_id(), - tracks_parser.video_encryption_key_id(), audio_config.codec()); - - if (!init_cb_.is_null()) - base::ResetAndReturn(&init_cb_).Run(params); + tracks_parser.video_encryption_key_id(), audio_stream_info->codec(), + new_sample_cb_)); return bytes_parsed; } @@ -252,29 +221,16 @@ int WebMStreamParser::ParseCluster(const uint8_t* data, int size) { if (bytes_parsed < 0) 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(); - - 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) { ChangeState(kParsingHeaders); - end_of_segment_cb_.Run(); } return bytes_parsed; } void WebMStreamParser::OnEncryptedMediaInitData(const std::string& key_id) { - std::vector key_id_vector(key_id.begin(), key_id.end()); - encrypted_media_init_data_cb_.Run(EmeInitDataType::WEBM, key_id_vector); + NOTIMPLEMENTED() << "WebM decryption is not implemented yet."; } } // namespace media diff --git a/packager/media/formats/webm/webm_stream_parser.h b/packager/media/formats/webm/webm_stream_parser.h index 6ab16b5ddd..920547d8c6 100644 --- a/packager/media/formats/webm/webm_stream_parser.h +++ b/packager/media/formats/webm/webm_stream_parser.h @@ -7,29 +7,23 @@ #include "packager/base/callback_forward.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/stream_parser.h" -#include "packager/media/base/video_decoder_config.h" +#include "packager/media/base/media_parser.h" namespace edash_packager { namespace media { class WebMClusterParser; -class WebMStreamParser : public StreamParser { +class WebMStreamParser : public MediaParser { public: WebMStreamParser(); ~WebMStreamParser() override; // StreamParser implementation. void Init(const InitCB& init_cb, - const NewConfigCB& config_cb, - const NewBuffersCB& new_buffers_cb, - bool ignore_text_tracks, - const EncryptedMediaInitDataCB& encrypted_media_init_data_cb, - const NewMediaSegmentCB& new_segment_cb, - const base::Closure& end_of_segment_cb) override; + const NewSampleCB& new_sample_cb, + KeySource* decryption_key_source) override; void Flush() override; bool Parse(const uint8_t* buf, int size) override; @@ -67,13 +61,9 @@ class WebMStreamParser : public StreamParser { State state_; InitCB init_cb_; - NewConfigCB config_cb_; - NewBuffersCB new_buffers_cb_; + NewSampleCB new_sample_cb_; + KeySource* decryption_key_source_; bool ignore_text_tracks_; - EncryptedMediaInitDataCB encrypted_media_init_data_cb_; - - NewMediaSegmentCB new_segment_cb_; - base::Closure end_of_segment_cb_; bool unknown_segment_size_; diff --git a/packager/media/formats/webm/webm_tracks_parser.cc b/packager/media/formats/webm/webm_tracks_parser.cc index 7fd54272d3..966e6ca48c 100644 --- a/packager/media/formats/webm/webm_tracks_parser.cc +++ b/packager/media/formats/webm/webm_tracks_parser.cc @@ -7,7 +7,7 @@ #include "packager/base/logging.h" #include "packager/base/strings/string_number_conversions.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_content_encodings.h" @@ -30,19 +30,19 @@ static TextKind CodecIdToTextKind(const std::string& codec_id) { return kTextNone; } -static base::TimeDelta PrecisionCappedDefaultDuration( +static int64_t PrecisionCappedDefaultDuration( const double timecode_scale_in_us, const int64_t duration_in_ns) { if (duration_in_ns <= 0) - return kNoTimestamp(); + return kNoTimestamp; int64_t mult = duration_in_ns / 1000; mult /= timecode_scale_in_us; if (mult == 0) - return kNoTimestamp(); + return kNoTimestamp; mult = static_cast(mult) * timecode_scale_in_us; - return base::TimeDelta::FromMicroseconds(mult); + return mult; } WebMTracksParser::WebMTracksParser(bool ignore_text_tracks) @@ -70,10 +70,10 @@ int WebMTracksParser::Parse(const uint8_t* buf, int size) { track_language_.clear(); audio_track_num_ = -1; audio_default_duration_ = -1; - audio_decoder_config_ = AudioDecoderConfig(); + audio_stream_info_ = nullptr; video_track_num_ = -1; video_default_duration_ = -1; - video_decoder_config_ = VideoDecoderConfig(); + video_stream_info_ = nullptr; text_tracks_.clear(); ignored_tracks_.clear(); @@ -87,13 +87,13 @@ int WebMTracksParser::Parse(const uint8_t* buf, int size) { return parser.IsParsingComplete() ? result : 0; } -base::TimeDelta WebMTracksParser::GetAudioDefaultDuration( +int64_t WebMTracksParser::GetAudioDefaultDuration( const double timecode_scale_in_us) const { return PrecisionCappedDefaultDuration(timecode_scale_in_us, audio_default_duration_); } -base::TimeDelta WebMTracksParser::GetVideoDefaultDuration( +int64_t WebMTracksParser::GetVideoDefaultDuration( const double timecode_scale_in_us) const { return PrecisionCappedDefaultDuration(timecode_scale_in_us, video_default_duration_); @@ -201,12 +201,12 @@ bool WebMTracksParser::OnListEnd(int id) { } audio_default_duration_ = default_duration_; - DCHECK(!audio_decoder_config_.IsValidConfig()); - if (!audio_client_.InitializeConfig( - codec_id_, codec_private_, seek_preroll_, codec_delay_, - !audio_encryption_key_id_.empty(), &audio_decoder_config_)) { + DCHECK(!audio_stream_info_); + audio_stream_info_ = audio_client_.GetAudioStreamInfo( + audio_track_num_, codec_id_, codec_private_, track_language_, + !audio_encryption_key_id_.empty()); + if (!audio_stream_info_) return false; - } } else { DLOG(INFO) << "Ignoring audio track " << track_num_; ignored_tracks_.insert(track_num_); @@ -223,12 +223,12 @@ bool WebMTracksParser::OnListEnd(int id) { } video_default_duration_ = default_duration_; - DCHECK(!video_decoder_config_.IsValidConfig()); - if (!video_client_.InitializeConfig( - codec_id_, codec_private_, !video_encryption_key_id_.empty(), - &video_decoder_config_)) { + DCHECK(!video_stream_info_); + video_stream_info_ = video_client_.GetVideoStreamInfo( + video_track_num_, codec_id_, codec_private_, + !video_encryption_key_id_.empty()); + if (!video_stream_info_) return false; - } } else { DLOG(INFO) << "Ignoring video track " << track_num_; ignored_tracks_.insert(track_num_); diff --git a/packager/media/formats/webm/webm_tracks_parser.h b/packager/media/formats/webm/webm_tracks_parser.h index 28c8ca4cbc..e79e6d4338 100644 --- a/packager/media/formats/webm/webm_tracks_parser.h +++ b/packager/media/formats/webm/webm_tracks_parser.h @@ -13,9 +13,9 @@ #include "packager/base/compiler_specific.h" #include "packager/base/memory/scoped_ptr.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/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_content_encodings_client.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 // precision not greater than |timecode_scale_in_us|. Defaults to // kNoTimestamp(). - base::TimeDelta GetAudioDefaultDuration( - const double timecode_scale_in_us) const; - base::TimeDelta GetVideoDefaultDuration( - const double timecode_scale_in_us) const; + int64_t GetAudioDefaultDuration(const double timecode_scale_in_us) const; + int64_t GetVideoDefaultDuration(const double timecode_scale_in_us) const; const std::set& ignored_tracks() const { return ignored_tracks_; } @@ -55,16 +53,16 @@ class WebMTracksParser : public WebMParserClient { return audio_encryption_key_id_; } - const AudioDecoderConfig& audio_decoder_config() { - return audio_decoder_config_; + scoped_refptr audio_stream_info() { + return audio_stream_info_; } const std::string& video_encryption_key_id() const { return video_encryption_key_id_; } - const VideoDecoderConfig& video_decoder_config() { - return video_decoder_config_; + scoped_refptr video_stream_info() { + return video_stream_info_; } typedef std::map TextTracks; @@ -104,10 +102,10 @@ class WebMTracksParser : public WebMParserClient { std::string video_encryption_key_id_; WebMAudioClient audio_client_; - AudioDecoderConfig audio_decoder_config_; + scoped_refptr audio_stream_info_; WebMVideoClient video_client_; - VideoDecoderConfig video_decoder_config_; + scoped_refptr video_stream_info_; DISALLOW_COPY_AND_ASSIGN(WebMTracksParser); }; diff --git a/packager/media/formats/webm/webm_tracks_parser_unittest.cc b/packager/media/formats/webm/webm_tracks_parser_unittest.cc index 92f962e05e..63171ec43c 100644 --- a/packager/media/formats/webm/webm_tracks_parser_unittest.cc +++ b/packager/media/formats/webm/webm_tracks_parser_unittest.cc @@ -8,8 +8,7 @@ #include #include "packager/base/logging.h" -#include "packager/media/base/channel_layout.h" -#include "packager/media/base/timestamp_constants.h" +#include "packager/media/base/timestamp.h" #include "packager/media/formats/webm/tracks_builder.h" #include "packager/media/formats/webm/webm_constants.h" @@ -140,20 +139,22 @@ TEST_F(WebMTracksParserTest, AudioVideoDefaultDurationUnset) { EXPECT_LE(0, result); EXPECT_EQ(static_cast(buf.size()), result); - EXPECT_EQ(kNoTimestamp(), + EXPECT_EQ(kNoTimestamp, parser->GetAudioDefaultDuration(kDefaultTimecodeScaleInUs)); - EXPECT_EQ(kNoTimestamp(), + EXPECT_EQ(kNoTimestamp, parser->GetVideoDefaultDuration(kDefaultTimecodeScaleInUs)); - const VideoDecoderConfig& video_config = parser->video_decoder_config(); - EXPECT_TRUE(video_config.IsValidConfig()); - EXPECT_EQ(320, video_config.coded_size().width()); - EXPECT_EQ(240, video_config.coded_size().height()); + scoped_refptr video_stream_info = + parser->video_stream_info(); + EXPECT_TRUE(video_stream_info); + EXPECT_EQ(320u, video_stream_info->width()); + EXPECT_EQ(240u, video_stream_info->height()); - const AudioDecoderConfig& audio_config = parser->audio_decoder_config(); - EXPECT_TRUE(audio_config.IsValidConfig()); - EXPECT_EQ(CHANNEL_LAYOUT_STEREO, audio_config.channel_layout()); - EXPECT_EQ(8000, audio_config.samples_per_second()); + scoped_refptr audio_stream_info = + parser->audio_stream_info(); + EXPECT_TRUE(audio_stream_info); + EXPECT_EQ(2u, audio_stream_info->num_channels()); + EXPECT_EQ(8000u, audio_stream_info->sampling_frequency()); } TEST_F(WebMTracksParserTest, AudioVideoDefaultDurationSet) { @@ -169,14 +170,12 @@ TEST_F(WebMTracksParserTest, AudioVideoDefaultDurationSet) { EXPECT_LE(0, result); EXPECT_EQ(static_cast(buf.size()), result); - EXPECT_EQ(base::TimeDelta::FromMicroseconds(12000), - parser->GetAudioDefaultDuration(kDefaultTimecodeScaleInUs)); - EXPECT_EQ(base::TimeDelta::FromMicroseconds(985000), + EXPECT_EQ(12000, parser->GetAudioDefaultDuration(kDefaultTimecodeScaleInUs)); + EXPECT_EQ(985000, parser->GetVideoDefaultDuration(5000.0)); // 5 ms resolution - EXPECT_EQ(kNoTimestamp(), parser->GetAudioDefaultDuration(12346.0)); - EXPECT_EQ(base::TimeDelta::FromMicroseconds(12345), - parser->GetAudioDefaultDuration(12345.0)); - EXPECT_EQ(base::TimeDelta::FromMicroseconds(12003), + EXPECT_EQ(kNoTimestamp, parser->GetAudioDefaultDuration(12346.0)); + EXPECT_EQ(12345, parser->GetAudioDefaultDuration(12345.0)); + EXPECT_EQ(12003, parser->GetAudioDefaultDuration(1000.3)); // 1.0003 ms resolution } diff --git a/packager/media/formats/webm/webm_video_client.cc b/packager/media/formats/webm/webm_video_client.cc index 4dbdb558fd..201993d7d9 100644 --- a/packager/media/formats/webm/webm_video_client.cc +++ b/packager/media/formats/webm/webm_video_client.cc @@ -4,9 +4,25 @@ #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" +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 media { @@ -30,31 +46,23 @@ void WebMVideoClient::Reset() { alpha_mode_ = -1; } -bool WebMVideoClient::InitializeConfig( +scoped_refptr WebMVideoClient::GetVideoStreamInfo( + int64_t track_num, const std::string& codec_id, const std::vector& codec_private, - bool is_encrypted, - VideoDecoderConfig* config) { - DCHECK(config); - + bool is_encrypted) { VideoCodec video_codec = kUnknownVideoCodec; - VideoCodecProfile profile = VIDEO_CODEC_PROFILE_UNKNOWN; if (codec_id == "V_VP8") { video_codec = kCodecVP8; - profile = VP8PROFILE_ANY; } else if (codec_id == "V_VP9") { video_codec = kCodecVP9; - profile = VP9PROFILE_ANY; } else { LOG(ERROR) << "Unsupported video codec_id " << codec_id; - return false; + return scoped_refptr(); } - VideoPixelFormat format = - (alpha_mode_ == 1) ? PIXEL_FORMAT_YV12A : PIXEL_FORMAT_YV12; - if (pixel_width_ <= 0 || pixel_height_ <= 0) - return false; + return scoped_refptr(); // Set crop and display unit defaults if these elements are not present. if (crop_bottom_ == -1) @@ -72,23 +80,28 @@ bool WebMVideoClient::InitializeConfig( if (display_unit_ == -1) display_unit_ = 0; - gfx::Size coded_size(pixel_width_, pixel_height_); - gfx::Rect visible_rect(crop_top_, crop_left_, - pixel_width_ - (crop_left_ + crop_right_), - pixel_height_ - (crop_top_ + crop_bottom_)); + uint16_t width_after_crop = pixel_width_ - (crop_left_ + crop_right_); + uint16_t height_after_crop = pixel_height_ - (crop_top_ + crop_bottom_); + if (display_unit_ == 0) { if (display_width_ <= 0) - display_width_ = visible_rect.width(); + display_width_ = width_after_crop; if (display_height_ <= 0) - display_height_ = visible_rect.height(); + display_height_ = height_after_crop; } else if (display_unit_ == 3) { if (display_width_ <= 0 || display_height_ <= 0) - return false; + return scoped_refptr(); } else { LOG(ERROR) << "Unsupported display unit type " << display_unit_; - return false; + return scoped_refptr(); } - 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; size_t extra_data_size = 0; if (codec_private.size() > 0) { @@ -96,10 +109,11 @@ bool WebMVideoClient::InitializeConfig( extra_data_size = codec_private.size(); } - config->Initialize(video_codec, profile, format, COLOR_SPACE_HD_REC709, - coded_size, visible_rect, natural_size, extra_data, - extra_data_size, is_encrypted); - return config->IsValidConfig(); + return scoped_refptr( + new VideoStreamInfo(track_num, kWebMTimeScale, 0, video_codec, + VideoStreamInfo::GetCodecString(video_codec, 0, 0, 0), + "", 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) { diff --git a/packager/media/formats/webm/webm_video_client.h b/packager/media/formats/webm/webm_video_client.h index ac8238e7ff..a9f6ef9f49 100644 --- a/packager/media/formats/webm/webm_video_client.h +++ b/packager/media/formats/webm/webm_video_client.h @@ -8,6 +8,8 @@ #include #include +#include "packager/base/memory/scoped_ptr.h" +#include "packager/media/base/video_stream_info.h" #include "packager/media/formats/webm/webm_parser.h" 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. void Reset(); - // Initialize |config| with the data in |codec_id|, |codec_private|, - // |is_encrypted| and the fields parsed from the last video track element this - // object was used to parse. - // Returns true if |config| was successfully initialized. - // Returns false if there was unexpected values in the provided parameters or - // video track element fields. The contents of |config| are undefined in this - // case and should not be relied upon. - bool InitializeConfig(const std::string& codec_id, - const std::vector& codec_private, - bool is_encrypted, - VideoDecoderConfig* config); + // Create a VideoStreamInfo with the data in |track_num|, |codec_id|, + // |codec_private|, |is_encrypted| and the fields parsed from the last video + // track element this object was used to parse. + // Returns a VideoStreamInfo scoped_refptr if successful. + // Returns an empty scoped_refptr if there was unexpected values in the + // provided parameters or video track element fields. + scoped_refptr GetVideoStreamInfo( + int64_t track_num, + const std::string& codec_id, + const std::vector& codec_private, + bool is_encrypted); private: // WebMParserClient implementation. diff --git a/packager/packager.gyp b/packager/packager.gyp index 7cc2973d4c..fdbb647c4b 100644 --- a/packager/packager.gyp +++ b/packager/packager.gyp @@ -38,6 +38,7 @@ 'media/formats/mp2t/mp2t.gyp:mp2t', 'media/formats/mp4/mp4.gyp:mp4', 'media/formats/mpeg/mpeg.gyp:mpeg', + 'media/formats/webm/webm.gyp:webm', 'media/formats/wvm/wvm.gyp:wvm', 'mpd/mpd.gyp:mpd_builder', 'third_party/boringssl/boringssl.gyp:boringssl', @@ -97,6 +98,7 @@ 'media/filters/filters.gyp:filters_unittest', 'media/formats/mp2t/mp2t.gyp:mp2t_unittest', 'media/formats/mp4/mp4.gyp:mp4_unittest', + 'media/formats/webm/webm.gyp:webm_unittest', 'media/formats/wvm/wvm.gyp:wvm_unittest', 'mpd/mpd.gyp:mpd_unittest', 'packager_test',