1 /*
2  * Copyright (C) 2016 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 
17 #define LOG_TAG "EvsTest"
18 
19 #include "StreamHandler.h"
20 
21 #include <stdio.h>
22 #include <string.h>
23 
24 #include <android/log.h>
25 #include <cutils/native_handle.h>
26 #include <ui/GraphicBuffer.h>
27 
28 #include <algorithm>    // std::min
29 
30 
31 // For the moment, we're assuming that the underlying EVS driver we're working with
32 // is providing 4 byte RGBx data.  This is fine for loopback testing, although
33 // real hardware is expected to provide YUV data -- most likly formatted as YV12
34 static const unsigned kBytesPerPixel = 4;   // assuming 4 byte RGBx pixels
35 
36 
StreamHandler(android::sp<IEvsCamera> pCamera,android::sp<IEvsDisplay> pDisplay)37 StreamHandler::StreamHandler(android::sp<IEvsCamera> pCamera, android::sp<IEvsDisplay> pDisplay) :
38     mCamera(pCamera),
39     mDisplay(pDisplay) {
40 }
41 
42 
startStream()43 void StreamHandler::startStream() {
44     // Mark ourselves as running
45     mLock.lock();
46     mRunning = true;
47     mLock.unlock();
48 
49     // Tell the camera to start streaming
50     mCamera->startVideoStream(this);
51 }
52 
53 
asyncStopStream()54 void StreamHandler::asyncStopStream() {
55     // Tell the camera to stop streaming.
56     // This will result in a null frame being delivered when the stream actually stops.
57     mCamera->stopVideoStream();
58 }
59 
60 
blockingStopStream()61 void StreamHandler::blockingStopStream() {
62     // Tell the stream to stop
63     asyncStopStream();
64 
65     // Wait until the stream has actually stopped
66     std::unique_lock<std::mutex> lock(mLock);
67     mSignal.wait(lock, [this](){ return !mRunning; });
68 }
69 
70 
isRunning()71 bool StreamHandler::isRunning() {
72     std::unique_lock<std::mutex> lock(mLock);
73     return mRunning;
74 }
75 
76 
getFramesReceived()77 unsigned StreamHandler::getFramesReceived() {
78     std::unique_lock<std::mutex> lock(mLock);
79     return mFramesReceived;
80 };
81 
82 
getFramesCompleted()83 unsigned StreamHandler::getFramesCompleted() {
84     std::unique_lock<std::mutex> lock(mLock);
85     return mFramesCompleted;
86 };
87 
88 
deliverFrame(const BufferDesc & bufferArg)89 Return<void> StreamHandler::deliverFrame(const BufferDesc& bufferArg) {
90     ALOGD("Received a frame from the camera (%p)", bufferArg.memHandle.getNativeHandle());
91 
92     // Local flag we use to keep track of when the stream is stopping
93     bool timeToStop = false;
94 
95     if (bufferArg.memHandle.getNativeHandle() == nullptr) {
96         // Signal that the last frame has been received and that the stream should stop
97         timeToStop = true;
98         ALOGI("End of stream signaled");
99     } else {
100         // Get the output buffer we'll use to display the imagery
101         BufferDesc tgtBuffer = {};
102         mDisplay->getTargetBuffer([&tgtBuffer]
103                                   (const BufferDesc& buff) {
104                                       tgtBuffer = buff;
105                                       ALOGD("Got output buffer (%p) with id %d cloned as (%p)",
106                                             buff.memHandle.getNativeHandle(),
107                                             tgtBuffer.bufferId,
108                                             tgtBuffer.memHandle.getNativeHandle());
109                                   }
110         );
111 
112         if (tgtBuffer.memHandle == nullptr) {
113             printf("Didn't get target buffer - frame lost\n");
114             ALOGE("Didn't get requested output buffer -- skipping this frame.");
115         } else {
116             // Copy the contents of the of buffer.memHandle into tgtBuffer
117             copyBufferContents(tgtBuffer, bufferArg);
118 
119             // TODO:  Add a bit of overlay graphics?
120             // TODO:  Use OpenGL to render from texture?
121             // NOTE:  If we mess with the frame contents, we'll need to update the frame inspection
122             //        logic in the default (test) display driver.
123 
124             // Send the target buffer back for display
125             ALOGD("Calling returnTargetBufferForDisplay (%p)",
126                   tgtBuffer.memHandle.getNativeHandle());
127             Return<EvsResult> result = mDisplay->returnTargetBufferForDisplay(tgtBuffer);
128             if (!result.isOk()) {
129                 printf("HIDL error on display buffer (%s)- frame lost\n",
130                        result.description().c_str());
131                 ALOGE("Error making the remote function call.  HIDL said %s",
132                       result.description().c_str());
133             } else if (result != EvsResult::OK) {
134                 printf("Display reported error - frame lost\n");
135                 ALOGE("We encountered error %d when returning a buffer to the display!",
136                       (EvsResult)result);
137             } else {
138                 // Everything looks good!  Keep track so tests or watch dogs can monitor progress
139                 mLock.lock();
140                 mFramesCompleted++;
141                 mLock.unlock();
142                 printf("frame OK\n");
143             }
144         }
145 
146         // Send the camera buffer back now that we're done with it
147         ALOGD("Calling doneWithFrame");
148         // TODO:  Why is it that we get a HIDL crash if we pass back the cloned buffer?
149         mCamera->doneWithFrame(bufferArg);
150 
151         ALOGD("Frame handling complete");
152     }
153 
154 
155     // Update our received frame count and notify anybody who cares that things have changed
156     mLock.lock();
157     if (timeToStop) {
158         mRunning = false;
159     } else {
160         mFramesReceived++;
161     }
162     mLock.unlock();
163     mSignal.notify_all();
164 
165 
166     return Void();
167 }
168 
169 
copyBufferContents(const BufferDesc & tgtBuffer,const BufferDesc & srcBuffer)170 bool StreamHandler::copyBufferContents(const BufferDesc& tgtBuffer,
171                                        const BufferDesc& srcBuffer) {
172     bool success = true;
173 
174     // Make sure we don't run off the end of either buffer
175     const unsigned width     = std::min(tgtBuffer.width,
176                                         srcBuffer.width);
177     const unsigned height    = std::min(tgtBuffer.height,
178                                         srcBuffer.height);
179 
180     sp<android::GraphicBuffer> tgt = new android::GraphicBuffer(
181             tgtBuffer.memHandle, android::GraphicBuffer::CLONE_HANDLE,
182             tgtBuffer.width, tgtBuffer.height, tgtBuffer.format, 1,
183             tgtBuffer.usage, tgtBuffer.stride);
184     sp<android::GraphicBuffer> src = new android::GraphicBuffer(
185             srcBuffer.memHandle, android::GraphicBuffer::CLONE_HANDLE,
186             srcBuffer.width, srcBuffer.height, srcBuffer.format, 1,
187             srcBuffer.usage, srcBuffer.stride);
188 
189     // Lock our source buffer for reading
190     unsigned char* srcPixels = nullptr;
191     src->lock(GRALLOC_USAGE_SW_READ_OFTEN, (void **) &srcPixels);
192 
193     // Lock our target buffer for writing
194     unsigned char* tgtPixels = nullptr;
195     tgt->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void **) &tgtPixels);
196 
197     if (srcPixels && tgtPixels) {
198         for (unsigned row = 0; row < height; row++) {
199             // Copy the entire row of pixel data
200             memcpy(tgtPixels, srcPixels, width * kBytesPerPixel);
201 
202             // Advance to the next row (keeping in mind that stride here is in units of pixels)
203             tgtPixels += tgtBuffer.stride * kBytesPerPixel;
204             srcPixels += srcBuffer.stride * kBytesPerPixel;
205         }
206     } else {
207         ALOGE("Failed to copy buffer contents");
208         success = false;
209     }
210 
211     if (srcPixels) {
212         src->unlock();
213     }
214     if (tgtPixels) {
215         tgt->unlock();
216     }
217 
218     return success;
219 }
220