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