1 /*
2  * Copyright (C) 2017 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 #include <vector>
17 #include <stdio.h>
18 #include <fcntl.h>
19 #include <alloca.h>
20 #include <unistd.h>
21 #include <sys/ioctl.h>
22 #include <malloc.h>
23 #include <png.h>
24 
25 #include "VideoTex.h"
26 #include "glError.h"
27 
28 #include <ui/GraphicBuffer.h>
29 
30 // Eventually we shouldn't need this dependency, but for now the
31 // graphics allocator interface isn't fully supported on all platforms
32 // and this is our work around.
33 using ::android::GraphicBuffer;
34 
35 
VideoTex(sp<IEvsEnumerator> pEnum,sp<IEvsCamera> pCamera,sp<StreamHandler> pStreamHandler,EGLDisplay glDisplay)36 VideoTex::VideoTex(sp<IEvsEnumerator> pEnum,
37                    sp<IEvsCamera> pCamera,
38                    sp<StreamHandler> pStreamHandler,
39                    EGLDisplay glDisplay)
40     : TexWrapper()
41     , mEnumerator(pEnum)
42     , mCamera(pCamera)
43     , mStreamHandler(pStreamHandler)
44     , mDisplay(glDisplay) {
45     // Nothing but initialization here...
46 }
47 
~VideoTex()48 VideoTex::~VideoTex() {
49     // Tell the stream to stop flowing
50     mStreamHandler->asyncStopStream();
51 
52     // Close the camera
53     mEnumerator->closeCamera(mCamera);
54 
55     // Drop our device texture image
56     if (mKHRimage != EGL_NO_IMAGE_KHR) {
57         eglDestroyImageKHR(mDisplay, mKHRimage);
58         mKHRimage = EGL_NO_IMAGE_KHR;
59     }
60 }
61 
62 
63 // Return true if the texture contents are changed
refresh()64 bool VideoTex::refresh() {
65     if (!mStreamHandler->newFrameAvailable()) {
66         // No new image has been delivered, so there's nothing to do here
67         return false;
68     }
69 
70     // If we already have an image backing us, then it's time to return it
71     if (mImageBuffer.memHandle.getNativeHandle() != nullptr) {
72         // Drop our device texture image
73         if (mKHRimage != EGL_NO_IMAGE_KHR) {
74             eglDestroyImageKHR(mDisplay, mKHRimage);
75             mKHRimage = EGL_NO_IMAGE_KHR;
76         }
77 
78         // Return it since we're done with it
79         mStreamHandler->doneWithFrame(mImageBuffer);
80     }
81 
82     // Get the new image we want to use as our contents
83     mImageBuffer = mStreamHandler->getNewFrame();
84 
85 
86     // create a GraphicBuffer from the existing handle
87     sp<GraphicBuffer> pGfxBuffer = new GraphicBuffer(mImageBuffer.memHandle,
88                                                      GraphicBuffer::CLONE_HANDLE,
89                                                      mImageBuffer.width, mImageBuffer.height,
90                                                      mImageBuffer.format, 1, // layer count
91                                                      GRALLOC_USAGE_HW_TEXTURE,
92                                                      mImageBuffer.stride);
93     if (pGfxBuffer.get() == nullptr) {
94         ALOGE("Failed to allocate GraphicBuffer to wrap image handle");
95         // Returning "true" in this error condition because we already released the
96         // previous image (if any) and so the texture may change in unpredictable ways now!
97         return true;
98     }
99 
100     // Get a GL compatible reference to the graphics buffer we've been given
101     EGLint eglImageAttributes[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE};
102     EGLClientBuffer clientBuf = static_cast<EGLClientBuffer>(pGfxBuffer->getNativeBuffer());
103     mKHRimage = eglCreateImageKHR(mDisplay, EGL_NO_CONTEXT,
104                                   EGL_NATIVE_BUFFER_ANDROID, clientBuf,
105                                   eglImageAttributes);
106     if (mKHRimage == EGL_NO_IMAGE_KHR) {
107         const char *msg = getEGLError();
108         ALOGE("error creating EGLImage: %s", msg);
109     } else {
110         // Update the texture handle we already created to refer to this gralloc buffer
111         glActiveTexture(GL_TEXTURE0);
112         glBindTexture(GL_TEXTURE_2D, glId());
113         glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, static_cast<GLeglImageOES>(mKHRimage));
114 
115         // Initialize the sampling properties (it seems the sample may not work if this isn't done)
116         // The user of this texture may very well want to set their own filtering, but we're going
117         // to pay the (minor) price of setting this up for them to avoid the dreaded "black image"
118         // if they forget.
119         // TODO:  Can we do this once for the texture ID rather than ever refresh?
120         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
121         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
122         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
123         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
124     }
125 
126     return true;
127 }
128 
129 
createVideoTexture(sp<IEvsEnumerator> pEnum,const char * evsCameraId,EGLDisplay glDisplay)130 VideoTex* createVideoTexture(sp<IEvsEnumerator> pEnum,
131                              const char* evsCameraId,
132                              EGLDisplay glDisplay) {
133     // Set up the camera to feed this texture
134     sp<IEvsCamera> pCamera = pEnum->openCamera(evsCameraId);
135     if (pCamera.get() == nullptr) {
136         ALOGE("Failed to allocate new EVS Camera interface for %s", evsCameraId);
137         return nullptr;
138     }
139 
140     // Initialize the stream that will help us update this texture's contents
141     sp<StreamHandler> pStreamHandler = new StreamHandler(pCamera);
142     if (pStreamHandler.get() == nullptr) {
143         ALOGE("failed to allocate FrameHandler");
144         return nullptr;
145     }
146 
147     // Start the video stream
148     if (!pStreamHandler->startStream()) {
149         printf("Couldn't start the camera stream (%s)\n", evsCameraId);
150         ALOGE("start stream failed for %s", evsCameraId);
151         return nullptr;
152     }
153 
154     return new VideoTex(pEnum, pCamera, pStreamHandler, glDisplay);
155 }
156