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 3 // found in the LICENSE file. 4 5 #include "media/base/android/media_codec_bridge.h" 6 7 #include <jni.h> 8 9 #include "base/android/build_info.h" 10 #include "base/android/jni_android.h" 11 #include "base/android/jni_array.h" 12 #include "base/android/jni_string.h" 13 #include "base/basictypes.h" 14 #include "base/lazy_instance.h" 15 #include "base/logging.h" 16 #include "base/safe_numerics.h" 17 #include "base/strings/stringprintf.h" 18 #include "jni/MediaCodecBridge_jni.h" 19 #include "media/base/bit_reader.h" 20 #include "media/base/decrypt_config.h" 21 22 using base::android::AttachCurrentThread; 23 using base::android::ConvertUTF8ToJavaString; 24 using base::android::ScopedJavaLocalRef; 25 26 namespace media { 27 28 enum { kBufferFlagEndOfStream = 4 }; 29 30 static const char* AudioCodecToMimeType(const AudioCodec codec) { 31 switch (codec) { 32 case kCodecMP3: 33 return "audio/mpeg"; 34 case kCodecVorbis: 35 return "audio/vorbis"; 36 case kCodecAAC: 37 return "audio/mp4a-latm"; 38 default: 39 return NULL; 40 } 41 } 42 43 static const char* VideoCodecToMimeType(const VideoCodec codec) { 44 switch (codec) { 45 case kCodecH264: 46 return "video/avc"; 47 case kCodecVP8: 48 return "video/x-vnd.on2.vp8"; 49 default: 50 return NULL; 51 } 52 } 53 54 static ScopedJavaLocalRef<jintArray> ToJavaIntArray( 55 JNIEnv* env, scoped_ptr<jint[]> native_array, int size) { 56 ScopedJavaLocalRef<jintArray> j_array(env, env->NewIntArray(size)); 57 env->SetIntArrayRegion(j_array.obj(), 0, size, native_array.get()); 58 return j_array; 59 } 60 61 // static 62 const base::TimeDelta MediaCodecBridge::kTimeOutInfinity = 63 base::TimeDelta::FromMicroseconds(-1); 64 65 // static 66 const base::TimeDelta MediaCodecBridge::kTimeOutNoWait = 67 base::TimeDelta::FromMicroseconds(0); 68 69 // static 70 bool MediaCodecBridge::IsAvailable() { 71 // MediaCodec is only available on JB and greater. 72 return base::android::BuildInfo::GetInstance()->sdk_int() >= 16; 73 } 74 75 MediaCodecBridge::MediaCodecBridge(const char* mime) { 76 JNIEnv* env = AttachCurrentThread(); 77 CHECK(env); 78 DCHECK(mime); 79 80 ScopedJavaLocalRef<jstring> j_type = ConvertUTF8ToJavaString(env, mime); 81 j_media_codec_.Reset(Java_MediaCodecBridge_create( 82 env, j_type.obj())); 83 } 84 85 MediaCodecBridge::~MediaCodecBridge() { 86 JNIEnv* env = AttachCurrentThread(); 87 CHECK(env); 88 Java_MediaCodecBridge_release(env, j_media_codec_.obj()); 89 } 90 91 void MediaCodecBridge::StartInternal() { 92 JNIEnv* env = AttachCurrentThread(); 93 Java_MediaCodecBridge_start(env, j_media_codec_.obj()); 94 GetOutputBuffers(); 95 } 96 97 int MediaCodecBridge::Reset() { 98 JNIEnv* env = AttachCurrentThread(); 99 return Java_MediaCodecBridge_flush(env, j_media_codec_.obj()); 100 } 101 102 void MediaCodecBridge::Stop() { 103 JNIEnv* env = AttachCurrentThread(); 104 Java_MediaCodecBridge_stop(env, j_media_codec_.obj()); 105 } 106 107 void MediaCodecBridge::GetOutputFormat(int* width, int* height) { 108 JNIEnv* env = AttachCurrentThread(); 109 110 *width = Java_MediaCodecBridge_getOutputWidth(env, j_media_codec_.obj()); 111 *height = Java_MediaCodecBridge_getOutputHeight(env, j_media_codec_.obj()); 112 } 113 114 size_t MediaCodecBridge::QueueInputBuffer( 115 int index, const uint8* data, int size, 116 const base::TimeDelta& presentation_time) { 117 size_t size_to_copy = FillInputBuffer(index, data, size); 118 JNIEnv* env = AttachCurrentThread(); 119 Java_MediaCodecBridge_queueInputBuffer( 120 env, j_media_codec_.obj(), 121 index, 0, size_to_copy, presentation_time.InMicroseconds(), 0); 122 return size_to_copy; 123 } 124 125 size_t MediaCodecBridge::QueueSecureInputBuffer( 126 int index, const uint8* data, int data_size, const uint8* key_id, 127 int key_id_size, const uint8* iv, int iv_size, 128 const SubsampleEntry* subsamples, int subsamples_size, 129 const base::TimeDelta& presentation_time) { 130 size_t size_to_copy = FillInputBuffer(index, data, data_size); 131 132 JNIEnv* env = AttachCurrentThread(); 133 ScopedJavaLocalRef<jbyteArray> j_key_id = 134 base::android::ToJavaByteArray(env, key_id, key_id_size); 135 ScopedJavaLocalRef<jbyteArray> j_iv = 136 base::android::ToJavaByteArray(env, iv, iv_size); 137 scoped_ptr<jint[]> native_clear_array(new jint[subsamples_size]); 138 scoped_ptr<jint[]> native_cypher_array(new jint[subsamples_size]); 139 for (int i = 0; i < subsamples_size; ++i) { 140 native_clear_array[i] = subsamples[i].clear_bytes; 141 native_cypher_array[i] = subsamples[i].cypher_bytes; 142 } 143 ScopedJavaLocalRef<jintArray> clear_array = ToJavaIntArray( 144 env, native_clear_array.Pass(), subsamples_size); 145 ScopedJavaLocalRef<jintArray> cypher_array = ToJavaIntArray( 146 env, native_cypher_array.Pass(), subsamples_size); 147 148 Java_MediaCodecBridge_queueSecureInputBuffer( 149 env, j_media_codec_.obj(), index, 0, j_iv.obj(), j_key_id.obj(), 150 clear_array.obj(), cypher_array.obj(), subsamples_size, 151 presentation_time.InMicroseconds()); 152 153 return size_to_copy; 154 } 155 156 void MediaCodecBridge::QueueEOS(int input_buffer_index) { 157 JNIEnv* env = AttachCurrentThread(); 158 Java_MediaCodecBridge_queueInputBuffer( 159 env, j_media_codec_.obj(), 160 input_buffer_index, 0, 0, 0, kBufferFlagEndOfStream); 161 } 162 163 int MediaCodecBridge::DequeueInputBuffer(base::TimeDelta timeout) { 164 JNIEnv* env = AttachCurrentThread(); 165 return Java_MediaCodecBridge_dequeueInputBuffer( 166 env, j_media_codec_.obj(), timeout.InMicroseconds()); 167 } 168 169 int MediaCodecBridge::DequeueOutputBuffer( 170 base::TimeDelta timeout, size_t* offset, size_t* size, 171 base::TimeDelta* presentation_time, bool* end_of_stream) { 172 JNIEnv* env = AttachCurrentThread(); 173 174 ScopedJavaLocalRef<jobject> result = 175 Java_MediaCodecBridge_dequeueOutputBuffer(env, j_media_codec_.obj(), 176 timeout.InMicroseconds()); 177 178 int j_buffer = Java_DequeueOutputResult_index(env, result.obj()); 179 if (j_buffer >= 0) { 180 int64 presentation_time_us = 181 Java_DequeueOutputResult_presentationTimeMicroseconds( 182 env, result.obj()); 183 int flags = Java_DequeueOutputResult_flags(env, result.obj()); 184 *offset = base::checked_numeric_cast<size_t>( 185 Java_DequeueOutputResult_offset(env, result.obj())); 186 *size = base::checked_numeric_cast<size_t>( 187 Java_DequeueOutputResult_numBytes(env, result.obj())); 188 *presentation_time = 189 base::TimeDelta::FromMicroseconds(presentation_time_us); 190 *end_of_stream = flags & kBufferFlagEndOfStream; 191 } 192 return j_buffer; 193 } 194 195 void MediaCodecBridge::ReleaseOutputBuffer(int index, bool render) { 196 JNIEnv* env = AttachCurrentThread(); 197 CHECK(env); 198 199 Java_MediaCodecBridge_releaseOutputBuffer( 200 env, j_media_codec_.obj(), index, render); 201 } 202 203 void MediaCodecBridge::GetOutputBuffers() { 204 JNIEnv* env = AttachCurrentThread(); 205 Java_MediaCodecBridge_getOutputBuffers(env, j_media_codec_.obj()); 206 } 207 208 size_t MediaCodecBridge::FillInputBuffer( 209 int index, const uint8* data, int size) { 210 JNIEnv* env = AttachCurrentThread(); 211 212 ScopedJavaLocalRef<jobject> j_buffer( 213 Java_MediaCodecBridge_getInputBuffer(env, j_media_codec_.obj(), index)); 214 215 uint8* direct_buffer = 216 static_cast<uint8*>(env->GetDirectBufferAddress(j_buffer.obj())); 217 int64 buffer_capacity = env->GetDirectBufferCapacity(j_buffer.obj()); 218 219 int size_to_copy = (buffer_capacity < size) ? buffer_capacity : size; 220 // TODO(qinmin): Handling the case that not all the data can be copied. 221 DCHECK(size_to_copy == size) << 222 "Failed to fill all the data into the input buffer. Size to fill: " 223 << size << ". Size filled: " << size_to_copy; 224 if (size_to_copy > 0) 225 memcpy(direct_buffer, data, size_to_copy); 226 return size_to_copy; 227 } 228 229 AudioCodecBridge::AudioCodecBridge(const char* mime) 230 : MediaCodecBridge(mime) { 231 } 232 233 bool AudioCodecBridge::Start( 234 const AudioCodec codec, int sample_rate, int channel_count, 235 const uint8* extra_data, size_t extra_data_size, bool play_audio, 236 jobject media_crypto) { 237 JNIEnv* env = AttachCurrentThread(); 238 DCHECK(AudioCodecToMimeType(codec)); 239 240 ScopedJavaLocalRef<jstring> j_mime = 241 ConvertUTF8ToJavaString(env, AudioCodecToMimeType(codec)); 242 ScopedJavaLocalRef<jobject> j_format( 243 Java_MediaCodecBridge_createAudioFormat( 244 env, j_mime.obj(), sample_rate, channel_count)); 245 DCHECK(!j_format.is_null()); 246 247 if (!ConfigureMediaFormat(j_format.obj(), codec, extra_data, extra_data_size)) 248 return false; 249 250 if (!Java_MediaCodecBridge_configureAudio( 251 env, media_codec(), j_format.obj(), media_crypto, 0, play_audio)) { 252 return false; 253 } 254 StartInternal(); 255 return true; 256 } 257 258 bool AudioCodecBridge::ConfigureMediaFormat( 259 jobject j_format, const AudioCodec codec, const uint8* extra_data, 260 size_t extra_data_size) { 261 if (extra_data_size == 0) 262 return true; 263 264 JNIEnv* env = AttachCurrentThread(); 265 switch(codec) { 266 case kCodecVorbis: 267 { 268 if (extra_data[0] != 2) { 269 LOG(ERROR) << "Invalid number of vorbis headers before the codec " 270 << "header: " << extra_data[0]; 271 return false; 272 } 273 274 size_t header_length[2]; 275 // |total_length| keeps track of the total number of bytes before the last 276 // header. 277 size_t total_length = 1; 278 const uint8* current_pos = extra_data; 279 // Calculate the length of the first 2 headers. 280 for (int i = 0; i < 2; ++i) { 281 header_length[i] = 0; 282 while (total_length < extra_data_size) { 283 size_t size = *(++current_pos); 284 total_length += 1 + size; 285 if (total_length > 0x80000000) { 286 LOG(ERROR) << "Vorbis header size too large"; 287 return false; 288 } 289 header_length[i] += size; 290 if (size < 0xFF) 291 break; 292 } 293 if (total_length >= extra_data_size) { 294 LOG(ERROR) << "Invalid vorbis header size in the extra data"; 295 return false; 296 } 297 } 298 current_pos++; 299 // The first header is identification header. 300 ScopedJavaLocalRef<jbyteArray> first_header = 301 base::android::ToJavaByteArray(env, current_pos, header_length[0]); 302 Java_MediaCodecBridge_setCodecSpecificData( 303 env, j_format, 0, first_header.obj()); 304 // The last header is codec header. 305 ScopedJavaLocalRef<jbyteArray> last_header = 306 base::android::ToJavaByteArray( 307 env, extra_data + total_length, extra_data_size - total_length); 308 Java_MediaCodecBridge_setCodecSpecificData( 309 env, j_format, 1, last_header.obj()); 310 break; 311 } 312 case kCodecAAC: 313 { 314 media::BitReader reader(extra_data, extra_data_size); 315 316 // The following code is copied from aac.cc 317 // TODO(qinmin): refactor the code in aac.cc to make it more reusable. 318 uint8 profile = 0; 319 uint8 frequency_index = 0; 320 uint8 channel_config = 0; 321 if (!reader.ReadBits(5, &profile) || 322 !reader.ReadBits(4, &frequency_index)) { 323 LOG(ERROR) << "Unable to parse AAC header"; 324 return false; 325 } 326 if (0xf == frequency_index && !reader.SkipBits(24)) { 327 LOG(ERROR) << "Unable to parse AAC header"; 328 return false; 329 } 330 if (!reader.ReadBits(4, &channel_config)) { 331 LOG(ERROR) << "Unable to parse AAC header"; 332 return false; 333 } 334 335 if (profile < 1 || profile > 4 || frequency_index == 0xf || 336 channel_config > 7) { 337 LOG(ERROR) << "Invalid AAC header"; 338 return false; 339 } 340 const size_t kCsdLength = 2; 341 uint8 csd[kCsdLength]; 342 csd[0] = profile << 3 | frequency_index >> 1; 343 csd[1] = (frequency_index & 0x01) << 7 | channel_config << 3; 344 ScopedJavaLocalRef<jbyteArray> byte_array = 345 base::android::ToJavaByteArray(env, csd, kCsdLength); 346 Java_MediaCodecBridge_setCodecSpecificData( 347 env, j_format, 0, byte_array.obj()); 348 349 // TODO(qinmin): pass an extra variable to this function to determine 350 // whether we need to call this. 351 Java_MediaCodecBridge_setFrameHasADTSHeader(env, j_format); 352 break; 353 } 354 default: 355 LOG(ERROR) << "Invalid header encountered for codec: " 356 << AudioCodecToMimeType(codec); 357 return false; 358 } 359 return true; 360 } 361 362 void AudioCodecBridge::PlayOutputBuffer(int index, size_t size) { 363 DCHECK_LE(0, index); 364 int numBytes = base::checked_numeric_cast<int>(size); 365 JNIEnv* env = AttachCurrentThread(); 366 ScopedJavaLocalRef<jobject> buf = 367 Java_MediaCodecBridge_getOutputBuffer(env, media_codec(), index); 368 uint8* buffer = static_cast<uint8*>(env->GetDirectBufferAddress(buf.obj())); 369 370 ScopedJavaLocalRef<jbyteArray> byte_array = 371 base::android::ToJavaByteArray(env, buffer, numBytes); 372 Java_MediaCodecBridge_playOutputBuffer( 373 env, media_codec(), byte_array.obj()); 374 } 375 376 void AudioCodecBridge::SetVolume(double volume) { 377 JNIEnv* env = AttachCurrentThread(); 378 Java_MediaCodecBridge_setVolume(env, media_codec(), volume); 379 } 380 381 VideoCodecBridge::VideoCodecBridge(const char* mime) 382 : MediaCodecBridge(mime) { 383 } 384 385 bool VideoCodecBridge::Start( 386 const VideoCodec codec, const gfx::Size& size, jobject surface, 387 jobject media_crypto) { 388 JNIEnv* env = AttachCurrentThread(); 389 DCHECK(VideoCodecToMimeType(codec)); 390 391 ScopedJavaLocalRef<jstring> j_mime = 392 ConvertUTF8ToJavaString(env, VideoCodecToMimeType(codec)); 393 ScopedJavaLocalRef<jobject> j_format( 394 Java_MediaCodecBridge_createVideoFormat( 395 env, j_mime.obj(), size.width(), size.height())); 396 DCHECK(!j_format.is_null()); 397 if (!Java_MediaCodecBridge_configureVideo( 398 env, media_codec(), j_format.obj(), surface, media_crypto, 0)) { 399 return false; 400 } 401 StartInternal(); 402 return true; 403 } 404 405 AudioCodecBridge* AudioCodecBridge::Create(const AudioCodec codec) { 406 const char* mime = AudioCodecToMimeType(codec); 407 return mime ? new AudioCodecBridge(mime) : NULL; 408 } 409 410 VideoCodecBridge* VideoCodecBridge::Create(const VideoCodec codec) { 411 const char* mime = VideoCodecToMimeType(codec); 412 return mime ? new VideoCodecBridge(mime) : NULL; 413 } 414 415 bool MediaCodecBridge::RegisterMediaCodecBridge(JNIEnv* env) { 416 return RegisterNativesImpl(env); 417 } 418 419 } // namespace media 420 421