Home | History | Annotate | Download | only in android
      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