| 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 | 8 |
| 9 #include "base/android/build_info.h" | 9 #include "base/android/build_info.h" |
| 10 #include "base/android/jni_android.h" | 10 #include "base/android/jni_android.h" |
| 11 #include "base/android/jni_array.h" | 11 #include "base/android/jni_array.h" |
| 12 #include "base/android/jni_string.h" | 12 #include "base/android/jni_string.h" |
| 13 #include "base/basictypes.h" | 13 #include "base/basictypes.h" |
| 14 #include "base/lazy_instance.h" | 14 #include "base/lazy_instance.h" |
| 15 #include "base/logging.h" | 15 #include "base/logging.h" |
| 16 #include "base/safe_numerics.h" | 16 #include "base/safe_numerics.h" |
| 17 #include "base/stringprintf.h" | 17 #include "base/stringprintf.h" |
| 18 | |
| 19 #include "jni/MediaCodecBridge_jni.h" | 18 #include "jni/MediaCodecBridge_jni.h" |
| 19 #include "media/base/bit_reader.h" |
| 20 | 20 |
| 21 using base::android::AttachCurrentThread; | 21 using base::android::AttachCurrentThread; |
| 22 using base::android::ConvertUTF8ToJavaString; | 22 using base::android::ConvertUTF8ToJavaString; |
| 23 using base::android::ScopedJavaLocalRef; | 23 using base::android::ScopedJavaLocalRef; |
| 24 | 24 |
| 25 namespace media { | 25 namespace media { |
| 26 | 26 |
| 27 enum { kBufferFlagEndOfStream = 4 }; | 27 enum { kBufferFlagEndOfStream = 4 }; |
| 28 | 28 |
| 29 static const char* AudioCodecToMimeType(const AudioCodec codec) { | 29 static const char* AudioCodecToMimeType(const AudioCodec codec) { |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 76 | 76 |
| 77 MediaCodecBridge::~MediaCodecBridge() { | 77 MediaCodecBridge::~MediaCodecBridge() { |
| 78 JNIEnv* env = AttachCurrentThread(); | 78 JNIEnv* env = AttachCurrentThread(); |
| 79 CHECK(env); | 79 CHECK(env); |
| 80 Java_MediaCodecBridge_release(env, j_media_codec_.obj()); | 80 Java_MediaCodecBridge_release(env, j_media_codec_.obj()); |
| 81 } | 81 } |
| 82 | 82 |
| 83 void MediaCodecBridge::StartInternal() { | 83 void MediaCodecBridge::StartInternal() { |
| 84 JNIEnv* env = AttachCurrentThread(); | 84 JNIEnv* env = AttachCurrentThread(); |
| 85 Java_MediaCodecBridge_start(env, j_media_codec_.obj()); | 85 Java_MediaCodecBridge_start(env, j_media_codec_.obj()); |
| 86 GetOutputBuffers(); |
| 86 } | 87 } |
| 87 | 88 |
| 88 void MediaCodecBridge::Reset() { | 89 void MediaCodecBridge::Reset() { |
| 89 JNIEnv* env = AttachCurrentThread(); | 90 JNIEnv* env = AttachCurrentThread(); |
| 90 Java_MediaCodecBridge_flush(env, j_media_codec_.obj()); | 91 Java_MediaCodecBridge_flush(env, j_media_codec_.obj()); |
| 91 } | 92 } |
| 92 | 93 |
| 93 void MediaCodecBridge::Stop() { | 94 void MediaCodecBridge::Stop() { |
| 94 JNIEnv* env = AttachCurrentThread(); | 95 JNIEnv* env = AttachCurrentThread(); |
| 95 Java_MediaCodecBridge_stop(env, j_media_codec_.obj()); | 96 Java_MediaCodecBridge_stop(env, j_media_codec_.obj()); |
| (...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 186 JNIEnv* env = AttachCurrentThread(); | 187 JNIEnv* env = AttachCurrentThread(); |
| 187 DCHECK(AudioCodecToMimeType(codec)); | 188 DCHECK(AudioCodecToMimeType(codec)); |
| 188 | 189 |
| 189 ScopedJavaLocalRef<jstring> j_mime = | 190 ScopedJavaLocalRef<jstring> j_mime = |
| 190 ConvertUTF8ToJavaString(env, AudioCodecToMimeType(codec)); | 191 ConvertUTF8ToJavaString(env, AudioCodecToMimeType(codec)); |
| 191 ScopedJavaLocalRef<jobject> j_format( | 192 ScopedJavaLocalRef<jobject> j_format( |
| 192 Java_MediaCodecBridge_createAudioFormat( | 193 Java_MediaCodecBridge_createAudioFormat( |
| 193 env, j_mime.obj(), sample_rate, channel_count)); | 194 env, j_mime.obj(), sample_rate, channel_count)); |
| 194 DCHECK(!j_format.is_null()); | 195 DCHECK(!j_format.is_null()); |
| 195 | 196 |
| 196 if (extra_data_size > 0) { | 197 if (!ConfigureMediaFormat(j_format.obj(), codec, extra_data, extra_data_size)) |
| 197 DCHECK_EQ(kCodecVorbis, codec); | 198 return false; |
| 198 if (extra_data[0] != 2) { | |
| 199 LOG(ERROR) << "Invalid number of headers before the codec header: " | |
| 200 << extra_data[0]; | |
| 201 return false; | |
| 202 } | |
| 203 | |
| 204 size_t header_length[2]; | |
| 205 // |total_length| keeps track of the total number of bytes before the last | |
| 206 // header. | |
| 207 size_t total_length = 1; | |
| 208 const uint8* current_pos = extra_data; | |
| 209 // Calculate the length of the first 2 headers. | |
| 210 for (int i = 0; i < 2; ++i) { | |
| 211 header_length[i] = 0; | |
| 212 while (total_length < extra_data_size) { | |
| 213 size_t size = *(++current_pos); | |
| 214 total_length += 1 + size; | |
| 215 if (total_length > 0x80000000) { | |
| 216 LOG(ERROR) << "Header size too large"; | |
| 217 return false; | |
| 218 } | |
| 219 header_length[i] += size; | |
| 220 if (size < 0xFF) | |
| 221 break; | |
| 222 } | |
| 223 if (total_length >= extra_data_size) { | |
| 224 LOG(ERROR) << "Invalid header size in the extra data"; | |
| 225 return false; | |
| 226 } | |
| 227 } | |
| 228 current_pos++; | |
| 229 // The first header is identification header. | |
| 230 jobject identification_header = env->NewDirectByteBuffer( | |
| 231 const_cast<uint8*>(current_pos), header_length[0]); | |
| 232 Java_MediaCodecBridge_setCodecSpecificData( | |
| 233 env, j_format.obj(), 0, identification_header); | |
| 234 // The last header is codec header. | |
| 235 jobject codec_header = env->NewDirectByteBuffer( | |
| 236 const_cast<uint8*>(extra_data + total_length), | |
| 237 extra_data_size - total_length); | |
| 238 Java_MediaCodecBridge_setCodecSpecificData( | |
| 239 env, j_format.obj(), 1, codec_header); | |
| 240 env->DeleteLocalRef(codec_header); | |
| 241 env->DeleteLocalRef(identification_header); | |
| 242 } | |
| 243 | 199 |
| 244 Java_MediaCodecBridge_configureAudio( | 200 Java_MediaCodecBridge_configureAudio( |
| 245 env, media_codec(), j_format.obj(), NULL, 0, play_audio); | 201 env, media_codec(), j_format.obj(), NULL, 0, play_audio); |
| 246 StartInternal(); | 202 StartInternal(); |
| 247 return true; | 203 return true; |
| 248 } | 204 } |
| 249 | 205 |
| 206 bool AudioCodecBridge::ConfigureMediaFormat( |
| 207 jobject j_format, const AudioCodec codec, const uint8* extra_data, |
| 208 size_t extra_data_size) { |
| 209 if (extra_data_size == 0) |
| 210 return true; |
| 211 |
| 212 JNIEnv* env = AttachCurrentThread(); |
| 213 switch(codec) { |
| 214 case kCodecVorbis: |
| 215 { |
| 216 if (extra_data[0] != 2) { |
| 217 LOG(ERROR) << "Invalid number of vorbis headers before the codec " |
| 218 << "header: " << extra_data[0]; |
| 219 return false; |
| 220 } |
| 221 |
| 222 size_t header_length[2]; |
| 223 // |total_length| keeps track of the total number of bytes before the last |
| 224 // header. |
| 225 size_t total_length = 1; |
| 226 const uint8* current_pos = extra_data; |
| 227 // Calculate the length of the first 2 headers. |
| 228 for (int i = 0; i < 2; ++i) { |
| 229 header_length[i] = 0; |
| 230 while (total_length < extra_data_size) { |
| 231 size_t size = *(++current_pos); |
| 232 total_length += 1 + size; |
| 233 if (total_length > 0x80000000) { |
| 234 LOG(ERROR) << "Vorbis header size too large"; |
| 235 return false; |
| 236 } |
| 237 header_length[i] += size; |
| 238 if (size < 0xFF) |
| 239 break; |
| 240 } |
| 241 if (total_length >= extra_data_size) { |
| 242 LOG(ERROR) << "Invalid vorbis header size in the extra data"; |
| 243 return false; |
| 244 } |
| 245 } |
| 246 current_pos++; |
| 247 // The first header is identification header. |
| 248 jobject identification_header = env->NewDirectByteBuffer( |
| 249 const_cast<uint8*>(current_pos), header_length[0]); |
| 250 Java_MediaCodecBridge_setCodecSpecificData( |
| 251 env, j_format, 0, identification_header); |
| 252 // The last header is codec header. |
| 253 jobject codec_header = env->NewDirectByteBuffer( |
| 254 const_cast<uint8*>(extra_data + total_length), |
| 255 extra_data_size - total_length); |
| 256 Java_MediaCodecBridge_setCodecSpecificData( |
| 257 env, j_format, 1, codec_header); |
| 258 env->DeleteLocalRef(codec_header); |
| 259 env->DeleteLocalRef(identification_header); |
| 260 break; |
| 261 } |
| 262 case kCodecAAC: |
| 263 { |
| 264 media::BitReader reader(extra_data, extra_data_size); |
| 265 |
| 266 // The following code is copied from aac.cc |
| 267 // TODO(qinmin): refactor the code in aac.cc to make it more reusable. |
| 268 uint8 profile = 0; |
| 269 uint8 frequency_index = 0; |
| 270 uint8 channel_config = 0; |
| 271 if (!reader.ReadBits(5, &profile) || |
| 272 !reader.ReadBits(4, &frequency_index)) { |
| 273 LOG(ERROR) << "Unable to parse AAC header"; |
| 274 return false; |
| 275 } |
| 276 if (0xf == frequency_index && !reader.SkipBits(24)) { |
| 277 LOG(ERROR) << "Unable to parse AAC header"; |
| 278 return false; |
| 279 } |
| 280 if (!reader.ReadBits(4, &channel_config)) { |
| 281 LOG(ERROR) << "Unable to parse AAC header"; |
| 282 return false; |
| 283 } |
| 284 |
| 285 if (profile < 1 || profile > 4 || frequency_index == 0xf || |
| 286 channel_config > 7) { |
| 287 LOG(ERROR) << "Invalid AAC header"; |
| 288 return false; |
| 289 } |
| 290 uint8 csd[2]; |
| 291 csd[0] = profile << 3 | frequency_index >> 1; |
| 292 csd[1] = (frequency_index & 0x01) << 7 | channel_config << 3; |
| 293 jobject header = env->NewDirectByteBuffer(csd, 2); |
| 294 Java_MediaCodecBridge_setCodecSpecificData( |
| 295 env, j_format, 0, header); |
| 296 // TODO(qinmin): pass an extra variable to this function to determine |
| 297 // whether we need to call this. |
| 298 Java_MediaCodecBridge_setFrameHasADTSHeader(env, j_format); |
| 299 env->DeleteLocalRef(header); |
| 300 break; |
| 301 } |
| 302 default: |
| 303 LOG(ERROR) << "Invalid header encountered for codec: " |
| 304 << AudioCodecToMimeType(codec); |
| 305 return false; |
| 306 } |
| 307 return true; |
| 308 } |
| 309 |
| 250 void AudioCodecBridge::PlayOutputBuffer(int index, size_t size) { | 310 void AudioCodecBridge::PlayOutputBuffer(int index, size_t size) { |
| 251 DCHECK_LE(0, index); | 311 DCHECK_LE(0, index); |
| 252 int numBytes = base::checked_numeric_cast<int>(size); | 312 int numBytes = base::checked_numeric_cast<int>(size); |
| 253 JNIEnv* env = AttachCurrentThread(); | 313 JNIEnv* env = AttachCurrentThread(); |
| 254 ScopedJavaLocalRef<jobject> buf = | 314 ScopedJavaLocalRef<jobject> buf = |
| 255 Java_MediaCodecBridge_getOutputBuffer(env, media_codec(), index); | 315 Java_MediaCodecBridge_getOutputBuffer(env, media_codec(), index); |
| 256 uint8* buffer = static_cast<uint8*>(env->GetDirectBufferAddress(buf.obj())); | 316 uint8* buffer = static_cast<uint8*>(env->GetDirectBufferAddress(buf.obj())); |
| 257 | 317 |
| 258 ScopedJavaLocalRef<jbyteArray> byte_array = | 318 ScopedJavaLocalRef<jbyteArray> byte_array = |
| 259 base::android::ToJavaByteArray(env, buffer, numBytes); | 319 base::android::ToJavaByteArray(env, buffer, numBytes); |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 291 const char* mime = VideoCodecToMimeType(codec); | 351 const char* mime = VideoCodecToMimeType(codec); |
| 292 return mime ? new VideoCodecBridge(mime) : NULL; | 352 return mime ? new VideoCodecBridge(mime) : NULL; |
| 293 } | 353 } |
| 294 | 354 |
| 295 bool MediaCodecBridge::RegisterMediaCodecBridge(JNIEnv* env) { | 355 bool MediaCodecBridge::RegisterMediaCodecBridge(JNIEnv* env) { |
| 296 return RegisterNativesImpl(env); | 356 return RegisterNativesImpl(env); |
| 297 } | 357 } |
| 298 | 358 |
| 299 } // namespace media | 359 } // namespace media |
| 300 | 360 |
| OLD | NEW |