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 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 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 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 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