1 /*
2 * Copyright (C) 2013, Fluendo S.A.
3 * Author: Andoni Morales <amorales@fluendo.com>
4 *
5 * Copyright (C) 2014,2018 Collabora Ltd.
6 * Author: Matthieu Bouron <matthieu.bouron@collabora.com>
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation
11 * version 2.1 of the License.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 *
22 */
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #include "gstjniutils.h"
29 #include "gstamcsurfacetexture-jni.h"
30
31 struct _GstAmcSurfaceTextureJNI
32 {
33 GstAmcSurfaceTexture parent_instance;
34 jobject jobject;
35 gint texture_id;
36
37 jobject listener;
38 jmethodID set_context_id;
39 GstAmcSurfaceTextureOnFrameAvailableCallback callback;
40 gpointer user_data;
41 };
42
43 static struct
44 {
45 jclass jklass;
46 jmethodID constructor;
47 jmethodID set_on_frame_available_listener;
48 jmethodID update_tex_image;
49 jmethodID detach_from_gl_context;
50 jmethodID attach_to_gl_context;
51 jmethodID get_transform_matrix;
52 jmethodID get_timestamp;
53 jmethodID release;
54 } surface_texture;
55
56
57 G_DEFINE_TYPE (GstAmcSurfaceTextureJNI, gst_amc_surface_texture_jni,
58 GST_TYPE_AMC_SURFACE_TEXTURE);
59
60 gboolean
gst_amc_surface_texture_static_init(void)61 gst_amc_surface_texture_static_init (void)
62 {
63 JNIEnv *env;
64 GError *err = NULL;
65
66 env = gst_amc_jni_get_env ();
67
68 surface_texture.jklass = gst_amc_jni_get_class (env, &err,
69 "android/graphics/SurfaceTexture");
70 if (!surface_texture.jklass) {
71 GST_ERROR ("Failed to get android.graphics.SurfaceTexture class: %s",
72 err->message);
73 g_clear_error (&err);
74 return FALSE;
75 }
76
77 surface_texture.constructor =
78 gst_amc_jni_get_method_id (env, &err, surface_texture.jklass, "<init>",
79 "(I)V");
80 if (!surface_texture.constructor) {
81 goto error;
82 }
83
84 surface_texture.set_on_frame_available_listener =
85 gst_amc_jni_get_method_id (env, &err, surface_texture.jklass,
86 "setOnFrameAvailableListener",
87 "(Landroid/graphics/SurfaceTexture$OnFrameAvailableListener;)V");
88 if (!surface_texture.set_on_frame_available_listener) {
89 goto error;
90 }
91
92 surface_texture.update_tex_image =
93 gst_amc_jni_get_method_id (env, &err, surface_texture.jklass,
94 "updateTexImage", "()V");
95 if (!surface_texture.update_tex_image) {
96 goto error;
97 }
98
99 surface_texture.detach_from_gl_context =
100 gst_amc_jni_get_method_id (env, &err, surface_texture.jklass,
101 "detachFromGLContext", "()V");
102 if (!surface_texture.detach_from_gl_context) {
103 goto error;
104 }
105
106 surface_texture.attach_to_gl_context =
107 gst_amc_jni_get_method_id (env, &err, surface_texture.jklass,
108 "attachToGLContext", "(I)V");
109 if (!surface_texture.attach_to_gl_context) {
110 goto error;
111 }
112
113 surface_texture.get_transform_matrix =
114 gst_amc_jni_get_method_id (env, &err, surface_texture.jklass,
115 "getTransformMatrix", "([F)V");
116 if (!surface_texture.get_transform_matrix) {
117 goto error;
118 }
119
120 surface_texture.get_timestamp =
121 gst_amc_jni_get_method_id (env, &err, surface_texture.jklass,
122 "getTimestamp", "()J");
123 if (!surface_texture.get_timestamp) {
124 goto error;
125 }
126
127 surface_texture.release =
128 gst_amc_jni_get_method_id (env, &err, surface_texture.jklass, "release",
129 "()V");
130 if (!surface_texture.release) {
131 goto error;
132 }
133
134 return TRUE;
135
136 error:
137 GST_ERROR ("Failed to get android.graphics.SurfaceTexture methods: %s",
138 err->message);
139 g_clear_error (&err);
140 gst_amc_jni_object_unref (env, surface_texture.constructor);
141 return FALSE;
142 }
143
144 static gboolean
gst_amc_surface_texture_jni_update_tex_image(GstAmcSurfaceTexture * base,GError ** err)145 gst_amc_surface_texture_jni_update_tex_image (GstAmcSurfaceTexture * base,
146 GError ** err)
147 {
148 GstAmcSurfaceTextureJNI *self = GST_AMC_SURFACE_TEXTURE_JNI (base);
149 JNIEnv *env;
150
151 env = gst_amc_jni_get_env ();
152
153 return gst_amc_jni_call_void_method (env, err, self->jobject,
154 surface_texture.update_tex_image);
155 }
156
157 static gboolean
gst_amc_surface_texture_jni_detach_from_gl_context(GstAmcSurfaceTexture * base,GError ** err)158 gst_amc_surface_texture_jni_detach_from_gl_context (GstAmcSurfaceTexture * base,
159 GError ** err)
160 {
161 GstAmcSurfaceTextureJNI *self = GST_AMC_SURFACE_TEXTURE_JNI (base);
162 JNIEnv *env;
163 gboolean ret;
164
165 env = gst_amc_jni_get_env ();
166
167 ret =
168 gst_amc_jni_call_void_method (env, err, self->jobject,
169 surface_texture.detach_from_gl_context);
170 self->texture_id = 0;
171 return ret;
172 }
173
174 static gboolean
gst_amc_surface_texture_jni_attach_to_gl_context(GstAmcSurfaceTexture * base,gint texture_id,GError ** err)175 gst_amc_surface_texture_jni_attach_to_gl_context (GstAmcSurfaceTexture * base,
176 gint texture_id, GError ** err)
177 {
178 GstAmcSurfaceTextureJNI *self = GST_AMC_SURFACE_TEXTURE_JNI (base);
179 JNIEnv *env;
180 gboolean ret;
181
182 env = gst_amc_jni_get_env ();
183
184 ret =
185 gst_amc_jni_call_void_method (env, err, self->jobject,
186 surface_texture.attach_to_gl_context, texture_id);
187 self->texture_id = texture_id;
188 return ret;
189 }
190
191 static gboolean
gst_amc_surface_texture_jni_get_transform_matrix(GstAmcSurfaceTexture * base,gfloat * matrix,GError ** err)192 gst_amc_surface_texture_jni_get_transform_matrix (GstAmcSurfaceTexture * base,
193 gfloat * matrix, GError ** err)
194 {
195 GstAmcSurfaceTextureJNI *self = GST_AMC_SURFACE_TEXTURE_JNI (base);
196 JNIEnv *env;
197 gboolean ret;
198 /* 4x4 Matrix */
199 jsize size = 16;
200 jfloatArray floatarray;
201
202 env = gst_amc_jni_get_env ();
203
204 floatarray = (*env)->NewFloatArray (env, size);
205 ret =
206 gst_amc_jni_call_void_method (env, err, self->jobject,
207 surface_texture.get_transform_matrix, floatarray);
208 if (ret) {
209 (*env)->GetFloatArrayRegion (env, floatarray, 0, size, (jfloat *) matrix);
210 (*env)->DeleteLocalRef (env, floatarray);
211 }
212
213 return ret;
214 }
215
216 static gboolean
gst_amc_surface_texture_jni_get_timestamp(GstAmcSurfaceTexture * base,gint64 * result,GError ** err)217 gst_amc_surface_texture_jni_get_timestamp (GstAmcSurfaceTexture * base,
218 gint64 * result, GError ** err)
219 {
220 GstAmcSurfaceTextureJNI *self = GST_AMC_SURFACE_TEXTURE_JNI (base);
221 JNIEnv *env;
222
223 env = gst_amc_jni_get_env ();
224
225 return gst_amc_jni_call_long_method (env, err, self->jobject,
226 surface_texture.get_timestamp, result);
227 }
228
229 static gboolean
gst_amc_surface_texture_jni_release(GstAmcSurfaceTexture * base,GError ** err)230 gst_amc_surface_texture_jni_release (GstAmcSurfaceTexture * base, GError ** err)
231 {
232 GstAmcSurfaceTextureJNI *self = GST_AMC_SURFACE_TEXTURE_JNI (base);
233 JNIEnv *env;
234
235 env = gst_amc_jni_get_env ();
236
237 return gst_amc_jni_call_void_method (env, err, self->jobject,
238 surface_texture.release);
239 }
240
241 static void
on_frame_available_cb(JNIEnv * env,jobject thiz,long long context,jobject surfaceTexture)242 on_frame_available_cb (JNIEnv * env, jobject thiz,
243 long long context, jobject surfaceTexture)
244 {
245 GstAmcSurfaceTextureJNI *self = JLONG_TO_GPOINTER (context);
246
247 self->callback (GST_AMC_SURFACE_TEXTURE (self), self->user_data);
248 }
249
250 static gboolean
create_listener(GstAmcSurfaceTextureJNI * self,JNIEnv * env,GError ** err)251 create_listener (GstAmcSurfaceTextureJNI * self, JNIEnv * env, GError ** err)
252 {
253 jclass listener_cls = NULL;
254 jmethodID constructor_id = 0;
255
256 JNINativeMethod amcOnFrameAvailableListener = {
257 "native_onFrameAvailable",
258 "(JLandroid/graphics/SurfaceTexture;)V",
259 (void *) on_frame_available_cb,
260 };
261
262 listener_cls =
263 gst_amc_jni_get_application_class (env,
264 "org/freedesktop/gstreamer/androidmedia/GstAmcOnFrameAvailableListener",
265 err);
266 if (!listener_cls) {
267 return FALSE;
268 }
269
270 (*env)->RegisterNatives (env, listener_cls, &amcOnFrameAvailableListener, 1);
271 if ((*env)->ExceptionCheck (env)) {
272 gst_amc_jni_set_error (env, err, GST_LIBRARY_ERROR,
273 GST_LIBRARY_ERROR_FAILED, "Failed to register native methods");
274 goto done;
275 }
276
277 constructor_id =
278 gst_amc_jni_get_method_id (env, err, listener_cls, "<init>", "()V");
279 if (!constructor_id) {
280 goto done;
281 }
282
283 self->set_context_id =
284 gst_amc_jni_get_method_id (env, err, listener_cls, "setContext", "(J)V");
285 if (!self->set_context_id) {
286 goto done;
287 }
288
289 self->listener =
290 gst_amc_jni_new_object (env, err, TRUE, listener_cls, constructor_id);
291 if (!self->listener) {
292 goto done;
293 }
294
295 if (!gst_amc_jni_call_void_method (env, err, self->listener,
296 self->set_context_id, GPOINTER_TO_JLONG (self))) {
297 gst_amc_jni_object_unref (env, self->listener);
298 self->listener = NULL;
299 }
300
301 done:
302 gst_amc_jni_object_unref (env, listener_cls);
303
304 return self->listener != NULL;
305 }
306
307 static gboolean
remove_listener(GstAmcSurfaceTextureJNI * self,JNIEnv * env,GError ** err)308 remove_listener (GstAmcSurfaceTextureJNI * self, JNIEnv * env, GError ** err)
309 {
310 if (self->listener) {
311 if (!gst_amc_jni_call_void_method (env, err, self->listener,
312 self->set_context_id, GPOINTER_TO_JLONG (NULL)))
313 return FALSE;
314
315 gst_amc_jni_object_unref (env, self->listener);
316 self->listener = NULL;
317 }
318
319 return TRUE;
320 }
321
322 static gboolean
gst_amc_surface_texture_jni_set_on_frame_available_callback(GstAmcSurfaceTexture * base,GstAmcSurfaceTextureOnFrameAvailableCallback callback,gpointer user_data,GError ** err)323 gst_amc_surface_texture_jni_set_on_frame_available_callback
324 (GstAmcSurfaceTexture * base,
325 GstAmcSurfaceTextureOnFrameAvailableCallback callback, gpointer user_data,
326 GError ** err)
327 {
328 GstAmcSurfaceTextureJNI *self = GST_AMC_SURFACE_TEXTURE_JNI (base);
329 JNIEnv *env;
330 GError *local_error = NULL;
331
332 env = gst_amc_jni_get_env ();
333
334 if (!remove_listener (self, env, err))
335 return FALSE;
336
337 self->callback = callback;
338 self->user_data = user_data;
339 if (callback == NULL)
340 return TRUE;
341
342 if (!create_listener (self, env, &local_error)) {
343 GST_ERROR ("Could not create listener: %s", local_error->message);
344 g_propagate_error (err, local_error);
345 return FALSE;
346 }
347
348 if (!gst_amc_jni_call_void_method (env, err, self->jobject,
349 surface_texture.set_on_frame_available_listener, self->listener)) {
350 remove_listener (self, env, NULL);
351 return FALSE;
352 }
353
354 return TRUE;
355 }
356
357 static void
gst_amc_surface_texture_jni_dispose(GObject * object)358 gst_amc_surface_texture_jni_dispose (GObject * object)
359 {
360 GstAmcSurfaceTextureJNI *self = GST_AMC_SURFACE_TEXTURE_JNI (object);
361 JNIEnv *env;
362 GError *err = NULL;
363
364 env = gst_amc_jni_get_env ();
365
366 if (!gst_amc_surface_texture_jni_release (GST_AMC_SURFACE_TEXTURE (self),
367 &err)) {
368 GST_ERROR ("Could not release surface texture: %s", err->message);
369 g_clear_error (&err);
370 }
371
372 remove_listener (self, env, NULL);
373
374 if (self->jobject) {
375 gst_amc_jni_object_unref (env, self->jobject);
376 }
377
378 G_OBJECT_CLASS (gst_amc_surface_texture_jni_parent_class)->dispose (object);
379 }
380
381 static void
gst_amc_surface_texture_jni_class_init(GstAmcSurfaceTextureJNIClass * klass)382 gst_amc_surface_texture_jni_class_init (GstAmcSurfaceTextureJNIClass * klass)
383 {
384 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
385 GstAmcSurfaceTextureClass *surface_texture_class =
386 GST_AMC_SURFACE_TEXTURE_CLASS (klass);
387
388 gobject_class->dispose = gst_amc_surface_texture_jni_dispose;
389
390 surface_texture_class->update_tex_image =
391 gst_amc_surface_texture_jni_update_tex_image;
392 surface_texture_class->detach_from_gl_context =
393 gst_amc_surface_texture_jni_detach_from_gl_context;
394 surface_texture_class->attach_to_gl_context =
395 gst_amc_surface_texture_jni_attach_to_gl_context;
396 surface_texture_class->get_transform_matrix =
397 gst_amc_surface_texture_jni_get_transform_matrix;
398 surface_texture_class->get_timestamp =
399 gst_amc_surface_texture_jni_get_timestamp;
400 surface_texture_class->release = gst_amc_surface_texture_jni_release;
401 surface_texture_class->set_on_frame_available_callback =
402 gst_amc_surface_texture_jni_set_on_frame_available_callback;
403 }
404
405 static void
gst_amc_surface_texture_jni_init(GstAmcSurfaceTextureJNI * self)406 gst_amc_surface_texture_jni_init (GstAmcSurfaceTextureJNI * self)
407 {
408 }
409
410 GstAmcSurfaceTextureJNI *
gst_amc_surface_texture_jni_new(GError ** err)411 gst_amc_surface_texture_jni_new (GError ** err)
412 {
413 GstAmcSurfaceTextureJNI *self = NULL;
414 JNIEnv *env;
415
416 self = g_object_new (GST_TYPE_AMC_SURFACE_TEXTURE_JNI, NULL);
417 env = gst_amc_jni_get_env ();
418
419 self->texture_id = 0;
420
421 self->jobject =
422 gst_amc_jni_new_object (env, err, TRUE, surface_texture.jklass,
423 surface_texture.constructor, self->texture_id);
424 if (self->jobject == NULL) {
425 goto error;
426 }
427
428 if (!gst_amc_surface_texture_jni_detach_from_gl_context ((GstAmcSurfaceTexture
429 *) self, err)) {
430 goto error;
431 }
432
433 return self;
434
435 error:
436 if (self)
437 g_object_unref (self);
438 return NULL;
439 }
440
441 jobject
gst_amc_surface_texture_jni_get_jobject(GstAmcSurfaceTextureJNI * self)442 gst_amc_surface_texture_jni_get_jobject (GstAmcSurfaceTextureJNI * self)
443 {
444 return self->jobject;
445 }
446