Home | History | Annotate | Download | only in app
      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