summaryrefslogtreecommitdiff
path: root/chromium/media/base/android/media_codec_bridge.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/media/base/android/media_codec_bridge.cc')
-rw-r--r--chromium/media/base/android/media_codec_bridge.cc278
1 files changed, 190 insertions, 88 deletions
diff --git a/chromium/media/base/android/media_codec_bridge.cc b/chromium/media/base/android/media_codec_bridge.cc
index 47ee5997160..a029e209805 100644
--- a/chromium/media/base/android/media_codec_bridge.cc
+++ b/chromium/media/base/android/media_codec_bridge.cc
@@ -5,6 +5,7 @@
#include "media/base/android/media_codec_bridge.h"
#include <jni.h>
+#include <string>
#include "base/android/build_info.h"
#include "base/android/jni_android.h"
@@ -20,6 +21,7 @@
#include "media/base/decrypt_config.h"
using base::android::AttachCurrentThread;
+using base::android::ConvertJavaStringToUTF8;
using base::android::ConvertUTF8ToJavaString;
using base::android::ScopedJavaLocalRef;
@@ -27,7 +29,7 @@ namespace media {
enum { kBufferFlagEndOfStream = 4 };
-static const char* AudioCodecToMimeType(const AudioCodec codec) {
+static const std::string AudioCodecToAndroidMimeType(const AudioCodec codec) {
switch (codec) {
case kCodecMP3:
return "audio/mpeg";
@@ -36,21 +38,53 @@ static const char* AudioCodecToMimeType(const AudioCodec codec) {
case kCodecAAC:
return "audio/mp4a-latm";
default:
- return NULL;
+ return std::string();
}
}
-static const char* VideoCodecToMimeType(const VideoCodec codec) {
+static const std::string VideoCodecToAndroidMimeType(const VideoCodec codec) {
switch (codec) {
case kCodecH264:
return "video/avc";
case kCodecVP8:
return "video/x-vnd.on2.vp8";
default:
- return NULL;
+ 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 == "mp4a")
+ return "audio/mp4a-latm";
+ if (codec == "vp8" || codec == "vp8.0")
+ return "video/x-vnd.on2.vp8";
+ if (codec == "vorbis")
+ return "audio/vorbis";
+ 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/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";
+ return std::string();
+}
+
static ScopedJavaLocalRef<jintArray> ToJavaIntArray(
JNIEnv* env, scoped_ptr<jint[]> native_array, int size) {
ScopedJavaLocalRef<jintArray> j_array(env, env->NewIntArray(size));
@@ -59,44 +93,78 @@ static ScopedJavaLocalRef<jintArray> ToJavaIntArray(
}
// static
-const base::TimeDelta MediaCodecBridge::kTimeOutInfinity =
- base::TimeDelta::FromMicroseconds(-1);
+bool MediaCodecBridge::IsAvailable() {
+ // MediaCodec is only available on JB and greater.
+ return base::android::BuildInfo::GetInstance()->sdk_int() >= 16;
+}
// static
-const base::TimeDelta MediaCodecBridge::kTimeOutNoWait =
- base::TimeDelta::FromMicroseconds(0);
+void MediaCodecBridge::GetCodecsInfo(
+ std::vector<CodecsInfo>* codecs_info) {
+ JNIEnv* env = AttachCurrentThread();
+ if (!IsAvailable())
+ return;
+
+ 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);
+ CodecsInfo info;
+ info.codecs = AndroidMimeTypeToCodecType(mime_type);
+ info.secure_decoder_supported =
+ Java_CodecInfo_isSecureDecoderSupported(env, j_info.obj());
+ codecs_info->push_back(info);
+ }
+}
// static
-bool MediaCodecBridge::IsAvailable() {
- // MediaCodec is only available on JB and greater.
- return base::android::BuildInfo::GetInstance()->sdk_int() >= 16;
+bool MediaCodecBridge::CanDecode(const std::string& codec, bool is_secure) {
+ 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);
+ if (!j_media_codec_bridge.is_null()) {
+ Java_MediaCodecBridge_release(env, j_media_codec_bridge.obj());
+ return true;
+ }
+ return false;
}
-MediaCodecBridge::MediaCodecBridge(const char* mime) {
+MediaCodecBridge::MediaCodecBridge(const std::string& mime, bool is_secure) {
JNIEnv* env = AttachCurrentThread();
CHECK(env);
- DCHECK(mime);
-
- ScopedJavaLocalRef<jstring> j_type = ConvertUTF8ToJavaString(env, mime);
- j_media_codec_.Reset(Java_MediaCodecBridge_create(
- env, j_type.obj()));
+ DCHECK(!mime.empty());
+ ScopedJavaLocalRef<jstring> j_mime = ConvertUTF8ToJavaString(env, mime);
+ j_media_codec_.Reset(
+ Java_MediaCodecBridge_create(env, j_mime.obj(), is_secure));
}
MediaCodecBridge::~MediaCodecBridge() {
JNIEnv* env = AttachCurrentThread();
CHECK(env);
- Java_MediaCodecBridge_release(env, j_media_codec_.obj());
+ if (j_media_codec_.obj())
+ Java_MediaCodecBridge_release(env, j_media_codec_.obj());
}
-void MediaCodecBridge::StartInternal() {
+bool MediaCodecBridge::StartInternal() {
JNIEnv* env = AttachCurrentThread();
- Java_MediaCodecBridge_start(env, j_media_codec_.obj());
- GetOutputBuffers();
+ return Java_MediaCodecBridge_start(env, j_media_codec_.obj()) &&
+ GetOutputBuffers();
}
-int MediaCodecBridge::Reset() {
+MediaCodecStatus MediaCodecBridge::Reset() {
JNIEnv* env = AttachCurrentThread();
- return Java_MediaCodecBridge_flush(env, j_media_codec_.obj());
+ return static_cast<MediaCodecStatus>(
+ Java_MediaCodecBridge_flush(env, j_media_codec_.obj()));
}
void MediaCodecBridge::Stop() {
@@ -111,46 +179,69 @@ void MediaCodecBridge::GetOutputFormat(int* width, int* height) {
*height = Java_MediaCodecBridge_getOutputHeight(env, j_media_codec_.obj());
}
-size_t MediaCodecBridge::QueueInputBuffer(
- int index, const uint8* data, int size,
+MediaCodecStatus MediaCodecBridge::QueueInputBuffer(
+ int index, const uint8* data, int data_size,
const base::TimeDelta& presentation_time) {
- size_t size_to_copy = FillInputBuffer(index, data, size);
+ int size_to_copy = FillInputBuffer(index, data, data_size);
+ DCHECK_EQ(size_to_copy, data_size);
JNIEnv* env = AttachCurrentThread();
- Java_MediaCodecBridge_queueInputBuffer(
+ return static_cast<MediaCodecStatus>(Java_MediaCodecBridge_queueInputBuffer(
env, j_media_codec_.obj(),
- index, 0, size_to_copy, presentation_time.InMicroseconds(), 0);
- return size_to_copy;
+ index, 0, size_to_copy, presentation_time.InMicroseconds(), 0));
}
-size_t MediaCodecBridge::QueueSecureInputBuffer(
+MediaCodecStatus MediaCodecBridge::QueueSecureInputBuffer(
int index, const uint8* data, int 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) {
- size_t size_to_copy = FillInputBuffer(index, data, data_size);
+ int size_to_copy = FillInputBuffer(index, data, data_size);
+ DCHECK_EQ(size_to_copy, data_size);
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);
- scoped_ptr<jint[]> native_clear_array(new jint[subsamples_size]);
- scoped_ptr<jint[]> native_cypher_array(new jint[subsamples_size]);
- for (int i = 0; i < subsamples_size; ++i) {
- native_clear_array[i] = subsamples[i].clear_bytes;
- native_cypher_array[i] = subsamples[i].cypher_bytes;
+
+ // 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(), subsamples_size);
- ScopedJavaLocalRef<jintArray> cypher_array = ToJavaIntArray(
- env, native_cypher_array.Pass(), subsamples_size);
- Java_MediaCodecBridge_queueSecureInputBuffer(
- env, j_media_codec_.obj(), index, 0, j_iv.obj(), j_key_id.obj(),
- clear_array.obj(), cypher_array.obj(), subsamples_size,
- presentation_time.InMicroseconds());
+ 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 size_to_copy;
+ 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) {
@@ -160,36 +251,34 @@ void MediaCodecBridge::QueueEOS(int input_buffer_index) {
input_buffer_index, 0, 0, 0, kBufferFlagEndOfStream);
}
-int MediaCodecBridge::DequeueInputBuffer(base::TimeDelta timeout) {
+MediaCodecStatus MediaCodecBridge::DequeueInputBuffer(
+ const base::TimeDelta& timeout, int* index) {
JNIEnv* env = AttachCurrentThread();
- return Java_MediaCodecBridge_dequeueInputBuffer(
+ ScopedJavaLocalRef<jobject> result = Java_MediaCodecBridge_dequeueInputBuffer(
env, j_media_codec_.obj(), timeout.InMicroseconds());
+ *index = Java_DequeueInputResult_index(env, result.obj());
+ return static_cast<MediaCodecStatus>(
+ Java_DequeueInputResult_status(env, result.obj()));
}
-int MediaCodecBridge::DequeueOutputBuffer(
- base::TimeDelta timeout, size_t* offset, size_t* size,
+MediaCodecStatus MediaCodecBridge::DequeueOutputBuffer(
+ const base::TimeDelta& timeout, int* index, size_t* offset, size_t* size,
base::TimeDelta* presentation_time, bool* end_of_stream) {
JNIEnv* env = AttachCurrentThread();
-
ScopedJavaLocalRef<jobject> result =
Java_MediaCodecBridge_dequeueOutputBuffer(env, j_media_codec_.obj(),
timeout.InMicroseconds());
-
- int j_buffer = Java_DequeueOutputResult_index(env, result.obj());
- if (j_buffer >= 0) {
- int64 presentation_time_us =
- Java_DequeueOutputResult_presentationTimeMicroseconds(
- env, result.obj());
- int flags = Java_DequeueOutputResult_flags(env, result.obj());
- *offset = base::checked_numeric_cast<size_t>(
- Java_DequeueOutputResult_offset(env, result.obj()));
- *size = base::checked_numeric_cast<size_t>(
- Java_DequeueOutputResult_numBytes(env, result.obj()));
- *presentation_time =
- base::TimeDelta::FromMicroseconds(presentation_time_us);
- *end_of_stream = flags & kBufferFlagEndOfStream;
- }
- return j_buffer;
+ *index = Java_DequeueOutputResult_index(env, result.obj());;
+ *offset = base::checked_numeric_cast<size_t>(
+ Java_DequeueOutputResult_offset(env, result.obj()));
+ *size = base::checked_numeric_cast<size_t>(
+ Java_DequeueOutputResult_numBytes(env, result.obj()));
+ *presentation_time = base::TimeDelta::FromMicroseconds(
+ Java_DequeueOutputResult_presentationTimeMicroseconds(env, result.obj()));
+ int flags = Java_DequeueOutputResult_flags(env, result.obj());
+ *end_of_stream = flags & kBufferFlagEndOfStream;
+ return static_cast<MediaCodecStatus>(
+ Java_DequeueOutputResult_status(env, result.obj()));
}
void MediaCodecBridge::ReleaseOutputBuffer(int index, bool render) {
@@ -200,9 +289,9 @@ void MediaCodecBridge::ReleaseOutputBuffer(int index, bool render) {
env, j_media_codec_.obj(), index, render);
}
-void MediaCodecBridge::GetOutputBuffers() {
+bool MediaCodecBridge::GetOutputBuffers() {
JNIEnv* env = AttachCurrentThread();
- Java_MediaCodecBridge_getOutputBuffers(env, j_media_codec_.obj());
+ return Java_MediaCodecBridge_getOutputBuffers(env, j_media_codec_.obj());
}
size_t MediaCodecBridge::FillInputBuffer(
@@ -226,8 +315,9 @@ size_t MediaCodecBridge::FillInputBuffer(
return size_to_copy;
}
-AudioCodecBridge::AudioCodecBridge(const char* mime)
- : MediaCodecBridge(mime) {
+AudioCodecBridge::AudioCodecBridge(const std::string& mime)
+ // Audio codec doesn't care about security level.
+ : MediaCodecBridge(mime, false) {
}
bool AudioCodecBridge::Start(
@@ -235,10 +325,16 @@ bool AudioCodecBridge::Start(
const uint8* extra_data, size_t extra_data_size, bool play_audio,
jobject media_crypto) {
JNIEnv* env = AttachCurrentThread();
- DCHECK(AudioCodecToMimeType(codec));
+
+ if (!media_codec())
+ return false;
+
+ std::string codec_string = AudioCodecToAndroidMimeType(codec);
+ if (codec_string.empty())
+ return false;
ScopedJavaLocalRef<jstring> j_mime =
- ConvertUTF8ToJavaString(env, AudioCodecToMimeType(codec));
+ ConvertUTF8ToJavaString(env, codec_string);
ScopedJavaLocalRef<jobject> j_format(
Java_MediaCodecBridge_createAudioFormat(
env, j_mime.obj(), sample_rate, channel_count));
@@ -251,8 +347,8 @@ bool AudioCodecBridge::Start(
env, media_codec(), j_format.obj(), media_crypto, 0, play_audio)) {
return false;
}
- StartInternal();
- return true;
+
+ return StartInternal();
}
bool AudioCodecBridge::ConfigureMediaFormat(
@@ -262,7 +358,7 @@ bool AudioCodecBridge::ConfigureMediaFormat(
return true;
JNIEnv* env = AttachCurrentThread();
- switch(codec) {
+ switch (codec) {
case kCodecVorbis:
{
if (extra_data[0] != 2) {
@@ -353,7 +449,7 @@ bool AudioCodecBridge::ConfigureMediaFormat(
}
default:
LOG(ERROR) << "Invalid header encountered for codec: "
- << AudioCodecToMimeType(codec);
+ << AudioCodecToAndroidMimeType(codec);
return false;
}
return true;
@@ -378,18 +474,24 @@ void AudioCodecBridge::SetVolume(double volume) {
Java_MediaCodecBridge_setVolume(env, media_codec(), volume);
}
-VideoCodecBridge::VideoCodecBridge(const char* mime)
- : MediaCodecBridge(mime) {
+VideoCodecBridge::VideoCodecBridge(const std::string& mime, bool is_secure)
+ : MediaCodecBridge(mime, is_secure) {
}
bool VideoCodecBridge::Start(
const VideoCodec codec, const gfx::Size& size, jobject surface,
jobject media_crypto) {
JNIEnv* env = AttachCurrentThread();
- DCHECK(VideoCodecToMimeType(codec));
+
+ if (!media_codec())
+ return false;
+
+ std::string codec_string = VideoCodecToAndroidMimeType(codec);
+ if (codec_string.empty())
+ return false;
ScopedJavaLocalRef<jstring> j_mime =
- ConvertUTF8ToJavaString(env, VideoCodecToMimeType(codec));
+ ConvertUTF8ToJavaString(env, codec_string);
ScopedJavaLocalRef<jobject> j_format(
Java_MediaCodecBridge_createVideoFormat(
env, j_mime.obj(), size.width(), size.height()));
@@ -398,18 +500,19 @@ bool VideoCodecBridge::Start(
env, media_codec(), j_format.obj(), surface, media_crypto, 0)) {
return false;
}
- StartInternal();
- return true;
+
+ return StartInternal();
}
AudioCodecBridge* AudioCodecBridge::Create(const AudioCodec codec) {
- const char* mime = AudioCodecToMimeType(codec);
- return mime ? new AudioCodecBridge(mime) : NULL;
+ const std::string mime = AudioCodecToAndroidMimeType(codec);
+ return mime.empty() ? NULL : new AudioCodecBridge(mime);
}
-VideoCodecBridge* VideoCodecBridge::Create(const VideoCodec codec) {
- const char* mime = VideoCodecToMimeType(codec);
- return mime ? new VideoCodecBridge(mime) : NULL;
+VideoCodecBridge* VideoCodecBridge::Create(const VideoCodec codec,
+ bool is_secure) {
+ const std::string mime = VideoCodecToAndroidMimeType(codec);
+ return mime.empty() ? NULL : new VideoCodecBridge(mime, is_secure);
}
bool MediaCodecBridge::RegisterMediaCodecBridge(JNIEnv* env) {
@@ -417,4 +520,3 @@ bool MediaCodecBridge::RegisterMediaCodecBridge(JNIEnv* env) {
}
} // namespace media
-