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