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