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