blob: 28da6dffbd9b5c02ea515f9591a1fde468d01ffa [file] [log] [blame]
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "StreamHandler.h"
#include <stdio.h>
#include <string.h>
#include <log/log.h>
#include <cutils/native_handle.h>
StreamHandler::StreamHandler(android::sp <IEvsCamera> pCamera) :
mCamera(pCamera)
{
// We rely on the camera having at least two buffers available since we'll hold one and
// expect the camera to be able to capture a new image in the background.
pCamera->setMaxFramesInFlight(2);
}
void StreamHandler::shutdown()
{
// Make sure we're not still streaming
blockingStopStream();
// At this point, the receiver thread is no longer running, so we can safely drop
// our remote object references so they can be freed
mCamera = nullptr;
}
bool StreamHandler::startStream() {
std::unique_lock<std::mutex> lock(mLock);
if (!mRunning) {
// Tell the camera to start streaming
Return <EvsResult> result = mCamera->startVideoStream(this);
if (result != EvsResult::OK) {
return false;
}
// Mark ourselves as running
mRunning = true;
}
return true;
}
void StreamHandler::asyncStopStream() {
// Tell the camera to stop streaming.
// This will result in a null frame being delivered when the stream actually stops.
mCamera->stopVideoStream();
}
void StreamHandler::blockingStopStream() {
// Tell the stream to stop
asyncStopStream();
// Wait until the stream has actually stopped
std::unique_lock<std::mutex> lock(mLock);
if (mRunning) {
mSignal.wait(lock, [this]() { return !mRunning; });
}
}
bool StreamHandler::isRunning() {
std::unique_lock<std::mutex> lock(mLock);
return mRunning;
}
bool StreamHandler::newFrameAvailable() {
std::unique_lock<std::mutex> lock(mLock);
return (mReadyBuffer >= 0);
}
const BufferDesc& StreamHandler::getNewFrame() {
std::unique_lock<std::mutex> lock(mLock);
if (mHeldBuffer >= 0) {
ALOGE("Ignored call for new frame while still holding the old one.");
} else {
if (mReadyBuffer < 0) {
ALOGE("Returning invalid buffer because we don't have any. Call newFrameAvailable first?");
mReadyBuffer = 0; // This is a lie!
}
// Move the ready buffer into the held position, and clear the ready position
mHeldBuffer = mReadyBuffer;
mReadyBuffer = -1;
}
return mBuffers[mHeldBuffer];
}
void StreamHandler::doneWithFrame(const BufferDesc& buffer) {
std::unique_lock<std::mutex> lock(mLock);
// We better be getting back the buffer we original delivered!
if ((mHeldBuffer < 0) || (buffer.bufferId != mBuffers[mHeldBuffer].bufferId)) {
ALOGE("StreamHandler::doneWithFrame got an unexpected buffer!");
}
// Send the buffer back to the underlying camera
mCamera->doneWithFrame(mBuffers[mHeldBuffer]);
// Clear the held position
mHeldBuffer = -1;
}
Return<void> StreamHandler::deliverFrame(const BufferDesc& buffer) {
ALOGD("Received a frame from the camera (%p)", buffer.memHandle.getNativeHandle());
// Take the lock to protect our frame slots and running state variable
{
std::unique_lock <std::mutex> lock(mLock);
if (buffer.memHandle.getNativeHandle() == nullptr) {
// Signal that the last frame has been received and the stream is stopped
mRunning = false;
} else {
// Do we already have a "ready" frame?
if (mReadyBuffer >= 0) {
// Send the previously saved buffer back to the camera unused
mCamera->doneWithFrame(mBuffers[mReadyBuffer]);
// We'll reuse the same ready buffer index
} else if (mHeldBuffer >= 0) {
// The client is holding a buffer, so use the other slot for "on deck"
mReadyBuffer = 1 - mHeldBuffer;
} else {
// This is our first buffer, so just pick a slot
mReadyBuffer = 0;
}
// Save this frame until our client is interested in it
mBuffers[mReadyBuffer] = buffer;
}
}
// Notify anybody who cares that things have changed
mSignal.notify_all();
return Void();
}