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