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 "HalCamera.h"
18#include "VirtualCamera.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
32// TODO:  We need to hook up death monitoring to detect stream death so we can attempt a reconnect
33
34
35sp<VirtualCamera> HalCamera::makeVirtualCamera() {
36
37    // Create the client camera interface object
38    sp<VirtualCamera> client = new VirtualCamera(this);
39    if (client == nullptr) {
40        ALOGE("Failed to create client camera object");
41        return nullptr;
42    }
43
44    // Make sure we have enough buffers available for all our clients
45    if (!changeFramesInFlight(client->getAllowedBuffers())) {
46        // Gah!  We couldn't get enough buffers, so we can't support this client
47        // Null the pointer, dropping our reference, thus destroying the client object
48        client = nullptr;
49        return nullptr;
50    }
51
52    // Add this client to our ownership list via weak pointer
53    mClients.push_back(client);
54
55    // Return the strong pointer to the client
56    return client;
57}
58
59
60void HalCamera::disownVirtualCamera(sp<VirtualCamera> virtualCamera) {
61    // Ignore calls with null pointers
62    if (virtualCamera.get() == nullptr) {
63        ALOGW("Ignoring disownVirtualCamera call with null pointer");
64        return;
65    }
66
67    // Make sure the virtual camera's stream is stopped
68    virtualCamera->stopVideoStream();
69
70    // Remove the virtual camera from our client list
71    unsigned clientCount = mClients.size();
72    mClients.remove(virtualCamera);
73    if (clientCount != mClients.size() + 1) {
74        ALOGE("Couldn't find camera in our client list to remove it");
75    }
76    virtualCamera->shutdown();
77
78    // Recompute the number of buffers required with the target camera removed from the list
79    if (!changeFramesInFlight(0)) {
80        ALOGE("Error when trying to reduce the in flight buffer count");
81    }
82}
83
84
85bool HalCamera::changeFramesInFlight(int delta) {
86    // Walk all our clients and count their currently required frames
87    unsigned bufferCount = 0;
88    for (auto&& client :  mClients) {
89        sp<VirtualCamera> virtCam = client.promote();
90        if (virtCam != nullptr) {
91            bufferCount += virtCam->getAllowedBuffers();
92        }
93    }
94
95    // Add the requested delta
96    bufferCount += delta;
97
98    // Never drop below 1 buffer -- even if all client cameras get closed
99    if (bufferCount < 1) {
100        bufferCount = 1;
101    }
102
103    // Ask the hardware for the resulting buffer count
104    Return<EvsResult> result = mHwCamera->setMaxFramesInFlight(bufferCount);
105    bool success = (result.isOk() && result == EvsResult::OK);
106
107    // Update the size of our array of outstanding frame records
108    if (success) {
109        std::vector<FrameRecord> newRecords;
110        newRecords.reserve(bufferCount);
111
112        // Copy and compact the old records that are still active
113        for (const auto& rec : mFrames) {
114            if (rec.refCount > 0) {
115                newRecords.emplace_back(rec);
116            }
117        }
118        if (newRecords.size() > (unsigned)bufferCount) {
119            ALOGW("We found more frames in use than requested.");
120        }
121
122        mFrames.swap(newRecords);
123    }
124
125    return success;
126}
127
128
129Return<EvsResult> HalCamera::clientStreamStarting() {
130    Return<EvsResult> result = EvsResult::OK;
131
132    if (mStreamState == STOPPED) {
133        result = mHwCamera->startVideoStream(this);
134    }
135
136    return result;
137}
138
139
140void HalCamera::clientStreamEnding() {
141    // Do we still have a running client?
142    bool stillRunning = false;
143    for (auto&& client : mClients) {
144        sp<VirtualCamera> virtCam = client.promote();
145        if (virtCam != nullptr) {
146            stillRunning |= virtCam->isStreaming();
147        }
148    }
149
150    // If not, then stop the hardware stream
151    if (!stillRunning) {
152        mHwCamera->stopVideoStream();
153    }
154}
155
156
157Return<void> HalCamera::doneWithFrame(const BufferDesc& buffer) {
158    // Find this frame in our list of outstanding frames
159    unsigned i;
160    for (i=0; i<mFrames.size(); i++) {
161        if (mFrames[i].frameId == buffer.bufferId) {
162            break;
163        }
164    }
165    if (i == mFrames.size()) {
166        ALOGE("We got a frame back with an ID we don't recognize!");
167    } else {
168        // Are there still clients using this buffer?
169        mFrames[i].refCount--;
170        if (mFrames[i].refCount <= 0) {
171            // Since all our clients are done with this buffer, return it to the device layer
172            mHwCamera->doneWithFrame(buffer);
173        }
174    }
175
176    return Void();
177}
178
179
180Return<void> HalCamera::deliverFrame(const BufferDesc& buffer) {
181    // Run through all our clients and deliver this frame to any who are eligible
182    unsigned frameDeliveries = 0;
183    for (auto&& client : mClients) {
184        sp<VirtualCamera> virtCam = client.promote();
185        if (virtCam != nullptr) {
186            if (virtCam->deliverFrame(buffer)) {
187                frameDeliveries++;
188            }
189        }
190    }
191
192    if (frameDeliveries < 1) {
193        // If none of our clients could accept the frame, then return it right away
194        ALOGI("Trivially rejecting frame with no acceptances");
195        mHwCamera->doneWithFrame(buffer);
196    } else {
197        // Add an entry for this frame in our tracking list
198        unsigned i;
199        for (i=0; i<mFrames.size(); i++) {
200            if (mFrames[i].refCount == 0) {
201                break;
202            }
203        }
204        if (i == mFrames.size()) {
205            mFrames.emplace_back(buffer.bufferId);
206        } else {
207            mFrames[i].frameId = buffer.bufferId;
208        }
209        mFrames[i].refCount = frameDeliveries;
210    }
211
212    return Void();
213}
214
215} // namespace implementation
216} // namespace V1_0
217} // namespace evs
218} // namespace automotive
219} // namespace android
220