blob: d531790da3c50175289ef1326730f565a1c563e8 [file] [log] [blame]
// Copyright 2019 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.
#ifndef SRC_MEDIA_CODEC_CODECS_VAAPI_CODEC_RUNNER_APP_H_
#define SRC_MEDIA_CODEC_CODECS_VAAPI_CODEC_RUNNER_APP_H_
#include <fuchsia/mediacodec/cpp/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/fidl/cpp/interface_request.h>
#include <lib/inspect/cpp/inspector.h>
#include <lib/sys/cpp/component_context.h>
#include <lib/syslog/cpp/log_level.h>
#include <lib/syslog/cpp/log_settings.h>
#include <lib/syslog/cpp/macros.h>
#include <lib/trace-provider/provider.h>
#include <initializer_list>
#include <memory>
#include <string>
#include "local_single_codec_factory.h"
// If a software can only provide an encoder or decoder, the other should be
// assigned NoAdapter in the template arguments, e.g.:
// CodecRunnerApp<CodecAdapterFfmpeg, NoAdapter>
template <typename Decoder, typename Encoder>
class CodecRunnerApp {
public:
CodecRunnerApp()
: codec_admission_control_(std::make_unique<CodecAdmissionControl>(loop_.dispatcher())) {}
void Init() {
static const std::initializer_list<std::string> kLogTags = {"vaapi_codec_runner"};
#ifdef NDEBUG
fuchsia_logging::SetTags(kLogTags);
#else
fuchsia_logging::SetLogSettings({.min_log_level = fuchsia_logging::LOG_DEBUG}, kLogTags);
#endif
trace_provider_ =
std::make_unique<trace::TraceProviderWithFdio>(loop_.dispatcher(), "vaapi_codec_runner");
// This is bit of a misnomer since CodecRunnerApp isn't a driver but instead a component. Since
// we need a new instance for every FIDL connection a driver does not make sense currently.
codec_diagnostics_ =
std::make_unique<CodecDiagnostics>("vaapi_codec_runner", loop_.dispatcher());
component_context_->outgoing()->AddPublicService(
fidl::InterfaceRequestHandler<fuchsia::mediacodec::CodecFactory>(
[this, codec_admission_control = codec_admission_control_.get()](
fidl::InterfaceRequest<fuchsia::mediacodec::CodecFactory> request) {
// We RemoveService() near the end of the present lambda, so it
// should be impossible to receive a second CodecFactory request.
FX_DCHECK(!codec_factory_);
fidl::InterfaceHandle<fuchsia::sysmem::Allocator> sysmem;
component_context_->svc()->Connect(sysmem.NewRequest());
codec_factory_ = std::make_unique<LocalSingleCodecFactory<Decoder, Encoder>>(
loop_.dispatcher(), std::move(sysmem), std::move(request),
[this](std::unique_ptr<CodecImpl> created_codec_instance) {
ZX_DEBUG_ASSERT(!codec_instance_);
if (!created_codec_instance) {
// Drop factory and close factory channel on factory
// failure to create instance.
codec_factory_ = nullptr;
// The codec_instance_ channel is the only reason for the
// isolate to exist.
loop_.Quit();
return;
}
// Own codec implementation and bind it.
codec_instance_ = std::move(created_codec_instance);
codec_instance_->BindAsync([this] {
// Drop codec implementation and close channel on
// error.
codec_instance_ = nullptr;
// The codec_instance_ channel is the only reason for
// the isolate to exist.
loop_.Quit();
});
// Drop factory and close factory channel.
codec_factory_ = nullptr;
},
codec_admission_control,
[this](zx_status_t error) {
// Drop factory and close factory channel on error.
codec_factory_ = nullptr;
// The codec_instance_ channel is the only reason for
// the isolate to exist. If codec_instance_ wasn't
// created via the codec_factory_ before this point,
// it'll never be created via codec_factory_.
if (!codec_instance_) {
loop_.Quit();
}
},
codec_diagnostics_.get());
// This runner only expects a single Local Codec Factory to ever
// be requested.
//
// This call deletes the presently-running lambda, so nothing
// after this call can use the lambda's captures, including the
// "this" pointer implicitly.
component_context_->outgoing()
->RemovePublicService<fuchsia::mediacodec::CodecFactory>();
}));
}
void Serve() { component_context_->outgoing()->ServeFromStartupInfo(); }
void Run() {
loop_.Run();
// Run the loop_.Shutdown() here (before ~CodecRunnerApp), so that any
// pending tasks get deleted sooner rather than later. The only pending
// task we expect to potentially be deleted here is the task queued in
// ~CodecImpl that does ~CodecAdmission and then ~zx::channel (even if the
// task is just deleted and not run). That task needs to run or be
// deleted before ~CodecAdmissionControl.
loop_.Shutdown();
}
std::unique_ptr<sys::ComponentContext>& component_context() { return component_context_; }
private:
async::Loop loop_{&kAsyncLoopConfigAttachToCurrentThread};
std::unique_ptr<sys::ComponentContext> component_context_{sys::ComponentContext::Create()};
std::unique_ptr<CodecAdmissionControl> codec_admission_control_;
std::unique_ptr<LocalSingleCodecFactory<Decoder, Encoder>> codec_factory_;
std::unique_ptr<CodecImpl> codec_instance_;
std::unique_ptr<trace::TraceProviderWithFdio> trace_provider_;
std::unique_ptr<CodecDiagnostics> codec_diagnostics_;
};
#endif // SRC_MEDIA_CODEC_CODECS_VAAPI_CODEC_RUNNER_APP_H_