diff options
author | acleung <acleung@chromium.org> | 2015-12-14 16:23:57 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-12-15 00:24:44 +0000 |
commit | 04baa92bc02e36182c623632f831c1b3411e8434 (patch) | |
tree | 1321a672cac2465182835aa34ac35377f6f9354f | |
parent | 80b21c78d9b898185a0da875a95079bfb392865f (diff) | |
download | chromium_src-04baa92bc02e36182c623632f831c1b3411e8434.zip chromium_src-04baa92bc02e36182c623632f831c1b3411e8434.tar.gz chromium_src-04baa92bc02e36182c623632f831c1b3411e8434.tar.bz2 |
Revert of Refactor MediaCodecBridge to add support for NDK APIs (patchset #5 id:100001 of https://codereview.chromium.org/1472943002/ )
Reason for revert:
This breaks 64bit ARM
https://code.google.com/p/chromium/issues/detail?id=568923
Original issue's description:
> Refactor MediaCodecBridge to add support for NDK APIs
>
> Currently MediaCodecBridge is implemented with android MediaCodec SDK API.
> On android-21, MediaCodec NDK APIs have been added.
> This CL makes MediaCodecBridge a common ancestor inherited by the
> SDK and NDK implementation.
> Some common java functions are moved to MediaCodecUtil.java.
> The original native MediaCodecBridge class is renamed to
> SdkMediaCodecBridge.
> The NdkMediaCodecBridge is still WIP.
>
> BUG=560451
>
> Committed: https://crrev.com/5d52e0f8e63a294179df028a0dabc76a042cf9ec
> Cr-Commit-Position: refs/heads/master@{#363374}
TBR=timav@chromium.org,xhwang@chromium.org,sievers@chromium.org,qinmin@chromium.org
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
BUG=560451
Review URL: https://codereview.chromium.org/1525003002
Cr-Commit-Position: refs/heads/master@{#365131}
33 files changed, 1592 insertions, 2127 deletions
diff --git a/components/cdm/browser/cdm_message_filter_android.cc b/components/cdm/browser/cdm_message_filter_android.cc index 14043e1..e9399f6 100644 --- a/components/cdm/browser/cdm_message_filter_android.cc +++ b/components/cdm/browser/cdm_message_filter_android.cc @@ -9,10 +9,11 @@ #include "components/cdm/common/cdm_messages_android.h" #include "ipc/ipc_message_macros.h" -#include "media/base/android/media_codec_util.h" +#include "media/base/android/media_codec_bridge.h" #include "media/base/android/media_drm_bridge.h" using content::BrowserThread; +using media::MediaCodecBridge; using media::MediaDrmBridge; using media::SupportedCodecs; @@ -58,7 +59,7 @@ static SupportedCodecs GetSupportedCodecs( if ((request.codecs & info.codec) && MediaDrmBridge::IsKeySystemSupportedWithType( key_system, info.container_mime_type) && - media::MediaCodecUtil::CanDecode(info.codec_name, is_secure)) { + MediaCodecBridge::CanDecode(info.codec_name, is_secure)) { supported_codecs |= info.codec; } } diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc index 3a8bffa..274a902 100644 --- a/content/child/runtime_features.cc +++ b/content/child/runtime_features.cc @@ -19,7 +19,7 @@ #include <cpu-features.h> #include "base/android/build_info.h" #include "base/metrics/field_trial.h" -#include "media/base/android/media_codec_util.h" +#include "media/base/android/media_codec_bridge.h" #elif defined(OS_WIN) #include "base/win/windows_version.h" #endif @@ -34,7 +34,7 @@ static void SetRuntimeFeatureDefaultsForPlatform() { #if defined(OS_ANDROID) // MSE/EME implementation needs Android MediaCodec API. - if (!media::MediaCodecUtil::IsMediaCodecAvailable()) { + if (!media::MediaCodecBridge::IsAvailable()) { WebRuntimeFeatures::enableMediaSource(false); WebRuntimeFeatures::enablePrefixedEncryptedMedia(false); WebRuntimeFeatures::enableEncryptedMedia(false); @@ -43,7 +43,7 @@ static void SetRuntimeFeatureDefaultsForPlatform() { // is available. AndroidCpuFamily cpu_family = android_getCpuFamily(); WebRuntimeFeatures::enableWebAudio( - media::MediaCodecUtil::IsMediaCodecAvailable() && + media::MediaCodecBridge::IsAvailable() && ((cpu_family == ANDROID_CPU_FAMILY_ARM) || (cpu_family == ANDROID_CPU_FAMILY_ARM64) || (cpu_family == ANDROID_CPU_FAMILY_X86) || @@ -121,7 +121,7 @@ void SetRuntimeFeaturesDefaultsAndUpdateFromArgs( // API is available. WebRuntimeFeatures::enableWebAudio( !command_line.HasSwitch(switches::kDisableWebAudio) && - media::MediaCodecUtil::IsMediaCodecAvailable()); + media::MediaCodecBridge::IsAvailable()); #else if (command_line.HasSwitch(switches::kDisableWebAudio)) WebRuntimeFeatures::enableWebAudio(false); diff --git a/content/common/gpu/media/android_video_decode_accelerator.h b/content/common/gpu/media/android_video_decode_accelerator.h index 6221ad9..e79f0c0 100644 --- a/content/common/gpu/media/android_video_decode_accelerator.h +++ b/content/common/gpu/media/android_video_decode_accelerator.h @@ -17,7 +17,7 @@ #include "content/common/content_export.h" #include "content/common/gpu/media/avda_state_provider.h" #include "gpu/command_buffer/service/gles2_cmd_decoder.h" -#include "media/base/android/sdk_media_codec_bridge.h" +#include "media/base/android/media_codec_bridge.h" #include "media/video/video_decode_accelerator.h" namespace gfx { diff --git a/content/common/gpu/media/android_video_decode_accelerator_unittest.cc b/content/common/gpu/media/android_video_decode_accelerator_unittest.cc index 400d7cc..5ad3f2c 100644 --- a/content/common/gpu/media/android_video_decode_accelerator_unittest.cc +++ b/content/common/gpu/media/android_video_decode_accelerator_unittest.cc @@ -12,7 +12,7 @@ #include "content/common/gpu/media/android_copying_backing_strategy.h" #include "content/common/gpu/media/android_video_decode_accelerator.h" #include "gpu/command_buffer/service/gles2_cmd_decoder_mock.h" -#include "media/base/android/media_codec_util.h" +#include "media/base/android/media_codec_bridge.h" #include "media/base/android/media_jni_registrar.h" #include "media/video/picture.h" #include "media/video/video_decode_accelerator.h" @@ -91,7 +91,7 @@ TEST_F(AndroidVideoDecodeAcceleratorTest, ConfigureUnsupportedCodec) { } TEST_F(AndroidVideoDecodeAcceleratorTest, ConfigureSupportedCodec) { - if (!media::MediaCodecUtil::IsMediaCodecAvailable()) + if (!media::MediaCodecBridge::IsAvailable()) return; EXPECT_TRUE(Configure(media::kCodecVP8)); } diff --git a/content/common/gpu/media/android_video_encode_accelerator.cc b/content/common/gpu/media/android_video_encode_accelerator.cc index 72689c9..06931bf 100644 --- a/content/common/gpu/media/android_video_encode_accelerator.cc +++ b/content/common/gpu/media/android_video_encode_accelerator.cc @@ -14,7 +14,7 @@ #include "content/common/gpu/gpu_channel.h" #include "content/public/common/content_switches.h" #include "gpu/command_buffer/service/gles2_cmd_decoder.h" -#include "media/base/android/media_codec_util.h" +#include "media/base/android/media_codec_bridge.h" #include "media/base/bitstream_buffer.h" #include "media/base/limits.h" #include "media/video/picture.h" @@ -22,6 +22,7 @@ #include "ui/gl/android/scoped_java_surface.h" #include "ui/gl/gl_bindings.h" +using media::MediaCodecBridge; using media::VideoCodecBridge; using media::VideoFrame; @@ -82,7 +83,7 @@ static bool GetSupportedColorFormatForMime(const std::string& mime, if (mime.empty()) return false; - std::set<int> formats = media::MediaCodecUtil::GetEncoderColorFormats(mime); + std::set<int> formats = MediaCodecBridge::GetEncoderColorFormats(mime); if (formats.count(COLOR_FORMAT_YUV420_SEMIPLANAR) > 0) *pixel_format = COLOR_FORMAT_YUV420_SEMIPLANAR; else if (formats.count(COLOR_FORMAT_YUV420_PLANAR) > 0) @@ -156,8 +157,8 @@ bool AndroidVideoEncodeAccelerator::Initialize( client_ptr_factory_.reset(new base::WeakPtrFactory<Client>(client)); - if (!(media::MediaCodecUtil::SupportsSetParameters() && - format == media::PIXEL_FORMAT_I420)) { + if (!(media::MediaCodecBridge::SupportsSetParameters() && + format == media::PIXEL_FORMAT_I420)) { DLOG(ERROR) << "Unexpected combo: " << format << ", " << output_profile; return false; } diff --git a/content/common/gpu/media/android_video_encode_accelerator.h b/content/common/gpu/media/android_video_encode_accelerator.h index 1528726..6608b87 100644 --- a/content/common/gpu/media/android_video_encode_accelerator.h +++ b/content/common/gpu/media/android_video_encode_accelerator.h @@ -16,7 +16,7 @@ #include "base/timer/timer.h" #include "base/tuple.h" #include "content/common/content_export.h" -#include "media/base/android/sdk_media_codec_bridge.h" +#include "media/base/android/media_codec_bridge.h" #include "media/video/video_encode_accelerator.h" namespace media { diff --git a/content/common/gpu/media/avda_shared_state.h b/content/common/gpu/media/avda_shared_state.h index 474037d..a64bf3a 100644 --- a/content/common/gpu/media/avda_shared_state.h +++ b/content/common/gpu/media/avda_shared_state.h @@ -6,7 +6,7 @@ #define CONTENT_COMMON_GPU_AVDA_SHARED_STATE_H_ #include "gpu/command_buffer/service/gles2_cmd_decoder.h" -#include "media/base/android/sdk_media_codec_bridge.h" +#include "media/base/android/media_codec_bridge.h" #include "ui/gl/gl_image.h" namespace gfx { diff --git a/content/renderer/media/webrtc/peer_connection_dependency_factory.cc b/content/renderer/media/webrtc/peer_connection_dependency_factory.cc index a7b373e..ac524a6 100644 --- a/content/renderer/media/webrtc/peer_connection_dependency_factory.cc +++ b/content/renderer/media/webrtc/peer_connection_dependency_factory.cc @@ -63,7 +63,7 @@ #include "third_party/webrtc/base/ssladapter.h" #if defined(OS_ANDROID) -#include "media/base/android/media_codec_util.h" +#include "media/base/android/media_codec_bridge.h" #endif namespace content { @@ -335,7 +335,7 @@ void PeerConnectionDependencyFactory::InitializeSignalingThread( } #if defined(OS_ANDROID) - if (!media::MediaCodecUtil::SupportsSetParameters()) + if (!media::MediaCodecBridge::SupportsSetParameters()) encoder_factory.reset(); #endif diff --git a/media/BUILD.gn b/media/BUILD.gn index 746437c..1c2a73f 100644 --- a/media/BUILD.gn +++ b/media/BUILD.gn @@ -396,15 +396,6 @@ component("media") { "//media/base/android:video_capture_jni_headers", ] - # Only 64 bit builds are using android-21 NDK library, check common.gypi - if (current_cpu == "arm64" || current_cpu == "x64" || - current_cpu == "mips64el") { - sources += [ - "base/android/ndk_media_codec_bridge.cc", - "base/android/ndk_media_codec_bridge.h", - ] - libs += [ "mediandk" ] - } allow_circular_includes_from = [ "//media/base/android" ] } diff --git a/media/base/android/BUILD.gn b/media/base/android/BUILD.gn index 75c6717..c51ee6b 100644 --- a/media/base/android/BUILD.gn +++ b/media/base/android/BUILD.gn @@ -31,8 +31,6 @@ source_set("android") { "media_codec_decoder.h", "media_codec_player.cc", "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", @@ -60,8 +58,6 @@ source_set("android") { "media_task_runner.h", "media_url_interceptor.h", "provision_fetcher.h", - "sdk_media_codec_bridge.cc", - "sdk_media_codec_bridge.h", "video_decoder_job.cc", "video_decoder_job.h", "webaudio_media_codec_bridge.cc", @@ -85,12 +81,12 @@ source_set("unittests") { testonly = true sources = [ "access_unit_queue_unittest.cc", + "media_codec_bridge_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", - "sdk_media_codec_bridge_unittest.cc", "test_data_factory.cc", "test_data_factory.h", "test_statistics.h", @@ -110,7 +106,6 @@ generate_jni("media_jni_headers") { "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/media/base/android/audio_decoder_job.cc b/media/base/android/audio_decoder_job.cc index 3cf2c5f..66c6a60 100644 --- a/media/base/android/audio_decoder_job.cc +++ b/media/base/android/audio_decoder_job.cc @@ -7,7 +7,7 @@ #include "base/bind.h" #include "base/lazy_instance.h" #include "base/threading/thread.h" -#include "media/base/android/sdk_media_codec_bridge.h" +#include "media/base/android/media_codec_bridge.h" #include "media/base/audio_timestamp_helper.h" #include "media/base/timestamp_constants.h" @@ -161,12 +161,10 @@ MediaDecoderJob::MediaDecoderJobStatus if (!media_codec_bridge_) return STATUS_FAILURE; - if (!(static_cast<AudioCodecBridge*>(media_codec_bridge_.get())) - ->ConfigureAndStart(audio_codec_, config_sampling_rate_, - num_channels_, &audio_extra_data_[0], - audio_extra_data_.size(), audio_codec_delay_ns_, - audio_seek_preroll_ns_, true, - GetMediaCrypto())) { + if (!(static_cast<AudioCodecBridge*>(media_codec_bridge_.get()))->Start( + audio_codec_, config_sampling_rate_, num_channels_, &audio_extra_data_[0], + audio_extra_data_.size(), audio_codec_delay_ns_, audio_seek_preroll_ns_, + true, GetMediaCrypto())) { media_codec_bridge_.reset(); return STATUS_FAILURE; } diff --git a/media/base/android/java/src/org/chromium/media/MediaCodecBridge.java b/media/base/android/java/src/org/chromium/media/MediaCodecBridge.java index 3a62582..72c3964 100644 --- a/media/base/android/java/src/org/chromium/media/MediaCodecBridge.java +++ b/media/base/android/java/src/org/chromium/media/MediaCodecBridge.java @@ -22,6 +22,9 @@ import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNINamespace; import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; /** * A wrapper of the MediaCodec class to facilitate exception capturing and @@ -44,6 +47,10 @@ class MediaCodecBridge { private static final int MEDIA_CODEC_ABORT = 8; private static final int MEDIA_CODEC_ERROR = 9; + // Codec direction. Keep this in sync with media_codec_bridge.h. + private static final int MEDIA_CODEC_DECODER = 0; + private static final int MEDIA_CODEC_ENCODER = 1; + // Max adaptive playback size to be supplied to the decoder. private static final int MAX_ADAPTIVE_PLAYBACK_WIDTH = 1920; private static final int MAX_ADAPTIVE_PLAYBACK_HEIGHT = 1080; @@ -93,6 +100,37 @@ class MediaCodecBridge { } } + /** + * This class represents supported android codec information. + */ + private static class CodecInfo { + private final String mCodecType; // e.g. "video/x-vnd.on2.vp8". + private final String mCodecName; // e.g. "OMX.google.vp8.decoder". + private final int mDirection; + + private CodecInfo(String codecType, String codecName, + int direction) { + mCodecType = codecType; + mCodecName = codecName; + mDirection = direction; + } + + @CalledByNative("CodecInfo") + private String codecType() { + return mCodecType; + } + + @CalledByNative("CodecInfo") + private String codecName() { + return mCodecName; + } + + @CalledByNative("CodecInfo") + private int direction() { + return mDirection; + } + } + private static class DequeueOutputResult { private final int mStatus; private final int mIndex; @@ -142,6 +180,105 @@ class MediaCodecBridge { } } + /** + * Get a list of supported android codec mimes. + */ + @SuppressWarnings("deprecation") + @CalledByNative + private static CodecInfo[] getCodecsInfo() { + // Return the first (highest-priority) codec for each MIME type. + Map<String, CodecInfo> encoderInfoMap = new HashMap<String, CodecInfo>(); + Map<String, CodecInfo> decoderInfoMap = new HashMap<String, CodecInfo>(); + int count = MediaCodecList.getCodecCount(); + for (int i = 0; i < count; ++i) { + MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i); + int direction = + info.isEncoder() ? MEDIA_CODEC_ENCODER : MEDIA_CODEC_DECODER; + String codecString = info.getName(); + String[] supportedTypes = info.getSupportedTypes(); + for (int j = 0; j < supportedTypes.length; ++j) { + Map<String, CodecInfo> map = info.isEncoder() ? encoderInfoMap : decoderInfoMap; + if (!map.containsKey(supportedTypes[j])) { + map.put(supportedTypes[j], new CodecInfo( + supportedTypes[j], codecString, direction)); + } + } + } + ArrayList<CodecInfo> codecInfos = new ArrayList<CodecInfo>( + decoderInfoMap.size() + encoderInfoMap.size()); + codecInfos.addAll(encoderInfoMap.values()); + codecInfos.addAll(decoderInfoMap.values()); + return codecInfos.toArray(new CodecInfo[codecInfos.size()]); + } + + /** + * Get a name of default android codec. + */ + @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) + @SuppressWarnings("deprecation") + @CalledByNative + private static String getDefaultCodecName(String mime, int direction) { + String codecName = ""; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + try { + MediaCodec mediaCodec = null; + if (direction == MEDIA_CODEC_ENCODER) { + mediaCodec = MediaCodec.createEncoderByType(mime); + } else { + mediaCodec = MediaCodec.createDecoderByType(mime); + } + codecName = mediaCodec.getName(); + mediaCodec.release(); + } catch (Exception e) { + Log.w(TAG, "getDefaultCodecName: Failed to create MediaCodec: %s, direction: %d", + mime, direction, e); + } + } + return codecName; + } + + /** + * Get a list of encoder supported color formats for specified mime type. + */ + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + @SuppressWarnings("deprecation") + @CalledByNative + private static int[] getEncoderColorFormatsForMime(String mime) { + MediaCodecInfo[] codecs = null; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + MediaCodecList mediaCodecList = new MediaCodecList(MediaCodecList.ALL_CODECS); + codecs = mediaCodecList.getCodecInfos(); + } else { + int count = MediaCodecList.getCodecCount(); + if (count <= 0) { + return null; + } + codecs = new MediaCodecInfo[count]; + for (int i = 0; i < count; ++i) { + MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i); + codecs[i] = info; + } + } + + for (int i = 0; i < codecs.length; i++) { + if (!codecs[i].isEncoder()) { + continue; + } + + String[] supportedTypes = codecs[i].getSupportedTypes(); + for (int j = 0; j < supportedTypes.length; ++j) { + if (!supportedTypes[j].equalsIgnoreCase(mime)) { + continue; + } + + MediaCodecInfo.CodecCapabilities capabilities = + codecs[i].getCapabilitiesForType(mime); + return capabilities.colorFormats; + } + } + return null; + } + @SuppressWarnings("deprecation") private static String getDecoderNameForMime(String mime) { int count = MediaCodecList.getCodecCount(); @@ -184,9 +321,8 @@ class MediaCodecBridge { boolean adaptivePlaybackSupported = false; try { // |isSecure| only applies to video decoders. - if (mime.startsWith("video") && isSecure - && direction == MediaCodecUtil.MEDIA_CODEC_DECODER) { - String decoderName = MediaCodecUtil.getDecoderNameForMime(mime); + if (mime.startsWith("video") && isSecure && direction == MEDIA_CODEC_DECODER) { + String decoderName = getDecoderNameForMime(mime); if (decoderName == null) { return null; } @@ -200,7 +336,7 @@ class MediaCodecBridge { } mediaCodec = MediaCodec.createByCodecName(decoderName + ".secure"); } else { - if (direction == MediaCodecUtil.MEDIA_CODEC_ENCODER) { + if (direction == MEDIA_CODEC_ENCODER) { mediaCodec = MediaCodec.createEncoderByType(mime); } else { mediaCodec = MediaCodec.createDecoderByType(mime); diff --git a/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java b/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java deleted file mode 100644 index b498b29..0000000 --- a/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java +++ /dev/null @@ -1,231 +0,0 @@ -// 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. - -package org.chromium.media; - -import android.annotation.TargetApi; -import android.media.MediaCodec; -import android.media.MediaCodecInfo; -import android.media.MediaCodecList; -import android.os.Build; - -import org.chromium.base.Log; -import org.chromium.base.annotations.CalledByNative; -import org.chromium.base.annotations.JNINamespace; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; - -/** - * A collection of MediaCodec utility functions. - */ -@JNINamespace("media") -class MediaCodecUtil { - private static final String TAG = "MediaCodecUtil"; - - // Codec direction. Keep this in sync with media_codec_bridge.h. - static final int MEDIA_CODEC_DECODER = 0; - static final int MEDIA_CODEC_ENCODER = 1; - - /** - * This class represents supported android codec information. - */ - private static class CodecInfo { - private final String mCodecType; // e.g. "video/x-vnd.on2.vp8". - private final String mCodecName; // e.g. "OMX.google.vp8.decoder". - private final int mDirection; - - private CodecInfo(String codecType, String codecName, int direction) { - mCodecType = codecType; - mCodecName = codecName; - mDirection = direction; - } - - @CalledByNative("CodecInfo") - private String codecType() { - return mCodecType; - } - - @CalledByNative("CodecInfo") - private String codecName() { - return mCodecName; - } - - @CalledByNative("CodecInfo") - private int direction() { - return mDirection; - } - } - - /** - * @return a list of supported android codec information. - */ - @SuppressWarnings("deprecation") - @CalledByNative - private static CodecInfo[] getCodecsInfo() { - // Return the first (highest-priority) codec for each MIME type. - Map<String, CodecInfo> encoderInfoMap = new HashMap<String, CodecInfo>(); - Map<String, CodecInfo> decoderInfoMap = new HashMap<String, CodecInfo>(); - int count = MediaCodecList.getCodecCount(); - for (int i = 0; i < count; ++i) { - MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i); - int direction = info.isEncoder() ? MEDIA_CODEC_ENCODER : MEDIA_CODEC_DECODER; - String codecString = info.getName(); - String[] supportedTypes = info.getSupportedTypes(); - for (int j = 0; j < supportedTypes.length; ++j) { - Map<String, CodecInfo> map = info.isEncoder() ? encoderInfoMap : decoderInfoMap; - if (!map.containsKey(supportedTypes[j])) { - map.put(supportedTypes[j], - new CodecInfo(supportedTypes[j], codecString, direction)); - } - } - } - ArrayList<CodecInfo> codecInfos = - new ArrayList<CodecInfo>(decoderInfoMap.size() + encoderInfoMap.size()); - codecInfos.addAll(encoderInfoMap.values()); - codecInfos.addAll(decoderInfoMap.values()); - return codecInfos.toArray(new CodecInfo[codecInfos.size()]); - } - - /** - * Get a name of default android codec. - * @param mime MIME type of the media. - * @param direction Whether this is encoder or decoder. - * @return name of the codec. - */ - @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) - @SuppressWarnings("deprecation") - @CalledByNative - private static String getDefaultCodecName(String mime, int direction) { - String codecName = ""; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { - try { - MediaCodec mediaCodec = null; - if (direction == MEDIA_CODEC_ENCODER) { - mediaCodec = MediaCodec.createEncoderByType(mime); - } else { - mediaCodec = MediaCodec.createDecoderByType(mime); - } - codecName = mediaCodec.getName(); - mediaCodec.release(); - } catch (Exception e) { - Log.w(TAG, "getDefaultCodecName: Failed to create MediaCodec: %s, direction: %d", - mime, direction, e); - } - } - return codecName; - } - - /** - * Get a list of encoder supported color formats for specified MIME type. - * @param mime MIME type of the media format. - * @return a list of encoder supported color formats. - */ - @TargetApi(Build.VERSION_CODES.LOLLIPOP) - @SuppressWarnings("deprecation") - @CalledByNative - private static int[] getEncoderColorFormatsForMime(String mime) { - MediaCodecInfo[] codecs = null; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - MediaCodecList mediaCodecList = new MediaCodecList(MediaCodecList.ALL_CODECS); - codecs = mediaCodecList.getCodecInfos(); - } else { - int count = MediaCodecList.getCodecCount(); - if (count <= 0) { - return null; - } - codecs = new MediaCodecInfo[count]; - for (int i = 0; i < count; ++i) { - MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i); - codecs[i] = info; - } - } - - for (int i = 0; i < codecs.length; i++) { - if (!codecs[i].isEncoder()) { - continue; - } - - String[] supportedTypes = codecs[i].getSupportedTypes(); - for (int j = 0; j < supportedTypes.length; ++j) { - if (!supportedTypes[j].equalsIgnoreCase(mime)) { - continue; - } - - MediaCodecInfo.CodecCapabilities capabilities = - codecs[i].getCapabilitiesForType(mime); - return capabilities.colorFormats; - } - } - return null; - } - - /** - * Get decoder name for the input MIME type. - * @param mime MIME type of the media. - * @return name of the decoder. - */ - @SuppressWarnings("deprecation") - protected static String getDecoderNameForMime(String mime) { - int count = MediaCodecList.getCodecCount(); - for (int i = 0; i < count; ++i) { - MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i); - if (info.isEncoder()) { - continue; - } - - String[] supportedTypes = info.getSupportedTypes(); - for (int j = 0; j < supportedTypes.length; ++j) { - if (supportedTypes[j].equalsIgnoreCase(mime)) { - return info.getName(); - } - } - } - return null; - } - - /** - * Check if a given MIME type can be decoded. - * @param mime MIME type of the media. - * @param secure Whether secure decoder is required. - * @return true if system is able to decode, or false otherwise. - */ - @CalledByNative - private static boolean canDecode(String mime, boolean isSecure) { - // Creation of ".secure" codecs sometimes crash instead of throwing exceptions - // on pre-JBMR2 devices. - if (isSecure && Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) { - return false; - } - MediaCodec mediaCodec = null; - try { - // |isSecure| only applies to video decoders. - if (mime.startsWith("video") && isSecure) { - String decoderName = getDecoderNameForMime(mime); - if (decoderName == null) return false; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - // To work around an issue that we cannot get the codec info from the secure - // decoder, create an insecure decoder first so that we can query its codec - // info. http://b/15587335. - MediaCodec insecureCodec = MediaCodec.createByCodecName(decoderName); - insecureCodec.release(); - } - mediaCodec = MediaCodec.createByCodecName(decoderName + ".secure"); - } else { - mediaCodec = MediaCodec.createDecoderByType(mime); - } - } catch (Exception e) { - Log.e(TAG, "Failed to create MediaCodec: %s, isSecure: %s", mime, isSecure, e); - } - - if (mediaCodec == null) return false; - try { - mediaCodec.release(); - } catch (IllegalStateException e) { - Log.e(TAG, "Cannot release media codec", e); - } - return true; - } -} diff --git a/media/base/android/media_codec_audio_decoder.cc b/media/base/android/media_codec_audio_decoder.cc index f68a07e..4b8a076 100644 --- a/media/base/android/media_codec_audio_decoder.cc +++ b/media/base/android/media_codec_audio_decoder.cc @@ -6,8 +6,8 @@ #include "base/bind.h" #include "base/logging.h" +#include "media/base/android/media_codec_bridge.h" #include "media/base/android/media_statistics.h" -#include "media/base/android/sdk_media_codec_bridge.h" #include "media/base/audio_timestamp_helper.h" #include "media/base/demuxer_stream.h" @@ -144,11 +144,16 @@ MediaCodecDecoder::ConfigStatus MediaCodecAudioDecoder::ConfigureInternal( return kConfigFailure; if (!(static_cast<AudioCodecBridge*>(media_codec_bridge_.get())) - ->ConfigureAndStart( - configs_.audio_codec, configs_.audio_sampling_rate, - configs_.audio_channels, &configs_.audio_extra_data[0], - configs_.audio_extra_data.size(), configs_.audio_codec_delay_ns, - configs_.audio_seek_preroll_ns, true, media_crypto)) { + ->Start( + configs_.audio_codec, + configs_.audio_sampling_rate, + configs_.audio_channels, + &configs_.audio_extra_data[0], + configs_.audio_extra_data.size(), + configs_.audio_codec_delay_ns, + configs_.audio_seek_preroll_ns, + true, + media_crypto)) { DVLOG(0) << class_name() << "::" << __FUNCTION__ << " failed: cannot start audio codec"; diff --git a/media/base/android/media_codec_bridge.cc b/media/base/android/media_codec_bridge.cc index 6ca7e48..9acec21 100644 --- a/media/base/android/media_codec_bridge.cc +++ b/media/base/android/media_codec_bridge.cc @@ -5,13 +5,20 @@ #include "media/base/android/media_codec_bridge.h" #include <algorithm> +#include <limits> #include "base/android/build_info.h" #include "base/android/jni_android.h" #include "base/android/jni_array.h" #include "base/android/jni_string.h" +#include "base/basictypes.h" +#include "base/lazy_instance.h" #include "base/logging.h" +#include "base/numerics/safe_conversions.h" #include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "jni/MediaCodecBridge_jni.h" +#include "media/base/bit_reader.h" #include "media/base/decrypt_config.h" using base::android::AttachCurrentThread; @@ -20,11 +27,321 @@ using base::android::ConvertUTF8ToJavaString; using base::android::JavaIntArrayToIntVector; using base::android::ScopedJavaLocalRef; +#define RETURN_ON_ERROR(condition) \ + do { \ + if (!(condition)) { \ + LOG(ERROR) << "Unable to parse AAC header: " #condition; \ + return false; \ + } \ + } while (0) + namespace media { -MediaCodecBridge::MediaCodecBridge() {} +enum { + kBufferFlagSyncFrame = 1, // BUFFER_FLAG_SYNC_FRAME + kBufferFlagEndOfStream = 4, // BUFFER_FLAG_END_OF_STREAM + kConfigureFlagEncode = 1, // CONFIGURE_FLAG_ENCODE +}; + +static const std::string AudioCodecToAndroidMimeType(const AudioCodec& codec) { + switch (codec) { + case kCodecMP3: + return "audio/mpeg"; + case kCodecVorbis: + return "audio/vorbis"; + case kCodecOpus: + return "audio/opus"; + case kCodecAAC: + return "audio/mp4a-latm"; + default: + return std::string(); + } +} + +static const std::string VideoCodecToAndroidMimeType(const VideoCodec& codec) { + switch (codec) { + case kCodecH264: + return "video/avc"; + case kCodecHEVC: + return "video/hevc"; + case kCodecVP8: + return "video/x-vnd.on2.vp8"; + case kCodecVP9: + return "video/x-vnd.on2.vp9"; + default: + return std::string(); + } +} + +static const 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"; + if (codec == "hvc1") + return "video/hevc"; + if (codec == "mp4a") + return "audio/mp4a-latm"; + if (codec == "vp8" || codec == "vp8.0") + return "video/x-vnd.on2.vp8"; + if (codec == "vp9" || codec == "vp9.0") + return "video/x-vnd.on2.vp9"; + if (codec == "vorbis") + return "audio/vorbis"; + if (codec == "opus") + return "audio/opus"; + return std::string(); +} + +// TODO(qinmin): using a map to help all the conversions in this class. +static 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(); +} + +static ScopedJavaLocalRef<jintArray> +ToJavaIntArray(JNIEnv* env, scoped_ptr<jint[]> native_array, int size) { + ScopedJavaLocalRef<jintArray> j_array(env, env->NewIntArray(size)); + env->SetIntArrayRegion(j_array.obj(), 0, size, native_array.get()); + return j_array; +} + +// static +bool MediaCodecBridge::IsAvailable() { + // MediaCodec is only available on JB and greater. + if (base::android::BuildInfo::GetInstance()->sdk_int() < 16) + return false; + // Blacklist some devices on Jellybean as for MediaCodec support is buggy. + // http://crbug.com/365494. + if (base::android::BuildInfo::GetInstance()->sdk_int() == 16) { + std::string model(base::android::BuildInfo::GetInstance()->model()); + return model != "GT-I9100" && model != "GT-I9300" && model != "GT-N7000"; + } + return true; +} + +// static +bool MediaCodecBridge::SupportsSetParameters() { + // MediaCodec.setParameters() is only available starting with K. + return base::android::BuildInfo::GetInstance()->sdk_int() >= 19; +} + +// static +bool MediaCodecBridge::SupportsGetName() { + // MediaCodec.getName() is only available on JB MR2 and greater. + return base::android::BuildInfo::GetInstance()->sdk_int() >= 18; +} + +// static +std::vector<MediaCodecBridge::CodecsInfo> MediaCodecBridge::GetCodecsInfo() { + std::vector<CodecsInfo> codecs_info; + if (!IsAvailable()) + return codecs_info; + + JNIEnv* env = AttachCurrentThread(); + std::string mime_type; + ScopedJavaLocalRef<jobjectArray> j_codec_info_array = + Java_MediaCodecBridge_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; +} + +// static +std::string MediaCodecBridge::GetDefaultCodecName( + const std::string& mime_type, + MediaCodecDirection direction) { + if (!IsAvailable()) + return std::string(); -MediaCodecBridge::~MediaCodecBridge() {} + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef<jstring> j_mime = ConvertUTF8ToJavaString(env, mime_type); + ScopedJavaLocalRef<jstring> j_codec_name = + Java_MediaCodecBridge_getDefaultCodecName(env, j_mime.obj(), direction); + return ConvertJavaStringToUTF8(env, j_codec_name.obj()); +} + +// static +std::set<int> MediaCodecBridge::GetEncoderColorFormats( + const std::string& mime_type) { + std::set<int> color_formats; + if (!IsAvailable()) + return color_formats; + + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef<jstring> j_mime = ConvertUTF8ToJavaString(env, mime_type); + ScopedJavaLocalRef<jintArray> j_color_format_array = + Java_MediaCodecBridge_getEncoderColorFormatsForMime(env, j_mime.obj()); + + if (j_color_format_array.obj()) { + std::vector<int> formats; + JavaIntArrayToIntVector(env, j_color_format_array.obj(), &formats); + color_formats = std::set<int>(formats.begin(), formats.end()); + } + + return color_formats; +} + +// static +bool MediaCodecBridge::CanDecode(const std::string& codec, bool is_secure) { + if (!IsAvailable()) + return false; + + JNIEnv* env = AttachCurrentThread(); + std::string mime = CodecTypeToAndroidMimeType(codec); + if (mime.empty()) + return false; + ScopedJavaLocalRef<jstring> j_mime = ConvertUTF8ToJavaString(env, mime); + ScopedJavaLocalRef<jobject> j_media_codec_bridge = + Java_MediaCodecBridge_create(env, j_mime.obj(), is_secure, false); + if (!j_media_codec_bridge.is_null()) { + Java_MediaCodecBridge_release(env, j_media_codec_bridge.obj()); + return true; + } + return false; +} + +// static +bool MediaCodecBridge::IsKnownUnaccelerated(const std::string& mime_type, + MediaCodecDirection direction) { + if (!IsAvailable()) + return true; + + std::string codec_name; + if (SupportsGetName()) { + codec_name = GetDefaultCodecName(mime_type, direction); + } else { + std::string codec_type = AndroidMimeTypeToCodecType(mime_type); + std::vector<media::MediaCodecBridge::CodecsInfo> codecs_info = + MediaCodecBridge::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; + } + } + } + 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; +} + +MediaCodecBridge::MediaCodecBridge(const std::string& mime, + bool is_secure, + MediaCodecDirection direction) { + JNIEnv* env = AttachCurrentThread(); + CHECK(env); + DCHECK(!mime.empty()); + ScopedJavaLocalRef<jstring> j_mime = ConvertUTF8ToJavaString(env, mime); + j_media_codec_.Reset( + Java_MediaCodecBridge_create(env, j_mime.obj(), is_secure, direction)); +} + +MediaCodecBridge::~MediaCodecBridge() { + JNIEnv* env = AttachCurrentThread(); + CHECK(env); + if (j_media_codec_.obj()) + Java_MediaCodecBridge_release(env, j_media_codec_.obj()); +} + +bool MediaCodecBridge::StartInternal() { + JNIEnv* env = AttachCurrentThread(); + return Java_MediaCodecBridge_start(env, j_media_codec_.obj()); +} + +MediaCodecStatus MediaCodecBridge::Reset() { + JNIEnv* env = AttachCurrentThread(); + return static_cast<MediaCodecStatus>( + Java_MediaCodecBridge_flush(env, j_media_codec_.obj())); +} + +void MediaCodecBridge::Stop() { + JNIEnv* env = AttachCurrentThread(); + Java_MediaCodecBridge_stop(env, j_media_codec_.obj()); +} + +void MediaCodecBridge::GetOutputFormat(int* width, int* height) { + JNIEnv* env = AttachCurrentThread(); + + *width = Java_MediaCodecBridge_getOutputWidth(env, j_media_codec_.obj()); + *height = Java_MediaCodecBridge_getOutputHeight(env, j_media_codec_.obj()); +} + +int MediaCodecBridge::GetOutputSamplingRate() { + JNIEnv* env = AttachCurrentThread(); + + return Java_MediaCodecBridge_getOutputSamplingRate(env, j_media_codec_.obj()); +} + +MediaCodecStatus MediaCodecBridge::QueueInputBuffer( + int index, + const uint8* data, + size_t data_size, + const base::TimeDelta& presentation_time) { + DVLOG(3) << __PRETTY_FUNCTION__ << index << ": " << data_size; + if (data_size > + base::checked_cast<size_t>(std::numeric_limits<int32_t>::max())) + return MEDIA_CODEC_ERROR; + if (data && !FillInputBuffer(index, data, data_size)) + return MEDIA_CODEC_ERROR; + JNIEnv* env = AttachCurrentThread(); + return static_cast<MediaCodecStatus>( + Java_MediaCodecBridge_queueInputBuffer(env, + j_media_codec_.obj(), + index, + 0, + data_size, + presentation_time.InMicroseconds(), + 0)); +} MediaCodecStatus MediaCodecBridge::QueueSecureInputBuffer( int index, @@ -34,25 +351,205 @@ MediaCodecStatus MediaCodecBridge::QueueSecureInputBuffer( const std::string& iv, const std::vector<SubsampleEntry>& subsamples, const base::TimeDelta& presentation_time) { - const std::vector<char> key_vec(key_id.begin(), key_id.end()); - const std::vector<char> iv_vec(iv.begin(), iv.end()); - return QueueSecureInputBuffer(index, data, data_size, key_vec, iv_vec, - subsamples.empty() ? nullptr : &subsamples[0], - subsamples.size(), presentation_time); + return QueueSecureInputBuffer( + index, data, data_size, reinterpret_cast<const uint8_t*>(key_id.data()), + key_id.size(), reinterpret_cast<const uint8_t*>(iv.data()), iv.size(), + subsamples.empty() ? nullptr : &subsamples[0], subsamples.size(), + presentation_time); +} + +// TODO(timav): Combine this and above methods together keeping only the first +// interface after we switch to Spitzer pipeline. +MediaCodecStatus MediaCodecBridge::QueueSecureInputBuffer( + int index, + const uint8* data, + size_t data_size, + const uint8* key_id, + int key_id_size, + const uint8* iv, + int iv_size, + const SubsampleEntry* subsamples, + int subsamples_size, + const base::TimeDelta& presentation_time) { + DVLOG(3) << __PRETTY_FUNCTION__ << index << ": " << data_size; + if (data_size > + base::checked_cast<size_t>(std::numeric_limits<int32_t>::max())) + return MEDIA_CODEC_ERROR; + if (data && !FillInputBuffer(index, data, data_size)) + return MEDIA_CODEC_ERROR; + + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef<jbyteArray> j_key_id = + base::android::ToJavaByteArray(env, key_id, key_id_size); + ScopedJavaLocalRef<jbyteArray> j_iv = + base::android::ToJavaByteArray(env, iv, iv_size); + + // MediaCodec.CryptoInfo documentations says passing NULL for |clear_array| + // to indicate that all data is encrypted. But it doesn't specify what + // |cypher_array| and |subsamples_size| should be in that case. Passing + // one subsample here just to be on the safe side. + int new_subsamples_size = subsamples_size == 0 ? 1 : subsamples_size; + + scoped_ptr<jint[]> native_clear_array(new jint[new_subsamples_size]); + scoped_ptr<jint[]> native_cypher_array(new jint[new_subsamples_size]); + + if (subsamples_size == 0) { + DCHECK(!subsamples); + native_clear_array[0] = 0; + native_cypher_array[0] = data_size; + } else { + DCHECK_GT(subsamples_size, 0); + DCHECK(subsamples); + for (int i = 0; i < subsamples_size; ++i) { + DCHECK(subsamples[i].clear_bytes <= std::numeric_limits<uint16>::max()); + if (subsamples[i].cypher_bytes > + static_cast<uint32>(std::numeric_limits<jint>::max())) { + return MEDIA_CODEC_ERROR; + } + + native_clear_array[i] = subsamples[i].clear_bytes; + native_cypher_array[i] = subsamples[i].cypher_bytes; + } + } + + ScopedJavaLocalRef<jintArray> clear_array = + ToJavaIntArray(env, native_clear_array.Pass(), new_subsamples_size); + ScopedJavaLocalRef<jintArray> cypher_array = + ToJavaIntArray(env, native_cypher_array.Pass(), new_subsamples_size); + + return static_cast<MediaCodecStatus>( + Java_MediaCodecBridge_queueSecureInputBuffer( + env, + j_media_codec_.obj(), + index, + 0, + j_iv.obj(), + j_key_id.obj(), + clear_array.obj(), + cypher_array.obj(), + new_subsamples_size, + presentation_time.InMicroseconds())); +} + +void MediaCodecBridge::QueueEOS(int input_buffer_index) { + DVLOG(3) << __PRETTY_FUNCTION__ << ": " << input_buffer_index; + JNIEnv* env = AttachCurrentThread(); + Java_MediaCodecBridge_queueInputBuffer(env, + j_media_codec_.obj(), + input_buffer_index, + 0, + 0, + 0, + kBufferFlagEndOfStream); +} + +MediaCodecStatus MediaCodecBridge::DequeueInputBuffer( + const base::TimeDelta& timeout, + int* index) { + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef<jobject> result = Java_MediaCodecBridge_dequeueInputBuffer( + env, j_media_codec_.obj(), timeout.InMicroseconds()); + *index = Java_DequeueInputResult_index(env, result.obj()); + MediaCodecStatus status = static_cast<MediaCodecStatus>( + Java_DequeueInputResult_status(env, result.obj())); + DVLOG(3) << __PRETTY_FUNCTION__ << ": status: " << status + << ", index: " << *index; + return status; +} + +MediaCodecStatus MediaCodecBridge::DequeueOutputBuffer( + const base::TimeDelta& timeout, + int* index, + size_t* offset, + size_t* size, + base::TimeDelta* presentation_time, + bool* end_of_stream, + bool* key_frame) { + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef<jobject> result = + Java_MediaCodecBridge_dequeueOutputBuffer( + env, j_media_codec_.obj(), timeout.InMicroseconds()); + *index = Java_DequeueOutputResult_index(env, result.obj()); + *offset = base::checked_cast<size_t>( + Java_DequeueOutputResult_offset(env, result.obj())); + *size = base::checked_cast<size_t>( + Java_DequeueOutputResult_numBytes(env, result.obj())); + if (presentation_time) { + *presentation_time = base::TimeDelta::FromMicroseconds( + Java_DequeueOutputResult_presentationTimeMicroseconds(env, + result.obj())); + } + int flags = Java_DequeueOutputResult_flags(env, result.obj()); + if (end_of_stream) + *end_of_stream = flags & kBufferFlagEndOfStream; + if (key_frame) + *key_frame = flags & kBufferFlagSyncFrame; + MediaCodecStatus status = static_cast<MediaCodecStatus>( + Java_DequeueOutputResult_status(env, result.obj())); + DVLOG(3) << __PRETTY_FUNCTION__ << ": status: " << status + << ", index: " << *index << ", offset: " << *offset + << ", size: " << *size << ", flags: " << flags; + return status; +} + +void MediaCodecBridge::ReleaseOutputBuffer(int index, bool render) { + DVLOG(3) << __PRETTY_FUNCTION__ << ": " << index; + JNIEnv* env = AttachCurrentThread(); + CHECK(env); + + Java_MediaCodecBridge_releaseOutputBuffer( + env, j_media_codec_.obj(), index, render); } int MediaCodecBridge::GetOutputBuffersCount() { - return 0; + JNIEnv* env = AttachCurrentThread(); + return Java_MediaCodecBridge_getOutputBuffersCount(env, j_media_codec_.obj()); } size_t MediaCodecBridge::GetOutputBuffersCapacity() { - return 0; + JNIEnv* env = AttachCurrentThread(); + return Java_MediaCodecBridge_getOutputBuffersCapacity(env, + j_media_codec_.obj()); +} + +void MediaCodecBridge::GetInputBuffer(int input_buffer_index, + uint8** data, + size_t* capacity) { + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef<jobject> j_buffer(Java_MediaCodecBridge_getInputBuffer( + env, j_media_codec_.obj(), input_buffer_index)); + *data = static_cast<uint8*>(env->GetDirectBufferAddress(j_buffer.obj())); + *capacity = base::checked_cast<size_t>( + env->GetDirectBufferCapacity(j_buffer.obj())); +} + +bool MediaCodecBridge::CopyFromOutputBuffer(int index, + size_t offset, + void* dst, + int dst_size) { + void* src_data = nullptr; + int src_capacity = GetOutputBufferAddress(index, offset, &src_data); + if (src_capacity < dst_size) + return false; + memcpy(dst, src_data, dst_size); + return true; +} + +int MediaCodecBridge::GetOutputBufferAddress(int index, + size_t offset, + void** addr) { + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef<jobject> j_buffer( + Java_MediaCodecBridge_getOutputBuffer(env, j_media_codec_.obj(), index)); + *addr = reinterpret_cast<uint8*>( + env->GetDirectBufferAddress(j_buffer.obj())) + offset; + return env->GetDirectBufferCapacity(j_buffer.obj()) - offset; } bool MediaCodecBridge::FillInputBuffer(int index, const uint8_t* data, size_t size) { - uint8_t* dst = nullptr; + uint8* dst = NULL; size_t capacity = 0; GetInputBuffer(index, &dst, &capacity); CHECK(dst); @@ -67,4 +564,334 @@ bool MediaCodecBridge::FillInputBuffer(int index, return true; } +AudioCodecBridge::AudioCodecBridge(const std::string& mime) + // Audio codec doesn't care about security level and there is no need for + // audio encoding yet. + : MediaCodecBridge(mime, false, MEDIA_CODEC_DECODER) {} + +bool AudioCodecBridge::Start(const AudioCodec& codec, + int sample_rate, + int channel_count, + const uint8* extra_data, + size_t extra_data_size, + int64 codec_delay_ns, + int64 seek_preroll_ns, + bool play_audio, + jobject media_crypto) { + JNIEnv* env = AttachCurrentThread(); + + if (!media_codec()) + return false; + + std::string codec_string = AudioCodecToAndroidMimeType(codec); + if (codec_string.empty()) + return false; + + ScopedJavaLocalRef<jstring> j_mime = + ConvertUTF8ToJavaString(env, codec_string); + ScopedJavaLocalRef<jobject> j_format(Java_MediaCodecBridge_createAudioFormat( + env, j_mime.obj(), sample_rate, channel_count)); + DCHECK(!j_format.is_null()); + + if (!ConfigureMediaFormat(j_format.obj(), codec, extra_data, extra_data_size, + codec_delay_ns, seek_preroll_ns)) { + return false; + } + + if (!Java_MediaCodecBridge_configureAudio( + env, media_codec(), j_format.obj(), media_crypto, 0, play_audio)) { + return false; + } + + return StartInternal(); +} + +bool AudioCodecBridge::ConfigureMediaFormat(jobject j_format, + const AudioCodec& codec, + const uint8* extra_data, + size_t extra_data_size, + int64 codec_delay_ns, + int64 seek_preroll_ns) { + if (extra_data_size == 0 && codec != kCodecOpus) + return true; + + JNIEnv* env = AttachCurrentThread(); + switch (codec) { + case kCodecVorbis: { + if (extra_data[0] != 2) { + LOG(ERROR) << "Invalid number of vorbis headers before the codec " + << "header: " << extra_data[0]; + return false; + } + + size_t header_length[2]; + // |total_length| keeps track of the total number of bytes before the last + // header. + size_t total_length = 1; + const uint8* current_pos = extra_data; + // Calculate the length of the first 2 headers. + for (int i = 0; i < 2; ++i) { + header_length[i] = 0; + while (total_length < extra_data_size) { + size_t size = *(++current_pos); + total_length += 1 + size; + if (total_length > 0x80000000) { + LOG(ERROR) << "Vorbis header size too large"; + return false; + } + header_length[i] += size; + if (size < 0xFF) + break; + } + if (total_length >= extra_data_size) { + LOG(ERROR) << "Invalid vorbis header size in the extra data"; + return false; + } + } + current_pos++; + // The first header is identification header. + ScopedJavaLocalRef<jbyteArray> first_header = + base::android::ToJavaByteArray(env, current_pos, header_length[0]); + Java_MediaCodecBridge_setCodecSpecificData( + env, j_format, 0, first_header.obj()); + // The last header is codec header. + ScopedJavaLocalRef<jbyteArray> last_header = + base::android::ToJavaByteArray( + env, extra_data + total_length, extra_data_size - total_length); + Java_MediaCodecBridge_setCodecSpecificData( + env, j_format, 1, last_header.obj()); + break; + } + case kCodecAAC: { + media::BitReader reader(extra_data, extra_data_size); + + // The following code is copied from aac.cc + // TODO(qinmin): refactor the code in aac.cc to make it more reusable. + uint8 profile = 0; + uint8 frequency_index = 0; + uint8 channel_config = 0; + RETURN_ON_ERROR(reader.ReadBits(5, &profile)); + RETURN_ON_ERROR(reader.ReadBits(4, &frequency_index)); + + if (0xf == frequency_index) + RETURN_ON_ERROR(reader.SkipBits(24)); + RETURN_ON_ERROR(reader.ReadBits(4, &channel_config)); + + if (profile == 5 || profile == 29) { + // Read extension config. + RETURN_ON_ERROR(reader.ReadBits(4, &frequency_index)); + if (frequency_index == 0xf) + RETURN_ON_ERROR(reader.SkipBits(24)); + RETURN_ON_ERROR(reader.ReadBits(5, &profile)); + } + + if (profile < 1 || profile > 4 || frequency_index == 0xf || + channel_config > 7) { + LOG(ERROR) << "Invalid AAC header"; + return false; + } + const size_t kCsdLength = 2; + uint8 csd[kCsdLength]; + csd[0] = profile << 3 | frequency_index >> 1; + csd[1] = (frequency_index & 0x01) << 7 | channel_config << 3; + ScopedJavaLocalRef<jbyteArray> byte_array = + base::android::ToJavaByteArray(env, csd, kCsdLength); + Java_MediaCodecBridge_setCodecSpecificData( + env, j_format, 0, byte_array.obj()); + + // TODO(qinmin): pass an extra variable to this function to determine + // whether we need to call this. + Java_MediaCodecBridge_setFrameHasADTSHeader(env, j_format); + break; + } + case kCodecOpus: { + if (!extra_data || extra_data_size == 0 || + codec_delay_ns < 0 || seek_preroll_ns < 0) { + LOG(ERROR) << "Invalid Opus Header"; + return false; + } + + // csd0 - Opus Header + ScopedJavaLocalRef<jbyteArray> csd0 = + base::android::ToJavaByteArray(env, extra_data, extra_data_size); + Java_MediaCodecBridge_setCodecSpecificData(env, j_format, 0, csd0.obj()); + + // csd1 - Codec Delay + ScopedJavaLocalRef<jbyteArray> csd1 = + base::android::ToJavaByteArray( + env, reinterpret_cast<const uint8*>(&codec_delay_ns), + sizeof(int64_t)); + Java_MediaCodecBridge_setCodecSpecificData(env, j_format, 1, csd1.obj()); + + // csd2 - Seek Preroll + ScopedJavaLocalRef<jbyteArray> csd2 = + base::android::ToJavaByteArray( + env, reinterpret_cast<const uint8*>(&seek_preroll_ns), + sizeof(int64_t)); + Java_MediaCodecBridge_setCodecSpecificData(env, j_format, 2, csd2.obj()); + break; + } + default: + LOG(ERROR) << "Invalid header encountered for codec: " + << AudioCodecToAndroidMimeType(codec); + return false; + } + return true; +} + +int64 AudioCodecBridge::PlayOutputBuffer(int index, + size_t size, + size_t offset, + bool postpone) { + 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); + CHECK_GE(numBytes, 0); + + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef<jbyteArray> byte_array = base::android::ToJavaByteArray( + env, static_cast<uint8*>(buffer), numBytes); + return Java_MediaCodecBridge_playOutputBuffer(env, media_codec(), + byte_array.obj(), postpone); +} + +void AudioCodecBridge::SetVolume(double volume) { + JNIEnv* env = AttachCurrentThread(); + Java_MediaCodecBridge_setVolume(env, media_codec(), volume); +} + +// static +AudioCodecBridge* AudioCodecBridge::Create(const AudioCodec& codec) { + if (!MediaCodecBridge::IsAvailable()) + return NULL; + + const std::string mime = AudioCodecToAndroidMimeType(codec); + return mime.empty() ? NULL : new AudioCodecBridge(mime); +} + +// static +bool AudioCodecBridge::IsKnownUnaccelerated(const AudioCodec& codec) { + return MediaCodecBridge::IsKnownUnaccelerated( + AudioCodecToAndroidMimeType(codec), MEDIA_CODEC_DECODER); +} + +// static +bool VideoCodecBridge::IsKnownUnaccelerated(const VideoCodec& codec, + MediaCodecDirection direction) { + return MediaCodecBridge::IsKnownUnaccelerated( + VideoCodecToAndroidMimeType(codec), direction); +} + +// static +VideoCodecBridge* VideoCodecBridge::CreateDecoder(const VideoCodec& codec, + bool is_secure, + const gfx::Size& size, + jobject surface, + jobject media_crypto) { + if (!MediaCodecBridge::IsAvailable()) + return NULL; + + const std::string mime = VideoCodecToAndroidMimeType(codec); + if (mime.empty()) + return NULL; + + scoped_ptr<VideoCodecBridge> bridge( + new VideoCodecBridge(mime, is_secure, MEDIA_CODEC_DECODER)); + if (!bridge->media_codec()) + return NULL; + + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef<jstring> j_mime = ConvertUTF8ToJavaString(env, mime); + ScopedJavaLocalRef<jobject> j_format( + 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)) { + return NULL; + } + + return bridge->StartInternal() ? bridge.release() : NULL; +} + +// static +VideoCodecBridge* VideoCodecBridge::CreateEncoder(const VideoCodec& codec, + const gfx::Size& size, + int bit_rate, + int frame_rate, + int i_frame_interval, + int color_format) { + if (!MediaCodecBridge::IsAvailable()) + return NULL; + + const std::string mime = VideoCodecToAndroidMimeType(codec); + if (mime.empty()) + return NULL; + + scoped_ptr<VideoCodecBridge> bridge( + new VideoCodecBridge(mime, false, MEDIA_CODEC_ENCODER)); + if (!bridge->media_codec()) + return NULL; + + JNIEnv* env = AttachCurrentThread(); + ScopedJavaLocalRef<jstring> j_mime = ConvertUTF8ToJavaString(env, mime); + ScopedJavaLocalRef<jobject> j_format( + Java_MediaCodecBridge_createVideoEncoderFormat(env, + j_mime.obj(), + size.width(), + size.height(), + bit_rate, + frame_rate, + i_frame_interval, + color_format)); + DCHECK(!j_format.is_null()); + if (!Java_MediaCodecBridge_configureVideo(env, + bridge->media_codec(), + j_format.obj(), + NULL, + NULL, + kConfigureFlagEncode)) { + return NULL; + } + + return bridge->StartInternal() ? bridge.release() : NULL; +} + +VideoCodecBridge::VideoCodecBridge(const std::string& mime, + bool is_secure, + MediaCodecDirection direction) + : MediaCodecBridge(mime, is_secure, direction), + adaptive_playback_supported_for_testing_(-1) {} + +void VideoCodecBridge::SetVideoBitrate(int bps) { + JNIEnv* env = AttachCurrentThread(); + Java_MediaCodecBridge_setVideoBitrate(env, media_codec(), bps); +} + +void VideoCodecBridge::RequestKeyFrameSoon() { + JNIEnv* env = AttachCurrentThread(); + Java_MediaCodecBridge_requestKeyFrameSoon(env, media_codec()); +} + +bool VideoCodecBridge::IsAdaptivePlaybackSupported(int width, int height) { + if (adaptive_playback_supported_for_testing_ == 0) + return false; + else if (adaptive_playback_supported_for_testing_ > 0) + return true; + JNIEnv* env = AttachCurrentThread(); + return Java_MediaCodecBridge_isAdaptivePlaybackSupported( + env, media_codec(), width, height); +} + +bool MediaCodecBridge::RegisterMediaCodecBridge(JNIEnv* env) { + return RegisterNativesImpl(env); +} + } // namespace media diff --git a/media/base/android/media_codec_bridge.h b/media/base/android/media_codec_bridge.h index 02a29f9..45361c88b 100644 --- a/media/base/android/media_codec_bridge.h +++ b/media/base/android/media_codec_bridge.h @@ -5,16 +5,15 @@ #ifndef MEDIA_BASE_ANDROID_MEDIA_CODEC_BRIDGE_H_ #define MEDIA_BASE_ANDROID_MEDIA_CODEC_BRIDGE_H_ -#include <stdint.h> - +#include <jni.h> #include <set> #include <string> -#include <vector> -#include "base/compiler_specific.h" +#include "base/android/scoped_java_ref.h" #include "base/time/time.h" -#include "media/base/android/media_codec_util.h" -#include "media/base/media_export.h" +#include "media/base/audio_decoder_config.h" +#include "media/base/video_decoder_config.h" +#include "ui/gfx/geometry/size.h" namespace media { @@ -35,12 +34,59 @@ enum MediaCodecStatus { MEDIA_CODEC_ERROR }; -// Interface for wrapping different Android MediaCodec implementations. For -// more information on Android MediaCodec, check +// Codec direction. Keep this in sync with MediaCodecBridge.java. +enum MediaCodecDirection { + MEDIA_CODEC_DECODER, + MEDIA_CODEC_ENCODER, +}; + +// This class serves as a bridge for native code to call java functions inside +// Android MediaCodec class. For more information on Android MediaCodec, check // http://developer.android.com/reference/android/media/MediaCodec.html // Note: MediaCodec is only available on JB and greater. +// Use AudioCodecBridge or VideoCodecBridge to create an instance of this +// object. +// +// TODO(fischman,xhwang): replace this (and the enums that go with it) with +// chromium's JNI auto-generation hotness. class MEDIA_EXPORT MediaCodecBridge { public: + // Returns true if MediaCodec is available on the device. + // All other static methods check IsAvailable() internally. There's no need + // to check IsAvailable() explicitly before calling them. + static bool IsAvailable(); + + // Returns true if MediaCodec.setParameters() is available on the device. + static bool SupportsSetParameters(); + + // Returns true if MediaCodec.getName() is available on the device. + static bool SupportsGetName(); + + // Returns whether MediaCodecBridge has a decoder that |is_secure| and can + // decode |codec| type. + static bool CanDecode(const std::string& codec, bool is_secure); + + // 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. + static std::vector<CodecsInfo> GetCodecsInfo(); + + // Get default codec name for |mime_type|. + static std::string GetDefaultCodecName(const std::string& mime_type, + MediaCodecDirection direction); + + // Get a list of encoder supported color formats for |mime_type|. + // The mapping of color format name and its value refers to + // MediaCodecInfo.CodecCapabilities. + static std::set<int> GetEncoderColorFormats(const std::string& mime_type); + virtual ~MediaCodecBridge(); // Resets both input and output, all indices previously returned in calls to @@ -49,36 +95,31 @@ class MEDIA_EXPORT MediaCodecBridge { // words, there will be no outputs until new input is provided. // Returns MEDIA_CODEC_ERROR if an unexpected error happens, or Media_CODEC_OK // otherwise. - virtual MediaCodecStatus Reset() = 0; - - // Calls start() against the media codec instance. Returns whether media - // codec was successfully started. - virtual bool Start() = 0; + MediaCodecStatus Reset(); // Finishes the decode/encode session. The instance remains active // and ready to be StartAudio/Video()ed again. HOWEVER, due to the buggy // vendor's implementation , b/8125974, Stop() -> StartAudio/Video() may not // work on some devices. For reliability, Stop() -> delete and recreate new // instance -> StartAudio/Video() is recommended. - virtual void Stop() = 0; + void Stop(); // 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; + void GetOutputFormat(int* width, int* height); // Used for checking for new sampling rate after DequeueInputBuffer() returns // INFO_OUTPUT_FORMAT_CHANGED - virtual int GetOutputSamplingRate() = 0; + int GetOutputSamplingRate(); // Submits a byte array to the given input buffer. Call this after getting an - // available buffer from DequeueInputBuffer(). If |data| is NULL, assume the + // available buffer from DequeueInputBuffer(). If |data| is NULL, assume the // input buffer has already been populated (but still obey |size|). // |data_size| must be less than kint32max (because Java). - virtual MediaCodecStatus QueueInputBuffer( - int index, - const uint8_t* data, - size_t data_size, - const base::TimeDelta& presentation_time) = 0; + MediaCodecStatus QueueInputBuffer(int index, + const uint8* data, + size_t data_size, + const base::TimeDelta& presentation_time); // Similar to the above call, but submits a buffer that is encrypted. Note: // NULL |subsamples| indicates the whole buffer is encrypted. If |data| is @@ -97,18 +138,20 @@ class MEDIA_EXPORT MediaCodecBridge { // and MediaCodecPlayer. // TODO(timav): remove this method and keep only the one above after we // switch to the Spitzer pipeline. - virtual MediaCodecStatus QueueSecureInputBuffer( + MediaCodecStatus QueueSecureInputBuffer( int index, const uint8_t* data, size_t data_size, - const std::vector<char>& key_id, - const std::vector<char>& iv, + const uint8* key_id, + int key_id_size, + const uint8* iv, + int iv_size, const SubsampleEntry* subsamples, int subsamples_size, - const base::TimeDelta& presentation_time) = 0; + const base::TimeDelta& presentation_time); // Submits an empty buffer with a EOS (END OF STREAM) flag. - virtual void QueueEOS(int input_buffer_index) = 0; + void QueueEOS(int input_buffer_index); // Returns: // MEDIA_CODEC_OK if an input buffer is ready to be filled with valid data, @@ -116,8 +159,8 @@ class MEDIA_EXPORT MediaCodecBridge { // MEDIA_CODEC_ERROR if unexpected error happens. // Note: Never use infinite timeout as this would block the decoder thread and // prevent the decoder job from being released. - virtual MediaCodecStatus DequeueInputBuffer(const base::TimeDelta& timeout, - int* index) = 0; + MediaCodecStatus DequeueInputBuffer(const base::TimeDelta& timeout, + int* index); // Dequeues an output buffer, block at most timeout_us microseconds. // Returns the status of this operation. If OK is returned, the output @@ -128,51 +171,159 @@ class MEDIA_EXPORT MediaCodecBridge { // prevent the decoder job from being released. // TODO(xhwang): Can we drop |end_of_stream| and return // MEDIA_CODEC_OUTPUT_END_OF_STREAM? - virtual MediaCodecStatus DequeueOutputBuffer( - const base::TimeDelta& timeout, - int* index, - size_t* offset, - size_t* size, - base::TimeDelta* presentation_time, - bool* end_of_stream, - bool* key_frame) = 0; + MediaCodecStatus DequeueOutputBuffer(const base::TimeDelta& timeout, + int* index, + size_t* offset, + size_t* size, + base::TimeDelta* presentation_time, + bool* end_of_stream, + bool* key_frame); // Returns the buffer to the codec. If you previously specified a surface when // configuring this video decoder you can optionally render the buffer. - virtual void ReleaseOutputBuffer(int index, bool render) = 0; + void ReleaseOutputBuffer(int index, bool render); // Returns the number of output buffers used by the codec. // TODO(qinmin): this call is deprecated in Lollipop. - virtual int GetOutputBuffersCount(); + 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(); + 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; + void GetInputBuffer(int input_buffer_index, uint8** data, size_t* capacity); // 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; + bool CopyFromOutputBuffer(int index, size_t offset, void* dst, int dst_size); + + static bool RegisterMediaCodecBridge(JNIEnv* env); protected: - MediaCodecBridge(); + // Returns true if |mime_type| is known to be unaccelerated (i.e. backed by a + // software codec instead of a hardware one). + static bool IsKnownUnaccelerated(const std::string& mime_type, + MediaCodecDirection direction); + + MediaCodecBridge(const std::string& mime, + bool is_secure, + MediaCodecDirection direction); + + // Calls start() against the media codec instance. Used in StartXXX() after + // configuring media codec. Returns whether media codec was successfully + // started. + bool StartInternal() WARN_UNUSED_RESULT; + + // 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); + jobject media_codec() { return j_media_codec_.obj(); } + MediaCodecDirection direction_; + + private: // Fills a particular input buffer; returns false if |data_size| exceeds the // input buffer's capacity (and doesn't touch the input buffer in that case). bool FillInputBuffer(int index, const uint8_t* data, size_t data_size) WARN_UNUSED_RESULT; + // Java MediaCodec instance. + base::android::ScopedJavaGlobalRef<jobject> j_media_codec_; + DISALLOW_COPY_AND_ASSIGN(MediaCodecBridge); }; +class AudioCodecBridge : public MediaCodecBridge { + public: + // Returns an AudioCodecBridge instance if |codec| is supported, or a NULL + // pointer otherwise. + static AudioCodecBridge* Create(const AudioCodec& codec); + + // See MediaCodecBridge::IsKnownUnaccelerated(). + static bool IsKnownUnaccelerated(const AudioCodec& codec); + + // Start the audio codec bridge. + bool Start(const AudioCodec& codec, int sample_rate, int channel_count, + const uint8* extra_data, size_t extra_data_size, + int64 codec_delay_ns, int64 seek_preroll_ns, + bool play_audio, jobject media_crypto) WARN_UNUSED_RESULT; + + // 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. + // 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 PlayOutputBuffer(int index, + size_t size, + size_t offset, + bool postpone = false); + + // Set the volume of the audio output. + void SetVolume(double volume); + + private: + explicit AudioCodecBridge(const std::string& mime); + + // Configure the java MediaFormat object with the extra codec data passed in. + bool ConfigureMediaFormat(jobject j_format, const AudioCodec& codec, + const uint8* extra_data, size_t extra_data_size, + int64 codec_delay_ns, int64 seek_preroll_ns); +}; + +class MEDIA_EXPORT VideoCodecBridge : public MediaCodecBridge { + public: + // See MediaCodecBridge::IsKnownUnaccelerated(). + static bool IsKnownUnaccelerated(const VideoCodec& codec, + MediaCodecDirection direction); + + // 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. + + // Create, start, and return a VideoCodecBridge encoder or NULL on failure. + static VideoCodecBridge* CreateEncoder( + const VideoCodec& codec, // e.g. media::kCodecVP8 + const gfx::Size& size, // input frame size + int bit_rate, // bits/second + int frame_rate, // frames/second + int i_frame_interval, // count + int color_format); // MediaCodecInfo.CodecCapabilities. + + void SetVideoBitrate(int bps); + void RequestKeyFrameSoon(); + + // Returns whether adaptive playback is supported for this object given + // the new size. + bool IsAdaptivePlaybackSupported(int width, int height); + + // Test-only method to set the return value of IsAdaptivePlaybackSupported(). + // Without this function, the return value of that function will be device + // dependent. If |adaptive_playback_supported| is equal to 0, the return value + // will be false. If |adaptive_playback_supported| is larger than 0, the + // return value will be true. + void set_adaptive_playback_supported_for_testing( + int adaptive_playback_supported) { + adaptive_playback_supported_for_testing_ = adaptive_playback_supported; + } + + private: + VideoCodecBridge(const std::string& mime, + bool is_secure, + MediaCodecDirection direction); + + int adaptive_playback_supported_for_testing_; +}; + } // namespace media #endif // MEDIA_BASE_ANDROID_MEDIA_CODEC_BRIDGE_H_ diff --git a/media/base/android/media_codec_bridge_unittest.cc b/media/base/android/media_codec_bridge_unittest.cc new file mode 100644 index 0000000..f1c0471 --- /dev/null +++ b/media/base/android/media_codec_bridge_unittest.cc @@ -0,0 +1,319 @@ +// Copyright (c) 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 <string> + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "media/base/android/media_codec_bridge.h" +#include "media/base/decoder_buffer.h" +#include "media/base/test_data_util.h" +#include "testing/gmock/include/gmock/gmock.h" + +namespace { + +// The first frame of +// http://www.html5rocks.com/en/tutorials/audio/quick/test.mp3 +unsigned char test_mp3[] = { + 0xff, 0xfb, 0xd2, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0x05, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0d, 0x20, 0x00, 0x00, 0x00, 0x2a, 0x7e, 0x40, + 0xc0, 0x19, 0x4a, 0x80, 0x0d, 0x60, 0x48, 0x1b, 0x40, 0xf7, 0xbd, 0xb9, + 0xd9, 0x40, 0x6f, 0x82, 0x01, 0x8b, 0x17, 0xa0, 0x80, 0xc5, 0x01, 0xad, + 0x9a, 0xd3, 0x00, 0x12, 0xc0, 0x72, 0x93, 0x67, 0xd0, 0x03, 0x6f, 0xa4, + 0xc0, 0xc3, 0x23, 0xee, 0x9b, 0xc0, 0xcc, 0x02, 0xa0, 0xa1, 0x30, 0x0c, + 0x52, 0x2d, 0xfd, 0x6e, 0x08, 0x83, 0x60, 0x40, 0x46, 0x06, 0x4b, 0x20, + 0x82, 0x82, 0x7f, 0xd4, 0x81, 0xe7, 0x00, 0x64, 0x20, 0x18, 0xec, 0xc2, + 0x06, 0x57, 0x0f, 0x81, 0x93, 0x0b, 0x00, 0x66, 0xe3, 0xb7, 0xe8, 0x32, + 0x6e, 0xf0, 0x32, 0xb0, 0x58, 0x0c, 0x7c, 0x3a, 0x03, 0x22, 0x14, 0x80, + 0xc9, 0x01, 0x80, 0x30, 0x20, 0x14, 0x0c, 0x96, 0x73, 0xfe, 0x9f, 0x6c, + 0x0c, 0xd2, 0x25, 0x0f, 0xdc, 0x0c, 0x32, 0x43, 0x03, 0x27, 0x87, 0xc0, + 0xc2, 0xc0, 0x20, 0xfc, 0x42, 0xc5, 0xff, 0xff, 0xd4, 0x80, 0x01, 0x01, + 0x80, 0xc3, 0x81, 0x01, 0x95, 0x03, 0x28, 0x82, 0xc0, 0xc3, 0x01, 0xa1, + 0x06, 0x81, 0x87, 0xc2, 0x40, 0x64, 0xc1, 0xf0, 0x12, 0x02, 0xff, 0xf6, + 0x5b, 0x9f, 0x44, 0xdc, 0xdd, 0x0b, 0x38, 0x59, 0xe0, 0x31, 0x71, 0x60, + 0x0c, 0xb4, 0x22, 0x03, 0x3b, 0x96, 0x40, 0xc8, 0x63, 0x90, 0x0a, 0x23, + 0x81, 0x9e, 0x4c, 0x20, 0x65, 0xb3, 0x18, 0x19, 0x6c, 0x42, 0x06, 0x36, + 0x1d, 0x01, 0x90, 0x87, 0xdf, 0xff, 0xd0, 0x65, 0xa6, 0xea, 0x66, 0xfd, + 0x40, 0x0c, 0x48, 0x03, 0x1a, 0x09, 0x01, 0x21, 0x98, 0x19, 0x2c, 0x36, + 0x06, 0x43, 0x21, 0x81, 0x92, 0xca, 0x60, 0x64, 0x70, 0xb8, 0x19, 0x20, + 0x6c, 0x02, 0x83, 0x80, 0xcb, 0x60, 0x65, 0x32, 0x28, 0x18, 0x64, 0x24, + 0x06, 0x3a, 0x0c, 0x00, 0xe1, 0x00, 0x18, 0xd0, 0x35, 0xff, 0xff, 0xff, + 0xe8, 0x32, 0xef, 0xb2, 0x90, 0x65, 0xbb, 0xdd, 0x94, 0x82, 0x0b, 0x4c, + 0xfa, 0x25, 0xf3, 0x74, 0x13, 0x0f, 0xf8, 0x19, 0x28, 0x84, 0x06, 0x36, + 0x11, 0x01, 0x20, 0x80, 0x18, 0xb4, 0x52, 0x0e, 0x15, 0x00, 0x30, 0x50, + 0x0c, 0x84, 0x32, 0x03, 0x11, 0x04, 0x03, 0x48, 0x04, 0x00, 0x00, 0x31, + 0x21, 0x00, 0x0c, 0x84, 0x18, 0x03, 0x07, 0x85, 0x40, 0xc6, 0xa5, 0x70, + 0x32, 0xb8, 0x7c, 0x0c, 0x54, 0x04, 0x00, 0xd0, 0x08, 0x59, 0x58, 0x18, + 0x20, 0x14, 0x06, 0x30, 0x30, 0x01, 0x9b, 0x86, 0x00, 0x6b, 0x54, 0xa8, + 0x19, 0x8c, 0x2a, 0x06, 0x16, 0x09, 0x01, 0xa0, 0xd0, 0xa0, 0x69, 0x74, + 0xb8, 0x19, 0xc4, 0x4a, 0xa3, 0xda, 0x9d, 0x1e, 0x4f, 0x05, 0xc0, 0x5b, + 0x0b, 0x03, 0xc2, 0x76, 0xa3, 0x4f, 0xb9, 0x16, 0xc2, 0x70, 0x41, 0x07, + 0xa0, 0x84, 0x16, 0x38, 0x4a, 0xc8, 0xaf, 0xee, 0x7f, 0x93, 0xb5, 0x5c, + 0x39, 0x1e, 0x29, 0xd9, 0x8c, 0x80, 0xb5, 0x80, 0xe6, 0x85, 0xb2, 0x99, + 0x68, 0x85, 0x46, 0x91, 0x60, 0xdb, 0x06, 0xfa, 0x38, 0x7a, 0xc7, 0xac, + 0x85, 0xa8, 0xd3, 0xe6, 0x99, 0x3b, 0x66, 0x43, 0x23, 0x1f, 0x84, 0xe1, + 0x65, 0x5e, 0xbc, 0x84, 0x18, 0x62, 0xe6, 0x42, 0x0b, 0x82, 0xe4, 0xd3, + 0x42, 0xd2, 0x05, 0x81, 0x4e, 0xe4, 0x9f, 0x8c, 0xc8, 0x7f, 0xa3, 0xe0, + 0x8d, 0xf1, 0x0f, 0x38, 0xe5, 0x3f, 0xc4, 0x2c, 0x24, 0x65, 0x8d, 0xb9, + 0x58, 0xac, 0x39, 0x0e, 0x37, 0x99, 0x2e, 0x85, 0xe0, 0xb7, 0x98, 0x41, + 0x20, 0x38, 0x1b, 0x95, 0x07, 0xfa, 0xa8, 0x9c, 0x21, 0x0f, 0x13, 0x8c, + 0xa5, 0xc1, 0x76, 0xae, 0x0b, 0xc1, 0x30, 0x27, 0x08, 0xc1, 0xf6, 0x4d, + 0xce, 0xb4, 0x41, 0x38, 0x1e, 0x82, 0x10, 0x74, 0x45, 0x91, 0x90, 0xff, + 0x41, 0x8b, 0x62, 0x1a, 0x71, 0xb6, 0x45, 0x63, 0x8c, 0xce, 0xb8, 0x54, + 0x1b, 0xe8, 0x5d, 0x9e, 0x35, 0x9d, 0x6c, 0xac, 0xe8, 0x83, 0xa1, 0xe9, + 0x3f, 0x13, 0x74, 0x11, 0x04, 0x10, 0xf1, 0x37, 0x38, 0xc6, 0x00, 0x60, + 0x27, 0x48, 0x38, 0x85, 0x92, 0x76, 0xb7, 0xf3, 0xa7, 0x1c, 0x4b, 0xf9, + 0x3b, 0x5a, 0x88, 0xac, 0x60, 0x1b, 0x85, 0x81, 0x16, 0xab, 0x44, 0x17, + 0x08, 0x2e, 0x0f, 0xd4, 0xe2, 0xde, 0x49, 0xc9, 0xe1, 0xc0, 0xc0, 0xa0, + 0x7e, 0x73, 0xa1, 0x67, 0xf8, 0xf5, 0x9f, 0xc4, 0x21, 0x50, 0x4f, 0x05, + 0x2c, 0xfc, 0x5c, 0xaa, 0x85, 0xb0, 0xfa, 0x67, 0x80, 0x7e, 0x0f, 0xfd, + 0x92, 0x30, 0xd5, 0xa0, 0xd4, 0x05, 0xdd, 0x06, 0x68, 0x1d, 0x6e, 0x4e, + 0x8b, 0x79, 0xd6, 0xfc, 0xff, 0x2e, 0x6e, 0x7c, 0xba, 0x03, 0x90, 0xd4, + 0x25, 0x65, 0x8e, 0xe7, 0x3a, 0xd1, 0xd6, 0xdc, 0xf0, 0xbe, 0x12, 0xc4, + 0x31, 0x08, 0x16, 0x70, 0x31, 0x85, 0x61, 0x38, 0x27, 0x0a, 0x91, 0x5f, + 0x03, 0x38, 0xeb, 0x37, 0x13, 0x48, 0x41, 0xbe, 0x7f, 0x04, 0x70, 0x62, + 0x2b, 0x15, 0x91, 0x67, 0x63, 0x4f, 0xad, 0xa7, 0x1d, 0x3f, 0x44, 0x17, + 0x02, 0x08, 0x0d, 0xf2, 0xfc, 0x03, 0xa0, 0x74, 0x21, 0x8b, 0x07, 0x3a, + 0x8d, 0x0f, 0x54, 0x58, 0x94, 0x12, 0xc5, 0x62, 0x18, 0xb9, 0x42, 0xf0, + 0x6c, 0x73, 0xa0, 0x92, 0xad, 0x27, 0x1c, 0x20, 0x0f, 0xc1, 0xca, 0x44, + 0x87, 0x47, 0xc5, 0x43, 0x23, 0x01, 0xda, 0x23, 0xe2, 0x89, 0x38, 0x9f, + 0x1f, 0x8d, 0x8c, 0xc6, 0x95, 0xa3, 0x34, 0x21, 0x21, 0x2d, 0x49, 0xea, + 0x4b, 0x05, 0x85, 0xf5, 0x58, 0x25, 0x13, 0xcd, 0x51, 0x19, 0x1a, 0x88, + 0xa6, 0x83, 0xd6, 0xd0, 0xbc, 0x25, 0x19, 0x1c, 0x92, 0x12, 0x44, 0x5d, + 0x1c, 0x04, 0xf1, 0x99, 0xdf, 0x92, 0x8e, 0x09, 0x85, 0xf3, 0x88, 0x82, + 0x4c, 0x22, 0x17, 0xc5, 0x25, 0x23, 0xed, 0x78, 0xf5, 0x41, 0xd1, 0xe9, + 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 +}; + +} // namespace + +namespace media { + +// Helper macro to skip the test if MediaCodecBridge isn't available. +#define SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE() \ + do { \ + if (!MediaCodecBridge::IsAvailable()) { \ + VLOG(0) << "Could not run test - not supported on device."; \ + return; \ + } \ + } while (0) + +static const int kPresentationTimeBase = 100; + +static inline const base::TimeDelta InfiniteTimeOut() { + return base::TimeDelta::FromMicroseconds(-1); +} + +void DecodeMediaFrame( + VideoCodecBridge* media_codec, const uint8* data, size_t data_size, + const base::TimeDelta input_presentation_timestamp, + const base::TimeDelta initial_timestamp_lower_bound) { + base::TimeDelta input_pts = input_presentation_timestamp; + base::TimeDelta timestamp = initial_timestamp_lower_bound; + base::TimeDelta new_timestamp; + for (int i = 0; i < 10; ++i) { + int input_buf_index = -1; + MediaCodecStatus status = + media_codec->DequeueInputBuffer(InfiniteTimeOut(), &input_buf_index); + ASSERT_EQ(MEDIA_CODEC_OK, status); + + media_codec->QueueInputBuffer( + input_buf_index, data, data_size, input_presentation_timestamp); + + size_t unused_offset = 0; + size_t size = 0; + bool eos = false; + int output_buf_index = -1; + status = media_codec->DequeueOutputBuffer(InfiniteTimeOut(), + &output_buf_index, + &unused_offset, + &size, + &new_timestamp, + &eos, + NULL); + + if (status == MEDIA_CODEC_OK && output_buf_index > 0) { + media_codec->ReleaseOutputBuffer(output_buf_index, false); + } + // Output time stamp should not be smaller than old timestamp. + ASSERT_TRUE(new_timestamp >= timestamp); + input_pts += base::TimeDelta::FromMicroseconds(33000); + timestamp = new_timestamp; + } +} + +TEST(MediaCodecBridgeTest, Initialize) { + SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE(); + + scoped_ptr<media::MediaCodecBridge> media_codec; + media_codec.reset(VideoCodecBridge::CreateDecoder( + kCodecH264, false, gfx::Size(640, 480), NULL, NULL)); +} + +TEST(MediaCodecBridgeTest, DoNormal) { + SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE(); + + scoped_ptr<media::AudioCodecBridge> media_codec; + media_codec.reset(AudioCodecBridge::Create(kCodecMP3)); + + ASSERT_TRUE(media_codec->Start( + kCodecMP3, 44100, 2, NULL, 0, 0, 0, false, NULL)); + + int input_buf_index = -1; + MediaCodecStatus status = + media_codec->DequeueInputBuffer(InfiniteTimeOut(), &input_buf_index); + ASSERT_EQ(MEDIA_CODEC_OK, status); + ASSERT_GE(input_buf_index, 0); + + int64 input_pts = kPresentationTimeBase; + media_codec->QueueInputBuffer(input_buf_index, + test_mp3, + sizeof(test_mp3), + base::TimeDelta::FromMicroseconds(++input_pts)); + + status = media_codec->DequeueInputBuffer(InfiniteTimeOut(), &input_buf_index); + media_codec->QueueInputBuffer( + input_buf_index, test_mp3, sizeof(test_mp3), + base::TimeDelta::FromMicroseconds(++input_pts)); + + status = media_codec->DequeueInputBuffer(InfiniteTimeOut(), &input_buf_index); + media_codec->QueueEOS(input_buf_index); + + input_pts = kPresentationTimeBase; + bool eos = false; + while (!eos) { + size_t unused_offset = 0; + size_t size = 0; + base::TimeDelta timestamp; + int output_buf_index = -1; + status = media_codec->DequeueOutputBuffer(InfiniteTimeOut(), + &output_buf_index, + &unused_offset, + &size, + ×tamp, + &eos, + NULL); + switch (status) { + case MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER: + FAIL(); + return; + + case MEDIA_CODEC_OUTPUT_FORMAT_CHANGED: + continue; + + case MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED: + continue; + + default: + break; + } + ASSERT_GE(output_buf_index, 0); + EXPECT_LE(1u, size); + if (!eos) + EXPECT_EQ(++input_pts, timestamp.InMicroseconds()); + ASSERT_LE(input_pts, kPresentationTimeBase + 2); + } + ASSERT_EQ(input_pts, kPresentationTimeBase + 2); +} + +TEST(MediaCodecBridgeTest, InvalidVorbisHeader) { + SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE(); + + scoped_ptr<media::AudioCodecBridge> media_codec; + media_codec.reset(AudioCodecBridge::Create(kCodecVorbis)); + + // The first byte of the header is not 0x02. + uint8 invalid_first_byte[] = { 0x00, 0xff, 0xff, 0xff, 0xff }; + EXPECT_FALSE(media_codec->Start( + kCodecVorbis, 44100, 2, invalid_first_byte, sizeof(invalid_first_byte), + 0, 0, false, NULL)); + + // Size of the header does not match with the data we passed in. + uint8 invalid_size[] = { 0x02, 0x01, 0xff, 0x01, 0xff }; + EXPECT_FALSE(media_codec->Start( + kCodecVorbis, 44100, 2, invalid_size, sizeof(invalid_size), + 0, 0, false, NULL)); + + // Size of the header is too large. + size_t large_size = 8 * 1024 * 1024 + 2; + uint8* very_large_header = new uint8[large_size]; + very_large_header[0] = 0x02; + for (size_t i = 1; i < large_size - 1; ++i) + very_large_header[i] = 0xff; + very_large_header[large_size - 1] = 0xfe; + EXPECT_FALSE(media_codec->Start( + kCodecVorbis, 44100, 2, very_large_header, 0x80000000, + 0, 0, false, NULL)); + delete[] very_large_header; +} + +TEST(MediaCodecBridgeTest, InvalidOpusHeader) { + SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE(); + + scoped_ptr<media::AudioCodecBridge> media_codec; + media_codec.reset(AudioCodecBridge::Create(kCodecOpus)); + uint8 dummy_extra_data[] = { 0, 0 }; + + // Extra Data is NULL. + EXPECT_FALSE(media_codec->Start( + kCodecOpus, 48000, 2, NULL, 0, -1, 0, false, NULL)); + + // Codec Delay is < 0. + EXPECT_FALSE(media_codec->Start( + kCodecOpus, 48000, 2, dummy_extra_data, sizeof(dummy_extra_data), + -1, 0, false, NULL)); + + // Seek Preroll is < 0. + EXPECT_FALSE(media_codec->Start( + kCodecOpus, 48000, 2, dummy_extra_data, sizeof(dummy_extra_data), + 0, -1, false, NULL)); +} + +TEST(MediaCodecBridgeTest, PresentationTimestampsDoNotDecrease) { + SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE(); + + scoped_ptr<VideoCodecBridge> media_codec(VideoCodecBridge::CreateDecoder( + kCodecVP8, false, gfx::Size(320, 240), NULL, NULL)); + EXPECT_TRUE(media_codec.get()); + scoped_refptr<DecoderBuffer> buffer = + ReadTestDataFile("vp8-I-frame-320x240"); + DecodeMediaFrame(media_codec.get(), + buffer->data(), + buffer->data_size(), + base::TimeDelta(), + base::TimeDelta()); + + // Simulate a seek to 10 seconds, and each chunk has 2 I-frames. + std::vector<uint8> chunk(buffer->data(), + buffer->data() + buffer->data_size()); + chunk.insert(chunk.end(), buffer->data(), + buffer->data() + buffer->data_size()); + media_codec->Reset(); + DecodeMediaFrame(media_codec.get(), + &chunk[0], + chunk.size(), + base::TimeDelta::FromMicroseconds(10000000), + base::TimeDelta::FromMicroseconds(9900000)); + + // Simulate a seek to 5 seconds. + media_codec->Reset(); + DecodeMediaFrame(media_codec.get(), + &chunk[0], + chunk.size(), + base::TimeDelta::FromMicroseconds(5000000), + base::TimeDelta::FromMicroseconds(4900000)); +} + +TEST(MediaCodecBridgeTest, CreateUnsupportedCodec) { + EXPECT_EQ(NULL, AudioCodecBridge::Create(kUnknownAudioCodec)); + EXPECT_EQ( + NULL, + VideoCodecBridge::CreateDecoder( + kUnknownVideoCodec, false, gfx::Size(320, 240), NULL, NULL)); +} + +} // namespace media diff --git a/media/base/android/media_codec_decoder.cc b/media/base/android/media_codec_decoder.cc index 67d7188..cdbaf07 100644 --- a/media/base/android/media_codec_decoder.cc +++ b/media/base/android/media_codec_decoder.cc @@ -738,7 +738,9 @@ bool MediaCodecDecoder::EnqueueInputBuffer() { << " subsamples size:" << unit->subsamples.size(); status = media_codec_bridge_->QueueSecureInputBuffer( - index, &unit->data[0], unit->data.size(), unit->key_id, unit->iv, + index, &unit->data[0], unit->data.size(), + reinterpret_cast<const uint8_t*>(&unit->key_id[0]), unit->key_id.size(), + reinterpret_cast<const uint8_t*>(&unit->iv[0]), unit->iv.size(), unit->subsamples.empty() ? nullptr : &unit->subsamples[0], unit->subsamples.size(), unit->timestamp); } diff --git a/media/base/android/media_codec_decoder_unittest.cc b/media/base/android/media_codec_decoder_unittest.cc index 2b14820..322407b 100644 --- a/media/base/android/media_codec_decoder_unittest.cc +++ b/media/base/android/media_codec_decoder_unittest.cc @@ -7,10 +7,9 @@ #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/media_codec_util.h" +#include "media/base/android/media_codec_bridge.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/timestamp_constants.h" @@ -19,6 +18,15 @@ namespace media { +// Helper macro to skip the test if MediaCodecBridge isn't available. +#define SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE() \ + do { \ + if (!MediaCodecBridge::IsAvailable()) { \ + VLOG(0) << "Could not run test - not supported on device."; \ + return; \ + } \ + } while (0) + namespace { const base::TimeDelta kDefaultTimeout = base::TimeDelta::FromMilliseconds(200); diff --git a/media/base/android/media_codec_player_unittest.cc b/media/base/android/media_codec_player_unittest.cc index 628de1ce..46b95fc 100644 --- a/media/base/android/media_codec_player_unittest.cc +++ b/media/base/android/media_codec_player_unittest.cc @@ -8,11 +8,10 @@ #include "base/logging.h" #include "base/timer/timer.h" #include "media/base/android/demuxer_android.h" +#include "media/base/android/media_codec_bridge.h" #include "media/base/android/media_codec_player.h" -#include "media/base/android/media_codec_util.h" #include "media/base/android/media_player_manager.h" #include "media/base/android/media_task_runner.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/timestamp_constants.h" @@ -21,6 +20,15 @@ namespace media { +// Helper macro to skip the test if MediaCodecBridge isn't available. +#define SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE() \ + do { \ + if (!MediaCodecBridge::IsAvailable()) { \ + VLOG(0) << "Could not run test - not supported on device."; \ + return; \ + } \ + } while (0) + #define RUN_ON_MEDIA_THREAD(CLASS, METHOD, ...) \ do { \ if (!GetMediaTaskRunner()->BelongsToCurrentThread()) { \ diff --git a/media/base/android/media_codec_util.cc b/media/base/android/media_codec_util.cc deleted file mode 100644 index cb31ef9..0000000 --- a/media/base/android/media_codec_util.cc +++ /dev/null @@ -1,225 +0,0 @@ -// 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/android/media_codec_util.h" - -#include <algorithm> - -#include "base/android/build_info.h" -#include "base/android/jni_android.h" -#include "base/android/jni_array.h" -#include "base/android/jni_string.h" -#include "base/basictypes.h" -#include "base/logging.h" -#include "base/strings/string_util.h" -#include "jni/MediaCodecUtil_jni.h" - -using base::android::AttachCurrentThread; -using base::android::ConvertJavaStringToUTF8; -using base::android::ConvertUTF8ToJavaString; -using base::android::JavaIntArrayToIntVector; -using base::android::ScopedJavaLocalRef; - -namespace media { - -// static -const 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"; - if (codec == "hvc1") - return "video/hevc"; - if (codec == "mp4a") - return "audio/mp4a-latm"; - if (codec == "vp8" || codec == "vp8.0") - return "video/x-vnd.on2.vp8"; - if (codec == "vp9" || codec == "vp9.0") - return "video/x-vnd.on2.vp9"; - if (codec == "vorbis") - return "audio/vorbis"; - if (codec == "opus") - return "audio/opus"; - 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(); - - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef<jstring> j_mime = ConvertUTF8ToJavaString(env, mime_type); - ScopedJavaLocalRef<jstring> j_codec_name = - Java_MediaCodecUtil_getDefaultCodecName(env, j_mime.obj(), direction); - 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; - - 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; -} - -// static -bool MediaCodecUtil::IsMediaCodecAvailable() { - // MediaCodec is only available on JB and greater. - if (base::android::BuildInfo::GetInstance()->sdk_int() < 16) - return false; - // Blacklist some devices on Jellybean as for MediaCodec support is buggy. - // http://crbug.com/365494. - if (base::android::BuildInfo::GetInstance()->sdk_int() == 16) { - std::string model(base::android::BuildInfo::GetInstance()->model()); - return model != "GT-I9100" && model != "GT-I9300" && model != "GT-N7000"; - } - return true; -} - -// static -bool MediaCodecUtil::SupportsSetParameters() { - // MediaCodec.setParameters() is only available starting with K. - return base::android::BuildInfo::GetInstance()->sdk_int() >= 19; -} - -// static -std::set<int> MediaCodecUtil::GetEncoderColorFormats( - const std::string& mime_type) { - std::set<int> color_formats; - if (!IsMediaCodecAvailable()) - return color_formats; - - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef<jstring> j_mime = ConvertUTF8ToJavaString(env, mime_type); - ScopedJavaLocalRef<jintArray> j_color_format_array = - Java_MediaCodecUtil_getEncoderColorFormatsForMime(env, j_mime.obj()); - - if (j_color_format_array.obj()) { - std::vector<int> formats; - JavaIntArrayToIntVector(env, j_color_format_array.obj(), &formats); - color_formats = std::set<int>(formats.begin(), formats.end()); - } - - return color_formats; -} - -// static -bool MediaCodecUtil::CanDecode(const std::string& codec, bool is_secure) { - if (!IsMediaCodecAvailable()) - return false; - - JNIEnv* env = AttachCurrentThread(); - std::string mime = CodecTypeToAndroidMimeType(codec); - if (mime.empty()) - return false; - ScopedJavaLocalRef<jstring> j_mime = ConvertUTF8ToJavaString(env, mime); - return Java_MediaCodecUtil_canDecode(env, j_mime.obj(), is_secure); -} - -// static -bool MediaCodecUtil::IsKnownUnaccelerated(const std::string& mime_type, - MediaCodecDirection direction) { - 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; - } - } - } - 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; -} - -// static -bool MediaCodecUtil::RegisterMediaCodecUtil(JNIEnv* env) { - return RegisterNativesImpl(env); -} - -} // namespace media diff --git a/media/base/android/media_codec_util.h b/media/base/android/media_codec_util.h deleted file mode 100644 index 604ce5d..0000000 --- a/media/base/android/media_codec_util.h +++ /dev/null @@ -1,63 +0,0 @@ -// 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_ANDROID_MEDIA_CODEC_UTIL_H_ -#define MEDIA_BASE_ANDROID_MEDIA_CODEC_UTIL_H_ - -#include <jni.h> -#include <set> -#include <string> -#include <vector> - -#include "base/compiler_specific.h" -#include "base/macros.h" -#include "media/base/media_export.h" - -namespace media { - -// Helper macro to skip the test if MediaCodecBridge isn't available. -#define SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE() \ - do { \ - if (!MediaCodecUtil::IsMediaCodecAvailable()) { \ - VLOG(0) << "Could not run test - not supported on device."; \ - return; \ - } \ - } while (0) - -// Codec direction. Keep this in sync with MediaCodecUtil.java. -enum MediaCodecDirection { - MEDIA_CODEC_DECODER, - MEDIA_CODEC_ENCODER, -}; - -class MEDIA_EXPORT MediaCodecUtil { - public: - // Returns true if MediaCodec is available on the device. - // All other static methods check IsAvailable() internally. There's no need - // to check IsAvailable() explicitly before calling them. - static bool IsMediaCodecAvailable(); - - // Returns true if MediaCodec.setParameters() is available on the device. - static bool SupportsSetParameters(); - - // Returns whether MediaCodecBridge has a decoder that |is_secure| and can - // decode |codec| type. - static bool CanDecode(const std::string& codec, bool is_secure); - - // Get a list of encoder supported color formats for |mime_type|. - // The mapping of color format name and its value refers to - // MediaCodecInfo.CodecCapabilities. - static std::set<int> GetEncoderColorFormats(const std::string& mime_type); - - // Returns true if |mime_type| is known to be unaccelerated (i.e. backed by a - // software codec instead of a hardware one). - static bool IsKnownUnaccelerated(const std::string& mime_type, - MediaCodecDirection direction); - - static bool RegisterMediaCodecUtil(JNIEnv* env); -}; - -} // namespace media - -#endif // MEDIA_BASE_ANDROID_MEDIA_CODEC_UTIL_H_ diff --git a/media/base/android/media_codec_video_decoder.cc b/media/base/android/media_codec_video_decoder.cc index 928f2e7..f20bbeb 100644 --- a/media/base/android/media_codec_video_decoder.cc +++ b/media/base/android/media_codec_video_decoder.cc @@ -6,8 +6,8 @@ #include "base/bind.h" #include "base/logging.h" +#include "media/base/android/media_codec_bridge.h" #include "media/base/android/media_statistics.h" -#include "media/base/android/sdk_media_codec_bridge.h" #include "media/base/demuxer_stream.h" #include "media/base/timestamp_constants.h" diff --git a/media/base/android/media_decoder_job.cc b/media/base/android/media_decoder_job.cc index 9e0a216..8689107 100644 --- a/media/base/android/media_decoder_job.cc +++ b/media/base/android/media_decoder_job.cc @@ -282,9 +282,13 @@ MediaCodecStatus MediaDecoderJob::QueueInputBuffer(const AccessUnit& unit) { } MediaCodecStatus status = media_codec_bridge_->QueueSecureInputBuffer( - input_buf_index, &unit.data[0], unit.data.size(), unit.key_id, unit.iv, + input_buf_index, + &unit.data[0], unit.data.size(), + reinterpret_cast<const uint8*>(&unit.key_id[0]), unit.key_id.size(), + reinterpret_cast<const uint8*>(&unit.iv[0]), unit.iv.size(), unit.subsamples.empty() ? NULL : &unit.subsamples[0], - unit.subsamples.size(), unit.timestamp); + unit.subsamples.size(), + unit.timestamp); // In case of MEDIA_CODEC_NO_KEY, we must reuse the |input_buf_index_|. // Otherwise MediaDrm will report errors. diff --git a/media/base/android/media_jni_registrar.cc b/media/base/android/media_jni_registrar.cc index cc8bc1d..2020469 100644 --- a/media/base/android/media_jni_registrar.cc +++ b/media/base/android/media_jni_registrar.cc @@ -10,11 +10,10 @@ #include "media/audio/android/audio_manager_android.h" #include "media/audio/android/audio_record_input.h" -#include "media/base/android/media_codec_util.h" +#include "media/base/android/media_codec_bridge.h" #include "media/base/android/media_drm_bridge.h" #include "media/base/android/media_player_bridge.h" #include "media/base/android/media_player_listener.h" -#include "media/base/android/sdk_media_codec_bridge.h" #include "media/base/android/webaudio_media_codec_bridge.h" #include "media/capture/video/android/video_capture_device_android.h" #include "media/capture/video/android/video_capture_device_factory_android.h" @@ -22,19 +21,24 @@ namespace media { static base::android::RegistrationMethod kMediaRegisteredMethods[] = { - {"AudioManagerAndroid", AudioManagerAndroid::RegisterAudioManager}, - {"AudioRecordInput", AudioRecordInputStream::RegisterAudioRecordInput}, - {"MediaDrmBridge", MediaDrmBridge::RegisterMediaDrmBridge}, - {"MediaPlayerBridge", MediaPlayerBridge::RegisterMediaPlayerBridge}, - {"MediaPlayerListener", MediaPlayerListener::RegisterMediaPlayerListener}, - {"SdkMediaCodecBridge", SdkMediaCodecBridge::RegisterSdkMediaCodecBridge}, - {"MediaCodecUtil", MediaCodecUtil::RegisterMediaCodecUtil}, - {"VideoCaptureDevice", - VideoCaptureDeviceAndroid::RegisterVideoCaptureDevice}, - {"VideoCaptureDeviceFactory", - VideoCaptureDeviceFactoryAndroid::RegisterVideoCaptureDeviceFactory}, - {"WebAudioMediaCodecBridge", - WebAudioMediaCodecBridge::RegisterWebAudioMediaCodecBridge}, + { "AudioManagerAndroid", + AudioManagerAndroid::RegisterAudioManager }, + { "AudioRecordInput", + AudioRecordInputStream::RegisterAudioRecordInput }, + { "MediaCodecBridge", + MediaCodecBridge::RegisterMediaCodecBridge }, + { "MediaDrmBridge", + MediaDrmBridge::RegisterMediaDrmBridge }, + { "MediaPlayerBridge", + MediaPlayerBridge::RegisterMediaPlayerBridge }, + { "MediaPlayerListener", + MediaPlayerListener::RegisterMediaPlayerListener }, + { "VideoCaptureDevice", + VideoCaptureDeviceAndroid::RegisterVideoCaptureDevice }, + { "VideoCaptureDeviceFactory", + VideoCaptureDeviceFactoryAndroid::RegisterVideoCaptureDeviceFactory }, + { "WebAudioMediaCodecBridge", + WebAudioMediaCodecBridge::RegisterWebAudioMediaCodecBridge }, }; bool RegisterJni(JNIEnv* env) { diff --git a/media/base/android/media_source_player_unittest.cc b/media/base/android/media_source_player_unittest.cc index b7cab6c..fd4a725 100644 --- a/media/base/android/media_source_player_unittest.cc +++ b/media/base/android/media_source_player_unittest.cc @@ -9,12 +9,11 @@ #include "base/memory/scoped_ptr.h" #include "base/strings/stringprintf.h" #include "media/base/android/audio_decoder_job.h" -#include "media/base/android/media_codec_util.h" +#include "media/base/android/media_codec_bridge.h" #include "media/base/android/media_drm_bridge.h" #include "media/base/android/media_player_manager.h" #include "media/base/android/media_source_player.h" #include "media/base/android/media_url_interceptor.h" -#include "media/base/android/sdk_media_codec_bridge.h" #include "media/base/android/video_decoder_job.h" #include "media/base/bind_to_current_loop.h" #include "media/base/decoder_buffer.h" @@ -25,6 +24,15 @@ namespace media { +// Helper macro to skip the test if MediaCodecBridge isn't available. +#define SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE() \ + do { \ + if (!MediaCodecBridge::IsAvailable()) { \ + VLOG(0) << "Could not run test - not supported on device."; \ + return; \ + } \ + } while (0) + const base::TimeDelta kDefaultDuration = base::TimeDelta::FromMilliseconds(10000); diff --git a/media/base/android/ndk_media_codec_bridge.cc b/media/base/android/ndk_media_codec_bridge.cc deleted file mode 100644 index 2ce2a0f..0000000 --- a/media/base/android/ndk_media_codec_bridge.cc +++ /dev/null @@ -1,234 +0,0 @@ -// 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/android/ndk_media_codec_bridge.h" - -#include <media/NdkMediaError.h> -#include <media/NdkMediaFormat.h> - -#include <limits> - -#include "base/strings/string_util.h" -#include "media/base/decrypt_config.h" - -namespace { -const char kMediaFormatKeyCropLeft[] = "crop-left"; -const char kMediaFormatKeyCropRight[] = "crop-right"; -const char kMediaFormatKeyCropBottom[] = "crop-bottom"; -const char kMediaFormatKeyCropTop[] = "crop-top"; -} - -namespace media { - -// Translate media_status_t to MediaCodecStatus. -static MediaCodecStatus TranslateMediaCodecStatus(media_status_t status) { - switch (status) { - case AMEDIA_OK: - return MEDIA_CODEC_OK; - case AMEDIA_DRM_NEED_KEY: - return MEDIA_CODEC_NO_KEY; - default: - return MEDIA_CODEC_ERROR; - } -} - -NdkMediaCodecBridge::~NdkMediaCodecBridge() {} - -NdkMediaCodecBridge::NdkMediaCodecBridge(const std::string& mime, - bool is_secure, - MediaCodecDirection direction) { - if (base::StartsWith(mime, "video", base::CompareCase::SENSITIVE) && - is_secure && direction == MEDIA_CODEC_DECODER) { - // TODO(qinmin): get the secure decoder name from java. - NOTIMPLEMENTED(); - return; - } - - if (direction == MEDIA_CODEC_DECODER) - media_codec_.reset(AMediaCodec_createDecoderByType(mime.c_str())); - else - media_codec_.reset(AMediaCodec_createEncoderByType(mime.c_str())); -} - -MediaCodecStatus NdkMediaCodecBridge::Reset() { - media_status_t status = AMediaCodec_flush(media_codec_.get()); - return TranslateMediaCodecStatus(status); -} - -bool NdkMediaCodecBridge::Start() { - return AMEDIA_OK == AMediaCodec_start(media_codec_.get()); -} - -void NdkMediaCodecBridge::Stop() { - AMediaCodec_stop(media_codec_.get()); -} - -void NdkMediaCodecBridge::GetOutputFormat(int* width, int* height) { - AMediaFormat* format = AMediaCodec_getOutputFormat(media_codec_.get()); - int left, right, bottom, top; - bool has_left = AMediaFormat_getInt32(format, kMediaFormatKeyCropLeft, &left); - bool has_right = - AMediaFormat_getInt32(format, kMediaFormatKeyCropRight, &right); - bool has_bottom = - AMediaFormat_getInt32(format, kMediaFormatKeyCropBottom, &bottom); - bool has_top = AMediaFormat_getInt32(format, kMediaFormatKeyCropTop, &top); - 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; - } else { - AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_WIDTH, width); - AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_HEIGHT, height); - } -} - -int NdkMediaCodecBridge::GetOutputSamplingRate() { - 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; -} - -MediaCodecStatus NdkMediaCodecBridge::QueueInputBuffer( - int index, - const uint8_t* data, - size_t data_size, - const base::TimeDelta& presentation_time) { - if (data_size > - base::checked_cast<size_t>(std::numeric_limits<int32_t>::max())) { - return MEDIA_CODEC_ERROR; - } - if (data && !FillInputBuffer(index, data, data_size)) - return MEDIA_CODEC_ERROR; - - media_status_t status = - AMediaCodec_queueInputBuffer(media_codec_.get(), index, 0, data_size, - presentation_time.InMicroseconds(), 0); - return TranslateMediaCodecStatus(status); -} - -MediaCodecStatus NdkMediaCodecBridge::QueueSecureInputBuffer( - int index, - const uint8_t* data, - size_t data_size, - const std::vector<char>& key_id, - const std::vector<char>& iv, - const SubsampleEntry* subsamples, - int subsamples_size, - const base::TimeDelta& presentation_time) { - if (data_size > - base::checked_cast<size_t>(std::numeric_limits<int32_t>::max())) { - return MEDIA_CODEC_ERROR; - } - if (key_id.size() > 16 || iv.size()) - return MEDIA_CODEC_ERROR; - if (data && !FillInputBuffer(index, data, data_size)) - return MEDIA_CODEC_ERROR; - - int new_subsamples_size = subsamples_size == 0 ? 1 : subsamples_size; - std::vector<size_t> clear_data, encrypted_data; - if (subsamples_size == 0) { - DCHECK(!subsamples); - clear_data.push_back(0); - encrypted_data.push_back(data_size); - } else { - DCHECK_GT(subsamples_size, 0); - DCHECK(subsamples); - for (int i = 0; i < subsamples_size; ++i) { - DCHECK(subsamples[i].clear_bytes <= std::numeric_limits<uint16_t>::max()); - if (subsamples[i].cypher_bytes > - static_cast<uint32_t>(std::numeric_limits<int32_t>::max())) { - return MEDIA_CODEC_ERROR; - } - clear_data.push_back(subsamples[i].clear_bytes); - encrypted_data.push_back(subsamples[i].cypher_bytes); - } - } - - AMediaCodecCryptoInfo* crypto_info = AMediaCodecCryptoInfo_new( - new_subsamples_size, - reinterpret_cast<uint8_t*>(const_cast<char*>(key_id.data())), - reinterpret_cast<uint8_t*>(const_cast<char*>(iv.data())), - AMEDIACODECRYPTOINFO_MODE_AES_CTR, clear_data.data(), - encrypted_data.data()); - - media_status_t status = AMediaCodec_queueSecureInputBuffer( - media_codec_.get(), index, 0, crypto_info, - presentation_time.InMicroseconds(), 0); - return TranslateMediaCodecStatus(status); -} - -void NdkMediaCodecBridge::QueueEOS(int input_buffer_index) { - AMediaCodec_queueInputBuffer(media_codec_.get(), input_buffer_index, 0, 0, 0, - AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM); -} - -MediaCodecStatus NdkMediaCodecBridge::DequeueInputBuffer( - const base::TimeDelta& timeout, - int* index) { - *index = AMediaCodec_dequeueInputBuffer(media_codec_.get(), - timeout.InMicroseconds()); - if (*index >= 0) - return MEDIA_CODEC_OK; - else if (*index == AMEDIACODEC_INFO_TRY_AGAIN_LATER) - return MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER; - else - return MEDIA_CODEC_ERROR; -} - -MediaCodecStatus NdkMediaCodecBridge::DequeueOutputBuffer( - const base::TimeDelta& timeout, - int* index, - size_t* offset, - size_t* size, - base::TimeDelta* presentation_time, - bool* end_of_stream, - bool* key_frame) { - AMediaCodecBufferInfo buffer_info; - *index = AMediaCodec_dequeueOutputBuffer(media_codec_.get(), &buffer_info, - timeout.InMicroseconds()); - *offset = buffer_info.offset; - *size = buffer_info.size; - *presentation_time = - base::TimeDelta::FromMicroseconds(buffer_info.presentationTimeUs); - if (end_of_stream) - *end_of_stream = buffer_info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM; - if (key_frame) - *key_frame = false; // This is deprecated. - if (*index >= 0) - return MEDIA_CODEC_OK; - else if (*index == AMEDIACODEC_INFO_TRY_AGAIN_LATER) - return MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER; - else - return MEDIA_CODEC_ERROR; -} - -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) { - *data = AMediaCodec_getInputBuffer(media_codec_.get(), input_buffer_index, - capacity); -} - -bool NdkMediaCodecBridge::CopyFromOutputBuffer(int index, - size_t offset, - void* dst, - int dst_size) { - size_t capacity; - 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; -} - -} // namespace media diff --git a/media/base/android/ndk_media_codec_bridge.h b/media/base/android/ndk_media_codec_bridge.h deleted file mode 100644 index d16d6f8..0000000 --- a/media/base/android/ndk_media_codec_bridge.h +++ /dev/null @@ -1,80 +0,0 @@ -// 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_ANDROID_NDK_MEDIA_CODEC_BRIDGE_H_ -#define MEDIA_BASE_ANDROID_NDK_MEDIA_CODEC_BRIDGE_H_ - -#include <media/NdkMediaCodec.h> -#include <stdint.h> - -#include "base/macros.h" -#include "base/memory/scoped_ptr.h" -#include "base/time/time.h" -#include "media/base/android/media_codec_bridge.h" -#include "media/base/media_export.h" - -namespace media { - -class MEDIA_EXPORT NdkMediaCodecBridge : public MediaCodecBridge { - public: - ~NdkMediaCodecBridge() override; - - // MediaCodecBridge implementation. - MediaCodecStatus Reset() override; - bool Start() override; - void Stop() override; - void GetOutputFormat(int* width, int* height) override; - int GetOutputSamplingRate() override; - MediaCodecStatus QueueInputBuffer( - int index, - const uint8_t* data, - size_t data_size, - const base::TimeDelta& presentation_time) override; - using MediaCodecBridge::QueueSecureInputBuffer; - MediaCodecStatus QueueSecureInputBuffer( - int index, - const uint8_t* data, - size_t data_size, - const std::vector<char>& key_id, - const std::vector<char>& iv, - const SubsampleEntry* subsamples, - int subsamples_size, - const base::TimeDelta& presentation_time) override; - void QueueEOS(int input_buffer_index); - MediaCodecStatus DequeueInputBuffer(const base::TimeDelta& timeout, - int* index) override; - MediaCodecStatus DequeueOutputBuffer(const base::TimeDelta& timeout, - int* index, - size_t* offset, - size_t* size, - base::TimeDelta* presentation_time, - 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; - - protected: - NdkMediaCodecBridge(const std::string& mime, - bool is_secure, - MediaCodecDirection direction); - - private: - struct AMediaCodecDeleter { - inline void operator()(AMediaCodec* ptr) const { AMediaCodec_delete(ptr); } - }; - - scoped_ptr<AMediaCodec, AMediaCodecDeleter> media_codec_; - - DISALLOW_COPY_AND_ASSIGN(NdkMediaCodecBridge); -}; - -} // namespace media - -#endif // MEDIA_BASE_ANDROID_NDK_MEDIA_CODEC_BRIDGE_H_ diff --git a/media/base/android/sdk_media_codec_bridge.cc b/media/base/android/sdk_media_codec_bridge.cc deleted file mode 100644 index 0c6e25f..0000000 --- a/media/base/android/sdk_media_codec_bridge.cc +++ /dev/null @@ -1,643 +0,0 @@ -// Copyright (c) 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/android/sdk_media_codec_bridge.h" - -#include <algorithm> -#include <limits> - -#include "base/android/build_info.h" -#include "base/android/jni_android.h" -#include "base/android/jni_array.h" -#include "base/android/jni_string.h" -#include "base/basictypes.h" -#include "base/logging.h" -#include "base/numerics/safe_conversions.h" -#include "base/strings/string_util.h" -#include "jni/MediaCodecBridge_jni.h" -#include "media/base/bit_reader.h" -#include "media/base/decrypt_config.h" - -using base::android::AttachCurrentThread; -using base::android::ConvertJavaStringToUTF8; -using base::android::ConvertUTF8ToJavaString; -using base::android::JavaIntArrayToIntVector; -using base::android::ScopedJavaLocalRef; - -#define RETURN_ON_ERROR(condition) \ - do { \ - if (!(condition)) { \ - LOG(ERROR) << "Unable to parse AAC header: " #condition; \ - return false; \ - } \ - } while (0) - -namespace media { - -enum { - kBufferFlagSyncFrame = 1, // BUFFER_FLAG_SYNC_FRAME - kBufferFlagEndOfStream = 4, // BUFFER_FLAG_END_OF_STREAM - kConfigureFlagEncode = 1, // CONFIGURE_FLAG_ENCODE -}; - -static ScopedJavaLocalRef<jintArray> -ToJavaIntArray(JNIEnv* env, scoped_ptr<jint[]> native_array, int size) { - ScopedJavaLocalRef<jintArray> j_array(env, env->NewIntArray(size)); - env->SetIntArrayRegion(j_array.obj(), 0, size, native_array.get()); - return j_array; -} - -static const std::string AudioCodecToAndroidMimeType(const AudioCodec& codec) { - switch (codec) { - case kCodecMP3: - return "audio/mpeg"; - case kCodecVorbis: - return "audio/vorbis"; - case kCodecOpus: - return "audio/opus"; - case kCodecAAC: - return "audio/mp4a-latm"; - default: - return std::string(); - } -} - -static const std::string VideoCodecToAndroidMimeType(const VideoCodec& codec) { - switch (codec) { - case kCodecH264: - return "video/avc"; - case kCodecHEVC: - return "video/hevc"; - case kCodecVP8: - return "video/x-vnd.on2.vp8"; - case kCodecVP9: - return "video/x-vnd.on2.vp9"; - default: - return std::string(); - } -} - -SdkMediaCodecBridge::SdkMediaCodecBridge(const std::string& mime, - bool is_secure, - MediaCodecDirection direction) { - JNIEnv* env = AttachCurrentThread(); - CHECK(env); - DCHECK(!mime.empty()); - ScopedJavaLocalRef<jstring> j_mime = ConvertUTF8ToJavaString(env, mime); - j_media_codec_.Reset( - Java_MediaCodecBridge_create(env, j_mime.obj(), is_secure, direction)); -} - -SdkMediaCodecBridge::~SdkMediaCodecBridge() { - JNIEnv* env = AttachCurrentThread(); - CHECK(env); - if (j_media_codec_.obj()) - Java_MediaCodecBridge_release(env, j_media_codec_.obj()); -} - -MediaCodecStatus SdkMediaCodecBridge::Reset() { - JNIEnv* env = AttachCurrentThread(); - return static_cast<MediaCodecStatus>( - Java_MediaCodecBridge_flush(env, j_media_codec_.obj())); -} - -bool SdkMediaCodecBridge::Start() { - JNIEnv* env = AttachCurrentThread(); - return Java_MediaCodecBridge_start(env, j_media_codec_.obj()); -} - -void SdkMediaCodecBridge::Stop() { - JNIEnv* env = AttachCurrentThread(); - Java_MediaCodecBridge_stop(env, j_media_codec_.obj()); -} - -void SdkMediaCodecBridge::GetOutputFormat(int* width, int* height) { - JNIEnv* env = AttachCurrentThread(); - - *width = Java_MediaCodecBridge_getOutputWidth(env, j_media_codec_.obj()); - *height = Java_MediaCodecBridge_getOutputHeight(env, j_media_codec_.obj()); -} - -int SdkMediaCodecBridge::GetOutputSamplingRate() { - JNIEnv* env = AttachCurrentThread(); - - return Java_MediaCodecBridge_getOutputSamplingRate(env, j_media_codec_.obj()); -} - -MediaCodecStatus SdkMediaCodecBridge::QueueInputBuffer( - int index, - const uint8_t* data, - size_t data_size, - const base::TimeDelta& presentation_time) { - DVLOG(3) << __PRETTY_FUNCTION__ << index << ": " << data_size; - if (data_size > - base::checked_cast<size_t>(std::numeric_limits<int32_t>::max())) { - return MEDIA_CODEC_ERROR; - } - if (data && !FillInputBuffer(index, data, data_size)) - return MEDIA_CODEC_ERROR; - JNIEnv* env = AttachCurrentThread(); - return static_cast<MediaCodecStatus>(Java_MediaCodecBridge_queueInputBuffer( - env, j_media_codec_.obj(), index, 0, data_size, - presentation_time.InMicroseconds(), 0)); -} - -// TODO(timav): Combine this and above methods together keeping only the first -// interface after we switch to Spitzer pipeline. -MediaCodecStatus SdkMediaCodecBridge::QueueSecureInputBuffer( - int index, - const uint8_t* data, - size_t data_size, - const std::vector<char>& key_id, - const std::vector<char>& iv, - const SubsampleEntry* subsamples, - int subsamples_size, - const base::TimeDelta& presentation_time) { - DVLOG(3) << __PRETTY_FUNCTION__ << index << ": " << data_size; - if (data_size > - base::checked_cast<size_t>(std::numeric_limits<int32_t>::max())) { - return MEDIA_CODEC_ERROR; - } - if (data && !FillInputBuffer(index, data, data_size)) - return MEDIA_CODEC_ERROR; - - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef<jbyteArray> j_key_id = base::android::ToJavaByteArray( - env, reinterpret_cast<const uint8_t*>(key_id.data()), key_id.size()); - ScopedJavaLocalRef<jbyteArray> j_iv = base::android::ToJavaByteArray( - env, reinterpret_cast<const uint8_t*>(iv.data()), iv.size()); - - // MediaCodec.CryptoInfo documentations says passing NULL for |clear_array| - // to indicate that all data is encrypted. But it doesn't specify what - // |cypher_array| and |subsamples_size| should be in that case. Passing - // one subsample here just to be on the safe side. - int new_subsamples_size = subsamples_size == 0 ? 1 : subsamples_size; - - scoped_ptr<jint[]> native_clear_array(new jint[new_subsamples_size]); - scoped_ptr<jint[]> native_cypher_array(new jint[new_subsamples_size]); - - if (subsamples_size == 0) { - DCHECK(!subsamples); - native_clear_array[0] = 0; - native_cypher_array[0] = data_size; - } else { - DCHECK_GT(subsamples_size, 0); - DCHECK(subsamples); - for (int i = 0; i < subsamples_size; ++i) { - DCHECK(subsamples[i].clear_bytes <= std::numeric_limits<uint16_t>::max()); - if (subsamples[i].cypher_bytes > - static_cast<uint32_t>(std::numeric_limits<jint>::max())) { - return MEDIA_CODEC_ERROR; - } - - native_clear_array[i] = subsamples[i].clear_bytes; - native_cypher_array[i] = subsamples[i].cypher_bytes; - } - } - - ScopedJavaLocalRef<jintArray> clear_array = - ToJavaIntArray(env, native_clear_array.Pass(), new_subsamples_size); - ScopedJavaLocalRef<jintArray> cypher_array = - ToJavaIntArray(env, native_cypher_array.Pass(), new_subsamples_size); - - return static_cast<MediaCodecStatus>( - Java_MediaCodecBridge_queueSecureInputBuffer( - env, j_media_codec_.obj(), index, 0, j_iv.obj(), j_key_id.obj(), - clear_array.obj(), cypher_array.obj(), new_subsamples_size, - presentation_time.InMicroseconds())); -} - -void SdkMediaCodecBridge::QueueEOS(int input_buffer_index) { - DVLOG(3) << __PRETTY_FUNCTION__ << ": " << input_buffer_index; - JNIEnv* env = AttachCurrentThread(); - Java_MediaCodecBridge_queueInputBuffer(env, j_media_codec_.obj(), - input_buffer_index, 0, 0, 0, - kBufferFlagEndOfStream); -} - -MediaCodecStatus SdkMediaCodecBridge::DequeueInputBuffer( - const base::TimeDelta& timeout, - int* index) { - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef<jobject> result = Java_MediaCodecBridge_dequeueInputBuffer( - env, j_media_codec_.obj(), timeout.InMicroseconds()); - *index = Java_DequeueInputResult_index(env, result.obj()); - MediaCodecStatus status = static_cast<MediaCodecStatus>( - Java_DequeueInputResult_status(env, result.obj())); - DVLOG(3) << __PRETTY_FUNCTION__ << ": status: " << status - << ", index: " << *index; - return status; -} - -MediaCodecStatus SdkMediaCodecBridge::DequeueOutputBuffer( - const base::TimeDelta& timeout, - int* index, - size_t* offset, - size_t* size, - base::TimeDelta* presentation_time, - bool* end_of_stream, - bool* key_frame) { - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef<jobject> result = - Java_MediaCodecBridge_dequeueOutputBuffer(env, j_media_codec_.obj(), - timeout.InMicroseconds()); - *index = Java_DequeueOutputResult_index(env, result.obj()); - *offset = base::checked_cast<size_t>( - Java_DequeueOutputResult_offset(env, result.obj())); - *size = base::checked_cast<size_t>( - Java_DequeueOutputResult_numBytes(env, result.obj())); - if (presentation_time) { - *presentation_time = base::TimeDelta::FromMicroseconds( - Java_DequeueOutputResult_presentationTimeMicroseconds(env, - result.obj())); - } - int flags = Java_DequeueOutputResult_flags(env, result.obj()); - if (end_of_stream) - *end_of_stream = flags & kBufferFlagEndOfStream; - if (key_frame) - *key_frame = flags & kBufferFlagSyncFrame; - MediaCodecStatus status = static_cast<MediaCodecStatus>( - Java_DequeueOutputResult_status(env, result.obj())); - DVLOG(3) << __PRETTY_FUNCTION__ << ": status: " << status - << ", index: " << *index << ", offset: " << *offset - << ", size: " << *size << ", flags: " << flags; - return status; -} - -void SdkMediaCodecBridge::ReleaseOutputBuffer(int index, bool render) { - DVLOG(3) << __PRETTY_FUNCTION__ << ": " << index; - JNIEnv* env = AttachCurrentThread(); - CHECK(env); - - Java_MediaCodecBridge_releaseOutputBuffer(env, j_media_codec_.obj(), index, - 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) { - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef<jobject> j_buffer(Java_MediaCodecBridge_getInputBuffer( - env, j_media_codec_.obj(), input_buffer_index)); - *data = static_cast<uint8_t*>(env->GetDirectBufferAddress(j_buffer.obj())); - *capacity = - base::checked_cast<size_t>(env->GetDirectBufferCapacity(j_buffer.obj())); -} - -bool SdkMediaCodecBridge::CopyFromOutputBuffer(int index, - size_t offset, - void* dst, - int dst_size) { - 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; - } - memcpy(dst, static_cast<uint8_t*>(src_data) + offset, dst_size); - return true; -} - -int SdkMediaCodecBridge::GetOutputBufferAddress(int index, - size_t offset, - void** addr) { - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef<jobject> j_buffer( - Java_MediaCodecBridge_getOutputBuffer(env, j_media_codec_.obj(), index)); - *addr = - reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(j_buffer.obj())) + - offset; - return env->GetDirectBufferCapacity(j_buffer.obj()) - offset; -} - -// static -bool SdkMediaCodecBridge::RegisterSdkMediaCodecBridge(JNIEnv* env) { - return RegisterNativesImpl(env); -} - -// static -AudioCodecBridge* AudioCodecBridge::Create(const AudioCodec& codec) { - if (!MediaCodecUtil::IsMediaCodecAvailable()) - return nullptr; - - const std::string mime = AudioCodecToAndroidMimeType(codec); - return mime.empty() ? nullptr : new AudioCodecBridge(mime); -} - -// static -bool AudioCodecBridge::IsKnownUnaccelerated(const AudioCodec& codec) { - return MediaCodecUtil::IsKnownUnaccelerated( - AudioCodecToAndroidMimeType(codec), MEDIA_CODEC_DECODER); -} - -AudioCodecBridge::AudioCodecBridge(const std::string& mime) - // Audio codec doesn't care about security level and there is no need for - // audio encoding yet. - : SdkMediaCodecBridge(mime, false, MEDIA_CODEC_DECODER) {} - -bool AudioCodecBridge::ConfigureAndStart(const AudioCodec& codec, - int sample_rate, - int channel_count, - const uint8_t* extra_data, - size_t extra_data_size, - int64_t codec_delay_ns, - int64_t seek_preroll_ns, - bool play_audio, - jobject media_crypto) { - JNIEnv* env = AttachCurrentThread(); - - if (!media_codec()) - return false; - - std::string codec_string = AudioCodecToAndroidMimeType(codec); - if (codec_string.empty()) - return false; - - ScopedJavaLocalRef<jstring> j_mime = - ConvertUTF8ToJavaString(env, codec_string); - ScopedJavaLocalRef<jobject> j_format(Java_MediaCodecBridge_createAudioFormat( - env, j_mime.obj(), sample_rate, channel_count)); - DCHECK(!j_format.is_null()); - - if (!ConfigureMediaFormat(j_format.obj(), codec, extra_data, extra_data_size, - codec_delay_ns, seek_preroll_ns)) { - return false; - } - - if (!Java_MediaCodecBridge_configureAudio(env, media_codec(), j_format.obj(), - media_crypto, 0, play_audio)) { - return false; - } - - return Start(); -} - -bool AudioCodecBridge::ConfigureMediaFormat(jobject j_format, - const AudioCodec& codec, - const uint8_t* extra_data, - size_t extra_data_size, - int64_t codec_delay_ns, - int64_t seek_preroll_ns) { - if (extra_data_size == 0 && codec != kCodecOpus) - return true; - - JNIEnv* env = AttachCurrentThread(); - switch (codec) { - case kCodecVorbis: { - if (extra_data[0] != 2) { - LOG(ERROR) << "Invalid number of vorbis headers before the codec " - << "header: " << extra_data[0]; - return false; - } - - size_t header_length[2]; - // |total_length| keeps track of the total number of bytes before the last - // header. - size_t total_length = 1; - const uint8_t* current_pos = extra_data; - // Calculate the length of the first 2 headers. - for (int i = 0; i < 2; ++i) { - header_length[i] = 0; - while (total_length < extra_data_size) { - size_t size = *(++current_pos); - total_length += 1 + size; - if (total_length > 0x80000000) { - LOG(ERROR) << "Vorbis header size too large"; - return false; - } - header_length[i] += size; - if (size < 0xFF) - break; - } - if (total_length >= extra_data_size) { - LOG(ERROR) << "Invalid vorbis header size in the extra data"; - return false; - } - } - current_pos++; - // The first header is identification header. - ScopedJavaLocalRef<jbyteArray> first_header = - base::android::ToJavaByteArray(env, current_pos, header_length[0]); - Java_MediaCodecBridge_setCodecSpecificData(env, j_format, 0, - first_header.obj()); - // The last header is codec header. - ScopedJavaLocalRef<jbyteArray> last_header = - base::android::ToJavaByteArray(env, extra_data + total_length, - extra_data_size - total_length); - Java_MediaCodecBridge_setCodecSpecificData(env, j_format, 1, - last_header.obj()); - break; - } - case kCodecAAC: { - media::BitReader reader(extra_data, extra_data_size); - - // The following code is copied from aac.cc - // TODO(qinmin): refactor the code in aac.cc to make it more reusable. - uint8_t profile = 0; - uint8_t frequency_index = 0; - uint8_t channel_config = 0; - RETURN_ON_ERROR(reader.ReadBits(5, &profile)); - RETURN_ON_ERROR(reader.ReadBits(4, &frequency_index)); - - if (0xf == frequency_index) - RETURN_ON_ERROR(reader.SkipBits(24)); - RETURN_ON_ERROR(reader.ReadBits(4, &channel_config)); - - if (profile == 5 || profile == 29) { - // Read extension config. - RETURN_ON_ERROR(reader.ReadBits(4, &frequency_index)); - if (frequency_index == 0xf) - RETURN_ON_ERROR(reader.SkipBits(24)); - RETURN_ON_ERROR(reader.ReadBits(5, &profile)); - } - - if (profile < 1 || profile > 4 || frequency_index == 0xf || - channel_config > 7) { - LOG(ERROR) << "Invalid AAC header"; - return false; - } - const size_t kCsdLength = 2; - uint8_t csd[kCsdLength]; - csd[0] = profile << 3 | frequency_index >> 1; - csd[1] = (frequency_index & 0x01) << 7 | channel_config << 3; - ScopedJavaLocalRef<jbyteArray> byte_array = - base::android::ToJavaByteArray(env, csd, kCsdLength); - Java_MediaCodecBridge_setCodecSpecificData(env, j_format, 0, - byte_array.obj()); - - // TODO(qinmin): pass an extra variable to this function to determine - // whether we need to call this. - Java_MediaCodecBridge_setFrameHasADTSHeader(env, j_format); - break; - } - case kCodecOpus: { - if (!extra_data || extra_data_size == 0 || codec_delay_ns < 0 || - seek_preroll_ns < 0) { - LOG(ERROR) << "Invalid Opus Header"; - return false; - } - - // csd0 - Opus Header - ScopedJavaLocalRef<jbyteArray> csd0 = - base::android::ToJavaByteArray(env, extra_data, extra_data_size); - Java_MediaCodecBridge_setCodecSpecificData(env, j_format, 0, csd0.obj()); - - // csd1 - Codec Delay - ScopedJavaLocalRef<jbyteArray> csd1 = base::android::ToJavaByteArray( - env, reinterpret_cast<const uint8_t*>(&codec_delay_ns), - sizeof(int64_t)); - Java_MediaCodecBridge_setCodecSpecificData(env, j_format, 1, csd1.obj()); - - // csd2 - Seek Preroll - ScopedJavaLocalRef<jbyteArray> csd2 = base::android::ToJavaByteArray( - env, reinterpret_cast<const uint8_t*>(&seek_preroll_ns), - sizeof(int64_t)); - Java_MediaCodecBridge_setCodecSpecificData(env, j_format, 2, csd2.obj()); - break; - } - default: - LOG(ERROR) << "Invalid header encountered for codec: " - << AudioCodecToAndroidMimeType(codec); - return false; - } - return true; -} - -int64_t AudioCodecBridge::PlayOutputBuffer(int index, - size_t size, - size_t offset, - bool postpone) { - 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); - 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); -} - -void AudioCodecBridge::SetVolume(double volume) { - JNIEnv* env = AttachCurrentThread(); - Java_MediaCodecBridge_setVolume(env, media_codec(), volume); -} - -// static -bool VideoCodecBridge::IsKnownUnaccelerated(const VideoCodec& codec, - MediaCodecDirection direction) { - return MediaCodecUtil::IsKnownUnaccelerated( - VideoCodecToAndroidMimeType(codec), direction); -} - -// static -VideoCodecBridge* VideoCodecBridge::CreateDecoder(const VideoCodec& codec, - bool is_secure, - const gfx::Size& size, - jobject surface, - jobject media_crypto) { - if (!MediaCodecUtil::IsMediaCodecAvailable()) - return nullptr; - - const std::string mime = VideoCodecToAndroidMimeType(codec); - if (mime.empty()) - return nullptr; - - scoped_ptr<VideoCodecBridge> bridge( - new VideoCodecBridge(mime, is_secure, MEDIA_CODEC_DECODER)); - if (!bridge->media_codec()) - return nullptr; - - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef<jstring> j_mime = ConvertUTF8ToJavaString(env, mime); - ScopedJavaLocalRef<jobject> j_format( - 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)) { - return nullptr; - } - - return bridge->Start() ? bridge.release() : nullptr; -} - -// static -VideoCodecBridge* VideoCodecBridge::CreateEncoder(const VideoCodec& codec, - const gfx::Size& size, - int bit_rate, - int frame_rate, - int i_frame_interval, - int color_format) { - if (!MediaCodecUtil::IsMediaCodecAvailable()) - return nullptr; - - const std::string mime = VideoCodecToAndroidMimeType(codec); - if (mime.empty()) - return nullptr; - - scoped_ptr<VideoCodecBridge> bridge( - new VideoCodecBridge(mime, false, MEDIA_CODEC_ENCODER)); - if (!bridge->media_codec()) - return nullptr; - - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef<jstring> j_mime = ConvertUTF8ToJavaString(env, mime); - ScopedJavaLocalRef<jobject> j_format( - Java_MediaCodecBridge_createVideoEncoderFormat( - env, j_mime.obj(), size.width(), size.height(), bit_rate, frame_rate, - i_frame_interval, color_format)); - DCHECK(!j_format.is_null()); - if (!Java_MediaCodecBridge_configureVideo(env, bridge->media_codec(), - j_format.obj(), nullptr, nullptr, - kConfigureFlagEncode)) { - return nullptr; - } - - return bridge->Start() ? bridge.release() : nullptr; -} - -VideoCodecBridge::VideoCodecBridge(const std::string& mime, - bool is_secure, - MediaCodecDirection direction) - : SdkMediaCodecBridge(mime, is_secure, direction), - adaptive_playback_supported_for_testing_(-1) {} - -void VideoCodecBridge::SetVideoBitrate(int bps) { - JNIEnv* env = AttachCurrentThread(); - Java_MediaCodecBridge_setVideoBitrate(env, media_codec(), bps); -} - -void VideoCodecBridge::RequestKeyFrameSoon() { - JNIEnv* env = AttachCurrentThread(); - Java_MediaCodecBridge_requestKeyFrameSoon(env, media_codec()); -} - -bool VideoCodecBridge::IsAdaptivePlaybackSupported(int width, int height) { - if (adaptive_playback_supported_for_testing_ == 0) - return false; - else if (adaptive_playback_supported_for_testing_ > 0) - return true; - JNIEnv* env = AttachCurrentThread(); - return Java_MediaCodecBridge_isAdaptivePlaybackSupported(env, media_codec(), - width, height); -} - -} // namespace media diff --git a/media/base/android/sdk_media_codec_bridge.h b/media/base/android/sdk_media_codec_bridge.h deleted file mode 100644 index c495ffc9..0000000 --- a/media/base/android/sdk_media_codec_bridge.h +++ /dev/null @@ -1,198 +0,0 @@ -// Copyright (c) 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_ANDROID_SDK_MEDIA_CODEC_BRIDGE_H_ -#define MEDIA_BASE_ANDROID_SDK_MEDIA_CODEC_BRIDGE_H_ - -#include <jni.h> -#include <stdint.h> - -#include <set> -#include <string> - -#include "base/android/scoped_java_ref.h" -#include "base/macros.h" -#include "base/time/time.h" -#include "media/base/android/media_codec_bridge.h" -#include "media/base/audio_decoder_config.h" -#include "media/base/media_export.h" -#include "media/base/video_decoder_config.h" -#include "ui/gfx/geometry/size.h" - -namespace media { - -// This class implements MediaCodecBridge using android MediaCodec SDK APIs. -class MEDIA_EXPORT SdkMediaCodecBridge : public MediaCodecBridge { - public: - ~SdkMediaCodecBridge() override; - - // MediaCodecBridge implementations. - MediaCodecStatus Reset() override; - bool Start() override; - void Stop() override; - void GetOutputFormat(int* width, int* height) override; - int GetOutputSamplingRate() override; - MediaCodecStatus QueueInputBuffer( - int index, - const uint8_t* data, - size_t data_size, - const base::TimeDelta& presentation_time) override; - using MediaCodecBridge::QueueSecureInputBuffer; - MediaCodecStatus QueueSecureInputBuffer( - int index, - const uint8_t* data, - size_t data_size, - const std::vector<char>& key_id, - const std::vector<char>& iv, - const SubsampleEntry* subsamples, - int subsamples_size, - const base::TimeDelta& presentation_time) override; - void QueueEOS(int input_buffer_index) override; - MediaCodecStatus DequeueInputBuffer(const base::TimeDelta& timeout, - int* index) override; - MediaCodecStatus DequeueOutputBuffer(const base::TimeDelta& timeout, - int* index, - size_t* offset, - size_t* size, - base::TimeDelta* presentation_time, - 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; - - static bool RegisterSdkMediaCodecBridge(JNIEnv* env); - - protected: - SdkMediaCodecBridge(const std::string& mime, - bool is_secure, - 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); - - jobject media_codec() { return j_media_codec_.obj(); } - MediaCodecDirection direction_; - - private: - // Java MediaCodec instance. - base::android::ScopedJavaGlobalRef<jobject> j_media_codec_; - - DISALLOW_COPY_AND_ASSIGN(SdkMediaCodecBridge); -}; - -// Class for handling audio decoding using android MediaCodec APIs. -// TODO(qinmin): implement the logic to switch between NDK and SDK -// MediaCodecBridge. -class MEDIA_EXPORT AudioCodecBridge : public SdkMediaCodecBridge { - public: - // Returns an AudioCodecBridge instance if |codec| is supported, or a NULL - // pointer otherwise. - static AudioCodecBridge* Create(const AudioCodec& codec); - - // See MediaCodecUtil::IsKnownUnaccelerated(). - static bool IsKnownUnaccelerated(const AudioCodec& codec); - - // Start the audio codec bridge. - bool ConfigureAndStart(const AudioCodec& codec, - int sample_rate, - int channel_count, - const uint8_t* extra_data, - size_t extra_data_size, - int64_t codec_delay_ns, - int64_t seek_preroll_ns, - bool play_audio, - jobject media_crypto) WARN_UNUSED_RESULT; - - // 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. - // 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); - - // Set the volume of the audio output. - void SetVolume(double volume); - - private: - explicit AudioCodecBridge(const std::string& mime); - - // Configure the java MediaFormat object with the extra codec data passed in. - bool ConfigureMediaFormat(jobject j_format, - const AudioCodec& codec, - const uint8_t* extra_data, - size_t extra_data_size, - int64_t codec_delay_ns, - int64_t seek_preroll_ns); -}; - -// Class for handling video encoding/decoding using android MediaCodec APIs. -// TODO(qinmin): implement the logic to switch between NDK and SDK -// MediaCodecBridge. -class MEDIA_EXPORT VideoCodecBridge : public SdkMediaCodecBridge { - public: - // See MediaCodecUtil::IsKnownUnaccelerated(). - static bool IsKnownUnaccelerated(const VideoCodec& codec, - MediaCodecDirection direction); - - // 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. - - // Create, start, and return a VideoCodecBridge encoder or NULL on failure. - static VideoCodecBridge* CreateEncoder( - const VideoCodec& codec, // e.g. media::kCodecVP8 - const gfx::Size& size, // input frame size - int bit_rate, // bits/second - int frame_rate, // frames/second - int i_frame_interval, // count - int color_format); // MediaCodecInfo.CodecCapabilities. - - void SetVideoBitrate(int bps); - void RequestKeyFrameSoon(); - - // Returns whether adaptive playback is supported for this object given - // the new size. - bool IsAdaptivePlaybackSupported(int width, int height); - - // Test-only method to set the return value of IsAdaptivePlaybackSupported(). - // Without this function, the return value of that function will be device - // dependent. If |adaptive_playback_supported| is equal to 0, the return value - // will be false. If |adaptive_playback_supported| is larger than 0, the - // return value will be true. - void set_adaptive_playback_supported_for_testing( - int adaptive_playback_supported) { - adaptive_playback_supported_for_testing_ = adaptive_playback_supported; - } - - private: - VideoCodecBridge(const std::string& mime, - bool is_secure, - MediaCodecDirection direction); - - int adaptive_playback_supported_for_testing_; -}; - -} // namespace media - -#endif // MEDIA_BASE_ANDROID_SDK_MEDIA_CODEC_BRIDGE_H_ diff --git a/media/base/android/sdk_media_codec_bridge_unittest.cc b/media/base/android/sdk_media_codec_bridge_unittest.cc deleted file mode 100644 index 2f6274f..0000000 --- a/media/base/android/sdk_media_codec_bridge_unittest.cc +++ /dev/null @@ -1,300 +0,0 @@ -// 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 <string> - -#include "base/basictypes.h" -#include "base/logging.h" -#include "base/memory/scoped_ptr.h" -#include "media/base/android/media_codec_util.h" -#include "media/base/android/sdk_media_codec_bridge.h" -#include "media/base/decoder_buffer.h" -#include "media/base/test_data_util.h" -#include "testing/gmock/include/gmock/gmock.h" - -namespace { - -// The first frame of -// http://www.html5rocks.com/en/tutorials/audio/quick/test.mp3 -unsigned char test_mp3[] = { - 0xff, 0xfb, 0xd2, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0x05, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0d, 0x20, 0x00, 0x00, 0x00, 0x2a, 0x7e, 0x40, - 0xc0, 0x19, 0x4a, 0x80, 0x0d, 0x60, 0x48, 0x1b, 0x40, 0xf7, 0xbd, 0xb9, - 0xd9, 0x40, 0x6f, 0x82, 0x01, 0x8b, 0x17, 0xa0, 0x80, 0xc5, 0x01, 0xad, - 0x9a, 0xd3, 0x00, 0x12, 0xc0, 0x72, 0x93, 0x67, 0xd0, 0x03, 0x6f, 0xa4, - 0xc0, 0xc3, 0x23, 0xee, 0x9b, 0xc0, 0xcc, 0x02, 0xa0, 0xa1, 0x30, 0x0c, - 0x52, 0x2d, 0xfd, 0x6e, 0x08, 0x83, 0x60, 0x40, 0x46, 0x06, 0x4b, 0x20, - 0x82, 0x82, 0x7f, 0xd4, 0x81, 0xe7, 0x00, 0x64, 0x20, 0x18, 0xec, 0xc2, - 0x06, 0x57, 0x0f, 0x81, 0x93, 0x0b, 0x00, 0x66, 0xe3, 0xb7, 0xe8, 0x32, - 0x6e, 0xf0, 0x32, 0xb0, 0x58, 0x0c, 0x7c, 0x3a, 0x03, 0x22, 0x14, 0x80, - 0xc9, 0x01, 0x80, 0x30, 0x20, 0x14, 0x0c, 0x96, 0x73, 0xfe, 0x9f, 0x6c, - 0x0c, 0xd2, 0x25, 0x0f, 0xdc, 0x0c, 0x32, 0x43, 0x03, 0x27, 0x87, 0xc0, - 0xc2, 0xc0, 0x20, 0xfc, 0x42, 0xc5, 0xff, 0xff, 0xd4, 0x80, 0x01, 0x01, - 0x80, 0xc3, 0x81, 0x01, 0x95, 0x03, 0x28, 0x82, 0xc0, 0xc3, 0x01, 0xa1, - 0x06, 0x81, 0x87, 0xc2, 0x40, 0x64, 0xc1, 0xf0, 0x12, 0x02, 0xff, 0xf6, - 0x5b, 0x9f, 0x44, 0xdc, 0xdd, 0x0b, 0x38, 0x59, 0xe0, 0x31, 0x71, 0x60, - 0x0c, 0xb4, 0x22, 0x03, 0x3b, 0x96, 0x40, 0xc8, 0x63, 0x90, 0x0a, 0x23, - 0x81, 0x9e, 0x4c, 0x20, 0x65, 0xb3, 0x18, 0x19, 0x6c, 0x42, 0x06, 0x36, - 0x1d, 0x01, 0x90, 0x87, 0xdf, 0xff, 0xd0, 0x65, 0xa6, 0xea, 0x66, 0xfd, - 0x40, 0x0c, 0x48, 0x03, 0x1a, 0x09, 0x01, 0x21, 0x98, 0x19, 0x2c, 0x36, - 0x06, 0x43, 0x21, 0x81, 0x92, 0xca, 0x60, 0x64, 0x70, 0xb8, 0x19, 0x20, - 0x6c, 0x02, 0x83, 0x80, 0xcb, 0x60, 0x65, 0x32, 0x28, 0x18, 0x64, 0x24, - 0x06, 0x3a, 0x0c, 0x00, 0xe1, 0x00, 0x18, 0xd0, 0x35, 0xff, 0xff, 0xff, - 0xe8, 0x32, 0xef, 0xb2, 0x90, 0x65, 0xbb, 0xdd, 0x94, 0x82, 0x0b, 0x4c, - 0xfa, 0x25, 0xf3, 0x74, 0x13, 0x0f, 0xf8, 0x19, 0x28, 0x84, 0x06, 0x36, - 0x11, 0x01, 0x20, 0x80, 0x18, 0xb4, 0x52, 0x0e, 0x15, 0x00, 0x30, 0x50, - 0x0c, 0x84, 0x32, 0x03, 0x11, 0x04, 0x03, 0x48, 0x04, 0x00, 0x00, 0x31, - 0x21, 0x00, 0x0c, 0x84, 0x18, 0x03, 0x07, 0x85, 0x40, 0xc6, 0xa5, 0x70, - 0x32, 0xb8, 0x7c, 0x0c, 0x54, 0x04, 0x00, 0xd0, 0x08, 0x59, 0x58, 0x18, - 0x20, 0x14, 0x06, 0x30, 0x30, 0x01, 0x9b, 0x86, 0x00, 0x6b, 0x54, 0xa8, - 0x19, 0x8c, 0x2a, 0x06, 0x16, 0x09, 0x01, 0xa0, 0xd0, 0xa0, 0x69, 0x74, - 0xb8, 0x19, 0xc4, 0x4a, 0xa3, 0xda, 0x9d, 0x1e, 0x4f, 0x05, 0xc0, 0x5b, - 0x0b, 0x03, 0xc2, 0x76, 0xa3, 0x4f, 0xb9, 0x16, 0xc2, 0x70, 0x41, 0x07, - 0xa0, 0x84, 0x16, 0x38, 0x4a, 0xc8, 0xaf, 0xee, 0x7f, 0x93, 0xb5, 0x5c, - 0x39, 0x1e, 0x29, 0xd9, 0x8c, 0x80, 0xb5, 0x80, 0xe6, 0x85, 0xb2, 0x99, - 0x68, 0x85, 0x46, 0x91, 0x60, 0xdb, 0x06, 0xfa, 0x38, 0x7a, 0xc7, 0xac, - 0x85, 0xa8, 0xd3, 0xe6, 0x99, 0x3b, 0x66, 0x43, 0x23, 0x1f, 0x84, 0xe1, - 0x65, 0x5e, 0xbc, 0x84, 0x18, 0x62, 0xe6, 0x42, 0x0b, 0x82, 0xe4, 0xd3, - 0x42, 0xd2, 0x05, 0x81, 0x4e, 0xe4, 0x9f, 0x8c, 0xc8, 0x7f, 0xa3, 0xe0, - 0x8d, 0xf1, 0x0f, 0x38, 0xe5, 0x3f, 0xc4, 0x2c, 0x24, 0x65, 0x8d, 0xb9, - 0x58, 0xac, 0x39, 0x0e, 0x37, 0x99, 0x2e, 0x85, 0xe0, 0xb7, 0x98, 0x41, - 0x20, 0x38, 0x1b, 0x95, 0x07, 0xfa, 0xa8, 0x9c, 0x21, 0x0f, 0x13, 0x8c, - 0xa5, 0xc1, 0x76, 0xae, 0x0b, 0xc1, 0x30, 0x27, 0x08, 0xc1, 0xf6, 0x4d, - 0xce, 0xb4, 0x41, 0x38, 0x1e, 0x82, 0x10, 0x74, 0x45, 0x91, 0x90, 0xff, - 0x41, 0x8b, 0x62, 0x1a, 0x71, 0xb6, 0x45, 0x63, 0x8c, 0xce, 0xb8, 0x54, - 0x1b, 0xe8, 0x5d, 0x9e, 0x35, 0x9d, 0x6c, 0xac, 0xe8, 0x83, 0xa1, 0xe9, - 0x3f, 0x13, 0x74, 0x11, 0x04, 0x10, 0xf1, 0x37, 0x38, 0xc6, 0x00, 0x60, - 0x27, 0x48, 0x38, 0x85, 0x92, 0x76, 0xb7, 0xf3, 0xa7, 0x1c, 0x4b, 0xf9, - 0x3b, 0x5a, 0x88, 0xac, 0x60, 0x1b, 0x85, 0x81, 0x16, 0xab, 0x44, 0x17, - 0x08, 0x2e, 0x0f, 0xd4, 0xe2, 0xde, 0x49, 0xc9, 0xe1, 0xc0, 0xc0, 0xa0, - 0x7e, 0x73, 0xa1, 0x67, 0xf8, 0xf5, 0x9f, 0xc4, 0x21, 0x50, 0x4f, 0x05, - 0x2c, 0xfc, 0x5c, 0xaa, 0x85, 0xb0, 0xfa, 0x67, 0x80, 0x7e, 0x0f, 0xfd, - 0x92, 0x30, 0xd5, 0xa0, 0xd4, 0x05, 0xdd, 0x06, 0x68, 0x1d, 0x6e, 0x4e, - 0x8b, 0x79, 0xd6, 0xfc, 0xff, 0x2e, 0x6e, 0x7c, 0xba, 0x03, 0x90, 0xd4, - 0x25, 0x65, 0x8e, 0xe7, 0x3a, 0xd1, 0xd6, 0xdc, 0xf0, 0xbe, 0x12, 0xc4, - 0x31, 0x08, 0x16, 0x70, 0x31, 0x85, 0x61, 0x38, 0x27, 0x0a, 0x91, 0x5f, - 0x03, 0x38, 0xeb, 0x37, 0x13, 0x48, 0x41, 0xbe, 0x7f, 0x04, 0x70, 0x62, - 0x2b, 0x15, 0x91, 0x67, 0x63, 0x4f, 0xad, 0xa7, 0x1d, 0x3f, 0x44, 0x17, - 0x02, 0x08, 0x0d, 0xf2, 0xfc, 0x03, 0xa0, 0x74, 0x21, 0x8b, 0x07, 0x3a, - 0x8d, 0x0f, 0x54, 0x58, 0x94, 0x12, 0xc5, 0x62, 0x18, 0xb9, 0x42, 0xf0, - 0x6c, 0x73, 0xa0, 0x92, 0xad, 0x27, 0x1c, 0x20, 0x0f, 0xc1, 0xca, 0x44, - 0x87, 0x47, 0xc5, 0x43, 0x23, 0x01, 0xda, 0x23, 0xe2, 0x89, 0x38, 0x9f, - 0x1f, 0x8d, 0x8c, 0xc6, 0x95, 0xa3, 0x34, 0x21, 0x21, 0x2d, 0x49, 0xea, - 0x4b, 0x05, 0x85, 0xf5, 0x58, 0x25, 0x13, 0xcd, 0x51, 0x19, 0x1a, 0x88, - 0xa6, 0x83, 0xd6, 0xd0, 0xbc, 0x25, 0x19, 0x1c, 0x92, 0x12, 0x44, 0x5d, - 0x1c, 0x04, 0xf1, 0x99, 0xdf, 0x92, 0x8e, 0x09, 0x85, 0xf3, 0x88, 0x82, - 0x4c, 0x22, 0x17, 0xc5, 0x25, 0x23, 0xed, 0x78, 0xf5, 0x41, 0xd1, 0xe9, - 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}; - -} // namespace - -namespace media { - -// Helper macro to skip the test if MediaCodecBridge isn't available. -#define SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE() \ - do { \ - if (!MediaCodecUtil::IsMediaCodecAvailable()) { \ - VLOG(0) << "Could not run test - not supported on device."; \ - return; \ - } \ - } while (0) - -static const int kPresentationTimeBase = 100; - -static inline const base::TimeDelta InfiniteTimeOut() { - return base::TimeDelta::FromMicroseconds(-1); -} - -void DecodeMediaFrame(VideoCodecBridge* media_codec, - const uint8* data, - size_t data_size, - const base::TimeDelta input_presentation_timestamp, - const base::TimeDelta initial_timestamp_lower_bound) { - base::TimeDelta input_pts = input_presentation_timestamp; - base::TimeDelta timestamp = initial_timestamp_lower_bound; - base::TimeDelta new_timestamp; - for (int i = 0; i < 10; ++i) { - int input_buf_index = -1; - MediaCodecStatus status = - media_codec->DequeueInputBuffer(InfiniteTimeOut(), &input_buf_index); - ASSERT_EQ(MEDIA_CODEC_OK, status); - - media_codec->QueueInputBuffer(input_buf_index, data, data_size, - input_presentation_timestamp); - - size_t unused_offset = 0; - size_t size = 0; - bool eos = false; - int output_buf_index = -1; - status = media_codec->DequeueOutputBuffer( - InfiniteTimeOut(), &output_buf_index, &unused_offset, &size, - &new_timestamp, &eos, nullptr); - - if (status == MEDIA_CODEC_OK && output_buf_index > 0) { - media_codec->ReleaseOutputBuffer(output_buf_index, false); - } - // Output time stamp should not be smaller than old timestamp. - ASSERT_TRUE(new_timestamp >= timestamp); - input_pts += base::TimeDelta::FromMicroseconds(33000); - timestamp = new_timestamp; - } -} - -TEST(SdkMediaCodecBridgeTest, Initialize) { - SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE(); - - scoped_ptr<media::MediaCodecBridge> media_codec; - media_codec.reset(VideoCodecBridge::CreateDecoder( - kCodecH264, false, gfx::Size(640, 480), nullptr, nullptr)); -} - -TEST(SdkMediaCodecBridgeTest, DoNormal) { - SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE(); - - scoped_ptr<media::AudioCodecBridge> media_codec; - media_codec.reset(AudioCodecBridge::Create(kCodecMP3)); - - ASSERT_TRUE(media_codec->ConfigureAndStart(kCodecMP3, 44100, 2, nullptr, 0, 0, - 0, false, nullptr)); - - int input_buf_index = -1; - MediaCodecStatus status = - media_codec->DequeueInputBuffer(InfiniteTimeOut(), &input_buf_index); - ASSERT_EQ(MEDIA_CODEC_OK, status); - ASSERT_GE(input_buf_index, 0); - - int64 input_pts = kPresentationTimeBase; - media_codec->QueueInputBuffer(input_buf_index, test_mp3, sizeof(test_mp3), - base::TimeDelta::FromMicroseconds(++input_pts)); - - status = media_codec->DequeueInputBuffer(InfiniteTimeOut(), &input_buf_index); - media_codec->QueueInputBuffer(input_buf_index, test_mp3, sizeof(test_mp3), - base::TimeDelta::FromMicroseconds(++input_pts)); - - status = media_codec->DequeueInputBuffer(InfiniteTimeOut(), &input_buf_index); - media_codec->QueueEOS(input_buf_index); - - input_pts = kPresentationTimeBase; - bool eos = false; - while (!eos) { - size_t unused_offset = 0; - size_t size = 0; - base::TimeDelta timestamp; - int output_buf_index = -1; - status = media_codec->DequeueOutputBuffer(InfiniteTimeOut(), - &output_buf_index, &unused_offset, - &size, ×tamp, &eos, nullptr); - switch (status) { - case MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER: - FAIL(); - return; - - case MEDIA_CODEC_OUTPUT_FORMAT_CHANGED: - continue; - - case MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED: - continue; - - default: - break; - } - ASSERT_GE(output_buf_index, 0); - EXPECT_LE(1u, size); - if (!eos) - EXPECT_EQ(++input_pts, timestamp.InMicroseconds()); - ASSERT_LE(input_pts, kPresentationTimeBase + 2); - } - ASSERT_EQ(input_pts, kPresentationTimeBase + 2); -} - -TEST(SdkMediaCodecBridgeTest, InvalidVorbisHeader) { - SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE(); - - scoped_ptr<media::AudioCodecBridge> media_codec; - media_codec.reset(AudioCodecBridge::Create(kCodecVorbis)); - - // The first byte of the header is not 0x02. - uint8 invalid_first_byte[] = {0x00, 0xff, 0xff, 0xff, 0xff}; - EXPECT_FALSE(media_codec->ConfigureAndStart( - kCodecVorbis, 44100, 2, invalid_first_byte, sizeof(invalid_first_byte), 0, - 0, false, nullptr)); - - // Size of the header does not match with the data we passed in. - uint8 invalid_size[] = {0x02, 0x01, 0xff, 0x01, 0xff}; - EXPECT_FALSE(media_codec->ConfigureAndStart( - kCodecVorbis, 44100, 2, invalid_size, sizeof(invalid_size), 0, 0, false, - nullptr)); - - // Size of the header is too large. - size_t large_size = 8 * 1024 * 1024 + 2; - uint8* very_large_header = new uint8[large_size]; - very_large_header[0] = 0x02; - for (size_t i = 1; i < large_size - 1; ++i) - very_large_header[i] = 0xff; - very_large_header[large_size - 1] = 0xfe; - EXPECT_FALSE(media_codec->ConfigureAndStart(kCodecVorbis, 44100, 2, - very_large_header, 0x80000000, 0, - 0, false, nullptr)); - delete[] very_large_header; -} - -TEST(SdkMediaCodecBridgeTest, InvalidOpusHeader) { - SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE(); - - scoped_ptr<media::AudioCodecBridge> media_codec; - media_codec.reset(AudioCodecBridge::Create(kCodecOpus)); - uint8 dummy_extra_data[] = {0, 0}; - - // Extra Data is NULL. - EXPECT_FALSE(media_codec->ConfigureAndStart(kCodecOpus, 48000, 2, nullptr, 0, - -1, 0, false, nullptr)); - - // Codec Delay is < 0. - EXPECT_FALSE(media_codec->ConfigureAndStart( - kCodecOpus, 48000, 2, dummy_extra_data, sizeof(dummy_extra_data), -1, 0, - false, nullptr)); - - // Seek Preroll is < 0. - EXPECT_FALSE(media_codec->ConfigureAndStart( - kCodecOpus, 48000, 2, dummy_extra_data, sizeof(dummy_extra_data), 0, -1, - false, nullptr)); -} - -TEST(SdkMediaCodecBridgeTest, PresentationTimestampsDoNotDecrease) { - SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE(); - - scoped_ptr<VideoCodecBridge> media_codec(VideoCodecBridge::CreateDecoder( - kCodecVP8, false, gfx::Size(320, 240), nullptr, nullptr)); - EXPECT_TRUE(media_codec.get()); - scoped_refptr<DecoderBuffer> buffer = ReadTestDataFile("vp8-I-frame-320x240"); - DecodeMediaFrame(media_codec.get(), buffer->data(), buffer->data_size(), - base::TimeDelta(), base::TimeDelta()); - - // Simulate a seek to 10 seconds, and each chunk has 2 I-frames. - std::vector<uint8> chunk(buffer->data(), - buffer->data() + buffer->data_size()); - chunk.insert(chunk.end(), buffer->data(), - buffer->data() + buffer->data_size()); - media_codec->Reset(); - DecodeMediaFrame(media_codec.get(), &chunk[0], chunk.size(), - base::TimeDelta::FromMicroseconds(10000000), - base::TimeDelta::FromMicroseconds(9900000)); - - // Simulate a seek to 5 seconds. - media_codec->Reset(); - DecodeMediaFrame(media_codec.get(), &chunk[0], chunk.size(), - base::TimeDelta::FromMicroseconds(5000000), - base::TimeDelta::FromMicroseconds(4900000)); -} - -TEST(SdkMediaCodecBridgeTest, CreateUnsupportedCodec) { - EXPECT_EQ(nullptr, AudioCodecBridge::Create(kUnknownAudioCodec)); - EXPECT_EQ(nullptr, VideoCodecBridge::CreateDecoder(kUnknownVideoCodec, false, - gfx::Size(320, 240), - nullptr, nullptr)); -} - -} // namespace media diff --git a/media/base/android/video_decoder_job.cc b/media/base/android/video_decoder_job.cc index 3dfe3b8..cec1b39 100644 --- a/media/base/android/video_decoder_job.cc +++ b/media/base/android/video_decoder_job.cc @@ -7,8 +7,8 @@ #include "base/bind.h" #include "base/lazy_instance.h" #include "base/threading/thread.h" +#include "media/base/android/media_codec_bridge.h" #include "media/base/android/media_drm_bridge.h" -#include "media/base/android/sdk_media_codec_bridge.h" namespace media { diff --git a/media/media.gyp b/media/media.gyp index 70be514..dbd452d 100644 --- a/media/media.gyp +++ b/media/media.gyp @@ -1187,12 +1187,12 @@ ], 'sources': [ 'base/android/access_unit_queue_unittest.cc', + 'base/android/media_codec_bridge_unittest.cc', 'base/android/media_codec_decoder_unittest.cc', 'base/android/media_codec_player_unittest.cc', 'base/android/media_drm_bridge_unittest.cc', 'base/android/media_player_bridge_unittest.cc', 'base/android/media_source_player_unittest.cc', - 'base/android/sdk_media_codec_bridge_unittest.cc', 'base/android/test_data_factory.cc', 'base/android/test_data_factory.h', 'base/android/test_statistics.h', @@ -1813,7 +1813,6 @@ 'base/android/java/src/org/chromium/media/AudioManagerAndroid.java', 'base/android/java/src/org/chromium/media/AudioRecordInput.java', 'base/android/java/src/org/chromium/media/MediaCodecBridge.java', - 'base/android/java/src/org/chromium/media/MediaCodecUtil.java', 'base/android/java/src/org/chromium/media/MediaDrmBridge.java', 'base/android/java/src/org/chromium/media/MediaPlayerBridge.java', 'base/android/java/src/org/chromium/media/MediaPlayerListener.java', @@ -1863,8 +1862,6 @@ 'base/android/media_codec_player.h', 'base/android/media_codec_video_decoder.cc', 'base/android/media_codec_video_decoder.h', - 'base/android/media_codec_util.cc', - 'base/android/media_codec_util.h', 'base/android/media_common_android.h', 'base/android/media_decoder_job.cc', 'base/android/media_decoder_job.h', @@ -1891,28 +1888,12 @@ 'base/android/media_task_runner.h', 'base/android/media_url_interceptor.h', 'base/android/provision_fetcher.h', - 'base/android/sdk_media_codec_bridge.cc', - 'base/android/sdk_media_codec_bridge.h', 'base/android/video_decoder_job.cc', 'base/android/video_decoder_job.h', 'base/android/webaudio_media_codec_bridge.cc', 'base/android/webaudio_media_codec_bridge.h', 'base/android/webaudio_media_codec_info.h', ], - 'conditions': [ - # Only 64 bit builds are using android-21 NDK library, check common.gypi - ['target_arch=="arm64" or target_arch=="x64" or target_arch=="mips64el"', { - 'sources': [ - 'base/android/ndk_media_codec_bridge.cc', - 'base/android/ndk_media_codec_bridge.h', - ], - 'link_settings': { - 'libraries': [ - '-lmediandk', - ], - }, - }], - ], 'dependencies': [ '../base/base.gyp:base', '../third_party/widevine/cdm/widevine_cdm.gyp:widevine_cdm_version_h', |