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