1 /*
2  *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 
11 #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
12 #include "webrtc/modules/video_render/android/video_render_android_surface_view.h"
13 #include "webrtc/system_wrappers/include/critical_section_wrapper.h"
14 #include "webrtc/system_wrappers/include/tick_util.h"
15 
16 #ifdef ANDROID_LOG
17 #include <android/log.h>
18 #include <stdio.h>
19 
20 #undef WEBRTC_TRACE
21 #define WEBRTC_TRACE(a,b,c,...)  __android_log_print(ANDROID_LOG_DEBUG, "*WEBRTC*", __VA_ARGS__)
22 #else
23 #include "webrtc/system_wrappers/include/trace.h"
24 #endif
25 
26 namespace webrtc {
27 
AndroidSurfaceViewRenderer(const int32_t id,const VideoRenderType videoRenderType,void * window,const bool fullscreen)28 AndroidSurfaceViewRenderer::AndroidSurfaceViewRenderer(
29     const int32_t id,
30     const VideoRenderType videoRenderType,
31     void* window,
32     const bool fullscreen) :
33     VideoRenderAndroid(id,videoRenderType,window,fullscreen),
34     _javaRenderObj(NULL),
35     _javaRenderClass(NULL) {
36 }
37 
~AndroidSurfaceViewRenderer()38 AndroidSurfaceViewRenderer::~AndroidSurfaceViewRenderer() {
39   WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id,
40                "AndroidSurfaceViewRenderer dtor");
41   if(g_jvm) {
42     // get the JNI env for this thread
43     bool isAttached = false;
44     JNIEnv* env = NULL;
45     if (g_jvm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) {
46       // try to attach the thread and get the env
47       // Attach this thread to JVM
48       jint res = g_jvm->AttachCurrentThread(&env, NULL);
49 
50       // Get the JNI env for this thread
51       if ((res < 0) || !env) {
52         WEBRTC_TRACE(kTraceError,
53                      kTraceVideoRenderer,
54                      _id,
55                      "%s: Could not attach thread to JVM (%d, %p)",
56                      __FUNCTION__,
57                      res,
58                      env);
59         env=NULL;
60       }
61       else {
62         isAttached = true;
63       }
64     }
65     env->DeleteGlobalRef(_javaRenderObj);
66     env->DeleteGlobalRef(_javaRenderClass);
67 
68     if (isAttached) {
69       if (g_jvm->DetachCurrentThread() < 0) {
70         WEBRTC_TRACE(kTraceWarning,
71                      kTraceVideoRenderer,
72                      _id,
73                      "%s: Could not detach thread from JVM",
74                      __FUNCTION__);
75       }
76     }
77   }
78 }
79 
Init()80 int32_t AndroidSurfaceViewRenderer::Init() {
81   WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "%s", __FUNCTION__);
82   if (!g_jvm) {
83     WEBRTC_TRACE(kTraceError,
84                  kTraceVideoRenderer,
85                  _id,
86                  "(%s): Not a valid Java VM pointer.",
87                  __FUNCTION__);
88     return -1;
89   }
90   if(!_ptrWindow) {
91     WEBRTC_TRACE(kTraceWarning,
92                  kTraceVideoRenderer,
93                  _id,
94                  "(%s): No window have been provided.",
95                  __FUNCTION__);
96     return -1;
97   }
98 
99   // get the JNI env for this thread
100   bool isAttached = false;
101   JNIEnv* env = NULL;
102   if (g_jvm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) {
103     // try to attach the thread and get the env
104     // Attach this thread to JVM
105     jint res = g_jvm->AttachCurrentThread(&env, NULL);
106 
107     // Get the JNI env for this thread
108     if ((res < 0) || !env) {
109       WEBRTC_TRACE(kTraceError,
110                    kTraceVideoRenderer,
111                    _id,
112                    "%s: Could not attach thread to JVM (%d, %p)",
113                    __FUNCTION__,
114                    res,
115                    env);
116       return -1;
117     }
118     isAttached = true;
119   }
120 
121   // get the ViESurfaceRender class
122   jclass javaRenderClassLocal =
123       env->FindClass("org/webrtc/videoengine/ViESurfaceRenderer");
124   if (!javaRenderClassLocal) {
125     WEBRTC_TRACE(kTraceError,
126                  kTraceVideoRenderer,
127                  _id,
128                  "%s: could not find ViESurfaceRenderer",
129                  __FUNCTION__);
130     return -1;
131   }
132 
133   // create a global reference to the class (to tell JNI that
134   // we are referencing it after this function has returned)
135   _javaRenderClass =
136       reinterpret_cast<jclass>(env->NewGlobalRef(javaRenderClassLocal));
137   if (!_javaRenderClass) {
138     WEBRTC_TRACE(kTraceError,
139                  kTraceVideoRenderer,
140                  _id,
141                  "%s: could not create Java ViESurfaceRenderer class reference",
142                  __FUNCTION__);
143     return -1;
144   }
145 
146   // Delete local class ref, we only use the global ref
147   env->DeleteLocalRef(javaRenderClassLocal);
148 
149   // get the method ID for the constructor
150   jmethodID cid = env->GetMethodID(_javaRenderClass,
151                                    "<init>",
152                                    "(Landroid/view/SurfaceView;)V");
153   if (cid == NULL) {
154     WEBRTC_TRACE(kTraceError,
155                  kTraceVideoRenderer,
156                  _id,
157                  "%s: could not get constructor ID",
158                  __FUNCTION__);
159     return -1; /* exception thrown */
160   }
161 
162   // construct the object
163   jobject javaRenderObjLocal = env->NewObject(_javaRenderClass,
164                                               cid,
165                                               _ptrWindow);
166   if (!javaRenderObjLocal) {
167     WEBRTC_TRACE(kTraceError,
168                  kTraceVideoRenderer,
169                  _id,
170                  "%s: could not create Java Render",
171                  __FUNCTION__);
172     return -1;
173   }
174 
175   // create a reference to the object (to tell JNI that we are referencing it
176   // after this function has returned)
177   _javaRenderObj = env->NewGlobalRef(javaRenderObjLocal);
178   if (!_javaRenderObj) {
179     WEBRTC_TRACE(kTraceError,
180                  kTraceVideoRenderer,
181                  _id,
182                  "%s: could not create Java SurfaceRender object reference",
183                  __FUNCTION__);
184     return -1;
185   }
186 
187   // Detach this thread if it was attached
188   if (isAttached) {
189     if (g_jvm->DetachCurrentThread() < 0) {
190       WEBRTC_TRACE(kTraceWarning,
191                    kTraceVideoRenderer,
192                    _id,
193                    "%s: Could not detach thread from JVM", __FUNCTION__);
194     }
195   }
196 
197   WEBRTC_TRACE(kTraceDebug, kTraceVideoRenderer, _id, "%s done", __FUNCTION__);
198   return 0;
199 }
200 
201 AndroidStream*
CreateAndroidRenderChannel(int32_t streamId,int32_t zOrder,const float left,const float top,const float right,const float bottom,VideoRenderAndroid & renderer)202 AndroidSurfaceViewRenderer::CreateAndroidRenderChannel(
203     int32_t streamId,
204     int32_t zOrder,
205     const float left,
206     const float top,
207     const float right,
208     const float bottom,
209     VideoRenderAndroid& renderer) {
210   WEBRTC_TRACE(kTraceDebug,
211                kTraceVideoRenderer,
212                _id,
213                "%s: Id %d",
214                __FUNCTION__,
215                streamId);
216   AndroidSurfaceViewChannel* stream =
217       new AndroidSurfaceViewChannel(streamId, g_jvm, renderer, _javaRenderObj);
218   if(stream && stream->Init(zOrder, left, top, right, bottom) == 0)
219     return stream;
220   else
221     delete stream;
222   return NULL;
223 }
224 
AndroidSurfaceViewChannel(uint32_t streamId,JavaVM * jvm,VideoRenderAndroid & renderer,jobject javaRenderObj)225 AndroidSurfaceViewChannel::AndroidSurfaceViewChannel(
226     uint32_t streamId,
227     JavaVM* jvm,
228     VideoRenderAndroid& renderer,
229     jobject javaRenderObj) :
230     _id(streamId),
231     _renderCritSect(*CriticalSectionWrapper::CreateCriticalSection()),
232     _renderer(renderer),
233     _jvm(jvm),
234     _javaRenderObj(javaRenderObj),
235 #ifndef ANDROID_NDK_8_OR_ABOVE
236     _javaByteBufferObj(NULL),
237     _directBuffer(NULL),
238 #endif
239     _bitmapWidth(0),
240     _bitmapHeight(0) {
241 }
242 
~AndroidSurfaceViewChannel()243 AndroidSurfaceViewChannel::~AndroidSurfaceViewChannel() {
244   WEBRTC_TRACE(kTraceInfo,
245                kTraceVideoRenderer,
246                _id,
247                "AndroidSurfaceViewChannel dtor");
248   delete &_renderCritSect;
249   if(_jvm) {
250     // get the JNI env for this thread
251     bool isAttached = false;
252     JNIEnv* env = NULL;
253     if ( _jvm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) {
254       // try to attach the thread and get the env
255       // Attach this thread to JVM
256       jint res = _jvm->AttachCurrentThread(&env, NULL);
257 
258       // Get the JNI env for this thread
259       if ((res < 0) || !env) {
260         WEBRTC_TRACE(kTraceError,
261                      kTraceVideoRenderer,
262                      _id,
263                      "%s: Could not attach thread to JVM (%d, %p)",
264                      __FUNCTION__,
265                      res,
266                      env);
267         env=NULL;
268       }
269       else {
270         isAttached = true;
271       }
272     }
273 
274     env->DeleteGlobalRef(_javaByteBufferObj);
275     if (isAttached) {
276       if (_jvm->DetachCurrentThread() < 0) {
277         WEBRTC_TRACE(kTraceWarning,
278                      kTraceVideoRenderer,
279                      _id,
280                      "%s: Could not detach thread from JVM",
281                      __FUNCTION__);
282       }
283     }
284   }
285 }
286 
Init(int32_t,const float left,const float top,const float right,const float bottom)287 int32_t AndroidSurfaceViewChannel::Init(
288     int32_t /*zOrder*/,
289     const float left,
290     const float top,
291     const float right,
292     const float bottom) {
293 
294   WEBRTC_TRACE(kTraceDebug,
295                kTraceVideoRenderer,
296                _id,
297                "%s: AndroidSurfaceViewChannel",
298                __FUNCTION__);
299   if (!_jvm) {
300     WEBRTC_TRACE(kTraceError,
301                  kTraceVideoRenderer,
302                  _id,
303                  "%s: Not a valid Java VM pointer",
304                  __FUNCTION__);
305     return -1;
306   }
307 
308   if( (top > 1 || top < 0) ||
309       (right > 1 || right < 0) ||
310       (bottom > 1 || bottom < 0) ||
311       (left > 1 || left < 0)) {
312     WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id,
313                  "%s: Wrong coordinates", __FUNCTION__);
314     return -1;
315   }
316 
317   // get the JNI env for this thread
318   bool isAttached = false;
319   JNIEnv* env = NULL;
320   if (_jvm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) {
321     // try to attach the thread and get the env
322     // Attach this thread to JVM
323     jint res = _jvm->AttachCurrentThread(&env, NULL);
324 
325     // Get the JNI env for this thread
326     if ((res < 0) || !env) {
327       WEBRTC_TRACE(kTraceError,
328                    kTraceVideoRenderer,
329                    _id,
330                    "%s: Could not attach thread to JVM (%d, %p)",
331                    __FUNCTION__,
332                    res,
333                    env);
334       return -1;
335     }
336     isAttached = true;
337   }
338 
339   jclass javaRenderClass =
340       env->FindClass("org/webrtc/videoengine/ViESurfaceRenderer");
341   if (!javaRenderClass) {
342     WEBRTC_TRACE(kTraceError,
343                  kTraceVideoRenderer,
344                  _id,
345                  "%s: could not find ViESurfaceRenderer",
346                  __FUNCTION__);
347     return -1;
348   }
349 
350   // get the method ID for the CreateIntArray
351   _createByteBufferCid =
352       env->GetMethodID(javaRenderClass,
353                        "CreateByteBuffer",
354                        "(II)Ljava/nio/ByteBuffer;");
355   if (_createByteBufferCid == NULL) {
356     WEBRTC_TRACE(kTraceError,
357                  kTraceVideoRenderer,
358                  _id,
359                  "%s: could not get CreateByteBuffer ID",
360                  __FUNCTION__);
361     return -1; /* exception thrown */
362   }
363 
364   // get the method ID for the DrawByteBuffer function
365   _drawByteBufferCid = env->GetMethodID(javaRenderClass,
366                                         "DrawByteBuffer",
367                                         "()V");
368   if (_drawByteBufferCid == NULL) {
369     WEBRTC_TRACE(kTraceError,
370                  kTraceVideoRenderer,
371                  _id,
372                  "%s: could not get DrawByteBuffer ID",
373                  __FUNCTION__);
374     return -1; /* exception thrown */
375   }
376 
377   // get the method ID for the SetCoordinates function
378   _setCoordinatesCid = env->GetMethodID(javaRenderClass,
379                                         "SetCoordinates",
380                                         "(FFFF)V");
381   if (_setCoordinatesCid == NULL) {
382     WEBRTC_TRACE(kTraceError,
383                  kTraceVideoRenderer,
384                  _id,
385                  "%s: could not get SetCoordinates ID",
386                  __FUNCTION__);
387     return -1; /* exception thrown */
388   }
389 
390   env->CallVoidMethod(_javaRenderObj, _setCoordinatesCid,
391                       left, top, right, bottom);
392 
393   // Detach this thread if it was attached
394   if (isAttached) {
395     if (_jvm->DetachCurrentThread() < 0) {
396       WEBRTC_TRACE(kTraceWarning,
397                    kTraceVideoRenderer,
398                    _id,
399                    "%s: Could not detach thread from JVM",
400                    __FUNCTION__);
401     }
402   }
403 
404   WEBRTC_TRACE(kTraceDebug,
405                kTraceVideoRenderer,
406                _id,
407                "%s: AndroidSurfaceViewChannel done",
408                __FUNCTION__);
409   return 0;
410 }
411 
RenderFrame(const uint32_t,const VideoFrame & videoFrame)412 int32_t AndroidSurfaceViewChannel::RenderFrame(const uint32_t /*streamId*/,
413                                                const VideoFrame& videoFrame) {
414   // WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer,_id, "%s:" ,__FUNCTION__);
415   _renderCritSect.Enter();
416   _bufferToRender = videoFrame;
417   _renderCritSect.Leave();
418   _renderer.ReDraw();
419   return 0;
420 }
421 
422 
423 /*Implements AndroidStream
424  * Calls the Java object and render the buffer in _bufferToRender
425  */
DeliverFrame(JNIEnv * jniEnv)426 void AndroidSurfaceViewChannel::DeliverFrame(JNIEnv* jniEnv) {
427   _renderCritSect.Enter();
428 
429   if (_bitmapWidth != _bufferToRender.width() ||
430       _bitmapHeight != _bufferToRender.height()) {
431     WEBRTC_TRACE(kTraceInfo, kTraceVideoRenderer, _id, "%s: New render size %d "
432                  "%d",__FUNCTION__,
433                  _bufferToRender.width(), _bufferToRender.height());
434     if (_javaByteBufferObj) {
435       jniEnv->DeleteGlobalRef(_javaByteBufferObj);
436       _javaByteBufferObj = NULL;
437       _directBuffer = NULL;
438     }
439 
440     jobject javaByteBufferObj =
441         jniEnv->CallObjectMethod(_javaRenderObj, _createByteBufferCid,
442                                  _bufferToRender.width(),
443                                  _bufferToRender.height());
444     _javaByteBufferObj = jniEnv->NewGlobalRef(javaByteBufferObj);
445     if (!_javaByteBufferObj) {
446       WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id,  "%s: could not "
447                    "create Java ByteBuffer object reference", __FUNCTION__);
448       _renderCritSect.Leave();
449       return;
450     } else {
451       _directBuffer = static_cast<unsigned char*>
452           (jniEnv->GetDirectBufferAddress(_javaByteBufferObj));
453       _bitmapWidth = _bufferToRender.width();
454       _bitmapHeight = _bufferToRender.height();
455     }
456   }
457 
458   if(_javaByteBufferObj && _bitmapWidth && _bitmapHeight) {
459     const int conversionResult =
460         ConvertFromI420(_bufferToRender, kRGB565, 0, _directBuffer);
461 
462     if (conversionResult < 0)  {
463       WEBRTC_TRACE(kTraceError, kTraceVideoRenderer, _id, "%s: Color conversion"
464                    " failed.", __FUNCTION__);
465       _renderCritSect.Leave();
466       return;
467     }
468   }
469   _renderCritSect.Leave();
470   // Draw the Surface
471   jniEnv->CallVoidMethod(_javaRenderObj, _drawByteBufferCid);
472 }
473 
474 }  // namespace webrtc
475