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