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