• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 #include "StreamHandler.h"
18 
19 #include <stdio.h>
20 #include <string.h>
21 
22 #include <log/log.h>
23 #include <cutils/native_handle.h>
24 
25 #include <ui/GraphicBufferAllocator.h>
26 #include <ui/GraphicBufferMapper.h>
27 
28 #include "Frame.h"
29 #include "ResourceManager.h"
30 
31 namespace android {
32 namespace automotive {
33 namespace evs {
34 namespace support {
35 
36 using ::std::lock_guard;
37 using ::std::unique_lock;
38 
StreamHandler(android::sp<IEvsCamera> pCamera)39 StreamHandler::StreamHandler(android::sp <IEvsCamera> pCamera) :
40     mCamera(pCamera),
41     mAnalyzeCallback(nullptr),
42     mAnalyzerRunning(false)
43 {
44     // We rely on the camera having at least two buffers available since we'll hold one and
45     // expect the camera to be able to capture a new image in the background.
46     pCamera->setMaxFramesInFlight(2);
47 }
48 
49 // TODO(b/130246343): investigate further to make sure the resources are cleaned
50 // up properly in the shutdown logic.
shutdown()51 void StreamHandler::shutdown()
52 {
53     // Tell the camera to stop streaming.
54     // This will result in a null frame being delivered when the stream actually stops.
55     mCamera->stopVideoStream();
56 
57     // Wait until the stream has actually stopped
58     unique_lock<mutex> lock(mLock);
59     if (mRunning) {
60         mSignal.wait(lock, [this]() { return !mRunning; });
61     }
62 
63     // At this point, the receiver thread is no longer running, so we can safely drop
64     // our remote object references so they can be freed
65     mCamera = nullptr;
66 }
67 
68 
startStream()69 bool StreamHandler::startStream() {
70     lock_guard<mutex> lock(mLock);
71 
72     if (!mRunning) {
73         // Tell the camera to start streaming
74         Return <EvsResult> result = mCamera->startVideoStream(this);
75         if (result != EvsResult::OK) {
76             return false;
77         }
78 
79         // Mark ourselves as running
80         mRunning = true;
81     }
82 
83     return true;
84 }
85 
86 
newDisplayFrameAvailable()87 bool StreamHandler::newDisplayFrameAvailable() {
88     lock_guard<mutex> lock(mLock);
89     return (mReadyBuffer >= 0);
90 }
91 
92 
getNewDisplayFrame()93 const BufferDesc& StreamHandler::getNewDisplayFrame() {
94     lock_guard<mutex> lock(mLock);
95 
96     if (mHeldBuffer >= 0) {
97         ALOGE("Ignored call for new frame while still holding the old one.");
98     } else {
99         if (mReadyBuffer < 0) {
100             ALOGE("Returning invalid buffer because we don't have any. "
101                   " Call newDisplayFrameAvailable first?");
102             mReadyBuffer = 0;   // This is a lie!
103         }
104 
105         // Move the ready buffer into the held position, and clear the ready position
106         mHeldBuffer = mReadyBuffer;
107         mReadyBuffer = -1;
108     }
109 
110     if (mRenderCallback == nullptr) {
111         return mOriginalBuffers[mHeldBuffer];
112     } else {
113         return mProcessedBuffers[mHeldBuffer];
114     }
115 }
116 
117 
doneWithFrame(const BufferDesc & buffer)118 void StreamHandler::doneWithFrame(const BufferDesc& buffer) {
119     lock_guard<mutex> lock(mLock);
120 
121     // We better be getting back the buffer we original delivered!
122     if ((mHeldBuffer < 0)
123         || (buffer.bufferId != mOriginalBuffers[mHeldBuffer].bufferId)) {
124         ALOGE("StreamHandler::doneWithFrame got an unexpected buffer!");
125         ALOGD("Held buffer id: %d, input buffer id: %d",
126               mOriginalBuffers[mHeldBuffer].bufferId, buffer.bufferId);
127         return;
128     }
129 
130     // Send the buffer back to the underlying camera
131     mCamera->doneWithFrame(mOriginalBuffers[mHeldBuffer]);
132 
133     // Clear the held position
134     mHeldBuffer = -1;
135 }
136 
137 
deliverFrame(const BufferDesc & buffer)138 Return<void> StreamHandler::deliverFrame(const BufferDesc& buffer) {
139     ALOGD("Received a frame from the camera. NativeHandle:%p, buffer id:%d",
140           buffer.memHandle.getNativeHandle(), buffer.bufferId);
141 
142     // Take the lock to protect our frame slots and running state variable
143     {
144         lock_guard <mutex> lock(mLock);
145 
146         if (buffer.memHandle.getNativeHandle() == nullptr) {
147             // Signal that the last frame has been received and the stream is stopped
148             mRunning = false;
149         } else {
150             // Do we already have a "ready" frame?
151             if (mReadyBuffer >= 0) {
152                 // Send the previously saved buffer back to the camera unused
153                 mCamera->doneWithFrame(mOriginalBuffers[mReadyBuffer]);
154 
155                 // We'll reuse the same ready buffer index
156             } else if (mHeldBuffer >= 0) {
157                 // The client is holding a buffer, so use the other slot for "on deck"
158                 mReadyBuffer = 1 - mHeldBuffer;
159             } else {
160                 // This is our first buffer, so just pick a slot
161                 mReadyBuffer = 0;
162             }
163 
164             // Save this frame until our client is interested in it
165             mOriginalBuffers[mReadyBuffer] = buffer;
166 
167             // If render callback is not null, process the frame with render
168             // callback.
169             if (mRenderCallback != nullptr) {
170                 processFrame(mOriginalBuffers[mReadyBuffer],
171                              mProcessedBuffers[mReadyBuffer]);
172             } else {
173                 ALOGI("Render callback is null in deliverFrame.");
174             }
175 
176             // If analyze callback is not null and the analyze thread is
177             // available, copy the frame and run the analyze callback in
178             // analyze thread.
179             {
180                 std::shared_lock<std::shared_mutex> analyzerLock(mAnalyzerLock);
181                 if (mAnalyzeCallback != nullptr && !mAnalyzerRunning) {
182                     copyAndAnalyzeFrame(mOriginalBuffers[mReadyBuffer]);
183                 }
184             }
185         }
186     }
187 
188     // Notify anybody who cares that things have changed
189     mSignal.notify_all();
190 
191     return Void();
192 }
193 
attachRenderCallback(BaseRenderCallback * callback)194 void StreamHandler::attachRenderCallback(BaseRenderCallback* callback) {
195     ALOGD("StreamHandler::attachRenderCallback");
196 
197     lock_guard<mutex> lock(mLock);
198 
199     if (mRenderCallback != nullptr) {
200         ALOGW("Ignored! There should only be one render callback");
201         return;
202     }
203     mRenderCallback = callback;
204 }
205 
detachRenderCallback()206 void StreamHandler::detachRenderCallback() {
207     ALOGD("StreamHandler::detachRenderCallback");
208 
209     lock_guard<mutex> lock(mLock);
210 
211     mRenderCallback = nullptr;
212 }
213 
attachAnalyzeCallback(BaseAnalyzeCallback * callback)214 void StreamHandler::attachAnalyzeCallback(BaseAnalyzeCallback* callback) {
215     ALOGD("StreamHandler::attachAnalyzeCallback");
216 
217     if (mAnalyzeCallback != nullptr) {
218         ALOGW("Ignored! There should only be one analyze callcack");
219         return;
220     }
221 
222     {
223         lock_guard<std::shared_mutex> lock(mAnalyzerLock);
224         mAnalyzeCallback = callback;
225     }
226 }
227 
detachAnalyzeCallback()228 void StreamHandler::detachAnalyzeCallback() {
229     ALOGD("StreamHandler::detachAnalyzeCallback");
230 
231     {
232         std::unique_lock<std::shared_mutex> lock(mAnalyzerLock);
233 
234         // Wait until current running analyzer ends
235         mAnalyzerSignal.wait(lock, [this] { return !mAnalyzerRunning; });
236         mAnalyzeCallback = nullptr;
237     }
238 }
239 
isSameFormat(const BufferDesc & input,const BufferDesc & output)240 bool isSameFormat(const BufferDesc& input, const BufferDesc& output) {
241     return input.width == output.width
242         && input.height == output.height
243         && input.format == output.format
244         && input.usage == output.usage
245         && input.stride == output.stride
246         && input.pixelSize == output.pixelSize;
247 }
248 
allocate(BufferDesc & buffer)249 bool allocate(BufferDesc& buffer) {
250     ALOGD("StreamHandler::allocate");
251     buffer_handle_t handle;
252     android::GraphicBufferAllocator& alloc(android::GraphicBufferAllocator::get());
253     android::status_t result = alloc.allocate(
254         buffer.width, buffer.height, buffer.format, 1, buffer.usage,
255         &handle, &buffer.stride, 0, "EvsDisplay");
256     if (result != android::NO_ERROR) {
257         ALOGE("Error %d allocating %d x %d graphics buffer", result, buffer.width,
258               buffer.height);
259         return false;
260     }
261 
262     // The reason that we have to check null for "handle" is because that the
263     // above "result" might not cover all the failure scenarios.
264     // By looking into Gralloc4.cpp (and 3, 2, as well), it turned out that if
265     // there is anything that goes wrong in the process of buffer importing (see
266     // Ln 385 in Gralloc4.cpp), the error won't be covered by the above "result"
267     // we got from "allocate" method. In other words, it means that there is
268     // still a chance that the "result" is "NO_ERROR" but the handle is nullptr
269     // (that means buffer importing failed).
270     if (!handle) {
271         ALOGE("We didn't get a buffer handle back from the allocator");
272         return false;
273     }
274 
275     buffer.memHandle = hidl_handle(handle);
276     return true;
277 }
278 
processFrame(const BufferDesc & input,BufferDesc & output)279 bool StreamHandler::processFrame(const BufferDesc& input,
280                                  BufferDesc& output) {
281     ALOGD("StreamHandler::processFrame");
282     if (!isSameFormat(input, output)
283         || output.memHandle.getNativeHandle() == nullptr) {
284         output.width = input.width;
285         output.height = input.height;
286         output.format = input.format;
287         output.usage = input.usage;
288         output.stride = input.stride;
289         output.pixelSize = input.pixelSize;
290 
291         // free the allocated output frame handle if it is not null
292         if (output.memHandle.getNativeHandle() != nullptr) {
293             GraphicBufferAllocator::get().free(output.memHandle);
294         }
295 
296         if (!allocate(output)) {
297             ALOGE("Error allocating buffer");
298             return false;
299         }
300     }
301     output.bufferId = input.bufferId;
302 
303     // Create a GraphicBuffer from the existing handle
304     sp<GraphicBuffer> inputBuffer = new GraphicBuffer(
305         input.memHandle, GraphicBuffer::CLONE_HANDLE, input.width,
306         input.height, input.format, 1,  // layer count
307         GRALLOC_USAGE_HW_TEXTURE, input.stride);
308 
309     if (inputBuffer.get() == nullptr) {
310         ALOGE("Failed to allocate GraphicBuffer to wrap image handle");
311         // Returning "true" in this error condition because we already released
312         // the previous image (if any) and so the texture may change in
313         // unpredictable ways now!
314         return false;
315     }
316 
317     // Lock the input GraphicBuffer and map it to a pointer. If we failed to
318     // lock, return false.
319     void* inputDataPtr;
320     inputBuffer->lock(
321         GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_NEVER,
322         &inputDataPtr);
323 
324     // Unlock the buffer and return if lock did not succeed.
325     if (!inputDataPtr) {
326         ALOGE("Failed to gain read access to image buffer");
327 
328         // The program reaches at here when it fails to lock the buffer. But
329         // it is still safer to unlock it. The reason is as described in "lock"
330         // method in  Gralloc.h: "The ownership of acquireFence is always
331         // transferred to the callee, even on errors."
332         // And even if the buffer was not locked, it does not harm anything
333         // given the comment for "unlock" method in IMapper.hal:
334         // "`BAD_BUFFER` if the buffer is invalid or not locked."
335         inputBuffer->unlock();
336         return false;
337     }
338 
339     // Lock the allocated buffer in output BufferDesc and map it to a pointer
340     void* outputDataPtr = nullptr;
341     android::GraphicBufferMapper& mapper = android::GraphicBufferMapper::get();
342     mapper.lock(output.memHandle,
343                 GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_SW_READ_NEVER,
344                 android::Rect(output.width, output.height),
345                 (void**)&outputDataPtr);
346 
347     // If we failed to lock the pixel buffer, return false, and unlock both
348     // input and output buffers.
349     if (!outputDataPtr) {
350         ALOGE("Failed to gain write access to image buffer");
351 
352         // Please refer to the previous "if" block for why we want to unlock
353         // the buffers even if the buffer locking fails.
354         inputBuffer->unlock();
355         mapper.unlock(output.memHandle);
356         return false;
357     }
358 
359     // Wrap the raw data and copied data, and pass them to the callback.
360     Frame inputFrame = {
361         .width = input.width,
362         .height = input.height,
363         .stride = input.stride,
364         .data = (uint8_t*)inputDataPtr
365     };
366 
367     Frame outputFrame = {
368         .width = output.width,
369         .height = output.height,
370         .stride = output.stride,
371         .data = (uint8_t*)outputDataPtr
372     };
373 
374     mRenderCallback->render(inputFrame, outputFrame);
375 
376     // Unlock the buffers after all changes to the buffer are completed.
377     inputBuffer->unlock();
378     mapper.unlock(output.memHandle);
379 
380     return true;
381 }
382 
copyAndAnalyzeFrame(const BufferDesc & input)383 bool StreamHandler::copyAndAnalyzeFrame(const BufferDesc& input) {
384     ALOGD("StreamHandler::copyAndAnalyzeFrame");
385 
386     // TODO(b/130246434): make the following into a method. Some lines are
387     // duplicated with processFrame, move them into new methods as well.
388     if (!isSameFormat(input, mAnalyzeBuffer)
389         || mAnalyzeBuffer.memHandle.getNativeHandle() == nullptr) {
390         mAnalyzeBuffer.width = input.width;
391         mAnalyzeBuffer.height = input.height;
392         mAnalyzeBuffer.format = input.format;
393         mAnalyzeBuffer.usage = input.usage;
394         mAnalyzeBuffer.stride = input.stride;
395         mAnalyzeBuffer.pixelSize = input.pixelSize;
396         mAnalyzeBuffer.bufferId = input.bufferId;
397 
398         // free the allocated output frame handle if it is not null
399         if (mAnalyzeBuffer.memHandle.getNativeHandle() != nullptr) {
400             GraphicBufferAllocator::get().free(mAnalyzeBuffer.memHandle);
401         }
402 
403         if (!allocate(mAnalyzeBuffer)) {
404             ALOGE("Error allocating buffer");
405             return false;
406         }
407     }
408 
409     // create a GraphicBuffer from the existing handle
410     sp<GraphicBuffer> inputBuffer = new GraphicBuffer(
411         input.memHandle, GraphicBuffer::CLONE_HANDLE, input.width,
412         input.height, input.format, 1,  // layer count
413         GRALLOC_USAGE_HW_TEXTURE, input.stride);
414 
415     if (inputBuffer.get() == nullptr) {
416         ALOGE("Failed to allocate GraphicBuffer to wrap image handle");
417         // Returning "true" in this error condition because we already released the
418         // previous image (if any) and so the texture may change in unpredictable
419         // ways now!
420         return false;
421     }
422 
423     // Lock the input GraphicBuffer and map it to a pointer.  If we failed to
424     // lock, return false.
425     void* inputDataPtr;
426     inputBuffer->lock(
427         GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_NEVER,
428         &inputDataPtr);
429     if (!inputDataPtr) {
430         ALOGE("Failed to gain read access to imageGraphicBuffer");
431         inputBuffer->unlock();
432         return false;
433     }
434 
435     // Lock the allocated buffer in output BufferDesc and map it to a pointer
436     void* analyzeDataPtr = nullptr;
437     android::GraphicBufferMapper::get().lock(
438         mAnalyzeBuffer.memHandle,
439         GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_SW_READ_NEVER,
440         android::Rect(mAnalyzeBuffer.width, mAnalyzeBuffer.height),
441         (void**)&analyzeDataPtr);
442 
443     // If we failed to lock the pixel buffer, return false, and unlock both
444     // input and output buffers.
445     if (!analyzeDataPtr) {
446         ALOGE("Camera failed to gain access to image buffer for analyzing");
447         return false;
448     }
449 
450     // Wrap the raw data and copied data, and pass them to the callback.
451     Frame analyzeFrame = {
452         .width = mAnalyzeBuffer.width,
453         .height = mAnalyzeBuffer.height,
454         .stride = mAnalyzeBuffer.stride,
455         .data = (uint8_t*)analyzeDataPtr,
456     };
457 
458     memcpy(analyzeDataPtr, inputDataPtr, mAnalyzeBuffer.stride * mAnalyzeBuffer.height * 4);
459 
460     // Unlock the buffers after all changes to the buffer are completed.
461     inputBuffer->unlock();
462 
463     mAnalyzerRunning = true;
464     std::thread([this, analyzeFrame]() {
465         ALOGD("StreamHandler: Analyze Thread starts");
466 
467         std::shared_lock<std::shared_mutex> lock(mAnalyzerLock);
468         if (this->mAnalyzeCallback != nullptr) {
469             this->mAnalyzeCallback->analyze(analyzeFrame);
470             android::GraphicBufferMapper::get().unlock(this->mAnalyzeBuffer.memHandle);
471         }
472         this->mAnalyzerRunning = false;
473         mAnalyzerSignal.notify_one();
474         ALOGD("StreamHandler: Analyze Thread ends");
475     }).detach();
476 
477     return true;
478 }
479 
480 }  // namespace support
481 }  // namespace evs
482 }  // namespace automotive
483 }  // namespace android
484