blob: ca65aa2395067cb134ee462afec7954c18b2e1ef [file] [log] [blame]
Pawin Vongmasa36653902018-11-15 00:10:25 -08001/*
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>
Marco Nelissenfa8be7d2019-09-23 12:15:57 -070033#include <datasource/DataSourceFactory.h>
Pawin Vongmasa36653902018-11-15 00:10:25 -080034#include <media/DataSource.h>
Marco Nelissen55b259c2019-09-27 10:21:55 -070035#include <mediadrm/ICrypto.h>
Pawin Vongmasa36653902018-11-15 00:10:25 -080036#include <media/IMediaHTTPService.h>
Marco Nelissen7291da62019-12-17 13:01:55 -080037#include <media/stagefright/MediaSource.h>
Pawin Vongmasa36653902018-11-15 00:10:25 -080038#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>
Pawin Vongmasa36653902018-11-15 00:10:25 -080042#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>
Pawin Vongmasa36653902018-11-15 00:10:25 -080049#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
61using namespace android;
62using namespace std::chrono_literals;
63
64namespace {
65
66class LinearBuffer : public C2Buffer {
67public:
68 explicit LinearBuffer(const std::shared_ptr<C2LinearBlock> &block)
69 : C2Buffer({ block->share(block->offset(), block->size(), ::C2Fence()) }) {}
70};
71
72class Listener;
73
74class SimplePlayer {
75public:
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
87private:
88 typedef std::unique_lock<std::mutex> ULock;
89
90 std::shared_ptr<Listener> mListener;
91 std::shared_ptr<C2Component> mComponent;
92
Carlos Martinez Romeroae9834f2024-07-03 13:33:47 -070093 sp<SurfaceListener> mSurfaceListener;
Pawin Vongmasa36653902018-11-15 00:10:25 -080094
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
113class Listener : public C2Component::Listener {
114public:
115 explicit Listener(SimplePlayer *thiz) : mThis(thiz) {}
116 virtual ~Listener() = default;
117
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
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
128 virtual void onError_nb(std::weak_ptr<C2Component> component,
129 uint32_t errorCode) override {
130 mThis->onError(component, errorCode);
131 }
132
133private:
134 SimplePlayer * const mThis;
135};
136
137
138SimplePlayer::SimplePlayer()
139 : mListener(new Listener(this)),
Carlos Martinez Romeroae9834f2024-07-03 13:33:47 -0700140 mSurfaceListener(new StubSurfaceListener),
Pawin Vongmasa36653902018-11-15 00:10:25 -0800141 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);
Carlos Martinez Romeroae9834f2024-07-03 13:33:47 -0700166 mSurface->connect(NATIVE_WINDOW_API_CPU, mSurfaceListener);
Pawin Vongmasa36653902018-11-15 00:10:25 -0800167}
168
169SimplePlayer::~SimplePlayer() {
170 mComposerClient->dispose();
171}
172
173void 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
184void 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
192void SimplePlayer::onError(std::weak_ptr<C2Component> component, uint32_t errorCode) {
193 (void) component;
194 (void) errorCode;
195 // TODO
196}
197
198void 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
384static void usage(const char *me) {
385 fprintf(stderr, "usage: %s [options] [input_filename]\n", me);
386 fprintf(stderr, " -h(elp)\n");
387}
388
389int 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 =
Dongwon Kang79b0c242019-10-13 08:17:34 -0700420 DataSourceFactory::getInstance()->CreateFromURI(nullptr /* httpService */, filename);
Pawin Vongmasa36653902018-11-15 00:10:25 -0800421
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}