| 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 1acd23afa7abac6d5da42d24f09595e4e27284e5..e1656df8580f452d7b57335a737ab2c3d074babf 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.
|
| + 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;
|
| + 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,15 @@ 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 +219,14 @@ 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,
|
| 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 +235,20 @@ MediaCodecStatus MediaCodecBridge::QueueInputBuffer(
|
| }
|
|
|
| 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,
|
| + int index,
|
| + const uint8* data,
|
| + int orig_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) {
|
| - 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();
|
| @@ -268,6 +298,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(),
|
| @@ -280,13 +311,21 @@ 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) {
|
| + 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(),
|
| @@ -296,15 +335,26 @@ 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);
|
|
|
| @@ -312,33 +362,77 @@ 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,
|
| @@ -494,57 +588,103 @@ 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) {
|
|
|