diff options
Diffstat (limited to 'chromium/media/base/android/media_codec_bridge.cc')
-rw-r--r-- | chromium/media/base/android/media_codec_bridge.cc | 278 |
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 - |