1 /*
2  * Copyright (C) 2018 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_NDEBUG 0
18 #define LOG_TAG "Codec2-InputSurfaceConnection"
19 #include <log/log.h>
20 
21 #include <codec2/hidl/1.0/InputSurfaceConnection.h>
22 
23 #include <memory>
24 #include <list>
25 #include <mutex>
26 #include <atomic>
27 
28 #include <hidl/HidlSupport.h>
29 #include <media/stagefright/bqhelper/ComponentWrapper.h>
30 #include <system/graphics.h>
31 #include <ui/GraphicBuffer.h>
32 #include <utils/Errors.h>
33 
34 #include <C2.h>
35 #include <C2AllocatorGralloc.h>
36 #include <C2BlockInternal.h>
37 #include <C2Buffer.h>
38 #include <C2Component.h>
39 #include <C2Config.h>
40 #include <C2Debug.h>
41 #include <C2PlatformSupport.h>
42 #include <C2Work.h>
43 
44 namespace hardware {
45 namespace google {
46 namespace media {
47 namespace c2 {
48 namespace V1_0 {
49 namespace utils {
50 
51 constexpr int32_t kBufferCount = 16;
52 
53 using namespace ::android;
54 using ::android::hardware::hidl_string;
55 using ::android::hardware::hidl_vec;
56 using ::android::hardware::Return;
57 
58 namespace /* unnamed */ {
59 
60 class Buffer2D : public C2Buffer {
61 public:
Buffer2D(C2ConstGraphicBlock block)62     explicit Buffer2D(C2ConstGraphicBlock block) : C2Buffer({ block }) {
63     }
64 };
65 
66 } // unnamed namespace
67 
68 struct InputSurfaceConnection::Impl : public ComponentWrapper {
Implhardware::google::media::c2::V1_0::utils::InputSurfaceConnection::Impl69     Impl(const sp<GraphicBufferSource>& source,
70          const std::shared_ptr<C2Component>& comp) :
71             mSource(source), mComp(comp), mRemoteComp(),
72             mFrameIndex(0) {
73         std::shared_ptr<C2ComponentInterface> intf = comp->intf();
74         mCompName = intf ? intf->getName() : "";
75     }
76 
Implhardware::google::media::c2::V1_0::utils::InputSurfaceConnection::Impl77     Impl(const sp<GraphicBufferSource>& source,
78          const sp<IComponent>& comp) :
79             mSource(source), mComp(), mRemoteComp(comp),
80             mFrameIndex(0) {
81         Return<void> transStatus = comp->getName(
82                 [this](const hidl_string& name) {
83                     mCompName = name.c_str();
84                 });
85         if (!transStatus.isOk()) {
86             ALOGD("getName -- Cannot obtain remote component name.");
87         }
88     }
89 
90     virtual ~Impl() = default;
91 
inithardware::google::media::c2::V1_0::utils::InputSurfaceConnection::Impl92     bool init() {
93         sp<GraphicBufferSource> source = mSource.promote();
94         if (source == nullptr) {
95             return false;
96         }
97         status_t err = source->initCheck();
98         if (err != OK) {
99             ALOGD("Impl::init -- GBS init failed: %d", err);
100             return false;
101         }
102 
103         // TODO: read settings properly from the interface
104         C2VideoSizeStreamTuning::input inputSize;
105         C2StreamUsageTuning::input usage;
106         c2_status_t c2Status = compQuery({ &inputSize, &usage },
107                                          {},
108                                          C2_MAY_BLOCK,
109                                          nullptr);
110         if (c2Status != C2_OK) {
111             ALOGD("Impl::init -- cannot query information from "
112                     "the component interface: %s.", asString(c2Status));
113             return false;
114         }
115 
116         // TODO: proper color aspect & dataspace
117         android_dataspace dataSpace = HAL_DATASPACE_BT709;
118 
119         // TODO: use the usage read from intf
120         // uint32_t grallocUsage =
121         //         C2AndroidMemoryUsage(C2MemoryUsage(usage.value)).
122         //         asGrallocUsage();
123 
124         uint32_t grallocUsage =
125                 mCompName.compare(0, 11, "c2.android.") == 0 ?
126                 GRALLOC_USAGE_SW_READ_OFTEN :
127                 GRALLOC_USAGE_HW_VIDEO_ENCODER;
128 
129         err = source->configure(
130                 this, dataSpace, kBufferCount,
131                 inputSize.width, inputSize.height,
132                 grallocUsage);
133         if (err != OK) {
134             ALOGD("Impl::init -- GBS configure failed: %d", err);
135             return false;
136         }
137         for (int32_t i = 0; i < kBufferCount; ++i) {
138             if (!source->onInputBufferAdded(i).isOk()) {
139                 ALOGD("Impl::init: populating GBS slots failed");
140                 return false;
141             }
142         }
143         if (!source->start().isOk()) {
144             ALOGD("Impl::init -- GBS start failed");
145             return false;
146         }
147         mAllocatorMutex.lock();
148         c2_status_t c2err = GetCodec2PlatformAllocatorStore()->fetchAllocator(
149                 C2AllocatorStore::PLATFORM_START + 1,  // GRALLOC
150                 &mAllocator);
151         mAllocatorMutex.unlock();
152         if (c2err != OK) {
153             ALOGD("Impl::init -- failed to fetch gralloc allocator: %d", c2err);
154             return false;
155         }
156         return true;
157     }
158 
159     // From ComponentWrapper
submitBufferhardware::google::media::c2::V1_0::utils::InputSurfaceConnection::Impl160     virtual status_t submitBuffer(
161             int32_t bufferId,
162             const sp<GraphicBuffer>& buffer,
163             int64_t timestamp,
164             int fenceFd) override {
165         ALOGV("Impl::submitBuffer -- bufferId = %d", bufferId);
166         // TODO: Use fd to construct fence
167         (void)fenceFd;
168 
169         std::shared_ptr<C2GraphicAllocation> alloc;
170         C2Handle* handle = WrapNativeCodec2GrallocHandle(
171                 buffer->handle,
172                 buffer->width, buffer->height,
173                 buffer->format, buffer->usage, buffer->stride);
174         mAllocatorMutex.lock();
175         c2_status_t err = mAllocator->priorGraphicAllocation(handle, &alloc);
176         mAllocatorMutex.unlock();
177         if (err != OK) {
178             return UNKNOWN_ERROR;
179         }
180         std::shared_ptr<C2GraphicBlock> block =
181                 _C2BlockFactory::CreateGraphicBlock(alloc);
182 
183         std::unique_ptr<C2Work> work(new C2Work);
184         work->input.flags = (C2FrameData::flags_t)0;
185         work->input.ordinal.timestamp = timestamp;
186         work->input.ordinal.frameIndex = mFrameIndex.fetch_add(
187                 1, std::memory_order_relaxed);
188         work->input.buffers.clear();
189         std::shared_ptr<C2Buffer> c2Buffer(
190                 // TODO: fence
191                 new Buffer2D(block->share(
192                         C2Rect(block->width(), block->height()), ::C2Fence())),
193                 [bufferId, src = mSource](C2Buffer* ptr) {
194                     delete ptr;
195                     sp<GraphicBufferSource> source = src.promote();
196                     if (source != nullptr) {
197                         // TODO: fence
198                         (void)source->onInputBufferEmptied(bufferId, -1);
199                     }
200                 });
201         work->input.buffers.push_back(c2Buffer);
202         work->worklets.clear();
203         work->worklets.emplace_back(new C2Worklet);
204         std::list<std::unique_ptr<C2Work>> items;
205         items.push_back(std::move(work));
206 
207         err = compQueue(&items);
208         return (err == C2_OK) ? OK : UNKNOWN_ERROR;
209     }
210 
submitEoshardware::google::media::c2::V1_0::utils::InputSurfaceConnection::Impl211     virtual status_t submitEos(int32_t /* bufferId */) override {
212         ALOGV("Impl::submitEos");
213 
214         std::unique_ptr<C2Work> work(new C2Work);
215         work->input.flags = (C2FrameData::flags_t)0;
216         work->input.ordinal.frameIndex = mFrameIndex.fetch_add(
217                 1, std::memory_order_relaxed);
218         work->input.buffers.clear();
219         work->worklets.clear();
220         work->worklets.emplace_back(new C2Worklet);
221         std::list<std::unique_ptr<C2Work>> items;
222         items.push_back(std::move(work));
223 
224         c2_status_t err = compQueue(&items);
225         return (err == C2_OK) ? OK : UNKNOWN_ERROR;
226     }
227 
dispatchDataSpaceChangedhardware::google::media::c2::V1_0::utils::InputSurfaceConnection::Impl228     void dispatchDataSpaceChanged(
229             int32_t dataSpace, int32_t aspects, int32_t pixelFormat) override {
230         // TODO
231         (void)dataSpace;
232         (void)aspects;
233         (void)pixelFormat;
234     }
235 
236 private:
compQueryhardware::google::media::c2::V1_0::utils::InputSurfaceConnection::Impl237     c2_status_t compQuery(
238             const std::vector<C2Param*> &stackParams,
239             const std::vector<C2Param::Index> &heapParamIndices,
240             c2_blocking_t mayBlock,
241             std::vector<std::unique_ptr<C2Param>>* const heapParams) {
242         std::shared_ptr<C2Component> comp = mComp.lock();
243         if (comp) {
244             std::shared_ptr<C2ComponentInterface> intf = comp->intf();
245             if (intf) {
246                 return intf->query_vb(stackParams,
247                                       heapParamIndices,
248                                       mayBlock,
249                                       heapParams);
250             } else {
251                 ALOGD("compQuery -- component does not have an interface.");
252                 return C2_BAD_STATE;
253             }
254         }
255         if (!mRemoteComp) {
256             ALOGD("compQuery -- component no longer exists.");
257             return C2_BAD_STATE;
258         }
259 
260         hidl_vec<ParamIndex> indices(
261                 stackParams.size() + heapParamIndices.size());
262         size_t numIndices = 0;
263         for (C2Param* const& stackParam : stackParams) {
264             if (!stackParam) {
265                 ALOGD("compQuery -- null stack param encountered.");
266                 continue;
267             }
268             indices[numIndices++] = static_cast<ParamIndex>(stackParam->index());
269         }
270         size_t numStackIndices = numIndices;
271         for (const C2Param::Index& index : heapParamIndices) {
272             indices[numIndices++] =
273                     static_cast<ParamIndex>(static_cast<uint32_t>(index));
274         }
275         indices.resize(numIndices);
276         if (heapParams) {
277             heapParams->reserve(heapParams->size() + numIndices);
278         }
279         c2_status_t status;
280         Return<void> transStatus = mRemoteComp->query(
281                 indices,
282                 mayBlock == C2_MAY_BLOCK,
283                 [&status, &numStackIndices, &stackParams, heapParams](
284                         Status s, const Params& p) {
285                     status = static_cast<c2_status_t>(s);
286                     if (status != C2_OK && status != C2_BAD_INDEX) {
287                         ALOGD("compQuery -- call failed: %s.", asString(status));
288                         return;
289                     }
290                     std::vector<C2Param*> paramPointers;
291                     c2_status_t parseStatus = parseParamsBlob(&paramPointers, p);
292                     if (parseStatus != C2_OK) {
293                         ALOGD("compQuery -- error while parsing params: %s.",
294                               asString(parseStatus));
295                         status = parseStatus;
296                         return;
297                     }
298                     size_t i = 0;
299                     for (auto it = paramPointers.begin();
300                             it != paramPointers.end(); ) {
301                         C2Param* paramPointer = *it;
302                         if (numStackIndices > 0) {
303                             --numStackIndices;
304                             if (!paramPointer) {
305                                 ALOGD("compQuery -- null stack param.");
306                                 ++it;
307                                 continue;
308                             }
309                             for (; i < stackParams.size() &&
310                                     !stackParams[i]; ) {
311                                 ++i;
312                             }
313                             CHECK(i < stackParams.size());
314                             if (stackParams[i]->index() !=
315                                     paramPointer->index()) {
316                                 ALOGD("compQuery -- param skipped. index = %d",
317                                       static_cast<int>(
318                                       stackParams[i]->index()));
319                                 stackParams[i++]->invalidate();
320                                 continue;
321                             }
322                             if (!stackParams[i++]->updateFrom(*paramPointer)) {
323                                 ALOGD("compQuery -- param update failed: "
324                                       "index = %d.",
325                                       static_cast<int>(paramPointer->index()));
326                             }
327                         } else {
328                             if (!paramPointer) {
329                                 ALOGD("compQuery -- null heap param.");
330                                 ++it;
331                                 continue;
332                             }
333                             if (!heapParams) {
334                                 ALOGD("compQuery -- too many stack params.");
335                                 break;
336                             }
337                             heapParams->emplace_back(C2Param::Copy(*paramPointer));
338                         }
339                         ++it;
340                     }
341                 });
342         if (!transStatus.isOk()) {
343             ALOGD("compQuery -- transaction failed.");
344             return C2_CORRUPTED;
345         }
346         return status;
347     }
348 
compQueuehardware::google::media::c2::V1_0::utils::InputSurfaceConnection::Impl349     c2_status_t compQueue(std::list<std::unique_ptr<C2Work>>* const items) {
350         std::shared_ptr<C2Component> comp = mComp.lock();
351         if (comp) {
352             return comp->queue_nb(items);
353         }
354 
355         WorkBundle workBundle;
356         Status hidlStatus = objcpy(&workBundle, *items, nullptr);
357         if (hidlStatus != Status::OK) {
358             ALOGD("compQueue -- bad input.");
359             return C2_CORRUPTED;
360         }
361         Return<Status> transStatus = mRemoteComp->queue(workBundle);
362         if (!transStatus.isOk()) {
363             ALOGD("compQueue -- transaction failed.");
364             return C2_CORRUPTED;
365         }
366         c2_status_t status =
367                 static_cast<c2_status_t>(static_cast<Status>(transStatus));
368         if (status != C2_OK) {
369             ALOGV("compQueue -- call failed: %s.", asString(status));
370         }
371         return status;
372     }
373 
374     wp<GraphicBufferSource> mSource;
375     std::weak_ptr<C2Component> mComp;
376     sp<IComponent> mRemoteComp;
377     std::string mCompName;
378 
379     // Needed for ComponentWrapper implementation
380     std::mutex mAllocatorMutex;
381     std::shared_ptr<C2Allocator> mAllocator;
382     std::atomic_uint64_t mFrameIndex;
383 };
384 
InputSurfaceConnection(const sp<GraphicBufferSource> & source,const std::shared_ptr<C2Component> & comp)385 InputSurfaceConnection::InputSurfaceConnection(
386         const sp<GraphicBufferSource>& source,
387         const std::shared_ptr<C2Component>& comp) :
388     mSource(source),
389     mImpl(new Impl(source, comp)) {
390 }
391 
InputSurfaceConnection(const sp<GraphicBufferSource> & source,const sp<IComponent> & comp)392 InputSurfaceConnection::InputSurfaceConnection(
393         const sp<GraphicBufferSource>& source,
394         const sp<IComponent>& comp) :
395     mSource(source),
396     mImpl(new Impl(source, comp)) {
397 }
398 
~InputSurfaceConnection()399 InputSurfaceConnection::~InputSurfaceConnection() {
400     if (mSource) {
401         (void)mSource->stop();
402         (void)mSource->release();
403         mSource.clear();
404     }
405     mImpl.clear();
406 }
407 
init()408 bool InputSurfaceConnection::init() {
409     mMutex.lock();
410     sp<Impl> impl = mImpl;
411     mMutex.unlock();
412 
413     if (!impl) {
414         return false;
415     }
416     return impl->init();
417 }
418 
disconnect()419 Return<Status> InputSurfaceConnection::disconnect() {
420     ALOGV("disconnect");
421     mMutex.lock();
422     if (mSource) {
423         (void)mSource->stop();
424         (void)mSource->release();
425         mSource.clear();
426     }
427     mImpl.clear();
428     mMutex.unlock();
429     ALOGV("disconnected");
430     return Status::OK;
431 }
432 
433 }  // namespace utils
434 }  // namespace V1_0
435 }  // namespace c2
436 }  // namespace media
437 }  // namespace google
438 }  // namespace hardware
439 
440