android_media_MediaMuxer.cpp revision 656fd0402613cec0196d5e2ae0a460d044d2805b
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 31namespace android { 32 33struct fields_t { 34 jfieldID context; 35 jmethodID arrayID; 36}; 37 38static fields_t gFields; 39 40} 41 42using namespace android; 43 44static jint android_media_MediaMuxer_addTrack( 45 JNIEnv *env, jclass clazz, jlong 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 74static void android_media_MediaMuxer_writeSampleData( 75 JNIEnv *env, jclass clazz, jlong 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. 136static 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 149static void android_media_MediaMuxer_setOrientationHint( 150 JNIEnv *env, jclass clazz, jlong 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 167static 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 179static void android_media_MediaMuxer_start(JNIEnv *env, jclass clazz, 180 jlong 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 197static void android_media_MediaMuxer_stop(JNIEnv *env, jclass clazz, 198 jlong 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 215static void android_media_MediaMuxer_native_release( 216 JNIEnv *env, jclass clazz, jlong nativeObject) { 217 sp<MediaMuxer> muxer(reinterpret_cast<MediaMuxer *>(nativeObject)); 218 if (muxer != NULL) { 219 muxer->decStrong(clazz); 220 } 221} 222 223static JNINativeMethod gMethods[] = { 224 225 { "nativeAddTrack", "(J[Ljava/lang/String;[Ljava/lang/Object;)I", 226 (void *)android_media_MediaMuxer_addTrack }, 227 228 { "nativeSetOrientationHint", "(JI)V", 229 (void *)android_media_MediaMuxer_setOrientationHint}, 230 231 { "nativeSetLocation", "(JII)V", 232 (void *)android_media_MediaMuxer_setLocation}, 233 234 { "nativeStart", "(J)V", (void *)android_media_MediaMuxer_start}, 235 236 { "nativeWriteSampleData", "(JILjava/nio/ByteBuffer;IIJI)V", 237 (void *)android_media_MediaMuxer_writeSampleData }, 238 239 { "nativeStop", "(J)V", (void *)android_media_MediaMuxer_stop}, 240 241 { "nativeSetup", "(Ljava/io/FileDescriptor;I)J", 242 (void *)android_media_MediaMuxer_native_setup }, 243 244 { "nativeRelease", "(J)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 251int 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", "J"); 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