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     jmethodID arrayID;
     35 };
     36 
     37 static fields_t gFields;
     38 
     39 }
     40 
     41 using namespace android;
     42 
     43 static jint android_media_MediaMuxer_addTrack(
     44         JNIEnv *env, jclass clazz, jlong nativeObject, jobjectArray keys,
     45         jobjectArray values) {
     46     sp<MediaMuxer> muxer(reinterpret_cast<MediaMuxer *>(nativeObject));
     47     if (muxer == NULL) {
     48         jniThrowException(env, "java/lang/IllegalStateException",
     49                           "Muxer was not set up correctly");
     50         return -1;
     51     }
     52 
     53     sp<AMessage> trackformat;
     54     status_t err = ConvertKeyValueArraysToMessage(env, keys, values,
     55                                                   &trackformat);
     56     if (err != OK) {
     57         jniThrowException(env, "java/lang/IllegalArgumentException",
     58                           "ConvertKeyValueArraysToMessage got an error");
     59         return err;
     60     }
     61 
     62     // Return negative value when errors happen in addTrack.
     63     jint trackIndex = muxer->addTrack(trackformat);
     64 
     65     if (trackIndex < 0) {
     66         jniThrowException(env, "java/lang/IllegalStateException",
     67                           "Failed to add the track to the muxer");
     68         return -1;
     69     }
     70     return trackIndex;
     71 }
     72 
     73 static void android_media_MediaMuxer_writeSampleData(
     74         JNIEnv *env, jclass clazz, jlong nativeObject, jint trackIndex,
     75         jobject byteBuf, jint offset, jint size, jlong timeUs, jint flags) {
     76     sp<MediaMuxer> muxer(reinterpret_cast<MediaMuxer *>(nativeObject));
     77     if (muxer == NULL) {
     78         jniThrowException(env, "java/lang/IllegalStateException",
     79                           "Muxer was not set up correctly");
     80         return;
     81     }
     82 
     83     // Try to convert the incoming byteBuffer into ABuffer
     84     void *dst = env->GetDirectBufferAddress(byteBuf);
     85 
     86     jlong dstSize;
     87     jbyteArray byteArray = NULL;
     88 
     89     if (dst == NULL) {
     90 
     91         byteArray =
     92             (jbyteArray)env->CallObjectMethod(byteBuf, gFields.arrayID);
     93 
     94         if (byteArray == NULL) {
     95             jniThrowException(env, "java/lang/IllegalArgumentException",
     96                               "byteArray is null");
     97             return;
     98         }
     99 
    100         jboolean isCopy;
    101         dst = env->GetByteArrayElements(byteArray, &isCopy);
    102 
    103         dstSize = env->GetArrayLength(byteArray);
    104     } else {
    105         dstSize = env->GetDirectBufferCapacity(byteBuf);
    106     }
    107 
    108     if (dstSize < (offset + size)) {
    109         ALOGE("writeSampleData saw wrong dstSize %lld, size  %d, offset %d",
    110               dstSize, size, offset);
    111         if (byteArray != NULL) {
    112             env->ReleaseByteArrayElements(byteArray, (jbyte *)dst, 0);
    113         }
    114         jniThrowException(env, "java/lang/IllegalArgumentException",
    115                           "sample has a wrong size");
    116         return;
    117     }
    118 
    119     sp<ABuffer> buffer = new ABuffer((char *)dst + offset, size);
    120 
    121     status_t err = muxer->writeSampleData(buffer, trackIndex, timeUs, flags);
    122 
    123     if (byteArray != NULL) {
    124         env->ReleaseByteArrayElements(byteArray, (jbyte *)dst, 0);
    125     }
    126 
    127     if (err != OK) {
    128         jniThrowException(env, "java/lang/IllegalStateException",
    129                           "writeSampleData returned an error");
    130     }
    131     return;
    132 }
    133 
    134 // Constructor counterpart.
    135 static jlong android_media_MediaMuxer_native_setup(
    136         JNIEnv *env, jclass clazz, jobject fileDescriptor,
    137         jint format) {
    138     int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
    139     ALOGV("native_setup: fd %d", fd);
    140 
    141     MediaMuxer::OutputFormat fileFormat =
    142         static_cast<MediaMuxer::OutputFormat>(format);
    143     sp<MediaMuxer> muxer = new MediaMuxer(fd, fileFormat);
    144     muxer->incStrong(clazz);
    145     return reinterpret_cast<jlong>(muxer.get());
    146 }
    147 
    148 static void android_media_MediaMuxer_setOrientationHint(
    149         JNIEnv *env, jclass clazz, jlong nativeObject, jint degrees) {
    150     sp<MediaMuxer> muxer(reinterpret_cast<MediaMuxer *>(nativeObject));
    151     if (muxer == NULL) {
    152         jniThrowException(env, "java/lang/IllegalStateException",
    153                           "Muxer was not set up correctly");
    154         return;
    155     }
    156     status_t err = muxer->setOrientationHint(degrees);
    157 
    158     if (err != OK) {
    159         jniThrowException(env, "java/lang/IllegalStateException",
    160                           "Failed to set orientation hint");
    161         return;
    162     }
    163 
    164 }
    165 
    166 static void android_media_MediaMuxer_setLocation(
    167         JNIEnv *env, jclass clazz, jlong nativeObject, jint latitude, jint longitude) {
    168     MediaMuxer* muxer = reinterpret_cast<MediaMuxer *>(nativeObject);
    169 
    170     status_t res = muxer->setLocation(latitude, longitude);
    171     if (res != OK) {
    172         jniThrowException(env, "java/lang/IllegalStateException",
    173                           "Failed to set location");
    174         return;
    175     }
    176 }
    177 
    178 static void android_media_MediaMuxer_start(JNIEnv *env, jclass clazz,
    179                                            jlong nativeObject) {
    180     sp<MediaMuxer> muxer(reinterpret_cast<MediaMuxer *>(nativeObject));
    181     if (muxer == NULL) {
    182         jniThrowException(env, "java/lang/IllegalStateException",
    183                           "Muxer was not set up correctly");
    184         return;
    185     }
    186     status_t err = muxer->start();
    187 
    188     if (err != OK) {
    189         jniThrowException(env, "java/lang/IllegalStateException",
    190                           "Failed to start the muxer");
    191         return;
    192     }
    193 
    194 }
    195 
    196 static void android_media_MediaMuxer_stop(JNIEnv *env, jclass clazz,
    197                                           jlong nativeObject) {
    198     sp<MediaMuxer> muxer(reinterpret_cast<MediaMuxer *>(nativeObject));
    199     if (muxer == NULL) {
    200         jniThrowException(env, "java/lang/IllegalStateException",
    201                           "Muxer was not set up correctly");
    202         return;
    203     }
    204 
    205     status_t err = muxer->stop();
    206 
    207     if (err != OK) {
    208         jniThrowException(env, "java/lang/IllegalStateException",
    209                           "Failed to stop the muxer");
    210         return;
    211     }
    212 }
    213 
    214 static void android_media_MediaMuxer_native_release(
    215         JNIEnv *env, jclass clazz, jlong nativeObject) {
    216     sp<MediaMuxer> muxer(reinterpret_cast<MediaMuxer *>(nativeObject));
    217     if (muxer != NULL) {
    218         muxer->decStrong(clazz);
    219     }
    220 }
    221 
    222 static JNINativeMethod gMethods[] = {
    223 
    224     { "nativeAddTrack", "(J[Ljava/lang/String;[Ljava/lang/Object;)I",
    225         (void *)android_media_MediaMuxer_addTrack },
    226 
    227     { "nativeSetOrientationHint", "(JI)V",
    228         (void *)android_media_MediaMuxer_setOrientationHint},
    229 
    230     { "nativeSetLocation", "(JII)V",
    231         (void *)android_media_MediaMuxer_setLocation},
    232 
    233     { "nativeStart", "(J)V", (void *)android_media_MediaMuxer_start},
    234 
    235     { "nativeWriteSampleData", "(JILjava/nio/ByteBuffer;IIJI)V",
    236         (void *)android_media_MediaMuxer_writeSampleData },
    237 
    238     { "nativeStop", "(J)V", (void *)android_media_MediaMuxer_stop},
    239 
    240     { "nativeSetup", "(Ljava/io/FileDescriptor;I)J",
    241         (void *)android_media_MediaMuxer_native_setup },
    242 
    243     { "nativeRelease", "(J)V",
    244         (void *)android_media_MediaMuxer_native_release },
    245 
    246 };
    247 
    248 // This function only registers the native methods, and is called from
    249 // JNI_OnLoad in android_media_MediaPlayer.cpp
    250 int register_android_media_MediaMuxer(JNIEnv *env) {
    251     int err = AndroidRuntime::registerNativeMethods(env,
    252                 "android/media/MediaMuxer", gMethods, NELEM(gMethods));
    253 
    254     jclass byteBufClass = env->FindClass("java/nio/ByteBuffer");
    255     CHECK(byteBufClass != NULL);
    256 
    257     gFields.arrayID =
    258         env->GetMethodID(byteBufClass, "array", "()[B");
    259     CHECK(gFields.arrayID != NULL);
    260 
    261     return err;
    262 }
    263