| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <fuchsia/mediacodec/cpp/fidl.h> |
| #include <lib/async-loop/cpp/loop.h> |
| #include <lib/async/cpp/task.h> |
| #include <lib/component/cpp/startup_context.h> |
| |
| #include <stdio.h> |
| |
| // This test is currently manual because it needs to talk to the main |
| // CodecFactory which in turn needs to see/open a /dev/class/media-codec/000. |
| |
| void FailFatal() { |
| printf("FAIL\n"); |
| exit(-1); |
| } |
| |
| void PostSerial(async_dispatcher_t* dispatcher, fit::closure to_run) { |
| zx_status_t result = async::PostTask(dispatcher, std::move(to_run)); |
| if (result != ZX_OK) { |
| printf("async::PostTask() failed\n"); |
| FailFatal(); |
| } |
| } |
| |
| void test_factory() { |
| // We don't just use Sync FIDL proxies because we might need to receive events |
| // before long. |
| |
| async::Loop fidl_loop(&kAsyncLoopConfigNoAttachToThread); |
| // Start a separate FIDL thread for two reasons: |
| // * It's handy for the main thread to stay separate to control the test. |
| // * By having a separate FIDL thread, this test shows how to do so without |
| // creating problems. |
| fidl_loop.StartThread("FIDL_thread"); |
| |
| std::unique_ptr<component::StartupContext> startup_context; |
| // Use FIDL thread to run CreateFromStartupInfo(), since it uses the calling |
| // thread's default async_t, and we don't want to be accidentally doing |
| // FIDL requests from the main thread, so we use |
| // kAsyncLoopConfigNoAttachToThread above. |
| PostSerial(fidl_loop.dispatcher(), [&startup_context] { |
| startup_context = component::StartupContext::CreateFromStartupInfo(); |
| }); |
| |
| fuchsia::mediacodec::CodecFactoryPtr codec_factory; |
| codec_factory.set_error_handler([](zx_status_t error) { |
| printf("codec_factory failed with error %d\n", error); |
| FailFatal(); |
| }); |
| |
| // It appears ConnectToEnvironmentService() is probably currently safe to call |
| // from the main thread, but if it moves to use FIDL libs instead, then it |
| // won't be any longer, so call from FIDL thread instead. |
| PostSerial( |
| fidl_loop.dispatcher(), |
| [&startup_context, |
| request = codec_factory.NewRequest(fidl_loop.dispatcher())]() mutable { |
| startup_context |
| ->ConnectToEnvironmentService<fuchsia::mediacodec::CodecFactory>( |
| std::move(request)); |
| }); |
| |
| fuchsia::media::StreamProcessorPtr codec; |
| codec.set_error_handler([](zx_status_t error) { |
| printf( |
| "codec failed with error %d (for now this is normal if not running " |
| "this on VIM2)\n", |
| error); |
| FailFatal(); |
| }); |
| // Use FIDL thread to send request for Codec. |
| PostSerial( |
| fidl_loop.dispatcher(), |
| [&codec_factory, request = codec.NewRequest(fidl_loop.dispatcher()), |
| params = fuchsia::mediacodec::CreateDecoder_Params{ |
| .input_details.format_details_version_ordinal = 0, |
| .input_details.mime_type = "video/h264", |
| .promise_separate_access_units_on_input = true, |
| .require_hw = true, |
| }]() mutable { |
| codec_factory->CreateDecoder2(std::move(params), std::move(request)); |
| }); |
| |
| // Use FIDL thread to check that codec can communicate to the driver |
| // round-trip. The other-thread usage is a bit unnatural here, but we want to |
| // keep the test sequencing on a thread of its own, and the current thread is |
| // that thread. |
| std::mutex is_sync_done_lock; |
| bool is_sync_done = false; |
| std::condition_variable is_sync_done_condition; |
| PostSerial(fidl_loop.dispatcher(), [&codec, &is_sync_done_lock, &is_sync_done, |
| &is_sync_done_condition] { |
| codec->Sync([&is_sync_done_lock, &is_sync_done, &is_sync_done_condition] { |
| printf("codec->Sync() completing (FIDL thread)\n"); |
| { // scope lock |
| std::unique_lock<std::mutex> lock(is_sync_done_lock); |
| is_sync_done = true; |
| } // ~lock |
| is_sync_done_condition.notify_all(); |
| }); |
| }); |
| |
| // Wait for Sync() to be done, or a channel to fail (in which case the error |
| // handler(s) will exit(-1) and fail the test). |
| { // scope lock |
| std::unique_lock<std::mutex> lock(is_sync_done_lock); |
| while (!is_sync_done) { |
| is_sync_done_condition.wait_for(lock, std::chrono::seconds(10)); |
| if (!is_sync_done) { |
| printf("still waiting for codec->Sync() to be done.\n"); |
| } |
| } |
| } // ~lock |
| |
| printf("main thread knows codec->Sync() completed - cleaning up\n"); |
| |
| // To avoid the hassle of needing to switch to the FIDL thread to un-bind |
| // safely, we can use the other workable way to un-bind from a different |
| // thread, which is to stop the FIDL thread first. |
| fidl_loop.Quit(); |
| fidl_loop.JoinThreads(); |
| |
| // ~codec - unbinds |
| // ~codec_factory - unbinds |
| // ~fidl_loop - does Shutdown() |
| } |
| |
| void usage(const char* prog_name) { printf("usage: %s\n", prog_name); } |
| |
| int main(int argc, char* argv[]) { |
| if (argc != 1) { |
| usage(argv[0]); |
| FailFatal(); |
| } |
| |
| test_factory(); |
| |
| // PASS |
| printf("PASS\n"); |
| // No destructors run after printing PASS. |
| return 0; |
| } |