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 (getNativeHandle(mImageBuffer) != 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 }
88
89 // Get the new image we want to use as our contents
90 mImageBuffer = dupBufferDesc(mStreamHandler->getNewFrame());
91
92 // create a GraphicBuffer from the existing handle
93 native_handle_t* nativeHandle = getNativeHandle(mImageBuffer);
94 const auto handleGuard =
95 android::base::make_scope_guard([nativeHandle] { free(nativeHandle); });
96 if (nativeHandle == nullptr) {
97 // New frame contains an invalid native handle.
98 return false;
99 }
100
101 const AHardwareBuffer_Desc* pDesc =
102 reinterpret_cast<const AHardwareBuffer_Desc*>(&mImageBuffer.buffer.description);
103 android::sp<GraphicBuffer> pGfxBuffer = // AHardwareBuffer_to_GraphicBuffer?
104 new GraphicBuffer(nativeHandle, GraphicBuffer::CLONE_HANDLE, pDesc->width,
105 pDesc->height, pDesc->format, pDesc->layers, pDesc->usage,
106 pDesc->stride);
107 if (!pGfxBuffer) {
108 LOG(ERROR) << "Failed to allocate GraphicBuffer to wrap image handle";
109 // Returning "true" in this error condition because we already released the
110 // previous image (if any) and so the texture may change in unpredictable ways now!
111 return true;
112 }
113
114 if (auto status = pGfxBuffer->initCheck(); status != android::OK) {
115 LOG(ERROR) << "Failed to initialize the graphic buffer, error = "
116 << android::statusToString(status);
117 // Same here.
118 return true;
119 }
120
121 // Get a GL compatible reference to the graphics buffer we've been given
122 EGLint eglImageAttributes[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE};
123 EGLClientBuffer clientBuf = static_cast<EGLClientBuffer>(pGfxBuffer->getNativeBuffer());
124 mKHRimage = eglCreateImageKHR(mDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, clientBuf,
125 eglImageAttributes);
126 if (mKHRimage == EGL_NO_IMAGE_KHR) {
127 const char* msg = getEGLError();
128 LOG(ERROR) << "Error creating EGLImage: " << msg;
129 } else {
130 // Update the texture handle we already created to refer to this gralloc buffer
131 glActiveTexture(GL_TEXTURE0);
132 glBindTexture(GL_TEXTURE_2D, glId());
133 glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, static_cast<GLeglImageOES>(mKHRimage));
134
135 // Initialize the sampling properties (it seems the sample may not work if this isn't done)
136 // The user of this texture may very well want to set their own filtering, but we're going
137 // to pay the (minor) price of setting this up for them to avoid the dreaded "black image"
138 // if they forget.
139 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
140 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
141 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
142 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
143 }
144
145 return true;
146 }
147
createVideoTexture(const std::shared_ptr<IEvsEnumerator> & pEnum,const char * evsCameraId,std::unique_ptr<Stream> streamCfg,EGLDisplay glDisplay,bool useExternalMemory,android_pixel_format_t format)148 VideoTex* createVideoTexture(const std::shared_ptr<IEvsEnumerator>& pEnum, const char* evsCameraId,
149 std::unique_ptr<Stream> streamCfg, EGLDisplay glDisplay,
150 bool useExternalMemory, android_pixel_format_t format) {
151 // Set up the camera to feed this texture
152 std::shared_ptr<IEvsCamera> pCamera;
153 std::shared_ptr<StreamHandler> pStreamHandler;
154 if (!streamCfg) {
155 LOG(ERROR) << "Given stream configuration is invalid.";
156 return nullptr;
157 }
158
159 if (auto status = pEnum->openCamera(evsCameraId, *streamCfg, &pCamera); !status.isOk()) {
160 LOG(ERROR) << "Failed to open a camera " << evsCameraId;
161 return nullptr;
162 }
163
164 // Initialize the stream that will help us update this texture's contents
165 pStreamHandler =
166 ndk::SharedRefBase::make<StreamHandler>(pCamera, /* numBuffers= */ 2, useExternalMemory,
167 format, streamCfg->width, streamCfg->height);
168 if (!pStreamHandler) {
169 LOG(ERROR) << "Failed to allocate FrameHandler";
170 return nullptr;
171 }
172
173 // Start the video stream
174 if (!pStreamHandler->startStream()) {
175 printf("Couldn't start the camera stream (%s)\n", evsCameraId);
176 LOG(ERROR) << "Start stream failed for " << evsCameraId;
177 return nullptr;
178 }
179
180 return new VideoTex(pEnum, pCamera, pStreamHandler, glDisplay);
181 }
182