summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorqinmin <qinmin@chromium.org>2015-12-06 10:56:13 -0800
committerCommit bot <commit-bot@chromium.org>2015-12-06 18:57:19 +0000
commit5d52e0f8e63a294179df028a0dabc76a042cf9ec (patch)
tree3dc77dffcfb06283efe6bf54b78754e9931c474e
parentb6adea99ac09da288ebfa137466727c306bfb9fa (diff)
downloadchromium_src-5d52e0f8e63a294179df028a0dabc76a042cf9ec.zip
chromium_src-5d52e0f8e63a294179df028a0dabc76a042cf9ec.tar.gz
chromium_src-5d52e0f8e63a294179df028a0dabc76a042cf9ec.tar.bz2
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 Review URL: https://codereview.chromium.org/1472943002 Cr-Commit-Position: refs/heads/master@{#363374}
-rw-r--r--components/cdm/browser/cdm_message_filter_android.cc5
-rw-r--r--content/child/runtime_features.cc8
-rw-r--r--content/common/gpu/media/android_video_decode_accelerator.h2
-rw-r--r--content/common/gpu/media/android_video_decode_accelerator_unittest.cc4
-rw-r--r--content/common/gpu/media/android_video_encode_accelerator.cc9
-rw-r--r--content/common/gpu/media/android_video_encode_accelerator.h2
-rw-r--r--content/common/gpu/media/avda_shared_state.h2
-rw-r--r--content/renderer/media/webrtc/peer_connection_dependency_factory.cc4
-rw-r--r--media/BUILD.gn10
-rw-r--r--media/base/android/BUILD.gn7
-rw-r--r--media/base/android/audio_decoder_job.cc12
-rw-r--r--media/base/android/java/src/org/chromium/media/MediaCodecBridge.java144
-rw-r--r--media/base/android/java/src/org/chromium/media/MediaCodecUtil.java231
-rw-r--r--media/base/android/media_codec_audio_decoder.cc17
-rw-r--r--media/base/android/media_codec_bridge.cc843
-rw-r--r--media/base/android/media_codec_bridge.h245
-rw-r--r--media/base/android/media_codec_bridge_unittest.cc319
-rw-r--r--media/base/android/media_codec_decoder.cc4
-rw-r--r--media/base/android/media_codec_decoder_unittest.cc12
-rw-r--r--media/base/android/media_codec_player_unittest.cc12
-rw-r--r--media/base/android/media_codec_util.cc225
-rw-r--r--media/base/android/media_codec_util.h63
-rw-r--r--media/base/android/media_codec_video_decoder.cc2
-rw-r--r--media/base/android/media_decoder_job.cc8
-rw-r--r--media/base/android/media_jni_registrar.cc34
-rw-r--r--media/base/android/media_source_player_unittest.cc12
-rw-r--r--media/base/android/ndk_media_codec_bridge.cc228
-rw-r--r--media/base/android/ndk_media_codec_bridge.h79
-rw-r--r--media/base/android/sdk_media_codec_bridge.cc638
-rw-r--r--media/base/android/sdk_media_codec_bridge.h196
-rw-r--r--media/base/android/sdk_media_codec_bridge_unittest.cc300
-rw-r--r--media/base/android/video_decoder_job.cc2
-rw-r--r--media/media.gyp21
33 files changed, 2112 insertions, 1588 deletions
diff --git a/components/cdm/browser/cdm_message_filter_android.cc b/components/cdm/browser/cdm_message_filter_android.cc
index e9399f6..14043e1 100644
--- a/components/cdm/browser/cdm_message_filter_android.cc
+++ b/components/cdm/browser/cdm_message_filter_android.cc
@@ -9,11 +9,10 @@
#include "components/cdm/common/cdm_messages_android.h"
#include "ipc/ipc_message_macros.h"
-#include "media/base/android/media_codec_bridge.h"
+#include "media/base/android/media_codec_util.h"
#include "media/base/android/media_drm_bridge.h"
using content::BrowserThread;
-using media::MediaCodecBridge;
using media::MediaDrmBridge;
using media::SupportedCodecs;
@@ -59,7 +58,7 @@ static SupportedCodecs GetSupportedCodecs(
if ((request.codecs & info.codec) &&
MediaDrmBridge::IsKeySystemSupportedWithType(
key_system, info.container_mime_type) &&
- MediaCodecBridge::CanDecode(info.codec_name, is_secure)) {
+ media::MediaCodecUtil::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 be71402..691b8b9 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_bridge.h"
+#include "media/base/android/media_codec_util.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::MediaCodecBridge::IsAvailable()) {
+ if (!media::MediaCodecUtil::IsMediaCodecAvailable()) {
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::MediaCodecBridge::IsAvailable() &&
+ media::MediaCodecUtil::IsMediaCodecAvailable() &&
((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::MediaCodecBridge::IsAvailable());
+ media::MediaCodecUtil::IsMediaCodecAvailable());
#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 2e8058a..945a4d0 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/media_codec_bridge.h"
+#include "media/base/android/sdk_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 2e0e7ac2..c1e91f1 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_bridge.h"
+#include "media/base/android/media_codec_util.h"
#include "media/base/android/media_jni_registrar.h"
#include "media/video/picture.h"
#include "media/video/video_decode_accelerator.h"
@@ -92,7 +92,7 @@ TEST_F(AndroidVideoDecodeAcceleratorTest, ConfigureUnsupportedCodec) {
}
TEST_F(AndroidVideoDecodeAcceleratorTest, ConfigureSupportedCodec) {
- if (!media::MediaCodecBridge::IsAvailable())
+ if (!media::MediaCodecUtil::IsMediaCodecAvailable())
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 1cb6e54..e439203 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_bridge.h"
+#include "media/base/android/media_codec_util.h"
#include "media/base/bitstream_buffer.h"
#include "media/base/limits.h"
#include "media/video/picture.h"
@@ -22,7 +22,6 @@
#include "ui/gl/android/scoped_java_surface.h"
#include "ui/gl/gl_bindings.h"
-using media::MediaCodecBridge;
using media::VideoCodecBridge;
using media::VideoFrame;
@@ -83,7 +82,7 @@ static bool GetSupportedColorFormatForMime(const std::string& mime,
if (mime.empty())
return false;
- std::set<int> formats = MediaCodecBridge::GetEncoderColorFormats(mime);
+ std::set<int> formats = media::MediaCodecUtil::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)
@@ -157,8 +156,8 @@ bool AndroidVideoEncodeAccelerator::Initialize(
client_ptr_factory_.reset(new base::WeakPtrFactory<Client>(client));
- if (!(media::MediaCodecBridge::SupportsSetParameters() &&
- format == media::PIXEL_FORMAT_I420)) {
+ if (!(media::MediaCodecUtil::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 98fa3c2..367ecf8 100644
--- a/content/common/gpu/media/android_video_encode_accelerator.h
+++ b/content/common/gpu/media/android_video_encode_accelerator.h
@@ -14,7 +14,7 @@
#include "base/timer/timer.h"
#include "base/tuple.h"
#include "content/common/content_export.h"
-#include "media/base/android/media_codec_bridge.h"
+#include "media/base/android/sdk_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 a64bf3a..474037d 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/media_codec_bridge.h"
+#include "media/base/android/sdk_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 4d20f5e..869a4a5 100644
--- a/content/renderer/media/webrtc/peer_connection_dependency_factory.cc
+++ b/content/renderer/media/webrtc/peer_connection_dependency_factory.cc
@@ -62,7 +62,7 @@
#include "third_party/webrtc/base/ssladapter.h"
#if defined(OS_ANDROID)
-#include "media/base/android/media_codec_bridge.h"
+#include "media/base/android/media_codec_util.h"
#endif
namespace content {
@@ -415,7 +415,7 @@ void PeerConnectionDependencyFactory::InitializeSignalingThread(
}
#if defined(OS_ANDROID)
- if (!media::MediaCodecBridge::SupportsSetParameters())
+ if (!media::MediaCodecUtil::SupportsSetParameters())
encoder_factory.reset();
#endif
diff --git a/media/BUILD.gn b/media/BUILD.gn
index 4d7e198..d53cb64 100644
--- a/media/BUILD.gn
+++ b/media/BUILD.gn
@@ -395,6 +395,16 @@ component("media") {
"//media/base/android:media_jni_headers",
"//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 += [ "-lmediandk" ]
+ }
allow_circular_includes_from = [ "//media/base/android" ]
}
diff --git a/media/base/android/BUILD.gn b/media/base/android/BUILD.gn
index c51ee6b..75c6717 100644
--- a/media/base/android/BUILD.gn
+++ b/media/base/android/BUILD.gn
@@ -31,6 +31,8 @@ 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",
@@ -58,6 +60,8 @@ 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",
@@ -81,12 +85,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",
@@ -106,6 +110,7 @@ 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 4ef622a..06f728a 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/media_codec_bridge.h"
+#include "media/base/android/sdk_media_codec_bridge.h"
#include "media/base/audio_timestamp_helper.h"
#include "media/base/timestamp_constants.h"
@@ -161,10 +161,12 @@ MediaDecoderJob::MediaDecoderJobStatus
if (!media_codec_bridge_)
return STATUS_FAILURE;
- 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().obj())) {
+ 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().obj())) {
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 72c3964..3a62582 100644
--- a/media/base/android/java/src/org/chromium/media/MediaCodecBridge.java
+++ b/media/base/android/java/src/org/chromium/media/MediaCodecBridge.java
@@ -22,9 +22,6 @@ 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
@@ -47,10 +44,6 @@ 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;
@@ -100,37 +93,6 @@ 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;
@@ -180,105 +142,6 @@ 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();
@@ -321,8 +184,9 @@ class MediaCodecBridge {
boolean adaptivePlaybackSupported = false;
try {
// |isSecure| only applies to video decoders.
- if (mime.startsWith("video") && isSecure && direction == MEDIA_CODEC_DECODER) {
- String decoderName = getDecoderNameForMime(mime);
+ if (mime.startsWith("video") && isSecure
+ && direction == MediaCodecUtil.MEDIA_CODEC_DECODER) {
+ String decoderName = MediaCodecUtil.getDecoderNameForMime(mime);
if (decoderName == null) {
return null;
}
@@ -336,7 +200,7 @@ class MediaCodecBridge {
}
mediaCodec = MediaCodec.createByCodecName(decoderName + ".secure");
} else {
- if (direction == MEDIA_CODEC_ENCODER) {
+ if (direction == MediaCodecUtil.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
new file mode 100644
index 0000000..b498b29
--- /dev/null
+++ b/media/base/android/java/src/org/chromium/media/MediaCodecUtil.java
@@ -0,0 +1,231 @@
+// 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 4b8a076..f68a07e 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,16 +144,11 @@ MediaCodecDecoder::ConfigStatus MediaCodecAudioDecoder::ConfigureInternal(
return kConfigFailure;
if (!(static_cast<AudioCodecBridge*>(media_codec_bridge_.get()))
- ->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)) {
+ ->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)) {
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 9bae69e..c242dba 100644
--- a/media/base/android/media_codec_bridge.cc
+++ b/media/base/android/media_codec_bridge.cc
@@ -11,13 +11,8 @@
#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;
@@ -26,320 +21,11 @@ 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 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();
-
- 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;
+MediaCodecBridge::MediaCodecBridge() {}
- 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>(kint32max))
- 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));
-}
+MediaCodecBridge::~MediaCodecBridge() {}
MediaCodecStatus MediaCodecBridge::QueueSecureInputBuffer(
int index,
@@ -349,204 +35,25 @@ MediaCodecStatus MediaCodecBridge::QueueSecureInputBuffer(
const std::string& iv,
const std::vector<SubsampleEntry>& subsamples,
const base::TimeDelta& 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>(kint32max))
- 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);
+ 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);
}
int MediaCodecBridge::GetOutputBuffersCount() {
- JNIEnv* env = AttachCurrentThread();
- return Java_MediaCodecBridge_getOutputBuffersCount(env, j_media_codec_.obj());
+ return 0;
}
size_t MediaCodecBridge::GetOutputBuffersCapacity() {
- 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;
+ return 0;
}
bool MediaCodecBridge::FillInputBuffer(int index,
const uint8* data,
size_t size) {
- uint8* dst = NULL;
+ uint8* dst = nullptr;
size_t capacity = 0;
GetInputBuffer(index, &dst, &capacity);
CHECK(dst);
@@ -561,334 +68,4 @@ 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 73f070d..0719448 100644
--- a/media/base/android/media_codec_bridge.h
+++ b/media/base/android/media_codec_bridge.h
@@ -5,15 +5,14 @@
#ifndef MEDIA_BASE_ANDROID_MEDIA_CODEC_BRIDGE_H_
#define MEDIA_BASE_ANDROID_MEDIA_CODEC_BRIDGE_H_
-#include <jni.h>
#include <set>
#include <string>
+#include <vector>
-#include "base/android/scoped_java_ref.h"
+#include "base/compiler_specific.h"
#include "base/time/time.h"
-#include "media/base/audio_decoder_config.h"
-#include "media/base/video_decoder_config.h"
-#include "ui/gfx/geometry/size.h"
+#include "media/base/android/media_codec_util.h"
+#include "media/base/media_export.h"
namespace media {
@@ -34,59 +33,12 @@ enum MediaCodecStatus {
MEDIA_CODEC_ERROR
};
-// 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
+// Interface for wrapping different Android MediaCodec implementations. 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
@@ -95,31 +47,36 @@ 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.
- MediaCodecStatus Reset();
+ virtual MediaCodecStatus Reset() = 0;
+
+ // Calls start() against the media codec instance. Returns whether media
+ // codec was successfully started.
+ virtual bool Start() = 0;
// 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.
- void Stop();
+ virtual void Stop() = 0;
// Used for getting output format. This is valid after DequeueInputBuffer()
// returns a format change by returning INFO_OUTPUT_FORMAT_CHANGED
- void GetOutputFormat(int* width, int* height);
+ virtual void GetOutputFormat(int* width, int* height) = 0;
// Used for checking for new sampling rate after DequeueInputBuffer() returns
// INFO_OUTPUT_FORMAT_CHANGED
- int GetOutputSamplingRate();
+ virtual int GetOutputSamplingRate() = 0;
// Submits a byte array to the given input buffer. Call this after getting an
- // available buffer from DequeueInputBuffer(). If |data| is NULL, assume the
+ // 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).
- MediaCodecStatus QueueInputBuffer(int index,
- const uint8* data,
- size_t data_size,
- const base::TimeDelta& presentation_time);
+ virtual MediaCodecStatus QueueInputBuffer(
+ int index,
+ const uint8* data,
+ size_t data_size,
+ const base::TimeDelta& presentation_time) = 0;
// Similar to the above call, but submits a buffer that is encrypted. Note:
// NULL |subsamples| indicates the whole buffer is encrypted. If |data| is
@@ -138,20 +95,18 @@ class MEDIA_EXPORT MediaCodecBridge {
// and MediaCodecPlayer.
// TODO(timav): remove this method and keep only the one above after we
// switch to the Spitzer pipeline.
- MediaCodecStatus QueueSecureInputBuffer(
+ virtual MediaCodecStatus 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 std::vector<char>& key_id,
+ const std::vector<char>& iv,
const SubsampleEntry* subsamples,
int subsamples_size,
- const base::TimeDelta& presentation_time);
+ const base::TimeDelta& presentation_time) = 0;
// Submits an empty buffer with a EOS (END OF STREAM) flag.
- void QueueEOS(int input_buffer_index);
+ virtual void QueueEOS(int input_buffer_index) = 0;
// Returns:
// MEDIA_CODEC_OK if an input buffer is ready to be filled with valid data,
@@ -159,8 +114,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.
- MediaCodecStatus DequeueInputBuffer(const base::TimeDelta& timeout,
- int* index);
+ virtual MediaCodecStatus DequeueInputBuffer(const base::TimeDelta& timeout,
+ int* index) = 0;
// Dequeues an output buffer, block at most timeout_us microseconds.
// Returns the status of this operation. If OK is returned, the output
@@ -171,159 +126,51 @@ 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?
- 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);
+ 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;
// Returns the buffer to the codec. If you previously specified a surface when
// configuring this video decoder you can optionally render the buffer.
- void ReleaseOutputBuffer(int index, bool render);
+ virtual void ReleaseOutputBuffer(int index, bool render) = 0;
// Returns the number of output buffers used by the codec.
// TODO(qinmin): this call is deprecated in Lollipop.
- int GetOutputBuffersCount();
+ virtual int GetOutputBuffersCount();
// Returns the capacity of each output buffer used by the codec.
// TODO(qinmin): this call is deprecated in Lollipop.
- size_t GetOutputBuffersCapacity();
+ virtual size_t GetOutputBuffersCapacity();
// Returns an input buffer's base pointer and capacity.
- void GetInputBuffer(int input_buffer_index, uint8** data, size_t* capacity);
+ virtual void GetInputBuffer(int input_buffer_index,
+ uint8** data,
+ size_t* capacity) = 0;
// Copy |dst_size| bytes from output buffer |index|'s |offset| onwards into
// |*dst|.
- bool CopyFromOutputBuffer(int index, size_t offset, void* dst, int dst_size);
-
- static bool RegisterMediaCodecBridge(JNIEnv* env);
+ virtual bool CopyFromOutputBuffer(int index,
+ size_t offset,
+ void* dst,
+ int dst_size) = 0;
protected:
- // 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_;
+ MediaCodecBridge();
- 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* 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
deleted file mode 100644
index f1c0471..0000000
--- a/media/base/android/media_codec_bridge_unittest.cc
+++ /dev/null
@@ -1,319 +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 <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,
- &timestamp,
- &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 cdbaf07..67d7188 100644
--- a/media/base/android/media_codec_decoder.cc
+++ b/media/base/android/media_codec_decoder.cc
@@ -738,9 +738,7 @@ bool MediaCodecDecoder::EnqueueInputBuffer() {
<< " subsamples size:" << unit->subsamples.size();
status = media_codec_bridge_->QueueSecureInputBuffer(
- 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(),
+ index, &unit->data[0], unit->data.size(), unit->key_id, unit->iv,
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 322407b..2b14820 100644
--- a/media/base/android/media_codec_decoder_unittest.cc
+++ b/media/base/android/media_codec_decoder_unittest.cc
@@ -7,9 +7,10 @@
#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_bridge.h"
+#include "media/base/android/media_codec_util.h"
#include "media/base/android/media_codec_video_decoder.h"
#include "media/base/android/media_statistics.h"
+#include "media/base/android/sdk_media_codec_bridge.h"
#include "media/base/android/test_data_factory.h"
#include "media/base/android/test_statistics.h"
#include "media/base/timestamp_constants.h"
@@ -18,15 +19,6 @@
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 46b95fc..628de1ce 100644
--- a/media/base/android/media_codec_player_unittest.cc
+++ b/media/base/android/media_codec_player_unittest.cc
@@ -8,10 +8,11 @@
#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"
@@ -20,15 +21,6 @@
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
new file mode 100644
index 0000000..cb31ef9
--- /dev/null
+++ b/media/base/android/media_codec_util.cc
@@ -0,0 +1,225 @@
+// 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
new file mode 100644
index 0000000..604ce5d
--- /dev/null
+++ b/media/base/android/media_codec_util.h
@@ -0,0 +1,63 @@
+// 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 f20bbeb..928f2e7 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 3bf5279..ed367d7 100644
--- a/media/base/android/media_decoder_job.cc
+++ b/media/base/android/media_decoder_job.cc
@@ -285,13 +285,9 @@ MediaCodecStatus MediaDecoderJob::QueueInputBuffer(const AccessUnit& unit) {
}
MediaCodecStatus status = media_codec_bridge_->QueueSecureInputBuffer(
- 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(),
+ input_buf_index, &unit.data[0], unit.data.size(), unit.key_id, unit.iv,
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 2020469..cc8bc1d 100644
--- a/media/base/android/media_jni_registrar.cc
+++ b/media/base/android/media_jni_registrar.cc
@@ -10,10 +10,11 @@
#include "media/audio/android/audio_manager_android.h"
#include "media/audio/android/audio_record_input.h"
-#include "media/base/android/media_codec_bridge.h"
+#include "media/base/android/media_codec_util.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"
@@ -21,24 +22,19 @@
namespace media {
static base::android::RegistrationMethod kMediaRegisteredMethods[] = {
- { "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 },
+ {"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},
};
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 fd4a725..b7cab6c 100644
--- a/media/base/android/media_source_player_unittest.cc
+++ b/media/base/android/media_source_player_unittest.cc
@@ -9,11 +9,12 @@
#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_bridge.h"
+#include "media/base/android/media_codec_util.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"
@@ -24,15 +25,6 @@
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
new file mode 100644
index 0000000..ae23aff
--- /dev/null
+++ b/media/base/android/ndk_media_codec_bridge.cc
@@ -0,0 +1,228 @@
+// 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 "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* data,
+ size_t data_size,
+ const base::TimeDelta& presentation_time) {
+ if (data_size > base::checked_cast<size_t>(kint32max))
+ 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* 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>(kint32max))
+ 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>::max());
+ if (subsamples[i].cypher_bytes >
+ static_cast<uint32>(std::numeric_limits<int32>::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*>(const_cast<char*>(key_id.data())),
+ reinterpret_cast<uint8*>(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** 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
new file mode 100644
index 0000000..75bada9
--- /dev/null
+++ b/media/base/android/ndk_media_codec_bridge.h
@@ -0,0 +1,79 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MEDIA_BASE_ANDROID_NDK_MEDIA_CODEC_BRIDGE_H_
+#define MEDIA_BASE_ANDROID_NDK_MEDIA_CODEC_BRIDGE_H_
+
+#include <media/NdkMediaCodec.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* data,
+ size_t data_size,
+ const base::TimeDelta& presentation_time) override;
+ using MediaCodecBridge::QueueSecureInputBuffer;
+ MediaCodecStatus QueueSecureInputBuffer(
+ int index,
+ const uint8* 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** 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
new file mode 100644
index 0000000..01cfa3f
--- /dev/null
+++ b/media/base/android/sdk_media_codec_bridge.cc
@@ -0,0 +1,638 @@
+// 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 "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* 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>(kint32max))
+ 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* 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>(kint32max))
+ 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*>(key_id.data()), key_id.size());
+ ScopedJavaLocalRef<jbyteArray> j_iv = base::android::ToJavaByteArray(
+ env, reinterpret_cast<const uint8*>(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>::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 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** 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 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*>(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*>(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* 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 Start();
+}
+
+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
+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
new file mode 100644
index 0000000..3518ad7
--- /dev/null
+++ b/media/base/android/sdk_media_codec_bridge.h
@@ -0,0 +1,196 @@
+// 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 <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* data,
+ size_t data_size,
+ const base::TimeDelta& presentation_time) override;
+ using MediaCodecBridge::QueueSecureInputBuffer;
+ MediaCodecStatus QueueSecureInputBuffer(
+ int index,
+ const uint8* 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** 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* 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 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
new file mode 100644
index 0000000..2f6274f
--- /dev/null
+++ b/media/base/android/sdk_media_codec_bridge_unittest.cc
@@ -0,0 +1,300 @@
+// 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, &timestamp, &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 d43f450..d74ba4a 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 e463802..4b25ca1 100644
--- a/media/media.gyp
+++ b/media/media.gyp
@@ -1182,12 +1182,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',
@@ -1807,6 +1807,7 @@
'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',
@@ -1856,6 +1857,8 @@
'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',
@@ -1882,12 +1885,28 @@
'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',