summaryrefslogtreecommitdiff
path: root/chromium/media
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2020-10-06 12:48:11 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2020-10-13 09:33:43 +0000
commit7b5b123ac58f58ffde0f4f6e488bcd09aa4decd3 (patch)
treefa14ba0ca8d2683ba2efdabd246dc9b18a1229c6 /chromium/media
parent79b4f909db1049fca459c07cca55af56a9b54fe3 (diff)
downloadqtwebengine-chromium-7b5b123ac58f58ffde0f4f6e488bcd09aa4decd3.tar.gz
BASELINE: Update Chromium to 84.0.4147.141
Change-Id: Ib85eb4cfa1cbe2b2b81e5022c8cad5c493969535 Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/media')
-rw-r--r--chromium/media/BUILD.gn7
-rw-r--r--chromium/media/README.md5
-rw-r--r--chromium/media/audio/android/opensles_util.cc2
-rw-r--r--chromium/media/audio/audio_device_description.cc2
-rw-r--r--chromium/media/audio/audio_device_thread.cc2
-rw-r--r--chromium/media/audio/audio_input_device.cc11
-rw-r--r--chromium/media/audio/audio_output_device.cc3
-rw-r--r--chromium/media/audio/audio_output_device_thread_callback.cc50
-rw-r--r--chromium/media/audio/audio_output_device_thread_callback.h20
-rw-r--r--chromium/media/audio/audio_output_proxy.cc2
-rw-r--r--chromium/media/audio/cras/audio_manager_cras.cc2
-rw-r--r--chromium/media/audio/fake_audio_output_stream.cc2
-rw-r--r--chromium/media/audio/mock_audio_manager.cc2
-rw-r--r--chromium/media/audio/pulse/pulse_input.cc2
-rw-r--r--chromium/media/audio/simple_sources_unittest.cc1
-rw-r--r--chromium/media/audio/wav_audio_handler_unittest.cc1
-rw-r--r--chromium/media/audio/win/audio_output_win_unittest.cc72
-rw-r--r--chromium/media/audio/win/avrt_wrapper_win.cc4
-rw-r--r--chromium/media/base/BUILD.gn12
-rw-r--r--chromium/media/base/android/BUILD.gn10
-rw-r--r--chromium/media/base/android/media_codec_util.cc50
-rw-r--r--chromium/media/base/android/media_codec_util.h15
-rw-r--r--chromium/media/base/android/media_drm_bridge_client.cc2
-rw-r--r--chromium/media/base/android/media_drm_bridge_delegate.cc2
-rw-r--r--chromium/media/base/android/media_player_bridge.cc3
-rw-r--r--chromium/media/base/android/media_player_listener.cc2
-rw-r--r--chromium/media/base/android/stream_texture_wrapper.h1
-rw-r--r--chromium/media/base/audio_block_fifo.cc2
-rw-r--r--chromium/media/base/audio_buffer_converter.cc2
-rw-r--r--chromium/media/base/audio_buffer_queue.cc2
-rw-r--r--chromium/media/base/audio_buffer_queue_unittest.cc1
-rw-r--r--chromium/media/base/audio_bus.cc4
-rw-r--r--chromium/media/base/audio_codecs.cc1
-rw-r--r--chromium/media/base/audio_fifo.cc4
-rw-r--r--chromium/media/base/audio_hash_unittest.cc1
-rw-r--r--chromium/media/base/audio_latency_unittest.cc1
-rw-r--r--chromium/media/base/audio_parameters.cc3
-rw-r--r--chromium/media/base/audio_power_monitor.cc2
-rw-r--r--chromium/media/base/audio_pull_fifo.cc2
-rw-r--r--chromium/media/base/audio_push_fifo.cc3
-rw-r--r--chromium/media/base/audio_renderer_mixer.cc35
-rw-r--r--chromium/media/base/audio_renderer_mixer.h14
-rw-r--r--chromium/media/base/audio_timestamp_helper.cc2
-rw-r--r--chromium/media/base/bit_reader_core.cc1
-rw-r--r--chromium/media/base/buffering_state.cc5
-rw-r--r--chromium/media/base/byte_queue.cc3
-rw-r--r--chromium/media/base/cdm_callback_promise.cc2
-rw-r--r--chromium/media/base/cdm_context.cc9
-rw-r--r--chromium/media/base/cdm_context.h25
-rw-r--r--chromium/media/base/cdm_key_information.cc1
-rw-r--r--chromium/media/base/channel_mixer.cc3
-rw-r--r--chromium/media/base/channel_mixing_matrix.cc2
-rw-r--r--chromium/media/base/container_names.cc3
-rw-r--r--chromium/media/base/container_names.h5
-rw-r--r--chromium/media/base/data_source.cc1
-rw-r--r--chromium/media/base/decode_status.cc3
-rw-r--r--chromium/media/base/decoder_buffer.cc2
-rw-r--r--chromium/media/base/decrypt_config.cc2
-rw-r--r--chromium/media/base/demuxer.h8
-rw-r--r--chromium/media/base/encryption_scheme.cc1
-rw-r--r--chromium/media/base/fake_audio_worker.cc2
-rw-r--r--chromium/media/base/fake_demuxer_stream.cc3
-rw-r--r--chromium/media/base/hdr_metadata.h9
-rw-r--r--chromium/media/base/ipc/media_param_traits_macros.h15
-rw-r--r--chromium/media/base/key_systems_unittest.cc3
-rw-r--r--chromium/media/base/keyboard_event_counter.cc2
-rw-r--r--chromium/media/base/keyboard_event_counter_unittest.cc1
-rw-r--r--chromium/media/base/localized_strings.cc1
-rw-r--r--chromium/media/base/mac/audio_latency_mac.cc2
-rw-r--r--chromium/media/base/media_client.cc1
-rw-r--r--chromium/media/base/media_log.cc27
-rw-r--r--chromium/media/base/media_log.h26
-rw-r--r--chromium/media/base/media_log_events.cc11
-rw-r--r--chromium/media/base/media_log_events.h10
-rw-r--r--chromium/media/base/media_log_message_levels.cc2
-rw-r--r--chromium/media/base/media_log_properties.cc2
-rw-r--r--chromium/media/base/media_log_properties.h9
-rw-r--r--chromium/media/base/media_log_record.h2
-rw-r--r--chromium/media/base/media_log_type_enforcement.h10
-rw-r--r--chromium/media/base/media_log_unittest.cc70
-rw-r--r--chromium/media/base/media_observer.h3
-rw-r--r--chromium/media/base/media_serializers.h1
-rw-r--r--chromium/media/base/media_switches.cc59
-rw-r--r--chromium/media/base/media_switches.h13
-rw-r--r--chromium/media/base/media_types.h1
-rw-r--r--chromium/media/base/media_url_demuxer.cc5
-rw-r--r--chromium/media/base/media_url_demuxer.h2
-rw-r--r--chromium/media/base/mime_util_internal.cc1
-rw-r--r--chromium/media/base/mock_filters.cc2
-rw-r--r--chromium/media/base/mock_filters.h8
-rw-r--r--chromium/media/base/mock_media_log.cc1
-rw-r--r--chromium/media/base/mock_media_log.h8
-rw-r--r--chromium/media/base/multi_channel_resampler.cc2
-rw-r--r--chromium/media/base/multi_channel_resampler_unittest.cc1
-rw-r--r--chromium/media/base/reentrancy_checker_unittest.cc2
-rw-r--r--chromium/media/base/sample_format.cc4
-rw-r--r--chromium/media/base/sample_rates.cc2
-rw-r--r--chromium/media/base/scopedfd_helper.cc2
-rw-r--r--chromium/media/base/seekable_buffer.cc2
-rw-r--r--chromium/media/base/seekable_buffer_unittest.cc1
-rw-r--r--chromium/media/base/silent_sink_suspender.cc64
-rw-r--r--chromium/media/base/silent_sink_suspender.h31
-rw-r--r--chromium/media/base/silent_sink_suspender_unittest.cc116
-rw-r--r--chromium/media/base/sinc_resampler.cc2
-rw-r--r--chromium/media/base/status_codes.h13
-rw-r--r--chromium/media/base/stream_parser_buffer.cc2
-rw-r--r--chromium/media/base/supported_types.cc29
-rw-r--r--chromium/media/base/supported_types_unittest.cc54
-rw-r--r--chromium/media/base/test_data_util.cc2
-rw-r--r--chromium/media/base/test_helpers.cc3
-rw-r--r--chromium/media/base/test_helpers.h1
-rw-r--r--chromium/media/base/text_ranges.cc2
-rw-r--r--chromium/media/base/text_renderer.cc3
-rw-r--r--chromium/media/base/time_delta_interpolator.cc2
-rw-r--r--chromium/media/base/time_delta_interpolator_unittest.cc1
-rw-r--r--chromium/media/base/unaligned_shared_memory_unittest.cc1
-rw-r--r--chromium/media/base/user_input_monitor_linux.cc28
-rw-r--r--chromium/media/base/vector_math.cc2
-rw-r--r--chromium/media/base/video_bitrate_allocation.cc3
-rw-r--r--chromium/media/base/video_bitrate_allocation_unittest.cc1
-rw-r--r--chromium/media/base/video_codecs_unittest.cc1
-rw-r--r--chromium/media/base/video_color_space_unittest.cc1
-rw-r--r--chromium/media/base/video_decoder_config.cc3
-rw-r--r--chromium/media/base/video_encoder.cc21
-rw-r--r--chromium/media/base/video_encoder.h107
-rw-r--r--chromium/media/base/video_frame.h12
-rw-r--r--chromium/media/base/video_frame_layout.cc2
-rw-r--r--chromium/media/base/video_frame_metadata.cc19
-rw-r--r--chromium/media/base/video_transformation.cc2
-rw-r--r--chromium/media/base/video_types.cc4
-rw-r--r--chromium/media/base/video_util.cc3
-rw-r--r--chromium/media/base/watch_time_keys.cc2
-rw-r--r--chromium/media/base/win/BUILD.gn8
-rw-r--r--chromium/media/base/win/hresult_status_helper.cc22
-rw-r--r--chromium/media/base/win/hresult_status_helper.h25
-rw-r--r--chromium/media/base/win/mf_helpers.h19
-rw-r--r--chromium/media/base/win/mf_initializer.h2
-rw-r--r--chromium/media/blink/cdm_result_promise_helper.cc2
-rw-r--r--chromium/media/blink/interval_map_unittest.cc1
-rw-r--r--chromium/media/blink/run_all_unittests.cc2
-rw-r--r--chromium/media/blink/url_index_unittest.cc1
-rw-r--r--chromium/media/blink/video_frame_compositor.cc26
-rw-r--r--chromium/media/blink/video_frame_compositor.h15
-rw-r--r--chromium/media/blink/video_frame_compositor_unittest.cc29
-rw-r--r--chromium/media/blink/watch_time_reporter_unittest.cc3
-rw-r--r--chromium/media/blink/webcontentdecryptionmodule_impl.cc3
-rw-r--r--chromium/media/blink/webcontentdecryptionmodulesession_impl.cc3
-rw-r--r--chromium/media/blink/webinbandtexttrack_impl.cc2
-rw-r--r--chromium/media/blink/webmediaplayer_impl.cc120
-rw-r--r--chromium/media/blink/webmediaplayer_impl.h11
-rw-r--r--chromium/media/blink/webmediaplayer_impl_unittest.cc151
-rw-r--r--chromium/media/blink/webmediaplayer_params.cc7
-rw-r--r--chromium/media/blink/webmediaplayer_params.h7
-rw-r--r--chromium/media/capabilities/in_memory_video_decode_stats_db_unittest.cc2
-rw-r--r--chromium/media/capabilities/video_decode_stats_db.cc1
-rw-r--r--chromium/media/capabilities/video_decode_stats_db_impl.cc165
-rw-r--r--chromium/media/capabilities/video_decode_stats_db_impl.h75
-rw-r--r--chromium/media/capabilities/video_decode_stats_db_impl_unittest.cc49
-rw-r--r--chromium/media/capture/BUILD.gn18
-rw-r--r--chromium/media/capture/capture_switches.cc10
-rw-r--r--chromium/media/capture/capture_switches.h2
-rw-r--r--chromium/media/capture/content/capture_resolution_chooser.cc1
-rw-r--r--chromium/media/capture/mojom/video_capture_types.mojom10
-rw-r--r--chromium/media/capture/mojom/video_capture_types_mojom_traits.cc41
-rw-r--r--chromium/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera2.java8
-rw-r--r--chromium/media/capture/video/chromeos/camera_app_device_impl.cc75
-rw-r--r--chromium/media/capture/video/chromeos/camera_app_device_impl.h91
-rw-r--r--chromium/media/capture/video/chromeos/camera_device_delegate.cc55
-rw-r--r--chromium/media/capture/video/chromeos/camera_device_delegate.h2
-rw-r--r--chromium/media/capture/video/chromeos/gpu_memory_buffer_tracker.cc3
-rw-r--r--chromium/media/capture/video/chromeos/mock_vendor_tag_ops.cc2
-rw-r--r--chromium/media/capture/video/chromeos/mojom/camera_app.mojom9
-rw-r--r--chromium/media/capture/video/chromeos/request_manager.cc24
-rw-r--r--chromium/media/capture/video/chromeos/request_manager.h10
-rw-r--r--chromium/media/capture/video/chromeos/video_capture_jpeg_decoder_impl.cc5
-rw-r--r--chromium/media/capture/video/fuchsia/video_capture_device_factory_fuchsia.cc235
-rw-r--r--chromium/media/capture/video/fuchsia/video_capture_device_factory_fuchsia.h34
-rw-r--r--chromium/media/capture/video/fuchsia/video_capture_device_fuchsia.cc437
-rw-r--r--chromium/media/capture/video/fuchsia/video_capture_device_fuchsia.h118
-rw-r--r--chromium/media/capture/video/fuchsia/video_capture_device_fuchsia_test.cc324
-rw-r--r--chromium/media/capture/video/mac/video_capture_device_avfoundation_mac.mm11
-rw-r--r--chromium/media/capture/video/shared_memory_buffer_tracker.cc3
-rw-r--r--chromium/media/capture/video/video_capture_buffer_handle.cc4
-rw-r--r--chromium/media/capture/video/video_capture_buffer_tracker.h2
-rw-r--r--chromium/media/capture/video/video_capture_device_client_unittest.cc2
-rw-r--r--chromium/media/capture/video/video_capture_device_descriptor.cc3
-rw-r--r--chromium/media/capture/video/video_capture_device_descriptor.h1
-rw-r--r--chromium/media/capture/video/video_capture_device_unittest.cc32
-rw-r--r--chromium/media/capture/video/win/capability_list_win.cc2
-rw-r--r--chromium/media/capture/video/win/filter_base_win.cc2
-rw-r--r--chromium/media/capture/video/win/metrics.cc1
-rw-r--r--chromium/media/capture/video/win/pin_base_win.cc3
-rw-r--r--chromium/media/capture/video/win/sink_filter_win.cc1
-rw-r--r--chromium/media/capture/video/win/video_capture_device_mf_win.cc271
-rw-r--r--chromium/media/capture/video/win/video_capture_device_mf_win.h7
-rw-r--r--chromium/media/capture/video/win/video_capture_device_utils_win.cc35
-rw-r--r--chromium/media/capture/video/win/video_capture_device_utils_win.h74
-rw-r--r--chromium/media/capture/video/win/video_capture_device_win.cc124
-rw-r--r--chromium/media/capture/video_capture_types.cc2
-rw-r--r--chromium/media/capture/video_capture_types.h9
-rw-r--r--chromium/media/cast/BUILD.gn6
-rw-r--r--chromium/media/cast/cast_config.cc9
-rw-r--r--chromium/media/cast/cast_environment.cc2
-rw-r--r--chromium/media/cast/cast_sender.h8
-rw-r--r--chromium/media/cast/cast_sender_impl.cc10
-rw-r--r--chromium/media/cast/cast_sender_impl.h4
-rw-r--r--chromium/media/cast/common/clock_drift_smoother.cc3
-rw-r--r--chromium/media/cast/logging/log_deserializer.cc1
-rw-r--r--chromium/media/cast/logging/logging_defines.cc2
-rw-r--r--chromium/media/cast/logging/proto/proto_utils.cc2
-rw-r--r--chromium/media/cast/logging/receiver_time_offset_estimator_impl.cc2
-rw-r--r--chromium/media/cast/logging/simple_event_subscriber.cc2
-rw-r--r--chromium/media/cast/logging/stats_event_subscriber.cc3
-rw-r--r--chromium/media/cast/net/cast_transport_config.h2
-rw-r--r--chromium/media/cast/net/cast_transport_impl_unittest.cc10
-rw-r--r--chromium/media/cast/net/pacing/paced_sender.cc15
-rw-r--r--chromium/media/cast/net/pacing/paced_sender_unittest.cc28
-rw-r--r--chromium/media/cast/net/rtcp/receiver_rtcp_event_subscriber.cc2
-rw-r--r--chromium/media/cast/net/rtcp/test_rtcp_packet_builder.cc2
-rw-r--r--chromium/media/cast/net/rtp/frame_buffer.cc2
-rw-r--r--chromium/media/cast/net/rtp/packet_storage.cc3
-rw-r--r--chromium/media/cast/net/rtp/receiver_stats.cc1
-rw-r--r--chromium/media/cast/net/rtp/rtp_packet_builder.cc2
-rw-r--r--chromium/media/cast/net/rtp/rtp_packetizer.cc2
-rw-r--r--chromium/media/cast/net/rtp/rtp_packetizer_unittest.cc8
-rw-r--r--chromium/media/cast/net/rtp/rtp_parser.cc2
-rw-r--r--chromium/media/cast/net/udp_transport_impl.cc16
-rw-r--r--chromium/media/cast/net/udp_transport_impl.h4
-rw-r--r--chromium/media/cast/sender/audio_sender.cc5
-rw-r--r--chromium/media/cast/sender/audio_sender.h2
-rw-r--r--chromium/media/cast/sender/audio_sender_unittest.cc9
-rw-r--r--chromium/media/cast/sender/video_encoder_impl.cc2
-rw-r--r--chromium/media/cast/sender/video_sender_unittest.cc18
-rw-r--r--chromium/media/cdm/BUILD.gn8
-rw-r--r--chromium/media/cdm/aes_decryptor.h1
-rw-r--r--chromium/media/cdm/aes_decryptor_unittest.cc5
-rw-r--r--chromium/media/cdm/api/content_decryption_module.h14
-rw-r--r--chromium/media/cdm/api/content_decryption_module_proxy.h129
-rw-r--r--chromium/media/cdm/cdm_adapter.cc38
-rw-r--r--chromium/media/cdm/cdm_adapter.h5
-rw-r--r--chromium/media/cdm/cdm_adapter_unittest.cc11
-rw-r--r--chromium/media/cdm/cdm_auxiliary_helper.cc10
-rw-r--r--chromium/media/cdm/cdm_auxiliary_helper.h14
-rw-r--r--chromium/media/cdm/cdm_context_ref_impl.cc2
-rw-r--r--chromium/media/cdm/cdm_helpers.cc2
-rw-r--r--chromium/media/cdm/cdm_module.cc1
-rw-r--r--chromium/media/cdm/cdm_module.h12
-rw-r--r--chromium/media/cdm/cdm_proxy.cc14
-rw-r--r--chromium/media/cdm/cdm_proxy.h160
-rw-r--r--chromium/media/cdm/cdm_proxy_context.cc22
-rw-r--r--chromium/media/cdm/cdm_proxy_context.h67
-rw-r--r--chromium/media/cdm/cenc_utils_unittest.cc2
-rw-r--r--chromium/media/cdm/json_web_key.cc23
-rw-r--r--chromium/media/cdm/json_web_key_unittest.cc2
-rw-r--r--chromium/media/cdm/library_cdm/cdm_host_proxy.h1
-rw-r--r--chromium/media/cdm/library_cdm/cdm_host_proxy_impl.h13
-rw-r--r--chromium/media/cdm/library_cdm/clear_key_cdm/BUILD.gn20
-rw-r--r--chromium/media/cdm/library_cdm/clear_key_cdm/cdm_proxy_common.h21
-rw-r--r--chromium/media/cdm/library_cdm/clear_key_cdm/cdm_proxy_handler.cc119
-rw-r--r--chromium/media/cdm/library_cdm/clear_key_cdm/cdm_proxy_handler.h60
-rw-r--r--chromium/media/cdm/library_cdm/clear_key_cdm/clear_key_cdm.cc95
-rw-r--r--chromium/media/cdm/library_cdm/clear_key_cdm/clear_key_cdm.h21
-rw-r--r--chromium/media/cdm/library_cdm/clear_key_cdm/clear_key_cdm_proxy.cc129
-rw-r--r--chromium/media/cdm/library_cdm/clear_key_cdm/clear_key_cdm_proxy.h59
-rw-r--r--chromium/media/cdm/simple_cdm_buffer.cc2
-rw-r--r--chromium/media/filters/BUILD.gn10
-rw-r--r--chromium/media/filters/aom_video_decoder.cc9
-rw-r--r--chromium/media/filters/aom_video_decoder_unittest.cc7
-rw-r--r--chromium/media/filters/audio_clock.cc2
-rw-r--r--chromium/media/filters/audio_file_reader_unittest.cc1
-rw-r--r--chromium/media/filters/chunk_demuxer.cc5
-rw-r--r--chromium/media/filters/chunk_demuxer.h2
-rw-r--r--chromium/media/filters/decoder_selector.cc8
-rw-r--r--chromium/media/filters/decoder_selector_unittest.cc3
-rw-r--r--chromium/media/filters/decoder_stream.cc3
-rw-r--r--chromium/media/filters/ffmpeg_demuxer.cc14
-rw-r--r--chromium/media/filters/ffmpeg_demuxer.h2
-rw-r--r--chromium/media/filters/ffmpeg_glue.cc3
-rw-r--r--chromium/media/filters/ffmpeg_glue_unittest.cc2
-rw-r--r--chromium/media/filters/file_data_source.cc2
-rw-r--r--chromium/media/filters/frame_buffer_pool.cc2
-rw-r--r--chromium/media/filters/fuchsia/fuchsia_video_decoder.cc13
-rw-r--r--chromium/media/filters/ivf_parser.cc2
-rw-r--r--chromium/media/filters/media_file_checker_unittest.cc1
-rw-r--r--chromium/media/filters/memory_data_source.cc2
-rw-r--r--chromium/media/filters/pipeline_controller.cc19
-rw-r--r--chromium/media/filters/pipeline_controller.h6
-rw-r--r--chromium/media/filters/pipeline_controller_unittest.cc15
-rw-r--r--chromium/media/filters/video_cadence_estimator.cc1
-rw-r--r--chromium/media/filters/vp9_raw_bits_reader.cc2
-rw-r--r--chromium/media/filters/wsola_internals.cc3
-rw-r--r--chromium/media/formats/mp2t/descriptors.cc2
-rw-r--r--chromium/media/formats/mp2t/es_adapter_video_unittest.cc1
-rw-r--r--chromium/media/formats/mp2t/es_parser_adts_unittest.cc1
-rw-r--r--chromium/media/formats/mp2t/es_parser_h264_unittest.cc2
-rw-r--r--chromium/media/formats/mp2t/es_parser_mpeg1audio_unittest.cc1
-rw-r--r--chromium/media/formats/mp2t/es_parser_test_base.cc2
-rw-r--r--chromium/media/formats/mp2t/timestamp_unroller.cc2
-rw-r--r--chromium/media/formats/mp2t/timestamp_unroller_unittest.cc1
-rw-r--r--chromium/media/formats/mp2t/ts_section_cets_ecm.cc2
-rw-r--r--chromium/media/formats/mp2t/ts_section_cets_pssh.cc2
-rw-r--r--chromium/media/formats/mp2t/ts_section_pmt.cc2
-rw-r--r--chromium/media/formats/mp4/mp4_box_reader_fuzzer.cc2
-rw-r--r--chromium/media/formats/mp4/nalu_test_helper.cc2
-rw-r--r--chromium/media/formats/mp4/sample_to_group_iterator.cc2
-rw-r--r--chromium/media/formats/webm/cluster_builder.cc2
-rw-r--r--chromium/media/formats/webm/opus_packet_builder.cc2
-rw-r--r--chromium/media/formats/webm/tracks_builder.cc4
-rw-r--r--chromium/media/formats/webm/webm_content_encodings.cc2
-rw-r--r--chromium/media/formats/webm/webm_parser.cc1
-rw-r--r--chromium/media/formats/webm/webm_stream_parser.cc3
-rw-r--r--chromium/media/fuchsia/audio/BUILD.gn14
-rw-r--r--chromium/media/fuchsia/audio/fake_audio_consumer.cc268
-rw-r--r--chromium/media/fuchsia/audio/fake_audio_consumer.h164
-rw-r--r--chromium/media/fuchsia/audio/fuchsia_audio_renderer.cc8
-rw-r--r--chromium/media/fuchsia/camera/BUILD.gn19
-rw-r--r--chromium/media/fuchsia/camera/fake_fuchsia_camera.cc517
-rw-r--r--chromium/media/fuchsia/camera/fake_fuchsia_camera.h194
-rw-r--r--chromium/media/fuchsia/cdm/fuchsia_decryptor.cc3
-rw-r--r--chromium/media/fuchsia/common/sysmem_buffer_pool.cc20
-rw-r--r--chromium/media/fuchsia/common/sysmem_buffer_pool.h5
-rw-r--r--chromium/media/fuchsia/common/sysmem_buffer_reader.cc71
-rw-r--r--chromium/media/fuchsia/common/sysmem_buffer_reader.h25
-rw-r--r--chromium/media/fuchsia/metrics/BUILD.gn28
-rw-r--r--chromium/media/fuchsia/metrics/DEPS3
-rw-r--r--chromium/media/fuchsia/metrics/fuchsia_playback_events_recorder.cc139
-rw-r--r--chromium/media/fuchsia/metrics/fuchsia_playback_events_recorder.h70
-rw-r--r--chromium/media/fuchsia/metrics/fuchsia_playback_events_recorder_test.cc202
-rw-r--r--chromium/media/gpu/BUILD.gn22
-rw-r--r--chromium/media/gpu/OWNERS1
-rw-r--r--chromium/media/gpu/android/android_video_surface_chooser_impl_unittest.cc1
-rw-r--r--chromium/media/gpu/android/codec_allocator_unittest.cc2
-rw-r--r--chromium/media/gpu/android/codec_image.cc184
-rw-r--r--chromium/media/gpu/android/codec_image.h37
-rw-r--r--chromium/media/gpu/android/codec_image_group_unittest.cc9
-rw-r--r--chromium/media/gpu/android/codec_image_unittest.cc79
-rw-r--r--chromium/media/gpu/android/codec_output_buffer_renderer.cc166
-rw-r--r--chromium/media/gpu/android/codec_output_buffer_renderer.h102
-rw-r--r--chromium/media/gpu/android/codec_wrapper_unittest.cc1
-rw-r--r--chromium/media/gpu/android/direct_shared_image_video_provider.cc10
-rw-r--r--chromium/media/gpu/android/frame_info_helper.cc120
-rw-r--r--chromium/media/gpu/android/frame_info_helper.h63
-rw-r--r--chromium/media/gpu/android/mock_codec_image.cc3
-rw-r--r--chromium/media/gpu/android/mock_codec_image.h2
-rw-r--r--chromium/media/gpu/android/promotion_hint_aggregator_impl_unittest.cc1
-rw-r--r--chromium/media/gpu/android/shared_image_video_provider.cc4
-rw-r--r--chromium/media/gpu/android/shared_image_video_provider.h4
-rw-r--r--chromium/media/gpu/android/surface_chooser_helper_unittest.cc1
-rw-r--r--chromium/media/gpu/android/video_frame_factory_impl.cc169
-rw-r--r--chromium/media/gpu/android/video_frame_factory_impl.h42
-rw-r--r--chromium/media/gpu/android/video_frame_factory_impl_unittest.cc140
-rw-r--r--chromium/media/gpu/android/ycbcr_helper.cc82
-rw-r--r--chromium/media/gpu/android/ycbcr_helper.h48
-rw-r--r--chromium/media/gpu/chromeos/fourcc_unittests.cc1
-rw-r--r--chromium/media/gpu/chromeos/platform_video_frame_utils.cc19
-rw-r--r--chromium/media/gpu/chromeos/platform_video_frame_utils_unittest.cc2
-rw-r--r--chromium/media/gpu/chromeos/video_decoder_pipeline.cc18
-rw-r--r--chromium/media/gpu/chromeos/video_decoder_pipeline.h12
-rw-r--r--chromium/media/gpu/gles2_decoder_helper.cc2
-rw-r--r--chromium/media/gpu/gpu_video_encode_accelerator_factory.cc18
-rw-r--r--chromium/media/gpu/gpu_video_encode_accelerator_helpers.cc2
-rw-r--r--chromium/media/gpu/h264_decoder.cc2
-rw-r--r--chromium/media/gpu/h264_decoder_unittest.cc2
-rw-r--r--chromium/media/gpu/v4l2/BUILD.gn2
-rw-r--r--chromium/media/gpu/v4l2/v4l2_device.cc104
-rw-r--r--chromium/media/gpu/v4l2/v4l2_device.h27
-rw-r--r--chromium/media/gpu/v4l2/v4l2_image_processor_backend.cc60
-rw-r--r--chromium/media/gpu/v4l2/v4l2_image_processor_backend.h4
-rw-r--r--chromium/media/gpu/v4l2/v4l2_mjpeg_decode_accelerator.cc14
-rw-r--r--chromium/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.cc16
-rw-r--r--chromium/media/gpu/v4l2/v4l2_slice_video_decoder.cc5
-rw-r--r--chromium/media/gpu/v4l2/v4l2_slice_video_decoder.h2
-rw-r--r--chromium/media/gpu/v4l2/v4l2_vda_helpers.cc6
-rw-r--r--chromium/media/gpu/v4l2/v4l2_video_decode_accelerator.cc65
-rw-r--r--chromium/media/gpu/v4l2/v4l2_video_decoder_backend_stateless.cc7
-rw-r--r--chromium/media/gpu/v4l2/v4l2_video_encode_accelerator.cc102
-rw-r--r--chromium/media/gpu/vaapi/BUILD.gn4
-rw-r--r--chromium/media/gpu/vaapi/h264_encoder.h2
-rw-r--r--chromium/media/gpu/vaapi/vaapi_image_decode_accelerator_worker_unittest.cc3
-rw-r--r--chromium/media/gpu/vaapi/vaapi_image_processor_backend.cc6
-rw-r--r--chromium/media/gpu/vaapi/vaapi_jpeg_encoder.cc2
-rw-r--r--chromium/media/gpu/vaapi/vaapi_unittest.cc19
-rw-r--r--chromium/media/gpu/vaapi/vaapi_video_decode_accelerator.cc37
-rw-r--r--chromium/media/gpu/vaapi/vaapi_video_decode_accelerator_unittest.cc5
-rw-r--r--chromium/media/gpu/vaapi/vaapi_video_decoder.cc4
-rw-r--r--chromium/media/gpu/vaapi/vaapi_video_decoder.h2
-rw-r--r--chromium/media/gpu/vaapi/vaapi_video_encode_accelerator.cc11
-rw-r--r--chromium/media/gpu/vaapi/vaapi_video_encode_accelerator_unittest.cc82
-rw-r--r--chromium/media/gpu/vaapi/vaapi_wrapper.cc81
-rw-r--r--chromium/media/gpu/vaapi/vaapi_wrapper.h8
-rw-r--r--chromium/media/gpu/vaapi/vp8_encoder.cc13
-rw-r--r--chromium/media/gpu/vaapi/vp8_encoder.h2
-rw-r--r--chromium/media/gpu/vaapi/vp9_encoder.cc15
-rw-r--r--chromium/media/gpu/vaapi/vp9_encoder.h2
-rw-r--r--chromium/media/gpu/video_encode_accelerator_unittest.cc14
-rw-r--r--chromium/media/gpu/vp8_decoder_unittest.cc1
-rw-r--r--chromium/media/gpu/windows/d3d11_cdm_proxy.cc658
-rw-r--r--chromium/media/gpu/windows/d3d11_cdm_proxy.h126
-rw-r--r--chromium/media/gpu/windows/d3d11_cdm_proxy_unittest.cc896
-rw-r--r--chromium/media/gpu/windows/d3d11_copying_texture_wrapper.cc19
-rw-r--r--chromium/media/gpu/windows/d3d11_copying_texture_wrapper.h7
-rw-r--r--chromium/media/gpu/windows/d3d11_copying_texture_wrapper_unittest.cc90
-rw-r--r--chromium/media/gpu/windows/d3d11_decryptor.cc381
-rw-r--r--chromium/media/gpu/windows/d3d11_decryptor.h96
-rw-r--r--chromium/media/gpu/windows/d3d11_decryptor_unittest.cc519
-rw-r--r--chromium/media/gpu/windows/d3d11_h264_accelerator.cc30
-rw-r--r--chromium/media/gpu/windows/d3d11_h264_accelerator.h6
-rw-r--r--chromium/media/gpu/windows/d3d11_picture_buffer.cc18
-rw-r--r--chromium/media/gpu/windows/d3d11_picture_buffer.h25
-rw-r--r--chromium/media/gpu/windows/d3d11_texture_wrapper.cc103
-rw-r--r--chromium/media/gpu/windows/d3d11_texture_wrapper.h43
-rw-r--r--chromium/media/gpu/windows/d3d11_texture_wrapper_unittest.cc126
-rw-r--r--chromium/media/gpu/windows/d3d11_video_decoder.cc260
-rw-r--r--chromium/media/gpu/windows/d3d11_video_decoder.h57
-rw-r--r--chromium/media/gpu/windows/d3d11_video_decoder_impl.cc28
-rw-r--r--chromium/media/gpu/windows/d3d11_video_decoder_impl.h24
-rw-r--r--chromium/media/gpu/windows/d3d11_video_decoder_unittest.cc31
-rw-r--r--chromium/media/gpu/windows/d3d11_video_processor_proxy.cc23
-rw-r--r--chromium/media/gpu/windows/d3d11_video_processor_proxy.h8
-rw-r--r--chromium/media/gpu/windows/d3d11_vp9_accelerator.cc47
-rw-r--r--chromium/media/gpu/windows/d3d11_vp9_accelerator.h7
-rw-r--r--chromium/media/gpu/windows/display_helper.h1
-rw-r--r--chromium/media/gpu/windows/dxva_video_decode_accelerator_win.cc128
-rw-r--r--chromium/media/gpu/windows/hresult_status_debug_device.cc63
-rw-r--r--chromium/media/gpu/windows/hresult_status_debug_device.h27
-rw-r--r--chromium/media/gpu/windows/media_foundation_video_encode_accelerator_win.cc690
-rw-r--r--chromium/media/gpu/windows/media_foundation_video_encode_accelerator_win.h57
-rw-r--r--chromium/media/learning/common/media_learning_tasks.cc2
-rw-r--r--chromium/media/learning/common/value.cc2
-rw-r--r--chromium/media/learning/impl/extra_trees_trainer.cc2
-rw-r--r--chromium/media/learning/impl/learning_session_impl.cc2
-rw-r--r--chromium/media/learning/impl/learning_task_controller_impl.cc2
-rw-r--r--chromium/media/learning/impl/lookup_table_trainer.cc1
-rw-r--r--chromium/media/learning/impl/model.h1
-rw-r--r--chromium/media/learning/impl/random_number_generator.cc1
-rw-r--r--chromium/media/learning/impl/random_tree_trainer.cc2
-rw-r--r--chromium/media/media_options.gni91
-rw-r--r--chromium/media/midi/message_util.cc2
-rw-r--r--chromium/media/midi/midi_manager_mac_unittest.cc1
-rw-r--r--chromium/media/midi/midi_manager_unittest.cc2
-rw-r--r--chromium/media/midi/midi_manager_usb.cc2
-rw-r--r--chromium/media/midi/midi_message_queue.cc3
-rw-r--r--chromium/media/mojo/BUILD.gn49
-rw-r--r--chromium/media/mojo/README.md22
-rw-r--r--chromium/media/mojo/clients/mojo_cdm.cc26
-rw-r--r--chromium/media/mojo/clients/mojo_cdm.h7
-rw-r--r--chromium/media/mojo/clients/mojo_cdm_factory.cc4
-rw-r--r--chromium/media/mojo/clients/mojo_cdm_unittest.cc1
-rw-r--r--chromium/media/mojo/mojom/BUILD.gn12
-rw-r--r--chromium/media/mojo/mojom/audio_output_stream.mojom3
-rw-r--r--chromium/media/mojo/mojom/cdm_key_information_mojom_traits.cc2
-rw-r--r--chromium/media/mojo/mojom/cdm_proxy.mojom77
-rw-r--r--chromium/media/mojo/mojom/cdm_proxy.typemap21
-rw-r--r--chromium/media/mojo/mojom/cdm_service.mojom8
-rw-r--r--chromium/media/mojo/mojom/frame_interface_factory.mojom24
-rw-r--r--chromium/media/mojo/mojom/interface_factory.mojom12
-rw-r--r--chromium/media/mojo/mojom/key_system_support.mojom3
-rw-r--r--chromium/media/mojo/mojom/media_metrics_provider.mojom10
-rw-r--r--chromium/media/mojo/mojom/media_service.mojom6
-rw-r--r--chromium/media/mojo/mojom/media_types.mojom1
-rw-r--r--chromium/media/mojo/mojom/playback_events_recorder.mojom45
-rw-r--r--chromium/media/mojo/mojom/soda_service.mojom42
-rw-r--r--chromium/media/mojo/mojom/speech_recognition_service.mojom50
-rw-r--r--chromium/media/mojo/mojom/typemaps.gni4
-rw-r--r--chromium/media/mojo/mojom/video_encode_accelerator.mojom12
-rw-r--r--chromium/media/mojo/mojom/video_encode_accelerator.typemap1
-rw-r--r--chromium/media/mojo/mojom/video_encode_accelerator_mojom_traits.cc23
-rw-r--r--chromium/media/mojo/mojom/video_encode_accelerator_mojom_traits.h42
-rw-r--r--chromium/media/mojo/mojom/video_encode_accelerator_mojom_traits_unittest.cc156
-rw-r--r--chromium/media/mojo/mojom/video_encoder_info.mojom2
-rw-r--r--chromium/media/mojo/mojom/video_encoder_info_mojom_traits.h2
-rw-r--r--chromium/media/mojo/mojom/video_encoder_info_mojom_traits_unittest.cc88
-rw-r--r--chromium/media/mojo/mojom/video_frame_mojom_traits.cc6
-rw-r--r--chromium/media/mojo/mojom/video_frame_mojom_traits.h5
-rw-r--r--chromium/media/mojo/services/BUILD.gn23
-rw-r--r--chromium/media/mojo/services/android_mojo_media_client.cc8
-rw-r--r--chromium/media/mojo/services/android_mojo_media_client.h2
-rw-r--r--chromium/media/mojo/services/android_mojo_util.cc19
-rw-r--r--chromium/media/mojo/services/android_mojo_util.h13
-rw-r--r--chromium/media/mojo/services/cdm_service.cc12
-rw-r--r--chromium/media/mojo/services/cdm_service.h9
-rw-r--r--chromium/media/mojo/services/cdm_service_unittest.cc8
-rw-r--r--chromium/media/mojo/services/gpu_mojo_media_client.cc32
-rw-r--r--chromium/media/mojo/services/gpu_mojo_media_client.h12
-rw-r--r--chromium/media/mojo/services/interface_factory_impl.cc57
-rw-r--r--chromium/media/mojo/services/interface_factory_impl.h21
-rw-r--r--chromium/media/mojo/services/media_interface_provider.cc21
-rw-r--r--chromium/media/mojo/services/media_interface_provider.h40
-rw-r--r--chromium/media/mojo/services/media_metrics_provider.cc13
-rw-r--r--chromium/media/mojo/services/media_metrics_provider.h2
-rw-r--r--chromium/media/mojo/services/media_service.cc7
-rw-r--r--chromium/media/mojo/services/media_service.h5
-rw-r--r--chromium/media/mojo/services/media_service_factory.cc13
-rw-r--r--chromium/media/mojo/services/media_service_factory.h9
-rw-r--r--chromium/media/mojo/services/media_service_unittest.cc145
-rw-r--r--chromium/media/mojo/services/mojo_audio_output_stream_provider.cc4
-rw-r--r--chromium/media/mojo/services/mojo_audio_output_stream_provider.h8
-rw-r--r--chromium/media/mojo/services/mojo_audio_output_stream_provider_unittest.cc6
-rw-r--r--chromium/media/mojo/services/mojo_cdm_helper.cc40
-rw-r--r--chromium/media/mojo/services/mojo_cdm_helper.h25
-rw-r--r--chromium/media/mojo/services/mojo_cdm_helper_unittest.cc31
-rw-r--r--chromium/media/mojo/services/mojo_cdm_promise.cc2
-rw-r--r--chromium/media/mojo/services/mojo_cdm_proxy.cc217
-rw-r--r--chromium/media/mojo/services/mojo_cdm_proxy.h85
-rw-r--r--chromium/media/mojo/services/mojo_cdm_proxy_service.cc101
-rw-r--r--chromium/media/mojo/services/mojo_cdm_proxy_service.h87
-rw-r--r--chromium/media/mojo/services/mojo_cdm_proxy_unittest.cc390
-rw-r--r--chromium/media/mojo/services/mojo_cdm_service.cc10
-rw-r--r--chromium/media/mojo/services/mojo_cdm_service_context.cc70
-rw-r--r--chromium/media/mojo/services/mojo_cdm_service_context.h16
-rw-r--r--chromium/media/mojo/services/mojo_decryptor_service.cc23
-rw-r--r--chromium/media/mojo/services/mojo_decryptor_service.h5
-rw-r--r--chromium/media/mojo/services/mojo_media_client.cc17
-rw-r--r--chromium/media/mojo/services/mojo_media_client.h23
-rw-r--r--chromium/media/mojo/services/mojo_video_encode_accelerator_provider.cc1
-rw-r--r--chromium/media/mojo/services/test_mojo_media_client.cc33
-rw-r--r--chromium/media/mojo/services/test_mojo_media_client.h9
-rw-r--r--chromium/media/mojo/services/video_decode_perf_history_unittest.cc51
-rw-r--r--chromium/media/mojo/services/watch_time_recorder.cc14
-rw-r--r--chromium/media/parsers/jpeg_parser.cc2
-rw-r--r--chromium/media/parsers/vp8_bool_decoder.cc1
-rw-r--r--chromium/media/parsers/vp8_parser.cc2
-rw-r--r--chromium/media/parsers/webp_parser.cc2
-rw-r--r--chromium/media/remoting/courier_renderer_factory.cc2
-rw-r--r--chromium/media/remoting/integration_test.cc19
-rw-r--r--chromium/media/remoting/renderer_controller.cc15
-rw-r--r--chromium/media/remoting/renderer_controller.h3
-rw-r--r--chromium/media/remoting/renderer_controller_unittest.cc24
-rw-r--r--chromium/media/renderers/BUILD.gn15
-rw-r--r--chromium/media/renderers/default_decoder_factory.cc20
-rw-r--r--chromium/media/renderers/default_renderer_factory.cc15
-rw-r--r--chromium/media/renderers/default_renderer_factory.h12
-rw-r--r--chromium/media/renderers/paint_canvas_video_renderer.cc302
-rw-r--r--chromium/media/renderers/paint_canvas_video_renderer.h3
-rw-r--r--chromium/media/renderers/video_resource_updater.cc26
-rw-r--r--chromium/media/renderers/win/media_engine_extension.cc16
-rw-r--r--chromium/media/renderers/win/media_engine_notify_impl.cc84
-rw-r--r--chromium/media/renderers/win/media_engine_notify_impl.h8
-rw-r--r--chromium/media/renderers/win/media_foundation_audio_stream.cc2
-rw-r--r--chromium/media/renderers/win/media_foundation_protection_manager.cc12
-rw-r--r--chromium/media/renderers/win/media_foundation_renderer.cc641
-rw-r--r--chromium/media/renderers/win/media_foundation_renderer.h163
-rw-r--r--chromium/media/renderers/win/media_foundation_renderer_extension.h45
-rw-r--r--chromium/media/renderers/win/media_foundation_renderer_integration_test.cc96
-rw-r--r--chromium/media/renderers/win/media_foundation_renderer_unittest.cc268
-rw-r--r--chromium/media/renderers/win/media_foundation_source_wrapper.cc61
-rw-r--r--chromium/media/renderers/win/media_foundation_stream_wrapper.cc86
-rw-r--r--chromium/media/renderers/yuv_util.cc221
-rw-r--r--chromium/media/renderers/yuv_util.h54
-rwxr-xr-xchromium/media/tools/constrained_network_server/cns.py35
-rwxr-xr-xchromium/media/tools/constrained_network_server/cns_test.py37
-rw-r--r--chromium/media/video/BUILD.gn8
-rw-r--r--chromium/media/video/fake_video_encode_accelerator.cc2
-rw-r--r--chromium/media/video/h264_bit_reader.cc2
-rw-r--r--chromium/media/video/picture.cc4
-rw-r--r--chromium/media/video/video_decode_accelerator.h2
-rw-r--r--chromium/media/video/video_encode_accelerator.cc28
-rw-r--r--chromium/media/video/video_encode_accelerator.h28
-rw-r--r--chromium/media/video/video_encoder_info.h9
-rw-r--r--chromium/media/video/vpx_video_encoder.cc278
-rw-r--r--chromium/media/video/vpx_video_encoder.h45
-rw-r--r--chromium/media/webcodecs/BUILD.gn39
-rw-r--r--chromium/media/webcodecs/wc_decoder_selector.cc142
-rw-r--r--chromium/media/webcodecs/wc_decoder_selector.h83
-rw-r--r--chromium/media/webcodecs/wc_decoder_selector_unittest.cc240
-rw-r--r--chromium/media/webrtc/BUILD.gn32
-rw-r--r--chromium/media/webrtc/audio_processing.gni4
-rw-r--r--chromium/media/webrtc/audio_processor.cc361
-rw-r--r--chromium/media/webrtc/audio_processor.h121
-rw-r--r--chromium/media/webrtc/audio_processor_unittest.cc277
-rw-r--r--chromium/media/webrtc/webrtc_switches.cc28
-rw-r--r--chromium/media/webrtc/webrtc_switches.h14
572 files changed, 11593 insertions, 9118 deletions
diff --git a/chromium/media/BUILD.gn b/chromium/media/BUILD.gn
index 8ba5ec2ca12..13ec8a79c74 100644
--- a/chromium/media/BUILD.gn
+++ b/chromium/media/BUILD.gn
@@ -22,7 +22,6 @@ buildflag_header("media_buildflags") {
"CDM_PLATFORM_SPECIFIC_PATH=\"$cdm_platform_specific_path\"",
"ENABLE_PLATFORM_AC3_EAC3_AUDIO=$enable_platform_ac3_eac3_audio",
"ENABLE_CDM_HOST_VERIFICATION=$enable_cdm_host_verification",
- "ENABLE_CDM_PROXY=$enable_cdm_proxy",
"ENABLE_CDM_STORAGE_ID=$enable_cdm_storage_id",
"ENABLE_DAV1D_DECODER=$enable_dav1d_decoder",
"ENABLE_AV1_DECODER=$enable_av1_decoder",
@@ -158,6 +157,7 @@ test("media_unittests") {
"//media/test:pipeline_integration_tests",
"//media/test:run_all_unittests",
"//media/video:unit_tests",
+ "//media/webcodecs:unit_tests",
"//media/webrtc:unit_tests",
]
@@ -182,7 +182,10 @@ test("media_unittests") {
}
if (is_fuchsia) {
- deps += [ "//media/fuchsia/audio:unittests" ]
+ deps += [
+ "//media/fuchsia/audio:unittests",
+ "//media/fuchsia/metrics:unittests",
+ ]
}
if (enable_media_remoting) {
diff --git a/chromium/media/README.md b/chromium/media/README.md
index 01b21adc2e9..71f8fb1d91a 100644
--- a/chromium/media/README.md
+++ b/chromium/media/README.md
@@ -98,6 +98,11 @@ components required for HTML media elements and extensions:
* [Media Source Extensions](https://www.w3.org/TR/media-source/)
* [Encrypted Media Extensions](https://www.w3.org/TR/encrypted-media/)
+The following diagram provides a simplified overview of the media playback
+pipeline.
+
+![Media Pipeline Overview](/docs/media/media_pipeline_overview.png)
+
As a case study we'll consider the playback of a video through the `<video>` tag.
`<video>` (and `<audio>`) starts in `blink::HTMLMediaElement` in
diff --git a/chromium/media/audio/android/opensles_util.cc b/chromium/media/audio/android/opensles_util.cc
index cf5ea3b98d8..128a3bd20d2 100644
--- a/chromium/media/audio/android/opensles_util.cc
+++ b/chromium/media/audio/android/opensles_util.cc
@@ -4,6 +4,8 @@
#include "media/audio/android/opensles_util.h"
+#include "base/logging.h"
+
namespace media {
#define SL_ANDROID_SPEAKER_QUAD \
diff --git a/chromium/media/audio/audio_device_description.cc b/chromium/media/audio/audio_device_description.cc
index c72b61478a7..bd3f61100e8 100644
--- a/chromium/media/audio/audio_device_description.cc
+++ b/chromium/media/audio/audio_device_description.cc
@@ -7,7 +7,7 @@
#include <utility>
#include "base/bind.h"
-#include "base/logging.h"
+#include "base/notreached.h"
#include "build/chromecast_buildflags.h"
#include "media/base/localized_strings.h"
diff --git a/chromium/media/audio/audio_device_thread.cc b/chromium/media/audio/audio_device_thread.cc
index 2acfb8ad6e9..b813314b867 100644
--- a/chromium/media/audio/audio_device_thread.cc
+++ b/chromium/media/audio/audio_device_thread.cc
@@ -6,7 +6,7 @@
#include <limits>
-#include "base/logging.h"
+#include "base/check_op.h"
#include "base/system/sys_info.h"
namespace media {
diff --git a/chromium/media/audio/audio_input_device.cc b/chromium/media/audio/audio_input_device.cc
index 26367015351..aad6984307d 100644
--- a/chromium/media/audio/audio_input_device.cc
+++ b/chromium/media/audio/audio_input_device.cc
@@ -81,7 +81,6 @@ class AudioInputDevice::AudioThreadCallback
base::ReadOnlySharedMemoryRegion shared_memory_region_;
base::ReadOnlySharedMemoryMapping shared_memory_mapping_;
const base::TimeTicks start_time_;
- bool no_callbacks_received_;
size_t current_segment_id_;
uint32_t last_buffer_id_;
std::vector<std::unique_ptr<const media::AudioBus>> audio_buses_;
@@ -357,7 +356,6 @@ AudioInputDevice::AudioThreadCallback::AudioThreadCallback(
enable_uma_(enable_uma),
shared_memory_region_(std::move(shared_memory_region)),
start_time_(base::TimeTicks::Now()),
- no_callbacks_received_(true),
current_segment_id_(0u),
last_buffer_id_(UINT32_MAX),
capture_callback_(capture_callback),
@@ -400,15 +398,6 @@ void AudioInputDevice::AudioThreadCallback::MapSharedMemory() {
void AudioInputDevice::AudioThreadCallback::Process(uint32_t pending_data) {
TRACE_EVENT_BEGIN0("audio", "AudioInputDevice::AudioThreadCallback::Process");
-
- if (no_callbacks_received_) {
- if (enable_uma_) {
- UMA_HISTOGRAM_TIMES("Media.Audio.Render.InputDeviceStartTime",
- base::TimeTicks::Now() - start_time_);
- }
- no_callbacks_received_ = false;
- }
-
// The shared memory represents parameters, size of the data buffer and the
// actual data buffer containing audio data. Map the memory into this
// structure and parse out parameters and the data area.
diff --git a/chromium/media/audio/audio_output_device.cc b/chromium/media/audio/audio_output_device.cc
index 86239ef1bc8..92448e7469f 100644
--- a/chromium/media/audio/audio_output_device.cc
+++ b/chromium/media/audio/audio_output_device.cc
@@ -402,8 +402,7 @@ void AudioOutputDevice::OnStreamCreated(
DCHECK(!audio_callback_);
audio_callback_.reset(new AudioOutputDeviceThreadCallback(
- audio_parameters_, std::move(shared_memory_region), callback_,
- std::make_unique<AudioOutputDeviceThreadCallback::Metrics>()));
+ audio_parameters_, std::move(shared_memory_region), callback_));
if (playing_automatically)
audio_callback_->InitializePlayStartTime();
audio_thread_.reset(new AudioDeviceThread(
diff --git a/chromium/media/audio/audio_output_device_thread_callback.cc b/chromium/media/audio/audio_output_device_thread_callback.cc
index c19252afbb9..c834b0d77d8 100644
--- a/chromium/media/audio/audio_output_device_thread_callback.cc
+++ b/chromium/media/audio/audio_output_device_thread_callback.cc
@@ -11,57 +11,23 @@
namespace media {
-AudioOutputDeviceThreadCallback::Metrics::Metrics()
- : first_play_start_time_(base::nullopt) {}
-
-AudioOutputDeviceThreadCallback::Metrics::~Metrics() = default;
-
-void AudioOutputDeviceThreadCallback::Metrics::OnCreated() {
- start_time_ = base::TimeTicks::Now();
-}
-
-void AudioOutputDeviceThreadCallback::Metrics::OnProcess() {
- if (first_play_start_time_) {
- UMA_HISTOGRAM_TIMES("Media.Audio.Render.OutputDeviceStartTime",
- base::TimeTicks::Now() - *first_play_start_time_);
- }
-}
-
-void AudioOutputDeviceThreadCallback::Metrics::OnInitializePlayStartTime() {
- if (!first_play_start_time_.has_value())
- first_play_start_time_ = base::TimeTicks::Now();
-}
-
-void AudioOutputDeviceThreadCallback::Metrics::OnDestroyed() {
- DCHECK(!start_time_.is_null());
- UMA_HISTOGRAM_LONG_TIMES("Media.Audio.Render.OutputStreamDuration",
- base::TimeTicks::Now() - start_time_);
-}
-
AudioOutputDeviceThreadCallback::AudioOutputDeviceThreadCallback(
const media::AudioParameters& audio_parameters,
base::UnsafeSharedMemoryRegion shared_memory_region,
- media::AudioRendererSink::RenderCallback* render_callback,
- std::unique_ptr<Metrics> metrics)
+ media::AudioRendererSink::RenderCallback* render_callback)
: media::AudioDeviceThread::Callback(
audio_parameters,
ComputeAudioOutputBufferSize(audio_parameters),
/*segment count*/ 1),
shared_memory_region_(std::move(shared_memory_region)),
render_callback_(render_callback),
- callback_num_(0),
- metrics_(std::move(metrics)) {
+ callback_num_(0) {
// CHECK that the shared memory is large enough. The memory allocated must be
// at least as large as expected.
CHECK(memory_length_ <= shared_memory_region_.GetSize());
- if (metrics_)
- metrics_->OnCreated();
}
-AudioOutputDeviceThreadCallback::~AudioOutputDeviceThreadCallback() {
- if (metrics_)
- metrics_->OnDestroyed();
-}
+AudioOutputDeviceThreadCallback::~AudioOutputDeviceThreadCallback() = default;
void AudioOutputDeviceThreadCallback::MapSharedMemory() {
CHECK_EQ(total_segments_, 1u);
@@ -103,11 +69,8 @@ void AudioOutputDeviceThreadCallback::Process(uint32_t control_signal) {
// When playback starts, we get an immediate callback to Process to make sure
// that we have some data, we'll get another one after the device is awake and
// ingesting data, which is what we want to track with this trace.
- if (callback_num_ == 2) {
- if (metrics_)
- metrics_->OnProcess();
+ if (callback_num_ == 2)
TRACE_EVENT_ASYNC_END0("audio", "StartingPlayback", this);
- }
// Update the audio-delay measurement, inform about the number of skipped
// frames, and ask client to render audio. Since |output_bus_| is wrapping
@@ -131,9 +94,6 @@ bool AudioOutputDeviceThreadCallback::CurrentThreadIsAudioDeviceThread() {
return thread_checker_.CalledOnValidThread();
}
-void AudioOutputDeviceThreadCallback::InitializePlayStartTime() {
- if (metrics_)
- metrics_->OnInitializePlayStartTime();
-}
+void AudioOutputDeviceThreadCallback::InitializePlayStartTime() {}
} // namespace media
diff --git a/chromium/media/audio/audio_output_device_thread_callback.h b/chromium/media/audio/audio_output_device_thread_callback.h
index 2a529bf9082..84b80d9af78 100644
--- a/chromium/media/audio/audio_output_device_thread_callback.h
+++ b/chromium/media/audio/audio_output_device_thread_callback.h
@@ -20,27 +20,10 @@ namespace media {
class MEDIA_EXPORT AudioOutputDeviceThreadCallback
: public media::AudioDeviceThread::Callback {
public:
- class Metrics {
- public:
- Metrics();
- ~Metrics();
-
- void OnCreated();
- void OnProcess();
- void OnInitializePlayStartTime();
- void OnDestroyed();
-
- private:
- base::TimeTicks start_time_;
- // If set, this is used to record the startup duration UMA stat.
- base::Optional<base::TimeTicks> first_play_start_time_;
- };
-
AudioOutputDeviceThreadCallback(
const media::AudioParameters& audio_parameters,
base::UnsafeSharedMemoryRegion shared_memory_region,
- media::AudioRendererSink::RenderCallback* render_callback,
- std::unique_ptr<Metrics> metrics = nullptr);
+ media::AudioRendererSink::RenderCallback* render_callback);
~AudioOutputDeviceThreadCallback() override;
void MapSharedMemory() override;
@@ -64,7 +47,6 @@ class MEDIA_EXPORT AudioOutputDeviceThreadCallback
media::AudioRendererSink::RenderCallback* render_callback_;
std::unique_ptr<media::AudioBus> output_bus_;
uint64_t callback_num_;
- std::unique_ptr<Metrics> metrics_;
DISALLOW_COPY_AND_ASSIGN(AudioOutputDeviceThreadCallback);
};
diff --git a/chromium/media/audio/audio_output_proxy.cc b/chromium/media/audio/audio_output_proxy.cc
index ec6657a5f67..d205d67091e 100644
--- a/chromium/media/audio/audio_output_proxy.cc
+++ b/chromium/media/audio/audio_output_proxy.cc
@@ -4,7 +4,7 @@
#include "media/audio/audio_output_proxy.h"
-#include "base/logging.h"
+#include "base/check_op.h"
#include "media/audio/audio_manager.h"
#include "media/audio/audio_output_dispatcher.h"
diff --git a/chromium/media/audio/cras/audio_manager_cras.cc b/chromium/media/audio/cras/audio_manager_cras.cc
index 376989bfb50..fb4f792d5d0 100644
--- a/chromium/media/audio/cras/audio_manager_cras.cc
+++ b/chromium/media/audio/cras/audio_manager_cras.cc
@@ -11,9 +11,9 @@
#include <utility>
#include "base/bind.h"
+#include "base/check_op.h"
#include "base/command_line.h"
#include "base/environment.h"
-#include "base/logging.h"
#include "base/metrics/field_trial_params.h"
#include "base/nix/xdg_util.h"
#include "base/stl_util.h"
diff --git a/chromium/media/audio/fake_audio_output_stream.cc b/chromium/media/audio/fake_audio_output_stream.cc
index b39e13454d0..04a624e321f 100644
--- a/chromium/media/audio/fake_audio_output_stream.cc
+++ b/chromium/media/audio/fake_audio_output_stream.cc
@@ -6,7 +6,7 @@
#include "base/bind.h"
#include "base/bind_helpers.h"
-#include "base/logging.h"
+#include "base/check.h"
#include "base/single_thread_task_runner.h"
#include "base/time/time.h"
#include "media/audio/audio_manager_base.h"
diff --git a/chromium/media/audio/mock_audio_manager.cc b/chromium/media/audio/mock_audio_manager.cc
index 16ab5c719ad..a4056b2edf7 100644
--- a/chromium/media/audio/mock_audio_manager.cc
+++ b/chromium/media/audio/mock_audio_manager.cc
@@ -8,7 +8,7 @@
#include "base/bind.h"
#include "base/callback.h"
-#include "base/logging.h"
+#include "base/check.h"
#include "media/audio/mock_audio_debug_recording_manager.h"
#include "media/base/audio_parameters.h"
diff --git a/chromium/media/audio/pulse/pulse_input.cc b/chromium/media/audio/pulse/pulse_input.cc
index 3f0c9d7a1d4..10bbd2d2af6 100644
--- a/chromium/media/audio/pulse/pulse_input.cc
+++ b/chromium/media/audio/pulse/pulse_input.cc
@@ -6,7 +6,7 @@
#include <stdint.h>
-#include "base/logging.h"
+#include "base/check.h"
#include "base/strings/stringprintf.h"
#include "media/audio/audio_device_description.h"
#include "media/audio/pulse/audio_manager_pulse.h"
diff --git a/chromium/media/audio/simple_sources_unittest.cc b/chromium/media/audio/simple_sources_unittest.cc
index 44009d4b87c..263d639242f 100644
--- a/chromium/media/audio/simple_sources_unittest.cc
+++ b/chromium/media/audio/simple_sources_unittest.cc
@@ -11,7 +11,6 @@
#include <memory>
#include "base/files/file_util.h"
-#include "base/logging.h"
#include "base/time/time.h"
#include "media/audio/audio_io.h"
#include "media/audio/test_data.h"
diff --git a/chromium/media/audio/wav_audio_handler_unittest.cc b/chromium/media/audio/wav_audio_handler_unittest.cc
index 9969c116c43..89521245a61 100644
--- a/chromium/media/audio/wav_audio_handler_unittest.cc
+++ b/chromium/media/audio/wav_audio_handler_unittest.cc
@@ -9,7 +9,6 @@
#include <memory>
#include <string>
-#include "base/logging.h"
#include "base/stl_util.h"
#include "base/strings/string_piece.h"
#include "media/audio/test_data.h"
diff --git a/chromium/media/audio/win/audio_output_win_unittest.cc b/chromium/media/audio/win/audio_output_win_unittest.cc
index f207cf2b0fb..7a4fc8648c4 100644
--- a/chromium/media/audio/win/audio_output_win_unittest.cc
+++ b/chromium/media/audio/win/audio_output_win_unittest.cc
@@ -472,8 +472,12 @@ TEST_F(WinAudioTest, PCMWaveStreamPendingBytes) {
// from a potentially remote thread.
class SyncSocketSource : public AudioOutputStream::AudioSourceCallback {
public:
- SyncSocketSource(base::SyncSocket* socket, const AudioParameters& params)
- : socket_(socket), params_(params) {
+ SyncSocketSource(base::SyncSocket* socket,
+ const AudioParameters& params,
+ int expected_packet_count)
+ : socket_(socket),
+ params_(params),
+ expected_packet_count_(expected_packet_count) {
// Setup AudioBus wrapping data we'll receive over the sync socket.
packet_size_ = AudioBus::CalculateMemorySize(params);
data_.reset(static_cast<float*>(
@@ -488,18 +492,28 @@ class SyncSocketSource : public AudioOutputStream::AudioSourceCallback {
base::TimeTicks delay_timestamp,
int /* prior_frames_skipped */,
AudioBus* dest) override {
- uint32_t control_signal = 0;
- socket_->Send(&control_signal, sizeof(control_signal));
- output_buffer()->params.delay_us = delay.InMicroseconds();
- output_buffer()->params.delay_timestamp_us =
- (delay_timestamp - base::TimeTicks()).InMicroseconds();
- uint32_t size = socket_->Receive(data_.get(), packet_size_);
-
- DCHECK_EQ(static_cast<size_t>(size) % sizeof(*audio_bus_->channel(0)), 0U);
- audio_bus_->CopyTo(dest);
- return audio_bus_->frames();
+ // If we ask for more data once the producer has shutdown, we will hang
+ // on |socket_->Receive()|.
+ if (current_packet_count_ < expected_packet_count_) {
+ uint32_t control_signal = 0;
+ socket_->Send(&control_signal, sizeof(control_signal));
+ output_buffer()->params.delay_us = delay.InMicroseconds();
+ output_buffer()->params.delay_timestamp_us =
+ (delay_timestamp - base::TimeTicks()).InMicroseconds();
+ uint32_t size = socket_->Receive(data_.get(), packet_size_);
+ ++current_packet_count_;
+
+ DCHECK_EQ(static_cast<size_t>(size) % sizeof(*audio_bus_->channel(0)),
+ 0U);
+ audio_bus_->CopyTo(dest);
+ return audio_bus_->frames();
+ }
+
+ return 0;
}
+
int packet_size() const { return packet_size_; }
+
AudioOutputBuffer* output_buffer() const {
return reinterpret_cast<AudioOutputBuffer*>(data_.get());
}
@@ -513,6 +527,11 @@ class SyncSocketSource : public AudioOutputStream::AudioSourceCallback {
int packet_size_;
std::unique_ptr<float, base::AlignedFreeDeleter> data_;
std::unique_ptr<AudioBus> audio_bus_;
+
+ // This test produces a fixed number of packets, we need these so we know
+ // when to stop listening.
+ const int expected_packet_count_;
+ int current_packet_count_ = 0;
};
struct SyncThreadContext {
@@ -523,6 +542,7 @@ struct SyncThreadContext {
double sine_freq;
uint32_t packet_size_bytes;
AudioOutputBuffer* buffer;
+ int total_packets;
};
// This thread provides the data that the SyncSocketSource above needs
@@ -541,10 +561,11 @@ DWORD __stdcall SyncSocketThread(void* context) {
AudioBus::WrapMemory(ctx.channels, ctx.frames, data.get());
SineWaveAudioSource sine(1, ctx.sine_freq, ctx.sample_rate);
- const int kTwoSecFrames = ctx.sample_rate * 2;
uint32_t control_signal = 0;
- for (int ix = 0; ix < kTwoSecFrames; ix += ctx.frames) {
+ for (int ix = 0; ix < ctx.total_packets; ++ix) {
+ // Listen for a signal from the Audio Stream that it wants data. This is a
+ // blocking call and will not proceed until we receive the signal.
if (ctx.socket->Receive(&control_signal, sizeof(control_signal)) == 0)
break;
base::TimeDelta delay =
@@ -553,6 +574,8 @@ DWORD __stdcall SyncSocketThread(void* context) {
base::TimeTicks() + base::TimeDelta::FromMicroseconds(
ctx.buffer->params.delay_timestamp_us);
sine.OnMoreData(delay, delay_timestamp, 0, audio_bus.get());
+
+ // Send the audio data to the Audio Stream.
ctx.socket->Send(data.get(), ctx.packet_size_bytes);
}
@@ -572,6 +595,9 @@ TEST_F(WinAudioTest, SyncSocketBasic) {
static const int sample_rate = AudioParameters::kAudioCDSampleRate;
static const uint32_t kSamples20ms = sample_rate / 50;
+ // We want 2 seconds of audio, which means we need 100 packets as each packet
+ // contains 20ms worth of audio samples.
+ static const int kPackets2s = 100;
AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR, CHANNEL_LAYOUT_MONO,
sample_rate, kSamples20ms);
@@ -581,11 +607,20 @@ TEST_F(WinAudioTest, SyncSocketBasic) {
ASSERT_TRUE(oas->Open());
+ // Create two sockets and connect them with a named pipe.
base::SyncSocket sockets[2];
ASSERT_TRUE(base::SyncSocket::CreatePair(&sockets[0], &sockets[1]));
- SyncSocketSource source(&sockets[0], params);
+ // Give one socket to the source, which receives requests from the
+ // AudioOutputStream for more data. On such a request, it will send a control
+ // signal to the SyncThreadContext, then it will receive
+ // an audio packet back which it will give to the AudioOutputStream.
+ SyncSocketSource source(&sockets[0], params, kPackets2s);
+ // Give the other socket to the thread. This thread runs a loop that will
+ // generate enough audio packets for 2 seconds worth of audio. It will listen
+ // for a control signal and when it gets one it will write one audio packet
+ // to the pipe that connects the two sockets.
SyncThreadContext thread_context;
thread_context.sample_rate = params.sample_rate();
thread_context.sine_freq = 200.0;
@@ -594,15 +629,22 @@ TEST_F(WinAudioTest, SyncSocketBasic) {
thread_context.channels = params.channels();
thread_context.socket = &sockets[1];
thread_context.buffer = source.output_buffer();
+ thread_context.total_packets = kPackets2s;
HANDLE thread = ::CreateThread(NULL, 0, SyncSocketThread,
&thread_context, 0, NULL);
+ // Start the AudioOutputStream, which will request data via
+ // SyncSocketSource::OnMoreData until the SyncThreadContext has run out of
+ // data to give.
oas->Start(&source);
+ // Wait for the SyncThreadContext to finish its loop, should take 2 seconds.
+ // During this time it is providing audio data as described above.
::WaitForSingleObject(thread, INFINITE);
::CloseHandle(thread);
+ // Once no more data is being sent, we can stop and close the stream.
oas->Stop();
oas->Close();
}
diff --git a/chromium/media/audio/win/avrt_wrapper_win.cc b/chromium/media/audio/win/avrt_wrapper_win.cc
index 2fb716fcb59..6e3c1f75112 100644
--- a/chromium/media/audio/win/avrt_wrapper_win.cc
+++ b/chromium/media/audio/win/avrt_wrapper_win.cc
@@ -4,7 +4,7 @@
#include "media/audio/win/avrt_wrapper_win.h"
-#include "base/logging.h"
+#include "base/check.h"
#include "base/stl_util.h"
namespace avrt {
@@ -23,7 +23,7 @@ bool Initialize() {
if (!g_set_mm_thread_priority) {
// The avrt.dll is available on Windows Vista and later.
wchar_t path[MAX_PATH] = {0};
- ExpandEnvironmentStrings(L"%WINDIR%\\system32\\avrt.dll", path,
+ ExpandEnvironmentStrings(L"%SystemRoot%\\system32\\avrt.dll", path,
base::size(path));
g_avrt = LoadLibraryExW(path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
if (!g_avrt)
diff --git a/chromium/media/base/BUILD.gn b/chromium/media/base/BUILD.gn
index 80cc2864762..e5f3cb49ffd 100644
--- a/chromium/media/base/BUILD.gn
+++ b/chromium/media/base/BUILD.gn
@@ -269,7 +269,6 @@ 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",
@@ -309,6 +308,8 @@ jumbo_source_set("base") {
"video_decoder.h",
"video_decoder_config.cc",
"video_decoder_config.h",
+ "video_encoder.cc",
+ "video_encoder.h",
"video_frame.cc",
"video_frame.h",
"video_frame_layout.cc",
@@ -376,10 +377,6 @@ jumbo_source_set("base") {
}
if (use_x11) {
- configs += [
- "//build/config/linux:x11",
- "//build/config/linux:xext",
- ]
sources += [ "user_input_monitor_linux.cc" ]
deps += [
"//ui/events:events_base",
@@ -425,6 +422,10 @@ jumbo_source_set("base") {
sources += [ "demuxer_memory_limit_default.cc" ]
}
+ if (!is_android) {
+ sources += [ "speech_recognition_client.h" ]
+ }
+
if (enable_media_drm_storage) {
sources += [
"media_drm_key_type.h",
@@ -441,6 +442,7 @@ source_set("video_facing") {
if (is_android) {
java_cpp_enum("java_enums") {
sources = [
+ "container_names.h",
"encryption_scheme.h",
"video_codecs.h",
]
diff --git a/chromium/media/base/android/BUILD.gn b/chromium/media/base/android/BUILD.gn
index 480089fa540..8b37cff0072 100644
--- a/chromium/media/base/android/BUILD.gn
+++ b/chromium/media/base/android/BUILD.gn
@@ -160,6 +160,7 @@ if (is_android) {
android_library("media_java") {
deps = [
+ ":display_java",
":media_java_resources",
"//base:base_java",
"//base:jni_java",
@@ -177,6 +178,7 @@ if (is_android) {
"java/src/org/chromium/media/BitrateAdjuster.java",
"java/src/org/chromium/media/CodecProfileLevelList.java",
"java/src/org/chromium/media/HdrMetadata.java",
+ "java/src/org/chromium/media/MaxAnticipatedResolutionEstimator.java",
"java/src/org/chromium/media/MediaCodecBridge.java",
"java/src/org/chromium/media/MediaCodecBridgeBuilder.java",
"java/src/org/chromium/media/MediaCodecEncoder.java",
@@ -188,9 +190,17 @@ if (is_android) {
"java/src/org/chromium/media/MediaPlayerBridge.java",
"java/src/org/chromium/media/MediaPlayerListener.java",
"java/src/org/chromium/media/MediaServerCrashListener.java",
+ "java/src/org/chromium/media/ScreenResolutionUtil.java",
]
}
+ # TODO (b/146418831): Replace with androidx version
+ android_library("display_java") {
+ sources = [ "java/src/org/chromium/media/DisplayCompat.java" ]
+
+ deps = [ "//third_party/android_deps:androidx_annotation_annotation_java" ]
+ }
+
junit_binary("media_base_junit_tests") {
sources = [
"java/src/test/org/chromium/media/AudioTrackOutputStreamTest.java",
diff --git a/chromium/media/base/android/media_codec_util.cc b/chromium/media/base/android/media_codec_util.cc
index d9af96da9e8..e70b11484a2 100644
--- a/chromium/media/base/android/media_codec_util.cc
+++ b/chromium/media/base/android/media_codec_util.cc
@@ -239,19 +239,6 @@ std::set<int> MediaCodecUtil::GetEncoderColorFormats(
}
// static
-bool MediaCodecUtil::IsHLSPath(const GURL& url) {
- return (url.SchemeIsHTTPOrHTTPS() || url.SchemeIsFile()) &&
- base::EndsWith(url.path(), ".m3u8",
- base::CompareCase::INSENSITIVE_ASCII);
-}
-
-// static
-bool MediaCodecUtil::IsHLSURL(const GURL& url) {
- return (url.SchemeIsHTTPOrHTTPS() || url.SchemeIsFile()) &&
- url.spec().find("m3u8") != std::string::npos;
-}
-
-// static
bool MediaCodecUtil::IsVp8DecoderAvailable() {
return IsMediaCodecAvailable() && IsDecoderSupportedByDevice(kVp8MimeType);
}
@@ -303,16 +290,16 @@ bool MediaCodecUtil::IsSurfaceViewOutputSupported() {
// Notably this is codec agnostic at present, so any devices added to
// the blacklist will avoid trying to play any codecs on SurfaceView. If
// needed in the future this can be expanded to be codec specific.
- const char* model_prefixes[] = {// Exynos 4 (Mali-400)
- "GT-I9300", "GT-I9305", "SHV-E210",
- // Snapdragon S4 (Adreno-225)
- "SCH-I535", "SCH-J201", "SCH-R530",
- "SCH-I960", "SCH-S968", "SGH-T999",
- "SGH-I747", "SGH-N064", 0};
+ constexpr const char* kDisabledModels[] = {// Exynos 4 (Mali-400)
+ "GT-I9300", "GT-I9305", "SHV-E210",
+ // Snapdragon S4 (Adreno-225)
+ "SCH-I535", "SCH-J201", "SCH-R530",
+ "SCH-I960", "SCH-S968", "SGH-T999",
+ "SGH-I747", "SGH-N064"};
std::string model(base::android::BuildInfo::GetInstance()->model());
- for (int i = 0; model_prefixes[i]; ++i) {
- if (base::StartsWith(model, model_prefixes[i],
+ for (auto* disabled_model : kDisabledModels) {
+ if (base::StartsWith(model, disabled_model,
base::CompareCase::INSENSITIVE_ASCII)) {
return false;
}
@@ -343,8 +330,25 @@ bool MediaCodecUtil::CanDecode(AudioCodec codec) {
}
// static
-bool MediaCodecUtil::IsH264EncoderAvailable() {
- return IsMediaCodecAvailable() && IsEncoderSupportedByDevice(kAvcMimeType);
+bool MediaCodecUtil::IsH264EncoderAvailable(bool use_codec_list) {
+ if (!IsMediaCodecAvailable())
+ return false;
+
+ constexpr const char* kDisabledModels[] = {"SAMSUNG-SGH-I337", "Nexus 7",
+ "Nexus 4"};
+ const std::string model(base::android::BuildInfo::GetInstance()->model());
+ for (auto* disabled_model : kDisabledModels) {
+ if (base::StartsWith(model, disabled_model,
+ base::CompareCase::INSENSITIVE_ASCII)) {
+ return false;
+ }
+ }
+
+ if (use_codec_list)
+ return IsEncoderSupportedByDevice(kAvcMimeType);
+
+ // Assume support since Chrome only supports Lollipop+.
+ return true;
}
// static
diff --git a/chromium/media/base/android/media_codec_util.h b/chromium/media/base/android/media_codec_util.h
index e077453f637..5fd273de2ed 100644
--- a/chromium/media/base/android/media_codec_util.h
+++ b/chromium/media/base/android/media_codec_util.h
@@ -17,8 +17,6 @@
#include "media/base/media_export.h"
#include "media/base/video_codecs.h"
-class GURL;
-
namespace media {
class MediaCodecBridge;
@@ -51,12 +49,6 @@ class MEDIA_EXPORT MediaCodecUtil {
// Returns true if MediaCodec supports CBCS Encryption.
static bool PlatformSupportsCbcsEncryption(int sdk);
- // Test whether a URL contains "m3u8".
- static bool IsHLSURL(const GURL& url);
-
- // Test whether the path of a URL ends with ".m3u8".
- static bool IsHLSPath(const GURL& url);
-
// Indicates if the vp8 decoder or encoder is available on this device.
static bool IsVp8DecoderAvailable();
static bool IsVp8EncoderAvailable();
@@ -103,9 +95,10 @@ class MEDIA_EXPORT MediaCodecUtil {
// Indicates if the h264 encoder is available on this device.
//
- // WARNING: This can't be used from the renderer process since it attempts to
- // access MediaCodecList (which requires permissions).
- static bool IsH264EncoderAvailable();
+ // WARNING: If |use_codec_list| is true, this can't be used from the renderer
+ // process since it attempts to access MediaCodecList (which requires
+ // permissions).
+ static bool IsH264EncoderAvailable(bool use_codec_list = true);
// Returns a vector of supported codecs profiles and levels.
//
diff --git a/chromium/media/base/android/media_drm_bridge_client.cc b/chromium/media/base/android/media_drm_bridge_client.cc
index 8b4b35b59b8..cad887c4e14 100644
--- a/chromium/media/base/android/media_drm_bridge_client.cc
+++ b/chromium/media/base/android/media_drm_bridge_client.cc
@@ -4,7 +4,7 @@
#include "media/base/android/media_drm_bridge_client.h"
-#include "base/logging.h"
+#include "base/check.h"
#include "base/stl_util.h"
namespace media {
diff --git a/chromium/media/base/android/media_drm_bridge_delegate.cc b/chromium/media/base/android/media_drm_bridge_delegate.cc
index 5630d4af0da..8bd43ca1120 100644
--- a/chromium/media/base/android/media_drm_bridge_delegate.cc
+++ b/chromium/media/base/android/media_drm_bridge_delegate.cc
@@ -4,7 +4,7 @@
#include "media/base/android/media_drm_bridge_delegate.h"
-#include "base/logging.h"
+#include "base/check.h"
namespace media {
diff --git a/chromium/media/base/android/media_player_bridge.cc b/chromium/media/base/android/media_player_bridge.cc
index c6e7846be91..c0a10cef065 100644
--- a/chromium/media/base/android/media_player_bridge.cc
+++ b/chromium/media/base/android/media_player_bridge.cc
@@ -11,8 +11,9 @@
#include "base/android/jni_string.h"
#include "base/android/scoped_java_ref.h"
#include "base/bind.h"
-#include "base/logging.h"
+#include "base/check_op.h"
#include "base/metrics/histogram_macros.h"
+#include "base/notreached.h"
#include "base/numerics/ranges.h"
#include "base/strings/string_util.h"
#include "base/threading/thread_task_runner_handle.h"
diff --git a/chromium/media/base/android/media_player_listener.cc b/chromium/media/base/android/media_player_listener.cc
index 11a5dc87f30..5b7f206dceb 100644
--- a/chromium/media/base/android/media_player_listener.cc
+++ b/chromium/media/base/android/media_player_listener.cc
@@ -7,8 +7,8 @@
#include "base/android/jni_android.h"
#include "base/android/scoped_java_ref.h"
#include "base/bind.h"
+#include "base/check.h"
#include "base/location.h"
-#include "base/logging.h"
#include "base/single_thread_task_runner.h"
#include "media/base/android/media_jni_headers/MediaPlayerListener_jni.h"
#include "media/base/android/media_player_bridge.h"
diff --git a/chromium/media/base/android/stream_texture_wrapper.h b/chromium/media/base/android/stream_texture_wrapper.h
index 69a388e5cef..4900adf3805 100644
--- a/chromium/media/base/android/stream_texture_wrapper.h
+++ b/chromium/media/base/android/stream_texture_wrapper.h
@@ -23,7 +23,6 @@ class MEDIA_EXPORT StreamTextureWrapper {
// See StreamTextureWrapperImpl.
virtual void Initialize(
const base::RepeatingClosure& received_frame_cb,
- const gfx::Size& natural_size,
scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner,
StreamTextureWrapperInitCB init_cb) = 0;
diff --git a/chromium/media/base/audio_block_fifo.cc b/chromium/media/base/audio_block_fifo.cc
index fe33ce5ef22..85fa6d27b93 100644
--- a/chromium/media/base/audio_block_fifo.cc
+++ b/chromium/media/base/audio_block_fifo.cc
@@ -8,7 +8,7 @@
#include "media/base/audio_block_fifo.h"
-#include "base/logging.h"
+#include "base/check_op.h"
#include "base/trace_event/trace_event.h"
namespace media {
diff --git a/chromium/media/base/audio_buffer_converter.cc b/chromium/media/base/audio_buffer_converter.cc
index 17779fc6f92..3e5c0fa114f 100644
--- a/chromium/media/base/audio_buffer_converter.cc
+++ b/chromium/media/base/audio_buffer_converter.cc
@@ -7,7 +7,7 @@
#include <algorithm>
#include <cmath>
-#include "base/logging.h"
+#include "base/check_op.h"
#include "media/base/audio_bus.h"
#include "media/base/audio_decoder_config.h"
#include "media/base/audio_timestamp_helper.h"
diff --git a/chromium/media/base/audio_buffer_queue.cc b/chromium/media/base/audio_buffer_queue.cc
index d77946578d1..ca5be72da5c 100644
--- a/chromium/media/base/audio_buffer_queue.cc
+++ b/chromium/media/base/audio_buffer_queue.cc
@@ -6,7 +6,7 @@
#include <algorithm>
-#include "base/logging.h"
+#include "base/check_op.h"
#include "media/base/audio_bus.h"
namespace media {
diff --git a/chromium/media/base/audio_buffer_queue_unittest.cc b/chromium/media/base/audio_buffer_queue_unittest.cc
index 8fbeb98de08..3cf5e120c56 100644
--- a/chromium/media/base/audio_buffer_queue_unittest.cc
+++ b/chromium/media/base/audio_buffer_queue_unittest.cc
@@ -9,7 +9,6 @@
#include <limits>
#include <memory>
-#include "base/logging.h"
#include "base/time/time.h"
#include "media/base/audio_buffer.h"
#include "media/base/audio_bus.h"
diff --git a/chromium/media/base/audio_bus.cc b/chromium/media/base/audio_bus.cc
index 991e73e11e0..694b53e8c24 100644
--- a/chromium/media/base/audio_bus.cc
+++ b/chromium/media/base/audio_bus.cc
@@ -7,11 +7,13 @@
#include <stddef.h>
#include <stdint.h>
+#include <cstring>
#include <limits>
#include <utility>
-#include "base/logging.h"
+#include "base/check_op.h"
#include "base/memory/ptr_util.h"
+#include "base/notreached.h"
#include "base/numerics/safe_conversions.h"
#include "media/base/audio_parameters.h"
#include "media/base/limits.h"
diff --git a/chromium/media/base/audio_codecs.cc b/chromium/media/base/audio_codecs.cc
index ba5dbc66d12..06a9e73c295 100644
--- a/chromium/media/base/audio_codecs.cc
+++ b/chromium/media/base/audio_codecs.cc
@@ -4,7 +4,6 @@
#include "media/base/audio_codecs.h"
-#include "base/logging.h"
#include "base/strings/string_util.h"
namespace media {
diff --git a/chromium/media/base/audio_fifo.cc b/chromium/media/base/audio_fifo.cc
index 507edd133f9..75408b335fc 100644
--- a/chromium/media/base/audio_fifo.cc
+++ b/chromium/media/base/audio_fifo.cc
@@ -4,7 +4,9 @@
#include "media/base/audio_fifo.h"
-#include "base/logging.h"
+#include <cstring>
+
+#include "base/check_op.h"
namespace media {
diff --git a/chromium/media/base/audio_hash_unittest.cc b/chromium/media/base/audio_hash_unittest.cc
index 8e04bf0d5c2..03383cfb3f8 100644
--- a/chromium/media/base/audio_hash_unittest.cc
+++ b/chromium/media/base/audio_hash_unittest.cc
@@ -4,7 +4,6 @@
#include <memory>
-#include "base/logging.h"
#include "base/macros.h"
#include "media/base/audio_bus.h"
#include "media/base/audio_hash.h"
diff --git a/chromium/media/base/audio_latency_unittest.cc b/chromium/media/base/audio_latency_unittest.cc
index b4c3d9496f1..dcf378defbe 100644
--- a/chromium/media/base/audio_latency_unittest.cc
+++ b/chromium/media/base/audio_latency_unittest.cc
@@ -6,7 +6,6 @@
#include <stdint.h>
-#include "base/logging.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "media/base/limits.h"
diff --git a/chromium/media/base/audio_parameters.cc b/chromium/media/base/audio_parameters.cc
index 8bf16c27f67..b3b07180107 100644
--- a/chromium/media/base/audio_parameters.cc
+++ b/chromium/media/base/audio_parameters.cc
@@ -4,7 +4,8 @@
#include "media/base/audio_parameters.h"
-#include "base/logging.h"
+#include <sstream>
+
#include "media/base/limits.h"
namespace media {
diff --git a/chromium/media/base/audio_power_monitor.cc b/chromium/media/base/audio_power_monitor.cc
index 70d9afd4172..3b3c6b5556c 100644
--- a/chromium/media/base/audio_power_monitor.cc
+++ b/chromium/media/base/audio_power_monitor.cc
@@ -7,7 +7,7 @@
#include <algorithm>
#include <cmath>
-#include "base/logging.h"
+#include "base/check_op.h"
#include "base/numerics/ranges.h"
#include "base/time/time.h"
#include "media/base/audio_bus.h"
diff --git a/chromium/media/base/audio_pull_fifo.cc b/chromium/media/base/audio_pull_fifo.cc
index 65a637d11e4..ed9e9efcc17 100644
--- a/chromium/media/base/audio_pull_fifo.cc
+++ b/chromium/media/base/audio_pull_fifo.cc
@@ -6,7 +6,7 @@
#include <algorithm>
-#include "base/logging.h"
+#include "base/check_op.h"
#include "media/base/audio_bus.h"
namespace media {
diff --git a/chromium/media/base/audio_push_fifo.cc b/chromium/media/base/audio_push_fifo.cc
index 5922b8a5844..afb2b0fbeba 100644
--- a/chromium/media/base/audio_push_fifo.cc
+++ b/chromium/media/base/audio_push_fifo.cc
@@ -7,6 +7,7 @@
#include <algorithm>
#include "base/logging.h"
+#include "base/trace_event/trace_event.h"
namespace media {
@@ -27,6 +28,8 @@ void AudioPushFifo::Reset(int frames_per_buffer) {
}
void AudioPushFifo::Push(const AudioBus& input_bus) {
+ TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("audio"), "AudioPushFifo::Push");
+
DCHECK_GT(frames_per_buffer_, 0);
// Fast path: No buffering required.
diff --git a/chromium/media/base/audio_renderer_mixer.cc b/chromium/media/base/audio_renderer_mixer.cc
index d8a61da696c..d3c1bd8c7db 100644
--- a/chromium/media/base/audio_renderer_mixer.cc
+++ b/chromium/media/base/audio_renderer_mixer.cc
@@ -8,13 +8,17 @@
#include "base/bind.h"
#include "base/bind_helpers.h"
-#include "base/logging.h"
+#include "base/check_op.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_macros.h"
+#include "base/task/task_traits.h"
+#include "base/task/thread_pool.h"
#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
#include "media/base/audio_renderer_mixer_input.h"
#include "media/base/audio_timestamp_helper.h"
+#include "media/base/media_switches.h"
+#include "media/base/silent_sink_suspender.h"
namespace media {
@@ -64,13 +68,28 @@ AudioRendererMixer::AudioRendererMixer(const AudioParameters& output_params,
playing_(true),
input_count_tracker_(new UMAMaxValueTracker(std::move(log_callback))) {
DCHECK(audio_sink_);
- audio_sink_->Initialize(output_params, this);
+
+ // If enabled we will disable the real audio output stream for muted/silent
+ // playbacks after some time elapses.
+ RenderCallback* callback = this;
+ if (base::FeatureList::IsEnabled(media::kSuspendMutedAudio)) {
+ // We use slightly more than |pause_delay_| time before suspending the sink
+ // to ensure that we just Pause() entirely instead of using a fake sink when
+ // possible.
+ muted_suspender_.reset(new SilentSinkSuspender(
+ this, pause_delay_ + base::TimeDelta::FromMilliseconds(500),
+ output_params, audio_sink_, GetSuspenderTaskRunner()));
+ callback = muted_suspender_.get();
+ }
+
+ audio_sink_->Initialize(output_params, callback);
audio_sink_->Start();
}
AudioRendererMixer::~AudioRendererMixer() {
// AudioRendererSink must be stopped before mixer is destructed.
audio_sink_->Stop();
+ muted_suspender_.reset();
// Ensure that all mixer inputs have removed themselves prior to destruction.
DCHECK(master_converter_.empty());
@@ -167,6 +186,8 @@ int AudioRendererMixer::Render(base::TimeDelta delay,
last_play_time_ = now;
} else if (now - last_play_time_ >= pause_delay_ && playing_) {
audio_sink_->Pause();
+ if (muted_suspender_)
+ muted_suspender_->OnPaused();
playing_ = false;
}
@@ -188,4 +209,14 @@ void AudioRendererMixer::OnRenderError() {
input->OnRenderError();
}
+scoped_refptr<base::SingleThreadTaskRunner>
+AudioRendererMixer::GetSuspenderTaskRunner() {
+ if (!suspender_task_runner_) {
+ suspender_task_runner_ = base::ThreadPool::CreateSingleThreadTaskRunner(
+ {base::TaskPriority::USER_VISIBLE,
+ base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN});
+ }
+ return suspender_task_runner_;
+}
+
} // namespace media
diff --git a/chromium/media/base/audio_renderer_mixer.h b/chromium/media/base/audio_renderer_mixer.h
index 9c9325a2281..b8917dcf532 100644
--- a/chromium/media/base/audio_renderer_mixer.h
+++ b/chromium/media/base/audio_renderer_mixer.h
@@ -21,8 +21,13 @@
#include "media/base/audio_renderer_sink.h"
#include "media/base/loopback_audio_converter.h"
+namespace base {
+class SingleThreadTaskRunner;
+}
+
namespace media {
class AudioRendererMixerInput;
+class SilentSinkSuspender;
// Mixes a set of AudioConverter::InputCallbacks into a single output stream
// which is funneled into a single shared AudioRendererSink; saving a bundle
@@ -66,6 +71,8 @@ class MEDIA_EXPORT AudioRendererMixer
AudioBus* audio_bus) override;
void OnRenderError() override;
+ scoped_refptr<base::SingleThreadTaskRunner> GetSuspenderTaskRunner();
+
bool is_master_sample_rate(int sample_rate) const {
return sample_rate == output_params_.sample_rate();
}
@@ -76,6 +83,13 @@ class MEDIA_EXPORT AudioRendererMixer
// Output sink for this mixer.
const scoped_refptr<AudioRendererSink> audio_sink_;
+ // Optional utility class which disables audio output when only silent audio
+ // is being delivered to the physical audio output stream.
+ std::unique_ptr<SilentSinkSuspender> muted_suspender_;
+
+ // Task Runner used by |muted_suspender_|.
+ scoped_refptr<base::SingleThreadTaskRunner> suspender_task_runner_;
+
// ---------------[ All variables below protected by |lock_| ]---------------
base::Lock lock_;
diff --git a/chromium/media/base/audio_timestamp_helper.cc b/chromium/media/base/audio_timestamp_helper.cc
index 0d62c125b75..5a65a240281 100644
--- a/chromium/media/base/audio_timestamp_helper.cc
+++ b/chromium/media/base/audio_timestamp_helper.cc
@@ -4,7 +4,7 @@
#include "media/base/audio_timestamp_helper.h"
-#include "base/logging.h"
+#include "base/check_op.h"
#include "media/base/timestamp_constants.h"
namespace media {
diff --git a/chromium/media/base/bit_reader_core.cc b/chromium/media/base/bit_reader_core.cc
index cd3e896cc05..715a25b3e36 100644
--- a/chromium/media/base/bit_reader_core.cc
+++ b/chromium/media/base/bit_reader_core.cc
@@ -5,6 +5,7 @@
#include "media/base/bit_reader_core.h"
#include <stdint.h>
+#include <cstring>
#include "base/sys_byteorder.h"
diff --git a/chromium/media/base/buffering_state.cc b/chromium/media/base/buffering_state.cc
index 4cf3af511fd..eabc940bf52 100644
--- a/chromium/media/base/buffering_state.cc
+++ b/chromium/media/base/buffering_state.cc
@@ -3,8 +3,11 @@
// found in the LICENSE file.
#include "media/base/buffering_state.h"
+
#include <string>
-#include "base/logging.h"
+#include <vector>
+
+#include "base/check.h"
namespace media {
diff --git a/chromium/media/base/byte_queue.cc b/chromium/media/base/byte_queue.cc
index c54ac79bdfb..ed3d67ebca3 100644
--- a/chromium/media/base/byte_queue.cc
+++ b/chromium/media/base/byte_queue.cc
@@ -5,8 +5,9 @@
#include "media/base/byte_queue.h"
#include <algorithm>
+#include <cstring>
-#include "base/logging.h"
+#include "base/check_op.h"
#include "base/numerics/checked_math.h"
namespace media {
diff --git a/chromium/media/base/cdm_callback_promise.cc b/chromium/media/base/cdm_callback_promise.cc
index 103507f72fb..26d6f7aa839 100644
--- a/chromium/media/base/cdm_callback_promise.cc
+++ b/chromium/media/base/cdm_callback_promise.cc
@@ -5,7 +5,7 @@
#include "media/base/cdm_callback_promise.h"
#include "base/callback_helpers.h"
-#include "base/logging.h"
+#include "base/check.h"
namespace media {
diff --git a/chromium/media/base/cdm_context.cc b/chromium/media/base/cdm_context.cc
index a09fb064c80..dac22fbd70f 100644
--- a/chromium/media/base/cdm_context.cc
+++ b/chromium/media/base/cdm_context.cc
@@ -29,11 +29,12 @@ bool CdmContext::RequiresMediaFoundationRenderer() {
return false;
}
-#if BUILDFLAG(ENABLE_LIBRARY_CDMS)
-CdmProxyContext* CdmContext::GetCdmProxyContext() {
- return nullptr;
+#if defined(OS_WIN)
+bool CdmContext::GetMediaFoundationCdmProxy(
+ GetMediaFoundationCdmProxyCB get_mf_cdm_proxy_cb) {
+ return false;
}
-#endif // BUILDFLAG(ENABLE_LIBRARY_CDMS)
+#endif
#if defined(OS_ANDROID)
MediaCryptoContext* CdmContext::GetMediaCryptoContext() {
diff --git a/chromium/media/base/cdm_context.h b/chromium/media/base/cdm_context.h
index 53986b9fb03..e346698746e 100644
--- a/chromium/media/base/cdm_context.h
+++ b/chromium/media/base/cdm_context.h
@@ -11,10 +11,13 @@
#include "media/base/media_export.h"
#include "media/media_buildflags.h"
+#if defined(OS_WIN)
+struct IMFCdmProxy;
+#endif
+
namespace media {
class CallbackRegistration;
-class CdmProxyContext;
class Decryptor;
class MediaCryptoContext;
@@ -86,12 +89,18 @@ class MEDIA_EXPORT CdmContext {
// TODO(crbug.com/804397): Use base::UnguessableToken for CDM ID.
virtual int GetCdmId() const;
-#if BUILDFLAG(ENABLE_LIBRARY_CDMS)
- // Returns a CdmProxyContext that can be used by hardware decoders/decryptors.
- // Returns nullptr if CdmProxyContext is not supported, e.g. |this| is not
- // hosted by a CdmProxy.
- virtual CdmProxyContext* GetCdmProxyContext();
-#endif // BUILDFLAG(ENABLE_LIBRARY_CDMS)
+#if defined(OS_WIN)
+ using GetMediaFoundationCdmProxyCB = base::OnceCallback<void(IMFCdmProxy*)>;
+ // This allows a CdmContext to expose an IMFTrustedInput instance for use in
+ // a Media Foundation rendering pipeline. This method is asynchronous because
+ // the underlying MF-based CDM might not have a native session created yet.
+ // When the return value is true, the callback might also not be invoked
+ // if the application has never caused the MF-based CDM to create its
+ // native session.
+ // NOTE: the callback should always be fired asynchronously.
+ virtual bool GetMediaFoundationCdmProxy(
+ GetMediaFoundationCdmProxyCB get_mf_cdm_proxy_cb);
+#endif
#if defined(OS_ANDROID)
// Returns a MediaCryptoContext that can be used by MediaCodec based decoders.
@@ -120,7 +129,7 @@ MEDIA_EXPORT void IgnoreCdmAttached(bool success);
// A reference holder to make sure the CdmContext is always valid as long as
// |this| is alive. Typically |this| will hold a reference (directly or
-// indirectly) to the host, e.g. a ContentDecryptionModule or a CdmProxy.
+// indirectly) to the host, e.g. a ContentDecryptionModule.
// This class must be held on the same thread where the host lives. The raw
// CdmContext pointer returned by GetCdmContext() may be used on other threads
// if it's supported by the CdmContext implementation.
diff --git a/chromium/media/base/cdm_key_information.cc b/chromium/media/base/cdm_key_information.cc
index 66a5290a218..616732cc393 100644
--- a/chromium/media/base/cdm_key_information.cc
+++ b/chromium/media/base/cdm_key_information.cc
@@ -4,6 +4,7 @@
#include "media/base/cdm_key_information.h"
+#include "base/notreached.h"
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
diff --git a/chromium/media/base/channel_mixer.cc b/chromium/media/base/channel_mixer.cc
index 841933842a6..87e6c4590db 100644
--- a/chromium/media/base/channel_mixer.cc
+++ b/chromium/media/base/channel_mixer.cc
@@ -5,8 +5,9 @@
#include "media/base/channel_mixer.h"
#include <stddef.h>
+#include <string.h>
-#include "base/logging.h"
+#include "base/check_op.h"
#include "media/base/audio_bus.h"
#include "media/base/audio_parameters.h"
#include "media/base/channel_mixing_matrix.h"
diff --git a/chromium/media/base/channel_mixing_matrix.cc b/chromium/media/base/channel_mixing_matrix.cc
index 51a556ad04b..3ad627e1694 100644
--- a/chromium/media/base/channel_mixing_matrix.cc
+++ b/chromium/media/base/channel_mixing_matrix.cc
@@ -8,7 +8,7 @@
#include <algorithm>
-#include "base/logging.h"
+#include "base/check_op.h"
#include "media/base/channel_mixer.h"
namespace media {
diff --git a/chromium/media/base/container_names.cc b/chromium/media/base/container_names.cc
index 64fb021861d..496fdbef244 100644
--- a/chromium/media/base/container_names.cc
+++ b/chromium/media/base/container_names.cc
@@ -5,11 +5,12 @@
#include "media/base/container_names.h"
#include <stddef.h>
+#include <string.h>
#include <cctype>
#include <limits>
-#include "base/logging.h"
+#include "base/check_op.h"
#include "base/numerics/safe_conversions.h"
#include "base/stl_util.h"
#include "media/base/bit_reader.h"
diff --git a/chromium/media/base/container_names.h b/chromium/media/base/container_names.h
index 4e464859054..99cb7bf7ccd 100644
--- a/chromium/media/base/container_names.h
+++ b/chromium/media/base/container_names.h
@@ -18,6 +18,9 @@ namespace container_names {
// done at the end of the list (before CONTAINER_MAX). This list must be kept in
// sync with the enum definition "MediaContainers" in
// tools/metrics/histograms/histograms.xml.
+//
+// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.media
+// GENERATED_JAVA_PREFIX_TO_STRIP: CONTAINER_
enum MediaContainerName {
CONTAINER_UNKNOWN, // Unknown
CONTAINER_AAC, // AAC (Advanced Audio Coding)
@@ -59,7 +62,7 @@ enum MediaContainerName {
CONTAINER_WTV, // WTV (Windows Television)
CONTAINER_DASH, // DASH (MPEG-DASH)
CONTAINER_SMOOTHSTREAM, // SmoothStreaming
- CONTAINER_MAX = CONTAINER_SMOOTHSTREAM // Must be last
+ CONTAINER_MAX = CONTAINER_SMOOTHSTREAM, // Must be last
};
// Minimum size considered for processing.
diff --git a/chromium/media/base/data_source.cc b/chromium/media/base/data_source.cc
index 0ded6d30d37..48f76091f31 100644
--- a/chromium/media/base/data_source.cc
+++ b/chromium/media/base/data_source.cc
@@ -4,7 +4,6 @@
#include "media/base/data_source.h"
-#include "base/logging.h"
namespace media {
diff --git a/chromium/media/base/decode_status.cc b/chromium/media/base/decode_status.cc
index 110696fd941..97fda7276d0 100644
--- a/chromium/media/base/decode_status.cc
+++ b/chromium/media/base/decode_status.cc
@@ -19,6 +19,9 @@ const char* GetDecodeStatusString(DecodeStatus status) {
case DecodeStatus::DECODE_ERROR:
return "DecodeStatus::DECODE_ERROR";
}
+
+ NOTREACHED();
+ return "";
}
std::ostream& operator<<(std::ostream& os, const DecodeStatus& status) {
diff --git a/chromium/media/base/decoder_buffer.cc b/chromium/media/base/decoder_buffer.cc
index 5a07221ef8a..0a593b8d6ef 100644
--- a/chromium/media/base/decoder_buffer.cc
+++ b/chromium/media/base/decoder_buffer.cc
@@ -4,6 +4,8 @@
#include "media/base/decoder_buffer.h"
+#include <sstream>
+
#include "base/debug/alias.h"
namespace media {
diff --git a/chromium/media/base/decrypt_config.cc b/chromium/media/base/decrypt_config.cc
index ce994949a5a..5b7c02c8f5e 100644
--- a/chromium/media/base/decrypt_config.cc
+++ b/chromium/media/base/decrypt_config.cc
@@ -6,7 +6,7 @@
#include <stddef.h>
-#include "base/logging.h"
+#include "base/check_op.h"
#include "base/memory/ptr_util.h"
#include "base/strings/string_number_conversions.h"
#include "media/media_buildflags.h"
diff --git a/chromium/media/base/demuxer.h b/chromium/media/base/demuxer.h
index c41493a781d..a06793ab07a 100644
--- a/chromium/media/base/demuxer.h
+++ b/chromium/media/base/demuxer.h
@@ -13,6 +13,7 @@
#include "base/macros.h"
#include "base/optional.h"
#include "base/time/time.h"
+#include "media/base/container_names.h"
#include "media/base/data_source.h"
#include "media/base/demuxer_stream.h"
#include "media/base/eme_constants.h"
@@ -138,6 +139,13 @@ class MEDIA_EXPORT Demuxer : public MediaResource {
// Returns the memory usage in bytes for the demuxer.
virtual int64_t GetMemoryUsage() const = 0;
+ // Returns the container name to use for metrics.
+ // Implementations where this is not meaningful will return an empty value.
+ // Implementations that do provide values should always provide a value,
+ // returning CONTAINER_UNKNOWN in cases where the container is not known.
+ virtual base::Optional<container_names::MediaContainerName>
+ GetContainerForMetrics() const = 0;
+
// The |track_ids| vector has either 1 track, or is empty, indicating that
// all tracks should be disabled. |change_completed_cb| is fired after the
// demuxer streams are disabled, however this callback should then notify
diff --git a/chromium/media/base/encryption_scheme.cc b/chromium/media/base/encryption_scheme.cc
index 40de184e5df..0fd36097378 100644
--- a/chromium/media/base/encryption_scheme.cc
+++ b/chromium/media/base/encryption_scheme.cc
@@ -6,7 +6,6 @@
#include <ostream>
-#include "base/logging.h"
namespace media {
diff --git a/chromium/media/base/fake_audio_worker.cc b/chromium/media/base/fake_audio_worker.cc
index c32f8503d22..bcaf73ebef5 100644
--- a/chromium/media/base/fake_audio_worker.cc
+++ b/chromium/media/base/fake_audio_worker.cc
@@ -9,8 +9,8 @@
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/cancelable_callback.h"
+#include "base/check_op.h"
#include "base/location.h"
-#include "base/logging.h"
#include "base/macros.h"
#include "base/single_thread_task_runner.h"
#include "base/synchronization/lock.h"
diff --git a/chromium/media/base/fake_demuxer_stream.cc b/chromium/media/base/fake_demuxer_stream.cc
index dac08196da9..61b463adc80 100644
--- a/chromium/media/base/fake_demuxer_stream.cc
+++ b/chromium/media/base/fake_demuxer_stream.cc
@@ -11,8 +11,9 @@
#include "base/bind.h"
#include "base/callback_helpers.h"
+#include "base/check_op.h"
#include "base/location.h"
-#include "base/logging.h"
+#include "base/notreached.h"
#include "base/single_thread_task_runner.h"
#include "base/stl_util.h"
#include "base/threading/thread_task_runner_handle.h"
diff --git a/chromium/media/base/hdr_metadata.h b/chromium/media/base/hdr_metadata.h
index 4846025e3bf..715642d32cd 100644
--- a/chromium/media/base/hdr_metadata.h
+++ b/chromium/media/base/hdr_metadata.h
@@ -52,6 +52,15 @@ struct MEDIA_EXPORT HDRMetadata {
}
};
+// HDR metadata types as described in
+// https://w3c.github.io/media-capabilities/#enumdef-hdrmetadatatype
+enum class HdrMetadataType {
+ kNone,
+ kSmpteSt2086,
+ kSmpteSt2094_10,
+ kSmpteSt2094_40,
+};
+
} // namespace media
#endif // MEDIA_BASE_HDR_METADATA_H_
diff --git a/chromium/media/base/ipc/media_param_traits_macros.h b/chromium/media/base/ipc/media_param_traits_macros.h
index 3e09669bce0..1eca0edb227 100644
--- a/chromium/media/base/ipc/media_param_traits_macros.h
+++ b/chromium/media/base/ipc/media_param_traits_macros.h
@@ -37,9 +37,6 @@
#include "media/base/video_types.h"
#include "media/base/waiting.h"
#include "media/base/watch_time_keys.h"
-// TODO(crbug.com/676224): When EnabledIf attribute is supported in mojom files,
-// move CdmProxy related code into #if BUILDFLAG(ENABLE_LIBRARY_CDMS).
-#include "media/cdm/cdm_proxy.h"
#include "media/media_buildflags.h"
#include "media/video/supported_video_decoder_config.h"
#include "ui/gfx/ipc/color/gfx_param_traits_macros.h"
@@ -73,18 +70,6 @@ IPC_ENUM_TRAITS_MAX_VALUE(media::CdmMessageType,
IPC_ENUM_TRAITS_MAX_VALUE(media::CdmPromise::Exception,
media::CdmPromise::Exception::EXCEPTION_MAX)
-IPC_ENUM_TRAITS_MAX_VALUE(media::CdmProxy::Function,
- media::CdmProxy::Function::kMaxValue)
-
-IPC_ENUM_TRAITS_MAX_VALUE(media::CdmProxy::KeyType,
- media::CdmProxy::KeyType::kMaxValue)
-
-IPC_ENUM_TRAITS_MAX_VALUE(media::CdmProxy::Protocol,
- media::CdmProxy::Protocol::kMaxValue)
-
-IPC_ENUM_TRAITS_MAX_VALUE(media::CdmProxy::Status,
- media::CdmProxy::Status::kMaxValue)
-
IPC_ENUM_TRAITS_MAX_VALUE(media::CdmSessionType,
media::CdmSessionType::kMaxValue)
diff --git a/chromium/media/base/key_systems_unittest.cc b/chromium/media/base/key_systems_unittest.cc
index 29c042a4c77..64a7a4b4336 100644
--- a/chromium/media/base/key_systems_unittest.cc
+++ b/chromium/media/base/key_systems_unittest.cc
@@ -12,7 +12,8 @@
#include <string>
#include <vector>
-#include "base/logging.h"
+#include "base/check.h"
+#include "base/notreached.h"
#include "media/base/audio_parameters.h"
#include "media/base/decrypt_config.h"
#include "media/base/eme_constants.h"
diff --git a/chromium/media/base/keyboard_event_counter.cc b/chromium/media/base/keyboard_event_counter.cc
index 46c2a2d7a73..6c17f676e8c 100644
--- a/chromium/media/base/keyboard_event_counter.cc
+++ b/chromium/media/base/keyboard_event_counter.cc
@@ -4,7 +4,7 @@
#include "media/base/keyboard_event_counter.h"
-#include "base/logging.h"
+#include "base/check_op.h"
namespace media {
diff --git a/chromium/media/base/keyboard_event_counter_unittest.cc b/chromium/media/base/keyboard_event_counter_unittest.cc
index ec3707c7ad5..7d104dc76c8 100644
--- a/chromium/media/base/keyboard_event_counter_unittest.cc
+++ b/chromium/media/base/keyboard_event_counter_unittest.cc
@@ -6,7 +6,6 @@
#include <memory>
-#include "base/logging.h"
#include "base/run_loop.h"
#include "build/build_config.h"
#include "testing/gmock/include/gmock/gmock.h"
diff --git a/chromium/media/base/localized_strings.cc b/chromium/media/base/localized_strings.cc
index 5c1f24bdd7f..613accca08a 100644
--- a/chromium/media/base/localized_strings.cc
+++ b/chromium/media/base/localized_strings.cc
@@ -4,7 +4,6 @@
#include "media/base/localized_strings.h"
-#include "base/logging.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
diff --git a/chromium/media/base/mac/audio_latency_mac.cc b/chromium/media/base/mac/audio_latency_mac.cc
index a4e9bd59f03..3d1aa2fd638 100644
--- a/chromium/media/base/mac/audio_latency_mac.cc
+++ b/chromium/media/base/mac/audio_latency_mac.cc
@@ -3,7 +3,7 @@
// found in the LICENSE file.
#include "media/base/mac/audio_latency_mac.h"
-#include "base/logging.h"
+#include "base/check_op.h"
#include "media/base/limits.h"
namespace media {
diff --git a/chromium/media/base/media_client.cc b/chromium/media/base/media_client.cc
index 584ccc185fd..011f3b74137 100644
--- a/chromium/media/base/media_client.cc
+++ b/chromium/media/base/media_client.cc
@@ -4,7 +4,6 @@
#include "media/base/media_client.h"
-#include "base/logging.h"
namespace media {
diff --git a/chromium/media/base/media_log.cc b/chromium/media/base/media_log.cc
index babef2baa8a..6cb08ed64cb 100644
--- a/chromium/media/base/media_log.cc
+++ b/chromium/media/base/media_log.cc
@@ -24,8 +24,7 @@ static base::AtomicSequenceNumber g_media_log_count;
MediaLog::MediaLog() : MediaLog(new ParentLogRecord(this)) {}
MediaLog::MediaLog(scoped_refptr<ParentLogRecord> parent_log_record)
- : parent_log_record_(std::move(parent_log_record)),
- id_(g_media_log_count.GetNext()) {}
+ : parent_log_record_(std::move(parent_log_record)) {}
MediaLog::~MediaLog() {
// If we are the underlying log, then somebody should have called
@@ -39,8 +38,7 @@ MediaLog::~MediaLog() {
// We could get around this if we introduce a new NullMediaLog that handles
// log invalidation, so we could dcheck here. However, that seems like a lot
// of boilerplate.
- if (parent_log_record_->media_log == this)
- InvalidateLog();
+ InvalidateLog();
}
// Default *Locked implementations
@@ -65,6 +63,13 @@ void MediaLog::NotifyError(PipelineStatus status) {
AddLogRecord(std::move(record));
}
+void MediaLog::NotifyError(Status status) {
+ DCHECK(!status.is_ok());
+ std::string output_str;
+ base::JSONWriter::Write(MediaSerialize(status), &output_str);
+ AddMessage(MediaLogMessageLevel::kERROR, output_str);
+}
+
void MediaLog::OnWebMediaPlayerDestroyedLocked() {}
void MediaLog::OnWebMediaPlayerDestroyed() {
AddEvent<MediaLogEvent::kWebMediaPlayerDestroyed>();
@@ -98,7 +103,7 @@ void MediaLog::AddLogRecord(std::unique_ptr<MediaLogRecord> record) {
std::unique_ptr<MediaLogRecord> MediaLog::CreateRecord(
MediaLogRecord::Type type) {
auto record = std::make_unique<MediaLogRecord>();
- record->id = id_;
+ record->id = id();
record->type = type;
record->time = base::TimeTicks::Now();
return record;
@@ -106,15 +111,15 @@ std::unique_ptr<MediaLogRecord> MediaLog::CreateRecord(
void MediaLog::InvalidateLog() {
base::AutoLock auto_lock(parent_log_record_->lock);
- // It's almost certainly unintentional to invalidate a parent log.
- DCHECK(parent_log_record_->media_log == nullptr ||
- parent_log_record_->media_log == this);
-
- parent_log_record_->media_log = nullptr;
+ // Do nothing if this log didn't create the record, i.e.
+ // it's not the parent log. The parent log should invalidate itself.
+ if (parent_log_record_->media_log == this)
+ parent_log_record_->media_log = nullptr;
// Keep |parent_log_record_| around, since the lock must keep working.
}
-MediaLog::ParentLogRecord::ParentLogRecord(MediaLog* log) : media_log(log) {}
+MediaLog::ParentLogRecord::ParentLogRecord(MediaLog* log)
+ : id(g_media_log_count.GetNext()), media_log(log) {}
MediaLog::ParentLogRecord::~ParentLogRecord() = default;
LogHelper::LogHelper(MediaLogMessageLevel level, MediaLog* media_log)
diff --git a/chromium/media/base/media_log.h b/chromium/media/base/media_log.h
index 957181676fd..4812b87b119 100644
--- a/chromium/media/base/media_log.h
+++ b/chromium/media/base/media_log.h
@@ -23,7 +23,6 @@
#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"
@@ -81,6 +80,9 @@ class MEDIA_EXPORT MediaLog {
// TODO(tmathmeyer) replace with Status when that's ready.
void NotifyError(PipelineStatus status);
+ // Notify a non-ok Status. This method Should _not_ be given an OK status.
+ void NotifyError(Status status);
+
// Notify the media log that the player is destroyed. Some implementations
// will want to change event handling based on this.
void OnWebMediaPlayerDestroyed();
@@ -97,16 +99,17 @@ class MEDIA_EXPORT MediaLog {
// 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_; }
+ int32_t id() const { return parent_log_record_->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
- // log is closed by whoever owns it.
+ // still write to the same player's log. It is not guaranteed that this will
+ // log forever; it might start silently discarding log messages if the
+ // original log is closed by whoever owns it. However, it's safe to use it
+ // even if this occurs, in the "won't crash" sense.
virtual std::unique_ptr<MediaLog> Clone();
protected:
@@ -147,7 +150,10 @@ class MEDIA_EXPORT MediaLog {
void InvalidateLog();
struct ParentLogRecord : base::RefCountedThreadSafe<ParentLogRecord> {
- ParentLogRecord(MediaLog* log);
+ explicit ParentLogRecord(MediaLog* log);
+
+ // A unique (to this process) id for this MediaLog.
+ int32_t id;
// |lock_| protects the rest of this structure.
base::Lock lock;
@@ -162,15 +168,15 @@ class MEDIA_EXPORT MediaLog {
DISALLOW_COPY_AND_ASSIGN(ParentLogRecord);
};
- // Use |parent_log_record| instead of making a new one.
- MediaLog(scoped_refptr<ParentLogRecord> parent_log_record);
-
private:
// Allows MediaLogTest to construct MediaLog directly for testing.
friend class MediaLogTest;
FRIEND_TEST_ALL_PREFIXES(MediaLogTest, EventsAreForwarded);
FRIEND_TEST_ALL_PREFIXES(MediaLogTest, EventsAreNotForwardedAfterInvalidate);
+ // Use |parent_log_record| instead of making a new one.
+ explicit MediaLog(scoped_refptr<ParentLogRecord> parent_log_record);
+
// Helper methods to create events and their parameters.
std::unique_ptr<MediaLogRecord> CreateRecord(MediaLogRecord::Type type);
@@ -187,8 +193,6 @@ class MEDIA_EXPORT MediaLog {
// The underlying media log.
scoped_refptr<ParentLogRecord> parent_log_record_;
- // A unique (to this process) id for this MediaLog.
- int32_t id_;
DISALLOW_COPY_AND_ASSIGN(MediaLog);
};
diff --git a/chromium/media/base/media_log_events.cc b/chromium/media/base/media_log_events.cc
index 187ddcc9b5e..7ce6c22375c 100644
--- a/chromium/media/base/media_log_events.cc
+++ b/chromium/media/base/media_log_events.cc
@@ -6,7 +6,7 @@
#include <string>
-#include "base/logging.h"
+#include "base/notreached.h"
namespace media {
@@ -41,4 +41,13 @@ std::string MediaLogEventToString(MediaLogEvent level) {
return "";
}
+std::string TruncateUrlString(std::string url) {
+ constexpr size_t kMaxUrlLength = 1000;
+ if (url.length() > kMaxUrlLength) {
+ url.resize(kMaxUrlLength);
+ url.replace(url.end() - 3, url.end(), "...");
+ }
+ return url;
+}
+
} // namespace media
diff --git a/chromium/media/base/media_log_events.h b/chromium/media/base/media_log_events.h
index eed7a29725b..9cd26e5b128 100644
--- a/chromium/media/base/media_log_events.h
+++ b/chromium/media/base/media_log_events.h
@@ -67,6 +67,9 @@ enum class MediaLogEvent {
// instead of using macro stringification.
MEDIA_EXPORT std::string MediaLogEventToString(MediaLogEvent level);
+// Sometimes URLs can have encoded data that can be exteremly large.
+MEDIA_EXPORT std::string TruncateUrlString(std::string url);
+
// These events can be triggered with no extra associated data.
MEDIA_LOG_EVENT_TYPELESS(kPlay);
MEDIA_LOG_EVENT_TYPELESS(kPause);
@@ -77,12 +80,15 @@ 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");
+MEDIA_LOG_EVENT_NAMED_DATA_OP(kLoad, std::string, "url", TruncateUrlString);
+MEDIA_LOG_EVENT_NAMED_DATA_OP(kWebMediaPlayerCreated,
+ std::string,
+ "origin_url",
+ TruncateUrlString);
// Each type of buffering state gets a different name.
MEDIA_LOG_EVENT_NAMED_DATA(
diff --git a/chromium/media/base/media_log_message_levels.cc b/chromium/media/base/media_log_message_levels.cc
index bea3f563008..c39257c8a31 100644
--- a/chromium/media/base/media_log_message_levels.cc
+++ b/chromium/media/base/media_log_message_levels.cc
@@ -6,7 +6,7 @@
#include <string>
-#include "base/logging.h"
+#include "base/notreached.h"
namespace media {
diff --git a/chromium/media/base/media_log_properties.cc b/chromium/media/base/media_log_properties.cc
index d9ced3b410f..80d6a8e5c73 100644
--- a/chromium/media/base/media_log_properties.cc
+++ b/chromium/media/base/media_log_properties.cc
@@ -32,6 +32,8 @@ std::string MediaLogPropertyKeyToString(MediaLogProperty property) {
STRINGIFY(kIsPlatformAudioDecoder);
STRINGIFY(kAudioTracks);
STRINGIFY(kVideoTracks);
+ STRINGIFY(kFramerate);
+ STRINGIFY(kVideoPlaybackRoughness);
}
#undef STRINGIFY
}
diff --git a/chromium/media/base/media_log_properties.h b/chromium/media/base/media_log_properties.h
index f5283195094..d14b702c658 100644
--- a/chromium/media/base/media_log_properties.h
+++ b/chromium/media/base/media_log_properties.h
@@ -75,6 +75,13 @@ enum class MediaLogProperty {
// Track metadata.
kAudioTracks,
kVideoTracks,
+
+ // Effective video playback frame rate adjusted for the playback speed.
+ // Updated along with kVideoPlaybackRoughness (i.e. not very often)
+ kFramerate,
+
+ // A playback quality metric calculated by VideoPlaybackRoughnessReporter
+ kVideoPlaybackRoughness,
};
MEDIA_LOG_PROPERTY_SUPPORTS_TYPE(kResolution, gfx::Size);
@@ -97,6 +104,8 @@ MEDIA_LOG_PROPERTY_SUPPORTS_TYPE(kIsPlatformAudioDecoder, bool);
MEDIA_LOG_PROPERTY_SUPPORTS_TYPE(kIsAudioDecryptingDemuxerStream, bool);
MEDIA_LOG_PROPERTY_SUPPORTS_TYPE(kAudioTracks, std::vector<AudioDecoderConfig>);
MEDIA_LOG_PROPERTY_SUPPORTS_TYPE(kVideoTracks, std::vector<VideoDecoderConfig>);
+MEDIA_LOG_PROPERTY_SUPPORTS_TYPE(kFramerate, double);
+MEDIA_LOG_PROPERTY_SUPPORTS_TYPE(kVideoPlaybackRoughness, double);
// Convert the enum to a string (used for the front-end enum matching).
MEDIA_EXPORT std::string MediaLogPropertyKeyToString(MediaLogProperty property);
diff --git a/chromium/media/base/media_log_record.h b/chromium/media/base/media_log_record.h
index 76822114c9e..36125cc2cd0 100644
--- a/chromium/media/base/media_log_record.h
+++ b/chromium/media/base/media_log_record.h
@@ -13,6 +13,8 @@
namespace media {
+// TODO(tmathmeyer) refactor this so that we aren't packing type-erased data
+// with different meanings into "params".
struct MediaLogRecord {
MediaLogRecord() {}
diff --git a/chromium/media/base/media_log_type_enforcement.h b/chromium/media/base/media_log_type_enforcement.h
index 8404e00a23f..7624ce34490 100644
--- a/chromium/media/base/media_log_type_enforcement.h
+++ b/chromium/media/base/media_log_type_enforcement.h
@@ -46,6 +46,16 @@ struct MediaLogEventTypeSupport {};
static std::string TypeName() { return #EVENT; } \
}
+#define MEDIA_LOG_EVENT_NAMED_DATA_OP(EVENT, TYPE, DISPLAY, OP) \
+ template <> \
+ struct MediaLogEventTypeSupport<MediaLogEvent::EVENT, TYPE> { \
+ static void AddExtraData(base::Value* params, const TYPE& t) { \
+ DCHECK(params); \
+ params->SetKey(DISPLAY, MediaSerialize<TYPE>(OP(t))); \
+ } \
+ static std::string TypeName() { return #EVENT; } \
+ }
+
// Specifically do not create the Convert or DisplayName methods
#define MEDIA_LOG_EVENT_TYPELESS(EVENT) \
template <> \
diff --git a/chromium/media/base/media_log_unittest.cc b/chromium/media/base/media_log_unittest.cc
index 09a5126bca3..2a126bada33 100644
--- a/chromium/media/base/media_log_unittest.cc
+++ b/chromium/media/base/media_log_unittest.cc
@@ -16,15 +16,11 @@ namespace media {
// Friend class of MediaLog for access to internal constants.
class MediaLogTest : public testing::Test {
- public:
- static constexpr size_t kMaxUrlLength = MediaLog::kMaxUrlLength;
-
protected:
- MediaLog media_log;
-};
-
-constexpr size_t MediaLogTest::kMaxUrlLength;
+ std::unique_ptr<MockMediaLog> root_log_;
+ void CreateLog() { root_log_ = std::make_unique<MockMediaLog>(); }
+};
TEST_F(MediaLogTest, EventsAreForwarded) {
// Make sure that |root_log_| receives events.
@@ -44,4 +40,64 @@ TEST_F(MediaLogTest, EventsAreNotForwardedAfterInvalidate) {
child_media_log->AddMessage(MediaLogMessageLevel::kERROR, "test");
}
+TEST_F(MediaLogTest, ClonedLogsInhertParentPlayerId) {
+ std::unique_ptr<MockMediaLog> root_log(std::make_unique<MockMediaLog>());
+ std::unique_ptr<MediaLog> child_media_log(root_log->Clone());
+ EXPECT_CALL(*root_log, DoAddLogRecordLogString(_)).Times(1);
+ child_media_log->AddMessage(MediaLogMessageLevel::kERROR, "test");
+ auto event = root_log->take_most_recent_event();
+ EXPECT_NE(event, nullptr);
+ EXPECT_EQ(event->id, root_log->id());
+}
+
+TEST_F(MediaLogTest, DontTruncateShortUrlString) {
+ CreateLog();
+ EXPECT_CALL(*root_log_, DoAddLogRecordLogString(_)).Times(2);
+ const std::string short_url("chromium.org");
+
+ // Verify that LoadEvent does not truncate the short URL.
+ root_log_->AddEvent<MediaLogEvent::kLoad>(short_url);
+ auto event = root_log_->take_most_recent_event();
+ EXPECT_NE(event, nullptr);
+ EXPECT_EQ(*event->params.FindStringPath("url"), "chromium.org");
+
+ // Verify that CreatedEvent does not truncate the short URL.
+ root_log_->AddEvent<MediaLogEvent::kWebMediaPlayerCreated>(short_url);
+ event = root_log_->take_most_recent_event();
+ EXPECT_NE(event, nullptr);
+ EXPECT_EQ(*event->params.FindStringPath("origin_url"), "chromium.org");
+}
+
+TEST_F(MediaLogTest, TruncateLongUrlStrings) {
+ CreateLog();
+ EXPECT_CALL(*root_log_, DoAddLogRecordLogString(_)).Times(2);
+ // Build a long string that exceeds the URL length limit.
+ std::stringstream string_builder;
+ constexpr size_t kLongStringLength = 1010;
+ for (size_t i = 0; i < kLongStringLength; i++) {
+ string_builder << "c";
+ }
+ const std::string long_url = string_builder.str();
+
+ std::stringstream expected_string_builder;
+ constexpr size_t kMaxLength = 1000;
+ for (size_t i = 0; i < kMaxLength - 3; i++) {
+ expected_string_builder << "c";
+ }
+ expected_string_builder << "...";
+ const std::string expected_url = expected_string_builder.str();
+
+ // Verify that LoadEvent does not truncate the short URL.
+ root_log_->AddEvent<MediaLogEvent::kLoad>(long_url);
+ auto event = root_log_->take_most_recent_event();
+ EXPECT_NE(event, nullptr);
+ EXPECT_EQ(*event->params.FindStringPath("url"), expected_url);
+
+ // Verify that CreatedEvent does not truncate the short URL.
+ root_log_->AddEvent<MediaLogEvent::kWebMediaPlayerCreated>(long_url);
+ event = root_log_->take_most_recent_event();
+ EXPECT_NE(event, nullptr);
+ EXPECT_EQ(*event->params.FindStringPath("origin_url"), expected_url);
+}
+
} // namespace media
diff --git a/chromium/media/base/media_observer.h b/chromium/media/base/media_observer.h
index 225cc8d7985..eb0e3969c58 100644
--- a/chromium/media/base/media_observer.h
+++ b/chromium/media/base/media_observer.h
@@ -67,6 +67,9 @@ class MEDIA_EXPORT MediaObserver {
// Remote Playback API spec: https://w3c.github.io/remote-playback
virtual void OnRemotePlaybackDisabled(bool disabled) = 0;
+ // Called on Android, whenever we detect that we are playing back HLS.
+ virtual void OnHlsManifestDetected() = 0;
+
// Called when the media is playing/paused.
virtual void OnPlaying() = 0;
virtual void OnPaused() = 0;
diff --git a/chromium/media/base/media_serializers.h b/chromium/media/base/media_serializers.h
index 6333c44170f..832625da3ff 100644
--- a/chromium/media/base/media_serializers.h
+++ b/chromium/media/base/media_serializers.h
@@ -5,6 +5,7 @@
#ifndef MEDIA_BASE_MEDIA_SERIALIZERS_H_
#define MEDIA_BASE_MEDIA_SERIALIZERS_H_
+#include <sstream>
#include <vector>
#include "base/location.h"
diff --git a/chromium/media/base/media_switches.cc b/chromium/media/base/media_switches.cc
index 636dec1c063..e0d62061b43 100644
--- a/chromium/media/base/media_switches.cc
+++ b/chromium/media/base/media_switches.cc
@@ -29,9 +29,9 @@ const char kFailAudioStreamCreation[] = "fail-audio-stream-creation";
// Set number of threads to use for video decoding.
const char kVideoThreads[] = "video-threads";
-// Suspend media pipeline on background tabs.
-const char kEnableMediaSuspend[] = "enable-media-suspend";
-const char kDisableMediaSuspend[] = "disable-media-suspend";
+// Do not immediately suspend media in background tabs.
+const char kDisableBackgroundMediaSuspend[] =
+ "disable-background-media-suspend";
// Force to report VP9 as an unsupported MIME type.
const char kReportVp9AsAnUnsupportedMimeType[] =
@@ -76,9 +76,6 @@ const char kEnableProtectedVideoBuffers[] = "enable-protected-video-buffers";
const char kForceProtectedVideoOutputBuffers[] =
"force-protected-video-output-buffers";
-// Enables fuchsia.media.AudioConsumer to be used to render audio streams.
-const char kEnableFuchsiaAudioConsumer[] = "enable-fuchsia-audio-consumer";
-
#endif // defined(OS_FUCHSIA)
#if defined(USE_CRAS)
@@ -369,7 +366,14 @@ const base::Feature kGlobalMediaControlsOverlayControls{
// Show picture-in-picture button in Global Media Controls.
const base::Feature kGlobalMediaControlsPictureInPicture{
- "GlobalMediaControlsPictureInPicture", base::FEATURE_DISABLED_BY_DEFAULT};
+ "GlobalMediaControlsPictureInPicture",
+#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
+};
// Enable new cpu load estimator. Intended for evaluation in local
// testing and origin-trial.
@@ -382,6 +386,11 @@ const base::Feature kNewEncodeCpuLoadEstimator{
const base::Feature kSpecCompliantCanPlayThrough{
"SpecCompliantCanPlayThrough", base::FEATURE_ENABLED_BY_DEFAULT};
+// Disables the real audio output stream after silent audio has been delivered
+// for too long. Should save quite a bit of power in the muted video case.
+const base::Feature kSuspendMutedAudio{"SuspendMutedAudio",
+ base::FEATURE_DISABLED_BY_DEFAULT};
+
// Use shared block-based buffering for media.
const base::Feature kUseNewMediaCache{"use-new-media-cache",
base::FEATURE_ENABLED_BY_DEFAULT};
@@ -390,12 +399,6 @@ 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};
@@ -455,6 +458,9 @@ const base::Feature kFailUrlProvisionFetcherForTesting{
const base::Feature kHardwareSecureDecryption{
"HardwareSecureDecryption", base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kWakeLockOptimisationHiddenMuted{
+ "kWakeLockOptimisationHiddenMuted", base::FEATURE_ENABLED_BY_DEFAULT};
+
// Enables encrypted AV1 support in EME requestMediaKeySystemAccess() query by
// Widevine key system if it is also supported by the underlying Widevine CDM.
// This feature does not affect the actual playback of encrypted AV1 if it's
@@ -565,9 +571,20 @@ const base::Feature kUsePooledSharedImageVideoProvider{
const base::Feature kDelayCopyNV12Textures{"DelayCopyNV12Textures",
base::FEATURE_ENABLED_BY_DEFAULT};
-// Enables H264 HW encode acceleration using Media Foundation for Windows.
-const base::Feature kMediaFoundationH264Encoding{
- "MediaFoundationH264Encoding", base::FEATURE_ENABLED_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
+const base::Feature kDirectShowGetPhotoState{"DirectShowGetPhotoState",
+ base::FEATURE_ENABLED_BY_DEFAULT};
+
+// Enables asynchronous H264 HW encode acceleration using Media Foundation for
+// Windows.
+const base::Feature kMediaFoundationAsyncH264Encoding{
+ "MediaFoundationAsyncH264Encoding", base::FEATURE_DISABLED_BY_DEFAULT};
+
+// Enables AV1 decode acceleration for Windows.
+const base::Feature MEDIA_EXPORT kMediaFoundationAV1Decoding{
+ "MediaFoundationAV1Decoding", base::FEATURE_DISABLED_BY_DEFAULT};
// Enables MediaFoundation based video capture
const base::Feature kMediaFoundationVideoCapture{
@@ -577,12 +594,6 @@ const base::Feature kMediaFoundationVideoCapture{
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
-const base::Feature kDirectShowGetPhotoState{"DirectShowGetPhotoState",
- base::FEATURE_ENABLED_BY_DEFAULT};
-
#endif // defined(OS_WIN)
std::string GetEffectiveAutoplayPolicy(const base::CommandLine& command_line) {
@@ -644,11 +655,11 @@ const base::Feature kMediaFeeds{"MediaFeeds",
// Enables checking Media Feeds against safe search to prevent adult content.
const base::Feature kMediaFeedsSafeSearch{"MediaFeedsSafeSearch",
- base::FEATURE_DISABLED_BY_DEFAULT};
+ base::FEATURE_ENABLED_BY_DEFAULT};
// Send events to devtools rather than to chrome://media-internals
const base::Feature kMediaInspectorLogging{"MediaInspectorLogging",
- base::FEATURE_DISABLED_BY_DEFAULT};
+ base::FEATURE_ENABLED_BY_DEFAULT};
// Enables experimental local learning for media. Used in the context of media
// capabilities only. Adds reporting only; does not change media behavior.
diff --git a/chromium/media/base/media_switches.h b/chromium/media/base/media_switches.h
index dfe622f670c..5add050c996 100644
--- a/chromium/media/base/media_switches.h
+++ b/chromium/media/base/media_switches.h
@@ -31,9 +31,7 @@ MEDIA_EXPORT extern const char kFailAudioStreamCreation[];
MEDIA_EXPORT extern const char kVideoThreads[];
-// TODO(crbug.com/867146): remove these switches.
-MEDIA_EXPORT extern const char kEnableMediaSuspend[];
-MEDIA_EXPORT extern const char kDisableMediaSuspend[];
+MEDIA_EXPORT extern const char kDisableBackgroundMediaSuspend[];
MEDIA_EXPORT extern const char kReportVp9AsAnUnsupportedMimeType[];
@@ -52,7 +50,6 @@ MEDIA_EXPORT extern const char kWaveOutBuffers[];
#if defined(OS_FUCHSIA)
MEDIA_EXPORT extern const char kEnableProtectedVideoBuffers[];
MEDIA_EXPORT extern const char kForceProtectedVideoOutputBuffers[];
-MEDIA_EXPORT extern const char kEnableFuchsiaAudioConsumer[];
#endif
#if BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)
@@ -159,12 +156,12 @@ MEDIA_EXPORT extern const base::Feature kRecordMediaEngagementScores;
MEDIA_EXPORT extern const base::Feature kRecordWebAudioEngagement;
MEDIA_EXPORT extern const base::Feature kResumeBackgroundVideo;
MEDIA_EXPORT extern const base::Feature kRevokeMediaSourceObjectURLOnAttach;
+MEDIA_EXPORT extern const base::Feature kSuspendMutedAudio;
MEDIA_EXPORT extern const base::Feature kSpecCompliantCanPlayThrough;
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,6 +169,7 @@ MEDIA_EXPORT extern const base::Feature kVaapiLowPowerEncoder;
MEDIA_EXPORT extern const base::Feature kVaapiVP8Encoder;
MEDIA_EXPORT extern const base::Feature kVaapiVP9Encoder;
MEDIA_EXPORT extern const base::Feature kVideoBlitColorAccuracy;
+MEDIA_EXPORT extern const base::Feature kWakeLockOptimisationHiddenMuted;
MEDIA_EXPORT extern const base::Feature kWidevineAv1;
MEDIA_EXPORT extern const base::Feature kWidevineAv1ForceSupportForTesting;
@@ -195,10 +193,11 @@ MEDIA_EXPORT extern const base::Feature kUsePooledSharedImageVideoProvider;
#if defined(OS_WIN)
MEDIA_EXPORT extern const base::Feature kDelayCopyNV12Textures;
-MEDIA_EXPORT extern const base::Feature kMediaFoundationH264Encoding;
+MEDIA_EXPORT extern const base::Feature kDirectShowGetPhotoState;
+MEDIA_EXPORT extern const base::Feature kMediaFoundationAsyncH264Encoding;
+MEDIA_EXPORT extern const base::Feature kMediaFoundationAV1Decoding;
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)
// Based on a |command_line| and the current platform, returns the effective
diff --git a/chromium/media/base/media_types.h b/chromium/media/base/media_types.h
index c7f6f2a5d99..acffbce4486 100644
--- a/chromium/media/base/media_types.h
+++ b/chromium/media/base/media_types.h
@@ -33,6 +33,7 @@ struct MEDIA_EXPORT VideoType {
VideoCodecProfile profile;
int level;
VideoColorSpace color_space;
+ HdrMetadataType hdr_metadata_type;
};
MEDIA_EXPORT bool operator==(const AudioType& x, const AudioType& y);
diff --git a/chromium/media/base/media_url_demuxer.cc b/chromium/media/base/media_url_demuxer.cc
index 71e2764ac2c..ce7af50c5a8 100644
--- a/chromium/media/base/media_url_demuxer.cc
+++ b/chromium/media/base/media_url_demuxer.cc
@@ -82,6 +82,11 @@ int64_t MediaUrlDemuxer::GetMemoryUsage() const {
return 0;
}
+base::Optional<container_names::MediaContainerName>
+MediaUrlDemuxer::GetContainerForMetrics() const {
+ return base::nullopt;
+}
+
void MediaUrlDemuxer::OnEnabledAudioTracksChanged(
const std::vector<MediaTrack::Id>& track_ids,
base::TimeDelta curr_time,
diff --git a/chromium/media/base/media_url_demuxer.h b/chromium/media/base/media_url_demuxer.h
index ff0fe02a477..638cc9b14a7 100644
--- a/chromium/media/base/media_url_demuxer.h
+++ b/chromium/media/base/media_url_demuxer.h
@@ -58,6 +58,8 @@ class MEDIA_EXPORT MediaUrlDemuxer : public Demuxer {
base::TimeDelta GetStartTime() const override;
base::Time GetTimelineOffset() const override;
int64_t GetMemoryUsage() const override;
+ base::Optional<container_names::MediaContainerName> GetContainerForMetrics()
+ const override;
void OnEnabledAudioTracksChanged(const std::vector<MediaTrack::Id>& track_ids,
base::TimeDelta curr_time,
TrackChangeCB change_completed_cb) override;
diff --git a/chromium/media/base/mime_util_internal.cc b/chromium/media/base/mime_util_internal.cc
index 000325d0dd4..30347811de0 100644
--- a/chromium/media/base/mime_util_internal.cc
+++ b/chromium/media/base/mime_util_internal.cc
@@ -6,6 +6,7 @@
#include "base/command_line.h"
#include "base/feature_list.h"
+#include "base/logging.h"
#include "base/no_destructor.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
diff --git a/chromium/media/base/mock_filters.cc b/chromium/media/base/mock_filters.cc
index 85c3e6dd11f..e16cd6a65a9 100644
--- a/chromium/media/base/mock_filters.cc
+++ b/chromium/media/base/mock_filters.cc
@@ -4,7 +4,7 @@
#include "media/base/mock_filters.h"
-#include "base/logging.h"
+#include "base/check_op.h"
using ::testing::_;
using ::testing::NiceMock;
diff --git a/chromium/media/base/mock_filters.h b/chromium/media/base/mock_filters.h
index 36a270f7101..1d900919416 100644
--- a/chromium/media/base/mock_filters.h
+++ b/chromium/media/base/mock_filters.h
@@ -13,6 +13,7 @@
#include "base/callback.h"
#include "base/macros.h"
+#include "build/build_config.h"
#include "media/base/audio_decoder.h"
#include "media/base/audio_decoder_config.h"
#include "media/base/audio_parameters.h"
@@ -168,6 +169,8 @@ class MockDemuxer : public Demuxer {
MOCK_CONST_METHOD0(GetStartTime, base::TimeDelta());
MOCK_CONST_METHOD0(GetTimelineOffset, base::Time());
MOCK_CONST_METHOD0(GetMemoryUsage, int64_t());
+ MOCK_CONST_METHOD0(GetContainerForMetrics,
+ base::Optional<container_names::MediaContainerName>());
MOCK_METHOD3(OnEnabledAudioTracksChanged,
void(const std::vector<MediaTrack::Id>&,
base::TimeDelta,
@@ -523,6 +526,11 @@ class MockCdmContext : public CdmContext {
MOCK_METHOD0(GetDecryptor, Decryptor*());
MOCK_METHOD0(RequiresMediaFoundationRenderer, bool());
+#if defined(OS_WIN)
+ MOCK_METHOD1(GetMediaFoundationCdmProxy,
+ bool(GetMediaFoundationCdmProxyCB get_mf_cdm_proxy_cb));
+#endif
+
int GetCdmId() const override;
void set_cdm_id(int cdm_id);
diff --git a/chromium/media/base/mock_media_log.cc b/chromium/media/base/mock_media_log.cc
index 3ae29f64829..0d5729a4e38 100644
--- a/chromium/media/base/mock_media_log.cc
+++ b/chromium/media/base/mock_media_log.cc
@@ -15,6 +15,7 @@ void MockMediaLog::AddLogRecordLocked(std::unique_ptr<MediaLogRecord> event) {
const auto log_string = MediaEventToLogString(*event);
VLOG(2) << "MediaLog: " << log_string;
DoAddLogRecordLogString(log_string);
+ most_recent_event_ = std::move(event);
}
std::string MockMediaLog::MediaEventToLogString(const MediaLogRecord& event) {
diff --git a/chromium/media/base/mock_media_log.h b/chromium/media/base/mock_media_log.h
index 828960f0171..b568f7e1080 100644
--- a/chromium/media/base/mock_media_log.h
+++ b/chromium/media/base/mock_media_log.h
@@ -97,7 +97,15 @@ class MockMediaLog : public MediaLog {
static std::string MediaEventToLogString(const MediaLogRecord& event);
+ // Return whatever the last AddLogRecordLocked() was given.
+ // TODO(tmathmeyer): Remove this in favor of a mock, to allow EXPECT_CALL.
+ std::unique_ptr<MediaLogRecord> take_most_recent_event() {
+ return std::move(most_recent_event_);
+ }
+
private:
+ std::unique_ptr<MediaLogRecord> most_recent_event_;
+
DISALLOW_COPY_AND_ASSIGN(MockMediaLog);
};
diff --git a/chromium/media/base/multi_channel_resampler.cc b/chromium/media/base/multi_channel_resampler.cc
index ecc1e0b9574..f7e52a70278 100644
--- a/chromium/media/base/multi_channel_resampler.cc
+++ b/chromium/media/base/multi_channel_resampler.cc
@@ -9,7 +9,7 @@
#include "base/bind.h"
#include "base/bind_helpers.h"
-#include "base/logging.h"
+#include "base/check_op.h"
#include "media/base/audio_bus.h"
namespace media {
diff --git a/chromium/media/base/multi_channel_resampler_unittest.cc b/chromium/media/base/multi_channel_resampler_unittest.cc
index df0a8447e93..dae0c72a805 100644
--- a/chromium/media/base/multi_channel_resampler_unittest.cc
+++ b/chromium/media/base/multi_channel_resampler_unittest.cc
@@ -9,7 +9,6 @@
#include "base/bind.h"
#include "base/bind_helpers.h"
-#include "base/logging.h"
#include "base/macros.h"
#include "media/base/audio_bus.h"
#include "testing/gtest/include/gtest/gtest.h"
diff --git a/chromium/media/base/reentrancy_checker_unittest.cc b/chromium/media/base/reentrancy_checker_unittest.cc
index 7eff2e7fa65..0f0ae67882f 100644
--- a/chromium/media/base/reentrancy_checker_unittest.cc
+++ b/chromium/media/base/reentrancy_checker_unittest.cc
@@ -4,7 +4,7 @@
#include "media/base/reentrancy_checker.h"
-#include "base/logging.h"
+#include "base/check_op.h"
#include "base/test/gtest_util.h"
#include "testing/gtest/include/gtest/gtest.h"
diff --git a/chromium/media/base/sample_format.cc b/chromium/media/base/sample_format.cc
index 97492ccf418..591572eda5f 100644
--- a/chromium/media/base/sample_format.cc
+++ b/chromium/media/base/sample_format.cc
@@ -4,7 +4,9 @@
#include "media/base/sample_format.h"
-#include "base/logging.h"
+#include <ostream>
+
+#include "base/notreached.h"
namespace media {
diff --git a/chromium/media/base/sample_rates.cc b/chromium/media/base/sample_rates.cc
index 8c2fb931a8a..6af08622e9f 100644
--- a/chromium/media/base/sample_rates.cc
+++ b/chromium/media/base/sample_rates.cc
@@ -4,7 +4,7 @@
#include "media/base/sample_rates.h"
-#include "base/logging.h"
+#include "base/check.h"
namespace media {
diff --git a/chromium/media/base/scopedfd_helper.cc b/chromium/media/base/scopedfd_helper.cc
index e506cccc8e1..8dcf3dc1f16 100644
--- a/chromium/media/base/scopedfd_helper.cc
+++ b/chromium/media/base/scopedfd_helper.cc
@@ -6,7 +6,7 @@
#include <vector>
-#include "base/logging.h"
+#include "base/check.h"
#include "base/posix/eintr_wrapper.h"
#include "media/base/scopedfd_helper.h"
diff --git a/chromium/media/base/seekable_buffer.cc b/chromium/media/base/seekable_buffer.cc
index 1828920a62a..9e8faf6f1e6 100644
--- a/chromium/media/base/seekable_buffer.cc
+++ b/chromium/media/base/seekable_buffer.cc
@@ -6,7 +6,7 @@
#include <algorithm>
-#include "base/logging.h"
+#include "base/check_op.h"
#include "media/base/data_buffer.h"
#include "media/base/timestamp_constants.h"
diff --git a/chromium/media/base/seekable_buffer_unittest.cc b/chromium/media/base/seekable_buffer_unittest.cc
index e3895620650..9c3a60b585a 100644
--- a/chromium/media/base/seekable_buffer_unittest.cc
+++ b/chromium/media/base/seekable_buffer_unittest.cc
@@ -9,7 +9,6 @@
#include <cstdlib>
-#include "base/logging.h"
#include "base/stl_util.h"
#include "base/time/time.h"
#include "media/base/data_buffer.h"
diff --git a/chromium/media/base/silent_sink_suspender.cc b/chromium/media/base/silent_sink_suspender.cc
index abcb48cb726..42de9b7cd83 100644
--- a/chromium/media/base/silent_sink_suspender.cc
+++ b/chromium/media/base/silent_sink_suspender.cc
@@ -14,14 +14,14 @@ SilentSinkSuspender::SilentSinkSuspender(
AudioRendererSink::RenderCallback* callback,
base::TimeDelta silence_timeout,
const AudioParameters& params,
- const scoped_refptr<AudioRendererSink>& sink,
- const scoped_refptr<base::SingleThreadTaskRunner>& worker)
+ scoped_refptr<AudioRendererSink> sink,
+ scoped_refptr<base::SingleThreadTaskRunner> worker)
: callback_(callback),
params_(params),
- sink_(sink),
+ sink_(std::move(sink)),
task_runner_(base::ThreadTaskRunnerHandle::Get()),
silence_timeout_(silence_timeout),
- fake_sink_(worker, params_),
+ fake_sink_(std::move(worker), params_),
sink_transition_callback_(
base::BindRepeating(&SilentSinkSuspender::TransitionSinks,
base::Unretained(this))) {
@@ -58,11 +58,11 @@ int SilentSinkSuspender::Render(base::TimeDelta delay,
// |delay_timestamp| contains the value cached at
// |latest_output_delay_timestamp_|
// so we simulate the real sink output, promoting |delay_timestamp| with
- // |elapsedTime|.
+ // |elapsed_time|.
DCHECK_EQ(delay_timestamp, latest_output_delay_timestamp_);
- base::TimeDelta elapsedTime =
+ base::TimeDelta elapsed_time =
base::TimeTicks::Now() - fake_sink_transition_time_;
- delay_timestamp += elapsedTime;
+ delay_timestamp += elapsed_time;
// If we have no buffers or a transition is pending, one or more extra
// Render() calls have occurred in before TransitionSinks() can run, so we
@@ -84,7 +84,7 @@ int SilentSinkSuspender::Render(base::TimeDelta delay,
callback_->Render(delay, delay_timestamp, prior_frames_skipped, dest);
// Check for silence or real audio data and transition if necessary.
- if (!dest->AreFramesZero()) {
+ if (!dest->AreFramesZero() || !detect_silence_) {
first_silence_time_ = base::TimeTicks();
if (is_using_fake_sink_) {
is_transition_pending_ = true;
@@ -115,6 +115,52 @@ void SilentSinkSuspender::OnRenderError() {
callback_->OnRenderError();
}
+void SilentSinkSuspender::OnPaused() {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+
+ base::AutoLock al(transition_lock_);
+
+ // Nothing to do if we haven't touched the sink.
+ if (!is_using_fake_sink_ && !is_transition_pending_) {
+ first_silence_time_ = base::TimeTicks();
+ return;
+ }
+
+ // If we've moved over to the fake sink, we just need to stop it and cancel
+ // any pending transitions.
+ if (is_using_fake_sink_) {
+ is_using_fake_sink_ = false;
+ fake_sink_.Stop();
+ }
+
+ // Cancel any pending transitions.
+ is_transition_pending_ = false;
+ first_silence_time_ = base::TimeTicks();
+ sink_transition_callback_.Reset(base::BindRepeating(
+ &SilentSinkSuspender::TransitionSinks, base::Unretained(this)));
+}
+
+void SilentSinkSuspender::SetDetectSilence(bool detect_silence) {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+
+ bool should_transition_to_real_sink = false;
+ {
+ base::AutoLock lock(transition_lock_);
+ detect_silence_ = detect_silence;
+ should_transition_to_real_sink = !detect_silence_ && is_using_fake_sink_;
+
+ // If we haven't started the transition abort it.
+ if (is_transition_pending_) {
+ is_transition_pending_ = false;
+ sink_transition_callback_.Reset(base::BindRepeating(
+ &SilentSinkSuspender::TransitionSinks, base::Unretained(this)));
+ }
+ }
+
+ if (should_transition_to_real_sink)
+ TransitionSinks(/*use_fake_sink=*/false);
+}
+
bool SilentSinkSuspender::IsUsingFakeSinkForTesting() {
base::AutoLock al(transition_lock_);
return is_using_fake_sink_;
@@ -170,4 +216,4 @@ void SilentSinkSuspender::TransitionSinks(bool use_fake_sink) {
}
}
-} // namespace content
+} // namespace media
diff --git a/chromium/media/base/silent_sink_suspender.h b/chromium/media/base/silent_sink_suspender.h
index d9638651159..52fffadbfe4 100644
--- a/chromium/media/base/silent_sink_suspender.h
+++ b/chromium/media/base/silent_sink_suspender.h
@@ -29,7 +29,7 @@ namespace media {
// Helper class for suspending AudioRenderSink instances after silence has been
// detected for some time. When this is detected, the provided |sink_| is paused
// and a fake sink is injected to black hole the silent audio data and avoid
-// physical hardwasre usage. Note: The transition from real to fake audio output
+// physical hardware usage. Note: The transition from real to fake audio output
// and vice versa may result in some irregular Render() callbacks.
class MEDIA_EXPORT SilentSinkSuspender
: public AudioRendererSink::RenderCallback {
@@ -38,12 +38,11 @@ class MEDIA_EXPORT SilentSinkSuspender
// used to initialize |sink|, |sink| is the sink to monitor for idle, and
// |worker| is the task runner to run the fake Render() callbacks on. The
// amount of silence to allow before suspension is |silence_timeout|.
- SilentSinkSuspender(
- AudioRendererSink::RenderCallback* callback,
- base::TimeDelta silence_timeout,
- const AudioParameters& params,
- const scoped_refptr<AudioRendererSink>& sink,
- const scoped_refptr<base::SingleThreadTaskRunner>& worker);
+ SilentSinkSuspender(AudioRendererSink::RenderCallback* callback,
+ base::TimeDelta silence_timeout,
+ const AudioParameters& params,
+ scoped_refptr<AudioRendererSink> sink,
+ scoped_refptr<base::SingleThreadTaskRunner> worker);
~SilentSinkSuspender() override;
// AudioRendererSink::RenderCallback implementation.
@@ -53,6 +52,16 @@ class MEDIA_EXPORT SilentSinkSuspender
AudioBus* dest) override;
void OnRenderError() override;
+ // Cancels any outstanding callbacks and transitions. Subsequent playback will
+ // be through the real sink until the suspend conditions are met again.
+ void OnPaused();
+
+ // Enables or disables silence detection. If disabled, |silence_timeout| will
+ // be ignored and we will never transition to the fake sink. If we're already
+ // on the fake sink when SetDetectSilence(false) is called, we'll transition
+ // back to the real sink.
+ void SetDetectSilence(bool detect_silence);
+
bool IsUsingFakeSinkForTesting();
private:
@@ -95,6 +104,10 @@ class MEDIA_EXPORT SilentSinkSuspender
// only be used when |transition_lock_| is held or both sinks are stopped.
bool is_transition_pending_ GUARDED_BY(transition_lock_) = false;
+ // Whether we should detect silence and transition to the fake sink or not if
+ // |silence_timeout_| elapses.
+ bool detect_silence_ GUARDED_BY(transition_lock_) = true;
+
// Buffers accumulated during the transition from |fake_sink_| to |sink_|.
base::circular_deque<std::unique_ptr<AudioBus>> buffers_after_silence_;
@@ -105,15 +118,17 @@ class MEDIA_EXPORT SilentSinkSuspender
// Audio output delay at the moment when transition to |fake_sink_| starts.
base::TimeDelta latest_output_delay_;
+
// Audio output delay timestamp at the moment when transition to |fake_sink_|
// starts.
base::TimeTicks latest_output_delay_timestamp_;
+
// Time when transition to |fake_sink_| starts.
base::TimeTicks fake_sink_transition_time_;
DISALLOW_COPY_AND_ASSIGN(SilentSinkSuspender);
};
-} // namespace content
+} // namespace media
#endif // MEDIA_BASE_SILENT_SINK_SUSPENDER_H_
diff --git a/chromium/media/base/silent_sink_suspender_unittest.cc b/chromium/media/base/silent_sink_suspender_unittest.cc
index 3d8316bedfc..5ac51cc74a2 100644
--- a/chromium/media/base/silent_sink_suspender_unittest.cc
+++ b/chromium/media/base/silent_sink_suspender_unittest.cc
@@ -184,4 +184,120 @@ TEST_F(SilentSinkSuspenderTest, MultipleResume) {
0);
}
+TEST_F(SilentSinkSuspenderTest, SetDetectSilence) {
+ // Verify a normal Render() doesn't invoke suspend.
+ EXPECT_FALSE(suspender_.IsUsingFakeSinkForTesting());
+ temp_bus_->Zero();
+ EXPECT_EQ(temp_bus_->frames(),
+ suspender_.Render(base::TimeDelta(), base::TimeTicks(), 0,
+ temp_bus_.get()));
+ EXPECT_FALSE(temp_bus_->AreFramesZero());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_FALSE(suspender_.IsUsingFakeSinkForTesting());
+
+ // Mute all audio generated by the callback, this should suspend immediately.
+ fake_callback_.set_volume(0);
+ temp_bus_->Zero();
+ EXPECT_EQ(temp_bus_->frames(),
+ suspender_.Render(base::TimeDelta(), base::TimeTicks(), 0,
+ temp_bus_.get()));
+ EXPECT_TRUE(temp_bus_->AreFramesZero());
+ {
+ base::RunLoop run_loop;
+ EXPECT_CALL(*mock_sink_, Pause())
+ .WillOnce(RunClosure(run_loop.QuitClosure()));
+ run_loop.Run();
+ EXPECT_TRUE(suspender_.IsUsingFakeSinkForTesting());
+ }
+
+ // Disable silence detection, but don't unmute the audio, the FakeWorker
+ // inside |suspender_| should be running now, so we don't need to manually
+ // invoke Render().
+ {
+ base::RunLoop run_loop;
+ EXPECT_CALL(*mock_sink_, Play())
+ .WillOnce(RunClosure(run_loop.QuitClosure()));
+
+ // Disable silence detection which should put us back on the real sink.
+ suspender_.SetDetectSilence(false);
+
+ run_loop.Run();
+ EXPECT_FALSE(suspender_.IsUsingFakeSinkForTesting());
+ }
+
+ // Run one more Render() to ensure we stay on the real sink even w/ silent
+ // audio.
+ temp_bus_->Zero();
+ EXPECT_EQ(temp_bus_->frames(),
+ suspender_.Render(base::TimeDelta(), base::TimeTicks(), 0,
+ temp_bus_.get()));
+
+ base::RunLoop().RunUntilIdle();
+ EXPECT_FALSE(suspender_.IsUsingFakeSinkForTesting());
+
+ // Enable silence detection which should transition to the fake sink on the
+ // next Render().
+ suspender_.SetDetectSilence(true);
+ temp_bus_->Zero();
+ EXPECT_EQ(temp_bus_->frames(),
+ suspender_.Render(base::TimeDelta(), base::TimeTicks(), 0,
+ temp_bus_.get()));
+ EXPECT_TRUE(temp_bus_->AreFramesZero());
+ {
+ base::RunLoop run_loop;
+ EXPECT_CALL(*mock_sink_, Pause())
+ .WillOnce(RunClosure(run_loop.QuitClosure()));
+ run_loop.Run();
+ EXPECT_TRUE(suspender_.IsUsingFakeSinkForTesting());
+ }
+}
+
+TEST_F(SilentSinkSuspenderTest, OnPaused) {
+ // Verify a normal Render() doesn't invoke suspend.
+ EXPECT_FALSE(suspender_.IsUsingFakeSinkForTesting());
+ temp_bus_->Zero();
+ EXPECT_EQ(temp_bus_->frames(),
+ suspender_.Render(base::TimeDelta(), base::TimeTicks(), 0,
+ temp_bus_.get()));
+ EXPECT_FALSE(temp_bus_->AreFramesZero());
+ base::RunLoop().RunUntilIdle();
+ EXPECT_FALSE(suspender_.IsUsingFakeSinkForTesting());
+
+ // OnPaused() at this point should do nothing, since we're on the real sink.
+ suspender_.OnPaused();
+
+ // Mute all audio generated by the callback, this should suspend immediately.
+ fake_callback_.set_volume(0);
+ temp_bus_->Zero();
+ EXPECT_EQ(temp_bus_->frames(),
+ suspender_.Render(base::TimeDelta(), base::TimeTicks(), 0,
+ temp_bus_.get()));
+ EXPECT_TRUE(temp_bus_->AreFramesZero());
+ {
+ base::RunLoop run_loop;
+ EXPECT_CALL(*mock_sink_, Pause())
+ .WillOnce(RunClosure(run_loop.QuitClosure()));
+ run_loop.Run();
+ EXPECT_TRUE(suspender_.IsUsingFakeSinkForTesting());
+ }
+
+ // Call OnPaused(), which should disable the fake sink, but won't call Play().
+ suspender_.OnPaused();
+ base::RunLoop().RunUntilIdle();
+ EXPECT_FALSE(suspender_.IsUsingFakeSinkForTesting());
+
+ // Render silence again, which should attempt to transition to the fake sink.
+ temp_bus_->Zero();
+ EXPECT_EQ(temp_bus_->frames(),
+ suspender_.Render(base::TimeDelta(), base::TimeTicks(), 0,
+ temp_bus_.get()));
+
+ // OnPaused() should cancel any pending transitions.
+ suspender_.OnPaused();
+
+ // We should not transition to the fake sink now.
+ base::RunLoop().RunUntilIdle();
+ EXPECT_FALSE(suspender_.IsUsingFakeSinkForTesting());
+}
+
} // namespace media
diff --git a/chromium/media/base/sinc_resampler.cc b/chromium/media/base/sinc_resampler.cc
index d9965ca9ec2..79b5e2fbe12 100644
--- a/chromium/media/base/sinc_resampler.cc
+++ b/chromium/media/base/sinc_resampler.cc
@@ -77,7 +77,7 @@
#include <limits>
-#include "base/logging.h"
+#include "base/check_op.h"
#include "base/numerics/math_constants.h"
#include "build/build_config.h"
#include "cc/base/math_util.h"
diff --git a/chromium/media/base/status_codes.h b/chromium/media/base/status_codes.h
index 3f161c20316..468ff813946 100644
--- a/chromium/media/base/status_codes.h
+++ b/chromium/media/base/status_codes.h
@@ -44,11 +44,15 @@ enum class StatusCode : StatusCodeType {
// Windows Errors: 0x02
kWindowsWrappedHresult = 0x00000201,
kWindowsApiNotAvailible = 0x00000202,
+ kWindowsD3D11Error = 0x00000203,
// D3D11VideoDecoder Errors: 0x03
kCantMakeContextCurrent = 0x00000301,
kCantPostTexture = 0x00000302,
kCantPostAcquireStream = 0x00000303,
+ kCantCreateEglStream = 0x00000304,
+ kCantCreateEglStreamConsumer = 0x00000305,
+ kCantCreateEglStreamProducer = 0x00000306,
// MojoDecoder Errors: 0x04
kMojoDecoderNoWrappedDecoder = 0x00000401,
@@ -68,6 +72,15 @@ enum class StatusCode : StatusCodeType {
kVaapiReinitializedDuringDecode = 0x00000508,
kVaapiFailedAcceleratorCreation = 0x00000509,
+ // Encoder Error: 0x06
+ kEncoderInitializeNeverCompleted = 0x00000601,
+ kEncoderInitializeTwice = 0x00000602,
+ kEncoderFailedEncode = 0x00000603,
+ kEncoderUnsupportedProfile = 0x00000604,
+ kEncoderUnsupportedCodec = 0x00000605,
+ kEncoderUnsupportedConfig = 0x00000606,
+ kEncoderInitializationError = 0x00000607,
+
// Special codes
kGenericErrorPleaseRemove = 0x79999999,
kCodeOnlyForTesting = std::numeric_limits<StatusCodeType>::max(),
diff --git a/chromium/media/base/stream_parser_buffer.cc b/chromium/media/base/stream_parser_buffer.cc
index 5bff35f2ede..4ff451afb8d 100644
--- a/chromium/media/base/stream_parser_buffer.cc
+++ b/chromium/media/base/stream_parser_buffer.cc
@@ -6,7 +6,7 @@
#include <algorithm>
-#include "base/logging.h"
+#include "base/check_op.h"
#include "base/memory/ptr_util.h"
#include "media/base/timestamp_constants.h"
diff --git a/chromium/media/base/supported_types.cc b/chromium/media/base/supported_types.cc
index 19266cf96d8..c98ce24e557 100644
--- a/chromium/media/base/supported_types.cc
+++ b/chromium/media/base/supported_types.cc
@@ -5,6 +5,7 @@
#include "media/base/supported_types.h"
#include "base/feature_list.h"
+#include "base/logging.h"
#include "base/no_destructor.h"
#include "build/build_config.h"
#include "media/base/media.h"
@@ -31,6 +32,25 @@
namespace media {
+namespace {
+
+bool IsSupportedHdrMetadata(const HdrMetadataType& hdr_metadata_type) {
+ switch (hdr_metadata_type) {
+ case HdrMetadataType::kNone:
+ return true;
+
+ case HdrMetadataType::kSmpteSt2086:
+ case HdrMetadataType::kSmpteSt2094_10:
+ case HdrMetadataType::kSmpteSt2094_40:
+ return false;
+ }
+
+ NOTREACHED();
+ return false;
+}
+
+} // namespace
+
bool IsSupportedAudioType(const AudioType& type) {
MediaClient* media_client = GetMediaClient();
if (media_client)
@@ -182,6 +202,9 @@ bool IsAudioCodecProprietary(AudioCodec codec) {
case kUnknownAudioCodec:
return false;
}
+
+ NOTREACHED();
+ return false;
}
bool IsDefaultSupportedAudioType(const AudioType& type) {
@@ -252,11 +275,17 @@ bool IsVideoCodecProprietary(VideoCodec codec) {
case kCodecAV1:
return false;
}
+
+ NOTREACHED();
+ return false;
}
// TODO(chcunningham): Add platform specific logic for Android (move from
// MimeUtilIntenral).
bool IsDefaultSupportedVideoType(const VideoType& type) {
+ if (!IsSupportedHdrMetadata(type.hdr_metadata_type))
+ return false;
+
#if !BUILDFLAG(USE_PROPRIETARY_CODECS)
if (IsVideoCodecProprietary(type.codec))
return false;
diff --git a/chromium/media/base/supported_types_unittest.cc b/chromium/media/base/supported_types_unittest.cc
index 764561440d6..48013e18e31 100644
--- a/chromium/media/base/supported_types_unittest.cc
+++ b/chromium/media/base/supported_types_unittest.cc
@@ -240,4 +240,58 @@ TEST(SupportedTypesTest, XHE_AACSupportedOnAndroidOnly) {
#endif
}
+TEST(SupportedTypesTest, IsSupportedVideoTypeWithHdrMetadataBasics) {
+ // Default to common 709.
+ media::VideoColorSpace color_space = media::VideoColorSpace::REC709();
+
+ // Some codecs do not have a notion of level.
+ const int kUnspecifiedLevel = 0;
+
+ // Expect support for baseline configuration of known codecs.
+ EXPECT_TRUE(IsSupportedVideoType({media::kCodecVP8, media::VP8PROFILE_ANY,
+ kUnspecifiedLevel, color_space}));
+ EXPECT_TRUE(
+ IsSupportedVideoType({media::kCodecVP9, media::VP9PROFILE_PROFILE0,
+ kUnspecifiedLevel, color_space}));
+ EXPECT_TRUE(IsSupportedVideoType({media::kCodecTheora,
+ media::VIDEO_CODEC_PROFILE_UNKNOWN,
+ kUnspecifiedLevel, color_space}));
+
+ // All combinations of combinations of color gamuts and transfer functions
+ // should be supported.
+ color_space.primaries = media::VideoColorSpace::PrimaryID::SMPTEST431_2;
+ color_space.transfer = media::VideoColorSpace::TransferID::SMPTEST2084;
+ EXPECT_TRUE(IsSupportedVideoType({media::kCodecVP8, media::VP8PROFILE_ANY,
+ kUnspecifiedLevel, color_space}));
+ EXPECT_TRUE(
+ IsSupportedVideoType({media::kCodecVP9, media::VP9PROFILE_PROFILE0,
+ kUnspecifiedLevel, color_space}));
+ EXPECT_TRUE(IsSupportedVideoType({media::kCodecTheora,
+ media::VIDEO_CODEC_PROFILE_UNKNOWN,
+ kUnspecifiedLevel, color_space}));
+
+ color_space.primaries = media::VideoColorSpace::PrimaryID::BT2020;
+ color_space.transfer = media::VideoColorSpace::TransferID::ARIB_STD_B67;
+ EXPECT_TRUE(IsSupportedVideoType({media::kCodecVP8, media::VP8PROFILE_ANY,
+ kUnspecifiedLevel, color_space}));
+ EXPECT_TRUE(
+ IsSupportedVideoType({media::kCodecVP9, media::VP9PROFILE_PROFILE0,
+ kUnspecifiedLevel, color_space}));
+ EXPECT_TRUE(IsSupportedVideoType({media::kCodecTheora,
+ media::VIDEO_CODEC_PROFILE_UNKNOWN,
+ kUnspecifiedLevel, color_space}));
+
+ // No HDR metadata types are supported.
+ EXPECT_FALSE(IsSupportedVideoType({media::kCodecVP8, media::VP8PROFILE_ANY,
+ kUnspecifiedLevel, color_space,
+ media::HdrMetadataType::kSmpteSt2086}));
+
+ EXPECT_FALSE(IsSupportedVideoType({media::kCodecVP8, media::VP8PROFILE_ANY,
+ kUnspecifiedLevel, color_space,
+ media::HdrMetadataType::kSmpteSt2094_10}));
+
+ EXPECT_FALSE(IsSupportedVideoType({media::kCodecVP8, media::VP8PROFILE_ANY,
+ kUnspecifiedLevel, color_space,
+ media::HdrMetadataType::kSmpteSt2094_40}));
+}
} // namespace media
diff --git a/chromium/media/base/test_data_util.cc b/chromium/media/base/test_data_util.cc
index d849db56af1..fe6691abb84 100644
--- a/chromium/media/base/test_data_util.cc
+++ b/chromium/media/base/test_data_util.cc
@@ -6,9 +6,9 @@
#include <stdint.h>
+#include "base/check_op.h"
#include "base/containers/flat_map.h"
#include "base/files/file_util.h"
-#include "base/logging.h"
#include "base/no_destructor.h"
#include "base/numerics/safe_conversions.h"
#include "base/path_service.h"
diff --git a/chromium/media/base/test_helpers.cc b/chromium/media/base/test_helpers.cc
index c815328ee39..140bcba4fdf 100644
--- a/chromium/media/base/test_helpers.cc
+++ b/chromium/media/base/test_helpers.cc
@@ -7,8 +7,9 @@
#include <stdint.h>
#include "base/bind.h"
-#include "base/logging.h"
+#include "base/check_op.h"
#include "base/macros.h"
+#include "base/notreached.h"
#include "base/pickle.h"
#include "base/run_loop.h"
#include "base/test/test_timeouts.h"
diff --git a/chromium/media/base/test_helpers.h b/chromium/media/base/test_helpers.h
index 8e97cdc778d..bffe5960363 100644
--- a/chromium/media/base/test_helpers.h
+++ b/chromium/media/base/test_helpers.h
@@ -16,6 +16,7 @@
#include "base/strings/stringprintf.h"
#include "media/base/audio_parameters.h"
#include "media/base/channel_layout.h"
+#include "media/base/demuxer_stream.h"
#include "media/base/media_log.h"
#include "media/base/pipeline_status.h"
#include "media/base/sample_format.h"
diff --git a/chromium/media/base/text_ranges.cc b/chromium/media/base/text_ranges.cc
index a88cc90c076..c57dc65529e 100644
--- a/chromium/media/base/text_ranges.cc
+++ b/chromium/media/base/text_ranges.cc
@@ -4,7 +4,7 @@
#include "media/base/text_ranges.h"
-#include "base/logging.h"
+#include "base/check_op.h"
namespace media {
diff --git a/chromium/media/base/text_renderer.cc b/chromium/media/base/text_renderer.cc
index aa1e5a34d1e..06c18f6a9d6 100644
--- a/chromium/media/base/text_renderer.cc
+++ b/chromium/media/base/text_renderer.cc
@@ -10,7 +10,8 @@
#include "base/bind.h"
#include "base/callback_helpers.h"
-#include "base/logging.h"
+#include "base/check_op.h"
+#include "base/notreached.h"
#include "base/single_thread_task_runner.h"
#include "media/base/bind_to_current_loop.h"
#include "media/base/decoder_buffer.h"
diff --git a/chromium/media/base/time_delta_interpolator.cc b/chromium/media/base/time_delta_interpolator.cc
index cbfe38d8765..33e06a6f0be 100644
--- a/chromium/media/base/time_delta_interpolator.cc
+++ b/chromium/media/base/time_delta_interpolator.cc
@@ -8,7 +8,7 @@
#include <algorithm>
-#include "base/logging.h"
+#include "base/check.h"
#include "base/time/tick_clock.h"
#include "media/base/timestamp_constants.h"
diff --git a/chromium/media/base/time_delta_interpolator_unittest.cc b/chromium/media/base/time_delta_interpolator_unittest.cc
index 8f7122b46e0..540768ee45b 100644
--- a/chromium/media/base/time_delta_interpolator_unittest.cc
+++ b/chromium/media/base/time_delta_interpolator_unittest.cc
@@ -2,7 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "base/logging.h"
#include "base/test/simple_test_tick_clock.h"
#include "media/base/time_delta_interpolator.h"
#include "testing/gtest/include/gtest/gtest.h"
diff --git a/chromium/media/base/unaligned_shared_memory_unittest.cc b/chromium/media/base/unaligned_shared_memory_unittest.cc
index d9891a89509..8af5cf69715 100644
--- a/chromium/media/base/unaligned_shared_memory_unittest.cc
+++ b/chromium/media/base/unaligned_shared_memory_unittest.cc
@@ -9,7 +9,6 @@
#include <limits>
-#include "base/logging.h"
#include "base/stl_util.h"
#include "testing/gtest/include/gtest/gtest.h"
diff --git a/chromium/media/base/user_input_monitor_linux.cc b/chromium/media/base/user_input_monitor_linux.cc
index b52df992d79..00d818f1f68 100644
--- a/chromium/media/base/user_input_monitor_linux.cc
+++ b/chromium/media/base/user_input_monitor_linux.cc
@@ -120,21 +120,21 @@ uint32_t UserInputMonitorLinuxCore::GetKeyPressCount() const {
void UserInputMonitorLinuxCore::StartMonitor() {
DCHECK(io_task_runner_->BelongsToCurrentThread());
- // TODO(jamiewalch): We should pass the display in. At that point, since
- // XRecord needs a private connection to the X Server for its data channel
- // and both channels are used from a separate thread, we'll need to duplicate
- // them with something like the following:
- // XOpenDisplay(DisplayString(display));
- if (!x_control_display_)
- x_control_display_ = gfx::OpenNewXDisplay();
-
- if (!x_record_display_)
- x_record_display_ = gfx::OpenNewXDisplay();
-
if (!x_control_display_ || !x_record_display_) {
- LOG(ERROR) << "Couldn't open X display";
- StopMonitor();
- return;
+ // TODO(jamiewalch): We should pass the display in.
+ if (auto* x_display = gfx::GetXDisplay()) {
+ if (!x_control_display_)
+ x_control_display_ = gfx::CloneXDisplay(x_display);
+
+ if (!x_record_display_)
+ x_record_display_ = gfx::CloneXDisplay(x_display);
+ }
+
+ if (!x_control_display_ || !x_record_display_) {
+ LOG(ERROR) << "Couldn't open X display";
+ StopMonitor();
+ return;
+ }
}
int xr_opcode, xr_event, xr_error;
diff --git a/chromium/media/base/vector_math.cc b/chromium/media/base/vector_math.cc
index e6e26922249..fabb796d324 100644
--- a/chromium/media/base/vector_math.cc
+++ b/chromium/media/base/vector_math.cc
@@ -7,7 +7,7 @@
#include <algorithm>
-#include "base/logging.h"
+#include "base/check_op.h"
#include "build/build_config.h"
// NaCl does not allow intrinsics.
diff --git a/chromium/media/base/video_bitrate_allocation.cc b/chromium/media/base/video_bitrate_allocation.cc
index 8d270b866cb..d9a286dd649 100644
--- a/chromium/media/base/video_bitrate_allocation.cc
+++ b/chromium/media/base/video_bitrate_allocation.cc
@@ -4,10 +4,11 @@
#include "video_bitrate_allocation.h"
+#include <cstring>
#include <limits>
#include <numeric>
-#include "base/logging.h"
+#include "base/check_op.h"
#include "base/numerics/checked_math.h"
namespace media {
diff --git a/chromium/media/base/video_bitrate_allocation_unittest.cc b/chromium/media/base/video_bitrate_allocation_unittest.cc
index df8b3ae43ea..32dffd3cc2d 100644
--- a/chromium/media/base/video_bitrate_allocation_unittest.cc
+++ b/chromium/media/base/video_bitrate_allocation_unittest.cc
@@ -4,7 +4,6 @@
#include <set>
-#include "base/logging.h"
#include "media/base/video_bitrate_allocation.h"
#include "testing/gtest/include/gtest/gtest.h"
diff --git a/chromium/media/base/video_codecs_unittest.cc b/chromium/media/base/video_codecs_unittest.cc
index c6d9484e3c8..c5ac4c592ae 100644
--- a/chromium/media/base/video_codecs_unittest.cc
+++ b/chromium/media/base/video_codecs_unittest.cc
@@ -4,7 +4,6 @@
#include <set>
-#include "base/logging.h"
#include "base/strings/stringprintf.h"
#include "media/base/video_codecs.h"
#include "media/base/video_color_space.h"
diff --git a/chromium/media/base/video_color_space_unittest.cc b/chromium/media/base/video_color_space_unittest.cc
index c49d08bccb6..580fe15f5ed 100644
--- a/chromium/media/base/video_color_space_unittest.cc
+++ b/chromium/media/base/video_color_space_unittest.cc
@@ -3,7 +3,6 @@
// found in the LICENSE file.
#include "media/base/video_color_space.h"
-#include "base/logging.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/color_space.h"
#include "ui/gfx/color_transform.h"
diff --git a/chromium/media/base/video_decoder_config.cc b/chromium/media/base/video_decoder_config.cc
index e9c54664dfc..f59b7394a03 100644
--- a/chromium/media/base/video_decoder_config.cc
+++ b/chromium/media/base/video_decoder_config.cc
@@ -7,7 +7,8 @@
#include <iomanip>
#include <vector>
-#include "base/logging.h"
+#include "base/check_op.h"
+#include "base/notreached.h"
#include "base/strings/string_number_conversions.h"
#include "media/base/limits.h"
#include "media/base/media_util.h"
diff --git a/chromium/media/base/video_encoder.cc b/chromium/media/base/video_encoder.cc
new file mode 100644
index 00000000000..2be81f8e9b2
--- /dev/null
+++ b/chromium/media/base/video_encoder.cc
@@ -0,0 +1,21 @@
+// 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/video_encoder.h"
+
+#include "media/base/video_frame.h"
+
+namespace media {
+
+VideoEncoderOutput::VideoEncoderOutput() = default;
+VideoEncoderOutput::VideoEncoderOutput(VideoEncoderOutput&&) = default;
+VideoEncoderOutput::~VideoEncoderOutput() = default;
+
+VideoEncoder::VideoEncoder() = default;
+VideoEncoder::~VideoEncoder() = default;
+
+VideoEncoder::Options::Options() = default;
+VideoEncoder::Options::~Options() = default;
+
+} // namespace media \ No newline at end of file
diff --git a/chromium/media/base/video_encoder.h b/chromium/media/base/video_encoder.h
new file mode 100644
index 00000000000..90a15478122
--- /dev/null
+++ b/chromium/media/base/video_encoder.h
@@ -0,0 +1,107 @@
+// 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_VIDEO_ENCODER_H_
+#define MEDIA_BASE_VIDEO_ENCODER_H_
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/optional.h"
+#include "media/base/media_export.h"
+#include "media/base/status.h"
+#include "media/base/video_codecs.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace media {
+
+class VideoFrame;
+
+// Encoded video frame, its data and metadata.
+struct MEDIA_EXPORT VideoEncoderOutput {
+ VideoEncoderOutput();
+ VideoEncoderOutput(VideoEncoderOutput&&);
+ ~VideoEncoderOutput();
+
+ // Feel free take this buffer out and use underlying memory as is without
+ // copying.
+ std::unique_ptr<uint8_t[]> data;
+ size_t size;
+
+ base::TimeDelta timestamp;
+ bool key_frame;
+};
+
+class MEDIA_EXPORT VideoEncoder {
+ public:
+ struct MEDIA_EXPORT Options {
+ Options();
+ ~Options();
+ base::Optional<uint64_t> bitrate;
+ double framerate = 30.0;
+
+ int width = 0;
+ int height = 0;
+
+ base::Optional<int> threads;
+ base::Optional<int> keyframe_interval = 10000;
+ };
+
+ // Callback for VideoEncoder to report an encoded video frame whenever it
+ // becomes available.
+ using OutputCB = base::RepeatingCallback<void(VideoEncoderOutput output)>;
+
+ // Callback to report success and errors in encoder calls.
+ using StatusCB = base::OnceCallback<void(Status error)>;
+
+ VideoEncoder();
+ virtual ~VideoEncoder();
+
+ // Initializes a VideoEncoder with the given |options|, executing the
+ // |done_cb| upon completion. |output_cb| is called for each encoded frame
+ // produced by the coder.
+ //
+ // Note:
+ // 1) Can't be called more than once for the same instance of the encoder.
+ // 2) No VideoEncoder calls should be made before |done_cb| is executed.
+ virtual void Initialize(VideoCodecProfile profile,
+ const Options& options,
+ OutputCB output_cb,
+ StatusCB done_cb) = 0;
+
+ // Requests a |frame| to be encoded. The status of the encoder and the frame
+ // are returned via the provided callback |done_cb|.
+ //
+ // |done_cb| will not be called from within this method, and that it will be
+ // called even if Encode() is never called again.
+
+ // After the frame, or several frames, are encoded the encoder calls
+ // |output_cb| specified in Initialize() for available VideoEncoderOutput.
+ // |output_cb| may be called before or after |done_cb|,
+ // including before Encode() returns.
+ // Encode() does not expect EOS frames, use Flush() to finalize the stream
+ // and harvest the outputs.
+ virtual void Encode(scoped_refptr<const VideoFrame> frame,
+ bool key_frame,
+ StatusCB done_cb) = 0;
+
+ // Adjust encoder options for future frames, executing the
+ // |done_cb| upon completion.
+ //
+ // Note:
+ // 1. Not all options can be changed on the fly.
+ // 2. ChangeOptions() should be called after calling Flush() and waiting
+ // for it to finish.
+ virtual void ChangeOptions(const Options& options, StatusCB done_cb) = 0;
+
+ // Requests all outputs for already encoded frames to be
+ // produced via |output_cb| and calls |dene_cb| after that.
+ virtual void Flush(StatusCB done_cb) = 0;
+
+ protected:
+ DISALLOW_COPY_AND_ASSIGN(VideoEncoder);
+};
+
+} // namespace media
+
+#endif // MEDIA_BASE_VIDEO_ENCODER_H_
diff --git a/chromium/media/base/video_frame.h b/chromium/media/base/video_frame.h
index 6b1a5b455a7..271fcdf19fd 100644
--- a/chromium/media/base/video_frame.h
+++ b/chromium/media/base/video_frame.h
@@ -27,6 +27,7 @@
#include "build/build_config.h"
#include "gpu/command_buffer/common/mailbox_holder.h"
#include "gpu/ipc/common/vulkan_ycbcr_info.h"
+#include "media/base/hdr_metadata.h"
#include "media/base/video_frame_layout.h"
#include "media/base/video_frame_metadata.h"
#include "media/base/video_types.h"
@@ -417,6 +418,14 @@ class MEDIA_EXPORT VideoFrame : public base::RefCountedThreadSafe<VideoFrame> {
color_space_ = color_space;
}
+ const base::Optional<HDRMetadata>& hdr_metadata() const {
+ return hdr_metadata_;
+ }
+
+ void set_hdr_metadata(const base::Optional<HDRMetadata>& hdr_metadata) {
+ hdr_metadata_ = hdr_metadata;
+ }
+
const VideoFrameLayout& layout() const { return layout_; }
VideoPixelFormat format() const { return layout_.format(); }
@@ -461,7 +470,7 @@ class MEDIA_EXPORT VideoFrame : public base::RefCountedThreadSafe<VideoFrame> {
}
const base::Optional<gpu::VulkanYCbCrInfo>& ycbcr_info() const {
- return ycbcr_info_;
+ return wrapped_frame_ ? wrapped_frame_->ycbcr_info() : ycbcr_info_;
}
// Returns pointer to the data in the visible region of the frame, for
@@ -686,6 +695,7 @@ class MEDIA_EXPORT VideoFrame : public base::RefCountedThreadSafe<VideoFrame> {
const int unique_id_;
gfx::ColorSpace color_space_;
+ base::Optional<HDRMetadata> hdr_metadata_;
// Sampler conversion information which is used in vulkan context for android.
base::Optional<gpu::VulkanYCbCrInfo> ycbcr_info_;
diff --git a/chromium/media/base/video_frame_layout.cc b/chromium/media/base/video_frame_layout.cc
index b7210ce1126..9ded967aecc 100644
--- a/chromium/media/base/video_frame_layout.cc
+++ b/chromium/media/base/video_frame_layout.cc
@@ -8,7 +8,7 @@
#include <numeric>
#include <sstream>
-#include "base/logging.h"
+#include "base/notreached.h"
namespace media {
diff --git a/chromium/media/base/video_frame_metadata.cc b/chromium/media/base/video_frame_metadata.cc
index 1b403379bd0..ad45ea010c9 100644
--- a/chromium/media/base/video_frame_metadata.cc
+++ b/chromium/media/base/video_frame_metadata.cc
@@ -6,8 +6,10 @@
#include <stdint.h>
#include <utility>
+#include <vector>
-#include "base/logging.h"
+#include "base/check_op.h"
+#include "base/no_destructor.h"
#include "base/strings/string_number_conversions.h"
#include "base/value_conversions.h"
#include "ui/gfx/geometry/rect.h"
@@ -16,10 +18,19 @@ namespace media {
namespace {
-// Map enum key to internal std::string key used by base::DictionaryValue.
-inline std::string ToInternalKey(VideoFrameMetadata::Key key) {
+std::vector<std::string> CreateInternalKeys() {
+ std::vector<std::string> result(VideoFrameMetadata::NUM_KEYS);
+ for (size_t i = 0; i < result.size(); i++)
+ result[i] = base::NumberToString(i);
+ return result;
+}
+
+// Map enum key to internal StringPiece key used by base::DictionaryValue.
+inline base::StringPiece ToInternalKey(VideoFrameMetadata::Key key) {
DCHECK_LT(key, VideoFrameMetadata::NUM_KEYS);
- return base::NumberToString(static_cast<int>(key));
+ static const base::NoDestructor<std::vector<std::string>> internal_keys(
+ CreateInternalKeys());
+ return (*internal_keys)[int{key}];
}
} // namespace
diff --git a/chromium/media/base/video_transformation.cc b/chromium/media/base/video_transformation.cc
index 4ba3658ffae..8b107b940db 100644
--- a/chromium/media/base/video_transformation.cc
+++ b/chromium/media/base/video_transformation.cc
@@ -7,7 +7,7 @@
#include <math.h>
#include <stddef.h>
-#include "base/logging.h"
+#include "base/notreached.h"
namespace media {
namespace {
diff --git a/chromium/media/base/video_types.cc b/chromium/media/base/video_types.cc
index 6f23ea771a0..6814a364db1 100644
--- a/chromium/media/base/video_types.cc
+++ b/chromium/media/base/video_types.cc
@@ -4,7 +4,9 @@
#include "media/base/video_types.h"
-#include "base/logging.h"
+#include <ostream>
+
+#include "base/notreached.h"
#include "base/strings/stringprintf.h"
namespace media {
diff --git a/chromium/media/base/video_util.cc b/chromium/media/base/video_util.cc
index bc4c55619d3..3daa1cb27d5 100644
--- a/chromium/media/base/video_util.cc
+++ b/chromium/media/base/video_util.cc
@@ -7,7 +7,8 @@
#include <cmath>
#include "base/bind.h"
-#include "base/logging.h"
+#include "base/check_op.h"
+#include "base/notreached.h"
#include "base/numerics/safe_conversions.h"
#include "base/numerics/safe_math.h"
#include "media/base/video_frame.h"
diff --git a/chromium/media/base/watch_time_keys.cc b/chromium/media/base/watch_time_keys.cc
index 90f301cb55e..c0480b51303 100644
--- a/chromium/media/base/watch_time_keys.cc
+++ b/chromium/media/base/watch_time_keys.cc
@@ -4,6 +4,8 @@
#include "media/base/watch_time_keys.h"
+#include "base/notreached.h"
+
namespace media {
// TODO(dalecurtis): Key strings aren't really necessary anymore, so instead
diff --git a/chromium/media/base/win/BUILD.gn b/chromium/media/base/win/BUILD.gn
index c4d20d2fed4..b59888ffc6c 100644
--- a/chromium/media/base/win/BUILD.gn
+++ b/chromium/media/base/win/BUILD.gn
@@ -49,6 +49,14 @@ source_set("d3d11") {
deps = [ "//base" ]
}
+source_set("hresult_status_helper") {
+ sources = [
+ "hresult_status_helper.cc",
+ "hresult_status_helper.h",
+ ]
+ deps = [ "//media" ]
+}
+
source_set("d3d11_test_support") {
testonly = true
sources = [
diff --git a/chromium/media/base/win/hresult_status_helper.cc b/chromium/media/base/win/hresult_status_helper.cc
new file mode 100644
index 00000000000..e3d6a43ebc0
--- /dev/null
+++ b/chromium/media/base/win/hresult_status_helper.cc
@@ -0,0 +1,22 @@
+// 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/win/hresult_status_helper.h"
+
+#include "base/logging.h"
+
+namespace media {
+
+Status HresultToStatus(HRESULT hresult,
+ const char* message,
+ StatusCode code,
+ const base::Location& location) {
+ if (SUCCEEDED(hresult))
+ return OkStatus();
+
+ return Status(code, message == nullptr ? "HRESULT" : message, location)
+ .WithData("value", logging::SystemErrorCodeToString(hresult));
+}
+
+} // namespace media
diff --git a/chromium/media/base/win/hresult_status_helper.h b/chromium/media/base/win/hresult_status_helper.h
new file mode 100644
index 00000000000..67ffe3f8943
--- /dev/null
+++ b/chromium/media/base/win/hresult_status_helper.h
@@ -0,0 +1,25 @@
+// 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_WIN_HRESULT_STATUS_HELPER_H_
+#define MEDIA_BASE_WIN_HRESULT_STATUS_HELPER_H_
+
+#include <wrl/client.h>
+
+#include "media/base/status.h"
+
+namespace media {
+
+// Generate a status from an HRESULT. If message is provided, it will add the
+// HRESULT hex value as a data value to the status. Otherwise, the hex value
+// will be included in the error message itself.
+Status HresultToStatus(
+ HRESULT hresult,
+ const char* message = nullptr,
+ StatusCode code = StatusCode::kWindowsWrappedHresult,
+ const base::Location& location = base::Location::Current());
+
+} // namespace media
+
+#endif // MEDIA_BASE_WIN_HRESULT_STATUS_HELPER_H_
diff --git a/chromium/media/base/win/mf_helpers.h b/chromium/media/base/win/mf_helpers.h
index ac163084247..11c21881392 100644
--- a/chromium/media/base/win/mf_helpers.h
+++ b/chromium/media/base/win/mf_helpers.h
@@ -15,6 +15,12 @@
namespace media {
+// Helper function to print HRESULT to std::string.
+const auto PrintHr = logging::SystemErrorCodeToString;
+
+// Helper macro for DVLOG with function name and this pointer.
+#define DVLOG_FUNC(level) DVLOG(level) << __func__ << ": (" << this << ") "
+
// 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.
@@ -25,8 +31,8 @@ namespace media {
do { \
HRESULT hresult = (expr); \
if (FAILED(hresult)) { \
- DLOG(ERROR) << __func__ << ": failed with \"" \
- << logging::SystemErrorCodeToString(hresult) << "\""; \
+ DLOG(ERROR) << __func__ << ": failed with \"" << PrintHr(hresult) \
+ << "\""; \
return hresult; \
} \
} while (0)
@@ -39,10 +45,8 @@ namespace media {
} \
} while (0)
-#define RETURN_ON_HR_FAILURE(hresult, log, ret) \
- RETURN_ON_FAILURE(SUCCEEDED(hresult), \
- log << ", " << logging::SystemErrorCodeToString(hresult), \
- ret);
+#define RETURN_ON_HR_FAILURE(hresult, log, ret) \
+ RETURN_ON_FAILURE(SUCCEEDED(hresult), log << ", " << PrintHr(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.
@@ -53,7 +57,7 @@ CreateEmptySampleWithBuffer(uint32_t buffer_length, int align);
// instance.
class MF_INITIALIZER_EXPORT MediaBufferScopedPointer {
public:
- MediaBufferScopedPointer(IMFMediaBuffer* media_buffer);
+ explicit MediaBufferScopedPointer(IMFMediaBuffer* media_buffer);
~MediaBufferScopedPointer();
uint8_t* get() { return buffer_; }
@@ -72,7 +76,6 @@ class MF_INITIALIZER_EXPORT MediaBufferScopedPointer {
class MF_INITIALIZER_EXPORT DXGIDeviceScopedHandle {
public:
explicit DXGIDeviceScopedHandle(IMFDXGIDeviceManager* device_manager);
-
~DXGIDeviceScopedHandle();
HRESULT LockDevice(REFIID riid, void** device_out);
diff --git a/chromium/media/base/win/mf_initializer.h b/chromium/media/base/win/mf_initializer.h
index f1e1bd06a6c..2f8431b65bc 100644
--- a/chromium/media/base/win/mf_initializer.h
+++ b/chromium/media/base/win/mf_initializer.h
@@ -7,6 +7,8 @@
#include <mfapi.h>
+#include <memory>
+
#include "base/logging.h"
#include "media/base/win/mf_initializer_export.h"
diff --git a/chromium/media/blink/cdm_result_promise_helper.cc b/chromium/media/blink/cdm_result_promise_helper.cc
index a7999788c98..4e00f2bda04 100644
--- a/chromium/media/blink/cdm_result_promise_helper.cc
+++ b/chromium/media/blink/cdm_result_promise_helper.cc
@@ -4,8 +4,8 @@
#include "media/blink/cdm_result_promise_helper.h"
-#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
+#include "base/notreached.h"
namespace media {
diff --git a/chromium/media/blink/interval_map_unittest.cc b/chromium/media/blink/interval_map_unittest.cc
index 2d10e2cdbce..fc53afd8ca5 100644
--- a/chromium/media/blink/interval_map_unittest.cc
+++ b/chromium/media/blink/interval_map_unittest.cc
@@ -6,6 +6,7 @@
#include <string>
+#include "base/logging.h"
#include "base/strings/stringprintf.h"
#include "media/base/test_random.h"
#include "media/blink/interval_map.h"
diff --git a/chromium/media/blink/run_all_unittests.cc b/chromium/media/blink/run_all_unittests.cc
index fddb3fed104..fa3e4ed6084 100644
--- a/chromium/media/blink/run_all_unittests.cc
+++ b/chromium/media/blink/run_all_unittests.cc
@@ -10,6 +10,7 @@
#include "media/base/media.h"
#include "media/blink/blink_platform_with_task_environment.h"
#include "mojo/public/cpp/bindings/binder_map.h"
+#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
#include "third_party/blink/public/web/blink.h"
@@ -60,6 +61,7 @@ class MediaBlinkTestSuite : public base::TestSuite {
mojo::core::Init();
#endif
+ blink::Platform::InitializeBlink();
platform_ = std::make_unique<BlinkPlatformWithTaskEnvironment>();
mojo::BinderMap binders;
diff --git a/chromium/media/blink/url_index_unittest.cc b/chromium/media/blink/url_index_unittest.cc
index 1dc131a2348..0e4992c808d 100644
--- a/chromium/media/blink/url_index_unittest.cc
+++ b/chromium/media/blink/url_index_unittest.cc
@@ -7,7 +7,6 @@
#include <list>
#include <string>
-#include "base/logging.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
diff --git a/chromium/media/blink/video_frame_compositor.cc b/chromium/media/blink/video_frame_compositor.cc
index 43a4c98bf17..2b3816c8c42 100644
--- a/chromium/media/blink/video_frame_compositor.cc
+++ b/chromium/media/blink/video_frame_compositor.cc
@@ -20,6 +20,7 @@ namespace media {
// Amount of time to wait between UpdateCurrentFrame() callbacks before starting
// background rendering to keep the Render() callbacks moving.
const int kBackgroundRenderingTimeoutMs = 250;
+const int kForceBeginFramesTimeoutMs = 1000;
// static
constexpr const char VideoFrameCompositor::kTracingCategory[];
@@ -34,6 +35,11 @@ VideoFrameCompositor::VideoFrameCompositor(
base::TimeDelta::FromMilliseconds(kBackgroundRenderingTimeoutMs),
base::Bind(&VideoFrameCompositor::BackgroundRender,
base::Unretained(this))),
+ force_begin_frames_timer_(
+ FROM_HERE,
+ base::TimeDelta::FromMilliseconds(kForceBeginFramesTimeoutMs),
+ base::Bind(&VideoFrameCompositor::StopForceBeginFrames,
+ base::Unretained(this))),
submitter_(std::move(submitter)) {
if (submitter_) {
task_runner_->PostTask(
@@ -58,7 +64,7 @@ void VideoFrameCompositor::SetIsSurfaceVisible(bool is_visible) {
void VideoFrameCompositor::InitializeSubmitter() {
DCHECK(task_runner_->BelongsToCurrentThread());
- submitter_->Initialize(this);
+ submitter_->Initialize(this, /* is_media_stream = */ false);
}
VideoFrameCompositor::~VideoFrameCompositor() {
@@ -276,6 +282,24 @@ void VideoFrameCompositor::SetOnFramePresentedCallback(
OnNewFramePresentedCB present_cb) {
base::AutoLock lock(current_frame_lock_);
new_presented_frame_cb_ = std::move(present_cb);
+
+ task_runner_->PostTask(
+ FROM_HERE, base::BindOnce(&VideoFrameCompositor::StartForceBeginFrames,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void VideoFrameCompositor::StartForceBeginFrames() {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+ if (!submitter_)
+ return;
+
+ submitter_->SetForceBeginFrames(true);
+ force_begin_frames_timer_.Reset();
+}
+
+void VideoFrameCompositor::StopForceBeginFrames() {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+ submitter_->SetForceBeginFrames(false);
}
std::unique_ptr<blink::WebMediaPlayer::VideoFramePresentationMetadata>
diff --git a/chromium/media/blink/video_frame_compositor.h b/chromium/media/blink/video_frame_compositor.h
index 5f2aabe1e74..fc8456053bd 100644
--- a/chromium/media/blink/video_frame_compositor.h
+++ b/chromium/media/blink/video_frame_compositor.h
@@ -189,6 +189,17 @@ class MEDIA_BLINK_EXPORT VideoFrameCompositor : public VideoRendererSink,
void SetCurrentFrame_Locked(scoped_refptr<VideoFrame> frame,
base::TimeTicks expected_display_time);
+ // Sets the ForceBeginFrames flag on |submitter_|, and resets
+ // |force_begin_frames_timer_|.
+ //
+ // The flag is used to keep receiving BeginFrame()/UpdateCurrentFrame() calls
+ // even if the video element is not visible, so websites can still use the
+ // requestVideoFrameCallback() API when the video is offscreen.
+ void StartForceBeginFrames();
+
+ // Called from |force_begin_frames_timer_| to unset the flag on |submitter_|.
+ void StopForceBeginFrames();
+
// Called by |background_rendering_timer_| when enough time elapses where we
// haven't seen a Render() call.
void BackgroundRender();
@@ -220,6 +231,10 @@ class MEDIA_BLINK_EXPORT VideoFrameCompositor : public VideoRendererSink,
// after each successful UpdateCurrentFrame() call.
base::RetainingOneShotTimer background_rendering_timer_;
+ // Calls StopForceBeginFrames() once we stop receiving calls to
+ // requestVideoFrameCallback() (or SetOnFramePresentedCallback() in our case).
+ base::RetainingOneShotTimer force_begin_frames_timer_;
+
// These values are only set and read on the compositor thread.
cc::VideoFrameProvider::Client* client_ = nullptr;
bool rendering_ = false;
diff --git a/chromium/media/blink/video_frame_compositor_unittest.cc b/chromium/media/blink/video_frame_compositor_unittest.cc
index 3b147d1bfde..70dff67751a 100644
--- a/chromium/media/blink/video_frame_compositor_unittest.cc
+++ b/chromium/media/blink/video_frame_compositor_unittest.cc
@@ -34,11 +34,12 @@ class MockWebVideoFrameSubmitter : public blink::WebVideoFrameSubmitter {
MOCK_METHOD0(StartRendering, void());
MOCK_METHOD0(StopRendering, void());
MOCK_CONST_METHOD0(IsDrivingFrameUpdates, bool(void));
- MOCK_METHOD1(Initialize, void(cc::VideoFrameProvider*));
+ MOCK_METHOD2(Initialize, void(cc::VideoFrameProvider*, bool));
MOCK_METHOD1(SetRotation, void(media::VideoRotation));
MOCK_METHOD1(SetIsSurfaceVisible, void(bool));
MOCK_METHOD1(SetIsPageVisible, void(bool));
MOCK_METHOD1(SetForceSubmit, void(bool));
+ MOCK_METHOD1(SetForceBeginFrames, void(bool));
void DidReceiveFrame() override { ++did_receive_frame_count_; }
int did_receive_frame_count() { return did_receive_frame_count_; }
@@ -69,7 +70,7 @@ class VideoFrameCompositorTest : public VideoRendererSink::RenderCallback,
base::ThreadTaskRunnerHandle::Get(), nullptr);
compositor_->SetVideoFrameProviderClient(client_.get());
} else {
- EXPECT_CALL(*submitter_, Initialize(_));
+ EXPECT_CALL(*submitter_, Initialize(_, _));
compositor_ = std::make_unique<VideoFrameCompositor>(
base::ThreadTaskRunnerHandle::Get(), std::move(client_));
base::RunLoop().RunUntilIdle();
@@ -200,7 +201,7 @@ TEST_P(VideoFrameCompositorTest, PaintSingleFrame) {
EXPECT_EQ(1, submitter_->did_receive_frame_count());
}
-TEST_P(VideoFrameCompositorTest, RenderFiresPrensentationCallback) {
+TEST_P(VideoFrameCompositorTest, RenderFiresPresentationCallback) {
// Advance the clock so we can differentiate between base::TimeTicks::Now()
// and base::TimeTicks().
tick_clock_.Advance(base::TimeDelta::FromSeconds(1));
@@ -208,6 +209,7 @@ TEST_P(VideoFrameCompositorTest, RenderFiresPrensentationCallback) {
scoped_refptr<VideoFrame> opaque_frame = CreateOpaqueFrame();
EXPECT_CALL(*this, Render(_, _, true)).WillRepeatedly(Return(opaque_frame));
EXPECT_CALL(*this, OnNewFramePresented());
+ EXPECT_CALL(*submitter_, SetForceBeginFrames(true)).Times(AnyNumber());
compositor()->SetOnFramePresentedCallback(GetNewFramePresentedCB());
StartVideoRendererSink();
StopVideoRendererSink(true);
@@ -217,6 +219,26 @@ TEST_P(VideoFrameCompositorTest, RenderFiresPrensentationCallback) {
EXPECT_NE(base::TimeTicks(), metadata->expected_display_time);
}
+TEST_P(VideoFrameCompositorTest, PresentationCallbackForcesBeginFrames) {
+ if (!IsSurfaceLayerForVideoEnabled())
+ return;
+
+ // A call to the requestVideoFrameCallback() API should set ForceBeginFrames.
+ EXPECT_CALL(*submitter_, SetForceBeginFrames(true));
+ compositor()->SetOnFramePresentedCallback(GetNewFramePresentedCB());
+ base::RunLoop().RunUntilIdle();
+
+ testing::Mock::VerifyAndClear(submitter_);
+
+ // The flag should be un-set when stop receiving callbacks.
+ base::RunLoop run_loop;
+ EXPECT_CALL(*submitter_, SetForceBeginFrames(false))
+ .WillOnce(RunClosure(run_loop.QuitClosure()));
+ run_loop.Run();
+
+ testing::Mock::VerifyAndClear(submitter_);
+}
+
TEST_P(VideoFrameCompositorTest, MultiplePresentationCallbacks) {
// Advance the clock so we can differentiate between base::TimeTicks::Now()
// and base::TimeTicks().
@@ -231,6 +253,7 @@ TEST_P(VideoFrameCompositorTest, MultiplePresentationCallbacks) {
scoped_refptr<VideoFrame> opaque_frame_3 = CreateOpaqueFrame(kSize3, kSize3);
EXPECT_CALL(*this, OnNewFramePresented()).Times(1);
+ EXPECT_CALL(*submitter_, SetForceBeginFrames(_)).Times(AnyNumber());
compositor()->SetOnFramePresentedCallback(GetNewFramePresentedCB());
compositor()->PaintSingleFrame(opaque_frame_1);
diff --git a/chromium/media/blink/watch_time_reporter_unittest.cc b/chromium/media/blink/watch_time_reporter_unittest.cc
index 591629ea0b2..7db8151cfc9 100644
--- a/chromium/media/blink/watch_time_reporter_unittest.cc
+++ b/chromium/media/blink/watch_time_reporter_unittest.cc
@@ -261,6 +261,9 @@ class WatchTimeReporterTest
const std::string& taskName,
mojo::PendingReceiver<media::learning::mojom::LearningTaskController>
receiver) override {}
+ void AcquirePlaybackEventsRecorder(
+ mojo::PendingReceiver<mojom::PlaybackEventsRecorder> receiver)
+ override {}
void Initialize(bool is_mse, mojom::MediaURLScheme url_scheme) override {}
void OnError(PipelineStatus status) override {}
void SetIsEME() override {}
diff --git a/chromium/media/blink/webcontentdecryptionmodule_impl.cc b/chromium/media/blink/webcontentdecryptionmodule_impl.cc
index 1e86a444b62..9e4ade56fd7 100644
--- a/chromium/media/blink/webcontentdecryptionmodule_impl.cc
+++ b/chromium/media/blink/webcontentdecryptionmodule_impl.cc
@@ -7,7 +7,8 @@
#include <utility>
#include "base/bind.h"
-#include "base/logging.h"
+#include "base/check.h"
+#include "base/notreached.h"
#include "base/numerics/safe_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
diff --git a/chromium/media/blink/webcontentdecryptionmodulesession_impl.cc b/chromium/media/blink/webcontentdecryptionmodulesession_impl.cc
index a62902e873a..464b8fa30a6 100644
--- a/chromium/media/blink/webcontentdecryptionmodulesession_impl.cc
+++ b/chromium/media/blink/webcontentdecryptionmodulesession_impl.cc
@@ -8,8 +8,9 @@
#include "base/bind.h"
#include "base/callback_helpers.h"
-#include "base/logging.h"
+#include "base/check_op.h"
#include "base/metrics/histogram_functions.h"
+#include "base/notreached.h"
#include "base/numerics/safe_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
diff --git a/chromium/media/blink/webinbandtexttrack_impl.cc b/chromium/media/blink/webinbandtexttrack_impl.cc
index 3b66718d924..df182a7b906 100644
--- a/chromium/media/blink/webinbandtexttrack_impl.cc
+++ b/chromium/media/blink/webinbandtexttrack_impl.cc
@@ -4,7 +4,7 @@
#include "media/blink/webinbandtexttrack_impl.h"
-#include "base/logging.h"
+#include "base/check.h"
namespace media {
diff --git a/chromium/media/blink/webmediaplayer_impl.cc b/chromium/media/blink/webmediaplayer_impl.cc
index bf58056d782..254902da241 100644
--- a/chromium/media/blink/webmediaplayer_impl.cc
+++ b/chromium/media/blink/webmediaplayer_impl.cc
@@ -110,14 +110,10 @@ void SetSinkIdOnMediaThread(
}
bool IsBackgroundSuspendEnabled(const WebMediaPlayerImpl* wmpi) {
- // TODO(crbug.com/867146): remove these switches.
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kDisableMediaSuspend))
+ switches::kDisableBackgroundMediaSuspend)) {
return false;
- if (base::CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kEnableMediaSuspend))
- return true;
-
+ }
return wmpi->IsBackgroundMediaSuspendEnabled();
}
@@ -280,7 +276,8 @@ void CreateAllocation(base::trace_event::ProcessMemoryDump* pmd,
auto* std_allocator = base::trace_event::MemoryDumpManager::GetInstance()
->system_allocator_pool_name();
- pmd->AddSuballocation(dump->guid(), std_allocator);
+ if (std_allocator)
+ pmd->AddSuballocation(dump->guid(), std_allocator);
}
} // namespace
@@ -335,13 +332,13 @@ WebMediaPlayerImpl::WebMediaPlayerImpl(
is_background_video_track_optimization_supported_(
params->IsBackgroundVideoTrackOptimizationSupported()),
is_remoting_renderer_enabled_(params->IsRemotingRendererEnabled()),
- reported_renderer_type_(RendererFactoryType::kDefault),
simple_watch_timer_(
base::BindRepeating(&WebMediaPlayerImpl::OnSimpleWatchTimerTick,
base::Unretained(this)),
base::BindRepeating(&WebMediaPlayerImpl::GetCurrentTimeInternal,
base::Unretained(this))),
will_play_helper_(nullptr),
+ demuxer_override_(params->TakeDemuxerOverride()),
power_status_helper_(params->TakePowerStatusHelper()) {
DVLOG(1) << __func__;
DCHECK(adjust_allocated_memory_cb_);
@@ -416,6 +413,13 @@ WebMediaPlayerImpl::WebMediaPlayerImpl(
base::BindRepeating(&WebMediaPlayerImpl::OnMainThreadMemoryDump,
weak_this_, media_log_->id()));
+ media_metrics_provider_->AcquirePlaybackEventsRecorder(
+ playback_events_recorder_.BindNewPipeAndPassReceiver());
+
+ // MediaMetricsProvider may drop the request for PlaybackEventsRecorder if
+ // it's not interested in recording these events.
+ playback_events_recorder_.reset_on_disconnect();
+
#if defined(OS_ANDROID)
renderer_factory_selector_->SetRemotePlayStateChangeCB(
BindToCurrentLoop(base::BindRepeating(
@@ -728,8 +732,9 @@ void WebMediaPlayerImpl::DoLoad(LoadType load_type,
load_type == kLoadTypeURL ? blink::GetMediaURLScheme(loaded_url_)
: mojom::MediaURLScheme::kUnknown);
- // Media source pipelines can start immediately.
- if (load_type == kLoadTypeMediaSource) {
+ if (demuxer_override_ || load_type == kLoadTypeMediaSource) {
+ // If a demuxer override was specified or a Media Source pipeline will be
+ // used, the pipeline can start immediately.
StartPipeline();
} else {
// Short circuit the more complex loading path for data:// URLs. Sending
@@ -791,6 +796,9 @@ void WebMediaPlayerImpl::Play() {
// Try to create the smoothness helper, in case we were paused before.
UpdateSmoothnessHelper();
+ if (playback_events_recorder_)
+ playback_events_recorder_->OnPlaying();
+
watch_time_reporter_->SetAutoplayInitiated(client_->WasAutoplayInitiated());
// If we're seeking we'll trigger the watch time reporter upon seek completed;
@@ -811,6 +819,9 @@ void WebMediaPlayerImpl::Play() {
MaybeUpdateBufferSizesForPlayback();
UpdatePlayState();
+ // Paused changed so we should update media position state.
+ UpdateMediaPositionState();
+
// Notify the learning task, if needed.
will_play_helper_.CompleteObservationIfNeeded(learning::TargetValue(true));
}
@@ -842,6 +853,9 @@ void WebMediaPlayerImpl::Pause() {
if (observer_)
observer_->OnPaused();
+ if (playback_events_recorder_)
+ playback_events_recorder_->OnPaused();
+
DCHECK(watch_time_reporter_);
watch_time_reporter_->OnPaused();
@@ -897,6 +911,9 @@ void WebMediaPlayerImpl::DoSeek(base::TimeDelta time, bool time_updated) {
return;
}
+ if (playback_events_recorder_)
+ playback_events_recorder_->OnSeeking();
+
// Call this before setting |seeking_| so that the current media time can be
// recorded by the reporter.
if (watch_time_reporter_)
@@ -936,6 +953,10 @@ void WebMediaPlayerImpl::SetRate(double rate) {
pipeline_controller_->SetPlaybackRate(rate);
MaybeUpdateBufferSizesForPlayback();
+
+ // The playback rate has changed so we should rebuild the media position
+ // state.
+ UpdateMediaPositionState();
}
void WebMediaPlayerImpl::SetVolume(double volume) {
@@ -1593,6 +1614,8 @@ void WebMediaPlayerImpl::OnPipelineSeeked(bool time_updated) {
} else {
DCHECK(watch_time_reporter_);
watch_time_reporter_->OnPlaying();
+ if (playback_events_recorder_)
+ playback_events_recorder_->OnPlaying();
}
if (time_updated)
should_notify_time_changed_ = true;
@@ -1766,6 +1789,9 @@ void WebMediaPlayerImpl::OnError(PipelineStatus status) {
if (found_hls && mb_data_source_) {
demuxer_found_hls_ = true;
+ if (observer_)
+ observer_->OnHlsManifestDetected();
+
UMA_HISTOGRAM_BOOLEAN("Media.WebMediaPlayerImpl.HLS.IsCorsCrossOrigin",
mb_data_source_->IsCorsCrossOrigin());
if (mb_data_source_->IsCorsCrossOrigin()) {
@@ -1823,10 +1849,12 @@ void WebMediaPlayerImpl::OnError(PipelineStatus status) {
status = PIPELINE_ERROR_EXTERNAL_RENDERER_FAILED;
#endif
- MaybeSetContainerName();
+ MaybeSetContainerNameForMetrics();
simple_watch_timer_.Stop();
media_log_->NotifyError(status);
media_metrics_provider_->OnError(status);
+ if (playback_events_recorder_)
+ playback_events_recorder_->OnError(status);
if (watch_time_reporter_)
watch_time_reporter_->OnError(status);
@@ -1857,6 +1885,9 @@ void WebMediaPlayerImpl::OnEnded() {
ended_ = true;
client_->TimeChanged();
+ if (playback_events_recorder_)
+ playback_events_recorder_->OnEnded();
+
// We don't actually want this to run until |client_| calls seek() or pause(),
// but that should have already happened in timeChanged() and so this is
// expected to be a no-op.
@@ -1873,7 +1904,7 @@ void WebMediaPlayerImpl::OnMetadata(const PipelineMetadata& metadata) {
media_metrics_provider_->SetTimeToMetadata(time_to_metadata_);
RecordTimingUMA("Media.TimeToMetadata", time_to_metadata_);
- MaybeSetContainerName();
+ MaybeSetContainerNameForMetrics();
pipeline_metadata_ = metadata;
if (power_status_helper_)
@@ -2142,6 +2173,9 @@ void WebMediaPlayerImpl::OnBufferingStateChangeInternal(
watch_time_reporter_->OnUnderflowComplete(elapsed);
underflow_timer_.reset();
}
+
+ if (playback_events_recorder_)
+ playback_events_recorder_->OnBufferingComplete();
} else {
// Buffering has underflowed.
DCHECK_EQ(state, BUFFERING_HAVE_NOTHING);
@@ -2153,6 +2187,9 @@ void WebMediaPlayerImpl::OnBufferingStateChangeInternal(
!seeking_) {
underflow_timer_ = std::make_unique<base::ElapsedTimer>();
watch_time_reporter_->OnUnderflow();
+
+ if (playback_events_recorder_)
+ playback_events_recorder_->OnBuffering();
}
// It shouldn't be possible to underflow if we've not advanced past
@@ -2746,7 +2783,13 @@ void WebMediaPlayerImpl::StartPipeline() {
#endif // defined(OS_ANDROID)
// Figure out which demuxer to use.
- if (load_type_ != kLoadTypeMediaSource) {
+ if (demuxer_override_) {
+ DCHECK(!chunk_demuxer_);
+
+ SetDemuxer(std::move(demuxer_override_));
+ // TODO(https://crbug.com/1076267): Should everything else after this block
+ // run in the demuxer override case?
+ } else if (load_type_ != kLoadTypeMediaSource) {
DCHECK(!chunk_demuxer_);
DCHECK(data_source_);
@@ -2782,12 +2825,6 @@ void WebMediaPlayerImpl::StartPipeline() {
}
}
- // TODO(sandersd): FileSystem objects may also be non-static, but due to our
- // caching layer such situations are broken already. http://crbug.com/593159
- bool is_static = !chunk_demuxer_;
- bool is_streaming = IsStreaming();
- UMA_HISTOGRAM_BOOLEAN("Media.IsStreaming", is_streaming);
-
// If possible attempt to avoid decoder spool up until playback starts.
Pipeline::StartType start_type = Pipeline::StartType::kNormal;
if (!chunk_demuxer_ && preload_ == MultibufferDataSource::METADATA &&
@@ -2799,10 +2836,14 @@ void WebMediaPlayerImpl::StartPipeline() {
attempting_suspended_start_ = true;
}
+ // TODO(sandersd): FileSystem objects may also be non-static, but due to our
+ // caching layer such situations are broken already. http://crbug.com/593159
+ const bool is_static = !chunk_demuxer_;
+
// ... and we're ready to go!
// TODO(sandersd): On Android, defer Start() if the tab is not visible.
seeking_ = true;
- pipeline_controller_->Start(start_type, demuxer_.get(), this, is_streaming,
+ pipeline_controller_->Start(start_type, demuxer_.get(), this, IsStreaming(),
is_static);
}
@@ -2829,6 +2870,10 @@ void WebMediaPlayerImpl::SetReadyState(WebMediaPlayer::ReadyState state) {
// Always notify to ensure client has the latest value.
client_->ReadyStateChanged();
+
+ // The ready state affects the effective playback rate so we should update
+ // the media position state.
+ UpdateMediaPositionState();
}
scoped_refptr<blink::WebAudioSourceProviderImpl>
@@ -2898,8 +2943,12 @@ void WebMediaPlayerImpl::UpdateMediaPositionState() {
if (current_time > duration)
current_time = duration;
- media_session::MediaPosition new_position(paused_ ? 0.0 : playback_rate_,
- duration, current_time);
+ const double effective_playback_rate =
+ paused_ || ready_state_ < kReadyStateHaveFutureData ? 0.0
+ : playback_rate_;
+
+ media_session::MediaPosition new_position(effective_playback_rate, duration,
+ current_time);
if (media_position_state_ == new_position)
return;
@@ -3191,7 +3240,7 @@ void WebMediaPlayerImpl::FinishMemoryUsageReport(int64_t demuxer_memory_usage) {
stats.audio_memory_usage + video_memory_usage + data_source_memory_usage +
demuxer_memory_usage;
- DVLOG(2) << "Memory Usage -- Total: " << current_memory_usage
+ DVLOG(3) << "Memory Usage -- Total: " << current_memory_usage
<< " Audio: " << stats.audio_memory_usage
<< ", Video: " << video_memory_usage
<< ", DataSource: " << data_source_memory_usage
@@ -3653,6 +3702,9 @@ void WebMediaPlayerImpl::RecordVideoNaturalSize(const gfx::Size& natural_size) {
UMA_HISTOGRAM_VIDEO_HEIGHT("Media.VideoHeight.Initial.EME", height);
UMA_HISTOGRAM_VIDEO_HEIGHT("Media.VideoHeight.Initial.All", height);
+
+ if (playback_events_recorder_)
+ playback_events_recorder_->OnNaturalSizeChanged(natural_size);
}
#undef UMA_HISTOGRAM_VIDEO_HEIGHT
@@ -3712,11 +3764,7 @@ void WebMediaPlayerImpl::OnPictureInPictureAvailabilityChanged(bool available) {
delegate_->DidPictureInPictureAvailabilityChange(delegate_id_, available);
}
-void WebMediaPlayerImpl::MaybeSetContainerName() {
- // MSE nor MediaPlayerRenderer provide container information.
- if (chunk_demuxer_ || using_media_player_renderer_)
- return;
-
+void WebMediaPlayerImpl::MaybeSetContainerNameForMetrics() {
// Pipeline startup failed before even getting a demuxer setup.
if (!demuxer_)
return;
@@ -3725,11 +3773,10 @@ void WebMediaPlayerImpl::MaybeSetContainerName() {
if (highest_ready_state_ >= WebMediaPlayer::kReadyStateHaveMetadata)
return;
-// If ffmpeg isn't enabled, we can't get the container name.
-#if BUILDFLAG(ENABLE_FFMPEG)
- media_metrics_provider_->SetContainerName(
- static_cast<FFmpegDemuxer*>(demuxer_.get())->container());
-#endif
+ // Only report metrics for demuxers that provide container information.
+ auto container = demuxer_->GetContainerForMetrics();
+ if (container.has_value())
+ media_metrics_provider_->SetContainerName(container.value());
}
void WebMediaPlayerImpl::MaybeUpdateBufferSizesForPlayback() {
@@ -3742,14 +3789,13 @@ void WebMediaPlayerImpl::MaybeUpdateBufferSizesForPlayback() {
mb_data_source_->MediaPlaybackRateChanged(playback_rate_);
if (!paused_)
mb_data_source_->MediaIsPlaying();
-
- // The playback rate has changed so we should rebuild the media position
- // state.
- UpdateMediaPositionState();
}
void WebMediaPlayerImpl::OnSimpleWatchTimerTick() {
RecordSimpleWatchTimeUMA(reported_renderer_type_);
+
+ if (playback_events_recorder_)
+ playback_events_recorder_->OnPipelineStatistics(GetPipelineStatistics());
}
GURL WebMediaPlayerImpl::GetSrcAfterRedirects() {
diff --git a/chromium/media/blink/webmediaplayer_impl.h b/chromium/media/blink/webmediaplayer_impl.h
index 69e853bb93e..b62f8cf93b9 100644
--- a/chromium/media/blink/webmediaplayer_impl.h
+++ b/chromium/media/blink/webmediaplayer_impl.h
@@ -45,6 +45,7 @@
#include "media/blink/webmediaplayer_params.h"
#include "media/filters/pipeline_controller.h"
#include "media/learning/common/media_learning_tasks.h"
+#include "media/mojo/mojom/playback_events_recorder.mojom.h"
#include "media/renderers/paint_canvas_video_renderer.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "services/media_session/public/cpp/media_position.h"
@@ -630,7 +631,7 @@ class MEDIA_BLINK_EXPORT WebMediaPlayerImpl
bool IsInPictureInPicture() const;
// Sets the UKM container name if needed.
- void MaybeSetContainerName();
+ void MaybeSetContainerNameForMetrics();
// Switch to SurfaceLayer, either initially or from VideoLayer.
void ActivateSurfaceLayerForVideo();
@@ -867,6 +868,8 @@ class MEDIA_BLINK_EXPORT WebMediaPlayerImpl
// unimportant.
bool suppress_destruction_errors_ = false;
+ // TODO(dalecurtis): The following comment is inaccurate as this value is also
+ // used for, for example, data URLs.
// Used for HLS playback and in certain fallback paths (e.g. on older devices
// that can't support the unified media pipeline).
GURL loaded_url_;
@@ -1004,6 +1007,7 @@ class MEDIA_BLINK_EXPORT WebMediaPlayerImpl
base::CancelableClosure update_background_status_cb_;
mojo::Remote<mojom::MediaMetricsProvider> media_metrics_provider_;
+ mojo::Remote<mojom::PlaybackEventsRecorder> playback_events_recorder_;
base::Optional<ReadyState> stale_state_override_for_testing_;
@@ -1044,11 +1048,14 @@ class MEDIA_BLINK_EXPORT WebMediaPlayerImpl
base::CancelableOnceClosure have_enough_after_lazy_load_cb_;
// State for simplified watch time reporting.
- RendererFactoryType reported_renderer_type_;
+ RendererFactoryType reported_renderer_type_ = RendererFactoryType::kDefault;
SimpleWatchTimer simple_watch_timer_;
LearningExperimentHelper will_play_helper_;
+ // Stores the optional override Demuxer until it is used in DoLoad().
+ std::unique_ptr<Demuxer> demuxer_override_;
+
std::unique_ptr<PowerStatusHelper> power_status_helper_;
// Created while playing, deleted otherwise.
diff --git a/chromium/media/blink/webmediaplayer_impl_unittest.cc b/chromium/media/blink/webmediaplayer_impl_unittest.cc
index f778620cf6e..77dc3344993 100644
--- a/chromium/media/blink/webmediaplayer_impl_unittest.cc
+++ b/chromium/media/blink/webmediaplayer_impl_unittest.cc
@@ -49,6 +49,7 @@
#include "media/mojo/services/watch_time_recorder.h"
#include "media/renderers/default_decoder_factory.h"
#include "media/renderers/default_renderer_factory.h"
+#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
@@ -318,17 +319,17 @@ class WebMediaPlayerImplTest
public:
WebMediaPlayerImplTest()
: media_thread_("MediaThreadForTest"),
- web_view_(
- blink::WebView::Create(/*client=*/nullptr,
- /*is_hidden=*/false,
- /*compositing_enabled=*/false,
- nullptr,
- mojo::ScopedInterfaceEndpointHandle())),
- web_local_frame_(
- blink::WebLocalFrame::CreateMainFrame(web_view_,
- &web_frame_client_,
- nullptr,
- nullptr)),
+ web_view_(blink::WebView::Create(/*client=*/nullptr,
+ /*is_hidden=*/false,
+ /*compositing_enabled=*/false,
+ nullptr,
+ mojo::NullAssociatedReceiver())),
+ web_local_frame_(blink::WebLocalFrame::CreateMainFrame(
+ web_view_,
+ &web_frame_client_,
+ nullptr,
+ base::UnguessableToken::Create(),
+ nullptr)),
context_provider_(viz::TestContextProvider::Create()),
audio_parameters_(TestAudioParameters::Normal()),
memory_dump_manager_(
@@ -349,6 +350,30 @@ class WebMediaPlayerImplTest
}
void InitializeWebMediaPlayerImpl() {
+ InitializeWebMediaPlayerImplInternal(nullptr);
+ }
+
+ ~WebMediaPlayerImplTest() override {
+ if (!wmpi_)
+ return;
+ EXPECT_CALL(client_, SetCcLayer(nullptr));
+ EXPECT_CALL(client_, MediaRemotingStopped(_));
+
+ // Destruct WebMediaPlayerImpl and pump the message loop to ensure that
+ // objects passed to the message loop for destruction are released.
+ //
+ // NOTE: This should be done before any other member variables are
+ // destructed since WMPI may reference them during destruction.
+ wmpi_.reset();
+
+ CycleThreads();
+
+ web_view_->Close();
+ }
+
+ protected:
+ void InitializeWebMediaPlayerImplInternal(
+ std::unique_ptr<media::Demuxer> demuxer_override) {
auto media_log = std::make_unique<NiceMock<MockMediaLog>>();
InitializeSurfaceLayerBridge();
@@ -361,13 +386,19 @@ class WebMediaPlayerImplTest
auto factory_selector = std::make_unique<RendererFactorySelector>();
renderer_factory_selector_ = factory_selector.get();
decoder_factory_.reset(new media::DefaultDecoderFactory(nullptr));
+#if defined(OS_ANDROID)
factory_selector->AddBaseFactory(
RendererFactoryType::kDefault,
std::make_unique<DefaultRendererFactory>(
media_log.get(), decoder_factory_.get(),
- DefaultRendererFactory::GetGpuFactoriesCB(), nullptr));
-#if defined(OS_ANDROID)
+ DefaultRendererFactory::GetGpuFactoriesCB()));
factory_selector->StartRequestRemotePlayStateCB(base::DoNothing());
+#else
+ factory_selector->AddBaseFactory(
+ RendererFactoryType::kDefault,
+ std::make_unique<DefaultRendererFactory>(
+ media_log.get(), decoder_factory_.get(),
+ DefaultRendererFactory::GetGpuFactoriesCB(), nullptr));
#endif
mojo::Remote<mojom::MediaMetricsProvider> provider;
@@ -405,7 +436,7 @@ class WebMediaPlayerImplTest
viz::TestContextProvider::Create(),
blink::WebMediaPlayer::SurfaceLayerMode::kAlways,
is_background_suspend_enabled_, is_background_video_playback_enabled_,
- true, false, nullptr);
+ true, false, std::move(demuxer_override), nullptr);
auto compositor = std::make_unique<NiceMock<MockVideoFrameCompositor>>(
params->video_frame_compositor_task_runner());
@@ -417,25 +448,6 @@ class WebMediaPlayerImplTest
std::move(params));
}
- ~WebMediaPlayerImplTest() override {
- if (!wmpi_)
- return;
- EXPECT_CALL(client_, SetCcLayer(nullptr));
- EXPECT_CALL(client_, MediaRemotingStopped(_));
-
- // Destruct WebMediaPlayerImpl and pump the message loop to ensure that
- // objects passed to the message loop for destruction are released.
- //
- // NOTE: This should be done before any other member variables are
- // destructed since WMPI may reference them during destruction.
- wmpi_.reset();
-
- CycleThreads();
-
- web_view_->Close();
- }
-
- protected:
std::unique_ptr<blink::WebSurfaceLayerBridge> CreateMockSurfaceLayerBridge(
blink::WebSurfaceLayerBridgeObserver*,
cc::UpdateSubmissionStateCB) {
@@ -1606,6 +1618,48 @@ TEST_F(WebMediaPlayerImplTest, MediaPositionState_PlayPauseSetRate) {
Play();
}
+TEST_F(WebMediaPlayerImplTest, MediaPositionState_Underflow) {
+ InitializeWebMediaPlayerImpl();
+
+ testing::Sequence sequence;
+ EXPECT_CALL(delegate_,
+ DidPlayerMediaPositionStateChange(delegate_.player_id(), _))
+ .InSequence(sequence)
+ .WillOnce([](auto id, auto position) {
+ EXPECT_EQ(0.0, position.playback_rate());
+ });
+ EXPECT_CALL(delegate_,
+ DidPlayerMediaPositionStateChange(delegate_.player_id(), _))
+ .InSequence(sequence)
+ .WillOnce([](auto id, auto position) {
+ EXPECT_EQ(1.0, position.playback_rate());
+ });
+ EXPECT_CALL(delegate_,
+ DidPlayerMediaPositionStateChange(delegate_.player_id(), _))
+ .InSequence(sequence)
+ .WillOnce([](auto id, auto position) {
+ EXPECT_EQ(0.0, position.playback_rate());
+ });
+ EXPECT_CALL(delegate_,
+ DidPlayerMediaPositionStateChange(delegate_.player_id(), _))
+ .InSequence(sequence)
+ .WillOnce([](auto id, auto position) {
+ EXPECT_EQ(1.0, position.playback_rate());
+ });
+
+ wmpi_->SetRate(1.0);
+ LoadAndWaitForReadyState(kAudioOnlyTestFile,
+ blink::WebMediaPlayer::kReadyStateHaveCurrentData);
+ // Play will set the playback rate to 1.0.
+ Play();
+
+ // Underflow will set the playback rate to 0.0.
+ SetReadyState(blink::WebMediaPlayer::kReadyStateHaveCurrentData);
+
+ // Leaving the underflow state will restore the playback rate of 1.0.
+ SetReadyState(blink::WebMediaPlayer::kReadyStateHaveFutureData);
+}
+
TEST_F(WebMediaPlayerImplTest, MediaPositionState_Seeking) {
InitializeWebMediaPlayerImpl();
@@ -1629,9 +1683,9 @@ TEST_F(WebMediaPlayerImplTest, MediaPositionState_Seeking) {
DidPlayerMediaPositionStateChange(delegate_.player_id(), _))
.InSequence(s)
.WillOnce([](auto id, auto position) {
- EXPECT_EQ(1.0, position.playback_rate());
+ EXPECT_EQ(0.0, position.playback_rate());
EXPECT_EQ(kAudioOnlyTestFileDuration, position.duration());
- EXPECT_EQ(base::TimeDelta::FromMilliseconds(100),
+ EXPECT_EQ(base::TimeDelta(),
position.GetPositionAtTime(position.last_updated_time()));
});
EXPECT_CALL(delegate_, DidPlayerMediaPositionStateChange(
@@ -2139,6 +2193,33 @@ TEST_F(WebMediaPlayerImplTest, MemDumpReporting) {
EXPECT_EQ(dump_count, 3);
}
+// Verify that a demuxer override is used when specified.
+TEST_F(WebMediaPlayerImplTest, DemuxerOverride) {
+ std::unique_ptr<MockDemuxer> demuxer =
+ std::make_unique<NiceMock<MockDemuxer>>();
+ StrictMock<MockDemuxerStream> stream(DemuxerStream::AUDIO);
+ stream.set_audio_decoder_config(TestAudioConfig::Normal());
+ std::vector<DemuxerStream*> streams;
+ streams.push_back(&stream);
+
+ EXPECT_CALL(stream, SupportsConfigChanges()).WillRepeatedly(Return(false));
+
+ EXPECT_CALL(*demuxer.get(), OnInitialize(_, _))
+ .WillOnce(RunOnceCallback<1>(PIPELINE_OK));
+ EXPECT_CALL(*demuxer.get(), GetAllStreams()).WillRepeatedly(Return(streams));
+ // Called when WebMediaPlayerImpl is destroyed.
+ EXPECT_CALL(*demuxer.get(), Stop());
+
+ InitializeWebMediaPlayerImplInternal(std::move(demuxer));
+
+ EXPECT_FALSE(IsSuspended());
+ wmpi_->Load(blink::WebMediaPlayer::kLoadTypeURL,
+ blink::WebMediaPlayerSource(blink::WebURL(GURL("data://test"))),
+ blink::WebMediaPlayer::kCorsModeUnspecified);
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(IsSuspended());
+}
+
class WebMediaPlayerImplBackgroundBehaviorTest
: public WebMediaPlayerImplTest,
public ::testing::WithParamInterface<
diff --git a/chromium/media/blink/webmediaplayer_params.cc b/chromium/media/blink/webmediaplayer_params.cc
index 24fda578b1a..a9dede964a4 100644
--- a/chromium/media/blink/webmediaplayer_params.cc
+++ b/chromium/media/blink/webmediaplayer_params.cc
@@ -7,6 +7,7 @@
#include "base/single_thread_task_runner.h"
#include "base/task_runner.h"
#include "media/base/audio_renderer_sink.h"
+#include "media/base/demuxer.h"
namespace media {
@@ -33,6 +34,7 @@ WebMediaPlayerParams::WebMediaPlayerParams(
bool is_background_video_playback_enabled,
bool is_background_video_track_optimization_supported,
bool is_remoting_renderer_enabled,
+ std::unique_ptr<Demuxer> demuxer_override,
std::unique_ptr<PowerStatusHelper> power_status_helper)
: defer_load_cb_(defer_load_cb),
audio_renderer_sink_(audio_renderer_sink),
@@ -57,8 +59,13 @@ WebMediaPlayerParams::WebMediaPlayerParams(
is_background_video_track_optimization_supported_(
is_background_video_track_optimization_supported),
is_remoting_renderer_enabled_(is_remoting_renderer_enabled),
+ demuxer_override_(std::move(demuxer_override)),
power_status_helper_(std::move(power_status_helper)) {}
WebMediaPlayerParams::~WebMediaPlayerParams() = default;
+std::unique_ptr<Demuxer> WebMediaPlayerParams::TakeDemuxerOverride() {
+ return std::move(demuxer_override_);
+}
+
} // namespace media
diff --git a/chromium/media/blink/webmediaplayer_params.h b/chromium/media/blink/webmediaplayer_params.h
index 0a324a32db0..4cc8756d4e2 100644
--- a/chromium/media/blink/webmediaplayer_params.h
+++ b/chromium/media/blink/webmediaplayer_params.h
@@ -44,6 +44,7 @@ using CreateSurfaceLayerBridgeCB =
blink::WebSurfaceLayerBridgeObserver*,
cc::UpdateSubmissionStateCB)>;
+class Demuxer;
class SwitchableAudioRendererSink;
// Holds parameters for constructing WebMediaPlayerImpl without having
@@ -85,6 +86,7 @@ class MEDIA_BLINK_EXPORT WebMediaPlayerParams {
bool is_background_video_play_enabled,
bool is_background_video_track_optimization_supported,
bool is_remoting_renderer_enabled,
+ std::unique_ptr<Demuxer> demuxer_override,
std::unique_ptr<PowerStatusHelper> power_status_helper);
~WebMediaPlayerParams();
@@ -172,6 +174,8 @@ class MEDIA_BLINK_EXPORT WebMediaPlayerParams {
return is_remoting_renderer_enabled_;
}
+ std::unique_ptr<Demuxer> TakeDemuxerOverride();
+
std::unique_ptr<PowerStatusHelper> TakePowerStatusHelper() {
return std::move(power_status_helper_);
}
@@ -207,6 +211,9 @@ class MEDIA_BLINK_EXPORT WebMediaPlayerParams {
// Whether the media in this frame is a remoting media.
bool is_remoting_renderer_enabled_ = false;
+ // Optional custom demuxer to use instead of the standard demuxers.
+ std::unique_ptr<Demuxer> demuxer_override_;
+
std::unique_ptr<PowerStatusHelper> power_status_helper_;
DISALLOW_IMPLICIT_CONSTRUCTORS(WebMediaPlayerParams);
diff --git a/chromium/media/capabilities/in_memory_video_decode_stats_db_unittest.cc b/chromium/media/capabilities/in_memory_video_decode_stats_db_unittest.cc
index d64077c95fe..2d69342f7f9 100644
--- a/chromium/media/capabilities/in_memory_video_decode_stats_db_unittest.cc
+++ b/chromium/media/capabilities/in_memory_video_decode_stats_db_unittest.cc
@@ -6,7 +6,7 @@
#include "base/bind.h"
#include "base/bind_helpers.h"
-#include "base/logging.h"
+#include "base/check_op.h"
#include "base/memory/ptr_util.h"
#include "base/test/gtest_util.h"
#include "base/test/task_environment.h"
diff --git a/chromium/media/capabilities/video_decode_stats_db.cc b/chromium/media/capabilities/video_decode_stats_db.cc
index d5b4d93b949..74c54417118 100644
--- a/chromium/media/capabilities/video_decode_stats_db.cc
+++ b/chromium/media/capabilities/video_decode_stats_db.cc
@@ -4,6 +4,7 @@
#include "media/capabilities/video_decode_stats_db.h"
+#include "base/check_op.h"
#include "base/format_macros.h"
#include "base/strings/stringprintf.h"
#include "media/capabilities/bucket_utility.h"
diff --git a/chromium/media/capabilities/video_decode_stats_db_impl.cc b/chromium/media/capabilities/video_decode_stats_db_impl.cc
index 359c2d6c941..ca4c73c9432 100644
--- a/chromium/media/capabilities/video_decode_stats_db_impl.cc
+++ b/chromium/media/capabilities/video_decode_stats_db_impl.cc
@@ -5,6 +5,7 @@
#include "media/capabilities/video_decode_stats_db_impl.h"
#include <memory>
+#include <string>
#include <tuple>
#include "base/bind.h"
@@ -13,10 +14,12 @@
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/field_trial_params.h"
+#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/sequence_checker.h"
#include "base/task/post_task.h"
#include "base/task/thread_pool.h"
+#include "base/threading/thread_task_runner_handle.h"
#include "base/time/default_clock.h"
#include "components/leveldb_proto/public/proto_database_provider.h"
#include "media/base/media_switches.h"
@@ -28,12 +31,23 @@ using ProtoDecodeStatsEntry = leveldb_proto::ProtoDatabase<DecodeStatsProto>;
namespace {
+// Timeout threshold for DB operations. See OnOperationTimeout().
+// NOTE: Used by UmaHistogramOpTime. Change the name if you change the time.
+static constexpr base::TimeDelta kPendingOpTimeout =
+ base::TimeDelta::FromSeconds(30);
+
const int kMaxFramesPerBufferDefault = 2500;
const int kMaxDaysToKeepStatsDefault = 30;
const bool kEnableUnweightedEntriesDefault = false;
+void UmaHistogramOpTime(const std::string& op_name, base::TimeDelta duration) {
+ base::UmaHistogramCustomMicrosecondsTimes(
+ "Media.VideoDecodeStatsDB.OpTiming." + op_name, duration,
+ base::TimeDelta::FromMilliseconds(1), kPendingOpTimeout, 50);
+}
+
} // namespace
const char VideoDecodeStatsDBImpl::kMaxFramesPerBufferParamName[] =
@@ -45,6 +59,41 @@ const char VideoDecodeStatsDBImpl::kMaxDaysToKeepStatsParamName[] =
const char VideoDecodeStatsDBImpl::kEnableUnweightedEntriesParamName[] =
"db_enable_unweighted_entries";
+VideoDecodeStatsDBImpl::PendingOperation::PendingOperation(
+ std::string uma_str,
+ std::unique_ptr<base::CancelableOnceClosure> timeout_closure)
+ : uma_str_(uma_str),
+ timeout_closure_(std::move(timeout_closure)),
+ start_ticks_(base::TimeTicks::Now()) {
+ DVLOG(3) << __func__ << " Started " << uma_str_;
+}
+
+VideoDecodeStatsDBImpl::PendingOperation::~PendingOperation() {
+ // Destroying a pending operation that hasn't timed out yet implies the
+ // operation has completed.
+ if (timeout_closure_ && !timeout_closure_->IsCancelled()) {
+ base::TimeDelta op_duration = base::TimeTicks::Now() - start_ticks_;
+ UmaHistogramOpTime(uma_str_, op_duration);
+ DVLOG(3) << __func__ << " Completed " << uma_str_ << " ("
+ << op_duration.InMilliseconds() << ")";
+
+ // Ensure the timeout doesn't fire. Destruction should cancel the callback
+ // implicitly, but that's not a documented contract, so just taking the safe
+ // route.
+ timeout_closure_->Cancel();
+ }
+}
+
+void VideoDecodeStatsDBImpl::PendingOperation::OnTimeout() {
+ UmaHistogramOpTime(uma_str_, kPendingOpTimeout);
+ LOG(WARNING) << " Timeout performing " << uma_str_
+ << " operation on VideoDecodeStatsDB";
+
+ // Cancel the closure to ensure we don't double report the task as completed
+ // in ~PendingOperation().
+ timeout_closure_->Cancel();
+}
+
// static
int VideoDecodeStatsDBImpl::GetMaxFramesPerBuffer() {
return base::GetFieldTrialParamByFeatureAsDouble(
@@ -97,6 +146,43 @@ VideoDecodeStatsDBImpl::~VideoDecodeStatsDBImpl() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
+VideoDecodeStatsDBImpl::PendingOpId VideoDecodeStatsDBImpl::StartPendingOp(
+ std::string uma_str) {
+ PendingOpId op_id = next_op_id_++;
+
+ auto timeout_closure = std::make_unique<base::CancelableOnceClosure>(
+ base::BindOnce(&VideoDecodeStatsDBImpl::OnPendingOpTimeout,
+ weak_ptr_factory_.GetWeakPtr(), op_id));
+
+ base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE, timeout_closure->callback(), kPendingOpTimeout);
+
+ pending_ops_.emplace(op_id, std::make_unique<PendingOperation>(
+ uma_str, std::move(timeout_closure)));
+
+ return op_id;
+}
+
+void VideoDecodeStatsDBImpl::CompletePendingOp(PendingOpId op_id) {
+ // Destructing the PendingOperation will trigger UMA for completion timing.
+ int count = pending_ops_.erase(op_id);
+
+ // No big deal, but very unusual. Timeout is very generous, so tasks that
+ // timeout are generally assumed to be permanently hung.
+ if (!count)
+ DVLOG(2) << __func__ << " DB operation completed after timeout.";
+}
+
+void VideoDecodeStatsDBImpl::OnPendingOpTimeout(PendingOpId op_id) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+ auto it = pending_ops_.find(op_id);
+ DCHECK(it != pending_ops_.end());
+
+ it->second->OnTimeout();
+ pending_ops_.erase(it);
+}
+
void VideoDecodeStatsDBImpl::Initialize(InitializeCB init_cb) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(init_cb);
@@ -107,15 +193,18 @@ void VideoDecodeStatsDBImpl::Initialize(InitializeCB init_cb) {
// spamming the cache.
// TODO(chcunningham): Keep an eye on the size as the table evolves.
db_->Init(base::BindOnce(&VideoDecodeStatsDBImpl::OnInit,
- weak_ptr_factory_.GetWeakPtr(), std::move(init_cb)));
+ weak_ptr_factory_.GetWeakPtr(),
+ StartPendingOp("Initialize"), std::move(init_cb)));
}
-void VideoDecodeStatsDBImpl::OnInit(InitializeCB init_cb,
+void VideoDecodeStatsDBImpl::OnInit(PendingOpId op_id,
+ InitializeCB init_cb,
leveldb_proto::Enums::InitStatus status) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_NE(status, leveldb_proto::Enums::InitStatus::kInvalidOperation);
bool success = status == leveldb_proto::Enums::InitStatus::kOK;
DVLOG(2) << __func__ << (success ? " succeeded" : " FAILED!");
+ CompletePendingOp(op_id);
UMA_HISTOGRAM_BOOLEAN("Media.VideoDecodeStatsDB.OpSuccess.Initialize",
success);
@@ -143,10 +232,11 @@ void VideoDecodeStatsDBImpl::AppendDecodeStats(
DVLOG(3) << __func__ << " Reading key " << key.ToLogString()
<< " from DB with intent to update with " << entry.ToLogString();
- db_->GetEntry(key.Serialize(),
- base::BindOnce(&VideoDecodeStatsDBImpl::WriteUpdatedEntry,
- weak_ptr_factory_.GetWeakPtr(), key, entry,
- std::move(append_done_cb)));
+ db_->GetEntry(
+ key.Serialize(),
+ base::BindOnce(&VideoDecodeStatsDBImpl::WriteUpdatedEntry,
+ weak_ptr_factory_.GetWeakPtr(), StartPendingOp("Read"),
+ key, entry, std::move(append_done_cb)));
}
void VideoDecodeStatsDBImpl::GetDecodeStats(const VideoDescKey& key,
@@ -159,7 +249,8 @@ void VideoDecodeStatsDBImpl::GetDecodeStats(const VideoDescKey& key,
db_->GetEntry(
key.Serialize(),
base::BindOnce(&VideoDecodeStatsDBImpl::OnGotDecodeStats,
- weak_ptr_factory_.GetWeakPtr(), std::move(get_stats_cb)));
+ weak_ptr_factory_.GetWeakPtr(), StartPendingOp("Read"),
+ std::move(get_stats_cb)));
}
bool VideoDecodeStatsDBImpl::AreStatsUsable(
@@ -211,6 +302,7 @@ bool VideoDecodeStatsDBImpl::AreStatsUsable(
}
void VideoDecodeStatsDBImpl::WriteUpdatedEntry(
+ PendingOpId op_id,
const VideoDescKey& key,
const DecodeStatsEntry& new_entry,
AppendDecodeStatsCB append_done_cb,
@@ -218,6 +310,7 @@ void VideoDecodeStatsDBImpl::WriteUpdatedEntry(
std::unique_ptr<DecodeStatsProto> stats_proto) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(IsInitialized());
+ CompletePendingOp(op_id);
// Note: outcome of "Write" operation logged in OnEntryUpdated().
UMA_HISTOGRAM_BOOLEAN("Media.VideoDecodeStatsDB.OpSuccess.Read",
@@ -355,26 +448,31 @@ void VideoDecodeStatsDBImpl::WriteUpdatedEntry(
std::unique_ptr<DBType::KeyEntryVector> entries =
std::make_unique<DBType::KeyEntryVector>();
entries->emplace_back(key.Serialize(), *stats_proto);
- db_->UpdateEntries(std::move(entries),
- std::make_unique<leveldb_proto::KeyVector>(),
- base::BindOnce(&VideoDecodeStatsDBImpl::OnEntryUpdated,
- weak_ptr_factory_.GetWeakPtr(),
- std::move(append_done_cb)));
+ db_->UpdateEntries(
+ std::move(entries), std::make_unique<leveldb_proto::KeyVector>(),
+ base::BindOnce(&VideoDecodeStatsDBImpl::OnEntryUpdated,
+ weak_ptr_factory_.GetWeakPtr(), StartPendingOp("Write"),
+ std::move(append_done_cb)));
}
-void VideoDecodeStatsDBImpl::OnEntryUpdated(AppendDecodeStatsCB append_done_cb,
+void VideoDecodeStatsDBImpl::OnEntryUpdated(PendingOpId op_id,
+ AppendDecodeStatsCB append_done_cb,
bool success) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- UMA_HISTOGRAM_BOOLEAN("Media.VideoDecodeStatsDB.OpSuccess.Write", success);
DVLOG(3) << __func__ << " update " << (success ? "succeeded" : "FAILED!");
+ CompletePendingOp(op_id);
+ UMA_HISTOGRAM_BOOLEAN("Media.VideoDecodeStatsDB.OpSuccess.Write", success);
std::move(append_done_cb).Run(success);
}
void VideoDecodeStatsDBImpl::OnGotDecodeStats(
+ PendingOpId op_id,
GetDecodeStatsCB get_stats_cb,
bool success,
std::unique_ptr<DecodeStatsProto> stats_proto) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ DVLOG(3) << __func__ << " get " << (success ? "succeeded" : "FAILED!");
+ CompletePendingOp(op_id);
UMA_HISTOGRAM_BOOLEAN("Media.VideoDecodeStatsDB.OpSuccess.Read", success);
std::unique_ptr<DecodeStatsEntry> entry;
@@ -435,40 +533,23 @@ void VideoDecodeStatsDBImpl::ClearStats(base::OnceClosure clear_done_cb) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DVLOG(2) << __func__;
- db_->LoadKeys(
- base::BindOnce(&VideoDecodeStatsDBImpl::OnLoadAllKeysForClearing,
- weak_ptr_factory_.GetWeakPtr(), std::move(clear_done_cb)));
+ db_->UpdateEntriesWithRemoveFilter(
+ std::make_unique<ProtoDecodeStatsEntry::KeyEntryVector>(),
+ base::BindRepeating([](const std::string& key) { return true; }),
+ base::BindOnce(&VideoDecodeStatsDBImpl::OnStatsCleared,
+ weak_ptr_factory_.GetWeakPtr(), StartPendingOp("Clear"),
+ std::move(clear_done_cb)));
}
-void VideoDecodeStatsDBImpl::OnLoadAllKeysForClearing(
- base::OnceClosure clear_done_cb,
- bool success,
- std::unique_ptr<std::vector<std::string>> keys) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DVLOG(2) << __func__ << (success ? " succeeded" : " FAILED!");
-
- UMA_HISTOGRAM_BOOLEAN("Media.VideoDecodeStatsDB.OpSuccess.LoadKeys", success);
-
- if (success) {
- // Remove all keys.
- db_->UpdateEntries(
- std::make_unique<ProtoDecodeStatsEntry::KeyEntryVector>(),
- std::move(keys) /* keys_to_remove */,
- base::BindOnce(&VideoDecodeStatsDBImpl::OnStatsCleared,
- weak_ptr_factory_.GetWeakPtr(),
- std::move(clear_done_cb)));
- } else {
- // Fail silently. See comment in OnStatsCleared().
- std::move(clear_done_cb).Run();
- }
-}
-
-void VideoDecodeStatsDBImpl::OnStatsCleared(base::OnceClosure clear_done_cb,
+void VideoDecodeStatsDBImpl::OnStatsCleared(PendingOpId op_id,
+ base::OnceClosure clear_done_cb,
bool success) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DVLOG(2) << __func__ << (success ? " succeeded" : " FAILED!");
- UMA_HISTOGRAM_BOOLEAN("Media.VideoDecodeStatsDB.OpSuccess.Destroy", success);
+ CompletePendingOp(op_id);
+
+ UMA_HISTOGRAM_BOOLEAN("Media.VideoDecodeStatsDB.OpSuccess.Clear", success);
// We don't pass success to |clear_done_cb|. Clearing is best effort and
// there is no additional action for callers to take in case of failure.
diff --git a/chromium/media/capabilities/video_decode_stats_db_impl.h b/chromium/media/capabilities/video_decode_stats_db_impl.h
index f2c1cae047c..c9826ee9367 100644
--- a/chromium/media/capabilities/video_decode_stats_db_impl.h
+++ b/chromium/media/capabilities/video_decode_stats_db_impl.h
@@ -7,6 +7,8 @@
#include <memory>
+#include "base/cancelable_callback.h"
+#include "base/containers/flat_map.h"
#include "base/files/file_path.h"
#include "base/memory/weak_ptr.h"
#include "components/leveldb_proto/public/proto_database.h"
@@ -58,6 +60,8 @@ class MEDIA_EXPORT VideoDecodeStatsDBImpl : public VideoDecodeStatsDB {
private:
friend class VideoDecodeStatsDBImplTest;
+ using PendingOpId = int;
+
// Private constructor only called by tests (friends). Production code
// should always use the static Create() method.
VideoDecodeStatsDBImpl(
@@ -82,16 +86,63 @@ class MEDIA_EXPORT VideoDecodeStatsDBImpl : public VideoDecodeStatsDB {
// regardless of how many frames were decoded.
static bool GetEnableUnweightedEntries();
+ // Creates a PendingOperation using |uma_str| and adds it to |pending_ops_|
+ // map. Returns PendingOpId for newly started operation. Callers must later
+ // call CompletePendingOp() with this id to destroy the PendingOperation and
+ // finalize timing UMA.
+ PendingOpId StartPendingOp(std::string uma_str);
+
+ // Removes PendingOperation from |pending_ops_| using |op_id_| as a key. This
+ // destroys the object and triggers timing UMA.
+ void CompletePendingOp(PendingOpId op_id);
+
+ // Unified handler for timeouts of pending DB operations. PendingOperation
+ // will be notified that it timed out (to trigger timing UMA) and removed from
+ // |penidng_ops_|.
+ void OnPendingOpTimeout(PendingOpId id);
+
+ // Helper to report timing information for DB operations, including when they
+ // hang indefinitely.
+ class PendingOperation {
+ public:
+ PendingOperation(
+ std::string uma_str,
+ std::unique_ptr<base::CancelableOnceClosure> timeout_closure);
+ // Records task timing UMA if it hasn't already timed out.
+ virtual ~PendingOperation();
+
+ // Copies disallowed. Incompatible with move-only members and UMA logging in
+ // the destructor.
+ PendingOperation(const PendingOperation&) = delete;
+ PendingOperation& operator=(const PendingOperation&) = delete;
+
+ // Trigger UMA recording for timeout.
+ void OnTimeout();
+
+ private:
+ friend class VideoDecodeStatsDBImplTest;
+
+ std::string uma_str_;
+ std::unique_ptr<base::CancelableOnceClosure> timeout_closure_;
+ base::TimeTicks start_ticks_;
+ };
+
+ // Map of operation id -> outstanding PendingOperations.
+ base::flat_map<PendingOpId, std::unique_ptr<PendingOperation>> pending_ops_;
+
// Called when the database has been initialized. Will immediately call
// |init_cb| to forward |success|.
- void OnInit(InitializeCB init_cb, leveldb_proto::Enums::InitStatus status);
+ void OnInit(PendingOpId id,
+ InitializeCB init_cb,
+ leveldb_proto::Enums::InitStatus status);
// Returns true if the DB is successfully initialized.
bool IsInitialized();
// Passed as the callback for |OnGotDecodeStats| by |AppendDecodeStats| to
// update the database once we've read the existing stats entry.
- void WriteUpdatedEntry(const VideoDescKey& key,
+ void WriteUpdatedEntry(PendingOpId op_id,
+ const VideoDescKey& key,
const DecodeStatsEntry& entry,
AppendDecodeStatsCB append_done_cb,
bool read_success,
@@ -99,24 +150,23 @@ class MEDIA_EXPORT VideoDecodeStatsDBImpl : public VideoDecodeStatsDB {
// Called when the database has been modified after a call to
// |WriteUpdatedEntry|. Will run |append_done_cb| when done.
- void OnEntryUpdated(AppendDecodeStatsCB append_done_cb, bool success);
+ void OnEntryUpdated(PendingOpId op_id,
+ AppendDecodeStatsCB append_done_cb,
+ bool success);
// Called when GetDecodeStats() operation was performed. |get_stats_cb|
// will be run with |success| and a |DecodeStatsEntry| created from
// |stats_proto| or nullptr if no entry was found for the requested key.
- void OnGotDecodeStats(GetDecodeStatsCB get_stats_cb,
+ void OnGotDecodeStats(PendingOpId op_id,
+ GetDecodeStatsCB get_stats_cb,
bool success,
std::unique_ptr<DecodeStatsProto> stats_proto);
- // Internal callback for first step of ClearStats(). Will clear all stats If
- // |keys| fetched successfully.
- void OnLoadAllKeysForClearing(base::OnceClosure clear_done_cb,
- bool success,
- std::unique_ptr<std::vector<std::string>> keys);
-
// Internal callback for OnLoadAllKeysForClearing(), initially triggered by
// ClearStats(). Method simply logs |success| and runs |clear_done_cb|.
- void OnStatsCleared(base::OnceClosure clear_done_cb, bool success);
+ void OnStatsCleared(PendingOpId op_id,
+ base::OnceClosure clear_done_cb,
+ bool success);
// Return true if:
// values aren't corrupted nonsense (e.g. way more frames dropped than
@@ -130,6 +180,9 @@ class MEDIA_EXPORT VideoDecodeStatsDBImpl : public VideoDecodeStatsDB {
wall_clock_ = tick_clock;
}
+ // Next PendingOpId for use in |pending_ops_| map. See StartPendingOp().
+ PendingOpId next_op_id_ = 0;
+
// Indicates whether initialization is completed. Does not indicate whether it
// was successful. Will be reset upon calling DestroyStats(). Failed
// initialization is signaled by setting |db_| to null.
diff --git a/chromium/media/capabilities/video_decode_stats_db_impl_unittest.cc b/chromium/media/capabilities/video_decode_stats_db_impl_unittest.cc
index e3a8c742a0a..2c504964dbf 100644
--- a/chromium/media/capabilities/video_decode_stats_db_impl_unittest.cc
+++ b/chromium/media/capabilities/video_decode_stats_db_impl_unittest.cc
@@ -8,8 +8,8 @@
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/callback.h"
+#include "base/check.h"
#include "base/files/file_path.h"
-#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
@@ -65,6 +65,20 @@ class VideoDecodeStatsDBImplTest : public ::testing::Test {
std::unique_ptr<FakeDB<DecodeStatsProto>>(fake_db_)));
}
+ ~VideoDecodeStatsDBImplTest() override {
+ // Tests should always complete any pending operations
+ VerifyNoPendingOps();
+ }
+
+ void VerifyOnePendingOp(std::string op_name) {
+ EXPECT_EQ(stats_db_->pending_ops_.size(), 1u);
+ VideoDecodeStatsDBImpl::PendingOperation* pending_op =
+ stats_db_->pending_ops_.begin()->second.get();
+ EXPECT_EQ(pending_op->uma_str_, op_name);
+ }
+
+ void VerifyNoPendingOps() { EXPECT_TRUE(stats_db_->pending_ops_.empty()); }
+
int GetMaxFramesPerBuffer() {
return VideoDecodeStatsDBImpl::GetMaxFramesPerBuffer();
}
@@ -95,7 +109,9 @@ class VideoDecodeStatsDBImplTest : public ::testing::Test {
key, entry,
base::BindOnce(&VideoDecodeStatsDBImplTest::MockAppendDecodeStatsCb,
base::Unretained(this)));
+ VerifyOnePendingOp("Read");
fake_db_->GetCallback(true);
+ VerifyOnePendingOp("Write");
fake_db_->UpdateCallback(true);
testing::Mock::VerifyAndClearExpectations(this);
}
@@ -106,6 +122,7 @@ class VideoDecodeStatsDBImplTest : public ::testing::Test {
stats_db_->GetDecodeStats(
key, base::BindOnce(&VideoDecodeStatsDBImplTest::GetDecodeStatsCb,
base::Unretained(this)));
+ VerifyOnePendingOp("Read");
fake_db_->GetCallback(true);
testing::Mock::VerifyAndClearExpectations(this);
}
@@ -115,6 +132,7 @@ class VideoDecodeStatsDBImplTest : public ::testing::Test {
stats_db_->GetDecodeStats(
key, base::BindOnce(&VideoDecodeStatsDBImplTest::GetDecodeStatsCb,
base::Unretained(this)));
+ VerifyOnePendingOp("Read");
fake_db_->GetCallback(true);
testing::Mock::VerifyAndClearExpectations(this);
}
@@ -157,7 +175,8 @@ class VideoDecodeStatsDBImplTest : public ::testing::Test {
MOCK_METHOD0(MockClearStatsCb, void());
protected:
- base::test::TaskEnvironment task_environment_;
+ base::test::TaskEnvironment task_environment_{
+ base::test::TaskEnvironment::TimeSource::MOCK_TIME};
const VideoDescKey kStatsKeyVp9;
const VideoDescKey kStatsKeyAvc;
@@ -175,9 +194,30 @@ class VideoDecodeStatsDBImplTest : public ::testing::Test {
DISALLOW_COPY_AND_ASSIGN(VideoDecodeStatsDBImplTest);
};
-TEST_F(VideoDecodeStatsDBImplTest, FailedInitialize) {
+TEST_F(VideoDecodeStatsDBImplTest, InitializeFailed) {
+ stats_db_->Initialize(base::BindOnce(
+ &VideoDecodeStatsDBImplTest::OnInitialize, base::Unretained(this)));
+ EXPECT_CALL(*this, OnInitialize(false));
+ fake_db_->InitStatusCallback(leveldb_proto::Enums::InitStatus::kError);
+}
+
+TEST_F(VideoDecodeStatsDBImplTest, InitializeTimedOut) {
+ // Queue up an Initialize.
stats_db_->Initialize(base::BindOnce(
&VideoDecodeStatsDBImplTest::OnInitialize, base::Unretained(this)));
+ VerifyOnePendingOp("Initialize");
+
+ // Move time forward enough to trigger timeout.
+ EXPECT_CALL(*this, OnInitialize(_)).Times(0);
+ task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(100));
+ task_environment_.RunUntilIdle();
+
+ // Verify we didn't get an init callback and task is no longer considered
+ // pending (because it timed out).
+ testing::Mock::VerifyAndClearExpectations(this);
+ VerifyNoPendingOps();
+
+ // Verify callback still works if init completes very late.
EXPECT_CALL(*this, OnInitialize(false));
fake_db_->InitStatusCallback(leveldb_proto::Enums::InitStatus::kError);
}
@@ -204,9 +244,10 @@ TEST_F(VideoDecodeStatsDBImplTest, WriteReadAndClear) {
VerifyReadStats(kStatsKeyVp9, aggregate_entry);
// Clear all stats from the DB.
+ EXPECT_CALL(*this, MockClearStatsCb);
stats_db_->ClearStats(base::BindOnce(
&VideoDecodeStatsDBImplTest::MockClearStatsCb, base::Unretained(this)));
- fake_db_->LoadKeysCallback(true);
+ VerifyOnePendingOp("Clear");
fake_db_->UpdateCallback(true);
// Database is now empty. Expect null entry.
diff --git a/chromium/media/capture/BUILD.gn b/chromium/media/capture/BUILD.gn
index 0f253c9e858..06e45ca2886 100644
--- a/chromium/media/capture/BUILD.gn
+++ b/chromium/media/capture/BUILD.gn
@@ -309,6 +309,14 @@ jumbo_component("capture_lib") {
sources += [
"video/fuchsia/video_capture_device_factory_fuchsia.cc",
"video/fuchsia/video_capture_device_factory_fuchsia.h",
+ "video/fuchsia/video_capture_device_fuchsia.cc",
+ "video/fuchsia/video_capture_device_fuchsia.h",
+ ]
+ deps += [
+ "//media/fuchsia/common",
+ "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.camera3",
+ "//third_party/fuchsia-sdk/sdk/pkg/sys_cpp",
+ "//third_party/libyuv",
]
}
}
@@ -385,6 +393,16 @@ test("capture_unittests") {
]
}
+ if (is_fuchsia) {
+ deps += [
+ "//media/fuchsia/camera:test_support",
+ "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.camera3",
+ "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.sysmem",
+ "//third_party/fuchsia-sdk/sdk/pkg/sys_cpp",
+ ]
+ sources += [ "video/fuchsia/video_capture_device_fuchsia_test.cc" ]
+ }
+
if (is_win) {
sources += [
"video/win/video_capture_device_factory_win_unittest.cc",
diff --git a/chromium/media/capture/capture_switches.cc b/chromium/media/capture/capture_switches.cc
index 6f88b435bd4..6348e859089 100644
--- a/chromium/media/capture/capture_switches.cc
+++ b/chromium/media/capture/capture_switches.cc
@@ -10,4 +10,14 @@ namespace switches {
const char kVideoCaptureUseGpuMemoryBuffer[] =
"video-capture-use-gpu-memory-buffer";
+// This is for the same feature controlled by kVideoCaptureUseGpuMemoryBuffer.
+// kVideoCaptureUseGpuMemoryBuffer is settled by chromeos overlays. This flag is
+// necessary to overwrite the settings via chrome:// flag. The behavior of
+// chrome://flag#zero-copy-video-capture is as follows;
+// Default : Respect chromeos overlays settings.
+// Enabled : Force to enable kVideoCaptureUseGpuMemoryBuffer.
+// Disabled : Force to disable kVideoCaptureUseGpuMemoryBuffer.
+const char kDisableVideoCaptureUseGpuMemoryBuffer[] =
+ "disable-video-capture-use-gpu-memory-buffer";
+
} // namespace switches
diff --git a/chromium/media/capture/capture_switches.h b/chromium/media/capture/capture_switches.h
index cccc92ae5a4..dfc352ab54d 100644
--- a/chromium/media/capture/capture_switches.h
+++ b/chromium/media/capture/capture_switches.h
@@ -11,6 +11,8 @@ namespace switches {
CAPTURE_EXPORT extern const char kVideoCaptureUseGpuMemoryBuffer[];
+CAPTURE_EXPORT extern const char kDisableVideoCaptureUseGpuMemoryBuffer[];
+
} // namespace switches
#endif // MEDIA_CAPTURE_CAPTURE_SWITCHES_H_
diff --git a/chromium/media/capture/content/capture_resolution_chooser.cc b/chromium/media/capture/content/capture_resolution_chooser.cc
index f665bf30dc5..496ae311fa9 100644
--- a/chromium/media/capture/content/capture_resolution_chooser.cc
+++ b/chromium/media/capture/content/capture_resolution_chooser.cc
@@ -7,6 +7,7 @@
#include <algorithm>
#include <limits>
+#include "base/logging.h"
#include "base/strings/string_util.h"
#include "media/base/video_util.h"
diff --git a/chromium/media/capture/mojom/video_capture_types.mojom b/chromium/media/capture/mojom/video_capture_types.mojom
index 5b09aac62ad..eaeff60009f 100644
--- a/chromium/media/capture/mojom/video_capture_types.mojom
+++ b/chromium/media/capture/mojom/video_capture_types.mojom
@@ -74,6 +74,7 @@ enum VideoCaptureApi {
ANDROID_API2_LEGACY,
ANDROID_API2_FULL,
ANDROID_API2_LIMITED,
+ FUCHSIA_CAMERA3,
VIRTUAL_DEVICE,
UNKNOWN
};
@@ -213,7 +214,14 @@ enum VideoCaptureError {
kMacDeckLinkUnsupportedPixelFormat,
kMacAvFoundationReceivedAVCaptureSessionRuntimeErrorNotification,
kAndroidApi2ErrorConfiguringCamera,
- kCrosHalV3DeviceDelegateFailedToFlush
+ kCrosHalV3DeviceDelegateFailedToFlush,
+ kFuchsiaCameraDeviceDisconnected,
+ kFuchsiaCameraStreamDisconnected,
+ kFuchsiaSysmemDidNotSetImageFormat,
+ kFuchsiaSysmemInvalidBufferIndex,
+ kFuchsiaSysmemInvalidBufferSize,
+ kFuchsiaUnsupportedPixelFormat,
+ kFuchsiaFailedToMapSysmemBuffer,
};
enum VideoCaptureFrameDropReason {
diff --git a/chromium/media/capture/mojom/video_capture_types_mojom_traits.cc b/chromium/media/capture/mojom/video_capture_types_mojom_traits.cc
index 32dc4185a8c..a910706f1ac 100644
--- a/chromium/media/capture/mojom/video_capture_types_mojom_traits.cc
+++ b/chromium/media/capture/mojom/video_capture_types_mojom_traits.cc
@@ -686,6 +686,21 @@ EnumTraits<media::mojom::VideoCaptureError, media::VideoCaptureError>::ToMojom(
case media::VideoCaptureError::kCrosHalV3DeviceDelegateFailedToFlush:
return media::mojom::VideoCaptureError::
kCrosHalV3DeviceDelegateFailedToFlush;
+ case media::VideoCaptureError::kFuchsiaCameraDeviceDisconnected:
+ return media::mojom::VideoCaptureError::kFuchsiaCameraDeviceDisconnected;
+ case media::VideoCaptureError::kFuchsiaCameraStreamDisconnected:
+ return media::mojom::VideoCaptureError::kFuchsiaCameraStreamDisconnected;
+ case media::VideoCaptureError::kFuchsiaSysmemDidNotSetImageFormat:
+ return media::mojom::VideoCaptureError::
+ kFuchsiaSysmemDidNotSetImageFormat;
+ case media::VideoCaptureError::kFuchsiaSysmemInvalidBufferIndex:
+ return media::mojom::VideoCaptureError::kFuchsiaSysmemInvalidBufferIndex;
+ case media::VideoCaptureError::kFuchsiaSysmemInvalidBufferSize:
+ return media::mojom::VideoCaptureError::kFuchsiaSysmemInvalidBufferSize;
+ case media::VideoCaptureError::kFuchsiaUnsupportedPixelFormat:
+ return media::mojom::VideoCaptureError::kFuchsiaUnsupportedPixelFormat;
+ case media::VideoCaptureError::kFuchsiaFailedToMapSysmemBuffer:
+ return media::mojom::VideoCaptureError::kFuchsiaFailedToMapSysmemBuffer;
}
NOTREACHED();
return media::mojom::VideoCaptureError::kNone;
@@ -1207,6 +1222,27 @@ bool EnumTraits<media::mojom::VideoCaptureError, media::VideoCaptureError>::
case media::mojom::VideoCaptureError::kCrosHalV3DeviceDelegateFailedToFlush:
*output = media::VideoCaptureError::kCrosHalV3DeviceDelegateFailedToFlush;
return true;
+ case media::mojom::VideoCaptureError::kFuchsiaCameraDeviceDisconnected:
+ *output = media::VideoCaptureError::kFuchsiaCameraDeviceDisconnected;
+ return true;
+ case media::mojom::VideoCaptureError::kFuchsiaCameraStreamDisconnected:
+ *output = media::VideoCaptureError::kFuchsiaCameraStreamDisconnected;
+ return true;
+ case media::mojom::VideoCaptureError::kFuchsiaSysmemDidNotSetImageFormat:
+ *output = media::VideoCaptureError::kFuchsiaSysmemDidNotSetImageFormat;
+ return true;
+ case media::mojom::VideoCaptureError::kFuchsiaSysmemInvalidBufferIndex:
+ *output = media::VideoCaptureError::kFuchsiaSysmemInvalidBufferIndex;
+ return true;
+ case media::mojom::VideoCaptureError::kFuchsiaSysmemInvalidBufferSize:
+ *output = media::VideoCaptureError::kFuchsiaSysmemInvalidBufferSize;
+ return true;
+ case media::mojom::VideoCaptureError::kFuchsiaUnsupportedPixelFormat:
+ *output = media::VideoCaptureError::kFuchsiaUnsupportedPixelFormat;
+ return true;
+ case media::mojom::VideoCaptureError::kFuchsiaFailedToMapSysmemBuffer:
+ *output = media::VideoCaptureError::kFuchsiaFailedToMapSysmemBuffer;
+ return true;
}
NOTREACHED();
return false;
@@ -1510,6 +1546,8 @@ EnumTraits<media::mojom::VideoCaptureApi, media::VideoCaptureApi>::ToMojom(
return media::mojom::VideoCaptureApi::ANDROID_API2_FULL;
case media::VideoCaptureApi::ANDROID_API2_LIMITED:
return media::mojom::VideoCaptureApi::ANDROID_API2_LIMITED;
+ case media::VideoCaptureApi::FUCHSIA_CAMERA3:
+ return media::mojom::VideoCaptureApi::FUCHSIA_CAMERA3;
case media::VideoCaptureApi::VIRTUAL_DEVICE:
return media::mojom::VideoCaptureApi::VIRTUAL_DEVICE;
case media::VideoCaptureApi::UNKNOWN:
@@ -1554,6 +1592,9 @@ bool EnumTraits<media::mojom::VideoCaptureApi, media::VideoCaptureApi>::
case media::mojom::VideoCaptureApi::ANDROID_API2_LIMITED:
*output = media::VideoCaptureApi::ANDROID_API2_LIMITED;
return true;
+ case media::mojom::VideoCaptureApi::FUCHSIA_CAMERA3:
+ *output = media::VideoCaptureApi::FUCHSIA_CAMERA3;
+ return true;
case media::mojom::VideoCaptureApi::VIRTUAL_DEVICE:
*output = media::VideoCaptureApi::VIRTUAL_DEVICE;
return true;
diff --git a/chromium/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera2.java b/chromium/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera2.java
index 7a1ad8db0e1..f849089585b 100644
--- a/chromium/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera2.java
+++ b/chromium/media/capture/video/android/java/src/org/chromium/media/VideoCaptureCamera2.java
@@ -575,7 +575,9 @@ public class VideoCaptureCamera2 extends VideoCapture {
}
}
try {
- if (cameraCharacteristics.get(CameraCharacteristics.CONTROL_AE_LOCK_AVAILABLE)) {
+ Boolean ae_lock_available =
+ cameraCharacteristics.get(CameraCharacteristics.CONTROL_AE_LOCK_AVAILABLE);
+ if (ae_lock_available != null && ae_lock_available.booleanValue()) {
exposureModes.add(Integer.valueOf(AndroidMeteringMode.FIXED));
}
} catch (NoSuchFieldError e) {
@@ -622,7 +624,9 @@ public class VideoCaptureCamera2 extends VideoCapture {
}
}
try {
- if (cameraCharacteristics.get(CameraCharacteristics.CONTROL_AWB_LOCK_AVAILABLE)) {
+ Boolean awb_lock_available =
+ cameraCharacteristics.get(CameraCharacteristics.CONTROL_AWB_LOCK_AVAILABLE);
+ if (awb_lock_available != null && awb_lock_available.booleanValue()) {
whiteBalanceModes.add(Integer.valueOf(AndroidMeteringMode.FIXED));
}
} catch (NoSuchFieldError e) {
diff --git a/chromium/media/capture/video/chromeos/camera_app_device_impl.cc b/chromium/media/capture/video/chromeos/camera_app_device_impl.cc
index 3120a85871d..d81afe5b3e5 100644
--- a/chromium/media/capture/video/chromeos/camera_app_device_impl.cc
+++ b/chromium/media/capture/video/chromeos/camera_app_device_impl.cc
@@ -28,13 +28,6 @@ ReprocessTask::ReprocessTask(ReprocessTask&& other)
ReprocessTask::~ReprocessTask() = default;
-bool CameraAppDeviceImpl::SizeComparator::operator()(
- const gfx::Size& size_1,
- const gfx::Size& size_2) const {
- return size_1.width() < size_2.width() || (size_1.width() == size_2.width() &&
- size_1.height() < size_2.height());
-}
-
// static
int CameraAppDeviceImpl::GetReprocessReturnCode(
cros::mojom::Effect effect,
@@ -107,16 +100,16 @@ void CameraAppDeviceImpl::ConsumeReprocessOptions(
std::move(consumption_callback).Run(std::move(result_task_queue));
}
-void CameraAppDeviceImpl::GetFpsRange(const gfx::Size& resolution,
- GetFpsRangeCallback callback) {
+base::Optional<gfx::Range> CameraAppDeviceImpl::GetFpsRange() {
base::AutoLock lock(fps_ranges_lock_);
- auto it = resolution_fps_range_map_.find(resolution);
- if (it == resolution_fps_range_map_.end()) {
- std::move(callback).Run({});
- return;
- }
- std::move(callback).Run(it->second);
+ return specified_fps_range_;
+}
+
+gfx::Size CameraAppDeviceImpl::GetStillCaptureResolution() {
+ base::AutoLock lock(still_capture_resolution_lock_);
+
+ return still_capture_resolution_;
}
cros::mojom::CaptureIntent CameraAppDeviceImpl::GetCaptureIntent() {
@@ -144,19 +137,6 @@ void CameraAppDeviceImpl::OnShutterDone() {
}
}
-void CameraAppDeviceImpl::SetReprocessResult(
- SetReprocessOptionCallback callback,
- const int32_t status,
- media::mojom::BlobPtr blob) {
- auto callback_on_mojo_thread = base::BindOnce(
- [](const int32_t status, media::mojom::BlobPtr blob,
- SetReprocessOptionCallback callback) {
- std::move(callback).Run(status, std::move(blob));
- },
- status, std::move(blob), std::move(callback));
- task_runner_->PostTask(FROM_HERE, std::move(callback_on_mojo_thread));
-}
-
void CameraAppDeviceImpl::GetCameraInfo(GetCameraInfoCallback callback) {
DCHECK(camera_info_);
std::move(callback).Run(camera_info_.Clone());
@@ -183,8 +163,7 @@ void CameraAppDeviceImpl::SetReprocessOption(
reprocess_task_queue_.push(std::move(task));
}
-void CameraAppDeviceImpl::SetFpsRange(const gfx::Size& resolution,
- const gfx::Range& fps_range,
+void CameraAppDeviceImpl::SetFpsRange(const gfx::Range& fps_range,
SetFpsRangeCallback callback) {
const int entry_length = 2;
@@ -208,19 +187,20 @@ void CameraAppDeviceImpl::SetFpsRange(const gfx::Size& resolution,
base::AutoLock lock(fps_ranges_lock_);
- if (!is_valid) {
- // If the input range is invalid, we should still clear the cache range so
- // that it will fallback to use default fps range rather than the cache one.
- auto it = resolution_fps_range_map_.find(resolution);
- if (it != resolution_fps_range_map_.end()) {
- resolution_fps_range_map_.erase(it);
- }
- std::move(callback).Run(false);
- return;
+ if (is_valid) {
+ specified_fps_range_ = fps_range;
+ } else {
+ specified_fps_range_ = {};
}
+ std::move(callback).Run(is_valid);
+}
- resolution_fps_range_map_[resolution] = fps_range;
- std::move(callback).Run(true);
+void CameraAppDeviceImpl::SetStillCaptureResolution(
+ const gfx::Size& resolution,
+ SetStillCaptureResolutionCallback callback) {
+ base::AutoLock lock(still_capture_resolution_lock_);
+ still_capture_resolution_ = resolution;
+ std::move(callback).Run();
}
void CameraAppDeviceImpl::SetCaptureIntent(
@@ -294,4 +274,17 @@ void CameraAppDeviceImpl::DisableEeNr(ReprocessTask* task) {
task->extra_metadata.push_back(std::move(nr_entry));
}
+void CameraAppDeviceImpl::SetReprocessResult(
+ SetReprocessOptionCallback callback,
+ const int32_t status,
+ media::mojom::BlobPtr blob) {
+ auto callback_on_mojo_thread = base::BindOnce(
+ [](const int32_t status, media::mojom::BlobPtr blob,
+ SetReprocessOptionCallback callback) {
+ std::move(callback).Run(status, std::move(blob));
+ },
+ status, std::move(blob), std::move(callback));
+ task_runner_->PostTask(FROM_HERE, std::move(callback_on_mojo_thread));
+}
+
} // namespace media
diff --git a/chromium/media/capture/video/chromeos/camera_app_device_impl.h b/chromium/media/capture/video/chromeos/camera_app_device_impl.h
index 2f4ae3a20c5..a0853f0ac2e 100644
--- a/chromium/media/capture/video/chromeos/camera_app_device_impl.h
+++ b/chromium/media/capture/video/chromeos/camera_app_device_impl.h
@@ -42,22 +42,19 @@ constexpr uint32_t kPortraitModeVendorKey = 0x80000000;
constexpr uint32_t kPortraitModeSegmentationResultVendorKey = 0x80000001;
constexpr int32_t kReprocessSuccess = 0;
+// Implementation of CameraAppDevice that is used as the ommunication bridge
+// between Chrome Camera App (CCA) and the ChromeOS Video Capture Device. By
+// using this, we can do more complicated operations on cameras which is not
+// supported by Chrome API.
class CAPTURE_EXPORT CameraAppDeviceImpl : public cros::mojom::CameraAppDevice {
public:
- struct SizeComparator {
- bool operator()(const gfx::Size& size_1, const gfx::Size& size_2) const;
- };
-
- using GetFpsRangeCallback =
- base::OnceCallback<void(base::Optional<gfx::Range>)>;
-
- using ResolutionFpsRangeMap =
- base::flat_map<gfx::Size, gfx::Range, SizeComparator>;
-
+ // Retrieve the return code for reprocess |effect| from the |metadata|.
static int GetReprocessReturnCode(
cros::mojom::Effect effect,
const cros::mojom::CameraMetadataPtr* metadata);
+ // Construct a ReprocessTaskQueue for regular capture with
+ // |take_photo_callback|.
static ReprocessTaskQueue GetSingleShotReprocessOptions(
media::mojom::ImageCapture::TakePhotoCallback take_photo_callback);
@@ -65,52 +62,58 @@ class CAPTURE_EXPORT CameraAppDeviceImpl : public cros::mojom::CameraAppDevice {
cros::mojom::CameraInfoPtr camera_info);
~CameraAppDeviceImpl() override;
+ // Binds the mojo receiver to this implementation.
void BindReceiver(
mojo::PendingReceiver<cros::mojom::CameraAppDevice> receiver);
+ // Consumes all the pending reprocess tasks if there is any and eventually
+ // generates a ReprocessTaskQueue which contains:
+ // 1. A regular capture task with |take_photo_callback|.
+ // 2. One or more reprocess tasks if there is any.
+ // And passes the generated ReprocessTaskQueue through |consumption_callback|.
void ConsumeReprocessOptions(
media::mojom::ImageCapture::TakePhotoCallback take_photo_callback,
base::OnceCallback<void(ReprocessTaskQueue)> consumption_callback);
- void GetFpsRange(const gfx::Size& resolution, GetFpsRangeCallback callback);
+ // Retrieves the fps range if it is specified by the app.
+ base::Optional<gfx::Range> GetFpsRange();
+
+ // Retrieves the corresponding capture resolution which is specified by the
+ // app.
+ gfx::Size GetStillCaptureResolution();
+ // Gets the capture intent which is specified by the app.
cros::mojom::CaptureIntent GetCaptureIntent();
+ // Delivers the result |metadata| with its |stream_type| to the metadata
+ // observers.
void OnResultMetadataAvailable(const cros::mojom::CameraMetadataPtr& metadata,
const cros::mojom::StreamType stream_type);
+ // Notifies the camera event observers that the shutter is finished.
void OnShutterDone();
- void SetReprocessResult(SetReprocessOptionCallback callback,
- const int32_t status,
- media::mojom::BlobPtr blob);
-
// cros::mojom::CameraAppDevice implementations.
void GetCameraInfo(GetCameraInfoCallback callback) override;
-
void SetReprocessOption(cros::mojom::Effect effect,
SetReprocessOptionCallback callback) override;
-
- void SetFpsRange(const gfx::Size& resolution,
- const gfx::Range& fps_range,
+ void SetFpsRange(const gfx::Range& fps_range,
SetFpsRangeCallback callback) override;
-
+ void SetStillCaptureResolution(
+ const gfx::Size& resolution,
+ SetStillCaptureResolutionCallback callback) override;
void SetCaptureIntent(cros::mojom::CaptureIntent capture_intent,
SetCaptureIntentCallback callback) override;
-
void AddResultMetadataObserver(
mojo::PendingRemote<cros::mojom::ResultMetadataObserver> observer,
cros::mojom::StreamType streamType,
AddResultMetadataObserverCallback callback) override;
-
void RemoveResultMetadataObserver(
uint32_t id,
RemoveResultMetadataObserverCallback callback) override;
-
void AddCameraEventObserver(
mojo::PendingRemote<cros::mojom::CameraEventObserver> observer,
AddCameraEventObserverCallback callback) override;
-
void RemoveCameraEventObserver(
uint32_t id,
RemoveCameraEventObserverCallback callback) override;
@@ -118,6 +121,10 @@ class CAPTURE_EXPORT CameraAppDeviceImpl : public cros::mojom::CameraAppDevice {
private:
static void DisableEeNr(ReprocessTask* task);
+ void SetReprocessResult(SetReprocessOptionCallback callback,
+ const int32_t status,
+ media::mojom::BlobPtr blob);
+
std::string device_id_;
mojo::ReceiverSet<cros::mojom::CameraAppDevice> receivers_;
@@ -126,34 +133,38 @@ class CAPTURE_EXPORT CameraAppDeviceImpl : public cros::mojom::CameraAppDevice {
const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+ // The queue will be enqueued and dequeued from different threads.
base::Lock reprocess_tasks_lock_;
+ base::queue<ReprocessTask> reprocess_task_queue_
+ GUARDED_BY(reprocess_tasks_lock_);
- base::queue<ReprocessTask> reprocess_task_queue_;
-
+ // It will be inserted and read from different threads.
base::Lock fps_ranges_lock_;
+ base::Optional<gfx::Range> specified_fps_range_ GUARDED_BY(fps_ranges_lock_);
- ResolutionFpsRangeMap resolution_fps_range_map_;
+ // It will be inserted and read from different threads.
+ base::Lock still_capture_resolution_lock_;
+ gfx::Size still_capture_resolution_
+ GUARDED_BY(still_capture_resolution_lock_);
+ // It will be modified and read from different threads.
base::Lock capture_intent_lock_;
+ cros::mojom::CaptureIntent capture_intent_ GUARDED_BY(capture_intent_lock_);
- cros::mojom::CaptureIntent capture_intent_;
-
+ // Those maps will be changed and used from different threads.
base::Lock metadata_observers_lock_;
-
- base::Lock camera_event_observers_lock_;
-
- uint32_t next_metadata_observer_id_;
-
- uint32_t next_camera_event_observer_id_;
-
+ uint32_t next_metadata_observer_id_ GUARDED_BY(metadata_observers_lock_);
base::flat_map<uint32_t, mojo::Remote<cros::mojom::ResultMetadataObserver>>
- metadata_observers_;
-
+ metadata_observers_ GUARDED_BY(metadata_observers_lock_);
base::flat_map<cros::mojom::StreamType, base::flat_set<uint32_t>>
- stream_metadata_observer_ids_;
+ stream_metadata_observer_ids_ GUARDED_BY(metadata_observers_lock_);
+ // Those maps will be changed and used from different threads.
+ base::Lock camera_event_observers_lock_;
+ uint32_t next_camera_event_observer_id_
+ GUARDED_BY(camera_event_observers_lock_);
base::flat_map<uint32_t, mojo::Remote<cros::mojom::CameraEventObserver>>
- camera_event_observers_;
+ camera_event_observers_ GUARDED_BY(camera_event_observers_lock_);
std::unique_ptr<base::WeakPtrFactory<CameraAppDeviceImpl>> weak_ptr_factory_;
diff --git a/chromium/media/capture/video/chromeos/camera_device_delegate.cc b/chromium/media/capture/video/chromeos/camera_device_delegate.cc
index 8d5fae0d635..c361ae56ad9 100644
--- a/chromium/media/capture/video/chromeos/camera_device_delegate.cc
+++ b/chromium/media/capture/video/chromeos/camera_device_delegate.cc
@@ -621,25 +621,15 @@ void CameraDeviceDelegate::ConfigureStreams(
int32_t blob_width = 0;
int32_t blob_height = 0;
if (require_photo) {
- std::vector<gfx::Size> blob_resolutions;
- GetStreamResolutions(
- static_metadata_, cros::mojom::Camera3StreamType::CAMERA3_STREAM_OUTPUT,
- cros::mojom::HalPixelFormat::HAL_PIXEL_FORMAT_BLOB, &blob_resolutions);
- if (blob_resolutions.empty()) {
+ auto blob_resolution = GetBlobResolution(new_blob_resolution);
+ if (blob_resolution.IsEmpty()) {
LOG(ERROR) << "Failed to configure streans: No BLOB resolution found.";
return;
}
- if (new_blob_resolution.has_value() &&
- std::find(blob_resolutions.begin(), blob_resolutions.end(),
- *new_blob_resolution) != blob_resolutions.end()) {
- blob_width = new_blob_resolution->width();
- blob_height = new_blob_resolution->height();
- } else {
- // Use the largest resolution as default.
- blob_width = blob_resolutions.back().width();
- blob_height = blob_resolutions.back().height();
- }
+ blob_width = blob_resolution.width();
+ blob_height = blob_resolution.height();
+
cros::mojom::Camera3StreamPtr still_capture_stream =
cros::mojom::Camera3Stream::New();
still_capture_stream->id = static_cast<uint64_t>(StreamType::kJpegOutput);
@@ -875,11 +865,7 @@ void CameraDeviceDelegate::OnConstructedDefaultPreviewRequestSettings(
}
if (camera_app_device_) {
- camera_app_device_->GetFpsRange(
- chrome_capture_params_.requested_format.frame_size,
- media::BindToCurrentLoop(
- base::BindOnce(&CameraDeviceDelegate::OnGotFpsRange, GetWeakPtr(),
- std::move(settings))));
+ OnGotFpsRange(std::move(settings), camera_app_device_->GetFpsRange());
} else {
OnGotFpsRange(std::move(settings), {});
}
@@ -947,6 +933,35 @@ void CameraDeviceDelegate::OnGotFpsRange(
}
}
+gfx::Size CameraDeviceDelegate::GetBlobResolution(
+ base::Optional<gfx::Size> new_blob_resolution) {
+ std::vector<gfx::Size> blob_resolutions;
+ GetStreamResolutions(
+ static_metadata_, cros::mojom::Camera3StreamType::CAMERA3_STREAM_OUTPUT,
+ cros::mojom::HalPixelFormat::HAL_PIXEL_FORMAT_BLOB, &blob_resolutions);
+ if (blob_resolutions.empty()) {
+ return {};
+ }
+
+ // Try the given blob resolution first. If it is invalid, try the
+ // resolution specified through Mojo API as a fallback. If it fails too,
+ // use the largest resolution as default.
+ if (new_blob_resolution.has_value() &&
+ base::Contains(blob_resolutions, *new_blob_resolution)) {
+ return *new_blob_resolution;
+ }
+
+ if (camera_app_device_) {
+ auto specified_capture_resolution =
+ camera_app_device_->GetStillCaptureResolution();
+ if (!specified_capture_resolution.IsEmpty() &&
+ base::Contains(blob_resolutions, specified_capture_resolution)) {
+ return specified_capture_resolution;
+ }
+ }
+ return blob_resolutions.back();
+}
+
void CameraDeviceDelegate::ProcessCaptureRequest(
cros::mojom::Camera3CaptureRequestPtr request,
base::OnceCallback<void(int32_t)> callback) {
diff --git a/chromium/media/capture/video/chromeos/camera_device_delegate.h b/chromium/media/capture/video/chromeos/camera_device_delegate.h
index 7440e2b5910..a8aceeea4c0 100644
--- a/chromium/media/capture/video/chromeos/camera_device_delegate.h
+++ b/chromium/media/capture/video/chromeos/camera_device_delegate.h
@@ -164,6 +164,8 @@ class CAPTURE_EXPORT CameraDeviceDelegate final {
void OnGotFpsRange(cros::mojom::CameraMetadataPtr settings,
base::Optional<gfx::Range> specified_fps_range);
+ gfx::Size GetBlobResolution(base::Optional<gfx::Size> new_blob_resolution);
+
// StreamCaptureInterface implementations. These methods are called by
// |stream_buffer_manager_| on |ipc_task_runner_|.
void ProcessCaptureRequest(cros::mojom::Camera3CaptureRequestPtr request,
diff --git a/chromium/media/capture/video/chromeos/gpu_memory_buffer_tracker.cc b/chromium/media/capture/video/chromeos/gpu_memory_buffer_tracker.cc
index dc3b45df656..a1be18153d4 100644
--- a/chromium/media/capture/video/chromeos/gpu_memory_buffer_tracker.cc
+++ b/chromium/media/capture/video/chromeos/gpu_memory_buffer_tracker.cc
@@ -4,7 +4,8 @@
#include "media/capture/video/chromeos/gpu_memory_buffer_tracker.h"
-#include "base/logging.h"
+#include "base/check.h"
+#include "base/notreached.h"
#include "media/capture/video/chromeos/pixel_format_utils.h"
#include "media/capture/video/video_capture_buffer_handle.h"
#include "ui/gfx/geometry/size.h"
diff --git a/chromium/media/capture/video/chromeos/mock_vendor_tag_ops.cc b/chromium/media/capture/video/chromeos/mock_vendor_tag_ops.cc
index cb828d2de33..07529a63bbe 100644
--- a/chromium/media/capture/video/chromeos/mock_vendor_tag_ops.cc
+++ b/chromium/media/capture/video/chromeos/mock_vendor_tag_ops.cc
@@ -5,7 +5,7 @@
#include "media/capture/video/chromeos/mock_vendor_tag_ops.h"
#include "base/bind.h"
-#include "base/logging.h"
+#include "base/check.h"
#include "base/synchronization/waitable_event.h"
namespace media {
diff --git a/chromium/media/capture/video/chromeos/mojom/camera_app.mojom b/chromium/media/capture/video/chromeos/mojom/camera_app.mojom
index 9f4d63d0ec0..7bc0da4e710 100644
--- a/chromium/media/capture/video/chromeos/mojom/camera_app.mojom
+++ b/chromium/media/capture/video/chromeos/mojom/camera_app.mojom
@@ -86,12 +86,15 @@ interface CameraAppDevice {
=> (int32 status, media.mojom.Blob? blob);
// Sets the fps range for upcoming configured camera stream.
- // The caller sets the |fps_range| for target |resolution|.
+ // The caller sets the |fps_range|.
// If the given fps range is valid and set successfully, |is_success| returns
// true. If the given fps range is invalid, the fps range which is cached
// previously will be cleared and |is_success| will return false.
- SetFpsRange(gfx.mojom.Size resolution, gfx.mojom.Range fps_range)
- => (bool is_success);
+ SetFpsRange(gfx.mojom.Range fps_range) => (bool is_success);
+
+ // Sets the |resolution| for still capture stream which should be
+ // configured later so that we can configure the most suitable properties.
+ SetStillCaptureResolution(gfx.mojom.Size resolution) => ();
// Sets the intent for the upcoming capture session. The underlying video
// capture device should configure the streams accordingly. Returns an empty
diff --git a/chromium/media/capture/video/chromeos/request_manager.cc b/chromium/media/capture/video/chromeos/request_manager.cc
index 4908f3d8c34..9b93346ad4f 100644
--- a/chromium/media/capture/video/chromeos/request_manager.cc
+++ b/chromium/media/capture/video/chromeos/request_manager.cc
@@ -227,11 +227,10 @@ void RequestManager::UnsetRepeatingCaptureMetadata(
}
void RequestManager::SetJpegOrientation(
- cros::mojom::CameraMetadataPtr* settings) {
+ cros::mojom::CameraMetadataPtr* settings,
+ int32_t orientation) {
auto e = BuildMetadataEntry(
- cros::mojom::CameraMetadataTag::ANDROID_JPEG_ORIENTATION,
- base::checked_cast<int32_t>(
- device_context_->GetCameraFrameOrientation()));
+ cros::mojom::CameraMetadataTag::ANDROID_JPEG_ORIENTATION, orientation);
AddOrUpdateMetadataEntry(settings, std::move(e));
}
@@ -319,6 +318,7 @@ void RequestManager::PrepareCaptureRequest() {
pending_result.input_buffer_id = input_buffer_id;
pending_result.reprocess_effect = reprocess_effect;
pending_result.still_capture_callback = std::move(callback);
+ pending_result.orientation = device_context_->GetCameraFrameOrientation();
// For reprocess supported devices, bind the ReprocessTaskQueue with this
// frame number. Once the shot result is returned, we will rebind the
@@ -377,7 +377,7 @@ bool RequestManager::TryPrepareReprocessRequest(
// Prepare metadata by adding extra metadata.
*settings = reprocess_job_info->metadata.Clone();
SetSensorTimestamp(settings, reprocess_job_info->shutter_timestamp);
- SetJpegOrientation(settings);
+ SetJpegOrientation(settings, reprocess_job_info->orientation);
for (auto& metadata : task.extra_metadata) {
AddOrUpdateMetadataEntry(settings, std::move(metadata));
}
@@ -440,7 +440,7 @@ bool RequestManager::TryPrepareOneShotRequest(
take_photo_callback_queue_.pop();
*settings = std::move(take_photo_settings_queue_.front());
- SetJpegOrientation(settings);
+ SetJpegOrientation(settings, device_context_->GetCameraFrameOrientation());
}
SetZeroShutterLag(settings, true);
take_photo_settings_queue_.pop();
@@ -795,7 +795,8 @@ void RequestManager::SubmitCaptureResult(
DCHECK_GT(pending_result.shutter_timestamp, 0UL);
ReprocessJobInfo reprocess_job_info(
std::move(frame_number_reprocess_tasks_map_[frame_number]),
- std::move(pending_result.metadata), pending_result.shutter_timestamp);
+ std::move(pending_result.metadata), pending_result.shutter_timestamp,
+ pending_result.orientation);
buffer_id_reprocess_job_info_map_.emplace(buffer_ipc_id,
std::move(reprocess_job_info));
frame_number_reprocess_tasks_map_.erase(frame_number);
@@ -947,15 +948,18 @@ RequestManager::CaptureResult::~CaptureResult() = default;
RequestManager::ReprocessJobInfo::ReprocessJobInfo(
ReprocessTaskQueue queue,
cros::mojom::CameraMetadataPtr metadata,
- uint64_t timestamp)
+ uint64_t timestamp,
+ int32_t orientation)
: task_queue(std::move(queue)),
metadata(std::move(metadata)),
- shutter_timestamp(timestamp) {}
+ shutter_timestamp(timestamp),
+ orientation(orientation) {}
RequestManager::ReprocessJobInfo::ReprocessJobInfo(ReprocessJobInfo&& info)
: task_queue(std::move(info.task_queue)),
metadata(std::move(info.metadata)),
- shutter_timestamp(info.shutter_timestamp) {}
+ shutter_timestamp(info.shutter_timestamp),
+ orientation(info.orientation) {}
RequestManager::ReprocessJobInfo::~ReprocessJobInfo() = default;
diff --git a/chromium/media/capture/video/chromeos/request_manager.h b/chromium/media/capture/video/chromeos/request_manager.h
index 4d3afac35f6..0f2c89e1601 100644
--- a/chromium/media/capture/video/chromeos/request_manager.h
+++ b/chromium/media/capture/video/chromeos/request_manager.h
@@ -119,6 +119,9 @@ class CAPTURE_EXPORT RequestManager final
cros::mojom::Effect reprocess_effect;
// The input buffer id for this capture request.
base::Optional<uint64_t> input_buffer_id;
+ // The orientation which is stored at the time the request is prepared. It
+ // can be used to construct the reprocess job info when the result is back.
+ int32_t orientation;
};
RequestManager(mojo::PendingReceiver<cros::mojom::Camera3CallbackOps>
@@ -207,17 +210,20 @@ class CAPTURE_EXPORT RequestManager final
struct ReprocessJobInfo {
ReprocessJobInfo(ReprocessTaskQueue queue,
cros::mojom::CameraMetadataPtr metadata,
- uint64_t timestamp);
+ uint64_t timestamp,
+ int32_t orientation);
ReprocessJobInfo(ReprocessJobInfo&& info);
~ReprocessJobInfo();
ReprocessTaskQueue task_queue;
cros::mojom::CameraMetadataPtr metadata;
uint64_t shutter_timestamp;
+ int32_t orientation;
};
// Puts Jpeg orientation information into the metadata.
- void SetJpegOrientation(cros::mojom::CameraMetadataPtr* settings);
+ void SetJpegOrientation(cros::mojom::CameraMetadataPtr* settings,
+ int32_t orientation);
// Puts sensor timestamp into the metadata for reprocess request.
void SetSensorTimestamp(cros::mojom::CameraMetadataPtr* settings,
diff --git a/chromium/media/capture/video/chromeos/video_capture_jpeg_decoder_impl.cc b/chromium/media/capture/video/chromeos/video_capture_jpeg_decoder_impl.cc
index d1a11c243a3..665ac4eb39e 100644
--- a/chromium/media/capture/video/chromeos/video_capture_jpeg_decoder_impl.cc
+++ b/chromium/media/capture/video/chromeos/video_capture_jpeg_decoder_impl.cc
@@ -215,9 +215,8 @@ void VideoCaptureJpegDecoderImpl::FinishInitialization() {
decoder_task_runner_, std::move(remote_decoder));
decoder_->InitializeAsync(
- this,
- base::BindRepeating(&VideoCaptureJpegDecoderImpl::OnInitializationDone,
- weak_ptr_factory_.GetWeakPtr()));
+ this, base::BindOnce(&VideoCaptureJpegDecoderImpl::OnInitializationDone,
+ weak_ptr_factory_.GetWeakPtr()));
}
void VideoCaptureJpegDecoderImpl::OnInitializationDone(bool success) {
diff --git a/chromium/media/capture/video/fuchsia/video_capture_device_factory_fuchsia.cc b/chromium/media/capture/video/fuchsia/video_capture_device_factory_fuchsia.cc
index 42c2e28885d..32cf755b561 100644
--- a/chromium/media/capture/video/fuchsia/video_capture_device_factory_fuchsia.cc
+++ b/chromium/media/capture/video/fuchsia/video_capture_device_factory_fuchsia.cc
@@ -4,32 +4,257 @@
#include "media/capture/video/fuchsia/video_capture_device_factory_fuchsia.h"
+#include <lib/sys/cpp/component_context.h>
+
+#include "base/check_op.h"
+#include "base/fuchsia/default_context.h"
+#include "base/fuchsia/fuchsia_logging.h"
+#include "base/location.h"
#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
+#include "media/capture/video/fuchsia/video_capture_device_fuchsia.h"
namespace media {
-VideoCaptureDeviceFactoryFuchsia::VideoCaptureDeviceFactoryFuchsia() = default;
-VideoCaptureDeviceFactoryFuchsia::~VideoCaptureDeviceFactoryFuchsia() = default;
+class VideoCaptureDeviceFactoryFuchsia::DeviceInfoFetcher {
+ public:
+ DeviceInfoFetcher(uint64_t device_id, fuchsia::camera3::DevicePtr device)
+ : device_id_(device_id), device_(std::move(device)) {
+ device_.set_error_handler(
+ fit::bind_member(this, &DeviceInfoFetcher::OnError));
+ device_->GetIdentifier(
+ fit::bind_member(this, &DeviceInfoFetcher::OnDescription));
+ device_->GetConfigurations(
+ fit::bind_member(this, &DeviceInfoFetcher::OnConfigurations));
+ }
+
+ ~DeviceInfoFetcher() = default;
+
+ DeviceInfoFetcher(const DeviceInfoFetcher&) = delete;
+ const DeviceInfoFetcher& operator=(const DeviceInfoFetcher&) = delete;
+
+ void WaitResults() {
+ DCHECK(!wait_results_run_loop_);
+
+ if (have_results())
+ return;
+
+ if (!device_)
+ return;
+
+ wait_results_run_loop_.emplace();
+ wait_results_run_loop_->Run();
+ wait_results_run_loop_.reset();
+ }
+
+ bool have_results() const { return description_ && formats_; }
+ bool is_usable() const { return device_ || have_results(); }
+
+ uint64_t device_id() const { return device_id_; }
+ const std::string& description() const { return description_.value(); }
+ const VideoCaptureFormats& formats() const { return formats_.value(); }
+
+ private:
+ void OnError(zx_status_t status) {
+ ZX_LOG(ERROR, status) << "fuchsia.camera3.Device disconnected";
+
+ if (wait_results_run_loop_)
+ wait_results_run_loop_->Quit();
+ }
+
+ void OnDescription(fidl::StringPtr identifier) {
+ description_ = identifier.value_or("");
+ MaybeSignalDone();
+ }
+
+ void OnConfigurations(std::vector<fuchsia::camera3::Configuration> configs) {
+ VideoCaptureFormats formats;
+ for (auto& config : configs) {
+ for (auto& props : config.streams) {
+ VideoCaptureFormat format;
+ format.frame_size = gfx::Size(props.image_format.display_width,
+ props.image_format.display_height);
+ format.frame_rate = static_cast<float>(props.frame_rate.numerator) /
+ props.frame_rate.denominator;
+ format.pixel_format =
+ VideoCaptureDeviceFuchsia::GetConvertedPixelFormat(
+ props.image_format.pixel_format.type);
+ if (format.pixel_format == PIXEL_FORMAT_UNKNOWN)
+ continue;
+ formats.push_back(format);
+ }
+ }
+
+ formats_ = std::move(formats);
+ MaybeSignalDone();
+ }
+
+ void MaybeSignalDone() {
+ if (!have_results())
+ return;
+
+ device_.Unbind();
+
+ if (wait_results_run_loop_)
+ wait_results_run_loop_->Quit();
+ }
+
+ uint64_t device_id_;
+ fuchsia::camera3::DevicePtr device_;
+ base::Optional<std::string> description_;
+ base::Optional<VideoCaptureFormats> formats_;
+ base::Optional<base::RunLoop> wait_results_run_loop_;
+};
+
+VideoCaptureDeviceFactoryFuchsia::VideoCaptureDeviceFactoryFuchsia() {
+ DETACH_FROM_THREAD(thread_checker_);
+}
+
+VideoCaptureDeviceFactoryFuchsia::~VideoCaptureDeviceFactoryFuchsia() {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+}
std::unique_ptr<VideoCaptureDevice>
VideoCaptureDeviceFactoryFuchsia::CreateDevice(
const VideoCaptureDeviceDescriptor& device_descriptor) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- NOTIMPLEMENTED();
- return nullptr;
+ uint64_t device_id;
+ bool converted =
+ base::StringToUint64(device_descriptor.device_id, &device_id);
+
+ // Test may call CreateDevice() with an invalid |device_id|.
+ if (!converted)
+ return nullptr;
+
+ fidl::InterfaceHandle<fuchsia::camera3::Device> device;
+ device_watcher_->ConnectToDevice(device_id, device.NewRequest());
+ return std::make_unique<VideoCaptureDeviceFuchsia>(std::move(device));
}
void VideoCaptureDeviceFactoryFuchsia::GetDeviceDescriptors(
VideoCaptureDeviceDescriptors* device_descriptors) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
device_descriptors->clear();
+
+ if (!device_watcher_) {
+ DCHECK(!first_update_run_loop_);
+ DCHECK(devices_.empty());
+
+ Initialize();
+
+ // The RunLoop will quit when either we've received the first WatchDevices()
+ // response or DeviceWatcher fails. |devices_| will be empty in case of a
+ // failure.
+ first_update_run_loop_.emplace();
+ first_update_run_loop_->Run();
+ first_update_run_loop_.reset();
+ }
+
+ for (auto& d : devices_) {
+ d.second->WaitResults();
+ if (d.second->is_usable()) {
+ device_descriptors->push_back(VideoCaptureDeviceDescriptor(
+ d.second->description(), base::NumberToString(d.first),
+ VideoCaptureApi::FUCHSIA_CAMERA3));
+ }
+ }
}
void VideoCaptureDeviceFactoryFuchsia::GetSupportedFormats(
const VideoCaptureDeviceDescriptor& device,
VideoCaptureFormats* capture_formats) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- capture_formats->clear();
+
+ uint64_t device_id;
+ bool converted = base::StringToUint64(device.device_id, &device_id);
+ DCHECK(converted);
+
+ auto it = devices_.find(device_id);
+ if (it == devices_.end()) {
+ capture_formats->clear();
+ } else {
+ it->second->WaitResults();
+ *capture_formats = it->second->formats();
+ }
+}
+
+void VideoCaptureDeviceFactoryFuchsia::Initialize() {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ DCHECK(!device_watcher_);
+ DCHECK(devices_.empty());
+
+ base::fuchsia::ComponentContextForCurrentProcess()->svc()->Connect(
+ device_watcher_.NewRequest());
+
+ device_watcher_.set_error_handler(fit::bind_member(
+ this, &VideoCaptureDeviceFactoryFuchsia::OnDeviceWatcherDisconnected));
+
+ WatchDevices();
+}
+
+void VideoCaptureDeviceFactoryFuchsia::OnDeviceWatcherDisconnected(
+ zx_status_t status) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+ ZX_LOG(ERROR, status) << "fuchsia.camera3.DeviceWatcher disconnected.";
+ devices_.clear();
+
+ if (first_update_run_loop_)
+ first_update_run_loop_->Quit();
+}
+
+void VideoCaptureDeviceFactoryFuchsia::WatchDevices() {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+ device_watcher_->WatchDevices(fit::bind_member(
+ this, &VideoCaptureDeviceFactoryFuchsia::OnWatchDevicesResult));
+}
+
+void VideoCaptureDeviceFactoryFuchsia::OnWatchDevicesResult(
+ std::vector<fuchsia::camera3::WatchDevicesEvent> events) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+ for (auto& e : events) {
+ if (e.is_removed()) {
+ int erased = devices_.erase(e.removed());
+ if (!erased) {
+ LOG(WARNING) << "Received device removed event for a device that "
+ "wasn't previously registered.";
+ }
+ continue;
+ }
+
+ uint64_t id;
+ if (e.is_added()) {
+ id = e.added();
+ if (devices_.find(id) != devices_.end()) {
+ LOG(WARNING) << "Received device added event for a device that was "
+ "previously registered.";
+ continue;
+ }
+ } else {
+ id = e.existing();
+ if (devices_.find(id) != devices_.end()) {
+ continue;
+ }
+ LOG(WARNING) << "Received device exists event for a device that wasn't "
+ "previously registered.";
+ }
+
+ fuchsia::camera3::DevicePtr device;
+ device_watcher_->ConnectToDevice(id, device.NewRequest());
+ devices_.emplace(
+ id, std::make_unique<DeviceInfoFetcher>(id, std::move(device)));
+ }
+
+ if (first_update_run_loop_)
+ first_update_run_loop_->Quit();
+
+ // Watch for further updates.
+ WatchDevices();
}
} // namespace media
diff --git a/chromium/media/capture/video/fuchsia/video_capture_device_factory_fuchsia.h b/chromium/media/capture/video/fuchsia/video_capture_device_factory_fuchsia.h
index 7a5a6507284..86f3c6b8268 100644
--- a/chromium/media/capture/video/fuchsia/video_capture_device_factory_fuchsia.h
+++ b/chromium/media/capture/video/fuchsia/video_capture_device_factory_fuchsia.h
@@ -5,6 +5,13 @@
#ifndef MEDIA_CAPTURE_VIDEO_FUCHSIA_VIDEO_CAPTURE_DEVICE_FACTORY_FUCHSIA_H_
#define MEDIA_CAPTURE_VIDEO_FUCHSIA_VIDEO_CAPTURE_DEVICE_FACTORY_FUCHSIA_H_
+#include <fuchsia/camera3/cpp/fidl.h>
+
+#include <map>
+
+#include "base/containers/small_map.h"
+#include "base/optional.h"
+#include "base/run_loop.h"
#include "media/capture/video/video_capture_device_factory.h"
namespace media {
@@ -15,6 +22,12 @@ class CAPTURE_EXPORT VideoCaptureDeviceFactoryFuchsia
VideoCaptureDeviceFactoryFuchsia();
~VideoCaptureDeviceFactoryFuchsia() override;
+ VideoCaptureDeviceFactoryFuchsia(const VideoCaptureDeviceFactoryFuchsia&) =
+ delete;
+ VideoCaptureDeviceFactoryFuchsia& operator=(
+ const VideoCaptureDeviceFactoryFuchsia&) = delete;
+
+ // VideoCaptureDeviceFactory implementation.
std::unique_ptr<VideoCaptureDevice> CreateDevice(
const VideoCaptureDeviceDescriptor& device_descriptor) override;
void GetDeviceDescriptors(
@@ -23,7 +36,26 @@ class CAPTURE_EXPORT VideoCaptureDeviceFactoryFuchsia
VideoCaptureFormats* supported_formats) override;
private:
- DISALLOW_COPY_AND_ASSIGN(VideoCaptureDeviceFactoryFuchsia);
+ // Helper class used to fetch per-device information.
+ class DeviceInfoFetcher;
+
+ void Initialize();
+
+ void OnDeviceWatcherDisconnected(zx_status_t status);
+
+ void WatchDevices();
+ void OnWatchDevicesResult(
+ std::vector<fuchsia::camera3::WatchDevicesEvent> events);
+
+ fuchsia::camera3::DeviceWatcherPtr device_watcher_;
+ base::small_map<std::map<uint64_t, std::unique_ptr<DeviceInfoFetcher>>>
+ devices_;
+
+ // RunLoop used to wait for the first WatchDevices() response. Currently
+ // required because GetDeviceDescriptors() is synchronous.
+ // TODO(crbug.com/1072932) Refactor interface to allow asynchronous
+ // enumeration and remove this hack.
+ base::Optional<base::RunLoop> first_update_run_loop_;
};
} // namespace media
diff --git a/chromium/media/capture/video/fuchsia/video_capture_device_fuchsia.cc b/chromium/media/capture/video/fuchsia/video_capture_device_fuchsia.cc
new file mode 100644
index 00000000000..946ad151a4e
--- /dev/null
+++ b/chromium/media/capture/video/fuchsia/video_capture_device_fuchsia.cc
@@ -0,0 +1,437 @@
+// 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/capture/video/fuchsia/video_capture_device_fuchsia.h"
+
+#include <zircon/status.h>
+
+#include "base/fuchsia/fuchsia_logging.h"
+#include "base/strings/stringprintf.h"
+#include "base/time/time.h"
+#include "media/base/video_types.h"
+#include "third_party/libyuv/include/libyuv/convert.h"
+#include "third_party/libyuv/include/libyuv/video_common.h"
+#include "ui/gfx/buffer_format_util.h"
+
+namespace media {
+
+namespace {
+
+size_t RoundUp(size_t value, size_t alignment) {
+ return ((value + alignment - 1) / alignment) * alignment;
+}
+
+libyuv::FourCC GetFourccForPixelFormat(
+ fuchsia::sysmem::PixelFormatType src_pixel_format) {
+ switch (src_pixel_format) {
+ case fuchsia::sysmem::PixelFormatType::I420:
+ return libyuv::FourCC::FOURCC_I420;
+ case fuchsia::sysmem::PixelFormatType::YV12:
+ return libyuv::FourCC::FOURCC_YV12;
+ case fuchsia::sysmem::PixelFormatType::NV12:
+ return libyuv::FourCC::FOURCC_NV12;
+ default:
+ NOTREACHED();
+ return libyuv::FourCC::FOURCC_I420;
+ }
+}
+
+libyuv::RotationMode CameraOrientationToLibyuvRotation(
+ fuchsia::camera3::Orientation orientation,
+ bool* flip_y) {
+ switch (orientation) {
+ case fuchsia::camera3::Orientation::UP:
+ *flip_y = false;
+ return libyuv::RotationMode::kRotate0;
+
+ case fuchsia::camera3::Orientation::DOWN:
+ *flip_y = false;
+ return libyuv::RotationMode::kRotate180;
+
+ case fuchsia::camera3::Orientation::LEFT:
+ *flip_y = false;
+ return libyuv::RotationMode::kRotate270;
+
+ case fuchsia::camera3::Orientation::RIGHT:
+ *flip_y = false;
+ return libyuv::RotationMode::kRotate90;
+
+ case fuchsia::camera3::Orientation::UP_FLIPPED:
+ *flip_y = true;
+ return libyuv::RotationMode::kRotate180;
+
+ case fuchsia::camera3::Orientation::DOWN_FLIPPED:
+ *flip_y = true;
+ return libyuv::RotationMode::kRotate0;
+
+ case fuchsia::camera3::Orientation::LEFT_FLIPPED:
+ *flip_y = true;
+ return libyuv::RotationMode::kRotate90;
+
+ case fuchsia::camera3::Orientation::RIGHT_FLIPPED:
+ *flip_y = true;
+ return libyuv::RotationMode::kRotate270;
+ }
+}
+
+gfx::Size RotateSize(gfx::Size size, libyuv::RotationMode rotation) {
+ switch (rotation) {
+ case libyuv::RotationMode::kRotate0:
+ case libyuv::RotationMode::kRotate180:
+ return size;
+
+ case libyuv::RotationMode::kRotate90:
+ case libyuv::RotationMode::kRotate270:
+ return gfx::Size(size.height(), size.width());
+ }
+}
+
+} // namespace
+
+// static
+VideoPixelFormat VideoCaptureDeviceFuchsia::GetConvertedPixelFormat(
+ fuchsia::sysmem::PixelFormatType format) {
+ switch (format) {
+ case fuchsia::sysmem::PixelFormatType::I420:
+ case fuchsia::sysmem::PixelFormatType::YV12:
+ case fuchsia::sysmem::PixelFormatType::NV12:
+ // Convert all YUV formats to I420 since consumers currently don't support
+ // NV12 or YV12.
+ return PIXEL_FORMAT_I420;
+
+ default:
+ LOG(ERROR) << "Camera uses unsupported pixel format "
+ << static_cast<int>(format);
+ return PIXEL_FORMAT_UNKNOWN;
+ }
+}
+
+bool VideoCaptureDeviceFuchsia::IsSupportedPixelFormat(
+ fuchsia::sysmem::PixelFormatType format) {
+ return GetConvertedPixelFormat(format) != PIXEL_FORMAT_UNKNOWN;
+}
+
+VideoCaptureDeviceFuchsia::VideoCaptureDeviceFuchsia(
+ fidl::InterfaceHandle<fuchsia::camera3::Device> device) {
+ device_.Bind(std::move(device));
+ device_.set_error_handler(
+ fit::bind_member(this, &VideoCaptureDeviceFuchsia::OnDeviceError));
+}
+
+VideoCaptureDeviceFuchsia::~VideoCaptureDeviceFuchsia() {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+}
+
+void VideoCaptureDeviceFuchsia::AllocateAndStart(
+ const VideoCaptureParams& params,
+ std::unique_ptr<Client> client) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ DCHECK_EQ(params.requested_format.pixel_format, PIXEL_FORMAT_I420);
+ DCHECK(!client_);
+ DCHECK(!stream_);
+
+ client_ = std::move(client);
+
+ if (!device_) {
+ OnError(FROM_HERE, VideoCaptureError::kFuchsiaCameraDeviceDisconnected,
+ "fuchsia.camera3.Device disconnected");
+ return;
+ }
+
+ start_time_ = base::TimeTicks::Now();
+ frames_received_ = 0;
+
+ // TODO(crbug.com/1075839) Select stream_id based on requested resolution.
+ device_->ConnectToStream(/*stream_id=*/0, stream_.NewRequest());
+ stream_.set_error_handler(
+ fit::bind_member(this, &VideoCaptureDeviceFuchsia::OnStreamError));
+
+ WatchResolution();
+ WatchOrientation();
+
+ // Call SetBufferCollection() with a new buffer collection token to indicate
+ // that we are interested in buffer collection negotiation. The collection
+ // token will be returned back from WatchBufferCollection(). After that it
+ // will be initialized in InitializeBufferCollection().
+ stream_->SetBufferCollection(sysmem_allocator_.CreateNewToken());
+ WatchBufferCollection();
+}
+
+void VideoCaptureDeviceFuchsia::StopAndDeAllocate() {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ DisconnectStream();
+ client_.reset();
+}
+
+void VideoCaptureDeviceFuchsia::OnDeviceError(zx_status_t status) {
+ OnError(FROM_HERE, VideoCaptureError::kFuchsiaCameraDeviceDisconnected,
+ base::StringPrintf("fuchsia.camera3.Device disconnected: %s (%d)",
+ zx_status_get_string(status), status));
+}
+
+void VideoCaptureDeviceFuchsia::OnStreamError(zx_status_t status) {
+ OnError(FROM_HERE, VideoCaptureError::kFuchsiaCameraStreamDisconnected,
+ base::StringPrintf("fuchsia.camera3.Stream disconnected: %s (%d)",
+ zx_status_get_string(status), status));
+}
+
+void VideoCaptureDeviceFuchsia::DisconnectStream() {
+ stream_.Unbind();
+ buffer_collection_creator_.reset();
+ buffer_collection_.reset();
+ buffer_reader_.reset();
+ frame_size_.reset();
+}
+
+void VideoCaptureDeviceFuchsia::OnError(base::Location location,
+ VideoCaptureError error,
+ const std::string& reason) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+ DisconnectStream();
+
+ if (client_) {
+ client_->OnError(error, location, reason);
+ }
+}
+
+void VideoCaptureDeviceFuchsia::WatchResolution() {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+ stream_->WatchResolution(fit::bind_member(
+ this, &VideoCaptureDeviceFuchsia::OnWatchResolutionResult));
+}
+
+void VideoCaptureDeviceFuchsia::OnWatchResolutionResult(
+ fuchsia::math::Size frame_size) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+ frame_size_ = gfx::Size(frame_size.width, frame_size.height);
+
+ WatchResolution();
+}
+
+void VideoCaptureDeviceFuchsia::WatchOrientation() {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+ stream_->WatchOrientation(fit::bind_member(
+ this, &VideoCaptureDeviceFuchsia::OnWatchOrientationResult));
+}
+
+void VideoCaptureDeviceFuchsia::OnWatchOrientationResult(
+ fuchsia::camera3::Orientation orientation) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+ orientation_ = orientation;
+ WatchOrientation();
+}
+
+void VideoCaptureDeviceFuchsia::WatchBufferCollection() {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+ stream_->WatchBufferCollection(
+ [this](fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken>
+ token_handle) {
+ InitializeBufferCollection(std::move(token_handle));
+ WatchBufferCollection();
+ });
+}
+
+void VideoCaptureDeviceFuchsia::InitializeBufferCollection(
+ fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken>
+ token_handle) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+ // Drop old buffers.
+ buffer_collection_.reset();
+ buffer_reader_.reset();
+
+ // Initialize the new collection.
+ fuchsia::sysmem::BufferCollectionTokenPtr token;
+ token.Bind(std::move(token_handle));
+ buffer_collection_creator_ =
+ sysmem_allocator_.MakeBufferPoolCreatorFromToken(std::move(token));
+
+ // Request just one buffer in collection constraints: each frame is copied as
+ // soon as it's received.
+ const size_t kMaxUsedOutputFrames = 1;
+ fuchsia::sysmem::BufferCollectionConstraints constraints =
+ SysmemBufferReader::GetRecommendedConstraints(kMaxUsedOutputFrames);
+ buffer_collection_creator_->Create(
+ std::move(constraints),
+ base::BindOnce(&VideoCaptureDeviceFuchsia::OnBufferCollectionCreated,
+ base::Unretained(this)));
+}
+
+void VideoCaptureDeviceFuchsia::OnBufferCollectionCreated(
+ std::unique_ptr<SysmemBufferPool> collection) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+ buffer_collection_ = std::move(collection);
+ buffer_collection_->CreateReader(
+ base::BindOnce(&VideoCaptureDeviceFuchsia::OnBufferReaderCreated,
+ base::Unretained(this)));
+}
+
+void VideoCaptureDeviceFuchsia::OnBufferReaderCreated(
+ std::unique_ptr<SysmemBufferReader> reader) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+ buffer_reader_ = std::move(reader);
+ if (!buffer_reader_->buffer_settings().has_image_format_constraints) {
+ OnError(FROM_HERE, VideoCaptureError::kFuchsiaSysmemDidNotSetImageFormat,
+ "Sysmem created buffer without image format constraints");
+ return;
+ }
+
+ auto pixel_format = buffer_reader_->buffer_settings()
+ .image_format_constraints.pixel_format.type;
+ if (!IsSupportedPixelFormat(pixel_format)) {
+ OnError(FROM_HERE, VideoCaptureError::kFuchsiaUnsupportedPixelFormat,
+ base::StringPrintf("Unsupported video frame format: %d",
+ static_cast<int>(pixel_format)));
+ return;
+ }
+
+ if (!started_) {
+ started_ = true;
+ client_->OnStarted();
+ ReceiveNextFrame();
+ }
+}
+
+void VideoCaptureDeviceFuchsia::ReceiveNextFrame() {
+ stream_->GetNextFrame([this](fuchsia::camera3::FrameInfo frame_info) {
+ ProcessNewFrame(std::move(frame_info));
+ ReceiveNextFrame();
+ });
+}
+
+void VideoCaptureDeviceFuchsia::ProcessNewFrame(
+ fuchsia::camera3::FrameInfo frame_info) {
+ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+ DCHECK(client_);
+
+ if (!buffer_reader_) {
+ DLOG(WARNING) << "Dropping frame received before sysmem collection has "
+ "been initialized.";
+ return;
+ }
+
+ size_t index = frame_info.buffer_index;
+ if (index >= buffer_reader_->num_buffers()) {
+ OnError(FROM_HERE, VideoCaptureError::kFuchsiaSysmemInvalidBufferIndex,
+ base::StringPrintf("Received frame with invalid buffer_index=%zu",
+ index));
+ return;
+ }
+
+ const fuchsia::sysmem::ImageFormatConstraints& sysmem_buffer_format =
+ buffer_reader_->buffer_settings().image_format_constraints;
+
+ // Calculate coded frame dimensions for the buffer collection based on the
+ // sysmem collection constraints. This logic should match
+ // LogicalBufferCollection::Allocate() in sysmem.
+ size_t src_coded_width =
+ RoundUp(std::max(sysmem_buffer_format.min_coded_width,
+ sysmem_buffer_format.required_max_coded_width),
+ sysmem_buffer_format.coded_width_divisor);
+ size_t src_coded_height =
+ RoundUp(std::max(sysmem_buffer_format.min_coded_height,
+ sysmem_buffer_format.required_max_coded_height),
+ sysmem_buffer_format.coded_height_divisor);
+ size_t src_stride = RoundUp(
+ std::max(static_cast<size_t>(sysmem_buffer_format.min_bytes_per_row),
+ src_coded_width),
+ sysmem_buffer_format.bytes_per_row_divisor);
+ gfx::Size visible_size =
+ frame_size_.value_or(gfx::Size(src_coded_width, src_coded_height));
+ gfx::Size nonrotated_output_size((visible_size.width() + 1) & ~1,
+ (visible_size.height() + 1) & ~1);
+
+ bool flip_y;
+ libyuv::RotationMode rotation =
+ CameraOrientationToLibyuvRotation(orientation_, &flip_y);
+
+ gfx::Size output_size = RotateSize(nonrotated_output_size, rotation);
+ visible_size = RotateSize(visible_size, rotation);
+
+ base::TimeTicks reference_time =
+ base::TimeTicks::FromZxTime(frame_info.timestamp);
+ base::TimeDelta timestamp =
+ std::max(reference_time - start_time_, base::TimeDelta());
+
+ float frame_rate =
+ (timestamp > base::TimeDelta())
+ ? static_cast<float>(frames_received_) / timestamp.InSecondsF()
+ : 0.0;
+ VideoCaptureFormat capture_format(output_size, frame_rate, PIXEL_FORMAT_I420);
+ capture_format.pixel_format = PIXEL_FORMAT_I420;
+
+ Client::Buffer buffer;
+ Client::ReserveResult result = client_->ReserveOutputBuffer(
+ capture_format.frame_size, capture_format.pixel_format,
+ /*frame_feedback_id=*/0, &buffer);
+ if (result != Client::ReserveResult::kSucceeded) {
+ DLOG(WARNING) << "Failed to allocate output buffer for a video frame";
+ return;
+ }
+
+ auto src_span = buffer_reader_->GetMappingForBuffer(index);
+ if (src_span.empty()) {
+ OnError(FROM_HERE, VideoCaptureError::kFuchsiaFailedToMapSysmemBuffer,
+ "Failed to map buffers allocated by sysmem");
+ return;
+ }
+
+ // For all supported formats (I420, NV12 and YV12) the U and V channels are
+ // subsampled at 2x in both directions, so together they occupy half of the
+ // space needed for the Y plane and the total buffer size is 3/2 of the Y
+ // plane size.
+ size_t src_buffer_size = src_coded_height * src_stride * 3 / 2;
+ if (src_span.size() < src_buffer_size) {
+ OnError(FROM_HERE, VideoCaptureError::kFuchsiaSysmemInvalidBufferSize,
+ "Sysmem allocated buffer that's smaller than expected");
+ return;
+ }
+
+ std::unique_ptr<VideoCaptureBufferHandle> output_handle =
+ buffer.handle_provider->GetHandleForInProcessAccess();
+
+ // Calculate offsets and strides for the output buffer.
+ uint8_t* dst_y = output_handle->data();
+ int dst_stride_y = output_size.width();
+ size_t dst_y_plane_size = output_size.width() * output_size.height();
+ uint8_t* dst_u = dst_y + dst_y_plane_size;
+ int dst_stride_u = output_size.width() / 2;
+ uint8_t* dst_v = dst_u + dst_y_plane_size / 4;
+ int dst_stride_v = output_size.width() / 2;
+
+ // Check that the output fits in the buffer.
+ const uint8_t* dst_end = dst_v + dst_y_plane_size / 4;
+ CHECK_LE(dst_end, output_handle->data() + output_handle->mapped_size());
+
+ // Vertical flip is indicated to ConvertToI420() by negating src_height.
+ int flipped_src_height = static_cast<int>(src_coded_height);
+ if (flip_y)
+ flipped_src_height = -flipped_src_height;
+
+ auto four_cc =
+ GetFourccForPixelFormat(buffer_reader_->buffer_settings()
+ .image_format_constraints.pixel_format.type);
+
+ libyuv::ConvertToI420(src_span.data(), src_span.size(), dst_y, dst_stride_y,
+ dst_u, dst_stride_u, dst_v, dst_stride_v,
+ /*crop_x=*/0, /*crop_y=*/0, src_stride,
+ flipped_src_height, nonrotated_output_size.width(),
+ nonrotated_output_size.height(), rotation, four_cc);
+
+ client_->OnIncomingCapturedBufferExt(
+ std::move(buffer), capture_format, gfx::ColorSpace(), reference_time,
+ timestamp, gfx::Rect(visible_size), VideoFrameMetadata());
+
+ // Frame buffer is returned to the device by dropping the |frame_info|.
+}
+
+} // namespace media \ No newline at end of file
diff --git a/chromium/media/capture/video/fuchsia/video_capture_device_fuchsia.h b/chromium/media/capture/video/fuchsia/video_capture_device_fuchsia.h
new file mode 100644
index 00000000000..1d2e40ce55a
--- /dev/null
+++ b/chromium/media/capture/video/fuchsia/video_capture_device_fuchsia.h
@@ -0,0 +1,118 @@
+// 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_CAPTURE_VIDEO_FUCHSIA_VIDEO_CAPTURE_DEVICE_FUCHSIA_H_
+#define MEDIA_CAPTURE_VIDEO_FUCHSIA_VIDEO_CAPTURE_DEVICE_FUCHSIA_H_
+
+#include <fuchsia/camera3/cpp/fidl.h>
+
+#include <memory>
+
+#include "base/optional.h"
+#include "base/threading/thread_checker.h"
+#include "base/time/time.h"
+#include "media/capture/video/video_capture_device.h"
+#include "media/fuchsia/common/sysmem_buffer_pool.h"
+#include "media/fuchsia/common/sysmem_buffer_reader.h"
+
+namespace media {
+
+class CAPTURE_EXPORT VideoCaptureDeviceFuchsia : public VideoCaptureDevice {
+ public:
+ // Returns pixel format to which video frames in the specified sysmem pixel
+ // |format| will be converted. PIXEL_FORMAT_UNKNOWN is returned for
+ // unsupported formats.
+ static VideoPixelFormat GetConvertedPixelFormat(
+ fuchsia::sysmem::PixelFormatType format);
+
+ static bool IsSupportedPixelFormat(fuchsia::sysmem::PixelFormatType format);
+
+ explicit VideoCaptureDeviceFuchsia(
+ fidl::InterfaceHandle<fuchsia::camera3::Device> device);
+ ~VideoCaptureDeviceFuchsia() final;
+
+ VideoCaptureDeviceFuchsia(const VideoCaptureDeviceFuchsia&) = delete;
+ VideoCaptureDeviceFuchsia& operator=(const VideoCaptureDeviceFuchsia&) =
+ delete;
+
+ // VideoCaptureDevice implementation.
+ void AllocateAndStart(const VideoCaptureParams& params,
+ std::unique_ptr<Client> client) final;
+ void StopAndDeAllocate() final;
+
+ private:
+ // Disconnects the |stream_| and resets related state.
+ void DisconnectStream();
+
+ // Reports the specified |error| to the client.
+ void OnError(base::Location location,
+ VideoCaptureError error,
+ const std::string& reason);
+
+ // Error handlers for the |device_| and |stream_|.
+ void OnDeviceError(zx_status_t status);
+ void OnStreamError(zx_status_t status);
+
+ // Watches for resolution updates and updates |frame_size_| accordingly.
+ void WatchResolution();
+
+ // Callback for WatchResolution().
+ void OnWatchResolutionResult(fuchsia::math::Size frame_size);
+
+ // Watches for orientation updates and updates |orientation_| accordingly.
+ void WatchOrientation();
+
+ // Callback for WatchOrientation().
+ void OnWatchOrientationResult(fuchsia::camera3::Orientation orientation);
+
+ // Watches for sysmem buffer collection updates from the camera.
+ void WatchBufferCollection();
+
+ // Initializes buffer collection using the specified token. Initialization is
+ // asynchronous. |buffer_reader_| will be set once the initialization is
+ // complete. Old buffer collection are dropped synchronously (whether they
+ // have finished initialization or not).
+ void InitializeBufferCollection(
+ fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken>
+ token_handle);
+
+ // Callback for SysmemBufferPool::Creator.
+ void OnBufferCollectionCreated(std::unique_ptr<SysmemBufferPool> collection);
+
+ // Callback for SysmemBufferPool::CreateReader().
+ void OnBufferReaderCreated(std::unique_ptr<SysmemBufferReader> reader);
+
+ // Calls Stream::GetNextFrame() in a loop to receive incoming frames.
+ void ReceiveNextFrame();
+
+ // Processes new frames received by ReceiveNextFrame() and passes it to the
+ // |client_|.
+ void ProcessNewFrame(fuchsia::camera3::FrameInfo frame_info);
+
+ fuchsia::camera3::DevicePtr device_;
+ fuchsia::camera3::StreamPtr stream_;
+
+ std::unique_ptr<Client> client_;
+
+ media::BufferAllocator sysmem_allocator_;
+ std::unique_ptr<SysmemBufferPool::Creator> buffer_collection_creator_;
+ std::unique_ptr<SysmemBufferPool> buffer_collection_;
+ std::unique_ptr<SysmemBufferReader> buffer_reader_;
+
+ base::Optional<gfx::Size> frame_size_;
+ fuchsia::camera3::Orientation orientation_ =
+ fuchsia::camera3::Orientation::UP;
+
+ base::TimeTicks start_time_;
+
+ bool started_ = false;
+
+ size_t frames_received_ = 0;
+
+ THREAD_CHECKER(thread_checker_);
+};
+
+} // namespace media
+
+#endif // MEDIA_CAPTURE_VIDEO_FUCHSIA_VIDEO_CAPTURE_DEVICE_FUCHSIA_H_ \ No newline at end of file
diff --git a/chromium/media/capture/video/fuchsia/video_capture_device_fuchsia_test.cc b/chromium/media/capture/video/fuchsia/video_capture_device_fuchsia_test.cc
new file mode 100644
index 00000000000..63627c460a7
--- /dev/null
+++ b/chromium/media/capture/video/fuchsia/video_capture_device_fuchsia_test.cc
@@ -0,0 +1,324 @@
+// 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/capture/video/fuchsia/video_capture_device_fuchsia.h"
+
+#include "base/test/task_environment.h"
+#include "media/fuchsia/camera/fake_fuchsia_camera.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace media {
+
+namespace {
+
+struct ReceivedFrame {
+ VideoCaptureDevice::Client::Buffer buffer;
+ VideoCaptureFormat format;
+ gfx::ColorSpace color_space;
+ base::TimeTicks reference_time;
+ base::TimeDelta timestamp;
+ gfx::Rect visible_rect;
+};
+
+void ValidateReceivedFrame(const ReceivedFrame& frame,
+ gfx::Size expected_size,
+ uint8_t salt) {
+ gfx::Size coded_size((expected_size.width() + 1) & ~1,
+ (expected_size.height() + 1) & ~1);
+ ASSERT_EQ(frame.format.frame_size, coded_size);
+ EXPECT_EQ(frame.format.pixel_format, PIXEL_FORMAT_I420);
+ EXPECT_EQ(frame.visible_rect, gfx::Rect(expected_size));
+ EXPECT_EQ(frame.color_space, gfx::ColorSpace());
+
+ auto handle = frame.buffer.handle_provider->GetHandleForInProcessAccess();
+
+ FakeCameraStream::ValidateFrameData(handle->data(), coded_size, salt);
+}
+
+// VideoCaptureBufferHandle implementation that references memory allocated on
+// the heap.
+class HeapBufferHandle : public VideoCaptureBufferHandle {
+ public:
+ HeapBufferHandle(size_t size, uint8_t* data) : size_(size), data_(data) {}
+
+ size_t mapped_size() const final { return size_; }
+ uint8_t* data() const final { return data_; }
+ const uint8_t* const_data() const final { return data_; }
+
+ private:
+ const size_t size_;
+ uint8_t* const data_;
+};
+
+// VideoCaptureDevice::Client::Buffer::HandleProvider implementation that
+// allocates memory on the heap.
+class HeapBufferHandleProvider
+ : public VideoCaptureDevice::Client::Buffer::HandleProvider {
+ public:
+ HeapBufferHandleProvider(size_t size) : data_(size) {}
+ ~HeapBufferHandleProvider() final = default;
+
+ base::UnsafeSharedMemoryRegion DuplicateAsUnsafeRegion() final {
+ NOTREACHED();
+ return {};
+ }
+
+ mojo::ScopedSharedBufferHandle DuplicateAsMojoBuffer() final {
+ NOTREACHED();
+ return {};
+ }
+
+ std::unique_ptr<VideoCaptureBufferHandle> GetHandleForInProcessAccess()
+ override {
+ return std::make_unique<HeapBufferHandle>(data_.size(), data_.data());
+ }
+
+ gfx::GpuMemoryBufferHandle GetGpuMemoryBufferHandle() override {
+ return gfx::GpuMemoryBufferHandle();
+ }
+
+ private:
+ std::vector<uint8_t> data_;
+};
+
+class TestVideoCaptureClient : public VideoCaptureDevice::Client {
+ public:
+ ~TestVideoCaptureClient() final = default;
+
+ void WaitFrame() {
+ EXPECT_FALSE(wait_frame_run_loop_);
+
+ wait_frame_run_loop_.emplace();
+ wait_frame_run_loop_->Run();
+ wait_frame_run_loop_.reset();
+ }
+
+ const std::vector<ReceivedFrame>& received_frames() {
+ return received_frames_;
+ }
+
+ private:
+ // VideoCaptureDevice::Client implementation.
+ void OnStarted() final {
+ EXPECT_FALSE(started_);
+ started_ = true;
+ }
+
+ ReserveResult ReserveOutputBuffer(const gfx::Size& dimensions,
+ VideoPixelFormat format,
+ int frame_feedback_id,
+ Buffer* buffer) final {
+ EXPECT_TRUE(started_);
+ EXPECT_EQ(format, PIXEL_FORMAT_I420);
+ EXPECT_EQ(dimensions.width() % 2, 0);
+ EXPECT_EQ(dimensions.height() % 2, 0);
+
+ size_t size = dimensions.width() * dimensions.height() * 3 / 2;
+ buffer->handle_provider = std::make_unique<HeapBufferHandleProvider>(size);
+ return VideoCaptureDevice::Client::ReserveResult::kSucceeded;
+ }
+
+ void OnIncomingCapturedBufferExt(
+ Buffer buffer,
+ const VideoCaptureFormat& format,
+ const gfx::ColorSpace& color_space,
+ base::TimeTicks reference_time,
+ base::TimeDelta timestamp,
+ gfx::Rect visible_rect,
+ const VideoFrameMetadata& additional_metadata) final {
+ EXPECT_TRUE(started_);
+
+ received_frames_.push_back(ReceivedFrame{std::move(buffer), format,
+ color_space, reference_time,
+ timestamp, visible_rect});
+
+ if (wait_frame_run_loop_)
+ wait_frame_run_loop_->Quit();
+ }
+
+ void OnIncomingCapturedData(const uint8_t* data,
+ int length,
+ const VideoCaptureFormat& frame_format,
+ const gfx::ColorSpace& color_space,
+ int clockwise_rotation,
+ bool flip_y,
+ base::TimeTicks reference_time,
+ base::TimeDelta timestamp,
+ int frame_feedback_id) final {
+ NOTREACHED();
+ }
+ void OnIncomingCapturedGfxBuffer(gfx::GpuMemoryBuffer* buffer,
+ const VideoCaptureFormat& frame_format,
+ int clockwise_rotation,
+ base::TimeTicks reference_time,
+ base::TimeDelta timestamp,
+ int frame_feedback_id) final {
+ NOTREACHED();
+ }
+ void OnIncomingCapturedBuffer(Buffer buffer,
+ const VideoCaptureFormat& format,
+ base::TimeTicks reference_time,
+ base::TimeDelta timestamp) final {
+ NOTREACHED();
+ }
+ void OnError(VideoCaptureError error,
+ const base::Location& from_here,
+ const std::string& reason) final {
+ NOTREACHED();
+ }
+ void OnFrameDropped(VideoCaptureFrameDropReason reason) final {
+ NOTREACHED();
+ }
+ void OnLog(const std::string& message) final { NOTREACHED(); }
+ double GetBufferPoolUtilization() const final {
+ NOTREACHED();
+ return 0;
+ }
+
+ bool started_ = false;
+ std::vector<ReceivedFrame> received_frames_;
+ base::Optional<base::RunLoop> wait_frame_run_loop_;
+};
+
+} // namespace
+
+class VideoCaptureDeviceFuchsiaTest : public testing::Test {
+ public:
+ VideoCaptureDeviceFuchsiaTest() {
+ fidl::InterfaceHandle<fuchsia::camera3::Device> device_handle;
+ fake_device_.Bind(device_handle.NewRequest());
+ device_ =
+ std::make_unique<VideoCaptureDeviceFuchsia>(std::move(device_handle));
+ }
+
+ ~VideoCaptureDeviceFuchsiaTest() override { device_->StopAndDeAllocate(); }
+
+ void StartCapturer() {
+ VideoCaptureParams params;
+ params.requested_format.frame_size = FakeCameraStream::kDefaultFrameSize;
+ params.requested_format.frame_rate = 30.0;
+ params.requested_format.pixel_format = PIXEL_FORMAT_I420;
+
+ auto client = std::make_unique<TestVideoCaptureClient>();
+ client_ = client.get();
+ device_->AllocateAndStart(params, std::move(client));
+
+ EXPECT_TRUE(fake_stream_.WaitBuffersAllocated());
+ }
+
+ protected:
+ base::test::SingleThreadTaskEnvironment task_environment_{
+ base::test::SingleThreadTaskEnvironment::MainThreadType::IO};
+
+ FakeCameraStream fake_stream_;
+ FakeCameraDevice fake_device_{&fake_stream_};
+ std::unique_ptr<VideoCaptureDeviceFuchsia> device_;
+ TestVideoCaptureClient* client_ = nullptr;
+};
+
+TEST_F(VideoCaptureDeviceFuchsiaTest, Initialize) {
+ StartCapturer();
+}
+
+TEST_F(VideoCaptureDeviceFuchsiaTest, SendFrame) {
+ StartCapturer();
+
+ auto frame_timestamp = base::TimeTicks::Now();
+ fake_stream_.ProduceFrame(frame_timestamp, 1);
+ client_->WaitFrame();
+
+ ASSERT_EQ(client_->received_frames().size(), 1U);
+ EXPECT_EQ(client_->received_frames()[0].reference_time, frame_timestamp);
+ ValidateReceivedFrame(client_->received_frames()[0],
+ FakeCameraStream::kDefaultFrameSize, 1);
+}
+
+TEST_F(VideoCaptureDeviceFuchsiaTest, MultipleFrames) {
+ StartCapturer();
+
+ EXPECT_TRUE(fake_stream_.WaitBuffersAllocated());
+
+ for (size_t i = 0; i < 10; ++i) {
+ ASSERT_TRUE(fake_stream_.WaitFreeBuffer());
+
+ auto frame_timestamp =
+ base::TimeTicks() + base::TimeDelta::FromMilliseconds(i * 16);
+ fake_stream_.ProduceFrame(frame_timestamp, i);
+ client_->WaitFrame();
+
+ ASSERT_EQ(client_->received_frames().size(), i + 1);
+ EXPECT_EQ(client_->received_frames()[i].reference_time, frame_timestamp);
+ ValidateReceivedFrame(client_->received_frames()[i],
+ FakeCameraStream::kDefaultFrameSize, i);
+ }
+}
+
+TEST_F(VideoCaptureDeviceFuchsiaTest, FrameRotation) {
+ const gfx::Size kResolution(4, 2);
+ fake_stream_.SetFakeResolution(kResolution);
+
+ StartCapturer();
+
+ EXPECT_TRUE(fake_stream_.WaitBuffersAllocated());
+
+ for (int i = static_cast<int>(fuchsia::camera3::Orientation::UP);
+ i <= static_cast<int>(fuchsia::camera3::Orientation::RIGHT_FLIPPED);
+ ++i) {
+ SCOPED_TRACE(testing::Message() << "Orientation " << i);
+
+ auto orientation = static_cast<fuchsia::camera3::Orientation>(i);
+
+ ASSERT_TRUE(fake_stream_.WaitFreeBuffer());
+ fake_stream_.SetFakeOrientation(orientation);
+ fake_stream_.ProduceFrame(base::TimeTicks::Now(), i);
+ client_->WaitFrame();
+
+ gfx::Size expected_size = kResolution;
+ if (orientation == fuchsia::camera3::Orientation::LEFT ||
+ orientation == fuchsia::camera3::Orientation::LEFT_FLIPPED ||
+ orientation == fuchsia::camera3::Orientation::RIGHT ||
+ orientation == fuchsia::camera3::Orientation::RIGHT_FLIPPED) {
+ expected_size = gfx::Size(expected_size.height(), expected_size.width());
+ }
+ ValidateReceivedFrame(client_->received_frames().back(), expected_size, i);
+ }
+}
+
+TEST_F(VideoCaptureDeviceFuchsiaTest, FrameDimensionsNotDivisibleBy2) {
+ const gfx::Size kOddResolution(21, 7);
+ fake_stream_.SetFakeResolution(kOddResolution);
+
+ StartCapturer();
+
+ fake_stream_.ProduceFrame(base::TimeTicks::Now(), 1);
+ client_->WaitFrame();
+
+ ASSERT_EQ(client_->received_frames().size(), 1U);
+ ValidateReceivedFrame(client_->received_frames()[0], kOddResolution, 1);
+}
+
+TEST_F(VideoCaptureDeviceFuchsiaTest, MidStreamResolutionChange) {
+ StartCapturer();
+
+ // Capture the first frame at the default resolution.
+ fake_stream_.ProduceFrame(base::TimeTicks::Now(), 1);
+ client_->WaitFrame();
+ ASSERT_TRUE(fake_stream_.WaitFreeBuffer());
+
+ // Update resolution and produce another frames.
+ const gfx::Size kUpdatedResolution(3, 14);
+ fake_stream_.SetFakeResolution(kUpdatedResolution);
+ fake_stream_.ProduceFrame(base::TimeTicks::Now(), 1);
+ client_->WaitFrame();
+
+ // Verify that we get captured frames with correct resolution.
+ ASSERT_EQ(client_->received_frames().size(), 2U);
+ ValidateReceivedFrame(client_->received_frames()[0],
+ FakeCameraStream::kDefaultFrameSize, 1);
+ ValidateReceivedFrame(client_->received_frames()[1], kUpdatedResolution, 1);
+}
+
+} // namespace media
diff --git a/chromium/media/capture/video/mac/video_capture_device_avfoundation_mac.mm b/chromium/media/capture/video/mac/video_capture_device_avfoundation_mac.mm
index f803ae0efc8..d19f9e68b86 100644
--- a/chromium/media/capture/video/mac/video_capture_device_avfoundation_mac.mm
+++ b/chromium/media/capture/video/mac/video_capture_device_avfoundation_mac.mm
@@ -24,10 +24,6 @@
#include "services/video_capture/public/uma/video_capture_service_event.h"
#include "ui/gfx/geometry/size.h"
-// Prefer MJPEG if frame width or height is larger than this.
-static const int kMjpegWidthThreshold = 640;
-static const int kMjpegHeightThreshold = 480;
-
namespace {
enum MacBookVersions {
@@ -351,16 +347,9 @@ void ExtractBaseAddressAndLength(char** base_address,
_frameRate = frameRate;
FourCharCode best_fourcc = kCMPixelFormat_422YpCbCr8;
- const bool prefer_mjpeg =
- width > kMjpegWidthThreshold || height > kMjpegHeightThreshold;
for (AVCaptureDeviceFormat* format in [_captureDevice formats]) {
const FourCharCode fourcc =
CMFormatDescriptionGetMediaSubType([format formatDescription]);
- if (prefer_mjpeg && fourcc == kCMVideoCodecType_JPEG_OpenDML) {
- best_fourcc = fourcc;
- break;
- }
-
// Compare according to Chromium preference.
if (media::VideoCaptureFormat::ComparePixelFormatPreference(
FourCCToChromiumPixelFormat(fourcc),
diff --git a/chromium/media/capture/video/shared_memory_buffer_tracker.cc b/chromium/media/capture/video/shared_memory_buffer_tracker.cc
index 6cd0b698736..7865039b34a 100644
--- a/chromium/media/capture/video/shared_memory_buffer_tracker.cc
+++ b/chromium/media/capture/video/shared_memory_buffer_tracker.cc
@@ -4,7 +4,8 @@
#include "media/capture/video/shared_memory_buffer_tracker.h"
-#include "base/logging.h"
+#include "base/check.h"
+#include "base/notreached.h"
#include "media/base/video_frame.h"
#include "mojo/public/cpp/system/platform_handle.h"
#include "ui/gfx/geometry/size.h"
diff --git a/chromium/media/capture/video/video_capture_buffer_handle.cc b/chromium/media/capture/video/video_capture_buffer_handle.cc
index 693c3d18fd2..40f8c92c331 100644
--- a/chromium/media/capture/video/video_capture_buffer_handle.cc
+++ b/chromium/media/capture/video/video_capture_buffer_handle.cc
@@ -4,7 +4,9 @@
#include "media/capture/video/video_capture_buffer_handle.h"
-#include "base/logging.h"
+#include <ostream>
+
+#include "base/notreached.h"
namespace media {
diff --git a/chromium/media/capture/video/video_capture_buffer_tracker.h b/chromium/media/capture/video/video_capture_buffer_tracker.h
index 19811b064b5..67576418379 100644
--- a/chromium/media/capture/video/video_capture_buffer_tracker.h
+++ b/chromium/media/capture/video/video_capture_buffer_tracker.h
@@ -62,6 +62,6 @@ class CAPTURE_EXPORT VideoCaptureBufferTracker {
int frame_feedback_id_;
};
-} // namespace content
+} // namespace media
#endif // MEDIA_CAPTURE_VIDEO_VIDEO_CAPTURE_BUFFER_TRACKER_H_
diff --git a/chromium/media/capture/video/video_capture_device_client_unittest.cc b/chromium/media/capture/video/video_capture_device_client_unittest.cc
index 0e69a632fa4..3175ba31e69 100644
--- a/chromium/media/capture/video/video_capture_device_client_unittest.cc
+++ b/chromium/media/capture/video/video_capture_device_client_unittest.cc
@@ -9,7 +9,7 @@
#include <memory>
#include "base/bind.h"
-#include "base/logging.h"
+#include "base/check.h"
#include "base/macros.h"
#include "build/build_config.h"
#include "media/base/limits.h"
diff --git a/chromium/media/capture/video/video_capture_device_descriptor.cc b/chromium/media/capture/video/video_capture_device_descriptor.cc
index be6c87ec4ce..81ee65a45b8 100644
--- a/chromium/media/capture/video/video_capture_device_descriptor.cc
+++ b/chromium/media/capture/video/video_capture_device_descriptor.cc
@@ -4,7 +4,6 @@
#include "media/capture/video/video_capture_device_descriptor.h"
-#include "base/logging.h"
#include "base/strings/string_util.h"
namespace media {
@@ -90,6 +89,8 @@ const char* VideoCaptureDeviceDescriptor::GetCaptureApiTypeString() const {
return "Camera API2 Full";
case VideoCaptureApi::ANDROID_API2_LIMITED:
return "Camera API2 Limited";
+ case VideoCaptureApi::FUCHSIA_CAMERA3:
+ return "fuchsia.camera3 API";
case VideoCaptureApi::VIRTUAL_DEVICE:
return "Virtual Device";
case VideoCaptureApi::UNKNOWN:
diff --git a/chromium/media/capture/video/video_capture_device_descriptor.h b/chromium/media/capture/video/video_capture_device_descriptor.h
index 6d690a4e192..1c3da3f99dc 100644
--- a/chromium/media/capture/video/video_capture_device_descriptor.h
+++ b/chromium/media/capture/video/video_capture_device_descriptor.h
@@ -26,6 +26,7 @@ enum class VideoCaptureApi {
ANDROID_API2_LEGACY,
ANDROID_API2_FULL,
ANDROID_API2_LIMITED,
+ FUCHSIA_CAMERA3,
VIRTUAL_DEVICE,
UNKNOWN
};
diff --git a/chromium/media/capture/video/video_capture_device_unittest.cc b/chromium/media/capture/video/video_capture_device_unittest.cc
index 7e690783c24..d7d800d6a08 100644
--- a/chromium/media/capture/video/video_capture_device_unittest.cc
+++ b/chromium/media/capture/video/video_capture_device_unittest.cc
@@ -69,8 +69,9 @@
#define MAYBE_UsingRealWebcam_CaptureWithSize UsingRealWebcam_CaptureWithSize
#define MAYBE_UsingRealWebcam_CheckPhotoCallbackRelease \
UsingRealWebcam_CheckPhotoCallbackRelease
-#elif defined(OS_WIN)
-// TODO(crbug.com/893494): Fails on win: error: Value of: device_descriptor.
+#elif defined(OS_WIN) || defined(OS_FUCHSIA)
+// Windows test bots don't have camera.
+// On Fuchsia the tests run under emulator that doesn't support camera.
#define MAYBE_UsingRealWebcam_AllocateBadSize \
DISABLED_UsingRealWebcam_AllocateBadSize
#define MAYBE_UsingRealWebcam_CaptureMjpeg DISABLED_UsingRealWebcam_CaptureMjpeg
@@ -97,7 +98,9 @@
#define MAYBE_UsingRealWebcam_CaptureMjpeg UsingRealWebcam_CaptureMjpeg
#define MAYBE_UsingRealWebcam_TakePhoto UsingRealWebcam_TakePhoto
#define MAYBE_UsingRealWebcam_GetPhotoState UsingRealWebcam_GetPhotoState
-#define MAYBE_UsingRealWebcam_CaptureWithSize UsingRealWebcam_CaptureWithSize
+// Flaky crash: https://crbug.com/1069608
+#define MAYBE_UsingRealWebcam_CaptureWithSize \
+ DISABLED_UsingRealWebcam_CaptureWithSize
#define MAYBE_UsingRealWebcam_CheckPhotoCallbackRelease \
UsingRealWebcam_CheckPhotoCallbackRelease
#elif defined(OS_LINUX)
@@ -219,6 +222,18 @@ class MockImageCaptureClient
mojom::PhotoStatePtr state_;
};
+base::test::SingleThreadTaskEnvironment::MainThreadType kMainThreadType =
+#if defined(OS_MACOSX)
+ // Video capture code on MacOSX must run on a CFRunLoop enabled thread
+ // for interaction with AVFoundation.
+ base::test::SingleThreadTaskEnvironment::MainThreadType::UI;
+#elif defined(OS_FUCHSIA)
+ // FIDL APIs on Fuchsia requires IO thread.
+ base::test::SingleThreadTaskEnvironment::MainThreadType::IO;
+#else
+ base::test::SingleThreadTaskEnvironment::MainThreadType::DEFAULT;
+#endif
+
} // namespace
class VideoCaptureDeviceTest
@@ -247,13 +262,7 @@ class VideoCaptureDeviceTest
typedef VideoCaptureDevice::Client Client;
VideoCaptureDeviceTest()
- :
-#if defined(OS_MACOSX)
- // Video capture code on MacOSX must run on a CFRunLoop enabled thread
- // for interaction with AVFoundation.
- task_environment_(
- base::test::SingleThreadTaskEnvironment::MainThreadType::UI),
-#endif
+ : task_environment_(kMainThreadType),
device_descriptors_(new VideoCaptureDeviceDescriptors()),
main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()),
video_capture_client_(CreateDeviceClient()),
@@ -466,8 +475,9 @@ class VideoCaptureDeviceTest
std::unique_ptr<VideoCaptureDeviceFactory> video_capture_device_factory_;
};
+// Causes a flaky crash on Chrome OS. https://crbug.com/1069608
// Cause hangs on Windows Debug. http://crbug.com/417824
-#if defined(OS_WIN) && !defined(NDEBUG)
+#if defined(OS_CHROMEOS) || (defined(OS_WIN) && !defined(NDEBUG))
#define MAYBE_OpenInvalidDevice DISABLED_OpenInvalidDevice
#else
#define MAYBE_OpenInvalidDevice OpenInvalidDevice
diff --git a/chromium/media/capture/video/win/capability_list_win.cc b/chromium/media/capture/video/win/capability_list_win.cc
index c16708dc315..1e4a3425309 100644
--- a/chromium/media/capture/video/win/capability_list_win.cc
+++ b/chromium/media/capture/video/win/capability_list_win.cc
@@ -7,7 +7,7 @@
#include <algorithm>
#include <functional>
-#include "base/logging.h"
+#include "base/check.h"
#include "media/capture/video_capture_types.h"
namespace media {
diff --git a/chromium/media/capture/video/win/filter_base_win.cc b/chromium/media/capture/video/win/filter_base_win.cc
index 5ad3b757ad2..f014afff6b0 100644
--- a/chromium/media/capture/video/win/filter_base_win.cc
+++ b/chromium/media/capture/video/win/filter_base_win.cc
@@ -4,6 +4,8 @@
#include "media/capture/video/win/filter_base_win.h"
+#include "base/notreached.h"
+
#pragma comment(lib, "strmiids.lib")
namespace media {
diff --git a/chromium/media/capture/video/win/metrics.cc b/chromium/media/capture/video/win/metrics.cc
index f128ee18e0b..812030168d5 100644
--- a/chromium/media/capture/video/win/metrics.cc
+++ b/chromium/media/capture/video/win/metrics.cc
@@ -6,6 +6,7 @@
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
+#include "base/notreached.h"
namespace media {
diff --git a/chromium/media/capture/video/win/pin_base_win.cc b/chromium/media/capture/video/win/pin_base_win.cc
index a5d1c10998b..a2e840ff98f 100644
--- a/chromium/media/capture/video/win/pin_base_win.cc
+++ b/chromium/media/capture/video/win/pin_base_win.cc
@@ -4,7 +4,8 @@
#include "media/capture/video/win/pin_base_win.h"
-#include "base/logging.h"
+#include "base/check.h"
+#include "base/notreached.h"
namespace media {
diff --git a/chromium/media/capture/video/win/sink_filter_win.cc b/chromium/media/capture/video/win/sink_filter_win.cc
index 0b6c8e76fd3..90ad4ac4486 100644
--- a/chromium/media/capture/video/win/sink_filter_win.cc
+++ b/chromium/media/capture/video/win/sink_filter_win.cc
@@ -4,7 +4,6 @@
#include "media/capture/video/win/sink_filter_win.h"
-#include "base/logging.h"
#include "media/capture/video/win/sink_input_pin_win.h"
namespace media {
diff --git a/chromium/media/capture/video/win/video_capture_device_mf_win.cc b/chromium/media/capture/video/win/video_capture_device_mf_win.cc
index 5d71ad2721b..c432b210efd 100644
--- a/chromium/media/capture/video/win/video_capture_device_mf_win.cc
+++ b/chromium/media/capture/video/win/video_capture_device_mf_win.cc
@@ -32,6 +32,17 @@ using Microsoft::WRL::ComPtr;
namespace media {
+#if DCHECK_IS_ON()
+#define DLOG_IF_FAILED_WITH_HRESULT(message, hr) \
+ { \
+ DLOG_IF(ERROR, FAILED(hr)) \
+ << (message) << ": " << logging::SystemErrorCodeToString(hr); \
+ }
+#else
+#define DLOG_IF_FAILED_WITH_HRESULT(message, hr) \
+ {}
+#endif
+
namespace {
class MFPhotoCallback final
@@ -345,6 +356,26 @@ HRESULT CreateCaptureEngine(IMFCaptureEngine** engine) {
return capture_engine_class_factory->CreateInstance(CLSID_MFCaptureEngine,
IID_PPV_ARGS(engine));
}
+
+// Retrieves the control range and value, and
+// optionally returns the associated supported and current mode.
+template <typename ControlInterface, typename ControlProperty>
+mojom::RangePtr RetrieveControlRangeAndCurrent(
+ ComPtr<ControlInterface>& control_interface,
+ ControlProperty control_property,
+ std::vector<mojom::MeteringMode>* supported_modes = nullptr,
+ mojom::MeteringMode* current_mode = nullptr,
+ double (*value_converter)(long) = PlatformToCaptureValue,
+ double (*step_converter)(long) = PlatformToCaptureValue) {
+ return media::RetrieveControlRangeAndCurrent(
+ [&control_interface, control_property](auto... args) {
+ return control_interface->GetRange(control_property, args...);
+ },
+ [&control_interface, control_property](auto... args) {
+ return control_interface->Get(control_property, args...);
+ },
+ supported_modes, current_mode, value_converter, step_converter);
+}
} // namespace
class MFVideoCallback final
@@ -407,9 +438,46 @@ class MFVideoCallback final
ComPtr<IMFMediaBuffer> buffer;
sample->GetBufferByIndex(i, &buffer);
if (buffer) {
- DWORD length = 0, max_length = 0;
- BYTE* data = NULL;
- buffer->Lock(&data, &max_length, &length);
+ // Lock the buffer using the fastest method that it supports. The
+ // Lock2DSize() method is faster than Lock2D(), which is faster than
+ // Lock().
+ DWORD length = 0;
+ BYTE* data = nullptr;
+ ComPtr<IMF2DBuffer> buffer_2d;
+ if (SUCCEEDED(buffer.As(&buffer_2d))) {
+ HRESULT lock_result;
+ BYTE* scanline_0 = nullptr;
+ LONG pitch = 0;
+ ComPtr<IMF2DBuffer2> buffer_2d_2;
+ if (SUCCEEDED(buffer.As(&buffer_2d_2))) {
+ BYTE* data_start;
+ lock_result =
+ buffer_2d_2->Lock2DSize(MF2DBuffer_LockFlags_Read, &scanline_0,
+ &pitch, &data_start, &length);
+ } else {
+ lock_result = buffer_2d->Lock2D(&scanline_0, &pitch);
+ }
+ if (SUCCEEDED(lock_result)) {
+ // Use |buffer_2d| only if it is contiguous and has positive pitch.
+ BOOL is_contiguous;
+ if (pitch > 0 &&
+ SUCCEEDED(buffer_2d->IsContiguousFormat(&is_contiguous)) &&
+ is_contiguous &&
+ (length ||
+ SUCCEEDED(buffer_2d->GetContiguousLength(&length)))) {
+ data = scanline_0;
+ } else {
+ buffer_2d->Unlock2D();
+ }
+ }
+ }
+ if (!data) {
+ // If the faster methods fail, fall back to Lock to lock the buffer.
+ buffer_2d = nullptr;
+ DWORD max_length = 0;
+ buffer->Lock(&data, &max_length, &length);
+ }
+
if (data) {
observer_->OnIncomingCapturedData(data, length, reference_time,
timestamp);
@@ -418,7 +486,12 @@ class MFVideoCallback final
VideoCaptureFrameDropReason::
kWinMediaFoundationLockingBufferDelieveredNullptr);
}
- buffer->Unlock();
+
+ if (buffer_2d)
+ buffer_2d->Unlock2D();
+ else
+ buffer->Unlock();
+
} else {
observer_->OnFrameDropped(
VideoCaptureFrameDropReason::
@@ -581,7 +654,10 @@ VideoCaptureDeviceMFWin::VideoCaptureDeviceMFWin(
source_(source),
engine_(engine),
is_started_(false),
- has_sent_on_started_to_client_(false) {
+ has_sent_on_started_to_client_(false),
+ exposure_mode_manual_(false),
+ focus_mode_manual_(false),
+ white_balance_mode_manual_(false) {
DETACH_FROM_SEQUENCE(sequence_checker_);
}
@@ -602,14 +678,20 @@ VideoCaptureDeviceMFWin::~VideoCaptureDeviceMFWin() {
bool VideoCaptureDeviceMFWin::Init() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!is_initialized_);
+ HRESULT hr;
- HRESULT hr = S_OK;
- if (!engine_)
- hr = CreateCaptureEngine(&engine_);
+ hr = source_.As(&camera_control_);
+ DLOG_IF_FAILED_WITH_HRESULT("Failed to retrieve IAMCameraControl", hr);
- if (FAILED(hr)) {
- LogError(FROM_HERE, hr);
- return false;
+ hr = source_.As(&video_control_);
+ DLOG_IF_FAILED_WITH_HRESULT("Failed to retrieve IAMVideoProcAmp", hr);
+
+ if (!engine_) {
+ hr = CreateCaptureEngine(&engine_);
+ if (FAILED(hr)) {
+ LogError(FROM_HERE, hr);
+ return false;
+ }
}
ComPtr<IMFAttributes> attributes;
@@ -931,6 +1013,48 @@ void VideoCaptureDeviceMFWin::GetPhotoState(GetPhotoStateCallback callback) {
photo_capabilities->width = mojom::Range::New(
max_size.width(), min_size.width(), current_size.width(), 1);
+ if (camera_control_ && video_control_) {
+ photo_capabilities->color_temperature = RetrieveControlRangeAndCurrent(
+ video_control_, VideoProcAmp_WhiteBalance,
+ &photo_capabilities->supported_white_balance_modes,
+ &photo_capabilities->current_white_balance_mode);
+
+ photo_capabilities->exposure_time = RetrieveControlRangeAndCurrent(
+ camera_control_, CameraControl_Exposure,
+ &photo_capabilities->supported_exposure_modes,
+ &photo_capabilities->current_exposure_mode,
+ PlatformExposureTimeToCaptureValue, PlatformExposureTimeToCaptureStep);
+
+ photo_capabilities->focus_distance = RetrieveControlRangeAndCurrent(
+ camera_control_, CameraControl_Focus,
+ &photo_capabilities->supported_focus_modes,
+ &photo_capabilities->current_focus_mode);
+
+ photo_capabilities->brightness =
+ RetrieveControlRangeAndCurrent(video_control_, VideoProcAmp_Brightness);
+ photo_capabilities->contrast =
+ RetrieveControlRangeAndCurrent(video_control_, VideoProcAmp_Contrast);
+ photo_capabilities->exposure_compensation =
+ RetrieveControlRangeAndCurrent(video_control_, VideoProcAmp_Gain);
+ // There is no ISO control property in IAMCameraControl or IAMVideoProcAmp
+ // interfaces nor any other control property with direct mapping to ISO.
+ photo_capabilities->iso = mojom::Range::New();
+ photo_capabilities->red_eye_reduction = mojom::RedEyeReduction::NEVER;
+ photo_capabilities->saturation =
+ RetrieveControlRangeAndCurrent(video_control_, VideoProcAmp_Saturation);
+ photo_capabilities->sharpness =
+ RetrieveControlRangeAndCurrent(video_control_, VideoProcAmp_Sharpness);
+ photo_capabilities->torch = false;
+ photo_capabilities->pan = RetrieveControlRangeAndCurrent(
+ camera_control_, CameraControl_Pan, nullptr, nullptr,
+ PlatformAngleToCaptureValue, PlatformAngleToCaptureStep);
+ photo_capabilities->tilt = RetrieveControlRangeAndCurrent(
+ camera_control_, CameraControl_Tilt, nullptr, nullptr,
+ PlatformAngleToCaptureValue, PlatformAngleToCaptureStep);
+ photo_capabilities->zoom =
+ RetrieveControlRangeAndCurrent(camera_control_, CameraControl_Zoom);
+ }
+
std::move(callback).Run(std::move(photo_capabilities));
}
@@ -979,6 +1103,131 @@ void VideoCaptureDeviceMFWin::SetPhotoOptions(
selected_photo_capability_.reset(new CapabilityWin(best_match));
}
+ if (camera_control_ && video_control_) {
+ if (settings->has_white_balance_mode) {
+ if (settings->white_balance_mode == mojom::MeteringMode::CONTINUOUS) {
+ hr = video_control_->Set(VideoProcAmp_WhiteBalance, 0L,
+ VideoProcAmp_Flags_Auto);
+ DLOG_IF_FAILED_WITH_HRESULT("Auto white balance config failed", hr);
+ if (FAILED(hr))
+ return;
+ white_balance_mode_manual_ = false;
+ } else {
+ white_balance_mode_manual_ = true;
+ }
+ }
+ if (white_balance_mode_manual_ && settings->has_color_temperature) {
+ hr = video_control_->Set(VideoProcAmp_WhiteBalance,
+ settings->color_temperature,
+ VideoProcAmp_Flags_Manual);
+ DLOG_IF_FAILED_WITH_HRESULT("Color temperature config failed", hr);
+ if (FAILED(hr))
+ return;
+ }
+
+ if (settings->has_exposure_mode) {
+ if (settings->exposure_mode == mojom::MeteringMode::CONTINUOUS) {
+ hr = camera_control_->Set(CameraControl_Exposure, 0L,
+ CameraControl_Flags_Auto);
+ DLOG_IF_FAILED_WITH_HRESULT("Auto exposure config failed", hr);
+ if (FAILED(hr))
+ return;
+ exposure_mode_manual_ = false;
+ } else {
+ exposure_mode_manual_ = true;
+ }
+ }
+ if (exposure_mode_manual_ && settings->has_exposure_time) {
+ hr = camera_control_->Set(
+ CameraControl_Exposure,
+ CaptureExposureTimeToPlatformValue(settings->exposure_time),
+ CameraControl_Flags_Manual);
+ DLOG_IF_FAILED_WITH_HRESULT("Exposure Time config failed", hr);
+ if (FAILED(hr))
+ return;
+ }
+
+ if (settings->has_focus_mode) {
+ if (settings->focus_mode == mojom::MeteringMode::CONTINUOUS) {
+ hr = camera_control_->Set(CameraControl_Focus, 0L,
+ CameraControl_Flags_Auto);
+ DLOG_IF_FAILED_WITH_HRESULT("Auto focus config failed", hr);
+ if (FAILED(hr))
+ return;
+ focus_mode_manual_ = false;
+ } else {
+ focus_mode_manual_ = true;
+ }
+ }
+ if (focus_mode_manual_ && settings->has_focus_distance) {
+ hr = camera_control_->Set(CameraControl_Focus, settings->focus_distance,
+ CameraControl_Flags_Manual);
+ DLOG_IF_FAILED_WITH_HRESULT("Focus Distance config failed", hr);
+ if (FAILED(hr))
+ return;
+ }
+
+ if (settings->has_brightness) {
+ hr = video_control_->Set(VideoProcAmp_Brightness, settings->brightness,
+ VideoProcAmp_Flags_Manual);
+ DLOG_IF_FAILED_WITH_HRESULT("Brightness config failed", hr);
+ if (FAILED(hr))
+ return;
+ }
+ if (settings->has_contrast) {
+ hr = video_control_->Set(VideoProcAmp_Contrast, settings->contrast,
+ VideoProcAmp_Flags_Manual);
+ DLOG_IF_FAILED_WITH_HRESULT("Contrast config failed", hr);
+ if (FAILED(hr))
+ return;
+ }
+ if (settings->has_exposure_compensation) {
+ hr = video_control_->Set(VideoProcAmp_Gain,
+ settings->exposure_compensation,
+ VideoProcAmp_Flags_Manual);
+ DLOG_IF_FAILED_WITH_HRESULT("Exposure Compensation config failed", hr);
+ if (FAILED(hr))
+ return;
+ }
+ if (settings->has_saturation) {
+ hr = video_control_->Set(VideoProcAmp_Saturation, settings->saturation,
+ VideoProcAmp_Flags_Manual);
+ DLOG_IF_FAILED_WITH_HRESULT("Saturation config failed", hr);
+ if (FAILED(hr))
+ return;
+ }
+ if (settings->has_sharpness) {
+ hr = video_control_->Set(VideoProcAmp_Sharpness, settings->sharpness,
+ VideoProcAmp_Flags_Manual);
+ DLOG_IF_FAILED_WITH_HRESULT("Sharpness config failed", hr);
+ if (FAILED(hr))
+ return;
+ }
+ if (settings->has_pan) {
+ hr = camera_control_->Set(CameraControl_Pan,
+ CaptureAngleToPlatformValue(settings->pan),
+ CameraControl_Flags_Manual);
+ DLOG_IF_FAILED_WITH_HRESULT("Pan config failed", hr);
+ if (FAILED(hr))
+ return;
+ }
+ if (settings->has_tilt) {
+ hr = camera_control_->Set(CameraControl_Tilt,
+ CaptureAngleToPlatformValue(settings->tilt),
+ CameraControl_Flags_Manual);
+ DLOG_IF_FAILED_WITH_HRESULT("Tilt config failed", hr);
+ if (FAILED(hr))
+ return;
+ }
+ if (settings->has_zoom) {
+ hr = camera_control_->Set(CameraControl_Zoom, settings->zoom,
+ CameraControl_Flags_Manual);
+ DLOG_IF_FAILED_WITH_HRESULT("Zoom config failed", hr);
+ if (FAILED(hr))
+ return;
+ }
+ }
+
std::move(callback).Run(true);
}
diff --git a/chromium/media/capture/video/win/video_capture_device_mf_win.h b/chromium/media/capture/video/win/video_capture_device_mf_win.h
index 36111a184b2..e5ecd1250ba 100644
--- a/chromium/media/capture/video/win/video_capture_device_mf_win.h
+++ b/chromium/media/capture/video/win/video_capture_device_mf_win.h
@@ -13,6 +13,7 @@
#include <mfidl.h>
#include <mfreadwrite.h>
#include <stdint.h>
+#include <strmif.h>
#include <wrl/client.h>
#include <vector>
@@ -131,12 +132,18 @@ class CAPTURE_EXPORT VideoCaptureDeviceMFWin : public VideoCaptureDevice {
std::unique_ptr<VideoCaptureDevice::Client> client_;
const Microsoft::WRL::ComPtr<IMFMediaSource> source_;
+ Microsoft::WRL::ComPtr<IAMCameraControl> camera_control_;
+ Microsoft::WRL::ComPtr<IAMVideoProcAmp> video_control_;
Microsoft::WRL::ComPtr<IMFCaptureEngine> engine_;
std::unique_ptr<CapabilityWin> selected_video_capability_;
CapabilityList photo_capabilities_;
std::unique_ptr<CapabilityWin> selected_photo_capability_;
bool is_started_;
bool has_sent_on_started_to_client_;
+ // These flags keep the manual/auto mode between cycles of SetPhotoOptions().
+ bool exposure_mode_manual_;
+ bool focus_mode_manual_;
+ bool white_balance_mode_manual_;
base::queue<TakePhotoCallback> video_stream_take_photo_callbacks_;
SEQUENCE_CHECKER(sequence_checker_);
diff --git a/chromium/media/capture/video/win/video_capture_device_utils_win.cc b/chromium/media/capture/video/win/video_capture_device_utils_win.cc
index 868ae1990b4..d6bf4584c77 100644
--- a/chromium/media/capture/video/win/video_capture_device_utils_win.cc
+++ b/chromium/media/capture/video/win/video_capture_device_utils_win.cc
@@ -4,6 +4,7 @@
#include "media/capture/video/win/video_capture_device_utils_win.h"
+#include <cmath>
#include <iostream>
#include "base/win/win_util.h"
@@ -11,6 +12,40 @@
namespace media {
+namespace {
+const int kDegreesToArcSeconds = 3600;
+const int kSecondsTo100MicroSeconds = 10000;
+} // namespace
+
+// Windows platform stores pan and tilt (min, max, step and current) in
+// degrees. Spec expects them in arc seconds.
+// https://docs.microsoft.com/en-us/windows/win32/api/strmif/ne-strmif-cameracontrolproperty
+// spec: https://w3c.github.io/mediacapture-image/#pan
+long CaptureAngleToPlatformValue(double arc_seconds) {
+ return std::round(arc_seconds / kDegreesToArcSeconds);
+}
+
+double PlatformAngleToCaptureValue(long degrees) {
+ return 1.0 * degrees * kDegreesToArcSeconds;
+}
+
+// Windows platform stores exposure time (min, max and current) in log base 2
+// seconds. If value is n, exposure time is 2^n seconds. Spec expects exposure
+// times in 100 micro seconds.
+// https://docs.microsoft.com/en-us/windows/win32/api/strmif/ne-strmif-cameracontrolproperty
+// spec: https://w3c.github.io/mediacapture-image/#exposure-time
+long CaptureExposureTimeToPlatformValue(double hundreds_of_microseconds) {
+ return std::log2(hundreds_of_microseconds / kSecondsTo100MicroSeconds);
+}
+
+double PlatformExposureTimeToCaptureValue(long log_seconds) {
+ return std::exp2(log_seconds) * kSecondsTo100MicroSeconds;
+}
+
+double PlatformExposureTimeToCaptureStep(long log_step) {
+ return std::exp2(log_step);
+}
+
// Note: Because we can't find a solid way to detect camera location (front/back
// or external USB camera) with Win32 APIs, assume it's always front camera when
// auto rotation is enabled for now.
diff --git a/chromium/media/capture/video/win/video_capture_device_utils_win.h b/chromium/media/capture/video/win/video_capture_device_utils_win.h
index 84c6b1a8239..cff4bfc1b99 100644
--- a/chromium/media/capture/video/win/video_capture_device_utils_win.h
+++ b/chromium/media/capture/video/win/video_capture_device_utils_win.h
@@ -5,12 +5,33 @@
#ifndef MEDIA_CAPTURE_VIDEO_WIN_VIDEO_CAPTURE_DEVICE_UTILS_WIN_H_
#define MEDIA_CAPTURE_VIDEO_WIN_VIDEO_CAPTURE_DEVICE_UTILS_WIN_H_
+// Avoid including strsafe.h via dshow as it will cause build warnings.
+#define NO_DSHOW_STRSAFE
+#include <dshow.h>
#include <windows.h>
#include "media/base/video_facing.h"
+#include "media/capture/mojom/image_capture_types.h"
namespace media {
+// Windows platform stores pan and tilt (min, max, step and current) in
+// degrees. Spec expects them in arc seconds.
+// https://docs.microsoft.com/en-us/windows/win32/api/strmif/ne-strmif-cameracontrolproperty
+// spec: https://w3c.github.io/mediacapture-image/#pan
+long CaptureAngleToPlatformValue(double arc_seconds);
+double PlatformAngleToCaptureValue(long degrees);
+constexpr auto PlatformAngleToCaptureStep = PlatformAngleToCaptureValue;
+
+// Windows platform stores exposure time (min, max and current) in log base 2
+// seconds. If value is n, exposure time is 2^n seconds. Spec expects exposure
+// times in 100 micro seconds.
+// https://docs.microsoft.com/en-us/windows/win32/api/strmif/ne-strmif-cameracontrolproperty
+// spec: https://w3c.github.io/mediacapture-image/#exposure-time
+long CaptureExposureTimeToPlatformValue(double hundreds_of_microseconds);
+double PlatformExposureTimeToCaptureValue(long log_seconds);
+double PlatformExposureTimeToCaptureStep(long log_step);
+
// Returns the rotation of the camera. Returns 0 if it's not a built-in camera,
// or auto-rotation is not enabled, or only displays on external monitors.
int GetCameraRotation(VideoFacingMode facing);
@@ -30,6 +51,57 @@ HRESULT CheckPathInfoForInternal(const PCWSTR device_name);
// Returns true if this is an integrated display panel.
bool IsInternalVideoOutput(
const DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY video_output_tech_type);
+
+static double PlatformToCaptureValue(long value) {
+ return value;
+}
+constexpr auto PlatformToCaptureStep = PlatformToCaptureValue;
+
+// Retrieves the control range and value using the provided getters, and
+// optionally returns the associated supported and current mode.
+template <typename RangeGetter, typename CurrentValueGetter>
+static mojom::RangePtr RetrieveControlRangeAndCurrent(
+ RangeGetter range_getter,
+ CurrentValueGetter current_value_getter,
+ std::vector<mojom::MeteringMode>* supported_modes = nullptr,
+ mojom::MeteringMode* current_mode = nullptr,
+ double (*value_converter)(long) = PlatformToCaptureValue,
+ double (*step_converter)(long) = PlatformToCaptureStep) {
+ auto control_range = mojom::Range::New();
+
+ long min, max, step, default_value, flags;
+ HRESULT hr = range_getter(&min, &max, &step, &default_value, &flags);
+ DLOG_IF(ERROR, FAILED(hr)) << "Control range reading failed: "
+ << logging::SystemErrorCodeToString(hr);
+ if (SUCCEEDED(hr)) {
+ control_range->min = value_converter(min);
+ control_range->max = value_converter(max);
+ control_range->step = step_converter(step);
+ if (supported_modes != nullptr) {
+ if (flags & CameraControl_Flags_Auto)
+ supported_modes->push_back(mojom::MeteringMode::CONTINUOUS);
+ if (flags & CameraControl_Flags_Manual)
+ supported_modes->push_back(mojom::MeteringMode::MANUAL);
+ }
+ }
+
+ long current;
+ hr = current_value_getter(&current, &flags);
+ DLOG_IF(ERROR, FAILED(hr)) << "Control value reading failed: "
+ << logging::SystemErrorCodeToString(hr);
+ if (SUCCEEDED(hr)) {
+ control_range->current = value_converter(current);
+ if (current_mode != nullptr) {
+ if (flags & CameraControl_Flags_Auto)
+ *current_mode = mojom::MeteringMode::CONTINUOUS;
+ else if (flags & CameraControl_Flags_Manual)
+ *current_mode = mojom::MeteringMode::MANUAL;
+ }
+ }
+
+ return control_range;
+}
+
} // namespace media
-#endif // MEDIA_CAPTURE_VIDEO_WIN_VIDEO_CAPTURE_DEVICE_UTILS_WIN_H_ \ No newline at end of file
+#endif // MEDIA_CAPTURE_VIDEO_WIN_VIDEO_CAPTURE_DEVICE_UTILS_WIN_H_
diff --git a/chromium/media/capture/video/win/video_capture_device_win.cc b/chromium/media/capture/video/win/video_capture_device_win.cc
index 96421b15d91..ff7d0f13cad 100644
--- a/chromium/media/capture/video/win/video_capture_device_win.cc
+++ b/chromium/media/capture/video/win/video_capture_device_win.cc
@@ -29,19 +29,6 @@ using base::win::ScopedCoMem;
using base::win::ScopedVariant;
using Microsoft::WRL::ComPtr;
-namespace {
-const int kSecondsTo100MicroSeconds = 10000;
-
-// Windows platform stores exposure time (min, max and current) in log base 2
-// seconds. If value is n, exposure time is 2^n seconds. Spec expects exposure
-// times in 100 micro seconds.
-// https://docs.microsoft.com/en-us/previous-versions/ms784800(v%3Dvs.85)
-// spec: https://w3c.github.io/mediacapture-image/#exposure-time
-long ConvertWindowsTimeToSpec(long seconds) {
- return (std::exp2(seconds) * kSecondsTo100MicroSeconds);
-}
-} // namespace
-
namespace media {
#if DCHECK_IS_ON()
@@ -81,45 +68,6 @@ bool PinMatchesMajorType(IPin* pin, REFGUID major_type) {
return SUCCEEDED(hr) && connection_media_type.majortype == major_type;
}
-// Retrieves the control range and value using the provided getters, and
-// optionally returns the associated supported and current mode.
-template <typename RangeGetter, typename ValueGetter>
-mojom::RangePtr RetrieveControlRangeAndCurrent(
- RangeGetter range_getter,
- ValueGetter value_getter,
- std::vector<mojom::MeteringMode>* supported_modes = nullptr,
- mojom::MeteringMode* current_mode = nullptr) {
- auto control_range = mojom::Range::New();
- long min, max, step, default_value, flags;
- HRESULT hr = range_getter(&min, &max, &step, &default_value, &flags);
- DLOG_IF_FAILED_WITH_HRESULT("Control range reading failed", hr);
- if (SUCCEEDED(hr)) {
- control_range->min = min;
- control_range->max = max;
- control_range->step = step;
- if (supported_modes != nullptr) {
- if (flags & CameraControl_Flags_Auto)
- supported_modes->push_back(mojom::MeteringMode::CONTINUOUS);
- if (flags & CameraControl_Flags_Manual)
- supported_modes->push_back(mojom::MeteringMode::MANUAL);
- }
- }
- long current;
- hr = value_getter(&current, &flags);
- DLOG_IF_FAILED_WITH_HRESULT("Control value reading failed", hr);
- if (SUCCEEDED(hr)) {
- control_range->current = current;
- if (current_mode != nullptr) {
- if (flags & CameraControl_Flags_Auto)
- *current_mode = mojom::MeteringMode::CONTINUOUS;
- else if (flags & CameraControl_Flags_Manual)
- *current_mode = mojom::MeteringMode::MANUAL;
- }
- }
-
- return control_range;
-}
-
// static
void VideoCaptureDeviceWin::GetDeviceCapabilityList(
const std::string& device_id,
@@ -674,18 +622,8 @@ void VideoCaptureDeviceWin::GetPhotoState(GetPhotoStateCallback callback) {
return this->camera_control_->get_Exposure(args...);
},
&photo_capabilities->supported_exposure_modes,
- &photo_capabilities->current_exposure_mode);
-
- // Windows returns the exposure time in log base 2 seconds.
- // If value is n, exposure time is 2^n seconds.
- photo_capabilities->exposure_time->min =
- ConvertWindowsTimeToSpec(photo_capabilities->exposure_time->min);
- photo_capabilities->exposure_time->max =
- ConvertWindowsTimeToSpec(photo_capabilities->exposure_time->max);
- photo_capabilities->exposure_time->step =
- std::exp2(photo_capabilities->exposure_time->step);
- photo_capabilities->exposure_time->current =
- ConvertWindowsTimeToSpec(photo_capabilities->exposure_time->current);
+ &photo_capabilities->current_exposure_mode,
+ PlatformExposureTimeToCaptureValue, PlatformExposureTimeToCaptureStep);
photo_capabilities->color_temperature = RetrieveControlRangeAndCurrent(
[this](auto... args) {
@@ -737,7 +675,20 @@ void VideoCaptureDeviceWin::GetPhotoState(GetPhotoStateCallback callback) {
[this](auto... args) {
return this->video_control_->get_Sharpness(args...);
});
-
+ photo_capabilities->pan = RetrieveControlRangeAndCurrent(
+ [this](auto... args) {
+ return this->camera_control_->getRange_Pan(args...);
+ },
+ [this](auto... args) { return this->camera_control_->get_Pan(args...); },
+ nullptr, nullptr, PlatformAngleToCaptureValue,
+ PlatformAngleToCaptureStep);
+ photo_capabilities->tilt = RetrieveControlRangeAndCurrent(
+ [this](auto... args) {
+ return this->camera_control_->getRange_Tilt(args...);
+ },
+ [this](auto... args) { return this->camera_control_->get_Tilt(args...); },
+ nullptr, nullptr, PlatformAngleToCaptureValue,
+ PlatformAngleToCaptureStep);
photo_capabilities->zoom = RetrieveControlRangeAndCurrent(
[this](auto... args) {
return this->camera_control_->getRange_Zoom(args...);
@@ -770,13 +721,6 @@ void VideoCaptureDeviceWin::SetPhotoOptions(
HRESULT hr;
- if (settings->has_zoom) {
- hr = camera_control_->put_Zoom(settings->zoom, CameraControl_Flags_Manual);
- DLOG_IF_FAILED_WITH_HRESULT("Zoom config failed", hr);
- if (FAILED(hr))
- return;
- }
-
if (settings->has_white_balance_mode) {
if (settings->white_balance_mode == mojom::MeteringMode::CONTINUOUS) {
hr = video_control_->put_WhiteBalance(0L, VideoProcAmp_Flags_Auto);
@@ -791,7 +735,7 @@ void VideoCaptureDeviceWin::SetPhotoOptions(
}
if (white_balance_mode_manual_ && settings->has_color_temperature) {
hr = video_control_->put_WhiteBalance(settings->color_temperature,
- CameraControl_Flags_Manual);
+ VideoProcAmp_Flags_Manual);
DLOG_IF_FAILED_WITH_HRESULT("Color temperature config failed", hr);
if (FAILED(hr))
return;
@@ -799,7 +743,7 @@ void VideoCaptureDeviceWin::SetPhotoOptions(
if (settings->has_focus_mode) {
if (settings->focus_mode == mojom::MeteringMode::CONTINUOUS) {
- hr = camera_control_->put_Focus(0L, VideoProcAmp_Flags_Auto);
+ hr = camera_control_->put_Focus(0L, CameraControl_Flags_Auto);
DLOG_IF_FAILED_WITH_HRESULT("Auto focus config failed", hr);
if (FAILED(hr))
return;
@@ -819,7 +763,7 @@ void VideoCaptureDeviceWin::SetPhotoOptions(
if (settings->has_exposure_mode) {
if (settings->exposure_mode == mojom::MeteringMode::CONTINUOUS) {
- hr = camera_control_->put_Exposure(0L, VideoProcAmp_Flags_Auto);
+ hr = camera_control_->put_Exposure(0L, CameraControl_Flags_Auto);
DLOG_IF_FAILED_WITH_HRESULT("Auto exposure config failed", hr);
if (FAILED(hr))
return;
@@ -832,7 +776,7 @@ void VideoCaptureDeviceWin::SetPhotoOptions(
if (exposure_mode_manual_ && settings->has_exposure_time) {
// Windows expects the exposure time in log base 2 seconds.
hr = camera_control_->put_Exposure(
- std::log2(settings->exposure_time / kSecondsTo100MicroSeconds),
+ CaptureExposureTimeToPlatformValue(settings->exposure_time),
CameraControl_Flags_Manual);
DLOG_IF_FAILED_WITH_HRESULT("Exposure Time config failed", hr);
if (FAILED(hr))
@@ -840,32 +784,52 @@ void VideoCaptureDeviceWin::SetPhotoOptions(
}
if (settings->has_brightness) {
hr = video_control_->put_Brightness(settings->brightness,
- CameraControl_Flags_Manual);
+ VideoProcAmp_Flags_Manual);
DLOG_IF_FAILED_WITH_HRESULT("Brightness config failed", hr);
if (FAILED(hr))
return;
}
if (settings->has_contrast) {
hr = video_control_->put_Contrast(settings->contrast,
- CameraControl_Flags_Manual);
+ VideoProcAmp_Flags_Manual);
DLOG_IF_FAILED_WITH_HRESULT("Contrast config failed", hr);
if (FAILED(hr))
return;
}
if (settings->has_saturation) {
hr = video_control_->put_Saturation(settings->saturation,
- CameraControl_Flags_Manual);
+ VideoProcAmp_Flags_Manual);
DLOG_IF_FAILED_WITH_HRESULT("Saturation config failed", hr);
if (FAILED(hr))
return;
}
if (settings->has_sharpness) {
hr = video_control_->put_Sharpness(settings->sharpness,
- CameraControl_Flags_Manual);
+ VideoProcAmp_Flags_Manual);
DLOG_IF_FAILED_WITH_HRESULT("Sharpness config failed", hr);
if (FAILED(hr))
return;
}
+ if (settings->has_pan) {
+ hr = camera_control_->put_Pan(CaptureAngleToPlatformValue(settings->pan),
+ CameraControl_Flags_Manual);
+ DLOG_IF_FAILED_WITH_HRESULT("Pan config failed", hr);
+ if (FAILED(hr))
+ return;
+ }
+ if (settings->has_tilt) {
+ hr = camera_control_->put_Tilt(CaptureAngleToPlatformValue(settings->tilt),
+ CameraControl_Flags_Manual);
+ DLOG_IF_FAILED_WITH_HRESULT("Tilt config failed", hr);
+ if (FAILED(hr))
+ return;
+ }
+ if (settings->has_zoom) {
+ hr = camera_control_->put_Zoom(settings->zoom, CameraControl_Flags_Manual);
+ DLOG_IF_FAILED_WITH_HRESULT("Zoom config failed", hr);
+ if (FAILED(hr))
+ return;
+ }
std::move(callback).Run(true);
}
diff --git a/chromium/media/capture/video_capture_types.cc b/chromium/media/capture/video_capture_types.cc
index 585dafba21b..bbee389d99f 100644
--- a/chromium/media/capture/video_capture_types.cc
+++ b/chromium/media/capture/video_capture_types.cc
@@ -4,7 +4,7 @@
#include "media/capture/video_capture_types.h"
-#include "base/logging.h"
+#include "base/check.h"
#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "media/base/limits.h"
diff --git a/chromium/media/capture/video_capture_types.h b/chromium/media/capture/video_capture_types.h
index 98fca4a9bd0..4fab0d0a07f 100644
--- a/chromium/media/capture/video_capture_types.h
+++ b/chromium/media/capture/video_capture_types.h
@@ -181,7 +181,14 @@ enum class VideoCaptureError {
kMacAvFoundationReceivedAVCaptureSessionRuntimeErrorNotification = 113,
kAndroidApi2ErrorConfiguringCamera = 114,
kCrosHalV3DeviceDelegateFailedToFlush = 115,
- kMaxValue = 115
+ kFuchsiaCameraDeviceDisconnected = 116,
+ kFuchsiaCameraStreamDisconnected = 117,
+ kFuchsiaSysmemDidNotSetImageFormat = 118,
+ kFuchsiaSysmemInvalidBufferIndex = 119,
+ kFuchsiaSysmemInvalidBufferSize = 120,
+ kFuchsiaUnsupportedPixelFormat = 121,
+ kFuchsiaFailedToMapSysmemBuffer = 122,
+ kMaxValue = 122
};
// WARNING: Do not change the values assigned to the entries. They are used for
diff --git a/chromium/media/cast/BUILD.gn b/chromium/media/cast/BUILD.gn
index a915bacefa2..ccd2acdb8ba 100644
--- a/chromium/media/cast/BUILD.gn
+++ b/chromium/media/cast/BUILD.gn
@@ -406,6 +406,7 @@ if (is_win || is_mac || (is_linux && !is_chromeos)) {
executable("cast_receiver_app") {
testonly = true
sources = [ "test/receiver.cc" ]
+ public_deps = []
deps = [
":common",
":net",
@@ -423,10 +424,7 @@ if (is_win || is_mac || (is_linux && !is_chromeos)) {
"test/linux_output_window.cc",
"test/linux_output_window.h",
]
- configs += [
- "//build/config/linux:x11",
- "//build/config/linux:xext",
- ]
+ public_deps += [ "//ui/gfx/x" ]
deps += [ "//third_party/libyuv" ]
}
}
diff --git a/chromium/media/cast/cast_config.cc b/chromium/media/cast/cast_config.cc
index 3a27dc0db88..30e6c41c74c 100644
--- a/chromium/media/cast/cast_config.cc
+++ b/chromium/media/cast/cast_config.cc
@@ -23,10 +23,15 @@ VideoCodecParams::~VideoCodecParams() = default;
FrameSenderConfig::FrameSenderConfig()
: sender_ssrc(0),
receiver_ssrc(0),
+ // In production, these values are overridden by the mirror settings
+ // and potentially the mirroring session parameters, however we provide
+ // a reasonable default here for some use cases, such as tests.
+ // All three delays are set to the same value due to adaptive latency
+ // being disabled in Chrome. This will be fixed as part of the migration
+ // to libcast.
min_playout_delay(
base::TimeDelta::FromMilliseconds(kDefaultRtpMaxDelayMs)),
- max_playout_delay(
- base::TimeDelta::FromMilliseconds(kDefaultRtpMaxDelayMs)),
+ max_playout_delay(min_playout_delay),
animated_playout_delay(min_playout_delay),
rtp_payload_type(RtpPayloadType::UNKNOWN),
use_external_encoder(false),
diff --git a/chromium/media/cast/cast_environment.cc b/chromium/media/cast/cast_environment.cc
index 281b904903f..0f9b4ca896f 100644
--- a/chromium/media/cast/cast_environment.cc
+++ b/chromium/media/cast/cast_environment.cc
@@ -8,7 +8,7 @@
#include "base/bind.h"
#include "base/location.h"
-#include "base/logging.h"
+#include "base/notreached.h"
using base::SingleThreadTaskRunner;
diff --git a/chromium/media/cast/cast_sender.h b/chromium/media/cast/cast_sender.h
index b7bd819efc5..48b2d3d33d6 100644
--- a/chromium/media/cast/cast_sender.h
+++ b/chromium/media/cast/cast_sender.h
@@ -82,6 +82,9 @@ class AudioFrameInput : public base::RefCountedThreadSafe<AudioFrameInput> {
// have halted the session.
using StatusChangeCallback = base::RepeatingCallback<void(OperationalStatus)>;
+// The equivalent of StatusChangeCallback when only one change is expected.
+using StatusChangeOnceCallback = base::OnceCallback<void(OperationalStatus)>;
+
// All methods of CastSender must be called on the main thread.
// Provided CastTransport will also be called on the main thread.
class CastSender {
@@ -100,9 +103,8 @@ class CastSender {
// Initialize the audio stack. Must be called in order to send audio frames.
// |status_change_cb| will be run as operational status changes.
- virtual void InitializeAudio(
- const FrameSenderConfig& audio_config,
- const StatusChangeCallback& status_change_cb) = 0;
+ virtual void InitializeAudio(const FrameSenderConfig& audio_config,
+ StatusChangeOnceCallback status_change_cb) = 0;
// Initialize the video stack. Must be called in order to send video frames.
// |status_change_cb| will be run as operational status changes.
diff --git a/chromium/media/cast/cast_sender_impl.cc b/chromium/media/cast/cast_sender_impl.cc
index 7d9392b5981..bf75331db7e 100644
--- a/chromium/media/cast/cast_sender_impl.cc
+++ b/chromium/media/cast/cast_sender_impl.cc
@@ -104,7 +104,7 @@ CastSenderImpl::CastSenderImpl(scoped_refptr<CastEnvironment> cast_environment,
void CastSenderImpl::InitializeAudio(
const FrameSenderConfig& audio_config,
- const StatusChangeCallback& status_change_cb) {
+ StatusChangeOnceCallback status_change_cb) {
DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
CHECK(audio_config.use_external_encoder ||
cast_environment_->HasAudioThread());
@@ -113,8 +113,8 @@ void CastSenderImpl::InitializeAudio(
audio_sender_ = std::make_unique<AudioSender>(
cast_environment_, audio_config,
- base::BindRepeating(&CastSenderImpl::OnAudioStatusChange,
- weak_factory_.GetWeakPtr(), status_change_cb),
+ base::BindOnce(&CastSenderImpl::OnAudioStatusChange,
+ weak_factory_.GetWeakPtr(), std::move(status_change_cb)),
transport_sender_);
if (video_sender_) {
DCHECK(audio_sender_->GetTargetPlayoutDelay() ==
@@ -169,14 +169,14 @@ void CastSenderImpl::SetTargetPlayoutDelay(
}
void CastSenderImpl::OnAudioStatusChange(
- const StatusChangeCallback& status_change_cb,
+ StatusChangeOnceCallback status_change_cb,
OperationalStatus status) {
DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN));
if (status == STATUS_INITIALIZED && !audio_frame_input_) {
audio_frame_input_ =
new LocalAudioFrameInput(cast_environment_, audio_sender_->AsWeakPtr());
}
- status_change_cb.Run(status);
+ std::move(status_change_cb).Run(status);
}
void CastSenderImpl::OnVideoStatusChange(
diff --git a/chromium/media/cast/cast_sender_impl.h b/chromium/media/cast/cast_sender_impl.h
index 9ec3651f5d6..0d36d971bb7 100644
--- a/chromium/media/cast/cast_sender_impl.h
+++ b/chromium/media/cast/cast_sender_impl.h
@@ -27,7 +27,7 @@ class CastSenderImpl : public CastSender {
CastTransport* const transport_sender);
void InitializeAudio(const FrameSenderConfig& audio_config,
- const StatusChangeCallback& status_change_cb) final;
+ StatusChangeOnceCallback status_change_cb) final;
void InitializeVideo(
const FrameSenderConfig& video_config,
const StatusChangeCallback& status_change_cb,
@@ -43,7 +43,7 @@ class CastSenderImpl : public CastSender {
private:
void ReceivedPacket(std::unique_ptr<Packet> packet);
- void OnAudioStatusChange(const StatusChangeCallback& status_change_cb,
+ void OnAudioStatusChange(StatusChangeOnceCallback status_change_cb,
OperationalStatus status);
void OnVideoStatusChange(const StatusChangeCallback& status_change_cb,
OperationalStatus status);
diff --git a/chromium/media/cast/common/clock_drift_smoother.cc b/chromium/media/cast/common/clock_drift_smoother.cc
index 79a60212bcd..dd8c39e61ed 100644
--- a/chromium/media/cast/common/clock_drift_smoother.cc
+++ b/chromium/media/cast/common/clock_drift_smoother.cc
@@ -6,7 +6,8 @@
#include <stdint.h>
-#include "base/logging.h"
+#include "base/check.h"
+#include "base/notreached.h"
namespace media {
namespace cast {
diff --git a/chromium/media/cast/logging/log_deserializer.cc b/chromium/media/cast/logging/log_deserializer.cc
index b006df4951f..64c90ee88f8 100644
--- a/chromium/media/cast/logging/log_deserializer.cc
+++ b/chromium/media/cast/logging/log_deserializer.cc
@@ -10,6 +10,7 @@
#include <utility>
#include "base/big_endian.h"
+#include "base/logging.h"
#include "third_party/zlib/zlib.h"
using media::cast::FrameEventMap;
diff --git a/chromium/media/cast/logging/logging_defines.cc b/chromium/media/cast/logging/logging_defines.cc
index a7e4c714d2d..b492c0a18ac 100644
--- a/chromium/media/cast/logging/logging_defines.cc
+++ b/chromium/media/cast/logging/logging_defines.cc
@@ -4,7 +4,7 @@
#include "media/cast/logging/logging_defines.h"
-#include "base/logging.h"
+#include "base/notreached.h"
#define ENUM_TO_STRING(enum) \
case enum: \
diff --git a/chromium/media/cast/logging/proto/proto_utils.cc b/chromium/media/cast/logging/proto/proto_utils.cc
index 03251e64c03..63eb55717d0 100644
--- a/chromium/media/cast/logging/proto/proto_utils.cc
+++ b/chromium/media/cast/logging/proto/proto_utils.cc
@@ -4,7 +4,7 @@
#include "media/cast/logging/proto/proto_utils.h"
-#include "base/logging.h"
+#include "base/notreached.h"
#define TO_PROTO_ENUM(enum) \
case enum: \
diff --git a/chromium/media/cast/logging/receiver_time_offset_estimator_impl.cc b/chromium/media/cast/logging/receiver_time_offset_estimator_impl.cc
index 2979cc621b2..b401ead8d9d 100644
--- a/chromium/media/cast/logging/receiver_time_offset_estimator_impl.cc
+++ b/chromium/media/cast/logging/receiver_time_offset_estimator_impl.cc
@@ -5,7 +5,7 @@
#include <algorithm>
#include <utility>
-#include "base/logging.h"
+#include "base/check.h"
#include "base/time/tick_clock.h"
#include "media/cast/logging/receiver_time_offset_estimator_impl.h"
diff --git a/chromium/media/cast/logging/simple_event_subscriber.cc b/chromium/media/cast/logging/simple_event_subscriber.cc
index d0f0df567c1..86901a82e4a 100644
--- a/chromium/media/cast/logging/simple_event_subscriber.cc
+++ b/chromium/media/cast/logging/simple_event_subscriber.cc
@@ -4,7 +4,7 @@
#include "media/cast/logging/simple_event_subscriber.h"
-#include "base/logging.h"
+#include "base/check.h"
namespace media {
namespace cast {
diff --git a/chromium/media/cast/logging/stats_event_subscriber.cc b/chromium/media/cast/logging/stats_event_subscriber.cc
index 985c69137c7..50fc1620b56 100644
--- a/chromium/media/cast/logging/stats_event_subscriber.cc
+++ b/chromium/media/cast/logging/stats_event_subscriber.cc
@@ -9,8 +9,9 @@
#include <memory>
#include <utility>
+#include "base/check_op.h"
#include "base/format_macros.h"
-#include "base/logging.h"
+#include "base/notreached.h"
#include "base/strings/stringprintf.h"
#include "base/values.h"
diff --git a/chromium/media/cast/net/cast_transport_config.h b/chromium/media/cast/net/cast_transport_config.h
index 126d942a4e7..5042241637a 100644
--- a/chromium/media/cast/net/cast_transport_config.h
+++ b/chromium/media/cast/net/cast_transport_config.h
@@ -127,7 +127,7 @@ class PacketTransport {
// SendPacket again until |cb| has been called. Any other errors that
// occur will be reported through side channels, in such cases, this function
// will return true indicating that the channel is not blocked.
- virtual bool SendPacket(PacketRef packet, const base::Closure& cb) = 0;
+ virtual bool SendPacket(PacketRef packet, base::OnceClosure cb) = 0;
// Returns the number of bytes ever sent.
virtual int64_t GetBytesSent() = 0;
diff --git a/chromium/media/cast/net/cast_transport_impl_unittest.cc b/chromium/media/cast/net/cast_transport_impl_unittest.cc
index f78e63540c6..137378e5e52 100644
--- a/chromium/media/cast/net/cast_transport_impl_unittest.cc
+++ b/chromium/media/cast/net/cast_transport_impl_unittest.cc
@@ -47,10 +47,10 @@ class FakePacketSender : public PacketTransport {
public:
FakePacketSender() : paused_(false), packets_sent_(0), bytes_sent_(0) {}
- bool SendPacket(PacketRef packet, const base::Closure& cb) final {
+ bool SendPacket(PacketRef packet, base::OnceClosure cb) final {
if (paused_) {
stored_packet_ = packet;
- callback_ = cb;
+ callback_ = std::move(cb);
return false;
}
++packets_sent_;
@@ -67,8 +67,8 @@ class FakePacketSender : public PacketTransport {
void SetPaused(bool paused) {
paused_ = paused;
if (!paused && stored_packet_.get()) {
- SendPacket(stored_packet_, callback_);
- callback_.Run();
+ SendPacket(stored_packet_, base::OnceClosure());
+ std::move(callback_).Run();
}
}
@@ -76,7 +76,7 @@ class FakePacketSender : public PacketTransport {
private:
bool paused_;
- base::Closure callback_;
+ base::OnceClosure callback_;
PacketRef stored_packet_;
int packets_sent_;
int64_t bytes_sent_;
diff --git a/chromium/media/cast/net/pacing/paced_sender.cc b/chromium/media/cast/net/pacing/paced_sender.cc
index c1554090254..3e1e8232b42 100644
--- a/chromium/media/cast/net/pacing/paced_sender.cc
+++ b/chromium/media/cast/net/pacing/paced_sender.cc
@@ -224,10 +224,9 @@ bool PacedSender::SendRtcpPacket(uint32_t ssrc, PacketRef packet) {
priority_packet_list_[key] = make_pair(PacketType_RTCP, packet);
} else {
// We pass the RTCP packets straight through.
- if (!transport_->SendPacket(
- packet,
- base::Bind(&PacedSender::SendStoredPackets,
- weak_factory_.GetWeakPtr()))) {
+ if (!transport_->SendPacket(packet,
+ base::BindOnce(&PacedSender::SendStoredPackets,
+ weak_factory_.GetWeakPtr()))) {
state_ = State_TransportBlocked;
}
}
@@ -248,8 +247,8 @@ void PacedSender::CancelSendingPacket(const PacketKey& packet_key) {
PacketRef PacedSender::PopNextPacket(PacketType* packet_type,
PacketKey* packet_key) {
// Always pop from the priority list first.
- PacketList* list = !priority_packet_list_.empty() ?
- &priority_packet_list_ : &packet_list_;
+ PacketList* list =
+ !priority_packet_list_.empty() ? &priority_packet_list_ : &packet_list_;
DCHECK(!list->empty());
// Determine which packet in the frame should be popped by examining the
@@ -354,8 +353,8 @@ void PacedSender::SendStoredPackets() {
next_next_max_burst_size_ = max_burst_size;
}
- base::Closure cb = base::Bind(&PacedSender::SendStoredPackets,
- weak_factory_.GetWeakPtr());
+ base::RepeatingClosure cb = base::BindRepeating(
+ &PacedSender::SendStoredPackets, weak_factory_.GetWeakPtr());
while (!empty()) {
if (current_burst_size_ >= current_max_burst_size_) {
transport_task_runner_->PostDelayedTask(FROM_HERE,
diff --git a/chromium/media/cast/net/pacing/paced_sender_unittest.cc b/chromium/media/cast/net/pacing/paced_sender_unittest.cc
index 37c7ba8761d..67c829abbf6 100644
--- a/chromium/media/cast/net/pacing/paced_sender_unittest.cc
+++ b/chromium/media/cast/net/pacing/paced_sender_unittest.cc
@@ -42,7 +42,7 @@ class TestPacketSender : public PacketTransport {
public:
TestPacketSender() : bytes_sent_(0) {}
- bool SendPacket(PacketRef packet, const base::Closure& cb) final {
+ bool SendPacket(PacketRef packet, base::OnceClosure cb) final {
EXPECT_FALSE(expected_packet_sizes_.empty());
size_t expected_packet_size = expected_packet_sizes_.front();
expected_packet_sizes_.pop_front();
@@ -184,16 +184,14 @@ TEST_F(PacedSenderTest, PassThroughRtcp) {
mock_transport_.AddExpectedSizesAndPacketIds(kSize2, kRtcpPacketIdMagic, 1);
Packet tmp(kSize2, kValue);
- EXPECT_TRUE(paced_sender_->SendRtcpPacket(
- 1,
- new base::RefCountedData<Packet>(tmp)));
+ EXPECT_TRUE(
+ paced_sender_->SendRtcpPacket(1, new base::RefCountedData<Packet>(tmp)));
}
TEST_F(PacedSenderTest, BasicPace) {
int num_of_packets = 27;
- SendPacketVector packets = CreateSendPacketVector(kSize1,
- num_of_packets,
- false);
+ SendPacketVector packets =
+ CreateSendPacketVector(kSize1, num_of_packets, false);
const base::TimeTicks earliest_event_timestamp = testing_clock_.NowTicks();
mock_transport_.AddExpectedSizesAndPacketIds(kSize1, UINT16_C(0), 10);
@@ -294,7 +292,7 @@ TEST_F(PacedSenderTest, PaceWithNack) {
int expected_video_network_event_count = num_of_packets_in_frame;
int expected_video_retransmitted_event_count = 2 * num_of_packets_in_nack;
- expected_video_retransmitted_event_count -= 2; // 2 packets deduped
+ expected_video_retransmitted_event_count -= 2; // 2 packets deduped
int expected_audio_network_event_count = num_of_packets_in_frame;
EXPECT_EQ(expected_video_network_event_count +
expected_video_retransmitted_event_count +
@@ -412,26 +410,24 @@ TEST_F(PacedSenderTest, SendPriority) {
paced_sender_->RegisterPrioritySsrc(kAudioSsrc);
// Retransmission packets with the earlier timestamp.
- SendPacketVector resend_packets =
- CreateSendPacketVector(kSize4, 10, false);
+ SendPacketVector resend_packets = CreateSendPacketVector(kSize4, 10, false);
testing_clock_.Advance(base::TimeDelta::FromMilliseconds(10));
// Send 20 normal video packets. Only 10 will be sent in this
// call, the rest will be sitting in the queue waiting for pacing.
- EXPECT_TRUE(paced_sender_->SendPackets(
- CreateSendPacketVector(kSize2, 20, false)));
+ EXPECT_TRUE(
+ paced_sender_->SendPackets(CreateSendPacketVector(kSize2, 20, false)));
testing_clock_.Advance(base::TimeDelta::FromMilliseconds(10));
// Send normal audio packet. This is queued and will be sent
// earlier than video packets.
- EXPECT_TRUE(paced_sender_->SendPackets(
- CreateSendPacketVector(kSize1, 1, true)));
+ EXPECT_TRUE(
+ paced_sender_->SendPackets(CreateSendPacketVector(kSize1, 1, true)));
// Send RTCP packet. This is queued and will be sent first.
EXPECT_TRUE(paced_sender_->SendRtcpPacket(
- kVideoSsrc,
- new base::RefCountedData<Packet>(Packet(kSize3, kValue))));
+ kVideoSsrc, new base::RefCountedData<Packet>(Packet(kSize3, kValue))));
// Resend video packets. This is queued and will be sent
// earlier than normal video packets.
diff --git a/chromium/media/cast/net/rtcp/receiver_rtcp_event_subscriber.cc b/chromium/media/cast/net/rtcp/receiver_rtcp_event_subscriber.cc
index ac4c05d589e..a888edf7166 100644
--- a/chromium/media/cast/net/rtcp/receiver_rtcp_event_subscriber.cc
+++ b/chromium/media/cast/net/rtcp/receiver_rtcp_event_subscriber.cc
@@ -7,6 +7,8 @@
#include <algorithm>
#include <utility>
+#include "base/logging.h"
+
namespace media {
namespace cast {
diff --git a/chromium/media/cast/net/rtcp/test_rtcp_packet_builder.cc b/chromium/media/cast/net/rtcp/test_rtcp_packet_builder.cc
index dd03612346f..8c00d8e9b15 100644
--- a/chromium/media/cast/net/rtcp/test_rtcp_packet_builder.cc
+++ b/chromium/media/cast/net/rtcp/test_rtcp_packet_builder.cc
@@ -4,7 +4,7 @@
#include "media/cast/net/rtcp/test_rtcp_packet_builder.h"
-#include "base/logging.h"
+#include "base/check_op.h"
#include "media/cast/net/rtcp/rtcp_utility.h"
namespace media {
diff --git a/chromium/media/cast/net/rtp/frame_buffer.cc b/chromium/media/cast/net/rtp/frame_buffer.cc
index 1c345799e2e..255b5067157 100644
--- a/chromium/media/cast/net/rtp/frame_buffer.cc
+++ b/chromium/media/cast/net/rtp/frame_buffer.cc
@@ -4,7 +4,7 @@
#include "media/cast/net/rtp/frame_buffer.h"
-#include "base/logging.h"
+#include "base/check_op.h"
namespace media {
namespace cast {
diff --git a/chromium/media/cast/net/rtp/packet_storage.cc b/chromium/media/cast/net/rtp/packet_storage.cc
index 53c0d358191..15bb977779f 100644
--- a/chromium/media/cast/net/rtp/packet_storage.cc
+++ b/chromium/media/cast/net/rtp/packet_storage.cc
@@ -4,7 +4,8 @@
#include "media/cast/net/rtp/packet_storage.h"
-#include "base/logging.h"
+#include "base/check_op.h"
+#include "base/notreached.h"
#include "media/cast/constants.h"
namespace media {
diff --git a/chromium/media/cast/net/rtp/receiver_stats.cc b/chromium/media/cast/net/rtp/receiver_stats.cc
index 5c7b4295d9c..c9d3f68c203 100644
--- a/chromium/media/cast/net/rtp/receiver_stats.cc
+++ b/chromium/media/cast/net/rtp/receiver_stats.cc
@@ -4,7 +4,6 @@
#include "media/cast/net/rtp/receiver_stats.h"
-#include "base/logging.h"
#include "media/cast/net/rtp/rtp_defines.h"
namespace media {
diff --git a/chromium/media/cast/net/rtp/rtp_packet_builder.cc b/chromium/media/cast/net/rtp/rtp_packet_builder.cc
index dbeeedeb7a6..c49c7189db7 100644
--- a/chromium/media/cast/net/rtp/rtp_packet_builder.cc
+++ b/chromium/media/cast/net/rtp/rtp_packet_builder.cc
@@ -5,7 +5,7 @@
#include "media/cast/net/rtp/rtp_packet_builder.h"
#include "base/big_endian.h"
-#include "base/logging.h"
+#include "base/check_op.h"
namespace media {
namespace cast {
diff --git a/chromium/media/cast/net/rtp/rtp_packetizer.cc b/chromium/media/cast/net/rtp/rtp_packetizer.cc
index 403de59a809..f4778415c01 100644
--- a/chromium/media/cast/net/rtp/rtp_packetizer.cc
+++ b/chromium/media/cast/net/rtp/rtp_packetizer.cc
@@ -7,7 +7,7 @@
#include <string>
#include "base/big_endian.h"
-#include "base/logging.h"
+#include "base/check_op.h"
#include "media/cast/net/pacing/paced_sender.h"
#include "media/cast/net/rtp/rtp_defines.h"
diff --git a/chromium/media/cast/net/rtp/rtp_packetizer_unittest.cc b/chromium/media/cast/net/rtp/rtp_packetizer_unittest.cc
index 9b3e50685f0..84dab88d15a 100644
--- a/chromium/media/cast/net/rtp/rtp_packetizer_unittest.cc
+++ b/chromium/media/cast/net/rtp/rtp_packetizer_unittest.cc
@@ -27,7 +27,7 @@ static const uint16_t kSeqNum = 33;
static const int kMaxPacketLength = 1500;
static const int kSsrc = 0x12345;
static const unsigned int kFrameSize = 5000;
-}
+} // namespace
class TestRtpPacketTransport : public PacketTransport {
public:
@@ -65,7 +65,7 @@ class TestRtpPacketTransport : public PacketTransport {
}
}
- bool SendPacket(PacketRef packet, const base::Closure& cb) final {
+ bool SendPacket(PacketRef packet, base::OnceClosure cb) final {
++packets_sent_;
RtpParser parser(kSsrc, kPayload);
RtpCastHeader rtp_header;
@@ -122,8 +122,8 @@ class RtpPacketizerTest : public ::testing::Test {
&testing_clock_, nullptr, transport_.get(),
task_runner_));
pacer_->RegisterSsrc(config_.ssrc, false);
- rtp_packetizer_.reset(new RtpPacketizer(
- pacer_.get(), &packet_storage_, config_));
+ rtp_packetizer_.reset(
+ new RtpPacketizer(pacer_.get(), &packet_storage_, config_));
video_frame_.dependency = EncodedFrame::DEPENDENT;
video_frame_.frame_id = FrameId::first() + 1;
video_frame_.referenced_frame_id = video_frame_.frame_id - 1;
diff --git a/chromium/media/cast/net/rtp/rtp_parser.cc b/chromium/media/cast/net/rtp/rtp_parser.cc
index 4d85bae8e18..f9ec4374d11 100644
--- a/chromium/media/cast/net/rtp/rtp_parser.cc
+++ b/chromium/media/cast/net/rtp/rtp_parser.cc
@@ -5,7 +5,7 @@
#include "media/cast/net/rtp/rtp_parser.h"
#include "base/big_endian.h"
-#include "base/logging.h"
+#include "base/check.h"
#include "media/cast/constants.h"
#include "media/cast/net/rtp/rtp_defines.h"
diff --git a/chromium/media/cast/net/udp_transport_impl.cc b/chromium/media/cast/net/udp_transport_impl.cc
index 6b9993d3fa6..9c6fd7d248c 100644
--- a/chromium/media/cast/net/udp_transport_impl.cc
+++ b/chromium/media/cast/net/udp_transport_impl.cc
@@ -220,8 +220,7 @@ void UdpTransportImpl::ReceiveNextPacket(int length_or_status) {
}
}
-bool UdpTransportImpl::SendPacket(PacketRef packet,
- const base::RepeatingClosure& cb) {
+bool UdpTransportImpl::SendPacket(PacketRef packet, base::OnceClosure cb) {
DCHECK(io_thread_proxy_->RunsTasksInCurrentSequence());
if (!udp_socket_)
return true;
@@ -252,8 +251,9 @@ bool UdpTransportImpl::SendPacket(PacketRef packet,
reinterpret_cast<char*>(&packet->data.front()));
int result;
- base::RepeatingCallback<void(int)> callback = base::BindRepeating(
- &UdpTransportImpl::OnSent, weak_factory_.GetWeakPtr(), buf, packet, cb);
+ net::CompletionOnceCallback callback =
+ base::BindOnce(&UdpTransportImpl::OnSent, weak_factory_.GetWeakPtr(), buf,
+ packet, std::move(cb));
if (client_connected_) {
// If we called Connect() before we must call Write() instead of
// SendTo(). Otherwise on some platforms we might get
@@ -288,11 +288,11 @@ bool UdpTransportImpl::SendPacket(PacketRef packet,
result =
udp_socket_->Write(buf.get(), static_cast<int>(packet->data.size()),
- callback, traffic_annotation);
+ std::move(callback), traffic_annotation);
} else if (!IsEmpty(remote_addr_)) {
result =
udp_socket_->SendTo(buf.get(), static_cast<int>(packet->data.size()),
- remote_addr_, callback);
+ remote_addr_, std::move(callback));
} else {
VLOG(1) << "Failed to send packet; socket is neither bound nor "
<< "connected.";
@@ -313,7 +313,7 @@ int64_t UdpTransportImpl::GetBytesSent() {
void UdpTransportImpl::OnSent(const scoped_refptr<net::IOBuffer>& buf,
PacketRef packet,
- const base::RepeatingClosure& cb,
+ base::OnceClosure cb,
int result) {
DCHECK(io_thread_proxy_->RunsTasksInCurrentSequence());
@@ -324,7 +324,7 @@ void UdpTransportImpl::OnSent(const scoped_refptr<net::IOBuffer>& buf,
ScheduleReceiveNextPacket();
if (!cb.is_null()) {
- cb.Run();
+ std::move(cb).Run();
}
}
diff --git a/chromium/media/cast/net/udp_transport_impl.h b/chromium/media/cast/net/udp_transport_impl.h
index d6a6864a0a3..0b60a35bded 100644
--- a/chromium/media/cast/net/udp_transport_impl.h
+++ b/chromium/media/cast/net/udp_transport_impl.h
@@ -49,7 +49,7 @@ class UdpTransportImpl final : public PacketTransport, public UdpTransport {
~UdpTransportImpl() final;
// PacketTransport implementations.
- bool SendPacket(PacketRef packet, const base::RepeatingClosure& cb) final;
+ bool SendPacket(PacketRef packet, base::OnceClosure cb) final;
int64_t GetBytesSent() final;
// Start receiving packets. Packets are submitted to |packet_receiver|.
void StartReceiving(PacketReceiverCallbackWithStatus packet_receiver) final;
@@ -97,7 +97,7 @@ class UdpTransportImpl final : public PacketTransport, public UdpTransport {
void OnSent(const scoped_refptr<net::IOBuffer>& buf,
PacketRef packet,
- const base::RepeatingClosure& cb,
+ base::OnceClosure cb,
int result);
// Called by |reader_| when it completes reading a packet from the data pipe.
diff --git a/chromium/media/cast/sender/audio_sender.cc b/chromium/media/cast/sender/audio_sender.cc
index 7e60bcbef9c..b4b379059a3 100644
--- a/chromium/media/cast/sender/audio_sender.cc
+++ b/chromium/media/cast/sender/audio_sender.cc
@@ -7,7 +7,8 @@
#include <utility>
#include "base/bind.h"
-#include "base/logging.h"
+#include "base/check_op.h"
+#include "base/notreached.h"
#include "media/cast/common/rtp_time.h"
#include "media/cast/net/cast_transport_config.h"
#include "media/cast/sender/audio_encoder.h"
@@ -17,7 +18,7 @@ namespace cast {
AudioSender::AudioSender(scoped_refptr<CastEnvironment> cast_environment,
const FrameSenderConfig& audio_config,
- StatusChangeCallback status_change_cb,
+ StatusChangeOnceCallback status_change_cb,
CastTransport* const transport_sender)
: FrameSender(cast_environment,
transport_sender,
diff --git a/chromium/media/cast/sender/audio_sender.h b/chromium/media/cast/sender/audio_sender.h
index 124cb5afb65..ee3b6b5207c 100644
--- a/chromium/media/cast/sender/audio_sender.h
+++ b/chromium/media/cast/sender/audio_sender.h
@@ -33,7 +33,7 @@ class AudioSender : public FrameSender {
public:
AudioSender(scoped_refptr<CastEnvironment> cast_environment,
const FrameSenderConfig& audio_config,
- StatusChangeCallback status_change_cb,
+ StatusChangeOnceCallback status_change_cb,
CastTransport* const transport_sender);
~AudioSender() final;
diff --git a/chromium/media/cast/sender/audio_sender_unittest.cc b/chromium/media/cast/sender/audio_sender_unittest.cc
index e5440ccdf83..10551754218 100644
--- a/chromium/media/cast/sender/audio_sender_unittest.cc
+++ b/chromium/media/cast/sender/audio_sender_unittest.cc
@@ -58,7 +58,7 @@ class TestPacketSender : public PacketTransport {
public:
TestPacketSender() : number_of_rtp_packets_(0), number_of_rtcp_packets_(0) {}
- bool SendPacket(PacketRef packet, const base::Closure& cb) final {
+ bool SendPacket(PacketRef packet, base::OnceClosure cb) final {
if (IsRtcpPacket(&packet->data[0], packet->data.size())) {
++number_of_rtcp_packets_;
} else {
@@ -111,9 +111,8 @@ class AudioSenderTest : public ::testing::Test {
base::WrapUnique(transport_), task_runner_));
OperationalStatus operational_status = STATUS_UNINITIALIZED;
audio_sender_.reset(new AudioSender(
- cast_environment_,
- audio_config_,
- base::Bind(&SaveOperationalStatus, &operational_status),
+ cast_environment_, audio_config_,
+ base::BindOnce(&SaveOperationalStatus, &operational_status),
transport_sender_.get()));
task_runner_->RunTasks();
CHECK_EQ(STATUS_INITIALIZED, operational_status);
@@ -122,7 +121,7 @@ class AudioSenderTest : public ::testing::Test {
~AudioSenderTest() override = default;
base::SimpleTestTickClock testing_clock_;
- TestPacketSender* transport_; // Owned by CastTransport.
+ TestPacketSender* transport_; // Owned by CastTransport.
std::unique_ptr<CastTransportImpl> transport_sender_;
scoped_refptr<FakeSingleThreadTaskRunner> task_runner_;
std::unique_ptr<AudioSender> audio_sender_;
diff --git a/chromium/media/cast/sender/video_encoder_impl.cc b/chromium/media/cast/sender/video_encoder_impl.cc
index 50e74ffa685..4fc74e74b5d 100644
--- a/chromium/media/cast/sender/video_encoder_impl.cc
+++ b/chromium/media/cast/sender/video_encoder_impl.cc
@@ -9,7 +9,7 @@
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/callback.h"
-#include "base/logging.h"
+#include "base/check.h"
#include "media/base/video_frame.h"
#include "media/cast/sender/fake_software_video_encoder.h"
#include "media/cast/sender/vp8_encoder.h"
diff --git a/chromium/media/cast/sender/video_sender_unittest.cc b/chromium/media/cast/sender/video_sender_unittest.cc
index 5688b319cf8..e97396f68d0 100644
--- a/chromium/media/cast/sender/video_sender_unittest.cc
+++ b/chromium/media/cast/sender/video_sender_unittest.cc
@@ -41,7 +41,6 @@ static const int kHeight = 240;
using testing::_;
using testing::AtLeast;
-
void SaveOperationalStatus(OperationalStatus* out_status,
OperationalStatus in_status) {
DVLOG(1) << "OperationalStatus transitioning from " << *out_status << " to "
@@ -52,15 +51,13 @@ void SaveOperationalStatus(OperationalStatus* out_status,
class TestPacketSender : public PacketTransport {
public:
TestPacketSender()
- : number_of_rtp_packets_(0),
- number_of_rtcp_packets_(0),
- paused_(false) {}
+ : number_of_rtp_packets_(0), number_of_rtcp_packets_(0), paused_(false) {}
// A singular packet implies a RTCP packet.
- bool SendPacket(PacketRef packet, const base::Closure& cb) final {
+ bool SendPacket(PacketRef packet, base::OnceClosure cb) final {
if (paused_) {
stored_packet_ = packet;
- callback_ = cb;
+ callback_ = std::move(cb);
return false;
}
if (IsRtcpPacket(&packet->data[0], packet->data.size())) {
@@ -90,8 +87,8 @@ class TestPacketSender : public PacketTransport {
void SetPause(bool paused) {
paused_ = paused;
if (!paused && stored_packet_.get()) {
- SendPacket(stored_packet_, callback_);
- callback_.Run();
+ SendPacket(stored_packet_, base::OnceClosure());
+ std::move(callback_).Run();
}
}
@@ -99,14 +96,13 @@ class TestPacketSender : public PacketTransport {
int number_of_rtp_packets_;
int number_of_rtcp_packets_;
bool paused_;
- base::Closure callback_;
+ base::OnceClosure callback_;
PacketRef stored_packet_;
DISALLOW_COPY_AND_ASSIGN(TestPacketSender);
};
-void IgnorePlayoutDelayChanges(base::TimeDelta unused_playout_delay) {
-}
+void IgnorePlayoutDelayChanges(base::TimeDelta unused_playout_delay) {}
class PeerVideoSender : public VideoSender {
public:
diff --git a/chromium/media/cdm/BUILD.gn b/chromium/media/cdm/BUILD.gn
index 5ecddbe144e..806ce494dd1 100644
--- a/chromium/media/cdm/BUILD.gn
+++ b/chromium/media/cdm/BUILD.gn
@@ -78,14 +78,6 @@ source_set("cdm") {
"cdm_helpers.h",
"cdm_module.cc",
"cdm_module.h",
-
- # Not putting these under |enable_cdm_proxy| so we don't have to if/def
- # every usage of CdmProxy base types, e.g. in CdmCapability. This also
- # helps enable more tests.
- "cdm_proxy.cc",
- "cdm_proxy.h",
- "cdm_proxy_context.cc",
- "cdm_proxy_context.h",
"cdm_type_conversion.cc",
"cdm_type_conversion.h",
"cdm_wrapper.h",
diff --git a/chromium/media/cdm/aes_decryptor.h b/chromium/media/cdm/aes_decryptor.h
index f96e07a88a2..640e0ad1bf0 100644
--- a/chromium/media/cdm/aes_decryptor.h
+++ b/chromium/media/cdm/aes_decryptor.h
@@ -88,7 +88,6 @@ class MEDIA_EXPORT AesDecryptor : public ContentDecryptionModule,
private:
// Testing classes that needs to manipulate internal states for testing.
friend class ClearKeyPersistentSessionCdm;
- friend class ClearKeyCdmProxy;
// Internally this class supports persistent license type sessions so that
// it can be used by ClearKeyPersistentSessionCdm. The following methods
diff --git a/chromium/media/cdm/aes_decryptor_unittest.cc b/chromium/media/cdm/aes_decryptor_unittest.cc
index 1ff11c89d1a..0a812f3c310 100644
--- a/chromium/media/cdm/aes_decryptor_unittest.cc
+++ b/chromium/media/cdm/aes_decryptor_unittest.cc
@@ -59,9 +59,8 @@ MATCHER(NotEmpty, "") {
}
MATCHER(IsJSONDictionary, "") {
std::string result(arg.begin(), arg.end());
- std::unique_ptr<base::Value> root(
- base::JSONReader().ReadToValueDeprecated(result));
- return (root.get() && root->type() == base::Value::Type::DICTIONARY);
+ base::Optional<base::Value> root = base::JSONReader::Read(result);
+ return (root && root->type() == base::Value::Type::DICTIONARY);
}
MATCHER(IsNullTime, "") {
return arg.is_null();
diff --git a/chromium/media/cdm/api/content_decryption_module.h b/chromium/media/cdm/api/content_decryption_module.h
index 7cf9d3d8eae..0e7301230b9 100644
--- a/chromium/media/cdm/api/content_decryption_module.h
+++ b/chromium/media/cdm/api/content_decryption_module.h
@@ -8,7 +8,6 @@
#include <type_traits>
#include "content_decryption_module_export.h"
-#include "content_decryption_module_proxy.h"
#if defined(_MSC_VER)
typedef unsigned char uint8_t;
@@ -1348,19 +1347,6 @@ class CDM_CLASS_API Host_11 {
// CDM can call this method multiple times to operate on different files.
virtual FileIO* CreateFileIO(FileIOClient* client) = 0;
- // Requests a CdmProxy that proxies part of CDM functionalities to a different
- // entity, e.g. a hardware CDM module. A CDM instance can have at most one
- // CdmProxy throughout its lifetime, which must be requested and initialized
- // during CDM instance initialization time, i.e. in or after CDM::Initialize()
- // and before OnInitialized() is called, to ensure proper connection of the
- // CdmProxy and the media player (e.g. hardware decoder). The CdmProxy is
- // owned by the host and is guaranteed to be valid throughout the CDM
- // instance's lifetime. The CDM must ensure that the |client| remain valid
- // before the CDM instance is destroyed. Returns null if CdmProxy is not
- // supported, called before CDM::Initialize(), RequestCdmProxy() is called
- // more than once, or called after the CDM instance has been initialized.
- virtual CdmProxy* RequestCdmProxy(CdmProxyClient* client) = 0;
-
// Requests a specific version of the storage ID. A storage ID is a stable,
// device specific ID used by the CDM to securely store persistent data. The
// ID will be returned by the host via ContentDecryptionModule::OnStorageId().
diff --git a/chromium/media/cdm/api/content_decryption_module_proxy.h b/chromium/media/cdm/api/content_decryption_module_proxy.h
deleted file mode 100644
index 374f54a2a02..00000000000
--- a/chromium/media/cdm/api/content_decryption_module_proxy.h
+++ /dev/null
@@ -1,129 +0,0 @@
-// Copyright 2017 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 CDM_CONTENT_DECRYPTION_MODULE_PROXY_H_
-#define CDM_CONTENT_DECRYPTION_MODULE_PROXY_H_
-
-#include "content_decryption_module_export.h"
-
-#if defined(_MSC_VER)
-typedef unsigned char uint8_t;
-typedef unsigned int uint32_t;
-typedef unsigned __int64 uint64_t;
-#else
-#include <stdint.h>
-#endif
-
-namespace cdm {
-
-class CDM_CLASS_API CdmProxyClient;
-
-// A proxy class for the CDM.
-// In general, the interpretation of the CdmProxy and CdmProxyClient method
-// parameters are protocol dependent. For enum parameters, values outside the
-// enum range may not work.
-class CDM_CLASS_API CdmProxy {
- public:
- enum Function : uint32_t {
- // For Intel Negotiate Crypto SessionKey Exchange (CSME) path to call
- // ID3D11VideoContext::NegotiateCryptoSessionKeyExchange.
- kIntelNegotiateCryptoSessionKeyExchange = 1,
- // There will be more values in the future e.g. for D3D11 RSA method.
- };
-
- enum KeyType : uint32_t {
- kDecryptOnly = 0,
- kDecryptAndDecode = 1,
- };
-
- // Initializes the proxy. The results will be returned in
- // CdmProxyClient::OnInitialized().
- virtual void Initialize() = 0;
-
- // Processes and updates the state of the proxy.
- // |output_data_size| is required by some protocol to set up the output data.
- // The operation may fail if the |output_data_size| is wrong. The results will
- // be returned in CdmProxyClient::OnProcessed().
- virtual void Process(Function function,
- uint32_t crypto_session_id,
- const uint8_t* input_data,
- uint32_t input_data_size,
- uint32_t output_data_size) = 0;
-
- // Creates a crypto session for handling media.
- // If extra data has to be passed to further setup the media crypto session,
- // pass the data as |input_data|. The results will be returned in
- // CdmProxyClient::OnMediaCryptoSessionCreated().
- virtual void CreateMediaCryptoSession(const uint8_t* input_data,
- uint32_t input_data_size) = 0;
-
- // Sets a key for the session identified by |crypto_session_id|.
- virtual void SetKey(uint32_t crypto_session_id,
- const uint8_t* key_id,
- uint32_t key_id_size,
- KeyType key_type,
- const uint8_t* key_blob,
- uint32_t key_blob_size) = 0;
-
- // Removes a key for the session identified by |crypto_session_id|.
- virtual void RemoveKey(uint32_t crypto_session_id,
- const uint8_t* key_id,
- uint32_t key_id_size) = 0;
-
- protected:
- CdmProxy() {}
- virtual ~CdmProxy() {}
-};
-
-// Responses to CdmProxy calls. All responses will be called asynchronously.
-class CDM_CLASS_API CdmProxyClient {
- public:
- enum Status : uint32_t {
- kOk,
- kFail,
- };
-
- enum Protocol : uint32_t {
- kNone = 0, // No protocol supported. Can be used in failure cases.
- kIntel, // Method using Intel CSME.
- // There will be more values in the future e.g. kD3D11RsaHardware,
- // kD3D11RsaSoftware to use the D3D11 RSA method.
- };
-
- // Callback for Initialize(). If the proxy created a crypto session, then the
- // ID for the crypto session is |crypto_session_id|.
- virtual void OnInitialized(Status status,
- Protocol protocol,
- uint32_t crypto_session_id) = 0;
-
- // Callback for Process(). |output_data| is the output of processing.
- virtual void OnProcessed(Status status,
- const uint8_t* output_data,
- uint32_t output_data_size) = 0;
-
- // Callback for CreateMediaCryptoSession(). On success:
- // - |crypto_session_id| is the ID for the created crypto session.
- // - |output_data| is extra value, if any.
- // Otherwise, |crypto_session_id| and |output_data| should be ignored.
- virtual void OnMediaCryptoSessionCreated(Status status,
- uint32_t crypto_session_id,
- uint64_t output_data) = 0;
-
- // Callback for SetKey().
- virtual void OnKeySet(Status status) = 0;
-
- // Callback for RemoveKey().
- virtual void OnKeyRemoved(Status status) = 0;
-
- // Called when there is a hardware reset and all the hardware context is lost.
- virtual void NotifyHardwareReset() = 0;
-
- protected:
- CdmProxyClient() {}
- virtual ~CdmProxyClient() {}
-};
-
-} // namespace cdm
-
-#endif // CDM_CONTENT_DECRYPTION_MODULE_PROXY_H_
diff --git a/chromium/media/cdm/cdm_adapter.cc b/chromium/media/cdm/cdm_adapter.cc
index 5ae3f92709f..caf9974ec57 100644
--- a/chromium/media/cdm/cdm_adapter.cc
+++ b/chromium/media/cdm/cdm_adapter.cc
@@ -413,29 +413,12 @@ std::unique_ptr<CallbackRegistration> CdmAdapter::RegisterEventCB(
Decryptor* CdmAdapter::GetDecryptor() {
DCHECK(task_runner_->BelongsToCurrentThread());
-
- // When using HW secure codecs, we cannot and should not use the CDM instance
- // to do decrypt and/or decode. Instead, we should use the CdmProxy.
- // TODO(xhwang): Fix External Clear Key key system to be able to set
- // |use_hw_secure_codecs| so that we don't have to check both.
- // TODO(xhwang): Update this logic to support transcryption.
- if (cdm_config_.use_hw_secure_codecs || cdm_proxy_created_) {
- DVLOG(2) << __func__ << ": GetDecryptor() returns null";
- return nullptr;
- }
-
return this;
}
int CdmAdapter::GetCdmId() const {
DCHECK(task_runner_->BelongsToCurrentThread());
-#if BUILDFLAG(ENABLE_CDM_PROXY)
- int cdm_id = helper_->GetCdmProxyCdmId();
- DVLOG(2) << __func__ << ": cdm_id = " << cdm_id;
- return cdm_id;
-#else
return CdmContext::kInvalidCdmId;
-#endif // BUILDFLAG(ENABLE_CDM_PROXY)
}
void CdmAdapter::RegisterNewKeyCB(StreamType stream_type,
@@ -1067,27 +1050,6 @@ void CdmAdapter::RequestStorageId(uint32_t version) {
weak_factory_.GetWeakPtr()));
}
-cdm::CdmProxy* CdmAdapter::RequestCdmProxy(cdm::CdmProxyClient* client) {
- DVLOG(3) << __func__;
- DCHECK(task_runner_->BelongsToCurrentThread());
-
-#if BUILDFLAG(ENABLE_CDM_PROXY)
- // CdmProxy should only be created once, at CDM initialization time.
- if (cdm_proxy_created_ ||
- init_promise_id_ == CdmPromiseAdapter::kInvalidPromiseId) {
- DVLOG(1) << __func__
- << ": CdmProxy can only be created once, and must be created "
- "during CDM initialization.";
- return nullptr;
- }
-
- cdm_proxy_created_ = true;
- return helper_->CreateCdmProxy(client);
-#else
- return nullptr;
-#endif // BUILDFLAG(ENABLE_CDM_PROXY)
-}
-
void CdmAdapter::OnStorageIdObtained(uint32_t version,
const std::vector<uint8_t>& storage_id) {
DVLOG(2) << __func__ << ": version = " << version;
diff --git a/chromium/media/cdm/cdm_adapter.h b/chromium/media/cdm/cdm_adapter.h
index b2ecdaae327..2b87592bb62 100644
--- a/chromium/media/cdm/cdm_adapter.h
+++ b/chromium/media/cdm/cdm_adapter.h
@@ -155,9 +155,6 @@ class MEDIA_EXPORT CdmAdapter : public ContentDecryptionModule,
cdm::FileIO* CreateFileIO(cdm::FileIOClient* client) override;
void RequestStorageId(uint32_t version) override;
- // cdm::Host_11 specific implementation.
- cdm::CdmProxy* RequestCdmProxy(cdm::CdmProxyClient* client) override;
-
private:
CdmAdapter(const std::string& key_system,
const url::Origin& security_origin,
@@ -259,8 +256,6 @@ class MEDIA_EXPORT CdmAdapter : public ContentDecryptionModule,
int last_read_file_size_kb_ = 0;
bool file_size_uma_reported_ = false;
- bool cdm_proxy_created_ = false;
-
// Used to keep track of promises while the CDM is processing the request.
CdmPromiseAdapter cdm_promise_adapter_;
diff --git a/chromium/media/cdm/cdm_adapter_unittest.cc b/chromium/media/cdm/cdm_adapter_unittest.cc
index 193ac302e3e..c8d9ad159b1 100644
--- a/chromium/media/cdm/cdm_adapter_unittest.cc
+++ b/chromium/media/cdm/cdm_adapter_unittest.cc
@@ -8,8 +8,8 @@
#include <memory>
#include "base/bind.h"
+#include "base/check.h"
#include "base/command_line.h"
-#include "base/logging.h"
#include "base/run_loop.h"
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
@@ -558,13 +558,4 @@ TEST_P(CdmAdapterTestWithMockCdm, GetDecryptor) {
EXPECT_TRUE(cdm_context->GetDecryptor());
}
-TEST_P(CdmAdapterTestWithMockCdm, GetDecryptor_UseHwSecureCodecs) {
- CdmConfig cdm_config;
- cdm_config.use_hw_secure_codecs = true;
- InitializeWithCdmConfig(cdm_config);
- auto* cdm_context = cdm_->GetCdmContext();
- ASSERT_TRUE(cdm_context);
- EXPECT_FALSE(cdm_context->GetDecryptor());
-}
-
} // namespace media
diff --git a/chromium/media/cdm/cdm_auxiliary_helper.cc b/chromium/media/cdm/cdm_auxiliary_helper.cc
index c87d9a93d08..4d163e112aa 100644
--- a/chromium/media/cdm/cdm_auxiliary_helper.cc
+++ b/chromium/media/cdm/cdm_auxiliary_helper.cc
@@ -18,16 +18,6 @@ cdm::FileIO* CdmAuxiliaryHelper::CreateCdmFileIO(cdm::FileIOClient* client) {
return nullptr;
}
-#if BUILDFLAG(ENABLE_CDM_PROXY)
-cdm::CdmProxy* CdmAuxiliaryHelper::CreateCdmProxy(cdm::CdmProxyClient* client) {
- return nullptr;
-}
-
-int CdmAuxiliaryHelper::GetCdmProxyCdmId() {
- return CdmContext::kInvalidCdmId;
-}
-#endif // BUILDFLAG(ENABLE_CDM_PROXY)
-
cdm::Buffer* CdmAuxiliaryHelper::CreateCdmBuffer(size_t capacity) {
return nullptr;
}
diff --git a/chromium/media/cdm/cdm_auxiliary_helper.h b/chromium/media/cdm/cdm_auxiliary_helper.h
index 2ecf88765f8..b6c829351a1 100644
--- a/chromium/media/cdm/cdm_auxiliary_helper.h
+++ b/chromium/media/cdm/cdm_auxiliary_helper.h
@@ -21,8 +21,6 @@
namespace cdm {
class FileIO;
class FileIOClient;
-class CdmProxy;
-class CdmProxyClient;
} // namespace cdm
namespace media {
@@ -48,18 +46,6 @@ class MEDIA_EXPORT CdmAuxiliaryHelper : public CdmAllocator,
// needed anymore.
virtual cdm::FileIO* CreateCdmFileIO(cdm::FileIOClient* client);
-#if BUILDFLAG(ENABLE_CDM_PROXY)
- // Creates a cdm::CdmProxy object and returns it.
- // The caller does not own the returned object and should not delete it
- // directly. Instead, it should call cdm::CdmProxy::Destroy() after it's not
- // needed anymore.
- virtual cdm::CdmProxy* CreateCdmProxy(cdm::CdmProxyClient* client);
-
- // Returns a CDM ID associated with the last returned CdmProxy. Should only
- // be called after the CdmProxy has been initialized.
- virtual int GetCdmProxyCdmId();
-#endif // BUILDFLAG(ENABLE_CDM_PROXY)
-
// CdmAllocator implementation.
cdm::Buffer* CreateCdmBuffer(size_t capacity) override;
std::unique_ptr<VideoFrameImpl> CreateCdmVideoFrame() override;
diff --git a/chromium/media/cdm/cdm_context_ref_impl.cc b/chromium/media/cdm/cdm_context_ref_impl.cc
index 32df59ade60..7dee07ef0c2 100644
--- a/chromium/media/cdm/cdm_context_ref_impl.cc
+++ b/chromium/media/cdm/cdm_context_ref_impl.cc
@@ -4,7 +4,7 @@
#include "media/cdm/cdm_context_ref_impl.h"
-#include "base/logging.h"
+#include "base/check_op.h"
#include "media/base/content_decryption_module.h"
namespace media {
diff --git a/chromium/media/cdm/cdm_helpers.cc b/chromium/media/cdm/cdm_helpers.cc
index eb98759559a..5a15d074d20 100644
--- a/chromium/media/cdm/cdm_helpers.cc
+++ b/chromium/media/cdm/cdm_helpers.cc
@@ -4,7 +4,7 @@
#include "media/cdm/cdm_helpers.h"
-#include "base/logging.h"
+#include "base/check.h"
#include "ui/gfx/color_space.h"
namespace media {
diff --git a/chromium/media/cdm/cdm_module.cc b/chromium/media/cdm/cdm_module.cc
index 1750e423820..6f451fcfad7 100644
--- a/chromium/media/cdm/cdm_module.cc
+++ b/chromium/media/cdm/cdm_module.cc
@@ -5,6 +5,7 @@
#include "media/cdm/cdm_module.h"
#include "base/files/file_util.h"
+#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
diff --git a/chromium/media/cdm/cdm_module.h b/chromium/media/cdm/cdm_module.h
index 894c18c7ae3..3e8ed8068b7 100644
--- a/chromium/media/cdm/cdm_module.h
+++ b/chromium/media/cdm/cdm_module.h
@@ -31,11 +31,7 @@ class MEDIA_EXPORT CdmModule {
~CdmModule();
- using CreateCdmFunc = void* (*)(int cdm_interface_version,
- const char* key_system,
- uint32_t key_system_size,
- GetCdmHostFunc get_cdm_host_func,
- void* user_data);
+ using CreateCdmFunc = decltype(&::CreateCdmInstance);
CreateCdmFunc GetCreateCdmFunc();
@@ -57,9 +53,9 @@ class MEDIA_EXPORT CdmModule {
bool was_initialize_called() const { return was_initialize_called_; }
private:
- using InitializeCdmModuleFunc = void (*)();
- using DeinitializeCdmModuleFunc = void (*)();
- using GetCdmVersionFunc = char* (*)();
+ using InitializeCdmModuleFunc = decltype(&::INITIALIZE_CDM_MODULE);
+ using DeinitializeCdmModuleFunc = decltype(&::DeinitializeCdmModule);
+ using GetCdmVersionFunc = decltype(&::GetCdmVersion);
CdmModule();
diff --git a/chromium/media/cdm/cdm_proxy.cc b/chromium/media/cdm/cdm_proxy.cc
deleted file mode 100644
index 31063510da3..00000000000
--- a/chromium/media/cdm/cdm_proxy.cc
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2017 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/cdm/cdm_proxy.h"
-
-namespace media {
-
-CdmProxy::Client::Client() = default;
-CdmProxy::Client::~Client() = default;
-CdmProxy::CdmProxy() = default;
-CdmProxy::~CdmProxy() = default;
-
-} // namespace media
diff --git a/chromium/media/cdm/cdm_proxy.h b/chromium/media/cdm/cdm_proxy.h
deleted file mode 100644
index 6d908e09f71..00000000000
--- a/chromium/media/cdm/cdm_proxy.h
+++ /dev/null
@@ -1,160 +0,0 @@
-// Copyright 2017 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_CDM_CDM_PROXY_H_
-#define MEDIA_CDM_CDM_PROXY_H_
-
-#include <stdint.h>
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/callback.h"
-#include "base/memory/weak_ptr.h"
-#include "media/base/cdm_context.h"
-#include "media/base/media_export.h"
-
-namespace base {
-class Token;
-}
-
-namespace media {
-
-// A class that proxies part of ContentDecryptionModule (CDM) functionalities to
-// a different entity, e.g. hardware CDM modules.
-// In general, the interpretation of the method and callback parameters are
-// protocol dependent. For enum parameters, values outside the enum range may
-// not work.
-class MEDIA_EXPORT CdmProxy {
- public:
- // Client of the proxy.
- class MEDIA_EXPORT Client {
- public:
- Client();
- virtual ~Client();
-
- // Called when there is a hardware reset. When hardware reset happens, all
- // the hardware context is lost and all crypto sessions are destroyed. The
- // CdmProxy returns to an uninitialized state and the caller must call
- // Initialize() on the CdmProxy again to be able to continue using it.
- virtual void NotifyHardwareReset() = 0;
- };
-
- enum class Status {
- kOk,
- kFail,
- kMaxValue = kFail,
- };
-
- enum class Protocol {
- // No supported protocol. Used in failure cases.
- kNone,
- // Method using Intel CSME.
- kIntel,
- // There will be more values in the future e.g. kD3D11RsaHardware,
- // kD3D11RsaSoftware to use the D3D11 RSA method.
- kMaxValue = kIntel,
- };
-
- enum class Function {
- // For Intel CSME path to call
- // ID3D11VideoContext::NegotiateCryptoSessionKeyExchange.
- kIntelNegotiateCryptoSessionKeyExchange,
- // There will be more values in the future e.g. for D3D11 RSA method.
- kMaxValue = kIntelNegotiateCryptoSessionKeyExchange,
- };
-
- enum class KeyType {
- kDecryptOnly,
- kDecryptAndDecode,
- kMaxValue = kDecryptAndDecode,
- };
-
- CdmProxy();
- virtual ~CdmProxy();
-
- // Returns a weak pointer of the CdmContext associated with |this|.
- // The weak pointer will be null if |this| is destroyed.
- virtual base::WeakPtr<CdmContext> GetCdmContext() = 0;
-
- // Callback for Initialize(). If the proxy created a crypto session, then the
- // ID for the crypto session is |crypto_session_id|.
- using InitializeCB = base::OnceCallback<
- void(Status status, Protocol protocol, uint32_t crypto_session_id)>;
-
- // Initializes the proxy. The status and the return values of the call is
- // reported to |init_cb|. All other methods should only be called after the
- // proxy is fully initialized. Otherwise they may fail.
- // Note: The proxy also needs to be reinitialized after hardware reset. See
- // Client::NotifyHardwareReset() for details.
- virtual void Initialize(Client* client, InitializeCB init_cb) = 0;
-
- // Callback for Process(). |output_data| is the output of processing.
- using ProcessCB =
- base::OnceCallback<void(Status status,
- const std::vector<uint8_t>& output_data)>;
-
- // Processes and updates the state of the proxy.
- // |expected_output_size| is the size of the output data passed to the
- // callback. Whether this value is required or not is protocol dependent.
- // The status and the return values of the call is reported to |process_cb|.
- virtual void Process(Function function,
- uint32_t crypto_session_id,
- const std::vector<uint8_t>& input_data,
- uint32_t expected_output_data_size,
- ProcessCB process_cb) = 0;
-
- // Callback for CreateMediaCryptoSession().
- // On success:
- // |crypto_session_id| is the ID for the created crypto session.
- // |output_data| is extra value, if any.
- using CreateMediaCryptoSessionCB = base::OnceCallback<
- void(Status status, uint32_t crypto_session_id, uint64_t output_data)>;
-
- // Creates a crypto session for handling media.
- // If extra data has to be passed to further setup the media crypto session,
- // pass the data as |input_data|.
- // The status and the return values of the call is reported to
- // |create_media_crypto_session_cb|.
- virtual void CreateMediaCryptoSession(
- const std::vector<uint8_t>& input_data,
- CreateMediaCryptoSessionCB create_media_crypto_session_cb) = 0;
-
- // Callback for SetKey().
- using SetKeyCB = base::OnceCallback<void(Status status)>;
-
- // Sets a key in the proxy.
- // |crypto_session_id| is the crypto session for decryption.
- // |key_id| is the ID of the key.
- // |key_type| is the type of the key.
- // |key_blob| is the opaque key blob for decrypting or decoding.
- // The status of the call is reported to |set_key_cb|.
- virtual void SetKey(uint32_t crypto_session_id,
- const std::vector<uint8_t>& key_id,
- KeyType key_type,
- const std::vector<uint8_t>& key_blob,
- SetKeyCB set_key_cb) = 0;
-
- // Callback for RemoveKey().
- using RemoveKeyCB = base::OnceCallback<void(Status status)>;
-
- // Removes a key from the proxy.
- // |crypto_session_id| is the crypto session for decryption.
- // |key_id| is the ID of the key.
- // The status of the call is reported to |remove_key_cb|.
- virtual void RemoveKey(uint32_t crypto_session_id,
- const std::vector<uint8_t>& key_id,
- RemoveKeyCB remove_key_cb) = 0;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(CdmProxy);
-};
-
-using CdmProxyFactoryCB = base::RepeatingCallback<std::unique_ptr<CdmProxy>(
- const base::Token& cdm_guid)>;
-
-} // namespace media
-
-#endif // MEDIA_CDM_CDM_PROXY_H_
diff --git a/chromium/media/cdm/cdm_proxy_context.cc b/chromium/media/cdm/cdm_proxy_context.cc
deleted file mode 100644
index 8e5f67adbec..00000000000
--- a/chromium/media/cdm/cdm_proxy_context.cc
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2018 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/cdm/cdm_proxy_context.h"
-
-#include "build/build_config.h"
-
-namespace media {
-
-CdmProxyContext::CdmProxyContext() {}
-CdmProxyContext::~CdmProxyContext() {}
-
-#if defined(OS_WIN)
-base::Optional<CdmProxyContext::D3D11DecryptContext>
-CdmProxyContext::GetD3D11DecryptContext(CdmProxy::KeyType key_type,
- const std::string& key_id) {
- return base::nullopt;
-}
-#endif
-
-} // namespace media \ No newline at end of file
diff --git a/chromium/media/cdm/cdm_proxy_context.h b/chromium/media/cdm/cdm_proxy_context.h
deleted file mode 100644
index a04173d4610..00000000000
--- a/chromium/media/cdm/cdm_proxy_context.h
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright 2018 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_CDM_CDM_PROXY_CONTEXT_H_
-#define MEDIA_CDM_CDM_PROXY_CONTEXT_H_
-
-#include <stdint.h>
-
-#include <string>
-
-#include "base/compiler_specific.h"
-#include "base/macros.h"
-#include "base/optional.h"
-#include "build/build_config.h"
-#include "media/base/media_export.h"
-#include "media/cdm/cdm_proxy.h"
-
-#if defined(OS_WIN)
-#include <d3d11.h>
-#endif
-
-namespace media {
-
-// An interface for accessing various CdmProxy data required for decrypting
-// and/or decoding data.
-class MEDIA_EXPORT CdmProxyContext {
- public:
-#if defined(OS_WIN)
- struct D3D11DecryptContext {
- // Crypto session pointer for decryption.
- // The pointer is owned by the CdmContext implementation.
- ID3D11CryptoSession* crypto_session;
-
- // Opaque key blob for decrypting or decoding.
- // The pointer is owned by the CdmContext implementation.
- const void* key_blob;
-
- // The size of the blob.
- uint32_t key_blob_size;
-
- // GUID identifying the hardware key info.
- GUID key_info_guid;
- };
-
- // Returns D3D11DecryptContext on success. Returns nullopt otherwise. The
- // D3D11DecryptContext instance is only guaranteed to be valid before the
- // caller returns.
- // |key_type| is the requesting key type.
- // |key_id| is the key ID of the media to decrypt.
- virtual base::Optional<D3D11DecryptContext> GetD3D11DecryptContext(
- CdmProxy::KeyType key_type,
- const std::string& key_id) WARN_UNUSED_RESULT;
-#endif // defined(OS_WIN)
-
- // TODO(crbug.com/787657): There will be more methods for this class.
-
- protected:
- CdmProxyContext();
- virtual ~CdmProxyContext();
-
- DISALLOW_COPY_AND_ASSIGN(CdmProxyContext);
-};
-
-} // namespace media
-
-#endif // MEDIA_CDM_CDM_PROXY_CONTEXT_H_
diff --git a/chromium/media/cdm/cenc_utils_unittest.cc b/chromium/media/cdm/cenc_utils_unittest.cc
index d068c11b473..c5cfe9d0273 100644
--- a/chromium/media/cdm/cenc_utils_unittest.cc
+++ b/chromium/media/cdm/cenc_utils_unittest.cc
@@ -9,7 +9,7 @@
#include <limits>
-#include "base/logging.h"
+#include "base/check.h"
#include "base/stl_util.h"
#include "testing/gtest/include/gtest/gtest.h"
diff --git a/chromium/media/cdm/json_web_key.cc b/chromium/media/cdm/json_web_key.cc
index 3b3ba1c9785..d09a4921eb7 100644
--- a/chromium/media/cdm/json_web_key.cc
+++ b/chromium/media/cdm/json_web_key.cc
@@ -172,16 +172,15 @@ bool ExtractKeysFromJWKSet(const std::string& jwk_set,
return false;
}
- std::unique_ptr<base::Value> root(
- base::JSONReader().ReadToValueDeprecated(jwk_set));
- if (!root.get() || root->type() != base::Value::Type::DICTIONARY) {
- DVLOG(1) << "Not valid JSON: " << jwk_set << ", root: " << root.get();
+ base::Optional<base::Value> root = base::JSONReader::Read(jwk_set);
+ if (!root || root->type() != base::Value::Type::DICTIONARY) {
+ DVLOG(1) << "Not valid JSON: " << jwk_set;
return false;
}
// Locate the set from the dictionary.
base::DictionaryValue* dictionary =
- static_cast<base::DictionaryValue*>(root.get());
+ static_cast<base::DictionaryValue*>(&root.value());
base::ListValue* list_val = NULL;
if (!dictionary->GetList(kKeysTag, &list_val)) {
DVLOG(1) << "Missing '" << kKeysTag
@@ -242,9 +241,8 @@ bool ExtractKeyIdsFromKeyIdsInitData(const std::string& input,
return false;
}
- std::unique_ptr<base::Value> root(
- base::JSONReader().ReadToValueDeprecated(input));
- if (!root.get() || root->type() != base::Value::Type::DICTIONARY) {
+ base::Optional<base::Value> root = base::JSONReader::Read(input);
+ if (!root || root->type() != base::Value::Type::DICTIONARY) {
error_message->assign("Not valid JSON: ");
error_message->append(ShortenTo64Characters(input));
return false;
@@ -252,7 +250,7 @@ bool ExtractKeyIdsFromKeyIdsInitData(const std::string& input,
// Locate the set from the dictionary.
base::DictionaryValue* dictionary =
- static_cast<base::DictionaryValue*>(root.get());
+ static_cast<base::DictionaryValue*>(&root.value());
base::ListValue* list_val = NULL;
if (!dictionary->GetList(kKeyIdsTag, &list_val)) {
error_message->assign("Missing '");
@@ -376,16 +374,15 @@ bool ExtractFirstKeyIdFromLicenseRequest(const std::vector<uint8_t>& license,
return false;
}
- std::unique_ptr<base::Value> root(
- base::JSONReader().ReadToValueDeprecated(license_as_str));
- if (!root.get() || root->type() != base::Value::Type::DICTIONARY) {
+ base::Optional<base::Value> root = base::JSONReader::Read(license_as_str);
+ if (!root || root->type() != base::Value::Type::DICTIONARY) {
DVLOG(1) << "Not valid JSON: " << license_as_str;
return false;
}
// Locate the set from the dictionary.
base::DictionaryValue* dictionary =
- static_cast<base::DictionaryValue*>(root.get());
+ static_cast<base::DictionaryValue*>(&root.value());
base::ListValue* list_val = NULL;
if (!dictionary->GetList(kKeyIdsTag, &list_val)) {
DVLOG(1) << "Missing '" << kKeyIdsTag << "' parameter or not a list";
diff --git a/chromium/media/cdm/json_web_key_unittest.cc b/chromium/media/cdm/json_web_key_unittest.cc
index 3c4ed4f28f3..e0c4d348a97 100644
--- a/chromium/media/cdm/json_web_key_unittest.cc
+++ b/chromium/media/cdm/json_web_key_unittest.cc
@@ -8,7 +8,7 @@
#include <stdint.h>
#include "base/base64.h"
-#include "base/logging.h"
+#include "base/check.h"
#include "base/stl_util.h"
#include "media/base/content_decryption_module.h"
#include "testing/gtest/include/gtest/gtest.h"
diff --git a/chromium/media/cdm/library_cdm/cdm_host_proxy.h b/chromium/media/cdm/library_cdm/cdm_host_proxy.h
index ccbda3b6913..ac1ff8d5403 100644
--- a/chromium/media/cdm/library_cdm/cdm_host_proxy.h
+++ b/chromium/media/cdm/library_cdm/cdm_host_proxy.h
@@ -54,7 +54,6 @@ class CdmHostProxy {
virtual void OnDeferredInitializationDone(cdm::StreamType stream_type,
cdm::Status decoder_status) = 0;
virtual cdm::FileIO* CreateFileIO(cdm::FileIOClient* client) = 0;
- virtual cdm::CdmProxy* RequestCdmProxy(cdm::CdmProxyClient* client) = 0;
virtual void RequestStorageId(uint32_t version) = 0;
};
diff --git a/chromium/media/cdm/library_cdm/cdm_host_proxy_impl.h b/chromium/media/cdm/library_cdm/cdm_host_proxy_impl.h
index 3a3bbb0eac5..22ebd602a0a 100644
--- a/chromium/media/cdm/library_cdm/cdm_host_proxy_impl.h
+++ b/chromium/media/cdm/library_cdm/cdm_host_proxy_impl.h
@@ -110,10 +110,6 @@ class CdmHostProxyImpl : public CdmHostProxy {
return host_->CreateFileIO(client);
}
- cdm::CdmProxy* RequestCdmProxy(cdm::CdmProxyClient* client) final {
- return host_->RequestCdmProxy(client);
- }
-
void RequestStorageId(uint32_t version) final {
host_->RequestStorageId(version);
}
@@ -124,15 +120,6 @@ class CdmHostProxyImpl : public CdmHostProxy {
DISALLOW_COPY_AND_ASSIGN(CdmHostProxyImpl);
};
-// Specialization for cdm::Host_10 methods.
-
-template <>
-cdm::CdmProxy* CdmHostProxyImpl<cdm::Host_10>::RequestCdmProxy(
- cdm::CdmProxyClient* /* client */) {
- NOTREACHED() << "cdm::ContentDecryptionModule_10 CDM should never call this.";
- return nullptr;
-}
-
} // namespace media
#endif // MEDIA_CDM_LIBRARY_CDM_CDM_HOST_PROXY_IMPL_H_
diff --git a/chromium/media/cdm/library_cdm/clear_key_cdm/BUILD.gn b/chromium/media/cdm/library_cdm/clear_key_cdm/BUILD.gn
index 8e01c758d09..25b1dfee1ef 100644
--- a/chromium/media/cdm/library_cdm/clear_key_cdm/BUILD.gn
+++ b/chromium/media/cdm/library_cdm/clear_key_cdm/BUILD.gn
@@ -15,8 +15,6 @@ loadable_module("clear_key_cdm") {
"cdm_file_adapter.h",
"cdm_file_io_test.cc",
"cdm_file_io_test.h",
- "cdm_proxy_handler.cc",
- "cdm_proxy_handler.h",
"cdm_video_decoder.cc",
"cdm_video_decoder.h",
"clear_key_cdm.cc",
@@ -31,7 +29,6 @@ loadable_module("clear_key_cdm") {
defines = [ "CDM_IMPLEMENTATION" ]
deps = [
- ":cdm_proxy_common",
"//base",
"//media",
"//media:media_buildflags",
@@ -53,20 +50,3 @@ loadable_module("clear_key_cdm") {
deps += [ "//third_party/ffmpeg" ]
}
}
-
-source_set("clear_key_cdm_proxy") {
- sources = [
- "clear_key_cdm_proxy.cc",
- "clear_key_cdm_proxy.h",
- ]
-
- deps = [
- ":cdm_proxy_common",
- "//base",
- "//media",
- ]
-}
-
-source_set("cdm_proxy_common") {
- sources = [ "cdm_proxy_common.h" ]
-}
diff --git a/chromium/media/cdm/library_cdm/clear_key_cdm/cdm_proxy_common.h b/chromium/media/cdm/library_cdm/clear_key_cdm/cdm_proxy_common.h
deleted file mode 100644
index f0a6a8a2f79..00000000000
--- a/chromium/media/cdm/library_cdm/clear_key_cdm/cdm_proxy_common.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2017 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_CDM_LIBRARY_CDM_CLEAR_KEY_CDM_CDM_PROXY_COMMON_H_
-#define MEDIA_CDM_LIBRARY_CDM_CLEAR_KEY_CDM_CDM_PROXY_COMMON_H_
-
-#include <stdint.h>
-#include <array>
-
-namespace media {
-
-// Constants used to test CdmProxy stack using ClearKeyCdm and ClearKeyCdmProxy.
-constexpr uint32_t kClearKeyCdmProxyCryptoSessionId = 1;
-constexpr uint32_t kClearKeyCdmProxyMediaCryptoSessionId = 23;
-constexpr std::array<uint8_t, 3> kClearKeyCdmProxyInputData = {4, 5, 6};
-constexpr std::array<uint8_t, 4> kClearKeyCdmProxyOutputData = {7, 8, 9, 10};
-
-} // namespace media
-
-#endif // MEDIA_CDM_LIBRARY_CDM_CLEAR_KEY_CDM_CDM_PROXY_COMMON_H_
diff --git a/chromium/media/cdm/library_cdm/clear_key_cdm/cdm_proxy_handler.cc b/chromium/media/cdm/library_cdm/clear_key_cdm/cdm_proxy_handler.cc
deleted file mode 100644
index 115dcdf1de9..00000000000
--- a/chromium/media/cdm/library_cdm/clear_key_cdm/cdm_proxy_handler.cc
+++ /dev/null
@@ -1,119 +0,0 @@
-// Copyright 2017 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/cdm/library_cdm/clear_key_cdm/cdm_proxy_handler.h"
-
-#include <stdint.h>
-#include <algorithm>
-
-#include "base/logging.h"
-#include "base/macros.h"
-#include "media/cdm/library_cdm/cdm_host_proxy.h"
-#include "media/cdm/library_cdm/clear_key_cdm/cdm_proxy_common.h"
-
-namespace media {
-
-CdmProxyHandler::CdmProxyHandler(CdmHostProxy* cdm_host_proxy)
- : cdm_host_proxy_(cdm_host_proxy) {}
-
-CdmProxyHandler::~CdmProxyHandler() {}
-
-void CdmProxyHandler::Initialize(InitCB init_cb) {
- DVLOG(1) << __func__;
- init_cb_ = std::move(init_cb);
-
- cdm_proxy_ = cdm_host_proxy_->RequestCdmProxy(this);
- if (!cdm_proxy_) {
- FinishInitialization(false);
- return;
- }
-
- cdm_proxy_->Initialize();
-}
-
-void CdmProxyHandler::SetKey(const std::vector<uint8_t>& response,
- SetKeyCB set_key_cb) {
- DVLOG(2) << __func__;
- DCHECK(!set_key_cb_);
- set_key_cb_ = std::move(set_key_cb);
- cdm_proxy_->SetKey(crypto_session_id_, nullptr, 0,
- cdm::CdmProxy::kDecryptAndDecode, response.data(),
- response.size());
-}
-
-void CdmProxyHandler::FinishInitialization(bool success) {
- DVLOG(1) << __func__ << ": success = " << success;
- std::move(init_cb_).Run(success);
-}
-
-void CdmProxyHandler::OnInitialized(Status status,
- Protocol protocol,
- uint32_t crypto_session_id) {
- DVLOG(1) << __func__ << ": status = " << status;
-
- if (status != Status::kOk ||
- crypto_session_id != kClearKeyCdmProxyCryptoSessionId) {
- FinishInitialization(false);
- return;
- }
-
- // Only one CdmProxy can be created during the lifetime of the CDM instance.
- if (cdm_host_proxy_->RequestCdmProxy(this)) {
- FinishInitialization(false);
- return;
- }
-
- cdm_proxy_->Process(cdm::CdmProxy::kIntelNegotiateCryptoSessionKeyExchange,
- crypto_session_id, kClearKeyCdmProxyInputData.data(),
- kClearKeyCdmProxyInputData.size(), 0);
-}
-
-void CdmProxyHandler::OnProcessed(Status status,
- const uint8_t* output_data,
- uint32_t output_data_size) {
- DVLOG(2) << __func__ << ": status = " << status;
-
- if (status != Status::kOk ||
- !std::equal(output_data, output_data + output_data_size,
- kClearKeyCdmProxyOutputData.begin())) {
- FinishInitialization(false);
- return;
- }
-
- cdm_proxy_->CreateMediaCryptoSession(kClearKeyCdmProxyInputData.data(),
- kClearKeyCdmProxyInputData.size());
-}
-
-void CdmProxyHandler::OnMediaCryptoSessionCreated(Status status,
- uint32_t crypto_session_id,
- uint64_t output_data) {
- DVLOG(2) << __func__ << ": status = " << status;
-
- if (status != Status::kOk ||
- crypto_session_id != kClearKeyCdmProxyMediaCryptoSessionId) {
- FinishInitialization(false);
- return;
- }
-
- FinishInitialization(true);
-}
-
-void CdmProxyHandler::OnKeySet(Status status) {
- DVLOG(2) << __func__ << ": status = " << status;
- DCHECK(set_key_cb_);
-
- std::move(set_key_cb_).Run(status == Status::kOk);
-}
-
-void CdmProxyHandler::OnKeyRemoved(Status status) {
- DVLOG(2) << __func__;
- NOTREACHED();
-}
-
-void CdmProxyHandler::NotifyHardwareReset() {
- DVLOG(1) << __func__;
- NOTREACHED();
-}
-
-} // namespace media
diff --git a/chromium/media/cdm/library_cdm/clear_key_cdm/cdm_proxy_handler.h b/chromium/media/cdm/library_cdm/clear_key_cdm/cdm_proxy_handler.h
deleted file mode 100644
index 850d905716c..00000000000
--- a/chromium/media/cdm/library_cdm/clear_key_cdm/cdm_proxy_handler.h
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright 2017 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_CDM_LIBRARY_CDM_CLEAR_KEY_CDM_CDM_PROXY_HANDLER_H_
-#define MEDIA_CDM_LIBRARY_CDM_CLEAR_KEY_CDM_CDM_PROXY_HANDLER_H_
-
-#include "base/callback.h"
-#include "base/macros.h"
-#include "media/cdm/api/content_decryption_module.h"
-
-namespace media {
-
-class CdmHostProxy;
-
-class CdmProxyHandler : public cdm::CdmProxyClient {
- public:
- using InitCB = base::OnceCallback<void(bool success)>;
- using SetKeyCB = base::OnceCallback<void(bool success)>;
-
- explicit CdmProxyHandler(CdmHostProxy* cdm_host_proxy);
- ~CdmProxyHandler() override;
-
- // Initializes the CdmProxyHandler and returns the result through |init_cb|.
- // This will request and initialize the CdmProxy, create media crypto session
- // and do some trivial procesing for better test coverage.
- void Initialize(InitCB init_cb);
-
- // Push a response that contains a license to the CdmProxy.
- void SetKey(const std::vector<uint8_t>& response, SetKeyCB set_key_cb);
-
- private:
- void FinishInitialization(bool success);
-
- // cdm::CdmProxyClient implementation.
- void OnInitialized(Status status,
- Protocol protocol,
- uint32_t crypto_session_id) final;
- void OnProcessed(Status status,
- const uint8_t* output_data,
- uint32_t output_data_size) final;
- void OnMediaCryptoSessionCreated(Status status,
- uint32_t crypto_session_id,
- uint64_t output_data) final;
- void OnKeySet(Status status) final;
- void OnKeyRemoved(Status status) final;
- void NotifyHardwareReset() final;
-
- CdmHostProxy* const cdm_host_proxy_ = nullptr;
- InitCB init_cb_;
- SetKeyCB set_key_cb_;
- cdm::CdmProxy* cdm_proxy_ = nullptr;
- uint32_t crypto_session_id_ = 0u;
-
- DISALLOW_COPY_AND_ASSIGN(CdmProxyHandler);
-};
-
-} // namespace media
-
-#endif // MEDIA_CDM_LIBRARY_CDM_CLEAR_KEY_CDM_CDM_PROXY_HANDLER_H_
diff --git a/chromium/media/cdm/library_cdm/clear_key_cdm/clear_key_cdm.cc b/chromium/media/cdm/library_cdm/clear_key_cdm/clear_key_cdm.cc
index 4d3c278b531..5ad5056e830 100644
--- a/chromium/media/cdm/library_cdm/clear_key_cdm/clear_key_cdm.cc
+++ b/chromium/media/cdm/library_cdm/clear_key_cdm/clear_key_cdm.cc
@@ -29,7 +29,6 @@
#include "media/cdm/library_cdm/cdm_host_proxy.h"
#include "media/cdm/library_cdm/cdm_host_proxy_impl.h"
#include "media/cdm/library_cdm/clear_key_cdm/cdm_file_io_test.h"
-#include "media/cdm/library_cdm/clear_key_cdm/cdm_proxy_handler.h"
#include "media/cdm/library_cdm/clear_key_cdm/cdm_video_decoder.h"
#include "media/media_buildflags.h"
@@ -67,8 +66,6 @@ const char kExternalClearKeyStorageIdTestKeySystem[] =
"org.chromium.externalclearkey.storageidtest";
const char kExternalClearKeyDifferentGuidTestKeySystem[] =
"org.chromium.externalclearkey.differentguid";
-const char kExternalClearKeyCdmProxyKeySystem[] =
- "org.chromium.externalclearkey.cdmproxy";
const int64_t kMsPerSecond = 1000;
const int64_t kMaxTimerDelayMs = 5 * kMsPerSecond;
@@ -181,8 +178,7 @@ void* CreateCdmInstance(int cdm_interface_version,
key_system_string != kExternalClearKeyCrashKeySystem &&
key_system_string != kExternalClearKeyVerifyCdmHostTestKeySystem &&
key_system_string != kExternalClearKeyStorageIdTestKeySystem &&
- key_system_string != kExternalClearKeyDifferentGuidTestKeySystem &&
- key_system_string != kExternalClearKeyCdmProxyKeySystem) {
+ key_system_string != kExternalClearKeyDifferentGuidTestKeySystem) {
DVLOG(1) << "Unsupported key system:" << key_system_string;
return nullptr;
}
@@ -349,13 +345,6 @@ void ClearKeyCdm::Initialize(bool allow_distinctive_identifier,
// to check persistent state permission.
allow_persistent_state_ = allow_persistent_state;
- // CdmProxy must be created during initialization time. OnInitialized() will
- // be called in OnCdmProxyHandlerInitialized().
- if (key_system_ == kExternalClearKeyCdmProxyKeySystem) {
- InitializeCdmProxyHandler();
- return;
- }
-
cdm_host_proxy_->OnInitialized(true);
}
@@ -441,54 +430,15 @@ void ClearKeyCdm::UpdateSession(uint32_t promise_id,
DVLOG(1) << __func__;
std::string web_session_str(session_id, session_id_length);
std::vector<uint8_t> response_vector(response, response + response_size);
- auto pending_update_params = std::make_unique<UpdateParams>(
- promise_id, std::move(web_session_str), response_vector);
-
- // Push the license to the CdmProxy. The license will still be pushed to the
- // |cdm_| after OnKeySet() is called, which then triggers
- // OnSessionKeysChange(). This order is critical to avoid race conditions like
- // OnSessionKeysChange() being called before the keys are actually available
- // in the CdmProxy.
- if (cdm_proxy_handler_) {
- if (pending_update_params_) {
- OnPromiseFailed(promise_id, CdmPromise::Exception::INVALID_STATE_ERROR, 0,
- "Parallel updates not supported.");
- return;
- }
-
- pending_update_params_ = std::move(pending_update_params);
- cdm_proxy_handler_->SetKey(
- response_vector,
- base::BindOnce(&ClearKeyCdm::OnCdmProxyKeySet, base::Unretained(this)));
- return;
- }
-
- UpdateSessionInternal(std::move(pending_update_params));
-}
-
-void ClearKeyCdm::OnCdmProxyKeySet(bool success) {
- DCHECK(pending_update_params_);
-
- if (!success) {
- auto promise_id = pending_update_params_->promise_id;
- pending_update_params_.reset();
- OnPromiseFailed(promise_id, CdmPromise::Exception::INVALID_STATE_ERROR, 0,
- "Parallel updates not supported.");
- return;
- }
-
- UpdateSessionInternal(std::move(pending_update_params_));
-}
-void ClearKeyCdm::UpdateSessionInternal(std::unique_ptr<UpdateParams> params) {
std::unique_ptr<media::SimpleCdmPromise> promise(
new media::CdmCallbackPromise<>(
base::BindOnce(&ClearKeyCdm::OnUpdateSuccess, base::Unretained(this),
- params->promise_id, params->session_id),
+ promise_id, web_session_str),
base::BindOnce(&ClearKeyCdm::OnPromiseFailed, base::Unretained(this),
- params->promise_id)));
+ promise_id)));
- cdm_->UpdateSession(params->session_id, params->response, std::move(promise));
+ cdm_->UpdateSession(session_id, response_vector, std::move(promise));
}
void ClearKeyCdm::OnUpdateSuccess(uint32_t promise_id,
@@ -616,10 +566,6 @@ cdm::Status ClearKeyCdm::Decrypt(const cdm::InputBuffer_2& encrypted_buffer,
DVLOG(1) << __func__;
DCHECK(encrypted_buffer.data);
- // When CdmProxy is used, the CDM cannot do any decryption or decoding.
- if (key_system_ == kExternalClearKeyCdmProxyKeySystem)
- return cdm::kDecryptError;
-
scoped_refptr<DecoderBuffer> buffer;
cdm::Status status = DecryptToMediaDecoderBuffer(encrypted_buffer, &buffer);
@@ -639,10 +585,8 @@ cdm::Status ClearKeyCdm::Decrypt(const cdm::InputBuffer_2& encrypted_buffer,
cdm::Status ClearKeyCdm::InitializeAudioDecoder(
const cdm::AudioDecoderConfig_2& audio_decoder_config) {
- if (key_system_ == kExternalClearKeyDecryptOnlyKeySystem ||
- key_system_ == kExternalClearKeyCdmProxyKeySystem) {
+ if (key_system_ == kExternalClearKeyDecryptOnlyKeySystem)
return cdm::kInitializationError;
- }
#if defined(CLEAR_KEY_CDM_USE_FFMPEG_DECODER)
if (!audio_decoder_)
@@ -665,10 +609,8 @@ cdm::Status ClearKeyCdm::InitializeVideoDecoder(
cdm::Status ClearKeyCdm::InitializeVideoDecoder(
const cdm::VideoDecoderConfig_3& video_decoder_config) {
- if (key_system_ == kExternalClearKeyDecryptOnlyKeySystem ||
- key_system_ == kExternalClearKeyCdmProxyKeySystem) {
+ if (key_system_ == kExternalClearKeyDecryptOnlyKeySystem)
return cdm::kInitializationError;
- }
if (!video_decoder_) {
video_decoder_ =
@@ -1029,29 +971,4 @@ void ClearKeyCdm::StartStorageIdTest() {
cdm_host_proxy_->RequestStorageId(0);
}
-void ClearKeyCdm::InitializeCdmProxyHandler() {
- DVLOG(1) << __func__;
- DCHECK(!cdm_proxy_handler_);
-
- cdm_proxy_handler_ = std::make_unique<CdmProxyHandler>(cdm_host_proxy_.get());
- cdm_proxy_handler_->Initialize(base::BindOnce(
- &ClearKeyCdm::OnCdmProxyHandlerInitialized, base::Unretained(this)));
-}
-
-void ClearKeyCdm::OnCdmProxyHandlerInitialized(bool success) {
- DVLOG(1) << __func__;
- DCHECK(cdm_proxy_handler_);
-
- cdm_host_proxy_->OnInitialized(success);
-}
-
-ClearKeyCdm::UpdateParams::UpdateParams(uint32_t promise_id,
- std::string session_id,
- std::vector<uint8_t> response)
- : promise_id(promise_id),
- session_id(std::move(session_id)),
- response(std::move(response)) {}
-
-ClearKeyCdm::UpdateParams::~UpdateParams() {}
-
} // namespace media
diff --git a/chromium/media/cdm/library_cdm/clear_key_cdm/clear_key_cdm.h b/chromium/media/cdm/library_cdm/clear_key_cdm/clear_key_cdm.h
index b33cd54d188..79e86d52656 100644
--- a/chromium/media/cdm/library_cdm/clear_key_cdm/clear_key_cdm.h
+++ b/chromium/media/cdm/library_cdm/clear_key_cdm/clear_key_cdm.h
@@ -23,7 +23,6 @@
namespace media {
class CdmHostProxy;
-class CdmProxyHandler;
class CdmVideoDecoder;
class DecoderBuffer;
class FFmpegCdmAudioDecoder;
@@ -103,17 +102,6 @@ class ClearKeyCdm : public cdm::ContentDecryptionModule_10,
uint32_t storage_id_size) override;
private:
- struct UpdateParams {
- UpdateParams(uint32_t promise_id,
- std::string session_id,
- std::vector<uint8_t> response);
- ~UpdateParams();
-
- const uint32_t promise_id;
- const std::string session_id;
- const std::vector<uint8_t> response;
- };
-
// ContentDecryptionModule callbacks.
void OnSessionMessage(const std::string& session_id,
CdmMessageType message_type,
@@ -163,12 +151,6 @@ class ClearKeyCdm : public cdm::ContentDecryptionModule_10,
void ReportVerifyCdmHostTestResult();
void StartStorageIdTest();
- void InitializeCdmProxyHandler();
- void OnCdmProxyHandlerInitialized(bool success);
- void OnCdmProxyKeySet(bool success);
-
- void UpdateSessionInternal(std::unique_ptr<UpdateParams> params);
-
int host_interface_version_ = 0;
std::unique_ptr<CdmHostProxy> cdm_host_proxy_;
@@ -193,12 +175,9 @@ class ClearKeyCdm : public cdm::ContentDecryptionModule_10,
std::unique_ptr<FFmpegCdmAudioDecoder> audio_decoder_;
#endif // CLEAR_KEY_CDM_USE_FFMPEG_DECODER
- std::unique_ptr<UpdateParams> pending_update_params_;
-
std::unique_ptr<CdmVideoDecoder> video_decoder_;
std::unique_ptr<FileIOTestRunner> file_io_test_runner_;
- std::unique_ptr<CdmProxyHandler> cdm_proxy_handler_;
bool is_running_output_protection_test_ = false;
bool is_running_platform_verification_test_ = false;
diff --git a/chromium/media/cdm/library_cdm/clear_key_cdm/clear_key_cdm_proxy.cc b/chromium/media/cdm/library_cdm/clear_key_cdm/clear_key_cdm_proxy.cc
deleted file mode 100644
index 0b4ac9c4a1c..00000000000
--- a/chromium/media/cdm/library_cdm/clear_key_cdm/clear_key_cdm_proxy.cc
+++ /dev/null
@@ -1,129 +0,0 @@
-// Copyright 2017 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/cdm/library_cdm/clear_key_cdm/clear_key_cdm_proxy.h"
-
-#include "base/bind_helpers.h"
-#include "base/logging.h"
-#include "media/base/content_decryption_module.h"
-#include "media/cdm/library_cdm/clear_key_cdm/cdm_proxy_common.h"
-
-namespace media {
-
-namespace {
-
-constexpr char kDummySessionId[] = "dummy session id";
-
-class IgnoreResponsePromise : public SimpleCdmPromise {
- public:
- IgnoreResponsePromise() = default;
- ~IgnoreResponsePromise() override = default;
-
- // SimpleCdmPromise implementation.
- void resolve() final { MarkPromiseSettled(); }
- void reject(CdmPromise::Exception exception_code,
- uint32_t system_code,
- const std::string& error_message) final {
- MarkPromiseSettled();
- }
-};
-
-} // namespace
-
-ClearKeyCdmProxy::ClearKeyCdmProxy() {}
-
-ClearKeyCdmProxy::~ClearKeyCdmProxy() {}
-
-base::WeakPtr<CdmContext> ClearKeyCdmProxy::GetCdmContext() {
- DVLOG(1) << __func__;
- return weak_factory_.GetWeakPtr();
-}
-
-void ClearKeyCdmProxy::Initialize(Client* client, InitializeCB init_cb) {
- DVLOG(1) << __func__;
-
- std::move(init_cb).Run(Status::kOk, Protocol::kIntel,
- kClearKeyCdmProxyCryptoSessionId);
-}
-
-void ClearKeyCdmProxy::Process(Function function,
- uint32_t crypto_session_id,
- const std::vector<uint8_t>& input_data,
- uint32_t expected_output_data_size,
- ProcessCB process_cb) {
- DVLOG(2) << __func__;
-
- if (crypto_session_id != kClearKeyCdmProxyCryptoSessionId ||
- !std::equal(input_data.begin(), input_data.end(),
- kClearKeyCdmProxyInputData.begin(),
- kClearKeyCdmProxyInputData.end())) {
- std::move(process_cb).Run(Status::kFail, {});
- return;
- }
-
- std::move(process_cb)
- .Run(Status::kOk,
- std::vector<uint8_t>(kClearKeyCdmProxyOutputData.begin(),
- kClearKeyCdmProxyOutputData.end()));
-}
-
-void ClearKeyCdmProxy::CreateMediaCryptoSession(
- const std::vector<uint8_t>& input_data,
- CreateMediaCryptoSessionCB create_media_crypto_session_cb) {
- DVLOG(2) << __func__;
-
- if (!std::equal(input_data.begin(), input_data.end(),
- kClearKeyCdmProxyInputData.begin(),
- kClearKeyCdmProxyInputData.end())) {
- std::move(create_media_crypto_session_cb).Run(Status::kFail, 0, 0);
- return;
- }
-
- std::move(create_media_crypto_session_cb)
- .Run(Status::kOk, kClearKeyCdmProxyMediaCryptoSessionId, 0);
-}
-
-void ClearKeyCdmProxy::SetKey(uint32_t crypto_session_id,
- const std::vector<uint8_t>& key_id,
- KeyType /* key_type */,
- const std::vector<uint8_t>& key_blob,
- SetKeyCB set_key_cb) {
- DVLOG(1) << __func__;
-
- if (!aes_decryptor_)
- CreateDecryptor();
-
- aes_decryptor_->UpdateSession(kDummySessionId, key_blob,
- std::make_unique<IgnoreResponsePromise>());
- std::move(set_key_cb).Run(Status::kOk);
-}
-
-void ClearKeyCdmProxy::RemoveKey(uint32_t crypto_session_id,
- const std::vector<uint8_t>& key_id,
- RemoveKeyCB remove_key_cb) {
- std::move(remove_key_cb).Run(Status::kOk);
-}
-
-Decryptor* ClearKeyCdmProxy::GetDecryptor() {
- DVLOG(1) << __func__;
-
- if (!aes_decryptor_)
- CreateDecryptor();
-
- return aes_decryptor_.get();
-}
-
-void ClearKeyCdmProxy::CreateDecryptor() {
- DVLOG(1) << __func__;
- DCHECK(!aes_decryptor_);
-
- aes_decryptor_ =
- base::MakeRefCounted<AesDecryptor>(base::DoNothing(), base::DoNothing(),
- base::DoNothing(), base::DoNothing());
-
- // Also create a dummy session to be used for SetKey().
- aes_decryptor_->CreateSession(kDummySessionId, CdmSessionType::kTemporary);
-}
-
-} // namespace media
diff --git a/chromium/media/cdm/library_cdm/clear_key_cdm/clear_key_cdm_proxy.h b/chromium/media/cdm/library_cdm/clear_key_cdm/clear_key_cdm_proxy.h
deleted file mode 100644
index bc78f23bd6b..00000000000
--- a/chromium/media/cdm/library_cdm/clear_key_cdm/clear_key_cdm_proxy.h
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright 2017 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_CDM_LIBRARY_CDM_CLEAR_KEY_CDM_CLEAR_KEY_CDM_PROXY_H_
-#define MEDIA_CDM_LIBRARY_CDM_CLEAR_KEY_CDM_CLEAR_KEY_CDM_PROXY_H_
-
-#include "base/callback.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/weak_ptr.h"
-#include "media/base/cdm_context.h"
-#include "media/cdm/aes_decryptor.h"
-#include "media/cdm/cdm_proxy.h"
-
-namespace media {
-
-// CdmProxy implementation for Clear Key CDM to test CDM Proxy support.
-class ClearKeyCdmProxy : public CdmProxy, public CdmContext {
- public:
- ClearKeyCdmProxy();
- ~ClearKeyCdmProxy() final;
-
- // CdmProxy implementation.
- base::WeakPtr<CdmContext> GetCdmContext() final;
- void Initialize(Client* client, InitializeCB init_cb) final;
- void Process(Function function,
- uint32_t crypto_session_id,
- const std::vector<uint8_t>& input_data,
- uint32_t expected_output_data_size,
- ProcessCB process_cb) final;
- void CreateMediaCryptoSession(
- const std::vector<uint8_t>& input_data,
- CreateMediaCryptoSessionCB create_media_crypto_session_cb) final;
- void SetKey(uint32_t crypto_session_id,
- const std::vector<uint8_t>& key_id,
- KeyType key_type,
- const std::vector<uint8_t>& key_blob,
- SetKeyCB set_key_cb) final;
- void RemoveKey(uint32_t crypto_session_id,
- const std::vector<uint8_t>& key_id,
- RemoveKeyCB remove_key_cb) final;
-
- // CdmContext implementation.
- Decryptor* GetDecryptor() final;
-
- private:
- void CreateDecryptor();
-
- scoped_refptr<AesDecryptor> aes_decryptor_;
-
- base::WeakPtrFactory<ClearKeyCdmProxy> weak_factory_{this};
-
- DISALLOW_COPY_AND_ASSIGN(ClearKeyCdmProxy);
-};
-
-} // namespace media
-
-#endif // MEDIA_CDM_LIBRARY_CDM_CLEAR_KEY_CDM_CLEAR_KEY_CDM_PROXY_H_
diff --git a/chromium/media/cdm/simple_cdm_buffer.cc b/chromium/media/cdm/simple_cdm_buffer.cc
index 76d0e1e91b0..ea2a5d357d4 100644
--- a/chromium/media/cdm/simple_cdm_buffer.cc
+++ b/chromium/media/cdm/simple_cdm_buffer.cc
@@ -6,7 +6,7 @@
#include <limits>
-#include "base/logging.h"
+#include "base/check_op.h"
#include "base/numerics/safe_conversions.h"
namespace media {
diff --git a/chromium/media/filters/BUILD.gn b/chromium/media/filters/BUILD.gn
index 9b97365808f..c49ebe92141 100644
--- a/chromium/media/filters/BUILD.gn
+++ b/chromium/media/filters/BUILD.gn
@@ -14,6 +14,7 @@ jumbo_source_set("filters") {
visibility = [
"//media",
"//media/renderers",
+ "//media/webcodecs",
]
sources = [
@@ -187,6 +188,13 @@ jumbo_source_set("filters") {
"ffmpeg_video_decoder.h",
]
}
+
+ if (is_android) {
+ sources += [
+ "android/video_frame_extractor.cc",
+ "android/video_frame_extractor.h",
+ ]
+ }
}
if (is_android) {
@@ -199,8 +207,6 @@ jumbo_source_set("filters") {
sources += [
"android/media_codec_audio_decoder.cc",
"android/media_codec_audio_decoder.h",
- "android/video_frame_extractor.cc",
- "android/video_frame_extractor.h",
]
deps += [ "//media/base/android" ]
}
diff --git a/chromium/media/filters/aom_video_decoder.cc b/chromium/media/filters/aom_video_decoder.cc
index 6d754abcc69..1ce06159a32 100644
--- a/chromium/media/filters/aom_video_decoder.cc
+++ b/chromium/media/filters/aom_video_decoder.cc
@@ -11,6 +11,7 @@
#include "media/base/bind_to_current_loop.h"
#include "media/base/decoder_buffer.h"
#include "media/base/media_log.h"
+#include "media/base/status.h"
#include "media/base/video_util.h"
#include "media/filters/frame_buffer_pool.h"
#include "third_party/libyuv/include/libyuv/convert.h"
@@ -147,7 +148,7 @@ void AomVideoDecoder::Initialize(const VideoDecoderConfig& config,
InitCB bound_init_cb = BindToCurrentLoop(std::move(init_cb));
if (config.is_encrypted() || config.codec() != kCodecAV1) {
- std::move(bound_init_cb).Run(false);
+ std::move(bound_init_cb).Run(StatusCode::kDecoderFailedInitialization);
return;
}
@@ -172,7 +173,7 @@ void AomVideoDecoder::Initialize(const VideoDecoderConfig& config,
0 /* flags */) != AOM_CODEC_OK) {
MEDIA_LOG(ERROR, media_log_) << "aom_codec_dec_init() failed: "
<< aom_codec_error(aom_decoder_.get());
- std::move(bound_init_cb).Run(false);
+ std::move(bound_init_cb).Run(StatusCode::kDecoderFailedInitialization);
return;
}
@@ -184,7 +185,7 @@ void AomVideoDecoder::Initialize(const VideoDecoderConfig& config,
memory_pool_.get()) != AOM_CODEC_OK) {
DLOG(ERROR) << "Failed to configure external buffers. "
<< aom_codec_error(context.get());
- std::move(bound_init_cb).Run(false);
+ std::move(bound_init_cb).Run(StatusCode::kDecoderFailedInitialization);
return;
}
@@ -192,7 +193,7 @@ void AomVideoDecoder::Initialize(const VideoDecoderConfig& config,
state_ = DecoderState::kNormal;
output_cb_ = BindToCurrentLoop(output_cb);
aom_decoder_ = std::move(context);
- std::move(bound_init_cb).Run(true);
+ std::move(bound_init_cb).Run(OkStatus());
}
void AomVideoDecoder::Decode(scoped_refptr<DecoderBuffer> buffer,
diff --git a/chromium/media/filters/aom_video_decoder_unittest.cc b/chromium/media/filters/aom_video_decoder_unittest.cc
index f8e2612e0fd..3de1a5440c0 100644
--- a/chromium/media/filters/aom_video_decoder_unittest.cc
+++ b/chromium/media/filters/aom_video_decoder_unittest.cc
@@ -49,7 +49,12 @@ class AomVideoDecoderTest : public testing::Test {
void InitializeWithConfigWithResult(const VideoDecoderConfig& config,
bool success) {
- decoder_->Initialize(config, false, nullptr, NewExpectedBoolCB(success),
+ decoder_->Initialize(config, false, nullptr,
+ base::BindOnce(
+ [](bool success, Status status) {
+ EXPECT_EQ(status.is_ok(), success);
+ },
+ success),
base::BindRepeating(&AomVideoDecoderTest::FrameReady,
base::Unretained(this)),
base::NullCallback());
diff --git a/chromium/media/filters/audio_clock.cc b/chromium/media/filters/audio_clock.cc
index 67fab3e7b87..017e1c9e2ba 100644
--- a/chromium/media/filters/audio_clock.cc
+++ b/chromium/media/filters/audio_clock.cc
@@ -10,7 +10,7 @@
#include <algorithm>
#include <cmath>
-#include "base/logging.h"
+#include "base/check_op.h"
namespace media {
diff --git a/chromium/media/filters/audio_file_reader_unittest.cc b/chromium/media/filters/audio_file_reader_unittest.cc
index 11b1931a607..edae7b8a577 100644
--- a/chromium/media/filters/audio_file_reader_unittest.cc
+++ b/chromium/media/filters/audio_file_reader_unittest.cc
@@ -7,7 +7,6 @@
#include <memory>
#include "base/hash/md5.h"
-#include "base/logging.h"
#include "base/macros.h"
#include "build/build_config.h"
#include "media/base/audio_bus.h"
diff --git a/chromium/media/filters/chunk_demuxer.cc b/chromium/media/filters/chunk_demuxer.cc
index 4651bb5fdb3..782cfa91af5 100644
--- a/chromium/media/filters/chunk_demuxer.cc
+++ b/chromium/media/filters/chunk_demuxer.cc
@@ -576,6 +576,11 @@ int64_t ChunkDemuxer::GetMemoryUsage() const {
return mem;
}
+base::Optional<container_names::MediaContainerName>
+ChunkDemuxer::GetContainerForMetrics() const {
+ return base::nullopt;
+}
+
void ChunkDemuxer::AbortPendingReads() {
base::AutoLock auto_lock(lock_);
DCHECK(state_ == INITIALIZED || state_ == ENDED || state_ == SHUTDOWN ||
diff --git a/chromium/media/filters/chunk_demuxer.h b/chromium/media/filters/chunk_demuxer.h
index d79bf696f35..dc6dcfa62ba 100644
--- a/chromium/media/filters/chunk_demuxer.h
+++ b/chromium/media/filters/chunk_demuxer.h
@@ -223,6 +223,8 @@ class MEDIA_EXPORT ChunkDemuxer : public Demuxer {
std::vector<DemuxerStream*> GetAllStreams() override;
base::TimeDelta GetStartTime() const override;
int64_t GetMemoryUsage() const override;
+ base::Optional<container_names::MediaContainerName> GetContainerForMetrics()
+ const override;
void AbortPendingReads() override;
// ChunkDemuxer reads are abortable. StartWaitingForSeek() and
diff --git a/chromium/media/filters/decoder_selector.cc b/chromium/media/filters/decoder_selector.cc
index 78d20de6231..bb06378b92d 100644
--- a/chromium/media/filters/decoder_selector.cc
+++ b/chromium/media/filters/decoder_selector.cc
@@ -165,6 +165,14 @@ void DecoderSelector<StreamType>::OnDecoderInitializeDone(Status status) {
DCHECK(task_runner_->BelongsToCurrentThread());
if (!status.is_ok()) {
+ // TODO(tmathmeyer) this might be noisy in media log. Consider batching
+ // all failures as causes to a single Status object and only surfacing it if
+ // decoder selection fails entirely.
+ media_log_->NotifyError(
+ Status(StatusCode::kDecoderFailedInitialization)
+ .WithData("Decoder name", decoder_->GetDisplayName())
+ .AddCause(std::move(status)));
+
// Try the next decoder on the list.
decoder_.reset();
InitializeDecoder();
diff --git a/chromium/media/filters/decoder_selector_unittest.cc b/chromium/media/filters/decoder_selector_unittest.cc
index cd94e12d429..d625e4840c8 100644
--- a/chromium/media/filters/decoder_selector_unittest.cc
+++ b/chromium/media/filters/decoder_selector_unittest.cc
@@ -6,9 +6,10 @@
#include <tuple>
#include "base/bind.h"
-#include "base/logging.h"
+#include "base/check.h"
#include "base/macros.h"
#include "base/memory/scoped_refptr.h"
+#include "base/notreached.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/task_environment.h"
#include "build/build_config.h"
diff --git a/chromium/media/filters/decoder_stream.cc b/chromium/media/filters/decoder_stream.cc
index ae43bb477a4..f5be9e03c14 100644
--- a/chromium/media/filters/decoder_stream.cc
+++ b/chromium/media/filters/decoder_stream.cc
@@ -91,6 +91,9 @@ const char* GetStatusString(
case DecoderStream<StreamType>::DECODE_ERROR:
return "decode_error";
}
+
+ NOTREACHED();
+ return "";
}
template <DemuxerStream::Type StreamType>
diff --git a/chromium/media/filters/ffmpeg_demuxer.cc b/chromium/media/filters/ffmpeg_demuxer.cc
index 550ddf62655..97812376c52 100644
--- a/chromium/media/filters/ffmpeg_demuxer.cc
+++ b/chromium/media/filters/ffmpeg_demuxer.cc
@@ -641,6 +641,15 @@ void FFmpegDemuxerStream::EnqueuePacket(ScopedAVPacket packet) {
if (packet->flags & AV_PKT_FLAG_KEY)
buffer->set_is_key_frame(true);
+ // One last sanity check on the packet timestamps in case any of the above
+ // calculations have pushed the values to the limits.
+ if (buffer->timestamp() == kNoTimestamp ||
+ buffer->timestamp() == kInfiniteDuration) {
+ MEDIA_LOG(ERROR, media_log_) << "FFmpegDemuxer: PTS is not defined";
+ demuxer_->NotifyDemuxerError(DEMUXER_ERROR_COULD_NOT_PARSE);
+ return;
+ }
+
last_packet_timestamp_ = buffer->timestamp();
last_packet_duration_ = buffer->duration();
@@ -1150,6 +1159,11 @@ int64_t FFmpegDemuxer::GetMemoryUsage() const {
return allocation_size;
}
+base::Optional<container_names::MediaContainerName>
+FFmpegDemuxer::GetContainerForMetrics() const {
+ return container();
+}
+
void FFmpegDemuxer::OnEncryptedMediaInitData(
EmeInitDataType init_data_type,
const std::string& encryption_key_id) {
diff --git a/chromium/media/filters/ffmpeg_demuxer.h b/chromium/media/filters/ffmpeg_demuxer.h
index 2578a11f7ef..ab12030c1d0 100644
--- a/chromium/media/filters/ffmpeg_demuxer.h
+++ b/chromium/media/filters/ffmpeg_demuxer.h
@@ -231,6 +231,8 @@ class MEDIA_EXPORT FFmpegDemuxer : public Demuxer {
std::vector<DemuxerStream*> GetAllStreams() override;
base::TimeDelta GetStartTime() const override;
int64_t GetMemoryUsage() const override;
+ base::Optional<container_names::MediaContainerName> GetContainerForMetrics()
+ const override;
// Calls |encrypted_media_init_data_cb_| with the initialization data
// encountered in the file.
diff --git a/chromium/media/filters/ffmpeg_glue.cc b/chromium/media/filters/ffmpeg_glue.cc
index e0ac8452536..53ec21394d7 100644
--- a/chromium/media/filters/ffmpeg_glue.cc
+++ b/chromium/media/filters/ffmpeg_glue.cc
@@ -4,9 +4,10 @@
#include "media/filters/ffmpeg_glue.h"
-#include "base/logging.h"
+#include "base/check_op.h"
#include "base/macros.h"
#include "base/metrics/histogram_functions.h"
+#include "base/notreached.h"
#include "media/base/container_names.h"
#include "media/ffmpeg/ffmpeg_common.h"
diff --git a/chromium/media/filters/ffmpeg_glue_unittest.cc b/chromium/media/filters/ffmpeg_glue_unittest.cc
index 87cff01fea7..0e56adf9bee 100644
--- a/chromium/media/filters/ffmpeg_glue_unittest.cc
+++ b/chromium/media/filters/ffmpeg_glue_unittest.cc
@@ -8,7 +8,7 @@
#include <memory>
-#include "base/logging.h"
+#include "base/check.h"
#include "base/macros.h"
#include "base/test/metrics/histogram_tester.h"
#include "media/base/container_names.h"
diff --git a/chromium/media/filters/file_data_source.cc b/chromium/media/filters/file_data_source.cc
index 443d378781e..4fce5b44158 100644
--- a/chromium/media/filters/file_data_source.cc
+++ b/chromium/media/filters/file_data_source.cc
@@ -7,7 +7,7 @@
#include <algorithm>
#include <utility>
-#include "base/logging.h"
+#include "base/check_op.h"
namespace media {
diff --git a/chromium/media/filters/frame_buffer_pool.cc b/chromium/media/filters/frame_buffer_pool.cc
index a008db80a19..03740545fc0 100644
--- a/chromium/media/filters/frame_buffer_pool.cc
+++ b/chromium/media/filters/frame_buffer_pool.cc
@@ -6,8 +6,8 @@
#include "base/bind.h"
#include "base/callback_helpers.h"
+#include "base/check_op.h"
#include "base/location.h"
-#include "base/logging.h"
#include "base/macros.h"
#include "base/sequenced_task_runner.h"
#include "base/stl_util.h"
diff --git a/chromium/media/filters/fuchsia/fuchsia_video_decoder.cc b/chromium/media/filters/fuchsia/fuchsia_video_decoder.cc
index 8c3647f8f2a..8706bc45b3a 100644
--- a/chromium/media/filters/fuchsia/fuchsia_video_decoder.cc
+++ b/chromium/media/filters/fuchsia/fuchsia_video_decoder.cc
@@ -897,11 +897,18 @@ void FuchsiaVideoDecoder::OnOutputPacket(fuchsia::media::Packet output_packet,
pixel_aspect_ratio = container_pixel_aspect_ratio_;
}
- base::TimeDelta timestamp;
- if (output_packet.has_timestamp_ish()) {
- timestamp = base::TimeDelta::FromNanoseconds(output_packet.timestamp_ish());
+ // SendInputPacket() sets timestamp for all packets sent to the decoder, so we
+ // expect to receive timestamp for all decoded frames. Missing timestamp
+ // indicates a bug in the decoder implementation.
+ if (!output_packet.has_timestamp_ish()) {
+ LOG(ERROR) << "Received frame without timestamp.";
+ OnError();
+ return;
}
+ base::TimeDelta timestamp =
+ base::TimeDelta::FromNanoseconds(output_packet.timestamp_ish());
+
num_used_output_buffers_++;
auto frame = output_mailboxes_[buffer_index]->CreateFrame(
diff --git a/chromium/media/filters/ivf_parser.cc b/chromium/media/filters/ivf_parser.cc
index 8361088b040..4e3a149e8d8 100644
--- a/chromium/media/filters/ivf_parser.cc
+++ b/chromium/media/filters/ivf_parser.cc
@@ -4,6 +4,8 @@
#include "media/filters/ivf_parser.h"
+#include <cstring>
+
#include "base/logging.h"
#include "base/numerics/safe_conversions.h"
#include "base/sys_byteorder.h"
diff --git a/chromium/media/filters/media_file_checker_unittest.cc b/chromium/media/filters/media_file_checker_unittest.cc
index 9ee40bf98cf..38047fd8ed1 100644
--- a/chromium/media/filters/media_file_checker_unittest.cc
+++ b/chromium/media/filters/media_file_checker_unittest.cc
@@ -7,7 +7,6 @@
#include <utility>
#include "base/files/file.h"
-#include "base/logging.h"
#include "build/build_config.h"
#include "media/base/test_data_util.h"
#include "media/media_buildflags.h"
diff --git a/chromium/media/filters/memory_data_source.cc b/chromium/media/filters/memory_data_source.cc
index 9f1c0359b90..99401d6ad1f 100644
--- a/chromium/media/filters/memory_data_source.cc
+++ b/chromium/media/filters/memory_data_source.cc
@@ -6,7 +6,7 @@
#include <algorithm>
-#include "base/logging.h"
+#include "base/check.h"
namespace media {
diff --git a/chromium/media/filters/pipeline_controller.cc b/chromium/media/filters/pipeline_controller.cc
index c8f2036bd01..c2f32d9ec8d 100644
--- a/chromium/media/filters/pipeline_controller.cc
+++ b/chromium/media/filters/pipeline_controller.cc
@@ -96,9 +96,13 @@ void PipelineController::Suspend() {
void PipelineController::Resume() {
DCHECK(thread_checker_.CalledOnValidThread());
pending_suspend_ = false;
- if (state_ == State::SUSPENDING || state_ == State::SUSPENDED) {
+ // TODO(sandersd) fix resume during suspended start.
+ if (state_ == State::SUSPENDING || state_ == State::SUSPENDED ||
+ (state_ == State::SWITCHING_TRACKS &&
+ previous_track_change_state_ == State::SUSPENDED)) {
pending_resume_ = true;
Dispatch();
+ return;
}
}
@@ -279,7 +283,7 @@ void PipelineController::Dispatch() {
// We can only switch tracks if we are not in a transitioning state already.
if ((pending_audio_track_change_ || pending_video_track_change_) &&
(state_ == State::PLAYING || state_ == State::SUSPENDED)) {
- State old_state = state_;
+ previous_track_change_state_ = state_;
state_ = State::SWITCHING_TRACKS;
// Attempt to do a track change _before_ attempting a seek operation,
@@ -290,7 +294,7 @@ void PipelineController::Dispatch() {
pipeline_->OnEnabledAudioTracksChanged(
pending_audio_track_change_ids_,
base::BindOnce(&PipelineController::OnTrackChangeComplete,
- weak_factory_.GetWeakPtr(), old_state));
+ weak_factory_.GetWeakPtr()));
return;
}
@@ -299,7 +303,7 @@ void PipelineController::Dispatch() {
pipeline_->OnSelectedVideoTrackChanged(
pending_video_track_change_id_,
base::BindOnce(&PipelineController::OnTrackChangeComplete,
- weak_factory_.GetWeakPtr(), old_state));
+ weak_factory_.GetWeakPtr()));
return;
}
}
@@ -431,14 +435,15 @@ void PipelineController::OnSelectedVideoTrackChanged(
}
void PipelineController::FireOnTrackChangeCompleteForTesting(State set_to) {
- OnTrackChangeComplete(set_to);
+ previous_track_change_state_ = set_to;
+ OnTrackChangeComplete();
}
-void PipelineController::OnTrackChangeComplete(State previous_state) {
+void PipelineController::OnTrackChangeComplete() {
DCHECK(thread_checker_.CalledOnValidThread());
if (state_ == State::SWITCHING_TRACKS)
- state_ = previous_state;
+ state_ = previous_track_change_state_;
// Other track changed or seek/suspend/resume, etc may be waiting.
Dispatch();
diff --git a/chromium/media/filters/pipeline_controller.h b/chromium/media/filters/pipeline_controller.h
index 81bd337d3da..52bb4757b4d 100644
--- a/chromium/media/filters/pipeline_controller.h
+++ b/chromium/media/filters/pipeline_controller.h
@@ -152,7 +152,7 @@ class MEDIA_EXPORT PipelineController {
// PipelineStaus callback that also carries the target state.
void OnPipelineStatus(State state, PipelineStatus pipeline_status);
- void OnTrackChangeComplete(State previous_state);
+ void OnTrackChangeComplete();
// The Pipeline we are managing state for.
std::unique_ptr<Pipeline> pipeline_;
@@ -188,6 +188,10 @@ class MEDIA_EXPORT PipelineController {
// Tracks the current state of |pipeline_|.
State state_ = State::STOPPED;
+ // The previous state of |pipeline_| if it's currently undergoing a track
+ // change.
+ State previous_track_change_state_ = State::STOPPED;
+
// Indicates that a seek has occurred. When set, a seeked callback will be
// issued at the next stable state.
bool pending_seeked_cb_ = false;
diff --git a/chromium/media/filters/pipeline_controller_unittest.cc b/chromium/media/filters/pipeline_controller_unittest.cc
index bf9a7f99232..bf795f37fe8 100644
--- a/chromium/media/filters/pipeline_controller_unittest.cc
+++ b/chromium/media/filters/pipeline_controller_unittest.cc
@@ -8,9 +8,9 @@
#include "base/bind.h"
#include "base/bind_helpers.h"
-#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
+#include "base/notreached.h"
#include "base/run_loop.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/gmock_move_support.h"
@@ -535,4 +535,17 @@ TEST_F(PipelineControllerTest, SuspendDuringAudioTrackChange) {
EXPECT_FALSE(was_resumed_);
}
+TEST_F(PipelineControllerTest, ResumePlaybackDuringSwitchingTracksState) {
+ Complete(StartPipeline());
+ Complete(SuspendPipeline());
+ EXPECT_CALL(*pipeline_, OnSelectedVideoTrackChanged(_, _)).Times(1);
+ EXPECT_CALL(*pipeline_, GetMediaTime()).Times(1);
+ EXPECT_CALL(*pipeline_, OnResume(_, _)).Times(1);
+
+ pipeline_controller_.OnSelectedVideoTrackChanged({});
+ pipeline_controller_.Resume();
+ pipeline_controller_.FireOnTrackChangeCompleteForTesting(
+ PipelineController::State::SUSPENDED);
+}
+
} // namespace media
diff --git a/chromium/media/filters/video_cadence_estimator.cc b/chromium/media/filters/video_cadence_estimator.cc
index 72f9963a770..8b8c2e8b5b2 100644
--- a/chromium/media/filters/video_cadence_estimator.cc
+++ b/chromium/media/filters/video_cadence_estimator.cc
@@ -11,6 +11,7 @@
#include <numeric>
#include <string>
+#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "media/base/media_switches.h"
diff --git a/chromium/media/filters/vp9_raw_bits_reader.cc b/chromium/media/filters/vp9_raw_bits_reader.cc
index 1fc8f775f63..f29a504cc2d 100644
--- a/chromium/media/filters/vp9_raw_bits_reader.cc
+++ b/chromium/media/filters/vp9_raw_bits_reader.cc
@@ -6,7 +6,7 @@
#include <limits.h>
-#include "base/logging.h"
+#include "base/check_op.h"
#include "media/base/bit_reader.h"
namespace media {
diff --git a/chromium/media/filters/wsola_internals.cc b/chromium/media/filters/wsola_internals.cc
index f750d87c3d2..dd269a67ef3 100644
--- a/chromium/media/filters/wsola_internals.cc
+++ b/chromium/media/filters/wsola_internals.cc
@@ -6,10 +6,11 @@
#include <algorithm>
#include <cmath>
+#include <cstring>
#include <limits>
#include <memory>
-#include "base/logging.h"
+#include "base/check_op.h"
#include "base/numerics/math_constants.h"
#include "build/build_config.h"
#include "media/base/audio_bus.h"
diff --git a/chromium/media/formats/mp2t/descriptors.cc b/chromium/media/formats/mp2t/descriptors.cc
index 7d06dc972d2..075ea59ecce 100644
--- a/chromium/media/formats/mp2t/descriptors.cc
+++ b/chromium/media/formats/mp2t/descriptors.cc
@@ -6,7 +6,7 @@
#include <vector>
-#include "base/logging.h"
+#include "base/check.h"
#include "media/base/bit_reader.h"
#include "media/base/encryption_pattern.h"
#include "media/formats/mp2t/mp2t_common.h"
diff --git a/chromium/media/formats/mp2t/es_adapter_video_unittest.cc b/chromium/media/formats/mp2t/es_adapter_video_unittest.cc
index 8d07d2967cb..f3afd2ac710 100644
--- a/chromium/media/formats/mp2t/es_adapter_video_unittest.cc
+++ b/chromium/media/formats/mp2t/es_adapter_video_unittest.cc
@@ -10,7 +10,6 @@
#include <vector>
#include "base/bind.h"
-#include "base/logging.h"
#include "base/stl_util.h"
#include "base/strings/string_util.h"
#include "base/time/time.h"
diff --git a/chromium/media/formats/mp2t/es_parser_adts_unittest.cc b/chromium/media/formats/mp2t/es_parser_adts_unittest.cc
index 70b80b99a3f..5dbc26428c8 100644
--- a/chromium/media/formats/mp2t/es_parser_adts_unittest.cc
+++ b/chromium/media/formats/mp2t/es_parser_adts_unittest.cc
@@ -5,7 +5,6 @@
#include <vector>
#include "base/bind.h"
-#include "base/logging.h"
#include "base/macros.h"
#include "base/time/time.h"
#include "media/base/stream_parser_buffer.h"
diff --git a/chromium/media/formats/mp2t/es_parser_h264_unittest.cc b/chromium/media/formats/mp2t/es_parser_h264_unittest.cc
index fad0a43f44a..05f57751b88 100644
--- a/chromium/media/formats/mp2t/es_parser_h264_unittest.cc
+++ b/chromium/media/formats/mp2t/es_parser_h264_unittest.cc
@@ -10,7 +10,7 @@
#include <vector>
#include "base/bind.h"
-#include "base/logging.h"
+#include "base/check.h"
#include "base/macros.h"
#include "base/strings/string_util.h"
#include "base/time/time.h"
diff --git a/chromium/media/formats/mp2t/es_parser_mpeg1audio_unittest.cc b/chromium/media/formats/mp2t/es_parser_mpeg1audio_unittest.cc
index f54cc6c6409..46477bf9487 100644
--- a/chromium/media/formats/mp2t/es_parser_mpeg1audio_unittest.cc
+++ b/chromium/media/formats/mp2t/es_parser_mpeg1audio_unittest.cc
@@ -5,7 +5,6 @@
#include <vector>
#include "base/bind.h"
-#include "base/logging.h"
#include "base/macros.h"
#include "base/time/time.h"
#include "media/base/media_util.h"
diff --git a/chromium/media/formats/mp2t/es_parser_test_base.cc b/chromium/media/formats/mp2t/es_parser_test_base.cc
index 3d59a225313..157812f6f33 100644
--- a/chromium/media/formats/mp2t/es_parser_test_base.cc
+++ b/chromium/media/formats/mp2t/es_parser_test_base.cc
@@ -4,8 +4,8 @@
#include "media/formats/mp2t/es_parser_test_base.h"
+#include "base/check_op.h"
#include "base/files/memory_mapped_file.h"
-#include "base/logging.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "media/base/stream_parser_buffer.h"
diff --git a/chromium/media/formats/mp2t/timestamp_unroller.cc b/chromium/media/formats/mp2t/timestamp_unroller.cc
index 4c39c007ecd..31a994dabbe 100644
--- a/chromium/media/formats/mp2t/timestamp_unroller.cc
+++ b/chromium/media/formats/mp2t/timestamp_unroller.cc
@@ -4,7 +4,7 @@
#include "media/formats/mp2t/timestamp_unroller.h"
-#include "base/logging.h"
+#include "base/check_op.h"
namespace media {
namespace mp2t {
diff --git a/chromium/media/formats/mp2t/timestamp_unroller_unittest.cc b/chromium/media/formats/mp2t/timestamp_unroller_unittest.cc
index 8fd0e3fd12e..7c355831a94 100644
--- a/chromium/media/formats/mp2t/timestamp_unroller_unittest.cc
+++ b/chromium/media/formats/mp2t/timestamp_unroller_unittest.cc
@@ -6,7 +6,6 @@
#include <stdint.h>
#include <vector>
-#include "base/logging.h"
#include "base/stl_util.h"
#include "base/test/perf_test_suite.h"
#include "media/formats/mp2t/timestamp_unroller.h"
diff --git a/chromium/media/formats/mp2t/ts_section_cets_ecm.cc b/chromium/media/formats/mp2t/ts_section_cets_ecm.cc
index a50dec10152..d458c06fb5c 100644
--- a/chromium/media/formats/mp2t/ts_section_cets_ecm.cc
+++ b/chromium/media/formats/mp2t/ts_section_cets_ecm.cc
@@ -4,7 +4,7 @@
#include "media/formats/mp2t/ts_section_cets_ecm.h"
-#include "base/logging.h"
+#include "base/check.h"
#include "media/base/bit_reader.h"
#include "media/formats/mp2t/mp2t_common.h"
diff --git a/chromium/media/formats/mp2t/ts_section_cets_pssh.cc b/chromium/media/formats/mp2t/ts_section_cets_pssh.cc
index 89f1d3aa537..976d4b53898 100644
--- a/chromium/media/formats/mp2t/ts_section_cets_pssh.cc
+++ b/chromium/media/formats/mp2t/ts_section_cets_pssh.cc
@@ -4,7 +4,7 @@
#include "media/formats/mp2t/ts_section_cets_pssh.h"
-#include "base/logging.h"
+#include "base/check.h"
#include "media/base/bit_reader.h"
#include "media/formats/mp2t/mp2t_common.h"
diff --git a/chromium/media/formats/mp2t/ts_section_pmt.cc b/chromium/media/formats/mp2t/ts_section_pmt.cc
index ed6be68b92a..4e8cbf0171e 100644
--- a/chromium/media/formats/mp2t/ts_section_pmt.cc
+++ b/chromium/media/formats/mp2t/ts_section_pmt.cc
@@ -6,7 +6,7 @@
#include <map>
-#include "base/logging.h"
+#include "base/check.h"
#include "media/base/bit_reader.h"
#include "media/formats/mp2t/mp2t_common.h"
diff --git a/chromium/media/formats/mp4/mp4_box_reader_fuzzer.cc b/chromium/media/formats/mp4/mp4_box_reader_fuzzer.cc
index 30ae9eba7cd..25a5e4eeb84 100644
--- a/chromium/media/formats/mp4/mp4_box_reader_fuzzer.cc
+++ b/chromium/media/formats/mp4/mp4_box_reader_fuzzer.cc
@@ -7,7 +7,7 @@
#include <memory>
-#include "base/logging.h"
+#include "base/check.h"
#include "media/base/media_util.h"
#include "media/formats/mp4/box_reader.h"
diff --git a/chromium/media/formats/mp4/nalu_test_helper.cc b/chromium/media/formats/mp4/nalu_test_helper.cc
index abdbc6142b1..acb81028e42 100644
--- a/chromium/media/formats/mp4/nalu_test_helper.cc
+++ b/chromium/media/formats/mp4/nalu_test_helper.cc
@@ -4,7 +4,7 @@
#include "media/formats/mp4/nalu_test_helper.h"
-#include "base/logging.h"
+#include "base/check_op.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "media/video/h264_parser.h"
diff --git a/chromium/media/formats/mp4/sample_to_group_iterator.cc b/chromium/media/formats/mp4/sample_to_group_iterator.cc
index f5ce394aae2..b59916f42da 100644
--- a/chromium/media/formats/mp4/sample_to_group_iterator.cc
+++ b/chromium/media/formats/mp4/sample_to_group_iterator.cc
@@ -4,7 +4,7 @@
#include "media/formats/mp4/sample_to_group_iterator.h"
-#include "base/logging.h"
+#include "base/check.h"
namespace media {
namespace mp4 {
diff --git a/chromium/media/formats/webm/cluster_builder.cc b/chromium/media/formats/webm/cluster_builder.cc
index b685c2350a8..94c85d35107 100644
--- a/chromium/media/formats/webm/cluster_builder.cc
+++ b/chromium/media/formats/webm/cluster_builder.cc
@@ -7,7 +7,7 @@
#include <memory>
#include <utility>
-#include "base/logging.h"
+#include "base/check_op.h"
#include "media/base/data_buffer.h"
#include "media/formats/webm/webm_constants.h"
diff --git a/chromium/media/formats/webm/opus_packet_builder.cc b/chromium/media/formats/webm/opus_packet_builder.cc
index c629bfbb183..9f71100df5d 100644
--- a/chromium/media/formats/webm/opus_packet_builder.cc
+++ b/chromium/media/formats/webm/opus_packet_builder.cc
@@ -4,7 +4,7 @@
#include "media/formats/webm/opus_packet_builder.h"
-#include "base/logging.h"
+#include "base/check_op.h"
#include "media/formats/webm/webm_cluster_parser.h"
namespace media {
diff --git a/chromium/media/formats/webm/tracks_builder.cc b/chromium/media/formats/webm/tracks_builder.cc
index 60c0abb7721..7ba64607f1f 100644
--- a/chromium/media/formats/webm/tracks_builder.cc
+++ b/chromium/media/formats/webm/tracks_builder.cc
@@ -4,7 +4,9 @@
#include "media/formats/webm/tracks_builder.h"
-#include "base/logging.h"
+#include <cstring>
+
+#include "base/check_op.h"
#include "media/formats/webm/webm_constants.h"
namespace media {
diff --git a/chromium/media/formats/webm/webm_content_encodings.cc b/chromium/media/formats/webm/webm_content_encodings.cc
index 9a03ea153ea..7c9aacaeeac 100644
--- a/chromium/media/formats/webm/webm_content_encodings.cc
+++ b/chromium/media/formats/webm/webm_content_encodings.cc
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "base/logging.h"
#include "media/formats/webm/webm_content_encodings.h"
+#include "base/check_op.h"
namespace media {
diff --git a/chromium/media/formats/webm/webm_parser.cc b/chromium/media/formats/webm/webm_parser.cc
index 39fc6c5c49f..f59bec22748 100644
--- a/chromium/media/formats/webm/webm_parser.cc
+++ b/chromium/media/formats/webm/webm_parser.cc
@@ -13,6 +13,7 @@
#include <stddef.h>
+#include <cstring>
#include <iomanip>
#include <limits>
diff --git a/chromium/media/formats/webm/webm_stream_parser.cc b/chromium/media/formats/webm/webm_stream_parser.cc
index 9e3a0c90489..4a6799bd373 100644
--- a/chromium/media/formats/webm/webm_stream_parser.cc
+++ b/chromium/media/formats/webm/webm_stream_parser.cc
@@ -157,7 +157,6 @@ int WebMStreamParser::ParseInfoAndTracks(const uint8_t* data, int size) {
}
// Skip the element.
return result + element_size;
- break;
case kWebMIdCluster:
if (!cluster_parser_) {
MEDIA_LOG(ERROR, media_log_) << "Found Cluster element before Info.";
@@ -166,14 +165,12 @@ int WebMStreamParser::ParseInfoAndTracks(const uint8_t* data, int size) {
ChangeState(kParsingClusters);
new_segment_cb_.Run();
return 0;
- break;
case kWebMIdSegment:
// Segment of unknown size indicates live stream.
if (element_size == kWebMUnknownSize)
unknown_segment_size_ = true;
// Just consume the segment header.
return result;
- break;
case kWebMIdInfo:
// We've found the element we are looking for.
break;
diff --git a/chromium/media/fuchsia/audio/BUILD.gn b/chromium/media/fuchsia/audio/BUILD.gn
index 039276f85ff..23c6d3db808 100644
--- a/chromium/media/fuchsia/audio/BUILD.gn
+++ b/chromium/media/fuchsia/audio/BUILD.gn
@@ -21,6 +21,20 @@ source_set("audio") {
]
}
+source_set("test_support") {
+ testonly = true
+ public_deps = [
+ "//base",
+ "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.media",
+ "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.media.audio",
+ "//third_party/fuchsia-sdk/sdk/pkg/fidl_cpp",
+ ]
+ sources = [
+ "fake_audio_consumer.cc",
+ "fake_audio_consumer.h",
+ ]
+}
+
source_set("unittests") {
testonly = true
diff --git a/chromium/media/fuchsia/audio/fake_audio_consumer.cc b/chromium/media/fuchsia/audio/fake_audio_consumer.cc
new file mode 100644
index 00000000000..f8368980332
--- /dev/null
+++ b/chromium/media/fuchsia/audio/fake_audio_consumer.cc
@@ -0,0 +1,268 @@
+// 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/fuchsia/audio/fake_audio_consumer.h"
+
+#include <lib/vfs/cpp/pseudo_dir.h>
+#include <lib/vfs/cpp/service.h>
+
+#include "base/fuchsia/fuchsia_logging.h"
+
+namespace media {
+
+namespace {
+
+// Lead time range returned from WatchStatus();
+constexpr base::TimeDelta kMinLeadTime = base::TimeDelta::FromMilliseconds(100);
+constexpr base::TimeDelta kMaxLeadTime = base::TimeDelta::FromMilliseconds(500);
+
+} // namespace
+
+// Buffering delay.
+constexpr base::TimeDelta kBufferDelay = base::TimeDelta::FromMilliseconds(30);
+
+FakeAudioConsumer::FakeAudioConsumer(
+ uint64_t session_id,
+ fidl::InterfaceRequest<fuchsia::media::AudioConsumer> request)
+ : session_id_(session_id),
+ audio_consumer_binding_(this),
+ stream_sink_binding_(this),
+ volume_control_binding_(this) {
+ audio_consumer_binding_.Bind(std::move(request));
+}
+
+FakeAudioConsumer::~FakeAudioConsumer() = default;
+
+base::TimeDelta FakeAudioConsumer::GetMediaPosition() {
+ base::TimeDelta result = media_pos_;
+ if (state_ == State::kPlaying) {
+ result += (base::TimeTicks::Now() - reference_time_) * media_delta_ /
+ reference_delta_;
+ }
+ return result;
+}
+
+void FakeAudioConsumer::CreateStreamSink(
+ std::vector<zx::vmo> buffers,
+ fuchsia::media::AudioStreamType stream_type,
+ std::unique_ptr<fuchsia::media::Compression> compression,
+ fidl::InterfaceRequest<fuchsia::media::StreamSink> stream_sink_request) {
+ num_buffers_ = buffers.size();
+ CHECK_GT(num_buffers_, 0U);
+ stream_sink_binding_.Bind(std::move(stream_sink_request));
+}
+
+void FakeAudioConsumer::Start(fuchsia::media::AudioConsumerStartFlags flags,
+ int64_t reference_time,
+ int64_t media_time) {
+ CHECK(state_ == State::kStopped);
+
+ if (reference_time != fuchsia::media::NO_TIMESTAMP) {
+ reference_time_ = base::TimeTicks::FromZxTime(reference_time);
+ } else {
+ reference_time_ = base::TimeTicks::Now() + kBufferDelay;
+ }
+
+ if (media_time != fuchsia::media::NO_TIMESTAMP) {
+ media_pos_ = base::TimeDelta::FromZxDuration(media_time);
+ } else {
+ if (media_pos_.is_min()) {
+ media_pos_ = base::TimeDelta();
+ }
+ }
+
+ state_ = State::kPlaying;
+
+ OnStatusUpdate();
+ ScheduleNextStreamPosUpdate();
+}
+
+void FakeAudioConsumer::Stop() {
+ CHECK(state_ != State::kPlaying);
+
+ state_ = State::kStopped;
+ OnStatusUpdate();
+}
+
+void FakeAudioConsumer::WatchStatus(WatchStatusCallback callback) {
+ status_callback_ = std::move(callback);
+ if (have_status_update_) {
+ CallStatusCallback();
+ }
+}
+
+void FakeAudioConsumer::SetRate(float rate) {
+ // Playback rate must not be negative.
+ CHECK_GE(rate, 0.0);
+
+ // Update reference position.
+ auto now = base::TimeTicks::Now();
+ media_pos_ =
+ media_pos_ + (now - reference_time_) * media_delta_ / reference_delta_;
+ reference_time_ = now;
+
+ // Approximate the rate as n/1000;
+ reference_delta_ = 1000;
+ media_delta_ = static_cast<int>(rate * 1000.0);
+
+ OnStatusUpdate();
+
+ if (update_timer_.IsRunning())
+ update_timer_.Reset();
+ ScheduleNextStreamPosUpdate();
+}
+
+void FakeAudioConsumer::BindVolumeControl(
+ fidl::InterfaceRequest<fuchsia::media::audio::VolumeControl>
+ volume_control_request) {
+ volume_control_binding_.Bind(std::move(volume_control_request));
+}
+
+void FakeAudioConsumer::SendPacket(fuchsia::media::StreamPacket stream_packet,
+ SendPacketCallback callback) {
+ CHECK_LT(stream_packet.payload_buffer_id, num_buffers_);
+
+ Packet packet;
+ if (stream_packet.pts == fuchsia::media::NO_TIMESTAMP) {
+ if (media_pos_.is_min()) {
+ packet.pts = base::TimeDelta();
+ } else {
+ packet.pts = media_pos_;
+ }
+ } else {
+ packet.pts = base::TimeDelta::FromZxDuration(stream_packet.pts);
+ }
+ pending_packets_.push_back(std::move(packet));
+
+ callback();
+
+ ScheduleNextStreamPosUpdate();
+}
+
+void FakeAudioConsumer::SendPacketNoReply(fuchsia::media::StreamPacket packet) {
+ NOTREACHED();
+}
+
+void FakeAudioConsumer::EndOfStream() {
+ Packet packet;
+ packet.is_eos = true;
+ pending_packets_.push_back(std::move(packet));
+}
+
+void FakeAudioConsumer::DiscardAllPackets(DiscardAllPacketsCallback callback) {
+ DiscardAllPacketsNoReply();
+ std::move(callback)();
+}
+
+void FakeAudioConsumer::DiscardAllPacketsNoReply() {
+ pending_packets_.clear();
+}
+
+void FakeAudioConsumer::SetVolume(float volume) {
+ volume_ = volume;
+}
+
+void FakeAudioConsumer::SetMute(bool mute) {
+ is_muted_ = mute;
+}
+
+void FakeAudioConsumer::NotImplemented_(const std::string& name) {
+ LOG(FATAL) << "Reached non-implemented " << name;
+}
+
+void FakeAudioConsumer::ScheduleNextStreamPosUpdate() {
+ if (pending_packets_.empty() || update_timer_.IsRunning() ||
+ media_delta_ == 0 || state_ != State::kPlaying) {
+ return;
+ }
+ base::TimeDelta delay;
+ if (!pending_packets_.front().is_eos) {
+ auto next_packet_time =
+ reference_time_ + (pending_packets_.front().pts - media_pos_) *
+ reference_delta_ / media_delta_;
+ delay = (next_packet_time - base::TimeTicks::Now());
+ }
+ update_timer_.Start(FROM_HERE, delay,
+ base::BindOnce(&FakeAudioConsumer::UpdateStreamPos,
+ base::Unretained(this)));
+}
+
+void FakeAudioConsumer::UpdateStreamPos() {
+ if (state_ != State::kPlaying)
+ return;
+
+ auto now = base::TimeTicks::Now();
+ auto new_media_pos =
+ media_pos_ + (now - reference_time_) * media_delta_ / reference_delta_;
+
+ // Drop all packets with PTS before the current position.
+ while (!pending_packets_.empty()) {
+ if (!pending_packets_.front().is_eos &&
+ pending_packets_.front().pts > new_media_pos) {
+ break;
+ }
+
+ Packet packet = pending_packets_.front();
+ pending_packets_.pop_front();
+
+ if (packet.is_eos) {
+ // No data should be submitted after EOS.
+ CHECK(pending_packets_.empty());
+ audio_consumer_binding_.events().OnEndOfStream();
+ state_ = State::kEndOfStream;
+ media_pos_ = new_media_pos;
+ reference_time_ = now;
+ }
+ }
+
+ ScheduleNextStreamPosUpdate();
+}
+
+void FakeAudioConsumer::OnStatusUpdate() {
+ have_status_update_ = true;
+ if (status_callback_) {
+ CallStatusCallback();
+ }
+}
+
+void FakeAudioConsumer::CallStatusCallback() {
+ DCHECK(status_callback_);
+ DCHECK(have_status_update_);
+
+ fuchsia::media::AudioConsumerStatus status;
+ if (state_ == State::kPlaying) {
+ fuchsia::media::TimelineFunction timeline;
+ timeline.reference_time = reference_time_.ToZxTime();
+ timeline.subject_time = media_pos_.ToZxDuration();
+ timeline.reference_delta = reference_delta_;
+ timeline.subject_delta = media_delta_;
+
+ status.set_presentation_timeline(std::move(timeline));
+ }
+
+ status.set_min_lead_time(kMinLeadTime.ToZxDuration());
+ status.set_max_lead_time(kMaxLeadTime.ToZxDuration());
+
+ have_status_update_ = false;
+ std::move(status_callback_)(std::move(status));
+ status_callback_ = {};
+}
+
+FakeAudioConsumerService::FakeAudioConsumerService(vfs::PseudoDir* pseudo_dir)
+ : binding_(pseudo_dir, this) {}
+
+FakeAudioConsumerService::~FakeAudioConsumerService() {}
+
+void FakeAudioConsumerService::CreateAudioConsumer(
+ uint64_t session_id,
+ fidl::InterfaceRequest<fuchsia::media::AudioConsumer> request) {
+ audio_consumers_.push_back(
+ std::make_unique<FakeAudioConsumer>(session_id, std::move(request)));
+}
+
+void FakeAudioConsumerService::NotImplemented_(const std::string& name) {
+ LOG(FATAL) << "Reached non-implemented " << name;
+}
+
+} // namespace media \ No newline at end of file
diff --git a/chromium/media/fuchsia/audio/fake_audio_consumer.h b/chromium/media/fuchsia/audio/fake_audio_consumer.h
new file mode 100644
index 00000000000..56615269cbd
--- /dev/null
+++ b/chromium/media/fuchsia/audio/fake_audio_consumer.h
@@ -0,0 +1,164 @@
+// 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_FUCHSIA_AUDIO_FAKE_AUDIO_CONSUMER_H_
+#define MEDIA_FUCHSIA_AUDIO_FAKE_AUDIO_CONSUMER_H_
+
+#include <fuchsia/media/audio/cpp/fidl.h>
+#include <fuchsia/media/audio/cpp/fidl_test_base.h>
+#include <fuchsia/media/cpp/fidl.h>
+#include <fuchsia/media/cpp/fidl_test_base.h>
+#include <lib/fidl/cpp/binding.h>
+
+#include <vector>
+
+#include "base/fuchsia/scoped_service_binding.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+
+namespace vfs {
+class PseudoDir;
+} // namespace vfs
+
+namespace media {
+
+// Fake implementation of fuchsia::media::AudioConsumer interface. Used for
+// tests.
+class FakeAudioConsumer
+ : public fuchsia::media::testing::AudioConsumer_TestBase,
+ public fuchsia::media::testing::StreamSink_TestBase,
+ public fuchsia::media::audio::testing::VolumeControl_TestBase {
+ public:
+ FakeAudioConsumer(
+ uint64_t session_id,
+ fidl::InterfaceRequest<fuchsia::media::AudioConsumer> request);
+ ~FakeAudioConsumer() final;
+
+ FakeAudioConsumer(const FakeAudioConsumer&) = delete;
+ FakeAudioConsumer& operator=(const FakeAudioConsumer&) = delete;
+
+ uint64_t session_id() { return session_id_; }
+ float volume() const { return volume_; }
+ bool is_muted() const { return is_muted_; }
+
+ base::TimeDelta GetMediaPosition();
+
+ private:
+ enum class State {
+ kStopped,
+ kPlaying,
+ kEndOfStream,
+ };
+
+ struct Packet {
+ base::TimeDelta pts;
+ bool is_eos = false;
+ };
+
+ // fuchsia::media::AudioConsumer interface;
+ void CreateStreamSink(
+ std::vector<zx::vmo> buffers,
+ fuchsia::media::AudioStreamType stream_type,
+ std::unique_ptr<fuchsia::media::Compression> compression,
+ fidl::InterfaceRequest<fuchsia::media::StreamSink> stream_sink_request)
+ final;
+ void Start(fuchsia::media::AudioConsumerStartFlags flags,
+ int64_t reference_time,
+ int64_t media_time) final;
+ void Stop() final;
+ void WatchStatus(WatchStatusCallback callback) final;
+ void SetRate(float rate) final;
+ void BindVolumeControl(
+ fidl::InterfaceRequest<fuchsia::media::audio::VolumeControl>
+ volume_control_request) final;
+
+ // fuchsia::media::StreamSink interface.
+ void SendPacket(fuchsia::media::StreamPacket packet,
+ SendPacketCallback callback) final;
+ void SendPacketNoReply(fuchsia::media::StreamPacket packet) final;
+ void EndOfStream() final;
+ void DiscardAllPackets(DiscardAllPacketsCallback callback) final;
+ void DiscardAllPacketsNoReply() final;
+
+ // fuchsia::media::audio::VolumeControl interface.
+ void SetVolume(float volume) final;
+ void SetMute(bool mute) final;
+
+ // Not-implemented handler for _TestBase parents.
+ void NotImplemented_(const std::string& name) final;
+
+ void ScheduleNextStreamPosUpdate();
+
+ // Updates stream position and drops old packets from the stream.
+ void UpdateStreamPos();
+
+ void OnStatusUpdate();
+ void CallStatusCallback();
+
+ const uint64_t session_id_;
+
+ fidl::Binding<fuchsia::media::AudioConsumer> audio_consumer_binding_;
+ fidl::Binding<fuchsia::media::StreamSink> stream_sink_binding_;
+ fidl::Binding<fuchsia::media::audio::VolumeControl> volume_control_binding_;
+
+ size_t num_buffers_ = 0;
+
+ State state_ = State::kStopped;
+
+ bool have_status_update_ = true;
+ WatchStatusCallback status_callback_;
+
+ base::TimeTicks reference_time_;
+
+ // Numerator and denumerator for current playback rate.
+ uint32_t media_delta_ = 1;
+ uint32_t reference_delta_ = 1;
+
+ // Last known media position. Min value indicates that the stream position
+ // hasn't been set. If stream is playing then value corresponds to
+ // |reference_time_|.
+ base::TimeDelta media_pos_ = base::TimeDelta::Min();
+
+ std::list<Packet> pending_packets_;
+
+ // Timer to call UpdateStreamPos() for the next packet.
+ base::OneShotTimer update_timer_;
+
+ float volume_ = 1.0;
+ bool is_muted_ = false;
+};
+
+class FakeAudioConsumerService
+ : public fuchsia::media::testing::SessionAudioConsumerFactory_TestBase {
+ public:
+ explicit FakeAudioConsumerService(vfs::PseudoDir* pseudo_dir);
+ ~FakeAudioConsumerService() final;
+
+ FakeAudioConsumerService(const FakeAudioConsumerService&) = delete;
+ FakeAudioConsumerService& operator=(const FakeAudioConsumerService&) = delete;
+
+ size_t num_instances() { return audio_consumers_.size(); }
+ FakeAudioConsumer* instance(size_t index) {
+ return audio_consumers_[index].get();
+ }
+
+ private:
+ // fuchsia::media::SessionAudioConsumerFactory implementation.
+ void CreateAudioConsumer(uint64_t session_id,
+ fidl::InterfaceRequest<fuchsia::media::AudioConsumer>
+ audio_consumer_request) final;
+
+ // Not-implemented handler for SessionAudioConsumerFactory_TestBase.
+ void NotImplemented_(const std::string& name) final;
+
+ base::fuchsia::ScopedServiceBinding<
+ fuchsia::media::SessionAudioConsumerFactory>
+ binding_;
+
+ std::vector<std::unique_ptr<FakeAudioConsumer>> audio_consumers_;
+};
+
+} // namespace media
+
+#endif // MEDIA_FUCHSIA_AUDIO_FAKE_AUDIO_CONSUMER_H_
diff --git a/chromium/media/fuchsia/audio/fuchsia_audio_renderer.cc b/chromium/media/fuchsia/audio/fuchsia_audio_renderer.cc
index 9c1c262e547..b4f6476516c 100644
--- a/chromium/media/fuchsia/audio/fuchsia_audio_renderer.cc
+++ b/chromium/media/fuchsia/audio/fuchsia_audio_renderer.cc
@@ -254,7 +254,6 @@ void FuchsiaAudioRenderer::StartTicking() {
SetPlaybackState(PlaybackState::kStarting);
}
-
audio_consumer_->Start(flags, fuchsia::media::NO_TIMESTAMP,
media_pos.ToZxDuration());
}
@@ -528,6 +527,13 @@ void FuchsiaAudioRenderer::OnDemuxerStreamReadDone(
OnStreamSendDone(buffer_index);
});
+ // AudioConsumer doesn't report exact time when the data is decoded, but it's
+ // safe to report it as decoded right away since the packet is expected to be
+ // decoded soon after AudioConsumer receives it.
+ PipelineStatistics stats;
+ stats.audio_bytes_decoded = buffer->data_size();
+ client_->OnStatisticsUpdate(stats);
+
last_packet_timestamp_ = buffer->timestamp();
ScheduleReadDemuxerStream();
diff --git a/chromium/media/fuchsia/camera/BUILD.gn b/chromium/media/fuchsia/camera/BUILD.gn
new file mode 100644
index 00000000000..c8ec86f4a93
--- /dev/null
+++ b/chromium/media/fuchsia/camera/BUILD.gn
@@ -0,0 +1,19 @@
+# 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.
+
+source_set("test_support") {
+ testonly = true
+ public_deps = [
+ "//base",
+ "//testing/gtest",
+ "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.camera3",
+ "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.sysmem",
+ "//third_party/fuchsia-sdk/sdk/pkg/fidl_cpp",
+ "//ui/gfx/geometry",
+ ]
+ sources = [
+ "fake_fuchsia_camera.cc",
+ "fake_fuchsia_camera.h",
+ ]
+}
diff --git a/chromium/media/fuchsia/camera/fake_fuchsia_camera.cc b/chromium/media/fuchsia/camera/fake_fuchsia_camera.cc
new file mode 100644
index 00000000000..921a1b6cfb1
--- /dev/null
+++ b/chromium/media/fuchsia/camera/fake_fuchsia_camera.cc
@@ -0,0 +1,517 @@
+// 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/fuchsia/camera/fake_fuchsia_camera.h"
+
+#include <fuchsia/sysmem/cpp/fidl.h>
+#include <lib/sys/cpp/component_context.h>
+
+#include "base/fuchsia/default_context.h"
+#include "base/memory/platform_shared_memory_region.h"
+#include "base/memory/writable_shared_memory_region.h"
+#include "base/message_loop/message_loop_current.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+
+namespace {
+
+constexpr uint64_t kDefaultFakeDeviceId = 42;
+
+constexpr uint8_t kYPlaneSalt = 1;
+constexpr uint8_t kUPlaneSalt = 2;
+constexpr uint8_t kVPlaneSalt = 3;
+
+uint8_t GetTestFrameValue(gfx::Size size, int x, int y, uint8_t salt) {
+ return static_cast<uint8_t>(y + x * size.height() + salt);
+}
+
+// Fills one plane of a test frame. |data| points at the location of the pixel
+// (0, 0). |orientation| specifies frame orientation transformation that will be
+// applied on the receiving end, so this function applies _reverse_ of the
+// |orientation| transformation.
+void FillPlane(uint8_t* data,
+ gfx::Size size,
+ int x_step,
+ int y_step,
+ fuchsia::camera3::Orientation orientation,
+ uint8_t salt) {
+ // First flip X axis for flipped orientation.
+ if (orientation == fuchsia::camera3::Orientation::UP_FLIPPED ||
+ orientation == fuchsia::camera3::Orientation::DOWN_FLIPPED ||
+ orientation == fuchsia::camera3::Orientation::RIGHT_FLIPPED ||
+ orientation == fuchsia::camera3::Orientation::LEFT_FLIPPED) {
+ // Move the origin to the top right corner and flip the X axis.
+ data += (size.width() - 1) * x_step;
+ x_step = -x_step;
+ }
+
+ switch (orientation) {
+ case fuchsia::camera3::Orientation::UP:
+ case fuchsia::camera3::Orientation::UP_FLIPPED:
+ break;
+
+ case fuchsia::camera3::Orientation::DOWN:
+ case fuchsia::camera3::Orientation::DOWN_FLIPPED:
+ // Move |data| to point to the bottom right corner and reverse direction
+ // of both axes.
+ data += (size.width() - 1) * x_step + (size.height() - 1) * y_step;
+ x_step = -x_step;
+ y_step = -y_step;
+ break;
+
+ case fuchsia::camera3::Orientation::LEFT:
+ case fuchsia::camera3::Orientation::LEFT_FLIPPED:
+ // Rotate 90 degrees clockwise by moving |data| to point to the right top
+ // corner, swapping the axes and reversing direction of the Y axis.
+ data += (size.width() - 1) * x_step;
+ size = gfx::Size(size.height(), size.width());
+ std::swap(x_step, y_step);
+ y_step = -y_step;
+ break;
+
+ case fuchsia::camera3::Orientation::RIGHT:
+ case fuchsia::camera3::Orientation::RIGHT_FLIPPED:
+ // Rotate 90 degrees counter-clockwise by moving |data| to point to the
+ // bottom left corner, swapping the axes and reversing direction of the X
+ // axis.
+ data += (size.height() - 1) * y_step;
+ size = gfx::Size(size.height(), size.width());
+ std::swap(x_step, y_step);
+ x_step = -x_step;
+ break;
+ }
+
+ for (int y = 0; y < size.height(); ++y) {
+ for (int x = 0; x < size.width(); ++x) {
+ data[x * x_step + y * y_step] = GetTestFrameValue(size, x, y, salt);
+ }
+ }
+}
+
+void ValidatePlane(const uint8_t* data,
+ gfx::Size size,
+ size_t x_step,
+ size_t y_step,
+ uint8_t salt) {
+ for (int y = 0; y < size.height(); ++y) {
+ for (int x = 0; x < size.width(); ++x) {
+ SCOPED_TRACE(testing::Message() << "x=" << x << " y=" << y);
+ EXPECT_EQ(data[x * x_step + y * y_step],
+ GetTestFrameValue(size, x, y, salt));
+ }
+ }
+}
+
+} // namespace
+
+// static
+const gfx::Size FakeCameraStream::kMaxFrameSize = gfx::Size(100, 60);
+// static
+const gfx::Size FakeCameraStream::kDefaultFrameSize = gfx::Size(60, 40);
+
+// static
+void FakeCameraStream::ValidateFrameData(const uint8_t* data,
+ gfx::Size size,
+ uint8_t salt) {
+ const uint8_t* y_plane = data;
+ {
+ SCOPED_TRACE("Y plane");
+ ValidatePlane(y_plane, size, 1, size.width(), salt + kYPlaneSalt);
+ }
+
+ gfx::Size uv_size(size.width() / 2, size.height() / 2);
+ const uint8_t* u_plane = y_plane + size.width() * size.height();
+ {
+ SCOPED_TRACE("U plane");
+ ValidatePlane(u_plane, uv_size, 1, uv_size.width(), salt + kUPlaneSalt);
+ }
+
+ const uint8_t* v_plane = u_plane + uv_size.width() * uv_size.height();
+ {
+ SCOPED_TRACE("V plane");
+ ValidatePlane(v_plane, uv_size, 1, uv_size.width(), salt + kVPlaneSalt);
+ }
+}
+
+struct FakeCameraStream::Buffer {
+ explicit Buffer(base::WritableSharedMemoryMapping mapping)
+ : mapping(std::move(mapping)),
+ release_fence_watch_controller(FROM_HERE) {}
+
+ base::WritableSharedMemoryMapping mapping;
+
+ // Frame is used by the client when the |release_fence| is not null.
+ zx::eventpair release_fence;
+
+ base::MessagePumpForIO::ZxHandleWatchController
+ release_fence_watch_controller;
+};
+
+FakeCameraStream::FakeCameraStream() : binding_(this) {}
+FakeCameraStream::~FakeCameraStream() = default;
+
+void FakeCameraStream::Bind(
+ fidl::InterfaceRequest<fuchsia::camera3::Stream> request) {
+ binding_.Bind(std::move(request));
+}
+
+void FakeCameraStream::SetFakeResolution(gfx::Size resolution) {
+ resolution_ = resolution;
+ resolution_update_ =
+ fuchsia::math::Size{resolution_.width(), resolution_.height()};
+ SendResolution();
+}
+
+void FakeCameraStream::SetFakeOrientation(
+ fuchsia::camera3::Orientation orientation) {
+ orientation_ = orientation;
+ orientation_update_ = orientation;
+ SendOrientation();
+}
+
+bool FakeCameraStream::WaitBuffersAllocated() {
+ EXPECT_FALSE(wait_buffers_allocated_run_loop_);
+
+ if (!buffers_.empty())
+ return true;
+
+ wait_buffers_allocated_run_loop_.emplace();
+ wait_buffers_allocated_run_loop_->Run();
+ wait_buffers_allocated_run_loop_.reset();
+
+ return !buffers_.empty();
+}
+
+bool FakeCameraStream::WaitFreeBuffer() {
+ EXPECT_FALSE(wait_free_buffer_run_loop_);
+
+ if (num_used_buffers_ < buffers_.size())
+ return true;
+
+ wait_free_buffer_run_loop_.emplace();
+ wait_free_buffer_run_loop_->Run();
+ wait_free_buffer_run_loop_.reset();
+
+ return num_used_buffers_ < buffers_.size();
+}
+
+void FakeCameraStream::ProduceFrame(base::TimeTicks timestamp, uint8_t salt) {
+ ASSERT_LT(num_used_buffers_, buffers_.size());
+ ASSERT_FALSE(next_frame_);
+
+ size_t index = buffers_.size();
+ for (size_t i = 0; i < buffers_.size(); ++i) {
+ if (!buffers_[i]->release_fence) {
+ index = i;
+ break;
+ }
+ }
+ EXPECT_LT(index, buffers_.size());
+
+ auto* buffer = buffers_[index].get();
+
+ gfx::Size coded_size((resolution_.width() + 1) & ~1,
+ (resolution_.height() + 1) & ~1);
+
+ // Fill Y plane.
+ uint8_t* y_plane = reinterpret_cast<uint8_t*>(buffer->mapping.memory());
+ size_t stride = kMaxFrameSize.width();
+ FillPlane(y_plane, coded_size, /*x_step=*/1, /*y_step=*/stride, orientation_,
+ salt + kYPlaneSalt);
+
+ // Fill UV plane.
+ gfx::Size uv_size(coded_size.width() / 2, coded_size.height() / 2);
+ uint8_t* uv_plane = y_plane + kMaxFrameSize.width() * kMaxFrameSize.height();
+ FillPlane(uv_plane, uv_size, /*x_step=*/2, /*y_step=*/stride, orientation_,
+ salt + kUPlaneSalt);
+ FillPlane(uv_plane + 1, uv_size, /*x_step=*/2, /*y_step=*/stride,
+ orientation_, salt + kVPlaneSalt);
+
+ // Create FrameInfo.
+ fuchsia::camera3::FrameInfo frame;
+ frame.frame_counter = frame_counter_++;
+ frame.buffer_index = 0;
+ frame.timestamp = timestamp.ToZxTime();
+ EXPECT_EQ(
+ zx::eventpair::create(0u, &frame.release_fence, &buffer->release_fence),
+ ZX_OK);
+
+ // Watch release fence to get notified when the frame is released.
+ base::MessageLoopCurrentForIO::Get()->WatchZxHandle(
+ buffer->release_fence.get(), /*persistent=*/false,
+ ZX_EVENTPAIR_PEER_CLOSED, &buffer->release_fence_watch_controller, this);
+
+ num_used_buffers_++;
+ next_frame_ = std::move(frame);
+ SendNextFrame();
+}
+
+void FakeCameraStream::WatchResolution(WatchResolutionCallback callback) {
+ EXPECT_FALSE(watch_resolution_callback_);
+ watch_resolution_callback_ = std::move(callback);
+ SendResolution();
+}
+
+void FakeCameraStream::WatchOrientation(WatchOrientationCallback callback) {
+ EXPECT_FALSE(watch_orientation_callback_);
+ watch_orientation_callback_ = std::move(callback);
+ SendOrientation();
+}
+
+void FakeCameraStream::SetBufferCollection(
+ fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken>
+ token_handle) {
+ EXPECT_TRUE(token_handle);
+
+ // Drop old buffers.
+ buffers_.clear();
+ if (buffer_collection_) {
+ buffer_collection_->Close();
+ buffer_collection_.Unbind();
+ }
+
+ // Use a SyncPtr to be able to wait for Sync() synchronously.
+ fuchsia::sysmem::BufferCollectionTokenSyncPtr token;
+ token.Bind(std::move(token_handle));
+
+ // Duplicate the token to access from the stream.
+ fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken> local_token;
+ zx_status_t status =
+ token->Duplicate(/*rights_attenuation_mask=*/0, local_token.NewRequest());
+ EXPECT_EQ(status, ZX_OK);
+
+ status = token->Sync();
+ EXPECT_EQ(status, ZX_OK);
+
+ // Return the token back to the client.
+ new_buffer_collection_token_ = token.Unbind();
+ SendBufferCollection();
+
+ // Initialize the new collection using |local_token|.
+ auto allocator = base::fuchsia::ComponentContextForCurrentProcess()
+ ->svc()
+ ->Connect<fuchsia::sysmem::Allocator>();
+
+ allocator->BindSharedCollection(std::move(local_token),
+ buffer_collection_.NewRequest());
+ EXPECT_EQ(status, ZX_OK);
+
+ buffer_collection_.set_error_handler(
+ fit::bind_member(this, &FakeCameraStream::OnBufferCollectionError));
+
+ fuchsia::sysmem::BufferCollectionConstraints constraints;
+ constraints.usage.cpu =
+ fuchsia::sysmem::cpuUsageRead | fuchsia::sysmem::cpuUsageWrite;
+
+ // The client is expected to request buffers it may need. We don't need to
+ // reserve any for the server side.
+ constraints.min_buffer_count_for_camping = 0;
+
+ // Initialize image format.
+ constraints.image_format_constraints_count = 1;
+ constraints.image_format_constraints[0].pixel_format.type =
+ fuchsia::sysmem::PixelFormatType::NV12;
+ constraints.image_format_constraints[0].color_spaces_count = 1;
+ constraints.image_format_constraints[0].color_space[0].type =
+ fuchsia::sysmem::ColorSpaceType::REC601_NTSC;
+ constraints.image_format_constraints[0].required_max_coded_width =
+ kMaxFrameSize.width();
+ constraints.image_format_constraints[0].required_max_coded_height =
+ kMaxFrameSize.height();
+
+ buffer_collection_->SetConstraints(/*has_constraints=*/true,
+ std::move(constraints));
+ buffer_collection_->WaitForBuffersAllocated(
+ fit::bind_member(this, &FakeCameraStream::OnBufferCollectionAllocated));
+}
+
+void FakeCameraStream::WatchBufferCollection(
+ WatchBufferCollectionCallback callback) {
+ EXPECT_FALSE(watch_buffer_collection_callback_);
+ watch_buffer_collection_callback_ = std::move(callback);
+ SendBufferCollection();
+}
+
+void FakeCameraStream::GetNextFrame(GetNextFrameCallback callback) {
+ EXPECT_FALSE(get_next_frame_callback_);
+ get_next_frame_callback_ = std::move(callback);
+ SendNextFrame();
+}
+
+void FakeCameraStream::NotImplemented_(const std::string& name) {
+ ADD_FAILURE() << "NotImplemented_: " << name;
+}
+
+void FakeCameraStream::OnBufferCollectionError(zx_status_t status) {
+ ADD_FAILURE() << "BufferCollection failed.";
+ if (wait_buffers_allocated_run_loop_)
+ wait_buffers_allocated_run_loop_->Quit();
+ if (wait_free_buffer_run_loop_)
+ wait_free_buffer_run_loop_->Quit();
+}
+
+void FakeCameraStream::OnBufferCollectionAllocated(
+ zx_status_t status,
+ fuchsia::sysmem::BufferCollectionInfo_2 buffer_collection_info) {
+ if (status != ZX_OK) {
+ OnBufferCollectionError(status);
+ return;
+ }
+
+ EXPECT_TRUE(buffers_.empty());
+ EXPECT_TRUE(buffer_collection_info.settings.has_image_format_constraints);
+ EXPECT_EQ(buffer_collection_info.settings.image_format_constraints
+ .pixel_format.type,
+ fuchsia::sysmem::PixelFormatType::NV12);
+
+ size_t buffer_size =
+ buffer_collection_info.settings.buffer_settings.size_bytes;
+ for (size_t i = 0; i < buffer_collection_info.buffer_count; ++i) {
+ auto& buffer = buffer_collection_info.buffers[i];
+ EXPECT_EQ(buffer.vmo_usable_start, 0U);
+ auto region = base::WritableSharedMemoryRegion::Deserialize(
+ base::subtle::PlatformSharedMemoryRegion::Take(
+ std::move(buffer.vmo),
+ base::subtle::PlatformSharedMemoryRegion::Mode::kWritable,
+ buffer_size, base::UnguessableToken::Create()));
+ auto mapping = region.Map();
+ EXPECT_TRUE(mapping.IsValid());
+ buffers_.push_back(std::make_unique<Buffer>(std::move(mapping)));
+ }
+
+ if (wait_buffers_allocated_run_loop_)
+ wait_buffers_allocated_run_loop_->Quit();
+}
+
+void FakeCameraStream::SendResolution() {
+ if (!watch_resolution_callback_ || !resolution_update_)
+ return;
+ watch_resolution_callback_(resolution_update_.value());
+ watch_resolution_callback_ = {};
+ resolution_update_.reset();
+}
+
+void FakeCameraStream::SendOrientation() {
+ if (!watch_orientation_callback_ || !orientation_update_)
+ return;
+ watch_orientation_callback_(orientation_update_.value());
+ watch_orientation_callback_ = {};
+ orientation_update_.reset();
+}
+
+void FakeCameraStream::SendBufferCollection() {
+ if (!watch_buffer_collection_callback_ || !new_buffer_collection_token_)
+ return;
+ watch_buffer_collection_callback_(
+ std::move(new_buffer_collection_token_.value()));
+ watch_buffer_collection_callback_ = {};
+ new_buffer_collection_token_.reset();
+}
+
+void FakeCameraStream::SendNextFrame() {
+ if (!get_next_frame_callback_ || !next_frame_)
+ return;
+ get_next_frame_callback_(std::move(next_frame_.value()));
+ get_next_frame_callback_ = {};
+ next_frame_.reset();
+}
+
+void FakeCameraStream::OnZxHandleSignalled(zx_handle_t handle,
+ zx_signals_t signals) {
+ EXPECT_EQ(signals, ZX_EVENTPAIR_PEER_CLOSED);
+
+ // Find the buffer that corresponds to the |handle|.
+ size_t index = buffers_.size();
+ for (size_t i = 0; i < buffers_.size(); ++i) {
+ if (buffers_[i]->release_fence.get() == handle) {
+ index = i;
+ break;
+ }
+ }
+ ASSERT_LT(index, buffers_.size());
+ buffers_[index]->release_fence = {};
+ buffers_[index]->release_fence_watch_controller.StopWatchingZxHandle();
+ num_used_buffers_--;
+
+ if (wait_free_buffer_run_loop_)
+ wait_free_buffer_run_loop_->Quit();
+}
+FakeCameraDevice::FakeCameraDevice(FakeCameraStream* stream)
+ : binding_(this), stream_(stream) {}
+
+FakeCameraDevice::~FakeCameraDevice() = default;
+
+void FakeCameraDevice::Bind(
+ fidl::InterfaceRequest<fuchsia::camera3::Device> request) {
+ binding_.Bind(std::move(request));
+}
+
+void FakeCameraDevice::GetIdentifier(GetIdentifierCallback callback) {
+ callback("Fake Camera");
+}
+
+void FakeCameraDevice::GetConfigurations(GetConfigurationsCallback callback) {
+ std::vector<fuchsia::camera3::Configuration> configurations(1);
+ configurations[0].streams.resize(1);
+ configurations[0].streams[0].frame_rate.numerator = 30;
+ configurations[0].streams[0].frame_rate.denominator = 1;
+ configurations[0].streams[0].image_format.pixel_format.type =
+ fuchsia::sysmem::PixelFormatType::NV12;
+ configurations[0].streams[0].image_format.coded_width = 640;
+ configurations[0].streams[0].image_format.coded_height = 480;
+ configurations[0].streams[0].image_format.bytes_per_row = 640;
+ callback(std::move(configurations));
+}
+
+void FakeCameraDevice::ConnectToStream(
+ uint32_t index,
+ fidl::InterfaceRequest<fuchsia::camera3::Stream> request) {
+ EXPECT_EQ(index, 0U);
+ stream_->Bind(std::move(request));
+}
+
+void FakeCameraDevice::NotImplemented_(const std::string& name) {
+ ADD_FAILURE() << "NotImplemented_: " << name;
+}
+
+FakeCameraDeviceWatcher::FakeCameraDeviceWatcher(
+ sys::OutgoingDirectory* outgoing_directory) {
+ outgoing_directory->AddPublicService<fuchsia::camera3::DeviceWatcher>(
+ [this](fidl::InterfaceRequest<fuchsia::camera3::DeviceWatcher> request) {
+ bindings_.AddBinding(std::make_unique<Client>(&device_),
+ std::move(request));
+ });
+}
+
+FakeCameraDeviceWatcher::~FakeCameraDeviceWatcher() = default;
+
+FakeCameraDeviceWatcher::Client::Client(FakeCameraDevice* device)
+ : device_(device) {}
+FakeCameraDeviceWatcher::Client::~Client() {}
+
+void FakeCameraDeviceWatcher::Client::WatchDevices(
+ WatchDevicesCallback callback) {
+ if (devices_sent_)
+ return;
+
+ std::vector<fuchsia::camera3::WatchDevicesEvent> events(1);
+ events[0].set_added(kDefaultFakeDeviceId);
+ callback(std::move(events));
+
+ devices_sent_ = true;
+}
+
+void FakeCameraDeviceWatcher::Client::ConnectToDevice(
+ uint64_t id,
+ fidl::InterfaceRequest<fuchsia::camera3::Device> request) {
+ if (id == kDefaultFakeDeviceId)
+ device_->Bind(std::move(request));
+}
+
+void FakeCameraDeviceWatcher::Client::NotImplemented_(const std::string& name) {
+ ADD_FAILURE() << "NotImplemented_: " << name;
+}
+
+} // namespace media \ No newline at end of file
diff --git a/chromium/media/fuchsia/camera/fake_fuchsia_camera.h b/chromium/media/fuchsia/camera/fake_fuchsia_camera.h
new file mode 100644
index 00000000000..016c68d0fc1
--- /dev/null
+++ b/chromium/media/fuchsia/camera/fake_fuchsia_camera.h
@@ -0,0 +1,194 @@
+// 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_FUCHSIA_CAMERA_FAKE_FUCHSIA_CAMERA_H_
+#define MEDIA_FUCHSIA_CAMERA_FAKE_FUCHSIA_CAMERA_H_
+
+#include <fuchsia/camera3/cpp/fidl.h>
+#include <fuchsia/camera3/cpp/fidl_test_base.h>
+#include <lib/fidl/cpp/binding.h>
+#include <lib/fidl/cpp/binding_set.h>
+#include <lib/sys/cpp/outgoing_directory.h>
+
+#include <vector>
+
+#include "base/message_loop/message_pump_for_io.h"
+#include "base/optional.h"
+#include "base/run_loop.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace media {
+
+class FakeCameraStream : public fuchsia::camera3::testing::Stream_TestBase,
+ public base::MessagePumpForIO::ZxHandleWatcher {
+ public:
+ static const gfx::Size kMaxFrameSize;
+ static const gfx::Size kDefaultFrameSize;
+
+ // Verifies that the I420 image stored at |data| matches the frame produced
+ // by ProduceFrame().
+ static void ValidateFrameData(const uint8_t* data,
+ gfx::Size size,
+ uint8_t salt);
+
+ FakeCameraStream();
+ ~FakeCameraStream() final;
+
+ FakeCameraStream(const FakeCameraStream&) = delete;
+ FakeCameraStream& operator=(const FakeCameraStream&) = delete;
+
+ void Bind(fidl::InterfaceRequest<fuchsia::camera3::Stream> request);
+
+ void SetFakeResolution(gfx::Size resolution);
+ void SetFakeOrientation(fuchsia::camera3::Orientation orientation);
+
+ // Waits for the buffer collection to be allocated. Returns true if the buffer
+ // collection was allocated successfully.
+ bool WaitBuffersAllocated();
+
+ // Waits until there is at least one free buffer that can be used for the next
+ // frame.
+ bool WaitFreeBuffer();
+
+ void ProduceFrame(base::TimeTicks timestamp, uint8_t salt);
+
+ private:
+ struct Buffer;
+
+ // fuchsia::camera3::Stream implementation.
+ void WatchResolution(WatchResolutionCallback callback) final;
+ void WatchOrientation(WatchOrientationCallback callback) final;
+ void SetBufferCollection(
+ fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken>
+ token_handle) final;
+ void WatchBufferCollection(WatchBufferCollectionCallback callback) final;
+ void GetNextFrame(GetNextFrameCallback callback) final;
+
+ // fuchsia::camera3::testing::Stream_TestBase override.
+ void NotImplemented_(const std::string& name) override;
+
+ void OnBufferCollectionError(zx_status_t status);
+
+ void OnBufferCollectionAllocated(
+ zx_status_t status,
+ fuchsia::sysmem::BufferCollectionInfo_2 buffer_collection_info);
+
+ // Calls callback for the pending WatchResolution() if the call is pending and
+ // resolution has been updated.
+ void SendResolution();
+
+ // Calls callback for the pending WatchOrientation() if the call is pending
+ // and orientation has been updated.
+ void SendOrientation();
+
+ // Calls callback for the pending WatchBufferCollection() if we have a new
+ // token and the call is pending.
+ void SendBufferCollection();
+
+ // Calls callback for the pending GetNextFrame() if we have a new frame and
+ // the call is pending.
+ void SendNextFrame();
+
+ // ZxHandleWatcher interface. Used to wait for frame release_fences to get
+ // notified when the client releases a buffer.
+ void OnZxHandleSignalled(zx_handle_t handle, zx_signals_t signals) final;
+
+ fidl::Binding<fuchsia::camera3::Stream> binding_;
+
+ gfx::Size resolution_ = kDefaultFrameSize;
+ fuchsia::camera3::Orientation orientation_ =
+ fuchsia::camera3::Orientation::UP;
+
+ base::Optional<fuchsia::math::Size> resolution_update_ = fuchsia::math::Size{
+ kDefaultFrameSize.width(), kDefaultFrameSize.height()};
+ WatchResolutionCallback watch_resolution_callback_;
+
+ base::Optional<fuchsia::camera3::Orientation> orientation_update_ =
+ fuchsia::camera3::Orientation::UP;
+ WatchOrientationCallback watch_orientation_callback_;
+
+ base::Optional<fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken>>
+ new_buffer_collection_token_;
+ WatchBufferCollectionCallback watch_buffer_collection_callback_;
+
+ base::Optional<fuchsia::camera3::FrameInfo> next_frame_;
+ GetNextFrameCallback get_next_frame_callback_;
+
+ fuchsia::sysmem::BufferCollectionPtr buffer_collection_;
+
+ base::Optional<base::RunLoop> wait_buffers_allocated_run_loop_;
+ base::Optional<base::RunLoop> wait_free_buffer_run_loop_;
+
+ std::vector<std::unique_ptr<Buffer>> buffers_;
+ size_t num_used_buffers_ = 0;
+
+ size_t frame_counter_ = 0;
+};
+
+class FakeCameraDevice : public fuchsia::camera3::testing::Device_TestBase {
+ public:
+ explicit FakeCameraDevice(FakeCameraStream* stream);
+ ~FakeCameraDevice() final;
+
+ FakeCameraDevice(const FakeCameraDevice&) = delete;
+ FakeCameraDevice& operator=(const FakeCameraDevice&) = delete;
+
+ void Bind(fidl::InterfaceRequest<fuchsia::camera3::Device> request);
+
+ private:
+ // fuchsia::camera3::Device implementation.
+ void GetIdentifier(GetIdentifierCallback callback) final;
+ void GetConfigurations(GetConfigurationsCallback callback) final;
+ void ConnectToStream(
+ uint32_t index,
+ fidl::InterfaceRequest<fuchsia::camera3::Stream> request) final;
+
+ // fuchsia::camera3::testing::Device_TestBase override.
+ void NotImplemented_(const std::string& name) override;
+
+ fidl::Binding<fuchsia::camera3::Device> binding_;
+ FakeCameraStream* const stream_;
+};
+
+class FakeCameraDeviceWatcher {
+ public:
+ explicit FakeCameraDeviceWatcher(sys::OutgoingDirectory* outgoing_directory);
+ ~FakeCameraDeviceWatcher();
+
+ FakeCameraDeviceWatcher(const FakeCameraDeviceWatcher&) = delete;
+ FakeCameraDeviceWatcher& operator=(const FakeCameraDeviceWatcher&) = delete;
+
+ private:
+ class Client : public fuchsia::camera3::testing::DeviceWatcher_TestBase {
+ public:
+ explicit Client(FakeCameraDevice* device);
+ ~Client() final;
+
+ Client(const Client&) = delete;
+ Client& operator=(const Client&) = delete;
+
+ // fuchsia::camera3::testing::DeviceWatcher_TestBase override.
+ void NotImplemented_(const std::string& name) final;
+
+ // fuchsia::camera3::DeviceWatcher implementation.
+ void WatchDevices(WatchDevicesCallback callback) final;
+ void ConnectToDevice(
+ uint64_t id,
+ fidl::InterfaceRequest<fuchsia::camera3::Device> request) final;
+
+ private:
+ bool devices_sent_ = false;
+ FakeCameraDevice* const device_;
+ };
+
+ fidl::BindingSet<fuchsia::camera3::DeviceWatcher, std::unique_ptr<Client>>
+ bindings_;
+
+ FakeCameraStream stream_;
+ FakeCameraDevice device_{&stream_};
+};
+
+} // namespace media
+
+#endif // MEDIA_FUCHSIA_CAMERA_FAKE_FUCHSIA_CAMERA_H_
diff --git a/chromium/media/fuchsia/cdm/fuchsia_decryptor.cc b/chromium/media/fuchsia/cdm/fuchsia_decryptor.cc
index b36fcfb878c..e10b2a1a863 100644
--- a/chromium/media/fuchsia/cdm/fuchsia_decryptor.cc
+++ b/chromium/media/fuchsia/cdm/fuchsia_decryptor.cc
@@ -4,9 +4,10 @@
#include "media/fuchsia/cdm/fuchsia_decryptor.h"
+#include "base/check.h"
#include "base/fuchsia/fuchsia_logging.h"
#include "base/location.h"
-#include "base/logging.h"
+#include "base/notreached.h"
#include "base/threading/thread_task_runner_handle.h"
#include "media/base/decoder_buffer.h"
#include "media/base/video_frame.h"
diff --git a/chromium/media/fuchsia/common/sysmem_buffer_pool.cc b/chromium/media/fuchsia/common/sysmem_buffer_pool.cc
index 6c30ad8a0df..94c1a1f43d9 100644
--- a/chromium/media/fuchsia/common/sysmem_buffer_pool.cc
+++ b/chromium/media/fuchsia/common/sysmem_buffer_pool.cc
@@ -140,13 +140,18 @@ BufferAllocator::BufferAllocator() {
BufferAllocator::~BufferAllocator() = default;
+fuchsia::sysmem::BufferCollectionTokenPtr BufferAllocator::CreateNewToken() {
+ fuchsia::sysmem::BufferCollectionTokenPtr collection_token;
+ allocator_->AllocateSharedCollection(collection_token.NewRequest());
+ return collection_token;
+}
+
std::unique_ptr<SysmemBufferPool::Creator>
BufferAllocator::MakeBufferPoolCreator(size_t num_of_tokens) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
// Create a new sysmem buffer collection token for the allocated buffers.
- fuchsia::sysmem::BufferCollectionTokenPtr collection_token;
- allocator_->AllocateSharedCollection(collection_token.NewRequest());
+ fuchsia::sysmem::BufferCollectionTokenPtr collection_token = CreateNewToken();
// Create collection token for sharing with other components.
std::vector<fuchsia::sysmem::BufferCollectionTokenPtr> shared_tokens;
@@ -166,4 +171,15 @@ BufferAllocator::MakeBufferPoolCreator(size_t num_of_tokens) {
std::move(buffer_collection), std::move(shared_tokens));
}
+std::unique_ptr<SysmemBufferPool::Creator>
+BufferAllocator::MakeBufferPoolCreatorFromToken(
+ fuchsia::sysmem::BufferCollectionTokenPtr token) {
+ fuchsia::sysmem::BufferCollectionPtr buffer_collection;
+ allocator_->BindSharedCollection(std::move(token),
+ buffer_collection.NewRequest());
+ return std::make_unique<SysmemBufferPool::Creator>(
+ std::move(buffer_collection),
+ std::vector<fuchsia::sysmem::BufferCollectionTokenPtr>{});
+}
+
} // namespace media
diff --git a/chromium/media/fuchsia/common/sysmem_buffer_pool.h b/chromium/media/fuchsia/common/sysmem_buffer_pool.h
index c4c6ceabb54..ec657ed370d 100644
--- a/chromium/media/fuchsia/common/sysmem_buffer_pool.h
+++ b/chromium/media/fuchsia/common/sysmem_buffer_pool.h
@@ -98,9 +98,14 @@ class BufferAllocator {
BufferAllocator();
~BufferAllocator();
+ fuchsia::sysmem::BufferCollectionTokenPtr CreateNewToken();
+
std::unique_ptr<SysmemBufferPool::Creator> MakeBufferPoolCreator(
size_t num_shared_token);
+ std::unique_ptr<SysmemBufferPool::Creator> MakeBufferPoolCreatorFromToken(
+ fuchsia::sysmem::BufferCollectionTokenPtr token);
+
// TODO(sergeyu): Update FuchsiaVideoDecoder to use SysmemBufferPool and
// remove this function.
fuchsia::sysmem::Allocator* raw() { return allocator_.get(); }
diff --git a/chromium/media/fuchsia/common/sysmem_buffer_reader.cc b/chromium/media/fuchsia/common/sysmem_buffer_reader.cc
index 8667a877398..406096a2617 100644
--- a/chromium/media/fuchsia/common/sysmem_buffer_reader.cc
+++ b/chromium/media/fuchsia/common/sysmem_buffer_reader.cc
@@ -17,27 +17,74 @@ SysmemBufferReader::~SysmemBufferReader() = default;
bool SysmemBufferReader::Read(size_t index,
size_t offset,
base::span<uint8_t> data) {
- DCHECK_LT(index, collection_info_.buffer_count);
- const fuchsia::sysmem::BufferMemorySettings& settings =
- collection_info_.settings.buffer_settings;
- fuchsia::sysmem::VmoBuffer& buffer = collection_info_.buffers[index];
- DCHECK_LE(buffer.vmo_usable_start + offset + data.size(),
- settings.size_bytes);
+ DCHECK_LT(index, num_buffers());
+ DCHECK_LE(offset + data.size(),
+ collection_info_.settings.buffer_settings.size_bytes);
+ const fuchsia::sysmem::VmoBuffer& buffer = collection_info_.buffers[index];
size_t vmo_offset = buffer.vmo_usable_start + offset;
- // Invalidate cache.
- if (settings.coherency_domain == fuchsia::sysmem::CoherencyDomain::RAM) {
- zx_status_t status = buffer.vmo.op_range(
- ZX_VMO_OP_CACHE_CLEAN_INVALIDATE, vmo_offset, data.size(), nullptr, 0);
- ZX_LOG_IF(ERROR, status != ZX_OK, status) << "Fail to invalidate cache";
- }
+ InvalidateCacheIfNecessary(buffer.vmo, vmo_offset, data.size());
zx_status_t status = buffer.vmo.read(data.data(), vmo_offset, data.size());
+
ZX_LOG_IF(ERROR, status != ZX_OK, status) << "Fail to read";
return status == ZX_OK;
}
+base::span<const uint8_t> SysmemBufferReader::GetMappingForBuffer(
+ size_t index) {
+ if (mappings_.empty())
+ mappings_.resize(num_buffers());
+
+ DCHECK_LT(index, mappings_.size());
+
+ const fuchsia::sysmem::BufferMemorySettings& settings =
+ collection_info_.settings.buffer_settings;
+ fuchsia::sysmem::VmoBuffer& buffer = collection_info_.buffers[index];
+
+ auto& mapping = mappings_[index];
+ size_t buffer_start = buffer.vmo_usable_start;
+
+ if (!mapping.IsValid()) {
+ size_t mapping_size = buffer_start + settings.size_bytes;
+ auto region = base::ReadOnlySharedMemoryRegion::Deserialize(
+ base::subtle::PlatformSharedMemoryRegion::Take(
+ std::move(buffer.vmo),
+ base::subtle::PlatformSharedMemoryRegion::Mode::kReadOnly,
+ mapping_size, base::UnguessableToken::Create()));
+
+ mapping = region.Map();
+
+ // Return the VMO handle back to buffer_.
+ buffer.vmo = base::ReadOnlySharedMemoryRegion::TakeHandleForSerialization(
+ std::move(region))
+ .PassPlatformHandle();
+ }
+
+ if (!mapping.IsValid()) {
+ DLOG(WARNING) << "Failed to map VMO returned by sysmem";
+ return {};
+ }
+
+ InvalidateCacheIfNecessary(buffer.vmo, buffer_start, settings.size_bytes);
+
+ return base::make_span(
+ reinterpret_cast<const uint8_t*>(mapping.memory()) + buffer_start,
+ settings.size_bytes);
+}
+
+void SysmemBufferReader::InvalidateCacheIfNecessary(const zx::vmo& vmo,
+ size_t offset,
+ size_t size) {
+ if (collection_info_.settings.buffer_settings.coherency_domain ==
+ fuchsia::sysmem::CoherencyDomain::RAM) {
+ zx_status_t status = vmo.op_range(ZX_VMO_OP_CACHE_CLEAN_INVALIDATE, offset,
+ size, nullptr, 0);
+ ZX_LOG_IF(ERROR, status != ZX_OK, status) << "Fail to invalidate cache";
+ }
+}
+
// static
std::unique_ptr<SysmemBufferReader> SysmemBufferReader::Create(
fuchsia::sysmem::BufferCollectionInfo_2 info) {
diff --git a/chromium/media/fuchsia/common/sysmem_buffer_reader.h b/chromium/media/fuchsia/common/sysmem_buffer_reader.h
index 0c314162e68..bc9727facd0 100644
--- a/chromium/media/fuchsia/common/sysmem_buffer_reader.h
+++ b/chromium/media/fuchsia/common/sysmem_buffer_reader.h
@@ -11,6 +11,7 @@
#include <memory>
#include "base/containers/span.h"
+#include "base/memory/read_only_shared_memory_region.h"
namespace media {
@@ -26,11 +27,35 @@ class SysmemBufferReader {
explicit SysmemBufferReader(fuchsia::sysmem::BufferCollectionInfo_2 info);
~SysmemBufferReader();
+ size_t num_buffers() const { return collection_info_.buffer_count; }
+
+ const fuchsia::sysmem::SingleBufferSettings& buffer_settings() {
+ return collection_info_.settings;
+ }
+
// Read the buffer content at |index| into |data|, starting from |offset|.
bool Read(size_t index, size_t offset, base::span<uint8_t> data);
+ // Returns a span for the memory-mapping of the buffer with the specified
+ // |index|, invalidating the CPU cache for the specified buffer in the sysmem
+ // collection if necessary. Buffers are mapped lazily and remain mapped for
+ // the lifetime of SysmemBufferReader. Should be called every time before
+ // accessing the mapping to ensure that the CPU cache is invalidated for
+ // buffers with RAM coherency.
+ base::span<const uint8_t> GetMappingForBuffer(size_t index);
+
private:
+ // Invalidates CPU cache for the specified range of the specified vmo in
+ // case the collection was allocated with RAM coherency. No-op for collections
+ // with CPU coherency. Called from Read() and GetMapping() to ensure clients
+ // get up-to-date buffer content in case the buffer was updated by other
+ // participants directly in RAM (bypassing CPU cache).
+ void InvalidateCacheIfNecessary(const zx::vmo& buffer,
+ size_t offset,
+ size_t size);
+
fuchsia::sysmem::BufferCollectionInfo_2 collection_info_;
+ std::vector<base::ReadOnlySharedMemoryMapping> mappings_;
DISALLOW_COPY_AND_ASSIGN(SysmemBufferReader);
};
diff --git a/chromium/media/fuchsia/metrics/BUILD.gn b/chromium/media/fuchsia/metrics/BUILD.gn
new file mode 100644
index 00000000000..ee6952ba233
--- /dev/null
+++ b/chromium/media/fuchsia/metrics/BUILD.gn
@@ -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.
+
+source_set("metrics") {
+ sources = [
+ "fuchsia_playback_events_recorder.cc",
+ "fuchsia_playback_events_recorder.h",
+ ]
+ deps = [
+ "//media/mojo/mojom",
+ "//mojo/public/cpp/bindings",
+ ]
+}
+
+source_set("unittests") {
+ testonly = true
+
+ deps = [
+ ":metrics",
+ "//base",
+ "//base/test:test_support",
+ "//media",
+ "//testing/gtest",
+ ]
+
+ sources = [ "fuchsia_playback_events_recorder_test.cc" ]
+}
diff --git a/chromium/media/fuchsia/metrics/DEPS b/chromium/media/fuchsia/metrics/DEPS
new file mode 100644
index 00000000000..ef8ad28d9d4
--- /dev/null
+++ b/chromium/media/fuchsia/metrics/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+mojo/public",
+]
diff --git a/chromium/media/fuchsia/metrics/fuchsia_playback_events_recorder.cc b/chromium/media/fuchsia/metrics/fuchsia_playback_events_recorder.cc
new file mode 100644
index 00000000000..d883b08b38a
--- /dev/null
+++ b/chromium/media/fuchsia/metrics/fuchsia_playback_events_recorder.cc
@@ -0,0 +1,139 @@
+// 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/fuchsia/metrics/fuchsia_playback_events_recorder.h"
+
+#include "base/metrics/user_metrics.h"
+#include "base/strings/stringprintf.h"
+#include "mojo/public/cpp/bindings/self_owned_receiver.h"
+
+namespace media {
+
+namespace {
+
+void RecordEventWithValueAt(const char* name,
+ int64_t value,
+ base::TimeTicks time) {
+ base::RecordComputedActionAt(
+ base::StringPrintf("WebEngine.Media.%s:%ld", name, value), time);
+}
+
+void RecordEventWithValue(const char* name, int64_t value) {
+ RecordEventWithValueAt(name, value, base::TimeTicks::Now());
+}
+
+constexpr base::TimeDelta kBitrateReportPeriod =
+ base::TimeDelta::FromSeconds(5);
+
+} // namespace
+
+FuchsiaPlaybackEventsRecorder::BitrateEstimator::BitrateEstimator() {}
+FuchsiaPlaybackEventsRecorder::BitrateEstimator::~BitrateEstimator() {}
+
+void FuchsiaPlaybackEventsRecorder::BitrateEstimator::Update(
+ const PipelineStatistics& stats) {
+ base::TimeTicks now = base::TimeTicks::Now();
+
+ // The code below trusts that |stats| are valid even though they came from an
+ // untrusted process. That's accepable because the stats are used only to
+ // record metrics.
+ if (last_stats_) {
+ time_elapsed_ += now - last_stats_time_;
+ audio_bytes_ +=
+ stats.audio_bytes_decoded - last_stats_->audio_bytes_decoded;
+ video_bytes_ +=
+ stats.video_bytes_decoded - last_stats_->video_bytes_decoded;
+ if (time_elapsed_ >= kBitrateReportPeriod) {
+ size_t audio_bitrate_kbps =
+ 8 * audio_bytes_ / time_elapsed_.InMilliseconds();
+ RecordEventWithValueAt("AudioBitrate", audio_bitrate_kbps, now);
+
+ size_t video_bitrate_kbps =
+ 8 * video_bytes_ / time_elapsed_.InMilliseconds();
+ RecordEventWithValueAt("VideoBitrate", video_bitrate_kbps, now);
+
+ time_elapsed_ = base::TimeDelta();
+ audio_bytes_ = 0;
+ video_bytes_ = 0;
+ }
+ }
+
+ last_stats_ = stats;
+ last_stats_time_ = now;
+}
+
+void FuchsiaPlaybackEventsRecorder::BitrateEstimator::OnPause() {
+ last_stats_ = {};
+}
+
+// static
+void FuchsiaPlaybackEventsRecorder::Create(
+ mojo::PendingReceiver<mojom::PlaybackEventsRecorder> receiver) {
+ mojo::MakeSelfOwnedReceiver(std::make_unique<FuchsiaPlaybackEventsRecorder>(),
+ std::move(receiver));
+}
+
+FuchsiaPlaybackEventsRecorder::FuchsiaPlaybackEventsRecorder() = default;
+FuchsiaPlaybackEventsRecorder::~FuchsiaPlaybackEventsRecorder() = default;
+
+void FuchsiaPlaybackEventsRecorder::OnPlaying() {
+ base::RecordComputedAction("WebEngine.Media.Playing");
+}
+
+void FuchsiaPlaybackEventsRecorder::OnPaused() {
+ base::RecordComputedAction("WebEngine.Media.Pause");
+ bitrate_estimator_.OnPause();
+}
+
+void FuchsiaPlaybackEventsRecorder::OnSeeking() {
+ buffering_state_ = BufferingState::kInitialBuffering;
+}
+
+void FuchsiaPlaybackEventsRecorder::OnEnded() {
+ base::RecordComputedAction("WebEngine.Media.Ended");
+}
+
+void FuchsiaPlaybackEventsRecorder::OnBuffering() {
+ DCHECK(buffering_state_ == BufferingState::kBuffered);
+
+ buffering_start_time_ = base::TimeTicks::Now();
+ buffering_state_ = BufferingState::kBuffering;
+
+ bitrate_estimator_.OnPause();
+}
+
+void FuchsiaPlaybackEventsRecorder::OnBufferingComplete() {
+ auto now = base::TimeTicks::Now();
+
+ if (buffering_state_ == BufferingState::kBuffering) {
+ base::TimeDelta time_between_buffering =
+ buffering_start_time_ - last_buffering_end_time_;
+ RecordEventWithValueAt("PlayTimeBeforeAutoPause",
+ time_between_buffering.InMilliseconds(), now);
+
+ base::TimeDelta buffering_user_time = now - buffering_start_time_;
+ RecordEventWithValueAt("AutoPauseTime",
+ buffering_user_time.InMilliseconds(), now);
+ }
+
+ buffering_state_ = BufferingState::kBuffered;
+ last_buffering_end_time_ = now;
+}
+
+void FuchsiaPlaybackEventsRecorder::OnError(PipelineStatus status) {
+ RecordEventWithValue("Error", status);
+}
+
+void FuchsiaPlaybackEventsRecorder::OnNaturalSizeChanged(
+ const gfx::Size& size) {
+ base::RecordComputedAction(base::StringPrintf(
+ "WebEngine.Media.VideoResolution:%dx%d", size.width(), size.height()));
+}
+
+void FuchsiaPlaybackEventsRecorder::OnPipelineStatistics(
+ const PipelineStatistics& stats) {
+ bitrate_estimator_.Update(stats);
+}
+
+} // namespace media
diff --git a/chromium/media/fuchsia/metrics/fuchsia_playback_events_recorder.h b/chromium/media/fuchsia/metrics/fuchsia_playback_events_recorder.h
new file mode 100644
index 00000000000..2686bafec9e
--- /dev/null
+++ b/chromium/media/fuchsia/metrics/fuchsia_playback_events_recorder.h
@@ -0,0 +1,70 @@
+// 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_FUCHSIA_METRICS_FUCHSIA_PLAYBACK_EVENTS_RECORDER_H_
+#define MEDIA_FUCHSIA_METRICS_FUCHSIA_PLAYBACK_EVENTS_RECORDER_H_
+
+#include "media/mojo/mojom/playback_events_recorder.mojom.h"
+#include "mojo/public/cpp/bindings/pending_remote.h"
+#include "mojo/public/cpp/bindings/remote.h"
+
+namespace media {
+
+class FuchsiaPlaybackEventsRecorder : public mojom::PlaybackEventsRecorder {
+ public:
+ static void Create(
+ mojo::PendingReceiver<mojom::PlaybackEventsRecorder> receiver);
+
+ FuchsiaPlaybackEventsRecorder();
+ ~FuchsiaPlaybackEventsRecorder() final;
+
+ FuchsiaPlaybackEventsRecorder(const FuchsiaPlaybackEventsRecorder&) = delete;
+ FuchsiaPlaybackEventsRecorder& operator=(
+ const FuchsiaPlaybackEventsRecorder&) = delete;
+
+ // mojom::PlaybackEventsRecorder implementation.
+ void OnPlaying() final;
+ void OnPaused() final;
+ void OnSeeking() final;
+ void OnEnded() final;
+ void OnBuffering() final;
+ void OnBufferingComplete() final;
+ void OnError(PipelineStatus status) final;
+ void OnNaturalSizeChanged(const gfx::Size& size) final;
+ void OnPipelineStatistics(const PipelineStatistics& stats) final;
+
+ private:
+ class BitrateEstimator {
+ public:
+ BitrateEstimator();
+ ~BitrateEstimator();
+
+ void Update(const PipelineStatistics& stats);
+ void OnPause();
+
+ private:
+ base::TimeDelta time_elapsed_;
+ size_t audio_bytes_ = 0;
+ size_t video_bytes_ = 0;
+
+ base::Optional<PipelineStatistics> last_stats_;
+ base::TimeTicks last_stats_time_;
+ };
+
+ enum class BufferingState {
+ kInitialBuffering,
+ kBuffering,
+ kBuffered,
+ };
+
+ BufferingState buffering_state_ = BufferingState::kInitialBuffering;
+ base::TimeTicks buffering_start_time_;
+ base::TimeTicks last_buffering_end_time_;
+
+ BitrateEstimator bitrate_estimator_;
+};
+
+} // namespace media
+
+#endif // MEDIA_FUCHSIA_METRICS_FUCHSIA_PLAYBACK_EVENTS_RECORDER_H_ \ No newline at end of file
diff --git a/chromium/media/fuchsia/metrics/fuchsia_playback_events_recorder_test.cc b/chromium/media/fuchsia/metrics/fuchsia_playback_events_recorder_test.cc
new file mode 100644
index 00000000000..a18704575a2
--- /dev/null
+++ b/chromium/media/fuchsia/metrics/fuchsia_playback_events_recorder_test.cc
@@ -0,0 +1,202 @@
+// 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/fuchsia/metrics/fuchsia_playback_events_recorder.h"
+
+#include "base/metrics/user_metrics.h"
+#include "base/test/simple_test_tick_clock.h"
+#include "base/test/task_environment.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+
+constexpr base::TimeDelta kSecond = base::TimeDelta::FromSeconds(1);
+
+class FuchsiaPlaybackEventsRecorderTest : public testing::Test {
+ public:
+ FuchsiaPlaybackEventsRecorderTest()
+ : task_environment_(base::test::TaskEnvironment::MainThreadType::IO,
+ base::test::TaskEnvironment::TimeSource::MOCK_TIME) {
+ time_base_ = base::TimeTicks::Now();
+
+ base::SetRecordActionTaskRunner(
+ task_environment_.GetMainThreadTaskRunner());
+ action_callback_ = base::BindRepeating(
+ &FuchsiaPlaybackEventsRecorderTest::OnAction, base::Unretained(this));
+ base::AddActionCallback(action_callback_);
+ }
+
+ ~FuchsiaPlaybackEventsRecorderTest() override {
+ base::RemoveActionCallback(action_callback_);
+ }
+
+ protected:
+ struct Event {
+ base::TimeTicks time;
+ std::string name;
+
+ bool operator==(const Event& other) const {
+ return time == other.time && name == other.name;
+ }
+ };
+
+ void OnAction(const std::string& name, base::TimeTicks time) {
+ recorded_events_.push_back({time, name});
+ }
+
+ void ExpectEvents(const std::vector<Event>& expected) {
+ EXPECT_EQ(recorded_events_.size(), expected.size());
+ size_t end = std::min(recorded_events_.size(), expected.size());
+ for (size_t i = 0; i < end; ++i) {
+ SCOPED_TRACE(i);
+ EXPECT_EQ(recorded_events_[i].time, expected[i].time);
+ EXPECT_EQ(recorded_events_[i].name, expected[i].name);
+ }
+ }
+
+ base::test::TaskEnvironment task_environment_;
+
+ base::SimpleTestTickClock test_clock_;
+ base::TimeTicks time_base_;
+
+ base::ActionCallback action_callback_;
+ FuchsiaPlaybackEventsRecorder recorder_;
+ std::vector<Event> recorded_events_;
+};
+
+TEST_F(FuchsiaPlaybackEventsRecorderTest, PlayPause) {
+ recorder_.OnNaturalSizeChanged(gfx::Size(640, 480));
+ recorder_.OnPlaying();
+ task_environment_.AdvanceClock(2 * kSecond);
+ recorder_.OnPaused();
+
+ ExpectEvents({
+ {time_base_, "WebEngine.Media.VideoResolution:640x480"},
+ {time_base_, "WebEngine.Media.Playing"},
+ {time_base_ + 2 * kSecond, "WebEngine.Media.Pause"},
+ });
+}
+
+TEST_F(FuchsiaPlaybackEventsRecorderTest, Error) {
+ recorder_.OnPlaying();
+ task_environment_.AdvanceClock(2 * kSecond);
+ recorder_.OnError(PIPELINE_ERROR_DECODE);
+
+ ExpectEvents({
+ {time_base_, "WebEngine.Media.Playing"},
+ {time_base_ + 2 * kSecond, "WebEngine.Media.Error:3"},
+ });
+}
+
+TEST_F(FuchsiaPlaybackEventsRecorderTest, Buffering) {
+ recorder_.OnPlaying();
+ recorder_.OnBufferingComplete();
+ task_environment_.AdvanceClock(2 * kSecond);
+ recorder_.OnBuffering();
+ task_environment_.AdvanceClock(3 * kSecond);
+ recorder_.OnBufferingComplete();
+
+ ExpectEvents({
+ {time_base_, "WebEngine.Media.Playing"},
+ {time_base_ + 5 * kSecond,
+ "WebEngine.Media.PlayTimeBeforeAutoPause:2000"},
+ {time_base_ + 5 * kSecond, "WebEngine.Media.AutoPauseTime:3000"},
+ });
+}
+
+TEST_F(FuchsiaPlaybackEventsRecorderTest, Bitrate) {
+ recorder_.OnPlaying();
+ recorder_.OnBufferingComplete();
+
+ PipelineStatistics stats;
+ recorder_.OnPipelineStatistics(stats);
+
+ for (int i = 0; i < 5; ++i) {
+ stats.audio_bytes_decoded += 5000;
+ stats.video_bytes_decoded += 10000;
+
+ task_environment_.AdvanceClock(kSecond);
+ recorder_.OnPipelineStatistics(stats);
+ }
+
+ ExpectEvents({
+ {time_base_, "WebEngine.Media.Playing"},
+ {time_base_ + 5 * kSecond, "WebEngine.Media.AudioBitrate:40"},
+ {time_base_ + 5 * kSecond, "WebEngine.Media.VideoBitrate:80"},
+ });
+}
+
+TEST_F(FuchsiaPlaybackEventsRecorderTest, BitrateAfterPause) {
+ recorder_.OnPlaying();
+ recorder_.OnBufferingComplete();
+
+ PipelineStatistics stats;
+ recorder_.OnPipelineStatistics(stats);
+
+ for (int i = 0; i < 3; ++i) {
+ stats.audio_bytes_decoded += 5000;
+ stats.video_bytes_decoded += 10000;
+
+ task_environment_.AdvanceClock(kSecond);
+ recorder_.OnPipelineStatistics(stats);
+ }
+
+ recorder_.OnPaused();
+ task_environment_.AdvanceClock(10 * kSecond);
+ recorder_.OnPlaying();
+
+ for (int i = 0; i < 3; ++i) {
+ stats.audio_bytes_decoded += 5000;
+ stats.video_bytes_decoded += 10000;
+
+ task_environment_.AdvanceClock(kSecond);
+ recorder_.OnPipelineStatistics(stats);
+ }
+
+ ExpectEvents({
+ {time_base_, "WebEngine.Media.Playing"},
+ {time_base_ + 3 * kSecond, "WebEngine.Media.Pause"},
+ {time_base_ + 13 * kSecond, "WebEngine.Media.Playing"},
+ {time_base_ + 16 * kSecond, "WebEngine.Media.AudioBitrate:40"},
+ {time_base_ + 16 * kSecond, "WebEngine.Media.VideoBitrate:80"},
+ });
+}
+
+TEST_F(FuchsiaPlaybackEventsRecorderTest, BitrateAfterBuffering) {
+ recorder_.OnPlaying();
+ recorder_.OnBufferingComplete();
+
+ PipelineStatistics stats;
+ recorder_.OnPipelineStatistics(stats);
+
+ for (int i = 0; i < 3; ++i) {
+ stats.audio_bytes_decoded += 5000;
+ stats.video_bytes_decoded += 10000;
+
+ task_environment_.AdvanceClock(kSecond);
+ recorder_.OnPipelineStatistics(stats);
+ }
+
+ recorder_.OnBuffering();
+ task_environment_.AdvanceClock(10 * kSecond);
+ recorder_.OnBufferingComplete();
+
+ for (int i = 0; i < 3; ++i) {
+ stats.audio_bytes_decoded += 5000;
+ stats.video_bytes_decoded += 10000;
+
+ task_environment_.AdvanceClock(kSecond);
+ recorder_.OnPipelineStatistics(stats);
+ }
+
+ ExpectEvents({
+ {time_base_, "WebEngine.Media.Playing"},
+ {time_base_ + 13 * kSecond,
+ "WebEngine.Media.PlayTimeBeforeAutoPause:3000"},
+ {time_base_ + 13 * kSecond, "WebEngine.Media.AutoPauseTime:10000"},
+ {time_base_ + 16 * kSecond, "WebEngine.Media.AudioBitrate:40"},
+ {time_base_ + 16 * kSecond, "WebEngine.Media.VideoBitrate:80"},
+ });
+}
+} // namespace media \ No newline at end of file
diff --git a/chromium/media/gpu/BUILD.gn b/chromium/media/gpu/BUILD.gn
index dbe714444da..767a3316092 100644
--- a/chromium/media/gpu/BUILD.gn
+++ b/chromium/media/gpu/BUILD.gn
@@ -109,6 +109,8 @@ component("gpu") {
"android/codec_image.h",
"android/codec_image_group.cc",
"android/codec_image_group.h",
+ "android/codec_output_buffer_renderer.cc",
+ "android/codec_output_buffer_renderer.h",
"android/codec_surface_bundle.cc",
"android/codec_surface_bundle.h",
"android/codec_wrapper.cc",
@@ -117,6 +119,8 @@ component("gpu") {
"android/device_info.h",
"android/direct_shared_image_video_provider.cc",
"android/direct_shared_image_video_provider.h",
+ "android/frame_info_helper.cc",
+ "android/frame_info_helper.h",
"android/maybe_render_early_manager.cc",
"android/maybe_render_early_manager.h",
"android/media_codec_video_decoder.cc",
@@ -133,8 +137,6 @@ component("gpu") {
"android/video_frame_factory.h",
"android/video_frame_factory_impl.cc",
"android/video_frame_factory_impl.h",
- "android/ycbcr_helper.cc",
- "android/ycbcr_helper.h",
]
libs += [ "android" ]
deps += [
@@ -198,6 +200,8 @@ component("gpu") {
"windows/dxva_picture_buffer_win.h",
"windows/dxva_video_decode_accelerator_win.cc",
"windows/dxva_video_decode_accelerator_win.h",
+ "windows/hresult_status_debug_device.cc",
+ "windows/hresult_status_debug_device.h",
"windows/init_guid.cc",
"windows/media_foundation_video_encode_accelerator_win.cc",
"windows/media_foundation_video_encode_accelerator_win.h",
@@ -211,6 +215,7 @@ component("gpu") {
]
public_deps += [ "//media/base/win:media_foundation_util" ]
deps += [
+ "//media/base/win:hresult_status_helper",
"//media/parsers",
"//third_party/angle:includes",
"//ui/display",
@@ -231,14 +236,6 @@ component("gpu") {
"/DELAYLOAD:mf.dll",
"/DELAYLOAD:mfplat.dll",
]
- if (enable_library_cdms) {
- sources += [
- "windows/d3d11_cdm_proxy.cc",
- "windows/d3d11_cdm_proxy.h",
- "windows/d3d11_decryptor.cc",
- "windows/d3d11_decryptor.h",
- ]
- }
}
if (use_ozone) {
@@ -485,9 +482,11 @@ source_set("unit_tests") {
"//base/test:test_support",
"//media:test_support",
"//media/gpu",
+ "//media/gpu:test_support",
"//media/gpu/ipc/service:unit_tests",
"//testing/gmock",
"//testing/gtest",
+ "//ui/gl:test_support",
]
sources = [ "h264_decoder_unittest.cc" ]
@@ -507,11 +506,10 @@ source_set("unit_tests") {
}
if (is_win && enable_library_cdms) {
sources += [
- "windows/d3d11_cdm_proxy_unittest.cc",
"windows/d3d11_copying_texture_wrapper_unittest.cc",
"windows/d3d11_decoder_configurator_unittest.cc",
- "windows/d3d11_decryptor_unittest.cc",
"windows/d3d11_texture_selector_unittest.cc",
+ "windows/d3d11_texture_wrapper_unittest.cc",
"windows/d3d11_video_decoder_unittest.cc",
"windows/d3d11_video_device_format_support_unittest.cc",
"windows/d3d11_video_processor_proxy_unittest.cc",
diff --git a/chromium/media/gpu/OWNERS b/chromium/media/gpu/OWNERS
index 18fb4fa7ad2..2b325453c62 100644
--- a/chromium/media/gpu/OWNERS
+++ b/chromium/media/gpu/OWNERS
@@ -7,6 +7,7 @@ sandersd@chromium.org
acourbot@chromium.org
hiroh@chromium.org
jcliang@chromium.org
+jkardatzke@chromium.org
kcwu@chromium.org
mcasas@chromium.org
posciak@chromium.org
diff --git a/chromium/media/gpu/android/android_video_surface_chooser_impl_unittest.cc b/chromium/media/gpu/android/android_video_surface_chooser_impl_unittest.cc
index 0cc3adf01e6..9f5eff7e972 100644
--- a/chromium/media/gpu/android/android_video_surface_chooser_impl_unittest.cc
+++ b/chromium/media/gpu/android/android_video_surface_chooser_impl_unittest.cc
@@ -9,7 +9,6 @@
#include <memory>
#include "base/bind.h"
-#include "base/logging.h"
#include "base/test/simple_test_tick_clock.h"
#include "media/base/android/mock_android_overlay.h"
#include "testing/gmock/include/gmock/gmock.h"
diff --git a/chromium/media/gpu/android/codec_allocator_unittest.cc b/chromium/media/gpu/android/codec_allocator_unittest.cc
index 90d25b8ccaf..439a310fcd4 100644
--- a/chromium/media/gpu/android/codec_allocator_unittest.cc
+++ b/chromium/media/gpu/android/codec_allocator_unittest.cc
@@ -10,7 +10,7 @@
#include "base/bind.h"
#include "base/bind_helpers.h"
-#include "base/logging.h"
+#include "base/check.h"
#include "base/single_thread_task_runner.h"
#include "base/test/simple_test_tick_clock.h"
#include "base/test/task_environment.h"
diff --git a/chromium/media/gpu/android/codec_image.cc b/chromium/media/gpu/android/codec_image.cc
index 187fb240c74..ae3a90da5c9 100644
--- a/chromium/media/gpu/android/codec_image.cc
+++ b/chromium/media/gpu/android/codec_image.cc
@@ -16,45 +16,19 @@
#include "ui/gl/scoped_make_current.h"
namespace media {
-namespace {
-
-// Makes |texture_owner|'s context current if it isn't already.
-std::unique_ptr<ui::ScopedMakeCurrent> MakeCurrentIfNeeded(
- gpu::TextureOwner* texture_owner) {
- gl::GLContext* context = texture_owner->GetContext();
- // Note: this works for virtual contexts too, because IsCurrent() returns true
- // if their shared platform context is current, regardless of which virtual
- // context is current.
- if (context->IsCurrent(nullptr))
- return nullptr;
-
- auto scoped_current = std::make_unique<ui::ScopedMakeCurrent>(
- context, texture_owner->GetSurface());
- // Log an error if ScopedMakeCurrent failed for debugging
- // https://crbug.com/878042.
- // TODO(ericrk): Remove this once debugging is completed.
- if (!context->IsCurrent(nullptr)) {
- LOG(ERROR) << "Failed to make context current in CodecImage. Subsequent "
- "UpdateTexImage may fail.";
- }
- return scoped_current;
-}
-
-} // namespace
-CodecImage::CodecImage() = default;
+CodecImage::CodecImage(const gfx::Size& coded_size) : coded_size_(coded_size) {}
CodecImage::~CodecImage() {
NotifyUnused();
}
void CodecImage::Initialize(
- std::unique_ptr<CodecOutputBuffer> output_buffer,
+ std::unique_ptr<CodecOutputBufferRenderer> output_buffer_renderer,
scoped_refptr<CodecBufferWaitCoordinator> codec_buffer_wait_coordinator,
PromotionHintAggregator::NotifyPromotionHintCB promotion_hint_cb) {
- DCHECK(output_buffer);
- phase_ = Phase::kInCodec;
- output_buffer_ = std::move(output_buffer);
+ DCHECK(output_buffer_renderer);
+ output_buffer_renderer_ = std::move(output_buffer_renderer);
codec_buffer_wait_coordinator_ = std::move(codec_buffer_wait_coordinator);
promotion_hint_cb_ = std::move(promotion_hint_cb);
}
@@ -77,10 +51,7 @@ void CodecImage::NotifyUnused() {
}
gfx::Size CodecImage::GetSize() {
- // Return a nonzero size, to avoid GL errors, even if we dropped the codec
- // buffer already. Note that if we dropped it, there's no data in the
- // texture anyway, so the old size doesn't matter.
- return output_buffer_ ? output_buffer_->size() : gfx::Size(1, 1);
+ return coded_size_;
}
unsigned CodecImage::GetInternalFormat() {
@@ -119,7 +90,11 @@ bool CodecImage::CopyTexImage(unsigned target) {
codec_buffer_wait_coordinator_->texture_owner()->GetTextureId()))
return false;
- RenderToTextureOwnerFrontBuffer(BindingsMode::kEnsureTexImageBound);
+ if (!output_buffer_renderer_)
+ return true;
+
+ output_buffer_renderer_->RenderToTextureOwnerFrontBuffer(
+ BindingsMode::kEnsureTexImageBound);
return true;
}
@@ -183,23 +158,13 @@ void CodecImage::OnMemoryDump(base::trace_event::ProcessMemoryDump* pmd,
const std::string& dump_name) {}
void CodecImage::GetTextureMatrix(float matrix[16]) {
- // Default to identity.
- static constexpr float kYInvertedIdentity[16]{
- 1, 0, 0, 0, //
- 0, -1, 0, 0, //
- 0, 0, 1, 0, //
- 0, 1, 0, 1 //
+ static constexpr float kIdentity[16]{
+ 1, 0, 0, 0, //
+ 0, 1, 0, 0, //
+ 0, 0, 1, 0, //
+ 0, 0, 0, 1 //
};
- memcpy(matrix, kYInvertedIdentity, sizeof(kYInvertedIdentity));
- if (!codec_buffer_wait_coordinator_)
- return;
-
- // The matrix is available after we render to the front buffer. If that fails
- // we'll return the matrix from the previous frame, which is more likely to be
- // correct than the identity matrix anyway.
- RenderToTextureOwnerFrontBuffer(BindingsMode::kDontRestoreIfBound);
- codec_buffer_wait_coordinator_->texture_owner()->GetTransformMatrix(matrix);
- YInvertMatrix(matrix);
+ memcpy(matrix, kIdentity, sizeof(kIdentity));
}
void CodecImage::NotifyPromotionHint(bool promotion_hint,
@@ -222,8 +187,10 @@ void CodecImage::ReleaseResources() {
}
bool CodecImage::IsUsingGpuMemory() const {
+ if (!output_buffer_renderer_)
+ return false;
// Only the images which are bound to texture accounts for gpu memory.
- return was_tex_image_bound_;
+ return output_buffer_renderer_->was_tex_image_bound();
}
void CodecImage::UpdateAndBindTexImage() {
@@ -239,120 +206,33 @@ gpu::TextureBase* CodecImage::GetTextureBase() const {
}
bool CodecImage::RenderToFrontBuffer() {
- // This code is used to trigger early rendering of the image before it is used
- // for compositing, there is no need to bind the image.
- return codec_buffer_wait_coordinator_
- ? RenderToTextureOwnerFrontBuffer(BindingsMode::kRestoreIfBound)
- : RenderToOverlay();
+ if (!output_buffer_renderer_)
+ return false;
+ return output_buffer_renderer_->RenderToFrontBuffer();
}
bool CodecImage::RenderToTextureOwnerBackBuffer(BlockingMode blocking_mode) {
- DCHECK_NE(phase_, Phase::kInFrontBuffer);
- if (phase_ == Phase::kInBackBuffer)
- return true;
- if (phase_ == Phase::kInvalidated)
- return false;
-
- // Normally, we should have a wait coordinator if we're called. However, if
- // the renderer is torn down (either VideoFrameSubmitter or the whole process)
- // before we get returns back from viz, then we can be notified that we're
- // no longer in use (erroneously) when the VideoFrame is destroyed. So, if
- // we don't have a wait coordinator, then just fail.
- if (!codec_buffer_wait_coordinator_)
+ if (!output_buffer_renderer_)
return false;
- // Wait for a previous frame available so we don't confuse it with the one
- // we're about to release.
- if (codec_buffer_wait_coordinator_->IsExpectingFrameAvailable()) {
- if (blocking_mode == BlockingMode::kForbidBlocking)
- return false;
- codec_buffer_wait_coordinator_->WaitForFrameAvailable();
- }
- if (!output_buffer_->ReleaseToSurface()) {
- phase_ = Phase::kInvalidated;
- return false;
- }
- phase_ = Phase::kInBackBuffer;
- codec_buffer_wait_coordinator_->SetReleaseTimeToNow();
- return true;
+ return output_buffer_renderer_->RenderToTextureOwnerBackBuffer(blocking_mode);
}
bool CodecImage::RenderToTextureOwnerFrontBuffer(BindingsMode bindings_mode) {
- // Normally, we should have a wait coordinator if we're called. However, if
- // the renderer is torn down (either VideoFrameSubmitter or the whole process)
- // before we get returns back from viz, then we can be notified that we're
- // no longer in use (erroneously) when the VideoFrame is destroyed. So, if
- // we don't have a wait coordinator, then just fail.
- if (!codec_buffer_wait_coordinator_)
+ if (!output_buffer_renderer_)
return false;
-
- if (phase_ == Phase::kInFrontBuffer) {
- EnsureBoundIfNeeded(bindings_mode);
- return true;
- }
- if (phase_ == Phase::kInvalidated)
- return false;
-
- // Render it to the back buffer if it's not already there.
- if (!RenderToTextureOwnerBackBuffer())
- return false;
-
- // The image is now in the back buffer, so promote it to the front buffer.
- phase_ = Phase::kInFrontBuffer;
- if (codec_buffer_wait_coordinator_->IsExpectingFrameAvailable())
- codec_buffer_wait_coordinator_->WaitForFrameAvailable();
-
- std::unique_ptr<ui::ScopedMakeCurrent> scoped_make_current =
- MakeCurrentIfNeeded(
- codec_buffer_wait_coordinator_->texture_owner().get());
- // If updating the image will implicitly update the texture bindings then
- // restore if requested or the update needed a context switch.
- bool should_restore_bindings =
- codec_buffer_wait_coordinator_->texture_owner()
- ->binds_texture_on_update() &&
- (bindings_mode == BindingsMode::kRestoreIfBound || !!scoped_make_current);
-
- GLint bound_service_id = 0;
- if (should_restore_bindings)
- glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &bound_service_id);
- codec_buffer_wait_coordinator_->texture_owner()->UpdateTexImage();
- EnsureBoundIfNeeded(bindings_mode);
- if (should_restore_bindings)
- glBindTexture(GL_TEXTURE_EXTERNAL_OES, bound_service_id);
- return true;
-}
-
-void CodecImage::EnsureBoundIfNeeded(BindingsMode mode) {
- DCHECK(codec_buffer_wait_coordinator_);
-
- if (codec_buffer_wait_coordinator_->texture_owner()
- ->binds_texture_on_update()) {
- was_tex_image_bound_ = true;
- return;
- }
- if (mode != BindingsMode::kEnsureTexImageBound)
- return;
- codec_buffer_wait_coordinator_->texture_owner()->EnsureTexImageBound();
- was_tex_image_bound_ = true;
+ return output_buffer_renderer_->RenderToTextureOwnerFrontBuffer(
+ bindings_mode);
}
bool CodecImage::RenderToOverlay() {
- if (phase_ == Phase::kInFrontBuffer)
- return true;
- if (phase_ == Phase::kInvalidated)
+ if (!output_buffer_renderer_)
return false;
-
- if (!output_buffer_->ReleaseToSurface()) {
- phase_ = Phase::kInvalidated;
- return false;
- }
- phase_ = Phase::kInFrontBuffer;
- return true;
+ return output_buffer_renderer_->RenderToOverlay();
}
void CodecImage::ReleaseCodecBuffer() {
- output_buffer_ = nullptr;
- phase_ = Phase::kInvalidated;
+ output_buffer_renderer_.reset();
}
std::unique_ptr<base::android::ScopedHardwareBufferFenceSync>
@@ -369,9 +249,7 @@ CodecImage::GetAHardwareBuffer() {
}
gfx::Rect CodecImage::GetCropRect() {
- if (!codec_buffer_wait_coordinator_)
- return gfx::Rect();
- return codec_buffer_wait_coordinator_->texture_owner()->GetCropRect();
+ return gfx::Rect();
}
bool CodecImage::HasMutableState() const {
diff --git a/chromium/media/gpu/android/codec_image.h b/chromium/media/gpu/android/codec_image.h
index 61861825e4b..8693118f918 100644
--- a/chromium/media/gpu/android/codec_image.h
+++ b/chromium/media/gpu/android/codec_image.h
@@ -16,7 +16,7 @@
#include "gpu/command_buffer/service/gl_stream_texture_image.h"
#include "gpu/command_buffer/service/stream_texture_shared_image_interface.h"
#include "media/gpu/android/codec_buffer_wait_coordinator.h"
-#include "media/gpu/android/codec_wrapper.h"
+#include "media/gpu/android/codec_output_buffer_renderer.h"
#include "media/gpu/android/promotion_hint_aggregator.h"
#include "media/gpu/media_gpu_export.h"
@@ -33,8 +33,7 @@ namespace media {
class MEDIA_GPU_EXPORT CodecImage
: public gpu::StreamTextureSharedImageInterface {
public:
- // Whether RenderToTextureOwnerBackBuffer may block or not.
- enum class BlockingMode { kForbidBlocking, kAllowBlocking };
+ using BlockingMode = CodecOutputBufferRenderer::BlockingMode;
// Callback to notify that a codec image is now unused in the sense of not
// being out for display. This lets us signal interested folks once a video
@@ -47,14 +46,14 @@ class MEDIA_GPU_EXPORT CodecImage
// destroying it.
using UnusedCB = base::OnceCallback<void(CodecImage*)>;
- CodecImage();
+ CodecImage(const gfx::Size& coded_size);
// (Re-)Initialize this CodecImage to use |output_buffer| et. al.
//
// May be called on a random thread, but only if the CodecImage is otherwise
// not in use.
void Initialize(
- std::unique_ptr<CodecOutputBuffer> output_buffer,
+ std::unique_ptr<CodecOutputBufferRenderer> output_buffer_renderer,
scoped_refptr<CodecBufferWaitCoordinator> codec_buffer_wait_coordinator,
PromotionHintAggregator::NotifyPromotionHintCB promotion_hint_cb);
@@ -123,12 +122,11 @@ class MEDIA_GPU_EXPORT CodecImage
// Whether the codec buffer has been rendered to the front buffer.
bool was_rendered_to_front_buffer() const {
- return phase_ == Phase::kInFrontBuffer;
+ return output_buffer_renderer_
+ ? output_buffer_renderer_->was_rendered_to_front_buffer()
+ : false;
}
- // Whether the TextureOwner's texture is in the front buffer and bound to the
- // latest image.
- bool was_tex_image_bound() const { return was_tex_image_bound_; }
// Whether this image is backed by a texture owner.
// We want to check for texture_owner owned by
@@ -164,7 +162,9 @@ class MEDIA_GPU_EXPORT CodecImage
virtual void ReleaseCodecBuffer();
CodecOutputBuffer* get_codec_output_buffer_for_testing() const {
- return output_buffer_.get();
+ return output_buffer_renderer_
+ ? output_buffer_renderer_->get_codec_output_buffer_for_testing()
+ : nullptr;
}
protected:
@@ -173,22 +173,12 @@ class MEDIA_GPU_EXPORT CodecImage
private:
FRIEND_TEST_ALL_PREFIXES(CodecImageTest, RenderAfterUnusedDoesntCrash);
- // The lifecycle phases of an image.
- // The only possible transitions are from left to right. Both
- // kInFrontBuffer and kInvalidated are terminal.
- enum class Phase { kInCodec, kInBackBuffer, kInFrontBuffer, kInvalidated };
+ std::unique_ptr<CodecOutputBufferRenderer> output_buffer_renderer_;
// Renders this image to the texture owner front buffer by first rendering
// it to the back buffer if it's not already there, and then waiting for the
// frame available event before calling UpdateTexImage().
bool RenderToTextureOwnerFrontBuffer(BindingsMode bindings_mode);
- void EnsureBoundIfNeeded(BindingsMode mode);
-
- // The phase of the image buffer's lifecycle.
- Phase phase_ = Phase::kInvalidated;
-
- // The buffer backing this image.
- std::unique_ptr<CodecOutputBuffer> output_buffer_;
// The CodecBufferWaitCoordinator that |output_buffer_| will be rendered to.
// Or null, if this image is backed by an overlay.
@@ -197,13 +187,14 @@ class MEDIA_GPU_EXPORT CodecImage
// The bounds last sent to the overlay.
gfx::Rect most_recent_bounds_;
+ // Coded size of the image.
+ gfx::Size coded_size_;
+
// Callback to notify about promotion hints and overlay position.
PromotionHintAggregator::NotifyPromotionHintCB promotion_hint_cb_;
std::vector<UnusedCB> unused_cbs_;
- bool was_tex_image_bound_ = false;
-
DISALLOW_COPY_AND_ASSIGN(CodecImage);
};
diff --git a/chromium/media/gpu/android/codec_image_group_unittest.cc b/chromium/media/gpu/android/codec_image_group_unittest.cc
index 00cef1d3002..22c72c7ab9c 100644
--- a/chromium/media/gpu/android/codec_image_group_unittest.cc
+++ b/chromium/media/gpu/android/codec_image_group_unittest.cc
@@ -20,6 +20,9 @@
namespace media {
namespace {
+// Size used to create MockCodecImage.
+constexpr gfx::Size kMockImageSize(100, 100);
+
// Subclass of CodecImageGroup which will notify us when it's destroyed.
class CodecImageGroupWithDestructionHook : public CodecImageGroup {
public:
@@ -119,7 +122,7 @@ TEST_F(CodecImageGroupTest, ImagesRetainRefToGroup) {
bool was_destroyed = false;
rec.image_group->SetDestructionCallback(
base::BindOnce([](bool* flag) -> void { *flag = true; }, &was_destroyed));
- scoped_refptr<CodecImage> image = new MockCodecImage();
+ scoped_refptr<CodecImage> image = new MockCodecImage(kMockImageSize);
// We're supposed to call this from |gpu_task_runner_|, but all
// CodecImageGroup really cares about is being single sequence.
rec.image_group->AddCodecImage(image.get());
@@ -138,8 +141,8 @@ TEST_F(CodecImageGroupTest, ImageGroupDropsForwardsSurfaceDestruction) {
// also verify that the image group drops its ref to the surface bundle, so
// that it doesn't prevent destruction of the overlay that provided it.
Record rec = CreateImageGroup();
- scoped_refptr<MockCodecImage> image_1 = new MockCodecImage();
- scoped_refptr<MockCodecImage> image_2 = new MockCodecImage();
+ scoped_refptr<MockCodecImage> image_1 = new MockCodecImage(kMockImageSize);
+ scoped_refptr<MockCodecImage> image_2 = new MockCodecImage(kMockImageSize);
rec.image_group->AddCodecImage(image_1.get());
rec.image_group->AddCodecImage(image_2.get());
diff --git a/chromium/media/gpu/android/codec_image_unittest.cc b/chromium/media/gpu/android/codec_image_unittest.cc
index 840cf24f092..7fcd7eeba13 100644
--- a/chromium/media/gpu/android/codec_image_unittest.cc
+++ b/chromium/media/gpu/android/codec_image_unittest.cc
@@ -6,7 +6,6 @@
#include "base/bind.h"
#include "base/bind_helpers.h"
-#include "base/logging.h"
#include "base/test/mock_callback.h"
#include "base/test/task_environment.h"
#include "base/threading/sequenced_task_runner_handle.h"
@@ -87,10 +86,15 @@ class CodecImageTest : public testing::Test {
CodecImage::UnusedCB unused_cb = base::DoNothing()) {
std::unique_ptr<CodecOutputBuffer> buffer;
wrapper_->DequeueOutputBuffer(nullptr, nullptr, &buffer);
- scoped_refptr<CodecImage> image = new CodecImage();
+
+ auto codec_buffer_wait_coordinator =
+ kind == kTextureOwner ? codec_buffer_wait_coordinator_ : nullptr;
+ auto buffer_renderer = std::make_unique<CodecOutputBufferRenderer>(
+ std::move(buffer), codec_buffer_wait_coordinator);
+
+ scoped_refptr<CodecImage> image = new CodecImage(buffer_renderer->size());
image->Initialize(
- std::move(buffer),
- kind == kTextureOwner ? codec_buffer_wait_coordinator_ : nullptr,
+ std::move(buffer_renderer), codec_buffer_wait_coordinator,
base::BindRepeating(&PromotionHintReceiver::OnPromotionHint,
base::Unretained(&promotion_hint_receiver_)));
@@ -217,48 +221,6 @@ TEST_F(CodecImageTestExplicitBind, CopyTexImageTriggersFrontBufferRendering) {
ASSERT_TRUE(i->was_rendered_to_front_buffer());
}
-TEST_F(CodecImageTest, GetTextureMatrixTriggersFrontBufferRendering) {
- auto i = NewImage(kTextureOwner);
- InSequence s;
- EXPECT_CALL(*codec_, ReleaseOutputBuffer(_, true));
- EXPECT_CALL(*codec_buffer_wait_coordinator_, WaitForFrameAvailable());
- EXPECT_CALL(*codec_buffer_wait_coordinator_->texture_owner(),
- UpdateTexImage());
- EXPECT_CALL(*codec_buffer_wait_coordinator_->texture_owner(),
- GetTransformMatrix(_));
- float matrix[16];
- i->GetTextureMatrix(matrix);
- ASSERT_TRUE(i->was_rendered_to_front_buffer());
-}
-
-TEST_F(CodecImageTestExplicitBind,
- GetTextureMatrixTriggersFrontBufferRendering) {
- // GetTextureMatrix should not bind the image.
- codec_buffer_wait_coordinator_->texture_owner()->expect_update_tex_image =
- false;
-
- auto i = NewImage(kTextureOwner);
- InSequence s;
- EXPECT_CALL(*codec_, ReleaseOutputBuffer(_, true));
- EXPECT_CALL(*codec_buffer_wait_coordinator_, WaitForFrameAvailable());
- EXPECT_CALL(*codec_buffer_wait_coordinator_->texture_owner(),
- UpdateTexImage());
- EXPECT_CALL(*codec_buffer_wait_coordinator_->texture_owner(),
- GetTransformMatrix(_));
- float matrix[16];
- i->GetTextureMatrix(matrix);
- ASSERT_TRUE(i->was_rendered_to_front_buffer());
-}
-
-TEST_F(CodecImageTest, GetTextureMatrixReturnsIdentityForOverlayImages) {
- auto i = NewImage(kOverlay);
- float matrix[16]{0};
- i->GetTextureMatrix(matrix);
- // See GetTextureMatrix() for the expected result.
- ASSERT_EQ(matrix[0], 1);
- ASSERT_EQ(matrix[5], -1);
-}
-
TEST_F(CodecImageTest, ScheduleOverlayPlaneTriggersFrontBufferRendering) {
auto i = NewImage(kOverlay);
EXPECT_CALL(*codec_, ReleaseOutputBuffer(_, true));
@@ -413,15 +375,6 @@ TEST_F(CodecImageTest, GetAHardwareBufferAfterRelease) {
EXPECT_FALSE(i->GetAHardwareBuffer());
}
-TEST_F(CodecImageTest, GetCropRect) {
- auto i = NewImage(kTextureOwner);
- EXPECT_EQ(
- codec_buffer_wait_coordinator_->texture_owner()->get_crop_rect_count, 0);
- i->GetCropRect();
- EXPECT_EQ(
- codec_buffer_wait_coordinator_->texture_owner()->get_crop_rect_count, 1);
-}
-
TEST_F(CodecImageTest, RenderAfterUnusedDoesntCrash) {
auto i = NewImage(kTextureOwner);
i->NotifyUnused();
@@ -430,4 +383,20 @@ TEST_F(CodecImageTest, RenderAfterUnusedDoesntCrash) {
CodecImage::BindingsMode::kEnsureTexImageBound));
}
+TEST_F(CodecImageTest, CodedSizeVsVisibleSize) {
+ const gfx::Size coded_size(128, 128);
+ const gfx::Size visible_size(100, 100);
+ auto buffer = CodecOutputBuffer::CreateForTesting(0, visible_size);
+ auto buffer_renderer =
+ std::make_unique<CodecOutputBufferRenderer>(std::move(buffer), nullptr);
+
+ scoped_refptr<CodecImage> image = new CodecImage(coded_size);
+ image->Initialize(std::move(buffer_renderer), nullptr,
+ PromotionHintAggregator::NotifyPromotionHintCB());
+
+ // Verify that CodecImage::GetSize returns coded_size and not visible_size
+ // that comes in CodecOutputBuffer size.
+ EXPECT_EQ(image->GetSize(), coded_size);
+}
+
} // namespace media
diff --git a/chromium/media/gpu/android/codec_output_buffer_renderer.cc b/chromium/media/gpu/android/codec_output_buffer_renderer.cc
new file mode 100644
index 00000000000..57f32eabb24
--- /dev/null
+++ b/chromium/media/gpu/android/codec_output_buffer_renderer.cc
@@ -0,0 +1,166 @@
+// 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/gpu/android/codec_output_buffer_renderer.h"
+#include <string.h>
+
+#include "base/android/scoped_hardware_buffer_fence_sync.h"
+#include "base/bind_helpers.h"
+#include "gpu/command_buffer/service/gles2_cmd_decoder.h"
+#include "gpu/command_buffer/service/texture_manager.h"
+#include "ui/gl/gl_context.h"
+#include "ui/gl/scoped_make_current.h"
+
+namespace media {
+namespace {
+
+// Makes |texture_owner|'s context current if it isn't already.
+std::unique_ptr<ui::ScopedMakeCurrent> MakeCurrentIfNeeded(
+ gpu::TextureOwner* texture_owner) {
+ gl::GLContext* context = texture_owner->GetContext();
+ // Note: this works for virtual contexts too, because IsCurrent() returns true
+ // if their shared platform context is current, regardless of which virtual
+ // context is current.
+ if (context->IsCurrent(nullptr))
+ return nullptr;
+
+ auto scoped_current = std::make_unique<ui::ScopedMakeCurrent>(
+ context, texture_owner->GetSurface());
+ // Log an error if ScopedMakeCurrent failed for debugging
+ // https://crbug.com/878042.
+ // TODO(ericrk): Remove this once debugging is completed.
+ if (!context->IsCurrent(nullptr)) {
+ LOG(ERROR) << "Failed to make context current in CodecImage. Subsequent "
+ "UpdateTexImage may fail.";
+ }
+ return scoped_current;
+}
+
+} // namespace
+
+CodecOutputBufferRenderer::CodecOutputBufferRenderer(
+ std::unique_ptr<CodecOutputBuffer> output_buffer,
+ scoped_refptr<CodecBufferWaitCoordinator> codec_buffer_wait_coordinator)
+ : output_buffer_(std::move(output_buffer)),
+ codec_buffer_wait_coordinator_(std::move(codec_buffer_wait_coordinator)) {
+
+}
+
+CodecOutputBufferRenderer::~CodecOutputBufferRenderer() = default;
+
+bool CodecOutputBufferRenderer::RenderToTextureOwnerBackBuffer(
+ BlockingMode blocking_mode) {
+ DCHECK_NE(phase_, Phase::kInFrontBuffer);
+ if (phase_ == Phase::kInBackBuffer)
+ return true;
+ if (phase_ == Phase::kInvalidated)
+ return false;
+
+ // Normally, we should have a wait coordinator if we're called. However, if
+ // the renderer is torn down (either VideoFrameSubmitter or the whole process)
+ // before we get returns back from viz, then we can be notified that we're
+ // no longer in use (erroneously) when the VideoFrame is destroyed. So, if
+ // we don't have a wait coordinator, then just fail.
+ if (!codec_buffer_wait_coordinator_)
+ return false;
+
+ // Wait for a previous frame available so we don't confuse it with the one
+ // we're about to release.
+ if (codec_buffer_wait_coordinator_->IsExpectingFrameAvailable()) {
+ if (blocking_mode == BlockingMode::kForbidBlocking)
+ return false;
+ codec_buffer_wait_coordinator_->WaitForFrameAvailable();
+ }
+ if (!output_buffer_->ReleaseToSurface()) {
+ phase_ = Phase::kInvalidated;
+ return false;
+ }
+ phase_ = Phase::kInBackBuffer;
+ codec_buffer_wait_coordinator_->SetReleaseTimeToNow();
+ return true;
+}
+
+bool CodecOutputBufferRenderer::RenderToTextureOwnerFrontBuffer(
+ BindingsMode bindings_mode) {
+ // Normally, we should have a wait coordinator if we're called. However, if
+ // the renderer is torn down (either VideoFrameSubmitter or the whole process)
+ // before we get returns back from viz, then we can be notified that we're
+ // no longer in use (erroneously) when the VideoFrame is destroyed. So, if
+ // we don't have a wait coordinator, then just fail.
+ if (!codec_buffer_wait_coordinator_)
+ return false;
+
+ if (phase_ == Phase::kInFrontBuffer) {
+ EnsureBoundIfNeeded(bindings_mode);
+ return true;
+ }
+ if (phase_ == Phase::kInvalidated)
+ return false;
+
+ // Render it to the back buffer if it's not already there.
+ if (!RenderToTextureOwnerBackBuffer())
+ return false;
+
+ // The image is now in the back buffer, so promote it to the front buffer.
+ phase_ = Phase::kInFrontBuffer;
+ if (codec_buffer_wait_coordinator_->IsExpectingFrameAvailable())
+ codec_buffer_wait_coordinator_->WaitForFrameAvailable();
+
+ std::unique_ptr<ui::ScopedMakeCurrent> scoped_make_current =
+ MakeCurrentIfNeeded(
+ codec_buffer_wait_coordinator_->texture_owner().get());
+ // If updating the image will implicitly update the texture bindings then
+ // restore if requested or the update needed a context switch.
+ bool should_restore_bindings =
+ codec_buffer_wait_coordinator_->texture_owner()
+ ->binds_texture_on_update() &&
+ (bindings_mode == BindingsMode::kRestoreIfBound || !!scoped_make_current);
+
+ GLint bound_service_id = 0;
+ if (should_restore_bindings)
+ glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &bound_service_id);
+ codec_buffer_wait_coordinator_->texture_owner()->UpdateTexImage();
+ EnsureBoundIfNeeded(bindings_mode);
+ if (should_restore_bindings)
+ glBindTexture(GL_TEXTURE_EXTERNAL_OES, bound_service_id);
+ return true;
+}
+
+void CodecOutputBufferRenderer::EnsureBoundIfNeeded(BindingsMode mode) {
+ DCHECK(codec_buffer_wait_coordinator_);
+
+ if (codec_buffer_wait_coordinator_->texture_owner()
+ ->binds_texture_on_update()) {
+ was_tex_image_bound_ = true;
+ return;
+ }
+ if (mode != BindingsMode::kEnsureTexImageBound)
+ return;
+ codec_buffer_wait_coordinator_->texture_owner()->EnsureTexImageBound();
+ was_tex_image_bound_ = true;
+}
+
+bool CodecOutputBufferRenderer::RenderToOverlay() {
+ if (phase_ == Phase::kInFrontBuffer)
+ return true;
+ if (phase_ == Phase::kInvalidated)
+ return false;
+
+ if (!output_buffer_->ReleaseToSurface()) {
+ phase_ = Phase::kInvalidated;
+ return false;
+ }
+ phase_ = Phase::kInFrontBuffer;
+ return true;
+}
+
+bool CodecOutputBufferRenderer::RenderToFrontBuffer() {
+ // This code is used to trigger early rendering of the image before it is used
+ // for compositing, there is no need to bind the image.
+ return codec_buffer_wait_coordinator_
+ ? RenderToTextureOwnerFrontBuffer(BindingsMode::kRestoreIfBound)
+ : RenderToOverlay();
+}
+
+} // namespace media
diff --git a/chromium/media/gpu/android/codec_output_buffer_renderer.h b/chromium/media/gpu/android/codec_output_buffer_renderer.h
new file mode 100644
index 00000000000..69e6ea73121
--- /dev/null
+++ b/chromium/media/gpu/android/codec_output_buffer_renderer.h
@@ -0,0 +1,102 @@
+// 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_GPU_ANDROID_CODEC_OUTPUT_BUFFER_RENDERER_H_
+#define MEDIA_GPU_ANDROID_CODEC_OUTPUT_BUFFER_RENDERER_H_
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "gpu/command_buffer/service/stream_texture_shared_image_interface.h"
+#include "media/gpu/android/codec_buffer_wait_coordinator.h"
+#include "media/gpu/android/codec_wrapper.h"
+#include "media/gpu/media_gpu_export.h"
+
+namespace media {
+
+// A class that holds CodecOutputBuffer and renders it to TextureOwner or
+// overlay as necessary. Unit tests for this class are part of CodecImage unit
+// tests.
+class MEDIA_GPU_EXPORT CodecOutputBufferRenderer {
+ public:
+ using BindingsMode = gpu::StreamTextureSharedImageInterface::BindingsMode;
+ // Whether RenderToTextureOwnerBackBuffer may block or not.
+ enum class BlockingMode { kForbidBlocking, kAllowBlocking };
+
+ CodecOutputBufferRenderer(
+ std::unique_ptr<CodecOutputBuffer> output_buffer,
+ scoped_refptr<CodecBufferWaitCoordinator> codec_buffer_wait_coordinator);
+ ~CodecOutputBufferRenderer();
+
+ CodecOutputBufferRenderer(const CodecOutputBufferRenderer&) = delete;
+ CodecOutputBufferRenderer& operator=(const CodecOutputBufferRenderer&) =
+ delete;
+
+ // Renders this image to the overlay. Returns true if the buffer is in the
+ // overlay front buffer. Returns false if the buffer was invalidated.
+ bool RenderToOverlay();
+
+ // Renders this image to the texture owner front buffer by first rendering
+ // it to the back buffer if it's not already there, and then waiting for the
+ // frame available event before calling UpdateTexImage().
+ bool RenderToTextureOwnerFrontBuffer(BindingsMode bindings_mode);
+
+ // Renders this image to the front buffer of its backing surface.
+ // Returns true if the buffer is in the front buffer. Returns false if the
+ // buffer was invalidated. After an image is invalidated it's no longer
+ // possible to render it.
+ bool RenderToFrontBuffer();
+
+ // Renders this image to the back buffer of its texture owner. Only valid if
+ // is_texture_owner_backed(). Returns true if the buffer is in the back
+ // buffer. Returns false if the buffer was invalidated.
+ // |blocking_mode| indicates whether this should (a) wait for any previously
+ // pending rendered frame before rendering this one, or (b) fail if a wait
+ // is required.
+ bool RenderToTextureOwnerBackBuffer(
+ BlockingMode blocking_mode = BlockingMode::kAllowBlocking);
+
+ // Whether the codec buffer has been rendered to the front buffer.
+ bool was_rendered_to_front_buffer() const {
+ return phase_ == Phase::kInFrontBuffer;
+ }
+
+ gfx::Size size() const { return output_buffer_->size(); }
+
+ bool was_tex_image_bound() const { return was_tex_image_bound_; }
+
+ scoped_refptr<gpu::TextureOwner> texture_owner() const {
+ return codec_buffer_wait_coordinator_
+ ? codec_buffer_wait_coordinator_->texture_owner()
+ : nullptr;
+ }
+
+ CodecOutputBuffer* get_codec_output_buffer_for_testing() const {
+ return output_buffer_.get();
+ }
+
+ private:
+ // The lifecycle phases of an buffer.
+ // The only possible transitions are from left to right. Both
+ // kInFrontBuffer and kInvalidated are terminal.
+ enum class Phase { kInCodec, kInBackBuffer, kInFrontBuffer, kInvalidated };
+
+ void EnsureBoundIfNeeded(BindingsMode mode);
+
+ // The phase of the image buffer's lifecycle.
+ Phase phase_ = Phase::kInCodec;
+
+ // The buffer backing this image.
+ std::unique_ptr<CodecOutputBuffer> output_buffer_;
+
+ // The CodecBufferWaitCoordinator that |output_buffer_| will be rendered to.
+ // Or null, if this image is backed by an overlay.
+ scoped_refptr<CodecBufferWaitCoordinator> codec_buffer_wait_coordinator_;
+
+ bool was_tex_image_bound_ = false;
+};
+
+} // namespace media
+#endif // MEDIA_GPU_ANDROID_CODEC_OUTPUT_BUFFER_RENDERER_H
diff --git a/chromium/media/gpu/android/codec_wrapper_unittest.cc b/chromium/media/gpu/android/codec_wrapper_unittest.cc
index f6973a2ad73..66c48ad7f3e 100644
--- a/chromium/media/gpu/android/codec_wrapper_unittest.cc
+++ b/chromium/media/gpu/android/codec_wrapper_unittest.cc
@@ -5,7 +5,6 @@
#include <memory>
#include "base/bind.h"
-#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/synchronization/waitable_event.h"
#include "base/test/mock_callback.h"
diff --git a/chromium/media/gpu/android/direct_shared_image_video_provider.cc b/chromium/media/gpu/android/direct_shared_image_video_provider.cc
index e4b6b40d02b..e9383411ff6 100644
--- a/chromium/media/gpu/android/direct_shared_image_video_provider.cc
+++ b/chromium/media/gpu/android/direct_shared_image_video_provider.cc
@@ -148,7 +148,7 @@ void GpuSharedImageVideoFactory::CreateImage(
// Generate a shared image mailbox.
auto mailbox = gpu::Mailbox::GenerateForSharedImage();
- auto codec_image = base::MakeRefCounted<CodecImage>();
+ auto codec_image = base::MakeRefCounted<CodecImage>(spec.coded_size);
TRACE_EVENT0("media", "GpuSharedImageVideoFactory::CreateVideoFrame");
@@ -200,14 +200,14 @@ bool GpuSharedImageVideoFactory::CreateImageInternal(
if (!group)
return false;
- const auto& size = spec.size;
+ const auto& coded_size = spec.coded_size;
// Create a Texture and a CodecImage to back it.
// TODO(liberato): Once legacy mailbox support is removed, we don't need to
// create this texture. So, we won't need |texture_owner| either.
std::unique_ptr<AbstractTexture> texture = decoder_helper_->CreateTexture(
- GL_TEXTURE_EXTERNAL_OES, GL_RGBA, size.width(), size.height(), GL_RGBA,
- GL_UNSIGNED_BYTE);
+ GL_TEXTURE_EXTERNAL_OES, GL_RGBA, coded_size.width(), coded_size.height(),
+ GL_RGBA, GL_UNSIGNED_BYTE);
// Attach the image to the texture.
// Either way, we expect this to be UNBOUND (i.e., decoder-managed). For
@@ -239,7 +239,7 @@ bool GpuSharedImageVideoFactory::CreateImageInternal(
// TODO(vikassoni): This shared image need to be thread safe eventually for
// webview to work with shared images.
auto shared_image = std::make_unique<gpu::SharedImageVideo>(
- mailbox, size, gfx::ColorSpace::CreateSRGB(), std::move(image),
+ mailbox, coded_size, gfx::ColorSpace::CreateSRGB(), std::move(image),
std::move(texture), std::move(shared_context),
false /* is_thread_safe */);
diff --git a/chromium/media/gpu/android/frame_info_helper.cc b/chromium/media/gpu/android/frame_info_helper.cc
new file mode 100644
index 00000000000..efe1873cb1c
--- /dev/null
+++ b/chromium/media/gpu/android/frame_info_helper.cc
@@ -0,0 +1,120 @@
+// 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/gpu/android/frame_info_helper.h"
+
+#include "gpu/command_buffer/service/shared_image_video.h"
+#include "gpu/ipc/service/command_buffer_stub.h"
+#include "gpu/ipc/service/gpu_channel.h"
+#include "gpu/ipc/service/gpu_channel_manager.h"
+
+namespace media {
+
+FrameInfoHelper::FrameInfo::FrameInfo() = default;
+FrameInfoHelper::FrameInfo::~FrameInfo() = default;
+FrameInfoHelper::FrameInfo::FrameInfo(FrameInfo&&) = default;
+FrameInfoHelper::FrameInfo::FrameInfo(const FrameInfoHelper::FrameInfo&) =
+ default;
+FrameInfoHelper::FrameInfo& FrameInfoHelper::FrameInfo::operator=(
+ const FrameInfoHelper::FrameInfo&) = default;
+
+// Concrete implementation of FrameInfoHelper that renders output buffers and
+// gets the FrameInfo they need.
+class FrameInfoHelperImpl : public FrameInfoHelper,
+ public gpu::CommandBufferStub::DestructionObserver {
+ public:
+ FrameInfoHelperImpl(SharedImageVideoProvider::GetStubCB get_stub_cb) {
+ stub_ = get_stub_cb.Run();
+ if (stub_)
+ stub_->AddDestructionObserver(this);
+ }
+
+ ~FrameInfoHelperImpl() override {
+ if (stub_)
+ stub_->RemoveDestructionObserver(this);
+ }
+
+ void GetFrameInfo(
+ std::unique_ptr<CodecOutputBufferRenderer> buffer_renderer,
+ base::OnceCallback<
+ void(std::unique_ptr<CodecOutputBufferRenderer>, FrameInfo, bool)> cb)
+ override {
+ if (!buffer_renderer) {
+ std::move(cb).Run(nullptr, FrameInfo(), false);
+ return;
+ }
+
+ auto texture_owner = buffer_renderer->texture_owner();
+
+ FrameInfo info;
+
+ // Indicates that the FrameInfo is reliable and can be cached by caller.
+ // It's true if we either return cached values or we attempted to render
+ // frame and succeeded.
+ bool success = true;
+
+ // We default to visible size if if we can't get real size
+ info.coded_size = buffer_renderer->size();
+ info.visible_rect = gfx::Rect(info.coded_size);
+
+ if (texture_owner) {
+ if (visible_size_ == buffer_renderer->size()) {
+ info = frame_info_;
+ } else if (buffer_renderer->RenderToTextureOwnerFrontBuffer(
+ CodecOutputBufferRenderer::BindingsMode::
+ kDontRestoreIfBound)) {
+ visible_size_ = buffer_renderer->size();
+ texture_owner->GetCodedSizeAndVisibleRect(
+ visible_size_, &frame_info_.coded_size, &frame_info_.visible_rect);
+
+ frame_info_.ycbcr_info = GetYCbCrInfo(texture_owner.get());
+ info = frame_info_;
+ } else {
+ // We attempted to render frame and failed, mark request as failed so
+ // caller won't cache best-guess values.
+ success = false;
+ }
+ }
+
+ std::move(cb).Run(std::move(buffer_renderer), frame_info_, success);
+ }
+
+ void OnWillDestroyStub(bool have_context) override {
+ DCHECK(stub_);
+ stub_ = nullptr;
+ }
+
+ private:
+ // Gets YCbCrInfo from last rendered frame.
+ base::Optional<gpu::VulkanYCbCrInfo> GetYCbCrInfo(
+ gpu::TextureOwner* texture_owner) {
+ gpu::ContextResult result;
+ if (!stub_)
+ return base::nullopt;
+
+ auto shared_context =
+ stub_->channel()->gpu_channel_manager()->GetSharedContextState(&result);
+ auto context_provider =
+ (result == gpu::ContextResult::kSuccess) ? shared_context : nullptr;
+ if (!context_provider)
+ return base::nullopt;
+
+ return gpu::SharedImageVideo::GetYcbcrInfo(texture_owner, context_provider);
+ }
+
+ gpu::CommandBufferStub* stub_ = nullptr;
+
+ FrameInfo frame_info_;
+ gfx::Size visible_size_;
+};
+
+// static
+base::SequenceBound<FrameInfoHelper> FrameInfoHelper::Create(
+ scoped_refptr<base::SequencedTaskRunner> gpu_task_runner,
+ SharedImageVideoProvider::GetStubCB get_stub_cb) {
+ return base::SequenceBound<FrameInfoHelperImpl>(std::move(gpu_task_runner),
+ std::move(get_stub_cb));
+}
+
+} // namespace media
diff --git a/chromium/media/gpu/android/frame_info_helper.h b/chromium/media/gpu/android/frame_info_helper.h
new file mode 100644
index 00000000000..5fc4ffca328
--- /dev/null
+++ b/chromium/media/gpu/android/frame_info_helper.h
@@ -0,0 +1,63 @@
+// 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_GPU_ANDROID_FRAME_INFO_HELPER_H_
+#define MEDIA_GPU_ANDROID_FRAME_INFO_HELPER_H_
+
+#include "base/optional.h"
+#include "base/threading/sequence_bound.h"
+#include "media/gpu/android/codec_image.h"
+#include "media/gpu/android/shared_image_video_provider.h"
+#include "media/gpu/media_gpu_export.h"
+
+namespace media {
+
+// Helper class to fetch YCbCrInfo for Vulkan from a CodecImage.
+class MEDIA_GPU_EXPORT FrameInfoHelper {
+ public:
+ struct FrameInfo {
+ FrameInfo();
+ ~FrameInfo();
+
+ FrameInfo(FrameInfo&&);
+ FrameInfo(const FrameInfo&);
+ FrameInfo& operator=(const FrameInfo&);
+
+ gfx::Size coded_size;
+ gfx::Rect visible_rect;
+ base::Optional<gpu::VulkanYCbCrInfo> ycbcr_info;
+ };
+
+ static base::SequenceBound<FrameInfoHelper> Create(
+ scoped_refptr<base::SequencedTaskRunner> gpu_task_runner,
+ SharedImageVideoProvider::GetStubCB get_stub_cb);
+
+ virtual ~FrameInfoHelper() = default;
+
+ // Call |cb| with the FrameInfo. Will render |buffer_renderer| to the front
+ // buffer if we don't have frame info cached. For Vulkan this also will
+ // attempt to get YCbCrInfo and cache it. If all necessary info is cached the
+ // call will leave buffer_renderer intact and it can be rendered later.
+ // Rendering can fail for reasons. This function will make best efforts to
+ // fill FrameInfo which can be used to create VideoFrame, but shouldn't be
+ // cached by caller. Last parameter in |cb| is bool that indicates that info
+ // is reliable.
+ //
+ // While this API might seem to be out of its Vulkan mind, it's this
+ // complicated to (a) prevent rendering frames out of order to the front
+ // buffer, and (b) make it easy to handle the fact that sometimes, we just
+ // can't get a YCbCrInfo from a CodecImage due to timeouts.
+ virtual void GetFrameInfo(
+ std::unique_ptr<CodecOutputBufferRenderer> buffer_renderer,
+ base::OnceCallback<void(std::unique_ptr<CodecOutputBufferRenderer>,
+ FrameInfo,
+ bool)> cb) = 0;
+
+ protected:
+ FrameInfoHelper() = default;
+};
+
+} // namespace media
+
+#endif // MEDIA_GPU_ANDROID_FRAME_INFO_HELPER_H_
diff --git a/chromium/media/gpu/android/mock_codec_image.cc b/chromium/media/gpu/android/mock_codec_image.cc
index 09901993ac7..aca947f798b 100644
--- a/chromium/media/gpu/android/mock_codec_image.cc
+++ b/chromium/media/gpu/android/mock_codec_image.cc
@@ -6,7 +6,8 @@
namespace media {
-MockCodecImage::MockCodecImage() = default;
+MockCodecImage::MockCodecImage(const gfx::Size& coded_size)
+ : CodecImage(coded_size) {}
MockCodecImage::~MockCodecImage() = default;
diff --git a/chromium/media/gpu/android/mock_codec_image.h b/chromium/media/gpu/android/mock_codec_image.h
index aa83b341a50..6020705e0b3 100644
--- a/chromium/media/gpu/android/mock_codec_image.h
+++ b/chromium/media/gpu/android/mock_codec_image.h
@@ -15,7 +15,7 @@ namespace media {
// CodecImage with a mocked ReleaseCodecBuffer.
class MockCodecImage : public CodecImage {
public:
- MockCodecImage();
+ MockCodecImage(const gfx::Size& coded_size);
MOCK_METHOD0(ReleaseCodecBuffer, void());
diff --git a/chromium/media/gpu/android/promotion_hint_aggregator_impl_unittest.cc b/chromium/media/gpu/android/promotion_hint_aggregator_impl_unittest.cc
index 113b1c953aa..4e7e04acbfa 100644
--- a/chromium/media/gpu/android/promotion_hint_aggregator_impl_unittest.cc
+++ b/chromium/media/gpu/android/promotion_hint_aggregator_impl_unittest.cc
@@ -9,7 +9,6 @@
#include <memory>
#include "base/bind.h"
-#include "base/logging.h"
#include "base/test/simple_test_tick_clock.h"
#include "testing/gtest/include/gtest/gtest.h"
diff --git a/chromium/media/gpu/android/shared_image_video_provider.cc b/chromium/media/gpu/android/shared_image_video_provider.cc
index c543495f253..427987f6624 100644
--- a/chromium/media/gpu/android/shared_image_video_provider.cc
+++ b/chromium/media/gpu/android/shared_image_video_provider.cc
@@ -9,13 +9,13 @@ namespace media {
SharedImageVideoProvider::ImageSpec::ImageSpec() = default;
SharedImageVideoProvider::ImageSpec::ImageSpec(const gfx::Size& our_size,
uint64_t our_generation_id)
- : size(our_size), generation_id(our_generation_id) {}
+ : coded_size(our_size), generation_id(our_generation_id) {}
SharedImageVideoProvider::ImageSpec::ImageSpec(const ImageSpec&) = default;
SharedImageVideoProvider::ImageSpec::~ImageSpec() = default;
bool SharedImageVideoProvider::ImageSpec::operator==(
const ImageSpec& rhs) const {
- return size == rhs.size && generation_id == rhs.generation_id;
+ return coded_size == rhs.coded_size && generation_id == rhs.generation_id;
}
bool SharedImageVideoProvider::ImageSpec::operator!=(
diff --git a/chromium/media/gpu/android/shared_image_video_provider.h b/chromium/media/gpu/android/shared_image_video_provider.h
index 83332f58811..ffbac1331a5 100644
--- a/chromium/media/gpu/android/shared_image_video_provider.h
+++ b/chromium/media/gpu/android/shared_image_video_provider.h
@@ -31,12 +31,12 @@ class MEDIA_GPU_EXPORT SharedImageVideoProvider {
// Description of the underlying properties of the shared image.
struct ImageSpec {
ImageSpec();
- ImageSpec(const gfx::Size& size, uint64_t generation_id);
+ ImageSpec(const gfx::Size& coded_size, uint64_t generation_id);
ImageSpec(const ImageSpec&);
~ImageSpec();
// Size of the underlying texture.
- gfx::Size size;
+ gfx::Size coded_size;
// This is a hack to allow us to discard pooled images if the TextureOwner
// changes. We don't want to keep a ref to the TextureOwner here, so we
diff --git a/chromium/media/gpu/android/surface_chooser_helper_unittest.cc b/chromium/media/gpu/android/surface_chooser_helper_unittest.cc
index f9f4506fc85..353e9b0bfde 100644
--- a/chromium/media/gpu/android/surface_chooser_helper_unittest.cc
+++ b/chromium/media/gpu/android/surface_chooser_helper_unittest.cc
@@ -9,7 +9,6 @@
#include <memory>
#include "base/bind.h"
-#include "base/logging.h"
#include "base/test/simple_test_tick_clock.h"
#include "media/gpu/android/mock_android_video_surface_chooser.h"
#include "media/gpu/android/mock_promotion_hint_aggregator.h"
diff --git a/chromium/media/gpu/android/video_frame_factory_impl.cc b/chromium/media/gpu/android/video_frame_factory_impl.cc
index 78b08ea3fc3..b7c768bae0c 100644
--- a/chromium/media/gpu/android/video_frame_factory_impl.cc
+++ b/chromium/media/gpu/android/video_frame_factory_impl.cc
@@ -81,13 +81,13 @@ VideoFrameFactoryImpl::VideoFrameFactoryImpl(
const gpu::GpuPreferences& gpu_preferences,
std::unique_ptr<SharedImageVideoProvider> image_provider,
std::unique_ptr<MaybeRenderEarlyManager> mre_manager,
- base::SequenceBound<YCbCrHelper> ycbcr_helper)
+ base::SequenceBound<FrameInfoHelper> frame_info_helper)
: image_provider_(std::move(image_provider)),
gpu_task_runner_(std::move(gpu_task_runner)),
enable_threaded_texture_mailboxes_(
gpu_preferences.enable_threaded_texture_mailboxes),
mre_manager_(std::move(mre_manager)),
- ycbcr_helper_(std::move(ycbcr_helper)) {}
+ frame_info_helper_(std::move(frame_info_helper)) {}
VideoFrameFactoryImpl::~VideoFrameFactoryImpl() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
@@ -147,6 +147,9 @@ void VideoFrameFactoryImpl::CreateVideoFrame(
gfx::Size coded_size = output_buffer->size();
gfx::Rect visible_rect(coded_size);
+ auto output_buffer_renderer = std::make_unique<CodecOutputBufferRenderer>(
+ std::move(output_buffer), codec_buffer_wait_coordinator_);
+
// The pixel format doesn't matter here as long as it's valid for texture
// frames. But SkiaRenderer wants to ensure that the format of the resource
// used here which will eventually create a promise image must match the
@@ -165,21 +168,76 @@ void VideoFrameFactoryImpl::CreateVideoFrame(
return;
}
- // Update the current spec to match the size.
- image_spec_.size = coded_size;
+ auto image_ready_cb =
+ base::BindOnce(&VideoFrameFactoryImpl::CreateVideoFrame_OnImageReady,
+ weak_factory_.GetWeakPtr(), std::move(output_cb),
+ timestamp, natural_size, codec_buffer_wait_coordinator_,
+ std::move(promotion_hint_cb), pixel_format, overlay_mode_,
+ enable_threaded_texture_mailboxes_, gpu_task_runner_);
- auto image_ready_cb = base::BindOnce(
- &VideoFrameFactoryImpl::CreateVideoFrame_OnImageReady,
- weak_factory_.GetWeakPtr(), std::move(output_cb), timestamp, coded_size,
- natural_size, std::move(output_buffer), codec_buffer_wait_coordinator_,
- std::move(promotion_hint_cb), pixel_format, overlay_mode_,
- enable_threaded_texture_mailboxes_, gpu_task_runner_);
-
- image_provider_->RequestImage(
- std::move(image_ready_cb), image_spec_,
- codec_buffer_wait_coordinator_
- ? codec_buffer_wait_coordinator_->texture_owner()
- : nullptr);
+ RequestImage(std::move(output_buffer_renderer), std::move(image_ready_cb));
+}
+
+void VideoFrameFactoryImpl::RequestImage(
+ std::unique_ptr<CodecOutputBufferRenderer> buffer_renderer,
+ ImageWithInfoReadyCB image_ready_cb) {
+ if (buffer_renderer && visible_size_ == buffer_renderer->size()) {
+ auto cb = base::BindOnce(std::move(image_ready_cb),
+ std::move(buffer_renderer), frame_info_);
+
+ image_provider_->RequestImage(
+ std::move(cb), image_spec_,
+ codec_buffer_wait_coordinator_
+ ? codec_buffer_wait_coordinator_->texture_owner()
+ : nullptr);
+ return;
+ }
+
+ // We need to reset size to make sure VFFI pipeline is still ordered.
+ // e.g: CreateVideoFrame is called with new size. We post task to GPU thread
+ // to get new frame info. While we wait CreateVideoFrame might be called with
+ // old size again and if we don't reset size here we will skip GPU hop and new
+ // frame will be created earlier than first one.
+ visible_size_ = gfx::Size();
+
+ auto info_cb = BindToCurrentLoop(
+ base::BindOnce(&VideoFrameFactoryImpl::CreateVideoFrame_OnFrameInfoReady,
+ weak_factory_.GetWeakPtr(), std::move(image_ready_cb),
+ codec_buffer_wait_coordinator_));
+
+ frame_info_helper_.Post(FROM_HERE, &FrameInfoHelper::GetFrameInfo,
+ std::move(buffer_renderer), std::move(info_cb));
+}
+
+void VideoFrameFactoryImpl::CreateVideoFrame_OnFrameInfoReady(
+ ImageWithInfoReadyCB image_ready_cb,
+ scoped_refptr<CodecBufferWaitCoordinator> codec_buffer_wait_coordinator,
+ std::unique_ptr<CodecOutputBufferRenderer> output_buffer_renderer,
+ FrameInfoHelper::FrameInfo frame_info,
+ bool success) {
+ // To get frame info we need to render frame which might fail for variety of
+ // reason. FrameInfoHelper will provide best values we can proceed with, but
+ // we should not cache it and attempt to get info for next frame.
+ if (success) {
+ frame_info_ = frame_info;
+ visible_size_ = output_buffer_renderer->size();
+ }
+
+ // If we don't have output buffer here we can't rely on reply from
+ // FrameInfoHelper as there might be not cached value and we can't render
+ // nothing. But in this case call comes from RunAfterPendingVideoFrames and we
+ // just want to ask for the same image spec as before to order callback after
+ // all RequestImage, so skip updating image_spec_ in this case.
+ if (output_buffer_renderer)
+ image_spec_.coded_size = frame_info.coded_size;
+
+ auto cb = base::BindOnce(std::move(image_ready_cb),
+ std::move(output_buffer_renderer), frame_info);
+
+ auto texture_owner = codec_buffer_wait_coordinator
+ ? codec_buffer_wait_coordinator->texture_owner()
+ : nullptr;
+ image_provider_->RequestImage(std::move(cb), image_spec_, texture_owner);
}
// static
@@ -187,15 +245,15 @@ void VideoFrameFactoryImpl::CreateVideoFrame_OnImageReady(
base::WeakPtr<VideoFrameFactoryImpl> thiz,
OnceOutputCB output_cb,
base::TimeDelta timestamp,
- gfx::Size coded_size,
gfx::Size natural_size,
- std::unique_ptr<CodecOutputBuffer> output_buffer,
scoped_refptr<CodecBufferWaitCoordinator> codec_buffer_wait_coordinator,
PromotionHintAggregator::NotifyPromotionHintCB promotion_hint_cb,
VideoPixelFormat pixel_format,
OverlayMode overlay_mode,
bool enable_threaded_texture_mailboxes,
scoped_refptr<base::SequencedTaskRunner> gpu_task_runner,
+ std::unique_ptr<CodecOutputBufferRenderer> output_buffer_renderer,
+ FrameInfoHelper::FrameInfo frame_info,
SharedImageVideoProvider::ImageRecord record) {
TRACE_EVENT0("media", "VideoVideoFrameFactoryImpl::OnVideoFrameImageReady");
@@ -210,7 +268,7 @@ void VideoFrameFactoryImpl::CreateVideoFrame_OnImageReady(
// When we remove the output buffer management from CodecImage, then that's
// what we'd have a reference to here rather than CodecImage.
record.codec_image_holder->codec_image_raw()->Initialize(
- std::move(output_buffer), codec_buffer_wait_coordinator,
+ std::move(output_buffer_renderer), codec_buffer_wait_coordinator,
std::move(promotion_hint_cb));
// Send the CodecImage (via holder, since we can't touch the refcount here) to
@@ -221,75 +279,16 @@ void VideoFrameFactoryImpl::CreateVideoFrame_OnImageReady(
// record before we move it into |completion_cb|.
auto codec_image_holder = std::move(record.codec_image_holder);
- // Doesn't need to be weak-ptr'd, since we're either calling it inline, or
- // calling it from the YCbCr callback which is, itself weak-ptr'd.
- auto completion_cb = base::BindOnce(
- &VideoFrameFactoryImpl::CreateVideoFrame_Finish, thiz,
- std::move(output_cb), timestamp, coded_size, natural_size,
- std::move(codec_buffer_wait_coordinator), pixel_format, overlay_mode,
- enable_threaded_texture_mailboxes, std::move(record));
-
- // TODO(liberato): Use |ycbcr_helper_| as a signal about whether we're
- // supposed to get YCbCr info or not, rather than requiring the provider to
- // tell us. Note that right now, we do have the helper even if we don't
- // need it. See GpuMojoMediaClient.
- if (!thiz->ycbcr_info_ && record.is_vulkan) {
- // We need YCbCr info to create the frame. Post back to the gpu thread to
- // do it. Note that we might post multiple times before succeeding once,
- // both because of failures and because we might get multiple requests to
- // create frames on the mcvd thread, before the gpu thread returns one ycbcr
- // info to us. Either way, it's fine, since the helper also caches the
- // info locally. It won't render more frames than needed.
- auto ycbcr_cb = BindToCurrentLoop(base::BindOnce(
- &VideoFrameFactoryImpl::CreateVideoFrame_OnYCbCrInfo,
- thiz->weak_factory_.GetWeakPtr(), std::move(completion_cb)));
- thiz->ycbcr_helper_.Post(FROM_HERE, &YCbCrHelper::GetYCbCrInfo,
- std::move(codec_image_holder),
- std::move(ycbcr_cb));
- return;
- }
-
- std::move(completion_cb).Run();
-}
-
-void VideoFrameFactoryImpl::CreateVideoFrame_OnYCbCrInfo(
- base::OnceClosure completion_cb,
- YCbCrHelper::OptionalInfo ycbcr_info) {
- ycbcr_info_ = std::move(ycbcr_info);
- if (ycbcr_info_) {
- // Clear the helper just to free it up, though we might continue to get
- // callbacks from it if we've posted multiple requests.
- //
- // We only do this if we actually get the info; we should continue to ask
- // if we don't. This can happen if, for example, the frame failed to render
- // due to a timeout.
- ycbcr_helper_.Reset();
- }
- std::move(completion_cb).Run();
-}
-
-void VideoFrameFactoryImpl::CreateVideoFrame_Finish(
- OnceOutputCB output_cb,
- base::TimeDelta timestamp,
- gfx::Size coded_size,
- gfx::Size natural_size,
- scoped_refptr<CodecBufferWaitCoordinator> codec_buffer_wait_coordinator,
- VideoPixelFormat pixel_format,
- OverlayMode overlay_mode,
- bool enable_threaded_texture_mailboxes,
- SharedImageVideoProvider::ImageRecord record) {
gpu::MailboxHolder mailbox_holders[VideoFrame::kMaxPlanes];
mailbox_holders[0] = gpu::MailboxHolder(record.mailbox, gpu::SyncToken(),
GL_TEXTURE_EXTERNAL_OES);
- gfx::Rect visible_rect(coded_size);
-
auto frame = VideoFrame::WrapNativeTextures(
- pixel_format, mailbox_holders, VideoFrame::ReleaseMailboxCB(), coded_size,
- visible_rect, natural_size, timestamp);
+ pixel_format, mailbox_holders, VideoFrame::ReleaseMailboxCB(),
+ frame_info.coded_size, frame_info.visible_rect, natural_size, timestamp);
// For Vulkan.
- frame->set_ycbcr_info(ycbcr_info_);
+ frame->set_ycbcr_info(frame_info.ycbcr_info);
// If, for some reason, we failed to create a frame, then fail. Note that we
// don't need to call |release_cb|; dropping it is okay since the api says so.
@@ -370,17 +369,15 @@ void VideoFrameFactoryImpl::RunAfterPendingVideoFrames(
auto image_ready_cb = base::BindOnce(
[](base::OnceClosure closure,
+ std::unique_ptr<CodecOutputBufferRenderer> output_buffer_renderer,
+ FrameInfoHelper::FrameInfo frame_info,
SharedImageVideoProvider::ImageRecord record) {
// Ignore |record| since we don't actually need an image.
std::move(closure).Run();
},
std::move(closure));
- image_provider_->RequestImage(
- std::move(image_ready_cb), image_spec_,
- codec_buffer_wait_coordinator_
- ? codec_buffer_wait_coordinator_->texture_owner()
- : nullptr);
+ RequestImage(nullptr, std::move(image_ready_cb));
}
} // namespace media
diff --git a/chromium/media/gpu/android/video_frame_factory_impl.h b/chromium/media/gpu/android/video_frame_factory_impl.h
index 9b4c5f50329..624d7d2b650 100644
--- a/chromium/media/gpu/android/video_frame_factory_impl.h
+++ b/chromium/media/gpu/android/video_frame_factory_impl.h
@@ -16,10 +16,10 @@
#include "media/gpu/android/codec_buffer_wait_coordinator.h"
#include "media/gpu/android/codec_image.h"
#include "media/gpu/android/codec_wrapper.h"
+#include "media/gpu/android/frame_info_helper.h"
#include "media/gpu/android/maybe_render_early_manager.h"
#include "media/gpu/android/shared_image_video_provider.h"
#include "media/gpu/android/video_frame_factory.h"
-#include "media/gpu/android/ycbcr_helper.h"
#include "media/gpu/media_gpu_export.h"
#include "ui/gl/gl_bindings.h"
@@ -41,13 +41,18 @@ class MEDIA_GPU_EXPORT VideoFrameFactoryImpl : public VideoFrameFactory {
base::OnceCallback<void(gpu::Mailbox mailbox,
VideoFrame::ReleaseMailboxCB release_cb)>;
+ using ImageWithInfoReadyCB =
+ base::OnceCallback<void(std::unique_ptr<CodecOutputBufferRenderer>,
+ FrameInfoHelper::FrameInfo,
+ SharedImageVideoProvider::ImageRecord)>;
+
// |get_stub_cb| will be run on |gpu_task_runner|.
VideoFrameFactoryImpl(
scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,
const gpu::GpuPreferences& gpu_preferences,
std::unique_ptr<SharedImageVideoProvider> image_provider,
std::unique_ptr<MaybeRenderEarlyManager> mre_manager,
- base::SequenceBound<YCbCrHelper> ycbcr_helper);
+ base::SequenceBound<FrameInfoHelper> frame_info_helper);
~VideoFrameFactoryImpl() override;
void Initialize(OverlayMode overlay_mode, InitCB init_cb) override;
@@ -68,6 +73,8 @@ class MEDIA_GPU_EXPORT VideoFrameFactoryImpl : public VideoFrameFactory {
}
private:
+ void RequestImage(std::unique_ptr<CodecOutputBufferRenderer> buffer_renderer,
+ ImageWithInfoReadyCB image_ready_cb);
// ImageReadyCB that will construct a VideoFrame, and forward it to
// |output_cb| if construction succeeds. This is static for two reasons.
// First, we want to snapshot the state of the world when the request is made,
@@ -83,33 +90,23 @@ class MEDIA_GPU_EXPORT VideoFrameFactoryImpl : public VideoFrameFactory {
base::WeakPtr<VideoFrameFactoryImpl> thiz,
OnceOutputCB output_cb,
base::TimeDelta timestamp,
- gfx::Size coded_size,
gfx::Size natural_size,
- std::unique_ptr<CodecOutputBuffer> output_buffer,
scoped_refptr<CodecBufferWaitCoordinator> codec_buffer_wait_coordinator,
PromotionHintAggregator::NotifyPromotionHintCB promotion_hint_cb,
VideoPixelFormat pixel_format,
OverlayMode overlay_mode,
bool enable_threaded_texture_mailboxes,
scoped_refptr<base::SequencedTaskRunner> gpu_task_runner,
+ std::unique_ptr<CodecOutputBufferRenderer> output_buffer_renderer,
+ FrameInfoHelper::FrameInfo frame_info,
SharedImageVideoProvider::ImageRecord record);
- // Callback to receive YCbCrInfo from |provider_| while creating a VideoFrame.
- void CreateVideoFrame_OnYCbCrInfo(base::OnceClosure completion_cb,
- YCbCrHelper::OptionalInfo ycbcr_info);
-
- // Really create the VideoFrame, once we've tried to get the YCbCrInfo if it's
- // needed for it.
- void CreateVideoFrame_Finish(
- OnceOutputCB output_cb,
- base::TimeDelta timestamp,
- gfx::Size coded_size,
- gfx::Size natural_size,
+ void CreateVideoFrame_OnFrameInfoReady(
+ ImageWithInfoReadyCB image_ready_cb,
scoped_refptr<CodecBufferWaitCoordinator> codec_buffer_wait_coordinator,
- VideoPixelFormat pixel_format,
- OverlayMode overlay_mode,
- bool enable_threaded_texture_mailboxes,
- SharedImageVideoProvider::ImageRecord record);
+ std::unique_ptr<CodecOutputBufferRenderer> output_buffer_renderer,
+ FrameInfoHelper::FrameInfo frame_info,
+ bool success);
MaybeRenderEarlyManager* mre_manager() const { return mre_manager_.get(); }
@@ -131,11 +128,12 @@ class MEDIA_GPU_EXPORT VideoFrameFactoryImpl : public VideoFrameFactory {
std::unique_ptr<MaybeRenderEarlyManager> mre_manager_;
- // Sampler conversion information which is used in vulkan context.
- YCbCrHelper::OptionalInfo ycbcr_info_;
+ // Caches FrameInfo and visible size it was cached for.
+ gfx::Size visible_size_;
+ FrameInfoHelper::FrameInfo frame_info_;
// Optional helper to get the Vulkan YCbCrInfo.
- base::SequenceBound<YCbCrHelper> ycbcr_helper_;
+ base::SequenceBound<FrameInfoHelper> frame_info_helper_;
// The current image spec that we'll use to request images.
SharedImageVideoProvider::ImageSpec image_spec_;
diff --git a/chromium/media/gpu/android/video_frame_factory_impl_unittest.cc b/chromium/media/gpu/android/video_frame_factory_impl_unittest.cc
index eda146a5425..ade0a27c05d 100644
--- a/chromium/media/gpu/android/video_frame_factory_impl_unittest.cc
+++ b/chromium/media/gpu/android/video_frame_factory_impl_unittest.cc
@@ -41,21 +41,49 @@ class MockMaybeRenderEarlyManager : public MaybeRenderEarlyManager {
MOCK_METHOD0(MaybeRenderEarly, void());
};
-class MockYCbCrHelper : public YCbCrHelper, public DestructionObservable {
+class MockFrameInfoHelper : public FrameInfoHelper,
+ public DestructionObservable {
public:
- MockYCbCrHelper(MockYCbCrHelper** thiz) { *thiz = this; }
-
- void GetYCbCrInfo(
- scoped_refptr<CodecImageHolder> codec_image_holder,
- base::OnceCallback<void(OptionalInfo ycbcr_info)> cb) override {
- MockGetYCbCrInfo(codec_image_holder);
+ MockFrameInfoHelper(MockFrameInfoHelper** thiz) { *thiz = this; }
+
+ void GetFrameInfo(
+ std::unique_ptr<CodecOutputBufferRenderer> buffer_renderer,
+ base::OnceCallback<
+ void(std::unique_ptr<CodecOutputBufferRenderer>, FrameInfo, bool)> cb)
+ override {
+ MockGetFrameInfo(buffer_renderer.get());
cb_ = std::move(cb);
+ buffer_renderer_ = std::move(buffer_renderer);
+
+ if (run_callback_automatically_) {
+ RunWithYcbCrInfo(true);
+ base::RunLoop().RunUntilIdle();
+ }
}
- MOCK_METHOD1(MockGetYCbCrInfo,
- void(scoped_refptr<CodecImageHolder> codec_image_holder));
+ void RunWithYcbCrInfo(bool success) {
+ DCHECK(buffer_renderer_);
+
+ FrameInfo info;
+ info.coded_size = buffer_renderer_->size();
+ info.visible_rect = gfx::Rect(info.coded_size);
+
+ std::move(cb_).Run(std::move(buffer_renderer_), info, success);
+ }
- base::OnceCallback<void(OptionalInfo ycbcr_info)> cb_;
+ void set_run_callback_automatically(bool run_callback_automatically) {
+ run_callback_automatically_ = run_callback_automatically;
+ }
+
+ MOCK_METHOD1(MockGetFrameInfo,
+ void(CodecOutputBufferRenderer* buffer_renderer));
+
+ private:
+ bool run_callback_automatically_ = true;
+ base::OnceCallback<
+ void(std::unique_ptr<CodecOutputBufferRenderer>, FrameInfo, bool)>
+ cb_;
+ std::unique_ptr<CodecOutputBufferRenderer> buffer_renderer_;
};
class VideoFrameFactoryImplTest : public testing::Test {
@@ -68,8 +96,8 @@ class VideoFrameFactoryImplTest : public testing::Test {
auto mre_manager = std::make_unique<MockMaybeRenderEarlyManager>();
mre_manager_raw_ = mre_manager.get();
- auto ycbcr_helper =
- base::SequenceBound<MockYCbCrHelper>(task_runner_, &ycbcr_helper_raw_);
+ auto ycbcr_helper = base::SequenceBound<MockFrameInfoHelper>(
+ task_runner_, &ycbcr_helper_raw_);
base::RunLoop().RunUntilIdle(); // Init |ycbcr_helper_raw_|.
ycbcr_destruction_observer_ =
ycbcr_helper_raw_->CreateDestructionObserver();
@@ -128,7 +156,8 @@ class VideoFrameFactoryImplTest : public testing::Test {
*flag = true;
},
base::Unretained(release_cb_called_flag));
- auto codec_image = base::MakeRefCounted<MockCodecImage>();
+ auto codec_image =
+ base::MakeRefCounted<MockCodecImage>(gfx::Size(100, 100));
record.codec_image_holder =
base::MakeRefCounted<CodecImageHolder>(task_runner_, codec_image);
return record;
@@ -148,7 +177,7 @@ class VideoFrameFactoryImplTest : public testing::Test {
// Sent to |impl_| by RequestVideoFrame..
base::MockCallback<VideoFrameFactory::OnceOutputCB> output_cb_;
- MockYCbCrHelper* ycbcr_helper_raw_ = nullptr;
+ MockFrameInfoHelper* ycbcr_helper_raw_ = nullptr;
std::unique_ptr<DestructionObserver> ycbcr_destruction_observer_;
gpu::GpuPreferences gpu_preferences_;
@@ -244,77 +273,72 @@ TEST_F(VideoFrameFactoryImplTest,
base::RunLoop().RunUntilIdle();
}
-TEST_F(VideoFrameFactoryImplTest, DoesNotCallYCbCrHelperIfNotVulkan) {
- EXPECT_CALL(*ycbcr_helper_raw_, MockGetYCbCrInfo(_)).Times(0);
- RequestVideoFrame();
- auto image_record = MakeImageRecord();
- image_record.is_vulkan = false;
- image_provider_raw_->ProvideOneRequestedImage(&image_record);
- base::RunLoop().RunUntilIdle();
-}
+TEST_F(VideoFrameFactoryImplTest, DoesCallFrameInfoHelperIfVulkan) {
+ // We will be driving callback by ourselves in this test.
+ ycbcr_helper_raw_->set_run_callback_automatically(false);
+ // Expect call to get info for the first frame.
+ EXPECT_CALL(*ycbcr_helper_raw_, MockGetFrameInfo(_)).Times(1);
-TEST_F(VideoFrameFactoryImplTest, DoesCallYCbCrHelperIfVulkan) {
RequestVideoFrame();
- auto image_record = MakeImageRecord();
- base::OnceCallback<void(YCbCrHelper::OptionalInfo)> cb;
- EXPECT_CALL(*ycbcr_helper_raw_,
- MockGetYCbCrInfo(image_record.codec_image_holder))
- .Times(1);
- image_record.is_vulkan = true;
- image_provider_raw_->ProvideOneRequestedImage(&image_record);
+
+ // Provide info. It should send image request.
+ ycbcr_helper_raw_->RunWithYcbCrInfo(true);
base::RunLoop().RunUntilIdle();
- // Provide YCbCrInfo. It should provide the VideoFrame too.
+ testing::Mock::VerifyAndClearExpectations(ycbcr_helper_raw_);
+
+ // Fulfilling image request should provide video frame.
EXPECT_CALL(output_cb_, Run(_)).Times(1);
- gpu::VulkanYCbCrInfo ycbcr;
- std::move(ycbcr_helper_raw_->cb_).Run(ycbcr);
+
+ auto image_record = MakeImageRecord();
+ image_provider_raw_->ProvideOneRequestedImage(&image_record);
base::RunLoop().RunUntilIdle();
- // It's okay if the ycbcr helper is destroyed. If not, then verify
- // expectations explicitly now.
- if (ycbcr_destruction_observer_->destructed())
- ycbcr_helper_raw_ = nullptr;
- else
- testing::Mock::VerifyAndClearExpectations(ycbcr_helper_raw_);
// Verify that no more calls happen, since we don't want thread hops on every
// frame. Note that multiple could be dispatched before now. It should still
// send along a VideoFrame, though.
+ EXPECT_CALL(*ycbcr_helper_raw_, MockGetFrameInfo(_)).Times(0);
+ EXPECT_CALL(output_cb_, Run(_)).Times(1);
+
RequestVideoFrame();
auto other_image_record = MakeImageRecord();
// If the helper hasn't been destroyed, then we don't expect it to be called.
- if (ycbcr_helper_raw_)
- EXPECT_CALL(*ycbcr_helper_raw_, MockGetYCbCrInfo(_)).Times(0);
- EXPECT_CALL(output_cb_, Run(_)).Times(1);
image_provider_raw_->ProvideOneRequestedImage(&other_image_record);
base::RunLoop().RunUntilIdle();
}
TEST_F(VideoFrameFactoryImplTest, NullYCbCrInfoDoesntCrash) {
- // Sending a null YCbCrInfo then requesting a frame shouldn't cause a crash.
- // See https://crbug.com/1007196 .
+ // We will be driving callback by ourselves in this test.
+ ycbcr_helper_raw_->set_run_callback_automatically(false);
+
+ // Expect call to get info for the first frame.
+ EXPECT_CALL(*ycbcr_helper_raw_, MockGetFrameInfo(_)).Times(1);
+
RequestVideoFrame();
+
+ // Provide info. It should send image request.
+ ycbcr_helper_raw_->RunWithYcbCrInfo(false);
+ base::RunLoop().RunUntilIdle();
+
+ testing::Mock::VerifyAndClearExpectations(ycbcr_helper_raw_);
+
+ // Fulfilling image request should provide video frame.
+ EXPECT_CALL(output_cb_, Run(_)).Times(1);
+
auto image_record = MakeImageRecord();
- EXPECT_CALL(*ycbcr_helper_raw_,
- MockGetYCbCrInfo(image_record.codec_image_holder))
- .Times(1);
- image_record.is_vulkan = true;
image_provider_raw_->ProvideOneRequestedImage(&image_record);
base::RunLoop().RunUntilIdle();
- // Provide an empty YCbCrInfo.
+ // Verify that we will get call to GetFrameInfo as previous one failed.
+ EXPECT_CALL(*ycbcr_helper_raw_, MockGetFrameInfo(_)).Times(1);
EXPECT_CALL(output_cb_, Run(_)).Times(1);
- std::move(ycbcr_helper_raw_->cb_).Run(base::nullopt);
- base::RunLoop().RunUntilIdle();
- // It shouldn't crash on the next frame. crbug.com/1007196
RequestVideoFrame();
+ ycbcr_helper_raw_->RunWithYcbCrInfo(true);
+ base::RunLoop().RunUntilIdle();
+
auto other_image_record = MakeImageRecord();
- other_image_record.is_vulkan = true;
- // Should still call the helper, since it didn't get YCbCrInfo last time.
- EXPECT_CALL(*ycbcr_helper_raw_,
- MockGetYCbCrInfo(other_image_record.codec_image_holder))
- .Times(1);
- // Since we aren't sending YCbCr info, it won't call us back with a frame.
+ // If the helper hasn't been destroyed, then we don't expect it to be called.
image_provider_raw_->ProvideOneRequestedImage(&other_image_record);
base::RunLoop().RunUntilIdle();
}
diff --git a/chromium/media/gpu/android/ycbcr_helper.cc b/chromium/media/gpu/android/ycbcr_helper.cc
deleted file mode 100644
index bee89a685a8..00000000000
--- a/chromium/media/gpu/android/ycbcr_helper.cc
+++ /dev/null
@@ -1,82 +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.
-
-#include "media/gpu/android/ycbcr_helper.h"
-
-#include "gpu/command_buffer/service/shared_image_video.h"
-#include "gpu/ipc/service/command_buffer_stub.h"
-#include "gpu/ipc/service/gpu_channel.h"
-#include "gpu/ipc/service/gpu_channel_manager.h"
-
-namespace media {
-
-// Concrete implementation of YCbCrHelper that renders output buffers and gets
-// the YCbCrInfo they need.
-class YCbCrHelperImpl : public YCbCrHelper,
- public gpu::CommandBufferStub::DestructionObserver {
- public:
- YCbCrHelperImpl(SharedImageVideoProvider::GetStubCB get_stub_cb) {
- stub_ = get_stub_cb.Run();
- if (stub_)
- stub_->AddDestructionObserver(this);
- }
-
- ~YCbCrHelperImpl() override {
- if (stub_)
- stub_->RemoveDestructionObserver(this);
- }
-
- // YCbCrHelper
- void GetYCbCrInfo(
- scoped_refptr<CodecImageHolder> codec_image_holder,
- base::OnceCallback<void(OptionalInfo ycbcr_info)> cb) override {
- // If we don't have the info cached, then try to get it. If we have gotten
- // it, then don't try again. Assume that our caller asked for it before it
- // got the results back. We don't want to render more frames to the front
- // buffer if we don't need to.
- if (!ycbcr_info_)
- ycbcr_info_ = RenderImageAndGetYCbCrInfo(std::move(codec_image_holder));
-
- // Whether we got it or not, send it along.
- std::move(cb).Run(ycbcr_info_);
- }
-
- void OnWillDestroyStub(bool have_context) override {
- DCHECK(stub_);
- stub_ = nullptr;
- }
-
- private:
- // Render the codec output buffer, and use it to get the YCbCrInfo.
- OptionalInfo RenderImageAndGetYCbCrInfo(
- scoped_refptr<CodecImageHolder> codec_image_holder) {
- gpu::ContextResult result;
- if (!stub_)
- return base::nullopt;
-
- auto shared_context =
- stub_->channel()->gpu_channel_manager()->GetSharedContextState(&result);
- auto context_provider =
- (result == gpu::ContextResult::kSuccess) ? shared_context : nullptr;
- if (!context_provider)
- return base::nullopt;
-
- return gpu::SharedImageVideo::GetYcbcrInfo(
- codec_image_holder->codec_image_raw(), context_provider);
- }
-
- gpu::CommandBufferStub* stub_ = nullptr;
-
- OptionalInfo ycbcr_info_;
-};
-
-// static
-base::SequenceBound<YCbCrHelper> YCbCrHelper::Create(
- scoped_refptr<base::SequencedTaskRunner> gpu_task_runner,
- SharedImageVideoProvider::GetStubCB get_stub_cb) {
- return base::SequenceBound<YCbCrHelperImpl>(std::move(gpu_task_runner),
- std::move(get_stub_cb));
-}
-
-} // namespace media
diff --git a/chromium/media/gpu/android/ycbcr_helper.h b/chromium/media/gpu/android/ycbcr_helper.h
deleted file mode 100644
index 4576ece0086..00000000000
--- a/chromium/media/gpu/android/ycbcr_helper.h
+++ /dev/null
@@ -1,48 +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_GPU_ANDROID_YCBCR_HELPER_H_
-#define MEDIA_GPU_ANDROID_YCBCR_HELPER_H_
-
-#include "base/optional.h"
-#include "base/threading/sequence_bound.h"
-#include "media/gpu/android/codec_image.h"
-#include "media/gpu/android/shared_image_video_provider.h"
-#include "media/gpu/media_gpu_export.h"
-
-namespace media {
-
-// Helper class to fetch YCbCrInfo for Vulkan from a CodecImage.
-class MEDIA_GPU_EXPORT YCbCrHelper {
- public:
- using OptionalInfo = base::Optional<gpu::VulkanYCbCrInfo>;
-
- static base::SequenceBound<YCbCrHelper> Create(
- scoped_refptr<base::SequencedTaskRunner> gpu_task_runner,
- SharedImageVideoProvider::GetStubCB get_stub_cb);
-
- virtual ~YCbCrHelper() = default;
-
- // Call |cb| with the YCbCrInfo (or nullopt, if we can't get it). Will render
- // |codec_image_holder| to the front buffer if it hasn't successfully gotten
- // the YCbCrInfo on a previous call. Otherwise, will return the cached
- // YCbCrInfo and leave |codec_image_holder| unmodified. Once we call |cb|
- // with a non-nullopt YCbCrInfo, we will always return that same value; there
- // is no need to call us afterwards.
- //
- // While this API might seem to be out of its Vulkan mind, it's this
- // complicated to (a) prevent rendering frames out of order to the front
- // buffer, and (b) make it easy to handle the fact that sometimes, we just
- // can't get a YCbCrInfo from a CodecImage due to timeouts.
- virtual void GetYCbCrInfo(
- scoped_refptr<CodecImageHolder> codec_image_holder,
- base::OnceCallback<void(OptionalInfo ycbcr_info)> cb) = 0;
-
- protected:
- YCbCrHelper() = default;
-};
-
-} // namespace media
-
-#endif // MEDIA_GPU_ANDROID_YCBCR_HELPER_H_
diff --git a/chromium/media/gpu/chromeos/fourcc_unittests.cc b/chromium/media/gpu/chromeos/fourcc_unittests.cc
index 9ad8c921bea..ade4a4b663c 100644
--- a/chromium/media/gpu/chromeos/fourcc_unittests.cc
+++ b/chromium/media/gpu/chromeos/fourcc_unittests.cc
@@ -5,7 +5,6 @@
#include "base/optional.h"
#include "media/gpu/chromeos/fourcc.h"
-#include "base/logging.h"
#include "media/gpu/buildflags.h"
#include "testing/gtest/include/gtest/gtest.h"
diff --git a/chromium/media/gpu/chromeos/platform_video_frame_utils.cc b/chromium/media/gpu/chromeos/platform_video_frame_utils.cc
index 0c555fd8f05..9e7994040b8 100644
--- a/chromium/media/gpu/chromeos/platform_video_frame_utils.cc
+++ b/chromium/media/gpu/chromeos/platform_video_frame_utils.cc
@@ -4,12 +4,15 @@
#include "media/gpu/chromeos/platform_video_frame_utils.h"
-#include "base/atomic_sequence_num.h"
+#include <limits>
+
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/callback_helpers.h"
#include "base/files/scoped_file.h"
+#include "base/no_destructor.h"
#include "base/posix/eintr_wrapper.h"
+#include "base/synchronization/lock.h"
#include "gpu/ipc/common/gpu_client_ids.h"
#include "gpu/ipc/common/gpu_memory_buffer_support.h"
#include "gpu/ipc/service/gpu_memory_buffer_factory.h"
@@ -37,11 +40,19 @@ gfx::GpuMemoryBufferHandle AllocateGpuMemoryBufferHandle(
if (!buffer_format)
return gmb_handle;
- static base::AtomicSequenceNumber buffer_id_generator;
+ int gpu_memory_buffer_id;
+ {
+ static base::NoDestructor<base::Lock> id_lock;
+ static int next_gpu_memory_buffer_id = 0;
+ base::AutoLock lock(*id_lock);
+ CHECK_LT(next_gpu_memory_buffer_id, std::numeric_limits<int>::max());
+ gpu_memory_buffer_id = next_gpu_memory_buffer_id++;
+ }
+
// TODO(hiroh): Rename the client id to more generic one.
gmb_handle = factory->CreateGpuMemoryBuffer(
- gfx::GpuMemoryBufferId(buffer_id_generator.GetNext()), coded_size,
- *buffer_format, buffer_usage, gpu::kPlatformVideoFramePoolClientId,
+ gfx::GpuMemoryBufferId(gpu_memory_buffer_id), coded_size, *buffer_format,
+ buffer_usage, gpu::kPlatformVideoFramePoolClientId,
gfx::kNullAcceleratedWidget);
DCHECK(gmb_handle.is_null() || gmb_handle.type != gfx::NATIVE_PIXMAP ||
VideoFrame::NumPlanes(pixel_format) ==
diff --git a/chromium/media/gpu/chromeos/platform_video_frame_utils_unittest.cc b/chromium/media/gpu/chromeos/platform_video_frame_utils_unittest.cc
index 005b9dd92fc..abcc36328be 100644
--- a/chromium/media/gpu/chromeos/platform_video_frame_utils_unittest.cc
+++ b/chromium/media/gpu/chromeos/platform_video_frame_utils_unittest.cc
@@ -70,7 +70,7 @@ scoped_refptr<VideoFrame> CreateMockDmaBufVideoFrame(
class FakeGpuMemoryBufferFactory : public gpu::GpuMemoryBufferFactory {
public:
FakeGpuMemoryBufferFactory() = default;
- ~FakeGpuMemoryBufferFactory() {
+ ~FakeGpuMemoryBufferFactory() override {
for (const auto& buffers : gpu_memory_buffers_) {
if (!buffers.second.empty()) {
LOG(ERROR) << "client_id=" << buffers.first
diff --git a/chromium/media/gpu/chromeos/video_decoder_pipeline.cc b/chromium/media/gpu/chromeos/video_decoder_pipeline.cc
index 5ebd3caedbb..906861ba788 100644
--- a/chromium/media/gpu/chromeos/video_decoder_pipeline.cc
+++ b/chromium/media/gpu/chromeos/video_decoder_pipeline.cc
@@ -319,7 +319,7 @@ void VideoDecoderPipeline::ResetTask(base::OnceClosure closure) {
DCHECK(!client_reset_cb_);
DVLOGF(3);
- need_notify_decoder_flushed_ = false;
+ need_apply_new_resolution = false;
client_reset_cb_ = std::move(closure);
decoder_->Reset(
base::BindOnce(&VideoDecoderPipeline::OnResetDone, decoder_weak_this_));
@@ -431,7 +431,7 @@ void VideoDecoderPipeline::OnFrameConverted(scoped_refptr<VideoFrame> frame) {
// After outputting a frame, flush might be completed.
CallFlushCbIfNeeded(DecodeStatus::OK);
- CallOnPipelineFlushedIfNeeded();
+ CallApplyResolutionChangeIfNeeded();
}
bool VideoDecoderPipeline::HasPendingFrames() const {
@@ -468,19 +468,19 @@ void VideoDecoderPipeline::CallFlushCbIfNeeded(DecodeStatus status) {
void VideoDecoderPipeline::PrepareChangeResolution() {
DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_);
DVLOGF(3);
- DCHECK(!need_notify_decoder_flushed_);
+ DCHECK(!need_apply_new_resolution);
- need_notify_decoder_flushed_ = true;
- CallOnPipelineFlushedIfNeeded();
+ need_apply_new_resolution = true;
+ CallApplyResolutionChangeIfNeeded();
}
-void VideoDecoderPipeline::CallOnPipelineFlushedIfNeeded() {
+void VideoDecoderPipeline::CallApplyResolutionChangeIfNeeded() {
DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_);
DVLOGF(3);
- if (need_notify_decoder_flushed_ && !HasPendingFrames()) {
- need_notify_decoder_flushed_ = false;
- decoder_->OnPipelineFlushed();
+ if (need_apply_new_resolution && !HasPendingFrames()) {
+ need_apply_new_resolution = false;
+ decoder_->ApplyResolutionChange();
}
}
diff --git a/chromium/media/gpu/chromeos/video_decoder_pipeline.h b/chromium/media/gpu/chromeos/video_decoder_pipeline.h
index 396055e7227..030ed9058e1 100644
--- a/chromium/media/gpu/chromeos/video_decoder_pipeline.h
+++ b/chromium/media/gpu/chromeos/video_decoder_pipeline.h
@@ -59,7 +59,7 @@ class MEDIA_GPU_EXPORT DecoderInterface {
virtual DmabufVideoFramePool* GetVideoFramePool() const = 0;
// After this method is called from |decoder_|, the client needs to call
- // DecoderInterface::OnPipelineFlushed() when all pending frames are
+ // DecoderInterface::ApplyResolutionChange() when all pending frames are
// flushed.
virtual void PrepareChangeResolution() = 0;
@@ -114,7 +114,7 @@ class MEDIA_GPU_EXPORT DecoderInterface {
// After DecoderInterface calls |prepare_change_resolution_cb| passed
// from the constructor, this method is called when the pipeline flushes
// pending frames.
- virtual void OnPipelineFlushed() = 0;
+ virtual void ApplyResolutionChange() = 0;
protected:
// Decoder task runner. All public methods of
@@ -217,8 +217,8 @@ class MEDIA_GPU_EXPORT VideoDecoderPipeline : public VideoDecoder,
// i.e. |image_processor_| or |frame_converter_| has pending frames.
bool HasPendingFrames() const;
- // Call DecoderInterface::OnPipelineFlushed() when we need to.
- void CallOnPipelineFlushedIfNeeded();
+ // Call DecoderInterface::ApplyResolutionChange() when we need to.
+ void CallApplyResolutionChangeIfNeeded();
// Call |client_flush_cb_| with |status|.
void CallFlushCbIfNeeded(DecodeStatus status);
@@ -271,8 +271,8 @@ class MEDIA_GPU_EXPORT VideoDecoderPipeline : public VideoDecoder,
base::OnceClosure client_reset_cb_;
// True if we need to notify |decoder_| that the pipeline is flushed via
- // DecoderInterface::OnPipelineFlushed().
- bool need_notify_decoder_flushed_ = false;
+ // DecoderInterface::ApplyResolutionChange().
+ bool need_apply_new_resolution = false;
// True if the decoder needs bitstream conversion before decoding.
bool needs_bitstream_conversion_ = false;
diff --git a/chromium/media/gpu/gles2_decoder_helper.cc b/chromium/media/gpu/gles2_decoder_helper.cc
index 4d32c2ac2c6..bebb6a66629 100644
--- a/chromium/media/gpu/gles2_decoder_helper.cc
+++ b/chromium/media/gpu/gles2_decoder_helper.cc
@@ -6,7 +6,7 @@
#include <memory>
-#include "base/logging.h"
+#include "base/check_op.h"
#include "base/macros.h"
#include "base/threading/thread_checker.h"
#include "gpu/command_buffer/common/gles2_cmd_utils.h"
diff --git a/chromium/media/gpu/gpu_video_encode_accelerator_factory.cc b/chromium/media/gpu/gpu_video_encode_accelerator_factory.cc
index cf43708610e..21ecf1b6ed8 100644
--- a/chromium/media/gpu/gpu_video_encode_accelerator_factory.cc
+++ b/chromium/media/gpu/gpu_video_encode_accelerator_factory.cc
@@ -22,6 +22,7 @@
#endif
#if defined(OS_WIN)
#include "base/feature_list.h"
+#include "gpu/config/gpu_driver_bug_workarounds.h"
#include "media/base/media_switches.h"
#include "media/gpu/windows/media_foundation_video_encode_accelerator_win.h"
#endif
@@ -67,9 +68,11 @@ std::unique_ptr<VideoEncodeAccelerator> CreateVTVEA() {
// Creates a MediaFoundationVEA for Win 7 or later. If |compatible_with_win7| is
// true, VEA is limited to a subset of features that is compatible with Win 7.
std::unique_ptr<VideoEncodeAccelerator> CreateMediaFoundationVEA(
- bool compatible_with_win7) {
+ bool compatible_with_win7,
+ bool enable_async_mft) {
return base::WrapUnique<VideoEncodeAccelerator>(
- new MediaFoundationVideoEncodeAccelerator(compatible_with_win7));
+ new MediaFoundationVideoEncodeAccelerator(compatible_with_win7,
+ enable_async_mft));
}
#endif
@@ -100,11 +103,12 @@ std::vector<VEAFactoryFunction> GetVEAFactoryFunctions(
vea_factory_functions.push_back(base::BindRepeating(&CreateVTVEA));
#endif
#if defined(OS_WIN)
- if (base::FeatureList::IsEnabled(kMediaFoundationH264Encoding)) {
- vea_factory_functions.push_back(base::BindRepeating(
- &CreateMediaFoundationVEA,
- gpu_preferences.enable_media_foundation_vea_on_windows7));
- }
+ vea_factory_functions.push_back(base::BindRepeating(
+ &CreateMediaFoundationVEA,
+ gpu_preferences.enable_media_foundation_vea_on_windows7,
+ base::FeatureList::IsEnabled(kMediaFoundationAsyncH264Encoding) &&
+ !gpu::GpuDriverBugWorkarounds()
+ .disable_mediafoundation_async_h264_encoding));
#endif
return vea_factory_functions;
}
diff --git a/chromium/media/gpu/gpu_video_encode_accelerator_helpers.cc b/chromium/media/gpu/gpu_video_encode_accelerator_helpers.cc
index 2979dc754f7..151101e42aa 100644
--- a/chromium/media/gpu/gpu_video_encode_accelerator_helpers.cc
+++ b/chromium/media/gpu/gpu_video_encode_accelerator_helpers.cc
@@ -6,7 +6,7 @@
#include <algorithm>
-#include "base/logging.h"
+#include "base/check_op.h"
namespace media {
namespace {
diff --git a/chromium/media/gpu/h264_decoder.cc b/chromium/media/gpu/h264_decoder.cc
index 62c42d9393f..59ab81d16ba 100644
--- a/chromium/media/gpu/h264_decoder.cc
+++ b/chromium/media/gpu/h264_decoder.cc
@@ -437,7 +437,7 @@ void H264Decoder::ConstructReferencePicListsB(
std::sort(ref_pic_list_b1_.begin(), iter, POCAscCompare());
// Now add [3] and sort by ascending long_term_pic_num
- dpb_.GetShortTermRefPicsAppending(&ref_pic_list_b1_);
+ dpb_.GetLongTermRefPicsAppending(&ref_pic_list_b1_);
std::sort(ref_pic_list_b1_.begin() + num_short_refs, ref_pic_list_b1_.end(),
LongTermPicNumAscCompare());
diff --git a/chromium/media/gpu/h264_decoder_unittest.cc b/chromium/media/gpu/h264_decoder_unittest.cc
index b12d769a633..96703288876 100644
--- a/chromium/media/gpu/h264_decoder_unittest.cc
+++ b/chromium/media/gpu/h264_decoder_unittest.cc
@@ -9,11 +9,11 @@
#include <memory>
#include <string>
+#include "base/check.h"
#include "base/command_line.h"
#include "base/containers/queue.h"
#include "base/containers/span.h"
#include "base/files/file_util.h"
-#include "base/logging.h"
#include "media/base/test_data_util.h"
#include "media/gpu/h264_decoder.h"
#include "testing/gmock/include/gmock/gmock.h"
diff --git a/chromium/media/gpu/v4l2/BUILD.gn b/chromium/media/gpu/v4l2/BUILD.gn
index 51814e19868..88b72e36308 100644
--- a/chromium/media/gpu/v4l2/BUILD.gn
+++ b/chromium/media/gpu/v4l2/BUILD.gn
@@ -95,6 +95,7 @@ source_set("v4l2") {
"//media/gpu:buildflags",
"//media/gpu:common",
"//media/gpu/chromeos:common",
+ "//media/parsers",
"//third_party/libyuv",
"//ui/gfx/geometry",
"//ui/ozone",
@@ -116,7 +117,6 @@ source_set("v4l2") {
"//components/chromeos_camera:jpeg_encode_accelerator",
"//components/chromeos_camera:mjpeg_decode_accelerator",
"//media/gpu:video_frame_mapper_common",
- "//media/parsers",
]
}
}
diff --git a/chromium/media/gpu/v4l2/v4l2_device.cc b/chromium/media/gpu/v4l2/v4l2_device.cc
index ee5c4430e70..9b81f8046f2 100644
--- a/chromium/media/gpu/v4l2/v4l2_device.cc
+++ b/chromium/media/gpu/v4l2/v4l2_device.cc
@@ -47,6 +47,10 @@ namespace {
// Maximum number of requests that can be created.
constexpr size_t kMaxNumRequests = 32;
+gfx::Rect V4L2RectToGfxRect(const v4l2_rect& rect) {
+ return gfx::Rect(rect.left, rect.top, rect.width, rect.height);
+}
+
} // namespace
V4L2ExtCtrl::V4L2ExtCtrl(uint32_t id) {
@@ -236,9 +240,9 @@ scoped_refptr<VideoFrame> V4L2Buffer::GetVideoFrame() {
// We can create the VideoFrame only when using MMAP buffers.
if (v4l2_buffer_.memory != V4L2_MEMORY_MMAP) {
VLOGF(1) << "Cannot create video frame from non-MMAP buffer";
- // video_frame_ should be null since that's its default value.
- DCHECK_EQ(video_frame_, nullptr);
- return video_frame_;
+ // Allow NOTREACHED() on invalid argument because this is an internal
+ // method.
+ NOTREACHED();
}
// Create the video frame instance if requiring it for the first time.
@@ -881,6 +885,58 @@ base::Optional<struct v4l2_format> V4L2Queue::SetFormat(uint32_t fourcc,
return current_format_;
}
+std::pair<base::Optional<struct v4l2_format>, int> V4L2Queue::GetFormat() {
+ struct v4l2_format format;
+ memset(&format, 0, sizeof(format));
+ format.type = type_;
+ if (device_->Ioctl(VIDIOC_G_FMT, &format) != 0) {
+ VPQLOGF(2) << "Failed to get format";
+ return std::make_pair(base::nullopt, errno);
+ }
+
+ return std::make_pair(format, 0);
+}
+
+base::Optional<gfx::Rect> V4L2Queue::GetVisibleRect() {
+ // Some drivers prior to 4.13 only accept the non-MPLANE variant when using
+ // VIDIOC_G_SELECTION. This block can be removed once we stop supporting
+ // kernels < 4.13.
+ // For details, see the note at
+ // https://www.kernel.org/doc/html/latest/media/uapi/v4l/vidioc-g-selection.html
+ enum v4l2_buf_type compose_type;
+ switch (type_) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ compose_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ compose_type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ break;
+ default:
+ compose_type = type_;
+ break;
+ }
+
+ struct v4l2_selection selection = {};
+ selection.type = compose_type;
+ selection.target = V4L2_SEL_TGT_COMPOSE;
+ if (device_->Ioctl(VIDIOC_G_SELECTION, &selection) == 0) {
+ DVQLOGF(3) << "VIDIOC_G_SELECTION is supported";
+ return V4L2RectToGfxRect(selection.r);
+ }
+
+ // TODO(acourbot) using VIDIOC_G_CROP is considered legacy and can be
+ // removed once no active devices use it anymore.
+ DVQLOGF(3) << "Fallback to VIDIOC_G_CROP";
+ struct v4l2_crop crop = {};
+ crop.type = type_;
+ if (device_->Ioctl(VIDIOC_G_CROP, &crop) == 0) {
+ return V4L2RectToGfxRect(crop.c);
+ }
+
+ VQLOGF(1) << "Failed to get visible rect";
+ return base::nullopt;
+}
+
size_t V4L2Queue::AllocateBuffers(size_t count, enum v4l2_memory memory) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!free_buffers_);
@@ -906,13 +962,12 @@ size_t V4L2Queue::AllocateBuffers(size_t count, enum v4l2_memory memory) {
// This should not be required, but Tegra's VIDIOC_QUERYBUF will fail on
// output buffers if the number of specified planes does not exactly match the
// format.
- struct v4l2_format format = {.type = type_};
- int ret = device_->Ioctl(VIDIOC_G_FMT, &format);
- if (ret) {
- VPQLOGF(1) << "VIDIOC_G_FMT failed";
+ base::Optional<v4l2_format> format = GetFormat().first;
+ if (!format) {
+ VQLOGF(1) << "Cannot get format.";
return 0;
}
- planes_count_ = format.fmt.pix_mp.num_planes;
+ planes_count_ = format->fmt.pix_mp.num_planes;
DCHECK_LE(planes_count_, static_cast<size_t>(VIDEO_MAX_PLANES));
struct v4l2_requestbuffers reqbufs = {};
@@ -921,7 +976,7 @@ size_t V4L2Queue::AllocateBuffers(size_t count, enum v4l2_memory memory) {
reqbufs.memory = memory;
DVQLOGF(3) << "Requesting " << count << " buffers.";
- ret = device_->Ioctl(VIDIOC_REQBUFS, &reqbufs);
+ int ret = device_->Ioctl(VIDIOC_REQBUFS, &reqbufs);
if (ret) {
VPQLOGF(1) << "VIDIOC_REQBUFS failed";
return 0;
@@ -934,7 +989,7 @@ size_t V4L2Queue::AllocateBuffers(size_t count, enum v4l2_memory memory) {
// Now query all buffer information.
for (size_t i = 0; i < reqbufs.count; i++) {
- auto buffer = V4L2Buffer::Create(device_, type_, memory_, format, i);
+ auto buffer = V4L2Buffer::Create(device_, type_, memory_, *format, i);
if (!buffer) {
DeallocateBuffers();
@@ -1936,6 +1991,18 @@ void V4L2Device::SchedulePoll() {
device_poller_->SchedulePoll();
}
+base::Optional<struct v4l2_event> V4L2Device::DequeueEvent() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
+ struct v4l2_event event = {};
+
+ if (Ioctl(VIDIOC_DQEVENT, &event) != 0) {
+ VPLOGF(3) << "Failed to dequeue event";
+ return base::nullopt;
+ }
+
+ return event;
+}
+
V4L2RequestsQueue* V4L2Device::GetRequestsQueue() {
DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
@@ -1980,6 +2047,23 @@ bool V4L2Device::SetExtCtrls(uint32_t ctrl_class,
return Ioctl(VIDIOC_S_EXT_CTRLS, &ext_ctrls) == 0;
}
+base::Optional<struct v4l2_ext_control> V4L2Device::GetCtrl(uint32_t ctrl_id) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
+ struct v4l2_ext_control ctrl = {};
+ struct v4l2_ext_controls ext_ctrls = {};
+
+ ctrl.id = ctrl_id;
+ ext_ctrls.controls = &ctrl;
+ ext_ctrls.count = 1;
+
+ if (Ioctl(VIDIOC_G_EXT_CTRLS, &ext_ctrls) != 0) {
+ VPLOGF(3) << "Failed to get control";
+ return base::nullopt;
+ }
+
+ return ctrl;
+}
+
class V4L2Request {
public:
// Apply the passed controls to the request.
diff --git a/chromium/media/gpu/v4l2/v4l2_device.h b/chromium/media/gpu/v4l2/v4l2_device.h
index 346ec0c9495..310d4a4a1a5 100644
--- a/chromium/media/gpu/v4l2/v4l2_device.h
+++ b/chromium/media/gpu/v4l2/v4l2_device.h
@@ -293,6 +293,26 @@ class MEDIA_GPU_EXPORT V4L2Queue
size_t buffer_size)
WARN_UNUSED_RESULT;
+ // Returns the currently set format on the queue. The result is returned as
+ // a std::pair where the first member is the format, or base::nullopt if the
+ // format could not be obtained due to an ioctl error. The second member is
+ // only used in case of an error and contains the |errno| set by the failing
+ // ioctl. If the first member is not base::nullopt, the second member will
+ // always be zero.
+ //
+ // If the second member is 0, then the first member is guaranteed to have
+ // a valid value. So clients that are not interested in the precise error
+ // message can just check that the first member is valid and go on.
+ //
+ // This pair is used because not all failures to get the format are
+ // necessarily errors, so we need to way to let the use decide whether it
+ // is one or not.
+ std::pair<base::Optional<struct v4l2_format>, int> GetFormat();
+
+ // Codec-specific method to get the visible rectangle of the queue, using the
+ // VIDIOC_G_SELECTION ioctl if available, or VIDIOC_G_CROP as a fallback.
+ base::Optional<gfx::Rect> GetVisibleRect();
+
// Allocate |count| buffers for the current format of this queue, with a
// specific |memory| allocation, and returns the number of buffers allocated
// or zero if an error occurred, or if references to any previously allocated
@@ -693,6 +713,9 @@ class MEDIA_GPU_EXPORT V4L2Device
// to be called from V4L2Queue, clients should not need to call it directly.
void SchedulePoll();
+ // Attempt to dequeue a V4L2 event and return it.
+ base::Optional<struct v4l2_event> DequeueEvent();
+
// Returns requests queue to get free requests. A null pointer is returned if
// the queue creation failed or if requests are not supported.
V4L2RequestsQueue* GetRequestsQueue();
@@ -703,6 +726,10 @@ class MEDIA_GPU_EXPORT V4L2Device
// whether the operation succeeded.
bool SetExtCtrls(uint32_t ctrl_class, std::vector<V4L2ExtCtrl> ctrls);
+ // Get the value of a single control, or base::nullopt of the control is not
+ // exposed by the device.
+ base::Optional<struct v4l2_ext_control> GetCtrl(uint32_t ctrl_id);
+
protected:
friend class base::RefCountedThreadSafe<V4L2Device>;
V4L2Device();
diff --git a/chromium/media/gpu/v4l2/v4l2_image_processor_backend.cc b/chromium/media/gpu/v4l2/v4l2_image_processor_backend.cc
index 12eeef482fb..6498537e426 100644
--- a/chromium/media/gpu/v4l2/v4l2_image_processor_backend.cc
+++ b/chromium/media/gpu/v4l2/v4l2_image_processor_backend.cc
@@ -574,6 +574,33 @@ void V4L2ImageProcessorBackend::ProcessJobsTask() {
DCHECK_CALLED_ON_VALID_SEQUENCE(backend_sequence_checker_);
while (!input_job_queue_.empty()) {
+ if (!input_queue_->IsStreaming()) {
+ const VideoFrame& input_frame =
+ *(input_job_queue_.front()->input_frame.get());
+ const gfx::Size input_buffer_size(input_frame.stride(0),
+ input_frame.coded_size().height());
+ if (!ReconfigureV4L2Format(input_buffer_size, input_frame.visible_rect(),
+ V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)) {
+ NotifyError();
+ return;
+ }
+ }
+
+ if (input_job_queue_.front()
+ ->output_frame && // output_frame is nullptr in ALLOCATE mode.
+ !output_queue_->IsStreaming()) {
+ const VideoFrame& output_frame =
+ *(input_job_queue_.front()->output_frame.get());
+ const gfx::Size output_buffer_size(output_frame.stride(0),
+ output_frame.coded_size().height());
+ if (!ReconfigureV4L2Format(output_buffer_size,
+ output_frame.visible_rect(),
+ V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)) {
+ NotifyError();
+ return;
+ }
+ }
+
// We need one input and one output buffer to schedule the job
auto input_buffer = input_queue_->GetFreeBuffer();
auto output_buffer = output_queue_->GetFreeBuffer();
@@ -641,6 +668,39 @@ bool V4L2ImageProcessorBackend::ApplyCrop(const gfx::Rect& visible_rect,
return true;
}
+bool V4L2ImageProcessorBackend::ReconfigureV4L2Format(
+ const gfx::Size& size,
+ const gfx::Rect& visible_rect,
+ enum v4l2_buf_type type) {
+ v4l2_format format{};
+ format.type = type;
+ if (device_->Ioctl(VIDIOC_G_FMT, &format) != 0) {
+ VPLOGF(1) << "ioctl() failed: VIDIOC_G_FMT";
+ return false;
+ }
+
+ if (static_cast<int>(format.fmt.pix_mp.width) == size.width() &&
+ static_cast<int>(format.fmt.pix_mp.height) == size.height()) {
+ return true;
+ }
+ format.fmt.pix_mp.width = size.width();
+ format.fmt.pix_mp.height = size.height();
+ if (device_->Ioctl(VIDIOC_S_FMT, &format) != 0) {
+ VPLOGF(1) << "ioctl() failed: VIDIOC_S_FMT";
+ return false;
+ }
+ if (!ApplyCrop(visible_rect, type)) {
+ return false;
+ }
+
+ auto queue = device_->GetQueue(type);
+ const size_t num_buffers = queue->AllocatedBuffersCount();
+ const v4l2_memory memory_type = queue->GetMemoryType();
+ DCHECK_GT(num_buffers, 0u);
+ return queue->DeallocateBuffers() &&
+ AllocateV4L2Buffers(queue.get(), num_buffers, memory_type);
+}
+
bool V4L2ImageProcessorBackend::CreateInputBuffers() {
VLOGF(2);
DCHECK_CALLED_ON_VALID_SEQUENCE(backend_sequence_checker_);
diff --git a/chromium/media/gpu/v4l2/v4l2_image_processor_backend.h b/chromium/media/gpu/v4l2/v4l2_image_processor_backend.h
index 5214eeb6414..bd1c78ac4e9 100644
--- a/chromium/media/gpu/v4l2/v4l2_image_processor_backend.h
+++ b/chromium/media/gpu/v4l2/v4l2_image_processor_backend.h
@@ -135,6 +135,10 @@ class MEDIA_GPU_EXPORT V4L2ImageProcessorBackend
bool CreateOutputBuffers();
// Specify |visible_rect| to v4l2 |type| queue.
bool ApplyCrop(const gfx::Rect& visible_rect, enum v4l2_buf_type type);
+ // Reconfigure |size| and |visible_rect| to v4l2 |type| queue.
+ bool ReconfigureV4L2Format(const gfx::Size& size,
+ const gfx::Rect& visible_rect,
+ enum v4l2_buf_type type);
// Callback of VideoFrame destruction. Since VideoFrame destruction
// callback might be executed on any sequence, we use a thunk to post the
diff --git a/chromium/media/gpu/v4l2/v4l2_mjpeg_decode_accelerator.cc b/chromium/media/gpu/v4l2/v4l2_mjpeg_decode_accelerator.cc
index ff21cd97f99..9be3d4857bf 100644
--- a/chromium/media/gpu/v4l2/v4l2_mjpeg_decode_accelerator.cc
+++ b/chromium/media/gpu/v4l2/v4l2_mjpeg_decode_accelerator.cc
@@ -756,18 +756,16 @@ void V4L2MjpegDecodeAccelerator::DevicePollTask() {
bool V4L2MjpegDecodeAccelerator::DequeueSourceChangeEvent() {
DCHECK(decoder_task_runner_->BelongsToCurrentThread());
- struct v4l2_event ev;
- memset(&ev, 0, sizeof(ev));
-
- if (device_->Ioctl(VIDIOC_DQEVENT, &ev) == 0) {
- if (ev.type == V4L2_EVENT_SOURCE_CHANGE) {
- VLOGF(2) << ": got source change event: " << ev.u.src_change.changes;
- if (ev.u.src_change.changes & V4L2_EVENT_SRC_CH_RESOLUTION) {
+ if (base::Optional<struct v4l2_event> event = device_->DequeueEvent()) {
+ if (event->type == V4L2_EVENT_SOURCE_CHANGE) {
+ VLOGF(2) << ": got source change event: " << event->u.src_change.changes;
+ if (event->u.src_change.changes & V4L2_EVENT_SRC_CH_RESOLUTION) {
return true;
}
VLOGF(1) << "unexpected source change event.";
} else {
- VLOGF(1) << "got an event (" << ev.type << ") we haven't subscribed to.";
+ VLOGF(1) << "got an event (" << event->type
+ << ") we haven't subscribed to.";
}
} else {
VLOGF(1) << "dequeue event failed.";
diff --git a/chromium/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.cc b/chromium/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.cc
index 4f9ff1d5a67..dd2c2e853eb 100644
--- a/chromium/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.cc
+++ b/chromium/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.cc
@@ -649,15 +649,12 @@ bool V4L2SliceVideoDecodeAccelerator::CreateOutputBuffers() {
// Since VdaVideoDecoder doesn't allocate PictureBuffer with size adjusted by
// itself, we have to adjust here.
- struct v4l2_format format;
- memset(&format, 0, sizeof(format));
- format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
-
- if (device_->Ioctl(VIDIOC_G_FMT, &format) != 0) {
- PLOG(ERROR) << "Failed getting OUTPUT format";
+ auto ret = input_queue_->GetFormat().first;
+ if (!ret) {
NOTIFY_ERROR(PLATFORM_FAILURE);
return false;
}
+ struct v4l2_format format = std::move(*ret);
format.fmt.pix_mp.width = pic_size.width();
format.fmt.pix_mp.height = pic_size.height();
@@ -669,13 +666,12 @@ bool V4L2SliceVideoDecodeAccelerator::CreateOutputBuffers() {
}
// Get the coded size from the CAPTURE queue
- memset(&format, 0, sizeof(format));
- format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
- if (device_->Ioctl(VIDIOC_G_FMT, &format) != 0) {
- PLOG(ERROR) << "Failed getting CAPTURE format";
+ ret = output_queue_->GetFormat().first;
+ if (!ret) {
NOTIFY_ERROR(PLATFORM_FAILURE);
return false;
}
+ format = std::move(*ret);
coded_size_.SetSize(base::checked_cast<int>(format.fmt.pix_mp.width),
base::checked_cast<int>(format.fmt.pix_mp.height));
diff --git a/chromium/media/gpu/v4l2/v4l2_slice_video_decoder.cc b/chromium/media/gpu/v4l2/v4l2_slice_video_decoder.cc
index aa5526ef711..28e1b3b7e4a 100644
--- a/chromium/media/gpu/v4l2/v4l2_slice_video_decoder.cc
+++ b/chromium/media/gpu/v4l2/v4l2_slice_video_decoder.cc
@@ -425,13 +425,12 @@ void V4L2SliceVideoDecoder::ChangeResolution(gfx::Size pic_size,
client_->PrepareChangeResolution();
}
-void V4L2SliceVideoDecoder::OnPipelineFlushed() {
+void V4L2SliceVideoDecoder::ApplyResolutionChange() {
DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_);
DVLOGF(3);
DCHECK(continue_change_resolution_cb_);
- decoder_task_runner_->PostTask(FROM_HERE,
- std::move(continue_change_resolution_cb_));
+ std::move(continue_change_resolution_cb_).Run();
}
void V4L2SliceVideoDecoder::ContinueChangeResolution(
diff --git a/chromium/media/gpu/v4l2/v4l2_slice_video_decoder.h b/chromium/media/gpu/v4l2/v4l2_slice_video_decoder.h
index 801ededec56..d5b82bbf824 100644
--- a/chromium/media/gpu/v4l2/v4l2_slice_video_decoder.h
+++ b/chromium/media/gpu/v4l2/v4l2_slice_video_decoder.h
@@ -55,7 +55,7 @@ class MEDIA_GPU_EXPORT V4L2SliceVideoDecoder
const OutputCB& output_cb) override;
void Reset(base::OnceClosure closure) override;
void Decode(scoped_refptr<DecoderBuffer> buffer, DecodeCB decode_cb) override;
- void OnPipelineFlushed() override;
+ void ApplyResolutionChange() override;
// V4L2VideoDecoderBackend::Client implementation
void OnBackendError() override;
diff --git a/chromium/media/gpu/v4l2/v4l2_vda_helpers.cc b/chromium/media/gpu/v4l2/v4l2_vda_helpers.cc
index 86d2ccd8234..f520d93be0f 100644
--- a/chromium/media/gpu/v4l2/v4l2_vda_helpers.cc
+++ b/chromium/media/gpu/v4l2/v4l2_vda_helpers.cc
@@ -184,6 +184,7 @@ bool H264InputBufferFragmentSplitter::AdvanceFrameFragment(const uint8_t* data,
h264_parser_->SetStream(data, size);
H264NALU nalu;
H264Parser::Result result;
+ bool has_frame_data = false;
*endpos = 0;
// Keep on peeking the next NALs while they don't indicate a frame
@@ -197,7 +198,8 @@ bool H264InputBufferFragmentSplitter::AdvanceFrameFragment(const uint8_t* data,
}
if (result == H264Parser::kEOStream) {
// We've reached the end of the buffer before finding a frame boundary.
- partial_frame_pending_ = true;
+ if (has_frame_data)
+ partial_frame_pending_ = true;
*endpos = size;
return true;
}
@@ -207,6 +209,8 @@ bool H264InputBufferFragmentSplitter::AdvanceFrameFragment(const uint8_t* data,
if (nalu.size < 1)
return false;
+ has_frame_data = true;
+
// For these two, if the "first_mb_in_slice" field is zero, start a
// new frame and return. This field is Exp-Golomb coded starting on
// the eighth data bit of the NAL; a zero value is encoded with a
diff --git a/chromium/media/gpu/v4l2/v4l2_video_decode_accelerator.cc b/chromium/media/gpu/v4l2/v4l2_video_decode_accelerator.cc
index 7147d9f81f7..4a581cab841 100644
--- a/chromium/media/gpu/v4l2/v4l2_video_decode_accelerator.cc
+++ b/chromium/media/gpu/v4l2/v4l2_video_decode_accelerator.cc
@@ -419,8 +419,6 @@ void V4L2VideoDecodeAccelerator::AssignPictureBuffersTask(
for (auto&& buffer : v4l2_buffers) {
const int i = buffer.BufferId();
- DCHECK(buffers[i].size() == egl_image_size_);
-
OutputRecord& output_record = output_buffer_map_[i];
DCHECK_EQ(output_record.egl_image, EGL_NO_IMAGE_KHR);
DCHECK_EQ(output_record.picture_id, -1);
@@ -1391,17 +1389,15 @@ bool V4L2VideoDecodeAccelerator::DequeueResolutionChangeEvent() {
DCHECK_NE(decoder_state_, kUninitialized);
DVLOGF(3);
- struct v4l2_event ev;
- memset(&ev, 0, sizeof(ev));
-
- while (device_->Ioctl(VIDIOC_DQEVENT, &ev) == 0) {
- if (ev.type == V4L2_EVENT_SOURCE_CHANGE) {
- if (ev.u.src_change.changes & V4L2_EVENT_SRC_CH_RESOLUTION) {
+ while (base::Optional<struct v4l2_event> event = device_->DequeueEvent()) {
+ if (event->type == V4L2_EVENT_SOURCE_CHANGE) {
+ if (event->u.src_change.changes & V4L2_EVENT_SRC_CH_RESOLUTION) {
VLOGF(2) << "got resolution change event.";
return true;
}
} else {
- VLOGF(1) << "got an event (" << ev.type << ") we haven't subscribed to.";
+ VLOGF(1) << "got an event (" << event->type
+ << ") we haven't subscribed to.";
}
}
return false;
@@ -2084,18 +2080,19 @@ bool V4L2VideoDecodeAccelerator::GetFormatInfo(struct v4l2_format* format,
DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread());
*again = false;
- memset(format, 0, sizeof(*format));
- format->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
- if (device_->Ioctl(VIDIOC_G_FMT, format) != 0) {
- if (errno == EINVAL) {
+
+ auto ret = output_queue_->GetFormat();
+ switch (ret.second) {
+ case 0:
+ *format = *ret.first;
+ break;
+ case EINVAL:
// EINVAL means we haven't seen sufficient stream to decode the format.
*again = true;
return true;
- } else {
- PLOG(ERROR) << "ioctl() failed: VIDIOC_G_FMT";
+ default:
NOTIFY_ERROR(PLATFORM_FAILURE);
return false;
- }
}
// Make sure we are still getting the format we set on initialization.
@@ -2146,30 +2143,11 @@ gfx::Size V4L2VideoDecodeAccelerator::GetVisibleSize(
const gfx::Size& coded_size) {
DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread());
- struct v4l2_rect* visible_rect;
- struct v4l2_selection selection_arg;
- memset(&selection_arg, 0, sizeof(selection_arg));
- selection_arg.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- selection_arg.target = V4L2_SEL_TGT_COMPOSE;
-
- if (device_->Ioctl(VIDIOC_G_SELECTION, &selection_arg) == 0) {
- DVLOGF(3) << "VIDIOC_G_SELECTION is supported";
- visible_rect = &selection_arg.r;
- } else {
- DVLOGF(3) << "Fallback to VIDIOC_G_CROP";
- struct v4l2_crop crop_arg;
- memset(&crop_arg, 0, sizeof(crop_arg));
- crop_arg.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
-
- if (device_->Ioctl(VIDIOC_G_CROP, &crop_arg) != 0) {
- VPLOGF(1) << "ioctl() VIDIOC_G_CROP failed";
- return coded_size;
- }
- visible_rect = &crop_arg.c;
+ auto ret = output_queue_->GetVisibleRect();
+ if (!ret) {
+ return coded_size;
}
-
- gfx::Rect rect(visible_rect->left, visible_rect->top, visible_rect->width,
- visible_rect->height);
+ gfx::Rect rect = std::move(*ret);
DVLOGF(3) << "visible rectangle is " << rect.ToString();
if (!gfx::Rect(coded_size).Contains(rect)) {
DVLOGF(3) << "visible rectangle " << rect.ToString()
@@ -2398,11 +2376,10 @@ bool V4L2VideoDecodeAccelerator::CreateOutputBuffers() {
DCHECK(output_buffer_map_.empty());
// Number of output buffers we need.
- struct v4l2_control ctrl;
- memset(&ctrl, 0, sizeof(ctrl));
- ctrl.id = V4L2_CID_MIN_BUFFERS_FOR_CAPTURE;
- IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_G_CTRL, &ctrl);
- output_dpb_size_ = ctrl.value;
+ auto ctrl = device_->GetCtrl(V4L2_CID_MIN_BUFFERS_FOR_CAPTURE);
+ if (!ctrl)
+ return false;
+ output_dpb_size_ = ctrl->value;
// Output format setup in Initialize().
diff --git a/chromium/media/gpu/v4l2/v4l2_video_decoder_backend_stateless.cc b/chromium/media/gpu/v4l2/v4l2_video_decoder_backend_stateless.cc
index 3ca859a4caf..b8c3400a990 100644
--- a/chromium/media/gpu/v4l2/v4l2_video_decoder_backend_stateless.cc
+++ b/chromium/media/gpu/v4l2/v4l2_video_decoder_backend_stateless.cc
@@ -529,13 +529,12 @@ bool V4L2StatelessVideoDecoderBackend::ApplyResolution(
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_EQ(input_queue_->QueuedBuffersCount(), 0u);
- struct v4l2_format format = {};
-
- format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
- if (device_->Ioctl(VIDIOC_G_FMT, &format) != 0) {
+ auto ret = input_queue_->GetFormat().first;
+ if (!ret) {
VPLOGF(1) << "Failed getting OUTPUT format";
return false;
}
+ struct v4l2_format format = std::move(*ret);
format.fmt.pix_mp.width = pic_size.width();
format.fmt.pix_mp.height = pic_size.height();
diff --git a/chromium/media/gpu/v4l2/v4l2_video_encode_accelerator.cc b/chromium/media/gpu/v4l2/v4l2_video_encode_accelerator.cc
index 3694d71f1b1..8c7ea443927 100644
--- a/chromium/media/gpu/v4l2/v4l2_video_encode_accelerator.cc
+++ b/chromium/media/gpu/v4l2/v4l2_video_encode_accelerator.cc
@@ -203,6 +203,13 @@ bool V4L2VideoEncodeAccelerator::Initialize(const Config& config,
TRACE_EVENT0("media,gpu", "V4L2VEA::Initialize");
VLOGF(2) << ": " << config.AsHumanReadableString();
+ // V4L2VEA doesn't support temporal layers but we let it pass here to support
+ // simulcast.
+ if (config.HasSpatialLayer()) {
+ VLOGF(1) << "Spatial layer encoding is supported";
+ return false;
+ }
+
encoder_input_visible_rect_ = gfx::Rect(config.input_visible_size);
client_ptr_factory_.reset(new base::WeakPtrFactory<Client>(client));
@@ -461,6 +468,11 @@ bool V4L2VideoEncodeAccelerator::AllocateImageProcessorOutputBuffers(
DCHECK(image_processor_);
DCHECK_EQ(image_processor_->output_mode(),
ImageProcessor::OutputMode::IMPORT);
+
+ // The existing buffers in |image_processor_output_buffers_| may be alive
+ // until they are actually consumed by the encoder driver, after they are
+ // destroyed here.
+ image_processor_output_buffers_.clear();
image_processor_output_buffers_.resize(count);
const ImageProcessor::PortConfig& output_config =
image_processor_->output_config();
@@ -757,19 +769,36 @@ bool V4L2VideoEncodeAccelerator::ReconfigureFormatIfNeeded(
}
if (!input_buffer_map_.empty()) {
- // TODO(crbug.com/1060057): Handle coded_size change.
- if (frame.coded_size() != input_frame_size_) {
- VLOGF(1) << "Input frame size is changed during encoding"
- << ", frame.coded_size()=" << frame.coded_size().ToString()
- << ", input_frame_size=" << input_frame_size_.ToString();
+ // ReconfigureFormatIfNeeded() has been called with the first VideoFrame.
+ // We checks here we need to (re)create ImageProcessor because the visible
+ // rectangle of |frame| differs from the first VideoFrame.
+ // |frame.natural_size()| is the size to be encoded. It must be the same as
+ // |encoder_input_visible_rect_.size()|, otherwise VEA client must recreate
+ // VEA with the new encoder resolution.
+ if (frame.natural_size() != encoder_input_visible_rect_.size()) {
+ VLOGF(1) << "Encoder resolution is changed during encoding"
+ << ", frame.natural_size()=" << frame.natural_size().ToString()
+ << ", encoder_input_visible_rect_="
+ << input_frame_size_.ToString();
return false;
}
- return true;
- }
+ if (frame.coded_size() == input_frame_size_) {
+ return true;
+ }
+
+ // If a dimension of the underlying VideoFrame varies during video encoding
+ // (i.e. frame.coded_size() != input_frame_size_), we (re)create
+ // ImageProcessor to crop the VideoFrame, |frame.visible_rect()| ->
+ // |encoder_input_visible_rect_|.
+ // TODO(hiroh): if |frame.coded_size()| is the same as VideoFrame::
+ // DetermineAlignedSize(input_format, encoder_input_visible_rect_.size())
+ // and don't need a pixel format conversion, image processor is not
+ // necessary but we should rather NegotiateInputFormat().
+ } else if (frame.coded_size() == input_frame_size_) {
+ // This path is for the first frame on Encode().
+ // Height and width that V4L2VEA needs to configure.
+ const gfx::Size buffer_size(frame.stride(0), frame.coded_size().height());
- // Height and width that V4L2VEA needs to configure.
- const gfx::Size buffer_size(frame.stride(0), frame.coded_size().height());
- if (frame.coded_size() == input_frame_size_) {
// A buffer given by client is allocated with the same dimension using
// minigbm. However, it is possible that stride and height are different
// from ones adjusted by a driver.
@@ -792,13 +821,30 @@ bool V4L2VideoEncodeAccelerator::ReconfigureFormatIfNeeded(
// The |frame| dimension is different from the resolution configured to
// V4L2VEA. This is the case that V4L2VEA needs to create ImageProcessor for
- // scaling. Update |input_frame_size_| to check if succeeding frames'
- // dimensions are not different from one of the first frame.
+ // cropping and scaling. Update |input_frame_size_| to check if succeeding
+ // frames' dimensions are not different from the current one.
input_frame_size_ = frame.coded_size();
- return CreateImageProcessor(frame.layout(), device_input_layout_->format(),
- device_input_layout_->coded_size(),
- frame.visible_rect(),
- encoder_input_visible_rect_);
+ if (!CreateImageProcessor(frame.layout(), device_input_layout_->format(),
+ device_input_layout_->coded_size(),
+ frame.visible_rect(),
+ encoder_input_visible_rect_)) {
+ return false;
+ }
+
+ if (gfx::Size(image_processor_->output_config().planes[0].stride,
+ image_processor_->output_config().size.height()) !=
+ device_input_layout_->coded_size()) {
+ VLOGF(1) << "Image Processor's output buffer's size is different from "
+ << "input buffer size configure to the encoder driver. "
+ << "ip's output buffer size: "
+ << gfx::Size(image_processor_->output_config().planes[0].stride,
+ image_processor_->output_config().size.height())
+ .ToString()
+ << ", encoder's input buffer size: "
+ << device_input_layout_->coded_size().ToString();
+ return false;
+ }
+ return true;
}
void V4L2VideoEncodeAccelerator::InputImageProcessorTask() {
@@ -1553,8 +1599,28 @@ bool V4L2VideoEncodeAccelerator::SetFormats(VideoPixelFormat input_format,
if (!SetOutputFormat(output_profile))
return false;
- auto v4l2_format =
- NegotiateInputFormat(input_format, encoder_input_visible_rect_.size());
+ gfx::Size input_size = encoder_input_visible_rect_.size();
+ if (native_input_mode_) {
+ DCHECK(!image_processor_gmb_factory_);
+ image_processor_gmb_factory_ =
+ gpu::GpuMemoryBufferFactory::CreateNativeType(nullptr);
+ if (!image_processor_gmb_factory_) {
+ VLOGF(1) << "Failed to create GpuMemoryBufferFactory";
+ return false;
+ }
+
+ auto input_layout = GetPlatformVideoFrameLayout(
+ image_processor_gmb_factory_.get(), input_format,
+ encoder_input_visible_rect_.size(),
+ gfx::BufferUsage::SCANOUT_VEA_READ_CAMERA_AND_CPU_READ_WRITE);
+ if (!input_layout)
+ return false;
+ input_size = gfx::Size(input_layout->planes()[0].stride,
+ input_layout->coded_size().height());
+ }
+
+ DCHECK(input_frame_size_.IsEmpty());
+ auto v4l2_format = NegotiateInputFormat(input_format, input_size);
if (!v4l2_format)
return false;
diff --git a/chromium/media/gpu/vaapi/BUILD.gn b/chromium/media/gpu/vaapi/BUILD.gn
index 7ef509fafae..2524a1c31b5 100644
--- a/chromium/media/gpu/vaapi/BUILD.gn
+++ b/chromium/media/gpu/vaapi/BUILD.gn
@@ -109,7 +109,6 @@ source_set("vaapi") {
}
if (use_x11) {
- configs += [ "//build/config/linux:x11" ]
deps += [ "//ui/gfx/x" ]
sources += [
"vaapi_picture_tfp.cc",
@@ -167,7 +166,6 @@ source_set("common") {
}
if (use_x11) {
- configs += [ "//build/config/linux:x11" ]
deps += [ "//ui/gfx/x" ]
}
@@ -195,6 +193,7 @@ source_set("unit_test") {
"h264_encoder_unittest.cc",
"vaapi_image_decode_accelerator_worker_unittest.cc",
"vaapi_video_decode_accelerator_unittest.cc",
+ "vaapi_video_encode_accelerator_unittest.cc",
]
deps = [
":common",
@@ -285,6 +284,7 @@ test("vaapi_unittest") {
":common",
":vaapi_utils_unittest",
"//base",
+ "//base/test:test_support",
"//media/gpu/test:helpers",
"//testing/gtest",
]
diff --git a/chromium/media/gpu/vaapi/h264_encoder.h b/chromium/media/gpu/vaapi/h264_encoder.h
index 517ff2a5ede..908e1f7bf78 100644
--- a/chromium/media/gpu/vaapi/h264_encoder.h
+++ b/chromium/media/gpu/vaapi/h264_encoder.h
@@ -53,7 +53,7 @@ class H264Encoder : public AcceleratedVideoEncoder {
// Bitrate window size in bits.
unsigned int cpb_size_bits;
- // Quantization parameter.
+ // Quantization parameter. Their ranges are 0-51.
int initial_qp;
ScalingSettings scaling_settings;
diff --git a/chromium/media/gpu/vaapi/vaapi_image_decode_accelerator_worker_unittest.cc b/chromium/media/gpu/vaapi/vaapi_image_decode_accelerator_worker_unittest.cc
index 31f840b5695..0767729e06d 100644
--- a/chromium/media/gpu/vaapi/vaapi_image_decode_accelerator_worker_unittest.cc
+++ b/chromium/media/gpu/vaapi/vaapi_image_decode_accelerator_worker_unittest.cc
@@ -14,11 +14,12 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "base/bind.h"
+#include "base/check_op.h"
#include "base/containers/span.h"
-#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/memory/scoped_refptr.h"
+#include "base/notreached.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "gpu/config/gpu_finch_features.h"
diff --git a/chromium/media/gpu/vaapi/vaapi_image_processor_backend.cc b/chromium/media/gpu/vaapi/vaapi_image_processor_backend.cc
index 6f63dafba87..4cb6bceda56 100644
--- a/chromium/media/gpu/vaapi/vaapi_image_processor_backend.cc
+++ b/chromium/media/gpu/vaapi/vaapi_image_processor_backend.cc
@@ -25,6 +25,7 @@
namespace media {
+#if defined(OS_CHROMEOS)
namespace {
// UMA errors that the VaapiImageProcessorBackend class reports.
enum class VaIPFailure {
@@ -67,6 +68,7 @@ bool IsSupported(uint32_t input_va_fourcc,
}
} // namespace
+#endif
// static
std::unique_ptr<ImageProcessorBackend> VaapiImageProcessorBackend::Create(
@@ -78,8 +80,7 @@ std::unique_ptr<ImageProcessorBackend> VaapiImageProcessorBackend::Create(
// VaapiImageProcessorBackend supports ChromeOS only.
#if !defined(OS_CHROMEOS)
return nullptr;
-#endif
-
+#else
auto input_vafourcc = input_config.fourcc.ToVAFourCC();
if (!input_vafourcc) {
VLOGF(2) << "Input fourcc " << input_config.fourcc.ToString()
@@ -146,6 +147,7 @@ std::unique_ptr<ImageProcessorBackend> VaapiImageProcessorBackend::Create(
return base::WrapUnique<ImageProcessorBackend>(new VaapiImageProcessorBackend(
std::move(vaapi_wrapper), input_config, output_config, OutputMode::IMPORT,
std::move(error_cb), std::move(backend_task_runner)));
+#endif
}
VaapiImageProcessorBackend::VaapiImageProcessorBackend(
diff --git a/chromium/media/gpu/vaapi/vaapi_jpeg_encoder.cc b/chromium/media/gpu/vaapi/vaapi_jpeg_encoder.cc
index 854bababd3f..643dc91ca29 100644
--- a/chromium/media/gpu/vaapi/vaapi_jpeg_encoder.cc
+++ b/chromium/media/gpu/vaapi/vaapi_jpeg_encoder.cc
@@ -10,7 +10,7 @@
#include <stddef.h>
#include <string.h>
-#include "base/logging.h"
+#include "base/check_op.h"
#include "base/numerics/ranges.h"
#include "base/numerics/safe_conversions.h"
#include "base/stl_util.h"
diff --git a/chromium/media/gpu/vaapi/vaapi_unittest.cc b/chromium/media/gpu/vaapi/vaapi_unittest.cc
index 7de8dec2a07..d3d459fadf8 100644
--- a/chromium/media/gpu/vaapi/vaapi_unittest.cc
+++ b/chromium/media/gpu/vaapi/vaapi_unittest.cc
@@ -12,15 +12,14 @@
#include <va/va.h>
-#include "base/at_exit.h"
-#include "base/command_line.h"
#include "base/files/file.h"
#include "base/files/scoped_file.h"
-#include "base/logging.h"
#include "base/optional.h"
#include "base/process/launch.h"
#include "base/stl_util.h"
#include "base/strings/string_split.h"
+#include "base/test/launcher/unit_test_launcher.h"
+#include "base/test/test_suite.h"
#include "media/gpu/vaapi/vaapi_wrapper.h"
namespace media {
@@ -216,17 +215,11 @@ TEST_F(VaapiTest, DefaultEntrypointIsSupported) {
} // namespace media
int main(int argc, char** argv) {
- testing::InitGoogleTest(&argc, argv);
- base::CommandLine::Init(argc, argv);
- base::ShadowingAtExitManager at_exit_manager;
-
- // Needed to enable DVLOG through --vmodule.
- logging::LoggingSettings settings;
- settings.logging_dest =
- logging::LOG_TO_SYSTEM_DEBUG_LOG | logging::LOG_TO_STDERR;
- LOG_ASSERT(logging::InitLogging(settings));
+ base::TestSuite test_suite(argc, argv);
media::VaapiWrapper::PreSandboxInitialization();
- return RUN_ALL_TESTS();
+ return base::LaunchUnitTests(
+ argc, argv,
+ base::BindOnce(&base::TestSuite::Run, base::Unretained(&test_suite)));
}
diff --git a/chromium/media/gpu/vaapi/vaapi_video_decode_accelerator.cc b/chromium/media/gpu/vaapi/vaapi_video_decode_accelerator.cc
index 5020b2b8bf9..63ea51830ee 100644
--- a/chromium/media/gpu/vaapi/vaapi_video_decode_accelerator.cc
+++ b/chromium/media/gpu/vaapi/vaapi_video_decode_accelerator.cc
@@ -458,11 +458,25 @@ void VaapiVideoDecodeAccelerator::DecodeTask() {
"The visible rectangle is not contained by the picture size",
UNREADABLE_INPUT, );
VLOGF(2) << "Decoder requesting a new set of surfaces";
+ size_t required_num_of_pictures = decoder_->GetRequiredNumOfPictures();
+ if (buffer_allocation_mode_ == BufferAllocationMode::kNone &&
+ profile_ >= H264PROFILE_MIN && profile_ <= H264PROFILE_MAX) {
+ // For H.264, the decoder might request too few pictures. In
+ // BufferAllocationMode::kNone, this can cause us to do a lot of busy
+ // work waiting for picture buffers to come back from the client (see
+ // crbug.com/910986#c32). This is a workaround to increase the
+ // likelihood that we don't have to wait on buffers to come back from
+ // the client. |kNumOfPics| is picked to mirror the value returned by
+ // VP9Decoder::GetRequiredNumOfPictures().
+ constexpr size_t kMinNumOfPics = 13u;
+ required_num_of_pictures =
+ std::max(kMinNumOfPics, required_num_of_pictures);
+ }
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&VaapiVideoDecodeAccelerator::InitiateSurfaceSetChange,
- weak_this_, decoder_->GetRequiredNumOfPictures(), pic_size,
+ weak_this_, required_num_of_pictures, pic_size,
decoder_->GetNumReferenceFrames(), visible_rect));
// We'll get rescheduled once ProvidePictureBuffers() finishes.
return;
@@ -1158,12 +1172,11 @@ VaapiVideoDecodeAccelerator::DecideBufferAllocationMode() {
// On Gemini Lake, Kaby Lake and later we can pass to libva the client's
// PictureBuffers to decode onto, which skips the use of the Vpp unit and its
// associated format reconciliation copy, avoiding all internal buffer
- // allocations. This only works for VP8 and VP9: H264 GetNumReferenceFrames()
- // depends on the bitstream and sometimes it's not enough to cover the amount
- // of frames needed by the client pipeline (see b/133733739).
+ // allocations.
// TODO(crbug.com/911754): Enable for VP9 Profile 2.
if (IsGeminiLakeOrLater() &&
- (profile_ == VP9PROFILE_PROFILE0 || profile_ == VP8PROFILE_ANY)) {
+ (profile_ == VP9PROFILE_PROFILE0 || profile_ == VP8PROFILE_ANY ||
+ (profile_ >= H264PROFILE_MIN && profile_ <= H264PROFILE_MAX))) {
// Add one to the reference frames for the one being currently egressed, and
// an extra allocation for both |client_| and |decoder_|, see
// crrev.com/c/1576560.
@@ -1172,18 +1185,18 @@ VaapiVideoDecodeAccelerator::DecideBufferAllocationMode() {
return BufferAllocationMode::kNone;
}
- // If we're here, we have to use the Vpp unit and allocate buffers for
- // |decoder_|; usually we'd have to allocate the |decoder_|s
- // GetRequiredNumOfPictures() internally, we can allocate just |decoder_|s
- // GetNumReferenceFrames() + 1. Moreover, we also request the |client_| to
- // allocate less than the usual |decoder_|s GetRequiredNumOfPictures().
-
- // Another +1 is experimentally needed for high-to-high resolution changes.
+ // For H.264 on older devices, another +1 is experimentally needed for
+ // high-to-high resolution changes.
// TODO(mcasas): Figure out why and why only H264, see crbug.com/912295 and
// http://crrev.com/c/1363807/9/media/gpu/h264_decoder.cc#1449.
if (profile_ >= H264PROFILE_MIN && profile_ <= H264PROFILE_MAX)
return BufferAllocationMode::kReduced;
+ // If we're here, we have to use the Vpp unit and allocate buffers for
+ // |decoder_|; usually we'd have to allocate the |decoder_|s
+ // GetRequiredNumOfPictures() internally, we can allocate just |decoder_|s
+ // GetNumReferenceFrames() + 1. Moreover, we also request the |client_| to
+ // allocate less than the usual |decoder_|s GetRequiredNumOfPictures().
return BufferAllocationMode::kSuperReduced;
}
diff --git a/chromium/media/gpu/vaapi/vaapi_video_decode_accelerator_unittest.cc b/chromium/media/gpu/vaapi/vaapi_video_decode_accelerator_unittest.cc
index 280fad78379..12daaefd865 100644
--- a/chromium/media/gpu/vaapi/vaapi_video_decode_accelerator_unittest.cc
+++ b/chromium/media/gpu/vaapi/vaapi_video_decode_accelerator_unittest.cc
@@ -37,10 +37,10 @@ struct TestParams {
constexpr int32_t kBitstreamId = 123;
constexpr size_t kInputSize = 256;
-constexpr size_t kNumPictures = 4;
+constexpr size_t kNumPictures = 14;
const gfx::Size kPictureSize(64, 48);
-constexpr size_t kNewNumPictures = 3;
+constexpr size_t kNewNumPictures = 13;
const gfx::Size kNewPictureSize(64, 48);
MATCHER_P2(IsExpectedDecoderBuffer, data_size, decrypt_config, "") {
@@ -488,6 +488,7 @@ TEST_P(VaapiVideoDecodeAcceleratorTest,
constexpr TestParams kTestCases[] = {
{H264PROFILE_MIN, false /* decode_using_client_picture_buffers */},
+ {H264PROFILE_MIN, true /* decode_using_client_picture_buffers */},
{VP8PROFILE_MIN, false /* decode_using_client_picture_buffers */},
{VP9PROFILE_MIN, false /* decode_using_client_picture_buffers */},
{VP9PROFILE_MIN, true /* decode_using_client_picture_buffers */}};
diff --git a/chromium/media/gpu/vaapi/vaapi_video_decoder.cc b/chromium/media/gpu/vaapi/vaapi_video_decoder.cc
index b7f676aa93f..c97f1a06cd9 100644
--- a/chromium/media/gpu/vaapi/vaapi_video_decoder.cc
+++ b/chromium/media/gpu/vaapi/vaapi_video_decoder.cc
@@ -240,7 +240,7 @@ void VaapiVideoDecoder::HandleDecodeTask() {
case AcceleratedVideoDecoder::kConfigChange:
// A new set of output buffers is requested. We either didn't have any
// output buffers yet or encountered a resolution change.
- // After the pipeline flushes all frames, OnPipelineFlushed() will be
+ // After the pipeline flushes all frames, ApplyResolutionChange() will be
// called and we can start changing resolution.
DCHECK(client_);
SetState(State::kChangingResolution);
@@ -392,7 +392,7 @@ void VaapiVideoDecoder::OutputFrameTask(scoped_refptr<VideoFrame> video_frame,
output_cb_.Run(std::move(video_frame));
}
-void VaapiVideoDecoder::OnPipelineFlushed() {
+void VaapiVideoDecoder::ApplyResolutionChange() {
DCHECK_CALLED_ON_VALID_SEQUENCE(decoder_sequence_checker_);
DCHECK(state_ == State::kChangingResolution ||
state_ == State::kWaitingForInput);
diff --git a/chromium/media/gpu/vaapi/vaapi_video_decoder.h b/chromium/media/gpu/vaapi/vaapi_video_decoder.h
index 01e282d2613..db186f14734 100644
--- a/chromium/media/gpu/vaapi/vaapi_video_decoder.h
+++ b/chromium/media/gpu/vaapi/vaapi_video_decoder.h
@@ -54,7 +54,7 @@ class VaapiVideoDecoder : public DecoderInterface,
const OutputCB& output_cb) override;
void Decode(scoped_refptr<DecoderBuffer> buffer, DecodeCB decode_cb) override;
void Reset(base::OnceClosure reset_cb) override;
- void OnPipelineFlushed() override;
+ void ApplyResolutionChange() override;
// DecodeSurfaceHandler<VASurface> implementation.
scoped_refptr<VASurface> CreateSurface() override;
diff --git a/chromium/media/gpu/vaapi/vaapi_video_encode_accelerator.cc b/chromium/media/gpu/vaapi/vaapi_video_encode_accelerator.cc
index 234754e1463..c7ae04b8be9 100644
--- a/chromium/media/gpu/vaapi/vaapi_video_encode_accelerator.cc
+++ b/chromium/media/gpu/vaapi/vaapi_video_encode_accelerator.cc
@@ -280,6 +280,13 @@ bool VaapiVideoEncodeAccelerator::Initialize(const Config& config,
VLOGF(2) << "Initializing VAVEA, " << config.AsHumanReadableString();
+ // VaapiVEA doesn't support temporal layers but we let it pass here to support
+ // simulcast.
+ if (config.HasSpatialLayer()) {
+ VLOGF(1) << "Spatial layer encoding is supported";
+ return false;
+ }
+
client_ptr_factory_.reset(new base::WeakPtrFactory<Client>(client));
client_ = client_ptr_factory_->GetWeakPtr();
@@ -444,7 +451,9 @@ void VaapiVideoEncodeAccelerator::InitializeTask(const Config& config) {
num_frames_in_flight_, expected_input_coded_size_,
output_buffer_byte_size_));
- encoder_info_.scaling_settings = encoder_->GetScalingSettings();
+ // TODO(crbug.com/1034686): Set ScalingSettings causes getStats() hangs.
+ // Investigate and fix the issue.
+ // encoder_info_.scaling_settings = encoder_->GetScalingSettings();
// TODO(crbug.com/1030199): VaapiVideoEncodeAccelerator doesn't support either
// temporal-SVC or spatial-SVC. Update |fps_allocation| properly once they are
diff --git a/chromium/media/gpu/vaapi/vaapi_video_encode_accelerator_unittest.cc b/chromium/media/gpu/vaapi/vaapi_video_encode_accelerator_unittest.cc
new file mode 100644
index 00000000000..01bfbb3a6e0
--- /dev/null
+++ b/chromium/media/gpu/vaapi/vaapi_video_encode_accelerator_unittest.cc
@@ -0,0 +1,82 @@
+// 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/gpu/vaapi/vaapi_video_encode_accelerator.h"
+
+#include "base/test/task_environment.h"
+#include "media/video/video_encode_accelerator.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+namespace {
+
+constexpr gfx::Size kDefaultEncodeSize(1280, 720);
+constexpr uint32_t kDefaultBitrateBps = 4 * 1000 * 1000;
+constexpr uint32_t kDefaultFramerate = 30;
+const VideoEncodeAccelerator::Config kDefaultVEAConfig(PIXEL_FORMAT_I420,
+ kDefaultEncodeSize,
+ VP8PROFILE_ANY,
+ kDefaultBitrateBps,
+ kDefaultFramerate);
+
+class MockVideoEncodeAcceleratorClient : public VideoEncodeAccelerator::Client {
+ public:
+ MockVideoEncodeAcceleratorClient() = default;
+ virtual ~MockVideoEncodeAcceleratorClient() = default;
+
+ MOCK_METHOD3(RequireBitstreamBuffers,
+ void(unsigned int, const gfx::Size&, size_t output_buffer_size));
+ MOCK_METHOD2(BitstreamBufferReady,
+ void(int32_t, const BitstreamBufferMetadata&));
+ MOCK_METHOD1(NotifyError, void(VideoEncodeAccelerator::Error));
+ MOCK_METHOD1(NotifyEncoderInfoChange, void(const VideoEncoderInfo& info));
+};
+
+struct VaapiVEAInitializeTestParam {
+ uint8_t num_of_temporal_layers = 0;
+ uint8_t num_of_spatial_layers = 0;
+ bool expected_result;
+};
+
+class VaapiVEAInitializeTest
+ : public ::testing::TestWithParam<VaapiVEAInitializeTestParam> {
+ protected:
+ VaapiVEAInitializeTest() = default;
+ ~VaapiVEAInitializeTest() override = default;
+ base::test::TaskEnvironment task_environment_;
+};
+
+TEST_P(VaapiVEAInitializeTest, SpatialLayerAndTemporalLayerEncoding) {
+ VideoEncodeAccelerator::Config config = kDefaultVEAConfig;
+ const uint8_t num_of_temporal_layers = GetParam().num_of_temporal_layers;
+ const uint8_t num_of_spatial_layers = GetParam().num_of_spatial_layers;
+ constexpr int kDenom[] = {4, 2, 1};
+ for (uint8_t i = 0; i < num_of_spatial_layers; ++i) {
+ VideoEncodeAccelerator::Config::SpatialLayer spatial_layer;
+ int denom = kDenom[i];
+ spatial_layer.width = kDefaultEncodeSize.width() / denom;
+ spatial_layer.height = kDefaultEncodeSize.height() / denom;
+ spatial_layer.bitrate_bps = kDefaultBitrateBps / denom;
+ spatial_layer.framerate = kDefaultFramerate;
+ spatial_layer.max_qp = 30;
+ spatial_layer.num_of_temporal_layers = num_of_temporal_layers;
+ config.spatial_layers.push_back(spatial_layer);
+ }
+
+ VaapiVideoEncodeAccelerator vea;
+ MockVideoEncodeAcceleratorClient client;
+ EXPECT_EQ(vea.Initialize(config, &client), GetParam().expected_result);
+}
+
+constexpr VaapiVEAInitializeTestParam kTestCases[] = {
+ {1u, 3u, false}, // Spatial Layer only.
+ {3u, 3u, false}, // Temporal + Spatial Layer.
+};
+
+INSTANTIATE_TEST_SUITE_P(SpatialLayerAndTemporalLayerEncoding,
+ VaapiVEAInitializeTest,
+ ::testing::ValuesIn(kTestCases));
+} // namespace
+} // namespace media
diff --git a/chromium/media/gpu/vaapi/vaapi_wrapper.cc b/chromium/media/gpu/vaapi/vaapi_wrapper.cc
index aa951cf8ee7..f238e6f0851 100644
--- a/chromium/media/gpu/vaapi/vaapi_wrapper.cc
+++ b/chromium/media/gpu/vaapi/vaapi_wrapper.cc
@@ -21,6 +21,7 @@
#include "base/bind_helpers.h"
#include "base/bits.h"
#include "base/callback_helpers.h"
+#include "base/cpu.h"
#include "base/environment.h"
#include "base/files/scoped_file.h"
#include "base/logging.h"
@@ -141,6 +142,31 @@ namespace media {
namespace {
+// Returns true if the SoC has a 9.5 GPU. CPU model IDs are referenced from the
+// following file in the kernel source: arch/x86/include/asm/intel-family.h.
+bool IsGen95Gpu() {
+ constexpr int kPentiumAndLaterFamily = 0x06;
+ constexpr int kKabyLakeModelId = 0x9E;
+ // Amber Lake, Whiskey Lake and some Comet Lake CPU IDs are the same as KBL L.
+ constexpr int kKabyLake_LModelId = 0x8E;
+ constexpr int kGeminiLakeModelId = 0x7A;
+ constexpr int kCometLakeModelId = 0xA5;
+ constexpr int kCometLake_LModelId = 0xA6;
+ static base::NoDestructor<base::CPU> cpuid;
+ static const bool is_gen95_gpu = cpuid->family() == kPentiumAndLaterFamily &&
+ (cpuid->model() == kKabyLakeModelId ||
+ cpuid->model() == kKabyLake_LModelId ||
+ cpuid->model() == kGeminiLakeModelId ||
+ cpuid->model() == kCometLakeModelId ||
+ cpuid->model() == kCometLake_LModelId);
+ return is_gen95_gpu;
+}
+
+bool IsModeEncoding(VaapiWrapper::CodecMode mode) {
+ return mode == VaapiWrapper::CodecMode::kEncode ||
+ mode == VaapiWrapper::CodecMode::kEncodeConstantQuantizationParameter;
+}
+
bool GetNV12VisibleWidthBytes(int visible_width,
uint32_t plane,
size_t* bytes) {
@@ -220,7 +246,8 @@ static const struct {
{H264PROFILE_HIGH, VAProfileH264High},
{VP8PROFILE_ANY, VAProfileVP8Version0_3},
{VP9PROFILE_PROFILE0, VAProfileVP9Profile0},
- {VP9PROFILE_PROFILE1, VAProfileVP9Profile1},
+ // VP9 hw encode/decode on profile 1 is not enabled on chromium-vaapi.
+ // {VP9PROFILE_PROFILE1, VAProfileVP9Profile1},
// TODO(crbug.com/1011454, crbug.com/1011469): Reenable VP9PROFILE_PROFILE2
// and _PROFILE3 when P010 is completely supported.
//{VP9PROFILE_PROFILE2, VAProfileVP9Profile2},
@@ -308,14 +335,14 @@ std::string VAProfileToString(VAProfile va_profile) {
bool IsBlackListedDriver(const std::string& va_vendor_string,
VaapiWrapper::CodecMode mode,
VAProfile va_profile) {
- if (mode != VaapiWrapper::CodecMode::kEncode)
+ if (!IsModeEncoding(mode))
return false;
// TODO(crbug.com/828482): Remove once H264 encoder on AMD is enabled by
// default.
if (VendorStringToImplementationType(va_vendor_string) ==
VAImplementation::kMesaGallium &&
- va_vendor_string.find("AMD STONEY") != std::string::npos &&
+ base::Contains(va_vendor_string, "AMD STONEY") &&
!base::FeatureList::IsEnabled(kVaapiH264AMDEncoder)) {
constexpr VAProfile kH264Profiles[] = {VAProfileH264Baseline,
VAProfileH264Main, VAProfileH264High,
@@ -330,13 +357,17 @@ bool IsBlackListedDriver(const std::string& va_vendor_string,
return true;
}
- // TODO(crbug.com/811912): Remove once VP9 encoding is to be enabled by
- // default.
+ // TODO(crbug.com/811912): Remove once VP9 encoding is enabled by default.
if (va_profile == VAProfileVP9Profile0 &&
!base::FeatureList::IsEnabled(kVaapiVP9Encoder)) {
return true;
}
+ // TODO(b/158655609): Several Gen 9.5 GPU devices suffer from a GPU hang when
+ // VP8 encoding in some power saving states. Blacklist them temporarily.
+ if (IsGen95Gpu() && va_profile == VAProfileVP8Version0_3)
+ return true;
+
return false;
}
@@ -556,6 +587,9 @@ std::vector<VAEntrypoint> GetEntryPointsForProfile(const base::Lock* va_lock,
{VAEntrypointVLD}, // For kDecode.
{VAEntrypointEncSlice, VAEntrypointEncPicture,
VAEntrypointEncSliceLP}, // For kEncode.
+ {VAEntrypointEncSlice,
+ VAEntrypointEncSliceLP}, // For
+ // kEncodeConstantQuantizationParameter.
{VAEntrypointVideoProc} // For kVideoProcess.
};
std::vector<VAEntrypoint> entrypoints;
@@ -586,12 +620,15 @@ static bool GetRequiredAttribs(const base::Lock* va_lock,
required_attribs->push_back({VAConfigAttribRTFormat, VA_RT_FORMAT_YUV420});
}
- if (mode != VaapiWrapper::kEncode)
+ if (!IsModeEncoding(mode))
return true;
- // All encoding use constant bit rate except for JPEG.
- if (profile != VAProfileJPEGBaseline)
- required_attribs->push_back({VAConfigAttribRateControl, VA_RC_CBR});
+ if (profile != VAProfileJPEGBaseline) {
+ if (mode == VaapiWrapper::kEncode)
+ required_attribs->push_back({VAConfigAttribRateControl, VA_RC_CBR});
+ if (mode == VaapiWrapper::kEncodeConstantQuantizationParameter)
+ required_attribs->push_back({VAConfigAttribRateControl, VA_RC_CQP});
+ }
// VAConfigAttribEncPackedHeaders is H.264 specific.
if ((profile >= VAProfileH264Baseline && profile <= VAProfileH264High) ||
@@ -1359,6 +1396,11 @@ bool VaapiWrapper::GetJpegDecodeSuitableImageFourCC(unsigned int rt_format,
preferred_fourcc == VA_FOURCC_P010) {
preferred_fourcc = VA_FOURCC_I420;
}
+ } else if (GetImplementationType() == VAImplementation::kIntelIHD) {
+ // TODO(b/155939640): iHD v19.4 fails to allocate AYUV surfaces for the VPP
+ // on gen 9.5.
+ if (preferred_fourcc == VA_FOURCC_AYUV)
+ preferred_fourcc = VA_FOURCC_I420;
}
if (!VASupportedImageFormats::Get().IsImageFormatSupported(
@@ -1449,6 +1491,7 @@ VAEntrypoint VaapiWrapper::GetDefaultVaEntryPoint(CodecMode mode,
case VaapiWrapper::kDecode:
return VAEntrypointVLD;
case VaapiWrapper::kEncode:
+ case VaapiWrapper::kEncodeConstantQuantizationParameter:
if (profile == VAProfileJPEGBaseline)
return VAEntrypointEncPicture;
else
@@ -2191,12 +2234,18 @@ VaapiWrapper::~VaapiWrapper() {
}
bool VaapiWrapper::Initialize(CodecMode mode, VAProfile va_profile) {
+#if DCHECK_IS_ON()
+ if (mode == kEncodeConstantQuantizationParameter) {
+ DCHECK_NE(va_profile, VAProfileJPEGBaseline)
+ << "JPEG Encoding doesn't support CQP bitrate control";
+ }
+#endif // DCHECK_IS_ON()
+
if (mode != kVideoProcess)
TryToSetVADisplayAttributeToLocalGPU();
VAEntrypoint entrypoint = GetDefaultVaEntryPoint(mode, va_profile);
-
- if (mode == CodecMode::kEncode && IsLowPowerEncSupported(va_profile) &&
+ if (IsModeEncoding(mode) && IsLowPowerEncSupported(va_profile, mode) &&
base::FeatureList::IsEnabled(kVaapiLowPowerEncoder)) {
entrypoint = VAEntrypointEncSliceLP;
DVLOG(2) << "Enable VA-API Low-Power Encode Entrypoint";
@@ -2205,8 +2254,9 @@ bool VaapiWrapper::Initialize(CodecMode mode, VAProfile va_profile) {
base::AutoLock auto_lock(*va_lock_);
std::vector<VAConfigAttrib> required_attribs;
if (!GetRequiredAttribs(va_lock_, va_display_, mode, va_profile, entrypoint,
- &required_attribs))
+ &required_attribs)) {
return false;
+ }
VAStatus va_res =
vaCreateConfig(va_display_, va_profile, entrypoint,
@@ -2420,7 +2470,8 @@ void VaapiWrapper::TryToSetVADisplayAttributeToLocalGPU() {
}
// Check the support for low-power encode
-bool VaapiWrapper::IsLowPowerEncSupported(VAProfile va_profile) const {
+bool VaapiWrapper::IsLowPowerEncSupported(VAProfile va_profile,
+ CodecMode mode) const {
// Enabled only for H264/AVC & VP9 Encoders
if (va_profile != VAProfileH264ConstrainedBaseline &&
va_profile != VAProfileH264Main && va_profile != VAProfileH264High &&
@@ -2431,8 +2482,8 @@ bool VaapiWrapper::IsLowPowerEncSupported(VAProfile va_profile) const {
std::vector<VAConfigAttrib> required_attribs;
base::AutoLock auto_lock(*va_lock_);
- GetRequiredAttribs(va_lock_, va_display_, VaapiWrapper::CodecMode::kEncode,
- va_profile, kLowPowerEncEntryPoint, &required_attribs);
+ GetRequiredAttribs(va_lock_, va_display_, mode, va_profile,
+ kLowPowerEncEntryPoint, &required_attribs);
// Query the driver for required attributes.
std::vector<VAConfigAttrib> attribs = required_attribs;
for (size_t i = 0; i < required_attribs.size(); ++i)
diff --git a/chromium/media/gpu/vaapi/vaapi_wrapper.h b/chromium/media/gpu/vaapi/vaapi_wrapper.h
index c3190da3b94..7f087039c58 100644
--- a/chromium/media/gpu/vaapi/vaapi_wrapper.h
+++ b/chromium/media/gpu/vaapi/vaapi_wrapper.h
@@ -97,7 +97,9 @@ class MEDIA_GPU_EXPORT VaapiWrapper
public:
enum CodecMode {
kDecode,
- kEncode,
+ kEncode, // Encode with Constant Bitrate algorithm.
+ kEncodeConstantQuantizationParameter, // Encode with Constant Quantization
+ // Parameter algorithm.
kVideoProcess,
kCodecModeMax,
};
@@ -426,8 +428,8 @@ class MEDIA_GPU_EXPORT VaapiWrapper
// Attempt to set render mode to "render to texture.". Failure is non-fatal.
void TryToSetVADisplayAttributeToLocalGPU();
- // Check low-power encode support for the given profile
- bool IsLowPowerEncSupported(VAProfile va_profile) const;
+ // Check low-power encode support for |profile| and |mode|.
+ bool IsLowPowerEncSupported(VAProfile va_profile, CodecMode mode) const;
const CodecMode mode_;
diff --git a/chromium/media/gpu/vaapi/vp8_encoder.cc b/chromium/media/gpu/vaapi/vp8_encoder.cc
index 5d9591482e6..aaf6c7bf00c 100644
--- a/chromium/media/gpu/vaapi/vp8_encoder.cc
+++ b/chromium/media/gpu/vaapi/vp8_encoder.cc
@@ -16,12 +16,14 @@ constexpr size_t kKFPeriod = 3000;
// Arbitrarily chosen bitrate window size for rate control, in ms.
constexpr int kCPBWindowSizeMs = 1500;
-// Based on WebRTC's defaults.
+// Quantization parameter. They are vp8 ac/dc indices and their ranges are
+// 0-127. Based on WebRTC's defaults.
constexpr int kMinQP = 4;
// b/110059922, crbug.com/1001900: Tuned 112->117 for bitrate issue in a lower
// resolution (180p).
constexpr int kMaxQP = 117;
-constexpr int kDefaultQP = (3 * kMinQP + kMaxQP) / 4;
+// This stands for 32 as a real ac value (see rfc 14.1. table ac_qlookup).
+constexpr int kDefaultQP = 28;
} // namespace
VP8Encoder::EncodeParams::EncodeParams()
@@ -161,12 +163,7 @@ void VP8Encoder::InitializeFrameHeader() {
DCHECK(!visible_size_.IsEmpty());
current_frame_hdr_.width = visible_size_.width();
current_frame_hdr_.height = visible_size_.height();
- // Since initial_qp is always kDefaultQP (=32), y_ac_qi should be 28
- // (the table index for kDefaultQP, see rfc 14.1. table ac_qlookup)
- static_assert(kDefaultQP == 32, "kDefault QP is not 32");
- DCHECK_EQ(current_params_.initial_qp, kDefaultQP);
- constexpr uint8_t kDefaultQPACQIndex = 28;
- current_frame_hdr_.quantization_hdr.y_ac_qi = kDefaultQPACQIndex;
+ current_frame_hdr_.quantization_hdr.y_ac_qi = kDefaultQP;
current_frame_hdr_.show_frame = true;
// TODO(sprang): Make this dynamic. Value based on reference implementation
// in libyami (https://github.com/intel/libyami).
diff --git a/chromium/media/gpu/vaapi/vp8_encoder.h b/chromium/media/gpu/vaapi/vp8_encoder.h
index c38282253f6..b7dcbea3eb9 100644
--- a/chromium/media/gpu/vaapi/vp8_encoder.h
+++ b/chromium/media/gpu/vaapi/vp8_encoder.h
@@ -38,6 +38,8 @@ class VP8Encoder : public AcceleratedVideoEncoder {
// Coded picture buffer size in bits.
unsigned int cpb_size_bits;
+ // Quantization parameter. They are vp8 ac/dc indices and their ranges are
+ // 0-127.
int initial_qp;
ScalingSettings scaling_settings;
diff --git a/chromium/media/gpu/vaapi/vp9_encoder.cc b/chromium/media/gpu/vaapi/vp9_encoder.cc
index 6050678593a..140ac37af4c 100644
--- a/chromium/media/gpu/vaapi/vp9_encoder.cc
+++ b/chromium/media/gpu/vaapi/vp9_encoder.cc
@@ -16,10 +16,14 @@ constexpr size_t kKFPeriod = 3000;
// Arbitrarily chosen bitrate window size for rate control, in ms.
constexpr int kCPBWindowSizeMs = 500;
-// Based on WebRTC's defaults.
+// Quantization parameter. They are vp9 ac/dc indices and their ranges are
+// 0-255. Based on WebRTC's defaults.
constexpr int kMinQP = 4;
constexpr int kMaxQP = 112;
-constexpr int kDefaultQP = (3 * kMinQP + kMaxQP) / 4;
+// This stands for 31 as a real ac value (see rfc 8.6.1 table
+// ac_qlookup[3][256]). Note: This needs to be revisited once we have 10&12 bit
+// encoder support.
+constexpr int kDefaultQP = 24;
// filter level may affect on quality at lower bitrates; for now,
// we set a constant value (== 10) which is what other VA-API
@@ -166,12 +170,7 @@ void VP9Encoder::InitializeFrameHeader() {
current_frame_hdr_.frame_height = visible_size_.height();
current_frame_hdr_.render_width = visible_size_.width();
current_frame_hdr_.render_height = visible_size_.height();
- // Since initial_qp is always kDefaultQP (=31), base_q_idx should be 24
- // (the table index for kDefaultQP, see rfc 8.6.1 table ac_qlookup[3][256])
- // Note: This needs to be revisited once we have 10&12 bit encoder support
- DCHECK_EQ(current_params_.initial_qp, kDefaultQP);
- constexpr uint8_t kDefaultQPACQIndex = 24;
- current_frame_hdr_.quant_params.base_q_idx = kDefaultQPACQIndex;
+ current_frame_hdr_.quant_params.base_q_idx = kDefaultQP;
current_frame_hdr_.loop_filter.level = kDefaultLfLevel;
current_frame_hdr_.show_frame = true;
}
diff --git a/chromium/media/gpu/vaapi/vp9_encoder.h b/chromium/media/gpu/vaapi/vp9_encoder.h
index 0ff69c0d165..2f3eda4b440 100644
--- a/chromium/media/gpu/vaapi/vp9_encoder.h
+++ b/chromium/media/gpu/vaapi/vp9_encoder.h
@@ -40,6 +40,8 @@ class VP9Encoder : public AcceleratedVideoEncoder {
// Coded picture buffer size in bits.
unsigned int cpb_size_bits;
+ // Quantization parameter. They are vp9 ac/dc indices and their ranges are
+ // 0-255.
int initial_qp;
ScalingSettings scaling_settings;
diff --git a/chromium/media/gpu/video_encode_accelerator_unittest.cc b/chromium/media/gpu/video_encode_accelerator_unittest.cc
index 520e9153541..25b8342343c 100644
--- a/chromium/media/gpu/video_encode_accelerator_unittest.cc
+++ b/chromium/media/gpu/video_encode_accelerator_unittest.cc
@@ -301,8 +301,12 @@ bool ShouldSkipTest(VideoPixelFormat format) {
// Disable mid_stream_bitrate_switch test cases for elm/hana.
{"elm", "MidStreamParamSwitchBitrate", PIXEL_FORMAT_UNKNOWN},
{"elm", "MultipleEncoders", PIXEL_FORMAT_UNKNOWN},
+ {"elm-kernelnext", "MidStreamParamSwitchBitrate", PIXEL_FORMAT_UNKNOWN},
+ {"elm-kernelnext", "MultipleEncoders", PIXEL_FORMAT_UNKNOWN},
{"hana", "MidStreamParamSwitchBitrate", PIXEL_FORMAT_UNKNOWN},
{"hana", "MultipleEncoders", PIXEL_FORMAT_UNKNOWN},
+ {"hana-kernelnext", "MidStreamParamSwitchBitrate", PIXEL_FORMAT_UNKNOWN},
+ {"hana-kernelnext", "MultipleEncoders", PIXEL_FORMAT_UNKNOWN},
// crbug.com/965348#c6: Tegra driver calculates the wrong plane size of
// NV12. Disable all tests on nyan family for NV12 test.
@@ -691,7 +695,7 @@ class VideoEncodeAcceleratorTestEnvironment : public ::testing::Environment {
#if defined(USE_OZONE)
// Initialize Ozone so that DMABuf can be created through Ozone DRM.
ui::OzonePlatform::InitParams params;
- params.single_process = false;
+ params.single_process = true;
ui::OzonePlatform::InitializeForUI(params);
base::Thread::Options options;
@@ -1136,8 +1140,8 @@ void VideoFrameQualityValidator::Initialize(const gfx::Size& coded_size,
decoder_->Initialize(
config, false, nullptr,
- base::BindRepeating(&VideoFrameQualityValidator::InitializeCB,
- base::Unretained(this)),
+ base::BindOnce(&VideoFrameQualityValidator::InitializeCB,
+ base::Unretained(this)),
base::BindRepeating(&VideoFrameQualityValidator::VerifyOutputFrame,
base::Unretained(this)),
base::NullCallback());
@@ -2567,8 +2571,8 @@ void VEANoInputClient::RequireBitstreamBuffers(
// Timer is used to make sure there is no output frame in 100ms.
timer_.reset(new base::OneShotTimer());
timer_->Start(FROM_HERE, base::TimeDelta::FromMilliseconds(100),
- base::Bind(&VEANoInputClient::SetState, base::Unretained(this),
- CS_FINISHED));
+ base::BindOnce(&VEANoInputClient::SetState,
+ base::Unretained(this), CS_FINISHED));
}
void VEANoInputClient::BitstreamBufferReady(
diff --git a/chromium/media/gpu/vp8_decoder_unittest.cc b/chromium/media/gpu/vp8_decoder_unittest.cc
index 8598f483c50..c7e9b289522 100644
--- a/chromium/media/gpu/vp8_decoder_unittest.cc
+++ b/chromium/media/gpu/vp8_decoder_unittest.cc
@@ -8,7 +8,6 @@
#include "base/command_line.h"
#include "base/files/file_util.h"
-#include "base/logging.h"
#include "media/base/test_data_util.h"
#include "media/gpu/vp8_decoder.h"
#include "testing/gmock/include/gmock/gmock.h"
diff --git a/chromium/media/gpu/windows/d3d11_cdm_proxy.cc b/chromium/media/gpu/windows/d3d11_cdm_proxy.cc
deleted file mode 100644
index 1e274aba7ae..00000000000
--- a/chromium/media/gpu/windows/d3d11_cdm_proxy.cc
+++ /dev/null
@@ -1,658 +0,0 @@
-// Copyright 2017 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.
-
-// TODO(crbug.com/787657): Handle hardware key reset and notify the client.
-#include "media/gpu/windows/d3d11_cdm_proxy.h"
-
-#include <initguid.h>
-
-#include "base/bind.h"
-#include "base/logging.h"
-#include "base/memory/ptr_util.h"
-#include "base/power_monitor/power_monitor.h"
-#include "base/power_monitor/power_observer.h"
-#include "base/stl_util.h"
-#include "base/synchronization/waitable_event.h"
-#include "base/win/object_watcher.h"
-#include "media/base/callback_registry.h"
-#include "media/base/cdm_context.h"
-#include "media/cdm/cdm_proxy_context.h"
-#include "media/gpu/windows/d3d11_decryptor.h"
-
-namespace media {
-
-namespace {
-
-// Checks whether there is a hardware protected key exhange method.
-// https://msdn.microsoft.com/en-us/library/windows/desktop/dn894125(v=vs.85).aspx
-// The key exhange capabilities are checked using these.
-// https://msdn.microsoft.com/en-us/library/windows/desktop/hh447640%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396
-// https://msdn.microsoft.com/en-us/library/windows/desktop/hh447782(v=vs.85).aspx
-bool CanDoHardwareProtectedKeyExchange(ComD3D11VideoDevice video_device,
- const GUID& crypto_type) {
- D3D11_VIDEO_CONTENT_PROTECTION_CAPS caps = {};
- HRESULT hresult = video_device->GetContentProtectionCaps(
- &crypto_type, &D3D11_DECODER_PROFILE_H264_VLD_NOFGT, &caps);
- if (FAILED(hresult)) {
- DVLOG(1) << "Failed to get content protection caps.";
- return false;
- }
-
- for (uint32_t i = 0; i < caps.KeyExchangeTypeCount; ++i) {
- GUID kex_guid = {};
- hresult = video_device->CheckCryptoKeyExchange(
- &crypto_type, &D3D11_DECODER_PROFILE_H264_VLD_NOFGT, i, &kex_guid);
- if (FAILED(hresult)) {
- DVLOG(1) << "Failed to get key exchange GUID";
- return false;
- }
-
- if (kex_guid == D3D11_KEY_EXCHANGE_HW_PROTECTION)
- return true;
- }
-
- DVLOG(1) << "Hardware key exchange is not supported.";
- return false;
-}
-
-class D3D11CdmProxyContext : public CdmProxyContext {
- public:
- explicit D3D11CdmProxyContext(const GUID& key_info_guid)
- : key_info_guid_(key_info_guid) {}
- ~D3D11CdmProxyContext() override = default;
-
- // The pointers are owned by the caller.
- void SetKey(ID3D11CryptoSession* crypto_session,
- const std::vector<uint8_t>& key_id,
- CdmProxy::KeyType key_type,
- const std::vector<uint8_t>& key_blob) {
- std::string key_id_str(key_id.begin(), key_id.end());
- KeyInfo key_info(crypto_session, key_blob);
- // Note that this would overwrite an entry but it is completely valid, e.g.
- // updating the keyblob due to a configuration change.
- key_info_map_[key_id_str][key_type] = std::move(key_info);
- }
-
- void RemoveKey(ID3D11CryptoSession* crypto_session,
- const std::vector<uint8_t>& key_id) {
- // There's no need for a keytype for Remove() at the moment, because it's
- // used for completely removing keys associated to |key_id|.
- std::string key_id_str(key_id.begin(), key_id.end());
- key_info_map_.erase(key_id_str);
- }
-
- // Removes all keys from the context.
- void RemoveAllKeys() { key_info_map_.clear(); }
-
- // CdmProxyContext implementation.
- base::Optional<D3D11DecryptContext> GetD3D11DecryptContext(
- CdmProxy::KeyType key_type,
- const std::string& key_id) override {
- auto key_id_find_it = key_info_map_.find(key_id);
- if (key_id_find_it == key_info_map_.end())
- return base::nullopt;
-
- auto& key_type_to_key_info = key_id_find_it->second;
- auto key_type_find_it = key_type_to_key_info.find(key_type);
- if (key_type_find_it == key_type_to_key_info.end())
- return base::nullopt;
-
- auto& key_info = key_type_find_it->second;
- D3D11DecryptContext context = {};
- context.crypto_session = key_info.crypto_session;
- context.key_blob = key_info.key_blob.data();
- context.key_blob_size = key_info.key_blob.size();
- context.key_info_guid = key_info_guid_;
- return context;
- }
-
- private:
- // A structure to keep the data passed to SetKey(). See documentation for
- // SetKey() for what the fields mean.
- struct KeyInfo {
- KeyInfo() = default;
- KeyInfo(ID3D11CryptoSession* crypto_session, std::vector<uint8_t> key_blob)
- : crypto_session(crypto_session), key_blob(std::move(key_blob)) {}
- KeyInfo(const KeyInfo&) = default;
- ~KeyInfo() = default;
-
- ID3D11CryptoSession* crypto_session;
- std::vector<uint8_t> key_blob;
- };
-
- // Maps key ID -> key type -> KeyInfo.
- // The key ID's type is string, which is converted from |key_id| in
- // SetKey(). It's better to use string here rather than convert
- // vector<uint8_t> to string every time in GetD3D11DecryptContext() because
- // in most cases it would be called more often than SetKey() and RemoveKey()
- // combined.
- std::map<std::string, std::map<CdmProxy::KeyType, KeyInfo>> key_info_map_;
-
- const GUID key_info_guid_;
-
- DISALLOW_COPY_AND_ASSIGN(D3D11CdmProxyContext);
-};
-
-} // namespace
-
-// Watches for any content protection teardown events.
-// If the instance has been started for watching, the destructor will
-// automatically stop watching.
-class D3D11CdmProxy::HardwareEventWatcher
- : public base::win::ObjectWatcher::Delegate,
- public base::PowerObserver {
- public:
- ~HardwareEventWatcher() override;
-
- // |teardown_callback| is called on the current sequence.
- // Returns an instance if it starts watching for events, otherwise returns
- // nullptr.
- static std::unique_ptr<HardwareEventWatcher> Create(
- ComD3D11Device device,
- base::RepeatingClosure teardown_callback);
-
- private:
- HardwareEventWatcher(ComD3D11Device device,
- base::RepeatingClosure teardown_callback);
-
- // Start watching for events.
- bool StartWatching();
-
- // Registers for hardware content protection teardown events.
- // Return true on success.
- bool RegisterHardwareContentProtectionTeardown(ComD3D11Device device);
-
- // Regiesters for power events, specifically power resume event.
- // Returns true on success.
- bool RegisterPowerEvents();
-
- // base::win::ObjectWatcher::Delegate implementation.
- void OnObjectSignaled(HANDLE object) override;
-
- // base::PowerObserver implementation. Other power events are not relevant to
- // this class.
- void OnResume() override;
-
- // Stops watching for events. Good for clean up.
- void StopWatching();
-
- // IDXGIAdapter3::RegisterHardwareContentProtectionTeardownStatusEvent
- // allows watching for teardown events. It is queried thru the following
- // Devices.
- ComD3D11Device device_;
- ComDXGIDevice2 dxgi_device_;
- ComDXGIAdapter3 dxgi_adapter_;
-
- // Cookie, event, and watcher used for watching events from
- // RegisterHardwareContentProtectionTeardownStatusEvent.
- DWORD teardown_event_cookie_ = 0u;
- base::WaitableEvent content_protection_teardown_event_;
- base::RepeatingClosure teardown_callback_;
- base::win::ObjectWatcher teardown_status_watcher_;
-};
-
-class D3D11CdmContext : public CdmContext {
- public:
- explicit D3D11CdmContext(const GUID& key_info_guid)
- : cdm_proxy_context_(key_info_guid) {}
- ~D3D11CdmContext() override = default;
-
- // The pointers are owned by the caller.
- void SetKey(ID3D11CryptoSession* crypto_session,
- const std::vector<uint8_t>& key_id,
- CdmProxy::KeyType key_type,
- const std::vector<uint8_t>& key_blob) {
- cdm_proxy_context_.SetKey(crypto_session, key_id, key_type, key_blob);
- event_callbacks_.Notify(Event::kHasAdditionalUsableKey);
- }
- void RemoveKey(ID3D11CryptoSession* crypto_session,
- const std::vector<uint8_t>& key_id) {
- cdm_proxy_context_.RemoveKey(crypto_session, key_id);
- }
-
- // Notifies of hardware reset.
- void OnHardwareReset() {
- cdm_proxy_context_.RemoveAllKeys();
- event_callbacks_.Notify(Event::kHardwareContextLost);
- }
-
- base::WeakPtr<D3D11CdmContext> GetWeakPtr() {
- return weak_factory_.GetWeakPtr();
- }
-
- // CdmContext implementation.
- std::unique_ptr<CallbackRegistration> RegisterEventCB(
- EventCB event_cb) override {
- return event_callbacks_.Register(std::move(event_cb));
- }
- CdmProxyContext* GetCdmProxyContext() override { return &cdm_proxy_context_; }
-
- Decryptor* GetDecryptor() override {
- if (!decryptor_)
- decryptor_.reset(new D3D11Decryptor(&cdm_proxy_context_));
-
- return decryptor_.get();
- }
-
- private:
- D3D11CdmProxyContext cdm_proxy_context_;
-
- std::unique_ptr<D3D11Decryptor> decryptor_;
-
- CallbackRegistry<EventCB::RunType> event_callbacks_;
-
- base::WeakPtrFactory<D3D11CdmContext> weak_factory_{this};
-
- DISALLOW_COPY_AND_ASSIGN(D3D11CdmContext);
-};
-
-D3D11CdmProxy::D3D11CdmProxy(const GUID& crypto_type,
- CdmProxy::Protocol protocol,
- const FunctionIdMap& function_id_map)
- : crypto_type_(crypto_type),
- protocol_(protocol),
- function_id_map_(function_id_map),
- cdm_context_(std::make_unique<D3D11CdmContext>(crypto_type)),
- create_device_func_(base::BindRepeating(D3D11CreateDevice)) {}
-
-D3D11CdmProxy::~D3D11CdmProxy() {}
-
-base::WeakPtr<CdmContext> D3D11CdmProxy::GetCdmContext() {
- return cdm_context_->GetWeakPtr();
-}
-
-void D3D11CdmProxy::Initialize(Client* client, InitializeCB init_cb) {
- DCHECK(client);
-
- auto failed = [this, &init_cb]() {
- // The value doesn't matter as it shouldn't be used on a failure.
- const uint32_t kFailedCryptoSessionId = 0xFF;
- std::move(init_cb).Run(Status::kFail, protocol_, kFailedCryptoSessionId);
- };
-
- if (initialized_) {
- failed();
- NOTREACHED() << "CdmProxy should not be initialized more than once.";
- return;
- }
-
- client_ = client;
-
- const D3D_FEATURE_LEVEL feature_levels[] = {D3D_FEATURE_LEVEL_11_1};
-
- HRESULT hresult = create_device_func_.Run(
- nullptr, // No adapter.
- D3D_DRIVER_TYPE_HARDWARE, nullptr, // No software rasterizer.
- 0, // flags, none.
- feature_levels, base::size(feature_levels), D3D11_SDK_VERSION, &device_,
- nullptr, &device_context_);
- if (FAILED(hresult)) {
- DLOG(ERROR) << "Failed to create the D3D11Device:" << hresult;
- failed();
- return;
- }
-
- // TODO(rkuroiwa): This should be registered iff
- // D3D11_CONTENT_PROTECTION_CAPS_HARDWARE_TEARDOWN is set in the capabilities.
- hardware_event_watcher_ = HardwareEventWatcher::Create(
- device_, base::BindRepeating(
- &D3D11CdmProxy::NotifyHardwareContentProtectionTeardown,
- weak_factory_.GetWeakPtr()));
- if (!hardware_event_watcher_) {
- DLOG(ERROR)
- << "Failed to start waching for content protection teardown events.";
- failed();
- return;
- }
-
- hresult = device_.As(&video_device_);
- if (FAILED(hresult)) {
- DLOG(ERROR) << "Failed to get ID3D11VideoDevice: " << hresult;
- failed();
- return;
- }
-
- if (!CanDoHardwareProtectedKeyExchange(video_device_, crypto_type_)) {
- DLOG(ERROR) << "Cannot do hardware protected key exchange.";
- failed();
- return;
- }
-
- hresult = device_context_.As(&video_context_);
- if (FAILED(hresult)) {
- DLOG(ERROR) << "Failed to get ID3D11VideoContext: " << hresult;
- failed();
- return;
- }
-
- hresult = device_.As(&video_device1_);
- if (FAILED(hresult)) {
- DLOG(ERROR) << "Failed to get ID3D11VideoDevice1: " << hresult;
- failed();
- return;
- }
-
- hresult = device_context_.As(&video_context1_);
- if (FAILED(hresult)) {
- DLOG(ERROR) << "Failed to get ID3D11VideoContext1: " << hresult;
- failed();
- return;
- }
-
- ComD3D11CryptoSession csme_crypto_session;
- hresult = video_device_->CreateCryptoSession(
- &crypto_type_, &D3D11_DECODER_PROFILE_H264_VLD_NOFGT,
- &D3D11_KEY_EXCHANGE_HW_PROTECTION, &csme_crypto_session);
- if (FAILED(hresult)) {
- DLOG(ERROR) << "Failed to Create CryptoSession: " << hresult;
- failed();
- return;
- }
-
- hresult = video_device1_->GetCryptoSessionPrivateDataSize(
- &crypto_type_, &D3D11_DECODER_PROFILE_H264_VLD_NOFGT,
- &D3D11_KEY_EXCHANGE_HW_PROTECTION, &private_input_size_,
- &private_output_size_);
- if (FAILED(hresult)) {
- DLOG(ERROR) << "Failed to get private data sizes: " << hresult;
- failed();
- return;
- }
-
- const uint32_t crypto_session_id = next_crypto_session_id_++;
- crypto_session_map_[crypto_session_id] = std::move(csme_crypto_session);
- initialized_ = true;
- std::move(init_cb).Run(Status::kOk, protocol_, crypto_session_id);
-}
-
-void D3D11CdmProxy::Process(Function function,
- uint32_t crypto_session_id,
- const std::vector<uint8_t>& input_data_vec,
- uint32_t expected_output_data_size,
- ProcessCB process_cb) {
- auto failed = [&process_cb]() {
- std::move(process_cb).Run(Status::kFail, std::vector<uint8_t>());
- };
-
- if (!initialized_) {
- DLOG(ERROR) << "Not initialied.";
- failed();
- return;
- }
-
- auto function_id_it = function_id_map_.find(function);
- if (function_id_it == function_id_map_.end()) {
- DLOG(ERROR) << "Unrecognized function: " << static_cast<int>(function);
- failed();
- return;
- }
-
- auto crypto_session_it = crypto_session_map_.find(crypto_session_id);
- if (crypto_session_it == crypto_session_map_.end()) {
- DLOG(ERROR) << "Cannot find crypto session with ID " << crypto_session_id;
- failed();
- return;
- }
-
- ComD3D11CryptoSession& crypto_session = crypto_session_it->second;
-
- D3D11_KEY_EXCHANGE_HW_PROTECTION_DATA key_exchange_data = {};
- key_exchange_data.HWProtectionFunctionID = function_id_it->second;
-
- // Because D3D11_KEY_EXCHANGE_HW_PROTECTION_INPUT_DATA and
- // D3D11_KEY_EXCHANGE_HW_PROTECTION_OUTPUT_DATA are variable size structures,
- // uint8 array are allocated and casted to each type.
- // -4 for the "BYTE pbInput[4]" field.
- std::unique_ptr<uint8_t[]> input_data_raw(
- new uint8_t[sizeof(D3D11_KEY_EXCHANGE_HW_PROTECTION_INPUT_DATA) - 4 +
- input_data_vec.size()]);
- std::unique_ptr<uint8_t[]> output_data_raw(
- new uint8_t[sizeof(D3D11_KEY_EXCHANGE_HW_PROTECTION_OUTPUT_DATA) - 4 +
- expected_output_data_size]);
-
- D3D11_KEY_EXCHANGE_HW_PROTECTION_INPUT_DATA* input_data =
- reinterpret_cast<D3D11_KEY_EXCHANGE_HW_PROTECTION_INPUT_DATA*>(
- input_data_raw.get());
- D3D11_KEY_EXCHANGE_HW_PROTECTION_OUTPUT_DATA* output_data =
- reinterpret_cast<D3D11_KEY_EXCHANGE_HW_PROTECTION_OUTPUT_DATA*>(
- output_data_raw.get());
-
- key_exchange_data.pInputData = input_data;
- key_exchange_data.pOutputData = output_data;
- input_data->PrivateDataSize = private_input_size_;
- input_data->HWProtectionDataSize = 0;
- memcpy(input_data->pbInput, input_data_vec.data(), input_data_vec.size());
-
- output_data->PrivateDataSize = private_output_size_;
- output_data->HWProtectionDataSize = 0;
- output_data->TransportTime = 0;
- output_data->ExecutionTime = 0;
- output_data->MaxHWProtectionDataSize = expected_output_data_size;
-
- HRESULT hresult = video_context_->NegotiateCryptoSessionKeyExchange(
- crypto_session.Get(), sizeof(key_exchange_data), &key_exchange_data);
- if (FAILED(hresult)) {
- failed();
- return;
- }
-
- std::move(process_cb)
- .Run(Status::kOk, std::vector<uint8_t>(
- output_data->pbOutput,
- output_data->pbOutput + expected_output_data_size));
- return;
-}
-
-void D3D11CdmProxy::CreateMediaCryptoSession(
- const std::vector<uint8_t>& input_data,
- CreateMediaCryptoSessionCB create_media_crypto_session_cb) {
- auto failed = [&create_media_crypto_session_cb]() {
- const uint32_t kInvalidSessionId = 0;
- const uint64_t kNoOutputData = 0;
- std::move(create_media_crypto_session_cb)
- .Run(Status::kFail, kInvalidSessionId, kNoOutputData);
- };
- if (!initialized_) {
- DLOG(ERROR) << "Not initialized.";
- failed();
- return;
- }
-
- ComD3D11CryptoSession media_crypto_session;
- HRESULT hresult = video_device_->CreateCryptoSession(
- &crypto_type_, &D3D11_DECODER_PROFILE_H264_VLD_NOFGT, &crypto_type_,
- &media_crypto_session);
- if (FAILED(hresult)) {
- DLOG(ERROR) << "Failed to create a crypto session: " << hresult;
- failed();
- return;
- }
-
- // Don't do CheckCryptoSessionStatus() yet. The status may be something like
- // CONTEXT_LOST because GetDataForNewHardwareKey() is not called yet.
- uint64_t output_data = 0;
- if (!input_data.empty()) {
- hresult = video_context1_->GetDataForNewHardwareKey(
- media_crypto_session.Get(), input_data.size(), input_data.data(),
- &output_data);
- if (FAILED(hresult)) {
- DLOG(ERROR) << "Failed to establish hardware session: " << hresult;
- failed();
- return;
- }
- }
-
- D3D11_CRYPTO_SESSION_STATUS crypto_session_status = {};
- hresult = video_context1_->CheckCryptoSessionStatus(
- media_crypto_session.Get(), &crypto_session_status);
- if (FAILED(hresult) ||
- crypto_session_status != D3D11_CRYPTO_SESSION_STATUS_OK) {
- DLOG(ERROR) << "Crypto session is not OK. Crypto session status "
- << crypto_session_status << ". HRESULT " << hresult;
- failed();
- return;
- }
-
- const uint32_t media_crypto_session_id = next_crypto_session_id_++;
- crypto_session_map_[media_crypto_session_id] =
- std::move(media_crypto_session);
- std::move(create_media_crypto_session_cb)
- .Run(Status::kOk, media_crypto_session_id, output_data);
-}
-
-void D3D11CdmProxy::SetKey(uint32_t crypto_session_id,
- const std::vector<uint8_t>& key_id,
- KeyType key_type,
- const std::vector<uint8_t>& key_blob,
- SetKeyCB set_key_cb) {
- auto crypto_session_it = crypto_session_map_.find(crypto_session_id);
- if (crypto_session_it == crypto_session_map_.end()) {
- DLOG(WARNING) << crypto_session_id
- << " did not map to a crypto session instance.";
- std::move(set_key_cb).Run(Status::kFail);
- return;
- }
-
- cdm_context_->SetKey(crypto_session_it->second.Get(), key_id, key_type,
- key_blob);
- std::move(set_key_cb).Run(Status::kOk);
-}
-
-void D3D11CdmProxy::RemoveKey(uint32_t crypto_session_id,
- const std::vector<uint8_t>& key_id,
- RemoveKeyCB remove_key_cb) {
- auto crypto_session_it = crypto_session_map_.find(crypto_session_id);
- if (crypto_session_it == crypto_session_map_.end()) {
- DLOG(WARNING) << crypto_session_id
- << " did not map to a crypto session instance.";
- std::move(remove_key_cb).Run(Status::kFail);
- return;
- }
-
- cdm_context_->RemoveKey(crypto_session_it->second.Get(), key_id);
- std::move(remove_key_cb).Run(Status::kOk);
-}
-
-void D3D11CdmProxy::SetCreateDeviceCallbackForTesting(
- D3D11CreateDeviceCB callback) {
- create_device_func_ = std::move(callback);
-}
-
-void D3D11CdmProxy::NotifyHardwareContentProtectionTeardown() {
- cdm_context_->OnHardwareReset();
- client_->NotifyHardwareReset();
- Reset();
-}
-
-void D3D11CdmProxy::Reset() {
- client_ = nullptr;
- initialized_ = false;
- crypto_session_map_.clear();
- device_.Reset();
- device_context_.Reset();
- video_device_.Reset();
- video_device1_.Reset();
- video_context_.Reset();
- video_context1_.Reset();
- // Note that this deregisters hardware reset event watcher. It shouldn't
- // notify the clients until this is reinitialized. Also the client is set to
- // null in this method.
- hardware_event_watcher_ = nullptr;
- crypto_session_map_.clear();
- private_input_size_ = 0;
- private_output_size_ = 0;
-}
-
-D3D11CdmProxy::HardwareEventWatcher::~HardwareEventWatcher() {
- StopWatching();
-}
-
-std::unique_ptr<D3D11CdmProxy::HardwareEventWatcher>
-D3D11CdmProxy::HardwareEventWatcher::Create(
- ComD3D11Device device,
- base::RepeatingClosure teardown_callback) {
- std::unique_ptr<HardwareEventWatcher> event_watcher = base::WrapUnique(
- new HardwareEventWatcher(device, std::move(teardown_callback)));
- if (!event_watcher->StartWatching())
- return nullptr;
- return event_watcher;
-}
-
-D3D11CdmProxy::HardwareEventWatcher::HardwareEventWatcher(
- ComD3D11Device device,
- base::RepeatingClosure teardown_callback)
- : device_(device), teardown_callback_(std::move(teardown_callback)) {}
-
-bool D3D11CdmProxy::HardwareEventWatcher::StartWatching() {
- if (!RegisterPowerEvents() ||
- !RegisterHardwareContentProtectionTeardown(device_)) {
- StopWatching();
- return false;
- }
-
- return true;
-}
-
-bool D3D11CdmProxy::HardwareEventWatcher::
- RegisterHardwareContentProtectionTeardown(ComD3D11Device device) {
- device_ = device;
- HRESULT hresult = device_.As(&dxgi_device_);
- if (FAILED(hresult)) {
- DVLOG(1) << "Failed to get dxgi device from device: "
- << logging::SystemErrorCodeToString(hresult);
- return false;
- }
-
- hresult = dxgi_device_->GetParent(IID_PPV_ARGS(&dxgi_adapter_));
- if (FAILED(hresult)) {
- DVLOG(1) << "Failed to get dxgi adapter from dxgi device: "
- << logging::SystemErrorCodeToString(hresult);
- return false;
- }
-
- if (!teardown_status_watcher_.StartWatchingOnce(
- content_protection_teardown_event_.handle(), this)) {
- DVLOG(1) << "Failed to watch tear down event.";
- return false;
- }
-
- hresult = dxgi_adapter_->RegisterHardwareContentProtectionTeardownStatusEvent(
- content_protection_teardown_event_.handle(), &teardown_event_cookie_);
- if (FAILED(hresult)) {
- DVLOG(1)
- << "Failed to register for HardwareContentProtectionTeardownStatus: "
- << logging::SystemErrorCodeToString(hresult);
- return false;
- }
-
- return true;
-}
-
-bool D3D11CdmProxy::HardwareEventWatcher::RegisterPowerEvents() {
- if (!base::PowerMonitor::AddObserver(this)) {
- DVLOG(1) << "Power monitor not available.";
- return false;
- }
- return true;
-}
-
-void D3D11CdmProxy::HardwareEventWatcher::OnObjectSignaled(HANDLE object) {
- DCHECK_EQ(object, content_protection_teardown_event_.handle());
- teardown_callback_.Run();
-}
-
-void D3D11CdmProxy::HardwareEventWatcher::OnResume() {
- teardown_callback_.Run();
-}
-
-void D3D11CdmProxy::HardwareEventWatcher::StopWatching() {
- if (dxgi_adapter_) {
- dxgi_adapter_->UnregisterHardwareContentProtectionTeardownStatus(
- teardown_event_cookie_);
- }
- teardown_status_watcher_.StopWatching();
- base::PowerMonitor::RemoveObserver(this);
-}
-
-} // namespace media
diff --git a/chromium/media/gpu/windows/d3d11_cdm_proxy.h b/chromium/media/gpu/windows/d3d11_cdm_proxy.h
deleted file mode 100644
index 572ed2eab0b..00000000000
--- a/chromium/media/gpu/windows/d3d11_cdm_proxy.h
+++ /dev/null
@@ -1,126 +0,0 @@
-// Copyright 2017 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_GPU_WINDOWS_D3D11_CDM_PROXY_H_
-#define MEDIA_GPU_WINDOWS_D3D11_CDM_PROXY_H_
-
-#include "media/cdm/cdm_proxy.h"
-
-#include <d3d11_1.h>
-#include <dxgi1_4.h>
-#include <wrl/client.h>
-
-#include <map>
-#include <vector>
-
-#include "base/callback.h"
-#include "base/memory/weak_ptr.h"
-#include "media/base/win/d3d11_create_device_cb.h"
-#include "media/gpu/media_gpu_export.h"
-#include "media/gpu/windows/d3d11_com_defs.h"
-
-namespace media {
-
-class D3D11CdmContext;
-
-// This is a CdmProxy implementation that uses D3D11.
-class MEDIA_GPU_EXPORT D3D11CdmProxy : public CdmProxy {
- public:
- using FunctionIdMap = std::map<Function, uint32_t>;
-
- // |crypto_type| is the ID that is used to do crypto session operations. This
- // includes creating a crypto session with
- // ID3D11VideoDevice::CreateCryptoSession(). This is "a GUID that specifies
- // the type of encryption to use".
- // https://msdn.microsoft.com/en-us/library/windows/desktop/hh447785(v=vs.85).aspx
- // This is also used ot call
- // ID3D11VideoDevice1::GetCryptoSessionPrivateDataSize(). It "Indicates the
- // crypto type for which the private input and output size is queried."
- // https://msdn.microsoft.com/en-us/library/windows/desktop/dn894143(v=vs.85).aspx
- // |protocol| determines what protocol this is operating in. This
- // value is passed to callbacks that require a protocol enum value.
- // |function_id_map| maps Function enum to an integer.
- D3D11CdmProxy(const GUID& crypto_type,
- CdmProxy::Protocol protocol,
- const FunctionIdMap& function_id_map);
- ~D3D11CdmProxy() override;
-
- // CdmProxy implementation.
- base::WeakPtr<CdmContext> GetCdmContext() override;
- void Initialize(Client* client, InitializeCB init_cb) override;
- void Process(Function function,
- uint32_t crypto_session_id,
- const std::vector<uint8_t>& input_data,
- uint32_t expected_output_data_size,
- ProcessCB process_cb) override;
- void CreateMediaCryptoSession(
- const std::vector<uint8_t>& input_data,
- CreateMediaCryptoSessionCB create_media_crypto_session_cb) override;
- void SetKey(uint32_t crypto_session_id,
- const std::vector<uint8_t>& key_id,
- KeyType key_type,
- const std::vector<uint8_t>& key_blob,
- SetKeyCB set_key_cb) override;
- void RemoveKey(uint32_t crypto_session_id,
- const std::vector<uint8_t>& key_id,
- RemoveKeyCB remove_key_cb) override;
-
- void SetCreateDeviceCallbackForTesting(D3D11CreateDeviceCB callback);
-
- private:
-
- class HardwareEventWatcher;
-
- void NotifyHardwareContentProtectionTeardown();
-
- // Reset the state of this instance to be reinitializable.
- void Reset();
-
- const GUID crypto_type_;
- const CdmProxy::Protocol protocol_;
- const FunctionIdMap function_id_map_;
-
- std::unique_ptr<D3D11CdmContext> cdm_context_;
-
- // Implmenenting this class does not require this to be a callback. But in
- // order to inject D3D11CreateDevice() function for testing, this member is
- // required. The test will replace this with a function that returns a mock
- // devices.
- D3D11CreateDeviceCB create_device_func_;
-
- // Counter for assigning IDs to crypto sessions.
- uint32_t next_crypto_session_id_ = 1;
-
- // Everything from here until weak ptr factory (which must be at the end)
- // should be reset in Reset().
- Client* client_ = nullptr;
- bool initialized_ = false;
-
- ComD3D11Device device_;
- ComD3D11DeviceContext device_context_;
- // TODO(crbug.com/788880): Remove ID3D11VideoDevice and ID3D11VideoContext if
- // they are not required.
- ComD3D11VideoDevice video_device_;
- ComD3D11VideoDevice1 video_device1_;
- ComD3D11VideoContext video_context_;
- ComD3D11VideoContext1 video_context1_;
-
- std::unique_ptr<HardwareEventWatcher> hardware_event_watcher_;
-
- // Crypto session ID -> actual crypto session.
- std::map<uint32_t, ComD3D11CryptoSession> crypto_session_map_;
-
- // The values output from ID3D11VideoDevice1::GetCryptoSessionPrivateDataSize.
- // Used when calling NegotiateCryptoSessionKeyExchange.
- UINT private_input_size_ = 0;
- UINT private_output_size_ = 0;
-
- base::WeakPtrFactory<D3D11CdmProxy> weak_factory_{this};
-
- DISALLOW_COPY_AND_ASSIGN(D3D11CdmProxy);
-};
-
-} // namespace media
-
-#endif // MEDIA_GPU_WINDOWS_D3D11_CDM_PROXY_H_
diff --git a/chromium/media/gpu/windows/d3d11_cdm_proxy_unittest.cc b/chromium/media/gpu/windows/d3d11_cdm_proxy_unittest.cc
deleted file mode 100644
index a711e8c6b18..00000000000
--- a/chromium/media/gpu/windows/d3d11_cdm_proxy_unittest.cc
+++ /dev/null
@@ -1,896 +0,0 @@
-// Copyright 2017 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/gpu/windows/d3d11_cdm_proxy.h"
-
-#include <d3d11.h>
-#include <d3d11_1.h>
-#include <initguid.h>
-
-#include "base/bind.h"
-#include "base/power_monitor/power_monitor.h"
-#include "base/power_monitor/power_monitor_source.h"
-#include "base/run_loop.h"
-#include "base/test/mock_callback.h"
-#include "base/test/task_environment.h"
-#include "media/base/callback_registry.h"
-#include "media/base/win/d3d11_mocks.h"
-#include "media/cdm/cdm_proxy_context.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using Microsoft::WRL::ComPtr;
-
-using ::testing::_;
-using ::testing::AllOf;
-using ::testing::AtLeast;
-using ::testing::AtMost;
-using ::testing::DoAll;
-using ::testing::Invoke;
-using ::testing::InvokeWithoutArgs;
-using ::testing::Lt;
-using ::testing::Mock;
-using ::testing::Ne;
-using ::testing::NiceMock;
-using ::testing::Pointee;
-using ::testing::Return;
-using ::testing::SaveArg;
-using ::testing::SetArgPointee;
-using ::testing::WithArgs;
-
-namespace media {
-
-namespace {
-
-// TODO(rkuroiwa): Although inheriting from different classes, there are several
-// mock CdmProxy clients already. They all have NotifyHardwareReset(), so share
-// a single mock class that inherits from all the CdmProxy client classes.
-class MockProxyClient : public CdmProxy::Client {
- public:
- MOCK_METHOD0(NotifyHardwareReset, void());
-};
-
-class MockPowerMonitorSource : public base::PowerMonitorSource {
- public:
- // Use this method to send a power resume event.
- void Resume() {
- // Due to how ProcessPowerEvent() works, it has to be suspended first to
- // resume.
- ProcessPowerEvent(SUSPEND_EVENT);
- ProcessPowerEvent(RESUME_EVENT);
- }
-
- MOCK_METHOD0(IsOnBatteryPowerImpl, bool());
-};
-
-// The values doesn't matter as long as this is consistently used thruout the
-// test.
-const CdmProxy::Protocol kTestProtocol = CdmProxy::Protocol::kIntel;
-const CdmProxy::Function kTestFunction =
- CdmProxy::Function::kIntelNegotiateCryptoSessionKeyExchange;
-// TODO(rkuroiwa): Add test cases for KeyType.
-const CdmProxy::KeyType kTestKeyType = CdmProxy::KeyType::kDecryptOnly;
-const uint32_t kTestFunctionId = 123;
-// clang-format off
-DEFINE_GUID(CRYPTO_TYPE_GUID,
- 0x01020304, 0xffee, 0xefba,
- 0x93, 0xaa, 0x47, 0x77, 0x43, 0xb1, 0x22, 0x98);
-// clang-format on
-
-} // namespace
-
-// Class for mocking the callbacks that get passed to the proxy methods.
-class CallbackMock {
- public:
- MOCK_METHOD3(InitializeCallback, CdmProxy::InitializeCB::RunType);
- MOCK_METHOD2(ProcessCallback, CdmProxy::ProcessCB::RunType);
- MOCK_METHOD3(CreateMediaCryptoSessionCallback,
- CdmProxy::CreateMediaCryptoSessionCB::RunType);
- MOCK_METHOD1(SetKeyCallback, CdmProxy::SetKeyCB::RunType);
- MOCK_METHOD1(RemoveKeyCallback, CdmProxy::RemoveKeyCB::RunType);
-};
-
-class D3D11CdmProxyTest : public ::testing::Test {
- protected:
- void SetUp() override {
- std::map<CdmProxy::Function, uint32_t> function_id_map;
- function_id_map[kTestFunction] = kTestFunctionId;
-
- // Use NiceMock because we don't care about base::PowerMonitorSource events
- // other than calling Resume() directly.
- auto mock_power_monitor_source =
- std::make_unique<NiceMock<MockPowerMonitorSource>>();
- mock_power_monitor_source_ = mock_power_monitor_source.get();
- base::PowerMonitor::Initialize(std::move(mock_power_monitor_source));
-
- proxy_ = std::make_unique<D3D11CdmProxy>(CRYPTO_TYPE_GUID, kTestProtocol,
- function_id_map);
-
- device_mock_ = CreateD3D11Mock<D3D11DeviceMock>();
- video_device_mock_ = CreateD3D11Mock<D3D11VideoDeviceMock>();
- video_device1_mock_ = CreateD3D11Mock<D3D11VideoDevice1Mock>();
- crypto_session_mock_ = CreateD3D11Mock<D3D11CryptoSessionMock>();
- device_context_mock_ = CreateD3D11Mock<D3D11DeviceContextMock>();
- video_context_mock_ = CreateD3D11Mock<D3D11VideoContextMock>();
- video_context1_mock_ = CreateD3D11Mock<D3D11VideoContext1Mock>();
- dxgi_device_ = CreateD3D11Mock<DXGIDevice2Mock>();
- dxgi_adapter_ = CreateD3D11Mock<NiceMock<DXGIAdapter3Mock>>();
-
- // These flags are a reasonable subset of flags to get HARDWARE protected
- // playback.
- content_protection_caps_.Caps =
- D3D11_CONTENT_PROTECTION_CAPS_HARDWARE |
- D3D11_CONTENT_PROTECTION_CAPS_HARDWARE_PROTECT_UNCOMPRESSED |
- D3D11_CONTENT_PROTECTION_CAPS_HARDWARE_PROTECTED_MEMORY_PAGEABLE |
- D3D11_CONTENT_PROTECTION_CAPS_HARDWARE_TEARDOWN |
- D3D11_CONTENT_PROTECTION_CAPS_HARDWARE_DRM_COMMUNICATION;
- // 1 for the mock behavior below for CheckCryptoKeyExchange().
- content_protection_caps_.KeyExchangeTypeCount = 1;
- // This is arbitrary but 1 is reasonable, meaning doesn't need to be
- // aligned.
- content_protection_caps_.BlockAlignmentSize = 1;
- // This value is arbitrary.
- content_protection_caps_.ProtectedMemorySize = 10000000;
-
- OnCallsForInitialize();
-
- proxy_->SetCreateDeviceCallbackForTesting(
- base::BindRepeating(&D3D11CreateDeviceMock::Create,
- base::Unretained(&create_device_mock_)));
- }
-
- void TearDown() override {
- proxy_.reset();
- base::PowerMonitor::ShutdownForTesting();
- }
-
- // Sets up ON_CALLs for the mock objects. These can be overriden with
- // EXPECT_CALLs.
- // |content_protection_caps_| should be set.
- void OnCallsForInitialize() {
- ON_CALL(create_device_mock_,
- Create(_, D3D_DRIVER_TYPE_HARDWARE, _, _, _, _, _, _, _, _))
- .WillByDefault(
- DoAll(SetComPointee<7>(device_mock_.Get()),
- SetComPointeeAndReturnOk<9>(device_context_mock_.Get())));
-
- COM_ON_CALL(device_mock_, QueryInterface(IID_ID3D11VideoDevice, _))
- .WillByDefault(SetComPointeeAndReturnOk<1>(video_device_mock_.Get()));
-
- COM_ON_CALL(device_mock_, QueryInterface(IID_ID3D11VideoDevice1, _))
- .WillByDefault(SetComPointeeAndReturnOk<1>(video_device1_mock_.Get()));
-
- COM_ON_CALL(device_mock_, QueryInterface(IID_IDXGIDevice2, _))
- .WillByDefault(SetComPointeeAndReturnOk<1>(dxgi_device_.Get()));
-
- COM_ON_CALL(dxgi_device_, GetParent(IID_IDXGIAdapter3, _))
- .WillByDefault(SetComPointeeAndReturnOk<1>(dxgi_adapter_.Get()));
-
- COM_ON_CALL(dxgi_adapter_,
- RegisterHardwareContentProtectionTeardownStatusEvent(_, _))
- .WillByDefault(DoAll(SaveArg<0>(&teardown_event_), Return(S_OK)));
-
- COM_ON_CALL(device_context_mock_, QueryInterface(IID_ID3D11VideoContext, _))
- .WillByDefault(SetComPointeeAndReturnOk<1>(video_context_mock_.Get()));
-
- COM_ON_CALL(device_context_mock_,
- QueryInterface(IID_ID3D11VideoContext1, _))
- .WillByDefault(SetComPointeeAndReturnOk<1>(video_context1_mock_.Get()));
-
- COM_ON_CALL(
- video_device_mock_,
- CreateCryptoSession(Pointee(CRYPTO_TYPE_GUID), _,
- Pointee(D3D11_KEY_EXCHANGE_HW_PROTECTION), _))
- .WillByDefault(SetComPointeeAndReturnOk<3>(crypto_session_mock_.Get()));
-
- COM_ON_CALL(video_device1_mock_, GetCryptoSessionPrivateDataSize(
- Pointee(CRYPTO_TYPE_GUID), _, _, _, _))
- .WillByDefault(DoAll(SetArgPointee<3>(kPrivateInputSize),
- SetArgPointee<4>(kPrivateOutputSize),
- Return(S_OK)));
-
- COM_ON_CALL(video_device_mock_, GetContentProtectionCaps(_, _, _))
- .WillByDefault(
- DoAll(SetArgPointee<2>(content_protection_caps_), Return(S_OK)));
-
- COM_ON_CALL(video_device_mock_, CheckCryptoKeyExchange(_, _, Lt(1u), _))
- .WillByDefault(DoAll(SetArgPointee<3>(D3D11_KEY_EXCHANGE_HW_PROTECTION),
- Return(S_OK)));
- }
-
- // Helper method to do Initialize(). The returned mock objects are accessible
- // thru member variables.
- void Initialize(CdmProxy::Client* client, CdmProxy::InitializeCB callback) {
- EXPECT_CALL(create_device_mock_,
- Create(_, D3D_DRIVER_TYPE_HARDWARE, _, _, _, _, _, _, _, _));
- COM_EXPECT_CALL(device_mock_, QueryInterface(IID_ID3D11VideoDevice, _))
- .Times(AtLeast(1));
- COM_EXPECT_CALL(device_mock_, QueryInterface(IID_IDXGIDevice2, _))
- .Times(AtLeast(1));
- COM_EXPECT_CALL(dxgi_device_, GetParent(IID_IDXGIAdapter3, _))
- .Times(AtLeast(1));
- COM_EXPECT_CALL(dxgi_adapter_,
- RegisterHardwareContentProtectionTeardownStatusEvent(_, _))
- .Times(AtLeast(1));
- COM_EXPECT_CALL(device_mock_, QueryInterface(IID_ID3D11VideoDevice1, _))
- .Times(AtLeast(1));
- COM_EXPECT_CALL(device_context_mock_,
- QueryInterface(IID_ID3D11VideoContext, _))
- .Times(AtLeast(1));
- COM_EXPECT_CALL(device_context_mock_,
- QueryInterface(IID_ID3D11VideoContext1, _))
- .Times(AtLeast(1));
- COM_EXPECT_CALL(
- video_device_mock_,
- CreateCryptoSession(Pointee(CRYPTO_TYPE_GUID), _,
- Pointee(D3D11_KEY_EXCHANGE_HW_PROTECTION), _));
- COM_EXPECT_CALL(
- video_device1_mock_,
- GetCryptoSessionPrivateDataSize(Pointee(CRYPTO_TYPE_GUID), _, _, _, _));
-
- COM_EXPECT_CALL(video_device_mock_, GetContentProtectionCaps(_, _, _));
-
- COM_EXPECT_CALL(video_device_mock_,
- CheckCryptoKeyExchange(_, _, Lt(1u), _));
-
- proxy_->Initialize(client, std::move(callback));
-
- Mock::VerifyAndClearExpectations(device_mock_.Get());
- Mock::VerifyAndClearExpectations(video_device_mock_.Get());
- Mock::VerifyAndClearExpectations(video_device1_mock_.Get());
- Mock::VerifyAndClearExpectations(crypto_session_mock_.Get());
- Mock::VerifyAndClearExpectations(device_context_mock_.Get());
- Mock::VerifyAndClearExpectations(video_context_mock_.Get());
- Mock::VerifyAndClearExpectations(video_context1_mock_.Get());
- }
-
- // Test case where the proxy is initialized and then hardware content
- // protection teardown is notified.
- void HardwareContentProtectionTeardown() {
- base::RunLoop run_loop;
-
- EXPECT_CALL(callback_mock_,
- InitializeCallback(CdmProxy::Status::kOk, _, _));
- ASSERT_NO_FATAL_FAILURE(Initialize(
- &client_, base::BindOnce(&CallbackMock::InitializeCallback,
- base::Unretained(&callback_mock_))));
-
- EXPECT_CALL(client_, NotifyHardwareReset());
-
- base::MockCallback<CdmContext::EventCB> event_cb;
- auto callback_registration =
- proxy_->GetCdmContext()->RegisterEventCB(event_cb.Get());
- EXPECT_CALL(event_cb, Run(CdmContext::Event::kHardwareContextLost))
- .WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); }));
-
- SetEvent(teardown_event_);
- run_loop.Run();
- }
-
- MockProxyClient client_;
- std::unique_ptr<D3D11CdmProxy> proxy_;
- // Owned by PowerMonitor. Use this to simulate a power-resume.
- MockPowerMonitorSource* mock_power_monitor_source_;
-
- D3D11CreateDeviceMock create_device_mock_;
- CallbackMock callback_mock_;
-
- ComPtr<D3D11DeviceMock> device_mock_;
- ComPtr<D3D11VideoDeviceMock> video_device_mock_;
- ComPtr<D3D11VideoDevice1Mock> video_device1_mock_;
- ComPtr<D3D11CryptoSessionMock> crypto_session_mock_;
- ComPtr<D3D11DeviceContextMock> device_context_mock_;
- ComPtr<D3D11VideoContextMock> video_context_mock_;
- ComPtr<D3D11VideoContext1Mock> video_context1_mock_;
- ComPtr<DXGIDevice2Mock> dxgi_device_;
- ComPtr<NiceMock<DXGIAdapter3Mock>> dxgi_adapter_;
-
- D3D11_VIDEO_CONTENT_PROTECTION_CAPS content_protection_caps_ = {};
-
- // Event captured in Initialize(). Used in tests to notify hardware content
- // protection teardown.
- HANDLE teardown_event_;
-
- // These size values are arbitrary. Used for mocking
- // GetCryptoSessionPrivateDataSize().
- const UINT kPrivateInputSize = 10;
- const UINT kPrivateOutputSize = 40;
-
- // ObjectWatcher uses SequencedTaskRunnerHandle.
- base::test::TaskEnvironment task_environment_;
-};
-
-// Verifies that if device creation fails, then the call fails.
-TEST_F(D3D11CdmProxyTest, FailedToCreateDevice) {
- EXPECT_CALL(create_device_mock_, Create(_, _, _, _, _, _, _, _, _, _))
- .WillOnce(Return(E_FAIL));
- EXPECT_CALL(callback_mock_,
- InitializeCallback(CdmProxy::Status::kFail, _, _));
- proxy_->Initialize(&client_,
- base::BindOnce(&CallbackMock::InitializeCallback,
- base::Unretained(&callback_mock_)));
-}
-
-// Initialize() success case.
-TEST_F(D3D11CdmProxyTest, Initialize) {
- EXPECT_CALL(callback_mock_, InitializeCallback(CdmProxy::Status::kOk, _, _));
- ASSERT_NO_FATAL_FAILURE(
- Initialize(&client_, base::BindOnce(&CallbackMock::InitializeCallback,
- base::Unretained(&callback_mock_))));
-}
-
-// Hardware content protection teardown is notified to the proxy.
-// Verify that the client is notified.
-TEST_F(D3D11CdmProxyTest, HardwareContentProtectionTeardown) {
- EXPECT_NO_FATAL_FAILURE(HardwareContentProtectionTeardown());
-}
-
-// Verify that initialization after hardware content protection teardown works..
-TEST_F(D3D11CdmProxyTest, HardwareContentProtectionTeardownThenInitialize) {
- ASSERT_NO_FATAL_FAILURE(HardwareContentProtectionTeardown());
- EXPECT_CALL(callback_mock_, InitializeCallback(CdmProxy::Status::kOk, _, _));
- ASSERT_NO_FATAL_FAILURE(
- Initialize(&client_, base::BindOnce(&CallbackMock::InitializeCallback,
- base::Unretained(&callback_mock_))));
-}
-
-// Verify that failing to register to hardware content protection teardown
-// status event results in initialization failure.
-TEST_F(D3D11CdmProxyTest, FailedToRegisterForContentProtectionTeardown) {
- EXPECT_CALL(callback_mock_,
- InitializeCallback(CdmProxy::Status::kFail, _, _));
-
- COM_EXPECT_CALL(dxgi_adapter_,
- RegisterHardwareContentProtectionTeardownStatusEvent(_, _))
- .Times(AtLeast(1))
- .WillRepeatedly(Return(E_FAIL));
-
- proxy_->Initialize(&client_,
- base::BindOnce(&CallbackMock::InitializeCallback,
- base::Unretained(&callback_mock_)));
-}
-
-// Verify that the client is notified on power suspend.
-TEST_F(D3D11CdmProxyTest, PowerResume) {
- base::RunLoop run_loop;
-
- EXPECT_CALL(callback_mock_, InitializeCallback(CdmProxy::Status::kOk, _, _));
- ASSERT_NO_FATAL_FAILURE(
- Initialize(&client_, base::BindOnce(&CallbackMock::InitializeCallback,
- base::Unretained(&callback_mock_))));
-
- EXPECT_CALL(client_, NotifyHardwareReset()).WillOnce(Invoke([&run_loop]() {
- run_loop.Quit();
- }));
-
- mock_power_monitor_source_->Resume();
- run_loop.Run();
-}
-
-// IRL power resume is notified and then hardware content protection teardown
-// is notified. Make sure that the two notifications don't signal the clients
-// more than once (without being reinitialized in between the notifications).
-// Note that this test uses QuitWhenIdle(). If both notifications are processed
-// this test will run forever.
-TEST_F(D3D11CdmProxyTest, PowerResumeAndHardwareContentProtectionTeardown) {
- base::RunLoop run_loop;
-
- EXPECT_CALL(callback_mock_, InitializeCallback(CdmProxy::Status::kOk, _, _));
- ASSERT_NO_FATAL_FAILURE(
- Initialize(&client_, base::BindOnce(&CallbackMock::InitializeCallback,
- base::Unretained(&callback_mock_))));
-
- EXPECT_CALL(client_, NotifyHardwareReset())
- .Times(1)
- .WillOnce(Invoke([&run_loop]() { run_loop.QuitWhenIdle(); }));
-
- mock_power_monitor_source_->Resume();
- SetEvent(teardown_event_);
- run_loop.Run();
-}
-
-// Verify that if there isn't a power monitor, initialization fails.
-TEST_F(D3D11CdmProxyTest, NoPowerMonitor) {
- base::PowerMonitor::ShutdownForTesting();
- EXPECT_CALL(callback_mock_,
- InitializeCallback(CdmProxy::Status::kFail, _, _));
-
- proxy_->Initialize(&client_,
- base::BindOnce(&CallbackMock::InitializeCallback,
- base::Unretained(&callback_mock_)));
-}
-
-// Initialization failure because HW key exchange is not available.
-TEST_F(D3D11CdmProxyTest, NoHwKeyExchange) {
- EXPECT_CALL(callback_mock_,
- InitializeCallback(CdmProxy::Status::kFail, _, _));
- // GUID is set to non-D3D11_KEY_EXCHANGE_HW_PROTECTION, which means no HW key
- // exchange.
- COM_EXPECT_CALL(video_device_mock_, CheckCryptoKeyExchange(_, _, Lt(1u), _))
- .WillOnce(
- DoAll(SetArgPointee<3>(D3D11_CRYPTO_TYPE_AES128_CTR), Return(S_OK)));
-
- proxy_->Initialize(&client_,
- base::BindOnce(&CallbackMock::InitializeCallback,
- base::Unretained(&callback_mock_)));
-}
-
-// Verifies that Process() won't work if not initialized.
-TEST_F(D3D11CdmProxyTest, ProcessUninitialized) {
- // The size nor value here matter, so making non empty non zero vector.
- const std::vector<uint8_t> kAnyInput(16, 0xFF);
- // Output size is also arbitrary, just has to match with the mock.
- const uint32_t kExpectedOutputDataSize = 20;
- EXPECT_CALL(callback_mock_, ProcessCallback(CdmProxy::Status::kFail, _));
- proxy_->Process(kTestFunction, 0, kAnyInput, kExpectedOutputDataSize,
- base::BindOnce(&CallbackMock::ProcessCallback,
- base::Unretained(&callback_mock_)));
-}
-
-// Verifies that using a crypto session that is not reported will fail.
-TEST_F(D3D11CdmProxyTest, ProcessInvalidCryptoSessionID) {
- uint32_t crypto_session_id = 0;
- EXPECT_CALL(callback_mock_, InitializeCallback(CdmProxy::Status::kOk, _, _))
- .WillOnce(SaveArg<2>(&crypto_session_id));
- ASSERT_NO_FATAL_FAILURE(
- Initialize(&client_, base::BindOnce(&CallbackMock::InitializeCallback,
- base::Unretained(&callback_mock_))));
- Mock::VerifyAndClearExpectations(&callback_mock_);
-
- // The size nor value here matter, so making non empty non zero vector.
- const std::vector<uint8_t> kAnyInput(16, 0xFF);
- // Output size is also arbitrary, just has to match with the mock.
- const uint32_t kExpectedOutputDataSize = 20;
- EXPECT_CALL(callback_mock_, ProcessCallback(CdmProxy::Status::kFail, _));
-
- // Use a crypto session ID that hasn't been reported.
- proxy_->Process(kTestFunction, crypto_session_id + 1, kAnyInput,
- kExpectedOutputDataSize,
- base::BindOnce(&CallbackMock::ProcessCallback,
- base::Unretained(&callback_mock_)));
-}
-
-// Matcher for checking whether the structure passed to
-// NegotiateCryptoSessionKeyExchange has the expected values.
-MATCHER_P2(MatchesKeyExchangeStructure, expected, input_struct_size, "") {
- D3D11_KEY_EXCHANGE_HW_PROTECTION_DATA* actual =
- static_cast<D3D11_KEY_EXCHANGE_HW_PROTECTION_DATA*>(arg);
- if (expected->HWProtectionFunctionID != actual->HWProtectionFunctionID) {
- *result_listener << "function IDs mismatch. Expected "
- << expected->HWProtectionFunctionID << " actual "
- << actual->HWProtectionFunctionID;
- return false;
- }
- D3D11_KEY_EXCHANGE_HW_PROTECTION_INPUT_DATA* expected_input_data =
- expected->pInputData;
- D3D11_KEY_EXCHANGE_HW_PROTECTION_INPUT_DATA* actual_input_data =
- actual->pInputData;
- if (memcmp(expected_input_data, actual_input_data, input_struct_size) != 0) {
- *result_listener
- << "D3D11_KEY_EXCHANGE_HW_PROTECTION_INPUT_DATA don't match.";
- return false;
- }
- D3D11_KEY_EXCHANGE_HW_PROTECTION_OUTPUT_DATA* expected_output_data =
- expected->pOutputData;
- D3D11_KEY_EXCHANGE_HW_PROTECTION_OUTPUT_DATA* actual_output_data =
- actual->pOutputData;
- // Don't check that pbOutput field. It's filled by the callee.
- if (expected_output_data->PrivateDataSize !=
- actual_output_data->PrivateDataSize) {
- *result_listener << "D3D11_KEY_EXCHANGE_HW_PROTECTION_OUTPUT_DATA::"
- "PrivateDataSize don't match. Expected "
- << expected_output_data->PrivateDataSize << " actual "
- << actual_output_data->PrivateDataSize;
- return false;
- }
- if (expected_output_data->HWProtectionDataSize !=
- actual_output_data->HWProtectionDataSize) {
- *result_listener << "D3D11_KEY_EXCHANGE_HW_PROTECTION_OUTPUT_DATA::"
- "HWProtectionDataSize don't match. Expected "
- << expected_output_data->HWProtectionDataSize << " actual "
- << actual_output_data->HWProtectionDataSize;
- return false;
- }
- if (expected_output_data->TransportTime !=
- actual_output_data->TransportTime) {
- *result_listener << "D3D11_KEY_EXCHANGE_HW_PROTECTION_OUTPUT_DATA::"
- "TransportTime don't match. Expected "
- << expected_output_data->TransportTime << " actual "
- << actual_output_data->TransportTime;
- return false;
- }
- if (expected_output_data->ExecutionTime !=
- actual_output_data->ExecutionTime) {
- *result_listener << "D3D11_KEY_EXCHANGE_HW_PROTECTION_OUTPUT_DATA::"
- "ExecutionTime don't match. Expected "
- << expected_output_data->ExecutionTime << " actual "
- << actual_output_data->ExecutionTime;
- return false;
- }
- if (expected_output_data->MaxHWProtectionDataSize !=
- actual_output_data->MaxHWProtectionDataSize) {
- *result_listener << "D3D11_KEY_EXCHANGE_HW_PROTECTION_OUTPUT_DATA::"
- "MaxHWProtectionDataSize don't match. Expected "
- << expected_output_data->MaxHWProtectionDataSize
- << " actual "
- << actual_output_data->MaxHWProtectionDataSize;
- return false;
- }
- return true;
-}
-
-// Verifies that Process() works.
-TEST_F(D3D11CdmProxyTest, Process) {
- uint32_t crypto_session_id = 0;
- EXPECT_CALL(callback_mock_,
- InitializeCallback(CdmProxy::Status::kOk, kTestProtocol, _))
- .WillOnce(SaveArg<2>(&crypto_session_id));
- ASSERT_NO_FATAL_FAILURE(
- Initialize(&client_, base::BindOnce(&CallbackMock::InitializeCallback,
- base::Unretained(&callback_mock_))));
- Mock::VerifyAndClearExpectations(&callback_mock_);
-
- // The size nor value here matter, so making non empty non zero vector.
- const std::vector<uint8_t> kAnyInput(16, 0xFF);
- // Output size is also arbitrary, just has to match with the mock.
- const uint32_t kExpectedOutputDataSize = 20;
-
- const uint32_t input_structure_size =
- sizeof(D3D11_KEY_EXCHANGE_HW_PROTECTION_INPUT_DATA) - 4 +
- kAnyInput.size();
- const uint32_t output_structure_size =
- sizeof(D3D11_KEY_EXCHANGE_HW_PROTECTION_OUTPUT_DATA) - 4 +
- kExpectedOutputDataSize;
- std::unique_ptr<uint8_t[]> input_data_raw(new uint8_t[input_structure_size]);
- std::unique_ptr<uint8_t[]> output_data_raw(
- new uint8_t[output_structure_size]);
-
- D3D11_KEY_EXCHANGE_HW_PROTECTION_INPUT_DATA* input_data =
- reinterpret_cast<D3D11_KEY_EXCHANGE_HW_PROTECTION_INPUT_DATA*>(
- input_data_raw.get());
- D3D11_KEY_EXCHANGE_HW_PROTECTION_OUTPUT_DATA* output_data =
- reinterpret_cast<D3D11_KEY_EXCHANGE_HW_PROTECTION_OUTPUT_DATA*>(
- output_data_raw.get());
-
- D3D11_KEY_EXCHANGE_HW_PROTECTION_DATA expected_key_exchange_data = {};
- expected_key_exchange_data.HWProtectionFunctionID = kTestFunctionId;
- expected_key_exchange_data.pInputData = input_data;
- expected_key_exchange_data.pOutputData = output_data;
- input_data->PrivateDataSize = kPrivateInputSize;
- input_data->HWProtectionDataSize = 0;
- memcpy(input_data->pbInput, kAnyInput.data(), kAnyInput.size());
-
- output_data->PrivateDataSize = kPrivateOutputSize;
- output_data->HWProtectionDataSize = 0;
- output_data->TransportTime = 0;
- output_data->ExecutionTime = 0;
- output_data->MaxHWProtectionDataSize = kExpectedOutputDataSize;
-
- // The value does not matter, so making non zero vector.
- std::vector<uint8_t> test_output_data(kExpectedOutputDataSize, 0xAA);
- EXPECT_CALL(callback_mock_,
- ProcessCallback(CdmProxy::Status::kOk, test_output_data));
-
- auto set_test_output_data = [&test_output_data](void* output) {
- D3D11_KEY_EXCHANGE_HW_PROTECTION_DATA* kex_struct =
- static_cast<D3D11_KEY_EXCHANGE_HW_PROTECTION_DATA*>(output);
- memcpy(kex_struct->pOutputData->pbOutput, test_output_data.data(),
- test_output_data.size());
- };
-
- COM_EXPECT_CALL(video_context_mock_,
- NegotiateCryptoSessionKeyExchange(
- _, sizeof(expected_key_exchange_data),
- MatchesKeyExchangeStructure(&expected_key_exchange_data,
- input_structure_size)))
- .WillOnce(DoAll(WithArgs<2>(Invoke(set_test_output_data)), Return(S_OK)));
-
- proxy_->Process(kTestFunction, crypto_session_id, kAnyInput,
- kExpectedOutputDataSize,
- base::BindOnce(&CallbackMock::ProcessCallback,
- base::Unretained(&callback_mock_)));
-}
-
-TEST_F(D3D11CdmProxyTest, CreateMediaCryptoSessionUninitialized) {
- // The size nor value here matter, so making non empty non zero vector.
- const std::vector<uint8_t> kAnyInput(16, 0xFF);
- EXPECT_CALL(callback_mock_,
- CreateMediaCryptoSessionCallback(CdmProxy::Status::kFail, _, _));
- proxy_->CreateMediaCryptoSession(
- kAnyInput, base::BindOnce(&CallbackMock::CreateMediaCryptoSessionCallback,
- base::Unretained(&callback_mock_)));
-}
-
-// Tests the case where no extra data is specified. This is a success case.
-TEST_F(D3D11CdmProxyTest, CreateMediaCryptoSessionNoExtraData) {
- uint32_t crypto_session_id_from_initialize = 0;
- EXPECT_CALL(callback_mock_,
- InitializeCallback(CdmProxy::Status::kOk, kTestProtocol, _))
- .WillOnce(SaveArg<2>(&crypto_session_id_from_initialize));
- ASSERT_NO_FATAL_FAILURE(
- Initialize(&client_, base::BindOnce(&CallbackMock::InitializeCallback,
- base::Unretained(&callback_mock_))));
- Mock::VerifyAndClearExpectations(&callback_mock_);
-
- // Expect a new crypto session.
- EXPECT_CALL(callback_mock_, CreateMediaCryptoSessionCallback(
- CdmProxy::Status::kOk,
- Ne(crypto_session_id_from_initialize), _));
- auto media_crypto_session_mock = CreateD3D11Mock<D3D11CryptoSessionMock>();
- COM_EXPECT_CALL(video_device_mock_,
- CreateCryptoSession(Pointee(CRYPTO_TYPE_GUID), _,
- Pointee(CRYPTO_TYPE_GUID), _))
- .WillOnce(SetComPointeeAndReturnOk<3>(media_crypto_session_mock.Get()));
-
- COM_EXPECT_CALL(video_context1_mock_, GetDataForNewHardwareKey(_, _, _, _))
- .Times(0);
-
- COM_EXPECT_CALL(video_context1_mock_,
- CheckCryptoSessionStatus(media_crypto_session_mock.Get(), _))
- .WillOnce(DoAll(SetArgPointee<1>(D3D11_CRYPTO_SESSION_STATUS_OK),
- Return(S_OK)));
- proxy_->CreateMediaCryptoSession(
- std::vector<uint8_t>(),
- base::BindOnce(&CallbackMock::CreateMediaCryptoSessionCallback,
- base::Unretained(&callback_mock_)));
-}
-
-// |arg| is void*. This casts the pointer to uint8_t* and checks whether they
-// match.
-MATCHER_P(CastedToUint8Are, expected, "") {
- const uint8_t* actual = static_cast<const uint8_t*>(arg);
- for (size_t i = 0; i < expected.size(); ++i) {
- if (actual[i] != expected[i]) {
- *result_listener << "Mismatch at element " << i;
- return false;
- }
- }
- return true;
-}
-
-// Verifies that extra data is used when creating a media crypto session.
-TEST_F(D3D11CdmProxyTest, CreateMediaCryptoSessionWithExtraData) {
- uint32_t crypto_session_id_from_initialize = 0;
- EXPECT_CALL(callback_mock_,
- InitializeCallback(CdmProxy::Status::kOk, kTestProtocol, _))
- .WillOnce(SaveArg<2>(&crypto_session_id_from_initialize));
- ASSERT_NO_FATAL_FAILURE(
- Initialize(&client_, base::BindOnce(&CallbackMock::InitializeCallback,
- base::Unretained(&callback_mock_))));
- Mock::VerifyAndClearExpectations(&callback_mock_);
-
- // Expect a new crypto session.
- EXPECT_CALL(callback_mock_, CreateMediaCryptoSessionCallback(
- CdmProxy::Status::kOk,
- Ne(crypto_session_id_from_initialize), _));
-
- auto media_crypto_session_mock = CreateD3D11Mock<D3D11CryptoSessionMock>();
- COM_EXPECT_CALL(video_device_mock_,
- CreateCryptoSession(Pointee(CRYPTO_TYPE_GUID), _,
- Pointee(CRYPTO_TYPE_GUID), _))
- .WillOnce(SetComPointeeAndReturnOk<3>(media_crypto_session_mock.Get()));
- // The size nor value here matter, so making non empty non zero vector.
- const std::vector<uint8_t> kAnyInput(16, 0xFF);
- const uint64_t kAnyOutputData = 23298u;
- COM_EXPECT_CALL(video_context1_mock_,
- GetDataForNewHardwareKey(media_crypto_session_mock.Get(),
- kAnyInput.size(),
- CastedToUint8Are(kAnyInput), _))
- .WillOnce(DoAll(SetArgPointee<3>(kAnyOutputData), Return(S_OK)));
-
- COM_EXPECT_CALL(video_context1_mock_,
- CheckCryptoSessionStatus(media_crypto_session_mock.Get(), _))
- .WillOnce(DoAll(SetArgPointee<1>(D3D11_CRYPTO_SESSION_STATUS_OK),
- Return(S_OK)));
- proxy_->CreateMediaCryptoSession(
- kAnyInput, base::BindOnce(&CallbackMock::CreateMediaCryptoSessionCallback,
- base::Unretained(&callback_mock_)));
-}
-
-// Verify that GetCdmContext() is implemented and does not return null.
-TEST_F(D3D11CdmProxyTest, GetCdmContext) {
- base::WeakPtr<CdmContext> context = proxy_->GetCdmContext();
- ASSERT_TRUE(context);
-}
-
-TEST_F(D3D11CdmProxyTest, GetCdmProxyContext) {
- base::WeakPtr<CdmContext> context = proxy_->GetCdmContext();
- ASSERT_TRUE(context);
- ASSERT_TRUE(context->GetCdmProxyContext());
-}
-
-// No keys are set.
-TEST_F(D3D11CdmProxyTest, GetD3D11DecryptContextNoKey) {
- base::WeakPtr<CdmContext> context = proxy_->GetCdmContext();
- ASSERT_TRUE(context);
- CdmProxyContext* proxy_context = context->GetCdmProxyContext();
- auto decrypt_context =
- proxy_context->GetD3D11DecryptContext(kTestKeyType, "");
- EXPECT_FALSE(decrypt_context);
-}
-
-// A key is set but no keys for the key type requested.
-TEST_F(D3D11CdmProxyTest, GetD3D11DecryptContextNoKeyForKeyType) {
- uint32_t crypto_session_id_from_initialize = 0;
- EXPECT_CALL(callback_mock_,
- InitializeCallback(CdmProxy::Status::kOk, kTestProtocol, _))
- .WillOnce(SaveArg<2>(&crypto_session_id_from_initialize));
- ASSERT_NO_FATAL_FAILURE(
- Initialize(&client_, base::BindOnce(&CallbackMock::InitializeCallback,
- base::Unretained(&callback_mock_))));
- Mock::VerifyAndClearExpectations(&callback_mock_);
-
- const std::vector<uint8_t> kAnyBlob = {0x01, 0x4f, 0x83};
-
- EXPECT_CALL(callback_mock_, SetKeyCallback(CdmProxy::Status::kOk));
- proxy_->SetKey(crypto_session_id_from_initialize, kAnyBlob,
- CdmProxy::KeyType::kDecryptAndDecode, kAnyBlob,
- base::BindOnce(&CallbackMock::SetKeyCallback,
- base::Unretained(&callback_mock_)));
-
- base::WeakPtr<CdmContext> context = proxy_->GetCdmContext();
- CdmProxyContext* proxy_context = context->GetCdmProxyContext();
- auto decrypt_context = proxy_context->GetD3D11DecryptContext(
- CdmProxy::KeyType::kDecryptOnly,
- std::string(kAnyBlob.begin(), kAnyBlob.end()));
- EXPECT_FALSE(decrypt_context);
-}
-
-// Verifies that keys are set and is accessible with a getter.
-TEST_F(D3D11CdmProxyTest, SetKeyAndGetDecryptContext) {
- base::WeakPtr<CdmContext> context = proxy_->GetCdmContext();
- ASSERT_TRUE(context);
- CdmProxyContext* proxy_context = context->GetCdmProxyContext();
-
- uint32_t crypto_session_id_from_initialize = 0;
- EXPECT_CALL(callback_mock_,
- InitializeCallback(CdmProxy::Status::kOk, kTestProtocol, _))
- .WillOnce(SaveArg<2>(&crypto_session_id_from_initialize));
- ASSERT_NO_FATAL_FAILURE(
- Initialize(&client_, base::BindOnce(&CallbackMock::InitializeCallback,
- base::Unretained(&callback_mock_))));
- Mock::VerifyAndClearExpectations(&callback_mock_);
-
- std::vector<uint8_t> kKeyId = {
- 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
- 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
- };
- std::vector<uint8_t> kKeyBlob = {
- 0xab, 0x01, 0x20, 0xd3, 0xee, 0x05, 0x99, 0x87,
- 0xff, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x7F,
- };
-
- base::MockCallback<CdmContext::EventCB> event_cb;
- auto callback_registration = context->RegisterEventCB(event_cb.Get());
- EXPECT_CALL(event_cb, Run(CdmContext::Event::kHasAdditionalUsableKey));
-
- EXPECT_CALL(callback_mock_, SetKeyCallback(CdmProxy::Status::kOk));
- proxy_->SetKey(crypto_session_id_from_initialize, kKeyId, kTestKeyType,
- kKeyBlob,
- base::BindOnce(&CallbackMock::SetKeyCallback,
- base::Unretained(&callback_mock_)));
-
- // |event_cb| is posted. Run the loop to make sure it's fired.
- base::RunLoop().RunUntilIdle();
-
- std::string key_id_str(kKeyId.begin(), kKeyId.end());
- auto decrypt_context =
- proxy_context->GetD3D11DecryptContext(kTestKeyType, key_id_str);
- ASSERT_TRUE(decrypt_context);
-
- EXPECT_TRUE(decrypt_context->crypto_session)
- << "Crypto session should not be null.";
- const uint8_t* key_blob =
- reinterpret_cast<const uint8_t*>(decrypt_context->key_blob);
- EXPECT_EQ(kKeyBlob, std::vector<uint8_t>(
- key_blob, key_blob + decrypt_context->key_blob_size));
- EXPECT_EQ(CRYPTO_TYPE_GUID, decrypt_context->key_info_guid);
-}
-
-// Verify that the keys are not accessible via CdmProxyContext, after a
-// teardown..
-TEST_F(D3D11CdmProxyTest, ClearKeysAfterHardwareContentProtectionTeardown) {
- base::RunLoop run_loop;
-
- uint32_t crypto_session_id_from_initialize = 0;
- EXPECT_CALL(callback_mock_,
- InitializeCallback(CdmProxy::Status::kOk, kTestProtocol, _))
- .WillOnce(SaveArg<2>(&crypto_session_id_from_initialize));
- ASSERT_NO_FATAL_FAILURE(
- Initialize(&client_, base::BindOnce(&CallbackMock::InitializeCallback,
- base::Unretained(&callback_mock_))));
- Mock::VerifyAndClearExpectations(&callback_mock_);
-
- std::vector<uint8_t> kKeyId = {
- 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
- 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
- };
- std::vector<uint8_t> kKeyBlob = {
- 0xab, 0x01, 0x20, 0xd3, 0xee, 0x05, 0x99, 0x87,
- 0xff, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x7F,
- };
- EXPECT_CALL(callback_mock_, SetKeyCallback(CdmProxy::Status::kOk));
- proxy_->SetKey(crypto_session_id_from_initialize, kKeyId, kTestKeyType,
- kKeyBlob,
- base::BindOnce(&CallbackMock::SetKeyCallback,
- base::Unretained(&callback_mock_)));
-
- EXPECT_CALL(client_, NotifyHardwareReset()).WillOnce(Invoke([&run_loop]() {
- run_loop.Quit();
- }));
-
- SetEvent(teardown_event_);
- run_loop.Run();
-
- base::WeakPtr<CdmContext> context = proxy_->GetCdmContext();
- ASSERT_TRUE(context);
- CdmProxyContext* proxy_context = context->GetCdmProxyContext();
-
- std::string key_id_str(kKeyId.begin(), kKeyId.end());
- auto decrypt_context =
- proxy_context->GetD3D11DecryptContext(kTestKeyType, key_id_str);
- ASSERT_FALSE(decrypt_context);
-}
-
-// Verify that removing a key works.
-TEST_F(D3D11CdmProxyTest, RemoveKey) {
- base::WeakPtr<CdmContext> context = proxy_->GetCdmContext();
- ASSERT_TRUE(context);
- CdmProxyContext* proxy_context = context->GetCdmProxyContext();
-
- uint32_t crypto_session_id_from_initialize = 0;
- EXPECT_CALL(callback_mock_,
- InitializeCallback(CdmProxy::Status::kOk, kTestProtocol, _))
- .WillOnce(SaveArg<2>(&crypto_session_id_from_initialize));
- ASSERT_NO_FATAL_FAILURE(
- Initialize(&client_, base::BindOnce(&CallbackMock::InitializeCallback,
- base::Unretained(&callback_mock_))));
- Mock::VerifyAndClearExpectations(&callback_mock_);
-
- std::vector<uint8_t> kKeyId = {
- 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
- 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
- };
- std::vector<uint8_t> kKeyBlob = {
- 0xab, 0x01, 0x20, 0xd3, 0xee, 0x05, 0x99, 0x87,
- 0xff, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x7F,
- };
- EXPECT_CALL(callback_mock_, SetKeyCallback(CdmProxy::Status::kOk));
- EXPECT_CALL(callback_mock_, RemoveKeyCallback(CdmProxy::Status::kOk));
- proxy_->SetKey(crypto_session_id_from_initialize, kKeyId, kTestKeyType,
- kKeyBlob,
- base::BindOnce(&CallbackMock::SetKeyCallback,
- base::Unretained(&callback_mock_)));
- proxy_->RemoveKey(crypto_session_id_from_initialize, kKeyId,
- base::BindOnce(&CallbackMock::RemoveKeyCallback,
- base::Unretained(&callback_mock_)));
-
- std::string keyblob_str(kKeyId.begin(), kKeyId.end());
- auto decrypt_context =
- proxy_context->GetD3D11DecryptContext(kTestKeyType, keyblob_str);
- EXPECT_FALSE(decrypt_context);
-}
-
-// Calling SetKey() and RemoveKey() for non-existent crypto session should
-// fail but not crash.
-TEST_F(D3D11CdmProxyTest, SetRemoveKeyWrongCryptoSessionId) {
- const uint32_t kAnyCryptoSessionId = 0x9238;
- const std::vector<uint8_t> kEmpty;
- EXPECT_CALL(callback_mock_, RemoveKeyCallback(CdmProxy::Status::kFail));
- EXPECT_CALL(callback_mock_, SetKeyCallback(CdmProxy::Status::kFail));
- proxy_->RemoveKey(kAnyCryptoSessionId, kEmpty,
- base::BindOnce(&CallbackMock::RemoveKeyCallback,
- base::Unretained(&callback_mock_)));
- proxy_->SetKey(kAnyCryptoSessionId, kEmpty, kTestKeyType, kEmpty,
- base::BindOnce(&CallbackMock::SetKeyCallback,
- base::Unretained(&callback_mock_)));
-}
-
-TEST_F(D3D11CdmProxyTest, ProxyInvalidationInvalidatesCdmContext) {
- base::WeakPtr<CdmContext> context = proxy_->GetCdmContext();
- EXPECT_TRUE(context);
- proxy_.reset();
- EXPECT_FALSE(context);
-}
-
-} // namespace media
diff --git a/chromium/media/gpu/windows/d3d11_copying_texture_wrapper.cc b/chromium/media/gpu/windows/d3d11_copying_texture_wrapper.cc
index 6b739a87e0c..039062e324c 100644
--- a/chromium/media/gpu/windows/d3d11_copying_texture_wrapper.cc
+++ b/chromium/media/gpu/windows/d3d11_copying_texture_wrapper.cc
@@ -8,6 +8,7 @@
#include "gpu/command_buffer/service/mailbox_manager.h"
#include "media/gpu/windows/d3d11_com_defs.h"
+#include "media/gpu/windows/display_helper.h"
namespace media {
@@ -81,11 +82,25 @@ bool CopyingTexture2DWrapper::ProcessTexture(
output_texture_, 0, copy_color_space, mailbox_dest, output_color_space);
}
-bool CopyingTexture2DWrapper::Init(GetCommandBufferHelperCB get_helper_cb) {
+bool CopyingTexture2DWrapper::Init(
+ scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,
+ GetCommandBufferHelperCB get_helper_cb) {
if (!video_processor_->Init(size_.width(), size_.height()))
return false;
- return output_texture_wrapper_->Init(std::move(get_helper_cb));
+ return output_texture_wrapper_->Init(std::move(gpu_task_runner),
+ std::move(get_helper_cb));
+}
+
+void CopyingTexture2DWrapper::SetStreamHDRMetadata(
+ const HDRMetadata& stream_metadata) {
+ auto dxgi_stream_metadata = DisplayHelper::HdrMetadataToDXGI(stream_metadata);
+ video_processor_->SetStreamHDRMetadata(dxgi_stream_metadata);
+}
+
+void CopyingTexture2DWrapper::SetDisplayHDRMetadata(
+ const DXGI_HDR_METADATA_HDR10& dxgi_display_metadata) {
+ video_processor_->SetDisplayHDRMetadata(dxgi_display_metadata);
}
} // namespace media
diff --git a/chromium/media/gpu/windows/d3d11_copying_texture_wrapper.h b/chromium/media/gpu/windows/d3d11_copying_texture_wrapper.h
index 139f318ac11..0768f351a7e 100644
--- a/chromium/media/gpu/windows/d3d11_copying_texture_wrapper.h
+++ b/chromium/media/gpu/windows/d3d11_copying_texture_wrapper.h
@@ -37,7 +37,12 @@ class MEDIA_GPU_EXPORT CopyingTexture2DWrapper : public Texture2DWrapper {
MailboxHolderArray* mailbox_dest,
gfx::ColorSpace* output_color_space) override;
- bool Init(GetCommandBufferHelperCB get_helper_cb) override;
+ bool Init(scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,
+ GetCommandBufferHelperCB get_helper_cb) override;
+
+ void SetStreamHDRMetadata(const HDRMetadata& stream_metadata) override;
+ void SetDisplayHDRMetadata(
+ const DXGI_HDR_METADATA_HDR10& dxgi_display_metadata) override;
private:
gfx::Size size_;
diff --git a/chromium/media/gpu/windows/d3d11_copying_texture_wrapper_unittest.cc b/chromium/media/gpu/windows/d3d11_copying_texture_wrapper_unittest.cc
index da1adea3e03..ed36baac7e9 100644
--- a/chromium/media/gpu/windows/d3d11_copying_texture_wrapper_unittest.cc
+++ b/chromium/media/gpu/windows/d3d11_copying_texture_wrapper_unittest.cc
@@ -2,12 +2,16 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <string.h>
+
#include <utility>
#include "base/bind_helpers.h"
+#include "base/test/task_environment.h"
#include "media/gpu/windows/d3d11_copying_texture_wrapper.h"
#include "media/gpu/windows/d3d11_texture_wrapper.h"
#include "media/gpu/windows/d3d11_video_processor_proxy.h"
+#include "media/gpu/windows/display_helper.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -49,6 +53,16 @@ class MockVideoProcessorProxy : public VideoProcessorProxy {
last_output_color_space_ = color_space;
}
+ void SetStreamHDRMetadata(
+ const DXGI_HDR_METADATA_HDR10& stream_metadata) override {
+ last_stream_metadata_ = stream_metadata;
+ }
+
+ void SetDisplayHDRMetadata(
+ const DXGI_HDR_METADATA_HDR10& display_metadata) override {
+ last_display_metadata_ = display_metadata;
+ }
+
HRESULT VideoProcessorBlt(ID3D11VideoProcessorOutputView* output_view,
UINT output_frameno,
UINT stream_count,
@@ -61,9 +75,11 @@ class MockVideoProcessorProxy : public VideoProcessorProxy {
MOCK_METHOD0(MockCreateVideoProcessorInputView, HRESULT());
MOCK_METHOD0(MockVideoProcessorBlt, HRESULT());
- // Most recent arguments to SetStream/OutputColorSpace().
+ // Most recent arguments to SetStream/OutputColorSpace()/etc.
base::Optional<gfx::ColorSpace> last_stream_color_space_;
base::Optional<gfx::ColorSpace> last_output_color_space_;
+ base::Optional<DXGI_HDR_METADATA_HDR10> last_stream_metadata_;
+ base::Optional<DXGI_HDR_METADATA_HDR10> last_display_metadata_;
};
class MockTexture2DWrapper : public Texture2DWrapper {
@@ -81,12 +97,19 @@ class MockTexture2DWrapper : public Texture2DWrapper {
return MockProcessTexture();
}
- bool Init(GetCommandBufferHelperCB get_helper_cb) override {
+ bool Init(scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,
+ GetCommandBufferHelperCB get_helper_cb) override {
+ gpu_task_runner_ = std::move(gpu_task_runner);
return MockInit();
}
MOCK_METHOD0(MockInit, bool());
MOCK_METHOD0(MockProcessTexture, bool());
+ MOCK_METHOD1(SetStreamHDRMetadata, void(const HDRMetadata& stream_metadata));
+ MOCK_METHOD1(SetDisplayHDRMetadata,
+ void(const DXGI_HDR_METADATA_HDR10& dxgi_display_metadata));
+
+ scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner_;
};
CommandBufferHelperPtr UselessHelper() {
@@ -108,6 +131,10 @@ class D3D11CopyingTexture2DWrapperTest
FIELD(bool, PassthroughColorSpace, 6)
#undef FIELD
+ void SetUp() override {
+ gpu_task_runner_ = task_environment_.GetMainThreadTaskRunner();
+ }
+
std::unique_ptr<MockVideoProcessorProxy> ExpectProcessorProxy() {
auto result = std::make_unique<MockVideoProcessorProxy>();
ON_CALL(*result.get(), MockInit(_, _))
@@ -125,7 +152,7 @@ class D3D11CopyingTexture2DWrapperTest
return result;
}
- std::unique_ptr<Texture2DWrapper> ExpectTextureWrapper() {
+ std::unique_ptr<MockTexture2DWrapper> ExpectTextureWrapper() {
auto result = std::make_unique<MockTexture2DWrapper>();
ON_CALL(*result.get(), MockInit())
@@ -134,7 +161,7 @@ class D3D11CopyingTexture2DWrapperTest
ON_CALL(*result.get(), MockProcessTexture())
.WillByDefault(Return(GetProcessTexture()));
- return std::move(result);
+ return result;
}
GetCommandBufferHelperCB CreateMockHelperCB() {
@@ -151,6 +178,9 @@ class D3D11CopyingTexture2DWrapperTest
SUCCEEDED(GetCreateVideoProcessorInputView()) &&
SUCCEEDED(GetVideoProcessorBlt());
}
+
+ base::test::TaskEnvironment task_environment_;
+ scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner_;
};
INSTANTIATE_TEST_CASE_P(CopyingTexture2DWrapperTest,
@@ -175,14 +205,22 @@ TEST_P(D3D11CopyingTexture2DWrapperTest,
base::Optional<gfx::ColorSpace> copy_color_space;
if (!GetPassthroughColorSpace())
copy_color_space = gfx::ColorSpace::CreateDisplayP3D65();
+ auto texture_wrapper = ExpectTextureWrapper();
+ MockTexture2DWrapper* texture_wrapper_raw = texture_wrapper.get();
auto wrapper = std::make_unique<CopyingTexture2DWrapper>(
- size, ExpectTextureWrapper(), std::move(processor), nullptr,
+ size, std::move(texture_wrapper), std::move(processor), nullptr,
copy_color_space);
+ // TODO: check |gpu_task_runner_|.
+
MailboxHolderArray mailboxes;
gfx::ColorSpace input_color_space = gfx::ColorSpace::CreateSCRGBLinear();
gfx::ColorSpace output_color_space;
- EXPECT_EQ(wrapper->Init(CreateMockHelperCB()), InitSucceeds());
+ EXPECT_EQ(wrapper->Init(gpu_task_runner_, CreateMockHelperCB()),
+ InitSucceeds());
+ task_environment_.RunUntilIdle();
+ if (GetProcessorProxyInit())
+ EXPECT_EQ(texture_wrapper_raw->gpu_task_runner_, gpu_task_runner_);
EXPECT_EQ(wrapper->ProcessTexture(nullptr, 0, input_color_space, &mailboxes,
&output_color_space),
ProcessTextureSucceeds());
@@ -205,4 +243,44 @@ TEST_P(D3D11CopyingTexture2DWrapperTest,
// TODO: verify that these aren't sent multiple times, unless they change.
}
+TEST_P(D3D11CopyingTexture2DWrapperTest, HDRMetadataIsSentToVideoProcessor) {
+ HDRMetadata metadata;
+ metadata.mastering_metadata.primary_r =
+ MasteringMetadata::Chromaticity(0.1, 0.2);
+ metadata.mastering_metadata.primary_g =
+ MasteringMetadata::Chromaticity(0.3, 0.4);
+ metadata.mastering_metadata.primary_b =
+ MasteringMetadata::Chromaticity(0.5, 0.6);
+ metadata.mastering_metadata.white_point =
+ MasteringMetadata::Chromaticity(0.7, 0.8);
+ metadata.mastering_metadata.luminance_max = 0.9;
+ metadata.mastering_metadata.luminance_min = 0.05;
+ metadata.max_content_light_level = 1000;
+ metadata.max_frame_average_light_level = 10000;
+
+ auto processor = ExpectProcessorProxy();
+ MockVideoProcessorProxy* processor_raw = processor.get();
+ auto wrapper = std::make_unique<CopyingTexture2DWrapper>(
+ gfx::Size(100, 200), ExpectTextureWrapper(), std::move(processor),
+ nullptr, gfx::ColorSpace::CreateSCRGBLinear());
+
+ const DXGI_HDR_METADATA_HDR10 dxgi_metadata =
+ DisplayHelper::HdrMetadataToDXGI(metadata);
+
+ wrapper->SetStreamHDRMetadata(metadata);
+ EXPECT_TRUE(processor_raw->last_stream_metadata_);
+ EXPECT_FALSE(processor_raw->last_display_metadata_);
+ EXPECT_EQ(memcmp(&dxgi_metadata, &(*processor_raw->last_stream_metadata_),
+ sizeof(dxgi_metadata)),
+ 0);
+ processor_raw->last_stream_metadata_.reset();
+
+ wrapper->SetDisplayHDRMetadata(dxgi_metadata);
+ EXPECT_FALSE(processor_raw->last_stream_metadata_);
+ EXPECT_TRUE(processor_raw->last_display_metadata_);
+ EXPECT_EQ(memcmp(&dxgi_metadata, &(*processor_raw->last_display_metadata_),
+ sizeof(dxgi_metadata)),
+ 0);
+}
+
} // namespace media
diff --git a/chromium/media/gpu/windows/d3d11_decryptor.cc b/chromium/media/gpu/windows/d3d11_decryptor.cc
deleted file mode 100644
index 9f64e0cba0b..00000000000
--- a/chromium/media/gpu/windows/d3d11_decryptor.cc
+++ /dev/null
@@ -1,381 +0,0 @@
-// Copyright 2018 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/gpu/windows/d3d11_decryptor.h"
-
-#include "base/bind.h"
-#include "base/logging.h"
-#include "base/stl_util.h"
-#include "media/base/decoder_buffer.h"
-#include "media/gpu/windows/d3d11_com_defs.h"
-
-namespace media {
-
-namespace {
-
-// "A buffer is defined as a single subresource."
-// https://msdn.microsoft.com/en-us/library/windows/desktop/ff476901(v=vs.85).aspx
-const UINT kSubresourceIndex = 0;
-const UINT kWaitIfGPUBusy = 0;
-
-// This value is somewhat arbitrary but is a multiple of 16 and 4K and is
-// equal to D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION. Since the buffers are cast
-// to ID3D11Texture2D, setting it as its size should make sense.
-const UINT kBufferSize = 16384;
-
-// Creates ID3D11Buffer using the values. Return true on success.
-bool CreateBuffer(ID3D11Device* device,
- D3D11_USAGE usage,
- UINT bind_flags,
- UINT cpu_access,
- ID3D11Buffer** out) {
- D3D11_BUFFER_DESC buf_desc = {};
-
- buf_desc.ByteWidth = kBufferSize;
- buf_desc.BindFlags = bind_flags;
- buf_desc.Usage = usage;
- buf_desc.CPUAccessFlags = cpu_access;
-
- HRESULT hresult = device->CreateBuffer(&buf_desc, nullptr, out);
- return SUCCEEDED(hresult);
-}
-
-// Copies |input| into |output|, the output buffer should be a staging buffer
-// that is CPU writable.
-bool CopyDataToBuffer(base::span<const uint8_t> input,
- ID3D11DeviceContext* device_context,
- ID3D11Buffer* output) {
- D3D11_BUFFER_DESC output_buffer_desc = {};
- output->GetDesc(&output_buffer_desc);
-
- if (output_buffer_desc.ByteWidth < input.size()) {
- DVLOG(1) << input.size() << " does not fit in "
- << output_buffer_desc.ByteWidth;
- return false;
- }
-
- D3D11_MAPPED_SUBRESOURCE map_resource = {};
- HRESULT hresult =
- device_context->Map(output, kSubresourceIndex, D3D11_MAP_WRITE,
- kWaitIfGPUBusy, &map_resource);
- if (FAILED(hresult)) {
- DVLOG(3) << "Failed to map buffer for writing.";
- return false;
- }
- memcpy(map_resource.pData, input.data(), input.size_bytes());
- device_context->Unmap(output, kSubresourceIndex);
- return true;
-}
-
-// Copies |input| into |output|. The input buffer is should be a staging buffer
-// that is CPU readable.
-bool CopyDataOutFromBuffer(ID3D11Buffer* input,
- size_t input_size,
- ID3D11DeviceContext* device_context,
- std::vector<uint8_t>* output) {
- D3D11_MAPPED_SUBRESOURCE map_resource = {};
- HRESULT hresult = device_context->Map(
- input, kSubresourceIndex, D3D11_MAP_READ, kWaitIfGPUBusy, &map_resource);
- if (FAILED(hresult)) {
- DVLOG(3) << "Failed to map buffer for reading.";
- return false;
- }
- output->resize(input_size);
- memcpy(output->data(), map_resource.pData, input_size);
- device_context->Unmap(input, kSubresourceIndex);
- return true;
-}
-
-D3D11_AES_CTR_IV StringIvToD3D11Iv(const std::string& iv) {
- D3D11_AES_CTR_IV d3d11_iv = {};
- DCHECK_LE(iv.size(), 16u);
- memcpy(&d3d11_iv, iv.data(), iv.size());
- return d3d11_iv;
-}
-
-// Returns true if the entire sample is encrypted.
-bool IsWholeSampleEncrypted(const DecryptConfig& decrypt_config,
- size_t sample_size) {
- const auto& subsamples = decrypt_config.subsamples();
- if (subsamples.size() != 1)
- return false;
-
- return subsamples.front().clear_bytes == 0 &&
- subsamples.front().cypher_bytes == sample_size;
-}
-
-// Checks whether |device1| is the same component as |device2|.
-// Note that comparing COM pointers require using their IUnknowns.
-// https://docs.microsoft.com/en-us/windows/desktop/api/unknwn/nf-unknwn-iunknown-queryinterface(q_)
-bool SameDevices(ComD3D11Device device1, ComD3D11Device device2) {
- // For the case where both are nullptrs, they aren't devices, so returning
- // false here.
- if (!device1 || !device2)
- return false;
- Microsoft::WRL::ComPtr<IUnknown> device1_iunknown;
- Microsoft::WRL::ComPtr<IUnknown> device2_iunknown;
- HRESULT hr = device1.CopyTo(device1_iunknown.ReleaseAndGetAddressOf());
- if (FAILED(hr))
- return false;
- hr = device2.CopyTo(device2_iunknown.ReleaseAndGetAddressOf());
- if (FAILED(hr))
- return false;
- return device1_iunknown == device2_iunknown;
-}
-
-// Returns a value that is bigger than or equal to |num| that is a
-// multiple of 16.
-// E.g. num = 15 returns 16, 17 returns 32.
-UINT To16Multiple(size_t num) {
- return ((num + 15) >> 4) << 4;
-}
-
-} // namespace
-
-D3D11Decryptor::D3D11Decryptor(CdmProxyContext* cdm_proxy_context)
- : cdm_proxy_context_(cdm_proxy_context) {
- DCHECK(cdm_proxy_context_);
-}
-
-D3D11Decryptor::~D3D11Decryptor() {}
-
-void D3D11Decryptor::RegisterNewKeyCB(StreamType stream_type,
- NewKeyCB new_key_cb) {
- // TODO(crbug.com/821288): Use RegisterNewKeyCB() on CdmContext, and remove
- // RegisterNewKeyCB from Decryptor interface.
- NOTREACHED();
-}
-
-void D3D11Decryptor::Decrypt(StreamType stream_type,
- scoped_refptr<DecoderBuffer> encrypted,
- DecryptCB decrypt_cb) {
- if (encrypted->end_of_stream()) {
- std::move(decrypt_cb).Run(kSuccess, encrypted);
- return;
- }
-
- const auto* decrypt_config = encrypted->decrypt_config();
- if (!decrypt_config) {
- // Not encrypted, nothing to do.
- std::move(decrypt_cb).Run(kSuccess, encrypted);
- return;
- }
-
- if (decrypt_config->HasPattern()) {
- DVLOG(3) << "Cannot handle pattern decryption.";
- std::move(decrypt_cb).Run(kError, nullptr);
- return;
- }
-
- auto context = cdm_proxy_context_->GetD3D11DecryptContext(
- CdmProxy::KeyType::kDecryptOnly, decrypt_config->key_id());
- if (!context) {
- std::move(decrypt_cb).Run(kNoKey, nullptr);
- return;
- }
-
- // Because DecryptionBlt() implementation checks whether the device, buffers,
- // and the crypto session are from the same device, the buffers have to be
- // recreated.
- if (!InitializeDecryptionBuffer(*context)) {
- std::move(decrypt_cb).Run(kError, nullptr);
- return;
- }
-
- std::vector<uint8_t> output;
- if (IsWholeSampleEncrypted(*encrypted->decrypt_config(),
- encrypted->data_size())) {
- if (!CtrDecrypt(base::make_span(encrypted->data(), encrypted->data_size()),
- encrypted->decrypt_config()->iv(), *context, &output)) {
- std::move(decrypt_cb).Run(kError, nullptr);
- return;
- }
- } else {
- if (!SubsampleCtrDecrypt(encrypted, *context, &output)) {
- std::move(decrypt_cb).Run(kError, nullptr);
- return;
- }
- }
-
- auto decoder_buffer = DecoderBuffer::CopyFrom(output.data(), output.size());
- decoder_buffer->set_timestamp(encrypted->timestamp());
- decoder_buffer->set_duration(encrypted->duration());
- decoder_buffer->set_is_key_frame(encrypted->is_key_frame());
- decoder_buffer->CopySideDataFrom(encrypted->side_data(),
- encrypted->side_data_size());
- std::move(decrypt_cb).Run(kSuccess, decoder_buffer);
-}
-
-void D3D11Decryptor::CancelDecrypt(StreamType stream_type) {
- // Decrypt() calls the DecryptCB synchronously so there's nothing to cancel.
-}
-
-void D3D11Decryptor::InitializeAudioDecoder(const AudioDecoderConfig& config,
- DecoderInitCB init_cb) {
- // D3D11Decryptor does not support audio decoding.
- std::move(init_cb).Run(false);
-}
-
-void D3D11Decryptor::InitializeVideoDecoder(const VideoDecoderConfig& config,
- DecoderInitCB init_cb) {
- // D3D11Decryptor does not support video decoding.
- std::move(init_cb).Run(false);
-}
-
-void D3D11Decryptor::DecryptAndDecodeAudio(
- scoped_refptr<DecoderBuffer> encrypted,
- const AudioDecodeCB& audio_decode_cb) {
- NOTREACHED() << "D3D11Decryptor does not support audio decoding";
-}
-
-void D3D11Decryptor::DecryptAndDecodeVideo(
- scoped_refptr<DecoderBuffer> encrypted,
- const VideoDecodeCB& video_decode_cb) {
- NOTREACHED() << "D3D11Decryptor does not support video decoding";
-}
-
-void D3D11Decryptor::ResetDecoder(StreamType stream_type) {
- NOTREACHED() << "D3D11Decryptor does not support audio/video decoding";
-}
-
-void D3D11Decryptor::DeinitializeDecoder(StreamType stream_type) {
- // D3D11Decryptor does not support audio/video decoding, but since this can be
- // called any time after InitializeAudioDecoder/InitializeVideoDecoder,
- // nothing to be done here.
-}
-
-bool D3D11Decryptor::InitializeDecryptionBuffer(
- const CdmProxyContext::D3D11DecryptContext& decrypt_context) {
- ComPtr<ID3D11Device> crypto_session_device;
- decrypt_context.crypto_session->GetDevice(
- crypto_session_device.ReleaseAndGetAddressOf());
-
- // If they are the same devices, then there is no reason to reinitialize the
- // buffers.
- if (SameDevices(crypto_session_device, device_))
- return true;
-
- device_ = crypto_session_device;
- device_->GetImmediateContext(device_context_.ReleaseAndGetAddressOf());
-
- HRESULT hresult =
- device_context_.CopyTo(video_context_.ReleaseAndGetAddressOf());
- if (FAILED(hresult)) {
- DVLOG(2) << "Failed to get video context.";
- return false;
- }
-
- // The buffer is staging so that the data can be accessed by the CPU and HW.
- if (!CreateBuffer(device_.Get(), D3D11_USAGE_STAGING, 0, // no binding.
- D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE,
- encrypted_sample_buffer_.ReleaseAndGetAddressOf())) {
- DVLOG(2) << "Failed to create buffer for encrypted sample.";
- return false;
- }
-
- // Note that the cpu access flag is 0 because this buffer is used to write the
- // decrypted buffer in HW.
- if (!CreateBuffer(device_.Get(), D3D11_USAGE_DEFAULT,
- D3D11_BIND_RENDER_TARGET,
- 0, // no cpu access.
- decrypted_sample_buffer_.ReleaseAndGetAddressOf())) {
- DVLOG(2) << "Failed to create buffer for decrypted sample.";
- return false;
- }
-
- if (!CreateBuffer(device_.Get(), D3D11_USAGE_STAGING, 0, // no binding.
- D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE,
- cpu_accessible_buffer_.ReleaseAndGetAddressOf())) {
- DVLOG(2) << "Failed to create cpu accessible buffer.";
- return false;
- }
-
- return true;
-}
-
-bool D3D11Decryptor::CtrDecrypt(
- base::span<const uint8_t> input,
- const std::string& iv,
- const CdmProxyContext::D3D11DecryptContext& context,
- std::vector<uint8_t>* output) {
- output->clear();
- if (input.empty())
- return true;
-
- if (!CopyDataToBuffer(input, device_context_.Get(),
- encrypted_sample_buffer_.Get())) {
- return false;
- }
-
- D3D11_AES_CTR_IV aes_ctr_iv = StringIvToD3D11Iv(iv);
-
- // The size of the encrypted bytes must be a multiple of 16. See more at
- // https://crbug.com/849466.
- D3D11_ENCRYPTED_BLOCK_INFO block_info = {};
- block_info.NumEncryptedBytesAtBeginning = To16Multiple(input.size());
- DCHECK_LE(block_info.NumEncryptedBytesAtBeginning, kBufferSize);
- block_info.NumBytesInSkipPattern =
- kBufferSize - block_info.NumEncryptedBytesAtBeginning;
-
- // ID3D11Buffers should be used but since the interface takes ID3D11Texture2D,
- // it is reinterpret cast. See more at https://crbug.com/849466.
- video_context_->DecryptionBlt(
- context.crypto_session,
- reinterpret_cast<ID3D11Texture2D*>(encrypted_sample_buffer_.Get()),
- reinterpret_cast<ID3D11Texture2D*>(decrypted_sample_buffer_.Get()),
- &block_info, context.key_blob_size, context.key_blob, sizeof(aes_ctr_iv),
- &aes_ctr_iv);
-
- // Because DecryptionBlt() doesn't have a return value, this is a hack to
- // check for decryption operation status. If it has been modified, then there
- // was an error. See more at https://crbug.com/849466.
- HRESULT result = static_cast<HRESULT>(block_info.NumBytesInEncryptPattern);
- if (FAILED(result)) {
- DVLOG(3) << "Decryption error :"
- << logging::SystemErrorCodeToString(result);
- return false;
- }
-
- device_context_->CopyResource(cpu_accessible_buffer_.Get(),
- decrypted_sample_buffer_.Get());
- return CopyDataOutFromBuffer(cpu_accessible_buffer_.Get(), input.size(),
- device_context_.Get(), output);
-}
-
-// TODO(crbug.com/845631): This is the same as DecryptCencBuffer(), so it should
-// be deduped.
-bool D3D11Decryptor::SubsampleCtrDecrypt(
- scoped_refptr<DecoderBuffer> encrypted,
- const CdmProxyContext::D3D11DecryptContext& context,
- std::vector<uint8_t>* output) {
- const auto& subsamples = encrypted->decrypt_config()->subsamples();
- std::vector<uint8_t> encrypted_data;
- const uint8_t* data = encrypted->data();
- for (const auto& subsample : subsamples) {
- data += subsample.clear_bytes;
- encrypted_data.insert(encrypted_data.end(), data,
- data + subsample.cypher_bytes);
- data += subsample.cypher_bytes;
- }
-
- std::vector<uint8_t> decrypted_data;
- if (!CtrDecrypt(encrypted_data, encrypted->decrypt_config()->iv(), context,
- &decrypted_data)) {
- return false;
- }
-
- data = encrypted->data();
- const uint8_t* decrypted_data_ptr = decrypted_data.data();
- for (const auto& subsample : subsamples) {
- output->insert(output->end(), data, data + subsample.clear_bytes);
- data += subsample.clear_bytes;
- output->insert(output->end(), decrypted_data_ptr,
- decrypted_data_ptr + subsample.cypher_bytes);
- decrypted_data_ptr += subsample.cypher_bytes;
- data += subsample.cypher_bytes;
- }
- return true;
-}
-
-} // namespace media
diff --git a/chromium/media/gpu/windows/d3d11_decryptor.h b/chromium/media/gpu/windows/d3d11_decryptor.h
deleted file mode 100644
index 2f49a28c13a..00000000000
--- a/chromium/media/gpu/windows/d3d11_decryptor.h
+++ /dev/null
@@ -1,96 +0,0 @@
-// Copyright 2018 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_GPU_WINDOWS_D3D11_DECRYPTOR_H_
-#define MEDIA_GPU_WINDOWS_D3D11_DECRYPTOR_H_
-
-#include <wrl/client.h>
-
-#include "base/containers/span.h"
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "media/base/decryptor.h"
-#include "media/base/win/d3d11_create_device_cb.h"
-#include "media/cdm/cdm_proxy_context.h"
-#include "media/gpu/media_gpu_export.h"
-
-namespace media {
-
-class MEDIA_GPU_EXPORT D3D11Decryptor : public Decryptor {
- public:
- explicit D3D11Decryptor(CdmProxyContext* cdm_proxy_context);
- ~D3D11Decryptor() final;
-
- // Decryptor implementation.
- void RegisterNewKeyCB(StreamType stream_type, NewKeyCB key_added_cb) final;
- void Decrypt(StreamType stream_type,
- scoped_refptr<DecoderBuffer> encrypted,
- DecryptCB decrypt_cb) final;
- void CancelDecrypt(StreamType stream_type) final;
- void InitializeAudioDecoder(const AudioDecoderConfig& config,
- DecoderInitCB init_cb) final;
- void InitializeVideoDecoder(const VideoDecoderConfig& config,
- DecoderInitCB init_cb) final;
- void DecryptAndDecodeAudio(scoped_refptr<DecoderBuffer> encrypted,
- const AudioDecodeCB& audio_decode_cb) final;
- void DecryptAndDecodeVideo(scoped_refptr<DecoderBuffer> encrypted,
- const VideoDecodeCB& video_decode_cb) final;
- void ResetDecoder(StreamType stream_type) final;
- void DeinitializeDecoder(StreamType stream_type) final;
-
- private:
- // Initialize the buffers for decryption from decryption context.
- bool InitializeDecryptionBuffer(
- const CdmProxyContext::D3D11DecryptContext& decrypt_context);
-
- // CTR mode decrypts |encrypted| data into |output|. |output| is always
- // cleared. Returns true on success.
- bool CtrDecrypt(base::span<const uint8_t> input,
- const std::string& iv,
- const CdmProxyContext::D3D11DecryptContext& context,
- std::vector<uint8_t>* output);
-
- // CTR mode decryption method, aware of subsamples. |output| is always
- // cleared. Returns true and populates |output| on success.
- bool SubsampleCtrDecrypt(scoped_refptr<DecoderBuffer> encrypted,
- const CdmProxyContext::D3D11DecryptContext& context,
- std::vector<uint8_t>* output);
-
- CdmProxyContext* cdm_proxy_context_;
-
- template <class T>
- using ComPtr = Microsoft::WRL::ComPtr<T>;
-
- // After a successful InitializeDecryptionBuffer() call, these are set for the
- // current Decrypt() call.
- ComPtr<ID3D11Device> device_;
- ComPtr<ID3D11DeviceContext> device_context_;
- ComPtr<ID3D11VideoContext> video_context_;
-
- // Due to how D3D11 resource permissons work, there are differences between
- // CPU (user) and HW accessible buffers. And things get more complicated with
- // what can read or write from/to it, what combinations are valid, and
- // performance tradeoffs in giving different permissions. The most straight
- // forward way is to use three buffers as described below.
-
- // A buffer where encrypted data is written by the CPU and is readable by the
- // HW.
- ComPtr<ID3D11Buffer> encrypted_sample_buffer_;
-
- // A buffer where the decrypted buffer is written by the HW that is not CPU
- // accessible.
- ComPtr<ID3D11Buffer> decrypted_sample_buffer_;
-
- // A CPU accessible buffer where the content of |decrypted_buffer_| is copied
- // to.
- ComPtr<ID3D11Buffer> cpu_accessible_buffer_;
-
- base::WeakPtrFactory<D3D11Decryptor> weak_factory_{this};
-
- DISALLOW_COPY_AND_ASSIGN(D3D11Decryptor);
-};
-
-} // namespace media
-
-#endif // MEDIA_GPU_WINDOWS_D3D11_DECRYPTOR_H_
diff --git a/chromium/media/gpu/windows/d3d11_decryptor_unittest.cc b/chromium/media/gpu/windows/d3d11_decryptor_unittest.cc
deleted file mode 100644
index f00dbec95e7..00000000000
--- a/chromium/media/gpu/windows/d3d11_decryptor_unittest.cc
+++ /dev/null
@@ -1,519 +0,0 @@
-// Copyright 2018 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/gpu/windows/d3d11_decryptor.h"
-
-#include <initguid.h>
-
-#include <array>
-
-#include "base/bind.h"
-#include "base/containers/span.h"
-#include "base/stl_util.h"
-#include "media/base/decoder_buffer.h"
-#include "media/base/subsample_entry.h"
-#include "media/base/win/d3d11_mocks.h"
-#include "media/cdm/cdm_proxy_context.h"
-
-using ::testing::_;
-using ::testing::AtLeast;
-using ::testing::DoAll;
-using ::testing::ElementsAreArray;
-using ::testing::Invoke;
-using ::testing::IsNull;
-using ::testing::Mock;
-using ::testing::Pointee;
-using ::testing::Return;
-using ::testing::SetArgPointee;
-
-template <class T>
-using ComPtr = Microsoft::WRL::ComPtr<T>;
-
-namespace media {
-
-namespace {
-// clang-format off
-// The value doesn't matter this is just a GUID.
-DEFINE_GUID(TEST_GUID,
- 0x01020304, 0xffee, 0xefba,
- 0x93, 0xaa, 0x47, 0x77, 0x43, 0xb1, 0x22, 0x98);
-// clang-format on
-
-// Should be non-0 so that it's different from the default TimeDelta.
-constexpr base::TimeDelta kTestTimestamp =
- base::TimeDelta::FromMilliseconds(33);
-
-const uint8_t kAnyKeyBlob[] = {3, 5, 38, 19};
-const char kKeyId[] = "some 16 byte id.";
-const char kIv[] = "some 16 byte iv.";
-
-// For tests where the input doesn't matter.
-const uint8_t kAnyInput[] = {
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
-};
-
-const SubsampleEntry kAnyInputSubsample(0, base::size(kAnyInput));
-
-scoped_refptr<DecoderBuffer> TestDecoderBuffer(
- const uint8_t* input,
- size_t data_size,
- const std::string& key_id,
- const std::string& iv,
- const std::vector<SubsampleEntry>& subsamples) {
- scoped_refptr<DecoderBuffer> encrypted_buffer =
- DecoderBuffer::CopyFrom(input, data_size);
-
- encrypted_buffer->set_decrypt_config(
- DecryptConfig::CreateCencConfig(key_id, iv, subsamples));
- encrypted_buffer->set_timestamp(kTestTimestamp);
- return encrypted_buffer;
-}
-
-CdmProxyContext::D3D11DecryptContext TestDecryptContext(
- ComPtr<D3D11CryptoSessionMock> crypto_session_mock) {
- CdmProxyContext::D3D11DecryptContext decrypt_context = {};
- decrypt_context.crypto_session = crypto_session_mock.Get();
- decrypt_context.key_blob = kAnyKeyBlob;
- decrypt_context.key_blob_size = base::size(kAnyKeyBlob);
- decrypt_context.key_info_guid = TEST_GUID;
- return decrypt_context;
-}
-
-class CallbackMock {
- public:
- MOCK_METHOD2(DecryptCallback, Decryptor::DecryptCB::RunType);
-};
-
-class CdmProxyContextMock : public CdmProxyContext {
- public:
- MOCK_METHOD2(GetD3D11DecryptContext,
- base::Optional<D3D11DecryptContext>(CdmProxy::KeyType key_type,
- const std::string& key_id));
-};
-
-// Checks that BUFFER_DESC has these fields match.
-// Flags are ORed values, so this only checks that the expected flags are set.
-// The other fields are ignored.
-MATCHER_P3(BufferDescHas, usage, bind_flags, cpu_access, "") {
- const D3D11_BUFFER_DESC& buffer_desc = *arg;
- if (buffer_desc.Usage != usage)
- return false;
-
- // Because the flags are enums the compiler infers that the input flags are
- // signed ints. And the compiler rejects comparing signed int and unsigned
- // int, so they are cast here.
- const UINT unsigned_bind_flags = bind_flags;
- const UINT unsigned_cpu_access_flags = cpu_access;
-
- if ((buffer_desc.BindFlags & unsigned_bind_flags) != unsigned_bind_flags)
- return false;
-
- return (buffer_desc.CPUAccessFlags & unsigned_cpu_access_flags) ==
- unsigned_cpu_access_flags;
-}
-
-// NumEncryptedBytesAtBeginning must be greater than or equal to the size of the
-// encrypted data, and also a multiple of 16.
-MATCHER_P(NumEncryptedBytesAtBeginningGreaterOrEq, value, "") {
- const D3D11_ENCRYPTED_BLOCK_INFO& block_info = *arg;
- if (block_info.NumEncryptedBytesAtBeginning < value) {
- *result_listener << block_info.NumEncryptedBytesAtBeginning
- << " is not less than " << value;
- return false;
- }
- return block_info.NumEncryptedBytesAtBeginning % 16 == 0;
-}
-
-ACTION_P(SetBufferDescSize, size) {
- arg0->ByteWidth = size;
-}
-
-MATCHER_P2(OutputDataEquals, data, size, "") {
- scoped_refptr<DecoderBuffer> buffer = arg;
- if (size != buffer->data_size()) {
- return false;
- }
- if (buffer->timestamp() != kTestTimestamp) {
- return false;
- }
-
- std::vector<uint8_t> expected(data, data + size);
- std::vector<uint8_t> actual(buffer->data(),
- buffer->data() + buffer->data_size());
- return actual == expected;
-}
-
-} // namespace
-
-class D3D11DecryptorTest : public ::testing::Test {
- protected:
- void SetUp() override {
- decryptor_ = std::make_unique<D3D11Decryptor>(&mock_proxy_);
-
- device_mock_ = CreateD3D11Mock<D3D11DeviceMock>();
- device_context_mock_ = CreateD3D11Mock<D3D11DeviceContextMock>();
- video_context_mock_ = CreateD3D11Mock<D3D11VideoContextMock>();
- staging_buffer1_ = CreateD3D11Mock<D3D11BufferMock>();
- staging_buffer2_ = CreateD3D11Mock<D3D11BufferMock>();
- gpu_buffer_ = CreateD3D11Mock<D3D11BufferMock>();
- }
-
- // Only sets mock expectations to the objects. Use this for the case where the
- // buffers are expected to be created from |device_mock_|, that's accessible
- // through |crypto_session_mock|'s GetDevice() function.
- void SetExpectationsForSuccessfulBufferInitialization(
- D3D11CryptoSessionMock* crypto_session_mock,
- CdmProxyContext::D3D11DecryptContext* decrypt_context) {
- // As noted in the function comment, the device is accessible from the
- // crypto session.
- EXPECT_CALL(*crypto_session_mock, GetDevice(_))
- .Times(AtLeast(1))
- .WillRepeatedly(SetComPointee<0>(device_mock_.Get()));
-
- // The other components accessible (directly or indirectly) from the device.
- COM_EXPECT_CALL(device_mock_, GetImmediateContext(_))
- .Times(AtLeast(1))
- .WillRepeatedly(SetComPointee<0>(device_context_mock_.Get()));
- COM_EXPECT_CALL(device_context_mock_,
- QueryInterface(IID_ID3D11VideoContext, _))
- .Times(AtLeast(1))
- .WillRepeatedly(SetComPointeeAndReturnOk<1>(video_context_mock_.Get()));
-
- EXPECT_CALL(mock_proxy_,
- GetD3D11DecryptContext(CdmProxy::KeyType::kDecryptOnly, kKeyId))
- .WillOnce(Return(*decrypt_context));
-
- // These return big enough size.
- COM_ON_CALL(staging_buffer1_, GetDesc(_))
- .WillByDefault(SetBufferDescSize(20000));
- COM_ON_CALL(staging_buffer2_, GetDesc(_))
- .WillByDefault(SetBufferDescSize(20000));
- COM_ON_CALL(gpu_buffer_, GetDesc(_))
- .WillByDefault(SetBufferDescSize(20000));
-
- // It should be requesting for 2 staging buffers one for writing the data to
- // a GPU buffer and one for reading from the a GPU buffer.
- COM_EXPECT_CALL(device_mock_,
- CreateBuffer(BufferDescHas(D3D11_USAGE_STAGING, 0u,
- D3D11_CPU_ACCESS_READ |
- D3D11_CPU_ACCESS_WRITE),
- nullptr, _))
- .WillOnce(SetComPointeeAndReturnOk<2>(staging_buffer1_.Get()))
- .WillOnce(SetComPointeeAndReturnOk<2>(staging_buffer2_.Get()));
-
- // It should be requesting a GPU only accessible buffer to the decrypted
- // output.
- COM_EXPECT_CALL(device_mock_,
- CreateBuffer(BufferDescHas(D3D11_USAGE_DEFAULT,
- D3D11_BIND_RENDER_TARGET, 0u),
- nullptr, _))
- .WillOnce(SetComPointeeAndReturnOk<2>(gpu_buffer_.Get()));
- }
-
- // |input| is the input to the Decrypt() function, the subsample information
- // is in |subsamples| therefore |input| may not be entirely encrypted. The
- // data that is encrypted in |input| should be |encrypted_input|.
- // |whole_output| is the expected output from the Decrypt() call, reported by
- // the callback. The decrypted result of |encrypted_input| should be
- // |decrypted_output|.
- void ExpectSuccessfulDecryption(D3D11CryptoSessionMock* crypto_session_mock,
- base::span<const uint8_t> input,
- base::span<const uint8_t> encrypted_input,
- base::span<const uint8_t> whole_output,
- base::span<const uint8_t> decrypted_output,
- const std::vector<SubsampleEntry>& subsamples,
- D3D11Decryptor* decryptor) {
- D3D11_MAPPED_SUBRESOURCE staging_buffer1_subresource = {};
- auto staging_buffer1_subresource_buffer =
- std::make_unique<uint8_t[]>(20000);
- staging_buffer1_subresource.pData =
- staging_buffer1_subresource_buffer.get();
-
- // It should be requesting for a memory mapped buffer, from the staging
- // buffer, to pass the encrypted data to the GPU.
- COM_EXPECT_CALL(device_context_mock_,
- Map(staging_buffer1_.Get(), 0, D3D11_MAP_WRITE, _, _))
- .WillOnce(
- DoAll(SetArgPointee<4>(staging_buffer1_subresource), Return(S_OK)));
- COM_EXPECT_CALL(device_context_mock_, Unmap(staging_buffer1_.Get(), 0));
-
- COM_EXPECT_CALL(
- video_context_mock_,
- DecryptionBlt(
- crypto_session_mock,
- reinterpret_cast<ID3D11Texture2D*>(staging_buffer1_.Get()),
- reinterpret_cast<ID3D11Texture2D*>(gpu_buffer_.Get()),
- NumEncryptedBytesAtBeginningGreaterOrEq(encrypted_input.size()),
- sizeof(kAnyKeyBlob), kAnyKeyBlob, _, _));
- COM_EXPECT_CALL(device_context_mock_,
- CopyResource(staging_buffer2_.Get(), gpu_buffer_.Get()));
-
- D3D11_MAPPED_SUBRESOURCE staging_buffer2_subresource = {};
-
- // pData field is non-const void* so make a copy of kFakeDecryptedData that
- // can be cast to void*.
- std::unique_ptr<uint8_t[]> decrypted_data =
- std::make_unique<uint8_t[]>(decrypted_output.size());
- memcpy(decrypted_data.get(), decrypted_output.data(),
- decrypted_output.size());
- staging_buffer2_subresource.pData = decrypted_data.get();
-
- // Tt should be requesting for a memory mapped buffer, from the staging
- // buffer, to read the decrypted data out from the GPU buffer.
- COM_EXPECT_CALL(device_context_mock_,
- Map(staging_buffer2_.Get(), 0, D3D11_MAP_READ, _, _))
- .WillOnce(
- DoAll(SetArgPointee<4>(staging_buffer2_subresource), Return(S_OK)));
- COM_EXPECT_CALL(device_context_mock_, Unmap(staging_buffer2_.Get(), 0));
-
- CallbackMock callbacks;
- EXPECT_CALL(callbacks,
- DecryptCallback(Decryptor::kSuccess,
- OutputDataEquals(whole_output.data(),
- whole_output.size())));
-
- scoped_refptr<DecoderBuffer> encrypted_buffer =
- TestDecoderBuffer(input.data(), input.size(), kKeyId, kIv, subsamples);
- decryptor->Decrypt(Decryptor::kAudio, encrypted_buffer,
- base::BindRepeating(&CallbackMock::DecryptCallback,
- base::Unretained(&callbacks)));
-
- // Verify that the data copied to the GPU buffer is the encrypted portion.
- EXPECT_TRUE(std::equal(encrypted_input.begin(), encrypted_input.end(),
- staging_buffer1_subresource_buffer.get()));
- }
-
- std::unique_ptr<D3D11Decryptor> decryptor_;
- CdmProxyContextMock mock_proxy_;
-
- ComPtr<D3D11DeviceMock> device_mock_;
- ComPtr<D3D11DeviceContextMock> device_context_mock_;
- ComPtr<D3D11VideoContextMock> video_context_mock_;
-
- private:
- ComPtr<D3D11BufferMock> staging_buffer1_;
- ComPtr<D3D11BufferMock> staging_buffer2_;
- ComPtr<D3D11BufferMock> gpu_buffer_;
-};
-
-// Verify that full sample encrypted sample works.
-TEST_F(D3D11DecryptorTest, FullSampleCtrDecrypt) {
- const uint8_t kInput[] = {
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
- };
- const SubsampleEntry kSubsample(0, base::size(kInput));
- // This is arbitrary. Just used to check that this value is output from the
- // method.
- const uint8_t kFakeDecryptedData[] = {
- 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,
- };
- static_assert(base::size(kFakeDecryptedData) == base::size(kInput),
- "Fake input and output data size must match.");
-
- ComPtr<D3D11CryptoSessionMock> crypto_session_mock =
- CreateD3D11Mock<D3D11CryptoSessionMock>();
- CdmProxyContext::D3D11DecryptContext decrypt_context =
- TestDecryptContext(crypto_session_mock);
- SetExpectationsForSuccessfulBufferInitialization(crypto_session_mock.Get(),
- &decrypt_context);
-
- // The entire sample is encrypted so the encrypted/decrypted portions are the
- // input/output.
- ExpectSuccessfulDecryption(crypto_session_mock.Get(), kInput, kInput,
- kFakeDecryptedData, kFakeDecryptedData,
- {kSubsample}, decryptor_.get());
-}
-
-// Verify that it works for encrypted input that's not a multiple of 16.
-TEST_F(D3D11DecryptorTest, InputSizeNotMultipleOf16) {
- const uint8_t kInput[] = {
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
- };
- const SubsampleEntry kSubsample(0, base::size(kInput));
- // This is arbitrary. Just used to check that this value is output from the
- // method.
- const uint8_t kFakeDecryptedData[] = {
- 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,
- };
- static_assert(base::size(kFakeDecryptedData) == base::size(kInput),
- "Fake input and output data size must match.");
-
- ComPtr<D3D11CryptoSessionMock> crypto_session_mock =
- CreateD3D11Mock<D3D11CryptoSessionMock>();
- CdmProxyContext::D3D11DecryptContext decrypt_context =
- TestDecryptContext(crypto_session_mock);
-
- SetExpectationsForSuccessfulBufferInitialization(crypto_session_mock.Get(),
- &decrypt_context);
-
- // The entire sample is encrypted so the encrypted/decrypted portions are the
- // input/output.
- ExpectSuccessfulDecryption(crypto_session_mock.Get(), kInput, kInput,
- kFakeDecryptedData, kFakeDecryptedData,
- {kSubsample}, decryptor_.get());
-}
-
-// Verify subsample decryption works.
-TEST_F(D3D11DecryptorTest, SubsampleCtrDecrypt) {
- // clang-format off
- const uint8_t kInput[] = {
- // clear 16 bytes.
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
- // encrypted 16 bytes.
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
- // clear 5 bytes.
- 0, 1, 2, 3, 4,
- // encrypted 16 bytes.
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
- };
- // Encrypted parts of the input
- const uint8_t kInputEncrypted[] = {
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
- };
- // This is arbitrary. Just used to check that this value is output from the
- // method.
- const uint8_t kFakeOutputData[] = {
- // clear 16 bytes.
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
- // decrypted 16 bytes.
- 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,
- // clear 5 bytes.
- 0, 1, 2, 3, 4,
- // decrypted 16 bytes.
- 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,
- };
- const uint8_t kFakeDecryptedData[] = {
- 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,
- 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,
- };
- // clang-format on
- static_assert(base::size(kFakeOutputData) == base::size(kInput),
- "Fake input and output data size must match.");
- const std::vector<SubsampleEntry> subsamples = {SubsampleEntry(16, 16),
- SubsampleEntry(5, 16)};
-
- ComPtr<D3D11CryptoSessionMock> crypto_session_mock =
- CreateD3D11Mock<D3D11CryptoSessionMock>();
- CdmProxyContext::D3D11DecryptContext decrypt_context =
- TestDecryptContext(crypto_session_mock);
-
- SetExpectationsForSuccessfulBufferInitialization(crypto_session_mock.Get(),
- &decrypt_context);
- ExpectSuccessfulDecryption(crypto_session_mock.Get(), kInput, kInputEncrypted,
- kFakeOutputData, kFakeDecryptedData, subsamples,
- decryptor_.get());
-}
-
-// Verify that if the input is too big, it fails. This may be removed if the
-// implementation supports big input.
-TEST_F(D3D11DecryptorTest, DecryptInputTooBig) {
- // Something pretty big to be an audio frame. The actual data size doesn't
- // matter.
- std::array<uint8_t, 1000000> kInput;
- const SubsampleEntry kSubsample(0, base::size(kInput));
-
- ComPtr<D3D11CryptoSessionMock> crypto_session_mock =
- CreateD3D11Mock<D3D11CryptoSessionMock>();
- CdmProxyContext::D3D11DecryptContext decrypt_context =
- TestDecryptContext(crypto_session_mock);
-
- SetExpectationsForSuccessfulBufferInitialization(crypto_session_mock.Get(),
- &decrypt_context);
- CallbackMock callbacks;
- EXPECT_CALL(callbacks, DecryptCallback(Decryptor::kError, IsNull()));
-
- scoped_refptr<DecoderBuffer> encrypted_buffer = TestDecoderBuffer(
- kInput.data(), base::size(kInput), kKeyId, kIv, {kSubsample});
- decryptor_->Decrypt(Decryptor::kAudio, encrypted_buffer,
- base::BindRepeating(&CallbackMock::DecryptCallback,
- base::Unretained(&callbacks)));
-}
-
-// If there is no decrypt config, it must be in the clear, so it shouldn't
-// change the output.
-TEST_F(D3D11DecryptorTest, NoDecryptConfig) {
- scoped_refptr<DecoderBuffer> clear_buffer =
- DecoderBuffer::CopyFrom(kAnyInput, base::size(kAnyInput));
- clear_buffer->set_timestamp(kTestTimestamp);
- CallbackMock callbacks;
- EXPECT_CALL(
- callbacks,
- DecryptCallback(Decryptor::kSuccess,
- OutputDataEquals(kAnyInput, base::size(kAnyInput))));
- decryptor_->Decrypt(Decryptor::kAudio, clear_buffer,
- base::BindRepeating(&CallbackMock::DecryptCallback,
- base::Unretained(&callbacks)));
-}
-
-// The current decryptor cannot deal with pattern encryption.
-TEST_F(D3D11DecryptorTest, PatternDecryption) {
- scoped_refptr<DecoderBuffer> encrypted_buffer =
- DecoderBuffer::CopyFrom(kAnyInput, base::size(kAnyInput));
- encrypted_buffer->set_decrypt_config(DecryptConfig::CreateCbcsConfig(
- kKeyId, kIv, {kAnyInputSubsample}, EncryptionPattern(1, 9)));
-
- CallbackMock callbacks;
- EXPECT_CALL(callbacks, DecryptCallback(Decryptor::kError, IsNull()));
- decryptor_->Decrypt(Decryptor::kAudio, encrypted_buffer,
- base::BindRepeating(&CallbackMock::DecryptCallback,
- base::Unretained(&callbacks)));
-}
-
-// If there is no decrypt context, it's missing a key.
-TEST_F(D3D11DecryptorTest, NoDecryptContext) {
- scoped_refptr<DecoderBuffer> encrypted_buffer = TestDecoderBuffer(
- kAnyInput, base::size(kAnyInput), kKeyId, kIv, {kAnyInputSubsample});
-
- EXPECT_CALL(mock_proxy_,
- GetD3D11DecryptContext(CdmProxy::KeyType::kDecryptOnly, kKeyId))
- .WillOnce(Return(base::nullopt));
-
- CallbackMock callbacks;
- EXPECT_CALL(callbacks, DecryptCallback(Decryptor::kNoKey, IsNull()));
- decryptor_->Decrypt(Decryptor::kAudio, encrypted_buffer,
- base::BindRepeating(&CallbackMock::DecryptCallback,
- base::Unretained(&callbacks)));
-}
-
-// Verify that if the crypto session's device is the same as the previous call,
-// the buffers aren't recreated.
-TEST_F(D3D11DecryptorTest, ReuseBuffers) {
- ComPtr<D3D11CryptoSessionMock> crypto_session_mock =
- CreateD3D11Mock<D3D11CryptoSessionMock>();
- CdmProxyContext::D3D11DecryptContext decrypt_context =
- TestDecryptContext(crypto_session_mock);
-
- SetExpectationsForSuccessfulBufferInitialization(crypto_session_mock.Get(),
- &decrypt_context);
-
- // This test doesn't require checking the output or the correctness of the
- // decyrption, so just pass the input buffer for output.
- ExpectSuccessfulDecryption(crypto_session_mock.Get(), kAnyInput, kAnyInput,
- kAnyInput, kAnyInput, {kAnyInputSubsample},
- decryptor_.get());
- Mock::VerifyAndClearExpectations(crypto_session_mock.Get());
- Mock::VerifyAndClearExpectations(device_mock_.Get());
- Mock::VerifyAndClearExpectations(video_context_mock_.Get());
- Mock::VerifyAndClearExpectations(device_context_mock_.Get());
- Mock::VerifyAndClearExpectations(&mock_proxy_);
-
- COM_EXPECT_CALL(crypto_session_mock, GetDevice(_))
- .Times(AtLeast(1))
- .WillRepeatedly(SetComPointee<0>(device_mock_.Get()));
- EXPECT_CALL(mock_proxy_,
- GetD3D11DecryptContext(CdmProxy::KeyType::kDecryptOnly, kKeyId))
- .WillOnce(Return(decrypt_context));
-
- // Buffers should not be (re)initialized on the next call to decrypt because
- // it's the same device as the previous call.
- COM_EXPECT_CALL(device_mock_, CreateBuffer(_, _, _)).Times(0);
-
- // This calls Decrypt() so that the expectations above are triggered.
- ExpectSuccessfulDecryption(crypto_session_mock.Get(), kAnyInput, kAnyInput,
- kAnyInput, kAnyInput, {kAnyInputSubsample},
- decryptor_.get());
-}
-
-} // namespace media
diff --git a/chromium/media/gpu/windows/d3d11_h264_accelerator.cc b/chromium/media/gpu/windows/d3d11_h264_accelerator.cc
index b1e6e5edd7d..df549d3a380 100644
--- a/chromium/media/gpu/windows/d3d11_h264_accelerator.cc
+++ b/chromium/media/gpu/windows/d3d11_h264_accelerator.cc
@@ -10,7 +10,6 @@
#include "base/trace_event/trace_event.h"
#include "media/base/media_log.h"
#include "media/base/win/mf_helpers.h"
-#include "media/cdm/cdm_proxy_context.h"
#include "media/gpu/h264_decoder.h"
#include "media/gpu/h264_dpb.h"
#include "media/gpu/windows/d3d11_picture_buffer.h"
@@ -64,20 +63,16 @@ D3D11H264Picture::~D3D11H264Picture() {
D3D11H264Accelerator::D3D11H264Accelerator(
D3D11VideoDecoderClient* client,
MediaLog* media_log,
- CdmProxyContext* cdm_proxy_context,
ComD3D11VideoDecoder video_decoder,
ComD3D11VideoDevice video_device,
std::unique_ptr<VideoContextWrapper> video_context)
: client_(client),
media_log_(media_log),
- cdm_proxy_context_(cdm_proxy_context),
video_decoder_(video_decoder),
video_device_(video_device),
video_context_(std::move(video_context)) {
DCHECK(client);
DCHECK(media_log_);
- // |cdm_proxy_context_| is non-null for encrypted content but can be null for
- // clear content.
}
D3D11H264Accelerator::~D3D11H264Accelerator() {}
@@ -99,28 +94,9 @@ DecoderStatus D3D11H264Accelerator::SubmitFrameMetadata(
const H264Picture::Vector& ref_pic_listb1,
scoped_refptr<H264Picture> pic) {
const bool is_encrypted = pic->decrypt_config();
-
- std::unique_ptr<D3D11_VIDEO_DECODER_BEGIN_FRAME_CRYPTO_SESSION> content_key;
- // This decrypt context has to be outside the if block because pKeyInfo in
- // D3D11_VIDEO_DECODER_BEGIN_FRAME_CRYPTO_SESSION is a pointer (to a GUID).
- base::Optional<CdmProxyContext::D3D11DecryptContext> decrypt_context;
if (is_encrypted) {
- DCHECK(cdm_proxy_context_) << "No CdmProxyContext but picture is encrypted";
- decrypt_context = cdm_proxy_context_->GetD3D11DecryptContext(
- CdmProxy::KeyType::kDecryptAndDecode, pic->decrypt_config()->key_id());
- if (!decrypt_context) {
- RecordFailure("Cannot find decrypt context for the frame.");
- return DecoderStatus::kTryAgain;
- }
-
- content_key =
- std::make_unique<D3D11_VIDEO_DECODER_BEGIN_FRAME_CRYPTO_SESSION>();
- content_key->pCryptoSession = decrypt_context->crypto_session;
- content_key->pBlob = const_cast<void*>(decrypt_context->key_blob);
- content_key->BlobSize = decrypt_context->key_blob_size;
- content_key->pKeyInfoId = &decrypt_context->key_info_guid;
- frame_iv_.assign(pic->decrypt_config()->iv().begin(),
- pic->decrypt_config()->iv().end());
+ RecordFailure("Cannot find decrypt context for the frame.");
+ return DecoderStatus::kFail;
}
HRESULT hr;
@@ -128,7 +104,7 @@ DecoderStatus D3D11H264Accelerator::SubmitFrameMetadata(
hr = video_context_->DecoderBeginFrame(
video_decoder_.Get(),
static_cast<D3D11H264Picture*>(pic.get())->picture->output_view().Get(),
- content_key ? sizeof(*content_key) : 0, content_key.get());
+ 0, nullptr);
if (hr == E_PENDING || hr == D3DERR_WASSTILLDRAWING) {
// Hardware is busy. We should make the call again.
diff --git a/chromium/media/gpu/windows/d3d11_h264_accelerator.h b/chromium/media/gpu/windows/d3d11_h264_accelerator.h
index 7022dc8c395..cd9dd468755 100644
--- a/chromium/media/gpu/windows/d3d11_h264_accelerator.h
+++ b/chromium/media/gpu/windows/d3d11_h264_accelerator.h
@@ -26,17 +26,14 @@
#include "ui/gl/gl_image.h"
namespace media {
-class CdmProxyContext;
+
class D3D11H264Accelerator;
class MediaLog;
-
class D3D11H264Accelerator : public H264Decoder::H264Accelerator {
public:
- // |cdm_proxy_context| may be null for clear content.
D3D11H264Accelerator(D3D11VideoDecoderClient* client,
MediaLog* media_log,
- CdmProxyContext* cdm_proxy_context,
ComD3D11VideoDecoder video_decoder,
ComD3D11VideoDevice video_device,
std::unique_ptr<VideoContextWrapper> video_context);
@@ -90,7 +87,6 @@ class D3D11H264Accelerator : public H264Decoder::H264Accelerator {
D3D11VideoDecoderClient* client_;
MediaLog* media_log_ = nullptr;
- CdmProxyContext* const cdm_proxy_context_;
ComD3D11VideoDecoder video_decoder_;
ComD3D11VideoDevice video_device_;
diff --git a/chromium/media/gpu/windows/d3d11_picture_buffer.cc b/chromium/media/gpu/windows/d3d11_picture_buffer.cc
index ae193f7c4a1..7c0278b690e 100644
--- a/chromium/media/gpu/windows/d3d11_picture_buffer.cc
+++ b/chromium/media/gpu/windows/d3d11_picture_buffer.cc
@@ -22,11 +22,14 @@
namespace media {
D3D11PictureBuffer::D3D11PictureBuffer(
+ scoped_refptr<base::SequencedTaskRunner> delete_task_runner,
ComD3D11Texture2D texture,
std::unique_ptr<Texture2DWrapper> texture_wrapper,
gfx::Size size,
size_t level)
- : texture_(std::move(texture)),
+ : RefCountedDeleteOnSequence<D3D11PictureBuffer>(
+ std::move(delete_task_runner)),
+ texture_(std::move(texture)),
texture_wrapper_(std::move(texture_wrapper)),
size_(size),
level_(level) {}
@@ -34,16 +37,19 @@ D3D11PictureBuffer::D3D11PictureBuffer(
D3D11PictureBuffer::~D3D11PictureBuffer() {
}
-bool D3D11PictureBuffer::Init(GetCommandBufferHelperCB get_helper_cb,
- ComD3D11VideoDevice video_device,
- const GUID& decoder_guid,
- std::unique_ptr<MediaLog> media_log) {
+bool D3D11PictureBuffer::Init(
+ scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,
+ GetCommandBufferHelperCB get_helper_cb,
+ ComD3D11VideoDevice video_device,
+ const GUID& decoder_guid,
+ std::unique_ptr<MediaLog> media_log) {
D3D11_VIDEO_DECODER_OUTPUT_VIEW_DESC view_desc = {};
view_desc.DecodeProfile = decoder_guid;
view_desc.ViewDimension = D3D11_VDOV_DIMENSION_TEXTURE2D;
view_desc.Texture2D.ArraySlice = (UINT)level_;
- if (!texture_wrapper_->Init(std::move(get_helper_cb))) {
+ if (!texture_wrapper_->Init(std::move(gpu_task_runner),
+ std::move(get_helper_cb))) {
MEDIA_LOG(ERROR, media_log) << "Failed to Initialize the wrapper";
return false;
}
diff --git a/chromium/media/gpu/windows/d3d11_picture_buffer.h b/chromium/media/gpu/windows/d3d11_picture_buffer.h
index 77c14800319..d605772d147 100644
--- a/chromium/media/gpu/windows/d3d11_picture_buffer.h
+++ b/chromium/media/gpu/windows/d3d11_picture_buffer.h
@@ -11,8 +11,7 @@
#include <memory>
#include <vector>
-#include "base/memory/ref_counted.h"
-
+#include "base/memory/ref_counted_delete_on_sequence.h"
#include "gpu/command_buffer/service/mailbox_manager.h"
#include "gpu/command_buffer/service/texture_manager.h"
#include "gpu/ipc/service/command_buffer_stub.h"
@@ -44,17 +43,20 @@ class Texture2DWrapper;
// GpuResources have to be retained until the mailbox is used, but we just
// retain the whole thing.
class MEDIA_GPU_EXPORT D3D11PictureBuffer
- : public base::RefCountedThreadSafe<D3D11PictureBuffer> {
+ : public base::RefCountedDeleteOnSequence<D3D11PictureBuffer> {
public:
// |texture_wrapper| is responsible for controlling mailbox access to
// the ID3D11Texture2D,
// |level| is the picturebuffer index inside the Array-type ID3D11Texture2D.
- D3D11PictureBuffer(ComD3D11Texture2D texture,
- std::unique_ptr<Texture2DWrapper> texture_wrapper,
- gfx::Size size,
- size_t level);
-
- bool Init(GetCommandBufferHelperCB get_helper_cb,
+ D3D11PictureBuffer(
+ scoped_refptr<base::SequencedTaskRunner> delete_task_runner,
+ ComD3D11Texture2D texture,
+ std::unique_ptr<Texture2DWrapper> texture_wrapper,
+ gfx::Size size,
+ size_t level);
+
+ bool Init(scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,
+ GetCommandBufferHelperCB get_helper_cb,
ComD3D11VideoDevice video_device,
const GUID& decoder_guid,
std::unique_ptr<MediaLog> media_log);
@@ -84,12 +86,15 @@ class MEDIA_GPU_EXPORT D3D11PictureBuffer
return output_view_;
}
+ Texture2DWrapper* texture_wrapper() const { return texture_wrapper_.get(); }
+
// Shouldn't be here, but simpler for now.
base::TimeDelta timestamp_;
private:
~D3D11PictureBuffer();
- friend class base::RefCountedThreadSafe<D3D11PictureBuffer>;
+ friend class base::RefCountedDeleteOnSequence<D3D11PictureBuffer>;
+ friend class base::DeleteHelper<D3D11PictureBuffer>;
ComD3D11Texture2D texture_;
std::unique_ptr<Texture2DWrapper> texture_wrapper_;
diff --git a/chromium/media/gpu/windows/d3d11_texture_wrapper.cc b/chromium/media/gpu/windows/d3d11_texture_wrapper.cc
index e777c3cd341..ab7ea22a87f 100644
--- a/chromium/media/gpu/windows/d3d11_texture_wrapper.cc
+++ b/chromium/media/gpu/windows/d3d11_texture_wrapper.cc
@@ -10,6 +10,7 @@
#include <vector>
#include "gpu/command_buffer/service/mailbox_manager.h"
+#include "media/base/bind_to_current_loop.h"
#include "media/base/win/mf_helpers.h"
#include "ui/gl/gl_image.h"
@@ -67,15 +68,17 @@ bool DefaultTexture2DWrapper::ProcessTexture(
const gfx::ColorSpace& input_color_space,
MailboxHolderArray* mailbox_dest,
gfx::ColorSpace* output_color_space) {
- // TODO(liberato): When |gpu_resources_| is a SB<>, it's okay to post and
- // forget this call. It will still be ordered properly with respect to any
- // access on the gpu main thread.
- // TODO(liberato): Would be nice if SB<> knew how to post and reply, so that
- // we could get the error code back eventually, and fail later with it.
- auto result = gpu_resources_->PushNewTexture(std::move(texture), array_slice);
- if (!result.is_ok())
+ // If we've received an error, then return it to our caller. This is probably
+ // from some previous operation.
+ // TODO(liberato): Return the error.
+ if (received_error_)
return false;
+ // It's okay to post and forget this call, since it'll be ordered correctly
+ // with respect to any access on the gpu main thread.
+ gpu_resources_.Post(FROM_HERE, &GpuResources::PushNewTexture,
+ std::move(texture), array_slice);
+
// TODO(liberato): make sure that |mailbox_holders_| is zero-initialized in
// case we don't use all the planes.
for (size_t i = 0; i < VideoFrame::kMaxPlanes; i++)
@@ -87,15 +90,19 @@ bool DefaultTexture2DWrapper::ProcessTexture(
return true;
}
-bool DefaultTexture2DWrapper::Init(GetCommandBufferHelperCB get_helper_cb) {
- gpu_resources_ = std::make_unique<GpuResources>();
- if (!gpu_resources_)
- return false;
+bool DefaultTexture2DWrapper::Init(
+ scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,
+ GetCommandBufferHelperCB get_helper_cb) {
+ gpu_resources_ = base::SequenceBound<GpuResources>(
+ std::move(gpu_task_runner),
+ BindToCurrentLoop(base::BindOnce(&DefaultTexture2DWrapper::OnError,
+ weak_factory_.GetWeakPtr())));
// YUV textures are mapped onto two GL textures, while RGB use one.
int textures_per_picture = 0;
switch (dxgi_format_) {
case DXGI_FORMAT_NV12:
+ case DXGI_FORMAT_P010:
textures_per_picture = 2;
break;
case DXGI_FORMAT_B8G8R8A8_UNORM:
@@ -107,6 +114,8 @@ bool DefaultTexture2DWrapper::Init(GetCommandBufferHelperCB get_helper_cb) {
}
// Generate mailboxes and holders.
+ // TODO(liberato): Verify that this is really okay off the GPU main thread.
+ // The current implementation is.
std::vector<gpu::Mailbox> mailboxes;
for (int texture_idx = 0; texture_idx < textures_per_picture; texture_idx++) {
mailboxes.push_back(gpu::Mailbox::Generate());
@@ -119,15 +128,25 @@ bool DefaultTexture2DWrapper::Init(GetCommandBufferHelperCB get_helper_cb) {
// device for decoding. Sharing seems not to work very well. Otherwise, we
// would create the texture with KEYED_MUTEX and NTHANDLE, then send along
// a handle that we get from |texture| as an IDXGIResource1.
- // TODO(liberato): this should happen on the gpu thread.
- // TODO(liberato): the out param would be handled similarly to
- // CodecImageHolder when we add a pool.
- return gpu_resources_->Init(std::move(get_helper_cb), std::move(mailboxes),
- GL_TEXTURE_EXTERNAL_OES, size_,
- textures_per_picture);
+ gpu_resources_.Post(FROM_HERE, &GpuResources::Init, std::move(get_helper_cb),
+ std::move(mailboxes), GL_TEXTURE_EXTERNAL_OES, size_,
+ textures_per_picture);
+ return true;
+}
+
+void DefaultTexture2DWrapper::OnError(Status status) {
+ if (!received_error_)
+ received_error_ = status;
}
-DefaultTexture2DWrapper::GpuResources::GpuResources() {}
+void DefaultTexture2DWrapper::SetStreamHDRMetadata(
+ const HDRMetadata& stream_metadata) {}
+
+void DefaultTexture2DWrapper::SetDisplayHDRMetadata(
+ const DXGI_HDR_METADATA_HDR10& dxgi_display_metadata) {}
+
+DefaultTexture2DWrapper::GpuResources::GpuResources(OnErrorCB on_error_cb)
+ : on_error_cb_(std::move(on_error_cb)) {}
DefaultTexture2DWrapper::GpuResources::~GpuResources() {
if (helper_ && helper_->MakeContextCurrent()) {
@@ -136,7 +155,7 @@ DefaultTexture2DWrapper::GpuResources::~GpuResources() {
}
}
-bool DefaultTexture2DWrapper::GpuResources::Init(
+void DefaultTexture2DWrapper::GpuResources::Init(
GetCommandBufferHelperCB get_helper_cb,
const std::vector<gpu::Mailbox> mailboxes,
GLenum target,
@@ -144,8 +163,10 @@ bool DefaultTexture2DWrapper::GpuResources::Init(
int textures_per_picture) {
helper_ = get_helper_cb.Run();
- if (!helper_ || !helper_->MakeContextCurrent())
- return false;
+ if (!helper_ || !helper_->MakeContextCurrent()) {
+ NotifyError(StatusCode::kCantMakeContextCurrent);
+ return;
+ }
// Create the textures and attach them to the mailboxes.
// TODO(liberato): Should we use GL_FLOAT for an fp16 texture? It doesn't
@@ -168,7 +189,10 @@ bool DefaultTexture2DWrapper::GpuResources::Init(
// clang-format on
};
EGLStreamKHR stream = eglCreateStreamKHR(egl_display, stream_attributes);
- RETURN_ON_FAILURE(!!stream, "Could not create stream", false);
+ if (!stream) {
+ NotifyError(StatusCode::kCantCreateEglStream);
+ return;
+ }
// |stream| will be destroyed when the GLImage is.
// TODO(liberato): for tests, it will be destroyed pretty much at the end of
@@ -204,7 +228,10 @@ bool DefaultTexture2DWrapper::GpuResources::Init(
}
EGLBoolean result = eglStreamConsumerGLTextureExternalAttribsNV(
egl_display, stream, consumer_attributes.data());
- RETURN_ON_FAILURE(result, "Could not set stream consumer", false);
+ if (!result) {
+ NotifyError(StatusCode::kCantCreateEglStreamConsumer);
+ return;
+ }
EGLAttrib producer_attributes[] = {
EGL_NONE,
@@ -212,7 +239,10 @@ bool DefaultTexture2DWrapper::GpuResources::Init(
result = eglCreateStreamProducerD3DTextureANGLE(egl_display, stream,
producer_attributes);
- RETURN_ON_FAILURE(result, "Could not create stream", false);
+ if (!result) {
+ NotifyError(StatusCode::kCantCreateEglStreamProducer);
+ return;
+ }
// Note that this is valid as long as |gl_image_| is valid; it is
// what deletes the stream.
@@ -224,15 +254,15 @@ bool DefaultTexture2DWrapper::GpuResources::Init(
helper_->BindImage(service_ids_[texture_idx], gl_image_.get(),
false /* client_managed */);
}
-
- return true;
}
-Status DefaultTexture2DWrapper::GpuResources::PushNewTexture(
+void DefaultTexture2DWrapper::GpuResources::PushNewTexture(
ComD3D11Texture2D texture,
size_t array_slice) {
- if (!helper_ || !helper_->MakeContextCurrent())
- return Status(StatusCode::kCantMakeContextCurrent);
+ if (!helper_ || !helper_->MakeContextCurrent()) {
+ NotifyError(StatusCode::kCantMakeContextCurrent);
+ return;
+ }
// Notify |gl_image_| that it has a new texture.
gl_image_->SetTexture(texture, array_slice);
@@ -248,13 +278,20 @@ Status DefaultTexture2DWrapper::GpuResources::PushNewTexture(
if (!eglStreamPostD3DTextureANGLE(egl_display, stream_,
static_cast<void*>(texture.Get()),
frame_attributes)) {
- return Status(StatusCode::kCantPostTexture);
+ NotifyError(StatusCode::kCantPostTexture);
+ return;
}
- if (!eglStreamConsumerAcquireKHR(egl_display, stream_))
- return Status(StatusCode::kCantPostAcquireStream);
+ if (!eglStreamConsumerAcquireKHR(egl_display, stream_)) {
+ NotifyError(StatusCode::kCantPostAcquireStream);
+ return;
+ }
+}
- return OkStatus();
+void DefaultTexture2DWrapper::GpuResources::NotifyError(Status status) {
+ if (on_error_cb_)
+ std::move(on_error_cb_).Run(std::move(status));
+ // else this isn't the first error, so skip it.
}
} // namespace media
diff --git a/chromium/media/gpu/windows/d3d11_texture_wrapper.h b/chromium/media/gpu/windows/d3d11_texture_wrapper.h
index f7fe065285f..5d71e209809 100644
--- a/chromium/media/gpu/windows/d3d11_texture_wrapper.h
+++ b/chromium/media/gpu/windows/d3d11_texture_wrapper.h
@@ -10,8 +10,12 @@
#include <memory>
#include <vector>
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "base/threading/sequence_bound.h"
#include "gpu/command_buffer/service/mailbox_manager.h"
#include "gpu/command_buffer/service/texture_manager.h"
+#include "media/base/hdr_metadata.h"
#include "media/base/status.h"
#include "media/base/video_frame.h"
#include "media/gpu/command_buffer_helper.h"
@@ -41,7 +45,8 @@ class MEDIA_GPU_EXPORT Texture2DWrapper {
virtual ~Texture2DWrapper();
// Initialize the wrapper.
- virtual bool Init(GetCommandBufferHelperCB get_helper_cb) = 0;
+ virtual bool Init(scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,
+ GetCommandBufferHelperCB get_helper_cb) = 0;
// Import |texture|, |array_slice| and return the mailbox(es) that can be
// used to refer to it.
@@ -50,6 +55,10 @@ class MEDIA_GPU_EXPORT Texture2DWrapper {
const gfx::ColorSpace& input_color_space,
MailboxHolderArray* mailbox_dest_out,
gfx::ColorSpace* output_color_space) = 0;
+
+ virtual void SetStreamHDRMetadata(const HDRMetadata& stream_metadata) = 0;
+ virtual void SetDisplayHDRMetadata(
+ const DXGI_HDR_METADATA_HDR10& dxgi_display_metadata) = 0;
};
// The default texture wrapper that uses GPUResources to talk to hardware
@@ -58,12 +67,16 @@ class MEDIA_GPU_EXPORT Texture2DWrapper {
// instance for each concurrently outstanding texture.
class MEDIA_GPU_EXPORT DefaultTexture2DWrapper : public Texture2DWrapper {
public:
+ // Error callback for GpuResource to notify us of errors.
+ using OnErrorCB = base::OnceCallback<void(Status)>;
+
// While the specific texture instance can change on every call to
// ProcessTexture, the dxgi format must be the same for all of them.
DefaultTexture2DWrapper(const gfx::Size& size, DXGI_FORMAT dxgi_format);
~DefaultTexture2DWrapper() override;
- bool Init(GetCommandBufferHelperCB get_helper_cb) override;
+ bool Init(scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,
+ GetCommandBufferHelperCB get_helper_cb) override;
bool ProcessTexture(ComD3D11Texture2D texture,
size_t array_slice,
@@ -71,6 +84,10 @@ class MEDIA_GPU_EXPORT DefaultTexture2DWrapper : public Texture2DWrapper {
MailboxHolderArray* mailbox_dest,
gfx::ColorSpace* output_color_space) override;
+ void SetStreamHDRMetadata(const HDRMetadata& stream_metadata) override;
+ void SetDisplayHDRMetadata(
+ const DXGI_HDR_METADATA_HDR10& dxgi_display_metadata) override;
+
private:
// Things that are to be accessed / freed only on the main thread. In
// addition to setting up the textures to render from a D3D11 texture,
@@ -78,21 +95,27 @@ class MEDIA_GPU_EXPORT DefaultTexture2DWrapper : public Texture2DWrapper {
// can use the mailbox.
class GpuResources {
public:
- GpuResources();
+ GpuResources(OnErrorCB on_error_cb);
~GpuResources();
- bool Init(GetCommandBufferHelperCB get_helper_cb,
+ void Init(GetCommandBufferHelperCB get_helper_cb,
const std::vector<gpu::Mailbox> mailboxes,
GLenum target,
gfx::Size size,
int textures_per_picture);
// Push a new |texture|, |array_slice| to |gl_image_|.
- Status PushNewTexture(ComD3D11Texture2D texture, size_t array_slice);
+ void PushNewTexture(ComD3D11Texture2D texture, size_t array_slice);
std::vector<uint32_t> service_ids_;
private:
+ // Notify our wrapper about |status|, if we haven't before.
+ void NotifyError(Status status);
+
+ // May be empty if we've already sent an error.
+ OnErrorCB on_error_cb_;
+
scoped_refptr<CommandBufferHelper> helper_;
scoped_refptr<gl::GLImageDXGI> gl_image_;
EGLStreamKHR stream_;
@@ -100,10 +123,18 @@ class MEDIA_GPU_EXPORT DefaultTexture2DWrapper : public Texture2DWrapper {
DISALLOW_COPY_AND_ASSIGN(GpuResources);
};
+ // Receive an error from |gpu_resources_| and store it in |received_error_|.
+ void OnError(Status status);
+
+ // The first error status that we've received from |gpu_resources_|, if any.
+ base::Optional<Status> received_error_;
+
gfx::Size size_;
- std::unique_ptr<GpuResources> gpu_resources_;
+ base::SequenceBound<GpuResources> gpu_resources_;
MailboxHolderArray mailbox_holders_;
DXGI_FORMAT dxgi_format_;
+
+ base::WeakPtrFactory<DefaultTexture2DWrapper> weak_factory_{this};
};
} // namespace media
diff --git a/chromium/media/gpu/windows/d3d11_texture_wrapper_unittest.cc b/chromium/media/gpu/windows/d3d11_texture_wrapper_unittest.cc
new file mode 100644
index 00000000000..4b355a17dcd
--- /dev/null
+++ b/chromium/media/gpu/windows/d3d11_texture_wrapper_unittest.cc
@@ -0,0 +1,126 @@
+// 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/gpu/windows/d3d11_texture_wrapper.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/single_thread_task_runner.h"
+#include "base/test/task_environment.h"
+#include "base/win/windows_version.h"
+#include "media/gpu/test/fake_command_buffer_helper.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gl/gl_context_egl.h"
+#include "ui/gl/gl_egl_api_implementation.h"
+#include "ui/gl/gl_image.h"
+#include "ui/gl/gl_share_group.h"
+#include "ui/gl/gl_surface.h"
+#include "ui/gl/init/gl_factory.h"
+#include "ui/gl/test/gl_surface_test_support.h"
+
+using ::testing::_;
+using ::testing::Bool;
+using ::testing::Combine;
+using ::testing::Return;
+using ::testing::Values;
+
+namespace media {
+
+#define STOP_IF_WIN7() \
+ do { \
+ if (base::win::GetVersion() <= base::win::Version::WIN7) \
+ return; \
+ } while (0);
+
+class D3D11TextureWrapperUnittest : public ::testing::Test {
+ public:
+ void SetUp() override {
+ // Surface creation fails sometimes on win7, mostly. Just skip the test.
+ STOP_IF_WIN7();
+
+ task_runner_ = task_environment_.GetMainThreadTaskRunner();
+
+ gl::GLSurfaceTestSupport::InitializeOneOffImplementation(
+ gl::kGLImplementationEGLANGLE, false);
+ surface_ = gl::init::CreateOffscreenGLSurface(gfx::Size());
+ share_group_ = new gl::GLShareGroup();
+ context_ = gl::init::CreateGLContext(share_group_.get(), surface_.get(),
+ gl::GLContextAttribs());
+ context_->MakeCurrent(surface_.get());
+
+ // Create some objects that most tests want.
+ fake_command_buffer_helper_ =
+ base::MakeRefCounted<FakeCommandBufferHelper>(task_runner_);
+ get_helper_cb_ = base::BindRepeating(
+ [](scoped_refptr<CommandBufferHelper> helper) { return helper; },
+ fake_command_buffer_helper_);
+ }
+
+ void TearDown() override {
+ STOP_IF_WIN7();
+ context_->ReleaseCurrent(surface_.get());
+ context_ = nullptr;
+ share_group_ = nullptr;
+ surface_ = nullptr;
+ gl::init::ShutdownGL(false);
+ }
+
+ base::test::TaskEnvironment task_environment_;
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+
+ scoped_refptr<gl::GLSurface> surface_;
+ scoped_refptr<gl::GLShareGroup> share_group_;
+ scoped_refptr<gl::GLContext> context_;
+
+ // Made-up size for the images.
+ const gfx::Size size_{100, 200};
+
+ // CommandBufferHelper, and a callback that returns it. Useful to initialize
+ // a wrapper.
+ scoped_refptr<FakeCommandBufferHelper> fake_command_buffer_helper_;
+ GetCommandBufferHelperCB get_helper_cb_;
+};
+
+TEST_F(D3D11TextureWrapperUnittest, NV12InitSucceeds) {
+ STOP_IF_WIN7();
+ const DXGI_FORMAT dxgi_format = DXGI_FORMAT_NV12;
+
+ auto wrapper = std::make_unique<DefaultTexture2DWrapper>(size_, dxgi_format);
+ const bool init_result = wrapper->Init(task_runner_, get_helper_cb_);
+ EXPECT_TRUE(init_result);
+
+ // TODO: verify that ProcessTexture processes both textures.
+}
+
+TEST_F(D3D11TextureWrapperUnittest, BGRA8InitSucceeds) {
+ STOP_IF_WIN7();
+ const DXGI_FORMAT dxgi_format = DXGI_FORMAT_B8G8R8A8_UNORM;
+
+ auto wrapper = std::make_unique<DefaultTexture2DWrapper>(size_, dxgi_format);
+ const bool init_result = wrapper->Init(task_runner_, get_helper_cb_);
+ EXPECT_TRUE(init_result);
+}
+
+TEST_F(D3D11TextureWrapperUnittest, FP16InitSucceeds) {
+ STOP_IF_WIN7();
+ const DXGI_FORMAT dxgi_format = DXGI_FORMAT_R16G16B16A16_FLOAT;
+
+ auto wrapper = std::make_unique<DefaultTexture2DWrapper>(size_, dxgi_format);
+ const bool init_result = wrapper->Init(task_runner_, get_helper_cb_);
+ EXPECT_TRUE(init_result);
+}
+
+TEST_F(D3D11TextureWrapperUnittest, P010InitSucceeds) {
+ STOP_IF_WIN7();
+ const DXGI_FORMAT dxgi_format = DXGI_FORMAT_P010;
+
+ auto wrapper = std::make_unique<DefaultTexture2DWrapper>(size_, dxgi_format);
+ const bool init_result = wrapper->Init(task_runner_, get_helper_cb_);
+ EXPECT_TRUE(init_result);
+}
+
+} // namespace media \ No newline at end of file
diff --git a/chromium/media/gpu/windows/d3d11_video_decoder.cc b/chromium/media/gpu/windows/d3d11_video_decoder.cc
index eae07b3c08e..3ba6d9b3225 100644
--- a/chromium/media/gpu/windows/d3d11_video_decoder.cc
+++ b/chromium/media/gpu/windows/d3d11_video_decoder.cc
@@ -16,9 +16,9 @@
#include "base/memory/ref_counted_delete_on_sequence.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
+#include "base/threading/sequenced_task_runner_handle.h"
#include "base/trace_event/trace_event.h"
#include "media/base/bind_to_current_loop.h"
-#include "media/base/cdm_context.h"
#include "media/base/decoder_buffer.h"
#include "media/base/media_log.h"
#include "media/base/media_switches.h"
@@ -30,9 +30,9 @@
#include "media/gpu/windows/d3d11_video_context_wrapper.h"
#include "media/gpu/windows/d3d11_video_decoder_impl.h"
#include "media/gpu/windows/d3d11_video_device_format_support.h"
+#include "media/gpu/windows/display_helper.h"
#include "media/gpu/windows/supported_profile_helpers.h"
#include "media/media_buildflags.h"
-#include "ui/gl/direct_composition_surface_win.h"
#include "ui/gl/gl_angle_util_win.h"
#include "ui/gl/gl_switches.h"
@@ -82,7 +82,8 @@ std::unique_ptr<VideoDecoder> D3D11VideoDecoder::Create(
const gpu::GpuDriverBugWorkarounds& gpu_workarounds,
base::RepeatingCallback<gpu::CommandBufferStub*()> get_stub_cb,
D3D11VideoDecoder::GetD3D11DeviceCB get_d3d11_device_cb,
- SupportedConfigs supported_configs) {
+ SupportedConfigs supported_configs,
+ bool is_hdr_supported) {
// We create |impl_| on the wrong thread, but we never use it here.
// Note that the output callback will hop to our thread, post the video
// frame, and along with a callback that will hop back to the impl thread
@@ -94,13 +95,12 @@ std::unique_ptr<VideoDecoder> D3D11VideoDecoder::Create(
base::BindRepeating(CreateCommandBufferHelper, std::move(get_stub_cb),
scoped_refptr<CommandBufferHelperHolder>(
new CommandBufferHelperHolder(gpu_task_runner)));
- return base::WrapUnique<VideoDecoder>(
- new D3D11VideoDecoder(std::move(gpu_task_runner), std::move(media_log),
- gpu_preferences, gpu_workarounds,
- std::make_unique<D3D11VideoDecoderImpl>(
- std::move(cloned_media_log), get_helper_cb),
- get_helper_cb, std::move(get_d3d11_device_cb),
- std::move(supported_configs)));
+ return base::WrapUnique<VideoDecoder>(new D3D11VideoDecoder(
+ gpu_task_runner, std::move(media_log), gpu_preferences, gpu_workarounds,
+ base::SequenceBound<D3D11VideoDecoderImpl>(
+ gpu_task_runner, std::move(cloned_media_log), get_helper_cb),
+ get_helper_cb, std::move(get_d3d11_device_cb),
+ std::move(supported_configs), is_hdr_supported));
}
D3D11VideoDecoder::D3D11VideoDecoder(
@@ -108,23 +108,24 @@ D3D11VideoDecoder::D3D11VideoDecoder(
std::unique_ptr<MediaLog> media_log,
const gpu::GpuPreferences& gpu_preferences,
const gpu::GpuDriverBugWorkarounds& gpu_workarounds,
- std::unique_ptr<D3D11VideoDecoderImpl> impl,
+ base::SequenceBound<D3D11VideoDecoderImpl> impl,
base::RepeatingCallback<scoped_refptr<CommandBufferHelper>()> get_helper_cb,
GetD3D11DeviceCB get_d3d11_device_cb,
- SupportedConfigs supported_configs)
+ SupportedConfigs supported_configs,
+ bool is_hdr_supported)
: media_log_(std::move(media_log)),
impl_(std::move(impl)),
- impl_task_runner_(std::move(gpu_task_runner)),
+ gpu_task_runner_(std::move(gpu_task_runner)),
+ decoder_task_runner_(base::SequencedTaskRunnerHandle::Get()),
already_initialized_(false),
gpu_preferences_(gpu_preferences),
gpu_workarounds_(gpu_workarounds),
get_d3d11_device_cb_(std::move(get_d3d11_device_cb)),
get_helper_cb_(std::move(get_helper_cb)),
- supported_configs_(std::move(supported_configs)) {
+ supported_configs_(std::move(supported_configs)),
+ is_hdr_supported_(is_hdr_supported) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(media_log_);
-
- impl_weak_ = impl_->GetWeakPtr();
}
D3D11VideoDecoder::~D3D11VideoDecoder() {
@@ -133,10 +134,7 @@ D3D11VideoDecoder::~D3D11VideoDecoder() {
// from |impl_| will be cancelled by |weak_factory_| when we return.
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (impl_task_runner_->RunsTasksInCurrentSequence())
- impl_.reset();
- else
- impl_task_runner_->DeleteSoon(FROM_HERE, std::move(impl_));
+ impl_.Reset();
// Explicitly destroy the decoder, since it can reference picture buffers.
accelerated_video_decoder_.reset();
@@ -151,7 +149,6 @@ std::string D3D11VideoDecoder::GetDisplayName() const {
HRESULT D3D11VideoDecoder::InitializeAcceleratedDecoder(
const VideoDecoderConfig& config,
- CdmProxyContext* proxy_context,
ComD3D11VideoDecoder video_decoder) {
TRACE_EVENT0("gpu", "D3D11VideoDecoder::InitializeAcceleratedDecoder");
// If we got an 11.1 D3D11 Device, we can use a |ID3D11VideoContext1|,
@@ -169,18 +166,18 @@ HRESULT D3D11VideoDecoder::InitializeAcceleratedDecoder(
profile_ = config.profile();
if (config.codec() == kCodecVP9) {
accelerated_video_decoder_ = std::make_unique<VP9Decoder>(
- std::make_unique<D3D11VP9Accelerator>(
- this, media_log_.get(), proxy_context, video_decoder, video_device_,
- std::move(video_context)),
+ std::make_unique<D3D11VP9Accelerator>(this, media_log_.get(),
+ video_decoder, video_device_,
+ std::move(video_context)),
profile_, config.color_space_info());
return hr;
}
if (config.codec() == kCodecH264) {
accelerated_video_decoder_ = std::make_unique<H264Decoder>(
- std::make_unique<D3D11H264Accelerator>(
- this, media_log_.get(), proxy_context, video_decoder, video_device_,
- std::move(video_context)),
+ std::make_unique<D3D11H264Accelerator>(this, media_log_.get(),
+ video_decoder, video_device_,
+ std::move(video_context)),
profile_, config.color_space_info());
return hr;
}
@@ -190,10 +187,10 @@ HRESULT D3D11VideoDecoder::InitializeAcceleratedDecoder(
void D3D11VideoDecoder::Initialize(const VideoDecoderConfig& config,
bool low_delay,
- CdmContext* cdm_context,
+ CdmContext* /* cdm_context */,
InitCB init_cb,
const OutputCB& output_cb,
- const WaitingCB& waiting_cb) {
+ const WaitingCB& /* waiting_cb */) {
TRACE_EVENT0("gpu", "D3D11VideoDecoder::Initialize");
if (already_initialized_)
AddLifetimeProgressionStage(D3D11LifetimeProgression::kPlaybackSucceeded);
@@ -201,14 +198,12 @@ void D3D11VideoDecoder::Initialize(const VideoDecoderConfig& config,
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(output_cb);
- DCHECK(waiting_cb);
state_ = State::kInitializing;
config_ = config;
init_cb_ = std::move(init_cb);
output_cb_ = output_cb;
- waiting_cb_ = waiting_cb;
// Verify that |config| matches one of the supported configurations. This
// helps us skip configs that are supported by the VDA but not us, since
@@ -227,6 +222,11 @@ void D3D11VideoDecoder::Initialize(const VideoDecoderConfig& config,
return;
}
+ if (config.is_encrypted()) {
+ NotifyError("D3D11VideoDecoder does not support encrypted stream");
+ return;
+ }
+
// Initialize the video decoder.
// Note that we assume that this is the ANGLE device, since we don't implement
@@ -240,6 +240,9 @@ void D3D11VideoDecoder::Initialize(const VideoDecoderConfig& config,
// it's been specifically configured via switch to avoid d3d11.
//
// TODO(liberato): On re-init, we can probably re-use the device.
+ // TODO(liberato): This isn't allowed off the main thread, since the callback
+ // does who-knows-what. Either we should be given the angle device, or we
+ // should thread-hop to get it.
device_ = get_d3d11_device_cb_.Run();
if (!device_) {
// This happens if, for example, if chrome is configured to use
@@ -288,13 +291,12 @@ void D3D11VideoDecoder::Initialize(const VideoDecoderConfig& config,
// Use IsHDRSupported to guess whether the compositor can output HDR textures.
// See TextureSelector for notes about why the decoder should not care.
- texture_selector_ =
- TextureSelector::Create(gpu_preferences_, gpu_workarounds_,
- decoder_configurator_->TextureFormat(),
- gl::DirectCompositionSurfaceWin::IsHDRSupported()
- ? TextureSelector::HDRMode::kSDROrHDR
- : TextureSelector::HDRMode::kSDROnly,
- &format_checker, media_log_.get());
+ texture_selector_ = TextureSelector::Create(
+ gpu_preferences_, gpu_workarounds_,
+ decoder_configurator_->TextureFormat(),
+ is_hdr_supported_ ? TextureSelector::HDRMode::kSDROrHDR
+ : TextureSelector::HDRMode::kSDROnly,
+ &format_checker, media_log_.get());
if (!texture_selector_) {
NotifyError("D3DD11: Cannot get TextureSelector for format");
return;
@@ -333,12 +335,6 @@ void D3D11VideoDecoder::Initialize(const VideoDecoderConfig& config,
return;
}
- if (config.is_encrypted() && dec_config.guidConfigBitstreamEncryption !=
- D3D11_DECODER_ENCRYPTION_HW_CENC) {
- // For encrypted media, it has to use HW CENC decoder config.
- continue;
- }
-
if (config.codec() == kCodecVP9 && dec_config.ConfigBitstreamRaw == 1) {
// DXVA VP9 specification mentions ConfigBitstreamRaw "shall be 1".
found = true;
@@ -364,19 +360,7 @@ void D3D11VideoDecoder::Initialize(const VideoDecoderConfig& config,
return;
}
- CdmProxyContext* proxy_context = nullptr;
-#if BUILDFLAG(ENABLE_CDM_PROXY)
- if (cdm_context)
- proxy_context = cdm_context->GetCdmProxyContext();
-#endif
-
- // Ensure that if we are encrypted, that we have a CDM.
- if (config_.is_encrypted() && !proxy_context) {
- NotifyError("Video stream is encrypted, but no cdm was found");
- return;
- }
-
- hr = InitializeAcceleratedDecoder(config, proxy_context, video_decoder);
+ hr = InitializeAcceleratedDecoder(config, video_decoder);
if (!SUCCEEDED(hr)) {
NotifyError("Failed to get device context");
@@ -395,14 +379,6 @@ void D3D11VideoDecoder::Initialize(const VideoDecoderConfig& config,
config.GetHumanReadableCodecName());
}
- // |cdm_context| could be null for clear playback.
- // TODO(liberato): On re-init, should this still happen?
- if (cdm_context) {
- new_key_callback_registration_ =
- cdm_context->RegisterEventCB(base::BindRepeating(
- &D3D11VideoDecoder::OnCdmContextEvent, weak_factory_.GetWeakPtr()));
- }
-
auto impl_init_cb = base::BindOnce(&D3D11VideoDecoder::OnGpuInitComplete,
weak_factory_.GetWeakPtr());
@@ -412,25 +388,14 @@ void D3D11VideoDecoder::Initialize(const VideoDecoderConfig& config,
AddLifetimeProgressionStage(D3D11LifetimeProgression::kInitializeSucceeded);
- // Initialize the gpu side. We wait until everything else is initialized,
- // since we allow it to call us back re-entrantly to reduce latency. Note
- // that if we're not on the same thread, then we should probably post the
- // call earlier, since re-entrancy won't be an issue.
- if (impl_task_runner_->RunsTasksInCurrentSequence()) {
- impl_->Initialize(std::move(impl_init_cb),
- std::move(get_picture_buffer_cb));
- return;
- }
-
+ // Initialize the gpu side. It would be nice if we could ask SB<> to elide
+ // the post if we're already on that thread, but it can't.
// Bind our own init / output cb that hop to this thread, so we don't call
// the originals on some other thread.
// Important but subtle note: base::Bind will copy |config_| since it's a
// const ref.
- impl_task_runner_->PostTask(
- FROM_HERE,
- base::BindOnce(&D3D11VideoDecoderImpl::Initialize, impl_weak_,
- BindToCurrentLoop(std::move(impl_init_cb)),
- BindToCurrentLoop(std::move(get_picture_buffer_cb))));
+ impl_.Post(FROM_HERE, &D3D11VideoDecoderImpl::Initialize,
+ BindToCurrentLoop(std::move(impl_init_cb)));
}
void D3D11VideoDecoder::AddLifetimeProgressionStage(
@@ -455,7 +420,9 @@ void D3D11VideoDecoder::ReceivePictureBufferFromClient(
DoDecode();
}
-void D3D11VideoDecoder::OnGpuInitComplete(bool success) {
+void D3D11VideoDecoder::OnGpuInitComplete(
+ bool success,
+ D3D11VideoDecoderImpl::ReleaseMailboxCB release_mailbox_cb) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
TRACE_EVENT0("gpu", "D3D11VideoDecoder::OnGpuInitComplete");
@@ -472,6 +439,8 @@ void D3D11VideoDecoder::OnGpuInitComplete(bool success) {
return;
}
+ release_mailbox_cb_ = std::move(release_mailbox_cb);
+
state_ = State::kRunning;
std::move(init_cb_).Run(OkStatus());
}
@@ -595,9 +564,8 @@ void D3D11VideoDecoder::DoDecode() {
}
CreatePictureBuffers();
} else if (result == media::AcceleratedVideoDecoder::kTryAgain) {
- state_ = State::kWaitingForNewKey;
- waiting_cb_.Run(WaitingReason::kNoDecryptionKey);
- // Another DoDecode() task would be posted in OnCdmContextEvent().
+ LOG(ERROR) << "Try again is not supported";
+ NotifyError("Try again is not supported");
return;
} else {
LOG(ERROR) << "VDA Error " << result;
@@ -627,21 +595,6 @@ void D3D11VideoDecoder::Reset(base::OnceClosure closure) {
// TODO(liberato): how do we signal an error?
accelerated_video_decoder_->Reset();
- if (state_ == State::kWaitingForReset && config_.is_encrypted()) {
- // On a hardware context loss event, a new swap chain has to be created (in
- // the compositor). By clearing the picture buffers, next DoDecode() call
- // will create a new texture. This makes the compositor to create a new swap
- // chain.
- // More detailed explanation at crbug.com/858286
- picture_buffers_.clear();
- }
-
- // Transition out of kWaitingForNewKey since the new buffer could be clear or
- // have a different key ID. Transition out of kWaitingForReset since reset
- // just happened.
- if (state_ == State::kWaitingForNewKey || state_ == State::kWaitingForReset)
- state_ = State::kRunning;
-
std::move(closure).Run();
}
@@ -682,6 +635,19 @@ void D3D11VideoDecoder::CreatePictureBuffers() {
return;
}
+ HDRMetadata stream_metadata;
+ if (config_.hdr_metadata())
+ stream_metadata = *config_.hdr_metadata();
+ // else leave |stream_metadata| default-initialized. We might use it anyway.
+
+ base::Optional<DXGI_HDR_METADATA_HDR10> display_metadata;
+ if (decoder_configurator_->TextureFormat() == DXGI_FORMAT_P010) {
+ // For HDR formats, try to get the display metadata. This may fail, which
+ // is okay. We'll just skip sending the metadata.
+ DisplayHelper display_helper(device_);
+ display_metadata = display_helper.GetDisplayMetadata();
+ }
+
// Drop any old pictures.
for (auto& buffer : picture_buffers_)
DCHECK(!buffer->in_picture_use());
@@ -696,14 +662,28 @@ void D3D11VideoDecoder::CreatePictureBuffers() {
return;
}
- picture_buffers_.push_back(
- new D3D11PictureBuffer(in_texture, std::move(tex_wrapper), size, i));
- if (!picture_buffers_[i]->Init(get_helper_cb_, video_device_,
- decoder_configurator_->DecoderGuid(),
- media_log_->Clone())) {
+ picture_buffers_.push_back(new D3D11PictureBuffer(
+ decoder_task_runner_, in_texture, std::move(tex_wrapper), size, i));
+ if (!picture_buffers_[i]->Init(
+ gpu_task_runner_, get_helper_cb_, video_device_,
+ decoder_configurator_->DecoderGuid(), media_log_->Clone())) {
NotifyError("Unable to allocate PictureBuffer");
return;
}
+
+ // If we have display metadata, then tell the processor. Note that the
+ // order of these calls is important, and we must set the display metadata
+ // if we set the stream metadata, else it can crash on some AMD cards.
+ if (display_metadata) {
+ if (config_.hdr_metadata() ||
+ gpu_workarounds_.use_empty_video_hdr_metadata) {
+ // It's okay if this has an empty-initialized metadata.
+ picture_buffers_[i]->texture_wrapper()->SetStreamHDRMetadata(
+ stream_metadata);
+ }
+ picture_buffers_[i]->texture_wrapper()->SetDisplayHDRMetadata(
+ *display_metadata);
+ }
}
}
@@ -760,10 +740,16 @@ bool D3D11VideoDecoder::OutputResult(const CodecPicture* picture,
return false;
}
- // TODO(liberato): bind this to the gpu main thread.
- frame->SetReleaseMailboxCB(media::BindToCurrentLoop(
- base::BindOnce(&D3D11VideoDecoderImpl::OnMailboxReleased, impl_weak_,
- scoped_refptr<D3D11PictureBuffer>(picture_buffer))));
+ // Remember that this will likely thread-hop to the GPU main thread. Note
+ // that |picture_buffer| will delete on sequence, so it's okay even if
+ // |wait_complete_cb| doesn't ever run.
+ auto wait_complete_cb = BindToCurrentLoop(
+ base::BindOnce(&D3D11VideoDecoder::ReceivePictureBufferFromClient,
+ weak_factory_.GetWeakPtr(),
+ scoped_refptr<D3D11PictureBuffer>(picture_buffer)));
+ frame->SetReleaseMailboxCB(
+ base::BindOnce(release_mailbox_cb_, std::move(wait_complete_cb)));
+
frame->metadata()->SetBoolean(VideoFrameMetadata::POWER_EFFICIENT, true);
// For NV12, overlay is allowed by default. If the decoder is going to support
// non-NV12 textures, then this may have to be conditionally set. Also note
@@ -782,47 +768,11 @@ bool D3D11VideoDecoder::OutputResult(const CodecPicture* picture,
frame->metadata()->SetBoolean(VideoFrameMetadata::ALLOW_OVERLAY,
allow_overlay);
- if (config_.is_encrypted()) {
- frame->metadata()->SetBoolean(VideoFrameMetadata::PROTECTED_VIDEO, true);
- frame->metadata()->SetBoolean(VideoFrameMetadata::HW_PROTECTED, true);
- }
-
frame->set_color_space(output_color_space);
output_cb_.Run(frame);
return true;
}
-void D3D11VideoDecoder::OnCdmContextEvent(CdmContext::Event event) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DVLOG(1) << __func__ << ": event = " << static_cast<int>(event);
-
- if (state_ == State::kInitializing || state_ == State::kError) {
- DVLOG(1) << "Do nothing in " << static_cast<int>(state_) << " state.";
- return;
- }
-
- switch (event) {
- case CdmContext::Event::kHasAdditionalUsableKey:
- // Note that this event may happen before DoDecode() because the key
- // acquisition stack runs independently of the media decoding stack.
- // So if this isn't in kWaitingForNewKey state no "resuming" is
- // required therefore no special action taken here.
- if (state_ != State::kWaitingForNewKey)
- return;
-
- state_ = State::kRunning;
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(&D3D11VideoDecoder::DoDecode,
- weak_factory_.GetWeakPtr()));
- return;
-
- case CdmContext::Event::kHardwareContextLost:
- state_ = State::kWaitingForReset;
- waiting_cb_.Run(WaitingReason::kDecoderStateLost);
- return;
- }
-}
-
// TODO(tmathmeyer) eventually have this take a Status and pass it through
// to each of the callbacks.
void D3D11VideoDecoder::NotifyError(const char* reason) {
@@ -926,10 +876,6 @@ D3D11VideoDecoder::GetSupportedVideoDecoderConfigs(
return {};
}
- const bool allow_encrypted =
- (usable_feature_level > D3D_FEATURE_LEVEL_11_0) &&
- base::FeatureList::IsEnabled(kHardwareSecureDecryption);
-
std::vector<SupportedVideoDecoderConfig> configs;
// VP9 has no default resolutions since it may not even be supported.
ResolutionPair max_h264_resolutions(gfx::Size(1920, 1088), gfx::Size());
@@ -952,7 +898,7 @@ D3D11VideoDecoder::GetSupportedVideoDecoderConfigs(
1), // profile_max
min_resolution, // coded_size_min
max_h264_resolutions.first, // coded_size_max
- allow_encrypted, // allow_encrypted
+ false, // allow_encrypted
false)); // require_encrypted
configs.push_back(SupportedVideoDecoderConfig(
static_cast<VideoCodecProfile>(H264PROFILE_HIGH10PROFILE +
@@ -960,7 +906,7 @@ D3D11VideoDecoder::GetSupportedVideoDecoderConfigs(
H264PROFILE_MAX, // profile_max
min_resolution, // coded_size_min
max_h264_resolutions.first, // coded_size_max
- allow_encrypted, // allow_encrypted
+ false, // allow_encrypted
false)); // require_encrypted
// portrait
@@ -970,7 +916,7 @@ D3D11VideoDecoder::GetSupportedVideoDecoderConfigs(
1), // profile_max
min_resolution, // coded_size_min
max_h264_resolutions.second, // coded_size_max
- allow_encrypted, // allow_encrypted
+ false, // allow_encrypted
false)); // require_encrypted
configs.push_back(SupportedVideoDecoderConfig(
static_cast<VideoCodecProfile>(H264PROFILE_HIGH10PROFILE +
@@ -978,7 +924,7 @@ D3D11VideoDecoder::GetSupportedVideoDecoderConfigs(
H264PROFILE_MAX, // profile_max
min_resolution, // coded_size_min
max_h264_resolutions.second, // coded_size_max
- allow_encrypted, // allow_encrypted
+ false, // allow_encrypted
false)); // require_encrypted
}
@@ -991,7 +937,7 @@ D3D11VideoDecoder::GetSupportedVideoDecoderConfigs(
VP9PROFILE_PROFILE0, // profile_max
min_resolution, // coded_size_min
max_vp9_profile0_resolutions.first, // coded_size_max
- allow_encrypted, // allow_encrypted
+ false, // allow_encrypted
false)); // require_encrypted
// portrait
configs.push_back(SupportedVideoDecoderConfig(
@@ -999,7 +945,7 @@ D3D11VideoDecoder::GetSupportedVideoDecoderConfigs(
VP9PROFILE_PROFILE0, // profile_max
min_resolution, // coded_size_min
max_vp9_profile0_resolutions.second, // coded_size_max
- allow_encrypted, // allow_encrypted
+ false, // allow_encrypted
false)); // require_encrypted
}
@@ -1011,7 +957,7 @@ D3D11VideoDecoder::GetSupportedVideoDecoderConfigs(
VP9PROFILE_PROFILE2, // profile_max
min_resolution, // coded_size_min
max_vp9_profile2_resolutions.first, // coded_size_max
- allow_encrypted, // allow_encrypted
+ false, // allow_encrypted
false)); // require_encrypted
// portrait
configs.push_back(SupportedVideoDecoderConfig(
@@ -1019,12 +965,12 @@ D3D11VideoDecoder::GetSupportedVideoDecoderConfigs(
VP9PROFILE_PROFILE2, // profile_max
min_resolution, // coded_size_min
max_vp9_profile2_resolutions.second, // coded_size_max
- allow_encrypted, // allow_encrypted
+ false, // allow_encrypted
false)); // require_encrypted
}
}
- // TODO(liberato): Should we separate out h264, vp9, and encrypted?
+ // TODO(liberato): Should we separate out h264 and vp9?
UMA_HISTOGRAM_ENUMERATION(uma_name, NotSupportedReason::kVideoIsSupported);
return configs;
diff --git a/chromium/media/gpu/windows/d3d11_video_decoder.h b/chromium/media/gpu/windows/d3d11_video_decoder.h
index 94c609fd976..d9ba26ab254 100644
--- a/chromium/media/gpu/windows/d3d11_video_decoder.h
+++ b/chromium/media/gpu/windows/d3d11_video_decoder.h
@@ -15,6 +15,7 @@
#include "base/sequence_checker.h"
#include "base/sequenced_task_runner.h"
#include "base/single_thread_task_runner.h"
+#include "base/threading/sequence_bound.h"
#include "gpu/config/gpu_driver_bug_workarounds.h"
#include "gpu/config/gpu_preferences.h"
#include "media/base/callback_registry.h"
@@ -27,6 +28,7 @@
#include "media/gpu/windows/d3d11_h264_accelerator.h"
#include "media/gpu/windows/d3d11_texture_selector.h"
#include "media/gpu/windows/d3d11_video_decoder_client.h"
+#include "media/gpu/windows/d3d11_video_decoder_impl.h"
#include "media/gpu/windows/d3d11_vp9_accelerator.h"
#include "media/video/supported_video_decoder_config.h"
@@ -37,7 +39,6 @@ class CommandBufferStub;
namespace media {
class D3D11PictureBuffer;
-class D3D11VideoDecoderImpl;
class D3D11VideoDecoderTest;
class MediaLog;
@@ -63,7 +64,8 @@ class MEDIA_GPU_EXPORT D3D11VideoDecoder : public VideoDecoder,
const gpu::GpuDriverBugWorkarounds& gpu_workarounds,
base::RepeatingCallback<gpu::CommandBufferStub*()> get_stub_cb,
GetD3D11DeviceCB get_d3d11_device_cb,
- SupportedConfigs supported_configs);
+ SupportedConfigs supported_configs,
+ bool is_hdr_supported);
// VideoDecoder implementation:
std::string GetDisplayName() const override;
@@ -110,24 +112,26 @@ class MEDIA_GPU_EXPORT D3D11VideoDecoder : public VideoDecoder,
std::unique_ptr<MediaLog> media_log,
const gpu::GpuPreferences& gpu_preferences,
const gpu::GpuDriverBugWorkarounds& gpu_workarounds,
- std::unique_ptr<D3D11VideoDecoderImpl> impl,
+ base::SequenceBound<D3D11VideoDecoderImpl> impl,
base::RepeatingCallback<scoped_refptr<CommandBufferHelper>()>
get_helper_cb,
GetD3D11DeviceCB get_d3d11_device_cb,
- SupportedConfigs supported_configs);
+ SupportedConfigs supported_configs,
+ bool is_hdr_supported);
// Receive |buffer|, that is now unused by the client.
void ReceivePictureBufferFromClient(scoped_refptr<D3D11PictureBuffer> buffer);
// Called when the gpu side of initialization is complete.
- void OnGpuInitComplete(bool success);
+ void OnGpuInitComplete(
+ bool success,
+ D3D11VideoDecoderImpl::ReleaseMailboxCB release_mailbox_cb);
// Run the decoder loop.
void DoDecode();
// instantiate |accelerated_video_decoder_| based on the video profile
HRESULT InitializeAcceleratedDecoder(const VideoDecoderConfig& config,
- CdmProxyContext* proxy_context,
ComD3D11VideoDecoder video_decoder);
// Query the video device for a specific decoder ID.
@@ -196,38 +200,22 @@ class MEDIA_GPU_EXPORT D3D11VideoDecoder : public VideoDecoder,
// creation is pending.
kRunning,
- // The decoder cannot make progress because it doesn't have the key to
- // decrypt the buffer. Waiting for a new key to be available.
- // This should only be transitioned from kRunning, and should only
- // transition to kRunning or kWaitingForReset.
- kWaitingForNewKey,
-
- // The decoder cannot make progress because it's waiting for a Reset(). This
- // could happen as a result of CdmContext hardware context loss. This should
- // only be transitioned from kRunning or kWaitingForNewKey, and should only
- // transition to kRunning.
- kWaitingForReset,
-
// A fatal error occurred. A terminal state.
kError,
};
- // Callback to notify that new CdmContext event is available.
- void OnCdmContextEvent(CdmContext::Event event);
-
// Enter the kError state. This will fail any pending |init_cb_| and / or
// pending decode as well.
void NotifyError(const char* reason);
- // The implementation, which we trampoline to the impl thread.
- // This must be freed on the impl thread.
- std::unique_ptr<D3D11VideoDecoderImpl> impl_;
+ // The implementation, which lives on the GPU main thread.
+ base::SequenceBound<D3D11VideoDecoderImpl> impl_;
- // Weak ptr to |impl_|, which we use for callbacks.
- base::WeakPtr<D3D11VideoDecoderImpl> impl_weak_;
+ // GPU main thread task runner.
+ scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner_;
- // Task runner for |impl_|. This must be the GPU main thread.
- scoped_refptr<base::SequencedTaskRunner> impl_task_runner_;
+ // Task runner on which |this| lives.
+ scoped_refptr<base::SequencedTaskRunner> decoder_task_runner_;
// Set in initialize, and used to determine reinitializations.
bool already_initialized_;
@@ -239,13 +227,18 @@ class MEDIA_GPU_EXPORT D3D11VideoDecoder : public VideoDecoder,
VideoDecoderConfig config_;
InitCB init_cb_;
OutputCB output_cb_;
- WaitingCB waiting_cb_;
+
+ // Callback to be used as a release CB for VideoFrames. Be sure to
+ // BindToCurrentLoop the closure that it takes.
+ D3D11VideoDecoderImpl::ReleaseMailboxCB release_mailbox_cb_;
// Right now, this is used both for the video decoder and for display. In
// the future, this should only be for the video decoder. We should use
// the ANGLE device for display (plus texture sharing, if needed).
GetD3D11DeviceCB get_d3d11_device_cb_;
+ // These may be accessed from |decoder_task_runner_|, since the angle device
+ // is in multi-threaded mode. Just be sure not to set any global state.
ComD3D11Device device_;
ComD3D11DeviceContext device_context_;
ComD3D11VideoDevice video_device_;
@@ -265,9 +258,6 @@ class MEDIA_GPU_EXPORT D3D11VideoDecoder : public VideoDecoder,
DecodeCB current_decode_cb_;
base::TimeDelta current_timestamp_;
- // Callback registration to keep the new key callback registered.
- std::unique_ptr<CallbackRegistration> new_key_callback_registration_;
-
// Must be called on the gpu main thread. So, don't call it from here,
// since we don't know what thread we're on.
base::RepeatingCallback<gpu::CommandBufferStub*()> get_stub_cb_;
@@ -290,6 +280,9 @@ class MEDIA_GPU_EXPORT D3D11VideoDecoder : public VideoDecoder,
SupportedConfigs supported_configs_;
+ // Should we assume that we're outputting to an HDR display?
+ bool is_hdr_supported_;
+
base::WeakPtrFactory<D3D11VideoDecoder> weak_factory_{this};
DISALLOW_COPY_AND_ASSIGN(D3D11VideoDecoder);
diff --git a/chromium/media/gpu/windows/d3d11_video_decoder_impl.cc b/chromium/media/gpu/windows/d3d11_video_decoder_impl.cc
index 89c88e56c6f..719e7b516fe 100644
--- a/chromium/media/gpu/windows/d3d11_video_decoder_impl.cc
+++ b/chromium/media/gpu/windows/d3d11_video_decoder_impl.cc
@@ -8,6 +8,7 @@
#include "gpu/command_buffer/common/sync_token.h"
#include "gpu/command_buffer/service/scheduler.h"
#include "gpu/ipc/service/gpu_channel.h"
+#include "media/base/bind_to_current_loop.h"
#include "media/base/media_log.h"
#include "media/gpu/windows/d3d11_picture_buffer.h"
@@ -25,16 +26,12 @@ D3D11VideoDecoderImpl::~D3D11VideoDecoderImpl() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
}
-void D3D11VideoDecoderImpl::Initialize(
- InitCB init_cb,
- ReturnPictureBufferCB return_picture_buffer_cb) {
+void D3D11VideoDecoderImpl::Initialize(InitCB init_cb) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- return_picture_buffer_cb_ = std::move(return_picture_buffer_cb);
-
// If have a helper, then we're as initialized as we need to be.
if (helper_) {
- std::move(init_cb).Run(true);
+ std::move(init_cb).Run(true, release_mailbox_cb_);
return;
}
helper_ = get_helper_cb_.Run();
@@ -46,31 +43,36 @@ void D3D11VideoDecoderImpl::Initialize(
if (media_log_)
MEDIA_LOG(ERROR, media_log_) << reason;
- std::move(init_cb).Run(false);
+ std::move(init_cb).Run(false, ReleaseMailboxCB());
return;
}
- std::move(init_cb).Run(true);
+ release_mailbox_cb_ = BindToCurrentLoop(base::BindRepeating(
+ &D3D11VideoDecoderImpl::OnMailboxReleased, GetWeakPtr()));
+
+ std::move(init_cb).Run(true, release_mailbox_cb_);
}
void D3D11VideoDecoderImpl::OnMailboxReleased(
- scoped_refptr<D3D11PictureBuffer> buffer,
+ base::OnceClosure wait_complete_cb,
const gpu::SyncToken& sync_token) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- if (!helper_)
+ if (!helper_) {
+ std::move(wait_complete_cb).Run();
return;
+ }
helper_->WaitForSyncToken(
sync_token, base::BindOnce(&D3D11VideoDecoderImpl::OnSyncTokenReleased,
- GetWeakPtr(), std::move(buffer)));
+ GetWeakPtr(), std::move(wait_complete_cb)));
}
void D3D11VideoDecoderImpl::OnSyncTokenReleased(
- scoped_refptr<D3D11PictureBuffer> buffer) {
+ base::OnceClosure wait_complete_cb) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- return_picture_buffer_cb_.Run(std::move(buffer));
+ std::move(wait_complete_cb).Run();
}
base::WeakPtr<D3D11VideoDecoderImpl> D3D11VideoDecoderImpl::GetWeakPtr() {
diff --git a/chromium/media/gpu/windows/d3d11_video_decoder_impl.h b/chromium/media/gpu/windows/d3d11_video_decoder_impl.h
index c6549268c2c..770d81cf88f 100644
--- a/chromium/media/gpu/windows/d3d11_video_decoder_impl.h
+++ b/chromium/media/gpu/windows/d3d11_video_decoder_impl.h
@@ -34,6 +34,10 @@ class D3D11PictureBuffer;
// TODO(liberato): Rename this class as a follow-on to this refactor.
class MEDIA_GPU_EXPORT D3D11VideoDecoderImpl {
public:
+ // Callback to us to wait for a sync token, then call a closure.
+ using ReleaseMailboxCB =
+ base::RepeatingCallback<void(base::OnceClosure, const gpu::SyncToken&)>;
+
// May be constructed on any thread.
explicit D3D11VideoDecoderImpl(
std::unique_ptr<MediaLog> media_log,
@@ -41,20 +45,17 @@ class MEDIA_GPU_EXPORT D3D11VideoDecoderImpl {
get_helper_cb);
virtual ~D3D11VideoDecoderImpl();
- using InitCB = base::OnceCallback<void(bool success)>;
+ using InitCB = base::OnceCallback<void(bool success, ReleaseMailboxCB)>;
// Returns a picture buffer that's no longer in use by the client.
using ReturnPictureBufferCB =
base::RepeatingCallback<void(scoped_refptr<D3D11PictureBuffer>)>;
- // We will call back |init_cb| with the init status. |try_decoding_cb| should
- // try to re-start decoding. We'll call this when we do something that might
- // allow decoding to make progress, such as reclaim a picture buffer.
- virtual void Initialize(InitCB init_cb,
- ReturnPictureBufferCB return_picture_buffer_cb);
+ // We will call back |init_cb| with the init status.
+ virtual void Initialize(InitCB init_cb);
- // Called when the VideoFrame that uses |buffer| is freed.
- void OnMailboxReleased(scoped_refptr<D3D11PictureBuffer> buffer,
+ // Called to wait on |sync_token|, and call |wait_complete_cb| when done.
+ void OnMailboxReleased(base::OnceClosure wait_complete_cb,
const gpu::SyncToken& sync_token);
// Return a weak ptr, since D3D11VideoDecoder constructs callbacks for us.
@@ -62,12 +63,15 @@ class MEDIA_GPU_EXPORT D3D11VideoDecoderImpl {
base::WeakPtr<D3D11VideoDecoderImpl> GetWeakPtr();
private:
- void OnSyncTokenReleased(scoped_refptr<D3D11PictureBuffer> buffer);
+ void OnSyncTokenReleased(base::OnceClosure);
std::unique_ptr<MediaLog> media_log_;
+ // Cached copy of the callback to OnMailboxReleased.
+ ReleaseMailboxCB release_mailbox_cb_;
+
// Called when we get a picture buffer back from the client.
- ReturnPictureBufferCB return_picture_buffer_cb_;
+ // ReturnPictureBufferCB return_picture_buffer_cb_;
base::RepeatingCallback<scoped_refptr<CommandBufferHelper>()> get_helper_cb_;
scoped_refptr<CommandBufferHelper> helper_;
diff --git a/chromium/media/gpu/windows/d3d11_video_decoder_unittest.cc b/chromium/media/gpu/windows/d3d11_video_decoder_unittest.cc
index 1eb58153b01..91908445262 100644
--- a/chromium/media/gpu/windows/d3d11_video_decoder_unittest.cc
+++ b/chromium/media/gpu/windows/d3d11_video_decoder_unittest.cc
@@ -40,16 +40,15 @@ namespace media {
class MockD3D11VideoDecoderImpl : public D3D11VideoDecoderImpl {
public:
- MockD3D11VideoDecoderImpl()
+ MockD3D11VideoDecoderImpl(MockD3D11VideoDecoderImpl** thiz)
: D3D11VideoDecoderImpl(
nullptr,
- base::RepeatingCallback<scoped_refptr<CommandBufferHelper>()>()) {}
-
- void Initialize(InitCB init_cb,
- ReturnPictureBufferCB return_picture_buffer_cb) override {
- MockInitialize();
+ base::RepeatingCallback<scoped_refptr<CommandBufferHelper>()>()) {
+ *thiz = this;
}
+ void Initialize(InitCB init_cb) override { MockInitialize(); }
+
MOCK_METHOD0(MockInitialize, void());
};
@@ -65,6 +64,7 @@ class D3D11VideoDecoderTest : public ::testing::Test {
gpu_preferences_.enable_zero_copy_dxgi_video = true;
gpu_preferences_.use_passthrough_cmd_decoder = false;
gpu_workarounds_.disable_dxgi_zero_copy_video = false;
+ gpu_task_runner_ = task_environment_.GetMainThreadTaskRunner();
// Create a mock D3D11 device that supports 11.0. Note that if you change
// this, then you probably also want VideoDevice1 and friends, below.
@@ -189,11 +189,9 @@ class D3D11VideoDecoderTest : public ::testing::Test {
supported_configs = D3D11VideoDecoder::GetSupportedVideoDecoderConfigs(
gpu_preferences_, gpu_workarounds_, get_device_cb);
}
- std::unique_ptr<MockD3D11VideoDecoderImpl> impl =
- std::make_unique<NiceMock<MockD3D11VideoDecoderImpl>>();
- impl_ = impl.get();
-
- gpu_task_runner_ = base::ThreadTaskRunnerHandle::Get();
+ base::SequenceBound<MockD3D11VideoDecoderImpl> impl(gpu_task_runner_,
+ &impl_);
+ task_environment_.RunUntilIdle();
// We store it in a std::unique_ptr<VideoDecoder> so that the default
// deleter works. The dtor is protected.
@@ -202,7 +200,7 @@ class D3D11VideoDecoderTest : public ::testing::Test {
gpu_task_runner_, std::make_unique<NullMediaLog>(),
gpu_preferences_, gpu_workarounds_, std::move(impl),
base::RepeatingCallback<scoped_refptr<CommandBufferHelper>()>(),
- get_device_cb, *supported_configs));
+ get_device_cb, *supported_configs, is_hdr_supported_));
}
void InitializeDecoder(const VideoDecoderConfig& config,
@@ -218,8 +216,8 @@ class D3D11VideoDecoderTest : public ::testing::Test {
}
decoder_->Initialize(
config, low_delay, cdm_context,
- base::BindRepeating(&D3D11VideoDecoderTest::CheckExpectedStatus,
- base::Unretained(this), expectation),
+ base::BindOnce(&D3D11VideoDecoderTest::CheckExpectedStatus,
+ base::Unretained(this), expectation),
base::DoNothing(), base::DoNothing());
base::RunLoop().RunUntilIdle();
}
@@ -231,7 +229,7 @@ class D3D11VideoDecoderTest : public ::testing::Test {
MOCK_METHOD1(MockInitCB, void(Status));
- base::test::TaskEnvironment env_;
+ base::test::TaskEnvironment task_environment_;
scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner_;
@@ -249,6 +247,9 @@ class D3D11VideoDecoderTest : public ::testing::Test {
Microsoft::WRL::ComPtr<DXGIDeviceMock> mock_dxgi_device_;
Microsoft::WRL::ComPtr<DXGIAdapterMock> mock_dxgi_adapter_;
+ // Used by CreateDecoder() to tell D3D11VideoDecoder about HDR support.
+ bool is_hdr_supported_ = true;
+
DXGI_ADAPTER_DESC mock_adapter_desc_;
base::Optional<base::test::ScopedFeatureList> scoped_feature_list_;
diff --git a/chromium/media/gpu/windows/d3d11_video_processor_proxy.cc b/chromium/media/gpu/windows/d3d11_video_processor_proxy.cc
index 74341290230..2799986e5a9 100644
--- a/chromium/media/gpu/windows/d3d11_video_processor_proxy.cc
+++ b/chromium/media/gpu/windows/d3d11_video_processor_proxy.cc
@@ -98,6 +98,29 @@ void VideoProcessorProxy::SetOutputColorSpace(
}
}
+void VideoProcessorProxy::SetStreamHDRMetadata(
+ const DXGI_HDR_METADATA_HDR10& stream_metadata) {
+ ComD3D11VideoContext2 video_context2;
+ if (FAILED(video_context_.As(&video_context2)))
+ return;
+
+ // TODO: we shouldn't do this unless we also set the display metadata.
+ video_context2->VideoProcessorSetOutputHDRMetaData(
+ video_processor_.Get(), DXGI_HDR_METADATA_TYPE_HDR10,
+ sizeof(stream_metadata), &stream_metadata);
+}
+
+void VideoProcessorProxy::SetDisplayHDRMetadata(
+ const DXGI_HDR_METADATA_HDR10& display_metadata) {
+ ComD3D11VideoContext2 video_context2;
+ if (FAILED(video_context_.As(&video_context2)))
+ return;
+
+ video_context2->VideoProcessorSetOutputHDRMetaData(
+ video_processor_.Get(), DXGI_HDR_METADATA_TYPE_HDR10,
+ sizeof(display_metadata), &display_metadata);
+}
+
HRESULT VideoProcessorProxy::VideoProcessorBlt(
ID3D11VideoProcessorOutputView* output_view,
UINT output_frameno,
diff --git a/chromium/media/gpu/windows/d3d11_video_processor_proxy.h b/chromium/media/gpu/windows/d3d11_video_processor_proxy.h
index a6eece036f3..1e5679a2b74 100644
--- a/chromium/media/gpu/windows/d3d11_video_processor_proxy.h
+++ b/chromium/media/gpu/windows/d3d11_video_processor_proxy.h
@@ -9,6 +9,7 @@
#include <wrl/client.h>
#include <cstdint>
+#include "media/base/hdr_metadata.h"
#include "media/gpu/media_gpu_export.h"
#include "media/gpu/windows/d3d11_com_defs.h"
#include "ui/gfx/color_space.h"
@@ -43,6 +44,13 @@ class MEDIA_GPU_EXPORT VideoProcessorProxy {
// Configure the output color space on the video context.
virtual void SetOutputColorSpace(const gfx::ColorSpace& color_space);
+ // Set the stream / display metadata. Optional, and may silently do nothing
+ // if it's not supported.
+ virtual void SetStreamHDRMetadata(
+ const DXGI_HDR_METADATA_HDR10& stream_metadata);
+ virtual void SetDisplayHDRMetadata(
+ const DXGI_HDR_METADATA_HDR10& display_metadata);
+
virtual HRESULT VideoProcessorBlt(ID3D11VideoProcessorOutputView* output_view,
UINT output_frameno,
UINT stream_count,
diff --git a/chromium/media/gpu/windows/d3d11_vp9_accelerator.cc b/chromium/media/gpu/windows/d3d11_vp9_accelerator.cc
index 267f09a4d17..eeec7896bde 100644
--- a/chromium/media/gpu/windows/d3d11_vp9_accelerator.cc
+++ b/chromium/media/gpu/windows/d3d11_vp9_accelerator.cc
@@ -9,7 +9,6 @@
#include <utility>
#include "base/memory/ptr_util.h"
-#include "media/cdm/cdm_proxy_context.h"
#include "media/gpu/windows/d3d11_vp9_picture.h"
namespace media {
@@ -37,21 +36,17 @@ CreateSubsampleMappingBlock(const std::vector<SubsampleEntry>& from) {
D3D11VP9Accelerator::D3D11VP9Accelerator(
D3D11VideoDecoderClient* client,
MediaLog* media_log,
- CdmProxyContext* cdm_proxy_context,
ComD3D11VideoDecoder video_decoder,
ComD3D11VideoDevice video_device,
std::unique_ptr<VideoContextWrapper> video_context)
: client_(client),
media_log_(media_log),
- cdm_proxy_context_(cdm_proxy_context),
status_feedback_(0),
video_decoder_(std::move(video_decoder)),
video_device_(std::move(video_device)),
video_context_(std::move(video_context)) {
DCHECK(client);
DCHECK(media_log_);
- // |cdm_proxy_context_| is non-null for encrypted content but can be null for
- // clear content.
}
D3D11VP9Accelerator::~D3D11VP9Accelerator() {}
@@ -70,33 +65,18 @@ scoped_refptr<VP9Picture> D3D11VP9Accelerator::CreateVP9Picture() {
}
bool D3D11VP9Accelerator::BeginFrame(const D3D11VP9Picture& pic) {
- // This |decrypt_context| has to be outside the if block because pKeyInfo in
- // D3D11_VIDEO_DECODER_BEGIN_FRAME_CRYPTO_SESSION is a pointer (to a GUID).
- base::Optional<CdmProxyContext::D3D11DecryptContext> decrypt_context;
- std::unique_ptr<D3D11_VIDEO_DECODER_BEGIN_FRAME_CRYPTO_SESSION> content_key;
- if (const DecryptConfig* config = pic.decrypt_config()) {
- DCHECK(cdm_proxy_context_) << "No CdmProxyContext but picture is encrypted";
- decrypt_context = cdm_proxy_context_->GetD3D11DecryptContext(
- CdmProxy::KeyType::kDecryptAndDecode, config->key_id());
- if (!decrypt_context) {
- RecordFailure("crypto_config",
- "Cannot find the decrypt context for the frame.");
- return false; // TODO(crbug.com/894573): support kTryAgain.
- }
-
- content_key =
- std::make_unique<D3D11_VIDEO_DECODER_BEGIN_FRAME_CRYPTO_SESSION>();
- content_key->pCryptoSession = decrypt_context->crypto_session;
- content_key->pBlob = const_cast<void*>(decrypt_context->key_blob);
- content_key->BlobSize = decrypt_context->key_blob_size;
- content_key->pKeyInfoId = &decrypt_context->key_info_guid;
+ const bool is_encrypted = pic.decrypt_config();
+ if (is_encrypted) {
+ RecordFailure("crypto_config",
+ "Cannot find the decrypt context for the frame.");
+ return false;
}
HRESULT hr;
do {
hr = video_context_->DecoderBeginFrame(
- video_decoder_.Get(), pic.picture_buffer()->output_view().Get(),
- content_key ? sizeof(*content_key) : 0, content_key.get());
+ video_decoder_.Get(), pic.picture_buffer()->output_view().Get(), 0,
+ nullptr);
} while (hr == E_PENDING || hr == D3DERR_WASSTILLDRAWING);
if (FAILED(hr)) {
@@ -121,7 +101,6 @@ void D3D11VP9Accelerator::CopyFrameParams(const D3D11VP9Picture& pic,
COPY_PARAM(frame_context_idx);
COPY_PARAM(reset_frame_context);
COPY_PARAM(allow_high_precision_mv);
- COPY_PARAM(refresh_frame_context);
COPY_PARAM(frame_parallel_decoding_mode);
COPY_PARAM(intra_only);
COPY_PARAM(frame_context_idx);
@@ -145,6 +124,18 @@ void D3D11VP9Accelerator::CopyFrameParams(const D3D11VP9Picture& pic,
SET_PARAM(log2_tile_rows, tile_rows_log2);
#undef COPY_PARAM
#undef SET_PARAM
+
+ // This is taken, approximately, from libvpx.
+ gfx::Size this_frame_size(pic.frame_hdr->frame_width,
+ pic.frame_hdr->frame_height);
+ pic_params->use_prev_in_find_mv_refs = last_frame_size_ == this_frame_size &&
+ !pic.frame_hdr->error_resilient_mode &&
+ !pic.frame_hdr->intra_only &&
+ last_show_frame_;
+
+ // TODO(liberato): So, uh, do we ever need to reset this?
+ last_frame_size_ = this_frame_size;
+ last_show_frame_ = pic.frame_hdr->show_frame;
}
void D3D11VP9Accelerator::CopyReferenceFrames(
diff --git a/chromium/media/gpu/windows/d3d11_vp9_accelerator.h b/chromium/media/gpu/windows/d3d11_vp9_accelerator.h
index e31d7a33d9a..dc262d68d26 100644
--- a/chromium/media/gpu/windows/d3d11_vp9_accelerator.h
+++ b/chromium/media/gpu/windows/d3d11_vp9_accelerator.h
@@ -19,13 +19,11 @@
#include "media/gpu/windows/d3d11_vp9_picture.h"
namespace media {
-class CdmProxyContext;
class D3D11VP9Accelerator : public VP9Decoder::VP9Accelerator {
public:
D3D11VP9Accelerator(D3D11VideoDecoderClient* client,
MediaLog* media_log,
- CdmProxyContext* cdm_proxy_context,
ComD3D11VideoDecoder video_decoder,
ComD3D11VideoDevice video_device,
std::unique_ptr<VideoContextWrapper> video_context);
@@ -73,12 +71,15 @@ class D3D11VP9Accelerator : public VP9Decoder::VP9Accelerator {
D3D11VideoDecoderClient* client_;
MediaLog* const media_log_;
- CdmProxyContext* cdm_proxy_context_;
UINT status_feedback_;
ComD3D11VideoDecoder video_decoder_;
ComD3D11VideoDevice video_device_;
std::unique_ptr<VideoContextWrapper> video_context_;
+ // Used to set |use_prev_in_find_mv_refs| properly.
+ gfx::Size last_frame_size_;
+ bool last_show_frame_ = false;
+
DISALLOW_COPY_AND_ASSIGN(D3D11VP9Accelerator);
};
diff --git a/chromium/media/gpu/windows/display_helper.h b/chromium/media/gpu/windows/display_helper.h
index baf45eec5f0..2dc77aa1174 100644
--- a/chromium/media/gpu/windows/display_helper.h
+++ b/chromium/media/gpu/windows/display_helper.h
@@ -5,6 +5,7 @@
#ifndef MEDIA_GPU_WINDOWS_DISPLAY_HELPER_H_
#define MEDIA_GPU_WINDOWS_DISPLAY_HELPER_H_
+#include "base/macros.h"
#include "base/optional.h"
#include "media/base/hdr_metadata.h"
#include "media/gpu/media_gpu_export.h"
diff --git a/chromium/media/gpu/windows/dxva_video_decode_accelerator_win.cc b/chromium/media/gpu/windows/dxva_video_decode_accelerator_win.cc
index 30fc6d3977b..ff451f0bb17 100644
--- a/chromium/media/gpu/windows/dxva_video_decode_accelerator_win.cc
+++ b/chromium/media/gpu/windows/dxva_video_decode_accelerator_win.cc
@@ -128,6 +128,12 @@ static const GUID DXVA2_Intel_ModeH264_E = {
0x4c54,
{0x88, 0xFE, 0xAB, 0xD2, 0x5C, 0x15, 0xB3, 0xD6}};
+static const CLSID CLSID_CAV1DecoderMFT = {
+ 0xC843981A,
+ 0x3359,
+ 0x4721,
+ {0xB3, 0xF5, 0xD4, 0x84, 0xD8, 0x56, 0x1E, 0x47}};
+
constexpr const wchar_t* const kMediaFoundationVideoDecoderDLLs[] = {
L"mf.dll", L"mfplat.dll", L"msmpeg2vdec.dll",
};
@@ -140,6 +146,23 @@ uint64_t GetCurrentQPC() {
return perf_counter_now.QuadPart;
}
+HRESULT CreateAV1Decoder(const IID& iid, void** object) {
+ MFT_REGISTER_TYPE_INFO type_info = {MFMediaType_Video, MFVideoFormat_AV1};
+
+ base::win::ScopedCoMem<IMFActivate*> acts;
+ UINT32 acts_num;
+ HRESULT hr =
+ ::MFTEnumEx(MFT_CATEGORY_VIDEO_DECODER, MFT_ENUM_FLAG_SORTANDFILTER,
+ &type_info, nullptr, &acts, &acts_num);
+ if (FAILED(hr))
+ return hr;
+
+ if (acts_num < 1)
+ return E_FAIL;
+
+ return acts[0]->ActivateObject(iid, object);
+}
+
uint64_t g_last_process_output_time;
HRESULT g_last_device_removed_reason;
@@ -162,8 +185,9 @@ HRESULT g_last_device_removed_reason;
namespace media {
static const VideoCodecProfile kSupportedProfiles[] = {
- H264PROFILE_BASELINE, H264PROFILE_MAIN, H264PROFILE_HIGH,
- VP8PROFILE_ANY, VP9PROFILE_PROFILE0, VP9PROFILE_PROFILE2};
+ H264PROFILE_BASELINE, H264PROFILE_MAIN, H264PROFILE_HIGH,
+ VP8PROFILE_ANY, VP9PROFILE_PROFILE0, VP9PROFILE_PROFILE2,
+ AV1PROFILE_PROFILE_MAIN, AV1PROFILE_PROFILE_HIGH, AV1PROFILE_PROFILE_PRO};
CreateDXGIDeviceManager
DXVAVideoDecodeAccelerator::create_dxgi_device_manager_ = NULL;
@@ -437,6 +461,8 @@ class VP9ConfigChangeDetector : public ConfigChangeDetector {
// Returns false on failure.
bool DetectConfig(const uint8_t* stream, unsigned int size) override {
parser_.SetStream(stream, size, nullptr);
+ config_changed_ = false;
+
Vp9FrameHeader fhdr;
gfx::Size allocate_size;
std::unique_ptr<DecryptConfig> null_config;
@@ -494,11 +520,17 @@ class VP8ConfigChangeDetector : public ConfigChangeDetector {
// Detects stream configuration changes.
// Returns false on failure.
bool DetectConfig(const uint8_t* stream, unsigned int size) override {
+ config_changed_ = false;
+
Vp8FrameHeader fhdr;
if (!parser_.ParseFrame(stream, size, &fhdr))
return false;
- if (fhdr.IsKeyframe() && fhdr.is_full_range) {
+ // VP8 parser only return resolution and range fields on key frames.
+ if (!fhdr.IsKeyframe())
+ return true;
+
+ if (fhdr.is_full_range) {
// VP8 has no color space information, only the range. We will always
// prefer the config color space if set, but indicate JPEG when the full
// range flag is set on the frame header.
@@ -507,22 +539,12 @@ class VP8ConfigChangeDetector : public ConfigChangeDetector {
// Does VP8 need a separate visible rect?
gfx::Size new_size(fhdr.width, fhdr.height);
- if (!size_.IsEmpty() && !pending_config_changed_ && !config_changed_ &&
- size_ != new_size) {
- pending_config_changed_ = true;
+ if (!size_.IsEmpty() && size_ != new_size) {
+ config_changed_ = true;
DVLOG(1) << "Configuration changed from " << size_.ToString() << " to "
<< new_size.ToString();
}
size_ = new_size;
-
- // Resolution changes can happen on any frame technically, so wait for a
- // keyframe before signaling the config change.
- if (fhdr.IsKeyframe() && pending_config_changed_) {
- config_changed_ = true;
- pending_config_changed_ = false;
- }
- if (pending_config_changed_)
- DVLOG(3) << "Deferring config change until next keyframe...";
return true;
}
@@ -539,7 +561,6 @@ class VP8ConfigChangeDetector : public ConfigChangeDetector {
private:
gfx::Size size_;
- bool pending_config_changed_ = false;
VideoColorSpace color_space_;
Vp8Parser parser_;
};
@@ -1343,7 +1364,9 @@ DXVAVideoDecodeAccelerator::GetSupportedProfiles(
const bool is_vp9 = supported_profile >= VP9PROFILE_MIN &&
supported_profile <= VP9PROFILE_MAX;
const bool is_vp8 = supported_profile == VP8PROFILE_ANY;
- DCHECK(is_h264 || is_vp9 || is_vp8);
+ const bool is_av1 = supported_profile >= AV1PROFILE_MIN &&
+ supported_profile <= AV1PROFILE_MAX;
+ DCHECK(is_h264 || is_vp9 || is_vp8 || is_av1);
ResolutionPair max_resolutions;
if (is_h264) {
@@ -1354,6 +1377,17 @@ DXVAVideoDecodeAccelerator::GetSupportedProfiles(
max_resolutions = max_vp9_profile2_resolutions;
} else if (is_vp8) {
max_resolutions = max_vp8_resolutions;
+ } else if (is_av1) {
+ if (!base::FeatureList::IsEnabled(kMediaFoundationAV1Decoding))
+ continue;
+
+ // TODO(dalecurtis): Update GetResolutionsForDecoders() to support AV1.
+ SupportedProfile profile;
+ profile.profile = supported_profile;
+ profile.min_resolution = gfx::Size();
+ profile.max_resolution = gfx::Size(8192, 8192);
+ profiles.push_back(profile);
+ continue;
}
// Skip adding VPx profiles if it's not supported or disabled.
@@ -1452,13 +1486,23 @@ bool DXVAVideoDecodeAccelerator::InitDecoder(VideoCodecProfile profile) {
using_ms_vpx_mft_ = true;
}
- if (!decoder_dll) {
- RETURN_ON_FAILURE(false, "Unsupported codec.", false);
- }
+ if (base::FeatureList::IsEnabled(kMediaFoundationAV1Decoding) &&
+ (profile >= AV1PROFILE_MIN && profile <= AV1PROFILE_MAX)) {
+ codec_ = kCodecAV1;
+ clsid = CLSID_CAV1DecoderMFT;
- HRESULT hr =
- CreateCOMObjectFromDll(decoder_dll, clsid, IID_PPV_ARGS(&decoder_));
- RETURN_ON_HR_FAILURE(hr, "Failed to create decoder instance", false);
+ // Since the AV1 decoder is a Windows Store package, it can't be created
+ // from a DLL file like the other codecs. Microsoft baked helper code into
+ // the OS for VP9 which is why it works and AV1 doesn't.
+ HRESULT hr = CreateAV1Decoder(IID_PPV_ARGS(&decoder_));
+ RETURN_ON_HR_FAILURE(hr, "Failed to create decoder instance", false);
+ } else {
+ if (!decoder_dll)
+ RETURN_ON_FAILURE(false, "Unsupported codec.", false);
+ HRESULT hr =
+ CreateCOMObjectFromDll(decoder_dll, clsid, IID_PPV_ARGS(&decoder_));
+ RETURN_ON_HR_FAILURE(hr, "Failed to create decoder instance", false);
+ }
RETURN_ON_FAILURE(CheckDecoderDxvaSupport(),
"Failed to check decoder DXVA support", false);
@@ -1482,8 +1526,8 @@ bool DXVAVideoDecodeAccelerator::InitDecoder(VideoCodecProfile profile) {
device_manager_to_use = reinterpret_cast<ULONG_PTR>(device_manager_.Get());
}
- hr = decoder_->ProcessMessage(MFT_MESSAGE_SET_D3D_MANAGER,
- device_manager_to_use);
+ HRESULT hr = decoder_->ProcessMessage(MFT_MESSAGE_SET_D3D_MANAGER,
+ device_manager_to_use);
if (use_dx11_) {
RETURN_ON_HR_FAILURE(hr, "Failed to pass DX11 manager to decoder", false);
} else {
@@ -1635,6 +1679,8 @@ bool DXVAVideoDecodeAccelerator::SetDecoderInputMediaType() {
hr = media_type->SetGUID(MF_MT_SUBTYPE, MEDIASUBTYPE_VP90);
} else if (codec_ == kCodecVP8) {
hr = media_type->SetGUID(MF_MT_SUBTYPE, MEDIASUBTYPE_VP80);
+ } else if (codec_ == kCodecAV1) {
+ hr = media_type->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_AV1);
} else {
NOTREACHED();
RETURN_ON_FAILURE(false, "Unsupported codec on input media type.", false);
@@ -1708,29 +1754,35 @@ bool DXVAVideoDecodeAccelerator::GetStreamsInfoAndBufferReqs() {
DVLOG(1) << "Input stream info: ";
DVLOG(1) << "Max latency: " << input_stream_info_.hnsMaxLatency;
- if (codec_ == kCodecH264) {
- // There should be three flags, one for requiring a whole frame be in a
- // single sample, one for requiring there be one buffer only in a single
- // sample, and one that specifies a fixed sample size. (as in cbSize)
- CHECK_EQ(input_stream_info_.dwFlags, 0x7u);
- }
+
+ // There should be three flags, one for requiring a whole frame be in a
+ // single sample, one for requiring there be one buffer only in a single
+ // sample, and one that specifies a fixed sample size. (as in cbSize)
+ if (codec_ == kCodecH264 && input_stream_info_.dwFlags != 0x7u)
+ return false;
DVLOG(1) << "Min buffer size: " << input_stream_info_.cbSize;
DVLOG(1) << "Max lookahead: " << input_stream_info_.cbMaxLookahead;
DVLOG(1) << "Alignment: " << input_stream_info_.cbAlignment;
DVLOG(1) << "Output stream info: ";
- // The flags here should be the same and mean the same thing, except when
- // DXVA is enabled, there is an extra 0x100 flag meaning decoder will
- // allocate its own sample.
DVLOG(1) << "Flags: " << std::hex << std::showbase
<< output_stream_info_.dwFlags;
- if (codec_ == kCodecH264) {
- CHECK_EQ(output_stream_info_.dwFlags, 0x107u);
- }
DVLOG(1) << "Min buffer size: " << output_stream_info_.cbSize;
DVLOG(1) << "Alignment: " << output_stream_info_.cbAlignment;
- return true;
+
+ // The flags here should be the same and mean the same thing, except when
+ // DXVA is enabled, there is an extra 0x100 flag meaning decoder will
+ // allocate its own sample.
+ if (codec_ == kCodecH264 && output_stream_info_.dwFlags != 0x107u)
+ return false;
+
+ // We should fail above during MFT_MESSAGE_SET_D3D_MANAGER if the decoder
+ // doesn't allocate its own samples, but some MFTs are broken.
+ if (output_stream_info_.dwFlags & MFT_OUTPUT_STREAM_PROVIDES_SAMPLES)
+ return true;
+
+ return false;
}
void DXVAVideoDecodeAccelerator::DoDecode(const gfx::Rect& visible_rect,
diff --git a/chromium/media/gpu/windows/hresult_status_debug_device.cc b/chromium/media/gpu/windows/hresult_status_debug_device.cc
new file mode 100644
index 00000000000..25349655d05
--- /dev/null
+++ b/chromium/media/gpu/windows/hresult_status_debug_device.cc
@@ -0,0 +1,63 @@
+// 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/gpu/windows/hresult_status_debug_device.h"
+#include "media/base/media_serializers.h"
+#include "media/base/win/hresult_status_helper.h"
+
+namespace media {
+
+// Only define this method in debug mode.
+#if !defined(NDEBUG)
+
+Status AddDebugMessages(Status error, ComD3D11Device device) {
+ // MSDN says that this needs to be casted twice, then GetMessage should
+ // be called with a malloc.
+ Microsoft::WRL::ComPtr<ID3D11Debug> debug_layer;
+ if (!SUCCEEDED(device.As(&debug_layer)))
+ return error;
+
+ Microsoft::WRL::ComPtr<ID3D11InfoQueue> message_layer;
+ if (!SUCCEEDED(debug_layer.As(&message_layer)))
+ return error;
+
+ uint64_t messages_count = message_layer->GetNumStoredMessages();
+ if (messages_count == 0)
+ return error;
+
+ std::vector<std::string> messages(messages_count, "");
+ for (uint64_t i = 0; i < messages_count; i++) {
+ SIZE_T size;
+ message_layer->GetMessage(i, nullptr, &size);
+ D3D11_MESSAGE* message = reinterpret_cast<D3D11_MESSAGE*>(malloc(size));
+ if (!message) // probably OOM - so just stop trying to get more.
+ return error;
+ message_layer->GetMessage(i, message, &size);
+ messages.emplace_back(message->pDescription);
+ free(message);
+ }
+
+ return std::move(error).WithData("debug_info", messages);
+}
+
+#endif // !defined(NDEBUG)
+
+Status D3D11HresultToStatus(HRESULT hresult,
+ ComD3D11Device device,
+ const char* message,
+ const base::Location& location) {
+ if (SUCCEEDED(hresult))
+ return OkStatus();
+#if !defined(NDEBUG)
+ return AddDebugMessages(
+ HresultToStatus(hresult, message, StatusCode::kWindowsD3D11Error,
+ location),
+ device);
+#else // !defined(NDEBUG)
+ return HresultToStatus(hresult, message, StatusCode::kWindowsD3D11Error,
+ location);
+#endif // !defined(NDEBUG)
+}
+
+} // namespace media
diff --git a/chromium/media/gpu/windows/hresult_status_debug_device.h b/chromium/media/gpu/windows/hresult_status_debug_device.h
new file mode 100644
index 00000000000..b07fdc5714c
--- /dev/null
+++ b/chromium/media/gpu/windows/hresult_status_debug_device.h
@@ -0,0 +1,27 @@
+// 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_GPU_WINDOWS_HRESULT_STATUS_DEBUG_DEVICE_H_
+#define MEDIA_GPU_WINDOWS_HRESULT_STATUS_DEBUG_DEVICE_H_
+
+#include <d3d11.h>
+#include <wrl/client.h>
+
+#include "media/base/status.h"
+#include "media/gpu/windows/d3d11_com_defs.h"
+
+namespace media {
+
+// In debug mode, this uses |AddDebugMessages()| to give us a detailed error
+// trace from the d3d11 stack. Otherwise, it just generates a Status with the
+// kWindowsD3D11Error code.
+Status D3D11HresultToStatus(
+ HRESULT hresult,
+ ComD3D11Device device,
+ const char* message = nullptr,
+ const base::Location& location = base::Location::Current());
+
+} // namespace media
+
+#endif // MEDIA_GPU_WINDOWS_HRESULT_STATUS_DEBUG_DEVICE_H_ \ No newline at end of file
diff --git a/chromium/media/gpu/windows/media_foundation_video_encode_accelerator_win.cc b/chromium/media/gpu/windows/media_foundation_video_encode_accelerator_win.cc
index 910f3784ee7..e7a30122177 100644
--- a/chromium/media/gpu/windows/media_foundation_video_encode_accelerator_win.cc
+++ b/chromium/media/gpu/windows/media_foundation_video_encode_accelerator_win.cc
@@ -40,7 +40,7 @@ const size_t kMaxResolutionWidth = 1920;
const size_t kMaxResolutionHeight = 1088;
const size_t kNumInputBuffers = 3;
// Media Foundation uses 100 nanosecond units for time, see
-// https://msdn.microsoft.com/en-us/library/windows/desktop/ms697282(v=vs.85).aspx
+// https://msdn.microsoft.com/en-us/library/windows/desktop/ms697282(v=vs.85).aspx.
const size_t kOneMicrosecondInMFSampleTimeUnits = 10;
const size_t kOutputSampleBufferSizeRatio = 4;
@@ -48,9 +48,6 @@ constexpr const wchar_t* const kMediaFoundationVideoEncoderDLLs[] = {
L"mf.dll", L"mfplat.dll",
};
-// Resolutions that some platforms support, should be listed in ascending order.
-constexpr const gfx::Size kOptionalMaxResolutions[] = {gfx::Size(3840, 2176)};
-
eAVEncH264VProfile GetH264VProfile(VideoCodecProfile profile) {
switch (profile) {
case H264PROFILE_BASELINE:
@@ -106,8 +103,12 @@ struct MediaFoundationVideoEncodeAccelerator::BitstreamBufferRef {
// attributes are not supported by Windows 7, setting them will return errors.
// See bug: http://crbug.com/777659.
MediaFoundationVideoEncodeAccelerator::MediaFoundationVideoEncodeAccelerator(
- bool compatible_with_win7)
+ bool compatible_with_win7,
+ bool enable_async_mft)
: compatible_with_win7_(compatible_with_win7),
+ enable_async_mft_(enable_async_mft),
+ is_async_mft_(false),
+ input_required_(false),
main_client_task_runner_(base::ThreadTaskRunnerHandle::Get()),
encoder_thread_("MFEncoderThread") {}
@@ -130,21 +131,28 @@ MediaFoundationVideoEncodeAccelerator::GetSupportedProfiles() {
SupportedProfiles profiles;
target_bitrate_ = kDefaultTargetBitrate;
frame_rate_ = kMaxFrameRateNumerator / kMaxFrameRateDenominator;
- input_visible_size_ = gfx::Size(kMaxResolutionWidth, kMaxResolutionHeight);
- if (!CreateHardwareEncoderMFT() || !SetEncoderModes() ||
- !InitializeInputOutputSamples(H264PROFILE_BASELINE)) {
+ IMFActivate** pp_activate = nullptr;
+ uint32_t encoder_count = EnumerateHardwareEncoders(&pp_activate);
+ if (!encoder_count) {
ReleaseEncoderResources();
DVLOG(1)
<< "Hardware encode acceleration is not available on this platform.";
+
return profiles;
}
- gfx::Size highest_supported_resolution = input_visible_size_;
- for (const auto& resolution : kOptionalMaxResolutions) {
- DCHECK_GT(resolution.GetArea(), highest_supported_resolution.GetArea());
- if (!IsResolutionSupported(resolution))
- break;
- highest_supported_resolution = resolution;
+ if (pp_activate) {
+ // Release the enumerated instances if any.
+ // According to Windows Dev Center,
+ // https://docs.microsoft.com/en-us/windows/win32/api/mfapi/nf-mfapi-mftenumex
+ // The caller must release the pointers.
+ for (UINT32 i = 0; i < encoder_count; i++) {
+ if (pp_activate[i]) {
+ pp_activate[i]->Release();
+ pp_activate[i] = nullptr;
+ }
+ }
+ CoTaskMemFree(pp_activate);
}
ReleaseEncoderResources();
@@ -154,7 +162,7 @@ MediaFoundationVideoEncodeAccelerator::GetSupportedProfiles() {
profile.profile = H264PROFILE_BASELINE;
profile.max_framerate_numerator = kMaxFrameRateNumerator;
profile.max_framerate_denominator = kMaxFrameRateDenominator;
- profile.max_resolution = highest_supported_resolution;
+ profile.max_resolution = gfx::Size(kMaxResolutionWidth, kMaxResolutionHeight);
profiles.push_back(profile);
profile.profile = H264PROFILE_MAIN;
@@ -188,39 +196,61 @@ bool MediaFoundationVideoEncodeAccelerator::Initialize(const Config& config,
return false;
}
encoder_thread_task_runner_ = encoder_thread_.task_runner();
-
- if (!CreateHardwareEncoderMFT()) {
- DLOG(ERROR) << "Failed creating a hardware encoder MFT.";
+ IMFActivate** pp_activate = nullptr;
+ uint32_t encoder_count = EnumerateHardwareEncoders(&pp_activate);
+ if (!encoder_count) {
+ DLOG(ERROR) << "Failed finding a hardware encoder MFT.";
return false;
}
+ if (is_async_mft_) {
+ if (!ActivateAsyncEncoder(pp_activate, encoder_count)) {
+ DLOG(ERROR) << "Failed activating an async hardware encoder MFT.";
+
+ if (pp_activate) {
+ // Release the enumerated instances if any.
+ // According to Windows Dev Center,
+ // https://docs.microsoft.com/en-us/windows/win32/api/mfapi/nf-mfapi-mftenumex
+ // The caller must release the pointers.
+ for (UINT32 i = 0; i < encoder_count; i++) {
+ if (pp_activate[i]) {
+ pp_activate[i]->Release();
+ pp_activate[i] = nullptr;
+ }
+ }
+ CoTaskMemFree(pp_activate);
+ }
+ return false;
+ }
+
+ if (pp_activate) {
+ // Release the enumerated instances if any.
+ // According to Windows Dev Center,
+ // https://docs.microsoft.com/en-us/windows/win32/api/mfapi/nf-mfapi-mftenumex
+ // The caller must release the pointers.
+ for (UINT32 i = 0; i < encoder_count; i++) {
+ if (pp_activate[i]) {
+ pp_activate[i]->Release();
+ pp_activate[i] = nullptr;
+ }
+ }
+ CoTaskMemFree(pp_activate);
+ }
+ }
+
main_client_weak_factory_.reset(new base::WeakPtrFactory<Client>(client));
main_client_ = main_client_weak_factory_->GetWeakPtr();
input_visible_size_ = config.input_visible_size;
frame_rate_ = kMaxFrameRateNumerator / kMaxFrameRateDenominator;
target_bitrate_ = config.initial_bitrate;
bitstream_buffer_size_ = config.input_visible_size.GetArea();
- u_plane_offset_ =
- VideoFrame::PlaneSize(PIXEL_FORMAT_I420, VideoFrame::kYPlane,
- input_visible_size_)
- .GetArea();
- v_plane_offset_ = u_plane_offset_ + VideoFrame::PlaneSize(PIXEL_FORMAT_I420,
- VideoFrame::kUPlane,
- input_visible_size_)
- .GetArea();
- y_stride_ = VideoFrame::RowBytes(VideoFrame::kYPlane, PIXEL_FORMAT_I420,
- input_visible_size_.width());
- u_stride_ = VideoFrame::RowBytes(VideoFrame::kUPlane, PIXEL_FORMAT_I420,
- input_visible_size_.width());
- v_stride_ = VideoFrame::RowBytes(VideoFrame::kVPlane, PIXEL_FORMAT_I420,
- input_visible_size_.width());
if (!SetEncoderModes()) {
DLOG(ERROR) << "Failed setting encoder parameters.";
return false;
}
- if (!InitializeInputOutputSamples(config.output_profile)) {
+ if (!InitializeInputOutputParameters(config.output_profile)) {
DLOG(ERROR) << "Failed initializing input-output samples.";
return false;
}
@@ -232,20 +262,40 @@ bool MediaFoundationVideoEncodeAccelerator::Initialize(const Config& config,
input_sample_ = CreateEmptySampleWithBuffer(
input_stream_info.cbSize
? input_stream_info.cbSize
- : VideoFrame::AllocationSize(PIXEL_FORMAT_I420, input_visible_size_),
+ : VideoFrame::AllocationSize(PIXEL_FORMAT_NV12, input_visible_size_),
input_stream_info.cbAlignment);
- MFT_OUTPUT_STREAM_INFO output_stream_info;
- hr = encoder_->GetOutputStreamInfo(output_stream_id_, &output_stream_info);
- RETURN_ON_HR_FAILURE(hr, "Couldn't get output stream info", false);
- output_sample_ = CreateEmptySampleWithBuffer(
- output_stream_info.cbSize
- ? output_stream_info.cbSize
- : bitstream_buffer_size_ * kOutputSampleBufferSizeRatio,
- output_stream_info.cbAlignment);
-
- hr = encoder_->ProcessMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, NULL);
- RETURN_ON_HR_FAILURE(hr, "Couldn't set ProcessMessage", false);
+ if (is_async_mft_) {
+ // Start the asynchronous processing model
+ hr = encoder_->ProcessMessage(MFT_MESSAGE_COMMAND_FLUSH, 0);
+ RETURN_ON_HR_FAILURE(
+ hr, "Couldn't set ProcessMessage MFT_MESSAGE_COMMAND_FLUSH", false);
+ hr = encoder_->ProcessMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0);
+ RETURN_ON_HR_FAILURE(
+ hr, "Couldn't set ProcessMessage MFT_MESSAGE_NOTIFY_BEGIN_STREAMING",
+ false);
+ hr = encoder_->ProcessMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0);
+ RETURN_ON_HR_FAILURE(
+ hr, "Couldn't set ProcessMessage MFT_MESSAGE_NOTIFY_START_OF_STREAM",
+ false);
+ hr = encoder_->QueryInterface(IID_PPV_ARGS(&event_generator_));
+ RETURN_ON_HR_FAILURE(hr, "Couldn't get event generator", false);
+ } else {
+ // Create output sample for synchronous processing model
+ MFT_OUTPUT_STREAM_INFO output_stream_info;
+ hr = encoder_->GetOutputStreamInfo(output_stream_id_, &output_stream_info);
+ RETURN_ON_HR_FAILURE(hr, "Couldn't get output stream info", false);
+ output_sample_ = CreateEmptySampleWithBuffer(
+ output_stream_info.cbSize
+ ? output_stream_info.cbSize
+ : bitstream_buffer_size_ * kOutputSampleBufferSizeRatio,
+ output_stream_info.cbAlignment);
+
+ hr = encoder_->ProcessMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, 0);
+ RETURN_ON_HR_FAILURE(
+ hr, "Couldn't set ProcessMessage MFT_MESSAGE_NOTIFY_BEGIN_STREAMING",
+ false);
+ }
main_client_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&Client::RequireBitstreamBuffers, main_client_,
@@ -342,25 +392,27 @@ bool MediaFoundationVideoEncodeAccelerator::PreSandboxInitialization() {
return result;
}
-bool MediaFoundationVideoEncodeAccelerator::CreateHardwareEncoderMFT() {
+uint32_t MediaFoundationVideoEncodeAccelerator::EnumerateHardwareEncoders(
+ IMFActivate*** pp_activate) {
DVLOG(3) << __func__;
DCHECK(main_client_task_runner_->BelongsToCurrentThread());
if (!compatible_with_win7_ &&
base::win::GetVersion() < base::win::Version::WIN8) {
DVLOG(ERROR) << "Windows versions earlier than 8 are not supported.";
- return false;
+ return 0;
}
for (const wchar_t* mfdll : kMediaFoundationVideoEncoderDLLs) {
if (!::GetModuleHandle(mfdll)) {
DVLOG(ERROR) << mfdll << " is required for encoding";
- return false;
+ return 0;
}
}
- if (!(session_ = InitializeMediaFoundation()))
- return false;
+ if (!(session_ = InitializeMediaFoundation())) {
+ return 0;
+ }
uint32_t flags = MFT_ENUM_FLAG_HARDWARE | MFT_ENUM_FLAG_SORTANDFILTER;
MFT_REGISTER_TYPE_INFO input_info;
@@ -370,32 +422,119 @@ bool MediaFoundationVideoEncodeAccelerator::CreateHardwareEncoderMFT() {
output_info.guidMajorType = MFMediaType_Video;
output_info.guidSubtype = MFVideoFormat_H264;
- base::win::ScopedCoMem<CLSID> CLSIDs;
uint32_t count = 0;
- HRESULT hr = MFTEnum(MFT_CATEGORY_VIDEO_ENCODER, flags, &input_info,
- &output_info, NULL, &CLSIDs, &count);
- RETURN_ON_HR_FAILURE(hr, "Couldn't enumerate hardware encoder", false);
- RETURN_ON_FAILURE((count > 0), "No HW encoder found", false);
- DVLOG(3) << "HW encoder(s) found: " << count;
- hr = ::CoCreateInstance(CLSIDs[0], nullptr, CLSCTX_ALL,
- IID_PPV_ARGS(&encoder_));
- RETURN_ON_HR_FAILURE(hr, "Couldn't activate hardware encoder", false);
+ HRESULT hr = E_FAIL;
+ if (enable_async_mft_) {
+ // Use MFTEnumEx to find hardware encoder.
+ hr = MFTEnumEx(MFT_CATEGORY_VIDEO_ENCODER, flags, &input_info, &output_info,
+ pp_activate, &count);
+ RETURN_ON_HR_FAILURE(
+ hr, "Couldn't enumerate hardware encoder from MFTEnumEx", 0);
+ RETURN_ON_FAILURE((count > 0), "No asynchronous MFT encoder found", 0);
+ DVLOG(3) << "Hardware encoder(s) available found from MFTEnumEx: " << count;
+ is_async_mft_ = true;
+ } else {
+ // Use MFTEnum to find hardware encoder.
+ base::win::ScopedCoMem<CLSID> CLSIDs;
+ hr = MFTEnum(MFT_CATEGORY_VIDEO_ENCODER, flags, &input_info, &output_info,
+ nullptr, &CLSIDs, &count);
+ RETURN_ON_HR_FAILURE(hr, "Couldn't enumerate hardware encoder from MFTEnum",
+ 0);
+ RETURN_ON_FAILURE((count > 0), "No legacy MFT encoder found", 0);
+ DVLOG(3) << "Hardware encoder(s) available found from MFTEnum: " << count;
+ hr = ::CoCreateInstance(CLSIDs[0], nullptr, CLSCTX_ALL,
+ IID_PPV_ARGS(&encoder_));
+ RETURN_ON_HR_FAILURE(hr, "Couldn't create legacy MFT encoder", 0);
+ RETURN_ON_FAILURE((encoder_.Get() != nullptr),
+ "No legacy MFT encoder instance created", 0);
+ is_async_mft_ = false;
+ }
+
+ return count;
+}
+
+bool MediaFoundationVideoEncodeAccelerator::ActivateAsyncEncoder(
+ IMFActivate** pp_activate,
+ uint32_t encoder_count) {
+ DVLOG(3) << __func__;
+ DCHECK(main_client_task_runner_->BelongsToCurrentThread());
+
+ // Try to create the encoder with priority according to merit value.
+ HRESULT hr = E_FAIL;
+ for (UINT32 i = 0; i < encoder_count; i++) {
+ if (FAILED(hr)) {
+ DCHECK(!encoder_);
+ DCHECK(!activate_);
+ hr = pp_activate[i]->ActivateObject(IID_PPV_ARGS(&encoder_));
+ if (encoder_.Get() != nullptr) {
+ DCHECK(SUCCEEDED(hr));
+
+ activate_ = pp_activate[i];
+ pp_activate[i] = nullptr;
+
+ // Print the friendly name.
+ base::win::ScopedCoMem<WCHAR> friendly_name;
+ UINT32 name_length;
+ activate_->GetAllocatedString(MFT_FRIENDLY_NAME_Attribute,
+ &friendly_name, &name_length);
+ DVLOG(3) << "Selected asynchronous hardware encoder's friendly name: "
+ << friendly_name;
+ } else {
+ DCHECK(FAILED(hr));
+
+ // The component that calls ActivateObject is
+ // responsible for calling ShutdownObject,
+ // https://docs.microsoft.com/en-us/windows/win32/api/mfobjects/nf-mfobjects-imfactivate-shutdownobject.
+ pp_activate[i]->ShutdownObject();
+ }
+ }
+ }
+
+ RETURN_ON_HR_FAILURE(hr, "Couldn't activate asynchronous hardware encoder",
+ false);
RETURN_ON_FAILURE((encoder_.Get() != nullptr),
- "No HW encoder instance created", false);
+ "No asynchronous hardware encoder instance created", false);
+
+ Microsoft::WRL::ComPtr<IMFAttributes> all_attributes;
+ hr = encoder_->GetAttributes(&all_attributes);
+ if (SUCCEEDED(hr)) {
+ // An asynchronous MFT must support dynamic format changes,
+ // https://docs.microsoft.com/en-us/windows/win32/medfound/asynchronous-mfts#format-changes.
+ UINT32 dynamic = FALSE;
+ hr = all_attributes->GetUINT32(MFT_SUPPORT_DYNAMIC_FORMAT_CHANGE, &dynamic);
+ if (!dynamic) {
+ DLOG(ERROR) << "Couldn't support dynamic format change.";
+ return false;
+ }
+
+ // Unlock the selected asynchronous MFTs,
+ // https://docs.microsoft.com/en-us/windows/win32/medfound/asynchronous-mfts#unlocking-asynchronous-mfts.
+ UINT32 async = FALSE;
+ hr = all_attributes->GetUINT32(MF_TRANSFORM_ASYNC, &async);
+ if (!async) {
+ DLOG(ERROR) << "MFT encoder is not asynchronous.";
+ return false;
+ }
+
+ hr = all_attributes->SetUINT32(MF_TRANSFORM_ASYNC_UNLOCK, TRUE);
+ RETURN_ON_HR_FAILURE(hr, "Couldn't unlock transform async", false);
+ }
+
return true;
}
-bool MediaFoundationVideoEncodeAccelerator::InitializeInputOutputSamples(
+bool MediaFoundationVideoEncodeAccelerator::InitializeInputOutputParameters(
VideoCodecProfile output_profile) {
DCHECK(main_client_task_runner_->BelongsToCurrentThread());
+ DCHECK(encoder_);
DWORD input_count = 0;
DWORD output_count = 0;
HRESULT hr = encoder_->GetStreamCount(&input_count, &output_count);
RETURN_ON_HR_FAILURE(hr, "Couldn't get stream count", false);
if (input_count < 1 || output_count < 1) {
- LOG(ERROR) << "Stream count too few: input " << input_count << ", output "
- << output_count;
+ DLOG(ERROR) << "Stream count too few: input " << input_count << ", output "
+ << output_count;
return false;
}
@@ -410,13 +549,13 @@ bool MediaFoundationVideoEncodeAccelerator::InitializeInputOutputSamples(
input_stream_id_ = 0;
output_stream_id_ = 0;
} else {
- LOG(ERROR) << "Couldn't find stream ids.";
+ DLOG(ERROR) << "Couldn't find stream ids from hardware encoder.";
return false;
}
// Initialize output parameters.
hr = MFCreateMediaType(&imf_output_media_type_);
- RETURN_ON_HR_FAILURE(hr, "Couldn't create media type", false);
+ RETURN_ON_HR_FAILURE(hr, "Couldn't create output media type", false);
hr = imf_output_media_type_->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
RETURN_ON_HR_FAILURE(hr, "Couldn't set media type", false);
hr = imf_output_media_type_->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_H264);
@@ -442,10 +581,10 @@ bool MediaFoundationVideoEncodeAccelerator::InitializeInputOutputSamples(
// Initialize input parameters.
hr = MFCreateMediaType(&imf_input_media_type_);
- RETURN_ON_HR_FAILURE(hr, "Couldn't create media type", false);
+ RETURN_ON_HR_FAILURE(hr, "Couldn't create input media type", false);
hr = imf_input_media_type_->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
RETURN_ON_HR_FAILURE(hr, "Couldn't set media type", false);
- hr = imf_input_media_type_->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_YV12);
+ hr = imf_input_media_type_->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_NV12);
RETURN_ON_HR_FAILURE(hr, "Couldn't set video format", false);
hr = MFSetAttributeRatio(imf_input_media_type_.Get(), MF_MT_FRAME_RATE,
frame_rate_, 1);
@@ -465,11 +604,11 @@ bool MediaFoundationVideoEncodeAccelerator::InitializeInputOutputSamples(
bool MediaFoundationVideoEncodeAccelerator::SetEncoderModes() {
DCHECK(main_client_task_runner_->BelongsToCurrentThread());
- RETURN_ON_FAILURE((encoder_.Get() != nullptr),
- "No HW encoder instance created", false);
+ DCHECK(encoder_);
HRESULT hr = encoder_.As(&codec_api_);
RETURN_ON_HR_FAILURE(hr, "Couldn't get ICodecAPI", false);
+
VARIANT var;
var.vt = VT_UI4;
var.ulVal = eAVEncCommonRateControlMode_CBR;
@@ -481,44 +620,42 @@ bool MediaFoundationVideoEncodeAccelerator::SetEncoderModes() {
// setting it on Windows 7 returns error.
RETURN_ON_HR_FAILURE(hr, "Couldn't set CommonRateControlMode", false);
}
+
+ if (is_async_mft_ && S_OK == codec_api_->IsModifiable(
+ &CODECAPI_AVEncVideoTemporalLayerCount)) {
+ var.ulVal = 1;
+ hr = codec_api_->SetValue(&CODECAPI_AVEncVideoTemporalLayerCount, &var);
+ if (!compatible_with_win7_) {
+ RETURN_ON_HR_FAILURE(hr, "Couldn't set temporal layer count", false);
+ }
+ }
+
var.ulVal = target_bitrate_;
hr = codec_api_->SetValue(&CODECAPI_AVEncCommonMeanBitRate, &var);
if (!compatible_with_win7_) {
RETURN_ON_HR_FAILURE(hr, "Couldn't set bitrate", false);
}
- var.ulVal = eAVEncAdaptiveMode_Resolution;
- hr = codec_api_->SetValue(&CODECAPI_AVEncAdaptiveMode, &var);
- if (!compatible_with_win7_) {
- RETURN_ON_HR_FAILURE(hr, "Couldn't set FrameRate", false);
- }
- var.vt = VT_BOOL;
- var.boolVal = VARIANT_TRUE;
- hr = codec_api_->SetValue(&CODECAPI_AVLowLatencyMode, &var);
- if (!compatible_with_win7_) {
- RETURN_ON_HR_FAILURE(hr, "Couldn't set LowLatencyMode", false);
- }
- return true;
-}
-
-bool MediaFoundationVideoEncodeAccelerator::IsResolutionSupported(
- const gfx::Size& resolution) {
- DCHECK(main_client_task_runner_->BelongsToCurrentThread());
- DCHECK(encoder_);
-
- HRESULT hr =
- MFSetAttributeSize(imf_output_media_type_.Get(), MF_MT_FRAME_SIZE,
- resolution.width(), resolution.height());
- RETURN_ON_HR_FAILURE(hr, "Couldn't set frame size", false);
- hr = encoder_->SetOutputType(output_stream_id_, imf_output_media_type_.Get(),
- 0);
- RETURN_ON_HR_FAILURE(hr, "Couldn't set output media type", false);
+ if (!is_async_mft_ ||
+ (is_async_mft_ &&
+ S_OK == codec_api_->IsModifiable(&CODECAPI_AVEncAdaptiveMode))) {
+ var.ulVal = eAVEncAdaptiveMode_Resolution;
+ hr = codec_api_->SetValue(&CODECAPI_AVEncAdaptiveMode, &var);
+ if (!compatible_with_win7_) {
+ RETURN_ON_HR_FAILURE(hr, "Couldn't set adaptive mode", false);
+ }
+ }
- hr = MFSetAttributeSize(imf_input_media_type_.Get(), MF_MT_FRAME_SIZE,
- resolution.width(), resolution.height());
- RETURN_ON_HR_FAILURE(hr, "Couldn't set frame size", false);
- hr = encoder_->SetInputType(input_stream_id_, imf_input_media_type_.Get(), 0);
- RETURN_ON_HR_FAILURE(hr, "Couldn't set input media type", false);
+ if (!is_async_mft_ ||
+ (is_async_mft_ &&
+ S_OK == codec_api_->IsModifiable(&CODECAPI_AVLowLatencyMode))) {
+ var.vt = VT_BOOL;
+ var.boolVal = VARIANT_TRUE;
+ hr = codec_api_->SetValue(&CODECAPI_AVLowLatencyMode, &var);
+ if (!compatible_with_win7_) {
+ RETURN_ON_HR_FAILURE(hr, "Couldn't set low latency mode", false);
+ }
+ }
return true;
}
@@ -538,30 +675,131 @@ void MediaFoundationVideoEncodeAccelerator::EncodeTask(
DVLOG(3) << __func__;
DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
+ if (is_async_mft_) {
+ AsyncEncodeTask(frame, force_keyframe);
+ } else {
+ SyncEncodeTask(frame, force_keyframe);
+ }
+}
+
+void MediaFoundationVideoEncodeAccelerator::AsyncEncodeTask(
+ scoped_refptr<VideoFrame> frame,
+ bool force_keyframe) {
+ bool input_delivered = false;
+ HRESULT hr = E_FAIL;
+ if (input_required_) {
+ // Hardware MFT is waiting for this coming input.
+ hr = ProcessInput(frame, force_keyframe);
+ if (FAILED(hr)) {
+ NotifyError(kPlatformFailureError);
+ RETURN_ON_HR_FAILURE(hr, "Couldn't encode", );
+ }
+
+ DVLOG(3) << "Sent for encode " << hr;
+ input_delivered = true;
+ input_required_ = false;
+ } else {
+ Microsoft::WRL::ComPtr<IMFMediaEvent> media_event;
+ hr = event_generator_->GetEvent(MF_EVENT_FLAG_NO_WAIT, &media_event);
+ if (FAILED(hr)) {
+ DLOG(WARNING) << "Abandoned input frame for video encoder.";
+ return;
+ }
+
+ MediaEventType event_type;
+ hr = media_event->GetType(&event_type);
+ if (FAILED(hr)) {
+ DLOG(ERROR) << "Failed to get the type of media event.";
+ return;
+ }
+
+ // Always deliver the current input into HMFT.
+ if (event_type == METransformNeedInput) {
+ hr = ProcessInput(frame, force_keyframe);
+ if (FAILED(hr)) {
+ NotifyError(kPlatformFailureError);
+ RETURN_ON_HR_FAILURE(hr, "Couldn't encode", );
+ }
+
+ DVLOG(3) << "Sent for encode " << hr;
+ input_delivered = true;
+ } else if (event_type == METransformHaveOutput) {
+ ProcessOutputAsync();
+ input_delivered =
+ TryToDeliverInputFrame(std::move(frame), force_keyframe);
+ }
+ }
+
+ if (!input_delivered) {
+ DLOG(ERROR) << "Failed to deliver input frame to video encoder";
+ return;
+ }
+
+ TryToReturnBitstreamBuffer();
+}
+
+void MediaFoundationVideoEncodeAccelerator::SyncEncodeTask(
+ scoped_refptr<VideoFrame> frame,
+ bool force_keyframe) {
+ HRESULT hr = E_FAIL;
+ hr = ProcessInput(frame, force_keyframe);
+
+ // According to MSDN, if encoder returns MF_E_NOTACCEPTING, we need to try
+ // processing the output. This error indicates that encoder does not accept
+ // any more input data.
+ if (hr == MF_E_NOTACCEPTING) {
+ DVLOG(3) << "MF_E_NOTACCEPTING";
+ ProcessOutputSync();
+ hr = encoder_->ProcessInput(input_stream_id_, input_sample_.Get(), 0);
+ if (FAILED(hr)) {
+ NotifyError(kPlatformFailureError);
+ RETURN_ON_HR_FAILURE(hr, "Couldn't encode", );
+ }
+ } else if (FAILED(hr)) {
+ NotifyError(kPlatformFailureError);
+ RETURN_ON_HR_FAILURE(hr, "Couldn't encode", );
+ }
+ DVLOG(3) << "Sent for encode " << hr;
+
+ ProcessOutputSync();
+}
+
+HRESULT MediaFoundationVideoEncodeAccelerator::ProcessInput(
+ scoped_refptr<VideoFrame> frame,
+ bool force_keyframe) {
+ DVLOG(3) << __func__;
+ DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
+ DCHECK_EQ(frame->format(), PIXEL_FORMAT_I420);
+
+ // Convert I420 to NV12 as input.
Microsoft::WRL::ComPtr<IMFMediaBuffer> input_buffer;
input_sample_->GetBufferByIndex(0, &input_buffer);
{
MediaBufferScopedPointer scoped_buffer(input_buffer.Get());
DCHECK(scoped_buffer.get());
- libyuv::I420Copy(frame->visible_data(VideoFrame::kYPlane),
- frame->stride(VideoFrame::kYPlane),
- frame->visible_data(VideoFrame::kVPlane),
- frame->stride(VideoFrame::kVPlane),
- frame->visible_data(VideoFrame::kUPlane),
- frame->stride(VideoFrame::kUPlane), scoped_buffer.get(),
- y_stride_, scoped_buffer.get() + u_plane_offset_,
- u_stride_, scoped_buffer.get() + v_plane_offset_,
- v_stride_, input_visible_size_.width(),
- input_visible_size_.height());
+ int dst_stride_y = frame->stride(VideoFrame::kYPlane);
+ uint8_t* dst_uv =
+ scoped_buffer.get() +
+ frame->stride(VideoFrame::kYPlane) * frame->rows(VideoFrame::kYPlane);
+ int dst_stride_uv = frame->stride(VideoFrame::kUPlane) * 2;
+ libyuv::I420ToNV12(frame->visible_data(VideoFrame::kYPlane),
+ frame->stride(VideoFrame::kYPlane),
+ frame->visible_data(VideoFrame::kUPlane),
+ frame->stride(VideoFrame::kUPlane),
+ frame->visible_data(VideoFrame::kVPlane),
+ frame->stride(VideoFrame::kVPlane), scoped_buffer.get(),
+ dst_stride_y, dst_uv, dst_stride_uv,
+ input_visible_size_.width(),
+ input_visible_size_.height());
}
input_sample_->SetSampleTime(frame->timestamp().InMicroseconds() *
kOneMicrosecondInMFSampleTimeUnits);
- UINT64 sample_duration = 1;
+ UINT64 sample_duration = 0;
HRESULT hr =
MFFrameRateToAverageTimePerFrame(frame_rate_, 1, &sample_duration);
- RETURN_ON_HR_FAILURE(hr, "Couldn't calculate sample duration", );
+ RETURN_ON_HR_FAILURE(hr, "Couldn't calculate sample duration", E_FAIL);
input_sample_->SetSampleDuration(sample_duration);
// Release frame after input is copied.
@@ -572,34 +810,100 @@ void MediaFoundationVideoEncodeAccelerator::EncodeTask(
var.vt = VT_UI4;
var.ulVal = 1;
hr = codec_api_->SetValue(&CODECAPI_AVEncVideoForceKeyFrame, &var);
- if (!compatible_with_win7_ && !SUCCEEDED(hr)) {
+ if (!compatible_with_win7_ && FAILED(hr)) {
LOG(WARNING) << "Failed to set CODECAPI_AVEncVideoForceKeyFrame, "
"HRESULT: 0x" << std::hex << hr;
}
}
- hr = encoder_->ProcessInput(input_stream_id_, input_sample_.Get(), 0);
- // According to MSDN, if encoder returns MF_E_NOTACCEPTING, we need to try
- // processing the output. This error indicates that encoder does not accept
- // any more input data.
- if (hr == MF_E_NOTACCEPTING) {
- DVLOG(3) << "MF_E_NOTACCEPTING";
- ProcessOutput();
- hr = encoder_->ProcessInput(input_stream_id_, input_sample_.Get(), 0);
- if (!SUCCEEDED(hr)) {
- NotifyError(kPlatformFailureError);
- RETURN_ON_HR_FAILURE(hr, "Couldn't encode", );
+ return encoder_->ProcessInput(input_stream_id_, input_sample_.Get(), 0);
+}
+
+void MediaFoundationVideoEncodeAccelerator::ProcessOutputAsync() {
+ DVLOG(3) << __func__;
+ DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
+
+ MFT_OUTPUT_DATA_BUFFER output_data_buffer = {0};
+ output_data_buffer.dwStreamID = output_stream_id_;
+ output_data_buffer.dwStatus = 0;
+ output_data_buffer.pEvents = nullptr;
+ output_data_buffer.pSample = nullptr;
+ DWORD status = 0;
+ HRESULT hr = encoder_->ProcessOutput(0, 1, &output_data_buffer, &status);
+ if (hr == MF_E_TRANSFORM_STREAM_CHANGE) {
+ hr = S_OK;
+ Microsoft::WRL::ComPtr<IMFMediaType> media_type;
+ for (DWORD type_index = 0; SUCCEEDED(hr); ++type_index) {
+ hr = encoder_->GetOutputAvailableType(output_stream_id_, type_index,
+ &media_type);
+ if (SUCCEEDED(hr)) {
+ break;
+ }
}
- } else if (!SUCCEEDED(hr)) {
- NotifyError(kPlatformFailureError);
- RETURN_ON_HR_FAILURE(hr, "Couldn't encode", );
+ hr = encoder_->SetOutputType(output_stream_id_, media_type.Get(), 0);
+ return;
}
- DVLOG(3) << "Sent for encode " << hr;
- ProcessOutput();
+ RETURN_ON_HR_FAILURE(hr, "Couldn't get encoded data", );
+ DVLOG(3) << "Got encoded data " << hr;
+
+ Microsoft::WRL::ComPtr<IMFMediaBuffer> output_buffer;
+ hr = output_data_buffer.pSample->GetBufferByIndex(0, &output_buffer);
+ RETURN_ON_HR_FAILURE(hr, "Couldn't get buffer by index", );
+
+ DWORD size = 0;
+ hr = output_buffer->GetCurrentLength(&size);
+ RETURN_ON_HR_FAILURE(hr, "Couldn't get buffer length", );
+
+ base::TimeDelta timestamp;
+ LONGLONG sample_time;
+ hr = output_data_buffer.pSample->GetSampleTime(&sample_time);
+ if (SUCCEEDED(hr)) {
+ timestamp = base::TimeDelta::FromMicroseconds(
+ sample_time / kOneMicrosecondInMFSampleTimeUnits);
+ }
+
+ const bool keyframe = MFGetAttributeUINT32(
+ output_data_buffer.pSample, MFSampleExtension_CleanPoint, false);
+ DVLOG(3) << "Encoded data with size:" << size << " keyframe " << keyframe;
+
+ // If no bit stream buffer presents, queue the output first.
+ if (bitstream_buffer_queue_.empty()) {
+ DVLOG(3) << "No bitstream buffers.";
+ // We need to copy the output so that encoding can continue.
+ std::unique_ptr<EncodeOutput> encode_output(
+ new EncodeOutput(size, keyframe, timestamp));
+ {
+ MediaBufferScopedPointer scoped_buffer(output_buffer.Get());
+ memcpy(encode_output->memory(), scoped_buffer.get(), size);
+ }
+ encoder_output_queue_.push_back(std::move(encode_output));
+ output_data_buffer.pSample->Release();
+ output_data_buffer.pSample = nullptr;
+ return;
+ }
+
+ // Immediately return encoded buffer with BitstreamBuffer to client.
+ std::unique_ptr<MediaFoundationVideoEncodeAccelerator::BitstreamBufferRef>
+ buffer_ref = std::move(bitstream_buffer_queue_.front());
+ bitstream_buffer_queue_.pop_front();
+
+ {
+ MediaBufferScopedPointer scoped_buffer(output_buffer.Get());
+ memcpy(buffer_ref->mapping.memory(), scoped_buffer.get(), size);
+ }
+
+ output_data_buffer.pSample->Release();
+ output_data_buffer.pSample = nullptr;
+
+ main_client_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&Client::BitstreamBufferReady, main_client_,
+ buffer_ref->id,
+ BitstreamBufferMetadata(size, keyframe, timestamp)));
}
-void MediaFoundationVideoEncodeAccelerator::ProcessOutput() {
+void MediaFoundationVideoEncodeAccelerator::ProcessOutputSync() {
DVLOG(3) << __func__;
DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
@@ -676,7 +980,87 @@ void MediaFoundationVideoEncodeAccelerator::ProcessOutput() {
// Keep calling ProcessOutput recursively until MF_E_TRANSFORM_NEED_MORE_INPUT
// is returned to flush out all the output.
- ProcessOutput();
+ ProcessOutputSync();
+}
+
+bool MediaFoundationVideoEncodeAccelerator::TryToDeliverInputFrame(
+ scoped_refptr<VideoFrame> frame,
+ bool force_keyframe) {
+ bool input_delivered = false;
+ Microsoft::WRL::ComPtr<IMFMediaEvent> media_event;
+ MediaEventType event_type;
+ do {
+ HRESULT hr =
+ event_generator_->GetEvent(MF_EVENT_FLAG_NO_WAIT, &media_event);
+ if (FAILED(hr)) {
+ break;
+ }
+
+ hr = media_event->GetType(&event_type);
+ if (FAILED(hr)) {
+ DLOG(ERROR) << "Failed to get the type of media event.";
+ break;
+ }
+
+ switch (event_type) {
+ case METransformHaveOutput: {
+ ProcessOutputAsync();
+ continue;
+ }
+ case METransformNeedInput: {
+ hr = ProcessInput(frame, force_keyframe);
+ if (FAILED(hr)) {
+ NotifyError(kPlatformFailureError);
+ RETURN_ON_HR_FAILURE(hr, "Couldn't encode", false);
+ }
+
+ DVLOG(3) << "Sent for encode " << hr;
+ return true;
+ }
+ default:
+ break;
+ }
+ } while (true);
+
+ return input_delivered;
+}
+
+void MediaFoundationVideoEncodeAccelerator::TryToReturnBitstreamBuffer() {
+ // Try to fetch the encoded frame in time.
+ bool output_processed = false;
+ do {
+ Microsoft::WRL::ComPtr<IMFMediaEvent> media_event;
+ MediaEventType event_type;
+ HRESULT hr =
+ event_generator_->GetEvent(MF_EVENT_FLAG_NO_WAIT, &media_event);
+ if (FAILED(hr)) {
+ if (!output_processed) {
+ continue;
+ } else {
+ break;
+ }
+ }
+
+ hr = media_event->GetType(&event_type);
+ if (FAILED(hr)) {
+ DLOG(ERROR) << "Failed to get the type of media event.";
+ break;
+ }
+
+ switch (event_type) {
+ case METransformHaveOutput: {
+ ProcessOutputAsync();
+ output_processed = true;
+ break;
+ }
+ case METransformNeedInput: {
+ input_required_ = true;
+ continue;
+ }
+ default:
+ break;
+ }
+ } while (true);
}
void MediaFoundationVideoEncodeAccelerator::UseOutputBitstreamBufferTask(
@@ -689,31 +1073,22 @@ void MediaFoundationVideoEncodeAccelerator::UseOutputBitstreamBufferTask(
std::unique_ptr<MediaFoundationVideoEncodeAccelerator::EncodeOutput>
encode_output = std::move(encoder_output_queue_.front());
encoder_output_queue_.pop_front();
- ReturnBitstreamBuffer(std::move(encode_output), std::move(buffer_ref));
+ memcpy(buffer_ref->mapping.memory(), encode_output->memory(),
+ encode_output->size());
+
+ main_client_task_runner_->PostTask(
+ FROM_HERE,
+ base::BindOnce(&Client::BitstreamBufferReady, main_client_,
+ buffer_ref->id,
+ BitstreamBufferMetadata(
+ encode_output->size(), encode_output->keyframe,
+ encode_output->capture_timestamp)));
return;
}
bitstream_buffer_queue_.push_back(std::move(buffer_ref));
}
-void MediaFoundationVideoEncodeAccelerator::ReturnBitstreamBuffer(
- std::unique_ptr<EncodeOutput> encode_output,
- std::unique_ptr<MediaFoundationVideoEncodeAccelerator::BitstreamBufferRef>
- buffer_ref) {
- DVLOG(3) << __func__;
- DCHECK(encoder_thread_task_runner_->BelongsToCurrentThread());
-
- memcpy(buffer_ref->mapping.memory(), encode_output->memory(),
- encode_output->size());
- main_client_task_runner_->PostTask(
- FROM_HERE,
- base::BindOnce(&Client::BitstreamBufferReady, main_client_,
- buffer_ref->id,
- BitstreamBufferMetadata(
- encode_output->size(), encode_output->keyframe,
- encode_output->capture_timestamp)));
-}
-
void MediaFoundationVideoEncodeAccelerator::RequestEncodingParametersChangeTask(
uint32_t bitrate,
uint32_t framerate) {
@@ -732,7 +1107,7 @@ void MediaFoundationVideoEncodeAccelerator::RequestEncodingParametersChangeTask(
var.ulVal = target_bitrate_;
HRESULT hr = codec_api_->SetValue(&CODECAPI_AVEncCommonMeanBitRate, &var);
if (!compatible_with_win7_) {
- RETURN_ON_HR_FAILURE(hr, "Couldn't set bitrate", );
+ RETURN_ON_HR_FAILURE(hr, "Couldn't update bitrate", );
}
}
}
@@ -748,12 +1123,23 @@ void MediaFoundationVideoEncodeAccelerator::DestroyTask() {
}
void MediaFoundationVideoEncodeAccelerator::ReleaseEncoderResources() {
+ while (!bitstream_buffer_queue_.empty())
+ bitstream_buffer_queue_.pop_front();
+ while (!encoder_output_queue_.empty())
+ encoder_output_queue_.pop_front();
+
+ if (activate_.Get() != nullptr) {
+ activate_->ShutdownObject();
+ activate_->Release();
+ activate_.Reset();
+ }
encoder_.Reset();
codec_api_.Reset();
+ event_generator_.Reset();
imf_input_media_type_.Reset();
imf_output_media_type_.Reset();
input_sample_.Reset();
output_sample_.Reset();
}
-} // namespace content
+} // namespace media
diff --git a/chromium/media/gpu/windows/media_foundation_video_encode_accelerator_win.h b/chromium/media/gpu/windows/media_foundation_video_encode_accelerator_win.h
index e7f5cff666e..274b64e9e88 100644
--- a/chromium/media/gpu/windows/media_foundation_video_encode_accelerator_win.h
+++ b/chromium/media/gpu/windows/media_foundation_video_encode_accelerator_win.h
@@ -37,7 +37,8 @@ class MEDIA_GPU_EXPORT MediaFoundationVideoEncodeAccelerator
// If |compatible_with_win7| is true, MediaFoundationVideoEncoderAccelerator
// works on Windows 7. Some attributes of the encoder are not supported on old
// systems, which may impact the performance or quality of the output.
- explicit MediaFoundationVideoEncodeAccelerator(bool compatible_with_win7);
+ explicit MediaFoundationVideoEncodeAccelerator(bool compatible_with_win7,
+ bool enable_async_mft);
// VideoEncodeAccelerator implementation.
VideoEncodeAccelerator::SupportedProfiles GetSupportedProfiles() override;
@@ -48,7 +49,7 @@ class MEDIA_GPU_EXPORT MediaFoundationVideoEncodeAccelerator
uint32_t framerate) override;
void Destroy() override;
- // Preload dlls required for encoding. Returns true if all required dlls are
+ // Preloads dlls required for encoding. Returns true if all required dlls are
// correctly loaded.
static bool PreSandboxInitialization();
@@ -62,40 +63,46 @@ class MEDIA_GPU_EXPORT MediaFoundationVideoEncodeAccelerator
// Holds output buffers coming from the encoder.
class EncodeOutput;
- // Creates an hardware encoder backed IMFTransform instance on |encoder_|.
- bool CreateHardwareEncoderMFT();
+ // Enumerates all hardware encoder backed IMFTransform instances.
+ uint32_t EnumerateHardwareEncoders(IMFActivate*** pp_activate);
- // Initializes and allocates memory for input and output samples.
- bool InitializeInputOutputSamples(VideoCodecProfile output_profile);
+ // Activates the asynchronous encoder instance |encoder_| according to codec
+ // merit.
+ bool ActivateAsyncEncoder(IMFActivate** pp_activate, uint32_t activate_count);
+
+ // Initializes and allocates memory for input and output parameters.
+ bool InitializeInputOutputParameters(VideoCodecProfile output_profile);
// Initializes encoder parameters for real-time use.
bool SetEncoderModes();
- // Returns true if we can initialize input and output samples with the given
- // frame size, otherwise false.
- bool IsResolutionSupported(const gfx::Size& size);
-
// Helper function to notify the client of an error on
// |main_client_task_runner_|.
void NotifyError(VideoEncodeAccelerator::Error error);
// Encoding tasks to be run on |encoder_thread_|.
void EncodeTask(scoped_refptr<VideoFrame> frame, bool force_keyframe);
+ void AsyncEncodeTask(scoped_refptr<VideoFrame> frame, bool force_keyframe);
+ void SyncEncodeTask(scoped_refptr<VideoFrame> frame, bool force_keyframe);
+
+ // Processes the input video frame for the encoder.
+ HRESULT ProcessInput(scoped_refptr<VideoFrame> frame, bool force_keyframe);
// Checks for and copies encoded output on |encoder_thread_|.
- void ProcessOutput();
+ void ProcessOutputAsync();
+ void ProcessOutputSync();
+
+ // Tries to deliver the input frame to the encoder.
+ bool TryToDeliverInputFrame(scoped_refptr<VideoFrame> frame,
+ bool force_keyframe);
+
+ // Tries to return a bitstream buffer to the client.
+ void TryToReturnBitstreamBuffer();
// Inserts the output buffers for reuse on |encoder_thread_|.
void UseOutputBitstreamBufferTask(
std::unique_ptr<BitstreamBufferRef> buffer_ref);
- // Copies EncodeOutput into a BitstreamBuffer and returns it to the
- // |main_client_|.
- void ReturnBitstreamBuffer(
- std::unique_ptr<EncodeOutput> encode_output,
- std::unique_ptr<MediaFoundationVideoEncodeAccelerator::BitstreamBufferRef>
- buffer_ref);
-
// Changes encode parameters on |encoder_thread_|.
void RequestEncodingParametersChangeTask(uint32_t bitrate,
uint32_t framerate);
@@ -108,6 +115,12 @@ class MEDIA_GPU_EXPORT MediaFoundationVideoEncodeAccelerator
const bool compatible_with_win7_;
+ // Flag to enable the usage of MFTEnumEx.
+ const bool enable_async_mft_;
+
+ // Whether asynchronous hardware encoder enabled or not.
+ bool is_async_mft_;
+
// Bitstream buffers ready to be used to return encoded output as a FIFO.
base::circular_deque<std::unique_ptr<BitstreamBufferRef>>
bitstream_buffer_queue_;
@@ -119,14 +132,11 @@ class MEDIA_GPU_EXPORT MediaFoundationVideoEncodeAccelerator
size_t bitstream_buffer_size_;
uint32_t frame_rate_;
uint32_t target_bitrate_;
- size_t u_plane_offset_;
- size_t v_plane_offset_;
- size_t y_stride_;
- size_t u_stride_;
- size_t v_stride_;
+ Microsoft::WRL::ComPtr<IMFActivate> activate_;
Microsoft::WRL::ComPtr<IMFTransform> encoder_;
Microsoft::WRL::ComPtr<ICodecAPI> codec_api_;
+ Microsoft::WRL::ComPtr<IMFMediaEventGenerator> event_generator_;
DWORD input_stream_id_;
DWORD output_stream_id_;
@@ -134,6 +144,7 @@ class MEDIA_GPU_EXPORT MediaFoundationVideoEncodeAccelerator
Microsoft::WRL::ComPtr<IMFMediaType> imf_input_media_type_;
Microsoft::WRL::ComPtr<IMFMediaType> imf_output_media_type_;
+ bool input_required_;
Microsoft::WRL::ComPtr<IMFSample> input_sample_;
Microsoft::WRL::ComPtr<IMFSample> output_sample_;
diff --git a/chromium/media/learning/common/media_learning_tasks.cc b/chromium/media/learning/common/media_learning_tasks.cc
index 4af7bc2e3b1..e579028e441 100644
--- a/chromium/media/learning/common/media_learning_tasks.cc
+++ b/chromium/media/learning/common/media_learning_tasks.cc
@@ -4,6 +4,8 @@
#include "media/learning/common/media_learning_tasks.h"
+#include "base/notreached.h"
+
namespace media {
namespace learning {
diff --git a/chromium/media/learning/common/value.cc b/chromium/media/learning/common/value.cc
index 757a8b3d33d..b3fedfb7f68 100644
--- a/chromium/media/learning/common/value.cc
+++ b/chromium/media/learning/common/value.cc
@@ -4,6 +4,8 @@
#include "media/learning/common/value.h"
+#include <cstring>
+
#include "base/hash/hash.h"
namespace media {
diff --git a/chromium/media/learning/impl/extra_trees_trainer.cc b/chromium/media/learning/impl/extra_trees_trainer.cc
index b820c6a64d8..572c0f1f56f 100644
--- a/chromium/media/learning/impl/extra_trees_trainer.cc
+++ b/chromium/media/learning/impl/extra_trees_trainer.cc
@@ -7,7 +7,7 @@
#include <set>
#include "base/bind.h"
-#include "base/logging.h"
+#include "base/check_op.h"
#include "media/learning/impl/voting_ensemble.h"
namespace media {
diff --git a/chromium/media/learning/impl/learning_session_impl.cc b/chromium/media/learning/impl/learning_session_impl.cc
index 39fe3da2a20..2680a881dd4 100644
--- a/chromium/media/learning/impl/learning_session_impl.cc
+++ b/chromium/media/learning/impl/learning_session_impl.cc
@@ -8,7 +8,7 @@
#include <utility>
#include "base/bind.h"
-#include "base/logging.h"
+#include "base/check.h"
#include "media/learning/impl/distribution_reporter.h"
#include "media/learning/impl/learning_task_controller_impl.h"
diff --git a/chromium/media/learning/impl/learning_task_controller_impl.cc b/chromium/media/learning/impl/learning_task_controller_impl.cc
index 1ec060c00c4..45c812d2489 100644
--- a/chromium/media/learning/impl/learning_task_controller_impl.cc
+++ b/chromium/media/learning/impl/learning_task_controller_impl.cc
@@ -9,6 +9,8 @@
#include <vector>
#include "base/bind.h"
+#include "base/check_op.h"
+#include "base/notreached.h"
#include "media/learning/impl/distribution_reporter.h"
#include "media/learning/impl/extra_trees_trainer.h"
#include "media/learning/impl/lookup_table_trainer.h"
diff --git a/chromium/media/learning/impl/lookup_table_trainer.cc b/chromium/media/learning/impl/lookup_table_trainer.cc
index 57ebdbbc0f6..f858e68756e 100644
--- a/chromium/media/learning/impl/lookup_table_trainer.cc
+++ b/chromium/media/learning/impl/lookup_table_trainer.cc
@@ -6,7 +6,6 @@
#include <map>
-#include "base/logging.h"
namespace media {
namespace learning {
diff --git a/chromium/media/learning/impl/model.h b/chromium/media/learning/impl/model.h
index 361cb241c53..673b61e4c43 100644
--- a/chromium/media/learning/impl/model.h
+++ b/chromium/media/learning/impl/model.h
@@ -5,6 +5,7 @@
#ifndef MEDIA_LEARNING_IMPL_MODEL_H_
#define MEDIA_LEARNING_IMPL_MODEL_H_
+#include "base/callback.h"
#include "base/component_export.h"
#include "media/learning/common/labelled_example.h"
#include "media/learning/common/target_histogram.h"
diff --git a/chromium/media/learning/impl/random_number_generator.cc b/chromium/media/learning/impl/random_number_generator.cc
index fb503bf2d52..6fcff2f5ebc 100644
--- a/chromium/media/learning/impl/random_number_generator.cc
+++ b/chromium/media/learning/impl/random_number_generator.cc
@@ -6,7 +6,6 @@
#include <limits>
-#include "base/logging.h"
#include "base/rand_util.h"
namespace media {
diff --git a/chromium/media/learning/impl/random_tree_trainer.cc b/chromium/media/learning/impl/random_tree_trainer.cc
index c45b757716e..b75deafec04 100644
--- a/chromium/media/learning/impl/random_tree_trainer.cc
+++ b/chromium/media/learning/impl/random_tree_trainer.cc
@@ -7,7 +7,7 @@
#include <math.h>
#include "base/bind.h"
-#include "base/logging.h"
+#include "base/check_op.h"
#include "base/optional.h"
#include "base/threading/sequenced_task_runner_handle.h"
diff --git a/chromium/media/media_options.gni b/chromium/media/media_options.gni
index 1d62f7f714a..ef8fbe4de4a 100644
--- a/chromium/media/media_options.gni
+++ b/chromium/media/media_options.gni
@@ -29,6 +29,7 @@ media_subcomponent_deps = [
"//media/muxers",
"//media/renderers",
"//media/video",
+ "//media/webcodecs",
]
if (is_fuchsia) {
@@ -158,20 +159,6 @@ enable_library_cdms =
(is_linux && !is_chromecast) || is_mac || is_win || is_fuchsia
declare_args() {
- # Experiment to enable mojo media services (e.g. "renderer", "cdm", see
- # |mojo_media_services|). When enabled, selected mojo paths will be enabled in
- # the media pipeline and corresponding services will hosted in the selected
- # remote process (e.g. "utility" process, see |mojo_media_host|).
- # This is explicitly disabled for Fuchsia.
- enable_mojo_media =
- !is_fuchsia &&
- (is_android || is_chromeos || is_mac || is_win || enable_library_cdms ||
- (is_desktop_linux && use_vaapi) || is_chromecast)
-
- # Enable the TestMojoMediaClient to be used in mojo MediaService. This is for
- # testing only and will override the default platform MojoMediaClient, if any.
- enable_test_mojo_media_client = false
-
# When enabled, this feature allows developers to use a runtime flag to
# choose the implementation of the renderer that is used. On a build which
# enables the mojo renderer, if --disable-mojo-renderer is passed at start-up,
@@ -195,65 +182,43 @@ declare_args() {
# (which takes precedence), or by setting |alternate_cdm_storage_id_key|.
# The key must be a string of at least 32 characters.
alternate_cdm_storage_id_key = ""
-
- # This feature is still under development. See crbug.com/641132.
- enable_cdm_proxy = false
}
-# This feature can only be enabled when using Mojo media. Furthermore, the
-# "renderer" service must be enabled, which is asserted in
-# //media/mojo/services/BUILD.gn
-assert(
- !enable_runtime_media_renderer_selection || enable_mojo_media,
- "Runtime media renderer selection only applies when mojo media is enabled.")
-
assert(!enable_cdm_host_verification || is_mac || is_win,
"CDM host verification is only supported on Mac and Windows.")
-assert(enable_library_cdms || !enable_cdm_proxy,
- "CDM Proxy should only be enabled when library CDM is enabled.")
-
+# Default |mojo_media_services| and |mojo_media_host| on various platforms. See
+# comments below for valid values. Can be overridden by gn build arguments from
+# the --args command line flag.
_default_mojo_media_services = []
-_default_mojo_media_host = "none"
-
-# Default mojo_media_services and mojo_media_host on various platforms.
-# Can be overridden by gn build arguments from the --args command line flag
-# for local testing.
-if (enable_mojo_media) {
- if (is_chromecast) {
- _default_mojo_media_services = cast_mojo_media_services
- _default_mojo_media_host = cast_mojo_media_host
- } else if (is_android) {
- _default_mojo_media_services = [
- "cdm",
- "audio_decoder",
- "video_decoder",
- ]
- _default_mojo_media_host = "gpu"
- } else if (is_chromeos || is_mac || is_win ||
- (is_desktop_linux && use_vaapi)) {
- _default_mojo_media_services = [ "video_decoder" ]
- _default_mojo_media_host = "gpu"
- }
-
- if (enable_library_cdms) {
- _default_mojo_media_services += [ "cdm" ]
+_default_mojo_media_host = ""
+
+if (is_chromecast) {
+ _default_mojo_media_services = cast_mojo_media_services
+ _default_mojo_media_host = cast_mojo_media_host
+} else if (is_android) {
+ _default_mojo_media_services = [
+ "cdm",
+ "audio_decoder",
+ "video_decoder",
+ ]
+ _default_mojo_media_host = "gpu"
+} else if (is_chromeos || is_mac || is_win || (is_desktop_linux && use_vaapi)) {
+ _default_mojo_media_services = [ "video_decoder" ]
+ _default_mojo_media_host = "gpu"
+}
- # Having a CDM running means it could require a CdmProxy running in the GPU
- # process.
- assert(
- _default_mojo_media_host == "none" || _default_mojo_media_host == "gpu",
- "For now, mojo_media_host should not overwrite it with a different " +
- "value if it has been set.")
- _default_mojo_media_host = "gpu"
- }
+# When |enable_library_cdms| is true, the "cdm" service will run in a separate
+# CdmService in the CDM (utility) process. Therefor there's no need to specify
+# |_default_mojo_media_host| which controls where the MediaService runs in.
+# On Fuchsia, |enable_library_cdms| is only enabled to build libclearkeycdm.so,
+# the mojo CDM service is not used.
+if (enable_library_cdms && !is_fuchsia) {
+ _default_mojo_media_services += [ "cdm" ]
}
-# Use another declare_args() to pick up possible overrides of enable_mojo_media
-# from --args command line flags. See "gn help declare_args".
declare_args() {
# A list of mojo media services that should be used in the media pipeline.
- # Must not be empty if |enable_mojo_media| is true.
# Valid entries in the list are:
# - "renderer": Use mojo-based media Renderer service.
# - "cdm": Use mojo-based Content Decryption Module.
@@ -270,10 +235,10 @@ declare_args() {
# |mojo_media_services| still run in the MediaService in the process specified
# by "mojo_media_host".
# Valid options are:
- # - "none": Do not use mojo media service.
# - "browser": Use mojo media service hosted in the browser process.
# - "gpu": Use mojo media service hosted in the gpu process.
# - "utility": Use mojo media service hosted in the utility process.
+ # - "": Do not use mojo media service.
mojo_media_host = _default_mojo_media_host
}
diff --git a/chromium/media/midi/message_util.cc b/chromium/media/midi/message_util.cc
index 539d62dcd3c..350932e9f23 100644
--- a/chromium/media/midi/message_util.cc
+++ b/chromium/media/midi/message_util.cc
@@ -4,8 +4,8 @@
#include "media/midi/message_util.h"
-#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
+#include "base/notreached.h"
namespace midi {
diff --git a/chromium/media/midi/midi_manager_mac_unittest.cc b/chromium/media/midi/midi_manager_mac_unittest.cc
index d3f54ef9e53..30771290c4f 100644
--- a/chromium/media/midi/midi_manager_mac_unittest.cc
+++ b/chromium/media/midi/midi_manager_mac_unittest.cc
@@ -10,7 +10,6 @@
#include <memory>
-#include "base/logging.h"
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/synchronization/lock.h"
diff --git a/chromium/media/midi/midi_manager_unittest.cc b/chromium/media/midi/midi_manager_unittest.cc
index 4a3e4abb6e2..b629c7df468 100644
--- a/chromium/media/midi/midi_manager_unittest.cc
+++ b/chromium/media/midi/midi_manager_unittest.cc
@@ -11,7 +11,7 @@
#include <vector>
#include "base/bind.h"
-#include "base/logging.h"
+#include "base/check_op.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/run_loop.h"
diff --git a/chromium/media/midi/midi_manager_usb.cc b/chromium/media/midi/midi_manager_usb.cc
index d57f374d8b0..b3bce71d379 100644
--- a/chromium/media/midi/midi_manager_usb.cc
+++ b/chromium/media/midi/midi_manager_usb.cc
@@ -8,7 +8,7 @@
#include <utility>
#include "base/bind.h"
-#include "base/logging.h"
+#include "base/check_op.h"
#include "base/strings/stringprintf.h"
#include "media/midi/midi_service.h"
#include "media/midi/task_service.h"
diff --git a/chromium/media/midi/midi_message_queue.cc b/chromium/media/midi/midi_message_queue.cc
index 08501ec772c..1beb682c1cb 100644
--- a/chromium/media/midi/midi_message_queue.cc
+++ b/chromium/media/midi/midi_message_queue.cc
@@ -6,7 +6,8 @@
#include <algorithm>
-#include "base/logging.h"
+#include "base/check_op.h"
+#include "base/notreached.h"
#include "media/midi/message_util.h"
namespace midi {
diff --git a/chromium/media/mojo/BUILD.gn b/chromium/media/mojo/BUILD.gn
index 4401902c5b4..7005cfbbb78 100644
--- a/chromium/media/mojo/BUILD.gn
+++ b/chromium/media/mojo/BUILD.gn
@@ -18,41 +18,32 @@ buildflag_header("buildflags") {
enable_mojo_media_in_gpu_process = false
enable_mojo_media_in_utility_process = false
- if (!enable_mojo_media) {
- assert(mojo_media_services == [], "Mojo media is not enabled")
- assert(mojo_media_host == "none", "Mojo media is not enabled")
- assert(!enable_test_mojo_media_client, "Mojo media is not enabled")
- } else {
- assert(mojo_media_services != [], "No mojo media service specified")
- foreach(service, mojo_media_services) {
- if (service == "renderer") {
- enable_mojo_renderer = true
- } else if (service == "cdm") {
- enable_mojo_cdm = true
- } else if (service == "audio_decoder") {
- enable_mojo_audio_decoder = true
- } else if (service == "video_decoder") {
- enable_mojo_video_decoder = true
- } else {
- assert(false, "Invalid mojo media service: $service")
- }
- }
-
- if (mojo_media_host == "browser") {
- enable_mojo_media_in_browser_process = true
- } else if (mojo_media_host == "gpu") {
- enable_mojo_media_in_gpu_process = true
- } else if (mojo_media_host == "utility") {
- enable_mojo_media_in_utility_process = true
+ foreach(service, mojo_media_services) {
+ if (service == "renderer") {
+ enable_mojo_renderer = true
+ } else if (service == "cdm") {
+ enable_mojo_cdm = true
+ } else if (service == "audio_decoder") {
+ enable_mojo_audio_decoder = true
+ } else if (service == "video_decoder") {
+ enable_mojo_video_decoder = true
} else {
- assert(false, "Invalid mojo media host: $mojo_media_host")
+ assert(false, "Invalid mojo media service: $service")
}
}
+ if (mojo_media_host == "browser") {
+ enable_mojo_media_in_browser_process = true
+ } else if (mojo_media_host == "gpu") {
+ enable_mojo_media_in_gpu_process = true
+ } else if (mojo_media_host == "utility") {
+ enable_mojo_media_in_utility_process = true
+ } else if (mojo_media_host != "") {
+ assert(false, "Invalid mojo media host: $mojo_media_host")
+ }
+
flags = [
"ENABLE_CAST_RENDERER=$enable_cast_renderer",
- "ENABLE_MOJO_MEDIA=$enable_mojo_media",
- "ENABLE_TEST_MOJO_MEDIA_CLIENT=$enable_test_mojo_media_client",
"ENABLE_MOJO_RENDERER=$enable_mojo_renderer",
"ENABLE_MOJO_CDM=$enable_mojo_cdm",
"ENABLE_MOJO_AUDIO_DECODER=$enable_mojo_audio_decoder",
diff --git a/chromium/media/mojo/README.md b/chromium/media/mojo/README.md
index 8ed718d7198..d13dc2c607e 100644
--- a/chromium/media/mojo/README.md
+++ b/chromium/media/mojo/README.md
@@ -62,10 +62,8 @@ specify which remote media components you want to enable. For example, with the
following gn arguments, the media pipeline will enable `MojoRenderer` and
`MojoCdm`:
```
-enable_mojo_media = true
mojo_media_services = ["renderer", "cdm"]
```
-Note that you must set `enable_mojo_media` first.
### Media Mojo Interface Factory
@@ -320,11 +318,6 @@ local media components get services from content layer through the `MediaClient`
interface. In `MediaService` and `CdmService`, remote media components get
services from the through **secure auxiliary services**.
-Note that as a `service_manager::Service`, `MediaService` and `CdmService` can
-always connect to other `service_manager::Service` hosted by the service_manager
-through the `Connector` interface. However, these are generic services that
-doesn’t belong to any individual `RenderFrame`, or even user profile.
-
Some services do require `RenderFrame` or user profile identity, e.g. file
system. Since media components all belong to a given `RenderFrame`, we must
maintain the frame identity when accessing these services for security reasons.
@@ -332,10 +325,12 @@ These services are called secure auxiliary services. `FrameServiceBase` is a
base class for all secure auxiliary services to help manage the lifetime of
these services (e.g. to handle navigation).
-In `MediaInterfaceProxy`, when we request `media::mojom::InterfaceFactory` in
-the `MediaService` or `CdmService`, we call `GetFrameServices()` to configure
-which secure auxiliary services are exposed to the remote components over the
-separate `blink::mojom::BrowserInterfaceBroker`.
+When a `MediaInterfaceProxy` is created, in addition to providing the
+`media::mojom::InterfaceFactory`, the `RenderFrame` is provisioned with a
+`media::mojom::FrameInterfaceFactory` that exposes these secure auxiliary
+services on a per-frame basis. The `FrameInterfaceFactory` directly provides
+services from //content, and it provides a way for //content embedders to
+register additional auxiliary services via the `BindEmbedderReceiver()` method.
Currently only the remote CDM needs secure auxiliary services. This is a list of
currently supported services:
@@ -344,7 +339,6 @@ currently supported services:
* `PlatformVerification`: to check whether the platform is secure
* `CdmFileIO`: for the CDM to store persistent data
* `ProvisionFetcher`: for Android MediaDrm device provisioning
-* `CdmProxy`: (in progress)
### Security
@@ -363,8 +357,7 @@ which process in production, see [Adoption](#Adoption) below.
Also note that all the [Secure Auxiliary Services](#Secure-Auxiliary-Services)
are running in a more privileged process than the process where the media
components that use them run in. For example, all of the existing services run
-in the browser process except for the `CdmProxy`, which runs in the GPU process.
-They must defend against compromised media components.
+in the browser process. They must defend against compromised media components.
### Adoption
@@ -405,7 +398,6 @@ They must defend against compromised media components.
* `MediaService` in the GPU process (registered in `GpuServiceFactory` with
`GpuMojoMediaClient`)
* `MojoVideoDecoder` + hardware video decoders such as D3D11VideoDecoder
- * Provides `CdmProxy` to the `CdmService`
## Other Services
diff --git a/chromium/media/mojo/clients/mojo_cdm.cc b/chromium/media/mojo/clients/mojo_cdm.cc
index 1bd9ba9b9e9..acc9d1ca9de 100644
--- a/chromium/media/mojo/clients/mojo_cdm.cc
+++ b/chromium/media/mojo/clients/mojo_cdm.cc
@@ -21,7 +21,6 @@
#include "media/mojo/clients/mojo_decryptor.h"
#include "media/mojo/common/media_type_converters.h"
#include "media/mojo/mojom/decryptor.mojom.h"
-#include "media/mojo/mojom/interface_factory.mojom.h"
#include "services/service_manager/public/cpp/connect.h"
#include "services/service_manager/public/mojom/interface_provider.mojom.h"
#include "url/origin.h"
@@ -43,15 +42,14 @@ void MojoCdm::Create(
const url::Origin& security_origin,
const CdmConfig& cdm_config,
mojo::PendingRemote<mojom::ContentDecryptionModule> remote_cdm,
- mojom::InterfaceFactory* interface_factory,
const SessionMessageCB& session_message_cb,
const SessionClosedCB& session_closed_cb,
const SessionKeysChangeCB& session_keys_change_cb,
const SessionExpirationUpdateCB& session_expiration_update_cb,
CdmCreatedCB cdm_created_cb) {
- scoped_refptr<MojoCdm> mojo_cdm(new MojoCdm(
- std::move(remote_cdm), interface_factory, session_message_cb,
- session_closed_cb, session_keys_change_cb, session_expiration_update_cb));
+ scoped_refptr<MojoCdm> mojo_cdm(
+ new MojoCdm(std::move(remote_cdm), session_message_cb, session_closed_cb,
+ session_keys_change_cb, session_expiration_update_cb));
// |mojo_cdm| ownership is passed to the promise.
auto promise = std::make_unique<CdmInitializedPromise>(
@@ -62,13 +60,11 @@ void MojoCdm::Create(
}
MojoCdm::MojoCdm(mojo::PendingRemote<mojom::ContentDecryptionModule> remote_cdm,
- mojom::InterfaceFactory* interface_factory,
const SessionMessageCB& session_message_cb,
const SessionClosedCB& session_closed_cb,
const SessionKeysChangeCB& session_keys_change_cb,
const SessionExpirationUpdateCB& session_expiration_update_cb)
: remote_cdm_(std::move(remote_cdm)),
- interface_factory_(interface_factory),
cdm_id_(CdmContext::kInvalidCdmId),
session_message_cb_(session_message_cb),
session_closed_cb_(session_closed_cb),
@@ -305,26 +301,12 @@ Decryptor* MojoCdm::GetDecryptor() {
if (decryptor_)
return decryptor_.get();
- mojo::PendingRemote<mojom::Decryptor> decryptor_remote;
-
// Can be called on a different thread.
if (decryptor_remote_.is_valid()) {
DVLOG(1) << __func__ << ": Using Decryptor exposed by the CDM directly";
- decryptor_remote = std::move(decryptor_remote_);
- } else if (interface_factory_ && cdm_id_ != CdmContext::kInvalidCdmId) {
-#if BUILDFLAG(ENABLE_CDM_PROXY)
- // TODO(xhwang): Pass back info on whether Decryptor is supported by the
- // remote CDM.
- DVLOG(1) << __func__ << ": Using Decryptor associated with CDM ID "
- << cdm_id_ << ", typically hosted by CdmProxy in MediaService";
- interface_factory_->CreateDecryptor(
- cdm_id_, decryptor_remote.InitWithNewPipeAndPassReceiver());
-#endif // BUILDFLAG(ENABLE_CDM_PROXY)
+ decryptor_ = std::make_unique<MojoDecryptor>(std::move(decryptor_remote_));
}
- if (decryptor_remote)
- decryptor_ = std::make_unique<MojoDecryptor>(std::move(decryptor_remote));
-
return decryptor_.get();
}
diff --git a/chromium/media/mojo/clients/mojo_cdm.h b/chromium/media/mojo/clients/mojo_cdm.h
index 4c1a0ffbeff..45f0fcb09f1 100644
--- a/chromium/media/mojo/clients/mojo_cdm.h
+++ b/chromium/media/mojo/clients/mojo_cdm.h
@@ -35,10 +35,6 @@ class Origin;
namespace media {
-namespace mojom {
-class InterfaceFactory;
-}
-
class MojoDecryptor;
// A ContentDecryptionModule that proxies to a mojom::ContentDecryptionModule.
@@ -55,7 +51,6 @@ class MojoCdm : public ContentDecryptionModule,
const url::Origin& security_origin,
const CdmConfig& cdm_config,
mojo::PendingRemote<mojom::ContentDecryptionModule> remote_cdm,
- mojom::InterfaceFactory* interface_factory,
const SessionMessageCB& session_message_cb,
const SessionClosedCB& session_closed_cb,
const SessionKeysChangeCB& session_keys_change_cb,
@@ -91,7 +86,6 @@ class MojoCdm : public ContentDecryptionModule,
private:
MojoCdm(mojo::PendingRemote<mojom::ContentDecryptionModule> remote_cdm,
- mojom::InterfaceFactory* interface_factory,
const SessionMessageCB& session_message_cb,
const SessionClosedCB& session_closed_cb,
const SessionKeysChangeCB& session_keys_change_cb,
@@ -140,7 +134,6 @@ class MojoCdm : public ContentDecryptionModule,
THREAD_CHECKER(thread_checker_);
mojo::Remote<mojom::ContentDecryptionModule> remote_cdm_;
- mojom::InterfaceFactory* interface_factory_;
mojo::AssociatedReceiver<ContentDecryptionModuleClient> client_receiver_{
this};
diff --git a/chromium/media/mojo/clients/mojo_cdm_factory.cc b/chromium/media/mojo/clients/mojo_cdm_factory.cc
index 118f0abd01f..d606e735eb1 100644
--- a/chromium/media/mojo/clients/mojo_cdm_factory.cc
+++ b/chromium/media/mojo/clients/mojo_cdm_factory.cc
@@ -64,8 +64,8 @@ void MojoCdmFactory::Create(
key_system, cdm_pending_remote.InitWithNewPipeAndPassReceiver());
MojoCdm::Create(key_system, security_origin, cdm_config,
- std::move(cdm_pending_remote), interface_factory_,
- session_message_cb, session_closed_cb, session_keys_change_cb,
+ std::move(cdm_pending_remote), session_message_cb,
+ session_closed_cb, session_keys_change_cb,
session_expiration_update_cb, std::move(cdm_created_cb));
}
diff --git a/chromium/media/mojo/clients/mojo_cdm_unittest.cc b/chromium/media/mojo/clients/mojo_cdm_unittest.cc
index ab0eb09b91d..f7b402b4f54 100644
--- a/chromium/media/mojo/clients/mojo_cdm_unittest.cc
+++ b/chromium/media/mojo/clients/mojo_cdm_unittest.cc
@@ -99,7 +99,6 @@ class MojoCdmTest : public ::testing::Test {
MojoCdm::Create(key_system, url::Origin::Create(GURL(kTestSecurityOrigin)),
CdmConfig(), cdm_receiver_.BindNewPipeAndPassRemote(),
- nullptr,
base::Bind(&MockCdmClient::OnSessionMessage,
base::Unretained(&cdm_client_)),
base::Bind(&MockCdmClient::OnSessionClosed,
diff --git a/chromium/media/mojo/mojom/BUILD.gn b/chromium/media/mojo/mojom/BUILD.gn
index 10940dcef80..f86298e18d2 100644
--- a/chromium/media/mojo/mojom/BUILD.gn
+++ b/chromium/media/mojo/mojom/BUILD.gn
@@ -23,6 +23,7 @@ mojom("mojom") {
"decryptor.mojom",
"demuxer_stream.mojom",
"display_media_information.mojom",
+ "frame_interface_factory.mojom",
"interface_factory.mojom",
"key_system_support.mojom",
"media_log.mojom",
@@ -31,10 +32,10 @@ mojom("mojom") {
"media_types.mojom",
"output_protection.mojom",
"platform_verification.mojom",
+ "playback_events_recorder.mojom",
"provision_fetcher.mojom",
"renderer.mojom",
"renderer_extensions.mojom",
- "soda_service.mojom",
"video_decode_perf_history.mojom",
"video_decode_stats_recorder.mojom",
"video_decoder.mojom",
@@ -49,6 +50,8 @@ mojom("mojom") {
if (is_android) {
sources += [ "android_overlay.mojom" ]
+ } else {
+ sources += [ "speech_recognition_service.mojom" ]
}
if (is_chromecast) {
@@ -76,11 +79,6 @@ mojom("mojom") {
enabled_features = [ "enable_cast_renderer" ]
}
- if (enable_cdm_proxy) {
- enabled_features = [ "enable_cdm_proxy" ]
- sources += [ "cdm_proxy.mojom" ]
- }
-
export_class_attribute_blink = "BLINK_PLATFORM_EXPORT"
export_define_blink = "BLINK_PLATFORM_IMPLEMENTATION=1"
export_header_blink = "third_party/blink/public/platform/web_common.h"
@@ -115,7 +113,7 @@ source_set("unit_tests") {
"audio_decoder_config_mojom_traits_unittest.cc",
"cdm_key_information_mojom_traits_unittest.cc",
"video_decoder_config_mojom_traits_unittest.cc",
- "video_encoder_info_mojom_traits_unittest.cc",
+ "video_encode_accelerator_mojom_traits_unittest.cc",
"video_frame_mojom_traits_unittest.cc",
]
diff --git a/chromium/media/mojo/mojom/audio_output_stream.mojom b/chromium/media/mojo/mojom/audio_output_stream.mojom
index 8e7077e9401..68ed6e7db47 100644
--- a/chromium/media/mojo/mojom/audio_output_stream.mojom
+++ b/chromium/media/mojo/mojom/audio_output_stream.mojom
@@ -75,8 +75,7 @@ interface AudioOutputStreamProvider {
// output streams that are related during audio processing.
// This method fails if it is called more than once.
Acquire(AudioParameters params,
- pending_remote<AudioOutputStreamProviderClient> client,
- mojo_base.mojom.UnguessableToken? processing_id);
+ pending_remote<AudioOutputStreamProviderClient> client);
};
interface AudioOutputStreamProviderClient {
diff --git a/chromium/media/mojo/mojom/cdm_key_information_mojom_traits.cc b/chromium/media/mojo/mojom/cdm_key_information_mojom_traits.cc
index 94a09fef5b2..e3b2182b4e9 100644
--- a/chromium/media/mojo/mojom/cdm_key_information_mojom_traits.cc
+++ b/chromium/media/mojo/mojom/cdm_key_information_mojom_traits.cc
@@ -4,7 +4,7 @@
#include "media/mojo/mojom/cdm_key_information_mojom_traits.h"
-#include "base/logging.h"
+#include "base/notreached.h"
namespace mojo {
diff --git a/chromium/media/mojo/mojom/cdm_proxy.mojom b/chromium/media/mojo/mojom/cdm_proxy.mojom
deleted file mode 100644
index d5037632b77..00000000000
--- a/chromium/media/mojo/mojom/cdm_proxy.mojom
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2017 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.
-
-module media.mojom;
-
-// An interface that helps proxy part of ContentDecryptionModule (CDM)
-// functionalities to a different entity, e.g. hardware CDM modules.
-// In general, the interpretation of the method and callback parameters are
-// protocol dependent.
-// CdmProxy implementation is hosted in the GPU process.
-interface CdmProxy {
- // See media/cdm/cdm_proxy.h for the following native enums.
- [Native]
- enum Status;
-
- [Native]
- enum Protocol;
-
- [Native]
- enum Function;
-
- [Native]
- enum KeyType;
-
- // Initializes the proxy.
- // If the proxy created a crypto session, then the ID for the crypto session
- // is |crypto_session_id|.
- // |cdm_id| can be used to connect the remote media pipeline and CdmProxy.
- Initialize(pending_associated_remote<CdmProxyClient> client)
- => (Status status,
- Protocol protocol,
- uint32 crypto_session_id,
- int32 cdm_id);
-
- // Processes and updates the state of the proxy.
- // |func| specifies what type of function to use.
- // |crypto_session_id| is a value returned from Initialize() or
- // CreateMediaCryptoSessions().
- // |input_data| is the input data to be processed.
- // |output_data_size| is the expected size of |output_data|. Some protocols
- // require this field in order to determine the size of the output, but some
- // may completely ignore it.
- // The output data is passed back in |output_data|.
- Process(Function func,
- uint32 crypto_session_id,
- array<uint8> input_data,
- uint32 output_data_size) => (Status status,
- array<uint8> output_data);
-
- // Creates a crypto session for handling media.
- // If extra data has to be passed to further setup the media crypto session,
- // pass the data as |input_data|.
- // |crypto_session_id| is the ID for the crypto session.
- // |output_data| is extra value, if any.
- CreateMediaCryptoSession(array<uint8> input_data) => (
- Status status, uint32 crypto_session_id, uint64 output_data);
-
- // Sets a key in the proxy.
- // |crypto_session_id| is the crypto session for decryption.
- // |key_id| is the ID of the key.
- // |key_blob| is the opaque key blob for decrypting or decoding.
- SetKey(uint32 crypto_session_id, array<uint8> key_id, KeyType key_type,
- array<uint8> key_blob) => (Status status);
-
- // Removes a key from the proxy.
- // |crypto_session_id| is the crypto session for decryption.
- // |key_id| is the ID of the key.
- RemoveKey(uint32 crypto_session_id, array<uint8> key_id) => (Status status);
-};
-
-// Client of CdmProxy.
-// CdmProxyClient is running in the fully sandboxed CDM (e.g. utility) process.
-interface CdmProxyClient {
- // Notifies the client that there has been a hardware reset.
- NotifyHardwareReset();
-};
diff --git a/chromium/media/mojo/mojom/cdm_proxy.typemap b/chromium/media/mojo/mojom/cdm_proxy.typemap
deleted file mode 100644
index d2f05392218..00000000000
--- a/chromium/media/mojo/mojom/cdm_proxy.typemap
+++ /dev/null
@@ -1,21 +0,0 @@
-# Copyright 2017 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.
-
-mojom = "//media/mojo/mojom/cdm_proxy.mojom"
-
-public_headers = [ "//media/cdm/cdm_proxy.h" ]
-
-traits_headers = [ "//media/base/ipc/media_param_traits_macros.h" ]
-
-deps = [
- "//media",
- "//media/base/ipc",
-]
-
-type_mappings = [
- "media.mojom.CdmProxy.Function=::media::CdmProxy::Function",
- "media.mojom.CdmProxy.KeyType=::media::CdmProxy::KeyType",
- "media.mojom.CdmProxy.Protocol=::media::CdmProxy::Protocol",
- "media.mojom.CdmProxy.Status=::media::CdmProxy::Status",
-]
diff --git a/chromium/media/mojo/mojom/cdm_service.mojom b/chromium/media/mojo/mojom/cdm_service.mojom
index a5402a788fa..e154033021c 100644
--- a/chromium/media/mojo/mojom/cdm_service.mojom
+++ b/chromium/media/mojo/mojom/cdm_service.mojom
@@ -5,6 +5,7 @@
module media.mojom;
import "media/mojo/mojom/content_decryption_module.mojom";
+import "media/mojo/mojom/frame_interface_factory.mojom";
import "mojo/public/mojom/base/file_path.mojom";
import "services/service_manager/public/mojom/interface_provider.mojom";
@@ -28,11 +29,12 @@ interface CdmService {
[EnableIf=is_mac]
pending_remote<SeatbeltExtensionTokenProvider>? token_provider);
- // Requests an CdmFactory. |host_interfaces| can optionally be used to provide
- // interfaces hosted by the caller to the remote CdmFactory implementation.
+ // Requests an CdmFactory. |frame_interfaces| can optionally be used to
+ // provide interfaces hosted by the caller to the remote CdmFactory
+ // implementation.
CreateCdmFactory(
pending_receiver<CdmFactory> factory,
- pending_remote<service_manager.mojom.InterfaceProvider> host_interfaces);
+ pending_remote<FrameInterfaceFactory> frame_interfaces);
};
// An interface to provide a sandbox seatbelt extension token synchronously.
diff --git a/chromium/media/mojo/mojom/frame_interface_factory.mojom b/chromium/media/mojo/mojom/frame_interface_factory.mojom
new file mode 100644
index 00000000000..49a153bd05f
--- /dev/null
+++ b/chromium/media/mojo/mojom/frame_interface_factory.mojom
@@ -0,0 +1,24 @@
+// 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.
+
+module media.mojom;
+
+import "media/mojo/mojom/cdm_storage.mojom";
+import "media/mojo/mojom/provision_fetcher.mojom";
+import "mojo/public/mojom/base/generic_pending_receiver.mojom";
+
+// A factory for acquiring media mojo interfaces that are bound to a
+// RenderFrameHost.
+interface FrameInterfaceFactory {
+ // Binds the ProvisionFetcher for the frame.
+ CreateProvisionFetcher(pending_receiver<ProvisionFetcher> provision_fetcher);
+
+ // Binds the CdmStorage for the frame. This requires that the frame have
+ // CDM storage available.
+ CreateCdmStorage(pending_receiver<CdmStorage> cdm_storage);
+
+ // Binds a generic media frame-bound interface. This is to allow //content
+ // embedders to provide additional interfaces.
+ BindEmbedderReceiver(mojo_base.mojom.GenericPendingReceiver receiver);
+};
diff --git a/chromium/media/mojo/mojom/interface_factory.mojom b/chromium/media/mojo/mojom/interface_factory.mojom
index 8cea7ee2d35..7215013dfa0 100644
--- a/chromium/media/mojo/mojom/interface_factory.mojom
+++ b/chromium/media/mojo/mojom/interface_factory.mojom
@@ -5,8 +5,6 @@
module media.mojom;
import "media/mojo/mojom/audio_decoder.mojom";
-[EnableIf=enable_cdm_proxy]
-import "media/mojo/mojom/cdm_proxy.mojom";
import "media/mojo/mojom/decryptor.mojom";
import "media/mojo/mojom/content_decryption_module.mojom";
import "media/mojo/mojom/renderer.mojom";
@@ -63,14 +61,4 @@ interface InterfaceFactory {
// this call may be initiated by an untrusted process (e.g. renderer), so the
// implementation must fully validate |key_system| before creating the CDM.
CreateCdm(string key_system, pending_receiver<ContentDecryptionModule> cdm);
-
- // Creates a Decryptor associated with the |cdm_id|.
- CreateDecryptor(int32 cdm_id, pending_receiver<Decryptor> decryptor);
-
- // Creates a CdmProxy that proxies part of CDM functionalities to a different
- // entity, e.g. hardware CDM modules. The created |cdm_proxy| must match the
- // type of the CDM, identified by |cdm_guid|.
- [EnableIf=enable_cdm_proxy]
- CreateCdmProxy(mojo_base.mojom.Token cdm_guid,
- pending_receiver<CdmProxy> cdm_proxy);
};
diff --git a/chromium/media/mojo/mojom/key_system_support.mojom b/chromium/media/mojo/mojom/key_system_support.mojom
index 06ede1b0ee1..69d608ca7d5 100644
--- a/chromium/media/mojo/mojom/key_system_support.mojom
+++ b/chromium/media/mojo/mojom/key_system_support.mojom
@@ -16,8 +16,7 @@ struct KeySystemCapability {
bool supports_vp9_profile2;
array<EncryptionScheme> encryption_schemes;
- // Hardware secure codecs and encryption schemes supported by the CDM,
- // directly or indirectly through CdmProxy.
+ // Hardware secure codecs and encryption schemes supported by the CDM.
array<VideoCodec> hw_secure_video_codecs;
array<EncryptionScheme> hw_secure_encryption_schemes;
diff --git a/chromium/media/mojo/mojom/media_metrics_provider.mojom b/chromium/media/mojo/mojom/media_metrics_provider.mojom
index bb9ad8db7e4..526e2b17018 100644
--- a/chromium/media/mojo/mojom/media_metrics_provider.mojom
+++ b/chromium/media/mojo/mojom/media_metrics_provider.mojom
@@ -7,6 +7,7 @@ module media.mojom;
import "media/learning/mojo/public/mojom/learning_task_controller.mojom";
import "media/mojo/mojom/media_types.mojom";
import "media/mojo/mojom/video_decode_stats_recorder.mojom";
+import "media/mojo/mojom/playback_events_recorder.mojom";
import "media/mojo/mojom/watch_time_recorder.mojom";
import "mojo/public/mojom/base/time.mojom";
import "ui/gfx/geometry/mojom/geometry.mojom";
@@ -64,8 +65,13 @@ interface MediaMetricsProvider {
// Returns a LearningTaskController for the given |taskName|.
AcquireLearningTaskController(
- string taskName,
- pending_receiver<media.learning.mojom.LearningTaskController> controller);
+ string taskName,
+ pending_receiver<media.learning.mojom.LearningTaskController> controller);
+
+ // Returns a PlaybackEventsRecorder instance. Implementation may drop the
+ // |receiver| if it's not interested in recording playback events.
+ AcquirePlaybackEventsRecorder(
+ pending_receiver<PlaybackEventsRecorder> receiver);
// Can be called multiple times to set properties about a playback.
SetHasAudio(AudioCodec codec);
diff --git a/chromium/media/mojo/mojom/media_service.mojom b/chromium/media/mojo/mojom/media_service.mojom
index 995e6c55d98..738b54a72c7 100644
--- a/chromium/media/mojo/mojom/media_service.mojom
+++ b/chromium/media/mojo/mojom/media_service.mojom
@@ -4,18 +4,18 @@
module media.mojom;
+import "media/mojo/mojom/frame_interface_factory.mojom";
import "media/mojo/mojom/interface_factory.mojom";
-import "services/service_manager/public/mojom/interface_provider.mojom";
// A service to provide media InterfaceFactory, typically to the media pipeline
// running in the renderer process. The service itself runs in the process
// specified by the |mojo_media_host| gn build flag. The service is always
// connected from the browser process.
interface MediaService {
- // Requests an InterfaceFactory. |host_interfaces| can optionally be used to
+ // Requests an InterfaceFactory. |frame_interfaces| can optionally be used to
// provide interfaces hosted by the caller to the remote InterfaceFactory
// implementation.
CreateInterfaceFactory(
pending_receiver<InterfaceFactory> factory,
- pending_remote<service_manager.mojom.InterfaceProvider> host_interfaces);
+ pending_remote<FrameInterfaceFactory> frame_interfaces);
};
diff --git a/chromium/media/mojo/mojom/media_types.mojom b/chromium/media/mojo/mojom/media_types.mojom
index 06f95f16679..6b083bfe691 100644
--- a/chromium/media/mojo/mojom/media_types.mojom
+++ b/chromium/media/mojo/mojom/media_types.mojom
@@ -279,6 +279,7 @@ struct VideoFrame {
mojo_base.mojom.DictionaryValue metadata;
gfx.mojom.ColorSpace color_space;
+ HDRMetadata? hdr_metadata;
};
// Possible choices for storing VideoFrame data.
diff --git a/chromium/media/mojo/mojom/playback_events_recorder.mojom b/chromium/media/mojo/mojom/playback_events_recorder.mojom
new file mode 100644
index 00000000000..4971c776c16
--- /dev/null
+++ b/chromium/media/mojo/mojom/playback_events_recorder.mojom
@@ -0,0 +1,45 @@
+// 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.
+
+module media.mojom;
+
+import "media/mojo/mojom/media_types.mojom";
+import "ui/gfx/geometry/mojom/geometry.mojom";
+
+// PlaybackEventsRecorder allows to observe and record events occurring in the
+// media pipeline. The interface is called by the renderer process (in
+// blink::WebMediaPlayerImpl), while the implementation normally runs in the
+// browser.
+interface PlaybackEventsRecorder {
+ // Called when player has been started or resumed.
+ OnPlaying();
+
+ // Called when player has been paused.
+ OnPaused();
+
+ // Called when player position is being changed.
+ OnSeeking();
+
+ // Called when the player has reached the end of the current file.
+ OnEnded();
+
+ // Called when playback has failed due to the specified error.
+ OnError(PipelineStatus status);
+
+ // Called when playback has been suspended while buffering the media.
+ OnBuffering();
+
+ // Called after media buffering has completed. The player is in buffering
+ // state after the following events until this method is called:
+ // 1. Player initialized (i.e. after PlaybackEventsRecorder is created).
+ // 2. Seek operation, see OnSeeking().
+ // 3. Buffering started, see OnBuffering().
+ OnBufferingComplete();
+
+ // Called when video playback starts and every time video resolution changes.
+ OnNaturalSizeChanged(gfx.mojom.Size size);
+
+ // Called periodically (e.g. every second) while playback is active.
+ OnPipelineStatistics(PipelineStatistics stats);
+};
diff --git a/chromium/media/mojo/mojom/soda_service.mojom b/chromium/media/mojo/mojom/soda_service.mojom
deleted file mode 100644
index eb9057a32db..00000000000
--- a/chromium/media/mojo/mojom/soda_service.mojom
+++ /dev/null
@@ -1,42 +0,0 @@
-// 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.
-
-module media.mojom;
-
-import "media/mojo/mojom/media_types.mojom";
-
-// The main interface a client uses to interact with a SODA service
-// process. Every renderer can own one or more Remote<SodaContext>,
-// with the receiver bound through the BrowserInterfaceBroker.
-interface SodaContext {
- // Bind the recognizers to the SODA service.
- BindRecognizer(pending_receiver<SodaRecognizer> receiver,
- pending_remote<SodaRecognizerClient> client);
-};
-
-// The main interface to a Speech On-Device API (SODA) service process.
-// Used by the browser to issue top-level control requests to the service,
-// acquired during process launch.
-interface SodaService {
- // Bind the SODA context to a new instance of SODA.
- BindContext(pending_receiver<SodaContext> context);
-};
-
-// The interface used to pass raw audio from the renderer to the SODA
-// service. The remote lives in the renderer process and the receiver
-// lives in the SODA process.
-interface SodaRecognizer {
- // Initialize the SODA instance. SODA will use the SODA recognition client
- // to return the recognition events containing the transcribed audio back
- // to the originating media.
- SendAudioToSoda(AudioDataS16 buffer);
-};
-
-// The interface used to return speech recognition events from the SODA
-// service back to the originating media. The remote lives in the SODA
-// process and the receiver lives in the renderer process.
-interface SodaRecognizerClient {
- // Triggered by SODA on a speech recognition event.
- OnSodaRecognitionEvent(string transcription);
-};
diff --git a/chromium/media/mojo/mojom/speech_recognition_service.mojom b/chromium/media/mojo/mojom/speech_recognition_service.mojom
new file mode 100644
index 00000000000..6835c22c5ce
--- /dev/null
+++ b/chromium/media/mojo/mojom/speech_recognition_service.mojom
@@ -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.
+
+module media.mojom;
+
+import "media/mojo/mojom/media_types.mojom";
+
+// The main interface a client uses to interact with a speech recognition
+// service process. Every renderer can own one or more
+// Remote<SpeechRecognitionContext>, with the receiver bound through the
+// BrowserInterfaceBroker.
+interface SpeechRecognitionContext {
+ // Bind the recognizers to the speech recognition service.
+ BindRecognizer(pending_receiver<SpeechRecognitionRecognizer> receiver,
+ pending_remote<SpeechRecognitionRecognizerClient> client);
+};
+
+// The main interface to a speech secognition service process.
+// Used by the browser to issue top-level control requests to the service,
+// acquired during process launch.
+interface SpeechRecognitionService {
+ // Bind the context to a new instance of the speech recognition.
+ BindContext(pending_receiver<SpeechRecognitionContext> context);
+};
+
+// The interface used to pass raw audio from the renderer to the speech
+// recognition service. The remote lives in the renderer process and the
+// receiver lives in the speech recognition process.
+interface SpeechRecognitionRecognizer {
+ // Initialize the speech recognition instance. The speech recognition client
+ // will return the recognition events containing the transcribed audio back
+ // to the originating media.
+ SendAudioToSpeechRecognitionService(AudioDataS16 buffer);
+};
+
+// The interface used to return speech recognition events from the speech
+// recognition service back to the originating media. The remote lives in the
+// speech recognition process and the receiver lives in the renderer process.
+interface SpeechRecognitionRecognizerClient {
+ // Triggered by speech recognition process on a speech recognition event.
+ OnSpeechRecognitionRecognitionEvent(SpeechRecognitionResult result);
+};
+
+// A speech recognition result created by the speech service and passed to the
+// renderer.
+struct SpeechRecognitionResult {
+ string transcription;
+ bool is_final;
+};
diff --git a/chromium/media/mojo/mojom/typemaps.gni b/chromium/media/mojo/mojom/typemaps.gni
index 8e5ef00405d..44e4010102d 100644
--- a/chromium/media/mojo/mojom/typemaps.gni
+++ b/chromium/media/mojo/mojom/typemaps.gni
@@ -27,7 +27,3 @@ typemaps = [
if (enable_media_drm_storage) {
typemaps += [ "//media/mojo/mojom/media_drm_storage.typemap" ]
}
-
-if (enable_cdm_proxy) {
- typemaps += [ "//media/mojo/mojom/cdm_proxy.typemap" ]
-}
diff --git a/chromium/media/mojo/mojom/video_encode_accelerator.mojom b/chromium/media/mojo/mojom/video_encode_accelerator.mojom
index b1baa0fd9d4..2bab244c42d 100644
--- a/chromium/media/mojo/mojom/video_encode_accelerator.mojom
+++ b/chromium/media/mojo/mojom/video_encode_accelerator.mojom
@@ -61,6 +61,17 @@ struct VideoBitrateAllocation {
};
// This defines a mojo transport format for
+// media::VideoEncodeAccelerator::Config::SpatialLayer.
+struct SpatialLayer {
+ int32 width;
+ int32 height;
+ uint32 bitrate_bps;
+ uint32 framerate;
+ uint8 max_qp;
+ uint8 num_of_temporal_layers;
+};
+
+// This defines a mojo transport format for
// media::VideoEncodeAccelerator::Config.
struct VideoEncodeAcceleratorConfig {
// See media::VideoEncodeAccelerator::Config::ContentType
@@ -88,6 +99,7 @@ struct VideoEncodeAcceleratorConfig {
StorageType storage_type;
bool has_storage_type; // Whether or not config has storage type config
ContentType content_type;
+ array<SpatialLayer> spatial_layers;
};
interface VideoEncodeAccelerator {
diff --git a/chromium/media/mojo/mojom/video_encode_accelerator.typemap b/chromium/media/mojo/mojom/video_encode_accelerator.typemap
index 0f1caea7bf8..be2bfda9985 100644
--- a/chromium/media/mojo/mojom/video_encode_accelerator.typemap
+++ b/chromium/media/mojo/mojom/video_encode_accelerator.typemap
@@ -31,6 +31,7 @@ type_mappings = [
"media.mojom.VideoBitrateAllocation=::media::VideoBitrateAllocation",
"media.mojom.VideoEncodeAccelerator.Error=::media::VideoEncodeAccelerator::Error",
"media.mojom.VideoEncodeAcceleratorConfig=::media::VideoEncodeAccelerator::Config",
+ "media.mojom.SpatialLayer=::media::VideoEncodeAccelerator::Config::SpatialLayer",
"media.mojom.VideoEncodeAcceleratorSupportedProfile=::media::VideoEncodeAccelerator::SupportedProfile",
"media.mojom.Vp8Metadata=::media::Vp8Metadata",
]
diff --git a/chromium/media/mojo/mojom/video_encode_accelerator_mojom_traits.cc b/chromium/media/mojo/mojom/video_encode_accelerator_mojom_traits.cc
index 1d3b11b22a3..1a64d95d376 100644
--- a/chromium/media/mojo/mojom/video_encode_accelerator_mojom_traits.cc
+++ b/chromium/media/mojo/mojom/video_encode_accelerator_mojom_traits.cc
@@ -4,7 +4,7 @@
#include "media/mojo/mojom/video_encode_accelerator_mojom_traits.h"
-#include "base/logging.h"
+#include "base/notreached.h"
#include "base/optional.h"
#include "media/base/video_bitrate_allocation.h"
#include "mojo/public/cpp/base/time_mojom_traits.h"
@@ -196,6 +196,20 @@ bool EnumTraits<media::mojom::VideoEncodeAcceleratorConfig::ContentType,
}
// static
+bool StructTraits<media::mojom::SpatialLayerDataView,
+ media::VideoEncodeAccelerator::Config::SpatialLayer>::
+ Read(media::mojom::SpatialLayerDataView input,
+ media::VideoEncodeAccelerator::Config::SpatialLayer* output) {
+ output->width = input.width();
+ output->height = input.height();
+ output->bitrate_bps = input.bitrate_bps();
+ output->framerate = input.framerate();
+ output->max_qp = input.max_qp();
+ output->num_of_temporal_layers = input.num_of_temporal_layers();
+ return true;
+}
+
+// static
bool StructTraits<media::mojom::VideoEncodeAcceleratorConfigDataView,
media::VideoEncodeAccelerator::Config>::
Read(media::mojom::VideoEncodeAcceleratorConfigDataView input,
@@ -235,10 +249,15 @@ bool StructTraits<media::mojom::VideoEncodeAcceleratorConfigDataView,
if (!input.ReadContentType(&content_type))
return false;
+ std::vector<media::VideoEncodeAccelerator::Config::SpatialLayer>
+ spatial_layers;
+ if (!input.ReadSpatialLayers(&spatial_layers))
+ return false;
+
*output = media::VideoEncodeAccelerator::Config(
input_format, input_visible_size, output_profile, input.initial_bitrate(),
initial_framerate, gop_length, h264_output_level, storage_type,
- content_type);
+ content_type, spatial_layers);
return true;
}
diff --git a/chromium/media/mojo/mojom/video_encode_accelerator_mojom_traits.h b/chromium/media/mojo/mojom/video_encode_accelerator_mojom_traits.h
index 6b1a5546ec8..eebc8813801 100644
--- a/chromium/media/mojo/mojom/video_encode_accelerator_mojom_traits.h
+++ b/chromium/media/mojo/mojom/video_encode_accelerator_mojom_traits.h
@@ -132,6 +132,43 @@ struct EnumTraits<media::mojom::VideoEncodeAcceleratorConfig::ContentType,
};
template <>
+struct StructTraits<media::mojom::SpatialLayerDataView,
+ media::VideoEncodeAccelerator::Config::SpatialLayer> {
+ static int32_t width(
+ const media::VideoEncodeAccelerator::Config::SpatialLayer& input) {
+ return input.width;
+ }
+
+ static int32_t height(
+ const media::VideoEncodeAccelerator::Config::SpatialLayer& input) {
+ return input.height;
+ }
+
+ static uint32_t bitrate_bps(
+ const media::VideoEncodeAccelerator::Config::SpatialLayer& input) {
+ return input.bitrate_bps;
+ }
+
+ static uint32_t framerate(
+ const media::VideoEncodeAccelerator::Config::SpatialLayer& input) {
+ return input.framerate;
+ }
+
+ static uint8_t max_qp(
+ const media::VideoEncodeAccelerator::Config::SpatialLayer& input) {
+ return input.max_qp;
+ }
+
+ static uint8_t num_of_temporal_layers(
+ const media::VideoEncodeAccelerator::Config::SpatialLayer& input) {
+ return input.num_of_temporal_layers;
+ }
+
+ static bool Read(media::mojom::SpatialLayerDataView input,
+ media::VideoEncodeAccelerator::Config::SpatialLayer* output);
+};
+
+template <>
struct StructTraits<media::mojom::VideoEncodeAcceleratorConfigDataView,
media::VideoEncodeAccelerator::Config> {
static media::VideoPixelFormat input_format(
@@ -200,6 +237,11 @@ struct StructTraits<media::mojom::VideoEncodeAcceleratorConfigDataView,
return input.content_type;
}
+ static const std::vector<media::VideoEncodeAccelerator::Config::SpatialLayer>&
+ spatial_layers(const media::VideoEncodeAccelerator::Config& input) {
+ return input.spatial_layers;
+ }
+
static bool Read(media::mojom::VideoEncodeAcceleratorConfigDataView input,
media::VideoEncodeAccelerator::Config* output);
};
diff --git a/chromium/media/mojo/mojom/video_encode_accelerator_mojom_traits_unittest.cc b/chromium/media/mojo/mojom/video_encode_accelerator_mojom_traits_unittest.cc
new file mode 100644
index 00000000000..cfd516058b6
--- /dev/null
+++ b/chromium/media/mojo/mojom/video_encode_accelerator_mojom_traits_unittest.cc
@@ -0,0 +1,156 @@
+// 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/mojo/mojom/video_encode_accelerator_mojom_traits.h"
+#include "media/mojo/mojom/video_encoder_info_mojom_traits.h"
+
+#include "media/video/video_encode_accelerator.h"
+#include "media/video/video_encoder_info.h"
+#include "mojo/public/cpp/test_support/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+
+// These binary operators are implemented here because they are used in this
+// unittest. They cannot be enclosed by anonymous namespace, because they must
+// be visible by gtest in linking.
+bool operator==(const ::media::ScalingSettings& l,
+ const ::media::ScalingSettings& r) {
+ return l.min_qp == r.min_qp && l.max_qp == r.max_qp;
+}
+
+bool operator!=(const ::media::ScalingSettings& l,
+ const ::media::ScalingSettings& r) {
+ return !(l == r);
+}
+
+bool operator==(const ::media::ResolutionBitrateLimit& l,
+ const ::media::ResolutionBitrateLimit& r) {
+ return (l.frame_size == r.frame_size &&
+ l.min_start_bitrate_bps == r.min_start_bitrate_bps &&
+ l.min_bitrate_bps == r.min_bitrate_bps &&
+ l.max_bitrate_bps == r.max_bitrate_bps);
+}
+
+bool operator!=(const ::media::ResolutionBitrateLimit& l,
+ const ::media::ResolutionBitrateLimit& r) {
+ return !(l == r);
+}
+
+bool operator==(const ::media::VideoEncoderInfo& l,
+ const ::media::VideoEncoderInfo& r) {
+ if (l.implementation_name != r.implementation_name)
+ return false;
+ if (l.supports_native_handle != r.supports_native_handle)
+ return false;
+ if (l.has_trusted_rate_controller != r.has_trusted_rate_controller)
+ return false;
+ if (l.is_hardware_accelerated != r.is_hardware_accelerated)
+ return false;
+ if (l.supports_simulcast != r.supports_simulcast)
+ return false;
+ if (l.scaling_settings != r.scaling_settings)
+ return false;
+ for (size_t i = 0; i < ::media::VideoEncoderInfo::kMaxSpatialLayers; ++i) {
+ if (l.fps_allocation[i] != r.fps_allocation[i])
+ return false;
+ }
+ if (l.resolution_bitrate_limits != r.resolution_bitrate_limits)
+ return false;
+ return true;
+}
+
+bool operator==(
+ const ::media::VideoEncodeAccelerator::Config::SpatialLayer& l,
+ const ::media::VideoEncodeAccelerator::Config::SpatialLayer& r) {
+ return (l.width == r.width && l.height == r.height &&
+ l.bitrate_bps == r.bitrate_bps && l.framerate == r.framerate &&
+ l.max_qp == r.max_qp &&
+ l.num_of_temporal_layers == r.num_of_temporal_layers);
+}
+
+bool operator==(const ::media::VideoEncodeAccelerator::Config& l,
+ const ::media::VideoEncodeAccelerator::Config& r) {
+ return l.input_format == r.input_format &&
+ l.input_visible_size == r.input_visible_size &&
+ l.output_profile == r.output_profile &&
+ l.initial_bitrate == r.initial_bitrate &&
+ l.initial_framerate == r.initial_framerate &&
+ l.gop_length == r.gop_length &&
+ l.h264_output_level == r.h264_output_level &&
+ l.storage_type == r.storage_type && l.content_type == r.content_type &&
+ l.spatial_layers == r.spatial_layers;
+}
+
+TEST(VideoEncoderInfoStructTraitTest, RoundTrip) {
+ ::media::VideoEncoderInfo input;
+ input.implementation_name = "FakeVideoEncodeAccelerator";
+ // Scaling settings.
+ input.scaling_settings = ::media::ScalingSettings(12, 123);
+ // FPS allocation.
+ for (size_t i = 0; i < ::media::VideoEncoderInfo::kMaxSpatialLayers; ++i)
+ input.fps_allocation[i] = {5, 5, 10};
+ // Resolution bitrate limits.
+ input.resolution_bitrate_limits.push_back(::media::ResolutionBitrateLimit(
+ gfx::Size(123, 456), 123456, 123456, 789012));
+ input.resolution_bitrate_limits.push_back(::media::ResolutionBitrateLimit(
+ gfx::Size(789, 1234), 1234567, 1234567, 7890123));
+ // Other bool values.
+ input.supports_native_handle = true;
+ input.has_trusted_rate_controller = true;
+ input.is_hardware_accelerated = true;
+ input.supports_simulcast = true;
+
+ ::media::VideoEncoderInfo output = input;
+ ASSERT_TRUE(mojo::test::SerializeAndDeserialize<mojom::VideoEncoderInfo>(
+ &input, &output));
+ EXPECT_EQ(input, output);
+}
+
+TEST(SpatialLayerStructTraitTest, RoundTrip) {
+ ::media::VideoEncodeAccelerator::Config::SpatialLayer input_spatial_layer;
+ input_spatial_layer.width = 320;
+ input_spatial_layer.width = 180;
+ input_spatial_layer.bitrate_bps = 12345678u;
+ input_spatial_layer.framerate = 24u;
+ input_spatial_layer.max_qp = 30u;
+ input_spatial_layer.num_of_temporal_layers = 3u;
+ ::media::VideoEncodeAccelerator::Config::SpatialLayer output_spatial_layer;
+ ASSERT_TRUE(mojo::test::SerializeAndDeserialize<mojom::SpatialLayer>(
+ &input_spatial_layer, &output_spatial_layer));
+ EXPECT_EQ(input_spatial_layer, output_spatial_layer);
+}
+
+TEST(VideoEncodeAcceleratorConfigStructTraitTest, RoundTrip) {
+ std::vector<::media::VideoEncodeAccelerator::Config::SpatialLayer>
+ input_spatial_layers(3);
+ gfx::Size kBaseSize(320, 180);
+ uint32_t kBaseBitrateBps = 123456u;
+ uint32_t kBaseFramerate = 24u;
+ for (size_t i = 0; i < input_spatial_layers.size(); ++i) {
+ input_spatial_layers[i].width =
+ static_cast<int32_t>(kBaseSize.width() * (i + 1));
+ input_spatial_layers[i].height =
+ static_cast<int32_t>(kBaseSize.height() * (i + 1));
+ input_spatial_layers[i].bitrate_bps = kBaseBitrateBps * (i + 1) / 2;
+ input_spatial_layers[i].framerate = kBaseFramerate * 2 / (i + 1);
+ input_spatial_layers[i].max_qp = 30 * (i + 1) / 2;
+ input_spatial_layers[i].num_of_temporal_layers = 3 - i;
+ }
+ ::media::VideoEncodeAccelerator::Config input_config(
+ ::media::PIXEL_FORMAT_NV12, kBaseSize, ::media::VP9PROFILE_PROFILE0,
+ kBaseBitrateBps, kBaseFramerate, base::nullopt, base::nullopt,
+ ::media::VideoEncodeAccelerator::Config::StorageType::kDmabuf,
+ ::media::VideoEncodeAccelerator::Config::ContentType::kCamera,
+ input_spatial_layers);
+ DVLOG(4) << input_config.AsHumanReadableString();
+
+ ::media::VideoEncodeAccelerator::Config output_config{};
+ ASSERT_TRUE(
+ mojo::test::SerializeAndDeserialize<mojom::VideoEncodeAcceleratorConfig>(
+ &input_config, &output_config));
+ DVLOG(4) << output_config.AsHumanReadableString();
+ EXPECT_EQ(input_config, output_config);
+}
+} // namespace media
diff --git a/chromium/media/mojo/mojom/video_encoder_info.mojom b/chromium/media/mojo/mojom/video_encoder_info.mojom
index e02bead05ae..a299ca7f84e 100644
--- a/chromium/media/mojo/mojom/video_encoder_info.mojom
+++ b/chromium/media/mojo/mojom/video_encoder_info.mojom
@@ -26,7 +26,7 @@ struct VideoEncoderInfo {
bool is_hardware_accelerated;
bool supports_simulcast;
- ScalingSettings scaling_settings;
+ ScalingSettings? scaling_settings;
// This array size is equal to media::VideoEncoderInfo::kMaxSpatialLayers.
array<array<uint8>, 5> fps_allocation;
array<ResolutionBitrateLimit> resolution_bitrate_limits;
diff --git a/chromium/media/mojo/mojom/video_encoder_info_mojom_traits.h b/chromium/media/mojo/mojom/video_encoder_info_mojom_traits.h
index be13d828f3e..7489a2489c8 100644
--- a/chromium/media/mojo/mojom/video_encoder_info_mojom_traits.h
+++ b/chromium/media/mojo/mojom/video_encoder_info_mojom_traits.h
@@ -75,7 +75,7 @@ class StructTraits<media::mojom::VideoEncoderInfoDataView,
const media::VideoEncoderInfo& video_encoder_info) {
return video_encoder_info.supports_simulcast;
}
- static const media::ScalingSettings& scaling_settings(
+ static const base::Optional<media::ScalingSettings>& scaling_settings(
const media::VideoEncoderInfo& video_encoder_info) {
return video_encoder_info.scaling_settings;
}
diff --git a/chromium/media/mojo/mojom/video_encoder_info_mojom_traits_unittest.cc b/chromium/media/mojo/mojom/video_encoder_info_mojom_traits_unittest.cc
deleted file mode 100644
index 455ac3bd893..00000000000
--- a/chromium/media/mojo/mojom/video_encoder_info_mojom_traits_unittest.cc
+++ /dev/null
@@ -1,88 +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.
-
-#include "media/mojo/mojom/video_encoder_info_mojom_traits.h"
-
-#include "media/video/video_encoder_info.h"
-
-#include "mojo/public/cpp/test_support/test_utils.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace media {
-
-// These binary operators are implemented here because they are used in this
-// unittest. They cannot be enclosed by anonymous namespace, because they must
-// be visible by gtest in linking.
-bool operator==(const ::media::ScalingSettings& l,
- const ::media::ScalingSettings& r) {
- return l.min_qp == r.min_qp && l.max_qp == r.max_qp;
-}
-
-bool operator!=(const ::media::ScalingSettings& l,
- const ::media::ScalingSettings& r) {
- return !(l == r);
-}
-
-bool operator==(const ::media::ResolutionBitrateLimit& l,
- const ::media::ResolutionBitrateLimit& r) {
- return (l.frame_size == r.frame_size &&
- l.min_start_bitrate_bps == r.min_start_bitrate_bps &&
- l.min_bitrate_bps == r.min_bitrate_bps &&
- l.max_bitrate_bps == r.max_bitrate_bps);
-}
-
-bool operator!=(const ::media::ResolutionBitrateLimit& l,
- const ::media::ResolutionBitrateLimit& r) {
- return !(l == r);
-}
-
-bool operator==(const ::media::VideoEncoderInfo& l,
- const ::media::VideoEncoderInfo& r) {
- if (l.implementation_name != r.implementation_name)
- return false;
- if (l.supports_native_handle != r.supports_native_handle)
- return false;
- if (l.has_trusted_rate_controller != r.has_trusted_rate_controller)
- return false;
- if (l.is_hardware_accelerated != r.is_hardware_accelerated)
- return false;
- if (l.supports_simulcast != r.supports_simulcast)
- return false;
- if (l.scaling_settings != r.scaling_settings)
- return false;
- for (size_t i = 0; i < ::media::VideoEncoderInfo::kMaxSpatialLayers; ++i) {
- if (l.fps_allocation[i] != r.fps_allocation[i])
- return false;
- }
- if (l.resolution_bitrate_limits != r.resolution_bitrate_limits)
- return false;
- return true;
-}
-
-TEST(VideoEncoderInfoStructTraitTest, RoundTrip) {
- ::media::VideoEncoderInfo input;
- input.implementation_name = "FakeVideoEncodeAccelerator";
- // Scaling settings.
- input.scaling_settings.min_qp = 12;
- input.scaling_settings.max_qp = 123;
- // FPS allocation.
- for (size_t i = 0; i < ::media::VideoEncoderInfo::kMaxSpatialLayers; ++i)
- input.fps_allocation[i] = {5, 5, 10};
- // Resolution bitrate limits.
- input.resolution_bitrate_limits.push_back(::media::ResolutionBitrateLimit(
- gfx::Size(123, 456), 123456, 123456, 789012));
- input.resolution_bitrate_limits.push_back(::media::ResolutionBitrateLimit(
- gfx::Size(789, 1234), 1234567, 1234567, 7890123));
- // Other bool values.
- input.supports_native_handle = true;
- input.has_trusted_rate_controller = true;
- input.is_hardware_accelerated = true;
- input.supports_simulcast = true;
-
- ::media::VideoEncoderInfo output = input;
- ASSERT_TRUE(mojo::test::SerializeAndDeserialize<mojom::VideoEncoderInfo>(
- &input, &output));
- EXPECT_EQ(input, output);
-}
-} // namespace media
diff --git a/chromium/media/mojo/mojom/video_frame_mojom_traits.cc b/chromium/media/mojo/mojom/video_frame_mojom_traits.cc
index 464be5dd0cf..b2c93afe712 100644
--- a/chromium/media/mojo/mojom/video_frame_mojom_traits.cc
+++ b/chromium/media/mojo/mojom/video_frame_mojom_traits.cc
@@ -14,6 +14,7 @@
#include "media/base/color_plane_layout.h"
#include "media/base/format_utils.h"
#include "media/mojo/common/mojo_shared_buffer_video_frame.h"
+#include "media/mojo/mojom/hdr_metadata_mojom_traits.h"
#include "mojo/public/cpp/base/time_mojom_traits.h"
#include "mojo/public/cpp/base/values_mojom_traits.h"
#include "mojo/public/cpp/system/handle.h"
@@ -285,6 +286,11 @@ bool StructTraits<media::mojom::VideoFrameDataView,
return false;
frame->set_color_space(color_space);
+ base::Optional<media::HDRMetadata> hdr_metadata;
+ if (!input.ReadHdrMetadata(&hdr_metadata))
+ return false;
+ frame->set_hdr_metadata(std::move(hdr_metadata));
+
*output = std::move(frame);
return true;
}
diff --git a/chromium/media/mojo/mojom/video_frame_mojom_traits.h b/chromium/media/mojo/mojom/video_frame_mojom_traits.h
index 196243b4817..c064e76b79a 100644
--- a/chromium/media/mojo/mojom/video_frame_mojom_traits.h
+++ b/chromium/media/mojo/mojom/video_frame_mojom_traits.h
@@ -62,6 +62,11 @@ struct StructTraits<media::mojom::VideoFrameDataView,
return input->ColorSpace();
}
+ static const base::Optional<media::HDRMetadata>& hdr_metadata(
+ const scoped_refptr<media::VideoFrame>& input) {
+ return input->hdr_metadata();
+ }
+
static const base::Optional<gpu::VulkanYCbCrInfo>& ycbcr_info(
const scoped_refptr<media::VideoFrame>& input) {
return input->ycbcr_info();
diff --git a/chromium/media/mojo/services/BUILD.gn b/chromium/media/mojo/services/BUILD.gn
index 89d10b71f40..3f39a35e113 100644
--- a/chromium/media/mojo/services/BUILD.gn
+++ b/chromium/media/mojo/services/BUILD.gn
@@ -15,8 +15,6 @@ jumbo_component("services") {
"gpu_mojo_media_client.h",
"interface_factory_impl.cc",
"interface_factory_impl.h",
- "media_interface_provider.cc",
- "media_interface_provider.h",
"media_metrics_provider.cc",
"media_metrics_provider.h",
"media_mojo_export.h",
@@ -111,6 +109,10 @@ jumbo_component("services") {
]
}
+ if (is_fuchsia) {
+ deps += [ "//media/fuchsia/metrics" ]
+ }
+
if (enable_media_drm_storage) {
sources += [
"mojo_media_drm_storage.cc",
@@ -134,19 +136,6 @@ jumbo_component("services") {
"//media/cdm:cdm_paths",
]
- if (enable_cdm_proxy) {
- sources += [
- "mojo_cdm_proxy.cc",
- "mojo_cdm_proxy.h",
- "mojo_cdm_proxy_service.cc",
- "mojo_cdm_proxy_service.h",
- ]
- deps += [
- # Needed by test_mojo_media_client.cc to create ClearKeyCdmProxy.
- "//media/cdm/library_cdm/clear_key_cdm:clear_key_cdm_proxy",
- ]
- }
-
# TODO(xhwang): Ideally media should not worry about sandbox. Find a way to
# remove this dependency.
if (is_mac) {
@@ -202,10 +191,6 @@ source_set("unit_tests") {
"mojo_cdm_helper_unittest.cc",
]
- if (enable_cdm_proxy) {
- sources += [ "mojo_cdm_proxy_unittest.cc" ]
- }
-
deps += [ "//media/cdm:cdm_api" ]
}
diff --git a/chromium/media/mojo/services/android_mojo_media_client.cc b/chromium/media/mojo/services/android_mojo_media_client.cc
index aae7227cafb..fee056b790f 100644
--- a/chromium/media/mojo/services/android_mojo_media_client.cc
+++ b/chromium/media/mojo/services/android_mojo_media_client.cc
@@ -34,16 +34,16 @@ std::unique_ptr<AudioDecoder> AndroidMojoMediaClient::CreateAudioDecoder(
}
std::unique_ptr<CdmFactory> AndroidMojoMediaClient::CreateCdmFactory(
- service_manager::mojom::InterfaceProvider* host_interfaces) {
- if (!host_interfaces) {
+ mojom::FrameInterfaceFactory* frame_interfaces) {
+ if (!frame_interfaces) {
NOTREACHED() << "Host interfaces should be provided when using CDM with "
<< "AndroidMojoMediaClient";
return nullptr;
}
return std::make_unique<AndroidCdmFactory>(
- base::BindRepeating(&CreateProvisionFetcher, host_interfaces),
- base::BindRepeating(&CreateMediaDrmStorage, host_interfaces));
+ base::BindRepeating(&CreateProvisionFetcher, frame_interfaces),
+ base::BindRepeating(&CreateMediaDrmStorage, frame_interfaces));
}
} // namespace media
diff --git a/chromium/media/mojo/services/android_mojo_media_client.h b/chromium/media/mojo/services/android_mojo_media_client.h
index 346a7081648..4ab6dc31782 100644
--- a/chromium/media/mojo/services/android_mojo_media_client.h
+++ b/chromium/media/mojo/services/android_mojo_media_client.h
@@ -23,7 +23,7 @@ class AndroidMojoMediaClient : public MojoMediaClient {
scoped_refptr<base::SingleThreadTaskRunner> task_runner) final;
std::unique_ptr<CdmFactory> CreateCdmFactory(
- service_manager::mojom::InterfaceProvider* host_interfaces) final;
+ mojom::FrameInterfaceFactory* frame_interfaces) final;
private:
DISALLOW_COPY_AND_ASSIGN(AndroidMojoMediaClient);
diff --git a/chromium/media/mojo/services/android_mojo_util.cc b/chromium/media/mojo/services/android_mojo_util.cc
index 6b5a31e43fb..f91415e62a8 100644
--- a/chromium/media/mojo/services/android_mojo_util.cc
+++ b/chromium/media/mojo/services/android_mojo_util.cc
@@ -6,28 +6,25 @@
#include "media/mojo/services/mojo_media_drm_storage.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
-#include "services/service_manager/public/mojom/interface_provider.mojom.h"
namespace media {
namespace android_mojo_util {
std::unique_ptr<ProvisionFetcher> CreateProvisionFetcher(
- service_manager::mojom::InterfaceProvider* host_interfaces) {
- DCHECK(host_interfaces);
+ media::mojom::FrameInterfaceFactory* frame_interfaces) {
+ DCHECK(frame_interfaces);
mojo::PendingRemote<mojom::ProvisionFetcher> provision_fetcher;
- host_interfaces->GetInterface(
- mojom::ProvisionFetcher::Name_,
- provision_fetcher.InitWithNewPipeAndPassReceiver().PassPipe());
+ frame_interfaces->CreateProvisionFetcher(
+ provision_fetcher.InitWithNewPipeAndPassReceiver());
return std::make_unique<MojoProvisionFetcher>(std::move(provision_fetcher));
}
std::unique_ptr<MediaDrmStorage> CreateMediaDrmStorage(
- service_manager::mojom::InterfaceProvider* host_interfaces) {
- DCHECK(host_interfaces);
+ media::mojom::FrameInterfaceFactory* frame_interfaces) {
+ DCHECK(frame_interfaces);
mojo::PendingRemote<mojom::MediaDrmStorage> media_drm_storage;
- host_interfaces->GetInterface(
- mojom::MediaDrmStorage::Name_,
- media_drm_storage.InitWithNewPipeAndPassReceiver().PassPipe());
+ frame_interfaces->BindEmbedderReceiver(mojo::GenericPendingReceiver(
+ media_drm_storage.InitWithNewPipeAndPassReceiver()));
return std::make_unique<MojoMediaDrmStorage>(std::move(media_drm_storage));
}
diff --git a/chromium/media/mojo/services/android_mojo_util.h b/chromium/media/mojo/services/android_mojo_util.h
index 62f17839c18..3a41f51e251 100644
--- a/chromium/media/mojo/services/android_mojo_util.h
+++ b/chromium/media/mojo/services/android_mojo_util.h
@@ -7,25 +7,18 @@
#include <memory>
+#include "media/mojo/mojom/frame_interface_factory.mojom.h"
#include "media/mojo/services/mojo_media_drm_storage.h"
#include "media/mojo/services/mojo_provision_fetcher.h"
-namespace service_manager {
-namespace mojom {
-
-class InterfaceProvider;
-
-} // namespace mojom
-} // namespace service_manager
-
namespace media {
namespace android_mojo_util {
std::unique_ptr<ProvisionFetcher> CreateProvisionFetcher(
- service_manager::mojom::InterfaceProvider* host_interfaces);
+ media::mojom::FrameInterfaceFactory* frame_interfaces);
std::unique_ptr<MediaDrmStorage> CreateMediaDrmStorage(
- service_manager::mojom::InterfaceProvider* host_interfaces);
+ media::mojom::FrameInterfaceFactory* frame_interfaces);
} // namespace android_mojo_util
} // namespace media
diff --git a/chromium/media/mojo/services/cdm_service.cc b/chromium/media/mojo/services/cdm_service.cc
index 06a61b8a1e8..7601bfdfda4 100644
--- a/chromium/media/mojo/services/cdm_service.cc
+++ b/chromium/media/mojo/services/cdm_service.cc
@@ -41,9 +41,8 @@ namespace {
// details.
class CdmFactoryImpl : public DeferredDestroy<mojom::CdmFactory> {
public:
- CdmFactoryImpl(
- CdmService::Client* client,
- mojo::PendingRemote<service_manager::mojom::InterfaceProvider> interfaces)
+ CdmFactoryImpl(CdmService::Client* client,
+ mojo::PendingRemote<mojom::FrameInterfaceFactory> interfaces)
: client_(client), interfaces_(std::move(interfaces)) {
DVLOG(1) << __func__;
@@ -99,7 +98,7 @@ class CdmFactoryImpl : public DeferredDestroy<mojom::CdmFactory> {
MojoCdmServiceContext cdm_service_context_;
CdmService::Client* client_;
- mojo::Remote<service_manager::mojom::InterfaceProvider> interfaces_;
+ mojo::Remote<mojom::FrameInterfaceFactory> interfaces_;
mojo::UniqueReceiverSet<mojom::ContentDecryptionModule> cdm_receivers_;
std::unique_ptr<media::CdmFactory> cdm_factory_;
base::OnceClosure destroy_cb_;
@@ -183,15 +182,14 @@ void CdmService::LoadCdm(const base::FilePath& cdm_path) {
void CdmService::CreateCdmFactory(
mojo::PendingReceiver<mojom::CdmFactory> receiver,
- mojo::PendingRemote<service_manager::mojom::InterfaceProvider>
- host_interfaces) {
+ mojo::PendingRemote<mojom::FrameInterfaceFactory> frame_interfaces) {
// Ignore receiver if service has already stopped.
if (!client_)
return;
cdm_factory_receivers_.AddReceiver(
std::make_unique<CdmFactoryImpl>(client_.get(),
- std::move(host_interfaces)),
+ std::move(frame_interfaces)),
std::move(receiver));
}
diff --git a/chromium/media/mojo/services/cdm_service.h b/chromium/media/mojo/services/cdm_service.h
index 23f683b33f2..f85cb1af521 100644
--- a/chromium/media/mojo/services/cdm_service.h
+++ b/chromium/media/mojo/services/cdm_service.h
@@ -12,12 +12,12 @@
#include "media/media_buildflags.h"
#include "media/mojo/mojom/cdm_service.mojom.h"
#include "media/mojo/mojom/content_decryption_module.mojom.h"
+#include "media/mojo/mojom/frame_interface_factory.mojom.h"
#include "media/mojo/services/deferred_destroy_unique_receiver_set.h"
#include "media/mojo/services/media_mojo_export.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
-#include "services/service_manager/public/mojom/interface_provider.mojom.h"
#if BUILDFLAG(ENABLE_CDM_HOST_VERIFICATION)
#include "media/cdm/cdm_host_file.h"
@@ -37,11 +37,11 @@ class MEDIA_MOJO_EXPORT CdmService : public mojom::CdmService {
// be a no-op if the process is already sandboxed.
virtual void EnsureSandboxed() = 0;
- // Returns the CdmFactory to be used by MojoCdmService. |host_interfaces|
+ // Returns the CdmFactory to be used by MojoCdmService. |frame_interfaces|
// can be used to request interfaces provided remotely by the host. It may
// be a nullptr if the host chose not to bind the InterfacePtr.
virtual std::unique_ptr<CdmFactory> CreateCdmFactory(
- service_manager::mojom::InterfaceProvider* host_interfaces) = 0;
+ mojom::FrameInterfaceFactory* frame_interfaces) = 0;
#if BUILDFLAG(ENABLE_CDM_HOST_VERIFICATION)
// Gets a list of CDM host file paths and put them in |cdm_host_file_paths|.
@@ -73,8 +73,7 @@ class MEDIA_MOJO_EXPORT CdmService : public mojom::CdmService {
#endif // defined(OS_MACOSX)
void CreateCdmFactory(
mojo::PendingReceiver<mojom::CdmFactory> receiver,
- mojo::PendingRemote<service_manager::mojom::InterfaceProvider>
- host_interfaces) final;
+ mojo::PendingRemote<mojom::FrameInterfaceFactory> frame_interfaces) final;
mojo::Receiver<mojom::CdmService> receiver_;
std::unique_ptr<Client> client_;
diff --git a/chromium/media/mojo/services/cdm_service_unittest.cc b/chromium/media/mojo/services/cdm_service_unittest.cc
index 3190e5d3e1b..02aec3d22e6 100644
--- a/chromium/media/mojo/services/cdm_service_unittest.cc
+++ b/chromium/media/mojo/services/cdm_service_unittest.cc
@@ -12,7 +12,6 @@
#include "media/cdm/default_cdm_factory.h"
#include "media/media_buildflags.h"
#include "media/mojo/services/cdm_service.h"
-#include "media/mojo/services/media_interface_provider.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -45,7 +44,7 @@ class MockCdmServiceClient : public media::CdmService::Client {
MOCK_METHOD0(EnsureSandboxed, void());
std::unique_ptr<media::CdmFactory> CreateCdmFactory(
- service_manager::mojom::InterfaceProvider* host_interfaces) override {
+ mojom::FrameInterfaceFactory* frame_interfaces) override {
return std::make_unique<media::DefaultCdmFactory>();
}
@@ -74,9 +73,8 @@ class CdmServiceTest : public testing::Test {
base::TimeDelta(), base::BindRepeating(&CdmServiceTest::CdmServiceIdle,
base::Unretained(this)));
- mojo::PendingRemote<service_manager::mojom::InterfaceProvider> interfaces;
- auto provider = std::make_unique<MediaInterfaceProvider>(
- interfaces.InitWithNewPipeAndPassReceiver());
+ mojo::PendingRemote<mojom::FrameInterfaceFactory> interfaces;
+ ignore_result(interfaces.InitWithNewPipeAndPassReceiver());
ASSERT_FALSE(cdm_factory_remote_);
cdm_service_remote_->CreateCdmFactory(
diff --git a/chromium/media/mojo/services/gpu_mojo_media_client.cc b/chromium/media/mojo/services/gpu_mojo_media_client.cc
index 5074b4350a0..293591767dc 100644
--- a/chromium/media/mojo/services/gpu_mojo_media_client.cc
+++ b/chromium/media/mojo/services/gpu_mojo_media_client.cc
@@ -42,6 +42,7 @@
#if defined(OS_WIN)
#include "media/gpu/windows/d3d11_video_decoder.h"
+#include "ui/gl/direct_composition_surface_win.h"
#include "ui/gl/gl_angle_util_win.h"
#endif // defined(OS_WIN)
@@ -117,18 +118,18 @@ GpuMojoMediaClient::GpuMojoMediaClient(
scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,
base::WeakPtr<MediaGpuChannelManager> media_gpu_channel_manager,
gpu::GpuMemoryBufferFactory* gpu_memory_buffer_factory,
- AndroidOverlayMojoFactoryCB android_overlay_factory_cb,
- CdmProxyFactoryCB cdm_proxy_factory_cb)
+ AndroidOverlayMojoFactoryCB android_overlay_factory_cb)
: gpu_preferences_(gpu_preferences),
gpu_workarounds_(gpu_workarounds),
gpu_feature_info_(gpu_feature_info),
gpu_task_runner_(std::move(gpu_task_runner)),
media_gpu_channel_manager_(std::move(media_gpu_channel_manager)),
- android_overlay_factory_cb_(std::move(android_overlay_factory_cb)),
+ android_overlay_factory_cb_(std::move(android_overlay_factory_cb))
#if BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)
- gpu_memory_buffer_factory_(gpu_memory_buffer_factory),
+ ,
+ gpu_memory_buffer_factory_(gpu_memory_buffer_factory)
#endif // BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)
- cdm_proxy_factory_cb_(std::move(cdm_proxy_factory_cb)) {
+{
}
GpuMojoMediaClient::~GpuMojoMediaClient() = default;
@@ -240,8 +241,8 @@ std::unique_ptr<VideoDecoder> GpuMojoMediaClient::CreateVideoDecoder(
// ignored. If we can tell that here, then VideoFrameFactory can use it
// as a signal about whether it's supposed to get YCbCrInfo rather than
// requiring the provider to set |is_vulkan| in the ImageRecord.
- auto ycbcr_helper =
- YCbCrHelper::Create(gpu_task_runner_, std::move(get_stub_cb));
+ auto frame_info_helper =
+ FrameInfoHelper::Create(gpu_task_runner_, std::move(get_stub_cb));
video_decoder = std::make_unique<MediaCodecVideoDecoder>(
gpu_preferences_, gpu_feature_info_, media_log->Clone(),
DeviceInfo::GetInstance(),
@@ -252,7 +253,7 @@ std::unique_ptr<VideoDecoder> GpuMojoMediaClient::CreateVideoDecoder(
std::make_unique<VideoFrameFactoryImpl>(
gpu_task_runner_, gpu_preferences_, std::move(image_provider),
MaybeRenderEarlyManager::Create(gpu_task_runner_),
- std::move(ycbcr_helper)));
+ std::move(frame_info_helper)));
#elif BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)
if (IsNewAcceleratedVideoDecoderUsed(gpu_preferences_)) {
@@ -309,7 +310,8 @@ std::unique_ptr<VideoDecoder> GpuMojoMediaClient::CreateVideoDecoder(
media_gpu_channel_manager_,
command_buffer_id->channel_token,
command_buffer_id->route_id),
- GetD3D11DeviceCallback(), *d3d11_supported_configs_);
+ GetD3D11DeviceCallback(), *d3d11_supported_configs_,
+ gl::DirectCompositionSurfaceWin::IsHDRSupported());
}
#endif // defined(OS_WIN)
break;
@@ -320,7 +322,7 @@ std::unique_ptr<VideoDecoder> GpuMojoMediaClient::CreateVideoDecoder(
}
std::unique_ptr<CdmFactory> GpuMojoMediaClient::CreateCdmFactory(
- service_manager::mojom::InterfaceProvider* interface_provider) {
+ mojom::FrameInterfaceFactory* interface_provider) {
#if defined(OS_ANDROID)
return std::make_unique<AndroidCdmFactory>(
base::BindRepeating(&CreateProvisionFetcher, interface_provider),
@@ -330,14 +332,4 @@ std::unique_ptr<CdmFactory> GpuMojoMediaClient::CreateCdmFactory(
#endif // defined(OS_ANDROID)
}
-#if BUILDFLAG(ENABLE_CDM_PROXY)
-std::unique_ptr<CdmProxy> GpuMojoMediaClient::CreateCdmProxy(
- const base::Token& cdm_guid) {
- if (cdm_proxy_factory_cb_)
- return cdm_proxy_factory_cb_.Run(cdm_guid);
-
- return nullptr;
-}
-#endif // BUILDFLAG(ENABLE_CDM_PROXY)
-
} // namespace media
diff --git a/chromium/media/mojo/services/gpu_mojo_media_client.h b/chromium/media/mojo/services/gpu_mojo_media_client.h
index 889fa7fd67d..e62511df65a 100644
--- a/chromium/media/mojo/services/gpu_mojo_media_client.h
+++ b/chromium/media/mojo/services/gpu_mojo_media_client.h
@@ -17,7 +17,6 @@
#include "gpu/config/gpu_feature_info.h"
#include "gpu/config/gpu_preferences.h"
#include "media/base/android_overlay_mojo_factory.h"
-#include "media/cdm/cdm_proxy.h"
#include "media/media_buildflags.h"
#include "media/mojo/services/mojo_media_client.h"
#include "media/video/supported_video_decoder_config.h"
@@ -34,8 +33,6 @@ class GpuMojoMediaClient : public MojoMediaClient {
public:
// |media_gpu_channel_manager| must only be used on |gpu_task_runner|, which
// is expected to be the GPU main thread task runner.
- // |cdm_proxy_factory_cb| can be used to create a CdmProxy. May be null if
- // CdmProxy is not supported on the platform.
GpuMojoMediaClient(
const gpu::GpuPreferences& gpu_preferences,
const gpu::GpuDriverBugWorkarounds& gpu_workarounds,
@@ -43,8 +40,7 @@ class GpuMojoMediaClient : public MojoMediaClient {
scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner,
base::WeakPtr<MediaGpuChannelManager> media_gpu_channel_manager,
gpu::GpuMemoryBufferFactory* gpu_memory_buffer_factory,
- AndroidOverlayMojoFactoryCB android_overlay_factory_cb,
- CdmProxyFactoryCB cdm_proxy_factory_cb);
+ AndroidOverlayMojoFactoryCB android_overlay_factory_cb);
~GpuMojoMediaClient() final;
// MojoMediaClient implementation.
@@ -59,10 +55,7 @@ class GpuMojoMediaClient : public MojoMediaClient {
RequestOverlayInfoCB request_overlay_info_cb,
const gfx::ColorSpace& target_color_space) final;
std::unique_ptr<CdmFactory> CreateCdmFactory(
- service_manager::mojom::InterfaceProvider* interface_provider) final;
-#if BUILDFLAG(ENABLE_CDM_PROXY)
- std::unique_ptr<CdmProxy> CreateCdmProxy(const base::Token& cdm_guid) final;
-#endif // BUILDFLAG(ENABLE_CDM_PROXY)
+ mojom::FrameInterfaceFactory* interface_provider) final;
private:
gpu::GpuPreferences gpu_preferences_;
@@ -76,7 +69,6 @@ class GpuMojoMediaClient : public MojoMediaClient {
gpu::GpuMemoryBufferFactory* const gpu_memory_buffer_factory_;
base::Optional<SupportedVideoDecoderConfigs> cros_supported_configs_;
#endif // BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)
- CdmProxyFactoryCB cdm_proxy_factory_cb_;
#if defined(OS_WIN)
base::Optional<SupportedVideoDecoderConfigs> d3d11_supported_configs_;
#endif // defined(OS_WIN)
diff --git a/chromium/media/mojo/services/interface_factory_impl.cc b/chromium/media/mojo/services/interface_factory_impl.cc
index 4b07b310208..d6a15e06943 100644
--- a/chromium/media/mojo/services/interface_factory_impl.cc
+++ b/chromium/media/mojo/services/interface_factory_impl.cc
@@ -14,7 +14,6 @@
#include "media/mojo/mojom/renderer_extensions.mojom.h"
#include "media/mojo/services/mojo_decryptor_service.h"
#include "media/mojo/services/mojo_media_client.h"
-#include "services/service_manager/public/mojom/interface_provider.mojom.h"
#if BUILDFLAG(ENABLE_MOJO_AUDIO_DECODER)
#include "media/mojo/services/mojo_audio_decoder_service.h"
@@ -35,17 +34,12 @@
#include "media/mojo/services/mojo_cdm_service.h"
#endif // BUILDFLAG(ENABLE_MOJO_CDM)
-#if BUILDFLAG(ENABLE_CDM_PROXY)
-#include "media/mojo/services/mojo_cdm_proxy_service.h"
-#endif // BUILDFLAG(ENABLE_CDM_PROXY)
-
namespace media {
InterfaceFactoryImpl::InterfaceFactoryImpl(
- mojo::PendingRemote<service_manager::mojom::InterfaceProvider>
- host_interfaces,
+ mojo::PendingRemote<mojom::FrameInterfaceFactory> frame_interfaces,
MojoMediaClient* mojo_media_client)
- : host_interfaces_(std::move(host_interfaces)),
+ : frame_interfaces_(std::move(frame_interfaces)),
mojo_media_client_(mojo_media_client) {
DVLOG(1) << __func__;
DCHECK(mojo_media_client_);
@@ -96,7 +90,7 @@ void InterfaceFactoryImpl::CreateDefaultRenderer(
DVLOG(2) << __func__;
#if BUILDFLAG(ENABLE_MOJO_RENDERER)
auto renderer = mojo_media_client_->CreateRenderer(
- host_interfaces_.get(), base::ThreadTaskRunnerHandle::Get(), &media_log_,
+ frame_interfaces_.get(), base::ThreadTaskRunnerHandle::Get(), &media_log_,
audio_device_id);
if (!renderer) {
DLOG(ERROR) << "Renderer creation failed.";
@@ -126,7 +120,7 @@ void InterfaceFactoryImpl::CreateCastRenderer(
mojo::PendingReceiver<media::mojom::Renderer> receiver) {
DVLOG(2) << __func__;
auto renderer = mojo_media_client_->CreateCastRenderer(
- host_interfaces_.get(), base::ThreadTaskRunnerHandle::Get(), &media_log_,
+ frame_interfaces_.get(), base::ThreadTaskRunnerHandle::Get(), &media_log_,
overlay_plane_id);
if (!renderer) {
DLOG(ERROR) << "Renderer creation failed.";
@@ -184,37 +178,6 @@ void InterfaceFactoryImpl::CreateCdm(
#endif // BUILDFLAG(ENABLE_MOJO_CDM)
}
-void InterfaceFactoryImpl::CreateDecryptor(
- int cdm_id,
- mojo::PendingReceiver<mojom::Decryptor> receiver) {
- DVLOG(2) << __func__;
- auto mojo_decryptor_service =
- MojoDecryptorService::Create(cdm_id, &cdm_service_context_);
- if (!mojo_decryptor_service) {
- DLOG(ERROR) << "MojoDecryptorService creation failed.";
- return;
- }
-
- decryptor_receivers_.Add(std::move(mojo_decryptor_service),
- std::move(receiver));
-}
-#if BUILDFLAG(ENABLE_CDM_PROXY)
-void InterfaceFactoryImpl::CreateCdmProxy(
- const base::Token& cdm_guid,
- mojo::PendingReceiver<mojom::CdmProxy> receiver) {
- DVLOG(2) << __func__;
- auto cdm_proxy = mojo_media_client_->CreateCdmProxy(cdm_guid);
- if (!cdm_proxy) {
- DLOG(ERROR) << "CdmProxy creation failed.";
- return;
- }
-
- cdm_proxy_receivers_.Add(std::make_unique<MojoCdmProxyService>(
- std::move(cdm_proxy), &cdm_service_context_),
- std::move(receiver));
-}
-#endif // BUILDFLAG(ENABLE_CDM_PROXY)
-
void InterfaceFactoryImpl::OnDestroyPending(base::OnceClosure destroy_cb) {
DVLOG(1) << __func__;
destroy_cb_ = std::move(destroy_cb);
@@ -244,11 +207,6 @@ bool InterfaceFactoryImpl::IsEmpty() {
return false;
#endif // BUILDFLAG(ENABLE_MOJO_CDM)
-#if BUILDFLAG(ENABLE_CDM_PROXY)
- if (!cdm_proxy_receivers_.empty())
- return false;
-#endif // BUILDFLAG(ENABLE_CDM_PROXY)
-
if (!decryptor_receivers_.empty())
return false;
@@ -278,10 +236,6 @@ void InterfaceFactoryImpl::SetReceiverDisconnectHandler() {
cdm_receivers_.set_disconnect_handler(disconnect_cb);
#endif // BUILDFLAG(ENABLE_MOJO_CDM)
-#if BUILDFLAG(ENABLE_CDM_PROXY)
- cdm_proxy_receivers_.set_disconnect_handler(disconnect_cb);
-#endif // BUILDFLAG(ENABLE_CDM_PROXY)
-
decryptor_receivers_.set_disconnect_handler(disconnect_cb);
}
@@ -294,7 +248,8 @@ void InterfaceFactoryImpl::OnReceiverDisconnect() {
#if BUILDFLAG(ENABLE_MOJO_CDM)
CdmFactory* InterfaceFactoryImpl::GetCdmFactory() {
if (!cdm_factory_) {
- cdm_factory_ = mojo_media_client_->CreateCdmFactory(host_interfaces_.get());
+ cdm_factory_ =
+ mojo_media_client_->CreateCdmFactory(frame_interfaces_.get());
LOG_IF(ERROR, !cdm_factory_) << "CdmFactory not available.";
}
return cdm_factory_.get();
diff --git a/chromium/media/mojo/services/interface_factory_impl.h b/chromium/media/mojo/services/interface_factory_impl.h
index 79d71cb6e96..bf6d0e64b07 100644
--- a/chromium/media/mojo/services/interface_factory_impl.h
+++ b/chromium/media/mojo/services/interface_factory_impl.h
@@ -16,6 +16,7 @@
#include "media/mojo/mojom/audio_decoder.mojom.h"
#include "media/mojo/mojom/content_decryption_module.mojom.h"
#include "media/mojo/mojom/decryptor.mojom.h"
+#include "media/mojo/mojom/frame_interface_factory.mojom.h"
#include "media/mojo/mojom/interface_factory.mojom.h"
#include "media/mojo/mojom/renderer.mojom.h"
#include "media/mojo/mojom/video_decoder.mojom.h"
@@ -26,11 +27,6 @@
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/bindings/unique_receiver_set.h"
-#include "services/service_manager/public/mojom/interface_provider.mojom.h"
-
-#if BUILDFLAG(ENABLE_CDM_PROXY)
-#include "media/mojo/mojom/cdm_proxy.mojom.h"
-#endif // BUILDFLAG(ENABLE_CDM_PROXY)
namespace media {
@@ -40,8 +36,7 @@ class MojoMediaClient;
class InterfaceFactoryImpl : public DeferredDestroy<mojom::InterfaceFactory> {
public:
InterfaceFactoryImpl(
- mojo::PendingRemote<service_manager::mojom::InterfaceProvider>
- host_interfaces,
+ mojo::PendingRemote<mojom::FrameInterfaceFactory> frame_interfaces,
MojoMediaClient* mojo_media_client);
~InterfaceFactoryImpl() final;
@@ -74,12 +69,6 @@ class InterfaceFactoryImpl : public DeferredDestroy<mojom::InterfaceFactory> {
void CreateCdm(
const std::string& key_system,
mojo::PendingReceiver<mojom::ContentDecryptionModule> receiver) final;
- void CreateDecryptor(int cdm_id,
- mojo::PendingReceiver<mojom::Decryptor> receiver) final;
-#if BUILDFLAG(ENABLE_CDM_PROXY)
- void CreateCdmProxy(const base::Token& cdm_guid,
- mojo::PendingReceiver<mojom::CdmProxy> receiver) final;
-#endif // BUILDFLAG(ENABLE_CDM_PROXY)
// DeferredDestroy<mojom::InterfaceFactory> implemenation.
void OnDestroyPending(base::OnceClosure destroy_cb) final;
@@ -120,11 +109,7 @@ class InterfaceFactoryImpl : public DeferredDestroy<mojom::InterfaceFactory> {
mojo::UniqueReceiverSet<mojom::ContentDecryptionModule> cdm_receivers_;
#endif // BUILDFLAG(ENABLE_MOJO_CDM)
-#if BUILDFLAG(ENABLE_CDM_PROXY)
- mojo::UniqueReceiverSet<mojom::CdmProxy> cdm_proxy_receivers_;
-#endif // BUILDFLAG(ENABLE_CDM_PROXY)
-
- mojo::Remote<service_manager::mojom::InterfaceProvider> host_interfaces_;
+ mojo::Remote<mojom::FrameInterfaceFactory> frame_interfaces_;
mojo::UniqueReceiverSet<mojom::Decryptor> decryptor_receivers_;
diff --git a/chromium/media/mojo/services/media_interface_provider.cc b/chromium/media/mojo/services/media_interface_provider.cc
deleted file mode 100644
index 844078fca80..00000000000
--- a/chromium/media/mojo/services/media_interface_provider.cc
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2017 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/mojo/services/media_interface_provider.h"
-
-namespace media {
-
-MediaInterfaceProvider::MediaInterfaceProvider(
- mojo::PendingReceiver<service_manager::mojom::InterfaceProvider> receiver)
- : receiver_(this, std::move(receiver)) {}
-
-MediaInterfaceProvider::~MediaInterfaceProvider() = default;
-
-void MediaInterfaceProvider::GetInterface(
- const std::string& interface_name,
- mojo::ScopedMessagePipeHandle handle) {
- registry_.BindInterface(interface_name, std::move(handle));
-}
-
-} // namespace media
diff --git a/chromium/media/mojo/services/media_interface_provider.h b/chromium/media/mojo/services/media_interface_provider.h
deleted file mode 100644
index 26c1e2a652a..00000000000
--- a/chromium/media/mojo/services/media_interface_provider.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2017 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_MOJO_SERVICES_MEDIA_INTERFACE_PROVIDER_H_
-#define MEDIA_MOJO_SERVICES_MEDIA_INTERFACE_PROVIDER_H_
-
-#include "media/mojo/services/media_mojo_export.h"
-#include "mojo/public/cpp/bindings/pending_receiver.h"
-#include "mojo/public/cpp/bindings/receiver.h"
-#include "services/service_manager/public/cpp/binder_registry.h"
-#include "services/service_manager/public/mojom/interface_provider.mojom.h"
-
-namespace media {
-
-class MEDIA_MOJO_EXPORT MediaInterfaceProvider
- : public service_manager::mojom::InterfaceProvider {
- public:
- explicit MediaInterfaceProvider(
- mojo::PendingReceiver<service_manager::mojom::InterfaceProvider>
- receiver);
- ~MediaInterfaceProvider() override;
-
- service_manager::BinderRegistry* registry() { return &registry_; }
-
- private:
- // service_manager::mojom::InterfaceProvider:
- void GetInterface(const std::string& interface_name,
- mojo::ScopedMessagePipeHandle handle) override;
-
- service_manager::BinderRegistry registry_;
-
- mojo::Receiver<service_manager::mojom::InterfaceProvider> receiver_;
-
- DISALLOW_COPY_AND_ASSIGN(MediaInterfaceProvider);
-};
-
-} // namespace media
-
-#endif // MEDIA_MOJO_SERVICES_MEDIA_BINDER_REGISTRY_H_
diff --git a/chromium/media/mojo/services/media_metrics_provider.cc b/chromium/media/mojo/services/media_metrics_provider.cc
index f6cd6bedd49..e0920fce86b 100644
--- a/chromium/media/mojo/services/media_metrics_provider.cc
+++ b/chromium/media/mojo/services/media_metrics_provider.cc
@@ -20,7 +20,11 @@
#if !defined(OS_ANDROID)
#include "media/filters/decrypting_video_decoder.h"
-#endif
+#endif // !defined(OS_ANDROID)
+
+#if defined(OS_FUCHSIA)
+#include "media/fuchsia/metrics/fuchsia_playback_events_recorder.h"
+#endif // defined(OS_FUCHSIA)
namespace media {
@@ -290,6 +294,13 @@ void MediaMetricsProvider::AcquireVideoDecodeStatsRecorder(
std::move(receiver));
}
+void MediaMetricsProvider::AcquirePlaybackEventsRecorder(
+ mojo::PendingReceiver<mojom::PlaybackEventsRecorder> receiver) {
+#if defined(OS_FUCHSIA)
+ FuchsiaPlaybackEventsRecorder::Create(std::move(receiver));
+#endif
+}
+
void MediaMetricsProvider::AcquireLearningTaskController(
const std::string& taskName,
mojo::PendingReceiver<media::learning::mojom::LearningTaskController>
diff --git a/chromium/media/mojo/services/media_metrics_provider.h b/chromium/media/mojo/services/media_metrics_provider.h
index 4141ad2934a..c9857f2c771 100644
--- a/chromium/media/mojo/services/media_metrics_provider.h
+++ b/chromium/media/mojo/services/media_metrics_provider.h
@@ -121,6 +121,8 @@ class MEDIA_MOJO_EXPORT MediaMetricsProvider
mojo::PendingReceiver<mojom::WatchTimeRecorder> receiver) override;
void AcquireVideoDecodeStatsRecorder(
mojo::PendingReceiver<mojom::VideoDecodeStatsRecorder> receiver) override;
+ void AcquirePlaybackEventsRecorder(
+ mojo::PendingReceiver<mojom::PlaybackEventsRecorder> receiver) override;
void AcquireLearningTaskController(
const std::string& taskName,
mojo::PendingReceiver<media::learning::mojom::LearningTaskController>
diff --git a/chromium/media/mojo/services/media_service.cc b/chromium/media/mojo/services/media_service.cc
index c669262a3c8..2ff11ac5139 100644
--- a/chromium/media/mojo/services/media_service.cc
+++ b/chromium/media/mojo/services/media_service.cc
@@ -5,7 +5,7 @@
#include "media/mojo/services/media_service.h"
#include "base/bind.h"
-#include "base/logging.h"
+#include "base/check.h"
#include "media/media_buildflags.h"
#include "media/mojo/mojom/interface_factory.mojom.h"
#include "media/mojo/services/interface_factory_impl.h"
@@ -25,14 +25,13 @@ MediaService::~MediaService() = default;
void MediaService::CreateInterfaceFactory(
mojo::PendingReceiver<mojom::InterfaceFactory> receiver,
- mojo::PendingRemote<service_manager::mojom::InterfaceProvider>
- host_interfaces) {
+ mojo::PendingRemote<mojom::FrameInterfaceFactory> frame_interfaces) {
// Ignore request if service has already stopped.
if (!mojo_media_client_)
return;
interface_factory_receivers_.Add(
- std::make_unique<InterfaceFactoryImpl>(std::move(host_interfaces),
+ std::make_unique<InterfaceFactoryImpl>(std::move(frame_interfaces),
mojo_media_client_.get()),
std::move(receiver));
}
diff --git a/chromium/media/mojo/services/media_service.h b/chromium/media/mojo/services/media_service.h
index 9656a8a1064..01f72aec1eb 100644
--- a/chromium/media/mojo/services/media_service.h
+++ b/chromium/media/mojo/services/media_service.h
@@ -9,6 +9,7 @@
#include "base/macros.h"
#include "build/build_config.h"
+#include "media/mojo/mojom/frame_interface_factory.mojom.h"
#include "media/mojo/mojom/interface_factory.mojom.h"
#include "media/mojo/mojom/media_service.mojom.h"
#include "media/mojo/services/media_mojo_export.h"
@@ -16,7 +17,6 @@
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/unique_receiver_set.h"
-#include "services/service_manager/public/mojom/interface_provider.mojom.h"
namespace media {
@@ -34,8 +34,7 @@ class MEDIA_MOJO_EXPORT MediaService : public mojom::MediaService {
// mojom::MediaService implementation:
void CreateInterfaceFactory(
mojo::PendingReceiver<mojom::InterfaceFactory> receiver,
- mojo::PendingRemote<service_manager::mojom::InterfaceProvider>
- host_interfaces) final;
+ mojo::PendingRemote<mojom::FrameInterfaceFactory> frame_interfaces) final;
mojo::Receiver<mojom::MediaService> receiver_;
diff --git a/chromium/media/mojo/services/media_service_factory.cc b/chromium/media/mojo/services/media_service_factory.cc
index 249632aada1..af044a25cf7 100644
--- a/chromium/media/mojo/services/media_service_factory.cc
+++ b/chromium/media/mojo/services/media_service_factory.cc
@@ -6,7 +6,8 @@
#include <memory>
-#include "base/logging.h"
+#include "base/notreached.h"
+#include "build/build_config.h"
#include "media/mojo/buildflags.h"
#include "media/mojo/services/gpu_mojo_media_client.h"
#include "media/mojo/services/media_service.h"
@@ -20,9 +21,7 @@ namespace media {
std::unique_ptr<MediaService> CreateMediaService(
mojo::PendingReceiver<mojom::MediaService> receiver) {
-#if BUILDFLAG(ENABLE_TEST_MOJO_MEDIA_CLIENT)
- return CreateMediaServiceForTesting(std::move(receiver));
-#elif defined(OS_ANDROID)
+#if defined(OS_ANDROID)
return std::make_unique<MediaService>(
std::make_unique<AndroidMojoMediaClient>(), std::move(receiver));
#else
@@ -39,14 +38,12 @@ std::unique_ptr<MediaService> CreateGpuMediaService(
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
base::WeakPtr<MediaGpuChannelManager> media_gpu_channel_manager,
gpu::GpuMemoryBufferFactory* gpu_memory_buffer_factory,
- AndroidOverlayMojoFactoryCB android_overlay_factory_cb,
- CdmProxyFactoryCB cdm_proxy_factory_cb) {
+ AndroidOverlayMojoFactoryCB android_overlay_factory_cb) {
return std::make_unique<MediaService>(
std::make_unique<GpuMojoMediaClient>(
gpu_preferences, gpu_workarounds, gpu_feature_info, task_runner,
media_gpu_channel_manager, gpu_memory_buffer_factory,
- std::move(android_overlay_factory_cb),
- std::move(cdm_proxy_factory_cb)),
+ std::move(android_overlay_factory_cb)),
std::move(receiver));
}
diff --git a/chromium/media/mojo/services/media_service_factory.h b/chromium/media/mojo/services/media_service_factory.h
index cc55902e763..d0052bf2acf 100644
--- a/chromium/media/mojo/services/media_service_factory.h
+++ b/chromium/media/mojo/services/media_service_factory.h
@@ -14,7 +14,6 @@
#include "gpu/config/gpu_feature_info.h"
#include "gpu/config/gpu_preferences.h"
#include "media/base/android_overlay_mojo_factory.h"
-#include "media/cdm/cdm_proxy.h"
#include "media/mojo/mojom/media_service.mojom.h"
#include "media/mojo/services/media_mojo_export.h"
#include "media/mojo/services/media_service.h"
@@ -29,16 +28,13 @@ namespace media {
class MediaGpuChannelManager;
// Creates a MediaService instance using the default MojoMediaClient on each
-// platform. Uses the TestMojoMediaClient if |enable_test_mojo_media_client| is
-// true.
+// platform.
std::unique_ptr<MediaService> MEDIA_MOJO_EXPORT
CreateMediaService(mojo::PendingReceiver<mojom::MediaService> receiver);
// Creates a MediaService instance using the GpuMojoMediaClient.
// |media_gpu_channel_manager| must only be used on |task_runner|, which is
// expected to be the GPU main thread task runner.
-// |cdm_proxy_factory_cb| can be used to create a CdmProxy. May be null if
-// CdmProxy is not supported on the platform.
std::unique_ptr<MediaService> MEDIA_MOJO_EXPORT CreateGpuMediaService(
mojo::PendingReceiver<mojom::MediaService> receiver,
const gpu::GpuPreferences& gpu_preferences,
@@ -47,8 +43,7 @@ std::unique_ptr<MediaService> MEDIA_MOJO_EXPORT CreateGpuMediaService(
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
base::WeakPtr<MediaGpuChannelManager> media_gpu_channel_manager,
gpu::GpuMemoryBufferFactory* gpu_memory_buffer_factory,
- AndroidOverlayMojoFactoryCB android_overlay_factory_cb,
- CdmProxyFactoryCB cdm_proxy_factory_cb);
+ AndroidOverlayMojoFactoryCB android_overlay_factory_cb);
// Creates a MediaService instance using the TestMojoMediaClient.
std::unique_ptr<MediaService> MEDIA_MOJO_EXPORT CreateMediaServiceForTesting(
diff --git a/chromium/media/mojo/services/media_service_unittest.cc b/chromium/media/mojo/services/media_service_unittest.cc
index a0852d6b6c5..ef5f39687a0 100644
--- a/chromium/media/mojo/services/media_service_unittest.cc
+++ b/chromium/media/mojo/services/media_service_unittest.cc
@@ -27,7 +27,6 @@
#include "media/mojo/mojom/interface_factory.mojom.h"
#include "media/mojo/mojom/media_service.mojom.h"
#include "media/mojo/mojom/renderer.mojom.h"
-#include "media/mojo/services/media_interface_provider.h"
#include "media/mojo/services/media_service_factory.h"
#include "mojo/public/cpp/bindings/associated_receiver.h"
#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
@@ -38,11 +37,6 @@
#include "url/gurl.h"
#include "url/origin.h"
-#if BUILDFLAG(ENABLE_CDM_PROXY)
-#include "media/cdm/cdm_paths.h" // nogncheck
-#include "media/mojo/mojom/cdm_proxy.mojom.h"
-#endif
-
namespace media {
namespace {
@@ -67,28 +61,6 @@ const char kInvalidKeySystem[] = "invalid.key.system";
const char kSecurityOrigin[] = "https://foo.com";
-// Returns a trivial encrypted DecoderBuffer.
-scoped_refptr<DecoderBuffer> CreateEncryptedBuffer() {
- scoped_refptr<DecoderBuffer> encrypted_buffer(new DecoderBuffer(100));
- encrypted_buffer->set_decrypt_config(
- DecryptConfig::CreateCencConfig("dummy_key_id", "0123456789ABCDEF", {}));
- return encrypted_buffer;
-}
-
-#if BUILDFLAG(ENABLE_CDM_PROXY)
-class MockCdmProxyClient : public mojom::CdmProxyClient {
- public:
- MockCdmProxyClient() = default;
- ~MockCdmProxyClient() override = default;
-
- // mojom::CdmProxyClient implementation.
- MOCK_METHOD0(NotifyHardwareReset, void());
-
- private:
- DISALLOW_COPY_AND_ASSIGN(MockCdmProxyClient);
-};
-#endif // BUILDFLAG(ENABLE_CDM_PROXY)
-
class MockRendererClient : public mojom::RendererClient {
public:
MockRendererClient() = default;
@@ -127,20 +99,13 @@ ACTION_P(QuitLoop, run_loop) {
class MediaServiceTest : public testing::Test {
public:
MediaServiceTest()
- :
-#if BUILDFLAG(ENABLE_CDM_PROXY)
- cdm_proxy_client_receiver_(&cdm_proxy_client_),
-#endif
- renderer_client_receiver_(&renderer_client_),
- video_stream_(DemuxerStream::VIDEO) {
- }
+ : renderer_client_receiver_(&renderer_client_),
+ video_stream_(DemuxerStream::VIDEO) {}
~MediaServiceTest() override = default;
void SetUp() override {
- mojo::PendingRemote<service_manager::mojom::InterfaceProvider>
- host_interfaces;
- auto provider = std::make_unique<MediaInterfaceProvider>(
- host_interfaces.InitWithNewPipeAndPassReceiver());
+ mojo::PendingRemote<mojom::FrameInterfaceFactory> frame_interfaces;
+ ignore_result(frame_interfaces.InitWithNewPipeAndPassReceiver());
media_service_impl_ = CreateMediaServiceForTesting(
media_service_.BindNewPipeAndPassReceiver());
@@ -150,7 +115,7 @@ class MediaServiceTest : public testing::Test {
base::Unretained(this)));
media_service_->CreateInterfaceFactory(
interface_factory_.BindNewPipeAndPassReceiver(),
- std::move(host_interfaces));
+ std::move(frame_interfaces));
}
MOCK_METHOD3(OnCdmInitialized,
@@ -182,58 +147,6 @@ class MediaServiceTest : public testing::Test {
return cdm_id;
}
-#if BUILDFLAG(ENABLE_CDM_PROXY)
- MOCK_METHOD4(OnCdmProxyInitialized,
- void(CdmProxy::Status status,
- CdmProxy::Protocol protocol,
- uint32_t crypto_session_id,
- int cdm_id));
-
- // Returns the CDM ID associated with the CdmProxy.
- int InitializeCdmProxy(const base::Token& cdm_guid) {
- base::RunLoop run_loop;
- interface_factory_->CreateCdmProxy(cdm_guid,
- cdm_proxy_.BindNewPipeAndPassReceiver());
-
- mojo::PendingAssociatedRemote<mojom::CdmProxyClient> client_remote;
- cdm_proxy_client_receiver_.Bind(
- client_remote.InitWithNewEndpointAndPassReceiver());
- int cdm_id = CdmContext::kInvalidCdmId;
-
- EXPECT_CALL(*this, OnCdmProxyInitialized(CdmProxy::Status::kOk, _, _, _))
- .WillOnce(DoAll(SaveArg<3>(&cdm_id), QuitLoop(&run_loop)));
- cdm_proxy_->Initialize(
- std::move(client_remote),
- base::BindOnce(&MediaServiceTest::OnCdmProxyInitialized,
- base::Unretained(this)));
- run_loop.Run();
- return cdm_id;
- }
-#endif // BUILDFLAG(ENABLE_CDM_PROXY)
-
- MOCK_METHOD2(OnDecrypted,
- void(Decryptor::Status, scoped_refptr<DecoderBuffer>));
-
- void CreateDecryptor(int cdm_id, bool expected_result) {
- base::RunLoop run_loop;
- mojo::PendingRemote<mojom::Decryptor> decryptor_remote;
- interface_factory_->CreateDecryptor(
- cdm_id, decryptor_remote.InitWithNewPipeAndPassReceiver());
- MojoDecryptor mojo_decryptor(std::move(decryptor_remote));
-
- // In the success case, there's no decryption key to decrypt the buffer so
- // we would expect no-key.
- auto expected_status =
- expected_result ? Decryptor::kNoKey : Decryptor::kError;
-
- EXPECT_CALL(*this, OnDecrypted(expected_status, _))
- .WillOnce(QuitLoop(&run_loop));
- mojo_decryptor.Decrypt(
- Decryptor::kVideo, CreateEncryptedBuffer(),
- base::BindOnce(&MediaServiceTest::OnDecrypted, base::Unretained(this)));
- run_loop.Run();
- }
-
MOCK_METHOD1(OnRendererInitialized, void(bool));
void InitializeRenderer(const VideoDecoderConfig& video_config,
@@ -276,12 +189,6 @@ class MediaServiceTest : public testing::Test {
std::unique_ptr<MediaService> media_service_impl_;
-#if BUILDFLAG(ENABLE_CDM_PROXY)
- mojo::Remote<mojom::CdmProxy> cdm_proxy_;
- NiceMock<MockCdmProxyClient> cdm_proxy_client_;
- mojo::AssociatedReceiver<mojom::CdmProxyClient> cdm_proxy_client_receiver_;
-#endif // BUILDFLAG(ENABLE_CDM_PROXY)
-
NiceMock<MockRendererClient> renderer_client_;
mojo::AssociatedReceiver<mojom::RendererClient> renderer_client_receiver_;
@@ -311,11 +218,6 @@ TEST_F(MediaServiceTest, InitializeCdm_Success) {
TEST_F(MediaServiceTest, InitializeCdm_InvalidKeySystem) {
InitializeCdm(kInvalidKeySystem, false);
}
-
-TEST_F(MediaServiceTest, Decryptor_WithCdm) {
- int cdm_id = InitializeCdm(kClearKeyKeySystem, true);
- CreateDecryptor(cdm_id, true);
-}
#endif // BUILDFLAG(ENABLE_MOJO_CDM) && !defined(OS_ANDROID)
#if BUILDFLAG(ENABLE_MOJO_RENDERER)
@@ -324,43 +226,6 @@ TEST_F(MediaServiceTest, InitializeRenderer) {
}
#endif // BUILDFLAG(ENABLE_MOJO_RENDERER)
-#if BUILDFLAG(ENABLE_CDM_PROXY)
-TEST_F(MediaServiceTest, CdmProxy) {
- InitializeCdmProxy(kClearKeyCdmGuid);
-}
-
-TEST_F(MediaServiceTest, Decryptor_WithCdmProxy) {
- int cdm_id = InitializeCdmProxy(kClearKeyCdmGuid);
- CreateDecryptor(cdm_id, true);
-}
-
-TEST_F(MediaServiceTest, Decryptor_WrongCdmId) {
- int cdm_id = InitializeCdmProxy(kClearKeyCdmGuid);
- CreateDecryptor(cdm_id + 1, false);
-}
-
-TEST_F(MediaServiceTest, CdmProxyPreventsIdling) {
- InitializeCdmProxy(kClearKeyCdmGuid);
-
- // Disconnecting InterfaceFactory should not terminate the MediaService since
- // there is still a CdmProxy hosted.
- interface_factory_.reset();
- cdm_proxy_.FlushForTesting();
-
- // Disconnecting CdmProxy will cause the service to idle since no other
- // connections should be active.
- base::RunLoop run_loop;
- EXPECT_CALL(*this, OnMediaServiceIdle()).WillOnce(QuitLoop(&run_loop));
- cdm_proxy_.reset();
- run_loop.Run();
-}
-#endif // BUILDFLAG(ENABLE_CDM_PROXY)
-
-TEST_F(MediaServiceTest, Decryptor_WithoutCdmOrCdmProxy) {
- // Creating decryptor without creating CDM or CdmProxy.
- CreateDecryptor(1, false);
-}
-
TEST_F(MediaServiceTest, InterfaceFactoryPreventsIdling) {
// The service should not idle during this operation.
interface_factory_.FlushForTesting();
diff --git a/chromium/media/mojo/services/mojo_audio_output_stream_provider.cc b/chromium/media/mojo/services/mojo_audio_output_stream_provider.cc
index 111ffb0cee0..7a1ab08dfbd 100644
--- a/chromium/media/mojo/services/mojo_audio_output_stream_provider.cc
+++ b/chromium/media/mojo/services/mojo_audio_output_stream_provider.cc
@@ -38,8 +38,8 @@ MojoAudioOutputStreamProvider::~MojoAudioOutputStreamProvider() {
void MojoAudioOutputStreamProvider::Acquire(
const AudioParameters& params,
- mojo::PendingRemote<mojom::AudioOutputStreamProviderClient> provider_client,
- const base::Optional<base::UnguessableToken>& processing_id) {
+ mojo::PendingRemote<mojom::AudioOutputStreamProviderClient>
+ provider_client) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// |processing_id| gets dropped here. It's not supported outside of the audio
// service. As this class is slated for removal, it will not be updated to
diff --git a/chromium/media/mojo/services/mojo_audio_output_stream_provider.h b/chromium/media/mojo/services/mojo_audio_output_stream_provider.h
index a787e455853..52c23ee60ee 100644
--- a/chromium/media/mojo/services/mojo_audio_output_stream_provider.h
+++ b/chromium/media/mojo/services/mojo_audio_output_stream_provider.h
@@ -45,11 +45,9 @@ class MEDIA_MOJO_EXPORT MojoAudioOutputStreamProvider
private:
// mojom::AudioOutputStreamProvider implementation.
- void Acquire(
- const AudioParameters& params,
- mojo::PendingRemote<mojom::AudioOutputStreamProviderClient>
- provider_client,
- const base::Optional<base::UnguessableToken>& processing_id) override;
+ void Acquire(const AudioParameters& params,
+ mojo::PendingRemote<mojom::AudioOutputStreamProviderClient>
+ provider_client) override;
// Called when |audio_output_| had an error.
void CleanUp(bool had_error);
diff --git a/chromium/media/mojo/services/mojo_audio_output_stream_provider_unittest.cc b/chromium/media/mojo/services/mojo_audio_output_stream_provider_unittest.cc
index 6484b81d97b..04fa66ef823 100644
--- a/chromium/media/mojo/services/mojo_audio_output_stream_provider_unittest.cc
+++ b/chromium/media/mojo/services/mojo_audio_output_stream_provider_unittest.cc
@@ -92,12 +92,12 @@ TEST(MojoAudioOutputStreamProviderTest, AcquireTwice_BadMessage) {
mojo::PendingRemote<mojom::AudioOutputStreamProviderClient> client_1;
ignore_result(client_1.InitWithNewPipeAndPassReceiver());
provider_remote->Acquire(media::AudioParameters::UnavailableDeviceParams(),
- std::move(client_1), base::nullopt);
+ std::move(client_1));
mojo::PendingRemote<mojom::AudioOutputStreamProviderClient> client_2;
ignore_result(client_2.InitWithNewPipeAndPassReceiver());
provider_remote->Acquire(media::AudioParameters::UnavailableDeviceParams(),
- std::move(client_2), base::nullopt);
+ std::move(client_2));
EXPECT_CALL(deleter, Run(provider)).WillOnce(DeleteArg<0>());
base::RunLoop().RunUntilIdle();
@@ -130,7 +130,7 @@ TEST(MojoAudioOutputStreamProviderTest,
mojo::PendingRemote<mojom::AudioOutputStreamProviderClient> client;
ignore_result(client.InitWithNewPipeAndPassReceiver());
- provider_remote->Acquire(params, std::move(client), base::nullopt);
+ provider_remote->Acquire(params, std::move(client));
#if defined(OS_ANDROID)
base::RunLoop().RunUntilIdle();
diff --git a/chromium/media/mojo/services/mojo_cdm_helper.cc b/chromium/media/mojo/services/mojo_cdm_helper.cc
index ae74069d828..2d5761e44e5 100644
--- a/chromium/media/mojo/services/mojo_cdm_helper.cc
+++ b/chromium/media/mojo/services/mojo_cdm_helper.cc
@@ -11,13 +11,11 @@
#include "media/mojo/services/mojo_cdm_file_io.h"
#include "mojo/public/cpp/bindings/callback_helpers.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
-#include "services/service_manager/public/cpp/connect.h"
namespace media {
-MojoCdmHelper::MojoCdmHelper(
- service_manager::mojom::InterfaceProvider* interface_provider)
- : interface_provider_(interface_provider) {}
+MojoCdmHelper::MojoCdmHelper(mojom::FrameInterfaceFactory* frame_interfaces)
+ : frame_interfaces_(frame_interfaces) {}
MojoCdmHelper::~MojoCdmHelper() = default;
@@ -39,27 +37,6 @@ cdm::FileIO* MojoCdmHelper::CreateCdmFileIO(cdm::FileIOClient* client) {
return cdm_file_io;
}
-#if BUILDFLAG(ENABLE_CDM_PROXY)
-cdm::CdmProxy* MojoCdmHelper::CreateCdmProxy(cdm::CdmProxyClient* client) {
- DVLOG(3) << __func__;
- if (cdm_proxy_) {
- DVLOG(1) << __func__ << ": Only one outstanding CdmProxy allowed.";
- return nullptr;
- }
-
- mojo::PendingRemote<mojom::CdmProxy> cdm_proxy_remote;
- service_manager::GetInterface<mojom::CdmProxy>(
- interface_provider_, cdm_proxy_remote.InitWithNewPipeAndPassReceiver());
- cdm_proxy_ =
- std::make_unique<MojoCdmProxy>(std::move(cdm_proxy_remote), client);
- return cdm_proxy_.get();
-}
-
-int MojoCdmHelper::GetCdmProxyCdmId() {
- return cdm_proxy_ ? cdm_proxy_->GetCdmId() : CdmContext::kInvalidCdmId;
-}
-#endif // BUILDFLAG(ENABLE_CDM_PROXY)
-
cdm::Buffer* MojoCdmHelper::CreateCdmBuffer(size_t capacity) {
return GetAllocator()->CreateCdmBuffer(capacity);
}
@@ -118,8 +95,8 @@ void MojoCdmHelper::ReportFileReadSize(int file_size_bytes) {
void MojoCdmHelper::ConnectToCdmStorage() {
if (!cdm_storage_remote_) {
- service_manager::GetInterface<mojom::CdmStorage>(
- interface_provider_, cdm_storage_remote_.BindNewPipeAndPassReceiver());
+ frame_interfaces_->CreateCdmStorage(
+ cdm_storage_remote_.BindNewPipeAndPassReceiver());
}
}
@@ -131,16 +108,15 @@ CdmAllocator* MojoCdmHelper::GetAllocator() {
void MojoCdmHelper::ConnectToOutputProtection() {
if (!output_protection_) {
- service_manager::GetInterface<mojom::OutputProtection>(
- interface_provider_, output_protection_.BindNewPipeAndPassReceiver());
+ frame_interfaces_->BindEmbedderReceiver(mojo::GenericPendingReceiver(
+ output_protection_.BindNewPipeAndPassReceiver()));
}
}
void MojoCdmHelper::ConnectToPlatformVerification() {
if (!platform_verification_) {
- interface_provider_->GetInterface(
- mojom::PlatformVerification::Name_,
- platform_verification_.BindNewPipeAndPassReceiver().PassPipe());
+ frame_interfaces_->BindEmbedderReceiver(mojo::GenericPendingReceiver(
+ platform_verification_.BindNewPipeAndPassReceiver()));
}
}
diff --git a/chromium/media/mojo/services/mojo_cdm_helper.h b/chromium/media/mojo/services/mojo_cdm_helper.h
index 4a875028470..25bf7d13790 100644
--- a/chromium/media/mojo/services/mojo_cdm_helper.h
+++ b/chromium/media/mojo/services/mojo_cdm_helper.h
@@ -14,23 +14,13 @@
#include "media/cdm/cdm_auxiliary_helper.h"
#include "media/media_buildflags.h"
#include "media/mojo/mojom/cdm_storage.mojom.h"
+#include "media/mojo/mojom/frame_interface_factory.mojom.h"
#include "media/mojo/mojom/output_protection.mojom.h"
#include "media/mojo/mojom/platform_verification.mojom.h"
#include "media/mojo/services/media_mojo_export.h"
#include "media/mojo/services/mojo_cdm_file_io.h"
#include "mojo/public/cpp/bindings/remote.h"
-#if BUILDFLAG(ENABLE_CDM_PROXY)
-#include "media/mojo/mojom/cdm_proxy.mojom.h"
-#include "media/mojo/services/mojo_cdm_proxy.h"
-#endif
-
-namespace service_manager {
-namespace mojom {
-class InterfaceProvider;
-}
-} // namespace service_manager
-
namespace media {
// Helper class that connects the CDM to various auxiliary services. All
@@ -39,17 +29,12 @@ namespace media {
class MEDIA_MOJO_EXPORT MojoCdmHelper final : public CdmAuxiliaryHelper,
public MojoCdmFileIO::Delegate {
public:
- explicit MojoCdmHelper(
- service_manager::mojom::InterfaceProvider* interface_provider);
+ explicit MojoCdmHelper(mojom::FrameInterfaceFactory* frame_interfaces);
~MojoCdmHelper() final;
// CdmAuxiliaryHelper implementation.
void SetFileReadCB(FileReadCB file_read_cb) final;
cdm::FileIO* CreateCdmFileIO(cdm::FileIOClient* client) final;
-#if BUILDFLAG(ENABLE_CDM_PROXY)
- cdm::CdmProxy* CreateCdmProxy(cdm::CdmProxyClient* client) final;
- int GetCdmProxyCdmId() final;
-#endif
cdm::Buffer* CreateCdmBuffer(size_t capacity) final;
std::unique_ptr<VideoFrameImpl> CreateCdmVideoFrame() final;
void QueryStatus(QueryStatusCB callback) final;
@@ -72,7 +57,7 @@ class MEDIA_MOJO_EXPORT MojoCdmHelper final : public CdmAuxiliaryHelper,
void ConnectToPlatformVerification();
// Provides interfaces when needed.
- service_manager::mojom::InterfaceProvider* interface_provider_;
+ mojom::FrameInterfaceFactory* frame_interfaces_;
// Connections to the additional services. For the mojom classes, if a
// connection error occurs, we will not be able to reconnect to the
@@ -89,10 +74,6 @@ class MEDIA_MOJO_EXPORT MojoCdmHelper final : public CdmAuxiliaryHelper,
// TODO(xhwang): Switch to use UniquePtrComparator.
std::vector<std::unique_ptr<MojoCdmFileIO>> cdm_file_io_set_;
-#if BUILDFLAG(ENABLE_CDM_PROXY)
- std::unique_ptr<MojoCdmProxy> cdm_proxy_;
-#endif
-
base::WeakPtrFactory<MojoCdmHelper> weak_factory_{this};
DISALLOW_COPY_AND_ASSIGN(MojoCdmHelper);
};
diff --git a/chromium/media/mojo/services/mojo_cdm_helper_unittest.cc b/chromium/media/mojo/services/mojo_cdm_helper_unittest.cc
index fcdec73a081..1b52d254b27 100644
--- a/chromium/media/mojo/services/mojo_cdm_helper_unittest.cc
+++ b/chromium/media/mojo/services/mojo_cdm_helper_unittest.cc
@@ -13,8 +13,6 @@
#include "mojo/public/cpp/bindings/associated_receiver.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
-#include "services/service_manager/public/cpp/binder_registry.h"
-#include "services/service_manager/public/mojom/interface_provider.mojom.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -66,36 +64,27 @@ class MockCdmStorage : public mojom::CdmStorage {
mojo::AssociatedReceiver<mojom::CdmFile> client_receiver_{&cdm_file_};
};
-void CreateCdmStorage(mojo::PendingReceiver<mojom::CdmStorage> receiver) {
- mojo::MakeSelfOwnedReceiver(std::make_unique<MockCdmStorage>(),
- std::move(receiver));
-}
-
-class TestInterfaceProvider : public service_manager::mojom::InterfaceProvider {
+class TestFrameInterfaceFactory : public mojom::FrameInterfaceFactory {
public:
- TestInterfaceProvider() {
- registry_.AddInterface(base::Bind(&CreateCdmStorage));
- }
- ~TestInterfaceProvider() override = default;
-
- void GetInterface(const std::string& interface_name,
- mojo::ScopedMessagePipeHandle handle) override {
- registry_.BindInterface(interface_name, std::move(handle));
+ void CreateProvisionFetcher(
+ mojo::PendingReceiver<mojom::ProvisionFetcher>) override {}
+ void CreateCdmStorage(
+ mojo::PendingReceiver<mojom::CdmStorage> receiver) override {
+ mojo::MakeSelfOwnedReceiver(std::make_unique<MockCdmStorage>(),
+ std::move(receiver));
}
-
- private:
- service_manager::BinderRegistry registry_;
+ void BindEmbedderReceiver(mojo::GenericPendingReceiver) override {}
};
} // namespace
class MojoCdmHelperTest : public testing::Test {
protected:
- MojoCdmHelperTest() : helper_(&test_interface_provider_) {}
+ MojoCdmHelperTest() : helper_(&frame_interfaces_) {}
~MojoCdmHelperTest() override = default;
base::test::TaskEnvironment task_environment_;
- TestInterfaceProvider test_interface_provider_;
+ TestFrameInterfaceFactory frame_interfaces_;
MockFileIOClient file_io_client_;
MojoCdmHelper helper_;
};
diff --git a/chromium/media/mojo/services/mojo_cdm_promise.cc b/chromium/media/mojo/services/mojo_cdm_promise.cc
index 41b18ec793e..5bd1295a42f 100644
--- a/chromium/media/mojo/services/mojo_cdm_promise.cc
+++ b/chromium/media/mojo/services/mojo_cdm_promise.cc
@@ -9,7 +9,7 @@
#include <vector>
#include "base/bind.h"
-#include "base/logging.h"
+#include "base/check.h"
#include "media/base/content_decryption_module.h"
#include "media/base/decryptor.h"
diff --git a/chromium/media/mojo/services/mojo_cdm_proxy.cc b/chromium/media/mojo/services/mojo_cdm_proxy.cc
deleted file mode 100644
index 4c454fa19c4..00000000000
--- a/chromium/media/mojo/services/mojo_cdm_proxy.cc
+++ /dev/null
@@ -1,217 +0,0 @@
-// Copyright 2017 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/mojo/services/mojo_cdm_proxy.h"
-
-#include <vector>
-
-#include "base/bind.h"
-#include "base/callback.h"
-#include "base/logging.h"
-#include "media/base/cdm_context.h"
-#include "mojo/public/cpp/bindings/callback_helpers.h"
-
-namespace media {
-
-namespace {
-
-inline std::ostream& operator<<(std::ostream& out, CdmProxy::Status status) {
- switch (status) {
- case CdmProxy::Status::kOk:
- return out << "kOk";
- case CdmProxy::Status::kFail:
- return out << "kFail";
- }
- NOTREACHED();
- return out << "Invalid Status!";
-}
-
-cdm::CdmProxyClient::Status ToCdmStatus(CdmProxy::Status status) {
- switch (status) {
- case CdmProxy::Status::kOk:
- return cdm::CdmProxyClient::Status::kOk;
- case CdmProxy::Status::kFail:
- return cdm::CdmProxyClient::Status::kFail;
- }
-
- NOTREACHED() << "Unexpected status: " << status;
- return cdm::CdmProxyClient::Status::kFail;
-}
-
-cdm::CdmProxyClient::Protocol ToCdmProtocol(CdmProxy::Protocol protocol) {
- switch (protocol) {
- case CdmProxy::Protocol::kNone:
- return cdm::CdmProxyClient::Protocol::kNone;
- case CdmProxy::Protocol::kIntel:
- return cdm::CdmProxyClient::Protocol::kIntel;
- }
-
- NOTREACHED() << "Unexpected protocol: " << static_cast<int32_t>(protocol);
- return cdm::CdmProxyClient::Protocol::kNone;
-}
-
-CdmProxy::Function ToMediaFunction(cdm::CdmProxy::Function function) {
- switch (function) {
- case cdm::CdmProxy::Function::kIntelNegotiateCryptoSessionKeyExchange:
- return CdmProxy::Function::kIntelNegotiateCryptoSessionKeyExchange;
- }
-
- // TODO(xhwang): Return an invalid function?
- NOTREACHED() << "Unexpected function: " << static_cast<int32_t>(function);
- return CdmProxy::Function::kIntelNegotiateCryptoSessionKeyExchange;
-}
-
-CdmProxy::KeyType ToMediaKeyType(cdm::CdmProxy::KeyType key_type) {
- switch (key_type) {
- case cdm::CdmProxy::KeyType::kDecryptOnly:
- return CdmProxy::KeyType::kDecryptOnly;
- case cdm::CdmProxy::KeyType::kDecryptAndDecode:
- return CdmProxy::KeyType::kDecryptAndDecode;
- }
-
- NOTREACHED() << "Unexpected key type: " << static_cast<int32_t>(key_type);
- return CdmProxy::KeyType::kDecryptOnly;
-}
-
-} // namespace
-
-MojoCdmProxy::MojoCdmProxy(
- mojo::PendingRemote<mojom::CdmProxy> cdm_proxy_remote,
- cdm::CdmProxyClient* client)
- : cdm_proxy_remote_(std::move(cdm_proxy_remote)), client_(client) {
- DVLOG(1) << __func__;
- DCHECK(client);
-}
-
-MojoCdmProxy::~MojoCdmProxy() {
- DVLOG(1) << __func__;
-}
-
-void MojoCdmProxy::Initialize() {
- DVLOG(2) << __func__;
-
- auto callback = mojo::WrapCallbackWithDefaultInvokeIfNotRun(
- base::BindOnce(&MojoCdmProxy::OnInitialized, weak_factory_.GetWeakPtr()),
- media::CdmProxy::Status::kFail, media::CdmProxy::Protocol::kNone, 0,
- CdmContext::kInvalidCdmId);
- cdm_proxy_remote_->Initialize(client_receiver_.BindNewEndpointAndPassRemote(),
- std::move(callback));
-}
-
-void MojoCdmProxy::Process(Function function,
- uint32_t crypto_session_id,
- const uint8_t* input_data,
- uint32_t input_data_size,
- uint32_t expected_output_data_size) {
- DVLOG(3) << __func__;
- CHECK(client_) << "Initialize not called.";
-
- auto callback = mojo::WrapCallbackWithDefaultInvokeIfNotRun(
- base::BindOnce(&MojoCdmProxy::OnProcessed, weak_factory_.GetWeakPtr()),
- media::CdmProxy::Status::kFail, std::vector<uint8_t>());
-
- cdm_proxy_remote_->Process(
- ToMediaFunction(function), crypto_session_id,
- std::vector<uint8_t>(input_data, input_data + input_data_size),
- expected_output_data_size, std::move(callback));
-}
-
-void MojoCdmProxy::CreateMediaCryptoSession(const uint8_t* input_data,
- uint32_t input_data_size) {
- DVLOG(3) << __func__;
- CHECK(client_) << "Initialize not called.";
-
- auto callback = mojo::WrapCallbackWithDefaultInvokeIfNotRun(
- base::BindOnce(&MojoCdmProxy::OnMediaCryptoSessionCreated,
- weak_factory_.GetWeakPtr()),
- media::CdmProxy::Status::kFail, 0, 0);
-
- cdm_proxy_remote_->CreateMediaCryptoSession(
- std::vector<uint8_t>(input_data, input_data + input_data_size),
- std::move(callback));
-}
-
-void MojoCdmProxy::SetKey(uint32_t crypto_session_id,
- const uint8_t* key_id,
- uint32_t key_id_size,
- KeyType key_type,
- const uint8_t* key_blob,
- uint32_t key_blob_size) {
- DVLOG(3) << __func__;
- CHECK(client_) << "Initialize not called.";
-
- auto callback = mojo::WrapCallbackWithDefaultInvokeIfNotRun(
- base::BindOnce(&MojoCdmProxy::OnKeySet, weak_factory_.GetWeakPtr()),
- media::CdmProxy::Status::kFail);
-
- cdm_proxy_remote_->SetKey(
- crypto_session_id, std::vector<uint8_t>(key_id, key_id + key_id_size),
- ToMediaKeyType(key_type),
- std::vector<uint8_t>(key_blob, key_blob + key_blob_size),
- std::move(callback));
-}
-
-void MojoCdmProxy::RemoveKey(uint32_t crypto_session_id,
- const uint8_t* key_id,
- uint32_t key_id_size) {
- DVLOG(3) << __func__;
- CHECK(client_) << "Initialize not called.";
-
- auto callback = mojo::WrapCallbackWithDefaultInvokeIfNotRun(
- base::BindOnce(&MojoCdmProxy::OnKeyRemoved, weak_factory_.GetWeakPtr()),
- media::CdmProxy::Status::kFail);
-
- cdm_proxy_remote_->RemoveKey(
- crypto_session_id, std::vector<uint8_t>(key_id, key_id + key_id_size),
- std::move(callback));
-}
-
-void MojoCdmProxy::NotifyHardwareReset() {
- DVLOG(2) << __func__;
- client_->NotifyHardwareReset();
-}
-
-int MojoCdmProxy::GetCdmId() {
- DVLOG(2) << __func__ << ": cdm_id = " << cdm_id_;
- return cdm_id_;
-}
-
-void MojoCdmProxy::OnInitialized(media::CdmProxy::Status status,
- media::CdmProxy::Protocol protocol,
- uint32_t crypto_session_id,
- int cdm_id) {
- DVLOG(3) << __func__ << ": status = " << status
- << ", crypto_session_id = " << crypto_session_id;
- cdm_id_ = cdm_id;
- client_->OnInitialized(ToCdmStatus(status), ToCdmProtocol(protocol),
- crypto_session_id);
-}
-
-void MojoCdmProxy::OnProcessed(media::CdmProxy::Status status,
- const std::vector<uint8_t>& output_data) {
- DVLOG(3) << __func__ << ": status = " << status;
- client_->OnProcessed(ToCdmStatus(status), output_data.data(),
- output_data.size());
-}
-
-void MojoCdmProxy::OnMediaCryptoSessionCreated(media::CdmProxy::Status status,
- uint32_t crypto_session_id,
- uint64_t output_data) {
- DVLOG(3) << __func__ << ": status = " << status
- << ", crypto_session_id = " << crypto_session_id;
- client_->OnMediaCryptoSessionCreated(ToCdmStatus(status), crypto_session_id,
- output_data);
-}
-
-void MojoCdmProxy::OnKeySet(media::CdmProxy::Status status) {
- DVLOG(3) << __func__ << ": status = " << status;
- client_->OnKeySet(ToCdmStatus(status));
-}
-
-void MojoCdmProxy::OnKeyRemoved(media::CdmProxy::Status status) {
- DVLOG(3) << __func__ << ": status = " << status;
- client_->OnKeyRemoved(ToCdmStatus(status));
-}
-
-} // namespace media
diff --git a/chromium/media/mojo/services/mojo_cdm_proxy.h b/chromium/media/mojo/services/mojo_cdm_proxy.h
deleted file mode 100644
index 5c7264052b2..00000000000
--- a/chromium/media/mojo/services/mojo_cdm_proxy.h
+++ /dev/null
@@ -1,85 +0,0 @@
-// Copyright 2017 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_MOJO_SERVICES_MOJO_CDM_PROXY_H_
-#define MEDIA_MOJO_SERVICES_MOJO_CDM_PROXY_H_
-
-#include <stdint.h>
-
-#include <vector>
-
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "media/base/cdm_context.h"
-#include "media/cdm/api/content_decryption_module.h"
-#include "media/mojo/mojom/cdm_proxy.mojom.h"
-#include "media/mojo/services/media_mojo_export.h"
-#include "mojo/public/cpp/bindings/associated_receiver.h"
-#include "mojo/public/cpp/bindings/pending_remote.h"
-#include "mojo/public/cpp/bindings/remote.h"
-
-namespace media {
-
-// Implements a cdm::CdmProxy that communicates with mojom::CdmProxy.
-class MEDIA_MOJO_EXPORT MojoCdmProxy : public cdm::CdmProxy,
- mojom::CdmProxyClient {
- public:
- MojoCdmProxy(mojo::PendingRemote<mojom::CdmProxy> cdm_proxy_remote,
- cdm::CdmProxyClient* client);
- ~MojoCdmProxy() override;
-
- // cdm::CdmProxy implementation.
- void Initialize() final;
- void Process(Function function,
- uint32_t crypto_session_id,
- const uint8_t* input_data,
- uint32_t input_data_size,
- uint32_t expected_output_data_size) final;
- void CreateMediaCryptoSession(const uint8_t* input_data,
- uint32_t input_data_size) final;
- void SetKey(uint32_t crypto_session_id,
- const uint8_t* key_id,
- uint32_t key_id_size,
- KeyType key_type,
- const uint8_t* key_blob,
- uint32_t key_blob_size) final;
- void RemoveKey(uint32_t crypto_session_id,
- const uint8_t* key_id,
- uint32_t key_id_size) final;
-
- // mojom::CdmProxyClient implementation.
- void NotifyHardwareReset() final;
-
- // Returns the CDM ID associated with the remote CdmProxy.
- int GetCdmId();
-
- private:
- void OnInitialized(media::CdmProxy::Status status,
- media::CdmProxy::Protocol protocol,
- uint32_t crypto_session_id,
- int cdm_id);
- void OnProcessed(media::CdmProxy::Status status,
- const std::vector<uint8_t>& output_data);
- void OnMediaCryptoSessionCreated(media::CdmProxy::Status status,
- uint32_t crypto_session_id,
- uint64_t output_data);
- void OnKeySet(media::CdmProxy::Status status);
- void OnKeyRemoved(media::CdmProxy::Status status);
-
- mojo::Remote<mojom::CdmProxy> cdm_proxy_remote_;
- cdm::CdmProxyClient* client_;
-
- mojo::AssociatedReceiver<mojom::CdmProxyClient> client_receiver_{this};
-
- int cdm_id_ = CdmContext::kInvalidCdmId;
-
- // NOTE: Weak pointers must be invalidated before all other member variables.
- base::WeakPtrFactory<MojoCdmProxy> weak_factory_{this};
-
- DISALLOW_COPY_AND_ASSIGN(MojoCdmProxy);
-};
-
-} // namespace media
-
-#endif // MEDIA_MOJO_SERVICES_MOJO_CDM_PROXY_H_
diff --git a/chromium/media/mojo/services/mojo_cdm_proxy_service.cc b/chromium/media/mojo/services/mojo_cdm_proxy_service.cc
deleted file mode 100644
index 1c74993b907..00000000000
--- a/chromium/media/mojo/services/mojo_cdm_proxy_service.cc
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright 2017 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/mojo/services/mojo_cdm_proxy_service.h"
-
-#include "base/bind.h"
-#include "base/macros.h"
-#include "media/mojo/services/mojo_cdm_service_context.h"
-
-namespace media {
-
-MojoCdmProxyService::MojoCdmProxyService(
- std::unique_ptr<::media::CdmProxy> cdm_proxy,
- MojoCdmServiceContext* context)
- : cdm_proxy_(std::move(cdm_proxy)), context_(context) {
- DVLOG(1) << __func__;
- DCHECK(cdm_proxy_);
- DCHECK(context_);
-}
-
-MojoCdmProxyService::~MojoCdmProxyService() {
- DVLOG(1) << __func__;
-
- if (cdm_id_ != CdmContext::kInvalidCdmId)
- context_->UnregisterCdmProxy(cdm_id_);
-}
-
-void MojoCdmProxyService::Initialize(
- mojo::PendingAssociatedRemote<mojom::CdmProxyClient> client,
- InitializeCallback callback) {
- DVLOG(2) << __func__;
-
- CHECK(!has_initialize_been_called_) << "Initialize should only happen once";
- has_initialize_been_called_ = true;
-
- client_.Bind(std::move(client));
-
- cdm_proxy_->Initialize(
- this, base::BindOnce(&MojoCdmProxyService::OnInitialized,
- weak_factory_.GetWeakPtr(), std::move(callback)));
-}
-
-void MojoCdmProxyService::Process(media::CdmProxy::Function function,
- uint32_t crypto_session_id,
- const std::vector<uint8_t>& input_data,
- uint32_t expected_output_data_size,
- ProcessCallback callback) {
- DVLOG(3) << __func__;
- cdm_proxy_->Process(function, crypto_session_id, input_data,
- expected_output_data_size, std::move(callback));
-}
-
-void MojoCdmProxyService::CreateMediaCryptoSession(
- const std::vector<uint8_t>& input_data,
- CreateMediaCryptoSessionCallback callback) {
- DVLOG(3) << __func__;
- cdm_proxy_->CreateMediaCryptoSession(input_data, std::move(callback));
-}
-
-void MojoCdmProxyService::SetKey(uint32_t crypto_session_id,
- const std::vector<uint8_t>& key_id,
- media::CdmProxy::KeyType key_type,
- const std::vector<uint8_t>& key_blob,
- SetKeyCallback callback) {
- DVLOG(3) << __func__;
- cdm_proxy_->SetKey(crypto_session_id, key_id, key_type, key_blob,
- std::move(callback));
-}
-
-void MojoCdmProxyService::RemoveKey(uint32_t crypto_session_id,
- const std::vector<uint8_t>& key_id,
- RemoveKeyCallback callback) {
- DVLOG(3) << __func__;
- cdm_proxy_->RemoveKey(crypto_session_id, key_id, std::move(callback));
-}
-
-void MojoCdmProxyService::NotifyHardwareReset() {
- DVLOG(2) << __func__;
- client_->NotifyHardwareReset();
-}
-
-base::WeakPtr<CdmContext> MojoCdmProxyService::GetCdmContext() {
- DVLOG(2) << __func__;
- return cdm_proxy_->GetCdmContext();
-}
-
-void MojoCdmProxyService::OnInitialized(InitializeCallback callback,
- ::media::CdmProxy::Status status,
- ::media::CdmProxy::Protocol protocol,
- uint32_t crypto_session_id) {
- CHECK_EQ(cdm_id_, CdmContext::kInvalidCdmId)
- << "CDM proxy should only be created once.";
-
- if (status == ::media::CdmProxy::Status::kOk)
- cdm_id_ = context_->RegisterCdmProxy(this);
-
- std::move(callback).Run(status, protocol, crypto_session_id, cdm_id_);
-}
-
-} // namespace media
diff --git a/chromium/media/mojo/services/mojo_cdm_proxy_service.h b/chromium/media/mojo/services/mojo_cdm_proxy_service.h
deleted file mode 100644
index e8eaa950260..00000000000
--- a/chromium/media/mojo/services/mojo_cdm_proxy_service.h
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright 2017 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_MOJO_SERVICES_MOJO_CDM_PROXY_SERVICE_H_
-#define MEDIA_MOJO_SERVICES_MOJO_CDM_PROXY_SERVICE_H_
-
-#include <stdint.h>
-
-#include <memory>
-
-#include "base/compiler_specific.h"
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "media/base/cdm_context.h"
-#include "media/cdm/cdm_proxy.h"
-#include "media/mojo/mojom/cdm_proxy.mojom.h"
-#include "media/mojo/services/media_mojo_export.h"
-#include "mojo/public/cpp/bindings/associated_remote.h"
-#include "mojo/public/cpp/bindings/pending_associated_remote.h"
-
-namespace media {
-
-class MojoCdmServiceContext;
-
-// A mojom::CdmProxy implementation backed by a media::CdmProxy.
-class MEDIA_MOJO_EXPORT MojoCdmProxyService : public mojom::CdmProxy,
- public CdmProxy::Client {
- public:
- MojoCdmProxyService(std::unique_ptr<::media::CdmProxy> cdm_proxy,
- MojoCdmServiceContext* context);
-
- ~MojoCdmProxyService() final;
-
- // mojom::CdmProxy implementation.
- void Initialize(mojo::PendingAssociatedRemote<mojom::CdmProxyClient> client,
- InitializeCallback callback) final;
- void Process(media::CdmProxy::Function function,
- uint32_t crypto_session_id,
- const std::vector<uint8_t>& input_data,
- uint32_t expected_output_data_size,
- ProcessCallback callback) final;
- void CreateMediaCryptoSession(
- const std::vector<uint8_t>& input_data,
- CreateMediaCryptoSessionCallback callback) final;
- void SetKey(uint32_t crypto_session_id,
- const std::vector<uint8_t>& key_id,
- media::CdmProxy::KeyType key_type,
- const std::vector<uint8_t>& key_blob,
- SetKeyCallback callback) final;
- void RemoveKey(uint32_t crypto_session_id,
- const std::vector<uint8_t>& key_id,
- RemoveKeyCallback callback) final;
-
- // CdmProxy::Client implementation.
- void NotifyHardwareReset() final;
-
- // Get CdmContext to be used by the media pipeline.
- base::WeakPtr<CdmContext> GetCdmContext();
-
- int GetCdmIdForTesting() const { return cdm_id_; }
-
- private:
- void OnInitialized(InitializeCallback callback,
- ::media::CdmProxy::Status status,
- ::media::CdmProxy::Protocol protocol,
- uint32_t crypto_session_id);
-
- bool has_initialize_been_called_ = false;
-
- std::unique_ptr<::media::CdmProxy> cdm_proxy_;
- MojoCdmServiceContext* const context_ = nullptr;
-
- mojo::AssociatedRemote<mojom::CdmProxyClient> client_;
-
- // Set to a valid CDM ID if the |cdm_proxy_| is successfully initialized.
- int cdm_id_ = CdmContext::kInvalidCdmId;
-
- // NOTE: Weak pointers must be invalidated before all other member variables.
- base::WeakPtrFactory<MojoCdmProxyService> weak_factory_{this};
-
- DISALLOW_COPY_AND_ASSIGN(MojoCdmProxyService);
-};
-
-} // namespace media
-
-#endif // MEDIA_MOJO_SERVICES_MOJO_CDM_PROXY_SERVICE_H_
diff --git a/chromium/media/mojo/services/mojo_cdm_proxy_unittest.cc b/chromium/media/mojo/services/mojo_cdm_proxy_unittest.cc
deleted file mode 100644
index 8088e2ffafe..00000000000
--- a/chromium/media/mojo/services/mojo_cdm_proxy_unittest.cc
+++ /dev/null
@@ -1,390 +0,0 @@
-// Copyright 2018 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 <stdint.h>
-
-#include <memory>
-
-#include "base/bind.h"
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "base/run_loop.h"
-#include "base/test/gmock_callback_support.h"
-#include "base/test/gtest_util.h"
-#include "base/test/test_message_loop.h"
-#include "media/base/mock_filters.h"
-#include "media/cdm/cdm_proxy_context.h"
-#include "media/mojo/mojom/cdm_proxy.mojom.h"
-#include "media/mojo/services/mojo_cdm_proxy.h"
-#include "media/mojo/services/mojo_cdm_proxy_service.h"
-#include "media/mojo/services/mojo_cdm_service_context.h"
-#include "mojo/public/cpp/bindings/interface_request.h"
-#include "mojo/public/cpp/bindings/receiver.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using ::testing::_;
-using ::testing::NotNull;
-using ::testing::SaveArg;
-using ::testing::StrictMock;
-
-namespace media {
-
-namespace {
-
-MATCHER_P(StatusEq, status, "") {
- return (arg == cdm::CdmProxyClient::Status::kOk &&
- status == media::CdmProxy::Status::kOk) ||
- (arg == cdm::CdmProxyClient::Status::kFail &&
- status == media::CdmProxy::Status::kFail);
-}
-
-constexpr uint32_t kCryptoSessionId = 1010;
-
-class MockCdmProxyContext : public CdmProxyContext {};
-
-class MockCdmProxy : public media::CdmProxy, public media::CdmContext {
- public:
- MockCdmProxy() {}
- ~MockCdmProxy() override = default;
-
- // media::CdmProxy implementation.
-
- base::WeakPtr<CdmContext> GetCdmContext() override {
- return weak_factory_.GetWeakPtr();
- }
-
- MOCK_METHOD2(Initialize, void(Client* client, InitializeCB init_cb));
-
- MOCK_METHOD5(Process,
- void(Function function,
- uint32_t crypto_session_id,
- const std::vector<uint8_t>& input_data,
- uint32_t expected_output_data_size,
- ProcessCB process_cb));
-
- MOCK_METHOD2(CreateMediaCryptoSession,
- void(const std::vector<uint8_t>& input_data,
- CreateMediaCryptoSessionCB create_media_crypto_session_cb));
-
- MOCK_METHOD5(SetKey,
- void(uint32_t crypto_session_id,
- const std::vector<uint8_t>& key_id,
- KeyType key_type,
- const std::vector<uint8_t>& key_blob,
- SetKeyCB set_key_cb));
- MOCK_METHOD3(RemoveKey,
- void(uint32_t crypto_session_id,
- const std::vector<uint8_t>& key_id,
- RemoveKeyCB remove_key_cb));
-
- // media::CdmContext implementation.
- CdmProxyContext* GetCdmProxyContext() override {
- return &mock_cdm_proxy_context_;
- }
-
- private:
- MockCdmProxyContext mock_cdm_proxy_context_;
- base::WeakPtrFactory<MockCdmProxy> weak_factory_{this};
-};
-
-class MockCdmProxyClient : public cdm::CdmProxyClient {
- public:
- MockCdmProxyClient() = default;
- ~MockCdmProxyClient() override = default;
-
- MOCK_METHOD3(OnInitialized,
- void(Status status,
- Protocol protocol,
- uint32_t crypto_session_id));
- MOCK_METHOD3(OnProcessed,
- void(Status status,
- const uint8_t* output_data,
- uint32_t output_data_size));
- MOCK_METHOD3(OnMediaCryptoSessionCreated,
- void(Status status,
- uint32_t crypto_session_id,
- uint64_t output_data));
- MOCK_METHOD1(OnKeySet, void(Status status));
- MOCK_METHOD1(OnKeyRemoved, void(Status status));
- MOCK_METHOD0(NotifyHardwareReset, void());
-};
-
-} // namespace
-
-class MojoCdmProxyTest : public ::testing::Test {
- public:
- using Status = CdmProxy::Status;
-
- MojoCdmProxyTest() {
- // Client side setup.
- mojo::PendingRemote<mojom::CdmProxy> cdm_proxy_remote;
- auto receiver = cdm_proxy_remote.InitWithNewPipeAndPassReceiver();
- mojo_cdm_proxy_.reset(
- new MojoCdmProxy(std::move(cdm_proxy_remote), &client_));
- cdm_proxy_ = mojo_cdm_proxy_.get();
-
- // Service side setup.
- std::unique_ptr<MockCdmProxy> mock_cdm_proxy(new MockCdmProxy());
- mock_cdm_proxy_ = mock_cdm_proxy.get();
- mojo_cdm_proxy_service_.reset(new MojoCdmProxyService(
- std::move(mock_cdm_proxy), &mojo_cdm_service_context_));
- receiver_.reset(new mojo::Receiver<mojom::CdmProxy>(
- mojo_cdm_proxy_service_.get(), std::move(receiver)));
- receiver_->set_disconnect_handler(base::BindOnce(
- &MojoCdmProxyTest::OnConnectionError, base::Unretained(this)));
-
- base::RunLoop().RunUntilIdle();
- }
-
- ~MojoCdmProxyTest() override = default;
-
- void Initialize(Status expected_status = Status::kOk,
- bool has_connection = true) {
- if (has_connection) {
- EXPECT_CALL(*mock_cdm_proxy_, Initialize(NotNull(), _))
- .WillOnce([&](auto, auto init_cb) {
- std::move(init_cb).Run(expected_status, CdmProxy::Protocol::kNone,
- kCryptoSessionId);
- });
- EXPECT_CALL(client_,
- OnInitialized(StatusEq(expected_status),
- cdm::CdmProxyClient::kNone, kCryptoSessionId))
- .WillOnce(SaveArg<2>(&crypto_session_id_));
- } else {
- // Client should always be called even without connection. But we only
- // care about status in this case.
- EXPECT_CALL(client_, OnInitialized(StatusEq(expected_status), _, _));
- }
-
- cdm_proxy_->Initialize();
- base::RunLoop().RunUntilIdle();
- }
-
- void Process(Status expected_status = Status::kOk,
- bool has_connection = true) {
- const std::vector<uint8_t> kInputData = {1, 2};
- const uint32_t kExpectedOutputDataSize = 111;
- const std::vector<uint8_t> kOutputData = {3, 4, 5};
-
- if (has_connection) {
- EXPECT_CALL(
- *mock_cdm_proxy_,
- Process(CdmProxy::Function::kIntelNegotiateCryptoSessionKeyExchange,
- crypto_session_id_, kInputData, kExpectedOutputDataSize, _))
- .WillOnce([&](auto, auto, auto, auto, auto process_cb) {
- std::move(process_cb).Run(expected_status, kOutputData);
- });
- EXPECT_CALL(client_, OnProcessed(StatusEq(expected_status), NotNull(),
- kOutputData.size()));
- } else {
- // Client should always be called even without connection. But we only
- // care about status in this case.
- EXPECT_CALL(client_, OnProcessed(StatusEq(expected_status), _, _));
- }
-
- cdm_proxy_->Process(
- cdm::CdmProxy::Function::kIntelNegotiateCryptoSessionKeyExchange,
- crypto_session_id_, kInputData.data(), kInputData.size(),
- kExpectedOutputDataSize);
- base::RunLoop().RunUntilIdle();
- }
-
- void CreateMediaCryptoSession(Status expected_status = Status::kOk,
- bool has_connection = true) {
- const std::vector<uint8_t> kInputData = {6, 7};
- const uint32_t kMediaCryptoSessionId = 222;
- const uint64_t kOutputData = 333;
-
- if (has_connection) {
- EXPECT_CALL(*mock_cdm_proxy_, CreateMediaCryptoSession(kInputData, _))
- .WillOnce([&](auto, auto create_media_crypto_session_cb) {
- std::move(create_media_crypto_session_cb)
- .Run(expected_status, kMediaCryptoSessionId, kOutputData);
- });
- EXPECT_CALL(client_, OnMediaCryptoSessionCreated(
- StatusEq(expected_status), kMediaCryptoSessionId,
- kOutputData));
- } else {
- // Client should always be called even without connection. But we only
- // care about status in this case.
- EXPECT_CALL(client_,
- OnMediaCryptoSessionCreated(StatusEq(expected_status), _, _));
- }
-
- cdm_proxy_->CreateMediaCryptoSession(kInputData.data(), kInputData.size());
- base::RunLoop().RunUntilIdle();
- }
-
- void SetKey() {
- const std::vector<uint8_t> key_id = {8, 9};
- const std::vector<uint8_t> key_blob = {10, 11, 12};
- EXPECT_CALL(*mock_cdm_proxy_,
- SetKey(crypto_session_id_, key_id, _, key_blob, _))
- .WillOnce([&](auto, auto, auto, auto, auto set_key_cb) {
- std::move(set_key_cb).Run(Status::kOk);
- });
- EXPECT_CALL(client_, OnKeySet(StatusEq(Status::kOk)));
- cdm_proxy_->SetKey(crypto_session_id_, key_id.data(), key_id.size(),
- cdm::CdmProxy::KeyType::kDecryptOnly, key_blob.data(),
- key_blob.size());
- base::RunLoop().RunUntilIdle();
- }
-
- void RemoveKey() {
- const std::vector<uint8_t> key_id = {13, 14};
- EXPECT_CALL(*mock_cdm_proxy_, RemoveKey(crypto_session_id_, key_id, _))
- .WillOnce([&](auto, auto, auto remove_key_cb) {
- std::move(remove_key_cb).Run(Status::kOk);
- });
- EXPECT_CALL(client_, OnKeyRemoved(StatusEq(Status::kOk)));
- cdm_proxy_->RemoveKey(crypto_session_id_, key_id.data(), key_id.size());
- base::RunLoop().RunUntilIdle();
- }
-
- // Simulate connecting the media component with the CdmContext. Can only be
- // called after the CdmProxy is successfully initialized (see Initialize()).
- void SetCdm() {
- int cdm_id = mojo_cdm_proxy_service_->GetCdmIdForTesting();
- cdm_context_ref_ = mojo_cdm_service_context_.GetCdmContextRef(cdm_id);
- cdm_context_ = cdm_context_ref_->GetCdmContext();
- }
-
- CdmProxyContext* GetCdmProxyContext() {
- return cdm_context_->GetCdmProxyContext();
- }
-
- void Destroy() {
- mojo_cdm_proxy_.reset();
- base::RunLoop().RunUntilIdle();
- }
-
- void OnConnectionError() { mojo_cdm_proxy_service_.reset(); }
-
- void ForceConnectionError() {
- receiver_->ResetWithReason(2, "Test closed connection.");
- mojo_cdm_proxy_service_.reset();
- base::RunLoop().RunUntilIdle();
- }
-
- base::TestMessageLoop message_loop_;
- uint32_t crypto_session_id_ = 0;
-
- // Client side members.
- StrictMock<MockCdmProxyClient> client_;
- std::unique_ptr<MojoCdmProxy> mojo_cdm_proxy_;
- cdm::CdmProxy* cdm_proxy_ = nullptr;
-
- // Service side members.
- MojoCdmServiceContext mojo_cdm_service_context_;
- std::unique_ptr<MojoCdmProxyService> mojo_cdm_proxy_service_;
- std::unique_ptr<mojo::Receiver<mojom::CdmProxy>> receiver_;
- MockCdmProxy* mock_cdm_proxy_ = nullptr;
-
- // Media component side members.
- std::unique_ptr<CdmContextRef> cdm_context_ref_;
- CdmContext* cdm_context_ = nullptr;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(MojoCdmProxyTest);
-};
-
-TEST_F(MojoCdmProxyTest, Initialize) {
- Initialize();
-}
-
-TEST_F(MojoCdmProxyTest, Initialize_Failure) {
- Initialize(Status::kFail);
-}
-
-TEST_F(MojoCdmProxyTest, Initialize_Twice) {
- Initialize();
- EXPECT_CHECK_DEATH(Initialize());
-}
-
-TEST_F(MojoCdmProxyTest, Process) {
- Initialize();
- Process();
-}
-
-TEST_F(MojoCdmProxyTest, Process_Failure) {
- Initialize();
- Process(Status::kFail);
-}
-
-TEST_F(MojoCdmProxyTest, CreateMediaCryptoSession) {
- Initialize();
- Process();
- CreateMediaCryptoSession();
-}
-
-TEST_F(MojoCdmProxyTest, CreateMediaCryptoSession_Failure) {
- Initialize();
- Process();
- CreateMediaCryptoSession(Status::kFail);
-}
-
-TEST_F(MojoCdmProxyTest, SetKey) {
- Initialize();
- Process();
- CreateMediaCryptoSession();
- SetKey();
-}
-
-TEST_F(MojoCdmProxyTest, RemoveKey) {
- Initialize();
- Process();
- CreateMediaCryptoSession();
- RemoveKey();
-}
-
-TEST_F(MojoCdmProxyTest, Destroy) {
- Initialize();
- Process();
- EXPECT_TRUE(mojo_cdm_proxy_service_);
- Destroy();
- EXPECT_FALSE(mojo_cdm_proxy_service_);
-}
-
-TEST_F(MojoCdmProxyTest, ConnectionError_BeforeInitialize) {
- ForceConnectionError();
- Initialize(Status::kFail, false);
-}
-
-TEST_F(MojoCdmProxyTest, ConnectionError_AfterInitialize) {
- Initialize();
- Process();
- CreateMediaCryptoSession();
-
- ForceConnectionError();
-
- // Calling Process() and CreateMediaCryptoSession() without connection. These
- // calls should fail but the client should still get notified (about the
- // failure).
- Process(Status::kFail, false);
- CreateMediaCryptoSession(Status::kFail, false);
-}
-
-TEST_F(MojoCdmProxyTest, GetCdmProxyContext) {
- Initialize();
- SetCdm();
- EXPECT_TRUE(GetCdmProxyContext());
-}
-
-TEST_F(MojoCdmProxyTest, GetCdmProxyContext_AfterDestroy) {
- Initialize();
- SetCdm();
- EXPECT_TRUE(GetCdmProxyContext());
- Destroy();
- EXPECT_FALSE(GetCdmProxyContext());
-}
-
-TEST_F(MojoCdmProxyTest, GetCdmProxyContext_AfterConnectionError) {
- Initialize();
- SetCdm();
- EXPECT_TRUE(GetCdmProxyContext());
- ForceConnectionError();
- EXPECT_FALSE(GetCdmProxyContext());
-}
-
-} // namespace media
diff --git a/chromium/media/mojo/services/mojo_cdm_service.cc b/chromium/media/mojo/services/mojo_cdm_service.cc
index 80910478b16..16e60b82eb9 100644
--- a/chromium/media/mojo/services/mojo_cdm_service.cc
+++ b/chromium/media/mojo/services/mojo_cdm_service.cc
@@ -188,16 +188,8 @@ void MojoCdmService::OnCdmCreated(
&MojoCdmService::OnDecryptorConnectionError, base::Unretained(this)));
}
- // If the |context_| is not null, we should support connecting the |cdm| with
- // the media player in the same process, which also has access to the
- // |context_|. Hence pass back the |cdm_id_| obtained from the |context_|.
- // Otherwise, if the |cdm| has a valid CDM ID by itself, this CDM can proxy
- // all or parts of its functionalities to another remote CDM or CdmProxy. In
- // this case, just we pass the remote CDM ID back.
- int cdm_id = context_ ? cdm_id_ : cdm_context->GetCdmId();
-
cdm_promise_result->success = true;
- std::move(callback).Run(std::move(cdm_promise_result), cdm_id,
+ std::move(callback).Run(std::move(cdm_promise_result), cdm_id_,
std::move(decryptor_remote));
}
diff --git a/chromium/media/mojo/services/mojo_cdm_service_context.cc b/chromium/media/mojo/services/mojo_cdm_service_context.cc
index a49f74ea49a..6279a4c717f 100644
--- a/chromium/media/mojo/services/mojo_cdm_service_context.cc
+++ b/chromium/media/mojo/services/mojo_cdm_service_context.cc
@@ -11,58 +11,18 @@
#include "media/cdm/cdm_context_ref_impl.h"
#include "media/mojo/services/mojo_cdm_service.h"
-#if BUILDFLAG(ENABLE_CDM_PROXY)
-#include "media/mojo/services/mojo_cdm_proxy_service.h"
-#endif // BUILDFLAG(ENABLE_CDM_PROXY)
-
namespace media {
namespace {
// Helper function to get the next unique (per-process) CDM ID to be assigned to
-// a CDM or CdmProxy. It will be used to locate the CDM by the media players
-// living in the same process.
+// a CDM. It will be used to locate the CDM by the media players living in the
+// same process.
int GetNextCdmId() {
static int g_next_cdm_id = CdmContext::kInvalidCdmId + 1;
return g_next_cdm_id++;
}
-#if BUILDFLAG(ENABLE_CDM_PROXY)
-class CdmProxyContextRef : public CdmContextRef, public CdmContext {
- public:
- explicit CdmProxyContextRef(base::WeakPtr<CdmContext> cdm_context)
- : cdm_context_(cdm_context) {}
- ~CdmProxyContextRef() final {}
-
- // CdmContextRef implementation.
- CdmContext* GetCdmContext() final { return this; }
-
- private:
- // CdmContext implementation.
- std::unique_ptr<CallbackRegistration> RegisterEventCB(
- EventCB event_cb) final {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- return cdm_context_ ? cdm_context_->RegisterEventCB(std::move(event_cb))
- : nullptr;
- }
-
- Decryptor* GetDecryptor() final {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- return cdm_context_ ? cdm_context_->GetDecryptor() : nullptr;
- }
-
- CdmProxyContext* GetCdmProxyContext() final {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- return cdm_context_ ? cdm_context_->GetCdmProxyContext() : nullptr;
- }
-
- base::WeakPtr<CdmContext> cdm_context_;
- THREAD_CHECKER(thread_checker_);
-
- DISALLOW_COPY_AND_ASSIGN(CdmProxyContextRef);
-};
-#endif // BUILDFLAG(ENABLE_CDM_PROXY)
-
} // namespace
MojoCdmServiceContext::MojoCdmServiceContext() = default;
@@ -83,23 +43,6 @@ void MojoCdmServiceContext::UnregisterCdm(int cdm_id) {
cdm_services_.erase(cdm_id);
}
-#if BUILDFLAG(ENABLE_CDM_PROXY)
-int MojoCdmServiceContext::RegisterCdmProxy(
- MojoCdmProxyService* cdm_proxy_service) {
- DCHECK(cdm_proxy_service);
- int cdm_id = GetNextCdmId();
- cdm_proxy_services_[cdm_id] = cdm_proxy_service;
- DVLOG(1) << __func__ << ": CdmProxyService registered with CDM ID " << cdm_id;
- return cdm_id;
-}
-
-void MojoCdmServiceContext::UnregisterCdmProxy(int cdm_id) {
- DVLOG(1) << __func__ << ": cdm_id = " << cdm_id;
- DCHECK(cdm_proxy_services_.count(cdm_id));
- cdm_proxy_services_.erase(cdm_id);
-}
-#endif // BUILDFLAG(ENABLE_CDM_PROXY)
-
std::unique_ptr<CdmContextRef> MojoCdmServiceContext::GetCdmContextRef(
int cdm_id) {
DVLOG(1) << __func__ << ": cdm_id = " << cdm_id;
@@ -114,15 +57,6 @@ std::unique_ptr<CdmContextRef> MojoCdmServiceContext::GetCdmContextRef(
return std::make_unique<CdmContextRefImpl>(cdm_service->second->GetCdm());
}
-#if BUILDFLAG(ENABLE_CDM_PROXY)
- // Next check all CdmProxies.
- auto cdm_proxy_service = cdm_proxy_services_.find(cdm_id);
- if (cdm_proxy_service != cdm_proxy_services_.end()) {
- return std::make_unique<CdmProxyContextRef>(
- cdm_proxy_service->second->GetCdmContext());
- }
-#endif // BUILDFLAG(ENABLE_CDM_PROXY)
-
LOG(ERROR) << "CdmContextRef cannot be obtained for CDM ID: " << cdm_id;
return nullptr;
}
diff --git a/chromium/media/mojo/services/mojo_cdm_service_context.h b/chromium/media/mojo/services/mojo_cdm_service_context.h
index f074cf00b19..07290ba2a8f 100644
--- a/chromium/media/mojo/services/mojo_cdm_service_context.h
+++ b/chromium/media/mojo/services/mojo_cdm_service_context.h
@@ -16,10 +16,8 @@
namespace media {
-class CdmProxy;
class CdmContextRef;
class MojoCdmService;
-class MojoCdmProxyService;
// A class that creates, owns and manages all MojoCdmService instances.
class MEDIA_MOJO_EXPORT MojoCdmServiceContext {
@@ -33,15 +31,6 @@ class MEDIA_MOJO_EXPORT MojoCdmServiceContext {
// Unregisters the CDM. Must be called before the CDM is destroyed.
void UnregisterCdm(int cdm_id);
-#if BUILDFLAG(ENABLE_CDM_PROXY)
- // Registers the |cdm_proxy_service| and returns a unique (per-process) CDM
- // ID.
- int RegisterCdmProxy(MojoCdmProxyService* cdm_proxy_service);
-
- // Unregisters the CdmProxy. Must be called before the CdmProxy is destroyed.
- void UnregisterCdmProxy(int cdm_id);
-#endif // BUILDFLAG(ENABLE_CDM_PROXY)
-
// Returns the CdmContextRef associated with |cdm_id|.
std::unique_ptr<CdmContextRef> GetCdmContextRef(int cdm_id);
@@ -49,11 +38,6 @@ class MEDIA_MOJO_EXPORT MojoCdmServiceContext {
// A map between CDM ID and MojoCdmService.
std::map<int, MojoCdmService*> cdm_services_;
-#if BUILDFLAG(ENABLE_CDM_PROXY)
- // A map between CDM ID and MojoCdmProxyService.
- std::map<int, MojoCdmProxyService*> cdm_proxy_services_;
-#endif // BUILDFLAG(ENABLE_CDM_PROXY)
-
DISALLOW_COPY_AND_ASSIGN(MojoCdmServiceContext);
};
diff --git a/chromium/media/mojo/services/mojo_decryptor_service.cc b/chromium/media/mojo/services/mojo_decryptor_service.cc
index 32ad28c74ac..900808c4f37 100644
--- a/chromium/media/mojo/services/mojo_decryptor_service.cc
+++ b/chromium/media/mojo/services/mojo_decryptor_service.cc
@@ -48,29 +48,6 @@ class FrameResourceReleaserImpl final : public mojom::FrameResourceReleaser {
} // namespace
-// static
-std::unique_ptr<MojoDecryptorService> MojoDecryptorService::Create(
- int cdm_id,
- MojoCdmServiceContext* mojo_cdm_service_context) {
- auto cdm_context_ref = mojo_cdm_service_context->GetCdmContextRef(cdm_id);
- if (!cdm_context_ref) {
- DVLOG(1) << "CdmContextRef not found for CDM ID: " << cdm_id;
- return nullptr;
- }
-
- auto* cdm_context = cdm_context_ref->GetCdmContext();
- DCHECK(cdm_context);
-
- auto* decryptor = cdm_context->GetDecryptor();
- if (!decryptor) {
- DVLOG(1) << "CdmContext does not support Decryptor";
- return nullptr;
- }
-
- return std::make_unique<MojoDecryptorService>(decryptor,
- std::move(cdm_context_ref));
-}
-
MojoDecryptorService::MojoDecryptorService(
media::Decryptor* decryptor,
std::unique_ptr<CdmContextRef> cdm_context_ref)
diff --git a/chromium/media/mojo/services/mojo_decryptor_service.h b/chromium/media/mojo/services/mojo_decryptor_service.h
index b4fb2e7a699..4c9d6a02b0e 100644
--- a/chromium/media/mojo/services/mojo_decryptor_service.h
+++ b/chromium/media/mojo/services/mojo_decryptor_service.h
@@ -21,7 +21,6 @@
namespace media {
class DecoderBuffer;
-class MojoCdmServiceContext;
class MojoDecoderBufferReader;
class MojoDecoderBufferWriter;
@@ -32,10 +31,6 @@ class MEDIA_MOJO_EXPORT MojoDecryptorService : public mojom::Decryptor {
using StreamType = media::Decryptor::StreamType;
using Status = media::Decryptor::Status;
- static std::unique_ptr<MojoDecryptorService> Create(
- int cdm_id,
- MojoCdmServiceContext* mojo_cdm_service_context);
-
// If |cdm_context_ref| is null, caller must ensure that |decryptor| outlives
// |this|. Otherwise, |decryptor| is guaranteed to be valid as long as
// |cdm_context_ref| is held.
diff --git a/chromium/media/mojo/services/mojo_media_client.cc b/chromium/media/mojo/services/mojo_media_client.cc
index 4b4fca75d96..b9f53ad90c5 100644
--- a/chromium/media/mojo/services/mojo_media_client.cc
+++ b/chromium/media/mojo/services/mojo_media_client.cc
@@ -11,10 +11,6 @@
#include "media/base/renderer.h"
#include "media/base/video_decoder.h"
-#if BUILDFLAG(ENABLE_CDM_PROXY)
-#include "media/cdm/cdm_proxy.h"
-#endif // BUILDFLAG(ENABLE_CDM_PROXY)
-
namespace media {
MojoMediaClient::MojoMediaClient() = default;
@@ -44,7 +40,7 @@ std::unique_ptr<VideoDecoder> MojoMediaClient::CreateVideoDecoder(
}
std::unique_ptr<Renderer> MojoMediaClient::CreateRenderer(
- service_manager::mojom::InterfaceProvider* host_interfaces,
+ mojom::FrameInterfaceFactory* frame_interfaces,
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
MediaLog* media_log,
const std::string& audio_device_id) {
@@ -53,7 +49,7 @@ std::unique_ptr<Renderer> MojoMediaClient::CreateRenderer(
#if BUILDFLAG(ENABLE_CAST_RENDERER)
std::unique_ptr<Renderer> MojoMediaClient::CreateCastRenderer(
- service_manager::mojom::InterfaceProvider* host_interfaces,
+ mojom::FrameInterfaceFactory* frame_interfaces,
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
MediaLog* media_log,
const base::UnguessableToken& overlay_plane_id) {
@@ -62,15 +58,8 @@ std::unique_ptr<Renderer> MojoMediaClient::CreateCastRenderer(
#endif // BUILDFLAG(ENABLE_CAST_RENDERER)
std::unique_ptr<CdmFactory> MojoMediaClient::CreateCdmFactory(
- service_manager::mojom::InterfaceProvider* host_interfaces) {
- return nullptr;
-}
-
-#if BUILDFLAG(ENABLE_CDM_PROXY)
-std::unique_ptr<CdmProxy> MojoMediaClient::CreateCdmProxy(
- const base::Token& cdm_guid) {
+ mojom::FrameInterfaceFactory* frame_interfaces) {
return nullptr;
}
-#endif // BUILDFLAG(ENABLE_CDM_PROXY)
} // namespace media
diff --git a/chromium/media/mojo/services/mojo_media_client.h b/chromium/media/mojo/services/mojo_media_client.h
index 03e62e3f3f0..03263ee3c63 100644
--- a/chromium/media/mojo/services/mojo_media_client.h
+++ b/chromium/media/mojo/services/mojo_media_client.h
@@ -15,30 +15,23 @@
#include "media/base/overlay_info.h"
#include "media/media_buildflags.h"
#include "media/mojo/buildflags.h"
+#include "media/mojo/mojom/frame_interface_factory.mojom.h"
#include "media/mojo/mojom/video_decoder.mojom.h"
#include "media/mojo/services/media_mojo_export.h"
#include "media/video/supported_video_decoder_config.h"
namespace base {
class SingleThreadTaskRunner;
-class Token;
} // namespace base
namespace gfx {
class ColorSpace;
} // namespace gfx
-namespace service_manager {
-namespace mojom {
-class InterfaceProvider;
-} // namespace mojom
-} // namespace service_manager
-
namespace media {
class AudioDecoder;
class CdmFactory;
-class CdmProxy;
class MediaLog;
class Renderer;
class VideoDecoder;
@@ -76,7 +69,7 @@ class MEDIA_MOJO_EXPORT MojoMediaClient {
// TODO(hubbe): Find out whether we should pass in |target_color_space| here.
// TODO(guohuideng): Merge this function into CreateCastRenderer.
virtual std::unique_ptr<Renderer> CreateRenderer(
- service_manager::mojom::InterfaceProvider* host_interfaces,
+ mojom::FrameInterfaceFactory* frame_interfaces,
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
MediaLog* media_log,
const std::string& audio_device_id);
@@ -88,23 +81,17 @@ class MEDIA_MOJO_EXPORT MojoMediaClient {
// associtated with.
// Chromecast also uses CreateRenderer to create "audio only" renderers.
virtual std::unique_ptr<Renderer> CreateCastRenderer(
- service_manager::mojom::InterfaceProvider* host_interfaces,
+ mojom::FrameInterfaceFactory* frame_interfaces,
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
MediaLog* media_log,
const base::UnguessableToken& overlay_plane_id);
#endif // BUILDFLAG(ENABLE_CAST_RENDERER)
- // Returns the CdmFactory to be used by MojoCdmService. |host_interfaces| can
+ // Returns the CdmFactory to be used by MojoCdmService. |frame_interfaces| can
// be used to request interfaces provided remotely by the host. It may be a
// nullptr if the host chose not to bind the InterfacePtr.
virtual std::unique_ptr<CdmFactory> CreateCdmFactory(
- service_manager::mojom::InterfaceProvider* host_interfaces);
-
-#if BUILDFLAG(ENABLE_CDM_PROXY)
- // Creates a CdmProxy that proxies part of CDM functionalities to a different
- // entity, e.g. hardware CDM modules.
- virtual std::unique_ptr<CdmProxy> CreateCdmProxy(const base::Token& cdm_guid);
-#endif // BUILDFLAG(ENABLE_CDM_PROXY)
+ mojom::FrameInterfaceFactory* frame_interfaces);
protected:
MojoMediaClient();
diff --git a/chromium/media/mojo/services/mojo_video_encode_accelerator_provider.cc b/chromium/media/mojo/services/mojo_video_encode_accelerator_provider.cc
index c2644513bdc..0ade6cc8fd8 100644
--- a/chromium/media/mojo/services/mojo_video_encode_accelerator_provider.cc
+++ b/chromium/media/mojo/services/mojo_video_encode_accelerator_provider.cc
@@ -7,7 +7,6 @@
#include <memory>
#include <utility>
-#include "base/logging.h"
#include "media/base/bind_to_current_loop.h"
#include "media/base/limits.h"
#include "media/gpu/gpu_video_encode_accelerator_factory.h"
diff --git a/chromium/media/mojo/services/test_mojo_media_client.cc b/chromium/media/mojo/services/test_mojo_media_client.cc
index aa6240b7912..7d51f3db006 100644
--- a/chromium/media/mojo/services/test_mojo_media_client.cc
+++ b/chromium/media/mojo/services/test_mojo_media_client.cc
@@ -8,6 +8,7 @@
#include "base/run_loop.h"
#include "base/threading/thread_task_runner_handle.h"
+#include "build/build_config.h"
#include "media/audio/audio_device_description.h"
#include "media/audio/audio_manager.h"
#include "media/audio/audio_output_stream_sink.h"
@@ -21,12 +22,6 @@
#include "media/renderers/default_decoder_factory.h"
#include "media/renderers/default_renderer_factory.h"
-#if BUILDFLAG(ENABLE_CDM_PROXY)
-#include "media/cdm/cdm_paths.h" // nogncheck
-#include "media/cdm/cdm_proxy.h" // nogncheck
-#include "media/cdm/library_cdm/clear_key_cdm/clear_key_cdm_proxy.h" // nogncheck
-#endif
-
namespace media {
TestMojoMediaClient::TestMojoMediaClient() = default;
@@ -55,7 +50,7 @@ void TestMojoMediaClient::Initialize() {
}
std::unique_ptr<Renderer> TestMojoMediaClient::CreateRenderer(
- service_manager::mojom::InterfaceProvider* host_interfaces,
+ mojom::FrameInterfaceFactory* frame_interfaces,
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
MediaLog* media_log,
const std::string& /* audio_device_id */) {
@@ -65,9 +60,15 @@ std::unique_ptr<Renderer> TestMojoMediaClient::CreateRenderer(
}
if (!renderer_factory_) {
+#if defined(OS_ANDROID)
+ renderer_factory_ = std::make_unique<DefaultRendererFactory>(
+ media_log, decoder_factory_.get(),
+ DefaultRendererFactory::GetGpuFactoriesCB());
+#else
renderer_factory_ = std::make_unique<DefaultRendererFactory>(
media_log, decoder_factory_.get(),
DefaultRendererFactory::GetGpuFactoriesCB(), nullptr);
+#endif
}
// We cannot share AudioOutputStreamSink or NullVideoSink among different
@@ -91,29 +92,19 @@ std::unique_ptr<Renderer> TestMojoMediaClient::CreateRenderer(
#if BUILDFLAG(ENABLE_CAST_RENDERER)
std::unique_ptr<Renderer> TestMojoMediaClient::CreateCastRenderer(
- service_manager::mojom::InterfaceProvider* host_interfaces,
+ mojom::FrameInterfaceFactory* frame_interfaces,
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
MediaLog* media_log,
const base::UnguessableToken& /* overlay_plane_id */) {
- return CreateRenderer(host_interfaces, task_runner, media_log, std::string());
+ return CreateRenderer(frame_interfaces, task_runner, media_log,
+ std::string());
}
#endif // BUILDFLAG(ENABLE_CAST_RENDERER)
std::unique_ptr<CdmFactory> TestMojoMediaClient::CreateCdmFactory(
- service_manager::mojom::InterfaceProvider* /* host_interfaces */) {
+ mojom::FrameInterfaceFactory* /* frame_interfaces */) {
DVLOG(1) << __func__;
return std::make_unique<DefaultCdmFactory>();
}
-#if BUILDFLAG(ENABLE_CDM_PROXY)
-std::unique_ptr<CdmProxy> TestMojoMediaClient::CreateCdmProxy(
- const base::Token& cdm_guid) {
- DVLOG(1) << __func__ << ": cdm_guid = " << cdm_guid.ToString();
- if (cdm_guid == kClearKeyCdmGuid)
- return std::make_unique<ClearKeyCdmProxy>();
-
- return nullptr;
-}
-#endif // BUILDFLAG(ENABLE_CDM_PROXY)
-
} // namespace media
diff --git a/chromium/media/mojo/services/test_mojo_media_client.h b/chromium/media/mojo/services/test_mojo_media_client.h
index d72fedd59a2..c8899c05404 100644
--- a/chromium/media/mojo/services/test_mojo_media_client.h
+++ b/chromium/media/mojo/services/test_mojo_media_client.h
@@ -30,22 +30,19 @@ class TestMojoMediaClient : public MojoMediaClient {
// MojoMediaClient implementation.
void Initialize() final;
std::unique_ptr<Renderer> CreateRenderer(
- service_manager::mojom::InterfaceProvider* host_interfaces,
+ mojom::FrameInterfaceFactory* frame_interfaces,
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
MediaLog* media_log,
const std::string& audio_device_id) final;
#if BUILDFLAG(ENABLE_CAST_RENDERER)
std::unique_ptr<Renderer> CreateCastRenderer(
- service_manager::mojom::InterfaceProvider* host_interfaces,
+ mojom::FrameInterfaceFactory* frame_interfaces,
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
MediaLog* media_log,
const base::UnguessableToken& overlay_plane_id) final;
#endif // BUILDFLAG(ENABLE_CAST_RENDERER)
std::unique_ptr<CdmFactory> CreateCdmFactory(
- service_manager::mojom::InterfaceProvider* /* host_interfaces */) final;
-#if BUILDFLAG(ENABLE_CDM_PROXY)
- std::unique_ptr<CdmProxy> CreateCdmProxy(const base::Token& cdm_guid) final;
-#endif // BUILDFLAG(ENABLE_CDM_PROXY)
+ mojom::FrameInterfaceFactory* /* frame_interfaces */) final;
private:
std::unique_ptr<AudioManager> audio_manager_;
diff --git a/chromium/media/mojo/services/video_decode_perf_history_unittest.cc b/chromium/media/mojo/services/video_decode_perf_history_unittest.cc
index ac736bf0ae0..3a5a11b8d66 100644
--- a/chromium/media/mojo/services/video_decode_perf_history_unittest.cc
+++ b/chromium/media/mojo/services/video_decode_perf_history_unittest.cc
@@ -52,6 +52,7 @@ class FakeVideoDecodeStatsDB : public VideoDecodeStatsDB {
// Call CompleteInitialize(...) to run |init_cb| callback.
void Initialize(base::OnceCallback<void(bool)> init_cb) override {
+ EXPECT_FALSE(!!pendnding_init_cb_);
pendnding_init_cb_ = std::move(init_cb);
}
@@ -59,7 +60,7 @@ class FakeVideoDecodeStatsDB : public VideoDecodeStatsDB {
// for success.
void CompleteInitialize(bool success) {
DVLOG(2) << __func__ << " running with success = " << success;
- EXPECT_FALSE(!pendnding_init_cb_);
+ EXPECT_TRUE(!!pendnding_init_cb_);
std::move(pendnding_init_cb_).Run(success);
}
@@ -1037,4 +1038,50 @@ INSTANTIATE_TEST_SUITE_P(VaryDBInitTiming,
VideoDecodePerfHistoryParamTest,
::testing::ValuesIn(kPerfHistoryTestParams));
-} // namespace media
+//
+// The following test are not parameterized. They instead always hard code
+// deferred initialization.
+//
+
+TEST_F(VideoDecodePerfHistoryTest, ClearHistoryTriggersSuccessfulInitialize) {
+ // Clear the DB. Completion callback shouldn't fire until initialize
+ // completes.
+ EXPECT_CALL(*this, MockOnClearedHistory()).Times(0);
+ perf_history_->ClearHistory(
+ base::BindOnce(&VideoDecodePerfHistoryParamTest::MockOnClearedHistory,
+ base::Unretained(this)));
+
+ // Give completion callback a chance to fire. Confirm it did not fire.
+ task_environment_.RunUntilIdle();
+ testing::Mock::VerifyAndClearExpectations(this);
+
+ // Expect completion callback after we successfully initialize.
+ EXPECT_CALL(*this, MockOnClearedHistory());
+ GetFakeDB()->CompleteInitialize(true);
+
+ // Give deferred callback a chance to fire.
+ task_environment_.RunUntilIdle();
+}
+
+TEST_F(VideoDecodePerfHistoryTest, ClearHistoryTriggersFailedInitialize) {
+ // Clear the DB. Completion callback shouldn't fire until initialize
+ // completes.
+ EXPECT_CALL(*this, MockOnClearedHistory()).Times(0);
+ perf_history_->ClearHistory(
+ base::BindOnce(&VideoDecodePerfHistoryParamTest::MockOnClearedHistory,
+ base::Unretained(this)));
+
+ // Give completion callback a chance to fire. Confirm it did not fire.
+ task_environment_.RunUntilIdle();
+ testing::Mock::VerifyAndClearExpectations(this);
+
+ // Expect completion callback after completing initialize. "Failure" is still
+ // a form of completion.
+ EXPECT_CALL(*this, MockOnClearedHistory());
+ GetFakeDB()->CompleteInitialize(false);
+
+ // Give deferred callback a chance to fire.
+ task_environment_.RunUntilIdle();
+}
+
+} // namespace media \ No newline at end of file
diff --git a/chromium/media/mojo/services/watch_time_recorder.cc b/chromium/media/mojo/services/watch_time_recorder.cc
index 512e4c950a1..6c1d44c7632 100644
--- a/chromium/media/mojo/services/watch_time_recorder.cc
+++ b/chromium/media/mojo/services/watch_time_recorder.cc
@@ -421,7 +421,7 @@ void WatchTimeRecorder::RecordUkmPlaybackData() {
base::flat_set<AudioCodecProfile> aac_profiles;
- base::TimeDelta total_watch_time;
+ base::TimeDelta total_foreground_audible_watch_time;
for (auto& ukm_record : ukm_records_) {
ukm::builders::Media_BasicPlayback builder(source_id_);
@@ -446,7 +446,11 @@ void WatchTimeRecorder::RecordUkmPlaybackData() {
// Only one of these keys should be present.
DCHECK(!recorded_all_metric);
recorded_all_metric = true;
- total_watch_time += kv.second;
+
+ // We should only add to the total watchtime if we were not in the
+ // background and not muted.
+ if (!properties_->is_muted && !properties_->is_background)
+ total_foreground_audible_watch_time += kv.second;
builder.SetWatchTime(kv.second.InMilliseconds());
if (ukm_record.total_underflow_count) {
@@ -554,10 +558,10 @@ void WatchTimeRecorder::RecordUkmPlaybackData() {
base::UmaHistogramEnumeration("Media.AudioCodecProfile.AAC", profile);
}
- if (total_watch_time > base::TimeDelta()) {
+ if (total_foreground_audible_watch_time > base::TimeDelta()) {
std::move(record_playback_cb_)
- .Run(total_watch_time, last_timestamp_, properties_->has_video,
- properties_->has_audio);
+ .Run(total_foreground_audible_watch_time, last_timestamp_,
+ properties_->has_video, properties_->has_audio);
}
ukm_records_.clear();
diff --git a/chromium/media/parsers/jpeg_parser.cc b/chromium/media/parsers/jpeg_parser.cc
index 344f7c36995..17dcc3c020b 100644
--- a/chromium/media/parsers/jpeg_parser.cc
+++ b/chromium/media/parsers/jpeg_parser.cc
@@ -4,6 +4,8 @@
#include "media/parsers/jpeg_parser.h"
+#include <cstring>
+
#include "base/big_endian.h"
#include "base/logging.h"
#include "base/stl_util.h"
diff --git a/chromium/media/parsers/vp8_bool_decoder.cc b/chromium/media/parsers/vp8_bool_decoder.cc
index 4f156ad8daa..17607732f69 100644
--- a/chromium/media/parsers/vp8_bool_decoder.cc
+++ b/chromium/media/parsers/vp8_bool_decoder.cc
@@ -46,6 +46,7 @@
#include <algorithm>
+#include "base/check_op.h"
#include "base/numerics/safe_conversions.h"
namespace media {
diff --git a/chromium/media/parsers/vp8_parser.cc b/chromium/media/parsers/vp8_parser.cc
index b38e3048668..b52c59fd070 100644
--- a/chromium/media/parsers/vp8_parser.cc
+++ b/chromium/media/parsers/vp8_parser.cc
@@ -7,6 +7,8 @@
#include "media/parsers/vp8_parser.h"
+#include <cstring>
+
#include "base/logging.h"
namespace media {
diff --git a/chromium/media/parsers/webp_parser.cc b/chromium/media/parsers/webp_parser.cc
index b0348037e79..9d2ba7a12a8 100644
--- a/chromium/media/parsers/webp_parser.cc
+++ b/chromium/media/parsers/webp_parser.cc
@@ -9,7 +9,7 @@
#include <string.h>
#include "base/bits.h"
-#include "base/logging.h"
+#include "base/check_op.h"
#include "base/numerics/safe_conversions.h"
#include "build/build_config.h"
#include "media/parsers/vp8_parser.h"
diff --git a/chromium/media/remoting/courier_renderer_factory.cc b/chromium/media/remoting/courier_renderer_factory.cc
index 22cbb5ef31a..23598e69503 100644
--- a/chromium/media/remoting/courier_renderer_factory.cc
+++ b/chromium/media/remoting/courier_renderer_factory.cc
@@ -6,7 +6,7 @@
#include <memory>
-#include "base/logging.h"
+#include "base/check.h"
#include "build/build_config.h"
#include "build/buildflag.h"
#include "media/base/overlay_info.h"
diff --git a/chromium/media/remoting/integration_test.cc b/chromium/media/remoting/integration_test.cc
index e56be6ae64d..bc0aa888909 100644
--- a/chromium/media/remoting/integration_test.cc
+++ b/chromium/media/remoting/integration_test.cc
@@ -12,25 +12,24 @@
namespace media {
namespace remoting {
-namespace {
-
constexpr int kAppendTimeSec = 1;
-std::unique_ptr<Renderer> CreateEnd2EndTestRenderer(
- std::unique_ptr<Renderer> default_renderer) {
- return std::make_unique<End2EndTestRenderer>(std::move(default_renderer));
-}
-
-} // namespace
-
class MediaRemotingIntegrationTest : public testing::Test,
public PipelineIntegrationTestBase {
public:
MediaRemotingIntegrationTest() {
- SetWrapRendererCB(base::BindRepeating(&CreateEnd2EndTestRenderer));
+ SetCreateRendererCB(base::BindRepeating(
+ &MediaRemotingIntegrationTest::CreateEnd2EndTestRenderer,
+ base::Unretained(this)));
}
private:
+ std::unique_ptr<Renderer> CreateEnd2EndTestRenderer(
+ base::Optional<RendererFactoryType> factory_type) {
+ return std::make_unique<End2EndTestRenderer>(
+ this->CreateDefaultRenderer(factory_type));
+ }
+
DISALLOW_COPY_AND_ASSIGN(MediaRemotingIntegrationTest);
};
diff --git a/chromium/media/remoting/renderer_controller.cc b/chromium/media/remoting/renderer_controller.cc
index a6f8b0ce595..ccbddb1b424 100644
--- a/chromium/media/remoting/renderer_controller.cc
+++ b/chromium/media/remoting/renderer_controller.cc
@@ -296,17 +296,22 @@ void RendererController::OnDataSourceInitialized(
UpdateRemotePlaybackAvailabilityMonitoringState();
}
+void RendererController::OnHlsManifestDetected() {
+#if defined(OS_ANDROID)
+ is_hls_ = true;
+ UpdateRemotePlaybackAvailabilityMonitoringState();
+#else
+ NOTREACHED();
+#endif
+}
+
void RendererController::UpdateRemotePlaybackAvailabilityMonitoringState() {
// Currently RemotePlayback-initated media remoting only supports URL flinging
// thus the source is supported when the URL is either http or https, video and
// audio codecs are supported by the remote playback device; HLS is playable by
// Chrome on Android (which is not detected by the pipeline metadata atm).
#if defined(OS_ANDROID)
- // TODO(tguilbert): Detect the presence of HLS based on demuxing results,
- // rather than the URL string. See crbug.com/663503.
- const bool is_media_supported =
- MediaCodecUtil::IsHLSURL(url_after_redirects_) ||
- IsRemotePlaybackSupported();
+ const bool is_media_supported = is_hls_ || IsRemotePlaybackSupported();
#else
const bool is_media_supported = IsAudioOrVideoSupported();
#endif
diff --git a/chromium/media/remoting/renderer_controller.h b/chromium/media/remoting/renderer_controller.h
index a38bf5b3ff0..8c22bbf09eb 100644
--- a/chromium/media/remoting/renderer_controller.h
+++ b/chromium/media/remoting/renderer_controller.h
@@ -61,6 +61,7 @@ class RendererController final : public mojom::RemotingSource,
void OnPlaying() override;
void OnPaused() override;
void OnDataSourceInitialized(const GURL& url_after_redirects) override;
+ void OnHlsManifestDetected() override;
void SetClient(MediaObserverClient* client) override;
base::WeakPtr<RendererController> GetWeakPtr() {
@@ -213,6 +214,8 @@ class RendererController final : public mojom::RemotingSource,
// Current data source information.
GURL url_after_redirects_;
+ bool is_hls_ = false;
+
// Records session events of interest.
SessionMetricsRecorder metrics_recorder_;
diff --git a/chromium/media/remoting/renderer_controller_unittest.cc b/chromium/media/remoting/renderer_controller_unittest.cc
index 809ae074f82..b42a44457c9 100644
--- a/chromium/media/remoting/renderer_controller_unittest.cc
+++ b/chromium/media/remoting/renderer_controller_unittest.cc
@@ -89,7 +89,9 @@ class RendererControllerTest : public ::testing::Test,
unsigned DecodedFrameCount() const override { return decoded_frames_; }
- void UpdateRemotePlaybackCompatibility(bool is_compatibe) override {}
+ void UpdateRemotePlaybackCompatibility(bool is_compatible) override {
+ is_remote_playback_compatible_ = is_compatible;
+ }
void CreateCdm(bool is_remoting) { is_remoting_cdm_ = is_remoting; }
@@ -163,6 +165,7 @@ class RendererControllerTest : public ::testing::Test,
bool is_rendering_remotely_ = false;
bool is_remoting_cdm_ = false;
bool disable_pipeline_suspend_ = false;
+ bool is_remote_playback_compatible_ = false;
size_t decoded_bytes_ = 0;
unsigned decoded_frames_ = 0;
base::SimpleTestTickClock clock_;
@@ -397,5 +400,24 @@ TEST_F(RendererControllerTest, SetClientNullptr) {
ExpectInLocalRendering();
}
+#if defined(OS_ANDROID)
+TEST_F(RendererControllerTest, RemotePlaybackHlsCompatibility) {
+ controller_ = FakeRemoterFactory::CreateController(true);
+ controller_->SetClient(this);
+
+ controller_->OnDataSourceInitialized(GURL("http://example.com/foo.m3u8"));
+
+ PipelineMetadata incompatible_metadata;
+ incompatible_metadata.has_video = false;
+ incompatible_metadata.has_audio = false;
+ controller_->OnMetadataChanged(incompatible_metadata);
+ EXPECT_FALSE(is_remote_playback_compatible_);
+
+ // HLS is compatible with RemotePlayback regardless of the metadata we have.
+ controller_->OnHlsManifestDetected();
+ EXPECT_TRUE(is_remote_playback_compatible_);
+}
+#endif
+
} // namespace remoting
} // namespace media
diff --git a/chromium/media/renderers/BUILD.gn b/chromium/media/renderers/BUILD.gn
index 509124a518c..684f41f71fe 100644
--- a/chromium/media/renderers/BUILD.gn
+++ b/chromium/media/renderers/BUILD.gn
@@ -32,6 +32,8 @@ source_set("renderers") {
"video_renderer_impl.h",
"video_resource_updater.cc",
"video_resource_updater.h",
+ "yuv_util.cc",
+ "yuv_util.h",
]
deps = [
@@ -59,7 +61,6 @@ source_set("renderers") {
if (is_fuchsia) {
deps += [ "//fuchsia/engine:switches" ]
}
-
configs += [
"//media:subcomponent_config",
@@ -97,7 +98,14 @@ source_set("unit_tests") {
"//ui/gl:test_support",
]
if (is_win) {
- deps += [ "//media/renderers:media_foundation_renderer" ]
+ sources += [
+ "win/media_foundation_renderer_integration_test.cc",
+ "win/media_foundation_renderer_unittest.cc",
+ ]
+ deps += [
+ "//media/renderers:media_foundation_renderer",
+ "//media/test:pipeline_integration_test_base",
+ ]
}
}
@@ -112,6 +120,9 @@ if (is_win) {
"win/media_foundation_audio_stream.h",
"win/media_foundation_protection_manager.cc",
"win/media_foundation_protection_manager.h",
+ "win/media_foundation_renderer.cc",
+ "win/media_foundation_renderer.h",
+ "win/media_foundation_renderer_extension.h",
"win/media_foundation_source_wrapper.cc",
"win/media_foundation_source_wrapper.h",
"win/media_foundation_stream_wrapper.cc",
diff --git a/chromium/media/renderers/default_decoder_factory.cc b/chromium/media/renderers/default_decoder_factory.cc
index c3a41f45c68..f86855c087b 100644
--- a/chromium/media/renderers/default_decoder_factory.cc
+++ b/chromium/media/renderers/default_decoder_factory.cc
@@ -121,10 +121,22 @@ void DefaultDecoderFactory::CreateVideoDecoders(
#if defined(OS_FUCHSIA)
if (gpu_factories) {
auto* context_provider = gpu_factories->GetMediaContextProvider();
- DCHECK(context_provider);
- video_decoders->push_back(
- CreateFuchsiaVideoDecoder(gpu_factories->SharedImageInterface(),
- context_provider->ContextSupport()));
+
+ // GetMediaContextProvider() may return nullptr when the context was lost
+ // (e.g. after GPU process crash). To handle this case RenderThreadImpl
+ // creates a new GpuVideoAcceleratorFactories with a new ContextProvider
+ // instance, but there is no way to get it here. For now just don't add
+ // FuchsiaVideoDecoder in that scenario.
+ //
+ // TODO(crbug.com/580386): Handle context loss properly.
+ if (context_provider) {
+ video_decoders->push_back(
+ CreateFuchsiaVideoDecoder(gpu_factories->SharedImageInterface(),
+ context_provider->ContextSupport()));
+ } else {
+ DLOG(ERROR)
+ << "Can't created FuchsiaVideoDecoder due to GPU context loss.";
+ }
}
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
diff --git a/chromium/media/renderers/default_renderer_factory.cc b/chromium/media/renderers/default_renderer_factory.cc
index 074896d761a..9d5c2ac959f 100644
--- a/chromium/media/renderers/default_renderer_factory.cc
+++ b/chromium/media/renderers/default_renderer_factory.cc
@@ -8,6 +8,7 @@
#include <utility>
#include "base/bind.h"
+#include "build/build_config.h"
#include "media/base/audio_buffer.h"
#include "media/base/bind_to_current_loop.h"
#include "media/base/decoder_factory.h"
@@ -19,6 +20,17 @@
namespace media {
+#if defined(OS_ANDROID)
+DefaultRendererFactory::DefaultRendererFactory(
+ MediaLog* media_log,
+ DecoderFactory* decoder_factory,
+ const GetGpuFactoriesCB& get_gpu_factories_cb)
+ : media_log_(media_log),
+ decoder_factory_(decoder_factory),
+ get_gpu_factories_cb_(get_gpu_factories_cb) {
+ DCHECK(decoder_factory_);
+}
+#else
DefaultRendererFactory::DefaultRendererFactory(
MediaLog* media_log,
DecoderFactory* decoder_factory,
@@ -30,6 +42,7 @@ DefaultRendererFactory::DefaultRendererFactory(
speech_recognition_client_(std::move(speech_recognition_client)) {
DCHECK(decoder_factory_);
}
+#endif
DefaultRendererFactory::~DefaultRendererFactory() = default;
@@ -115,10 +128,12 @@ std::unique_ptr<Renderer> DefaultRendererFactory::CreateRenderer(
void DefaultRendererFactory::TranscribeAudio(
scoped_refptr<media::AudioBuffer> buffer) {
+#if !defined(OS_ANDROID)
if (speech_recognition_client_ &&
speech_recognition_client_->IsSpeechRecognitionAvailable()) {
speech_recognition_client_->AddAudio(std::move(buffer));
}
+#endif
}
} // namespace media
diff --git a/chromium/media/renderers/default_renderer_factory.h b/chromium/media/renderers/default_renderer_factory.h
index b47761f561f..09de8928651 100644
--- a/chromium/media/renderers/default_renderer_factory.h
+++ b/chromium/media/renderers/default_renderer_factory.h
@@ -10,9 +10,13 @@
#include "base/callback.h"
#include "base/macros.h"
+#include "build/build_config.h"
#include "media/base/media_export.h"
#include "media/base/renderer_factory.h"
+
+#if !defined(OS_ANDROID)
#include "media/base/speech_recognition_client.h"
+#endif
namespace media {
@@ -36,11 +40,17 @@ class MEDIA_EXPORT DefaultRendererFactory : public RendererFactory {
using GetGpuFactoriesCB =
base::RepeatingCallback<GpuVideoAcceleratorFactories*()>;
+#if defined(OS_ANDROID)
+ DefaultRendererFactory(MediaLog* media_log,
+ DecoderFactory* decoder_factory,
+ const GetGpuFactoriesCB& get_gpu_factories_cb);
+#else
DefaultRendererFactory(
MediaLog* media_log,
DecoderFactory* decoder_factory,
const GetGpuFactoriesCB& get_gpu_factories_cb,
std::unique_ptr<SpeechRecognitionClient> speech_recognition_client);
+#endif
~DefaultRendererFactory() final;
std::unique_ptr<Renderer> CreateRenderer(
@@ -71,7 +81,9 @@ class MEDIA_EXPORT DefaultRendererFactory : public RendererFactory {
// Creates factories for supporting video accelerators. May be null.
GetGpuFactoriesCB get_gpu_factories_cb_;
+#if !defined(OS_ANDROID)
std::unique_ptr<SpeechRecognitionClient> speech_recognition_client_;
+#endif
DISALLOW_COPY_AND_ASSIGN(DefaultRendererFactory);
};
diff --git a/chromium/media/renderers/paint_canvas_video_renderer.cc b/chromium/media/renderers/paint_canvas_video_renderer.cc
index 0fdd0de5bf8..5eaa9529549 100644
--- a/chromium/media/renderers/paint_canvas_video_renderer.cc
+++ b/chromium/media/renderers/paint_canvas_video_renderer.cc
@@ -32,9 +32,11 @@
#include "gpu/command_buffer/common/shared_image_usage.h"
#include "media/base/data_buffer.h"
#include "media/base/video_frame.h"
+#include "media/renderers/yuv_util.h"
#include "third_party/libyuv/include/libyuv.h"
#include "third_party/skia/include/core/SkImage.h"
#include "third_party/skia/include/core/SkImageGenerator.h"
+#include "third_party/skia/include/core/SkImageInfo.h"
#include "third_party/skia/include/gpu/GrBackendSurface.h"
#include "third_party/skia/include/gpu/GrContext.h"
#include "third_party/skia/include/gpu/gl/GrGLTypes.h"
@@ -46,12 +48,14 @@
// shown here to indicate where ideal conversions are currently missing.
#if SK_B32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_R32_SHIFT == 16 && \
SK_A32_SHIFT == 24
+#define LIBYUV_I400_TO_ARGB libyuv::I400ToARGB
#define LIBYUV_I420_TO_ARGB libyuv::I420ToARGB
#define LIBYUV_I422_TO_ARGB libyuv::I422ToARGB
#define LIBYUV_I444_TO_ARGB libyuv::I444ToARGB
#define LIBYUV_I420ALPHA_TO_ARGB libyuv::I420AlphaToARGB
+#define LIBYUV_J400_TO_ARGB libyuv::J400ToARGB
#define LIBYUV_J420_TO_ARGB libyuv::J420ToARGB
#define LIBYUV_J422_TO_ARGB libyuv::J422ToARGB
#define LIBYUV_J444_TO_ARGB libyuv::J444ToARGB
@@ -83,12 +87,14 @@
#define LIBYUV_NV12_TO_ARGB libyuv::NV12ToARGB
#elif SK_R32_SHIFT == 0 && SK_G32_SHIFT == 8 && SK_B32_SHIFT == 16 && \
SK_A32_SHIFT == 24
+#define LIBYUV_I400_TO_ARGB libyuv::I400ToARGB
#define LIBYUV_I420_TO_ARGB libyuv::I420ToABGR
#define LIBYUV_I422_TO_ARGB libyuv::I422ToABGR
#define LIBYUV_I444_TO_ARGB libyuv::I444ToABGR
#define LIBYUV_I420ALPHA_TO_ARGB libyuv::I420AlphaToABGR
+#define LIBYUV_J400_TO_ARGB libyuv::J400ToARGB
#define LIBYUV_J420_TO_ARGB libyuv::J420ToABGR
#define LIBYUV_J422_TO_ARGB libyuv::J422ToABGR
#define LIBYUV_J444_TO_ARGB libyuv::J444ToABGR
@@ -147,32 +153,6 @@ class SyncTokenClientImpl : public VideoFrame::SyncTokenClient {
DISALLOW_IMPLICIT_CONSTRUCTORS(SyncTokenClientImpl);
};
-sk_sp<SkImage> YUVGrBackendTexturesToSkImage(
- GrContext* gr_context,
- gfx::ColorSpace video_color_space,
- VideoPixelFormat video_format,
- GrBackendTexture* yuv_textures,
- const GrBackendTexture& result_texture) {
- // TODO(hubbe): This should really default to rec709.
- // https://crbug.com/828599
- SkYUVColorSpace color_space = kRec601_SkYUVColorSpace;
- video_color_space.ToSkYUVColorSpace(&color_space);
-
- switch (video_format) {
- case PIXEL_FORMAT_NV12:
- return SkImage::MakeFromNV12TexturesCopyWithExternalBackend(
- gr_context, color_space, yuv_textures, kTopLeft_GrSurfaceOrigin,
- result_texture);
- case PIXEL_FORMAT_I420:
- return SkImage::MakeFromYUVTexturesCopyWithExternalBackend(
- gr_context, color_space, yuv_textures, kTopLeft_GrSurfaceOrigin,
- result_texture);
- default:
- NOTREACHED();
- return nullptr;
- }
-}
-
// Helper class that begins/ends access to a mailbox within a scope. The mailbox
// must have been imported into |texture|.
class ScopedSharedImageAccess {
@@ -242,108 +222,6 @@ GLuint SynchronizeAndImportMailbox(gpu::raster::RasterInterface* ri,
return ri->CreateAndConsumeForGpuRaster(mailbox);
}
-static constexpr size_t kNumYUVPlanes = 3;
-struct YUVPlaneTextureInfo {
- GrGLTextureInfo texture = {0, 0};
- bool is_shared_image = false;
-};
-using YUVTexturesInfo = std::array<YUVPlaneTextureInfo, kNumYUVPlanes>;
-
-YUVTexturesInfo GetYUVTexturesInfo(
- const VideoFrame* video_frame,
- viz::RasterContextProvider* raster_context_provider) {
- YUVTexturesInfo yuv_textures_info;
-
- gpu::raster::RasterInterface* ri = raster_context_provider->RasterInterface();
- DCHECK(ri);
- // TODO(bsalomon): Use GL_RGB8 once Skia supports it.
- // skbug.com/7533
- GrGLenum skia_texture_format =
- video_frame->format() == PIXEL_FORMAT_NV12 ? GL_RGBA8 : GL_R8_EXT;
- for (size_t i = 0; i < video_frame->NumTextures(); ++i) {
- // Get the texture from the mailbox and wrap it in a GrTexture.
- const gpu::MailboxHolder& mailbox_holder = video_frame->mailbox_holder(i);
- DCHECK(mailbox_holder.texture_target == GL_TEXTURE_2D ||
- mailbox_holder.texture_target == GL_TEXTURE_EXTERNAL_OES ||
- mailbox_holder.texture_target == GL_TEXTURE_RECTANGLE_ARB)
- << "Unsupported texture target " << std::hex << std::showbase
- << mailbox_holder.texture_target;
- yuv_textures_info[i].texture.fID = SynchronizeAndImportMailbox(
- ri, mailbox_holder.sync_token, mailbox_holder.mailbox);
- if (mailbox_holder.mailbox.IsSharedImage()) {
- yuv_textures_info[i].is_shared_image = true;
- ri->BeginSharedImageAccessDirectCHROMIUM(
- yuv_textures_info[i].texture.fID,
- GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM);
- }
-
- yuv_textures_info[i].texture.fTarget = mailbox_holder.texture_target;
- yuv_textures_info[i].texture.fFormat = skia_texture_format;
- }
-
- return yuv_textures_info;
-}
-
-void DeleteYUVTextures(const VideoFrame* video_frame,
- viz::RasterContextProvider* raster_context_provider,
- const YUVTexturesInfo& yuv_textures_info) {
- gpu::raster::RasterInterface* ri = raster_context_provider->RasterInterface();
- DCHECK(ri);
-
- for (size_t i = 0; i < video_frame->NumTextures(); ++i) {
- if (yuv_textures_info[i].is_shared_image)
- ri->EndSharedImageAccessDirectCHROMIUM(yuv_textures_info[i].texture.fID);
- ri->DeleteGpuRasterTexture(yuv_textures_info[i].texture.fID);
- }
-}
-
-sk_sp<SkImage> NewSkImageFromVideoFrameYUVTexturesWithExternalBackend(
- const VideoFrame* video_frame,
- viz::RasterContextProvider* raster_context_provider,
- unsigned int texture_target,
- unsigned int texture_id) {
- DCHECK(video_frame->HasTextures());
- GrContext* gr_context = raster_context_provider->GrContext();
- DCHECK(gr_context);
- // TODO: We should compare the DCHECK vs when UpdateLastImage calls this
- // function. (https://crbug.com/674185)
- DCHECK(video_frame->format() == PIXEL_FORMAT_I420 ||
- video_frame->format() == PIXEL_FORMAT_NV12);
-
- gfx::Size ya_tex_size = video_frame->coded_size();
- gfx::Size uv_tex_size((ya_tex_size.width() + 1) / 2,
- (ya_tex_size.height() + 1) / 2);
-
- GrGLTextureInfo backend_texture{};
-
- YUVTexturesInfo yuv_textures_info =
- GetYUVTexturesInfo(video_frame, raster_context_provider);
-
- GrBackendTexture yuv_textures[3] = {
- GrBackendTexture(ya_tex_size.width(), ya_tex_size.height(),
- GrMipMapped::kNo, yuv_textures_info[0].texture),
- GrBackendTexture(uv_tex_size.width(), uv_tex_size.height(),
- GrMipMapped::kNo, yuv_textures_info[1].texture),
- GrBackendTexture(uv_tex_size.width(), uv_tex_size.height(),
- GrMipMapped::kNo, yuv_textures_info[2].texture),
- };
- backend_texture.fID = texture_id;
- backend_texture.fTarget = texture_target;
- backend_texture.fFormat = GL_RGBA8;
- GrBackendTexture result_texture(video_frame->coded_size().width(),
- video_frame->coded_size().height(),
- GrMipMapped::kNo, backend_texture);
-
- sk_sp<SkImage> img = YUVGrBackendTexturesToSkImage(
- gr_context, video_frame->ColorSpace(), video_frame->format(),
- yuv_textures, result_texture);
- gr_context->flush();
-
- DeleteYUVTextures(video_frame, raster_context_provider, yuv_textures_info);
-
- return img;
-}
-
const gpu::MailboxHolder& GetVideoFrameMailboxHolder(VideoFrame* video_frame) {
DCHECK(video_frame->HasTextures());
DCHECK_EQ(video_frame->NumTextures(), 1u);
@@ -377,18 +255,6 @@ GLuint ImportVideoFrameSingleMailbox(gpu::gles2::GLES2Interface* gl,
return SynchronizeAndImportMailbox(gl, mailbox_holder.sync_token, *mailbox);
}
-// TODO(crbug.com/1023270): Remove this function once we're no longer relying on
-// texture ids for Mailbox access as that is only supported on
-// RasterImplementationGLES.
-GLuint ImportVideoFrameSingleMailbox(gpu::raster::RasterInterface* ri,
- VideoFrame* video_frame,
- gpu::Mailbox* mailbox) {
- const gpu::MailboxHolder& mailbox_holder =
- GetVideoFrameMailboxHolder(video_frame);
- *mailbox = mailbox_holder.mailbox;
- return SynchronizeAndImportMailbox(ri, mailbox_holder.sync_token, *mailbox);
-}
-
gpu::Mailbox SynchronizeVideoFrameSingleMailbox(
gpu::raster::RasterInterface* ri,
VideoFrame* video_frame) {
@@ -551,6 +417,18 @@ void ConvertVideoFrameToRGBPixelsTask(const VideoFrame* video_frame,
SkYUVColorSpace color_space = kRec601_SkYUVColorSpace;
video_frame->ColorSpace().ToSkYUVColorSpace(&color_space);
+ if (!video_frame->data(VideoFrame::kUPlane) &&
+ !video_frame->data(VideoFrame::kVPlane)) {
+ DCHECK_EQ(video_frame->format(), PIXEL_FORMAT_I420);
+ auto func = (color_space == kJPEG_SkYUVColorSpace) ? LIBYUV_J400_TO_ARGB
+ : LIBYUV_I400_TO_ARGB;
+ func(plane_meta[VideoFrame::kYPlane].data,
+ plane_meta[VideoFrame::kYPlane].stride, pixels, row_bytes, width,
+ rows);
+ done->Run();
+ return;
+ }
+
auto convert_yuv = [&](auto&& func) {
func(plane_meta[VideoFrame::kYPlane].data,
plane_meta[VideoFrame::kYPlane].stride,
@@ -869,6 +747,22 @@ class VideoImageGenerator : public cc::PaintImageGenerator {
DISALLOW_IMPLICIT_CONSTRUCTORS(VideoImageGenerator);
};
+class VideoTextureBacking : public cc::TextureBacking {
+ public:
+ explicit VideoTextureBacking(sk_sp<SkImage> sk_image)
+ : sk_image_(std::move(sk_image)) {}
+
+ const SkImageInfo& GetSkImageInfo() override {
+ return sk_image_->imageInfo();
+ }
+ gpu::Mailbox GetMailbox() const override { return mailbox_; }
+ sk_sp<SkImage> GetAcceleratedSkImage() override { return sk_image_; }
+
+ private:
+ const sk_sp<SkImage> sk_image_;
+ const gpu::Mailbox mailbox_;
+};
+
PaintCanvasVideoRenderer::PaintCanvasVideoRenderer()
: cache_deleting_timer_(
FROM_HERE,
@@ -1030,6 +924,7 @@ scoped_refptr<VideoFrame> DownShiftHighbitVideoFrame(
VideoPixelFormat format;
switch (video_frame->format()) {
case PIXEL_FORMAT_YUV420P12:
+ case PIXEL_FORMAT_YUV420P10:
case PIXEL_FORMAT_YUV420P9:
format = PIXEL_FORMAT_I420;
break;
@@ -1065,6 +960,13 @@ scoped_refptr<VideoFrame> DownShiftHighbitVideoFrame(
const uint16_t* src =
reinterpret_cast<const uint16_t*>(video_frame->data(plane));
uint8_t* dst = ret->data(plane);
+ if (!src) {
+ // An AV1 monochrome (grayscale) frame has no U and V planes. Set all U
+ // and V samples to the neutral value (128).
+ DCHECK_NE(plane, VideoFrame::kYPlane);
+ memset(dst, 128, ret->rows(plane) * ret->stride(plane));
+ continue;
+ }
for (int row = 0; row < video_frame->rows(plane); row++) {
for (int x = 0; x < width; x++) {
dst[x] = src[x] >> shift;
@@ -1227,6 +1129,17 @@ void PaintCanvasVideoRenderer::ConvertVideoFrameToRGBPixels(
temporary_frame = DownShiftHighbitVideoFrame(video_frame);
video_frame = temporary_frame.get();
break;
+ case PIXEL_FORMAT_YUV420P10:
+ // In AV1, a monochrome (grayscale) frame is represented as a YUV 4:2:0
+ // frame with no U and V planes. Since there are no 10-bit versions of
+ // libyuv::I400ToARGB() and libyuv::J400ToARGB(), convert the frame to an
+ // 8-bit YUV 4:2:0 frame with U and V planes.
+ if (!video_frame->data(VideoFrame::kUPlane) &&
+ !video_frame->data(VideoFrame::kVPlane)) {
+ temporary_frame = DownShiftHighbitVideoFrame(video_frame);
+ video_frame = temporary_frame.get();
+ }
+ break;
case PIXEL_FORMAT_Y16:
// Since it is grayscale conversion, we disregard
// SK_PMCOLOR_BYTE_ORDER and always use GL_RGBA.
@@ -1415,13 +1328,8 @@ bool PaintCanvasVideoRenderer::PrepareVideoFrameForWebGL(
destination_gl->GenUnverifiedSyncTokenCHROMIUM(
mailbox_holder.sync_token.GetData());
- source_ri->WaitSyncTokenCHROMIUM(mailbox_holder.sync_token.GetConstData());
-
- uint32_t shared_texture =
- source_ri->CreateAndConsumeForGpuRaster(mailbox_holder.mailbox);
-
- if (!PrepareVideoFrame(video_frame, raster_context_provider, target,
- shared_texture)) {
+ if (!PrepareVideoFrame(video_frame, raster_context_provider,
+ mailbox_holder)) {
return false;
}
@@ -1698,9 +1606,7 @@ bool PaintCanvasVideoRenderer::Cache::Recycle() {
// We need a new texture ID because skia will destroy the previous one with
// the SkImage.
texture_ownership_in_skia = false;
- source_texture =
- SynchronizeAndImportMailbox(raster_context_provider->RasterInterface(),
- gpu::SyncToken(), source_mailbox);
+ source_texture = 0;
return true;
}
@@ -1728,17 +1634,13 @@ bool PaintCanvasVideoRenderer::UpdateLastImage(
auto* ri = raster_context_provider->RasterInterface();
DCHECK(ri);
- sk_sp<SkImage> source_image;
-
if (allow_wrap_texture && video_frame->NumTextures() == 1) {
cache_.emplace(video_frame->unique_id());
- cache_->source_texture = ImportVideoFrameSingleMailbox(
- ri, video_frame.get(), &cache_->source_mailbox);
+ const gpu::MailboxHolder& holder =
+ GetVideoFrameMailboxHolder(video_frame.get());
+ cache_->source_mailbox = holder.mailbox;
+ ri->WaitSyncTokenCHROMIUM(holder.sync_token.GetConstData());
cache_->wraps_video_frame_texture = true;
- source_image =
- WrapGLTexture(video_frame->mailbox_holder(0).texture_target,
- cache_->source_texture, video_frame->coded_size(),
- video_frame->ColorSpace(), raster_context_provider);
} else {
if (cache_ &&
cache_->raster_context_provider == raster_context_provider &&
@@ -1749,11 +1651,20 @@ bool PaintCanvasVideoRenderer::UpdateLastImage(
} else {
cache_.emplace(video_frame->unique_id());
auto* sii = raster_context_provider->SharedImageInterface();
+
+ // TODO(nazabris): Sort out what to do when GLES2 is needed but the
+ // cached shared image is created without it.
+ uint32_t flags =
+ gpu::SHARED_IMAGE_USAGE_GLES2 | gpu::SHARED_IMAGE_USAGE_RASTER;
+ if (raster_context_provider->ContextCapabilities()
+ .supports_oop_raster) {
+ flags |= gpu::SHARED_IMAGE_USAGE_OOP_RASTERIZATION;
+ }
cache_->source_mailbox = sii->CreateSharedImage(
viz::ResourceFormat::RGBA_8888, video_frame->coded_size(),
- gfx::ColorSpace(), gpu::SHARED_IMAGE_USAGE_GLES2);
- cache_->source_texture = SynchronizeAndImportMailbox(
- ri, sii->GenUnverifiedSyncToken(), cache_->source_mailbox);
+ gfx::ColorSpace(), flags);
+ ri->WaitSyncTokenCHROMIUM(
+ sii->GenUnverifiedSyncToken().GetConstData());
}
DCHECK(!cache_->texture_ownership_in_skia);
@@ -1764,19 +1675,31 @@ bool PaintCanvasVideoRenderer::UpdateLastImage(
frame_mailbox, cache_->source_mailbox, GL_TEXTURE_2D, 0, 0, 0, 0,
video_frame->coded_size().width(),
video_frame->coded_size().height(), GL_FALSE, GL_FALSE);
- source_image = WrapGLTexture(
- GL_TEXTURE_2D, cache_->source_texture, video_frame->coded_size(),
- gfx::ColorSpace(), raster_context_provider);
} else {
- ScopedSharedImageAccess dest_access(
- ri, cache_->source_texture, cache_->source_mailbox,
- GL_SHARED_IMAGE_ACCESS_MODE_READWRITE_CHROMIUM);
- source_image = NewSkImageFromVideoFrameYUVTexturesWithExternalBackend(
- video_frame.get(), raster_context_provider, GL_TEXTURE_2D,
- cache_->source_texture);
+ gpu::MailboxHolder dest_holder{cache_->source_mailbox,
+ gpu::SyncToken(), GL_TEXTURE_2D};
+ ConvertFromVideoFrameYUV(video_frame.get(), raster_context_provider,
+ dest_holder);
}
raster_context_provider->GrContext()->flush();
}
+
+ // TODO(jochin): Don't always generate SkImage here.
+ DCHECK(cache_->source_texture == 0);
+ cache_->source_texture =
+ ri->CreateAndConsumeForGpuRaster(cache_->source_mailbox);
+
+ // TODO(nazabris): Handle scoped access correctly. This follows the
+ // current pattern but is most likely bugged. Access should last for the
+ // lifetime of the SkImage.
+ ScopedSharedImageAccess(ri, cache_->source_texture,
+ cache_->source_mailbox);
+ auto source_image =
+ WrapGLTexture(cache_->wraps_video_frame_texture
+ ? video_frame->mailbox_holder(0).texture_target
+ : GL_TEXTURE_2D,
+ cache_->source_texture, video_frame->coded_size(),
+ video_frame->ColorSpace(), raster_context_provider);
if (!source_image) {
// Couldn't create the SkImage.
cache_.reset();
@@ -1809,8 +1732,10 @@ bool PaintCanvasVideoRenderer::UpdateLastImage(
kPremul_SkAlphaType, source_image->imageInfo().refColorSpace());
}
}
- paint_image_builder.set_image(source_subset,
- cc::PaintImage::GetNextContentId());
+ paint_image_builder.set_texture_backing(
+ sk_sp<VideoTextureBacking>(
+ new VideoTextureBacking(std::move(source_subset))),
+ cc::PaintImage::GetNextContentId());
} else {
cache_.emplace(video_frame->unique_id());
paint_image_builder.set_paint_image_generator(
@@ -1832,48 +1757,21 @@ bool PaintCanvasVideoRenderer::UpdateLastImage(
bool PaintCanvasVideoRenderer::PrepareVideoFrame(
scoped_refptr<VideoFrame> video_frame,
viz::RasterContextProvider* raster_context_provider,
- unsigned int textureTarget,
- unsigned int texture) {
- cache_.emplace(video_frame->unique_id());
- auto paint_image_builder =
- cc::PaintImageBuilder::WithDefault()
- .set_id(renderer_stable_id_)
- .set_animation_type(cc::PaintImage::AnimationType::VIDEO)
- .set_completion_state(cc::PaintImage::CompletionState::DONE);
-
+ const gpu::MailboxHolder& dest_holder) {
// Generate a new image.
// Note: Skia will hold onto |video_frame| via |video_generator| only when
// |video_frame| is software.
// Holding |video_frame| longer than this call when using GPUVideoDecoder
// could cause problems since the pool of VideoFrames has a fixed size.
if (video_frame->HasTextures()) {
- DCHECK(raster_context_provider);
- DCHECK(raster_context_provider->GrContext());
- DCHECK(raster_context_provider->RasterInterface());
- sk_sp<SkImage> source_image;
if (video_frame->NumTextures() > 1) {
- source_image = NewSkImageFromVideoFrameYUVTexturesWithExternalBackend(
- video_frame.get(), raster_context_provider, textureTarget, texture);
- if (!source_image) {
- // Couldn't create the SkImage.
- cache_.reset();
- return false;
- }
+ ConvertFromVideoFrameYUV(video_frame.get(), raster_context_provider,
+ dest_holder);
} else {
// We don't support Android now.
- cache_.reset();
return false;
}
- cache_->coded_size = video_frame->coded_size();
- cache_->visible_rect = video_frame->visible_rect();
- paint_image_builder.set_image(
- source_image->makeSubset(gfx::RectToSkIRect(cache_->visible_rect)),
- cc::PaintImage::GetNextContentId());
- } else {
- paint_image_builder.set_paint_image_generator(
- sk_make_sp<VideoImageGenerator>(video_frame));
}
- cache_deleting_timer_.Reset();
return true;
}
diff --git a/chromium/media/renderers/paint_canvas_video_renderer.h b/chromium/media/renderers/paint_canvas_video_renderer.h
index d8ca22e360e..d5b1a141670 100644
--- a/chromium/media/renderers/paint_canvas_video_renderer.h
+++ b/chromium/media/renderers/paint_canvas_video_renderer.h
@@ -245,8 +245,7 @@ class MEDIA_EXPORT PaintCanvasVideoRenderer {
bool PrepareVideoFrame(scoped_refptr<VideoFrame> video_frame,
viz::RasterContextProvider* raster_context_provider,
- unsigned int textureTarget,
- unsigned int texture);
+ const gpu::MailboxHolder& dest_holder);
base::Optional<Cache> cache_;
diff --git a/chromium/media/renderers/video_resource_updater.cc b/chromium/media/renderers/video_resource_updater.cc
index 8eeaa0a6c4d..27678f2b65f 100644
--- a/chromium/media/renderers/video_resource_updater.cc
+++ b/chromium/media/renderers/video_resource_updater.cc
@@ -528,13 +528,13 @@ void VideoResourceUpdater::AppendQuads(viz::RenderPass* render_pass,
gfx::Rect visible_rect = frame->visible_rect();
gfx::Size coded_size = frame->coded_size();
- const float tex_width_scale =
- static_cast<float>(visible_rect.width()) / coded_size.width();
- const float tex_height_scale =
- static_cast<float>(visible_rect.height()) / coded_size.height();
+ const gfx::PointF uv_top_left(
+ static_cast<float>(visible_rect.x()) / coded_size.width(),
+ static_cast<float>(visible_rect.y()) / coded_size.height());
- const gfx::PointF uv_top_left(0.f, 0.f);
- const gfx::PointF uv_bottom_right(tex_width_scale, tex_height_scale);
+ const gfx::PointF uv_bottom_right(
+ static_cast<float>(visible_rect.right()) / coded_size.width(),
+ static_cast<float>(visible_rect.bottom()) / coded_size.height());
switch (frame_resource_type_) {
case VideoFrameResourceType::VIDEO_HOLE: {
@@ -620,17 +620,13 @@ void VideoResourceUpdater::AppendQuads(viz::RenderPass* render_pass,
protected_video_type = gfx::ProtectedVideoType::kSoftwareProtected;
}
- const gfx::Vector2dF offset(
- static_cast<float>(visible_rect.x()) / coded_size.width(),
- static_cast<float>(visible_rect.y()) / coded_size.height());
-
auto* texture_quad =
render_pass->CreateAndAppendDrawQuad<viz::TextureDrawQuad>();
- texture_quad->SetNew(
- shared_quad_state, quad_rect, visible_quad_rect, needs_blending,
- frame_resources_[0].id, premultiplied_alpha, uv_top_left + offset,
- uv_bottom_right + offset, SK_ColorTRANSPARENT, opacity, flipped,
- nearest_neighbor, false, protected_video_type);
+ texture_quad->SetNew(shared_quad_state, quad_rect, visible_quad_rect,
+ needs_blending, frame_resources_[0].id,
+ premultiplied_alpha, uv_top_left, uv_bottom_right,
+ SK_ColorTRANSPARENT, opacity, flipped,
+ nearest_neighbor, false, protected_video_type);
texture_quad->set_resource_size_in_pixels(coded_size);
for (viz::ResourceId resource_id : texture_quad->resources) {
resource_provider_->ValidateResource(resource_id);
diff --git a/chromium/media/renderers/win/media_engine_extension.cc b/chromium/media/renderers/win/media_engine_extension.cc
index 1fb3147f365..fcb34978e31 100644
--- a/chromium/media/renderers/win/media_engine_extension.cc
+++ b/chromium/media/renderers/win/media_engine_extension.cc
@@ -16,7 +16,7 @@ MediaEngineExtension::MediaEngineExtension() = default;
MediaEngineExtension::~MediaEngineExtension() = default;
HRESULT MediaEngineExtension::RuntimeClassInitialize() {
- DVLOG(1) << __func__ << ": this=" << this;
+ DVLOG_FUNC(1);
return S_OK;
}
@@ -35,7 +35,7 @@ HRESULT MediaEngineExtension::BeginCreateObject(BSTR url_bstr,
IUnknown** cancel_cookie,
IMFAsyncCallback* callback,
IUnknown* state) {
- DVLOG(1) << __func__ << ": this=" << this << ",type=" << type;
+ DVLOG_FUNC(1) << "type=" << type;
if (cancel_cookie) {
// We don't support a cancel cookie.
@@ -51,7 +51,7 @@ HRESULT MediaEngineExtension::BeginCreateObject(BSTR url_bstr,
}
if (type == MF_OBJECT_MEDIASOURCE) {
- DVLOG(2) << "Begin to resolve mf_media_source_: this=" << this;
+ DVLOG_FUNC(2) << "Begin to resolve |mf_media_source_|";
DCHECK(local_source) << "Media Source should have been set";
ComPtr<IMFAsyncResult> async_result;
@@ -71,20 +71,20 @@ HRESULT MediaEngineExtension::BeginCreateObject(BSTR url_bstr,
HRESULT MediaEngineExtension::CancelObjectCreation(
__in IUnknown* cancel_cookie) {
- DVLOG(1) << __func__ << ": this=" << this;
+ DVLOG_FUNC(1);
return MF_E_UNEXPECTED;
}
HRESULT MediaEngineExtension::EndCreateObject(__in IMFAsyncResult* result,
__deref_out IUnknown** ret_obj) {
- DVLOG(1) << __func__ << ": this=" << this;
+ DVLOG_FUNC(1);
*ret_obj = nullptr;
if (!pending_create_object_)
return MF_E_UNEXPECTED;
- DVLOG(2) << "End to resolve mf_media_source_: this=" << this;
+ DVLOG_FUNC(2) << "End to resolve |mf_media_source_|";
RETURN_IF_FAILED(result->GetStatus());
RETURN_IF_FAILED(result->GetObject(ret_obj));
pending_create_object_ = false;
@@ -92,7 +92,7 @@ HRESULT MediaEngineExtension::EndCreateObject(__in IMFAsyncResult* result,
}
HRESULT MediaEngineExtension::SetMediaSource(IUnknown* mf_media_source) {
- DVLOG(1) << __func__ << ": this=" << this;
+ DVLOG_FUNC(1);
base::AutoLock lock(lock_);
if (has_shutdown_)
@@ -103,7 +103,7 @@ HRESULT MediaEngineExtension::SetMediaSource(IUnknown* mf_media_source) {
// Break cycles.
void MediaEngineExtension::Shutdown() {
- DVLOG(1) << __func__ << ": this=" << this;
+ DVLOG_FUNC(1);
base::AutoLock lock(lock_);
if (!has_shutdown_) {
diff --git a/chromium/media/renderers/win/media_engine_notify_impl.cc b/chromium/media/renderers/win/media_engine_notify_impl.cc
index 4a36907a16f..e68adcdbc81 100644
--- a/chromium/media/renderers/win/media_engine_notify_impl.cc
+++ b/chromium/media/renderers/win/media_engine_notify_impl.cc
@@ -4,10 +4,64 @@
#include "media/renderers/win/media_engine_notify_impl.h"
+#include "media/base/win/mf_helpers.h"
+
namespace media {
namespace {
+#define ENUM_TO_STRING(enum) \
+ case enum: \
+ return #enum
+
+std::string MediaEngineEventToString(MF_MEDIA_ENGINE_EVENT event) {
+ switch (event) {
+ ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_LOADSTART);
+ ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_PROGRESS);
+ ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_SUSPEND);
+ ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_ABORT);
+ ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_ERROR);
+ ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_EMPTIED);
+ ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_STALLED);
+ ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_PLAY);
+ ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_PAUSE);
+ ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_LOADEDMETADATA);
+ ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_LOADEDDATA);
+ ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_WAITING);
+ ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_PLAYING);
+ ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_CANPLAY);
+ ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_CANPLAYTHROUGH);
+ ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_SEEKING);
+ ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_SEEKED);
+ ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_TIMEUPDATE);
+ ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_ENDED);
+ ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_RATECHANGE);
+ ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_DURATIONCHANGE);
+ ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_VOLUMECHANGE);
+ ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_FORMATCHANGE);
+ ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_PURGEQUEUEDEVENTS);
+ ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_TIMELINE_MARKER);
+ ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_BALANCECHANGE);
+ ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_DOWNLOADCOMPLETE);
+ ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_BUFFERINGSTARTED);
+ ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_BUFFERINGENDED);
+ ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_FRAMESTEPCOMPLETED);
+ ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_NOTIFYSTABLESTATE);
+ ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_FIRSTFRAMEREADY);
+ ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_TRACKSCHANGE);
+ ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_OPMINFO);
+ ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_RESOURCELOST);
+ ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_DELAYLOADEVENT_CHANGED);
+ ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_STREAMRENDERINGERROR);
+ ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_SUPPORTEDRATES_CHANGED);
+ ENUM_TO_STRING(MF_MEDIA_ENGINE_EVENT_AUDIOENDPOINTCHANGE);
+ default:
+ return "Unknown MF_MEDIA_ENGINE_EVENT";
+ }
+}
+
+#undef ENUM_TO_STRING
+
PipelineStatus MediaEngineStatusToPipelineStatus(
MF_MEDIA_ENGINE_ERR media_engine_status) {
switch (media_engine_status) {
@@ -37,16 +91,16 @@ MediaEngineNotifyImpl::~MediaEngineNotifyImpl() = default;
HRESULT MediaEngineNotifyImpl::RuntimeClassInitialize(
ErrorCB error_cb,
EndedCB ended_cb,
- DurationChangedCB duration_changed_cb,
BufferingStateChangedCB buffering_state_changed_cb,
- VideoNaturalSizeChangedCB video_natural_size_changed_cb) {
- DVLOG(1) << __func__ << ": this=" << this;
+ VideoNaturalSizeChangedCB video_natural_size_changed_cb,
+ TimeUpdateCB time_update_cb) {
+ DVLOG_FUNC(1);
error_cb_ = std::move(error_cb);
ended_cb_ = std::move(ended_cb);
- duration_changed_cb_ = std::move(duration_changed_cb);
buffering_state_changed_cb_ = std::move(buffering_state_changed_cb);
video_natural_size_changed_cb_ = std::move(video_natural_size_changed_cb);
+ time_update_cb_ = std::move(time_update_cb);
return S_OK;
}
@@ -57,29 +111,26 @@ HRESULT MediaEngineNotifyImpl::RuntimeClassInitialize(
HRESULT MediaEngineNotifyImpl::EventNotify(DWORD event_code,
DWORD_PTR param1,
DWORD param2) {
- DVLOG(3) << __func__ << ": this=" << this << ",eventCode=" << event_code
- << ",param1=" << static_cast<unsigned>(param1)
- << ",param2=" << static_cast<unsigned>(param2);
+ auto event = static_cast<MF_MEDIA_ENGINE_EVENT>(event_code);
+ DVLOG_FUNC(3) << "event=" << MediaEngineEventToString(event);
base::AutoLock lock(lock_);
if (has_shutdown_)
return S_OK;
- switch (static_cast<MF_MEDIA_ENGINE_EVENT>(event_code)) {
+ switch (event) {
case MF_MEDIA_ENGINE_EVENT_ERROR: {
// |param1| - A member of the MF_MEDIA_ENGINE_ERR enumeration.
// |param2| - An HRESULT error code, or zero.
MF_MEDIA_ENGINE_ERR error = static_cast<MF_MEDIA_ENGINE_ERR>(param1);
- DLOG(ERROR) << __func__ << ": error=" << error << ",hr=" << param2;
+ LOG(ERROR) << __func__ << ": error=" << error
+ << ", hr=" << PrintHr(param2);
error_cb_.Run(MediaEngineStatusToPipelineStatus(error));
break;
}
case MF_MEDIA_ENGINE_EVENT_ENDED:
ended_cb_.Run();
break;
- case MF_MEDIA_ENGINE_EVENT_DURATIONCHANGE:
- duration_changed_cb_.Run();
- break;
case MF_MEDIA_ENGINE_EVENT_FORMATCHANGE:
video_natural_size_changed_cb_.Run();
break;
@@ -96,16 +147,19 @@ HRESULT MediaEngineNotifyImpl::EventNotify(DWORD event_code,
BufferingState::BUFFERING_HAVE_NOTHING,
BufferingStateChangeReason::BUFFERING_CHANGE_REASON_UNKNOWN);
break;
+ case MF_MEDIA_ENGINE_EVENT_TIMEUPDATE:
+ time_update_cb_.Run();
+ break;
+
default:
- DVLOG(3) << __func__ << ": this=" << this
- << ", unhandled event_code=" << event_code;
+ DVLOG_FUNC(2) << "Unhandled event=" << MediaEngineEventToString(event);
break;
}
return S_OK;
}
void MediaEngineNotifyImpl::Shutdown() {
- DVLOG(1) << __func__ << ": this=" << this;
+ DVLOG_FUNC(1);
base::AutoLock lock(lock_);
has_shutdown_ = true;
diff --git a/chromium/media/renderers/win/media_engine_notify_impl.h b/chromium/media/renderers/win/media_engine_notify_impl.h
index 1fe51656e60..b0dcf6a4001 100644
--- a/chromium/media/renderers/win/media_engine_notify_impl.h
+++ b/chromium/media/renderers/win/media_engine_notify_impl.h
@@ -18,10 +18,10 @@ namespace media {
using ErrorCB = base::RepeatingCallback<void(PipelineStatus)>;
using EndedCB = base::RepeatingClosure;
-using DurationChangedCB = base::RepeatingClosure;
using BufferingStateChangedCB =
base::RepeatingCallback<void(BufferingState, BufferingStateChangeReason)>;
using VideoNaturalSizeChangedCB = base::RepeatingClosure;
+using TimeUpdateCB = base::RepeatingClosure;
// Implements IMFMediaEngineNotify required by IMFMediaEngine
// (https://docs.microsoft.com/en-us/windows/win32/api/mfmediaengine/nn-mfmediaengine-imfmediaengine).
@@ -38,9 +38,9 @@ class MediaEngineNotifyImpl
HRESULT RuntimeClassInitialize(
ErrorCB error_cb,
EndedCB ended_cb,
- DurationChangedCB duration_changed_cb,
BufferingStateChangedCB buffering_state_changed_cb,
- VideoNaturalSizeChangedCB video_natural_size_changed_cb);
+ VideoNaturalSizeChangedCB video_natural_size_changed_cb,
+ TimeUpdateCB time_update_cb);
// IMFMediaEngineNotify implementation.
IFACEMETHODIMP EventNotify(DWORD event_code,
@@ -55,9 +55,9 @@ class MediaEngineNotifyImpl
// e.g. using BindToCurrentLoop().
ErrorCB error_cb_;
EndedCB ended_cb_;
- DurationChangedCB duration_changed_cb_;
BufferingStateChangedCB buffering_state_changed_cb_;
VideoNaturalSizeChangedCB video_natural_size_changed_cb_;
+ TimeUpdateCB time_update_cb_;
// EventNotify is invoked from MF threadpool thread where the callbacks are
// called.
diff --git a/chromium/media/renderers/win/media_foundation_audio_stream.cc b/chromium/media/renderers/win/media_foundation_audio_stream.cc
index ccbf6a4d3b4..68f4485e6c3 100644
--- a/chromium/media/renderers/win/media_foundation_audio_stream.cc
+++ b/chromium/media/renderers/win/media_foundation_audio_stream.cc
@@ -245,7 +245,7 @@ HRESULT MediaFoundationAACAudioStream::GetMediaType(
// accordingly).
HRESULT MediaFoundationAACAudioStream::TransformSample(
Microsoft::WRL::ComPtr<IMFSample>& sample) {
- DVLOG(3) << __func__ << ": this=" << this;
+ DVLOG_FUNC(3);
if (!enable_adts_header_removal_)
return S_OK;
diff --git a/chromium/media/renderers/win/media_foundation_protection_manager.cc b/chromium/media/renderers/win/media_foundation_protection_manager.cc
index 711e59b5952..d7ef8edf861 100644
--- a/chromium/media/renderers/win/media_foundation_protection_manager.cc
+++ b/chromium/media/renderers/win/media_foundation_protection_manager.cc
@@ -22,7 +22,7 @@ MediaFoundationProtectionManager::MediaFoundationProtectionManager() = default;
MediaFoundationProtectionManager::~MediaFoundationProtectionManager() = default;
HRESULT MediaFoundationProtectionManager::RuntimeClassInitialize() {
- DVLOG(1) << __func__ << ": this=" << this;
+ DVLOG_FUNC(1);
if (!base::win::ScopedHString::ResolveCoreWinRTStringDelayload())
return E_FAIL;
@@ -37,7 +37,7 @@ HRESULT MediaFoundationProtectionManager::RuntimeClassInitialize() {
}
HRESULT MediaFoundationProtectionManager::SetCdmProxy(IMFCdmProxy* cdm_proxy) {
- DVLOG(1) << __func__ << ": this=" << this;
+ DVLOG_FUNC(1);
DCHECK(cdm_proxy);
cdm_proxy_ = cdm_proxy;
@@ -49,7 +49,7 @@ HRESULT MediaFoundationProtectionManager::SetCdmProxy(IMFCdmProxy* cdm_proxy) {
HRESULT MediaFoundationProtectionManager::SetPMPServer(
ABI::Windows::Media::Protection::IMediaProtectionPMPServer* pmp_server) {
- DVLOG(1) << __func__ << ": this=" << this;
+ DVLOG_FUNC(1);
DCHECK(pmp_server);
ComPtr<ABI::Windows::Foundation::Collections::IMap<HSTRING, IInspectable*>>
@@ -72,7 +72,7 @@ HRESULT MediaFoundationProtectionManager::BeginEnableContent(
IMFTopology* topology,
IMFAsyncCallback* callback,
IUnknown* state) {
- DVLOG(1) << __func__ << ": this=" << this;
+ DVLOG_FUNC(1);
ComPtr<IUnknown> unknown_object;
ComPtr<IMFAsyncResult> async_result;
@@ -115,7 +115,7 @@ HRESULT MediaFoundationProtectionManager::BeginEnableContent(
HRESULT MediaFoundationProtectionManager::EndEnableContent(
IMFAsyncResult* async_result) {
- DVLOG(1) << __func__ << ": this=" << this;
+ DVLOG_FUNC(1);
// Get status from the given |async_result| for the purpose of logging.
// Returns S_OK as there is no additional work being done here.
@@ -164,7 +164,7 @@ HRESULT MediaFoundationProtectionManager::remove_ComponentLoadFailed(
HRESULT MediaFoundationProtectionManager::get_Properties(
ABI::Windows::Foundation::Collections::IPropertySet** properties) {
- DVLOG(2) << __func__ << ": this=" << this;
+ DVLOG_FUNC(2);
if (!properties)
return E_POINTER;
return property_set_.CopyTo(properties);
diff --git a/chromium/media/renderers/win/media_foundation_renderer.cc b/chromium/media/renderers/win/media_foundation_renderer.cc
new file mode 100644
index 00000000000..b8cfde9f7b7
--- /dev/null
+++ b/chromium/media/renderers/win/media_foundation_renderer.cc
@@ -0,0 +1,641 @@
+// 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/renderers/win/media_foundation_renderer.h"
+
+#include <Audioclient.h>
+#include <mferror.h>
+#include <memory>
+#include <string>
+
+#include "base/bind_helpers.h"
+#include "base/callback_helpers.h"
+#include "base/guid.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/win/scoped_bstr.h"
+#include "base/win/scoped_hdc.h"
+#include "base/win/scoped_propvariant.h"
+#include "base/win/windows_version.h"
+#include "base/win/wrapped_window_proc.h"
+#include "media/base/bind_to_current_loop.h"
+#include "media/base/timestamp_constants.h"
+#include "media/base/win/mf_helpers.h"
+
+namespace media {
+
+using Microsoft::WRL::ComPtr;
+using Microsoft::WRL::MakeAndInitialize;
+
+namespace {
+
+ATOM g_video_window_class = 0;
+
+// The |g_video_window_class| atom obtained is used as the |lpClassName|
+// parameter in CreateWindowEx().
+// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createwindowexa
+//
+// To enable OPM
+// (https://docs.microsoft.com/en-us/windows/win32/medfound/output-protection-manager)
+// protection for video playback, We call CreateWindowEx() to get a window
+// and pass it to MFMediaEngine as an attribute.
+bool InitializeVideoWindowClass() {
+ if (g_video_window_class)
+ return true;
+
+ WNDCLASSEX intermediate_class;
+ base::win::InitializeWindowClass(
+ L"VirtualMediaFoundationCdmVideoWindow",
+ &base::win::WrappedWindowProc<::DefWindowProc>, CS_OWNDC, 0, 0, nullptr,
+ reinterpret_cast<HBRUSH>(GetStockObject(BLACK_BRUSH)), nullptr, nullptr,
+ nullptr, &intermediate_class);
+ g_video_window_class = RegisterClassEx(&intermediate_class);
+ if (!g_video_window_class) {
+ HRESULT register_class_error = HRESULT_FROM_WIN32(GetLastError());
+ DLOG(ERROR) << "RegisterClass failed: " << PrintHr(register_class_error);
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace
+
+// static
+bool MediaFoundationRenderer::IsSupported() {
+ return base::win::GetVersion() >= base::win::Version::WIN10;
+}
+
+MediaFoundationRenderer::MediaFoundationRenderer(
+ bool muted,
+ scoped_refptr<base::SequencedTaskRunner> task_runner,
+ bool force_dcomp_mode_for_testing)
+ : muted_(muted),
+ task_runner_(task_runner),
+ force_dcomp_mode_for_testing_(force_dcomp_mode_for_testing) {
+ DVLOG_FUNC(1);
+}
+
+MediaFoundationRenderer::~MediaFoundationRenderer() {
+ DVLOG_FUNC(1);
+
+ // Perform shutdown/cleanup in the order (shutdown/detach/destroy) we wanted
+ // without depending on the order of destructors being invoked. We also need
+ // to invoke MFShutdown() after shutdown/cleanup of MF related objects.
+
+ StopSendingStatistics();
+
+ if (mf_media_engine_extension_)
+ mf_media_engine_extension_->Shutdown();
+ if (mf_media_engine_notify_)
+ mf_media_engine_notify_->Shutdown();
+ if (mf_media_engine_)
+ mf_media_engine_->Shutdown();
+
+ if (mf_source_)
+ mf_source_->DetachResource();
+
+ if (dxgi_device_manager_) {
+ dxgi_device_manager_.Reset();
+ MFUnlockDXGIDeviceManager();
+ }
+ if (virtual_video_window_)
+ DestroyWindow(virtual_video_window_);
+}
+
+void MediaFoundationRenderer::Initialize(MediaResource* media_resource,
+ RendererClient* client,
+ PipelineStatusCallback init_cb) {
+ DVLOG_FUNC(1);
+
+ renderer_client_ = client;
+
+ HRESULT hr = CreateMediaEngine(media_resource);
+ if (FAILED(hr)) {
+ DLOG(ERROR) << "Failed to create media engine: " << PrintHr(hr);
+ std::move(init_cb).Run(PIPELINE_ERROR_INITIALIZATION_FAILED);
+ } else {
+ std::move(init_cb).Run(PIPELINE_OK);
+ }
+}
+
+HRESULT MediaFoundationRenderer::CreateMediaEngine(
+ MediaResource* media_resource) {
+ DVLOG_FUNC(1);
+
+ mf_session_life_time_ = InitializeMediaFoundation();
+ if (!mf_session_life_time_)
+ return E_FAIL;
+
+ // TODO(frankli): Only call the followings when there is a video stream.
+ RETURN_IF_FAILED(InitializeDXGIDeviceManager());
+ RETURN_IF_FAILED(InitializeVirtualVideoWindow());
+
+ // The OnXxx() callbacks are invoked by MF threadpool thread, we would like
+ // to bind the callbacks to |task_runner_| MessgaeLoop.
+ DCHECK(task_runner_->RunsTasksInCurrentSequence());
+ auto weak_this = weak_factory_.GetWeakPtr();
+ RETURN_IF_FAILED(MakeAndInitialize<MediaEngineNotifyImpl>(
+ &mf_media_engine_notify_,
+ BindToCurrentLoop(base::BindRepeating(
+ &MediaFoundationRenderer::OnPlaybackError, weak_this)),
+ BindToCurrentLoop(base::BindRepeating(
+ &MediaFoundationRenderer::OnPlaybackEnded, weak_this)),
+ BindToCurrentLoop(base::BindRepeating(
+ &MediaFoundationRenderer::OnBufferingStateChanged, weak_this)),
+ BindToCurrentLoop(base::BindRepeating(
+ &MediaFoundationRenderer::OnVideoNaturalSizeChanged, weak_this)),
+ BindToCurrentLoop(base::BindRepeating(
+ &MediaFoundationRenderer::OnTimeUpdate, weak_this))));
+
+ ComPtr<IMFAttributes> creation_attributes;
+ RETURN_IF_FAILED(MFCreateAttributes(&creation_attributes, 6));
+ RETURN_IF_FAILED(creation_attributes->SetUnknown(
+ MF_MEDIA_ENGINE_CALLBACK, mf_media_engine_notify_.Get()));
+ RETURN_IF_FAILED(
+ creation_attributes->SetUINT32(MF_MEDIA_ENGINE_CONTENT_PROTECTION_FLAGS,
+ MF_MEDIA_ENGINE_ENABLE_PROTECTED_CONTENT));
+ RETURN_IF_FAILED(creation_attributes->SetUINT32(
+ MF_MEDIA_ENGINE_AUDIO_CATEGORY, AudioCategory_Media));
+ if (virtual_video_window_) {
+ RETURN_IF_FAILED(creation_attributes->SetUINT64(
+ MF_MEDIA_ENGINE_OPM_HWND,
+ reinterpret_cast<uint64_t>(virtual_video_window_)));
+ }
+
+ if (dxgi_device_manager_) {
+ RETURN_IF_FAILED(creation_attributes->SetUnknown(
+ MF_MEDIA_ENGINE_DXGI_MANAGER, dxgi_device_manager_.Get()));
+ }
+
+ RETURN_IF_FAILED(
+ MakeAndInitialize<MediaEngineExtension>(&mf_media_engine_extension_));
+ RETURN_IF_FAILED(creation_attributes->SetUnknown(
+ MF_MEDIA_ENGINE_EXTENSION, mf_media_engine_extension_.Get()));
+
+ ComPtr<IMFMediaEngineClassFactory> class_factory;
+ RETURN_IF_FAILED(CoCreateInstance(CLSID_MFMediaEngineClassFactory, nullptr,
+ CLSCTX_INPROC_SERVER,
+ IID_PPV_ARGS(&class_factory)));
+ // TODO(frankli): Use MF_MEDIA_ENGINE_REAL_TIME_MODE for low latency hint
+ // instead of 0.
+ RETURN_IF_FAILED(class_factory->CreateInstance(0, creation_attributes.Get(),
+ &mf_media_engine_));
+
+ auto media_resource_type_ = media_resource->GetType();
+ if (media_resource_type_ != MediaResource::Type::STREAM) {
+ DLOG(ERROR) << "MediaResource is not of STREAM";
+ return E_INVALIDARG;
+ }
+
+ if (!playback_element_id_) {
+ DLOG(ERROR) << "Invalid playback_element_id_.";
+ return HRESULT_FROM_WIN32(ERROR_INVALID_STATE);
+ }
+
+ RETURN_IF_FAILED(MakeAndInitialize<MediaFoundationSourceWrapper>(
+ &mf_source_, playback_element_id_, media_resource, task_runner_));
+
+ if (force_dcomp_mode_for_testing_)
+ SetDCompMode(true, base::DoNothing());
+
+ if (!mf_source_->HasEncryptedStream()) {
+ // Supports clear stream for testing.
+ return SetSourceOnMediaEngine();
+ }
+
+ // Has encrypted stream.
+ RETURN_IF_FAILED(MakeAndInitialize<MediaFoundationProtectionManager>(
+ &content_protection_manager_));
+ ComPtr<IMFMediaEngineProtectedContent> protected_media_engine;
+ RETURN_IF_FAILED(mf_media_engine_.As(&protected_media_engine));
+ RETURN_IF_FAILED(protected_media_engine->SetContentProtectionManager(
+ content_protection_manager_.Get()));
+
+ waiting_for_mf_cdm_ = true;
+ if (!cdm_context_)
+ return S_OK;
+
+ // Has |cdm_context_|.
+ if (!cdm_context_->GetMediaFoundationCdmProxy(
+ base::BindOnce(&MediaFoundationRenderer::OnCdmProxyReceived,
+ weak_factory_.GetWeakPtr()))) {
+ DLOG(ERROR) << __func__
+ << ": CdmContext does not support MF CDM interface.";
+ return MF_E_UNEXPECTED;
+ }
+
+ return S_OK;
+}
+
+HRESULT MediaFoundationRenderer::SetSourceOnMediaEngine() {
+ DVLOG_FUNC(1);
+
+ if (!mf_source_) {
+ LOG(ERROR) << "mf_source_ is null.";
+ return HRESULT_FROM_WIN32(ERROR_INVALID_STATE);
+ }
+
+ ComPtr<IUnknown> source_unknown;
+ RETURN_IF_FAILED(mf_source_.As(&source_unknown));
+ RETURN_IF_FAILED(
+ mf_media_engine_extension_->SetMediaSource(source_unknown.Get()));
+
+ DVLOG(2) << "Set MFRendererSrc scheme as the source for MFMediaEngine.";
+ base::win::ScopedBstr mf_renderer_source_scheme(
+ base::ASCIIToUTF16("MFRendererSrc"));
+ // We need to set our source scheme first in order for the MFMediaEngine to
+ // load of our custom MFMediaSource.
+ RETURN_IF_FAILED(
+ mf_media_engine_->SetSource(mf_renderer_source_scheme.Get()));
+
+ return S_OK;
+}
+
+HRESULT MediaFoundationRenderer::InitializeDXGIDeviceManager() {
+ UINT device_reset_token;
+ RETURN_IF_FAILED(
+ MFLockDXGIDeviceManager(&device_reset_token, &dxgi_device_manager_));
+
+ 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};
+ RETURN_IF_FAILED(
+ D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, 0, creation_flags,
+ feature_levels, base::size(feature_levels),
+ D3D11_SDK_VERSION, &d3d11_device, nullptr, nullptr));
+
+ ComPtr<ID3D10Multithread> multithreaded_device;
+ RETURN_IF_FAILED(d3d11_device.As(&multithreaded_device));
+ multithreaded_device->SetMultithreadProtected(TRUE);
+
+ return dxgi_device_manager_->ResetDevice(d3d11_device.Get(),
+ device_reset_token);
+}
+
+HRESULT MediaFoundationRenderer::InitializeVirtualVideoWindow() {
+ if (!InitializeVideoWindowClass())
+ return E_FAIL;
+
+ virtual_video_window_ =
+ CreateWindowEx(WS_EX_NOPARENTNOTIFY | WS_EX_LAYERED | WS_EX_TRANSPARENT |
+ WS_EX_NOREDIRECTIONBITMAP,
+ reinterpret_cast<wchar_t*>(g_video_window_class), L"",
+ WS_POPUP | WS_DISABLED | WS_CLIPSIBLINGS, 0, 0, 1, 1,
+ nullptr, nullptr, nullptr, nullptr);
+ if (!virtual_video_window_) {
+ HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
+ DLOG(ERROR) << "Failed to create virtual window: " << PrintHr(hr);
+ return hr;
+ }
+
+ return S_OK;
+}
+
+void MediaFoundationRenderer::SetCdm(CdmContext* cdm_context,
+ CdmAttachedCB cdm_attached_cb) {
+ DVLOG_FUNC(1);
+
+ if (cdm_context_ || !cdm_context) {
+ DLOG(ERROR) << "Failed in checking CdmContext.";
+ std::move(cdm_attached_cb).Run(false);
+ return;
+ }
+
+ cdm_context_ = cdm_context;
+
+ if (waiting_for_mf_cdm_) {
+ if (!cdm_context_->GetMediaFoundationCdmProxy(
+ base::BindOnce(&MediaFoundationRenderer::OnCdmProxyReceived,
+ weak_factory_.GetWeakPtr()))) {
+ DLOG(ERROR) << "Decryptor does not support MF CDM interface.";
+ std::move(cdm_attached_cb).Run(false);
+ return;
+ }
+ }
+
+ std::move(cdm_attached_cb).Run(true);
+}
+
+void MediaFoundationRenderer::SetLatencyHint(
+ base::Optional<base::TimeDelta> /*latency_hint*/) {
+ // TODO(frankli): Ensure MFMediaEngine rendering pipeine is in real time mode.
+ NOTIMPLEMENTED() << "We do not use the latency hint today";
+}
+
+// TODO(frankli): Use ComPtr<> for |cdm|.
+void MediaFoundationRenderer::OnCdmProxyReceived(IMFCdmProxy* cdm) {
+ DVLOG_FUNC(1);
+
+ if (!waiting_for_mf_cdm_ || !content_protection_manager_) {
+ DLOG(ERROR) << "Failed in checking internal state.";
+ renderer_client_->OnError(PipelineStatus::PIPELINE_ERROR_INVALID_STATE);
+ return;
+ }
+
+ waiting_for_mf_cdm_ = false;
+
+ ComPtr<IMFCdmProxy> cdm_proxy;
+ cdm_proxy.Attach(cdm);
+ content_protection_manager_->SetCdmProxy(cdm_proxy.Get());
+ mf_source_->SetCdmProxy(cdm_proxy.Get());
+ HRESULT hr = SetSourceOnMediaEngine();
+ if (FAILED(hr)) {
+ DLOG(ERROR) << "Failed to set source on media engine: " << PrintHr(hr);
+ renderer_client_->OnError(PipelineStatus::PIPELINE_ERROR_COULD_NOT_RENDER);
+ return;
+ }
+}
+
+void MediaFoundationRenderer::Flush(base::OnceClosure flush_cb) {
+ DVLOG_FUNC(2);
+
+ HRESULT hr = mf_media_engine_->Pause();
+ // Ignore any Pause() error. We can continue to flush |mf_source_| instead of
+ // stopping the playback with error.
+ DVLOG_IF(1, FAILED(hr)) << "Failed to pause playback on flush: "
+ << PrintHr(hr);
+
+ StopSendingStatistics();
+ mf_source_->FlushStreams();
+ std::move(flush_cb).Run();
+}
+
+void MediaFoundationRenderer::StartPlayingFrom(base::TimeDelta time) {
+ double current_time = time.InSecondsF();
+ DVLOG_FUNC(2) << "current_time=" << current_time;
+
+ // Note: It is okay for |waiting_for_mf_cdm_| to be true here. The
+ // MFMediaEngine supports calls to Play/SetCurrentTime before a source is set
+ // (it will apply the relevant changes to the playback state once a source is
+ // set on it).
+
+ // SetCurrentTime() completes asynchronously. When the seek operation starts,
+ // the MFMediaEngine sends an MF_MEDIA_ENGINE_EVENT_SEEKING event. When the
+ // seek operation completes, the MFMediaEngine sends an
+ // MF_MEDIA_ENGINE_EVENT_SEEKED event.
+ HRESULT hr = mf_media_engine_->SetCurrentTime(current_time);
+ if (FAILED(hr)) {
+ DLOG(ERROR) << "Failed to SetCurrentTime: " << PrintHr(hr);
+ renderer_client_->OnError(PipelineStatus::PIPELINE_ERROR_COULD_NOT_RENDER);
+ return;
+ }
+
+ hr = mf_media_engine_->Play();
+ if (FAILED(hr)) {
+ DLOG(ERROR) << "Failed to start playback: " << PrintHr(hr);
+ renderer_client_->OnError(PipelineStatus::PIPELINE_ERROR_COULD_NOT_RENDER);
+ return;
+ }
+
+ StartSendingStatistics();
+}
+
+void MediaFoundationRenderer::SetPlaybackRate(double playback_rate) {
+ DVLOG_FUNC(2) << "playback_rate=" << playback_rate;
+
+ HRESULT hr = mf_media_engine_->SetPlaybackRate(playback_rate);
+ // Ignore error so that the media continues to play rather than stopped.
+ DVLOG_IF(1, FAILED(hr)) << "Failed to set playback rate: " << PrintHr(hr);
+}
+
+void MediaFoundationRenderer::SetDCompMode(bool enabled,
+ SetDCompModeCB callback) {
+ DVLOG_FUNC(1);
+
+ HRESULT hr = SetDCompModeInternal(enabled);
+ if (FAILED(hr)) {
+ DLOG(ERROR) << "Failed to set DComp mode: " << PrintHr(hr);
+ std::move(callback).Run(false);
+ return;
+ }
+
+ std::move(callback).Run(true);
+}
+
+void MediaFoundationRenderer::GetDCompSurface(GetDCompSurfaceCB callback) {
+ DVLOG_FUNC(1);
+
+ HANDLE surface_handle = INVALID_HANDLE_VALUE;
+ HRESULT hr = GetDCompSurfaceInternal(&surface_handle);
+ DVLOG_IF(1, FAILED(hr)) << "Failed to get DComp surface: " << PrintHr(hr);
+ std::move(callback).Run(std::move(surface_handle));
+}
+
+// TODO(crbug.com/1070030): Investigate if we need to add
+// OnSelectedVideoTracksChanged() to media renderer.mojom.
+void MediaFoundationRenderer::SetVideoStreamEnabled(bool enabled) {
+ DVLOG_FUNC(1) << "enabled=" << enabled;
+ if (!mf_source_)
+ return;
+
+ const bool needs_restart = mf_source_->SetVideoStreamEnabled(enabled);
+ if (needs_restart) {
+ // If the media source indicates that we need to restart playback (e.g due
+ // to a newly enabled stream being EOS), queue a pause and play operation.
+ mf_media_engine_->Pause();
+ mf_media_engine_->Play();
+ }
+}
+
+void MediaFoundationRenderer::SetPlaybackElementId(
+ uint64_t playback_element_id) {
+ DVLOG_FUNC(1) << "playback_element_id=" << playback_element_id;
+
+ playback_element_id_ = playback_element_id;
+}
+
+void MediaFoundationRenderer::SetOutputParams(const gfx::Rect& output_rect) {
+ DVLOG_FUNC(2);
+
+ HRESULT hr = SetOutputParamsInternal(output_rect);
+ DVLOG_IF(1, FAILED(hr)) << "Failed to set output parameters: " << PrintHr(hr);
+}
+
+HRESULT MediaFoundationRenderer::SetOutputParamsInternal(
+ const gfx::Rect& output_rect) {
+ DVLOG_FUNC(2);
+
+ if (virtual_video_window_ &&
+ !::SetWindowPos(virtual_video_window_, HWND_BOTTOM, output_rect.x(),
+ output_rect.y(), output_rect.width(),
+ output_rect.height(), SWP_NOACTIVATE)) {
+ return HRESULT_FROM_WIN32(GetLastError());
+ }
+
+ // TODO(frankli): Update MFMediaEngineEx with |output_rect| change and update
+ // renderer client with output size.
+
+ return S_OK;
+}
+
+HRESULT MediaFoundationRenderer::GetDCompSurfaceInternal(
+ HANDLE* surface_handle) {
+ DVLOG_FUNC(1);
+
+ ComPtr<IMFMediaEngineEx> media_engine_ex;
+ RETURN_IF_FAILED(mf_media_engine_.As(&media_engine_ex));
+ RETURN_IF_FAILED(media_engine_ex->GetVideoSwapchainHandle(surface_handle));
+ return S_OK;
+}
+
+HRESULT MediaFoundationRenderer::SetDCompModeInternal(bool enabled) {
+ DVLOG_FUNC(1) << "enabled=" << enabled;
+
+ ComPtr<IMFMediaEngineEx> media_engine_ex;
+ RETURN_IF_FAILED(mf_media_engine_.As(&media_engine_ex));
+ RETURN_IF_FAILED(media_engine_ex->EnableWindowlessSwapchainMode(enabled));
+ return S_OK;
+}
+
+HRESULT MediaFoundationRenderer::PopulateStatistics(
+ PipelineStatistics& statistics) {
+ ComPtr<IMFMediaEngineEx> media_engine_ex;
+ RETURN_IF_FAILED(mf_media_engine_.As(&media_engine_ex));
+ base::win::ScopedPropVariant frames_rendered;
+ RETURN_IF_FAILED(media_engine_ex->GetStatistics(
+ MF_MEDIA_ENGINE_STATISTIC_FRAMES_RENDERED, frames_rendered.Receive()));
+ base::win::ScopedPropVariant frames_dropped;
+ RETURN_IF_FAILED(media_engine_ex->GetStatistics(
+ MF_MEDIA_ENGINE_STATISTIC_FRAMES_DROPPED, frames_dropped.Receive()));
+ statistics.video_frames_decoded = frames_rendered.get().ulVal;
+ statistics.video_frames_dropped = frames_dropped.get().ulVal;
+ return S_OK;
+}
+
+void MediaFoundationRenderer::SendStatistics() {
+ PipelineStatistics new_stats = {};
+ HRESULT hr = PopulateStatistics(new_stats);
+ if (FAILED(hr)) {
+ DVLOG(3) << "Failed to populate pipeline stats: " << PrintHr(hr);
+ return;
+ }
+
+ if (statistics_ != new_stats) {
+ statistics_ = new_stats;
+ renderer_client_->OnStatisticsUpdate(statistics_);
+ }
+}
+
+void MediaFoundationRenderer::StartSendingStatistics() {
+ const auto kPipelineStatsPollingPeriod =
+ base::TimeDelta::FromMilliseconds(500);
+ statistics_timer_.Start(FROM_HERE, kPipelineStatsPollingPeriod, this,
+ &MediaFoundationRenderer::SendStatistics);
+}
+
+void MediaFoundationRenderer::StopSendingStatistics() {
+ statistics_timer_.Stop();
+}
+
+void MediaFoundationRenderer::SetVolume(float volume) {
+ volume_ = volume;
+ float set_volume = muted_ ? 0 : volume_;
+ DVLOG_FUNC(2) << "set_volume=" << set_volume;
+
+ HRESULT hr = mf_media_engine_->SetVolume(set_volume);
+ DVLOG_IF(1, FAILED(hr)) << "Failed to set volume: " << PrintHr(hr);
+}
+
+base::TimeDelta MediaFoundationRenderer::GetMediaTime() {
+// GetCurrentTime is expaned as GetTickCount in base/win/windows_types.h
+#undef GetCurrentTime
+ double current_time = mf_media_engine_->GetCurrentTime();
+// Restore macro definition.
+#define GetCurrentTime() GetTickCount()
+ return base::TimeDelta::FromSecondsD(current_time);
+}
+
+void MediaFoundationRenderer::OnPlaybackError(PipelineStatus status) {
+ DVLOG_FUNC(1) << "status=" << status;
+
+ renderer_client_->OnError(status);
+ StopSendingStatistics();
+}
+
+void MediaFoundationRenderer::OnPlaybackEnded() {
+ DVLOG_FUNC(2);
+
+ renderer_client_->OnEnded();
+ StopSendingStatistics();
+}
+
+void MediaFoundationRenderer::OnBufferingStateChanged(
+ BufferingState state,
+ BufferingStateChangeReason reason) {
+ DVLOG_FUNC(2);
+
+ if (state == BufferingState::BUFFERING_HAVE_ENOUGH) {
+ max_buffering_state_ = state;
+ }
+
+ if (state == BufferingState::BUFFERING_HAVE_NOTHING &&
+ max_buffering_state_ != BufferingState::BUFFERING_HAVE_ENOUGH) {
+ // Prevent sending BUFFERING_HAVE_NOTHING if we haven't previously sent a
+ // BUFFERING_HAVE_ENOUGH state.
+ return;
+ }
+
+ renderer_client_->OnBufferingStateChange(state, reason);
+}
+
+void MediaFoundationRenderer::OnVideoNaturalSizeChanged() {
+ DVLOG_FUNC(2);
+
+ const bool has_video = mf_media_engine_->HasVideo();
+ DVLOG_FUNC(2) << "has_video=" << has_video;
+
+ // Skip if there are no video streams. This can happen because this is
+ // originated from MF_MEDIA_ENGINE_EVENT_FORMATCHANGE.
+ if (!has_video)
+ return;
+
+ DWORD native_width;
+ DWORD native_height;
+ HRESULT hr =
+ mf_media_engine_->GetNativeVideoSize(&native_width, &native_height);
+ if (FAILED(hr)) {
+ // TODO(xhwang): Add UMA to probe if this can happen.
+ DLOG(ERROR) << "Failed to get native video size from MediaEngine, using "
+ "default (640x320). hr="
+ << hr;
+ native_video_size_ = {640, 320};
+ } else {
+ native_video_size_ = {native_width, native_height};
+ }
+
+ // TODO(frankli): Use actual dest rect provided by client instead of video
+ // size. Will fix the following in another CL.
+ ComPtr<IMFMediaEngineEx> mf_media_engine_ex;
+ hr = mf_media_engine_.As(&mf_media_engine_ex);
+ if (FAILED(hr)) {
+ DLOG(ERROR) << PrintHr(hr);
+ return;
+ }
+
+ RECT video_dest_rect = {0};
+ video_dest_rect.right = native_video_size_.width();
+ video_dest_rect.bottom = native_video_size_.height();
+ hr =
+ mf_media_engine_ex->UpdateVideoStream(nullptr, &video_dest_rect, nullptr);
+ if (FAILED(hr)) {
+ DLOG(ERROR) << PrintHr(hr);
+ return;
+ }
+
+ renderer_client_->OnVideoNaturalSizeChange(native_video_size_);
+ return;
+}
+
+void MediaFoundationRenderer::OnTimeUpdate() {
+ DVLOG_FUNC(3) << "media_time=" << GetMediaTime();
+}
+
+} // namespace media
diff --git a/chromium/media/renderers/win/media_foundation_renderer.h b/chromium/media/renderers/win/media_foundation_renderer.h
new file mode 100644
index 00000000000..99c5193550f
--- /dev/null
+++ b/chromium/media/renderers/win/media_foundation_renderer.h
@@ -0,0 +1,163 @@
+// 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_RENDERERS_WIN_MEDIA_FOUNDATION_RENDERER_H_
+#define MEDIA_RENDERERS_WIN_MEDIA_FOUNDATION_RENDERER_H_
+
+#include <d3d11.h>
+#include <mfapi.h>
+#include <mfmediaengine.h>
+#include <wrl.h>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/sequenced_task_runner.h"
+#include "base/timer/timer.h"
+#include "base/unguessable_token.h"
+#include "base/win/windows_types.h"
+#include "media/base/buffering_state.h"
+#include "media/base/media_resource.h"
+#include "media/base/pipeline_status.h"
+#include "media/base/renderer.h"
+#include "media/base/renderer_client.h"
+#include "media/base/win/mf_initializer.h"
+#include "media/renderers/win/media_engine_extension.h"
+#include "media/renderers/win/media_engine_notify_impl.h"
+#include "media/renderers/win/media_foundation_protection_manager.h"
+#include "media/renderers/win/media_foundation_renderer_extension.h"
+#include "media/renderers/win/media_foundation_source_wrapper.h"
+
+namespace media {
+
+// MediaFoundationRenderer bridges the Renderer and Windows MFMediaEngine
+// interfaces.
+class MediaFoundationRenderer : public Renderer,
+ public MediaFoundationRendererExtension {
+ public:
+ // Whether MediaFoundationRenderer() is supported on the current device.
+ static bool IsSupported();
+
+ MediaFoundationRenderer(bool muted,
+ scoped_refptr<base::SequencedTaskRunner> task_runner,
+ bool force_dcomp_mode_for_testing = false);
+
+ ~MediaFoundationRenderer() override;
+
+ // TODO(frankli): naming: Change DComp into DirectComposition for interface
+ // method names in a separate CL.
+
+ // Renderer implementation.
+ void Initialize(MediaResource* media_resource,
+ RendererClient* client,
+ PipelineStatusCallback init_cb) override;
+ void SetCdm(CdmContext* cdm_context, CdmAttachedCB cdm_attached_cb) override;
+ void SetLatencyHint(base::Optional<base::TimeDelta> latency_hint) override;
+ void Flush(base::OnceClosure flush_cb) override;
+ void StartPlayingFrom(base::TimeDelta time) override;
+ void SetPlaybackRate(double playback_rate) override;
+ void SetVolume(float volume) override;
+ base::TimeDelta GetMediaTime() override;
+
+ // MediaFoundationRendererExtension implementation.
+ void SetDCompMode(bool enabled, SetDCompModeCB callback) override;
+ void GetDCompSurface(GetDCompSurfaceCB callback) override;
+ void SetVideoStreamEnabled(bool enabled) override;
+ // SetPlaybackElementId() must be called before Initialize() as
+ // |playback_element_id| is used in Initialize().
+ void SetPlaybackElementId(uint64_t playback_element_id) override;
+ void SetOutputParams(const gfx::Rect& output_rect) override;
+
+ private:
+ HRESULT CreateMediaEngine(MediaResource* media_resource);
+ HRESULT InitializeDXGIDeviceManager();
+ HRESULT InitializeVirtualVideoWindow();
+
+ // Update RendererClient with rendering statistics periodically.
+ HRESULT PopulateStatistics(PipelineStatistics& statistics);
+ void SendStatistics();
+ void StartSendingStatistics();
+ void StopSendingStatistics();
+
+ // Callbacks for |mf_media_engine_notify_|.
+ void OnPlaybackError(PipelineStatus status);
+ void OnPlaybackEnded();
+ void OnBufferingStateChanged(BufferingState state,
+ BufferingStateChangeReason reason);
+ void OnVideoNaturalSizeChanged();
+ void OnTimeUpdate();
+
+ void OnCdmProxyReceived(IMFCdmProxy* cdm);
+
+ HRESULT SetDCompModeInternal(bool enabled);
+ HRESULT GetDCompSurfaceInternal(HANDLE* surface_handle);
+ HRESULT SetSourceOnMediaEngine();
+ HRESULT SetOutputParamsInternal(const gfx::Rect& output_rect);
+
+ // TODO(crbug.com/1017943): Support Audio Indicator when using
+ // media::MojoRenderer. For now, keep |muted_| as const.
+ const bool muted_;
+
+ // Renderer methods are running in the same sequence.
+ scoped_refptr<base::SequencedTaskRunner> task_runner_;
+
+ // Once set, will force |mf_media_engine_| to use DirectComposition mode.
+ // This is used for testing.
+ const bool force_dcomp_mode_for_testing_;
+
+ // Keep this here so it's destroyed after all Media Foundation members below.
+ MFSessionLifetime mf_session_life_time_;
+
+ RendererClient* renderer_client_;
+
+ Microsoft::WRL::ComPtr<IMFMediaEngine> mf_media_engine_;
+ Microsoft::WRL::ComPtr<MediaEngineNotifyImpl> mf_media_engine_notify_;
+ Microsoft::WRL::ComPtr<MediaEngineExtension> mf_media_engine_extension_;
+ Microsoft::WRL::ComPtr<MediaFoundationSourceWrapper> mf_source_;
+ // This enables MFMediaEngine to use hardware acceleration for video decoding
+ // and vdieo processing.
+ Microsoft::WRL::ComPtr<IMFDXGIDeviceManager> dxgi_device_manager_;
+
+ // Current duration of the media.
+ base::TimeDelta duration_;
+
+ // This is the same as "natural_size" in Chromium.
+ gfx::Size native_video_size_;
+
+ // Keep the last volume value being set.
+ float volume_ = 1.0;
+
+ // Used for RendererClient::OnBufferingStateChange().
+ BufferingState max_buffering_state_ = BufferingState::BUFFERING_HAVE_NOTHING;
+
+ // Used for RendererClient::OnStatisticsUpdate().
+ PipelineStatistics statistics_ = {};
+ base::RepeatingTimer statistics_timer_;
+
+ // An identifier corresponds to a WebMediaPlayer. It allows MFMediaEngine
+ // to track the same playback session is running as Renderer can be destroyed
+ // after a period of inactivity by Chromium media pipeliine.
+ // Init it to an invalid ID.
+ uint64_t playback_element_id_ = 0;
+
+ // A fake window handle passed to MF-based rendering pipeline for OPM.
+ HWND virtual_video_window_ = nullptr;
+
+ base::UnguessableToken surface_request_token_;
+ base::win::ScopedHandle dcomp_surface_handle_;
+
+ bool waiting_for_mf_cdm_ = false;
+ CdmContext* cdm_context_ = nullptr;
+ Microsoft::WRL::ComPtr<MediaFoundationProtectionManager>
+ content_protection_manager_;
+
+ // NOTE: Weak pointers must be invalidated before all other member variables.
+ base::WeakPtrFactory<MediaFoundationRenderer> weak_factory_{this};
+
+ DISALLOW_COPY_AND_ASSIGN(MediaFoundationRenderer);
+};
+
+} // namespace media
+
+#endif // MEDIA_RENDERERS_WIN_MEDIA_FOUNDATION_RENDERER_H_
diff --git a/chromium/media/renderers/win/media_foundation_renderer_extension.h b/chromium/media/renderers/win/media_foundation_renderer_extension.h
new file mode 100644
index 00000000000..4d9b04d57f7
--- /dev/null
+++ b/chromium/media/renderers/win/media_foundation_renderer_extension.h
@@ -0,0 +1,45 @@
+// 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_RENDERERS_WIN_MEDIA_FOUNDATION_RENDERER_EXTENSION_H_
+#define MEDIA_RENDERERS_WIN_MEDIA_FOUNDATION_RENDERER_EXTENSION_H_
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "media/base/media_export.h"
+#include "ui/gfx/geometry/rect.h"
+
+namespace media {
+
+// C++ interface equivalent to mojom::MediaFoundationRendererExtension.
+// This interface allows MediaFoundationRenderer to support video rendering
+// using Direct Compositon.
+class MEDIA_EXPORT MediaFoundationRendererExtension {
+ public:
+ virtual ~MediaFoundationRendererExtension() = default;
+
+ // TODO(frankli): naming: Change DComp into DirectComposition for interface
+ // method names in a separate CL.
+
+ // Enable Direct Composition video rendering.
+ using SetDCompModeCB = base::OnceCallback<void(bool)>;
+ virtual void SetDCompMode(bool enabled, SetDCompModeCB callback) = 0;
+
+ // Get a Direct Composition Surface handle.
+ using GetDCompSurfaceCB = base::OnceCallback<void(HANDLE)>;
+ virtual void GetDCompSurface(GetDCompSurfaceCB callback) = 0;
+
+ // Notify renderer whether video is enabled.
+ virtual void SetVideoStreamEnabled(bool enabled) = 0;
+
+ // Provide a unique identifier which maps to a specific playback element.
+ virtual void SetPlaybackElementId(uint64_t playback_element_id) = 0;
+
+ // Notify renderer of output composition parameters.
+ virtual void SetOutputParams(const ::gfx::Rect& rect) = 0;
+};
+
+} // namespace media
+
+#endif // MEDIA_RENDERERS_WIN_MEDIA_FOUNDATION_RENDERER_EXTENSION_H_
diff --git a/chromium/media/renderers/win/media_foundation_renderer_integration_test.cc b/chromium/media/renderers/win/media_foundation_renderer_integration_test.cc
new file mode 100644
index 00000000000..a76833ef47b
--- /dev/null
+++ b/chromium/media/renderers/win/media_foundation_renderer_integration_test.cc
@@ -0,0 +1,96 @@
+// 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/renderers/win/media_foundation_renderer.h"
+
+#include <memory>
+
+#include "media/test/pipeline_integration_test_base.h"
+#include "media/test/test_media_source.h"
+
+namespace media {
+
+namespace {
+
+// TODO(xhwang): Generalize this to support more codecs, or use CanPlay() or
+// IsTypeSupported() which can take mime types directly.
+bool CanDecodeVp9() {
+ if (!MediaFoundationRenderer::IsSupported()) {
+ LOG(WARNING) << "MediaFoundationRenderer not supported";
+ return false;
+ }
+
+ MFT_REGISTER_TYPE_INFO input_type = {MFMediaType_Video, MFVideoFormat_VP90};
+ IMFActivate** activates = nullptr;
+ UINT32 count = 0;
+
+ if (FAILED(MFTEnumEx(MFT_CATEGORY_VIDEO_DECODER,
+ MFT_ENUM_FLAG_SYNCMFT | MFT_ENUM_FLAG_ASYNCMFT |
+ MFT_ENUM_FLAG_HARDWARE,
+ &input_type, /*output_type=*/nullptr, &activates,
+ &count))) {
+ return false;
+ }
+
+ for (UINT32 i = 0; i < count; ++i)
+ activates[i]->Release();
+ CoTaskMemFree(activates);
+
+ if (count == 0) {
+ LOG(WARNING) << "No decoder for VP9";
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace
+
+class MediaFoundationRendererIntegrationTest
+ : public testing::Test,
+ public PipelineIntegrationTestBase {
+ public:
+ MediaFoundationRendererIntegrationTest() {
+ SetCreateRendererCB(base::BindRepeating(
+ &MediaFoundationRendererIntegrationTest::CreateMediaFoundationRenderer,
+ base::Unretained(this)));
+ }
+
+ private:
+ std::unique_ptr<Renderer> CreateMediaFoundationRenderer(
+ base::Optional<RendererFactoryType> factory_type) {
+ auto renderer = std::make_unique<MediaFoundationRenderer>(
+ /*muted=*/false, task_environment_.GetMainThreadTaskRunner(),
+ /*force_dcomp_mode_for_testing=*/true);
+ renderer->SetPlaybackElementId(1); // Must be set before Initialize().
+ return renderer;
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(MediaFoundationRendererIntegrationTest);
+};
+
+TEST_F(MediaFoundationRendererIntegrationTest, BasicPlayback) {
+ if (!CanDecodeVp9())
+ return;
+
+ ASSERT_EQ(PIPELINE_OK, Start("bear-vp9.webm"));
+ Play();
+ ASSERT_TRUE(WaitUntilOnEnded());
+}
+
+TEST_F(MediaFoundationRendererIntegrationTest, BasicPlayback_MediaSource) {
+ if (!CanDecodeVp9())
+ return;
+
+ TestMediaSource source("bear-vp9.webm", 67504);
+ EXPECT_EQ(PIPELINE_OK, StartPipelineWithMediaSource(&source));
+ source.EndOfStream();
+
+ Play();
+ ASSERT_TRUE(WaitUntilOnEnded());
+ source.Shutdown();
+ Stop();
+}
+
+} // namespace media
diff --git a/chromium/media/renderers/win/media_foundation_renderer_unittest.cc b/chromium/media/renderers/win/media_foundation_renderer_unittest.cc
new file mode 100644
index 00000000000..a5d47595e73
--- /dev/null
+++ b/chromium/media/renderers/win/media_foundation_renderer_unittest.cc
@@ -0,0 +1,268 @@
+// 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/renderers/win/media_foundation_renderer.h"
+
+#include <windows.media.protection.h>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/single_thread_task_runner.h"
+#include "base/test/mock_callback.h"
+#include "base/test/task_environment.h"
+#include "base/win/scoped_com_initializer.h"
+#include "media/base/demuxer_stream.h"
+#include "media/base/mock_filters.h"
+#include "media/base/test_helpers.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::NiceMock;
+using ::testing::Return;
+using ::testing::StrictMock;
+
+namespace media {
+
+using ABI::Windows::Media::Protection::IMediaProtectionPMPServer;
+using Microsoft::WRL::ComPtr;
+
+#define MOCK_STDCALL_METHOD1(Name, Types) \
+ MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, Name, Types)
+
+#define MOCK_STDCALL_METHOD2(Name, Types) \
+ MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, Name, Types)
+
+#define MOCK_STDCALL_METHOD3(Name, Types) \
+ MOCK_METHOD3_WITH_CALLTYPE(STDMETHODCALLTYPE, Name, Types)
+
+#define MOCK_STDCALL_METHOD7(Name, Types) \
+ MOCK_METHOD7_WITH_CALLTYPE(STDMETHODCALLTYPE, Name, Types)
+
+class MockMFCdmProxy
+ : public Microsoft::WRL::RuntimeClass<
+ Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>,
+ IMFCdmProxy> {
+ public:
+ MockMFCdmProxy();
+ ~MockMFCdmProxy() override;
+
+ // IMFCdmProxy.
+ MOCK_STDCALL_METHOD2(GetPMPServer,
+ HRESULT(REFIID riid, void** object_result));
+ MOCK_STDCALL_METHOD7(GetInputTrustAuthority,
+ HRESULT(uint64_t playback_element_id,
+ uint32_t stream_id,
+ uint32_t stream_count,
+ const uint8_t* content_init_data,
+ uint32_t content_init_data_size,
+ REFIID riid,
+ IUnknown** object_result));
+
+ MOCK_STDCALL_METHOD1(RefreshTrustedInput,
+ HRESULT(uint64_t playback_element_id));
+ MOCK_STDCALL_METHOD3(SetLastKeyIds,
+ HRESULT(uint64_t playback_element_id,
+ GUID* key_ids,
+ uint32_t key_ids_count));
+ MOCK_STDCALL_METHOD2(ProcessContentEnabler,
+ HRESULT(IUnknown* request, IMFAsyncResult* result));
+};
+
+MockMFCdmProxy::MockMFCdmProxy() = default;
+MockMFCdmProxy::~MockMFCdmProxy() = default;
+
+class MockMediaProtectionPMPServer
+ : public Microsoft::WRL::RuntimeClass<
+ Microsoft::WRL::RuntimeClassFlags<
+ Microsoft::WRL::WinRt | Microsoft::WRL::InhibitRoOriginateError>,
+ IMediaProtectionPMPServer> {
+ public:
+ MockMediaProtectionPMPServer() = default;
+ virtual ~MockMediaProtectionPMPServer() = default;
+
+ static HRESULT MakeMockMediaProtectionPMPServer(
+ IMediaProtectionPMPServer** pmp_server) {
+ *pmp_server = Microsoft::WRL::Make<MockMediaProtectionPMPServer>().Detach();
+ return S_OK;
+ }
+
+ // Return E_NOINTERFACE to avoid a crash when MFMediaEngine tries to use the
+ // mocked IPropertySet from get_Properties().
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid,
+ void** object_result) override {
+ return E_NOINTERFACE;
+ }
+
+ // ABI::Windows::Media::Protection::IMediaProtectionPMPServer.
+ MOCK_STDCALL_METHOD1(
+ get_Properties,
+ HRESULT(
+ ABI::Windows::Foundation::Collections::IPropertySet** properties));
+};
+
+class MediaFoundationRendererTest : public testing::Test {
+ public:
+ MediaFoundationRendererTest() {
+ if (!MediaFoundationRenderer::IsSupported())
+ return;
+
+ MockMediaProtectionPMPServer::MakeMockMediaProtectionPMPServer(
+ &pmp_server_);
+
+ mf_renderer_ = std::make_unique<MediaFoundationRenderer>(
+ /*muted=*/false, task_environment_.GetMainThreadTaskRunner());
+ // It is required to invoke SetPlaybackElementId() before Initialize().
+ mf_renderer_->SetPlaybackElementId(9876543210);
+
+ // Some default actions.
+ ON_CALL(cdm_context_, GetMediaFoundationCdmProxy(_))
+ .WillByDefault(
+ Invoke(this, &MediaFoundationRendererTest::MockGetMFCdm));
+ ON_CALL(mf_cdm_proxy_, GetPMPServer(_, _))
+ .WillByDefault(
+ Invoke(this, &MediaFoundationRendererTest::MockGetPMPServer));
+
+ // Some expected calls with return values.
+ EXPECT_CALL(media_resource_, GetAllStreams())
+ .WillRepeatedly(
+ Invoke(this, &MediaFoundationRendererTest::GetAllStreams));
+ EXPECT_CALL(media_resource_, GetType())
+ .WillRepeatedly(Return(MediaResource::STREAM));
+ }
+
+ ~MediaFoundationRendererTest() override { mf_renderer_.reset(); }
+
+ void AddStream(DemuxerStream::Type type, bool encrypted) {
+ streams_.push_back(CreateMockDemuxerStream(type, encrypted));
+ }
+
+ std::vector<DemuxerStream*> GetAllStreams() {
+ std::vector<DemuxerStream*> streams;
+
+ for (auto& stream : streams_) {
+ streams.push_back(stream.get());
+ }
+
+ return streams;
+ }
+
+ void OnSendCdmProxy(
+ CdmContext::GetMediaFoundationCdmProxyCB get_mf_cdm_proxy_cb) {
+ std::move(get_mf_cdm_proxy_cb).Run(&mf_cdm_proxy_);
+ }
+
+ bool MockGetMFCdm(
+ CdmContext::GetMediaFoundationCdmProxyCB get_mf_cdm_proxy_cb) {
+ // The callback should be invoked asynchronously per API contract. Post
+ // to make callback from OnSendCdmProxy().
+ task_environment_.GetMainThreadTaskRunner()->PostTask(
+ FROM_HERE,
+ base::BindOnce(&MediaFoundationRendererTest::OnSendCdmProxy,
+ base::Unretained(this), std::move(get_mf_cdm_proxy_cb)));
+ return true;
+ }
+
+ HRESULT MockGetPMPServer(REFIID riid, LPVOID* object_result) {
+ ComPtr<IMediaProtectionPMPServer> pmp_server;
+ if (riid != __uuidof(**(&pmp_server)) || !object_result) {
+ return E_INVALIDARG;
+ }
+
+ return pmp_server_.CopyTo(
+ reinterpret_cast<IMediaProtectionPMPServer**>(object_result));
+ }
+
+ protected:
+ base::win::ScopedCOMInitializer com_initializer_;
+ base::test::TaskEnvironment task_environment_;
+ base::MockOnceCallback<void(bool)> set_cdm_cb_;
+ base::MockOnceCallback<void(PipelineStatus)> renderer_init_cb_;
+ NiceMock<MockCdmContext> cdm_context_;
+ NiceMock<MockMediaResource> media_resource_;
+ NiceMock<MockRendererClient> renderer_client_;
+ NiceMock<MockMFCdmProxy> mf_cdm_proxy_;
+ ComPtr<IMediaProtectionPMPServer> pmp_server_;
+ std::unique_ptr<MediaFoundationRenderer> mf_renderer_;
+ std::vector<std::unique_ptr<StrictMock<MockDemuxerStream>>> streams_;
+};
+
+TEST_F(MediaFoundationRendererTest, VerifyInitWithoutSetCdm) {
+ if (!MediaFoundationRenderer::IsSupported())
+ return;
+
+ AddStream(DemuxerStream::AUDIO, /*encrypted=*/false);
+ AddStream(DemuxerStream::VIDEO, /*encrypted=*/true);
+
+ EXPECT_CALL(renderer_init_cb_, Run(PIPELINE_OK));
+
+ mf_renderer_->Initialize(&media_resource_, &renderer_client_,
+ renderer_init_cb_.Get());
+
+ task_environment_.RunUntilIdle();
+}
+
+TEST_F(MediaFoundationRendererTest, SetCdmThenInit) {
+ if (!MediaFoundationRenderer::IsSupported())
+ return;
+
+ AddStream(DemuxerStream::AUDIO, /*encrypted=*/true);
+ AddStream(DemuxerStream::VIDEO, /*encrypted=*/true);
+
+ EXPECT_CALL(set_cdm_cb_, Run(true));
+ EXPECT_CALL(renderer_init_cb_, Run(PIPELINE_OK));
+
+ mf_renderer_->SetCdm(&cdm_context_, set_cdm_cb_.Get());
+ mf_renderer_->Initialize(&media_resource_, &renderer_client_,
+ renderer_init_cb_.Get());
+
+ task_environment_.RunUntilIdle();
+}
+
+TEST_F(MediaFoundationRendererTest, InitThenSetCdm) {
+ if (!MediaFoundationRenderer::IsSupported())
+ return;
+
+ AddStream(DemuxerStream::AUDIO, /*encrypted=*/true);
+ AddStream(DemuxerStream::VIDEO, /*encrypted=*/true);
+
+ EXPECT_CALL(set_cdm_cb_, Run(true));
+ EXPECT_CALL(renderer_init_cb_, Run(PIPELINE_OK));
+
+ mf_renderer_->Initialize(&media_resource_, &renderer_client_,
+ renderer_init_cb_.Get());
+ mf_renderer_->SetCdm(&cdm_context_, set_cdm_cb_.Get());
+
+ task_environment_.RunUntilIdle();
+}
+
+TEST_F(MediaFoundationRendererTest, DirectCompositionHandle) {
+ if (!MediaFoundationRenderer::IsSupported())
+ return;
+
+ base::MockCallback<MediaFoundationRendererExtension::SetDCompModeCB>
+ set_dcomp_mode_cb;
+ base::MockCallback<MediaFoundationRendererExtension::GetDCompSurfaceCB>
+ get_dcomp_cb;
+
+ AddStream(DemuxerStream::AUDIO, /*encrypted=*/true);
+ AddStream(DemuxerStream::VIDEO, /*encrypted=*/true);
+
+ EXPECT_CALL(set_cdm_cb_, Run(true));
+ EXPECT_CALL(renderer_init_cb_, Run(PIPELINE_OK));
+ EXPECT_CALL(set_dcomp_mode_cb, Run(true));
+ // Ignore the DirectComposition handle value returned as our |pmp_server_|
+ // has no real implementation.
+ EXPECT_CALL(get_dcomp_cb, Run(_));
+
+ mf_renderer_->Initialize(&media_resource_, &renderer_client_,
+ renderer_init_cb_.Get());
+ mf_renderer_->SetCdm(&cdm_context_, set_cdm_cb_.Get());
+ mf_renderer_->SetDCompMode(true, set_dcomp_mode_cb.Get());
+ mf_renderer_->GetDCompSurface(get_dcomp_cb.Get());
+
+ task_environment_.RunUntilIdle();
+}
+
+} // namespace media \ No newline at end of file
diff --git a/chromium/media/renderers/win/media_foundation_source_wrapper.cc b/chromium/media/renderers/win/media_foundation_source_wrapper.cc
index a0b1a1e3324..fc16deff47d 100644
--- a/chromium/media/renderers/win/media_foundation_source_wrapper.cc
+++ b/chromium/media/renderers/win/media_foundation_source_wrapper.cc
@@ -16,6 +16,7 @@ namespace media {
using Microsoft::WRL::ComPtr;
MediaFoundationSourceWrapper::MediaFoundationSourceWrapper() = default;
+
MediaFoundationSourceWrapper::~MediaFoundationSourceWrapper() {
if (!cdm_proxy_)
return;
@@ -29,14 +30,14 @@ MediaFoundationSourceWrapper::~MediaFoundationSourceWrapper() {
HRESULT hr = cdm_proxy_->SetLastKeyIds(playback_element_id_, key_ids.data(),
key_ids.size());
DLOG_IF(ERROR, FAILED(hr))
- << "Failed to notify CDM proxy of last Key IDs. hr=" << hr;
+ << "Failed to notify CDM proxy of last Key IDs: " << PrintHr(hr);
}
HRESULT MediaFoundationSourceWrapper::RuntimeClassInitialize(
uint64_t playback_element_id,
MediaResource* media_resource,
scoped_refptr<base::SequencedTaskRunner> task_runner) {
- DVLOG(1) << __func__ << ": this=" << this;
+ DVLOG_FUNC(1);
if (media_resource->GetType() != MediaResource::Type::STREAM) {
DLOG(ERROR) << "MediaResource is not of Type STREAM";
@@ -61,7 +62,7 @@ HRESULT MediaFoundationSourceWrapper::RuntimeClassInitialize(
}
void MediaFoundationSourceWrapper::DetachResource() {
- DVLOG(1) << __func__ << ": this=" << this;
+ DVLOG_FUNC(1);
for (auto stream : media_streams_) {
stream->DetachDemuxerStream();
@@ -70,7 +71,7 @@ void MediaFoundationSourceWrapper::DetachResource() {
HRESULT MediaFoundationSourceWrapper::GetCharacteristics(
DWORD* characteristics) {
- DVLOG(3) << __func__ << ": this=" << this;
+ DVLOG_FUNC(3);
if (state_ == State::kShutdown) {
DLOG(ERROR) << __func__ << ": MF_E_SHUTDOWN";
@@ -110,7 +111,7 @@ HRESULT MediaFoundationSourceWrapper::SelectDefaultStreams(
HRESULT MediaFoundationSourceWrapper::CreatePresentationDescriptor(
IMFPresentationDescriptor** presentation_descriptor_out) {
- DVLOG(2) << __func__ << ": this=" << this;
+ DVLOG_FUNC(2);
if (state_ == State::kShutdown) {
DLOG(ERROR) << __func__ << ": MF_E_SHUTDOWN";
@@ -141,7 +142,7 @@ HRESULT MediaFoundationSourceWrapper::Start(
IMFPresentationDescriptor* presentation_descriptor,
const GUID* guid_time_format,
const PROPVARIANT* start_position) {
- DVLOG(2) << __func__ << ": this=" << this;
+ DVLOG_FUNC(2);
if (state_ == State::kShutdown) {
DLOG(ERROR) << __func__ << ": MF_E_SHUTDOWN";
@@ -232,7 +233,7 @@ HRESULT MediaFoundationSourceWrapper::Start(
}
HRESULT MediaFoundationSourceWrapper::Stop() {
- DVLOG(2) << __func__ << ": this=" << this;
+ DVLOG_FUNC(2);
if (state_ == State::kShutdown) {
DLOG(ERROR) << __func__ << ": MF_E_SHUTDOWN";
@@ -252,7 +253,7 @@ HRESULT MediaFoundationSourceWrapper::Stop() {
}
HRESULT MediaFoundationSourceWrapper::Pause() {
- DVLOG(2) << __func__ << ": this=" << this;
+ DVLOG_FUNC(2);
if (state_ == State::kShutdown) {
DLOG(ERROR) << __func__ << ": MF_E_SHUTDOWN";
@@ -278,7 +279,7 @@ HRESULT MediaFoundationSourceWrapper::Pause() {
// After this method is called, methods on the media source and all of its
// media streams return MF_E_SHUTDOWN (except for IUnknown methods).
HRESULT MediaFoundationSourceWrapper::Shutdown() {
- DVLOG(1) << __func__ << ": this=" << this;
+ DVLOG_FUNC(1);
for (auto stream : media_streams_) {
stream->DetachParent();
@@ -295,7 +296,7 @@ HRESULT MediaFoundationSourceWrapper::Shutdown() {
//
HRESULT MediaFoundationSourceWrapper::GetEvent(DWORD flags,
IMFMediaEvent** event_out) {
- DVLOG(3) << __func__ << ": this=" << this;
+ DVLOG_FUNC(3);
DCHECK(mf_media_event_queue_);
// Not tracing hr to avoid the noise from MF_E_NO_EVENTS_AVAILABLE.
@@ -304,7 +305,7 @@ HRESULT MediaFoundationSourceWrapper::GetEvent(DWORD flags,
HRESULT MediaFoundationSourceWrapper::BeginGetEvent(IMFAsyncCallback* callback,
IUnknown* state) {
- DVLOG(3) << __func__ << ": this=" << this;
+ DVLOG_FUNC(3);
DCHECK(mf_media_event_queue_);
RETURN_IF_FAILED(mf_media_event_queue_->BeginGetEvent(callback, state));
@@ -313,7 +314,7 @@ HRESULT MediaFoundationSourceWrapper::BeginGetEvent(IMFAsyncCallback* callback,
HRESULT MediaFoundationSourceWrapper::EndGetEvent(IMFAsyncResult* result,
IMFMediaEvent** event_out) {
- DVLOG(3) << __func__ << ": this=" << this;
+ DVLOG_FUNC(3);
DCHECK(mf_media_event_queue_);
RETURN_IF_FAILED(mf_media_event_queue_->EndGetEvent(result, event_out));
@@ -324,7 +325,7 @@ HRESULT MediaFoundationSourceWrapper::QueueEvent(MediaEventType type,
REFGUID extended_type,
HRESULT status,
const PROPVARIANT* value) {
- DVLOG(3) << __func__ << ": this=" << this;
+ DVLOG_FUNC(3);
DCHECK(mf_media_event_queue_);
RETURN_IF_FAILED(mf_media_event_queue_->QueueEventParamVar(
@@ -336,19 +337,19 @@ HRESULT MediaFoundationSourceWrapper::GetInputTrustAuthority(
DWORD stream_id,
REFIID riid,
IUnknown** object_out) {
- DVLOG(1) << __func__ << ": this=" << this;
+ DVLOG_FUNC(1);
if (stream_id >= StreamCount())
return E_INVALIDARG;
if (!cdm_proxy_) {
- DLOG(ERROR) << __func__ << ": MF_E_NOT_PROTECTED";
+ DVLOG_FUNC(1) << "MF_E_NOT_PROTECTED";
return MF_E_NOT_PROTECTED;
}
if (!media_streams_[stream_id]->IsEncrypted()) {
- DVLOG(1) << __func__ << ". Unprotected stream. stream_id=" << stream_id
- << ",this=" << this;
+ DVLOG_FUNC(1) << "Unprotected stream; stream_id=" << stream_id;
+
return MF_E_NOT_PROTECTED;
}
@@ -362,7 +363,7 @@ HRESULT MediaFoundationSourceWrapper::GetInputTrustAuthority(
HRESULT MediaFoundationSourceWrapper::GetService(REFGUID guid_service,
REFIID riid,
LPVOID* result) {
- DVLOG(3) << __func__ << ": this=" << this;
+ DVLOG_FUNC(3);
DCHECK(result);
if (!IsEqualGUID(guid_service, MF_RATE_CONTROL_SERVICE))
@@ -373,7 +374,7 @@ HRESULT MediaFoundationSourceWrapper::GetService(REFGUID guid_service,
HRESULT MediaFoundationSourceWrapper::GetSlowestRate(MFRATE_DIRECTION direction,
BOOL supports_thinning,
float* rate) {
- DVLOG(2) << __func__ << ": this=" << this;
+ DVLOG_FUNC(3);
DCHECK(rate);
if (direction == MFRATE_REVERSE) {
@@ -386,7 +387,7 @@ HRESULT MediaFoundationSourceWrapper::GetSlowestRate(MFRATE_DIRECTION direction,
HRESULT MediaFoundationSourceWrapper::GetFastestRate(MFRATE_DIRECTION direction,
BOOL supports_thinning,
float* rate) {
- DVLOG(2) << __func__ << ": this=" << this;
+ DVLOG_FUNC(3);
DCHECK(rate);
if (direction == MFRATE_REVERSE) {
@@ -404,7 +405,7 @@ HRESULT MediaFoundationSourceWrapper::GetFastestRate(MFRATE_DIRECTION direction,
HRESULT MediaFoundationSourceWrapper::IsRateSupported(BOOL supports_thinning,
float new_rate,
float* supported_rate) {
- DVLOG(2) << __func__ << ": this=" << this;
+ DVLOG_FUNC(2) << "new_rate=" << new_rate;
if (state_ == State::kShutdown)
return MF_E_SHUTDOWN;
@@ -438,12 +439,13 @@ HRESULT MediaFoundationSourceWrapper::IsRateSupported(BOOL supports_thinning,
}
}
+ DVLOG_FUNC(2) << PrintHr(hr);
return hr;
}
HRESULT MediaFoundationSourceWrapper::SetRate(BOOL supports_thinning,
float rate) {
- DVLOG(2) << __func__ << ": this=" << this;
+ DVLOG_FUNC(2);
if (state_ == State::kShutdown)
return MF_E_SHUTDOWN;
@@ -460,7 +462,7 @@ HRESULT MediaFoundationSourceWrapper::SetRate(BOOL supports_thinning,
HRESULT MediaFoundationSourceWrapper::GetRate(BOOL* supports_thinning,
float* rate) {
- DVLOG(2) << __func__ << ": this=" << this;
+ DVLOG_FUNC(2);
*supports_thinning = FALSE;
*rate = 0.0f;
@@ -477,7 +479,7 @@ uint32_t MediaFoundationSourceWrapper::StreamCount() const {
}
void MediaFoundationSourceWrapper::CheckForEndOfPresentation() {
- DVLOG(2) << __func__ << ": this=" << this;
+ DVLOG_FUNC(2);
DCHECK(task_runner_->RunsTasksInCurrentSequence());
if (presentation_ended_) {
@@ -495,7 +497,7 @@ void MediaFoundationSourceWrapper::CheckForEndOfPresentation() {
if (presentation_ended_) {
HRESULT hr = QueueEvent(MEEndOfPresentation, GUID_NULL, S_OK, nullptr);
DLOG_IF(ERROR, FAILED(hr))
- << "Failed to notify end of presentation. hr=" << hr;
+ << "Failed to notify end of presentation: " << PrintHr(hr);
}
}
@@ -511,7 +513,7 @@ bool MediaFoundationSourceWrapper::HasEncryptedStream() const {
}
void MediaFoundationSourceWrapper::SetCdmProxy(IMFCdmProxy* cdm_proxy) {
- DVLOG(2) << __func__ << ": this=" << this;
+ DVLOG_FUNC(2);
DCHECK(task_runner_->RunsTasksInCurrentSequence());
// cdm_proxy_ should never change.
@@ -519,11 +521,12 @@ void MediaFoundationSourceWrapper::SetCdmProxy(IMFCdmProxy* cdm_proxy) {
cdm_proxy_ = cdm_proxy;
HRESULT hr = cdm_proxy_->RefreshTrustedInput(playback_element_id_);
- DLOG_IF(ERROR, FAILED(hr)) << "Failed to refresh trusted input. hr=" << hr;
+ DLOG_IF(ERROR, FAILED(hr))
+ << "Failed to refresh trusted input: " << PrintHr(hr);
}
bool MediaFoundationSourceWrapper::SetVideoStreamEnabled(bool enabled) {
- DVLOG(2) << __func__ << ": this=" << this << ",enabled=" << enabled;
+ DVLOG_FUNC(2) << "enabled=" << enabled;
DCHECK(task_runner_->RunsTasksInCurrentSequence());
if (enabled == video_stream_enabled_)
@@ -546,7 +549,7 @@ bool MediaFoundationSourceWrapper::SetVideoStreamEnabled(bool enabled) {
}
void MediaFoundationSourceWrapper::FlushStreams() {
- DVLOG(2) << __func__ << ": this=" << this;
+ DVLOG_FUNC(2);
DCHECK(task_runner_->RunsTasksInCurrentSequence());
for (auto stream : media_streams_) {
diff --git a/chromium/media/renderers/win/media_foundation_stream_wrapper.cc b/chromium/media/renderers/win/media_foundation_stream_wrapper.cc
index 3ea5d7c1305..bac34b20056 100644
--- a/chromium/media/renderers/win/media_foundation_stream_wrapper.cc
+++ b/chromium/media/renderers/win/media_foundation_stream_wrapper.cc
@@ -152,7 +152,7 @@ HRESULT MediaFoundationStreamWrapper::RuntimeClassInitialize(
int stream_id,
IMFMediaSource* parent_source,
DemuxerStream* demuxer_stream) {
- DVLOG(1) << __func__ << ": this=" << this;
+ DVLOG_FUNC(1);
{
base::AutoLock auto_lock(lock_);
@@ -168,27 +168,27 @@ HRESULT MediaFoundationStreamWrapper::RuntimeClassInitialize(
void MediaFoundationStreamWrapper::SetTaskRunner(
scoped_refptr<base::SequencedTaskRunner> task_runner) {
- DVLOG(1) << __func__ << ": this=" << this;
+ DVLOG_FUNC(1);
task_runner_ = std::move(task_runner);
}
void MediaFoundationStreamWrapper::DetachParent() {
- DVLOG(1) << __func__ << ": this=" << this;
+ DVLOG_FUNC(1);
base::AutoLock auto_lock(lock_);
parent_source_ = nullptr;
}
void MediaFoundationStreamWrapper::DetachDemuxerStream() {
- DVLOG(1) << __func__ << ": this=" << this;
+ DVLOG_FUNC(1);
DCHECK(task_runner_->RunsTasksInCurrentSequence());
demuxer_stream_ = nullptr;
}
void MediaFoundationStreamWrapper::SetSelected(bool selected) {
- DVLOG(2) << __func__ << ": this=" << this << ",selected=" << selected;
+ DVLOG_FUNC(2) << "selected=" << selected;
base::AutoLock auto_lock(lock_);
selected_ = selected;
@@ -196,20 +196,20 @@ void MediaFoundationStreamWrapper::SetSelected(bool selected) {
bool MediaFoundationStreamWrapper::IsSelected() {
base::AutoLock auto_lock(lock_);
- DVLOG(2) << __func__ << ": this=" << this << ",selected_=" << selected_;
+ DVLOG_FUNC(2) << "selected_=" << selected_;
return selected_;
}
bool MediaFoundationStreamWrapper::IsEnabled() {
base::AutoLock auto_lock(lock_);
- DVLOG(2) << __func__ << ": this=" << this << ",enabled_=" << enabled_;
+ DVLOG_FUNC(2) << "enabled_=" << enabled_;
return enabled_;
}
void MediaFoundationStreamWrapper::SetEnabled(bool enabled) {
- DVLOG(2) << __func__ << ": this=" << this << ",enabled=" << enabled;
+ DVLOG_FUNC(2) << "enabled=" << enabled;
{
base::AutoLock auto_lock(lock_);
@@ -222,7 +222,7 @@ void MediaFoundationStreamWrapper::SetEnabled(bool enabled) {
}
void MediaFoundationStreamWrapper::SetFlushed(bool flushed) {
- DVLOG(2) << __func__ << ": this=" << this << ",flushed=" << flushed;
+ DVLOG_FUNC(2) << "flushed=" << flushed;
base::AutoLock auto_lock(lock_);
flushed_ = flushed;
@@ -234,14 +234,14 @@ void MediaFoundationStreamWrapper::SetFlushed(bool flushed) {
}
bool MediaFoundationStreamWrapper::HasEnded() const {
- DVLOG(2) << __func__ << ": this=" << this;
+ DVLOG_FUNC(2);
return stream_ended_;
}
HRESULT MediaFoundationStreamWrapper::QueueStartedEvent(
const PROPVARIANT* start_position) {
- DVLOG(2) << __func__ << ": this=" << this;
+ DVLOG_FUNC(2);
state_ = State::kStarted;
RETURN_IF_FAILED(mf_media_event_queue_->QueueEventParamVar(
@@ -251,7 +251,7 @@ HRESULT MediaFoundationStreamWrapper::QueueStartedEvent(
HRESULT MediaFoundationStreamWrapper::QueueSeekedEvent(
const PROPVARIANT* start_position) {
- DVLOG(2) << __func__ << ": this=" << this;
+ DVLOG_FUNC(2);
state_ = State::kStarted;
RETURN_IF_FAILED(mf_media_event_queue_->QueueEventParamVar(
@@ -260,7 +260,7 @@ HRESULT MediaFoundationStreamWrapper::QueueSeekedEvent(
}
HRESULT MediaFoundationStreamWrapper::QueueStoppedEvent() {
- DVLOG(2) << __func__ << ": this=" << this;
+ DVLOG_FUNC(2);
state_ = State::kStopped;
RETURN_IF_FAILED(mf_media_event_queue_->QueueEventParamVar(
@@ -269,7 +269,7 @@ HRESULT MediaFoundationStreamWrapper::QueueStoppedEvent() {
}
HRESULT MediaFoundationStreamWrapper::QueuePausedEvent() {
- DVLOG(2) << __func__ << ": this=" << this;
+ DVLOG_FUNC(2);
state_ = State::kPaused;
RETURN_IF_FAILED(mf_media_event_queue_->QueueEventParamVar(
@@ -282,7 +282,7 @@ DemuxerStream::Type MediaFoundationStreamWrapper::StreamType() const {
}
void MediaFoundationStreamWrapper::ProcessRequestsIfPossible() {
- DVLOG(3) << __func__ << ". this=" << this;
+ DVLOG_FUNC(3);
DCHECK(task_runner_->RunsTasksInCurrentSequence());
{
@@ -313,16 +313,16 @@ void MediaFoundationStreamWrapper::ProcessRequestsIfPossible() {
HRESULT MediaFoundationStreamWrapper::ServiceSampleRequest(
IUnknown* token,
DecoderBuffer* buffer) {
- DVLOG(3) << __func__ << ". this=" << this;
+ DVLOG_FUNC(3);
DCHECK(task_runner_->RunsTasksInCurrentSequence());
lock_.AssertAcquired();
if (buffer->end_of_stream()) {
if (!enabled_) {
- DVLOG(2) << "Ignoring EOS for disabled stream: this=" << this;
+ DVLOG_FUNC(2) << "Ignoring EOS for disabled stream";
return S_OK;
}
- DVLOG(2) << "End of stream: this=" << this;
+ DVLOG_FUNC(2) << "End of stream";
RETURN_IF_FAILED(mf_media_event_queue_->QueueEventParamUnk(
MEEndOfStream, GUID_NULL, S_OK, nullptr));
stream_ended_ = true;
@@ -331,9 +331,8 @@ HRESULT MediaFoundationStreamWrapper::ServiceSampleRequest(
->CheckForEndOfPresentation();
}
} else {
- DVLOG(3) << __func__ << ". this=" << this
- << ",buffer ts(ms)=" << buffer->timestamp().InMilliseconds()
- << ",is_key_frame=" << buffer->is_key_frame();
+ DVLOG_FUNC(3) << "buffer ts=" << buffer->timestamp()
+ << ", is_key_frame=" << buffer->is_key_frame();
ComPtr<IMFSample> mf_sample;
RETURN_IF_FAILED(GenerateSampleFromDecoderBuffer(buffer, &mf_sample));
if (token) {
@@ -347,7 +346,7 @@ HRESULT MediaFoundationStreamWrapper::ServiceSampleRequest(
}
bool MediaFoundationStreamWrapper::ServicePostFlushSampleRequest() {
- DVLOG(3) << __func__ << ". this=" << this;
+ DVLOG_FUNC(3);
DCHECK(task_runner_->RunsTasksInCurrentSequence());
base::AutoLock auto_lock(lock_);
@@ -360,7 +359,7 @@ bool MediaFoundationStreamWrapper::ServicePostFlushSampleRequest() {
HRESULT hr = ServiceSampleRequest(request_token.Get(),
post_flush_buffers_.front().get());
if (FAILED(hr)) {
- DLOG(WARNING) << "Failed to service post flush sample. hr=" << hr;
+ DLOG(WARNING) << "Failed to service post flush sample: " << PrintHr(hr);
return false;
}
@@ -370,7 +369,7 @@ bool MediaFoundationStreamWrapper::ServicePostFlushSampleRequest() {
}
HRESULT MediaFoundationStreamWrapper::QueueFormatChangedEvent() {
- DVLOG(2) << __func__ << ": this=" << this;
+ DVLOG_FUNC(2);
ComPtr<IMFMediaType> media_type;
RETURN_IF_FAILED(GetMediaType(&media_type));
@@ -382,7 +381,7 @@ HRESULT MediaFoundationStreamWrapper::QueueFormatChangedEvent() {
void MediaFoundationStreamWrapper::OnDemuxerStreamRead(
DemuxerStream::Status status,
scoped_refptr<DecoderBuffer> buffer) {
- DVLOG(3) << __func__ << ". this=" << this;
+ DVLOG_FUNC(3);
{
base::AutoLock auto_lock(lock_);
DCHECK(pending_stream_read_);
@@ -395,39 +394,40 @@ void MediaFoundationStreamWrapper::OnDemuxerStreamRead(
// Push |buffer| to process later if needed. Otherwise, process it
// immediately.
if (flushed_ || !post_flush_buffers_.empty()) {
- DVLOG(3) << __func__ << ". this=" << this << ", push buffer.";
+ DVLOG_FUNC(3) << "push buffer.";
post_flush_buffers_.push(buffer);
} else {
hr = ServiceSampleRequest(token.Get(), buffer.get());
if (FAILED(hr)) {
- DLOG(ERROR) << __func__ << ": ServiceSampleRequest failed. hr=" << hr;
+ DLOG(ERROR) << __func__
+ << ": ServiceSampleRequest failed: " << PrintHr(hr);
return;
}
pending_sample_request_tokens_.pop();
}
} else if (status == DemuxerStream::Status::kConfigChanged) {
- DVLOG(2) << "Stream config changed. this=" << this
- << ",AreFormatChangesEnabled=" << AreFormatChangesEnabled();
+ DVLOG_FUNC(2) << "Stream config changed, AreFormatChangesEnabled="
+ << AreFormatChangesEnabled();
if (AreFormatChangesEnabled()) {
hr = QueueFormatChangedEvent();
if (FAILED(hr)) {
DLOG(ERROR) << __func__
- << ": QueueFormatChangedEvent failed. hr=" << hr;
+ << ": QueueFormatChangedEvent failed: " << PrintHr(hr);
return;
}
}
} else if (status == DemuxerStream::Status::kError) {
- DVLOG(2) << "Stream read error: this=" << this;
+ DVLOG_FUNC(2) << "Stream read error";
mf_media_event_queue_->QueueEventParamVar(
MEError, GUID_NULL, MF_E_INVALID_STREAM_DATA, nullptr);
return;
} else if (status == DemuxerStream::Status::kAborted) {
- DVLOG(2) << "Stream read aborted: this=" << this;
+ DVLOG_FUNC(2) << "Stream read aborted";
// Continue to ProcessRequestsIfPossible() to satisfy pending sample
// request by issuing DemuxerStream::Read() if necessary.
} else {
NOTREACHED() << "Unexpected demuxer stream status. status=" << status
- << ",this=" << this;
+ << ", this=" << this;
}
}
@@ -437,7 +437,7 @@ void MediaFoundationStreamWrapper::OnDemuxerStreamRead(
HRESULT MediaFoundationStreamWrapper::GenerateSampleFromDecoderBuffer(
DecoderBuffer* buffer,
IMFSample** sample_out) {
- DVLOG(3) << __func__ << ". this=" << this;
+ DVLOG_FUNC(3);
ComPtr<IMFSample> mf_sample;
RETURN_IF_FAILED(MFCreateSample(&mf_sample));
@@ -478,14 +478,14 @@ HRESULT MediaFoundationStreamWrapper::GenerateSampleFromDecoderBuffer(
HRESULT MediaFoundationStreamWrapper::TransformSample(
Microsoft::WRL::ComPtr<IMFSample>& sample) {
- DVLOG(3) << __func__ << ". this=" << this;
+ DVLOG_FUNC(3);
return S_OK;
}
HRESULT MediaFoundationStreamWrapper::GetMediaSource(
IMFMediaSource** media_source_out) {
- DVLOG(2) << __func__ << ": this=" << this;
+ DVLOG_FUNC(2);
DCHECK(!task_runner_->RunsTasksInCurrentSequence());
base::AutoLock auto_lock(lock_);
@@ -499,7 +499,7 @@ HRESULT MediaFoundationStreamWrapper::GetMediaSource(
HRESULT MediaFoundationStreamWrapper::GetStreamDescriptor(
IMFStreamDescriptor** stream_descriptor_out) {
- DVLOG(2) << __func__ << ": this=" << this;
+ DVLOG_FUNC(2);
if (!mf_stream_descriptor_) {
DLOG(ERROR) << __func__ << ": MF_E_NOT_INITIALIZED";
@@ -510,7 +510,7 @@ HRESULT MediaFoundationStreamWrapper::GetStreamDescriptor(
}
HRESULT MediaFoundationStreamWrapper::RequestSample(IUnknown* token) {
- DVLOG(3) << __func__ << ". this=" << this;
+ DVLOG_FUNC(3);
DCHECK(!task_runner_->RunsTasksInCurrentSequence());
base::AutoLock auto_lock(lock_);
@@ -527,7 +527,7 @@ HRESULT MediaFoundationStreamWrapper::RequestSample(IUnknown* token) {
HRESULT MediaFoundationStreamWrapper::GetEvent(DWORD flags,
IMFMediaEvent** event_out) {
- DVLOG(3) << __func__ << ". this=" << this;
+ DVLOG_FUNC(3);
DCHECK(mf_media_event_queue_);
// Not tracing hr to avoid the noise from MF_E_NO_EVENTS_AVAILABLE.
@@ -536,7 +536,7 @@ HRESULT MediaFoundationStreamWrapper::GetEvent(DWORD flags,
HRESULT MediaFoundationStreamWrapper::BeginGetEvent(IMFAsyncCallback* callback,
IUnknown* state) {
- DVLOG(3) << __func__ << ". this=" << this;
+ DVLOG_FUNC(3);
DCHECK(mf_media_event_queue_);
RETURN_IF_FAILED(mf_media_event_queue_->BeginGetEvent(callback, state));
@@ -545,7 +545,7 @@ HRESULT MediaFoundationStreamWrapper::BeginGetEvent(IMFAsyncCallback* callback,
HRESULT MediaFoundationStreamWrapper::EndGetEvent(IMFAsyncResult* result,
IMFMediaEvent** event_out) {
- DVLOG(3) << __func__ << ". this=" << this;
+ DVLOG_FUNC(3);
DCHECK(mf_media_event_queue_);
RETURN_IF_FAILED(mf_media_event_queue_->EndGetEvent(result, event_out));
@@ -556,7 +556,7 @@ HRESULT MediaFoundationStreamWrapper::QueueEvent(MediaEventType type,
REFGUID extended_type,
HRESULT status,
const PROPVARIANT* value) {
- DVLOG(3) << __func__ << ". this=" << this;
+ DVLOG_FUNC(3);
DCHECK(mf_media_event_queue_);
RETURN_IF_FAILED(mf_media_event_queue_->QueueEventParamVar(
@@ -565,7 +565,7 @@ HRESULT MediaFoundationStreamWrapper::QueueEvent(MediaEventType type,
}
HRESULT MediaFoundationStreamWrapper::GenerateStreamDescriptor() {
- DVLOG(2) << __func__ << ": this=" << this;
+ DVLOG_FUNC(2);
ComPtr<IMFMediaType> media_type;
IMFMediaType** mediaTypes = &media_type;
diff --git a/chromium/media/renderers/yuv_util.cc b/chromium/media/renderers/yuv_util.cc
new file mode 100644
index 00000000000..3a66d63bd94
--- /dev/null
+++ b/chromium/media/renderers/yuv_util.cc
@@ -0,0 +1,221 @@
+// 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.
+
+#include "media/renderers/yuv_util.h"
+
+#include <GLES3/gl3.h>
+
+#include "components/viz/common/gpu/raster_context_provider.h"
+#include "gpu/GLES2/gl2extchromium.h"
+#include "gpu/command_buffer/client/raster_interface.h"
+#include "gpu/command_buffer/common/mailbox_holder.h"
+#include "media/base/video_frame.h"
+#include "third_party/skia/include/core/SkImage.h"
+#include "third_party/skia/include/gpu/GrContext.h"
+
+namespace media {
+
+namespace {
+
+static constexpr size_t kNumNV12Planes = 2;
+static constexpr size_t kNumYUVPlanes = 3;
+using YUVMailboxes = std::array<gpu::MailboxHolder, kNumYUVPlanes>;
+
+YUVMailboxes GetYUVMailboxes(const VideoFrame* video_frame,
+ gpu::raster::RasterInterface* ri) {
+ YUVMailboxes mailboxes;
+
+ for (size_t i = 0; i < video_frame->NumTextures(); ++i) {
+ mailboxes[i] = video_frame->mailbox_holder(i);
+ DCHECK(mailboxes[i].texture_target == GL_TEXTURE_2D ||
+ mailboxes[i].texture_target == GL_TEXTURE_EXTERNAL_OES ||
+ mailboxes[i].texture_target == GL_TEXTURE_RECTANGLE_ARB)
+ << "Unsupported texture target " << std::hex << std::showbase
+ << mailboxes[i].texture_target;
+ ri->WaitSyncTokenCHROMIUM(mailboxes[i].sync_token.GetConstData());
+ }
+
+ return mailboxes;
+}
+
+struct YUVPlaneTextureInfo {
+ GrGLTextureInfo texture = {0, 0};
+ bool is_shared_image = false;
+};
+using YUVTexturesInfo = std::array<YUVPlaneTextureInfo, kNumYUVPlanes>;
+
+YUVTexturesInfo GetYUVTexturesInfo(
+ const VideoFrame* video_frame,
+ viz::RasterContextProvider* raster_context_provider) {
+ gpu::raster::RasterInterface* ri = raster_context_provider->RasterInterface();
+ DCHECK(ri);
+ YUVMailboxes mailboxes = GetYUVMailboxes(video_frame, ri);
+ YUVTexturesInfo yuv_textures_info;
+
+ GrGLenum skia_texture_format =
+ video_frame->format() == PIXEL_FORMAT_NV12 ? GL_RGB8 : GL_R8_EXT;
+ for (size_t i = 0; i < video_frame->NumTextures(); ++i) {
+ yuv_textures_info[i].texture.fID =
+ ri->CreateAndConsumeForGpuRaster(mailboxes[i].mailbox);
+ if (mailboxes[i].mailbox.IsSharedImage()) {
+ yuv_textures_info[i].is_shared_image = true;
+ ri->BeginSharedImageAccessDirectCHROMIUM(
+ yuv_textures_info[i].texture.fID,
+ GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM);
+ }
+
+ yuv_textures_info[i].texture.fTarget = mailboxes[i].texture_target;
+ yuv_textures_info[i].texture.fFormat = skia_texture_format;
+ }
+
+ return yuv_textures_info;
+}
+
+void DeleteYUVTextures(const VideoFrame* video_frame,
+ viz::RasterContextProvider* raster_context_provider,
+ const YUVTexturesInfo& yuv_textures_info) {
+ gpu::raster::RasterInterface* ri = raster_context_provider->RasterInterface();
+ DCHECK(ri);
+
+ for (size_t i = 0; i < video_frame->NumTextures(); ++i) {
+ if (yuv_textures_info[i].is_shared_image)
+ ri->EndSharedImageAccessDirectCHROMIUM(yuv_textures_info[i].texture.fID);
+ ri->DeleteGpuRasterTexture(yuv_textures_info[i].texture.fID);
+ }
+}
+
+void ConvertFromVideoFrameYUVWithGrContext(
+ const VideoFrame* video_frame,
+ viz::RasterContextProvider* raster_context_provider,
+ const gpu::MailboxHolder& dest_mailbox_holder) {
+ gpu::raster::RasterInterface* ri = raster_context_provider->RasterInterface();
+ DCHECK(ri);
+ ri->WaitSyncTokenCHROMIUM(dest_mailbox_holder.sync_token.GetConstData());
+ GLuint dest_tex_id =
+ ri->CreateAndConsumeForGpuRaster(dest_mailbox_holder.mailbox);
+ if (dest_mailbox_holder.mailbox.IsSharedImage()) {
+ ri->BeginSharedImageAccessDirectCHROMIUM(
+ dest_tex_id, GL_SHARED_IMAGE_ACCESS_MODE_READWRITE_CHROMIUM);
+ }
+ // Let the SkImage fall out of scope and track the result using dest_tex_id
+ NewSkImageFromVideoFrameYUVTexturesWithExternalBackend(
+ video_frame, raster_context_provider, dest_mailbox_holder.texture_target,
+ dest_tex_id);
+ if (dest_mailbox_holder.mailbox.IsSharedImage())
+ ri->EndSharedImageAccessDirectCHROMIUM(dest_tex_id);
+ ri->DeleteGpuRasterTexture(dest_tex_id);
+}
+
+SkYUVColorSpace ColorSpaceToSkYUVColorSpace(
+ const gfx::ColorSpace& color_space) {
+ // TODO(hubbe): This should really default to rec709.
+ // https://crbug.com/828599
+ SkYUVColorSpace sk_color_space = kRec601_SkYUVColorSpace;
+ color_space.ToSkYUVColorSpace(&sk_color_space);
+ return sk_color_space;
+}
+
+} // namespace
+
+void ConvertFromVideoFrameYUV(
+ const VideoFrame* video_frame,
+ viz::RasterContextProvider* raster_context_provider,
+ const gpu::MailboxHolder& dest_mailbox_holder) {
+ DCHECK(raster_context_provider);
+ if (raster_context_provider->GrContext()) {
+ ConvertFromVideoFrameYUVWithGrContext(video_frame, raster_context_provider,
+ dest_mailbox_holder);
+ return;
+ }
+
+ auto* ri = raster_context_provider->RasterInterface();
+ DCHECK(ri);
+ ri->WaitSyncTokenCHROMIUM(dest_mailbox_holder.sync_token.GetConstData());
+ YUVMailboxes mailboxes = GetYUVMailboxes(video_frame, ri);
+ SkYUVColorSpace color_space =
+ ColorSpaceToSkYUVColorSpace(video_frame->ColorSpace());
+ if (video_frame->format() == PIXEL_FORMAT_I420) {
+ DCHECK_EQ(video_frame->NumTextures(), kNumYUVPlanes);
+ ri->ConvertYUVMailboxesToRGB(dest_mailbox_holder.mailbox, color_space,
+ mailboxes[0].mailbox, mailboxes[1].mailbox,
+ mailboxes[2].mailbox);
+ } else {
+ DCHECK_EQ(video_frame->format(), PIXEL_FORMAT_NV12);
+ DCHECK_EQ(video_frame->NumTextures(), kNumNV12Planes);
+ ri->ConvertNV12MailboxesToRGB(dest_mailbox_holder.mailbox, color_space,
+ mailboxes[0].mailbox, mailboxes[1].mailbox);
+ }
+}
+
+sk_sp<SkImage> NewSkImageFromVideoFrameYUVTexturesWithExternalBackend(
+ const VideoFrame* video_frame,
+ viz::RasterContextProvider* raster_context_provider,
+ unsigned int texture_target,
+ unsigned int texture_id) {
+ DCHECK(video_frame->HasTextures());
+ GrContext* gr_context = raster_context_provider->GrContext();
+ DCHECK(gr_context);
+ // TODO: We should compare the DCHECK vs when UpdateLastImage calls this
+ // function. (https://crbug.com/674185)
+ DCHECK(video_frame->format() == PIXEL_FORMAT_I420 ||
+ video_frame->format() == PIXEL_FORMAT_NV12);
+
+ gfx::Size ya_tex_size = video_frame->coded_size();
+ gfx::Size uv_tex_size((ya_tex_size.width() + 1) / 2,
+ (ya_tex_size.height() + 1) / 2);
+
+ GrGLTextureInfo backend_texture{};
+
+ YUVTexturesInfo yuv_textures_info =
+ GetYUVTexturesInfo(video_frame, raster_context_provider);
+
+ GrBackendTexture yuv_textures[3] = {
+ GrBackendTexture(ya_tex_size.width(), ya_tex_size.height(),
+ GrMipMapped::kNo, yuv_textures_info[0].texture),
+ GrBackendTexture(uv_tex_size.width(), uv_tex_size.height(),
+ GrMipMapped::kNo, yuv_textures_info[1].texture),
+ GrBackendTexture(uv_tex_size.width(), uv_tex_size.height(),
+ GrMipMapped::kNo, yuv_textures_info[2].texture),
+ };
+ backend_texture.fID = texture_id;
+ backend_texture.fTarget = texture_target;
+ backend_texture.fFormat = GL_RGBA8;
+ GrBackendTexture result_texture(video_frame->coded_size().width(),
+ video_frame->coded_size().height(),
+ GrMipMapped::kNo, backend_texture);
+
+ sk_sp<SkImage> img = YUVGrBackendTexturesToSkImage(
+ gr_context, video_frame->ColorSpace(), video_frame->format(),
+ yuv_textures, result_texture);
+ gr_context->flush();
+
+ DeleteYUVTextures(video_frame, raster_context_provider, yuv_textures_info);
+
+ return img;
+}
+
+sk_sp<SkImage> YUVGrBackendTexturesToSkImage(
+ GrContext* gr_context,
+ gfx::ColorSpace video_color_space,
+ VideoPixelFormat video_format,
+ GrBackendTexture* yuv_textures,
+ const GrBackendTexture& result_texture) {
+ SkYUVColorSpace color_space = ColorSpaceToSkYUVColorSpace(video_color_space);
+
+ switch (video_format) {
+ case PIXEL_FORMAT_NV12:
+ return SkImage::MakeFromNV12TexturesCopyWithExternalBackend(
+ gr_context, color_space, yuv_textures, kTopLeft_GrSurfaceOrigin,
+ result_texture);
+ case PIXEL_FORMAT_I420:
+ return SkImage::MakeFromYUVTexturesCopyWithExternalBackend(
+ gr_context, color_space, yuv_textures, kTopLeft_GrSurfaceOrigin,
+ result_texture);
+ default:
+ NOTREACHED();
+ return nullptr;
+ }
+}
+
+} // namespace media
diff --git a/chromium/media/renderers/yuv_util.h b/chromium/media/renderers/yuv_util.h
new file mode 100644
index 00000000000..e8fe451ab07
--- /dev/null
+++ b/chromium/media/renderers/yuv_util.h
@@ -0,0 +1,54 @@
+// 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.
+
+#ifndef MEDIA_RENDERERS_YUV_UTIL_H_
+#define MEDIA_RENDERERS_YUV_UTIL_H_
+
+#include "media/base/media_export.h"
+#include "media/base/video_types.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+#include "ui/gfx/color_space.h"
+
+// Skia forward declarations
+class GrBackendTexture;
+class GrContext;
+class SkImage;
+
+namespace gpu {
+struct MailboxHolder;
+}
+
+namespace viz {
+class RasterContextProvider;
+} // namespace viz
+
+namespace media {
+
+class VideoFrame;
+
+// Converts a YUV video frame to RGB format and stores the results in the
+// provided mailbox. The caller of this function maintains ownership of the
+// mailbox.
+MEDIA_EXPORT void ConvertFromVideoFrameYUV(
+ const VideoFrame* video_frame,
+ viz::RasterContextProvider* raster_context_provider,
+ const gpu::MailboxHolder& dest_mailbox_holder);
+
+MEDIA_EXPORT sk_sp<SkImage>
+NewSkImageFromVideoFrameYUVTexturesWithExternalBackend(
+ const VideoFrame* video_frame,
+ viz::RasterContextProvider* raster_context_provider,
+ unsigned int texture_target,
+ unsigned int texture_id);
+
+MEDIA_EXPORT sk_sp<SkImage> YUVGrBackendTexturesToSkImage(
+ GrContext* gr_context,
+ gfx::ColorSpace video_color_space,
+ VideoPixelFormat video_format,
+ GrBackendTexture* yuv_textures,
+ const GrBackendTexture& result_texture);
+
+} // namespace media
+
+#endif // MEDIA_RENDERERS_YUV_UTIL_H_ \ No newline at end of file
diff --git a/chromium/media/tools/constrained_network_server/cns.py b/chromium/media/tools/constrained_network_server/cns.py
index 58e2ba000d8..d039d8e7acc 100755
--- a/chromium/media/tools/constrained_network_server/cns.py
+++ b/chromium/media/tools/constrained_network_server/cns.py
@@ -2,6 +2,41 @@
# 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.
+#
+# [VPYTHON:BEGIN]
+# wheel: <
+# name: "infra/python/wheels/pytz-py2_py3"
+# version: "version:2018.4"
+# >
+# wheel: <
+# name: "infra/python/wheels/tempora-py2_py3"
+# version: "version:1.11"
+# >
+# wheel: <
+# name: "infra/python/wheels/more-itertools-py2_py3"
+# version: "version:4.1.0"
+# >
+# wheel: <
+# name: "infra/python/wheels/backports_functools_lru_cache-py2_py3"
+# version: "version:1.5"
+# >
+# wheel: <
+# name: "infra/python/wheels/six-py2_py3"
+# version: "version:1.12.0"
+# >
+# wheel: <
+# name: "infra/python/wheels/portend-py2_py3"
+# version: "version:2.2"
+# >
+# wheel: <
+# name: "infra/python/wheels/cheroot-py2_py3"
+# version: "version:6.2.4"
+# >
+# wheel: <
+# name: "infra/python/wheels/cherrypy-py2_py3"
+# version: "version:14.2.0"
+# >
+# [VPYTHON:END]
"""Constrained Network Server. Serves files with supplied network constraints.
diff --git a/chromium/media/tools/constrained_network_server/cns_test.py b/chromium/media/tools/constrained_network_server/cns_test.py
index 5d794956755..a3ac54e368d 100755
--- a/chromium/media/tools/constrained_network_server/cns_test.py
+++ b/chromium/media/tools/constrained_network_server/cns_test.py
@@ -1,8 +1,43 @@
-#!/usr/bin/env python
+#!/usr/bin/env vpython
# 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.
+# [VPYTHON:BEGIN]
+# wheel: <
+# name: "infra/python/wheels/pytz-py2_py3"
+# version: "version:2018.4"
+# >
+# wheel: <
+# name: "infra/python/wheels/tempora-py2_py3"
+# version: "version:1.11"
+# >
+# wheel: <
+# name: "infra/python/wheels/more-itertools-py2_py3"
+# version: "version:4.1.0"
+# >
+# wheel: <
+# name: "infra/python/wheels/backports_functools_lru_cache-py2_py3"
+# version: "version:1.5"
+# >
+# wheel: <
+# name: "infra/python/wheels/six-py2_py3"
+# version: "version:1.12.0"
+# >
+# wheel: <
+# name: "infra/python/wheels/portend-py2_py3"
+# version: "version:2.2"
+# >
+# wheel: <
+# name: "infra/python/wheels/cheroot-py2_py3"
+# version: "version:6.2.4"
+# >
+# wheel: <
+# name: "infra/python/wheels/cherrypy-py2_py3"
+# version: "version:14.2.0"
+# >
+# [VPYTHON:END]
+
"""Tests for Constrained Network Server."""
import os
import signal
diff --git a/chromium/media/video/BUILD.gn b/chromium/media/video/BUILD.gn
index f6c7977c7ad..4eacf416cf1 100644
--- a/chromium/media/video/BUILD.gn
+++ b/chromium/media/video/BUILD.gn
@@ -66,6 +66,14 @@ source_set("video") {
"//build/config/compiler:no_size_t_to_int_warning",
"//media:subcomponent_config",
]
+
+ if (media_use_libvpx) {
+ sources += [
+ "vpx_video_encoder.cc",
+ "vpx_video_encoder.h",
+ ]
+ deps += [ "//third_party/libvpx" ]
+ }
}
# Note: This is a roll-up only target; do not expand the visibility. DEPS should
diff --git a/chromium/media/video/fake_video_encode_accelerator.cc b/chromium/media/video/fake_video_encode_accelerator.cc
index 30f2251ccdf..783f8f0ba7f 100644
--- a/chromium/media/video/fake_video_encode_accelerator.cc
+++ b/chromium/media/video/fake_video_encode_accelerator.cc
@@ -5,8 +5,8 @@
#include "media/video/fake_video_encode_accelerator.h"
#include "base/bind.h"
+#include "base/check.h"
#include "base/location.h"
-#include "base/logging.h"
#include "base/single_thread_task_runner.h"
namespace media {
diff --git a/chromium/media/video/h264_bit_reader.cc b/chromium/media/video/h264_bit_reader.cc
index b3a13719a81..0fe227055c2 100644
--- a/chromium/media/video/h264_bit_reader.cc
+++ b/chromium/media/video/h264_bit_reader.cc
@@ -3,7 +3,7 @@
// found in the LICENSE file.
#include "media/video/h264_bit_reader.h"
-#include "base/logging.h"
+#include "base/check.h"
namespace media {
diff --git a/chromium/media/video/picture.cc b/chromium/media/video/picture.cc
index 316ce15e5b8..3e169be0b80 100644
--- a/chromium/media/video/picture.cc
+++ b/chromium/media/video/picture.cc
@@ -2,9 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "base/macros.h"
#include "media/video/picture.h"
+#include "base/logging.h"
+#include "base/macros.h"
+
namespace media {
PictureBuffer::PictureBuffer(int32_t id, const gfx::Size& size)
diff --git a/chromium/media/video/video_decode_accelerator.h b/chromium/media/video/video_decode_accelerator.h
index da79cfc4c83..c574411cea4 100644
--- a/chromium/media/video/video_decode_accelerator.h
+++ b/chromium/media/video/video_decode_accelerator.h
@@ -39,7 +39,7 @@ namespace media {
// implement the backend of PPB_VideoDecoder_Dev.
class MEDIA_EXPORT VideoDecodeAccelerator {
public:
- // Specification of a decoding profile supported by an decoder.
+ // Specification of a decoding profile supported by a decoder.
// |max_resolution| and |min_resolution| are inclusive.
struct MEDIA_EXPORT SupportedProfile {
SupportedProfile();
diff --git a/chromium/media/video/video_encode_accelerator.cc b/chromium/media/video/video_encode_accelerator.cc
index 01ed6203d05..81528abd02a 100644
--- a/chromium/media/video/video_encode_accelerator.cc
+++ b/chromium/media/video/video_encode_accelerator.cc
@@ -41,7 +41,8 @@ VideoEncodeAccelerator::Config::Config(
base::Optional<uint32_t> gop_length,
base::Optional<uint8_t> h264_output_level,
base::Optional<StorageType> storage_type,
- ContentType content_type)
+ ContentType content_type,
+ const std::vector<SpatialLayer>& spatial_layers)
: input_format(input_format),
input_visible_size(input_visible_size),
output_profile(output_profile),
@@ -51,7 +52,8 @@ VideoEncodeAccelerator::Config::Config(
gop_length(gop_length),
h264_output_level(h264_output_level),
storage_type(storage_type),
- content_type(content_type) {}
+ content_type(content_type),
+ spatial_layers(spatial_layers) {}
VideoEncodeAccelerator::Config::~Config() = default;
@@ -74,9 +76,31 @@ std::string VideoEncodeAccelerator::Config::AsHumanReadableString() const {
str += base::StringPrintf(", h264_output_level: %u",
h264_output_level.value());
}
+
+ for (size_t i = 0; i < spatial_layers.size(); ++i) {
+ const auto& sl = spatial_layers[i];
+ str += base::StringPrintf(
+ "\nSL#%zu: width=%d, height=%d, bitrate_bps=%u"
+ ", framerate=%u, max_qp=%u"
+ ", num_of_temporal_layers=%u",
+ i, sl.width, sl.height, sl.bitrate_bps, sl.framerate, sl.max_qp,
+ sl.num_of_temporal_layers);
+ }
return str;
}
+bool VideoEncodeAccelerator::Config::HasTemporalLayer() const {
+ for (const auto& sl : spatial_layers) {
+ if (sl.num_of_temporal_layers > 1u)
+ return true;
+ }
+ return false;
+}
+
+bool VideoEncodeAccelerator::Config::HasSpatialLayer() const {
+ return spatial_layers.size() > 1u;
+}
+
void VideoEncodeAccelerator::Client::NotifyEncoderInfoChange(
const VideoEncoderInfo& info) {
// Do nothing if a client doesn't use the info.
diff --git a/chromium/media/video/video_encode_accelerator.h b/chromium/media/video/video_encode_accelerator.h
index 4ed0582116e..84a31261293 100644
--- a/chromium/media/video/video_encode_accelerator.h
+++ b/chromium/media/video/video_encode_accelerator.h
@@ -110,6 +110,22 @@ class MEDIA_EXPORT VideoEncodeAccelerator {
// kDmabuf if a video frame is referred by dmabuf.
enum class StorageType { kShmem, kDmabuf };
+ struct MEDIA_EXPORT SpatialLayer {
+ // The encoder dimension of the spatial layer.
+ int32_t width = 0;
+ int32_t height = 0;
+ // The bitrate of encoded output stream of the spatial layer in bits per
+ // second.
+ uint32_t bitrate_bps = 0u;
+ uint32_t framerate = 0u;
+ // The recommended maximum qp value of the spatial layer. VEA can ignore
+ // this value.
+ uint8_t max_qp = 0u;
+ // The number of temporal layers of the spatial layer. The detail of
+ // the temporal layer structure is up to VideoEncodeAccelerator.
+ uint8_t num_of_temporal_layers = 0u;
+ };
+
Config();
Config(const Config& config);
@@ -121,12 +137,16 @@ class MEDIA_EXPORT VideoEncodeAccelerator {
base::Optional<uint32_t> gop_length = base::nullopt,
base::Optional<uint8_t> h264_output_level = base::nullopt,
base::Optional<StorageType> storage_type = base::nullopt,
- ContentType content_type = ContentType::kCamera);
+ ContentType content_type = ContentType::kCamera,
+ const std::vector<SpatialLayer>& spatial_layers = {});
~Config();
std::string AsHumanReadableString() const;
+ bool HasTemporalLayer() const;
+ bool HasSpatialLayer() const;
+
// Frame format of input stream (as would be reported by
// VideoFrame::format() for frames passed to Encode()).
VideoPixelFormat input_format;
@@ -168,6 +188,12 @@ class MEDIA_EXPORT VideoEncodeAccelerator {
// bright colors. With this content hint the encoder may choose to optimize
// for the given use case.
ContentType content_type;
+
+ // The configuration for spatial layers. This is not empty if and only if
+ // either spatial or temporal layer encoding is configured. When this is not
+ // empty, VideoEncodeAccelerator should refer the width, height, bitrate and
+ // etc. of |spatial_layers|.
+ std::vector<SpatialLayer> spatial_layers;
};
// Interface for clients that use VideoEncodeAccelerator. These callbacks will
diff --git a/chromium/media/video/video_encoder_info.h b/chromium/media/video/video_encoder_info.h
index 97ca2cd8859..0258afb2134 100644
--- a/chromium/media/video/video_encoder_info.h
+++ b/chromium/media/video/video_encoder_info.h
@@ -9,6 +9,7 @@
#include <string>
#include <vector>
+#include "base/optional.h"
#include "media/base/media_export.h"
#include "ui/gfx/geometry/size.h"
@@ -23,8 +24,10 @@ struct MEDIA_EXPORT ScalingSettings {
ScalingSettings(const ScalingSettings&);
~ScalingSettings();
- int min_qp = 4;
- int max_qp = 157;
+ // Quantization Parameter in ScalingSettings are codec specific.
+ // The range of qp is 0-51 (H264), 0-127 (VP8) and 0-255 (VP9 and AV1).
+ int min_qp = 0;
+ int max_qp = 255;
};
struct MEDIA_EXPORT ResolutionBitrateLimit {
@@ -56,7 +59,7 @@ struct MEDIA_EXPORT VideoEncoderInfo {
bool is_hardware_accelerated = true;
bool supports_simulcast = false;
- ScalingSettings scaling_settings;
+ base::Optional<ScalingSettings> scaling_settings;
std::vector<uint8_t> fps_allocation[kMaxSpatialLayers];
std::vector<ResolutionBitrateLimit> resolution_bitrate_limits;
};
diff --git a/chromium/media/video/vpx_video_encoder.cc b/chromium/media/video/vpx_video_encoder.cc
new file mode 100644
index 00000000000..808d585b919
--- /dev/null
+++ b/chromium/media/video/vpx_video_encoder.cc
@@ -0,0 +1,278 @@
+// 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/video/vpx_video_encoder.h"
+#include "base/strings/stringprintf.h"
+#include "base/time/time.h"
+#include "media/base/bind_to_current_loop.h"
+#include "media/base/video_frame.h"
+#include "third_party/libvpx/source/libvpx/vpx/vp8cx.h"
+
+namespace media {
+
+namespace {
+Status SetUpVpxConfig(const VideoEncoder::Options& opts,
+ vpx_codec_enc_cfg_t* config) {
+ if (opts.width <= 0 || opts.height <= 0)
+ return Status(StatusCode::kEncoderUnsupportedConfig,
+ "Negative width or height values");
+
+ config->g_pass = VPX_RC_ONE_PASS;
+ config->g_lag_in_frames = 0;
+ config->rc_resize_allowed = 0;
+ config->rc_dropframe_thresh = 0; // Don't drop frames
+ config->g_timebase.num = 1;
+ config->g_timebase.den = base::Time::kMicrosecondsPerSecond;
+
+ if (opts.threads.has_value())
+ config->g_threads = opts.threads.value();
+ config->g_w = opts.width;
+ config->g_h = opts.height;
+
+ // Insert keyframes at will with a given max interval
+ if (opts.keyframe_interval.has_value()) {
+ config->kf_mode = VPX_KF_AUTO;
+ config->kf_min_dist = 0;
+ config->kf_max_dist = opts.keyframe_interval.value();
+ }
+
+ if (opts.bitrate.has_value()) {
+ config->rc_end_usage = VPX_CBR;
+ config->rc_target_bitrate = opts.bitrate.value() / 1000;
+ } else {
+ config->rc_end_usage = VPX_VBR;
+ }
+ return Status();
+}
+} // namespace
+
+VpxVideoEncoder::VpxVideoEncoder() = default;
+
+void VpxVideoEncoder::Initialize(VideoCodecProfile profile,
+ const Options& options,
+ OutputCB output_cb,
+ StatusCB done_cb) {
+ done_cb = media::BindToCurrentLoop(std::move(done_cb));
+ if (codec_) {
+ std::move(done_cb).Run(StatusCode::kEncoderInitializeTwice);
+ return;
+ }
+
+ vpx_codec_iface_t* iface = nullptr;
+ if (profile == media::VP8PROFILE_ANY) {
+ iface = vpx_codec_vp8_cx();
+ } else if (profile >= media::VP9PROFILE_PROFILE0 &&
+ profile <= media::VP9PROFILE_PROFILE3) {
+ iface = vpx_codec_vp9_cx();
+ } else {
+ auto status = Status(StatusCode::kEncoderUnsupportedProfile)
+ .WithData("profile", profile);
+ std::move(done_cb).Run(status);
+ return;
+ }
+
+ auto vpx_error = vpx_codec_enc_config_default(iface, &codec_config_, 0);
+ if (vpx_error != VPX_CODEC_OK) {
+ auto status = Status(StatusCode::kEncoderInitializationError,
+ "Failed to get default VPX config.")
+ .WithData("vpx_error", vpx_error);
+ std::move(done_cb).Run(status);
+ return;
+ }
+ switch (profile) {
+ case media::VP9PROFILE_PROFILE1:
+ codec_config_.g_profile = 1;
+ break;
+ case media::VP9PROFILE_PROFILE2:
+ codec_config_.g_profile = 2;
+ break;
+ case media::VP9PROFILE_PROFILE3:
+ codec_config_.g_profile = 3;
+ break;
+ default:
+ codec_config_.g_profile = 0;
+ break;
+ }
+
+ auto status = SetUpVpxConfig(options, &codec_config_);
+ if (!status.is_ok()) {
+ std::move(done_cb).Run(status);
+ return;
+ }
+
+ codec_ = new vpx_codec_ctx_t;
+ vpx_error = vpx_codec_enc_init(codec_, iface, &codec_config_, 0 /* flags */);
+ if (vpx_error != VPX_CODEC_OK) {
+ std::string msg = base::StringPrintf("VPX encoder initialization error: %s",
+ vpx_codec_err_to_string(vpx_error));
+
+ status = Status(StatusCode::kEncoderInitializationError, msg);
+ std::move(done_cb).Run(status);
+ return;
+ }
+
+ // Due to https://bugs.chromium.org/p/webm/issues/detail?id=1684
+ // values less than 5 crash VP9 encoder.
+ vpx_error = vpx_codec_control(codec_, VP8E_SET_CPUUSED, 5);
+ if (vpx_error != VPX_CODEC_OK) {
+ std::string msg =
+ base::StringPrintf("VPX encoder VP8E_SET_CPUUSED error: %s",
+ vpx_codec_err_to_string(vpx_error));
+
+ status = Status(StatusCode::kEncoderInitializationError, msg);
+ std::move(done_cb).Run(status);
+ return;
+ }
+
+ options_ = options;
+ output_cb_ = media::BindToCurrentLoop(std::move(output_cb));
+ std::move(done_cb).Run(Status());
+}
+
+void VpxVideoEncoder::Encode(scoped_refptr<const VideoFrame> frame,
+ bool key_frame,
+ StatusCB done_cb) {
+ Status status;
+ done_cb = media::BindToCurrentLoop(std::move(done_cb));
+ if (!codec_) {
+ std::move(done_cb).Run(StatusCode::kEncoderInitializeNeverCompleted);
+ return;
+ }
+
+ if (!frame) {
+ std::move(done_cb).Run(Status(StatusCode::kEncoderFailedEncode,
+ "No frame provided for encoding."));
+ return;
+ }
+ if (!frame->IsMappable() || frame->format() != media::PIXEL_FORMAT_I420) {
+ status =
+ Status(StatusCode::kEncoderFailedEncode, "Unexpected frame format.")
+ .WithData("IsMappable", frame->IsMappable())
+ .WithData("format", frame->format());
+ std::move(done_cb).Run(std::move(status));
+ return;
+ }
+
+ auto size = frame->visible_rect().size();
+ auto* img_data =
+ const_cast<unsigned char*>(frame->data(media::VideoFrame::kYPlane));
+ vpx_image_t vpx_image;
+ if (&vpx_image != vpx_img_wrap(&vpx_image, VPX_IMG_FMT_I420, size.width(),
+ size.height(), 1 /* align */, img_data)) {
+ std::move(done_cb).Run(StatusCode::kEncoderFailedEncode);
+ return;
+ }
+
+ vpx_image.planes[VPX_PLANE_Y] = const_cast<unsigned char*>(
+ frame->visible_data(media::VideoFrame::kYPlane));
+ vpx_image.planes[VPX_PLANE_U] = const_cast<unsigned char*>(
+ frame->visible_data(media::VideoFrame::kUPlane));
+ vpx_image.planes[VPX_PLANE_V] = const_cast<unsigned char*>(
+ frame->visible_data(media::VideoFrame::kVPlane));
+ vpx_image.stride[VPX_PLANE_Y] = frame->stride(media::VideoFrame::kYPlane);
+ vpx_image.stride[VPX_PLANE_U] = frame->stride(media::VideoFrame::kUPlane);
+ vpx_image.stride[VPX_PLANE_V] = frame->stride(media::VideoFrame::kVPlane);
+
+ auto timestamp = frame->timestamp().InMicroseconds();
+ auto duration = GetFrameDuration(*frame);
+ auto deadline = VPX_DL_REALTIME;
+ vpx_codec_flags_t flags = key_frame ? VPX_EFLAG_FORCE_KF : 0;
+ auto vpx_error = vpx_codec_encode(codec_, &vpx_image, timestamp, duration,
+ flags, deadline);
+
+ if (vpx_error != VPX_CODEC_OK) {
+ std::string msg = base::StringPrintf("VPX encoding error: %s (%s)",
+ vpx_codec_err_to_string(vpx_error),
+ vpx_codec_error_detail(codec_));
+ status = Status(StatusCode::kEncoderFailedEncode, msg)
+ .WithData("vpx_error", vpx_error);
+ std::move(done_cb).Run(std::move(status));
+ return;
+ }
+
+ DrainOutputs();
+ std::move(done_cb).Run(Status());
+}
+
+void VpxVideoEncoder::ChangeOptions(const Options& options, StatusCB done_cb) {
+ done_cb = media::BindToCurrentLoop(std::move(done_cb));
+ if (!codec_) {
+ std::move(done_cb).Run(StatusCode::kEncoderInitializeNeverCompleted);
+ return;
+ }
+
+ vpx_codec_enc_cfg_t new_config = codec_config_;
+ auto status = SetUpVpxConfig(options, &new_config);
+ if (status.is_ok()) {
+ auto vpx_error = vpx_codec_enc_config_set(codec_, &new_config);
+ if (vpx_error == VPX_CODEC_OK) {
+ codec_config_ = new_config;
+ options_ = options;
+ } else {
+ status = Status(StatusCode::kEncoderUnsupportedConfig,
+ "Failed to set new VPX config")
+ .WithData("vpx_error", vpx_error);
+ }
+ }
+
+ std::move(done_cb).Run(std::move(status));
+ return;
+}
+
+uint64_t VpxVideoEncoder::GetFrameDuration(const VideoFrame& frame) {
+ base::TimeDelta result;
+ if (!frame.metadata()->GetTimeDelta(media::VideoFrameMetadata::FRAME_DURATION,
+ &result)) {
+ result = base::TimeDelta::FromSecondsD(1.0 / options_.framerate);
+ }
+ return result.InMicroseconds();
+}
+
+VpxVideoEncoder::~VpxVideoEncoder() {
+ if (!codec_)
+ return;
+
+ auto error = vpx_codec_destroy(codec_);
+ DCHECK_EQ(error, VPX_CODEC_OK);
+ delete codec_;
+}
+
+void VpxVideoEncoder::Flush(StatusCB done_cb) {
+ done_cb = media::BindToCurrentLoop(std::move(done_cb));
+ if (!codec_) {
+ std::move(done_cb).Run(StatusCode::kEncoderInitializeNeverCompleted);
+ return;
+ }
+
+ auto vpx_error = vpx_codec_encode(codec_, nullptr, -1, 0, 0, 0);
+ if (vpx_error != VPX_CODEC_OK) {
+ std::string msg = base::StringPrintf("VPX flushing error: %s (%s)",
+ vpx_codec_err_to_string(vpx_error),
+ vpx_codec_error_detail(codec_));
+ Status status = Status(StatusCode::kEncoderFailedEncode, msg)
+ .WithData("vpx_error", vpx_error);
+ std::move(done_cb).Run(std::move(status));
+ return;
+ }
+ DrainOutputs();
+ std::move(done_cb).Run(Status());
+}
+
+void VpxVideoEncoder::DrainOutputs() {
+ vpx_codec_iter_t iter = nullptr;
+ const vpx_codec_cx_pkt_t* pkt = nullptr;
+ while ((pkt = vpx_codec_get_cx_data(codec_, &iter))) {
+ if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) {
+ VideoEncoderOutput result;
+ result.key_frame = (pkt->data.frame.flags & VPX_FRAME_IS_KEY) != 0;
+ result.timestamp = base::TimeDelta::FromMicroseconds(pkt->data.frame.pts);
+ result.size = pkt->data.frame.sz;
+ result.data.reset(new uint8_t[result.size]);
+ memcpy(result.data.get(), pkt->data.frame.buf, result.size);
+ output_cb_.Run(std::move(result));
+ }
+ }
+}
+
+} // namespace media
diff --git a/chromium/media/video/vpx_video_encoder.h b/chromium/media/video/vpx_video_encoder.h
new file mode 100644
index 00000000000..4b6c9ff763e
--- /dev/null
+++ b/chromium/media/video/vpx_video_encoder.h
@@ -0,0 +1,45 @@
+// 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_VIDEO_VPX_VIDEO_ENCODER_H_
+#define MEDIA_VIDEO_VPX_VIDEO_ENCODER_H_
+
+#include <memory>
+
+#include "base/callback_forward.h"
+#include "media/base/media_export.h"
+#include "media/base/video_encoder.h"
+#include "third_party/libvpx/source/libvpx/vpx/vpx_encoder.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace media {
+
+class MEDIA_EXPORT VpxVideoEncoder : public VideoEncoder {
+ public:
+ VpxVideoEncoder();
+ ~VpxVideoEncoder() override;
+
+ // VideoDecoder implementation.
+ void Initialize(VideoCodecProfile profile,
+ const Options& options,
+ OutputCB output_cb,
+ StatusCB done_cb) override;
+ void Encode(scoped_refptr<const VideoFrame> frame,
+ bool key_frame,
+ StatusCB done_cb) override;
+ void ChangeOptions(const Options& options, StatusCB done_cb) override;
+ void Flush(StatusCB done_cb) override;
+
+ private:
+ uint64_t GetFrameDuration(const VideoFrame& frame);
+ void DrainOutputs();
+
+ vpx_codec_ctx_t* codec_ = nullptr;
+ vpx_codec_enc_cfg_t codec_config_ = {};
+ Options options_;
+ OutputCB output_cb_;
+};
+
+} // namespace media
+#endif // MEDIA_VIDEO_VPX_VIDEO_ENCODER_H_ \ No newline at end of file
diff --git a/chromium/media/webcodecs/BUILD.gn b/chromium/media/webcodecs/BUILD.gn
new file mode 100644
index 00000000000..325aa27053d
--- /dev/null
+++ b/chromium/media/webcodecs/BUILD.gn
@@ -0,0 +1,39 @@
+# 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.
+
+import("//testing/test.gni")
+
+source_set("webcodecs") {
+ # Do not expand the visibility here without double-checking with OWNERS, this
+ # is a roll-up target which is part of the //media component. Most other DEPs
+ # should be using //media and not directly DEP this roll-up target.
+ visibility = [ "//media" ]
+
+ sources = [
+ "wc_decoder_selector.cc",
+ "wc_decoder_selector.h",
+ ]
+
+ public_deps = [
+ "//base",
+ "//media/base",
+ "//media/filters",
+ ]
+
+ deps = []
+
+ configs += [ "//media:subcomponent_config" ]
+}
+
+source_set("unit_tests") {
+ testonly = true
+ sources = [ "wc_decoder_selector_unittest.cc" ]
+
+ deps = [
+ "//base/test:test_support",
+ "//media:test_support",
+ "//testing/gmock",
+ "//testing/gtest",
+ ]
+}
diff --git a/chromium/media/webcodecs/wc_decoder_selector.cc b/chromium/media/webcodecs/wc_decoder_selector.cc
new file mode 100644
index 00000000000..73b927dc4cc
--- /dev/null
+++ b/chromium/media/webcodecs/wc_decoder_selector.cc
@@ -0,0 +1,142 @@
+// 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/webcodecs/wc_decoder_selector.h"
+
+#include "base/bind.h"
+#include "base/check_op.h"
+#include "base/notreached.h"
+#include "base/single_thread_task_runner.h"
+#include "media/base/channel_layout.h"
+#include "media/base/demuxer_stream.h"
+#include "media/filters/decrypting_demuxer_stream.h"
+
+namespace media {
+
+// Demuxing isn't part of WebCodecs. This shim allows us to reuse decoder
+// selection logic from <video>.
+// TODO(chcunningham): Maybe refactor DecoderSelector to separate dependency on
+// DemuxerStream. DecoderSelection doesn't conceptually require a Demuxer. The
+// tough part is re-working DecryptingDemuxerStream.
+template <DemuxerStream::Type StreamType>
+class ShimDemuxerStream : public DemuxerStream {
+ public:
+ using DecoderConfigType =
+ typename DecoderStreamTraits<StreamType>::DecoderConfigType;
+
+ ~ShimDemuxerStream() override = default;
+
+ void Read(ReadCB read_cb) override { NOTREACHED(); }
+ bool IsReadPending() const override {
+ NOTREACHED();
+ return false;
+ }
+
+ void Configure(DecoderConfigType config);
+
+ AudioDecoderConfig audio_decoder_config() override {
+ DCHECK_EQ(type(), DemuxerStream::AUDIO);
+ return audio_decoder_config_;
+ }
+
+ VideoDecoderConfig video_decoder_config() override {
+ DCHECK_EQ(type(), DemuxerStream::VIDEO);
+ return video_decoder_config_;
+ }
+
+ Type type() const override { return stream_type; }
+
+ bool SupportsConfigChanges() override {
+ NOTREACHED();
+ return true;
+ }
+
+ private:
+ static const DemuxerStream::Type stream_type = StreamType;
+
+ AudioDecoderConfig audio_decoder_config_;
+ VideoDecoderConfig video_decoder_config_;
+};
+
+template <>
+void ShimDemuxerStream<DemuxerStream::AUDIO>::Configure(
+ DecoderConfigType config) {
+ audio_decoder_config_ = config;
+}
+
+template <>
+void ShimDemuxerStream<DemuxerStream::VIDEO>::Configure(
+ DecoderConfigType config) {
+ video_decoder_config_ = config;
+}
+
+template <DemuxerStream::Type StreamType>
+WebCodecsDecoderSelector<StreamType>::WebCodecsDecoderSelector(
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+ CreateDecodersCB create_decoders_cb,
+ typename Decoder::OutputCB output_cb)
+ : impl_(std::move(task_runner),
+ std::move(create_decoders_cb),
+ &null_media_log_),
+ demuxer_stream_(new ShimDemuxerStream<StreamType>()),
+ stream_traits_(CreateStreamTraits()),
+ output_cb_(output_cb) {
+ impl_.Initialize(stream_traits_.get(), demuxer_stream_.get(),
+ nullptr /*CdmContext*/, WaitingCB());
+}
+
+template <DemuxerStream::Type StreamType>
+WebCodecsDecoderSelector<StreamType>::~WebCodecsDecoderSelector() {}
+
+template <DemuxerStream::Type StreamType>
+void WebCodecsDecoderSelector<StreamType>::SelectDecoder(
+ const DecoderConfig& config,
+ SelectDecoderCB select_decoder_cb) {
+ // |impl_| will internally use this the |config| from our ShimDemuxerStream.
+ demuxer_stream_->Configure(config);
+
+ // |impl_| uses a WeakFactory for its SelectDecoderCB, so we're safe to use
+ // Unretained here.
+ impl_.SelectDecoder(
+ base::BindOnce(&WebCodecsDecoderSelector<StreamType>::OnDecoderSelected,
+ base::Unretained(this), std::move(select_decoder_cb)),
+ output_cb_);
+}
+
+template <>
+std::unique_ptr<WebCodecsAudioDecoderSelector::StreamTraits>
+WebCodecsDecoderSelector<DemuxerStream::AUDIO>::CreateStreamTraits() {
+ // TODO(chcunningham): Consider plumbing real hw channel layout.
+ return std::make_unique<WebCodecsDecoderSelector::StreamTraits>(
+ &null_media_log_, CHANNEL_LAYOUT_NONE);
+}
+
+template <>
+std::unique_ptr<WebCodecsVideoDecoderSelector::StreamTraits>
+WebCodecsDecoderSelector<DemuxerStream::VIDEO>::CreateStreamTraits() {
+ return std::make_unique<WebCodecsDecoderSelector::StreamTraits>(
+ &null_media_log_);
+}
+
+template <DemuxerStream::Type StreamType>
+void WebCodecsDecoderSelector<StreamType>::OnDecoderSelected(
+ SelectDecoderCB select_decoder_cb,
+ std::unique_ptr<Decoder> decoder,
+ std::unique_ptr<DecryptingDemuxerStream> decrypting_demuxer_stream) {
+ DCHECK(!decrypting_demuxer_stream);
+
+ // We immediately finalize decoder selection. From a spec POV we strongly
+ // prefer to avoid replicating our internal design of having to wait for the
+ // first frame to arrive before we consider configuration successful.
+ // TODO(chcunningham): Measure first frame decode failures and find other ways
+ // to solve (or minimize) the problem.
+ impl_.FinalizeDecoderSelection();
+
+ std::move(select_decoder_cb).Run(std::move(decoder));
+}
+
+template class WebCodecsDecoderSelector<DemuxerStream::VIDEO>;
+template class WebCodecsDecoderSelector<DemuxerStream::AUDIO>;
+
+} // namespace media
diff --git a/chromium/media/webcodecs/wc_decoder_selector.h b/chromium/media/webcodecs/wc_decoder_selector.h
new file mode 100644
index 00000000000..207350f1a94
--- /dev/null
+++ b/chromium/media/webcodecs/wc_decoder_selector.h
@@ -0,0 +1,83 @@
+// 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_WEBCODECS_WC_DECODER_SELECTOR_H_
+#define MEDIA_WEBCODECS_WC_DECODER_SELECTOR_H_
+
+#include <memory>
+
+#include "media/base/demuxer_stream.h"
+#include "media/base/media_export.h"
+#include "media/base/media_util.h"
+#include "media/filters/decoder_selector.h"
+#include "media/filters/decoder_stream_traits.h"
+
+namespace media {
+
+template <DemuxerStream::Type StreamType>
+class ShimDemuxerStream;
+
+template <DemuxerStream::Type StreamType>
+class MEDIA_EXPORT WebCodecsDecoderSelector {
+ public:
+ typedef DecoderStreamTraits<StreamType> StreamTraits;
+ typedef typename StreamTraits::DecoderType Decoder;
+ typedef typename StreamTraits::DecoderConfigType DecoderConfig;
+
+ // Callback to create a list of decoders to select from.
+ using CreateDecodersCB =
+ base::RepeatingCallback<std::vector<std::unique_ptr<Decoder>>()>;
+
+ // Emits the result of a single call to SelectDecoder(). Parameter is
+ // the initialized Decoder. nullptr if selection failed. The caller owns the
+ // Decoder.
+ using SelectDecoderCB = base::OnceCallback<void(std::unique_ptr<Decoder>)>;
+
+ WebCodecsDecoderSelector(
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+ CreateDecodersCB create_decoders_cb,
+ typename Decoder::OutputCB output_cb);
+
+ // Aborts any pending decoder selection.
+ ~WebCodecsDecoderSelector();
+
+ // Selects and initializes a decoder using |config|. Decoder will
+ // be returned via |select_decoder_cb| posted to |task_runner_|. Subsequent
+ // calls will again select from the full list of decoders.
+ void SelectDecoder(const DecoderConfig& config,
+ SelectDecoderCB select_decoder_cb);
+
+ private:
+ // Helper to create |stream_traits_|.
+ std::unique_ptr<StreamTraits> CreateStreamTraits();
+
+ // Proxy SelectDecoderCB from impl_ to our |select_decoder_cb|.
+ void OnDecoderSelected(SelectDecoderCB select_decoder_cb,
+ std::unique_ptr<Decoder> decoder,
+ std::unique_ptr<DecryptingDemuxerStream>);
+
+ // Implements heavy lifting for decoder selection.
+ DecoderSelector<StreamType> impl_;
+
+ // Shim to satisfy dependencies of |impl_|. Provides DecoderConfig to |impl_|.
+ std::unique_ptr<ShimDemuxerStream<StreamType>> demuxer_stream_;
+
+ // Helper to unify API for configuring audio/video decoders.
+ std::unique_ptr<StreamTraits> stream_traits_;
+
+ // Repeating callback for decoder outputs.
+ typename Decoder::OutputCB output_cb_;
+
+ // TODO(chcunningham): Route MEDIA_LOG for WebCodecs.
+ NullMediaLog null_media_log_;
+};
+
+typedef WebCodecsDecoderSelector<DemuxerStream::VIDEO>
+ WebCodecsVideoDecoderSelector;
+typedef WebCodecsDecoderSelector<DemuxerStream::AUDIO>
+ WebCodecsAudioDecoderSelector;
+
+} // namespace media
+
+#endif // MEDIA_WEBCODECS_WC_DECODER_SELECTOR_H_
diff --git a/chromium/media/webcodecs/wc_decoder_selector_unittest.cc b/chromium/media/webcodecs/wc_decoder_selector_unittest.cc
new file mode 100644
index 00000000000..a14258468ee
--- /dev/null
+++ b/chromium/media/webcodecs/wc_decoder_selector_unittest.cc
@@ -0,0 +1,240 @@
+// 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 <vector>
+
+#include "base/test/task_environment.h"
+#include "media/base/demuxer_stream.h"
+#include "media/base/media_util.h"
+#include "media/base/mock_filters.h"
+#include "media/base/status.h"
+#include "media/base/test_helpers.h"
+#include "media/base/video_decoder.h"
+#include "media/filters/decoder_stream.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#include "media/webcodecs/wc_decoder_selector.h"
+
+using ::testing::_;
+using ::testing::IsNull;
+using ::testing::StrictMock;
+
+namespace media {
+
+namespace {
+
+enum DecoderCapability {
+ kFail,
+ kSucceed,
+};
+
+const char kNoDecoder[] = "";
+const char kDecoder1[] = "Decoder1";
+const char kDecoder2[] = "Decoder2";
+
+// Specializations for the AUDIO version of the test.
+class AudioDecoderSelectorTestParam {
+ public:
+ static constexpr DemuxerStream::Type kStreamType = DemuxerStream::AUDIO;
+
+ using DecoderSelector = WebCodecsDecoderSelector<DemuxerStream::AUDIO>;
+ using MockDecoder = MockAudioDecoder;
+ using Output = AudioBuffer;
+
+ static AudioDecoderConfig CreateConfig() { return TestAudioConfig::Normal(); }
+
+ // Create a config that won't match the return of CreateConfig().
+ static AudioDecoderConfig CreateAlternateConfig() {
+ return TestAudioConfig::NormalEncrypted();
+ }
+
+ // Decoder::Initialize() takes different parameters depending on the type.
+ static void ExpectInitialize(MockDecoder* decoder,
+ DecoderCapability capability,
+ AudioDecoderConfig expected_config) {
+ EXPECT_CALL(*decoder, Initialize_(_, _, _, _, _))
+ .WillRepeatedly([capability, expected_config](
+ const AudioDecoderConfig& config, CdmContext*,
+ AudioDecoder::InitCB& init_cb,
+ const AudioDecoder::OutputCB&, const WaitingCB&) {
+ EXPECT_TRUE(config.Matches(expected_config));
+ std::move(init_cb).Run(capability == kSucceed
+ ? OkStatus()
+ : StatusCode::kCodeOnlyForTesting);
+ });
+ }
+};
+
+// Specializations for the VIDEO version of the test.
+class VideoDecoderSelectorTestParam {
+ public:
+ static constexpr DemuxerStream::Type kStreamType = DemuxerStream::VIDEO;
+
+ using DecoderSelector = WebCodecsDecoderSelector<DemuxerStream::VIDEO>;
+ using MockDecoder = MockVideoDecoder;
+ using Output = VideoFrame;
+
+ static VideoDecoderConfig CreateConfig() { return TestVideoConfig::Normal(); }
+
+ // Create a config that won't match the return of CreateConfig().
+ static VideoDecoderConfig CreateAlternateConfig() {
+ return TestVideoConfig::LargeEncrypted();
+ }
+
+ static void ExpectInitialize(MockDecoder* decoder,
+ DecoderCapability capability,
+ VideoDecoderConfig expected_config) {
+ EXPECT_CALL(*decoder, Initialize_(_, _, _, _, _, _))
+ .WillRepeatedly([capability, expected_config](
+ const VideoDecoderConfig& config, bool low_delay,
+ CdmContext*, VideoDecoder::InitCB& init_cb,
+ const VideoDecoder::OutputCB&, const WaitingCB&) {
+ EXPECT_TRUE(config.Matches(expected_config));
+ std::move(init_cb).Run(capability == kSucceed
+ ? OkStatus()
+ : StatusCode::kCodeOnlyForTesting);
+ });
+ }
+};
+
+// Allocate storage for the member variables.
+constexpr DemuxerStream::Type AudioDecoderSelectorTestParam::kStreamType;
+constexpr DemuxerStream::Type VideoDecoderSelectorTestParam::kStreamType;
+
+} // namespace
+
+// Note: The parameter is called TypeParam in the test cases regardless of what
+// we call it here. It's been named the same for convenience.
+// Note: The test fixtures inherit from this class. Inside the test cases the
+// test fixture class is called TestFixture.
+template <typename TypeParam>
+class WebCodecsDecoderSelectorTest : public ::testing::Test {
+ public:
+ // Convenience aliases.
+ using Self = WebCodecsDecoderSelectorTest<TypeParam>;
+ using Decoder = typename TypeParam::DecoderSelector::Decoder;
+ using DecoderConfig = typename TypeParam::DecoderSelector::DecoderConfig;
+ using MockDecoder = typename TypeParam::MockDecoder;
+ using Output = typename TypeParam::Output;
+
+ WebCodecsDecoderSelectorTest() { CreateDecoderSelector(); }
+
+ void OnOutput(scoped_refptr<Output> output) { NOTREACHED(); }
+
+ MOCK_METHOD1_T(OnDecoderSelected, void(std::string));
+
+ void OnDecoderSelectedThunk(std::unique_ptr<Decoder> decoder) {
+ // Report only the name of the decoder, since that's what the tests care
+ // about. The decoder will be destructed immediately.
+ OnDecoderSelected(decoder ? decoder->GetDisplayName() : kNoDecoder);
+ }
+
+ void AddMockDecoder(const std::string& decoder_name,
+ DecoderCapability capability) {
+ // Actual decoders are created in CreateDecoders(), which may be called
+ // multiple times by the DecoderSelector.
+ mock_decoders_to_create_.emplace_back(decoder_name, capability);
+ }
+
+ std::vector<std::unique_ptr<Decoder>> CreateDecoders() {
+ std::vector<std::unique_ptr<Decoder>> decoders;
+
+ for (const auto& info : mock_decoders_to_create_) {
+ std::unique_ptr<StrictMock<MockDecoder>> decoder =
+ std::make_unique<StrictMock<MockDecoder>>(info.first);
+ TypeParam::ExpectInitialize(decoder.get(), info.second,
+ last_set_decoder_config_);
+ decoders.push_back(std::move(decoder));
+ }
+
+ return decoders;
+ }
+
+ void CreateDecoderSelector() {
+ decoder_selector_ =
+ std::make_unique<WebCodecsDecoderSelector<TypeParam::kStreamType>>(
+ task_environment_.GetMainThreadTaskRunner(),
+ base::BindRepeating(&Self::CreateDecoders, base::Unretained(this)),
+ base::BindRepeating(&Self::OnOutput, base::Unretained(this)));
+ }
+
+ void SelectDecoder(DecoderConfig config = TypeParam::CreateConfig()) {
+ last_set_decoder_config_ = config;
+ decoder_selector_->SelectDecoder(
+ config,
+ base::BindOnce(&Self::OnDecoderSelectedThunk, base::Unretained(this)));
+ RunUntilIdle();
+ }
+
+ void RunUntilIdle() { task_environment_.RunUntilIdle(); }
+
+ base::test::TaskEnvironment task_environment_;
+ NullMediaLog media_log_;
+
+ DecoderConfig last_set_decoder_config_;
+
+ std::unique_ptr<WebCodecsDecoderSelector<TypeParam::kStreamType>>
+ decoder_selector_;
+
+ std::vector<std::pair<std::string, DecoderCapability>>
+ mock_decoders_to_create_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(WebCodecsDecoderSelectorTest);
+};
+
+using WebCodecsDecoderSelectorTestParams =
+ ::testing::Types<AudioDecoderSelectorTestParam,
+ VideoDecoderSelectorTestParam>;
+TYPED_TEST_SUITE(WebCodecsDecoderSelectorTest,
+ WebCodecsDecoderSelectorTestParams);
+
+TYPED_TEST(WebCodecsDecoderSelectorTest, NoDecoders) {
+ EXPECT_CALL(*this, OnDecoderSelected(kNoDecoder));
+ this->SelectDecoder();
+}
+
+TYPED_TEST(WebCodecsDecoderSelectorTest, OneDecoder) {
+ this->AddMockDecoder(kDecoder1, kSucceed);
+
+ EXPECT_CALL(*this, OnDecoderSelected(kDecoder1));
+ this->SelectDecoder();
+}
+
+TYPED_TEST(WebCodecsDecoderSelectorTest, TwoDecoders) {
+ this->AddMockDecoder(kDecoder1, kFail);
+ this->AddMockDecoder(kDecoder2, kSucceed);
+
+ EXPECT_CALL(*this, OnDecoderSelected(kDecoder2));
+ this->SelectDecoder();
+}
+
+TYPED_TEST(WebCodecsDecoderSelectorTest, TwoDecoders_SelectAgain) {
+ this->AddMockDecoder(kDecoder1, kSucceed);
+ this->AddMockDecoder(kDecoder2, kSucceed);
+
+ EXPECT_CALL(*this, OnDecoderSelected(kDecoder1));
+ this->SelectDecoder();
+
+ // Selecting again should give (a new instance of) the same decoder.
+ EXPECT_CALL(*this, OnDecoderSelected(kDecoder1));
+ this->SelectDecoder();
+}
+
+TYPED_TEST(WebCodecsDecoderSelectorTest, TwoDecoders_NewConfigSelectAgain) {
+ this->AddMockDecoder(kDecoder1, kSucceed);
+ this->AddMockDecoder(kDecoder2, kSucceed);
+
+ EXPECT_CALL(*this, OnDecoderSelected(kDecoder1));
+ this->SelectDecoder(TypeParam::CreateConfig());
+
+ // Selecting again should give (a new instance of) the same decoder.
+ EXPECT_CALL(*this, OnDecoderSelected(kDecoder1));
+ // Select again with a different config. Expected config verified during
+ // CreateDecoders() the SelectDecoder() call.
+ this->SelectDecoder(TypeParam::CreateAlternateConfig());
+}
+
+} // namespace media
diff --git a/chromium/media/webrtc/BUILD.gn b/chromium/media/webrtc/BUILD.gn
index 8ba246a9f12..61bec7b7dd8 100644
--- a/chromium/media/webrtc/BUILD.gn
+++ b/chromium/media/webrtc/BUILD.gn
@@ -3,15 +3,8 @@
# found in the LICENSE file.
import("//media/media_options.gni")
-import("//media/webrtc/audio_processing.gni")
import("//third_party/webrtc/webrtc.gni")
-config("audio_processing_build_flag") {
- if (audio_processing_in_audio_service_supported) {
- defines = [ "AUDIO_PROCESSING_IN_AUDIO_SERVICE" ]
- }
-}
-
component("webrtc") {
output_name = "media_webrtc"
@@ -31,33 +24,8 @@ component("webrtc") {
"//media:shared_memory_support",
"//third_party/webrtc_overrides:webrtc_component",
]
-
- if (audio_processing_in_audio_service_supported) {
- # Only build this on platforms where it's supported and used.
- sources += [
- "audio_processor.cc",
- "audio_processor.h",
- "audio_processor_controls.h",
- ]
-
- deps += [ "//media" ]
-
- public_configs = [ ":audio_processing_build_flag" ]
- }
}
source_set("unit_tests") {
testonly = true
- if (audio_processing_in_audio_service_supported) {
- deps = [
- "//base",
- "//base/test:test_support",
- "//media:test_support",
- "//media/webrtc",
- "//testing/gmock",
- "//testing/gtest",
- "//third_party/webrtc_overrides:webrtc_component",
- ]
- sources = [ "audio_processor_unittest.cc" ]
- }
}
diff --git a/chromium/media/webrtc/audio_processing.gni b/chromium/media/webrtc/audio_processing.gni
deleted file mode 100644
index 8645de00541..00000000000
--- a/chromium/media/webrtc/audio_processing.gni
+++ /dev/null
@@ -1,4 +0,0 @@
-declare_args() {
- # Note: the audio service must be sandboxed for us to use the APM there.
- audio_processing_in_audio_service_supported = is_linux || is_win || is_mac
-}
diff --git a/chromium/media/webrtc/audio_processor.cc b/chromium/media/webrtc/audio_processor.cc
deleted file mode 100644
index 0e7b791aa5b..00000000000
--- a/chromium/media/webrtc/audio_processor.cc
+++ /dev/null
@@ -1,361 +0,0 @@
-// Copyright 2018 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/webrtc/audio_processor.h"
-
-#include <array>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/command_line.h"
-#include "base/feature_list.h"
-#include "base/files/file_util.h"
-#include "base/memory/ptr_util.h"
-#include "base/metrics/field_trial.h"
-#include "base/metrics/field_trial_params.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/task/post_task.h"
-#include "base/task/task_traits.h"
-#include "base/task/thread_pool.h"
-#include "media/base/limits.h"
-#include "media/webrtc/helpers.h"
-#include "media/webrtc/webrtc_switches.h"
-#include "third_party/webrtc/api/audio/echo_canceller3_factory.h"
-#include "third_party/webrtc/modules/audio_processing/aec_dump/aec_dump_factory.h"
-#include "third_party/webrtc_overrides/task_queue_factory.h"
-
-namespace media {
-
-namespace {
-
-bool UseMultiChannelCaptureProcessing() {
- return base::FeatureList::IsEnabled(
- features::kWebRtcEnableCaptureMultiChannelApm);
-}
-
-constexpr int kBuffersPerSecond = 100; // 10 ms per buffer.
-
-} // namespace
-
-AudioProcessor::ProcessingResult::ProcessingResult(
- const AudioBus& audio,
- base::Optional<double> new_volume)
- : audio(audio), new_volume(new_volume) {}
-AudioProcessor::ProcessingResult::ProcessingResult(const ProcessingResult& b) =
- default;
-AudioProcessor::ProcessingResult::~ProcessingResult() = default;
-
-AudioProcessor::AudioProcessor(const AudioParameters& audio_parameters,
- const AudioProcessingSettings& settings)
- : audio_parameters_(audio_parameters),
- settings_(settings),
- output_bus_(AudioBus::Create(audio_parameters_)),
- audio_delay_stats_reporter_(kBuffersPerSecond),
- use_capture_multi_channel_processing_(
- UseMultiChannelCaptureProcessing()) {
- DCHECK(audio_parameters.IsValid());
- DCHECK_EQ(audio_parameters_.GetBufferDuration(),
- base::TimeDelta::FromMilliseconds(10));
- InitializeAPM();
- output_ptrs_.reserve(audio_parameters_.channels());
- for (int i = 0; i < audio_parameters_.channels(); ++i) {
- output_ptrs_.push_back(output_bus_->channel(i));
- }
-}
-
-AudioProcessor::~AudioProcessor() {
- StopEchoCancellationDump();
-}
-
-// Process the audio from source and return a pointer to the processed data.
-AudioProcessor::ProcessingResult AudioProcessor::ProcessCapture(
- const AudioBus& source,
- base::TimeTicks capture_time,
- double volume,
- bool key_pressed) {
- base::Optional<double> new_volume;
-
- if (audio_processing_) {
- UpdateDelayEstimate(capture_time);
- UpdateAnalogLevel(volume);
- audio_processing_->set_stream_key_pressed(key_pressed);
-
- // Writes to |output_bus_|.
- FeedDataToAPM(source);
-
- UpdateTypingDetected(key_pressed);
- new_volume = GetNewVolumeFromAGC(volume);
- } else {
- source.CopyTo(output_bus_.get());
- }
-
- if (settings_.stereo_mirroring &&
- audio_parameters_.channel_layout() == CHANNEL_LAYOUT_STEREO) {
- output_bus_->SwapChannels(0, 1);
- }
-
- return {*output_bus_, new_volume};
-}
-
-void AudioProcessor::AnalyzePlayout(const AudioBus& audio,
- const AudioParameters& parameters,
- base::TimeTicks playout_time) {
- if (!audio_processing_)
- return;
-
- render_delay_ = playout_time - base::TimeTicks::Now();
-
- DCHECK_GE(parameters.channels(), 1);
- DCHECK_LE(parameters.channels(), audio.channels());
- DCHECK_LE(parameters.channels(), media::limits::kMaxChannels);
- webrtc::StreamConfig input_stream_config = CreateStreamConfig(parameters);
- // If the input audio appears to contain upmixed mono audio, then APM is only
- // given the left channel. This reduces computational complexity and improves
- // convergence of audio processing algorithms.
- // TODO(crbug.com/1023337): Ensure correct channel count in input audio bus.
- assume_upmixed_mono_playout_ =
- assume_upmixed_mono_playout_ && LeftAndRightChannelsAreSymmetric(audio);
- if (assume_upmixed_mono_playout_) {
- input_stream_config.set_num_channels(1);
- }
-
- std::array<const float*, media::limits::kMaxChannels> input_ptrs;
- for (int i = 0; i < static_cast<int>(input_stream_config.num_channels());
- ++i) {
- input_ptrs[i] = audio.channel(i);
- }
-
- const int apm_error = audio_processing_->AnalyzeReverseStream(
- input_ptrs.data(), input_stream_config);
-
- DCHECK_EQ(apm_error, webrtc::AudioProcessing::kNoError);
-}
-
-void AudioProcessor::GetStats(GetStatsCB callback) {
- webrtc::AudioProcessorInterface::AudioProcessorStatistics out = {};
- if (audio_processing_) {
- out.typing_noise_detected = typing_detected_;
- out.apm_statistics = audio_processing_->GetStatistics(has_reverse_stream_);
- }
- std::move(callback).Run(out);
-}
-
-void AudioProcessor::StartEchoCancellationDump(base::File file) {
- if (!audio_processing_) {
- // The destructor of File is blocking. Post it to a task runner to avoid
- // blocking the main thread.
- base::ThreadPool::PostTask(
- FROM_HERE, {base::TaskPriority::LOWEST, base::MayBlock()},
- base::BindOnce([](base::File) {}, std::move(file)));
- return;
- }
-
- // Here tasks will be posted on the |worker_queue_|. It must be kept alive
- // until StopEchoCancellationDump is called or the webrtc::AudioProcessing
- // instance is destroyed.
-
- DCHECK(file.IsValid());
-
- if (!worker_queue_) {
- worker_queue_ = std::make_unique<rtc::TaskQueue>(
- CreateWebRtcTaskQueue(rtc::TaskQueue::Priority::LOW));
- }
- auto aec_dump = webrtc::AecDumpFactory::Create(
- FileToFILE(std::move(file), "wb"), -1 /* max_log_size_bytes */,
- worker_queue_.get());
- if (!aec_dump) {
- // AecDumpFactory::Create takes ownership of stream even if it fails, so we
- // don't need to close it.
- LOG(ERROR) << "Failed to start AEC debug recording";
- return;
- }
- audio_processing_->AttachAecDump(std::move(aec_dump));
-}
-
-void AudioProcessor::StopEchoCancellationDump() {
- if (audio_processing_)
- audio_processing_->DetachAecDump();
- // Note that deleting an rtc::TaskQueue has to be done from the
- // thread that created it.
- worker_queue_.reset();
-}
-
-void AudioProcessor::InitializeAPM() {
- // Most features must have some configuration applied before constructing the
- // APM, and some after; the initialization is divided into "part 1" and "part
- // 2" in those cases.
-
- // If we use nothing but, possibly, audio mirroring, don't initialize the APM.
- if (settings_.echo_cancellation != EchoCancellationType::kAec3 &&
- settings_.noise_suppression == NoiseSuppressionType::kDisabled &&
- settings_.automatic_gain_control == AutomaticGainControlType::kDisabled &&
- !settings_.high_pass_filter && !settings_.typing_detection) {
- return;
- }
-
- webrtc::Config ap_config; // Note: ap_config.Set(x) transfers ownership of x.
- webrtc::AudioProcessingBuilder ap_builder;
-
- // AEC setup part 1.
-
- // Echo cancellation is configured both before and after AudioProcessing
- // construction, but before Initialize.
- if (settings_.echo_cancellation == EchoCancellationType::kAec3) {
- ap_builder.SetEchoControlFactory(
- std::make_unique<webrtc::EchoCanceller3Factory>());
- }
-
- // AGC setup part 1.
- if (settings_.automatic_gain_control ==
- AutomaticGainControlType::kExperimental ||
- settings_.automatic_gain_control ==
- AutomaticGainControlType::kHybridExperimental) {
- std::string min_volume_str(
- base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
- switches::kAgcStartupMinVolume));
- int startup_min_volume;
- // |startup_min_volume| set to 0 in case of failure/empty string, which is
- // the default.
- base::StringToInt(min_volume_str, &startup_min_volume);
- auto* experimental_agc =
- new webrtc::ExperimentalAgc(true, startup_min_volume);
- experimental_agc->digital_adaptive_disabled =
- settings_.automatic_gain_control ==
- AutomaticGainControlType::kHybridExperimental;
- ap_config.Set<webrtc::ExperimentalAgc>(experimental_agc);
- } else {
- ap_config.Set<webrtc::ExperimentalAgc>(new webrtc::ExperimentalAgc(false));
- }
-
- // Noise suppression setup part 1.
- ap_config.Set<webrtc::ExperimentalNs>(new webrtc::ExperimentalNs(
- settings_.noise_suppression == NoiseSuppressionType::kExperimental));
-
- // Audio processing module construction.
- audio_processing_ = base::WrapUnique(ap_builder.Create(ap_config));
-
- webrtc::AudioProcessing::Config apm_config = audio_processing_->GetConfig();
-
- // APM audio pipeline setup.
- apm_config.pipeline.multi_channel_render = true;
- apm_config.pipeline.multi_channel_capture =
- use_capture_multi_channel_processing_;
-
- // Typing detection setup.
- if (settings_.typing_detection) {
- typing_detector_ = std::make_unique<webrtc::TypingDetection>();
- // Configure the update period to 1s (100 * 10ms) in the typing detector.
- typing_detector_->SetParameters(0, 0, 0, 0, 0, 100);
-
- apm_config.voice_detection.enabled = true;
- }
-
- // AEC setup part 2.
- apm_config.echo_canceller.enabled =
- settings_.echo_cancellation == EchoCancellationType::kAec3;
- apm_config.echo_canceller.mobile_mode = false;
-
- // High-pass filter setup.
- apm_config.high_pass_filter.enabled = settings_.high_pass_filter;
-
- // Noise suppression setup part 2.
- apm_config.noise_suppression.enabled =
- settings_.noise_suppression != NoiseSuppressionType::kDisabled;
- apm_config.noise_suppression.level =
- webrtc::AudioProcessing::Config::NoiseSuppression::kHigh;
-
- // AGC setup part 2.
- apm_config.gain_controller1.enabled =
- settings_.automatic_gain_control != AutomaticGainControlType::kDisabled;
- apm_config.gain_controller1.mode =
- webrtc::AudioProcessing::Config::GainController1::kAdaptiveAnalog;
- if (settings_.automatic_gain_control ==
- AutomaticGainControlType::kExperimental ||
- settings_.automatic_gain_control ==
- AutomaticGainControlType::kHybridExperimental) {
- apm_config.gain_controller2.enabled =
- settings_.automatic_gain_control ==
- AutomaticGainControlType::kHybridExperimental;
- apm_config.gain_controller2.fixed_digital.gain_db = 0.f;
- apm_config.gain_controller2.adaptive_digital.enabled = true;
- const bool use_peaks_not_rms = base::GetFieldTrialParamByFeatureAsBool(
- features::kWebRtcHybridAgc, "use_peaks_not_rms", false);
- using Shortcut =
- webrtc::AudioProcessing::Config::GainController2::LevelEstimator;
- apm_config.gain_controller2.adaptive_digital.level_estimator =
- use_peaks_not_rms ? Shortcut::kPeak : Shortcut::kRms;
-
- const int saturation_margin = base::GetFieldTrialParamByFeatureAsInt(
- features::kWebRtcHybridAgc, "saturation_margin", -1);
- if (saturation_margin != -1) {
- apm_config.gain_controller2.adaptive_digital.extra_saturation_margin_db =
- saturation_margin;
- }
- }
- audio_processing_->ApplyConfig(apm_config);
-}
-
-void AudioProcessor::UpdateDelayEstimate(base::TimeTicks capture_time) {
- const base::TimeDelta capture_delay = base::TimeTicks::Now() - capture_time;
- // Note: this delay calculation doesn't make sense, but it's how it's done
- // right now. Instead, the APM should probably attach the playout time to each
- // reference buffer it gets and store that internally?
- const base::TimeDelta render_delay = render_delay_.load();
-
- audio_delay_stats_reporter_.ReportDelay(capture_delay, render_delay);
-
- const base::TimeDelta total_delay = capture_delay + render_delay;
- audio_processing_->set_stream_delay_ms(total_delay.InMilliseconds());
- if (total_delay > base::TimeDelta::FromMilliseconds(300)) {
- DLOG(WARNING) << "Large audio delay, capture delay: "
- << capture_delay.InMilliseconds() << "ms; render delay: "
- << render_delay_.load().InMilliseconds() << "ms";
- }
-}
-
-void AudioProcessor::UpdateAnalogLevel(double volume) {
- DCHECK_LE(volume, 1.0);
- constexpr double kWebRtcMaxVolume = 255;
- const int webrtc_volume = volume * kWebRtcMaxVolume;
- audio_processing_->set_stream_analog_level(webrtc_volume);
-}
-
-void AudioProcessor::FeedDataToAPM(const AudioBus& source) {
- DCHECK_LE(source.channels(), media::limits::kMaxChannels);
- std::array<const float*, media::limits::kMaxChannels> input_ptrs;
- for (int i = 0; i < source.channels(); ++i) {
- input_ptrs[i] = source.channel(i);
- }
-
- const webrtc::StreamConfig config = CreateStreamConfig(audio_parameters_);
- int err = audio_processing_->ProcessStream(input_ptrs.data(), config, config,
- output_ptrs_.data());
- DCHECK_EQ(err, 0) << "ProcessStream() error: " << err;
-}
-
-void AudioProcessor::UpdateTypingDetected(bool key_pressed) {
- if (typing_detector_) {
- // Ignore remote tracks to avoid unnecessary stats computation.
- auto voice_detected =
- audio_processing_->GetStatistics(false /* has_remote_tracks */)
- .voice_detected;
- DCHECK(voice_detected.has_value());
- typing_detected_ = typing_detector_->Process(key_pressed, *voice_detected);
- }
-}
-
-base::Optional<double> AudioProcessor::GetNewVolumeFromAGC(double volume) {
- constexpr double kWebRtcMaxVolume = 255;
- const int webrtc_volume = volume * kWebRtcMaxVolume;
- const int new_webrtc_volume =
- audio_processing_->recommended_stream_analog_level();
-
- return new_webrtc_volume == webrtc_volume
- ? base::nullopt
- : base::Optional<double>(static_cast<double>(new_webrtc_volume) /
- kWebRtcMaxVolume);
-}
-
-} // namespace media
diff --git a/chromium/media/webrtc/audio_processor.h b/chromium/media/webrtc/audio_processor.h
deleted file mode 100644
index babf659ae62..00000000000
--- a/chromium/media/webrtc/audio_processor.h
+++ /dev/null
@@ -1,121 +0,0 @@
-// Copyright 2018 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_WEBRTC_AUDIO_PROCESSOR_H_
-#define MEDIA_WEBRTC_AUDIO_PROCESSOR_H_
-
-#include <atomic>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/component_export.h"
-#include "base/macros.h"
-#include "base/optional.h"
-#include "base/time/time.h"
-#include "media/audio/audio_io.h"
-#include "media/base/audio_parameters.h"
-#include "media/base/audio_processing.h"
-#include "media/webrtc/audio_delay_stats_reporter.h"
-#include "media/webrtc/audio_processor_controls.h"
-#include "third_party/webrtc/modules/audio_processing/include/audio_processing.h"
-#include "third_party/webrtc/modules/audio_processing/typing_detection.h"
-#include "third_party/webrtc/rtc_base/task_queue.h"
-
-namespace media {
-
-// The AudioProcessor wraps the WebRTC AudioProcessingModule. It also provides
-// stereo channel mirroring. For now, it will only run in the audio service when
-// run outside the browser process. As such, it does not support Android
-// specific configuration, like enabling mobile mode.
-class COMPONENT_EXPORT(MEDIA_WEBRTC) AudioProcessor final
- : public AudioProcessorControls {
- public:
- // |audio_parameters| describe the format to use, and |settings| what
- // processing to perform. The WebRTC APM will resample internally if
- // necessary. AudioProcessor uses the same format for input and output. Audio
- // must be provided in 10ms chunks.
- AudioProcessor(const AudioParameters& audio_parameters,
- const AudioProcessingSettings& settings);
-
- ~AudioProcessor() final;
-
- // The result of a call to ProcessCapture. |audio| is allocated and owned by
- // the AudioProcessor. It is valid until the next call to ProcessCapture or
- // until destruction of the AudioProcessor.
- struct COMPONENT_EXPORT(MEDIA_WEBRTC) ProcessingResult {
- ProcessingResult(const AudioBus& audio, base::Optional<double> new_volume);
- ProcessingResult(const ProcessingResult& b);
- ~ProcessingResult();
-
- const AudioBus& audio;
- base::Optional<double> new_volume;
- };
-
- ProcessingResult ProcessCapture(const AudioBus& source,
- base::TimeTicks capture_time,
- double volume,
- bool key_pressed);
-
- void AnalyzePlayout(const AudioBus& audio,
- const AudioParameters& parameters,
- base::TimeTicks playout_time);
-
- void set_has_reverse_stream(bool has_reverse_stream) {
- has_reverse_stream_ = has_reverse_stream;
- }
-
- // AudioProcessorControls implementation.
- void GetStats(GetStatsCB callback) override;
- void StartEchoCancellationDump(base::File file) override;
- void StopEchoCancellationDump() override;
-
- private:
- friend class WebRtcAudioProcessorTest;
-
- void InitializeAPM();
-
- // These functions are all part of ProcessCapture and assume that
- // |audio_processing_| is set.
- void UpdateDelayEstimate(base::TimeTicks capture_time);
- void UpdateAnalogLevel(double volume);
- void FeedDataToAPM(const AudioBus& source);
- void UpdateTypingDetected(bool key_pressed);
- base::Optional<double> GetNewVolumeFromAGC(double volume);
-
- const AudioParameters audio_parameters_;
- const AudioProcessingSettings settings_;
-
- // State related to interaction with APM.
- std::unique_ptr<webrtc::AudioProcessing> audio_processing_;
- std::unique_ptr<webrtc::TypingDetection> typing_detector_;
- std::atomic<bool> typing_detected_ = {false};
- std::atomic<base::TimeDelta> render_delay_ = {base::TimeDelta()};
- bool has_reverse_stream_ = false;
-
- // Indicates whether the audio processor playout signal has ever had
- // asymmetric left and right channel content.
- bool assume_upmixed_mono_playout_ = true;
-
- // The APM writes the processed data here.
- std::unique_ptr<AudioBus> output_bus_;
- std::vector<float*> output_ptrs_;
-
- // For reporting audio delay stats.
- AudioDelayStatsReporter audio_delay_stats_reporter_;
-
- // Low-priority task queue for doing AEC dump recordings. It has to
- // out-live |audio_processing_| and be created/destroyed from the same
- // thread.
- std::unique_ptr<rtc::TaskQueue> worker_queue_;
-
- // Flag indicating whether capture multi channel processing should be active.
- const bool use_capture_multi_channel_processing_;
-
- DISALLOW_COPY_AND_ASSIGN(AudioProcessor);
-};
-
-} // namespace media
-
-#endif // MEDIA_WEBRTC_AUDIO_PROCESSOR_H_
diff --git a/chromium/media/webrtc/audio_processor_unittest.cc b/chromium/media/webrtc/audio_processor_unittest.cc
deleted file mode 100644
index c8663af8a10..00000000000
--- a/chromium/media/webrtc/audio_processor_unittest.cc
+++ /dev/null
@@ -1,277 +0,0 @@
-// Copyright 2018 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 <stddef.h>
-#include <stdint.h>
-
-#include <string>
-#include <vector>
-
-#include "base/files/file_path.h"
-#include "base/files/file_util.h"
-#include "base/files/scoped_temp_dir.h"
-#include "base/logging.h"
-#include "base/macros.h"
-#include "base/memory/aligned_memory.h"
-#include "base/path_service.h"
-#include "base/stl_util.h"
-#include "base/test/task_environment.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "base/time/time.h"
-#include "build/build_config.h"
-#include "media/base/audio_bus.h"
-#include "media/base/audio_parameters.h"
-#include "media/webrtc/audio_processor.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using ::testing::_;
-using ::testing::AnyNumber;
-using ::testing::AtLeast;
-using ::testing::Return;
-
-namespace media {
-
-namespace {
-
-const int kAudioProcessingSampleRate = 48000;
-const int kAudioProcessingNumberOfChannels = 1;
-
-// The number of packers used for testing.
-const int kNumberOfPacketsForTest = 100;
-
-void ReadDataFromSpeechFile(char* data, int length) {
- base::FilePath file;
- CHECK(base::PathService::Get(base::DIR_SOURCE_ROOT, &file));
- file = file.Append(FILE_PATH_LITERAL("media"))
- .Append(FILE_PATH_LITERAL("test"))
- .Append(FILE_PATH_LITERAL("data"))
- .Append(FILE_PATH_LITERAL("speech_16b_stereo_48kHz.raw"));
- DCHECK(base::PathExists(file));
- int64_t data_file_size64 = 0;
- DCHECK(base::GetFileSize(file, &data_file_size64));
- EXPECT_EQ(length, base::ReadFile(file, data, length));
- DCHECK(data_file_size64 > length);
-}
-
-} // namespace
-
-class WebRtcAudioProcessorTest : public ::testing::Test {
- public:
- WebRtcAudioProcessorTest()
- : params_(media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
- media::CHANNEL_LAYOUT_STEREO,
- 48000,
- 480) {}
-
- protected:
- // Helper method to save duplicated code.
- void ProcessDataAndVerifyFormat(AudioProcessor* audio_processor,
- int expected_output_sample_rate,
- int expected_output_channels,
- int expected_output_buffer_size) {
- // Read the audio data from a file.
- const media::AudioParameters& params = audio_processor->audio_parameters_;
- const int packet_size = params.frames_per_buffer() * 2 * params.channels();
- const size_t length = packet_size * kNumberOfPacketsForTest;
- std::unique_ptr<char[]> capture_data(new char[length]);
- ReadDataFromSpeechFile(capture_data.get(), length);
- const int16_t* data_ptr =
- reinterpret_cast<const int16_t*>(capture_data.get());
- std::unique_ptr<media::AudioBus> data_bus =
- media::AudioBus::Create(params.channels(), params.frames_per_buffer());
-
- // |data_bus_playout| is used if the capture channels include a keyboard
- // channel. |data_bus_playout_to_use| points to the AudioBus to use, either
- // |data_bus| or |data_bus_playout|.
- std::unique_ptr<media::AudioBus> data_bus_playout;
- media::AudioBus* data_bus_playout_to_use = data_bus.get();
- media::AudioParameters playout_params = params;
- const bool has_keyboard_mic = params.channel_layout() ==
- media::CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC;
- if (has_keyboard_mic) {
- data_bus_playout = media::AudioBus::CreateWrapper(2);
- data_bus_playout->set_frames(params.frames_per_buffer());
- data_bus_playout_to_use = data_bus_playout.get();
- playout_params.Reset(params.format(), CHANNEL_LAYOUT_STEREO,
- params.sample_rate(), params.frames_per_buffer());
- }
-
- const base::TimeDelta input_capture_delay =
- base::TimeDelta::FromMilliseconds(20);
- for (int i = 0; i < kNumberOfPacketsForTest; ++i) {
- data_bus->FromInterleaved<SignedInt16SampleTypeTraits>(
- data_ptr, data_bus->frames());
- // |audio_processor| does nothing when the audio processing is off in
- // the processor.
- webrtc::AudioProcessing* ap = audio_processor->audio_processing_.get();
- const bool is_aec_enabled = ap && ap->GetConfig().echo_canceller.enabled;
- if (is_aec_enabled) {
- if (has_keyboard_mic) {
- for (int i = 0; i < data_bus_playout->channels(); ++i) {
- data_bus_playout->SetChannelData(
- i, const_cast<float*>(data_bus->channel(i)));
- }
- }
- base::TimeTicks in_the_future =
- base::TimeTicks::Now() + base::TimeDelta::FromMilliseconds(10);
- audio_processor->AnalyzePlayout(*data_bus_playout_to_use,
- playout_params, in_the_future);
- }
-
- auto result = audio_processor->ProcessCapture(
- *data_bus, base::TimeTicks::Now() - input_capture_delay, 1.0, false);
- data_ptr += params.frames_per_buffer() * params.channels();
- }
- }
-
- void VerifyEnabledComponents(AudioProcessor* audio_processor) {
- webrtc::AudioProcessing* audio_processing =
- audio_processor->audio_processing_.get();
- webrtc::AudioProcessing::Config ap_config = audio_processing->GetConfig();
- EXPECT_TRUE(ap_config.echo_canceller.enabled);
- EXPECT_FALSE(ap_config.echo_canceller.mobile_mode);
- EXPECT_TRUE(ap_config.high_pass_filter.enabled);
- EXPECT_TRUE(ap_config.gain_controller1.enabled);
- EXPECT_EQ(ap_config.gain_controller1.mode,
- ap_config.gain_controller1.kAdaptiveAnalog);
- EXPECT_TRUE(ap_config.noise_suppression.enabled);
- EXPECT_EQ(ap_config.noise_suppression.level,
- ap_config.noise_suppression.kHigh);
- EXPECT_TRUE(ap_config.voice_detection.enabled);
- }
-
- AudioProcessingSettings GetEnabledAudioProcessingSettings() const {
- AudioProcessingSettings settings;
- settings.echo_cancellation = EchoCancellationType::kAec3;
- settings.noise_suppression = NoiseSuppressionType::kExperimental;
- settings.automatic_gain_control = AutomaticGainControlType::kExperimental;
- settings.high_pass_filter = true;
- settings.typing_detection = true;
- return settings;
- }
-
- base::test::TaskEnvironment task_environment_;
- media::AudioParameters params_;
-};
-
-TEST_F(WebRtcAudioProcessorTest, WithAudioProcessing) {
- AudioProcessor audio_processor(params_, GetEnabledAudioProcessingSettings());
- VerifyEnabledComponents(&audio_processor);
- ProcessDataAndVerifyFormat(&audio_processor, kAudioProcessingSampleRate,
- kAudioProcessingNumberOfChannels,
- kAudioProcessingSampleRate / 100);
-}
-
-TEST_F(WebRtcAudioProcessorTest, WithoutAnyProcessing) {
- // All processing settings are disabled by default
- AudioProcessingSettings settings;
- const media::AudioParameters source_params(
- media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
- media::CHANNEL_LAYOUT_STEREO, kAudioProcessingSampleRate,
- kAudioProcessingSampleRate / 100);
- AudioProcessor audio_processor(source_params, settings);
-
- ProcessDataAndVerifyFormat(&audio_processor, params_.sample_rate(),
- params_.channels(), params_.sample_rate() / 100);
-}
-
-TEST_F(WebRtcAudioProcessorTest, TestAllSampleRates) {
- for (int sample_rate : {8000, 16000, 32000, 44100, 48000}) {
- int buffer_size = sample_rate / 100;
- media::AudioParameters params(media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
- media::CHANNEL_LAYOUT_STEREO, sample_rate,
- buffer_size);
- AudioProcessor audio_processor(params, GetEnabledAudioProcessingSettings());
-
- VerifyEnabledComponents(&audio_processor);
-
- ProcessDataAndVerifyFormat(&audio_processor, kAudioProcessingSampleRate,
- kAudioProcessingNumberOfChannels,
- kAudioProcessingSampleRate / 100);
- }
-}
-
-TEST_F(WebRtcAudioProcessorTest, TestStereoAudio) {
- // All processing settings are disabled by default
- AudioProcessingSettings settings;
- settings.stereo_mirroring = true;
- const media::AudioParameters source_params(
- media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
- media::CHANNEL_LAYOUT_STEREO, kAudioProcessingSampleRate,
- kAudioProcessingSampleRate / 100);
- AudioProcessor audio_processor(source_params, settings);
-
- // Construct left and right channels, and assign different values to the
- // first data of the left channel and right channel.
- const int size = media::AudioBus::CalculateMemorySize(source_params);
- std::unique_ptr<float, base::AlignedFreeDeleter> left_channel(
- static_cast<float*>(base::AlignedAlloc(size, 32)));
- std::unique_ptr<float, base::AlignedFreeDeleter> right_channel(
- static_cast<float*>(base::AlignedAlloc(size, 32)));
- std::unique_ptr<media::AudioBus> wrapper =
- media::AudioBus::CreateWrapper(source_params.channels());
- wrapper->set_frames(source_params.frames_per_buffer());
- wrapper->SetChannelData(0, left_channel.get());
- wrapper->SetChannelData(1, right_channel.get());
- wrapper->Zero();
- float* left_channel_ptr = left_channel.get();
- left_channel_ptr[0] = 1.0f;
-
- // Run the test consecutively to make sure the stereo channels are not
- // flipped back and forth.
- static const int kNumberOfPacketsForTest = 100;
- const base::TimeDelta pushed_capture_delay =
- base::TimeDelta::FromMilliseconds(42);
- for (int i = 0; i < kNumberOfPacketsForTest; ++i) {
- auto result = audio_processor.ProcessCapture(
- *wrapper, base::TimeTicks::Now() + pushed_capture_delay, 1.0, false);
-
- EXPECT_EQ(result.audio.channel(0)[0], 0);
- EXPECT_NE(result.audio.channel(1)[0], 0);
- }
-}
-
-TEST_F(WebRtcAudioProcessorTest, TestWithKeyboardMicChannel) {
- AudioProcessingSettings settings = GetEnabledAudioProcessingSettings();
- media::AudioParameters params(media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
- media::CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC,
- kAudioProcessingSampleRate,
- kAudioProcessingSampleRate / 100);
- AudioProcessor audio_processor(params, settings);
- ProcessDataAndVerifyFormat(&audio_processor, kAudioProcessingSampleRate,
- kAudioProcessingNumberOfChannels,
- kAudioProcessingSampleRate / 100);
-}
-
-TEST_F(WebRtcAudioProcessorTest, StartStopAecDump) {
- base::ScopedTempDir temp_directory;
- ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
- base::FilePath temp_file_path;
- ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_directory.GetPath(),
- &temp_file_path));
- {
- AudioProcessor audio_processor(params_,
- GetEnabledAudioProcessingSettings());
-
- // Start and stop recording.
- audio_processor.StartEchoCancellationDump(base::File(
- temp_file_path, base::File::FLAG_WRITE | base::File::FLAG_OPEN));
- audio_processor.StopEchoCancellationDump();
-
- // Start and wait for d-tor.
- audio_processor.StartEchoCancellationDump(base::File(
- temp_file_path, base::File::FLAG_WRITE | base::File::FLAG_OPEN));
- }
-
- // Check that dump file is non-empty after audio processor has been
- // destroyed. Note that this test fails when compiling WebRTC
- // without protobuf support, rtc_enable_protobuf=false.
- std::string output;
- ASSERT_TRUE(base::ReadFileToString(temp_file_path, &output));
- ASSERT_FALSE(output.empty());
- // The temporary file is deleted when temp_directory exists scope.
-}
-
-} // namespace media
diff --git a/chromium/media/webrtc/webrtc_switches.cc b/chromium/media/webrtc/webrtc_switches.cc
index 481e16adc76..05bba547469 100644
--- a/chromium/media/webrtc/webrtc_switches.cc
+++ b/chromium/media/webrtc/webrtc_switches.cc
@@ -20,12 +20,6 @@ const char kAgcStartupMinVolume[] = "agc-startup-min-volume";
namespace features {
-// Enables running WebRTC Audio Processing in the audio service, rather than
-// in the renderer process. Should be combined with running the audio service
-// out of the browser process, except for when testing locally.
-const base::Feature kWebRtcApmInAudioService{"WebRtcApmInAudioService",
- base::FEATURE_DISABLED_BY_DEFAULT};
-
// Enables multi channel capture audio to be processed without
// downmixing in the WebRTC audio processing module when running in the renderer
// process.
@@ -38,25 +32,3 @@ const base::Feature kWebRtcHybridAgc{"WebRtcHybridAgc",
base::FEATURE_DISABLED_BY_DEFAULT};
} // namespace features
-
-namespace switches {
-
-const char kForceDisableWebRtcApmInAudioService[] =
- "disable-webrtc-apm-in-audio-service";
-
-} // namespace switches
-
-namespace media {
-
-bool IsWebRtcApmInAudioServiceEnabled() {
-#if defined(OS_WIN) || defined(OS_MACOSX) || \
- (defined(OS_LINUX) && !defined(OS_CHROMEOS))
- return base::FeatureList::IsEnabled(features::kWebRtcApmInAudioService) &&
- !base::CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kForceDisableWebRtcApmInAudioService);
-#else
- return false;
-#endif
-}
-
-} // namespace media
diff --git a/chromium/media/webrtc/webrtc_switches.h b/chromium/media/webrtc/webrtc_switches.h
index de0f83cf1af..e3fd32da833 100644
--- a/chromium/media/webrtc/webrtc_switches.h
+++ b/chromium/media/webrtc/webrtc_switches.h
@@ -19,9 +19,6 @@ COMPONENT_EXPORT(MEDIA_WEBRTC) extern const char kAgcStartupMinVolume[];
namespace features {
COMPONENT_EXPORT(MEDIA_WEBRTC)
-extern const base::Feature kWebRtcApmInAudioService;
-
-COMPONENT_EXPORT(MEDIA_WEBRTC)
extern const base::Feature kWebRtcEnableCaptureMultiChannelApm;
COMPONENT_EXPORT(MEDIA_WEBRTC)
@@ -29,15 +26,4 @@ extern const base::Feature kWebRtcHybridAgc;
} // namespace features
-namespace switches {
-COMPONENT_EXPORT(MEDIA_WEBRTC)
-extern const char kForceDisableWebRtcApmInAudioService[];
-} // namespace switches
-
-namespace media {
-
-COMPONENT_EXPORT(MEDIA_WEBRTC) bool IsWebRtcApmInAudioServiceEnabled();
-
-} // namespace media
-
#endif // MEDIA_WEBRTC_WEBRTC_SWITCHES_H_