Home | History | Annotate | Download | only in jni
      1 /*
      2  * Copyright 2013, The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *     http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 //#define LOG_NDEBUG 0
     18 #define LOG_TAG "MediaMuxer-JNI"
     19 #include <utils/Log.h>
     20 
     21 #include "android_media_Utils.h"
     22 #include "android_runtime/AndroidRuntime.h"
     23 #include "jni.h"
     24 #include "JNIHelp.h"
     25 
     26 #include <media/stagefright/foundation/ABuffer.h>
     27 #include <media/stagefright/foundation/ADebug.h>
     28 #include <media/stagefright/foundation/AMessage.h>
     29 #include <media/stagefright/MediaMuxer.h>
     30 
     31 namespace android {
     32 
     33 struct fields_t {
     34     jfieldID context;
     35     jmethodID arrayID;
     36 };
     37 
     38 static fields_t gFields;
     39 
     40 }
     41 
     42 using namespace android;
     43 
     44 static jint android_media_MediaMuxer_addTrack(
     45         JNIEnv *env, jclass clazz, jint nativeObject, jobjectArray keys,
     46         jobjectArray values) {
     47     sp<MediaMuxer> muxer(reinterpret_cast<MediaMuxer *>(nativeObject));
     48     if (muxer == NULL) {
     49         jniThrowException(env, "java/lang/IllegalStateException",
     50                           "Muxer was not set up correctly");
     51         return -1;
     52     }
     53 
     54     sp<AMessage> trackformat;
     55     status_t err = ConvertKeyValueArraysToMessage(env, keys, values,
     56                                                   &trackformat);
     57     if (err != OK) {
     58         jniThrowException(env, "java/lang/IllegalArgumentException",
     59                           "ConvertKeyValueArraysToMessage got an error");
     60         return err;
     61     }
     62 
     63     // Return negative value when errors happen in addTrack.
     64     jint trackIndex = muxer->addTrack(trackformat);
     65 
     66     if (trackIndex < 0) {
     67         jniThrowException(env, "java/lang/IllegalStateException",
     68                           "Failed to add the track to the muxer");
     69         return -1;
     70     }
     71     return trackIndex;
     72 }
     73 
     74 static void android_media_MediaMuxer_writeSampleData(
     75         JNIEnv *env, jclass clazz, jint nativeObject, jint trackIndex,
     76         jobject byteBuf, jint offset, jint size, jlong timeUs, jint flags) {
     77     sp<MediaMuxer> muxer(reinterpret_cast<MediaMuxer *>(nativeObject));
     78     if (muxer == NULL) {
     79         jniThrowException(env, "java/lang/IllegalStateException",
     80                           "Muxer was not set up correctly");
     81         return;
     82     }
     83 
     84     // Try to convert the incoming byteBuffer into ABuffer
     85     void *dst = env->GetDirectBufferAddress(byteBuf);
     86 
     87     jlong dstSize;
     88     jbyteArray byteArray = NULL;
     89 
     90     if (dst == NULL) {
     91 
     92         byteArray =
     93             (jbyteArray)env->CallObjectMethod(byteBuf, gFields.arrayID);
     94 
     95         if (byteArray == NULL) {
     96             jniThrowException(env, "java/lang/IllegalArgumentException",
     97                               "byteArray is null");
     98             return;
     99         }
    100 
    101         jboolean isCopy;
    102         dst = env->GetByteArrayElements(byteArray, &isCopy);
    103 
    104         dstSize = env->GetArrayLength(byteArray);
    105     } else {
    106         dstSize = env->GetDirectBufferCapacity(byteBuf);
    107     }
    108 
    109     if (dstSize < (offset + size)) {
    110         ALOGE("writeSampleData saw wrong dstSize %lld, size  %d, offset %d",
    111               dstSize, size, offset);
    112         if (byteArray != NULL) {
    113             env->ReleaseByteArrayElements(byteArray, (jbyte *)dst, 0);
    114         }
    115         jniThrowException(env, "java/lang/IllegalArgumentException",
    116                           "sample has a wrong size");
    117         return;
    118     }
    119 
    120     sp<ABuffer> buffer = new ABuffer((char *)dst + offset, size);
    121 
    122     status_t err = muxer->writeSampleData(buffer, trackIndex, timeUs, flags);
    123 
    124     if (byteArray != NULL) {
    125         env->ReleaseByteArrayElements(byteArray, (jbyte *)dst, 0);
    126     }
    127 
    128     if (err != OK) {
    129         jniThrowException(env, "java/lang/IllegalStateException",
    130                           "writeSampleData returned an error");
    131     }
    132     return;
    133 }
    134 
    135 // Constructor counterpart.
    136 static jint android_media_MediaMuxer_native_setup(
    137         JNIEnv *env, jclass clazz, jobject fileDescriptor,
    138         jint format) {
    139     int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
    140     ALOGV("native_setup: fd %d", fd);
    141 
    142     MediaMuxer::OutputFormat fileFormat =
    143         static_cast<MediaMuxer::OutputFormat>(format);
    144     sp<MediaMuxer> muxer = new MediaMuxer(fd, fileFormat);
    145     muxer->incStrong(clazz);
    146     return int(muxer.get());
    147 }
    148 
    149 static void android_media_MediaMuxer_setOrientationHint(
    150         JNIEnv *env, jclass clazz, jint nativeObject, jint degrees) {
    151     sp<MediaMuxer> muxer(reinterpret_cast<MediaMuxer *>(nativeObject));
    152     if (muxer == NULL) {
    153         jniThrowException(env, "java/lang/IllegalStateException",
    154                           "Muxer was not set up correctly");
    155         return;
    156     }
    157     status_t err = muxer->setOrientationHint(degrees);
    158 
    159     if (err != OK) {
    160         jniThrowException(env, "java/lang/IllegalStateException",
    161                           "Failed to set orientation hint");
    162         return;
    163     }
    164 
    165 }
    166 
    167 static void android_media_MediaMuxer_setLocation(
    168         JNIEnv *env, jclass clazz, jint nativeObject, jint latitude, jint longitude) {
    169     MediaMuxer* muxer = reinterpret_cast<MediaMuxer *>(nativeObject);
    170 
    171     status_t res = muxer->setLocation(latitude, longitude);
    172     if (res != OK) {
    173         jniThrowException(env, "java/lang/IllegalStateException",
    174                           "Failed to set location");
    175         return;
    176     }
    177 }
    178 
    179 static void android_media_MediaMuxer_start(JNIEnv *env, jclass clazz,
    180                                            jint nativeObject) {
    181     sp<MediaMuxer> muxer(reinterpret_cast<MediaMuxer *>(nativeObject));
    182     if (muxer == NULL) {
    183         jniThrowException(env, "java/lang/IllegalStateException",
    184                           "Muxer was not set up correctly");
    185         return;
    186     }
    187     status_t err = muxer->start();
    188 
    189     if (err != OK) {
    190         jniThrowException(env, "java/lang/IllegalStateException",
    191                           "Failed to start the muxer");
    192         return;
    193     }
    194 
    195 }
    196 
    197 static void android_media_MediaMuxer_stop(JNIEnv *env, jclass clazz,
    198                                           jint nativeObject) {
    199     sp<MediaMuxer> muxer(reinterpret_cast<MediaMuxer *>(nativeObject));
    200     if (muxer == NULL) {
    201         jniThrowException(env, "java/lang/IllegalStateException",
    202                           "Muxer was not set up correctly");
    203         return;
    204     }
    205 
    206     status_t err = muxer->stop();
    207 
    208     if (err != OK) {
    209         jniThrowException(env, "java/lang/IllegalStateException",
    210                           "Failed to stop the muxer");
    211         return;
    212     }
    213 }
    214 
    215 static void android_media_MediaMuxer_native_release(
    216         JNIEnv *env, jclass clazz, jint nativeObject) {
    217     sp<MediaMuxer> muxer(reinterpret_cast<MediaMuxer *>(nativeObject));
    218     if (muxer != NULL) {
    219         muxer->decStrong(clazz);
    220     }
    221 }
    222 
    223 static JNINativeMethod gMethods[] = {
    224 
    225     { "nativeAddTrack", "(I[Ljava/lang/String;[Ljava/lang/Object;)I",
    226         (void *)android_media_MediaMuxer_addTrack },
    227 
    228     { "nativeSetOrientationHint", "(II)V",
    229         (void *)android_media_MediaMuxer_setOrientationHint},
    230 
    231     { "nativeSetLocation", "(III)V",
    232         (void *)android_media_MediaMuxer_setLocation},
    233 
    234     { "nativeStart", "(I)V", (void *)android_media_MediaMuxer_start},
    235 
    236     { "nativeWriteSampleData", "(IILjava/nio/ByteBuffer;IIJI)V",
    237         (void *)android_media_MediaMuxer_writeSampleData },
    238 
    239     { "nativeStop", "(I)V", (void *)android_media_MediaMuxer_stop},
    240 
    241     { "nativeSetup", "(Ljava/io/FileDescriptor;I)I",
    242         (void *)android_media_MediaMuxer_native_setup },
    243 
    244     { "nativeRelease", "(I)V",
    245         (void *)android_media_MediaMuxer_native_release },
    246 
    247 };
    248 
    249 // This function only registers the native methods, and is called from
    250 // JNI_OnLoad in android_media_MediaPlayer.cpp
    251 int register_android_media_MediaMuxer(JNIEnv *env) {
    252     int err = AndroidRuntime::registerNativeMethods(env,
    253                 "android/media/MediaMuxer", gMethods, NELEM(gMethods));
    254 
    255     jclass clazz = env->FindClass("android/media/MediaMuxer");
    256     CHECK(clazz != NULL);
    257 
    258     gFields.context = env->GetFieldID(clazz, "mNativeContext", "I");
    259     CHECK(gFields.context != NULL);
    260 
    261     jclass byteBufClass = env->FindClass("java/nio/ByteBuffer");
    262     CHECK(byteBufClass != NULL);
    263 
    264     gFields.arrayID =
    265         env->GetMethodID(byteBufClass, "array", "()[B");
    266     CHECK(gFields.arrayID != NULL);
    267 
    268     return err;
    269 }
    270