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