VirtualCamera.cpp revision 29803803b299652fc219713774181dd9a7dd8dec
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 "EvsManager" 18 19#include "VirtualCamera.h" 20#include "HalCamera.h" 21 22#include <ui/GraphicBufferAllocator.h> 23#include <ui/GraphicBufferMapper.h> 24 25 26namespace android { 27namespace automotive { 28namespace evs { 29namespace V1_0 { 30namespace implementation { 31 32 33// If the client dies before closing the camera, this is our chance to clean up... 34VirtualCamera::~VirtualCamera() { 35 // If we have been connected to a camera device we have some cleanup to do... 36 if (mHalCamera != nullptr) { 37 mStreamState = STOPPED; 38 39 if (mFramesHeld.size() > 0) { 40 ALOGW("VirtualCamera destructing with frames in flight."); 41 42 // Return to the underlying hardware camera any buffers the client was holding 43 for (auto&& heldBuffer : mFramesHeld) { 44 // Tell our parent that we're done with this buffer 45 mHalCamera->doneWithFrame(heldBuffer); 46 } 47 mFramesHeld.clear(); 48 } 49 50 // Give the underlying hardware camera the heads up that it might be time to stop 51 mHalCamera->clientStreamEnding(); 52 53 // Disconnect ourselves from the underlying hardware camera so it can adjust it's resources 54 mHalCamera->disownVirtualCamera(this); 55 } 56} 57 58 59void VirtualCamera::shutdown() { 60 mHalCamera=nullptr; 61} 62 63 64bool VirtualCamera::deliverFrame(const BufferDesc& buffer) { 65 if (buffer.memHandle == nullptr) { 66 // Warn if we got an unexpected stream termination 67 if (mStreamState != STOPPING) { 68 // TODO: Should we suicide in this case to trigger a restart of the stack? 69 ALOGW("Stream unexpectedly stopped"); 70 } 71 72 // This is the stream end marker, so send it along, then mark the stream as stopped 73 mStream->deliverFrame(buffer); 74 mStreamState = STOPPED; 75 return true; 76 } else { 77 if (mStreamState == STOPPED) { 78 // A stopped stream gets no frames 79 return false; 80 } else if (mFramesHeld.size() >= mFramesAllowed) { 81 // Indicate that we declined to send the frame to the client because they're at quota 82 ALOGI("Skipping new frame as we hold %lu of %u allowed.", 83 mFramesHeld.size(), mFramesAllowed); 84 return false; 85 } else { 86 // Keep a record of this frame so we can clean up if we have to in case of client death 87 mFramesHeld.push_back(buffer); 88 89 // Pass this buffer through to our client 90 mStream->deliverFrame(buffer); 91 return true; 92 } 93 } 94} 95 96 97// Methods from ::android::hardware::evs::V1_0::IEvsCamera follow. 98Return<void> VirtualCamera::getId(getId_cb id_cb) { 99 // Straight pass through to hardware layer 100 return mHalCamera->getHwCamera()->getId(id_cb); 101} 102 103 104Return<EvsResult> VirtualCamera::setMaxFramesInFlight(uint32_t bufferCount) { 105 // How many buffers are we trying to add (or remove if negative) 106 int bufferCountChange = bufferCount - mFramesAllowed; 107 108 // Ask our parent for more buffers 109 bool result = mHalCamera->changeFramesInFlight(bufferCountChange); 110 if (!result) { 111 ALOGE("Failed to change buffer count by %d to %d", bufferCountChange, bufferCount); 112 return EvsResult::BUFFER_NOT_AVAILABLE; 113 } 114 115 // Update our notion of how many frames we're allowed 116 mFramesAllowed = bufferCount; 117 return EvsResult::OK; 118} 119 120 121Return<EvsResult> VirtualCamera::startVideoStream(const ::android::sp<IEvsCameraStream>& stream) { 122 // We only support a single stream at a time 123 if (mStreamState != STOPPED) { 124 ALOGE("ignoring startVideoStream call when a stream is already running."); 125 return EvsResult::STREAM_ALREADY_RUNNING; 126 } 127 128 // Validate our held frame count is starting out at zero as we expect 129 assert(mFramesHeld.size() == 0); 130 131 // Record the user's callback for use when we have a frame ready 132 mStream = stream; 133 mStreamState = RUNNING; 134 135 // Tell the underlying camera hardware that we want to stream 136 Return<EvsResult> result = mHalCamera->clientStreamStarting(); 137 if ((!result.isOk()) || (result != EvsResult::OK)) { 138 // If we failed to start the underlying stream, then we're not actually running 139 mStream = nullptr; 140 mStreamState = STOPPED; 141 return EvsResult::UNDERLYING_SERVICE_ERROR; 142 } 143 144 // TODO: Detect and exit if we encounter a stalled stream or unresponsive driver? 145 // Consider using a timer and watching for frame arrival? 146 147 return EvsResult::OK; 148} 149 150 151Return<void> VirtualCamera::doneWithFrame(const BufferDesc& buffer) { 152 if (buffer.memHandle == nullptr) { 153 ALOGE("ignoring doneWithFrame called with invalid handle"); 154 } else { 155 // Find this buffer in our "held" list 156 auto it = mFramesHeld.begin(); 157 while (it != mFramesHeld.end()) { 158 if (it->bufferId == buffer.bufferId) { 159 // found it! 160 break; 161 } 162 ++it; 163 } 164 if (it == mFramesHeld.end()) { 165 // We should always find the frame in our "held" list 166 ALOGE("Ignoring doneWithFrame called with unrecognized frameID %d", buffer.bufferId); 167 } else { 168 // Take this frame out of our "held" list 169 mFramesHeld.erase(it); 170 171 // Tell our parent that we're done with this buffer 172 mHalCamera->doneWithFrame(buffer); 173 } 174 } 175 176 return Void(); 177} 178 179 180Return<void> VirtualCamera::stopVideoStream() { 181 if (mStreamState == RUNNING) { 182 // Tell the frame delivery pipeline we don't want any more frames 183 mStreamState = STOPPING; 184 185 // Deliver an empty frame to close out the frame stream 186 BufferDesc nullBuff = {}; 187 auto result = mStream->deliverFrame(nullBuff); 188 if (!result.isOk()) { 189 ALOGE("Error delivering end of stream marker"); 190 } 191 192 // Since we are single threaded, no frame can be delivered while this function is running, 193 // so we can go directly to the STOPPED state here on the server. 194 // Note, however, that there still might be frames already queued that client will see 195 // after returning from the client side of this call. 196 mStreamState = STOPPED; 197 198 // Give the underlying hardware camera the heads up that it might be time to stop 199 mHalCamera->clientStreamEnding(); 200 } 201 202 return Void(); 203} 204 205 206Return<int32_t> VirtualCamera::getExtendedInfo(uint32_t opaqueIdentifier) { 207 // Pass straight through to the hardware device 208 return mHalCamera->getHwCamera()->getExtendedInfo(opaqueIdentifier); 209} 210 211 212Return<EvsResult> VirtualCamera::setExtendedInfo(uint32_t opaqueIdentifier, int32_t opaqueValue) { 213 // Pass straight through to the hardware device 214 // TODO: Should we restrict access to this entry point somehow? 215 return mHalCamera->getHwCamera()->setExtendedInfo(opaqueIdentifier, opaqueValue); 216} 217 218} // namespace implementation 219} // namespace V1_0 220} // namespace evs 221} // namespace automotive 222} // namespace android 223