diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-07-16 11:45:35 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-07-17 08:59:23 +0000 |
commit | 552906b0f222c5d5dd11b9fd73829d510980461a (patch) | |
tree | 3a11e6ed0538a81dd83b20cf3a4783e297f26d91 /chromium/media/base | |
parent | 1b05827804eaf047779b597718c03e7d38344261 (diff) | |
download | qtwebengine-chromium-552906b0f222c5d5dd11b9fd73829d510980461a.tar.gz |
BASELINE: Update Chromium to 83.0.4103.122
Change-Id: Ie3a82f5bb0076eec2a7c6a6162326b4301ee291e
Reviewed-by: Michael BrĂ¼ning <michael.bruning@qt.io>
Diffstat (limited to 'chromium/media/base')
169 files changed, 4096 insertions, 1580 deletions
diff --git a/chromium/media/base/BUILD.gn b/chromium/media/base/BUILD.gn index ed396499e1a..80cc2864762 100644 --- a/chromium/media/base/BUILD.gn +++ b/chromium/media/base/BUILD.gn @@ -60,6 +60,8 @@ jumbo_source_set("base") { "audio_fifo.h", "audio_hash.cc", "audio_hash.h", + "audio_power_monitor.cc", + "audio_power_monitor.h", "audio_processing.cc", "audio_processing.h", "audio_pull_fifo.cc", @@ -85,6 +87,7 @@ jumbo_source_set("base") { "bit_reader_core.h", "bitstream_buffer.cc", "bitstream_buffer.h", + "buffering_state.cc", "buffering_state.h", "byte_queue.cc", "byte_queue.h", @@ -153,6 +156,8 @@ jumbo_source_set("base") { "flinging_controller.h", "format_utils.cc", "format_utils.h", + "frame_rate_estimator.cc", + "frame_rate_estimator.h", "hdr_metadata.cc", "hdr_metadata.h", "key_system_names.cc", @@ -176,16 +181,22 @@ jumbo_source_set("base") { "media_export.h", "media_log.cc", "media_log.h", - "media_log_event.h", + "media_log_events.cc", + "media_log_events.h", + "media_log_message_levels.cc", + "media_log_message_levels.h", "media_log_properties.cc", "media_log_properties.h", - "media_log_properties_helper.h", + "media_log_record.h", + "media_log_type_enforcement.h", "media_observer.cc", "media_observer.h", "media_permission.cc", "media_permission.h", "media_resource.cc", "media_resource.h", + "media_serializers.h", + "media_serializers_base.h", "media_status.cc", "media_status.h", "media_status_observer.h", @@ -203,6 +214,8 @@ jumbo_source_set("base") { "media_url_params.h", "media_util.cc", "media_util.h", + "memory_dump_provider_proxy.cc", + "memory_dump_provider_proxy.h", "mime_util.cc", "mime_util.h", "mime_util_internal.cc", @@ -256,6 +269,11 @@ jumbo_source_set("base") { "simple_watch_timer.h", "sinc_resampler.cc", "sinc_resampler.h", + "speech_recognition_client.h", + "status.cc", + "status.h", + "status_codes.cc", + "status_codes.h", "stream_parser.cc", "stream_parser.h", "stream_parser_buffer.cc", @@ -406,12 +424,18 @@ jumbo_source_set("base") { } else { sources += [ "demuxer_memory_limit_default.cc" ] } + + if (enable_media_drm_storage) { + sources += [ + "media_drm_key_type.h", + "media_drm_storage.cc", + "media_drm_storage.h", + ] + } } source_set("video_facing") { - sources = [ - "video_facing.h", - ] + sources = [ "video_facing.h" ] } if (is_android) { @@ -467,9 +491,7 @@ static_library("test_support") { # Do not add any other //media deps except this; this ensures other # test_support targets all use the same //media component to avoid duplicate # and undefined symbol issues on Windows. - public_deps = [ - "//media", - ] + public_deps = [ "//media" ] deps = [ "//base", @@ -499,6 +521,7 @@ source_set("unit_tests") { "audio_latency_unittest.cc", "audio_parameters_unittest.cc", "audio_point_unittest.cc", + "audio_power_monitor_unittest.cc", "audio_pull_fifo_unittest.cc", "audio_push_fifo_unittest.cc", "audio_renderer_mixer_input_unittest.cc", @@ -522,8 +545,10 @@ source_set("unit_tests") { "fake_demuxer_stream_unittest.cc", "fallback_video_decoder_unittest.cc", "feedback_signal_accumulator_unittest.cc", + "frame_rate_estimator_unittest.cc", "key_systems_unittest.cc", "media_log_unittest.cc", + "media_serializers_unittest.cc", "media_url_demuxer_unittest.cc", "mime_util_unittest.cc", "moving_average_unittest.cc", @@ -537,6 +562,7 @@ source_set("unit_tests") { "serial_runner_unittest.cc", "silent_sink_suspender_unittest.cc", "sinc_resampler_unittest.cc", + "status_unittest.cc", "stream_parser_unittest.cc", "subsample_entry_unittest.cc", "supported_types_unittest.cc", @@ -582,6 +608,14 @@ source_set("unit_tests") { if (is_linux || is_win) { sources += [ "keyboard_event_counter_unittest.cc" ] } + if (is_win) { + sources += [ "win/dxgi_device_scope_handle_unittest.cc" ] + deps += [ "//media/base/win:media_foundation_util" ] + libs = [ + "d3d11.lib", + "mfplat.lib", + ] + } } source_set("perftests") { @@ -609,9 +643,7 @@ source_set("perftests") { } fuzzer_test("media_bit_reader_fuzzer") { - sources = [ - "bit_reader_fuzzertest.cc", - ] + sources = [ "bit_reader_fuzzertest.cc" ] deps = [ "//base", "//media:test_support", @@ -619,9 +651,7 @@ fuzzer_test("media_bit_reader_fuzzer") { } fuzzer_test("media_container_names_fuzzer") { - sources = [ - "container_names_fuzzertest.cc", - ] + sources = [ "container_names_fuzzertest.cc" ] deps = [ "//base", "//media:test_support", diff --git a/chromium/media/base/android/BUILD.gn b/chromium/media/base/android/BUILD.gn index 89c724d5379..480089fa540 100644 --- a/chromium/media/base/android/BUILD.gn +++ b/chromium/media/base/android/BUILD.gn @@ -47,9 +47,6 @@ if (is_android) { "media_drm_bridge_delegate.h", "media_drm_bridge_factory.cc", "media_drm_bridge_factory.h", - "media_drm_key_type.h", - "media_drm_storage.cc", - "media_drm_storage.h", "media_drm_storage_bridge.cc", "media_drm_storage_bridge.h", "media_player_bridge.cc", @@ -66,9 +63,7 @@ if (is_android) { "stream_texture_wrapper.h", ] configs += [ "//media:subcomponent_config" ] - public_deps = [ - ":media_jni_headers", - ] + public_deps = [ ":media_jni_headers" ] deps = [ "//media/audio", "//media/base", @@ -154,15 +149,13 @@ if (is_android) { } java_cpp_strings("java_switches") { - sources = [ - "//media/base/media_switches.cc", - ] + sources = [ "//media/base/media_switches.cc" ] template = "//media/base/android/java_templates/MediaSwitches.java.tmpl" } android_resources("media_java_resources") { custom_package = "org.chromium.media" - resource_dirs = [ "java/res" ] + sources = [ "java/res/raw/empty.wav" ] } android_library("media_java") { @@ -178,7 +171,7 @@ if (is_android) { ":java_switches", "//media/base:java_enums", ] - java_files = [ + sources = [ "java/src/org/chromium/media/AudioManagerAndroid.java", "java/src/org/chromium/media/AudioTrackOutputStream.java", "java/src/org/chromium/media/BitrateAdjuster.java", @@ -186,8 +179,8 @@ if (is_android) { "java/src/org/chromium/media/HdrMetadata.java", "java/src/org/chromium/media/MediaCodecBridge.java", "java/src/org/chromium/media/MediaCodecBridgeBuilder.java", - "java/src/org/chromium/media/MediaCodecUtil.java", "java/src/org/chromium/media/MediaCodecEncoder.java", + "java/src/org/chromium/media/MediaCodecUtil.java", "java/src/org/chromium/media/MediaDrmBridge.java", "java/src/org/chromium/media/MediaDrmSessionManager.java", "java/src/org/chromium/media/MediaDrmStorageBridge.java", @@ -199,7 +192,7 @@ if (is_android) { } junit_binary("media_base_junit_tests") { - java_files = [ + sources = [ "java/src/test/org/chromium/media/AudioTrackOutputStreamTest.java", "java/src/test/org/chromium/media/BitrateAdjusterTest.java", "java/src/test/org/chromium/media/MediaFormatBuilderTest.java", diff --git a/chromium/media/base/android/android_cdm_factory.cc b/chromium/media/base/android/android_cdm_factory.cc index 4844d1316eb..a2c20394f23 100644 --- a/chromium/media/base/android/android_cdm_factory.cc +++ b/chromium/media/base/android/android_cdm_factory.cc @@ -28,16 +28,16 @@ void ReportMediaDrmBridgeKeySystemSupport(bool supported) { } // namespace -AndroidCdmFactory::AndroidCdmFactory(const CreateFetcherCB& create_fetcher_cb, - const CreateStorageCB& create_storage_cb) - : create_fetcher_cb_(create_fetcher_cb), - create_storage_cb_(create_storage_cb) {} +AndroidCdmFactory::AndroidCdmFactory(CreateFetcherCB create_fetcher_cb, + CreateStorageCB create_storage_cb) + : create_fetcher_cb_(std::move(create_fetcher_cb)), + create_storage_cb_(std::move(create_storage_cb)) {} AndroidCdmFactory::~AndroidCdmFactory() { weak_factory_.InvalidateWeakPtrs(); for (auto& pending_creation : pending_creations_) { - auto& cdm_created_cb = pending_creation.second.second; - cdm_created_cb.Run(nullptr, "CDM creation aborted"); + CdmCreatedCB cdm_created_cb = std::move(pending_creation.second.second); + std::move(cdm_created_cb).Run(nullptr, "CDM creation aborted"); } } @@ -49,14 +49,15 @@ void AndroidCdmFactory::Create( const SessionClosedCB& session_closed_cb, const SessionKeysChangeCB& session_keys_change_cb, const SessionExpirationUpdateCB& session_expiration_update_cb, - const CdmCreatedCB& cdm_created_cb) { + CdmCreatedCB cdm_created_cb) { DVLOG(1) << __func__; // Bound |cdm_created_cb| so we always fire it asynchronously. - CdmCreatedCB bound_cdm_created_cb = BindToCurrentLoop(cdm_created_cb); + CdmCreatedCB bound_cdm_created_cb = + BindToCurrentLoop(std::move(cdm_created_cb)); if (security_origin.opaque()) { - bound_cdm_created_cb.Run(nullptr, "Invalid origin."); + std::move(bound_cdm_created_cb).Run(nullptr, "Invalid origin."); return; } @@ -67,7 +68,7 @@ void AndroidCdmFactory::Create( scoped_refptr<ContentDecryptionModule> cdm( new AesDecryptor(session_message_cb, session_closed_cb, session_keys_change_cb, session_expiration_update_cb)); - bound_cdm_created_cb.Run(cdm, ""); + std::move(bound_cdm_created_cb).Run(cdm, ""); return; } @@ -75,8 +76,8 @@ void AndroidCdmFactory::Create( if (!MediaDrmBridge::IsKeySystemSupported(key_system)) { ReportMediaDrmBridgeKeySystemSupport(false); - bound_cdm_created_cb.Run( - nullptr, "Key system not supported unexpectedly: " + key_system); + std::move(bound_cdm_created_cb) + .Run(nullptr, "Key system not supported unexpectedly: " + key_system); return; } @@ -88,13 +89,14 @@ void AndroidCdmFactory::Create( creation_id_++; pending_creations_.emplace( - creation_id_, PendingCreation(std::move(factory), bound_cdm_created_cb)); - - raw_factory->Create( - key_system, security_origin, cdm_config, session_message_cb, - session_closed_cb, session_keys_change_cb, session_expiration_update_cb, - base::BindRepeating(&AndroidCdmFactory::OnCdmCreated, - weak_factory_.GetWeakPtr(), creation_id_)); + creation_id_, + PendingCreation(std::move(factory), std::move(bound_cdm_created_cb))); + + raw_factory->Create(key_system, security_origin, cdm_config, + session_message_cb, session_closed_cb, + session_keys_change_cb, session_expiration_update_cb, + base::BindOnce(&AndroidCdmFactory::OnCdmCreated, + weak_factory_.GetWeakPtr(), creation_id_)); } void AndroidCdmFactory::OnCdmCreated( @@ -104,11 +106,12 @@ void AndroidCdmFactory::OnCdmCreated( DVLOG(1) << __func__ << ": creation_id = " << creation_id; DCHECK(pending_creations_.count(creation_id)); - auto cdm_created_cb = pending_creations_[creation_id].second; + CdmCreatedCB cdm_created_cb = + std::move(pending_creations_[creation_id].second); pending_creations_.erase(creation_id); LOG_IF(ERROR, !cdm) << error_message; - cdm_created_cb.Run(cdm, error_message); + std::move(cdm_created_cb).Run(cdm, error_message); } } // namespace media diff --git a/chromium/media/base/android/android_cdm_factory.h b/chromium/media/base/android/android_cdm_factory.h index 1ca1c77943f..b32b7935b77 100644 --- a/chromium/media/base/android/android_cdm_factory.h +++ b/chromium/media/base/android/android_cdm_factory.h @@ -23,8 +23,8 @@ struct CdmConfig; class MEDIA_EXPORT AndroidCdmFactory : public CdmFactory { public: - AndroidCdmFactory(const CreateFetcherCB& create_fetcher_cb, - const CreateStorageCB& create_storage_cb); + AndroidCdmFactory(CreateFetcherCB create_fetcher_cb, + CreateStorageCB create_storage_cb); ~AndroidCdmFactory() final; // CdmFactory implementation. @@ -35,7 +35,7 @@ class MEDIA_EXPORT AndroidCdmFactory : public CdmFactory { const SessionClosedCB& session_closed_cb, const SessionKeysChangeCB& session_keys_change_cb, const SessionExpirationUpdateCB& session_expiration_update_cb, - const CdmCreatedCB& cdm_created_cb) final; + CdmCreatedCB cdm_created_cb) final; private: // Callback for MediaDrmBridgeFactory::Create(). diff --git a/chromium/media/base/android/media_codec_bridge_impl.cc b/chromium/media/base/android/media_codec_bridge_impl.cc index 48350424847..8bd73acadf1 100644 --- a/chromium/media/base/android/media_codec_bridge_impl.cc +++ b/chromium/media/base/android/media_codec_bridge_impl.cc @@ -22,7 +22,6 @@ #include "media/base/android/media_jni_headers/MediaCodecBridgeBuilder_jni.h" #include "media/base/android/media_jni_headers/MediaCodecBridge_jni.h" #include "media/base/audio_codecs.h" -#include "media/base/bit_reader.h" #include "media/base/subsample_entry.h" #include "media/base/video_codecs.h" @@ -55,20 +54,27 @@ enum { using CodecSpecificData = std::vector<uint8_t>; // Parses |extra_data| to get info to be added to a Java MediaFormat. -bool GetCodecSpecificDataForAudio(AudioCodec codec, - const uint8_t* extra_data, - size_t extra_data_size, - int64_t codec_delay_ns, - int64_t seek_preroll_ns, +bool GetCodecSpecificDataForAudio(const AudioDecoderConfig& config, CodecSpecificData* output_csd0, CodecSpecificData* output_csd1, CodecSpecificData* output_csd2, bool* output_frame_has_adts_header) { + // It's important that the multiplication is first in this calculation to + // reduce the precision loss due to integer truncation. + const int64_t codec_delay_ns = base::Time::kNanosecondsPerSecond * + config.codec_delay() / + config.samples_per_second(); + const int64_t seek_preroll_ns = config.seek_preroll().InMicroseconds() * + base::Time::kNanosecondsPerMicrosecond; + + const uint8_t* extra_data = config.extra_data().data(); + const size_t extra_data_size = config.extra_data().size(); + *output_frame_has_adts_header = false; - if (extra_data_size == 0 && codec != kCodecOpus) + if (extra_data_size == 0 && config.codec() != kCodecOpus) return true; - switch (codec) { + switch (config.codec()) { case kCodecVorbis: { if (extra_data[0] != 2) { LOG(ERROR) << "Invalid number of vorbis headers before the codec " @@ -111,39 +117,9 @@ bool GetCodecSpecificDataForAudio(AudioCodec codec, break; } case kCodecAAC: { - media::BitReader reader(extra_data, extra_data_size); - - // The following code is copied from aac.cc - // TODO(qinmin): refactor the code in aac.cc to make it more reusable. - uint8_t profile = 0; - uint8_t frequency_index = 0; - uint8_t channel_config = 0; - RETURN_ON_ERROR(reader.ReadBits(5, &profile)); - RETURN_ON_ERROR(reader.ReadBits(4, &frequency_index)); - - if (0xf == frequency_index) - RETURN_ON_ERROR(reader.SkipBits(24)); - RETURN_ON_ERROR(reader.ReadBits(4, &channel_config)); - - if (profile == 5 || profile == 29) { - // Read extension config. - uint8_t ext_frequency_index = 0; - RETURN_ON_ERROR(reader.ReadBits(4, &ext_frequency_index)); - if (ext_frequency_index == 0xf) - RETURN_ON_ERROR(reader.SkipBits(24)); - RETURN_ON_ERROR(reader.ReadBits(5, &profile)); - } - - if (profile < 1 || profile > 4 || frequency_index == 0xf || - channel_config > 7) { - LOG(ERROR) << "Invalid AAC header"; - return false; - } - - output_csd0->push_back(profile << 3 | frequency_index >> 1); - output_csd0->push_back((frequency_index & 0x01) << 7 | channel_config - << 3); - *output_frame_has_adts_header = true; + output_csd0->assign(extra_data, extra_data + extra_data_size); + *output_frame_has_adts_header = + config.profile() != AudioCodecProfile::kXHE_AAC; break; } case kCodecOpus: { @@ -170,8 +146,8 @@ bool GetCodecSpecificDataForAudio(AudioCodec codec, break; } default: - LOG(ERROR) << "Invalid header encountered for codec: " - << GetCodecName(codec); + LOG(ERROR) << "Unsupported audio codec encountered: " + << GetCodecName(config.codec()); return false; } return true; @@ -204,19 +180,9 @@ std::unique_ptr<MediaCodecBridge> MediaCodecBridgeImpl::CreateAudioDecoder( const int channel_count = ChannelLayoutToChannelCount(config.channel_layout()); - // It's important that the multiplication is first in this calculation to - // reduce the precision loss due to integer truncation. - const int64_t codec_delay_ns = base::Time::kNanosecondsPerSecond * - config.codec_delay() / - config.samples_per_second(); - const int64_t seek_preroll_ns = config.seek_preroll().InMicroseconds() * - base::Time::kNanosecondsPerMicrosecond; - CodecSpecificData csd0, csd1, csd2; bool output_frame_has_adts_header; - if (!GetCodecSpecificDataForAudio(config.codec(), config.extra_data().data(), - config.extra_data().size(), codec_delay_ns, - seek_preroll_ns, &csd0, &csd1, &csd2, + if (!GetCodecSpecificDataForAudio(config, &csd0, &csd1, &csd2, &output_frame_has_adts_header)) { return nullptr; } diff --git a/chromium/media/base/android/media_codec_loop.h b/chromium/media/base/android/media_codec_loop.h index 3eb0d81da0c..81651149a3c 100644 --- a/chromium/media/base/android/media_codec_loop.h +++ b/chromium/media/base/android/media_codec_loop.h @@ -107,10 +107,6 @@ namespace media { class MEDIA_EXPORT MediaCodecLoop { public: - // TODO(liberato): this exists in video_decoder.h and audio_decoder.h too. - using InitCB = base::Callback<void(bool success)>; - using DecodeCB = base::Callback<void(DecodeStatus status)>; - // Data that the client wants to put into an input buffer. struct InputData { InputData(); diff --git a/chromium/media/base/android/media_crypto_context_impl.cc b/chromium/media/base/android/media_crypto_context_impl.cc index 434c94250ec..4304fc2bb90 100644 --- a/chromium/media/base/android/media_crypto_context_impl.cc +++ b/chromium/media/base/android/media_crypto_context_impl.cc @@ -15,9 +15,11 @@ MediaCryptoContextImpl::MediaCryptoContextImpl(MediaDrmBridge* media_drm_bridge) MediaCryptoContextImpl::~MediaCryptoContextImpl() {} -int MediaCryptoContextImpl::RegisterPlayer(const base::Closure& new_key_cb, - const base::Closure& cdm_unset_cb) { - return media_drm_bridge_->RegisterPlayer(new_key_cb, cdm_unset_cb); +int MediaCryptoContextImpl::RegisterPlayer( + base::RepeatingClosure new_key_cb, + base::RepeatingClosure cdm_unset_cb) { + return media_drm_bridge_->RegisterPlayer(std::move(new_key_cb), + std::move(cdm_unset_cb)); } void MediaCryptoContextImpl::UnregisterPlayer(int registration_id) { diff --git a/chromium/media/base/android/media_crypto_context_impl.h b/chromium/media/base/android/media_crypto_context_impl.h index 24a4ca1423a..3bc2d5f2e7e 100644 --- a/chromium/media/base/android/media_crypto_context_impl.h +++ b/chromium/media/base/android/media_crypto_context_impl.h @@ -36,8 +36,8 @@ class MEDIA_EXPORT MediaCryptoContextImpl : public MediaCryptoContext { // // Note: RegisterPlayer() must be called before SetMediaCryptoReadyCB() to // avoid missing any new key notifications. - int RegisterPlayer(const base::Closure& new_key_cb, - const base::Closure& cdm_unset_cb) final; + int RegisterPlayer(base::RepeatingClosure new_key_cb, + base::RepeatingClosure cdm_unset_cb) final; void UnregisterPlayer(int registration_id) final; // MediaCryptoContext implementation. diff --git a/chromium/media/base/android/media_drm_bridge.cc b/chromium/media/base/android/media_drm_bridge.cc index d90fc4f7f2e..6bdfc3a974d 100644 --- a/chromium/media/base/android/media_drm_bridge.cc +++ b/chromium/media/base/android/media_drm_bridge.cc @@ -30,9 +30,9 @@ #include "media/base/android/media_codec_util.h" #include "media/base/android/media_drm_bridge_client.h" #include "media/base/android/media_drm_bridge_delegate.h" -#include "media/base/android/media_drm_key_type.h" #include "media/base/android/media_jni_headers/MediaDrmBridge_jni.h" #include "media/base/cdm_key_information.h" +#include "media/base/media_drm_key_type.h" #include "media/base/media_switches.h" #include "media/base/provision_fetcher.h" #include "third_party/widevine/cdm/widevine_cdm_common.h" @@ -368,7 +368,7 @@ scoped_refptr<MediaDrmBridge> MediaDrmBridge::CreateInternal( SecurityLevel security_level, bool requires_media_crypto, std::unique_ptr<MediaDrmStorageBridge> storage, - const CreateFetcherCB& create_fetcher_cb, + CreateFetcherCB create_fetcher_cb, const SessionMessageCB& session_message_cb, const SessionClosedCB& session_closed_cb, const SessionKeysChangeCB& session_keys_change_cb, @@ -382,7 +382,7 @@ scoped_refptr<MediaDrmBridge> MediaDrmBridge::CreateInternal( scoped_refptr<MediaDrmBridge> media_drm_bridge(new MediaDrmBridge( scheme_uuid, origin_id, security_level, requires_media_crypto, - std::move(storage), create_fetcher_cb, session_message_cb, + std::move(storage), std::move(create_fetcher_cb), session_message_cb, session_closed_cb, session_keys_change_cb, session_expiration_update_cb)); if (!media_drm_bridge->j_media_drm_) @@ -396,7 +396,7 @@ scoped_refptr<MediaDrmBridge> MediaDrmBridge::CreateWithoutSessionSupport( const std::string& key_system, const std::string& origin_id, SecurityLevel security_level, - const CreateFetcherCB& create_fetcher_cb) { + CreateFetcherCB create_fetcher_cb) { DVLOG(1) << __func__; // Sessions won't be used so decoding capability is not required. @@ -412,7 +412,7 @@ scoped_refptr<MediaDrmBridge> MediaDrmBridge::CreateWithoutSessionSupport( return CreateInternal( scheme_uuid, origin_id, security_level, requires_media_crypto, - std::make_unique<MediaDrmStorageBridge>(), create_fetcher_cb, + std::make_unique<MediaDrmStorageBridge>(), std::move(create_fetcher_cb), SessionMessageCB(), SessionClosedCB(), SessionKeysChangeCB(), SessionExpirationUpdateCB()); } @@ -579,10 +579,11 @@ MediaCryptoContext* MediaDrmBridge::GetMediaCryptoContext() { return &media_crypto_context_; } -int MediaDrmBridge::RegisterPlayer(const base::Closure& new_key_cb, - const base::Closure& cdm_unset_cb) { +int MediaDrmBridge::RegisterPlayer(base::RepeatingClosure new_key_cb, + base::RepeatingClosure cdm_unset_cb) { // |player_tracker_| can be accessed from any thread. - return player_tracker_.RegisterPlayer(new_key_cb, cdm_unset_cb); + return player_tracker_.RegisterPlayer(std::move(new_key_cb), + std::move(cdm_unset_cb)); } void MediaDrmBridge::UnregisterPlayer(int registration_id) { @@ -680,10 +681,9 @@ void MediaDrmBridge::OnMediaCryptoReady( DVLOG(1) << __func__; task_runner_->PostTask( - FROM_HERE, - base::BindOnce(&MediaDrmBridge::NotifyMediaCryptoReady, - weak_factory_.GetWeakPtr(), - base::Passed(CreateJavaObjectPtr(j_media_crypto.obj())))); + FROM_HERE, base::BindOnce(&MediaDrmBridge::NotifyMediaCryptoReady, + weak_factory_.GetWeakPtr(), + CreateJavaObjectPtr(j_media_crypto.obj()))); } void MediaDrmBridge::OnProvisionRequest( @@ -815,7 +815,7 @@ void MediaDrmBridge::OnSessionKeysChange( task_runner_->PostTask( FROM_HERE, base::BindOnce(session_keys_change_cb_, std::move(session_id), - has_additional_usable_key, base::Passed(&cdm_keys_info))); + has_additional_usable_key, std::move(cdm_keys_info))); if (has_additional_usable_key) { task_runner_->PostTask( @@ -963,8 +963,8 @@ void MediaDrmBridge::SendProvisioningRequest(const std::string& default_url, provision_fetcher_->Retrieve( default_url, request_data, - base::Bind(&MediaDrmBridge::ProcessProvisionResponse, - weak_factory_.GetWeakPtr())); + base::BindOnce(&MediaDrmBridge::ProcessProvisionResponse, + weak_factory_.GetWeakPtr())); } void MediaDrmBridge::ProcessProvisionResponse(bool success, diff --git a/chromium/media/base/android/media_drm_bridge.h b/chromium/media/base/android/media_drm_bridge.h index dc0829626ca..3c800d2c5ee 100644 --- a/chromium/media/base/android/media_drm_bridge.h +++ b/chromium/media/base/android/media_drm_bridge.h @@ -21,12 +21,12 @@ #include "media/base/android/android_util.h" #include "media/base/android/media_crypto_context.h" #include "media/base/android/media_crypto_context_impl.h" -#include "media/base/android/media_drm_storage.h" #include "media/base/android/media_drm_storage_bridge.h" #include "media/base/cdm_context.h" #include "media/base/cdm_promise.h" #include "media/base/cdm_promise_adapter.h" #include "media/base/content_decryption_module.h" +#include "media/base/media_drm_storage.h" #include "media/base/media_export.h" #include "media/base/player_tracker.h" #include "media/base/provision_fetcher.h" @@ -100,7 +100,7 @@ class MEDIA_EXPORT MediaDrmBridge : public ContentDecryptionModule, const std::string& key_system, const std::string& origin_id, SecurityLevel security_level, - const CreateFetcherCB& create_fetcher_cb); + CreateFetcherCB create_fetcher_cb); // ContentDecryptionModule implementation. void SetServerCertificate( @@ -147,8 +147,8 @@ class MEDIA_EXPORT MediaDrmBridge : public ContentDecryptionModule, // // Note: RegisterPlayer() should be called before SetMediaCryptoReadyCB() to // avoid missing any new key notifications. - int RegisterPlayer(const base::Closure& new_key_cb, - const base::Closure& cdm_unset_cb) override; + int RegisterPlayer(base::RepeatingClosure new_key_cb, + base::RepeatingClosure cdm_unset_cb) override; void UnregisterPlayer(int registration_id) override; // Helper function to determine whether a secure decoder is required for the @@ -256,7 +256,7 @@ class MEDIA_EXPORT MediaDrmBridge : public ContentDecryptionModule, SecurityLevel security_level, bool requires_media_crypto, std::unique_ptr<MediaDrmStorageBridge> storage, - const CreateFetcherCB& create_fetcher_cb, + CreateFetcherCB create_fetcher_cb, const SessionMessageCB& session_message_cb, const SessionClosedCB& session_closed_cb, const SessionKeysChangeCB& session_keys_change_cb, diff --git a/chromium/media/base/android/media_drm_bridge_factory.cc b/chromium/media/base/android/media_drm_bridge_factory.cc index dc32b11d8ab..52b3e01c271 100644 --- a/chromium/media/base/android/media_drm_bridge_factory.cc +++ b/chromium/media/base/android/media_drm_bridge_factory.cc @@ -13,11 +13,10 @@ namespace media { -MediaDrmBridgeFactory::MediaDrmBridgeFactory( - const CreateFetcherCB& create_fetcher_cb, - const CreateStorageCB& create_storage_cb) - : create_fetcher_cb_(create_fetcher_cb), - create_storage_cb_(create_storage_cb) { +MediaDrmBridgeFactory::MediaDrmBridgeFactory(CreateFetcherCB create_fetcher_cb, + CreateStorageCB create_storage_cb) + : create_fetcher_cb_(std::move(create_fetcher_cb)), + create_storage_cb_(std::move(create_storage_cb)) { DCHECK(create_fetcher_cb_); DCHECK(create_storage_cb_); } @@ -35,7 +34,7 @@ void MediaDrmBridgeFactory::Create( const SessionClosedCB& session_closed_cb, const SessionKeysChangeCB& session_keys_change_cb, const SessionExpirationUpdateCB& session_expiration_update_cb, - const CdmCreatedCB& cdm_created_cb) { + CdmCreatedCB cdm_created_cb) { DCHECK(MediaDrmBridge::IsKeySystemSupported(key_system)); DCHECK(MediaDrmBridge::IsAvailable()); DCHECK(!security_origin.opaque()); @@ -56,7 +55,7 @@ void MediaDrmBridgeFactory::Create( key_system + " may require use_video_overlay_for_embedded_encrypted_video"; NOTREACHED() << error_message; - cdm_created_cb.Run(nullptr, error_message); + std::move(cdm_created_cb).Run(nullptr, error_message); return; } @@ -64,7 +63,7 @@ void MediaDrmBridgeFactory::Create( session_closed_cb_ = session_closed_cb; session_keys_change_cb_ = session_keys_change_cb; session_expiration_update_cb_ = session_expiration_update_cb; - cdm_created_cb_ = cdm_created_cb; + cdm_created_cb_ = std::move(cdm_created_cb); // MediaDrmStorage may be lazy created in MediaDrmStorageBridge. storage_ = std::make_unique<MediaDrmStorageBridge>(); diff --git a/chromium/media/base/android/media_drm_bridge_factory.h b/chromium/media/base/android/media_drm_bridge_factory.h index 088cbe67ad2..4dfa133dcf2 100644 --- a/chromium/media/base/android/media_drm_bridge_factory.h +++ b/chromium/media/base/android/media_drm_bridge_factory.h @@ -26,8 +26,8 @@ struct CdmConfig; // at any time. class MEDIA_EXPORT MediaDrmBridgeFactory : public CdmFactory { public: - MediaDrmBridgeFactory(const CreateFetcherCB& create_fetcher_cb, - const CreateStorageCB& create_storage_cb); + MediaDrmBridgeFactory(CreateFetcherCB create_fetcher_cb, + CreateStorageCB create_storage_cb); ~MediaDrmBridgeFactory() final; // CdmFactory implementation. @@ -38,7 +38,7 @@ class MEDIA_EXPORT MediaDrmBridgeFactory : public CdmFactory { const SessionClosedCB& session_closed_cb, const SessionKeysChangeCB& session_keys_change_cb, const SessionExpirationUpdateCB& session_expiration_update_cb, - const CdmCreatedCB& cdm_created_cb) final; + CdmCreatedCB cdm_created_cb) final; private: // Callback for Initialize() on |storage_|. diff --git a/chromium/media/base/android/media_drm_storage_bridge.cc b/chromium/media/base/android/media_drm_storage_bridge.cc index 23c0bfffb7c..606951bc800 100644 --- a/chromium/media/base/android/media_drm_storage_bridge.cc +++ b/chromium/media/base/android/media_drm_storage_bridge.cc @@ -18,8 +18,8 @@ #include "base/unguessable_token.h" #include "media/base/android/android_util.h" #include "media/base/android/media_drm_bridge.h" -#include "media/base/android/media_drm_key_type.h" #include "media/base/android/media_jni_headers/MediaDrmStorageBridge_jni.h" +#include "media/base/media_drm_key_type.h" using base::android::AttachCurrentThread; using base::android::ConvertUTF8ToJavaString; @@ -62,7 +62,7 @@ void MediaDrmStorageBridge::OnProvisioned( // Bind callback to WeakPtr in case callback is called // after object is deleted. weak_factory_.GetWeakPtr(), - base::Passed(CreateJavaObjectPtr(j_callback.obj()))))); + CreateJavaObjectPtr(j_callback.obj())))); } void MediaDrmStorageBridge::OnLoadInfo( @@ -81,8 +81,7 @@ void MediaDrmStorageBridge::OnLoadInfo( session_id, base::BindOnce(&MediaDrmStorageBridge::OnSessionDataLoaded, weak_factory_.GetWeakPtr(), - base::Passed(CreateJavaObjectPtr(j_callback.obj())), - session_id))); + CreateJavaObjectPtr(j_callback.obj()), session_id))); } void MediaDrmStorageBridge::OnSaveInfo( @@ -120,7 +119,7 @@ void MediaDrmStorageBridge::OnSaveInfo( key_type), base::BindOnce(&MediaDrmStorageBridge::RunAndroidBoolCallback, weak_factory_.GetWeakPtr(), - base::Passed(CreateJavaObjectPtr(j_callback.obj()))))); + CreateJavaObjectPtr(j_callback.obj())))); } void MediaDrmStorageBridge::OnClearInfo( @@ -139,7 +138,7 @@ void MediaDrmStorageBridge::OnClearInfo( std::move(session_id), base::BindOnce(&MediaDrmStorageBridge::RunAndroidBoolCallback, weak_factory_.GetWeakPtr(), - base::Passed(CreateJavaObjectPtr(j_callback.obj()))))); + CreateJavaObjectPtr(j_callback.obj())))); } void MediaDrmStorageBridge::RunAndroidBoolCallback(JavaObjectPtr j_callback, diff --git a/chromium/media/base/android/media_drm_storage_bridge.h b/chromium/media/base/android/media_drm_storage_bridge.h index 8e6dd2bcd61..ee4edf9fc43 100644 --- a/chromium/media/base/android/media_drm_storage_bridge.h +++ b/chromium/media/base/android/media_drm_storage_bridge.h @@ -14,7 +14,7 @@ #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" #include "media/base/android/android_util.h" -#include "media/base/android/media_drm_storage.h" +#include "media/base/media_drm_storage.h" #include "url/origin.h" namespace base { diff --git a/chromium/media/base/android/media_server_crash_listener.cc b/chromium/media/base/android/media_server_crash_listener.cc index 51238982ca9..958df440d1f 100644 --- a/chromium/media/base/android/media_server_crash_listener.cc +++ b/chromium/media/base/android/media_server_crash_listener.cc @@ -11,9 +11,9 @@ namespace media { MediaServerCrashListener::MediaServerCrashListener( - const OnMediaServerCrashCB& on_server_crash_cb, + OnMediaServerCrashCB on_server_crash_cb, scoped_refptr<base::SingleThreadTaskRunner> callback_thread) - : on_server_crash_cb_(on_server_crash_cb), + : on_server_crash_cb_(std::move(on_server_crash_cb)), callback_task_runner_(std::move(callback_thread)) { JNIEnv* env = base::android::AttachCurrentThread(); CHECK(env); diff --git a/chromium/media/base/android/media_server_crash_listener.h b/chromium/media/base/android/media_server_crash_listener.h index 74e7f87076c..7a19e20205e 100644 --- a/chromium/media/base/android/media_server_crash_listener.h +++ b/chromium/media/base/android/media_server_crash_listener.h @@ -22,12 +22,12 @@ namespace media { // be more than a single instance of this class per process. class MediaServerCrashListener { public: - using OnMediaServerCrashCB = base::Callback<void(bool)>; + using OnMediaServerCrashCB = base::RepeatingCallback<void(bool)>; // Basic constructor. |on_server_crash_cb| will be posted to - // |callback_task_runner| everytime the watchdog MediaPlayer detects a crash. + // |callback_task_runner| every time the watchdog MediaPlayer detects a crash. MediaServerCrashListener( - const OnMediaServerCrashCB& on_server_crash_cb, + OnMediaServerCrashCB on_server_crash_cb, scoped_refptr<base::SingleThreadTaskRunner> callback_task_runner); ~MediaServerCrashListener(); diff --git a/chromium/media/base/android/media_service_throttler.cc b/chromium/media/base/android/media_service_throttler.cc index 2d69884be87..eb42e0c58a1 100644 --- a/chromium/media/base/android/media_service_throttler.cc +++ b/chromium/media/base/android/media_service_throttler.cc @@ -7,6 +7,7 @@ #include <memory> #include "base/bind.h" +#include "base/bind_helpers.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/default_tick_clock.h" #include "media/base/android/media_server_crash_listener.h" @@ -89,7 +90,7 @@ MediaServiceThrottler::MediaServiceThrottler() crash_listener_task_runner_(base::ThreadTaskRunnerHandle::Get()) { // base::Unretained is safe because the MediaServiceThrottler is supposed to // live until the process dies. - release_crash_listener_cb_ = base::Bind( + release_crash_listener_cb_ = base::BindRepeating( &MediaServiceThrottler::ReleaseCrashListener, base::Unretained(this)); EnsureCrashListenerStarted(); } @@ -197,8 +198,8 @@ void MediaServiceThrottler::EnsureCrashListenerStarted() { // base::Unretained is safe here because the MediaServiceThrottler will live // until the process is terminated. crash_listener_ = std::make_unique<MediaServerCrashListener>( - base::Bind(&MediaServiceThrottler::OnMediaServerCrash, - base::Unretained(this)), + base::BindRepeating(&MediaServiceThrottler::OnMediaServerCrash, + base::Unretained(this)), crash_listener_task_runner_); } else { crash_listener_->EnsureListening(); @@ -225,8 +226,7 @@ void MediaServiceThrottler::SetCrashListenerTaskRunnerForTesting( // Re-create the crash listener. crash_listener_ = std::make_unique<MediaServerCrashListener>( - MediaServerCrashListener::OnMediaServerCrashCB(), - crash_listener_task_runner_); + base::NullCallback(), crash_listener_task_runner_); } } // namespace media diff --git a/chromium/media/base/android/media_service_throttler.h b/chromium/media/base/android/media_service_throttler.h index 3c7c34b7c53..8c2c292bf15 100644 --- a/chromium/media/base/android/media_service_throttler.h +++ b/chromium/media/base/android/media_service_throttler.h @@ -100,8 +100,8 @@ class MEDIA_EXPORT MediaServiceThrottler { base::TimeTicks last_schedule_call_; // Callbacks used to release |crash_listener_| after 60s of inactivity. - base::Closure release_crash_listener_cb_; - base::CancelableClosure cancelable_release_crash_listener_cb_; + base::RepeatingClosure release_crash_listener_cb_; + base::CancelableRepeatingClosure cancelable_release_crash_listener_cb_; // Listens for MediaServer crashes using a watchdog MediaPlayer. std::unique_ptr<MediaServerCrashListener> crash_listener_; diff --git a/chromium/media/base/android/mock_media_crypto_context.cc b/chromium/media/base/android/mock_media_crypto_context.cc index 017677287a0..e20d085c3f4 100644 --- a/chromium/media/base/android/mock_media_crypto_context.cc +++ b/chromium/media/base/android/mock_media_crypto_context.cc @@ -25,7 +25,7 @@ MockMediaCryptoContext::MockMediaCryptoContext(bool has_media_crypto_context) // Provide some sane defaults. ON_CALL(*this, RegisterPlayer(_, _)) - .WillByDefault(DoAll(SaveArg<0>(&new_key_cb), SaveArg<1>(&cdm_unset_cb), + .WillByDefault(DoAll(MoveArg<0>(&new_key_cb), MoveArg<1>(&cdm_unset_cb), Return(kRegistrationId))); ON_CALL(*this, SetMediaCryptoReadyCB_(_)) .WillByDefault(MoveArg<0>(&media_crypto_ready_cb)); diff --git a/chromium/media/base/android/mock_media_crypto_context.h b/chromium/media/base/android/mock_media_crypto_context.h index 6630574cd39..745019fc18a 100644 --- a/chromium/media/base/android/mock_media_crypto_context.h +++ b/chromium/media/base/android/mock_media_crypto_context.h @@ -27,8 +27,8 @@ class MEDIA_EXPORT MockMediaCryptoContext // MediaCryptoContext implementation. MOCK_METHOD2(RegisterPlayer, - int(const base::Closure& new_key_cb, - const base::Closure& cdm_unset_cb)); + int(base::RepeatingClosure new_key_cb, + base::RepeatingClosure cdm_unset_cb)); MOCK_METHOD1(UnregisterPlayer, void(int registration_id)); void SetMediaCryptoReadyCB( MediaCryptoReadyCB media_crypto_ready_cb) override { @@ -39,8 +39,8 @@ class MEDIA_EXPORT MockMediaCryptoContext static constexpr int kRegistrationId = 1000; - base::Closure new_key_cb; - base::Closure cdm_unset_cb; + base::RepeatingClosure new_key_cb; + base::RepeatingClosure cdm_unset_cb; MediaCryptoReadyCB media_crypto_ready_cb; // To be set to true when |media_crypto_ready_cb| is consumed and run. bool ran_media_crypto_ready_cb = false; diff --git a/chromium/media/base/android/test_destruction_observable.cc b/chromium/media/base/android/test_destruction_observable.cc index cf323942865..eb8c226323a 100644 --- a/chromium/media/base/android/test_destruction_observable.cc +++ b/chromium/media/base/android/test_destruction_observable.cc @@ -23,8 +23,8 @@ DestructionObserver::DestructionObserver(DestructionObservable* observable) // Only one observer is allowed. DCHECK(!observable->destruction_cb.Release()); observable->destruction_cb.ReplaceClosure( - base::Bind(&DestructionObserver::OnObservableDestructed, - weak_factory_.GetWeakPtr())); + base::BindOnce(&DestructionObserver::OnObservableDestructed, + weak_factory_.GetWeakPtr())); } DestructionObserver::~DestructionObserver() { diff --git a/chromium/media/base/audio_buffer.cc b/chromium/media/base/audio_buffer.cc index 6de13f0cc5e..d0f34c51e5b 100644 --- a/chromium/media/base/audio_buffer.cc +++ b/chromium/media/base/audio_buffer.cc @@ -82,6 +82,8 @@ AudioBuffer::AudioBuffer(SampleFormat sample_format, if (!create_buffer) return; + CHECK_NE(sample_format, kUnknownSampleFormat); + int data_size_per_channel = frame_count * bytes_per_channel; if (IsPlanar(sample_format)) { DCHECK(!IsBitstreamFormat()) << sample_format_; diff --git a/chromium/media/base/audio_buffer.h b/chromium/media/base/audio_buffer.h index e96bfa8edf9..71f5c87e8b7 100644 --- a/chromium/media/base/audio_buffer.h +++ b/chromium/media/base/audio_buffer.h @@ -157,6 +157,10 @@ class MEDIA_EXPORT AudioBuffer // Return the sample rate. int sample_rate() const { return sample_rate_; } + // Return the sample format of the internal buffer, not that of what is + // returned by ReadFrames(). + int sample_format() const { return sample_format_; } + // Return the channel layout. ChannelLayout channel_layout() const { return channel_layout_; } diff --git a/chromium/media/base/audio_buffer_queue.cc b/chromium/media/base/audio_buffer_queue.cc index 3e699663898..d77946578d1 100644 --- a/chromium/media/base/audio_buffer_queue.cc +++ b/chromium/media/base/audio_buffer_queue.cc @@ -75,8 +75,11 @@ int AudioBufferQueue::InternalRead(int frames, int taken = buffer->frame_count(); // if |dest| is NULL, there's no need to copy. - if (dest) + if (dest) { + // We always copy a whole bitstream buffer. Make sure we have space. + CHECK_GE(frames, buffer->frame_count()); buffer->ReadFrames(buffer->frame_count(), 0, dest_frame_offset, dest); + } if (advance_position) { // Update the appropriate values since |taken| frames have been copied diff --git a/chromium/media/base/audio_codecs.cc b/chromium/media/base/audio_codecs.cc index 5b39eb04738..ba5dbc66d12 100644 --- a/chromium/media/base/audio_codecs.cc +++ b/chromium/media/base/audio_codecs.cc @@ -47,8 +47,15 @@ std::string GetCodecName(AudioCodec codec) { case kCodecMpegHAudio: return "mpeg-h-audio"; } - NOTREACHED(); - return ""; +} + +std::string GetProfileName(AudioCodecProfile profile) { + switch (profile) { + case AudioCodecProfile::kUnknown: + return "unknown"; + case AudioCodecProfile::kXHE_AAC: + return "xhe-aac"; + } } AudioCodec StringToAudioCodec(const std::string& codec_id) { diff --git a/chromium/media/base/audio_codecs.h b/chromium/media/base/audio_codecs.h index 756a3bb3293..5eb5ddcb1a4 100644 --- a/chromium/media/base/audio_codecs.h +++ b/chromium/media/base/audio_codecs.h @@ -42,7 +42,18 @@ enum AudioCodec { kAudioCodecMax = kCodecMpegHAudio, }; +enum class AudioCodecProfile { + // These values are histogrammed over time; do not change their ordinal + // values. When deleting a profile replace it with a dummy value; when adding + // a profile, do so at the bottom before kMaxValue, and update the value of + // kMaxValue to equal the new codec. + kUnknown = 0, + kXHE_AAC = 1, + kMaxValue = kXHE_AAC, +}; + std::string MEDIA_EXPORT GetCodecName(AudioCodec codec); +std::string MEDIA_EXPORT GetProfileName(AudioCodecProfile profile); MEDIA_EXPORT AudioCodec StringToAudioCodec(const std::string& codec_id); diff --git a/chromium/media/base/audio_converter.cc b/chromium/media/base/audio_converter.cc index 35115c58976..e54ded8b8fc 100644 --- a/chromium/media/base/audio_converter.cc +++ b/chromium/media/base/audio_converter.cc @@ -118,6 +118,12 @@ void AudioConverter::PrimeWithSilence() { } } +int AudioConverter::GetMaxInputFramesRequested(int output_frames_requested) { + return resampler_ + ? resampler_->GetMaxInputFramesRequested(output_frames_requested) + : output_frames_requested; +} + void AudioConverter::ConvertWithDelay(uint32_t initial_frames_delayed, AudioBus* dest) { initial_frames_delayed_ = initial_frames_delayed; diff --git a/chromium/media/base/audio_converter.h b/chromium/media/base/audio_converter.h index ba6d2a7c18c..5e1b609fabf 100644 --- a/chromium/media/base/audio_converter.h +++ b/chromium/media/base/audio_converter.h @@ -96,6 +96,12 @@ class MEDIA_EXPORT AudioConverter { // See SincResampler::PrimeWithSilence. void PrimeWithSilence(); + // Maximum number of frames requested via InputCallback::ProvideInput, when + // trying to convert |output_frames_requested| at a time. + // Returns |output_frames_requested| when we are not resampling, and a + // multiple of the request size when we are. + int GetMaxInputFramesRequested(int output_frames_requested); + bool empty() const { return transform_inputs_.empty(); } private: diff --git a/chromium/media/base/audio_decoder.h b/chromium/media/base/audio_decoder.h index 39a893bd693..23bf7ae5037 100644 --- a/chromium/media/base/audio_decoder.h +++ b/chromium/media/base/audio_decoder.h @@ -16,6 +16,7 @@ #include "media/base/decoder_buffer.h" #include "media/base/media_export.h" #include "media/base/pipeline_status.h" +#include "media/base/status.h" #include "media/base/waiting.h" namespace media { @@ -26,7 +27,7 @@ class CdmContext; class MEDIA_EXPORT AudioDecoder { public: // Callback for VideoDecoder initialization. - using InitCB = base::OnceCallback<void(bool success)>; + using InitCB = base::OnceCallback<void(Status)>; // Callback for AudioDecoder to return a decoded frame whenever it becomes // available. Only non-EOS frames should be returned via this callback. diff --git a/chromium/media/base/audio_decoder_config.cc b/chromium/media/base/audio_decoder_config.cc index 4f13ee3eebf..c05a64e5fae 100644 --- a/chromium/media/base/audio_decoder_config.cc +++ b/chromium/media/base/audio_decoder_config.cc @@ -75,12 +75,14 @@ bool AudioDecoderConfig::Matches(const AudioDecoderConfig& config) const { (seek_preroll() == config.seek_preroll()) && (codec_delay() == config.codec_delay()) && (should_discard_decoder_delay() == - config.should_discard_decoder_delay())); + config.should_discard_decoder_delay()) && + (profile() == config.profile())); } std::string AudioDecoderConfig::AsHumanReadableString() const { std::ostringstream s; s << "codec: " << GetCodecName(codec()) + << ", profile: " << GetProfileName(profile()) << ", bytes_per_channel: " << bytes_per_channel() << ", channel_layout: " << ChannelLayoutToString(channel_layout()) << ", channels: " << channels() diff --git a/chromium/media/base/audio_decoder_config.h b/chromium/media/base/audio_decoder_config.h index a7f294d7973..8fc0cd8d58d 100644 --- a/chromium/media/base/audio_decoder_config.h +++ b/chromium/media/base/audio_decoder_config.h @@ -112,8 +112,14 @@ class MEDIA_EXPORT AudioDecoderConfig { return target_output_channel_layout_; } + // Optionally set if the AudioCodec has a profile which may preclude certain + // decoders from having support. + void set_profile(AudioCodecProfile profile) { profile_ = profile; } + AudioCodecProfile profile() const { return profile_; } + private: AudioCodec codec_ = kUnknownAudioCodec; + AudioCodecProfile profile_ = AudioCodecProfile::kUnknown; SampleFormat sample_format_ = kUnknownSampleFormat; int bytes_per_channel_ = 0; int samples_per_second_ = 0; diff --git a/chromium/media/base/audio_latency.cc b/chromium/media/base/audio_latency.cc index 9d2d1e6a6ec..887dea9cf0d 100644 --- a/chromium/media/base/audio_latency.cc +++ b/chromium/media/base/audio_latency.cc @@ -61,7 +61,9 @@ bool AudioLatency::IsResamplingPassthroughSupported(LatencyType type) { int AudioLatency::GetHighLatencyBufferSize(int sample_rate, int preferred_buffer_size) { // Empirically, we consider 20ms of samples to be high latency. +#if !defined(USE_CRAS) const double twenty_ms_size = 2.0 * sample_rate / 100; +#endif #if defined(OS_WIN) preferred_buffer_size = std::max(preferred_buffer_size, 1); @@ -84,7 +86,12 @@ int AudioLatency::GetHighLatencyBufferSize(int sample_rate, // // On Linux, the minimum hardware buffer size is 512, so the lower calculated // values are unused. OSX may have a value as low as 128. +#if defined(USE_CRAS) + const double eighty_ms_size = 8.0 * sample_rate / 100; + const int high_latency_buffer_size = RoundUpToPowerOfTwo(eighty_ms_size); +#else const int high_latency_buffer_size = RoundUpToPowerOfTwo(twenty_ms_size); +#endif // defined(USE_CRAS) #endif // defined(OS_WIN) return std::max(preferred_buffer_size, high_latency_buffer_size); diff --git a/chromium/media/base/audio_latency_unittest.cc b/chromium/media/base/audio_latency_unittest.cc index e3540690a72..b4c3d9496f1 100644 --- a/chromium/media/base/audio_latency_unittest.cc +++ b/chromium/media/base/audio_latency_unittest.cc @@ -137,7 +137,11 @@ TEST(AudioLatency, HighLatencyBufferSizes) { } #else for (int i = 6400; i <= 204800; i *= 2) +#if defined(USE_CRAS) + EXPECT_EQ(8 * (i / 100), AudioLatency::GetHighLatencyBufferSize(i, 32)); +#else EXPECT_EQ(2 * (i / 100), AudioLatency::GetHighLatencyBufferSize(i, 32)); +#endif // defined(USE_CRAS) #endif // defined(OS_WIN) } @@ -165,7 +169,7 @@ TEST_P(AudioLatencyTest, ExactBufferSizes) { } INSTANTIATE_TEST_SUITE_P( - /* no prefix */, + All, AudioLatencyTest, #if defined(OS_WIN) // Windows 10 with supported driver will have valid min and max buffer sizes diff --git a/chromium/media/base/audio_parameters.cc b/chromium/media/base/audio_parameters.cc index 801896f1e6d..8bf16c27f67 100644 --- a/chromium/media/base/audio_parameters.cc +++ b/chromium/media/base/audio_parameters.cc @@ -9,6 +9,22 @@ namespace media { +const char* FormatToString(AudioParameters::Format format) { + switch (format) { + case AudioParameters::AUDIO_PCM_LINEAR: + return "PCM_LINEAR"; + case AudioParameters::AUDIO_PCM_LOW_LATENCY: + return "PCM_LOW_LATENCY"; + case AudioParameters::AUDIO_BITSTREAM_AC3: + return "BITSTREAM_AC3"; + case AudioParameters::AUDIO_BITSTREAM_EAC3: + return "BITSTREAM_EAC3"; + case AudioParameters::AUDIO_FAKE: + return "FAKE"; + } + return "INVALID"; +} + base::CheckedNumeric<uint32_t> ComputeAudioInputBufferSizeChecked( const AudioParameters& parameters, uint32_t shared_memory_count) { @@ -117,8 +133,9 @@ bool AudioParameters::IsValid() const { std::string AudioParameters::AsHumanReadableString() const { std::ostringstream s; - s << "format: " << format() << ", channel_layout: " << channel_layout() - << ", channels: " << channels() << ", sample_rate: " << sample_rate() + s << "format: " << FormatToString(format()) + << ", channel_layout: " << channel_layout() << ", channels: " << channels() + << ", sample_rate: " << sample_rate() << ", frames_per_buffer: " << frames_per_buffer() << ", effects: " << effects() << ", mic_positions: " << PointsToString(mic_positions_); diff --git a/chromium/media/base/audio_power_monitor.cc b/chromium/media/base/audio_power_monitor.cc new file mode 100644 index 00000000000..70d9afd4172 --- /dev/null +++ b/chromium/media/base/audio_power_monitor.cc @@ -0,0 +1,93 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/base/audio_power_monitor.h" + +#include <algorithm> +#include <cmath> + +#include "base/logging.h" +#include "base/numerics/ranges.h" +#include "base/time/time.h" +#include "media/base/audio_bus.h" +#include "media/base/vector_math.h" + +namespace media { + +AudioPowerMonitor::AudioPowerMonitor(int sample_rate, + base::TimeDelta time_constant) + : sample_weight_(1.0f - + expf(-1.0f / (sample_rate * time_constant.InSecondsF()))) { + Reset(); +} + +AudioPowerMonitor::~AudioPowerMonitor() = default; + +void AudioPowerMonitor::Reset() { + // These are only read/written by Scan(), but Scan() should not be running + // when Reset() is called. + average_power_ = 0.0f; + has_clipped_ = false; + + // These are the copies read by ReadCurrentPowerAndClip(). The lock here is + // not necessary, as racey writes/reads are acceptable, but this prevents + // quality-enhancement tools like TSAN from complaining. + base::AutoLock for_reset(reading_lock_); + power_reading_ = 0.0f; + clipped_reading_ = false; +} + +void AudioPowerMonitor::Scan(const AudioBus& buffer, int num_frames) { + DCHECK_LE(num_frames, buffer.frames()); + const int num_channels = buffer.channels(); + if (num_frames <= 0 || num_channels <= 0) + return; + + // Calculate a new average power by applying a first-order low-pass filter + // (a.k.a. an exponentially-weighted moving average) over the audio samples in + // each channel in |buffer|. + float sum_power = 0.0f; + for (int i = 0; i < num_channels; ++i) { + const std::pair<float, float> ewma_and_max = vector_math::EWMAAndMaxPower( + average_power_, buffer.channel(i), num_frames, sample_weight_); + // If data in audio buffer is garbage, ignore its effect on the result. + if (!std::isfinite(ewma_and_max.first)) { + sum_power += average_power_; + } else { + sum_power += ewma_and_max.first; + has_clipped_ |= (ewma_and_max.second > 1.0f); + } + } + + // Update accumulated results, with clamping for sanity. + average_power_ = base::ClampToRange(sum_power / num_channels, 0.0f, 1.0f); + + // Push results for reading by other threads, non-blocking. + if (reading_lock_.Try()) { + power_reading_ = average_power_; + if (has_clipped_) { + clipped_reading_ = true; + has_clipped_ = false; + } + reading_lock_.Release(); + } +} + +std::pair<float, bool> AudioPowerMonitor::ReadCurrentPowerAndClip() { + base::AutoLock for_reading(reading_lock_); + + // Convert power level to dBFS units, and pin it down to zero if it is + // insignificantly small. + const float kInsignificantPower = 1.0e-10f; // -100 dBFS + const float power_dbfs = power_reading_ < kInsignificantPower + ? zero_power() + : 10.0f * log10f(power_reading_); + + const bool clipped = clipped_reading_; + clipped_reading_ = false; + + return std::make_pair(power_dbfs, clipped); +} + +} // namespace media diff --git a/chromium/media/base/audio_power_monitor.h b/chromium/media/base/audio_power_monitor.h new file mode 100644 index 00000000000..d95b2836064 --- /dev/null +++ b/chromium/media/base/audio_power_monitor.h @@ -0,0 +1,88 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEDIA_BASE_AUDIO_POWER_MONITOR_H_ +#define MEDIA_BASE_AUDIO_POWER_MONITOR_H_ + +#include <limits> +#include <utility> + +#include "base/callback.h" +#include "base/macros.h" +#include "base/synchronization/lock.h" +#include "media/base/media_export.h" + +// An audio signal power monitor. It is periodically provided an AudioBus by +// the native audio thread, and the audio samples in each channel are analyzed +// to determine the average power of the signal over a time period. Here +// "average power" is a running average calculated by using a first-order +// low-pass filter over the square of the samples scanned. Whenever reporting +// the power level, this running average is converted to dBFS (decibels relative +// to full-scale) units. +// +// Note that extreme care has been taken to make the AudioPowerMonitor::Scan() +// method safe to be called on the native audio thread. The code acquires no +// locks, nor engages in any operation that could result in an +// undetermined/unbounded amount of run-time. + +namespace base { +class TimeDelta; +} + +namespace media { + +class AudioBus; + +class MEDIA_EXPORT AudioPowerMonitor { + public: + // |sample_rate| is the audio signal sample rate (Hz). |time_constant| + // characterizes how samples are averaged over time to determine the power + // level; and is the amount of time it takes a zero power level to increase to + // ~63.2% of maximum given a step input signal. + AudioPowerMonitor(int sample_rate, base::TimeDelta time_constant); + + ~AudioPowerMonitor(); + + // Reset power monitor to initial state (zero power level). This should not + // be called while another thread is scanning. + void Reset(); + + // Scan more |frames| of audio data from |buffer|. It is safe to call this + // from a real-time priority thread. + void Scan(const AudioBus& buffer, int frames); + + // Returns the current power level in dBFS and clip status. Clip status is + // true whenever any *one* sample scanned exceeded maximum amplitude since + // this method's last invocation. It is safe to call this method from any + // thread. + std::pair<float, bool> ReadCurrentPowerAndClip(); + + // dBFS value corresponding to zero power in the audio signal. + static float zero_power() { return -std::numeric_limits<float>::infinity(); } + + // dBFS value corresponding to maximum power in the audio signal. + static float max_power() { return 0.0f; } + + private: + // The weight applied when averaging-in each sample. Computed from the + // |sample_rate| and |time_constant|. + const float sample_weight_; + + // Accumulated results over one or more calls to Scan(). These should only be + // touched by the thread invoking Scan(). + float average_power_; + bool has_clipped_; + + // Copies of power and clip status, used to deliver results synchronously + // across threads. + base::Lock reading_lock_; + float power_reading_; + bool clipped_reading_; + + DISALLOW_COPY_AND_ASSIGN(AudioPowerMonitor); +}; + +} // namespace media + +#endif // MEDIA_BASE_AUDIO_POWER_MONITOR_H_ diff --git a/chromium/media/base/audio_power_monitor_unittest.cc b/chromium/media/base/audio_power_monitor_unittest.cc new file mode 100644 index 00000000000..7ce16e04301 --- /dev/null +++ b/chromium/media/base/audio_power_monitor_unittest.cc @@ -0,0 +1,309 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/base/audio_power_monitor.h" + +#include <limits> +#include <memory> + +#include "base/macros.h" +#include "base/time/time.h" +#include "media/base/audio_bus.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace media { + +static const int kSampleRate = 48000; +static const int kFramesPerBuffer = 128; + +static const int kTimeConstantMillis = 5; + +namespace { + +// Container for each parameterized test's data (input and expected results). +class TestScenario { + public: + TestScenario(const float* data, + int num_channels, + int num_frames, + float expected_power, + bool expected_clipped) + : expected_power_(expected_power), expected_clipped_(expected_clipped) { + CreatePopulatedBuffer(data, num_channels, num_frames); + } + + // Copy constructor and assignment operator for ::testing::Values(...). + TestScenario(const TestScenario& other) { *this = other; } + TestScenario& operator=(const TestScenario& other) { + this->expected_power_ = other.expected_power_; + this->expected_clipped_ = other.expected_clipped_; + this->bus_ = AudioBus::Create(other.bus_->channels(), other.bus_->frames()); + other.bus_->CopyTo(this->bus_.get()); + return *this; + } + + // Returns this TestScenario, but with a bad sample value placed in the middle + // of channel 0. + TestScenario WithABadSample(float bad_value) const { + TestScenario result(*this); + result.bus_->channel(0)[result.bus_->frames() / 2] = bad_value; + return result; + } + + const AudioBus& data() const { return *bus_; } + + float expected_power() const { return expected_power_; } + + bool expected_clipped() const { return expected_clipped_; } + + private: + // Creates an AudioBus, sized and populated with kFramesPerBuffer frames of + // data. The given test |data| is repeated to fill the buffer. + void CreatePopulatedBuffer(const float* data, + int num_channels, + int num_frames) { + bus_ = AudioBus::Create(num_channels, kFramesPerBuffer); + for (int ch = 0; ch < num_channels; ++ch) { + for (int frames = 0; frames < kFramesPerBuffer; frames += num_frames) { + const int num_to_copy = std::min(num_frames, kFramesPerBuffer - frames); + memcpy(bus_->channel(ch) + frames, data + num_frames * ch, + sizeof(float) * num_to_copy); + } + } + } + + float expected_power_; + bool expected_clipped_; + std::unique_ptr<AudioBus> bus_; +}; + +// Value printer for TestScenario. Required to prevent Valgrind "access to +// uninitialized memory" errors (http://crbug.com/263315). +::std::ostream& operator<<(::std::ostream& os, const TestScenario& ts) { + return os << "{" << ts.data().channels() << "-channel signal} --> {" + << ts.expected_power() << " dBFS, " + << (ts.expected_clipped() ? "clipped" : "not clipped") << "}"; +} + +// An observer that receives power measurements. Each power measurement should +// should make progress towards the goal value. +class MeasurementObserver { + public: + explicit MeasurementObserver(float goal_power_measurement) + : goal_power_measurement_(goal_power_measurement), + measurement_count_(0), + last_power_measurement_(AudioPowerMonitor::zero_power()), + last_clipped_(false) {} + + int measurement_count() const { return measurement_count_; } + + float last_power_measurement() const { return last_power_measurement_; } + + bool last_clipped() const { return last_clipped_; } + + void OnPowerMeasured(float cur_power_measurement, bool clipped) { + if (measurement_count_ == 0) { + measurements_should_increase_ = + (cur_power_measurement < goal_power_measurement_); + } else { + SCOPED_TRACE(::testing::Message() + << "Power: goal=" << goal_power_measurement_ + << "; last=" << last_power_measurement_ + << "; cur=" << cur_power_measurement); + + if (last_power_measurement_ != goal_power_measurement_) { + if (measurements_should_increase_) { + EXPECT_LE(last_power_measurement_, cur_power_measurement) + << "Measurements should be monotonically increasing."; + } else { + EXPECT_GE(last_power_measurement_, cur_power_measurement) + << "Measurements should be monotonically decreasing."; + } + } else { + EXPECT_EQ(last_power_measurement_, cur_power_measurement) + << "Measurements are numerically unstable at goal value."; + } + } + + last_power_measurement_ = cur_power_measurement; + last_clipped_ = clipped; + ++measurement_count_; + } + + private: + const float goal_power_measurement_; + int measurement_count_; + bool measurements_should_increase_; + float last_power_measurement_; + bool last_clipped_; + + DISALLOW_COPY_AND_ASSIGN(MeasurementObserver); +}; + +} // namespace + +class AudioPowerMonitorTest : public ::testing::TestWithParam<TestScenario> { + public: + AudioPowerMonitorTest() + : power_monitor_(kSampleRate, + base::TimeDelta::FromMilliseconds(kTimeConstantMillis)) { + } + + void FeedAndCheckExpectedPowerIsMeasured(const AudioBus& bus, + float power, + bool clipped) { + // Feed the AudioPowerMonitor, read measurements from it, and record them in + // MeasurementObserver. + static const int kNumFeedIters = 100; + MeasurementObserver observer(power); + for (int i = 0; i < kNumFeedIters; ++i) { + power_monitor_.Scan(bus, bus.frames()); + const std::pair<float, bool>& reading = + power_monitor_.ReadCurrentPowerAndClip(); + observer.OnPowerMeasured(reading.first, reading.second); + } + + // Check that the results recorded by the observer are the same whole-number + // dBFS. + EXPECT_EQ(static_cast<int>(power), + static_cast<int>(observer.last_power_measurement())); + EXPECT_EQ(clipped, observer.last_clipped()); + } + + private: + AudioPowerMonitor power_monitor_; + + DISALLOW_COPY_AND_ASSIGN(AudioPowerMonitorTest); +}; + +TEST_P(AudioPowerMonitorTest, MeasuresPowerOfSignal) { + const TestScenario& scenario = GetParam(); + + std::unique_ptr<AudioBus> zeroed_bus = + AudioBus::Create(scenario.data().channels(), scenario.data().frames()); + zeroed_bus->Zero(); + + // Send a "zero power" audio signal, then this scenario's audio signal, then + // the "zero power" audio signal again; testing that the power monitor + // measurements match expected values. + FeedAndCheckExpectedPowerIsMeasured(*zeroed_bus, + AudioPowerMonitor::zero_power(), false); + FeedAndCheckExpectedPowerIsMeasured( + scenario.data(), scenario.expected_power(), scenario.expected_clipped()); + FeedAndCheckExpectedPowerIsMeasured(*zeroed_bus, + AudioPowerMonitor::zero_power(), false); +} + +static const float kMonoSilentNoise[] = {0.01f, -0.01f}; + +static const float kMonoMaxAmplitude[] = {1.0f}; + +static const float kMonoMaxAmplitude2[] = {-1.0f, 1.0f}; + +static const float kMonoHalfMaxAmplitude[] = {0.5f, -0.5f, 0.5f, -0.5f}; + +static const float kMonoAmplitudeClipped[] = {2.0f, -2.0f}; + +static const float kMonoMaxAmplitudeWithClip[] = {2.0f, 0.0, 0.0f, 0.0f}; + +static const float kMonoMaxAmplitudeWithClip2[] = {4.0f, 0.0, 0.0f, 0.0f}; + +static const float kStereoSilentNoise[] = { + // left channel + 0.005f, -0.005f, + // right channel + 0.005f, -0.005f}; + +static const float kStereoMaxAmplitude[] = { + // left channel + 1.0f, -1.0f, + // right channel + -1.0f, 1.0f}; + +static const float kRightChannelMaxAmplitude[] = { + // left channel + 0.0f, 0.0f, 0.0f, 0.0f, + // right channel + -1.0f, 1.0f, -1.0f, 1.0f}; + +static const float kLeftChannelHalfMaxAmplitude[] = { + // left channel + 0.5f, + -0.5f, + 0.5f, + -0.5f, + // right channel + 0.0f, + 0.0f, + 0.0f, + 0.0f, +}; + +static const float kStereoMixed[] = { + // left channel + 0.5f, -0.5f, 0.5f, -0.5f, + // right channel + -1.0f, 1.0f, -1.0f, 1.0f}; + +static const float kStereoMixed2[] = { + // left channel + 1.0f, -1.0f, 0.75f, -0.75f, 0.5f, -0.5f, 0.25f, -0.25f, + // right channel + 0.25f, -0.25f, 0.5f, -0.5f, 0.75f, -0.75f, 1.0f, -1.0f}; + +INSTANTIATE_TEST_SUITE_P( + Scenarios, + AudioPowerMonitorTest, + ::testing::Values( + TestScenario(kMonoSilentNoise, 1, 2, -40, false), + TestScenario(kMonoMaxAmplitude, + 1, + 1, + AudioPowerMonitor::max_power(), + false), + TestScenario(kMonoMaxAmplitude2, + 1, + 2, + AudioPowerMonitor::max_power(), + false), + TestScenario(kMonoHalfMaxAmplitude, 1, 4, -6, false), + TestScenario(kMonoAmplitudeClipped, + 1, + 2, + AudioPowerMonitor::max_power(), + true), + TestScenario(kMonoMaxAmplitudeWithClip, + 1, + 4, + AudioPowerMonitor::max_power(), + true), + TestScenario(kMonoMaxAmplitudeWithClip2, + 1, + 4, + AudioPowerMonitor::max_power(), + true), + TestScenario(kMonoSilentNoise, + 1, + 2, + AudioPowerMonitor::zero_power(), + false) + .WithABadSample(std::numeric_limits<float>::infinity()), + TestScenario(kMonoHalfMaxAmplitude, + 1, + 4, + AudioPowerMonitor::zero_power(), + false) + .WithABadSample(std::numeric_limits<float>::quiet_NaN()), + TestScenario(kStereoSilentNoise, 2, 2, -46, false), + TestScenario(kStereoMaxAmplitude, + 2, + 2, + AudioPowerMonitor::max_power(), + false), + TestScenario(kRightChannelMaxAmplitude, 2, 4, -3, false), + TestScenario(kLeftChannelHalfMaxAmplitude, 2, 4, -9, false), + TestScenario(kStereoMixed, 2, 4, -2, false), + TestScenario(kStereoMixed2, 2, 8, -3, false))); + +} // namespace media diff --git a/chromium/media/base/audio_pull_fifo.cc b/chromium/media/base/audio_pull_fifo.cc index 549adddcc8e..65a637d11e4 100644 --- a/chromium/media/base/audio_pull_fifo.cc +++ b/chromium/media/base/audio_pull_fifo.cc @@ -11,8 +11,8 @@ namespace media { -AudioPullFifo::AudioPullFifo(int channels, int frames, const ReadCB& read_cb) - : read_cb_(read_cb), +AudioPullFifo::AudioPullFifo(int channels, int frames, ReadCB read_cb) + : read_cb_(std::move(read_cb)), fifo_(AudioBus::Create(channels, frames)), fifo_index_(frames) {} diff --git a/chromium/media/base/audio_pull_fifo.h b/chromium/media/base/audio_pull_fifo.h index 3983e09407b..9d9ac6f5a9d 100644 --- a/chromium/media/base/audio_pull_fifo.h +++ b/chromium/media/base/audio_pull_fifo.h @@ -25,13 +25,14 @@ class MEDIA_EXPORT AudioPullFifo { // to be completely filled with data upon return; zero padded if not enough // frames are available to satisfy the request. |frame_delay| is the number // of output frames already processed and can be used to estimate delay. - typedef base::Callback<void(int frame_delay, AudioBus* audio_bus)> ReadCB; + using ReadCB = + base::RepeatingCallback<void(int frame_delay, AudioBus* audio_bus)>; // Constructs an AudioPullFifo with the specified |read_cb|, which is used to // read audio data to the FIFO if data is not already available. The internal // FIFO can contain |channel| number of channels, where each channel is of // length |frames| audio frames. - AudioPullFifo(int channels, int frames, const ReadCB& read_cb); + AudioPullFifo(int channels, int frames, ReadCB read_cb); virtual ~AudioPullFifo(); // Consumes |frames_to_consume| audio frames from the FIFO and copies diff --git a/chromium/media/base/audio_renderer.h b/chromium/media/base/audio_renderer.h index 988d917a09a..eac3c82ffea 100644 --- a/chromium/media/base/audio_renderer.h +++ b/chromium/media/base/audio_renderer.h @@ -7,6 +7,7 @@ #include "base/callback.h" #include "base/macros.h" +#include "base/optional.h" #include "base/time/time.h" #include "media/base/buffering_state.h" #include "media/base/media_export.h" @@ -40,7 +41,7 @@ class MEDIA_EXPORT AudioRenderer { virtual void Initialize(DemuxerStream* stream, CdmContext* cdm_context, RendererClient* client, - const PipelineStatusCB& init_cb) = 0; + PipelineStatusCallback init_cb) = 0; // Returns the TimeSource associated with audio rendering. virtual TimeSource* GetTimeSource() = 0; @@ -59,6 +60,11 @@ class MEDIA_EXPORT AudioRenderer { // Sets the output volume. virtual void SetVolume(float volume) = 0; + // Set a hint indicating target latency. See comment in renderer.h. + // |latency_hint| may be nullopt to indicate the hint has been cleared + // (restore UA default). + virtual void SetLatencyHint(base::Optional<base::TimeDelta> latency_hint) = 0; + private: DISALLOW_COPY_AND_ASSIGN(AudioRenderer); }; diff --git a/chromium/media/base/audio_renderer_mixer_unittest.cc b/chromium/media/base/audio_renderer_mixer_unittest.cc index 55a9d6f8766..14cdde6d460 100644 --- a/chromium/media/base/audio_renderer_mixer_unittest.cc +++ b/chromium/media/base/audio_renderer_mixer_unittest.cc @@ -526,7 +526,7 @@ TEST_P(AudioRendererMixerBehavioralTest, MixerPausesStream) { } INSTANTIATE_TEST_SUITE_P( - /* no prefix */, + All, AudioRendererMixerTest, testing::Values( // No resampling, 1 input sample rate. @@ -560,7 +560,7 @@ INSTANTIATE_TEST_SUITE_P( // support single item lists and we don't want these test cases to run for every // parameter set. INSTANTIATE_TEST_SUITE_P( - /* no prefix */, + All, AudioRendererMixerBehavioralTest, testing::ValuesIn(std::vector<AudioRendererMixerTestData>( 1, diff --git a/chromium/media/base/bit_reader_fuzzertest.cc b/chromium/media/base/bit_reader_fuzzertest.cc index cb6205b17d2..9b91f1317c3 100644 --- a/chromium/media/base/bit_reader_fuzzertest.cc +++ b/chromium/media/base/bit_reader_fuzzertest.cc @@ -5,19 +5,29 @@ #include <stddef.h> #include <stdint.h> -#include "base/hash/hash.h" +#include "base/containers/buffer_iterator.h" +#include "base/containers/span.h" #include "base/numerics/safe_conversions.h" #include "media/base/bit_reader.h" #include "media/base/test_random.h" // Entry point for LibFuzzer. extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - media::BitReader reader(data, base::checked_cast<int>(size)); + base::BufferIterator<const uint8_t> iterator(data, size); + + const uint32_t* random_seed = iterator.Object<uint32_t>(); + if (!random_seed) + return 0; // Need a simple random number generator to generate the number of bits to - // read/skip in a reproducible way (given the same |data|). Using Hash() to - // ensure the seed varies significantly over minor changes in |data|. - media::TestRandom rnd(base::Hash(data, size)); + // read/skip in a reproducible way (given the same |data|). + media::TestRandom rnd(*random_seed); + + base::span<const uint8_t> remaining = + iterator.Span<uint8_t>(iterator.total_size() - iterator.position()); + + media::BitReader reader(remaining.data(), + base::checked_cast<int>(remaining.size())); // Read and skip through the data in |reader|. while (reader.bits_available() > 0) { diff --git a/chromium/media/base/buffering_state.cc b/chromium/media/base/buffering_state.cc new file mode 100644 index 00000000000..4cf3af511fd --- /dev/null +++ b/chromium/media/base/buffering_state.cc @@ -0,0 +1,33 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/base/buffering_state.h" +#include <string> +#include "base/logging.h" + +namespace media { + +std::string BufferingStateToString(BufferingState state, + BufferingStateChangeReason reason) { + DCHECK(state == BUFFERING_HAVE_NOTHING || state == BUFFERING_HAVE_ENOUGH); + DCHECK(reason == BUFFERING_CHANGE_REASON_UNKNOWN || + reason == DEMUXER_UNDERFLOW || reason == DECODER_UNDERFLOW || + reason == REMOTING_NETWORK_CONGESTION); + + std::string state_string = state == BUFFERING_HAVE_NOTHING + ? "BUFFERING_HAVE_NOTHING" + : "BUFFERING_HAVE_ENOUGH"; + + std::vector<std::string> flag_strings; + if (reason == DEMUXER_UNDERFLOW) + state_string += " (DEMUXER_UNDERFLOW)"; + else if (reason == DECODER_UNDERFLOW) + state_string += " (DECODER_UNDERFLOW)"; + else if (reason == REMOTING_NETWORK_CONGESTION) + state_string += " (REMOTING_NETWORK_CONGESTION)"; + + return state_string; +} + +} // namespace media diff --git a/chromium/media/base/buffering_state.h b/chromium/media/base/buffering_state.h index 2de430f6cf1..3bae42ec3f8 100644 --- a/chromium/media/base/buffering_state.h +++ b/chromium/media/base/buffering_state.h @@ -5,6 +5,8 @@ #ifndef MEDIA_BASE_BUFFERING_STATE_H_ #define MEDIA_BASE_BUFFERING_STATE_H_ +#include <string> + #include "base/callback_forward.h" namespace media { @@ -48,9 +50,28 @@ enum BufferingStateChangeReason { BUFFERING_STATE_CHANGE_REASON_MAX = REMOTING_NETWORK_CONGESTION, }; +enum class SerializableBufferingStateType { + kPipeline, + kVideo, + kAudio, +}; + +// A serializable combo of the state, type, and reason. +template <SerializableBufferingStateType T> +struct SerializableBufferingState { + BufferingState state; + BufferingStateChangeReason reason; + // Only included in the serialized state if |type == kPipeline| + bool suspended_start = false; +}; + // Used to indicate changes in buffering state; -typedef base::Callback<void(BufferingState, BufferingStateChangeReason)> - BufferingStateCB; +using BufferingStateCB = + base::RepeatingCallback<void(BufferingState, BufferingStateChangeReason)>; + +std::string BufferingStateToString( + BufferingState state, + BufferingStateChangeReason reason = BUFFERING_CHANGE_REASON_UNKNOWN); } // namespace media diff --git a/chromium/media/base/cdm_factory.h b/chromium/media/base/cdm_factory.h index 36056a4e2d5..29d764d8ae0 100644 --- a/chromium/media/base/cdm_factory.h +++ b/chromium/media/base/cdm_factory.h @@ -20,8 +20,8 @@ namespace media { // Callback used when CDM is created. |error_message| only used if // ContentDecryptionModule is null (i.e. CDM can't be created). using CdmCreatedCB = - base::Callback<void(const scoped_refptr<ContentDecryptionModule>&, - const std::string& error_message)>; + base::OnceCallback<void(const scoped_refptr<ContentDecryptionModule>&, + const std::string& error_message)>; struct CdmConfig; @@ -40,7 +40,7 @@ class MEDIA_EXPORT CdmFactory { const SessionClosedCB& session_closed_cb, const SessionKeysChangeCB& session_keys_change_cb, const SessionExpirationUpdateCB& session_expiration_update_cb, - const CdmCreatedCB& cdm_created_cb) = 0; + CdmCreatedCB cdm_created_cb) = 0; private: DISALLOW_COPY_AND_ASSIGN(CdmFactory); diff --git a/chromium/media/base/channel_layout.h b/chromium/media/base/channel_layout.h index 008c081208b..0b13d509d99 100644 --- a/chromium/media/base/channel_layout.h +++ b/chromium/media/base/channel_layout.h @@ -143,7 +143,12 @@ constexpr int kMaxConcurrentChannels = 8; // from 0 to ChannelLayoutToChannelCount(layout) - 1. MEDIA_SHMEM_EXPORT int ChannelOrder(ChannelLayout layout, Channels channel); -// Returns the number of channels in a given ChannelLayout. +// Returns the number of channels in a given ChannelLayout or 0 if the +// channel layout can't be mapped to a valid value. Currently, 0 +// is returned for CHANNEL_LAYOUT_NONE, CHANNEL_LAYOUT_UNSUPPORTED, +// CHANNEL_LAYOUT_DISCRETE, and CHANNEL_LAYOUT_BITSTREAM. For these cases, +// additional steps must be taken to manually figure out the corresponding +// number of channels. MEDIA_SHMEM_EXPORT int ChannelLayoutToChannelCount(ChannelLayout layout); // Given the number of channels, return the best layout, diff --git a/chromium/media/base/content_decryption_module.h b/chromium/media/base/content_decryption_module.h index 112d6ed866f..b6185c8b7c0 100644 --- a/chromium/media/base/content_decryption_module.h +++ b/chromium/media/base/content_decryption_module.h @@ -187,32 +187,33 @@ struct MEDIA_EXPORT ContentDecryptionModuleTraits { // Called when the CDM needs to queue a message event to the session object. // See http://w3c.github.io/encrypted-media/#dom-evt-message -typedef base::Callback<void(const std::string& session_id, - CdmMessageType message_type, - const std::vector<uint8_t>& message)> - SessionMessageCB; +using SessionMessageCB = + base::RepeatingCallback<void(const std::string& session_id, + CdmMessageType message_type, + const std::vector<uint8_t>& message)>; // Called when the session specified by |session_id| is closed. Note that the // CDM may close a session at any point, such as in response to a CloseSession() // call, when the session is no longer needed, or when system resources are // lost. See http://w3c.github.io/encrypted-media/#session-close -typedef base::Callback<void(const std::string& session_id)> SessionClosedCB; +using SessionClosedCB = + base::RepeatingCallback<void(const std::string& session_id)>; // Called when there has been a change in the keys in the session or their // status. See http://w3c.github.io/encrypted-media/#dom-evt-keystatuseschange -typedef base::Callback<void(const std::string& session_id, - bool has_additional_usable_key, - CdmKeysInfo keys_info)> - SessionKeysChangeCB; +using SessionKeysChangeCB = + base::RepeatingCallback<void(const std::string& session_id, + bool has_additional_usable_key, + CdmKeysInfo keys_info)>; // Called when the CDM changes the expiration time of a session. // See http://w3c.github.io/encrypted-media/#update-expiration // A null base::Time() will be translated to NaN in Javascript, which means "no // such time exists or if the license explicitly never expires, as determined // by the CDM", according to the EME spec. -typedef base::Callback<void(const std::string& session_id, - base::Time new_expiry_time)> - SessionExpirationUpdateCB; +using SessionExpirationUpdateCB = + base::RepeatingCallback<void(const std::string& session_id, + base::Time new_expiry_time)>; } // namespace media diff --git a/chromium/media/base/data_source.h b/chromium/media/base/data_source.h index 27902b2c87c..82dd76485fa 100644 --- a/chromium/media/base/data_source.h +++ b/chromium/media/base/data_source.h @@ -16,8 +16,7 @@ namespace media { class MEDIA_EXPORT DataSource { public: - typedef base::Callback<void(int64_t, int64_t)> StatusCallback; - typedef base::Callback<void(int)> ReadCB; + using ReadCB = base::OnceCallback<void(int)>; enum { kReadError = -1, kAborted = -2 }; @@ -30,7 +29,7 @@ class MEDIA_EXPORT DataSource { virtual void Read(int64_t position, int size, uint8_t* data, - const DataSource::ReadCB& read_cb) = 0; + DataSource::ReadCB read_cb) = 0; // Stops the DataSource. Once this is called all future Read() calls will // return an error. This is a synchronous call and may be called from any diff --git a/chromium/media/base/decoder_buffer.cc b/chromium/media/base/decoder_buffer.cc index 9a2648e810a..5a07221ef8a 100644 --- a/chromium/media/base/decoder_buffer.cc +++ b/chromium/media/base/decoder_buffer.cc @@ -8,15 +8,6 @@ namespace media { -// Allocates a block of memory which is padded for use with the SIMD -// optimizations used by FFmpeg. -static uint8_t* AllocateFFmpegSafeBlock(size_t size) { - uint8_t* const block = reinterpret_cast<uint8_t*>(base::AlignedAlloc( - size + DecoderBuffer::kPaddingSize, DecoderBuffer::kAlignmentSize)); - memset(block + size, 0, DecoderBuffer::kPaddingSize); - return block; -} - DecoderBuffer::DecoderBuffer(size_t size) : size_(size), side_data_size_(0), is_key_frame_(false) { Initialize(); @@ -46,6 +37,12 @@ DecoderBuffer::DecoderBuffer(const uint8_t* data, memcpy(side_data_.get(), side_data, side_data_size_); } +DecoderBuffer::DecoderBuffer(std::unique_ptr<uint8_t[]> data, size_t size) + : data_(std::move(data)), + size_(size), + side_data_size_(0), + is_key_frame_(false) {} + DecoderBuffer::DecoderBuffer(std::unique_ptr<UnalignedSharedMemory> shm, size_t size) : size_(size), @@ -67,9 +64,9 @@ DecoderBuffer::~DecoderBuffer() { } void DecoderBuffer::Initialize() { - data_.reset(AllocateFFmpegSafeBlock(size_)); + data_.reset(new uint8_t[size_]); if (side_data_size_ > 0) - side_data_.reset(AllocateFFmpegSafeBlock(side_data_size_)); + side_data_.reset(new uint8_t[side_data_size_]); } // static @@ -93,6 +90,14 @@ scoped_refptr<DecoderBuffer> DecoderBuffer::CopyFrom(const uint8_t* data, } // static +scoped_refptr<DecoderBuffer> DecoderBuffer::FromArray( + std::unique_ptr<uint8_t[]> data, + size_t size) { + CHECK(data); + return base::WrapRefCounted(new DecoderBuffer(std::move(data), size)); +} + +// static scoped_refptr<DecoderBuffer> DecoderBuffer::FromSharedMemoryRegion( base::subtle::PlatformSharedMemoryRegion region, off_t offset, @@ -185,7 +190,7 @@ void DecoderBuffer::CopySideDataFrom(const uint8_t* side_data, size_t side_data_size) { if (side_data_size > 0) { side_data_size_ = side_data_size; - side_data_.reset(AllocateFFmpegSafeBlock(side_data_size_)); + side_data_.reset(new uint8_t[side_data_size_]); memcpy(side_data_.get(), side_data, side_data_size_); } else { side_data_.reset(); diff --git a/chromium/media/base/decoder_buffer.h b/chromium/media/base/decoder_buffer.h index 05ab14e9227..e1bd6297b47 100644 --- a/chromium/media/base/decoder_buffer.h +++ b/chromium/media/base/decoder_buffer.h @@ -28,10 +28,6 @@ namespace media { // A specialized buffer for interfacing with audio / video decoders. // -// Specifically ensures that data is aligned and padded as necessary by the -// underlying decoding framework. On desktop platforms this means memory is -// allocated using FFmpeg with particular alignment and padding requirements. -// // Also includes decoder specific functionality for decryption. // // NOTE: It is illegal to call any method when end_of_stream() is true. @@ -47,25 +43,30 @@ class MEDIA_EXPORT DecoderBuffer #endif }; - // Allocates buffer with |size| >= 0. Buffer will be padded and aligned - // as necessary, and |is_key_frame_| will default to false. + // Allocates buffer with |size| >= 0. |is_key_frame_| will default to false. explicit DecoderBuffer(size_t size); - // Create a DecoderBuffer whose |data_| is copied from |data|. Buffer will be - // padded and aligned as necessary. |data| must not be NULL and |size| >= 0. - // The buffer's |is_key_frame_| will default to false. + // Create a DecoderBuffer whose |data_| is copied from |data|. |data| must not + // be NULL and |size| >= 0. The buffer's |is_key_frame_| will default to + // false. static scoped_refptr<DecoderBuffer> CopyFrom(const uint8_t* data, size_t size); // Create a DecoderBuffer whose |data_| is copied from |data| and |side_data_| - // is copied from |side_data|. Buffers will be padded and aligned as necessary - // Data pointers must not be NULL and sizes must be >= 0. The buffer's - // |is_key_frame_| will default to false. + // is copied from |side_data|. Data pointers must not be NULL and sizes must + // be >= 0. The buffer's |is_key_frame_| will default to false. static scoped_refptr<DecoderBuffer> CopyFrom(const uint8_t* data, size_t size, const uint8_t* side_data, size_t side_data_size); + // Create a DecoderBuffer where data() of |size| bytes resides within the heap + // as byte array. The buffer's |is_key_frame_| will default to false. + // + // Ownership of |data| is transferred to the buffer. + static scoped_refptr<DecoderBuffer> FromArray(std::unique_ptr<uint8_t[]> data, + size_t size); + // Create a DecoderBuffer where data() of |size| bytes resides within the // memory referred to by |region| at non-negative offset |offset|. The // buffer's |is_key_frame_| will default to false. @@ -197,21 +198,26 @@ class MEDIA_EXPORT DecoderBuffer protected: friend class base::RefCountedThreadSafe<DecoderBuffer>; - // Allocates a buffer of size |size| >= 0 and copies |data| into it. Buffer - // will be padded and aligned as necessary. If |data| is NULL then |data_| is - // set to NULL and |buffer_size_| to 0. |is_key_frame_| will default to - // false. + // Allocates a buffer of size |size| >= 0 and copies |data| into it. If |data| + // is NULL then |data_| is set to NULL and |buffer_size_| to 0. + // |is_key_frame_| will default to false. DecoderBuffer(const uint8_t* data, size_t size, const uint8_t* side_data, size_t side_data_size); + DecoderBuffer(std::unique_ptr<uint8_t[]> data, size_t size); + DecoderBuffer(std::unique_ptr<UnalignedSharedMemory> shm, size_t size); DecoderBuffer(std::unique_ptr<ReadOnlyUnalignedMapping> shared_mem_mapping, size_t size); + virtual ~DecoderBuffer(); + // Encoded data, if it is stored on the heap. + std::unique_ptr<uint8_t[]> data_; + private: // Presentation time of the frame. base::TimeDelta timestamp_; @@ -220,12 +226,10 @@ class MEDIA_EXPORT DecoderBuffer // Size of the encoded data. size_t size_; - // Encoded data, if it is stored on the heap. - std::unique_ptr<uint8_t, base::AlignedFreeDeleter> data_; // Side data. Used for alpha channel in VPx, and for text cues. size_t side_data_size_; - std::unique_ptr<uint8_t, base::AlignedFreeDeleter> side_data_; + std::unique_ptr<uint8_t[]> side_data_; // Encoded data, if it is stored in a shared memory mapping. std::unique_ptr<ReadOnlyUnalignedMapping> shared_mem_mapping_; diff --git a/chromium/media/base/decoder_buffer_unittest.cc b/chromium/media/base/decoder_buffer_unittest.cc index 49a393f9f1a..a58d81de99b 100644 --- a/chromium/media/base/decoder_buffer_unittest.cc +++ b/chromium/media/base/decoder_buffer_unittest.cc @@ -63,6 +63,21 @@ TEST(DecoderBufferTest, CopyFrom) { EXPECT_FALSE(buffer3->is_key_frame()); } +TEST(DecoderBufferTest, FromArray) { + const uint8_t kData[] = "hello"; + const size_t kDataSize = base::size(kData); + std::unique_ptr<uint8_t[]> ptr(new uint8_t[kDataSize]); + memcpy(ptr.get(), kData, kDataSize); + + scoped_refptr<DecoderBuffer> buffer( + DecoderBuffer::FromArray(std::move(ptr), kDataSize)); + ASSERT_TRUE(buffer.get()); + EXPECT_EQ(buffer->data_size(), kDataSize); + EXPECT_EQ(0, memcmp(buffer->data(), kData, kDataSize)); + EXPECT_FALSE(buffer->end_of_stream()); + EXPECT_FALSE(buffer->is_key_frame()); +} + TEST(DecoderBufferTest, FromPlatformSharedMemoryRegion) { const uint8_t kData[] = "hello"; const size_t kDataSize = base::size(kData); @@ -171,35 +186,6 @@ TEST(DecoderBufferTest, FromSharedMemoryRegion_ZeroSize) { ASSERT_FALSE(buffer.get()); } -#if !defined(OS_ANDROID) -TEST(DecoderBufferTest, PaddingAlignment) { - const uint8_t kData[] = "hello"; - const size_t kDataSize = base::size(kData); - scoped_refptr<DecoderBuffer> buffer2(DecoderBuffer::CopyFrom( - reinterpret_cast<const uint8_t*>(&kData), kDataSize)); - ASSERT_TRUE(buffer2.get()); - - // Padding data should always be zeroed. - for(int i = 0; i < DecoderBuffer::kPaddingSize; i++) - EXPECT_EQ((buffer2->data() + kDataSize)[i], 0); - - // If the data is padded correctly we should be able to read and write past - // the end of the data by DecoderBuffer::kPaddingSize bytes without crashing - // or Valgrind/ASAN throwing errors. - const uint8_t kFillChar = 0xFF; - memset( - buffer2->writable_data() + kDataSize, kFillChar, - DecoderBuffer::kPaddingSize); - for(int i = 0; i < DecoderBuffer::kPaddingSize; i++) - EXPECT_EQ((buffer2->data() + kDataSize)[i], kFillChar); - - EXPECT_EQ(0u, reinterpret_cast<uintptr_t>( - buffer2->data()) & (DecoderBuffer::kAlignmentSize - 1)); - - EXPECT_FALSE(buffer2->is_key_frame()); -} -#endif - TEST(DecoderBufferTest, ReadingWriting) { const char kData[] = "hello"; const size_t kDataSize = base::size(kData); diff --git a/chromium/media/base/decoder_factory.cc b/chromium/media/base/decoder_factory.cc index 1eb9877eb6c..47aa49bab02 100644 --- a/chromium/media/base/decoder_factory.cc +++ b/chromium/media/base/decoder_factory.cc @@ -21,7 +21,7 @@ void DecoderFactory::CreateVideoDecoders( scoped_refptr<base::SingleThreadTaskRunner> task_runner, GpuVideoAcceleratorFactories* gpu_factories, MediaLog* media_log, - const RequestOverlayInfoCB& request_overlay_info_cb, + RequestOverlayInfoCB request_overlay_info_cb, const gfx::ColorSpace& target_color_space, std::vector<std::unique_ptr<VideoDecoder>>* video_decoders) {} diff --git a/chromium/media/base/decoder_factory.h b/chromium/media/base/decoder_factory.h index eed5bec05fa..d01d55e6e56 100644 --- a/chromium/media/base/decoder_factory.h +++ b/chromium/media/base/decoder_factory.h @@ -47,7 +47,7 @@ class MEDIA_EXPORT DecoderFactory { scoped_refptr<base::SingleThreadTaskRunner> task_runner, GpuVideoAcceleratorFactories* gpu_factories, MediaLog* media_log, - const RequestOverlayInfoCB& request_overlay_info_cb, + RequestOverlayInfoCB request_overlay_info_cb, const gfx::ColorSpace& target_color_space, std::vector<std::unique_ptr<VideoDecoder>>* video_decoders); diff --git a/chromium/media/base/decryptor.h b/chromium/media/base/decryptor.h index 62b7902c6df..e084056471c 100644 --- a/chromium/media/base/decryptor.h +++ b/chromium/media/base/decryptor.h @@ -46,7 +46,7 @@ class MEDIA_EXPORT Decryptor { // Indicates that a new key has been added to the ContentDecryptionModule // object associated with the Decryptor. - typedef base::Callback<void()> NewKeyCB; + using NewKeyCB = base::RepeatingClosure; // Registers a NewKeyCB which should be called when a new key is added to the // decryptor. Only one NewKeyCB can be registered for one |stream_type|. @@ -55,7 +55,7 @@ class MEDIA_EXPORT Decryptor { // registering a null callback cancels the originally registered callback. // TODO(crbug.com/821288): Replace this with CdmContext::RegisterEventCB(). virtual void RegisterNewKeyCB(StreamType stream_type, - const NewKeyCB& key_added_cb) = 0; + NewKeyCB key_added_cb) = 0; // Indicates completion of a decryption operation. // @@ -71,7 +71,8 @@ class MEDIA_EXPORT Decryptor { // - Only |data|, |data_size| and |timestamp| are set in the returned // DecoderBuffer. The callback handler is responsible for setting other // fields as appropriate. - typedef base::Callback<void(Status, scoped_refptr<DecoderBuffer>)> DecryptCB; + using DecryptCB = + base::OnceCallback<void(Status, scoped_refptr<DecoderBuffer>)>; // Decrypts the |encrypted| buffer. The decrypt status and decrypted buffer // are returned via the provided callback |decrypt_cb|. The |encrypted| buffer @@ -81,7 +82,7 @@ class MEDIA_EXPORT Decryptor { // a time for a given |stream_type|. virtual void Decrypt(StreamType stream_type, scoped_refptr<DecoderBuffer> encrypted, - const DecryptCB& decrypt_cb) = 0; + DecryptCB decrypt_cb) = 0; // Cancels the scheduled decryption operation for |stream_type| and fires the // pending DecryptCB immediately with kSuccess and NULL. @@ -93,14 +94,14 @@ class MEDIA_EXPORT Decryptor { // // First Parameter: Indicates initialization success. // - Set to true if initialization was successful. False if an error occurred. - typedef base::Callback<void(bool)> DecoderInitCB; + using DecoderInitCB = base::OnceCallback<void(bool)>; // Initializes a decoder with the given |config|, executing the |init_cb| // upon completion. virtual void InitializeAudioDecoder(const AudioDecoderConfig& config, - const DecoderInitCB& init_cb) = 0; + DecoderInitCB init_cb) = 0; virtual void InitializeVideoDecoder(const VideoDecoderConfig& config, - const DecoderInitCB& init_cb) = 0; + DecoderInitCB init_cb) = 0; // Helper structure for managing multiple decoded audio buffers per input. typedef std::list<scoped_refptr<AudioBuffer> > AudioFrames; diff --git a/chromium/media/base/demuxer.h b/chromium/media/base/demuxer.h index 820744b4148..c41493a781d 100644 --- a/chromium/media/base/demuxer.h +++ b/chromium/media/base/demuxer.h @@ -57,14 +57,14 @@ class MEDIA_EXPORT Demuxer : public MediaResource { // First parameter - The type of initialization data. // Second parameter - The initialization data associated with the stream. using EncryptedMediaInitDataCB = - base::Callback<void(EmeInitDataType type, - const std::vector<uint8_t>& init_data)>; + base::RepeatingCallback<void(EmeInitDataType type, + const std::vector<uint8_t>& init_data)>; // Notifies demuxer clients that media track configuration has been updated // (e.g. the initial stream metadata has been parsed successfully, or a new // init segment has been parsed successfully in MSE case). using MediaTracksUpdatedCB = - base::Callback<void(std::unique_ptr<MediaTracks>)>; + base::RepeatingCallback<void(std::unique_ptr<MediaTracks>)>; // Called once the demuxer has finished enabling or disabling tracks. The type // argument is required because the vector may be empty. diff --git a/chromium/media/base/fallback_video_decoder.cc b/chromium/media/base/fallback_video_decoder.cc index 07b047edae6..a4796c2ef4e 100644 --- a/chromium/media/base/fallback_video_decoder.cc +++ b/chromium/media/base/fallback_video_decoder.cc @@ -48,11 +48,11 @@ void FallbackVideoDecoder::FallbackInitialize(const VideoDecoderConfig& config, InitCB init_cb, const OutputCB& output_cb, const WaitingCB& waiting_cb, - bool success) { + Status status) { // The preferred decoder was successfully initialized. - if (success) { + if (status.is_ok()) { selected_decoder_ = preferred_decoder_.get(); - std::move(init_cb).Run(true); + std::move(init_cb).Run(OkStatus()); return; } diff --git a/chromium/media/base/fallback_video_decoder.h b/chromium/media/base/fallback_video_decoder.h index c0833ad3f30..98c06c82949 100644 --- a/chromium/media/base/fallback_video_decoder.h +++ b/chromium/media/base/fallback_video_decoder.h @@ -43,7 +43,7 @@ class MEDIA_EXPORT FallbackVideoDecoder : public VideoDecoder { InitCB init_cb, const OutputCB& output_cb, const WaitingCB& waiting_cb, - bool success); + Status status); std::unique_ptr<media::VideoDecoder> preferred_decoder_; std::unique_ptr<media::VideoDecoder> fallback_decoder_; diff --git a/chromium/media/base/fallback_video_decoder_unittest.cc b/chromium/media/base/fallback_video_decoder_unittest.cc index d54bd02a6f1..ecf80f36b23 100644 --- a/chromium/media/base/fallback_video_decoder_unittest.cc +++ b/chromium/media/base/fallback_video_decoder_unittest.cc @@ -42,13 +42,15 @@ class FallbackVideoDecoderUnittest : public ::testing::TestWithParam<bool> { if (is_fallback && !preferred_should_succeed) { EXPECT_CALL(*result, Initialize_(_, _, _, _, _, _)) - .WillOnce(RunOnceCallback<3>(true)); + .WillOnce(RunOnceCallback<3>(OkStatus())); } if (!is_fallback) { preferred_decoder_ = result; EXPECT_CALL(*result, Initialize_(_, _, _, _, _, _)) - .WillOnce(RunOnceCallback<3>(preferred_should_succeed)); + .WillOnce(RunOnceCallback<3>(preferred_should_succeed + ? OkStatus() + : StatusCode::kCodeOnlyForTesting)); } else { backup_decoder_ = result; } @@ -63,7 +65,7 @@ class FallbackVideoDecoderUnittest : public ::testing::TestWithParam<bool> { fallback_decoder_->Initialize( video_decoder_config_, false, nullptr, - base::BindRepeating([](bool success) { EXPECT_TRUE(success); }), + base::BindOnce([](Status status) { EXPECT_TRUE(status.is_ok()); }), base::DoNothing(), base::DoNothing()); } @@ -123,16 +125,16 @@ TEST_P(FallbackVideoDecoderUnittest, ReinitializeWithPreferredFailing) { Initialize(PreferredShouldSucceed()); // If we succeedd the first time, it should still be alive. - if (PreferredShouldSucceed()) { + if (PreferredShouldSucceed()) { // fail initialization EXPECT_CALL(*preferred_decoder_, Initialize_(_, _, _, _, _, _)) - .WillOnce(RunOnceCallback<3>(false)); // fail initialization + .WillOnce(RunOnceCallback<3>(StatusCode::kCodeOnlyForTesting)); } EXPECT_CALL(*backup_decoder_, Initialize_(_, _, _, _, _, _)) - .WillOnce(RunOnceCallback<3>(true)); + .WillOnce(RunOnceCallback<3>(OkStatus())); fallback_decoder_->Initialize( video_decoder_config_, false, nullptr, - base::BindRepeating([](bool success) { EXPECT_TRUE(success); }), + base::BindOnce([](Status status) { EXPECT_TRUE(status.is_ok()); }), base::DoNothing(), base::DoNothing()); } @@ -148,16 +150,16 @@ TEST_P(FallbackVideoDecoderUnittest, ReinitializeWithPreferredSuccessful) { // If we succeedd the first time, it should still be alive. if (PreferredShouldSucceed()) { EXPECT_CALL(*preferred_decoder_, Initialize_(_, _, _, _, _, _)) - .WillOnce(RunOnceCallback<3>(true)); // pass initialization + .WillOnce(RunOnceCallback<3>(OkStatus())); // pass initialization } else { // Otherwise, preferred was deleted, and we only backup still exists. EXPECT_CALL(*backup_decoder_, Initialize_(_, _, _, _, _, _)) - .WillOnce(RunOnceCallback<3>(true)); + .WillOnce(RunOnceCallback<3>(OkStatus())); } fallback_decoder_->Initialize( video_decoder_config_, false, nullptr, - base::BindOnce([](bool success) { EXPECT_TRUE(success); }), + base::BindOnce([](Status status) { EXPECT_TRUE(status.is_ok()); }), base::DoNothing(), base::DoNothing()); } diff --git a/chromium/media/base/frame_rate_estimator.cc b/chromium/media/base/frame_rate_estimator.cc new file mode 100644 index 00000000000..96a39b90707 --- /dev/null +++ b/chromium/media/base/frame_rate_estimator.cc @@ -0,0 +1,87 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/base/frame_rate_estimator.h" + +#include <array> + +namespace media { + +// Number of samples we need before we'll trust the estimate. All samples must +// end up in the same bucket, else we won't report an FPS for the window. +// kMaxSamples is the maximum that we'll need, if we think that things are +// unstable. kMinSamples is the minimum that we'll need to establish a +// baseline fps optimistically. +static constexpr int kMaxSamples = 15; +static constexpr int kMinSamples = 3; + +// Size (in FPS) of our buckets, which take on integral multiples of +// |BucketSize|. Observed frame rates are rounded to the nearest bucket, so +// that 1.75 and 1.25 might both end up in bucket 2. +static constexpr int BucketSize = 1; + +namespace { + +// Convert |duration| into an FPS bucket. +int ToBucket(base::TimeDelta duration) { + return static_cast<int>(((1.0 / duration.InSecondsF()) + (BucketSize / 2.0)) / + BucketSize) * + BucketSize; +} + +} // namespace + +FrameRateEstimator::FrameRateEstimator() + : duration_(kMaxSamples), required_samples_(kMinSamples) {} + +FrameRateEstimator::~FrameRateEstimator() = default; + +void FrameRateEstimator::AddSample(base::TimeDelta frame_duration) { + duration_.AddSample(frame_duration); + + // See if the duration averages have enough samples. If not, then we can't + // do anything else yet. + if (duration_.count() < required_samples_) + return; + + // Make sure that the entire window is in the same bucket. + auto extremes = duration_.GetMinAndMax(); + // See if the current sample is too far from the bucketed average. + int bucketed_fps_min = ToBucket(extremes.first); + int bucketed_fps_max = ToBucket(extremes.second); + + if (bucketed_fps_min != bucketed_fps_max) { + // There's no current bucket until the entire window agrees. Use the + // maximum window size since we don't like disagreement. + most_recent_bucket_.reset(); + required_samples_ = kMaxSamples; + return; + } + + most_recent_bucket_ = bucketed_fps_min; +} + +base::Optional<int> FrameRateEstimator::ComputeFPS() { + return most_recent_bucket_; +} + +void FrameRateEstimator::Reset() { + duration_.Reset(); + most_recent_bucket_.reset(); + required_samples_ = kMinSamples; +} + +int FrameRateEstimator::GetRequiredSamplesForTesting() const { + return required_samples_; +} + +int FrameRateEstimator::GetMinSamplesForTesting() const { + return kMinSamples; +} + +int FrameRateEstimator::GetMaxSamplesForTesting() const { + return kMaxSamples; +} + +} // namespace media diff --git a/chromium/media/base/frame_rate_estimator.h b/chromium/media/base/frame_rate_estimator.h new file mode 100644 index 00000000000..3a00f7bcd5e --- /dev/null +++ b/chromium/media/base/frame_rate_estimator.h @@ -0,0 +1,51 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEDIA_BASE_FRAME_RATE_ESTIMATOR_H_ +#define MEDIA_BASE_FRAME_RATE_ESTIMATOR_H_ + +#include "base/macros.h" +#include "base/optional.h" +#include "media/base/media_export.h" +#include "media/base/moving_average.h" + +namespace media { + +// Utility class to provide a bucketed frame rate estimator. This class should +// provide a stable frame rate, as measured by a sequence of frame durations, +// or an indication that the fps isn't currently stable. +class MEDIA_EXPORT FrameRateEstimator { + public: + FrameRateEstimator(); + ~FrameRateEstimator(); + + // Add a frame with the given duration. + void AddSample(base::TimeDelta frame_duration); + + // Return the current (bucketed) frame rate (not duration), or nullopt if one + // isn't available with suitable certainty. + base::Optional<int> ComputeFPS(); + + // Reset everything. + void Reset(); + + // Return the current number of required samples. + int GetRequiredSamplesForTesting() const; + + // Return the min / max samples that we'll require for fast / slow estimates. + int GetMinSamplesForTesting() const; + int GetMaxSamplesForTesting() const; + + private: + MovingAverage duration_; + + uint64_t required_samples_; + + // Most recently computed bucketed FPS (not duration), if any. + base::Optional<int> most_recent_bucket_; +}; + +} // namespace media + +#endif // MEDIA_BASE_FRAME_RATE_ESTIMATOR_H_ diff --git a/chromium/media/base/frame_rate_estimator_unittest.cc b/chromium/media/base/frame_rate_estimator_unittest.cc new file mode 100644 index 00000000000..4701c3bd03a --- /dev/null +++ b/chromium/media/base/frame_rate_estimator_unittest.cc @@ -0,0 +1,142 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/base/frame_rate_estimator.h" + +#include <tuple> + +#include "testing/gtest/include/gtest/gtest.h" + +namespace media { + +using FpsPair = std::tuple<int, int>; + +class FrameRateEstimatorTest : public testing::TestWithParam<FpsPair> { + public: + void ProvideSamples(base::TimeDelta duration, int count) { + while (count--) + estimator_.AddSample(duration); + } + + void ProvideSample(base::TimeDelta duration) { + estimator_.AddSample(duration); + } + + int low_fps() const { return std::get<0>(GetParam()); } + int high_fps() const { return std::get<1>(GetParam()); } + + base::TimeDelta duration(int fps) { + return base::TimeDelta::FromSecondsD(1.0 / fps); + } + + FrameRateEstimator estimator_; +}; + +TEST_P(FrameRateEstimatorTest, NoEstimateInitially) { + // Asking for an estimate with no samples is okay, though it shouldn't return + // an estimate. + EXPECT_FALSE(estimator_.ComputeFPS()); +} + +TEST_P(FrameRateEstimatorTest, AverageConvergesThenReset) { + // Verify that the estimate is provided after the required samples are reached + // and that Reset() clears it. + + // The initial sample requirement should allow quick convergence. + EXPECT_EQ(estimator_.GetRequiredSamplesForTesting(), + estimator_.GetMinSamplesForTesting()); + + // Make sure that it doesn't converge before the required sample count. + ProvideSamples(duration(low_fps()), + estimator_.GetRequiredSamplesForTesting() - 1); + EXPECT_FALSE(estimator_.ComputeFPS()); + ProvideSample(duration(low_fps())); + EXPECT_EQ(*estimator_.ComputeFPS(), low_fps()); + + estimator_.Reset(); + EXPECT_FALSE(estimator_.ComputeFPS()); + ProvideSamples(duration(low_fps()), + estimator_.GetRequiredSamplesForTesting() - 1); + EXPECT_FALSE(estimator_.ComputeFPS()); +} + +TEST_P(FrameRateEstimatorTest, DurationJitterIsFine) { + // A little jitter doesn't change anything. + ProvideSamples(duration(low_fps()), + estimator_.GetRequiredSamplesForTesting()); + + // Compute a jitter that's not big enough to move it out of its bucket. We + // use +1 so it works either above or below (below has more room). 2.0 would + // be fine ideally, but we make it a bit smaller than that just to prevent + // floating point weirdness. + auto jitter = (duration(low_fps()) - duration(low_fps() + 1)) / 2.1; + for (int i = 0; i < estimator_.GetRequiredSamplesForTesting(); i++) { + ProvideSample(duration(low_fps()) + jitter); + EXPECT_EQ(*estimator_.ComputeFPS(), low_fps()); + } + + for (int i = 0; i < estimator_.GetRequiredSamplesForTesting(); i++) { + ProvideSample(duration(low_fps()) - jitter); + EXPECT_EQ(*estimator_.ComputeFPS(), low_fps()); + } +} + +TEST_P(FrameRateEstimatorTest, AverageDoesntSkew) { + // Changing frame rates shouldn't skew between them. It should stop providing + // estimates temporarily. + ProvideSamples(duration(low_fps()), + estimator_.GetRequiredSamplesForTesting()); + EXPECT_EQ(*estimator_.ComputeFPS(), low_fps()); + + ProvideSample(duration(high_fps())); + EXPECT_FALSE(estimator_.ComputeFPS()); + // We should now require more samples one we destabilized. + EXPECT_EQ(estimator_.GetRequiredSamplesForTesting(), + estimator_.GetMaxSamplesForTesting()); + ProvideSamples(duration(high_fps()), + estimator_.GetRequiredSamplesForTesting() - 2); + EXPECT_FALSE(estimator_.ComputeFPS()); + ProvideSample(duration(high_fps())); + EXPECT_EQ(*estimator_.ComputeFPS(), high_fps()); +} + +TEST_P(FrameRateEstimatorTest, ResetAllowsFastConvergence) { + // If we're in slow-convergence mode, Reset() should allow fast convergence. + + // Get into slow convergence mode by providing a non-uniform window. + ProvideSamples(duration(low_fps()), estimator_.GetMinSamplesForTesting() - 1); + ProvideSamples(duration(high_fps()), 1); + EXPECT_EQ(estimator_.GetRequiredSamplesForTesting(), + estimator_.GetMaxSamplesForTesting()); + + // See if Reset() gets us back to fast convergence. + estimator_.Reset(); + EXPECT_EQ(estimator_.GetRequiredSamplesForTesting(), + estimator_.GetMinSamplesForTesting()); +} + +// Instantiate tests for lots of common frame rates. +INSTANTIATE_TEST_SUITE_P(All, + FrameRateEstimatorTest, + testing::Values(FpsPair(24, 30), + FpsPair(24, 60), + FpsPair(24, 90), + FpsPair(24, 120), + FpsPair(24, 240), + + FpsPair(30, 60), + FpsPair(30, 90), + FpsPair(30, 120), + FpsPair(30, 240), + + FpsPair(60, 90), + FpsPair(60, 120), + FpsPair(60, 240), + + FpsPair(90, 120), + FpsPair(90, 240), + + FpsPair(120, 240))); + +} // namespace media diff --git a/chromium/media/base/ipc/media_param_traits_macros.h b/chromium/media/base/ipc/media_param_traits_macros.h index 805726bcfe0..3e09669bce0 100644 --- a/chromium/media/base/ipc/media_param_traits_macros.h +++ b/chromium/media/base/ipc/media_param_traits_macros.h @@ -23,12 +23,13 @@ #include "media/base/eme_constants.h" #include "media/base/encryption_scheme.h" #include "media/base/hdr_metadata.h" -#include "media/base/media_log_event.h" +#include "media/base/media_log_record.h" #include "media/base/media_status.h" #include "media/base/output_device_info.h" #include "media/base/overlay_info.h" #include "media/base/pipeline_status.h" #include "media/base/sample_format.h" +#include "media/base/status_codes.h" #include "media/base/subsample_entry.h" #include "media/base/video_codecs.h" #include "media/base/video_color_space.h" @@ -43,13 +44,15 @@ #include "media/video/supported_video_decoder_config.h" #include "ui/gfx/ipc/color/gfx_param_traits_macros.h" -#if defined(OS_ANDROID) -#include "media/base/android/media_drm_key_type.h" -#endif // defined(OS_ANDROID) +#if BUILDFLAG(ENABLE_MEDIA_DRM_STORAGE) +#include "media/base/media_drm_key_type.h" +#endif // BUILDFLAG(ENABLE_MEDIA_DRM_STORAGE) // Enum traits. IPC_ENUM_TRAITS_MAX_VALUE(media::AudioCodec, media::AudioCodec::kAudioCodecMax) +IPC_ENUM_TRAITS_MAX_VALUE(media::AudioCodecProfile, + media::AudioCodecProfile::kMaxValue) IPC_ENUM_TRAITS_MAX_VALUE(media::AudioLatency::LatencyType, media::AudioLatency::LATENCY_COUNT) @@ -110,8 +113,8 @@ IPC_ENUM_TRAITS_MAX_VALUE(media::EncryptionScheme, IPC_ENUM_TRAITS_MAX_VALUE(media::HdcpVersion, media::HdcpVersion::kHdcpVersionMax) -IPC_ENUM_TRAITS_MAX_VALUE(media::MediaLogEvent::Type, - media::MediaLogEvent::TYPE_LAST) +IPC_ENUM_TRAITS_MAX_VALUE(media::MediaLogRecord::Type, + media::MediaLogRecord::Type::kMaxValue) IPC_ENUM_TRAITS_MAX_VALUE(media::MediaStatus::State, media::MediaStatus::State::STATE_MAX) @@ -145,11 +148,13 @@ IPC_ENUM_TRAITS_MAX_VALUE(media::VideoRotation, media::VIDEO_ROTATION_MAX) IPC_ENUM_TRAITS_MAX_VALUE(media::container_names::MediaContainerName, media::container_names::CONTAINER_MAX) -#if defined(OS_ANDROID) +IPC_ENUM_TRAITS_MAX_VALUE(media::StatusCode, media::StatusCode::kMaxValue) + +#if BUILDFLAG(ENABLE_MEDIA_DRM_STORAGE) IPC_ENUM_TRAITS_MIN_MAX_VALUE(media::MediaDrmKeyType, media::MediaDrmKeyType::MIN, media::MediaDrmKeyType::MAX) -#endif // defined(OS_ANDROID) +#endif // BUILDFLAG(ENABLE_MEDIA_DRM_STORAGE) IPC_ENUM_TRAITS_VALIDATE( media::VideoColorSpace::PrimaryID, @@ -177,7 +182,7 @@ IPC_STRUCT_TRAITS_BEGIN(media::CdmConfig) IPC_STRUCT_TRAITS_MEMBER(use_hw_secure_codecs) IPC_STRUCT_TRAITS_END() -IPC_STRUCT_TRAITS_BEGIN(media::MediaLogEvent) +IPC_STRUCT_TRAITS_BEGIN(media::MediaLogRecord) IPC_STRUCT_TRAITS_MEMBER(id) IPC_STRUCT_TRAITS_MEMBER(type) IPC_STRUCT_TRAITS_MEMBER(params) diff --git a/chromium/media/base/key_systems.cc b/chromium/media/base/key_systems.cc index 88cb98dd58d..eabf9796a93 100644 --- a/chromium/media/base/key_systems.cc +++ b/chromium/media/base/key_systems.cc @@ -10,6 +10,7 @@ #include <unordered_map> #include "base/logging.h" +#include "base/no_destructor.h" #include "base/stl_util.h" #include "base/strings/string_util.h" #include "base/threading/thread_checker.h" @@ -232,8 +233,6 @@ class KeySystemsImpl : public KeySystems { public: static KeySystemsImpl* GetInstance(); - void UpdateIfNeeded(); - // These two functions are for testing purpose only. void AddCodecMaskForTesting(EmeMediaType media_type, const std::string& codec, @@ -242,6 +241,8 @@ class KeySystemsImpl : public KeySystems { uint32_t mask); // Implementation of KeySystems interface. + void UpdateIfNeeded() override; + bool IsSupportedKeySystem(const std::string& key_system) const override; bool CanUseAesDecryptor(const std::string& key_system) const override; @@ -277,6 +278,8 @@ class KeySystemsImpl : public KeySystems { const std::string& key_system) const override; private: + friend class base::NoDestructor<KeySystemsImpl>; + KeySystemsImpl(); ~KeySystemsImpl() override; @@ -328,9 +331,9 @@ class KeySystemsImpl : public KeySystems { }; KeySystemsImpl* KeySystemsImpl::GetInstance() { - static KeySystemsImpl* key_systems = new KeySystemsImpl(); + static base::NoDestructor<KeySystemsImpl> key_systems; key_systems->UpdateIfNeeded(); - return key_systems; + return key_systems.get(); } // Because we use a thread-safe static, the key systems info must be populated diff --git a/chromium/media/base/key_systems.h b/chromium/media/base/key_systems.h index c912c1bbf72..d57aa8e6c96 100644 --- a/chromium/media/base/key_systems.h +++ b/chromium/media/base/key_systems.h @@ -28,6 +28,9 @@ class MEDIA_EXPORT KeySystems { public: static KeySystems* GetInstance(); + // Refreshes the list of available key systems if it may be out of date. + virtual void UpdateIfNeeded() = 0; + // Returns whether |key_system| is a supported key system. virtual bool IsSupportedKeySystem(const std::string& key_system) const = 0; diff --git a/chromium/media/base/keyboard_event_counter.h b/chromium/media/base/keyboard_event_counter.h index 5e84730c672..d9f4e56d24f 100644 --- a/chromium/media/base/keyboard_event_counter.h +++ b/chromium/media/base/keyboard_event_counter.h @@ -12,8 +12,8 @@ #include "base/macros.h" #include "media/base/media_export.h" -#include "ui/events/event_constants.h" #include "ui/events/keycodes/keyboard_codes.h" +#include "ui/events/types/event_type.h" namespace media { diff --git a/chromium/media/base/mac/BUILD.gn b/chromium/media/base/mac/BUILD.gn index 459cd144711..9a1e04142a8 100644 --- a/chromium/media/base/mac/BUILD.gn +++ b/chromium/media/base/mac/BUILD.gn @@ -39,9 +39,7 @@ jumbo_source_set("mac") { source_set("unit_tests") { testonly = true - sources = [ - "video_frame_mac_unittests.cc", - ] + sources = [ "video_frame_mac_unittests.cc" ] libs = [ "CoreVideo.framework" ] configs += [ "//media:media_config" ] deps = [ diff --git a/chromium/media/base/mac/video_frame_mac_unittests.cc b/chromium/media/base/mac/video_frame_mac_unittests.cc index 86ecdf5bfe9..f0b6de5857f 100644 --- a/chromium/media/base/mac/video_frame_mac_unittests.cc +++ b/chromium/media/base/mac/video_frame_mac_unittests.cc @@ -96,7 +96,7 @@ TEST(VideoFrameMac, CheckLifetime) { auto wrapper_frame = VideoFrame::WrapVideoFrame( frame, frame->format(), frame->visible_rect(), frame->natural_size()); wrapper_frame->AddDestructionObserver( - base::Bind(&Increment, &instances_destroyed)); + base::BindOnce(&Increment, &instances_destroyed)); ASSERT_TRUE(wrapper_frame.get()); auto pb = WrapVideoFrameInCVPixelBuffer(*wrapper_frame); diff --git a/chromium/media/base/android/media_drm_key_type.h b/chromium/media/base/media_drm_key_type.h index 4996085af0b..fea284d0ed2 100644 --- a/chromium/media/base/android/media_drm_key_type.h +++ b/chromium/media/base/media_drm_key_type.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef MEDIA_BASE_ANDROID_MEDIA_DRM_KEY_TYPE_H_ -#define MEDIA_BASE_ANDROID_MEDIA_DRM_KEY_TYPE_H_ +#ifndef MEDIA_BASE_MEDIA_DRM_KEY_TYPE_H_ +#define MEDIA_BASE_MEDIA_DRM_KEY_TYPE_H_ #include <stdint.h> @@ -22,4 +22,4 @@ enum class MediaDrmKeyType : uint32_t { }; } // namespace media -#endif // MEDIA_BASE_ANDROID_MEDIA_DRM_KEY_TYPE_H_ +#endif // MEDIA_BASE_MEDIA_DRM_KEY_TYPE_H_ diff --git a/chromium/media/base/android/media_drm_storage.cc b/chromium/media/base/media_drm_storage.cc index 9f4a92b9b01..f27eba98ea1 100644 --- a/chromium/media/base/android/media_drm_storage.cc +++ b/chromium/media/base/media_drm_storage.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "media/base/android/media_drm_storage.h" +#include "media/base/media_drm_storage.h" #include <utility> diff --git a/chromium/media/base/android/media_drm_storage.h b/chromium/media/base/media_drm_storage.h index 276ebc9ae6e..ac17314bc3e 100644 --- a/chromium/media/base/android/media_drm_storage.h +++ b/chromium/media/base/media_drm_storage.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef MEDIA_BASE_ANDROID_MEDIA_DRM_STORAGE_H_ -#define MEDIA_BASE_ANDROID_MEDIA_DRM_STORAGE_H_ +#ifndef MEDIA_BASE_MEDIA_DRM_STORAGE_H_ +#define MEDIA_BASE_MEDIA_DRM_STORAGE_H_ #include <stdint.h> @@ -15,7 +15,7 @@ #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "base/optional.h" -#include "media/base/android/media_drm_key_type.h" +#include "media/base/media_drm_key_type.h" #include "media/base/media_export.h" #include "url/origin.h" @@ -34,7 +34,7 @@ class MEDIA_EXPORT MediaDrmStorage // If not specified, the device specific origin ID is to be used. using MediaDrmOriginId = base::Optional<base::UnguessableToken>; - struct SessionData { + struct MEDIA_EXPORT SessionData { SessionData(std::vector<uint8_t> key_set_id, std::string mime_type, MediaDrmKeyType key_type); @@ -99,8 +99,9 @@ class MEDIA_EXPORT MediaDrmStorage DISALLOW_COPY_AND_ASSIGN(MediaDrmStorage); }; -using CreateStorageCB = base::Callback<std::unique_ptr<MediaDrmStorage>()>; +using CreateStorageCB = + base::RepeatingCallback<std::unique_ptr<MediaDrmStorage>()>; } // namespace media -#endif // MEDIA_BASE_ANDROID_MEDIA_DRM_STORAGE_H_ +#endif // MEDIA_BASE_MEDIA_DRM_STORAGE_H_ diff --git a/chromium/media/base/media_log.cc b/chromium/media/base/media_log.cc index 177866c8ee4..babef2baa8a 100644 --- a/chromium/media/base/media_log.cc +++ b/chromium/media/base/media_log.cc @@ -14,145 +14,13 @@ namespace media { +const char MediaLog::kEventKey[] = "event"; +const char MediaLog::kStatusText[] = "pipeline_error"; + // A count of all MediaLogs created in the current process. Used to generate // unique IDs. static base::AtomicSequenceNumber g_media_log_count; -std::string MediaLog::MediaLogLevelToString(MediaLogLevel level) { - switch (level) { - case MEDIALOG_ERROR: - return "error"; - case MEDIALOG_WARNING: - return "warning"; - case MEDIALOG_INFO: - return "info"; - case MEDIALOG_DEBUG: - return "debug"; - } - NOTREACHED(); - return NULL; -} - -MediaLogEvent::Type MediaLog::MediaLogLevelToEventType(MediaLogLevel level) { - switch (level) { - case MEDIALOG_ERROR: - return MediaLogEvent::MEDIA_ERROR_LOG_ENTRY; - case MEDIALOG_WARNING: - return MediaLogEvent::MEDIA_WARNING_LOG_ENTRY; - case MEDIALOG_INFO: - return MediaLogEvent::MEDIA_INFO_LOG_ENTRY; - case MEDIALOG_DEBUG: - return MediaLogEvent::MEDIA_DEBUG_LOG_ENTRY; - } - NOTREACHED(); - return MediaLogEvent::MEDIA_ERROR_LOG_ENTRY; -} - -std::string MediaLog::EventTypeToString(MediaLogEvent::Type type) { - switch (type) { - case MediaLogEvent::WEBMEDIAPLAYER_CREATED: - return "WEBMEDIAPLAYER_CREATED"; - case MediaLogEvent::WEBMEDIAPLAYER_DESTROYED: - return "WEBMEDIAPLAYER_DESTROYED"; - case MediaLogEvent::LOAD: - return "LOAD"; - case MediaLogEvent::SEEK: - return "SEEK"; - case MediaLogEvent::PLAY: - return "PLAY"; - case MediaLogEvent::PAUSE: - return "PAUSE"; - case MediaLogEvent::PIPELINE_STATE_CHANGED: - return "PIPELINE_STATE_CHANGED"; - case MediaLogEvent::PIPELINE_ERROR: - return "PIPELINE_ERROR"; - case MediaLogEvent::VIDEO_SIZE_SET: - return "VIDEO_SIZE_SET"; - case MediaLogEvent::DURATION_SET: - return "DURATION_SET"; - case MediaLogEvent::ENDED: - return "ENDED"; - case MediaLogEvent::TEXT_ENDED: - return "TEXT_ENDED"; - case MediaLogEvent::MEDIA_ERROR_LOG_ENTRY: - return "MEDIA_ERROR_LOG_ENTRY"; - case MediaLogEvent::MEDIA_WARNING_LOG_ENTRY: - return "MEDIA_WARNING_LOG_ENTRY"; - case MediaLogEvent::MEDIA_INFO_LOG_ENTRY: - return "MEDIA_INFO_LOG_ENTRY"; - case MediaLogEvent::MEDIA_DEBUG_LOG_ENTRY: - return "MEDIA_DEBUG_LOG_ENTRY"; - case MediaLogEvent::PROPERTY_CHANGE: - return "PROPERTY_CHANGE"; - case MediaLogEvent::BUFFERING_STATE_CHANGE: - return "BUFFERING_STATE_CHANGE"; - case MediaLogEvent::SUSPENDED: - return "SUSPENDED"; - } - NOTREACHED(); - return NULL; -} - -std::string MediaLog::MediaEventToLogString(const MediaLogEvent& event) { - // Special case for PIPELINE_ERROR, since that's by far the most useful - // event for figuring out media pipeline failures, and just reporting - // pipeline status as numeric code is not very helpful/user-friendly. - int error_code = 0; - if (event.type == MediaLogEvent::PIPELINE_ERROR && - event.params.GetInteger("pipeline_error", &error_code)) { - PipelineStatus status = static_cast<PipelineStatus>(error_code); - return EventTypeToString(event.type) + " " + PipelineStatusToString(status); - } - - std::string params_json; - base::JSONWriter::Write(event.params, ¶ms_json); - return EventTypeToString(event.type) + " " + params_json; -} - -std::string MediaLog::MediaEventToMessageString(const MediaLogEvent& event) { - switch (event.type) { - case MediaLogEvent::PIPELINE_ERROR: { - int error_code = 0; - event.params.GetInteger("pipeline_error", &error_code); - DCHECK_NE(error_code, 0); - return PipelineStatusToString(static_cast<PipelineStatus>(error_code)); - } - case MediaLogEvent::MEDIA_ERROR_LOG_ENTRY: { - std::string result = ""; - if (event.params.GetString(MediaLogLevelToString(MEDIALOG_ERROR), - &result)) - base::ReplaceChars(result, "\n", " ", &result); - return result; - } - default: - NOTREACHED(); - return ""; - } -} - -std::string MediaLog::BufferingStateToString( - BufferingState state, - BufferingStateChangeReason reason) { - DCHECK(state == BUFFERING_HAVE_NOTHING || state == BUFFERING_HAVE_ENOUGH); - DCHECK(reason == BUFFERING_CHANGE_REASON_UNKNOWN || - reason == DEMUXER_UNDERFLOW || reason == DECODER_UNDERFLOW || - reason == REMOTING_NETWORK_CONGESTION); - - std::string state_string = state == BUFFERING_HAVE_NOTHING - ? "BUFFERING_HAVE_NOTHING" - : "BUFFERING_HAVE_ENOUGH"; - - std::vector<std::string> flag_strings; - if (reason == DEMUXER_UNDERFLOW) - state_string += " (DEMUXER_UNDERFLOW)"; - else if (reason == DECODER_UNDERFLOW) - state_string += " (DECODER_UNDERFLOW)"; - else if (reason == REMOTING_NETWORK_CONGESTION) - state_string += " (REMOTING_NETWORK_CONGESTION)"; - - return state_string; -} - MediaLog::MediaLog() : MediaLog(new ParentLogRecord(this)) {} MediaLog::MediaLog(scoped_refptr<ParentLogRecord> parent_log_record) @@ -175,25 +43,37 @@ MediaLog::~MediaLog() { InvalidateLog(); } -void MediaLog::OnWebMediaPlayerDestroyed() { - AddEvent(CreateEvent(MediaLogEvent::WEBMEDIAPLAYER_DESTROYED)); - base::AutoLock auto_lock(parent_log_record_->lock); - // Forward to the parent log's implementation. - if (parent_log_record_->media_log) - parent_log_record_->media_log->OnWebMediaPlayerDestroyedLocked(); +// Default *Locked implementations +void MediaLog::AddLogRecordLocked(std::unique_ptr<MediaLogRecord> event) {} + +std::string MediaLog::GetErrorMessageLocked() { + return ""; } -void MediaLog::OnWebMediaPlayerDestroyedLocked() {} +void MediaLog::AddMessage(MediaLogMessageLevel level, std::string message) { + std::unique_ptr<MediaLogRecord> record( + CreateRecord(MediaLogRecord::Type::kMessage)); + record->params.SetStringPath(MediaLogMessageLevelToString(level), + std::move(message)); + AddLogRecord(std::move(record)); +} -void MediaLog::AddEvent(std::unique_ptr<MediaLogEvent> event) { +void MediaLog::NotifyError(PipelineStatus status) { + std::unique_ptr<MediaLogRecord> record( + CreateRecord(MediaLogRecord::Type::kMediaStatus)); + record->params.SetIntPath(MediaLog::kStatusText, status); + AddLogRecord(std::move(record)); +} + +void MediaLog::OnWebMediaPlayerDestroyedLocked() {} +void MediaLog::OnWebMediaPlayerDestroyed() { + AddEvent<MediaLogEvent::kWebMediaPlayerDestroyed>(); base::AutoLock auto_lock(parent_log_record_->lock); // Forward to the parent log's implementation. if (parent_log_record_->media_log) - parent_log_record_->media_log->AddEventLocked(std::move(event)); + parent_log_record_->media_log->OnWebMediaPlayerDestroyedLocked(); } -void MediaLog::AddEventLocked(std::unique_ptr<MediaLogEvent> event) {} - std::string MediaLog::GetErrorMessage() { base::AutoLock auto_lock(parent_log_record_->lock); // Forward to the parent log's implementation. @@ -203,132 +83,26 @@ std::string MediaLog::GetErrorMessage() { return ""; } -std::string MediaLog::GetErrorMessageLocked() { - return ""; -} - -std::unique_ptr<MediaLogEvent> MediaLog::CreateCreatedEvent( - const std::string& origin_url) { - std::unique_ptr<MediaLogEvent> event( - CreateEvent(MediaLogEvent::WEBMEDIAPLAYER_CREATED)); - event->params.SetString("origin_url", TruncateUrlString(origin_url)); - return event; -} - -std::unique_ptr<MediaLogEvent> MediaLog::CreateEvent(MediaLogEvent::Type type) { - std::unique_ptr<MediaLogEvent> event(new MediaLogEvent); - event->id = id_; - event->type = type; - event->time = base::TimeTicks::Now(); - return event; -} - -std::unique_ptr<MediaLogEvent> MediaLog::CreateBooleanEvent( - MediaLogEvent::Type type, - const std::string& property, - bool value) { - std::unique_ptr<MediaLogEvent> event(CreateEvent(type)); - event->params.SetBoolean(property, value); - return event; -} - -std::unique_ptr<MediaLogEvent> MediaLog::CreateStringEvent( - MediaLogEvent::Type type, - const std::string& property, - const std::string& value) { - std::unique_ptr<MediaLogEvent> event(CreateEvent(type)); - event->params.SetString(property, value); - return event; -} - -std::unique_ptr<MediaLogEvent> MediaLog::CreateTimeEvent( - MediaLogEvent::Type type, - const std::string& property, - base::TimeDelta value) { - return CreateTimeEvent(type, property, value.InSecondsF()); -} - -std::unique_ptr<MediaLogEvent> MediaLog::CreateTimeEvent( - MediaLogEvent::Type type, - const std::string& property, - double value) { - std::unique_ptr<MediaLogEvent> event(CreateEvent(type)); - if (std::isfinite(value)) - event->params.SetDouble(property, value); - else - event->params.SetString(property, "unknown"); - return event; -} - -std::unique_ptr<MediaLogEvent> MediaLog::CreateLoadEvent( - const std::string& url) { - std::unique_ptr<MediaLogEvent> event(CreateEvent(MediaLogEvent::LOAD)); - event->params.SetString("url", TruncateUrlString(url)); - return event; -} - -std::unique_ptr<MediaLogEvent> MediaLog::CreatePipelineStateChangedEvent( - PipelineImpl::State state) { - std::unique_ptr<MediaLogEvent> event( - CreateEvent(MediaLogEvent::PIPELINE_STATE_CHANGED)); - event->params.SetString("pipeline_state", - PipelineImpl::GetStateString(state)); - return event; -} - -std::unique_ptr<MediaLogEvent> MediaLog::CreatePipelineErrorEvent( - PipelineStatus error) { - std::unique_ptr<MediaLogEvent> event( - CreateEvent(MediaLogEvent::PIPELINE_ERROR)); - event->params.SetInteger("pipeline_error", error); - return event; -} - -std::unique_ptr<MediaLogEvent> MediaLog::CreateVideoSizeSetEvent( - size_t width, - size_t height) { - std::unique_ptr<MediaLogEvent> event( - CreateEvent(MediaLogEvent::VIDEO_SIZE_SET)); - event->params.SetInteger("width", width); - event->params.SetInteger("height", height); - return event; -} - -std::unique_ptr<MediaLogEvent> MediaLog::CreateBufferingStateChangedEvent( - const std::string& property, - BufferingState state, - BufferingStateChangeReason reason) { - return CreateStringEvent(MediaLogEvent::BUFFERING_STATE_CHANGE, property, - BufferingStateToString(state, reason)); -} - -void MediaLog::AddLogEvent(MediaLogLevel level, const std::string& message) { - std::unique_ptr<MediaLogEvent> event( - CreateEvent(MediaLogLevelToEventType(level))); - event->params.SetString(MediaLogLevelToString(level), message); - AddEvent(std::move(event)); -} - std::unique_ptr<MediaLog> MediaLog::Clone() { // Protected ctor, so we can't use std::make_unique. return base::WrapUnique(new MediaLog(parent_log_record_)); } -// static -std::string MediaLog::TruncateUrlString(std::string log_string) { - if (log_string.length() > kMaxUrlLength) { - log_string.resize(kMaxUrlLength); - - // Room for the ellipsis. - DCHECK_GE(kMaxUrlLength, std::size_t{3}); - log_string.replace(log_string.end() - 3, log_string.end(), "..."); - } - - return log_string; +void MediaLog::AddLogRecord(std::unique_ptr<MediaLogRecord> record) { + base::AutoLock auto_lock(parent_log_record_->lock); + // Forward to the parent log's implementation. + if (parent_log_record_->media_log) + parent_log_record_->media_log->AddLogRecordLocked(std::move(record)); } -MediaLog::ParentLogRecord::ParentLogRecord(MediaLog* log) : media_log(log) {} -MediaLog::ParentLogRecord::~ParentLogRecord() = default; +std::unique_ptr<MediaLogRecord> MediaLog::CreateRecord( + MediaLogRecord::Type type) { + auto record = std::make_unique<MediaLogRecord>(); + record->id = id_; + record->type = type; + record->time = base::TimeTicks::Now(); + return record; +} void MediaLog::InvalidateLog() { base::AutoLock auto_lock(parent_log_record_->lock); @@ -340,17 +114,20 @@ void MediaLog::InvalidateLog() { // Keep |parent_log_record_| around, since the lock must keep working. } -LogHelper::LogHelper(MediaLog::MediaLogLevel level, MediaLog* media_log) +MediaLog::ParentLogRecord::ParentLogRecord(MediaLog* log) : media_log(log) {} +MediaLog::ParentLogRecord::~ParentLogRecord() = default; + +LogHelper::LogHelper(MediaLogMessageLevel level, MediaLog* media_log) : level_(level), media_log_(media_log) { DCHECK(media_log_); } -LogHelper::LogHelper(MediaLog::MediaLogLevel level, +LogHelper::LogHelper(MediaLogMessageLevel level, const std::unique_ptr<MediaLog>& media_log) : LogHelper(level, media_log.get()) {} LogHelper::~LogHelper() { - media_log_->AddLogEvent(level_, stream_.str()); + media_log_->AddMessage(level_, stream_.str()); } } //namespace media diff --git a/chromium/media/base/media_log.h b/chromium/media/base/media_log.h index 25ea7c84176..957181676fd 100644 --- a/chromium/media/base/media_log.h +++ b/chromium/media/base/media_log.h @@ -19,8 +19,10 @@ #include "base/thread_annotations.h" #include "media/base/buffering_state.h" #include "media/base/media_export.h" -#include "media/base/media_log_event.h" +#include "media/base/media_log_events.h" +#include "media/base/media_log_message_levels.h" #include "media/base/media_log_properties.h" +#include "media/base/media_log_record.h" #include "media/base/pipeline_impl.h" #include "media/base/pipeline_status.h" #include "url/gurl.h" @@ -31,55 +33,53 @@ namespace media { // // To provide a logging implementation, derive from MediaLog instead. // -// Implementations only need to implement AddEventLocked(), which must be thread -// safe in the sense that it may be called from multiple threads, though it will -// not be called concurrently. See below for more details. +// Implementations only need to implement AddLogRecordLocked(), which must be +// thread safe in the sense that it may be called from multiple threads, though +// it will not be called concurrently. See below for more details. // // Implementations should also call InvalidateLog during destruction, to signal // to any child logs that the underlying log is no longer available. class MEDIA_EXPORT MediaLog { public: - enum MediaLogLevel { - // Fatal error, e.g. cause of playback failure. Since this is also used to - // form MediaError.message, do NOT use this for non-fatal errors to avoid - // contaminating MediaError.message. - MEDIALOG_ERROR, - - // Warning about non-fatal issues, e.g. quality of playback issues such as - // audio/video out of sync. - MEDIALOG_WARNING, - - // General info useful for Chromium and/or web developers, testers and even - // users, e.g. audio/video codecs used in a playback instance. - MEDIALOG_INFO, - - // Misc debug info for Chromium developers. - MEDIALOG_DEBUG, - }; - - // Convert various enums to strings. - static std::string MediaLogLevelToString(MediaLogLevel level); - static MediaLogEvent::Type MediaLogLevelToEventType(MediaLogLevel level); - static std::string EventTypeToString(MediaLogEvent::Type type); - - static std::string BufferingStateToString( - BufferingState state, - BufferingStateChangeReason reason = BUFFERING_CHANGE_REASON_UNKNOWN); - - static std::string MediaEventToLogString(const MediaLogEvent& event); - - // Returns a string usable as part of a MediaError.message, for only - // PIPELINE_ERROR or MEDIA_ERROR_LOG_ENTRY events, with any newlines replaced - // with whitespace in the latter kind of events. - static std::string MediaEventToMessageString(const MediaLogEvent& event); + static const char kEventKey[]; + static const char kStatusText[]; // Constructor is protected, see below. - virtual ~MediaLog(); - // Add an event to this log. Inheritors should override AddEventLocked to - // do something. - void AddEvent(std::unique_ptr<MediaLogEvent> event); + // Report a log message at the specified log level. + void AddMessage(MediaLogMessageLevel level, std::string message); + + // Typechecked property setter, since all properties must take values. + // For example, MediaLogProperty::kResolution supports only gfx::Size as + // an argument (see media_log_properties.h for this), so calling + // media_log->SetProperty<MediaLogProperty::kResolution>(1); + // would lead to a compile error, while + // gfx::Size rect = {100, 100}; + // media_log->SetProperty<MediaLogProperty::kResolution>(rect); + // is correct. + template <MediaLogProperty P, typename T> + void SetProperty(const T& value) { + AddLogRecord(CreatePropertyRecord<P, T>(value)); + } + + // TODO(tmathmeyer) add the ability to report events with a separated + // start and end time. + // Send an event to the media log that may or may not have attached data. + // For example, MediaLogEvent::kPlay takes no arguments, while + // MediaLogEvent::kSeek takes a double as an argument, representing the time. + // A proper way to add either of these events would be + // media_log->AddEvent<MediaLogEvent::kPlay>(); + // media_log->AddEvent<MediaLogEvent::kSeek>(1.99); + template <MediaLogEvent E, typename... T> + void AddEvent(const T&... value) { + std::unique_ptr<MediaLogRecord> record = CreateEventRecord<E, T...>(); + MediaLogEventTypeSupport<E, T...>::AddExtraData(&record->params, value...); + AddLogRecord(std::move(record)); + } + + // TODO(tmathmeyer) replace with Status when that's ready. + void NotifyError(PipelineStatus status); // Notify the media log that the player is destroyed. Some implementations // will want to change event handling based on this. @@ -91,50 +91,18 @@ class MEDIA_EXPORT MediaLog { // Note: The base class definition only produces empty messages. See // RenderMediaLog for where this method is meaningful. // Inheritors should override GetErrorMessageLocked(). + // TODO(tmathmeyer) Use a media::Status when that is ready. std::string GetErrorMessage(); - // Helper methods to create events and their parameters. - std::unique_ptr<MediaLogEvent> CreateEvent(MediaLogEvent::Type type); - std::unique_ptr<MediaLogEvent> CreateBooleanEvent(MediaLogEvent::Type type, - const std::string& property, - bool value); - std::unique_ptr<MediaLogEvent> CreateCreatedEvent( - const std::string& origin_url); - std::unique_ptr<MediaLogEvent> CreateStringEvent(MediaLogEvent::Type type, - const std::string& property, - const std::string& value); - std::unique_ptr<MediaLogEvent> CreateTimeEvent(MediaLogEvent::Type type, - const std::string& property, - base::TimeDelta value); - std::unique_ptr<MediaLogEvent> CreateTimeEvent(MediaLogEvent::Type type, - const std::string& property, - double value); - std::unique_ptr<MediaLogEvent> CreateLoadEvent(const std::string& url); - std::unique_ptr<MediaLogEvent> CreatePipelineStateChangedEvent( - PipelineImpl::State state); - std::unique_ptr<MediaLogEvent> CreatePipelineErrorEvent(PipelineStatus error); - std::unique_ptr<MediaLogEvent> CreateVideoSizeSetEvent(size_t width, - size_t height); - std::unique_ptr<MediaLogEvent> CreateBufferingStateChangedEvent( - const std::string& property, - BufferingState state, - BufferingStateChangeReason reason); - - // Report a log message at the specified log level. - void AddLogEvent(MediaLogLevel level, const std::string& message); - - // Only way to access the MediaLogEvent::PROPERTY_CHANGE event type, - // so that parameter types can be checked from media_log_properties.h. - template <MediaLogProperty P, typename T> - void SetProperty(const T& value) { - AddEvent(CreatePropertyEvent<P, T>(value)); - } - - // Getter for |id_|. Used by MojoMediaLogService to construct MediaLogEvents + // Getter for |id_|. Used by MojoMediaLogService to construct MediaLogRecords // to log into this MediaLog. Also used in trace events to associate each // event with a specific media playback. int32_t id() const { return id_; } + // Add a record to this log. Inheritors should override AddLogRecordLocked to + // do something. This needs to be public for MojoMediaLogService to use it. + void AddLogRecord(std::unique_ptr<MediaLogRecord> event); + // Provide a MediaLog which can have a separate lifetime from this one, but // still write to the same log. It is not guaranteed that this will log // forever; it might start silently discarding log messages if the original @@ -150,21 +118,29 @@ class MEDIA_EXPORT MediaLog { // any other thread, and with any parent log invalidation. // // Please see the documentation for the corresponding public methods. - virtual void AddEventLocked(std::unique_ptr<MediaLogEvent> event); + virtual void AddLogRecordLocked(std::unique_ptr<MediaLogRecord> event); virtual void OnWebMediaPlayerDestroyedLocked(); virtual std::string GetErrorMessageLocked(); // MockMediaLog also needs to call this method. template <MediaLogProperty P, typename T> - std::unique_ptr<MediaLogEvent> CreatePropertyEvent(const T& value) { - auto event = CreateEvent(MediaLogEvent::PROPERTY_CHANGE); - event->params.SetKey(MediaLogPropertyKeyToString(P), - MediaLogPropertyTypeSupport<P, T>::Convert(value)); - return event; + std::unique_ptr<MediaLogRecord> CreatePropertyRecord(const T& value) { + auto record = CreateRecord(MediaLogRecord::Type::kMediaPropertyChange); + record->params.SetKey(MediaLogPropertyKeyToString(P), + MediaLogPropertyTypeSupport<P, T>::Convert(value)); + return record; + } + template <MediaLogEvent E, typename... Opt> + std::unique_ptr<MediaLogRecord> CreateEventRecord() { + std::unique_ptr<MediaLogRecord> record( + CreateRecord(MediaLogRecord::Type::kMediaEventTriggered)); + record->params.SetString(MediaLog::kEventKey, + MediaLogEventTypeSupport<E, Opt...>::TypeName()); + return record; } // Notify all child logs that they should stop working. This should be called - // to guarantee that no further calls into AddEvent should be allowed. + // to guarantee that no further calls into AddLogRecord should be allowed. // Further, since calls into this log may happen on any thread, it's important // to call this while the log is still in working order. For example, calling // it immediately during destruction is a good idea. @@ -195,6 +171,9 @@ class MEDIA_EXPORT MediaLog { FRIEND_TEST_ALL_PREFIXES(MediaLogTest, EventsAreForwarded); FRIEND_TEST_ALL_PREFIXES(MediaLogTest, EventsAreNotForwardedAfterInvalidate); + // Helper methods to create events and their parameters. + std::unique_ptr<MediaLogRecord> CreateRecord(MediaLogRecord::Type type); + enum : size_t { // Max length of URLs in Created/Load events. Exceeding triggers truncation. kMaxUrlLength = 1000, @@ -216,15 +195,15 @@ class MEDIA_EXPORT MediaLog { // Helper class to make it easier to use MediaLog like DVLOG(). class MEDIA_EXPORT LogHelper { public: - LogHelper(MediaLog::MediaLogLevel level, MediaLog* media_log); - LogHelper(MediaLog::MediaLogLevel level, + LogHelper(MediaLogMessageLevel level, MediaLog* media_log); + LogHelper(MediaLogMessageLevel level, const std::unique_ptr<MediaLog>& media_log); ~LogHelper(); std::ostream& stream() { return stream_; } private: - const MediaLog::MediaLogLevel level_; + const MediaLogMessageLevel level_; MediaLog* const media_log_; std::stringstream stream_; }; @@ -232,7 +211,7 @@ class MEDIA_EXPORT LogHelper { // Provides a stringstream to collect a log entry to pass to the provided // MediaLog at the requested level. #define MEDIA_LOG(level, media_log) \ - LogHelper((MediaLog::MEDIALOG_##level), (media_log)).stream() + LogHelper((MediaLogMessageLevel::k##level), (media_log)).stream() // Logs only while |count| < |max|, increments |count| for each log, and warns // in the log if |count| has just reached |max|. diff --git a/chromium/media/base/media_log_event.h b/chromium/media/base/media_log_event.h deleted file mode 100644 index 96d3e364503..00000000000 --- a/chromium/media/base/media_log_event.h +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MEDIA_BASE_MEDIA_LOG_EVENT_H_ -#define MEDIA_BASE_MEDIA_LOG_EVENT_H_ - -#include <stdint.h> -#include <memory> - -#include "base/time/time.h" -#include "base/values.h" - -namespace media { - -struct MediaLogEvent { - MediaLogEvent() {} - - MediaLogEvent(const MediaLogEvent& event) { *this = event; } - - MediaLogEvent& operator=(const MediaLogEvent& event) { - id = event.id; - type = event.type; - std::unique_ptr<base::DictionaryValue> event_copy(event.params.DeepCopy()); - params.Swap(event_copy.get()); - time = event.time; - return *this; - } - - enum Type { - // A WebMediaPlayer is being created or destroyed. - // params: none. - WEBMEDIAPLAYER_CREATED, - WEBMEDIAPLAYER_DESTROYED, - - // A media player is loading a resource. - // params: "url": <URL of the resource>. - LOAD, - - // A media player has started seeking. - // params: "seek_target": <number of seconds to which to seek>. - SEEK, - - // A media player has been told to play or pause. - // params: none. - PLAY, - PAUSE, - - // The state of Pipeline has changed. - // params: "pipeline_state": <string name of the state>. - PIPELINE_STATE_CHANGED, - - // An error has occurred in the pipeline. - // params: "pipeline_error": <integral PipelineStatus error code>. - PIPELINE_ERROR, - - // The size of the video has been determined. - // params: "width": <integral width of the video>. - // "height": <integral height of the video>. - VIDEO_SIZE_SET, - - // A property of the pipeline has been set by a filter. - // These take a single parameter based upon the name of the event and of - // the appropriate type. e.g. DURATION_SET: "duration" of type TimeDelta. - DURATION_SET, - - // Audio/Video stream playback has ended. - ENDED, - - // Text stream playback has ended. - TEXT_ENDED, - - // Error log reported by media code such as reasons of playback error. - MEDIA_ERROR_LOG_ENTRY, - // params: "error": Error string describing the error detected. - - // Warning log reported by media code such as playback quality issues. - MEDIA_WARNING_LOG_ENTRY, - // params: "warning": String describing the warning. - - // Informative log reported by media code. - MEDIA_INFO_LOG_ENTRY, - // params: "info": String with details of an informative log entry. - - // Debug log reported by media code. - MEDIA_DEBUG_LOG_ENTRY, - // params: "debug": String with details of a debug log entry. - - // A property has changed without any special event occurring. - PROPERTY_CHANGE, - - // A change to any demuxer or pipeline buffer state. - BUFFERING_STATE_CHANGE, - - // Issued when a player is suspended. - SUSPENDED, - - TYPE_LAST = SUSPENDED - }; - - int32_t id; - Type type; - base::DictionaryValue params; - base::TimeTicks time; -}; - -} // namespace media - -#endif // MEDIA_BASE_MEDIA_LOG_EVENT_H_ diff --git a/chromium/media/base/media_log_events.cc b/chromium/media/base/media_log_events.cc new file mode 100644 index 00000000000..187ddcc9b5e --- /dev/null +++ b/chromium/media/base/media_log_events.cc @@ -0,0 +1,44 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/base/media_log_events.h" + +#include <string> + +#include "base/logging.h" + +namespace media { + +std::string MediaLogEventToString(MediaLogEvent level) { + switch (level) { + case MediaLogEvent::kPlay: + return "PLAY"; + case MediaLogEvent::kPause: + return "PAUSE"; + case MediaLogEvent::kSeek: + return "SEEK"; + case MediaLogEvent::kPipelineStateChange: + return "PIPELINE_STATE_CHANGED"; + case MediaLogEvent::kWebMediaPlayerCreated: + return "WEBMEDIAPLAYER_CREATED"; + case MediaLogEvent::kWebMediaPlayerDestroyed: + return "WEBMEDIAPLAYER_DESTROYED"; + case MediaLogEvent::kLoad: + return "LOAD"; + case MediaLogEvent::kVideoSizeChanged: + return "VIDEO_SIZE_SET"; + case MediaLogEvent::kDurationChanged: + return "DURATION_SET"; + case MediaLogEvent::kEnded: + return "ENDED"; + case MediaLogEvent::kBufferingStateChanged: + return "BUFFERING_STATE_CHANGE"; + case MediaLogEvent::kSuspended: + return "SUSPENDED"; + } + NOTREACHED(); + return ""; +} + +} // namespace media diff --git a/chromium/media/base/media_log_events.h b/chromium/media/base/media_log_events.h new file mode 100644 index 00000000000..eed7a29725b --- /dev/null +++ b/chromium/media/base/media_log_events.h @@ -0,0 +1,103 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEDIA_BASE_MEDIA_LOG_EVENTS_H_ +#define MEDIA_BASE_MEDIA_LOG_EVENTS_H_ + +#include <string> +#include "media/base/media_export.h" +#include "media/base/media_log_type_enforcement.h" +#include "ui/gfx/geometry/size.h" + +namespace media { + +// Events are changes in the state of a player, or a user interaction, or any +// other internal representation of a player at a given point in time. +// This list contains both events that are instant, such as play/pause, as +// well as events that span ranges of time, such as waiting for more data +// from the network, or decoding a video frame. +enum class MediaLogEvent { + // The media player has started playing. + kPlay, + + // The media player has entered a paused state. + kPause, + + // The media player has _started_ a seek operation. + kSeek, + + // The pipeline state has changed - see PipelineStatus in + // media/base/pipeline_status.h + kPipelineStateChange, + + // The media stack implementation of the blink media player has been created + // but may not be fully initialized. + kWebMediaPlayerCreated, + + // The media player has been destroyed and the log will soon die. No events + // can come after receiving this one. + kWebMediaPlayerDestroyed, + + // A web request has finished and the pipeline will start iminently. + kLoad, + + // The video size has changed. + // TODO(tmathmeyer) This is already a property, it might be useless to have it + // be an event too. consider removing it. + kVideoSizeChanged, + + // The runtime of the video was changed by the demuxer. + kDurationChanged, + + // There is no more content to consume. + kEnded, + + // There was a change to the buffering state of the video. This can be caused + // by either network slowness or decoding slowness. See the comments in + // media/base/buffering_state.h for more information. + kBufferingStateChanged, + + // The player has been suspended to save resources. + kSuspended, +}; + +// This has to be declared before the macros use it - Some infra code relies on +// the enum names to be UPPER_CASE, so this will convert them manually +// instead of using macro stringification. +MEDIA_EXPORT std::string MediaLogEventToString(MediaLogEvent level); + +// These events can be triggered with no extra associated data. +MEDIA_LOG_EVENT_TYPELESS(kPlay); +MEDIA_LOG_EVENT_TYPELESS(kPause); +MEDIA_LOG_EVENT_TYPELESS(kWebMediaPlayerDestroyed); +MEDIA_LOG_EVENT_TYPELESS(kEnded); +MEDIA_LOG_EVENT_TYPELESS(kSuspended); +MEDIA_LOG_EVENT_TYPELESS(kWebMediaPlayerCreated); + +// These events can be triggered with the extra data / names as defined here. +// Note that some events can be defined multiple times. +MEDIA_LOG_EVENT_NAMED_DATA(kLoad, std::string, "url"); +MEDIA_LOG_EVENT_NAMED_DATA(kSeek, double, "seek_target"); +MEDIA_LOG_EVENT_NAMED_DATA(kVideoSizeChanged, gfx::Size, "dimensions"); +MEDIA_LOG_EVENT_NAMED_DATA(kDurationChanged, base::TimeDelta, "duration"); +MEDIA_LOG_EVENT_NAMED_DATA(kWebMediaPlayerCreated, std::string, "origin_url"); +MEDIA_LOG_EVENT_NAMED_DATA(kPipelineStateChange, std::string, "pipeline_state"); + +// Each type of buffering state gets a different name. +MEDIA_LOG_EVENT_NAMED_DATA( + kBufferingStateChanged, + SerializableBufferingState<SerializableBufferingStateType::kVideo>, + "video_buffering_state"); +MEDIA_LOG_EVENT_NAMED_DATA( + kBufferingStateChanged, + SerializableBufferingState<SerializableBufferingStateType::kAudio>, + "audio_buffering_state"); +MEDIA_LOG_EVENT_NAMED_DATA( + kBufferingStateChanged, + SerializableBufferingState<SerializableBufferingStateType::kPipeline>, + "pipeline_buffering_state"); + +} // namespace media + +#endif // MEDIA_BASE_MEDIA_LOG_EVENTS_H_ diff --git a/chromium/media/base/media_log_message_levels.cc b/chromium/media/base/media_log_message_levels.cc new file mode 100644 index 00000000000..bea3f563008 --- /dev/null +++ b/chromium/media/base/media_log_message_levels.cc @@ -0,0 +1,28 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/base/media_log_message_levels.h" + +#include <string> + +#include "base/logging.h" + +namespace media { + +std::string MediaLogMessageLevelToString(MediaLogMessageLevel level) { + switch (level) { + case MediaLogMessageLevel::kERROR: + return "error"; + case MediaLogMessageLevel::kWARNING: + return "warning"; + case MediaLogMessageLevel::kINFO: + return "info"; + case MediaLogMessageLevel::kDEBUG: + return "debug"; + } + NOTREACHED(); + return ""; +} + +} // namespace media
\ No newline at end of file diff --git a/chromium/media/base/media_log_message_levels.h b/chromium/media/base/media_log_message_levels.h new file mode 100644 index 00000000000..d79df67db10 --- /dev/null +++ b/chromium/media/base/media_log_message_levels.h @@ -0,0 +1,28 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEDIA_BASE_MEDIA_LOG_MESSAGE_LEVELS_H_ +#define MEDIA_BASE_MEDIA_LOG_MESSAGE_LEVELS_H_ + +#include <string> + +#include "media/base/media_export.h" + +namespace media { + +// TODO(tmathmeyer) Find a nice way to make this use the kCamelCase style, while +// still preserving the "MEDIA_LOG(ERROR, ...)" syntax. macros are bad :( +enum class MediaLogMessageLevel { + kERROR, + kWARNING, + kINFO, + kDEBUG, +}; + +MEDIA_EXPORT std::string MediaLogMessageLevelToString( + MediaLogMessageLevel level); + +} // namespace media + +#endif // MEDIA_BASE_MEDIA_LOG_MESSAGE_LEVELS_H_ diff --git a/chromium/media/base/media_log_properties.h b/chromium/media/base/media_log_properties.h index 1c897915a08..f5283195094 100644 --- a/chromium/media/base/media_log_properties.h +++ b/chromium/media/base/media_log_properties.h @@ -6,11 +6,11 @@ #define MEDIA_BASE_MEDIA_LOG_PROPERTIES_H_ #include <string> -#include <utility> +#include <vector> #include "media/base/audio_decoder_config.h" #include "media/base/media_export.h" -#include "media/base/media_log_properties_helper.h" +#include "media/base/media_log_type_enforcement.h" #include "media/base/video_decoder_config.h" #include "ui/gfx/geometry/size.h" diff --git a/chromium/media/base/media_log_properties_helper.h b/chromium/media/base/media_log_properties_helper.h deleted file mode 100644 index 95ff70a82be..00000000000 --- a/chromium/media/base/media_log_properties_helper.h +++ /dev/null @@ -1,182 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MEDIA_BASE_MEDIA_LOG_PROPERTIES_HELPER_H_ -#define MEDIA_BASE_MEDIA_LOG_PROPERTIES_HELPER_H_ - -#include <string> -#include <vector> - -#include "base/strings/stringprintf.h" -#include "base/values.h" -#include "media/base/audio_decoder_config.h" -#include "media/base/video_decoder_config.h" -#include "ui/gfx/geometry/size.h" - -namespace media { - -namespace internal { - -// Converter struct. -template <typename T> -struct MediaLogPropertyTypeConverter {}; - -// Some types can be passed to the base::Value constructor. -#define _VALUE_CONSTRUCTOR_TYPE(TEMPLATE_TYPE, PARAM_TYPE) \ - template <> \ - struct MediaLogPropertyTypeConverter<TEMPLATE_TYPE> { \ - static base::Value Convert(PARAM_TYPE value) { \ - return base::Value(value); \ - } \ - } - -_VALUE_CONSTRUCTOR_TYPE(std::string, const std::string&); -_VALUE_CONSTRUCTOR_TYPE(bool, bool); -_VALUE_CONSTRUCTOR_TYPE(int, int); -#undef _VALUE_CONSTRUCTOR_TYPE - -// Can't send non-finite double values to a base::Value. -template <> -struct MediaLogPropertyTypeConverter<double> { - static base::Value Convert(double value) { - return std::isfinite(value) ? base::Value(value) : base::Value("unknown"); - } -}; - -// Just upcast this to get the NaN check. -template <> -struct MediaLogPropertyTypeConverter<float> { - static base::Value Convert(float value) { - return MediaLogPropertyTypeConverter<double>::Convert(value); - } -}; - -/* Support serializing for a selection of types */ -// support 64 bit ints, this is a weird workaround for the base::Value -// int type only being 32 bit, as specified in the base/values.h header comment. -template <> -struct MediaLogPropertyTypeConverter<int64_t> { - static base::Value Convert(int64_t value) { - return base::Value(static_cast<double>(value)); - } -}; - -// Support gfx::Size -> "{x}x{y}" -template <> -struct MediaLogPropertyTypeConverter<gfx::Size> { - static base::Value Convert(const gfx::Size& value) { - return base::Value(value.ToString()); - } -}; - -// Support vectors of anything else thiat can be serialized -template <typename T> -struct MediaLogPropertyTypeConverter<std::vector<T>> { - static base::Value Convert(const std::vector<T>& value) { - base::Value result(base::Value::Type::LIST); - for (const auto& entry : value) - result.Append(MediaLogPropertyTypeConverter<T>::Convert(entry)); - return result; - } -}; - -// Specializer for sending AudioDecoderConfigs to the media tab in devtools. -template <> -struct internal::MediaLogPropertyTypeConverter<media::AudioDecoderConfig> { - static base::Value Convert(const AudioDecoderConfig& value) { - base::Value result(base::Value::Type::DICTIONARY); - result.SetStringKey("codec", GetCodecName(value.codec())); - result.SetIntKey("bytes per channel", value.bytes_per_channel()); - result.SetStringKey("channel layout", - ChannelLayoutToString(value.channel_layout())); - result.SetIntKey("channels", value.channels()); - result.SetIntKey("samples per second", value.samples_per_second()); - result.SetStringKey("sample format", - SampleFormatToString(value.sample_format())); - result.SetIntKey("bytes per frame", value.bytes_per_frame()); - // TODO(tmathmeyer) drop the units, let the frontend handle it. - // use ostringstreams because windows & linux have _different types_ - // defined for int64_t, (long vs long long) so format specifiers dont work. - std::ostringstream preroll; - preroll << value.seek_preroll().InMicroseconds() << "us"; - result.SetStringKey("seek preroll", preroll.str()); - result.SetIntKey("codec delay", value.codec_delay()); - result.SetBoolKey("has extra data", !value.extra_data().empty()); - std::ostringstream encryptionSchemeString; - encryptionSchemeString << value.encryption_scheme(); - result.SetStringKey("encryption scheme", encryptionSchemeString.str()); - result.SetBoolKey("discard decoder delay", - value.should_discard_decoder_delay()); - return result; - } -}; - -// Specializer for sending VideoDecoderConfigs to the media tab in devtools. -template <> -struct internal::MediaLogPropertyTypeConverter<VideoDecoderConfig> { - static base::Value Convert(const VideoDecoderConfig& value) { - base::Value result(base::Value::Type::DICTIONARY); - result.SetStringKey("codec", GetCodecName(value.codec())); - result.SetStringKey("profile", GetProfileName(value.profile())); - result.SetStringKey( - "alpha mode", - (value.alpha_mode() == VideoDecoderConfig::AlphaMode::kHasAlpha - ? "has_alpha" - : "is_opaque")); - result.SetStringKey("coded size", value.coded_size().ToString()); - result.SetStringKey("visible rect", value.visible_rect().ToString()); - result.SetStringKey("natural size", value.natural_size().ToString()); - result.SetBoolKey("has_extra_data", !value.extra_data().empty()); - std::ostringstream encryptionSchemeString; - encryptionSchemeString << value.encryption_scheme(); - result.SetStringKey("encryption scheme", encryptionSchemeString.str()); - result.SetStringKey("rotation", VideoRotationToString( - value.video_transformation().rotation)); - result.SetBoolKey("flipped", value.video_transformation().mirrored); - result.SetStringKey("color space", - value.color_space_info().ToGfxColorSpace().ToString()); - - if (value.hdr_metadata().has_value()) { - result.SetKey( - "luminance range", - MediaLogPropertyTypeConverter<float>::Convert( - value.hdr_metadata()->mastering_metadata.luminance_min)); - result.SetStringKey( - "primaries", - base::StringPrintf( - "[r:%.4f,%.4f, g:%.4f,%.4f, b:%.4f,%.4f, wp:%.4f,%.4f]", - value.hdr_metadata()->mastering_metadata.primary_r.x(), - value.hdr_metadata()->mastering_metadata.primary_r.y(), - value.hdr_metadata()->mastering_metadata.primary_g.x(), - value.hdr_metadata()->mastering_metadata.primary_g.y(), - value.hdr_metadata()->mastering_metadata.primary_b.x(), - value.hdr_metadata()->mastering_metadata.primary_b.y(), - value.hdr_metadata()->mastering_metadata.white_point.x(), - value.hdr_metadata()->mastering_metadata.white_point.y())); - } - return result; - } -}; - -} // namespace internal - -// Forward declare the enum. -enum class MediaLogProperty; - -// Allow only specific types for an individual property. -template <MediaLogProperty PROP, typename T> -struct MediaLogPropertyTypeSupport {}; - -// Lets us define the supported type in a single line in media_log_properties.h. -#define MEDIA_LOG_PROPERTY_SUPPORTS_TYPE(PROPERTY, TYPE) \ - template <> \ - struct MediaLogPropertyTypeSupport<MediaLogProperty::PROPERTY, TYPE> { \ - static base::Value Convert(const TYPE& type) { \ - return internal::MediaLogPropertyTypeConverter<TYPE>::Convert(type); \ - } \ - } - -} // namespace media - -#endif // MEDIA_BASE_MEDIA_LOG_PROPERTIES_HELPER_H_ diff --git a/chromium/media/base/media_log_record.h b/chromium/media/base/media_log_record.h new file mode 100644 index 00000000000..76822114c9e --- /dev/null +++ b/chromium/media/base/media_log_record.h @@ -0,0 +1,54 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEDIA_BASE_MEDIA_LOG_RECORD_H_ +#define MEDIA_BASE_MEDIA_LOG_RECORD_H_ + +#include <stdint.h> +#include <memory> + +#include "base/time/time.h" +#include "base/values.h" + +namespace media { + +struct MediaLogRecord { + MediaLogRecord() {} + + MediaLogRecord(const MediaLogRecord& event) { *this = event; } + + MediaLogRecord& operator=(const MediaLogRecord& event) { + id = event.id; + type = event.type; + std::unique_ptr<base::DictionaryValue> event_copy(event.params.DeepCopy()); + params.Swap(event_copy.get()); + time = event.time; + return *this; + } + + enum class Type { + // See media/base/media_log_message_levels.h for info. + kMessage, + + // See media/base/media_log_properties.h for info. + kMediaPropertyChange, + + // See media/base/media_log_events.h for info. + kMediaEventTriggered, + + // TODO(tmathmeyer) use media::Status eventually instead of PipelineStatus + kMediaStatus, + + kMaxValue = kMediaStatus, + }; + + int32_t id; + Type type; + base::DictionaryValue params; + base::TimeTicks time; +}; + +} // namespace media + +#endif // MEDIA_BASE_MEDIA_LOG_RECORD_H_ diff --git a/chromium/media/base/media_log_type_enforcement.h b/chromium/media/base/media_log_type_enforcement.h new file mode 100644 index 00000000000..8404e00a23f --- /dev/null +++ b/chromium/media/base/media_log_type_enforcement.h @@ -0,0 +1,59 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEDIA_BASE_MEDIA_LOG_TYPE_ENFORCEMENT_H_ +#define MEDIA_BASE_MEDIA_LOG_TYPE_ENFORCEMENT_H_ + +#include "media/base/media_serializers.h" + +namespace media { + +namespace internal { +enum class UnmatchableType {}; +} // namespace internal + +// Forward declare the enums. +enum class MediaLogProperty; +enum class MediaLogEvent; + +// Allow only specific types for an individual property. +template <MediaLogProperty PROP, typename T> +struct MediaLogPropertyTypeSupport {}; + +// Allow only specific types for an individual event. +// However unlike Property, T is not required, so we default it to some +// unmatchable type that will never be passed as an argument accidentally. +template <MediaLogEvent EVENT, typename T = internal::UnmatchableType> +struct MediaLogEventTypeSupport {}; + +// Lets us define the supported type in a single line in media_log_properties.h. +#define MEDIA_LOG_PROPERTY_SUPPORTS_TYPE(PROPERTY, TYPE) \ + template <> \ + struct MediaLogPropertyTypeSupport<MediaLogProperty::PROPERTY, TYPE> { \ + static base::Value Convert(const TYPE& type) { \ + return MediaSerialize<TYPE>(type); \ + } \ + } + +#define MEDIA_LOG_EVENT_NAMED_DATA(EVENT, TYPE, DISPLAY) \ + template <> \ + struct MediaLogEventTypeSupport<MediaLogEvent::EVENT, TYPE> { \ + static void AddExtraData(base::Value* params, const TYPE& t) { \ + DCHECK(params); \ + params->SetKey(DISPLAY, MediaSerialize<TYPE>(t)); \ + } \ + static std::string TypeName() { return #EVENT; } \ + } + +// Specifically do not create the Convert or DisplayName methods +#define MEDIA_LOG_EVENT_TYPELESS(EVENT) \ + template <> \ + struct MediaLogEventTypeSupport<MediaLogEvent::EVENT> { \ + static std::string TypeName() { return #EVENT; } \ + static void AddExtraData(base::Value* params) {} \ + } + +} // namespace media + +#endif // MEDIA_BASE_MEDIA_LOG_TYPE_ENFORCEMENT_H_ diff --git a/chromium/media/base/media_log_unittest.cc b/chromium/media/base/media_log_unittest.cc index 741806b8c55..09a5126bca3 100644 --- a/chromium/media/base/media_log_unittest.cc +++ b/chromium/media/base/media_log_unittest.cc @@ -25,69 +25,13 @@ class MediaLogTest : public testing::Test { constexpr size_t MediaLogTest::kMaxUrlLength; -TEST_F(MediaLogTest, DontTruncateShortUrlString) { - const std::string short_url("chromium.org"); - EXPECT_LT(short_url.length(), MediaLogTest::kMaxUrlLength); - - // Verify that CreatedEvent does not truncate the short URL. - std::unique_ptr<MediaLogEvent> created_event = - media_log.CreateCreatedEvent(short_url); - std::string stored_url; - created_event->params.GetString("origin_url", &stored_url); - EXPECT_EQ(stored_url, short_url); - - // Verify that LoadEvent does not truncate the short URL. - std::unique_ptr<MediaLogEvent> load_event = - media_log.CreateLoadEvent(short_url); - load_event->params.GetString("url", &stored_url); - EXPECT_EQ(stored_url, short_url); -} - -TEST_F(MediaLogTest, TruncateLongUrlStrings) { - // Build a long string that exceeds the URL length limit. - std::stringstream string_builder; - constexpr size_t kLongStringLength = MediaLogTest::kMaxUrlLength + 10; - for (size_t i = 0; i < kLongStringLength; i++) { - string_builder << "c"; - } - const std::string long_url = string_builder.str(); - EXPECT_GT(long_url.length(), MediaLogTest::kMaxUrlLength); - - // Verify that long CreatedEvent URL... - std::unique_ptr<MediaLogEvent> created_event = - media_log.CreateCreatedEvent(long_url); - std::string stored_url; - created_event->params.GetString("origin_url", &stored_url); - - // ... is truncated - EXPECT_EQ(stored_url.length(), MediaLogTest::kMaxUrlLength); - // ... ends with ellipsis - EXPECT_EQ(stored_url.compare(MediaLogTest::kMaxUrlLength - 3, 3, "..."), 0); - // ... is otherwise a substring of the longer URL - EXPECT_EQ(stored_url.compare(0, MediaLogTest::kMaxUrlLength - 3, long_url, 0, - MediaLogTest::kMaxUrlLength - 3), - 0); - - // Verify that long LoadEvent URL... - std::unique_ptr<MediaLogEvent> load_event = - media_log.CreateCreatedEvent(long_url); - load_event->params.GetString("url", &stored_url); - // ... is truncated - EXPECT_EQ(stored_url.length(), MediaLogTest::kMaxUrlLength); - // ... ends with ellipsis - EXPECT_EQ(stored_url.compare(MediaLogTest::kMaxUrlLength - 3, 3, "..."), 0); - // ... is otherwise a substring of the longer URL - EXPECT_EQ(stored_url.compare(0, MediaLogTest::kMaxUrlLength - 3, long_url, 0, - MediaLogTest::kMaxUrlLength - 3), - 0); -} TEST_F(MediaLogTest, EventsAreForwarded) { // Make sure that |root_log_| receives events. std::unique_ptr<MockMediaLog> root_log(std::make_unique<MockMediaLog>()); std::unique_ptr<MediaLog> child_media_log(root_log->Clone()); - EXPECT_CALL(*root_log, DoAddEventLogString(_)).Times(1); - child_media_log->AddLogEvent(MediaLog::MediaLogLevel::MEDIALOG_ERROR, "test"); + EXPECT_CALL(*root_log, DoAddLogRecordLogString(_)).Times(1); + child_media_log->AddMessage(MediaLogMessageLevel::kERROR, "test"); } TEST_F(MediaLogTest, EventsAreNotForwardedAfterInvalidate) { @@ -95,9 +39,9 @@ TEST_F(MediaLogTest, EventsAreNotForwardedAfterInvalidate) { // underlying log. std::unique_ptr<MockMediaLog> root_log(std::make_unique<MockMediaLog>()); std::unique_ptr<MediaLog> child_media_log(root_log->Clone()); - EXPECT_CALL(*root_log, DoAddEventLogString(_)).Times(0); + EXPECT_CALL(*root_log, DoAddLogRecordLogString(_)).Times(0); root_log.reset(); - child_media_log->AddLogEvent(MediaLog::MediaLogLevel::MEDIALOG_ERROR, "test"); + child_media_log->AddMessage(MediaLogMessageLevel::kERROR, "test"); } } // namespace media diff --git a/chromium/media/base/media_observer.h b/chromium/media/base/media_observer.h index 1bb629930e5..225cc8d7985 100644 --- a/chromium/media/base/media_observer.h +++ b/chromium/media/base/media_observer.h @@ -34,9 +34,6 @@ class MEDIA_EXPORT MediaObserverClient { // may be displayed to explain why the switch occurred. virtual void SwitchToLocalRenderer(ReasonToSwitchToLocal reason) = 0; - // Requests to activate monitoring changes on viewport intersection. - virtual void ActivateViewportIntersectionMonitoring(bool activate) = 0; - // Reports the latest compatibility state of the element's source for remote // playback. virtual void UpdateRemotePlaybackCompatibility(bool is_compatible) = 0; diff --git a/chromium/media/base/media_serializers.h b/chromium/media/base/media_serializers.h new file mode 100644 index 00000000000..6333c44170f --- /dev/null +++ b/chromium/media/base/media_serializers.h @@ -0,0 +1,392 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEDIA_BASE_MEDIA_SERIALIZERS_H_ +#define MEDIA_BASE_MEDIA_SERIALIZERS_H_ + +#include <vector> + +#include "base/location.h" +#include "base/strings/stringprintf.h" +#include "media/base/audio_decoder_config.h" +#include "media/base/buffering_state.h" +#include "media/base/media_serializers_base.h" +#include "media/base/status.h" +#include "media/base/status_codes.h" +#include "media/base/video_decoder_config.h" +#include "ui/gfx/geometry/size.h" + +namespace media { + +namespace internal { + +// Serializing any const or reference combination. +template <typename T> +struct MediaSerializer<const T> { + static base::Value Serialize(const T& value) { + return MediaSerializer<T>::Serialize(value); + } +}; + +template <typename T> +struct MediaSerializer<T&> { + static base::Value Serialize(const T& value) { + return MediaSerializer<T>::Serialize(value); + } +}; + +// Serialize default value. +template <> +struct MediaSerializer<base::Value> { + static base::Value Serialize(const base::Value& value) { + return value.Clone(); + } +}; + +// Serialize vectors of things +template <typename VecType> +struct MediaSerializer<std::vector<VecType>> { + static base::Value Serialize(const std::vector<VecType>& vec) { + base::Value result(base::Value::Type::LIST); + for (const VecType& value : vec) + result.Append(MediaSerializer<VecType>::Serialize(value)); + return result; + } +}; + +// serialize optional types +template <typename OptType> +struct MediaSerializer<base::Optional<OptType>> { + static base::Value Serialize(const base::Optional<OptType>& opt) { + return opt ? MediaSerializer<OptType>::Serialize(opt.value()) + : base::Value("unset"); // TODO(tmathmeyer) maybe empty string? + } +}; + +// Sometimes raw strings wont template match to a char*. +template <int len> +struct MediaSerializer<char[len]> { + static inline base::Value Serialize(const char* code) { + return base::Value(code); + } +}; + +// Can't send non-finite double values to a base::Value. +template <> +struct MediaSerializer<double> { + static inline base::Value Serialize(double value) { + return std::isfinite(value) ? base::Value(value) : base::Value("unknown"); + } +}; + +template <> +struct MediaSerializer<int64_t> { + static inline base::Value Serialize(int64_t value) { + return MediaSerializer<double>::Serialize(static_cast<double>(value)); + } +}; + +// Just upcast this to get the NaN check. +template <> +struct MediaSerializer<float> { + static inline base::Value Serialize(float value) { + return MediaSerializer<double>::Serialize(value); + } +}; + +// Serialization for chromium-specific types. +// Each serializer should be commented like: +// Class/Enum (simple/complex) +// where Classes should take constref arguments, and "simple" methods should +// be declared inline. + +// the FIELD_SERIALIZE method can be used whenever the result is a dict named +// |result|. +#define FIELD_SERIALIZE(NAME, CONSTEXPR) \ + result.SetKey(NAME, MediaSerialize(CONSTEXPR)) + +// Class (simple) +template <> +struct MediaSerializer<gfx::Size> { + static inline base::Value Serialize(const gfx::Size& value) { + return base::Value(value.ToString()); + } +}; + +// Class (simple) +template <> +struct MediaSerializer<gfx::Rect> { + static inline base::Value Serialize(const gfx::Rect& value) { + return base::Value(value.ToString()); + } +}; + +// enum (simple) +template <> +struct MediaSerializer<base::TimeDelta> { + static inline base::Value Serialize(const base::TimeDelta value) { + return MediaSerializer<double>::Serialize(value.InSecondsF()); + } +}; + +// Enum (simple) +template <> +struct MediaSerializer<media::AudioCodec> { + static inline base::Value Serialize(media::AudioCodec value) { + return base::Value(GetCodecName(value)); + } +}; + +// Enum (simple) +template <> +struct MediaSerializer<media::AudioCodecProfile> { + static inline base::Value Serialize(media::AudioCodecProfile value) { + return base::Value(GetProfileName(value)); + } +}; + +// Enum (simple) +template <> +struct MediaSerializer<media::VideoCodec> { + static inline base::Value Serialize(media::VideoCodec value) { + return base::Value(GetCodecName(value)); + } +}; + +// Enum (simple) +template <> +struct MediaSerializer<media::VideoCodecProfile> { + static inline base::Value Serialize(media::VideoCodecProfile value) { + return base::Value(GetProfileName(value)); + } +}; + +// Enum (simple) +template <> +struct MediaSerializer<media::ChannelLayout> { + static inline base::Value Serialize(media::ChannelLayout value) { + return base::Value(ChannelLayoutToString(value)); + } +}; + +// Enum (simple) +template <> +struct MediaSerializer<media::SampleFormat> { + static inline base::Value Serialize(media::SampleFormat value) { + return base::Value(SampleFormatToString(value)); + } +}; + +// Enum (complex) +template <> +struct MediaSerializer<media::EncryptionScheme> { + static base::Value Serialize(const media::EncryptionScheme& value) { + std::ostringstream encryptionSchemeString; + encryptionSchemeString << value; + return base::Value(encryptionSchemeString.str()); + } +}; + +// Class (complex) +template <> +struct MediaSerializer<media::VideoTransformation> { + static base::Value Serialize(const media::VideoTransformation& value) { + std::string rotation = VideoRotationToString(value.rotation); + if (value.mirrored) + rotation += ", mirrored"; + return base::Value(rotation); + } +}; + +// Class (simple) +template <> +struct MediaSerializer<media::VideoColorSpace> { + static inline base::Value Serialize(const media::VideoColorSpace& value) { + return base::Value(value.ToGfxColorSpace().ToString()); + } +}; + +// Class (complex) +template <> +struct MediaSerializer<media::HDRMetadata> { + static base::Value Serialize(const media::HDRMetadata& value) { + // TODO(tmathmeyer) serialize more fields here potentially. + base::Value result(base::Value::Type::DICTIONARY); + FIELD_SERIALIZE("luminance range", + base::StringPrintf("%.2f => %.2f", + value.mastering_metadata.luminance_min, + value.mastering_metadata.luminance_max)); + FIELD_SERIALIZE("primaries", + base::StringPrintf( + "[r:%.4f,%.4f, g:%.4f,%.4f, b:%.4f,%.4f, wp:%.4f,%.4f]", + value.mastering_metadata.primary_r.x(), + value.mastering_metadata.primary_r.y(), + value.mastering_metadata.primary_g.x(), + value.mastering_metadata.primary_g.y(), + value.mastering_metadata.primary_b.x(), + value.mastering_metadata.primary_b.y(), + value.mastering_metadata.white_point.x(), + value.mastering_metadata.white_point.y())); + return result; + } +}; + +// Class (complex) +template <> +struct MediaSerializer<media::AudioDecoderConfig> { + static base::Value Serialize(const media::AudioDecoderConfig& value) { + base::Value result(base::Value::Type::DICTIONARY); + FIELD_SERIALIZE("codec", value.codec()); + FIELD_SERIALIZE("profile", value.profile()); + FIELD_SERIALIZE("bytes per channel", value.bytes_per_channel()); + FIELD_SERIALIZE("channel layout", value.channel_layout()); + FIELD_SERIALIZE("channels", value.channels()); + FIELD_SERIALIZE("samples per second", value.samples_per_second()); + FIELD_SERIALIZE("sample format", value.sample_format()); + FIELD_SERIALIZE("bytes per frame", value.bytes_per_frame()); + FIELD_SERIALIZE("codec delay", value.codec_delay()); + FIELD_SERIALIZE("has extra data", !value.extra_data().empty()); + FIELD_SERIALIZE("encryption scheme", value.encryption_scheme()); + FIELD_SERIALIZE("discard decoder delay", + value.should_discard_decoder_delay()); + + // TODO(tmathmeyer) drop the units, let the frontend handle it. + // use ostringstreams because windows & linux have _different types_ + // defined for int64_t, (long vs long long) so format specifiers dont work. + std::ostringstream preroll; + preroll << value.seek_preroll().InMicroseconds() << "us"; + result.SetStringKey("seek preroll", preroll.str()); + + return result; + } +}; + +// Enum (simple) +template <> +struct MediaSerializer<media::VideoDecoderConfig::AlphaMode> { + static inline base::Value Serialize( + media::VideoDecoderConfig::AlphaMode value) { + return base::Value(value == VideoDecoderConfig::AlphaMode::kHasAlpha + ? "has_alpha" + : "is_opaque"); + } +}; + +// Class (complex) +template <> +struct MediaSerializer<media::VideoDecoderConfig> { + static base::Value Serialize(const media::VideoDecoderConfig& value) { + base::Value result(base::Value::Type::DICTIONARY); + FIELD_SERIALIZE("codec", value.codec()); + FIELD_SERIALIZE("profile", value.profile()); + FIELD_SERIALIZE("alpha mode", value.alpha_mode()); + FIELD_SERIALIZE("coded size", value.coded_size()); + FIELD_SERIALIZE("visible rect", value.visible_rect()); + FIELD_SERIALIZE("natural size", value.natural_size()); + FIELD_SERIALIZE("has extra data", !value.extra_data().empty()); + FIELD_SERIALIZE("encryption scheme", value.encryption_scheme()); + FIELD_SERIALIZE("orientation", value.video_transformation()); + FIELD_SERIALIZE("color space", value.color_space_info()); + FIELD_SERIALIZE("hdr metadata", value.hdr_metadata()); + return result; + } +}; + +// enum (simple) +template <> +struct MediaSerializer<media::BufferingState> { + static inline base::Value Serialize(const media::BufferingState value) { + return base::Value(value == media::BufferingState::BUFFERING_HAVE_ENOUGH + ? "BUFFERING_HAVE_ENOUGH" + : "BUFFERING_HAVE_NOTHING"); + } +}; + +// enum (complex) +template <> +struct MediaSerializer<media::BufferingStateChangeReason> { + static base::Value Serialize(const media::BufferingStateChangeReason value) { + switch (value) { + case DEMUXER_UNDERFLOW: + return base::Value("DEMUXER_UNDERFLOW"); + case DECODER_UNDERFLOW: + return base::Value("DECODER_UNDERFLOW"); + case REMOTING_NETWORK_CONGESTION: + return base::Value("REMOTING_NETWORK_CONGESTION"); + case BUFFERING_CHANGE_REASON_UNKNOWN: + return base::Value("BUFFERING_CHANGE_REASON_UNKNOWN"); + } + } +}; + +// Class (complex) +template <media::SerializableBufferingStateType T> +struct MediaSerializer<media::SerializableBufferingState<T>> { + static base::Value Serialize( + const media::SerializableBufferingState<T>& value) { + base::Value result(base::Value::Type::DICTIONARY); + FIELD_SERIALIZE("state", value.state); + + switch (value.reason) { + case DEMUXER_UNDERFLOW: + case DECODER_UNDERFLOW: + case REMOTING_NETWORK_CONGESTION: + FIELD_SERIALIZE("reason", value.reason); + break; + + // Don't write anything here if the reason is unknown. + case BUFFERING_CHANGE_REASON_UNKNOWN: + break; + } + + if (T == SerializableBufferingStateType::kPipeline) + result.SetBoolKey("for_suspended_start", value.suspended_start); + + return result; + } +}; + +// enum (simple) +template <> +struct MediaSerializer<media::StatusCode> { + static inline base::Value Serialize(media::StatusCode code) { + return base::Value(static_cast<int>(code)); + } +}; + +// Class (complex) +template <> +struct MediaSerializer<media::Status> { + static base::Value Serialize(const media::Status& status) { + if (status.is_ok()) + return base::Value("Ok"); + + base::Value result(base::Value::Type::DICTIONARY); + FIELD_SERIALIZE("status_code", status.code()); + FIELD_SERIALIZE("status_message", status.message()); + FIELD_SERIALIZE("stack", status.data_->frames); + FIELD_SERIALIZE("data", status.data_->data); + FIELD_SERIALIZE("causes", status.data_->causes); + return result; + } +}; + +// Class (complex) +template <> +struct MediaSerializer<base::Location> { + static base::Value Serialize(const base::Location& value) { + base::Value result(base::Value::Type::DICTIONARY); + FIELD_SERIALIZE("file", value.file_name()); + FIELD_SERIALIZE("line", value.line_number()); + return result; + } +}; + +#undef FIELD_SERIALIZE + +} // namespace internal + +} // namespace media + +#endif // MEDIA_BASE_MEDIA_SERIALIZERS_H_ diff --git a/chromium/media/base/media_serializers_base.h b/chromium/media/base/media_serializers_base.h new file mode 100644 index 00000000000..8e1359b9841 --- /dev/null +++ b/chromium/media/base/media_serializers_base.h @@ -0,0 +1,34 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEDIA_BASE_MEDIA_SERIALIZERS_BASE_H_ +#define MEDIA_BASE_MEDIA_SERIALIZERS_BASE_H_ + +#include <vector> + +#include "base/values.h" +#include "media/base/media_export.h" + +namespace media { + +namespace internal { + +// Serializer specializer struct. +// All the types that base::Value's constructor can take should be passed +// by non-const values. (int, bool, std::string, char*, etc). +template <typename T> +struct MediaSerializer { + static inline base::Value Serialize(T value) { return base::Value(value); } +}; + +} // namespace internal + +template <typename T> +base::Value MediaSerialize(const T& t) { + return internal::MediaSerializer<T>::Serialize(t); +} + +} // namespace media + +#endif // MEDIA_BASE_MEDIA_SERIALIZERS_BASE_H_ diff --git a/chromium/media/base/media_serializers_unittest.cc b/chromium/media/base/media_serializers_unittest.cc new file mode 100644 index 00000000000..e3f3a7bfc5c --- /dev/null +++ b/chromium/media/base/media_serializers_unittest.cc @@ -0,0 +1,59 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/base/media_serializers.h" + +#include <memory> + +#include "base/json/json_writer.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace media { + +std::string ToString(const base::Value& value) { + if (value.is_string()) { + return value.GetString(); + } + std::string output_str; + base::JSONWriter::Write(value, &output_str); + return output_str; +} + +TEST(MediaSerializersTest, BaseTypes) { + int a = 1; + int64_t b = 2; + bool c = false; + double d = 100; + float e = 4523; + std::string f = "foo"; + const char* g = "bar"; + + ASSERT_EQ(ToString(MediaSerialize(a)), "1"); + ASSERT_EQ(ToString(MediaSerialize(b)), "2.0"); + ASSERT_EQ(ToString(MediaSerialize(c)), "false"); + ASSERT_EQ(ToString(MediaSerialize(d)), "100.0"); + ASSERT_EQ(ToString(MediaSerialize(e)), "4523.0"); + ASSERT_EQ(ToString(MediaSerialize(f)), "foo"); + ASSERT_EQ(ToString(MediaSerialize(g)), "bar"); + + ASSERT_EQ(ToString(MediaSerialize("raw string")), "raw string"); +} + +TEST(MediaSerializersTest, Optional) { + base::Optional<int> foo; + ASSERT_EQ(ToString(MediaSerialize(foo)), "unset"); + + foo = 1; + ASSERT_EQ(ToString(MediaSerialize(foo)), "1"); +} + +TEST(MediaSerializersTest, Vector) { + std::vector<int> foo = {1, 2, 3, 6, 78, 8}; + ASSERT_EQ(ToString(MediaSerialize(foo)), "[1,2,3,6,78,8]"); + + std::vector<std::string> bar = {"1", "3"}; + ASSERT_EQ(ToString(MediaSerialize(bar)), "[\"1\",\"3\"]"); +} + +} // namespace media diff --git a/chromium/media/base/media_switches.cc b/chromium/media/base/media_switches.cc index 5f33919f151..636dec1c063 100644 --- a/chromium/media/base/media_switches.cc +++ b/chromium/media/base/media_switches.cc @@ -98,6 +98,8 @@ const char kUnsafelyAllowProtectedMediaIdentifierForDomain[] = "unsafely-allow-protected-media-identifier-for-domain"; // Use fake device for Media Stream to replace actual camera and microphone. +// For the list of allowed parameters, see +// FakeVideoCaptureDeviceFactory::ParseFakeDevicesConfigFromOptionsString(). const char kUseFakeDeviceForMediaStream[] = "use-fake-device-for-media-stream"; // Use an .y4m file to play as the webcam. See the comments in @@ -182,9 +184,11 @@ const char kOverrideEnabledCdmInterfaceVersion[] = const char kOverrideHardwareSecureCodecsForTesting[] = "override-hardware-secure-codecs-for-testing"; -// Enables GpuMemoryBuffer-based buffer pool. -const char kVideoCaptureUseGpuMemoryBuffer[] = - "video-capture-use-gpu-memory-buffer"; +#if BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION) +// Force to disable kChromeosVideoDecoder feature, used for unsupported boards. +const char kForceDisableNewAcceleratedVideoDecoder[] = + "force-disable-new-accelerated-video-decoder"; +#endif // BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION) namespace autoplay { @@ -255,6 +259,11 @@ const base::Feature kUseAndroidOverlayAggressively{ const base::Feature kBackgroundVideoPauseOptimization{ "BackgroundVideoPauseOptimization", base::FEATURE_ENABLED_BY_DEFAULT}; +// CDM host verification is enabled by default. Can be disabled for testing. +// Has no effect if ENABLE_CDM_HOST_VERIFICATION buildflag is false. +const base::Feature kCdmHostVerification{"CdmHostVerification", + base::FEATURE_ENABLED_BY_DEFAULT}; + // Make MSE garbage collection algorithm more aggressive when we are under // moderate or critical memory pressure. This will relieve memory pressure by // releasing stale data from MSE buffers. @@ -284,10 +293,6 @@ const base::Feature kRevokeMediaSourceObjectURLOnAttach{ const base::Feature kChromeosVideoDecoder{"ChromeosVideoDecoder", base::FEATURE_DISABLED_BY_DEFAULT}; -// Don't allow use of 11.1 devices, even if supported. They might be more crashy -const base::Feature kD3D11LimitTo11_0{"D3D11VideoDecoderLimitTo11_0", - base::FEATURE_DISABLED_BY_DEFAULT}; - // Enable saving playback information in a crash trace, to see if some codecs // are crashier than others. const base::Feature kD3D11PrintCodecOnCrash{"D3D11PrintCodecOnCrash", @@ -333,9 +338,24 @@ const base::Feature kD3D11VideoDecoderAllowOverlay{ const base::Feature kFallbackAfterDecodeError{"FallbackAfterDecodeError", base::FEATURE_ENABLED_BY_DEFAULT}; +// Use Gav1VideoDecoder to decode AV1 streams. +const base::Feature kGav1VideoDecoder{"Gav1VideoDecoder", + base::FEATURE_DISABLED_BY_DEFAULT}; + // Show toolbar button that opens dialog for controlling media sessions. -const base::Feature kGlobalMediaControls{"GlobalMediaControls", - base::FEATURE_DISABLED_BY_DEFAULT}; +const base::Feature kGlobalMediaControls { + "GlobalMediaControls", +#if defined(OS_WIN) || defined(OS_MACOSX) || \ + (defined(OS_LINUX) && !defined(OS_CHROMEOS)) + base::FEATURE_ENABLED_BY_DEFAULT +#else + base::FEATURE_DISABLED_BY_DEFAULT +#endif +}; + +// Auto-dismiss global media controls. +const base::Feature kGlobalMediaControlsAutoDismiss{ + "GlobalMediaControlsAutoDismiss", base::FEATURE_ENABLED_BY_DEFAULT}; // Show Cast sessions in Global Media Controls. It is no-op if // kGlobalMediaControls is not enabled. @@ -347,6 +367,10 @@ const base::Feature kGlobalMediaControlsForCast{ const base::Feature kGlobalMediaControlsOverlayControls{ "GlobalMediaControlsOverlayControls", base::FEATURE_DISABLED_BY_DEFAULT}; +// Show picture-in-picture button in Global Media Controls. +const base::Feature kGlobalMediaControlsPictureInPicture{ + "GlobalMediaControlsPictureInPicture", base::FEATURE_DISABLED_BY_DEFAULT}; + // Enable new cpu load estimator. Intended for evaluation in local // testing and origin-trial. // TODO(nisse): Delete once we have switched over to always using the @@ -366,6 +390,12 @@ const base::Feature kUseNewMediaCache{"use-new-media-cache", const base::Feature kUseMediaHistoryStore{"UseMediaHistoryStore", base::FEATURE_DISABLED_BY_DEFAULT}; +// Causes video.requestAniationFrame to use a microtask instead of running with +// the rendering steps. TODO(crbug.com/1012063): Remove this once we figure out +// which implementation to use. +const base::Feature kUseMicrotaskForVideoRAF{"UseMicrotaskForVideoRAF", + base::FEATURE_DISABLED_BY_DEFAULT}; + // Use R16 texture for 9-16 bit channel instead of half-float conversion by CPU. const base::Feature kUseR16Texture{"use-r16-texture", base::FEATURE_DISABLED_BY_DEFAULT}; @@ -377,7 +407,7 @@ const base::Feature kUnifiedAutoplay{"UnifiedAutoplay", // Enable VA-API hardware encode acceleration for H264 on AMD. const base::Feature kVaapiH264AMDEncoder{"VaapiH264AMDEncoder", - base::FEATURE_DISABLED_BY_DEFAULT}; + base::FEATURE_ENABLED_BY_DEFAULT}; // Enable VA-API hardware low power encoder for all codecs. const base::Feature kVaapiLowPowerEncoder{"VaapiLowPowerEncoder", @@ -407,6 +437,10 @@ const base::Feature kVideoBlitColorAccuracy{"video-blit-color-accuracy", const base::Feature kExternalClearKeyForTesting{ "ExternalClearKeyForTesting", base::FEATURE_DISABLED_BY_DEFAULT}; +// Enables the LiveCaption feature. +const base::Feature kLiveCaption{"LiveCaption", + base::FEATURE_DISABLED_BY_DEFAULT}; + // Prevents UrlProvisionFetcher from making a provisioning request. If // specified, any provisioning request made will not be sent to the provisioning // server, and the response will indicate a failure to communicate with the @@ -489,10 +523,6 @@ const base::Feature kMediaDrmPreprovisioning{"MediaDrmPreprovisioning", const base::Feature kMediaDrmPreprovisioningAtStartup{ "MediaDrmPreprovisioningAtStartup", base::FEATURE_ENABLED_BY_DEFAULT}; -// Enables the Android Image Reader path for Video decoding(for AVDA and MCVD) -const base::Feature kAImageReaderVideoOutput{"AImageReaderVideoOutput", - base::FEATURE_ENABLED_BY_DEFAULT}; - // Prevents using SurfaceLayer for videos. This is meant to be used by embedders // that cannot support SurfaceLayer at the moment. const base::Feature kDisableSurfaceLayerForVideo{ @@ -511,6 +541,11 @@ const base::Feature kCanPlayHls{"CanPlayHls", base::FEATURE_ENABLED_BY_DEFAULT}; // HLS manifests will fail to load (triggering source fallback or load error). const base::Feature kHlsPlayer{"HlsPlayer", base::FEATURE_ENABLED_BY_DEFAULT}; +// When enabled, Playing media sessions will request audio focus from the +// Android system. +const base::Feature kRequestSystemAudioFocus{"RequestSystemAudioFocus", + base::FEATURE_ENABLED_BY_DEFAULT}; + // Use the (hacky) AudioManager.getOutputLatency() call to get the estimated // hardware latency for a stream for OpenSLES playback. This is normally not // needed, except for some Android TV devices. @@ -538,6 +573,10 @@ const base::Feature kMediaFoundationH264Encoding{ const base::Feature kMediaFoundationVideoCapture{ "MediaFoundationVideoCapture", base::FEATURE_ENABLED_BY_DEFAULT}; +// Enables VP8 decode acceleration for Windows. +const base::Feature MEDIA_EXPORT kMediaFoundationVP8Decoding{ + "MediaFoundationVP8Decoding", base::FEATURE_DISABLED_BY_DEFAULT}; + // Enables DirectShow GetPhotoState implementation // Created to act as a kill switch by disabling it, in the case of the // resurgence of https://crbug.com/722038 @@ -598,6 +637,15 @@ const base::Feature kPreloadMediaEngagementData{ const base::Feature kMediaEngagementHTTPSOnly{ "MediaEngagementHTTPSOnly", base::FEATURE_DISABLED_BY_DEFAULT}; +// Enables Media Feeds to allow sites to provide specific recommendations for +// users. +const base::Feature kMediaFeeds{"MediaFeeds", + base::FEATURE_DISABLED_BY_DEFAULT}; + +// Enables checking Media Feeds against safe search to prevent adult content. +const base::Feature kMediaFeedsSafeSearch{"MediaFeedsSafeSearch", + base::FEATURE_DISABLED_BY_DEFAULT}; + // Send events to devtools rather than to chrome://media-internals const base::Feature kMediaInspectorLogging{"MediaInspectorLogging", base::FEATURE_DISABLED_BY_DEFAULT}; @@ -612,6 +660,11 @@ const base::Feature kMediaLearningExperiment{"MediaLearningExperiment", const base::Feature kMediaLearningFramework{"MediaLearningFramework", base::FEATURE_DISABLED_BY_DEFAULT}; +// Enables the smoothness prediction experiment. Requires +// kMediaLearningFramework to be enabled also, else it does nothing. +const base::Feature kMediaLearningSmoothnessExperiment{ + "MediaLearningSmoothnessExperiment", base::FEATURE_DISABLED_BY_DEFAULT}; + // Enable aggregate power measurement for media playback. const base::Feature kMediaPowerExperiment{"MediaPowerExperiment", base::FEATURE_DISABLED_BY_DEFAULT}; @@ -645,9 +698,17 @@ const base::Feature kInternalMediaSession { #endif }; +const base::Feature kKaleidoscope{"Kaleidoscope", + base::FEATURE_DISABLED_BY_DEFAULT}; + const base::Feature kUseFakeDeviceForMediaStream{ "use-fake-device-for-media-stream", base::FEATURE_DISABLED_BY_DEFAULT}; +// Makes VideoCadenceEstimator use Bresenham-like algorithm for frame cadence +// estimations. +const base::Feature kBresenhamCadence{"BresenhamCadence", + base::FEATURE_DISABLED_BY_DEFAULT}; + bool IsVideoCaptureAcceleratedJpegDecodingEnabled() { if (base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kDisableAcceleratedMjpegDecode)) { diff --git a/chromium/media/base/media_switches.h b/chromium/media/base/media_switches.h index 6bf9883256c..dfe622f670c 100644 --- a/chromium/media/base/media_switches.h +++ b/chromium/media/base/media_switches.h @@ -55,6 +55,10 @@ MEDIA_EXPORT extern const char kForceProtectedVideoOutputBuffers[]; MEDIA_EXPORT extern const char kEnableFuchsiaAudioConsumer[]; #endif +#if BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION) +MEDIA_EXPORT extern const char kForceDisableNewAcceleratedVideoDecoder[]; +#endif + #if defined(USE_CRAS) MEDIA_EXPORT extern const char kUseCras[]; #endif @@ -84,8 +88,6 @@ MEDIA_EXPORT extern const char kClearKeyCdmPathForTesting[]; MEDIA_EXPORT extern const char kOverrideEnabledCdmInterfaceVersion[]; MEDIA_EXPORT extern const char kOverrideHardwareSecureCodecsForTesting[]; -MEDIA_EXPORT extern const char kVideoCaptureUseGpuMemoryBuffer[]; - namespace autoplay { MEDIA_EXPORT extern const char kDocumentUserActivationRequiredPolicy[]; @@ -107,7 +109,8 @@ MEDIA_EXPORT extern const base::Feature kAutoplayIgnoreWebAudio; MEDIA_EXPORT extern const base::Feature kAutoplayDisableSettings; MEDIA_EXPORT extern const base::Feature kAutoplayWhitelistSettings; MEDIA_EXPORT extern const base::Feature kBackgroundVideoPauseOptimization; -MEDIA_EXPORT extern const base::Feature kD3D11LimitTo11_0; +MEDIA_EXPORT extern const base::Feature kBresenhamCadence; +MEDIA_EXPORT extern const base::Feature kCdmHostVerification; MEDIA_EXPORT extern const base::Feature kD3D11PrintCodecOnCrash; MEDIA_EXPORT extern const base::Feature kD3D11VideoDecoder; MEDIA_EXPORT extern const base::Feature kD3D11VideoDecoderIgnoreWorkarounds; @@ -120,20 +123,28 @@ MEDIA_EXPORT extern const base::Feature kExternalClearKeyForTesting; MEDIA_EXPORT extern const base::Feature kFFmpegDecodeOpaqueVP8; MEDIA_EXPORT extern const base::Feature kFailUrlProvisionFetcherForTesting; MEDIA_EXPORT extern const base::Feature kFallbackAfterDecodeError; +MEDIA_EXPORT extern const base::Feature kGav1VideoDecoder; MEDIA_EXPORT extern const base::Feature kGlobalMediaControls; +MEDIA_EXPORT extern const base::Feature kGlobalMediaControlsAutoDismiss; MEDIA_EXPORT extern const base::Feature kGlobalMediaControlsForCast; MEDIA_EXPORT extern const base::Feature kGlobalMediaControlsOverlayControls; +MEDIA_EXPORT extern const base::Feature kGlobalMediaControlsPictureInPicture; MEDIA_EXPORT extern const base::Feature kHardwareMediaKeyHandling; MEDIA_EXPORT extern const base::Feature kHardwareSecureDecryption; MEDIA_EXPORT extern const base::Feature kInternalMediaSession; +MEDIA_EXPORT extern const base::Feature kKaleidoscope; +MEDIA_EXPORT extern const base::Feature kLiveCaption; MEDIA_EXPORT extern const base::Feature kLowDelayVideoRenderingOnLiveStream; MEDIA_EXPORT extern const base::Feature kMediaCapabilitiesWithParameters; MEDIA_EXPORT extern const base::Feature kMediaCastOverlayButton; MEDIA_EXPORT extern const base::Feature kMediaEngagementBypassAutoplayPolicies; MEDIA_EXPORT extern const base::Feature kMediaEngagementHTTPSOnly; +MEDIA_EXPORT extern const base::Feature kMediaFeeds; +MEDIA_EXPORT extern const base::Feature kMediaFeedsSafeSearch; MEDIA_EXPORT extern const base::Feature kMediaInspectorLogging; MEDIA_EXPORT extern const base::Feature kMediaLearningExperiment; MEDIA_EXPORT extern const base::Feature kMediaLearningFramework; +MEDIA_EXPORT extern const base::Feature kMediaLearningSmoothnessExperiment; MEDIA_EXPORT extern const base::Feature kMediaPowerExperiment; MEDIA_EXPORT extern const base::Feature kMemoryPressureBasedSourceBufferGC; MEDIA_EXPORT extern const base::Feature kChromeosVideoDecoder; @@ -153,6 +164,7 @@ MEDIA_EXPORT extern const base::Feature kUnifiedAutoplay; MEDIA_EXPORT extern const base::Feature kUseAndroidOverlayAggressively; MEDIA_EXPORT extern const base::Feature kUseFakeDeviceForMediaStream; MEDIA_EXPORT extern const base::Feature kUseMediaHistoryStore; +MEDIA_EXPORT extern const base::Feature kUseMicrotaskForVideoRAF; MEDIA_EXPORT extern const base::Feature kUseNewMediaCache; MEDIA_EXPORT extern const base::Feature kUseR16Texture; MEDIA_EXPORT extern const base::Feature kVaapiH264AMDEncoder; @@ -172,11 +184,11 @@ MEDIA_EXPORT extern const base::Feature kMediaControlsExpandGesture; MEDIA_EXPORT extern const base::Feature kMediaDrmPersistentLicense; MEDIA_EXPORT extern const base::Feature kMediaDrmPreprovisioning; MEDIA_EXPORT extern const base::Feature kMediaDrmPreprovisioningAtStartup; -MEDIA_EXPORT extern const base::Feature kAImageReaderVideoOutput; MEDIA_EXPORT extern const base::Feature kDisableSurfaceLayerForVideo; MEDIA_EXPORT extern const base::Feature kCanPlayHls; MEDIA_EXPORT extern const base::Feature kPictureInPictureAPI; MEDIA_EXPORT extern const base::Feature kHlsPlayer; +MEDIA_EXPORT extern const base::Feature kRequestSystemAudioFocus; MEDIA_EXPORT extern const base::Feature kUseAudioLatencyFromHAL; MEDIA_EXPORT extern const base::Feature kUsePooledSharedImageVideoProvider; #endif // defined(OS_ANDROID) @@ -185,6 +197,7 @@ MEDIA_EXPORT extern const base::Feature kUsePooledSharedImageVideoProvider; MEDIA_EXPORT extern const base::Feature kDelayCopyNV12Textures; MEDIA_EXPORT extern const base::Feature kMediaFoundationH264Encoding; MEDIA_EXPORT extern const base::Feature kMediaFoundationVideoCapture; +MEDIA_EXPORT extern const base::Feature kMediaFoundationVP8Decoding; MEDIA_EXPORT extern const base::Feature kDirectShowGetPhotoState; #endif // defined(OS_WIN) diff --git a/chromium/media/base/media_types.cc b/chromium/media/base/media_types.cc index bc980c125aa..ceaddc7f7b6 100644 --- a/chromium/media/base/media_types.cc +++ b/chromium/media/base/media_types.cc @@ -10,7 +10,7 @@ namespace media { // static AudioType AudioType::FromDecoderConfig(const AudioDecoderConfig& config) { - return {config.codec()}; + return {config.codec(), AudioCodecProfile::kUnknown, false}; } // static @@ -57,7 +57,8 @@ VideoType VideoType::FromDecoderConfig(const VideoDecoderConfig& config) { } bool operator==(const AudioType& x, const AudioType& y) { - return x.codec == y.codec; + return std::tie(x.codec, x.profile, x.spatial_rendering) == + std::tie(y.codec, y.profile, y.spatial_rendering); } bool operator!=(const AudioType& x, const AudioType& y) { diff --git a/chromium/media/base/media_types.h b/chromium/media/base/media_types.h index 7ee96a1ff52..c7f6f2a5d99 100644 --- a/chromium/media/base/media_types.h +++ b/chromium/media/base/media_types.h @@ -22,7 +22,8 @@ struct MEDIA_EXPORT AudioType { static AudioType FromDecoderConfig(const AudioDecoderConfig& config); AudioCodec codec; - bool spatialRendering; + AudioCodecProfile profile; + bool spatial_rendering; }; struct MEDIA_EXPORT VideoType { diff --git a/chromium/media/base/media_util.h b/chromium/media/base/media_util.h index 65aee57e2f8..fac5af522da 100644 --- a/chromium/media/base/media_util.h +++ b/chromium/media/base/media_util.h @@ -28,7 +28,8 @@ class MEDIA_EXPORT NullMediaLog : public media::MediaLog { NullMediaLog() = default; ~NullMediaLog() override = default; - void AddEventLocked(std::unique_ptr<media::MediaLogEvent> event) override {} + void AddLogRecordLocked( + std::unique_ptr<media::MediaLogRecord> event) override {} private: DISALLOW_COPY_AND_ASSIGN(NullMediaLog); diff --git a/chromium/media/base/memory_dump_provider_proxy.cc b/chromium/media/base/memory_dump_provider_proxy.cc new file mode 100644 index 00000000000..72117cb3a78 --- /dev/null +++ b/chromium/media/base/memory_dump_provider_proxy.cc @@ -0,0 +1,34 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/base/memory_dump_provider_proxy.h" + +#include <utility> + +#include "base/trace_event/memory_dump_manager.h" + +namespace media { + +MemoryDumpProviderProxy::MemoryDumpProviderProxy( + const char* name, + scoped_refptr<base::SingleThreadTaskRunner> task_runner, + MemoryDumpCB dump_cb) + : dump_cb_(std::move(dump_cb)) { + base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider( + this, name, std::move(task_runner)); +} + +MemoryDumpProviderProxy::~MemoryDumpProviderProxy() { + base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider( + this); +} + +bool MemoryDumpProviderProxy::OnMemoryDump( + const base::trace_event::MemoryDumpArgs& args, + base::trace_event::ProcessMemoryDump* pmd) { + dump_cb_.Run(args, pmd); + return true; +} + +} // namespace media diff --git a/chromium/media/base/memory_dump_provider_proxy.h b/chromium/media/base/memory_dump_provider_proxy.h new file mode 100644 index 00000000000..72767e003fe --- /dev/null +++ b/chromium/media/base/memory_dump_provider_proxy.h @@ -0,0 +1,50 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEDIA_BASE_MEMORY_DUMP_PROVIDER_PROXY_H_ +#define MEDIA_BASE_MEMORY_DUMP_PROVIDER_PROXY_H_ + +#include <stddef.h> +#include <stdint.h> + +#include <memory> +#include <string> +#include <utility> + +#include "base/logging.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/single_thread_task_runner.h" +#include "base/strings/string_piece.h" +#include "base/trace_event/memory_dump_provider.h" +#include "media/base/media_export.h" + +namespace media { + +using MemoryDumpCB = + base::RepeatingCallback<void(const base::trace_event::MemoryDumpArgs& args, + base::trace_event::ProcessMemoryDump* pmd)>; + +class MEDIA_EXPORT MemoryDumpProviderProxy final + : public base::trace_event::MemoryDumpProvider { + public: + MemoryDumpProviderProxy( + const char* name, + scoped_refptr<base::SingleThreadTaskRunner> task_runner, + MemoryDumpCB dump_cb); + + ~MemoryDumpProviderProxy() override; + + bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args, + base::trace_event::ProcessMemoryDump* pmd) override; + + private: + MemoryDumpCB dump_cb_; + + DISALLOW_COPY_AND_ASSIGN(MemoryDumpProviderProxy); +}; + +} // namespace media + +#endif // MEDIA_BASE_MEMORY_DUMP_PROVIDER_PROXY_H_ diff --git a/chromium/media/base/mime_util_internal.cc b/chromium/media/base/mime_util_internal.cc index 23381d026f1..000325d0dd4 100644 --- a/chromium/media/base/mime_util_internal.cc +++ b/chromium/media/base/mime_util_internal.cc @@ -69,6 +69,7 @@ const StringToCodecMap& GetStringToCodecMap() { {"mp4a.40.5", MimeUtil::MPEG4_AAC}, {"mp4a.40.05", MimeUtil::MPEG4_AAC}, {"mp4a.40.29", MimeUtil::MPEG4_AAC}, + {"mp4a.40.42", MimeUtil::MPEG4_XHE_AAC}, // TODO(servolk): Strictly speaking only mp4a.A5 and mp4a.A6 // codec ids are valid according to RFC 6381 section 3.3, 3.4. // Lower-case oti (mp4a.a5 and mp4a.a6) should be rejected. But @@ -170,6 +171,7 @@ AudioCodec MimeUtilToAudioCodec(MimeUtil::Codec codec) { return kCodecEAC3; case MimeUtil::MPEG2_AAC: case MimeUtil::MPEG4_AAC: + case MimeUtil::MPEG4_XHE_AAC: return kCodecAAC; case MimeUtil::MPEG_H_AUDIO: return kCodecMpegHAudio; @@ -310,7 +312,7 @@ void MimeUtil::AddSupportedMediaFormats() { mp4_video_codecs.emplace(VP9); #if BUILDFLAG(USE_PROPRIETARY_CODECS) - const CodecSet aac{MPEG2_AAC, MPEG4_AAC}; + const CodecSet aac{MPEG2_AAC, MPEG4_AAC, MPEG4_XHE_AAC}; mp4_audio_codecs.insert(aac.begin(), aac.end()); CodecSet avc_and_aac(aac); @@ -585,6 +587,10 @@ bool MimeUtil::IsCodecSupportedOnAndroid( DCHECK(!is_encrypted || platform_info.has_platform_decoders); return true; + case MPEG4_XHE_AAC: + // xHE-AAC is only supported via MediaCodec. + return platform_info.has_platform_decoders; + case MPEG_H_AUDIO: return false; @@ -885,7 +891,11 @@ SupportsType MimeUtil::IsCodecSupported(const std::string& mime_type_lower_case, AudioCodec audio_codec = MimeUtilToAudioCodec(codec); if (audio_codec != kUnknownAudioCodec) { - if (!IsSupportedAudioType({audio_codec})) + AudioCodecProfile audio_profile = AudioCodecProfile::kUnknown; + if (codec == MPEG4_XHE_AAC) + audio_profile = AudioCodecProfile::kXHE_AAC; + + if (!IsSupportedAudioType({audio_codec, audio_profile, false})) return IsNotSupported; } diff --git a/chromium/media/base/mime_util_internal.h b/chromium/media/base/mime_util_internal.h index 5f604ae93fe..1401ba2e1a9 100644 --- a/chromium/media/base/mime_util_internal.h +++ b/chromium/media/base/mime_util_internal.h @@ -35,6 +35,7 @@ class MEDIA_EXPORT MimeUtil { EAC3, MPEG2_AAC, MPEG4_AAC, + MPEG4_XHE_AAC, VORBIS, OPUS, FLAC, diff --git a/chromium/media/base/mime_util_unittest.cc b/chromium/media/base/mime_util_unittest.cc index 01a6de51539..31258457dc0 100644 --- a/chromium/media/base/mime_util_unittest.cc +++ b/chromium/media/base/mime_util_unittest.cc @@ -440,6 +440,7 @@ TEST(IsCodecSupportedOnAndroidTest, EncryptedCodecBehavior) { case MimeUtil::MP3: case MimeUtil::MPEG2_AAC: case MimeUtil::MPEG4_AAC: + case MimeUtil::MPEG4_XHE_AAC: case MimeUtil::VORBIS: case MimeUtil::FLAC: case MimeUtil::H264: @@ -514,6 +515,10 @@ TEST(IsCodecSupportedOnAndroidTest, ClearCodecBehavior) { break; // These codecs are only supported if platform decoders are supported. + case MimeUtil::MPEG4_XHE_AAC: + EXPECT_EQ(info.has_platform_decoders, result); + break; + case MimeUtil::HEVC: #if BUILDFLAG(ENABLE_PLATFORM_HEVC) EXPECT_EQ( diff --git a/chromium/media/base/mock_filters.cc b/chromium/media/base/mock_filters.cc index 85f9ed7a303..85c3e6dd11f 100644 --- a/chromium/media/base/mock_filters.cc +++ b/chromium/media/base/mock_filters.cc @@ -220,10 +220,10 @@ void MockCdmFactory::Create( const SessionClosedCB& session_closed_cb, const SessionKeysChangeCB& session_keys_change_cb, const SessionExpirationUpdateCB& session_expiration_update_cb, - const CdmCreatedCB& cdm_created_cb) { + CdmCreatedCB cdm_created_cb) { // If no key system specified, notify that Create() failed. if (key_system.empty()) { - cdm_created_cb.Run(nullptr, "CDM creation failed"); + std::move(cdm_created_cb).Run(nullptr, "CDM creation failed"); return; } @@ -239,7 +239,7 @@ void MockCdmFactory::Create( key_system, security_origin, session_message_cb, session_closed_cb, session_keys_change_cb, session_expiration_update_cb); created_cdm_ = cdm.get(); - cdm_created_cb.Run(std::move(cdm), ""); + std::move(cdm_created_cb).Run(std::move(cdm), ""); } MockCdm* MockCdmFactory::GetCreatedCdm() { @@ -247,8 +247,8 @@ MockCdm* MockCdmFactory::GetCreatedCdm() { } void MockCdmFactory::SetBeforeCreationCB( - const base::Closure& before_creation_cb) { - before_creation_cb_ = before_creation_cb; + base::RepeatingClosure before_creation_cb) { + before_creation_cb_ = std::move(before_creation_cb); } MockStreamParser::MockStreamParser() = default; diff --git a/chromium/media/base/mock_filters.h b/chromium/media/base/mock_filters.h index 9b679e99c08..36a270f7101 100644 --- a/chromium/media/base/mock_filters.h +++ b/chromium/media/base/mock_filters.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright (c) 2020 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -59,12 +59,13 @@ class MockPipelineClient : public Pipeline::Client { void(BufferingState, BufferingStateChangeReason)); MOCK_METHOD0(OnDurationChange, void()); MOCK_METHOD2(OnAddTextTrack, - void(const TextTrackConfig&, const AddTextTrackDoneCB&)); + void(const TextTrackConfig&, AddTextTrackDoneCB)); MOCK_METHOD1(OnWaiting, void(WaitingReason)); MOCK_METHOD1(OnAudioConfigChange, void(const AudioDecoderConfig&)); MOCK_METHOD1(OnVideoConfigChange, void(const VideoDecoderConfig&)); MOCK_METHOD1(OnVideoNaturalSizeChange, void(const gfx::Size&)); MOCK_METHOD1(OnVideoOpacityChange, void(bool)); + MOCK_METHOD1(OnVideoFrameRateChange, void(base::Optional<int>)); MOCK_METHOD0(OnVideoAverageKeyframeDistanceUpdate, void()); MOCK_METHOD1(OnAudioDecoderChange, void(const PipelineDecoderInfo&)); MOCK_METHOD1(OnVideoDecoderChange, void(const PipelineDecoderInfo&)); @@ -76,12 +77,25 @@ class MockPipeline : public Pipeline { MockPipeline(); ~MockPipeline() override; - MOCK_METHOD4(Start, - void(StartType, Demuxer*, Client*, const PipelineStatusCB&)); + void Start(StartType start_type, + Demuxer* demuxer, + Client* client, + PipelineStatusCallback seek_cb) { + OnStart(start_type, demuxer, client, seek_cb); + } + MOCK_METHOD4(OnStart, + void(StartType, Demuxer*, Client*, PipelineStatusCallback&)); MOCK_METHOD0(Stop, void()); - MOCK_METHOD2(Seek, void(base::TimeDelta, const PipelineStatusCB&)); - MOCK_METHOD1(Suspend, void(const PipelineStatusCB&)); - MOCK_METHOD2(Resume, void(base::TimeDelta, const PipelineStatusCB&)); + void Seek(base::TimeDelta time, PipelineStatusCallback seek_cb) { + OnSeek(time, seek_cb); + } + MOCK_METHOD2(OnSeek, void(base::TimeDelta, PipelineStatusCallback&)); + void Suspend(PipelineStatusCallback cb) { OnSuspend(cb); } + MOCK_METHOD1(OnSuspend, void(PipelineStatusCallback&)); + void Resume(base::TimeDelta time, PipelineStatusCallback seek_cb) { + OnResume(time, seek_cb); + } + MOCK_METHOD2(OnResume, void(base::TimeDelta, PipelineStatusCallback&)); MOCK_METHOD2(OnEnabledAudioTracksChanged, void(const std::vector<MediaTrack::Id>&, base::OnceClosure)); MOCK_METHOD2(OnSelectedVideoTrackChanged, @@ -281,6 +295,7 @@ class MockRendererClient : public RendererClient { MOCK_METHOD1(OnVideoConfigChange, void(const VideoDecoderConfig&)); MOCK_METHOD1(OnVideoNaturalSizeChange, void(const gfx::Size&)); MOCK_METHOD1(OnVideoOpacityChange, void(bool)); + MOCK_METHOD1(OnVideoFrameRateChange, void(base::Optional<int>)); MOCK_METHOD1(OnDurationChange, void(base::TimeDelta)); MOCK_METHOD1(OnRemotePlayStateChange, void(MediaStatus::State state)); MOCK_METHOD0(IsVideoStreamAvailable, bool()); @@ -292,16 +307,25 @@ class MockVideoRenderer : public VideoRenderer { ~MockVideoRenderer() override; // VideoRenderer implementation. - MOCK_METHOD5(Initialize, + void Initialize(DemuxerStream* stream, + CdmContext* cdm_context, + RendererClient* client, + const TimeSource::WallClockTimeCB& wall_clock_time_cb, + PipelineStatusCallback init_cb) { + OnInitialize(stream, cdm_context, client, wall_clock_time_cb, init_cb); + } + MOCK_METHOD5(OnInitialize, void(DemuxerStream* stream, CdmContext* cdm_context, RendererClient* client, const TimeSource::WallClockTimeCB& wall_clock_time_cb, - const PipelineStatusCB& init_cb)); + PipelineStatusCallback& init_cb)); MOCK_METHOD1(Flush, void(base::OnceClosure flush_cb)); MOCK_METHOD1(StartPlayingFrom, void(base::TimeDelta)); MOCK_METHOD0(OnTimeProgressing, void()); MOCK_METHOD0(OnTimeStopped, void()); + MOCK_METHOD1(SetLatencyHint, + void(base::Optional<base::TimeDelta> latency_hint)); private: DISALLOW_COPY_AND_ASSIGN(MockVideoRenderer); @@ -313,15 +337,23 @@ class MockAudioRenderer : public AudioRenderer { ~MockAudioRenderer() override; // AudioRenderer implementation. - MOCK_METHOD4(Initialize, + void Initialize(DemuxerStream* stream, + CdmContext* cdm_context, + RendererClient* client, + PipelineStatusCallback init_cb) { + OnInitialize(stream, cdm_context, client, init_cb); + } + MOCK_METHOD4(OnInitialize, void(DemuxerStream* stream, CdmContext* cdm_context, RendererClient* client, - const PipelineStatusCB& init_cb)); + PipelineStatusCallback& init_cb)); MOCK_METHOD0(GetTimeSource, TimeSource*()); MOCK_METHOD1(Flush, void(base::OnceClosure flush_cb)); MOCK_METHOD0(StartPlaying, void()); MOCK_METHOD1(SetVolume, void(float volume)); + MOCK_METHOD1(SetLatencyHint, + void(base::Optional<base::TimeDelta> latency_hint)); private: DISALLOW_COPY_AND_ASSIGN(MockAudioRenderer); @@ -377,7 +409,7 @@ class MockRendererFactory : public RendererFactory { const scoped_refptr<base::TaskRunner>&, AudioRendererSink*, VideoRendererSink*, - const RequestOverlayInfoCB&, + RequestOverlayInfoCB, const gfx::ColorSpace&)); private: @@ -459,18 +491,16 @@ class MockDecryptor : public Decryptor { ~MockDecryptor() override; MOCK_METHOD2(RegisterNewKeyCB, - void(StreamType stream_type, const NewKeyCB& new_key_cb)); + void(StreamType stream_type, NewKeyCB new_key_cb)); MOCK_METHOD3(Decrypt, void(StreamType stream_type, scoped_refptr<DecoderBuffer> encrypted, - const DecryptCB& decrypt_cb)); + DecryptCB decrypt_cb)); MOCK_METHOD1(CancelDecrypt, void(StreamType stream_type)); MOCK_METHOD2(InitializeAudioDecoder, - void(const AudioDecoderConfig& config, - const DecoderInitCB& init_cb)); + void(const AudioDecoderConfig& config, DecoderInitCB init_cb)); MOCK_METHOD2(InitializeVideoDecoder, - void(const VideoDecoderConfig& config, - const DecoderInitCB& init_cb)); + void(const VideoDecoderConfig& config, DecoderInitCB init_cb)); MOCK_METHOD2(DecryptAndDecodeAudio, void(scoped_refptr<DecoderBuffer> encrypted, const AudioDecodeCB& audio_decode_cb)); @@ -614,20 +644,20 @@ class MockCdmFactory : public CdmFactory { const SessionClosedCB& session_closed_cb, const SessionKeysChangeCB& session_keys_change_cb, const SessionExpirationUpdateCB& session_expiration_update_cb, - const CdmCreatedCB& cdm_created_cb) override; + CdmCreatedCB cdm_created_cb) override; // Return a pointer to the created CDM. MockCdm* GetCreatedCdm(); // Provide a callback to be called before the CDM is created and returned. - void SetBeforeCreationCB(const base::Closure& before_creation_cb); + void SetBeforeCreationCB(base::RepeatingClosure before_creation_cb); private: // Reference to the created CDM. scoped_refptr<MockCdm> created_cdm_; // Callback to be used before Create() successfully calls |cdm_created_cb|. - base::Closure before_creation_cb_; + base::RepeatingClosure before_creation_cb_; DISALLOW_COPY_AND_ASSIGN(MockCdmFactory); }; diff --git a/chromium/media/base/mock_media_log.cc b/chromium/media/base/mock_media_log.cc index dd60bcb0934..3ae29f64829 100644 --- a/chromium/media/base/mock_media_log.cc +++ b/chromium/media/base/mock_media_log.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "media/base/mock_media_log.h" +#include "base/json/json_writer.h" namespace media { @@ -10,10 +11,27 @@ MockMediaLog::MockMediaLog() = default; MockMediaLog::~MockMediaLog() = default; -void MockMediaLog::AddEventLocked(std::unique_ptr<MediaLogEvent> event) { +void MockMediaLog::AddLogRecordLocked(std::unique_ptr<MediaLogRecord> event) { const auto log_string = MediaEventToLogString(*event); VLOG(2) << "MediaLog: " << log_string; - DoAddEventLogString(log_string); + DoAddLogRecordLogString(log_string); +} + +std::string MockMediaLog::MediaEventToLogString(const MediaLogRecord& event) { + // Special case for PIPELINE_ERROR, since that's by far the most useful + // event for figuring out media pipeline failures, and just reporting + // pipeline status as numeric code is not very helpful/user-friendly. + int error_code = 0; + if (event.type == MediaLogRecord::Type::kMediaStatus && + event.params.GetInteger(media::MediaLog::kStatusText, &error_code)) { + PipelineStatus status = static_cast<PipelineStatus>(error_code); + return std::string(media::MediaLog::kStatusText) + " " + + PipelineStatusToString(status); + } + + std::string params_json; + base::JSONWriter::Write(event.params, ¶ms_json); + return params_json; } } // namespace media diff --git a/chromium/media/base/mock_media_log.h b/chromium/media/base/mock_media_log.h index a8f04576071..828960f0171 100644 --- a/chromium/media/base/mock_media_log.h +++ b/chromium/media/base/mock_media_log.h @@ -37,35 +37,37 @@ struct TypeAndCodec { // |log| is expected to evaluate to a MockMediaLog, optionally a NiceMock or // StrictMock, in scope of the usage of this macro. -#define EXPECT_MEDIA_LOG_ON(log, x) EXPECT_CALL((log), DoAddEventLogString((x))) +#define EXPECT_MEDIA_LOG_ON(log, x) \ + EXPECT_CALL((log), DoAddLogRecordLogString((x))) // Requires |media_log_| to be available. #define EXPECT_MEDIA_LOG_PROPERTY(property, value) \ EXPECT_CALL( \ media_log_, \ - DoAddEventLogString( \ - MatchesPropertyExactValue(MediaLog::MediaEventToLogString( \ + DoAddLogRecordLogString( \ + MatchesPropertyExactValue(MockMediaLog::MediaEventToLogString( \ *media_log_.CreatePropertyTestEvent<MediaLogProperty::property>( \ value))))) -#define EXPECT_MEDIA_LOG_PROPERTY_ANY_VALUE(property) \ - EXPECT_CALL( \ - media_log_, \ - DoAddEventLogString(MatchesPropertyAnyValue( \ - "PROPERTY_CHANGE {\"" + \ - MediaLogPropertyKeyToString(MediaLogProperty::property) + "\""))) +#define EXPECT_MEDIA_LOG_PROPERTY_ANY_VALUE(property) \ + EXPECT_CALL( \ + media_log_, \ + DoAddLogRecordLogString(MatchesPropertyAnyValue( \ + "{\"" + MediaLogPropertyKeyToString(MediaLogProperty::property) + \ + "\""))) -#define EXPECT_FOUND_CODEC_NAME(stream_type, codec_name) \ - EXPECT_CALL(media_log_, DoAddEventLogString(TracksHasCodecName(TypeAndCodec( \ - MediaLogPropertyKeyToString( \ - MediaLogProperty::k##stream_type##Tracks), \ - codec_name)))) +#define EXPECT_FOUND_CODEC_NAME(stream_type, codec_name) \ + EXPECT_CALL(media_log_, \ + DoAddLogRecordLogString(TracksHasCodecName( \ + TypeAndCodec(MediaLogPropertyKeyToString( \ + MediaLogProperty::k##stream_type##Tracks), \ + codec_name)))) namespace media { MATCHER_P(TracksHasCodecName, tandc, "") { - return (arg.substr(0, 31) == "PROPERTY_CHANGE {\"" + tandc.type + "\"") && - (arg[33] != ']') && CONTAINS_STRING(arg, tandc.codec); + return (arg.substr(0, 15) == "{\"" + tandc.type + "\"") && (arg[16] != ']') && + CONTAINS_STRING(arg, tandc.codec); } MATCHER_P(MatchesPropertyExactValue, message, "") { @@ -81,18 +83,20 @@ class MockMediaLog : public MediaLog { MockMediaLog(); ~MockMediaLog() override; - MOCK_METHOD1(DoAddEventLogString, void(const std::string& event)); + MOCK_METHOD1(DoAddLogRecordLogString, void(const std::string& event)); // Trampoline method to workaround GMOCK problems with std::unique_ptr<>. // Also simplifies tests to be able to string match on the log string // representation on the added event. - void AddEventLocked(std::unique_ptr<MediaLogEvent> event) override; + void AddLogRecordLocked(std::unique_ptr<MediaLogRecord> event) override; template <MediaLogProperty P, typename T> - std::unique_ptr<MediaLogEvent> CreatePropertyTestEvent(const T& value) { - return CreatePropertyEvent<P, T>(value); + std::unique_ptr<MediaLogRecord> CreatePropertyTestEvent(const T& value) { + return CreatePropertyRecord<P, T>(value); } + static std::string MediaEventToLogString(const MediaLogRecord& event); + private: DISALLOW_COPY_AND_ASSIGN(MockMediaLog); }; diff --git a/chromium/media/base/moving_average.cc b/chromium/media/base/moving_average.cc index 0f188e2ca14..124e0b51046 100644 --- a/chromium/media/base/moving_average.cc +++ b/chromium/media/base/moving_average.cc @@ -55,4 +55,18 @@ void MovingAverage::Reset() { std::fill(samples_.begin(), samples_.end(), base::TimeDelta()); } +std::pair<base::TimeDelta, base::TimeDelta> MovingAverage::GetMinAndMax() { + std::pair<base::TimeDelta, base::TimeDelta> result(samples_[0], samples_[0]); + + const uint64_t size = std::min(static_cast<uint64_t>(depth_), count_); + for (uint64_t i = 1; i < size; i++) { + if (samples_[i] < result.first) + result.first = samples_[i]; + if (samples_[i] > result.second) + result.second = samples_[i]; + } + + return result; +} + } // namespace media diff --git a/chromium/media/base/moving_average.h b/chromium/media/base/moving_average.h index 9f6032e5bb7..d1a966e632f 100644 --- a/chromium/media/base/moving_average.h +++ b/chromium/media/base/moving_average.h @@ -8,6 +8,7 @@ #include <stddef.h> #include <stdint.h> +#include <utility> #include <vector> #include "base/macros.h" @@ -41,6 +42,11 @@ class MEDIA_EXPORT MovingAverage { base::TimeDelta max() const { return max_; } + size_t depth() const { return depth_; } + + // |first| is min, |second| is max of all samples in the window. + std::pair<base::TimeDelta, base::TimeDelta> GetMinAndMax(); + private: // Maximum number of elements allowed in the average. const size_t depth_; diff --git a/chromium/media/base/moving_average_unittest.cc b/chromium/media/base/moving_average_unittest.cc index 39ad094231a..a195985297d 100644 --- a/chromium/media/base/moving_average_unittest.cc +++ b/chromium/media/base/moving_average_unittest.cc @@ -45,4 +45,19 @@ TEST(MovingAverageTest, Reset) { EXPECT_EQ(base::TimeDelta(), moving_average.Deviation()); } +TEST(MovingAverageTest, MinAndMax) { + MovingAverage moving_average(5); + base::TimeDelta min = base::TimeDelta::FromSeconds(1); + base::TimeDelta med = base::TimeDelta::FromSeconds(50); + base::TimeDelta max = base::TimeDelta::FromSeconds(100); + moving_average.AddSample(min); + moving_average.AddSample(med); + moving_average.AddSample(med); + moving_average.AddSample(med); + moving_average.AddSample(max); + auto extremes = moving_average.GetMinAndMax(); + EXPECT_EQ(extremes.first, min); + EXPECT_EQ(extremes.second, max); +} + } // namespace media diff --git a/chromium/media/base/multi_channel_resampler.cc b/chromium/media/base/multi_channel_resampler.cc index 155441057c0..ecc1e0b9574 100644 --- a/chromium/media/base/multi_channel_resampler.cc +++ b/chromium/media/base/multi_channel_resampler.cc @@ -117,6 +117,11 @@ int MultiChannelResampler::ChunkSize() const { return resamplers_[0]->ChunkSize(); } +int MultiChannelResampler::GetMaxInputFramesRequested( + int output_frames_requested) const { + DCHECK(!resamplers_.empty()); + return resamplers_[0]->GetMaxInputFramesRequested(output_frames_requested); +} double MultiChannelResampler::BufferedFrames() const { DCHECK(!resamplers_.empty()); diff --git a/chromium/media/base/multi_channel_resampler.h b/chromium/media/base/multi_channel_resampler.h index f5dd5716acd..b455adb4163 100644 --- a/chromium/media/base/multi_channel_resampler.h +++ b/chromium/media/base/multi_channel_resampler.h @@ -54,6 +54,9 @@ class MEDIA_EXPORT MultiChannelResampler { // single call to |read_cb_| for more data. int ChunkSize() const; + // See SincResampler::GetMaxInputFramesRequested(). + int GetMaxInputFramesRequested(int output_frames_requested) const; + // See SincResampler::BufferedFrames. double BufferedFrames() const; diff --git a/chromium/media/base/null_video_sink.h b/chromium/media/base/null_video_sink.h index 1d5a31ad87b..3b972a9a156 100644 --- a/chromium/media/base/null_video_sink.h +++ b/chromium/media/base/null_video_sink.h @@ -43,9 +43,7 @@ class MEDIA_EXPORT NullVideoSink : public VideoRendererSink { } // Sets |stop_cb_|, which will be fired when Stop() is called. - void set_stop_cb(const base::Closure& stop_cb) { - stop_cb_ = stop_cb; - } + void set_stop_cb(base::OnceClosure stop_cb) { stop_cb_ = std::move(stop_cb); } bool is_started() const { return started_; } @@ -84,7 +82,7 @@ class MEDIA_EXPORT NullVideoSink : public VideoRendererSink { const base::TickClock* tick_clock_; // If set, called when Stop() is called. - base::Closure stop_cb_; + base::OnceClosure stop_cb_; // Value passed to RenderCallback::Render(). bool background_render_; diff --git a/chromium/media/base/null_video_sink_unittest.cc b/chromium/media/base/null_video_sink_unittest.cc index 33b3aea3c93..4ead7a76dfc 100644 --- a/chromium/media/base/null_video_sink_unittest.cc +++ b/chromium/media/base/null_video_sink_unittest.cc @@ -16,7 +16,7 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" -using base::test::RunClosure; +using base::test::RunOnceClosure; using testing::_; using testing::DoAll; using testing::Return; @@ -84,7 +84,7 @@ TEST_F(NullVideoSinkTest, BasicFunctionality) { .WillOnce(Return(test_frame)); WaitableMessageLoopEvent event; EXPECT_CALL(*this, FrameReceived(test_frame)) - .WillOnce(RunClosure(event.GetClosure())); + .WillOnce(RunOnceClosure(event.GetClosure())); event.RunAndWait(); } @@ -103,7 +103,7 @@ TEST_F(NullVideoSinkTest, BasicFunctionality) { .WillOnce(Return(test_frame_2)); EXPECT_CALL(*this, FrameReceived(test_frame)).Times(0); EXPECT_CALL(*this, FrameReceived(test_frame_2)) - .WillOnce(RunClosure(event.GetClosure())); + .WillOnce(RunOnceClosure(event.GetClosure())); event.RunAndWait(); } @@ -147,7 +147,7 @@ TEST_F(NullVideoSinkTest, ClocklessFunctionality) { EXPECT_CALL(*this, Render(current_time + i * interval, current_time + (i + 1) * interval, false)) .WillOnce( - DoAll(RunClosure(event.GetClosure()), Return(test_frame_2))); + DoAll(RunOnceClosure(event.GetClosure()), Return(test_frame_2))); } } event.RunAndWait(); diff --git a/chromium/media/base/overlay_info.h b/chromium/media/base/overlay_info.h index 489e0ec2e61..603a2208f86 100644 --- a/chromium/media/base/overlay_info.h +++ b/chromium/media/base/overlay_info.h @@ -40,9 +40,12 @@ struct MEDIA_EXPORT OverlayInfo { bool is_persistent_video = false; }; -using ProvideOverlayInfoCB = base::Callback<void(const OverlayInfo&)>; +// Used by the WebMediaPlayer to provide overlay information to the decoder, +// which can ask for that information repeatedly (see +// WebMediaPlayerImpl::OnOverlayInfoRequested). +using ProvideOverlayInfoCB = base::RepeatingCallback<void(const OverlayInfo&)>; using RequestOverlayInfoCB = - base::Callback<void(bool, const ProvideOverlayInfoCB&)>; + base::RepeatingCallback<void(bool, ProvideOverlayInfoCB)>; } // namespace media diff --git a/chromium/media/base/pipeline.h b/chromium/media/base/pipeline.h index b09ac029c91..fbd6575e3d0 100644 --- a/chromium/media/base/pipeline.h +++ b/chromium/media/base/pipeline.h @@ -58,7 +58,7 @@ class MEDIA_EXPORT Pipeline { // Executed whenever a text track is added. // The client is expected to create a TextTrack and call |done_cb|. virtual void OnAddTextTrack(const TextTrackConfig& config, - const AddTextTrackDoneCB& done_cb) = 0; + AddTextTrackDoneCB done_cb) = 0; // Executed whenever the pipeline is waiting because of |reason|. virtual void OnWaiting(WaitingReason reason) = 0; @@ -81,6 +81,11 @@ class MEDIA_EXPORT Pipeline { // during playback. virtual void OnAudioDecoderChange(const PipelineDecoderInfo& info) = 0; virtual void OnVideoDecoderChange(const PipelineDecoderInfo& info) = 0; + + // Executed whenever the video frame rate changes. |fps| will be unset if + // the frame rate is unstable. The duration used for the frame rate is + // based on wall clock time, not media time. + virtual void OnVideoFrameRateChange(base::Optional<int> fps) = 0; }; virtual ~Pipeline() {} @@ -106,7 +111,7 @@ class MEDIA_EXPORT Pipeline { virtual void Start(StartType start_type, Demuxer* demuxer, Client* client, - const PipelineStatusCB& seek_cb) = 0; + PipelineStatusCallback seek_cb) = 0; // Track switching works similarly for both audio and video. Callbacks are // used to notify when it is time to procede to the next step, since many of @@ -163,7 +168,7 @@ class MEDIA_EXPORT Pipeline { // // It is an error to call this method if the pipeline has not started or // has been suspended. - virtual void Seek(base::TimeDelta time, const PipelineStatusCB& seek_cb) = 0; + virtual void Seek(base::TimeDelta time, PipelineStatusCallback seek_cb) = 0; // Suspends the pipeline, discarding the current renderer. // @@ -172,14 +177,14 @@ class MEDIA_EXPORT Pipeline { // // It is an error to call this method if the pipeline has not started or is // seeking. - virtual void Suspend(const PipelineStatusCB& suspend_cb) = 0; + virtual void Suspend(PipelineStatusCallback suspend_cb) = 0; // Resume the pipeline and seek to |timestamp|. // // It is an error to call this method if the pipeline has not finished // suspending. virtual void Resume(base::TimeDelta timestamp, - const PipelineStatusCB& seek_cb) = 0; + PipelineStatusCallback seek_cb) = 0; // Returns true if the pipeline has been started via Start(). If IsRunning() // returns true, it is expected that Stop() will be called before destroying diff --git a/chromium/media/base/pipeline_impl.cc b/chromium/media/base/pipeline_impl.cc index 81252cf1238..e1d7be53c5c 100644 --- a/chromium/media/base/pipeline_impl.cc +++ b/chromium/media/base/pipeline_impl.cc @@ -5,6 +5,7 @@ #include "media/base/pipeline_impl.h" #include <algorithm> +#include <utility> #include "base/bind.h" #include "base/bind_helpers.h" @@ -144,6 +145,7 @@ class PipelineImpl::RendererWrapper : public DemuxerHost, void OnVideoConfigChange(const VideoDecoderConfig& config) final; void OnVideoNaturalSizeChange(const gfx::Size& size) final; void OnVideoOpacityChange(bool opaque) final; + void OnVideoFrameRateChange(base::Optional<int> fps) final; // Common handlers for notifications from renderers and demuxer. void OnPipelineError(PipelineStatus error); @@ -297,8 +299,8 @@ void PipelineImpl::RendererWrapper::Start( // Run tasks. pending_callbacks_ = SerialRunner::Run( std::move(fns), - base::BindRepeating(&RendererWrapper::CompleteSeek, - weak_factory_.GetWeakPtr(), base::TimeDelta())); + base::BindOnce(&RendererWrapper::CompleteSeek, weak_factory_.GetWeakPtr(), + base::TimeDelta())); } void PipelineImpl::RendererWrapper::Stop() { @@ -370,8 +372,8 @@ void PipelineImpl::RendererWrapper::Seek(base::TimeDelta time) { // Run tasks. pending_callbacks_ = SerialRunner::Run( std::move(bound_fns), - base::BindRepeating(&RendererWrapper::CompleteSeek, - weak_factory_.GetWeakPtr(), seek_timestamp)); + base::BindOnce(&RendererWrapper::CompleteSeek, weak_factory_.GetWeakPtr(), + seek_timestamp)); } void PipelineImpl::RendererWrapper::Suspend() { @@ -402,8 +404,8 @@ void PipelineImpl::RendererWrapper::Suspend() { // No need to flush the renderer since it's going to be destroyed. pending_callbacks_ = SerialRunner::Run( - std::move(fns), base::BindRepeating(&RendererWrapper::CompleteSuspend, - weak_factory_.GetWeakPtr())); + std::move(fns), base::BindOnce(&RendererWrapper::CompleteSuspend, + weak_factory_.GetWeakPtr())); } void PipelineImpl::RendererWrapper::Resume( @@ -436,8 +438,8 @@ void PipelineImpl::RendererWrapper::Resume( // Queue the asynchronous actions required to start playback. SerialRunner::Queue fns; - fns.Push(base::BindRepeating(&Demuxer::Seek, base::Unretained(demuxer_), - start_timestamp)); + fns.Push(base::BindOnce(&Demuxer::Seek, base::Unretained(demuxer_), + start_timestamp)); fns.Push(base::BindOnce(&RendererWrapper::CreateRenderer, weak_factory_.GetWeakPtr())); @@ -447,8 +449,8 @@ void PipelineImpl::RendererWrapper::Resume( pending_callbacks_ = SerialRunner::Run( std::move(fns), - base::BindRepeating(&RendererWrapper::CompleteSeek, - weak_factory_.GetWeakPtr(), start_timestamp)); + base::BindOnce(&RendererWrapper::CompleteSeek, weak_factory_.GetWeakPtr(), + start_timestamp)); } void PipelineImpl::RendererWrapper::SetPlaybackRate(double playback_rate) { @@ -580,8 +582,7 @@ void PipelineImpl::RendererWrapper::OnBufferedTimeRangesChanged( void PipelineImpl::RendererWrapper::SetDuration(base::TimeDelta duration) { // TODO(alokp): Add thread DCHECK after ensuring that all Demuxer // implementations call DemuxerHost on the media thread. - media_log_->AddEvent(media_log_->CreateTimeEvent(MediaLogEvent::DURATION_SET, - "duration", duration)); + media_log_->AddEvent<MediaLogEvent::kDurationChanged>(duration); main_task_runner_->PostTask( FROM_HERE, base::BindOnce(&PipelineImpl::OnDurationChange, weak_pipeline_, duration)); @@ -602,7 +603,7 @@ void PipelineImpl::RendererWrapper::OnError(PipelineStatus error) { void PipelineImpl::RendererWrapper::OnEnded() { DCHECK(media_task_runner_->BelongsToCurrentThread()); - media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::ENDED)); + media_log_->AddEvent<MediaLogEvent::kEnded>(); if (state_ != kPlaying) return; @@ -660,7 +661,7 @@ void PipelineImpl::RendererWrapper::OnEnabledAudioTracksChanged( enabled_track_ids, GetCurrentTimestamp(), base::BindOnce(&RendererWrapper::OnDemuxerCompletedTrackChange, weak_factory_.GetWeakPtr(), - base::Passed(&change_completed_cb))); + std::move(change_completed_cb))); } void PipelineImpl::OnSelectedVideoTrackChanged( @@ -700,7 +701,7 @@ void PipelineImpl::RendererWrapper::OnSelectedVideoTrackChanged( tracks, GetCurrentTimestamp(), base::BindOnce(&RendererWrapper::OnDemuxerCompletedTrackChange, weak_factory_.GetWeakPtr(), - base::Passed(&change_completed_cb))); + std::move(change_completed_cb))); } void PipelineImpl::RendererWrapper::OnDemuxerCompletedTrackChange( @@ -818,6 +819,15 @@ void PipelineImpl::RendererWrapper::OnVideoOpacityChange(bool opaque) { weak_pipeline_, opaque)); } +void PipelineImpl::RendererWrapper::OnVideoFrameRateChange( + base::Optional<int> fps) { + DCHECK(media_task_runner_->BelongsToCurrentThread()); + + main_task_runner_->PostTask( + FROM_HERE, base::BindOnce(&PipelineImpl::OnVideoFrameRateChange, + weak_pipeline_, fps)); +} + void PipelineImpl::RendererWrapper::OnAudioConfigChange( const AudioDecoderConfig& config) { DCHECK(media_task_runner_->BelongsToCurrentThread()); @@ -890,7 +900,11 @@ void PipelineImpl::RendererWrapper::SetState(State next_state) { << PipelineImpl::GetStateString(next_state); state_ = next_state; - media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(next_state)); + + // TODO(tmathmeyer) Make State serializable so GetStateString won't need + // to be called here. + media_log_->AddEvent<MediaLogEvent::kPipelineStateChange>( + std::string(PipelineImpl::GetStateString(next_state))); } void PipelineImpl::RendererWrapper::CompleteSeek(base::TimeDelta seek_time, @@ -1155,7 +1169,7 @@ PipelineImpl::~PipelineImpl() { void PipelineImpl::Start(StartType start_type, Demuxer* demuxer, Client* client, - const PipelineStatusCB& seek_cb) { + PipelineStatusCallback seek_cb) { DVLOG(2) << __func__ << ": start_type=" << static_cast<int>(start_type); DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(demuxer); @@ -1165,7 +1179,7 @@ void PipelineImpl::Start(StartType start_type, DCHECK(!client_); DCHECK(!seek_cb_); client_ = client; - seek_cb_ = seek_cb; + seek_cb_ = std::move(seek_cb); last_media_time_ = base::TimeDelta(); seek_time_ = kNoTimestamp; @@ -1216,19 +1230,19 @@ void PipelineImpl::Stop() { weak_factory_.InvalidateWeakPtrs(); } -void PipelineImpl::Seek(base::TimeDelta time, const PipelineStatusCB& seek_cb) { +void PipelineImpl::Seek(base::TimeDelta time, PipelineStatusCallback seek_cb) { DVLOG(2) << __func__ << " to " << time.InMicroseconds(); DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(seek_cb); if (!IsRunning()) { DLOG(ERROR) << "Media pipeline isn't running. Ignoring Seek()."; - seek_cb.Run(PIPELINE_ERROR_INVALID_STATE); + std::move(seek_cb).Run(PIPELINE_ERROR_INVALID_STATE); return; } DCHECK(!seek_cb_); - seek_cb_ = seek_cb; + seek_cb_ = std::move(seek_cb); seek_time_ = time; last_media_time_ = base::TimeDelta(); media_task_runner_->PostTask( @@ -1237,13 +1251,13 @@ void PipelineImpl::Seek(base::TimeDelta time, const PipelineStatusCB& seek_cb) { base::Unretained(renderer_wrapper_.get()), time)); } -void PipelineImpl::Suspend(const PipelineStatusCB& suspend_cb) { +void PipelineImpl::Suspend(PipelineStatusCallback suspend_cb) { DVLOG(2) << __func__; DCHECK(suspend_cb); DCHECK(IsRunning()); DCHECK(!suspend_cb_); - suspend_cb_ = suspend_cb; + suspend_cb_ = std::move(suspend_cb); media_task_runner_->PostTask( FROM_HERE, base::BindOnce(&RendererWrapper::Suspend, @@ -1251,14 +1265,14 @@ void PipelineImpl::Suspend(const PipelineStatusCB& suspend_cb) { } void PipelineImpl::Resume(base::TimeDelta time, - const PipelineStatusCB& seek_cb) { + PipelineStatusCallback seek_cb) { DVLOG(2) << __func__; DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(seek_cb); DCHECK(IsRunning()); DCHECK(!seek_cb_); - seek_cb_ = seek_cb; + seek_cb_ = std::move(seek_cb); seek_time_ = time; last_media_time_ = base::TimeDelta(); @@ -1531,6 +1545,15 @@ void PipelineImpl::OnVideoOpacityChange(bool opaque) { client_->OnVideoOpacityChange(opaque); } +void PipelineImpl::OnVideoFrameRateChange(base::Optional<int> fps) { + DVLOG(2) << __func__; + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(IsRunning()); + + DCHECK(client_); + client_->OnVideoFrameRateChange(fps); +} + void PipelineImpl::OnAudioConfigChange(const AudioDecoderConfig& config) { DVLOG(2) << __func__; DCHECK(thread_checker_.CalledOnValidThread()); diff --git a/chromium/media/base/pipeline_impl.h b/chromium/media/base/pipeline_impl.h index 0f4a036c45d..101de818805 100644 --- a/chromium/media/base/pipeline_impl.h +++ b/chromium/media/base/pipeline_impl.h @@ -92,11 +92,11 @@ class MEDIA_EXPORT PipelineImpl : public Pipeline { void Start(StartType start_type, Demuxer* demuxer, Client* client, - const PipelineStatusCB& seek_cb) override; + PipelineStatusCallback seek_cb) override; void Stop() override; - void Seek(base::TimeDelta time, const PipelineStatusCB& seek_cb) override; - void Suspend(const PipelineStatusCB& suspend_cb) override; - void Resume(base::TimeDelta time, const PipelineStatusCB& seek_cb) override; + void Seek(base::TimeDelta time, PipelineStatusCallback seek_cb) override; + void Suspend(PipelineStatusCallback suspend_cb) override; + void Resume(base::TimeDelta time, PipelineStatusCallback seek_cb) override; bool IsRunning() const override; bool IsSuspended() const override; double GetPlaybackRate() const override; @@ -163,6 +163,7 @@ class MEDIA_EXPORT PipelineImpl : public Pipeline { void OnAudioDecoderChange(const PipelineDecoderInfo& info); void OnVideoDecoderChange(const PipelineDecoderInfo& info); void OnRemotePlayStateChange(MediaStatus::State state); + void OnVideoFrameRateChange(base::Optional<int> fps); // Task completion callbacks from RendererWrapper. void OnSeekDone(bool is_suspended); @@ -180,10 +181,10 @@ class MEDIA_EXPORT PipelineImpl : public Pipeline { std::unique_ptr<RendererWrapper> renderer_wrapper_; // Temporary callback used for Start(), Seek(), and Resume(). - PipelineStatusCB seek_cb_; + PipelineStatusCallback seek_cb_; // Temporary callback used for Suspend(). - PipelineStatusCB suspend_cb_; + PipelineStatusCallback suspend_cb_; // Current playback rate (>= 0.0). This value is set immediately via // SetPlaybackRate() and a task is dispatched on the task runner to notify diff --git a/chromium/media/base/pipeline_impl_unittest.cc b/chromium/media/base/pipeline_impl_unittest.cc index c5897b78633..444f22ca07c 100644 --- a/chromium/media/base/pipeline_impl_unittest.cc +++ b/chromium/media/base/pipeline_impl_unittest.cc @@ -176,9 +176,9 @@ class PipelineImplTest : public ::testing::Test { void StartPipeline( Pipeline::StartType start_type = Pipeline::StartType::kNormal) { EXPECT_CALL(callbacks_, OnWaiting(_)).Times(0); - pipeline_->Start( - start_type, demuxer_.get(), &callbacks_, - base::Bind(&CallbackHelper::OnStart, base::Unretained(&callbacks_))); + pipeline_->Start(start_type, demuxer_.get(), &callbacks_, + base::BindOnce(&CallbackHelper::OnStart, + base::Unretained(&callbacks_))); } void SetRendererPostStartExpectations() { @@ -250,8 +250,8 @@ class PipelineImplTest : public ::testing::Test { EXPECT_CALL(*renderer_, OnSetCdm(_, _)).WillOnce(RunOnceCallback<1>(true)); EXPECT_CALL(callbacks_, OnCdmAttached(expected_result)); pipeline_->SetCdm(&cdm_context_, - base::BindRepeating(&CallbackHelper::OnCdmAttached, - base::Unretained(&callbacks_))); + base::BindOnce(&CallbackHelper::OnCdmAttached, + base::Unretained(&callbacks_))); base::RunLoop().RunUntilIdle(); } @@ -275,8 +275,8 @@ class PipelineImplTest : public ::testing::Test { } void DoSeek(const base::TimeDelta& seek_time) { - pipeline_->Seek(seek_time, base::Bind(&CallbackHelper::OnSeek, - base::Unretained(&callbacks_))); + pipeline_->Seek(seek_time, base::BindOnce(&CallbackHelper::OnSeek, + base::Unretained(&callbacks_))); base::RunLoop().RunUntilIdle(); } @@ -287,8 +287,8 @@ class PipelineImplTest : public ::testing::Test { } void DoSuspend() { - pipeline_->Suspend( - base::Bind(&CallbackHelper::OnSuspend, base::Unretained(&callbacks_))); + pipeline_->Suspend(base::BindOnce(&CallbackHelper::OnSuspend, + base::Unretained(&callbacks_))); base::RunLoop().RunUntilIdle(); ResetRenderer(); } @@ -320,8 +320,8 @@ class PipelineImplTest : public ::testing::Test { } void DoResume(const base::TimeDelta& seek_time) { - pipeline_->Resume(seek_time, base::Bind(&CallbackHelper::OnResume, - base::Unretained(&callbacks_))); + pipeline_->Resume(seek_time, base::BindOnce(&CallbackHelper::OnResume, + base::Unretained(&callbacks_))); base::RunLoop().RunUntilIdle(); } @@ -546,7 +546,7 @@ TEST_F(PipelineImplTest, EncryptedStream_SetCdmAfterStart) { .WillOnce(RunClosure(run_loop.QuitClosure())); pipeline_->Start( Pipeline::StartType::kNormal, demuxer_.get(), &callbacks_, - base::Bind(&CallbackHelper::OnStart, base::Unretained(&callbacks_))); + base::BindOnce(&CallbackHelper::OnStart, base::Unretained(&callbacks_))); run_loop.Run(); ExpectRendererInitialization(); @@ -584,7 +584,7 @@ TEST_F(PipelineImplTest, SeekAfterError) { EXPECT_CALL(callbacks_, OnSeek(PIPELINE_ERROR_INVALID_STATE)); pipeline_->Seek( base::TimeDelta::FromMilliseconds(100), - base::Bind(&CallbackHelper::OnSeek, base::Unretained(&callbacks_))); + base::BindOnce(&CallbackHelper::OnSeek, base::Unretained(&callbacks_))); base::RunLoop().RunUntilIdle(); } @@ -740,8 +740,8 @@ TEST_F(PipelineImplTest, ErrorDuringSeek) { .WillOnce(RunOnceCallback<1>(PIPELINE_ERROR_READ)); EXPECT_CALL(*demuxer_, Stop()); - pipeline_->Seek(seek_time, base::Bind(&CallbackHelper::OnSeek, - base::Unretained(&callbacks_))); + pipeline_->Seek(seek_time, base::BindOnce(&CallbackHelper::OnSeek, + base::Unretained(&callbacks_))); EXPECT_CALL(callbacks_, OnSeek(PIPELINE_ERROR_READ)) .WillOnce(Stop(pipeline_.get())); base::RunLoop().RunUntilIdle(); @@ -822,8 +822,8 @@ TEST_F(PipelineImplTest, GetMediaTimeAfterSeek) { // notified of the seek (via media thread). base::TimeDelta kSeekTime = kMediaTime - base::TimeDelta::FromSeconds(1); ExpectSeek(kSeekTime, false); - pipeline_->Seek(kSeekTime, base::Bind(&CallbackHelper::OnSeek, - base::Unretained(&callbacks_))); + pipeline_->Seek(kSeekTime, base::BindOnce(&CallbackHelper::OnSeek, + base::Unretained(&callbacks_))); // Verify pipeline returns the seek time in spite of renderer returning the // stale media time. @@ -997,7 +997,7 @@ class PipelineTeardownTest : public PipelineImplTest { pipeline_->Seek( base::TimeDelta::FromSeconds(10), - base::Bind(&CallbackHelper::OnSeek, base::Unretained(&callbacks_))); + base::BindOnce(&CallbackHelper::OnSeek, base::Unretained(&callbacks_))); base::RunLoop().RunUntilIdle(); } diff --git a/chromium/media/base/player_tracker.h b/chromium/media/base/player_tracker.h index 57c59ab5bfd..8c7bf35f9f5 100644 --- a/chromium/media/base/player_tracker.h +++ b/chromium/media/base/player_tracker.h @@ -22,8 +22,8 @@ class MEDIA_EXPORT PlayerTracker { // - |cdm_unset_cb| is fired when the CDM is detached from the player. The // player should stop using the CDM and release any ref-count to the CDM. // Returns a registration ID which can be used to unregister a player. - virtual int RegisterPlayer(const base::Closure& new_key_cb, - const base::Closure& cdm_unset_cb) = 0; + virtual int RegisterPlayer(base::RepeatingClosure new_key_cb, + base::RepeatingClosure cdm_unset_cb) = 0; // Unregisters a previously registered player. This should be called when // the CDM is detached from the player (e.g. setMediaKeys(0)), or when the diff --git a/chromium/media/base/provision_fetcher.h b/chromium/media/base/provision_fetcher.h index ed08751078a..e63b4786be2 100644 --- a/chromium/media/base/provision_fetcher.h +++ b/chromium/media/base/provision_fetcher.h @@ -34,7 +34,8 @@ class ProvisionFetcher { ResponseCB response_cb) = 0; }; -using CreateFetcherCB = base::Callback<std::unique_ptr<ProvisionFetcher>()>; +using CreateFetcherCB = + base::RepeatingCallback<std::unique_ptr<ProvisionFetcher>()>; } // namespace media diff --git a/chromium/media/base/renderer_client.h b/chromium/media/base/renderer_client.h index 249c5ee76da..f11f2132430 100644 --- a/chromium/media/base/renderer_client.h +++ b/chromium/media/base/renderer_client.h @@ -54,6 +54,11 @@ class MEDIA_EXPORT RendererClient { // TODO(crbug.com/988535): Used by AudioRendererImpl. This can be removed // when the bug is resolved. virtual bool IsVideoStreamAvailable(); + + // Called when the bucketed frames per second has changed. |fps| will be + // unset if the frame rate is unstable. The duration used for the frame rate + // is based on the wall clock time, not the media time. + virtual void OnVideoFrameRateChange(base::Optional<int> fps) = 0; }; } // namespace media diff --git a/chromium/media/base/renderer_factory.h b/chromium/media/base/renderer_factory.h index d362347e412..1001d7e0ff0 100644 --- a/chromium/media/base/renderer_factory.h +++ b/chromium/media/base/renderer_factory.h @@ -41,7 +41,7 @@ class MEDIA_EXPORT RendererFactory { const scoped_refptr<base::TaskRunner>& worker_task_runner, AudioRendererSink* audio_renderer_sink, VideoRendererSink* video_renderer_sink, - const RequestOverlayInfoCB& request_overlay_info_cb, + RequestOverlayInfoCB request_overlay_info_cb, const gfx::ColorSpace& target_color_space) = 0; // Returns the MediaResource::Type that should be used with the renderers diff --git a/chromium/media/base/renderer_factory_selector_unittest.cc b/chromium/media/base/renderer_factory_selector_unittest.cc index e93e8bbd2d4..c4900ab51e8 100644 --- a/chromium/media/base/renderer_factory_selector_unittest.cc +++ b/chromium/media/base/renderer_factory_selector_unittest.cc @@ -25,7 +25,7 @@ class RendererFactorySelectorTest : public testing::Test { const scoped_refptr<base::TaskRunner>& worker_task_runner, AudioRendererSink* audio_renderer_sink, VideoRendererSink* video_renderer_sink, - const RequestOverlayInfoCB& request_overlay_info_cb, + RequestOverlayInfoCB request_overlay_info_cb, const gfx::ColorSpace& target_color_space) override { return nullptr; } diff --git a/chromium/media/base/serial_runner.cc b/chromium/media/base/serial_runner.cc index 176a9c9f722..3bc61077257 100644 --- a/chromium/media/base/serial_runner.cc +++ b/chromium/media/base/serial_runner.cc @@ -103,9 +103,9 @@ void SerialRunner::RunNextInSeries(PipelineStatus last_status) { BoundPipelineStatusCallback bound_fn = bound_fns_.Pop(); std::move(bound_fn).Run( - base::BindRepeating(&RunOnTaskRunner, task_runner_, - base::BindRepeating(&SerialRunner::RunNextInSeries, - weak_factory_.GetWeakPtr()))); + base::BindOnce(&RunOnTaskRunner, task_runner_, + base::BindRepeating(&SerialRunner::RunNextInSeries, + weak_factory_.GetWeakPtr()))); } } // namespace media diff --git a/chromium/media/base/serial_runner_unittest.cc b/chromium/media/base/serial_runner_unittest.cc index f619951a8be..4d7b4435b6a 100644 --- a/chromium/media/base/serial_runner_unittest.cc +++ b/chromium/media/base/serial_runner_unittest.cc @@ -100,10 +100,9 @@ class SerialRunnerTest : public ::testing::Test { void StartRunnerInternal(SerialRunner::Queue bound_fns) { inside_start_ = true; - runner_ = - SerialRunner::Run(std::move(bound_fns), - base::BindRepeating(&SerialRunnerTest::DoneCallback, - base::Unretained(this))); + runner_ = SerialRunner::Run(std::move(bound_fns), + base::BindOnce(&SerialRunnerTest::DoneCallback, + base::Unretained(this))); inside_start_ = false; } diff --git a/chromium/media/base/silent_sink_suspender_unittest.cc b/chromium/media/base/silent_sink_suspender_unittest.cc index e6b2c851c47..3d8316bedfc 100644 --- a/chromium/media/base/silent_sink_suspender_unittest.cc +++ b/chromium/media/base/silent_sink_suspender_unittest.cc @@ -2,19 +2,19 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "media/base/silent_sink_suspender.h" + #include "base/run_loop.h" +#include "base/test/gmock_callback_support.h" #include "base/test/test_message_loop.h" #include "media/base/fake_audio_render_callback.h" #include "media/base/mock_audio_renderer_sink.h" -#include "media/base/silent_sink_suspender.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" -namespace media { +using base::test::RunClosure; -ACTION_P(RunClosure, closure) { - closure.Run(); -} +namespace media { class SilentSinkSuspenderTest : public testing::Test { public: @@ -184,4 +184,4 @@ TEST_F(SilentSinkSuspenderTest, MultipleResume) { 0); } -} // namespace content +} // namespace media diff --git a/chromium/media/base/sinc_resampler.cc b/chromium/media/base/sinc_resampler.cc index b3dd4fd7fcc..d9965ca9ec2 100644 --- a/chromium/media/base/sinc_resampler.cc +++ b/chromium/media/base/sinc_resampler.cc @@ -310,6 +310,14 @@ void SincResampler::Flush() { UpdateRegions(false); } +int SincResampler::GetMaxInputFramesRequested( + int output_frames_requested) const { + const int num_chunks = static_cast<int>( + std::ceil(static_cast<float>(output_frames_requested) / chunk_size_)); + + return num_chunks * request_frames_; +} + double SincResampler::BufferedFrames() const { return buffer_primed_ ? request_frames_ - virtual_source_idx_ : 0; } diff --git a/chromium/media/base/sinc_resampler.h b/chromium/media/base/sinc_resampler.h index 42e2161257a..b8f42d691a2 100644 --- a/chromium/media/base/sinc_resampler.h +++ b/chromium/media/base/sinc_resampler.h @@ -61,6 +61,10 @@ class MEDIA_EXPORT SincResampler { // kKernelSize / (2 * io_sample_rate_ratio). See the .cc file for details. int ChunkSize() const { return chunk_size_; } + // Returns the max number of frames that could be requested (via multiple + // calls to |read_cb_|) during one Resample(|output_frames_requested|) call. + int GetMaxInputFramesRequested(int output_frames_requested) const; + // Guarantees that ChunkSize() will not change between calls by initializing // the input buffer with silence. Note, this will cause the first few samples // of output to be biased towards silence. Must be called again after Flush(). diff --git a/chromium/media/base/sinc_resampler_unittest.cc b/chromium/media/base/sinc_resampler_unittest.cc index d10b5f0e1a6..0197ffdec6d 100644 --- a/chromium/media/base/sinc_resampler_unittest.cc +++ b/chromium/media/base/sinc_resampler_unittest.cc @@ -408,4 +408,46 @@ INSTANTIATE_TEST_SUITE_P( std::make_tuple(96000, 192000, kResamplingRMSError, -73.52), std::make_tuple(192000, 192000, kResamplingRMSError, -73.52))); +// Verify the resampler properly reports the max number of input frames it would +// request. +TEST(SincResamplerTest, GetMaxInputFramesRequestedTest) { + SincResampler resampler(kSampleRateRatio, SincResampler::kDefaultRequestSize, + SincResampler::ReadCB()); + + EXPECT_EQ(SincResampler::kDefaultRequestSize, + resampler.GetMaxInputFramesRequested(resampler.ChunkSize())); + + // Request sizes smaller than ChunkSize should still trigger 1 read. + EXPECT_EQ(SincResampler::kDefaultRequestSize, + resampler.GetMaxInputFramesRequested(resampler.ChunkSize() - 10)); + + // Request sizes bigger than ChunkSize can trigger multiple reads. + EXPECT_EQ(2 * SincResampler::kDefaultRequestSize, + resampler.GetMaxInputFramesRequested(resampler.ChunkSize() + 10)); + + // The number of input frames requested should grow proportionally to the + // output frames requested. + EXPECT_EQ( + 5 * SincResampler::kDefaultRequestSize, + resampler.GetMaxInputFramesRequested(4 * resampler.ChunkSize() + 10)); + + const int kCustomRequestSize = SincResampler::kDefaultRequestSize + 128; + SincResampler custom_size_resampler(kSampleRateRatio, kCustomRequestSize, + SincResampler::ReadCB()); + + // The input frames requested should be a multiple of the request size. + EXPECT_EQ(2 * kCustomRequestSize, + custom_size_resampler.GetMaxInputFramesRequested( + custom_size_resampler.ChunkSize() + 10)); + + // Verify we get results with both downsampling and upsampling ratios. + SincResampler inverse_ratio_resampler(1.0 / kSampleRateRatio, + SincResampler::kDefaultRequestSize, + SincResampler::ReadCB()); + + EXPECT_EQ(2 * SincResampler::kDefaultRequestSize, + inverse_ratio_resampler.GetMaxInputFramesRequested( + inverse_ratio_resampler.ChunkSize() + 10)); +} + } // namespace media diff --git a/chromium/media/base/speech_recognition_client.h b/chromium/media/base/speech_recognition_client.h new file mode 100644 index 00000000000..1290cd88c25 --- /dev/null +++ b/chromium/media/base/speech_recognition_client.h @@ -0,0 +1,28 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEDIA_BASE_SPEECH_RECOGNITION_CLIENT_H_ +#define MEDIA_BASE_SPEECH_RECOGNITION_CLIENT_H_ + +#include <memory> + +#include "media/base/audio_buffer.h" +#include "media/base/media_export.h" + +namespace media { + +// The interface for the speech recognition client used to transcribe audio into +// captions. +class MEDIA_EXPORT SpeechRecognitionClient { + public: + virtual ~SpeechRecognitionClient() = default; + + virtual void AddAudio(scoped_refptr<AudioBuffer> buffer) = 0; + + virtual bool IsSpeechRecognitionAvailable() = 0; +}; + +} // namespace media + +#endif // MEDIA_BASE_SPEECH_RECOGNITION_CLIENT_H_ diff --git a/chromium/media/base/status.cc b/chromium/media/base/status.cc new file mode 100644 index 00000000000..003b6575330 --- /dev/null +++ b/chromium/media/base/status.cc @@ -0,0 +1,76 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/base/status.h" + +#include <memory> +#include "media/base/media_serializers.h" + +namespace media { + +Status::Status() = default; + +Status::Status(StatusCode code, + base::StringPiece message, + const base::Location& location) { + DCHECK(code != StatusCode::kOk); + data_ = std::make_unique<StatusInternal>(code, message.as_string()); + AddFrame(location); +} + +// Copy Constructor +Status::Status(const Status& copy) { + *this = copy; +} + +Status& Status::operator=(const Status& copy) { + if (copy.is_ok()) { + data_.reset(); + return *this; + } + + data_ = std::make_unique<StatusInternal>(copy.code(), copy.message()); + for (const base::Value& frame : copy.data_->frames) + data_->frames.push_back(frame.Clone()); + for (const Status& err : copy.data_->causes) + data_->causes.push_back(err); + data_->data = copy.data_->data.Clone(); + return *this; +} + +// Allow move. +Status::Status(Status&&) = default; +Status& Status::operator=(Status&&) = default; + +Status::~Status() = default; + +Status::StatusInternal::StatusInternal(StatusCode code, std::string message) + : code(code), + message(std::move(message)), + data(base::Value(base::Value::Type::DICTIONARY)) {} + +Status::StatusInternal::~StatusInternal() = default; + +Status&& Status::AddHere(const base::Location& location) && { + DCHECK(data_); + AddFrame(location); + return std::move(*this); +} + +Status&& Status::AddCause(Status&& cause) && { + DCHECK(data_ && cause.data_); + data_->causes.push_back(std::move(cause)); + return std::move(*this); +} + +void Status::AddFrame(const base::Location& location) { + DCHECK(data_); + data_->frames.push_back(MediaSerialize(location)); +} + +Status OkStatus() { + return Status(); +} + +} // namespace media diff --git a/chromium/media/base/status.h b/chromium/media/base/status.h new file mode 100644 index 00000000000..c06635b27a8 --- /dev/null +++ b/chromium/media/base/status.h @@ -0,0 +1,214 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEDIA_BASE_STATUS_H_ +#define MEDIA_BASE_STATUS_H_ + +#include <memory> +#include <string> +#include <utility> +#include <vector> + +#include "base/location.h" +#include "base/strings/string_piece.h" +#include "base/values.h" +#include "media/base/media_export.h" +#include "media/base/media_serializers_base.h" +#include "media/base/status_codes.h" + +// Mojo namespaces for serialization friend declarations. +namespace mojo { +template <typename T, typename U> +struct StructTraits; +} // namespace mojo + +namespace media { + +namespace mojom { +class StatusDataView; +} + +// Status is meant to be a relatively small (sizeof(void*) bytes) object +// that can be returned as a status value from functions or passed to callbacks +// that want a report of status. Status allows attaching of arbitrary named +// data, other Status' as causes, and stack frames, which can all be logged +// and reported throughout the media stack. The status code and message are +// immutable and can be used to give a stable numeric ID for any error +// generated by media code. +// There is also an OK state which can't hold any data and is only for +// successful returns. +class MEDIA_EXPORT Status { + public: + // Default constructor can be used for OkStatus(); + Status(); + + // Constructor to create a new Status from a numeric code & message. + // These are immutable; if you'd like to change them, then you likely should + // create a new Status. {} or OkStatus() should be used to create a + // success status. + // NOTE: This should never be given a location parameter when called - It is + // defaulted in order to grab the caller location. + Status(StatusCode code, + base::StringPiece message = "", + const base::Location& location = base::Location::Current()); + + // Copy Constructor & assignment. (Mojo uses both of these) + Status(const Status&); + Status& operator=(const Status&); + + // Allows move. + Status(Status&&); + Status& operator=(Status&&); + + // Needs an out of line destructor... + ~Status(); + + bool is_ok() const { return !data_; } + + // Getters for internal fields + const std::string& message() const { + DCHECK(data_); + return data_->message; + } + + StatusCode code() const { return data_ ? data_->code : StatusCode::kOk; } + + // Adds the current location to Status as it’s passed upwards. + // This does not need to be called at every location that touches it, but + // should be called for those locations where the path is ambiguous or + // critical. This can be especially helpful across IPC boundaries. This will + // fail on an OK status. + // NOTE: This should never be given a parameter when called - It is defaulted + // in order to grab the caller location. + Status&& AddHere( + const base::Location& location = base::Location::Current()) &&; + + // Add |cause| as the error that triggered this one. For example, + // DecoderStream might return kDecoderSelectionFailed with one or more causes + // that are the specific errors from the decoders that it tried. + Status&& AddCause(Status&& cause) &&; + void AddCause(Status&& cause) &; + + // Allows us to append any datatype which can be converted to + // an int/bool/string/base::Value. Any existing data associated with |key| + // will be overwritten by |value|. This will fail on an OK status. + template <typename T> + Status&& WithData(const char* key, const T& value) && { + DCHECK(data_); + data_->data.SetKey(key, MediaSerialize(value)); + return std::move(*this); + } + + template <typename T> + void WithData(const char* key, const T& value) & { + DCHECK(data_); + data_->data.SetKey(key, MediaSerialize(value)); + } + + private: + // Private helper to add the current stack frame to the error trace. + void AddFrame(const base::Location& location); + + // Keep the internal data in a unique ptr to minimize size of OK errors. + struct MEDIA_EXPORT StatusInternal { + StatusInternal(StatusCode code, std::string message); + ~StatusInternal(); + + // The current error code + StatusCode code = StatusCode::kOk; + + // The current error message (Can be used for + // https://developer.mozilla.org/en-US/docs/Web/API/Status) + std::string message; + + // Stack frames + std::vector<base::Value> frames; + + // Causes + std::vector<Status> causes; + + // Data attached to the error + base::Value data; + }; + + // Allow self-serialization + friend struct internal::MediaSerializer<Status>; + + // Allow mojo-serialization + friend struct mojo::StructTraits<media::mojom::StatusDataView, Status>; + + // A null internals is an implicit OK. + std::unique_ptr<StatusInternal> data_; +}; + +// Convenience function to return |kOk|. +// OK won't have a message, trace, or data associated with them, and DCHECK +// if they are added. +MEDIA_EXPORT Status OkStatus(); + +// Helper class to allow returning a |T| or a Status. Typical usage: +// +// ErrorOr<std::unique_ptr<MyObject>> FactoryFn() { +// if (success) +// return std::make_unique<MyObject>(); +// return Status(StatusCodes::kSomethingBadHappened); +// } +// +// auto result = FactoryFn(); +// if (result.has_error()) return std::move(result.error()); +// my_object_ = std::move(result.value()); +// +// Also useful if one would like to get an enum class return value, unless an +// error occurs: +// +// enum class ResultType { kNeedMoreInput, kOutputIsReady, kFormatChanged }; +// +// ErrorOr<ResultType> Foo() { ... } +// +// auto result = Foo(); +// if (result.has_error()) return std::move(result.error()); +// switch (result.value()) { +// case ResultType::kNeedMoreInput: +// ... +// } +template <typename T> +class ErrorOr { + public: + // All of these may be implicit, so that one may just return Status or + // the value in question. + ErrorOr(Status&& error) : error_(std::move(error)) {} + ErrorOr(const Status& error) : error_(error) {} + ErrorOr(T&& value) : value_(std::move(value)) {} + ErrorOr(const T& value) : value_(value) {} + + ~ErrorOr() = default; + + // Move- and copy- construction and assignment are okay. + ErrorOr(const ErrorOr&) = default; + ErrorOr(ErrorOr&&) = default; + ErrorOr& operator=(ErrorOr&) = default; + ErrorOr& operator=(ErrorOr&&) = default; + + // Do we have a value? + bool has_value() const { return value_.has_value(); } + + // Since we often test for errors, provide this too. + bool has_error() const { return !has_value(); } + + // Return the error, if we have one. Up to the caller to make sure that we + // have one via |!has_value()|. + Status& error() { return *error_; } + + // Return a ref to the value. It's up to the caller to verify that we have a + // value before calling this. + T& value() { return *value_; } + + private: + base::Optional<Status> error_; + base::Optional<T> value_; +}; + +} // namespace media + +#endif // MEDIA_BASE_STATUS_H_ diff --git a/chromium/media/base/status_codes.cc b/chromium/media/base/status_codes.cc new file mode 100644 index 00000000000..2aba1c20da3 --- /dev/null +++ b/chromium/media/base/status_codes.cc @@ -0,0 +1,15 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/base/status_codes.h" + +namespace media { + +// TODO(tmathmeyer) consider a way to get the names, since we don't have +// the easy c++20 way yet. +std::ostream& operator<<(std::ostream& os, const StatusCode& code) { + return os << std::hex << static_cast<StatusCodeType>(code); +} + +} // namespace media diff --git a/chromium/media/base/status_codes.h b/chromium/media/base/status_codes.h new file mode 100644 index 00000000000..3f161c20316 --- /dev/null +++ b/chromium/media/base/status_codes.h @@ -0,0 +1,81 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEDIA_BASE_STATUS_CODES_H_ +#define MEDIA_BASE_STATUS_CODES_H_ + +#include <cstdint> +#include <limits> +#include <ostream> + +#include "media/base/media_export.h" + +namespace media { + +using StatusCodeType = int32_t; +// TODO(tmathmeyer, liberato, xhwang) These numbers are not yet finalized: +// DO NOT use them for reporting statistics, and DO NOT report them to any +// user-facing feature, including media log. + +// Codes are grouped with a bitmask: +// 0xFFFFFFFF +// └─┬┘├┘└┴ enumeration within the group +// │ └─ group code +// └─ reserved for now +// 256 groups is more than anyone will ever need on a computer. +enum class StatusCode : StatusCodeType { + kOk = 0, + + // Decoder Errors: 0x01 + kDecoderInitializeNeverCompleted = 0x00000101, + kDecoderFailedDecode = 0x00000102, + kDecoderUnsupportedProfile = 0x00000103, + kDecoderUnsupportedCodec = 0x00000104, + kDecoderUnsupportedConfig = 0x00000105, + kEncryptedContentUnsupported = 0x00000106, + kClearContentUnsupported = 0x00000107, + kDecoderMissingCdmForEncryptedContent = 0x00000108, + kDecoderFailedInitialization = 0x00000109, + kDecoderCantChangeCodec = 0x0000010A, + kDecoderFailedCreation = 0x0000010B, + kInitializationUnspecifiedFailure = 0x0000010C, + + // Windows Errors: 0x02 + kWindowsWrappedHresult = 0x00000201, + kWindowsApiNotAvailible = 0x00000202, + + // D3D11VideoDecoder Errors: 0x03 + kCantMakeContextCurrent = 0x00000301, + kCantPostTexture = 0x00000302, + kCantPostAcquireStream = 0x00000303, + + // MojoDecoder Errors: 0x04 + kMojoDecoderNoWrappedDecoder = 0x00000401, + kMojoDecoderStoppedBeforeInitDone = 0x00000402, + kMojoDecoderUnsupported = 0x00000403, + kMojoDecoderNoConnection = 0x00000404, + kMojoDecoderDeletedWithoutInitialization = 0x00000405, + + // Chromeos Errors: 0x05 + kChromeOSVideoDecoderNoDecoders = 0x00000501, + kV4l2NoDevice = 0x00000502, + kV4l2FailedToStopStreamQueue = 0x00000503, + kV4l2NoDecoder = 0x00000504, + kV4l2FailedFileCapabilitiesCheck = 0x00000505, + kV4l2FailedResourceAllocation = 0x00000506, + kV4l2BadFormat = 0x00000507, + kVaapiReinitializedDuringDecode = 0x00000508, + kVaapiFailedAcceleratorCreation = 0x00000509, + + // Special codes + kGenericErrorPleaseRemove = 0x79999999, + kCodeOnlyForTesting = std::numeric_limits<StatusCodeType>::max(), + kMaxValue = kCodeOnlyForTesting, +}; + +MEDIA_EXPORT std::ostream& operator<<(std::ostream& os, const StatusCode& code); + +} // namespace media + +#endif // MEDIA_BASE_STATUS_CODES_H_ diff --git a/chromium/media/base/status_unittest.cc b/chromium/media/base/status_unittest.cc new file mode 100644 index 00000000000..b9e6604c7ac --- /dev/null +++ b/chromium/media/base/status_unittest.cc @@ -0,0 +1,246 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <sstream> +#include <string> + +#include "base/json/json_writer.h" +#include "base/macros.h" +#include "media/base/media_serializers.h" +#include "media/base/status.h" +#include "media/base/test_helpers.h" +#include "testing/gtest/include/gtest/gtest.h" + +using ::testing::HasSubstr; + +namespace media { + +class UselessThingToBeSerialized { + public: + explicit UselessThingToBeSerialized(const char* name) : name_(name) {} + const char* name_; +}; + +namespace internal { + +template <> +struct MediaSerializer<UselessThingToBeSerialized> { + static base::Value Serialize(const UselessThingToBeSerialized& t) { + return base::Value(t.name_); + } +}; + +} // namespace internal + +// Friend class of MediaLog for access to internal constants. +class StatusTest : public testing::Test { + public: + Status DontFail() { return OkStatus(); } + + Status FailEasily() { + return Status(StatusCode::kCodeOnlyForTesting, "Message"); + } + + Status FailRecursively(unsigned int count) { + if (!count) { + return FailEasily(); + } + return FailRecursively(count - 1).AddHere(); + } + + template <typename T> + Status FailWithData(const char* key, const T& t) { + return Status(StatusCode::kCodeOnlyForTesting, "Message", FROM_HERE) + .WithData(key, t); + } + + Status FailWithCause() { + Status err = FailEasily(); + return FailEasily().AddCause(std::move(err)); + } + + Status DoSomethingGiveItBack(Status me) { + me.WithData("data", "Hey you! psst! Help me outta here! I'm trapped!"); + return me; + } + + // Make sure that the typical usage of ErrorOr actually compiles. + ErrorOr<std::unique_ptr<int>> TypicalErrorOrUsage(bool succeed) { + if (succeed) + return std::make_unique<int>(123); + return Status(StatusCode::kCodeOnlyForTesting); + } +}; + +TEST_F(StatusTest, StaticOKMethodGivesCorrectSerialization) { + Status ok = DontFail(); + base::Value actual = MediaSerialize(ok); + ASSERT_EQ(actual.GetString(), "Ok"); +} + +TEST_F(StatusTest, SingleLayerError) { + Status failed = FailEasily(); + base::Value actual = MediaSerialize(failed); + ASSERT_EQ(actual.DictSize(), 5ul); + ASSERT_EQ(actual.FindIntPath("status_code"), + static_cast<int32_t>(StatusCode::kCodeOnlyForTesting)); + ASSERT_EQ(*actual.FindStringPath("status_message"), "Message"); + ASSERT_EQ(actual.FindListPath("stack")->GetList().size(), 1ul); + ASSERT_EQ(actual.FindListPath("causes")->GetList().size(), 0ul); + ASSERT_EQ(actual.FindDictPath("data")->DictSize(), 0ul); + + const auto& stack = actual.FindListPath("stack")->GetList(); + ASSERT_EQ(stack[0].DictSize(), 2ul); // line and file + + // This is a bit fragile, since it's dependent on the file layout. + ASSERT_EQ(stack[0].FindIntPath("line").value_or(-1), 42); + ASSERT_THAT(*stack[0].FindStringPath("file"), + HasSubstr("status_unittest.cc")); +} + +TEST_F(StatusTest, MultipleErrorLayer) { + Status failed = FailRecursively(3); + base::Value actual = MediaSerialize(failed); + ASSERT_EQ(actual.DictSize(), 5ul); + ASSERT_EQ(actual.FindIntPath("status_code").value_or(-1), + static_cast<int32_t>(StatusCode::kCodeOnlyForTesting)); + ASSERT_EQ(*actual.FindStringPath("status_message"), "Message"); + ASSERT_EQ(actual.FindListPath("stack")->GetList().size(), 4ul); + ASSERT_EQ(actual.FindListPath("causes")->GetList().size(), 0ul); + ASSERT_EQ(actual.FindDictPath("data")->DictSize(), 0ul); + + const auto& stack = actual.FindListPath("stack")->GetList(); + ASSERT_EQ(stack[0].DictSize(), 2ul); // line and file +} + +TEST_F(StatusTest, CanHaveData) { + Status failed = FailWithData("example", "data"); + base::Value actual = MediaSerialize(failed); + ASSERT_EQ(actual.DictSize(), 5ul); + ASSERT_EQ(actual.FindIntPath("status_code").value_or(-1), + static_cast<int32_t>(StatusCode::kCodeOnlyForTesting)); + ASSERT_EQ(*actual.FindStringPath("status_message"), "Message"); + ASSERT_EQ(actual.FindListPath("stack")->GetList().size(), 1ul); + ASSERT_EQ(actual.FindListPath("causes")->GetList().size(), 0ul); + ASSERT_EQ(actual.FindDictPath("data")->DictSize(), 1ul); + + const auto& stack = actual.FindListPath("stack")->GetList(); + ASSERT_EQ(stack[0].DictSize(), 2ul); // line and file + + ASSERT_EQ(*actual.FindDictPath("data")->FindStringPath("example"), "data"); +} + +TEST_F(StatusTest, CanUseCustomSerializer) { + Status failed = FailWithData("example", UselessThingToBeSerialized("F")); + base::Value actual = MediaSerialize(failed); + ASSERT_EQ(actual.DictSize(), 5ul); + ASSERT_EQ(actual.FindIntPath("status_code"), + static_cast<int32_t>(StatusCode::kCodeOnlyForTesting)); + ASSERT_EQ(*actual.FindStringPath("status_message"), "Message"); + ASSERT_EQ(actual.FindListPath("stack")->GetList().size(), 1ul); + ASSERT_EQ(actual.FindListPath("causes")->GetList().size(), 0ul); + ASSERT_EQ(actual.FindDictPath("data")->DictSize(), 1ul); + + const auto& stack = actual.FindListPath("stack")->GetList(); + ASSERT_EQ(stack[0].DictSize(), 2ul); // line and file + + ASSERT_EQ(*actual.FindDictPath("data")->FindStringPath("example"), "F"); +} + +TEST_F(StatusTest, CausedByHasVector) { + Status causal = FailWithCause(); + base::Value actual = MediaSerialize(causal); + ASSERT_EQ(actual.DictSize(), 5ul); + ASSERT_EQ(actual.FindIntPath("status_code").value_or(-1), + static_cast<int32_t>(StatusCode::kCodeOnlyForTesting)); + ASSERT_EQ(*actual.FindStringPath("status_message"), "Message"); + ASSERT_EQ(actual.FindListPath("stack")->GetList().size(), 1ul); + ASSERT_EQ(actual.FindListPath("causes")->GetList().size(), 1ul); + ASSERT_EQ(actual.FindDictPath("data")->DictSize(), 0ul); + + base::Value& nested = actual.FindListPath("causes")->GetList()[0]; + ASSERT_EQ(nested.DictSize(), 5ul); + ASSERT_EQ(nested.FindIntPath("status_code").value_or(-1), + static_cast<int32_t>(StatusCode::kCodeOnlyForTesting)); + ASSERT_EQ(*nested.FindStringPath("status_message"), "Message"); + ASSERT_EQ(nested.FindListPath("stack")->GetList().size(), 1ul); + ASSERT_EQ(nested.FindListPath("causes")->GetList().size(), 0ul); + ASSERT_EQ(nested.FindDictPath("data")->DictSize(), 0ul); +} + +TEST_F(StatusTest, CanCopyEasily) { + Status failed = FailEasily(); + Status withData = DoSomethingGiveItBack(failed); + + base::Value actual = MediaSerialize(failed); + ASSERT_EQ(actual.DictSize(), 5ul); + ASSERT_EQ(actual.FindIntPath("status_code"), + static_cast<int32_t>(StatusCode::kCodeOnlyForTesting)); + ASSERT_EQ(*actual.FindStringPath("status_message"), "Message"); + ASSERT_EQ(actual.FindListPath("stack")->GetList().size(), 1ul); + ASSERT_EQ(actual.FindListPath("causes")->GetList().size(), 0ul); + ASSERT_EQ(actual.FindDictPath("data")->DictSize(), 0ul); + + actual = MediaSerialize(withData); + ASSERT_EQ(actual.DictSize(), 5ul); + ASSERT_EQ(actual.FindIntPath("status_code"), + static_cast<int32_t>(StatusCode::kCodeOnlyForTesting)); + ASSERT_EQ(*actual.FindStringPath("status_message"), "Message"); + ASSERT_EQ(actual.FindListPath("stack")->GetList().size(), 1ul); + ASSERT_EQ(actual.FindListPath("causes")->GetList().size(), 0ul); + ASSERT_EQ(actual.FindDictPath("data")->DictSize(), 1ul); +} + +TEST_F(StatusTest, ErrorOrTypicalUsage) { + // Mostly so we have some code coverage on the default usage. + EXPECT_TRUE(TypicalErrorOrUsage(true).has_value()); + EXPECT_FALSE(TypicalErrorOrUsage(true).has_error()); + EXPECT_FALSE(TypicalErrorOrUsage(false).has_value()); + EXPECT_TRUE(TypicalErrorOrUsage(false).has_error()); +} + +TEST_F(StatusTest, ErrorOrWithMoveOnlyType) { + ErrorOr<std::unique_ptr<int>> error_or(std::make_unique<int>(123)); + EXPECT_TRUE(error_or.has_value()); + EXPECT_FALSE(error_or.has_error()); + std::unique_ptr<int> result = std::move(error_or.value()); + EXPECT_EQ(error_or.value(), nullptr); + EXPECT_NE(result.get(), nullptr); + EXPECT_EQ(*result, 123); +} + +TEST_F(StatusTest, ErrorOrWithCopyableType) { + ErrorOr<int> error_or(123); + EXPECT_TRUE(error_or.has_value()); + EXPECT_FALSE(error_or.has_error()); + int result = std::move(error_or.value()); + EXPECT_EQ(result, 123); + // Should be unaffected by the move. + EXPECT_EQ(error_or.value(), 123); +} + +TEST_F(StatusTest, ErrorOrMoveConstructionAndAssignment) { + // Make sure that we can move-construct and move-assign a move-only value. + ErrorOr<std::unique_ptr<int>> error_or_0(std::make_unique<int>(123)); + + ErrorOr<std::unique_ptr<int>> error_or_1(std::move(error_or_0)); + EXPECT_EQ(error_or_0.value(), nullptr); + + ErrorOr<std::unique_ptr<int>> error_or_2 = std::move(error_or_1); + EXPECT_EQ(error_or_1.value(), nullptr); + + // |error_or_2| should have gotten the original. + std::unique_ptr<int> value = std::move(error_or_2.value()); + EXPECT_EQ(*value, 123); +} + +TEST_F(StatusTest, ErrorOrCopyWorks) { + // Make sure that we can move-construct and move-assign a move-only value. + ErrorOr<int> error_or_0(123); + ErrorOr<int> error_or_1(std::move(error_or_0)); + ErrorOr<int> error_or_2 = std::move(error_or_1); + EXPECT_EQ(error_or_2.value(), 123); +} + +} // namespace media diff --git a/chromium/media/base/supported_types.cc b/chromium/media/base/supported_types.cc index 0a9ff944f0c..19266cf96d8 100644 --- a/chromium/media/base/supported_types.cc +++ b/chromium/media/base/supported_types.cc @@ -185,7 +185,7 @@ bool IsAudioCodecProprietary(AudioCodec codec) { } bool IsDefaultSupportedAudioType(const AudioType& type) { - if (type.spatialRendering) + if (type.spatial_rendering) return false; #if !BUILDFLAG(USE_PROPRIETARY_CODECS) @@ -195,6 +195,15 @@ bool IsDefaultSupportedAudioType(const AudioType& type) { switch (type.codec) { case kCodecAAC: + if (type.profile != AudioCodecProfile::kXHE_AAC) + return true; +#if defined(OS_ANDROID) + return base::android::BuildInfo::GetInstance()->sdk_int() >= + base::android::SDK_VERSION_P; +#else + return false; +#endif + case kCodecFLAC: case kCodecMP3: case kCodecOpus: @@ -258,13 +267,15 @@ bool IsDefaultSupportedVideoType(const VideoType& type) { // If the AV1 decoder is enabled, or if we're on Q or later, yes. #if BUILDFLAG(ENABLE_AV1_DECODER) return IsColorSpaceSupported(type.color_space); -#elif defined(OS_ANDROID) +#else +#if defined(OS_ANDROID) if (base::android::BuildInfo::GetInstance()->is_at_least_q() && IsColorSpaceSupported(type.color_space)) { return true; } #endif return false; +#endif case kCodecVP9: // Color management required for HDR to not look terrible. diff --git a/chromium/media/base/supported_types_unittest.cc b/chromium/media/base/supported_types_unittest.cc index 24d5ea882ae..764561440d6 100644 --- a/chromium/media/base/supported_types_unittest.cc +++ b/chromium/media/base/supported_types_unittest.cc @@ -4,8 +4,13 @@ #include "media/base/supported_types.h" +#include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h" +#if defined(OS_ANDROID) +#include "base/android/build_info.h" +#endif + namespace media { #if BUILDFLAG(USE_PROPRIETARY_CODECS) @@ -168,36 +173,71 @@ TEST(SupportedTypesTest, IsSupportedAudioTypeWithSpatialRenderingBasics) { const bool is_spatial_rendering = true; // Dolby Atmos = E-AC3 (Dolby Digital Plus) + spatialRendering. Currently not // supported. - EXPECT_FALSE(IsSupportedAudioType({media::kCodecEAC3, is_spatial_rendering})); + EXPECT_FALSE(IsSupportedAudioType( + {media::kCodecEAC3, AudioCodecProfile::kUnknown, is_spatial_rendering})); // Expect non-support for codecs with which there is no spatial audio format. - EXPECT_FALSE(IsSupportedAudioType({media::kCodecAAC, is_spatial_rendering})); - EXPECT_FALSE(IsSupportedAudioType({media::kCodecMP3, is_spatial_rendering})); - EXPECT_FALSE(IsSupportedAudioType({media::kCodecPCM, is_spatial_rendering})); - EXPECT_FALSE( - IsSupportedAudioType({media::kCodecVorbis, is_spatial_rendering})); - EXPECT_FALSE(IsSupportedAudioType({media::kCodecFLAC, is_spatial_rendering})); - EXPECT_FALSE( - IsSupportedAudioType({media::kCodecAMR_NB, is_spatial_rendering})); + EXPECT_FALSE(IsSupportedAudioType( + {media::kCodecAAC, AudioCodecProfile::kUnknown, is_spatial_rendering})); + EXPECT_FALSE(IsSupportedAudioType( + {media::kCodecMP3, AudioCodecProfile::kUnknown, is_spatial_rendering})); + EXPECT_FALSE(IsSupportedAudioType( + {media::kCodecPCM, AudioCodecProfile::kUnknown, is_spatial_rendering})); EXPECT_FALSE( - IsSupportedAudioType({media::kCodecAMR_WB, is_spatial_rendering})); + IsSupportedAudioType({media::kCodecVorbis, AudioCodecProfile::kUnknown, + is_spatial_rendering})); + EXPECT_FALSE(IsSupportedAudioType( + {media::kCodecFLAC, AudioCodecProfile::kUnknown, is_spatial_rendering})); EXPECT_FALSE( - IsSupportedAudioType({media::kCodecPCM_MULAW, is_spatial_rendering})); + IsSupportedAudioType({media::kCodecAMR_NB, AudioCodecProfile::kUnknown, + is_spatial_rendering})); EXPECT_FALSE( - IsSupportedAudioType({media::kCodecGSM_MS, is_spatial_rendering})); + IsSupportedAudioType({media::kCodecAMR_WB, AudioCodecProfile::kUnknown, + is_spatial_rendering})); EXPECT_FALSE( - IsSupportedAudioType({media::kCodecPCM_S16BE, is_spatial_rendering})); + IsSupportedAudioType({media::kCodecPCM_MULAW, AudioCodecProfile::kUnknown, + is_spatial_rendering})); EXPECT_FALSE( - IsSupportedAudioType({media::kCodecPCM_S24BE, is_spatial_rendering})); - EXPECT_FALSE(IsSupportedAudioType({media::kCodecOpus, is_spatial_rendering})); + IsSupportedAudioType({media::kCodecGSM_MS, AudioCodecProfile::kUnknown, + is_spatial_rendering})); EXPECT_FALSE( - IsSupportedAudioType({media::kCodecPCM_ALAW, is_spatial_rendering})); - EXPECT_FALSE(IsSupportedAudioType({media::kCodecALAC, is_spatial_rendering})); - EXPECT_FALSE(IsSupportedAudioType({media::kCodecAC3, is_spatial_rendering})); + IsSupportedAudioType({media::kCodecPCM_S16BE, AudioCodecProfile::kUnknown, + is_spatial_rendering})); EXPECT_FALSE( - IsSupportedAudioType({media::kCodecMpegHAudio, is_spatial_rendering})); + IsSupportedAudioType({media::kCodecPCM_S24BE, AudioCodecProfile::kUnknown, + is_spatial_rendering})); + EXPECT_FALSE(IsSupportedAudioType( + {media::kCodecOpus, AudioCodecProfile::kUnknown, is_spatial_rendering})); EXPECT_FALSE( - IsSupportedAudioType({media::kUnknownAudioCodec, is_spatial_rendering})); + IsSupportedAudioType({media::kCodecPCM_ALAW, AudioCodecProfile::kUnknown, + is_spatial_rendering})); + EXPECT_FALSE(IsSupportedAudioType( + {media::kCodecALAC, AudioCodecProfile::kUnknown, is_spatial_rendering})); + EXPECT_FALSE(IsSupportedAudioType( + {media::kCodecAC3, AudioCodecProfile::kUnknown, is_spatial_rendering})); + EXPECT_FALSE(IsSupportedAudioType({media::kCodecMpegHAudio, + AudioCodecProfile::kUnknown, + is_spatial_rendering})); + EXPECT_FALSE(IsSupportedAudioType({media::kUnknownAudioCodec, + AudioCodecProfile::kUnknown, + is_spatial_rendering})); +} + +TEST(SupportedTypesTest, XHE_AACSupportedOnAndroidOnly) { + // TODO(dalecurtis): Update this test if we ever have support elsewhere. +#if defined(OS_ANDROID) + const bool is_supported = + kPropCodecsEnabled && + base::android::BuildInfo::GetInstance()->sdk_int() >= + base::android::SDK_VERSION_P; + + EXPECT_EQ(is_supported, + IsSupportedAudioType( + {media::kCodecAAC, AudioCodecProfile::kXHE_AAC, false})); +#else + EXPECT_FALSE(IsSupportedAudioType( + {media::kCodecAAC, AudioCodecProfile::kXHE_AAC, false})); +#endif } } // namespace media diff --git a/chromium/media/base/test_data_util.cc b/chromium/media/base/test_data_util.cc index 469be49afea..d849db56af1 100644 --- a/chromium/media/base/test_data_util.cc +++ b/chromium/media/base/test_data_util.cc @@ -40,6 +40,7 @@ const char kMp4Vp9Profile2Video[] = "video/mp4; codecs=\"vp09.02.10.10.01.02.02.02.00\""; const char kMp4Vp9Video[] = "video/mp4; codecs=\"vp09.00.10.08.01.02.02.02.00\""; +const char kMp4XheAacAudio[] = "audio/mp4; codecs=\"mp4a.40.42\""; // WebM const char kWebMAv110bitVideo[] = "video/webm; codecs=\"av01.0.04M.10\""; const char kWebMAv1Video[] = "video/webm; codecs=\"av01.0.04M.08\""; @@ -139,6 +140,7 @@ const FileToMimeTypeMap& GetFileToMimeTypeMap() { {"bear-vp9.webm", kWebMVp9Video}, {"frame_size_change-av_enc-v.webm", kWebMVorbisAudioVp8Video}, {"icy_sfx.mp3", kMp3Audio}, + {"noise-xhe-aac.mp4", kMp4XheAacAudio}, {"opus-trimming-test.mp4", kMp4OpusAudio}, {"opus-trimming-test.webm", kWebMOpusAudio}, {"sfx-flac_frag.mp4", kMp4FlacAudio}, diff --git a/chromium/media/base/test_helpers.cc b/chromium/media/base/test_helpers.cc index 97bca27c607..c815328ee39 100644 --- a/chromium/media/base/test_helpers.cc +++ b/chromium/media/base/test_helpers.cc @@ -45,22 +45,22 @@ class MockCallback : public base::RefCountedThreadSafe<MockCallback> { MockCallback::MockCallback() = default; MockCallback::~MockCallback() = default; -base::Closure NewExpectedClosure() { +base::OnceClosure NewExpectedClosure() { StrictMock<MockCallback>* callback = new StrictMock<MockCallback>(); EXPECT_CALL(*callback, Run()); - return base::Bind(&MockCallback::Run, WrapRefCounted(callback)); + return base::BindOnce(&MockCallback::Run, WrapRefCounted(callback)); } -base::Callback<void(bool)> NewExpectedBoolCB(bool success) { +base::OnceCallback<void(bool)> NewExpectedBoolCB(bool success) { StrictMock<MockCallback>* callback = new StrictMock<MockCallback>(); EXPECT_CALL(*callback, RunWithBool(success)); - return base::Bind(&MockCallback::RunWithBool, WrapRefCounted(callback)); + return base::BindOnce(&MockCallback::RunWithBool, WrapRefCounted(callback)); } -PipelineStatusCB NewExpectedStatusCB(PipelineStatus status) { +PipelineStatusCallback NewExpectedStatusCB(PipelineStatus status) { StrictMock<MockCallback>* callback = new StrictMock<MockCallback>(); EXPECT_CALL(*callback, RunWithStatus(status)); - return base::Bind(&MockCallback::RunWithStatus, WrapRefCounted(callback)); + return base::BindOnce(&MockCallback::RunWithStatus, WrapRefCounted(callback)); } WaitableMessageLoopEvent::WaitableMessageLoopEvent() @@ -73,17 +73,16 @@ WaitableMessageLoopEvent::~WaitableMessageLoopEvent() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); } -base::Closure WaitableMessageLoopEvent::GetClosure() { +base::OnceClosure WaitableMessageLoopEvent::GetClosure() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - return BindToCurrentLoop(base::Bind( - &WaitableMessageLoopEvent::OnCallback, base::Unretained(this), - PIPELINE_OK)); + return BindToCurrentLoop(base::BindOnce(&WaitableMessageLoopEvent::OnCallback, + base::Unretained(this), PIPELINE_OK)); } -PipelineStatusCB WaitableMessageLoopEvent::GetPipelineStatusCB() { +PipelineStatusCallback WaitableMessageLoopEvent::GetPipelineStatusCB() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - return BindToCurrentLoop(base::Bind( - &WaitableMessageLoopEvent::OnCallback, base::Unretained(this))); + return BindToCurrentLoop(base::BindOnce(&WaitableMessageLoopEvent::OnCallback, + base::Unretained(this))); } void WaitableMessageLoopEvent::RunAndWait() { @@ -100,9 +99,9 @@ void WaitableMessageLoopEvent::RunAndWaitForStatus(PipelineStatus expected) { run_loop_.reset(new base::RunLoop()); base::OneShotTimer timer; - timer.Start( - FROM_HERE, timeout_, - base::Bind(&WaitableMessageLoopEvent::OnTimeout, base::Unretained(this))); + timer.Start(FROM_HERE, timeout_, + base::BindOnce(&WaitableMessageLoopEvent::OnTimeout, + base::Unretained(this))); run_loop_->Run(); EXPECT_TRUE(signaled_); diff --git a/chromium/media/base/test_helpers.h b/chromium/media/base/test_helpers.h index b2caa61094b..8e97cdc778d 100644 --- a/chromium/media/base/test_helpers.h +++ b/chromium/media/base/test_helpers.h @@ -19,6 +19,7 @@ #include "media/base/media_log.h" #include "media/base/pipeline_status.h" #include "media/base/sample_format.h" +#include "media/base/status.h" #include "media/base/video_decoder_config.h" #include "testing/gmock/include/gmock/gmock.h" #include "ui/gfx/geometry/size.h" @@ -36,9 +37,9 @@ class DecoderBuffer; class MockDemuxerStream; // Return a callback that expects to be run once. -base::Closure NewExpectedClosure(); -base::Callback<void(bool)> NewExpectedBoolCB(bool success); -PipelineStatusCB NewExpectedStatusCB(PipelineStatus status); +base::OnceClosure NewExpectedClosure(); +base::OnceCallback<void(bool)> NewExpectedBoolCB(bool success); +PipelineStatusCallback NewExpectedStatusCB(PipelineStatus status); // Helper class for running a message loop until a callback has run. Useful for // testing classes that run on more than a single thread. @@ -51,8 +52,8 @@ class WaitableMessageLoopEvent { ~WaitableMessageLoopEvent(); // Returns a thread-safe closure that will signal |this| when executed. - base::Closure GetClosure(); - PipelineStatusCB GetPipelineStatusCB(); + base::OnceClosure GetClosure(); + PipelineStatusCallback GetPipelineStatusCB(); // Runs the current message loop until |this| has been signaled. // @@ -199,6 +200,20 @@ bool VerifyFakeVideoBufferForTest(const DecoderBuffer& buffer, std::unique_ptr<::testing::StrictMock<MockDemuxerStream>> CreateMockDemuxerStream(DemuxerStream::Type type, bool encrypted); +// Compares two media::Status by StatusCode only. +MATCHER_P(SameStatusCode, status, "") { + return arg.code() == status.code(); +} + +// Compares two an |arg| Status to a StatusCode provided +MATCHER_P(HasStatusCode, status_code, "") { + return arg.code() == status_code; +} + +MATCHER(IsOkStatus, "") { + return arg.is_ok(); +} + // Compares two {Audio|Video}DecoderConfigs MATCHER_P(DecoderConfigEq, config, "") { return arg.Matches(config); diff --git a/chromium/media/base/text_renderer.cc b/chromium/media/base/text_renderer.cc index fa57294773c..aa1e5a34d1e 100644 --- a/chromium/media/base/text_renderer.cc +++ b/chromium/media/base/text_renderer.cc @@ -34,7 +34,7 @@ TextRenderer::~TextRenderer() { std::move(pause_cb_).Run(); } -void TextRenderer::Initialize(const base::Closure& ended_cb) { +void TextRenderer::Initialize(const base::RepeatingClosure& ended_cb) { DCHECK(task_runner_->BelongsToCurrentThread()); DCHECK(ended_cb); DCHECK_EQ(kUninitialized, state_) << "state_ " << state_; @@ -65,22 +65,22 @@ void TextRenderer::StartPlaying() { state_ = kPlaying; } -void TextRenderer::Pause(const base::Closure& callback) { +void TextRenderer::Pause(base::OnceClosure callback) { DCHECK(task_runner_->BelongsToCurrentThread()); DCHECK(state_ == kPlaying || state_ == kEnded) << "state_ " << state_; DCHECK_GE(pending_read_count_, 0); if (pending_read_count_ == 0) { state_ = kPaused; - task_runner_->PostTask(FROM_HERE, callback); + task_runner_->PostTask(FROM_HERE, std::move(callback)); return; } - pause_cb_ = callback; + pause_cb_ = std::move(callback); state_ = kPausePending; } -void TextRenderer::Flush(const base::Closure& callback) { +void TextRenderer::Flush(base::OnceClosure callback) { DCHECK(task_runner_->BelongsToCurrentThread()); DCHECK_EQ(pending_read_count_, 0); DCHECK(state_ == kPaused) << "state_ " << state_; @@ -91,7 +91,7 @@ void TextRenderer::Flush(const base::Closure& callback) { itr->second->text_ranges_.Reset(); } DCHECK_EQ(pending_eos_set_.size(), text_track_state_map_.size()); - task_runner_->PostTask(FROM_HERE, callback); + task_runner_->PostTask(FROM_HERE, std::move(callback)); } void TextRenderer::AddTextStream(DemuxerStream* text_stream, @@ -103,12 +103,11 @@ void TextRenderer::AddTextStream(DemuxerStream* text_stream, DCHECK(pending_eos_set_.find(text_stream) == pending_eos_set_.end()); - AddTextTrackDoneCB done_cb = - BindToCurrentLoop(base::Bind(&TextRenderer::OnAddTextTrackDone, - weak_factory_.GetWeakPtr(), - text_stream)); + AddTextTrackDoneCB done_cb = BindToCurrentLoop( + base::BindOnce(&TextRenderer::OnAddTextTrackDone, + weak_factory_.GetWeakPtr(), text_stream)); - add_text_track_cb_.Run(config, done_cb); + add_text_track_cb_.Run(config, std::move(done_cb)); } void TextRenderer::RemoveTextStream(DemuxerStream* text_stream) { diff --git a/chromium/media/base/text_renderer.h b/chromium/media/base/text_renderer.h index e8348bd9c02..52fef826088 100644 --- a/chromium/media/base/text_renderer.h +++ b/chromium/media/base/text_renderer.h @@ -45,17 +45,17 @@ class MEDIA_EXPORT TextRenderer { // |ended_cb| is executed when all of the text tracks have reached // end of stream, following a play request. - void Initialize(const base::Closure& ended_cb); + void Initialize(const base::RepeatingClosure& ended_cb); // Starts text track cue decoding and rendering. void StartPlaying(); // Temporarily suspends decoding and rendering, executing |callback| when // playback has been suspended. - void Pause(const base::Closure& callback); + void Pause(base::OnceClosure callback); // Discards any text data, executing |callback| when completed. - void Flush(const base::Closure& callback); + void Flush(base::OnceClosure callback); // Adds new |text_stream|, having the indicated |config|, to the text stream // collection managed by this text renderer. @@ -106,10 +106,10 @@ class MEDIA_EXPORT TextRenderer { const AddTextTrackCB add_text_track_cb_; // Callbacks provided during Initialize(). - base::Closure ended_cb_; + base::RepeatingClosure ended_cb_; // Callback provided to Pause(). - base::Closure pause_cb_; + base::OnceClosure pause_cb_; // Simple state tracking variable. enum State { diff --git a/chromium/media/base/text_renderer_unittest.cc b/chromium/media/base/text_renderer_unittest.cc index a9fa4872e83..d6ab92c4733 100644 --- a/chromium/media/base/text_renderer_unittest.cc +++ b/chromium/media/base/text_renderer_unittest.cc @@ -29,9 +29,9 @@ namespace media { // Local implementation of the TextTrack interface. class FakeTextTrack : public TextTrack { public: - FakeTextTrack(const base::Closure& destroy_cb, const TextTrackConfig& config) - : destroy_cb_(destroy_cb), config_(config) {} - ~FakeTextTrack() override { destroy_cb_.Run(); } + FakeTextTrack(base::OnceClosure destroy_cb, const TextTrackConfig& config) + : destroy_cb_(std::move(destroy_cb)), config_(config) {} + ~FakeTextTrack() override { std::move(destroy_cb_).Run(); } MOCK_METHOD5(addWebVTTCue, void(base::TimeDelta start, @@ -40,7 +40,7 @@ class FakeTextTrack : public TextTrack { const std::string& content, const std::string& settings)); - const base::Closure destroy_cb_; + base::OnceClosure destroy_cb_; const TextTrackConfig config_; private: @@ -54,11 +54,12 @@ class TextRendererTest : public testing::Test { void CreateTextRenderer() { DCHECK(!text_renderer_); - text_renderer_.reset(new TextRenderer( - task_environment_.GetMainThreadTaskRunner(), - base::Bind(&TextRendererTest::OnAddTextTrack, base::Unretained(this)))); + text_renderer_.reset( + new TextRenderer(task_environment_.GetMainThreadTaskRunner(), + base::BindRepeating(&TextRendererTest::OnAddTextTrack, + base::Unretained(this)))); text_renderer_->Initialize( - base::Bind(&TextRendererTest::OnEnd, base::Unretained(this))); + base::BindRepeating(&TextRendererTest::OnEnd, base::Unretained(this))); } void Destroy() { @@ -87,16 +88,16 @@ class TextRendererTest : public testing::Test { } void OnAddTextTrack(const TextTrackConfig& config, - const AddTextTrackDoneCB& done_cb) { - base::Closure destroy_cb = - base::Bind(&TextRendererTest::OnDestroyTextTrack, - base::Unretained(this), text_tracks_.size()); + AddTextTrackDoneCB done_cb) { + base::OnceClosure destroy_cb = + base::BindOnce(&TextRendererTest::OnDestroyTextTrack, + base::Unretained(this), text_tracks_.size()); // Text track objects are owned by the text renderer, but we cache them // here so we can inspect them. They get removed from our cache when the // text renderer deallocates them. - text_tracks_.push_back(new FakeTextTrack(destroy_cb, config)); + text_tracks_.push_back(new FakeTextTrack(std::move(destroy_cb), config)); std::unique_ptr<TextTrack> text_track(text_tracks_.back()); - done_cb.Run(std::move(text_track)); + std::move(done_cb).Run(std::move(text_track)); } void RemoveTextTrack(unsigned idx) { @@ -171,14 +172,14 @@ class TextRendererTest : public testing::Test { void Pause() { text_renderer_->Pause( - base::Bind(&TextRendererTest::OnPause, base::Unretained(this))); + base::BindOnce(&TextRendererTest::OnPause, base::Unretained(this))); base::RunLoop().RunUntilIdle(); } void Flush() { EXPECT_CALL(*this, OnFlush()); text_renderer_->Flush( - base::Bind(&TextRendererTest::OnFlush, base::Unretained(this))); + base::BindOnce(&TextRendererTest::OnFlush, base::Unretained(this))); } void ExpectRead(size_t idx) { @@ -205,9 +206,10 @@ class TextRendererTest : public testing::Test { }; TEST_F(TextRendererTest, CreateTextRendererNoInit) { - text_renderer_.reset(new TextRenderer( - task_environment_.GetMainThreadTaskRunner(), - base::Bind(&TextRendererTest::OnAddTextTrack, base::Unretained(this)))); + text_renderer_.reset( + new TextRenderer(task_environment_.GetMainThreadTaskRunner(), + base::BindRepeating(&TextRendererTest::OnAddTextTrack, + base::Unretained(this)))); text_renderer_.reset(); } diff --git a/chromium/media/base/text_track.h b/chromium/media/base/text_track.h index f7ee5ee36d6..4d594a0bc50 100644 --- a/chromium/media/base/text_track.h +++ b/chromium/media/base/text_track.h @@ -25,10 +25,11 @@ class TextTrack { const std::string& settings) = 0; }; -using AddTextTrackDoneCB = base::Callback<void(std::unique_ptr<TextTrack>)>; +using AddTextTrackDoneCB = base::OnceCallback<void(std::unique_ptr<TextTrack>)>; -using AddTextTrackCB = base::Callback<void(const TextTrackConfig& config, - const AddTextTrackDoneCB& done_cb)>; +using AddTextTrackCB = + base::RepeatingCallback<void(const TextTrackConfig& config, + AddTextTrackDoneCB done_cb)>; } // namespace media diff --git a/chromium/media/base/time_source.h b/chromium/media/base/time_source.h index 9d478d17340..92d6528d909 100644 --- a/chromium/media/base/time_source.h +++ b/chromium/media/base/time_source.h @@ -18,8 +18,8 @@ class MEDIA_EXPORT TimeSource { public: // Helper alias for converting media timestamps into a wall clock timestamps. using WallClockTimeCB = - base::Callback<bool(const std::vector<base::TimeDelta>&, - std::vector<base::TimeTicks>*)>; + base::RepeatingCallback<bool(const std::vector<base::TimeDelta>&, + std::vector<base::TimeTicks>*)>; TimeSource() {} virtual ~TimeSource() {} diff --git a/chromium/media/base/video_codecs.h b/chromium/media/base/video_codecs.h index bf7e4bc9282..f81a273096a 100644 --- a/chromium/media/base/video_codecs.h +++ b/chromium/media/base/video_codecs.h @@ -96,10 +96,13 @@ enum VideoCodecProfile { VIDEO_CODEC_PROFILE_MAX = DOLBYVISION_PROFILE9, }; +using VideoCodecLevel = uint32_t; +constexpr VideoCodecLevel kNoVideoCodecLevel = 0; + struct CodecProfileLevel { VideoCodec codec; VideoCodecProfile profile; - int level; + VideoCodecLevel level; }; std::string MEDIA_EXPORT GetCodecName(VideoCodec codec); diff --git a/chromium/media/base/video_decoder.h b/chromium/media/base/video_decoder.h index 52753e8c17e..bbb0b11b151 100644 --- a/chromium/media/base/video_decoder.h +++ b/chromium/media/base/video_decoder.h @@ -13,6 +13,7 @@ #include "media/base/decode_status.h" #include "media/base/media_export.h" #include "media/base/pipeline_status.h" +#include "media/base/status.h" #include "media/base/waiting.h" #include "ui/gfx/geometry/size.h" @@ -26,7 +27,7 @@ class VideoFrame; class MEDIA_EXPORT VideoDecoder { public: // Callback for VideoDecoder initialization. - using InitCB = base::OnceCallback<void(bool success)>; + using InitCB = base::OnceCallback<void(Status status)>; // Callback for VideoDecoder to return a decoded frame whenever it becomes // available. Only non-EOS frames should be returned via this callback. diff --git a/chromium/media/base/video_decoder_config.cc b/chromium/media/base/video_decoder_config.cc index dfe1f7500f0..e9c54664dfc 100644 --- a/chromium/media/base/video_decoder_config.cc +++ b/chromium/media/base/video_decoder_config.cc @@ -8,6 +8,7 @@ #include <vector> #include "base/logging.h" +#include "base/strings/string_number_conversions.h" #include "media/base/limits.h" #include "media/base/media_util.h" #include "media/base/video_types.h" @@ -67,11 +68,7 @@ VideoCodec VideoCodecProfileToVideoCodec(VideoCodecProfile profile) { return kUnknownVideoCodec; } -VideoDecoderConfig::VideoDecoderConfig() - : codec_(kUnknownVideoCodec), - profile_(VIDEO_CODEC_PROFILE_UNKNOWN), - alpha_mode_(AlphaMode::kIsOpaque), - transformation_(kNoTransformation) {} +VideoDecoderConfig::VideoDecoderConfig() = default; VideoDecoderConfig::VideoDecoderConfig(VideoCodec codec, VideoCodecProfile profile, @@ -92,23 +89,6 @@ VideoDecoderConfig::VideoDecoderConfig(const VideoDecoderConfig& other) = VideoDecoderConfig::~VideoDecoderConfig() = default; -void VideoDecoderConfig::set_color_space_info( - const VideoColorSpace& color_space) { - color_space_info_ = color_space; -} - -const VideoColorSpace& VideoDecoderConfig::color_space_info() const { - return color_space_info_; -} - -void VideoDecoderConfig::set_hdr_metadata(const HDRMetadata& hdr_metadata) { - hdr_metadata_ = hdr_metadata; -} - -const base::Optional<HDRMetadata>& VideoDecoderConfig::hdr_metadata() const { - return hdr_metadata_; -} - void VideoDecoderConfig::Initialize(VideoCodec codec, VideoCodecProfile profile, AlphaMode alpha_mode, @@ -147,13 +127,16 @@ bool VideoDecoderConfig::Matches(const VideoDecoderConfig& config) const { extra_data() == config.extra_data() && encryption_scheme() == config.encryption_scheme() && color_space_info() == config.color_space_info() && - hdr_metadata() == config.hdr_metadata(); + hdr_metadata() == config.hdr_metadata() && level() == config.level(); } std::string VideoDecoderConfig::AsHumanReadableString() const { std::ostringstream s; s << "codec: " << GetCodecName(codec()) - << ", profile: " << GetProfileName(profile()) << ", alpha_mode: " + << ", profile: " << GetProfileName(profile()) << ", level: " + << (level() > kNoVideoCodecLevel ? base::NumberToString(level()) + : "not available") + << ", alpha_mode: " << (alpha_mode() == AlphaMode::kHasAlpha ? "has_alpha" : "is_opaque") << ", coded size: [" << coded_size().width() << "," << coded_size().height() << "]" @@ -166,6 +149,7 @@ std::string VideoDecoderConfig::AsHumanReadableString() const { << ", rotation: " << VideoRotationToString(video_transformation().rotation) << ", flipped: " << video_transformation().mirrored << ", color space: " << color_space_info().ToGfxColorSpace().ToString(); + if (hdr_metadata().has_value()) { s << std::setprecision(4) << ", luminance range: " << hdr_metadata()->mastering_metadata.luminance_min << "-" @@ -179,6 +163,7 @@ std::string VideoDecoderConfig::AsHumanReadableString() const { << hdr_metadata()->mastering_metadata.white_point.x() << "," << hdr_metadata()->mastering_metadata.white_point.y() << ")"; } + return s.str(); } diff --git a/chromium/media/base/video_decoder_config.h b/chromium/media/base/video_decoder_config.h index e9ca2795a61..405be9f8102 100644 --- a/chromium/media/base/video_decoder_config.h +++ b/chromium/media/base/video_decoder_config.h @@ -78,8 +78,6 @@ class MEDIA_EXPORT VideoDecoderConfig { std::string GetHumanReadableCodecName() const; - static std::string GetHumanReadableProfile(VideoCodecProfile profile); - VideoCodec codec() const { return codec_; } VideoCodecProfile profile() const { return profile_; } AlphaMode alpha_mode() const { return alpha_mode_; } @@ -148,24 +146,38 @@ class MEDIA_EXPORT VideoDecoderConfig { EncryptionScheme encryption_scheme() const { return encryption_scheme_; } // Color space of the image data. - void set_color_space_info(const VideoColorSpace& color_space); - const VideoColorSpace& color_space_info() const; + void set_color_space_info(const VideoColorSpace& color_space) { + color_space_info_ = color_space; + } + const VideoColorSpace& color_space_info() const { return color_space_info_; } // Dynamic range of the image data. - void set_hdr_metadata(const HDRMetadata& hdr_metadata); - const base::Optional<HDRMetadata>& hdr_metadata() const; + void set_hdr_metadata(const HDRMetadata& hdr_metadata) { + hdr_metadata_ = hdr_metadata; + } + const base::Optional<HDRMetadata>& hdr_metadata() const { + return hdr_metadata_; + } + + // Codec level. + void set_level(VideoCodecLevel level) { level_ = level; } + VideoCodecLevel level() const { return level_; } // Sets the config to be encrypted or not encrypted manually. This can be // useful for decryptors that decrypts an encrypted stream to a clear stream. void SetIsEncrypted(bool is_encrypted); private: - VideoCodec codec_; - VideoCodecProfile profile_; + VideoCodec codec_ = kUnknownVideoCodec; + VideoCodecProfile profile_ = VIDEO_CODEC_PROFILE_UNKNOWN; + + // Optional video codec level. kNoVideoCodecLevel means the field is not + // available. + VideoCodecLevel level_ = kNoVideoCodecLevel; - AlphaMode alpha_mode_; + AlphaMode alpha_mode_ = AlphaMode::kIsOpaque; - VideoTransformation transformation_; + VideoTransformation transformation_ = kNoTransformation; // Deprecated. TODO(wolenetz): Remove. See https://crbug.com/665539. gfx::Size coded_size_; diff --git a/chromium/media/base/video_frame.cc b/chromium/media/base/video_frame.cc index b3e63e70521..0bb025a1d5f 100644 --- a/chromium/media/base/video_frame.cc +++ b/chromium/media/base/video_frame.cc @@ -131,6 +131,7 @@ gfx::Size VideoFrame::SampleSize(VideoPixelFormat format, size_t plane) { case PIXEL_FORMAT_P016LE: return gfx::Size(2, 2); + case PIXEL_FORMAT_UYVY: case PIXEL_FORMAT_UNKNOWN: case PIXEL_FORMAT_YUY2: case PIXEL_FORMAT_ARGB: @@ -193,6 +194,7 @@ static bool RequiresEvenSizeAllocation(VideoPixelFormat format) { case PIXEL_FORMAT_YUV422P12: case PIXEL_FORMAT_YUV444P12: case PIXEL_FORMAT_I420A: + case PIXEL_FORMAT_UYVY: case PIXEL_FORMAT_P016LE: return true; case PIXEL_FORMAT_UNKNOWN: @@ -732,8 +734,7 @@ scoped_refptr<VideoFrame> VideoFrame::WrapVideoFrame( if (frame->storage_type() == STORAGE_SHMEM) { DCHECK(frame->shm_region_ && frame->shm_region_->IsValid()); - wrapping_frame->BackWithSharedMemory(frame->shm_region_, - frame->shared_memory_offset()); + wrapping_frame->BackWithSharedMemory(frame->shm_region_); } wrapping_frame->wrapped_frame_ = std::move(frame); @@ -864,6 +865,7 @@ int VideoFrame::BytesPerElement(VideoPixelFormat format, size_t plane) { case PIXEL_FORMAT_RGB24: return 3; case PIXEL_FORMAT_Y16: + case PIXEL_FORMAT_UYVY: case PIXEL_FORMAT_YUY2: case PIXEL_FORMAT_YUV420P9: case PIXEL_FORMAT_YUV422P9: @@ -941,8 +943,7 @@ void VideoFrame::HashFrameForTesting(base::MD5Context* context, } } -void VideoFrame::BackWithSharedMemory(base::UnsafeSharedMemoryRegion* region, - size_t offset) { +void VideoFrame::BackWithSharedMemory(base::UnsafeSharedMemoryRegion* region) { DCHECK(!shm_region_); DCHECK(!owned_shm_region_.IsValid()); // Either we should be backing a frame created with WrapExternal*, or we are @@ -953,13 +954,11 @@ void VideoFrame::BackWithSharedMemory(base::UnsafeSharedMemoryRegion* region, DCHECK(region && region->IsValid()); storage_type_ = STORAGE_SHMEM; shm_region_ = region; - shared_memory_offset_ = offset; } void VideoFrame::BackWithOwnedSharedMemory( base::UnsafeSharedMemoryRegion region, - base::WritableSharedMemoryMapping mapping, - size_t offset) { + base::WritableSharedMemoryMapping mapping) { DCHECK(!shm_region_); DCHECK(!owned_shm_region_.IsValid()); // We should be backing a frame created with WrapExternal*. We cannot be @@ -969,7 +968,6 @@ void VideoFrame::BackWithOwnedSharedMemory( owned_shm_region_ = std::move(region); shm_region_ = &owned_shm_region_; owned_shm_mapping_ = std::move(mapping); - shared_memory_offset_ = offset; } bool VideoFrame::IsMappable() const { @@ -1006,6 +1004,17 @@ gfx::GpuMemoryBuffer* VideoFrame::GetGpuMemoryBuffer() const { : gpu_memory_buffer_.get(); } +bool VideoFrame::IsSameAllocation(VideoPixelFormat format, + const gfx::Size& coded_size, + const gfx::Rect& visible_rect, + const gfx::Size& natural_size) const { + // CreateFrameInternal() changes coded_size to new_coded_size. Match that + // behavior here. + const gfx::Size new_coded_size = DetermineAlignedSize(format, coded_size); + return this->format() == format && this->coded_size() == new_coded_size && + visible_rect_ == visible_rect && natural_size_ == natural_size; +} + gfx::ColorSpace VideoFrame::ColorSpace() const { return color_space_; } @@ -1109,7 +1118,7 @@ gpu::SyncToken VideoFrame::UpdateReleaseSyncToken(SyncTokenClient* client) { return release_sync_token_; } -std::string VideoFrame::AsHumanReadableString() { +std::string VideoFrame::AsHumanReadableString() const { if (metadata()->IsTrue(VideoFrameMetadata::END_OF_STREAM)) return "end of stream"; @@ -1201,7 +1210,7 @@ scoped_refptr<VideoFrame> VideoFrame::CreateFrameInternal( // line up on sample boundaries. See discussion at http://crrev.com/1240833003 const gfx::Size new_coded_size = DetermineAlignedSize(format, coded_size); auto layout = VideoFrameLayout::CreateWithStrides( - format, new_coded_size, ComputeStrides(format, coded_size)); + format, new_coded_size, ComputeStrides(format, new_coded_size)); if (!layout) { DLOG(ERROR) << "Invalid layout."; return nullptr; diff --git a/chromium/media/base/video_frame.h b/chromium/media/base/video_frame.h index 9ea6ceba22d..6b1a5b455a7 100644 --- a/chromium/media/base/video_frame.h +++ b/chromium/media/base/video_frame.h @@ -112,8 +112,7 @@ class MEDIA_EXPORT VideoFrame : public base::RefCountedThreadSafe<VideoFrame> { DISALLOW_COPY_AND_ASSIGN(SyncTokenClient); }; - // Call prior to CreateFrame to ensure validity of frame configuration. Called - // automatically by VideoDecoderConfig::IsValidConfig(). + // Returns true if frame configuration is valid. static bool IsValidConfig(VideoPixelFormat format, StorageType storage_type, const gfx::Size& coded_size, @@ -312,6 +311,10 @@ class MEDIA_EXPORT VideoFrame : public base::RefCountedThreadSafe<VideoFrame> { static size_t AllocationSize(VideoPixelFormat format, const gfx::Size& coded_size); + // Returns |dimensions| adjusted to appropriate boundaries based on |format|. + static gfx::Size DetermineAlignedSize(VideoPixelFormat format, + const gfx::Size& dimensions); + // Returns the plane gfx::Size (in bytes) for a plane of the given coded size // and format. static gfx::Size PlaneSize(VideoPixelFormat format, @@ -370,21 +373,12 @@ class MEDIA_EXPORT VideoFrame : public base::RefCountedThreadSafe<VideoFrame> { // // The region is NOT owned by the video frame. Both the region and its // associated mapping must outlive this instance. - void BackWithSharedMemory(base::UnsafeSharedMemoryRegion* region, - size_t offset = 0); + void BackWithSharedMemory(base::UnsafeSharedMemoryRegion* region); // As above, but the VideoFrame owns the shared memory region as well as the // mapping. They will be destroyed with their VideoFrame. void BackWithOwnedSharedMemory(base::UnsafeSharedMemoryRegion region, - base::WritableSharedMemoryMapping mapping, - size_t offset = 0); - - // Returns the offset into the shared memory where the frame data begins. Only - // valid if the frame is backed by shared memory. - size_t shared_memory_offset() const { - DCHECK(IsValidSharedMemoryFrame()); - return shared_memory_offset_; - } + base::WritableSharedMemoryMapping mapping); // Valid for shared memory backed VideoFrames. base::UnsafeSharedMemoryRegion* shm_region() { @@ -411,6 +405,12 @@ class MEDIA_EXPORT VideoFrame : public base::RefCountedThreadSafe<VideoFrame> { // Gets the GpuMemoryBuffer backing the VideoFrame. gfx::GpuMemoryBuffer* GetGpuMemoryBuffer() const; + // Returns true if the video frame was created with the given parameters. + bool IsSameAllocation(VideoPixelFormat format, + const gfx::Size& coded_size, + const gfx::Rect& visible_rect, + const gfx::Size& natural_size) const; + // Returns the color space of this frame's content. gfx::ColorSpace ColorSpace() const; void set_color_space(const gfx::ColorSpace& color_space) { @@ -544,7 +544,7 @@ class MEDIA_EXPORT VideoFrame : public base::RefCountedThreadSafe<VideoFrame> { gpu::SyncToken UpdateReleaseSyncToken(SyncTokenClient* client); // Returns a human-readable string describing |*this|. - std::string AsHumanReadableString(); + std::string AsHumanReadableString() const; // Unique identifier for this video frame; generated at construction time and // guaranteed to be unique within a single process. @@ -579,10 +579,6 @@ class MEDIA_EXPORT VideoFrame : public base::RefCountedThreadSafe<VideoFrame> { const gfx::Rect& visible_rect, const gfx::Size& natural_size); - // Returns |dimensions| adjusted to appropriate boundaries based on |format|. - static gfx::Size DetermineAlignedSize(VideoPixelFormat format, - const gfx::Size& dimensions); - void set_data(size_t plane, uint8_t* ptr) { DCHECK(IsValidPlane(format(), plane)); DCHECK(ptr); @@ -650,10 +646,6 @@ class MEDIA_EXPORT VideoFrame : public base::RefCountedThreadSafe<VideoFrame> { // to is unowned. base::UnsafeSharedMemoryRegion* shm_region_ = nullptr; - // If this is a STORAGE_SHMEM frame, the offset of the data within the shared - // memory. - size_t shared_memory_offset_ = 0; - // Used if this is a STORAGE_SHMEM frame with owned shared memory. In that // case, shm_region_ will refer to this region. base::UnsafeSharedMemoryRegion owned_shm_region_; diff --git a/chromium/media/base/video_frame_layout.cc b/chromium/media/base/video_frame_layout.cc index 51e06ff8f95..b7210ce1126 100644 --- a/chromium/media/base/video_frame_layout.cc +++ b/chromium/media/base/video_frame_layout.cc @@ -43,6 +43,7 @@ std::vector<ColorPlaneLayout> PlanesFromStrides( // static size_t VideoFrameLayout::NumPlanes(VideoPixelFormat format) { switch (format) { + case PIXEL_FORMAT_UYVY: case PIXEL_FORMAT_YUY2: case PIXEL_FORMAT_ARGB: case PIXEL_FORMAT_BGRA: diff --git a/chromium/media/base/video_frame_layout.h b/chromium/media/base/video_frame_layout.h index c7f59083cff..aeb0ceaeef5 100644 --- a/chromium/media/base/video_frame_layout.h +++ b/chromium/media/base/video_frame_layout.h @@ -38,11 +38,10 @@ class MEDIA_EXPORT VideoFrameLayout { static constexpr size_t kBufferAddressAlignment = 32; // Factory functions. - // |format| and |coded_size| must be specified. - // |is_single_planar| is optional. It describes planes can be stored (although - // not always) in multiple buffers. It is specified only in HW decoder code. + // |format| and |coded_size| must always be specified. // |planes| info is also optional but useful to represent the layout of a - // video frame buffer correctly. + // video frame buffer correctly. When omitted, its information is all set + // to zero, so clients should be wary not to use this information. // |buffer_addr_align| can be specified to request a specific buffer memory // alignment. // |modifier| is the additional information of |format|. It will become some @@ -50,17 +49,24 @@ class MEDIA_EXPORT VideoFrameLayout { // buffer format is different from a standard |format| due to tiling. // The returned base::Optional will be base::nullopt if the configured values // are invalid. + + // Create a layout suitable for |format| at |coded_size|. The stride, offsets + // and size of all planes are set to 0, since that information cannot reliably + // be infered from the arguments. static base::Optional<VideoFrameLayout> Create(VideoPixelFormat format, const gfx::Size& coded_size); - // The size of |strides| must be NumPlanes(|format|). Planes' offset will be - // 0. + // Create a layout suitable for |format| at |coded_size|, with the |strides| + // for each plane specified. The offsets and size of all planes are set to 0. + // The size of |strides| must be equal to NumPlanes(|format|). static base::Optional<VideoFrameLayout> CreateWithStrides( VideoPixelFormat format, const gfx::Size& coded_size, std::vector<int32_t> strides); - // The size of |planes| must be NumPlanes(|format|). + // Create a layout suitable for |format| at |coded_size|, with the |planes| + // fully provided. + // The size of |planes| must be equal to NumPlanes(|format|). static base::Optional<VideoFrameLayout> CreateWithPlanes( VideoPixelFormat format, const gfx::Size& coded_size, diff --git a/chromium/media/base/video_frame_metadata.cc b/chromium/media/base/video_frame_metadata.cc index 0257844c9cc..1b403379bd0 100644 --- a/chromium/media/base/video_frame_metadata.cc +++ b/chromium/media/base/video_frame_metadata.cc @@ -50,33 +50,23 @@ void VideoFrameMetadata::SetRotation(Key key, VideoRotation value) { } void VideoFrameMetadata::SetString(Key key, const std::string& value) { - dictionary_.SetWithoutPathExpansion( + dictionary_.SetKey( ToInternalKey(key), - // Using BinaryValue since we don't want the |value| interpreted as having + + // Using BlobStorage since we don't want the |value| interpreted as having // any particular character encoding (e.g., UTF-8) by // base::DictionaryValue. - base::Value::CreateWithCopiedBuffer(value.data(), value.size())); + base::Value(base::Value::BlobStorage(value.begin(), value.end()))); } -namespace { -template<class TimeType> -void SetTimeValue(VideoFrameMetadata::Key key, - const TimeType& value, - base::DictionaryValue* dictionary) { - const int64_t internal_value = value.ToInternalValue(); - dictionary->SetWithoutPathExpansion( - ToInternalKey(key), base::Value::CreateWithCopiedBuffer( - reinterpret_cast<const char*>(&internal_value), - sizeof(internal_value))); -} -} // namespace - void VideoFrameMetadata::SetTimeDelta(Key key, const base::TimeDelta& value) { - SetTimeValue(key, value, &dictionary_); + dictionary_.SetKey(ToInternalKey(key), base::CreateTimeDeltaValue(value)); } void VideoFrameMetadata::SetTimeTicks(Key key, const base::TimeTicks& value) { - SetTimeValue(key, value, &dictionary_); + // Serialize TimeTicks as TimeDeltas. + dictionary_.SetKey(ToInternalKey(key), + base::CreateTimeDeltaValue(value - base::TimeTicks())); } void VideoFrameMetadata::SetUnguessableToken( @@ -90,72 +80,76 @@ void VideoFrameMetadata::SetRect(Key key, const gfx::Rect& value) { base::Value init[] = {base::Value(value.x()), base::Value(value.y()), base::Value(value.width()), base::Value(value.height())}; - SetValue(key, std::make_unique<base::ListValue>(base::Value::ListStorage{ - std::make_move_iterator(std::begin(init)), - std::make_move_iterator(std::end(init))})); -} - -void VideoFrameMetadata::SetValue(Key key, std::unique_ptr<base::Value> value) { - dictionary_.SetWithoutPathExpansion(ToInternalKey(key), std::move(value)); + dictionary_.SetKey(ToInternalKey(key), + base::Value(base::Value::ListStorage( + std::make_move_iterator(std::begin(init)), + std::make_move_iterator(std::end(init))))); } bool VideoFrameMetadata::GetBoolean(Key key, bool* value) const { DCHECK(value); - return dictionary_.GetBooleanWithoutPathExpansion(ToInternalKey(key), value); + auto opt_bool = dictionary_.FindBoolKey(ToInternalKey(key)); + if (opt_bool) + *value = opt_bool.value(); + + return opt_bool.has_value(); } bool VideoFrameMetadata::GetInteger(Key key, int* value) const { DCHECK(value); - return dictionary_.GetIntegerWithoutPathExpansion(ToInternalKey(key), value); + auto opt_int = dictionary_.FindIntKey(ToInternalKey(key)); + if (opt_int) + *value = opt_int.value(); + + return opt_int.has_value(); } bool VideoFrameMetadata::GetDouble(Key key, double* value) const { DCHECK(value); - return dictionary_.GetDoubleWithoutPathExpansion(ToInternalKey(key), value); + auto opt_double = dictionary_.FindDoubleKey(ToInternalKey(key)); + if (opt_double) + *value = opt_double.value(); + + return opt_double.has_value(); } bool VideoFrameMetadata::GetRotation(Key key, VideoRotation* value) const { DCHECK_EQ(ROTATION, key); DCHECK(value); - int int_value; - const bool rv = dictionary_.GetIntegerWithoutPathExpansion(ToInternalKey(key), - &int_value); - if (rv) - *value = static_cast<VideoRotation>(int_value); - return rv; + auto opt_int = dictionary_.FindIntKey(ToInternalKey(key)); + if (opt_int) + *value = static_cast<VideoRotation>(opt_int.value()); + return opt_int.has_value(); } bool VideoFrameMetadata::GetString(Key key, std::string* value) const { DCHECK(value); - const base::Value* const binary_value = GetBinaryValue(key); - if (binary_value) - value->assign(binary_value->GetBlob().begin(), - binary_value->GetBlob().end()); - return !!binary_value; -} + const base::Value::BlobStorage* const binary_value = + dictionary_.FindBlobKey(ToInternalKey(key)); -namespace { -template <class TimeType> -bool ToTimeValue(const base::Value& binary_value, TimeType* value) { - DCHECK(value); - int64_t internal_value; - if (binary_value.GetBlob().size() != sizeof(internal_value)) - return false; - memcpy(&internal_value, binary_value.GetBlob().data(), - sizeof(internal_value)); - *value = TimeType::FromInternalValue(internal_value); - return true; + if (!!binary_value) + value->assign(binary_value->begin(), binary_value->end()); + + return !!binary_value; } -} // namespace bool VideoFrameMetadata::GetTimeDelta(Key key, base::TimeDelta* value) const { - const base::Value* const binary_value = GetBinaryValue(key); - return binary_value && ToTimeValue(*binary_value, value); + const base::Value* internal_value = dictionary_.FindKey(ToInternalKey(key)); + if (!internal_value) + return false; + return base::GetValueAsTimeDelta(*internal_value, value); } bool VideoFrameMetadata::GetTimeTicks(Key key, base::TimeTicks* value) const { - const base::Value* const binary_value = GetBinaryValue(key); - return binary_value && ToTimeValue(*binary_value, value); + // Deserialize TimeTicks from TimeDelta. + const base::Value* internal_value = dictionary_.FindKey(ToInternalKey(key)); + base::TimeDelta delta; + + if (!internal_value || !base::GetValueAsTimeDelta(*internal_value, &delta)) + return false; + + *value = base::TimeTicks() + delta; + return true; } bool VideoFrameMetadata::GetUnguessableToken( @@ -168,7 +162,8 @@ bool VideoFrameMetadata::GetUnguessableToken( } bool VideoFrameMetadata::GetRect(Key key, gfx::Rect* value) const { - const base::ListValue* internal_value = GetList(key); + const base::Value* internal_value = + dictionary_.FindListKey(ToInternalKey(key)); if (!internal_value || internal_value->GetList().size() != 4) return false; *value = gfx::Rect(internal_value->GetList()[0].GetInt(), @@ -178,32 +173,14 @@ bool VideoFrameMetadata::GetRect(Key key, gfx::Rect* value) const { return true; } -const base::ListValue* VideoFrameMetadata::GetList(Key key) const { - return static_cast<const base::ListValue*>( - dictionary_.FindKeyOfType(ToInternalKey(key), base::Value::Type::LIST)); -} - -const base::Value* VideoFrameMetadata::GetValue(Key key) const { - return dictionary_.FindKey(ToInternalKey(key)); -} - bool VideoFrameMetadata::IsTrue(Key key) const { bool value = false; return GetBoolean(key, &value) && value; } -std::unique_ptr<base::DictionaryValue> VideoFrameMetadata::CopyInternalValues() - const { - return dictionary_.CreateDeepCopy(); -} - void VideoFrameMetadata::MergeInternalValuesFrom(const base::Value& in) { - const base::DictionaryValue* dict; - if (!in.GetAsDictionary(&dict)) { - NOTREACHED(); - return; - } - dictionary_.MergeDictionary(dict); + // This function CHECKs if |in| is a dictionary. + dictionary_.MergeDictionary(&in); } void VideoFrameMetadata::MergeMetadataFrom( @@ -211,11 +188,4 @@ void VideoFrameMetadata::MergeMetadataFrom( dictionary_.MergeDictionary(&metadata_source->dictionary_); } -const base::Value* VideoFrameMetadata::GetBinaryValue(Key key) const { - const base::Value* internal_value = dictionary_.FindKey(ToInternalKey(key)); - if (internal_value && (internal_value->type() == base::Value::Type::BINARY)) - return internal_value; - return nullptr; -} - } // namespace media diff --git a/chromium/media/base/video_frame_metadata.h b/chromium/media/base/video_frame_metadata.h index 8ae0303db90..4e873e4152d 100644 --- a/chromium/media/base/video_frame_metadata.h +++ b/chromium/media/base/video_frame_metadata.h @@ -177,6 +177,18 @@ class MEDIA_EXPORT VideoFrameMetadata { // https://w3c.github.io/webrtc-pc/#dom-rtcrtpcontributingsource RTP_TIMESTAMP, + // For video frames coming from a remote source, this is the time the + // encoded frame was received by the platform, i.e., the time at + // which the last packet belonging to this frame was received over the + // network. + RECEIVE_TIME, + + // If present, this field represents the duration this frame is ideally + // expected to spend on the screen during playback. Unlike FRAME_DURATION + // this field takes into account current playback rate. + // Use Get/SetTimeDelta() for this key. + WALLCLOCK_FRAME_DURATION, + NUM_KEYS }; @@ -197,7 +209,6 @@ class MEDIA_EXPORT VideoFrameMetadata { void SetTimeTicks(Key key, const base::TimeTicks& value); void SetUnguessableToken(Key key, const base::UnguessableToken& value); void SetRect(Key key, const gfx::Rect& value); - void SetValue(Key key, std::unique_ptr<base::Value> value); // Getters. Returns true if |key| is present, and its value has been set. bool GetBoolean(Key key, bool* value) const WARN_UNUSED_RESULT; @@ -210,16 +221,11 @@ class MEDIA_EXPORT VideoFrameMetadata { bool GetUnguessableToken(Key key, base::UnguessableToken* value) const WARN_UNUSED_RESULT; bool GetRect(Key key, gfx::Rect* value) const WARN_UNUSED_RESULT; - // Returns null if |key| was not present or value was not a ListValue. - const base::ListValue* GetList(Key key) const WARN_UNUSED_RESULT; - // Returns null if |key| was not present. - const base::Value* GetValue(Key key) const WARN_UNUSED_RESULT; // Convenience method that returns true if |key| exists and is set to true. bool IsTrue(Key key) const WARN_UNUSED_RESULT; // For serialization. - std::unique_ptr<base::DictionaryValue> CopyInternalValues() const; void MergeInternalValuesFrom(const base::Value& in); const base::Value& GetInternalValues() const { return dictionary_; } @@ -227,8 +233,6 @@ class MEDIA_EXPORT VideoFrameMetadata { void MergeMetadataFrom(const VideoFrameMetadata* metadata_source); private: - const base::Value* GetBinaryValue(Key key) const; - base::DictionaryValue dictionary_; DISALLOW_COPY_AND_ASSIGN(VideoFrameMetadata); diff --git a/chromium/media/base/video_frame_pool.cc b/chromium/media/base/video_frame_pool.cc index 37dd36ad513..1d029f054b6 100644 --- a/chromium/media/base/video_frame_pool.cc +++ b/chromium/media/base/video_frame_pool.cc @@ -85,14 +85,12 @@ scoped_refptr<VideoFrame> VideoFramePool::PoolImpl::CreateFrame( DCHECK(!is_shutdown_); scoped_refptr<VideoFrame> frame; - while (!frame && !frames_.empty()) { + while (!frames_.empty()) { scoped_refptr<VideoFrame> pool_frame = std::move(frames_.back().frame); frames_.pop_back(); - if (pool_frame->format() == format && - pool_frame->coded_size() == coded_size && - pool_frame->visible_rect() == visible_rect && - pool_frame->natural_size() == natural_size) { + if (pool_frame->IsSameAllocation(format, coded_size, visible_rect, + natural_size)) { frame = pool_frame; frame->set_timestamp(timestamp); frame->metadata()->Clear(); diff --git a/chromium/media/base/video_frame_pool_unittest.cc b/chromium/media/base/video_frame_pool_unittest.cc index 231f58c8099..1588e9d919b 100644 --- a/chromium/media/base/video_frame_pool_unittest.cc +++ b/chromium/media/base/video_frame_pool_unittest.cc @@ -5,14 +5,17 @@ #include <stddef.h> #include <stdint.h> #include <memory> +#include <tuple> +#include "base/bits.h" #include "base/test/simple_test_tick_clock.h" #include "media/base/video_frame_pool.h" #include "testing/gmock/include/gmock/gmock.h" namespace media { -class VideoFramePoolTest : public ::testing::TestWithParam<VideoPixelFormat> { +class VideoFramePoolTest + : public ::testing::TestWithParam<std::tuple<VideoPixelFormat, gfx::Size>> { public: VideoFramePoolTest() : pool_(new VideoFramePool()) { // Seed test clock with some dummy non-zero value to avoid confusion with @@ -22,8 +25,8 @@ class VideoFramePoolTest : public ::testing::TestWithParam<VideoPixelFormat> { } scoped_refptr<VideoFrame> CreateFrame(VideoPixelFormat format, + const gfx::Size& coded_size, int timestamp_ms) { - gfx::Size coded_size(320,240); gfx::Rect visible_rect(coded_size); gfx::Size natural_size(coded_size); @@ -34,7 +37,13 @@ class VideoFramePoolTest : public ::testing::TestWithParam<VideoPixelFormat> { EXPECT_EQ(format, frame->format()); EXPECT_EQ(base::TimeDelta::FromMilliseconds(timestamp_ms), frame->timestamp()); - EXPECT_EQ(coded_size, frame->coded_size()); + if (format == PIXEL_FORMAT_ARGB) { + EXPECT_EQ(coded_size, frame->coded_size()); + } else { + const gfx::Size adjusted(base::bits::Align(coded_size.width(), 2), + base::bits::Align(coded_size.height(), 2)); + EXPECT_EQ(adjusted, frame->coded_size()); + } EXPECT_EQ(visible_rect, frame->visible_rect()); EXPECT_EQ(natural_size, frame->natural_size()); @@ -51,34 +60,44 @@ class VideoFramePoolTest : public ::testing::TestWithParam<VideoPixelFormat> { }; TEST_P(VideoFramePoolTest, FrameInitializedAndZeroed) { - scoped_refptr<VideoFrame> frame = CreateFrame(GetParam(), 10); + scoped_refptr<VideoFrame> frame = + CreateFrame(std::get<0>(GetParam()), std::get<1>(GetParam()), 10); // Verify that frame is initialized with zeros. for (size_t i = 0; i < VideoFrame::NumPlanes(frame->format()); ++i) EXPECT_EQ(0, frame->data(i)[0]); } -INSTANTIATE_TEST_SUITE_P(All, - VideoFramePoolTest, - testing::Values(PIXEL_FORMAT_I420, - PIXEL_FORMAT_NV12, - PIXEL_FORMAT_ARGB)); - -TEST_F(VideoFramePoolTest, SimpleFrameReuse) { - scoped_refptr<VideoFrame> frame = CreateFrame(PIXEL_FORMAT_I420, 10); +TEST_P(VideoFramePoolTest, FrameReuse) { + scoped_refptr<VideoFrame> frame = + CreateFrame(std::get<0>(GetParam()), std::get<1>(GetParam()), 10); const uint8_t* old_y_data = frame->data(VideoFrame::kYPlane); // Clear frame reference to return the frame to the pool. frame.reset(); // Verify that the next frame from the pool uses the same memory. - scoped_refptr<VideoFrame> new_frame = CreateFrame(PIXEL_FORMAT_I420, 20); + scoped_refptr<VideoFrame> new_frame = + CreateFrame(std::get<0>(GetParam()), std::get<1>(GetParam()), 20); EXPECT_EQ(old_y_data, new_frame->data(VideoFrame::kYPlane)); } +INSTANTIATE_TEST_SUITE_P(All, + VideoFramePoolTest, + ::testing::Combine(testing::Values(PIXEL_FORMAT_I420, + PIXEL_FORMAT_NV12, + PIXEL_FORMAT_ARGB), + testing::Values(gfx::Size(320, 240), + gfx::Size(321, 240), + gfx::Size(320, 241), + gfx::Size(321, + 241)))); + TEST_F(VideoFramePoolTest, SimpleFormatChange) { - scoped_refptr<VideoFrame> frame_a = CreateFrame(PIXEL_FORMAT_I420, 10); - scoped_refptr<VideoFrame> frame_b = CreateFrame(PIXEL_FORMAT_I420, 10); + scoped_refptr<VideoFrame> frame_a = + CreateFrame(PIXEL_FORMAT_I420, gfx::Size(320, 240), 10); + scoped_refptr<VideoFrame> frame_b = + CreateFrame(PIXEL_FORMAT_I420, gfx::Size(320, 240), 10); // Clear frame references to return the frames to the pool. frame_a.reset(); @@ -89,12 +108,14 @@ TEST_F(VideoFramePoolTest, SimpleFormatChange) { // Verify that requesting a frame with a different format causes the pool // to get drained. - scoped_refptr<VideoFrame> new_frame = CreateFrame(PIXEL_FORMAT_I420A, 10); + scoped_refptr<VideoFrame> new_frame = + CreateFrame(PIXEL_FORMAT_I420A, gfx::Size(320, 240), 10); CheckPoolSize(0u); } TEST_F(VideoFramePoolTest, FrameValidAfterPoolDestruction) { - scoped_refptr<VideoFrame> frame = CreateFrame(PIXEL_FORMAT_I420, 10); + scoped_refptr<VideoFrame> frame = + CreateFrame(PIXEL_FORMAT_I420, gfx::Size(320, 240), 10); // Destroy the pool. pool_.reset(); @@ -106,8 +127,10 @@ TEST_F(VideoFramePoolTest, FrameValidAfterPoolDestruction) { } TEST_F(VideoFramePoolTest, StaleFramesAreExpired) { - scoped_refptr<VideoFrame> frame_1 = CreateFrame(PIXEL_FORMAT_I420, 10); - scoped_refptr<VideoFrame> frame_2 = CreateFrame(PIXEL_FORMAT_I420, 10); + scoped_refptr<VideoFrame> frame_1 = + CreateFrame(PIXEL_FORMAT_I420, gfx::Size(320, 240), 10); + scoped_refptr<VideoFrame> frame_2 = + CreateFrame(PIXEL_FORMAT_I420, gfx::Size(320, 240), 10); EXPECT_NE(frame_1.get(), frame_2.get()); CheckPoolSize(0u); diff --git a/chromium/media/base/video_frame_unittest.cc b/chromium/media/base/video_frame_unittest.cc index 88024574d37..17a1b972c64 100644 --- a/chromium/media/base/video_frame_unittest.cc +++ b/chromium/media/base/video_frame_unittest.cc @@ -353,33 +353,6 @@ TEST(VideoFrame, WrapSharedMemory) { EXPECT_EQ(frame->data(media::VideoFrame::kYPlane)[0], 0xff); } -// Create a frame that wraps shared memory with an offset. -TEST(VideoFrame, WrapUnsafeSharedMemoryWithOffset) { - const size_t kOffset = 64; - const size_t kDataSize = 2 * 256 * 256; - base::UnsafeSharedMemoryRegion region = - base::UnsafeSharedMemoryRegion::Create(kDataSize + kOffset); - ASSERT_TRUE(region.IsValid()); - base::WritableSharedMemoryMapping mapping = region.Map(); - ASSERT_TRUE(mapping.IsValid()); - gfx::Size coded_size(256, 256); - gfx::Rect visible_rect(coded_size); - CreateTestY16Frame( - coded_size, visible_rect, - mapping.GetMemoryAsSpan<uint8_t>().subspan(kOffset).data()); - auto timestamp = base::TimeDelta::FromMilliseconds(1); - auto frame = VideoFrame::WrapExternalData( - media::PIXEL_FORMAT_Y16, coded_size, visible_rect, visible_rect.size(), - mapping.GetMemoryAsSpan<uint8_t>().subspan(kOffset).data(), kDataSize, - timestamp); - frame->BackWithSharedMemory(®ion, kOffset); - - EXPECT_EQ(frame->coded_size(), coded_size); - EXPECT_EQ(frame->visible_rect(), visible_rect); - EXPECT_EQ(frame->timestamp(), timestamp); - EXPECT_EQ(frame->data(media::VideoFrame::kYPlane)[0], 0xff); -} - TEST(VideoFrame, WrapExternalGpuMemoryBuffer) { gfx::Size coded_size = gfx::Size(256, 256); gfx::Rect visible_rect(coded_size); @@ -630,6 +603,7 @@ TEST(VideoFrame, AllocationSize_OddSize) { EXPECT_EQ(72u, VideoFrame::AllocationSize(format, size)) << VideoPixelFormatToString(format); break; + case PIXEL_FORMAT_UYVY: case PIXEL_FORMAT_YUY2: case PIXEL_FORMAT_I422: EXPECT_EQ(48u, VideoFrame::AllocationSize(format, size)) @@ -708,29 +682,44 @@ TEST(VideoFrameMetadata, SetAndThenGetAllKeysForAllTypes) { metadata.Clear(); EXPECT_FALSE(metadata.HasKey(key)); - metadata.SetTimeDelta(key, base::TimeDelta::FromInternalValue(42 + i)); + base::TimeDelta reference_delta = base::TimeDelta::FromMilliseconds(42 + i); + metadata.SetTimeDelta(key, reference_delta); EXPECT_TRUE(metadata.HasKey(key)); base::TimeDelta delta_value; EXPECT_TRUE(metadata.GetTimeDelta(key, &delta_value)); - EXPECT_EQ(base::TimeDelta::FromInternalValue(42 + i), delta_value); + EXPECT_EQ(reference_delta, delta_value); metadata.Clear(); EXPECT_FALSE(metadata.HasKey(key)); - metadata.SetTimeTicks(key, base::TimeTicks::FromInternalValue(~(0LL) + i)); + base::TimeTicks reference_ticks = + base::TimeTicks() + base::TimeDelta::FromMilliseconds(1234 + i); + metadata.SetTimeTicks(key, reference_ticks); EXPECT_TRUE(metadata.HasKey(key)); base::TimeTicks ticks_value; EXPECT_TRUE(metadata.GetTimeTicks(key, &ticks_value)); - EXPECT_EQ(base::TimeTicks::FromInternalValue(~(0LL) + i), ticks_value); + EXPECT_EQ(reference_ticks, ticks_value); metadata.Clear(); EXPECT_FALSE(metadata.HasKey(key)); - metadata.SetValue(key, std::make_unique<base::Value>()); + gfx::Rect reference_rect = gfx::Rect(3, 5, 240, 360); + metadata.SetRect(key, reference_rect); EXPECT_TRUE(metadata.HasKey(key)); - const base::Value* const null_value = metadata.GetValue(key); - EXPECT_TRUE(null_value); - EXPECT_EQ(base::Value::Type::NONE, null_value->type()); + gfx::Rect rect_value; + EXPECT_TRUE(metadata.GetRect(key, &rect_value)); + EXPECT_EQ(reference_rect, rect_value); metadata.Clear(); } + + // The Get/SetRotation methods only accept ROTATION as a key. + auto rot_key = VideoFrameMetadata::Key::ROTATION; + EXPECT_FALSE(metadata.HasKey(rot_key)); + VideoRotation reference_rot = VideoRotation::VIDEO_ROTATION_270; + metadata.SetRotation(rot_key, reference_rot); + EXPECT_TRUE(metadata.HasKey(rot_key)); + VideoRotation rot_value; + EXPECT_TRUE(metadata.GetRotation(rot_key, &rot_value)); + EXPECT_EQ(reference_rot, rot_value); + metadata.Clear(); } TEST(VideoFrameMetadata, PassMetadataViaIntermediary) { @@ -749,6 +738,16 @@ TEST(VideoFrameMetadata, PassMetadataViaIntermediary) { EXPECT_TRUE(result.GetInteger(key, &value)); EXPECT_EQ(i, value); } + + result.Clear(); + result.MergeInternalValuesFrom(expected.GetInternalValues()); + + for (int i = 0; i < VideoFrameMetadata::NUM_KEYS; ++i) { + const VideoFrameMetadata::Key key = static_cast<VideoFrameMetadata::Key>(i); + int value = -1; + EXPECT_TRUE(result.GetInteger(key, &value)); + EXPECT_EQ(i, value); + } } } // namespace media diff --git a/chromium/media/base/video_renderer.h b/chromium/media/base/video_renderer.h index c8268c571f4..bfbc676c856 100644 --- a/chromium/media/base/video_renderer.h +++ b/chromium/media/base/video_renderer.h @@ -7,6 +7,7 @@ #include "base/callback_forward.h" #include "base/macros.h" +#include "base/optional.h" #include "media/base/media_export.h" #include "media/base/pipeline_status.h" #include "media/base/time_source.h" @@ -42,7 +43,7 @@ class MEDIA_EXPORT VideoRenderer { CdmContext* cdm_context, RendererClient* client, const TimeSource::WallClockTimeCB& wall_clock_time_cb, - const PipelineStatusCB& init_cb) = 0; + PipelineStatusCallback init_cb) = 0; // Discards any video data and stops reading from |stream|, executing // |callback| when completed. @@ -63,6 +64,12 @@ class MEDIA_EXPORT VideoRenderer { virtual void OnTimeProgressing() = 0; virtual void OnTimeStopped() = 0; + // Sets a hint indicating target latency. See comment in header for + // media::Renderer::SetLatencyHint(). + // |latency_hint| may be nullopt to indicate the hint has been cleared + // (restore UA default). + virtual void SetLatencyHint(base::Optional<base::TimeDelta> latency_hint) = 0; + private: DISALLOW_COPY_AND_ASSIGN(VideoRenderer); }; diff --git a/chromium/media/base/video_thumbnail_decoder.cc b/chromium/media/base/video_thumbnail_decoder.cc index d6f67b05f69..130813a00fb 100644 --- a/chromium/media/base/video_thumbnail_decoder.cc +++ b/chromium/media/base/video_thumbnail_decoder.cc @@ -29,15 +29,15 @@ void VideoThumbnailDecoder::Start(VideoFrameCallback video_frame_callback) { DCHECK(video_frame_callback_); decoder_->Initialize( config_, false, nullptr, - base::BindRepeating(&VideoThumbnailDecoder::OnVideoDecoderInitialized, - weak_factory_.GetWeakPtr()), + base::BindOnce(&VideoThumbnailDecoder::OnVideoDecoderInitialized, + weak_factory_.GetWeakPtr()), base::BindRepeating(&VideoThumbnailDecoder::OnVideoFrameDecoded, weak_factory_.GetWeakPtr()), base::DoNothing()); } -void VideoThumbnailDecoder::OnVideoDecoderInitialized(bool success) { - if (!success) { +void VideoThumbnailDecoder::OnVideoDecoderInitialized(Status status) { + if (!status.is_ok()) { NotifyComplete(nullptr); return; } @@ -45,8 +45,8 @@ void VideoThumbnailDecoder::OnVideoDecoderInitialized(bool success) { auto buffer = DecoderBuffer::CopyFrom(&encoded_data_[0], encoded_data_.size()); encoded_data_.clear(); - decoder_->Decode( - buffer, base::BindRepeating(&VideoThumbnailDecoder::OnVideoBufferDecoded, + decoder_->Decode(buffer, + base::BindOnce(&VideoThumbnailDecoder::OnVideoBufferDecoded, weak_factory_.GetWeakPtr())); } @@ -57,10 +57,9 @@ void VideoThumbnailDecoder::OnVideoBufferDecoded(DecodeStatus status) { } // Enqueue eos since only one video frame is needed for thumbnail. - decoder_->Decode( - DecoderBuffer::CreateEOSBuffer(), - base::BindRepeating(&VideoThumbnailDecoder::OnEosBufferDecoded, - weak_factory_.GetWeakPtr())); + decoder_->Decode(DecoderBuffer::CreateEOSBuffer(), + base::BindOnce(&VideoThumbnailDecoder::OnEosBufferDecoded, + weak_factory_.GetWeakPtr())); } void VideoThumbnailDecoder::OnEosBufferDecoded(DecodeStatus status) { diff --git a/chromium/media/base/video_thumbnail_decoder.h b/chromium/media/base/video_thumbnail_decoder.h index 363006f1208..69f0c4faf76 100644 --- a/chromium/media/base/video_thumbnail_decoder.h +++ b/chromium/media/base/video_thumbnail_decoder.h @@ -38,7 +38,7 @@ class MEDIA_EXPORT VideoThumbnailDecoder { void Start(VideoFrameCallback video_frame_callback); private: - void OnVideoDecoderInitialized(bool success); + void OnVideoDecoderInitialized(Status status); void OnVideoBufferDecoded(DecodeStatus status); void OnEosBufferDecoded(DecodeStatus status); diff --git a/chromium/media/base/video_thumbnail_decoder_unittest.cc b/chromium/media/base/video_thumbnail_decoder_unittest.cc index 57550ae10bd..93e343345b9 100644 --- a/chromium/media/base/video_thumbnail_decoder_unittest.cc +++ b/chromium/media/base/video_thumbnail_decoder_unittest.cc @@ -85,8 +85,8 @@ class VideoThumbnailDecoderTest : public testing::Test { TEST_F(VideoThumbnailDecoderTest, Success) { auto expected_frame = CreateFrame(); EXPECT_CALL(*mock_video_decoder(), Initialize_(_, _, _, _, _, _)) - .WillOnce( - DoAll(RunOnceCallback<3>(true), RunCallback<4>(expected_frame))); + .WillOnce(DoAll(RunOnceCallback<3>(OkStatus()), + RunCallback<4>(expected_frame))); EXPECT_CALL(*mock_video_decoder(), Decode_(_, _)) .Times(2) .WillRepeatedly(RunOnceCallback<1>(DecodeStatus::OK)); @@ -99,7 +99,7 @@ TEST_F(VideoThumbnailDecoderTest, Success) { TEST_F(VideoThumbnailDecoderTest, InitializationFailed) { auto expected_frame = CreateFrame(); EXPECT_CALL(*mock_video_decoder(), Initialize_(_, _, _, _, _, _)) - .WillOnce(RunOnceCallback<3>(false)); + .WillOnce(RunOnceCallback<3>(StatusCode::kCodeOnlyForTesting)); Start(); EXPECT_FALSE(frame()); @@ -109,7 +109,7 @@ TEST_F(VideoThumbnailDecoderTest, InitializationFailed) { TEST_F(VideoThumbnailDecoderTest, DecodingFailed) { auto expected_frame = CreateFrame(); EXPECT_CALL(*mock_video_decoder(), Initialize_(_, _, _, _, _, _)) - .WillOnce(RunOnceCallback<3>(true)); + .WillOnce(RunOnceCallback<3>(OkStatus())); EXPECT_CALL(*mock_video_decoder(), Decode_(_, _)) .WillOnce(RunOnceCallback<1>(DecodeStatus::DECODE_ERROR)); diff --git a/chromium/media/base/video_types.cc b/chromium/media/base/video_types.cc index 624f89deff0..6f23ea771a0 100644 --- a/chromium/media/base/video_types.cc +++ b/chromium/media/base/video_types.cc @@ -27,6 +27,8 @@ std::string VideoPixelFormatToString(VideoPixelFormat format) { return "PIXEL_FORMAT_NV12"; case PIXEL_FORMAT_NV21: return "PIXEL_FORMAT_NV21"; + case PIXEL_FORMAT_UYVY: + return "PIXEL_FORMAT_UYVY"; case PIXEL_FORMAT_YUY2: return "PIXEL_FORMAT_YUY2"; case PIXEL_FORMAT_ARGB: @@ -112,6 +114,7 @@ bool IsYuvPlanar(VideoPixelFormat format) { return true; case PIXEL_FORMAT_UNKNOWN: + case PIXEL_FORMAT_UYVY: case PIXEL_FORMAT_YUY2: case PIXEL_FORMAT_ARGB: case PIXEL_FORMAT_XRGB: @@ -138,6 +141,7 @@ bool IsOpaque(VideoPixelFormat format) { case PIXEL_FORMAT_NV12: case PIXEL_FORMAT_NV21: case PIXEL_FORMAT_YUY2: + case PIXEL_FORMAT_UYVY: case PIXEL_FORMAT_XRGB: case PIXEL_FORMAT_RGB24: case PIXEL_FORMAT_MJPEG: @@ -178,6 +182,7 @@ size_t BitDepth(VideoPixelFormat format) { case PIXEL_FORMAT_NV12: case PIXEL_FORMAT_NV21: case PIXEL_FORMAT_YUY2: + case PIXEL_FORMAT_UYVY: case PIXEL_FORMAT_ARGB: case PIXEL_FORMAT_XRGB: case PIXEL_FORMAT_RGB24: diff --git a/chromium/media/base/video_types.h b/chromium/media/base/video_types.h index 8fda6d85b19..2205036a07c 100644 --- a/chromium/media/base/video_types.h +++ b/chromium/media/base/video_types.h @@ -37,7 +37,8 @@ enum VideoPixelFormat { 6, // 12bpp with Y plane followed by a 2x2 interleaved UV plane. PIXEL_FORMAT_NV21 = 7, // 12bpp with Y plane followed by a 2x2 interleaved VU plane. - /* PIXEL_FORMAT_UYVY = 8, Deprecated */ + PIXEL_FORMAT_UYVY = + 8, // 16bpp interleaved 2x1 U, 1x1 Y, 2x1 V, 1x1 Y samples. PIXEL_FORMAT_YUY2 = 9, // 16bpp interleaved 1x1 Y, 2x1 U, 1x1 Y, 2x1 V samples. PIXEL_FORMAT_ARGB = 10, // 32bpp BGRA (byte-order), 1 plane. diff --git a/chromium/media/base/video_util.cc b/chromium/media/base/video_util.cc index e97350aaf81..bc4c55619d3 100644 --- a/chromium/media/base/video_util.cc +++ b/chromium/media/base/video_util.cc @@ -382,6 +382,10 @@ gfx::Size ScaleSizeToEncompassTarget(const gfx::Size& size, return ScaleSizeToTarget(size, target, false); } +gfx::Size GetRectSizeFromOrigin(const gfx::Rect& rect) { + return gfx::Size(rect.right(), rect.bottom()); +} + gfx::Size PadToMatchAspectRatio(const gfx::Size& size, const gfx::Size& target) { if (target.IsEmpty()) diff --git a/chromium/media/base/video_util.h b/chromium/media/base/video_util.h index b40331c0293..42e060a25b7 100644 --- a/chromium/media/base/video_util.h +++ b/chromium/media/base/video_util.h @@ -114,6 +114,17 @@ MEDIA_EXPORT gfx::Size ScaleSizeToFitWithinTarget(const gfx::Size& size, MEDIA_EXPORT gfx::Size ScaleSizeToEncompassTarget(const gfx::Size& size, const gfx::Size& target); +// Returns the size of a rectangle whose upper left corner is at the origin (0, +// 0) and whose bottom right corner is the same as that of |rect|. This is +// useful to get the size of a buffer that contains the visible rectangle plus +// the non-visible area above and to the left of the visible rectangle. +// +// An example to illustrate: suppose the visible rectangle of a decoded frame is +// 10,10,100,100. The size of this rectangle is 90x90. However, we need to +// create a texture of size 100x100 because the client will want to sample from +// the texture starting with uv coordinates corresponding to 10,10. +MEDIA_EXPORT gfx::Size GetRectSizeFromOrigin(const gfx::Rect& rect); + // Returns |size| with only one of its dimensions increased such that the result // matches the aspect ratio of |target|. This is different from // ScaleSizeToEncompassTarget() in two ways: 1) The goal is to match the aspect diff --git a/chromium/media/base/video_util_unittest.cc b/chromium/media/base/video_util_unittest.cc index b4b5513b35d..79af565e5b0 100644 --- a/chromium/media/base/video_util_unittest.cc +++ b/chromium/media/base/video_util_unittest.cc @@ -458,6 +458,12 @@ TEST_F(VideoUtilTest, ComputeLetterboxRegion) { gfx::Size(40000, 30000))); EXPECT_TRUE(ComputeLetterboxRegion(gfx::Rect(0, 0, 2000000000, 2000000000), gfx::Size(0, 0)).IsEmpty()); + + // Some operations in the internal ScaleSizeToTarget() use rounded division + // and might lose some precision, this expectation codifies that. + EXPECT_EQ( + gfx::Rect(0, 0, 1279, 720), + ComputeLetterboxRegion(gfx::Rect(0, 0, 1280, 720), gfx::Size(1057, 595))); } // Tests the ComputeLetterboxRegionForI420 function. diff --git a/chromium/media/base/win/BUILD.gn b/chromium/media/base/win/BUILD.gn index d851563429a..c4d20d2fed4 100644 --- a/chromium/media/base/win/BUILD.gn +++ b/chromium/media/base/win/BUILD.gn @@ -45,12 +45,8 @@ jumbo_component("media_foundation_util") { } source_set("d3d11") { - sources = [ - "d3d11_create_device_cb.h", - ] - deps = [ - "//base", - ] + sources = [ "d3d11_create_device_cb.h" ] + deps = [ "//base" ] } source_set("d3d11_test_support") { diff --git a/chromium/media/base/win/d3d11_mocks.cc b/chromium/media/base/win/d3d11_mocks.cc index 4dddc97321d..b921f2551e8 100644 --- a/chromium/media/base/win/d3d11_mocks.cc +++ b/chromium/media/base/win/d3d11_mocks.cc @@ -18,12 +18,21 @@ D3D11BufferMock::~D3D11BufferMock() = default; D3D11DeviceMock::D3D11DeviceMock() = default; D3D11DeviceMock::~D3D11DeviceMock() = default; +DXGIFactoryMock::DXGIFactoryMock() = default; +DXGIFactoryMock::~DXGIFactoryMock() = default; + DXGIDeviceMock::DXGIDeviceMock() = default; DXGIDeviceMock::~DXGIDeviceMock() = default; DXGIDevice2Mock::DXGIDevice2Mock() = default; DXGIDevice2Mock::~DXGIDevice2Mock() = default; +DXGIOutputMock::DXGIOutputMock() = default; +DXGIOutputMock::~DXGIOutputMock() = default; + +DXGIOutput6Mock::DXGIOutput6Mock() = default; +DXGIOutput6Mock::~DXGIOutput6Mock() = default; + DXGIAdapterMock::DXGIAdapterMock() = default; DXGIAdapterMock::~DXGIAdapterMock() = default; diff --git a/chromium/media/base/win/d3d11_mocks.h b/chromium/media/base/win/d3d11_mocks.h index 81169c0dbdb..fef030f6ca5 100644 --- a/chromium/media/base/win/d3d11_mocks.h +++ b/chromium/media/base/win/d3d11_mocks.h @@ -8,6 +8,7 @@ #include <d3d11.h> #include <d3d11_1.h> #include <dxgi1_4.h> +#include <dxgi1_6.h> #include <wrl/client.h> #include <wrl/implements.h> @@ -286,6 +287,28 @@ class D3D11DeviceMock MOCK_STDCALL_METHOD0(GetExceptionMode, UINT()); }; +class DXGIFactoryMock + : public Microsoft::WRL::RuntimeClass< + Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>, + IDXGIFactory> { + public: + DXGIFactoryMock(); + ~DXGIFactoryMock() override; + MOCK_STDCALL_METHOD2(CreateSoftwareAdapter, HRESULT(HMODULE, IDXGIAdapter**)); + MOCK_STDCALL_METHOD3(CreateSwapChain, + HRESULT(IUnknown*, + DXGI_SWAP_CHAIN_DESC*, + IDXGISwapChain**)); + MOCK_STDCALL_METHOD2(EnumAdapters, HRESULT(UINT, IDXGIAdapter**)); + MOCK_STDCALL_METHOD1(GetWindowAssociation, HRESULT(HWND*)); + MOCK_STDCALL_METHOD2(MakeWindowAssociation, HRESULT(HWND, UINT)); + MOCK_STDCALL_METHOD3(SetPrivateData, HRESULT(REFGUID, UINT, const void*)); + MOCK_STDCALL_METHOD2(SetPrivateDataInterface, + HRESULT(REFGUID, const IUnknown*)); + MOCK_STDCALL_METHOD2(GetParent, HRESULT(REFIID, void**)); + MOCK_STDCALL_METHOD3(GetPrivateData, HRESULT(REFGUID, UINT*, void*)); +}; + class DXGIDeviceMock : public Microsoft::WRL::RuntimeClass< Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>, @@ -417,7 +440,112 @@ class DXGIAdapterMock HRESULT(REFGUID, const IUnknown*)); }; -// TODO(crbug.com/788880): This may not be necessary. Tyr out and see if +class DXGIOutputMock + : public Microsoft::WRL::RuntimeClass< + Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>, + IDXGIOutput> { + public: + DXGIOutputMock(); + ~DXGIOutputMock() override; + MOCK_STDCALL_METHOD3(FindClosestMatchingMode, + HRESULT(const DXGI_MODE_DESC*, + DXGI_MODE_DESC*, + IUnknown*)); + MOCK_STDCALL_METHOD1(GetDesc, HRESULT(DXGI_OUTPUT_DESC*)); + MOCK_STDCALL_METHOD4(GetDisplayModeList, + HRESULT(DXGI_FORMAT, UINT, UINT*, DXGI_MODE_DESC*)); + MOCK_STDCALL_METHOD1(GetDisplaySurfaceData, HRESULT(IDXGISurface*)); + MOCK_STDCALL_METHOD1(GetFrameStatistics, HRESULT(DXGI_FRAME_STATISTICS*)); + MOCK_STDCALL_METHOD1(GetGammaControl, HRESULT(DXGI_GAMMA_CONTROL*)); + MOCK_STDCALL_METHOD1(GetGammaControlCapabilities, + HRESULT(DXGI_GAMMA_CONTROL_CAPABILITIES*)); + MOCK_STDCALL_METHOD0(ReleaseOwnership, void()); + MOCK_STDCALL_METHOD1(SetDisplaySurface, HRESULT(IDXGISurface*)); + MOCK_STDCALL_METHOD1(SetGammaControl, HRESULT(const DXGI_GAMMA_CONTROL*)); + MOCK_STDCALL_METHOD2(TakeOwnership, HRESULT(IUnknown*, BOOL)); + MOCK_STDCALL_METHOD0(WaitForVBlank, HRESULT()); + + MOCK_STDCALL_METHOD3(SetPrivateData, HRESULT(REFGUID, UINT, const void*)); + MOCK_STDCALL_METHOD2(SetPrivateDataInterface, + HRESULT(REFGUID, const IUnknown*)); + MOCK_STDCALL_METHOD2(GetParent, HRESULT(REFIID, void**)); + MOCK_STDCALL_METHOD3(GetPrivateData, HRESULT(REFGUID, UINT*, void*)); + + // IUnknown + MOCK_STDCALL_METHOD2(QueryInterface, HRESULT(REFIID riid, void** ppv)); +}; + +class DXGIOutput6Mock + : public Microsoft::WRL::RuntimeClass< + Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>, + IDXGIOutput6> { + public: + DXGIOutput6Mock(); + ~DXGIOutput6Mock() override; + + // IDXGIOutput6 + MOCK_STDCALL_METHOD1(GetDesc1, HRESULT(DXGI_OUTPUT_DESC1*)); + MOCK_STDCALL_METHOD1(CheckHardwareCompositionSupport, HRESULT(UINT*)); + + // IDXGIOutput5 + MOCK_STDCALL_METHOD5(DuplicateOutput1, + HRESULT(IUnknown*, + UINT, + UINT, + const DXGI_FORMAT*, + IDXGIOutputDuplication**)); + + // IDXGIOutput4 + MOCK_STDCALL_METHOD4( + CheckOverlayColorSpaceSupport, + HRESULT(DXGI_FORMAT, DXGI_COLOR_SPACE_TYPE, IUnknown*, UINT*)); + + // IDXGIOutput3 + MOCK_STDCALL_METHOD3(CheckOverlaySupport, + HRESULT(DXGI_FORMAT, IUnknown*, UINT*)); + + // IDXGIOutput2 + MOCK_STDCALL_METHOD0(SupportsOverlays, BOOL()); + + // IDXGIOutput1 + MOCK_STDCALL_METHOD2(DuplicateOutput, + HRESULT(IUnknown*, IDXGIOutputDuplication**)); + MOCK_STDCALL_METHOD3(FindClosestMatchingMode1, + HRESULT(const DXGI_MODE_DESC1*, + DXGI_MODE_DESC1*, + IUnknown*)); + MOCK_STDCALL_METHOD4(GetDisplayModeList1, + HRESULT(DXGI_FORMAT, UINT, UINT*, DXGI_MODE_DESC1*)); + MOCK_STDCALL_METHOD1(GetDisplaySurfaceData1, HRESULT(IDXGIResource*)); + + // IDXGIOutput + MOCK_STDCALL_METHOD3(FindClosestMatchingMode, + HRESULT(const DXGI_MODE_DESC*, + DXGI_MODE_DESC*, + IUnknown*)); + MOCK_STDCALL_METHOD1(GetDesc, HRESULT(DXGI_OUTPUT_DESC*)); + MOCK_STDCALL_METHOD4(GetDisplayModeList, + HRESULT(DXGI_FORMAT, UINT, UINT*, DXGI_MODE_DESC*)); + MOCK_STDCALL_METHOD1(GetDisplaySurfaceData, HRESULT(IDXGISurface*)); + MOCK_STDCALL_METHOD1(GetFrameStatistics, HRESULT(DXGI_FRAME_STATISTICS*)); + MOCK_STDCALL_METHOD1(GetGammaControl, HRESULT(DXGI_GAMMA_CONTROL*)); + MOCK_STDCALL_METHOD1(GetGammaControlCapabilities, + HRESULT(DXGI_GAMMA_CONTROL_CAPABILITIES*)); + MOCK_STDCALL_METHOD0(ReleaseOwnership, void()); + MOCK_STDCALL_METHOD1(SetDisplaySurface, HRESULT(IDXGISurface*)); + MOCK_STDCALL_METHOD1(SetGammaControl, HRESULT(const DXGI_GAMMA_CONTROL*)); + MOCK_STDCALL_METHOD2(TakeOwnership, HRESULT(IUnknown*, BOOL)); + MOCK_STDCALL_METHOD0(WaitForVBlank, HRESULT()); + + // IDXGIObject + MOCK_STDCALL_METHOD3(SetPrivateData, HRESULT(REFGUID, UINT, const void*)); + MOCK_STDCALL_METHOD2(SetPrivateDataInterface, + HRESULT(REFGUID, const IUnknown*)); + MOCK_STDCALL_METHOD2(GetParent, HRESULT(REFIID, void**)); + MOCK_STDCALL_METHOD3(GetPrivateData, HRESULT(REFGUID, UINT*, void*)); +}; + +// TODO(crbug.com/788880): This may not be necessary. Try out and see if // D3D11VideoDevice1Mock is sufficient. and if so, remove this. class D3D11VideoDeviceMock : public Microsoft::WRL::RuntimeClass< diff --git a/chromium/media/base/win/dxgi_device_scope_handle_unittest.cc b/chromium/media/base/win/dxgi_device_scope_handle_unittest.cc new file mode 100644 index 00000000000..5058d1bfd31 --- /dev/null +++ b/chromium/media/base/win/dxgi_device_scope_handle_unittest.cc @@ -0,0 +1,91 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <d3d11.h> +#include <mfapi.h> + +#include "base/win/windows_version.h" +#include "media/base/test_helpers.h" +#include "media/base/win/mf_helpers.h" +#include "media/base/win/mf_initializer.h" + +namespace media { + +using Microsoft::WRL::ComPtr; + +class DXGIDeviceScopedHandleTest : public testing::Test { + public: + DXGIDeviceScopedHandleTest() + : test_supported_(base::win::GetVersion() >= base::win::Version::WIN10) {} + ~DXGIDeviceScopedHandleTest() override = default; + + protected: + void SetUp() override { + if (!test_supported_) + return; + + ASSERT_NE(nullptr, session_ = InitializeMediaFoundation()); + + // Get a shared DXGI Device Manager from Media Foundation. + ASSERT_HRESULT_SUCCEEDED( + MFLockDXGIDeviceManager(&device_reset_token_, &dxgi_device_man_)); + + // |dxgi_device_man_| does not create the device, creates Direct3D device. + ComPtr<ID3D11Device> d3d11_device; + UINT creation_flags = + (D3D11_CREATE_DEVICE_VIDEO_SUPPORT | D3D11_CREATE_DEVICE_BGRA_SUPPORT | + D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS); + static const D3D_FEATURE_LEVEL feature_levels[] = { + D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, + D3D_FEATURE_LEVEL_10_0, D3D_FEATURE_LEVEL_9_3, D3D_FEATURE_LEVEL_9_2, + D3D_FEATURE_LEVEL_9_1}; + ASSERT_HRESULT_SUCCEEDED( + D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, 0, creation_flags, + feature_levels, ARRAYSIZE(feature_levels), + D3D11_SDK_VERSION, &d3d11_device, nullptr, nullptr)); + + ComPtr<ID3D10Multithread> multithreaded_device; + ASSERT_HRESULT_SUCCEEDED(d3d11_device.As(&multithreaded_device)); + multithreaded_device->SetMultithreadProtected(TRUE); + + // Set Direct3D device to the device manager. + ASSERT_HRESULT_SUCCEEDED( + dxgi_device_man_->ResetDevice(d3d11_device.Get(), device_reset_token_)); + } + + void TearDown() override { + if (test_supported_) { + ASSERT_HRESULT_SUCCEEDED(MFUnlockDXGIDeviceManager()); + } + } + + MFSessionLifetime session_; + Microsoft::WRL::ComPtr<IMFDXGIDeviceManager> dxgi_device_man_ = nullptr; + UINT device_reset_token_ = 0; + const bool test_supported_; +}; + +TEST_F(DXGIDeviceScopedHandleTest, UseDXGIDeviceScopedHandle) { + if (!test_supported_) + return; + + { + // Create DXGIDeviceScopedHandle in an inner scope without LockDevice + // call. + DXGIDeviceScopedHandle device_handle_1(dxgi_device_man_.Get()); + } + { + // Create DXGIDeviceScopedHandle in an inner scope with LockDevice call. + DXGIDeviceScopedHandle device_handle_2(dxgi_device_man_.Get()); + ComPtr<ID3D11Device> device2; + ASSERT_HRESULT_SUCCEEDED( + device_handle_2.LockDevice(IID_PPV_ARGS(&device2))); + } + // Use the device in an outer scope. + DXGIDeviceScopedHandle device_handle_3(dxgi_device_man_.Get()); + ComPtr<ID3D11Device> device3; + ASSERT_HRESULT_SUCCEEDED(device_handle_3.LockDevice(IID_PPV_ARGS(&device3))); +} + +} // namespace media
\ No newline at end of file diff --git a/chromium/media/base/win/mf_helpers.cc b/chromium/media/base/win/mf_helpers.cc index cb331bb4473..fce7ada08e4 100644 --- a/chromium/media/base/win/mf_helpers.cc +++ b/chromium/media/base/win/mf_helpers.cc @@ -4,25 +4,15 @@ #include "media/base/win/mf_helpers.h" -#include "base/metrics/histogram_functions.h" - namespace media { -namespace mf { - -void LogDXVAError(int line) { - LOG(ERROR) << "Error in dxva_video_decode_accelerator_win.cc on line " - << line; - base::UmaHistogramSparse("Media.DXVAVDA.ErrorLine", line); -} - Microsoft::WRL::ComPtr<IMFSample> CreateEmptySampleWithBuffer( uint32_t buffer_length, int align) { CHECK_GT(buffer_length, 0U); Microsoft::WRL::ComPtr<IMFSample> sample; - HRESULT hr = MFCreateSample(sample.GetAddressOf()); + HRESULT hr = MFCreateSample(&sample); RETURN_ON_HR_FAILURE(hr, "MFCreateSample failed", Microsoft::WRL::ComPtr<IMFSample>()); @@ -30,10 +20,9 @@ Microsoft::WRL::ComPtr<IMFSample> CreateEmptySampleWithBuffer( if (align == 0) { // Note that MFCreateMemoryBuffer is same as MFCreateAlignedMemoryBuffer // with the align argument being 0. - hr = MFCreateMemoryBuffer(buffer_length, buffer.GetAddressOf()); + hr = MFCreateMemoryBuffer(buffer_length, &buffer); } else { - hr = MFCreateAlignedMemoryBuffer(buffer_length, align - 1, - buffer.GetAddressOf()); + hr = MFCreateAlignedMemoryBuffer(buffer_length, align - 1, &buffer); } RETURN_ON_HR_FAILURE(hr, "Failed to create memory buffer for sample", Microsoft::WRL::ComPtr<IMFSample>()); @@ -60,6 +49,32 @@ MediaBufferScopedPointer::~MediaBufferScopedPointer() { CHECK(SUCCEEDED(hr)); } -} // namespace mf +DXGIDeviceScopedHandle::DXGIDeviceScopedHandle( + IMFDXGIDeviceManager* device_manager) + : device_manager_(device_manager) {} + +DXGIDeviceScopedHandle::~DXGIDeviceScopedHandle() { + if (device_handle_ != INVALID_HANDLE_VALUE) { + HRESULT hr = device_manager_->CloseDeviceHandle(device_handle_); + CHECK(SUCCEEDED(hr)); + device_handle_ = INVALID_HANDLE_VALUE; + } +} + +HRESULT DXGIDeviceScopedHandle::LockDevice(REFIID riid, void** device_out) { + HRESULT hr; + if (device_handle_ == INVALID_HANDLE_VALUE) { + hr = device_manager_->OpenDeviceHandle(&device_handle_); + if (FAILED(hr)) { + return hr; + } + } + // see + // https://docs.microsoft.com/en-us/windows/win32/api/mfobjects/nf-mfobjects-imfdxgidevicemanager-lockdevice + // for details of LockDevice call. + hr = device_manager_->LockDevice(device_handle_, riid, device_out, + /*block=*/FALSE); + return hr; +} } // namespace media diff --git a/chromium/media/base/win/mf_helpers.h b/chromium/media/base/win/mf_helpers.h index 56b661d0874..ac163084247 100644 --- a/chromium/media/base/win/mf_helpers.h +++ b/chromium/media/base/win/mf_helpers.h @@ -15,37 +15,34 @@ namespace media { -namespace mf { - -#define RETURN_ON_FAILURE(result, log, ret) \ - do { \ - if (!(result)) { \ - DLOG(ERROR) << log; \ - mf::LogDXVAError(__LINE__); \ - return ret; \ - } \ +// Macros that contain return statements can make code harder to read. Only use +// these when necessary, e.g. in places where we deal with a lot of Windows API +// calls, for each of which we have to check the returned HRESULT. +// See discussion thread at: +// https://groups.google.com/a/chromium.org/d/msg/cxx/zw5Xmcs--S4/r7Fwb-TsCAAJ + +#define RETURN_IF_FAILED(expr) \ + do { \ + HRESULT hresult = (expr); \ + if (FAILED(hresult)) { \ + DLOG(ERROR) << __func__ << ": failed with \"" \ + << logging::SystemErrorCodeToString(hresult) << "\""; \ + return hresult; \ + } \ } while (0) -#define RETURN_ON_HR_FAILURE(result, log, ret) \ - RETURN_ON_FAILURE(SUCCEEDED(result), \ - log << ", HRESULT: 0x" << std::hex << result, ret); - -#define RETURN_AND_NOTIFY_ON_FAILURE(result, log, error_code, ret) \ - do { \ - if (!(result)) { \ - DVLOG(1) << log; \ - mf::LogDXVAError(__LINE__); \ - StopOnError(error_code); \ - return ret; \ - } \ +#define RETURN_ON_FAILURE(success, log, ret) \ + do { \ + if (!(success)) { \ + DLOG(ERROR) << log; \ + return ret; \ + } \ } while (0) -#define RETURN_AND_NOTIFY_ON_HR_FAILURE(result, log, error_code, ret) \ - RETURN_AND_NOTIFY_ON_FAILURE(SUCCEEDED(result), \ - log << ", HRESULT: 0x" << std::hex << result, \ - error_code, ret); - -MF_INITIALIZER_EXPORT void LogDXVAError(int line); +#define RETURN_ON_HR_FAILURE(hresult, log, ret) \ + RETURN_ON_FAILURE(SUCCEEDED(hresult), \ + log << ", " << logging::SystemErrorCodeToString(hresult), \ + ret); // Creates a Media Foundation sample with one buffer of length |buffer_length| // on a |align|-byte boundary. Alignment must be a perfect power of 2 or 0. @@ -71,7 +68,20 @@ class MF_INITIALIZER_EXPORT MediaBufferScopedPointer { DISALLOW_COPY_AND_ASSIGN(MediaBufferScopedPointer); }; -} // namespace mf +// Wrap around the usage of device handle from |device_manager|. +class MF_INITIALIZER_EXPORT DXGIDeviceScopedHandle { + public: + explicit DXGIDeviceScopedHandle(IMFDXGIDeviceManager* device_manager); + + ~DXGIDeviceScopedHandle(); + + HRESULT LockDevice(REFIID riid, void** device_out); + + private: + Microsoft::WRL::ComPtr<IMFDXGIDeviceManager> device_manager_; + + HANDLE device_handle_ = INVALID_HANDLE_VALUE; +}; } // namespace media diff --git a/chromium/media/base/win/mf_initializer.cc b/chromium/media/base/win/mf_initializer.cc index 47d78225c91..a19f79f6ff4 100644 --- a/chromium/media/base/win/mf_initializer.cc +++ b/chromium/media/base/win/mf_initializer.cc @@ -8,13 +8,19 @@ #include "base/logging.h" +#include "base/optional.h" + namespace media { -bool InitializeMediaFoundation() { - static const bool success = MFStartup(MF_VERSION, MFSTARTUP_LITE) == S_OK; - DVLOG_IF(1, !success) - << "Media Foundation unavailable or it failed to initialize"; - return success; +MFSessionLifetime InitializeMediaFoundation() { + if (MFStartup(MF_VERSION, MFSTARTUP_LITE) == S_OK) + return std::make_unique<MFSession>(); + DVLOG(1) << "Media Foundation unavailable or it failed to initialize"; + return nullptr; +} + +MFSession::~MFSession() { + MFShutdown(); } } // namespace media diff --git a/chromium/media/base/win/mf_initializer.h b/chromium/media/base/win/mf_initializer.h index 982cdc6c01b..f1e1bd06a6c 100644 --- a/chromium/media/base/win/mf_initializer.h +++ b/chromium/media/base/win/mf_initializer.h @@ -5,15 +5,25 @@ #ifndef MEDIA_BASE_WIN_MF_INITIALIZER_H_ #define MEDIA_BASE_WIN_MF_INITIALIZER_H_ +#include <mfapi.h> + +#include "base/logging.h" #include "media/base/win/mf_initializer_export.h" namespace media { -// Makes sure MFStartup() is called exactly once. Returns true if Media -// Foundation is available and has been initialized successfully. Note that it -// is expected to return false on an "N" edition of Windows, see -// https://en.wikipedia.org/wiki/Windows_7_editions#Special-purpose_editions. -MF_INITIALIZER_EXPORT bool InitializeMediaFoundation(); +// Handy-dandy wrapper struct that kills MediaFoundation on destruction. +struct MF_INITIALIZER_EXPORT MFSession { + ~MFSession(); +}; + +using MFSessionLifetime = std::unique_ptr<MFSession>; + +// Make sure that MFShutdown is called for each MFStartup that is successful. +// The public documentation stating that it needs to have a corresponding +// shutdown for all startups (even failed ones) is wrong. +MF_INITIALIZER_EXPORT MFSessionLifetime InitializeMediaFoundation() + WARN_UNUSED_RESULT; } // namespace media |