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