Chromium Code Reviews| Index: media/base/android/media_codec_bridge.cc |
| diff --git a/media/base/android/media_codec_bridge.cc b/media/base/android/media_codec_bridge.cc |
| index 50bd09795113d7d8568b7bcece41f56093bc468c..4e9209585a25f7e1e7b8b87027f3389a95af87aa 100644 |
| --- a/media/base/android/media_codec_bridge.cc |
| +++ b/media/base/android/media_codec_bridge.cc |
| @@ -28,7 +28,11 @@ using base::android::ScopedJavaLocalRef; |
| namespace media { |
| -enum { kBufferFlagEndOfStream = 4 }; |
| +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) { |
| @@ -104,11 +108,17 @@ bool MediaCodecBridge::IsAvailable() { |
| } |
| // static |
| -void MediaCodecBridge::GetCodecsInfo( |
| - std::vector<CodecsInfo>* codecs_info) { |
| +bool MediaCodecBridge::SupportsSetParameters() { |
| + // MediaCodec.setParameters() is only available starting with K, but bugs mean |
| + // we need to wait for the next release. |
| + return base::android::BuildInfo::GetInstance()->sdk_int() > 19; |
| +} |
| + |
| +// static |
| +std::vector<MediaCodecBridge::CodecsInfo> MediaCodecBridge::GetCodecsInfo() { |
| + std::vector<CodecsInfo> codecs_info; |
| JNIEnv* env = AttachCurrentThread(); |
| - if (!IsAvailable()) |
| - return; |
| + if (!IsAvailable()) return codecs_info; |
| std::string mime_type; |
| std::string codec_name; |
| @@ -126,8 +136,10 @@ void MediaCodecBridge::GetCodecsInfo( |
| CodecsInfo info; |
| info.codecs = AndroidMimeTypeToCodecType(mime_type); |
| ConvertJavaStringToUTF8(env, j_codec_name.obj(), &info.name); |
| - codecs_info->push_back(info); |
| + info.is_encoder = Java_CodecInfo_isEncoder(env, j_info.obj()); |
| + codecs_info.push_back(info); |
| } |
| + return codecs_info; |
| } |
| // static |
| @@ -138,7 +150,7 @@ bool MediaCodecBridge::CanDecode(const std::string& codec, bool is_secure) { |
| return false; |
| ScopedJavaLocalRef<jstring> j_mime = ConvertUTF8ToJavaString(env, mime); |
| ScopedJavaLocalRef<jobject> j_media_codec_bridge = |
| - Java_MediaCodecBridge_create(env, j_mime.obj(), is_secure); |
| + 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; |
| @@ -147,12 +159,14 @@ bool MediaCodecBridge::CanDecode(const std::string& codec, bool is_secure) { |
| } |
| // static |
| -bool MediaCodecBridge::IsKnownUnaccelerated(const std::string& mime_type) { |
| +bool MediaCodecBridge::IsKnownUnaccelerated(const std::string& mime_type, |
| + bool is_encoder) { |
| std::string codec_type = AndroidMimeTypeToCodecType(mime_type); |
| - std::vector<media::MediaCodecBridge::CodecsInfo> codecs_info; |
| - media::MediaCodecBridge::GetCodecsInfo(&codecs_info); |
| + 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) { |
| + if (codecs_info[i].codecs == codec_type && |
| + codecs_info[i].is_encoder == is_encoder) { |
| // It would be nice if MediaCodecInfo externalized some notion of |
| // HW-acceleration but it doesn't. Android Media guidance is that the |
| // prefix below is always used for SW decoders, so that's what we use. |
| @@ -162,13 +176,14 @@ bool MediaCodecBridge::IsKnownUnaccelerated(const std::string& mime_type) { |
| return true; |
| } |
| -MediaCodecBridge::MediaCodecBridge(const std::string& mime, bool is_secure) { |
| +MediaCodecBridge::MediaCodecBridge(const std::string& mime, bool is_secure, |
| + bool is_encoder) { |
| 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)); |
| + Java_MediaCodecBridge_create(env, j_mime.obj(), is_secure, is_encoder)); |
| } |
| MediaCodecBridge::~MediaCodecBridge() { |
| @@ -203,9 +218,12 @@ void MediaCodecBridge::GetOutputFormat(int* width, int* height) { |
| } |
| MediaCodecStatus MediaCodecBridge::QueueInputBuffer( |
| - int index, const uint8* data, int data_size, |
| + int index, const uint8* data, int orig_data_size, |
|
xhwang
2013/11/19 00:11:25
Also update the header file.
Ami GONE FROM CHROMIUM
2013/11/21 22:59:07
Done.
|
| const base::TimeDelta& presentation_time) { |
| - if (!FillInputBuffer(index, data, data_size)) |
| + DVLOG(3) << "MediaCodecBridge::QueueInputBuffer: " << index << ": " |
| + << orig_data_size; |
| + size_t data_size = base::checked_numeric_cast<size_t>(orig_data_size); |
| + if (data && !FillInputBuffer(index, data, data_size)) |
| return MEDIA_CODEC_ERROR; |
| JNIEnv* env = AttachCurrentThread(); |
| return static_cast<MediaCodecStatus>(Java_MediaCodecBridge_queueInputBuffer( |
| @@ -214,11 +232,14 @@ MediaCodecStatus MediaCodecBridge::QueueInputBuffer( |
| } |
| MediaCodecStatus MediaCodecBridge::QueueSecureInputBuffer( |
| - int index, const uint8* data, int data_size, const uint8* key_id, |
| + int index, const uint8* data, int orig_data_size, const uint8* key_id, |
|
xhwang
2013/11/19 00:11:25
ditto
Ami GONE FROM CHROMIUM
2013/11/21 22:59:07
Done.
|
| int key_id_size, const uint8* iv, int iv_size, |
| const SubsampleEntry* subsamples, int subsamples_size, |
| const base::TimeDelta& presentation_time) { |
| - if (!FillInputBuffer(index, data, data_size)) |
| + DVLOG(3) << "MediaCodecBridge::QueueSecureInputBuffer: " << index << ": " |
| + << orig_data_size; |
| + size_t data_size = base::checked_numeric_cast<size_t>(orig_data_size); |
| + if (data && !FillInputBuffer(index, data, data_size)) |
| return MEDIA_CODEC_ERROR; |
| JNIEnv* env = AttachCurrentThread(); |
| @@ -262,6 +283,7 @@ MediaCodecStatus MediaCodecBridge::QueueSecureInputBuffer( |
| } |
| void MediaCodecBridge::QueueEOS(int input_buffer_index) { |
| + DVLOG(3) << "MediaCodecBridge::QueueEOS: " << input_buffer_index; |
| JNIEnv* env = AttachCurrentThread(); |
| Java_MediaCodecBridge_queueInputBuffer( |
| env, j_media_codec_.obj(), |
| @@ -274,13 +296,16 @@ MediaCodecStatus 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>( |
| + MediaCodecStatus status = static_cast<MediaCodecStatus>( |
| Java_DequeueInputResult_status(env, result.obj())); |
| + DVLOG(3) << "MediaCodecBridge::DequeueInputBuffer: 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) { |
| + 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(), |
| @@ -290,15 +315,24 @@ MediaCodecStatus MediaCodecBridge::DequeueOutputBuffer( |
| 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())); |
| + if (presentation_time) { |
| + *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>( |
| + 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) << "MediaCodecBridge::DequeueOutputBuffer: status: " << status |
| + << ", index: " << *index << ", offset: " << *offset |
| + << ", size: " << *size << ", flags: " << flags; |
| + return status; |
| } |
| void MediaCodecBridge::ReleaseOutputBuffer(int index, bool render) { |
| + DVLOG(3) << "MediaCodecBridge::ReleaseOutputBuffer: " << index; |
| JNIEnv* env = AttachCurrentThread(); |
| CHECK(env); |
| @@ -306,33 +340,72 @@ void MediaCodecBridge::ReleaseOutputBuffer(int index, bool render) { |
| env, j_media_codec_.obj(), index, render); |
| } |
| +int MediaCodecBridge::GetInputBuffersCount() { |
| + JNIEnv* env = AttachCurrentThread(); |
| + return Java_MediaCodecBridge_getInputBuffersCount(env, j_media_codec_.obj()); |
| +} |
| + |
| +int MediaCodecBridge::GetOutputBuffersCount() { |
| + JNIEnv* env = AttachCurrentThread(); |
| + return Java_MediaCodecBridge_getOutputBuffersCount(env, j_media_codec_.obj()); |
| +} |
| + |
| +size_t MediaCodecBridge::GetOutputBuffersCapacity() { |
| + JNIEnv* env = AttachCurrentThread(); |
| + return Java_MediaCodecBridge_getOutputBuffersCapacity(env, |
| + j_media_codec_.obj()); |
| +} |
| + |
| bool MediaCodecBridge::GetOutputBuffers() { |
| JNIEnv* env = AttachCurrentThread(); |
| return Java_MediaCodecBridge_getOutputBuffers(env, j_media_codec_.obj()); |
| } |
| -bool MediaCodecBridge::FillInputBuffer(int index, const uint8* data, int size) { |
| +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_numeric_cast<size_t>( |
| + env->GetDirectBufferCapacity(j_buffer.obj())); |
| +} |
| +bool MediaCodecBridge::CopyFromOutputBuffer(int index, size_t offset, void* dst, |
| + int dst_size) { |
| + JNIEnv* env = AttachCurrentThread(); |
| ScopedJavaLocalRef<jobject> j_buffer( |
| - Java_MediaCodecBridge_getInputBuffer(env, j_media_codec_.obj(), index)); |
| - jlong capacity = env->GetDirectBufferCapacity(j_buffer.obj()); |
| + Java_MediaCodecBridge_getOutputBuffer(env, j_media_codec_.obj(), index)); |
| + void* src_data = |
| + reinterpret_cast<uint8*>(env->GetDirectBufferAddress(j_buffer.obj())) + |
| + offset; |
| + int src_capacity = env->GetDirectBufferCapacity(j_buffer.obj()) - offset; |
| + if (src_capacity < dst_size) return false; |
| + memcpy(dst, src_data, dst_size); |
| + return true; |
| +} |
| + |
| +bool MediaCodecBridge::FillInputBuffer(int index, const uint8* data, |
| + size_t size) { |
| + uint8* dst = NULL; |
| + size_t capacity = 0; |
| + GetInputBuffer(index, &dst, &capacity); |
| + CHECK(dst); |
| + |
| if (size > capacity) { |
| LOG(ERROR) << "Input buffer size " << size |
| << " exceeds MediaCodec input buffer capacity: " << capacity; |
| return false; |
| } |
| - uint8* direct_buffer = |
| - static_cast<uint8*>(env->GetDirectBufferAddress(j_buffer.obj())); |
| - memcpy(direct_buffer, data, size); |
| + memcpy(dst, data, size); |
| return true; |
| } |
| AudioCodecBridge::AudioCodecBridge(const std::string& mime) |
| - // Audio codec doesn't care about security level. |
| - : MediaCodecBridge(mime, false) { |
| -} |
| + // Audio codec doesn't care about security level and there is no need for |
| + // audio encoding yet. |
| + : MediaCodecBridge(mime, false, false) {} |
| bool AudioCodecBridge::Start( |
| const AudioCodec& codec, int sample_rate, int channel_count, |
| @@ -488,57 +561,88 @@ void AudioCodecBridge::SetVolume(double volume) { |
| Java_MediaCodecBridge_setVolume(env, media_codec(), volume); |
| } |
| -VideoCodecBridge::VideoCodecBridge(const std::string& mime, bool is_secure) |
| - : MediaCodecBridge(mime, is_secure) { |
| +AudioCodecBridge* AudioCodecBridge::Create(const AudioCodec& codec) { |
| + const std::string mime = AudioCodecToAndroidMimeType(codec); |
| + return mime.empty() ? NULL : new AudioCodecBridge(mime); |
| } |
| -bool VideoCodecBridge::Start( |
| - const VideoCodec& codec, const gfx::Size& size, jobject surface, |
| - jobject media_crypto) { |
| - JNIEnv* env = AttachCurrentThread(); |
| +// static |
| +bool AudioCodecBridge::IsKnownUnaccelerated(const AudioCodec& codec) { |
| + return MediaCodecBridge::IsKnownUnaccelerated( |
| + AudioCodecToAndroidMimeType(codec), false); |
| +} |
| - if (!media_codec()) |
| - return false; |
| +// static |
| +bool VideoCodecBridge::IsKnownUnaccelerated(const VideoCodec& codec, |
| + bool is_encoder) { |
| + return MediaCodecBridge::IsKnownUnaccelerated( |
| + VideoCodecToAndroidMimeType(codec), is_encoder); |
| +} |
| - std::string codec_string = VideoCodecToAndroidMimeType(codec); |
| - if (codec_string.empty()) |
| - return false; |
| +VideoCodecBridge* VideoCodecBridge::CreateDecoder(const VideoCodec& codec, |
| + bool is_secure, |
| + const gfx::Size& size, |
| + jobject surface, |
| + jobject media_crypto) { |
| + JNIEnv* env = AttachCurrentThread(); |
| + const std::string mime = VideoCodecToAndroidMimeType(codec); |
| + if (mime.empty()) return NULL; |
| - ScopedJavaLocalRef<jstring> j_mime = |
| - ConvertUTF8ToJavaString(env, codec_string); |
| + scoped_ptr<VideoCodecBridge> bridge( |
| + new VideoCodecBridge(mime, is_secure, false)); |
| + |
| + ScopedJavaLocalRef<jstring> j_mime = ConvertUTF8ToJavaString(env, mime); |
| ScopedJavaLocalRef<jobject> j_format( |
| - Java_MediaCodecBridge_createVideoFormat( |
| + Java_MediaCodecBridge_createVideoDecoderFormat( |
| env, j_mime.obj(), size.width(), size.height())); |
| DCHECK(!j_format.is_null()); |
| - if (!Java_MediaCodecBridge_configureVideo( |
| - env, media_codec(), j_format.obj(), surface, media_crypto, 0)) { |
| - return false; |
| + if (!Java_MediaCodecBridge_configureVideo(env, bridge->media_codec(), |
| + j_format.obj(), surface, |
| + media_crypto, 0)) { |
| + return NULL; |
| } |
| - return StartInternal(); |
| + return bridge->StartInternal() ? bridge.release() : NULL; |
| } |
| -AudioCodecBridge* AudioCodecBridge::Create(const AudioCodec& codec) { |
| - const std::string mime = AudioCodecToAndroidMimeType(codec); |
| - return mime.empty() ? NULL : new AudioCodecBridge(mime); |
| -} |
| +VideoCodecBridge* VideoCodecBridge::CreateEncoder(const VideoCodec& codec, |
| + const gfx::Size& size, |
| + int bit_rate, int frame_rate, |
| + int i_frame_interval, |
| + int color_format) { |
| + JNIEnv* env = AttachCurrentThread(); |
| + const std::string mime = VideoCodecToAndroidMimeType(codec); |
| + if (mime.empty()) return NULL; |
| -// static |
| -bool AudioCodecBridge::IsKnownUnaccelerated(const AudioCodec& codec) { |
| - return MediaCodecBridge::IsKnownUnaccelerated( |
| - AudioCodecToAndroidMimeType(codec)); |
| + scoped_ptr<VideoCodecBridge> bridge(new VideoCodecBridge(mime, false, true)); |
| + |
| + 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::Create(const VideoCodec& codec, |
| - bool is_secure) { |
| - const std::string mime = VideoCodecToAndroidMimeType(codec); |
| - return mime.empty() ? NULL : new VideoCodecBridge(mime, is_secure); |
| +VideoCodecBridge::VideoCodecBridge(const std::string& mime, bool is_secure, |
| + bool is_encoder) |
| + : MediaCodecBridge(mime, is_secure, is_encoder) {} |
| + |
| +void VideoCodecBridge::SetVideoBitrate(int bps) { |
| + JNIEnv* env = AttachCurrentThread(); |
| + Java_MediaCodecBridge_setVideoBitrate(env, media_codec(), bps); |
| } |
| -// static |
| -bool VideoCodecBridge::IsKnownUnaccelerated(const VideoCodec& codec) { |
| - return MediaCodecBridge::IsKnownUnaccelerated( |
| - VideoCodecToAndroidMimeType(codec)); |
| +void VideoCodecBridge::RequestKeyFrameSoon() { |
| + JNIEnv* env = AttachCurrentThread(); |
| + Java_MediaCodecBridge_requestKeyFrameSoon(env, media_codec()); |
| } |
| bool MediaCodecBridge::RegisterMediaCodecBridge(JNIEnv* env) { |