| /* |
| * 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 |