/*
* Copyright (C) 2016 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 "HalCamera.h"
#include "VirtualCamera.h"
#include "Enumerator.h"
#include <ui/GraphicBufferAllocator.h>
#include <ui/GraphicBufferMapper.h>
namespace android {
namespace automotive {
namespace evs {
namespace V1_0 {
namespace implementation {
// TODO: We need to hook up death monitoring to detect stream death so we can attempt a reconnect
sp<VirtualCamera> HalCamera::makeVirtualCamera() {
// Create the client camera interface object
sp<VirtualCamera> client = new VirtualCamera(this);
if (client == nullptr) {
ALOGE("Failed to create client camera object");
return nullptr;
}
// Make sure we have enough buffers available for all our clients
if (!changeFramesInFlight(client->getAllowedBuffers())) {
// Gah! We couldn't get enough buffers, so we can't support this client
// Null the pointer, dropping our reference, thus destroying the client object
client = nullptr;
return nullptr;
}
// Add this client to our ownership list via weak pointer
mClients.push_back(client);
// Return the strong pointer to the client
return client;
}
void HalCamera::disownVirtualCamera(sp<VirtualCamera> virtualCamera) {
// Ignore calls with null pointers
if (virtualCamera.get() == nullptr) {
ALOGW("Ignoring disownVirtualCamera call with null pointer");
return;
}
// Make sure the virtual camera's stream is stopped
virtualCamera->stopVideoStream();
// Remove the virtual camera from our client list
unsigned clientCount = mClients.size();
mClients.remove(virtualCamera);
if (clientCount != mClients.size() + 1) {
ALOGE("Couldn't find camera in our client list to remove it");
}
virtualCamera->shutdown();
// Recompute the number of buffers required with the target camera removed from the list
if (!changeFramesInFlight(0)) {
ALOGE("Error when trying to reduce the in flight buffer count");
}
}
bool HalCamera::changeFramesInFlight(int delta) {
// Walk all our clients and count their currently required frames
unsigned bufferCount = 0;
for (auto&& client : mClients) {
sp<VirtualCamera> virtCam = client.promote();
if (virtCam != nullptr) {
bufferCount += virtCam->getAllowedBuffers();
}
}
// Add the requested delta
bufferCount += delta;
// Never drop below 1 buffer -- even if all client cameras get closed
if (bufferCount < 1) {
bufferCount = 1;
}
// Ask the hardware for the resulting buffer count
Return<EvsResult> result = mHwCamera->setMaxFramesInFlight(bufferCount);
bool success = (result.isOk() && result == EvsResult::OK);
// Update the size of our array of outstanding frame records
if (success) {
std::vector<FrameRecord> newRecords;
newRecords.reserve(bufferCount);
// Copy and compact the old records that are still active
for (const auto& rec : mFrames) {
if (rec.refCount > 0) {
newRecords.emplace_back(rec);
}
}
if (newRecords.size() > (unsigned)bufferCount) {
ALOGW("We found more frames in use than requested.");
}
mFrames.swap(newRecords);
}
return success;
}
Return<EvsResult> HalCamera::clientStreamStarting() {
Return<EvsResult> result = EvsResult::OK;
if (mStreamState == STOPPED) {
mStreamState = RUNNING;
result = mHwCamera->startVideoStream(this);
}
return result;
}
void HalCamera::clientStreamEnding() {
// Do we still have a running client?
bool stillRunning = false;
for (auto&& client : mClients) {
sp<VirtualCamera> virtCam = client.promote();
if (virtCam != nullptr) {
stillRunning |= virtCam->isStreaming();
}
}
// If not, then stop the hardware stream
if (!stillRunning) {
mStreamState = STOPPED;
mHwCamera->stopVideoStream();
}
}
Return<void> HalCamera::doneWithFrame(const BufferDesc& buffer) {
// Find this frame in our list of outstanding frames
unsigned i;
for (i=0; i<mFrames.size(); i++) {
if (mFrames[i].frameId == buffer.bufferId) {
break;
}
}
if (i == mFrames.size()) {
ALOGE("We got a frame back with an ID we don't recognize!");
} else {
// Are there still clients using this buffer?
mFrames[i].refCount--;
if (mFrames[i].refCount <= 0) {
// Since all our clients are done with this buffer, return it to the device layer
mHwCamera->doneWithFrame(buffer);
}
}
return Void();
}
Return<void> HalCamera::deliverFrame(const BufferDesc& buffer) {
// Run through all our clients and deliver this frame to any who are eligible
unsigned frameDeliveries = 0;
for (auto&& client : mClients) {
sp<VirtualCamera> virtCam = client.promote();
if (virtCam != nullptr) {
if (virtCam->deliverFrame(buffer)) {
frameDeliveries++;
}
}
}
if (frameDeliveries < 1) {
// If none of our clients could accept the frame, then return it right away
ALOGI("Trivially rejecting frame with no acceptances");
mHwCamera->doneWithFrame(buffer);
} else {
// Add an entry for this frame in our tracking list
unsigned i;
for (i=0; i<mFrames.size(); i++) {
if (mFrames[i].refCount == 0) {
break;
}
}
if (i == mFrames.size()) {
mFrames.emplace_back(buffer.bufferId);
} else {
mFrames[i].frameId = buffer.bufferId;
}
mFrames[i].refCount = frameDeliveries;
}
return Void();
}
} // namespace implementation
} // namespace V1_0
} // namespace evs
} // namespace automotive
} // namespace android