xref: /aosp_15_r20/frameworks/av/media/codec2/components/cmds/codec2.cpp (revision ec779b8e0859a360c3d303172224686826e6e0e1)
1 /*
2  * Copyright (C) 2017 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 <inttypes.h>
18 #include <fcntl.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <sys/time.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 
25 #include <thread>
26 
27 //#define LOG_NDEBUG 0
28 #define LOG_TAG "codec2"
29 #include <log/log.h>
30 
31 #include <binder/IServiceManager.h>
32 #include <binder/ProcessState.h>
33 #include <datasource/DataSourceFactory.h>
34 #include <media/DataSource.h>
35 #include <mediadrm/ICrypto.h>
36 #include <media/IMediaHTTPService.h>
37 #include <media/stagefright/MediaSource.h>
38 #include <media/stagefright/foundation/ABuffer.h>
39 #include <media/stagefright/foundation/ALooper.h>
40 #include <media/stagefright/foundation/AMessage.h>
41 #include <media/stagefright/foundation/AUtils.h>
42 #include <media/stagefright/MediaDefs.h>
43 #include <media/stagefright/MediaErrors.h>
44 #include <media/stagefright/MediaExtractorFactory.h>
45 #include <media/stagefright/MetaData.h>
46 #include <media/stagefright/Utils.h>
47 
48 #include <gui/GLConsumer.h>
49 #include <gui/Surface.h>
50 #include <gui/SurfaceComposerClient.h>
51 
52 #include <C2AllocatorGralloc.h>
53 #include <C2Buffer.h>
54 #include <C2BufferPriv.h>
55 #include <C2Component.h>
56 #include <C2Config.h>
57 #include <C2Debug.h>
58 #include <C2PlatformSupport.h>
59 #include <C2Work.h>
60 
61 using namespace android;
62 using namespace std::chrono_literals;
63 
64 namespace {
65 
66 class LinearBuffer : public C2Buffer {
67 public:
LinearBuffer(const std::shared_ptr<C2LinearBlock> & block)68     explicit LinearBuffer(const std::shared_ptr<C2LinearBlock> &block)
69         : C2Buffer({ block->share(block->offset(), block->size(), ::C2Fence()) }) {}
70 };
71 
72 class Listener;
73 
74 class SimplePlayer {
75 public:
76     SimplePlayer();
77     ~SimplePlayer();
78 
79     void onWorkDone(std::weak_ptr<C2Component> component,
80                     std::list<std::unique_ptr<C2Work>> workItems);
81     void onTripped(std::weak_ptr<C2Component> component,
82                    std::vector<std::shared_ptr<C2SettingResult>> settingResult);
83     void onError(std::weak_ptr<C2Component> component, uint32_t errorCode);
84 
85     void play(const sp<IMediaSource> &source);
86 
87 private:
88     typedef std::unique_lock<std::mutex> ULock;
89 
90     std::shared_ptr<Listener> mListener;
91     std::shared_ptr<C2Component> mComponent;
92 
93     sp<SurfaceListener> mSurfaceListener;
94 
95     std::atomic_int mLinearPoolId;
96 
97     std::shared_ptr<C2Allocator> mAllocIon;
98     std::shared_ptr<C2BlockPool> mLinearPool;
99 
100     std::mutex mQueueLock;
101     std::condition_variable mQueueCondition;
102     std::list<std::unique_ptr<C2Work>> mWorkQueue;
103 
104     std::mutex mProcessedLock;
105     std::condition_variable mProcessedCondition;
106     std::list<std::unique_ptr<C2Work>> mProcessedWork;
107 
108     sp<Surface> mSurface;
109     sp<SurfaceComposerClient> mComposerClient;
110     sp<SurfaceControl> mControl;
111 };
112 
113 class Listener : public C2Component::Listener {
114 public:
Listener(SimplePlayer * thiz)115     explicit Listener(SimplePlayer *thiz) : mThis(thiz) {}
116     virtual ~Listener() = default;
117 
onWorkDone_nb(std::weak_ptr<C2Component> component,std::list<std::unique_ptr<C2Work>> workItems)118     virtual void onWorkDone_nb(std::weak_ptr<C2Component> component,
119                             std::list<std::unique_ptr<C2Work>> workItems) override {
120         mThis->onWorkDone(component, std::move(workItems));
121     }
122 
onTripped_nb(std::weak_ptr<C2Component> component,std::vector<std::shared_ptr<C2SettingResult>> settingResult)123     virtual void onTripped_nb(std::weak_ptr<C2Component> component,
124                            std::vector<std::shared_ptr<C2SettingResult>> settingResult) override {
125         mThis->onTripped(component, settingResult);
126     }
127 
onError_nb(std::weak_ptr<C2Component> component,uint32_t errorCode)128     virtual void onError_nb(std::weak_ptr<C2Component> component,
129                          uint32_t errorCode) override {
130         mThis->onError(component, errorCode);
131     }
132 
133 private:
134     SimplePlayer * const mThis;
135 };
136 
137 
SimplePlayer()138 SimplePlayer::SimplePlayer()
139     : mListener(new Listener(this)),
140       mSurfaceListener(new StubSurfaceListener),
141       mLinearPoolId(C2BlockPool::PLATFORM_START),
142       mComposerClient(new SurfaceComposerClient) {
143     CHECK_EQ(mComposerClient->initCheck(), (status_t)OK);
144 
145     std::shared_ptr<C2AllocatorStore> store = GetCodec2PlatformAllocatorStore();
146     CHECK_EQ(store->fetchAllocator(C2AllocatorStore::DEFAULT_LINEAR, &mAllocIon), C2_OK);
147     mLinearPool = std::make_shared<C2PooledBlockPool>(mAllocIon, mLinearPoolId++);
148 
149     mControl = mComposerClient->createSurface(
150             String8("A Surface"),
151             1280,
152             800,
153             HAL_PIXEL_FORMAT_YV12);
154             //PIXEL_FORMAT_RGB_565);
155 
156     CHECK(mControl != nullptr);
157     CHECK(mControl->isValid());
158 
159     SurfaceComposerClient::Transaction{}
160             .setLayer(mControl, INT_MAX)
161             .show(mControl)
162             .apply();
163 
164     mSurface = mControl->getSurface();
165     CHECK(mSurface != nullptr);
166     mSurface->connect(NATIVE_WINDOW_API_CPU, mSurfaceListener);
167 }
168 
~SimplePlayer()169 SimplePlayer::~SimplePlayer() {
170     mComposerClient->dispose();
171 }
172 
onWorkDone(std::weak_ptr<C2Component> component,std::list<std::unique_ptr<C2Work>> workItems)173 void SimplePlayer::onWorkDone(
174         std::weak_ptr<C2Component> component, std::list<std::unique_ptr<C2Work>> workItems) {
175     ALOGV("SimplePlayer::onWorkDone");
176     (void) component;
177     ULock l(mProcessedLock);
178     for (auto & item : workItems) {
179         mProcessedWork.push_back(std::move(item));
180     }
181     mProcessedCondition.notify_all();
182 }
183 
onTripped(std::weak_ptr<C2Component> component,std::vector<std::shared_ptr<C2SettingResult>> settingResult)184 void SimplePlayer::onTripped(
185         std::weak_ptr<C2Component> component,
186         std::vector<std::shared_ptr<C2SettingResult>> settingResult) {
187     (void) component;
188     (void) settingResult;
189     // TODO
190 }
191 
onError(std::weak_ptr<C2Component> component,uint32_t errorCode)192 void SimplePlayer::onError(std::weak_ptr<C2Component> component, uint32_t errorCode) {
193     (void) component;
194     (void) errorCode;
195     // TODO
196 }
197 
play(const sp<IMediaSource> & source)198 void SimplePlayer::play(const sp<IMediaSource> &source) {
199     ALOGV("SimplePlayer::play");
200     sp<AMessage> format;
201     (void) convertMetaDataToMessage(source->getFormat(), &format);
202 
203     sp<ABuffer> csd0, csd1;
204     format->findBuffer("csd-0", &csd0);
205     format->findBuffer("csd-1", &csd1);
206 
207     status_t err = source->start();
208 
209     if (err != OK) {
210         fprintf(stderr, "source returned error %d (0x%08x)\n", err, err);
211         return;
212     }
213 
214     std::shared_ptr<C2ComponentStore> store = GetCodec2PlatformComponentStore();
215     std::shared_ptr<C2Component> component;
216     (void)store->createComponent("c2.android.avc.decoder", &component);
217 
218     (void)component->setListener_vb(mListener, C2_DONT_BLOCK);
219     std::unique_ptr<C2PortBlockPoolsTuning::output> pools =
220         C2PortBlockPoolsTuning::output::AllocUnique({ (uint64_t)C2BlockPool::BASIC_GRAPHIC });
221     std::vector<std::unique_ptr<C2SettingResult>> result;
222     (void)component->intf()->config_vb({pools.get()}, C2_DONT_BLOCK, &result);
223     component->start();
224 
225     for (int i = 0; i < 8; ++i) {
226         mWorkQueue.emplace_back(new C2Work);
227     }
228 
229     std::atomic_bool running(true);
230     std::thread surfaceThread([this, &running]() {
231         const sp<IGraphicBufferProducer> &igbp = mSurface->getIGraphicBufferProducer();
232         while (running) {
233             std::unique_ptr<C2Work> work;
234             {
235                 ULock l(mProcessedLock);
236                 if (mProcessedWork.empty()) {
237                     mProcessedCondition.wait_for(l, 100ms);
238                     if (mProcessedWork.empty()) {
239                         continue;
240                     }
241                 }
242                 work.swap(mProcessedWork.front());
243                 mProcessedWork.pop_front();
244             }
245             int slot;
246             sp<Fence> fence;
247             ALOGV("Render: Frame #%lld", work->worklets.front()->output.ordinal.frameIndex.peekll());
248             const std::shared_ptr<C2Buffer> &output = work->worklets.front()->output.buffers[0];
249             if (output) {
250                 const C2ConstGraphicBlock block = output->data().graphicBlocks().front();
251                 native_handle_t *grallocHandle = UnwrapNativeCodec2GrallocHandle(block.handle());
252                 sp<GraphicBuffer> buffer(new GraphicBuffer(
253                         grallocHandle,
254                         GraphicBuffer::CLONE_HANDLE,
255                         block.width(),
256                         block.height(),
257                         HAL_PIXEL_FORMAT_YV12,
258                         1,
259                         (uint64_t)GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
260                         block.width()));
261                 native_handle_delete(grallocHandle);
262 
263                 status_t err = igbp->attachBuffer(&slot, buffer);
264 
265                 IGraphicBufferProducer::QueueBufferInput qbi(
266                         (work->worklets.front()->output.ordinal.timestamp * 1000ll).peekll(),
267                         false,
268                         HAL_DATASPACE_UNKNOWN,
269                         Rect(block.width(), block.height()),
270                         NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW,
271                         0,
272                         Fence::NO_FENCE,
273                         0);
274                 IGraphicBufferProducer::QueueBufferOutput qbo;
275                 err = igbp->queueBuffer(slot, qbi, &qbo);
276             }
277 
278             work->input.buffers.clear();
279             work->worklets.clear();
280 
281             ULock l(mQueueLock);
282             mWorkQueue.push_back(std::move(work));
283             mQueueCondition.notify_all();
284         }
285         ALOGV("render loop finished");
286     });
287 
288     long numFrames = 0;
289     mLinearPool.reset(new C2PooledBlockPool(mAllocIon, mLinearPoolId++));
290 
291     for (;;) {
292         size_t size = 0u;
293         void *data = nullptr;
294         int64_t timestamp = 0u;
295         MediaBufferBase *buffer = nullptr;
296         sp<ABuffer> csd;
297         if (csd0 != nullptr) {
298             csd = csd0;
299             csd0 = nullptr;
300         } else if (csd1 != nullptr) {
301             csd = csd1;
302             csd1 = nullptr;
303         } else {
304             status_t err = source->read(&buffer);
305             if (err != OK) {
306                 CHECK(buffer == nullptr);
307 
308                 if (err == INFO_FORMAT_CHANGED) {
309                     continue;
310                 }
311 
312                 break;
313             }
314             MetaDataBase &meta = buffer->meta_data();
315             CHECK(meta.findInt64(kKeyTime, &timestamp));
316 
317             size = buffer->size();
318             data = buffer->data();
319         }
320 
321         if (csd != nullptr) {
322             size = csd->size();
323             data = csd->data();
324         }
325 
326         // Prepare C2Work
327 
328         std::unique_ptr<C2Work> work;
329         while (!work) {
330             ULock l(mQueueLock);
331             if (!mWorkQueue.empty()) {
332                 work.swap(mWorkQueue.front());
333                 mWorkQueue.pop_front();
334             } else {
335                 mQueueCondition.wait_for(l, 100ms);
336             }
337         }
338         work->input.flags = (C2FrameData::flags_t)0;
339         work->input.ordinal.timestamp = timestamp;
340         work->input.ordinal.frameIndex = numFrames;
341 
342         std::shared_ptr<C2LinearBlock> block;
343         mLinearPool->fetchLinearBlock(
344                 size,
345                 { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE },
346                 &block);
347         C2WriteView view = block->map().get();
348         if (view.error() != C2_OK) {
349             fprintf(stderr, "C2LinearBlock::map() failed : %d", view.error());
350             break;
351         }
352         memcpy(view.base(), data, size);
353 
354         work->input.buffers.clear();
355         work->input.buffers.emplace_back(new LinearBuffer(block));
356         work->worklets.clear();
357         work->worklets.emplace_back(new C2Worklet);
358 
359         std::list<std::unique_ptr<C2Work>> items;
360         items.push_back(std::move(work));
361 
362         ALOGV("Frame #%ld size = %zu", numFrames, size);
363         // DO THE DECODING
364         component->queue_nb(&items);
365 
366         if (buffer) {
367             buffer->release();
368             buffer = nullptr;
369         }
370 
371         ++numFrames;
372     }
373     ALOGV("main loop finished");
374     source->stop();
375     running.store(false);
376     surfaceThread.join();
377 
378     component->release();
379     printf("\n");
380 }
381 
382 }  // namespace
383 
usage(const char * me)384 static void usage(const char *me) {
385     fprintf(stderr, "usage: %s [options] [input_filename]\n", me);
386     fprintf(stderr, "       -h(elp)\n");
387 }
388 
main(int argc,char ** argv)389 int main(int argc, char **argv) {
390     android::ProcessState::self()->startThreadPool();
391 
392     int res;
393     while ((res = getopt(argc, argv, "h")) >= 0) {
394         switch (res) {
395             case 'h':
396             default:
397             {
398                 usage(argv[0]);
399                 exit(1);
400                 break;
401             }
402         }
403     }
404 
405     argc -= optind;
406     argv += optind;
407 
408     if (argc < 1) {
409         fprintf(stderr, "No input file specified\n");
410         return 1;
411     }
412 
413     status_t err = OK;
414     SimplePlayer player;
415 
416     for (int k = 0; k < argc && err == OK; ++k) {
417         const char *filename = argv[k];
418 
419         sp<DataSource> dataSource =
420             DataSourceFactory::getInstance()->CreateFromURI(nullptr /* httpService */, filename);
421 
422         if (strncasecmp(filename, "sine:", 5) && dataSource == nullptr) {
423             fprintf(stderr, "Unable to create data source.\n");
424             return 1;
425         }
426 
427         Vector<sp<IMediaSource> > mediaSources;
428         sp<IMediaSource> mediaSource;
429 
430         sp<IMediaExtractor> extractor = MediaExtractorFactory::Create(dataSource);
431 
432         if (extractor == nullptr) {
433             fprintf(stderr, "could not create extractor.\n");
434             return -1;
435         }
436 
437         sp<MetaData> meta = extractor->getMetaData();
438 
439         if (meta != nullptr) {
440             const char *mime;
441             if (!meta->findCString(kKeyMIMEType, &mime)) {
442                 fprintf(stderr, "extractor did not provide MIME type.\n");
443                 return -1;
444             }
445         }
446 
447         size_t numTracks = extractor->countTracks();
448 
449         size_t i;
450         for (i = 0; i < numTracks; ++i) {
451             meta = extractor->getTrackMetaData(i, 0);
452 
453             if (meta == nullptr) {
454                 break;
455             }
456             const char *mime;
457             meta->findCString(kKeyMIMEType, &mime);
458 
459             // TODO: allowing AVC only for the time being
460             if (!strncasecmp(mime, "video/avc", 9)) {
461                 break;
462             }
463 
464             meta = nullptr;
465         }
466 
467         if (meta == nullptr) {
468             fprintf(stderr, "No AVC track found.\n");
469             return -1;
470         }
471 
472         mediaSource = extractor->getTrack(i);
473         if (mediaSource == nullptr) {
474             fprintf(stderr, "skip NULL track %zu, total tracks %zu.\n", i, numTracks);
475             return -1;
476         }
477 
478         player.play(mediaSource);
479     }
480 
481     return 0;
482 }
483