| OLD | NEW |
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "media/base/android/media_codec_bridge.h" | 5 #include "media/base/android/media_codec_bridge.h" |
| 6 | 6 |
| 7 #include <jni.h> | 7 #include <jni.h> |
| 8 #include <string> | 8 #include <string> |
| 9 | 9 |
| 10 #include "base/android/build_info.h" | 10 #include "base/android/build_info.h" |
| (...skipping 10 matching lines...) Expand all Loading... |
| 21 #include "media/base/bit_reader.h" | 21 #include "media/base/bit_reader.h" |
| 22 #include "media/base/decrypt_config.h" | 22 #include "media/base/decrypt_config.h" |
| 23 | 23 |
| 24 using base::android::AttachCurrentThread; | 24 using base::android::AttachCurrentThread; |
| 25 using base::android::ConvertJavaStringToUTF8; | 25 using base::android::ConvertJavaStringToUTF8; |
| 26 using base::android::ConvertUTF8ToJavaString; | 26 using base::android::ConvertUTF8ToJavaString; |
| 27 using base::android::ScopedJavaLocalRef; | 27 using base::android::ScopedJavaLocalRef; |
| 28 | 28 |
| 29 namespace media { | 29 namespace media { |
| 30 | 30 |
| 31 enum { kBufferFlagEndOfStream = 4 }; | 31 enum { |
| 32 kBufferFlagSyncFrame = 1, // BUFFER_FLAG_SYNC_FRAME |
| 33 kBufferFlagEndOfStream = 4, // BUFFER_FLAG_END_OF_STREAM |
| 34 kConfigureFlagEncode = 1, // CONFIGURE_FLAG_ENCODE |
| 35 }; |
| 32 | 36 |
| 33 static const std::string AudioCodecToAndroidMimeType(const AudioCodec& codec) { | 37 static const std::string AudioCodecToAndroidMimeType(const AudioCodec& codec) { |
| 34 switch (codec) { | 38 switch (codec) { |
| 35 case kCodecMP3: | 39 case kCodecMP3: |
| 36 return "audio/mpeg"; | 40 return "audio/mpeg"; |
| 37 case kCodecVorbis: | 41 case kCodecVorbis: |
| 38 return "audio/vorbis"; | 42 return "audio/vorbis"; |
| 39 case kCodecAAC: | 43 case kCodecAAC: |
| 40 return "audio/mp4a-latm"; | 44 return "audio/mp4a-latm"; |
| 41 default: | 45 default: |
| (...skipping 15 matching lines...) Expand all Loading... |
| 57 } | 61 } |
| 58 | 62 |
| 59 static const std::string CodecTypeToAndroidMimeType(const std::string& codec) { | 63 static const std::string CodecTypeToAndroidMimeType(const std::string& codec) { |
| 60 // TODO(xhwang): Shall we handle more detailed strings like "mp4a.40.2"? | 64 // TODO(xhwang): Shall we handle more detailed strings like "mp4a.40.2"? |
| 61 if (codec == "avc1") | 65 if (codec == "avc1") |
| 62 return "video/avc"; | 66 return "video/avc"; |
| 63 if (codec == "mp4a") | 67 if (codec == "mp4a") |
| 64 return "audio/mp4a-latm"; | 68 return "audio/mp4a-latm"; |
| 65 if (codec == "vp8" || codec == "vp8.0") | 69 if (codec == "vp8" || codec == "vp8.0") |
| 66 return "video/x-vnd.on2.vp8"; | 70 return "video/x-vnd.on2.vp8"; |
| 67 if (codec == "vp9" || codec == "vp9.0") | 71 if (codec == "vp9" || codec == "vp9.0") |
| 68 return "video/x-vnd.on2.vp9"; | 72 return "video/x-vnd.on2.vp9"; |
| 69 if (codec == "vorbis") | 73 if (codec == "vorbis") |
| 70 return "audio/vorbis"; | 74 return "audio/vorbis"; |
| 71 return std::string(); | 75 return std::string(); |
| 72 } | 76 } |
| 73 | 77 |
| 74 // TODO(qinmin): using a map to help all the conversions in this class. | 78 // TODO(qinmin): using a map to help all the conversions in this class. |
| 75 static const std::string AndroidMimeTypeToCodecType(const std::string& mime) { | 79 static const std::string AndroidMimeTypeToCodecType(const std::string& mime) { |
| 76 if (mime == "video/mp4v-es") | 80 if (mime == "video/mp4v-es") |
| 77 return "mp4v"; | 81 return "mp4v"; |
| 78 if (mime == "video/avc") | 82 if (mime == "video/avc") |
| 79 return "avc1"; | 83 return "avc1"; |
| 80 if (mime == "video/x-vnd.on2.vp8") | 84 if (mime == "video/x-vnd.on2.vp8") |
| 81 return "vp8"; | 85 return "vp8"; |
| 82 if (mime == "video/x-vnd.on2.vp9") | 86 if (mime == "video/x-vnd.on2.vp9") |
| 83 return "vp9"; | 87 return "vp9"; |
| 84 if (mime == "audio/mp4a-latm") | 88 if (mime == "audio/mp4a-latm") |
| 85 return "mp4a"; | 89 return "mp4a"; |
| 86 if (mime == "audio/mpeg") | 90 if (mime == "audio/mpeg") |
| 87 return "mp3"; | 91 return "mp3"; |
| 88 if (mime == "audio/vorbis") | 92 if (mime == "audio/vorbis") |
| 89 return "vorbis"; | 93 return "vorbis"; |
| 90 return std::string(); | 94 return std::string(); |
| 91 } | 95 } |
| 92 | 96 |
| 93 static ScopedJavaLocalRef<jintArray> ToJavaIntArray( | 97 static ScopedJavaLocalRef<jintArray> |
| 94 JNIEnv* env, scoped_ptr<jint[]> native_array, int size) { | 98 ToJavaIntArray(JNIEnv* env, scoped_ptr<jint[]> native_array, int size) { |
| 95 ScopedJavaLocalRef<jintArray> j_array(env, env->NewIntArray(size)); | 99 ScopedJavaLocalRef<jintArray> j_array(env, env->NewIntArray(size)); |
| 96 env->SetIntArrayRegion(j_array.obj(), 0, size, native_array.get()); | 100 env->SetIntArrayRegion(j_array.obj(), 0, size, native_array.get()); |
| 97 return j_array; | 101 return j_array; |
| 98 } | 102 } |
| 99 | 103 |
| 100 // static | 104 // static |
| 101 bool MediaCodecBridge::IsAvailable() { | 105 bool MediaCodecBridge::IsAvailable() { |
| 102 // MediaCodec is only available on JB and greater. | 106 // MediaCodec is only available on JB and greater. |
| 103 return base::android::BuildInfo::GetInstance()->sdk_int() >= 16; | 107 return base::android::BuildInfo::GetInstance()->sdk_int() >= 16; |
| 104 } | 108 } |
| 105 | 109 |
| 106 // static | 110 // static |
| 107 void MediaCodecBridge::GetCodecsInfo( | 111 bool MediaCodecBridge::SupportsSetParameters() { |
| 108 std::vector<CodecsInfo>* codecs_info) { | 112 // MediaCodec.setParameters() is only available starting with K. |
| 113 return base::android::BuildInfo::GetInstance()->sdk_int() >= 19; |
| 114 } |
| 115 |
| 116 // static |
| 117 std::vector<MediaCodecBridge::CodecsInfo> MediaCodecBridge::GetCodecsInfo() { |
| 118 std::vector<CodecsInfo> codecs_info; |
| 109 JNIEnv* env = AttachCurrentThread(); | 119 JNIEnv* env = AttachCurrentThread(); |
| 110 if (!IsAvailable()) | 120 if (!IsAvailable()) |
| 111 return; | 121 return codecs_info; |
| 112 | 122 |
| 113 std::string mime_type; | 123 std::string mime_type; |
| 114 std::string codec_name; | 124 std::string codec_name; |
| 115 ScopedJavaLocalRef<jobjectArray> j_codec_info_array = | 125 ScopedJavaLocalRef<jobjectArray> j_codec_info_array = |
| 116 Java_MediaCodecBridge_getCodecsInfo(env); | 126 Java_MediaCodecBridge_getCodecsInfo(env); |
| 117 jsize len = env->GetArrayLength(j_codec_info_array.obj()); | 127 jsize len = env->GetArrayLength(j_codec_info_array.obj()); |
| 118 for (jsize i = 0; i < len; ++i) { | 128 for (jsize i = 0; i < len; ++i) { |
| 119 ScopedJavaLocalRef<jobject> j_info( | 129 ScopedJavaLocalRef<jobject> j_info( |
| 120 env, env->GetObjectArrayElement(j_codec_info_array.obj(), i)); | 130 env, env->GetObjectArrayElement(j_codec_info_array.obj(), i)); |
| 121 ScopedJavaLocalRef<jstring> j_codec_type = | 131 ScopedJavaLocalRef<jstring> j_codec_type = |
| 122 Java_CodecInfo_codecType(env, j_info.obj()); | 132 Java_CodecInfo_codecType(env, j_info.obj()); |
| 123 ConvertJavaStringToUTF8(env, j_codec_type.obj(), &mime_type); | 133 ConvertJavaStringToUTF8(env, j_codec_type.obj(), &mime_type); |
| 124 ScopedJavaLocalRef<jstring> j_codec_name = | 134 ScopedJavaLocalRef<jstring> j_codec_name = |
| 125 Java_CodecInfo_codecName(env, j_info.obj()); | 135 Java_CodecInfo_codecName(env, j_info.obj()); |
| 126 CodecsInfo info; | 136 CodecsInfo info; |
| 127 info.codecs = AndroidMimeTypeToCodecType(mime_type); | 137 info.codecs = AndroidMimeTypeToCodecType(mime_type); |
| 128 ConvertJavaStringToUTF8(env, j_codec_name.obj(), &info.name); | 138 ConvertJavaStringToUTF8(env, j_codec_name.obj(), &info.name); |
| 129 codecs_info->push_back(info); | 139 info.direction = static_cast<MediaCodecDirection>( |
| 140 Java_CodecInfo_direction(env, j_info.obj())); |
| 141 codecs_info.push_back(info); |
| 130 } | 142 } |
| 143 return codecs_info; |
| 131 } | 144 } |
| 132 | 145 |
| 133 // static | 146 // static |
| 134 bool MediaCodecBridge::CanDecode(const std::string& codec, bool is_secure) { | 147 bool MediaCodecBridge::CanDecode(const std::string& codec, bool is_secure) { |
| 135 JNIEnv* env = AttachCurrentThread(); | 148 JNIEnv* env = AttachCurrentThread(); |
| 136 std::string mime = CodecTypeToAndroidMimeType(codec); | 149 std::string mime = CodecTypeToAndroidMimeType(codec); |
| 137 if (mime.empty()) | 150 if (mime.empty()) |
| 138 return false; | 151 return false; |
| 139 ScopedJavaLocalRef<jstring> j_mime = ConvertUTF8ToJavaString(env, mime); | 152 ScopedJavaLocalRef<jstring> j_mime = ConvertUTF8ToJavaString(env, mime); |
| 140 ScopedJavaLocalRef<jobject> j_media_codec_bridge = | 153 ScopedJavaLocalRef<jobject> j_media_codec_bridge = |
| 141 Java_MediaCodecBridge_create(env, j_mime.obj(), is_secure); | 154 Java_MediaCodecBridge_create(env, j_mime.obj(), is_secure, false); |
| 142 if (!j_media_codec_bridge.is_null()) { | 155 if (!j_media_codec_bridge.is_null()) { |
| 143 Java_MediaCodecBridge_release(env, j_media_codec_bridge.obj()); | 156 Java_MediaCodecBridge_release(env, j_media_codec_bridge.obj()); |
| 144 return true; | 157 return true; |
| 145 } | 158 } |
| 146 return false; | 159 return false; |
| 147 } | 160 } |
| 148 | 161 |
| 149 // static | 162 // static |
| 150 bool MediaCodecBridge::IsKnownUnaccelerated(const std::string& mime_type) { | 163 bool MediaCodecBridge::IsKnownUnaccelerated(const std::string& mime_type, |
| 164 MediaCodecDirection direction) { |
| 151 std::string codec_type = AndroidMimeTypeToCodecType(mime_type); | 165 std::string codec_type = AndroidMimeTypeToCodecType(mime_type); |
| 152 std::vector<media::MediaCodecBridge::CodecsInfo> codecs_info; | 166 std::vector<media::MediaCodecBridge::CodecsInfo> codecs_info = |
| 153 media::MediaCodecBridge::GetCodecsInfo(&codecs_info); | 167 MediaCodecBridge::GetCodecsInfo(); |
| 154 for (size_t i = 0; i < codecs_info.size(); ++i) { | 168 for (size_t i = 0; i < codecs_info.size(); ++i) { |
| 155 if (codecs_info[i].codecs == codec_type) { | 169 if (codecs_info[i].codecs == codec_type && |
| 170 codecs_info[i].direction == direction) { |
| 156 // It would be nice if MediaCodecInfo externalized some notion of | 171 // It would be nice if MediaCodecInfo externalized some notion of |
| 157 // HW-acceleration but it doesn't. Android Media guidance is that the | 172 // HW-acceleration but it doesn't. Android Media guidance is that the |
| 158 // prefix below is always used for SW decoders, so that's what we use. | 173 // prefix below is always used for SW decoders, so that's what we use. |
| 159 return StartsWithASCII(codecs_info[i].name, "OMX.google.", true); | 174 return StartsWithASCII(codecs_info[i].name, "OMX.google.", true); |
| 160 } | 175 } |
| 161 } | 176 } |
| 162 return true; | 177 return true; |
| 163 } | 178 } |
| 164 | 179 |
| 165 MediaCodecBridge::MediaCodecBridge(const std::string& mime, bool is_secure) { | 180 MediaCodecBridge::MediaCodecBridge(const std::string& mime, |
| 181 bool is_secure, |
| 182 MediaCodecDirection direction) { |
| 166 JNIEnv* env = AttachCurrentThread(); | 183 JNIEnv* env = AttachCurrentThread(); |
| 167 CHECK(env); | 184 CHECK(env); |
| 168 DCHECK(!mime.empty()); | 185 DCHECK(!mime.empty()); |
| 169 ScopedJavaLocalRef<jstring> j_mime = ConvertUTF8ToJavaString(env, mime); | 186 ScopedJavaLocalRef<jstring> j_mime = ConvertUTF8ToJavaString(env, mime); |
| 170 j_media_codec_.Reset( | 187 j_media_codec_.Reset( |
| 171 Java_MediaCodecBridge_create(env, j_mime.obj(), is_secure)); | 188 Java_MediaCodecBridge_create(env, j_mime.obj(), is_secure, direction)); |
| 172 } | 189 } |
| 173 | 190 |
| 174 MediaCodecBridge::~MediaCodecBridge() { | 191 MediaCodecBridge::~MediaCodecBridge() { |
| 175 JNIEnv* env = AttachCurrentThread(); | 192 JNIEnv* env = AttachCurrentThread(); |
| 176 CHECK(env); | 193 CHECK(env); |
| 177 if (j_media_codec_.obj()) | 194 if (j_media_codec_.obj()) |
| 178 Java_MediaCodecBridge_release(env, j_media_codec_.obj()); | 195 Java_MediaCodecBridge_release(env, j_media_codec_.obj()); |
| 179 } | 196 } |
| 180 | 197 |
| 181 bool MediaCodecBridge::StartInternal() { | 198 bool MediaCodecBridge::StartInternal() { |
| (...skipping 14 matching lines...) Expand all Loading... |
| 196 } | 213 } |
| 197 | 214 |
| 198 void MediaCodecBridge::GetOutputFormat(int* width, int* height) { | 215 void MediaCodecBridge::GetOutputFormat(int* width, int* height) { |
| 199 JNIEnv* env = AttachCurrentThread(); | 216 JNIEnv* env = AttachCurrentThread(); |
| 200 | 217 |
| 201 *width = Java_MediaCodecBridge_getOutputWidth(env, j_media_codec_.obj()); | 218 *width = Java_MediaCodecBridge_getOutputWidth(env, j_media_codec_.obj()); |
| 202 *height = Java_MediaCodecBridge_getOutputHeight(env, j_media_codec_.obj()); | 219 *height = Java_MediaCodecBridge_getOutputHeight(env, j_media_codec_.obj()); |
| 203 } | 220 } |
| 204 | 221 |
| 205 MediaCodecStatus MediaCodecBridge::QueueInputBuffer( | 222 MediaCodecStatus MediaCodecBridge::QueueInputBuffer( |
| 206 int index, const uint8* data, int data_size, | 223 int index, |
| 224 const uint8* data, |
| 225 int orig_data_size, |
| 207 const base::TimeDelta& presentation_time) { | 226 const base::TimeDelta& presentation_time) { |
| 208 if (!FillInputBuffer(index, data, data_size)) | 227 DVLOG(3) << "MediaCodecBridge::QueueInputBuffer: " << index << ": " |
| 228 << orig_data_size; |
| 229 size_t data_size = base::checked_numeric_cast<size_t>(orig_data_size); |
| 230 if (data && !FillInputBuffer(index, data, data_size)) |
| 209 return MEDIA_CODEC_ERROR; | 231 return MEDIA_CODEC_ERROR; |
| 210 JNIEnv* env = AttachCurrentThread(); | 232 JNIEnv* env = AttachCurrentThread(); |
| 211 return static_cast<MediaCodecStatus>(Java_MediaCodecBridge_queueInputBuffer( | 233 return static_cast<MediaCodecStatus>( |
| 212 env, j_media_codec_.obj(), | 234 Java_MediaCodecBridge_queueInputBuffer(env, |
| 213 index, 0, data_size, presentation_time.InMicroseconds(), 0)); | 235 j_media_codec_.obj(), |
| 236 index, |
| 237 0, |
| 238 data_size, |
| 239 presentation_time.InMicroseconds(), |
| 240 0)); |
| 214 } | 241 } |
| 215 | 242 |
| 216 MediaCodecStatus MediaCodecBridge::QueueSecureInputBuffer( | 243 MediaCodecStatus MediaCodecBridge::QueueSecureInputBuffer( |
| 217 int index, const uint8* data, int data_size, const uint8* key_id, | 244 int index, |
| 218 int key_id_size, const uint8* iv, int iv_size, | 245 const uint8* data, |
| 219 const SubsampleEntry* subsamples, int subsamples_size, | 246 int orig_data_size, |
| 247 const uint8* key_id, |
| 248 int key_id_size, |
| 249 const uint8* iv, |
| 250 int iv_size, |
| 251 const SubsampleEntry* subsamples, |
| 252 int subsamples_size, |
| 220 const base::TimeDelta& presentation_time) { | 253 const base::TimeDelta& presentation_time) { |
| 221 if (!FillInputBuffer(index, data, data_size)) | 254 DVLOG(3) << "MediaCodecBridge::QueueSecureInputBuffer: " << index << ": " |
| 255 << orig_data_size; |
| 256 size_t data_size = base::checked_numeric_cast<size_t>(orig_data_size); |
| 257 if (data && !FillInputBuffer(index, data, data_size)) |
| 222 return MEDIA_CODEC_ERROR; | 258 return MEDIA_CODEC_ERROR; |
| 223 | 259 |
| 224 JNIEnv* env = AttachCurrentThread(); | 260 JNIEnv* env = AttachCurrentThread(); |
| 225 ScopedJavaLocalRef<jbyteArray> j_key_id = | 261 ScopedJavaLocalRef<jbyteArray> j_key_id = |
| 226 base::android::ToJavaByteArray(env, key_id, key_id_size); | 262 base::android::ToJavaByteArray(env, key_id, key_id_size); |
| 227 ScopedJavaLocalRef<jbyteArray> j_iv = | 263 ScopedJavaLocalRef<jbyteArray> j_iv = |
| 228 base::android::ToJavaByteArray(env, iv, iv_size); | 264 base::android::ToJavaByteArray(env, iv, iv_size); |
| 229 | 265 |
| 230 // MediaCodec.CryptoInfo documentations says passing NULL for |clear_array| | 266 // MediaCodec.CryptoInfo documentations says passing NULL for |clear_array| |
| 231 // to indicate that all data is encrypted. But it doesn't specify what | 267 // to indicate that all data is encrypted. But it doesn't specify what |
| (...skipping 17 matching lines...) Expand all Loading... |
| 249 static_cast<uint32>(std::numeric_limits<jint>::max())) { | 285 static_cast<uint32>(std::numeric_limits<jint>::max())) { |
| 250 return MEDIA_CODEC_ERROR; | 286 return MEDIA_CODEC_ERROR; |
| 251 } | 287 } |
| 252 | 288 |
| 253 native_clear_array[i] = subsamples[i].clear_bytes; | 289 native_clear_array[i] = subsamples[i].clear_bytes; |
| 254 native_cypher_array[i] = subsamples[i].cypher_bytes; | 290 native_cypher_array[i] = subsamples[i].cypher_bytes; |
| 255 } | 291 } |
| 256 } | 292 } |
| 257 | 293 |
| 258 ScopedJavaLocalRef<jintArray> clear_array = | 294 ScopedJavaLocalRef<jintArray> clear_array = |
| 259 ToJavaIntArray(env, native_clear_array.Pass(), new_subsamples_size); | 295 ToJavaIntArray(env, native_clear_array.Pass(), new_subsamples_size); |
| 260 ScopedJavaLocalRef<jintArray> cypher_array = | 296 ScopedJavaLocalRef<jintArray> cypher_array = |
| 261 ToJavaIntArray(env, native_cypher_array.Pass(), new_subsamples_size); | 297 ToJavaIntArray(env, native_cypher_array.Pass(), new_subsamples_size); |
| 262 | 298 |
| 263 return static_cast<MediaCodecStatus>( | 299 return static_cast<MediaCodecStatus>( |
| 264 Java_MediaCodecBridge_queueSecureInputBuffer( | 300 Java_MediaCodecBridge_queueSecureInputBuffer( |
| 265 env, j_media_codec_.obj(), index, 0, j_iv.obj(), j_key_id.obj(), | 301 env, |
| 266 clear_array.obj(), cypher_array.obj(), new_subsamples_size, | 302 j_media_codec_.obj(), |
| 303 index, |
| 304 0, |
| 305 j_iv.obj(), |
| 306 j_key_id.obj(), |
| 307 clear_array.obj(), |
| 308 cypher_array.obj(), |
| 309 new_subsamples_size, |
| 267 presentation_time.InMicroseconds())); | 310 presentation_time.InMicroseconds())); |
| 268 } | 311 } |
| 269 | 312 |
| 270 void MediaCodecBridge::QueueEOS(int input_buffer_index) { | 313 void MediaCodecBridge::QueueEOS(int input_buffer_index) { |
| 314 DVLOG(3) << "MediaCodecBridge::QueueEOS: " << input_buffer_index; |
| 271 JNIEnv* env = AttachCurrentThread(); | 315 JNIEnv* env = AttachCurrentThread(); |
| 272 Java_MediaCodecBridge_queueInputBuffer( | 316 Java_MediaCodecBridge_queueInputBuffer(env, |
| 273 env, j_media_codec_.obj(), | 317 j_media_codec_.obj(), |
| 274 input_buffer_index, 0, 0, 0, kBufferFlagEndOfStream); | 318 input_buffer_index, |
| 319 0, |
| 320 0, |
| 321 0, |
| 322 kBufferFlagEndOfStream); |
| 275 } | 323 } |
| 276 | 324 |
| 277 MediaCodecStatus MediaCodecBridge::DequeueInputBuffer( | 325 MediaCodecStatus MediaCodecBridge::DequeueInputBuffer( |
| 278 const base::TimeDelta& timeout, int* index) { | 326 const base::TimeDelta& timeout, |
| 327 int* index) { |
| 279 JNIEnv* env = AttachCurrentThread(); | 328 JNIEnv* env = AttachCurrentThread(); |
| 280 ScopedJavaLocalRef<jobject> result = Java_MediaCodecBridge_dequeueInputBuffer( | 329 ScopedJavaLocalRef<jobject> result = Java_MediaCodecBridge_dequeueInputBuffer( |
| 281 env, j_media_codec_.obj(), timeout.InMicroseconds()); | 330 env, j_media_codec_.obj(), timeout.InMicroseconds()); |
| 282 *index = Java_DequeueInputResult_index(env, result.obj()); | 331 *index = Java_DequeueInputResult_index(env, result.obj()); |
| 283 return static_cast<MediaCodecStatus>( | 332 MediaCodecStatus status = static_cast<MediaCodecStatus>( |
| 284 Java_DequeueInputResult_status(env, result.obj())); | 333 Java_DequeueInputResult_status(env, result.obj())); |
| 334 DVLOG(3) << "MediaCodecBridge::DequeueInputBuffer: status: " << status |
| 335 << ", index: " << *index; |
| 336 return status; |
| 285 } | 337 } |
| 286 | 338 |
| 287 MediaCodecStatus MediaCodecBridge::DequeueOutputBuffer( | 339 MediaCodecStatus MediaCodecBridge::DequeueOutputBuffer( |
| 288 const base::TimeDelta& timeout, int* index, size_t* offset, size_t* size, | 340 const base::TimeDelta& timeout, |
| 289 base::TimeDelta* presentation_time, bool* end_of_stream) { | 341 int* index, |
| 342 size_t* offset, |
| 343 size_t* size, |
| 344 base::TimeDelta* presentation_time, |
| 345 bool* end_of_stream, |
| 346 bool* key_frame) { |
| 290 JNIEnv* env = AttachCurrentThread(); | 347 JNIEnv* env = AttachCurrentThread(); |
| 291 ScopedJavaLocalRef<jobject> result = | 348 ScopedJavaLocalRef<jobject> result = |
| 292 Java_MediaCodecBridge_dequeueOutputBuffer(env, j_media_codec_.obj(), | 349 Java_MediaCodecBridge_dequeueOutputBuffer( |
| 293 timeout.InMicroseconds()); | 350 env, j_media_codec_.obj(), timeout.InMicroseconds()); |
| 294 *index = Java_DequeueOutputResult_index(env, result.obj());; | 351 *index = Java_DequeueOutputResult_index(env, result.obj()); |
| 295 *offset = base::checked_numeric_cast<size_t>( | 352 *offset = base::checked_numeric_cast<size_t>( |
| 296 Java_DequeueOutputResult_offset(env, result.obj())); | 353 Java_DequeueOutputResult_offset(env, result.obj())); |
| 297 *size = base::checked_numeric_cast<size_t>( | 354 *size = base::checked_numeric_cast<size_t>( |
| 298 Java_DequeueOutputResult_numBytes(env, result.obj())); | 355 Java_DequeueOutputResult_numBytes(env, result.obj())); |
| 299 *presentation_time = base::TimeDelta::FromMicroseconds( | 356 if (presentation_time) { |
| 300 Java_DequeueOutputResult_presentationTimeMicroseconds(env, result.obj())); | 357 *presentation_time = base::TimeDelta::FromMicroseconds( |
| 358 Java_DequeueOutputResult_presentationTimeMicroseconds(env, |
| 359 result.obj())); |
| 360 } |
| 301 int flags = Java_DequeueOutputResult_flags(env, result.obj()); | 361 int flags = Java_DequeueOutputResult_flags(env, result.obj()); |
| 302 *end_of_stream = flags & kBufferFlagEndOfStream; | 362 if (end_of_stream) |
| 303 return static_cast<MediaCodecStatus>( | 363 *end_of_stream = flags & kBufferFlagEndOfStream; |
| 364 if (key_frame) |
| 365 *key_frame = flags & kBufferFlagSyncFrame; |
| 366 MediaCodecStatus status = static_cast<MediaCodecStatus>( |
| 304 Java_DequeueOutputResult_status(env, result.obj())); | 367 Java_DequeueOutputResult_status(env, result.obj())); |
| 368 DVLOG(3) << "MediaCodecBridge::DequeueOutputBuffer: status: " << status |
| 369 << ", index: " << *index << ", offset: " << *offset |
| 370 << ", size: " << *size << ", flags: " << flags; |
| 371 return status; |
| 305 } | 372 } |
| 306 | 373 |
| 307 void MediaCodecBridge::ReleaseOutputBuffer(int index, bool render) { | 374 void MediaCodecBridge::ReleaseOutputBuffer(int index, bool render) { |
| 375 DVLOG(3) << "MediaCodecBridge::ReleaseOutputBuffer: " << index; |
| 308 JNIEnv* env = AttachCurrentThread(); | 376 JNIEnv* env = AttachCurrentThread(); |
| 309 CHECK(env); | 377 CHECK(env); |
| 310 | 378 |
| 311 Java_MediaCodecBridge_releaseOutputBuffer( | 379 Java_MediaCodecBridge_releaseOutputBuffer( |
| 312 env, j_media_codec_.obj(), index, render); | 380 env, j_media_codec_.obj(), index, render); |
| 313 } | 381 } |
| 314 | 382 |
| 383 int MediaCodecBridge::GetInputBuffersCount() { |
| 384 JNIEnv* env = AttachCurrentThread(); |
| 385 return Java_MediaCodecBridge_getInputBuffersCount(env, j_media_codec_.obj()); |
| 386 } |
| 387 |
| 388 int MediaCodecBridge::GetOutputBuffersCount() { |
| 389 JNIEnv* env = AttachCurrentThread(); |
| 390 return Java_MediaCodecBridge_getOutputBuffersCount(env, j_media_codec_.obj()); |
| 391 } |
| 392 |
| 393 size_t MediaCodecBridge::GetOutputBuffersCapacity() { |
| 394 JNIEnv* env = AttachCurrentThread(); |
| 395 return Java_MediaCodecBridge_getOutputBuffersCapacity(env, |
| 396 j_media_codec_.obj()); |
| 397 } |
| 398 |
| 315 bool MediaCodecBridge::GetOutputBuffers() { | 399 bool MediaCodecBridge::GetOutputBuffers() { |
| 316 JNIEnv* env = AttachCurrentThread(); | 400 JNIEnv* env = AttachCurrentThread(); |
| 317 return Java_MediaCodecBridge_getOutputBuffers(env, j_media_codec_.obj()); | 401 return Java_MediaCodecBridge_getOutputBuffers(env, j_media_codec_.obj()); |
| 318 } | 402 } |
| 319 | 403 |
| 320 bool MediaCodecBridge::FillInputBuffer(int index, const uint8* data, int size) { | 404 void MediaCodecBridge::GetInputBuffer(int input_buffer_index, |
| 405 uint8** data, |
| 406 size_t* capacity) { |
| 321 JNIEnv* env = AttachCurrentThread(); | 407 JNIEnv* env = AttachCurrentThread(); |
| 408 ScopedJavaLocalRef<jobject> j_buffer(Java_MediaCodecBridge_getInputBuffer( |
| 409 env, j_media_codec_.obj(), input_buffer_index)); |
| 410 *data = static_cast<uint8*>(env->GetDirectBufferAddress(j_buffer.obj())); |
| 411 *capacity = base::checked_numeric_cast<size_t>( |
| 412 env->GetDirectBufferCapacity(j_buffer.obj())); |
| 413 } |
| 322 | 414 |
| 415 bool MediaCodecBridge::CopyFromOutputBuffer(int index, |
| 416 size_t offset, |
| 417 void* dst, |
| 418 int dst_size) { |
| 419 JNIEnv* env = AttachCurrentThread(); |
| 323 ScopedJavaLocalRef<jobject> j_buffer( | 420 ScopedJavaLocalRef<jobject> j_buffer( |
| 324 Java_MediaCodecBridge_getInputBuffer(env, j_media_codec_.obj(), index)); | 421 Java_MediaCodecBridge_getOutputBuffer(env, j_media_codec_.obj(), index)); |
| 325 jlong capacity = env->GetDirectBufferCapacity(j_buffer.obj()); | 422 void* src_data = |
| 423 reinterpret_cast<uint8*>(env->GetDirectBufferAddress(j_buffer.obj())) + |
| 424 offset; |
| 425 int src_capacity = env->GetDirectBufferCapacity(j_buffer.obj()) - offset; |
| 426 if (src_capacity < dst_size) |
| 427 return false; |
| 428 memcpy(dst, src_data, dst_size); |
| 429 return true; |
| 430 } |
| 431 |
| 432 bool MediaCodecBridge::FillInputBuffer(int index, |
| 433 const uint8* data, |
| 434 size_t size) { |
| 435 uint8* dst = NULL; |
| 436 size_t capacity = 0; |
| 437 GetInputBuffer(index, &dst, &capacity); |
| 438 CHECK(dst); |
| 439 |
| 326 if (size > capacity) { | 440 if (size > capacity) { |
| 327 LOG(ERROR) << "Input buffer size " << size | 441 LOG(ERROR) << "Input buffer size " << size |
| 328 << " exceeds MediaCodec input buffer capacity: " << capacity; | 442 << " exceeds MediaCodec input buffer capacity: " << capacity; |
| 329 return false; | 443 return false; |
| 330 } | 444 } |
| 331 | 445 |
| 332 uint8* direct_buffer = | 446 memcpy(dst, data, size); |
| 333 static_cast<uint8*>(env->GetDirectBufferAddress(j_buffer.obj())); | |
| 334 memcpy(direct_buffer, data, size); | |
| 335 return true; | 447 return true; |
| 336 } | 448 } |
| 337 | 449 |
| 338 AudioCodecBridge::AudioCodecBridge(const std::string& mime) | 450 AudioCodecBridge::AudioCodecBridge(const std::string& mime) |
| 339 // Audio codec doesn't care about security level. | 451 // Audio codec doesn't care about security level and there is no need for |
| 340 : MediaCodecBridge(mime, false) { | 452 // audio encoding yet. |
| 341 } | 453 : MediaCodecBridge(mime, false, MEDIA_CODEC_DECODER) {} |
| 342 | 454 |
| 343 bool AudioCodecBridge::Start( | 455 bool AudioCodecBridge::Start(const AudioCodec& codec, |
| 344 const AudioCodec& codec, int sample_rate, int channel_count, | 456 int sample_rate, |
| 345 const uint8* extra_data, size_t extra_data_size, bool play_audio, | 457 int channel_count, |
| 346 jobject media_crypto) { | 458 const uint8* extra_data, |
| 459 size_t extra_data_size, |
| 460 bool play_audio, |
| 461 jobject media_crypto) { |
| 347 JNIEnv* env = AttachCurrentThread(); | 462 JNIEnv* env = AttachCurrentThread(); |
| 348 | 463 |
| 349 if (!media_codec()) | 464 if (!media_codec()) |
| 350 return false; | 465 return false; |
| 351 | 466 |
| 352 std::string codec_string = AudioCodecToAndroidMimeType(codec); | 467 std::string codec_string = AudioCodecToAndroidMimeType(codec); |
| 353 if (codec_string.empty()) | 468 if (codec_string.empty()) |
| 354 return false; | 469 return false; |
| 355 | 470 |
| 356 ScopedJavaLocalRef<jstring> j_mime = | 471 ScopedJavaLocalRef<jstring> j_mime = |
| 357 ConvertUTF8ToJavaString(env, codec_string); | 472 ConvertUTF8ToJavaString(env, codec_string); |
| 358 ScopedJavaLocalRef<jobject> j_format( | 473 ScopedJavaLocalRef<jobject> j_format(Java_MediaCodecBridge_createAudioFormat( |
| 359 Java_MediaCodecBridge_createAudioFormat( | 474 env, j_mime.obj(), sample_rate, channel_count)); |
| 360 env, j_mime.obj(), sample_rate, channel_count)); | |
| 361 DCHECK(!j_format.is_null()); | 475 DCHECK(!j_format.is_null()); |
| 362 | 476 |
| 363 if (!ConfigureMediaFormat(j_format.obj(), codec, extra_data, extra_data_size)) | 477 if (!ConfigureMediaFormat(j_format.obj(), codec, extra_data, extra_data_size)) |
| 364 return false; | 478 return false; |
| 365 | 479 |
| 366 if (!Java_MediaCodecBridge_configureAudio( | 480 if (!Java_MediaCodecBridge_configureAudio( |
| 367 env, media_codec(), j_format.obj(), media_crypto, 0, play_audio)) { | 481 env, media_codec(), j_format.obj(), media_crypto, 0, play_audio)) { |
| 368 return false; | 482 return false; |
| 369 } | 483 } |
| 370 | 484 |
| 371 return StartInternal(); | 485 return StartInternal(); |
| 372 } | 486 } |
| 373 | 487 |
| 374 bool AudioCodecBridge::ConfigureMediaFormat( | 488 bool AudioCodecBridge::ConfigureMediaFormat(jobject j_format, |
| 375 jobject j_format, const AudioCodec& codec, const uint8* extra_data, | 489 const AudioCodec& codec, |
| 376 size_t extra_data_size) { | 490 const uint8* extra_data, |
| 491 size_t extra_data_size) { |
| 377 if (extra_data_size == 0) | 492 if (extra_data_size == 0) |
| 378 return true; | 493 return true; |
| 379 | 494 |
| 380 JNIEnv* env = AttachCurrentThread(); | 495 JNIEnv* env = AttachCurrentThread(); |
| 381 switch (codec) { | 496 switch (codec) { |
| 382 case kCodecVorbis: | 497 case kCodecVorbis: { |
| 383 { | |
| 384 if (extra_data[0] != 2) { | 498 if (extra_data[0] != 2) { |
| 385 LOG(ERROR) << "Invalid number of vorbis headers before the codec " | 499 LOG(ERROR) << "Invalid number of vorbis headers before the codec " |
| 386 << "header: " << extra_data[0]; | 500 << "header: " << extra_data[0]; |
| 387 return false; | 501 return false; |
| 388 } | 502 } |
| 389 | 503 |
| 390 size_t header_length[2]; | 504 size_t header_length[2]; |
| 391 // |total_length| keeps track of the total number of bytes before the last | 505 // |total_length| keeps track of the total number of bytes before the last |
| 392 // header. | 506 // header. |
| 393 size_t total_length = 1; | 507 size_t total_length = 1; |
| (...skipping 24 matching lines...) Expand all Loading... |
| 418 Java_MediaCodecBridge_setCodecSpecificData( | 532 Java_MediaCodecBridge_setCodecSpecificData( |
| 419 env, j_format, 0, first_header.obj()); | 533 env, j_format, 0, first_header.obj()); |
| 420 // The last header is codec header. | 534 // The last header is codec header. |
| 421 ScopedJavaLocalRef<jbyteArray> last_header = | 535 ScopedJavaLocalRef<jbyteArray> last_header = |
| 422 base::android::ToJavaByteArray( | 536 base::android::ToJavaByteArray( |
| 423 env, extra_data + total_length, extra_data_size - total_length); | 537 env, extra_data + total_length, extra_data_size - total_length); |
| 424 Java_MediaCodecBridge_setCodecSpecificData( | 538 Java_MediaCodecBridge_setCodecSpecificData( |
| 425 env, j_format, 1, last_header.obj()); | 539 env, j_format, 1, last_header.obj()); |
| 426 break; | 540 break; |
| 427 } | 541 } |
| 428 case kCodecAAC: | 542 case kCodecAAC: { |
| 429 { | |
| 430 media::BitReader reader(extra_data, extra_data_size); | 543 media::BitReader reader(extra_data, extra_data_size); |
| 431 | 544 |
| 432 // The following code is copied from aac.cc | 545 // The following code is copied from aac.cc |
| 433 // TODO(qinmin): refactor the code in aac.cc to make it more reusable. | 546 // TODO(qinmin): refactor the code in aac.cc to make it more reusable. |
| 434 uint8 profile = 0; | 547 uint8 profile = 0; |
| 435 uint8 frequency_index = 0; | 548 uint8 frequency_index = 0; |
| 436 uint8 channel_config = 0; | 549 uint8 channel_config = 0; |
| 437 if (!reader.ReadBits(5, &profile) || | 550 if (!reader.ReadBits(5, &profile) || |
| 438 !reader.ReadBits(4, &frequency_index)) { | 551 !reader.ReadBits(4, &frequency_index)) { |
| 439 LOG(ERROR) << "Unable to parse AAC header"; | 552 LOG(ERROR) << "Unable to parse AAC header"; |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 478 void AudioCodecBridge::PlayOutputBuffer(int index, size_t size) { | 591 void AudioCodecBridge::PlayOutputBuffer(int index, size_t size) { |
| 479 DCHECK_LE(0, index); | 592 DCHECK_LE(0, index); |
| 480 int numBytes = base::checked_numeric_cast<int>(size); | 593 int numBytes = base::checked_numeric_cast<int>(size); |
| 481 JNIEnv* env = AttachCurrentThread(); | 594 JNIEnv* env = AttachCurrentThread(); |
| 482 ScopedJavaLocalRef<jobject> buf = | 595 ScopedJavaLocalRef<jobject> buf = |
| 483 Java_MediaCodecBridge_getOutputBuffer(env, media_codec(), index); | 596 Java_MediaCodecBridge_getOutputBuffer(env, media_codec(), index); |
| 484 uint8* buffer = static_cast<uint8*>(env->GetDirectBufferAddress(buf.obj())); | 597 uint8* buffer = static_cast<uint8*>(env->GetDirectBufferAddress(buf.obj())); |
| 485 | 598 |
| 486 ScopedJavaLocalRef<jbyteArray> byte_array = | 599 ScopedJavaLocalRef<jbyteArray> byte_array = |
| 487 base::android::ToJavaByteArray(env, buffer, numBytes); | 600 base::android::ToJavaByteArray(env, buffer, numBytes); |
| 488 Java_MediaCodecBridge_playOutputBuffer( | 601 Java_MediaCodecBridge_playOutputBuffer(env, media_codec(), byte_array.obj()); |
| 489 env, media_codec(), byte_array.obj()); | |
| 490 } | 602 } |
| 491 | 603 |
| 492 void AudioCodecBridge::SetVolume(double volume) { | 604 void AudioCodecBridge::SetVolume(double volume) { |
| 493 JNIEnv* env = AttachCurrentThread(); | 605 JNIEnv* env = AttachCurrentThread(); |
| 494 Java_MediaCodecBridge_setVolume(env, media_codec(), volume); | 606 Java_MediaCodecBridge_setVolume(env, media_codec(), volume); |
| 495 } | 607 } |
| 496 | 608 |
| 497 VideoCodecBridge::VideoCodecBridge(const std::string& mime, bool is_secure) | |
| 498 : MediaCodecBridge(mime, is_secure) { | |
| 499 } | |
| 500 | |
| 501 bool VideoCodecBridge::Start( | |
| 502 const VideoCodec& codec, const gfx::Size& size, jobject surface, | |
| 503 jobject media_crypto) { | |
| 504 JNIEnv* env = AttachCurrentThread(); | |
| 505 | |
| 506 if (!media_codec()) | |
| 507 return false; | |
| 508 | |
| 509 std::string codec_string = VideoCodecToAndroidMimeType(codec); | |
| 510 if (codec_string.empty()) | |
| 511 return false; | |
| 512 | |
| 513 ScopedJavaLocalRef<jstring> j_mime = | |
| 514 ConvertUTF8ToJavaString(env, codec_string); | |
| 515 ScopedJavaLocalRef<jobject> j_format( | |
| 516 Java_MediaCodecBridge_createVideoFormat( | |
| 517 env, j_mime.obj(), size.width(), size.height())); | |
| 518 DCHECK(!j_format.is_null()); | |
| 519 if (!Java_MediaCodecBridge_configureVideo( | |
| 520 env, media_codec(), j_format.obj(), surface, media_crypto, 0)) { | |
| 521 return false; | |
| 522 } | |
| 523 | |
| 524 return StartInternal(); | |
| 525 } | |
| 526 | |
| 527 AudioCodecBridge* AudioCodecBridge::Create(const AudioCodec& codec) { | 609 AudioCodecBridge* AudioCodecBridge::Create(const AudioCodec& codec) { |
| 528 const std::string mime = AudioCodecToAndroidMimeType(codec); | 610 const std::string mime = AudioCodecToAndroidMimeType(codec); |
| 529 return mime.empty() ? NULL : new AudioCodecBridge(mime); | 611 return mime.empty() ? NULL : new AudioCodecBridge(mime); |
| 530 } | 612 } |
| 531 | 613 |
| 532 // static | 614 // static |
| 533 bool AudioCodecBridge::IsKnownUnaccelerated(const AudioCodec& codec) { | 615 bool AudioCodecBridge::IsKnownUnaccelerated(const AudioCodec& codec) { |
| 534 return MediaCodecBridge::IsKnownUnaccelerated( | 616 return MediaCodecBridge::IsKnownUnaccelerated( |
| 535 AudioCodecToAndroidMimeType(codec)); | 617 AudioCodecToAndroidMimeType(codec), MEDIA_CODEC_DECODER); |
| 536 } | |
| 537 | |
| 538 VideoCodecBridge* VideoCodecBridge::Create(const VideoCodec& codec, | |
| 539 bool is_secure) { | |
| 540 const std::string mime = VideoCodecToAndroidMimeType(codec); | |
| 541 return mime.empty() ? NULL : new VideoCodecBridge(mime, is_secure); | |
| 542 } | 618 } |
| 543 | 619 |
| 544 // static | 620 // static |
| 545 bool VideoCodecBridge::IsKnownUnaccelerated(const VideoCodec& codec) { | 621 bool VideoCodecBridge::IsKnownUnaccelerated(const VideoCodec& codec, |
| 622 MediaCodecDirection direction) { |
| 546 return MediaCodecBridge::IsKnownUnaccelerated( | 623 return MediaCodecBridge::IsKnownUnaccelerated( |
| 547 VideoCodecToAndroidMimeType(codec)); | 624 VideoCodecToAndroidMimeType(codec), direction); |
| 625 } |
| 626 |
| 627 VideoCodecBridge* VideoCodecBridge::CreateDecoder(const VideoCodec& codec, |
| 628 bool is_secure, |
| 629 const gfx::Size& size, |
| 630 jobject surface, |
| 631 jobject media_crypto) { |
| 632 JNIEnv* env = AttachCurrentThread(); |
| 633 const std::string mime = VideoCodecToAndroidMimeType(codec); |
| 634 if (mime.empty()) |
| 635 return NULL; |
| 636 |
| 637 scoped_ptr<VideoCodecBridge> bridge( |
| 638 new VideoCodecBridge(mime, is_secure, MEDIA_CODEC_DECODER)); |
| 639 |
| 640 ScopedJavaLocalRef<jstring> j_mime = ConvertUTF8ToJavaString(env, mime); |
| 641 ScopedJavaLocalRef<jobject> j_format( |
| 642 Java_MediaCodecBridge_createVideoDecoderFormat( |
| 643 env, j_mime.obj(), size.width(), size.height())); |
| 644 DCHECK(!j_format.is_null()); |
| 645 if (!Java_MediaCodecBridge_configureVideo(env, |
| 646 bridge->media_codec(), |
| 647 j_format.obj(), |
| 648 surface, |
| 649 media_crypto, |
| 650 0)) { |
| 651 return NULL; |
| 652 } |
| 653 |
| 654 return bridge->StartInternal() ? bridge.release() : NULL; |
| 655 } |
| 656 |
| 657 VideoCodecBridge* VideoCodecBridge::CreateEncoder(const VideoCodec& codec, |
| 658 const gfx::Size& size, |
| 659 int bit_rate, |
| 660 int frame_rate, |
| 661 int i_frame_interval, |
| 662 int color_format) { |
| 663 JNIEnv* env = AttachCurrentThread(); |
| 664 const std::string mime = VideoCodecToAndroidMimeType(codec); |
| 665 if (mime.empty()) |
| 666 return NULL; |
| 667 |
| 668 scoped_ptr<VideoCodecBridge> bridge( |
| 669 new VideoCodecBridge(mime, false, MEDIA_CODEC_ENCODER)); |
| 670 |
| 671 ScopedJavaLocalRef<jstring> j_mime = ConvertUTF8ToJavaString(env, mime); |
| 672 ScopedJavaLocalRef<jobject> j_format( |
| 673 Java_MediaCodecBridge_createVideoEncoderFormat(env, |
| 674 j_mime.obj(), |
| 675 size.width(), |
| 676 size.height(), |
| 677 bit_rate, |
| 678 frame_rate, |
| 679 i_frame_interval, |
| 680 color_format)); |
| 681 DCHECK(!j_format.is_null()); |
| 682 if (!Java_MediaCodecBridge_configureVideo(env, |
| 683 bridge->media_codec(), |
| 684 j_format.obj(), |
| 685 NULL, |
| 686 NULL, |
| 687 kConfigureFlagEncode)) { |
| 688 return NULL; |
| 689 } |
| 690 |
| 691 return bridge->StartInternal() ? bridge.release() : NULL; |
| 692 } |
| 693 |
| 694 VideoCodecBridge::VideoCodecBridge(const std::string& mime, |
| 695 bool is_secure, |
| 696 MediaCodecDirection direction) |
| 697 : MediaCodecBridge(mime, is_secure, direction) {} |
| 698 |
| 699 void VideoCodecBridge::SetVideoBitrate(int bps) { |
| 700 JNIEnv* env = AttachCurrentThread(); |
| 701 Java_MediaCodecBridge_setVideoBitrate(env, media_codec(), bps); |
| 702 } |
| 703 |
| 704 void VideoCodecBridge::RequestKeyFrameSoon() { |
| 705 JNIEnv* env = AttachCurrentThread(); |
| 706 Java_MediaCodecBridge_requestKeyFrameSoon(env, media_codec()); |
| 548 } | 707 } |
| 549 | 708 |
| 550 bool MediaCodecBridge::RegisterMediaCodecBridge(JNIEnv* env) { | 709 bool MediaCodecBridge::RegisterMediaCodecBridge(JNIEnv* env) { |
| 551 return RegisterNativesImpl(env); | 710 return RegisterNativesImpl(env); |
| 552 } | 711 } |
| 553 | 712 |
| 554 } // namespace media | 713 } // namespace media |
| OLD | NEW |