blob: e167f836e8441b57f1d32d6bfa191dface764ec2 [file] [log] [blame]
Wei Jia071a8b72015-03-09 16:38:25 -07001/*
2 * Copyright 2015, 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 "MediaSync-JNI"
19#include <utils/Log.h>
20
21#include "android_media_MediaSync.h"
22
23#include "android_media_AudioTrack.h"
24#include "android_runtime/AndroidRuntime.h"
25#include "android_runtime/android_view_Surface.h"
26#include "jni.h"
27#include "JNIHelp.h"
28
29#include <gui/Surface.h>
30
31#include <media/AudioTrack.h>
Wei Jia217ec0a2015-04-09 18:48:18 -070032#include <media/stagefright/MediaClock.h>
Wei Jia071a8b72015-03-09 16:38:25 -070033#include <media/stagefright/MediaSync.h>
34#include <media/stagefright/foundation/ADebug.h>
35#include <media/stagefright/foundation/AString.h>
36
37#include <nativehelper/ScopedLocalRef.h>
38
39namespace android {
40
41struct fields_t {
42 jfieldID context;
Wei Jia217ec0a2015-04-09 18:48:18 -070043 jfieldID mediaTimestampMediaTimeUsID;
44 jfieldID mediaTimestampNanoTimeID;
45 jfieldID mediaTimestampClockRateID;
Wei Jia071a8b72015-03-09 16:38:25 -070046};
47
48static fields_t gFields;
49
50////////////////////////////////////////////////////////////////////////////////
51
52JMediaSync::JMediaSync() {
53 mSync = MediaSync::create();
54}
55
56JMediaSync::~JMediaSync() {
57}
58
59status_t JMediaSync::configureSurface(const sp<IGraphicBufferProducer> &bufferProducer) {
60 return mSync->configureSurface(bufferProducer);
61}
62
63status_t JMediaSync::configureAudioTrack(
64 const sp<AudioTrack> &audioTrack,
65 int32_t nativeSampleRateInHz) {
66 return mSync->configureAudioTrack(audioTrack, nativeSampleRateInHz);
67}
68
69status_t JMediaSync::createInputSurface(
70 sp<IGraphicBufferProducer>* bufferProducer) {
71 return mSync->createInputSurface(bufferProducer);
72}
73
Wei Jia0feab712015-04-15 13:24:35 -070074status_t JMediaSync::setPlaybackRate(float rate) {
75 return mSync->setPlaybackRate(rate);
Wei Jia071a8b72015-03-09 16:38:25 -070076}
77
Wei Jia217ec0a2015-04-09 18:48:18 -070078sp<const MediaClock> JMediaSync::getMediaClock() {
79 return mSync->getMediaClock();
80}
81
Wei Jia071a8b72015-03-09 16:38:25 -070082status_t JMediaSync::updateQueuedAudioData(
83 int sizeInBytes, int64_t presentationTimeUs) {
84 return mSync->updateQueuedAudioData(sizeInBytes, presentationTimeUs);
85}
86
87} // namespace android
88
89////////////////////////////////////////////////////////////////////////////////
90
91using namespace android;
92
93static sp<JMediaSync> setMediaSync(JNIEnv *env, jobject thiz, const sp<JMediaSync> &sync) {
94 sp<JMediaSync> old = (JMediaSync *)env->GetLongField(thiz, gFields.context);
95 if (sync != NULL) {
96 sync->incStrong(thiz);
97 }
98 if (old != NULL) {
99 old->decStrong(thiz);
100 }
101
102 env->SetLongField(thiz, gFields.context, (jlong)sync.get());
103
104 return old;
105}
106
107static sp<JMediaSync> getMediaSync(JNIEnv *env, jobject thiz) {
108 return (JMediaSync *)env->GetLongField(thiz, gFields.context);
109}
110
111static void android_media_MediaSync_release(JNIEnv *env, jobject thiz) {
112 setMediaSync(env, thiz, NULL);
113}
114
115static void throwExceptionAsNecessary(
116 JNIEnv *env, status_t err, const char *msg = NULL) {
117 switch (err) {
Wei Jia0feab712015-04-15 13:24:35 -0700118 case NO_ERROR:
Wei Jia071a8b72015-03-09 16:38:25 -0700119 break;
120
121 case BAD_VALUE:
122 jniThrowException(env, "java/lang/IllegalArgumentException", msg);
123 break;
124
Wei Jia0feab712015-04-15 13:24:35 -0700125 case NO_INIT:
126 case INVALID_OPERATION:
Wei Jia071a8b72015-03-09 16:38:25 -0700127 default:
Wei Jia0feab712015-04-15 13:24:35 -0700128 if (err > 0) {
129 break;
130 }
131 AString msgWithErrorCode(msg);
132 msgWithErrorCode.append(" error:");
133 msgWithErrorCode.append(err);
134 jniThrowException(env, "java/lang/IllegalStateException", msgWithErrorCode.c_str());
Wei Jia071a8b72015-03-09 16:38:25 -0700135 break;
136 }
137}
138
139static void android_media_MediaSync_native_configureSurface(
140 JNIEnv *env, jobject thiz, jobject jsurface) {
141 ALOGV("android_media_MediaSync_configureSurface");
142
143 sp<JMediaSync> sync = getMediaSync(env, thiz);
144 if (sync == NULL) {
145 throwExceptionAsNecessary(env, INVALID_OPERATION);
146 return;
147 }
148
149 sp<IGraphicBufferProducer> bufferProducer;
150 if (jsurface != NULL) {
151 sp<Surface> surface(android_view_Surface_getSurface(env, jsurface));
152 if (surface != NULL) {
153 bufferProducer = surface->getIGraphicBufferProducer();
154 } else {
155 throwExceptionAsNecessary(env, BAD_VALUE, "The surface has been released");
156 return;
157 }
158 }
159
160 status_t err = sync->configureSurface(bufferProducer);
161
162 if (err == INVALID_OPERATION) {
163 throwExceptionAsNecessary(
164 env, INVALID_OPERATION, "Surface has already been configured");
165 } if (err != NO_ERROR) {
166 AString msg("Failed to connect to surface with error ");
167 msg.append(err);
168 throwExceptionAsNecessary(env, BAD_VALUE, msg.c_str());
169 }
170}
171
172static void android_media_MediaSync_native_configureAudioTrack(
173 JNIEnv *env, jobject thiz, jobject jaudioTrack, jint nativeSampleRateInHz) {
174 ALOGV("android_media_MediaSync_configureAudioTrack");
175
176 sp<JMediaSync> sync = getMediaSync(env, thiz);
177 if (sync == NULL) {
178 throwExceptionAsNecessary(env, INVALID_OPERATION);
179 return;
180 }
181
182 sp<AudioTrack> audioTrack;
183 if (jaudioTrack != NULL) {
184 audioTrack = android_media_AudioTrack_getAudioTrack(env, jaudioTrack);
185 if (audioTrack == NULL) {
186 throwExceptionAsNecessary(env, BAD_VALUE, "The audio track has been released");
187 return;
188 }
189 }
190
191 status_t err = sync->configureAudioTrack(audioTrack, nativeSampleRateInHz);
192
193 if (err == INVALID_OPERATION) {
194 throwExceptionAsNecessary(
195 env, INVALID_OPERATION, "Audio track has already been configured");
196 } if (err != NO_ERROR) {
197 AString msg("Failed to configure audio track with error ");
198 msg.append(err);
199 throwExceptionAsNecessary(env, BAD_VALUE, msg.c_str());
200 }
201}
202
203static jobject android_media_MediaSync_createInputSurface(
204 JNIEnv* env, jobject thiz) {
205 ALOGV("android_media_MediaSync_createInputSurface");
206
207 sp<JMediaSync> sync = getMediaSync(env, thiz);
208 if (sync == NULL) {
209 throwExceptionAsNecessary(env, INVALID_OPERATION);
210 return NULL;
211 }
212
213 // Tell the MediaSync that we want to use a Surface as input.
214 sp<IGraphicBufferProducer> bufferProducer;
215 status_t err = sync->createInputSurface(&bufferProducer);
216 if (err != NO_ERROR) {
217 throwExceptionAsNecessary(env, INVALID_OPERATION);
218 return NULL;
219 }
220
221 // Wrap the IGBP in a Java-language Surface.
222 return android_view_Surface_createFromIGraphicBufferProducer(env,
223 bufferProducer);
224}
225
226static void android_media_MediaSync_native_updateQueuedAudioData(
227 JNIEnv *env, jobject thiz, jint sizeInBytes, jlong presentationTimeUs) {
228 sp<JMediaSync> sync = getMediaSync(env, thiz);
229 if (sync == NULL) {
230 throwExceptionAsNecessary(env, INVALID_OPERATION);
231 return;
232 }
233
234 status_t err = sync->updateQueuedAudioData(sizeInBytes, presentationTimeUs);
235 if (err != NO_ERROR) {
236 throwExceptionAsNecessary(env, err);
237 return;
238 }
239}
240
Wei Jia217ec0a2015-04-09 18:48:18 -0700241static jboolean android_media_MediaSync_native_getTimestamp(
242 JNIEnv *env, jobject thiz, jobject timestamp) {
243 sp<JMediaSync> sync = getMediaSync(env, thiz);
244 if (sync == NULL) {
245 throwExceptionAsNecessary(env, INVALID_OPERATION);
246 return JNI_FALSE;
247 }
248
249 sp<const MediaClock> mediaClock = sync->getMediaClock();
250 if (mediaClock == NULL) {
251 return JNI_FALSE;
252 }
253
254 int64_t nowUs = ALooper::GetNowUs();
255 int64_t mediaUs = 0;
256 if (mediaClock->getMediaTime(nowUs, &mediaUs) != OK) {
257 return JNI_FALSE;
258 }
259
260 env->SetLongField(timestamp, gFields.mediaTimestampMediaTimeUsID,
261 (jlong)mediaUs);
262 env->SetLongField(timestamp, gFields.mediaTimestampNanoTimeID,
263 (jlong)(nowUs * 1000));
264 env->SetFloatField(timestamp, gFields.mediaTimestampClockRateID,
265 (jfloat)mediaClock->getPlaybackRate());
266 return JNI_TRUE;
267}
268
Wei Jia071a8b72015-03-09 16:38:25 -0700269static void android_media_MediaSync_native_init(JNIEnv *env) {
270 ScopedLocalRef<jclass> clazz(env, env->FindClass("android/media/MediaSync"));
271 CHECK(clazz.get() != NULL);
272
273 gFields.context = env->GetFieldID(clazz.get(), "mNativeContext", "J");
274 CHECK(gFields.context != NULL);
Wei Jia217ec0a2015-04-09 18:48:18 -0700275
276 clazz.reset(env->FindClass("android/media/MediaTimestamp"));
277 CHECK(clazz.get() != NULL);
278
279 gFields.mediaTimestampMediaTimeUsID =
280 env->GetFieldID(clazz.get(), "mediaTimeUs", "J");
281 CHECK(gFields.mediaTimestampMediaTimeUsID != NULL);
282
283 gFields.mediaTimestampNanoTimeID =
284 env->GetFieldID(clazz.get(), "nanoTime", "J");
285 CHECK(gFields.mediaTimestampNanoTimeID != NULL);
286
287 gFields.mediaTimestampClockRateID =
Wei Jiadfb0e622015-04-10 16:47:12 -0700288 env->GetFieldID(clazz.get(), "clockRate", "F");
Wei Jia217ec0a2015-04-09 18:48:18 -0700289 CHECK(gFields.mediaTimestampClockRateID != NULL);
Wei Jia071a8b72015-03-09 16:38:25 -0700290}
291
292static void android_media_MediaSync_native_setup(JNIEnv *env, jobject thiz) {
293 sp<JMediaSync> sync = new JMediaSync();
294
295 setMediaSync(env, thiz, sync);
296}
297
298static void android_media_MediaSync_native_setPlaybackRate(
299 JNIEnv *env, jobject thiz, jfloat rate) {
300 sp<JMediaSync> sync = getMediaSync(env, thiz);
301 if (sync == NULL) {
302 throwExceptionAsNecessary(env, INVALID_OPERATION);
303 return;
304 }
305
Wei Jia0feab712015-04-15 13:24:35 -0700306 status_t err = sync->setPlaybackRate(rate);
307 if (err != NO_ERROR) {
308 throwExceptionAsNecessary(env, err);
309 return;
310 }
Wei Jia071a8b72015-03-09 16:38:25 -0700311}
312
313static void android_media_MediaSync_native_finalize(JNIEnv *env, jobject thiz) {
314 android_media_MediaSync_release(env, thiz);
315}
316
317static JNINativeMethod gMethods[] = {
318 { "native_configureSurface",
319 "(Landroid/view/Surface;)V",
320 (void *)android_media_MediaSync_native_configureSurface },
321
322 { "native_configureAudioTrack",
323 "(Landroid/media/AudioTrack;I)V",
324 (void *)android_media_MediaSync_native_configureAudioTrack },
325
326 { "createInputSurface", "()Landroid/view/Surface;",
327 (void *)android_media_MediaSync_createInputSurface },
328
329 { "native_updateQueuedAudioData",
330 "(IJ)V",
331 (void *)android_media_MediaSync_native_updateQueuedAudioData },
332
Wei Jia217ec0a2015-04-09 18:48:18 -0700333 { "native_getTimestamp",
334 "(Landroid/media/MediaTimestamp;)Z",
335 (void *)android_media_MediaSync_native_getTimestamp },
336
Wei Jia071a8b72015-03-09 16:38:25 -0700337 { "native_init", "()V", (void *)android_media_MediaSync_native_init },
338
339 { "native_setup", "()V", (void *)android_media_MediaSync_native_setup },
340
341 { "native_release", "()V", (void *)android_media_MediaSync_release },
342
343 { "native_setPlaybackRate", "(F)V", (void *)android_media_MediaSync_native_setPlaybackRate },
344
345 { "native_finalize", "()V", (void *)android_media_MediaSync_native_finalize },
346};
347
348int register_android_media_MediaSync(JNIEnv *env) {
349 return AndroidRuntime::registerNativeMethods(
350 env, "android/media/MediaSync", gMethods, NELEM(gMethods));
351}