1/*
2 * Copyright (C) 2011 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// #define LOG_NDEBUG 0
17
18#include <stdint.h>
19#include <android/native_window_jni.h>
20
21#include "jni/jni_gl_environment.h"
22#include "jni/jni_util.h"
23#include <media/mediarecorder.h>
24#include "native/core/gl_env.h"
25
26#include <gui/IGraphicBufferProducer.h>
27#include <gui/Surface.h>
28#include <utils/Errors.h>
29#include <system/window.h>
30
31
32using android::filterfw::GLEnv;
33using android::filterfw::WindowHandle;
34using android::MediaRecorder;
35using android::sp;
36using android::IGraphicBufferProducer;
37using android::Surface;
38
39
40class NativeWindowHandle : public WindowHandle {
41  public:
42    NativeWindowHandle(ANativeWindow* window) : window_(window) {
43    }
44
45    virtual ~NativeWindowHandle() {
46    }
47
48    virtual void Destroy() {
49      ALOGI("Releasing ANativeWindow!");
50      ANativeWindow_release(window_);
51    }
52
53    virtual const void* InternalHandle() const {
54      return window_;
55    }
56
57    virtual void* InternalHandle() {
58      return window_;
59    }
60
61  private:
62    ANativeWindow* window_;
63};
64
65jboolean Java_android_filterfw_core_GLEnvironment_nativeAllocate(JNIEnv* env, jobject thiz) {
66  return ToJBool(WrapObjectInJava(new GLEnv(), env, thiz, true));
67}
68
69jboolean Java_android_filterfw_core_GLEnvironment_nativeDeallocate(JNIEnv* env, jobject thiz) {
70  return ToJBool(DeleteNativeObject<GLEnv>(env, thiz));
71}
72
73jboolean Java_android_filterfw_core_GLEnvironment_nativeInitWithNewContext(JNIEnv* env,
74                                                                           jobject thiz) {
75  GLEnv* gl_env = ConvertFromJava<GLEnv>(env, thiz);
76  return gl_env ? ToJBool(gl_env->InitWithNewContext()) : JNI_FALSE;
77}
78
79jboolean Java_android_filterfw_core_GLEnvironment_nativeInitWithCurrentContext(JNIEnv* env,
80                                                                               jobject thiz) {
81  GLEnv* gl_env = ConvertFromJava<GLEnv>(env, thiz);
82  return gl_env ? ToJBool(gl_env->InitWithCurrentContext()) : JNI_FALSE;
83}
84
85jboolean Java_android_filterfw_core_GLEnvironment_nativeIsActive(JNIEnv* env, jobject thiz) {
86  GLEnv* gl_env = ConvertFromJava<GLEnv>(env, thiz);
87  return gl_env ? ToJBool(gl_env->IsActive()) : JNI_FALSE;
88}
89
90jboolean Java_android_filterfw_core_GLEnvironment_nativeIsContextActive(JNIEnv* env, jobject thiz) {
91  GLEnv* gl_env = ConvertFromJava<GLEnv>(env, thiz);
92  return gl_env ? ToJBool(gl_env->IsContextActive()) : JNI_FALSE;
93}
94
95jboolean Java_android_filterfw_core_GLEnvironment_nativeIsAnyContextActive(JNIEnv* env,
96                                                                           jclass clazz) {
97  return ToJBool(GLEnv::IsAnyContextActive());
98}
99
100jboolean Java_android_filterfw_core_GLEnvironment_nativeActivate(JNIEnv* env, jobject thiz) {
101  GLEnv* gl_env = ConvertFromJava<GLEnv>(env, thiz);
102  return gl_env ? ToJBool(gl_env->Activate()) : JNI_FALSE;
103}
104
105jboolean Java_android_filterfw_core_GLEnvironment_nativeDeactivate(JNIEnv* env, jobject thiz) {
106  GLEnv* gl_env = ConvertFromJava<GLEnv>(env, thiz);
107  return gl_env ? ToJBool(gl_env->Deactivate()) : JNI_FALSE;
108}
109
110jboolean Java_android_filterfw_core_GLEnvironment_nativeSwapBuffers(JNIEnv* env, jobject thiz) {
111  GLEnv* gl_env = ConvertFromJava<GLEnv>(env, thiz);
112  return gl_env ? ToJBool(gl_env->SwapBuffers()) : JNI_FALSE;
113}
114
115// Get the native mediarecorder object corresponding to the java object
116static sp<MediaRecorder> getMediaRecorder(JNIEnv* env, jobject jmediarecorder) {
117    jclass clazz = env->FindClass("android/media/MediaRecorder");
118    if (clazz == NULL) {
119        return NULL;
120    }
121
122    jfieldID context = env->GetFieldID(clazz, "mNativeContext", "J");
123    if (context == NULL) {
124        return NULL;
125    }
126
127    MediaRecorder* const p = (MediaRecorder*)env->GetLongField(jmediarecorder, context);
128    env->DeleteLocalRef(clazz);
129    return sp<MediaRecorder>(p);
130}
131
132
133jint Java_android_filterfw_core_GLEnvironment_nativeAddSurface(JNIEnv* env,
134                                                               jobject thiz,
135                                                               jobject surface) {
136  GLEnv* gl_env = ConvertFromJava<GLEnv>(env, thiz);
137  if (!surface) {
138    ALOGE("GLEnvironment: Null Surface passed!");
139    return -1;
140  } else if (gl_env) {
141    // Get the ANativeWindow
142    ANativeWindow* window = ANativeWindow_fromSurface(env, surface);
143    if (!window) {
144      ALOGE("GLEnvironment: Error creating window!");
145      return -1;
146    }
147
148    NativeWindowHandle* winHandle = new NativeWindowHandle(window);
149    int result = gl_env->FindSurfaceIdForWindow(winHandle);
150    if (result == -1) {
151      // Configure surface
152      EGLConfig config;
153      EGLint numConfigs = -1;
154      EGLint configAttribs[] = {
155        EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
156        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
157        EGL_RED_SIZE, 8,
158        EGL_GREEN_SIZE, 8,
159        EGL_BLUE_SIZE, 8,
160        EGL_RECORDABLE_ANDROID, EGL_TRUE,
161        EGL_NONE
162      };
163
164
165
166      eglChooseConfig(gl_env->display(), configAttribs, &config, 1, &numConfigs);
167      if (numConfigs < 1) {
168        ALOGE("GLEnvironment: No suitable EGL configuration found for surface!");
169        return -1;
170      }
171
172      // Create the EGL surface
173      EGLSurface egl_surface = eglCreateWindowSurface(gl_env->display(),
174                                                      config,
175                                                      window,
176                                                      NULL);
177
178      if (GLEnv::CheckEGLError("eglCreateWindowSurface")) {
179        ALOGE("GLEnvironment: Error creating window surface!");
180        return -1;
181      }
182
183      // Add it to GL Env and assign ID
184      result = gl_env->AddWindowSurface(egl_surface, winHandle);
185    } else {
186      delete winHandle;
187    }
188    return result;
189  }
190  return -1;
191}
192
193jint Java_android_filterfw_core_GLEnvironment_nativeAddSurfaceWidthHeight(JNIEnv* env,
194                                                                      jobject thiz,
195                                                                      jobject surface,
196                                                                      jint width,
197                                                                      jint height) {
198  GLEnv* gl_env = ConvertFromJava<GLEnv>(env, thiz);
199  if (!surface) {
200    ALOGE("GLEnvironment: Null SurfaceTexture passed!");
201    return -1;
202  } else if (gl_env) {
203    // Get the ANativeWindow
204    ANativeWindow* window = ANativeWindow_fromSurface(env, surface);
205    if (!window) {
206      ALOGE("GLEnvironment: Error creating window!");
207      return -1;
208    }
209
210    // Don't care about format (will get overridden by SurfaceTexture
211    // anyway), but do care about width and height
212    // TODO: Probably, this should be just be
213    // ANativeWindow_setBuffersDimensions. The pixel format is
214    // set during the eglCreateWindowSurface
215    ANativeWindow_setBuffersGeometry(window, width, height, 0);
216
217    NativeWindowHandle* winHandle = new NativeWindowHandle(window);
218    int result = gl_env->FindSurfaceIdForWindow(winHandle);
219    if (result == -1) {
220      // Configure surface
221      EGLConfig config;
222      EGLint numConfigs = -1;
223      EGLint configAttribs[] = {
224        EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
225        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
226        EGL_RED_SIZE, 8,
227        EGL_GREEN_SIZE, 8,
228        EGL_BLUE_SIZE, 8,
229        EGL_RECORDABLE_ANDROID, EGL_TRUE,
230        EGL_NONE
231      };
232
233
234
235      eglChooseConfig(gl_env->display(), configAttribs, &config, 1, &numConfigs);
236      if (numConfigs < 1) {
237        ALOGE("GLEnvironment: No suitable EGL configuration found for surface texture!");
238        return -1;
239      }
240
241      // Create the EGL surface
242      EGLSurface egl_surface = eglCreateWindowSurface(gl_env->display(),
243                                                      config,
244                                                      window,
245                                                      NULL);
246
247      if (GLEnv::CheckEGLError("eglCreateWindowSurface")) {
248        ALOGE("GLEnvironment: Error creating window surface!");
249        return -1;
250      }
251
252      // Add it to GL Env and assign ID
253      result = gl_env->AddWindowSurface(egl_surface, winHandle);
254    } else {
255      delete winHandle;
256    }
257    return result;
258  }
259  return -1;
260}
261
262// nativeAddSurfaceFromMediaRecorder gets an EGLSurface
263// using a MediaRecorder object.
264// When Mediarecorder is used for recording GL Frames, it
265// will have a reference to a Native Handle (a SurfaceTexureClient)
266// which talks to the StageFrightRecorder in mediaserver via
267// a binder interface.
268jint Java_android_filterfw_core_GLEnvironment_nativeAddSurfaceFromMediaRecorder(
269                                                      JNIEnv* env,
270                                                      jobject thiz,
271                                                      jobject jmediarecorder) {
272    ALOGV("GLEnv Jni: nativeAddSurfaceFromMediaRecorder");
273    GLEnv* gl_env = ConvertFromJava<GLEnv>(env, thiz);
274    if (!gl_env) {
275        return -1;
276    }
277    // get a native mediarecorder object from the java object
278    sp<MediaRecorder> mr = getMediaRecorder(env, jmediarecorder);
279    if (mr == NULL) {
280        ALOGE("GLEnvironment: Error- MediaRecorder could not be initialized!");
281        return -1;
282    }
283
284    // Ask the mediarecorder to return a handle to a surfacemediasource
285    // This will talk to the StageFrightRecorder via MediaRecorderClient
286    // over binder calls
287    sp<IGraphicBufferProducer> surfaceMS = mr->querySurfaceMediaSourceFromMediaServer();
288    if (surfaceMS == NULL) {
289      ALOGE("GLEnvironment: Error- MediaRecorder returned a null \
290              <IGraphicBufferProducer> handle.");
291      return -1;
292    }
293    sp<Surface> surfaceTC = new Surface(surfaceMS);
294    // Get the ANativeWindow
295    sp<ANativeWindow> window = surfaceTC;
296
297
298    if (window == NULL) {
299      ALOGE("GLEnvironment: Error creating window!");
300      return -1;
301    }
302    window->incStrong((void*)ANativeWindow_acquire);
303
304    // In case of encoding, no need to set the dimensions
305    // on the buffers. The dimensions for the final encoding are set by
306    // the consumer side.
307    // The pixel format is dictated by the GL, and set during the
308    // eglCreateWindowSurface
309
310    NativeWindowHandle* winHandle = new NativeWindowHandle(window.get());
311    int result = gl_env->FindSurfaceIdForWindow(winHandle);
312    // If we find a surface with that window handle, just return that id
313    if (result != -1) {
314        delete winHandle;
315        return result;
316    }
317    // If we do not find a surface with that window handle, create
318    // one and assign to it the handle
319    // Configure surface
320    EGLConfig config;
321    EGLint numConfigs = -1;
322    EGLint configAttribs[] = {
323          EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
324          EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
325          EGL_RED_SIZE, 8,
326          EGL_GREEN_SIZE, 8,
327          EGL_BLUE_SIZE, 8,
328          EGL_RECORDABLE_ANDROID, EGL_TRUE,
329          EGL_NONE
330    };
331
332
333    eglChooseConfig(gl_env->display(), configAttribs, &config, 1, &numConfigs);
334    if (numConfigs < 1) {
335      ALOGE("GLEnvironment: No suitable EGL configuration found for surface texture!");
336      delete winHandle;
337      return -1;
338    }
339
340    // Create the EGL surface
341    EGLSurface egl_surface = eglCreateWindowSurface(gl_env->display(),
342                                                    config,
343                                                    window.get(),
344                                                    NULL);
345
346    if (GLEnv::CheckEGLError("eglCreateWindowSurface")) {
347      ALOGE("GLEnvironment: Error creating window surface!");
348      delete winHandle;
349      return -1;
350    }
351
352    // Add it to GL Env and assign ID
353    result = gl_env->AddWindowSurface(egl_surface, winHandle);
354    return result;
355}
356
357jboolean Java_android_filterfw_core_GLEnvironment_nativeActivateSurfaceId(JNIEnv* env,
358                                                                          jobject thiz,
359                                                                          jint surfaceId) {
360  GLEnv* gl_env = ConvertFromJava<GLEnv>(env, thiz);
361  return gl_env ? ToJBool(gl_env->SwitchToSurfaceId(surfaceId) && gl_env->Activate()) : JNI_FALSE;
362}
363
364jboolean Java_android_filterfw_core_GLEnvironment_nativeRemoveSurfaceId(JNIEnv* env,
365                                                                        jobject thiz,
366                                                                        jint surfaceId) {
367  GLEnv* gl_env = ConvertFromJava<GLEnv>(env, thiz);
368  return gl_env ? ToJBool(gl_env->ReleaseSurfaceId(surfaceId)) : JNI_FALSE;
369}
370
371jboolean Java_android_filterfw_core_GLEnvironment_nativeSetSurfaceTimestamp(JNIEnv* env,
372                                                                            jobject thiz,
373                                                                            jlong timestamp) {
374  GLEnv* gl_env = ConvertFromJava<GLEnv>(env, thiz);
375  int64_t timestamp_native = timestamp;
376  return gl_env ? ToJBool(gl_env->SetSurfaceTimestamp(timestamp_native)) : JNI_FALSE;
377}
378