android_media_MediaMuxer.cpp revision effc9b4839f3cc109fe3d8244022f3c898cd080b
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, 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 74static 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. 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, 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 167static void android_media_MediaMuxer_start(JNIEnv *env, jclass clazz, 168 jint nativeObject) { 169 sp<MediaMuxer> muxer(reinterpret_cast<MediaMuxer *>(nativeObject)); 170 if (muxer == NULL) { 171 jniThrowException(env, "java/lang/IllegalStateException", 172 "Muxer was not set up correctly"); 173 return; 174 } 175 status_t err = muxer->start(); 176 177 if (err != OK) { 178 jniThrowException(env, "java/lang/IllegalStateException", 179 "Failed to start the muxer"); 180 return; 181 } 182 183} 184 185static void android_media_MediaMuxer_stop(JNIEnv *env, jclass clazz, 186 jint nativeObject) { 187 sp<MediaMuxer> muxer(reinterpret_cast<MediaMuxer *>(nativeObject)); 188 if (muxer == NULL) { 189 jniThrowException(env, "java/lang/IllegalStateException", 190 "Muxer was not set up correctly"); 191 return; 192 } 193 194 status_t err = muxer->stop(); 195 196 if (err != OK) { 197 jniThrowException(env, "java/lang/IllegalStateException", 198 "Failed to stop the muxer"); 199 return; 200 } 201} 202 203static void android_media_MediaMuxer_native_release( 204 JNIEnv *env, jclass clazz, jint nativeObject) { 205 sp<MediaMuxer> muxer(reinterpret_cast<MediaMuxer *>(nativeObject)); 206 if (muxer != NULL) { 207 muxer->decStrong(clazz); 208 } 209} 210 211static JNINativeMethod gMethods[] = { 212 213 { "nativeAddTrack", "(I[Ljava/lang/String;[Ljava/lang/Object;)I", 214 (void *)android_media_MediaMuxer_addTrack }, 215 216 { "nativeSetOrientationHint", "(II)V", 217 (void *)android_media_MediaMuxer_setOrientationHint}, 218 219 { "nativeStart", "(I)V", (void *)android_media_MediaMuxer_start}, 220 221 { "nativeWriteSampleData", "(IILjava/nio/ByteBuffer;IIJI)V", 222 (void *)android_media_MediaMuxer_writeSampleData }, 223 224 { "nativeStop", "(I)V", (void *)android_media_MediaMuxer_stop}, 225 226 { "nativeSetup", "(Ljava/io/FileDescriptor;I)I", 227 (void *)android_media_MediaMuxer_native_setup }, 228 229 { "nativeRelease", "(I)V", 230 (void *)android_media_MediaMuxer_native_release }, 231 232}; 233 234// This function only registers the native methods, and is called from 235// JNI_OnLoad in android_media_MediaPlayer.cpp 236int register_android_media_MediaMuxer(JNIEnv *env) { 237 int err = AndroidRuntime::registerNativeMethods(env, 238 "android/media/MediaMuxer", gMethods, NELEM(gMethods)); 239 240 jclass clazz = env->FindClass("android/media/MediaMuxer"); 241 CHECK(clazz != NULL); 242 243 gFields.context = env->GetFieldID(clazz, "mNativeContext", "I"); 244 CHECK(gFields.context != NULL); 245 246 jclass byteBufClass = env->FindClass("java/nio/ByteBuffer"); 247 CHECK(byteBufClass != NULL); 248 249 gFields.arrayID = 250 env->GetMethodID(byteBufClass, "array", "()[B"); 251 CHECK(gFields.arrayID != NULL); 252 253 return err; 254} 255