summaryrefslogtreecommitdiff
path: root/chromium/media/base
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@theqtcompany.com>2016-05-09 14:22:11 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2016-05-09 15:11:45 +0000
commit2ddb2d3e14eef3de7dbd0cef553d669b9ac2361c (patch)
treee75f511546c5fd1a173e87c1f9fb11d7ac8d1af3 /chromium/media/base
parenta4f3d46271c57e8155ba912df46a05559d14726e (diff)
downloadqtwebengine-chromium-2ddb2d3e14eef3de7dbd0cef553d669b9ac2361c.tar.gz
BASELINE: Update Chromium to 51.0.2704.41
Also adds in all smaller components by reversing logic for exclusion. Change-Id: Ibf90b506e7da088ea2f65dcf23f2b0992c504422 Reviewed-by: Joerg Bornemann <joerg.bornemann@theqtcompany.com>
Diffstat (limited to 'chromium/media/base')
-rw-r--r--chromium/media/base/BUILD.gn67
-rw-r--r--chromium/media/base/android/BUILD.gn43
-rw-r--r--chromium/media/base/android/audio_decoder_job.cc18
-rw-r--r--chromium/media/base/android/audio_media_codec_decoder.cc (renamed from chromium/media/base/android/media_codec_audio_decoder.cc)51
-rw-r--r--chromium/media/base/android/audio_media_codec_decoder.h (renamed from chromium/media/base/android/media_codec_audio_decoder.h)14
-rw-r--r--chromium/media/base/android/demuxer_stream_player_params.cc6
-rw-r--r--chromium/media/base/android/demuxer_stream_player_params.h3
-rw-r--r--chromium/media/base/android/media_codec_bridge.cc13
-rw-r--r--chromium/media/base/android/media_codec_bridge.h43
-rw-r--r--chromium/media/base/android/media_codec_decoder_unittest.cc18
-rw-r--r--chromium/media/base/android/media_codec_player.cc36
-rw-r--r--chromium/media/base/android/media_codec_player.h13
-rw-r--r--chromium/media/base/android/media_codec_player_unittest.cc48
-rw-r--r--chromium/media/base/android/media_codec_util.cc160
-rw-r--r--chromium/media/base/android/media_codec_util.h10
-rw-r--r--chromium/media/base/android/media_drm_bridge.cc97
-rw-r--r--chromium/media/base/android/media_drm_bridge.h28
-rw-r--r--chromium/media/base/android/media_drm_bridge_cdm_context.cc42
-rw-r--r--chromium/media/base/android/media_drm_bridge_cdm_context.h74
-rw-r--r--chromium/media/base/android/media_player_android.cc34
-rw-r--r--chromium/media/base/android/media_player_android.h48
-rw-r--r--chromium/media/base/android/media_player_bridge.cc71
-rw-r--r--chromium/media/base/android/media_player_bridge.h24
-rw-r--r--chromium/media/base/android/media_player_bridge_unittest.cc3
-rw-r--r--chromium/media/base/android/media_source_player.cc14
-rw-r--r--chromium/media/base/android/media_source_player.h7
-rw-r--r--chromium/media/base/android/media_source_player_unittest.cc6
-rw-r--r--chromium/media/base/android/ndk_media_codec_bridge.cc50
-rw-r--r--chromium/media/base/android/ndk_media_codec_bridge.h19
-rw-r--r--chromium/media/base/android/provision_fetcher.h1
-rw-r--r--chromium/media/base/android/sdk_media_codec_bridge.cc166
-rw-r--r--chromium/media/base/android/sdk_media_codec_bridge.h65
-rw-r--r--chromium/media/base/android/sdk_media_codec_bridge_unittest.cc13
-rw-r--r--chromium/media/base/android/video_decoder_job.cc2
-rw-r--r--chromium/media/base/android/video_media_codec_decoder.cc (renamed from chromium/media/base/android/media_codec_video_decoder.cc)64
-rw-r--r--chromium/media/base/android/video_media_codec_decoder.h (renamed from chromium/media/base/android/media_codec_video_decoder.h)14
-rw-r--r--chromium/media/base/audio_buffer.cc12
-rw-r--r--chromium/media/base/audio_buffer.h18
-rw-r--r--chromium/media/base/audio_capturer_source.h2
-rw-r--r--chromium/media/base/audio_codecs.cc51
-rw-r--r--chromium/media/base/audio_codecs.h48
-rw-r--r--chromium/media/base/audio_decoder.cc4
-rw-r--r--chromium/media/base/audio_decoder.h38
-rw-r--r--chromium/media/base/audio_decoder_config.cc68
-rw-r--r--chromium/media/base/audio_decoder_config.h50
-rw-r--r--chromium/media/base/audio_hardware_config.cc16
-rw-r--r--chromium/media/base/audio_hardware_config.h5
-rw-r--r--chromium/media/base/audio_hash.cc14
-rw-r--r--chromium/media/base/audio_hash.h5
-rw-r--r--chromium/media/base/audio_pull_fifo.cc4
-rw-r--r--chromium/media/base/audio_pull_fifo.h4
-rw-r--r--chromium/media/base/audio_pull_fifo_unittest.cc14
-rw-r--r--chromium/media/base/audio_push_fifo.cc86
-rw-r--r--chromium/media/base/audio_push_fifo.h73
-rw-r--r--chromium/media/base/audio_push_fifo_unittest.cc256
-rw-r--r--chromium/media/base/audio_renderer.h8
-rw-r--r--chromium/media/base/audio_renderer_mixer.cc17
-rw-r--r--chromium/media/base/audio_renderer_mixer.h8
-rw-r--r--chromium/media/base/audio_renderer_mixer_input.cc61
-rw-r--r--chromium/media/base/audio_renderer_mixer_input.h33
-rw-r--r--chromium/media/base/audio_renderer_mixer_unittest.cc2
-rw-r--r--chromium/media/base/audio_renderer_sink.h41
-rw-r--r--chromium/media/base/audio_shifter.cc46
-rw-r--r--chromium/media/base/audio_shifter.h12
-rw-r--r--chromium/media/base/audio_video_metadata_extractor.cc3
-rw-r--r--chromium/media/base/audio_video_metadata_extractor.h1
-rw-r--r--chromium/media/base/audio_video_metadata_extractor_unittest.cc61
-rw-r--r--chromium/media/base/bind_to_current_loop.h3
-rw-r--r--chromium/media/base/bind_to_current_loop_unittest.cc3
-rw-r--r--chromium/media/base/bit_reader_core.cc5
-rw-r--r--chromium/media/base/bit_reader_fuzzertest.cc37
-rw-r--r--chromium/media/base/bitstream_buffer.cc13
-rw-r--r--chromium/media/base/bitstream_buffer.h31
-rw-r--r--chromium/media/base/cdm_callback_promise.cc11
-rw-r--r--chromium/media/base/cdm_callback_promise.h2
-rw-r--r--chromium/media/base/cdm_context.cc15
-rw-r--r--chromium/media/base/cdm_context.h49
-rw-r--r--chromium/media/base/cdm_key_information.cc2
-rw-r--r--chromium/media/base/cdm_key_information.h1
-rw-r--r--chromium/media/base/cdm_promise.h13
-rw-r--r--chromium/media/base/container_names.cc10
-rw-r--r--chromium/media/base/container_names_fuzzertest.cc15
-rw-r--r--chromium/media/base/container_names_unittest.cc28
-rw-r--r--chromium/media/base/decode_status.cc26
-rw-r--r--chromium/media/base/decode_status.h27
-rw-r--r--chromium/media/base/decoder_factory.cc23
-rw-r--r--chromium/media/base/decoder_factory.h46
-rw-r--r--chromium/media/base/decrypt_config.h4
-rw-r--r--chromium/media/base/demuxer.h40
-rw-r--r--chromium/media/base/demuxer_perftest.cc10
-rw-r--r--chromium/media/base/encryption_scheme.cc37
-rw-r--r--chromium/media/base/encryption_scheme.h79
-rw-r--r--chromium/media/base/fake_audio_renderer_sink.cc15
-rw-r--r--chromium/media/base/fake_audio_renderer_sink.h7
-rw-r--r--chromium/media/base/fake_demuxer_stream.cc10
-rw-r--r--chromium/media/base/fake_output_device.cc36
-rw-r--r--chromium/media/base/fake_output_device.h35
-rw-r--r--chromium/media/base/fake_single_thread_task_runner.cc113
-rw-r--r--chromium/media/base/fake_single_thread_task_runner.h61
-rw-r--r--chromium/media/base/key_system_info.cc2
-rw-r--r--chromium/media/base/key_system_info.h10
-rw-r--r--chromium/media/base/key_systems.cc413
-rw-r--r--chromium/media/base/key_systems.h49
-rw-r--r--chromium/media/base/key_systems_support_uma.cc133
-rw-r--r--chromium/media/base/key_systems_support_uma.h57
-rw-r--r--chromium/media/base/key_systems_unittest.cc161
-rw-r--r--chromium/media/base/mac/BUILD.gn4
-rw-r--r--chromium/media/base/mac/avfoundation_glue.h4
-rw-r--r--chromium/media/base/mac/avfoundation_glue.mm162
-rw-r--r--chromium/media/base/mac/video_frame_mac.cc20
-rw-r--r--chromium/media/base/mac/video_frame_mac_unittests.cc73
-rw-r--r--chromium/media/base/mac/videotoolbox_glue.h15
-rw-r--r--chromium/media/base/mac/videotoolbox_glue.mm9
-rw-r--r--chromium/media/base/mac/videotoolbox_helpers.cc304
-rw-r--r--chromium/media/base/mac/videotoolbox_helpers.h69
-rw-r--r--chromium/media/base/media.cc63
-rw-r--r--chromium/media/base/media.h27
-rw-r--r--chromium/media/base/media_client.cc7
-rw-r--r--chromium/media/base/media_client.h9
-rw-r--r--chromium/media/base/media_keys.h31
-rw-r--r--chromium/media/base/media_log.cc40
-rw-r--r--chromium/media/base/media_log.h9
-rw-r--r--chromium/media/base/media_switches.cc46
-rw-r--r--chromium/media/base/media_switches.h23
-rw-r--r--chromium/media/base/media_track.cc18
-rw-r--r--chromium/media/base/media_track.h41
-rw-r--r--chromium/media/base/media_tracks.cc81
-rw-r--r--chromium/media/base/media_tracks.h62
-rw-r--r--chromium/media/base/media_util.cc9
-rw-r--r--chromium/media/base/media_util.h6
-rw-r--r--chromium/media/base/mime_util.cc732
-rw-r--r--chromium/media/base/mime_util.h25
-rw-r--r--chromium/media/base/mime_util_internal.cc685
-rw-r--r--chromium/media/base/mime_util_internal.h171
-rw-r--r--chromium/media/base/mime_util_unittest.cc307
-rw-r--r--chromium/media/base/mock_audio_renderer_sink.cc33
-rw-r--r--chromium/media/base/mock_audio_renderer_sink.h15
-rw-r--r--chromium/media/base/mock_filters.cc27
-rw-r--r--chromium/media/base/mock_filters.h80
-rw-r--r--chromium/media/base/output_device.h67
-rw-r--r--chromium/media/base/output_device_info.cc40
-rw-r--r--chromium/media/base/output_device_info.h68
-rw-r--r--chromium/media/base/pipeline.h367
-rw-r--r--chromium/media/base/pipeline_impl.cc (renamed from chromium/media/base/pipeline.cc)309
-rw-r--r--chromium/media/base/pipeline_impl.h346
-rw-r--r--chromium/media/base/pipeline_impl_unittest.cc (renamed from chromium/media/base/pipeline_unittest.cc)369
-rw-r--r--chromium/media/base/pipeline_status.h23
-rw-r--r--chromium/media/base/renderer_factory.h4
-rw-r--r--chromium/media/base/run_all_unittests.cc6
-rw-r--r--chromium/media/base/serial_runner.cc1
-rw-r--r--chromium/media/base/serial_runner.h1
-rw-r--r--chromium/media/base/stream_parser.cc6
-rw-r--r--chromium/media/base/stream_parser.h40
-rw-r--r--chromium/media/base/surface_manager.h45
-rw-r--r--chromium/media/base/test_helpers.cc39
-rw-r--r--chromium/media/base/test_helpers.h25
-rw-r--r--chromium/media/base/test_random.h45
-rw-r--r--chromium/media/base/text_track_config.cc2
-rw-r--r--chromium/media/base/text_track_config.h1
-rw-r--r--chromium/media/base/video_capturer_source.h10
-rw-r--r--chromium/media/base/video_codecs.cc104
-rw-r--r--chromium/media/base/video_codecs.h13
-rw-r--r--chromium/media/base/video_decoder.h21
-rw-r--r--chromium/media/base/video_decoder_config.cc41
-rw-r--r--chromium/media/base/video_decoder_config.h16
-rw-r--r--chromium/media/base/video_decoder_config_unittest.cc14
-rw-r--r--chromium/media/base/video_frame.cc355
-rw-r--r--chromium/media/base/video_frame.h66
-rw-r--r--chromium/media/base/video_frame_metadata.cc5
-rw-r--r--chromium/media/base/video_frame_metadata.h15
-rw-r--r--chromium/media/base/video_frame_pool.cc2
-rw-r--r--chromium/media/base/video_frame_unittest.cc32
-rw-r--r--chromium/media/base/video_renderer.h8
-rw-r--r--chromium/media/base/video_types.cc24
-rw-r--r--chromium/media/base/video_types.h10
-rw-r--r--chromium/media/base/video_util.cc23
-rw-r--r--chromium/media/base/video_util.h5
-rw-r--r--chromium/media/base/yuv_convert_perftest.cc1
-rw-r--r--chromium/media/base/yuv_convert_unittest.cc4
179 files changed, 6344 insertions, 3514 deletions
diff --git a/chromium/media/base/BUILD.gn b/chromium/media/base/BUILD.gn
index 531d6dd616e..c0213562122 100644
--- a/chromium/media/base/BUILD.gn
+++ b/chromium/media/base/BUILD.gn
@@ -8,10 +8,14 @@ import("//build/config/features.gni")
import("//build/config/ui.gni")
import("//build/config/linux/pkg_config.gni")
import("//media/media_options.gni")
+import("//testing/libfuzzer/fuzzer_test.gni")
source_set("base") {
# This is part of the media component.
- visibility = [ "//media" ]
+ visibility = [
+ "//media",
+ "//media/capture",
+ ]
sources = [
"audio_block_fifo.cc",
"audio_block_fifo.h",
@@ -22,6 +26,8 @@ source_set("base") {
"audio_buffer_queue.cc",
"audio_buffer_queue.h",
"audio_capturer_source.h",
+ "audio_codecs.cc",
+ "audio_codecs.h",
"audio_converter.cc",
"audio_converter.h",
"audio_decoder.cc",
@@ -38,6 +44,8 @@ source_set("base") {
"audio_hash.h",
"audio_pull_fifo.cc",
"audio_pull_fifo.h",
+ "audio_push_fifo.cc",
+ "audio_push_fifo.h",
"audio_renderer.cc",
"audio_renderer.h",
"audio_renderer_mixer.cc",
@@ -80,14 +88,20 @@ source_set("base") {
"channel_mixer.h",
"channel_mixing_matrix.cc",
"channel_mixing_matrix.h",
+ "container_names.cc",
+ "container_names.h",
"data_buffer.cc",
"data_buffer.h",
"data_source.cc",
"data_source.h",
+ "decode_status.cc",
+ "decode_status.h",
"decoder_buffer.cc",
"decoder_buffer.h",
"decoder_buffer_queue.cc",
"decoder_buffer_queue.h",
+ "decoder_factory.cc",
+ "decoder_factory.h",
"decrypt_config.cc",
"decrypt_config.h",
"decryptor.cc",
@@ -101,13 +115,13 @@ source_set("base") {
"djb2.cc",
"djb2.h",
"eme_constants.h",
+ "encryption_scheme.cc",
+ "encryption_scheme.h",
"key_system_info.cc",
"key_system_info.h",
"key_systems.cc",
"key_systems.h",
"key_systems.h",
- "key_systems_support_uma.cc",
- "key_systems_support_uma.h",
"loopback_audio_converter.cc",
"loopback_audio_converter.h",
"media.cc",
@@ -125,19 +139,27 @@ source_set("base") {
"media_resources.h",
"media_switches.cc",
"media_switches.h",
+ "media_track.cc",
+ "media_track.h",
+ "media_tracks.cc",
+ "media_tracks.h",
"media_util.cc",
"media_util.h",
"mime_util.cc",
"mime_util.h",
+ "mime_util_internal.cc",
+ "mime_util_internal.h",
"moving_average.cc",
"moving_average.h",
"multi_channel_resampler.cc",
"multi_channel_resampler.h",
"null_video_sink.cc",
"null_video_sink.h",
- "output_device.h",
- "pipeline.cc",
+ "output_device_info.cc",
+ "output_device_info.h",
"pipeline.h",
+ "pipeline_impl.cc",
+ "pipeline_impl.h",
"pipeline_status.h",
"player_tracker.cc",
"player_tracker.h",
@@ -165,6 +187,7 @@ source_set("base") {
"stream_parser.h",
"stream_parser_buffer.cc",
"stream_parser_buffer.h",
+ "surface_manager.h",
"text_cue.cc",
"text_cue.h",
"text_ranges.cc",
@@ -226,10 +249,6 @@ source_set("base") {
]
if (media_use_ffmpeg) {
- sources += [
- "container_names.cc",
- "container_names.h",
- ]
if (!is_android) {
sources += [
"audio_video_metadata_extractor.cc",
@@ -245,9 +264,7 @@ source_set("base") {
if (is_android) {
public_deps = [
"//media/base/android",
- "//media/base/android:media_java",
"//media/base/android:media_jni_headers",
- "//media/base/android:video_capture_jni_headers",
]
allow_circular_includes_from += [ "//media/base/android" ]
}
@@ -342,8 +359,8 @@ source_set("test_support") {
"fake_demuxer_stream.h",
"fake_media_resources.cc",
"fake_media_resources.h",
- "fake_output_device.cc",
- "fake_output_device.h",
+ "fake_single_thread_task_runner.cc",
+ "fake_single_thread_task_runner.h",
"fake_text_track_stream.cc",
"fake_text_track_stream.h",
"gmock_callback_support.h",
@@ -359,6 +376,7 @@ source_set("test_support") {
"test_data_util.h",
"test_helpers.cc",
"test_helpers.h",
+ "test_random.h",
]
configs += [ "//media:media_config" ]
deps = [
@@ -381,6 +399,7 @@ source_set("unittests") {
"audio_hardware_config_unittest.cc",
"audio_hash_unittest.cc",
"audio_pull_fifo_unittest.cc",
+ "audio_push_fifo_unittest.cc",
"audio_renderer_mixer_input_unittest.cc",
"audio_renderer_mixer_unittest.cc",
"audio_shifter_unittest.cc",
@@ -404,7 +423,7 @@ source_set("unittests") {
"moving_average_unittest.cc",
"multi_channel_resampler_unittest.cc",
"null_video_sink_unittest.cc",
- "pipeline_unittest.cc",
+ "pipeline_impl_unittest.cc",
"ranges_unittest.cc",
"run_all_unittests.cc",
"seekable_buffer_unittest.cc",
@@ -544,3 +563,23 @@ if (current_cpu == "x86" || current_cpu == "x64") {
}
}
}
+
+fuzzer_test("media_bit_reader_fuzzer") {
+ sources = [
+ "bit_reader_fuzzertest.cc",
+ ]
+ deps = [
+ "//base",
+ "//media",
+ ]
+}
+
+fuzzer_test("media_container_names_fuzzer") {
+ sources = [
+ "container_names_fuzzertest.cc",
+ ]
+ deps = [
+ "//base",
+ "//media",
+ ]
+}
diff --git a/chromium/media/base/android/BUILD.gn b/chromium/media/base/android/BUILD.gn
index 54d80743d09..c3fd33f6e11 100644
--- a/chromium/media/base/android/BUILD.gn
+++ b/chromium/media/base/android/BUILD.gn
@@ -18,13 +18,13 @@ source_set("android") {
"android_cdm_factory.h",
"audio_decoder_job.cc",
"audio_decoder_job.h",
+ "audio_media_codec_decoder.cc",
+ "audio_media_codec_decoder.h",
"demuxer_android.h",
"demuxer_stream_player_params.cc",
"demuxer_stream_player_params.h",
"media_client_android.cc",
"media_client_android.h",
- "media_codec_audio_decoder.cc",
- "media_codec_audio_decoder.h",
"media_codec_bridge.cc",
"media_codec_bridge.h",
"media_codec_decoder.cc",
@@ -33,12 +33,12 @@ source_set("android") {
"media_codec_player.h",
"media_codec_util.cc",
"media_codec_util.h",
- "media_codec_video_decoder.cc",
- "media_codec_video_decoder.h",
"media_decoder_job.cc",
"media_decoder_job.h",
"media_drm_bridge.cc",
"media_drm_bridge.h",
+ "media_drm_bridge_cdm_context.cc",
+ "media_drm_bridge_cdm_context.h",
"media_drm_bridge_delegate.cc",
"media_drm_bridge_delegate.h",
"media_jni_registrar.cc",
@@ -64,6 +64,8 @@ source_set("android") {
"sdk_media_codec_bridge.h",
"video_decoder_job.cc",
"video_decoder_job.h",
+ "video_media_codec_decoder.cc",
+ "video_media_codec_decoder.h",
]
configs += [
"//media:media_config",
@@ -83,7 +85,6 @@ source_set("unittests") {
sources = [
"access_unit_queue_unittest.cc",
"media_codec_decoder_unittest.cc",
- "media_codec_player_unittest.cc",
"media_drm_bridge_unittest.cc",
"media_player_bridge_unittest.cc",
"media_source_player_unittest.cc",
@@ -92,6 +93,11 @@ source_set("unittests") {
"test_data_factory.h",
"test_statistics.h",
]
+
+ if (proprietary_codecs) {
+ sources += [ "media_codec_player_unittest.cc" ]
+ }
+
deps = [
":android",
"//media/base:test_support",
@@ -115,27 +121,18 @@ generate_jni("media_jni_headers") {
jni_package = "media"
}
-generate_jni("video_capture_jni_headers") {
- sources = [
- "java/src/org/chromium/media/VideoCapture.java",
- "java/src/org/chromium/media/VideoCaptureFactory.java",
- ]
- jni_package = "media"
-}
-
-java_cpp_enum("media_java_enums_srcjar") {
- sources = [
- "//media/capture/video/android/video_capture_device_android.h",
- "//media/capture/video/video_capture_device.h",
- ]
-}
-
android_library("media_java") {
deps = [
"//base:base_java",
]
- srcjar_deps = [ ":media_java_enums_srcjar" ]
-
- DEPRECATED_java_in_dir = "java/src"
+ java_files = [
+ "java/src/org/chromium/media/AudioManagerAndroid.java",
+ "java/src/org/chromium/media/AudioRecordInput.java",
+ "java/src/org/chromium/media/MediaCodecBridge.java",
+ "java/src/org/chromium/media/MediaCodecUtil.java",
+ "java/src/org/chromium/media/MediaDrmBridge.java",
+ "java/src/org/chromium/media/MediaPlayerBridge.java",
+ "java/src/org/chromium/media/MediaPlayerListener.java",
+ ]
}
diff --git a/chromium/media/base/android/audio_decoder_job.cc b/chromium/media/base/android/audio_decoder_job.cc
index dd1fa5346f1..f0baca4e949 100644
--- a/chromium/media/base/android/audio_decoder_job.cc
+++ b/chromium/media/base/android/audio_decoder_job.cc
@@ -105,9 +105,16 @@ void AudioDecoderJob::ReleaseOutputBuffer(
render_output = render_output && (size != 0u);
bool is_audio_underrun = false;
if (render_output) {
- int64_t head_position =
+ bool postpone = false;
+ int64_t head_position;
+ MediaCodecStatus status =
(static_cast<AudioCodecBridge*>(media_codec_bridge_.get()))
- ->PlayOutputBuffer(output_buffer_index, size, offset);
+ ->PlayOutputBuffer(output_buffer_index, size, offset, postpone,
+ &head_position);
+ // TODO(timav,watk): This CHECK maintains the behavior of this call before
+ // we started catching CodecException and returning it as MEDIA_CODEC_ERROR.
+ // It needs to be handled some other way. http://crbug.com/585978
+ CHECK_EQ(status, MEDIA_CODEC_OK);
base::TimeTicks current_time = base::TimeTicks::Now();
@@ -191,7 +198,12 @@ void AudioDecoderJob::OnOutputFormatChanged() {
DCHECK(media_codec_bridge_);
int old_sampling_rate = output_sampling_rate_;
- output_sampling_rate_ = media_codec_bridge_->GetOutputSamplingRate();
+ MediaCodecStatus status =
+ media_codec_bridge_->GetOutputSamplingRate(&output_sampling_rate_);
+ // TODO(timav,watk): This CHECK maintains the behavior of this call before
+ // we started catching CodecException and returning it as MEDIA_CODEC_ERROR.
+ // It needs to be handled some other way. http://crbug.com/585978
+ CHECK_EQ(status, MEDIA_CODEC_OK);
if (output_sampling_rate_ != old_sampling_rate)
ResetTimestampHelper();
}
diff --git a/chromium/media/base/android/media_codec_audio_decoder.cc b/chromium/media/base/android/audio_media_codec_decoder.cc
index e1c1fdb45e6..5654e7ca4a0 100644
--- a/chromium/media/base/android/media_codec_audio_decoder.cc
+++ b/chromium/media/base/android/audio_media_codec_decoder.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "media/base/android/media_codec_audio_decoder.h"
+#include "media/base/android/audio_media_codec_decoder.h"
#include "base/bind.h"
#include "base/logging.h"
@@ -20,7 +20,7 @@ const int kBytesPerAudioOutputSample = 2;
namespace media {
-MediaCodecAudioDecoder::MediaCodecAudioDecoder(
+AudioMediaCodecDecoder::AudioMediaCodecDecoder(
const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner,
FrameStatistics* frame_statistics,
const base::Closure& request_data_cb,
@@ -43,26 +43,25 @@ MediaCodecAudioDecoder::MediaCodecAudioDecoder(
bytes_per_frame_(0),
output_sampling_rate_(0),
frame_count_(0),
- update_current_time_cb_(update_current_time_cb) {
-}
+ update_current_time_cb_(update_current_time_cb) {}
-MediaCodecAudioDecoder::~MediaCodecAudioDecoder() {
+AudioMediaCodecDecoder::~AudioMediaCodecDecoder() {
DCHECK(media_task_runner_->BelongsToCurrentThread());
DVLOG(1) << "AudioDecoder::~AudioDecoder()";
ReleaseDecoderResources();
}
-const char* MediaCodecAudioDecoder::class_name() const {
+const char* AudioMediaCodecDecoder::class_name() const {
return "AudioDecoder";
}
-bool MediaCodecAudioDecoder::HasStream() const {
+bool AudioMediaCodecDecoder::HasStream() const {
DCHECK(media_task_runner_->BelongsToCurrentThread());
return configs_.audio_codec != kUnknownAudioCodec;
}
-void MediaCodecAudioDecoder::SetDemuxerConfigs(const DemuxerConfigs& configs) {
+void AudioMediaCodecDecoder::SetDemuxerConfigs(const DemuxerConfigs& configs) {
DVLOG(1) << class_name() << "::" << __FUNCTION__ << " " << configs;
configs_ = configs;
@@ -70,13 +69,13 @@ void MediaCodecAudioDecoder::SetDemuxerConfigs(const DemuxerConfigs& configs) {
output_sampling_rate_ = configs.audio_sampling_rate;
}
-bool MediaCodecAudioDecoder::IsContentEncrypted() const {
+bool AudioMediaCodecDecoder::IsContentEncrypted() const {
// Make sure SetDemuxerConfigs() as been called.
DCHECK(configs_.audio_codec != kUnknownAudioCodec);
return configs_.is_audio_encrypted;
}
-void MediaCodecAudioDecoder::ReleaseDecoderResources() {
+void AudioMediaCodecDecoder::ReleaseDecoderResources() {
DCHECK(media_task_runner_->BelongsToCurrentThread());
DVLOG(1) << class_name() << "::" << __FUNCTION__;
@@ -85,14 +84,14 @@ void MediaCodecAudioDecoder::ReleaseDecoderResources() {
ReleaseMediaCodec();
}
-void MediaCodecAudioDecoder::Flush() {
+void AudioMediaCodecDecoder::Flush() {
DCHECK(media_task_runner_->BelongsToCurrentThread());
MediaCodecDecoder::Flush();
frame_count_ = 0;
}
-void MediaCodecAudioDecoder::SetVolume(double volume) {
+void AudioMediaCodecDecoder::SetVolume(double volume) {
DCHECK(media_task_runner_->BelongsToCurrentThread());
DVLOG(1) << class_name() << "::" << __FUNCTION__ << " " << volume;
@@ -101,7 +100,7 @@ void MediaCodecAudioDecoder::SetVolume(double volume) {
SetVolumeInternal();
}
-void MediaCodecAudioDecoder::SetBaseTimestamp(base::TimeDelta base_timestamp) {
+void AudioMediaCodecDecoder::SetBaseTimestamp(base::TimeDelta base_timestamp) {
// Called from Media thread and Decoder thread. When called from Media thread
// Decoder thread should not be running.
@@ -112,7 +111,7 @@ void MediaCodecAudioDecoder::SetBaseTimestamp(base::TimeDelta base_timestamp) {
audio_timestamp_helper_->SetBaseTimestamp(base_timestamp_);
}
-bool MediaCodecAudioDecoder::IsCodecReconfigureNeeded(
+bool AudioMediaCodecDecoder::IsCodecReconfigureNeeded(
const DemuxerConfigs& next) const {
if (always_reconfigure_for_tests_)
return true;
@@ -127,7 +126,7 @@ bool MediaCodecAudioDecoder::IsCodecReconfigureNeeded(
next.audio_extra_data.begin());
}
-MediaCodecDecoder::ConfigStatus MediaCodecAudioDecoder::ConfigureInternal(
+MediaCodecDecoder::ConfigStatus AudioMediaCodecDecoder::ConfigureInternal(
jobject media_crypto) {
DCHECK(media_task_runner_->BelongsToCurrentThread());
@@ -170,18 +169,19 @@ MediaCodecDecoder::ConfigStatus MediaCodecAudioDecoder::ConfigureInternal(
return kConfigOk;
}
-void MediaCodecAudioDecoder::OnOutputFormatChanged() {
+void AudioMediaCodecDecoder::OnOutputFormatChanged() {
DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread());
DCHECK(media_codec_bridge_);
int old_sampling_rate = output_sampling_rate_;
- output_sampling_rate_ = media_codec_bridge_->GetOutputSamplingRate();
- if (output_sampling_rate_ != old_sampling_rate)
+ MediaCodecStatus status =
+ media_codec_bridge_->GetOutputSamplingRate(&output_sampling_rate_);
+ if (status != MEDIA_CODEC_OK || output_sampling_rate_ != old_sampling_rate)
ResetTimestampHelper();
}
-void MediaCodecAudioDecoder::Render(int buffer_index,
+void AudioMediaCodecDecoder::Render(int buffer_index,
size_t offset,
size_t size,
RenderMode render_mode,
@@ -202,8 +202,13 @@ void MediaCodecAudioDecoder::Render(int buffer_index,
const bool postpone = (render_mode == kRenderAfterPreroll);
- int64_t head_position =
- audio_codec->PlayOutputBuffer(buffer_index, size, offset, postpone);
+ int64_t head_position;
+ MediaCodecStatus status = audio_codec->PlayOutputBuffer(
+ buffer_index, size, offset, postpone, &head_position);
+ // TODO(timav,watk): This CHECK maintains the behavior of this call before
+ // we started catching CodecException and returning it as MEDIA_CODEC_ERROR.
+ // It needs to be handled some other way. http://crbug.com/585978
+ CHECK_EQ(status, MEDIA_CODEC_OK);
base::TimeTicks current_time = base::TimeTicks::Now();
@@ -263,7 +268,7 @@ void MediaCodecAudioDecoder::Render(int buffer_index,
CheckLastFrame(eos_encountered, false); // no delayed tasks
}
-void MediaCodecAudioDecoder::SetVolumeInternal() {
+void AudioMediaCodecDecoder::SetVolumeInternal() {
DCHECK(media_task_runner_->BelongsToCurrentThread());
if (media_codec_bridge_) {
@@ -272,7 +277,7 @@ void MediaCodecAudioDecoder::SetVolumeInternal() {
}
}
-void MediaCodecAudioDecoder::ResetTimestampHelper() {
+void AudioMediaCodecDecoder::ResetTimestampHelper() {
// Media thread or Decoder thread
// When this method is called on Media thread, decoder thread
// should not be running.
diff --git a/chromium/media/base/android/media_codec_audio_decoder.h b/chromium/media/base/android/audio_media_codec_decoder.h
index 434edf062ee..ce264b60ee6 100644
--- a/chromium/media/base/android/media_codec_audio_decoder.h
+++ b/chromium/media/base/android/audio_media_codec_decoder.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef MEDIA_BASE_ANDROID_MEDIA_CODEC_AUDIO_DECODER_H_
-#define MEDIA_BASE_ANDROID_MEDIA_CODEC_AUDIO_DECODER_H_
+#ifndef MEDIA_BASE_ANDROID_AUDIO_MEDIA_CODEC_DECODER_H_
+#define MEDIA_BASE_ANDROID_AUDIO_MEDIA_CODEC_DECODER_H_
#include <stddef.h>
#include <stdint.h>
@@ -16,12 +16,12 @@ namespace media {
class AudioTimestampHelper;
// Audio decoder for MediaCodecPlayer
-class MediaCodecAudioDecoder : public MediaCodecDecoder {
+class AudioMediaCodecDecoder : public MediaCodecDecoder {
public:
// For parameters see media_codec_decoder.h
// update_current_time_cb: callback that reports current playback time.
// Called for each rendered frame.
- MediaCodecAudioDecoder(
+ AudioMediaCodecDecoder(
const scoped_refptr<base::SingleThreadTaskRunner>& media_runner,
FrameStatistics* frame_statistics,
const base::Closure& request_data_cb,
@@ -31,7 +31,7 @@ class MediaCodecAudioDecoder : public MediaCodecDecoder {
const base::Closure& waiting_for_decryption_key_cb,
const base::Closure& error_cb,
const SetTimeCallback& update_current_time_cb);
- ~MediaCodecAudioDecoder() override;
+ ~AudioMediaCodecDecoder() override;
const char* class_name() const override;
@@ -95,9 +95,9 @@ class MediaCodecAudioDecoder : public MediaCodecDecoder {
// The time limit for the next frame to avoid underrun.
base::TimeTicks next_frame_time_limit_;
- DISALLOW_COPY_AND_ASSIGN(MediaCodecAudioDecoder);
+ DISALLOW_COPY_AND_ASSIGN(AudioMediaCodecDecoder);
};
} // namespace media
-#endif // MEDIA_BASE_ANDROID_MEDIA_CODEC_DECODER_H_
+#endif // MEDIA_BASE_ANDROID_AUDIO_MEDIA_CODEC_DECODER_H_
diff --git a/chromium/media/base/android/demuxer_stream_player_params.cc b/chromium/media/base/android/demuxer_stream_player_params.cc
index bafcb6e01fa..739105eb59f 100644
--- a/chromium/media/base/android/demuxer_stream_player_params.cc
+++ b/chromium/media/base/android/demuxer_stream_player_params.cc
@@ -17,14 +17,20 @@ DemuxerConfigs::DemuxerConfigs()
video_codec(kUnknownVideoCodec),
is_video_encrypted(false) {}
+DemuxerConfigs::DemuxerConfigs(const DemuxerConfigs& other) = default;
+
DemuxerConfigs::~DemuxerConfigs() {}
AccessUnit::AccessUnit() : is_end_of_stream(false), is_key_frame(false) {}
+AccessUnit::AccessUnit(const AccessUnit& other) = default;
+
AccessUnit::~AccessUnit() {}
DemuxerData::DemuxerData() : type(DemuxerStream::UNKNOWN) {}
+DemuxerData::DemuxerData(const DemuxerData& other) = default;
+
DemuxerData::~DemuxerData() {}
namespace {
diff --git a/chromium/media/base/android/demuxer_stream_player_params.h b/chromium/media/base/android/demuxer_stream_player_params.h
index f7619c60480..e0f98b7e08b 100644
--- a/chromium/media/base/android/demuxer_stream_player_params.h
+++ b/chromium/media/base/android/demuxer_stream_player_params.h
@@ -20,6 +20,7 @@ namespace media {
struct MEDIA_EXPORT DemuxerConfigs {
DemuxerConfigs();
+ DemuxerConfigs(const DemuxerConfigs& other);
~DemuxerConfigs();
AudioCodec audio_codec;
@@ -40,6 +41,7 @@ struct MEDIA_EXPORT DemuxerConfigs {
struct MEDIA_EXPORT AccessUnit {
AccessUnit();
+ AccessUnit(const AccessUnit& other);
~AccessUnit();
DemuxerStream::Status status;
@@ -55,6 +57,7 @@ struct MEDIA_EXPORT AccessUnit {
struct MEDIA_EXPORT DemuxerData {
DemuxerData();
+ DemuxerData(const DemuxerData& other);
~DemuxerData();
DemuxerStream::Type type;
diff --git a/chromium/media/base/android/media_codec_bridge.cc b/chromium/media/base/android/media_codec_bridge.cc
index ca5588f4e65..833d51ef890 100644
--- a/chromium/media/base/android/media_codec_bridge.cc
+++ b/chromium/media/base/android/media_codec_bridge.cc
@@ -42,20 +42,15 @@ MediaCodecStatus MediaCodecBridge::QueueSecureInputBuffer(
subsamples.size(), presentation_time);
}
-int MediaCodecBridge::GetOutputBuffersCount() {
- return 0;
-}
-
-size_t MediaCodecBridge::GetOutputBuffersCapacity() {
- return 0;
-}
-
bool MediaCodecBridge::FillInputBuffer(int index,
const uint8_t* data,
size_t size) {
uint8_t* dst = nullptr;
size_t capacity = 0;
- GetInputBuffer(index, &dst, &capacity);
+ if (GetInputBuffer(index, &dst, &capacity) != MEDIA_CODEC_OK) {
+ LOG(ERROR) << "GetInputBuffer failed";
+ return false;
+ }
CHECK(dst);
if (size > capacity) {
diff --git a/chromium/media/base/android/media_codec_bridge.h b/chromium/media/base/android/media_codec_bridge.h
index 41a7351ff63..25d61f97cc3 100644
--- a/chromium/media/base/android/media_codec_bridge.h
+++ b/chromium/media/base/android/media_codec_bridge.h
@@ -17,6 +17,7 @@
#include "base/time/time.h"
#include "media/base/android/media_codec_util.h"
#include "media/base/media_export.h"
+#include "ui/gfx/geometry/size.h"
namespace media {
@@ -49,7 +50,7 @@ class MEDIA_EXPORT MediaCodecBridge {
// DequeueInputBuffer() and DequeueOutputBuffer() become invalid.
// Please note that this clears all the inputs in the media codec. In other
// words, there will be no outputs until new input is provided.
- // Returns MEDIA_CODEC_ERROR if an unexpected error happens, or Media_CODEC_OK
+ // Returns MEDIA_CODEC_ERROR if an unexpected error happens, or MEDIA_CODEC_OK
// otherwise.
virtual MediaCodecStatus Reset() = 0;
@@ -64,13 +65,15 @@ class MEDIA_EXPORT MediaCodecBridge {
// instance -> StartAudio/Video() is recommended.
virtual void Stop() = 0;
- // Used for getting output format. This is valid after DequeueInputBuffer()
- // returns a format change by returning INFO_OUTPUT_FORMAT_CHANGED
- virtual void GetOutputFormat(int* width, int* height) = 0;
+ // Used for getting the output size. This is valid after DequeueInputBuffer()
+ // returns a format change by returning INFO_OUTPUT_FORMAT_CHANGED.
+ // Returns MEDIA_CODEC_ERROR if an error occurs, or MEDIA_CODEC_OK otherwise.
+ virtual MediaCodecStatus GetOutputSize(gfx::Size* size) = 0;
// Used for checking for new sampling rate after DequeueInputBuffer() returns
// INFO_OUTPUT_FORMAT_CHANGED
- virtual int GetOutputSamplingRate() = 0;
+ // Returns MEDIA_CODEC_ERROR if an error occurs, or MEDIA_CODEC_OK otherwise.
+ virtual MediaCodecStatus GetOutputSamplingRate(int* sampling_rate) = 0;
// Submits a byte array to the given input buffer. Call this after getting an
// available buffer from DequeueInputBuffer(). If |data| is NULL, assume the
@@ -143,25 +146,19 @@ class MEDIA_EXPORT MediaCodecBridge {
// configuring this video decoder you can optionally render the buffer.
virtual void ReleaseOutputBuffer(int index, bool render) = 0;
- // Returns the number of output buffers used by the codec.
- // TODO(qinmin): this call is deprecated in Lollipop.
- virtual int GetOutputBuffersCount();
-
- // Returns the capacity of each output buffer used by the codec.
- // TODO(qinmin): this call is deprecated in Lollipop.
- virtual size_t GetOutputBuffersCapacity();
-
// Returns an input buffer's base pointer and capacity.
- virtual void GetInputBuffer(int input_buffer_index,
- uint8_t** data,
- size_t* capacity) = 0;
-
- // Copy |dst_size| bytes from output buffer |index|'s |offset| onwards into
- // |*dst|.
- virtual bool CopyFromOutputBuffer(int index,
- size_t offset,
- void* dst,
- int dst_size) = 0;
+ virtual MediaCodecStatus GetInputBuffer(int input_buffer_index,
+ uint8_t** data,
+ size_t* capacity) = 0;
+
+ // Copy |num| bytes from output buffer |index|'s |offset| into the memory
+ // region pointed to by |dst|. To avoid overflows, the size of both source
+ // and destination must be at least |num| bytes, and should not overlap.
+ // Returns MEDIA_CODEC_ERROR if an error occurs, or MEDIA_CODEC_OK otherwise.
+ virtual MediaCodecStatus CopyFromOutputBuffer(int index,
+ size_t offset,
+ void* dst,
+ size_t num) = 0;
protected:
MediaCodecBridge();
diff --git a/chromium/media/base/android/media_codec_decoder_unittest.cc b/chromium/media/base/android/media_codec_decoder_unittest.cc
index ea0ffb35f36..087aec5689e 100644
--- a/chromium/media/base/android/media_codec_decoder_unittest.cc
+++ b/chromium/media/base/android/media_codec_decoder_unittest.cc
@@ -9,13 +9,13 @@
#include "base/macros.h"
#include "base/thread_task_runner_handle.h"
#include "base/timer/timer.h"
-#include "media/base/android/media_codec_audio_decoder.h"
+#include "media/base/android/audio_media_codec_decoder.h"
#include "media/base/android/media_codec_util.h"
-#include "media/base/android/media_codec_video_decoder.h"
#include "media/base/android/media_statistics.h"
#include "media/base/android/sdk_media_codec_bridge.h"
#include "media/base/android/test_data_factory.h"
#include "media/base/android/test_statistics.h"
+#include "media/base/android/video_media_codec_decoder.h"
#include "media/base/timestamp_constants.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gl/android/surface_texture.h"
@@ -266,7 +266,7 @@ bool MediaCodecDecoderTest::WaitForCondition(const Predicate& condition,
}
void MediaCodecDecoderTest::CreateAudioDecoder() {
- decoder_ = scoped_ptr<MediaCodecDecoder>(new MediaCodecAudioDecoder(
+ decoder_ = scoped_ptr<MediaCodecDecoder>(new AudioMediaCodecDecoder(
task_runner_, &frame_statistics_,
base::Bind(&MediaCodecDecoderTest::OnDataRequested,
base::Unretained(this)),
@@ -284,7 +284,7 @@ void MediaCodecDecoderTest::CreateAudioDecoder() {
}
void MediaCodecDecoderTest::CreateVideoDecoder() {
- decoder_ = scoped_ptr<MediaCodecDecoder>(new MediaCodecVideoDecoder(
+ decoder_ = scoped_ptr<MediaCodecDecoder>(new VideoMediaCodecDecoder(
task_runner_, &frame_statistics_,
base::Bind(&MediaCodecDecoderTest::OnDataRequested,
base::Unretained(this)),
@@ -320,8 +320,8 @@ void MediaCodecDecoderTest::SetVideoSurface() {
surface_texture_ = gfx::SurfaceTexture::Create(0);
gfx::ScopedJavaSurface surface(surface_texture_.get());
ASSERT_NE(nullptr, decoder_.get());
- MediaCodecVideoDecoder* video_decoder =
- static_cast<MediaCodecVideoDecoder*>(decoder_.get());
+ VideoMediaCodecDecoder* video_decoder =
+ static_cast<VideoMediaCodecDecoder*>(decoder_.get());
video_decoder->SetVideoSurface(std::move(surface));
}
@@ -446,8 +446,8 @@ TEST_F(MediaCodecDecoderTest, VideoConfigureInvalidSurface) {
// Release the surface texture.
surface_texture = NULL;
- MediaCodecVideoDecoder* video_decoder =
- static_cast<MediaCodecVideoDecoder*>(decoder_.get());
+ VideoMediaCodecDecoder* video_decoder =
+ static_cast<VideoMediaCodecDecoder*>(decoder_.get());
video_decoder->SetVideoSurface(std::move(surface));
EXPECT_EQ(MediaCodecDecoder::kConfigFailure, decoder_->Configure(nullptr));
@@ -505,7 +505,7 @@ TEST_F(MediaCodecDecoderTest, AudioStartWithoutConfigure) {
}
// http://crbug.com/518900
-TEST_F(MediaCodecDecoderTest, AudioPlayTillCompletion) {
+TEST_F(MediaCodecDecoderTest, DISABLED_AudioPlayTillCompletion) {
SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
DVLOG(0) << "AudioPlayTillCompletion started";
diff --git a/chromium/media/base/android/media_codec_player.cc b/chromium/media/base/android/media_codec_player.cc
index 565c7452c3a..b5c3b1b4d4c 100644
--- a/chromium/media/base/android/media_codec_player.cc
+++ b/chromium/media/base/android/media_codec_player.cc
@@ -13,11 +13,11 @@
#include "base/logging.h"
#include "base/thread_task_runner_handle.h"
#include "base/threading/thread.h"
-#include "media/base/android/media_codec_audio_decoder.h"
-#include "media/base/android/media_codec_video_decoder.h"
+#include "media/base/android/audio_media_codec_decoder.h"
#include "media/base/android/media_drm_bridge.h"
#include "media/base/android/media_player_manager.h"
#include "media/base/android/media_task_runner.h"
+#include "media/base/android/video_media_codec_decoder.h"
#include "media/base/bind_to_current_loop.h"
#include "media/base/timestamp_constants.h"
@@ -41,11 +41,13 @@ MediaCodecPlayer::MediaCodecPlayer(
base::WeakPtr<MediaPlayerManager> manager,
const OnDecoderResourcesReleasedCB& on_decoder_resources_released_cb,
scoped_ptr<DemuxerAndroid> demuxer,
- const GURL& frame_url)
+ const GURL& frame_url,
+ int media_session_id)
: MediaPlayerAndroid(player_id,
manager.get(),
on_decoder_resources_released_cb,
- frame_url),
+ frame_url,
+ media_session_id),
ui_task_runner_(base::ThreadTaskRunnerHandle::Get()),
demuxer_(std::move(demuxer)),
state_(kStatePaused),
@@ -102,6 +104,10 @@ MediaCodecPlayer::~MediaCodecPlayer()
audio_decoder_->ReleaseDecoderResources();
if (cdm_) {
+ // Cancel previously registered callback (if any).
+ static_cast<MediaDrmBridge*>(cdm_.get())
+ ->SetMediaCryptoReadyCB(MediaDrmBridge::MediaCryptoReadyCB());
+
DCHECK(cdm_registration_id_);
static_cast<MediaDrmBridge*>(cdm_.get())
->UnregisterPlayer(cdm_registration_id_);
@@ -359,11 +365,11 @@ void MediaCodecPlayer::Release() {
}
}
-void MediaCodecPlayer::SetVolume(double volume) {
- RUN_ON_MEDIA_THREAD(SetVolume, volume);
+void MediaCodecPlayer::UpdateEffectiveVolumeInternal(double effective_volume) {
+ RUN_ON_MEDIA_THREAD(UpdateEffectiveVolumeInternal, effective_volume);
- DVLOG(1) << __FUNCTION__ << " " << volume;
- audio_decoder_->SetVolume(volume);
+ DVLOG(1) << __FUNCTION__ << " " << effective_volume;
+ audio_decoder_->SetVolume(effective_volume);
}
bool MediaCodecPlayer::HasAudio() const {
@@ -1347,7 +1353,7 @@ void MediaCodecPlayer::CreateDecoders() {
media_stat_.reset(new MediaStatistics());
- audio_decoder_.reset(new MediaCodecAudioDecoder(
+ audio_decoder_.reset(new AudioMediaCodecDecoder(
GetMediaTaskRunner(), &media_stat_->audio_frame_stats(),
base::Bind(&MediaCodecPlayer::RequestDemuxerData, media_weak_this_,
DemuxerStream::AUDIO),
@@ -1359,11 +1365,10 @@ void MediaCodecPlayer::CreateDecoders() {
DemuxerStream::AUDIO),
base::Bind(&MediaCodecPlayer::OnMissingKeyReported, media_weak_this_,
DemuxerStream::AUDIO),
- internal_error_cb_,
- base::Bind(&MediaCodecPlayer::OnTimeIntervalUpdate, media_weak_this_,
- DemuxerStream::AUDIO)));
+ internal_error_cb_, base::Bind(&MediaCodecPlayer::OnTimeIntervalUpdate,
+ media_weak_this_, DemuxerStream::AUDIO)));
- video_decoder_.reset(new MediaCodecVideoDecoder(
+ video_decoder_.reset(new VideoMediaCodecDecoder(
GetMediaTaskRunner(), &media_stat_->video_frame_stats(),
base::Bind(&MediaCodecPlayer::RequestDemuxerData, media_weak_this_,
DemuxerStream::VIDEO),
@@ -1375,9 +1380,8 @@ void MediaCodecPlayer::CreateDecoders() {
DemuxerStream::VIDEO),
base::Bind(&MediaCodecPlayer::OnMissingKeyReported, media_weak_this_,
DemuxerStream::VIDEO),
- internal_error_cb_,
- base::Bind(&MediaCodecPlayer::OnTimeIntervalUpdate, media_weak_this_,
- DemuxerStream::VIDEO),
+ internal_error_cb_, base::Bind(&MediaCodecPlayer::OnTimeIntervalUpdate,
+ media_weak_this_, DemuxerStream::VIDEO),
base::Bind(&MediaCodecPlayer::OnVideoResolutionChanged,
media_weak_this_)));
}
diff --git a/chromium/media/base/android/media_codec_player.h b/chromium/media/base/android/media_codec_player.h
index eca83e225e0..83b4164d5e9 100644
--- a/chromium/media/base/android/media_codec_player.h
+++ b/chromium/media/base/android/media_codec_player.h
@@ -158,8 +158,8 @@
namespace media {
-class MediaCodecAudioDecoder;
-class MediaCodecVideoDecoder;
+class AudioMediaCodecDecoder;
+class VideoMediaCodecDecoder;
class MEDIA_EXPORT MediaCodecPlayer : public MediaPlayerAndroid,
public DemuxerAndroidClient {
@@ -191,7 +191,8 @@ class MEDIA_EXPORT MediaCodecPlayer : public MediaPlayerAndroid,
base::WeakPtr<MediaPlayerManager> manager,
const OnDecoderResourcesReleasedCB& on_decoder_resources_released_cb,
scoped_ptr<DemuxerAndroid> demuxer,
- const GURL& frame_url);
+ const GURL& frame_url,
+ int media_session_id);
~MediaCodecPlayer() override;
// A helper method that performs the media thread part of initialization.
@@ -204,7 +205,6 @@ class MEDIA_EXPORT MediaCodecPlayer : public MediaPlayerAndroid,
void Pause(bool is_media_related_action) override;
void SeekTo(base::TimeDelta timestamp) override;
void Release() override;
- void SetVolume(double volume) override;
bool HasVideo() const override;
bool HasAudio() const override;
int GetVideoWidth() override;
@@ -268,6 +268,7 @@ class MEDIA_EXPORT MediaCodecPlayer : public MediaPlayerAndroid,
};
// MediaPlayerAndroid implementation.
+ void UpdateEffectiveVolumeInternal(double effective_volume) override;
// This method requests playback permission from the manager on UI
// thread, passing total duration and whether the media has audio
@@ -344,8 +345,8 @@ class MEDIA_EXPORT MediaCodecPlayer : public MediaPlayerAndroid,
// Major components: demuxer, audio and video decoders.
scoped_ptr<DemuxerAndroid> demuxer_;
- scoped_ptr<MediaCodecAudioDecoder> audio_decoder_;
- scoped_ptr<MediaCodecVideoDecoder> video_decoder_;
+ scoped_ptr<AudioMediaCodecDecoder> audio_decoder_;
+ scoped_ptr<VideoMediaCodecDecoder> video_decoder_;
// The state of the state machine.
PlayerState state_;
diff --git a/chromium/media/base/android/media_codec_player_unittest.cc b/chromium/media/base/android/media_codec_player_unittest.cc
index 5281c4280f1..eb3f827bbba 100644
--- a/chromium/media/base/android/media_codec_player_unittest.cc
+++ b/chromium/media/base/android/media_codec_player_unittest.cc
@@ -382,7 +382,7 @@ class MockDemuxerAndroid : public DemuxerAndroid {
// Conditions to wait for.
bool IsInitialized() const { return client_; }
- bool HasPendingConfigs() const { return pending_configs_; }
+ bool HasPendingConfigs() const { return !!pending_configs_; }
bool ReceivedSeekRequest() const { return num_seeks_ > 0; }
bool ReceivedBrowserSeekRequest() const { return num_browser_seeks_ > 0; }
@@ -644,7 +644,7 @@ void MediaCodecPlayerTest::CreatePlayer() {
manager_.GetWeakPtr(),
base::Bind(&MockMediaPlayerManager::OnMediaResourcesRequested,
base::Unretained(&manager_)),
- scoped_ptr<MockDemuxerAndroid>(demuxer_), GURL());
+ scoped_ptr<MockDemuxerAndroid>(demuxer_), GURL(), kDefaultMediaSessionId);
DCHECK(player_);
}
@@ -938,7 +938,7 @@ TEST_F(MediaCodecPlayerTest, SetAudioVideoConfigsAfterPlayerCreation) {
EXPECT_EQ(240, manager_.media_metadata_.height);
}
-TEST_F(MediaCodecPlayerTest, AudioPlayTillCompletion) {
+TEST_F(MediaCodecPlayerTest, DISABLED_AudioPlayTillCompletion) {
SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
base::TimeDelta duration = base::TimeDelta::FromMilliseconds(1000);
@@ -1053,7 +1053,7 @@ TEST_F(MediaCodecPlayerTest, VideoNoPermission) {
}
// http://crbug.com/518900
-TEST_F(MediaCodecPlayerTest, AudioSeekAfterStop) {
+TEST_F(MediaCodecPlayerTest, DISABLED_AudioSeekAfterStop) {
SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
// Play for 300 ms, then Pause, then Seek to beginning. The playback should
@@ -1112,7 +1112,7 @@ TEST_F(MediaCodecPlayerTest, AudioSeekAfterStop) {
&MockMediaPlayerManager::IsSeekCompleted, base::Unretained(&manager_))));
}
-TEST_F(MediaCodecPlayerTest, AudioSeekThenPlay) {
+TEST_F(MediaCodecPlayerTest, DISABLED_AudioSeekThenPlay) {
SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
// Issue Seek command immediately followed by Start. The playback should
@@ -1146,7 +1146,7 @@ TEST_F(MediaCodecPlayerTest, AudioSeekThenPlay) {
&MockMediaPlayerManager::IsSeekCompleted, base::Unretained(&manager_))));
}
-TEST_F(MediaCodecPlayerTest, AudioSeekThenPlayThenConfig) {
+TEST_F(MediaCodecPlayerTest, DISABLED_AudioSeekThenPlayThenConfig) {
SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
// Issue Seek command immediately followed by Start but without prior demuxer
@@ -1186,7 +1186,7 @@ TEST_F(MediaCodecPlayerTest, AudioSeekThenPlayThenConfig) {
}
// http://crbug.com/518900
-TEST_F(MediaCodecPlayerTest, AudioSeekWhilePlaying) {
+TEST_F(MediaCodecPlayerTest, DISABLED_AudioSeekWhilePlaying) {
SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
// Play for 300 ms, then issue several Seek commands in the row.
@@ -1265,7 +1265,7 @@ TEST_F(MediaCodecPlayerTest, VideoReplaceSurface) {
EXPECT_LE(duration, manager_.pts_stat_.max());
}
-TEST_F(MediaCodecPlayerTest, VideoRemoveAndSetSurface) {
+TEST_F(MediaCodecPlayerTest, DISABLED_VideoRemoveAndSetSurface) {
SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
base::TimeDelta duration = base::TimeDelta::FromMilliseconds(1000);
@@ -1315,7 +1315,7 @@ TEST_F(MediaCodecPlayerTest, VideoRemoveAndSetSurface) {
}
// http://crbug.com/518900
-TEST_F(MediaCodecPlayerTest, VideoReleaseAndStart) {
+TEST_F(MediaCodecPlayerTest, DISABLED_VideoReleaseAndStart) {
SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
base::TimeDelta duration = base::TimeDelta::FromMilliseconds(1000);
@@ -1363,7 +1363,7 @@ TEST_F(MediaCodecPlayerTest, VideoReleaseAndStart) {
EXPECT_LE(max_pts_before_backgrounding, manager_.pts_stat_.max());
}
-TEST_F(MediaCodecPlayerTest, VideoSeekAndRelease) {
+TEST_F(MediaCodecPlayerTest, DISABLED_VideoSeekAndRelease) {
SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
base::TimeDelta duration = base::TimeDelta::FromMilliseconds(2000);
@@ -1526,7 +1526,7 @@ TEST_F(MediaCodecPlayerTest, VideoPrerollAfterSeek) {
EXPECT_EQ(6, manager_.pts_stat_.num_values());
}
-TEST_F(MediaCodecPlayerTest, AVPrerollAudioWaitsForVideo) {
+TEST_F(MediaCodecPlayerTest, DISABLED_AVPrerollAudioWaitsForVideo) {
SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
// Test that during prerolling neither audio nor video plays and that both
@@ -1577,7 +1577,7 @@ TEST_F(MediaCodecPlayerTest, AVPrerollAudioWaitsForVideo) {
EXPECT_TRUE(AlmostEqual(seek_position, manager_.pts_stat_.min(), 25));
}
-TEST_F(MediaCodecPlayerTest, AVPrerollReleaseAndRestart) {
+TEST_F(MediaCodecPlayerTest, DISABLED_AVPrerollReleaseAndRestart) {
SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
// Test that player will resume prerolling if prerolling is interrupted by
@@ -1651,7 +1651,7 @@ TEST_F(MediaCodecPlayerTest, AVPrerollReleaseAndRestart) {
EXPECT_TRUE(AlmostEqual(seek_position, manager_.pts_stat_.min(), 50));
}
-TEST_F(MediaCodecPlayerTest, AVPrerollStopAndRestart) {
+TEST_F(MediaCodecPlayerTest, DISABLED_AVPrerollStopAndRestart) {
SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
// Test that if Pause() happens during the preroll phase,
@@ -1743,7 +1743,7 @@ TEST_F(MediaCodecPlayerTest, AVPrerollStopAndRestart) {
EXPECT_TRUE(AlmostEqual(seek_position, manager_.pts_stat_.min(), 25));
}
-TEST_F(MediaCodecPlayerTest, AVPrerollVideoEndsWhilePrerolling) {
+TEST_F(MediaCodecPlayerTest, DISABLED_AVPrerollVideoEndsWhilePrerolling) {
SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
// Test that when one stream ends in the preroll phase and another is not
@@ -1828,7 +1828,7 @@ TEST_F(MediaCodecPlayerTest, AVPrerollVideoEndsWhilePrerolling) {
DVLOG(0) << "AVPrerollVideoEndsWhilePrerolling: end";
}
-TEST_F(MediaCodecPlayerTest, VideoConfigChangeWhilePlaying) {
+TEST_F(MediaCodecPlayerTest, DISABLED_VideoConfigChangeWhilePlaying) {
SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
// Test that video only playback continues after video config change.
@@ -1898,7 +1898,8 @@ TEST_F(MediaCodecPlayerTest, VideoConfigChangeWhilePlaying) {
manager_.render_stat_[DemuxerStream::VIDEO].num_values());
}
-TEST_F(MediaCodecPlayerTest, AVVideoConfigChangeWhilePlaying) {
+// https://crbug.com/587195
+TEST_F(MediaCodecPlayerTest, DISABLED_AVVideoConfigChangeWhilePlaying) {
SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
// Test that A/V playback continues after video config change.
@@ -1945,7 +1946,7 @@ TEST_F(MediaCodecPlayerTest, AVVideoConfigChangeWhilePlaying) {
manager_.render_stat_[DemuxerStream::AUDIO].num_values());
}
-TEST_F(MediaCodecPlayerTest, AVAudioConfigChangeWhilePlaying) {
+TEST_F(MediaCodecPlayerTest, DISABLED_AVAudioConfigChangeWhilePlaying) {
SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
// Test that A/V playback continues after audio config change.
@@ -1989,7 +1990,7 @@ TEST_F(MediaCodecPlayerTest, AVAudioConfigChangeWhilePlaying) {
manager_.render_stat_[DemuxerStream::AUDIO].num_values());
}
-TEST_F(MediaCodecPlayerTest, AVSimultaneousConfigChange_1) {
+TEST_F(MediaCodecPlayerTest, DISABLED_AVSimultaneousConfigChange_1) {
SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
// Test that the playback continues if audio and video config changes happen
@@ -2036,7 +2037,7 @@ TEST_F(MediaCodecPlayerTest, AVSimultaneousConfigChange_1) {
manager_.render_stat_[DemuxerStream::AUDIO].num_values());
}
-TEST_F(MediaCodecPlayerTest, AVSimultaneousConfigChange_2) {
+TEST_F(MediaCodecPlayerTest, DISABLED_AVSimultaneousConfigChange_2) {
SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
// Test that the playback continues if audio and video config changes happen
@@ -2084,7 +2085,7 @@ TEST_F(MediaCodecPlayerTest, AVSimultaneousConfigChange_2) {
manager_.render_stat_[DemuxerStream::AUDIO].num_values());
}
-TEST_F(MediaCodecPlayerTest, AVAudioEndsAcrossVideoConfigChange) {
+TEST_F(MediaCodecPlayerTest, DISABLED_AVAudioEndsAcrossVideoConfigChange) {
SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
// Test that audio can end while video config change processing.
@@ -2134,7 +2135,8 @@ TEST_F(MediaCodecPlayerTest, AVAudioEndsAcrossVideoConfigChange) {
EXPECT_EQ(video_duration, manager_.pts_stat_.max());
}
-TEST_F(MediaCodecPlayerTest, AVVideoEndsAcrossAudioConfigChange) {
+// https://crbug.com/587195
+TEST_F(MediaCodecPlayerTest, DISABLED_AVVideoEndsAcrossAudioConfigChange) {
SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
// Test that video can end while audio config change processing.
@@ -2173,7 +2175,7 @@ TEST_F(MediaCodecPlayerTest, AVVideoEndsAcrossAudioConfigChange) {
manager_.render_stat_[DemuxerStream::AUDIO].num_values());
}
-TEST_F(MediaCodecPlayerTest, AVPrerollAcrossVideoConfigChange) {
+TEST_F(MediaCodecPlayerTest, DISABLED_AVPrerollAcrossVideoConfigChange) {
SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
// Test that preroll continues if interrupted by video config change.
@@ -2222,7 +2224,7 @@ TEST_F(MediaCodecPlayerTest, AVPrerollAcrossVideoConfigChange) {
EXPECT_TRUE(AlmostEqual(seek_position, manager_.pts_stat_.min(), 25));
}
-TEST_F(MediaCodecPlayerTest, AVPrerollAcrossAudioConfigChange) {
+TEST_F(MediaCodecPlayerTest, DISABLED_AVPrerollAcrossAudioConfigChange) {
SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
// Test that preroll continues if interrupted by video config change.
diff --git a/chromium/media/base/android/media_codec_util.cc b/chromium/media/base/android/media_codec_util.cc
index 7650ecd7f78..21877cc8fe3 100644
--- a/chromium/media/base/android/media_codec_util.cc
+++ b/chromium/media/base/android/media_codec_util.cc
@@ -25,8 +25,7 @@ using base::android::ScopedJavaLocalRef;
namespace media {
-// static
-const std::string CodecTypeToAndroidMimeType(const std::string& codec) {
+static std::string CodecTypeToAndroidMimeType(const std::string& codec) {
// TODO(xhwang): Shall we handle more detailed strings like "mp4a.40.2"?
if (codec == "avc1")
return "video/avc";
@@ -45,34 +44,9 @@ const std::string CodecTypeToAndroidMimeType(const std::string& codec) {
return std::string();
}
-// TODO(qinmin): using a map to help all the conversions in this class.
-const std::string AndroidMimeTypeToCodecType(const std::string& mime) {
- if (mime == "video/mp4v-es")
- return "mp4v";
- if (mime == "video/avc")
- return "avc1";
- if (mime == "video/hevc")
- return "hvc1";
- if (mime == "video/x-vnd.on2.vp8")
- return "vp8";
- if (mime == "video/x-vnd.on2.vp9")
- return "vp9";
- if (mime == "audio/mp4a-latm")
- return "mp4a";
- if (mime == "audio/mpeg")
- return "mp3";
- if (mime == "audio/vorbis")
- return "vorbis";
- if (mime == "audio/opus")
- return "opus";
- return std::string();
-}
-
-std::string GetDefaultCodecName(const std::string& mime_type,
- MediaCodecDirection direction) {
- if (!MediaCodecUtil::IsMediaCodecAvailable())
- return std::string();
-
+static std::string GetDefaultCodecName(const std::string& mime_type,
+ MediaCodecDirection direction) {
+ DCHECK(MediaCodecUtil::IsMediaCodecAvailable());
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jstring> j_mime = ConvertUTF8ToJavaString(env, mime_type);
ScopedJavaLocalRef<jstring> j_codec_name =
@@ -80,47 +54,11 @@ std::string GetDefaultCodecName(const std::string& mime_type,
return ConvertJavaStringToUTF8(env, j_codec_name.obj());
}
-bool SupportsGetName() {
- // MediaCodec.getName() is only available on JB MR2 and greater.
- return base::android::BuildInfo::GetInstance()->sdk_int() >= 18;
-}
-
-// Represents supported codecs on android.
-// TODO(qinmin): Currently the codecs string only contains one codec. Do we
-// need to support codecs separated by comma. (e.g. "vp8" -> "vp8, vp8.0")?
-struct CodecsInfo {
- std::string codecs; // E.g. "vp8" or "avc1".
- std::string name; // E.g. "OMX.google.vp8.decoder".
- MediaCodecDirection direction;
-};
-
-// Get a list of supported codecs.
-std::vector<CodecsInfo> GetCodecsInfo() {
- std::vector<CodecsInfo> codecs_info;
- if (!MediaCodecUtil::IsMediaCodecAvailable())
- return codecs_info;
-
+static bool IsDecoderSupportedByDevice(const std::string& mime_type) {
+ DCHECK(MediaCodecUtil::IsMediaCodecAvailable());
JNIEnv* env = AttachCurrentThread();
- std::string mime_type;
- ScopedJavaLocalRef<jobjectArray> j_codec_info_array =
- Java_MediaCodecUtil_getCodecsInfo(env);
- jsize len = env->GetArrayLength(j_codec_info_array.obj());
- for (jsize i = 0; i < len; ++i) {
- ScopedJavaLocalRef<jobject> j_info(
- env, env->GetObjectArrayElement(j_codec_info_array.obj(), i));
- ScopedJavaLocalRef<jstring> j_codec_type =
- Java_CodecInfo_codecType(env, j_info.obj());
- ConvertJavaStringToUTF8(env, j_codec_type.obj(), &mime_type);
- ScopedJavaLocalRef<jstring> j_codec_name =
- Java_CodecInfo_codecName(env, j_info.obj());
- CodecsInfo info;
- info.codecs = AndroidMimeTypeToCodecType(mime_type);
- ConvertJavaStringToUTF8(env, j_codec_name.obj(), &info.name);
- info.direction = static_cast<MediaCodecDirection>(
- Java_CodecInfo_direction(env, j_info.obj()));
- codecs_info.push_back(info);
- }
- return codecs_info;
+ ScopedJavaLocalRef<jstring> j_mime = ConvertUTF8ToJavaString(env, mime_type);
+ return Java_MediaCodecUtil_isDecoderSupportedForDevice(env, j_mime.obj());
}
// static
@@ -183,40 +121,33 @@ bool MediaCodecUtil::IsKnownUnaccelerated(const std::string& mime_type,
if (!IsMediaCodecAvailable())
return true;
- std::string codec_name;
- if (SupportsGetName()) {
- codec_name = GetDefaultCodecName(mime_type, direction);
- } else {
- std::string codec_type = AndroidMimeTypeToCodecType(mime_type);
- std::vector<CodecsInfo> codecs_info = GetCodecsInfo();
- for (size_t i = 0; i < codecs_info.size(); ++i) {
- if (codecs_info[i].codecs == codec_type &&
- codecs_info[i].direction == direction) {
- codec_name = codecs_info[i].name;
- break;
- }
- }
+ std::string codec_name = GetDefaultCodecName(mime_type, direction);
+ DVLOG(1) << __FUNCTION__ << "Default codec for " << mime_type << " : "
+ << codec_name << ", direction: " << direction;
+ if (!codec_name.size())
+ return true;
+
+ // MediaTek hardware vp8 is known slower than the software implementation.
+ // MediaTek hardware vp9 is known crashy, see http://crbug.com/446974 and
+ // http://crbug.com/597836.
+ if (base::StartsWith(codec_name, "OMX.MTK.", base::CompareCase::SENSITIVE)) {
+ if (mime_type == "video/x-vnd.on2.vp8")
+ return true;
+
+ if (mime_type == "video/x-vnd.on2.vp9")
+ return base::android::BuildInfo::GetInstance()->sdk_int() < 21;
+
+ return false;
}
- DVLOG(1) << __PRETTY_FUNCTION__ << "Default codec for " << mime_type << " : "
- << codec_name;
+
// It would be nice if MediaCodecInfo externalized some notion of
// HW-acceleration but it doesn't. Android Media guidance is that the
// "OMX.google" prefix is always used for SW decoders, so that's what we
// use. "OMX.SEC.*" codec is Samsung software implementation - report it
- // as unaccelerated as well. Also temporary blacklist Exynos and MediaTek
- // devices while HW decoder video freezes and distortions are
- // investigated - http://crbug.com/446974.
- if (codec_name.length() > 0) {
- return (base::StartsWith(codec_name, "OMX.google.",
- base::CompareCase::SENSITIVE) ||
- base::StartsWith(codec_name, "OMX.SEC.",
- base::CompareCase::SENSITIVE) ||
- base::StartsWith(codec_name, "OMX.MTK.",
- base::CompareCase::SENSITIVE) ||
- base::StartsWith(codec_name, "OMX.Exynos.",
- base::CompareCase::SENSITIVE));
- }
- return true;
+ // as unaccelerated as well.
+ return base::StartsWith(codec_name, "OMX.google.",
+ base::CompareCase::SENSITIVE) ||
+ base::StartsWith(codec_name, "OMX.SEC.", base::CompareCase::SENSITIVE);
}
// static
@@ -245,4 +176,35 @@ bool MediaCodecUtil::RegisterMediaCodecUtil(JNIEnv* env) {
return RegisterNativesImpl(env);
}
+// static
+bool MediaCodecUtil::IsVp8DecoderAvailable() {
+ return IsMediaCodecAvailable() &&
+ IsDecoderSupportedByDevice(CodecTypeToAndroidMimeType("vp8"));
+}
+
+// static
+bool MediaCodecUtil::IsVp8EncoderAvailable() {
+ // Currently the vp8 encoder and decoder blacklists cover the same devices,
+ // but we have a second method for clarity in future issues.
+ return IsVp8DecoderAvailable();
+}
+
+// static
+bool MediaCodecUtil::IsVp9DecoderAvailable() {
+ return IsMediaCodecAvailable() &&
+ IsDecoderSupportedByDevice(CodecTypeToAndroidMimeType("vp9"));
+}
+
+// static
+bool MediaCodecUtil::IsSurfaceViewOutputSupported() {
+ // Disable SurfaceView output for the Samsung Galaxy S3; it does not work
+ // well enough for even 360p24 H264 playback. http://crbug.com/602870.
+ //
+ // Notably this is not 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.
+ return !base::StartsWith(base::android::BuildInfo::GetInstance()->model(),
+ "GT-I9300", base::CompareCase::INSENSITIVE_ASCII);
+}
+
} // namespace media
diff --git a/chromium/media/base/android/media_codec_util.h b/chromium/media/base/android/media_codec_util.h
index 3c2d020eea4..211d9f038ba 100644
--- a/chromium/media/base/android/media_codec_util.h
+++ b/chromium/media/base/android/media_codec_util.h
@@ -65,6 +65,16 @@ class MEDIA_EXPORT MediaCodecUtil {
static bool IsHLSPath(const GURL& url);
static bool RegisterMediaCodecUtil(JNIEnv* env);
+
+ // Indicates if the vp8 decoder or encoder is available on this device.
+ static bool IsVp8DecoderAvailable();
+ static bool IsVp8EncoderAvailable();
+
+ // Indicates if the vp9 decoder is available on this device.
+ static bool IsVp9DecoderAvailable();
+
+ // Indicates if SurfaceView and MediaCodec work well together on this device.
+ static bool IsSurfaceViewOutputSupported();
};
} // namespace media
diff --git a/chromium/media/base/android/media_drm_bridge.cc b/chromium/media/base/android/media_drm_bridge.cc
index 0633c31b986..96821aec43c 100644
--- a/chromium/media/base/android/media_drm_bridge.cc
+++ b/chromium/media/base/android/media_drm_bridge.cc
@@ -26,6 +26,7 @@
#include "base/thread_task_runner_handle.h"
#include "jni/MediaDrmBridge_jni.h"
#include "media/base/android/media_client_android.h"
+#include "media/base/android/media_codec_util.h"
#include "media/base/android/media_drm_bridge_delegate.h"
#include "media/base/android/provision_fetcher.h"
#include "media/base/cdm_key_information.h"
@@ -174,8 +175,12 @@ base::LazyInstance<KeySystemManager>::Leaky g_key_system_manager =
// resolved.
bool IsKeySystemSupportedWithTypeImpl(const std::string& key_system,
const std::string& container_mime_type) {
- if (!MediaDrmBridge::IsAvailable())
+ DCHECK(MediaDrmBridge::IsAvailable());
+
+ if (key_system.empty()) {
+ NOTREACHED();
return false;
+ }
UUID scheme_uuid = g_key_system_manager.Get().GetUUID(key_system);
if (scheme_uuid.empty())
@@ -215,10 +220,7 @@ std::string GetSecurityLevelString(
return "";
}
-} // namespace
-
-// static
-bool MediaDrmBridge::IsAvailable() {
+bool AreMediaDrmApisAvailable() {
if (base::android::BuildInfo::GetInstance()->sdk_int() < 19)
return false;
@@ -233,6 +235,15 @@ bool MediaDrmBridge::IsAvailable() {
return true;
}
+} // namespace
+
+// MediaDrm is not generally usable without MediaCodec. Thus, both the MediaDrm
+// APIs and MediaCodec APIs must be enabled and not blacklisted.
+// static
+bool MediaDrmBridge::IsAvailable() {
+ return AreMediaDrmApisAvailable() && MediaCodecUtil::IsMediaCodecAvailable();
+}
+
// static
bool MediaDrmBridge::RegisterMediaDrmBridge(JNIEnv* env) {
return RegisterNativesImpl(env);
@@ -240,7 +251,9 @@ bool MediaDrmBridge::RegisterMediaDrmBridge(JNIEnv* env) {
// static
bool MediaDrmBridge::IsKeySystemSupported(const std::string& key_system) {
- DCHECK(!key_system.empty());
+ if (!MediaDrmBridge::IsAvailable())
+ return false;
+
return IsKeySystemSupportedWithTypeImpl(key_system, "");
}
@@ -248,17 +261,24 @@ bool MediaDrmBridge::IsKeySystemSupported(const std::string& key_system) {
bool MediaDrmBridge::IsKeySystemSupportedWithType(
const std::string& key_system,
const std::string& container_mime_type) {
- DCHECK(!key_system.empty() && !container_mime_type.empty());
+ DCHECK(!container_mime_type.empty()) << "Call IsKeySystemSupported instead";
+
+ if (!MediaDrmBridge::IsAvailable())
+ return false;
+
return IsKeySystemSupportedWithTypeImpl(key_system, container_mime_type);
}
// static
std::vector<std::string> MediaDrmBridge::GetPlatformKeySystemNames() {
+ if (!MediaDrmBridge::IsAvailable())
+ return std::vector<std::string>();
+
return g_key_system_manager.Get().GetPlatformKeySystemNames();
}
// static
-scoped_refptr<MediaDrmBridge> MediaDrmBridge::Create(
+scoped_refptr<MediaDrmBridge> MediaDrmBridge::CreateInternal(
const std::string& key_system,
SecurityLevel security_level,
const CreateFetcherCB& create_fetcher_cb,
@@ -267,10 +287,8 @@ scoped_refptr<MediaDrmBridge> MediaDrmBridge::Create(
const LegacySessionErrorCB& legacy_session_error_cb,
const SessionKeysChangeCB& session_keys_change_cb,
const SessionExpirationUpdateCB& session_expiration_update_cb) {
- DVLOG(1) << __FUNCTION__;
-
- if (!IsAvailable())
- return nullptr;
+ // All paths requires the MediaDrmApis.
+ DCHECK(AreMediaDrmApisAvailable());
UUID scheme_uuid = g_key_system_manager.Get().GetUUID(key_system);
if (scheme_uuid.empty())
@@ -288,12 +306,37 @@ scoped_refptr<MediaDrmBridge> MediaDrmBridge::Create(
}
// static
+scoped_refptr<MediaDrmBridge> MediaDrmBridge::Create(
+ const std::string& key_system,
+ SecurityLevel security_level,
+ const CreateFetcherCB& create_fetcher_cb,
+ const SessionMessageCB& session_message_cb,
+ const SessionClosedCB& session_closed_cb,
+ const LegacySessionErrorCB& legacy_session_error_cb,
+ const SessionKeysChangeCB& session_keys_change_cb,
+ const SessionExpirationUpdateCB& session_expiration_update_cb) {
+ DVLOG(1) << __FUNCTION__;
+
+ if (!IsAvailable())
+ return nullptr;
+
+ return CreateInternal(key_system, security_level, create_fetcher_cb,
+ session_message_cb, session_closed_cb,
+ legacy_session_error_cb, session_keys_change_cb,
+ session_expiration_update_cb);
+}
+
+// static
scoped_refptr<MediaDrmBridge> MediaDrmBridge::CreateWithoutSessionSupport(
const std::string& key_system,
SecurityLevel security_level,
const CreateFetcherCB& create_fetcher_cb) {
DVLOG(1) << __FUNCTION__;
+ // Sessions won't be used so decoding capability is not required.
+ if (!AreMediaDrmApisAvailable())
+ return nullptr;
+
return MediaDrmBridge::Create(key_system, security_level, create_fetcher_cb,
SessionMessageCB(), SessionClosedCB(),
LegacySessionErrorCB(), SessionKeysChangeCB(),
@@ -303,6 +346,7 @@ scoped_refptr<MediaDrmBridge> MediaDrmBridge::CreateWithoutSessionSupport(
void MediaDrmBridge::SetServerCertificate(
const std::vector<uint8_t>& certificate,
scoped_ptr<media::SimpleCdmPromise> promise) {
+ DCHECK(task_runner_->BelongsToCurrentThread());
DVLOG(2) << __FUNCTION__ << "(" << certificate.size() << " bytes)";
DCHECK(!certificate.empty());
@@ -323,6 +367,7 @@ void MediaDrmBridge::CreateSessionAndGenerateRequest(
media::EmeInitDataType init_data_type,
const std::vector<uint8_t>& init_data,
scoped_ptr<media::NewSessionCdmPromise> promise) {
+ DCHECK(task_runner_->BelongsToCurrentThread());
DVLOG(2) << __FUNCTION__;
if (session_type != media::MediaKeys::TEMPORARY_SESSION) {
@@ -347,6 +392,7 @@ void MediaDrmBridge::CreateSessionAndGenerateRequest(
&init_data_from_delegate,
&optional_parameters_from_delegate)) {
promise->reject(INVALID_ACCESS_ERROR, 0, "Invalid init data.");
+ return;
}
if (!init_data_from_delegate.empty()) {
j_init_data =
@@ -377,6 +423,7 @@ void MediaDrmBridge::LoadSession(
SessionType session_type,
const std::string& session_id,
scoped_ptr<media::NewSessionCdmPromise> promise) {
+ DCHECK(task_runner_->BelongsToCurrentThread());
DVLOG(2) << __FUNCTION__;
NOTIMPLEMENTED() << "EME persistent sessions not yet supported on Android.";
@@ -387,6 +434,7 @@ void MediaDrmBridge::UpdateSession(
const std::string& session_id,
const std::vector<uint8_t>& response,
scoped_ptr<media::SimpleCdmPromise> promise) {
+ DCHECK(task_runner_->BelongsToCurrentThread());
DVLOG(2) << __FUNCTION__;
JNIEnv* env = AttachCurrentThread();
@@ -402,6 +450,7 @@ void MediaDrmBridge::UpdateSession(
void MediaDrmBridge::CloseSession(const std::string& session_id,
scoped_ptr<media::SimpleCdmPromise> promise) {
+ DCHECK(task_runner_->BelongsToCurrentThread());
DVLOG(2) << __FUNCTION__;
JNIEnv* env = AttachCurrentThread();
@@ -416,12 +465,19 @@ void MediaDrmBridge::CloseSession(const std::string& session_id,
void MediaDrmBridge::RemoveSession(
const std::string& session_id,
scoped_ptr<media::SimpleCdmPromise> promise) {
+ DCHECK(task_runner_->BelongsToCurrentThread());
DVLOG(2) << __FUNCTION__;
NOTIMPLEMENTED() << "EME persistent sessions not yet supported on Android.";
promise->reject(NOT_SUPPORTED_ERROR, 0, "RemoveSession() is not supported.");
}
+CdmContext* MediaDrmBridge::GetCdmContext() {
+ DVLOG(2) << __FUNCTION__;
+
+ return &media_drm_bridge_cdm_context_;
+}
+
void MediaDrmBridge::DeleteOnCorrectThread() const {
DVLOG(1) << __FUNCTION__;
@@ -614,9 +670,6 @@ void MediaDrmBridge::OnSessionKeysChange(
bool has_additional_usable_key) {
DVLOG(2) << __FUNCTION__;
- if (has_additional_usable_key)
- player_tracker_.NotifyNewKey();
-
CdmKeysInfo cdm_keys_info;
size_t size = env->GetArrayLength(j_keys_info);
@@ -647,6 +700,12 @@ void MediaDrmBridge::OnSessionKeysChange(
FROM_HERE,
base::Bind(session_keys_change_cb_, AsString(env, j_session_id),
has_additional_usable_key, base::Passed(&cdm_keys_info)));
+
+ if (has_additional_usable_key) {
+ task_runner_->PostTask(FROM_HERE,
+ base::Bind(&MediaDrmBridge::OnHasAdditionalUsableKey,
+ weak_factory_.GetWeakPtr()));
+ }
}
// According to MeidaDrm documentation [1], zero |expiry_time_ms| means the keys
@@ -718,6 +777,7 @@ MediaDrmBridge::MediaDrmBridge(
session_keys_change_cb_(session_keys_change_cb),
session_expiration_update_cb_(session_expiration_update_cb),
task_runner_(base::ThreadTaskRunnerHandle::Get()),
+ media_drm_bridge_cdm_context_(this),
weak_factory_(this) {
DVLOG(1) << __FUNCTION__;
@@ -836,4 +896,11 @@ void MediaDrmBridge::ProcessProvisionResponse(bool success,
j_response.obj());
}
+void MediaDrmBridge::OnHasAdditionalUsableKey() {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+ DVLOG(1) << __FUNCTION__;
+
+ player_tracker_.NotifyNewKey();
+}
+
} // namespace media
diff --git a/chromium/media/base/android/media_drm_bridge.h b/chromium/media/base/android/media_drm_bridge.h
index d46abbe2856..8c31f89694a 100644
--- a/chromium/media/base/android/media_drm_bridge.h
+++ b/chromium/media/base/android/media_drm_bridge.h
@@ -15,6 +15,7 @@
#include "base/macros.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
+#include "media/base/android/media_drm_bridge_cdm_context.h"
#include "media/base/android/provision_fetcher.h"
#include "media/base/cdm_promise_adapter.h"
#include "media/base/media_export.h"
@@ -55,9 +56,9 @@ class MEDIA_EXPORT MediaDrmBridge : public MediaKeys, public PlayerTracker {
using MediaCryptoReadyCB = base::Callback<void(JavaObjectPtr media_crypto,
bool needs_protected_surface)>;
- // Checks whether MediaDRM is available.
- // All other static methods check IsAvailable() internally. There's no need
- // to check IsAvailable() explicitly before calling them.
+ // Checks whether MediaDRM is available and usable, including for decoding.
+ // All other static methods check IsAvailable() or equivalent internally.
+ // There is no need to check IsAvailable() explicitly before calling them.
static bool IsAvailable();
static bool RegisterMediaDrmBridge(JNIEnv* env);
@@ -114,6 +115,7 @@ class MEDIA_EXPORT MediaDrmBridge : public MediaKeys, public PlayerTracker {
scoped_ptr<media::SimpleCdmPromise> promise) override;
void RemoveSession(const std::string& session_id,
scoped_ptr<media::SimpleCdmPromise> promise) override;
+ CdmContext* GetCdmContext() override;
void DeleteOnCorrectThread() const override;
// PlayerTracker implementation. Can be called on any thread.
@@ -190,8 +192,8 @@ class MEDIA_EXPORT MediaDrmBridge : public MediaKeys, public PlayerTracker {
// Session event callbacks.
- // TODO(xhwang): Remove |j_legacy_destination_url| when prefixed EME support
- // is removed.
+ // TODO(xhwang): Remove |j_legacy_destination_url| now that prefixed EME
+ // support is removed. http://crbug.com/249976
void OnSessionMessage(
JNIEnv* env,
const base::android::JavaParamRef<jobject>& j_media_drm,
@@ -224,6 +226,7 @@ class MEDIA_EXPORT MediaDrmBridge : public MediaKeys, public PlayerTracker {
// unrelated to one of the MediaKeys calls that accept a |promise|.
// Note:
// - This method is only for supporting prefixed EME API.
+ // TODO(ddorwin): Remove it now. https://crbug.com/249976
// - This method will be ignored by unprefixed EME. All errors reported
// in this method should probably also be reported by one of other methods.
void OnLegacySessionError(
@@ -242,6 +245,16 @@ class MEDIA_EXPORT MediaDrmBridge : public MediaKeys, public PlayerTracker {
// For DeleteSoon() in DeleteOnCorrectThread().
friend class base::DeleteHelper<MediaDrmBridge>;
+ static scoped_refptr<MediaDrmBridge> CreateInternal(
+ const std::string& key_system,
+ SecurityLevel security_level,
+ const CreateFetcherCB& create_fetcher_cb,
+ const SessionMessageCB& session_message_cb,
+ const SessionClosedCB& session_closed_cb,
+ const LegacySessionErrorCB& legacy_session_error_cb,
+ const SessionKeysChangeCB& session_keys_change_cb,
+ const SessionExpirationUpdateCB& session_expiration_update_cb);
+
// Constructs a MediaDrmBridge for |scheme_uuid| and |security_level|. The
// default security level will be used if |security_level| is
// SECURITY_LEVEL_DEFAULT. Sessions should not be created if session callbacks
@@ -275,6 +288,9 @@ class MEDIA_EXPORT MediaDrmBridge : public MediaKeys, public PlayerTracker {
// Process the data received by provisioning server.
void ProcessProvisionResponse(bool success, const std::string& response);
+ // Called on the |task_runner_| when there is additional usable key.
+ void OnHasAdditionalUsableKey();
+
// UUID of the key system.
std::vector<uint8_t> scheme_uuid_;
@@ -315,6 +331,8 @@ class MEDIA_EXPORT MediaDrmBridge : public MediaKeys, public PlayerTracker {
// Default task runner.
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+ MediaDrmBridgeCdmContext media_drm_bridge_cdm_context_;
+
// NOTE: Weak pointers must be invalidated before all other member variables.
base::WeakPtrFactory<MediaDrmBridge> weak_factory_;
diff --git a/chromium/media/base/android/media_drm_bridge_cdm_context.cc b/chromium/media/base/android/media_drm_bridge_cdm_context.cc
new file mode 100644
index 00000000000..f9c9fa4e4b6
--- /dev/null
+++ b/chromium/media/base/android/media_drm_bridge_cdm_context.cc
@@ -0,0 +1,42 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/base/android/media_drm_bridge_cdm_context.h"
+
+#include "media/base/android/media_drm_bridge.h"
+
+namespace media {
+
+MediaDrmBridgeCdmContext::MediaDrmBridgeCdmContext(
+ MediaDrmBridge* media_drm_bridge)
+ : media_drm_bridge_(media_drm_bridge) {
+ DCHECK(media_drm_bridge_);
+}
+
+MediaDrmBridgeCdmContext::~MediaDrmBridgeCdmContext() {}
+
+Decryptor* MediaDrmBridgeCdmContext::GetDecryptor() {
+ return nullptr;
+}
+
+int MediaDrmBridgeCdmContext::GetCdmId() const {
+ return kInvalidCdmId;
+}
+
+int MediaDrmBridgeCdmContext::RegisterPlayer(
+ const base::Closure& new_key_cb,
+ const base::Closure& cdm_unset_cb) {
+ return media_drm_bridge_->RegisterPlayer(new_key_cb, cdm_unset_cb);
+}
+
+void MediaDrmBridgeCdmContext::UnregisterPlayer(int registration_id) {
+ media_drm_bridge_->UnregisterPlayer(registration_id);
+}
+
+void MediaDrmBridgeCdmContext::SetMediaCryptoReadyCB(
+ const MediaCryptoReadyCB& media_crypto_ready_cb) {
+ media_drm_bridge_->SetMediaCryptoReadyCB(media_crypto_ready_cb);
+}
+
+} // namespace media
diff --git a/chromium/media/base/android/media_drm_bridge_cdm_context.h b/chromium/media/base/android/media_drm_bridge_cdm_context.h
new file mode 100644
index 00000000000..de76c5ec3b6
--- /dev/null
+++ b/chromium/media/base/android/media_drm_bridge_cdm_context.h
@@ -0,0 +1,74 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_BASE_ANDROID_MEDIA_DRM_BRIDGE_CDM_CONTEXT_H_
+#define MEDIA_BASE_ANDROID_MEDIA_DRM_BRIDGE_CDM_CONTEXT_H_
+
+#include <jni.h>
+
+#include "base/android/scoped_java_ref.h"
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "media/base/cdm_context.h"
+#include "media/base/media_export.h"
+#include "media/base/player_tracker.h"
+
+namespace media {
+
+class MediaDrmBridge;
+
+// The CdmContext implementation for MediaDrmBridge. MediaDrmBridge supports
+// neither Decryptor nor CDM ID, but uses MediaCrypto to connect to MediaCodec.
+// MediaCodec-based decoders should cast the given CdmContext to this class to
+// access APIs defined in this class.
+//
+// Methods can be called on any thread. The registered callbacks will be fired
+// on the thread |media_drm_bridge_| is running on. The caller should make sure
+// that the callbacks are posted to the correct thread.
+//
+// TODO(xhwang): Remove PlayerTracker interface.
+class MEDIA_EXPORT MediaDrmBridgeCdmContext : public CdmContext,
+ public PlayerTracker {
+ public:
+ using JavaObjectPtr = scoped_ptr<base::android::ScopedJavaGlobalRef<jobject>>;
+
+ // Notification called when MediaCrypto object is ready.
+ // Parameters:
+ // |media_crypto| - reference to MediaCrypto object
+ // |needs_protected_surface| - true if protected surface is required
+ using MediaCryptoReadyCB = base::Callback<void(JavaObjectPtr media_crypto,
+ bool needs_protected_surface)>;
+
+ // The |media_drm_bridge| owns |this| and is guaranteed to outlive |this|.
+ explicit MediaDrmBridgeCdmContext(MediaDrmBridge* media_drm_bridge);
+
+ ~MediaDrmBridgeCdmContext() final;
+
+ // CdmContext implementation.
+ Decryptor* GetDecryptor() final;
+ int GetCdmId() const final;
+
+ // PlayerTracker implementation.
+ // Methods can be called on any thread. The registered callbacks will be fired
+ // on |task_runner_|. The caller should make sure that the callbacks are
+ // posted to the correct thread.
+ //
+ // Note: RegisterPlayer() must be called before SetMediaCryptoReadyCB() to
+ // avoid missing any new key notifications.
+ int RegisterPlayer(const base::Closure& new_key_cb,
+ const base::Closure& cdm_unset_cb) final;
+ void UnregisterPlayer(int registration_id) final;
+
+ void SetMediaCryptoReadyCB(const MediaCryptoReadyCB& media_crypto_ready_cb);
+
+ private:
+ MediaDrmBridge* const media_drm_bridge_;
+
+ DISALLOW_COPY_AND_ASSIGN(MediaDrmBridgeCdmContext);
+};
+
+} // namespace media
+
+#endif // MEDIA_BASE_ANDROID_MEDIA_DRM_BRIDGE_CDM_CONTEXT_H_
diff --git a/chromium/media/base/android/media_player_android.cc b/chromium/media/base/android/media_player_android.cc
index ec0fe6af495..b8f40800d6c 100644
--- a/chromium/media/base/android/media_player_android.cc
+++ b/chromium/media/base/android/media_player_android.cc
@@ -4,6 +4,8 @@
#include "media/base/android/media_player_android.h"
+#include <algorithm>
+
#include "base/android/context_utils.h"
#include "base/logging.h"
#include "base/single_thread_task_runner.h"
@@ -11,17 +13,29 @@
#include "media/base/android/media_drm_bridge.h"
#include "media/base/android/media_player_manager.h"
+namespace {
+
+const double kDefaultVolume = 1.0;
+
+} // namespace
+
namespace media {
+const double MediaPlayerAndroid::kDefaultVolumeMultiplier = 1.0;
+
MediaPlayerAndroid::MediaPlayerAndroid(
int player_id,
MediaPlayerManager* manager,
const OnDecoderResourcesReleasedCB& on_decoder_resources_released_cb,
- const GURL& frame_url)
+ const GURL& frame_url,
+ int media_session_id)
: on_decoder_resources_released_cb_(on_decoder_resources_released_cb),
player_id_(player_id),
+ volume_(kDefaultVolume),
+ volume_multiplier_(kDefaultVolumeMultiplier),
manager_(manager),
frame_url_(frame_url),
+ media_session_id_(media_session_id),
weak_factory_(this) {
listener_.reset(new MediaPlayerListener(base::ThreadTaskRunnerHandle::Get(),
weak_factory_.GetWeakPtr()));
@@ -29,6 +43,24 @@ MediaPlayerAndroid::MediaPlayerAndroid(
MediaPlayerAndroid::~MediaPlayerAndroid() {}
+void MediaPlayerAndroid::SetVolume(double volume) {
+ volume_ = std::max(0.0, std::min(volume, 1.0));
+ UpdateEffectiveVolume();
+}
+
+void MediaPlayerAndroid::SetVolumeMultiplier(double volume_multiplier) {
+ volume_multiplier_ = std::max(0.0, std::min(volume_multiplier, 1.0));
+ UpdateEffectiveVolume();
+}
+
+double MediaPlayerAndroid::GetEffectiveVolume() const {
+ return volume_ * volume_multiplier_;
+}
+
+void MediaPlayerAndroid::UpdateEffectiveVolume() {
+ UpdateEffectiveVolumeInternal(GetEffectiveVolume());
+}
+
// For most subclasses we can delete on the caller thread.
void MediaPlayerAndroid::DeleteOnCorrectThread() {
delete this;
diff --git a/chromium/media/base/android/media_player_android.h b/chromium/media/base/android/media_player_android.h
index 3f07a9319a8..68e3978cf67 100644
--- a/chromium/media/base/android/media_player_android.h
+++ b/chromium/media/base/android/media_player_android.h
@@ -10,6 +10,7 @@
#include "base/callback.h"
#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "media/base/android/media_player_listener.h"
@@ -23,6 +24,19 @@ namespace media {
class MediaKeys;
class MediaPlayerManager;
+enum {
+ // Id used for players not participating in any media sessions
+ // because of undefined behavior in the specification. When all
+ // media session interactions have been worked out, this id should
+ // no longer be used.
+ kInvalidMediaSessionId = -1,
+
+ // The media session for media elements that don't have an explicit
+ // user created media session set. Must be in-sync with
+ // WebMediaSession::DefaultID in blink.
+ kDefaultMediaSessionId = 0
+};
+
// This class serves as the base class for different media player
// implementations on Android. Subclasses need to provide their own
// MediaPlayerAndroid::Create() implementation.
@@ -36,8 +50,11 @@ class MEDIA_EXPORT MediaPlayerAndroid {
MEDIA_ERROR_DECODE,
MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK,
MEDIA_ERROR_INVALID_CODE,
+ MEDIA_ERROR_SERVER_DIED,
};
+ static const double kDefaultVolumeMultiplier;
+
// Callback when the player releases decoding resources.
typedef base::Callback<void(int player_id)> OnDecoderResourcesReleasedCB;
@@ -62,8 +79,13 @@ class MEDIA_EXPORT MediaPlayerAndroid {
// Release the player resources.
virtual void Release() = 0;
- // Set the player volume.
- virtual void SetVolume(double volume) = 0;
+ // Set the player volume, and take effect immediately.
+ // The volume should be between 0.0 and 1.0.
+ void SetVolume(double volume);
+
+ // Set the player volume multiplier, and take effect immediately.
+ // The volume should be between 0.0 and 1.0.
+ void SetVolumeMultiplier(double volume_multiplier);
// Get the media information from the player.
virtual bool HasVideo() const = 0;
@@ -100,6 +122,8 @@ class MEDIA_EXPORT MediaPlayerAndroid {
GURL frame_url() { return frame_url_; }
+ int media_session_id() { return media_session_id_; }
+
// Attach/Detaches |listener_| for listening to all the media events. If
// |j_media_player| is NULL, |listener_| only listens to the system media
// events. Otherwise, it also listens to the events from |j_media_player|.
@@ -111,7 +135,8 @@ class MEDIA_EXPORT MediaPlayerAndroid {
int player_id,
MediaPlayerManager* manager,
const OnDecoderResourcesReleasedCB& on_decoder_resources_released_cb,
- const GURL& frame_url);
+ const GURL& frame_url,
+ int media_session_id);
// TODO(qinmin): Simplify the MediaPlayerListener class to only listen to
// media interrupt events. And have a separate child class to listen to all
@@ -125,6 +150,9 @@ class MEDIA_EXPORT MediaPlayerAndroid {
virtual void OnSeekComplete();
virtual void OnMediaPrepared();
+ double GetEffectiveVolume() const;
+ void UpdateEffectiveVolume();
+
// When destroying a subclassed object on a non-UI thread
// it is still required to destroy the |listener_| related stuff
// on the UI thread.
@@ -137,11 +165,22 @@ class MEDIA_EXPORT MediaPlayerAndroid {
OnDecoderResourcesReleasedCB on_decoder_resources_released_cb_;
private:
+ // Set the effective player volume, implemented by children classes.
+ virtual void UpdateEffectiveVolumeInternal(double effective_volume) = 0;
+
friend class MediaPlayerListener;
// Player ID assigned to this player.
int player_id_;
+ // The player volume. Should be between 0.0 and 1.0.
+ double volume_;
+
+ // The player volume multiplier. Should be between 0.0 and 1.0. This
+ // should be a cached version of the MediaSession volume multiplier,
+ // and should keep updated.
+ double volume_multiplier_;
+
// Resource manager for all the media players.
MediaPlayerManager* manager_;
@@ -151,6 +190,9 @@ class MEDIA_EXPORT MediaPlayerAndroid {
// Listener object that listens to all the media player events.
scoped_ptr<MediaPlayerListener> listener_;
+ // Media session ID assigned to this player.
+ int media_session_id_;
+
// Weak pointer passed to |listener_| for callbacks.
// NOTE: Weak pointers must be invalidated before all other member variables.
base::WeakPtrFactory<MediaPlayerAndroid> weak_factory_;
diff --git a/chromium/media/base/android/media_player_bridge.cc b/chromium/media/base/android/media_player_bridge.cc
index 2f1fc0a3cd0..50a0af3ee50 100644
--- a/chromium/media/base/android/media_player_bridge.cc
+++ b/chromium/media/base/android/media_player_bridge.cc
@@ -10,6 +10,7 @@
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/logging.h"
+#include "base/metrics/histogram_macros.h"
#include "base/strings/string_util.h"
#include "jni/MediaPlayerBridge_jni.h"
#include "media/base/android/media_common_android.h"
@@ -23,6 +24,16 @@ using base::android::ScopedJavaLocalRef;
namespace media {
+namespace {
+
+enum UMAExitStatus {
+ UMA_EXIT_SUCCESS = 0,
+ UMA_EXIT_ERROR,
+ UMA_EXIT_STATUS_MAX = UMA_EXIT_ERROR,
+};
+
+} // namespace
+
MediaPlayerBridge::MediaPlayerBridge(
int player_id,
const GURL& url,
@@ -32,11 +43,13 @@ MediaPlayerBridge::MediaPlayerBridge(
MediaPlayerManager* manager,
const OnDecoderResourcesReleasedCB& on_decoder_resources_released_cb,
const GURL& frame_url,
- bool allow_credentials)
+ bool allow_credentials,
+ int media_session_id)
: MediaPlayerAndroid(player_id,
manager,
on_decoder_resources_released_cb,
- frame_url),
+ frame_url,
+ media_session_id),
prepared_(false),
pending_play_(false),
should_seek_on_prepare_(false),
@@ -49,10 +62,11 @@ MediaPlayerBridge::MediaPlayerBridge(
can_pause_(true),
can_seek_forward_(true),
can_seek_backward_(true),
- volume_(-1.0),
allow_credentials_(allow_credentials),
- weak_factory_(this) {
-}
+ is_active_(false),
+ has_error_(false),
+ has_ever_started_(false),
+ weak_factory_(this) {}
MediaPlayerBridge::~MediaPlayerBridge() {
if (!j_media_player_bridge_.is_null()) {
@@ -61,6 +75,12 @@ MediaPlayerBridge::~MediaPlayerBridge() {
Java_MediaPlayerBridge_destroy(env, j_media_player_bridge_.obj());
}
Release();
+
+ if (has_ever_started_) {
+ UMA_HISTOGRAM_ENUMERATION("Media.Android.MediaPlayerSuccess",
+ has_error_ ? UMA_EXIT_ERROR : UMA_EXIT_SUCCESS,
+ UMA_EXIT_STATUS_MAX + 1);
+ }
}
void MediaPlayerBridge::Initialize() {
@@ -100,8 +120,7 @@ void MediaPlayerBridge::CreateJavaMediaPlayerBridge() {
j_media_player_bridge_.Reset(Java_MediaPlayerBridge_create(
env, reinterpret_cast<intptr_t>(this)));
- if (volume_ >= 0)
- SetVolume(volume_);
+ UpdateEffectiveVolume();
AttachListener(j_media_player_bridge_.obj());
}
@@ -277,6 +296,17 @@ void MediaPlayerBridge::OnMediaMetadataExtracted(
}
void MediaPlayerBridge::Start() {
+ // A second Start() call after an error is considered another attempt for UMA
+ // and causes UMA reporting.
+ if (has_ever_started_ && has_error_) {
+ UMA_HISTOGRAM_ENUMERATION("Media.Android.MediaPlayerSuccess",
+ UMA_EXIT_ERROR, UMA_EXIT_STATUS_MAX + 1);
+ }
+
+ has_ever_started_ = true;
+ has_error_ = false;
+ is_active_ = true;
+
if (j_media_player_bridge_.is_null()) {
pending_play_ = true;
Prepare();
@@ -297,6 +327,8 @@ void MediaPlayerBridge::Pause(bool is_media_related_action) {
else
pending_play_ = false;
}
+
+ is_active_ = false;
}
bool MediaPlayerBridge::IsPlaying() {
@@ -367,6 +399,8 @@ base::TimeDelta MediaPlayerBridge::GetDuration() {
}
void MediaPlayerBridge::Release() {
+ is_active_ = false;
+
on_decoder_resources_released_cb_.Run(player_id());
if (j_media_player_bridge_.is_null())
return;
@@ -386,17 +420,16 @@ void MediaPlayerBridge::Release() {
DetachListener();
}
-void MediaPlayerBridge::SetVolume(double volume) {
+void MediaPlayerBridge::UpdateEffectiveVolumeInternal(double effective_volume) {
if (j_media_player_bridge_.is_null()) {
- volume_ = volume;
return;
}
JNIEnv* env = base::android::AttachCurrentThread();
CHECK(env);
- Java_MediaPlayerBridge_setVolume(
- env, j_media_player_bridge_.obj(), volume);
+ Java_MediaPlayerBridge_setVolume(env, j_media_player_bridge_.obj(),
+ effective_volume);
}
void MediaPlayerBridge::OnVideoSizeChanged(int width, int height) {
@@ -405,6 +438,22 @@ void MediaPlayerBridge::OnVideoSizeChanged(int width, int height) {
MediaPlayerAndroid::OnVideoSizeChanged(width, height);
}
+void MediaPlayerBridge::OnMediaError(int error_type) {
+ // Gather errors for UMA only in the active state.
+ // The MEDIA_ERROR_INVALID_CODE is reported by MediaPlayerListener.java in
+ // the situations that are considered normal, and is ignored by upper level.
+ if (is_active_ && error_type != MEDIA_ERROR_INVALID_CODE)
+ has_error_ = true;
+
+ // Do not propagate MEDIA_ERROR_SERVER_DIED. If it happens in the active state
+ // we want the playback to stall. It can be recovered by pressing the Play
+ // button again.
+ if (error_type == MEDIA_ERROR_SERVER_DIED)
+ error_type = MEDIA_ERROR_INVALID_CODE;
+
+ MediaPlayerAndroid::OnMediaError(error_type);
+}
+
void MediaPlayerBridge::OnPlaybackComplete() {
time_update_timer_.Stop();
MediaPlayerAndroid::OnPlaybackComplete();
diff --git a/chromium/media/base/android/media_player_bridge.h b/chromium/media/base/android/media_player_bridge.h
index b347e5408ea..21dc900ecca 100644
--- a/chromium/media/base/android/media_player_bridge.h
+++ b/chromium/media/base/android/media_player_bridge.h
@@ -52,7 +52,8 @@ class MEDIA_EXPORT MediaPlayerBridge : public MediaPlayerAndroid {
MediaPlayerManager* manager,
const OnDecoderResourcesReleasedCB& on_decoder_resources_released_cb,
const GURL& frame_url,
- bool allow_credentials);
+ bool allow_credentials,
+ int media_session_id);
~MediaPlayerBridge() override;
// Initialize this object and extract the metadata from the media.
@@ -64,7 +65,6 @@ class MEDIA_EXPORT MediaPlayerBridge : public MediaPlayerAndroid {
void Pause(bool is_media_related_action) override;
void SeekTo(base::TimeDelta timestamp) override;
void Release() override;
- void SetVolume(double volume) override;
bool HasVideo() const override;
bool HasAudio() const override;
int GetVideoWidth() override;
@@ -96,6 +96,7 @@ class MEDIA_EXPORT MediaPlayerBridge : public MediaPlayerAndroid {
// MediaPlayerAndroid implementation.
void OnVideoSizeChanged(int width, int height) override;
+ void OnMediaError(int error_type) override;
void OnPlaybackComplete() override;
void OnMediaInterrupted() override;
void OnMediaPrepared() override;
@@ -109,6 +110,9 @@ class MEDIA_EXPORT MediaPlayerBridge : public MediaPlayerAndroid {
private:
friend class MediaPlayerBridgeTest;
+ // MediaPlayerAndroid implementation
+ void UpdateEffectiveVolumeInternal(double effective_volume) override;
+
// Set the data source for the media player.
void SetDataSource(const std::string& url);
@@ -194,12 +198,22 @@ class MEDIA_EXPORT MediaPlayerBridge : public MediaPlayerAndroid {
base::TimeDelta last_time_update_timestamp_;
- // Volume of playback.
- double volume_;
-
// Whether user credentials are allowed to be passed.
bool allow_credentials_;
+ // Helper variables for UMA reporting.
+
+ // Whether the preparation for playback or the playback is currently going on.
+ // This flag is set in Start() and cleared in Pause() and Release(). Used for
+ // UMA reporting only.
+ bool is_active_;
+
+ // Whether there has been any errors in the active state.
+ bool has_error_;
+
+ // The flag is set if Start() has been called at least once.
+ bool has_ever_started_;
+
// NOTE: Weak pointers must be invalidated before all other member variables.
base::WeakPtrFactory<MediaPlayerBridge> weak_factory_;
diff --git a/chromium/media/base/android/media_player_bridge_unittest.cc b/chromium/media/base/android/media_player_bridge_unittest.cc
index c2b2447b6d6..d4db5ba6c5c 100644
--- a/chromium/media/base/android/media_player_bridge_unittest.cc
+++ b/chromium/media/base/android/media_player_bridge_unittest.cc
@@ -58,7 +58,8 @@ class MediaPlayerBridgeTest : public testing::Test {
base::Bind(&MockMediaPlayerManager::OnMediaResourcesRequested,
base::Unretained(&manager_)),
GURL(),
- false) {}
+ false,
+ kDefaultMediaSessionId) {}
void SetCanSeekForward(bool can_seek_forward) {
bridge_.can_seek_forward_ = can_seek_forward;
diff --git a/chromium/media/base/android/media_source_player.cc b/chromium/media/base/android/media_source_player.cc
index 84c0b43bec0..30636f64b19 100644
--- a/chromium/media/base/android/media_source_player.cc
+++ b/chromium/media/base/android/media_source_player.cc
@@ -32,11 +32,13 @@ MediaSourcePlayer::MediaSourcePlayer(
MediaPlayerManager* manager,
const OnDecoderResourcesReleasedCB& on_decoder_resources_released_cb,
scoped_ptr<DemuxerAndroid> demuxer,
- const GURL& frame_url)
+ const GURL& frame_url,
+ int media_session_id)
: MediaPlayerAndroid(player_id,
manager,
on_decoder_resources_released_cb,
- frame_url),
+ frame_url,
+ media_session_id),
demuxer_(std::move(demuxer)),
pending_event_(NO_EVENT_PENDING),
playing_(false),
@@ -74,6 +76,10 @@ MediaSourcePlayer::~MediaSourcePlayer() {
Release();
DCHECK_EQ(!cdm_, !cdm_registration_id_);
if (cdm_) {
+ // Cancel previously registered callback (if any).
+ static_cast<MediaDrmBridge*>(cdm_.get())
+ ->SetMediaCryptoReadyCB(MediaDrmBridge::MediaCryptoReadyCB());
+
static_cast<MediaDrmBridge*>(cdm_.get())
->UnregisterPlayer(cdm_registration_id_);
cdm_registration_id_ = 0;
@@ -207,8 +213,8 @@ void MediaSourcePlayer::Release() {
on_decoder_resources_released_cb_.Run(player_id());
}
-void MediaSourcePlayer::SetVolume(double volume) {
- audio_decoder_job_->SetVolume(volume);
+void MediaSourcePlayer::UpdateEffectiveVolumeInternal(double effective_volume) {
+ audio_decoder_job_->SetVolume(effective_volume);
}
bool MediaSourcePlayer::CanPause() {
diff --git a/chromium/media/base/android/media_source_player.h b/chromium/media/base/android/media_source_player.h
index f2ea9ba0aba..d6b26d72499 100644
--- a/chromium/media/base/android/media_source_player.h
+++ b/chromium/media/base/android/media_source_player.h
@@ -45,7 +45,8 @@ class MEDIA_EXPORT MediaSourcePlayer : public MediaPlayerAndroid,
MediaPlayerManager* manager,
const OnDecoderResourcesReleasedCB& on_decoder_resources_released_cb,
scoped_ptr<DemuxerAndroid> demuxer,
- const GURL& frame_url);
+ const GURL& frame_url,
+ int media_session_id);
~MediaSourcePlayer() override;
// MediaPlayerAndroid implementation.
@@ -54,7 +55,6 @@ class MEDIA_EXPORT MediaSourcePlayer : public MediaPlayerAndroid,
void Pause(bool is_media_related_action) override;
void SeekTo(base::TimeDelta timestamp) override;
void Release() override;
- void SetVolume(double volume) override;
bool HasVideo() const override;
bool HasAudio() const override;
int GetVideoWidth() override;
@@ -77,6 +77,9 @@ class MEDIA_EXPORT MediaSourcePlayer : public MediaPlayerAndroid,
private:
friend class MediaSourcePlayerTest;
+ // MediaPlayerAndroid implementation
+ void UpdateEffectiveVolumeInternal(double effective_volume) override;
+
// Update the current timestamp.
void UpdateTimestamps(base::TimeDelta current_presentation_timestamp,
base::TimeDelta max_presentation_timestamp);
diff --git a/chromium/media/base/android/media_source_player_unittest.cc b/chromium/media/base/android/media_source_player_unittest.cc
index 0ce01abf36b..36fc6791543 100644
--- a/chromium/media/base/android/media_source_player_unittest.cc
+++ b/chromium/media/base/android/media_source_player_unittest.cc
@@ -162,11 +162,13 @@ class MediaSourcePlayerTest : public testing::Test {
MediaSourcePlayerTest()
: manager_(&message_loop_),
demuxer_(new MockDemuxerAndroid(&message_loop_)),
- player_(0, &manager_,
+ player_(0,
+ &manager_,
base::Bind(&MockMediaPlayerManager::OnDecorderResourcesReleased,
base::Unretained(&manager_)),
scoped_ptr<DemuxerAndroid>(demuxer_),
- GURL()),
+ GURL(),
+ kDefaultMediaSessionId),
decoder_callback_hook_executed_(false),
surface_texture_a_is_next_(true) {}
diff --git a/chromium/media/base/android/ndk_media_codec_bridge.cc b/chromium/media/base/android/ndk_media_codec_bridge.cc
index 6ab751bc08e..3a66db675c9 100644
--- a/chromium/media/base/android/ndk_media_codec_bridge.cc
+++ b/chromium/media/base/android/ndk_media_codec_bridge.cc
@@ -67,7 +67,7 @@ void NdkMediaCodecBridge::Stop() {
AMediaCodec_stop(media_codec_.get());
}
-void NdkMediaCodecBridge::GetOutputFormat(int* width, int* height) {
+MediaCodecStatus NdkMediaCodecBridge::GetOutputSize(gfx::Size* size) {
AMediaFormat* format = AMediaCodec_getOutputFormat(media_codec_.get());
int left, right, bottom, top;
bool has_left = AMediaFormat_getInt32(format, kMediaFormatKeyCropLeft, &left);
@@ -76,22 +76,26 @@ void NdkMediaCodecBridge::GetOutputFormat(int* width, int* height) {
bool has_bottom =
AMediaFormat_getInt32(format, kMediaFormatKeyCropBottom, &bottom);
bool has_top = AMediaFormat_getInt32(format, kMediaFormatKeyCropTop, &top);
+ int width, height;
if (has_left && has_right && has_bottom && has_top) {
// Use crop size as it is more accurate. right and bottom are inclusive.
- *width = right - left + 1;
- *height = top - bottom + 1;
+ width = right - left + 1;
+ height = top - bottom + 1;
} else {
- AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_WIDTH, width);
- AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_HEIGHT, height);
+ AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_WIDTH, &width);
+ AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_HEIGHT, &height);
}
+ size->SetSize(width, height);
+ return MEDIA_CODEC_OK;
}
-int NdkMediaCodecBridge::GetOutputSamplingRate() {
+MediaCodecStatus NdkMediaCodecBridge::GetOutputSamplingRate(
+ int* sampling_rate) {
AMediaFormat* format = AMediaCodec_getOutputFormat(media_codec_.get());
- int sample_rate = 0;
- AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE, &sample_rate);
- DCHECK(sample_rate != 0);
- return sample_rate;
+ *sampling_rate = 0;
+ AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE, sampling_rate);
+ DCHECK_NE(*sampling_rate, 0);
+ return MEDIA_CODEC_OK;
}
MediaCodecStatus NdkMediaCodecBridge::QueueInputBuffer(
@@ -213,26 +217,24 @@ void NdkMediaCodecBridge::ReleaseOutputBuffer(int index, bool render) {
AMediaCodec_releaseOutputBuffer(media_codec_.get(), index, render);
}
-void NdkMediaCodecBridge::GetInputBuffer(int input_buffer_index,
- uint8_t** data,
- size_t* capacity) {
+MediaCodecStatus NdkMediaCodecBridge::GetInputBuffer(int input_buffer_index,
+ uint8_t** data,
+ size_t* capacity) {
*data = AMediaCodec_getInputBuffer(media_codec_.get(), input_buffer_index,
capacity);
+ return MEDIA_CODEC_OK;
}
-bool NdkMediaCodecBridge::CopyFromOutputBuffer(int index,
- size_t offset,
- void* dst,
- int dst_size) {
+MediaCodecStatus NdkMediaCodecBridge::CopyFromOutputBuffer(int index,
+ size_t offset,
+ void* dst,
+ size_t num) {
size_t capacity;
- uint8_t* src_data =
+ const uint8_t* src_data =
AMediaCodec_getOutputBuffer(media_codec_.get(), index, &capacity);
-
- if (capacity < offset || capacity - offset < static_cast<size_t>(dst_size))
- return false;
-
- memcpy(dst, src_data + offset, dst_size);
- return true;
+ CHECK_GE(capacity, offset + num);
+ memcpy(dst, src_data + offset, num);
+ return MEDIA_CODEC_OK;
}
} // namespace media
diff --git a/chromium/media/base/android/ndk_media_codec_bridge.h b/chromium/media/base/android/ndk_media_codec_bridge.h
index d675ffb058b..d7f2b108a1a 100644
--- a/chromium/media/base/android/ndk_media_codec_bridge.h
+++ b/chromium/media/base/android/ndk_media_codec_bridge.h
@@ -14,6 +14,7 @@
#include "base/time/time.h"
#include "media/base/android/media_codec_bridge.h"
#include "media/base/media_export.h"
+#include "ui/gfx/geometry/size.h"
namespace media {
@@ -25,8 +26,8 @@ class MEDIA_EXPORT NdkMediaCodecBridge : public MediaCodecBridge {
MediaCodecStatus Reset() override;
bool Start() override;
void Stop() override;
- void GetOutputFormat(int* width, int* height) override;
- int GetOutputSamplingRate() override;
+ MediaCodecStatus GetOutputSize(gfx::Size* size) override;
+ MediaCodecStatus GetOutputSamplingRate(int* sampling_rate) override;
MediaCodecStatus QueueInputBuffer(
int index,
const uint8_t* data,
@@ -53,13 +54,13 @@ class MEDIA_EXPORT NdkMediaCodecBridge : public MediaCodecBridge {
bool* end_of_stream,
bool* key_frame) override;
void ReleaseOutputBuffer(int index, bool render) override;
- void GetInputBuffer(int input_buffer_index,
- uint8_t** data,
- size_t* capacity) override;
- bool CopyFromOutputBuffer(int index,
- size_t offset,
- void* dst,
- int dst_size) override;
+ MediaCodecStatus GetInputBuffer(int input_buffer_index,
+ uint8_t** data,
+ size_t* capacity) override;
+ MediaCodecStatus CopyFromOutputBuffer(int index,
+ size_t offset,
+ void* dst,
+ size_t num) override;
protected:
NdkMediaCodecBridge(const std::string& mime,
diff --git a/chromium/media/base/android/provision_fetcher.h b/chromium/media/base/android/provision_fetcher.h
index 43db3e37629..c9673356e91 100644
--- a/chromium/media/base/android/provision_fetcher.h
+++ b/chromium/media/base/android/provision_fetcher.h
@@ -8,6 +8,7 @@
#include <string>
#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
#include "media/base/media_export.h"
namespace media {
diff --git a/chromium/media/base/android/sdk_media_codec_bridge.cc b/chromium/media/base/android/sdk_media_codec_bridge.cc
index 87b11760f06..226a90a6cbf 100644
--- a/chromium/media/base/android/sdk_media_codec_bridge.cc
+++ b/chromium/media/base/android/sdk_media_codec_bridge.cc
@@ -112,17 +112,29 @@ void SdkMediaCodecBridge::Stop() {
Java_MediaCodecBridge_stop(env, j_media_codec_.obj());
}
-void SdkMediaCodecBridge::GetOutputFormat(int* width, int* height) {
+MediaCodecStatus SdkMediaCodecBridge::GetOutputSize(gfx::Size* size) {
JNIEnv* env = AttachCurrentThread();
-
- *width = Java_MediaCodecBridge_getOutputWidth(env, j_media_codec_.obj());
- *height = Java_MediaCodecBridge_getOutputHeight(env, j_media_codec_.obj());
+ ScopedJavaLocalRef<jobject> result =
+ Java_MediaCodecBridge_getOutputFormat(env, j_media_codec_.obj());
+ MediaCodecStatus status = static_cast<MediaCodecStatus>(
+ Java_GetOutputFormatResult_status(env, result.obj()));
+ if (status == MEDIA_CODEC_OK) {
+ size->SetSize(Java_GetOutputFormatResult_width(env, result.obj()),
+ Java_GetOutputFormatResult_height(env, result.obj()));
+ }
+ return status;
}
-int SdkMediaCodecBridge::GetOutputSamplingRate() {
+MediaCodecStatus SdkMediaCodecBridge::GetOutputSamplingRate(
+ int* sampling_rate) {
JNIEnv* env = AttachCurrentThread();
-
- return Java_MediaCodecBridge_getOutputSamplingRate(env, j_media_codec_.obj());
+ ScopedJavaLocalRef<jobject> result =
+ Java_MediaCodecBridge_getOutputFormat(env, j_media_codec_.obj());
+ MediaCodecStatus status = static_cast<MediaCodecStatus>(
+ Java_GetOutputFormatResult_status(env, result.obj()));
+ if (status == MEDIA_CODEC_OK)
+ *sampling_rate = Java_GetOutputFormatResult_sampleRate(env, result.obj());
+ return status;
}
MediaCodecStatus SdkMediaCodecBridge::QueueInputBuffer(
@@ -274,52 +286,52 @@ void SdkMediaCodecBridge::ReleaseOutputBuffer(int index, bool render) {
render);
}
-int SdkMediaCodecBridge::GetOutputBuffersCount() {
- JNIEnv* env = AttachCurrentThread();
- return Java_MediaCodecBridge_getOutputBuffersCount(env, j_media_codec_.obj());
-}
-
-size_t SdkMediaCodecBridge::GetOutputBuffersCapacity() {
- JNIEnv* env = AttachCurrentThread();
- return Java_MediaCodecBridge_getOutputBuffersCapacity(env,
- j_media_codec_.obj());
-}
-
-void SdkMediaCodecBridge::GetInputBuffer(int input_buffer_index,
- uint8_t** data,
- size_t* capacity) {
+MediaCodecStatus SdkMediaCodecBridge::GetInputBuffer(int input_buffer_index,
+ uint8_t** data,
+ size_t* capacity) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> j_buffer(Java_MediaCodecBridge_getInputBuffer(
env, j_media_codec_.obj(), input_buffer_index));
+ if (j_buffer.is_null())
+ return MEDIA_CODEC_ERROR;
+
*data = static_cast<uint8_t*>(env->GetDirectBufferAddress(j_buffer.obj()));
*capacity =
base::checked_cast<size_t>(env->GetDirectBufferCapacity(j_buffer.obj()));
+ return MEDIA_CODEC_OK;
}
-bool SdkMediaCodecBridge::CopyFromOutputBuffer(int index,
- size_t offset,
- void* dst,
- int dst_size) {
+MediaCodecStatus SdkMediaCodecBridge::CopyFromOutputBuffer(int index,
+ size_t offset,
+ void* dst,
+ size_t num) {
void* src_data = nullptr;
- size_t src_capacity = GetOutputBufferAddress(index, offset, &src_data);
- if (src_capacity < offset ||
- src_capacity - offset < static_cast<size_t>(dst_size)) {
- return false;
+ size_t src_capacity = 0;
+ MediaCodecStatus status =
+ GetOutputBufferAddress(index, offset, &src_data, &src_capacity);
+ if (status == MEDIA_CODEC_OK) {
+ CHECK_GE(src_capacity, num);
+ memcpy(dst, src_data, num);
}
- memcpy(dst, static_cast<uint8_t*>(src_data) + offset, dst_size);
- return true;
+ return status;
}
-int SdkMediaCodecBridge::GetOutputBufferAddress(int index,
- size_t offset,
- void** addr) {
+MediaCodecStatus SdkMediaCodecBridge::GetOutputBufferAddress(int index,
+ size_t offset,
+ void** addr,
+ size_t* capacity) {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> j_buffer(
Java_MediaCodecBridge_getOutputBuffer(env, j_media_codec_.obj(), index));
+ if (j_buffer.is_null())
+ return MEDIA_CODEC_ERROR;
+ const size_t total_capacity = env->GetDirectBufferCapacity(j_buffer.obj());
+ CHECK_GE(total_capacity, offset);
*addr =
reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(j_buffer.obj())) +
offset;
- return env->GetDirectBufferCapacity(j_buffer.obj()) - offset;
+ *capacity = total_capacity - offset;
+ return MEDIA_CODEC_OK;
}
// static
@@ -333,7 +345,14 @@ AudioCodecBridge* AudioCodecBridge::Create(const AudioCodec& codec) {
return nullptr;
const std::string mime = AudioCodecToAndroidMimeType(codec);
- return mime.empty() ? nullptr : new AudioCodecBridge(mime);
+ if (mime.empty())
+ return nullptr;
+
+ scoped_ptr<AudioCodecBridge> bridge(new AudioCodecBridge(mime));
+ if (!bridge->media_codec())
+ return nullptr;
+
+ return bridge.release();
}
// static
@@ -347,6 +366,23 @@ AudioCodecBridge::AudioCodecBridge(const std::string& mime)
// audio encoding yet.
: SdkMediaCodecBridge(mime, false, MEDIA_CODEC_DECODER) {}
+bool AudioCodecBridge::ConfigureAndStart(const AudioDecoderConfig& config,
+ bool play_audio,
+ jobject media_crypto) {
+ const int channel_count =
+ ChannelLayoutToChannelCount(config.channel_layout());
+ const int64_t codec_delay_ns = base::Time::kNanosecondsPerSecond *
+ config.codec_delay() /
+ config.samples_per_second();
+ const int64_t seek_preroll_ns =
+ 1000LL * config.seek_preroll().InMicroseconds();
+
+ return ConfigureAndStart(config.codec(), config.samples_per_second(),
+ channel_count, config.extra_data().data(),
+ config.extra_data().size(), codec_delay_ns,
+ seek_preroll_ns, play_audio, media_crypto);
+}
+
bool AudioCodecBridge::ConfigureAndStart(const AudioCodec& codec,
int sample_rate,
int channel_count,
@@ -356,15 +392,22 @@ bool AudioCodecBridge::ConfigureAndStart(const AudioCodec& codec,
int64_t seek_preroll_ns,
bool play_audio,
jobject media_crypto) {
- JNIEnv* env = AttachCurrentThread();
-
- if (!media_codec())
- return false;
+ DVLOG(2) << __FUNCTION__ << ": "
+ << " codec:" << GetCodecName(codec)
+ << " samples_per_second:" << sample_rate
+ << " channel_count:" << channel_count
+ << " codec_delay_ns:" << codec_delay_ns
+ << " seek_preroll_ns:" << seek_preroll_ns
+ << " extra data size:" << extra_data_size
+ << " play audio:" << play_audio << " media_crypto:" << media_crypto;
+ DCHECK(media_codec());
std::string codec_string = AudioCodecToAndroidMimeType(codec);
if (codec_string.empty())
return false;
+ JNIEnv* env = AttachCurrentThread();
+
ScopedJavaLocalRef<jstring> j_mime =
ConvertUTF8ToJavaString(env, codec_string);
ScopedJavaLocalRef<jobject> j_format(Java_MediaCodecBridge_createAudioFormat(
@@ -515,23 +558,30 @@ bool AudioCodecBridge::ConfigureMediaFormat(jobject j_format,
return true;
}
-int64_t AudioCodecBridge::PlayOutputBuffer(int index,
- size_t size,
- size_t offset,
- bool postpone) {
+MediaCodecStatus AudioCodecBridge::PlayOutputBuffer(int index,
+ size_t size,
+ size_t offset,
+ bool postpone,
+ int64_t* playback_pos) {
DCHECK_LE(0, index);
int numBytes = base::checked_cast<int>(size);
void* buffer = nullptr;
- int capacity = GetOutputBufferAddress(index, offset, &buffer);
- numBytes = std::min(capacity, numBytes);
+ size_t capacity = 0;
+ MediaCodecStatus status =
+ GetOutputBufferAddress(index, offset, &buffer, &capacity);
+ if (status == MEDIA_CODEC_ERROR)
+ return status;
+
+ numBytes = std::min(base::checked_cast<int>(capacity), numBytes);
CHECK_GE(numBytes, 0);
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jbyteArray> byte_array = base::android::ToJavaByteArray(
env, static_cast<uint8_t*>(buffer), numBytes);
- return Java_MediaCodecBridge_playOutputBuffer(env, media_codec(),
- byte_array.obj(), postpone);
+ *playback_pos = Java_MediaCodecBridge_playOutputBuffer(
+ env, media_codec(), byte_array.obj(), postpone);
+ return status;
}
void AudioCodecBridge::SetVolume(double volume) {
@@ -547,11 +597,13 @@ bool VideoCodecBridge::IsKnownUnaccelerated(const VideoCodec& codec,
}
// static
-VideoCodecBridge* VideoCodecBridge::CreateDecoder(const VideoCodec& codec,
- bool is_secure,
- const gfx::Size& size,
- jobject surface,
- jobject media_crypto) {
+VideoCodecBridge* VideoCodecBridge::CreateDecoder(
+ const VideoCodec& codec,
+ bool is_secure,
+ const gfx::Size& size,
+ jobject surface,
+ jobject media_crypto,
+ bool allow_adaptive_playback) {
if (!MediaCodecUtil::IsMediaCodecAvailable())
return nullptr;
@@ -570,9 +622,9 @@ VideoCodecBridge* VideoCodecBridge::CreateDecoder(const VideoCodec& codec,
Java_MediaCodecBridge_createVideoDecoderFormat(
env, j_mime.obj(), size.width(), size.height()));
DCHECK(!j_format.is_null());
- if (!Java_MediaCodecBridge_configureVideo(env, bridge->media_codec(),
- j_format.obj(), surface,
- media_crypto, 0)) {
+ if (!Java_MediaCodecBridge_configureVideo(
+ env, bridge->media_codec(), j_format.obj(), surface, media_crypto, 0,
+ allow_adaptive_playback)) {
return nullptr;
}
@@ -607,7 +659,7 @@ VideoCodecBridge* VideoCodecBridge::CreateEncoder(const VideoCodec& codec,
DCHECK(!j_format.is_null());
if (!Java_MediaCodecBridge_configureVideo(env, bridge->media_codec(),
j_format.obj(), nullptr, nullptr,
- kConfigureFlagEncode)) {
+ kConfigureFlagEncode, true)) {
return nullptr;
}
diff --git a/chromium/media/base/android/sdk_media_codec_bridge.h b/chromium/media/base/android/sdk_media_codec_bridge.h
index 9079cbadd85..e23400df319 100644
--- a/chromium/media/base/android/sdk_media_codec_bridge.h
+++ b/chromium/media/base/android/sdk_media_codec_bridge.h
@@ -32,8 +32,8 @@ class MEDIA_EXPORT SdkMediaCodecBridge : public MediaCodecBridge {
MediaCodecStatus Reset() override;
bool Start() override;
void Stop() override;
- void GetOutputFormat(int* width, int* height) override;
- int GetOutputSamplingRate() override;
+ MediaCodecStatus GetOutputSize(gfx::Size* size) override;
+ MediaCodecStatus GetOutputSamplingRate(int* sampling_rate) override;
MediaCodecStatus QueueInputBuffer(
int index,
const uint8_t* data,
@@ -60,15 +60,13 @@ class MEDIA_EXPORT SdkMediaCodecBridge : public MediaCodecBridge {
bool* end_of_stream,
bool* key_frame) override;
void ReleaseOutputBuffer(int index, bool render) override;
- int GetOutputBuffersCount() override;
- size_t GetOutputBuffersCapacity() override;
- void GetInputBuffer(int input_buffer_index,
- uint8_t** data,
- size_t* capacity) override;
- bool CopyFromOutputBuffer(int index,
- size_t offset,
- void* dst,
- int dst_size) override;
+ MediaCodecStatus GetInputBuffer(int input_buffer_index,
+ uint8_t** data,
+ size_t* capacity) override;
+ MediaCodecStatus CopyFromOutputBuffer(int index,
+ size_t offset,
+ void* dst,
+ size_t num) override;
static bool RegisterSdkMediaCodecBridge(JNIEnv* env);
@@ -78,9 +76,13 @@ class MEDIA_EXPORT SdkMediaCodecBridge : public MediaCodecBridge {
MediaCodecDirection direction);
// Called to get the buffer address given the output buffer index and offset.
- // This function returns the size of the output and |addr| is the pointer to
- // the address to read.
- int GetOutputBufferAddress(int index, size_t offset, void** addr);
+ // The size of available data to read is written to |*capacity| and the
+ // address to read from is written to |*addr|.
+ // Returns MEDIA_CODEC_ERROR if a error occurs, or MEDIA_CODEC_OK otherwise.
+ MediaCodecStatus GetOutputBufferAddress(int index,
+ size_t offset,
+ void** addr,
+ size_t* capacity);
jobject media_codec() { return j_media_codec_.obj(); }
MediaCodecDirection direction_;
@@ -104,7 +106,16 @@ class MEDIA_EXPORT AudioCodecBridge : public SdkMediaCodecBridge {
// See MediaCodecUtil::IsKnownUnaccelerated().
static bool IsKnownUnaccelerated(const AudioCodec& codec);
- // Start the audio codec bridge.
+ // Start the audio codec bridge. If |play_audio| is true this method creates
+ // Android AudioTrack object for the actual audio playback
+ // (http://developer.android.com/reference/android/media/AudioTrack.html).
+ bool ConfigureAndStart(const AudioDecoderConfig& config,
+ bool play_audio,
+ jobject media_crypto);
+
+ // An overloaded variant used by AudioDecoderJob and AudioMediaCodecDecoder.
+ // TODO(timav): Modify the above mentioned classes to pass parameters as
+ // AudioDecoderConfig and remove this method.
bool ConfigureAndStart(const AudioCodec& codec,
int sample_rate,
int channel_count,
@@ -118,15 +129,17 @@ class MEDIA_EXPORT AudioCodecBridge : public SdkMediaCodecBridge {
// Plays the output buffer right away or save for later playback if |postpone|
// is set to true. This call must be called after DequeueOutputBuffer() and
// before ReleaseOutputBuffer. The data is extracted from the output buffers
- // using |index|, |size| and |offset|. Returns the playback head position
- // expressed in frames.
+ // using |index|, |size| and |offset|. The playback head position in frames is
+ // output in |*playback_pos|.
// When |postpone| is set to true, the next PlayOutputBuffer() should have
// postpone == false, and it will play two buffers: the postponed one and
// the one identified by |index|.
- int64_t PlayOutputBuffer(int index,
- size_t size,
- size_t offset,
- bool postpone = false);
+ // Returns MEDIA_CODEC_ERROR if an error occurs, or MEDIA_CODEC_OK otherwise.
+ MediaCodecStatus PlayOutputBuffer(int index,
+ size_t size,
+ size_t offset,
+ bool postpone,
+ int64_t* playback_pos);
// Set the volume of the audio output.
void SetVolume(double volume);
@@ -155,10 +168,12 @@ class MEDIA_EXPORT VideoCodecBridge : public SdkMediaCodecBridge {
// Create, start, and return a VideoCodecBridge decoder or NULL on failure.
static VideoCodecBridge* CreateDecoder(
const VideoCodec& codec, // e.g. media::kCodecVP8
- bool is_secure,
- const gfx::Size& size, // Output frame size.
- jobject surface, // Output surface, optional.
- jobject media_crypto); // MediaCrypto object, optional.
+ bool is_secure, // Will be used with encrypted content.
+ const gfx::Size& size, // Output frame size.
+ jobject surface, // Output surface, optional.
+ jobject media_crypto, // MediaCrypto object, optional.
+ bool allow_adaptive_playback =
+ true); // Should adaptive playback be allowed if supported.
// Create, start, and return a VideoCodecBridge encoder or NULL on failure.
static VideoCodecBridge* CreateEncoder(
diff --git a/chromium/media/base/android/sdk_media_codec_bridge_unittest.cc b/chromium/media/base/android/sdk_media_codec_bridge_unittest.cc
index ef32acf684f..5ef6da1d5e9 100644
--- a/chromium/media/base/android/sdk_media_codec_bridge_unittest.cc
+++ b/chromium/media/base/android/sdk_media_codec_bridge_unittest.cc
@@ -90,6 +90,7 @@ unsigned char test_mp3[] = {
0x8a, 0xb3, 0x52, 0xd1, 0x3d, 0x79, 0x81, 0x4d, 0x31, 0x24, 0xf9, 0x38,
0x96, 0xbc, 0xf4, 0x8c, 0x25, 0xe9, 0xf2, 0x73, 0x94, 0x85, 0xc2, 0x61,
0x6a, 0x34, 0x68, 0x65, 0x78, 0x87, 0xa6, 0x4f};
+static const size_t kDecodedAudioLengthInBytes = 9216u;
} // namespace
@@ -105,6 +106,7 @@ namespace media {
} while (0)
static const int kPresentationTimeBase = 100;
+static const int kMaxInputPts = kPresentationTimeBase + 2;
static inline const base::TimeDelta InfiniteTimeOut() {
return base::TimeDelta::FromMicroseconds(-1);
@@ -181,6 +183,7 @@ TEST(SdkMediaCodecBridgeTest, DoNormal) {
input_pts = kPresentationTimeBase;
bool eos = false;
+ size_t total_size = 0;
while (!eos) {
size_t unused_offset = 0;
size_t size = 0;
@@ -205,11 +208,10 @@ TEST(SdkMediaCodecBridgeTest, DoNormal) {
}
ASSERT_GE(output_buf_index, 0);
EXPECT_LE(1u, size);
- if (!eos)
- EXPECT_EQ(++input_pts, timestamp.InMicroseconds());
- ASSERT_LE(input_pts, kPresentationTimeBase + 2);
+ total_size += size;
}
- ASSERT_EQ(input_pts, kPresentationTimeBase + 2);
+ EXPECT_EQ(kDecodedAudioLengthInBytes, total_size);
+ ASSERT_LE(input_pts, kMaxInputPts);
}
TEST(SdkMediaCodecBridgeTest, InvalidVorbisHeader) {
@@ -248,6 +250,9 @@ TEST(SdkMediaCodecBridgeTest, InvalidOpusHeader) {
scoped_ptr<media::AudioCodecBridge> media_codec;
media_codec.reset(AudioCodecBridge::Create(kCodecOpus));
+ if (!media_codec)
+ return;
+
uint8_t dummy_extra_data[] = {0, 0};
// Extra Data is NULL.
diff --git a/chromium/media/base/android/video_decoder_job.cc b/chromium/media/base/android/video_decoder_job.cc
index fa49ab3ba48..3e3123c98ce 100644
--- a/chromium/media/base/android/video_decoder_job.cc
+++ b/chromium/media/base/android/video_decoder_job.cc
@@ -151,7 +151,7 @@ bool VideoDecoderJob::UpdateOutputFormat() {
return false;
int prev_output_width = output_width_;
int prev_output_height = output_height_;
- // See b/18224769. The values reported from MediaCodecBridge::GetOutputFormat
+ // See b/18224769. The values reported from MediaCodecBridge::GetOutputSize
// correspond to the actual video frame size, but this is not necessarily the
// size that should be output.
output_width_ = config_width_;
diff --git a/chromium/media/base/android/media_codec_video_decoder.cc b/chromium/media/base/android/video_media_codec_decoder.cc
index 2726d3aa5b0..e9a305830a4 100644
--- a/chromium/media/base/android/media_codec_video_decoder.cc
+++ b/chromium/media/base/android/video_media_codec_decoder.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "media/base/android/media_codec_video_decoder.h"
+#include "media/base/android/video_media_codec_decoder.h"
#include <utility>
@@ -19,7 +19,7 @@ namespace {
const int kDelayForStandAloneEOS = 2; // milliseconds
}
-MediaCodecVideoDecoder::MediaCodecVideoDecoder(
+VideoMediaCodecDecoder::VideoMediaCodecDecoder(
const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner,
FrameStatistics* frame_statistics,
const base::Closure& request_data_cb,
@@ -41,26 +41,25 @@ MediaCodecVideoDecoder::MediaCodecVideoDecoder(
error_cb),
is_protected_surface_required_(false),
update_current_time_cb_(update_current_time_cb),
- video_size_changed_cb_(video_size_changed_cb) {
-}
+ video_size_changed_cb_(video_size_changed_cb) {}
-MediaCodecVideoDecoder::~MediaCodecVideoDecoder() {
+VideoMediaCodecDecoder::~VideoMediaCodecDecoder() {
DCHECK(media_task_runner_->BelongsToCurrentThread());
DVLOG(1) << "VideoDecoder::~VideoDecoder()";
ReleaseDecoderResources();
}
-const char* MediaCodecVideoDecoder::class_name() const {
+const char* VideoMediaCodecDecoder::class_name() const {
return "VideoDecoder";
}
-bool MediaCodecVideoDecoder::HasStream() const {
+bool VideoMediaCodecDecoder::HasStream() const {
DCHECK(media_task_runner_->BelongsToCurrentThread());
return configs_.video_codec != kUnknownVideoCodec;
}
-void MediaCodecVideoDecoder::SetDemuxerConfigs(const DemuxerConfigs& configs) {
+void VideoMediaCodecDecoder::SetDemuxerConfigs(const DemuxerConfigs& configs) {
DVLOG(1) << class_name() << "::" << __FUNCTION__ << " " << configs;
configs_ = configs;
@@ -72,13 +71,13 @@ void MediaCodecVideoDecoder::SetDemuxerConfigs(const DemuxerConfigs& configs) {
}
}
-bool MediaCodecVideoDecoder::IsContentEncrypted() const {
+bool VideoMediaCodecDecoder::IsContentEncrypted() const {
// Make sure SetDemuxerConfigs() as been called.
DCHECK(configs_.video_codec != kUnknownVideoCodec);
return configs_.is_video_encrypted;
}
-void MediaCodecVideoDecoder::ReleaseDecoderResources() {
+void VideoMediaCodecDecoder::ReleaseDecoderResources() {
DCHECK(media_task_runner_->BelongsToCurrentThread());
DVLOG(1) << class_name() << "::" << __FUNCTION__;
@@ -89,14 +88,14 @@ void MediaCodecVideoDecoder::ReleaseDecoderResources() {
surface_ = gfx::ScopedJavaSurface();
}
-void MediaCodecVideoDecoder::ReleaseMediaCodec() {
+void VideoMediaCodecDecoder::ReleaseMediaCodec() {
DCHECK(media_task_runner_->BelongsToCurrentThread());
MediaCodecDecoder::ReleaseMediaCodec();
delayed_buffers_.clear();
}
-void MediaCodecVideoDecoder::SetVideoSurface(gfx::ScopedJavaSurface surface) {
+void VideoMediaCodecDecoder::SetVideoSurface(gfx::ScopedJavaSurface surface) {
DCHECK(media_task_runner_->BelongsToCurrentThread());
DVLOG(1) << class_name() << "::" << __FUNCTION__
@@ -107,25 +106,25 @@ void MediaCodecVideoDecoder::SetVideoSurface(gfx::ScopedJavaSurface surface) {
needs_reconfigure_ = true;
}
-bool MediaCodecVideoDecoder::HasVideoSurface() const {
+bool VideoMediaCodecDecoder::HasVideoSurface() const {
DCHECK(media_task_runner_->BelongsToCurrentThread());
return !surface_.IsEmpty();
}
-void MediaCodecVideoDecoder::SetProtectedSurfaceRequired(bool value) {
+void VideoMediaCodecDecoder::SetProtectedSurfaceRequired(bool value) {
DCHECK(media_task_runner_->BelongsToCurrentThread());
is_protected_surface_required_ = value;
}
-bool MediaCodecVideoDecoder::IsProtectedSurfaceRequired() const {
+bool VideoMediaCodecDecoder::IsProtectedSurfaceRequired() const {
DCHECK(media_task_runner_->BelongsToCurrentThread());
return is_protected_surface_required_;
}
-bool MediaCodecVideoDecoder::IsCodecReconfigureNeeded(
+bool VideoMediaCodecDecoder::IsCodecReconfigureNeeded(
const DemuxerConfigs& next) const {
if (always_reconfigure_for_tests_)
return true;
@@ -143,11 +142,11 @@ bool MediaCodecVideoDecoder::IsCodecReconfigureNeeded(
}
return !static_cast<VideoCodecBridge*>(media_codec_bridge_.get())
- ->IsAdaptivePlaybackSupported(next.video_size.width(),
- next.video_size.height());
+ ->IsAdaptivePlaybackSupported(next.video_size.width(),
+ next.video_size.height());
}
-MediaCodecDecoder::ConfigStatus MediaCodecVideoDecoder::ConfigureInternal(
+MediaCodecDecoder::ConfigStatus VideoMediaCodecDecoder::ConfigureInternal(
jobject media_crypto) {
DCHECK(media_task_runner_->BelongsToCurrentThread());
@@ -173,11 +172,8 @@ MediaCodecDecoder::ConfigStatus MediaCodecVideoDecoder::ConfigureInternal(
bool is_secure = IsContentEncrypted() && is_protected_surface_required_;
media_codec_bridge_.reset(VideoCodecBridge::CreateDecoder(
- configs_.video_codec,
- is_secure,
- configs_.video_size,
- surface_.j_surface().obj(),
- media_crypto));
+ configs_.video_codec, is_secure, configs_.video_size,
+ surface_.j_surface().obj(), media_crypto));
if (!media_codec_bridge_) {
DVLOG(0) << class_name() << "::" << __FUNCTION__
@@ -193,7 +189,7 @@ MediaCodecDecoder::ConfigStatus MediaCodecVideoDecoder::ConfigureInternal(
return kConfigOk;
}
-void MediaCodecVideoDecoder::AssociateCurrentTimeWithPTS(base::TimeDelta pts) {
+void VideoMediaCodecDecoder::AssociateCurrentTimeWithPTS(base::TimeDelta pts) {
DCHECK(media_task_runner_->BelongsToCurrentThread());
DVLOG(1) << class_name() << "::" << __FUNCTION__ << " pts:" << pts;
@@ -203,18 +199,18 @@ void MediaCodecVideoDecoder::AssociateCurrentTimeWithPTS(base::TimeDelta pts) {
last_seen_pts_ = pts;
}
-void MediaCodecVideoDecoder::DissociatePTSFromTime() {
+void VideoMediaCodecDecoder::DissociatePTSFromTime() {
DCHECK(media_task_runner_->BelongsToCurrentThread());
start_pts_ = last_seen_pts_ = kNoTimestamp();
}
-void MediaCodecVideoDecoder::OnOutputFormatChanged() {
+void VideoMediaCodecDecoder::OnOutputFormatChanged() {
DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread());
gfx::Size prev_size = video_size_;
- // See b/18224769. The values reported from MediaCodecBridge::GetOutputFormat
+ // See b/18224769. The values reported from MediaCodecBridge::GetOutputSize
// correspond to the actual video frame size, but this is not necessarily the
// size that should be output.
video_size_ = configs_.video_size;
@@ -224,7 +220,7 @@ void MediaCodecVideoDecoder::OnOutputFormatChanged() {
}
}
-void MediaCodecVideoDecoder::Render(int buffer_index,
+void VideoMediaCodecDecoder::Render(int buffer_index,
size_t offset,
size_t size,
RenderMode render_mode,
@@ -299,19 +295,19 @@ void MediaCodecVideoDecoder::Render(int buffer_index,
delayed_buffers_.insert(buffer_index);
decoder_thread_.task_runner()->PostDelayedTask(
- FROM_HERE, base::Bind(&MediaCodecVideoDecoder::ReleaseOutputBuffer,
+ FROM_HERE, base::Bind(&VideoMediaCodecDecoder::ReleaseOutputBuffer,
base::Unretained(this), buffer_index, pts, render,
update_time, eos_encountered),
time_to_render);
}
-int MediaCodecVideoDecoder::NumDelayedRenderTasks() const {
+int VideoMediaCodecDecoder::NumDelayedRenderTasks() const {
DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread());
return delayed_buffers_.size();
}
-void MediaCodecVideoDecoder::ReleaseDelayedBuffers() {
+void VideoMediaCodecDecoder::ReleaseDelayedBuffers() {
// Media thread
// Called when there is no decoder thread
for (int index : delayed_buffers_)
@@ -321,7 +317,7 @@ void MediaCodecVideoDecoder::ReleaseDelayedBuffers() {
}
#ifndef NDEBUG
-void MediaCodecVideoDecoder::VerifyUnitIsKeyFrame(
+void VideoMediaCodecDecoder::VerifyUnitIsKeyFrame(
const AccessUnit* unit) const {
// The first video frame in a sequence must be a key frame or stand-alone EOS.
DCHECK(unit);
@@ -330,7 +326,7 @@ void MediaCodecVideoDecoder::VerifyUnitIsKeyFrame(
}
#endif
-void MediaCodecVideoDecoder::ReleaseOutputBuffer(int buffer_index,
+void VideoMediaCodecDecoder::ReleaseOutputBuffer(int buffer_index,
base::TimeDelta pts,
bool render,
bool update_time,
diff --git a/chromium/media/base/android/media_codec_video_decoder.h b/chromium/media/base/android/video_media_codec_decoder.h
index b7fc3950a24..03497269ba1 100644
--- a/chromium/media/base/android/media_codec_video_decoder.h
+++ b/chromium/media/base/android/video_media_codec_decoder.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef MEDIA_BASE_ANDROID_MEDIA_CODEC_VIDEO_DECODER_H_
-#define MEDIA_BASE_ANDROID_MEDIA_CODEC_VIDEO_DECODER_H_
+#ifndef MEDIA_BASE_ANDROID_VIDEO_MEDIA_CODEC_DECODER_H_
+#define MEDIA_BASE_ANDROID_VIDEO_MEDIA_CODEC_DECODER_H_
#include <stddef.h>
@@ -16,7 +16,7 @@
namespace media {
// Video decoder for MediaCodecPlayer
-class MediaCodecVideoDecoder : public MediaCodecDecoder {
+class VideoMediaCodecDecoder : public MediaCodecDecoder {
public:
// Typedefs for the notification callbacks
typedef base::Callback<void(const gfx::Size& video_size)>
@@ -29,7 +29,7 @@ class MediaCodecVideoDecoder : public MediaCodecDecoder {
// codec_created_cb: reports that video codec has been created. A controller
// class might use it to release more resources so that this
// decoder can use them.
- MediaCodecVideoDecoder(
+ VideoMediaCodecDecoder(
const scoped_refptr<base::SingleThreadTaskRunner>& media_runner,
FrameStatistics* frame_statistics,
const base::Closure& request_data_cb,
@@ -40,7 +40,7 @@ class MediaCodecVideoDecoder : public MediaCodecDecoder {
const base::Closure& error_cb,
const SetTimeCallback& update_current_time_cb,
const VideoSizeChangedCallback& video_size_changed_cb);
- ~MediaCodecVideoDecoder() override;
+ ~VideoMediaCodecDecoder() override;
const char* class_name() const override;
@@ -122,9 +122,9 @@ class MediaCodecVideoDecoder : public MediaCodecDecoder {
// Mantain the last seen PTS for stand-alone EOS.
base::TimeDelta last_seen_pts_;
- DISALLOW_COPY_AND_ASSIGN(MediaCodecVideoDecoder);
+ DISALLOW_COPY_AND_ASSIGN(VideoMediaCodecDecoder);
};
} // namespace media
-#endif // MEDIA_BASE_ANDROID_MEDIA_CODEC_VIDEO_DECODER_H_
+#endif // MEDIA_BASE_ANDROID_VIDEO_MEDIA_CODEC_DECODER_H_
diff --git a/chromium/media/base/audio_buffer.cc b/chromium/media/base/audio_buffer.cc
index ac9c31a1b22..9370ee891b0 100644
--- a/chromium/media/base/audio_buffer.cc
+++ b/chromium/media/base/audio_buffer.cc
@@ -208,6 +208,12 @@ inline int16_t ConvertSample<float, int16_t>(float sample) {
: sample * std::numeric_limits<int16_t>::max()));
}
+void AudioBuffer::AdjustSampleRate(int sample_rate) {
+ DCHECK(!end_of_stream_);
+ sample_rate_ = sample_rate;
+ duration_ = CalculateDuration(adjusted_frame_count_, sample_rate_);
+}
+
void AudioBuffer::ReadFrames(int frames_to_copy,
int source_frame_offset,
int dest_frame_offset,
@@ -275,9 +281,9 @@ void AudioBuffer::ReadFrames(int frames_to_copy,
// Remaining formats are integer interleaved data. Use the deinterleaving code
// in AudioBus to copy the data.
- DCHECK(sample_format_ == kSampleFormatU8 ||
- sample_format_ == kSampleFormatS16 ||
- sample_format_ == kSampleFormatS32);
+ DCHECK(
+ sample_format_ == kSampleFormatU8 || sample_format_ == kSampleFormatS16 ||
+ sample_format_ == kSampleFormatS24 || sample_format_ == kSampleFormatS32);
int bytes_per_channel = SampleFormatToBytesPerChannel(sample_format_);
int frame_size = channel_count_ * bytes_per_channel;
const uint8_t* source_data = data_.get() + source_frame_offset * frame_size;
diff --git a/chromium/media/base/audio_buffer.h b/chromium/media/base/audio_buffer.h
index 8ae281eabda..6d74c7773cf 100644
--- a/chromium/media/base/audio_buffer.h
+++ b/chromium/media/base/audio_buffer.h
@@ -78,6 +78,12 @@ class MEDIA_EXPORT AudioBuffer
// is disallowed.
static scoped_refptr<AudioBuffer> CreateEOSBuffer();
+ // Update sample rate and computed duration.
+ // TODO(chcunningham): Remove this upon patching FFmpeg's AAC decoder to
+ // provide the correct sample rate at the boundary of an implicit config
+ // change.
+ void AdjustSampleRate(int sample_rate);
+
// Copy frames into |dest|. |frames_to_copy| is the number of frames to copy.
// |source_frame_offset| specifies how many frames in the buffer to skip
// first. |dest_frame_offset| is the frame offset in |dest|. The frames are
@@ -131,10 +137,16 @@ class MEDIA_EXPORT AudioBuffer
// If there's no data in this buffer, it represents end of stream.
bool end_of_stream() const { return end_of_stream_; }
- // Access to the raw buffer for ffmpeg to write directly to. Data for planar
- // data is grouped by channel. There is only 1 entry for interleaved formats.
+ // Access to the raw buffer for ffmpeg and Android MediaCodec decoders to
+ // write directly to. For planar formats the vector elements correspond to
+ // the channels. For interleaved formats the resulting vector has exactly
+ // one element which contains the buffer pointer.
const std::vector<uint8_t*>& channel_data() const { return channel_data_; }
+ // The size of allocated data memory block. For planar formats channels go
+ // sequentially in this block.
+ size_t data_size() const { return data_size_; }
+
private:
friend class base::RefCountedThreadSafe<AudioBuffer>;
@@ -162,7 +174,7 @@ class MEDIA_EXPORT AudioBuffer
const SampleFormat sample_format_;
const ChannelLayout channel_layout_;
const int channel_count_;
- const int sample_rate_;
+ int sample_rate_;
int adjusted_frame_count_;
int trim_start_;
const bool end_of_stream_;
diff --git a/chromium/media/base/audio_capturer_source.h b/chromium/media/base/audio_capturer_source.h
index 31b39bbf993..fd79db3dfcd 100644
--- a/chromium/media/base/audio_capturer_source.h
+++ b/chromium/media/base/audio_capturer_source.h
@@ -23,6 +23,8 @@ class AudioCapturerSource
class CaptureCallback {
public:
// Callback to deliver the captured data from the OS.
+ // TODO(chcunningham): Update delay argument to use frames instead of
+ // milliseconds to prevent loss of precision. See http://crbug.com/587291.
virtual void Capture(const AudioBus* audio_source,
int audio_delay_milliseconds,
double volume,
diff --git a/chromium/media/base/audio_codecs.cc b/chromium/media/base/audio_codecs.cc
new file mode 100644
index 00000000000..46a7b2510fa
--- /dev/null
+++ b/chromium/media/base/audio_codecs.cc
@@ -0,0 +1,51 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/base/audio_codecs.h"
+
+#include "base/logging.h"
+
+namespace media {
+
+// These names come from src/third_party/ffmpeg/libavcodec/codec_desc.c
+std::string GetCodecName(AudioCodec codec) {
+ switch (codec) {
+ case kUnknownAudioCodec:
+ return "unknown";
+ case kCodecAAC:
+ return "aac";
+ case kCodecMP3:
+ return "mp3";
+ case kCodecPCM:
+ case kCodecPCM_S16BE:
+ case kCodecPCM_S24BE:
+ return "pcm";
+ case kCodecVorbis:
+ return "vorbis";
+ case kCodecFLAC:
+ return "flac";
+ case kCodecAMR_NB:
+ return "amr_nb";
+ case kCodecAMR_WB:
+ return "amr_wb";
+ case kCodecPCM_MULAW:
+ return "pcm_mulaw";
+ case kCodecGSM_MS:
+ return "gsm_ms";
+ case kCodecOpus:
+ return "opus";
+ case kCodecPCM_ALAW:
+ return "pcm_alaw";
+ case kCodecEAC3:
+ return "eac3";
+ case kCodecALAC:
+ return "alac";
+ case kCodecAC3:
+ return "ac3";
+ }
+ NOTREACHED();
+ return "";
+}
+
+} // namespace media
diff --git a/chromium/media/base/audio_codecs.h b/chromium/media/base/audio_codecs.h
new file mode 100644
index 00000000000..5cba02955bf
--- /dev/null
+++ b/chromium/media/base/audio_codecs.h
@@ -0,0 +1,48 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_BASE_AUDIO_CODECS_H_
+#define MEDIA_BASE_AUDIO_CODECS_H_
+
+#include <string>
+#include "media/base/media_export.h"
+
+namespace media {
+
+enum AudioCodec {
+ // These values are histogrammed over time; do not change their ordinal
+ // values. When deleting a codec replace it with a dummy value; when adding a
+ // codec, do so at the bottom before kAudioCodecMax, and update the value of
+ // kAudioCodecMax to equal the new codec.
+ kUnknownAudioCodec = 0,
+ kCodecAAC = 1,
+ kCodecMP3 = 2,
+ kCodecPCM = 3,
+ kCodecVorbis = 4,
+ kCodecFLAC = 5,
+ kCodecAMR_NB = 6,
+ kCodecAMR_WB = 7,
+ kCodecPCM_MULAW = 8,
+ kCodecGSM_MS = 9,
+ kCodecPCM_S16BE = 10,
+ kCodecPCM_S24BE = 11,
+ kCodecOpus = 12,
+ kCodecEAC3 = 13,
+ kCodecPCM_ALAW = 14,
+ kCodecALAC = 15,
+ kCodecAC3 = 16,
+ // DO NOT ADD RANDOM AUDIO CODECS!
+ //
+ // The only acceptable time to add a new codec is if there is production code
+ // that uses said codec in the same CL.
+
+ // Must always be equal to the largest entry ever logged.
+ kAudioCodecMax = kCodecAC3,
+};
+
+std::string MEDIA_EXPORT GetCodecName(AudioCodec codec);
+
+} // namespace media
+
+#endif // MEDIA_BASE_AUDIO_CODECS_H_
diff --git a/chromium/media/base/audio_decoder.cc b/chromium/media/base/audio_decoder.cc
index 5212794d983..6e9245880ea 100644
--- a/chromium/media/base/audio_decoder.cc
+++ b/chromium/media/base/audio_decoder.cc
@@ -12,4 +12,8 @@ AudioDecoder::AudioDecoder() {}
AudioDecoder::~AudioDecoder() {}
+bool AudioDecoder::NeedsBitstreamConversion() const {
+ return false;
+}
+
} // namespace media
diff --git a/chromium/media/base/audio_decoder.h b/chromium/media/base/audio_decoder.h
index 681fac3523f..a68c824eaa7 100644
--- a/chromium/media/base/audio_decoder.h
+++ b/chromium/media/base/audio_decoder.h
@@ -11,8 +11,8 @@
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "media/base/audio_decoder_config.h"
-#include "media/base/cdm_context.h"
#include "media/base/channel_layout.h"
+#include "media/base/decode_status.h"
#include "media/base/decoder_buffer.h"
#include "media/base/media_export.h"
#include "media/base/pipeline_status.h"
@@ -20,19 +20,11 @@
namespace media {
class AudioBuffer;
+class CdmContext;
class DemuxerStream;
class MEDIA_EXPORT AudioDecoder {
public:
- // Status codes for decode operations.
- // TODO(rileya): Now that both AudioDecoder and VideoDecoder Status enums
- // match, break them into a decoder_status.h.
- enum Status {
- kOk, // We're all good.
- kAborted, // We aborted as a result of Reset() or destruction.
- kDecodeError // A decoding error occurred.
- };
-
// Callback for VideoDecoder initialization.
typedef base::Callback<void(bool success)> InitCB;
@@ -40,10 +32,9 @@ class MEDIA_EXPORT AudioDecoder {
// available. Only non-EOS frames should be returned via this callback.
typedef base::Callback<void(const scoped_refptr<AudioBuffer>&)> OutputCB;
- // Callback for Decode(). Called after the decoder has completed decoding
- // corresponding DecoderBuffer, indicating that it's ready to accept another
- // buffer to decode.
- typedef base::Callback<void(Status)> DecodeCB;
+ // Callback for Decode(). Called after the decoder has accepted corresponding
+ // DecoderBuffer, indicating that the pipeline can send next buffer to decode.
+ typedef base::Callback<void(DecodeStatus)> DecodeCB;
AudioDecoder();
@@ -56,17 +47,15 @@ class MEDIA_EXPORT AudioDecoder {
// Returns the name of the decoder for logging purpose.
virtual std::string GetDisplayName() const = 0;
- // Initializes an AudioDecoder with the given DemuxerStream, executing the
- // callback upon completion.
- //
- // |set_cdm_ready_cb| can be used to set/cancel a CdmReadyCB with which the
- // decoder can be notified when a CDM is ready. The decoder can use the CDM to
- // handle encrypted video stream.
+ // Initializes an AudioDecoder with |config|, executing the |init_cb| upon
+ // completion.
//
- // |init_cb| is used to return initialization status.
- // |output_cb| is called for decoded audio buffers (see Decode()).
+ // |cdm_context| can be used to handle encrypted buffers. May be null if the
+ // stream is not encrypted.
+ // |init_cb| is used to return initialization status.
+ // |output_cb| is called for decoded audio buffers (see Decode()).
virtual void Initialize(const AudioDecoderConfig& config,
- const SetCdmReadyCB& set_cdm_ready_cb,
+ CdmContext* cdm_context,
const InitCB& init_cb,
const OutputCB& output_cb) = 0;
@@ -88,6 +77,9 @@ class MEDIA_EXPORT AudioDecoder {
// aborted before |closure| is called.
virtual void Reset(const base::Closure& closure) = 0;
+ // Returns true if the decoder needs bitstream conversion before decoding.
+ virtual bool NeedsBitstreamConversion() const;
+
private:
DISALLOW_COPY_AND_ASSIGN(AudioDecoder);
};
diff --git a/chromium/media/base/audio_decoder_config.cc b/chromium/media/base/audio_decoder_config.cc
index 9d3f67209ba..253c82b147c 100644
--- a/chromium/media/base/audio_decoder_config.cc
+++ b/chromium/media/base/audio_decoder_config.cc
@@ -9,46 +9,6 @@
namespace media {
-// These names come from src/third_party/ffmpeg/libavcodec/codec_desc.c
-std::string GetCodecName(AudioCodec codec) {
- switch (codec) {
- case kUnknownAudioCodec:
- return "unknown";
- case kCodecAAC:
- return "aac";
- case kCodecMP3:
- return "mp3";
- case kCodecPCM:
- case kCodecPCM_S16BE:
- case kCodecPCM_S24BE:
- return "pcm";
- case kCodecVorbis:
- return "vorbis";
- case kCodecFLAC:
- return "flac";
- case kCodecAMR_NB:
- return "amr_nb";
- case kCodecAMR_WB:
- return "amr_wb";
- case kCodecPCM_MULAW:
- return "pcm_mulaw";
- case kCodecGSM_MS:
- return "gsm_ms";
- case kCodecOpus:
- return "opus";
- case kCodecPCM_ALAW:
- return "pcm_alaw";
- case kCodecEAC3:
- return "eac3";
- case kCodecALAC:
- return "alac";
- case kCodecAC3:
- return "ac3";
- }
- NOTREACHED();
- return "";
-}
-
AudioDecoderConfig::AudioDecoderConfig()
: codec_(kUnknownAudioCodec),
sample_format_(kUnknownSampleFormat),
@@ -56,26 +16,28 @@ AudioDecoderConfig::AudioDecoderConfig()
channel_layout_(CHANNEL_LAYOUT_UNSUPPORTED),
samples_per_second_(0),
bytes_per_frame_(0),
- is_encrypted_(false),
- codec_delay_(0) {
-}
+ codec_delay_(0) {}
-AudioDecoderConfig::AudioDecoderConfig(AudioCodec codec,
- SampleFormat sample_format,
- ChannelLayout channel_layout,
- int samples_per_second,
- const std::vector<uint8_t>& extra_data,
- bool is_encrypted) {
+AudioDecoderConfig::AudioDecoderConfig(
+ AudioCodec codec,
+ SampleFormat sample_format,
+ ChannelLayout channel_layout,
+ int samples_per_second,
+ const std::vector<uint8_t>& extra_data,
+ const EncryptionScheme& encryption_scheme) {
Initialize(codec, sample_format, channel_layout, samples_per_second,
- extra_data, is_encrypted, base::TimeDelta(), 0);
+ extra_data, encryption_scheme, base::TimeDelta(), 0);
}
+AudioDecoderConfig::AudioDecoderConfig(const AudioDecoderConfig& other) =
+ default;
+
void AudioDecoderConfig::Initialize(AudioCodec codec,
SampleFormat sample_format,
ChannelLayout channel_layout,
int samples_per_second,
const std::vector<uint8_t>& extra_data,
- bool is_encrypted,
+ const EncryptionScheme& encryption_scheme,
base::TimeDelta seek_preroll,
int codec_delay) {
codec_ = codec;
@@ -84,7 +46,7 @@ void AudioDecoderConfig::Initialize(AudioCodec codec,
sample_format_ = sample_format;
bytes_per_channel_ = SampleFormatToBytesPerChannel(sample_format);
extra_data_ = extra_data;
- is_encrypted_ = is_encrypted;
+ encryption_scheme_ = encryption_scheme;
seek_preroll_ = seek_preroll;
codec_delay_ = codec_delay;
@@ -112,7 +74,7 @@ bool AudioDecoderConfig::Matches(const AudioDecoderConfig& config) const {
(channel_layout() == config.channel_layout()) &&
(samples_per_second() == config.samples_per_second()) &&
(extra_data() == config.extra_data()) &&
- (is_encrypted() == config.is_encrypted()) &&
+ (encryption_scheme().Matches(config.encryption_scheme())) &&
(sample_format() == config.sample_format()) &&
(seek_preroll() == config.seek_preroll()) &&
(codec_delay() == config.codec_delay()));
diff --git a/chromium/media/base/audio_decoder_config.h b/chromium/media/base/audio_decoder_config.h
index a0e262f005d..266de82d605 100644
--- a/chromium/media/base/audio_decoder_config.h
+++ b/chromium/media/base/audio_decoder_config.h
@@ -12,45 +12,14 @@
#include "base/macros.h"
#include "base/time/time.h"
+#include "media/base/audio_codecs.h"
#include "media/base/channel_layout.h"
+#include "media/base/encryption_scheme.h"
#include "media/base/media_export.h"
#include "media/base/sample_format.h"
namespace media {
-enum AudioCodec {
- // These values are histogrammed over time; do not change their ordinal
- // values. When deleting a codec replace it with a dummy value; when adding a
- // codec, do so at the bottom before kAudioCodecMax, and update the value of
- // kAudioCodecMax to equal the new codec.
- kUnknownAudioCodec = 0,
- kCodecAAC = 1,
- kCodecMP3 = 2,
- kCodecPCM = 3,
- kCodecVorbis = 4,
- kCodecFLAC = 5,
- kCodecAMR_NB = 6,
- kCodecAMR_WB = 7,
- kCodecPCM_MULAW = 8,
- kCodecGSM_MS = 9,
- kCodecPCM_S16BE = 10,
- kCodecPCM_S24BE = 11,
- kCodecOpus = 12,
- kCodecEAC3 = 13,
- kCodecPCM_ALAW = 14,
- kCodecALAC = 15,
- kCodecAC3 = 16,
- // DO NOT ADD RANDOM AUDIO CODECS!
- //
- // The only acceptable time to add a new codec is if there is production code
- // that uses said codec in the same CL.
-
- // Must always be equal to the largest entry ever logged.
- kAudioCodecMax = kCodecAC3,
-};
-
-std::string MEDIA_EXPORT GetCodecName(AudioCodec codec);
-
// TODO(dalecurtis): FFmpeg API uses |bytes_per_channel| instead of
// |bits_per_channel|, we should switch over since bits are generally confusing
// to work with.
@@ -66,7 +35,9 @@ class MEDIA_EXPORT AudioDecoderConfig {
ChannelLayout channel_layout,
int samples_per_second,
const std::vector<uint8_t>& extra_data,
- bool is_encrypted);
+ const EncryptionScheme& encryption_scheme);
+
+ AudioDecoderConfig(const AudioDecoderConfig& other);
~AudioDecoderConfig();
@@ -76,7 +47,7 @@ class MEDIA_EXPORT AudioDecoderConfig {
ChannelLayout channel_layout,
int samples_per_second,
const std::vector<uint8_t>& extra_data,
- bool is_encrypted,
+ const EncryptionScheme& encryption_scheme,
base::TimeDelta seek_preroll,
int codec_delay);
@@ -109,7 +80,12 @@ class MEDIA_EXPORT AudioDecoderConfig {
// Whether the audio stream is potentially encrypted.
// Note that in a potentially encrypted audio stream, individual buffers
// can be encrypted or not encrypted.
- bool is_encrypted() const { return is_encrypted_; }
+ bool is_encrypted() const { return encryption_scheme_.is_encrypted(); }
+
+ // Encryption scheme used for encrypted buffers.
+ const EncryptionScheme& encryption_scheme() const {
+ return encryption_scheme_;
+ }
private:
AudioCodec codec_;
@@ -119,7 +95,7 @@ class MEDIA_EXPORT AudioDecoderConfig {
int samples_per_second_;
int bytes_per_frame_;
std::vector<uint8_t> extra_data_;
- bool is_encrypted_;
+ EncryptionScheme encryption_scheme_;
// |seek_preroll_| is the duration of the data that the decoder must decode
// before the decoded data is valid.
diff --git a/chromium/media/base/audio_hardware_config.cc b/chromium/media/base/audio_hardware_config.cc
index c4b56157957..f3f151385cd 100644
--- a/chromium/media/base/audio_hardware_config.cc
+++ b/chromium/media/base/audio_hardware_config.cc
@@ -100,17 +100,18 @@ void AudioHardwareConfig::UpdateOutputConfig(
}
// static
-int AudioHardwareConfig::GetHighLatencyBufferSize(
- const media::AudioParameters& output_params) {
+int AudioHardwareConfig::GetHighLatencyBufferSize(int sample_rate,
+ int buffer_size) {
// Empirically, we consider 20ms of samples to be high latency.
- const double twenty_ms_size = 2.0 * output_params.sample_rate() / 100;
+ const double twenty_ms_size = 2.0 * sample_rate / 100;
#if defined(OS_WIN)
+ buffer_size = std::max(buffer_size, 1);
+
// Windows doesn't use power of two buffer sizes, so we should always round up
// to the nearest multiple of the output buffer size.
const int high_latency_buffer_size =
- std::ceil(twenty_ms_size / output_params.frames_per_buffer()) *
- output_params.frames_per_buffer();
+ std::ceil(twenty_ms_size / buffer_size) * buffer_size;
#else
// On other platforms use the nearest higher power of two buffer size. For a
// given sample rate, this works out to:
@@ -128,12 +129,13 @@ int AudioHardwareConfig::GetHighLatencyBufferSize(
const int high_latency_buffer_size = RoundUpToPowerOfTwo(twenty_ms_size);
#endif // defined(OS_WIN)
- return std::max(output_params.frames_per_buffer(), high_latency_buffer_size);
+ return std::max(buffer_size, high_latency_buffer_size);
}
int AudioHardwareConfig::GetHighLatencyBufferSize() const {
AutoLock auto_lock(config_lock_);
- return GetHighLatencyBufferSize(output_params_);
+ return GetHighLatencyBufferSize(output_params_.sample_rate(),
+ output_params_.frames_per_buffer());
}
} // namespace media
diff --git a/chromium/media/base/audio_hardware_config.h b/chromium/media/base/audio_hardware_config.h
index f7d26789989..124695d09c1 100644
--- a/chromium/media/base/audio_hardware_config.h
+++ b/chromium/media/base/audio_hardware_config.h
@@ -45,8 +45,9 @@ class MEDIA_EXPORT AudioHardwareConfig {
// For clients which don't need low latency, a larger buffer size should be
// used to save power and CPU resources.
int GetHighLatencyBufferSize() const;
- static int GetHighLatencyBufferSize(
- const media::AudioParameters& output_params);
+
+ // |buffer_size| should be set to 0 if a client has no preference.
+ static int GetHighLatencyBufferSize(int sample_rate, int buffer_size);
private:
// Cached values; access is protected by |config_lock_|.
diff --git a/chromium/media/base/audio_hash.cc b/chromium/media/base/audio_hash.cc
index d879e545362..7f133dd78ba 100644
--- a/chromium/media/base/audio_hash.cc
+++ b/chromium/media/base/audio_hash.cc
@@ -5,6 +5,7 @@
// MSVC++ requires this to be set before any other includes to get M_PI.
#define _USE_MATH_DEFINES
#include <cmath>
+#include <sstream>
#include "media/base/audio_hash.h"
@@ -52,4 +53,17 @@ std::string AudioHash::ToString() const {
return result;
}
+bool AudioHash::IsEquivalent(const std::string& other, double tolerance) const {
+ float other_hash;
+ char comma;
+
+ std::stringstream is(other);
+ for (size_t i = 0; i < arraysize(audio_hash_); ++i) {
+ is >> other_hash >> comma;
+ if (fabs(audio_hash_[i] - other_hash) > tolerance)
+ return false;
+ }
+ return true;
+}
+
} // namespace media
diff --git a/chromium/media/base/audio_hash.h b/chromium/media/base/audio_hash.h
index 83ee901d3c9..5ac1aa267b7 100644
--- a/chromium/media/base/audio_hash.h
+++ b/chromium/media/base/audio_hash.h
@@ -43,6 +43,11 @@ class MEDIA_EXPORT AudioHash {
// Return a string representation of the current hash.
std::string ToString() const;
+ // Compare with another hash value given as string representation.
+ // Returns true if for every bucket the difference between this and
+ // other is less than tolerance.
+ bool IsEquivalent(const std::string& other, double tolerance) const;
+
private:
// Storage for the audio hash. The number of buckets controls the importance
// of position in the hash. A higher number reduces the chance of false
diff --git a/chromium/media/base/audio_pull_fifo.cc b/chromium/media/base/audio_pull_fifo.cc
index cf25142d904..03aa001a38a 100644
--- a/chromium/media/base/audio_pull_fifo.cc
+++ b/chromium/media/base/audio_pull_fifo.cc
@@ -46,6 +46,10 @@ void AudioPullFifo::Consume(AudioBus* destination, int frames_to_consume) {
void AudioPullFifo::Clear() { fifo_index_ = fifo_->frames(); }
+int AudioPullFifo::SizeInFrames() const {
+ return fifo_->frames();
+}
+
int AudioPullFifo::ReadFromFifo(AudioBus* destination,
int frames_to_provide,
int write_pos) {
diff --git a/chromium/media/base/audio_pull_fifo.h b/chromium/media/base/audio_pull_fifo.h
index a839c8d6c8e..61e8332a677 100644
--- a/chromium/media/base/audio_pull_fifo.h
+++ b/chromium/media/base/audio_pull_fifo.h
@@ -7,6 +7,7 @@
#include "base/callback.h"
#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
#include "media/base/media_export.h"
namespace media {
@@ -41,6 +42,9 @@ class MEDIA_EXPORT AudioPullFifo {
// Empties the FIFO without deallocating any memory.
void Clear();
+ // Returns the size of the fifo in number of frames.
+ int SizeInFrames() const;
+
private:
// Attempt to fulfill the request using what is available in the FIFO.
// Append new data to the |destination| starting at |write_pos|.
diff --git a/chromium/media/base/audio_pull_fifo_unittest.cc b/chromium/media/base/audio_pull_fifo_unittest.cc
index e6a7362ce2d..4f1d98ed5fa 100644
--- a/chromium/media/base/audio_pull_fifo_unittest.cc
+++ b/chromium/media/base/audio_pull_fifo_unittest.cc
@@ -28,11 +28,15 @@ class AudioPullFifoTest
: public testing::TestWithParam<int> {
public:
AudioPullFifoTest()
- : pull_fifo_(kChannels, kMaxFramesInFifo, base::Bind(
- &AudioPullFifoTest::ProvideInput, base::Unretained(this))),
- audio_bus_(AudioBus::Create(kChannels, kMaxFramesInFifo)),
- fill_value_(0),
- last_frame_delay_(-1) {}
+ : pull_fifo_(kChannels,
+ kMaxFramesInFifo,
+ base::Bind(&AudioPullFifoTest::ProvideInput,
+ base::Unretained(this))),
+ audio_bus_(AudioBus::Create(kChannels, kMaxFramesInFifo)),
+ fill_value_(0),
+ last_frame_delay_(-1) {
+ EXPECT_EQ(kMaxFramesInFifo, pull_fifo_.SizeInFrames());
+ }
virtual ~AudioPullFifoTest() {}
void VerifyValue(const float data[], int size, float start_value) {
diff --git a/chromium/media/base/audio_push_fifo.cc b/chromium/media/base/audio_push_fifo.cc
new file mode 100644
index 00000000000..5e759670b6b
--- /dev/null
+++ b/chromium/media/base/audio_push_fifo.cc
@@ -0,0 +1,86 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/base/audio_push_fifo.h"
+
+#include <algorithm>
+
+#include "base/logging.h"
+
+namespace media {
+
+AudioPushFifo::AudioPushFifo(const OutputCallback& callback)
+ : callback_(callback), frames_per_buffer_(0) {
+ DCHECK(!callback_.is_null());
+}
+
+AudioPushFifo::~AudioPushFifo() {}
+
+void AudioPushFifo::Reset(int frames_per_buffer) {
+ DCHECK_GT(frames_per_buffer, 0);
+
+ audio_queue_.reset();
+ queued_frames_ = 0;
+
+ frames_per_buffer_ = frames_per_buffer;
+}
+
+void AudioPushFifo::Push(const AudioBus& input_bus) {
+ DCHECK_GT(frames_per_buffer_, 0);
+
+ // Fast path: No buffering required.
+ if ((queued_frames_ == 0) && (input_bus.frames() == frames_per_buffer_)) {
+ callback_.Run(input_bus, 0);
+ return;
+ }
+
+ // Lazy-create the |audio_queue_| if needed.
+ if (!audio_queue_ || audio_queue_->channels() != input_bus.channels())
+ audio_queue_ = AudioBus::Create(input_bus.channels(), frames_per_buffer_);
+
+ // Start with a frame offset that refers to the position of the first sample
+ // in |audio_queue_| relative to the first sample in |input_bus|.
+ int frame_delay = -queued_frames_;
+
+ // Repeatedly fill up |audio_queue_| with more sample frames from |input_bus|
+ // and deliver batches until all sample frames in |input_bus| have been
+ // consumed.
+ int input_offset = 0;
+ do {
+ // Attempt to fill |audio_queue_| completely.
+ const int frames_to_enqueue =
+ std::min(static_cast<int>(input_bus.frames() - input_offset),
+ frames_per_buffer_ - queued_frames_);
+ if (frames_to_enqueue > 0) {
+ DVLOG(2) << "Enqueuing " << frames_to_enqueue << " frames.";
+ input_bus.CopyPartialFramesTo(input_offset, frames_to_enqueue,
+ queued_frames_, audio_queue_.get());
+ queued_frames_ += frames_to_enqueue;
+ input_offset += frames_to_enqueue;
+ }
+
+ // If |audio_queue_| has been filled completely, deliver the re-buffered
+ // audio to the consumer.
+ if (queued_frames_ == frames_per_buffer_) {
+ DVLOG(2) << "Delivering another " << queued_frames_ << " frames.";
+ callback_.Run(*audio_queue_, frame_delay);
+ frame_delay += frames_per_buffer_;
+ queued_frames_ = 0;
+ } else {
+ // Not enough frames queued-up yet to deliver more frames.
+ }
+ } while (input_offset < input_bus.frames());
+}
+
+void AudioPushFifo::Flush() {
+ if (queued_frames_ == 0)
+ return;
+
+ audio_queue_->ZeroFramesPartial(queued_frames_,
+ audio_queue_->frames() - queued_frames_);
+ callback_.Run(*audio_queue_, -queued_frames_);
+ queued_frames_ = 0;
+}
+
+} // namespace media
diff --git a/chromium/media/base/audio_push_fifo.h b/chromium/media/base/audio_push_fifo.h
new file mode 100644
index 00000000000..8512cd17932
--- /dev/null
+++ b/chromium/media/base/audio_push_fifo.h
@@ -0,0 +1,73 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_BASE_AUDIO_PUSH_FIFO_H_
+#define MEDIA_BASE_AUDIO_PUSH_FIFO_H_
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "media/base/audio_bus.h"
+#include "media/base/media_export.h"
+
+namespace media {
+
+// Yet another FIFO for audio data that re-buffers audio to a desired buffer
+// size. Unlike AudioFifo and AudioBlockFifo, this FIFO cannot overflow: The
+// client is required to provide a callback that is called synchronously during
+// a push whenever enough data becomes available. This implementation
+// eliminates redundant memory copies when the input buffer size always matches
+// the desired buffer size.
+class MEDIA_EXPORT AudioPushFifo final {
+ public:
+ // Called synchronously zero, one, or multiple times during a call to Push()
+ // to deliver re-buffered audio. |frame_delay| refers to the position of the
+ // first frame in |output| relative to the first frame in the Push() call. If
+ // negative, this indicates the output contains some data from a prior call to
+ // Push(). If zero or positive, the output contains data from the current
+ // call to Push(). Clients can use this to adjust timestamps.
+ using OutputCallback =
+ base::Callback<void(const AudioBus& output_bus, int frame_delay)>;
+
+ // Creates a new AudioPushFifo which delivers re-buffered audio by running
+ // |callback|.
+ explicit AudioPushFifo(const OutputCallback& callback);
+
+ ~AudioPushFifo();
+
+ // Returns the number of frames in each AudioBus delivered to the
+ // OutputCallback.
+ int frames_per_buffer() const { return frames_per_buffer_; }
+
+ // Must be called at least once before the first call to Push(). May be
+ // called later (e.g., to support an audio format change).
+ void Reset(int frames_per_buffer);
+
+ // Pushes all audio channel data from |input_bus| through the FIFO. This will
+ // result in zero, one, or multiple synchronous calls to the OutputCallback
+ // provided in the constructor. If the |input_bus| has a different number of
+ // channels than the prior Push() call, any currently-queued frames will be
+ // dropped.
+ void Push(const AudioBus& input_bus);
+
+ // Flushes any enqueued frames by invoking the OutputCallback with those
+ // frames plus padded zero samples. If there are no frames currently
+ // enqueued, OutputCallback is not run.
+ void Flush();
+
+ private:
+ const OutputCallback callback_;
+
+ int frames_per_buffer_;
+
+ // Queue of frames pending for delivery.
+ scoped_ptr<AudioBus> audio_queue_;
+ int queued_frames_;
+
+ DISALLOW_COPY_AND_ASSIGN(AudioPushFifo);
+};
+
+} // namespace media
+
+#endif // MEDIA_BASE_AUDIO_PUSH_FIFO_H_
diff --git a/chromium/media/base/audio_push_fifo_unittest.cc b/chromium/media/base/audio_push_fifo_unittest.cc
new file mode 100644
index 00000000000..8d815fd613f
--- /dev/null
+++ b/chromium/media/base/audio_push_fifo_unittest.cc
@@ -0,0 +1,256 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <limits>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/macros.h"
+#include "media/base/audio_bus.h"
+#include "media/base/audio_push_fifo.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace media {
+
+namespace {
+
+class AudioPushFifoTest : public testing::TestWithParam<int> {
+ public:
+ AudioPushFifoTest() {}
+ ~AudioPushFifoTest() override {}
+
+ int output_chunk_size() const { return GetParam(); }
+
+ void SetUp() final {
+ fifo_.reset(new AudioPushFifo(base::Bind(
+ &AudioPushFifoTest::ReceiveAndCheckNextChunk, base::Unretained(this))));
+ fifo_->Reset(output_chunk_size());
+ ASSERT_EQ(output_chunk_size(), fifo_->frames_per_buffer());
+ }
+
+ protected:
+ struct OutputChunkResult {
+ int num_frames;
+ float first_sample_value;
+ float last_sample_value;
+ int frame_delay;
+ };
+
+ // Returns the number of output chunks that should have been emitted given the
+ // number of input frames pushed so far.
+ size_t GetExpectedOutputChunks(int frames_pushed) const {
+ return static_cast<size_t>(frames_pushed / output_chunk_size());
+ }
+
+ // Returns the number of Push() calls to make in order to get at least 3
+ // output chunks.
+ int GetNumPushTestIterations(int input_chunk_size) const {
+ return 3 * std::max(1, output_chunk_size() / input_chunk_size);
+ }
+
+ // Repeatedly pushes constant-sized batches of input samples and checks that
+ // the input data is re-chunked correctly.
+ void RunSimpleRechunkTest(int input_chunk_size) {
+ const int num_iterations = GetNumPushTestIterations(input_chunk_size);
+
+ int sample_value = 0;
+ const scoped_ptr<AudioBus> audio_bus =
+ AudioBus::Create(1, input_chunk_size);
+
+ for (int i = 0; i < num_iterations; ++i) {
+ EXPECT_EQ(GetExpectedOutputChunks(i * input_chunk_size), results_.size());
+
+ // Fill audio data with predictable values.
+ for (int j = 0; j < audio_bus->frames(); ++j)
+ audio_bus->channel(0)[j] = static_cast<float>(sample_value++);
+
+ fifo_->Push(*audio_bus);
+ // Note: AudioPushFifo has just called ReceiveAndCheckNextChunk() zero or
+ // more times.
+ }
+ EXPECT_EQ(GetExpectedOutputChunks(num_iterations * input_chunk_size),
+ results_.size());
+
+ // Confirm first and last sample values that have been output are the
+ // expected ones.
+ ASSERT_FALSE(results_.empty());
+ EXPECT_EQ(0.0f, results_.front().first_sample_value);
+ const float last_value_in_last_chunk = static_cast<float>(
+ GetExpectedOutputChunks(num_iterations * input_chunk_size) *
+ output_chunk_size() -
+ 1);
+ EXPECT_EQ(last_value_in_last_chunk, results_.back().last_sample_value);
+
+ // Confirm the expected frame delays for the first output chunk (or two).
+ if (input_chunk_size < output_chunk_size()) {
+ const int num_queued_before_first_output =
+ ((output_chunk_size() - 1) / input_chunk_size) * input_chunk_size;
+ EXPECT_EQ(-num_queued_before_first_output, results_.front().frame_delay);
+ } else if (input_chunk_size >= output_chunk_size()) {
+ EXPECT_EQ(0, results_[0].frame_delay);
+ if (input_chunk_size >= 2 * output_chunk_size()) {
+ EXPECT_EQ(output_chunk_size(), results_[1].frame_delay);
+ } else {
+ const int num_remaining_after_first_output =
+ input_chunk_size - output_chunk_size();
+ EXPECT_EQ(-num_remaining_after_first_output, results_[1].frame_delay);
+ }
+ }
+
+ const size_t num_results_before_flush = results_.size();
+ fifo_->Flush();
+ const size_t num_results_after_flush = results_.size();
+ if (num_results_after_flush > num_results_before_flush) {
+ EXPECT_NE(0, results_.back().frame_delay);
+ EXPECT_LT(-output_chunk_size(), results_.back().frame_delay);
+ }
+ }
+
+ // Returns a "random" integer in the range [begin,end).
+ int GetRandomInRange(int begin, int end) {
+ const int len = end - begin;
+ const int rand_offset = (len == 0) ? 0 : (NextRandomInt() % (end - begin));
+ return begin + rand_offset;
+ }
+
+ scoped_ptr<AudioPushFifo> fifo_;
+ std::vector<OutputChunkResult> results_;
+
+ private:
+ // Called by |fifo_| to deliver another chunk of audio. Sanity checks
+ // the sample values are as expected, and without any dropped/duplicated, and
+ // adds a result to |results_|.
+ void ReceiveAndCheckNextChunk(const AudioBus& audio_bus, int frame_delay) {
+ OutputChunkResult result;
+ result.num_frames = audio_bus.frames();
+ result.first_sample_value = audio_bus.channel(0)[0];
+ result.last_sample_value = audio_bus.channel(0)[audio_bus.frames() - 1];
+ result.frame_delay = frame_delay;
+
+ // Check that each sample value is the previous sample value plus one.
+ for (int i = 1; i < audio_bus.frames(); ++i) {
+ const float expected_value = result.first_sample_value + i;
+ const float actual_value = audio_bus.channel(0)[i];
+ if (actual_value != expected_value) {
+ if (actual_value == 0.0f) {
+ // This chunk is probably being emitted by a Flush(). If that's true
+ // then the frame_delay will be negative and the rest of the
+ // |audio_bus| should be all zeroes.
+ ASSERT_GT(0, frame_delay);
+ for (int j = i + 1; j < audio_bus.frames(); ++j)
+ ASSERT_EQ(0.0f, audio_bus.channel(0)[j]);
+ break;
+ } else {
+ ASSERT_EQ(expected_value, actual_value) << "Sample at offset " << i
+ << " is incorrect.";
+ }
+ }
+ }
+
+ results_.push_back(result);
+ }
+
+ // Note: Not using base::RandInt() because it is horribly slow on debug
+ // builds. The following is a very simple, deterministic LCG:
+ int NextRandomInt() {
+ rand_seed_ = (1103515245 * rand_seed_ + 12345) % (1 << 31);
+ return static_cast<int>(rand_seed_);
+ }
+
+ uint32_t rand_seed_ = 0x7e110;
+
+ DISALLOW_COPY_AND_ASSIGN(AudioPushFifoTest);
+};
+
+// Tests an atypical edge case: Push()ing one frame at a time.
+TEST_P(AudioPushFifoTest, PushOneFrameAtATime) {
+ RunSimpleRechunkTest(1);
+}
+
+// Tests that re-chunking the audio from common platform input chunk sizes
+// works.
+TEST_P(AudioPushFifoTest, Push128FramesAtATime) {
+ RunSimpleRechunkTest(128);
+}
+TEST_P(AudioPushFifoTest, Push512FramesAtATime) {
+ RunSimpleRechunkTest(512);
+}
+
+// Tests that re-chunking the audio from common "10 ms" input chunk sizes
+// works (44100 Hz * 10 ms = 441, and 48000 Hz * 10 ms = 480).
+TEST_P(AudioPushFifoTest, Push441FramesAtATime) {
+ RunSimpleRechunkTest(441);
+}
+TEST_P(AudioPushFifoTest, Push480FramesAtATime) {
+ RunSimpleRechunkTest(480);
+}
+
+// Tests that re-chunking when input audio is provided in varying chunk sizes
+// works.
+TEST_P(AudioPushFifoTest, PushArbitraryNumbersOfFramesAtATime) {
+ // The loop below will run until both: 1) kMinNumIterations loops have
+ // occurred; and 2) there are at least 3 entries in |results_|.
+ const int kMinNumIterations = 30;
+
+ int sample_value = 0;
+ int frames_pushed_so_far = 0;
+ for (int i = 0; i < kMinNumIterations || results_.size() < 3; ++i) {
+ EXPECT_EQ(GetExpectedOutputChunks(frames_pushed_so_far), results_.size());
+
+ // Create an AudioBus of a random length, populated with sample values.
+ const int input_chunk_size = GetRandomInRange(1, 1920);
+ const scoped_ptr<AudioBus> audio_bus =
+ AudioBus::Create(1, input_chunk_size);
+ for (int j = 0; j < audio_bus->frames(); ++j)
+ audio_bus->channel(0)[j] = static_cast<float>(sample_value++);
+
+ fifo_->Push(*audio_bus);
+ // Note: AudioPushFifo has just called ReceiveAndCheckNextChunk() zero or
+ // more times.
+
+ frames_pushed_so_far += input_chunk_size;
+ }
+ EXPECT_EQ(GetExpectedOutputChunks(frames_pushed_so_far), results_.size());
+
+ ASSERT_FALSE(results_.empty());
+ EXPECT_EQ(0.0f, results_.front().first_sample_value);
+ const float last_value_in_last_chunk = static_cast<float>(
+ GetExpectedOutputChunks(frames_pushed_so_far) * output_chunk_size() - 1);
+ EXPECT_EQ(last_value_in_last_chunk, results_.back().last_sample_value);
+
+ const size_t num_results_before_flush = results_.size();
+ fifo_->Flush();
+ const size_t num_results_after_flush = results_.size();
+ if (num_results_after_flush > num_results_before_flush) {
+ EXPECT_NE(0, results_.back().frame_delay);
+ EXPECT_LT(-output_chunk_size(), results_.back().frame_delay);
+ }
+}
+
+INSTANTIATE_TEST_CASE_P(,
+ AudioPushFifoTest,
+ ::testing::Values(
+ // 1 ms output chunks at common sample rates.
+ 16, // 16000 Hz
+ 22, // 22050 Hz
+ 44, // 44100 Hz
+ 48, // 48000 Hz
+
+ // 10 ms output chunks at common sample rates.
+ 160, // 16000 Hz
+ 220, // 22050 Hz
+ 441, // 44100 Hz
+ 480, // 48000 Hz
+
+ // 60 ms output chunks at common sample rates.
+ 960, // 16000 Hz
+ 1323, // 22050 Hz
+ 2646, // 44100 Hz
+ 2880 // 48000 Hz
+ ));
+
+} // namespace
+
+} // namespace media
diff --git a/chromium/media/base/audio_renderer.h b/chromium/media/base/audio_renderer.h
index 79438c785bf..11e112f1a1e 100644
--- a/chromium/media/base/audio_renderer.h
+++ b/chromium/media/base/audio_renderer.h
@@ -9,12 +9,12 @@
#include "base/macros.h"
#include "base/time/time.h"
#include "media/base/buffering_state.h"
-#include "media/base/cdm_context.h"
#include "media/base/media_export.h"
#include "media/base/pipeline_status.h"
namespace media {
+class CdmContext;
class DemuxerStream;
class TimeSource;
@@ -29,8 +29,8 @@ class MEDIA_EXPORT AudioRenderer {
// completion. If initialization fails, only |init_cb| (not |error_cb|) will
// be called.
//
- // |set_cdm_ready_cb| is fired when a CDM is needed, i.e. when the |stream| is
- // encrypted.
+ // |cdm_context| can be used to handle encrypted streams. May be null if the
+ // stream is not encrypted.
//
// |statistics_cb| is executed periodically with audio rendering stats.
//
@@ -46,7 +46,7 @@ class MEDIA_EXPORT AudioRenderer {
virtual void Initialize(
DemuxerStream* stream,
const PipelineStatusCB& init_cb,
- const SetCdmReadyCB& set_cdm_ready_cb,
+ CdmContext* cdm_context,
const StatisticsCB& statistics_cb,
const BufferingStateCB& buffering_state_cb,
const base::Closure& ended_cb,
diff --git a/chromium/media/base/audio_renderer_mixer.cc b/chromium/media/base/audio_renderer_mixer.cc
index 218bb9e2c56..d6cca18c40f 100644
--- a/chromium/media/base/audio_renderer_mixer.cc
+++ b/chromium/media/base/audio_renderer_mixer.cc
@@ -4,6 +4,8 @@
#include "media/base/audio_renderer_mixer.h"
+#include <cmath>
+
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/logging.h"
@@ -110,14 +112,14 @@ void AudioRendererMixer::RemoveErrorCallback(const base::Closure& error_cb) {
NOTREACHED();
}
-OutputDevice* AudioRendererMixer::GetOutputDevice() {
+OutputDeviceInfo AudioRendererMixer::GetOutputDeviceInfo() {
DVLOG(1) << __FUNCTION__;
base::AutoLock auto_lock(lock_);
- return audio_sink_->GetOutputDevice();
+ return audio_sink_->GetOutputDeviceInfo();
}
int AudioRendererMixer::Render(AudioBus* audio_bus,
- uint32_t audio_delay_milliseconds,
+ uint32_t frames_delayed,
uint32_t frames_skipped) {
base::AutoLock auto_lock(lock_);
@@ -132,8 +134,13 @@ int AudioRendererMixer::Render(AudioBus* audio_bus,
playing_ = false;
}
- master_converter_.ConvertWithDelay(
- base::TimeDelta::FromMilliseconds(audio_delay_milliseconds), audio_bus);
+ // TODO(chcunningham): Delete this conversion and change ConvertWith delay to
+ // expect a count of frames delayed instead of TimeDelta (less precise).
+ // See http://crbug.com/587522.
+ base::TimeDelta audio_delay = base::TimeDelta::FromMicroseconds(
+ std::round(frames_delayed * output_params_.GetMicrosecondsPerFrame()));
+
+ master_converter_.ConvertWithDelay(audio_delay, audio_bus);
return audio_bus->frames();
}
diff --git a/chromium/media/base/audio_renderer_mixer.h b/chromium/media/base/audio_renderer_mixer.h
index dae8b7cd5e5..9c712105d98 100644
--- a/chromium/media/base/audio_renderer_mixer.h
+++ b/chromium/media/base/audio_renderer_mixer.h
@@ -46,11 +46,7 @@ class MEDIA_EXPORT AudioRendererMixer
pause_delay_ = delay;
}
- // TODO(guidou): remove this method. The output device of a mixer should
- // never be switched, as it may result in a discrepancy between the output
- // parameters of the new device and the output parameters with which the
- // mixer was initialized. See crbug.com/506507
- OutputDevice* GetOutputDevice();
+ OutputDeviceInfo GetOutputDeviceInfo();
private:
// Maps input sample rate to the dedicated converter.
@@ -58,7 +54,7 @@ class MEDIA_EXPORT AudioRendererMixer
// AudioRendererSink::RenderCallback implementation.
int Render(AudioBus* audio_bus,
- uint32_t audio_delay_milliseconds,
+ uint32_t frames_delayed,
uint32_t frames_skipped) override;
void OnRenderError() override;
diff --git a/chromium/media/base/audio_renderer_mixer_input.cc b/chromium/media/base/audio_renderer_mixer_input.cc
index d0d38d3b8ee..55dbfc8404f 100644
--- a/chromium/media/base/audio_renderer_mixer_input.cc
+++ b/chromium/media/base/audio_renderer_mixer_input.cc
@@ -4,6 +4,8 @@
#include "media/base/audio_renderer_mixer_input.h"
+#include <cmath>
+
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "media/base/audio_renderer_mixer.h"
@@ -15,36 +17,40 @@ AudioRendererMixerInput::AudioRendererMixerInput(
const RemoveMixerCB& remove_mixer_cb,
const std::string& device_id,
const url::Origin& security_origin)
- : initialized_(false),
+ : started_(false),
playing_(false),
volume_(1.0f),
get_mixer_cb_(get_mixer_cb),
remove_mixer_cb_(remove_mixer_cb),
device_id_(device_id),
security_origin_(security_origin),
- mixer_(NULL),
- callback_(NULL),
+ mixer_(nullptr),
+ callback_(nullptr),
error_cb_(base::Bind(&AudioRendererMixerInput::OnRenderError,
base::Unretained(this))) {}
AudioRendererMixerInput::~AudioRendererMixerInput() {
+ DCHECK(!started_);
DCHECK(!mixer_);
}
void AudioRendererMixerInput::Initialize(
const AudioParameters& params,
AudioRendererSink::RenderCallback* callback) {
+ DCHECK(!started_);
DCHECK(!mixer_);
DCHECK(callback);
params_ = params;
callback_ = callback;
- initialized_ = true;
}
void AudioRendererMixerInput::Start() {
- DCHECK(initialized_);
+ DCHECK(!started_);
DCHECK(!mixer_);
+ DCHECK(callback_); // Initialized.
+
+ started_ = true;
mixer_ = get_mixer_cb_.Run(params_, device_id_, security_origin_, nullptr);
if (!mixer_) {
callback_->OnRenderError();
@@ -64,10 +70,7 @@ void AudioRendererMixerInput::Start() {
void AudioRendererMixerInput::Stop() {
// Stop() may be called at any time, if Pause() hasn't been called we need to
// remove our mixer input before shutdown.
- if (playing_) {
- mixer_->RemoveMixerInput(params_, this);
- playing_ = false;
- }
+ Pause();
if (mixer_) {
// TODO(dalecurtis): This is required so that |callback_| isn't called after
@@ -75,9 +78,11 @@ void AudioRendererMixerInput::Stop() {
// should instead have sane ownership semantics: http://crbug.com/151051
mixer_->RemoveErrorCallback(error_cb_);
remove_mixer_cb_.Run(params_, device_id_, security_origin_);
- mixer_ = NULL;
+ mixer_ = nullptr;
}
+ started_ = false;
+
if (!pending_switch_callback_.is_null()) {
base::ResetAndReturn(&pending_switch_callback_)
.Run(OUTPUT_DEVICE_STATUS_ERROR_INTERNAL);
@@ -101,18 +106,19 @@ void AudioRendererMixerInput::Pause() {
}
bool AudioRendererMixerInput::SetVolume(double volume) {
+ base::AutoLock auto_lock(volume_lock_);
volume_ = volume;
return true;
}
-OutputDevice* AudioRendererMixerInput::GetOutputDevice() {
- return this;
+OutputDeviceInfo AudioRendererMixerInput::GetOutputDeviceInfo() {
+ return mixer_ ? mixer_->GetOutputDeviceInfo() : OutputDeviceInfo();
}
void AudioRendererMixerInput::SwitchOutputDevice(
const std::string& device_id,
const url::Origin& security_origin,
- const SwitchOutputDeviceCB& callback) {
+ const OutputDeviceStatusCB& callback) {
if (!mixer_) {
if (pending_switch_callback_.is_null()) {
pending_switch_callback_ = callback;
@@ -145,6 +151,7 @@ void AudioRendererMixerInput::SwitchOutputDevice(
security_origin_ = security_origin;
mixer_ = new_mixer;
mixer_->AddErrorCallback(error_cb_);
+ started_ = true;
if (was_playing)
Play();
@@ -152,21 +159,15 @@ void AudioRendererMixerInput::SwitchOutputDevice(
callback.Run(OUTPUT_DEVICE_STATUS_OK);
}
-AudioParameters AudioRendererMixerInput::GetOutputParameters() {
- return mixer_->GetOutputDevice()->GetOutputParameters();
-}
-
-OutputDeviceStatus AudioRendererMixerInput::GetDeviceStatus() {
- if (!mixer_)
- return OUTPUT_DEVICE_STATUS_ERROR_INTERNAL;
-
- return mixer_->GetOutputDevice()->GetDeviceStatus();
-}
-
double AudioRendererMixerInput::ProvideInput(AudioBus* audio_bus,
base::TimeDelta buffer_delay) {
- int frames_filled = callback_->Render(
- audio_bus, static_cast<int>(buffer_delay.InMillisecondsF() + 0.5), 0);
+ // TODO(chcunningham): Delete this conversion and change ProvideInput to more
+ // precisely describe delay as a count of frames delayed instead of TimeDelta.
+ // See http://crbug.com/587522.
+ uint32_t frames_delayed = std::round(buffer_delay.InMicroseconds() /
+ params_.GetMicrosecondsPerFrame());
+
+ int frames_filled = callback_->Render(audio_bus, frames_delayed, 0);
// AudioConverter expects unfilled frames to be zeroed.
if (frames_filled < audio_bus->frames()) {
@@ -174,7 +175,13 @@ double AudioRendererMixerInput::ProvideInput(AudioBus* audio_bus,
frames_filled, audio_bus->frames() - frames_filled);
}
- return frames_filled > 0 ? volume_ : 0;
+ // We're reading |volume_| from the audio device thread and must avoid racing
+ // with the main/media thread calls to SetVolume(). See thread safety comment
+ // in the header file.
+ {
+ base::AutoLock auto_lock(volume_lock_);
+ return frames_filled > 0 ? volume_ : 0;
+ }
}
void AudioRendererMixerInput::OnRenderError() {
diff --git a/chromium/media/base/audio_renderer_mixer_input.h b/chromium/media/base/audio_renderer_mixer_input.h
index 50f54bdd436..a75320e78c6 100644
--- a/chromium/media/base/audio_renderer_mixer_input.h
+++ b/chromium/media/base/audio_renderer_mixer_input.h
@@ -1,6 +1,17 @@
// 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.
+//
+// THREAD SAFETY
+//
+// This class is generally not thread safe. Callers should ensure thread safety.
+// For instance, the |sink_lock_| in WebAudioSourceProvider synchronizes access
+// to this object across the main thread (for WebAudio APIs) and the
+// media thread (for HTMLMediaElement APIs).
+//
+// The one exception is protection for |volume_| via |volume_lock_|. This lock
+// prevents races between SetVolume() (called on any thread) and ProvideInput
+// (called on audio device thread). See http://crbug.com/588992.
#ifndef MEDIA_BASE_AUDIO_RENDERER_MIXER_INPUT_H_
#define MEDIA_BASE_AUDIO_RENDERER_MIXER_INPUT_H_
@@ -9,6 +20,7 @@
#include "base/callback.h"
#include "base/macros.h"
+#include "base/synchronization/lock.h"
#include "media/base/audio_converter.h"
#include "media/base/audio_renderer_sink.h"
#include "url/origin.h"
@@ -18,8 +30,7 @@ namespace media {
class AudioRendererMixer;
class MEDIA_EXPORT AudioRendererMixerInput
- : NON_EXPORTED_BASE(public RestartableAudioRendererSink),
- NON_EXPORTED_BASE(public OutputDevice),
+ : NON_EXPORTED_BASE(public SwitchableAudioRendererSink),
public AudioConverter::InputCallback {
public:
typedef base::Callback<AudioRendererMixer*(const AudioParameters& params,
@@ -37,22 +48,18 @@ class MEDIA_EXPORT AudioRendererMixerInput
const std::string& device_id,
const url::Origin& security_origin);
- // RestartableAudioRendererSink implementation.
+ // SwitchableAudioRendererSink implementation.
void Start() override;
void Stop() override;
void Play() override;
void Pause() override;
bool SetVolume(double volume) override;
- OutputDevice* GetOutputDevice() override;
+ OutputDeviceInfo GetOutputDeviceInfo() override;
void Initialize(const AudioParameters& params,
AudioRendererSink::RenderCallback* renderer) override;
-
- // OutputDevice implementation.
void SwitchOutputDevice(const std::string& device_id,
const url::Origin& security_origin,
- const SwitchOutputDeviceCB& callback) override;
- AudioParameters GetOutputParameters() override;
- OutputDeviceStatus GetDeviceStatus() override;
+ const OutputDeviceStatusCB& callback) override;
// Called by AudioRendererMixer when an error occurs.
void OnRenderError();
@@ -63,7 +70,11 @@ class MEDIA_EXPORT AudioRendererMixerInput
private:
friend class AudioRendererMixerInputTest;
- bool initialized_;
+ // Protect |volume_|, accessed by separate threads in ProvideInput() and
+ // SetVolume().
+ base::Lock volume_lock_;
+
+ bool started_;
bool playing_;
double volume_;
@@ -95,7 +106,7 @@ class MEDIA_EXPORT AudioRendererMixerInput
// Pending switch-device callback, in case SwitchOutputDevice() is invoked
// before Start()
- SwitchOutputDeviceCB pending_switch_callback_;
+ OutputDeviceStatusCB pending_switch_callback_;
std::string pending_switch_device_id_;
url::Origin pending_switch_security_origin_;
diff --git a/chromium/media/base/audio_renderer_mixer_unittest.cc b/chromium/media/base/audio_renderer_mixer_unittest.cc
index fb81c0f145a..f338a696031 100644
--- a/chromium/media/base/audio_renderer_mixer_unittest.cc
+++ b/chromium/media/base/audio_renderer_mixer_unittest.cc
@@ -464,7 +464,7 @@ TEST_P(AudioRendererMixerBehavioralTest, OnRenderErrorPausedInput) {
// not call RemoveMixer().
TEST_P(AudioRendererMixerBehavioralTest, NoInitialize) {
EXPECT_CALL(*this, RemoveMixer(testing::_, testing::_, testing::_)).Times(0);
- scoped_refptr<AudioRendererMixerInput> audio_renderer_mixer =
+ scoped_refptr<AudioRendererMixerInput> audio_renderer_mixer_input =
new AudioRendererMixerInput(
base::Bind(&AudioRendererMixerTest::GetMixer, base::Unretained(this)),
base::Bind(&AudioRendererMixerTest::RemoveMixer,
diff --git a/chromium/media/base/audio_renderer_sink.h b/chromium/media/base/audio_renderer_sink.h
index 9da243b7c55..01be1017aeb 100644
--- a/chromium/media/base/audio_renderer_sink.h
+++ b/chromium/media/base/audio_renderer_sink.h
@@ -8,21 +8,13 @@
#include <stdint.h>
#include <string>
-#include <vector>
#include "base/callback.h"
-#include "base/logging.h"
#include "base/memory/ref_counted.h"
-#include "media/audio/audio_output_ipc.h"
#include "media/audio/audio_parameters.h"
#include "media/base/audio_bus.h"
-#include "media/base/media_export.h"
-#include "media/base/output_device.h"
-#include "url/gurl.h"
-
-namespace base {
-class SingleThreadTaskRunner;
-}
+#include "media/base/output_device_info.h"
+#include "url/origin.h"
namespace media {
@@ -39,7 +31,7 @@ class AudioRendererSink
// number of frames filled. |frames_skipped| contains the number of frames
// the consumer has skipped, if any.
virtual int Render(AudioBus* dest,
- uint32_t audio_delay_milliseconds,
+ uint32_t frames_delayed,
uint32_t frames_skipped) = 0;
// Signals an error has occurred.
@@ -71,12 +63,12 @@ class AudioRendererSink
// Returns |true| on success.
virtual bool SetVolume(double volume) = 0;
- // Returns a pointer to the internal output device.
- // This pointer is not to be owned by the caller and is valid only during
- // the lifetime of the AudioRendererSink.
- // It can be null, which means that access to the output device is not
- // supported.
- virtual OutputDevice* GetOutputDevice() = 0;
+ // Returns current output device information. If the information is not
+ // available yet, this method may block until it becomes available.
+ // If the sink is not associated with any output device, |device_status| of
+ // OutputDeviceInfo should be set to OUTPUT_DEVICE_STATUS_ERROR_INTERNAL.
+ // Must never be called on the IO thread.
+ virtual OutputDeviceInfo GetOutputDeviceInfo() = 0;
protected:
friend class base::RefCountedThreadSafe<AudioRendererSink>;
@@ -93,6 +85,21 @@ class RestartableAudioRendererSink : public AudioRendererSink {
~RestartableAudioRendererSink() override {}
};
+class SwitchableAudioRendererSink : public RestartableAudioRendererSink {
+ public:
+ // Attempts to switch the audio output device associated with a sink.
+ // Once the attempt is finished, |callback| is invoked with the
+ // result of the operation passed as a parameter. The result is a value from
+ // the media::OutputDeviceStatus enum.
+ // There is no guarantee about the thread where |callback| will be invoked.
+ virtual void SwitchOutputDevice(const std::string& device_id,
+ const url::Origin& security_origin,
+ const OutputDeviceStatusCB& callback) = 0;
+
+ protected:
+ ~SwitchableAudioRendererSink() override {}
+};
+
} // namespace media
#endif // MEDIA_BASE_AUDIO_RENDERER_SINK_H_
diff --git a/chromium/media/base/audio_shifter.cc b/chromium/media/base/audio_shifter.cc
index f5aee2c0e6b..6a268d3f633 100644
--- a/chromium/media/base/audio_shifter.cc
+++ b/chromium/media/base/audio_shifter.cc
@@ -82,27 +82,32 @@ AudioShifter::AudioQueueEntry::AudioQueueEntry(
audio(audio_.release()) {
}
+AudioShifter::AudioQueueEntry::AudioQueueEntry(const AudioQueueEntry& other) =
+ default;
+
AudioShifter::AudioQueueEntry::~AudioQueueEntry() {}
AudioShifter::AudioShifter(base::TimeDelta max_buffer_size,
base::TimeDelta clock_accuracy,
base::TimeDelta adjustment_time,
- size_t rate,
- int channels) :
- max_buffer_size_(max_buffer_size),
- clock_accuracy_(clock_accuracy),
- adjustment_time_(adjustment_time),
- rate_(rate),
- input_clock_smoother_(new ClockSmoother(clock_accuracy)),
- output_clock_smoother_(new ClockSmoother(clock_accuracy)),
- running_(false),
- position_(0),
- previous_requested_samples_(0),
- resampler_(channels, 1.0, 96,
- base::Bind(&AudioShifter::ResamplerCallback,
- base::Unretained(this))),
- current_ratio_(1.0) {
-}
+ int rate,
+ int channels)
+ : max_buffer_size_(max_buffer_size),
+ clock_accuracy_(clock_accuracy),
+ adjustment_time_(adjustment_time),
+ rate_(rate),
+ channels_(channels),
+ input_clock_smoother_(new ClockSmoother(clock_accuracy)),
+ output_clock_smoother_(new ClockSmoother(clock_accuracy)),
+ running_(false),
+ position_(0),
+ previous_requested_samples_(0),
+ resampler_(
+ channels,
+ 1.0,
+ 96,
+ base::Bind(&AudioShifter::ResamplerCallback, base::Unretained(this))),
+ current_ratio_(1.0) {}
AudioShifter::~AudioShifter() {}
@@ -270,15 +275,6 @@ void AudioShifter::ResamplerCallback(int frame_delay, AudioBus* destination) {
}
}
-void AudioShifter::Flush() {
- resampler_.Flush();
- position_ = 0;
- queue_.clear();
- running_ = false;
- previous_playout_time_ = base::TimeTicks();
- bias_ = base::TimeDelta();
-}
-
void AudioShifter::Zero(AudioBus* output) {
output->Zero();
running_ = false;
diff --git a/chromium/media/base/audio_shifter.h b/chromium/media/base/audio_shifter.h
index e7bc502af3e..3429a2f197d 100644
--- a/chromium/media/base/audio_shifter.h
+++ b/chromium/media/base/audio_shifter.h
@@ -56,10 +56,13 @@ class MEDIA_EXPORT AudioShifter {
AudioShifter(base::TimeDelta max_buffer_size,
base::TimeDelta clock_accuracy,
base::TimeDelta adjustment_time,
- size_t rate,
+ int rate,
int channels);
~AudioShifter();
+ int sample_rate() const { return rate_; }
+ int channels() const { return channels_; }
+
// Push Audio into the shifter. All inputs must have the same number of
// channels, but bus size can vary. The playout time can be noisy and
// does not have to line up perfectly with the number of samples pushed
@@ -79,9 +82,6 @@ class MEDIA_EXPORT AudioShifter {
// calculate playout_time would be now + audio pipeline delay.
void Pull(AudioBus* output, base::TimeTicks playout_time);
- // Flush audio (but leave timing info)
- void Flush();
-
private:
void Zero(AudioBus* output);
void ResamplerCallback(int frame_delay, AudioBus* destination);
@@ -89,6 +89,7 @@ private:
struct AudioQueueEntry {
AudioQueueEntry(base::TimeTicks target_playout_time_,
scoped_ptr<AudioBus> audio_);
+ AudioQueueEntry(const AudioQueueEntry& other);
~AudioQueueEntry();
base::TimeTicks target_playout_time;
linked_ptr<AudioBus> audio;
@@ -100,7 +101,8 @@ private:
const base::TimeDelta max_buffer_size_;
const base::TimeDelta clock_accuracy_;
const base::TimeDelta adjustment_time_;
- const size_t rate_;
+ const int rate_;
+ const int channels_;
// The clock smoothers are used to smooth out timestamps
// and adjust for drift and inaccurate clocks.
diff --git a/chromium/media/base/audio_video_metadata_extractor.cc b/chromium/media/base/audio_video_metadata_extractor.cc
index 0ba36e12062..5b92c1511c1 100644
--- a/chromium/media/base/audio_video_metadata_extractor.cc
+++ b/chromium/media/base/audio_video_metadata_extractor.cc
@@ -54,6 +54,9 @@ const int kAttachedImageSizeLimit = 4 * 1024 * 1024;
AudioVideoMetadataExtractor::StreamInfo::StreamInfo() {}
+AudioVideoMetadataExtractor::StreamInfo::StreamInfo(const StreamInfo& other) =
+ default;
+
AudioVideoMetadataExtractor::StreamInfo::~StreamInfo() {}
AudioVideoMetadataExtractor::AudioVideoMetadataExtractor()
diff --git a/chromium/media/base/audio_video_metadata_extractor.h b/chromium/media/base/audio_video_metadata_extractor.h
index 97f7a5b7e89..4e5906dbd1a 100644
--- a/chromium/media/base/audio_video_metadata_extractor.h
+++ b/chromium/media/base/audio_video_metadata_extractor.h
@@ -26,6 +26,7 @@ class MEDIA_EXPORT AudioVideoMetadataExtractor {
struct StreamInfo {
StreamInfo();
+ StreamInfo(const StreamInfo& other);
~StreamInfo();
std::string type;
TagDictionary tags;
diff --git a/chromium/media/base/audio_video_metadata_extractor_unittest.cc b/chromium/media/base/audio_video_metadata_extractor_unittest.cc
index d67808f16eb..744d3ebd96a 100644
--- a/chromium/media/base/audio_video_metadata_extractor_unittest.cc
+++ b/chromium/media/base/audio_video_metadata_extractor_unittest.cc
@@ -39,6 +39,13 @@ scoped_ptr<AudioVideoMetadataExtractor> GetExtractor(
return extractor;
}
+const std::string GetTagValue(
+ const media::AudioVideoMetadataExtractor::TagDictionary& tags,
+ const char* tag_name) {
+ auto tag_data = tags.find(tag_name);
+ return tag_data == tags.end() ? "" : tag_data->second;
+}
+
TEST(AudioVideoMetadataExtractorTest, InvalidFile) {
GetExtractor("ten_byte_file", true, false, 0, -1, -1);
}
@@ -56,7 +63,7 @@ TEST(AudioVideoMetadataExtractorTest, AudioOGG) {
EXPECT_EQ(1u, extractor->stream_infos()[1].tags.size());
EXPECT_EQ("vorbis", extractor->stream_infos()[1].type);
EXPECT_EQ("Processed by SoX",
- extractor->stream_infos()[1].tags.find("COMMENT")->second);
+ GetTagValue(extractor->stream_infos()[1].tags, "COMMENT"));
EXPECT_EQ(0u, extractor->attached_images_bytes().size());
}
@@ -72,9 +79,9 @@ TEST(AudioVideoMetadataExtractorTest, AudioWAV) {
EXPECT_EQ(2u, extractor->stream_infos()[0].tags.size());
EXPECT_EQ("Lavf54.37.100",
- extractor->stream_infos()[0].tags.find("encoder")->second);
+ GetTagValue(extractor->stream_infos()[0].tags, "encoder"));
EXPECT_EQ("Amadeus Pro",
- extractor->stream_infos()[0].tags.find("encoded_by")->second);
+ GetTagValue(extractor->stream_infos()[0].tags, "encoded_by"));
EXPECT_EQ("pcm_u8", extractor->stream_infos()[1].type);
EXPECT_EQ(0u, extractor->stream_infos()[1].tags.size());
@@ -92,7 +99,7 @@ TEST(AudioVideoMetadataExtractorTest, VideoWebM) {
EXPECT_EQ("matroska,webm", extractor->stream_infos()[0].type);
EXPECT_EQ(1u, extractor->stream_infos()[0].tags.size());
EXPECT_EQ("Lavf53.9.0",
- extractor->stream_infos()[0].tags.find("ENCODER")->second);
+ GetTagValue(extractor->stream_infos()[0].tags, "ENCODER"));
EXPECT_EQ("vp8", extractor->stream_infos()[1].type);
EXPECT_EQ(0u, extractor->stream_infos()[1].tags.size());
@@ -109,7 +116,7 @@ TEST(AudioVideoMetadataExtractorTest, VideoWebM) {
EXPECT_EQ("pcm_s16le", extractor->stream_infos()[5].type);
EXPECT_EQ(1u, extractor->stream_infos()[5].tags.size());
EXPECT_EQ("Lavc52.32.0",
- extractor->stream_infos()[5].tags.find("ENCODER")->second);
+ GetTagValue(extractor->stream_infos()[5].tags, "ENCODER"));
EXPECT_EQ(0u, extractor->attached_images_bytes().size());
}
@@ -125,33 +132,31 @@ TEST(AudioVideoMetadataExtractorTest, AndroidRotatedMP4Video) {
EXPECT_EQ("mov,mp4,m4a,3gp,3g2,mj2", extractor->stream_infos()[0].type);
EXPECT_EQ(4u, extractor->stream_infos()[0].tags.size());
- EXPECT_EQ(
- "isom3gp4",
- extractor->stream_infos()[0].tags.find("compatible_brands")->second);
- EXPECT_EQ(
- "2014-02-11 00:39:25",
- extractor->stream_infos()[0].tags.find("creation_time")->second);
+ EXPECT_EQ("isom3gp4", GetTagValue(extractor->stream_infos()[0].tags,
+ "compatible_brands"));
+ EXPECT_EQ("2014-02-11 00:39:25",
+ GetTagValue(extractor->stream_infos()[0].tags, "creation_time"));
EXPECT_EQ("isom",
- extractor->stream_infos()[0].tags.find("major_brand")->second);
+ GetTagValue(extractor->stream_infos()[0].tags, "major_brand"));
EXPECT_EQ("0",
- extractor->stream_infos()[0].tags.find("minor_version")->second);
+ GetTagValue(extractor->stream_infos()[0].tags, "minor_version"));
EXPECT_EQ("h264", extractor->stream_infos()[1].type);
EXPECT_EQ(5u, extractor->stream_infos()[1].tags.size());
EXPECT_EQ("2014-02-11 00:39:25",
- extractor->stream_infos()[1].tags.find("creation_time")->second);
+ GetTagValue(extractor->stream_infos()[1].tags, "creation_time"));
EXPECT_EQ("VideoHandle",
- extractor->stream_infos()[1].tags.find("handler_name")->second);
- EXPECT_EQ("eng", extractor->stream_infos()[1].tags.find("language")->second);
- EXPECT_EQ("90", extractor->stream_infos()[1].tags.find("rotate")->second);
+ GetTagValue(extractor->stream_infos()[1].tags, "handler_name"));
+ EXPECT_EQ("eng", GetTagValue(extractor->stream_infos()[1].tags, "language"));
+ EXPECT_EQ("90", GetTagValue(extractor->stream_infos()[1].tags, "rotate"));
EXPECT_EQ("aac", extractor->stream_infos()[2].type);
EXPECT_EQ(3u, extractor->stream_infos()[2].tags.size());
EXPECT_EQ("2014-02-11 00:39:25",
- extractor->stream_infos()[2].tags.find("creation_time")->second);
+ GetTagValue(extractor->stream_infos()[2].tags, "creation_time"));
EXPECT_EQ("SoundHandle",
- extractor->stream_infos()[2].tags.find("handler_name")->second);
- EXPECT_EQ("eng", extractor->stream_infos()[2].tags.find("language")->second);
+ GetTagValue(extractor->stream_infos()[2].tags, "handler_name"));
+ EXPECT_EQ("eng", GetTagValue(extractor->stream_infos()[2].tags, "language"));
EXPECT_EQ(0u, extractor->attached_images_bytes().size());
}
@@ -173,23 +178,23 @@ TEST(AudioVideoMetadataExtractorTest, AudioMP3) {
EXPECT_EQ("mp3", extractor->stream_infos()[0].type);
EXPECT_EQ(7u, extractor->stream_infos()[0].tags.size());
EXPECT_EQ("OK Computer",
- extractor->stream_infos()[0].tags.find("album")->second);
+ GetTagValue(extractor->stream_infos()[0].tags, "album"));
EXPECT_EQ("Radiohead",
- extractor->stream_infos()[0].tags.find("artist")->second);
- EXPECT_EQ("1997", extractor->stream_infos()[0].tags.find("date")->second);
+ GetTagValue(extractor->stream_infos()[0].tags, "artist"));
+ EXPECT_EQ("1997", GetTagValue(extractor->stream_infos()[0].tags, "date"));
EXPECT_EQ("Lavf54.4.100",
- extractor->stream_infos()[0].tags.find("encoder")->second);
+ GetTagValue(extractor->stream_infos()[0].tags, "encoder"));
EXPECT_EQ("Alternative",
- extractor->stream_infos()[0].tags.find("genre")->second);
- EXPECT_EQ("Airbag", extractor->stream_infos()[0].tags.find("title")->second);
- EXPECT_EQ("1", extractor->stream_infos()[0].tags.find("track")->second);
+ GetTagValue(extractor->stream_infos()[0].tags, "genre"));
+ EXPECT_EQ("Airbag", GetTagValue(extractor->stream_infos()[0].tags, "title"));
+ EXPECT_EQ("1", GetTagValue(extractor->stream_infos()[0].tags, "track"));
EXPECT_EQ("mp3", extractor->stream_infos()[1].type);
EXPECT_EQ(0u, extractor->stream_infos()[1].tags.size());
EXPECT_EQ("png", extractor->stream_infos()[2].type);
EXPECT_EQ(1u, extractor->stream_infos()[2].tags.size());
- EXPECT_EQ("Other", extractor->stream_infos()[2].tags.find("comment")->second);
+ EXPECT_EQ("Other", GetTagValue(extractor->stream_infos()[2].tags, "comment"));
EXPECT_EQ(1u, extractor->attached_images_bytes().size());
EXPECT_EQ(155752u, extractor->attached_images_bytes()[0].size());
diff --git a/chromium/media/base/bind_to_current_loop.h b/chromium/media/base/bind_to_current_loop.h
index da568ed6ac5..8c14e9e88cd 100644
--- a/chromium/media/base/bind_to_current_loop.h
+++ b/chromium/media/base/bind_to_current_loop.h
@@ -7,6 +7,7 @@
#include "base/bind.h"
#include "base/location.h"
+#include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h"
#include "base/single_thread_task_runner.h"
#include "base/thread_task_runner_handle.h"
@@ -26,7 +27,7 @@
namespace media {
-// Mimic base::internal::CallbackForward, replacing p.Pass() with
+// Mimic base::internal::CallbackForward, replacing std::move(p) with
// base::Passed(&p) to account for the extra layer of indirection.
namespace internal {
template <typename T>
diff --git a/chromium/media/base/bind_to_current_loop_unittest.cc b/chromium/media/base/bind_to_current_loop_unittest.cc
index 7fb56d48cdb..ba981d0e15b 100644
--- a/chromium/media/base/bind_to_current_loop_unittest.cc
+++ b/chromium/media/base/bind_to_current_loop_unittest.cc
@@ -6,6 +6,7 @@
#include <utility>
+#include "base/memory/free_deleter.h"
#include "base/message_loop/message_loop.h"
#include "base/synchronization/waitable_event.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -23,7 +24,7 @@ void BoundBoolSetFromScopedPtr(bool* var, scoped_ptr<bool> val) {
void BoundBoolSetFromScopedPtrFreeDeleter(
bool* var,
scoped_ptr<bool, base::FreeDeleter> val) {
- *var = val;
+ *var = *val;
}
void BoundBoolSetFromScopedArray(bool* var, scoped_ptr<bool[]> val) {
diff --git a/chromium/media/base/bit_reader_core.cc b/chromium/media/base/bit_reader_core.cc
index 32d2d530121..237470c7669 100644
--- a/chromium/media/base/bit_reader_core.cc
+++ b/chromium/media/base/bit_reader_core.cc
@@ -86,8 +86,11 @@ bool BitReaderCore::SkipBits(int num_bits) {
byte_stream_provider_->GetBytes(nbytes, &byte_stream_window);
DCHECK_GE(window_size, 0);
DCHECK_LE(window_size, nbytes);
- if (window_size < nbytes)
+ if (window_size < nbytes) {
+ // Note that some bytes were consumed.
+ bits_read_ += 8 * window_size;
return false;
+ }
num_bits -= 8 * nbytes;
bits_read_ += 8 * nbytes;
}
diff --git a/chromium/media/base/bit_reader_fuzzertest.cc b/chromium/media/base/bit_reader_fuzzertest.cc
new file mode 100644
index 00000000000..762a9b63818
--- /dev/null
+++ b/chromium/media/base/bit_reader_fuzzertest.cc
@@ -0,0 +1,37 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/hash.h"
+#include "base/numerics/safe_conversions.h"
+#include "media/base/bit_reader.h"
+#include "media/base/test_random.h"
+
+// Entry point for LibFuzzer.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ media::BitReader reader(data, base::checked_cast<int>(size));
+
+ // Need a simple random number generator to generate the number of bits to
+ // read/skip in a reproducible way (given the same |data|). Using Hash() to
+ // ensure the seed varies significantly over minor changes in |data|.
+ media::TestRandom rnd(base::Hash(reinterpret_cast<const char*>(data), size));
+
+ // Read and skip through the data in |reader|.
+ while (reader.bits_available() > 0) {
+ if (rnd.Rand() & 1) {
+ // Read up to 64 bits. This may fail if there is not enough bits
+ // remaining, but it doesn't matter (testing for failures is also good).
+ uint64_t value;
+ if (!reader.ReadBits(rnd.Rand() % 64 + 1, &value))
+ break;
+ } else {
+ // Skip up to 128 bits. As above, this may fail.
+ if (!reader.SkipBits(rnd.Rand() % 128 + 1))
+ break;
+ }
+ }
+ return 0;
+}
diff --git a/chromium/media/base/bitstream_buffer.cc b/chromium/media/base/bitstream_buffer.cc
index 49caf5b8215..5a1907db25d 100644
--- a/chromium/media/base/bitstream_buffer.cc
+++ b/chromium/media/base/bitstream_buffer.cc
@@ -6,23 +6,22 @@
namespace media {
-BitstreamBuffer::BitstreamBuffer(int32_t id,
- base::SharedMemoryHandle handle,
- size_t size)
- : id_(id),
- handle_(handle),
- size_(size),
- presentation_timestamp_(kNoTimestamp()) {}
+BitstreamBuffer::BitstreamBuffer()
+ : BitstreamBuffer(-1, base::SharedMemoryHandle(), 0) {}
BitstreamBuffer::BitstreamBuffer(int32_t id,
base::SharedMemoryHandle handle,
size_t size,
+ off_t offset,
base::TimeDelta presentation_timestamp)
: id_(id),
handle_(handle),
size_(size),
+ offset_(offset),
presentation_timestamp_(presentation_timestamp) {}
+BitstreamBuffer::BitstreamBuffer(const BitstreamBuffer& other) = default;
+
BitstreamBuffer::~BitstreamBuffer() {}
void BitstreamBuffer::SetDecryptConfig(const DecryptConfig& decrypt_config) {
diff --git a/chromium/media/base/bitstream_buffer.h b/chromium/media/base/bitstream_buffer.h
index fe3c8da358a..000836eaa85 100644
--- a/chromium/media/base/bitstream_buffer.h
+++ b/chromium/media/base/bitstream_buffer.h
@@ -15,18 +15,32 @@
#include "media/base/media_export.h"
#include "media/base/timestamp_constants.h"
+namespace IPC {
+template <class P>
+struct ParamTraits;
+}
+
namespace media {
// Class for passing bitstream buffers around. Does not take ownership of the
// data. This is the media-namespace equivalent of PP_VideoBitstreamBuffer_Dev.
class MEDIA_EXPORT BitstreamBuffer {
public:
- BitstreamBuffer(int32_t id, base::SharedMemoryHandle handle, size_t size);
-
+ BitstreamBuffer();
+
+ // Constructs a new BitstreamBuffer. The content of the bitstream is located
+ // at |offset| bytes away from the start of the shared memory and the payload
+ // is |size| bytes. When not provided, the default value for |offset| is 0.
+ // |presentation_timestamp| is when the decoded frame should be displayed.
+ // When not provided, |presentation_timestamp| will be
+ // |media::kNoTimestamp()|.
BitstreamBuffer(int32_t id,
base::SharedMemoryHandle handle,
size_t size,
- base::TimeDelta presentation_timestamp);
+ off_t offset = 0,
+ base::TimeDelta presentation_timestamp = kNoTimestamp());
+
+ BitstreamBuffer(const BitstreamBuffer& other);
~BitstreamBuffer();
@@ -34,13 +48,21 @@ class MEDIA_EXPORT BitstreamBuffer {
int32_t id() const { return id_; }
base::SharedMemoryHandle handle() const { return handle_; }
+
+ // The number of bytes of the actual bitstream data. It is the size of the
+ // content instead of the whole shared memory.
size_t size() const { return size_; }
+ // The offset to the start of actual bitstream data in the shared memory.
+ off_t offset() const { return offset_; }
+
// The timestamp is only valid if it's not equal to |media::kNoTimestamp()|.
base::TimeDelta presentation_timestamp() const {
return presentation_timestamp_;
}
+ void set_handle(const base::SharedMemoryHandle& handle) { handle_ = handle; }
+
// The following methods come from DecryptConfig.
const std::string& key_id() const { return key_id_; }
@@ -51,6 +73,7 @@ class MEDIA_EXPORT BitstreamBuffer {
int32_t id_;
base::SharedMemoryHandle handle_;
size_t size_;
+ off_t offset_;
// This is only set when necessary. For example, AndroidVideoDecodeAccelerator
// needs the timestamp because the underlying decoder may require it to
@@ -65,6 +88,8 @@ class MEDIA_EXPORT BitstreamBuffer {
std::string iv_; // initialization vector
std::vector<SubsampleEntry> subsamples_; // clear/cypher sizes
+ friend struct IPC::ParamTraits<media::BitstreamBuffer>;
+
// Allow compiler-generated copy & assign constructors.
};
diff --git a/chromium/media/base/cdm_callback_promise.cc b/chromium/media/base/cdm_callback_promise.cc
index 496ad1aba76..21a3412f7eb 100644
--- a/chromium/media/base/cdm_callback_promise.cc
+++ b/chromium/media/base/cdm_callback_promise.cc
@@ -4,6 +4,7 @@
#include "media/base/cdm_callback_promise.h"
+#include "base/callback_helpers.h"
#include "base/logging.h"
namespace media {
@@ -19,12 +20,17 @@ CdmCallbackPromise<T...>::CdmCallbackPromise(
template <typename... T>
CdmCallbackPromise<T...>::~CdmCallbackPromise() {
+ if (IsPromiseSettled())
+ return;
+
+ DCHECK(!resolve_cb_.is_null() && !reject_cb_.is_null());
+ RejectPromiseOnDestruction();
}
template <typename... T>
void CdmCallbackPromise<T...>::resolve(const T&... result) {
MarkPromiseSettled();
- resolve_cb_.Run(result...);
+ base::ResetAndReturn(&resolve_cb_).Run(result...);
}
template <typename... T>
@@ -32,7 +38,8 @@ void CdmCallbackPromise<T...>::reject(MediaKeys::Exception exception_code,
uint32_t system_code,
const std::string& error_message) {
MarkPromiseSettled();
- reject_cb_.Run(exception_code, system_code, error_message);
+ base::ResetAndReturn(&reject_cb_)
+ .Run(exception_code, system_code, error_message);
}
// Explicit template instantiation for the Promises needed.
diff --git a/chromium/media/base/cdm_callback_promise.h b/chromium/media/base/cdm_callback_promise.h
index b271a79bb35..3ddc34043fc 100644
--- a/chromium/media/base/cdm_callback_promise.h
+++ b/chromium/media/base/cdm_callback_promise.h
@@ -36,7 +36,9 @@ class MEDIA_EXPORT CdmCallbackPromise : public CdmPromiseTemplate<T...> {
const std::string& error_message) override;
private:
+ using CdmPromiseTemplate<T...>::IsPromiseSettled;
using CdmPromiseTemplate<T...>::MarkPromiseSettled;
+ using CdmPromiseTemplate<T...>::RejectPromiseOnDestruction;
base::Callback<void(const T&...)> resolve_cb_;
PromiseRejectedCB reject_cb_;
diff --git a/chromium/media/base/cdm_context.cc b/chromium/media/base/cdm_context.cc
index b53fc7d3a54..5bba924e7e0 100644
--- a/chromium/media/base/cdm_context.cc
+++ b/chromium/media/base/cdm_context.cc
@@ -6,19 +6,10 @@
namespace media {
-CdmContext::CdmContext() {
-}
+CdmContext::CdmContext() {}
-CdmContext::~CdmContext() {
-}
+CdmContext::~CdmContext() {}
-CdmContextProvider::CdmContextProvider() {
-}
-
-CdmContextProvider::~CdmContextProvider() {
-}
-
-void IgnoreCdmAttached(bool success) {
-}
+void IgnoreCdmAttached(bool /* success */) {}
} // namespace media
diff --git a/chromium/media/base/cdm_context.h b/chromium/media/base/cdm_context.h
index c64f276efd0..d41281ec53d 100644
--- a/chromium/media/base/cdm_context.h
+++ b/chromium/media/base/cdm_context.h
@@ -13,9 +13,9 @@ namespace media {
class Decryptor;
-// An interface representing the context that a media pipeline needs from a
+// An interface representing the context that a media player needs from a
// content decryption module (CDM) to decrypt (and decode) encrypted buffers.
-// Only used for implementing SetCdm().
+// This is used to pass the CDM to the media player (e.g. SetCdm()).
class MEDIA_EXPORT CdmContext {
public:
// Indicates an invalid CDM ID. See GetCdmId() for details.
@@ -24,15 +24,14 @@ class MEDIA_EXPORT CdmContext {
virtual ~CdmContext();
// Gets the Decryptor object associated with the CDM. Returns nullptr if the
- // CDM does not support a Decryptor. Must not return nullptr if GetCdmId()
- // returns kInvalidCdmId. The returned object is only guaranteed to be valid
- // during the CDM's lifetime.
+ // CDM does not support a Decryptor (i.e. platform-based CDMs where decryption
+ // occurs implicitly along with decoding). The returned object is only
+ // guaranteed to be valid during the CDM's lifetime.
virtual Decryptor* GetDecryptor() = 0;
- // Returns an ID that identifies a CDM, or kInvalidCdmId. The interpretation
- // is implementation-specific; current implementations use the ID to locate a
- // remote CDM in a different process. The return value will not be
- // kInvalidCdmId if GetDecryptor() returns nullptr.
+ // Returns an ID that can be used to find a remote CDM, in which case this CDM
+ // serves as a proxy to the remote one. Returns kInvalidCdmId when remote CDM
+ // is not supported (e.g. this CDM is a local CDM).
virtual int GetCdmId() const = 0;
protected:
@@ -42,25 +41,6 @@ class MEDIA_EXPORT CdmContext {
DISALLOW_COPY_AND_ASSIGN(CdmContext);
};
-// An interface for looking up CdmContext objects by the CDM ID.
-class MEDIA_EXPORT CdmContextProvider {
- public:
- virtual ~CdmContextProvider();
-
- // Returns the CdmContext corresponding to |cdm_id|. Returns nullptr if no
- // such CdmContext can be found.
- // Note: Calling GetCdmId() on the returned CdmContext returns kInvalidCdmId
- // (in all current cases) because the CDM will be local in the process where
- // GetCdmContext() is called.
- virtual CdmContext* GetCdmContext(int cdm_id) = 0;
-
- protected:
- CdmContextProvider();
-
- private:
- DISALLOW_COPY_AND_ASSIGN(CdmContextProvider);
-};
-
// Callback to notify that the CdmContext has been completely attached to
// the media pipeline. Parameter indicates whether the operation succeeded.
typedef base::Callback<void(bool)> CdmAttachedCB;
@@ -68,19 +48,6 @@ typedef base::Callback<void(bool)> CdmAttachedCB;
// A dummy implementation of CdmAttachedCB.
MEDIA_EXPORT void IgnoreCdmAttached(bool success);
-// Callback to notify that a CDM is ready. CdmAttachedCB is called when the CDM
-// has been completely attached to the media pipeline.
-typedef base::Callback<void(CdmContext*, const CdmAttachedCB&)> CdmReadyCB;
-
-// Callback to set/cancel a CdmReadyCB.
-// Calling this callback with a non-null callback registers CDM ready
-// notification. When the CDM is ready, notification will be sent
-// through the provided callback.
-// Calling this callback with a null callback cancels previously registered CDM
-// ready notification. Any previously provided callback will be fired
-// immediately with NULL.
-typedef base::Callback<void(const CdmReadyCB&)> SetCdmReadyCB;
-
} // namespace media
#endif // MEDIA_BASE_CDM_CONTEXT_H_
diff --git a/chromium/media/base/cdm_key_information.cc b/chromium/media/base/cdm_key_information.cc
index 70d4464b850..efaed61f971 100644
--- a/chromium/media/base/cdm_key_information.cc
+++ b/chromium/media/base/cdm_key_information.cc
@@ -32,6 +32,8 @@ CdmKeyInformation::CdmKeyInformation(const uint8_t* key_id_data,
status(status),
system_code(system_code) {}
+CdmKeyInformation::CdmKeyInformation(const CdmKeyInformation& other) = default;
+
CdmKeyInformation::~CdmKeyInformation() {
}
diff --git a/chromium/media/base/cdm_key_information.h b/chromium/media/base/cdm_key_information.h
index 89c8112a262..7ed8eff693e 100644
--- a/chromium/media/base/cdm_key_information.h
+++ b/chromium/media/base/cdm_key_information.h
@@ -40,6 +40,7 @@ struct MEDIA_EXPORT CdmKeyInformation {
size_t key_id_length,
KeyStatus status,
uint32_t system_code);
+ CdmKeyInformation(const CdmKeyInformation& other);
~CdmKeyInformation();
std::vector<uint8_t> key_id;
diff --git a/chromium/media/base/cdm_promise.h b/chromium/media/base/cdm_promise.h
index 0c44f80c8fa..dc11e7a0144 100644
--- a/chromium/media/base/cdm_promise.h
+++ b/chromium/media/base/cdm_promise.h
@@ -99,6 +99,8 @@ class MEDIA_EXPORT CdmPromiseTemplate : public CdmPromise {
}
protected:
+ bool IsPromiseSettled() const { return is_settled_; }
+
// All implementations must call this method in resolve() and reject() methods
// to indicate that the promise has been settled.
void MarkPromiseSettled() {
@@ -107,6 +109,17 @@ class MEDIA_EXPORT CdmPromiseTemplate : public CdmPromise {
is_settled_ = true;
}
+ // Must be called by the concrete destructor if !IsPromiseSettled().
+ // Note: We can't call reject() in ~CdmPromise() because reject() is virtual.
+ void RejectPromiseOnDestruction() {
+ DCHECK(!is_settled_);
+ std::string message =
+ "Unfulfilled promise rejected automatically during destruction.";
+ DVLOG(1) << message;
+ reject(MediaKeys::INVALID_STATE_ERROR, 0, message);
+ DCHECK(is_settled_);
+ }
+
private:
// Keep track of whether the promise has been resolved or rejected yet.
bool is_settled_;
diff --git a/chromium/media/base/container_names.cc b/chromium/media/base/container_names.cc
index 8bcb3c04336..24d8ea953ff 100644
--- a/chromium/media/base/container_names.cc
+++ b/chromium/media/base/container_names.cc
@@ -11,6 +11,7 @@
#include "base/logging.h"
#include "base/macros.h"
+#include "base/numerics/safe_conversions.h"
#include "media/base/bit_reader.h"
namespace media {
@@ -1257,13 +1258,15 @@ static bool CheckWebm(const uint8_t* buffer, int buffer_size) {
RCHECK(GetElementId(&reader) == 0x1a45dfa3);
// Get the header size, and ensure there are enough bits to check.
- int header_size = GetVint(&reader);
+ // Using saturated_cast<> in case the size read is really large
+ // (in which case the bits_available() check will fail).
+ int header_size = base::saturated_cast<int>(GetVint(&reader));
RCHECK(reader.bits_available() / 8 >= header_size);
// Loop through the header.
while (reader.bits_available() > 0) {
int tag = GetElementId(&reader);
- int tagsize = GetVint(&reader);
+ int tagsize = base::saturated_cast<int>(GetVint(&reader));
switch (tag) {
case 0x4286: // EBMLVersion
case 0x42f7: // EBMLReadVersion
@@ -1273,15 +1276,18 @@ static bool CheckWebm(const uint8_t* buffer, int buffer_size) {
case 0x4285: // DocTypeReadVersion
case 0xec: // void
case 0xbf: // CRC32
+ RCHECK(reader.bits_available() / 8 >= tagsize);
RCHECK(reader.SkipBits(tagsize * 8));
break;
case 0x4282: // EBMLDocType
// Need to see "webm" or "matroska" next.
+ RCHECK(reader.bits_available() >= 32);
switch (ReadBits(&reader, 32)) {
case TAG('w', 'e', 'b', 'm') :
return true;
case TAG('m', 'a', 't', 'r') :
+ RCHECK(reader.bits_available() >= 32);
return (ReadBits(&reader, 32) == TAG('o', 's', 'k', 'a'));
}
return false;
diff --git a/chromium/media/base/container_names_fuzzertest.cc b/chromium/media/base/container_names_fuzzertest.cc
new file mode 100644
index 00000000000..281fcc4b792
--- /dev/null
+++ b/chromium/media/base/container_names_fuzzertest.cc
@@ -0,0 +1,15 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+
+#include "base/numerics/safe_conversions.h"
+#include "media/base/container_names.h"
+
+// Entry point for LibFuzzer.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ media::container_names::DetermineContainer(data,
+ base::checked_cast<int>(size));
+ return 0;
+}
diff --git a/chromium/media/base/container_names_unittest.cc b/chromium/media/base/container_names_unittest.cc
index 4b71c329bb2..b05632acb63 100644
--- a/chromium/media/base/container_names_unittest.cc
+++ b/chromium/media/base/container_names_unittest.cc
@@ -82,6 +82,32 @@ uint8_t kBug263073Buffer[] = {
0x00, 0x00, 0x00, 0xaa, 0x2e, 0x22, 0xcf, 0x00, 0x00, 0x00, 0x37,
0x67, 0x64, 0x00, 0x28, 0xac, 0x2c, 0xa4, 0x01, 0xe0, 0x08, 0x9f,
0x97, 0x01, 0x52, 0x02, 0x02, 0x02, 0x80, 0x00, 0x01};
+uint8_t kBug584401Buffer[] = {
+ 0x1a, 0x45, 0xdf, 0xa3, 0x01, 0x00, 0x3b, 0x00, 0xb1, 0x00, 0x00, 0x1f,
+ 0x42, 0x86, 0x81, 0x01, 0x42, 0xf7, 0x81, 0x01, 0x42, 0xf2, 0x0b, 0x77,
+ 0x01, 0xb2, 0x74, 0x87, 0xc0, 0x00, 0x20, 0x84, 0x21, 0x08, 0x23, 0x00,
+ 0xae, 0x06, 0x06, 0x01, 0x81, 0x87, 0xbb, 0x8e, 0x0f, 0x9f, 0x3e, 0x7c,
+ 0xfa, 0x61, 0xeb, 0x8e, 0x97, 0x74, 0x37, 0xd0, 0x5f, 0x5c, 0x75, 0x1e,
+ 0x71, 0x30, 0xfe, 0x7c, 0xe0, 0xf9, 0xf3, 0xe7, 0xcf, 0x9f, 0x3e, 0x7c,
+ 0xf8, 0x00, 0xc7, 0x32, 0xe2, 0x31, 0xb9, 0x58, 0x1a, 0xe7, 0xf9, 0x98,
+ 0x0c, 0xdd, 0x6f, 0xd4, 0x20, 0xb7, 0xe4, 0xb4, 0x7e, 0xaa, 0xdf, 0x29,
+ 0xd6, 0x9d, 0x2b, 0x37, 0x2f, 0x7a, 0xc2, 0x5f, 0x52, 0x44, 0x01, 0xdc,
+ 0x32, 0x96, 0xbb, 0xcb, 0x25, 0x52, 0x52, 0xac, 0x6f, 0xb1, 0xbd, 0x85,
+ 0x02, 0x83, 0x7f, 0x5e, 0x43, 0x98, 0xa6, 0x81, 0x04, 0x42, 0xf3, 0x9b,
+ 0x81, 0x30, 0x26, 0xb2, 0x75, 0x8e, 0x66, 0xcf, 0x11, 0xa6, 0xd9, 0x00,
+ 0xaa, 0x00, 0x62, 0xce, 0x6c, 0x6d, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x02, 0xa1, 0xdc, 0xab, 0x8c, 0x47,
+ 0xa9, 0xcf, 0x11, 0x8e, 0xe4, 0x00, 0xc0, 0x0c, 0x20, 0x53, 0x65, 0x68,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x42, 0x82, 0x84, 0x77, 0x2a,
+ 0x65, 0x62, 0x6d, 0x42, 0xbe, 0x4c, 0xda, 0xc9, 0x4c, 0xa5, 0x0f, 0x15,
+ 0xe8, 0xfd, 0x5b, 0x09, 0x8c, 0x38, 0xd7, 0x18, 0x0a, 0x68, 0x41, 0x46,
+ 0x63, 0x18, 0xf9, 0xf4, 0xcb, 0xc7, 0x57, 0x95, 0xd8, 0x0b, 0x2c, 0x91,
+ 0x70, 0x1b, 0x81, 0xd3, 0xda, 0xa0, 0x62, 0x87, 0x2d, 0x03, 0x50, 0x6d,
+ 0x26, 0xb1, 0xcc, 0xb8, 0x8c, 0x81, 0x6e, 0x56};
+uint8_t kBug585243Buffer[] = {0x1a, 0x45, 0xdf, 0xa3, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x06,
+ 0x42, 0x82, 0x42, 0x82, 0x00, 0x00};
// Test that containers that start with fixed strings are handled correctly.
// This is to verify that the TAG matches the first 4 characters of the string.
@@ -100,6 +126,8 @@ TEST(ContainerNamesTest, CheckFixedStrings) {
VERIFY(kRm2Buffer, CONTAINER_RM);
VERIFY(kWtvBuffer, CONTAINER_WTV);
VERIFY(kBug263073Buffer, CONTAINER_MOV);
+ VERIFY(kBug584401Buffer, CONTAINER_EAC3);
+ VERIFY(kBug585243Buffer, CONTAINER_UNKNOWN);
}
// Determine the container type of a specified file.
diff --git a/chromium/media/base/decode_status.cc b/chromium/media/base/decode_status.cc
new file mode 100644
index 00000000000..b0156643062
--- /dev/null
+++ b/chromium/media/base/decode_status.cc
@@ -0,0 +1,26 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/base/decode_status.h"
+
+#include <ostream>
+
+namespace media {
+
+std::ostream& operator<<(std::ostream& os, const DecodeStatus& status) {
+ switch (status) {
+ case DecodeStatus::OK:
+ os << "DecodeStatus::OK";
+ break;
+ case DecodeStatus::ABORTED:
+ os << "DecodeStatus::ABORTED";
+ break;
+ case DecodeStatus::DECODE_ERROR:
+ os << "DecodeStatus::DECODE_ERROR";
+ break;
+ }
+ return os;
+}
+
+} // namespace media
diff --git a/chromium/media/base/decode_status.h b/chromium/media/base/decode_status.h
new file mode 100644
index 00000000000..790eb5fff8c
--- /dev/null
+++ b/chromium/media/base/decode_status.h
@@ -0,0 +1,27 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_BASE_DECODE_STATUS_H_
+#define MEDIA_BASE_DECODE_STATUS_H_
+
+#include <iosfwd>
+
+#include "media/base/media_export.h"
+
+namespace media {
+
+enum class DecodeStatus {
+ OK = 0, // Everything went as planned.
+ ABORTED, // Read aborted due to Reset() during pending read.
+ DECODE_ERROR, // Decoder returned decode error. Note: Prefixed by DECODE_
+ // since ERROR is a reserved name (special macro) on Windows.
+};
+
+// Helper function so that DecodeStatus can be printed easily.
+MEDIA_EXPORT std::ostream& operator<<(std::ostream& os,
+ const DecodeStatus& status);
+
+} // namespace media
+
+#endif // MEDIA_BASE_DECODE_STATUS_H_
diff --git a/chromium/media/base/decoder_factory.cc b/chromium/media/base/decoder_factory.cc
new file mode 100644
index 00000000000..c9fe0576e49
--- /dev/null
+++ b/chromium/media/base/decoder_factory.cc
@@ -0,0 +1,23 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/base/decoder_factory.h"
+
+#include "base/single_thread_task_runner.h"
+
+namespace media {
+
+DecoderFactory::DecoderFactory() {}
+
+DecoderFactory::~DecoderFactory() {}
+
+void DecoderFactory::CreateAudioDecoders(
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+ ScopedVector<AudioDecoder>* audio_decoders) {}
+
+void DecoderFactory::CreateVideoDecoders(
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+ ScopedVector<VideoDecoder>* video_decoders) {}
+
+} // namespace media
diff --git a/chromium/media/base/decoder_factory.h b/chromium/media/base/decoder_factory.h
new file mode 100644
index 00000000000..9ae3f1b2b52
--- /dev/null
+++ b/chromium/media/base/decoder_factory.h
@@ -0,0 +1,46 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_BASE_DECODER_FACTORY_H_
+#define MEDIA_BASE_DECODER_FACTORY_H_
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_vector.h"
+#include "media/base/media_export.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+}
+
+namespace media {
+
+class AudioDecoder;
+class VideoDecoder;
+
+// A factory class for creating audio and video decoders.
+class MEDIA_EXPORT DecoderFactory {
+ public:
+ DecoderFactory();
+ virtual ~DecoderFactory();
+
+ // Creates audio decoders and append them to the end of |audio_decoders|.
+ // Decoders are single-threaded, each decoder should run on |task_runner|.
+ virtual void CreateAudioDecoders(
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+ ScopedVector<AudioDecoder>* audio_decoders);
+
+ // Creates video decoders and append them to the end of |video_decoders|.
+ // Decoders are single-threaded, each decoder should run on |task_runner|.
+ virtual void CreateVideoDecoders(
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner,
+ ScopedVector<VideoDecoder>* video_decoders);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DecoderFactory);
+};
+
+} // namespace media
+
+#endif // MEDIA_BASE_DECODER_FACTORY_H_
diff --git a/chromium/media/base/decrypt_config.h b/chromium/media/base/decrypt_config.h
index c90a3bc1a99..84b3874886d 100644
--- a/chromium/media/base/decrypt_config.h
+++ b/chromium/media/base/decrypt_config.h
@@ -56,6 +56,10 @@ class MEDIA_EXPORT DecryptConfig {
const std::string& iv() const { return iv_; }
const std::vector<SubsampleEntry>& subsamples() const { return subsamples_; }
+ // Returns true if the corresponding decoder buffer requires decryption and
+ // false if that buffer is clear despite the presense of DecryptConfig.
+ bool is_encrypted() const { return !key_id_.empty() && !iv_.empty(); }
+
// Returns true if all fields in |config| match this config.
bool Matches(const DecryptConfig& config) const;
diff --git a/chromium/media/base/demuxer.h b/chromium/media/base/demuxer.h
index 5a2218a3266..027264ba79f 100644
--- a/chromium/media/base/demuxer.h
+++ b/chromium/media/base/demuxer.h
@@ -10,6 +10,7 @@
#include <vector>
#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
#include "base/time/time.h"
#include "media/base/data_source.h"
#include "media/base/demuxer_stream.h"
@@ -22,6 +23,7 @@
namespace media {
class TextTrackConfig;
+class MediaTracks;
class MEDIA_EXPORT DemuxerHost {
public:
@@ -37,8 +39,11 @@ class MEDIA_EXPORT DemuxerHost {
// Duration may be kInfiniteDuration() if the duration is not known.
virtual void SetDuration(base::TimeDelta duration) = 0;
- // Stops execution of the pipeline due to a fatal error. Do not call this
- // method with PIPELINE_OK.
+ // Stops execution of the pipeline due to a fatal error. Do not call this
+ // method with PIPELINE_OK. Stopping is not immediate so demuxers must be
+ // prepared to soft fail on subsequent calls. E.g., if Demuxer::Seek() is
+ // called after an unrecoverable error the provided PipelineStatusCB must be
+ // called with an error.
virtual void OnDemuxerError(PipelineStatus error) = 0;
// Add |text_stream| to the collection managed by the text renderer.
@@ -61,6 +66,11 @@ class MEDIA_EXPORT Demuxer : public DemuxerStreamProvider {
const std::vector<uint8_t>& init_data)>
EncryptedMediaInitDataCB;
+ // Notifies demuxer clients that media track configuration has been updated
+ // (e.g. the inital stream metadata has been parsed successfully, or a new
+ // init segment has been parsed successfully in MSE case).
+ typedef base::Callback<void(scoped_ptr<MediaTracks>)> MediaTracksUpdatedCB;
+
Demuxer();
~Demuxer() override;
@@ -76,6 +86,32 @@ class MEDIA_EXPORT Demuxer : public DemuxerStreamProvider {
const PipelineStatusCB& status_cb,
bool enable_text_tracks) = 0;
+ // Indicates that a new Seek() call is on its way. Implementations may abort
+ // pending reads and future Read() calls may return kAborted until Seek() is
+ // executed. |seek_time| is the presentation timestamp of the new Seek() call.
+ //
+ // In actual use, this call occurs on the main thread while Seek() is called
+ // on the media thread. StartWaitingForSeek() can be used to synchronize the
+ // two.
+ //
+ // StartWaitingForSeek() MUST be called before Seek().
+ virtual void StartWaitingForSeek(base::TimeDelta seek_time) = 0;
+
+ // Indicates that the current Seek() operation is obsoleted by a new one.
+ // Implementations can expect that StartWaitingForSeek() will be called
+ // when the current seek operation completes.
+ //
+ // Like StartWaitingForSeek(), CancelPendingSeek() is called on the main
+ // thread. Ordering with respect to the to-be-canceled Seek() is not
+ // guaranteed. Regardless of ordering, implementations may abort pending reads
+ // and may return kAborted from future Read() calls, until after
+ // StartWaitingForSeek() and the following Seek() call occurs.
+ //
+ // |seek_time| should match that passed to the next StartWaitingForSeek(), but
+ // may not if the seek target changes again before the current seek operation
+ // completes or is aborted.
+ virtual void CancelPendingSeek(base::TimeDelta seek_time) = 0;
+
// Carry out any actions required to seek to the given time, executing the
// callback upon completion.
virtual void Seek(base::TimeDelta time,
diff --git a/chromium/media/base/demuxer_perftest.cc b/chromium/media/base/demuxer_perftest.cc
index 7d8a10ba1de..6703222bc71 100644
--- a/chromium/media/base/demuxer_perftest.cc
+++ b/chromium/media/base/demuxer_perftest.cc
@@ -14,6 +14,7 @@
#include "build/build_config.h"
#include "media/base/media.h"
#include "media/base/media_log.h"
+#include "media/base/media_tracks.h"
#include "media/base/test_data_util.h"
#include "media/base/timestamp_constants.h"
#include "media/filters/ffmpeg_demuxer.h"
@@ -54,6 +55,10 @@ static void OnEncryptedMediaInitData(EmeInitDataType init_data_type,
VLOG(0) << "File is encrypted.";
}
+static void OnMediaTracksUpdated(scoped_ptr<MediaTracks> tracks) {
+ VLOG(0) << "Got media tracks info, tracks = " << tracks->tracks().size();
+}
+
typedef std::vector<media::DemuxerStream* > Streams;
// Simulates playback reading requirements by reading from each stream
@@ -183,8 +188,11 @@ static void RunDemuxerBenchmark(const std::string& filename) {
Demuxer::EncryptedMediaInitDataCB encrypted_media_init_data_cb =
base::Bind(&OnEncryptedMediaInitData);
+ Demuxer::MediaTracksUpdatedCB tracks_updated_cb =
+ base::Bind(&OnMediaTracksUpdated);
FFmpegDemuxer demuxer(message_loop.task_runner(), &data_source,
- encrypted_media_init_data_cb, new MediaLog());
+ encrypted_media_init_data_cb, tracks_updated_cb,
+ new MediaLog());
demuxer.Initialize(&demuxer_host,
base::Bind(&QuitLoopWithStatus, &message_loop),
diff --git a/chromium/media/base/encryption_scheme.cc b/chromium/media/base/encryption_scheme.cc
new file mode 100644
index 00000000000..70d133c65ab
--- /dev/null
+++ b/chromium/media/base/encryption_scheme.cc
@@ -0,0 +1,37 @@
+// Copyright 2015 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/encryption_scheme.h"
+
+namespace media {
+
+EncryptionScheme::Pattern::Pattern() {}
+
+EncryptionScheme::Pattern::Pattern(uint32_t encrypt_blocks,
+ uint32_t skip_blocks)
+ : encrypt_blocks_(encrypt_blocks), skip_blocks_(skip_blocks) {}
+
+EncryptionScheme::Pattern::~Pattern() {}
+
+bool EncryptionScheme::Pattern::Matches(const Pattern& other) const {
+ return encrypt_blocks_ == other.encrypt_blocks() &&
+ skip_blocks_ == other.skip_blocks();
+}
+
+bool EncryptionScheme::Pattern::IsInEffect() const {
+ return encrypt_blocks_ != 0 && skip_blocks_ != 0;
+}
+
+EncryptionScheme::EncryptionScheme() {}
+
+EncryptionScheme::EncryptionScheme(CipherMode mode, const Pattern& pattern)
+ : mode_(mode), pattern_(pattern) {}
+
+EncryptionScheme::~EncryptionScheme() {}
+
+bool EncryptionScheme::Matches(const EncryptionScheme& other) const {
+ return mode_ == other.mode_ && pattern_.Matches(other.pattern_);
+}
+
+} // namespace media
diff --git a/chromium/media/base/encryption_scheme.h b/chromium/media/base/encryption_scheme.h
new file mode 100644
index 00000000000..37bea6710d2
--- /dev/null
+++ b/chromium/media/base/encryption_scheme.h
@@ -0,0 +1,79 @@
+// Copyright 2015 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_ENCRYPTION_SCHEME_H_
+#define MEDIA_BASE_ENCRYPTION_SCHEME_H_
+
+#include <stdint.h>
+
+#include "media/base/media_export.h"
+
+namespace media {
+
+// Specification of whether and how the stream is encrypted (in whole or part).
+class MEDIA_EXPORT EncryptionScheme {
+ public:
+ // Algorithm and mode used for encryption. CIPHER_MODE_UNENCRYPTED indicates
+ // no encryption.
+ enum CipherMode {
+ CIPHER_MODE_UNENCRYPTED,
+ CIPHER_MODE_AES_CTR,
+ CIPHER_MODE_AES_CBC,
+ CIPHER_MODE_MAX = CIPHER_MODE_AES_CBC
+ };
+
+ // CENC 3rd Edition adds pattern encryption, through two new protection
+ // schemes: 'cens' (with AES-CTR) and 'cbcs' (with AES-CBC).
+ // The pattern applies independently to each 'encrypted' part of the frame (as
+ // defined by the relevant subsample entries), and reduces further the
+ // actual encryption applied through a repeating pattern of (encrypt:skip)
+ // 16 byte blocks. For example, in a (1:9) pattern, the first block is
+ // encrypted, and the next nine are skipped. This pattern is applied
+ // repeatedly until the end of the last 16-byte block in the subsample.
+ // Any remaining bytes are left clear.
+ // If either of encrypt_blocks or skip_blocks is 0, pattern encryption is
+ // disabled.
+ class MEDIA_EXPORT Pattern {
+ public:
+ Pattern();
+ Pattern(uint32_t encrypt_blocks, uint32_t skip_blocks);
+ ~Pattern();
+
+ bool Matches(const Pattern& other) const;
+
+ uint32_t encrypt_blocks() const { return encrypt_blocks_; }
+ uint32_t skip_blocks() const { return skip_blocks_; }
+
+ bool IsInEffect() const;
+
+ private:
+ uint32_t encrypt_blocks_ = 0;
+ uint32_t skip_blocks_ = 0;
+
+ // Allow copy and assignment.
+ };
+
+ // The default constructor makes an instance that indicates no encryption.
+ EncryptionScheme();
+
+ // This constructor allows specification of the cipher mode and the pattern.
+ EncryptionScheme(CipherMode mode, const Pattern& pattern);
+ ~EncryptionScheme();
+
+ bool Matches(const EncryptionScheme& other) const;
+
+ bool is_encrypted() const { return mode_ != CIPHER_MODE_UNENCRYPTED; }
+ CipherMode mode() const { return mode_; }
+ const Pattern& pattern() const { return pattern_; }
+
+ private:
+ CipherMode mode_ = CIPHER_MODE_UNENCRYPTED;
+ Pattern pattern_;
+
+ // Allow copy and assignment.
+};
+
+} // namespace media
+
+#endif // MEDIA_BASE_ENCRYPTION_SCHEME_H_
diff --git a/chromium/media/base/fake_audio_renderer_sink.cc b/chromium/media/base/fake_audio_renderer_sink.cc
index 5ce60740b05..40c3cdf9c27 100644
--- a/chromium/media/base/fake_audio_renderer_sink.cc
+++ b/chromium/media/base/fake_audio_renderer_sink.cc
@@ -7,15 +7,20 @@
#include "base/bind.h"
#include "base/location.h"
#include "base/logging.h"
-#include "base/single_thread_task_runner.h"
-#include "media/base/fake_output_device.h"
namespace media {
FakeAudioRendererSink::FakeAudioRendererSink()
: state_(kUninitialized),
callback_(NULL),
- output_device_(new FakeOutputDevice) {}
+ output_device_info_(
+ std::string(),
+ OUTPUT_DEVICE_STATUS_OK,
+ media::AudioParameters(media::AudioParameters::AUDIO_FAKE,
+ media::CHANNEL_LAYOUT_STEREO,
+ media::AudioParameters::kTelephoneSampleRate,
+ 16,
+ 1)) {}
FakeAudioRendererSink::~FakeAudioRendererSink() {
DCHECK(!callback_);
@@ -56,8 +61,8 @@ bool FakeAudioRendererSink::SetVolume(double volume) {
return true;
}
-OutputDevice* FakeAudioRendererSink::GetOutputDevice() {
- return output_device_.get();
+OutputDeviceInfo FakeAudioRendererSink::GetOutputDeviceInfo() {
+ return output_device_info_;
}
bool FakeAudioRendererSink::Render(AudioBus* dest,
diff --git a/chromium/media/base/fake_audio_renderer_sink.h b/chromium/media/base/fake_audio_renderer_sink.h
index afdd01870b2..b69c1b477c3 100644
--- a/chromium/media/base/fake_audio_renderer_sink.h
+++ b/chromium/media/base/fake_audio_renderer_sink.h
@@ -12,11 +12,10 @@
#include "base/macros.h"
#include "media/audio/audio_parameters.h"
#include "media/base/audio_renderer_sink.h"
+#include "media/base/output_device_info.h"
namespace media {
-class FakeOutputDevice;
-
class FakeAudioRendererSink : public AudioRendererSink {
public:
enum State {
@@ -37,7 +36,7 @@ class FakeAudioRendererSink : public AudioRendererSink {
void Pause() override;
void Play() override;
bool SetVolume(double volume) override;
- OutputDevice* GetOutputDevice() override;
+ OutputDeviceInfo GetOutputDeviceInfo() override;
// Attempts to call Render() on the callback provided to
// Initialize() with |dest| and |audio_delay_milliseconds|.
@@ -61,7 +60,7 @@ class FakeAudioRendererSink : public AudioRendererSink {
State state_;
RenderCallback* callback_;
- scoped_ptr<FakeOutputDevice> output_device_;
+ OutputDeviceInfo output_device_info_;
DISALLOW_COPY_AND_ASSIGN(FakeAudioRendererSink);
};
diff --git a/chromium/media/base/fake_demuxer_stream.cc b/chromium/media/base/fake_demuxer_stream.cc
index 267b3e5d8d2..7ed5841c4ab 100644
--- a/chromium/media/base/fake_demuxer_stream.cc
+++ b/chromium/media/base/fake_demuxer_stream.cc
@@ -147,11 +147,11 @@ void FakeDemuxerStream::SeekToStart() {
void FakeDemuxerStream::UpdateVideoDecoderConfig() {
const gfx::Rect kVisibleRect(kStartWidth, kStartHeight);
- video_decoder_config_.Initialize(kCodecVP8, VIDEO_CODEC_PROFILE_UNKNOWN,
- PIXEL_FORMAT_YV12, COLOR_SPACE_UNSPECIFIED,
- next_coded_size_, kVisibleRect,
- next_coded_size_, EmptyExtraData(),
- is_encrypted_);
+ video_decoder_config_.Initialize(
+ kCodecVP8, VIDEO_CODEC_PROFILE_UNKNOWN, PIXEL_FORMAT_YV12,
+ COLOR_SPACE_UNSPECIFIED, next_coded_size_, kVisibleRect, next_coded_size_,
+ EmptyExtraData(),
+ is_encrypted_ ? AesCtrEncryptionScheme() : Unencrypted());
next_coded_size_.Enlarge(kWidthDelta, kHeightDelta);
}
diff --git a/chromium/media/base/fake_output_device.cc b/chromium/media/base/fake_output_device.cc
deleted file mode 100644
index 2f7c8e70062..00000000000
--- a/chromium/media/base/fake_output_device.cc
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright (c) 2015 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/fake_output_device.h"
-
-#include "base/callback.h"
-
-namespace media {
-
-FakeOutputDevice::FakeOutputDevice()
- : FakeOutputDevice(OUTPUT_DEVICE_STATUS_OK) {}
-
-FakeOutputDevice::FakeOutputDevice(OutputDeviceStatus device_status)
- : device_status_(device_status) {}
-
-FakeOutputDevice::~FakeOutputDevice() {}
-
-void FakeOutputDevice::SwitchOutputDevice(
- const std::string& device_id,
- const url::Origin& security_origin,
- const SwitchOutputDeviceCB& callback) {
- callback.Run(device_status_);
-}
-
-AudioParameters FakeOutputDevice::GetOutputParameters() {
- return media::AudioParameters(
- media::AudioParameters::AUDIO_FAKE, media::CHANNEL_LAYOUT_STEREO,
- media::AudioParameters::kTelephoneSampleRate, 16, 1);
-}
-
-OutputDeviceStatus FakeOutputDevice::GetDeviceStatus() {
- return device_status_;
-}
-
-} // namespace media
diff --git a/chromium/media/base/fake_output_device.h b/chromium/media/base/fake_output_device.h
deleted file mode 100644
index 45a6abb7cfd..00000000000
--- a/chromium/media/base/fake_output_device.h
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (c) 2015 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_FAKE_OUTPUT_DEVICE_H_
-#define MEDIA_BASE_FAKE_OUTPUT_DEVICE_H_
-
-#include <string>
-
-#include "base/macros.h"
-#include "media/base/output_device.h"
-
-namespace media {
-
-class FakeOutputDevice : public OutputDevice {
- public:
- FakeOutputDevice();
- explicit FakeOutputDevice(OutputDeviceStatus status);
- ~FakeOutputDevice() override;
-
- // OutputDevice implementation.
- void SwitchOutputDevice(const std::string& device_id,
- const url::Origin& security_origin,
- const SwitchOutputDeviceCB& callback) override;
- AudioParameters GetOutputParameters() override;
- OutputDeviceStatus GetDeviceStatus() override;
-
- private:
- OutputDeviceStatus device_status_;
- DISALLOW_COPY_AND_ASSIGN(FakeOutputDevice);
-};
-
-} // namespace media
-
-#endif // MEDIA_BASE_FAKE_OUTPUT_DEVICE_H_
diff --git a/chromium/media/base/fake_single_thread_task_runner.cc b/chromium/media/base/fake_single_thread_task_runner.cc
new file mode 100644
index 00000000000..77f1f73d3a4
--- /dev/null
+++ b/chromium/media/base/fake_single_thread_task_runner.cc
@@ -0,0 +1,113 @@
+// Copyright 2014 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/fake_single_thread_task_runner.h"
+
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/time/tick_clock.h"
+
+namespace media {
+
+FakeSingleThreadTaskRunner::FakeSingleThreadTaskRunner(
+ base::SimpleTestTickClock* clock)
+ : clock_(clock), fail_on_next_task_(false) {}
+
+FakeSingleThreadTaskRunner::~FakeSingleThreadTaskRunner() {}
+
+bool FakeSingleThreadTaskRunner::PostDelayedTask(
+ const tracked_objects::Location& from_here,
+ const base::Closure& task,
+ base::TimeDelta delay) {
+ if (fail_on_next_task_) {
+ LOG(FATAL) << "Infinite task posting loop detected. Possibly caused by "
+ << from_here.ToString() << " posting a task with delay "
+ << delay.InMicroseconds() << " usec.";
+ }
+
+ CHECK_LE(base::TimeDelta(), delay);
+ const base::TimeTicks run_time = clock_->NowTicks() + delay;
+
+ // If there are one or more tasks with the exact same run time, schedule this
+ // task to occur after them. This mimics the FIFO ordering behavior when
+ // scheduling delayed tasks to be run via base::MessageLoop in a
+ // multi-threaded application.
+ if (!tasks_.empty()) {
+ const auto after_it = tasks_.lower_bound(
+ TaskKey(run_time + base::TimeDelta::FromMicroseconds(1), 0));
+ if (after_it != tasks_.begin()) {
+ auto it = after_it;
+ --it;
+ if (it->first.first == run_time) {
+ tasks_.insert(
+ after_it /* hint */,
+ std::make_pair(TaskKey(run_time, it->first.second + 1), task));
+ return true;
+ }
+ }
+ }
+
+ // No tasks have the exact same run time, so just do a simple insert.
+ tasks_.insert(std::make_pair(TaskKey(run_time, 0), task));
+ return true;
+}
+
+bool FakeSingleThreadTaskRunner::RunsTasksOnCurrentThread() const {
+ return true;
+}
+
+void FakeSingleThreadTaskRunner::RunTasks() {
+ while (true) {
+ // Run all tasks equal or older than current time.
+ const auto it = tasks_.begin();
+ if (it == tasks_.end())
+ return; // No more tasks.
+
+ if (clock_->NowTicks() < it->first.first)
+ return;
+
+ const base::Closure task = it->second;
+ tasks_.erase(it);
+ task.Run();
+ }
+}
+
+void FakeSingleThreadTaskRunner::Sleep(base::TimeDelta t) {
+ CHECK_LE(base::TimeDelta(), t);
+ const base::TimeTicks run_until = clock_->NowTicks() + t;
+
+ while (1) {
+ // Run up to 100000 tasks that were scheduled to run during the sleep
+ // period. 100000 should be enough for everybody (see comments below).
+ for (int i = 0; i < 100000; i++) {
+ const auto it = tasks_.begin();
+ if (it == tasks_.end() || run_until < it->first.first) {
+ clock_->Advance(run_until - clock_->NowTicks());
+ return;
+ }
+
+ clock_->Advance(it->first.first - clock_->NowTicks());
+ const base::Closure task = it->second;
+ tasks_.erase(it);
+ task.Run();
+ }
+
+ // If this point is reached, there's likely some sort of case where a new
+ // non-delayed task is being posted every time a task is popped and invoked
+ // from the queue. If that happens, set fail_on_next_task_ to true and throw
+ // an error when the next task is posted, where we might be able to identify
+ // the caller causing the problem via logging.
+ fail_on_next_task_ = true;
+ }
+}
+
+bool FakeSingleThreadTaskRunner::PostNonNestableDelayedTask(
+ const tracked_objects::Location& from_here,
+ const base::Closure& task,
+ base::TimeDelta delay) {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+} // namespace media
diff --git a/chromium/media/base/fake_single_thread_task_runner.h b/chromium/media/base/fake_single_thread_task_runner.h
new file mode 100644
index 00000000000..a9be9736fd7
--- /dev/null
+++ b/chromium/media/base/fake_single_thread_task_runner.h
@@ -0,0 +1,61 @@
+// Copyright 2014 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_CAST_TEST_FAKE_TASK_RUNNER_H_
+#define MEDIA_CAST_TEST_FAKE_TASK_RUNNER_H_
+
+#include <map>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/single_thread_task_runner.h"
+#include "base/test/simple_test_tick_clock.h"
+
+namespace media {
+
+class FakeSingleThreadTaskRunner : public base::SingleThreadTaskRunner {
+ public:
+ explicit FakeSingleThreadTaskRunner(base::SimpleTestTickClock* clock);
+
+ void RunTasks();
+
+ // Note: Advances |clock_|.
+ void Sleep(base::TimeDelta t);
+
+ // base::SingleThreadTaskRunner implementation.
+ bool PostDelayedTask(const tracked_objects::Location& from_here,
+ const base::Closure& task,
+ base::TimeDelta delay) final;
+
+ bool RunsTasksOnCurrentThread() const final;
+
+ // This function is currently not used, and will return false.
+ bool PostNonNestableDelayedTask(const tracked_objects::Location& from_here,
+ const base::Closure& task,
+ base::TimeDelta delay) final;
+
+ protected:
+ ~FakeSingleThreadTaskRunner() final;
+
+ private:
+ base::SimpleTestTickClock* const clock_;
+
+ // A compound key is used to ensure FIFO execution of delayed tasks scheduled
+ // for the same point-in-time. The second part of the key is simply a FIFO
+ // sequence number.
+ using TaskKey = std::pair<base::TimeTicks, unsigned int>;
+
+ // Note: The std::map data structure was chosen because the entire
+ // cast_unittests suite performed 20% faster than when using
+ // std::priority_queue. http://crbug.com/530842
+ std::map<TaskKey, base::Closure> tasks_;
+
+ bool fail_on_next_task_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeSingleThreadTaskRunner);
+};
+
+} // namespace media
+
+#endif // MEDIA_CAST_TEST_FAKE_TASK_RUNNER_H_
diff --git a/chromium/media/base/key_system_info.cc b/chromium/media/base/key_system_info.cc
index f36104ab120..da3586d7f4a 100644
--- a/chromium/media/base/key_system_info.cc
+++ b/chromium/media/base/key_system_info.cc
@@ -9,6 +9,8 @@ namespace media {
KeySystemInfo::KeySystemInfo() {
}
+KeySystemInfo::KeySystemInfo(const KeySystemInfo& other) = default;
+
KeySystemInfo::~KeySystemInfo() {
}
diff --git a/chromium/media/base/key_system_info.h b/chromium/media/base/key_system_info.h
index d349452b885..fe4dae7a418 100644
--- a/chromium/media/base/key_system_info.h
+++ b/chromium/media/base/key_system_info.h
@@ -21,10 +21,6 @@
// * Abstract key system
// A key system string that cannot be instantiated like a concrete key system
// but is otherwise useful, such as in discovery using isTypeSupported().
-// * Parent key system
-// A key system string that is one level up from the child key system. It may
-// be an abstract key system.
-// As an example, "com.example" is the parent of "com.example.foo".
namespace media {
@@ -32,6 +28,7 @@ namespace media {
// the corresponding CDM.
struct MEDIA_EXPORT KeySystemInfo {
KeySystemInfo();
+ KeySystemInfo(const KeySystemInfo& other);
~KeySystemInfo();
std::string key_system;
@@ -50,11 +47,6 @@ struct MEDIA_EXPORT KeySystemInfo {
EmeFeatureSupport persistent_state_support = EmeFeatureSupport::INVALID;
EmeFeatureSupport distinctive_identifier_support = EmeFeatureSupport::INVALID;
- // A hierarchical parent for |key_system|. This value can be used to check
- // supported types but cannot be used to instantiate a MediaKeys object.
- // Only one parent key system is currently supported per concrete key system.
- std::string parent_key_system;
-
// The following indicate how the corresponding CDM should be instantiated.
bool use_aes_decryptor = false;
#if defined(ENABLE_PEPPER_CDMS)
diff --git a/chromium/media/base/key_systems.cc b/chromium/media/base/key_systems.cc
index 6c2c8c2db31..8a3ae69c853 100644
--- a/chromium/media/base/key_systems.cc
+++ b/chromium/media/base/key_systems.cc
@@ -15,7 +15,7 @@
#include "base/time/time.h"
#include "build/build_config.h"
#include "media/base/key_system_info.h"
-#include "media/base/key_systems_support_uma.h"
+#include "media/base/media.h"
#include "media/base/media_client.h"
#include "media/cdm/key_system_names.h"
#include "third_party/widevine/cdm/widevine_cdm_common.h"
@@ -23,8 +23,6 @@
namespace media {
const char kClearKeyKeySystem[] = "org.w3.clearkey";
-const char kPrefixedClearKeyKeySystem[] = "webkit-org.w3.clearkey";
-const char kUnsupportedClearKeyKeySystem[] = "unsupported-org.w3.clearkey";
// These names are used by UMA. Do not change them!
const char kClearKeyKeySystemNameForUMA[] = "ClearKey";
@@ -36,31 +34,29 @@ struct NamedCodec {
};
// Mapping between containers and their codecs.
-// Only audio codec can belong to a "audio/*" container. Both audio and video
-// codecs can belong to a "video/*" container.
-// TODO(sandersd): This definition only makes sense for prefixed EME. Change it
-// when prefixed EME is removed. http://crbug.com/249976
-static NamedCodec kContainerToCodecMasks[] = {
+// Only audio codecs can belong to a "audio/*" mime_type, and only video codecs
+// can belong to a "video/*" mime_type.
+static const NamedCodec kMimeTypeToCodecMasks[] = {
{"audio/webm", EME_CODEC_WEBM_AUDIO_ALL},
- {"video/webm", EME_CODEC_WEBM_ALL},
+ {"video/webm", EME_CODEC_WEBM_VIDEO_ALL},
#if defined(USE_PROPRIETARY_CODECS)
{"audio/mp4", EME_CODEC_MP4_AUDIO_ALL},
- {"video/mp4", EME_CODEC_MP4_ALL}
+ {"video/mp4", EME_CODEC_MP4_VIDEO_ALL}
#endif // defined(USE_PROPRIETARY_CODECS)
};
// Mapping between codec names and enum values.
-static NamedCodec kCodecStrings[] = {
- {"opus", EME_CODEC_WEBM_OPUS},
- {"vorbis", EME_CODEC_WEBM_VORBIS},
- {"vp8", EME_CODEC_WEBM_VP8},
- {"vp8.0", EME_CODEC_WEBM_VP8},
- {"vp9", EME_CODEC_WEBM_VP9},
- {"vp9.0", EME_CODEC_WEBM_VP9},
+static const NamedCodec kCodecStrings[] = {
+ {"opus", EME_CODEC_WEBM_OPUS}, // Opus.
+ {"vorbis", EME_CODEC_WEBM_VORBIS}, // Vorbis.
+ {"vp8", EME_CODEC_WEBM_VP8}, // VP8.
+ {"vp8.0", EME_CODEC_WEBM_VP8}, // VP8.
+ {"vp9", EME_CODEC_WEBM_VP9}, // VP9.
+ {"vp9.0", EME_CODEC_WEBM_VP9}, // VP9.
#if defined(USE_PROPRIETARY_CODECS)
- {"mp4a", EME_CODEC_MP4_AAC},
- {"avc1", EME_CODEC_MP4_AVC1},
- {"avc3", EME_CODEC_MP4_AVC1}
+ {"mp4a", EME_CODEC_MP4_AAC}, // AAC.
+ {"avc1", EME_CODEC_MP4_AVC1}, // AVC1.
+ {"avc3", EME_CODEC_MP4_AVC1} // AVC3.
#endif // defined(USE_PROPRIETARY_CODECS)
};
@@ -80,7 +76,7 @@ static EmeRobustness ConvertRobustness(const std::string& robustness) {
return EmeRobustness::INVALID;
}
-static void AddClearKey(std::vector<KeySystemInfo>* concrete_key_systems) {
+static void AddClearKey(std::vector<KeySystemInfo>* key_systems) {
KeySystemInfo info;
info.key_system = kClearKeyKeySystem;
@@ -117,7 +113,7 @@ static void AddClearKey(std::vector<KeySystemInfo>* concrete_key_systems) {
info.use_aes_decryptor = true;
- concrete_key_systems->push_back(info);
+ key_systems->push_back(info);
}
// Returns whether the |key_system| is known to Chromium and is thus likely to
@@ -157,7 +153,7 @@ static bool IsPotentiallySupportedKeySystem(const std::string& key_system) {
// Chromecast defines behaviors for Cast clients within its reverse domain.
const char kChromecastRoot[] = "com.chromecast";
- if (IsParentKeySystemOf(kChromecastRoot, key_system))
+ if (IsChildKeySystemOf(key_system, kChromecastRoot))
return true;
// Implementations that do not have a specification or appropriate glue code
@@ -176,25 +172,19 @@ class KeySystemsImpl : public KeySystems {
void UpdateIfNeeded();
- bool IsConcreteSupportedKeySystem(const std::string& key_system) const;
-
- bool PrefixedIsSupportedKeySystemWithMediaMimeType(
- const std::string& mime_type,
- const std::vector<std::string>& codecs,
- const std::string& key_system);
-
std::string GetKeySystemNameForUMA(const std::string& key_system) const;
- bool UseAesDecryptor(const std::string& concrete_key_system) const;
+ bool UseAesDecryptor(const std::string& key_system) const;
#if defined(ENABLE_PEPPER_CDMS)
- std::string GetPepperType(const std::string& concrete_key_system) const;
+ std::string GetPepperType(const std::string& key_system) const;
#endif
- void AddContainerMask(const std::string& container, uint32_t mask);
+ // These two functions are for testing purpose only.
void AddCodecMask(EmeMediaType media_type,
const std::string& codec,
uint32_t mask);
+ void AddMimeTypeCodecMask(const std::string& mime_type, uint32_t mask);
// Implementation of KeySystems interface.
bool IsSupportedKeySystem(const std::string& key_system) const override;
@@ -233,50 +223,32 @@ class KeySystemsImpl : public KeySystems {
void UpdateSupportedKeySystems();
- void AddConcreteSupportedKeySystems(
- const std::vector<KeySystemInfo>& concrete_key_systems);
+ void AddSupportedKeySystems(const std::vector<KeySystemInfo>& key_systems);
+
+ void RegisterMimeType(const std::string& mime_type, EmeCodec codecs_mask);
+ bool IsValidMimeTypeCodecsCombination(const std::string& mime_type,
+ SupportedCodecs codecs_mask) const;
friend struct base::DefaultLazyInstanceTraits<KeySystemsImpl>;
typedef base::hash_map<std::string, KeySystemInfo> KeySystemInfoMap;
- typedef base::hash_map<std::string, std::string> ParentKeySystemMap;
- typedef base::hash_map<std::string, SupportedCodecs> ContainerCodecsMap;
+ typedef base::hash_map<std::string, SupportedCodecs> MimeTypeCodecsMap;
typedef base::hash_map<std::string, EmeCodec> CodecsMap;
typedef base::hash_map<std::string, EmeInitDataType> InitDataTypesMap;
typedef base::hash_map<std::string, std::string> KeySystemNameForUMAMap;
// TODO(sandersd): Separate container enum from codec mask value.
// http://crbug.com/417440
- SupportedCodecs GetCodecMaskForContainer(
- const std::string& container) const;
+ // Potentially pass EmeMediaType and a container enum.
+ SupportedCodecs GetCodecMaskForMimeType(
+ const std::string& container_mime_type) const;
EmeCodec GetCodecForString(const std::string& codec) const;
- const std::string& PrefixedGetConcreteKeySystemNameFor(
- const std::string& key_system) const;
-
- // Returns whether a |container| type is supported by checking
- // |key_system_supported_codecs|.
- // TODO(xhwang): Update this to actually check initDataType support.
- bool IsSupportedContainer(const std::string& container,
- SupportedCodecs key_system_supported_codecs) const;
-
- // Returns true if all |codecs| are supported in |container| by checking
- // |key_system_supported_codecs|.
- bool IsSupportedContainerAndCodecs(
- const std::string& container,
- const std::vector<std::string>& codecs,
- SupportedCodecs key_system_supported_codecs) const;
-
// Map from key system string to capabilities.
- KeySystemInfoMap concrete_key_system_map_;
+ KeySystemInfoMap key_system_map_;
- // Map from parent key system to the concrete key system that should be used
- // to represent its capabilities.
- ParentKeySystemMap parent_key_system_map_;
-
- KeySystemsSupportUMA key_systems_support_uma_;
-
- ContainerCodecsMap container_to_codec_mask_map_;
+ // This member should only be modified by RegisterMimeType().
+ MimeTypeCodecsMap mime_type_to_codec_mask_map_;
CodecsMap codec_string_map_;
KeySystemNameForUMAMap key_system_name_for_uma_map_;
@@ -303,16 +275,15 @@ KeySystemsImpl* KeySystemsImpl::GetInstance() {
KeySystemsImpl::KeySystemsImpl() :
audio_codec_mask_(EME_CODEC_AUDIO_ALL),
video_codec_mask_(EME_CODEC_VIDEO_ALL) {
- for (size_t i = 0; i < arraysize(kContainerToCodecMasks); ++i) {
- const std::string& name = kContainerToCodecMasks[i].name;
- DCHECK(!container_to_codec_mask_map_.count(name));
- container_to_codec_mask_map_[name] = kContainerToCodecMasks[i].type;
- }
for (size_t i = 0; i < arraysize(kCodecStrings); ++i) {
const std::string& name = kCodecStrings[i].name;
DCHECK(!codec_string_map_.count(name));
codec_string_map_[name] = kCodecStrings[i].type;
}
+ for (size_t i = 0; i < arraysize(kMimeTypeToCodecMasks); ++i) {
+ RegisterMimeType(kMimeTypeToCodecMasks[i].name,
+ kMimeTypeToCodecMasks[i].type);
+ }
InitializeUMAInfo();
@@ -323,13 +294,15 @@ KeySystemsImpl::KeySystemsImpl() :
KeySystemsImpl::~KeySystemsImpl() {
}
-SupportedCodecs KeySystemsImpl::GetCodecMaskForContainer(
- const std::string& container) const {
- ContainerCodecsMap::const_iterator iter =
- container_to_codec_mask_map_.find(container);
- if (iter != container_to_codec_mask_map_.end())
- return iter->second;
- return EME_CODEC_NONE;
+SupportedCodecs KeySystemsImpl::GetCodecMaskForMimeType(
+ const std::string& container_mime_type) const {
+ MimeTypeCodecsMap::const_iterator iter =
+ mime_type_to_codec_mask_map_.find(container_mime_type);
+ if (iter == mime_type_to_codec_mask_map_.end())
+ return EME_CODEC_NONE;
+
+ DCHECK(IsValidMimeTypeCodecsCombination(container_mime_type, iter->second));
+ return iter->second;
}
EmeCodec KeySystemsImpl::GetCodecForString(const std::string& codec) const {
@@ -339,15 +312,6 @@ EmeCodec KeySystemsImpl::GetCodecForString(const std::string& codec) const {
return EME_CODEC_NONE;
}
-const std::string& KeySystemsImpl::PrefixedGetConcreteKeySystemNameFor(
- const std::string& key_system) const {
- ParentKeySystemMap::const_iterator iter =
- parent_key_system_map_.find(key_system);
- if (iter != parent_key_system_map_.end())
- return iter->second;
- return key_system;
-}
-
void KeySystemsImpl::InitializeUMAInfo() {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(key_system_name_for_uma_map_.empty());
@@ -359,8 +323,6 @@ void KeySystemsImpl::InitializeUMAInfo() {
for (const KeySystemInfoForUMA& info : key_systems_info_for_uma) {
key_system_name_for_uma_map_[info.key_system] =
info.key_system_name_for_uma;
- if (info.reports_key_system_support_to_uma)
- key_systems_support_uma_.AddKeySystemToReport(info.key_system);
}
// Clear Key is always supported.
@@ -375,8 +337,7 @@ void KeySystemsImpl::UpdateIfNeeded() {
void KeySystemsImpl::UpdateSupportedKeySystems() {
DCHECK(thread_checker_.CalledOnValidThread());
- concrete_key_system_map_.clear();
- parent_key_system_map_.clear();
+ key_system_map_.clear();
// Build KeySystemInfo.
std::vector<KeySystemInfo> key_systems_info;
@@ -388,16 +349,15 @@ void KeySystemsImpl::UpdateSupportedKeySystems() {
// Clear Key is always supported.
AddClearKey(&key_systems_info);
- AddConcreteSupportedKeySystems(key_systems_info);
+ AddSupportedKeySystems(key_systems_info);
}
-void KeySystemsImpl::AddConcreteSupportedKeySystems(
- const std::vector<KeySystemInfo>& concrete_key_systems) {
+void KeySystemsImpl::AddSupportedKeySystems(
+ const std::vector<KeySystemInfo>& key_systems) {
DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK(concrete_key_system_map_.empty());
- DCHECK(parent_key_system_map_.empty());
+ DCHECK(key_system_map_.empty());
- for (const KeySystemInfo& info : concrete_key_systems) {
+ for (const KeySystemInfo& info : key_systems) {
DCHECK(!info.key_system.empty());
DCHECK(info.max_audio_robustness != EmeRobustness::INVALID);
DCHECK(info.max_video_robustness != EmeRobustness::INVALID);
@@ -448,77 +408,48 @@ void KeySystemsImpl::AddConcreteSupportedKeySystems(
EmeFeatureSupport::ALWAYS_ENABLED);
}
- DCHECK(!IsConcreteSupportedKeySystem(info.key_system))
+ DCHECK_EQ(key_system_map_.count(info.key_system), 0u)
<< "Key system '" << info.key_system << "' already registered";
- DCHECK(!parent_key_system_map_.count(info.key_system))
- << "'" << info.key_system << "' is already registered as a parent";
- concrete_key_system_map_[info.key_system] = info;
- if (!info.parent_key_system.empty()) {
- DCHECK(!IsConcreteSupportedKeySystem(info.parent_key_system))
- << "Parent '" << info.parent_key_system << "' "
- << "already registered concrete";
- DCHECK(!parent_key_system_map_.count(info.parent_key_system))
- << "Parent '" << info.parent_key_system << "' already registered";
- parent_key_system_map_[info.parent_key_system] = info.key_system;
+
+#if defined(OS_ANDROID)
+ // Ensure that the renderer can access the decoders necessary to use the
+ // key system.
+ if (!info.use_aes_decryptor && !ArePlatformDecodersAvailable()) {
+ DLOG(WARNING) << info.key_system << " not registered";
+ continue;
}
+#endif // defined(OS_ANDROID)
+
+ key_system_map_[info.key_system] = info;
}
}
-bool KeySystemsImpl::IsConcreteSupportedKeySystem(
- const std::string& key_system) const {
+// Adds the MIME type with the codec mask after verifying the validity.
+// Only this function should modify |mime_type_to_codec_mask_map_|.
+void KeySystemsImpl::RegisterMimeType(const std::string& mime_type,
+ EmeCodec codecs_mask) {
DCHECK(thread_checker_.CalledOnValidThread());
- return concrete_key_system_map_.count(key_system) != 0;
+ DCHECK(!mime_type_to_codec_mask_map_.count(mime_type));
+ DCHECK(IsValidMimeTypeCodecsCombination(mime_type, codecs_mask));
+
+ mime_type_to_codec_mask_map_[mime_type] = static_cast<EmeCodec>(codecs_mask);
}
-bool KeySystemsImpl::IsSupportedContainer(
- const std::string& container,
- SupportedCodecs key_system_supported_codecs) const {
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK(!container.empty());
-
- // When checking container support for EME, "audio/foo" should be treated the
- // same as "video/foo". Convert the |container| to achieve this.
- // TODO(xhwang): Replace this with real checks against supported initDataTypes
- // combined with supported demuxers.
- std::string canonical_container = container;
- if (container.find("audio/") == 0)
- canonical_container.replace(0, 6, "video/");
-
- // A container is supported iif at least one codec in that container is
- // supported.
- SupportedCodecs supported_codecs =
- GetCodecMaskForContainer(canonical_container);
- return (supported_codecs & key_system_supported_codecs) != 0;
-}
-
-bool KeySystemsImpl::IsSupportedContainerAndCodecs(
- const std::string& container,
- const std::vector<std::string>& codecs,
- SupportedCodecs key_system_supported_codecs) const {
+// Returns whether |mime_type| follows a valid format and the specified codecs
+// are of the correct type based on |*_codec_mask_|.
+// Only audio/ or video/ MIME types with their respective codecs are allowed.
+bool KeySystemsImpl::IsValidMimeTypeCodecsCombination(
+ const std::string& mime_type,
+ SupportedCodecs codecs_mask) const {
DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK(!container.empty());
- DCHECK(!codecs.empty());
- DCHECK(IsSupportedContainer(container, key_system_supported_codecs));
-
- SupportedCodecs container_supported_codecs =
- GetCodecMaskForContainer(container);
-
- for (size_t i = 0; i < codecs.size(); ++i) {
- if (codecs[i].empty())
- continue;
-
- EmeCodec codec = GetCodecForString(codecs[i]);
-
- // Unsupported codec.
- if (!(codec & key_system_supported_codecs))
- return false;
-
- // Unsupported codec/container combination, e.g. "video/webm" and "avc1".
- if (!(codec & container_supported_codecs))
- return false;
- }
+ if (!codecs_mask)
+ return false;
+ if (base::StartsWith(mime_type, "audio/", base::CompareCase::SENSITIVE))
+ return !(codecs_mask & ~audio_codec_mask_);
+ if (base::StartsWith(mime_type, "video/", base::CompareCase::SENSITIVE))
+ return !(codecs_mask & ~video_codec_mask_);
- return true;
+ return false;
}
bool KeySystemsImpl::IsSupportedInitDataType(
@@ -526,10 +457,9 @@ bool KeySystemsImpl::IsSupportedInitDataType(
EmeInitDataType init_data_type) const {
DCHECK(thread_checker_.CalledOnValidThread());
- // Locate |key_system|. Only concrete key systems are supported in unprefixed.
KeySystemInfoMap::const_iterator key_system_iter =
- concrete_key_system_map_.find(key_system);
- if (key_system_iter == concrete_key_system_map_.end()) {
+ key_system_map_.find(key_system);
+ if (key_system_iter == key_system_map_.end()) {
NOTREACHED();
return false;
}
@@ -551,49 +481,6 @@ bool KeySystemsImpl::IsSupportedInitDataType(
return false;
}
-bool KeySystemsImpl::PrefixedIsSupportedKeySystemWithMediaMimeType(
- const std::string& mime_type,
- const std::vector<std::string>& codecs,
- const std::string& key_system) {
- DCHECK(thread_checker_.CalledOnValidThread());
-
- const std::string& concrete_key_system =
- PrefixedGetConcreteKeySystemNameFor(key_system);
-
- bool has_type = !mime_type.empty();
-
- key_systems_support_uma_.ReportKeySystemQuery(key_system, has_type);
-
- // Check key system support.
- KeySystemInfoMap::const_iterator key_system_iter =
- concrete_key_system_map_.find(concrete_key_system);
- if (key_system_iter == concrete_key_system_map_.end())
- return false;
-
- key_systems_support_uma_.ReportKeySystemSupport(key_system, false);
-
- if (!has_type) {
- DCHECK(codecs.empty());
- return true;
- }
-
- SupportedCodecs key_system_supported_codecs =
- key_system_iter->second.supported_codecs;
-
- if (!IsSupportedContainer(mime_type, key_system_supported_codecs))
- return false;
-
- if (!codecs.empty() &&
- !IsSupportedContainerAndCodecs(
- mime_type, codecs, key_system_supported_codecs)) {
- return false;
- }
-
- key_systems_support_uma_.ReportKeySystemSupport(key_system, true);
-
- return true;
-}
-
std::string KeySystemsImpl::GetKeySystemNameForUMA(
const std::string& key_system) const {
DCHECK(thread_checker_.CalledOnValidThread());
@@ -606,14 +493,13 @@ std::string KeySystemsImpl::GetKeySystemNameForUMA(
return iter->second;
}
-bool KeySystemsImpl::UseAesDecryptor(
- const std::string& concrete_key_system) const {
+bool KeySystemsImpl::UseAesDecryptor(const std::string& key_system) const {
DCHECK(thread_checker_.CalledOnValidThread());
KeySystemInfoMap::const_iterator key_system_iter =
- concrete_key_system_map_.find(concrete_key_system);
- if (key_system_iter == concrete_key_system_map_.end()) {
- DLOG(ERROR) << concrete_key_system << " is not a known concrete system";
+ key_system_map_.find(key_system);
+ if (key_system_iter == key_system_map_.end()) {
+ DLOG(ERROR) << key_system << " is not a known system";
return false;
}
@@ -621,30 +507,22 @@ bool KeySystemsImpl::UseAesDecryptor(
}
#if defined(ENABLE_PEPPER_CDMS)
-std::string KeySystemsImpl::GetPepperType(
- const std::string& concrete_key_system) const {
+std::string KeySystemsImpl::GetPepperType(const std::string& key_system) const {
DCHECK(thread_checker_.CalledOnValidThread());
KeySystemInfoMap::const_iterator key_system_iter =
- concrete_key_system_map_.find(concrete_key_system);
- if (key_system_iter == concrete_key_system_map_.end()) {
- DLOG(FATAL) << concrete_key_system << " is not a known concrete system";
- return std::string();
+ key_system_map_.find(key_system);
+ if (key_system_iter == key_system_map_.end()) {
+ DLOG(FATAL) << key_system << " is not a known system";
+ return std::string();
}
const std::string& type = key_system_iter->second.pepper_type;
- DLOG_IF(FATAL, type.empty()) << concrete_key_system << " is not Pepper-based";
+ DLOG_IF(FATAL, type.empty()) << key_system << " is not Pepper-based";
return type;
}
#endif
-void KeySystemsImpl::AddContainerMask(const std::string& container,
- uint32_t mask) {
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK(!container_to_codec_mask_map_.count(container));
- container_to_codec_mask_map_[container] = static_cast<EmeCodec>(mask);
-}
-
void KeySystemsImpl::AddCodecMask(EmeMediaType media_type,
const std::string& codec,
uint32_t mask) {
@@ -658,10 +536,15 @@ void KeySystemsImpl::AddCodecMask(EmeMediaType media_type,
}
}
+void KeySystemsImpl::AddMimeTypeCodecMask(const std::string& mime_type,
+ uint32_t codecs_mask) {
+ RegisterMimeType(mime_type, static_cast<EmeCodec>(codecs_mask));
+}
+
bool KeySystemsImpl::IsSupportedKeySystem(const std::string& key_system) const {
DCHECK(thread_checker_.CalledOnValidThread());
- if (!IsConcreteSupportedKeySystem(key_system))
+ if (!key_system_map_.count(key_system))
return false;
// TODO(ddorwin): Move this to where we add key systems when prefixed EME is
@@ -683,27 +566,24 @@ EmeConfigRule KeySystemsImpl::GetContentTypeConfigRule(
const std::vector<std::string>& codecs) const {
DCHECK(thread_checker_.CalledOnValidThread());
- // Make sure the container matches |media_type|.
- SupportedCodecs media_type_codec_mask = EME_CODEC_NONE;
+ // Make sure the container MIME type matches |media_type|.
switch (media_type) {
case EmeMediaType::AUDIO:
if (!base::StartsWith(container_mime_type, "audio/",
base::CompareCase::SENSITIVE))
return EmeConfigRule::NOT_SUPPORTED;
- media_type_codec_mask = audio_codec_mask_;
break;
case EmeMediaType::VIDEO:
if (!base::StartsWith(container_mime_type, "video/",
base::CompareCase::SENSITIVE))
return EmeConfigRule::NOT_SUPPORTED;
- media_type_codec_mask = video_codec_mask_;
break;
}
// Look up the key system's supported codecs.
KeySystemInfoMap::const_iterator key_system_iter =
- concrete_key_system_map_.find(key_system);
- if (key_system_iter == concrete_key_system_map_.end()) {
+ key_system_map_.find(key_system);
+ if (key_system_iter == key_system_map_.end()) {
NOTREACHED();
return EmeConfigRule::NOT_SUPPORTED;
}
@@ -717,16 +597,16 @@ EmeConfigRule KeySystemsImpl::GetContentTypeConfigRule(
// Check that the container is supported by the key system. (This check is
// necessary because |codecs| may be empty.)
- SupportedCodecs container_codec_mask =
- GetCodecMaskForContainer(container_mime_type) & media_type_codec_mask;
- if ((key_system_codec_mask & container_codec_mask) == 0)
+ SupportedCodecs mime_type_codec_mask =
+ GetCodecMaskForMimeType(container_mime_type);
+ if ((key_system_codec_mask & mime_type_codec_mask) == 0)
return EmeConfigRule::NOT_SUPPORTED;
// Check that the codecs are supported by the key system and container.
EmeConfigRule support = EmeConfigRule::SUPPORTED;
for (size_t i = 0; i < codecs.size(); i++) {
SupportedCodecs codec = GetCodecForString(codecs[i]);
- if ((codec & key_system_codec_mask & container_codec_mask) == 0)
+ if ((codec & key_system_codec_mask & mime_type_codec_mask) == 0)
return EmeConfigRule::NOT_SUPPORTED;
#if defined(OS_ANDROID)
// Check whether the codec supports a hardware-secure mode. The goal is to
@@ -756,8 +636,8 @@ EmeConfigRule KeySystemsImpl::GetRobustnessConfigRule(
return EmeConfigRule::NOT_SUPPORTED;
KeySystemInfoMap::const_iterator key_system_iter =
- concrete_key_system_map_.find(key_system);
- if (key_system_iter == concrete_key_system_map_.end()) {
+ key_system_map_.find(key_system);
+ if (key_system_iter == key_system_map_.end()) {
NOTREACHED();
return EmeConfigRule::NOT_SUPPORTED;
}
@@ -821,8 +701,8 @@ EmeSessionTypeSupport KeySystemsImpl::GetPersistentLicenseSessionSupport(
DCHECK(thread_checker_.CalledOnValidThread());
KeySystemInfoMap::const_iterator key_system_iter =
- concrete_key_system_map_.find(key_system);
- if (key_system_iter == concrete_key_system_map_.end()) {
+ key_system_map_.find(key_system);
+ if (key_system_iter == key_system_map_.end()) {
NOTREACHED();
return EmeSessionTypeSupport::INVALID;
}
@@ -834,8 +714,8 @@ EmeSessionTypeSupport KeySystemsImpl::GetPersistentReleaseMessageSessionSupport(
DCHECK(thread_checker_.CalledOnValidThread());
KeySystemInfoMap::const_iterator key_system_iter =
- concrete_key_system_map_.find(key_system);
- if (key_system_iter == concrete_key_system_map_.end()) {
+ key_system_map_.find(key_system);
+ if (key_system_iter == key_system_map_.end()) {
NOTREACHED();
return EmeSessionTypeSupport::INVALID;
}
@@ -847,8 +727,8 @@ EmeFeatureSupport KeySystemsImpl::GetPersistentStateSupport(
DCHECK(thread_checker_.CalledOnValidThread());
KeySystemInfoMap::const_iterator key_system_iter =
- concrete_key_system_map_.find(key_system);
- if (key_system_iter == concrete_key_system_map_.end()) {
+ key_system_map_.find(key_system);
+ if (key_system_iter == key_system_map_.end()) {
NOTREACHED();
return EmeFeatureSupport::INVALID;
}
@@ -860,8 +740,8 @@ EmeFeatureSupport KeySystemsImpl::GetDistinctiveIdentifierSupport(
DCHECK(thread_checker_.CalledOnValidThread());
KeySystemInfoMap::const_iterator key_system_iter =
- concrete_key_system_map_.find(key_system);
- if (key_system_iter == concrete_key_system_map_.end()) {
+ key_system_map_.find(key_system);
+ if (key_system_iter == key_system_map_.end()) {
NOTREACHED();
return EmeFeatureSupport::INVALID;
}
@@ -874,56 +754,23 @@ KeySystems* KeySystems::GetInstance() {
//------------------------------------------------------------------------------
-std::string GetUnprefixedKeySystemName(const std::string& key_system) {
- if (key_system == kClearKeyKeySystem)
- return kUnsupportedClearKeyKeySystem;
-
- if (key_system == kPrefixedClearKeyKeySystem)
- return kClearKeyKeySystem;
-
- return key_system;
-}
-
-std::string GetPrefixedKeySystemName(const std::string& key_system) {
- DCHECK_NE(key_system, kPrefixedClearKeyKeySystem);
-
- if (key_system == kClearKeyKeySystem)
- return kPrefixedClearKeyKeySystem;
-
- return key_system;
-}
-
-bool PrefixedIsSupportedConcreteKeySystem(const std::string& key_system) {
- return KeySystemsImpl::GetInstance()->IsConcreteSupportedKeySystem(
- key_system);
-}
-
bool IsSupportedKeySystemWithInitDataType(const std::string& key_system,
EmeInitDataType init_data_type) {
return KeySystemsImpl::GetInstance()->IsSupportedInitDataType(key_system,
init_data_type);
}
-bool PrefixedIsSupportedKeySystemWithMediaMimeType(
- const std::string& mime_type,
- const std::vector<std::string>& codecs,
- const std::string& key_system) {
- return KeySystemsImpl::GetInstance()
- ->PrefixedIsSupportedKeySystemWithMediaMimeType(mime_type, codecs,
- key_system);
-}
-
std::string GetKeySystemNameForUMA(const std::string& key_system) {
return KeySystemsImpl::GetInstance()->GetKeySystemNameForUMA(key_system);
}
-bool CanUseAesDecryptor(const std::string& concrete_key_system) {
- return KeySystemsImpl::GetInstance()->UseAesDecryptor(concrete_key_system);
+bool CanUseAesDecryptor(const std::string& key_system) {
+ return KeySystemsImpl::GetInstance()->UseAesDecryptor(key_system);
}
#if defined(ENABLE_PEPPER_CDMS)
-std::string GetPepperType(const std::string& concrete_key_system) {
- return KeySystemsImpl::GetInstance()->GetPepperType(concrete_key_system);
+std::string GetPepperType(const std::string& key_system) {
+ return KeySystemsImpl::GetInstance()->GetPepperType(key_system);
}
#endif
@@ -933,15 +780,15 @@ std::string GetPepperType(const std::string& concrete_key_system) {
// "media" where "UNIT_TEST" is not defined. So we need to specify
// "MEDIA_EXPORT" here again so that they are visible to tests.
-MEDIA_EXPORT void AddContainerMask(const std::string& container,
- uint32_t mask) {
- KeySystemsImpl::GetInstance()->AddContainerMask(container, mask);
-}
-
MEDIA_EXPORT void AddCodecMask(EmeMediaType media_type,
const std::string& codec,
uint32_t mask) {
KeySystemsImpl::GetInstance()->AddCodecMask(media_type, codec, mask);
}
+MEDIA_EXPORT void AddMimeTypeCodecMask(const std::string& mime_type,
+ uint32_t mask) {
+ KeySystemsImpl::GetInstance()->AddMimeTypeCodecMask(mime_type, mask);
+}
+
} // namespace media
diff --git a/chromium/media/base/key_systems.h b/chromium/media/base/key_systems.h
index dcdd254842d..92c15d899ad 100644
--- a/chromium/media/base/key_systems.h
+++ b/chromium/media/base/key_systems.h
@@ -16,8 +16,7 @@
namespace media {
-// Provides an interface for querying registered key systems. The exposed API is
-// only intended to support unprefixed EME.
+// Provides an interface for querying registered key systems.
//
// Many of the original static methods are still available, they should be
// migrated into this interface over time (or removed).
@@ -71,58 +70,34 @@ class MEDIA_EXPORT KeySystems {
virtual ~KeySystems() {};
};
-// Prefixed EME API only supports prefixed (webkit-) key system name for
-// certain key systems. But internally only unprefixed key systems are
-// supported. The following two functions help convert between prefixed and
-// unprefixed key system names.
-
-// Gets the unprefixed key system name for |key_system|.
-MEDIA_EXPORT std::string GetUnprefixedKeySystemName(
- const std::string& key_system);
-
-// Gets the prefixed key system name for |key_system|.
-MEDIA_EXPORT std::string GetPrefixedKeySystemName(
- const std::string& key_system);
-
+// TODO(ddorwin): WebContentDecryptionModuleSessionImpl::initializeNewSession()
+// is violating this rule! https://crbug.com/249976.
// Use for prefixed EME only!
MEDIA_EXPORT bool IsSupportedKeySystemWithInitDataType(
const std::string& key_system,
EmeInitDataType init_data_type);
-// Use for prefixed EME only!
-// Returns whether |key_system| is a real supported key system that can be
-// instantiated.
-// Abstract parent |key_system| strings will return false.
-MEDIA_EXPORT bool PrefixedIsSupportedConcreteKeySystem(
- const std::string& key_system);
-
-// Use for prefixed EME only!
-// Returns whether |key_system| supports the specified media type and codec(s).
-// To be used with prefixed EME only as it generates UMAs based on the query.
-MEDIA_EXPORT bool PrefixedIsSupportedKeySystemWithMediaMimeType(
- const std::string& mime_type,
- const std::vector<std::string>& codecs,
- const std::string& key_system);
-
// Returns a name for |key_system| suitable to UMA logging.
MEDIA_EXPORT std::string GetKeySystemNameForUMA(const std::string& key_system);
-// Returns whether AesDecryptor can be used for the given |concrete_key_system|.
-MEDIA_EXPORT bool CanUseAesDecryptor(const std::string& concrete_key_system);
+// Returns whether AesDecryptor can be used for the given |key_system|.
+MEDIA_EXPORT bool CanUseAesDecryptor(const std::string& key_system);
#if defined(ENABLE_PEPPER_CDMS)
-// Returns the Pepper MIME type for |concrete_key_system|.
-// Returns empty string if |concrete_key_system| is unknown or not Pepper-based.
-MEDIA_EXPORT std::string GetPepperType(
- const std::string& concrete_key_system);
+// Returns the Pepper MIME type for |key_system|.
+// Returns empty string if |key_system| is unknown or not Pepper-based.
+MEDIA_EXPORT std::string GetPepperType(const std::string& key_system);
#endif
#if defined(UNIT_TEST)
// Helper functions to add container/codec types for testing purposes.
-MEDIA_EXPORT void AddContainerMask(const std::string& container, uint32_t mask);
+// Call AddCodecMask() first to ensure the mask values passed to
+// AddMimeTypeCodecMask() already exist.
MEDIA_EXPORT void AddCodecMask(EmeMediaType media_type,
const std::string& codec,
uint32_t mask);
+MEDIA_EXPORT void AddMimeTypeCodecMask(const std::string& mime_type,
+ uint32_t mask);
#endif // defined(UNIT_TEST)
} // namespace media
diff --git a/chromium/media/base/key_systems_support_uma.cc b/chromium/media/base/key_systems_support_uma.cc
deleted file mode 100644
index 7ac7a14b43e..00000000000
--- a/chromium/media/base/key_systems_support_uma.cc
+++ /dev/null
@@ -1,133 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "media/base/key_systems_support_uma.h"
-
-#include "base/metrics/histogram.h"
-#include "media/base/key_systems.h"
-
-namespace media {
-
-namespace {
-
-const char kKeySystemSupportUMAPrefix[] = "Media.EME.KeySystemSupport.";
-
-// These values are reported to UMA. Do not change the existing values!
-enum KeySystemSupportStatus {
- KEY_SYSTEM_QUERIED = 0,
- KEY_SYSTEM_SUPPORTED = 1,
- KEY_SYSTEM_WITH_TYPE_QUERIED = 2,
- KEY_SYSTEM_WITH_TYPE_SUPPORTED = 3,
- KEY_SYSTEM_SUPPORT_STATUS_COUNT
-};
-
-// Reports an event only once.
-class OneTimeReporter {
- public:
- OneTimeReporter(const std::string& key_system, KeySystemSupportStatus status);
- ~OneTimeReporter();
-
- void Report();
-
- private:
- bool is_reported_;
- const std::string key_system_;
- const KeySystemSupportStatus status_;
-};
-
-OneTimeReporter::OneTimeReporter(const std::string& key_system,
- KeySystemSupportStatus status)
- : is_reported_(false), key_system_(key_system), status_(status) {
-}
-
-OneTimeReporter::~OneTimeReporter() {}
-
-void OneTimeReporter::Report() {
- if (is_reported_)
- return;
-
- // Not using UMA_HISTOGRAM_ENUMERATION directly because UMA_* macros require
- // the names to be constant throughout the process' lifetime.
- base::LinearHistogram::FactoryGet(
- kKeySystemSupportUMAPrefix + GetKeySystemNameForUMA(key_system_), 1,
- KEY_SYSTEM_SUPPORT_STATUS_COUNT, KEY_SYSTEM_SUPPORT_STATUS_COUNT + 1,
- base::Histogram::kUmaTargetedHistogramFlag)->Add(status_);
-
- is_reported_ = true;
-}
-
-} // namespace
-
-class KeySystemsSupportUMA::Reporter {
- public:
- explicit Reporter(const std::string& key_system);
- ~Reporter();
-
- void Report(bool has_type, bool is_supported);
-
- private:
- const std::string key_system_;
-
- OneTimeReporter call_reporter_;
- OneTimeReporter call_with_type_reporter_;
- OneTimeReporter support_reporter_;
- OneTimeReporter support_with_type_reporter_;
-};
-
-KeySystemsSupportUMA::Reporter::Reporter(const std::string& key_system)
- : key_system_(key_system),
- call_reporter_(key_system, KEY_SYSTEM_QUERIED),
- call_with_type_reporter_(key_system, KEY_SYSTEM_WITH_TYPE_QUERIED),
- support_reporter_(key_system, KEY_SYSTEM_SUPPORTED),
- support_with_type_reporter_(key_system, KEY_SYSTEM_WITH_TYPE_SUPPORTED) {}
-
-KeySystemsSupportUMA::Reporter::~Reporter() {}
-
-void KeySystemsSupportUMA::Reporter::Report(bool has_type, bool is_supported) {
- call_reporter_.Report();
- if (has_type)
- call_with_type_reporter_.Report();
-
- if (!is_supported)
- return;
-
- support_reporter_.Report();
- if (has_type)
- support_with_type_reporter_.Report();
-}
-
-KeySystemsSupportUMA::KeySystemsSupportUMA() {}
-
-KeySystemsSupportUMA::~KeySystemsSupportUMA() {}
-
-void KeySystemsSupportUMA::AddKeySystemToReport(const std::string& key_system) {
- DCHECK(!GetReporter(key_system));
- reporters_.set(key_system, scoped_ptr<Reporter>(new Reporter(key_system)));
-}
-
-void KeySystemsSupportUMA::ReportKeySystemQuery(const std::string& key_system,
- bool has_type) {
- Reporter* reporter = GetReporter(key_system);
- if (!reporter)
- return;
- reporter->Report(has_type, false);
-}
-
-void KeySystemsSupportUMA::ReportKeySystemSupport(const std::string& key_system,
- bool has_type) {
- Reporter* reporter = GetReporter(key_system);
- if (!reporter)
- return;
- reporter->Report(has_type, true);
-}
-
-KeySystemsSupportUMA::Reporter* KeySystemsSupportUMA::GetReporter(
- const std::string& key_system) {
- Reporters::iterator reporter = reporters_.find(key_system);
- if (reporter == reporters_.end())
- return NULL;
- return reporter->second;
-}
-
-} // namespace media
diff --git a/chromium/media/base/key_systems_support_uma.h b/chromium/media/base/key_systems_support_uma.h
deleted file mode 100644
index 28f334db995..00000000000
--- a/chromium/media/base/key_systems_support_uma.h
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef MEDIA_BASE_KEY_SYSTEMS_SUPPORT_UMA_H_
-#define MEDIA_BASE_KEY_SYSTEMS_SUPPORT_UMA_H_
-
-#include <string>
-
-#include "base/containers/scoped_ptr_hash_map.h"
-
-namespace media {
-
-// Key system support UMA statistics for queried key systems.
-// 1. The key system is queried (with or without a MIME type).
-// 2. The key system is queried with a MIME type.
-// 3. The queried key system is supported (with or without a MIME type). This is
-// reported when the key system is supported when queried, regardless of
-// whether a MIME type is specified.
-// 4. The queried key system is supported with a MIME type. This is reported
-// when the key system is supported when queried without a MIME type
-// specified.
-// Note: All 4 stats are only reported once per renderer process per key system.
-class KeySystemsSupportUMA {
- public:
- KeySystemsSupportUMA();
- ~KeySystemsSupportUMA();
-
- // Adds a |key_system| for which query/support statistics are reported.
- // If you use this function to add key system to report, make sure to update
- // AddKeySystemSupportActions() in tools/metrics/actions/extract_actions.py.
- void AddKeySystemToReport(const std::string& key_system);
-
- // Reports that the |key_system| is queried. When |has_type|, also reports
- // that the |key_system| with a MIME type is queried.
- void ReportKeySystemQuery(const std::string& key_system, bool has_type);
-
- // Reports that the queried |key_system| is supported. When |has_type| (a
- // a MIME type is specified in the query), also reports that the queried
- // |key_system| is supported with that MIME type.
- void ReportKeySystemSupport(const std::string& key_system, bool has_type);
-
- private:
- class Reporter;
-
- // Returns the Reporter for |key_system|. Returns NULL if |key_system| was not
- // added for UMA reporting.
- Reporter* GetReporter(const std::string& key_system);
-
- // Key system <-> Reporter map.
- typedef base::ScopedPtrHashMap<std::string, scoped_ptr<Reporter>> Reporters;
- Reporters reporters_;
-};
-
-} // namespace media
-
-#endif // MEDIA_BASE_KEY_SYSTEMS_SUPPORT_UMA_H_
diff --git a/chromium/media/base/key_systems_unittest.cc b/chromium/media/base/key_systems_unittest.cc
index 12543946b0f..44b881939c9 100644
--- a/chromium/media/base/key_systems_unittest.cc
+++ b/chromium/media/base/key_systems_unittest.cc
@@ -16,6 +16,7 @@
#include "media/base/eme_constants.h"
#include "media/base/key_system_info.h"
#include "media/base/key_systems.h"
+#include "media/base/media.h"
#include "media/base/media_client.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -25,14 +26,11 @@ namespace media {
// kUsesAes uses the AesDecryptor like Clear Key.
// kExternal uses an external CDM, such as Pepper-based or Android platform CDM.
const char kUsesAes[] = "x-org.example.clear";
-const char kUsesAesParent[] = "x-org.example"; // Not registered.
const char kUseAesNameForUMA[] = "UseAes";
const char kExternal[] = "x-com.example.test";
-const char kExternalParent[] = "x-com.example";
const char kExternalNameForUMA[] = "External";
const char kClearKey[] = "org.w3.clearkey";
-const char kPrefixedClearKey[] = "webkit-org.w3.clearkey";
const char kExternalClearKey[] = "org.chromium.externalclearkey";
const char kAudioWebM[] = "audio/webm";
@@ -93,14 +91,26 @@ static void AddContainerAndCodecMasksForTest() {
if (is_test_masks_added)
return;
- AddContainerMask("audio/foo", TEST_CODEC_FOO_AUDIO_ALL);
- AddContainerMask("video/foo", TEST_CODEC_FOO_ALL);
AddCodecMask(EmeMediaType::AUDIO, "fooaudio", TEST_CODEC_FOO_AUDIO);
AddCodecMask(EmeMediaType::VIDEO, "foovideo", TEST_CODEC_FOO_VIDEO);
+ AddMimeTypeCodecMask("audio/foo", TEST_CODEC_FOO_AUDIO_ALL);
+ AddMimeTypeCodecMask("video/foo", TEST_CODEC_FOO_VIDEO_ALL);
is_test_masks_added = true;
}
+static bool CanRunExternalKeySystemTests() {
+#if defined(OS_ANDROID)
+ if (HasPlatformDecoderSupport())
+ return true;
+
+ EXPECT_FALSE(IsSupportedKeySystem(kExternal));
+ return false;
+#else
+ return true;
+#endif
+}
+
class TestMediaClient : public MediaClient {
public:
TestMediaClient();
@@ -143,9 +153,9 @@ TestMediaClient::~TestMediaClient() {
void TestMediaClient::AddKeySystemsInfoForUMA(
std::vector<KeySystemInfoForUMA>* key_systems_info_for_uma) {
key_systems_info_for_uma->push_back(
- media::KeySystemInfoForUMA(kUsesAes, kUseAesNameForUMA, false));
+ media::KeySystemInfoForUMA(kUsesAes, kUseAesNameForUMA));
key_systems_info_for_uma->push_back(
- media::KeySystemInfoForUMA(kExternal, kExternalNameForUMA, true));
+ media::KeySystemInfoForUMA(kExternal, kExternalNameForUMA));
}
bool TestMediaClient::IsKeySystemsUpdateNeeded() {
@@ -209,7 +219,6 @@ void TestMediaClient::AddExternalKeySystem(
ext.persistent_release_message_support = EmeSessionTypeSupport::NOT_SUPPORTED;
ext.persistent_state_support = EmeFeatureSupport::ALWAYS_ENABLED;
ext.distinctive_identifier_support = EmeFeatureSupport::ALWAYS_ENABLED;
- ext.parent_key_system = kExternalParent;
#if defined(ENABLE_PEPPER_CDMS)
ext.pepper_type = "application/x-ppapi-external-cdm";
#endif // defined(ENABLE_PEPPER_CDMS)
@@ -367,10 +376,6 @@ TEST_F(KeySystemsTest, ClearKey) {
kVideoWebM, no_codecs(), kClearKey));
EXPECT_EQ("ClearKey", GetKeySystemNameForUMA(kClearKey));
-
- // Prefixed Clear Key is not supported internally.
- EXPECT_FALSE(IsSupportedKeySystem(kPrefixedClearKey));
- EXPECT_EQ("Unknown", GetKeySystemNameForUMA(kPrefixedClearKey));
}
TEST_F(KeySystemsTest, ClearKeyWithInitDataType) {
@@ -396,9 +401,8 @@ TEST_F(KeySystemsTest, Basic_UnrecognizedKeySystem) {
#if defined(ENABLE_PEPPER_CDMS)
std::string type;
- EXPECT_DEBUG_DEATH(
- type = GetPepperType(kUnrecognized),
- "x-org.example.unrecognized is not a known concrete system");
+ EXPECT_DEBUG_DEATH(type = GetPepperType(kUnrecognized),
+ "x-org.example.unrecognized is not a known system");
EXPECT_TRUE(type.empty());
#endif
}
@@ -469,22 +473,6 @@ TEST_F(KeySystemsTest,
kAudioWebM, fooaudio_codec(), kUsesAes));
}
-// No parent is registered for UsesAes.
-TEST_F(KeySystemsTest, Parent_NoParentRegistered) {
- EXPECT_FALSE(IsSupportedKeySystem(kUsesAesParent));
-
- // The parent is not supported for most things.
- EXPECT_EQ("Unknown", GetKeySystemNameForUMA(kUsesAesParent));
- EXPECT_FALSE(CanUseAesDecryptor(kUsesAesParent));
-
-#if defined(ENABLE_PEPPER_CDMS)
- std::string type;
- EXPECT_DEBUG_DEATH(type = GetPepperType(kUsesAesParent),
- "x-org.example is not a known concrete system");
- EXPECT_TRUE(type.empty());
-#endif
-}
-
TEST_F(KeySystemsTest, IsSupportedKeySystem_InvalidVariants) {
// Case sensitive.
EXPECT_FALSE(IsSupportedKeySystem("x-org.example.ClEaR"));
@@ -495,7 +483,10 @@ TEST_F(KeySystemsTest, IsSupportedKeySystem_InvalidVariants) {
// Extra period.
EXPECT_FALSE(IsSupportedKeySystem("x-org.example.clear."));
+
+ // Prefix.
EXPECT_FALSE(IsSupportedKeySystem("x-org.example."));
+ EXPECT_FALSE(IsSupportedKeySystem("x-org.example"));
// Incomplete.
EXPECT_FALSE(IsSupportedKeySystem("x-org.example.clea"));
@@ -510,8 +501,6 @@ TEST_F(KeySystemsTest, IsSupportedKeySystem_InvalidVariants) {
TEST_F(KeySystemsTest, IsSupportedKeySystemWithMediaMimeType_NoType) {
EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
std::string(), no_codecs(), kUsesAes));
- EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
- std::string(), no_codecs(), kUsesAesParent));
EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(std::string(), no_codecs(),
"x-org.example.foo"));
@@ -574,6 +563,9 @@ TEST_F(KeySystemsTest,
//
TEST_F(KeySystemsTest, Basic_ExternalDecryptor) {
+ if (!CanRunExternalKeySystemTests())
+ return;
+
EXPECT_TRUE(IsSupportedKeySystem(kExternal));
EXPECT_TRUE(IsSupportedKeySystemWithMediaMimeType(
kVideoWebM, no_codecs(), kExternal));
@@ -584,27 +576,12 @@ TEST_F(KeySystemsTest, Basic_ExternalDecryptor) {
#endif // defined(ENABLE_PEPPER_CDMS)
}
-TEST_F(KeySystemsTest, Parent_ParentRegistered) {
- // Unprefixed has no parent key system support.
- EXPECT_FALSE(IsSupportedKeySystem(kExternalParent));
- EXPECT_TRUE(PrefixedIsSupportedKeySystemWithMediaMimeType(
- kVideoWebM, no_codecs(), kExternalParent));
-
- // The parent is not supported for most things.
- EXPECT_EQ("Unknown", GetKeySystemNameForUMA(kExternalParent));
- EXPECT_FALSE(CanUseAesDecryptor(kExternalParent));
-
-#if defined(ENABLE_PEPPER_CDMS)
- std::string type;
- EXPECT_DEBUG_DEATH(type = GetPepperType(kExternalParent),
- "x-com.example is not a known concrete system");
- EXPECT_TRUE(type.empty());
-#endif
-}
-
TEST_F(
KeySystemsTest,
IsSupportedKeySystemWithMediaMimeType_ExternalDecryptor_TypesContainer1) {
+ if (!CanRunExternalKeySystemTests())
+ return;
+
// Valid video types.
EXPECT_TRUE(IsSupportedKeySystemWithMediaMimeType(
kVideoWebM, no_codecs(), kExternal));
@@ -625,25 +602,6 @@ TEST_F(
EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
kVideoWebM, vorbis_codec(), kExternal));
- // Valid video types - parent key system.
- // Prefixed has parent key system support.
- EXPECT_TRUE(PrefixedIsSupportedKeySystemWithMediaMimeType(
- kVideoWebM, no_codecs(), kExternalParent));
- EXPECT_TRUE(PrefixedIsSupportedKeySystemWithMediaMimeType(
- kVideoWebM, vp8_codec(), kExternalParent));
- EXPECT_TRUE(PrefixedIsSupportedKeySystemWithMediaMimeType(
- kVideoWebM, vp80_codec(), kExternalParent));
- EXPECT_TRUE(PrefixedIsSupportedKeySystemWithMediaMimeType(
- kVideoWebM, vp8_and_vorbis_codecs(), kExternalParent));
- EXPECT_TRUE(PrefixedIsSupportedKeySystemWithMediaMimeType(
- kVideoWebM, vp9_codec(), kExternalParent));
- EXPECT_TRUE(PrefixedIsSupportedKeySystemWithMediaMimeType(
- kVideoWebM, vp90_codec(), kExternalParent));
- EXPECT_TRUE(PrefixedIsSupportedKeySystemWithMediaMimeType(
- kVideoWebM, vp9_and_vorbis_codecs(), kExternalParent));
- EXPECT_TRUE(PrefixedIsSupportedKeySystemWithMediaMimeType(
- kVideoWebM, vorbis_codec(), kExternalParent));
-
// Non-Webm codecs.
EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
kVideoWebM, foovideo_codec(), kExternal));
@@ -658,13 +616,6 @@ TEST_F(
EXPECT_TRUE(IsSupportedKeySystemWithAudioMimeType(
kAudioWebM, vorbis_codec(), kExternal));
- // Valid audio types - parent key system.
- // Prefixed has parent key system support.
- EXPECT_TRUE(PrefixedIsSupportedKeySystemWithMediaMimeType(
- kAudioWebM, no_codecs(), kExternalParent));
- EXPECT_TRUE(PrefixedIsSupportedKeySystemWithMediaMimeType(
- kAudioWebM, vorbis_codec(), kExternalParent));
-
// Non-audio codecs.
EXPECT_FALSE(IsSupportedKeySystemWithAudioMimeType(
kAudioWebM, vp8_codec(), kExternal));
@@ -683,6 +634,9 @@ TEST_F(
TEST_F(
KeySystemsTest,
IsSupportedKeySystemWithMediaMimeType_ExternalDecryptor_TypesContainer2) {
+ if (!CanRunExternalKeySystemTests())
+ return;
+
// Valid video types.
EXPECT_TRUE(IsSupportedKeySystemWithMediaMimeType(
kVideoFoo, no_codecs(), kExternal));
@@ -695,17 +649,6 @@ TEST_F(
EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
kVideoFoo, fooaudio_codec(), kExternal));
- // Valid video types - parent key system.
- // Prefixed has parent key system support.
- EXPECT_TRUE(PrefixedIsSupportedKeySystemWithMediaMimeType(
- kVideoFoo, no_codecs(), kExternalParent));
- EXPECT_TRUE(PrefixedIsSupportedKeySystemWithMediaMimeType(
- kVideoFoo, foovideo_codec(), kExternalParent));
- EXPECT_TRUE(PrefixedIsSupportedKeySystemWithMediaMimeType(
- kVideoFoo, foovideo_and_fooaudio_codecs(), kExternalParent));
- EXPECT_TRUE(PrefixedIsSupportedKeySystemWithMediaMimeType(
- kVideoFoo, fooaudio_codec(), kExternalParent));
-
// Extended codecs fail because this is handled by SimpleWebMimeRegistryImpl.
// They should really pass canPlayType().
EXPECT_FALSE(IsSupportedKeySystemWithMediaMimeType(
@@ -729,13 +672,6 @@ TEST_F(
EXPECT_TRUE(IsSupportedKeySystemWithAudioMimeType(
kAudioFoo, fooaudio_codec(), kExternal));
- // Valid audio types - parent key system.
- // Prefixed has parent key system support.
- EXPECT_TRUE(PrefixedIsSupportedKeySystemWithMediaMimeType(
- kAudioFoo, no_codecs(), kExternalParent));
- EXPECT_TRUE(PrefixedIsSupportedKeySystemWithMediaMimeType(
- kAudioFoo, fooaudio_codec(), kExternalParent));
-
// Non-audio codecs.
EXPECT_FALSE(IsSupportedKeySystemWithAudioMimeType(
kAudioFoo, foovideo_codec(), kExternal));
@@ -749,45 +685,30 @@ TEST_F(
TEST_F(KeySystemsTest, KeySystemNameForUMA) {
EXPECT_EQ("ClearKey", GetKeySystemNameForUMA(kClearKey));
- // Prefixed is not supported internally.
- EXPECT_EQ("Unknown", GetKeySystemNameForUMA(kPrefixedClearKey));
// External Clear Key never has a UMA name.
- EXPECT_EQ("Unknown", GetKeySystemNameForUMA(kExternalClearKey));
+ if (CanRunExternalKeySystemTests())
+ EXPECT_EQ("Unknown", GetKeySystemNameForUMA(kExternalClearKey));
}
TEST_F(KeySystemsTest, KeySystemsUpdate) {
EXPECT_TRUE(IsSupportedKeySystem(kUsesAes));
EXPECT_TRUE(IsSupportedKeySystemWithMediaMimeType(
kVideoWebM, no_codecs(), kUsesAes));
- EXPECT_TRUE(IsSupportedKeySystem(kExternal));
- EXPECT_TRUE(IsSupportedKeySystemWithMediaMimeType(
- kVideoWebM, no_codecs(), kExternal));
-
- UpdateClientKeySystems();
-
- EXPECT_TRUE(IsSupportedKeySystem(kUsesAes));
- EXPECT_TRUE(IsSupportedKeySystemWithMediaMimeType(
- kVideoWebM, no_codecs(), kUsesAes));
- EXPECT_FALSE(IsSupportedKeySystem(kExternal));
-}
-TEST_F(KeySystemsTest, PrefixedKeySystemsUpdate) {
- EXPECT_TRUE(IsSupportedKeySystem(kUsesAes));
- EXPECT_TRUE(PrefixedIsSupportedKeySystemWithMediaMimeType(
- kVideoWebM, no_codecs(), kUsesAes));
- EXPECT_TRUE(IsSupportedKeySystem(kExternal));
- EXPECT_TRUE(PrefixedIsSupportedKeySystemWithMediaMimeType(
- kVideoWebM, no_codecs(), kExternal));
+ if (CanRunExternalKeySystemTests()) {
+ EXPECT_TRUE(IsSupportedKeySystem(kExternal));
+ EXPECT_TRUE(IsSupportedKeySystemWithMediaMimeType(kVideoWebM, no_codecs(),
+ kExternal));
+ }
UpdateClientKeySystems();
EXPECT_TRUE(IsSupportedKeySystem(kUsesAes));
- EXPECT_TRUE(PrefixedIsSupportedKeySystemWithMediaMimeType(
+ EXPECT_TRUE(IsSupportedKeySystemWithMediaMimeType(
kVideoWebM, no_codecs(), kUsesAes));
- EXPECT_FALSE(IsSupportedKeySystem(kExternal));
- EXPECT_FALSE(PrefixedIsSupportedKeySystemWithMediaMimeType(
- kVideoWebM, no_codecs(), kExternal));
+ if (CanRunExternalKeySystemTests())
+ EXPECT_FALSE(IsSupportedKeySystem(kExternal));
}
TEST_F(KeySystemsPotentiallySupportedNamesTest, PotentiallySupportedNames) {
diff --git a/chromium/media/base/mac/BUILD.gn b/chromium/media/base/mac/BUILD.gn
index c1ebc9e64e7..95540d6890f 100644
--- a/chromium/media/base/mac/BUILD.gn
+++ b/chromium/media/base/mac/BUILD.gn
@@ -14,6 +14,8 @@ source_set("mac") {
"video_frame_mac.h",
"videotoolbox_glue.h",
"videotoolbox_glue.mm",
+ "videotoolbox_helpers.cc",
+ "videotoolbox_helpers.h",
]
if (is_mac) {
sources += [
@@ -21,6 +23,8 @@ source_set("mac") {
"avfoundation_glue.mm",
]
libs = [
+ "AVFoundation.framework",
+
# Required by video_frame_mac.cc.
"CoreVideo.framework",
]
diff --git a/chromium/media/base/mac/avfoundation_glue.h b/chromium/media/base/mac/avfoundation_glue.h
index fd744a3eeb7..a5b430412fd 100644
--- a/chromium/media/base/mac/avfoundation_glue.h
+++ b/chromium/media/base/mac/avfoundation_glue.h
@@ -28,10 +28,6 @@ class MEDIA_EXPORT AVFoundationGlue {
// AVFoundation methods.
static void InitializeAVFoundation();
- // This method returns true if the OS version supports AVFoundation and the
- // AVFoundation bundle could be loaded correctly, or false otherwise.
- static bool IsAVFoundationSupported();
-
#if defined(__OBJC__)
static NSBundle const* AVFoundationBundle();
diff --git a/chromium/media/base/mac/avfoundation_glue.mm b/chromium/media/base/mac/avfoundation_glue.mm
index 23f11775798..84b77347725 100644
--- a/chromium/media/base/mac/avfoundation_glue.mm
+++ b/chromium/media/base/mac/avfoundation_glue.mm
@@ -4,6 +4,7 @@
#import "media/base/mac/avfoundation_glue.h"
+#import <AVFoundation/AVFoundation.h>
#include <dlfcn.h>
#include <stddef.h>
@@ -15,26 +16,21 @@
#include "base/trace_event/trace_event.h"
#include "media/base/media_switches.h"
-namespace {
-
-// Used for logging capture API usage. Classes are a partition. Elements in this
-// enum should not be deleted or rearranged; the only permitted operation is to
-// add new elements before CAPTURE_API_MAX, that must be equal to the last item.
-enum CaptureApi {
- CAPTURE_API_QTKIT_DUE_TO_OS_PREVIOUS_TO_LION = 0,
- CAPTURE_API_QTKIT_FORCED_BY_FLAG = 1,
- CAPTURE_API_QTKIT_DUE_TO_NO_FLAG = 2,
- CAPTURE_API_QTKIT_DUE_TO_AVFOUNDATION_LOAD_ERROR = 3,
- CAPTURE_API_AVFOUNDATION_LOADED_OK = 4,
- CAPTURE_API_MAX = CAPTURE_API_AVFOUNDATION_LOADED_OK
-};
-
-void LogCaptureApi(CaptureApi api) {
- UMA_HISTOGRAM_ENUMERATION("Media.VideoCaptureApi.Mac",
- api,
- CAPTURE_API_MAX + 1);
-}
+// Forward declarations of AVFoundation.h strings.
+// This is needed to avoid compile time warnings since currently
+// |mac_deployment_target| is 10.6.
+extern NSString* const AVCaptureDeviceWasConnectedNotification;
+extern NSString* const AVCaptureDeviceWasDisconnectedNotification;
+extern NSString* const AVMediaTypeVideo;
+extern NSString* const AVMediaTypeAudio;
+extern NSString* const AVMediaTypeMuxed;
+extern NSString* const AVCaptureSessionRuntimeErrorNotification;
+extern NSString* const AVCaptureSessionDidStopRunningNotification;
+extern NSString* const AVCaptureSessionErrorKey;
+extern NSString* const AVVideoScalingModeKey;
+extern NSString* const AVVideoScalingModeResizeAspectFill;
+namespace {
// This class is used to retrieve AVFoundation NSBundle and library handle. It
// must be used as a LazyInstance so that it is initialised once and in a
// thread-safe way. Normally no work is done in constructors: LazyInstance is
@@ -44,37 +40,6 @@ class AVFoundationInternal {
AVFoundationInternal() {
bundle_ = [NSBundle
bundleWithPath:@"/System/Library/Frameworks/AVFoundation.framework"];
-
- const char* path = [[bundle_ executablePath] fileSystemRepresentation];
- CHECK(path);
- library_handle_ = dlopen(path, RTLD_LAZY | RTLD_LOCAL);
- CHECK(library_handle_) << dlerror();
-
- struct {
- NSString** loaded_string;
- const char* symbol;
- } av_strings[] = {
- {&AVCaptureDeviceWasConnectedNotification_,
- "AVCaptureDeviceWasConnectedNotification"},
- {&AVCaptureDeviceWasDisconnectedNotification_,
- "AVCaptureDeviceWasDisconnectedNotification"},
- {&AVMediaTypeVideo_, "AVMediaTypeVideo"},
- {&AVMediaTypeAudio_, "AVMediaTypeAudio"},
- {&AVMediaTypeMuxed_, "AVMediaTypeMuxed"},
- {&AVCaptureSessionRuntimeErrorNotification_,
- "AVCaptureSessionRuntimeErrorNotification"},
- {&AVCaptureSessionDidStopRunningNotification_,
- "AVCaptureSessionDidStopRunningNotification"},
- {&AVCaptureSessionErrorKey_, "AVCaptureSessionErrorKey"},
- {&AVVideoScalingModeKey_, "AVVideoScalingModeKey"},
- {&AVVideoScalingModeResizeAspectFill_,
- "AVVideoScalingModeResizeAspectFill"},
- };
- for (size_t i = 0; i < arraysize(av_strings); ++i) {
- *av_strings[i].loaded_string = *reinterpret_cast<NSString**>(
- dlsym(library_handle_, av_strings[i].symbol));
- DCHECK(*av_strings[i].loaded_string) << dlerror();
- }
}
NSBundle* bundle() const { return bundle_; }
@@ -106,58 +71,54 @@ class AVFoundationInternal {
NSBundle* bundle_;
void* library_handle_;
// The following members are replicas of the respectives in AVFoundation.
- NSString* AVCaptureDeviceWasConnectedNotification_;
- NSString* AVCaptureDeviceWasDisconnectedNotification_;
- NSString* AVMediaTypeVideo_;
- NSString* AVMediaTypeAudio_;
- NSString* AVMediaTypeMuxed_;
- NSString* AVCaptureSessionRuntimeErrorNotification_;
- NSString* AVCaptureSessionDidStopRunningNotification_;
- NSString* AVCaptureSessionErrorKey_;
- NSString* AVVideoScalingModeKey_;
- NSString* AVVideoScalingModeResizeAspectFill_;
+ NSString* AVCaptureDeviceWasConnectedNotification_ =
+ ::AVCaptureDeviceWasConnectedNotification;
+ NSString* AVCaptureDeviceWasDisconnectedNotification_ =
+ ::AVCaptureDeviceWasDisconnectedNotification;
+ NSString* AVMediaTypeVideo_ = ::AVMediaTypeVideo;
+ NSString* AVMediaTypeAudio_ = ::AVMediaTypeAudio;
+ NSString* AVMediaTypeMuxed_ = ::AVMediaTypeMuxed;
+ NSString* AVCaptureSessionRuntimeErrorNotification_ =
+ ::AVCaptureSessionRuntimeErrorNotification;
+ NSString* AVCaptureSessionDidStopRunningNotification_ =
+ ::AVCaptureSessionDidStopRunningNotification;
+ NSString* AVCaptureSessionErrorKey_ = ::AVCaptureSessionErrorKey;
+ NSString* AVVideoScalingModeKey_ = ::AVVideoScalingModeKey;
+ NSString* AVVideoScalingModeResizeAspectFill_ =
+ ::AVVideoScalingModeResizeAspectFill;
DISALLOW_COPY_AND_ASSIGN(AVFoundationInternal);
};
+static base::ThreadLocalStorage::StaticSlot g_avfoundation_handle =
+ TLS_INITIALIZER;
+
+void TlsCleanup(void* value) {
+ delete static_cast<AVFoundationInternal*>(value);
+}
+
+AVFoundationInternal* GetAVFoundationInternal() {
+ return static_cast<AVFoundationInternal*>(g_avfoundation_handle.Get());
+}
+
// This contains the logic of checking whether AVFoundation is supported.
// It's called only once and the results are cached in a static bool.
bool LoadAVFoundationInternal() {
- // AVFoundation is only available on OS Lion and above.
- if (!base::mac::IsOSLionOrLater()) {
- LogCaptureApi(CAPTURE_API_QTKIT_DUE_TO_OS_PREVIOUS_TO_LION);
- return false;
- }
-
- const base::CommandLine* command_line =
- base::CommandLine::ForCurrentProcess();
- // The force-qtkit flag takes precedence over enable-avfoundation.
- if (command_line->HasSwitch(switches::kForceQTKit)) {
- LogCaptureApi(CAPTURE_API_QTKIT_FORCED_BY_FLAG);
- return false;
- }
-
- if (!command_line->HasSwitch(switches::kEnableAVFoundation)) {
- LogCaptureApi(CAPTURE_API_QTKIT_DUE_TO_NO_FLAG);
- return false;
- }
+ g_avfoundation_handle.Initialize(TlsCleanup);
+ g_avfoundation_handle.Set(new AVFoundationInternal());
const bool ret = [AVFoundationGlue::AVFoundationBundle() load];
- LogCaptureApi(ret ? CAPTURE_API_AVFOUNDATION_LOADED_OK
- : CAPTURE_API_QTKIT_DUE_TO_AVFOUNDATION_LOAD_ERROR);
+ CHECK(ret);
return ret;
}
-} // namespace
-
-static base::LazyInstance<AVFoundationInternal>::Leaky g_avfoundation_handle =
- LAZY_INSTANCE_INITIALIZER;
-
enum {
INITIALIZE_NOT_CALLED = 0,
AVFOUNDATION_IS_SUPPORTED,
AVFOUNDATION_NOT_SUPPORTED
} static g_avfoundation_initialization = INITIALIZE_NOT_CALLED;
+} // namespace
+
void AVFoundationGlue::InitializeAVFoundation() {
TRACE_EVENT0("video", "AVFoundationGlue::InitializeAVFoundation");
CHECK([NSThread isMainThread]);
@@ -167,55 +128,50 @@ void AVFoundationGlue::InitializeAVFoundation() {
AVFOUNDATION_IS_SUPPORTED : AVFOUNDATION_NOT_SUPPORTED;
}
-bool AVFoundationGlue::IsAVFoundationSupported() {
- CHECK_NE(g_avfoundation_initialization, INITIALIZE_NOT_CALLED);
- return g_avfoundation_initialization == AVFOUNDATION_IS_SUPPORTED;
-}
-
NSBundle const* AVFoundationGlue::AVFoundationBundle() {
- return g_avfoundation_handle.Get().bundle();
+ return GetAVFoundationInternal()->bundle();
}
NSString* AVFoundationGlue::AVCaptureDeviceWasConnectedNotification() {
- return g_avfoundation_handle.Get().AVCaptureDeviceWasConnectedNotification();
+ return GetAVFoundationInternal()->AVCaptureDeviceWasConnectedNotification();
}
NSString* AVFoundationGlue::AVCaptureDeviceWasDisconnectedNotification() {
- return
- g_avfoundation_handle.Get().AVCaptureDeviceWasDisconnectedNotification();
+ return GetAVFoundationInternal()
+ ->AVCaptureDeviceWasDisconnectedNotification();
}
NSString* AVFoundationGlue::AVMediaTypeVideo() {
- return g_avfoundation_handle.Get().AVMediaTypeVideo();
+ return GetAVFoundationInternal()->AVMediaTypeVideo();
}
NSString* AVFoundationGlue::AVMediaTypeAudio() {
- return g_avfoundation_handle.Get().AVMediaTypeAudio();
+ return GetAVFoundationInternal()->AVMediaTypeAudio();
}
NSString* AVFoundationGlue::AVMediaTypeMuxed() {
- return g_avfoundation_handle.Get().AVMediaTypeMuxed();
+ return GetAVFoundationInternal()->AVMediaTypeMuxed();
}
NSString* AVFoundationGlue::AVCaptureSessionRuntimeErrorNotification() {
- return g_avfoundation_handle.Get().AVCaptureSessionRuntimeErrorNotification();
+ return GetAVFoundationInternal()->AVCaptureSessionRuntimeErrorNotification();
}
NSString* AVFoundationGlue::AVCaptureSessionDidStopRunningNotification() {
- return
- g_avfoundation_handle.Get().AVCaptureSessionDidStopRunningNotification();
+ return GetAVFoundationInternal()
+ ->AVCaptureSessionDidStopRunningNotification();
}
NSString* AVFoundationGlue::AVCaptureSessionErrorKey() {
- return g_avfoundation_handle.Get().AVCaptureSessionErrorKey();
+ return GetAVFoundationInternal()->AVCaptureSessionErrorKey();
}
NSString* AVFoundationGlue::AVVideoScalingModeKey() {
- return g_avfoundation_handle.Get().AVVideoScalingModeKey();
+ return GetAVFoundationInternal()->AVVideoScalingModeKey();
}
NSString* AVFoundationGlue::AVVideoScalingModeResizeAspectFill() {
- return g_avfoundation_handle.Get().AVVideoScalingModeResizeAspectFill();
+ return GetAVFoundationInternal()->AVVideoScalingModeResizeAspectFill();
}
Class AVFoundationGlue::AVCaptureSessionClass() {
diff --git a/chromium/media/base/mac/video_frame_mac.cc b/chromium/media/base/mac/video_frame_mac.cc
index 0d489148f91..e3af1f000de 100644
--- a/chromium/media/base/mac/video_frame_mac.cc
+++ b/chromium/media/base/mac/video_frame_mac.cc
@@ -59,15 +59,7 @@ WrapVideoFrameInCVPixelBuffer(const VideoFrame& frame) {
int num_planes = VideoFrame::NumPlanes(video_frame_format);
DCHECK_LE(num_planes, kMaxPlanes);
- gfx::Size coded_size = frame.coded_size();
-
- // TODO(jfroy): Support extended pixels (i.e. padding).
- if (coded_size != frame.visible_rect().size()) {
- DLOG(ERROR) << " frame with extended pixels not supported: "
- << " coded_size: " << coded_size.ToString()
- << ", visible_rect: " << frame.visible_rect().ToString();
- return pixel_buffer;
- }
+ const gfx::Rect& visible_rect = frame.visible_rect();
// Build arrays for each plane's data pointer, dimensions and byte alignment.
void* plane_ptrs[kMaxPlanes];
@@ -75,9 +67,9 @@ WrapVideoFrameInCVPixelBuffer(const VideoFrame& frame) {
size_t plane_heights[kMaxPlanes];
size_t plane_bytes_per_row[kMaxPlanes];
for (int plane_i = 0; plane_i < num_planes; ++plane_i) {
- plane_ptrs[plane_i] = const_cast<uint8_t*>(frame.data(plane_i));
+ plane_ptrs[plane_i] = const_cast<uint8_t*>(frame.visible_data(plane_i));
gfx::Size plane_size =
- VideoFrame::PlaneSize(video_frame_format, plane_i, coded_size);
+ VideoFrame::PlaneSize(video_frame_format, plane_i, visible_rect.size());
plane_widths[plane_i] = plane_size.width();
plane_heights[plane_i] = plane_size.height();
plane_bytes_per_row[plane_i] = frame.stride(plane_i);
@@ -94,9 +86,9 @@ WrapVideoFrameInCVPixelBuffer(const VideoFrame& frame) {
// give it a smart pointer to the frame, so instead pass a raw pointer and
// increment the frame's reference count manually.
CVReturn result = CVPixelBufferCreateWithPlanarBytes(
- kCFAllocatorDefault, coded_size.width(), coded_size.height(), cv_format,
- descriptor, 0, num_planes, plane_ptrs, plane_widths, plane_heights,
- plane_bytes_per_row, &CvPixelBufferReleaseCallback,
+ kCFAllocatorDefault, visible_rect.width(), visible_rect.height(),
+ cv_format, descriptor, 0, num_planes, plane_ptrs, plane_widths,
+ plane_heights, plane_bytes_per_row, &CvPixelBufferReleaseCallback,
const_cast<VideoFrame*>(&frame), nullptr, pixel_buffer.InitializeInto());
if (result != kCVReturnSuccess) {
DLOG(ERROR) << " CVPixelBufferCreateWithPlanarBytes failed: " << result;
diff --git a/chromium/media/base/mac/video_frame_mac_unittests.cc b/chromium/media/base/mac/video_frame_mac_unittests.cc
index 475d9aa9307..34cd0415bf0 100644
--- a/chromium/media/base/mac/video_frame_mac_unittests.cc
+++ b/chromium/media/base/mac/video_frame_mac_unittests.cc
@@ -21,6 +21,7 @@ namespace {
const int kWidth = 64;
const int kHeight = 48;
+const int kVisibleRectOffset = 8;
const base::TimeDelta kTimestamp = base::TimeDelta::FromMicroseconds(1337);
struct FormatPair {
@@ -43,8 +44,8 @@ TEST(VideoFrameMac, CheckBasicAttributes) {
auto pb = WrapVideoFrameInCVPixelBuffer(*frame);
ASSERT_TRUE(pb.get());
- gfx::Size coded_size = frame->coded_size();
- VideoPixelFormat format = frame->format();
+ const gfx::Size coded_size = frame->coded_size();
+ const VideoPixelFormat format = frame->format();
EXPECT_EQ(coded_size.width(), static_cast<int>(CVPixelBufferGetWidth(pb)));
EXPECT_EQ(coded_size.height(), static_cast<int>(CVPixelBufferGetHeight(pb)));
@@ -52,7 +53,7 @@ TEST(VideoFrameMac, CheckBasicAttributes) {
CVPixelBufferLockBaseAddress(pb, 0);
for (size_t i = 0; i < VideoFrame::NumPlanes(format); ++i) {
- gfx::Size plane_size = VideoFrame::PlaneSize(format, i, coded_size);
+ const gfx::Size plane_size = VideoFrame::PlaneSize(format, i, coded_size);
EXPECT_EQ(plane_size.width(),
static_cast<int>(CVPixelBufferGetWidthOfPlane(pb, i)));
EXPECT_EQ(plane_size.height(),
@@ -94,7 +95,7 @@ TEST(VideoFrameMac, CheckLifetime) {
int instances_destroyed = 0;
auto wrapper_frame = VideoFrame::WrapVideoFrame(
- frame, frame->visible_rect(), frame->natural_size());
+ frame, frame->format(), frame->visible_rect(), frame->natural_size());
wrapper_frame->AddDestructionObserver(
base::Bind(&Increment, &instances_destroyed));
ASSERT_TRUE(wrapper_frame.get());
@@ -115,7 +116,7 @@ TEST(VideoFrameMac, CheckWrapperFrame) {
CoreVideoGlue::kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange},
};
- gfx::Size size(kWidth, kHeight);
+ const gfx::Size size(kWidth, kHeight);
for (const auto& format_pair : format_pairs) {
base::ScopedCFTypeRef<CVPixelBufferRef> pb;
CVPixelBufferCreate(nullptr, kWidth, kHeight, format_pair.corevideo,
@@ -132,4 +133,66 @@ TEST(VideoFrameMac, CheckWrapperFrame) {
}
}
+static void FillFrameWithPredictableValues(const VideoFrame& frame) {
+ for (size_t i = 0; i < VideoFrame::NumPlanes(frame.format()); ++i) {
+ const gfx::Size& size =
+ VideoFrame::PlaneSize(frame.format(), i, frame.coded_size());
+ uint8_t* plane_ptr = const_cast<uint8_t*>(frame.data(i));
+ for (int h = 0; h < size.height(); ++h) {
+ const int row_index = h * frame.stride(i);
+ for (int w = 0; w < size.width(); ++w) {
+ const int index = row_index + w;
+ plane_ptr[index] = static_cast<uint8_t>(w ^ h);
+ }
+ }
+ }
+}
+
+TEST(VideoFrameMac, CorrectlyWrapsFramesWithPadding) {
+ const gfx::Size coded_size(kWidth, kHeight);
+ const gfx::Rect visible_rect(kVisibleRectOffset, kVisibleRectOffset,
+ kWidth - 2 * kVisibleRectOffset,
+ kHeight - 2 * kVisibleRectOffset);
+ auto frame =
+ VideoFrame::CreateFrame(PIXEL_FORMAT_I420, coded_size, visible_rect,
+ visible_rect.size(), kTimestamp);
+ ASSERT_TRUE(frame.get());
+ FillFrameWithPredictableValues(*frame);
+
+ auto pb = WrapVideoFrameInCVPixelBuffer(*frame);
+ ASSERT_TRUE(pb.get());
+ EXPECT_EQ(kCVPixelFormatType_420YpCbCr8Planar,
+ CVPixelBufferGetPixelFormatType(pb));
+ EXPECT_EQ(visible_rect.width(), static_cast<int>(CVPixelBufferGetWidth(pb)));
+ EXPECT_EQ(visible_rect.height(),
+ static_cast<int>(CVPixelBufferGetHeight(pb)));
+
+ CVPixelBufferLockBaseAddress(pb, 0);
+ for (size_t i = 0; i < VideoFrame::NumPlanes(frame->format()); ++i) {
+ const gfx::Size plane_size =
+ VideoFrame::PlaneSize(frame->format(), i, visible_rect.size());
+ EXPECT_EQ(plane_size.width(),
+ static_cast<int>(CVPixelBufferGetWidthOfPlane(pb, i)));
+ EXPECT_EQ(plane_size.height(),
+ static_cast<int>(CVPixelBufferGetHeightOfPlane(pb, i)));
+
+ uint8_t* plane_ptr =
+ reinterpret_cast<uint8_t*>(CVPixelBufferGetBaseAddressOfPlane(pb, i));
+ EXPECT_EQ(frame->visible_data(i), plane_ptr);
+ const int stride =
+ static_cast<int>(CVPixelBufferGetBytesPerRowOfPlane(pb, i));
+ EXPECT_EQ(frame->stride(i), stride);
+ const int offset = kVisibleRectOffset / ((i == 0) ? 1 : 2);
+ for (int h = 0; h < plane_size.height(); ++h) {
+ const int row_index = h * stride;
+ for (int w = 0; w < plane_size.width(); ++w) {
+ const int index = row_index + w;
+ EXPECT_EQ(static_cast<uint8_t>((w + offset) ^ (h + offset)),
+ plane_ptr[index]);
+ }
+ }
+ }
+ CVPixelBufferUnlockBaseAddress(pb, 0);
+}
+
} // namespace media
diff --git a/chromium/media/base/mac/videotoolbox_glue.h b/chromium/media/base/mac/videotoolbox_glue.h
index 212722a08d1..9a978bf09da 100644
--- a/chromium/media/base/mac/videotoolbox_glue.h
+++ b/chromium/media/base/mac/videotoolbox_glue.h
@@ -11,11 +11,13 @@
#include "media/base/mac/coremedia_glue.h"
#include "media/base/media_export.h"
-// VideoToolbox API is available in OS X 10.9 and iOS 8 (10.8 has support for
-// software encoding, but this class exposes the 10.9 API level). Chromium
-// requires OS X 10.6 or iOS 6. Linking with VideoToolbox therefore has to
-// happen at runtime. This class is defined to try and load the VideoToolbox
-// library. If it succeeds, clients can use VideoToolbox via this class.
+// VideoToolbox API is available in and after OS X 10.9 and iOS 8 (10.8 has
+// support for software encoding, but this class exposes the 10.9 API level).
+// Chromium requires OS X 10.9 or iOS 9. This class is defined to try and load
+// the VideoToolbox library at runtime. If it succeeds, clients can use
+// VideoToolbox via this class.
+// Note that this file is necessary because Chromium still targets OS X 10.6 for
+// deployment. It should be deprecated soon, see crbug.com/579648.
class MEDIA_EXPORT VideoToolboxGlue {
public:
class Loader;
@@ -49,6 +51,7 @@ class MEDIA_EXPORT VideoToolboxGlue {
CFStringRef kVTCompressionPropertyKey_AllowFrameReordering() const;
CFStringRef kVTCompressionPropertyKey_AverageBitRate() const;
CFStringRef kVTCompressionPropertyKey_ColorPrimaries() const;
+ CFStringRef kVTCompressionPropertyKey_DataRateLimits() const;
CFStringRef kVTCompressionPropertyKey_ExpectedFrameRate() const;
CFStringRef kVTCompressionPropertyKey_MaxFrameDelayCount() const;
CFStringRef kVTCompressionPropertyKey_MaxKeyFrameInterval() const;
@@ -68,6 +71,8 @@ class MEDIA_EXPORT VideoToolboxGlue {
CFStringRef
kVTVideoEncoderSpecification_EnableHardwareAcceleratedVideoEncoder()
const;
+ CFStringRef
+ kVTVideoEncoderSpecification_RequireHardwareAcceleratedVideoEncoder() const;
// Originally from VTCompressionSession.h
OSStatus VTCompressionSessionCreate(
diff --git a/chromium/media/base/mac/videotoolbox_glue.mm b/chromium/media/base/mac/videotoolbox_glue.mm
index 010767c8ee1..234f0ee184d 100644
--- a/chromium/media/base/mac/videotoolbox_glue.mm
+++ b/chromium/media/base/mac/videotoolbox_glue.mm
@@ -53,6 +53,7 @@ struct VideoToolboxGlue::Library {
CFStringRef* kVTCompressionPropertyKey_AllowFrameReordering;
CFStringRef* kVTCompressionPropertyKey_AverageBitRate;
CFStringRef* kVTCompressionPropertyKey_ColorPrimaries;
+ CFStringRef* kVTCompressionPropertyKey_DataRateLimits;
CFStringRef* kVTCompressionPropertyKey_ExpectedFrameRate;
CFStringRef* kVTCompressionPropertyKey_MaxFrameDelayCount;
CFStringRef* kVTCompressionPropertyKey_MaxKeyFrameInterval;
@@ -68,6 +69,8 @@ struct VideoToolboxGlue::Library {
CFStringRef* kVTProfileLevel_H264_High_AutoLevel;
CFStringRef*
kVTVideoEncoderSpecification_EnableHardwareAcceleratedVideoEncoder;
+ CFStringRef*
+ kVTVideoEncoderSpecification_RequireHardwareAcceleratedVideoEncoder;
};
// Lazy-instance responsible for loading VideoToolbox.
@@ -98,6 +101,7 @@ class VideoToolboxGlue::Loader {
LOAD_SYMBOL(kVTCompressionPropertyKey_AllowFrameReordering)
LOAD_SYMBOL(kVTCompressionPropertyKey_AverageBitRate)
LOAD_SYMBOL(kVTCompressionPropertyKey_ColorPrimaries)
+ LOAD_SYMBOL(kVTCompressionPropertyKey_DataRateLimits)
LOAD_SYMBOL(kVTCompressionPropertyKey_ExpectedFrameRate)
LOAD_SYMBOL(kVTCompressionPropertyKey_MaxFrameDelayCount)
LOAD_SYMBOL(kVTCompressionPropertyKey_MaxKeyFrameInterval)
@@ -113,6 +117,8 @@ class VideoToolboxGlue::Loader {
LOAD_SYMBOL(kVTProfileLevel_H264_High_AutoLevel)
LOAD_SYMBOL(
kVTVideoEncoderSpecification_EnableHardwareAcceleratedVideoEncoder)
+ LOAD_SYMBOL(
+ kVTVideoEncoderSpecification_RequireHardwareAcceleratedVideoEncoder)
#undef LOAD_SYMBOL
@@ -216,6 +222,7 @@ OSStatus VideoToolboxGlue::VTSessionSetProperty(VTSessionRef session,
KEY_ACCESSOR(kVTCompressionPropertyKey_AllowFrameReordering)
KEY_ACCESSOR(kVTCompressionPropertyKey_AverageBitRate)
KEY_ACCESSOR(kVTCompressionPropertyKey_ColorPrimaries)
+KEY_ACCESSOR(kVTCompressionPropertyKey_DataRateLimits)
KEY_ACCESSOR(kVTCompressionPropertyKey_ExpectedFrameRate)
KEY_ACCESSOR(kVTCompressionPropertyKey_MaxFrameDelayCount)
KEY_ACCESSOR(kVTCompressionPropertyKey_MaxKeyFrameInterval)
@@ -230,5 +237,7 @@ KEY_ACCESSOR(kVTProfileLevel_H264_Main_AutoLevel)
KEY_ACCESSOR(kVTProfileLevel_H264_Extended_AutoLevel)
KEY_ACCESSOR(kVTProfileLevel_H264_High_AutoLevel)
KEY_ACCESSOR(kVTVideoEncoderSpecification_EnableHardwareAcceleratedVideoEncoder)
+KEY_ACCESSOR(
+ kVTVideoEncoderSpecification_RequireHardwareAcceleratedVideoEncoder)
#undef KEY_ACCESSOR
diff --git a/chromium/media/base/mac/videotoolbox_helpers.cc b/chromium/media/base/mac/videotoolbox_helpers.cc
new file mode 100644
index 00000000000..47cfb2fe662
--- /dev/null
+++ b/chromium/media/base/mac/videotoolbox_helpers.cc
@@ -0,0 +1,304 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/base/mac/videotoolbox_helpers.h"
+
+#include <array>
+#include <vector>
+
+#include "base/big_endian.h"
+#include "base/memory/scoped_ptr.h"
+
+namespace media {
+
+namespace video_toolbox {
+
+base::ScopedCFTypeRef<CFDictionaryRef>
+DictionaryWithKeysAndValues(CFTypeRef* keys, CFTypeRef* values, size_t size) {
+ return base::ScopedCFTypeRef<CFDictionaryRef>(CFDictionaryCreate(
+ kCFAllocatorDefault, keys, values, size, &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks));
+}
+
+base::ScopedCFTypeRef<CFDictionaryRef> DictionaryWithKeyValue(CFTypeRef key,
+ CFTypeRef value) {
+ CFTypeRef keys[1] = {key};
+ CFTypeRef values[1] = {value};
+ return DictionaryWithKeysAndValues(keys, values, 1);
+}
+
+base::ScopedCFTypeRef<CFArrayRef> ArrayWithIntegers(const int* v, size_t size) {
+ std::vector<CFNumberRef> numbers;
+ numbers.reserve(size);
+ for (const int* end = v + size; v < end; ++v)
+ numbers.push_back(CFNumberCreate(nullptr, kCFNumberSInt32Type, v));
+ base::ScopedCFTypeRef<CFArrayRef> array(CFArrayCreate(
+ kCFAllocatorDefault, reinterpret_cast<const void**>(&numbers[0]),
+ numbers.size(), &kCFTypeArrayCallBacks));
+ for (auto& number : numbers) {
+ CFRelease(number);
+ }
+ return array;
+}
+
+base::ScopedCFTypeRef<CFArrayRef> ArrayWithIntegerAndFloat(int int_val,
+ float float_val) {
+ std::array<CFNumberRef, 2> numbers = {
+ {CFNumberCreate(nullptr, kCFNumberSInt32Type, &int_val),
+ CFNumberCreate(nullptr, kCFNumberFloat32Type, &float_val)}};
+ base::ScopedCFTypeRef<CFArrayRef> array(CFArrayCreate(
+ kCFAllocatorDefault, reinterpret_cast<const void**>(numbers.data()),
+ numbers.size(), &kCFTypeArrayCallBacks));
+ for (auto& number : numbers)
+ CFRelease(number);
+ return array;
+}
+
+// Wrapper class for writing AnnexBBuffer output into.
+class AnnexBBuffer {
+ public:
+ virtual bool Reserve(size_t size) = 0;
+ virtual void Append(const char* s, size_t n) = 0;
+ virtual size_t GetReservedSize() const = 0;
+};
+
+class RawAnnexBBuffer : public AnnexBBuffer {
+ public:
+ RawAnnexBBuffer(char* annexb_buffer, size_t annexb_buffer_size)
+ : annexb_buffer_(annexb_buffer),
+ annexb_buffer_size_(annexb_buffer_size),
+ annexb_buffer_offset_(0) {}
+ bool Reserve(size_t size) override {
+ reserved_size_ = size;
+ return size <= annexb_buffer_size_;
+ }
+ void Append(const char* s, size_t n) override {
+ memcpy(annexb_buffer_ + annexb_buffer_offset_, s, n);
+ annexb_buffer_offset_ += n;
+ DCHECK_GE(reserved_size_, annexb_buffer_offset_);
+ }
+ size_t GetReservedSize() const override { return reserved_size_; }
+
+ private:
+ char* annexb_buffer_;
+ size_t annexb_buffer_size_;
+ size_t annexb_buffer_offset_;
+ size_t reserved_size_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(RawAnnexBBuffer);
+};
+
+class StringAnnexBBuffer : public AnnexBBuffer {
+ public:
+ explicit StringAnnexBBuffer(std::string* str_annexb_buffer)
+ : str_annexb_buffer_(str_annexb_buffer) {}
+ bool Reserve(size_t size) override {
+ str_annexb_buffer_->reserve(size);
+ return true;
+ }
+ void Append(const char* s, size_t n) override {
+ str_annexb_buffer_->append(s, n);
+ }
+ size_t GetReservedSize() const override { return str_annexb_buffer_->size(); }
+
+ private:
+ std::string* str_annexb_buffer_;
+ DISALLOW_IMPLICIT_CONSTRUCTORS(StringAnnexBBuffer);
+};
+
+template <typename NalSizeType>
+void CopyNalsToAnnexB(char* avcc_buffer,
+ const size_t avcc_size,
+ AnnexBBuffer* annexb_buffer) {
+ static_assert(sizeof(NalSizeType) == 1 || sizeof(NalSizeType) == 2 ||
+ sizeof(NalSizeType) == 4,
+ "NAL size type has unsupported size");
+ static const char startcode_3[3] = {0, 0, 1};
+ DCHECK(avcc_buffer);
+ DCHECK(annexb_buffer);
+ size_t bytes_left = avcc_size;
+ while (bytes_left > 0) {
+ DCHECK_GT(bytes_left, sizeof(NalSizeType));
+ NalSizeType nal_size;
+ base::ReadBigEndian(avcc_buffer, &nal_size);
+ bytes_left -= sizeof(NalSizeType);
+ avcc_buffer += sizeof(NalSizeType);
+
+ DCHECK_GE(bytes_left, nal_size);
+ annexb_buffer->Append(startcode_3, sizeof(startcode_3));
+ annexb_buffer->Append(avcc_buffer, nal_size);
+ bytes_left -= nal_size;
+ avcc_buffer += nal_size;
+ }
+}
+
+bool CopySampleBufferToAnnexBBuffer(CoreMediaGlue::CMSampleBufferRef sbuf,
+ AnnexBBuffer* annexb_buffer,
+ bool keyframe) {
+ // Perform two pass, one to figure out the total output size, and another to
+ // copy the data after having performed a single output allocation. Note that
+ // we'll allocate a bit more because we'll count 4 bytes instead of 3 for
+ // video NALs.
+ OSStatus status;
+
+ // Get the sample buffer's block buffer and format description.
+ auto bb = CoreMediaGlue::CMSampleBufferGetDataBuffer(sbuf);
+ DCHECK(bb);
+ auto fdesc = CoreMediaGlue::CMSampleBufferGetFormatDescription(sbuf);
+ DCHECK(fdesc);
+
+ size_t bb_size = CoreMediaGlue::CMBlockBufferGetDataLength(bb);
+ size_t total_bytes = bb_size;
+
+ size_t pset_count;
+ int nal_size_field_bytes;
+ status = CoreMediaGlue::CMVideoFormatDescriptionGetH264ParameterSetAtIndex(
+ fdesc, 0, nullptr, nullptr, &pset_count, &nal_size_field_bytes);
+ if (status ==
+ CoreMediaGlue::kCMFormatDescriptionBridgeError_InvalidParameter) {
+ DLOG(WARNING) << " assuming 2 parameter sets and 4 bytes NAL length header";
+ pset_count = 2;
+ nal_size_field_bytes = 4;
+ } else if (status != noErr) {
+ DLOG(ERROR)
+ << " CMVideoFormatDescriptionGetH264ParameterSetAtIndex failed: "
+ << status;
+ return false;
+ }
+
+ if (keyframe) {
+ const uint8_t* pset;
+ size_t pset_size;
+ for (size_t pset_i = 0; pset_i < pset_count; ++pset_i) {
+ status =
+ CoreMediaGlue::CMVideoFormatDescriptionGetH264ParameterSetAtIndex(
+ fdesc, pset_i, &pset, &pset_size, nullptr, nullptr);
+ if (status != noErr) {
+ DLOG(ERROR)
+ << " CMVideoFormatDescriptionGetH264ParameterSetAtIndex failed: "
+ << status;
+ return false;
+ }
+ total_bytes += pset_size + nal_size_field_bytes;
+ }
+ }
+
+ if (!annexb_buffer->Reserve(total_bytes)) {
+ DLOG(ERROR) << "Cannot fit encode output into bitstream buffer. Requested:"
+ << total_bytes;
+ return false;
+ }
+
+ // Copy all parameter sets before keyframes.
+ if (keyframe) {
+ const uint8_t* pset;
+ size_t pset_size;
+ for (size_t pset_i = 0; pset_i < pset_count; ++pset_i) {
+ status =
+ CoreMediaGlue::CMVideoFormatDescriptionGetH264ParameterSetAtIndex(
+ fdesc, pset_i, &pset, &pset_size, nullptr, nullptr);
+ if (status != noErr) {
+ DLOG(ERROR)
+ << " CMVideoFormatDescriptionGetH264ParameterSetAtIndex failed: "
+ << status;
+ return false;
+ }
+ static const char startcode_4[4] = {0, 0, 0, 1};
+ annexb_buffer->Append(startcode_4, sizeof(startcode_4));
+ annexb_buffer->Append(reinterpret_cast<const char*>(pset), pset_size);
+ }
+ }
+
+ // Block buffers can be composed of non-contiguous chunks. For the sake of
+ // keeping this code simple, flatten non-contiguous block buffers.
+ base::ScopedCFTypeRef<CoreMediaGlue::CMBlockBufferRef> contiguous_bb(
+ bb, base::scoped_policy::RETAIN);
+ if (!CoreMediaGlue::CMBlockBufferIsRangeContiguous(bb, 0, 0)) {
+ contiguous_bb.reset();
+ status = CoreMediaGlue::CMBlockBufferCreateContiguous(
+ kCFAllocatorDefault, bb, kCFAllocatorDefault, nullptr, 0, 0, 0,
+ contiguous_bb.InitializeInto());
+ if (status != noErr) {
+ DLOG(ERROR) << " CMBlockBufferCreateContiguous failed: " << status;
+ return false;
+ }
+ }
+
+ // Copy all the NAL units. In the process convert them from AVCC format
+ // (length header) to AnnexB format (start code).
+ char* bb_data;
+ status = CoreMediaGlue::CMBlockBufferGetDataPointer(contiguous_bb, 0, nullptr,
+ nullptr, &bb_data);
+ if (status != noErr) {
+ DLOG(ERROR) << " CMBlockBufferGetDataPointer failed: " << status;
+ return false;
+ }
+
+ if (nal_size_field_bytes == 1) {
+ CopyNalsToAnnexB<uint8_t>(bb_data, bb_size, annexb_buffer);
+ } else if (nal_size_field_bytes == 2) {
+ CopyNalsToAnnexB<uint16_t>(bb_data, bb_size, annexb_buffer);
+ } else if (nal_size_field_bytes == 4) {
+ CopyNalsToAnnexB<uint32_t>(bb_data, bb_size, annexb_buffer);
+ } else {
+ NOTREACHED();
+ }
+ return true;
+}
+
+bool CopySampleBufferToAnnexBBuffer(CoreMediaGlue::CMSampleBufferRef sbuf,
+ bool keyframe,
+ std::string* annexb_buffer) {
+ StringAnnexBBuffer buffer(annexb_buffer);
+ return CopySampleBufferToAnnexBBuffer(sbuf, &buffer, keyframe);
+}
+
+bool CopySampleBufferToAnnexBBuffer(CoreMediaGlue::CMSampleBufferRef sbuf,
+ bool keyframe,
+ size_t annexb_buffer_size,
+ char* annexb_buffer,
+ size_t* used_buffer_size) {
+ RawAnnexBBuffer buffer(annexb_buffer, annexb_buffer_size);
+ const bool copy_rv = CopySampleBufferToAnnexBBuffer(sbuf, &buffer, keyframe);
+ *used_buffer_size = buffer.GetReservedSize();
+ return copy_rv;
+}
+
+SessionPropertySetter::SessionPropertySetter(
+ base::ScopedCFTypeRef<VideoToolboxGlue::VTCompressionSessionRef> session,
+ const VideoToolboxGlue* const glue)
+ : session_(session), glue_(glue) {}
+
+SessionPropertySetter::~SessionPropertySetter() {}
+
+bool SessionPropertySetter::Set(CFStringRef key, int32_t value) {
+ DCHECK(session_);
+ DCHECK(glue_);
+ base::ScopedCFTypeRef<CFNumberRef> cfvalue(
+ CFNumberCreate(nullptr, kCFNumberSInt32Type, &value));
+ return glue_->VTSessionSetProperty(session_, key, cfvalue) == noErr;
+}
+
+bool SessionPropertySetter::Set(CFStringRef key, bool value) {
+ DCHECK(session_);
+ DCHECK(glue_);
+ CFBooleanRef cfvalue = (value) ? kCFBooleanTrue : kCFBooleanFalse;
+ return glue_->VTSessionSetProperty(session_, key, cfvalue) == noErr;
+}
+
+bool SessionPropertySetter::Set(CFStringRef key, CFStringRef value) {
+ DCHECK(session_);
+ DCHECK(glue_);
+ return glue_->VTSessionSetProperty(session_, key, value) == noErr;
+}
+
+bool SessionPropertySetter::Set(CFStringRef key, CFArrayRef value) {
+ DCHECK(session_);
+ DCHECK(glue_);
+ return glue_->VTSessionSetProperty(session_, key, value) == noErr;
+}
+
+} // namespace video_toolbox
+
+} // namespace media
diff --git a/chromium/media/base/mac/videotoolbox_helpers.h b/chromium/media/base/mac/videotoolbox_helpers.h
new file mode 100644
index 00000000000..87d769eef81
--- /dev/null
+++ b/chromium/media/base/mac/videotoolbox_helpers.h
@@ -0,0 +1,69 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_BASE_MAC_VIDEOTOOLBOX_HELPERS_H_
+#define MEDIA_BASE_MAC_VIDEOTOOLBOX_HELPERS_H_
+
+#include "base/mac/scoped_cftyperef.h"
+#include "media/base/mac/videotoolbox_glue.h"
+#include "media/base/media_export.h"
+
+namespace media {
+
+namespace video_toolbox {
+
+// Create a CFDictionaryRef with the given keys and values.
+MEDIA_EXPORT base::ScopedCFTypeRef<CFDictionaryRef>
+DictionaryWithKeysAndValues(CFTypeRef* keys, CFTypeRef* values, size_t size);
+
+// Create a CFDictionaryRef with the given key and value.
+MEDIA_EXPORT base::ScopedCFTypeRef<CFDictionaryRef> DictionaryWithKeyValue(
+ CFTypeRef key,
+ CFTypeRef value);
+
+// Create a CFArrayRef with the given array of integers.
+MEDIA_EXPORT base::ScopedCFTypeRef<CFArrayRef> ArrayWithIntegers(const int* v,
+ size_t size);
+
+// Create a CFArrayRef with the given int and float values.
+MEDIA_EXPORT base::ScopedCFTypeRef<CFArrayRef> ArrayWithIntegerAndFloat(
+ int int_val,
+ float float_val);
+
+// Copy a H.264 frame stored in a CM sample buffer to an Annex B buffer. Copies
+// parameter sets for keyframes before the frame data as well.
+MEDIA_EXPORT bool CopySampleBufferToAnnexBBuffer(
+ CoreMediaGlue::CMSampleBufferRef sbuf,
+ bool keyframe,
+ std::string* annexb_buffer);
+MEDIA_EXPORT bool CopySampleBufferToAnnexBBuffer(
+ CoreMediaGlue::CMSampleBufferRef sbuf,
+ bool keyframe,
+ size_t annexb_buffer_size,
+ char* annexb_buffer,
+ size_t* used_buffer_size);
+
+// Helper class to add session properties to a VTCompressionSessionRef.
+class MEDIA_EXPORT SessionPropertySetter {
+ public:
+ SessionPropertySetter(
+ base::ScopedCFTypeRef<VideoToolboxGlue::VTCompressionSessionRef> session,
+ const VideoToolboxGlue* const glue);
+ ~SessionPropertySetter();
+
+ bool Set(CFStringRef key, int32_t value);
+ bool Set(CFStringRef key, bool value);
+ bool Set(CFStringRef key, CFStringRef value);
+ bool Set(CFStringRef key, CFArrayRef value);
+
+ private:
+ base::ScopedCFTypeRef<VideoToolboxGlue::VTCompressionSessionRef> session_;
+ const VideoToolboxGlue* glue_;
+};
+
+} // namespace video_toolbox
+
+} // namespace media
+
+#endif // MEDIA_BASE_MAC_VIDEOTOOLBOX_HELPERS_H_
diff --git a/chromium/media/base/media.cc b/chromium/media/base/media.cc
index f55d1c437a5..9fd89ccc7f0 100644
--- a/chromium/media/base/media.cc
+++ b/chromium/media/base/media.cc
@@ -4,15 +4,19 @@
#include "media/base/media.h"
-#include "base/files/file_path.h"
+#include "base/command_line.h"
#include "base/lazy_instance.h"
#include "base/macros.h"
-#include "base/path_service.h"
-#include "base/synchronization/lock.h"
+#include "base/metrics/field_trial.h"
#include "base/trace_event/trace_event.h"
-#include "build/build_config.h"
+#include "media/base/media_switches.h"
#include "media/base/yuv_convert.h"
+#if defined(OS_ANDROID)
+#include "base/android/build_info.h"
+#include "media/base/android/media_codec_util.h"
+#endif
+
#if !defined(MEDIA_DISABLE_FFMPEG)
#include "media/ffmpeg/ffmpeg_common.h"
#endif
@@ -21,6 +25,13 @@ namespace media {
// Media must only be initialized once, so use a LazyInstance to ensure this.
class MediaInitializer {
+ public:
+ void enable_platform_decoder_support() {
+ has_platform_decoder_support_ = true;
+ }
+
+ bool has_platform_decoder_support() { return has_platform_decoder_support_; }
+
private:
friend struct base::DefaultLazyInstanceTraits<MediaInitializer>;
@@ -51,6 +62,8 @@ class MediaInitializer {
NOTREACHED() << "MediaInitializer should be leaky!";
}
+ bool has_platform_decoder_support_ = false;
+
DISALLOW_COPY_AND_ASSIGN(MediaInitializer);
};
@@ -61,4 +74,46 @@ void InitializeMediaLibrary() {
g_media_library.Get();
}
+#if defined(OS_ANDROID)
+void EnablePlatformDecoderSupport() {
+ g_media_library.Pointer()->enable_platform_decoder_support();
+}
+
+bool HasPlatformDecoderSupport() {
+ return g_media_library.Pointer()->has_platform_decoder_support();
+}
+
+bool PlatformHasOpusSupport() {
+ return base::android::BuildInfo::GetInstance()->sdk_int() >= 21;
+}
+
+bool IsUnifiedMediaPipelineEnabled() {
+ // TODO(dalecurtis): This experiment is temporary and should be removed once
+ // we have enough data to support the primacy of the unified media pipeline;
+ // see http://crbug.com/533190 for details.
+ //
+ // Note: It's important to query the field trial state first, to ensure that
+ // UMA reports the correct group.
+ const std::string group_name =
+ base::FieldTrialList::FindFullName("UnifiedMediaPipelineTrial");
+ const bool disabled_via_cli =
+ base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDisableUnifiedMediaPipeline);
+ // TODO(watk, dalecurtis): AVDA has bugs on API level 16 and 17 so it's
+ // disabled for now. http://crbug.com/597467
+ const bool api_level_supported =
+ base::android::BuildInfo::GetInstance()->sdk_int() >= 18;
+
+ return !disabled_via_cli && api_level_supported &&
+ !base::StartsWith(group_name, "Disabled",
+ base::CompareCase::SENSITIVE);
+}
+
+bool ArePlatformDecodersAvailable() {
+ return IsUnifiedMediaPipelineEnabled()
+ ? HasPlatformDecoderSupport()
+ : MediaCodecUtil::IsMediaCodecAvailable();
+}
+#endif
+
} // namespace media
diff --git a/chromium/media/base/media.h b/chromium/media/base/media.h
index 01a913bc45f..c0fd87ee3bb 100644
--- a/chromium/media/base/media.h
+++ b/chromium/media/base/media.h
@@ -8,6 +8,7 @@
#ifndef MEDIA_BASE_MEDIA_H_
#define MEDIA_BASE_MEDIA_H_
+#include "build/build_config.h"
#include "media/base/media_export.h"
namespace base {
@@ -20,6 +21,32 @@ namespace media {
// features.
MEDIA_EXPORT void InitializeMediaLibrary();
+#if defined(OS_ANDROID)
+// Tells the media library it has support for OS level decoders. Should only be
+// used for actual decoders (e.g. MediaCodec) and not full-featured players
+// (e.g. MediaPlayer).
+MEDIA_EXPORT void EnablePlatformDecoderSupport();
+MEDIA_EXPORT bool HasPlatformDecoderSupport();
+
+// Indicates if the platform supports Opus. Determined *ONLY* by the platform
+// version, so does not guarantee that either can actually be played.
+MEDIA_EXPORT bool PlatformHasOpusSupport();
+
+// Returns true if the unified media pipeline is enabled; the pipeline may still
+// not work for all codecs if HasPlatformDecoderSupport() is false. Please see
+// MimeUtil for an exhaustive listing of supported codecs.
+//
+// TODO(dalecurtis): These methods are temporary and should be removed once the
+// unified media pipeline is supported everywhere. http://crbug.com/580626.
+MEDIA_EXPORT bool IsUnifiedMediaPipelineEnabled();
+
+// Returns whether the platform decoders are available for use.
+// This includes decoders being available on the platform and accessible, such
+// as via the GPU process. Should only be used for actual decoders
+// (e.g. MediaCodec) and not full-featured players (e.g. MediaPlayer).
+MEDIA_EXPORT bool ArePlatformDecodersAvailable();
+#endif
+
} // namespace media
#endif // MEDIA_BASE_MEDIA_H_
diff --git a/chromium/media/base/media_client.cc b/chromium/media/base/media_client.cc
index bcc6de0bffe..e7026aadc13 100644
--- a/chromium/media/base/media_client.cc
+++ b/chromium/media/base/media_client.cc
@@ -20,12 +20,9 @@ MediaClient* GetMediaClient() {
KeySystemInfoForUMA::KeySystemInfoForUMA(
const std::string& key_system,
- const std::string& key_system_name_for_uma,
- bool reports_key_system_support_to_uma)
+ const std::string& key_system_name_for_uma)
: key_system(key_system),
- key_system_name_for_uma(key_system_name_for_uma),
- reports_key_system_support_to_uma(reports_key_system_support_to_uma) {
-}
+ key_system_name_for_uma(key_system_name_for_uma) {}
KeySystemInfoForUMA::~KeySystemInfoForUMA() {
}
diff --git a/chromium/media/base/media_client.h b/chromium/media/base/media_client.h
index dc8da49218f..30d86799559 100644
--- a/chromium/media/base/media_client.h
+++ b/chromium/media/base/media_client.h
@@ -28,8 +28,7 @@ MEDIA_EXPORT MediaClient* GetMediaClient();
struct MEDIA_EXPORT KeySystemInfoForUMA {
KeySystemInfoForUMA(const std::string& key_system,
- const std::string& key_system_name_for_uma,
- bool reports_key_system_support_to_uma);
+ const std::string& key_system_name_for_uma);
~KeySystemInfoForUMA();
// Concrete key system name;
@@ -39,12 +38,6 @@ struct MEDIA_EXPORT KeySystemInfoForUMA {
// "org.w3.clearkey" is "ClearKey". When providing this value, make sure to
// update tools/metrics/histograms/histograms.xml.
std::string key_system_name_for_uma;
-
- // Whether query/support statistics for |key_system| should be reported.
- // If set to true, make sure to add a new Media.EME.KeySystemSupport.* to
- // tools/metrics/histograms/histograms.xml. See KeySystemsSupportUMA for
- // details on how key system query/support UMA is reported.
- bool reports_key_system_support_to_uma;
};
// A client interface for embedders (e.g. content/renderer) to provide
diff --git a/chromium/media/base/media_keys.h b/chromium/media/base/media_keys.h
index bb34adfbf3a..13c210c786a 100644
--- a/chromium/media/base/media_keys.h
+++ b/chromium/media/base/media_keys.h
@@ -66,22 +66,8 @@ typedef ScopedVector<CdmKeyInformation> CdmKeysInfo;
class MEDIA_EXPORT MediaKeys
: public base::RefCountedThreadSafe<MediaKeys, MediaKeysTraits> {
public:
- // Reported to UMA, so never reuse a value!
- // Must be kept in sync with blink::WebMediaPlayerClient::MediaKeyErrorCode
- // (enforced in webmediaplayer_impl.cc).
- // TODO(jrummell): Can this be moved to proxy_decryptor as it should only be
- // used by the prefixed EME code?
- enum KeyError {
- kUnknownError = 1,
- kClientError,
- // The commented v0.1b values below have never been used.
- // kServiceError,
- kOutputError = 4,
- // kHardwareChangeError,
- // kDomainError,
- kMaxKeyError // Must be last and greater than any legit value.
- };
-
+ // TODO(xhwang): Remove after prefixed EME support is removed. See
+ // http://crbug.com/249976
// Must be a superset of cdm::MediaKeyException.
enum Exception {
NOT_SUPPORTED_ERROR,
@@ -158,12 +144,14 @@ class MEDIA_EXPORT MediaKeys
virtual void RemoveSession(const std::string& session_id,
scoped_ptr<SimpleCdmPromise> promise) = 0;
- // Returns the CdmContext associated with |this| if Decryptor or CDM ID is
- // supported. The returned CdmContext is owned by |this|. Caller needs to make
- // sure it is not used after |this| is destructed.
- // Returns null if no Decryptor nor CDM ID is supported. Instead the media
- // player may use the CDM via some platform specific method.
+ // Returns the CdmContext associated with |this|. The returned CdmContext is
+ // owned by |this| and the caller needs to make sure it is not used after
+ // |this| is destructed.
+ // Returns null if CdmContext is not supported. Instead the media player may
+ // use the CDM via some platform specific method.
// By default this method returns null.
+ // TODO(xhwang): Convert all SetCdm() implementations to use CdmContext so
+ // that this function should never return nullptr.
virtual CdmContext* GetCdmContext();
// Deletes |this| on the correct thread. By default |this| is deleted
@@ -193,6 +181,7 @@ struct MEDIA_EXPORT MediaKeysTraits {
typedef base::Callback<void(const std::string& session_id,
MediaKeys::MessageType message_type,
const std::vector<uint8_t>& message,
+ // TODO(ddorwin): Remove. https://crbug.com/249976
const GURL& legacy_destination_url)>
SessionMessageCB;
diff --git a/chromium/media/base/media_log.cc b/chromium/media/base/media_log.cc
index 916021a3a63..128e5138ff5 100644
--- a/chromium/media/base/media_log.cc
+++ b/chromium/media/base/media_log.cc
@@ -91,8 +91,6 @@ std::string MediaLog::PipelineStatusToString(PipelineStatus status) {
switch (status) {
case PIPELINE_OK:
return "pipeline: ok";
- case PIPELINE_ERROR_URL_NOT_FOUND:
- return "pipeline: url not found";
case PIPELINE_ERROR_NETWORK:
return "pipeline: network error";
case PIPELINE_ERROR_DECODE:
@@ -105,8 +103,6 @@ std::string MediaLog::PipelineStatusToString(PipelineStatus status) {
return "pipeline: could not render";
case PIPELINE_ERROR_READ:
return "pipeline: read error";
- case PIPELINE_ERROR_OPERATION_PENDING:
- return "pipeline: operation pending";
case PIPELINE_ERROR_INVALID_STATE:
return "pipeline: invalid state";
case DEMUXER_ERROR_COULD_NOT_OPEN:
@@ -117,6 +113,16 @@ std::string MediaLog::PipelineStatusToString(PipelineStatus status) {
return "demuxer: no supported streams";
case DECODER_ERROR_NOT_SUPPORTED:
return "decoder: not supported";
+ case CHUNK_DEMUXER_ERROR_APPEND_FAILED:
+ return "chunk demuxer: append failed";
+ case CHUNK_DEMUXER_ERROR_EOS_STATUS_DECODE_ERROR:
+ return "chunk demuxer: application requested decode error on eos";
+ case CHUNK_DEMUXER_ERROR_EOS_STATUS_NETWORK_ERROR:
+ return "chunk demuxer: application requested network error on eos";
+ case AUDIO_RENDERER_ERROR:
+ return "audio renderer: output device reported an error";
+ case AUDIO_RENDERER_ERROR_SPLICE_FAILED:
+ return "audio renderer: post-decode audio splicing failed";
}
NOTREACHED();
return NULL;
@@ -144,6 +150,10 @@ MediaLog::~MediaLog() {}
void MediaLog::AddEvent(scoped_ptr<MediaLogEvent> event) {}
+std::string MediaLog::GetLastErrorMessage() {
+ return "";
+}
+
scoped_ptr<MediaLogEvent> MediaLog::CreateEvent(MediaLogEvent::Type type) {
scoped_ptr<MediaLogEvent> event(new MediaLogEvent);
event->id = id_;
@@ -195,10 +205,11 @@ scoped_ptr<MediaLogEvent> MediaLog::CreateSeekEvent(float seconds) {
}
scoped_ptr<MediaLogEvent> MediaLog::CreatePipelineStateChangedEvent(
- Pipeline::State state) {
+ PipelineImpl::State state) {
scoped_ptr<MediaLogEvent> event(
CreateEvent(MediaLogEvent::PIPELINE_STATE_CHANGED));
- event->params.SetString("pipeline_state", Pipeline::GetStateString(state));
+ event->params.SetString("pipeline_state",
+ PipelineImpl::GetStateString(state));
return event;
}
@@ -244,13 +255,6 @@ void MediaLog::SetStringProperty(
AddEvent(std::move(event));
}
-void MediaLog::SetIntegerProperty(
- const std::string& key, int value) {
- scoped_ptr<MediaLogEvent> event(CreateEvent(MediaLogEvent::PROPERTY_CHANGE));
- event->params.SetInteger(key, value);
- AddEvent(std::move(event));
-}
-
void MediaLog::SetDoubleProperty(
const std::string& key, double value) {
scoped_ptr<MediaLogEvent> event(CreateEvent(MediaLogEvent::PROPERTY_CHANGE));
@@ -265,16 +269,6 @@ void MediaLog::SetBooleanProperty(
AddEvent(std::move(event));
}
-void MediaLog::SetTimeProperty(
- const std::string& key, base::TimeDelta value) {
- scoped_ptr<MediaLogEvent> event(CreateEvent(MediaLogEvent::PROPERTY_CHANGE));
- if (value.is_max())
- event->params.SetString(key, "unknown");
- else
- event->params.SetDouble(key, value.InSecondsF());
- AddEvent(std::move(event));
-}
-
LogHelper::LogHelper(MediaLog::MediaLogLevel level,
const scoped_refptr<MediaLog>& media_log)
: level_(level), media_log_(media_log) {
diff --git a/chromium/media/base/media_log.h b/chromium/media/base/media_log.h
index c547ef776db..6ab18597eac 100644
--- a/chromium/media/base/media_log.h
+++ b/chromium/media/base/media_log.h
@@ -17,7 +17,7 @@
#include "base/memory/scoped_ptr.h"
#include "media/base/media_export.h"
#include "media/base/media_log_event.h"
-#include "media/base/pipeline.h"
+#include "media/base/pipeline_impl.h"
#include "media/base/pipeline_status.h"
namespace media {
@@ -44,6 +44,9 @@ class MEDIA_EXPORT MediaLog : public base::RefCountedThreadSafe<MediaLog> {
// with it.
virtual void AddEvent(scoped_ptr<MediaLogEvent> event);
+ // Retrieve an error message, if any.
+ virtual std::string GetLastErrorMessage();
+
// Helper methods to create events and their parameters.
scoped_ptr<MediaLogEvent> CreateEvent(MediaLogEvent::Type type);
scoped_ptr<MediaLogEvent> CreateBooleanEvent(
@@ -57,7 +60,7 @@ class MEDIA_EXPORT MediaLog : public base::RefCountedThreadSafe<MediaLog> {
scoped_ptr<MediaLogEvent> CreateLoadEvent(const std::string& url);
scoped_ptr<MediaLogEvent> CreateSeekEvent(float seconds);
scoped_ptr<MediaLogEvent> CreatePipelineStateChangedEvent(
- Pipeline::State state);
+ PipelineImpl::State state);
scoped_ptr<MediaLogEvent> CreatePipelineErrorEvent(PipelineStatus error);
scoped_ptr<MediaLogEvent> CreateVideoSizeSetEvent(
size_t width, size_t height);
@@ -70,10 +73,8 @@ class MEDIA_EXPORT MediaLog : public base::RefCountedThreadSafe<MediaLog> {
// Report a property change without an accompanying event.
void SetStringProperty(const std::string& key, const std::string& value);
- void SetIntegerProperty(const std::string& key, int value);
void SetDoubleProperty(const std::string& key, double value);
void SetBooleanProperty(const std::string& key, bool value);
- void SetTimeProperty(const std::string& key, base::TimeDelta value);
protected:
friend class base::RefCountedThreadSafe<MediaLog>;
diff --git a/chromium/media/base/media_switches.cc b/chromium/media/base/media_switches.cc
index bc5d67fb921..b3b34f4e7cb 100644
--- a/chromium/media/base/media_switches.cc
+++ b/chromium/media/base/media_switches.cc
@@ -22,13 +22,13 @@ const char kDisableMediaSuspend[] = "disable-media-suspend";
const char kDisableMediaThreadForMediaPlayback[] =
"disable-media-thread-for-media-playback";
+// Use WebMediaPlayerAndroid instead of WebMediaPlayerImpl. This is a temporary
+// switch for holding back the new unified media pipeline.
+const char kDisableUnifiedMediaPipeline[] = "disable-unified-media-pipeline";
+
// Sets the MediaSource player that uses the separate media thread
const char kEnableMediaThreadForMediaPlayback[] =
"enable-media-thread-for-media-playback";
-
-// Use WebMediaPlayerImpl instead of WebMediaPlayerAndroid. This is a temporary
-// switch for experimenting with unifying the Android playback pipeline.
-const char kEnableUnifiedMediaPipeline[] = "enable-unified-media-pipeline";
#endif
#if defined(OS_LINUX) || defined(OS_FREEBSD) || defined(OS_SOLARIS)
@@ -43,22 +43,6 @@ const char kAlsaOutputDevice[] = "alsa-output-device";
const char kUseGpuMemoryBuffersForCapture[] =
"use-gpu-memory-buffers-for-capture";
-#if defined(OS_MACOSX)
-// AVFoundation is available in versions 10.7 and onwards, and is to be used
-// http://crbug.com/288562 for both audio and video device monitoring and for
-// video capture. Being a dynamically loaded NSBundle and library, it hits the
-// Chrome startup time (http://crbug.com/311325 and http://crbug.com/311437);
-// for experimentation purposes, in particular library load time issue, the
-// usage of this library can be enabled by using this flag.
-const char kEnableAVFoundation[] = "enable-avfoundation";
-
-// QTKit is the media capture API predecessor to AVFoundation, available up and
-// until Mac OS X 10.9 (despite being deprecated in this last one). This flag
-// is used for troubleshooting and testing, and forces QTKit in builds and
-// configurations where AVFoundation would be used otherwise.
-const char kForceQTKit[] = "force-qtkit";
-#endif
-
#if defined(OS_WIN)
// Use exclusive mode audio streaming for Windows Vista and higher.
// Leads to lower latencies for audio streams which uses the
@@ -67,12 +51,6 @@ const char kForceQTKit[] = "force-qtkit";
// for details.
const char kEnableExclusiveAudio[] = "enable-exclusive-audio";
-// Used to troubleshoot problems with different video capture implementations
-// on Windows. By default we use the Media Foundation API on Windows 7 and up,
-// but specifying this switch will force use of DirectShow always.
-// See bug: http://crbug.com/268412
-const char kForceDirectShowVideoCapture[] = "force-directshow";
-
// Force the use of MediaFoundation for video capture. This is only supported in
// Windows 7 and above. Used, like |kForceDirectShowVideoCapture|, to
// troubleshoot problems in Windows platforms.
@@ -97,6 +75,13 @@ const char kWaveOutBuffers[] = "waveout-buffers";
const char kUseCras[] = "use-cras";
#endif
+#if !defined(OS_ANDROID)
+// Use a media session for each tabs in a way that two tabs can't play on top of
+// each other. This is different from the Media Session API as it is enabling a
+// default behaviour for the browser.
+const char kEnableDefaultMediaSession[] = "enable-default-media-session";
+#endif
+
// Use fake device for Media Stream to replace actual camera and microphone.
const char kUseFakeDeviceForMediaStream[] = "use-fake-device-for-media-stream";
@@ -129,7 +114,12 @@ const char kVideoUnderflowThresholdMs[] = "video-underflow-threshold-ms";
const char kDisableRTCSmoothnessAlgorithm[] =
"disable-rtc-smoothness-algorithm";
+} // namespace switches
+
+namespace media {
+
// Use shared block-based buffering for media.
-const char kUseNewMediaCache[] = "use-new-media-cache";
+const base::Feature kUseNewMediaCache{"use-new-media-cache",
+ base::FEATURE_DISABLED_BY_DEFAULT};
-} // namespace switches
+} // namespace media
diff --git a/chromium/media/base/media_switches.h b/chromium/media/base/media_switches.h
index e8c99b4b102..0ae1953f0f3 100644
--- a/chromium/media/base/media_switches.h
+++ b/chromium/media/base/media_switches.h
@@ -7,6 +7,7 @@
#ifndef MEDIA_BASE_MEDIA_SWITCHES_H_
#define MEDIA_BASE_MEDIA_SWITCHES_H_
+#include "base/feature_list.h"
#include "build/build_config.h"
#include "media/base/media_export.h"
@@ -21,8 +22,8 @@ MEDIA_EXPORT extern const char kDisableMediaSuspend[];
#if defined(OS_ANDROID)
MEDIA_EXPORT extern const char kDisableMediaThreadForMediaPlayback[];
+MEDIA_EXPORT extern const char kDisableUnifiedMediaPipeline[];
MEDIA_EXPORT extern const char kEnableMediaThreadForMediaPlayback[];
-MEDIA_EXPORT extern const char kEnableUnifiedMediaPipeline[];
#endif
#if defined(OS_LINUX) || defined(OS_FREEBSD) || defined(OS_SOLARIS)
@@ -32,14 +33,8 @@ MEDIA_EXPORT extern const char kAlsaOutputDevice[];
MEDIA_EXPORT extern const char kUseGpuMemoryBuffersForCapture[];
-#if defined(OS_MACOSX)
-MEDIA_EXPORT extern const char kEnableAVFoundation[];
-MEDIA_EXPORT extern const char kForceQTKit[];
-#endif
-
#if defined(OS_WIN)
MEDIA_EXPORT extern const char kEnableExclusiveAudio[];
-MEDIA_EXPORT extern const char kForceDirectShowVideoCapture[];
MEDIA_EXPORT extern const char kForceMediaFoundationVideoCapture[];
MEDIA_EXPORT extern const char kForceWaveAudio[];
MEDIA_EXPORT extern const char kTrySupportedChannelLayouts[];
@@ -50,6 +45,10 @@ MEDIA_EXPORT extern const char kWaveOutBuffers[];
MEDIA_EXPORT extern const char kUseCras[];
#endif
+#if !defined(OS_ANDROID)
+MEDIA_EXPORT extern const char kEnableDefaultMediaSession[];
+#endif
+
MEDIA_EXPORT extern const char kUseFakeDeviceForMediaStream[];
MEDIA_EXPORT extern const char kUseFileForFakeVideoCapture[];
MEDIA_EXPORT extern const char kUseFileForFakeAudioCapture[];
@@ -62,8 +61,14 @@ MEDIA_EXPORT extern const char kVideoUnderflowThresholdMs[];
MEDIA_EXPORT extern const char kDisableRTCSmoothnessAlgorithm[];
-MEDIA_EXPORT extern const char kUseNewMediaCache[];
-
} // namespace switches
+namespace media {
+
+// All features in alphabetical order. The features should be documented
+// alongside the definition of their values in the .cc file.
+MEDIA_EXPORT extern const base::Feature kUseNewMediaCache;
+
+} // namespace media
+
#endif // MEDIA_BASE_MEDIA_SWITCHES_H_
diff --git a/chromium/media/base/media_track.cc b/chromium/media/base/media_track.cc
new file mode 100644
index 00000000000..ad5178f2b02
--- /dev/null
+++ b/chromium/media/base/media_track.cc
@@ -0,0 +1,18 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/base/media_track.h"
+
+namespace media {
+
+MediaTrack::MediaTrack(Type type,
+ const std::string& id,
+ const std::string& kind,
+ const std::string& label,
+ const std::string& lang)
+ : type_(type), id_(id), kind_(kind), label_(label), language_(lang) {}
+
+MediaTrack::~MediaTrack() {}
+
+} // namespace media
diff --git a/chromium/media/base/media_track.h b/chromium/media/base/media_track.h
new file mode 100644
index 00000000000..a5cb6d3d818
--- /dev/null
+++ b/chromium/media/base/media_track.h
@@ -0,0 +1,41 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_BASE_MEDIA_TRACK_H_
+#define MEDIA_BASE_MEDIA_TRACK_H_
+
+#include <string>
+
+#include "media/base/media_export.h"
+
+namespace media {
+
+class MEDIA_EXPORT MediaTrack {
+ public:
+ enum Type { Text, Audio, Video };
+ MediaTrack(Type type,
+ const std::string& id,
+ const std::string& kind,
+ const std::string& label,
+ const std::string& lang);
+ ~MediaTrack();
+
+ Type type() const { return type_; }
+
+ const std::string& id() const { return id_; }
+ const std::string& kind() const { return kind_; }
+ const std::string& label() const { return label_; }
+ const std::string& language() const { return language_; }
+
+ private:
+ Type type_;
+ std::string id_;
+ std::string kind_;
+ std::string label_;
+ std::string language_;
+};
+
+} // namespace media
+
+#endif // MEDIA_BASE_MEDIA_TRACK_H_
diff --git a/chromium/media/base/media_tracks.cc b/chromium/media/base/media_tracks.cc
new file mode 100644
index 00000000000..832e5a822b1
--- /dev/null
+++ b/chromium/media/base/media_tracks.cc
@@ -0,0 +1,81 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/base/media_tracks.h"
+
+#include "base/bind.h"
+#include "media/base/audio_decoder_config.h"
+#include "media/base/video_decoder_config.h"
+
+namespace media {
+
+MediaTracks::MediaTracks() {}
+
+MediaTracks::~MediaTracks() {}
+
+void MediaTracks::AddAudioTrack(const AudioDecoderConfig& config,
+ const std::string& id,
+ const std::string& kind,
+ const std::string& label,
+ const std::string& language) {
+ DCHECK(config.IsValidConfig());
+ CHECK(audio_configs_.find(id) == audio_configs_.end());
+ scoped_ptr<MediaTrack> track = make_scoped_ptr(
+ new MediaTrack(MediaTrack::Audio, id, kind, label, language));
+ tracks_.push_back(std::move(track));
+ audio_configs_[id] = config;
+}
+
+void MediaTracks::AddVideoTrack(const VideoDecoderConfig& config,
+ const std::string& id,
+ const std::string& kind,
+ const std::string& label,
+ const std::string& language) {
+ DCHECK(config.IsValidConfig());
+ CHECK(video_configs_.find(id) == video_configs_.end());
+ scoped_ptr<MediaTrack> track = make_scoped_ptr(
+ new MediaTrack(MediaTrack::Video, id, kind, label, language));
+ tracks_.push_back(std::move(track));
+ video_configs_[id] = config;
+}
+
+const AudioDecoderConfig& MediaTracks::getAudioConfig(
+ const std::string& id) const {
+ auto it = audio_configs_.find(id);
+ if (it != audio_configs_.end())
+ return it->second;
+ static AudioDecoderConfig invalidConfig;
+ return invalidConfig;
+}
+
+const VideoDecoderConfig& MediaTracks::getVideoConfig(
+ const std::string& id) const {
+ auto it = video_configs_.find(id);
+ if (it != video_configs_.end())
+ return it->second;
+ static VideoDecoderConfig invalidConfig;
+ return invalidConfig;
+}
+
+const AudioDecoderConfig& MediaTracks::getFirstAudioConfig() const {
+ for (const auto& track : tracks()) {
+ if (track->type() == MediaTrack::Audio) {
+ return getAudioConfig(track->id());
+ }
+ }
+ static AudioDecoderConfig invalidConfig;
+ return invalidConfig;
+}
+
+const VideoDecoderConfig& MediaTracks::getFirstVideoConfig() const {
+ for (const auto& track : tracks()) {
+ if (track->type() == MediaTrack::Video) {
+ return getVideoConfig(track->id());
+ }
+ }
+ static VideoDecoderConfig invalidConfig;
+ return invalidConfig;
+}
+
+} // namespace media
diff --git a/chromium/media/base/media_tracks.h b/chromium/media/base/media_tracks.h
new file mode 100644
index 00000000000..4e0fbb0fc1f
--- /dev/null
+++ b/chromium/media/base/media_tracks.h
@@ -0,0 +1,62 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_BASE_MEDIA_TRACKS_H_
+#define MEDIA_BASE_MEDIA_TRACKS_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "media/base/media_export.h"
+#include "media/base/media_track.h"
+
+namespace media {
+
+class AudioDecoderConfig;
+class VideoDecoderConfig;
+
+class MEDIA_EXPORT MediaTracks {
+ public:
+ typedef std::vector<scoped_ptr<MediaTrack>> MediaTracksCollection;
+
+ MediaTracks();
+ ~MediaTracks();
+
+ // Callers need to ensure that track id is unique.
+ void AddAudioTrack(const AudioDecoderConfig& config,
+ const std::string& id,
+ const std::string& kind,
+ const std::string& label,
+ const std::string& language);
+ // Callers need to ensure that track id is unique.
+ void AddVideoTrack(const VideoDecoderConfig& config,
+ const std::string& id,
+ const std::string& kind,
+ const std::string& label,
+ const std::string& language);
+
+ const MediaTracksCollection& tracks() const { return tracks_; }
+
+ const AudioDecoderConfig& getAudioConfig(const std::string& id) const;
+ const VideoDecoderConfig& getVideoConfig(const std::string& id) const;
+
+ // TODO(servolk): These are temporary helpers useful until all code paths are
+ // converted to properly handle multiple media tracks.
+ const AudioDecoderConfig& getFirstAudioConfig() const;
+ const VideoDecoderConfig& getFirstVideoConfig() const;
+
+ private:
+ MediaTracksCollection tracks_;
+ std::map<std::string, AudioDecoderConfig> audio_configs_;
+ std::map<std::string, VideoDecoderConfig> video_configs_;
+
+ DISALLOW_COPY_AND_ASSIGN(MediaTracks);
+};
+
+} // namespace media
+
+#endif // MEDIA_BASE_MEDIA_TRACKS_H_
diff --git a/chromium/media/base/media_util.cc b/chromium/media/base/media_util.cc
index bd7929f5609..a6516cd2305 100644
--- a/chromium/media/base/media_util.cc
+++ b/chromium/media/base/media_util.cc
@@ -10,4 +10,13 @@ std::vector<uint8_t> EmptyExtraData() {
return std::vector<uint8_t>();
}
+EncryptionScheme Unencrypted() {
+ return EncryptionScheme();
+}
+
+EncryptionScheme AesCtrEncryptionScheme() {
+ return EncryptionScheme(EncryptionScheme::CIPHER_MODE_AES_CTR,
+ EncryptionScheme::Pattern());
+}
+
} // namespace media
diff --git a/chromium/media/base/media_util.h b/chromium/media/base/media_util.h
index 4e53c9a0c0e..c7ddc8bb1ea 100644
--- a/chromium/media/base/media_util.h
+++ b/chromium/media/base/media_util.h
@@ -8,6 +8,7 @@
#include <stdint.h>
#include <vector>
+#include "media/base/encryption_scheme.h"
#include "media/base/media_export.h"
namespace media {
@@ -16,6 +17,11 @@ namespace media {
// constructed with empty extra data.
MEDIA_EXPORT std::vector<uint8_t> EmptyExtraData();
+// The following helper functions return new instances of EncryptionScheme that
+// indicate widely used settings.
+MEDIA_EXPORT EncryptionScheme Unencrypted();
+MEDIA_EXPORT EncryptionScheme AesCtrEncryptionScheme();
+
} // namespace media
#endif // MEDIA_BASE_UTIL_H_
diff --git a/chromium/media/base/mime_util.cc b/chromium/media/base/mime_util.cc
index ff1d3369e1d..6eba4fc70b7 100644
--- a/chromium/media/base/mime_util.cc
+++ b/chromium/media/base/mime_util.cc
@@ -2,742 +2,42 @@
// 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 <map>
-
-#include "base/containers/hash_tables.h"
-#include "base/lazy_instance.h"
-#include "base/macros.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_split.h"
-#include "base/strings/string_util.h"
-#include "build/build_config.h"
#include "media/base/mime_util.h"
-#include "media/media_features.h"
-#if defined(OS_ANDROID)
-#include "base/android/build_info.h"
-#endif
+#include "base/lazy_instance.h"
+#include "media/base/mime_util_internal.h"
namespace media {
-// Singleton utility class for mime types.
-class MimeUtil {
- public:
- enum Codec {
- INVALID_CODEC,
- PCM,
- MP3,
- AC3,
- EAC3,
- MPEG2_AAC_LC,
- MPEG2_AAC_MAIN,
- MPEG2_AAC_SSR,
- MPEG4_AAC_LC,
- MPEG4_AAC_SBR_v1,
- MPEG4_AAC_SBR_PS_v2,
- VORBIS,
- OPUS,
- H264_BASELINE,
- H264_MAIN,
- H264_HIGH,
- HEVC_MAIN,
- VP8,
- VP9,
- THEORA
- };
-
- bool IsSupportedMediaMimeType(const std::string& mime_type) const;
-
- void ParseCodecString(const std::string& codecs,
- std::vector<std::string>* codecs_out,
- bool strip);
-
- SupportsType IsSupportedMediaFormat(
- const std::string& mime_type,
- const std::vector<std::string>& codecs) const;
-
- void RemoveProprietaryMediaTypesAndCodecsForTests();
-
- private:
- friend struct base::DefaultLazyInstanceTraits<MimeUtil>;
-
- typedef base::hash_set<int> CodecSet;
- typedef std::map<std::string, CodecSet> MediaFormatMappings;
- struct CodecEntry {
- CodecEntry() : codec(INVALID_CODEC), is_ambiguous(true) {}
- CodecEntry(Codec c, bool ambiguous) : codec(c), is_ambiguous(ambiguous) {}
- Codec codec;
- bool is_ambiguous;
- };
- typedef std::map<std::string, CodecEntry> StringToCodecMappings;
-
- MimeUtil();
-
- // For faster lookup, keep hash sets.
- void InitializeMimeTypeMaps();
-
- // Returns IsSupported if all codec IDs in |codecs| are unambiguous
- // and are supported by the platform. MayBeSupported is returned if
- // at least one codec ID in |codecs| is ambiguous but all the codecs
- // are supported by the platform. IsNotSupported is returned if at
- // least one codec ID is not supported by the platform.
- SupportsType AreSupportedCodecs(
- const CodecSet& supported_codecs,
- const std::vector<std::string>& codecs) const;
-
- // Converts a codec ID into an Codec enum value and indicates
- // whether the conversion was ambiguous.
- // Returns true if this method was able to map |codec_id| to a specific
- // Codec enum value. |codec| and |is_ambiguous| are only valid if true
- // is returned. Otherwise their value is undefined after the call.
- // |is_ambiguous| is true if |codec_id| did not have enough information to
- // unambiguously determine the proper Codec enum value. If |is_ambiguous|
- // is true |codec| contains the best guess for the intended Codec enum value.
- bool StringToCodec(const std::string& codec_id,
- Codec* codec,
- bool* is_ambiguous) const;
-
- // Returns true if |codec| is supported by the platform.
- // Note: This method will return false if the platform supports proprietary
- // codecs but |allow_proprietary_codecs_| is set to false.
- bool IsCodecSupported(Codec codec) const;
-
- // Returns true if |codec| refers to a proprietary codec.
- bool IsCodecProprietary(Codec codec) const;
-
- // Returns true and sets |*default_codec| if |mime_type| has a default codec
- // associated with it. Returns false otherwise and the value of
- // |*default_codec| is undefined.
- bool GetDefaultCodecLowerCase(const std::string& mime_type_lower_case,
- Codec* default_codec) const;
-
- // Returns true if |mime_type_lower_case| has a default codec associated with
- // it and IsCodecSupported() returns true for that particular codec.
- bool IsDefaultCodecSupportedLowerCase(
- const std::string& mime_type_lower_case) const;
-
- // A map of mime_types and hash map of the supported codecs for the mime_type.
- MediaFormatMappings media_format_map_;
-
- // Keeps track of whether proprietary codec support should be
- // advertised to callers.
- bool allow_proprietary_codecs_;
-
- // Lookup table for string compare based string -> Codec mappings.
- StringToCodecMappings string_to_codec_map_;
-
- DISALLOW_COPY_AND_ASSIGN(MimeUtil);
-}; // class MimeUtil
-
// This variable is Leaky because it is accessed from WorkerPool threads.
-static base::LazyInstance<MimeUtil>::Leaky g_media_mime_util =
+static base::LazyInstance<internal::MimeUtil>::Leaky g_media_mime_util =
LAZY_INSTANCE_INITIALIZER;
-#if defined(OS_ANDROID)
-static bool IsCodecSupportedOnAndroid(MimeUtil::Codec codec) {
- switch (codec) {
- case MimeUtil::INVALID_CODEC:
- return false;
-
- case MimeUtil::PCM:
- case MimeUtil::MP3:
- case MimeUtil::MPEG4_AAC_LC:
- case MimeUtil::MPEG4_AAC_SBR_v1:
- case MimeUtil::MPEG4_AAC_SBR_PS_v2:
- case MimeUtil::VORBIS:
- case MimeUtil::H264_BASELINE:
- case MimeUtil::H264_MAIN:
- case MimeUtil::H264_HIGH:
- case MimeUtil::VP8:
- return true;
-
- case MimeUtil::AC3:
- case MimeUtil::EAC3:
- // TODO(servolk): Revisit this for AC3/EAC3 support on AndroidTV
- return false;
-
- case MimeUtil::MPEG2_AAC_LC:
- case MimeUtil::MPEG2_AAC_MAIN:
- case MimeUtil::MPEG2_AAC_SSR:
- // MPEG-2 variants of AAC are not supported on Android.
- return false;
-
- case MimeUtil::OPUS:
- // Opus is supported only in Lollipop+ (API Level 21).
- return base::android::BuildInfo::GetInstance()->sdk_int() >= 21;
-
- case MimeUtil::HEVC_MAIN:
-#if BUILDFLAG(ENABLE_HEVC_DEMUXING)
- // HEVC/H.265 is supported in Lollipop+ (API Level 21), according to
- // http://developer.android.com/reference/android/media/MediaFormat.html
- return base::android::BuildInfo::GetInstance()->sdk_int() >= 21;
-#else
- return false;
-#endif
-
- case MimeUtil::VP9:
- // VP9 is supported only in KitKat+ (API Level 19).
- return base::android::BuildInfo::GetInstance()->sdk_int() >= 19;
-
- case MimeUtil::THEORA:
- return false;
- }
-
- return false;
-}
-#endif
-
-enum MediaFormatType { COMMON, PROPRIETARY };
-
-struct MediaFormat {
- const char* const mime_type;
- MediaFormatType format_type;
- const char* const codecs_list;
-};
-
-#if defined(USE_PROPRIETARY_CODECS)
-// Following is the list of RFC 6381 compliant codecs:
-// mp4a.66 - MPEG-2 AAC MAIN
-// mp4a.67 - MPEG-2 AAC LC
-// mp4a.68 - MPEG-2 AAC SSR
-// mp4a.69 - MPEG-2 extension to MPEG-1
-// mp4a.6B - MPEG-1 audio
-// mp4a.40.2 - MPEG-4 AAC LC
-// mp4a.40.02 - MPEG-4 AAC LC (leading 0 in aud-oti for compatibility)
-// mp4a.40.5 - MPEG-4 HE-AAC v1 (AAC LC + SBR)
-// mp4a.40.05 - MPEG-4 HE-AAC v1 (AAC LC + SBR) (leading 0 in aud-oti for
-// compatibility)
-// mp4a.40.29 - MPEG-4 HE-AAC v2 (AAC LC + SBR + PS)
-//
-// avc1.42E0xx - H.264 Baseline
-// avc1.4D40xx - H.264 Main
-// avc1.6400xx - H.264 High
-static const char kMP4AudioCodecsExpression[] =
- "mp4a.66,mp4a.67,mp4a.68,mp4a.69,mp4a.6B,mp4a.40.2,mp4a.40.02,mp4a.40.5,"
-#if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING)
- // Only one variant each of ac3 and eac3 codec string is sufficient here,
- // since these strings are parsed and mapped to MimeUtil::Codec enum values.
- "ac-3,ec-3,"
-#endif
- "mp4a.40.05,mp4a.40.29";
-static const char kMP4VideoCodecsExpression[] =
- // This is not a complete list of supported avc1 codecs. It is simply used
- // to register support for the corresponding Codec enum. Instead of using
- // strings in these three arrays, we should use the Codec enum values.
- // This will avoid confusion and unnecessary parsing at runtime.
- // kUnambiguousCodecStringMap/kAmbiguousCodecStringMap should be the only
- // mapping from strings to codecs. See crbug.com/461009.
- "avc1.42E00A,avc1.4D400A,avc1.64000A,"
-#if BUILDFLAG(ENABLE_HEVC_DEMUXING)
- // Any valid unambiguous HEVC codec id will work here, since these strings
- // are parsed and mapped to MimeUtil::Codec enum values.
- "hev1.1.6.L93.B0,"
-#endif
- "mp4a.66,mp4a.67,mp4a.68,mp4a.69,mp4a.6B,mp4a.40.2,mp4a.40.02,mp4a.40.5,"
-#if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING)
- // Only one variant each of ac3 and eac3 codec string is sufficient here,
- // since these strings are parsed and mapped to MimeUtil::Codec enum values.
- "ac-3,ec-3,"
-#endif
- "mp4a.40.05,mp4a.40.29";
-#endif // USE_PROPRIETARY_CODECS
-
-// A list of media types (https://en.wikipedia.org/wiki/Media_type) and
-// corresponding media codecs supported by these types/containers.
-// Media formats marked as PROPRIETARY are not supported by Chromium, only
-// Google Chrome browser supports them.
-static const MediaFormat kFormatCodecMappings[] = {
- {"video/webm", COMMON, "opus,vorbis,vp8,vp8.0,vp9,vp9.0"},
- {"audio/webm", COMMON, "opus,vorbis"},
- {"audio/wav", COMMON, "1"},
- {"audio/x-wav", COMMON, "1"},
-#if defined(OS_ANDROID)
- // Android does not support Opus in Ogg container.
- // Android does not support Theora and thus video/ogg.
- {"audio/ogg", COMMON, "vorbis"},
- {"application/ogg", COMMON, "vorbis"},
-#else
- {"video/ogg", COMMON, "opus,theora,vorbis"},
- {"audio/ogg", COMMON, "opus,vorbis"},
- {"application/ogg", COMMON, "opus,theora,vorbis"},
-#endif
-#if defined(USE_PROPRIETARY_CODECS)
- {"audio/mpeg", PROPRIETARY, "mp3"},
- {"audio/mp3", PROPRIETARY, ""},
- {"audio/x-mp3", PROPRIETARY, ""},
- {"audio/aac", PROPRIETARY, ""}, // AAC / ADTS
- {"audio/mp4", PROPRIETARY, kMP4AudioCodecsExpression},
- {"audio/x-m4a", PROPRIETARY, kMP4AudioCodecsExpression},
- {"video/mp4", PROPRIETARY, kMP4VideoCodecsExpression},
- {"video/x-m4v", PROPRIETARY, kMP4VideoCodecsExpression},
-#if BUILDFLAG(ENABLE_MSE_MPEG2TS_STREAM_PARSER)
- {"video/mp2t", PROPRIETARY, kMP4VideoCodecsExpression},
-#endif
-#if defined(OS_ANDROID)
- // HTTP Live Streaming (HLS)
- {"application/x-mpegurl", PROPRIETARY, kMP4VideoCodecsExpression},
- {"application/vnd.apple.mpegurl", PROPRIETARY, kMP4VideoCodecsExpression}
-#endif
-#endif // USE_PROPRIETARY_CODECS
-};
-
-struct CodecIDMappings {
- const char* const codec_id;
- MimeUtil::Codec codec;
-};
-
-// List of codec IDs that provide enough information to determine the
-// codec and profile being requested.
-//
-// The "mp4a" strings come from RFC 6381.
-static const CodecIDMappings kUnambiguousCodecStringMap[] = {
- {"1", MimeUtil::PCM}, // We only allow this for WAV so it isn't ambiguous.
- // avc1/avc3.XXXXXX may be unambiguous; handled by ParseH264CodecID().
- // hev1/hvc1.XXXXXX may be unambiguous; handled by ParseHEVCCodecID().
- {"mp3", MimeUtil::MP3},
- {"mp4a.66", MimeUtil::MPEG2_AAC_MAIN},
- {"mp4a.67", MimeUtil::MPEG2_AAC_LC},
- {"mp4a.68", MimeUtil::MPEG2_AAC_SSR},
- {"mp4a.69", MimeUtil::MP3},
- {"mp4a.6B", MimeUtil::MP3},
- {"mp4a.40.2", MimeUtil::MPEG4_AAC_LC},
- {"mp4a.40.02", MimeUtil::MPEG4_AAC_LC},
- {"mp4a.40.5", MimeUtil::MPEG4_AAC_SBR_v1},
- {"mp4a.40.05", MimeUtil::MPEG4_AAC_SBR_v1},
- {"mp4a.40.29", MimeUtil::MPEG4_AAC_SBR_PS_v2},
-#if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING)
- // TODO(servolk): Strictly speaking only mp4a.A5 and mp4a.A6 codec ids are
- // valid according to RFC 6381 section 3.3, 3.4. Lower-case oti (mp4a.a5 and
- // mp4a.a6) should be rejected. But we used to allow those in older versions
- // of Chromecast firmware and some apps (notably MPL) depend on those codec
- // types being supported, so they should be allowed for now
- // (crbug.com/564960).
- {"ac-3", MimeUtil::AC3},
- {"mp4a.a5", MimeUtil::AC3},
- {"mp4a.A5", MimeUtil::AC3},
- {"ec-3", MimeUtil::EAC3},
- {"mp4a.a6", MimeUtil::EAC3},
- {"mp4a.A6", MimeUtil::EAC3},
-#endif
- {"vorbis", MimeUtil::VORBIS},
- {"opus", MimeUtil::OPUS},
- {"vp8", MimeUtil::VP8},
- {"vp8.0", MimeUtil::VP8},
- {"vp9", MimeUtil::VP9},
- {"vp9.0", MimeUtil::VP9},
- {"theora", MimeUtil::THEORA}};
-
-// List of codec IDs that are ambiguous and don't provide
-// enough information to determine the codec and profile.
-// The codec in these entries indicate the codec and profile
-// we assume the user is trying to indicate.
-static const CodecIDMappings kAmbiguousCodecStringMap[] = {
- {"mp4a.40", MimeUtil::MPEG4_AAC_LC},
- {"avc1", MimeUtil::H264_BASELINE},
- {"avc3", MimeUtil::H264_BASELINE},
- // avc1/avc3.XXXXXX may be ambiguous; handled by ParseH264CodecID().
-};
-
-#if BUILDFLAG(ENABLE_MSE_MPEG2TS_STREAM_PARSER)
-static const char kHexString[] = "0123456789ABCDEF";
-static char IntToHex(int i) {
- DCHECK_GE(i, 0) << i << " not a hex value";
- DCHECK_LE(i, 15) << i << " not a hex value";
- return kHexString[i];
-}
-
-std::string TranslateLegacyAvc1CodecIds(const std::string& codec_id) {
- // Special handling for old, pre-RFC 6381 format avc1 strings, which are still
- // being used by some HLS apps to preserve backward compatibility with older
- // iOS devices. The old format was avc1.<profile>.<level>
- // Where <profile> is H.264 profile_idc encoded as a decimal number, i.e.
- // 66 is baseline profile (0x42)
- // 77 is main profile (0x4d)
- // 100 is high profile (0x64)
- // And <level> is H.264 level multiplied by 10, also encoded as decimal number
- // E.g. <level> 31 corresponds to H.264 level 3.1
- // See, for example, http://qtdevseed.apple.com/qadrift/testcases/tc-0133.php
- uint32_t level_start = 0;
- std::string result;
- if (base::StartsWith(codec_id, "avc1.66.", base::CompareCase::SENSITIVE)) {
- level_start = 8;
- result = "avc1.4200";
- } else if (base::StartsWith(codec_id, "avc1.77.",
- base::CompareCase::SENSITIVE)) {
- level_start = 8;
- result = "avc1.4D00";
- } else if (base::StartsWith(codec_id, "avc1.100.",
- base::CompareCase::SENSITIVE)) {
- level_start = 9;
- result = "avc1.6400";
- }
-
- uint32_t level = 0;
- if (level_start > 0 &&
- base::StringToUint(codec_id.substr(level_start), &level) && level < 256) {
- // This is a valid legacy avc1 codec id - return the codec id translated
- // into RFC 6381 format.
- result.push_back(IntToHex(level >> 4));
- result.push_back(IntToHex(level & 0xf));
- return result;
- }
-
- // This is not a valid legacy avc1 codec id - return the original codec id.
- return codec_id;
-}
-#endif
-
-MimeUtil::MimeUtil() : allow_proprietary_codecs_(false) {
- InitializeMimeTypeMaps();
-}
-
-SupportsType MimeUtil::AreSupportedCodecs(
- const CodecSet& supported_codecs,
- const std::vector<std::string>& codecs) const {
- DCHECK(!supported_codecs.empty());
- DCHECK(!codecs.empty());
-
- SupportsType result = IsSupported;
- for (size_t i = 0; i < codecs.size(); ++i) {
- bool is_ambiguous = true;
- Codec codec = INVALID_CODEC;
- if (!StringToCodec(codecs[i], &codec, &is_ambiguous))
- return IsNotSupported;
-
- if (!IsCodecSupported(codec) ||
- supported_codecs.find(codec) == supported_codecs.end()) {
- return IsNotSupported;
- }
-
- if (is_ambiguous)
- result = MayBeSupported;
- }
-
- return result;
-}
-
-void MimeUtil::InitializeMimeTypeMaps() {
- // Initialize the supported media types.
-#if defined(USE_PROPRIETARY_CODECS)
- allow_proprietary_codecs_ = true;
-#endif
-
- for (size_t i = 0; i < arraysize(kUnambiguousCodecStringMap); ++i) {
- string_to_codec_map_[kUnambiguousCodecStringMap[i].codec_id] =
- CodecEntry(kUnambiguousCodecStringMap[i].codec, false);
- }
-
- for (size_t i = 0; i < arraysize(kAmbiguousCodecStringMap); ++i) {
- string_to_codec_map_[kAmbiguousCodecStringMap[i].codec_id] =
- CodecEntry(kAmbiguousCodecStringMap[i].codec, true);
- }
-
- // Initialize the supported media formats.
- for (size_t i = 0; i < arraysize(kFormatCodecMappings); ++i) {
- std::vector<std::string> mime_type_codecs;
- ParseCodecString(kFormatCodecMappings[i].codecs_list, &mime_type_codecs,
- false);
-
- CodecSet codecs;
- for (size_t j = 0; j < mime_type_codecs.size(); ++j) {
- Codec codec = INVALID_CODEC;
- bool is_ambiguous = true;
- CHECK(StringToCodec(mime_type_codecs[j], &codec, &is_ambiguous));
- DCHECK(!is_ambiguous);
- codecs.insert(codec);
- }
-
- media_format_map_[kFormatCodecMappings[i].mime_type] = codecs;
- }
-}
-
-bool MimeUtil::IsSupportedMediaMimeType(const std::string& mime_type) const {
- return media_format_map_.find(base::ToLowerASCII(mime_type)) !=
- media_format_map_.end();
-}
-
-void MimeUtil::ParseCodecString(const std::string& codecs,
- std::vector<std::string>* codecs_out,
- bool strip) {
- *codecs_out = base::SplitString(
- base::TrimString(codecs, "\"", base::TRIM_ALL),
- ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
-
- // Convert empty or all-whitespace input to 0 results.
- if (codecs_out->size() == 1 && (*codecs_out)[0].empty())
- codecs_out->clear();
-
- if (!strip)
- return;
-
- // Strip everything past the first '.'
- for (std::vector<std::string>::iterator it = codecs_out->begin();
- it != codecs_out->end();
- ++it) {
- size_t found = it->find_first_of('.');
- if (found != std::string::npos)
- it->resize(found);
- }
-}
-
-SupportsType MimeUtil::IsSupportedMediaFormat(
- const std::string& mime_type,
- const std::vector<std::string>& codecs) const {
- const std::string mime_type_lower_case = base::ToLowerASCII(mime_type);
- MediaFormatMappings::const_iterator it_media_format_map =
- media_format_map_.find(mime_type_lower_case);
- if (it_media_format_map == media_format_map_.end())
- return IsNotSupported;
-
- if (it_media_format_map->second.empty()) {
- // We get here if the mimetype does not expect a codecs parameter.
- return (codecs.empty() &&
- IsDefaultCodecSupportedLowerCase(mime_type_lower_case))
- ? IsSupported
- : IsNotSupported;
- }
-
- if (codecs.empty()) {
- // We get here if the mimetype expects to get a codecs parameter,
- // but didn't get one. If |mime_type_lower_case| does not have a default
- // codec the best we can do is say "maybe" because we don't have enough
- // information.
- Codec default_codec = INVALID_CODEC;
- if (!GetDefaultCodecLowerCase(mime_type_lower_case, &default_codec))
- return MayBeSupported;
-
- return IsCodecSupported(default_codec) ? IsSupported : IsNotSupported;
- }
-
-#if BUILDFLAG(ENABLE_MSE_MPEG2TS_STREAM_PARSER)
- if (mime_type_lower_case == "video/mp2t") {
- std::vector<std::string> codecs_to_check;
- for (const auto& codec_id : codecs) {
- codecs_to_check.push_back(TranslateLegacyAvc1CodecIds(codec_id));
- }
- return AreSupportedCodecs(it_media_format_map->second, codecs_to_check);
- }
-#endif
-
- return AreSupportedCodecs(it_media_format_map->second, codecs);
-}
-
-void MimeUtil::RemoveProprietaryMediaTypesAndCodecsForTests() {
- for (size_t i = 0; i < arraysize(kFormatCodecMappings); ++i)
- if (kFormatCodecMappings[i].format_type == PROPRIETARY)
- media_format_map_.erase(kFormatCodecMappings[i].mime_type);
- allow_proprietary_codecs_ = false;
-}
-
-static bool IsValidH264Level(const std::string& level_str) {
- uint32_t level;
- if (level_str.size() != 2 || !base::HexStringToUInt(level_str, &level))
- return false;
-
- // Valid levels taken from Table A-1 in ISO-14496-10.
- // Essentially |level_str| is toHex(10 * level).
- return ((level >= 10 && level <= 13) ||
- (level >= 20 && level <= 22) ||
- (level >= 30 && level <= 32) ||
- (level >= 40 && level <= 42) ||
- (level >= 50 && level <= 51));
-}
-
-// Handle parsing H.264 codec IDs as outlined in RFC 6381 and ISO-14496-10.
-// avc1.42x0yy - H.264 Baseline
-// avc1.4Dx0yy - H.264 Main
-// avc1.64x0yy - H.264 High
-//
-// avc1.xxxxxx & avc3.xxxxxx are considered ambiguous forms that are trying to
-// signal H.264 Baseline. For example, the idc_level, profile_idc and
-// constraint_set3_flag pieces may explicitly require decoder to conform to
-// baseline profile at the specified level (see Annex A and constraint_set0 in
-// ISO-14496-10).
-static bool ParseH264CodecID(const std::string& codec_id,
- MimeUtil::Codec* codec,
- bool* is_ambiguous) {
- // Make sure we have avc1.xxxxxx or avc3.xxxxxx , where xxxxxx are hex digits
- if (!base::StartsWith(codec_id, "avc1.", base::CompareCase::SENSITIVE) &&
- !base::StartsWith(codec_id, "avc3.", base::CompareCase::SENSITIVE)) {
- return false;
- }
- if (codec_id.size() != 11 ||
- !base::IsHexDigit(codec_id[5]) || !base::IsHexDigit(codec_id[6]) ||
- !base::IsHexDigit(codec_id[7]) || !base::IsHexDigit(codec_id[8]) ||
- !base::IsHexDigit(codec_id[9]) || !base::IsHexDigit(codec_id[10])) {
- return false;
- }
-
- // Validate constraint flags and reserved bits.
- if (!base::IsHexDigit(codec_id[7]) || codec_id[8] != '0') {
- *codec = MimeUtil::H264_BASELINE;
- *is_ambiguous = true;
- return true;
- }
-
- // Extract the profile.
- std::string profile = base::ToUpperASCII(codec_id.substr(5, 2));
- if (profile == "42") {
- *codec = MimeUtil::H264_BASELINE;
- } else if (profile == "4D") {
- *codec = MimeUtil::H264_MAIN;
- } else if (profile == "64") {
- *codec = MimeUtil::H264_HIGH;
- } else {
- *codec = MimeUtil::H264_BASELINE;
- *is_ambiguous = true;
- return true;
- }
-
- // Validate level.
- *is_ambiguous = !IsValidH264Level(codec_id.substr(9));
- return true;
-}
-
-#if BUILDFLAG(ENABLE_HEVC_DEMUXING)
-// ISO/IEC FDIS 14496-15 standard section E.3 describes the syntax of codec ids
-// reserved for HEVC. According to that spec HEVC codec id must start with
-// either "hev1." or "hvc1.". We don't yet support full parsing of HEVC codec
-// ids, but since no other codec id starts with those string we'll just treat
-// any string starting with "hev1." or "hvc1." as valid HEVC codec ids.
-// crbug.com/482761
-static bool ParseHEVCCodecID(const std::string& codec_id,
- MimeUtil::Codec* codec,
- bool* is_ambiguous) {
- if (base::StartsWith(codec_id, "hev1.", base::CompareCase::SENSITIVE) ||
- base::StartsWith(codec_id, "hvc1.", base::CompareCase::SENSITIVE)) {
- *codec = MimeUtil::HEVC_MAIN;
-
- // TODO(servolk): Full HEVC codec id parsing is not implemented yet (see
- // crbug.com/482761). So treat HEVC codec ids as ambiguous for now.
- *is_ambiguous = true;
-
- // TODO(servolk): Most HEVC codec ids are treated as ambiguous (see above),
- // but we need to recognize at least one valid unambiguous HEVC codec id,
- // which is added into kMP4VideoCodecsExpression. We need it to be
- // unambiguous to avoid DCHECK(!is_ambiguous) in InitializeMimeTypeMaps. We
- // also use these in unit tests (see
- // content/browser/media/media_canplaytype_browsertest.cc).
- // Remove this workaround after crbug.com/482761 is fixed.
- if (codec_id == "hev1.1.6.L93.B0" || codec_id == "hvc1.1.6.L93.B0") {
- *is_ambiguous = false;
- }
-
- return true;
- }
-
- return false;
-}
-#endif
-
-bool MimeUtil::StringToCodec(const std::string& codec_id,
- Codec* codec,
- bool* is_ambiguous) const {
- StringToCodecMappings::const_iterator itr =
- string_to_codec_map_.find(codec_id);
- if (itr != string_to_codec_map_.end()) {
- *codec = itr->second.codec;
- *is_ambiguous = itr->second.is_ambiguous;
- return true;
- }
-
- // If |codec_id| is not in |string_to_codec_map_|, then we assume that it is
- // either H.264 or HEVC/H.265 codec ID because currently those are the only
- // ones that are not added to the |string_to_codec_map_| and require parsing.
-#if BUILDFLAG(ENABLE_HEVC_DEMUXING)
- if (ParseHEVCCodecID(codec_id, codec, is_ambiguous)) {
- return true;
- }
-#endif
- return ParseH264CodecID(codec_id, codec, is_ambiguous);
-}
-
-bool MimeUtil::IsCodecSupported(Codec codec) const {
- DCHECK_NE(codec, INVALID_CODEC);
-
-#if defined(OS_ANDROID)
- if (!IsCodecSupportedOnAndroid(codec))
- return false;
-#endif
-
- return allow_proprietary_codecs_ || !IsCodecProprietary(codec);
-}
-
-bool MimeUtil::IsCodecProprietary(Codec codec) const {
- switch (codec) {
- case INVALID_CODEC:
- case AC3:
- case EAC3:
- case MP3:
- case MPEG2_AAC_LC:
- case MPEG2_AAC_MAIN:
- case MPEG2_AAC_SSR:
- case MPEG4_AAC_LC:
- case MPEG4_AAC_SBR_v1:
- case MPEG4_AAC_SBR_PS_v2:
- case H264_BASELINE:
- case H264_MAIN:
- case H264_HIGH:
- case HEVC_MAIN:
- return true;
-
- case PCM:
- case VORBIS:
- case OPUS:
- case VP8:
- case VP9:
- case THEORA:
- return false;
- }
-
- return true;
-}
-
-bool MimeUtil::GetDefaultCodecLowerCase(const std::string& mime_type_lower_case,
- Codec* default_codec) const {
- if (mime_type_lower_case == "audio/mpeg" ||
- mime_type_lower_case == "audio/mp3" ||
- mime_type_lower_case == "audio/x-mp3") {
- *default_codec = MimeUtil::MP3;
- return true;
- }
-
- if (mime_type_lower_case == "audio/aac") {
- *default_codec = MimeUtil::MPEG4_AAC_LC;
- return true;
- }
-
- return false;
-}
-
-bool MimeUtil::IsDefaultCodecSupportedLowerCase(
- const std::string& mime_type_lower_case) const {
- Codec default_codec = Codec::INVALID_CODEC;
- if (!GetDefaultCodecLowerCase(mime_type_lower_case, &default_codec))
- return false;
- return IsCodecSupported(default_codec);
-}
-
bool IsSupportedMediaMimeType(const std::string& mime_type) {
- return g_media_mime_util.Get().IsSupportedMediaMimeType(mime_type);
+ return g_media_mime_util.Pointer()->IsSupportedMediaMimeType(mime_type);
}
SupportsType IsSupportedMediaFormat(const std::string& mime_type,
const std::vector<std::string>& codecs) {
- return g_media_mime_util.Get().IsSupportedMediaFormat(mime_type, codecs);
+ return g_media_mime_util.Pointer()->IsSupportedMediaFormat(mime_type, codecs,
+ false);
+}
+
+SupportsType IsSupportedEncryptedMediaFormat(
+ const std::string& mime_type,
+ const std::vector<std::string>& codecs) {
+ return g_media_mime_util.Pointer()->IsSupportedMediaFormat(mime_type, codecs,
+ true);
}
void ParseCodecString(const std::string& codecs,
std::vector<std::string>* codecs_out,
- const bool strip) {
- g_media_mime_util.Get().ParseCodecString(codecs, codecs_out, strip);
+ bool strip) {
+ g_media_mime_util.Pointer()->ParseCodecString(codecs, codecs_out, strip);
}
void RemoveProprietaryMediaTypesAndCodecsForTests() {
- g_media_mime_util.Get().RemoveProprietaryMediaTypesAndCodecsForTests();
+ g_media_mime_util.Pointer()->RemoveProprietaryMediaTypesAndCodecs();
}
} // namespace media
diff --git a/chromium/media/base/mime_util.h b/chromium/media/base/mime_util.h
index 058953e6a33..6a49eba2a6e 100644
--- a/chromium/media/base/mime_util.h
+++ b/chromium/media/base/mime_util.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef MEDIA_BASE_MIME_UTIL_H__
-#define MEDIA_BASE_MIME_UTIL_H__
+#ifndef MEDIA_BASE_MIME_UTIL_H_
+#define MEDIA_BASE_MIME_UTIL_H_
#include <string>
#include <vector>
@@ -25,19 +25,16 @@ MEDIA_EXPORT void ParseCodecString(const std::string& codecs,
std::vector<std::string>* codecs_out,
bool strip);
-// Indicates that the MIME type and (possible codec string) are supported by the
-// underlying platform.
+// Indicates that the MIME type and (possible codec string) are supported.
enum SupportsType {
- // The underlying platform is known not to support the given MIME type and
- // codec combination.
+ // The given MIME type and codec combination is not supported.
IsNotSupported,
- // The underlying platform is known to support the given MIME type and codec
- // combination.
+ // The given MIME type and codec combination is supported.
IsSupported,
- // The underlying platform is unsure whether the given MIME type and codec
- // combination can be rendered or not before actually trying to play it.
+ // There's not enough information to determine if the given MIME type and
+ // codec combination can be rendered or not before actually trying to play it.
MayBeSupported
};
@@ -56,6 +53,11 @@ MEDIA_EXPORT SupportsType
IsSupportedMediaFormat(const std::string& mime_type,
const std::vector<std::string>& codecs);
+// Similar to the above, but for encrypted formats.
+MEDIA_EXPORT SupportsType
+IsSupportedEncryptedMediaFormat(const std::string& mime_type,
+ const std::vector<std::string>& codecs);
+
// Test only method that removes proprietary media types and codecs from the
// list of supported MIME types and codecs. These types and codecs must be
// removed to ensure consistent layout test results across all Chromium
@@ -64,5 +66,4 @@ MEDIA_EXPORT void RemoveProprietaryMediaTypesAndCodecsForTests();
} // namespace media
-#endif // MEDIA_BASE_MIME_UTIL_H__
-
+#endif // MEDIA_BASE_MIME_UTIL_H_
diff --git a/chromium/media/base/mime_util_internal.cc b/chromium/media/base/mime_util_internal.cc
new file mode 100644
index 00000000000..7a26254f54d
--- /dev/null
+++ b/chromium/media/base/mime_util_internal.cc
@@ -0,0 +1,685 @@
+// Copyright 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.
+
+#include "media/base/mime_util_internal.h"
+
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "build/build_config.h"
+#include "media/base/media.h"
+#include "media/base/video_codecs.h"
+#include "media/media_features.h"
+
+#if defined(OS_ANDROID)
+#include "base/android/build_info.h"
+#include "media/base/android/media_codec_util.h"
+#endif
+
+namespace media {
+namespace internal {
+
+struct CodecIDMappings {
+ const char* const codec_id;
+ MimeUtil::Codec codec;
+};
+
+// List of codec IDs that provide enough information to determine the
+// codec and profile being requested.
+//
+// The "mp4a" strings come from RFC 6381.
+static const CodecIDMappings kUnambiguousCodecStringMap[] = {
+ {"1", MimeUtil::PCM}, // We only allow this for WAV so it isn't ambiguous.
+ // avc1/avc3.XXXXXX may be unambiguous; handled by ParseAVCCodecId().
+ // hev1/hvc1.XXXXXX may be unambiguous; handled by ParseHEVCCodecID().
+ {"mp3", MimeUtil::MP3},
+ // Following is the list of RFC 6381 compliant audio codec strings:
+ // mp4a.66 - MPEG-2 AAC MAIN
+ // mp4a.67 - MPEG-2 AAC LC
+ // mp4a.68 - MPEG-2 AAC SSR
+ // mp4a.69 - MPEG-2 extension to MPEG-1 (MP3)
+ // mp4a.6B - MPEG-1 audio (MP3)
+ // mp4a.40.2 - MPEG-4 AAC LC
+ // mp4a.40.02 - MPEG-4 AAC LC (leading 0 in aud-oti for compatibility)
+ // mp4a.40.5 - MPEG-4 HE-AAC v1 (AAC LC + SBR)
+ // mp4a.40.05 - MPEG-4 HE-AAC v1 (AAC LC + SBR) (leading 0 in aud-oti for
+ // compatibility)
+ // mp4a.40.29 - MPEG-4 HE-AAC v2 (AAC LC + SBR + PS)
+ {"mp4a.66", MimeUtil::MPEG2_AAC},
+ {"mp4a.67", MimeUtil::MPEG2_AAC},
+ {"mp4a.68", MimeUtil::MPEG2_AAC},
+ {"mp4a.69", MimeUtil::MP3},
+ {"mp4a.6B", MimeUtil::MP3},
+ {"mp4a.40.2", MimeUtil::MPEG4_AAC},
+ {"mp4a.40.02", MimeUtil::MPEG4_AAC},
+ {"mp4a.40.5", MimeUtil::MPEG4_AAC},
+ {"mp4a.40.05", MimeUtil::MPEG4_AAC},
+ {"mp4a.40.29", MimeUtil::MPEG4_AAC},
+#if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING)
+ // TODO(servolk): Strictly speaking only mp4a.A5 and mp4a.A6 codec ids are
+ // valid according to RFC 6381 section 3.3, 3.4. Lower-case oti (mp4a.a5 and
+ // mp4a.a6) should be rejected. But we used to allow those in older versions
+ // of Chromecast firmware and some apps (notably MPL) depend on those codec
+ // types being supported, so they should be allowed for now
+ // (crbug.com/564960).
+ {"ac-3", MimeUtil::AC3},
+ {"mp4a.a5", MimeUtil::AC3},
+ {"mp4a.A5", MimeUtil::AC3},
+ {"ec-3", MimeUtil::EAC3},
+ {"mp4a.a6", MimeUtil::EAC3},
+ {"mp4a.A6", MimeUtil::EAC3},
+#endif
+ {"vorbis", MimeUtil::VORBIS},
+ {"opus", MimeUtil::OPUS},
+ {"vp8", MimeUtil::VP8},
+ {"vp8.0", MimeUtil::VP8},
+ {"vp9", MimeUtil::VP9},
+ {"vp9.0", MimeUtil::VP9},
+ {"theora", MimeUtil::THEORA}};
+
+// List of codec IDs that are ambiguous and don't provide
+// enough information to determine the codec and profile.
+// The codec in these entries indicate the codec and profile
+// we assume the user is trying to indicate.
+static const CodecIDMappings kAmbiguousCodecStringMap[] = {
+ {"mp4a.40", MimeUtil::MPEG4_AAC},
+ {"avc1", MimeUtil::H264},
+ {"avc3", MimeUtil::H264},
+ // avc1/avc3.XXXXXX may be ambiguous; handled by ParseAVCCodecId().
+};
+
+#if BUILDFLAG(ENABLE_MSE_MPEG2TS_STREAM_PARSER)
+static const char kHexString[] = "0123456789ABCDEF";
+static char IntToHex(int i) {
+ DCHECK_GE(i, 0) << i << " not a hex value";
+ DCHECK_LE(i, 15) << i << " not a hex value";
+ return kHexString[i];
+}
+
+static std::string TranslateLegacyAvc1CodecIds(const std::string& codec_id) {
+ // Special handling for old, pre-RFC 6381 format avc1 strings, which are still
+ // being used by some HLS apps to preserve backward compatibility with older
+ // iOS devices. The old format was avc1.<profile>.<level>
+ // Where <profile> is H.264 profile_idc encoded as a decimal number, i.e.
+ // 66 is baseline profile (0x42)
+ // 77 is main profile (0x4d)
+ // 100 is high profile (0x64)
+ // And <level> is H.264 level multiplied by 10, also encoded as decimal number
+ // E.g. <level> 31 corresponds to H.264 level 3.1
+ // See, for example, http://qtdevseed.apple.com/qadrift/testcases/tc-0133.php
+ uint32_t level_start = 0;
+ std::string result;
+ if (base::StartsWith(codec_id, "avc1.66.", base::CompareCase::SENSITIVE)) {
+ level_start = 8;
+ result = "avc1.4200";
+ } else if (base::StartsWith(codec_id, "avc1.77.",
+ base::CompareCase::SENSITIVE)) {
+ level_start = 8;
+ result = "avc1.4D00";
+ } else if (base::StartsWith(codec_id, "avc1.100.",
+ base::CompareCase::SENSITIVE)) {
+ level_start = 9;
+ result = "avc1.6400";
+ }
+
+ uint32_t level = 0;
+ if (level_start > 0 &&
+ base::StringToUint(codec_id.substr(level_start), &level) && level < 256) {
+ // This is a valid legacy avc1 codec id - return the codec id translated
+ // into RFC 6381 format.
+ result.push_back(IntToHex(level >> 4));
+ result.push_back(IntToHex(level & 0xf));
+ return result;
+ }
+
+ // This is not a valid legacy avc1 codec id - return the original codec id.
+ return codec_id;
+}
+#endif
+
+static bool IsValidH264Level(uint8_t level_idc) {
+ // Valid levels taken from Table A-1 in ISO/IEC 14496-10.
+ // Level_idc represents the standard level represented as decimal number
+ // multiplied by ten, e.g. level_idc==32 corresponds to level==3.2
+ return ((level_idc >= 10 && level_idc <= 13) ||
+ (level_idc >= 20 && level_idc <= 22) ||
+ (level_idc >= 30 && level_idc <= 32) ||
+ (level_idc >= 40 && level_idc <= 42) ||
+ (level_idc >= 50 && level_idc <= 51));
+}
+
+#if BUILDFLAG(ENABLE_HEVC_DEMUXING)
+// ISO/IEC FDIS 14496-15 standard section E.3 describes the syntax of codec ids
+// reserved for HEVC. According to that spec HEVC codec id must start with
+// either "hev1." or "hvc1.". We don't yet support full parsing of HEVC codec
+// ids, but since no other codec id starts with those string we'll just treat
+// any string starting with "hev1." or "hvc1." as valid HEVC codec ids.
+// crbug.com/482761
+static bool ParseHEVCCodecID(const std::string& codec_id,
+ MimeUtil::Codec* codec,
+ bool* is_ambiguous) {
+ if (base::StartsWith(codec_id, "hev1.", base::CompareCase::SENSITIVE) ||
+ base::StartsWith(codec_id, "hvc1.", base::CompareCase::SENSITIVE)) {
+ *codec = MimeUtil::HEVC_MAIN;
+
+ // TODO(servolk): Full HEVC codec id parsing is not implemented yet (see
+ // crbug.com/482761). So treat HEVC codec ids as ambiguous for now.
+ *is_ambiguous = true;
+
+ // TODO(servolk): Most HEVC codec ids are treated as ambiguous (see above),
+ // but we need to recognize at least one valid unambiguous HEVC codec id,
+ // which is added into kMP4VideoCodecsExpression. We need it to be
+ // unambiguous to avoid DCHECK(!is_ambiguous) in InitializeMimeTypeMaps. We
+ // also use these in unit tests (see
+ // content/browser/media/media_canplaytype_browsertest.cc).
+ // Remove this workaround after crbug.com/482761 is fixed.
+ if (codec_id == "hev1.1.6.L93.B0" || codec_id == "hvc1.1.6.L93.B0") {
+ *is_ambiguous = false;
+ }
+
+ return true;
+ }
+
+ return false;
+}
+#endif
+
+MimeUtil::MimeUtil() : allow_proprietary_codecs_(false) {
+#if defined(OS_ANDROID)
+ platform_info_.is_unified_media_pipeline_enabled =
+ IsUnifiedMediaPipelineEnabled();
+ // When the unified media pipeline is enabled, we need support for both GPU
+ // video decoders and MediaCodec; indicated by HasPlatformDecoderSupport().
+ // When the Android pipeline is used, we only need access to MediaCodec.
+ platform_info_.has_platform_decoders = ArePlatformDecodersAvailable();
+ platform_info_.has_platform_vp8_decoder =
+ MediaCodecUtil::IsVp8DecoderAvailable();
+ platform_info_.has_platform_vp9_decoder =
+ MediaCodecUtil::IsVp9DecoderAvailable();
+ platform_info_.supports_opus = PlatformHasOpusSupport();
+#endif
+
+ InitializeMimeTypeMaps();
+}
+
+MimeUtil::~MimeUtil() {}
+
+SupportsType MimeUtil::AreSupportedCodecs(
+ const CodecSet& supported_codecs,
+ const std::vector<std::string>& codecs,
+ const std::string& mime_type_lower_case,
+ bool is_encrypted) const {
+ DCHECK(!supported_codecs.empty());
+ DCHECK(!codecs.empty());
+
+ SupportsType result = IsSupported;
+ for (size_t i = 0; i < codecs.size(); ++i) {
+ bool is_ambiguous = true;
+ Codec codec = INVALID_CODEC;
+ if (!StringToCodec(codecs[i], &codec, &is_ambiguous, is_encrypted))
+ return IsNotSupported;
+
+ if (!IsCodecSupported(codec, mime_type_lower_case, is_encrypted) ||
+ supported_codecs.find(codec) == supported_codecs.end()) {
+ return IsNotSupported;
+ }
+
+ if (is_ambiguous)
+ result = MayBeSupported;
+ }
+
+ return result;
+}
+
+void MimeUtil::InitializeMimeTypeMaps() {
+#if defined(USE_PROPRIETARY_CODECS)
+ allow_proprietary_codecs_ = true;
+#endif
+
+ for (size_t i = 0; i < arraysize(kUnambiguousCodecStringMap); ++i) {
+ string_to_codec_map_[kUnambiguousCodecStringMap[i].codec_id] =
+ CodecEntry(kUnambiguousCodecStringMap[i].codec, false);
+ }
+
+ for (size_t i = 0; i < arraysize(kAmbiguousCodecStringMap); ++i) {
+ string_to_codec_map_[kAmbiguousCodecStringMap[i].codec_id] =
+ CodecEntry(kAmbiguousCodecStringMap[i].codec, true);
+ }
+
+ AddSupportedMediaFormats();
+}
+
+// Each call to AddContainerWithCodecs() contains a media type
+// (https://en.wikipedia.org/wiki/Media_type) and corresponding media codec(s)
+// supported by these types/containers.
+// TODO(ddorwin): Replace insert() calls with initializer_list when allowed.
+void MimeUtil::AddSupportedMediaFormats() {
+ CodecSet implicit_codec;
+ CodecSet wav_codecs;
+ wav_codecs.insert(PCM);
+
+ CodecSet ogg_audio_codecs;
+ ogg_audio_codecs.insert(OPUS);
+ ogg_audio_codecs.insert(VORBIS);
+ CodecSet ogg_video_codecs;
+#if !defined(OS_ANDROID)
+ ogg_video_codecs.insert(THEORA);
+#endif // !defined(OS_ANDROID)
+ CodecSet ogg_codecs(ogg_audio_codecs);
+ ogg_codecs.insert(ogg_video_codecs.begin(), ogg_video_codecs.end());
+
+ CodecSet webm_audio_codecs;
+ webm_audio_codecs.insert(OPUS);
+ webm_audio_codecs.insert(VORBIS);
+ CodecSet webm_video_codecs;
+ webm_video_codecs.insert(VP8);
+ webm_video_codecs.insert(VP9);
+ CodecSet webm_codecs(webm_audio_codecs);
+ webm_codecs.insert(webm_video_codecs.begin(), webm_video_codecs.end());
+
+#if defined(USE_PROPRIETARY_CODECS)
+ CodecSet mp3_codecs;
+ mp3_codecs.insert(MP3);
+
+ CodecSet aac;
+ aac.insert(MPEG2_AAC);
+ aac.insert(MPEG4_AAC);
+
+ CodecSet avc_and_aac(aac);
+ avc_and_aac.insert(H264);
+
+ CodecSet mp4_audio_codecs(aac);
+ mp4_audio_codecs.insert(MP3);
+#if BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING)
+ mp4_audio_codecs.insert(AC3);
+ mp4_audio_codecs.insert(EAC3);
+#endif // BUILDFLAG(ENABLE_AC3_EAC3_AUDIO_DEMUXING)
+
+ CodecSet mp4_video_codecs;
+ mp4_video_codecs.insert(H264);
+#if BUILDFLAG(ENABLE_HEVC_DEMUXING)
+ mp4_video_codecs.insert(HEVC_MAIN);
+#endif // BUILDFLAG(ENABLE_HEVC_DEMUXING)
+ CodecSet mp4_codecs(mp4_audio_codecs);
+ mp4_codecs.insert(mp4_video_codecs.begin(), mp4_video_codecs.end());
+#endif // defined(USE_PROPRIETARY_CODECS)
+
+ AddContainerWithCodecs("audio/wav", wav_codecs, false);
+ AddContainerWithCodecs("audio/x-wav", wav_codecs, false);
+ AddContainerWithCodecs("audio/webm", webm_audio_codecs, false);
+ DCHECK(!webm_video_codecs.empty());
+ AddContainerWithCodecs("video/webm", webm_codecs, false);
+ AddContainerWithCodecs("audio/ogg", ogg_audio_codecs, false);
+ // video/ogg is only supported if an appropriate video codec is supported.
+ // Note: This assumes such codecs cannot be later excluded.
+ if (!ogg_video_codecs.empty())
+ AddContainerWithCodecs("video/ogg", ogg_codecs, false);
+ // TODO(ddorwin): Should the application type support Opus?
+ AddContainerWithCodecs("application/ogg", ogg_codecs, false);
+
+#if defined(USE_PROPRIETARY_CODECS)
+ AddContainerWithCodecs("audio/mpeg", mp3_codecs, true); // Allow "mp3".
+ AddContainerWithCodecs("audio/mp3", implicit_codec, true);
+ AddContainerWithCodecs("audio/x-mp3", implicit_codec, true);
+ AddContainerWithCodecs("audio/aac", implicit_codec, true); // AAC / ADTS.
+ AddContainerWithCodecs("audio/mp4", mp4_audio_codecs, true);
+ DCHECK(!mp4_video_codecs.empty());
+ AddContainerWithCodecs("video/mp4", mp4_codecs, true);
+ // These strings are supported for backwards compatibility only and thus only
+ // support the codecs needed for compatibility.
+ AddContainerWithCodecs("audio/x-m4a", aac, true);
+ AddContainerWithCodecs("video/x-m4v", avc_and_aac, true);
+
+#if BUILDFLAG(ENABLE_MSE_MPEG2TS_STREAM_PARSER)
+ // TODO(ddorwin): Exactly which codecs should be supported?
+ DCHECK(!mp4_video_codecs.empty());
+ AddContainerWithCodecs("video/mp2t", mp4_codecs, true);
+#endif // BUILDFLAG(ENABLE_MSE_MPEG2TS_STREAM_PARSER)
+#if defined(OS_ANDROID)
+ // HTTP Live Streaming (HLS).
+ // TODO(ddorwin): Is any MP3 codec string variant included in real queries?
+ CodecSet hls_codecs(avc_and_aac);
+ hls_codecs.insert(MP3);
+ AddContainerWithCodecs("application/x-mpegurl", hls_codecs, true);
+ AddContainerWithCodecs("application/vnd.apple.mpegurl", hls_codecs, true);
+#endif // defined(OS_ANDROID)
+#endif // defined(USE_PROPRIETARY_CODECS)
+}
+
+void MimeUtil::AddContainerWithCodecs(const std::string& mime_type,
+ const CodecSet& codecs,
+ bool is_proprietary_mime_type) {
+#if !defined(USE_PROPRIETARY_CODECS)
+ DCHECK(!is_proprietary_mime_type);
+#endif
+
+ media_format_map_[mime_type] = codecs;
+
+ if (is_proprietary_mime_type)
+ proprietary_media_containers_.push_back(mime_type);
+}
+
+bool MimeUtil::IsSupportedMediaMimeType(const std::string& mime_type) const {
+ return media_format_map_.find(base::ToLowerASCII(mime_type)) !=
+ media_format_map_.end();
+}
+
+void MimeUtil::ParseCodecString(const std::string& codecs,
+ std::vector<std::string>* codecs_out,
+ bool strip) {
+ *codecs_out =
+ base::SplitString(base::TrimString(codecs, "\"", base::TRIM_ALL), ",",
+ base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
+
+ // Convert empty or all-whitespace input to 0 results.
+ if (codecs_out->size() == 1 && (*codecs_out)[0].empty())
+ codecs_out->clear();
+
+ if (!strip)
+ return;
+
+ // Strip everything past the first '.'
+ for (std::vector<std::string>::iterator it = codecs_out->begin();
+ it != codecs_out->end(); ++it) {
+ size_t found = it->find_first_of('.');
+ if (found != std::string::npos)
+ it->resize(found);
+ }
+}
+
+SupportsType MimeUtil::IsSupportedMediaFormat(
+ const std::string& mime_type,
+ const std::vector<std::string>& codecs,
+ bool is_encrypted) const {
+ const std::string mime_type_lower_case = base::ToLowerASCII(mime_type);
+ MediaFormatMappings::const_iterator it_media_format_map =
+ media_format_map_.find(mime_type_lower_case);
+ if (it_media_format_map == media_format_map_.end())
+ return IsNotSupported;
+
+ if (it_media_format_map->second.empty()) {
+ // We get here if the mimetype does not expect a codecs parameter.
+ return (codecs.empty() && IsDefaultCodecSupportedLowerCase(
+ mime_type_lower_case, is_encrypted))
+ ? IsSupported
+ : IsNotSupported;
+ }
+
+ if (codecs.empty()) {
+ // We get here if the mimetype expects to get a codecs parameter,
+ // but didn't get one. If |mime_type_lower_case| does not have a default
+ // codec the best we can do is say "maybe" because we don't have enough
+ // information.
+ Codec default_codec = INVALID_CODEC;
+ if (!GetDefaultCodecLowerCase(mime_type_lower_case, &default_codec))
+ return MayBeSupported;
+
+ return IsCodecSupported(default_codec, mime_type_lower_case, is_encrypted)
+ ? IsSupported
+ : IsNotSupported;
+ }
+
+#if BUILDFLAG(ENABLE_MSE_MPEG2TS_STREAM_PARSER)
+ if (mime_type_lower_case == "video/mp2t") {
+ std::vector<std::string> codecs_to_check;
+ for (const auto& codec_id : codecs) {
+ codecs_to_check.push_back(TranslateLegacyAvc1CodecIds(codec_id));
+ }
+ return AreSupportedCodecs(it_media_format_map->second, codecs_to_check,
+ mime_type_lower_case, is_encrypted);
+ }
+#endif
+
+ return AreSupportedCodecs(it_media_format_map->second, codecs,
+ mime_type_lower_case, is_encrypted);
+}
+
+void MimeUtil::RemoveProprietaryMediaTypesAndCodecs() {
+ for (const auto& container : proprietary_media_containers_)
+ media_format_map_.erase(container);
+ allow_proprietary_codecs_ = false;
+}
+
+// static
+bool MimeUtil::IsCodecSupportedOnPlatform(
+ Codec codec,
+ const std::string& mime_type_lower_case,
+ bool is_encrypted,
+ const PlatformInfo& platform_info) {
+ DCHECK_NE(mime_type_lower_case, "");
+
+ // Encrypted block support is never available without platform decoders.
+ if (is_encrypted && !platform_info.has_platform_decoders)
+ return false;
+
+ // NOTE: We do not account for Media Source Extensions (MSE) within these
+ // checks since it has its own isTypeSupported() which will handle platform
+ // specific codec rejections. See http://crbug.com/587303.
+
+ switch (codec) {
+ // ----------------------------------------------------------------------
+ // The following codecs are never supported.
+ // ----------------------------------------------------------------------
+ case INVALID_CODEC:
+ case AC3:
+ case EAC3:
+ case THEORA:
+ return false;
+
+ // ----------------------------------------------------------------------
+ // The remaining codecs may be supported depending on platform abilities.
+ // ----------------------------------------------------------------------
+
+ case PCM:
+ case MP3:
+ case MPEG4_AAC:
+ case VORBIS:
+ // These codecs are always supported; via a platform decoder (when used
+ // with MSE/EME), a software decoder (the unified pipeline), or with
+ // MediaPlayer.
+ DCHECK(!is_encrypted || platform_info.has_platform_decoders);
+ return true;
+
+ case MPEG2_AAC:
+ // MPEG-2 variants of AAC are not supported on Android unless the unified
+ // media pipeline can be used and the container is not HLS. These codecs
+ // will be decoded in software. See https:crbug.com/544268 for details.
+ if (mime_type_lower_case == "application/x-mpegurl" ||
+ mime_type_lower_case == "application/vnd.apple.mpegurl") {
+ return false;
+ }
+ return !is_encrypted && platform_info.is_unified_media_pipeline_enabled;
+
+ case OPUS:
+ // If clear, the unified pipeline can always decode Opus in software.
+ if (!is_encrypted && platform_info.is_unified_media_pipeline_enabled)
+ return true;
+
+ // Otherwise, platform support is required.
+ if (!platform_info.supports_opus)
+ return false;
+
+ // MediaPlayer does not support Opus in ogg containers.
+ if (base::EndsWith(mime_type_lower_case, "ogg",
+ base::CompareCase::SENSITIVE)) {
+ return false;
+ }
+
+ DCHECK(!is_encrypted || platform_info.has_platform_decoders);
+ return true;
+
+ case H264:
+ // The unified pipeline requires platform support for h264.
+ if (platform_info.is_unified_media_pipeline_enabled)
+ return platform_info.has_platform_decoders;
+
+ // When MediaPlayer or MediaCodec is used, h264 is always supported.
+ DCHECK(!is_encrypted || platform_info.has_platform_decoders);
+ return true;
+
+ case HEVC_MAIN:
+#if BUILDFLAG(ENABLE_HEVC_DEMUXING)
+ if (platform_info.is_unified_media_pipeline_enabled &&
+ !platform_info.has_platform_decoders) {
+ return false;
+ }
+
+#if defined(OS_ANDROID)
+ // HEVC/H.265 is supported in Lollipop+ (API Level 21), according to
+ // http://developer.android.com/reference/android/media/MediaFormat.html
+ return base::android::BuildInfo::GetInstance()->sdk_int() >= 21;
+#else
+ return true;
+#endif // defined(OS_ANDROID)
+#else
+ return false;
+#endif // BUILDFLAG(ENABLE_HEVC_DEMUXING)
+
+ case VP8:
+ // If clear, the unified pipeline can always decode VP8 in software.
+ if (!is_encrypted && platform_info.is_unified_media_pipeline_enabled)
+ return true;
+
+ if (is_encrypted)
+ return platform_info.has_platform_vp8_decoder;
+
+ // MediaPlayer can always play VP8. Note: This is incorrect for MSE, but
+ // MSE does not use this code. http://crbug.com/587303.
+ return true;
+
+ case VP9: {
+ // If clear, the unified pipeline can always decode VP9 in software.
+ if (!is_encrypted && platform_info.is_unified_media_pipeline_enabled)
+ return true;
+
+ // Otherwise, platform support is required.
+ return platform_info.has_platform_vp9_decoder;
+ }
+ }
+
+ return false;
+}
+
+bool MimeUtil::StringToCodec(const std::string& codec_id,
+ Codec* codec,
+ bool* is_ambiguous,
+ bool is_encrypted) const {
+ StringToCodecMappings::const_iterator itr =
+ string_to_codec_map_.find(codec_id);
+ if (itr != string_to_codec_map_.end()) {
+ *codec = itr->second.codec;
+ *is_ambiguous = itr->second.is_ambiguous;
+ return true;
+ }
+
+// If |codec_id| is not in |string_to_codec_map_|, then we assume that it is
+// either H.264 or HEVC/H.265 codec ID because currently those are the only
+// ones that are not added to the |string_to_codec_map_| and require parsing.
+
+#if BUILDFLAG(ENABLE_HEVC_DEMUXING)
+ if (ParseHEVCCodecID(codec_id, codec, is_ambiguous)) {
+ return true;
+ }
+#endif
+
+ VideoCodecProfile profile = VIDEO_CODEC_PROFILE_UNKNOWN;
+ uint8_t level_idc = 0;
+ if (ParseAVCCodecId(codec_id, &profile, &level_idc)) {
+ *codec = MimeUtil::H264;
+ switch (profile) {
+// HIGH10PROFILE is supported through fallback to the ffmpeg decoder
+// which is not available on Android, or if FFMPEG is not used.
+#if !defined(MEDIA_DISABLE_FFMPEG) && !defined(OS_ANDROID)
+ case H264PROFILE_HIGH10PROFILE:
+ if (is_encrypted) {
+ // FFmpeg is not generally used for encrypted videos, so we do not
+ // know whether 10-bit is supported.
+ *is_ambiguous = true;
+ break;
+ }
+// Fall through.
+#endif
+
+ case H264PROFILE_BASELINE:
+ case H264PROFILE_MAIN:
+ case H264PROFILE_HIGH:
+ *is_ambiguous = !IsValidH264Level(level_idc);
+ break;
+ default:
+ *is_ambiguous = true;
+ }
+ return true;
+ }
+
+ DVLOG(4) << __FUNCTION__ << ": Unrecognized codec id " << codec_id;
+ return false;
+}
+
+bool MimeUtil::IsCodecSupported(Codec codec,
+ const std::string& mime_type_lower_case,
+ bool is_encrypted) const {
+ DCHECK_NE(codec, INVALID_CODEC);
+
+#if defined(OS_ANDROID)
+ if (!IsCodecSupportedOnPlatform(codec, mime_type_lower_case, is_encrypted,
+ platform_info_)) {
+ return false;
+ }
+#endif
+
+ return allow_proprietary_codecs_ || !IsCodecProprietary(codec);
+}
+
+bool MimeUtil::IsCodecProprietary(Codec codec) const {
+ switch (codec) {
+ case INVALID_CODEC:
+ case AC3:
+ case EAC3:
+ case MP3:
+ case MPEG2_AAC:
+ case MPEG4_AAC:
+ case H264:
+ case HEVC_MAIN:
+ return true;
+
+ case PCM:
+ case VORBIS:
+ case OPUS:
+ case VP8:
+ case VP9:
+ case THEORA:
+ return false;
+ }
+
+ return true;
+}
+
+bool MimeUtil::GetDefaultCodecLowerCase(const std::string& mime_type_lower_case,
+ Codec* default_codec) const {
+ if (mime_type_lower_case == "audio/mpeg" ||
+ mime_type_lower_case == "audio/mp3" ||
+ mime_type_lower_case == "audio/x-mp3") {
+ *default_codec = MimeUtil::MP3;
+ return true;
+ }
+
+ if (mime_type_lower_case == "audio/aac") {
+ *default_codec = MimeUtil::MPEG4_AAC;
+ return true;
+ }
+
+ return false;
+}
+
+bool MimeUtil::IsDefaultCodecSupportedLowerCase(
+ const std::string& mime_type_lower_case,
+ bool is_encrypted) const {
+ Codec default_codec = Codec::INVALID_CODEC;
+ if (!GetDefaultCodecLowerCase(mime_type_lower_case, &default_codec))
+ return false;
+ return IsCodecSupported(default_codec, mime_type_lower_case, is_encrypted);
+}
+
+} // namespace internal
+} // namespace media
diff --git a/chromium/media/base/mime_util_internal.h b/chromium/media/base/mime_util_internal.h
new file mode 100644
index 00000000000..67396cf654e
--- /dev/null
+++ b/chromium/media/base/mime_util_internal.h
@@ -0,0 +1,171 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_BASE_MIME_UTIL_INTERNAL_H_
+#define MEDIA_BASE_MIME_UTIL_INTERNAL_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/containers/hash_tables.h"
+#include "base/macros.h"
+#include "media/base/media_export.h"
+#include "media/base/mime_util.h"
+
+namespace media {
+namespace internal {
+
+// Internal utility class for handling mime types. Should only be invoked by
+// tests and the functions within mime_util.cc -- NOT for direct use by others.
+class MEDIA_EXPORT MimeUtil {
+ public:
+ MimeUtil();
+ ~MimeUtil();
+
+ enum Codec {
+ INVALID_CODEC,
+ PCM,
+ MP3,
+ AC3,
+ EAC3,
+ MPEG2_AAC,
+ MPEG4_AAC,
+ VORBIS,
+ OPUS,
+ H264,
+ HEVC_MAIN,
+ VP8,
+ VP9,
+ THEORA,
+ LAST_CODEC = THEORA
+ };
+
+ // Platform configuration structure. Controls which codecs are supported at
+ // runtime. Also used by tests to simulate platform differences.
+ struct PlatformInfo {
+ bool has_platform_decoders = false;
+
+ bool has_platform_vp8_decoder = false;
+ bool has_platform_vp9_decoder = false;
+ bool supports_opus = false;
+
+ bool is_unified_media_pipeline_enabled = false;
+ };
+
+ // See mime_util.h for more information on these methods.
+ bool IsSupportedMediaMimeType(const std::string& mime_type) const;
+ void ParseCodecString(const std::string& codecs,
+ std::vector<std::string>* codecs_out,
+ bool strip);
+ SupportsType IsSupportedMediaFormat(const std::string& mime_type,
+ const std::vector<std::string>& codecs,
+ bool is_encrypted) const;
+
+ void RemoveProprietaryMediaTypesAndCodecs();
+
+ // Checks special platform specific codec restrictions. Returns true if
+ // |codec| is supported when contained in |mime_type_lower_case|.
+ // |is_encrypted| means the codec will be used with encrypted blocks.
+ // |platform_info| describes the availability of various platform features;
+ // see PlatformInfo for more details.
+ static bool IsCodecSupportedOnPlatform(
+ Codec codec,
+ const std::string& mime_type_lower_case,
+ bool is_encrypted,
+ const PlatformInfo& platform_info);
+
+ private:
+ typedef base::hash_set<int> CodecSet;
+ typedef std::map<std::string, CodecSet> MediaFormatMappings;
+ struct CodecEntry {
+ CodecEntry() : codec(INVALID_CODEC), is_ambiguous(true) {}
+ CodecEntry(Codec c, bool ambiguous) : codec(c), is_ambiguous(ambiguous) {}
+ Codec codec;
+ bool is_ambiguous;
+ };
+ typedef std::map<std::string, CodecEntry> StringToCodecMappings;
+
+ // Initializes the supported media types into hash sets for faster lookup.
+ void InitializeMimeTypeMaps();
+
+ // Initializes the supported media formats (|media_format_map_|).
+ void AddSupportedMediaFormats();
+
+ // Adds |mime_type| with the specified codecs to |media_format_map_|.
+ void AddContainerWithCodecs(const std::string& mime_type,
+ const CodecSet& codecs_list,
+ bool is_proprietary_mime_type);
+
+ // Returns IsSupported if all codec IDs in |codecs| are unambiguous and are
+ // supported in |mime_type_lower_case|. MayBeSupported is returned if at least
+ // one codec ID in |codecs| is ambiguous but all the codecs are supported.
+ // IsNotSupported is returned if |mime_type_lower_case| is not supported or at
+ // least one is not supported in |mime_type_lower_case|. |is_encrypted| means
+ // the codec will be used with encrypted blocks.
+ SupportsType AreSupportedCodecs(const CodecSet& supported_codecs,
+ const std::vector<std::string>& codecs,
+ const std::string& mime_type_lower_case,
+ bool is_encrypted) const;
+
+ // Converts a codec ID into an Codec enum value and indicates
+ // whether the conversion was ambiguous.
+ // Returns true if this method was able to map |codec_id| to a specific
+ // Codec enum value. |codec| and |is_ambiguous| are only valid if true
+ // is returned. Otherwise their value is undefined after the call.
+ // |is_ambiguous| is true if |codec_id| did not have enough information to
+ // unambiguously determine the proper Codec enum value. If |is_ambiguous|
+ // is true |codec| contains the best guess for the intended Codec enum value.
+ // |is_encrypted| means the codec will be used with encrypted blocks.
+ bool StringToCodec(const std::string& codec_id,
+ Codec* codec,
+ bool* is_ambiguous,
+ bool is_encrypted) const;
+
+ // Returns true if |codec| is supported when contained in
+ // |mime_type_lower_case|. Note: This method will always return false for
+ // proprietary codecs if |allow_proprietary_codecs_| is set to false.
+ // |is_encrypted| means the codec will be used with encrypted blocks.
+ bool IsCodecSupported(Codec codec,
+ const std::string& mime_type_lower_case,
+ bool is_encrypted) const;
+
+ // Returns true if |codec| refers to a proprietary codec.
+ bool IsCodecProprietary(Codec codec) const;
+
+ // Returns true and sets |*default_codec| if |mime_type| has a default codec
+ // associated with it. Returns false otherwise and the value of
+ // |*default_codec| is undefined.
+ bool GetDefaultCodecLowerCase(const std::string& mime_type_lower_case,
+ Codec* default_codec) const;
+
+ // Returns true if |mime_type_lower_case| has a default codec associated with
+ // it and IsCodecSupported() returns true for that particular codec.
+ // |is_encrypted| means the codec will be used with encrypted blocks.
+ bool IsDefaultCodecSupportedLowerCase(const std::string& mime_type_lower_case,
+ bool is_encrypted) const;
+
+#if defined(OS_ANDROID)
+ // Indicates the support of various codecs within the platform.
+ PlatformInfo platform_info_;
+#endif
+
+ // A map of mime_types and hash map of the supported codecs for the mime_type.
+ MediaFormatMappings media_format_map_;
+
+ // List of proprietary containers in |media_format_map_|.
+ std::vector<std::string> proprietary_media_containers_;
+ // Whether proprietary codec support should be advertised to callers.
+ bool allow_proprietary_codecs_;
+
+ // Lookup table for string compare based string -> Codec mappings.
+ StringToCodecMappings string_to_codec_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(MimeUtil);
+};
+
+} // namespace internal
+} // namespace media
+
+#endif // MEDIA_BASE_MIME_UTIL_INTERNAL_H_
diff --git a/chromium/media/base/mime_util_unittest.cc b/chromium/media/base/mime_util_unittest.cc
index 34a2ea3a33a..4db5aa7c9f3 100644
--- a/chromium/media/base/mime_util_unittest.cc
+++ b/chromium/media/base/mime_util_unittest.cc
@@ -6,12 +6,116 @@
#include "base/macros.h"
#include "base/strings/string_split.h"
+#include "base/strings/stringprintf.h"
#include "build/build_config.h"
#include "media/base/mime_util.h"
+#include "media/base/mime_util_internal.h"
#include "media/media_features.h"
#include "testing/gtest/include/gtest/gtest.h"
+#if defined(OS_ANDROID)
+#include "base/android/build_info.h"
+#endif
+
namespace media {
+namespace internal {
+
+// MIME type for use with IsCodecSupportedOnPlatform() test; type is ignored in
+// all cases except for when paired with the Opus codec.
+const char kTestMimeType[] = "foo/foo";
+
+// Helper method for creating a multi-value vector of |kTestStates| if
+// |test_all_values| is true or if false, a single value vector containing
+// |single_value|.
+static std::vector<bool> CreateTestVector(bool test_all_values,
+ bool single_value) {
+ const bool kTestStates[] = {true, false};
+ if (test_all_values)
+ return std::vector<bool>(kTestStates, kTestStates + arraysize(kTestStates));
+ return std::vector<bool>(1, single_value);
+}
+
+// Helper method for running IsCodecSupportedOnPlatform() tests that will
+// iterate over all possible field values for a MimeUtil::PlatformInfo struct.
+//
+// To request a field be varied, set its value to true in the |states_to_vary|
+// struct. If false, the only value tested will be the field value from
+// |test_states|.
+//
+// |test_func| should have the signature <void(const MimeUtil::PlatformInfo&,
+// MimeUtil::Codec)>.
+template <typename TestCallback>
+static void RunCodecSupportTest(const MimeUtil::PlatformInfo& states_to_vary,
+ const MimeUtil::PlatformInfo& test_states,
+ TestCallback test_func) {
+#define MAKE_TEST_VECTOR(name) \
+ std::vector<bool> name##_states = \
+ CreateTestVector(states_to_vary.name, test_states.name)
+
+ // Stuff states to test into vectors for easy for_each() iteration.
+ MAKE_TEST_VECTOR(has_platform_decoders);
+ MAKE_TEST_VECTOR(has_platform_vp8_decoder);
+ MAKE_TEST_VECTOR(has_platform_vp9_decoder);
+ MAKE_TEST_VECTOR(supports_opus);
+ MAKE_TEST_VECTOR(is_unified_media_pipeline_enabled);
+#undef MAKE_TEST_VECTOR
+
+ MimeUtil::PlatformInfo info;
+
+#define RUN_TEST_VECTOR(name) \
+ size_t name##_index = 0; \
+ for (info.name = name##_states[name##_index]; \
+ name##_index < name##_states.size(); ++name##_index)
+
+ RUN_TEST_VECTOR(has_platform_decoders) {
+ RUN_TEST_VECTOR(has_platform_vp8_decoder) {
+ RUN_TEST_VECTOR(has_platform_vp9_decoder) {
+ RUN_TEST_VECTOR(supports_opus) {
+ RUN_TEST_VECTOR(is_unified_media_pipeline_enabled) {
+ for (int codec = MimeUtil::INVALID_CODEC;
+ codec <= MimeUtil::LAST_CODEC; ++codec) {
+ SCOPED_TRACE(base::StringPrintf(
+ "has_platform_decoders=%d, has_platform_vp8_decoder=%d, "
+ "supports_opus=%d, "
+ "has_platform_vp9_decoder=%d, "
+ "is_unified_media_pipeline_enabled=%d, "
+ "codec=%d",
+ info.has_platform_decoders, info.has_platform_vp8_decoder,
+ info.supports_opus, info.has_platform_vp9_decoder,
+ info.is_unified_media_pipeline_enabled, codec));
+ test_func(info, static_cast<MimeUtil::Codec>(codec));
+ }
+ }
+ }
+ }
+ }
+ }
+#undef RUN_TEST_VECTOR
+}
+
+// Helper method for generating the |states_to_vary| value used by
+// RunPlatformCodecTest(). Marks all fields to be varied.
+static MimeUtil::PlatformInfo VaryAllFields() {
+ MimeUtil::PlatformInfo states_to_vary;
+ states_to_vary.has_platform_vp8_decoder = true;
+ states_to_vary.has_platform_vp9_decoder = true;
+ states_to_vary.supports_opus = true;
+ states_to_vary.is_unified_media_pipeline_enabled = true;
+ states_to_vary.has_platform_decoders = true;
+ return states_to_vary;
+}
+
+static bool HasHevcSupport() {
+#if BUILDFLAG(ENABLE_HEVC_DEMUXING)
+#if defined(OS_ANDROID)
+ return base::android::BuildInfo::GetInstance()->sdk_int() >= 21;
+#else
+ return true;
+#endif // defined(OS_ANDROID)
+#else
+ return false;
+#endif // BUILDFLAG(ENABLE_HEVC_DEMUXING)
+}
TEST(MimeUtilTest, CommonMediaMimeType) {
EXPECT_TRUE(IsSupportedMediaMimeType("audio/webm"));
@@ -28,7 +132,7 @@ TEST(MimeUtilTest, CommonMediaMimeType) {
EXPECT_TRUE(IsSupportedMediaMimeType("video/ogg"));
#endif // OS_ANDROID
-#if defined(OS_ANDROID)
+#if defined(OS_ANDROID) && defined(USE_PROPRIETARY_CODECS)
// HLS is supported on Android API level 14 and higher and Chrome supports
// API levels 15 and higher, so these are expected to be supported.
bool kHlsSupported = true;
@@ -111,4 +215,205 @@ TEST(MimeUtilTest, ParseCodecString) {
EXPECT_EQ("mp4a.40.2", codecs_out[1]);
}
+TEST(IsCodecSupportedOnPlatformTest,
+ EncryptedCodecsFailWithoutPlatformSupport) {
+ // Vary all parameters except |has_platform_decoders|.
+ MimeUtil::PlatformInfo states_to_vary = VaryAllFields();
+ states_to_vary.has_platform_decoders = false;
+
+ // Disable platform decoders.
+ MimeUtil::PlatformInfo test_states;
+ test_states.has_platform_decoders = false;
+
+ // Every codec should fail since platform support is missing and we've
+ // requested encrypted codecs.
+ RunCodecSupportTest(
+ states_to_vary, test_states,
+ [](const MimeUtil::PlatformInfo& info, MimeUtil::Codec codec) {
+ EXPECT_FALSE(MimeUtil::IsCodecSupportedOnPlatform(codec, kTestMimeType,
+ true, info));
+ });
+}
+
+TEST(IsCodecSupportedOnPlatformTest, EncryptedCodecBehavior) {
+ // Vary all parameters except |has_platform_decoders|.
+ MimeUtil::PlatformInfo states_to_vary = VaryAllFields();
+ states_to_vary.has_platform_decoders = false;
+
+ // Enable platform decoders.
+ MimeUtil::PlatformInfo test_states;
+ test_states.has_platform_decoders = true;
+
+ RunCodecSupportTest(
+ states_to_vary, test_states,
+ [](const MimeUtil::PlatformInfo& info, MimeUtil::Codec codec) {
+ const bool result = MimeUtil::IsCodecSupportedOnPlatform(
+ codec, kTestMimeType, true, info);
+ switch (codec) {
+ // These codecs are never supported by the Android platform.
+ case MimeUtil::INVALID_CODEC:
+ case MimeUtil::AC3:
+ case MimeUtil::EAC3:
+ case MimeUtil::MPEG2_AAC:
+ case MimeUtil::THEORA:
+ EXPECT_FALSE(result);
+ break;
+
+ // These codecs are always available with platform decoder support.
+ case MimeUtil::PCM:
+ case MimeUtil::MP3:
+ case MimeUtil::MPEG4_AAC:
+ case MimeUtil::VORBIS:
+ case MimeUtil::H264:
+ EXPECT_TRUE(result);
+ break;
+
+ // The remaining codecs are not available on all platforms even when
+ // a platform decoder is available.
+ case MimeUtil::OPUS:
+ EXPECT_EQ(info.supports_opus, result);
+ break;
+
+ case MimeUtil::VP8:
+ EXPECT_EQ(info.has_platform_vp8_decoder, result);
+ break;
+
+ case MimeUtil::VP9:
+ EXPECT_EQ(info.has_platform_vp9_decoder, result);
+ break;
+
+ case MimeUtil::HEVC_MAIN:
+ EXPECT_EQ(HasHevcSupport(), result);
+ break;
+ }
+ });
+}
+
+TEST(IsCodecSupportedOnPlatformTest, ClearCodecBehaviorWithAndroidPipeline) {
+ // Vary all parameters except |is_unified_media_pipeline_enabled|.
+ MimeUtil::PlatformInfo states_to_vary = VaryAllFields();
+ states_to_vary.is_unified_media_pipeline_enabled = false;
+
+ // Disable the unified pipeline.
+ MimeUtil::PlatformInfo test_states;
+ test_states.is_unified_media_pipeline_enabled = false;
+
+ RunCodecSupportTest(
+ states_to_vary, test_states,
+ [](const MimeUtil::PlatformInfo& info, MimeUtil::Codec codec) {
+ const bool result = MimeUtil::IsCodecSupportedOnPlatform(
+ codec, kTestMimeType, false, info);
+ switch (codec) {
+ // These codecs are never supported by the Android platform.
+ case MimeUtil::INVALID_CODEC:
+ case MimeUtil::AC3:
+ case MimeUtil::EAC3:
+ case MimeUtil::MPEG2_AAC:
+ case MimeUtil::THEORA:
+ EXPECT_FALSE(result);
+ break;
+
+ // These codecs are always available via MediaPlayer.
+ case MimeUtil::PCM:
+ case MimeUtil::MP3:
+ case MimeUtil::MPEG4_AAC:
+ case MimeUtil::VORBIS:
+ case MimeUtil::H264:
+ case MimeUtil::VP8:
+ EXPECT_TRUE(result);
+ break;
+
+ // The remaining codecs depend on the platform version.
+ case MimeUtil::OPUS:
+ EXPECT_EQ(info.supports_opus, result);
+ break;
+
+ case MimeUtil::VP9:
+ EXPECT_EQ(info.has_platform_vp9_decoder, result);
+ break;
+
+ case MimeUtil::HEVC_MAIN:
+ EXPECT_EQ(HasHevcSupport(), result);
+ break;
+ }
+ });
+}
+
+TEST(IsCodecSupportedOnPlatformTest, ClearCodecBehaviorWithUnifiedPipeline) {
+ // Vary all parameters except |is_unified_media_pipeline_enabled|.
+ MimeUtil::PlatformInfo states_to_vary = VaryAllFields();
+ states_to_vary.is_unified_media_pipeline_enabled = false;
+
+ // Enable the unified pipeline.
+ MimeUtil::PlatformInfo test_states;
+ test_states.is_unified_media_pipeline_enabled = true;
+
+ RunCodecSupportTest(
+ states_to_vary, test_states,
+ [](const MimeUtil::PlatformInfo& info, MimeUtil::Codec codec) {
+ const bool result = MimeUtil::IsCodecSupportedOnPlatform(
+ codec, kTestMimeType, false, info);
+ switch (codec) {
+ // These codecs are never supported by the Android platform.
+ case MimeUtil::INVALID_CODEC:
+ case MimeUtil::AC3:
+ case MimeUtil::EAC3:
+ case MimeUtil::THEORA:
+ EXPECT_FALSE(result);
+ break;
+
+ // These codecs are always supported with the unified pipeline.
+ case MimeUtil::PCM:
+ case MimeUtil::MPEG2_AAC:
+ case MimeUtil::MP3:
+ case MimeUtil::MPEG4_AAC:
+ case MimeUtil::OPUS:
+ case MimeUtil::VORBIS:
+ case MimeUtil::VP8:
+ case MimeUtil::VP9:
+ EXPECT_TRUE(result);
+ break;
+
+ // These codecs are only supported if platform decoders are supported.
+ case MimeUtil::H264:
+ EXPECT_EQ(info.has_platform_decoders, result);
+ break;
+
+ case MimeUtil::HEVC_MAIN:
+ EXPECT_EQ(HasHevcSupport() && info.has_platform_decoders, result);
+ break;
+ }
+ });
+}
+
+TEST(IsCodecSupportedOnPlatformTest, OpusOggSupport) {
+ // Vary all parameters; thus use default initial state.
+ MimeUtil::PlatformInfo states_to_vary = VaryAllFields();
+ MimeUtil::PlatformInfo test_states;
+
+ RunCodecSupportTest(
+ states_to_vary, test_states,
+ [](const MimeUtil::PlatformInfo& info, MimeUtil::Codec codec) {
+ EXPECT_EQ(info.is_unified_media_pipeline_enabled,
+ MimeUtil::IsCodecSupportedOnPlatform(
+ MimeUtil::OPUS, "audio/ogg", false, info));
+ });
+}
+
+TEST(IsCodecSupportedOnPlatformTest, HLSDoesNotSupportMPEG2AAC) {
+ // Vary all parameters; thus use default initial state.
+ MimeUtil::PlatformInfo states_to_vary = VaryAllFields();
+ MimeUtil::PlatformInfo test_states;
+
+ RunCodecSupportTest(
+ states_to_vary, test_states,
+ [](const MimeUtil::PlatformInfo& info, MimeUtil::Codec codec) {
+ EXPECT_FALSE(MimeUtil::IsCodecSupportedOnPlatform(
+ MimeUtil::MPEG2_AAC, "application/x-mpegurl", false, info));
+ EXPECT_FALSE(MimeUtil::IsCodecSupportedOnPlatform(
+ MimeUtil::MPEG2_AAC, "application/vnd.apple.mpegurl", false, info));
+ });
+}
+
+} // namespace internal
} // namespace media
diff --git a/chromium/media/base/mock_audio_renderer_sink.cc b/chromium/media/base/mock_audio_renderer_sink.cc
index 79653b2f823..5785ee42254 100644
--- a/chromium/media/base/mock_audio_renderer_sink.cc
+++ b/chromium/media/base/mock_audio_renderer_sink.cc
@@ -3,24 +3,49 @@
// found in the LICENSE file.
#include "media/base/mock_audio_renderer_sink.h"
-#include "media/base/fake_output_device.h"
namespace media {
MockAudioRendererSink::MockAudioRendererSink()
: MockAudioRendererSink(OUTPUT_DEVICE_STATUS_OK) {}
MockAudioRendererSink::MockAudioRendererSink(OutputDeviceStatus device_status)
- : output_device_(new FakeOutputDevice(device_status)) {}
+ : MockAudioRendererSink(std::string(), device_status) {}
+
+MockAudioRendererSink::MockAudioRendererSink(const std::string& device_id,
+ OutputDeviceStatus device_status)
+ : MockAudioRendererSink(
+ device_id,
+ device_status,
+ media::AudioParameters(media::AudioParameters::AUDIO_FAKE,
+ media::CHANNEL_LAYOUT_STEREO,
+ media::AudioParameters::kTelephoneSampleRate,
+ 16,
+ 1)) {}
+
+MockAudioRendererSink::MockAudioRendererSink(
+ const std::string& device_id,
+ OutputDeviceStatus device_status,
+ const AudioParameters& device_output_params)
+ : output_device_info_(device_id, device_status, device_output_params) {}
MockAudioRendererSink::~MockAudioRendererSink() {}
+void MockAudioRendererSink::SwitchOutputDevice(
+ const std::string& device_id,
+ const url::Origin& security_origin,
+ const OutputDeviceStatusCB& callback) {
+ // NB: output device won't be changed, since it's not required by any tests
+ // now.
+ callback.Run(output_device_info_.device_status());
+}
+
void MockAudioRendererSink::Initialize(const AudioParameters& params,
RenderCallback* renderer) {
callback_ = renderer;
}
-OutputDevice* MockAudioRendererSink::GetOutputDevice() {
- return output_device_.get();
+OutputDeviceInfo MockAudioRendererSink::GetOutputDeviceInfo() {
+ return output_device_info_;
}
} // namespace media
diff --git a/chromium/media/base/mock_audio_renderer_sink.h b/chromium/media/base/mock_audio_renderer_sink.h
index 2194bc99d92..fa42a0ba46c 100644
--- a/chromium/media/base/mock_audio_renderer_sink.h
+++ b/chromium/media/base/mock_audio_renderer_sink.h
@@ -16,18 +16,27 @@ namespace media {
class FakeOutputDevice;
-class MockAudioRendererSink : public RestartableAudioRendererSink {
+class MockAudioRendererSink : public SwitchableAudioRendererSink {
public:
MockAudioRendererSink();
explicit MockAudioRendererSink(OutputDeviceStatus device_status);
+ MockAudioRendererSink(const std::string& device_id,
+ OutputDeviceStatus device_status);
+ MockAudioRendererSink(const std::string& device_id,
+ OutputDeviceStatus device_status,
+ const AudioParameters& device_output_params);
MOCK_METHOD0(Start, void());
MOCK_METHOD0(Stop, void());
MOCK_METHOD0(Pause, void());
MOCK_METHOD0(Play, void());
MOCK_METHOD1(SetVolume, bool(double volume));
- OutputDevice* GetOutputDevice();
+ OutputDeviceInfo GetOutputDeviceInfo();
+
+ void SwitchOutputDevice(const std::string& device_id,
+ const url::Origin& security_origin,
+ const OutputDeviceStatusCB& callback) override;
void Initialize(const AudioParameters& params,
RenderCallback* renderer) override;
AudioRendererSink::RenderCallback* callback() { return callback_; }
@@ -37,7 +46,7 @@ class MockAudioRendererSink : public RestartableAudioRendererSink {
private:
RenderCallback* callback_;
- scoped_ptr<FakeOutputDevice> output_device_;
+ OutputDeviceInfo output_device_info_;
DISALLOW_COPY_AND_ASSIGN(MockAudioRendererSink);
};
diff --git a/chromium/media/base/mock_filters.cc b/chromium/media/base/mock_filters.cc
index 98c23be9bf0..1c783bd55f0 100644
--- a/chromium/media/base/mock_filters.cc
+++ b/chromium/media/base/mock_filters.cc
@@ -14,6 +14,31 @@ using ::testing::Return;
namespace media {
+MockPipeline::MockPipeline() {}
+
+MockPipeline::~MockPipeline() {}
+
+void MockPipeline::Start(Demuxer* demuxer,
+ scoped_ptr<Renderer> renderer,
+ const base::Closure& ended_cb,
+ const PipelineStatusCB& error_cb,
+ const PipelineStatusCB& seek_cb,
+ const PipelineMetadataCB& metadata_cb,
+ const BufferingStateCB& buffering_state_cb,
+ const base::Closure& duration_change_cb,
+ const AddTextTrackCB& add_text_track_cb,
+ const base::Closure& waiting_for_decryption_key_cb) {
+ Start(demuxer, &renderer, ended_cb, error_cb, seek_cb, metadata_cb,
+ buffering_state_cb, duration_change_cb, add_text_track_cb,
+ waiting_for_decryption_key_cb);
+}
+
+void MockPipeline::Resume(scoped_ptr<Renderer> renderer,
+ base::TimeDelta timestamp,
+ const PipelineStatusCB& seek_cb) {
+ Resume(&renderer, timestamp, seek_cb);
+}
+
MockDemuxer::MockDemuxer() {}
MockDemuxer::~MockDemuxer() {}
@@ -71,7 +96,7 @@ std::string MockVideoDecoder::GetDisplayName() const {
}
MockVideoDecoder::MockVideoDecoder() {
- EXPECT_CALL(*this, HasAlpha()).WillRepeatedly(Return(false));
+ ON_CALL(*this, CanReadWithoutStalling()).WillByDefault(Return(true));
}
std::string MockAudioDecoder::GetDisplayName() const {
diff --git a/chromium/media/base/mock_filters.h b/chromium/media/base/mock_filters.h
index 39c8f3b7e33..f60c9cdf8f4 100644
--- a/chromium/media/base/mock_filters.h
+++ b/chromium/media/base/mock_filters.h
@@ -18,6 +18,7 @@
#include "media/base/decoder_buffer.h"
#include "media/base/decryptor.h"
#include "media/base/demuxer.h"
+#include "media/base/pipeline.h"
#include "media/base/pipeline_status.h"
#include "media/base/renderer.h"
#include "media/base/text_track.h"
@@ -30,6 +31,72 @@
namespace media {
+class MockPipeline : public Pipeline {
+ public:
+ MockPipeline();
+ virtual ~MockPipeline();
+
+ // Note: Start() and Resume() declarations are not actually overrides; they
+ // take scoped_ptr* instead of scoped_ptr so that they can be mock methods.
+ // Private stubs for Start() and Resume() implement the actual Pipeline
+ // interface by forwarding to these mock methods.
+ MOCK_METHOD10(Start,
+ void(Demuxer*,
+ scoped_ptr<Renderer>*,
+ const base::Closure&,
+ const PipelineStatusCB&,
+ const PipelineStatusCB&,
+ const PipelineMetadataCB&,
+ const BufferingStateCB&,
+ const base::Closure&,
+ const AddTextTrackCB&,
+ const base::Closure&));
+ MOCK_METHOD1(Stop, void(const base::Closure&));
+ MOCK_METHOD2(Seek, void(base::TimeDelta, const PipelineStatusCB&));
+ MOCK_METHOD1(Suspend, void(const PipelineStatusCB&));
+ MOCK_METHOD3(Resume,
+ void(scoped_ptr<Renderer>*,
+ base::TimeDelta,
+ const PipelineStatusCB&));
+
+ // TODO(sandersd): This should automatically return true between Start() and
+ // Stop(). (Or better, remove it from the interface entirely.)
+ MOCK_CONST_METHOD0(IsRunning, bool());
+
+ // TODO(sandersd): These should be regular getters/setters.
+ MOCK_CONST_METHOD0(GetPlaybackRate, double());
+ MOCK_METHOD1(SetPlaybackRate, void(double));
+ MOCK_CONST_METHOD0(GetVolume, float());
+ MOCK_METHOD1(SetVolume, void(float));
+
+ // TODO(sandersd): These should probably have setters too.
+ MOCK_CONST_METHOD0(GetMediaTime, base::TimeDelta());
+ MOCK_CONST_METHOD0(GetBufferedTimeRanges, Ranges<base::TimeDelta>());
+ MOCK_CONST_METHOD0(GetMediaDuration, base::TimeDelta());
+ MOCK_METHOD0(DidLoadingProgress, bool());
+ MOCK_CONST_METHOD0(GetStatistics, PipelineStatistics());
+
+ MOCK_METHOD2(SetCdm, void(CdmContext*, const CdmAttachedCB&));
+
+ private:
+ // Forwarding stubs (see comment above).
+ void Start(Demuxer* demuxer,
+ scoped_ptr<Renderer> renderer,
+ const base::Closure& ended_cb,
+ const PipelineStatusCB& error_cb,
+ const PipelineStatusCB& seek_cb,
+ const PipelineMetadataCB& metadata_cb,
+ const BufferingStateCB& buffering_state_cb,
+ const base::Closure& duration_change_cb,
+ const AddTextTrackCB& add_text_track_cb,
+ const base::Closure& waiting_for_decryption_key_cb) override;
+ void Resume(scoped_ptr<Renderer> renderer,
+ base::TimeDelta timestamp,
+ const PipelineStatusCB& seek_cb) override;
+
+ DISALLOW_COPY_AND_ASSIGN(MockPipeline);
+};
+
class MockDemuxer : public Demuxer {
public:
MockDemuxer();
@@ -39,10 +106,10 @@ class MockDemuxer : public Demuxer {
virtual std::string GetDisplayName() const;
MOCK_METHOD3(Initialize,
void(DemuxerHost* host, const PipelineStatusCB& cb, bool));
- MOCK_METHOD1(SetPlaybackRate, void(double playback_rate));
+ MOCK_METHOD1(StartWaitingForSeek, void(base::TimeDelta));
+ MOCK_METHOD1(CancelPendingSeek, void(base::TimeDelta));
MOCK_METHOD2(Seek, void(base::TimeDelta time, const PipelineStatusCB& cb));
MOCK_METHOD0(Stop, void());
- MOCK_METHOD0(OnAudioRendererDisabled, void());
MOCK_METHOD1(GetStream, DemuxerStream*(DemuxerStream::Type));
MOCK_CONST_METHOD0(GetStartTime, base::TimeDelta());
MOCK_CONST_METHOD0(GetTimelineOffset, base::Time());
@@ -91,13 +158,14 @@ class MockVideoDecoder : public VideoDecoder {
MOCK_METHOD5(Initialize,
void(const VideoDecoderConfig& config,
bool low_delay,
- const SetCdmReadyCB& set_cdm_ready_cb,
+ CdmContext* cdm_context,
const InitCB& init_cb,
const OutputCB& output_cb));
MOCK_METHOD2(Decode, void(const scoped_refptr<DecoderBuffer>& buffer,
const DecodeCB&));
MOCK_METHOD1(Reset, void(const base::Closure&));
MOCK_CONST_METHOD0(HasAlpha, bool());
+ MOCK_CONST_METHOD0(CanReadWithoutStalling, bool());
private:
DISALLOW_COPY_AND_ASSIGN(MockVideoDecoder);
@@ -112,7 +180,7 @@ class MockAudioDecoder : public AudioDecoder {
virtual std::string GetDisplayName() const;
MOCK_METHOD4(Initialize,
void(const AudioDecoderConfig& config,
- const SetCdmReadyCB& set_cdm_ready_cb,
+ CdmContext* cdm_context,
const InitCB& init_cb,
const OutputCB& output_cb));
MOCK_METHOD2(Decode,
@@ -133,7 +201,7 @@ class MockVideoRenderer : public VideoRenderer {
MOCK_METHOD9(Initialize,
void(DemuxerStream* stream,
const PipelineStatusCB& init_cb,
- const SetCdmReadyCB& set_cdm_ready_cb,
+ CdmContext* cdm_context,
const StatisticsCB& statistics_cb,
const BufferingStateCB& buffering_state_cb,
const base::Closure& ended_cb,
@@ -157,7 +225,7 @@ class MockAudioRenderer : public AudioRenderer {
MOCK_METHOD8(Initialize,
void(DemuxerStream* stream,
const PipelineStatusCB& init_cb,
- const SetCdmReadyCB& set_cdm_ready_cb,
+ CdmContext* cdm_context,
const StatisticsCB& statistics_cb,
const BufferingStateCB& buffering_state_cb,
const base::Closure& ended_cb,
diff --git a/chromium/media/base/output_device.h b/chromium/media/base/output_device.h
deleted file mode 100644
index 76f88c78ed9..00000000000
--- a/chromium/media/base/output_device.h
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright (c) 2015 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_OUTPUT_DEVICE_H_
-#define MEDIA_BASE_OUTPUT_DEVICE_H_
-
-#include <string>
-
-#include "base/callback.h"
-#include "media/audio/audio_parameters.h"
-#include "media/base/media_export.h"
-#include "url/origin.h"
-
-namespace media {
-
-// Result of an audio output device switch operation
-enum OutputDeviceStatus {
- OUTPUT_DEVICE_STATUS_OK = 0,
- OUTPUT_DEVICE_STATUS_ERROR_NOT_FOUND,
- OUTPUT_DEVICE_STATUS_ERROR_NOT_AUTHORIZED,
- OUTPUT_DEVICE_STATUS_ERROR_INTERNAL,
- OUTPUT_DEVICE_STATUS_LAST = OUTPUT_DEVICE_STATUS_ERROR_INTERNAL,
-};
-
-typedef base::Callback<void(OutputDeviceStatus)> SwitchOutputDeviceCB;
-
-// OutputDevice is an interface that allows performing operations related
-// audio output devices.
-
-class OutputDevice {
- public:
- // Attempts to switch the audio output device.
- // Once the attempt is finished, |callback| is invoked with the
- // result of the operation passed as a parameter. The result is a value from
- // the media::SwitchOutputDeviceResult enum.
- // There is no guarantee about the thread where |callback| will
- // be invoked, so users are advised to use media::BindToCurrentLoop() to
- // ensure that |callback| runs on the correct thread.
- // Note also that copy constructors and destructors for arguments bound to
- // |callback| may run on arbitrary threads as |callback| is moved across
- // threads. It is advisable to bind arguments such that they are released by
- // |callback| when it runs in order to avoid surprises.
- virtual void SwitchOutputDevice(const std::string& device_id,
- const url::Origin& security_origin,
- const SwitchOutputDeviceCB& callback) = 0;
-
- // Returns the device's audio output parameters.
- // The return value is undefined if the device status (as returned by
- // GetDeviceStatus()) is different from OUTPUT_DEVICE_STATUS_OK.
- // If the parameters are not available, this method may block until they
- // become available.
- // This method must never be called on the IO thread.
- virtual AudioParameters GetOutputParameters() = 0;
-
- // Returns the status of output device.
- // If the status is not available, this method may block until it becomes
- // available. Must never be called on the IO thread.
- virtual OutputDeviceStatus GetDeviceStatus() = 0;
-
- protected:
- virtual ~OutputDevice() {}
-};
-
-} // namespace media
-
-#endif // MEDIA_BASE_OUTPUT_DEVICE_H_
diff --git a/chromium/media/base/output_device_info.cc b/chromium/media/base/output_device_info.cc
new file mode 100644
index 00000000000..d1ffa4346a7
--- /dev/null
+++ b/chromium/media/base/output_device_info.cc
@@ -0,0 +1,40 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/base/output_device_info.h"
+
+namespace media {
+
+// Output device information returned by GetOutputDeviceInfo() methods of
+// various interfaces.
+OutputDeviceInfo::OutputDeviceInfo()
+ : OutputDeviceInfo(OUTPUT_DEVICE_STATUS_ERROR_INTERNAL) {}
+
+OutputDeviceInfo::OutputDeviceInfo(OutputDeviceStatus device_status)
+ : OutputDeviceInfo(std::string(),
+ device_status,
+ AudioParameters::UnavailableDeviceParams()) {}
+
+OutputDeviceInfo::OutputDeviceInfo(const std::string& device_id,
+ OutputDeviceStatus device_status,
+ const AudioParameters& output_params)
+ : device_id_(device_id),
+ device_status_(device_status),
+ output_params_(output_params) {}
+
+OutputDeviceInfo::OutputDeviceInfo(const OutputDeviceInfo&) = default;
+
+OutputDeviceInfo& OutputDeviceInfo::operator=(const OutputDeviceInfo&) =
+ default;
+
+OutputDeviceInfo::~OutputDeviceInfo() {}
+
+std::string OutputDeviceInfo::AsHumanReadableString() const {
+ std::ostringstream s;
+ s << "device_id: " << device_id() << " device_status: " << device_status()
+ << " output_params: [ " << output_params().AsHumanReadableString() << " ]";
+ return s.str();
+}
+
+} // namespace media
diff --git a/chromium/media/base/output_device_info.h b/chromium/media/base/output_device_info.h
new file mode 100644
index 00000000000..987ae6728fb
--- /dev/null
+++ b/chromium/media/base/output_device_info.h
@@ -0,0 +1,68 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_BASE_OUTPUT_DEVICE_INFO_H_
+#define MEDIA_BASE_OUTPUT_DEVICE_INFO_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "media/audio/audio_parameters.h"
+#include "media/base/media_export.h"
+
+namespace media {
+
+// Result of an audio output device switch operation
+enum OutputDeviceStatus {
+ OUTPUT_DEVICE_STATUS_OK = 0,
+ OUTPUT_DEVICE_STATUS_ERROR_NOT_FOUND,
+ OUTPUT_DEVICE_STATUS_ERROR_NOT_AUTHORIZED,
+ OUTPUT_DEVICE_STATUS_ERROR_INTERNAL,
+ OUTPUT_DEVICE_STATUS_LAST = OUTPUT_DEVICE_STATUS_ERROR_INTERNAL,
+};
+
+using OutputDeviceStatusCB = base::Callback<void(OutputDeviceStatus)>;
+
+class MEDIA_EXPORT OutputDeviceInfo {
+ public:
+ // Use this constructor to initialize with "no info available" values.
+ OutputDeviceInfo();
+
+ // Use this constructor to indicate a specific error status of the device.
+ explicit OutputDeviceInfo(OutputDeviceStatus device_status);
+
+ OutputDeviceInfo(const std::string& device_id,
+ OutputDeviceStatus device_status,
+ const AudioParameters& output_params);
+
+ OutputDeviceInfo(const OutputDeviceInfo& other);
+
+ OutputDeviceInfo& operator=(const OutputDeviceInfo&);
+
+ ~OutputDeviceInfo();
+
+ // Returns the device ID.
+ const std::string& device_id() const { return device_id_; }
+
+ // Returns the status of output device.
+ OutputDeviceStatus device_status() const { return device_status_; }
+
+ // Returns the device's audio output parameters.
+ // The return value is undefined if the device status (as returned by
+ // device_status()) is different from OUTPUT_DEVICE_STATUS_OK.
+ const AudioParameters& output_params() const { return output_params_; };
+
+ // Returns a human-readable string describing |*this|. For debugging & test
+ // output only.
+ std::string AsHumanReadableString() const;
+
+ private:
+ std::string device_id_;
+ OutputDeviceStatus device_status_;
+ AudioParameters output_params_;
+};
+
+} // namespace media
+
+#endif // MEDIA_BASE_OUTPUT_DEVICE_INFO_H_
diff --git a/chromium/media/base/pipeline.h b/chromium/media/base/pipeline.h
index 5eae2e09e1f..db326c29283 100644
--- a/chromium/media/base/pipeline.h
+++ b/chromium/media/base/pipeline.h
@@ -5,36 +5,21 @@
#ifndef MEDIA_BASE_PIPELINE_H_
#define MEDIA_BASE_PIPELINE_H_
-#include "base/gtest_prod_util.h"
-#include "base/macros.h"
#include "base/memory/ref_counted.h"
-#include "base/memory/weak_ptr.h"
-#include "base/synchronization/lock.h"
-#include "base/threading/thread_checker.h"
-#include "base/time/default_tick_clock.h"
+#include "base/time/time.h"
#include "media/base/buffering_state.h"
#include "media/base/cdm_context.h"
-#include "media/base/demuxer.h"
#include "media/base/media_export.h"
#include "media/base/pipeline_status.h"
#include "media/base/ranges.h"
-#include "media/base/serial_runner.h"
#include "media/base/text_track.h"
#include "media/base/video_rotation.h"
#include "ui/gfx/geometry/size.h"
-namespace base {
-class SingleThreadTaskRunner;
-class TimeDelta;
-}
-
namespace media {
-class MediaLog;
+class Demuxer;
class Renderer;
-class TextRenderer;
-class TextTrackConfig;
-class TimeDeltaInterpolator;
class VideoFrame;
// Metadata describing a pipeline once it has been initialized.
@@ -51,56 +36,11 @@ struct PipelineMetadata {
typedef base::Callback<void(PipelineMetadata)> PipelineMetadataCB;
-// Pipeline runs the media pipeline. Filters are created and called on the
-// task runner injected into this object. Pipeline works like a state
-// machine to perform asynchronous initialization, pausing, seeking and playing.
-//
-// Here's a state diagram that describes the lifetime of this object.
-//
-// [ *Created ] [ Any State ]
-// | Start() | Stop() / SetError()
-// V V
-// [ InitXXX (for each filter) ] [ Stopping ]
-// | |
-// V V
-// [ Playing ] <---------. [ Stopped ]
-// | | Seek() |
-// | V |
-// | [ Seeking ] ----'
-// | ^
-// | Suspend() |
-// V |
-// [ Suspending ] |
-// | |
-// V |
-// [ Suspended ] |
-// | Resume() |
-// V |
-// [ Resuming ] ---------'
-//
-// Initialization is a series of state transitions from "Created" through each
-// filter initialization state. When all filter initialization states have
-// completed, we simulate a Seek() to the beginning of the media to give filters
-// a chance to preroll. From then on the normal Seek() transitions are carried
-// out and we start playing the media.
-//
-// If any error ever happens, this object will transition to the "Error" state
-// from any state. If Stop() is ever called, this object will transition to
-// "Stopped" state.
-//
-// TODO(sandersd): It should be possible to pass through Suspended when going
-// from InitDemuxer to InitRenderer, thereby eliminating the Resuming state.
-// Some annoying differences between the two paths need to be removed first.
-class MEDIA_EXPORT Pipeline : public DemuxerHost {
+class MEDIA_EXPORT Pipeline {
public:
// Used to paint VideoFrame.
typedef base::Callback<void(const scoped_refptr<VideoFrame>&)> PaintCB;
- // Constructs a media pipeline that will execute on |task_runner|.
- Pipeline(const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
- MediaLog* media_log);
- ~Pipeline() override;
-
// Build a pipeline to using the given |demuxer| and |renderer| to construct
// a filter chain, executing |seek_cb| when the initial seek has completed.
//
@@ -120,16 +60,16 @@ class MEDIA_EXPORT Pipeline : public DemuxerHost {
// |waiting_for_decryption_key_cb| will be executed whenever the key needed
// to decrypt the stream is not available.
// It is an error to call this method after the pipeline has already started.
- void Start(Demuxer* demuxer,
- scoped_ptr<Renderer> renderer,
- const base::Closure& ended_cb,
- const PipelineStatusCB& error_cb,
- const PipelineStatusCB& seek_cb,
- const PipelineMetadataCB& metadata_cb,
- const BufferingStateCB& buffering_state_cb,
- const base::Closure& duration_change_cb,
- const AddTextTrackCB& add_text_track_cb,
- const base::Closure& waiting_for_decryption_key_cb);
+ virtual void Start(Demuxer* demuxer,
+ scoped_ptr<Renderer> renderer,
+ const base::Closure& ended_cb,
+ const PipelineStatusCB& error_cb,
+ const PipelineStatusCB& seek_cb,
+ const PipelineMetadataCB& metadata_cb,
+ const BufferingStateCB& buffering_state_cb,
+ const base::Closure& duration_change_cb,
+ const AddTextTrackCB& add_text_track_cb,
+ const base::Closure& waiting_for_decryption_key_cb) = 0;
// Asynchronously stops the pipeline, executing |stop_cb| when the pipeline
// teardown has completed.
@@ -138,7 +78,7 @@ class MEDIA_EXPORT Pipeline : public DemuxerHost {
// call Stop() at any point during the lifetime of the pipeline.
//
// It is safe to delete the pipeline during the execution of |stop_cb|.
- void Stop(const base::Closure& stop_cb);
+ virtual void Stop(const base::Closure& stop_cb) = 0;
// Attempt to seek to the position specified by time. |seek_cb| will be
// executed when the all filters in the pipeline have processed the seek.
@@ -147,291 +87,74 @@ class MEDIA_EXPORT Pipeline : public DemuxerHost {
// succeeded.
//
// It is an error to call this method if the pipeline has not started or
- // is suspended.
- void Seek(base::TimeDelta time, const PipelineStatusCB& seek_cb);
+ // has been suspended.
+ virtual void Seek(base::TimeDelta time, const PipelineStatusCB& seek_cb) = 0;
+
+ // Suspends the pipeline, discarding the current renderer.
+ //
+ // While suspended, GetMediaTime() returns the presentation timestamp of the
+ // last rendered frame.
+ //
+ // It is an error to call this method if the pipeline has not started or is
+ // seeking.
+ virtual void Suspend(const PipelineStatusCB& suspend_cb) = 0;
+
+ // Resume the pipeline with a new renderer, and initialize it with a seek.
+ //
+ // It is an error to call this method if the pipeline has not finished
+ // suspending.
+ virtual void Resume(scoped_ptr<Renderer> renderer,
+ base::TimeDelta timestamp,
+ const PipelineStatusCB& seek_cb) = 0;
// Returns true if the pipeline has been started via Start(). If IsRunning()
// returns true, it is expected that Stop() will be called before destroying
// the pipeline.
- bool IsRunning() const;
+ virtual bool IsRunning() const = 0;
// Gets the current playback rate of the pipeline. When the pipeline is
// started, the playback rate will be 0.0. A rate of 1.0 indicates
// that the pipeline is rendering the media at the standard rate. Valid
// values for playback rate are >= 0.0.
- double GetPlaybackRate() const;
+ virtual double GetPlaybackRate() const = 0;
// Attempt to adjust the playback rate. Setting a playback rate of 0.0 pauses
// all rendering of the media. A rate of 1.0 indicates a normal playback
// rate. Values for the playback rate must be greater than or equal to 0.0.
//
// TODO(scherkus): What about maximum rate? Does HTML5 specify a max?
- void SetPlaybackRate(double playback_rate);
-
- // Suspend the pipeline, discarding the current renderer.
- //
- // While suspended, GetMediaTime() returns the presentation timestamp of the
- // last rendered frame.
- //
- // It is an error to call this method if the pipeline has not started or is
- // seeking.
- void Suspend(const PipelineStatusCB& suspend_cb);
-
- // Resume the pipeline with a new renderer, and initialize it with a seek.
- void Resume(scoped_ptr<Renderer> renderer,
- base::TimeDelta timestamp,
- const PipelineStatusCB& seek_cb);
+ virtual void SetPlaybackRate(double playback_rate) = 0;
// Gets the current volume setting being used by the audio renderer. When
// the pipeline is started, this value will be 1.0f. Valid values range
// from 0.0f to 1.0f.
- float GetVolume() const;
+ virtual float GetVolume() const = 0;
// Attempt to set the volume of the audio renderer. Valid values for volume
// range from 0.0f (muted) to 1.0f (full volume). This value affects all
// channels proportionately for multi-channel audio streams.
- void SetVolume(float volume);
+ virtual void SetVolume(float volume) = 0;
// Returns the current media playback time, which progresses from 0 until
// GetMediaDuration().
- base::TimeDelta GetMediaTime() const;
+ virtual base::TimeDelta GetMediaTime() const = 0;
// Get approximate time ranges of buffered media.
- Ranges<base::TimeDelta> GetBufferedTimeRanges() const;
+ virtual Ranges<base::TimeDelta> GetBufferedTimeRanges() const = 0;
// Get the duration of the media in microseconds. If the duration has not
// been determined yet, then returns 0.
- base::TimeDelta GetMediaDuration() const;
+ virtual base::TimeDelta GetMediaDuration() const = 0;
// Return true if loading progress has been made since the last time this
// method was called.
- bool DidLoadingProgress();
+ virtual bool DidLoadingProgress() = 0;
// Gets the current pipeline statistics.
- PipelineStatistics GetStatistics() const;
-
- void SetCdm(CdmContext* cdm_context, const CdmAttachedCB& cdm_attached_cb);
-
- void SetErrorForTesting(PipelineStatus status);
- bool HasWeakPtrsForTesting() const;
-
- private:
- FRIEND_TEST_ALL_PREFIXES(PipelineTest, GetBufferedTimeRanges);
- FRIEND_TEST_ALL_PREFIXES(PipelineTest, EndedCallback);
- FRIEND_TEST_ALL_PREFIXES(PipelineTest, AudioStreamShorterThanVideo);
- friend class MediaLog;
-
- // Pipeline states, as described above.
- enum State {
- kCreated,
- kInitDemuxer,
- kInitRenderer,
- kSeeking,
- kPlaying,
- kStopping,
- kStopped,
- kSuspending,
- kSuspended,
- kResuming,
- };
-
- // Updates |state_|. All state transitions should use this call.
- void SetState(State next_state);
-
- static const char* GetStateString(State state);
- State GetNextState() const;
-
- // Helper method that runs & resets |seek_cb_| and resets |seek_timestamp_|
- // and |seek_pending_|.
- void FinishSeek();
-
- // DemuxerHost implementaion.
- void OnBufferedTimeRangesChanged(
- const Ranges<base::TimeDelta>& ranges) override;
- void SetDuration(base::TimeDelta duration) override;
- void OnDemuxerError(PipelineStatus error) override;
- void AddTextStream(DemuxerStream* text_stream,
- const TextTrackConfig& config) override;
- void RemoveTextStream(DemuxerStream* text_stream) override;
-
- // Callback executed when a rendering error happened, initiating the teardown
- // sequence.
- void OnError(PipelineStatus error);
-
- // Callback executed by filters to update statistics.
- void OnUpdateStatistics(const PipelineStatistics& stats_delta);
-
- // The following "task" methods correspond to the public methods, but these
- // methods are run as the result of posting a task to the Pipeline's
- // task runner.
- void StartTask();
-
- // Suspends the pipeline, discarding the current renderer.
- void SuspendTask(const PipelineStatusCB& suspend_cb);
-
- // Resumes the pipeline with a new renderer, and initializes it with a seek.
- void ResumeTask(scoped_ptr<Renderer> renderer,
- base::TimeDelta timestamp,
- const PipelineStatusCB& seek_sb);
-
- // Stops and destroys all filters, placing the pipeline in the kStopped state.
- void StopTask(const base::Closure& stop_cb);
-
- // Carries out stopping and destroying all filters, placing the pipeline in
- // the kStopped state.
- void ErrorChangedTask(PipelineStatus error);
-
- // Carries out notifying filters that the playback rate has changed.
- void PlaybackRateChangedTask(double playback_rate);
-
- // Carries out notifying filters that the volume has changed.
- void VolumeChangedTask(float volume);
-
- // Carries out notifying filters that we are seeking to a new timestamp.
- void SeekTask(base::TimeDelta time, const PipelineStatusCB& seek_cb);
-
- // Carries out setting the |cdm_context| in |renderer_|, and then fires
- // |cdm_attached_cb| with the result. If |renderer_| is null,
- // |cdm_attached_cb| will be fired immediately with true, and |cdm_context|
- // will be set in |renderer_| later when |renderer_| is created.
- void SetCdmTask(CdmContext* cdm_context,
- const CdmAttachedCB& cdm_attached_cb);
-
- // Callbacks executed when a renderer has ended.
- void OnRendererEnded();
- void OnTextRendererEnded();
- void RunEndedCallbackIfNeeded();
-
- scoped_ptr<TextRenderer> CreateTextRenderer();
-
- // Carries out adding a new text stream to the text renderer.
- void AddTextStreamTask(DemuxerStream* text_stream,
- const TextTrackConfig& config);
-
- // Carries out removing a text stream from the text renderer.
- void RemoveTextStreamTask(DemuxerStream* text_stream);
-
- // Callbacks executed when a text track is added.
- void OnAddTextTrack(const TextTrackConfig& config,
- const AddTextTrackDoneCB& done_cb);
-
- // Kicks off initialization for each media object, executing |done_cb| with
- // the result when completed.
- void InitializeDemuxer(const PipelineStatusCB& done_cb);
- void InitializeRenderer(const PipelineStatusCB& done_cb);
-
- void StateTransitionTask(PipelineStatus status);
-
- // Initiates an asynchronous pause-flush-seek-preroll call sequence
- // executing |done_cb| with the final status when completed.
- void DoSeek(base::TimeDelta seek_timestamp, const PipelineStatusCB& done_cb);
-
- // Initiates an asynchronous pause-flush-stop call sequence executing
- // |done_cb| when completed.
- void DoStop(const PipelineStatusCB& done_cb);
- void OnStopCompleted(PipelineStatus status);
-
- void ReportMetadata();
-
- void BufferingStateChanged(BufferingState new_buffering_state);
-
- // Task runner used to execute pipeline tasks.
- scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
-
- // MediaLog to which to log events.
- scoped_refptr<MediaLog> media_log_;
-
- // Lock used to serialize access for the following data members.
- mutable base::Lock lock_;
-
- // Whether or not the pipeline is running.
- bool running_;
-
- // Amount of available buffered data as reported by |demuxer_|.
- Ranges<base::TimeDelta> buffered_time_ranges_;
-
- // True when OnBufferedTimeRangesChanged() has been called more recently than
- // DidLoadingProgress().
- bool did_loading_progress_;
-
- // Current volume level (from 0.0f to 1.0f). This value is set immediately
- // via SetVolume() and a task is dispatched on the task runner to notify the
- // filters.
- float volume_;
-
- // Current playback rate (>= 0.0). This value is set immediately via
- // SetPlaybackRate() and a task is dispatched on the task runner to notify
- // the filters.
- double playback_rate_;
-
- // Current duration as reported by |demuxer_|.
- base::TimeDelta duration_;
-
- // Status of the pipeline. Initialized to PIPELINE_OK which indicates that
- // the pipeline is operating correctly. Any other value indicates that the
- // pipeline is stopped or is stopping. Clients can call the Stop() method to
- // reset the pipeline state, and restore this to PIPELINE_OK.
- PipelineStatus status_;
-
- // The following data members are only accessed by tasks posted to
- // |task_runner_|.
-
- // Member that tracks the current state.
- State state_;
-
- // The timestamp to start playback from after starting/seeking/resuming has
- // completed.
- base::TimeDelta start_timestamp_;
-
- // The media timestamp to return while the pipeline is suspended. Otherwise
- // set to kNoTimestamp().
- base::TimeDelta suspend_timestamp_;
-
- // Whether we've received the audio/video/text ended events.
- bool renderer_ended_;
- bool text_renderer_ended_;
-
- // Temporary callback used for Start(), Seek(), and Resume().
- PipelineStatusCB seek_cb_;
-
- // Temporary callback used for Stop().
- base::Closure stop_cb_;
-
- // Temporary callback used for Suspend().
- PipelineStatusCB suspend_cb_;
-
- // Permanent callbacks passed in via Start().
- base::Closure ended_cb_;
- PipelineStatusCB error_cb_;
- PipelineMetadataCB metadata_cb_;
- BufferingStateCB buffering_state_cb_;
- base::Closure duration_change_cb_;
- AddTextTrackCB add_text_track_cb_;
- base::Closure waiting_for_decryption_key_cb_;
-
- // Holds the initialized demuxer. Used for seeking. Owned by client.
- Demuxer* demuxer_;
-
- // Holds the initialized renderers. Used for setting the volume,
- // playback rate, and determining when playback has finished.
- scoped_ptr<Renderer> renderer_;
- scoped_ptr<TextRenderer> text_renderer_;
-
- PipelineStatistics statistics_;
-
- scoped_ptr<SerialRunner> pending_callbacks_;
-
- // CdmContext to be used to decrypt (and decode) encrypted stream in this
- // pipeline. Non-null only when SetCdm() is called and the pipeline has not
- // been started. Then during Start(), this value will be set on |renderer_|.
- CdmContext* pending_cdm_context_;
-
- base::ThreadChecker thread_checker_;
-
- // NOTE: Weak pointers must be invalidated before all other member variables.
- base::WeakPtrFactory<Pipeline> weak_factory_;
+ virtual PipelineStatistics GetStatistics() const = 0;
- DISALLOW_COPY_AND_ASSIGN(Pipeline);
+ virtual void SetCdm(CdmContext* cdm_context,
+ const CdmAttachedCB& cdm_attached_cb) = 0;
};
} // namespace media
diff --git a/chromium/media/base/pipeline.cc b/chromium/media/base/pipeline_impl.cc
index 867d6308a8e..5f4705b5d26 100644
--- a/chromium/media/base/pipeline.cc
+++ b/chromium/media/base/pipeline_impl.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "media/base/pipeline.h"
+#include "media/base/pipeline_impl.h"
#include <algorithm>
#include <utility>
@@ -32,7 +32,7 @@ using base::TimeDelta;
namespace media {
-Pipeline::Pipeline(
+PipelineImpl::PipelineImpl(
const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
MediaLog* media_log)
: task_runner_(task_runner),
@@ -47,12 +47,13 @@ Pipeline::Pipeline(
renderer_ended_(false),
text_renderer_ended_(false),
demuxer_(NULL),
- pending_cdm_context_(nullptr),
+ cdm_context_(nullptr),
weak_factory_(this) {
+ weak_this_ = weak_factory_.GetWeakPtr();
media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(kCreated));
}
-Pipeline::~Pipeline() {
+PipelineImpl::~PipelineImpl() {
DCHECK(thread_checker_.CalledOnValidThread())
<< "Pipeline must be destroyed on same thread that created it";
DCHECK(!running_) << "Stop() must complete before destroying object";
@@ -60,16 +61,16 @@ Pipeline::~Pipeline() {
DCHECK(seek_cb_.is_null());
}
-void Pipeline::Start(Demuxer* demuxer,
- scoped_ptr<Renderer> renderer,
- const base::Closure& ended_cb,
- const PipelineStatusCB& error_cb,
- const PipelineStatusCB& seek_cb,
- const PipelineMetadataCB& metadata_cb,
- const BufferingStateCB& buffering_state_cb,
- const base::Closure& duration_change_cb,
- const AddTextTrackCB& add_text_track_cb,
- const base::Closure& waiting_for_decryption_key_cb) {
+void PipelineImpl::Start(Demuxer* demuxer,
+ scoped_ptr<Renderer> renderer,
+ const base::Closure& ended_cb,
+ const PipelineStatusCB& error_cb,
+ const PipelineStatusCB& seek_cb,
+ const PipelineMetadataCB& metadata_cb,
+ const BufferingStateCB& buffering_state_cb,
+ const base::Closure& duration_change_cb,
+ const AddTextTrackCB& add_text_track_cb,
+ const base::Closure& waiting_for_decryption_key_cb) {
DCHECK(!ended_cb.is_null());
DCHECK(!error_cb.is_null());
DCHECK(!seek_cb.is_null());
@@ -91,41 +92,38 @@ void Pipeline::Start(Demuxer* demuxer,
add_text_track_cb_ = add_text_track_cb;
waiting_for_decryption_key_cb_ = waiting_for_decryption_key_cb;
- task_runner_->PostTask(
- FROM_HERE, base::Bind(&Pipeline::StartTask, weak_factory_.GetWeakPtr()));
+ task_runner_->PostTask(FROM_HERE,
+ base::Bind(&PipelineImpl::StartTask, weak_this_));
}
-void Pipeline::Stop(const base::Closure& stop_cb) {
+void PipelineImpl::Stop(const base::Closure& stop_cb) {
DVLOG(2) << __FUNCTION__;
task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&Pipeline::StopTask, weak_factory_.GetWeakPtr(), stop_cb));
+ FROM_HERE, base::Bind(&PipelineImpl::StopTask, weak_this_, stop_cb));
}
-void Pipeline::Seek(TimeDelta time, const PipelineStatusCB& seek_cb) {
+void PipelineImpl::Seek(TimeDelta time, const PipelineStatusCB& seek_cb) {
base::AutoLock auto_lock(lock_);
if (!running_) {
DLOG(ERROR) << "Media pipeline isn't running. Ignoring Seek().";
return;
}
- task_runner_->PostTask(
- FROM_HERE,
- base::Bind(
- &Pipeline::SeekTask, weak_factory_.GetWeakPtr(), time, seek_cb));
+ task_runner_->PostTask(FROM_HERE, base::Bind(&PipelineImpl::SeekTask,
+ weak_this_, time, seek_cb));
}
-bool Pipeline::IsRunning() const {
+bool PipelineImpl::IsRunning() const {
base::AutoLock auto_lock(lock_);
return running_;
}
-double Pipeline::GetPlaybackRate() const {
+double PipelineImpl::GetPlaybackRate() const {
base::AutoLock auto_lock(lock_);
return playback_rate_;
}
-void Pipeline::SetPlaybackRate(double playback_rate) {
+void PipelineImpl::SetPlaybackRate(double playback_rate) {
if (playback_rate < 0.0)
return;
@@ -133,33 +131,30 @@ void Pipeline::SetPlaybackRate(double playback_rate) {
playback_rate_ = playback_rate;
if (running_) {
task_runner_->PostTask(FROM_HERE,
- base::Bind(&Pipeline::PlaybackRateChangedTask,
- weak_factory_.GetWeakPtr(),
- playback_rate));
+ base::Bind(&PipelineImpl::PlaybackRateChangedTask,
+ weak_this_, playback_rate));
}
}
-void Pipeline::Suspend(const PipelineStatusCB& suspend_cb) {
- task_runner_->PostTask(
- FROM_HERE, base::Bind(&Pipeline::SuspendTask, weak_factory_.GetWeakPtr(),
- suspend_cb));
+void PipelineImpl::Suspend(const PipelineStatusCB& suspend_cb) {
+ task_runner_->PostTask(FROM_HERE, base::Bind(&PipelineImpl::SuspendTask,
+ weak_this_, suspend_cb));
}
-void Pipeline::Resume(scoped_ptr<Renderer> renderer,
- base::TimeDelta timestamp,
- const PipelineStatusCB& seek_cb) {
+void PipelineImpl::Resume(scoped_ptr<Renderer> renderer,
+ base::TimeDelta timestamp,
+ const PipelineStatusCB& seek_cb) {
task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&Pipeline::ResumeTask, weak_factory_.GetWeakPtr(),
- base::Passed(std::move(renderer)), timestamp, seek_cb));
+ FROM_HERE, base::Bind(&PipelineImpl::ResumeTask, weak_this_,
+ base::Passed(&renderer), timestamp, seek_cb));
}
-float Pipeline::GetVolume() const {
+float PipelineImpl::GetVolume() const {
base::AutoLock auto_lock(lock_);
return volume_;
}
-void Pipeline::SetVolume(float volume) {
+void PipelineImpl::SetVolume(float volume) {
if (volume < 0.0f || volume > 1.0f)
return;
@@ -168,12 +163,11 @@ void Pipeline::SetVolume(float volume) {
if (running_) {
task_runner_->PostTask(
FROM_HERE,
- base::Bind(
- &Pipeline::VolumeChangedTask, weak_factory_.GetWeakPtr(), volume));
+ base::Bind(&PipelineImpl::VolumeChangedTask, weak_this_, volume));
}
}
-TimeDelta Pipeline::GetMediaTime() const {
+TimeDelta PipelineImpl::GetMediaTime() const {
base::AutoLock auto_lock(lock_);
if (suspend_timestamp_ != kNoTimestamp())
return suspend_timestamp_;
@@ -181,54 +175,56 @@ TimeDelta Pipeline::GetMediaTime() const {
: TimeDelta();
}
-Ranges<TimeDelta> Pipeline::GetBufferedTimeRanges() const {
+Ranges<TimeDelta> PipelineImpl::GetBufferedTimeRanges() const {
base::AutoLock auto_lock(lock_);
return buffered_time_ranges_;
}
-TimeDelta Pipeline::GetMediaDuration() const {
+TimeDelta PipelineImpl::GetMediaDuration() const {
base::AutoLock auto_lock(lock_);
return duration_;
}
-bool Pipeline::DidLoadingProgress() {
+bool PipelineImpl::DidLoadingProgress() {
base::AutoLock auto_lock(lock_);
bool ret = did_loading_progress_;
did_loading_progress_ = false;
return ret;
}
-PipelineStatistics Pipeline::GetStatistics() const {
+PipelineStatistics PipelineImpl::GetStatistics() const {
base::AutoLock auto_lock(lock_);
return statistics_;
}
-void Pipeline::SetCdm(CdmContext* cdm_context,
- const CdmAttachedCB& cdm_attached_cb) {
+void PipelineImpl::SetCdm(CdmContext* cdm_context,
+ const CdmAttachedCB& cdm_attached_cb) {
task_runner_->PostTask(
- FROM_HERE, base::Bind(&Pipeline::SetCdmTask, weak_factory_.GetWeakPtr(),
- cdm_context, cdm_attached_cb));
+ FROM_HERE, base::Bind(&PipelineImpl::SetCdmTask, weak_this_, cdm_context,
+ cdm_attached_cb));
}
-void Pipeline::SetErrorForTesting(PipelineStatus status) {
+void PipelineImpl::SetErrorForTesting(PipelineStatus status) {
OnError(status);
}
-bool Pipeline::HasWeakPtrsForTesting() const {
+bool PipelineImpl::HasWeakPtrsForTesting() const {
DCHECK(task_runner_->BelongsToCurrentThread());
return weak_factory_.HasWeakPtrs();
}
-void Pipeline::SetState(State next_state) {
+void PipelineImpl::SetState(State next_state) {
DVLOG(1) << GetStateString(state_) << " -> " << GetStateString(next_state);
state_ = next_state;
media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(next_state));
}
-#define RETURN_STRING(state) case state: return #state;
+#define RETURN_STRING(state) \
+ case state: \
+ return #state;
-const char* Pipeline::GetStateString(State state) {
+const char* PipelineImpl::GetStateString(State state) {
switch (state) {
RETURN_STRING(kCreated);
RETURN_STRING(kInitDemuxer);
@@ -247,10 +243,9 @@ const char* Pipeline::GetStateString(State state) {
#undef RETURN_STRING
-Pipeline::State Pipeline::GetNextState() const {
+PipelineImpl::State PipelineImpl::GetNextState() const {
DCHECK(task_runner_->BelongsToCurrentThread());
- DCHECK(stop_cb_.is_null())
- << "State transitions don't happen when stopping";
+ DCHECK(stop_cb_.is_null()) << "State transitions don't happen when stopping";
DCHECK_EQ(status_, PIPELINE_OK)
<< "State transitions don't happen when there's an error: " << status_;
@@ -283,44 +278,38 @@ Pipeline::State Pipeline::GetNextState() const {
return state_;
}
-void Pipeline::OnDemuxerError(PipelineStatus error) {
- task_runner_->PostTask(FROM_HERE,
- base::Bind(&Pipeline::ErrorChangedTask,
- weak_factory_.GetWeakPtr(),
- error));
+void PipelineImpl::OnDemuxerError(PipelineStatus error) {
+ task_runner_->PostTask(FROM_HERE, base::Bind(&PipelineImpl::ErrorChangedTask,
+ weak_this_, error));
}
-void Pipeline::AddTextStream(DemuxerStream* text_stream,
- const TextTrackConfig& config) {
- task_runner_->PostTask(FROM_HERE,
- base::Bind(&Pipeline::AddTextStreamTask,
- weak_factory_.GetWeakPtr(),
- text_stream,
- config));
+void PipelineImpl::AddTextStream(DemuxerStream* text_stream,
+ const TextTrackConfig& config) {
+ task_runner_->PostTask(
+ FROM_HERE, base::Bind(&PipelineImpl::AddTextStreamTask, weak_this_,
+ text_stream, config));
}
-void Pipeline::RemoveTextStream(DemuxerStream* text_stream) {
- task_runner_->PostTask(FROM_HERE,
- base::Bind(&Pipeline::RemoveTextStreamTask,
- weak_factory_.GetWeakPtr(),
- text_stream));
+void PipelineImpl::RemoveTextStream(DemuxerStream* text_stream) {
+ task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&PipelineImpl::RemoveTextStreamTask, weak_this_, text_stream));
}
-void Pipeline::OnError(PipelineStatus error) {
+void PipelineImpl::OnError(PipelineStatus error) {
DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK(IsRunning());
DCHECK_NE(PIPELINE_OK, error);
VLOG(1) << "Media pipeline error: " << error;
- task_runner_->PostTask(FROM_HERE, base::Bind(
- &Pipeline::ErrorChangedTask, weak_factory_.GetWeakPtr(), error));
+ task_runner_->PostTask(FROM_HERE, base::Bind(&PipelineImpl::ErrorChangedTask,
+ weak_this_, error));
}
-void Pipeline::SetDuration(TimeDelta duration) {
+void PipelineImpl::SetDuration(TimeDelta duration) {
DCHECK(IsRunning());
- media_log_->AddEvent(
- media_log_->CreateTimeEvent(
- MediaLogEvent::DURATION_SET, "duration", duration));
+ media_log_->AddEvent(media_log_->CreateTimeEvent(MediaLogEvent::DURATION_SET,
+ "duration", duration));
UMA_HISTOGRAM_LONG_TIMES("Media.Duration", duration);
base::AutoLock auto_lock(lock_);
@@ -329,7 +318,7 @@ void Pipeline::SetDuration(TimeDelta duration) {
duration_change_cb_.Run();
}
-void Pipeline::StateTransitionTask(PipelineStatus status) {
+void PipelineImpl::StateTransitionTask(PipelineStatus status) {
DCHECK(task_runner_->BelongsToCurrentThread());
// No-op any state transitions if we're stopping.
@@ -353,7 +342,7 @@ void Pipeline::StateTransitionTask(PipelineStatus status) {
pending_callbacks_.reset();
PipelineStatusCB done_cb =
- base::Bind(&Pipeline::StateTransitionTask, weak_factory_.GetWeakPtr());
+ base::Bind(&PipelineImpl::StateTransitionTask, weak_this_);
// Switch states, performing any entrance actions for the new state as well.
SetState(GetNextState());
@@ -391,6 +380,8 @@ void Pipeline::StateTransitionTask(PipelineStatus status) {
case kSuspended:
renderer_.reset();
+ statistics_.audio_memory_usage = 0;
+ statistics_.video_memory_usage = 0;
base::ResetAndReturn(&suspend_cb_).Run(PIPELINE_OK);
return;
@@ -410,8 +401,8 @@ void Pipeline::StateTransitionTask(PipelineStatus status) {
//
// That being said, deleting the renderers while keeping |pending_callbacks_|
// running on the media thread would result in crashes.
-void Pipeline::DoSeek(TimeDelta seek_timestamp,
- const PipelineStatusCB& done_cb) {
+void PipelineImpl::DoSeek(TimeDelta seek_timestamp,
+ const PipelineStatusCB& done_cb) {
DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK(!pending_callbacks_.get());
DCHECK_EQ(state_, kSeeking);
@@ -419,8 +410,8 @@ void Pipeline::DoSeek(TimeDelta seek_timestamp,
// Pause.
if (text_renderer_) {
- bound_fns.Push(base::Bind(
- &TextRenderer::Pause, base::Unretained(text_renderer_.get())));
+ bound_fns.Push(base::Bind(&TextRenderer::Pause,
+ base::Unretained(text_renderer_.get())));
}
// Flush.
@@ -429,18 +420,18 @@ void Pipeline::DoSeek(TimeDelta seek_timestamp,
base::Bind(&Renderer::Flush, base::Unretained(renderer_.get())));
if (text_renderer_) {
- bound_fns.Push(base::Bind(
- &TextRenderer::Flush, base::Unretained(text_renderer_.get())));
+ bound_fns.Push(base::Bind(&TextRenderer::Flush,
+ base::Unretained(text_renderer_.get())));
}
// Seek demuxer.
- bound_fns.Push(base::Bind(
- &Demuxer::Seek, base::Unretained(demuxer_), seek_timestamp));
+ bound_fns.Push(
+ base::Bind(&Demuxer::Seek, base::Unretained(demuxer_), seek_timestamp));
pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb);
}
-void Pipeline::DoStop(const PipelineStatusCB& done_cb) {
+void PipelineImpl::DoStop(const PipelineStatusCB& done_cb) {
DVLOG(2) << __FUNCTION__;
DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK(!pending_callbacks_.get());
@@ -463,7 +454,7 @@ void Pipeline::DoStop(const PipelineStatusCB& done_cb) {
task_runner_->PostTask(FROM_HERE, base::Bind(done_cb, PIPELINE_OK));
}
-void Pipeline::OnStopCompleted(PipelineStatus status) {
+void PipelineImpl::OnStopCompleted(PipelineStatus status) {
DVLOG(2) << __FUNCTION__;
DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK_EQ(state_, kStopping);
@@ -507,16 +498,15 @@ void Pipeline::OnStopCompleted(PipelineStatus status) {
}
}
-void Pipeline::OnBufferedTimeRangesChanged(
+void PipelineImpl::OnBufferedTimeRangesChanged(
const Ranges<base::TimeDelta>& ranges) {
- DCHECK(IsRunning());
base::AutoLock auto_lock(lock_);
buffered_time_ranges_ = ranges;
did_loading_progress_ = true;
}
// Called from any thread.
-void Pipeline::OnUpdateStatistics(const PipelineStatistics& stats_delta) {
+void PipelineImpl::OnUpdateStatistics(const PipelineStatistics& stats_delta) {
base::AutoLock auto_lock(lock_);
statistics_.audio_bytes_decoded += stats_delta.audio_bytes_decoded;
statistics_.video_bytes_decoded += stats_delta.video_bytes_decoded;
@@ -526,7 +516,7 @@ void Pipeline::OnUpdateStatistics(const PipelineStatistics& stats_delta) {
statistics_.video_memory_usage += stats_delta.video_memory_usage;
}
-void Pipeline::StartTask() {
+void PipelineImpl::StartTask() {
DCHECK(task_runner_->BelongsToCurrentThread());
CHECK_EQ(kCreated, state_)
@@ -535,19 +525,13 @@ void Pipeline::StartTask() {
text_renderer_ = CreateTextRenderer();
if (text_renderer_) {
text_renderer_->Initialize(
- base::Bind(&Pipeline::OnTextRendererEnded, weak_factory_.GetWeakPtr()));
- }
-
- // Set CDM early to avoid unnecessary delay in Renderer::Initialize().
- if (pending_cdm_context_) {
- renderer_->SetCdm(pending_cdm_context_, base::Bind(&IgnoreCdmAttached));
- pending_cdm_context_ = nullptr;
+ base::Bind(&PipelineImpl::OnTextRendererEnded, weak_this_));
}
StateTransitionTask(PIPELINE_OK);
}
-void Pipeline::StopTask(const base::Closure& stop_cb) {
+void PipelineImpl::StopTask(const base::Closure& stop_cb) {
DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK(stop_cb_.is_null());
@@ -581,26 +565,32 @@ void Pipeline::StopTask(const base::Closure& stop_cb) {
SetState(kStopping);
pending_callbacks_.reset();
- DoStop(base::Bind(&Pipeline::OnStopCompleted, weak_factory_.GetWeakPtr()));
+ DoStop(base::Bind(&PipelineImpl::OnStopCompleted, weak_this_));
}
-void Pipeline::ErrorChangedTask(PipelineStatus error) {
+void PipelineImpl::ErrorChangedTask(PipelineStatus error) {
DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK_NE(PIPELINE_OK, error) << "PIPELINE_OK isn't an error!";
- media_log_->AddEvent(media_log_->CreatePipelineErrorEvent(error));
+ // Don't report pipeline error events to the media log here. The embedder will
+ // log this when |error_cb_| is called. If the pipeline is already stopped or
+ // stopping we also don't want to log any event. In case we are suspending or
+ // suspended, the error may be recoverable, so don't propagate it now, instead
+ // let the subsequent seek during resume propagate it if it's unrecoverable.
- if (state_ == kStopping || state_ == kStopped)
+ if (state_ == kStopping || state_ == kStopped || state_ == kSuspending ||
+ state_ == kSuspended) {
return;
+ }
SetState(kStopping);
pending_callbacks_.reset();
status_ = error;
- DoStop(base::Bind(&Pipeline::OnStopCompleted, weak_factory_.GetWeakPtr()));
+ DoStop(base::Bind(&PipelineImpl::OnStopCompleted, weak_this_));
}
-void Pipeline::PlaybackRateChangedTask(double playback_rate) {
+void PipelineImpl::PlaybackRateChangedTask(double playback_rate) {
DCHECK(task_runner_->BelongsToCurrentThread());
// Playback rate changes are only carried out while playing.
@@ -610,7 +600,7 @@ void Pipeline::PlaybackRateChangedTask(double playback_rate) {
renderer_->SetPlaybackRate(playback_rate);
}
-void Pipeline::VolumeChangedTask(float volume) {
+void PipelineImpl::VolumeChangedTask(float volume) {
DCHECK(task_runner_->BelongsToCurrentThread());
// Volume changes are only carried out while playing.
@@ -620,7 +610,7 @@ void Pipeline::VolumeChangedTask(float volume) {
renderer_->SetVolume(volume);
}
-void Pipeline::SeekTask(TimeDelta time, const PipelineStatusCB& seek_cb) {
+void PipelineImpl::SeekTask(TimeDelta time, const PipelineStatusCB& seek_cb) {
DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK(stop_cb_.is_null());
@@ -643,11 +633,11 @@ void Pipeline::SeekTask(TimeDelta time, const PipelineStatusCB& seek_cb) {
text_renderer_ended_ = false;
start_timestamp_ = seek_timestamp;
- DoSeek(seek_timestamp, base::Bind(&Pipeline::StateTransitionTask,
- weak_factory_.GetWeakPtr()));
+ DoSeek(seek_timestamp,
+ base::Bind(&PipelineImpl::StateTransitionTask, weak_this_));
}
-void Pipeline::SuspendTask(const PipelineStatusCB& suspend_cb) {
+void PipelineImpl::SuspendTask(const PipelineStatusCB& suspend_cb) {
DCHECK(task_runner_->BelongsToCurrentThread());
// Suppress suspending if we're not playing.
@@ -690,13 +680,12 @@ void Pipeline::SuspendTask(const PipelineStatusCB& suspend_cb) {
}
pending_callbacks_ = SerialRunner::Run(
- fns,
- base::Bind(&Pipeline::StateTransitionTask, weak_factory_.GetWeakPtr()));
+ fns, base::Bind(&PipelineImpl::StateTransitionTask, weak_this_));
}
-void Pipeline::ResumeTask(scoped_ptr<Renderer> renderer,
- base::TimeDelta timestamp,
- const PipelineStatusCB& seek_cb) {
+void PipelineImpl::ResumeTask(scoped_ptr<Renderer> renderer,
+ base::TimeDelta timestamp,
+ const PipelineStatusCB& seek_cb) {
DCHECK(task_runner_->BelongsToCurrentThread());
// Suppress resuming if we're not suspended.
@@ -724,30 +713,40 @@ void Pipeline::ResumeTask(scoped_ptr<Renderer> renderer,
// kInitDemuxer, and even if we did the current code would seek to the start
// instead of |timestamp|).
SerialRunner::Queue fns;
- base::WeakPtr<Pipeline> weak_this = weak_factory_.GetWeakPtr();
fns.Push(
base::Bind(&Demuxer::Seek, base::Unretained(demuxer_), start_timestamp_));
- fns.Push(base::Bind(&Pipeline::InitializeRenderer, weak_this));
+ fns.Push(base::Bind(&PipelineImpl::InitializeRenderer, weak_this_));
pending_callbacks_ = SerialRunner::Run(
- fns, base::Bind(&Pipeline::StateTransitionTask, weak_this));
+ fns, base::Bind(&PipelineImpl::StateTransitionTask, weak_this_));
}
-void Pipeline::SetCdmTask(CdmContext* cdm_context,
- const CdmAttachedCB& cdm_attached_cb) {
+void PipelineImpl::SetCdmTask(CdmContext* cdm_context,
+ const CdmAttachedCB& cdm_attached_cb) {
base::AutoLock auto_lock(lock_);
if (!renderer_) {
- pending_cdm_context_ = cdm_context;
+ cdm_context_ = cdm_context;
cdm_attached_cb.Run(true);
return;
}
- renderer_->SetCdm(cdm_context, cdm_attached_cb);
+ renderer_->SetCdm(cdm_context,
+ base::Bind(&PipelineImpl::OnCdmAttached, weak_this_,
+ cdm_attached_cb, cdm_context));
}
-void Pipeline::OnRendererEnded() {
+void PipelineImpl::OnCdmAttached(const CdmAttachedCB& cdm_attached_cb,
+ CdmContext* cdm_context,
+ bool success) {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+ if (success)
+ cdm_context_ = cdm_context;
+ cdm_attached_cb.Run(success);
+}
+
+void PipelineImpl::OnRendererEnded() {
DCHECK(task_runner_->BelongsToCurrentThread());
media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::ENDED));
@@ -760,7 +759,7 @@ void Pipeline::OnRendererEnded() {
RunEndedCallbackIfNeeded();
}
-void Pipeline::OnTextRendererEnded() {
+void PipelineImpl::OnTextRendererEnded() {
DCHECK(task_runner_->BelongsToCurrentThread());
media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::TEXT_ENDED));
@@ -773,7 +772,7 @@ void Pipeline::OnTextRendererEnded() {
RunEndedCallbackIfNeeded();
}
-void Pipeline::RunEndedCallbackIfNeeded() {
+void PipelineImpl::RunEndedCallbackIfNeeded() {
DCHECK(task_runner_->BelongsToCurrentThread());
if (renderer_ && !renderer_ended_)
@@ -786,7 +785,7 @@ void Pipeline::RunEndedCallbackIfNeeded() {
ended_cb_.Run();
}
-scoped_ptr<TextRenderer> Pipeline::CreateTextRenderer() {
+scoped_ptr<TextRenderer> PipelineImpl::CreateTextRenderer() {
DCHECK(task_runner_->BelongsToCurrentThread());
const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
@@ -794,12 +793,11 @@ scoped_ptr<TextRenderer> Pipeline::CreateTextRenderer() {
return scoped_ptr<media::TextRenderer>();
return scoped_ptr<media::TextRenderer>(new media::TextRenderer(
- task_runner_,
- base::Bind(&Pipeline::OnAddTextTrack, weak_factory_.GetWeakPtr())));
+ task_runner_, base::Bind(&PipelineImpl::OnAddTextTrack, weak_this_)));
}
-void Pipeline::AddTextStreamTask(DemuxerStream* text_stream,
- const TextTrackConfig& config) {
+void PipelineImpl::AddTextStreamTask(DemuxerStream* text_stream,
+ const TextTrackConfig& config) {
DCHECK(task_runner_->BelongsToCurrentThread());
// TODO(matthewjheaney): fix up text_ended_ when text stream
// is added (http://crbug.com/321446).
@@ -807,24 +805,24 @@ void Pipeline::AddTextStreamTask(DemuxerStream* text_stream,
text_renderer_->AddTextStream(text_stream, config);
}
-void Pipeline::RemoveTextStreamTask(DemuxerStream* text_stream) {
+void PipelineImpl::RemoveTextStreamTask(DemuxerStream* text_stream) {
DCHECK(task_runner_->BelongsToCurrentThread());
if (text_renderer_)
text_renderer_->RemoveTextStream(text_stream);
}
-void Pipeline::OnAddTextTrack(const TextTrackConfig& config,
- const AddTextTrackDoneCB& done_cb) {
+void PipelineImpl::OnAddTextTrack(const TextTrackConfig& config,
+ const AddTextTrackDoneCB& done_cb) {
DCHECK(task_runner_->BelongsToCurrentThread());
add_text_track_cb_.Run(config, done_cb);
}
-void Pipeline::InitializeDemuxer(const PipelineStatusCB& done_cb) {
+void PipelineImpl::InitializeDemuxer(const PipelineStatusCB& done_cb) {
DCHECK(task_runner_->BelongsToCurrentThread());
- demuxer_->Initialize(this, done_cb, text_renderer_);
+ demuxer_->Initialize(this, done_cb, !!text_renderer_);
}
-void Pipeline::InitializeRenderer(const PipelineStatusCB& done_cb) {
+void PipelineImpl::InitializeRenderer(const PipelineStatusCB& done_cb) {
DCHECK(task_runner_->BelongsToCurrentThread());
if (!demuxer_->GetStream(DemuxerStream::AUDIO) &&
@@ -837,18 +835,19 @@ void Pipeline::InitializeRenderer(const PipelineStatusCB& done_cb) {
return;
}
- base::WeakPtr<Pipeline> weak_this = weak_factory_.GetWeakPtr();
+ if (cdm_context_)
+ renderer_->SetCdm(cdm_context_, base::Bind(&IgnoreCdmAttached));
+
renderer_->Initialize(
- demuxer_,
- done_cb,
- base::Bind(&Pipeline::OnUpdateStatistics, weak_this),
- base::Bind(&Pipeline::BufferingStateChanged, weak_this),
- base::Bind(&Pipeline::OnRendererEnded, weak_this),
- base::Bind(&Pipeline::OnError, weak_this),
+ demuxer_, done_cb,
+ base::Bind(&PipelineImpl::OnUpdateStatistics, weak_this_),
+ base::Bind(&PipelineImpl::BufferingStateChanged, weak_this_),
+ base::Bind(&PipelineImpl::OnRendererEnded, weak_this_),
+ base::Bind(&PipelineImpl::OnError, weak_this_),
waiting_for_decryption_key_cb_);
}
-void Pipeline::ReportMetadata() {
+void PipelineImpl::ReportMetadata() {
DCHECK(task_runner_->BelongsToCurrentThread());
PipelineMetadata metadata;
metadata.timeline_offset = demuxer_->GetTimelineOffset();
@@ -864,7 +863,7 @@ void Pipeline::ReportMetadata() {
metadata_cb_.Run(metadata);
}
-void Pipeline::BufferingStateChanged(BufferingState new_buffering_state) {
+void PipelineImpl::BufferingStateChanged(BufferingState new_buffering_state) {
DVLOG(1) << __FUNCTION__ << "(" << new_buffering_state << ") ";
DCHECK(task_runner_->BelongsToCurrentThread());
buffering_state_cb_.Run(new_buffering_state);
diff --git a/chromium/media/base/pipeline_impl.h b/chromium/media/base/pipeline_impl.h
new file mode 100644
index 00000000000..7a6da3a7b51
--- /dev/null
+++ b/chromium/media/base/pipeline_impl.h
@@ -0,0 +1,346 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_BASE_PIPELINE_IMPL_H_
+#define MEDIA_BASE_PIPELINE_IMPL_H_
+
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/thread_checker.h"
+#include "base/time/time.h"
+#include "media/base/buffering_state.h"
+#include "media/base/cdm_context.h"
+#include "media/base/demuxer.h"
+#include "media/base/media_export.h"
+#include "media/base/pipeline.h"
+#include "media/base/pipeline_status.h"
+#include "media/base/ranges.h"
+#include "media/base/serial_runner.h"
+#include "media/base/text_track.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+}
+
+namespace media {
+
+class MediaLog;
+class Renderer;
+class TextRenderer;
+
+// Pipeline runs the media pipeline. Filters are created and called on the
+// task runner injected into this object. Pipeline works like a state
+// machine to perform asynchronous initialization, pausing, seeking and playing.
+//
+// Here's a state diagram that describes the lifetime of this object.
+//
+// [ *Created ] [ Any State ]
+// | Start() | Stop() / SetError()
+// V V
+// [ InitXXX (for each filter) ] [ Stopping ]
+// | |
+// V V
+// [ Playing ] <---------. [ Stopped ]
+// | | Seek() |
+// | V |
+// | [ Seeking ] ----'
+// | ^
+// | Suspend() |
+// V |
+// [ Suspending ] |
+// | |
+// V |
+// [ Suspended ] |
+// | Resume() |
+// V |
+// [ Resuming ] ---------'
+//
+// Initialization is a series of state transitions from "Created" through each
+// filter initialization state. When all filter initialization states have
+// completed, we simulate a Seek() to the beginning of the media to give filters
+// a chance to preroll. From then on the normal Seek() transitions are carried
+// out and we start playing the media.
+//
+// If any error ever happens, this object will transition to the "Error" state
+// from any state. If Stop() is ever called, this object will transition to
+// "Stopped" state.
+//
+// TODO(sandersd): It should be possible to pass through Suspended when going
+// from InitDemuxer to InitRenderer, thereby eliminating the Resuming state.
+// Some annoying differences between the two paths need to be removed first.
+class MEDIA_EXPORT PipelineImpl : public Pipeline, public DemuxerHost {
+ public:
+ // Constructs a media pipeline that will execute on |task_runner|.
+ PipelineImpl(const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
+ MediaLog* media_log);
+ ~PipelineImpl() override;
+
+ void SetErrorForTesting(PipelineStatus status);
+ bool HasWeakPtrsForTesting() const;
+
+ // Pipeline implementation.
+ void Start(Demuxer* demuxer,
+ scoped_ptr<Renderer> renderer,
+ const base::Closure& ended_cb,
+ const PipelineStatusCB& error_cb,
+ const PipelineStatusCB& seek_cb,
+ const PipelineMetadataCB& metadata_cb,
+ const BufferingStateCB& buffering_state_cb,
+ const base::Closure& duration_change_cb,
+ const AddTextTrackCB& add_text_track_cb,
+ const base::Closure& waiting_for_decryption_key_cb) override;
+ void Stop(const base::Closure& stop_cb) override;
+ void Seek(base::TimeDelta time, const PipelineStatusCB& seek_cb) override;
+ bool IsRunning() const override;
+ double GetPlaybackRate() const override;
+ void SetPlaybackRate(double playback_rate) override;
+ void Suspend(const PipelineStatusCB& suspend_cb) override;
+ void Resume(scoped_ptr<Renderer> renderer,
+ base::TimeDelta timestamp,
+ const PipelineStatusCB& seek_cb) override;
+ float GetVolume() const override;
+ void SetVolume(float volume) override;
+ base::TimeDelta GetMediaTime() const override;
+ Ranges<base::TimeDelta> GetBufferedTimeRanges() const override;
+ base::TimeDelta GetMediaDuration() const override;
+ bool DidLoadingProgress() override;
+ PipelineStatistics GetStatistics() const override;
+ void SetCdm(CdmContext* cdm_context,
+ const CdmAttachedCB& cdm_attached_cb) override;
+
+ private:
+ friend class MediaLog;
+ friend class PipelineImplTest;
+
+ // Pipeline states, as described above.
+ enum State {
+ kCreated,
+ kInitDemuxer,
+ kInitRenderer,
+ kSeeking,
+ kPlaying,
+ kStopping,
+ kStopped,
+ kSuspending,
+ kSuspended,
+ kResuming,
+ };
+
+ // Updates |state_|. All state transitions should use this call.
+ void SetState(State next_state);
+
+ static const char* GetStateString(State state);
+ State GetNextState() const;
+
+ // Helper method that runs & resets |seek_cb_| and resets |seek_timestamp_|
+ // and |seek_pending_|.
+ void FinishSeek();
+
+ // DemuxerHost implementaion.
+ void OnBufferedTimeRangesChanged(
+ const Ranges<base::TimeDelta>& ranges) override;
+ void SetDuration(base::TimeDelta duration) override;
+ void OnDemuxerError(PipelineStatus error) override;
+ void AddTextStream(DemuxerStream* text_stream,
+ const TextTrackConfig& config) override;
+ void RemoveTextStream(DemuxerStream* text_stream) override;
+
+ // Callback executed when a rendering error happened, initiating the teardown
+ // sequence.
+ void OnError(PipelineStatus error);
+
+ // Callback executed by filters to update statistics.
+ void OnUpdateStatistics(const PipelineStatistics& stats_delta);
+
+ // The following "task" methods correspond to the public methods, but these
+ // methods are run as the result of posting a task to the Pipeline's
+ // task runner.
+ void StartTask();
+
+ // Suspends the pipeline, discarding the current renderer.
+ void SuspendTask(const PipelineStatusCB& suspend_cb);
+
+ // Resumes the pipeline with a new renderer, and initializes it with a seek.
+ void ResumeTask(scoped_ptr<Renderer> renderer,
+ base::TimeDelta timestamp,
+ const PipelineStatusCB& seek_sb);
+
+ // Stops and destroys all filters, placing the pipeline in the kStopped state.
+ void StopTask(const base::Closure& stop_cb);
+
+ // Carries out stopping and destroying all filters, placing the pipeline in
+ // the kStopped state.
+ void ErrorChangedTask(PipelineStatus error);
+
+ // Carries out notifying filters that the playback rate has changed.
+ void PlaybackRateChangedTask(double playback_rate);
+
+ // Carries out notifying filters that the volume has changed.
+ void VolumeChangedTask(float volume);
+
+ // Carries out notifying filters that we are seeking to a new timestamp.
+ void SeekTask(base::TimeDelta time, const PipelineStatusCB& seek_cb);
+
+ // Carries out setting the |cdm_context| in |renderer_|, and then fires
+ // |cdm_attached_cb| with the result. If |renderer_| is null,
+ // |cdm_attached_cb| will be fired immediately with true, and |cdm_context|
+ // will be set in |renderer_| later when |renderer_| is created.
+ void SetCdmTask(CdmContext* cdm_context,
+ const CdmAttachedCB& cdm_attached_cb);
+ void OnCdmAttached(const CdmAttachedCB& cdm_attached_cb,
+ CdmContext* cdm_context,
+ bool success);
+
+ // Callbacks executed when a renderer has ended.
+ void OnRendererEnded();
+ void OnTextRendererEnded();
+ void RunEndedCallbackIfNeeded();
+
+ scoped_ptr<TextRenderer> CreateTextRenderer();
+
+ // Carries out adding a new text stream to the text renderer.
+ void AddTextStreamTask(DemuxerStream* text_stream,
+ const TextTrackConfig& config);
+
+ // Carries out removing a text stream from the text renderer.
+ void RemoveTextStreamTask(DemuxerStream* text_stream);
+
+ // Callbacks executed when a text track is added.
+ void OnAddTextTrack(const TextTrackConfig& config,
+ const AddTextTrackDoneCB& done_cb);
+
+ // Kicks off initialization for each media object, executing |done_cb| with
+ // the result when completed.
+ void InitializeDemuxer(const PipelineStatusCB& done_cb);
+ void InitializeRenderer(const PipelineStatusCB& done_cb);
+
+ void StateTransitionTask(PipelineStatus status);
+
+ // Initiates an asynchronous pause-flush-seek-preroll call sequence
+ // executing |done_cb| with the final status when completed.
+ void DoSeek(base::TimeDelta seek_timestamp, const PipelineStatusCB& done_cb);
+
+ // Initiates an asynchronous pause-flush-stop call sequence executing
+ // |done_cb| when completed.
+ void DoStop(const PipelineStatusCB& done_cb);
+ void OnStopCompleted(PipelineStatus status);
+
+ void ReportMetadata();
+
+ void BufferingStateChanged(BufferingState new_buffering_state);
+
+ // Task runner used to execute pipeline tasks.
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+
+ // MediaLog to which to log events.
+ scoped_refptr<MediaLog> media_log_;
+
+ // Lock used to serialize access for the following data members.
+ mutable base::Lock lock_;
+
+ // Whether or not the pipeline is running.
+ bool running_;
+
+ // Amount of available buffered data as reported by |demuxer_|.
+ Ranges<base::TimeDelta> buffered_time_ranges_;
+
+ // True when OnBufferedTimeRangesChanged() has been called more recently than
+ // DidLoadingProgress().
+ bool did_loading_progress_;
+
+ // Current volume level (from 0.0f to 1.0f). This value is set immediately
+ // via SetVolume() and a task is dispatched on the task runner to notify the
+ // filters.
+ float volume_;
+
+ // Current playback rate (>= 0.0). This value is set immediately via
+ // SetPlaybackRate() and a task is dispatched on the task runner to notify
+ // the filters.
+ double playback_rate_;
+
+ // Current duration as reported by |demuxer_|.
+ base::TimeDelta duration_;
+
+ // Status of the pipeline. Initialized to PIPELINE_OK which indicates that
+ // the pipeline is operating correctly. Any other value indicates that the
+ // pipeline is stopped or is stopping. Clients can call the Stop() method to
+ // reset the pipeline state, and restore this to PIPELINE_OK.
+ PipelineStatus status_;
+
+ // The following data members are only accessed by tasks posted to
+ // |task_runner_|.
+
+ // Member that tracks the current state.
+ State state_;
+
+ // The timestamp to start playback from after starting/seeking/resuming has
+ // completed.
+ base::TimeDelta start_timestamp_;
+
+ // The media timestamp to return while the pipeline is suspended. Otherwise
+ // set to kNoTimestamp().
+ base::TimeDelta suspend_timestamp_;
+
+ // Whether we've received the audio/video/text ended events.
+ bool renderer_ended_;
+ bool text_renderer_ended_;
+
+ // Temporary callback used for Start(), Seek(), and Resume().
+ PipelineStatusCB seek_cb_;
+
+ // Temporary callback used for Stop().
+ base::Closure stop_cb_;
+
+ // Temporary callback used for Suspend().
+ PipelineStatusCB suspend_cb_;
+
+ // Permanent callbacks passed in via Start().
+ base::Closure ended_cb_;
+ PipelineStatusCB error_cb_;
+ PipelineMetadataCB metadata_cb_;
+ BufferingStateCB buffering_state_cb_;
+ base::Closure duration_change_cb_;
+ AddTextTrackCB add_text_track_cb_;
+ base::Closure waiting_for_decryption_key_cb_;
+
+ // Holds the initialized demuxer. Used for seeking. Owned by client.
+ Demuxer* demuxer_;
+
+ // Holds the initialized renderers. Used for setting the volume,
+ // playback rate, and determining when playback has finished.
+ scoped_ptr<Renderer> renderer_;
+ scoped_ptr<TextRenderer> text_renderer_;
+
+ PipelineStatistics statistics_;
+
+ scoped_ptr<SerialRunner> pending_callbacks_;
+
+ // The CdmContext to be used to decrypt (and decode) encrypted stream in this
+ // pipeline. It is set when SetCdm() succeeds on the renderer (or when
+ // SetCdm() is called before Start()), after which it is guaranteed to outlive
+ // this pipeline. The saved value will be used to configure new renderers,
+ // when starting or resuming.
+ CdmContext* cdm_context_;
+
+ base::ThreadChecker thread_checker_;
+
+ // A weak pointer that can be safely copied on the media thread.
+ base::WeakPtr<PipelineImpl> weak_this_;
+
+ // Weak pointers must be created on the main thread, and must be dereferenced
+ // on the media thread.
+ //
+ // Declared last so that weak pointers will be invalidated before all other
+ // member variables.
+ base::WeakPtrFactory<PipelineImpl> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(PipelineImpl);
+};
+
+} // namespace media
+
+#endif // MEDIA_BASE_PIPELINE_IMPL_H_
diff --git a/chromium/media/base/pipeline_unittest.cc b/chromium/media/base/pipeline_impl_unittest.cc
index 8f43d608889..d08ecf1c173 100644
--- a/chromium/media/base/pipeline_unittest.cc
+++ b/chromium/media/base/pipeline_impl_unittest.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "media/base/pipeline.h"
+#include "media/base/pipeline_impl.h"
#include <stddef.h>
#include <utility>
@@ -72,7 +72,7 @@ ACTION_TEMPLATE(PostCallback,
// InitializationComplete(), which keeps the pipeline humming along. If
// either filters don't call InitializationComplete() immediately or filter
// initialization is moved to a separate thread this test will become flaky.
-class PipelineTest : public ::testing::Test {
+class PipelineImplTest : public ::testing::Test {
public:
// Used for setting expectations on pipeline callbacks. Using a StrictMock
// also lets us test for missing callbacks.
@@ -96,17 +96,16 @@ class PipelineTest : public ::testing::Test {
DISALLOW_COPY_AND_ASSIGN(CallbackHelper);
};
- PipelineTest()
- : pipeline_(new Pipeline(message_loop_.task_runner(),
- new MediaLog())),
+ PipelineImplTest()
+ : pipeline_(
+ new PipelineImpl(message_loop_.task_runner(), new MediaLog())),
demuxer_(new StrictMock<MockDemuxer>()),
scoped_renderer_(new StrictMock<MockRenderer>()),
renderer_(scoped_renderer_.get()) {
// SetDemuxerExpectations() adds overriding expectations for expected
// non-NULL streams.
DemuxerStream* null_pointer = NULL;
- EXPECT_CALL(*demuxer_, GetStream(_))
- .WillRepeatedly(Return(null_pointer));
+ EXPECT_CALL(*demuxer_, GetStream(_)).WillRepeatedly(Return(null_pointer));
EXPECT_CALL(*demuxer_, GetTimelineOffset())
.WillRepeatedly(Return(base::Time()));
@@ -117,7 +116,7 @@ class PipelineTest : public ::testing::Test {
EXPECT_CALL(*demuxer_, GetStartTime()).WillRepeatedly(Return(start_time_));
}
- virtual ~PipelineTest() {
+ virtual ~PipelineImplTest() {
if (!pipeline_ || !pipeline_->IsRunning())
return;
@@ -132,8 +131,8 @@ class PipelineTest : public ::testing::Test {
// Expect a stop callback if we were started.
ExpectPipelineStopAndDestroyPipeline();
- pipeline_->Stop(base::Bind(&CallbackHelper::OnStop,
- base::Unretained(&callbacks_)));
+ pipeline_->Stop(
+ base::Bind(&CallbackHelper::OnStop, base::Unretained(&callbacks_)));
message_loop_.RunUntilIdle();
}
@@ -166,9 +165,9 @@ class PipelineTest : public ::testing::Test {
SetDemuxerExpectations(streams, base::TimeDelta::FromSeconds(10));
}
- scoped_ptr<StrictMock<MockDemuxerStream> > CreateStream(
+ scoped_ptr<StrictMock<MockDemuxerStream>> CreateStream(
DemuxerStream::Type type) {
- scoped_ptr<StrictMock<MockDemuxerStream> > stream(
+ scoped_ptr<StrictMock<MockDemuxerStream>> stream(
new StrictMock<MockDemuxerStream>(type));
return stream;
}
@@ -176,18 +175,19 @@ class PipelineTest : public ::testing::Test {
// Sets up expectations to allow the video renderer to initialize.
void SetRendererExpectations() {
EXPECT_CALL(*renderer_, Initialize(_, _, _, _, _, _, _))
- .WillOnce(DoAll(SaveArg<3>(&buffering_state_cb_),
- SaveArg<4>(&ended_cb_),
- PostCallback<1>(PIPELINE_OK)));
+ .WillOnce(DoAll(SaveArg<2>(&statistics_cb_),
+ SaveArg<3>(&buffering_state_cb_),
+ SaveArg<4>(&ended_cb_), PostCallback<1>(PIPELINE_OK)));
EXPECT_CALL(*renderer_, HasAudio()).WillRepeatedly(Return(audio_stream()));
EXPECT_CALL(*renderer_, HasVideo()).WillRepeatedly(Return(video_stream()));
}
void AddTextStream() {
- EXPECT_CALL(*this, OnAddTextTrack(_,_))
- .WillOnce(Invoke(this, &PipelineTest::DoOnAddTextTrack));
- static_cast<DemuxerHost*>(pipeline_.get())->AddTextStream(text_stream(),
- TextTrackConfig(kTextSubtitles, "", "", ""));
+ EXPECT_CALL(*this, OnAddTextTrack(_, _))
+ .WillOnce(Invoke(this, &PipelineImplTest::DoOnAddTextTrack));
+ static_cast<DemuxerHost*>(pipeline_.get())
+ ->AddTextStream(text_stream(),
+ TextTrackConfig(kTextSubtitles, "", "", ""));
message_loop_.RunUntilIdle();
}
@@ -203,8 +203,8 @@ class PipelineTest : public ::testing::Test {
base::Unretained(&callbacks_)),
base::Bind(&CallbackHelper::OnDurationChange,
base::Unretained(&callbacks_)),
- base::Bind(&PipelineTest::OnAddTextTrack, base::Unretained(this)),
- base::Bind(&PipelineTest::OnWaitingForDecryptionKey,
+ base::Bind(&PipelineImplTest::OnAddTextTrack, base::Unretained(this)),
+ base::Bind(&PipelineImplTest::OnWaitingForDecryptionKey,
base::Unretained(this)));
}
@@ -218,8 +218,8 @@ class PipelineTest : public ::testing::Test {
EXPECT_CALL(*renderer_, SetPlaybackRate(0.0));
EXPECT_CALL(*renderer_, SetVolume(1.0f));
EXPECT_CALL(*renderer_, StartPlayingFrom(start_time_))
- .WillOnce(SetBufferingState(&buffering_state_cb_,
- BUFFERING_HAVE_ENOUGH));
+ .WillOnce(
+ SetBufferingState(&buffering_state_cb_, BUFFERING_HAVE_ENOUGH));
EXPECT_CALL(callbacks_, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH));
}
@@ -242,31 +242,25 @@ class PipelineTest : public ::testing::Test {
text_stream_ = std::move(text_stream);
}
- MockDemuxerStream* audio_stream() {
- return audio_stream_.get();
- }
+ MockDemuxerStream* audio_stream() { return audio_stream_.get(); }
- MockDemuxerStream* video_stream() {
- return video_stream_.get();
- }
+ MockDemuxerStream* video_stream() { return video_stream_.get(); }
- FakeTextTrackStream* text_stream() {
- return text_stream_.get();
- }
+ FakeTextTrackStream* text_stream() { return text_stream_.get(); }
void ExpectSeek(const base::TimeDelta& seek_time, bool underflowed) {
EXPECT_CALL(*demuxer_, Seek(seek_time, _))
.WillOnce(RunCallback<1>(PIPELINE_OK));
EXPECT_CALL(*renderer_, Flush(_))
- .WillOnce(DoAll(SetBufferingState(&buffering_state_cb_,
- BUFFERING_HAVE_NOTHING),
- RunClosure<0>()));
+ .WillOnce(DoAll(
+ SetBufferingState(&buffering_state_cb_, BUFFERING_HAVE_NOTHING),
+ RunClosure<0>()));
EXPECT_CALL(*renderer_, SetPlaybackRate(_));
EXPECT_CALL(*renderer_, SetVolume(_));
EXPECT_CALL(*renderer_, StartPlayingFrom(seek_time))
- .WillOnce(SetBufferingState(&buffering_state_cb_,
- BUFFERING_HAVE_ENOUGH));
+ .WillOnce(
+ SetBufferingState(&buffering_state_cb_, BUFFERING_HAVE_ENOUGH));
EXPECT_CALL(callbacks_, OnBufferingStateChange(BUFFERING_HAVE_NOTHING));
// We expect a successful seek callback followed by a buffering update.
@@ -275,9 +269,8 @@ class PipelineTest : public ::testing::Test {
}
void DoSeek(const base::TimeDelta& seek_time) {
- pipeline_->Seek(seek_time,
- base::Bind(&CallbackHelper::OnSeek,
- base::Unretained(&callbacks_)));
+ pipeline_->Seek(seek_time, base::Bind(&CallbackHelper::OnSeek,
+ base::Unretained(&callbacks_)));
message_loop_.RunUntilIdle();
}
@@ -337,11 +330,11 @@ class PipelineTest : public ::testing::Test {
// After the Pipeline is stopped, it could be destroyed any time. Always
// destroy the pipeline immediately after OnStop() to test this.
EXPECT_CALL(callbacks_, OnStop())
- .WillOnce(Invoke(this, &PipelineTest::DestroyPipeline));
+ .WillOnce(Invoke(this, &PipelineImplTest::DestroyPipeline));
}
- MOCK_METHOD2(OnAddTextTrack, void(const TextTrackConfig&,
- const AddTextTrackDoneCB&));
+ MOCK_METHOD2(OnAddTextTrack,
+ void(const TextTrackConfig&, const AddTextTrackDoneCB&));
MOCK_METHOD0(OnWaitingForDecryptionKey, void(void));
void DoOnAddTextTrack(const TextTrackConfig& config,
@@ -350,33 +343,47 @@ class PipelineTest : public ::testing::Test {
done_cb.Run(std::move(text_track));
}
+ void RunBufferedTimeRangesTest(const base::TimeDelta duration) {
+ EXPECT_EQ(0u, pipeline_->GetBufferedTimeRanges().size());
+ EXPECT_FALSE(pipeline_->DidLoadingProgress());
+ Ranges<base::TimeDelta> ranges;
+ ranges.Add(base::TimeDelta(), duration);
+ pipeline_->OnBufferedTimeRangesChanged(ranges);
+ EXPECT_TRUE(pipeline_->DidLoadingProgress());
+ EXPECT_FALSE(pipeline_->DidLoadingProgress());
+ EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size());
+ EXPECT_EQ(base::TimeDelta(), pipeline_->GetBufferedTimeRanges().start(0));
+ EXPECT_EQ(duration, pipeline_->GetBufferedTimeRanges().end(0));
+ }
+
// Fixture members.
StrictMock<CallbackHelper> callbacks_;
base::SimpleTestTickClock test_tick_clock_;
base::MessageLoop message_loop_;
- scoped_ptr<Pipeline> pipeline_;
+ scoped_ptr<PipelineImpl> pipeline_;
- scoped_ptr<StrictMock<MockDemuxer> > demuxer_;
- scoped_ptr<StrictMock<MockRenderer> > scoped_renderer_;
+ scoped_ptr<StrictMock<MockDemuxer>> demuxer_;
+ scoped_ptr<StrictMock<MockRenderer>> scoped_renderer_;
StrictMock<MockRenderer>* renderer_;
StrictMock<CallbackHelper> text_renderer_callbacks_;
TextRenderer* text_renderer_;
- scoped_ptr<StrictMock<MockDemuxerStream> > audio_stream_;
- scoped_ptr<StrictMock<MockDemuxerStream> > video_stream_;
+ scoped_ptr<StrictMock<MockDemuxerStream>> audio_stream_;
+ scoped_ptr<StrictMock<MockDemuxerStream>> video_stream_;
scoped_ptr<FakeTextTrackStream> text_stream_;
BufferingStateCB buffering_state_cb_;
base::Closure ended_cb_;
+ StatisticsCB statistics_cb_;
VideoDecoderConfig video_decoder_config_;
PipelineMetadata metadata_;
base::TimeDelta start_time_;
private:
- DISALLOW_COPY_AND_ASSIGN(PipelineTest);
+ DISALLOW_COPY_AND_ASSIGN(PipelineImplTest);
};
// Test that playback controls methods no-op when the pipeline hasn't been
// started.
-TEST_F(PipelineTest, NotStarted) {
+TEST_F(PipelineImplTest, NotStarted) {
const base::TimeDelta kZero;
EXPECT_FALSE(pipeline_->IsRunning());
@@ -400,7 +407,7 @@ TEST_F(PipelineTest, NotStarted) {
EXPECT_TRUE(kZero == pipeline_->GetMediaDuration());
}
-TEST_F(PipelineTest, NeverInitializes) {
+TEST_F(PipelineImplTest, NeverInitializes) {
// Don't execute the callback passed into Initialize().
EXPECT_CALL(*demuxer_, Initialize(_, _, _));
@@ -417,14 +424,14 @@ TEST_F(PipelineTest, NeverInitializes) {
EXPECT_CALL(callbacks_, OnStart(PIPELINE_OK));
}
-TEST_F(PipelineTest, StopWithoutStart) {
+TEST_F(PipelineImplTest, StopWithoutStart) {
ExpectPipelineStopAndDestroyPipeline();
pipeline_->Stop(
base::Bind(&CallbackHelper::OnStop, base::Unretained(&callbacks_)));
message_loop_.RunUntilIdle();
}
-TEST_F(PipelineTest, StartThenStopImmediately) {
+TEST_F(PipelineImplTest, StartThenStopImmediately) {
EXPECT_CALL(*demuxer_, Initialize(_, _, _))
.WillOnce(PostCallback<1>(PIPELINE_OK));
EXPECT_CALL(*demuxer_, Stop());
@@ -439,7 +446,7 @@ TEST_F(PipelineTest, StartThenStopImmediately) {
message_loop_.RunUntilIdle();
}
-TEST_F(PipelineTest, DemuxerErrorDuringStop) {
+TEST_F(PipelineImplTest, DemuxerErrorDuringStop) {
CreateAudioStream();
MockDemuxerStreamVector streams;
streams.push_back(audio_stream());
@@ -450,7 +457,7 @@ TEST_F(PipelineTest, DemuxerErrorDuringStop) {
StartPipelineAndExpect(PIPELINE_OK);
EXPECT_CALL(*demuxer_, Stop())
- .WillOnce(InvokeWithoutArgs(this, &PipelineTest::OnDemuxerError));
+ .WillOnce(InvokeWithoutArgs(this, &PipelineImplTest::OnDemuxerError));
ExpectPipelineStopAndDestroyPipeline();
pipeline_->Stop(
@@ -458,15 +465,7 @@ TEST_F(PipelineTest, DemuxerErrorDuringStop) {
message_loop_.RunUntilIdle();
}
-TEST_F(PipelineTest, URLNotFound) {
- EXPECT_CALL(*demuxer_, Initialize(_, _, _))
- .WillOnce(PostCallback<1>(PIPELINE_ERROR_URL_NOT_FOUND));
- EXPECT_CALL(*demuxer_, Stop());
-
- StartPipelineAndExpect(PIPELINE_ERROR_URL_NOT_FOUND);
-}
-
-TEST_F(PipelineTest, NoStreams) {
+TEST_F(PipelineImplTest, NoStreams) {
EXPECT_CALL(*demuxer_, Initialize(_, _, _))
.WillOnce(PostCallback<1>(PIPELINE_OK));
EXPECT_CALL(*demuxer_, Stop());
@@ -475,7 +474,7 @@ TEST_F(PipelineTest, NoStreams) {
StartPipelineAndExpect(PIPELINE_ERROR_COULD_NOT_RENDER);
}
-TEST_F(PipelineTest, AudioStream) {
+TEST_F(PipelineImplTest, AudioStream) {
CreateAudioStream();
MockDemuxerStreamVector streams;
streams.push_back(audio_stream());
@@ -488,7 +487,7 @@ TEST_F(PipelineTest, AudioStream) {
EXPECT_FALSE(metadata_.has_video);
}
-TEST_F(PipelineTest, VideoStream) {
+TEST_F(PipelineImplTest, VideoStream) {
CreateVideoStream();
MockDemuxerStreamVector streams;
streams.push_back(video_stream());
@@ -501,7 +500,7 @@ TEST_F(PipelineTest, VideoStream) {
EXPECT_TRUE(metadata_.has_video);
}
-TEST_F(PipelineTest, AudioVideoStream) {
+TEST_F(PipelineImplTest, AudioVideoStream) {
CreateAudioStream();
CreateVideoStream();
MockDemuxerStreamVector streams;
@@ -516,7 +515,7 @@ TEST_F(PipelineTest, AudioVideoStream) {
EXPECT_TRUE(metadata_.has_video);
}
-TEST_F(PipelineTest, VideoTextStream) {
+TEST_F(PipelineImplTest, VideoTextStream) {
CreateVideoStream();
CreateTextStream();
MockDemuxerStreamVector streams;
@@ -532,7 +531,7 @@ TEST_F(PipelineTest, VideoTextStream) {
AddTextStream();
}
-TEST_F(PipelineTest, VideoAudioTextStream) {
+TEST_F(PipelineImplTest, VideoAudioTextStream) {
CreateVideoStream();
CreateAudioStream();
CreateTextStream();
@@ -550,7 +549,7 @@ TEST_F(PipelineTest, VideoAudioTextStream) {
AddTextStream();
}
-TEST_F(PipelineTest, Seek) {
+TEST_F(PipelineImplTest, Seek) {
CreateAudioStream();
CreateVideoStream();
CreateTextStream();
@@ -570,7 +569,7 @@ TEST_F(PipelineTest, Seek) {
DoSeek(expected);
}
-TEST_F(PipelineTest, SeekAfterError) {
+TEST_F(PipelineImplTest, SeekAfterError) {
CreateAudioStream();
MockDemuxerStreamVector streams;
streams.push_back(audio_stream());
@@ -594,7 +593,7 @@ TEST_F(PipelineTest, SeekAfterError) {
message_loop_.RunUntilIdle();
}
-TEST_F(PipelineTest, SuspendResume) {
+TEST_F(PipelineImplTest, SuspendResume) {
CreateAudioStream();
CreateVideoStream();
CreateTextStream();
@@ -607,15 +606,28 @@ TEST_F(PipelineTest, SuspendResume) {
StartPipelineAndExpect(PIPELINE_OK);
+ // Inject some fake memory usage to verify its cleared after suspend.
+ PipelineStatistics stats;
+ stats.audio_memory_usage = 12345;
+ stats.video_memory_usage = 67890;
+ statistics_cb_.Run(stats);
+ EXPECT_EQ(stats.audio_memory_usage,
+ pipeline_->GetStatistics().audio_memory_usage);
+ EXPECT_EQ(stats.video_memory_usage,
+ pipeline_->GetStatistics().video_memory_usage);
+
ExpectSuspend();
DoSuspend();
+ EXPECT_EQ(pipeline_->GetStatistics().audio_memory_usage, 0);
+ EXPECT_EQ(pipeline_->GetStatistics().video_memory_usage, 0);
+
base::TimeDelta expected = base::TimeDelta::FromSeconds(2000);
ExpectResume(expected);
DoResume(expected);
}
-TEST_F(PipelineTest, SetVolume) {
+TEST_F(PipelineImplTest, SetVolume) {
CreateAudioStream();
MockDemuxerStreamVector streams;
streams.push_back(audio_stream());
@@ -632,7 +644,7 @@ TEST_F(PipelineTest, SetVolume) {
pipeline_->SetVolume(expected);
}
-TEST_F(PipelineTest, Properties) {
+TEST_F(PipelineImplTest, Properties) {
CreateVideoStream();
MockDemuxerStreamVector streams;
streams.push_back(video_stream());
@@ -647,7 +659,7 @@ TEST_F(PipelineTest, Properties) {
EXPECT_FALSE(pipeline_->DidLoadingProgress());
}
-TEST_F(PipelineTest, GetBufferedTimeRanges) {
+TEST_F(PipelineImplTest, GetBufferedTimeRanges) {
CreateVideoStream();
MockDemuxerStreamVector streams;
streams.push_back(video_stream());
@@ -657,18 +669,7 @@ TEST_F(PipelineTest, GetBufferedTimeRanges) {
SetRendererExpectations();
StartPipelineAndExpect(PIPELINE_OK);
-
- EXPECT_EQ(0u, pipeline_->GetBufferedTimeRanges().size());
-
- EXPECT_FALSE(pipeline_->DidLoadingProgress());
- Ranges<base::TimeDelta> ranges;
- ranges.Add(base::TimeDelta(), kDuration / 8);
- pipeline_->OnBufferedTimeRangesChanged(ranges);
- EXPECT_TRUE(pipeline_->DidLoadingProgress());
- EXPECT_FALSE(pipeline_->DidLoadingProgress());
- EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size());
- EXPECT_EQ(base::TimeDelta(), pipeline_->GetBufferedTimeRanges().start(0));
- EXPECT_EQ(kDuration / 8, pipeline_->GetBufferedTimeRanges().end(0));
+ RunBufferedTimeRangesTest(kDuration / 8);
base::TimeDelta kSeekTime = kDuration / 2;
ExpectSeek(kSeekTime, false);
@@ -677,7 +678,24 @@ TEST_F(PipelineTest, GetBufferedTimeRanges) {
EXPECT_FALSE(pipeline_->DidLoadingProgress());
}
-TEST_F(PipelineTest, EndedCallback) {
+TEST_F(PipelineImplTest, BufferedTimeRangesCanChangeAfterStop) {
+ EXPECT_CALL(*demuxer_, Initialize(_, _, _))
+ .WillOnce(PostCallback<1>(PIPELINE_OK));
+ EXPECT_CALL(*demuxer_, Stop());
+
+ EXPECT_CALL(callbacks_, OnStart(_));
+ StartPipeline();
+
+ EXPECT_CALL(callbacks_, OnStop());
+ pipeline_->Stop(
+ base::Bind(&CallbackHelper::OnStop, base::Unretained(&callbacks_)));
+ message_loop_.RunUntilIdle();
+
+ RunBufferedTimeRangesTest(base::TimeDelta::FromSeconds(5));
+ DestroyPipeline();
+}
+
+TEST_F(PipelineImplTest, EndedCallback) {
CreateAudioStream();
CreateVideoStream();
CreateTextStream();
@@ -700,7 +718,7 @@ TEST_F(PipelineTest, EndedCallback) {
message_loop_.RunUntilIdle();
}
-TEST_F(PipelineTest, ErrorDuringSeek) {
+TEST_F(PipelineImplTest, ErrorDuringSeek) {
CreateAudioStream();
MockDemuxerStreamVector streams;
streams.push_back(audio_stream());
@@ -718,9 +736,9 @@ TEST_F(PipelineTest, ErrorDuringSeek) {
EXPECT_CALL(callbacks_, OnBufferingStateChange(BUFFERING_HAVE_NOTHING));
EXPECT_CALL(*renderer_, Flush(_))
- .WillOnce(DoAll(SetBufferingState(&buffering_state_cb_,
- BUFFERING_HAVE_NOTHING),
- RunClosure<0>()));
+ .WillOnce(
+ DoAll(SetBufferingState(&buffering_state_cb_, BUFFERING_HAVE_NOTHING),
+ RunClosure<0>()));
EXPECT_CALL(*demuxer_, Seek(seek_time, _))
.WillOnce(RunCallback<1>(PIPELINE_ERROR_READ));
@@ -734,9 +752,9 @@ TEST_F(PipelineTest, ErrorDuringSeek) {
// Invoked function OnError. This asserts that the pipeline does not enqueue
// non-teardown related tasks while tearing down.
-static void TestNoCallsAfterError(
- Pipeline* pipeline, base::MessageLoop* message_loop,
- PipelineStatus /* status */) {
+static void TestNoCallsAfterError(PipelineImpl* pipeline,
+ base::MessageLoop* message_loop,
+ PipelineStatus /* status */) {
CHECK(pipeline);
CHECK(message_loop);
@@ -751,7 +769,7 @@ static void TestNoCallsAfterError(
EXPECT_TRUE(message_loop->IsIdleForTesting());
}
-TEST_F(PipelineTest, NoMessageDuringTearDownFromError) {
+TEST_F(PipelineImplTest, NoMessageDuringTearDownFromError) {
CreateAudioStream();
MockDemuxerStreamVector streams;
streams.push_back(audio_stream());
@@ -761,8 +779,8 @@ TEST_F(PipelineTest, NoMessageDuringTearDownFromError) {
StartPipelineAndExpect(PIPELINE_OK);
// Trigger additional requests on the pipeline during tear down from error.
- base::Callback<void(PipelineStatus)> cb = base::Bind(
- &TestNoCallsAfterError, pipeline_.get(), &message_loop_);
+ base::Callback<void(PipelineStatus)> cb =
+ base::Bind(&TestNoCallsAfterError, pipeline_.get(), &message_loop_);
ON_CALL(callbacks_, OnError(_))
.WillByDefault(Invoke(&cb, &base::Callback<void(PipelineStatus)>::Run));
@@ -770,9 +788,9 @@ TEST_F(PipelineTest, NoMessageDuringTearDownFromError) {
// Seek() isn't called as the demuxer errors out first.
EXPECT_CALL(*renderer_, Flush(_))
- .WillOnce(DoAll(SetBufferingState(&buffering_state_cb_,
- BUFFERING_HAVE_NOTHING),
- RunClosure<0>()));
+ .WillOnce(
+ DoAll(SetBufferingState(&buffering_state_cb_, BUFFERING_HAVE_NOTHING),
+ RunClosure<0>()));
EXPECT_CALL(callbacks_, OnBufferingStateChange(BUFFERING_HAVE_NOTHING));
EXPECT_CALL(*demuxer_, Seek(seek_time, _))
@@ -785,7 +803,7 @@ TEST_F(PipelineTest, NoMessageDuringTearDownFromError) {
message_loop_.RunUntilIdle();
}
-TEST_F(PipelineTest, DestroyAfterStop) {
+TEST_F(PipelineImplTest, DestroyAfterStop) {
CreateAudioStream();
MockDemuxerStreamVector streams;
streams.push_back(audio_stream());
@@ -801,7 +819,7 @@ TEST_F(PipelineTest, DestroyAfterStop) {
message_loop_.RunUntilIdle();
}
-TEST_F(PipelineTest, Underflow) {
+TEST_F(PipelineImplTest, Underflow) {
CreateAudioStream();
CreateVideoStream();
MockDemuxerStreamVector streams;
@@ -822,7 +840,7 @@ TEST_F(PipelineTest, Underflow) {
DoSeek(expected);
}
-TEST_F(PipelineTest, PositiveStartTime) {
+TEST_F(PipelineImplTest, PositiveStartTime) {
start_time_ = base::TimeDelta::FromSeconds(1);
EXPECT_CALL(*demuxer_, GetStartTime()).WillRepeatedly(Return(start_time_));
CreateAudioStream();
@@ -838,7 +856,7 @@ TEST_F(PipelineTest, PositiveStartTime) {
message_loop_.RunUntilIdle();
}
-class PipelineTeardownTest : public PipelineTest {
+class PipelineTeardownTest : public PipelineImplTest {
public:
enum TeardownState {
kInitDemuxer,
@@ -875,7 +893,7 @@ class PipelineTeardownTest : public PipelineTest {
case kPlaying:
DoInitialize(state, stop_or_error);
- DoStopOrError(stop_or_error);
+ DoStopOrError(stop_or_error, true);
break;
case kSuspending:
@@ -903,8 +921,8 @@ class PipelineTeardownTest : public PipelineTest {
PipelineStatus SetInitializeExpectations(TeardownState state,
StopOrError stop_or_error) {
PipelineStatus status = PIPELINE_OK;
- base::Closure stop_cb = base::Bind(
- &CallbackHelper::OnStop, base::Unretained(&callbacks_));
+ base::Closure stop_cb =
+ base::Bind(&CallbackHelper::OnStop, base::Unretained(&callbacks_));
if (state == kInitDemuxer) {
if (stop_or_error == kStop) {
@@ -959,8 +977,8 @@ class PipelineTeardownTest : public PipelineTest {
EXPECT_CALL(*renderer_, SetPlaybackRate(0.0));
EXPECT_CALL(*renderer_, SetVolume(1.0f));
EXPECT_CALL(*renderer_, StartPlayingFrom(base::TimeDelta()))
- .WillOnce(SetBufferingState(&buffering_state_cb_,
- BUFFERING_HAVE_ENOUGH));
+ .WillOnce(
+ SetBufferingState(&buffering_state_cb_, BUFFERING_HAVE_ENOUGH));
if (status == PIPELINE_OK)
EXPECT_CALL(callbacks_, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH));
@@ -979,32 +997,33 @@ class PipelineTeardownTest : public PipelineTest {
ExpectPipelineStopAndDestroyPipeline();
}
- pipeline_->Seek(base::TimeDelta::FromSeconds(10), base::Bind(
- &CallbackHelper::OnSeek, base::Unretained(&callbacks_)));
+ pipeline_->Seek(
+ base::TimeDelta::FromSeconds(10),
+ base::Bind(&CallbackHelper::OnSeek, base::Unretained(&callbacks_)));
message_loop_.RunUntilIdle();
}
PipelineStatus SetSeekExpectations(TeardownState state,
StopOrError stop_or_error) {
PipelineStatus status = PIPELINE_OK;
- base::Closure stop_cb = base::Bind(
- &CallbackHelper::OnStop, base::Unretained(&callbacks_));
+ base::Closure stop_cb =
+ base::Bind(&CallbackHelper::OnStop, base::Unretained(&callbacks_));
if (state == kFlushing) {
if (stop_or_error == kStop) {
EXPECT_CALL(*renderer_, Flush(_))
- .WillOnce(DoAll(Stop(pipeline_.get(), stop_cb),
- SetBufferingState(&buffering_state_cb_,
- BUFFERING_HAVE_NOTHING),
- RunClosure<0>()));
+ .WillOnce(DoAll(
+ Stop(pipeline_.get(), stop_cb),
+ SetBufferingState(&buffering_state_cb_, BUFFERING_HAVE_NOTHING),
+ RunClosure<0>()));
EXPECT_CALL(callbacks_, OnBufferingStateChange(BUFFERING_HAVE_NOTHING));
} else {
status = PIPELINE_ERROR_READ;
EXPECT_CALL(*renderer_, Flush(_))
- .WillOnce(DoAll(SetError(pipeline_.get(), status),
- SetBufferingState(&buffering_state_cb_,
- BUFFERING_HAVE_NOTHING),
- RunClosure<0>()));
+ .WillOnce(DoAll(
+ SetError(pipeline_.get(), status),
+ SetBufferingState(&buffering_state_cb_, BUFFERING_HAVE_NOTHING),
+ RunClosure<0>()));
EXPECT_CALL(callbacks_, OnBufferingStateChange(BUFFERING_HAVE_NOTHING));
}
@@ -1012,9 +1031,9 @@ class PipelineTeardownTest : public PipelineTest {
}
EXPECT_CALL(*renderer_, Flush(_))
- .WillOnce(DoAll(SetBufferingState(&buffering_state_cb_,
- BUFFERING_HAVE_NOTHING),
- RunClosure<0>()));
+ .WillOnce(DoAll(
+ SetBufferingState(&buffering_state_cb_, BUFFERING_HAVE_NOTHING),
+ RunClosure<0>()));
EXPECT_CALL(callbacks_, OnBufferingStateChange(BUFFERING_HAVE_NOTHING));
if (state == kSeeking) {
@@ -1024,8 +1043,7 @@ class PipelineTeardownTest : public PipelineTest {
RunCallback<1>(PIPELINE_OK)));
} else {
status = PIPELINE_ERROR_READ;
- EXPECT_CALL(*demuxer_, Seek(_, _))
- .WillOnce(RunCallback<1>(status));
+ EXPECT_CALL(*demuxer_, Seek(_, _)).WillOnce(RunCallback<1>(status));
}
return status;
@@ -1038,21 +1056,22 @@ class PipelineTeardownTest : public PipelineTest {
void DoSuspend(TeardownState state, StopOrError stop_or_error) {
PipelineStatus status = SetSuspendExpectations(state, stop_or_error);
- if (state != kSuspended) {
- // DoStopOrError() handles these for kSuspended.
+ if (state == kResuming) {
EXPECT_CALL(*demuxer_, Stop());
- if (status == PIPELINE_OK) {
+ if (status == PIPELINE_OK)
ExpectPipelineStopAndDestroyPipeline();
- }
}
- PipelineTest::DoSuspend();
+ PipelineImplTest::DoSuspend();
- if (state == kSuspended) {
- DoStopOrError(stop_or_error);
- } else if (state == kResuming) {
- PipelineTest::DoResume(base::TimeDelta());
+ if (state == kResuming) {
+ PipelineImplTest::DoResume(base::TimeDelta());
+ return;
}
+
+ // kSuspended, kSuspending never throw errors, since Resume() is always able
+ // to restore the pipeline to a pristine state.
+ DoStopOrError(stop_or_error, false);
}
PipelineStatus SetSuspendExpectations(TeardownState state,
@@ -1062,72 +1081,58 @@ class PipelineTeardownTest : public PipelineTest {
base::Bind(&CallbackHelper::OnStop, base::Unretained(&callbacks_));
EXPECT_CALL(*renderer_, SetPlaybackRate(0));
- if (state == kSuspended || state == kResuming) {
- EXPECT_CALL(*renderer_, Flush(_))
- .WillOnce(DoAll(
- SetBufferingState(&buffering_state_cb_, BUFFERING_HAVE_NOTHING),
- RunClosure<0>()));
- EXPECT_CALL(callbacks_, OnBufferingStateChange(BUFFERING_HAVE_NOTHING));
- EXPECT_CALL(callbacks_, OnSuspend(PIPELINE_OK));
- if (state == kResuming) {
- if (stop_or_error == kStop) {
- EXPECT_CALL(*demuxer_, Seek(_, _))
- .WillOnce(DoAll(Stop(pipeline_.get(), stop_cb),
- RunCallback<1>(PIPELINE_OK)));
- EXPECT_CALL(callbacks_, OnResume(PIPELINE_OK));
- } else {
- status = PIPELINE_ERROR_READ;
- EXPECT_CALL(*demuxer_, Seek(_, _)).WillOnce(RunCallback<1>(status));
- EXPECT_CALL(callbacks_, OnResume(status));
- }
- }
- return status;
- } else if (state == kSuspending) {
+ EXPECT_CALL(callbacks_, OnBufferingStateChange(BUFFERING_HAVE_NOTHING));
+ EXPECT_CALL(callbacks_, OnSuspend(PIPELINE_OK));
+ EXPECT_CALL(*renderer_, Flush(_))
+ .WillOnce(DoAll(
+ SetBufferingState(&buffering_state_cb_, BUFFERING_HAVE_NOTHING),
+ RunClosure<0>()));
+ if (state == kResuming) {
if (stop_or_error == kStop) {
- EXPECT_CALL(*renderer_, Flush(_))
- .WillOnce(DoAll(
- Stop(pipeline_.get(), stop_cb),
- SetBufferingState(&buffering_state_cb_, BUFFERING_HAVE_NOTHING),
- RunClosure<0>()));
- EXPECT_CALL(callbacks_, OnBufferingStateChange(BUFFERING_HAVE_NOTHING));
- EXPECT_CALL(callbacks_, OnSuspend(PIPELINE_OK));
+ EXPECT_CALL(*demuxer_, Seek(_, _))
+ .WillOnce(DoAll(Stop(pipeline_.get(), stop_cb),
+ RunCallback<1>(PIPELINE_OK)));
+ EXPECT_CALL(callbacks_, OnResume(PIPELINE_OK));
} else {
status = PIPELINE_ERROR_READ;
- EXPECT_CALL(*renderer_, Flush(_))
- .WillOnce(SetError(pipeline_.get(), status));
- EXPECT_CALL(callbacks_, OnSuspend(status));
+ EXPECT_CALL(*demuxer_, Seek(_, _)).WillOnce(RunCallback<1>(status));
+ EXPECT_CALL(callbacks_, OnResume(status));
}
- return status;
+ } else if (state != kSuspended && state != kSuspending) {
+ NOTREACHED() << "State not supported: " << state;
}
- NOTREACHED() << "State not supported: " << state;
return status;
}
- void DoStopOrError(StopOrError stop_or_error) {
+ void DoStopOrError(StopOrError stop_or_error, bool expect_errors) {
InSequence s;
- EXPECT_CALL(*demuxer_, Stop());
-
switch (stop_or_error) {
case kStop:
+ EXPECT_CALL(*demuxer_, Stop());
ExpectPipelineStopAndDestroyPipeline();
- pipeline_->Stop(base::Bind(
- &CallbackHelper::OnStop, base::Unretained(&callbacks_)));
+ pipeline_->Stop(
+ base::Bind(&CallbackHelper::OnStop, base::Unretained(&callbacks_)));
break;
case kError:
- EXPECT_CALL(callbacks_, OnError(PIPELINE_ERROR_READ));
+ if (expect_errors) {
+ EXPECT_CALL(*demuxer_, Stop());
+ EXPECT_CALL(callbacks_, OnError(PIPELINE_ERROR_READ));
+ }
pipeline_->SetErrorForTesting(PIPELINE_ERROR_READ);
break;
case kErrorAndStop:
- EXPECT_CALL(callbacks_, OnError(PIPELINE_ERROR_READ));
+ EXPECT_CALL(*demuxer_, Stop());
+ if (expect_errors)
+ EXPECT_CALL(callbacks_, OnError(PIPELINE_ERROR_READ));
ExpectPipelineStopAndDestroyPipeline();
pipeline_->SetErrorForTesting(PIPELINE_ERROR_READ);
message_loop_.RunUntilIdle();
- pipeline_->Stop(base::Bind(
- &CallbackHelper::OnStop, base::Unretained(&callbacks_)));
+ pipeline_->Stop(
+ base::Bind(&CallbackHelper::OnStop, base::Unretained(&callbacks_)));
break;
}
@@ -1137,10 +1142,10 @@ class PipelineTeardownTest : public PipelineTest {
DISALLOW_COPY_AND_ASSIGN(PipelineTeardownTest);
};
-#define INSTANTIATE_TEARDOWN_TEST(stop_or_error, state) \
- TEST_F(PipelineTeardownTest, stop_or_error##_##state) { \
- RunTest(k##state, k##stop_or_error); \
- }
+#define INSTANTIATE_TEARDOWN_TEST(stop_or_error, state) \
+ TEST_F(PipelineTeardownTest, stop_or_error##_##state) { \
+ RunTest(k##state, k##stop_or_error); \
+ }
INSTANTIATE_TEARDOWN_TEST(Stop, InitDemuxer);
INSTANTIATE_TEARDOWN_TEST(Stop, InitRenderer);
diff --git a/chromium/media/base/pipeline_status.h b/chromium/media/base/pipeline_status.h
index 32e163c913b..2f4b35945f6 100644
--- a/chromium/media/base/pipeline_status.h
+++ b/chromium/media/base/pipeline_status.h
@@ -15,10 +15,9 @@ namespace media {
// Status states for pipeline. All codes except PIPELINE_OK indicate errors.
// Logged to UMA, so never reuse a value, always add new/greater ones!
-// TODO(vrk/scherkus): Trim the unused status codes. (crbug.com/126070)
enum PipelineStatus {
PIPELINE_OK = 0,
- PIPELINE_ERROR_URL_NOT_FOUND = 1,
+ // Deprecated: PIPELINE_ERROR_URL_NOT_FOUND = 1,
PIPELINE_ERROR_NETWORK = 2,
PIPELINE_ERROR_DECODE = 3,
// Deprecated: PIPELINE_ERROR_DECRYPT = 4,
@@ -26,23 +25,35 @@ enum PipelineStatus {
PIPELINE_ERROR_INITIALIZATION_FAILED = 6,
PIPELINE_ERROR_COULD_NOT_RENDER = 8,
PIPELINE_ERROR_READ = 9,
- PIPELINE_ERROR_OPERATION_PENDING = 10,
+ // Deprecated: PIPELINE_ERROR_OPERATION_PENDING = 10,
PIPELINE_ERROR_INVALID_STATE = 11,
+
// Demuxer related errors.
DEMUXER_ERROR_COULD_NOT_OPEN = 12,
DEMUXER_ERROR_COULD_NOT_PARSE = 13,
DEMUXER_ERROR_NO_SUPPORTED_STREAMS = 14,
+
// Decoder related errors.
DECODER_ERROR_NOT_SUPPORTED = 15,
+
+ // ChunkDemuxer related errors.
+ CHUNK_DEMUXER_ERROR_APPEND_FAILED = 16,
+ CHUNK_DEMUXER_ERROR_EOS_STATUS_DECODE_ERROR = 17,
+ CHUNK_DEMUXER_ERROR_EOS_STATUS_NETWORK_ERROR = 18,
+
+ // Audio rendering errors.
+ AUDIO_RENDERER_ERROR = 19,
+ AUDIO_RENDERER_ERROR_SPLICE_FAILED = 20,
+
// Must be equal to the largest value ever logged.
- PIPELINE_STATUS_MAX = DECODER_ERROR_NOT_SUPPORTED,
+ PIPELINE_STATUS_MAX = AUDIO_RENDERER_ERROR_SPLICE_FAILED,
};
typedef base::Callback<void(PipelineStatus)> PipelineStatusCB;
struct PipelineStatistics {
- uint64_t audio_bytes_decoded = 0; // Should be uint64_t?
- uint32_t video_bytes_decoded = 0; // Should be uint64_t?
+ uint64_t audio_bytes_decoded = 0;
+ uint64_t video_bytes_decoded = 0;
uint32_t video_frames_decoded = 0;
uint32_t video_frames_dropped = 0;
int64_t audio_memory_usage = 0;
diff --git a/chromium/media/base/renderer_factory.h b/chromium/media/base/renderer_factory.h
index 4b35f01764b..65c5cbee693 100644
--- a/chromium/media/base/renderer_factory.h
+++ b/chromium/media/base/renderer_factory.h
@@ -10,6 +10,7 @@
#include "base/memory/scoped_ptr.h"
#include "media/base/media_export.h"
#include "media/base/renderer.h"
+#include "media/base/surface_manager.h"
namespace base {
class SingleThreadTaskRunner;
@@ -36,7 +37,8 @@ class MEDIA_EXPORT RendererFactory {
const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner,
const scoped_refptr<base::TaskRunner>& worker_task_runner,
AudioRendererSink* audio_renderer_sink,
- VideoRendererSink* video_renderer_sink) = 0;
+ VideoRendererSink* video_renderer_sink,
+ const RequestSurfaceCB& request_surface_cb) = 0;
private:
DISALLOW_COPY_AND_ASSIGN(RendererFactory);
diff --git a/chromium/media/base/run_all_unittests.cc b/chromium/media/base/run_all_unittests.cc
index 97e799b0317..c84e24d7c06 100644
--- a/chromium/media/base/run_all_unittests.cc
+++ b/chromium/media/base/run_all_unittests.cc
@@ -14,7 +14,9 @@
#if defined(OS_ANDROID)
#include "base/android/jni_android.h"
+#include "media/base/android/media_codec_util.h"
#include "media/base/android/media_jni_registrar.h"
+#include "media/capture/video/android/capture_jni_registrar.h"
#include "ui/gl/android/gl_jni_registrar.h"
#endif
@@ -43,6 +45,10 @@ void TestSuiteNoAtExit::Initialize() {
// Needed for surface texture support.
ui::gl::android::RegisterJni(env);
media::RegisterJni(env);
+ media::RegisterCaptureJni(env);
+
+ if (media::MediaCodecUtil::IsMediaCodecAvailable())
+ media::EnablePlatformDecoderSupport();
#endif
// Run this here instead of main() to ensure an AtExitManager is already
diff --git a/chromium/media/base/serial_runner.cc b/chromium/media/base/serial_runner.cc
index 2e085e35820..6cb89cbeaba 100644
--- a/chromium/media/base/serial_runner.cc
+++ b/chromium/media/base/serial_runner.cc
@@ -40,6 +40,7 @@ static void RunOnTaskRunner(
}
SerialRunner::Queue::Queue() {}
+SerialRunner::Queue::Queue(const Queue& other) = default;
SerialRunner::Queue::~Queue() {}
void SerialRunner::Queue::Push(const base::Closure& closure) {
diff --git a/chromium/media/base/serial_runner.h b/chromium/media/base/serial_runner.h
index 4847e4c4c30..3adfd64f730 100644
--- a/chromium/media/base/serial_runner.h
+++ b/chromium/media/base/serial_runner.h
@@ -34,6 +34,7 @@ class MEDIA_EXPORT SerialRunner {
class MEDIA_EXPORT Queue {
public:
Queue();
+ Queue(const Queue& other);
~Queue();
void Push(const base::Closure& closure);
diff --git a/chromium/media/base/stream_parser.cc b/chromium/media/base/stream_parser.cc
index f63644a7755..9f3119b33b0 100644
--- a/chromium/media/base/stream_parser.cc
+++ b/chromium/media/base/stream_parser.cc
@@ -11,8 +11,10 @@ namespace media {
StreamParser::InitParameters::InitParameters(base::TimeDelta duration)
: duration(duration),
auto_update_timestamp_offset(false),
- liveness(DemuxerStream::LIVENESS_UNKNOWN) {
-}
+ liveness(DemuxerStream::LIVENESS_UNKNOWN),
+ detected_audio_track_count(0),
+ detected_video_track_count(0),
+ detected_text_track_count(0) {}
StreamParser::StreamParser() {}
diff --git a/chromium/media/base/stream_parser.h b/chromium/media/base/stream_parser.h
index 27ebc3f0a59..fed228c50c2 100644
--- a/chromium/media/base/stream_parser.h
+++ b/chromium/media/base/stream_parser.h
@@ -25,10 +25,9 @@
namespace media {
-class AudioDecoderConfig;
+class MediaTracks;
class StreamParserBuffer;
class TextTrackConfig;
-class VideoDecoderConfig;
// Abstract interface for parsing media byte streams.
class MEDIA_EXPORT StreamParser {
@@ -53,7 +52,7 @@ class MEDIA_EXPORT StreamParser {
typedef std::map<TrackId, const BufferQueue> TextBufferQueueMap;
// Stream parameters passed in InitCB.
- struct InitParameters {
+ struct MEDIA_EXPORT InitParameters {
InitParameters(base::TimeDelta duration);
// Stream duration.
@@ -69,6 +68,12 @@ class MEDIA_EXPORT StreamParser {
// Indicates live stream.
DemuxerStream::Liveness liveness;
+
+ // Counts of tracks detected by type within this stream. Not all of these
+ // tracks may be selected for use by the parser.
+ int detected_audio_track_count;
+ int detected_video_track_count;
+ int detected_text_track_count;
};
// Indicates completion of parser initialization.
@@ -76,18 +81,17 @@ class MEDIA_EXPORT StreamParser {
typedef base::Callback<void(const InitParameters& params)> InitCB;
// Indicates when new stream configurations have been parsed.
- // First parameter - The new audio configuration. If the config is not valid
- // then it means that there isn't an audio stream.
- // Second parameter - The new video configuration. If the config is not valid
- // then it means that there isn't an audio stream.
- // Third parameter - The new text tracks configuration. If the map is empty,
- // then no text tracks were parsed from the stream.
+ // First parameter - An object containing information about media tracks as
+ // well as audio/video decoder configs associated with each
+ // track the parser will use from the stream.
+ // Second parameter - The new text tracks configuration. If the map is empty,
+ // then no text tracks were parsed for use from the stream.
// Return value - True if the new configurations are accepted.
// False if the new configurations are not supported
// and indicates that a parsing error should be signalled.
- typedef base::Callback<bool(const AudioDecoderConfig&,
- const VideoDecoderConfig&,
- const TextTrackConfigMap&)> NewConfigCB;
+ typedef base::Callback<bool(scoped_ptr<MediaTracks>,
+ const TextTrackConfigMap&)>
+ NewConfigCB;
// New stream buffers have been parsed.
// First parameter - A queue of newly parsed audio buffers.
@@ -106,6 +110,9 @@ class MEDIA_EXPORT StreamParser {
// Signals the beginning of a new media segment.
typedef base::Callback<void()> NewMediaSegmentCB;
+ // Signals the end of a media segment.
+ typedef base::Callback<void()> EndMediaSegmentCB;
+
// A new potentially encrypted stream has been parsed.
// First parameter - The type of the initialization data associated with the
// stream.
@@ -128,12 +135,13 @@ class MEDIA_EXPORT StreamParser {
bool ignore_text_track,
const EncryptedMediaInitDataCB& encrypted_media_init_data_cb,
const NewMediaSegmentCB& new_segment_cb,
- const base::Closure& end_of_segment_cb,
+ const EndMediaSegmentCB& end_of_segment_cb,
const scoped_refptr<MediaLog>& media_log) = 0;
- // Called when a seek occurs. This flushes the current parser state
- // and puts the parser in a state where it can receive data for the new seek
- // point.
+ // Called during the reset parser state algorithm. This flushes the current
+ // parser and puts the parser in a state where it can receive data. This
+ // method does not need to invoke the EndMediaSegmentCB since the parser reset
+ // algorithm already resets the segment parsing state.
virtual void Flush() = 0;
// Called when there is new data to parse.
diff --git a/chromium/media/base/surface_manager.h b/chromium/media/base/surface_manager.h
new file mode 100644
index 00000000000..84df7c136d6
--- /dev/null
+++ b/chromium/media/base/surface_manager.h
@@ -0,0 +1,45 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_BASE_SURFACE_MANAGER_H_
+#define MEDIA_BASE_SURFACE_MANAGER_H_
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "media/base/media_export.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace media {
+
+using SurfaceCreatedCB = base::Callback<void(int)>;
+using RequestSurfaceCB = base::Callback<void(const SurfaceCreatedCB&)>;
+
+class MEDIA_EXPORT SurfaceManager {
+ public:
+ enum { kNoSurfaceID = -1 };
+
+ SurfaceManager() {}
+ virtual ~SurfaceManager() {}
+
+ // Create a fullscreen surface. The id will be returned with
+ // |surface_created_cb|. If this is called more than once before the first
+ // |surface_created_cb| is called, the surface will be delivered to the last
+ // caller. If this is called after the fullscreen surface is created, the
+ // existing surface will be returned. The client should ensure that the
+ // previous consumer is no longer using the surface.
+ virtual void CreateFullscreenSurface(
+ const gfx::Size& video_natural_size,
+ const SurfaceCreatedCB& surface_created_cb) = 0;
+
+ // Call this when the natural size of the fullscreen video changes. The
+ // surface will be resized to match the aspect ratio.
+ virtual void NaturalSizeChanged(const gfx::Size& size) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SurfaceManager);
+};
+
+} // namespace media
+
+#endif // MEDIA_BASE_SURFACE_MANAGER_H_
diff --git a/chromium/media/base/test_helpers.cc b/chromium/media/base/test_helpers.cc
index 4b2128296b1..0ab19032f52 100644
--- a/chromium/media/base/test_helpers.cc
+++ b/chromium/media/base/test_helpers.cc
@@ -129,43 +129,57 @@ static VideoDecoderConfig GetTestConfig(VideoCodec codec,
gfx::Rect visible_rect(coded_size.width(), coded_size.height());
gfx::Size natural_size = coded_size;
- return VideoDecoderConfig(codec, VIDEO_CODEC_PROFILE_UNKNOWN,
- PIXEL_FORMAT_YV12, COLOR_SPACE_UNSPECIFIED,
- coded_size, visible_rect, natural_size,
- EmptyExtraData(), is_encrypted);
+ return VideoDecoderConfig(
+ codec, VIDEO_CODEC_PROFILE_UNKNOWN, PIXEL_FORMAT_YV12,
+ COLOR_SPACE_UNSPECIFIED, coded_size, visible_rect, natural_size,
+ EmptyExtraData(),
+ is_encrypted ? AesCtrEncryptionScheme() : Unencrypted());
}
static const gfx::Size kNormalSize(320, 240);
static const gfx::Size kLargeSize(640, 480);
+// static
VideoDecoderConfig TestVideoConfig::Invalid() {
return GetTestConfig(kUnknownVideoCodec, kNormalSize, false);
}
+// static
VideoDecoderConfig TestVideoConfig::Normal() {
return GetTestConfig(kCodecVP8, kNormalSize, false);
}
+// static
VideoDecoderConfig TestVideoConfig::NormalEncrypted() {
return GetTestConfig(kCodecVP8, kNormalSize, true);
}
+// static
VideoDecoderConfig TestVideoConfig::Large() {
return GetTestConfig(kCodecVP8, kLargeSize, false);
}
+// static
VideoDecoderConfig TestVideoConfig::LargeEncrypted() {
return GetTestConfig(kCodecVP8, kLargeSize, true);
}
+// static
gfx::Size TestVideoConfig::NormalCodedSize() {
return kNormalSize;
}
+// static
gfx::Size TestVideoConfig::LargeCodedSize() {
return kLargeSize;
}
+// static
+AudioParameters TestAudioParameters::Normal() {
+ return AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY,
+ CHANNEL_LAYOUT_STEREO, 48000, 16, 2048);
+}
+
template <class T>
scoped_refptr<AudioBuffer> MakeAudioBuffer(SampleFormat format,
ChannelLayout channel_layout,
@@ -262,21 +276,4 @@ bool VerifyFakeVideoBufferForTest(
height == config.coded_size().height());
}
-CallbackPairChecker::CallbackPairChecker() : expecting_b_(false) {
-}
-
-CallbackPairChecker::~CallbackPairChecker() {
- EXPECT_FALSE(expecting_b_);
-}
-
-void CallbackPairChecker::RecordACalled() {
- EXPECT_FALSE(expecting_b_);
- expecting_b_ = true;
-}
-
-void CallbackPairChecker::RecordBCalled() {
- EXPECT_TRUE(expecting_b_);
- expecting_b_ = false;
-}
-
} // namespace media
diff --git a/chromium/media/base/test_helpers.h b/chromium/media/base/test_helpers.h
index 214526439f3..d14d83c1983 100644
--- a/chromium/media/base/test_helpers.h
+++ b/chromium/media/base/test_helpers.h
@@ -9,6 +9,7 @@
#include "base/callback.h"
#include "base/macros.h"
+#include "media/audio/audio_parameters.h"
#include "media/base/channel_layout.h"
#include "media/base/media_log.h"
#include "media/base/pipeline_status.h"
@@ -90,7 +91,16 @@ class TestVideoConfig {
static gfx::Size LargeCodedSize();
private:
- DISALLOW_IMPLICIT_CONSTRUCTORS(TestVideoConfig);
+ DISALLOW_COPY_AND_ASSIGN(TestVideoConfig);
+};
+
+// Provides pre-canned AudioParameters objects.
+class TestAudioParameters {
+ public:
+ static AudioParameters Normal();
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TestAudioParameters);
};
// Create an AudioBuffer containing |frames| frames of data, where each sample
@@ -133,19 +143,6 @@ scoped_refptr<DecoderBuffer> CreateFakeVideoBufferForTest(
bool VerifyFakeVideoBufferForTest(const scoped_refptr<DecoderBuffer>& buffer,
const VideoDecoderConfig& config);
-// Used to verify that the each call to A() is followed by a call to B(),
-// before the next call to A(). There may be any number of pairs (including 0).
-class CallbackPairChecker {
- public:
- CallbackPairChecker();
- ~CallbackPairChecker();
- void RecordACalled();
- void RecordBCalled();
-
- private:
- bool expecting_b_;
-};
-
} // namespace media
#endif // MEDIA_BASE_TEST_HELPERS_H_
diff --git a/chromium/media/base/test_random.h b/chromium/media/base/test_random.h
new file mode 100644
index 00000000000..ba081cb57a0
--- /dev/null
+++ b/chromium/media/base/test_random.h
@@ -0,0 +1,45 @@
+// Copyright 2015 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_BLINK_TEST_RANDOM_H_
+#define MEDIA_BLINK_TEST_RANDOM_H_
+
+#include <stdint.h>
+
+#include "base/logging.h"
+
+// Vastly simplified ACM random class meant to only be used for testing.
+// This class is meant to generate predictable sequences of pseudorandom
+// numbers, unlike the classes in base/rand_util.h which are meant to generate
+// unpredictable sequences.
+// See
+// https://code.google.com/p/szl/source/browse/trunk/src/utilities/acmrandom.h
+// for more information.
+
+namespace media {
+
+class TestRandom {
+ public:
+ explicit TestRandom(uint32_t seed) {
+ seed_ = seed & 0x7fffffff; // make this a non-negative number
+ if (seed_ == 0 || seed_ == M) {
+ seed_ = 1;
+ }
+ }
+
+ int32_t Rand() {
+ static const uint64_t A = 16807; // bits 14, 8, 7, 5, 2, 1, 0
+ seed_ = static_cast<int32_t>((seed_ * A) % M);
+ CHECK_GT(seed_, 0);
+ return seed_;
+ }
+
+ private:
+ static const uint64_t M = 2147483647L; // 2^32-1
+ int32_t seed_;
+};
+
+} // namespace media
+
+#endif // MEDIA_BLINK_TEST_RANDOM_H_
diff --git a/chromium/media/base/text_track_config.cc b/chromium/media/base/text_track_config.cc
index 0d4b11f6ddb..dbd2b2d5c13 100644
--- a/chromium/media/base/text_track_config.cc
+++ b/chromium/media/base/text_track_config.cc
@@ -20,6 +20,8 @@ TextTrackConfig::TextTrackConfig(TextKind kind,
id_(id) {
}
+TextTrackConfig::TextTrackConfig(const TextTrackConfig& other) = default;
+
bool TextTrackConfig::Matches(const TextTrackConfig& config) const {
return config.kind() == kind_ &&
config.label() == label_ &&
diff --git a/chromium/media/base/text_track_config.h b/chromium/media/base/text_track_config.h
index 58efba4b035..33128df99a0 100644
--- a/chromium/media/base/text_track_config.h
+++ b/chromium/media/base/text_track_config.h
@@ -23,6 +23,7 @@ enum TextKind {
class MEDIA_EXPORT TextTrackConfig {
public:
TextTrackConfig();
+ TextTrackConfig(const TextTrackConfig& other);
TextTrackConfig(TextKind kind,
const std::string& label,
const std::string& language,
diff --git a/chromium/media/base/video_capturer_source.h b/chromium/media/base/video_capturer_source.h
index a99faf8e394..ddec90c6599 100644
--- a/chromium/media/base/video_capturer_source.h
+++ b/chromium/media/base/video_capturer_source.h
@@ -76,6 +76,16 @@ class MEDIA_EXPORT VideoCapturerSource {
const VideoCaptureDeliverFrameCB& new_frame_callback,
const RunningCallback& running_callback) = 0;
+ // Asks source to send a refresh frame. In cases where source does not provide
+ // a continuous rate of new frames (e.g. canvas capture, screen capture where
+ // the screen's content has not changed in a while), consumers may request a
+ // "refresh frame" to be delivered. For instance, this would be needed when
+ // a new sink is added to a MediaStreamTrack.
+ // The default implementation is a no-op and implementations are not required
+ // to honor this request. If they decide to and capturing is started
+ // successfully, then |new_frame_callback| should be called with a frame.
+ virtual void RequestRefreshFrame() {}
+
// Stops capturing frames and clears all callbacks including the
// SupportedFormatsCallback callback. Note that queued frame callbacks
// may still occur after this call, so the caller must take care to
diff --git a/chromium/media/base/video_codecs.cc b/chromium/media/base/video_codecs.cc
index 7b3f77f04ec..a2221a35d3d 100644
--- a/chromium/media/base/video_codecs.cc
+++ b/chromium/media/base/video_codecs.cc
@@ -5,6 +5,8 @@
#include "media/base/video_codecs.h"
#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
namespace media {
@@ -62,11 +64,109 @@ std::string GetProfileName(VideoCodecProfile profile) {
return "h264 multiview high";
case VP8PROFILE_ANY:
return "vp8";
- case VP9PROFILE_ANY:
- return "vp9";
+ case VP9PROFILE_PROFILE0:
+ return "vp9 profile0";
+ case VP9PROFILE_PROFILE1:
+ return "vp9 profile1";
+ case VP9PROFILE_PROFILE2:
+ return "vp9 profile2";
+ case VP9PROFILE_PROFILE3:
+ return "vp9 profile3";
}
NOTREACHED();
return "";
}
+bool ParseAVCCodecId(const std::string& codec_id,
+ VideoCodecProfile* profile,
+ uint8_t* level_idc) {
+ // Make sure we have avc1.xxxxxx or avc3.xxxxxx , where xxxxxx are hex digits
+ if (!base::StartsWith(codec_id, "avc1.", base::CompareCase::SENSITIVE) &&
+ !base::StartsWith(codec_id, "avc3.", base::CompareCase::SENSITIVE)) {
+ return false;
+ }
+ uint32_t elem = 0;
+ if (codec_id.size() != 11 ||
+ !base::HexStringToUInt(base::StringPiece(codec_id).substr(5), &elem)) {
+ DVLOG(4) << __FUNCTION__ << ": invalid avc codec id (" << codec_id << ")";
+ return false;
+ }
+
+ uint8_t level_byte = elem & 0xFF;
+ uint8_t constraints_byte = (elem >> 8) & 0xFF;
+ uint8_t profile_idc = (elem >> 16) & 0xFF;
+
+ // Check that the lower two bits of |constraints_byte| are zero (those are
+ // reserved and must be zero according to ISO IEC 14496-10).
+ if (constraints_byte & 3) {
+ DVLOG(4) << __FUNCTION__ << ": non-zero reserved bits in codec id "
+ << codec_id;
+ return false;
+ }
+
+ VideoCodecProfile out_profile = VIDEO_CODEC_PROFILE_UNKNOWN;
+ // profile_idc values for each profile are taken from ISO IEC 14496-10 and
+ // https://en.wikipedia.org/wiki/H.264/MPEG-4_AVC#Profiles
+ switch (profile_idc) {
+ case 66:
+ out_profile = H264PROFILE_BASELINE;
+ break;
+ case 77:
+ out_profile = H264PROFILE_MAIN;
+ break;
+ case 83:
+ out_profile = H264PROFILE_SCALABLEBASELINE;
+ break;
+ case 86:
+ out_profile = H264PROFILE_SCALABLEHIGH;
+ break;
+ case 88:
+ out_profile = H264PROFILE_EXTENDED;
+ break;
+ case 100:
+ out_profile = H264PROFILE_HIGH;
+ break;
+ case 110:
+ out_profile = H264PROFILE_HIGH10PROFILE;
+ break;
+ case 118:
+ out_profile = H264PROFILE_MULTIVIEWHIGH;
+ break;
+ case 122:
+ out_profile = H264PROFILE_HIGH422PROFILE;
+ break;
+ case 128:
+ out_profile = H264PROFILE_STEREOHIGH;
+ break;
+ case 244:
+ out_profile = H264PROFILE_HIGH444PREDICTIVEPROFILE;
+ break;
+ default:
+ DVLOG(1) << "Warning: unrecognized AVC/H.264 profile " << profile_idc;
+ return false;
+ }
+
+ // TODO(servolk): Take into account also constraint set flags 3 through 5.
+ uint8_t constraint_set0_flag = (constraints_byte >> 7) & 1;
+ uint8_t constraint_set1_flag = (constraints_byte >> 6) & 1;
+ uint8_t constraint_set2_flag = (constraints_byte >> 5) & 1;
+ if (constraint_set2_flag && out_profile > H264PROFILE_EXTENDED) {
+ out_profile = H264PROFILE_EXTENDED;
+ }
+ if (constraint_set1_flag && out_profile > H264PROFILE_MAIN) {
+ out_profile = H264PROFILE_MAIN;
+ }
+ if (constraint_set0_flag && out_profile > H264PROFILE_BASELINE) {
+ out_profile = H264PROFILE_BASELINE;
+ }
+
+ if (level_idc)
+ *level_idc = level_byte;
+
+ if (profile)
+ *profile = out_profile;
+
+ return true;
+}
+
} // namespace media
diff --git a/chromium/media/base/video_codecs.h b/chromium/media/base/video_codecs.h
index 9d9032ff750..28c3b3eebc8 100644
--- a/chromium/media/base/video_codecs.h
+++ b/chromium/media/base/video_codecs.h
@@ -5,6 +5,7 @@
#ifndef MEDIA_BASE_VIDEO_CODECS_H_
#define MEDIA_BASE_VIDEO_CODECS_H_
+#include <stdint.h>
#include <string>
#include "media/base/media_export.h"
@@ -57,14 +58,22 @@ enum VideoCodecProfile {
VP8PROFILE_ANY = VP8PROFILE_MIN,
VP8PROFILE_MAX = VP8PROFILE_ANY,
VP9PROFILE_MIN = 12,
- VP9PROFILE_ANY = VP9PROFILE_MIN,
- VP9PROFILE_MAX = VP9PROFILE_ANY,
+ VP9PROFILE_PROFILE0 = VP9PROFILE_MIN,
+ VP9PROFILE_PROFILE1 = 13,
+ VP9PROFILE_PROFILE2 = 14,
+ VP9PROFILE_PROFILE3 = 15,
+ VP9PROFILE_MAX = VP9PROFILE_PROFILE3,
VIDEO_CODEC_PROFILE_MAX = VP9PROFILE_MAX,
};
std::string MEDIA_EXPORT GetCodecName(VideoCodec codec);
std::string MEDIA_EXPORT GetProfileName(VideoCodecProfile profile);
+// Handle parsing AVC/H.264 codec ids as outlined in RFC 6381 and ISO-14496-10.
+MEDIA_EXPORT bool ParseAVCCodecId(const std::string& codec_id,
+ VideoCodecProfile* profile,
+ uint8_t* level_idc);
+
} // namespace media
#endif // MEDIA_BASE_VIDEO_CODECS_H_
diff --git a/chromium/media/base/video_decoder.h b/chromium/media/base/video_decoder.h
index 68737c7328d..d6abf03e175 100644
--- a/chromium/media/base/video_decoder.h
+++ b/chromium/media/base/video_decoder.h
@@ -10,28 +10,20 @@
#include "base/callback.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
-#include "media/base/cdm_context.h"
+#include "media/base/decode_status.h"
#include "media/base/media_export.h"
#include "media/base/pipeline_status.h"
#include "ui/gfx/geometry/size.h"
namespace media {
+class CdmContext;
class DecoderBuffer;
class VideoDecoderConfig;
class VideoFrame;
class MEDIA_EXPORT VideoDecoder {
public:
- // Status codes for decode operations on VideoDecoder.
- // TODO(rileya): Now that both AudioDecoder and VideoDecoder Status enums
- // match, break them into a decoder_status.h.
- enum Status {
- kOk, // Everything went as planned.
- kAborted, // Decode was aborted as a result of Reset() being called.
- kDecodeError // Decoding error happened.
- };
-
// Callback for VideoDecoder initialization.
typedef base::Callback<void(bool success)> InitCB;
@@ -42,7 +34,7 @@ class MEDIA_EXPORT VideoDecoder {
// Callback type for Decode(). Called after the decoder has completed decoding
// corresponding DecoderBuffer, indicating that it's ready to accept another
// buffer to decode.
- typedef base::Callback<void(Status status)> DecodeCB;
+ typedef base::Callback<void(DecodeStatus)> DecodeCB;
VideoDecoder();
@@ -65,9 +57,8 @@ class MEDIA_EXPORT VideoDecoder {
// Initialization should fail if |low_delay| is true and the decoder cannot
// satisfy the requirements above.
//
- // |set_cdm_ready_cb| can be used to set/cancel a CdmReadyCB with which the
- // decoder can be notified when a CDM is ready. The decoder can use the CDM to
- // handle encrypted video stream.
+ // |cdm_context| can be used to handle encrypted buffers. May be null if the
+ // stream is not encrypted.
//
// Note:
// 1) The VideoDecoder will be reinitialized if it was initialized before.
@@ -76,7 +67,7 @@ class MEDIA_EXPORT VideoDecoder {
// 3) No VideoDecoder calls should be made before |init_cb| is executed.
virtual void Initialize(const VideoDecoderConfig& config,
bool low_delay,
- const SetCdmReadyCB& set_cdm_ready_cb,
+ CdmContext* cdm_context,
const InitCB& init_cb,
const OutputCB& output_cb) = 0;
diff --git a/chromium/media/base/video_decoder_config.cc b/chromium/media/base/video_decoder_config.cc
index dfe7254e9af..03133608987 100644
--- a/chromium/media/base/video_decoder_config.cc
+++ b/chromium/media/base/video_decoder_config.cc
@@ -29,7 +29,10 @@ VideoCodec VideoCodecProfileToVideoCodec(VideoCodecProfile profile) {
return kCodecH264;
case VP8PROFILE_ANY:
return kCodecVP8;
- case VP9PROFILE_ANY:
+ case VP9PROFILE_PROFILE0:
+ case VP9PROFILE_PROFILE1:
+ case VP9PROFILE_PROFILE2:
+ case VP9PROFILE_PROFILE3:
return kCodecVP9;
}
NOTREACHED();
@@ -39,22 +42,25 @@ VideoCodec VideoCodecProfileToVideoCodec(VideoCodecProfile profile) {
VideoDecoderConfig::VideoDecoderConfig()
: codec_(kUnknownVideoCodec),
profile_(VIDEO_CODEC_PROFILE_UNKNOWN),
- format_(PIXEL_FORMAT_UNKNOWN),
- is_encrypted_(false) {}
+ format_(PIXEL_FORMAT_UNKNOWN) {}
-VideoDecoderConfig::VideoDecoderConfig(VideoCodec codec,
- VideoCodecProfile profile,
- VideoPixelFormat format,
- ColorSpace color_space,
- const gfx::Size& coded_size,
- const gfx::Rect& visible_rect,
- const gfx::Size& natural_size,
- const std::vector<uint8_t>& extra_data,
- bool is_encrypted) {
+VideoDecoderConfig::VideoDecoderConfig(
+ VideoCodec codec,
+ VideoCodecProfile profile,
+ VideoPixelFormat format,
+ ColorSpace color_space,
+ const gfx::Size& coded_size,
+ const gfx::Rect& visible_rect,
+ const gfx::Size& natural_size,
+ const std::vector<uint8_t>& extra_data,
+ const EncryptionScheme& encryption_scheme) {
Initialize(codec, profile, format, color_space, coded_size, visible_rect,
- natural_size, extra_data, is_encrypted);
+ natural_size, extra_data, encryption_scheme);
}
+VideoDecoderConfig::VideoDecoderConfig(const VideoDecoderConfig& other) =
+ default;
+
VideoDecoderConfig::~VideoDecoderConfig() {}
void VideoDecoderConfig::Initialize(VideoCodec codec,
@@ -65,7 +71,7 @@ void VideoDecoderConfig::Initialize(VideoCodec codec,
const gfx::Rect& visible_rect,
const gfx::Size& natural_size,
const std::vector<uint8_t>& extra_data,
- bool is_encrypted) {
+ const EncryptionScheme& encryption_scheme) {
codec_ = codec;
profile_ = profile;
format_ = format;
@@ -74,7 +80,7 @@ void VideoDecoderConfig::Initialize(VideoCodec codec,
visible_rect_ = visible_rect;
natural_size_ = natural_size;
extra_data_ = extra_data;
- is_encrypted_ = is_encrypted;
+ encryption_scheme_ = encryption_scheme;
}
bool VideoDecoderConfig::IsValidConfig() const {
@@ -86,14 +92,13 @@ bool VideoDecoderConfig::IsValidConfig() const {
}
bool VideoDecoderConfig::Matches(const VideoDecoderConfig& config) const {
- return ((codec() == config.codec()) &&
- (format() == config.format()) &&
+ return ((codec() == config.codec()) && (format() == config.format()) &&
(profile() == config.profile()) &&
(coded_size() == config.coded_size()) &&
(visible_rect() == config.visible_rect()) &&
(natural_size() == config.natural_size()) &&
(extra_data() == config.extra_data()) &&
- (is_encrypted() == config.is_encrypted()));
+ (encryption_scheme().Matches(config.encryption_scheme())));
}
std::string VideoDecoderConfig::AsHumanReadableString() const {
diff --git a/chromium/media/base/video_decoder_config.h b/chromium/media/base/video_decoder_config.h
index ae9340afea3..50dee06534c 100644
--- a/chromium/media/base/video_decoder_config.h
+++ b/chromium/media/base/video_decoder_config.h
@@ -11,6 +11,7 @@
#include <vector>
#include "base/macros.h"
+#include "media/base/encryption_scheme.h"
#include "media/base/media_export.h"
#include "media/base/video_codecs.h"
#include "media/base/video_types.h"
@@ -38,7 +39,9 @@ class MEDIA_EXPORT VideoDecoderConfig {
const gfx::Rect& visible_rect,
const gfx::Size& natural_size,
const std::vector<uint8_t>& extra_data,
- bool is_encrypted);
+ const EncryptionScheme& encryption_scheme);
+
+ VideoDecoderConfig(const VideoDecoderConfig& other);
~VideoDecoderConfig();
@@ -51,7 +54,7 @@ class MEDIA_EXPORT VideoDecoderConfig {
const gfx::Rect& visible_rect,
const gfx::Size& natural_size,
const std::vector<uint8_t>& extra_data,
- bool is_encrypted);
+ const EncryptionScheme& encryption_scheme);
// Returns true if this object has appropriate configuration values, false
// otherwise.
@@ -98,7 +101,12 @@ class MEDIA_EXPORT VideoDecoderConfig {
// Whether the video stream is potentially encrypted.
// Note that in a potentially encrypted video stream, individual buffers
// can be encrypted or not encrypted.
- bool is_encrypted() const { return is_encrypted_; }
+ bool is_encrypted() const { return encryption_scheme_.is_encrypted(); }
+
+ // Encryption scheme used for encrypted buffers.
+ const EncryptionScheme& encryption_scheme() const {
+ return encryption_scheme_;
+ }
private:
VideoCodec codec_;
@@ -113,7 +121,7 @@ class MEDIA_EXPORT VideoDecoderConfig {
std::vector<uint8_t> extra_data_;
- bool is_encrypted_;
+ EncryptionScheme encryption_scheme_;
// Not using DISALLOW_COPY_AND_ASSIGN here intentionally to allow the compiler
// generated copy constructor and assignment operator. Since the extra data is
diff --git a/chromium/media/base/video_decoder_config_unittest.cc b/chromium/media/base/video_decoder_config_unittest.cc
index 7885c8f7170..18528330e6c 100644
--- a/chromium/media/base/video_decoder_config_unittest.cc
+++ b/chromium/media/base/video_decoder_config_unittest.cc
@@ -19,7 +19,7 @@ TEST(VideoDecoderConfigTest, Invalid_UnsupportedPixelFormat) {
VideoDecoderConfig config(kCodecVP8, VIDEO_CODEC_PROFILE_UNKNOWN,
PIXEL_FORMAT_UNKNOWN, COLOR_SPACE_UNSPECIFIED,
kCodedSize, kVisibleRect, kNaturalSize,
- EmptyExtraData(), false);
+ EmptyExtraData(), Unencrypted());
EXPECT_FALSE(config.IsValidConfig());
}
@@ -27,7 +27,7 @@ TEST(VideoDecoderConfigTest, Invalid_AspectRatioNumeratorZero) {
gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), 0, 1);
VideoDecoderConfig config(kCodecVP8, VP8PROFILE_ANY, kVideoFormat,
COLOR_SPACE_UNSPECIFIED, kCodedSize, kVisibleRect,
- natural_size, EmptyExtraData(), false);
+ natural_size, EmptyExtraData(), Unencrypted());
EXPECT_FALSE(config.IsValidConfig());
}
@@ -35,7 +35,7 @@ TEST(VideoDecoderConfigTest, Invalid_AspectRatioDenominatorZero) {
gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), 1, 0);
VideoDecoderConfig config(kCodecVP8, VP8PROFILE_ANY, kVideoFormat,
COLOR_SPACE_UNSPECIFIED, kCodedSize, kVisibleRect,
- natural_size, EmptyExtraData(), false);
+ natural_size, EmptyExtraData(), Unencrypted());
EXPECT_FALSE(config.IsValidConfig());
}
@@ -43,7 +43,7 @@ TEST(VideoDecoderConfigTest, Invalid_AspectRatioNumeratorNegative) {
gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), -1, 1);
VideoDecoderConfig config(kCodecVP8, VP8PROFILE_ANY, kVideoFormat,
COLOR_SPACE_UNSPECIFIED, kCodedSize, kVisibleRect,
- natural_size, EmptyExtraData(), false);
+ natural_size, EmptyExtraData(), Unencrypted());
EXPECT_FALSE(config.IsValidConfig());
}
@@ -51,7 +51,7 @@ TEST(VideoDecoderConfigTest, Invalid_AspectRatioDenominatorNegative) {
gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), 1, -1);
VideoDecoderConfig config(kCodecVP8, VP8PROFILE_ANY, kVideoFormat,
COLOR_SPACE_UNSPECIFIED, kCodedSize, kVisibleRect,
- natural_size, EmptyExtraData(), false);
+ natural_size, EmptyExtraData(), Unencrypted());
EXPECT_FALSE(config.IsValidConfig());
}
@@ -61,7 +61,7 @@ TEST(VideoDecoderConfigTest, Invalid_AspectRatioNumeratorTooLarge) {
gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), num, 1);
VideoDecoderConfig config(kCodecVP8, VP8PROFILE_ANY, kVideoFormat,
COLOR_SPACE_UNSPECIFIED, kCodedSize, kVisibleRect,
- natural_size, EmptyExtraData(), false);
+ natural_size, EmptyExtraData(), Unencrypted());
EXPECT_FALSE(config.IsValidConfig());
}
@@ -72,7 +72,7 @@ TEST(VideoDecoderConfigTest, Invalid_AspectRatioDenominatorTooLarge) {
EXPECT_EQ(0, natural_size.width());
VideoDecoderConfig config(kCodecVP8, VP8PROFILE_ANY, kVideoFormat,
COLOR_SPACE_UNSPECIFIED, kCodedSize, kVisibleRect,
- natural_size, EmptyExtraData(), false);
+ natural_size, EmptyExtraData(), Unencrypted());
EXPECT_FALSE(config.IsValidConfig());
}
diff --git a/chromium/media/base/video_frame.cc b/chromium/media/base/video_frame.cc
index 0ad4ac0f807..b4a8392ff8c 100644
--- a/chromium/media/base/video_frame.cc
+++ b/chromium/media/base/video_frame.cc
@@ -36,17 +36,6 @@ static inline size_t RoundDown(size_t value, size_t alignment) {
return value & ~(alignment - 1);
}
-static std::string ConfigToString(const VideoPixelFormat format,
- const VideoFrame::StorageType storage_type,
- const gfx::Size& coded_size,
- const gfx::Rect& visible_rect,
- const gfx::Size& natural_size) {
- return base::StringPrintf(
- "format:%s coded_size:%s visible_rect:%s natural_size:%s",
- VideoPixelFormatToString(format).c_str(), coded_size.ToString().c_str(),
- visible_rect.ToString().c_str(), natural_size.ToString().c_str());
-}
-
static std::string StorageTypeToString(
const VideoFrame::StorageType storage_type) {
switch (storage_type) {
@@ -70,19 +59,14 @@ static std::string StorageTypeToString(
#endif
case VideoFrame::STORAGE_GPU_MEMORY_BUFFERS:
return "GPU_MEMORY_BUFFERS";
+ case VideoFrame::STORAGE_MOJO_SHARED_BUFFER:
+ return "MOJO_SHARED_BUFFER";
}
NOTREACHED() << "Invalid StorageType provided: " << storage_type;
return "INVALID";
}
-// Returns true if |plane| is a valid plane index for the given |format|.
-static bool IsValidPlane(size_t plane, VideoPixelFormat format) {
- DCHECK_LE(VideoFrame::NumPlanes(format),
- static_cast<size_t>(VideoFrame::kMaxPlanes));
- return (plane < VideoFrame::NumPlanes(format));
-}
-
// Returns true if |frame| is accesible mapped in the VideoFrame memory space.
// static
static bool IsStorageTypeMappable(VideoFrame::StorageType storage_type) {
@@ -95,97 +79,20 @@ static bool IsStorageTypeMappable(VideoFrame::StorageType storage_type) {
(storage_type == VideoFrame::STORAGE_UNOWNED_MEMORY ||
storage_type == VideoFrame::STORAGE_OWNED_MEMORY ||
storage_type == VideoFrame::STORAGE_SHMEM ||
- storage_type == VideoFrame::STORAGE_GPU_MEMORY_BUFFERS);
+ storage_type == VideoFrame::STORAGE_GPU_MEMORY_BUFFERS ||
+ storage_type == VideoFrame::STORAGE_MOJO_SHARED_BUFFER);
}
-// Returns the pixel size per element for given |plane| and |format|. E.g. 2x2
-// for the U-plane in PIXEL_FORMAT_I420.
-static gfx::Size SampleSize(VideoPixelFormat format, size_t plane) {
- DCHECK(IsValidPlane(plane, format));
-
- switch (plane) {
- case VideoFrame::kYPlane:
- case VideoFrame::kAPlane:
- return gfx::Size(1, 1);
-
- case VideoFrame::kUPlane: // and VideoFrame::kUVPlane:
- case VideoFrame::kVPlane:
- switch (format) {
- case PIXEL_FORMAT_YV24:
- return gfx::Size(1, 1);
-
- case PIXEL_FORMAT_YV16:
- return gfx::Size(2, 1);
-
- case PIXEL_FORMAT_YV12:
- case PIXEL_FORMAT_I420:
- case PIXEL_FORMAT_YV12A:
- case PIXEL_FORMAT_NV12:
- case PIXEL_FORMAT_NV21:
- case PIXEL_FORMAT_MT21:
- return gfx::Size(2, 2);
-
- case PIXEL_FORMAT_UNKNOWN:
- case PIXEL_FORMAT_UYVY:
- case PIXEL_FORMAT_YUY2:
- case PIXEL_FORMAT_ARGB:
- case PIXEL_FORMAT_XRGB:
- case PIXEL_FORMAT_RGB24:
- case PIXEL_FORMAT_RGB32:
- case PIXEL_FORMAT_MJPEG:
- break;
- }
- }
- NOTREACHED();
- return gfx::Size();
-}
+// Checks if |source_format| can be wrapped into a |target_format| frame.
+static bool AreValidPixelFormatsForWrap(VideoPixelFormat source_format,
+ VideoPixelFormat target_format) {
+ if (source_format == target_format)
+ return true;
-// Return the alignment for the whole frame, calculated as the max of the
-// alignment for each individual plane.
-static gfx::Size CommonAlignment(VideoPixelFormat format) {
- int max_sample_width = 0;
- int max_sample_height = 0;
- for (size_t plane = 0; plane < VideoFrame::NumPlanes(format); ++plane) {
- const gfx::Size sample_size = SampleSize(format, plane);
- max_sample_width = std::max(max_sample_width, sample_size.width());
- max_sample_height = std::max(max_sample_height, sample_size.height());
- }
- return gfx::Size(max_sample_width, max_sample_height);
-}
-
-// Returns the number of bytes per element for given |plane| and |format|.
-static int BytesPerElement(VideoPixelFormat format, size_t plane) {
- DCHECK(IsValidPlane(plane, format));
- switch (format) {
- case PIXEL_FORMAT_ARGB:
- case PIXEL_FORMAT_XRGB:
- case PIXEL_FORMAT_RGB32:
- return 4;
- case PIXEL_FORMAT_RGB24:
- return 3;
- case PIXEL_FORMAT_UYVY:
- case PIXEL_FORMAT_YUY2:
- return 2;
- case PIXEL_FORMAT_NV12:
- case PIXEL_FORMAT_NV21:
- case PIXEL_FORMAT_MT21: {
- static const int bytes_per_element[] = {1, 2};
- DCHECK_LT(plane, arraysize(bytes_per_element));
- return bytes_per_element[plane];
- }
- case PIXEL_FORMAT_YV12:
- case PIXEL_FORMAT_I420:
- case PIXEL_FORMAT_YV16:
- case PIXEL_FORMAT_YV12A:
- case PIXEL_FORMAT_YV24:
- return 1;
- case PIXEL_FORMAT_MJPEG:
- return 0;
- case PIXEL_FORMAT_UNKNOWN:
- break;
- }
- NOTREACHED();
- return 0;
+ // It is possible to add other planar to planar format conversions here if the
+ // use case is there.
+ return source_format == PIXEL_FORMAT_YV12A &&
+ target_format == PIXEL_FORMAT_I420;
}
// static
@@ -214,7 +121,7 @@ bool VideoFrame::IsValidConfig(VideoPixelFormat format,
return true;
// Make sure new formats are properly accounted for in the method.
- static_assert(PIXEL_FORMAT_MAX == 15,
+ static_assert(PIXEL_FORMAT_MAX == 21,
"Added pixel format, please review IsValidConfig()");
if (format == PIXEL_FORMAT_UNKNOWN) {
@@ -483,33 +390,39 @@ scoped_refptr<VideoFrame> VideoFrame::WrapCVPixelBuffer(
// static
scoped_refptr<VideoFrame> VideoFrame::WrapVideoFrame(
- const scoped_refptr<VideoFrame>& frame,
- const gfx::Rect& visible_rect,
- const gfx::Size& natural_size) {
+ const scoped_refptr<VideoFrame>& frame,
+ VideoPixelFormat format,
+ const gfx::Rect& visible_rect,
+ const gfx::Size& natural_size) {
// Frames with textures need mailbox info propagated, and there's no support
// for that here yet, see http://crbug/362521.
CHECK(!frame->HasTextures());
-
DCHECK(frame->visible_rect().Contains(visible_rect));
- if (!IsValidConfig(frame->format(), frame->storage_type(),
- frame->coded_size(), visible_rect, natural_size)) {
+ if (!AreValidPixelFormatsForWrap(frame->format(), format)) {
+ LOG(DFATAL) << __FUNCTION__ << " Invalid format conversion."
+ << VideoPixelFormatToString(frame->format()) << " to "
+ << VideoPixelFormatToString(format);
+ return nullptr;
+ }
+
+ if (!IsValidConfig(format, frame->storage_type(), frame->coded_size(),
+ visible_rect, natural_size)) {
LOG(DFATAL) << __FUNCTION__ << " Invalid config."
- << ConfigToString(frame->format(), frame->storage_type(),
+ << ConfigToString(format, frame->storage_type(),
frame->coded_size(), visible_rect,
natural_size);
return nullptr;
}
- scoped_refptr<VideoFrame> wrapping_frame(new VideoFrame(
- frame->format(), frame->storage_type(), frame->coded_size(), visible_rect,
- natural_size, frame->timestamp()));
- if (frame->metadata()->IsTrue(VideoFrameMetadata::END_OF_STREAM)) {
- wrapping_frame->metadata()->SetBoolean(VideoFrameMetadata::END_OF_STREAM,
- true);
- }
+ scoped_refptr<VideoFrame> wrapping_frame(
+ new VideoFrame(format, frame->storage_type(), frame->coded_size(),
+ visible_rect, natural_size, frame->timestamp()));
+
+ // Copy all metadata to the wrapped frame.
+ wrapping_frame->metadata()->MergeMetadataFrom(frame->metadata());
- for (size_t i = 0; i < NumPlanes(frame->format()); ++i) {
+ for (size_t i = 0; i < NumPlanes(format); ++i) {
wrapping_frame->strides_[i] = frame->stride(i);
wrapping_frame->data_[i] = frame->data(i);
}
@@ -617,6 +530,12 @@ size_t VideoFrame::NumPlanes(VideoPixelFormat format) {
case PIXEL_FORMAT_YV12:
case PIXEL_FORMAT_YV16:
case PIXEL_FORMAT_YV24:
+ case PIXEL_FORMAT_YUV420P9:
+ case PIXEL_FORMAT_YUV422P9:
+ case PIXEL_FORMAT_YUV444P9:
+ case PIXEL_FORMAT_YUV420P10:
+ case PIXEL_FORMAT_YUV422P10:
+ case PIXEL_FORMAT_YUV444P10:
return 3;
case PIXEL_FORMAT_YV12A:
return 4;
@@ -860,12 +779,9 @@ std::string VideoFrame::AsHumanReadableString() {
return "end of stream";
std::ostringstream s;
- s << "format: " << VideoPixelFormatToString(format_)
- << " storage_type: " << StorageTypeToString(storage_type_)
- << " coded_size: " << coded_size_.ToString()
- << " visible_rect: " << visible_rect_.ToString()
- << " natural_size: " << natural_size_.ToString()
- << " timestamp: " << timestamp_.InMicroseconds();
+ s << ConfigToString(format_, storage_type_, coded_size_, visible_rect_,
+ natural_size_)
+ << " timestamp:" << timestamp_.InMicroseconds();
return s.str();
}
@@ -941,6 +857,65 @@ VideoFrame::VideoFrame(VideoPixelFormat format,
memset(&data_, 0, sizeof(data_));
}
+VideoFrame::~VideoFrame() {
+ if (!mailbox_holders_release_cb_.is_null()) {
+ gpu::SyncToken release_sync_token;
+ {
+ // To ensure that changes to |release_sync_token_| are visible on this
+ // thread (imply a memory barrier).
+ base::AutoLock locker(release_sync_token_lock_);
+ release_sync_token = release_sync_token_;
+ }
+ base::ResetAndReturn(&mailbox_holders_release_cb_).Run(release_sync_token);
+ }
+
+ for (auto& callback : done_callbacks_)
+ base::ResetAndReturn(&callback).Run();
+}
+
+// static
+std::string VideoFrame::ConfigToString(const VideoPixelFormat format,
+ const StorageType storage_type,
+ const gfx::Size& coded_size,
+ const gfx::Rect& visible_rect,
+ const gfx::Size& natural_size) {
+ return base::StringPrintf(
+ "format:%s storage_type:%s coded_size:%s visible_rect:%s natural_size:%s",
+ VideoPixelFormatToString(format).c_str(),
+ StorageTypeToString(storage_type).c_str(), coded_size.ToString().c_str(),
+ visible_rect.ToString().c_str(), natural_size.ToString().c_str());
+}
+
+// static
+bool VideoFrame::IsValidPlane(size_t plane, VideoPixelFormat format) {
+ DCHECK_LE(NumPlanes(format), static_cast<size_t>(kMaxPlanes));
+ return (plane < NumPlanes(format));
+}
+
+// static
+gfx::Size VideoFrame::DetermineAlignedSize(VideoPixelFormat format,
+ const gfx::Size& dimensions) {
+ const gfx::Size alignment = CommonAlignment(format);
+ const gfx::Size adjusted =
+ gfx::Size(RoundUp(dimensions.width(), alignment.width()),
+ RoundUp(dimensions.height(), alignment.height()));
+ DCHECK((adjusted.width() % alignment.width() == 0) &&
+ (adjusted.height() % alignment.height() == 0));
+ return adjusted;
+}
+
+void VideoFrame::set_data(size_t plane, uint8_t* ptr) {
+ DCHECK(IsValidPlane(plane, format_));
+ DCHECK(ptr);
+ data_[plane] = ptr;
+}
+
+void VideoFrame::set_stride(size_t plane, int stride) {
+ DCHECK(IsValidPlane(plane, format_));
+ DCHECK_GT(stride, 0);
+ strides_[plane] = stride;
+}
+
VideoFrame::VideoFrame(VideoPixelFormat format,
StorageType storage_type,
const gfx::Size& coded_size,
@@ -978,22 +953,6 @@ VideoFrame::VideoFrame(VideoPixelFormat format,
mailbox_holders_release_cb_ = mailbox_holder_release_cb;
}
-VideoFrame::~VideoFrame() {
- if (!mailbox_holders_release_cb_.is_null()) {
- gpu::SyncToken release_sync_token;
- {
- // To ensure that changes to |release_sync_token_| are visible on this
- // thread (imply a memory barrier).
- base::AutoLock locker(release_sync_token_lock_);
- release_sync_token = release_sync_token_;
- }
- base::ResetAndReturn(&mailbox_holders_release_cb_).Run(release_sync_token);
- }
-
- for (auto& callback : done_callbacks_)
- base::ResetAndReturn(&callback).Run();
-}
-
// static
scoped_refptr<VideoFrame> VideoFrame::CreateFrameInternal(
VideoPixelFormat format,
@@ -1011,13 +970,7 @@ scoped_refptr<VideoFrame> VideoFrame::CreateFrameInternal(
// ourselves), we can pad the requested |coded_size| if necessary if the
// request does not line up on sample boundaries. See discussion at
// http://crrev.com/1240833003
- const gfx::Size alignment = CommonAlignment(format);
- const gfx::Size new_coded_size =
- gfx::Size(RoundUp(coded_size.width(), alignment.width()),
- RoundUp(coded_size.height(), alignment.height()));
- DCHECK((new_coded_size.width() % alignment.width() == 0) &&
- (new_coded_size.height() % alignment.height() == 0));
-
+ const gfx::Size new_coded_size = DetermineAlignedSize(format, coded_size);
const StorageType storage = STORAGE_OWNED_MEMORY;
if (!IsValidConfig(format, storage, new_coded_size, visible_rect,
natural_size)) {
@@ -1033,6 +986,106 @@ scoped_refptr<VideoFrame> VideoFrame::CreateFrameInternal(
return frame;
}
+// static
+gfx::Size VideoFrame::SampleSize(VideoPixelFormat format, size_t plane) {
+ DCHECK(IsValidPlane(plane, format));
+
+ switch (plane) {
+ case kYPlane:
+ case kAPlane:
+ return gfx::Size(1, 1);
+
+ case kUPlane: // and kUVPlane:
+ case kVPlane:
+ switch (format) {
+ case PIXEL_FORMAT_YV24:
+ case PIXEL_FORMAT_YUV444P9:
+ case PIXEL_FORMAT_YUV444P10:
+ return gfx::Size(1, 1);
+
+ case PIXEL_FORMAT_YV16:
+ case PIXEL_FORMAT_YUV422P9:
+ case PIXEL_FORMAT_YUV422P10:
+ return gfx::Size(2, 1);
+
+ case PIXEL_FORMAT_YV12:
+ case PIXEL_FORMAT_I420:
+ case PIXEL_FORMAT_YV12A:
+ case PIXEL_FORMAT_NV12:
+ case PIXEL_FORMAT_NV21:
+ case PIXEL_FORMAT_MT21:
+ case PIXEL_FORMAT_YUV420P9:
+ case PIXEL_FORMAT_YUV420P10:
+ return gfx::Size(2, 2);
+
+ case PIXEL_FORMAT_UNKNOWN:
+ case PIXEL_FORMAT_UYVY:
+ case PIXEL_FORMAT_YUY2:
+ case PIXEL_FORMAT_ARGB:
+ case PIXEL_FORMAT_XRGB:
+ case PIXEL_FORMAT_RGB24:
+ case PIXEL_FORMAT_RGB32:
+ case PIXEL_FORMAT_MJPEG:
+ break;
+ }
+ }
+ NOTREACHED();
+ return gfx::Size();
+}
+
+// static
+int VideoFrame::BytesPerElement(VideoPixelFormat format, size_t plane) {
+ DCHECK(IsValidPlane(plane, format));
+ switch (format) {
+ case PIXEL_FORMAT_ARGB:
+ case PIXEL_FORMAT_XRGB:
+ case PIXEL_FORMAT_RGB32:
+ return 4;
+ case PIXEL_FORMAT_RGB24:
+ return 3;
+ case PIXEL_FORMAT_UYVY:
+ case PIXEL_FORMAT_YUY2:
+ case PIXEL_FORMAT_YUV420P9:
+ case PIXEL_FORMAT_YUV422P9:
+ case PIXEL_FORMAT_YUV444P9:
+ case PIXEL_FORMAT_YUV420P10:
+ case PIXEL_FORMAT_YUV422P10:
+ case PIXEL_FORMAT_YUV444P10:
+ return 2;
+ case PIXEL_FORMAT_NV12:
+ case PIXEL_FORMAT_NV21:
+ case PIXEL_FORMAT_MT21: {
+ static const int bytes_per_element[] = {1, 2};
+ DCHECK_LT(plane, arraysize(bytes_per_element));
+ return bytes_per_element[plane];
+ }
+ case PIXEL_FORMAT_YV12:
+ case PIXEL_FORMAT_I420:
+ case PIXEL_FORMAT_YV16:
+ case PIXEL_FORMAT_YV12A:
+ case PIXEL_FORMAT_YV24:
+ return 1;
+ case PIXEL_FORMAT_MJPEG:
+ return 0;
+ case PIXEL_FORMAT_UNKNOWN:
+ break;
+ }
+ NOTREACHED();
+ return 0;
+}
+
+// static
+gfx::Size VideoFrame::CommonAlignment(VideoPixelFormat format) {
+ int max_sample_width = 0;
+ int max_sample_height = 0;
+ for (size_t plane = 0; plane < NumPlanes(format); ++plane) {
+ const gfx::Size sample_size = SampleSize(format, plane);
+ max_sample_width = std::max(max_sample_width, sample_size.width());
+ max_sample_height = std::max(max_sample_height, sample_size.height());
+ }
+ return gfx::Size(max_sample_width, max_sample_height);
+}
+
void VideoFrame::AllocateYUV(bool zero_initialize_memory) {
DCHECK_EQ(storage_type_, STORAGE_OWNED_MEMORY);
static_assert(0 == kYPlane, "y plane data must be index 0");
diff --git a/chromium/media/base/video_frame.h b/chromium/media/base/video_frame.h
index ad7f4c01a42..944a04018ca 100644
--- a/chromium/media/base/video_frame.h
+++ b/chromium/media/base/video_frame.h
@@ -73,7 +73,8 @@ class MEDIA_EXPORT VideoFrame : public base::RefCountedThreadSafe<VideoFrame> {
STORAGE_HOLE = 6,
#endif
STORAGE_GPU_MEMORY_BUFFERS = 7,
- STORAGE_LAST = STORAGE_GPU_MEMORY_BUFFERS,
+ STORAGE_MOJO_SHARED_BUFFER = 8,
+ STORAGE_LAST = STORAGE_MOJO_SHARED_BUFFER,
};
// CB to be called on the mailbox backing this frame when the frame is
@@ -244,6 +245,7 @@ class MEDIA_EXPORT VideoFrame : public base::RefCountedThreadSafe<VideoFrame> {
// frame->visible_rect().
static scoped_refptr<VideoFrame> WrapVideoFrame(
const scoped_refptr<VideoFrame>& frame,
+ VideoPixelFormat format,
const gfx::Rect& visible_rect,
const gfx::Size& natural_size);
@@ -413,28 +415,40 @@ class MEDIA_EXPORT VideoFrame : public base::RefCountedThreadSafe<VideoFrame> {
// Returns a human-readable string describing |*this|.
std::string AsHumanReadableString();
- private:
+ protected:
friend class base::RefCountedThreadSafe<VideoFrame>;
- static scoped_refptr<VideoFrame> WrapExternalStorage(
- VideoPixelFormat format,
- StorageType storage_type,
- const gfx::Size& coded_size,
- const gfx::Rect& visible_rect,
- const gfx::Size& natural_size,
- uint8_t* data,
- size_t data_size,
- base::TimeDelta timestamp,
- base::SharedMemoryHandle handle,
- size_t data_offset);
-
// Clients must use the static factory/wrapping methods to create a new frame.
+ // Derived classes should create their own factory/wrapping methods, and use
+ // this constructor to do basic initialization.
VideoFrame(VideoPixelFormat format,
StorageType storage_type,
const gfx::Size& coded_size,
const gfx::Rect& visible_rect,
const gfx::Size& natural_size,
base::TimeDelta timestamp);
+
+ virtual ~VideoFrame();
+
+ // Creates a summary of the configuration settings provided as parameters.
+ static std::string ConfigToString(const VideoPixelFormat format,
+ const VideoFrame::StorageType storage_type,
+ const gfx::Size& coded_size,
+ const gfx::Rect& visible_rect,
+ const gfx::Size& natural_size);
+
+ // Returns true if |plane| is a valid plane index for the given |format|.
+ static bool IsValidPlane(size_t plane, VideoPixelFormat format);
+
+ // Returns |dimensions| adjusted to appropriate boundaries based on |format|.
+ static gfx::Size DetermineAlignedSize(VideoPixelFormat format,
+ const gfx::Size& dimensions);
+
+ void set_data(size_t plane, uint8_t* ptr);
+ void set_stride(size_t plane, int stride);
+
+ private:
+ // Clients must use the static factory/wrapping methods to create a new frame.
VideoFrame(VideoPixelFormat format,
StorageType storage_type,
const gfx::Size& coded_size,
@@ -451,7 +465,18 @@ class MEDIA_EXPORT VideoFrame : public base::RefCountedThreadSafe<VideoFrame> {
const gpu::MailboxHolder(&mailbox_holders)[kMaxPlanes],
const ReleaseMailboxCB& mailbox_holder_release_cb,
base::TimeDelta timestamp);
- virtual ~VideoFrame();
+
+ static scoped_refptr<VideoFrame> WrapExternalStorage(
+ VideoPixelFormat format,
+ StorageType storage_type,
+ const gfx::Size& coded_size,
+ const gfx::Rect& visible_rect,
+ const gfx::Size& natural_size,
+ uint8_t* data,
+ size_t data_size,
+ base::TimeDelta timestamp,
+ base::SharedMemoryHandle handle,
+ size_t data_offset);
static scoped_refptr<VideoFrame> CreateFrameInternal(
VideoPixelFormat format,
@@ -461,6 +486,17 @@ class MEDIA_EXPORT VideoFrame : public base::RefCountedThreadSafe<VideoFrame> {
base::TimeDelta timestamp,
bool zero_initialize_memory);
+ // Returns the pixel size of each subsample for a given |plane| and |format|.
+ // E.g. 2x2 for the U-plane in PIXEL_FORMAT_I420.
+ static gfx::Size SampleSize(VideoPixelFormat format, size_t plane);
+
+ // Returns the number of bytes per element for given |plane| and |format|.
+ static int BytesPerElement(VideoPixelFormat format, size_t plane);
+
+ // Return the alignment for the whole frame, calculated as the max of the
+ // alignment for each individual plane.
+ static gfx::Size CommonAlignment(VideoPixelFormat format);
+
void AllocateYUV(bool zero_initialize_memory);
// Frame format.
diff --git a/chromium/media/base/video_frame_metadata.cc b/chromium/media/base/video_frame_metadata.cc
index 2ecf002550c..6aa322669c8 100644
--- a/chromium/media/base/video_frame_metadata.cc
+++ b/chromium/media/base/video_frame_metadata.cc
@@ -145,6 +145,11 @@ void VideoFrameMetadata::MergeInternalValuesFrom(
dictionary_.MergeDictionary(&in);
}
+void VideoFrameMetadata::MergeMetadataFrom(
+ const VideoFrameMetadata* metadata_source) {
+ dictionary_.MergeDictionary(&metadata_source->dictionary_);
+}
+
const base::BinaryValue* VideoFrameMetadata::GetBinaryValue(Key key) const {
const base::Value* internal_value = nullptr;
if (dictionary_.GetWithoutPathExpansion(ToInternalKey(key),
diff --git a/chromium/media/base/video_frame_metadata.h b/chromium/media/base/video_frame_metadata.h
index 12112a43ad0..1f6cd3539f3 100644
--- a/chromium/media/base/video_frame_metadata.h
+++ b/chromium/media/base/video_frame_metadata.h
@@ -9,6 +9,7 @@
#include "base/compiler_specific.h"
#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
#include "base/time/time.h"
#include "base/values.h"
#include "media/base/media_export.h"
@@ -40,6 +41,12 @@ class MEDIA_EXPORT VideoFrameMetadata {
// contexts.
COPY_REQUIRED,
+ // Indicates that the frame is owned by the decoder and that destroying the
+ // decoder will make the frame unrenderable. TODO(sandersd): Remove once OSX
+ // and Windows hardware decoders support frames which outlive the decoder.
+ // http://crbug.com/595716 and http://crbug.com/602708.
+ DECODER_OWNS_FRAME,
+
// Indicates if the current frame is the End of its current Stream. Use
// Get/SetBoolean() for this Key.
END_OF_STREAM,
@@ -94,6 +101,11 @@ class MEDIA_EXPORT VideoFrameMetadata {
// measurements would be used as feedback.
RESOURCE_UTILIZATION,
+ // Sources of VideoFrames use this marker to indicate that an instance of
+ // VideoFrameExternalResources produced from the associated video frame
+ // should use read lock fences.
+ READ_LOCK_FENCES_ENABLED,
+
NUM_KEYS
};
@@ -131,6 +143,9 @@ class MEDIA_EXPORT VideoFrameMetadata {
void MergeInternalValuesInto(base::DictionaryValue* out) const;
void MergeInternalValuesFrom(const base::DictionaryValue& in);
+ // Merges internal values from |metadata_source|.
+ void MergeMetadataFrom(const VideoFrameMetadata* metadata_source);
+
private:
const base::BinaryValue* GetBinaryValue(Key key) const;
diff --git a/chromium/media/base/video_frame_pool.cc b/chromium/media/base/video_frame_pool.cc
index 40611eedc8c..2bc7ff6a47e 100644
--- a/chromium/media/base/video_frame_pool.cc
+++ b/chromium/media/base/video_frame_pool.cc
@@ -86,7 +86,7 @@ scoped_refptr<VideoFrame> VideoFramePool::PoolImpl::CreateFrame(
}
scoped_refptr<VideoFrame> wrapped_frame = VideoFrame::WrapVideoFrame(
- frame, frame->visible_rect(), frame->natural_size());
+ frame, frame->format(), frame->visible_rect(), frame->natural_size());
wrapped_frame->AddDestructionObserver(
base::Bind(&VideoFramePool::PoolImpl::FrameReleased, this, frame));
return wrapped_frame;
diff --git a/chromium/media/base/video_frame_unittest.cc b/chromium/media/base/video_frame_unittest.cc
index 18e18932599..79652bb43cd 100644
--- a/chromium/media/base/video_frame_unittest.cc
+++ b/chromium/media/base/video_frame_unittest.cc
@@ -229,6 +229,8 @@ static void FrameNoLongerNeededCallback(
TEST(VideoFrame, WrapVideoFrame) {
const int kWidth = 4;
const int kHeight = 4;
+ const base::TimeDelta kFrameDuration = base::TimeDelta::FromMicroseconds(42);
+
scoped_refptr<media::VideoFrame> frame;
bool done_callback_was_run = false;
{
@@ -238,11 +240,12 @@ TEST(VideoFrame, WrapVideoFrame) {
gfx::Rect visible_rect(1, 1, 1, 1);
gfx::Size natural_size = visible_rect.size();
+ wrapped_frame->metadata()->SetTimeDelta(
+ media::VideoFrameMetadata::FRAME_DURATION, kFrameDuration);
frame = media::VideoFrame::WrapVideoFrame(
- wrapped_frame, visible_rect, natural_size);
- frame->AddDestructionObserver(
- base::Bind(&FrameNoLongerNeededCallback, wrapped_frame,
- &done_callback_was_run));
+ wrapped_frame, wrapped_frame->format(), visible_rect, natural_size);
+ frame->AddDestructionObserver(base::Bind(
+ &FrameNoLongerNeededCallback, wrapped_frame, &done_callback_was_run));
EXPECT_EQ(wrapped_frame->coded_size(), frame->coded_size());
EXPECT_EQ(wrapped_frame->data(media::VideoFrame::kYPlane),
frame->data(media::VideoFrame::kYPlane));
@@ -250,6 +253,20 @@ TEST(VideoFrame, WrapVideoFrame) {
EXPECT_EQ(visible_rect, frame->visible_rect());
EXPECT_NE(wrapped_frame->natural_size(), frame->natural_size());
EXPECT_EQ(natural_size, frame->natural_size());
+
+ // Verify metadata was copied to the wrapped frame.
+ base::TimeDelta frame_duration;
+ ASSERT_TRUE(frame->metadata()->GetTimeDelta(
+ media::VideoFrameMetadata::FRAME_DURATION, &frame_duration));
+
+ EXPECT_EQ(frame_duration, kFrameDuration);
+
+ // Verify the metadata copy was a deep copy.
+ wrapped_frame->metadata()->Clear();
+ EXPECT_NE(
+ wrapped_frame->metadata()->HasKey(
+ media::VideoFrameMetadata::FRAME_DURATION),
+ frame->metadata()->HasKey(media::VideoFrameMetadata::FRAME_DURATION));
}
EXPECT_FALSE(done_callback_was_run);
@@ -274,8 +291,8 @@ static void TextureCallback(gpu::SyncToken* called_sync_token,
// Verify the gpu::MailboxHolder::ReleaseCallback is called when VideoFrame is
// destroyed with the default release sync point.
TEST(VideoFrame, TextureNoLongerNeededCallbackIsCalled) {
- gpu::SyncToken called_sync_token(gpu::CommandBufferNamespace::GPU_IO, 0, 1,
- 1);
+ gpu::SyncToken called_sync_token(gpu::CommandBufferNamespace::GPU_IO, 0,
+ gpu::CommandBufferId::FromUnsafeValue(1), 1);
{
scoped_refptr<VideoFrame> frame = VideoFrame::WrapNativeTexture(
@@ -321,7 +338,8 @@ TEST(VideoFrame,
const int kPlanesNum = 3;
const gpu::CommandBufferNamespace kNamespace =
gpu::CommandBufferNamespace::GPU_IO;
- const uint64_t kCommandBufferId = 0x123;
+ const gpu::CommandBufferId kCommandBufferId =
+ gpu::CommandBufferId::FromUnsafeValue(0x123);
gpu::Mailbox mailbox[kPlanesNum];
for (int i = 0; i < kPlanesNum; ++i) {
mailbox[i].name[0] = 50 + 1;
diff --git a/chromium/media/base/video_renderer.h b/chromium/media/base/video_renderer.h
index 6acce776652..ddd47777daa 100644
--- a/chromium/media/base/video_renderer.h
+++ b/chromium/media/base/video_renderer.h
@@ -10,13 +10,13 @@
#include "base/memory/ref_counted.h"
#include "base/time/time.h"
#include "media/base/buffering_state.h"
-#include "media/base/cdm_context.h"
#include "media/base/media_export.h"
#include "media/base/pipeline_status.h"
#include "media/base/time_source.h"
namespace media {
+class CdmContext;
class DemuxerStream;
class VideoDecoder;
class VideoFrame;
@@ -35,8 +35,8 @@ class MEDIA_EXPORT VideoRenderer {
// completion. If initialization fails, only |init_cb| (not |error_cb|) will
// be called.
//
- // |set_cdm_ready_cb| is fired when a CDM is needed, i.e. when the |stream| is
- // encrypted.
+ // |cdm_context| can be used to handle encrypted streams. May be null if the
+ // stream is not encrypted.
//
// |statistics_cb| is executed periodically with video rendering stats, such
// as dropped frames.
@@ -56,7 +56,7 @@ class MEDIA_EXPORT VideoRenderer {
virtual void Initialize(
DemuxerStream* stream,
const PipelineStatusCB& init_cb,
- const SetCdmReadyCB& set_cdm_ready_cb,
+ CdmContext* cdm_context,
const StatisticsCB& statistics_cb,
const BufferingStateCB& buffering_state_cb,
const base::Closure& ended_cb,
diff --git a/chromium/media/base/video_types.cc b/chromium/media/base/video_types.cc
index e2f137d67dd..f8eeb3e1bb4 100644
--- a/chromium/media/base/video_types.cc
+++ b/chromium/media/base/video_types.cc
@@ -42,6 +42,18 @@ std::string VideoPixelFormatToString(VideoPixelFormat format) {
return "PIXEL_FORMAT_MJPEG";
case PIXEL_FORMAT_MT21:
return "PIXEL_FORMAT_MT21";
+ case PIXEL_FORMAT_YUV420P9:
+ return "PIXEL_FORMAT_YUV420P9";
+ case PIXEL_FORMAT_YUV420P10:
+ return "PIXEL_FORMAT_YUV420P10";
+ case PIXEL_FORMAT_YUV422P9:
+ return "PIXEL_FORMAT_YUV422P9";
+ case PIXEL_FORMAT_YUV422P10:
+ return "PIXEL_FORMAT_YUV422P10";
+ case PIXEL_FORMAT_YUV444P9:
+ return "PIXEL_FORMAT_YUV444P9";
+ case PIXEL_FORMAT_YUV444P10:
+ return "PIXEL_FORMAT_YUV444P10";
}
NOTREACHED() << "Invalid VideoPixelFormat provided: " << format;
return "";
@@ -57,6 +69,12 @@ bool IsYuvPlanar(VideoPixelFormat format) {
case PIXEL_FORMAT_NV12:
case PIXEL_FORMAT_NV21:
case PIXEL_FORMAT_MT21:
+ case PIXEL_FORMAT_YUV420P9:
+ case PIXEL_FORMAT_YUV420P10:
+ case PIXEL_FORMAT_YUV422P9:
+ case PIXEL_FORMAT_YUV422P10:
+ case PIXEL_FORMAT_YUV444P9:
+ case PIXEL_FORMAT_YUV444P10:
return true;
case PIXEL_FORMAT_UNKNOWN:
@@ -87,6 +105,12 @@ bool IsOpaque(VideoPixelFormat format) {
case PIXEL_FORMAT_RGB24:
case PIXEL_FORMAT_MJPEG:
case PIXEL_FORMAT_MT21:
+ case PIXEL_FORMAT_YUV420P9:
+ case PIXEL_FORMAT_YUV420P10:
+ case PIXEL_FORMAT_YUV422P9:
+ case PIXEL_FORMAT_YUV422P10:
+ case PIXEL_FORMAT_YUV444P9:
+ case PIXEL_FORMAT_YUV444P10:
return true;
case PIXEL_FORMAT_YV12A:
case PIXEL_FORMAT_ARGB:
diff --git a/chromium/media/base/video_types.h b/chromium/media/base/video_types.h
index 7cf196e677a..7590b1b3a25 100644
--- a/chromium/media/base/video_types.h
+++ b/chromium/media/base/video_types.h
@@ -46,9 +46,17 @@ enum VideoPixelFormat {
// Row pitch = ((width+15)/16) * 16.
// Plane size = Row pitch * (((height+31)/32)*32)
PIXEL_FORMAT_MT21 = 15,
+
+ PIXEL_FORMAT_YUV420P9 = 16,
+ PIXEL_FORMAT_YUV420P10 = 17,
+ PIXEL_FORMAT_YUV422P9 = 18,
+ PIXEL_FORMAT_YUV422P10 = 19,
+ PIXEL_FORMAT_YUV444P9 = 20,
+ PIXEL_FORMAT_YUV444P10 = 21,
+
// Please update UMA histogram enumeration when adding new formats here.
PIXEL_FORMAT_MAX =
- PIXEL_FORMAT_MT21, // Must always be equal to largest entry logged.
+ PIXEL_FORMAT_YUV444P10, // Must always be equal to largest entry logged.
};
// Color space or color range used for the pixels.
diff --git a/chromium/media/base/video_util.cc b/chromium/media/base/video_util.cc
index c6286738244..d9954f8f8e7 100644
--- a/chromium/media/base/video_util.cc
+++ b/chromium/media/base/video_util.cc
@@ -14,6 +14,13 @@
namespace media {
+namespace {
+
+// Empty method used for keeping a reference to the original media::VideoFrame.
+void ReleaseOriginalFrame(const scoped_refptr<media::VideoFrame>& frame) {}
+
+} // namespace
+
gfx::Size GetNaturalSize(const gfx::Size& visible_size,
int aspect_ratio_numerator,
int aspect_ratio_denominator) {
@@ -316,4 +323,20 @@ void CopyRGBToVideoFrame(const uint8_t* source,
uv_stride);
}
+scoped_refptr<VideoFrame> WrapAsI420VideoFrame(
+ const scoped_refptr<VideoFrame>& frame) {
+ DCHECK_EQ(VideoFrame::STORAGE_OWNED_MEMORY, frame->storage_type());
+ DCHECK_EQ(PIXEL_FORMAT_YV12A, frame->format());
+
+ scoped_refptr<media::VideoFrame> wrapped_frame =
+ media::VideoFrame::WrapVideoFrame(frame, PIXEL_FORMAT_I420,
+ frame->visible_rect(),
+ frame->natural_size());
+ if (!wrapped_frame)
+ return nullptr;
+ wrapped_frame->AddDestructionObserver(
+ base::Bind(&ReleaseOriginalFrame, frame));
+ return wrapped_frame;
+}
+
} // namespace media
diff --git a/chromium/media/base/video_util.h b/chromium/media/base/video_util.h
index e51343344a0..2dec7e93710 100644
--- a/chromium/media/base/video_util.h
+++ b/chromium/media/base/video_util.h
@@ -7,6 +7,7 @@
#include <stdint.h>
+#include "base/memory/ref_counted.h"
#include "media/base/media_export.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
@@ -92,6 +93,10 @@ MEDIA_EXPORT void CopyRGBToVideoFrame(const uint8_t* source,
const gfx::Rect& region_in_frame,
VideoFrame* frame);
+// Converts a frame with YV12A format into I420 by dropping alpha channel.
+MEDIA_EXPORT scoped_refptr<VideoFrame> WrapAsI420VideoFrame(
+ const scoped_refptr<VideoFrame>& frame);
+
} // namespace media
#endif // MEDIA_BASE_VIDEO_UTIL_H_
diff --git a/chromium/media/base/yuv_convert_perftest.cc b/chromium/media/base/yuv_convert_perftest.cc
index 3af1b7555ba..f92be0462e4 100644
--- a/chromium/media/base/yuv_convert_perftest.cc
+++ b/chromium/media/base/yuv_convert_perftest.cc
@@ -9,6 +9,7 @@
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
#include "base/path_service.h"
#include "base/time/time.h"
#include "build/build_config.h"
diff --git a/chromium/media/base/yuv_convert_unittest.cc b/chromium/media/base/yuv_convert_unittest.cc
index 77c48ce0b9c..a05e15a2e02 100644
--- a/chromium/media/base/yuv_convert_unittest.cc
+++ b/chromium/media/base/yuv_convert_unittest.cc
@@ -9,6 +9,7 @@
#include "base/cpu.h"
#include "base/files/file_util.h"
#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
#include "base/path_service.h"
#include "build/build_config.h"
#include "media/base/djb2.h"
@@ -39,7 +40,8 @@ static const int kRGBSizeScaled = kScaledWidth * kScaledHeight * kBpp;
static const int kRGB24Size = kSourceYSize * 3;
static const int kRGBSizeConverted = kSourceYSize * kBpp;
-#if !defined(ARCH_CPU_ARM_FAMILY) && !defined(ARCH_CPU_MIPS_FAMILY)
+#if !defined(ARCH_CPU_ARM_FAMILY) && !defined(ARCH_CPU_MIPS_FAMILY) && \
+ !defined(OS_ANDROID)
static const int kSourceAOffset = kSourceYSize * 12 / 8;
static const int kYUVA12Size = kSourceYSize * 20 / 8;
#endif