• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
android_media_MediaMuxer_addTrack(JNIEnv * env,jclass clazz,jint nativeObject,jobjectArray keys,jobjectArray values)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 
android_media_MediaMuxer_writeSampleData(JNIEnv * env,jclass clazz,jint nativeObject,jint trackIndex,jobject byteBuf,jint offset,jint size,jlong timeUs,jint flags)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.
android_media_MediaMuxer_native_setup(JNIEnv * env,jclass clazz,jobject fileDescriptor,jint format)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 
android_media_MediaMuxer_setOrientationHint(JNIEnv * env,jclass clazz,jint nativeObject,jint degrees)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 
android_media_MediaMuxer_start(JNIEnv * env,jclass clazz,jint nativeObject)167 static 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 
android_media_MediaMuxer_stop(JNIEnv * env,jclass clazz,jint nativeObject)185 static 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 
android_media_MediaMuxer_native_release(JNIEnv * env,jclass clazz,jint nativeObject)203 static 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 
211 static 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
register_android_media_MediaMuxer(JNIEnv * env)236 int 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