diff --git a/garnet/bin/media/BUILD.gn b/garnet/bin/media/BUILD.gn index 0331580f299a9e7fecdc75f79a73a28df1523976..0e3bd42f76b6851f88c4bc816300b1a61d1bc0f8 100644 --- a/garnet/bin/media/BUILD.gn +++ b/garnet/bin/media/BUILD.gn @@ -11,7 +11,6 @@ group("media") { ":audio_core", ":codec_factory", ":codec_runner_sw_ffmpeg", - ":codec_runner_sw_omx", ":services", ":tools", "audio", @@ -107,35 +106,6 @@ package("codec_factory") { ] } -# This package is only really expected to be used by the codec_factory package. -package("codec_runner_sw_omx") { - meta = [ - { - path = rebase_path("codecs/meta/codec_runner_sw_omx.cmx") - dest = "codec_runner_sw_omx.cmx" - }, - ] - - deps = [ - "//garnet/bin/media/codecs/sw/omx/codec_runner_sw_omx:codec_runner_sw_omx", - - # Codec loadable_module(s) - "//garnet/bin/media/codecs/sw/omx/dec/aac:libcodec_sw_omx_dec_aac", - ] - - binaries = [ - { - name = "codec_runner_sw_omx" - }, - ] - - loadable_modules = [ - { - name = "libcodec_sw_omx_dec_aac.so" - }, - ] -} - # This package is only really expected to be used by the codec_factory package. package("codec_runner_sw_ffmpeg") { meta = [ diff --git a/garnet/bin/media/codecs/meta/codec_runner_sw_omx.cmx b/garnet/bin/media/codecs/meta/codec_runner_sw_omx.cmx deleted file mode 100644 index 9f4f673856839a1a10266c5edceca84b0cbe7881..0000000000000000000000000000000000000000 --- a/garnet/bin/media/codecs/meta/codec_runner_sw_omx.cmx +++ /dev/null @@ -1,11 +0,0 @@ -{ - "program": { - "binary": "bin/codec_runner_sw_omx" - }, - "sandbox": { - "services": [ - "fuchsia.sys.Launcher", - "fuchsia.tracelink.Registry" - ] - } -} diff --git a/garnet/bin/media/codecs/sw/BUILD.gn b/garnet/bin/media/codecs/sw/BUILD.gn index 160011d709d23b562e38e3234d4e522aa60a6350..6aaaacd7f35313f39c721cf71415f280d28f9512 100644 --- a/garnet/bin/media/codecs/sw/BUILD.gn +++ b/garnet/bin/media/codecs/sw/BUILD.gn @@ -4,6 +4,7 @@ import("//build/test/test_package.gni") +# TODO(turnage): low_layer part needs to not be test-only. group("sw") { testonly = true deps = [ @@ -13,7 +14,7 @@ group("sw") { ":mpsc_queue_test_bin", ":mpsc_queue_tests", "ffmpeg", - "omx", + "low_layer", ] } diff --git a/garnet/bin/media/codecs/sw/omx/low_layer/BUILD.gn b/garnet/bin/media/codecs/sw/low_layer/BUILD.gn similarity index 100% rename from garnet/bin/media/codecs/sw/omx/low_layer/BUILD.gn rename to garnet/bin/media/codecs/sw/low_layer/BUILD.gn diff --git a/garnet/bin/media/codecs/sw/omx/low_layer/aac/BUILD.gn b/garnet/bin/media/codecs/sw/low_layer/aac/BUILD.gn similarity index 98% rename from garnet/bin/media/codecs/sw/omx/low_layer/aac/BUILD.gn rename to garnet/bin/media/codecs/sw/low_layer/aac/BUILD.gn index c9c59134c26a2791fd6be888532f27f1cbfaee99..6c593e4f43adcdeac567216b822a1713aceaf385 100644 --- a/garnet/bin/media/codecs/sw/omx/low_layer/aac/BUILD.gn +++ b/garnet/bin/media/codecs/sw/low_layer/aac/BUILD.gn @@ -21,9 +21,9 @@ config("public_include_dirs") { } static_library("libFraunhoferAAC") { + # TODO(turnage): more visibility as needed visibility = [ ":*", - "//garnet/bin/media/codecs/sw/omx/dec/aac/*", ] public = [ "//third_party/android/platform/external/aac/libAACdec/include/aacdecoder_lib.h", @@ -179,6 +179,6 @@ static_library("libFraunhoferAAC") { "-Wno-unused-label", ] deps = [ - "//garnet/bin/media/codecs/sw/omx/low_layer/codec_android_pal:codec_android_pal", + "//garnet/bin/media/codecs/sw/low_layer/codec_android_pal:codec_android_pal", ] } diff --git a/garnet/bin/media/codecs/sw/omx/low_layer/codec_android_pal/BUILD.gn b/garnet/bin/media/codecs/sw/low_layer/codec_android_pal/BUILD.gn similarity index 83% rename from garnet/bin/media/codecs/sw/omx/low_layer/codec_android_pal/BUILD.gn rename to garnet/bin/media/codecs/sw/low_layer/codec_android_pal/BUILD.gn index 831610cde9405dfa241964ae2779e490adddf834..e8792d19a42fdb4318ff697a9a2045c28b713746 100644 --- a/garnet/bin/media/codecs/sw/omx/low_layer/codec_android_pal/BUILD.gn +++ b/garnet/bin/media/codecs/sw/low_layer/codec_android_pal/BUILD.gn @@ -8,6 +8,6 @@ config("public_include_dirs") { } source_set("codec_android_pal") { - visibility = [ "//garnet/bin/media/codecs/sw/omx/low_layer/*" ] + visibility = [ "//garnet/bin/media/codecs/sw/low_layer/*" ] public_configs = [ ":public_include_dirs" ] } diff --git a/garnet/bin/media/codecs/sw/omx/low_layer/codec_android_pal/include/log/log.h b/garnet/bin/media/codecs/sw/low_layer/codec_android_pal/include/log/log.h similarity index 100% rename from garnet/bin/media/codecs/sw/omx/low_layer/codec_android_pal/include/log/log.h rename to garnet/bin/media/codecs/sw/low_layer/codec_android_pal/include/log/log.h diff --git a/garnet/bin/media/codecs/sw/omx/BUILD.gn b/garnet/bin/media/codecs/sw/omx/BUILD.gn deleted file mode 100644 index 10c25f58e4e6eaa8035db9d198aca9319c2e94bd..0000000000000000000000000000000000000000 --- a/garnet/bin/media/codecs/sw/omx/BUILD.gn +++ /dev/null @@ -1,13 +0,0 @@ -# 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. - -group("omx") { - testonly = true - deps = [ - "codec_runner_sw_omx", - "common", - "dec", - "low_layer", - ] -} diff --git a/garnet/bin/media/codecs/sw/omx/codec_runner_sw_omx/BUILD.gn b/garnet/bin/media/codecs/sw/omx/codec_runner_sw_omx/BUILD.gn deleted file mode 100644 index c58a65a1a190d25f3cc9b989a2c6261a93823c52..0000000000000000000000000000000000000000 --- a/garnet/bin/media/codecs/sw/omx/codec_runner_sw_omx/BUILD.gn +++ /dev/null @@ -1,49 +0,0 @@ -# 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. - -import("//build/test.gni") - -executable("codec_runner_sw_omx") { - # This executable is only intended for use by codec_factory. The - # codec_factory and codec_runner_sw_omx are currently peer packages. - visibility = [ - "//garnet/bin/media:codec_runner_sw_omx", - "//garnet/bin/media:codec_runner_sw_omx.manifest", - "//garnet/bin/media/*", # TODO(CF-235): Dep shouldn' be needed - ] - - sources = [ - "main.cc", - - # The component-focused parts that don't care about OMX: - "codec_runner_component.cc", - "codec_runner_component.h", - - # The local CodecFactory implementation that only needs to handle the - # specific limited usage by the main CodecFactory implementation. - "local_codec_factory.cc", - "local_codec_factory.h", - - # Abstract class to enable a wrapper codec runner as needed. - "codec_runner.cc", - "codec_runner.h", - - # The part that cares about OMX. - "omx_codec_runner.cc", - "omx_codec_runner.h", - ] - - deps = [ - "//garnet/public/lib/component/cpp", - "//garnet/public/lib/fsl", - "//sdk/fidl/fuchsia.mediacodec", - - # This is a reduced set of include paths vs. what the whole pal uses - just - # the paths necessary to use the OMX aspects of the .so entry point is the - # intent here. We don't get this for free as a public_dep from any of the - # .so codec libs because we use dlopen() on those. - "//garnet/bin/media/codecs/sw/omx/common/omx_android_pal:omx_so_entry_point_config_source_set", - "//zircon/public/lib/async-loop-cpp", - ] -} diff --git a/garnet/bin/media/codecs/sw/omx/codec_runner_sw_omx/array_size.h b/garnet/bin/media/codecs/sw/omx/codec_runner_sw_omx/array_size.h deleted file mode 100644 index 9dd4018075929a7746e418d87a9dbf4e7d29af53..0000000000000000000000000000000000000000 --- a/garnet/bin/media/codecs/sw/omx/codec_runner_sw_omx/array_size.h +++ /dev/null @@ -1,14 +0,0 @@ -// 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. - -#ifndef GARNET_BIN_MEDIA_CODECS_SW_OMX_CODEC_RUNNER_SW_OMX_ARRAY_SIZE_H_ -#define GARNET_BIN_MEDIA_CODECS_SW_OMX_CODEC_RUNNER_SW_OMX_ARRAY_SIZE_H_ - -// Only compiles for arrays with known size. -template <typename T, size_t N> -constexpr size_t array_size(T (&)[N]) { - return N; -} - -#endif // GARNET_BIN_MEDIA_CODECS_SW_OMX_CODEC_RUNNER_SW_OMX_ARRAY_SIZE_H_ diff --git a/garnet/bin/media/codecs/sw/omx/codec_runner_sw_omx/codec_runner.cc b/garnet/bin/media/codecs/sw/omx/codec_runner_sw_omx/codec_runner.cc deleted file mode 100644 index 2e2f06b1e98a5293e4e2454a25f822abb5483a7f..0000000000000000000000000000000000000000 --- a/garnet/bin/media/codecs/sw/omx/codec_runner_sw_omx/codec_runner.cc +++ /dev/null @@ -1,114 +0,0 @@ -// 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 "codec_runner.h" - -#include <lib/async/cpp/task.h> -#include <lib/fidl/cpp/clone.h> - -namespace codec_runner { - -CodecRunner::CodecRunner(async_dispatcher_t* fidl_dispatcher, - thrd_t fidl_thread) - : fidl_dispatcher_(fidl_dispatcher), fidl_thread_(fidl_thread) { - // nothing else to do here -} - -CodecRunner::~CodecRunner() = default; - -void CodecRunner::BindAndOwnSelf( - fidl::InterfaceRequest<fuchsia::media::StreamProcessor> codec_request, - std::unique_ptr<CodecRunner> self) { - assert(thrd_current() == fidl_thread_); - // We have input_constraints_ by now thanks to our behavior (server-side), - // so this can be an assert(). - assert(input_constraints_); - - binding_ = std::make_unique<BindingType>(std::move(self)); - binding_->set_error_handler([this](zx_status_t error) { - // No point in trying to send an epitaph here since the reason we're here - // is the other end being gone. - // - // This class is only used for running one Codec instance per process. - // - // Since the channel failed, the client probably won't see this message. - Exit("The Codec channel failed server-side. Normal if client is done."); - }); - binding_->Bind(std::move(codec_request), fidl_dispatcher_); - - // Some sub-classes already want to convey some output constraints as early - // as possible - this is a place for those sub-classes to do so. Sending - // before input constraints encourages the client to configure output before - // delivering input that starts the first stream, to try to avoid extra - // output re-configs. - onInputConstraintsReady(); - - // Now we can tell the client about the input constraints. We do this as an - // event because the client has no choice re. whether the client needs - // these. These are _always_ needed by the client. Also, as an event it - // would be easier to have the CodecFactory potentially send these instead - // of the Codec to save a bit on latency. - // - // Intentional copy, in case a derived class wants to refer to - // input_constraints_. - // - // TODO(dustingreen): Make these serial, make the serial context be the same - // one and be visible to all the places that need to send, probably serial - // context in CodecRunner as a protected field. OR, ask and confirm that - // async::PostTask() is guaranteed to remain serial (like it was before, and - // like it seems to be the vast majority of the time currently). - input_constraints_sent_ = true; - - // We post here so that we're ordered after similar posting done in - // onInputConstraintsReady() above, so that the derived class has every chance - // to send output constraints before input constraints to encourage client to - // configure output before starting to deliver input data. - async::PostTask(fidl_dispatcher_, [this] { - binding_->events().OnInputConstraints(fidl::Clone(*input_constraints_)); - }); - - onSetupDone(); -} - -void CodecRunner::Exit(const char* format, ...) { - // TODO(dustingreen): Send epitaph when possible. - - // Let's not have a buffer on the stack, not because it couldn't be done - // safely, but because we'd potentially run into stack size vs. message length - // tradeoffs, stack expansion granularity fun, or whatever else. - - va_list args; - va_start(args, format); - size_t buffer_bytes = vsnprintf(nullptr, 0, format, args) + 1; - va_end(args); - - // ~buffer never actually runs since this method never returns - std::unique_ptr<char[]> buffer(new char[buffer_bytes]); - - va_start(args, format); - size_t buffer_bytes_2 = - vsnprintf(buffer.get(), buffer_bytes, format, args) + 1; - (void)buffer_bytes_2; - // sanity check; should match so go ahead and assert that it does. - assert(buffer_bytes == buffer_bytes_2); - va_end(args); - - // TODO(dustingreen): It might be worth wiring this up to the log in a more - // official way, especially if doing so would print a timestamp automatically - // and/or provide filtering goodness etc. - printf("%s -- Codec server isolate will exit(-1)\n", buffer.get()); - - // TODO(dustingreen): Send string in buffer via epitaph, when possible. First - // we should switch to events so we'll only have the Codec channel not the - // CodecEvents channel. Note to self: The channel failing server-side may race - // with trying to send. - - // TODO(dustingreen): determine if our heap leak detection will be able to - // tolerate this exit(-1) and still detect leaks - and fix it to tolerate if - // it doesn't already, because it should. - - exit(-1); -} - -} // namespace codec_runner diff --git a/garnet/bin/media/codecs/sw/omx/codec_runner_sw_omx/codec_runner.h b/garnet/bin/media/codecs/sw/omx/codec_runner_sw_omx/codec_runner.h deleted file mode 100644 index 05ce6cdf0fd02a10c3f8e63bff734c0bf646bdf1..0000000000000000000000000000000000000000 --- a/garnet/bin/media/codecs/sw/omx/codec_runner_sw_omx/codec_runner.h +++ /dev/null @@ -1,116 +0,0 @@ -// 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. - -#ifndef GARNET_BIN_MEDIA_CODECS_SW_OMX_CODEC_RUNNER_SW_OMX_CODEC_RUNNER_H_ -#define GARNET_BIN_MEDIA_CODECS_SW_OMX_CODEC_RUNNER_SW_OMX_CODEC_RUNNER_H_ - -#include <threads.h> - -#include <fuchsia/mediacodec/cpp/fidl.h> - -#include "lib/fidl/cpp/binding.h" -#include "src/lib/fxl/macros.h" -#include "src/lib/fxl/synchronization/thread_annotations.h" - -namespace codec_runner { - -// This is an abstract base class whose main purpose is to prevent us from -// assuming that all codecs run locally will be OMX codecs. -class CodecRunner : public fuchsia::media::StreamProcessor { - public: - // needs a virtual destructor because unique_ptr will be deleting via vtable - // entry instead of direct call to destructor - virtual ~CodecRunner(); - - // Load() will be called after the derived class constructor. - virtual bool Load() = 0; - - // Only one of the following SetXXXParams() is called, corresponding to which - // codec type was requested via CodecFactory. These are meant to be an easy - // way to convey the most recent known version of complete codec creation - // parameters to the CodecRunner. As such they are not intended to be a - // complete CodecFactory implementation, nor does this class implement - // CodecFactory. - virtual void SetDecoderParams( - fuchsia::mediacodec::CreateDecoder_Params decoder_params) = 0; - // TODO(dustingreen): - // virtual void SetAudioEncoderParams(...) = 0; - // virtual void SetVideoEncoderParams(...) = 0; - // (or combined) - - // Now that type-specific params are set, input_constraints_ can be computed. - // We want this done before binding the Codec channel so we can immediately - // send the input constraints as soon as BindAndOwnSelf(), to ensure that - // input constraints get sent first from server to client, per the Codec - // protocol. - virtual void ComputeInputConstraints() = 0; - - // This call causes ownership of "this" to transfer to binding_, which - // essentially makes "this" self-owned (roughly speaking), or slightly more - // precisely, owned by the Codec channel via ImplPtr of the binding being - // std::unique_ptr<CodecRunner> here (instead of the default Codec*). - void BindAndOwnSelf( - fidl::InterfaceRequest<fuchsia::media::StreamProcessor> codec_request, - std::unique_ptr<CodecRunner> self); - - // Some sub-classes want to send initial output constraints very early, - // instead of waiting for any input data. This can be because the codec - // implementation isn't capable of waiting until input data has arrived before - // demanding output buffers despite a tendency (but not guarantee) of forcing - // re-configuration of those initial output buffers (I'm looking at you OMX), - // or because the codec really does already know the output buffer constraints - // based on codec creation info, so doesn't need any input data before - // indicating output constraints. - // - // This intentionally gets called _before_ sending input constraints, so extra - // output re-config is avoided if the client processes this before sending - // input data. - // - // The default implementation does nothing. - virtual void onInputConstraintsReady(){}; - - // The Setup ordering domain is done. This allows the items in the Setup - // ordering domain to be completely separate from the StreamControl ordering - // domain. - virtual void onSetupDone(){}; - - void Exit(const char* format, ...); - - protected: - CodecRunner(async_dispatcher_t* fidl_dispatcher, thrd_t fidl_thread); - - // Lock that protects stuff. - // - // TODO(dustingreen): Figure out which locks and condition variables / events - // to use from FXL, or how to get FXL_GUARDED_BY() to understand std::mutex + - // std::unique_lock<> (including when a unique_lock& is passed into a method - // along the way but will definitely be locked again by the time that method - // returns) if that's possible. We should use FXL_GUARDED_BY() where its - // expressiveness is sufficient. - std::mutex lock_; - - async_dispatcher_t* const fidl_dispatcher_; - const thrd_t fidl_thread_; - using BindingType = fidl::Binding<fuchsia::media::StreamProcessor, - std::unique_ptr<CodecRunner>>; - std::unique_ptr<BindingType> binding_; - - bool input_constraints_sent_ = false; - - // This must be set by derived class no later than the end of - // SetAudioDecoderParams() or analogous method, so that this will be - // guaranteed to be set before Codec binding occurs, so we can send these - // constraints during BindAndOwnSelf(). - // - // This remains valid after CodecRunner sends OnInputConstraints(), in case - // a derived class wants to refer to the input constraints. - std::unique_ptr<const fuchsia::media::StreamBufferConstraints> - input_constraints_; - - FXL_DISALLOW_IMPLICIT_CONSTRUCTORS(CodecRunner); -}; - -} // namespace codec_runner - -#endif // GARNET_BIN_MEDIA_CODECS_SW_OMX_CODEC_RUNNER_SW_OMX_CODEC_RUNNER_H_ diff --git a/garnet/bin/media/codecs/sw/omx/codec_runner_sw_omx/codec_runner_component.cc b/garnet/bin/media/codecs/sw/omx/codec_runner_sw_omx/codec_runner_component.cc deleted file mode 100644 index db31b0175336609a7c193ca3f374ff2c6deed98d..0000000000000000000000000000000000000000 --- a/garnet/bin/media/codecs/sw/omx/codec_runner_sw_omx/codec_runner_component.cc +++ /dev/null @@ -1,47 +0,0 @@ -// 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 "codec_runner_component.h" - -#include "local_codec_factory.h" - -namespace { - -// We may in future allow creation strategies that involve sharing a process -// across more than one Codec instance, but for now we don't, so enforce max of -// one CodecFactory instance ever in this process. -bool is_factory_created = false; - -} // namespace - -namespace codec_runner { - -CodecRunnerComponent::CodecRunnerComponent( - async_dispatcher_t* fidl_dispatcher, thrd_t fidl_thread, - std::unique_ptr<component::StartupContext> startup_context) - : fidl_dispatcher_(fidl_dispatcher), - fidl_thread_(fidl_thread), - startup_context_(std::move(startup_context)) { - startup_context_->outgoing().deprecated_services()->AddServiceForName( - [this](zx::channel request) { - // This process only intends to have up to one CodecFactory at least for - // now, so enforce that here. - if (is_factory_created) { - // TODO: send epitaph, when possible - request.reset(); - assert(!is_factory_created); - exit(-1); - } - // We use the self-owned pattern rather than a singleton, in case we - // later allow more than one, since the CodecFactory interface is - // stateful by design. - codec_factory::LocalCodecFactory::CreateSelfOwned( - fidl_dispatcher_, fidl_thread_, - fidl::InterfaceRequest<fuchsia::mediacodec::CodecFactory>( - std::move(request))); - }, - fuchsia::mediacodec::CodecFactory::Name_); -} - -} // namespace codec_runner diff --git a/garnet/bin/media/codecs/sw/omx/codec_runner_sw_omx/codec_runner_component.h b/garnet/bin/media/codecs/sw/omx/codec_runner_sw_omx/codec_runner_component.h deleted file mode 100644 index aaa7c70f146a7aaf124ae7df31cb9d331b202ff7..0000000000000000000000000000000000000000 --- a/garnet/bin/media/codecs/sw/omx/codec_runner_sw_omx/codec_runner_component.h +++ /dev/null @@ -1,29 +0,0 @@ -// 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. - -#ifndef GARNET_BIN_MEDIA_CODECS_SW_OMX_CODEC_RUNNER_SW_OMX_CODEC_RUNNER_COMPONENT_H_ -#define GARNET_BIN_MEDIA_CODECS_SW_OMX_CODEC_RUNNER_SW_OMX_CODEC_RUNNER_COMPONENT_H_ - -#include <fuchsia/mediacodec/cpp/fidl.h> - -#include "lib/component/cpp/startup_context.h" -#include "lib/fidl/cpp/binding.h" - -namespace codec_runner { - -class CodecRunnerComponent { - public: - CodecRunnerComponent( - async_dispatcher_t* fidl_dispatcher, thrd_t fidl_thread, - std::unique_ptr<component::StartupContext> startup_context); - - private: - async_dispatcher_t* fidl_dispatcher_ = nullptr; - thrd_t fidl_thread_ = 0; - std::unique_ptr<component::StartupContext> startup_context_; -}; - -} // namespace codec_runner - -#endif // GARNET_BIN_MEDIA_CODECS_SW_OMX_CODEC_RUNNER_SW_OMX_CODEC_RUNNER_COMPONENT_H_ diff --git a/garnet/bin/media/codecs/sw/omx/codec_runner_sw_omx/local_codec_factory.cc b/garnet/bin/media/codecs/sw/omx/codec_runner_sw_omx/local_codec_factory.cc deleted file mode 100644 index eeaa0f7fdb682ca28b9fca1dd552f686203c3324..0000000000000000000000000000000000000000 --- a/garnet/bin/media/codecs/sw/omx/codec_runner_sw_omx/local_codec_factory.cc +++ /dev/null @@ -1,164 +0,0 @@ -// 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 "local_codec_factory.h" - -#include "codec_runner.h" -#include "omx_codec_runner.h" - -#include <src/lib/fxl/arraysize.h> -#include <src/lib/fxl/logging.h> - -#include <list> -#include <map> - -namespace { - -char kLibDecoderAac[] = "libcodec_sw_omx_dec_aac.so"; - -} // namespace - -namespace codec_factory { - -// TODO(dustingreen): Include fuchsia::mediacodec::CodecDescription info in -// here, so we can select based on bool requirement fields in requests for a -// codec. -LocalCodecFactory::CodecStrategy LocalCodecFactory::codec_strategies[] = { - // TODO(dustingreen): Instead of CreateRawOmxRunner, create a wrapper that - // deals with the lack of kLibDecoderAac support for split ADTS headers, - // which so far is unique to this mime type. Until we get the rest working - // we'll just use the CreateRawOmxRunner without any wrapper and avoid - // annoying the broken Codec in the client code, but the Codec for this mime - // type should be made to work correctly one way or another before too long. - CodecStrategy{fuchsia::mediacodec::CodecType::DECODER, "audio/aac-adts", - kLibDecoderAac, CreateRawOmxRunner}, -}; - -void LocalCodecFactory::CreateSelfOwned( - async_dispatcher_t* fidl_dispatcher, thrd_t fidl_thread, - fidl::InterfaceRequest<CodecFactory> codec_factory_request) { - std::unique_ptr<LocalCodecFactory> codec_factory( - new LocalCodecFactory(fidl_dispatcher, fidl_thread)); - // C++ evaluation order is mostly arbitrary within a statement, so stash this - // result of unique_ptr::operator->() to avoid moving the same unique_ptr in a - // single statement. The actual pointed-at instance isn't moving, so it's - // fine to have this ref for a moment here. - std::unique_ptr<BindingType>& binding = codec_factory->binding_; - binding = std::make_unique<BindingType>(std::move(codec_factory), - std::move(codec_factory_request), - fidl_dispatcher); -} - -LocalCodecFactory::LocalCodecFactory(async_dispatcher_t* fidl_dispatcher, - thrd_t fidl_thread) - : fidl_dispatcher_(fidl_dispatcher), fidl_thread_(fidl_thread) { - // nothing else to do here -} - -// Decoder: - -void LocalCodecFactory::CreateDecoder( - fuchsia::mediacodec::CreateDecoder_Params decoder_params, - ::fidl::InterfaceRequest<fuchsia::media::StreamProcessor> decoder_request) { - if (!decoder_params.has_input_details()) { - FXL_LOG(WARNING) << "missing input_details"; - return; - } - - if (!decoder_params.input_details().has_mime_type()) { - FXL_LOG(WARNING) << "input details missing mime type"; - // Without mime_type we cannot search for a decoder. - return; - } - - CreateCommon(std::move(decoder_request), - fuchsia::mediacodec::CodecType::DECODER, - decoder_params.input_details().mime_type(), - [this, decoder_params = std::move(decoder_params)]( - codec_runner::CodecRunner* codec_runner) mutable { - codec_runner->SetDecoderParams(std::move(decoder_params)); - }); -} - -void LocalCodecFactory::CreateEncoder( - fuchsia::mediacodec::CreateEncoder_Params encoder_params, - ::fidl::InterfaceRequest<fuchsia::media::StreamProcessor> encoder_request) { - // We have no encoders to provide. - // ~encoder_request -} - -void LocalCodecFactory::CreateCommon( - ::fidl::InterfaceRequest<fuchsia::media::StreamProcessor> codec_request, - fuchsia::mediacodec::CodecType codec_type, std::string mime_type, - fit::function<void(codec_runner::CodecRunner* codec_runner)> - set_type_specific_params) { - std::unique_ptr<codec_runner::CodecRunner> codec_runner = - CreateCodec(fidl_dispatcher_, fidl_thread_, codec_type, mime_type); - if (!codec_runner) { - // TODO(dustingreen): epitaph - FXL_LOG(WARNING) << "!codec_runner - exiting"; - exit(-1); - } - set_type_specific_params(codec_runner.get()); - codec_runner->ComputeInputConstraints(); - codec_runner::CodecRunner& codec_runner_ref = *codec_runner; - codec_runner_ref.BindAndOwnSelf(std::move(codec_request), - std::move(codec_runner)); - // This CodecFactory instance is done creating the one Codec that this factory - // is willing to create, and that one Codec is now self-owned (owned by its - // own channel), so self-destruct "this" here: - binding_.reset(); -} - -// Appropriate for use with any mime_type where the raw OMX codec doesn't have -// any known open issues. -// -// TODO(dustingreen): We're currently using this method for audio/aac-adts, but -// instead the OMX codec runner will need to extract its own -// make_AudioSpecificConfig_from_ADTS_header() data instead of relying on the -// client to pass it down. TBD whether we use a wrapper for that or a more -// targetted behavior override. Either this method needs to know or another -// method to create a different way needs to exist. -std::unique_ptr<codec_runner::CodecRunner> -LocalCodecFactory::CreateRawOmxRunner(async_dispatcher_t* fidl_dispatcher, - thrd_t fidl_thread, - const CodecStrategy& codec_strategy) { - return std::make_unique<codec_runner::OmxCodecRunner>( - fidl_dispatcher, fidl_thread, codec_strategy.mime_type, - codec_strategy.lib_filename); -} - -std::unique_ptr<codec_runner::CodecRunner> LocalCodecFactory::CreateCodec( - async_dispatcher_t* fidl_dispatcher, thrd_t fidl_thread, - fuchsia::mediacodec::CodecType codec_type, std::string mime_type) { - const CodecStrategy* strategy = nullptr; - for (auto& codec_strategy : codec_strategies) { - if (codec_strategy.codec_type == codec_type && - codec_strategy.mime_type == mime_type) { - strategy = &codec_strategy; - break; - } - } - if (!strategy) { - // Currently this is what's seen when there's an overall failure to find a - // usable codec, when !require_hw. - FXL_LOG(WARNING) << "!strategy - no codec found - codec_type: " - << static_cast<uint32_t>(codec_type) - << " mime_type: " << mime_type; - return nullptr; - } - std::unique_ptr<codec_runner::CodecRunner> codec_runner = - strategy->create_runner(fidl_dispatcher, fidl_thread, *strategy); - if (!codec_runner) { - FXL_LOG(WARNING) << "!codec_runner"; - return nullptr; - } - if (!codec_runner->Load()) { - FXL_LOG(WARNING) << "!codec_runner->Load()"; - return nullptr; - } - return codec_runner; -} - -} // namespace codec_factory diff --git a/garnet/bin/media/codecs/sw/omx/codec_runner_sw_omx/local_codec_factory.h b/garnet/bin/media/codecs/sw/omx/codec_runner_sw_omx/local_codec_factory.h deleted file mode 100644 index 42698e12a327ec999bc13dd3a3540cb92daaf179..0000000000000000000000000000000000000000 --- a/garnet/bin/media/codecs/sw/omx/codec_runner_sw_omx/local_codec_factory.h +++ /dev/null @@ -1,125 +0,0 @@ -// 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. - -#ifndef GARNET_BIN_MEDIA_CODECS_SW_OMX_CODEC_RUNNER_SW_OMX_LOCAL_CODEC_FACTORY_H_ -#define GARNET_BIN_MEDIA_CODECS_SW_OMX_CODEC_RUNNER_SW_OMX_LOCAL_CODEC_FACTORY_H_ - -#include <threads.h> - -#include <functional> - -#include <fuchsia/mediacodec/cpp/fidl.h> -#include <lib/fit/function.h> - -#include "lib/fidl/cpp/binding.h" - -// The LocalCodecFactory implements CodecFactory, but it's a very limited local -// implementation. The main implementation of CodecFactory is in -// codec_codec_factory_impl.h/cc. -// -// The point of the implementation here is to do some basic sanity checks, -// accept config info, and call the owner of this class back to achieve the -// actual binding of the server end of a Codec channel to a Codec implementation -// provided by the owner. That way the owner can wire up the details however -// the owner wants. -// -// This class does not need to deal with every potential version of a codec -// creation request. Instead, this class only needs to deal with requests made -// by the latest main CodecFactory implementation, as the main CodecFactory will -// have already converted any older-style requests to the latest style. -// -// Any given instance of this class is only capable of creating the codec type -// for which it was instantiated, based on which constructor was called. This -// de-fans the CodecFactory interface for the owning code. It's fairly -// mechanical which is why it's a separate class to deal with the de-fan without -// really applying any real strategy in this class. -// -// The interaction between the main CodecFactory and built-in SW codec isolates -// is something that only needs to handle the same build version on both sides. - -namespace codec_runner { -class CodecRunner; -} // namespace codec_runner - -namespace codec_factory { - -class LocalCodecFactory : public fuchsia::mediacodec::CodecFactory { - public: - using BindAudioDecoderCallback = fit::function<void( - fidl::InterfaceRequest<fuchsia::media::StreamProcessor>, - fuchsia::mediacodec::CreateDecoder_Params audio_params)>; - - // This creates a self-owned CodecFactory instance that knows how to create - // any of the codecs supported by this isolate process, regardless of which - // codec type. - static void CreateSelfOwned( - async_dispatcher_t* fidl_dispatcher, thrd_t fidl_thread, - fidl::InterfaceRequest<fuchsia::mediacodec::CodecFactory> request); - - virtual void CreateDecoder( - fuchsia::mediacodec::CreateDecoder_Params audio_decoder_params, - ::fidl::InterfaceRequest<fuchsia::media::StreamProcessor> audio_decoder) - override; - - virtual void CreateEncoder( - fuchsia::mediacodec::CreateEncoder_Params encoder_params, - ::fidl::InterfaceRequest<fuchsia::media::StreamProcessor> encoder_request) - override; - - // TODO(dustingreen): Implement interface methods for: - // audio encoder - // video encoder - // (or combined) - - private: - // We let CreateSelfOwned() deal with setting up the binding_ directly, which - // means the constructor doesn't need to stash the - // InterfaceRequest<CodecFactory> - LocalCodecFactory(async_dispatcher_t* fidl_dispatcher, thrd_t fidl_thread); - - void CreateCommon( - ::fidl::InterfaceRequest<fuchsia::media::StreamProcessor> codec_request, - fuchsia::mediacodec::CodecType codec_type, std::string mime_type, - fit::function<void(codec_runner::CodecRunner* codec_runner)> - set_type_specific_params); - - // Some combinations of mime type and codec lib need a wrapper to compensate - // for the OMX lib's behavior - to ensure that the overall Codec served by - // this process conforms to the Codec interface rules. For now this is - // primarily about the OMX AAC decoder lib not dealing with split ADTS - // headers, which the Codec interface requires. - struct CodecStrategy { - fuchsia::mediacodec::CodecType codec_type; - std::string_view mime_type; - std::string_view lib_filename; - fit::function<std::unique_ptr<codec_runner::CodecRunner>( - async_dispatcher_t* dispatcher, thrd_t fidl_thread, - const CodecStrategy& codec_strategy)> - create_runner; - }; - - // Appropriate for use with any mime_type where the raw OMX codec doesn't have - // any known open issues. - static std::unique_ptr<codec_runner::CodecRunner> CreateRawOmxRunner( - async_dispatcher_t* fidl_dispatcher, thrd_t fidl_thread, - const CodecStrategy& codec_strategy); - - static std::unique_ptr<codec_runner::CodecRunner> CreateCodec( - async_dispatcher_t* fidl_dispatcher, thrd_t fidl_thread, - fuchsia::mediacodec::CodecType codec_type, std::string mime_type); - - async_dispatcher_t* fidl_dispatcher_; - thrd_t fidl_thread_ = 0; - - // The LocalCodecFactory instance is self-owned via binding_: - typedef fidl::Binding<CodecFactory, std::unique_ptr<LocalCodecFactory>> - BindingType; - std::unique_ptr<BindingType> binding_; - - static CodecStrategy codec_strategies[]; -}; - -} // namespace codec_factory - -#endif // GARNET_BIN_MEDIA_CODECS_SW_OMX_CODEC_RUNNER_SW_OMX_LOCAL_CODEC_FACTORY_H_ diff --git a/garnet/bin/media/codecs/sw/omx/codec_runner_sw_omx/main.cc b/garnet/bin/media/codecs/sw/omx/codec_runner_sw_omx/main.cc deleted file mode 100644 index 51fb807801fa2d55966038ee2d323df165b296aa..0000000000000000000000000000000000000000 --- a/garnet/bin/media/codecs/sw/omx/codec_runner_sw_omx/main.cc +++ /dev/null @@ -1,57 +0,0 @@ -// 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/time.h> - -#include "lib/fidl/cpp/binding.h" - -#include "codec_runner_component.h" - -// For now, this executable only knows about OMX .so libs (essentially as data -// deps), and won't load any others. -// -// The .so interface used between this executable and OMX .so libs is not part -// of the OMX standard, but it does stick to OMX C interfaces for the most part. -// -// The AOSP OMX codecs are just a convenient set of codecs to use as proof of -// concept. The CodecFactory and Codec interfaces are more relevant system-wide -// than the OMX interfaces. The OMX interfaces are used only in this -// executable. -// -// This executable serves up to one CodecFactory instance, only as a secondary -// implementation, with many assumptions re. the main CodecFactory's way of -// calling the secondary CodecFactory. This process's CodecFactory interface is -// only served to the main CodecFactory, not to the client of the main -// CodecFactory. -// -// This executable's CodecFactory is used by the main CodecFactory -// implementation to create up to one Codec instance which is directly served in -// the local process, backed by an OMX codec instance, and served to the end -// client of the main CodecFactory. For this reason, in contrast to the -// CodecFactory implementation which can make some simplifying interface usage -// assumptions, the Codec interface served by this process must be complete. - -void usage(char* binary_name) { printf("usage: %s\n", binary_name); } - -int main(int argc, char* argv[]) { - if (argc != 1) { - usage(argv[0]); - exit(-1); - } - - async::Loop loop(&kAsyncLoopConfigAttachToThread); - - std::unique_ptr<component::StartupContext> startup_context = - component::StartupContext::CreateFromStartupInfo(); - - codec_runner::CodecRunnerComponent codec_runner( - loop.dispatcher(), thrd_current(), std::move(startup_context)); - - loop.Run(); - - return 0; -} diff --git a/garnet/bin/media/codecs/sw/omx/codec_runner_sw_omx/omx_codec_runner.cc b/garnet/bin/media/codecs/sw/omx/codec_runner_sw_omx/omx_codec_runner.cc deleted file mode 100644 index f1e84adc72ceea7d74e59aab7fe05a96daeb7f46..0000000000000000000000000000000000000000 --- a/garnet/bin/media/codecs/sw/omx/codec_runner_sw_omx/omx_codec_runner.cc +++ /dev/null @@ -1,3637 +0,0 @@ -// 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 "omx_codec_runner.h" - -#include "so_entry_point.h" - -#include <dlfcn.h> -#include "OMX_Core.h" - -#include <lib/async-loop/cpp/loop.h> -#include <lib/async/cpp/task.h> -#include <lib/async/cpp/time.h> -#include <lib/fit/defer.h> -#include <src/lib/fxl/arraysize.h> -#include <src/lib/fxl/logging.h> -#include <lib/zx/vmo.h> -#include <zircon/types.h> - -#include <algorithm> - -// The VLOGF() and LOGF() macros are here because we want the calls sites to -// look like FX_VLOGF and FX_LOGF, but without hard-wiring to those. For now, -// printf() seems to work fine. - -#define VLOG_ENABLED 0 - -#if (VLOG_ENABLED) -#define VLOGF(...) printf(__VA_ARGS__) -#else -#define VLOGF(...) \ - do { \ - } while (0) -#endif - -#define LOGF(...) printf(__VA_ARGS__) - -namespace { - -class ScopedUnlock { - public: - explicit ScopedUnlock(std::unique_lock<std::mutex>& unique_lock) - : unique_lock_(unique_lock) { - unique_lock_.unlock(); - } - ~ScopedUnlock() { unique_lock_.lock(); } - - private: - std::unique_lock<std::mutex>& unique_lock_; - FXL_DISALLOW_IMPLICIT_CONSTRUCTORS(ScopedUnlock); -}; - -// Used within ScopedUnlock only. Normally we'd just leave a std::unique_lock -// locked until it's destructed. -class ScopedRelock { - public: - explicit ScopedRelock(std::unique_lock<std::mutex>& unique_lock) - : unique_lock_(unique_lock) { - unique_lock_.lock(); - } - ~ScopedRelock() { unique_lock_.unlock(); } - - private: - std::unique_lock<std::mutex>& unique_lock_; - FXL_DISALLOW_IMPLICIT_CONSTRUCTORS(ScopedRelock); -}; - -// The protocol does not permit an unbounded number of in-flight streams, as -// that would potentially result in unbounded data queued in the incoming -// channel with no valid circuit-breaker value for the incoming channel data. -constexpr size_t kMaxInFlightStreams = 10; -// Input constraints always have version ordinal 1 because version 0 isn't a -// valid ordinal (to simplify initial state handling) and there's only ever one -// version. -constexpr uint64_t kInputBufferConstraintsVersionOrdinal = 1; -// This is fairly arbitrary, but avoid recommending buffers that are pointlessly -// large. This is subject to change. -constexpr uint32_t kOmxRecommendedBufferVsMinBufferFactor = 1; -// This is fairly arbitrary. This is subject to change. Note that this places -// a constraint on the max vs. min at Codec layer, not the max at OMX layer, -// because at the OMX layer the nBufferSize is virtualized fairly heavily in -// single-buffer mode, so the OMX layer max nBufferSize value can become much -// larger than this factor (vs the initial value of nBufferSize). -constexpr uint32_t kOmxMaxBufferVsMinBufferFactor = 5; - -// This does not auto-add any buffers for client use or for performance, and we -// don't want to have every layer adding more buffer count for such reasons, so -// pass through the nBufferCountMin as the min and the recommended number. -constexpr uint32_t kOmxRecommendedBufferCountVsMinBufferCountFactor = 1; -// More than 3 times the min is probably pointless. This is fairly arbitrary. -constexpr uint32_t kOmxRecommendedMaxBufferCountVsMinBufferCountFactor = 3; -// This is fairly arbitrary. -constexpr uint32_t kOmxMaxBufferCountVsMinBufferCountFactor = 5; - -// These are packet_count based, so 0 means one beyond the last normal packet -// index, 1 means 2 beyond the last normal packet index. OMX knows about these -// packets (as OMX buffers) but the Codec client does not. -constexpr uint32_t kHiddenInputPacketIndexOffsetOob = 0; -constexpr uint32_t kHiddenInputPacketIndexOffsetEos = 1; -constexpr uint32_t kHiddenInputPacketCount = 2; - -// For input, we only send OnInputBufferSettings() once at the very beginning, -// so for now it makes sense (barely) to help the client select the client's -// buffer_lifetime_ordinal. -constexpr uint32_t kBestFirstBufferLifetimeOrdinal = 1; -// For output, don't try to help the client count from the wrong end of the -// channel. At best this would be of marginal value to simple clients and at -// worst it would lead to an expectation that the server knows what -// buffer_lifetime_ordinal values the client has used so far which the server -// has no way of knowing at any given instant. -constexpr uint32_t kInvalidDefaultBufferLifetimeOrdinal = 0; - -constexpr fuchsia::media::AudioChannelId - kOmxAudioChannelTypeToAudioChannelId[] = { - fuchsia::media::AudioChannelId::SKIP, // OMX_AUDIO_ChannelNone - fuchsia::media::AudioChannelId::LF, // OMX_AUDIO_ChannelLF - fuchsia::media::AudioChannelId::RF, // OMX_AUDIO_ChannelRF - fuchsia::media::AudioChannelId::CF, // OMX_AUDIO_ChannelCF - fuchsia::media::AudioChannelId::LS, // OMX_AUDIO_ChannelLS - fuchsia::media::AudioChannelId::RS, // OMX_AUDIO_ChannelRS - fuchsia::media::AudioChannelId::LFE, // OMX_AUDIO_ChannelLFE - fuchsia::media::AudioChannelId::CS, // OMX_AUDIO_ChannelCS - fuchsia::media::AudioChannelId::LR, // OMX_AUDIO_ChannelLR - fuchsia::media::AudioChannelId::RR, // OMX_AUDIO_ChannelRR -}; -// We do allow translating OMX_AUDIO_ChannelNone ("unused or empty") to Skip. -constexpr uint32_t kOmxAudioChannelTypeSupportedMin = 0; -constexpr uint32_t kOmxAudioChannelTypeSupportedMax = 9; - -uint32_t PacketCountFromPortSettings( - const fuchsia::media::StreamBufferSettings& settings) { - ZX_DEBUG_ASSERT(settings.has_packet_count_for_client()); - ZX_DEBUG_ASSERT(settings.has_packet_count_for_server()); - return settings.packet_count_for_server() + - settings.packet_count_for_client(); -} - -uint32_t BufferCountFromPortSettings( - const fuchsia::media::StreamBufferSettings& settings) { - ZX_DEBUG_ASSERT(settings.has_single_buffer_mode()); - if (settings.single_buffer_mode()) { - return 1; - } - return PacketCountFromPortSettings(settings); -} - -template <typename OMX_STRUCT> -void InitOmxStruct(OMX_STRUCT* omx_struct) { - memset(omx_struct, 0, sizeof(*omx_struct)); - omx_struct->nSize = sizeof(*omx_struct); - // Same as in SoftOMXComponent.cpp. - omx_struct->nVersion.s.nVersionMajor = 1; - omx_struct->nVersion.s.nVersionMinor = 0; - omx_struct->nVersion.s.nRevision = 0; - omx_struct->nVersion.s.nStep = 0; -} - -} // namespace - -namespace codec_runner { - -OmxCodecRunner::OmxCodecRunner(async_dispatcher_t* fidl_dispatcher, - thrd_t fidl_thread, std::string_view mime_type, - std::string_view lib_filename) - : CodecRunner(fidl_dispatcher, fidl_thread), - mime_type_(mime_type), - lib_filename_(lib_filename) { - // nothing else to do here -} - -// -// CodecRunner -// - -bool OmxCodecRunner::Load() { - // Load the per-omx-codec .so and find the one entry point. - createSoftOMXComponent_fn createSoftOMXComponent = nullptr; - void* dl = dlopen(lib_filename_.c_str(), RTLD_NOW | RTLD_LOCAL); - if (!dl) { - printf("dl is nullptr\n"); - return false; - } - VLOGF("loaded codec .so file.\n"); - createSoftOMXComponent = reinterpret_cast<createSoftOMXComponent_fn>( - dlsym(dl, "entrypoint_createSoftOMXComponent")); - if (!createSoftOMXComponent) { - printf("dlsym() failed.\n"); - return false; - } - VLOGF("found entrypoint.\n"); - - // This lock hold interval isn't really needed, but it also doesn't hurt, and - // it might make it easier to get FXL_GUARDED_BY annotations to work. - std::unique_lock<std::mutex> lock(lock_); - - omx_callbacks_.EventHandler = omx_EventHandler; - omx_callbacks_.EmptyBufferDone = omx_EmptyBufferDone; - omx_callbacks_.FillBufferDone = omx_FillBufferDone; - auto app_data = reinterpret_cast<OMX_PTR>(this); - // The direct_ version bypasses the .so stuff above, because it's an easier - // workflow if we don't have to replace two files, for now, until we figure - // out if there's a clean way to load an .so from an arbitrary file - like - // with dlopen_vmo or something maybe... Or with a different config that lets - // dlopen() find our .so in places other than /system/lib, which is currently - // hard to replace. Maybe package server stuff will make this easier soon. - createSoftOMXComponent("OMX.google.aac.decoder", &omx_callbacks_, app_data, - &omx_component_); - if (!omx_component_) { - printf("failed to create component_\n"); - return false; - } - VLOGF("successfully created omx_component_\n"); - - OMX_ERRORTYPE omx_result; - - // SetCallbacks() is nullptr, so apparently we don't need to call it, and the - // callbacks are passed in above, so that should do it. - - OMX_STATETYPE omx_state; - omx_result = omx_component_->GetState(omx_component_, &omx_state); - if (omx_result != OMX_ErrorNone) { - printf("omx_component->GetState() failed: %d\n", omx_result); - return false; - } - if (omx_state != OMX_StateLoaded) { - printf("unexpected OMX component state: %d\n", omx_state); - return false; - } - assert(omx_state == OMX_StateLoaded); - VLOGF("omx_component state is: %d\n", omx_state); - // Nobody is waiting for the state to change yet, so we can just set - // omx_state_ here without notifying omx_state_changed_. - omx_state_ = omx_state; - // This is OMX_StateLoaded. - omx_state_desired_ = omx_state; - - // OMX_GetComponentVersion entry point is nullptr. - // OMX_GetConfig and OMX_SetConfig just return OMX_ErrorUndefined. - - // Find input port and output port indexes. - // - // Also check that there are the expected number of ports, though this is - // slightly indirect and approximate given the lack of any direct way that I - // can find to check this via OMX. - OMX_PARAM_PORTDEFINITIONTYPE port_def; - for (int i = 0; i < 3; i++) { - InitOmxStruct(&port_def); - port_def.nPortIndex = i; - omx_result = omx_component_->GetParameter( - omx_component_, OMX_IndexParamPortDefinition, &port_def); - // check for errors differently depending on whether port index 2 or less - // than 2 - if (i != 2) { - if (omx_result != OMX_ErrorNone) { - printf("component_->GetParameter() failed: %d\n", omx_result); - return false; - } - if (port_def.eDir == OMX_DirInput) { - omx_port_index_[kInput] = i; - } else if (port_def.eDir == OMX_DirOutput) { - omx_port_index_[kOutput] = i; - } else { - printf("unexpected port_def.eDir: %d\n", port_def.eDir); - return false; - } - } else { - assert(i == 2); - // Avoid caring which specific error is return for port index 2, but it - // shouldn't succeed. - if (omx_result == OMX_ErrorNone) { - // For now, bail out if we don't find exactly two ports. There might - // be reasonable ways to deal with exceptions to this, but until we have - // an example of a codec that has more than two ports, postpone handling - // it. - printf("more than two ports found\n"); - return false; - } - } - } - if (omx_port_index_[kInput] == 0xFFFFFFFF) { - printf("failed to find input port\n"); - return false; - } - if (omx_port_index_[kOutput] == 0xFFFFFFFF) { - printf("failed to find output port\n"); - return false; - } - - VLOGF("input_port_index_: %d\n", omx_port_index_[kInput]); - VLOGF("output_port_index_: %d\n", omx_port_index_[kOutput]); - - // The default behavior is fine, since we don't need this to be the default - // loop for any thread. - // - // Go ahead and get the StreamControl domain's thread created and started, but - // its first item will be to wait for the Setup ordering domain to be done, - // which prevents any overlap between Setup items and StreamControl items. - // - // The StreamControl thread is allowed to block. - stream_control_ = - std::make_unique<async::Loop>(&kAsyncLoopConfigNoAttachToThread); - zx_status_t start_thread_result = stream_control_->StartThread( - "StreamControl_ordering_domain", &stream_control_thread_); - if (start_thread_result != ZX_OK) { - printf("stream_control_->StartThread() failed\n"); - return false; - } - stream_control_dispatcher_ = stream_control_->dispatcher(); - PostSerial(stream_control_dispatcher_, [this] { - { // scope lock - std::unique_lock<std::mutex> lock(lock_); - while (!is_setup_done_) { - // We don't share this process across Codec instances, so currently we - // don't need a way to give up here, short of exiting the whole process. - is_setup_done_condition_.wait(lock); - } - } - }); - - return true; -} - -// TODO(dustingreen): this method needs to understand how to translate between -// Codec and OMX for every entry in local_codec_factory.cc. That means this -// method and similar AudioEncoder/VideoDecoder/VideoEncoder methods will likely -// involve more fan-out to deal with all the formats. -// -// For now it's a non-goal to deal with formats outside the set listed in -// local_codec_factory.cc, and certainly a non-goal here to try to anticipate or -// handle any format beyond what OMX can describe. Any format future-proofing -// belongs in CodecFactory and Codec interfaces (if anywhere), but not here for -// now. -void OmxCodecRunner::SetDecoderParams( - fuchsia::mediacodec::CreateDecoder_Params audio_decoder_params) { - ZX_ASSERT(audio_decoder_params.has_input_details()); - ZX_ASSERT(audio_decoder_params.input_details().has_mime_type()); - struct AudioDecoder { - std::string_view codec_mime_type; - std::string_view omx_mime_type; - OMX_AUDIO_CODINGTYPE omx_coding_type; - void (OmxCodecRunner::*set_input_method_ptr)(); - }; - static const AudioDecoder known_audio_decoders[] = { - // TODO(dustingreen): add audio/aac soon. - {"audio/aac-adts", "audio/aac", OMX_AUDIO_CodingAAC, - &OmxCodecRunner::SetInputAacAdts}, - }; - const AudioDecoder* dec = nullptr; - for (const auto& known_audio_decoder : known_audio_decoders) { - if (known_audio_decoder.codec_mime_type == - audio_decoder_params.input_details().mime_type()) { - dec = &known_audio_decoder; - break; - } - } - // Reject up front any mime types that we don't handle at all yet. - if (!dec) { - // TODO(dustingreen): epitaph - binding_.reset(); - Exit("SetAudioDecoderParams() couldn't find a suitable decoder"); - } - - decoder_params_ = std::make_unique<fuchsia::mediacodec::CreateDecoder_Params>( - std::move(audio_decoder_params)); - initial_input_format_details_ = fuchsia::media::FormatDetails::New(); - zx_status_t clone_result = decoder_params_->input_details().Clone( - initial_input_format_details_.get()); - if (clone_result != ZX_OK) { - Exit("FormatDetails::Clone() failed - exiting"); - } - - // For the moment, let's check that the input is AAC. - // - // TODO(dustingreen): Do this generically across all codecs, probably based on - // fields in a built-in codec table. - InitOmxStruct(&omx_initial_port_def_[kInput]); - omx_initial_port_def_[kInput].nPortIndex = omx_port_index_[kInput]; - OMX_ERRORTYPE omx_result = - omx_component_->GetParameter(omx_component_, OMX_IndexParamPortDefinition, - &omx_initial_port_def_[kInput]); - if (omx_result != OMX_ErrorNone) { - Exit("omx_result->GetParameter(port def, input port) failed: %d\n", - omx_result); - } - if (omx_initial_port_def_[kInput].eDomain != OMX_PortDomainAudio) { - Exit("unexpected input port eDomain: %d\n", - omx_initial_port_def_[kInput].eDomain); - } - if (omx_initial_port_def_[kInput].format.audio.cMIMEType != - dec->omx_mime_type) { - Exit("unexpected input port mime type: %s\n", - omx_initial_port_def_[kInput].format.audio.cMIMEType); - } - if (omx_initial_port_def_[kInput].format.audio.eEncoding != - dec->omx_coding_type) { - Exit("unexpected input port format.audio.eEncoding: %d\n", - omx_initial_port_def_[kInput].format.audio.eEncoding); - } - if (omx_initial_port_def_[kInput].nBufferAlignment != 1) { - Exit("unexpected input buffer alignment: %d\n", - omx_initial_port_def_[kInput].nBufferAlignment); - } - - // For audio decoders, let's check that the output is PCM. - // - // TODO(dustingreen): Do this generically across all codecs, probably based on - // fields in a built-in codec table. - InitOmxStruct(&omx_initial_port_def_[kOutput]); - omx_initial_port_def_[kOutput].nPortIndex = omx_port_index_[kOutput]; - omx_result = - omx_component_->GetParameter(omx_component_, OMX_IndexParamPortDefinition, - &omx_initial_port_def_[kOutput]); - if (omx_result != OMX_ErrorNone) { - Exit("omx_component->GetParameter(port def, output port) failed: %d\n", - omx_result); - } - if (omx_initial_port_def_[kOutput].eDomain != OMX_PortDomainAudio) { - Exit("unexpected output port eDomain: %d\n", - omx_initial_port_def_[kOutput].eDomain); - } - std::string expected_output_mime("audio/raw"); - if (expected_output_mime != - omx_initial_port_def_[kOutput].format.audio.cMIMEType) { - Exit("unexpected output port mime type: %s\n", - omx_initial_port_def_[kOutput].format.audio.cMIMEType); - } - if (omx_initial_port_def_[kOutput].format.audio.eEncoding != - OMX_AUDIO_CodingPCM) { - Exit("unexpected output port format.audio.eEncoding: %d\n", - omx_initial_port_def_[kOutput].format.audio.eEncoding); - } - if (omx_initial_port_def_[kOutput].nBufferAlignment != 2) { - Exit("unexpected output buffer alignment: %d\n", - omx_initial_port_def_[kOutput].nBufferAlignment); - } - - for (Port port = kFirstPort; port < kPortCount; port++) { - // intentional copy - omx_port_def_[port] = omx_initial_port_def_[port]; - } - - // Handle per-format parameter setting. Method call to a method like - // SetInputAacAdts() or analogous method. - (this->*(dec->set_input_method_ptr))(); - - // next is ComputeInputConstraints() -} - -// Set the AAC decoder to ADTS mode. -void OmxCodecRunner::SetInputAacAdts() { - OMX_AUDIO_PARAM_AACPROFILETYPE aac_profile; - InitOmxStruct(&aac_profile); - aac_profile.nPortIndex = omx_port_index_[kInput]; - OMX_ERRORTYPE omx_result = omx_component_->GetParameter( - omx_component_, OMX_IndexParamAudioAac, &aac_profile); - if (omx_result != OMX_ErrorNone) { - Exit("omx_component->GetParameter(input, aac profile) failed: %d", - omx_result); - } - // For now, we won't strip off the ADTS-ness from the input .adts file, so put - // the AAC decoder in ADTS mode. - aac_profile.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4ADTS; - omx_result = omx_component_->SetParameter( - omx_component_, OMX_IndexParamAudioAac, &aac_profile); - if (omx_result != OMX_ErrorNone) { - Exit("omx_component->SetParameter(input, ADTS) failed: %d\n", omx_result); - } -} - -// This is called before the Codec channel is bound, so this class is still -// single-threaded during this method. -void OmxCodecRunner::ComputeInputConstraints() { - // The OMX info we need to generate input_constraints_ is all in - // omx_initial_port_def_[kInput] + omx_min_nBufferSize_[kInput] and was - // already queried from the OMX codec previously. - // - // The kInputBufferConstraintsVersionOrdinal is required in the sense that it - // is the only one and also required, vs output which has both required and - // not-required buffer_constraints_version_ordinal(s). Input has this only - // to allow sharing more code with output. - last_required_buffer_constraints_version_ordinal_[kInput] = - kInputBufferConstraintsVersionOrdinal; - sent_buffer_constraints_version_ordinal_[kInput] = - kInputBufferConstraintsVersionOrdinal; - OMX_U32 omx_min_buffer_size = omx_initial_port_def_[kInput].nBufferSize; - uint32_t packet_count_for_server_recommended = - kOmxRecommendedBufferCountVsMinBufferCountFactor * - omx_initial_port_def_[kInput].nBufferCountMin; - uint32_t per_packet_buffer_bytes_recommended = - kOmxRecommendedBufferVsMinBufferFactor * omx_min_buffer_size; - fuchsia::media::StreamBufferConstraints constraints; - constraints.set_buffer_constraints_version_ordinal( - kInputBufferConstraintsVersionOrdinal); - constraints.set_per_packet_buffer_bytes_min(omx_min_buffer_size); - constraints.set_per_packet_buffer_bytes_recommended( - per_packet_buffer_bytes_recommended); - constraints.set_per_packet_buffer_bytes_max(kOmxMaxBufferVsMinBufferFactor * - omx_min_buffer_size); - constraints.set_packet_count_for_server_min( - omx_initial_port_def_[kInput].nBufferCountMin); - constraints.set_packet_count_for_server_recommended( - packet_count_for_server_recommended); - constraints.set_packet_count_for_server_recommended_max( - kOmxRecommendedMaxBufferCountVsMinBufferCountFactor * - omx_initial_port_def_[kInput].nBufferCountMin); - constraints.set_packet_count_for_server_max( - kOmxMaxBufferCountVsMinBufferCountFactor * - omx_initial_port_def_[kInput].nBufferCountMin); - constraints.set_packet_count_for_client_max( - std::numeric_limits<uint32_t>::max()); - // TODO(dustingreen): verify that this works end to end for the - // OmxCodecRunner... - constraints.set_single_buffer_mode_allowed(true); - - // default_settings - // - // Initial input buffer_lifetime_ordinal of 1 is ok. It's also - // ok if it's any larger odd number, but 1 is the best choice. - constraints.mutable_default_settings()->set_buffer_lifetime_ordinal( - kBestFirstBufferLifetimeOrdinal); - // The buffer_constraints_version_ordinal is a pass-through value - // so clients will have no reason to change this - it's just so - // the server knows what version of constraints the client was - // aware of so far. - constraints.mutable_default_settings() - ->set_buffer_constraints_version_ordinal( - kInputBufferConstraintsVersionOrdinal); - constraints.mutable_default_settings()->set_packet_count_for_server( - packet_count_for_server_recommended); - constraints.mutable_default_settings()->set_packet_count_for_client( - ::fuchsia::media::kDefaultInputPacketCountForClient); - constraints.mutable_default_settings()->set_per_packet_buffer_bytes( - per_packet_buffer_bytes_recommended); - constraints.mutable_default_settings()->set_single_buffer_mode( - ::fuchsia::media::kDefaultInputIsSingleBufferMode); - input_constraints_ = - std::make_unique<fuchsia::media::StreamBufferConstraints>( - std::move(constraints)); - - // We're about to be bound to the Codec channel, which will immediately send - // the input_constraints_ to the client as the first server to client message. -} - -// -// Codec -// - -// The base class is about to send input_constraints_ using -// OnInputConstraints(). Since OMX codecs demand to have output buffers -// configured before generating OMX_EventPortSettingsChanged on the output port, -// and because OMX codecs can potentially not generate that event and just -// output into the initial buffers instead, and because this class doesn't -// virtualize that away with a bunch of memcpy + complicated tracking that would -// be required, the OmxCodecRunner will want to send the output constraints -// asap, which is when this method gets called. -// -// We want to send this _before_ the input constraints to encourage the client -// to configure output before queueing any input data for the first stream, else -// we can end up triggering another output re-config. -// -// This is called on the FIDL thread, but we post any sent messages back to the -// FIDL thread to be sent on a clean thread without lock_ held anyway. -void OmxCodecRunner::onInputConstraintsReady() { - std::unique_lock<std::mutex> lock(lock_); - StartIgnoringClientOldOutputConfigLocked(); - GenerateAndSendNewOutputConfig(lock, true); - - // Next is the client sending SetInputBufferSettings()+AddInputBuffer() or - // SetOutputBufferSettings()+AddOutputBuffer(). Preferably the latter first, - // but either is permitted. -} - -void OmxCodecRunner::GenerateAndSendNewOutputConfig( - std::unique_lock<std::mutex>& lock, - bool buffer_constraints_action_required) { - // This method is only called on these ordering domains: - // * Setup ordering domain - // * StreamControl ordering domain - // * InputData domain if buffer_constraints_action_required is false - // - // TODO(dustingreen): Create an assert that checks for the above. This - // commented-out assert doesn't include possibility of InputData domain: - // assert(!is_setup_done_ || thrd_current() == stream_control_thread_); - - uint64_t current_stream_lifetime_ordinal = stream_lifetime_ordinal_; - uint64_t new_output_buffer_constraints_version_ordinal = - next_output_buffer_constraints_version_ordinal_++; - uint64_t new_output_format_details_version_ordinal = - next_output_format_details_version_ordinal_++; - - // If buffer_constraints_action_required true, the caller bumped the - // last_required_buffer_constraints_version_ordinal_[kOutput] before calling - // this method (using StartIgnoringClientOldOutputConfigLocked()), to ensure - // any output config messages from the client are ignored until the client - // catches up to at least last_required_buffer_constraints_version_ordinal_. - assert(!buffer_constraints_action_required || - (last_required_buffer_constraints_version_ordinal_[kOutput] == - new_output_buffer_constraints_version_ordinal)); - - // printf("GenerateAndSendNewOutputConfig - // new_output_buffer_constraints_version_ordinal: %lu - // buffer_constraints_action_required: %d\n", - // new_output_buffer_constraints_version_ordinal, - // buffer_constraints_action_required); - - std::unique_ptr<const fuchsia::media::StreamOutputConfig> output_config; - { // scope unlock - ScopedUnlock unlock(lock); - // Don't call OMX under the lock_, because we can avoid doing so, and - // because of paranoia that OMX might call EventHandler() at any time using - // the same stack that we call OMX on - it's only partly paranoia, since OMX - // _does_ do that sometimes, for some calls into OMX - so assume that's the - // contract for all calls into OMX. - - // We know we're the only thread calling this currently, because this method - // is part of the Setup ordering domain and the onSetupDone() method - // prevents any overlap between Setup and StreamControl. - output_config = - BuildNewOutputConfig(current_stream_lifetime_ordinal, - new_output_buffer_constraints_version_ordinal, - new_output_format_details_version_ordinal, - buffer_constraints_action_required); - } // ~unlock - assert(current_stream_lifetime_ordinal == stream_lifetime_ordinal_); - - output_config_ = std::move(output_config); - - // Stay under lock after setting output_config_, to get proper ordering of - // sent messages even if a hostile client deduces the content of this message - // before we've sent it and manages to get the server to send another - // subsequent OnOutputConfig(). - - assert(sent_buffer_constraints_version_ordinal_[kOutput] + 1 == - new_output_buffer_constraints_version_ordinal); - assert(sent_format_details_version_ordinal_[kOutput] + 1 == - new_output_format_details_version_ordinal); - - // Setting this within same lock hold interval as we queue the message to be - // sent in order vs. other OnOutputConfig() messages. This way we can verify - // that the client's incoming messages are not trying to configure with - // respect to a buffer_constraints_version_ordinal that is newer than we've - // actually sent the client. - sent_buffer_constraints_version_ordinal_[kOutput] = - new_output_buffer_constraints_version_ordinal; - sent_format_details_version_ordinal_[kOutput] = - new_output_format_details_version_ordinal; - - // Intentional copy of fuchsia::media::StreamOutputConfig output_config_ here, - // as we want output_config_ to remain valid (at least for debugging reasons - // for now). - fuchsia::media::StreamOutputConfig config_copy; - zx_status_t clone_status = output_config_->Clone(&config_copy); - if (clone_status != ZX_OK) { - Exit("StreamOutputConfig::Clone() failed - exiting - status: %d\n", - clone_status); - } - VLOGF("GenerateAndSendNewOutputConfig() - fidl_dispatcher_: %p\n", - fidl_dispatcher_); - PostSerial(fidl_dispatcher_, - [this, output_config = std::move(config_copy)]() mutable { - binding_->events().OnOutputConfig(std::move(output_config)); - }); -} - -void OmxCodecRunner::onSetupDone() { - { // scope lock - std::unique_lock<std::mutex> lock(lock_); - is_setup_done_ = true; - } // ~lock - is_setup_done_condition_.notify_all(); -} - -// The only valid caller of this is EnsureStreamClosed(). We have this in a -// separate method only to make it easier to assert a couple things in the -// caller. -void OmxCodecRunner::EnsureCodecStreamClosedLockedInternal() { - assert(thrd_current() == stream_control_thread_); - if (stream_lifetime_ordinal_ % 2 == 0) { - // Already closed. - return; - } - assert(stream_queue_.front()->stream_lifetime_ordinal() == - stream_lifetime_ordinal_); - stream_ = nullptr; - stream_queue_.pop_front(); - stream_lifetime_ordinal_++; - // Even values mean no current stream. - assert(stream_lifetime_ordinal_ % 2 == 0); -} - -// This is called on Output ordering domain (FIDL thread) any time a message is -// received which would be able to start a new stream. -// -// More complete protocol validation happens on StreamControl ordering domain. -// The validation here is just to validate to degree needed to not break our -// stream_queue_ and future_stream_lifetime_ordinal_. -void OmxCodecRunner::EnsureFutureStreamSeenLocked( - uint64_t stream_lifetime_ordinal) { - if (future_stream_lifetime_ordinal_ == stream_lifetime_ordinal) { - return; - } - if (stream_lifetime_ordinal < future_stream_lifetime_ordinal_) { - Exit("stream_lifetime_ordinal went backward - exiting\n"); - } - assert(stream_lifetime_ordinal > future_stream_lifetime_ordinal_); - if (future_stream_lifetime_ordinal_ % 2 == 1) { - EnsureFutureStreamCloseSeenLocked(future_stream_lifetime_ordinal_); - } - future_stream_lifetime_ordinal_ = stream_lifetime_ordinal; - stream_queue_.push_back(std::make_unique<Stream>(stream_lifetime_ordinal)); - if (stream_queue_.size() > kMaxInFlightStreams) { - Exit( - "kMaxInFlightStreams reached - clients capable of causing this are " - "instead supposed to wait/postpone to prevent this from occurring - " - "exiting\n"); - } -} - -// This is called on Output ordering domain (FIDL thread) any time a message is -// received which would close a stream. -// -// More complete protocol validation happens on StreamControl ordering domain. -// The validation here is just to validate to degree needed to not break our -// stream_queue_ and future_stream_lifetime_ordinal_. -void OmxCodecRunner::EnsureFutureStreamCloseSeenLocked( - uint64_t stream_lifetime_ordinal) { - if (future_stream_lifetime_ordinal_ % 2 == 0) { - // Already closed. - if (stream_lifetime_ordinal != future_stream_lifetime_ordinal_ - 1) { - Exit( - "CloseCurrentStream() seen with stream_lifetime_ordinal != " - "most-recent seen stream - exiting\n"); - } - return; - } - if (stream_lifetime_ordinal != future_stream_lifetime_ordinal_) { - Exit( - "attempt to close a stream other than the latest seen stream - " - "exiting\n"); - } - assert(stream_lifetime_ordinal == future_stream_lifetime_ordinal_); - assert(stream_queue_.size() >= 1); - Stream* closing_stream = stream_queue_.back().get(); - assert(closing_stream->stream_lifetime_ordinal() == stream_lifetime_ordinal); - // It is permitted to see a FlushCurrentStream() before a CloseCurrentStream() - // and this can make sense if a client just wants to inform the server of all - // stream closes, or if the client wants to release_input_buffers or - // release_output_buffers after the flush is done. - // - // If we didn't previously flush, then this close is discarding. - if (!closing_stream->future_flush_end_of_stream()) { - closing_stream->SetFutureDiscarded(); - } - future_stream_lifetime_ordinal_++; - assert(future_stream_lifetime_ordinal_ % 2 == 0); -} - -// This is called on Output ordering domain (FIDL thread) any time a flush is -// seen. -// -// More complete protocol validation happens on StreamControl ordering domain. -// The validation here is just to validate to degree needed to not break our -// stream_queue_ and future_stream_lifetime_ordinal_. -void OmxCodecRunner::EnsureFutureStreamFlushSeenLocked( - uint64_t stream_lifetime_ordinal) { - if (stream_lifetime_ordinal != future_stream_lifetime_ordinal_) { - Exit( - "FlushCurrentStream() stream_lifetime_ordinal inconsistent - " - "exiting\n"); - } - assert(stream_queue_.size() >= 1); - Stream* flushing_stream = stream_queue_.back().get(); - // Thanks to the above future_stream_lifetime_ordinal_ check, we know the - // future stream is not discarded yet. - assert(!flushing_stream->future_discarded()); - if (flushing_stream->future_flush_end_of_stream()) { - Exit("FlushCurrentStream() used twice on same stream - exiting\n"); - } - - // We don't future-verify that we have a QueueInputEndOfStream(). We'll verify - // that later when StreamControl catches up to this stream. - - // Remember the flush so we later know that a close doesn't imply discard. - flushing_stream->SetFutureFlushEndOfStream(); - - // A FlushEndOfStreamAndCloseStream() is also a close, after the flush. This - // keeps future_stream_lifetime_ordinal_ consistent. - EnsureFutureStreamCloseSeenLocked(stream_lifetime_ordinal); -} - -// Caller must ensure that this is called only on one thread at a time. -std::unique_ptr<const fuchsia::media::StreamOutputConfig> -OmxCodecRunner::BuildNewOutputConfig( - uint64_t stream_lifetime_ordinal, - uint64_t new_output_buffer_constraints_version_ordinal, - uint64_t new_output_format_details_version_ordinal, - bool buffer_constraints_action_required) { - return CreateNewOutputConfigFromOmxOutputFormat( - OmxGetOutputFormat(), stream_lifetime_ordinal, - new_output_buffer_constraints_version_ordinal, - new_output_format_details_version_ordinal, - buffer_constraints_action_required); -} - -// Caller must ensure that this is called only on one thread at a time. -std::unique_ptr<const fuchsia::media::StreamOutputConfig> -OmxCodecRunner::CreateNewOutputConfigFromOmxOutputFormat( - std::unique_ptr<const OmxCodecRunner::OMX_GENERIC_PORT_FORMAT> - omx_output_format, - uint64_t stream_lifetime_ordinal, - uint64_t new_output_buffer_constraints_version_ordinal, - uint64_t new_output_format_details_version_ordinal, - bool buffer_constraints_action_required) { - // Unfortunately OMX only allows nBufferSize to increase, never decrease, so - // we have to convey that to the output constraints also, since we don't have - // any per-omx-buffer-lifetime way of reducing how much output data might be - // generated per output buffer. So we really are stuck with a min that's - // whatever OMX's nBufferSize is so far. For input the situation is - // different since we can control how many valid bytes per input buffer - // lifetime. - OMX_U32 per_packet_buffer_bytes_min = - omx_output_format->definition.nBufferSize; - const OMX_PARAM_PORTDEFINITIONTYPE& port = omx_output_format->definition; - uint32_t per_packet_buffer_bytes_recommended = - kOmxRecommendedBufferVsMinBufferFactor * per_packet_buffer_bytes_min; - uint32_t packet_count_for_server_recommended = - kOmxRecommendedBufferCountVsMinBufferCountFactor * port.nBufferCountMin; - fuchsia::media::FormatDetails format_details; - - format_details.set_format_details_version_ordinal( - new_output_format_details_version_ordinal); - fuchsia::media::StreamOutputConfig config; - auto* constraints = config.mutable_buffer_constraints(); - auto* default_settings = constraints->mutable_default_settings(); - - config.set_stream_lifetime_ordinal(stream_lifetime_ordinal_); - config.set_buffer_constraints_action_required( - buffer_constraints_action_required); - constraints->set_buffer_constraints_version_ordinal( - new_output_buffer_constraints_version_ordinal); - constraints->set_per_packet_buffer_bytes_min(per_packet_buffer_bytes_min); - constraints->set_per_packet_buffer_bytes_recommended( - per_packet_buffer_bytes_recommended); - constraints->set_per_packet_buffer_bytes_max(kOmxMaxBufferVsMinBufferFactor * - per_packet_buffer_bytes_min); - constraints->set_packet_count_for_server_min(port.nBufferCountMin); - constraints->set_packet_count_for_server_recommended( - packet_count_for_server_recommended); - constraints->set_packet_count_for_server_recommended_max( - kOmxRecommendedMaxBufferCountVsMinBufferCountFactor * - port.nBufferCountMin); - constraints->set_packet_count_for_server_max( - kOmxMaxBufferCountVsMinBufferCountFactor * port.nBufferCountMin); - constraints->set_packet_count_for_client_max( - std::numeric_limits<uint32_t>::max()); - constraints->set_single_buffer_mode_allowed(false); - - // default_settings - // - // Can't/won't help the client pick the client's - // buffer_lifetime_ordinal for output. - default_settings->set_buffer_lifetime_ordinal( - kInvalidDefaultBufferLifetimeOrdinal); - // The buffer_constraints_version_ordinal is a pass-through value - // so clients will have no reason to change this - it's just so - // the server knows what version of constraints the client was - // aware of so far. - default_settings->set_buffer_constraints_version_ordinal( - new_output_buffer_constraints_version_ordinal); - default_settings->set_packet_count_for_server( - packet_count_for_server_recommended); - default_settings->set_packet_count_for_client( - ::fuchsia::media::kDefaultOutputPacketCountForClient); - default_settings->set_per_packet_buffer_bytes( - per_packet_buffer_bytes_recommended); - default_settings->set_single_buffer_mode( - ::fuchsia::media::kDefaultOutputIsSingleBufferMode); - - config.set_format_details(std::move(format_details)); - std::unique_ptr<fuchsia::media::StreamOutputConfig> result = - std::make_unique<fuchsia::media::StreamOutputConfig>(std::move(config)); - switch (omx_output_format->definition.eDomain) { - case OMX_PortDomainAudio: - PopulateFormatDetailsFromOmxOutputFormat_Audio( - *omx_output_format.get(), result->mutable_format_details()); - break; - case OMX_PortDomainVideo: - // TODO(dustingreen): handle video format details - it likely makes - // sense to switch to the common format details FIDL struct/table - // first though. - Exit("for now, video OMX eDomain is not handled"); - break; - default: - // TODO(dustingreen): epitaph - Exit("unrecognized OMX eDomain: %d", - omx_output_format->definition.eDomain); - break; - } - return result; -} - -// Fill out everything except format_details_version_ordinal. -// -// TODO(dustingreen): handle audio encoders, which will need to fill out -// codec_oob_config based on the first output data, if available. -void OmxCodecRunner::PopulateFormatDetailsFromOmxOutputFormat_Audio( - const OmxCodecRunner::OMX_GENERIC_PORT_FORMAT& omx_output_format, - fuchsia::media::FormatDetails* format_details) { - assert(omx_output_format.definition.eDir == OMX_DirOutput); - assert(omx_output_format.definition.eDomain == OMX_PortDomainAudio); - const OMX_AUDIO_PORTDEFINITIONTYPE& omx_audio_port_def = - omx_output_format.definition.format.audio; - const OMX_AUDIO_PARAM_PORTFORMATTYPE& omx_audio_param_port_format = - omx_output_format.audio.format; - format_details->set_mime_type(omx_audio_port_def.cMIMEType); - if (omx_audio_port_def.eEncoding != omx_audio_param_port_format.eEncoding) { - Exit("inconsistent eEncoding from OMX - exiting"); - } - assert(omx_audio_port_def.eEncoding == omx_audio_param_port_format.eEncoding); - fuchsia::media::AudioFormat audio_format{}; - switch (omx_audio_param_port_format.eEncoding) { - case OMX_AUDIO_CodingPCM: { - const OMX_AUDIO_PARAM_PCMMODETYPE& omx_pcm = omx_output_format.audio.pcm; - fuchsia::media::PcmFormat pcm{}; - switch (omx_pcm.ePCMMode) { - case OMX_AUDIO_PCMModeLinear: - pcm.pcm_mode = fuchsia::media::AudioPcmMode::LINEAR; - break; - default: - Exit("unhandled OMX_AUDIO_PARAM_PCMMODETYPE.ePCMMode value: %d", - omx_pcm.ePCMMode); - break; - } - pcm.bits_per_sample = omx_pcm.nBitPerSample; - pcm.frames_per_second = omx_pcm.nSamplingRate; - std::vector<fuchsia::media::AudioChannelId> channel_map( - omx_pcm.nChannels); - for (uint32_t i = 0; i < omx_pcm.nChannels; i++) { - channel_map[i] = - AudioChannelIdFromOmxAudioChannelType(omx_pcm.eChannelMapping[i]); - } - pcm.channel_map = std::move(channel_map); - fuchsia::media::AudioUncompressedFormat uncompressed{}; - uncompressed.set_pcm(std::move(pcm)); - audio_format.set_uncompressed(std::move(uncompressed)); - } break; - case OMX_AUDIO_CodingAAC: - // TODO(dustingreen): implement, at least for AAC encode - // fallthrough for now - default: - Exit("unhandled OMX output format - value: %d", - omx_audio_param_port_format.eEncoding); - break; - } - format_details->mutable_domain()->set_audio(std::move(audio_format)); -} - -std::unique_ptr<const OmxCodecRunner::OMX_GENERIC_PORT_FORMAT> -OmxCodecRunner::OmxGetOutputFormat() { - std::unique_ptr<OMX_GENERIC_PORT_FORMAT> result = - std::make_unique<OMX_GENERIC_PORT_FORMAT>(); - // Grab all the output format info. - InitOmxStruct(&result->definition); - result->definition.nPortIndex = omx_port_index_[kOutput]; - OMX_ERRORTYPE omx_result = omx_component_->GetParameter( - omx_component_, OMX_IndexParamPortDefinition, &result->definition); - if (omx_result != OMX_ErrorNone) { - Exit("Couldn't get output port definition from OMX: %d", omx_result); - } - { // scope lock - std::unique_lock<std::mutex> lock(lock_); - // intentional copy - // - // We're stashing this structure from here because this method happens to - // be the common code path involved in all OMX updates of the output port - // definition where constraints might change which we need to pay - // attention to later. Mainly we care about nBufferSize. - omx_port_def_[kOutput] = result->definition; - } - switch (result->definition.eDomain) { - case OMX_PortDomainAudio: - InitOmxStruct(&result->audio.format); - result->audio.format.nPortIndex = omx_port_index_[kOutput]; - omx_result = omx_component_->GetParameter( - omx_component_, OMX_IndexParamAudioPortFormat, &result->audio.format); - if (omx_result != OMX_ErrorNone) { - Exit( - "GetParameter(OMX_IndexParamAudioPortFormat) failed: %d - " - "exiting\n", - omx_result); - } - switch (result->audio.format.eEncoding) { - case OMX_AUDIO_CodingPCM: - InitOmxStruct(&result->audio.pcm); - result->audio.pcm.nPortIndex = omx_port_index_[kOutput]; - omx_result = omx_component_->GetParameter( - omx_component_, OMX_IndexParamAudioPcm, &result->audio.pcm); - if (omx_result != OMX_ErrorNone) { - Exit("GetParameter(OMX_IndexParamAudioPcm) failed: %d - exiting\n", - omx_result); - } - break; - default: - Exit( - "un-handled output_port_format_.audio.format.eEncoding: %d - " - "exiting\n", - result->audio.format.eEncoding); - } - break; - case OMX_PortDomainVideo: - Exit("currently un-handled eDomain video: %d - exiting\n", - result->definition.eDomain); - default: - Exit("un-handled eDomain: %d - exiting\n", result->definition.eDomain); - } - return result; -} - -void OmxCodecRunner::EnableOnStreamFailed() { - std::unique_lock<std::mutex> lock(lock_); - enable_on_stream_failed_ = true; -} - -void OmxCodecRunner::SetInputBufferSettings( - fuchsia::media::StreamBufferSettings input_settings) { - PostSerial(stream_control_dispatcher_, - [this, input_settings = std::move(input_settings)]() mutable { - SetInputBufferSettings_StreamControl(std::move(input_settings)); - }); -} - -void OmxCodecRunner::SetInputBufferSettings_StreamControl( - fuchsia::media::StreamBufferSettings input_settings) { - assert(thrd_current() == stream_control_thread_); - { // scope lock - std::unique_lock<std::mutex> lock(lock_); - - if (!input_constraints_sent_) { - Exit( - "client sent SetInputBufferSettings() before first " - "OnInputConstraints()"); - } - - if (IsStreamActiveLocked()) { - Exit( - "client sent SetInputBufferSettings() with stream active - " - "exiting\n"); - } - - SetBufferSettingsCommonLocked(kInput, std::move(input_settings), - *input_constraints_); - } // ~lock -} - -void OmxCodecRunner::AddInputBuffer(fuchsia::media::StreamBuffer buffer) { - PostSerial(stream_control_dispatcher_, - [this, buffer = std::move(buffer)]() mutable { - AddInputBuffer_StreamControl(std::move(buffer)); - }); -} - -void OmxCodecRunner::AddInputBuffer_StreamControl( - fuchsia::media::StreamBuffer buffer) { - assert(thrd_current() == stream_control_thread_); - AddBufferCommon(kInput, std::move(buffer)); -} - -void OmxCodecRunner::SetBufferSettingsCommonLocked( - Port port, fuchsia::media::StreamBufferSettings settings, - const fuchsia::media::StreamBufferConstraints& constraints) { - // Invariant - assert((!port_settings_[port] && buffer_lifetime_ordinal_[port] == 0) || - (port_settings_[port]->has_buffer_lifetime_ordinal() && - (buffer_lifetime_ordinal_[port] >= - port_settings_[port]->buffer_lifetime_ordinal() && - buffer_lifetime_ordinal_[port] <= - port_settings_[port]->buffer_lifetime_ordinal() + 1))); - - if (!settings.has_buffer_lifetime_ordinal()) { - Exit("!settings.has_buffer_lifetime_ordinal()\n", port); - } - - if (settings.buffer_lifetime_ordinal() <= - protocol_buffer_lifetime_ordinal_[port]) { - Exit( - "settings.buffer_lifetime_ordinal <= " - "protocol_buffer_lifetime_ordinal_[port] - exiting - port: %d\n", - port); - } - protocol_buffer_lifetime_ordinal_[port] = settings.buffer_lifetime_ordinal(); - - if (settings.buffer_lifetime_ordinal() % 2 == 0) { - Exit( - "only odd values for buffer_lifetime_ordinal are permitted - exiting " - "- " - "port: %d value: %lu\n", - port, settings.buffer_lifetime_ordinal()); - } - - if (!settings.has_buffer_constraints_version_ordinal()) { - Exit("!settings.has_buffer_constraints_version_ordinal()\n", port); - } - - if (settings.buffer_constraints_version_ordinal() > - sent_buffer_constraints_version_ordinal_[port]) { - Exit( - "client sent too-new buffer_constraints_version_ordinal - exiting - " - "port: %d\n", - port); - } - - if (settings.buffer_constraints_version_ordinal() < - last_required_buffer_constraints_version_ordinal_[port]) { - // ignore - client will (probably) catch up later - return; - } - - // We've peeled off too new and too old above. - assert(settings.buffer_constraints_version_ordinal() >= - last_required_buffer_constraints_version_ordinal_[port] && - settings.buffer_constraints_version_ordinal() <= - sent_buffer_constraints_version_ordinal_[port]); - - // We've already checked above that the buffer_lifetime_ordinal is in - // sequence. - assert(!port_settings_[port] || - settings.buffer_lifetime_ordinal() > buffer_lifetime_ordinal_[port]); - - ValidateBufferSettingsVsConstraints(port, settings, constraints); - - // Regardless of mid-stream output config change or not (only relevant to - // output), we know that buffers aren't with OMX currently, so we can just - // de-ref low-layer output buffers without needing to interact with OMX - // here. - - // Little if any reason to do this outside the lock. - EnsureBuffersNotConfiguredLocked(port); - - // This also starts the new buffer_lifetime_ordinal. - port_settings_[port] = std::make_unique<fuchsia::media::StreamBufferSettings>( - std::move(settings)); - buffer_lifetime_ordinal_[port] = - port_settings_[port]->buffer_lifetime_ordinal(); -} - -void OmxCodecRunner::SetOutputBufferSettings( - fuchsia::media::StreamBufferSettings output_settings) { - { // scope lock - std::unique_lock<std::mutex> lock(lock_); - - if (!output_config_) { - // invalid client behavior - // - // client must have received at least the initial OnOutputConfig() first - // before sending SetOutputBufferSettings(). - Exit( - "client sent SetOutputBufferSettings() when no output_config_ - " - "exiting\n"); - } - - // For a mid-stream output format change, this also enforces that the - // client can only catch up to the mid-stream format change once. In - // other words, if the client has already caught up to the mid-stream - // config change, the client no longer has an excuse to re-configure again - // with a stream active. - // - // There's a check in SetBufferSettingsCommonLocked() that ignores this - // message if the client's buffer_constraints_version_ordinal is behind - // last_required_buffer_constraints_version_ordinal_, which gets updated - // under the same lock hold interval as the server's de-configuring of - // output buffers. - // - // There's a check in SetBufferSettingsCommonLocked() that closes the - // channel if the client is sending a buffer_constraints_version_ordinal - // that's newer than the last sent_buffer_constraints_version_ordinal_. - if (IsOutputConfiguredLocked() && IsStreamActiveLocked()) { - Exit( - "client sent SetOutputBufferSettings() with IsStreamActiveLocked() " - "+ " - "already-configured output"); - } - - if (!output_config_->has_buffer_constraints()) { - Exit("!output_config_->has_buffer_constraints()"); - } - - SetBufferSettingsCommonLocked(kOutput, std::move(output_settings), - output_config_->buffer_constraints()); - } // ~lock -} - -void OmxCodecRunner::AddOutputBuffer(fuchsia::media::StreamBuffer buffer) { - bool output_done_configuring = AddBufferCommon(kOutput, std::move(buffer)); - if (output_done_configuring) { - // The StreamControl domain _might_ be waiting for output to be - // configured. - wake_stream_control_.notify_all(); - } -} - -bool OmxCodecRunner::AddBufferCommon(Port port, - fuchsia::media::StreamBuffer buffer) { - bool done_configuring = false; - { // scope lock - std::unique_lock<std::mutex> lock(lock_); - - if (!buffer.has_buffer_lifetime_ordinal()) { - Exit("!buffer.has_buffer_lifetime_ordinal()"); - } - - if (buffer.buffer_lifetime_ordinal() % 2 == 0) { - Exit( - "client sent even buffer_lifetime_ordinal, but must be odd - " - "exiting " - "- port: %u\n", - port); - } - - if (buffer.buffer_lifetime_ordinal() != - protocol_buffer_lifetime_ordinal_[port]) { - Exit( - "incoherent SetOutputBufferSettings()/SetInputBufferSettings() + " - "AddOutputBuffer()/AddInputBuffer()s - exiting - port: %d\n", - port); - } - - // If the server is not interested in the client's - // buffer_lifetime_ordinal, the client's buffer_lifetime_ordinal won't - // match the server's buffer_lifetime_ordinal_. The client will probably - // later catch up. - if (buffer.buffer_lifetime_ordinal() != buffer_lifetime_ordinal_[port]) { - // The case that ends up here is when a client's output configuration - // (whole or last part) is being ignored because it's not yet caught up - // with last_required_buffer_constraints_version_ordinal_. - - // This case won't happen for input, at least for now. This is an - // assert rather than a client behavior check, because previous client - // protocol checks have already peeled off any invalid client behavior - // that might otherwise cause this assert to trigger. - assert(port == kOutput); - - // Ignore the client's message. The client will probably catch up - // later. - return false; - } - - if (!buffer.has_buffer_index()) { - Exit("!buffer.has_buffer_index()"); - } - - if (buffer.buffer_index() != all_buffers_[port].size()) { - Exit( - "AddOutputBuffer()/AddInputBuffer() had buffer_index out of " - "sequence " - "- port: %d buffer_index: %u all_buffers_[port].size(): %lu", - port, buffer.buffer_index(), all_buffers_[port].size()); - } - - uint32_t required_buffer_count = - BufferCountFromPortSettings(*port_settings_[port]); - if (buffer.buffer_index() >= required_buffer_count) { - Exit("AddOutputBuffer()/AddInputBuffer() extra buffer - port: %d", port); - } - - // So far, there's little reason to avoid doing the Init() part under the - // lock, even if it can be a bit more time consuming, since there's no - // data processing happening at this point anyway, and there wouldn't be - // any happening in any other code location where we could potentially - // move the Init() either. - - std::unique_ptr<Buffer> local_buffer = - std::make_unique<Buffer>(this, port, std::move(buffer)); - if (!local_buffer->Init()) { - Exit( - "AddOutputBuffer()/AddInputBuffer() couldn't Init() new buffer - " - "port: %d", - port); - } - all_buffers_[port].push_back(std::move(local_buffer)); - if (all_buffers_[port].size() == required_buffer_count) { - // Now we allocate all_packets_[port]. - assert(all_packets_[port].empty()); - uint32_t packet_count = - PacketCountFromPortSettings(*port_settings_[port]); - for (uint32_t i = 0; i < packet_count; i++) { - uint32_t buffer_index = required_buffer_count == 1 ? 0 : i; - Buffer* buffer = all_buffers_[port][buffer_index].get(); - assert(buffer_lifetime_ordinal_[port] == - port_settings_[port]->buffer_lifetime_ordinal()); - all_packets_[port].push_back(std::make_unique<Packet>( - port_settings_[port]->buffer_lifetime_ordinal(), i, buffer)); - } - // On input, free with client. On output, free with Codec server. - // Either way, initially free with the producer of data. - packet_free_bits_[port].resize(packet_count, true); - - // Now we allocate omx_input_packet_oob_ and omx_input_packet_eos_, if - // this is input. - if (port == kInput) { - // For the oob packet, we do need a real buffer, and it needs to be - // able to hold real (oob) data, so we have to allocate a buffer for - // this purpose server-side, since the Codec client won't be providing - // one. - // - // For now, we just allocate kMaxOobBytesSize for this (none of the - // relevant codecs need larger, and kMaxOobBytesSize is 1 page - // which is a non-zero-sized VMO's minimum size). - // - // We (in general) lie to OMX about the size being at least - // OMX_PARAM_PORTDEFINITIONTYPE.nBufferSize when allocating an OMX - // buffer for this packet, then we don't actually fill beyond - // kMaxOobBytesSize. - // - // We don't really care about OMX_PARAM_PORTDEFINITIONTYPE.nBufferSize - // aside from properly lying to OMX, since the size of normal buffers - // was never really directly relevant to how large OOB data can be. In - // other words, we don't force ourselves to support up to - // OMX_PARAM_PORTDEFINITIONTYPE.nBufferSize bytes of OOB config data, - // because there's no real value in doing so, despite OMX essentially - // sortof supporting up to that much OOB data in a - // OMX_BUFFERFLAG_CODECCONFIG buffer. - // - // If kMaxOobBytesSize isn't page size aligned, zx_vmo_create() - // will round up for us, so we don't have to handle that possibility - // here. - assert(!omx_input_buffer_oob_); - assert(!omx_input_packet_oob_); - static_assert( - fuchsia::media::kMaxOobBytesSize <= ZX_CHANNEL_MAX_MSG_BYTES, - "fuchsia::media::kMaxOobBytesSize must be <= " - "ZX_CHANNEL_MAX_MSG_BYTES"); - zx::vmo oob_vmo; - zx_status_t vmo_create_status = - zx::vmo::create(fuchsia::media::kMaxOobBytesSize, 0, &oob_vmo); - if (vmo_create_status != ZX_OK) { - Exit("zx::vmo::create() failed for omx_input_buffer_oob_"); - } - fuchsia::media::StreamBuffer oob_buffer; - oob_buffer.set_buffer_lifetime_ordinal( - port_settings_[port]->buffer_lifetime_ordinal()); - // We don't really use this for anything, so just set it to one - // beyond the last Codec protocol buffer_index, to avoid any - // ambiguity with any real buffer_index. - oob_buffer.set_buffer_index(required_buffer_count); - fuchsia::media::StreamBufferDataVmo data_vmo; - data_vmo.set_vmo_handle(std::move(oob_vmo)); - data_vmo.set_vmo_usable_start(0); - data_vmo.set_vmo_usable_size(fuchsia::media::kMaxOobBytesSize); - oob_buffer.mutable_data()->set_vmo(std::move(data_vmo)); - omx_input_buffer_oob_ = - std::make_unique<Buffer>(this, kInput, std::move(oob_buffer)); - // Unlike most input packets, the server requires the ability to write - // to this input packet's buffer. - if (!omx_input_buffer_oob_->Init(true)) { - Exit("omx_input_buffer_oob_->Init() failed"); - } - omx_input_packet_oob_ = std::make_unique<Packet>( - port_settings_[port]->buffer_lifetime_ordinal(), - packet_count + kHiddenInputPacketIndexOffsetOob, - omx_input_buffer_oob_.get()); - - // For the eos packet, we don't really need a real buffer, so we just - // share buffer 0. - assert(!omx_input_packet_eos_); - Buffer* buffer = all_buffers_[port][0].get(); - assert(buffer_lifetime_ordinal_[port] == - port_settings_[port]->buffer_lifetime_ordinal()); - omx_input_packet_eos_ = std::make_unique<Packet>( - port_settings_[port]->buffer_lifetime_ordinal(), - packet_count + kHiddenInputPacketIndexOffsetEos, buffer); - } - - // We tell OMX about the potentially-new buffer count separately later, - // just before moving from OMX loaded to OMX idle, or as part of - // mid-stream output config change. - - // We don't allocate OMX_BUFFERHEADERTYPE yet here by calling OMX - // UseBuffer() yet, because we can be in OMX_StateLoaded currently, and - // OMX UseBuffer() isn't valid until we're moving from OMX_StateLoaded - // to OMX_StateIdle. - - done_configuring = true; - } - } - return done_configuring; -} - -void OmxCodecRunner::FlushEndOfStreamAndCloseStream( - uint64_t stream_lifetime_ordinal) { - { // scope lock - std::unique_lock<std::mutex> lock(lock_); - EnsureFutureStreamFlushSeenLocked(stream_lifetime_ordinal); - } - PostSerial(stream_control_dispatcher_, [this, stream_lifetime_ordinal] { - FlushEndOfStreamAndCloseStream_StreamControl(stream_lifetime_ordinal); - }); -} - -void OmxCodecRunner::FlushEndOfStreamAndCloseStream_StreamControl( - uint64_t stream_lifetime_ordinal) { - assert(thrd_current() == stream_control_thread_); - { // scope lock - std::unique_lock<std::mutex> lock(lock_); - - // We re-check some things which were already future-verified a different - // way, to allow for flexibility in the future-tracking stuff to permit - // less checking in the Output ordering domain (FIDL thread) without - // breaking overall verification of a flush. Any checking in the Output - // ordering domain (FIDL thread) is for the future-tracking's own - // convenience only. The checking here is the real checking. - - CheckStreamLifetimeOrdinalLocked(stream_lifetime_ordinal); - assert(stream_lifetime_ordinal >= stream_lifetime_ordinal_); - if (!IsStreamActiveLocked() || - stream_lifetime_ordinal != stream_lifetime_ordinal_) { - // TODO(dustingreen): epitaph - Exit( - "FlushEndOfStreamAndCloseStream() only valid on an active current " - "stream (flush does not auto-create a new stream)"); - } - // At this point we know that the stream is not discarded, and not already - // flushed previously (because flush will discard the stream as there's - // nothing more that the stream is permitted to do). - assert(stream_); - assert(stream_->stream_lifetime_ordinal() == stream_lifetime_ordinal); - if (!stream_->input_end_of_stream()) { - Exit( - "FlushEndOfStreamAndCloseStream() is only permitted after " - "QueueInputEndOfStream()"); - } - while (!stream_->output_end_of_stream()) { - // While waiting, we'll continue to send OnOutputPacket(), - // OnOutputConfig(), and continue to process RecycleOutputPacket(), - // until the client catches up to the latest config (as needed) and - // we've started the send of output end_of_stream packet to the client. - // - // There is no way for the client to cancel a - // FlushEndOfStreamAndCloseStream() short of closing the Codec channel. - // Before long, the server will either send the OnOutputEndOfStream(), - // or will send OnOmxStreamFailed(), or will close the Codec channel. - // The server must do one of those things before long (not allowed to - // get stuck while flushing). - // - // OMX codecs have no way to report mid-stream input data corruption - // errors or similar without it being a stream failure, so if there's - // any stream error it turns into OnStreamFailed(). It's also permitted - // for a server to set error_detected_ bool(s) on output packets and - // send OnOutputEndOfStream() despite detected errors, but this is only - // a reasonable behavior for the server if the server normally would - // detect and report mid-stream input corruption errors without an - // OnStreamFailed(). - output_end_of_stream_seen_.wait(lock); - } - - // Now that flush is done, we close the current stream because there is - // not any subsequent message for the current stream that's valid. - EnsureStreamClosed(lock); - } // ~lock -} - -// This message is required to be idempotent. -void OmxCodecRunner::CloseCurrentStream(uint64_t stream_lifetime_ordinal, - bool release_input_buffers, - bool release_output_buffers) { - { // scope lock - std::unique_lock<std::mutex> lock(lock_); - EnsureFutureStreamCloseSeenLocked(stream_lifetime_ordinal); - } // ~lock - PostSerial(stream_control_dispatcher_, [this, stream_lifetime_ordinal, - release_input_buffers, - release_output_buffers] { - CloseCurrentStream_StreamControl( - stream_lifetime_ordinal, release_input_buffers, release_output_buffers); - }); -} - -void OmxCodecRunner::CloseCurrentStream_StreamControl( - uint64_t stream_lifetime_ordinal, bool release_input_buffers, - bool release_output_buffers) { - std::unique_lock<std::mutex> lock(lock_); - EnsureStreamClosed(lock); - if (release_input_buffers) { - EnsureBuffersNotConfiguredLocked(kInput); - } - if (release_output_buffers) { - EnsureBuffersNotConfiguredLocked(kOutput); - } -} - -void OmxCodecRunner::Sync(SyncCallback callback) { - // By posting to StreamControl ordering domain before calling the callback, - // we sync the Output ordering domain and the StreamControl ordering domain. - PostSerial(stream_control_dispatcher_, - [this, callback = std::move(callback)]() mutable { - Sync_StreamControl(std::move(callback)); - }); -} - -void OmxCodecRunner::Sync_StreamControl(SyncCallback callback) { callback(); } - -void OmxCodecRunner::RecycleOutputPacket( - fuchsia::media::PacketHeader available_output_packet) { - std::unique_lock<std::mutex> lock(lock_); - - if (!available_output_packet.has_buffer_lifetime_ordinal()) { - Exit("!available_output_packet.has_buffer_lifetime_ordinal()"); - } - - CheckOldBufferLifetimeOrdinalLocked( - kOutput, available_output_packet.buffer_lifetime_ordinal()); - if (available_output_packet.buffer_lifetime_ordinal() < - buffer_lifetime_ordinal_[kOutput]) { - // ignore arbitrarily-stale required by protocol - // - // Thanks to even values from the client being prohibited, this also - // covers mid-stream output config change where the server has already - // de-configured output buffers but the client doesn't know about that - // yet. We include that case here by setting - // buffer_lifetime_ordinal_[kOutput] to the next even value - // when de-configuring output server-side until the client has - // re-configured output. - return; - } - assert(available_output_packet.buffer_lifetime_ordinal() == - buffer_lifetime_ordinal_[kOutput]); - if (!IsOutputConfiguredLocked()) { - Exit( - "client sent RecycleOutputPacket() for buffer_lifetime_ordinal that " - "isn't fully configured yet - bad client behavior"); - } - assert(IsOutputConfiguredLocked()); - assert(!packet_free_bits_[kOutput].empty()); - assert(all_packets_[kOutput].size() == packet_free_bits_[kOutput].size()); - if (!available_output_packet.has_packet_index()) { - Exit("!available_output_packet.has_packet_index()"); - } - if (available_output_packet.packet_index() >= all_packets_[kOutput].size()) { - Exit("out of range packet_index from client in RecycleOutputPacket()"); - } - uint32_t packet_index = available_output_packet.packet_index(); - if (packet_free_bits_[kOutput][packet_index]) { - Exit( - "packet_index already free at protocol level - invalid client " - "message"); - } - // Mark free at protocol level. - packet_free_bits_[kOutput][packet_index] = true; - - // Recycle to OMX layer, if presently in acceptable OMX state. - OmxTryRecycleOutputPacketLocked( - all_packets_[kOutput][packet_index]->omx_header()); -} - -// TODO(dustingreen): At least for decoders, get the OOB config data if any, -// stash it temporarily, and convert to CODECCONFIG (instead of the codec -// creation format details). -void OmxCodecRunner::QueueInputFormatDetails( - uint64_t stream_lifetime_ordinal, - fuchsia::media::FormatDetails format_details) { - { // scope lock - std::unique_lock<std::mutex> lock(lock_); - EnsureFutureStreamSeenLocked(stream_lifetime_ordinal); - } // ~lock - PostSerial(stream_control_dispatcher_, - [this, stream_lifetime_ordinal, - format_details = std::move(format_details)]() mutable { - QueueInputFormatDetails_StreamControl(stream_lifetime_ordinal, - std::move(format_details)); - }); -} - -void OmxCodecRunner::QueueInputFormatDetails_StreamControl( - uint64_t stream_lifetime_ordinal, - fuchsia::media::FormatDetails format_details) { - assert(thrd_current() == stream_control_thread_); - - std::unique_lock<std::mutex> lock(lock_); - CheckStreamLifetimeOrdinalLocked(stream_lifetime_ordinal); - assert(stream_lifetime_ordinal >= stream_lifetime_ordinal_); - if (stream_lifetime_ordinal > stream_lifetime_ordinal_) { - StartNewStream(lock, stream_lifetime_ordinal); - } - assert(stream_lifetime_ordinal == stream_lifetime_ordinal_); - if (stream_->input_end_of_stream()) { - Exit("QueueInputFormatDetails() after QueueInputEndOfStream() unexpected"); - } - if (stream_->future_discarded()) { - // No reason to handle since the stream is future-discarded. - return; - } - stream_->SetInputFormatDetails( - std::make_unique<fuchsia::media::FormatDetails>( - std::move(format_details))); - // SetOobConfigPending(true) to ensure oob_config_pending() is true. - // - // This call is needed only to properly handle a call to - // QueueInputFormatDetails() mid-stream. For new streams that lack any - // calls to QueueInputFormatDetails() before an input packet arrives, the - // oob_config_pending() will already be true because it starts true for a - // new stream. For QueueInputFormatDetails() at the start of a stream - // before any packets, oob_config_pending() will already be true. - stream_->SetOobConfigPending(true); -} - -void OmxCodecRunner::QueueInputPacket(fuchsia::media::Packet packet) { - { // scope lock - std::unique_lock<std::mutex> lock(lock_); - if (!packet.has_stream_lifetime_ordinal()) { - Exit("!packet.has_stream_lifetime_ordinal()"); - } - EnsureFutureStreamSeenLocked(packet.stream_lifetime_ordinal()); - } // ~lock - PostSerial(stream_control_dispatcher_, - [this, packet = std::move(packet)]() mutable { - QueueInputPacket_StreamControl(std::move(packet)); - }); -} - -void OmxCodecRunner::QueueInputPacket_StreamControl( - fuchsia::media::Packet packet) { - // Unless we cancel this cleanup, we'll free the input packet back to the - // client. - // - // This is an example of where not being able to copy a FIDL struct using - // language-level copy can be a bit verbose, but overall it's probably worth - // forcing copy to be explicit. - fuchsia::media::PacketHeader temp_header_copy; - if (!packet.has_header()) { - Exit("!packet.has_header()"); - } - zx_status_t clone_result = packet.header().Clone(&temp_header_copy); - if (clone_result != ZX_OK) { - Exit("PacketHeader::Clone() failed"); - } - - { // scope lock - std::unique_lock<std::mutex> lock(lock_); - auto send_free_input_packet_locked = - fit::defer([this, header = std::move(temp_header_copy)]() mutable { - SendFreeInputPacketLocked(std::move(header)); - }); - - if (!packet.header().has_buffer_lifetime_ordinal()) { - Exit("!packet.header()->has_buffer_lifetime_ordinal()"); - } - CheckOldBufferLifetimeOrdinalLocked( - kInput, packet.header().buffer_lifetime_ordinal()); - - // For input, mid-stream config changes are not a thing and input buffers - // are never unilaterally de-configured by the Codec server. - assert(buffer_lifetime_ordinal_[kInput] == - port_settings_[kInput]->buffer_lifetime_ordinal()); - // For this message we're extra-strict re. buffer_lifetime_ordinal, at - // least for now. - // - // In contrast to output, the server doesn't use even values to track - // config changes that the client doesn't know about yet, since the server - // can't unilaterally demand any changes to the input settings after - // initially specifying the input constraints. - // - // One could somewhat-convincingly argue that this field in this - // particular message is a bit pointless, but it might serve to detect - // client-side bugs faster thanks to this check. - if (packet.header().buffer_lifetime_ordinal() != - port_settings_[kInput]->buffer_lifetime_ordinal()) { - Exit("client QueueInputPacket() with invalid buffer_lifetime_ordinal."); - } - - if (!packet.has_stream_lifetime_ordinal()) { - Exit("!packet.has_stream_lifetime_ordinal()"); - } - CheckStreamLifetimeOrdinalLocked(packet.stream_lifetime_ordinal()); - assert(packet.stream_lifetime_ordinal() >= stream_lifetime_ordinal_); - - if (packet.stream_lifetime_ordinal() > stream_lifetime_ordinal_) { - // This case implicitly starts a new stream. If the client wanted to - // ensure that the old stream would be fully processed, the client would - // have sent FlushEndOfStreamAndCloseStream() previously, whose - // processing (previous to reaching here) takes care of the flush. - // - // Start a new stream, synchronously. - StartNewStream(lock, packet.stream_lifetime_ordinal()); - } - assert(packet.stream_lifetime_ordinal() == stream_lifetime_ordinal_); - - if (!packet.header().has_packet_index()) { - Exit("!packet.header()->has_packet_index()"); - } - if (packet.header().packet_index() >= all_packets_[kInput].size()) { - Exit("client QueueInputPacket() with packet_index out of range"); - } - if (!packet.has_buffer_index()) { - Exit("!packet.has_buffer_index()"); - } - if (packet.buffer_index() >= all_buffers_[kInput].size()) { - Exit("client QueueInputPacket() with buffer_index out of range"); - } - - // Protocol check re. free/busy coherency. - if (!packet_free_bits_[kInput][packet.header().packet_index()]) { - Exit("client QueueInputPacket() with packet_index !free - exiting\n"); - } - packet_free_bits_[kInput][packet.header().packet_index()] = false; - - if (stream_->input_end_of_stream()) { - Exit("QueueInputPacket() after QueueInputEndOfStream() unexpeted"); - } - - if (stream_->future_discarded()) { - // Don't queue to OMX. The stream_ may have never fully started, or may - // have been future-discarded since. Either way, skip queueing to OMX. - // - // If the stream didn't fully start - as in, the client moved on to - // another stream before fully configuring output, then OMX is not - // presently in a state compatible with queueing input, but the Codec - // interface is. So in that case, we must avoid queueing to OMX for - // correctness. - // - // If the stream was just future-discarded after fully starting, then - // this is just an optimization to avoid giving OMX more work to do for - // a stream the client has already discarded. - // - // ~send_free_input_packet_locked - // ~lock - return; - } - - // Sending OnFreeInputPacket() will happen later instead, when OMX gives - // back the packet. - send_free_input_packet_locked.cancel(); - } // ~lock - - if (stream_->oob_config_pending()) { - OmxQueueInputOOB(); - stream_->SetOobConfigPending(false); - } - - // We don't need to be under lock for this, because the fact that we're on - // the StreamControl domain is enough to guarantee that any SendCommand to - // OMX will start after this. - OmxQueueInputPacket(packet); -} - -void OmxCodecRunner::StartNewStream(std::unique_lock<std::mutex>& lock, - uint64_t stream_lifetime_ordinal) { - assert(thrd_current() == stream_control_thread_); - assert((stream_lifetime_ordinal % 2 == 1) && - "new stream_lifetime_ordinal must be odd"); - - EnsureStreamClosed(lock); - assert((stream_lifetime_ordinal_ % 2 == 0) && "expecting no current stream"); - assert(!stream_); - - // Now it's time to start the new stream. We start the new stream at - // Codec layer first then OMX layer. - - if (!IsInputConfiguredLocked()) { - Exit("input not configured before start of stream (QueueInputPacket())"); - } - - assert(stream_queue_.size() >= 1); - assert(stream_lifetime_ordinal == - stream_queue_.front()->stream_lifetime_ordinal()); - stream_ = stream_queue_.front().get(); - // Update the stream_lifetime_ordinal_ to the new stream. We need to do - // this before we send new output config, since the output config will be - // generated using the current stream ordinal. - assert(stream_lifetime_ordinal > stream_lifetime_ordinal_); - stream_lifetime_ordinal_ = stream_lifetime_ordinal; - assert(stream_->stream_lifetime_ordinal() == stream_lifetime_ordinal_); - - // At this point, when driving an OMX codec, we need the output to be - // configured to _something_, as OMX doesn't support giving us the real - // output config unless the output is configured to at least something at - // first. If the client has not yet configured output, we also are - // required to tell the client about the output config needed by this - // stream in particular (at least the config needed by this stream in - // particular at the start of this stream - there's no guarantee that this - // stream will be able to continue without another output config change - // before OMX emits any data). - // - // At this point we know we've never sent a config that's tagged with the - // new stream yet. Send that now, if output isn't already configured. - - if (!IsOutputConfiguredLocked() || - port_settings_[kOutput]->buffer_constraints_version_ordinal() <= - omx_meh_output_buffer_constraints_version_ordinal_) { - StartIgnoringClientOldOutputConfigLocked(); - EnsureBuffersNotConfiguredLocked(kOutput); - // This does count as a mid-stream output config change, even when this is - // at the start of a stream - it's still while a stream is active, and - // still prevents this stream from outputting any data to the Codec client - // until the Codec client re-configures output while this stream is - // active. - GenerateAndSendNewOutputConfig(lock, true); - } - - // Now we can wait for the client to catch up to the current output config - // or for the client to tell the server to discard the current stream. - while (!stream_->future_discarded() && !IsOutputConfiguredLocked()) { - wake_stream_control_.wait(lock); - } - - if (stream_->future_discarded()) { - return; - } - - // Now we have both input and output configured, so we can move OMX from - // OMX loaded state to OMX executing state. This also calls or re-calls - // FillThisBuffer() on any currently-free output packets. - EnsureOmxStateExecuting(lock); -} - -void OmxCodecRunner::EnsureStreamClosed(std::unique_lock<std::mutex>& lock) { - // Move OMX codec to OMX loaded (from OMX executing), by using this thread - // to directly drive the codec from executing down to loaded. We do this - // first so OMX won't try to send us output while we have no stream at the - // Codec layer. - // - // We can de-init OMX codec here regardless of whether output buffers are - // yet ready. For some codecs we try to encourage the client to have the - // output buffers be ready before a stream starts, but that's generally not - // required by all codecs, and the client is not required to configure - // output before feeding input. - EnsureOmxStateLoaded(lock); - - // Now close the old stream at the Codec layer. - EnsureCodecStreamClosedLockedInternal(); - - assert((stream_lifetime_ordinal_ % 2 == 0) && "expecting no current stream"); - assert(!stream_); -} - -void OmxCodecRunner::EnsureOmxStateLoaded(std::unique_lock<std::mutex>& lock) { - assert(thrd_current() == stream_control_thread_); - // We never leave the OMX codec in OMX_StateIdle, because the only way to - // reset an OMX codec between streams is to drop all the way down to - // OMX_StateLoaded. - assert(omx_state_ == OMX_StateLoaded || omx_state_ == OMX_StateExecuting); - assert(omx_state_desired_ == omx_state_); - if (omx_state_ == OMX_StateLoaded) { - // Already done - return; - } - assert(omx_state_ == OMX_StateExecuting); - - is_omx_recycle_enabled_ = false; - - // Drop the codec from executing to idle, then from idle to loaded. - - OmxStartStateSetLocked(OMX_StateIdle); - - // (FillBufferDone() is ignoring the buffers returned thanks to - // omx_state_desired_ set other than OMX_StateExecuting by - // OmxStartStateSetLocked() above, and EmptyBufferDone() is sending - // OnFreeInputPacket() like usual.) - - VLOGF("waiting for idle state...\n"); - OmxWaitForState(lock, OMX_StateExecuting, OMX_StateIdle); - VLOGF("idle state reached\n"); - - // The codec by this point will have "returned" all the buffers by calling - // FillBufferDone() and/or EmptyBufferDone(). The buffers are still - // allocated. Unlike for port disable, we don't have to wait for this count - // to reach zero ourselves, because OMX essentially has two steps to get - // from OMX_StateExecuting to OMX_StateLoaded, but only one step to go from - // port enabled to port disabled. Only during port disable is this count - // reaching zero used as a signalling mechanism. If we wanted, we could - // pretend to wait for this just above or below waiting to reach - // OMX_StateIdle, but there would be no point. - assert(omx_output_buffer_with_omx_count_ == 0); - - OmxStartStateSetLocked(OMX_StateLoaded); - - // We've started the state change from OMX_StateIdle to OMX_StateLoaded, but - // for that state change to complete, we must call OMX FreeBuffer() on all - // the OMX buffer headers. We completely ignore the OMX spec where it says - // that low-layer buffers need to be deallocated before calling - // FreeBuffer(). Instead we leave our low-layer buffers completely allocated - // and will (potentially, if not reconfigured) use them again when moving - // from OMX_StateLoaded to OMX_StateIdle in future. - - // We know input is not happening currently because we're on StreamControl - // domain. We know RecycleOutputPacket() is not actually recycling output - // buffers back to OMX thanks to OmxTryRecycleOutputPacketLocked() checking - // omx_state_desired_ != OMX_StateExecuting. - - // We don't deallocate Packet(s) here, we only deallocate all the OMX buffer - // headers. - OmxFreeAllBufferHeaders(lock); - - VLOGF("waiting for loaded state...\n"); - OmxWaitForState(lock, OMX_StateIdle, OMX_StateLoaded); - VLOGF("loaded state reached\n"); - - // Ensure output port is enabled, to get it back to same state as if we had - // just loaded the codec. This is effectively the end of cancelling a - // mid-stream output config change. - OMX_PARAM_PORTDEFINITIONTYPE output_port_def; - InitOmxStruct(&output_port_def); - output_port_def.nPortIndex = omx_port_index_[kOutput]; - OMX_ERRORTYPE omx_result = omx_component_->GetParameter( - omx_component_, OMX_IndexParamPortDefinition, &output_port_def); - if (omx_result != OMX_ErrorNone) { - Exit( - "Couldn't get port definition from OMX (during ensure output enable) " - "- " - "result: %d", - omx_result); - } - if (!output_port_def.bEnabled) { - OmxOutputStartSetEnabledLocked(true); - // In this case we can immediately wait because we're in OMX_StateLoaded, - // so nothing to do before waiting in this case. - OmxWaitForOutputEnableStateChangeDone(lock); - } - - // Reset OMX codec state tracking. - omx_output_enabled_ = true; - omx_output_enabled_desired_ = true; - assert(omx_state_ == OMX_StateLoaded && - omx_state_desired_ == OMX_StateLoaded); - assert(omx_output_enabled_ && omx_output_enabled_desired_); - - // The OMX codec, and our associated tracking state, is now reset. -} - -void OmxCodecRunner::OmxOutputStartSetEnabledLocked(bool enable) { - // We post because we always post all FillThisBuffer() and - // SendCommand(), and because we want to call OMX only outside lock_. - omx_output_enabled_desired_ = enable; - PostSerial(fidl_dispatcher_, [this, enable] { - OMX_ERRORTYPE omx_result = omx_component_->SendCommand( - omx_component_, enable ? OMX_CommandPortEnable : OMX_CommandPortDisable, - omx_port_index_[kOutput], nullptr); - if (omx_result != OMX_ErrorNone) { - Exit( - "SendCommand(OMX_CommandPortEnable/OMX_CommandPortDisable) failed " - "- " - "exiting - enable: %d result: %d\n", - enable, omx_result); - } - }); -} - -// packet is modified; packet is not stashed -void OmxCodecRunner::OmxFreeBufferHeader(std::unique_lock<std::mutex>& lock, - Port port, Packet* packet) { - OMX_BUFFERHEADERTYPE* header = packet->omx_header(); - packet->SetOmxHeader(nullptr); - // We make all ScopedUnlock scopes stand out, even if a scope just goes to - // the end of a method. - { // scope unlock - ScopedUnlock unlock(lock); - OMX_ERRORTYPE omx_result = omx_component_->FreeBuffer( - omx_component_, omx_port_index_[port], header); - if (omx_result != OMX_ErrorNone) { - Exit("FreeBuffer() failed - exiting - port: %d\n", port); - } - } // ~unlock -} - -void OmxCodecRunner::OmxWaitForState(std::unique_lock<std::mutex>& lock, - OMX_STATETYPE from_state, - OMX_STATETYPE desired_state) { - while (omx_state_ != omx_state_desired_) { - if (omx_state_ != from_state && omx_state_ != desired_state) { - // We went off the expected state transition rails. This is treated as - // a fatal error. We don't expect this to happen. We don't have any - // reasonable way to handle this short of starting over with a new codec - // process. - Exit( - "while waiting for state transition, went off expected state rails " - "- " - "from_state: %d desired_state: %d omx_state_: %d\n", - from_state, desired_state, omx_state_); - } - omx_state_changed_.wait(lock); - } -} - -void OmxCodecRunner::OmxWaitForOutputEnableStateChangeDone( - std::unique_lock<std::mutex>& lock) { - while (omx_output_enabled_ != omx_output_enabled_desired_) { - omx_output_enabled_changed_.wait(lock); - } -} - -void OmxCodecRunner::EnsureOmxStateExecuting( - std::unique_lock<std::mutex>& lock) { - assert(stream_control_thread_ == thrd_current()); - for (Port port = kFirstPort; port < kPortCount; port++) { - // In contrast to Codec interface, OMX doesn't permit the output buffers - // to be not yet configured when moving to OMX_StateExecuting, so the - // caller takes care of ensuring that the client has configured output - // buffers. - uint32_t packet_count = PacketCountFromPortSettings(*port_settings_[port]); - (void)packet_count; - assert(all_packets_[port].size() == packet_count); - } - assert(omx_input_buffer_oob_); - assert(omx_input_packet_oob_); - assert(omx_input_packet_eos_); - if (omx_state_ == OMX_StateExecuting) { - // TODO(dustingreen): We don't actually use this method this way - // currently. If that stays true for much longer, rename and don't check - // for this case (but still assert below). - return; - } - assert(omx_state_ == OMX_StateLoaded); - - // First, make sure OMX has the proper buffer count, for each port. - EnsureOmxBufferCountCurrent(lock); - - VLOGF("starting transition to OMX_StateIdle\n"); - OmxStartStateSetLocked(OMX_StateIdle); - VLOGF("transition to idle started.\n"); - - // Allocate an OMX_BUFFERHEADERTYPE for each packet in all_packets_, and one - // for omx_input_packet_oob_ and one for omx_input_packet_eos_. - for (Port port = kFirstPort; port < kPortCount; port++) { - OmxPortUseBuffers(lock, port); - } - omx_input_packet_oob_->SetOmxHeader( - OmxUseBuffer(lock, kInput, *omx_input_packet_oob_)); - omx_input_packet_eos_->SetOmxHeader( - OmxUseBuffer(lock, kInput, *omx_input_packet_eos_)); - - // We've told the codec about all the buffers, so the codec should - // transition to idle soon if it isn't already. - VLOGF("waiting for OMX_StateIdle...\n"); - OmxWaitForState(lock, OMX_StateLoaded, OMX_StateIdle); - VLOGF("OMX_StateIdle reached\n"); - - // Now that the codec is idle, we can immediately transition the codec to - // executing. - VLOGF("starting codec transition to executing state\n"); - OmxStartStateSetLocked(OMX_StateExecuting); - VLOGF("transition to OMX_StateExecuting started\n"); - - OmxWaitForState(lock, OMX_StateIdle, OMX_StateExecuting); - VLOGF("done with transition to OMX_StateExecuting\n"); - - // Tell the codec to fill all the output buffers that are free. This is how - // the non-action sometimes in OmxTryRecycleOutputPacketLocked() ends up - // calling FillThisBuffer() at the appropriate time. This is that time. - // - // If an output packet is not free, that that packet is still with the Codec - // client, which can be entirely reasonable even for long periods of time if - // the Codec client set a non-zero packet_count_for_client. - // - // The input buffers thankfully start with the OMX client so that's already - // consistent with existing packet_free_bits_[kInput]. - for (auto& output_packet : all_packets_[kOutput]) { - if (packet_free_bits_[kOutput][output_packet->packet_index()]) { - OmxFillThisBufferLocked(output_packet->omx_header()); - } - } - is_omx_recycle_enabled_ = true; -} - -// Make sure OMX has the current buffer count for each port. -// -// During mid-stream format change, this method relies on input config changes -// being prohibited with an active stream - that's how this method avoids -// telling OMX to change the input config with the input port presently enabled. -void OmxCodecRunner::EnsureOmxBufferCountCurrent( - std::unique_lock<std::mutex>& lock) { - assert(stream_control_thread_ == thrd_current()); - // This method isn't called at a time when input or output config can be - // changing. Since we call OMX in here, we force the caller to provide the - // caller's unique_lock<> only so we can be more sure that we're not holding - // that lock while calling OMX. - // - // TODO(dustingreen): Change to locks that are capable of asserting that the - // current thread doesn't hold the lock, and switch to asserting here - // instead. - ScopedUnlock unlock(lock); - OMX_PARAM_PORTDEFINITIONTYPE port_definition[kPortCount]; - for (Port port = kFirstPort; port < kPortCount; port++) { - OMX_PARAM_PORTDEFINITIONTYPE& port_def = port_definition[port]; - InitOmxStruct(&port_def); - port_def.nPortIndex = omx_port_index_[port]; - OMX_ERRORTYPE omx_result = omx_component_->GetParameter( - omx_component_, OMX_IndexParamPortDefinition, &port_def); - if (omx_result != OMX_ErrorNone) { - Exit( - "Couldn't get port definition from OMX - exiting - port: %d " - "result: " - "%d\n", - port, omx_result); - } - assert(port_def.nBufferCountActual >= port_def.nBufferCountMin); - // We don't use the omx_input_packet_oob_ unless we're sending OOB data - // (and similar for omx_input_packet_eos_ unless we're sending an EOS), so - // those buffers don't really count for nBufferCountMin purposes. As in, - // we shouldn't expect the OMX codec to work properly unless there are as - // many normal buffers as required by OMX, not counting the - // kHiddenInputPacketCount. - uint32_t packet_count = PacketCountFromPortSettings(*port_settings_[port]); - assert(packet_count >= port_def.nBufferCountMin); - uint32_t omx_buffer_count = packet_count; - if (port == kInput) { - // for omx_input_packet_oob_ and omx_input_packet_eos_ - omx_buffer_count += kHiddenInputPacketCount; - assert(omx_buffer_count >= - port_def.nBufferCountMin + kHiddenInputPacketCount); - } - if (port_def.nBufferCountActual != omx_buffer_count) { - port_def.nBufferCountActual = omx_buffer_count; - assert(port_def.nBufferCountActual >= port_def.nBufferCountMin); - omx_result = omx_component_->SetParameter( - omx_component_, OMX_IndexParamPortDefinition, &port_def); - if (omx_result != OMX_ErrorNone) { - Exit("SetParamter(port_definition) failed - exiting\n"); - } - } - } -} - -void OmxCodecRunner::OmxPortUseBuffers(std::unique_lock<std::mutex>& lock, - Port port) { - assert(!all_packets_[port].empty()); - for (auto& packet : all_packets_[port]) { - packet->SetOmxHeader(OmxUseBuffer(lock, port, *packet)); - } -} - -OMX_BUFFERHEADERTYPE* OmxCodecRunner::OmxUseBuffer( - std::unique_lock<std::mutex>& lock, Port port, const Packet& packet) { - assert(!packet.omx_header()); - const Buffer& buffer = packet.buffer(); - size_t codec_buffer_size = buffer.buffer_size(); - // For input, we can report larger size to OMX than we'll actually use for - // any delivered input buffer when our input packets are smaller than OMX - // thinks they ought to be. - // - // For output, our codec packet buffers must be at least as large as what - // we're telling OMX, since OMX is free to fill up to header->nAllocLen. - const size_t omx_min_buffer_size = omx_port_def_[port].nBufferSize; - size_t omx_buffer_size_raw = std::max(omx_min_buffer_size, codec_buffer_size); - assert(omx_buffer_size_raw >= omx_min_buffer_size); - if (omx_buffer_size_raw > std::numeric_limits<OMX_U32>::max()) { - Exit("internal buffer size limit exceeded - exiting\n"); - } - auto omx_buffer_size = static_cast<OMX_U32>(omx_buffer_size_raw); - if (port == kOutput) { - // If codec_buffer_size is smaller, we won't have room for the amount of - // output OMX might create. If that happened somehow, when did OMX - // unilaterally change nBufferSize to be larger without informing us? It - // shouldn't. - // - // The codec_buffer_size can't be larger than omx_buffer_size due to the - // max above. We do want OMX to be able to fill as much of each codec - // packet as it wants. - assert(codec_buffer_size == omx_buffer_size); - } - OMX_BUFFERHEADERTYPE* header = nullptr; - { // scope unlock - ScopedUnlock unlock(lock); - OMX_ERRORTYPE omx_result = omx_component_->UseBuffer( - omx_component_, &header, omx_port_index_[port], - reinterpret_cast<OMX_PTR>(const_cast<Packet*>(&packet)), - omx_buffer_size, buffer.buffer_base()); - if (omx_result != OMX_ErrorNone) { - Exit("UseBuffer() failed - exiting - port: %d\n", port); - } - } // ~unlock - return header; -} - -void OmxCodecRunner::OmxStartStateSetLocked(OMX_STATETYPE omx_state_desired) { - omx_state_desired_ = omx_state_desired; - PostSerial(fidl_dispatcher_, [this, omx_state_desired] { - OMX_ERRORTYPE omx_result = omx_component_->SendCommand( - omx_component_, OMX_CommandStateSet, omx_state_desired, nullptr); - if (omx_result != OMX_ErrorNone) { - Exit("SendCommand(StateSet) failed - result: %d omx_state_desired: %d\n", - omx_result, omx_state_desired); - } - }); -} - -void OmxCodecRunner::QueueInputEndOfStream(uint64_t stream_lifetime_ordinal) { - { // scope lock - std::unique_lock<std::mutex> lock(lock_); - EnsureFutureStreamSeenLocked(stream_lifetime_ordinal); - } // ~lock - PostSerial(stream_control_dispatcher_, [this, stream_lifetime_ordinal] { - QueueInputEndOfStream_StreamControl(stream_lifetime_ordinal); - }); -} - -void OmxCodecRunner::QueueInputEndOfStream_StreamControl( - uint64_t stream_lifetime_ordinal) { - std::unique_lock<std::mutex> lock(lock_); - CheckStreamLifetimeOrdinalLocked(stream_lifetime_ordinal); - assert(stream_lifetime_ordinal >= stream_lifetime_ordinal_); - if (stream_lifetime_ordinal > stream_lifetime_ordinal_) { - // It might seem odd to start a new stream given an end-of-stream for a - // stream we've not seen before, but in my experience, allowing empty - // things to not be errors is better. - StartNewStream(lock, stream_lifetime_ordinal); - } - - if (stream_->future_discarded()) { - // Don't queue to OMX. The stream_ may have never fully started, or may - // have been future-discarded since. Either way, skip queueing to OMX. We - // only really must do this because the stream may not have ever fully - // started, in the case where the client moves on to a new stream before - // catching up to latest output config. - return; - } - - // Convert to an input OMX packet with EOS set. We have an extra OMX buffer - // reserved for this purpose. - OmxQueueInputEOS(); -} - -void OmxCodecRunner::OmxQueueInputPacket(const fuchsia::media::Packet& packet) { - assert(thrd_current() == stream_control_thread_); - // The OMX codec can report an error unilaterally, but it can't change state - // unilaterally. So on the StreamControl ordering domain it's ok to check - // the omx_state_ outside lock_. - assert(omx_state_ == OMX_StateExecuting); - // We only modify all_packets_[kInput] on StreamControl, so it's ok to read - // from it outside lock_. - if ((!decoder_params_->has_promise_separate_access_units_on_input() || - !decoder_params_->promise_separate_access_units_on_input()) && - packet.has_timestamp_ish()) { - Exit( - "timestamp_ish must be absent unless " - "promise_separate_access_units_on_input " - "- exiting\n"); - } - if (!packet.has_header()) { - Exit("!packet.has_header()"); - } - if (!packet.has_valid_length_bytes()) { - Exit("!packet.has_valid_length_bytes()"); - } - if (!packet.header().has_packet_index()) { - Exit("!packet.header()->has_packet_index()"); - } - OMX_BUFFERHEADERTYPE* header = - all_packets_[kInput][packet.header().packet_index()]->omx_header(); - header->nFilledLen = packet.valid_length_bytes(); - header->nOffset = 0; - header->nTimeStamp = packet.has_timestamp_ish() ? packet.timestamp_ish() : 0; - header->nFlags = 0; - OMX_ERRORTYPE omx_result = - omx_component_->EmptyThisBuffer(omx_component_, header); - if (omx_result != OMX_ErrorNone) { - Exit("component_->EmptyThisBuffer() failed - exiting - omx_result: %d\n", - omx_result); - } -} - -void OmxCodecRunner::OmxQueueInputOOB() { - assert(thrd_current() == stream_control_thread_); - assert(omx_state_ == OMX_StateExecuting); - - // Unlike for the omx_input_packet_eos_, there's no particular guarantee - // that the OOB packet is actually free at this point, so wait for it to be - // free first. This relies on the InputData domain not being the same as - // the StreamControl domain. - { // scope lock - std::unique_lock<std::mutex> lock(lock_); - while (!omx_input_packet_oob_free_) { - omx_input_packet_oob_free_condition_.wait(lock); - } - } // ~lock - - // Whether oob_bytes is needed can depend on codec type or specific - // input format. If there is no oob_bytes, we won't queue any - // OMX_BUFFERFLAG_CODECCONFIG buffer to OMX. - // - // TODO(dustingreen): For SoftAAC2 used in ADTS mode, extract OOB info from - // the first input data instead of requiring a client to provide OOB info - // that ultimately came from the first ADTS input data anyway... - // - // TODO(dustingreen): Consider enforcing whether a codec needs oob_bytes - // or whether it must not have oob_bytes, rather than relying on OMX SW - // codecs to fail in a reasonably sane way. Cover the case of empty - // oob_bytes also. - - assert(initial_input_format_details_); - const std::vector<uint8_t>* oob_bytes = nullptr; - if (stream_->has_input_format_details() && - stream_->input_format_details()->has_oob_bytes()) { - oob_bytes = &stream_->input_format_details()->oob_bytes(); - } else if (initial_input_format_details_->has_oob_bytes()) { - oob_bytes = &initial_input_format_details_->oob_bytes(); - } - if (!oob_bytes) { - // This is potentially fine. Let the OMX SW codec fail later if it wants - // to based on lack of OOB data, or maybe this codec and/or format doesn't - // need OOB data. - printf("!oob_bytes - potentially fine\n"); - return; - } - assert(oob_bytes); - if (oob_bytes->empty()) { - Exit("oob_bytes was non-null but empty - exiting\n"); - } - assert(omx_input_packet_oob_->buffer().buffer_size() >= - fuchsia::media::kMaxOobBytesSize); - if (oob_bytes->size() > fuchsia::media::kMaxOobBytesSize) { - Exit( - "oob_bytes.size() > fuchsia::media::kMaxOobBytesSize - " - "exiting\n"); - } - assert(oob_bytes->size() <= omx_input_packet_oob_->buffer().buffer_size()); - - size_t copy_size = oob_bytes->size(); - uint8_t* buffer_base = omx_input_packet_oob_->buffer().buffer_base(); - for (size_t i = 0; i < copy_size; i++) { - buffer_base[i] = (*oob_bytes)[i]; - } - - // This lock interval isn't strictly necessary, but to describe why it's not - // necessary would require delving into happens-before relationships in OMX, - // so go ahead and just grab the lock to assign false. It's not worth - // having different sync rules for how omx_input_packet_oob_free_ becomes - // true vs. becomes false. We can't do this assignment up above the state - // checks in the previous lock hold interval, because that would potentially - // incorrectly leave omx_input_packet_oob_free_ set to false in a case where - // we return early and don't use omx_input_packet_oob_. - { // scope lock - std::unique_lock<std::mutex> lock(lock_); - omx_input_packet_oob_free_ = false; - } // ~lock - - VLOGF("OmxQueueInputOOB() is queueing oob_bytes to the OMX codec.\n"); - OMX_BUFFERHEADERTYPE* header = omx_input_packet_oob_->omx_header(); - header->nFlags = OMX_BUFFERFLAG_CODECCONFIG; - header->nFilledLen = copy_size; - header->nOffset = 0; - header->nTimeStamp = 0; - OMX_ERRORTYPE omx_result = - omx_component_->EmptyThisBuffer(omx_component_, header); - if (omx_result != OMX_ErrorNone) { - Exit( - "component_->EmptyThisBuffer() failed (OOB case) - exiting - " - "omx_result: %d\n", - omx_result); - } -} - -void OmxCodecRunner::OmxQueueInputEOS() { - assert(thrd_current() == stream_control_thread_); - assert(omx_state_ == OMX_StateExecuting); - assert(omx_input_packet_eos_free_); - omx_input_packet_eos_free_ = false; - OMX_BUFFERHEADERTYPE* header = omx_input_packet_eos_->omx_header(); - header->nFlags = OMX_BUFFERFLAG_EOS; - header->nFilledLen = 0; - header->nOffset = 0; - header->nTimeStamp = 0; - OMX_ERRORTYPE omx_result = - omx_component_->EmptyThisBuffer(omx_component_, header); - if (omx_result != OMX_ErrorNone) { - Exit( - "component_->EmptyThisBuffer() failed (EOS case) - exiting - " - "omx_result: %d\n", - omx_result); - } -} - -bool OmxCodecRunner::IsInputConfiguredLocked() { - return IsPortConfiguredCommonLocked(kInput); -} - -bool OmxCodecRunner::IsOutputConfiguredLocked() { - return IsPortConfiguredCommonLocked(kOutput); -} - -bool OmxCodecRunner::IsPortConfiguredCommonLocked(Port port) { - if (!port_settings_[port]) { - return false; - } - assert(all_buffers_[port].size() <= - BufferCountFromPortSettings(*port_settings_[port])); - return all_buffers_[port].size() == - BufferCountFromPortSettings(*port_settings_[port]); -} - -OMX_ERRORTYPE OmxCodecRunner::omx_EventHandler(OMX_IN OMX_HANDLETYPE hComponent, - OMX_IN OMX_PTR pAppData, // this - OMX_IN OMX_EVENTTYPE eEvent, - OMX_IN OMX_U32 nData1, - OMX_IN OMX_U32 nData2, - OMX_IN OMX_PTR pEventData) { - VLOGF("omx_EventHandler eEvent: %d nData1: %d, nData2: %d pEventData: %p\n", - eEvent, nData1, nData2, pEventData); - fflush(nullptr); - auto* me = reinterpret_cast<OmxCodecRunner*>(pAppData); - assert(me->omx_component_ == hComponent); - return me->EventHandler(eEvent, nData1, nData2, pEventData); -} - -OMX_ERRORTYPE OmxCodecRunner::omx_EmptyBufferDone( - OMX_IN OMX_HANDLETYPE hComponent, OMX_IN OMX_PTR pAppData, - OMX_IN OMX_BUFFERHEADERTYPE* pBuffer) { - auto* me = reinterpret_cast<OmxCodecRunner*>(pAppData); - assert(me->omx_component_ == hComponent); - return me->EmptyBufferDone(pBuffer); -} - -OMX_ERRORTYPE OmxCodecRunner::omx_FillBufferDone( - OMX_IN OMX_HANDLETYPE hComponent, OMX_IN OMX_PTR pAppData, - OMX_IN OMX_BUFFERHEADERTYPE* pBuffer) { - auto* me = reinterpret_cast<OmxCodecRunner*>(pAppData); - assert(me->omx_component_ == hComponent); - return me->FillBufferDone(pBuffer); -} - -OMX_ERRORTYPE OmxCodecRunner::EventHandler(OMX_IN OMX_EVENTTYPE eEvent, - OMX_IN OMX_U32 nData1, - OMX_IN OMX_U32 nData2, - OMX_IN OMX_PTR pEventData) { - // We intentionally don't acquire lock_ yet. We postpone acquiring until - // the more detailed handlers called after we've parsed all the fan-out in - // this method. The point of this is to allow more optimal notification of - // condition variables while we're not presently holding the lock, without - // resorting to queuing "todo" work up the stack via the lock holds, to be - // run upon lock release, because we don't really need to go there to - // achieve the goal. - switch (eEvent) { - case OMX_EventCmdComplete: - // completed a command - VLOGF("OMX_EventCmdComplete\n"); - switch (nData1) { - case OMX_CommandStateSet: - VLOGF(" OMX_CommandStateSet - state reached: %d\n", nData2); - assert(pEventData == nullptr); - onOmxStateSetComplete(static_cast<OMX_STATETYPE>(nData2)); - break; - case OMX_CommandFlush: - printf(" OMX_CommandFlush - port index: %d\n", nData2); - assert(pEventData == nullptr); - assert(false && "we nver send OMX_CommandFlush\n"); - break; - case OMX_CommandPortDisable: - VLOGF(" OMX_CommandPortDisable - port index: %d\n", nData2); - assert(pEventData == nullptr); - if (nData2 == omx_port_index_[kOutput]) { - { // scope lock - std::unique_lock<std::mutex> lock(lock_); - omx_output_enabled_ = false; - assert(omx_output_enabled_ == omx_output_enabled_desired_); - } - omx_output_enabled_changed_.notify_all(); - } - break; - case OMX_CommandPortEnable: - VLOGF(" OMX_CommandPortEnable - port index: %d\n", nData2); - assert(pEventData == nullptr); - if (nData2 == omx_port_index_[kOutput]) { - { // scope lock - std::unique_lock<std::mutex> lock(lock_); - omx_output_enabled_ = true; - assert(omx_output_enabled_ == omx_output_enabled_desired_); - } - omx_output_enabled_changed_.notify_all(); - } - break; - case OMX_CommandMarkBuffer: - printf(" OMX_CommandMarkBuffer - port index: %d\n", nData2); - assert(pEventData == nullptr); - assert(false && "we nver send OMX_CommandMarkBuffer\n"); - break; - } - break; - case OMX_EventError: - // detected an error condition - { - // OMX spec says nData2 and pEventData are 0, but apparently not - // actually true... - printf("OMX_EventError - error: %d, nData2: %d, pEventData: %p\n", - nData1, nData2, pEventData); - const char* error_string = nullptr; - auto which_error = static_cast<OMX_ERRORTYPE>(nData1); - // recoverable means recoverable by faling the stream, not recoverable - // within a stream - there doesn't appear to be any way for AOSP OMX - // SW codecs to report mid-stream errors (despite what the OMX spec - // says) - bool recoverable = false; - switch (which_error) { - case OMX_ErrorNone: - error_string = "OMX_ErrorNone"; - // Not recoverable, because delivering this would make no sense. - break; - case OMX_ErrorInsufficientResources: - error_string = "OMX_ErrorInsufficientResources"; - // not recoverable because we don't use any OMX codecs where this - // would make any sense - break; - case OMX_ErrorUndefined: - error_string = "OMX_ErrorUndefined"; - // Some AOSP OMX SW codecs report recoverable errors this way. - recoverable = true; - break; - case OMX_ErrorInvalidComponentName: - error_string = "OMX_ErrorInvalidComponentName"; - break; - case OMX_ErrorComponentNotFound: - error_string = "OMX_ErrorComponentNotFound"; - break; - case OMX_ErrorInvalidComponent: - error_string = "OMX_ErrorInvalidComponent"; - break; - case OMX_ErrorBadParameter: - error_string = "OMX_ErrorBadParameter"; - break; - case OMX_ErrorNotImplemented: - error_string = "OMX_ErrorNotImplemented"; - break; - case OMX_ErrorUnderflow: - error_string = "OMX_ErrorUnderflow"; - break; - case OMX_ErrorOverflow: - error_string = "OMX_ErrorOverflow"; - break; - case OMX_ErrorHardware: - error_string = "OMX_ErrorHardware"; - break; - case OMX_ErrorInvalidState: - error_string = "OMX_ErrorInvalidState"; - break; - case OMX_ErrorStreamCorrupt: - error_string = "OMX_ErrorStreamCorrupt"; - // At least SoftAAC2.cpp can report a recoverable error this way - // in ADTS mode. Recoverable in the stream failure sense, not in - // the continues to process normally sense that the OMX spec talks - // about being "typical". Not typical in practice with these - // codecs... - recoverable = true; - break; - case OMX_ErrorPortsNotCompatible: - error_string = "OMX_ErrorPortsNotCompatible"; - break; - case OMX_ErrorResourcesLost: - error_string = "OMX_ErrorResourcesLost"; - break; - case OMX_ErrorNoMore: - error_string = "OMX_ErrorNoMore"; - break; - case OMX_ErrorVersionMismatch: - error_string = "OMX_ErrorVersionMismatch"; - break; - case OMX_ErrorNotReady: - error_string = "OMX_ErrorNotReady"; - break; - case OMX_ErrorTimeout: - error_string = "OMX_ErrorTimeout"; - break; - case OMX_ErrorSameState: - error_string = "OMX_ErrorSameState"; - break; - case OMX_ErrorResourcesPreempted: - error_string = "OMX_ErrorResourcesPreempted"; - break; - case OMX_ErrorPortUnresponsiveDuringAllocation: - error_string = "OMX_ErrorPortUnresponsiveDuringAllocation"; - break; - case OMX_ErrorPortUnresponsiveDuringDeallocation: - error_string = "OMX_ErrorPortUnresponsiveDuringDeallocation"; - break; - case OMX_ErrorPortUnresponsiveDuringStop: - error_string = "OMX_ErrorPortUnresponsiveDuringStop"; - break; - case OMX_ErrorIncorrectStateTransition: - error_string = "OMX_ErrorIncorrectStateTransition"; - break; - case OMX_ErrorIncorrectStateOperation: - error_string = "OMX_ErrorIncorrectStateOperation"; - break; - case OMX_ErrorUnsupportedSetting: - error_string = "OMX_ErrorUnsupportedSetting"; - break; - case OMX_ErrorUnsupportedIndex: - error_string = "OMX_ErrorUnsupportedIndex"; - break; - case OMX_ErrorBadPortIndex: - error_string = "OMX_ErrorBadPortIndex"; - break; - case OMX_ErrorPortUnpopulated: - error_string = "OMX_ErrorPortUnpopulated"; - break; - case OMX_ErrorComponentSuspended: - error_string = "OMX_ErrorComponentSuspended"; - break; - case OMX_ErrorDynamicResourcesUnavailable: - error_string = "OMX_ErrorDynamicResourcesUnavailable"; - break; - case OMX_ErrorMbErrorsInFrame: - error_string = "OMX_ErrorMbErrorsInFrame"; - break; - case OMX_ErrorFormatNotDetected: - error_string = "OMX_ErrorFormatNotDetected"; - break; - case OMX_ErrorContentPipeOpenFailed: - error_string = "OMX_ErrorContentPipeOpenFailed"; - break; - case OMX_ErrorContentPipeCreationFailed: - error_string = "OMX_ErrorContentPipeCreationFailed"; - break; - case OMX_ErrorSeperateTablesUsed: - error_string = "OMX_ErrorSeperateTablesUsed"; - break; - case OMX_ErrorTunnelingUnsupported: - error_string = "OMX_ErrorTunnelingUnsupported"; - break; - default: - error_string = "UNRECOGNIZED ERROR"; - } - printf("OMX_EventError error: %s\n", error_string); - if (!recoverable) { - Exit( - "error is not known to be recoverable - exiting - " - "error_string: " - "%s\n", - error_string); - } - assert(recoverable); - // To recover, we need to get over to StreamControl domain, and we do - // care whether the stream is the same stream as when this error was - // delivered. For this snap of the stream_lifetime_ordinal to be - // meaningful we rely on the current thread to be the codec's - // processing thread for all recoverable errors. - // - // TODO(dustingreen): See if we can find a good way to check that - // we're on that thread, and if not, treat the error as not - // recoverable after all. - uint64_t stream_lifetime_ordinal; - { // scope lock - std::unique_lock<std::mutex> lock(lock_); - stream_lifetime_ordinal = stream_lifetime_ordinal_; - } - PostSerial(stream_control_dispatcher_, [this, stream_lifetime_ordinal] { - onOmxStreamFailed(stream_lifetime_ordinal); - }); - } - break; - case OMX_EventMark: - // detected a buffer mark - printf("OMX_EventMark\n"); - // Before anyone gets excited, OMX buffer marking doesn't actually do - // anything in of the codecs we're interested in. - assert(false && "we never mark buffers"); - break; - case OMX_EventPortSettingsChanged: { - // This is the fun one. - - // For input port, we rely on the fact that OMX SW codecs, driven the - // way omx_codec_runner drives them, don't change the input port - // definition's nBufferSize (because we don't drive that way) or - // nBufferCountMin (because it probably doesn't change this field ever), - // and also don't notify via this event even if they were to change the - // input port definition (if we got out of sync on nBufferSize, that - // would be unfortunate; the Codec protocol doesn't have any way to - // force the Codec client to re-configure input, by design). - assert(nData1 == kOutput); - - bool output_re_config_required = - ((nData2 == 0) || (nData2 == OMX_IndexParamPortDefinition)); - VLOGF("OMX_EventPortSettingsChanged - output_re_config_required: %d\n", - output_re_config_required); - - // For a OMX_EventPortSettingsChanged that doesn't demand output buffer - // re-config before more output data, this translates to an ordered emit - // of a no-action-required OnOutputConfig() that just updates to the new - // format, without demanding output buffer re-config. HDR info can be - // conveyed this way, ordered with respect to output frames. OMX - // requires that we use this thread to collect OMX format info during - // EventHandler(). - if (!output_re_config_required) { - std::unique_lock<std::mutex> lock(lock_); - GenerateAndSendNewOutputConfig( - lock, - false); // buffer_constraints_action_required - break; - } - - // We have an OMX_EventPortSettingsChanged that does demand output - // buffer re-config before more output data. - assert(output_re_config_required); - - // We post over to StreamControl domain because we need to synchronize - // with any changes to stream state that might be driven by the client. - // When we get over there to StreamControl, we'll check if we're still - // talking about the same stream_lifetime_ordinal, and if not, we ignore - // the event, because a new stream may or may not have the same output - // settings, and we'll be re-generating an OnOutputConfig() as needed - // from current/later OMX output config anyway. Here are the - // possibilities: - // * Prior to the client moving to a new stream, we process this event - // on StreamControl ordering domain and have bumped - // buffer_lifetime_ordinal by the time we start any subsequent - // new stream from the client, which means we'll require the client - // to catch up to the new buffer_lifetime_ordinal before we start - // that new stream. - // * The client moves to a new stream before this event gets over to - // StreamControl. In this case we ignore the event on StreamControl - // domain since its stale by that point, but instead we use - // omx_meh_output_buffer_constraints_version_ordinal_ to cause the - // client's next stream to start with a new OnOutputConfig() that - // the client must catch up to before the stream can fully start. - // This way we know we're not ignoring a potential change to - // nBufferCountMin or anything like that. - uint64_t local_stream_lifetime_ordinal; - { // scope lock - std::unique_lock<std::mutex> lock(lock_); - // This part is not speculative. OMX has indicated that it's at least - // meh about the current output config, so ensure we do a required - // OnOutputConfig() before the next stream starts, even if the client - // moves on to a new stream such that the speculative part below - // becomes stale. - omx_meh_output_buffer_constraints_version_ordinal_ = - port_settings_[kOutput]->buffer_constraints_version_ordinal(); - // Speculative part - this part is speculative, in that we don't know - // if this post over to StreamControl will beat any client driving to - // a new stream. So we snap the stream_lifetime_ordinal so we know - // whether to ignore the post once it reaches StreamControl. - local_stream_lifetime_ordinal = stream_lifetime_ordinal_; - } // ~lock - PostSerial( - stream_control_dispatcher_, - [this, stream_lifetime_ordinal = local_stream_lifetime_ordinal] { - onOmxEventPortSettingsChanged(stream_lifetime_ordinal); - }); - } break; - case OMX_EventBufferFlag: - // detected and EOS (end of stream) - // - // According to the EOS spec this is generated by a sink that doesn't - // propagate anything downstream when the sink is done processing an EOS - // that arrived at the sink. None of the OMX SW codecs do this, - // presumably because none of them are sinks. If this were to arrive, - // it would make no sense, so don't ignore. - Exit("OMX_EventBufferFlag is unexpected"); - break; - case OMX_EventResourcesAcquired: - // component wanting to go to StateIdle - // - // None of the OMX SW codecs do this. - Exit("OMX_EventResouresAcquired is unexpected"); - break; - case OMX_EventComponentResumed: - // due to reacquistion of resources - // - // None of the OMX SW codecs do this. - Exit("OMX_EventComponentResumed is unexpected"); - break; - case OMX_EventDynamicResourcesAvailable: - // acquired previously unavailable dynamic resources - // - // None of the OMX SW codecs do this. - Exit("OMX_EventDynamicResourcesAvailable is unexpected"); - break; - case OMX_EventPortFormatDetected: - // deteted a supported format - // - // None of the OMX SW codecs do this. - Exit("OMX_EventPortFormatDetected is unexpected"); - break; - default: - // Despite getting annoyed for unexpected events above, we ignore any - // events that we don't even recognize the number of. - // - // TODO(dustingreen): See if we hit any of these, and if not, consider - // just failing here since ... we really don't expect these. - Exit("OMX_Event unrecognized and ignored."); - break; - } - return OMX_ErrorNone; -} - -void OmxCodecRunner::onOmxEventPortSettingsChanged( - uint64_t stream_lifetime_ordinal) { - assert(thrd_current() == stream_control_thread_); - std::unique_lock<std::mutex> lock(lock_); - if (stream_lifetime_ordinal < stream_lifetime_ordinal_) { - // ignore; The omx_meh_output_buffer_constraints_version_ordinal_ took - // care of it. - return; - } - assert(stream_lifetime_ordinal == stream_lifetime_ordinal_); - - is_omx_recycle_enabled_ = false; - - // Now we need to start disabling the port, wait for buffers to come back - // from OMX, free buffer headers, wait for the port to become fully - // disabled, unilaterally de-configure output buffers, demand a new output - // config from the client, wait for the client to configure output (but be - // willing to bail on waiting for the client if we notice future stream - // discard), re-enable the output port, allocate headers, wait for the port - // to be fully enabled, call FillThisBuffer() on the protocol-free buffers. - - // This is what starts the interval during which - // OmxTryRecycleOutputPacketLocked() won't call OMX, and the interval during - // which we'll ignore any in-progress client output config until the client - // catches up. - StartIgnoringClientOldOutputConfigLocked(); - - // Tell the codec to disable its output port, because that's how OMX deals - // with an output format change. - OmxOutputStartSetEnabledLocked(false); - // We can assert this because we still have lock_ and we've only posted the - // disable so far. - assert(omx_output_enabled_ && !omx_output_enabled_desired_); - - OmxWaitForOutputBuffersDoneReturning(lock); - - OmxFreeAllPortBufferHeaders(lock, kOutput); - - // State of omx_output_enabled_ in flux here (well, actually it's probably - // already false based on how OMX just used this thread during FreeHeader() - // just above to call back EventHandler(), but we don't assume that - // particular behavior so we don't assert what omx_output_enabled_ is here. - assert(!omx_output_enabled_desired_); - OmxWaitForOutputEnableStateChangeDone(lock); - assert(!omx_output_enabled_ && !omx_output_enabled_desired_); - - EnsureBuffersNotConfiguredLocked(kOutput); - - GenerateAndSendNewOutputConfig(lock, true); - - // Now we can wait for the client to catch up to the current output config - // or for the client to tell the server to discard the current stream. - while (!stream_->future_discarded() && !IsOutputConfiguredLocked()) { - wake_stream_control_.wait(lock); - } - - if (stream_->future_discarded()) { - // We already know how to handle this case, and - // omx_meh_output_buffer_constraints_version_ordinal_ is still set such - // that the client will be forced to re-configure output buffers at the - // start of the new stream. - return; - } - - // Ensure OMX has the latest buffer count (nBufferCountActual) for the - // output port. - // - // This will only actually update the output port config. The input port - // config won't have changed since SetInputBufferSettings() with an active - // stream is prohibited (and that is enforced elsewhere). - EnsureOmxBufferCountCurrent(lock); - - // Re-enable output port. - - OmxOutputStartSetEnabledLocked(true); - - // allocate OMX headers for output - OmxPortUseBuffers(lock, kOutput); - - OmxWaitForOutputEnableStateChangeDone(lock); - - // In this path, all output packets are free and with the Codec from a - // protocol point of view (not under client control because we have yet to - // deliver any packet under the new buffer_lifetime_ordinal). - for (auto& output_packet : all_packets_[kOutput]) { - assert(packet_free_bits_[kOutput][output_packet->packet_index()]); - OmxFillThisBufferLocked(output_packet->omx_header()); - } - is_omx_recycle_enabled_ = true; - - VLOGF("Done with mid-stream format change.\n"); -} - -// This method is only called when buffer_constraints_action_required will be -// true in an OnOutputConfig() message sent shortly after this method call. -// -// Even if the client is switching streams rapidly without configuring output, -// this method and GenerateAndSendNewOutputConfig() with -// buffer_constraints_action_required true always run in pairs. -// -// This is what starts the interval during which -// OmxTryRecycleOutputPacketLocked() won't call OMX. -// -// If the client is in the middle of configuring output, we'll start ignoring -// the client's messages re. the old buffer_lifetime_ordinal and old -// buffer_constraints_version_ordinal until the client catches up to the new -// last_required_buffer_constraints_version_ordinal_[kOutput]. -void OmxCodecRunner::StartIgnoringClientOldOutputConfigLocked() { - // buffer_constraints_action_required true processing is only performed on - // the StreamControl ordering domain (except during setup). - assert(!is_setup_done_ || thrd_current() == stream_control_thread_); - - // The buffer_lifetime_ordinal_[kOutput] can be even on entry due to at - // least two cases: 0, and when the client is switching streams repeatedly - // without setting a new buffer_lifetime_ordinal_[kOutput]. - if (buffer_lifetime_ordinal_[kOutput] % 2 == 1) { - assert(buffer_lifetime_ordinal_[kOutput] % 2 == 1); - assert(buffer_lifetime_ordinal_[kOutput] == - port_settings_[kOutput]->buffer_lifetime_ordinal()); - buffer_lifetime_ordinal_[kOutput]++; - assert(buffer_lifetime_ordinal_[kOutput] % 2 == 0); - assert(buffer_lifetime_ordinal_[kOutput] == - port_settings_[kOutput]->buffer_lifetime_ordinal() + 1); - } - - // When buffer_constraints_action_required true, we can assert in - // GenerateAndSendNewOutputConfig() that this value is still the - // next_output_buffer_constraints_version_ordinal_ in that method. - last_required_buffer_constraints_version_ordinal_[kOutput] = - next_output_buffer_constraints_version_ordinal_; -} - -void OmxCodecRunner::OmxFreeAllBufferHeaders( - std::unique_lock<std::mutex>& lock) { - for (Port port = kFirstPort; port < kPortCount; port++) { - OmxFreeAllPortBufferHeaders(lock, port); - } - // And same for the omx_input_packet_oob_ - OmxFreeBufferHeader(lock, kInput, omx_input_packet_oob_.get()); - // And same for the omx_input_packet_eos_ - OmxFreeBufferHeader(lock, kInput, omx_input_packet_eos_.get()); -} - -void OmxCodecRunner::OmxFreeAllPortBufferHeaders( - std::unique_lock<std::mutex>& lock, Port port) { - for (auto& packet : all_packets_[port]) { - OmxFreeBufferHeader(lock, port, packet.get()); - } -} - -void OmxCodecRunner::OmxWaitForOutputBuffersDoneReturning( - std::unique_lock<std::mutex>& lock) { - // We only actually call this when !omx_output_enabled_desired_, but there - // wouldn't be any harm in calling it during move out of executing, so allow - // that. - assert(!omx_output_enabled_desired_ || - omx_state_desired_ != OMX_StateExecuting); - while (omx_output_buffer_with_omx_count_) { - omx_output_buffers_done_returning_condition_.wait(lock); - } -} - -void OmxCodecRunner::onOmxStreamFailed(uint64_t stream_lifetime_ordinal) { - // When we come in here, we've just landed on the StreamControl domain, but - // nothing has stopped the client from moving on to a new stream before we - // got here. Given how the relevant OMX codecs refuse to process any more - // stream data of the stream when they fail with a "recoverable" error, it's - // reasonable to just ignore any stale stream failures, since the stream - // failure would only result in the client moving on to a new stream anyway, - // so if that's already happened we can ignore the old stream failure. - // - // We prefer to check the state of things on the StreamControl domain since - // this domain is in charge of stream transitions, so it's the easiest to - // reason about why checking here is safe. It would probably also be - // possible to check robustly on the Output ordering domain and avoid - // creating any invalid message orderings, but checking here is more - // obviously ok. - assert(thrd_current() == stream_control_thread_); - { // scope lock - std::unique_lock<std::mutex> lock(lock_); - assert(stream_lifetime_ordinal <= stream_lifetime_ordinal_); - if (stream_lifetime_ordinal < stream_lifetime_ordinal_) { - // ignore - old stream is already gone, so OMX codec is already reset. - // No point in telling the client about the failure of an old stream. - return; - } - assert(stream_lifetime_ordinal == stream_lifetime_ordinal_); - // We're failing the current stream. We should still queue to the output - // ordering domain to ensure ordering vs. any previously-sent output on - // this stream that was sent directly from codec processing thread. - // - // This failure is dispatcher, in the sense that the client may still be - // sending input data, and the OMX codec is expected to not reject that - // input data. - // - // There's not actually any need to track that the stream failed anywhere - // in the OmxCodecRunner. The client needs to move on from the failed - // stream to a new stream, or close the Codec channel. - printf("onOmxStreamFailed() - stream_lifetime_ordinal: %lu\n", - stream_lifetime_ordinal); - if (!enable_on_stream_failed_) { - Exit( - "onOmxStreamFailed() with a client that didn't send " - "EnableOnOmxStreamFailed(), so closing the Codec channel instead."); - } - PostSerial(fidl_dispatcher_, [this, stream_lifetime_ordinal] { - binding_->events().OnStreamFailed(stream_lifetime_ordinal); - }); - } // ~lock -} - -// OMX is freeing an input packet. -// -// Called on InputData ordering domain. A call from StreamControl would also -// work as long as the call into OMX that triggers this (if any) is made without -// lock_ held (which should be the case). I don't think -// SimpleSoftOMXComponent.cpp will call EmptyBufferDone using an incoming -// thread, but OMX spec doesn't specify AFAICT. -OMX_ERRORTYPE OmxCodecRunner::EmptyBufferDone( - OMX_IN OMX_BUFFERHEADERTYPE* pBuffer) { - auto* packet = reinterpret_cast<Packet*>(pBuffer->pAppPrivate); - assert(packet->omx_header() == pBuffer); - { // scope lock - std::unique_lock<std::mutex> lock(lock_); - - // We don't care if omx_state_desired_ is OMX_StateExecuting or not. OMX - // may be giving back a buffer which was actually "emptied", or may be - // giving back a buffer that OMX will not be emptying because we're moving - // from OMX executing to OMX idle. Either way, the OMX input buffer is - // free, the corresponding input packet is free, and the client should be - // told about the free packet. - - // If the client did a CloseCurrentStream() with release_input_buffers - // true, then the server is permitted to optimize away sending the free - // buffers back to the client, but at the moment this server doesn't - // optimize that away. - - // Because re-configuring input is only legal when there's no current - // stream, and stopping a stream at OMX layer involves OMX giving back all - // the OMX buffers (our packets) using this method first, this method - // can't be called for a packet with mis-matched buffer_lifetime_ordinal. - assert(packet->buffer_lifetime_ordinal() == - port_settings_[kInput]->buffer_lifetime_ordinal()); - assert(buffer_lifetime_ordinal_[kInput] == - port_settings_[kInput]->buffer_lifetime_ordinal()); - - // If the free packet is the omx_input_packet_oob_, don't tell the client - // about that packet/buffer, because it's not actually a packet at the - // Codec interface layer. - if (packet == omx_input_packet_oob_.get()) { - // ok, it's free - this is likely to happen before the stream is closed, - // but must happen by the time the stream is closing and the OMX codec - // is moving from OMX executing to OMX idle. - omx_input_packet_oob_free_ = true; - // Don't tell the client about this packet; it's not an official - // Packet. - goto oob_free_notify_outside_lock; - } - - // omx_input_packet_eos_ is handled similarly to omx_input_packet_oob_. - if (packet == omx_input_packet_eos_.get()) { - omx_input_packet_eos_free_ = true; - return OMX_ErrorNone; - } - - // Free/busy coherency from Codec interface to OMX doesn't involve - // trusting the client, so assert we're doing it right server-side. - assert(!packet_free_bits_[kInput][packet->packet_index()]); - packet_free_bits_[kInput][packet->packet_index()] = true; - fuchsia::media::PacketHeader header; - header.set_buffer_lifetime_ordinal(packet->buffer_lifetime_ordinal()); - header.set_packet_index(packet->packet_index()); - SendFreeInputPacketLocked(std::move(header)); - } // ~lock - return OMX_ErrorNone; -oob_free_notify_outside_lock:; - omx_input_packet_oob_free_condition_.notify_all(); - return OMX_ErrorNone; -} - -void OmxCodecRunner::SendFreeInputPacketLocked( - fuchsia::media::PacketHeader header) { - // We allow calling this method on StreamControl or InputData ordering - // domain. Because the InputData ordering domain thread isn't visible to - // this code, if this isn't the StreamControl then we can only assert that - // this thread isn't the FIDL thread, because we know the codec's InputData - // thread isn't the FIDL thread. - assert(thrd_current() == stream_control_thread_ || - thrd_current() != fidl_thread_); - // We only send using the FIDL thread. - PostSerial(fidl_dispatcher_, [this, header = std::move(header)]() mutable { - binding_->events().OnFreeInputPacket(std::move(header)); - }); -} - -// OMX is either emitting some output data, or just handing us back an OMX -// buffer that OMX is done with. -OMX_ERRORTYPE OmxCodecRunner::FillBufferDone( - OMX_OUT OMX_BUFFERHEADERTYPE* pBuffer) { - auto* packet = reinterpret_cast<Packet*>(pBuffer->pAppPrivate); - assert(packet->omx_header() == pBuffer); - { // scope lock - std::unique_lock<std::mutex> lock(lock_); - assert(stream_); - // We don't update packet_free_bits_[kOutput] for this, because the - // packets aren't really free or busy during this - it's more that they're - // not allocated. Instead we use a count. - omx_output_buffer_with_omx_count_--; - if (!omx_output_enabled_desired_ || - omx_state_desired_ != OMX_StateExecuting) { - VLOGF( - "FillBufferDone() short circuit because OMX just returning the " - "buffer\n"); - - // OMX can be giving us an actual output buffer under this path, or can - // be just trying to give us back a buffer without any data in it. - // - // We're not supposed to give this buffer back to OMX; we're trying to - // disable the output port or move the OMX codec back to Loaded state. - // - // This is only able to be checked this way because we make sure that - // calls to FillThisBuffer() always set the buffer to nFilledLen = 0 - // before sending the buffer to the codec. In addition, we can only - // check that nFilledLen == 0 if !omx_output_enabled_desired_, because - // only in that case do we know that the OMX codec itself is yet aware - // of the fact that we're returning output buffers without filling them, - // because because only in this case did the OMX codec initiate the - // change. - if (!omx_output_enabled_desired_ && - packet->omx_header()->nFilledLen != 0) { - Exit( - "OMX codec seems to be emitting a non-empty output buffer during " - "mid-stream output config change"); - } - - // Only need to notify re. buffers done returning if we're trying to - // disable port. We also notify when moving out of executing state but - // nobody actually cares about that notify since in that path we can - // just wait to reach OMX_StateIdle instead. - if (!omx_output_buffer_with_omx_count_) { - // notify outside lock - none of the output buffers are with OMX - - // some can still be with the client though. - goto notify_buffers_done_returning_outside_lock; - } - return OMX_ErrorNone; - } - auto recycle_packet = fit::defer([this, pBuffer] { - // A non-EOS zero-length buffer is allowed by OMX spec AFAICT, but we - // don't want to allow this in the Codec interface, so hand this buffer - // back to OMX so OMX can try filling it again. - // - // Similarly, when we're converting from a zero-length EOS packet to - // OnOutputEndOfStream(), the client never sees the packet, so hand the - // buffer back to OMX. - // - // To avoid assuming it's safe to call OMX on this thread directly with - // lock_ held (OMX spec basically implies it's not safe in general even - // though it would be safe assuming SimpleSoftOMXComponent.cpp), we - // queue the FillThisBuffer() instead. We always queue SendCommand() - // the same way, so we know that any SendCommand() that would change the - // OMX state so that calling FillThisBuffer() is no longer valid will be - // called after this FillThisBuffer() has already been called. - // - // We don't queue EmptyThisBuffer() from StreamControl to Output domain, - // but that's ok because StreamControl always synchronously waits for - // SendCommand() (which we do queue) to be done before StreamControl - // uses EmptyThisBuffer() to queue more input to the OMX codec, and all - // input is on StreamControl domain, so ordering is preserved between - // EmptyThisBuffer() and SendCommand(), in both directions. - printf("FillBufferDone() back to OMX without going to client\n"); - OmxFillThisBufferLocked(pBuffer); - }); - // Because we already checked that both "desired" ones are set this way, - // and because when moving to executing state or enabling the port, we - // don't - assert(omx_state_ == OMX_StateExecuting && - omx_state_desired_ == OMX_StateExecuting && omx_output_enabled_ && - omx_output_enabled_desired_); - // We don't want the Codec interface to send the client empty packets, - // except for the empty end_of_stream packet after the last stream data, - // so we take different action here depending on what OMX is handing us. - // - // If OMX is emitting an empty packet without EOS set, we want to send the - // packet back in to OMX, but not on this thread. - bool is_eos = ((pBuffer->nFlags & OMX_BUFFERFLAG_EOS) != 0); - if (pBuffer->nFilledLen != 0) { - // The output packet gets recycled later by the client. - recycle_packet.cancel(); - uint64_t timestamp_ish = 0; - if (decoder_params_->has_promise_separate_access_units_on_input() && - decoder_params_->promise_separate_access_units_on_input()) { - timestamp_ish = pBuffer->nTimeStamp; - } - packet_free_bits_[kOutput][packet->packet_index()] = false; - fuchsia::media::Packet p; - p.mutable_header()->set_buffer_lifetime_ordinal( - packet->buffer_lifetime_ordinal()); - p.mutable_header()->set_packet_index(packet->packet_index()); - p.set_buffer_index(packet->buffer().buffer_index()); - p.set_stream_lifetime_ordinal(stream_lifetime_ordinal_); - p.set_start_offset(pBuffer->nOffset); - p.set_valid_length_bytes(pBuffer->nFilledLen); - // TODO(dustingreen): verify whether other relevant codecs - // mess with this value - set to zero if codec wasn't - // created with promise_separate_access_units_on_input. - if (timestamp_ish) { - p.set_timestamp_ish(timestamp_ish); - } - // TODO(dustingreen): Figure out what to do for other codec - // types here, especially encoders. Might be able to be - // true always on output for OMX, hopefully. - p.set_start_access_unit(decoder_params_ ? true : false); - p.set_known_end_access_unit(decoder_params_ ? true : false); - PostSerial(fidl_dispatcher_, [this, p = std::move(p)]() mutable { - binding_->events().OnOutputPacket(std::move(p), false, false); - }); - } - if (is_eos) { - VLOGF("sending OnOutputEndOfStream()\n"); - PostSerial(fidl_dispatcher_, - [this, stream_lifetime_ordinal = stream_lifetime_ordinal_] { - // OMX in AOSP in practice appears to have zero ways to - // report mid-stream failures that don't fail the whole - // stream. I looked at both OMX_BUFFERFLAG_DATACORRUPT - // (OMX spec doesn't necessarily provide for this to be - // used on output buffers, and sparse if any usage by AOSP - // OMX codecs) and OMX_ErrorStreamCorrupt (OMX spec sounds - // somewhat promising if a bit wishy-washy, but in practice - // all the codecs stop processing the stream so it's - // effectively OnOmxStreamFailed()). I see no other - // potential ways in OMX for an OMX codec to report - // non-stream-fatal errors. I'm not sure to what degree - // various AOSP OMX codecs might silently tolerate - // corrupted input data. Some AOSP OMX codecs seem to - // actively try to detect corrupted input data and fail the - // stream (as in, require moving the OMX codec to Loaded - // state to achieve a reset before any more data will get - // processed). Those codecs do appear to report the - // problem via OMX_EventError with OMX_ErrorUndefined or - // OMX_ErrorStreamCorrupt, but refuse to process more input - // data until the codec goes through loaded state, so we - // treat those as OnOmxStreamFailed(), not - // error_detected_before. We don't currently try to - // compensate for OMX codec behavior by tracking specific - // input data, re-queuing input data that had been queued - // to a failed OMX stream, hide the stream failure from the - // Codec interface, etc. - bool error_detected_before = false; - binding_->events().OnOutputEndOfStream( - stream_lifetime_ordinal, error_detected_before); - }); - } - // ~recycle_packet may recycle if not already cancelled - this happens if - // OMX outputs a zero-length buffer, whether EOS or not. - } // ~lock - return OMX_ErrorNone; -notify_buffers_done_returning_outside_lock:; - omx_output_buffers_done_returning_condition_.notify_all(); - return OMX_ErrorNone; -} - -void OmxCodecRunner::OmxFillThisBufferLocked(OMX_BUFFERHEADERTYPE* header) { - // This is the only reason we expect to see nFilledLen == 0 when disabling - // the output port an getting buffers back from the codec via - // FillBufferDone() callback. It's also at least polite to the codec, and - // _maybe_ even required by some - but no proof of that. - header->nFilledLen = 0; - // rest of these are paranoia - header->nOffset = 0; - header->nTimeStamp = 0; - header->nFlags = 0; - omx_output_buffer_with_omx_count_++; - // Get out from under lock_ before calling OMX. We need to queue to OMX - // under the lock_ though, to ensure proper ordering with respect to - // SendCommand, which is also always queued. Since we also always queue - // SendCommand, the header will remain valid long enough. This is true for - // the same reason it would be true if we were only talking about the - // queueing that already exists internal to SimpleSoftOMXComponent.cpp. We - // queue despite that queueing because the OMX spec says the codec is - // allowed to call us back on the same thread we call in on. - PostSerial(fidl_dispatcher_, [this, header] { - OMX_ERRORTYPE omx_result = - omx_component_->FillThisBuffer(omx_component_, header); - if (omx_result != OMX_ErrorNone) { - Exit("FillThisBuffer() failed: %d", omx_result); - } - }); -} - -void OmxCodecRunner::onOmxStateSetComplete(OMX_STATETYPE state_reached) { - if (state_reached != OMX_StateLoaded && state_reached != OMX_StateIdle && - state_reached != OMX_StateExecuting) { - Exit( - "onOmxStateSetComplete() state_reached unexpected - exiting - " - "state_reached: %d\n", - state_reached); - } - { - std::unique_lock<std::mutex> lock(lock_); - omx_state_ = state_reached; - } - omx_state_changed_.notify_all(); -} - -bool OmxCodecRunner::IsStreamActiveLocked() { - return stream_lifetime_ordinal_ % 2 == 1; -} - -void OmxCodecRunner::EnsureBuffersNotConfiguredLocked(Port port) { - // This method can be called on input only if there's no current stream. - // - // On output, this method can be called if there's no current stream or if - // we're in the middle of an output config change. - // - // On input, this can only be called on stream_control_thread_. - // - // On output, this can be called on stream_control_thread_ or - // output_thread_. - - assert(thrd_current() == stream_control_thread_ || - (port == kOutput && (thrd_current() == fidl_thread_))); - assert(omx_state_ == omx_state_desired_); - assert(omx_state_ == OMX_StateLoaded || - (OMX_StateExecuting && !omx_output_enabled_desired_ && - !omx_output_enabled_ && (port == kOutput))); - // For mid-stream output config change, the caller is responsible for - // ensuring that OMX headers have been freed first. - assert(all_packets_[port].empty() || !all_packets_[port][0]->omx_header()); - all_packets_[port].resize(0); - if (port == kInput) { - omx_input_packet_oob_.reset(nullptr); - omx_input_buffer_oob_.reset(nullptr); - omx_input_packet_eos_.reset(nullptr); - } - all_buffers_[port].resize(0); - packet_free_bits_[port].resize(0); - assert(all_packets_[port].empty()); - assert(all_buffers_[port].empty()); - assert(packet_free_bits_[port].empty()); -} - -void OmxCodecRunner::CheckOldBufferLifetimeOrdinalLocked( - Port port, uint64_t buffer_lifetime_ordinal) { - // The client must only send odd values. 0 is even so we don't need a - // separate check for that. - if (buffer_lifetime_ordinal % 2 == 0) { - Exit( - "CheckOldBufferLifetimeOrdinalLocked() - buffer_lifetime_ordinal " - "must " - "be odd - exiting\n"); - } - if (buffer_lifetime_ordinal > protocol_buffer_lifetime_ordinal_[port]) { - Exit( - "client sent new buffer_lifetime_ordinal in message type that " - "doesn't " - "allow new buffer_lifetime_ordinals"); - } -} - -void OmxCodecRunner::CheckStreamLifetimeOrdinalLocked( - uint64_t stream_lifetime_ordinal) { - if (stream_lifetime_ordinal % 2 != 1) { - Exit("stream_lifetime_ordinal must be odd.\n"); - } - if (stream_lifetime_ordinal < stream_lifetime_ordinal_) { - Exit("client sent stream_lifetime_ordinal that went backwards"); - } -} - -void OmxCodecRunner::OmxTryRecycleOutputPacketLocked( - OMX_BUFFERHEADERTYPE* header) { - if (!is_omx_recycle_enabled_) { - // We'll rely on packet_free_bits_ to track which packets need to be sent - // back to OMX with FillThisBuffer() just after we've finished moving the - // OMX codec back to a suitable state. - return; - } - // We can assert all these things whenever is_omx_recycle_enabled_ is true. - // - // However, the reverse is not a valid statement, because we don't re-enable - // is_omx_recycle_enabled_ until we're back under lock_ on StreamControl - // ordering domain. Specifically, this condition becomes true on an OMX - // thread, followed by lock_ release, followed by lock_ acquire on - // StreamControl, followed by sending any packet_free_bits_ true packets - // back to OMX, followed by setting is_omx_recycle_enabled_ to true. - assert(omx_state_ == OMX_StateExecuting && - omx_state_desired_ == OMX_StateExecuting && omx_output_enabled_ && - omx_output_enabled_desired_); - // The caller only calls this method if the output buffers are configured at - // codec level, and for now at least, configured at codec level == - // configured at OMX level. - assert(IsOutputConfiguredLocked()); - OmxFillThisBufferLocked(header); -} - -fuchsia::media::AudioChannelId -OmxCodecRunner::AudioChannelIdFromOmxAudioChannelType( - OMX_AUDIO_CHANNELTYPE omx_audio_channeltype) { - uint32_t input_channeltype = omx_audio_channeltype; - if (input_channeltype > kOmxAudioChannelTypeSupportedMax || - input_channeltype < kOmxAudioChannelTypeSupportedMin) { - Exit("unsuppored OMX_AUDIO_CHANNELTYPE - exiting - value: %d\n", - omx_audio_channeltype); - } - return kOmxAudioChannelTypeToAudioChannelId[input_channeltype]; -} - -void OmxCodecRunner::ValidateBufferSettingsVsConstraints( - Port port, const fuchsia::media::StreamBufferSettings& settings, - const fuchsia::media::StreamBufferConstraints& constraints) { - if (!settings.has_packet_count_for_server()) { - Exit("!settings.has_packet_count_for_server()"); - } - ZX_ASSERT(constraints.has_packet_count_for_server_min()); - if (settings.packet_count_for_server() < - constraints.packet_count_for_server_min()) { - Exit("packet_count_for_server < packet_count_for_server_min"); - } - ZX_ASSERT(constraints.has_packet_count_for_server_max()); - if (settings.packet_count_for_server() > - constraints.packet_count_for_server_max()) { - Exit("packet_count_for_server > packet_count_for_server_max"); - } - - if (!settings.has_packet_count_for_client()) { - Exit("!settings.has_packet_count_for_client()"); - } - ZX_ASSERT(constraints.has_packet_count_for_client_max()); - if (settings.packet_count_for_client() > - constraints.packet_count_for_client_max()) { - Exit("packet_count_for_client > packet_count_for_client_max"); - } - - if (!settings.has_per_packet_buffer_bytes()) { - Exit("!settings.has_per_packet_buffer_bytes()"); - } - ZX_ASSERT(constraints.has_per_packet_buffer_bytes_min()); - if (settings.per_packet_buffer_bytes() < - constraints.per_packet_buffer_bytes_min()) { - Exit( - "settings.per_packet_buffer_bytes < " - "constraints.per_packet_buffer_bytes_min - exiting - port: %u " - "settings: %u constraint: %u", - port, settings.per_packet_buffer_bytes(), - constraints.per_packet_buffer_bytes_min()); - } - ZX_ASSERT(constraints.has_per_packet_buffer_bytes_max()); - if (settings.per_packet_buffer_bytes() > - constraints.per_packet_buffer_bytes_max()) { - Exit( - "settings.per_packet_buffer_bytes > " - "constraints.per_packet_buffer_bytes_max"); - } - - ZX_ASSERT(constraints.has_single_buffer_mode_allowed()); - if (settings.has_single_buffer_mode() && settings.single_buffer_mode() && - !constraints.single_buffer_mode_allowed()) { - Exit( - "settings.single_buffer_mode && " - "!constraints.single_buffer_mode_allowed"); - } -} - -void OmxCodecRunner::PostSerial(async_dispatcher_t* dispatcher, - fit::closure to_run) { - zx_status_t post_result = async::PostTask(dispatcher, std::move(to_run)); - if (post_result != ZX_OK) { - Exit("async::PostTask() failed - post_result %d", post_result); - } -} - -OmxCodecRunner::Buffer::Buffer(OmxCodecRunner* parent, Port port, - fuchsia::media::StreamBuffer buffer) - : parent_(parent), port_(port), buffer_(std::move(buffer)) { - // nothing else to do here -} - -OmxCodecRunner::Buffer::~Buffer() { - if (buffer_base_) { - zx_status_t res = zx::vmar::root_self()->unmap( - reinterpret_cast<uintptr_t>(buffer_base()), buffer_size()); - if (res != ZX_OK) { - parent_->Exit( - "OmxCodecRunner::Buffer::~Buffer() failed to unmap() Buffer"); - } - buffer_base_ = nullptr; - } -} - -bool OmxCodecRunner::Buffer::Init(bool input_require_write) { - assert(!input_require_write || port_ == kInput); - // Map the VMO in the local address space. - uintptr_t tmp; - zx_vm_option_t flags = ZX_VM_PERM_READ; - if (port_ == kOutput || input_require_write) { - flags |= ZX_VM_PERM_WRITE; - } - - if (!buffer_.has_data()) { - fprintf(stderr, "buffer_.has_data()"); - return false; - } - - if (!buffer_.data().is_vmo()) { - fprintf(stderr, "buffer_.data()->is_vmo()"); - return false; - } - - if (!buffer_.data().vmo().has_vmo_usable_start()) { - fprintf(stderr, "buffer_.data().vmo().has_vmo_usable_start()"); - return false; - } - - if (!buffer_.data().vmo().has_vmo_usable_size()) { - fprintf(stderr, "buffer_.data().vmo().has_vmo_usable_size()"); - return false; - } - - if (!buffer_.data().vmo().has_vmo_handle()) { - fprintf(stderr, "buffer_.data().vmo().has_vmo_handle()"); - return false; - } - - zx_status_t res = zx::vmar::root_self()->map( - 0, buffer_.data().vmo().vmo_handle(), - buffer_.data().vmo().vmo_usable_start(), - buffer_.data().vmo().vmo_usable_size(), flags, &tmp); - if (res != ZX_OK) { - printf("Failed to map %zu byte buffer vmo (res %d)\n", - buffer_.data().vmo().vmo_usable_size(), res); - return false; - } - buffer_base_ = reinterpret_cast<uint8_t*>(tmp); - return true; -} - -uint64_t OmxCodecRunner::Buffer::buffer_lifetime_ordinal() const { - assert(buffer_.has_buffer_lifetime_ordinal()); - return buffer_.buffer_lifetime_ordinal(); -} - -uint32_t OmxCodecRunner::Buffer::buffer_index() const { - assert(buffer_.has_buffer_index()); - return buffer_.buffer_index(); -} - -uint8_t* OmxCodecRunner::Buffer::buffer_base() const { - assert(buffer_base_ && "Shouldn't be using if Init() didn't work."); - return buffer_base_; -} - -size_t OmxCodecRunner::Buffer::buffer_size() const { - assert(buffer_.has_data()); - assert(buffer_.data().is_vmo()); - assert(buffer_.data().vmo().has_vmo_usable_size()); - return buffer_.data().vmo().vmo_usable_size(); -} - -OmxCodecRunner::Packet::Packet(uint64_t buffer_lifetime_ordinal, - uint32_t packet_index, Buffer* buffer) - : buffer_lifetime_ordinal_(buffer_lifetime_ordinal), - packet_index_(packet_index), - buffer_(buffer) { - // nothing else to do here -} - -uint64_t OmxCodecRunner::Packet::buffer_lifetime_ordinal() const { - return buffer_lifetime_ordinal_; -} - -uint32_t OmxCodecRunner::Packet::packet_index() const { return packet_index_; } - -const OmxCodecRunner::Buffer& OmxCodecRunner::Packet::buffer() const { - return *buffer_; -} - -// This can be called more than once, but must always either be moving from -// nullptr to non-nullptr, or from non-nullptr to nullptr. This pointer is -// not owned T lifetime of the omx_header pointer. -void OmxCodecRunner::Packet::SetOmxHeader(OMX_BUFFERHEADERTYPE* omx_header) { - omx_header_ = omx_header; -} - -OMX_BUFFERHEADERTYPE* OmxCodecRunner::Packet::omx_header() const { - return omx_header_; -} - -OmxCodecRunner::Stream::Stream(uint64_t stream_lifetime_ordinal) - : stream_lifetime_ordinal_(stream_lifetime_ordinal) { - // nothing else to do here -} - -uint64_t OmxCodecRunner::Stream::stream_lifetime_ordinal() { - return stream_lifetime_ordinal_; -} - -void OmxCodecRunner::Stream::SetFutureDiscarded() { - assert(!future_discarded_); - future_discarded_ = true; -} - -bool OmxCodecRunner::Stream::future_discarded() { return future_discarded_; } - -void OmxCodecRunner::Stream::SetFutureFlushEndOfStream() { - assert(!future_flush_end_of_stream_); - future_flush_end_of_stream_ = true; -} - -bool OmxCodecRunner::Stream::future_flush_end_of_stream() { - return future_flush_end_of_stream_; -} - -OmxCodecRunner::Stream::~Stream() { - VLOGF("~Stream() stream_lifetime_ordinal: %lu\n", stream_lifetime_ordinal_); -} - -void OmxCodecRunner::Stream::SetInputFormatDetails( - std::unique_ptr<fuchsia::media::FormatDetails> input_format_details) { - // This is allowed to happen multiple times per stream. - input_format_details_ = std::move(input_format_details); -} - -const fuchsia::media::FormatDetails* -OmxCodecRunner::Stream::input_format_details() { - return input_format_details_.get(); -} - -void OmxCodecRunner::Stream::SetOobConfigPending(bool pending) { - // SetOobConfigPending(true) is legal regardless of current state, but - // SetOobConfigPending(false) is only legal if the state is currently true. - assert(pending || oob_config_pending_); - oob_config_pending_ = pending; -} - -bool OmxCodecRunner::Stream::oob_config_pending() { - return oob_config_pending_; -} - -void OmxCodecRunner::Stream::SetInputEndOfStream() { - assert(!input_end_of_stream_); - input_end_of_stream_ = true; -} - -bool OmxCodecRunner::Stream::input_end_of_stream() { - return input_end_of_stream_; -} - -void OmxCodecRunner::Stream::SetOutputEndOfStream() { - assert(!output_end_of_stream_); - output_end_of_stream_ = true; -} - -bool OmxCodecRunner::Stream::output_end_of_stream() { - return output_end_of_stream_; -} - -} // namespace codec_runner diff --git a/garnet/bin/media/codecs/sw/omx/codec_runner_sw_omx/omx_codec_runner.h b/garnet/bin/media/codecs/sw/omx/codec_runner_sw_omx/omx_codec_runner.h deleted file mode 100644 index 91e0ac19cb39fd11e14c9f8a94942e8b7f3e139a..0000000000000000000000000000000000000000 --- a/garnet/bin/media/codecs/sw/omx/codec_runner_sw_omx/omx_codec_runner.h +++ /dev/null @@ -1,949 +0,0 @@ -// 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. - -#ifndef GARNET_BIN_MEDIA_CODECS_SW_OMX_CODEC_RUNNER_SW_OMX_OMX_CODEC_RUNNER_H_ -#define GARNET_BIN_MEDIA_CODECS_SW_OMX_CODEC_RUNNER_SW_OMX_OMX_CODEC_RUNNER_H_ - -#include <fuchsia/mediacodec/cpp/fidl.h> - -#include "codec_runner.h" - -#include <lib/async-loop/cpp/loop.h> -#include "lib/fidl/cpp/binding.h" -#include "src/lib/fxl/macros.h" - -#include "OMX_Component.h" -#include "OMX_Core.h" - -#include <list> - -namespace codec_runner { - -// The OmxCodecRunner is an implementation of CodecRunner (and of Codec) which -// loads and uses an OMX codec .so lib to perform processing. - -// The OMX spec is a bit wishy-washy when it comes to threading. As a general -// rule (with zero known exceptions), we don't hold lock_ while calling OMX. -// The only calls to OMX where we know holding lock would be a problem for -// SimpleSoftOMXComponent.cpp are UseBuffer, AllocateBuffer, FreeBuffer as those -// call directly back into EventHandler on the same thread that calls them. But -// for other OMX codec implementations, the OMX spec would permit calls directly -// back to EventHandler from _any_ call to OMX, AFAICT. So we don't hold lock_ -// when calling into OMX. -// -// We ensure proper ordering of calls to OMX using a combination of restricting -// which "ordering domain" can make a given call + queueing some calls to OMX -// (with ordering preserved) while holding lock_ but making the actual call to -// OMX outside lock_. -// -// In particular, we always queue calls to FillThisBuffer and SendCommand, even -// though SimpleSoftOMXComponent.cpp also posts them internally. We need to -// ensure the SendCommand happens after the FillThisBuffer, and holding a lock -// while queueing them is an easy way to ensure the later-queued SendCommand -// will be after the earlier-queued FillThisBuffer, but rather than rely on the -// codec to queue them internally, we take the OMX spec seriously when it says a -// codec might just do everything immediately on the incoming thread, so we -// queue ourselves. But... For EmptyThisBuffer(), we don't queue ourselves. -// Instead, we know that all calls to EmptyThisBuffer() and SendCommand() are -// initiated by the StreamControl ordering domain, so we know that an -// EmptyThisBuffer() call to OMX from StreamControl will happen before a -// SendCommand() queued later by the StreamControl domain. We don't care that -// the relative ordering of FillThisBuffer() and EmptyThisBuffer() calls is not -// preserved. - -// For some mime type + omx codec lib combos, we might end up with a derived -// class implementation (derived from OmxCodecRunner) which overrides -// OmxCodecRunner behavior enough to work around the known open issues in an omx -// codec's handling of a particular format - the currently-known example is the -// OMX AAC decoder not handling split ADTS headers, and requiring an OOB codec -// config to be synthesized despite all needed information being present in-band -// in ADTS. -// -// This class expects the creating code to bind an instance of this class to -// a Codec interface request. This class expects that binding to use the same -// async_t that's provided to the constructor of this class. -// -// These are the threads relevant to understanding this class: -// * Caller of Load() + SetAudioDecoderParams() (or analogous). Need not be -// the FIDL thread, but we do expect these to be called in order and -// complete before binding to the channel. -// * "FIDL thread" - the thread backing the async_t passed to the constructor. -// Obviously it's safe for this thread to call Codec methods implemented by -// this class. This class also uses the same thread for all sends to the -// Codec channel. We also use this thread to directly push the OMX codec -// through state changes including waiting for those state changes to take -// effect. The direct state pushing on this thread is not expected to ever -// take a long duration (except for FlushEndOfStreamAndCloseStream(), -// which seems like a reasonable exception given that message's purpose), -// and is not doing anything that would require round-trips to other -// processes. Input packets are the only queued work; aside from input -// packets, there's not any additional queue of incoming messages after the -// incoming channel messages. There might be some minor Codec -// responsiveness benefits to queueing state change work to a separate -// state-driving thread, to do with being able to notice that -// previously-dequeued messages can be safely ignored/skipped, but at the -// moment any such benefits seem unlikely to be significant enough to -// justify the complexity increase that would imply. -// * "OMX thread" - The OMX codec has its own primary thread. Calls to -// EventHandler() method of this class can come from the OMX thread or from -// the FIDL thread (during calls into the OMX codec from the FIDL thread). -// Since we want to use the async_t thread to push state changes of the OMX -// codec, we can't post back to the async_t thread to handle events, as -// processing events is part of OMX state changes. The -// callback-on-same-thread behavior of the OMX codec means we can't be -// holding lock_ when calling into the OMX codec, as we need EventHandler() -// to be able to acquire lock_ as needed. Some OMX codecs use the OMX -// primary thread as the data processing thread, so it's important that we -// not stall the OMX thread by doing any long-duration work in -// EventHandler(). The OMX spec also essentially says that EventHandler() -// must return quickly. -// * Any additional secondary threads created internally by the OMX codec. -// These are for concurrent data processing and don't interact directly with -// OmxCodecRunner. -// -// Queueing of input data and output data helps pipeline and avoids stalling -// data processing thread(s) unnecessarily. Input packets arrive ordered on the -// channel and we call the OMX codec directly from there to queue those input -// packets over to the OMX primary thread. Output data is emitted in order from -// the OMX codec using the OMX primary thread - we queue first to the async_t -// thread by posting a lambda (ordered with respect to others posted the same -// way), then that posted lambda sends an output message to the channel. -// -// TODO(dustingreen): We may be able to avoid posting emitted output over to the -// async_t thread for sending once we know how event sending works and what it -// does or doesn't guarantee. For now we post over there to avoid being fragile -// across any event-related changes. - -// Handling of OMX_EventPortSettingsChanged: -// -// It appears OMX is under-specified here. In practice it looks like nData2 -// being 0 or OMX_IndexParamPortDefinition means the OMX codec will be waiting -// for a port disable/enable before delivering more output data, else it'll -// keep delivering output data without interruption. -// -// The codec waiting for a disable/enable is how we know whether -// buffer_constraints_action_required. -// -// Either way, the output format may have changed, so we'll generate an -// OnOutputConfig() message in-order with respect to output data. -// -// The way that we achieve an output-ordered OnOutputConfig() varies depending -// on whether action is required. -// -// If action is not required, we just post over to the Output ordering domain -// (Codec FIDL thread) like we would for any emitted output packet. -// -// If action is required, we know the OMX codec will not be generating any -// subsequent output until action is taken, and we know action won't be taken -// until the client has caught up with this most recent config which we haven't -// sent to the client yet, and we know any fakery by the client that appears to -// be catching up to this most recent config impossibly soon will be blocked by -// protocol checking, because we haven't yet sent this latest config to the -// client. We also must ensure that we don't send the OnOutputConfig() until -// _after_ we've de-configured the output buffers server-side. We can also -// double check that the OMX codec is following the rules with regard to not -// trying to generate more output until action is taken. So, in this case we -// can post over to the StreamControl ordering domain, and from there, if the -// output config is still relevant to the current stream, we can disable the -// output port and de-configure output buffers before posting over to the Output -// ordering domain to send the OnOutputConfig() message. -// -// Because the codec is free to _immediately_ change the output config again the -// moment we return from EventHandler(), we are forced to collect the relevant -// output config data from the OMX codec immediately during the call to -// EventHandler() - there is no other valid option. -// -// As with emitted output packets, the StreamOutputConfig has a -// stream_lifetime_ordinal, and the client is free to ignore a -// StreamOutputConfig with stale stream_lifetime_ordinal (though this can -// increase latency to emitted output data in some cases). We rely on the OMX -// codec resetting its "mOutputPortSettingsChange AWAITING_DISABLED / -// AWAITING_ENABLED" state when the codec runs onReset() on dropping to OMX -// loaded state between streams, and we put the output port back to enabled -// state (despite it having no buffers at the time, which OMX is ok with when in -// OMX loaded state), but we need to give the OMX codec output buffers before we -// can put the codec back to OMX executing state and feed input. This means we -// have to generate a new OnConfigChange() for the new stream without any -// triggering OMX_EventPortSettingsChanged if we get input data for a new stream -// without a complete output config yet. This is very similar to the situation -// we're in when the Codec has just been created and we see input data before -// the client has finished configuring the output, so we treat the two -// situations the same way. -// -// As with emitted output packets, we don't necessarily need to generate or send -// a config if we know that the StreamControl ordering domain has already begun -// shutting down the current stream. We know the StreamControl ordering domain -// hasn't finished shutting down the current stream by the fact that we're still -// running in EventHandler(). While it doesn't necessarily matter which we -// choose to do, what does matter is having a valid stream_lifetime_ordinal on -// any generated messages. So if StreamControl has moved on to an even-numbered -// stream_lifetime_ordinal already, we can just elide any emitted output packet -// or output config rather than send a message with an even-numbered -// stream_lifetime_ordinal. -// -// The setting of buffer_constraints_action_required true vs false is driven -// directly from whether nData2 in OMX_EventPortSettingsChanged is 0 or is -// OMX_IndexParamPortDefinition. If so, then action is required. -class OmxCodecRunner : public CodecRunner { - public: - OmxCodecRunner(async_dispatcher_t* fidl_dispatcher, thrd_t fidl_thread, - std::string_view mime_type, std::string_view lib_filename); - - // - // CodecRunner - // - - bool Load() override; - - // Only one of these is called, corresponding to which codec type was - // requested via CodecFactory. - void SetDecoderParams( - fuchsia::mediacodec::CreateDecoder_Params decoder_params) override; - // TODO(dustingreen): - // virtual void SetAudioEncoderParams(...) override; - // virtual void SetVideoEncoderParams(...) override; - // (or combined) - - // These are called by CodecRunner at the appropriate times. - void ComputeInputConstraints() override; - void onInputConstraintsReady() override; - void onSetupDone() override; - - // - // Codec - // - - void EnableOnStreamFailed() override; - void SetInputBufferSettings( - fuchsia::media::StreamBufferSettings input_settings) override; - void SetInputBufferSettings_StreamControl( - fuchsia::media::StreamBufferSettings input_settings); - - void AddInputBuffer(fuchsia::media::StreamBuffer buffer) override; - void AddInputBuffer_StreamControl(fuchsia::media::StreamBuffer buffer); - void SetOutputBufferSettings( - fuchsia::media::StreamBufferSettings output_settings) override; - void AddOutputBuffer(fuchsia::media::StreamBuffer buffer) override; - void FlushEndOfStreamAndCloseStream( - uint64_t stream_lifetime_ordinal) override; - void FlushEndOfStreamAndCloseStream_StreamControl( - uint64_t stream_lifetime_ordinal); - void CloseCurrentStream(uint64_t stream_lifetime_ordinal, - bool release_input_buffers, - bool release_output_buffers) override; - void CloseCurrentStream_StreamControl(uint64_t stream_lifetime_ordinal, - bool release_input_buffers, - bool release_output_buffers); - void Sync(SyncCallback callback) override; - void Sync_StreamControl(SyncCallback callback); - void RecycleOutputPacket( - fuchsia::media::PacketHeader available_output_packet) override; - void QueueInputFormatDetails( - uint64_t stream_lifetime_ordinal, - fuchsia::media::FormatDetails format_details) override; - void QueueInputFormatDetails_StreamControl( - uint64_t stream_lifetime_ordinal, - fuchsia::media::FormatDetails format_details); - void QueueInputPacket(fuchsia::media::Packet packet) override; - void QueueInputPacket_StreamControl(fuchsia::media::Packet packet); - void QueueInputEndOfStream(uint64_t stream_lifetime_ordinal) override; - void QueueInputEndOfStream_StreamControl(uint64_t stream_lifetime_ordinal); - - protected: - // Constants for indexing into our own member variable arrays. - using Port = uint32_t; - static constexpr uint32_t kFirstPort = 0; - static constexpr uint32_t kInput = 0; - static constexpr uint32_t kOutput = 1; - static constexpr uint32_t kPortCount = 2; - - // TODO(dustingreen): maybe supporting non-VMO buffers would justify having a - // base class + a factory method in OmxCodecRunner maybe. - // - // These are tracked via shared_ptr<const Buffer>, to homogenize - // buffer-per-packet mode vs. single-buffer mode. - // - // These are 1:1 with Codec buffers, but not necessarily 1:1 with OMX - // "buffers". - class Buffer { - public: - Buffer(OmxCodecRunner* parent, Port port, - fuchsia::media::StreamBuffer buffer); - ~Buffer(); - bool Init(bool input_require_write = false); - - uint64_t buffer_lifetime_ordinal() const; - - uint32_t buffer_index() const; - - uint8_t* buffer_base() const; - - size_t buffer_size() const; - - private: - // The parent OmxCodecRunner instance. Just so we can call parent_->Exit(). - // The parent_ OmxCodecRunner out-lives the OmxCodecRunner::Buffer. - OmxCodecRunner* parent_; - Port port_ = 0; - // This msg still has the live vmo_handle. - fuchsia::media::StreamBuffer buffer_; - // This accounts for vmo_offset_begin. The content bytes are not part of - // a Buffer instance from a const-ness point of view. - uint8_t* buffer_base_ = nullptr; - }; - - // OMX buffers are most closely analogous to Codec packets, so we call these - // "Packet" despite them being 1:1 with OMX "buffers" while in OMX_StateIdle - // or OMX_StateExecuting. Our Codec buffers are represented by Buffer above. - // - // While a Packet instance continues to exist from stream to stream, an OMX - // buffer header does not, because the only way to reset an OMX codec is to - // drop it all the way to OMX_StateLoaded (OMX_CommandFlush isn't enough), and - // the only way to get to OMX_StateLoaded is to call OMX FreeBuffer on each - // OMX buffer header. - class Packet { - public: - // The buffer ptr is not owned. The buffer lifetime is slightly longer than - // the Packet lifetime. - Packet(uint64_t buffer_lifetime_ordinal, uint32_t packet_index, - Buffer* buffer); - - uint64_t buffer_lifetime_ordinal() const; - - uint32_t packet_index() const; - - const Buffer& buffer() const; - - // This can be called more than once, but must always either be moving from - // nullptr to non-nullptr, or from non-nullptr to nullptr. This pointer is - // not owned T lifetime of the omx_header pointer. - void SetOmxHeader(OMX_BUFFERHEADERTYPE* omx_header); - - OMX_BUFFERHEADERTYPE* omx_header() const; - - private: - uint64_t buffer_lifetime_ordinal_ = 0; - uint32_t packet_index_ = 0; - - // not owned - Buffer* buffer_ = nullptr; - - // not directly owned here - the caller uses this field as a place to stash - // this header while the caller owns the header - OMX_BUFFERHEADERTYPE* omx_header_ = nullptr; - - FXL_DISALLOW_COPY_AND_ASSIGN(Packet); - }; - - // We keep a queue of Stream objects rather than just a single current stream - // object, so we can track which streams are future-discarded and which are - // not yet known to be future-discarded. This difference matters because - // clients are not required to process OnOutputConfig() with - // stream_lifetime_ordinal of a stream that the client has since told the - // server to discard, so we don't want StreamControl ordering domain getting - // stuck waiting on a client to catch up to an output config that the client - // won't process. Instead, the StreamControl ordering domain can ignore any - // additional messages related to the discarded stream until the stream - // discarding message is reached at which point the OMX codec's mid-stream - // output config change is cancelled/forgotten when we move the OMX codec to - // OMX loaded state. - // - // In addition, if we're behind, we can catch up by skipping past some - // messages for future-discarded streams to catch up to non-discarded stream - // input quicker. Theoretically we could do even better by having the FIDL - // thread delete messages previously queued to the StreamControl domain - // regarding a stream that is now known to be discarded by the FIDL thread, - // and collapse/combine CloseCurrentStream() messages, but that's unlikely to - // help much in practice and would make the implementation more difficult to - // read, and we can mitigate unbounded queuing by demanding that clients not - // get too far ahead else we close the channel. While forcing a client to - // wait isn't great, if we don't, we can't impose a circuit-breaker limit on - // the count and/or size of queued channel messages either - ideally setting - // such a limit should be possible for any protocol, so at some convenient - // point the client needs to wait or postpone, but only if the client is - // written to be able to get far ahead in the first place. - // - // We also keep some stream-specific tracking information in here as a - // reasonably clean way to ensure that a new stream's tracking info is - // initialized properly. - class Stream { - public: - // These mutations occur in Output ordering domain (FIDL thread): - explicit Stream(uint64_t stream_lifetime_ordinal); - uint64_t stream_lifetime_ordinal(); - void SetFutureDiscarded(); - bool future_discarded(); - void SetFutureFlushEndOfStream(); - bool future_flush_end_of_stream(); - - // These mutations occur in StreamControl ordering domain: - ~Stream(); - // This can be called 0-N times for a given stream, and each call replaces - // any previously-set details. - void SetInputFormatDetails( - std::unique_ptr<fuchsia::media::FormatDetails> input_format_details); - // Can be nullptr if no per-stream details have been set, in which case the - // caller should look at OmxCodecRunner::initial_input_format_details_ - // instead. The returned pointer is only valid up until the next call to to - // SetInputFormatDetails() or when the stream is deleted, whichever comes - // first. This is only meant to be called on stream_control_thread_. - const fuchsia::media::FormatDetails* input_format_details(); - bool has_input_format_details() const { return !!input_format_details_; } - // We send oob_bytes (if any) to the OMX codec just before sending a - // packet to the OMX codec, but only when the stream has OOB data pending. - // A new stream has OOB data initially pending, and it becomes pending again - // if SetInputFormatDetails() is used and the oob_bytes don't match - // the effective oob_bytes before. This way we avoid causing extra - // OMX_EventPortSettingsChanged(s). - void SetOobConfigPending(bool pending); - bool oob_config_pending(); - void SetInputEndOfStream(); - bool input_end_of_stream(); - void SetOutputEndOfStream(); - bool output_end_of_stream(); - - private: - const uint64_t stream_lifetime_ordinal_ = 0; - bool future_discarded_ = false; - bool future_flush_end_of_stream_ = false; - // Starts as nullptr for each new stream with implicit fallback to - // initial_input_format_details_, but can be overridden on a per-stream - // basis with QueueInputFormatDetails(). - std::unique_ptr<fuchsia::media::FormatDetails> input_format_details_; - // This defaults to _true_, so that we send a OMX_BUFFERFLAG_CODECCONFIG - // buffer to OMX for each stream, if we have any oob_bytes to send. - bool oob_config_pending_ = true; - bool input_end_of_stream_ = false; - bool output_end_of_stream_ = false; - }; - - // TODO(dustingreen): For now this is essentially just a combined version of - // the OMX format structures for audio and video, but this is not how we want - // the codec interface to describe format (at least not in terms of the field - // names and inner struct names if nothing else), so this won't be the way the - // client sees the format. - struct OMX_GENERIC_PORT_FORMAT { - // While this is the same structure as out_port_def_, for uncompressed video - // output at least, it's important that this copy is filled out after the - // output port format has changed so that dimensions etc are known. For - // audio it's less important when this is filled out but we try to treat - // video and audio similarly where it's reasonable to do so. - OMX_PARAM_PORTDEFINITIONTYPE definition; - // Depends on which definition.eDomain, so never need more than one of these - // concurrently. - union { - struct { - // Mainly for the eEncoding field. - OMX_AUDIO_PARAM_PORTFORMATTYPE format; - // Depends on which format.eEncoding, so never need more than one of - // these concurrently. - union { - OMX_AUDIO_PARAM_AACPROFILETYPE aac; - OMX_AUDIO_PARAM_PCMMODETYPE pcm; - }; - // Potentially for the eProfile field, to at least consider whether we - // should plumb that up to the codec client as potentially relevant - // info. The eProfile field is apparently OMX_AUDIO_AACPROFILETYPE or - // OMX_AUDIO_WMAPROFILETYPE depending on "context" which is presumably - // depending on format.eEncoding. - // - // TODO(dustingreen): We'll need to have a list of eProfile values here - // if we really want to plumb all the info - we're not currently - // sweeping nProfileIndex until we hit OMX_ErrorNoMore. - // OMX_AUDIO_PARAM_ANDROID_PROFILETYPE android_profile; - } audio; - struct { - // TODO(dustingreen): video - } video; - }; - }; - - // Set AAC ADTS mode - called from SetAudioDecoderParams() - void SetInputAacAdts(); - - void onOmxStateSetComplete(OMX_STATETYPE state_reached); - - // things that don't need to be protected by lock_ - const std::string mime_type_; - const std::string lib_filename_; - - // See codec.md "ordering domain" comments for why we have more than one - // async_t. - - // The FIDL thread's async_t is in CodecRunner::dispatcher_ (parent class). - - bool is_setup_done_ = false; - std::condition_variable is_setup_done_condition_; - - // We don't run any FIDL interfaces on this thread - it's a way to queue - // stream control items such that FlushEndOfStreamAndCloseStream() can block - // on this thread while waiting for previously-queued input data to finish - // processing without getting in the way of recycling output buffers or - // required mid-stream output re-configs. - // - // If we find ourselves trying to get rid of as many thread switches as - // possible, we could refactor this class's implementation of the - // StreamControl ordering domain to not always use a separate thread (some - // complexity cost), or even to never use a separate thread (more complexity - // cost). - // - // We also handle OMX_EventPortSettingsChanged on the stream_control_ thread - // when buffer_constraints_action_required true. - std::unique_ptr<async::Loop> stream_control_; - async_dispatcher_t* stream_control_dispatcher_ = nullptr; - thrd_t stream_control_thread_ = 0; - - // - // We separate state into two chunks - one for Codec-related state and one for - // OMX-related state. - // - // TODO(dustingreen): Decide whether to split this class into two classes. - // However, it's likely more fruitful to treat the Codec client as if it will - // always behave perfectly, and move any Codec interface usage validation into - // a separate process that sits in between a Codec client and each Codec - // implementation. Once we do that, the hope is that there would remain little - // point in a separate class to handle Codec interface aspects since those - // aspects would be pretty much 1:1 with incoming Codec method calls, and - // there's not really any fundamentally better middle interface with any - // better representation than the Codec interface itself is already providing. - // The fact is, the translation between Codec interface and OMX interface - // isn't super simple, and it has to happen somewhere. This class is that - // somewhere. - // - - // - // Codec-related. - // - // Stuff that's here because of needing to implement the Codec iface. In the - // long run, this shouldn't be much - see above re. potentially moving any - // protocol validation / enforcement to a separate process to avoid each Codec - // implementation (and potentially each Codec client) needing to re-implement - // that part. - // - - bool IsActiveStream(); - - // Some common handling for SetOutputBufferSettings() and - // SetInputBufferSettings() - void SetBufferSettingsCommonLocked( - Port port, fuchsia::media::StreamBufferSettings settings, - const fuchsia::media::StreamBufferConstraints& constraints); - - // Returns true if adding this buffer completed the input or output - // configuration. For output we need to know this so we can wake up the - // StreamControl ordering domain. - bool AddBufferCommon(Port port, fuchsia::media::StreamBuffer buffer); - - void EnsureFutureStreamSeenLocked(uint64_t stream_lifetime_ordinal); - void EnsureFutureStreamCloseSeenLocked(uint64_t stream_lifetime_ordinal); - void EnsureFutureStreamFlushSeenLocked(uint64_t stream_lifetime_ordinal); - bool IsStreamActiveLocked(); - void CheckStreamLifetimeOrdinalLocked(uint64_t stream_lifetime_ordinal); - void CheckOldBufferLifetimeOrdinalLocked(Port port, - uint64_t buffer_lifetime_ordinal); - bool IsInputConfiguredLocked(); - bool IsOutputConfiguredLocked(); - bool IsPortConfiguredCommonLocked(Port port); - void SendFreeInputPacketLocked(fuchsia::media::PacketHeader header); - // During processing of a buffer_constraints_action_required true server-side, - // the server will be de-configuring output buffers unilaterally. Meanwhile, - // the client can concurrently be trying to configure output with an old - // buffer_constraints_version_ordinal. We call this method fairly early in - // the server-side processing to start ignoring any ongoing messages from the - // client re. stale output config. - void StartIgnoringClientOldOutputConfigLocked(); - - void ValidateBufferSettingsVsConstraints( - Port port, const fuchsia::media::StreamBufferSettings& settings, - const fuchsia::media::StreamBufferConstraints& constraints); - - bool enable_on_stream_failed_ = false; - - std::unique_ptr<fuchsia::mediacodec::CreateDecoder_Params> decoder_params_; - // TODO(dustingreen): Add these - consider whether/how to factor out strategy, - // consistent with overall factoring to compensate for particular OMX codec - // quirks. audio_encoder_params_ video_encoder_params_ (or combined) - - // Regardless of which type of codec was created, these track the input - // FormatDetails. - // - // We keep a copy of the format details used to create the codec, and on a - // per-stream basis those details are used as the default details, but can be - // overridden with QueueInputFormatDetails(). A new stream will default back - // to the FormatDetails used to create the codec unless that stream uses - // QueueInputFormatDetails(). The QueueInputFormatDetails() is not persistent - // across streams. - // - // This field must not be nullptr beyond the codec-type-specific method such - // as SetAudioDecoderParams(). The oob_bytes field can be null if the - // codec type or specific format does not require oob_bytes. - std::unique_ptr<fuchsia::media::FormatDetails> initial_input_format_details_; - - // This is the most recent settings received from the client and accepted, - // received via SetInputBufferSettings() or SetOutputBufferSettings(). The - // settings are as-received from the client. - std::unique_ptr<const fuchsia::media::StreamBufferSettings> - port_settings_[kPortCount]; - // The server's buffer_lifetime_ordinal, per port. In contrast to - // port_settings_[port].buffer_lifetime_ordinal, this value is allowed to be - // even when the previous odd buffer_lifetime_ordinal is over, due to buffer - // de-allocation. - uint64_t buffer_lifetime_ordinal_[kPortCount] = {}; - - // I am not absolutely certain that ignoring OMX's - // OMX_EventPortSettingsChanged would be safe in the case where a client - // moves on to a new stream before we've processed - // OMX_EventPortSettingsChanged the normal way. So in that case, we use this - // field to force the client's next stream start to generate a new - // OnOutputConfig() based on current OMX output config. We do this at next - // stream start rather than between streams so the client is forced to pay - // attention to the OnOutputConfig(). The client might de-configure and - // re-configure a few times based on the most recent OnOutputConfig(), so we - // need to associate this with the buffer_constraints_ordinal that OMX said - // meh to. - // - // TODO(dustingreen): Prove that this is needed or not needed, and keep or - // remove. - uint64_t omx_meh_output_buffer_constraints_version_ordinal_ = 0; - - // Allocating these values and sending these values are tracked separately, - // so that we can more tightly enforce the protocol. If a client tries to - // act on a newer ordinal before the server has actually sent it, the server - // will notice that invalid client behavior and close the channel (instead - // of just tracking a single number, which would potentially let the client - // drive the server into the weeds). - // - // The next value we'll use for output buffer_constraints_version_ordinal and - // output format_details_version_ordinal. - uint64_t next_output_buffer_constraints_version_ordinal_ = 1; - // For the OMX adapter, if the buffer constraints change, then the format - // details ordinal also changes (since there's not really any benefit to - // detecting lack of change). But for format-only changes that don't require - // buffer re-allocation, we can just increment the format details ordinal. - uint64_t next_output_format_details_version_ordinal_ = 1; - - // Separately from ordinal allocation, we track the most recent ordinal that - // we've actually sent to the client, to allow tighter protocol enforcement in - // case of a hostile client. - uint64_t sent_buffer_constraints_version_ordinal_[kPortCount] = {0}; - uint64_t sent_format_details_version_ordinal_[kPortCount] = {0}; - - uint64_t last_required_buffer_constraints_version_ordinal_[kPortCount] = {0}; - - // For OmxCodecRunner, the initial StreamOutputConfig is sent immediately - // after the input StreamBufferConstraints. The StreamOutputConfig is likely - // to change again before any output data is emitted, but it _may not_. - std::unique_ptr<const fuchsia::media::StreamOutputConfig> output_config_; - - // We ignore the part of the OMX spec where it says we should free the - // underlying buffer "before" calling OMX FreeBuffer(). For one thing there's - // no valid/reasonable way for OMX to actually detect whether that occurred or - // not, and for another, it's none of OMX's business anyway. This allows us - // to move the OMX codec back to OMX loaded state to get - // SimpleSoftOMXComponent.cpp to call onReset(), which we need to happen - // between streams, without forcing us to free underlying buffers. It still - // requires us to call OMX FreeBuffer() for every input packet and every - // output packet between streams, but there's nothing we can do about that - // above OMX. - - std::vector<std::unique_ptr<Buffer>> all_buffers_[kPortCount]; - - // This is the stream_lifetime_ordinal of the current stream as viewed from - // StreamControl ordering domain. This is the stream lifetime ordinal that - // gets removed from the head of the Stream queue when StreamControl is done - // with the stream. - uint64_t stream_lifetime_ordinal_ = 0; - - // This is the stream_lifetime_ordinal of the most recent stream as viewed - // from the Output ordering domain (FIDL thread). This is the stream lifetime - // ordinal that we add to the tail of the Stream queue. - uint64_t future_stream_lifetime_ordinal_ = 0; - - // The Output ordering domain (FIDL thread) adds items to the tail of this - // queue, and the StreamControl ordering domain removes items from the head - // of this queue. This queue is how the StreamControl ordering domain knows - // whether a stream is discarded or not. If a stream isn't discarded then the - // StreamControl domain can keep waiting for the client to process - // OnOutputConfig() for that stream. If the stream has been discarded, then - // StreamControl ordering domain cannot expect the client to ever process - // OnOutputConfig() for the stream, and the StreamControl ordering domain can - // instead move on to the next stream. - // - // In addition, this can allow the StreamControl ordering domain to skip past - // stream-specific items for a stream that's already known to be discarded by - // the client. - std::list<std::unique_ptr<Stream>> stream_queue_; - // When no current stream, this is nullptr. When there is a current stream, - // this points to that stream, owned by stream_queue_. - Stream* stream_ = nullptr; - - // True means free at protocol level. False means in-flight at protocol - // level. A size of 0 means not-allocated at protocol level. This is used - // to check for nonsense from the client. - // - // TODO(dustingreen): Consider moving these into Packet, despite probably - // losing on packing efficiency. - std::vector<bool> packet_free_bits_[kPortCount]; - - // This is the buffer_lifetime_ordinal from SetOutputBufferSettings() or - // SetInputBufferSettings(). This is used for protocol enforcement, to - // enforce that AddOutputBuffer() or AddInputBuffer() is part of the same - // buffer_lifetime_ordinal. - uint64_t protocol_buffer_lifetime_ordinal_[kPortCount] = {}; - - // - // Adapter-related. - // - // Stuff that's in the middle between Codec iface and OMX. - // - - void StartNewStream(std::unique_lock<std::mutex>& lock, - uint64_t stream_lifetime_ordinal); - void EnsureStreamClosed(std::unique_lock<std::mutex>& lock); - // Only EnsureStreamClosed() should call this. All other callers want - // EnsureStreamClosed() instead. - void EnsureCodecStreamClosedLockedInternal(); - - // Query OMX for output config info, convert that into StreamOutputConfig, and - // send that to the client with OnOutputConfig(). This can be called on the - // setup ordering domain, or on StreamControl ordering domain. - void GenerateAndSendNewOutputConfig(std::unique_lock<std::mutex>& lock, - bool buffer_constraints_action_required); - - std::unique_ptr<const fuchsia::media::StreamOutputConfig> - BuildNewOutputConfig(uint64_t stream_lifetime_ordinal, - uint64_t new_output_buffer_constraints_version_ordinal, - uint64_t new_output_format_details_version_ordinal, - bool buffer_constraints_action_required); - std::unique_ptr<const fuchsia::media::StreamOutputConfig> - CreateNewOutputConfigFromOmxOutputFormat( - std::unique_ptr<const OmxCodecRunner::OMX_GENERIC_PORT_FORMAT> - omx_output_format, - uint64_t stream_lifetime_ordinal, - uint64_t new_output_buffer_constraints_version_ordinal, - uint64_t new_output_format_details_version_ordinal, - bool buffer_constraints_action_required); - void PopulateFormatDetailsFromOmxOutputFormat_Audio( - const OmxCodecRunner::OMX_GENERIC_PORT_FORMAT& omx_output_format, - fuchsia::media::FormatDetails* format_details); - - void EnsureBuffersNotConfiguredLocked(Port port); - - fuchsia::media::AudioChannelId AudioChannelIdFromOmxAudioChannelType( - OMX_AUDIO_CHANNELTYPE omx_audio_channeltype); - - // These Packet(s) are both Codec packets and OMX "buffers". - // - // These vectors own these buffers. - std::vector<std::unique_ptr<Packet>> all_packets_[kPortCount]; - - uint32_t omx_output_buffer_with_omx_count_ = 0; - std::condition_variable omx_output_buffers_done_returning_condition_; - - // This OMX input buffer is special because it does not correspond to any - // Packet, and is reserved by this class for sending an empty OMX buffer - // with EOS set to OMX. For this purpose, we don't need the buffer's data - // space to really be there, but just in case any OMX SW codec reads from an - // input buffer despite zero valid data indicated, we point the extra buffer - // at the same data space as packet 0 in all_packets_. Zero codecs should be - // writing to any input buffer, and we'll detect any violation of that rule - // since we map our input VMOs read-only. - // - // We wrap this in a "Packet" primarily so that we can use "Packet*" as the - // void* we pass to/from OMX re. an OMX buffer, without forcing an - // OmxBuffer layer of abstraction to exist. - // - // The "eos" is "end of stream". - std::unique_ptr<Packet> omx_input_packet_eos_; - bool omx_input_packet_eos_free_ = true; - - // This OMX input buffer is special because it does not correspond to any - // Packet, and is reserved by this class for sending OMX the - // OMX_BUFFERFLAG_CODECCONFIG omx buffer, which contains the - // FormatDetails.oob_bytes. Unlike omx_input_packet_eos_, this - // packet's buffer corresponds to a VMO allocated server-side, since we do - // need it to be able to hold real data, unlike the eos packet. - // - // We wrap this in a "Packet" primarily so that we can use "Packet*" as the - // void* we pass to/from OMX re. an OMX buffer, without forcing an - // OmxBuffer layer of abstraction to exist. - // - // The "oob" is "out of band". - std::unique_ptr<Packet> omx_input_packet_oob_; - std::unique_ptr<Buffer> omx_input_buffer_oob_; - bool omx_input_packet_oob_free_ = true; - std::condition_variable omx_input_packet_oob_free_condition_; - - // This condition variable notifies all waiting threads when any of the - // following occur: - // * Output goes from not configured to configured. - // * A stream gets marked as discarded. - // Note that input going from not configured to configured does not trigger - // this, as that's already inherently in the StreamControl ordering domain. - // - // This allows the StreamControl ordering domain to wait for a stream's output - // config to catch up _or_ for the stream to get discarded by the client, in - // which case the client won't necessarily ever be catching up to the stream's - // output config. - // - // As is typical with condition variables, we don't worry about spurious - // wakes. We might be waking re. a different stream than the StreamControl is - // actually working on currently, for example. - std::condition_variable wake_stream_control_; - - // This is set when stream_.output_end_of_stream is set. - std::condition_variable output_end_of_stream_seen_; - - // - // OMX-related. - // - // Stuff that's here to deal with OMX. This stuff would pretty much need to - // exist even if were just trying to use an OMX codec without adapting to - // the Codec interface. - // - - // OMX handlers - - // Shim handler that calls pAppData->EventHandler() essentially. - static OMX_ERRORTYPE omx_EventHandler(OMX_IN OMX_HANDLETYPE hComponent, - OMX_IN OMX_PTR pAppData, // this - OMX_IN OMX_EVENTTYPE eEvent, - OMX_IN OMX_U32 nData1, - OMX_IN OMX_U32 nData2, - OMX_IN OMX_PTR pEventData); - static OMX_ERRORTYPE omx_EmptyBufferDone( - OMX_IN OMX_HANDLETYPE hComponent, OMX_IN OMX_PTR pAppData, - OMX_IN OMX_BUFFERHEADERTYPE* pBuffer); - static OMX_ERRORTYPE omx_FillBufferDone(OMX_IN OMX_HANDLETYPE hComponent, - OMX_IN OMX_PTR pAppData, - OMX_IN OMX_BUFFERHEADERTYPE* pBuffer); - - // These are called by omx_EventHanlder (or corresponding callback) with - // this == pAppData, and with hComponent not a parameter, because that's - // available as a member variable. - OMX_ERRORTYPE EventHandler(OMX_IN OMX_EVENTTYPE eEvent, OMX_IN OMX_U32 nData1, - OMX_IN OMX_U32 nData2, OMX_IN OMX_PTR pEventData); - OMX_ERRORTYPE EmptyBufferDone(OMX_IN OMX_BUFFERHEADERTYPE* pBuffer); - OMX_ERRORTYPE FillBufferDone(OMX_OUT OMX_BUFFERHEADERTYPE* pBuffer); - - std::unique_ptr<const OMX_GENERIC_PORT_FORMAT> OmxGetOutputFormat(); - - void OmxTryRecycleOutputPacketLocked(OMX_BUFFERHEADERTYPE* header); - void OmxQueueInputPacket(const fuchsia::media::Packet& packet); - void EnsureOmxStateExecuting(std::unique_lock<std::mutex>& lock); - void EnsureOmxBufferCountCurrent(std::unique_lock<std::mutex>& lock); - void EnsureOmxStateLoaded(std::unique_lock<std::mutex>& lock); - void OmxStartStateSetLocked(OMX_STATETYPE omx_state_desired); - void OmxWaitForState(std::unique_lock<std::mutex>& lock, - OMX_STATETYPE from_state, OMX_STATETYPE desired_state); - void OmxFreeAllBufferHeaders(std::unique_lock<std::mutex>& lock); - void OmxFreeAllPortBufferHeaders(std::unique_lock<std::mutex>& lock, - Port port); - void OmxFreeBufferHeader(std::unique_lock<std::mutex>& lock, Port port, - Packet* packet); - void OmxPortUseBuffers(std::unique_lock<std::mutex>& lock, Port port); - OMX_BUFFERHEADERTYPE* OmxUseBuffer(std::unique_lock<std::mutex>& lock, - Port port, const Packet& packet); - void OmxOutputStartSetEnabledLocked(bool enable); - void OmxWaitForOutputEnableStateChangeDone( - std::unique_lock<std::mutex>& lock); - void OmxFillThisBufferLocked(OMX_BUFFERHEADERTYPE* header); - void OmxQueueInputOOB(); - void OmxQueueInputEOS(); - void onOmxStreamFailed(uint64_t stream_lifetime_ordinal); - void onOmxEventPortSettingsChanged(uint64_t stream_lifetime_ordinal); - void OmxWaitForOutputBuffersDoneReturning(std::unique_lock<std::mutex>& lock); - - // nullptr if not created yet, or the OMX component handle if created - OMX_COMPONENTTYPE* omx_component_; - OMX_CALLBACKTYPE omx_callbacks_; - // what OMX state - starts in OMX_StateInvalid until the component is created - // at which point the component will be in OMX_StateLoaded initially. See OMX - // IL spec for more on how component states and state transitions work. We - // don't handle all possible OMX state transitions in this class, only the - // ones that are relevant/possible for the android SW codecs. For example we - // don't do anything with the "wait for resources" state. - OMX_STATETYPE omx_state_; - // This tracks the latest state that we've tried to move the codec to, and - // is updated as soon as we start trying to move the codec to the new state. - // Keeping this around helps us avoid pestering the OMX codec if/when client - // code is trying to ask for something unreasonable/impossible given an - // ongoing state change, without leaning on the OMX codec to do this check - // for us. At the least, this achieves a nicer error message than a generic - // failure message re. the OMX codec returning failure. - OMX_STATETYPE omx_state_desired_; - // Any time the omx_state_ changes this condition variable signals all - // waiters. See WaitForStateOrError() for a way to wait for a specific state - // to be reached. This condition variable must only be waited on using a - // unique_lock on lock_ (not on any other lock). - std::condition_variable omx_state_changed_; - - bool omx_output_enabled_ = true; - bool omx_output_enabled_desired_ = true; - std::condition_variable omx_output_enabled_changed_; - - bool is_omx_recycle_enabled_ = false; - - OMX_U32 omx_port_index_[kPortCount] = {}; - - // These are the _initial_ OMX port defs. This initial-ness is important - // because OMX allows nBufferSize to change, but we'd like to use the initial - // nBufferSize as the Codec min buffer bytes per packet for input, and not - // require the Codec client to ever re-configure input buffers. This seems - // reasonable because even OMX doesn't actually force re-configuration of - // input buffers during dynamic output format detection when starting a - // stream, so we'll see if we can get the up-front input nBufferSize to work - // as a Codec input min buffer size. - // - // We'll smooth over when OMX nBufferSize is larger then Codec nBufferSize on - // input by reporting nBufferSize to OMX but not filling buffers beyond their - // actual Codec size. If any codec tries to read beyond the valid data bytes - // of an input packet, we'll have to re-evaluate this strategy... - // - // On output, OMX is allowed to change output format, and we'll let OMX - // nBufferSize drive per_packet_buffer_bytes_min. - // - // We never force nBufferSize to increase via SetParameter() with - // OMX_IndexParamPortDefinition since the OMX spec says nBufferSize is - // read-only, despite SimpleSoftOMXComponent.cpp permitting nBufferSize to - // increase but not decrease. But OMX may unilaterially change nBufferSize - // both up or down based on other parameters being set, or based on output - // format detection. For input, it isn't changed during format detection, but - // still can change up or down based on other parameters being set. See above - // re. how we smooth over any such input nBufferSize changes. - // - // TODO(dustingreen): We should only really be using this for asserts to do - // with the input buffer sizes - if that remains true then we could replace - // this with OMX_U32 omx_initial_input_nBufferSize_ or similar. - OMX_PARAM_PORTDEFINITIONTYPE omx_initial_port_def_[kPortCount] = {}; - - // This isn't the latest we've seen from OMX via any GetParameter call. - // Instead, this is the latest we've seen from OMX via any path where we - // expect OMX to potentially change OMX buffer constraints, in a way that we - // are forced to reflect via the Codec interface. - // - // For input, if nBufferSize increases in OMX unilaterally (we have no such - // cases yet but could in future), we report larger-than-actual input buffers - // to OMX and then don't fill them beyond their actual size. - // - // For output, we only expect nBufferSize to change when OMX triggers - // OMX_EventPortSettingsChanged. We update the output part of this member - // var during OmxGetOutputFormat(), just because that's the common path - // involved. - OMX_PARAM_PORTDEFINITIONTYPE omx_port_def_[kPortCount] = {}; - - // - // Overall behavior. - // - - // Post to dispatcher in a way that's guaranteed to run the posted work in the - // same order as the posting order. - void PostSerial(async_dispatcher_t* dispatcher, fit::closure to_run); - - FXL_DISALLOW_IMPLICIT_CONSTRUCTORS(OmxCodecRunner); -}; - -} // namespace codec_runner - -#endif // GARNET_BIN_MEDIA_CODECS_SW_OMX_CODEC_RUNNER_SW_OMX_OMX_CODEC_RUNNER_H_ diff --git a/garnet/bin/media/codecs/sw/omx/common/BUILD.gn b/garnet/bin/media/codecs/sw/omx/common/BUILD.gn deleted file mode 100644 index b54b8f718e397ec96cfa9210cf5c1a5397dd4507..0000000000000000000000000000000000000000 --- a/garnet/bin/media/codecs/sw/omx/common/BUILD.gn +++ /dev/null @@ -1,10 +0,0 @@ -# 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. - -group("common") { - testonly = true - deps = [ - "omx_android_pal", - ] -} diff --git a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/AHandler.cc b/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/AHandler.cc deleted file mode 100644 index d483866194fb5c08d78313740b6491d497e53874..0000000000000000000000000000000000000000 --- a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/AHandler.cc +++ /dev/null @@ -1,28 +0,0 @@ -// 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 "AHandler.h" - -namespace android { - -AHandler::AHandler() : id_(0) {} - -ALooper::handler_id AHandler::id() const { return id_; } - -wp<ALooper> AHandler::getLooper() const { return looper_; } - -wp<AHandler> AHandler::getHandler() const { - return const_cast<AHandler*>(this); -} - -void AHandler::setID(ALooper::handler_id id, const wp<ALooper>& looper) { - id_ = id; - looper_ = looper; -} - -void AHandler::deliverMessage(const sp<AMessage>& message) { - onMessageReceived(message); -} - -} // namespace android diff --git a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/ALooperRoster.cc b/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/ALooperRoster.cc deleted file mode 100644 index f40d127fb5368aadecfd9aff7792dee7c918766f..0000000000000000000000000000000000000000 --- a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/ALooperRoster.cc +++ /dev/null @@ -1,28 +0,0 @@ -// 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 "ALooperRoster.h" - -#include "AHandler.h" - -namespace android { - -ALooperRoster::ALooperRoster() : next_handler_id_(1) {} - -ALooper::handler_id ALooperRoster::registerHandler( - const sp<ALooper>& looper, const sp<AHandler>& handler) { - ALooper::handler_id handler_id; - { // scope lock - std::unique_lock<std::mutex> lock(mutex_); - handler_id = next_handler_id_++; - } // ~lock - handler->setID(handler_id, looper); - return handler_id; -} - -void ALooperRoster::unregisterHandler(ALooper::handler_id handler_id) {} - -void ALooperRoster::unregisterStaleHandlers() {} - -} // namespace android diff --git a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/AMessage.cc b/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/AMessage.cc deleted file mode 100644 index 5ea43b5ca5f64ee27795610238debc2bf7b135e8..0000000000000000000000000000000000000000 --- a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/AMessage.cc +++ /dev/null @@ -1,5 +0,0 @@ -// 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. - -// nothing needed here so far diff --git a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/AString.cc b/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/AString.cc deleted file mode 100644 index b88c56a5e55e854d4fc2807fcb4ca54a8a22709f..0000000000000000000000000000000000000000 --- a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/AString.cc +++ /dev/null @@ -1,58 +0,0 @@ -// 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 "./include/media/stagefright/foundation/AString.h" - -#include <media/stagefright/foundation/ADebug.h> -#include <cstdarg> - -namespace android { - -AString::AString() = default; - -AString::AString(const char* from_string) : std::string(from_string) {} - -AString::AString(const char* from_string, size_t size) - : std::string(from_string, size) {} - -AString::AString(const AString& from) : std::string(from) {} - -void AString::append(int int_to_append) { - char string_buf[16]; - int result = snprintf(string_buf, sizeof(string_buf), "%d", int_to_append); - (void)result; - assert((result > 0) && ((size_t)result) < sizeof(string_buf)); - append(string_buf); -} - -void AString::append(const char* string_to_append) { - (*this) += string_to_append; -} - -void AString::append(const char* string_to_append, size_t size) { - (*this) += std::string(string_to_append, size); -} - -void AString::append(const AString& string_to_append) { - (*this) += string_to_append; -} - -AString& AString::operator=(const AString& from) { - std::string::operator=(from); - return (*this); -} - -AString AStringPrintf(const char* format, ...) { - va_list ap; - va_start(ap, format); - char* buffer; - vasprintf(&buffer, format, ap); - va_end(ap); - AString result(buffer); - free(buffer); - buffer = nullptr; - return result; -} - -} // namespace android diff --git a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/BUILD.gn b/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/BUILD.gn deleted file mode 100644 index 7ca0a79d07912bfd8fba847d19874ea71ba10690..0000000000000000000000000000000000000000 --- a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/BUILD.gn +++ /dev/null @@ -1,171 +0,0 @@ -# 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. - -config("android_media_headers_config") { - visibility = [ ":*" ] - include_dirs = [ - # android media-related include paths - "//third_party/android/platform/frameworks/av/media/libstagefright/omx/include", - "//third_party/android/platform/frameworks/av/media/libstagefright/foundation/include", - "//third_party/android/platform/frameworks/av/media/libstagefright/foundation/include/media/stagefright/foundation", - "//third_party/android/platform/frameworks/av/media/libstagefright/include", - "//third_party/android/platform/frameworks/native/headers/media_plugin/media/openmax", - ] -} - -source_set("android_media_headers_source_set") { - visibility = [ ":*" ] - public_configs = [ ":android_media_headers_config" ] -} - -config("android_non_media_headers_config") { - visibility = [ ":*" ] - include_dirs = [ - # other android headers - we try to minimize these - "//third_party/android/platform/system/core/libutils/include", - ] -} - -source_set("android_non_media_headers_source_set") { - visibility = [ ":*" ] - - # Not sure if this actually limits included headers to only this list, but - # that's the intent. - public = [ - "//third_party/android/platform/system/core/libutils/include/utils/RefBase.h", - ] - public_configs = [ ":android_non_media_headers_config" ] -} - -config("pal_config") { - visibility = [ ":*" ] - include_dirs = [ - # intentionally before android/platform/system/core/libutils/include - "include", - "include/media/stagefright/foundation", - ] - cflags = [ - #"-v", - "-Werror", - #"-includeclang_typeof.h", - ] - libs = [ - # for zx_clock_get() - "zircon", - ] - ldflags = [ - #"-v", - ] -} - -# This source set has a name starting with many 'a's which forces it to go -# first in the list of omx_android_pal_config_source_set public_deps even -# across a git-file-format. We need it to go first because for now we're -# overriding some android headers with omx_android_pal headers, and some of -# those headers we're overriding are in the same directories as other headers -# which we're not overriding, and include path ordering is determined by -# public_deps list ordering below. The downside: this is a bit arcane. The -# upside: We can build (at least one of - maybe more later) the Android OMX SW -# codecs from un-modified AOSP sources this way despite them not being in -# cross-platform libs, and this allows us to more easily track any new -# potentially-needed changes from there. If we continue to use these codecs we -# may at some point consider different ways of building and/or sharing them. -source_set("aaaaaaa_internal_pal_config_source_set") { - visibility = [ ":*" ] - public_configs = [ ":pal_config" ] -} - -# We want to control include path ordering. We choose to do that by having all -# ordering-sensitive include paths pulled in via public_deps. A public_deps -# section can't pull in a config directly, so wrap public_config_config in -# public_config_source_set. -source_set("omx_android_pal_config_source_set") { - visibility = [ ":*" ] - public_deps = [ - # This internal pal config source set must go first to enable us to override - # some AOSP headers while still pulling some other AOSP headers from some of - # the same AOSP directories. - ":aaaaaaa_internal_pal_config_source_set", - ":android_media_headers_source_set", - ":android_non_media_headers_source_set", - ":omx_so_entry_point_config_source_set", - ] -} - -config("so_entry_point_config") { - visibility = [ ":*" ] - include_dirs = [ - # so_entry_point.h has it's own include dir, since it's needed by clients - # that don't use the omx_android_pal and don't want to include any of the - # other PAL headers. The reason so_entry_point is part of the - # omx_android_pal is because on android the OMX libs have a C++ ABI, while - # on fuchsia the OMX libs have a C-only ABI, and the omx_android_pal is - # what smooths that over (among other things). - "so_entry_point", - - # For the structs used by so_entry_point's C-only ABI. - "//third_party/android/platform/frameworks/native/headers/media_plugin/media/openmax", - ] -} - -source_set("omx_so_entry_point_config_source_set") { - visibility = [ - "//garnet/bin/media/codecs/sw/omx/codec_runner_sw_omx/*", - ":omx_android_pal_config_source_set", - ] - public_configs = [ ":so_entry_point_config" ] -} - -source_set("omx_android_pal") { - visibility = [ - # This "omx_android_pal" is only for building OMX SW codecs from un-modified - # AOSP sources. - "//garnet/bin/media/codecs/sw/omx/*", - ] - sources = [ - # The single linker-level entry point to each .so is declared and - # defined here: - "so_entry_point.cc", - - # Adapters/shims/replacements to make un-modified AOSP OMX SW codecs build - # for Fuchsia (along with the header include path ordering established in - # omx_android_pal_config_source_set). - "AHandler.cc", - "ALooperRoster.cc", - "AMessage.cc", - "AString.cc", - "Condition.cc", - "Mutex.cc", - "String16.cc", - "String8.cc", - "Threads.cc", - "Timers.cc", - "not_Parcel.cc", - "port.cc", - - # Enough stagefright code to satisfy OMX SW codec dependencies. - "//third_party/android/platform/frameworks/av/media/libstagefright/foundation/ALooper.cpp", - "//third_party/android/platform/frameworks/av/media/libstagefright/foundation/AMessage.cpp", - "//third_party/android/platform/frameworks/av/media/libstagefright/omx/SimpleSoftOMXComponent.cpp", - "//third_party/android/platform/frameworks/av/media/libstagefright/omx/SoftOMXComponent.cpp", - - # We build these two utils classes as-is. - "//third_party/android/platform/system/core/libutils/RefBase.cpp", - "//third_party/android/platform/system/core/libutils/StrongPointer.cpp", - ] - - # All include paths are exposed (to OMX codec target only) via public_deps - # instead of as a mix of public_deps and public_configs, since a - # public_configs entry can always be converted to public_deps form via an - # intervening source_set, and because this way we avoid relying on include - # path ordering between public_configs and public_deps (instead only within - # public_deps). - public_deps = [ - ":omx_android_pal_config_source_set", - ] - deps = [ - "//src/lib/fxl", - "//zircon/public/lib/fit", - ] -} diff --git a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/Condition.cc b/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/Condition.cc deleted file mode 100644 index 15464e5ababf8d8745465c9382a711a8032a8f9e..0000000000000000000000000000000000000000 --- a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/Condition.cc +++ /dev/null @@ -1,33 +0,0 @@ -// 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 <utils/Condition.h> - -#include <utils/Mutex.h> - -namespace android { - -status_t Condition::wait(Mutex& mutex) { - std::unique_lock<std::mutex> tmp(mutex.mutex_, std::adopt_lock); - condition_.wait(tmp); - tmp.release(); - // This _might_ be a lie compared to android's semantics when there's a - // spurious wake, but relevant call sites don't appear to care. - return OK; -} - -status_t Condition::waitRelative(Mutex& mutex, nsecs_t relative_timeout) { - std::unique_lock<std::mutex> tmp(mutex.mutex_, std::adopt_lock); - condition_.wait_for(tmp, std::chrono::nanoseconds(relative_timeout)); - tmp.release(); - // This _might_ be a lie compared to android's semantics when there's a - // spurious wake, but relevant call sites don't appear to care. - return OK; -} - -void Condition::signal() { condition_.notify_one(); } - -void Condition::broadcast() { condition_.notify_all(); } - -} // namespace android diff --git a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/Mutex.cc b/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/Mutex.cc deleted file mode 100644 index 0e7298e4edd9ecb5b1558b692a18d6539d3d6f08..0000000000000000000000000000000000000000 --- a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/Mutex.cc +++ /dev/null @@ -1,13 +0,0 @@ -// 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 "utils/Mutex.h" - -namespace android { - -Mutex::Mutex() = default; - -Mutex::Mutex(const char* mutex_name) {} - -} // namespace android diff --git a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/String16.cc b/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/String16.cc deleted file mode 100644 index bc2e645a1c2605c3a8cf6e8b77c4984cda38d113..0000000000000000000000000000000000000000 --- a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/String16.cc +++ /dev/null @@ -1,30 +0,0 @@ -// 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 <utils/String16.h> - -#include <codecvt> -#include <cwchar> -#include <locale> -#include <string> - -namespace { - -class DestructibleConverter : public std::codecvt<char16_t, char, mbstate_t> { - public: - DestructibleConverter(std::size_t refs = 0) - : std::codecvt<char16_t, char, mbstate_t>(refs) {} - ~DestructibleConverter() override = default; -}; - -} // anonymous namespace - -namespace android { - -String16::String16(const char* from_string) { - std::wstring_convert<DestructibleConverter, char16_t> converter; - std::u16string::operator=(converter.from_bytes(from_string)); -} - -} // namespace android diff --git a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/String8.cc b/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/String8.cc deleted file mode 100644 index c38ff0a823160189325ef068737651ac4c132f41..0000000000000000000000000000000000000000 --- a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/String8.cc +++ /dev/null @@ -1,65 +0,0 @@ -// 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 <utils/String8.h> - -#include <cassert> -#include <codecvt> -#include <cwchar> -#include <locale> -#include <string> - -namespace android { - -String8::String8() = default; - -status_t String8::appendFormat(const char* format, ...) { - va_list ap; - va_start(ap, format); - - status_t result = appendFormatV(format, ap); - - va_end(ap); - return result; -} - -status_t String8::appendFormatV(const char* format, va_list ap) { - va_list tmp_ap; - // ap is undefined after vsnprintf, so we need a copy here to avoid the secodn - // vsnprintf accessing undefined ap. - va_copy(tmp_ap, ap); - int n = vsnprintf(nullptr, 0, format, tmp_ap); - va_end(tmp_ap); - if (n) { - size_t old_length = size(); - - // With -fno-exceptions, I believe the behavior will be to abort() the - // process instead of throwing std::bad_alloc. - reserve(old_length + n); - - // TODO: C++17 has a data() accessor that'll return non-const CharT*. - // Once all relevant toolchains are C++17, we could switch to using that - // here to avoid this allocation and copy, and just vsnprintf() directly - // into the latter part of the string instead. - - // Similar to above, with -fno-exceptions, I believe the behavior will be to - // abort() the proces instead of throwing std::bad_alloc. - std::unique_ptr<char> temp = std::make_unique<char>(n + 1); - - int actual = vsnprintf(temp.get(), n + 1, format, ap); - (void)actual; - - // Concurrent modification of the string by mulitple threads isn't - // supported. - assert(n == actual); - - // passing the "n" only to avoid forcing a re-count - append(temp.get(), n); - } - return NO_ERROR; -} - -const char* String8::string() const { return c_str(); } - -} // namespace android diff --git a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/Threads.cc b/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/Threads.cc deleted file mode 100644 index cdec1eb637a15e5b6fa900203976b216f15edea1..0000000000000000000000000000000000000000 --- a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/Threads.cc +++ /dev/null @@ -1,196 +0,0 @@ -// 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 "utils/Thread.h" - -namespace android { - -Thread::Thread(bool can_call_java) { - // Fuchsia android::Thread shim doesn't support can_call_java == true. - assert(!can_call_java); -} - -Thread::~Thread() { - bool is_join_needed = false; - { // scope lock - std::unique_lock<std::mutex> lock(lock_); - if (!is_run_called_) { - // Nothing started, so nothing to shut down. - return; - } - // run() can't fail in any way other than calling abort() on the process. - assert(thread_); - // The caller _must_ have at least requested that the thread stop by this - // point, by calling requestExit() or requestExitAndWait(), or by returning - // false from threadLoop(), or by returning a failing status from - // readyToRun(), or by dropping the strong refcount to 0. - assert(is_exit_requested_); - // If the current thread is this thread, then don't try to wait for this - // thread to exit. - if (std::this_thread::get_id() == thread_->get_id()) { - thread_->detach(); - // This usage pattern isn't necessarily consistent with safe un-load of a - // shared library. For the Fuchsia scenarios involving this code (for - // now), we don't currently care about code un-load safety but we may in - // future (in those same scenarios), so we assert in this case for now, - // since we don't expect this case to get hit in the first place. - assert(false); - // ~thread_ here will be able to ~std::thread successfully because of the - // std::thread::detach() above. - return; - } - if (!is_joiner_selected_) { - is_join_needed = true; - is_joiner_selected_ = true; - } else { - // Some other thread was selected as the joiner. That means that other - // thread started running requestExitAndWait(). If that other thread is - // _still_ running requestExitAndWait(), that's a bug in calling code, - // because that code shouldn't race Thread::requestExitAndWait() with - // Thread::~Thread(). - // - // I suppose another way to end up here would be for two threads to both - // call ~Thread() on teh same Thread instance, which would of course also - // be a bug in calling code. Note that this assert only potentially - // detects one sub-case of that bug - the sub-case where the thread isn't - // joined yet. - assert(is_joined_); - } - } - - if (is_join_needed) { - joinCommon(); - } - - // Definitely true by this ponit. Don't bother acquiring the lock for this - // check as once this becomes true it stays true, and we already had the lock - // enough above to make lock holding for this check unnecessary. - assert(is_joined_); - - // Now it's safe to delete the std::thread, which will happen during ~thread_ - // implicitly here. -} - -status_t Thread::readyToRun() { return NO_ERROR; } - -status_t Thread::run(const char* thread_name, int32_t thread_priority, - size_t stack_size) { - assert(thread_name); - std::unique_lock<std::mutex> lock(lock_); - if (is_run_called_) { - return INVALID_OPERATION; - } - is_run_called_ = true; - // hold strong reference on self until _threadLoop() gets going - hold_self_ = this; - thread_ = std::make_unique<std::thread>([this] { _threadLoop(); }); - // Can't touch this. - return NO_ERROR; -} - -void Thread::_threadLoop() { - sp<Thread> strong(hold_self_); - wp<Thread> weak(strong); - hold_self_.clear(); - bool first = true; - do { - bool is_wanting_to_run; - if (first) { - first = false; - start_status_ = readyToRun(); - is_wanting_to_run = (start_status_ == NO_ERROR); - if (is_wanting_to_run && !isExitRequested()) { - is_wanting_to_run = threadLoop(); - } - } else { - is_wanting_to_run = threadLoop(); - } - - { // scope lock - std::unique_lock<std::mutex> lock(lock_); - if (!is_wanting_to_run) { - is_exit_requested_ = true; - } - if (is_exit_requested_) { - // We don't try to self-report that this thread is done, because this - // thread isn't done running code of this method until the "ret" - // instruction at the end of this method (or equivalent) is over, so the - // only safe way to know that this thread is done running code of this - // method is to use OS-provided mechanism to determine that this thread - // is really done running, which std::thread.join() does do (or at - // least, certainly should do). - break; - } - } // ~lock - - strong.clear(); - strong = weak.promote(); - if (strong == nullptr) { - std::unique_lock<std::mutex> lock(lock_); - // It's nice to treat the strong refcount dropping to zero as an official - // exit request, before ~wp. - is_exit_requested_ = true; - } - } while (strong != nullptr); - // ~wp can be how this gets deleted, but for now we asser in the destructor - // if ~wp calls delete this, because that usage pattern isn't consistent with - // safe un-load of the code of a shared library. -} - -void Thread::requestExit() { - std::unique_lock<std::mutex> lock(lock_); - is_exit_requested_ = true; -} - -status_t Thread::requestExitAndWait() { - bool is_join_needed = false; - { // scope lock - std::unique_lock<std::mutex> lock(lock_); - if (!is_run_called_) { - return NO_ERROR; - } - if (thread_->get_id() == std::this_thread::get_id()) { - return WOULD_BLOCK; - } - if (!is_exit_requested_) { - is_exit_requested_ = true; - is_join_needed = true; - is_joiner_selected_ = true; - } else { - if (!is_joiner_selected_) { - is_join_needed = true; - is_joiner_selected_ = true; - } - } - // Even if this thread isn't the one selected to do the join, this thread - // still has to wait for the join to be done. - if (!is_join_needed) { - while (!is_joined_) { - joined_condition_.wait(lock); - } - return NO_ERROR; - } - } // ~lock - - assert(is_join_needed); - joinCommon(); - - return start_status_; -} - -bool Thread::isExitRequested() const { - std::unique_lock<std::mutex> lock(lock_); - return is_exit_requested_; -} - -void Thread::joinCommon() { - thread_->join(); - { // scope lock - std::unique_lock<std::mutex> lock(lock_); - is_joined_ = true; - } // ~lock - joined_condition_.notify_all(); -} - -} // namespace android diff --git a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/Timers.cc b/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/Timers.cc deleted file mode 100644 index 01ee273a811bc2acb7665c75613d6100b78470d1..0000000000000000000000000000000000000000 --- a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/Timers.cc +++ /dev/null @@ -1,17 +0,0 @@ -// 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 <utils/Timers.h> - -#include <zircon/syscalls.h> - -#include <cassert> - -nsecs_t systemTime(int clock) { - if (clock != SYSTEM_TIME_MONOTONIC) { - assert(clock == SYSTEM_TIME_MONOTONIC); - return 0; - } - return zx_clock_get_monotonic(); -} diff --git a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/binder/Parcel.h b/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/binder/Parcel.h deleted file mode 100644 index 4eed6184a28b4f6155652a1a63e401dc0ae073f8..0000000000000000000000000000000000000000 --- a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/binder/Parcel.h +++ /dev/null @@ -1,31 +0,0 @@ -// 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. - -#ifndef GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_BINDER_PARCEL_H_ -#define GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_BINDER_PARCEL_H_ - -#include <stdint.h> -#include <utils/Errors.h> - -namespace android { - -class Parcel { - public: - int32_t readInt32() const; - const char* readCString() const; - int64_t readInt64() const; - float readFloat() const; - double readDouble() const; - status_t writeInt32(int32_t value); - status_t writeCString(const char* string); - status_t writeInt64(int64_t value); - status_t writeFloat(float value); - status_t writeDouble(double value); - - private: -}; - -} // namespace android - -#endif // GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_BINDER_PARCEL_H_ diff --git a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/cutils/properties.h b/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/cutils/properties.h deleted file mode 100644 index 94a312d6bea23ddafb91c6c05b7f6e9fd5f6f021..0000000000000000000000000000000000000000 --- a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/cutils/properties.h +++ /dev/null @@ -1,16 +0,0 @@ -// 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. - -#ifndef GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_CUTILS_PROPERTIES_H_ -#define GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_CUTILS_PROPERTIES_H_ - -#define PROPERTY_VALUE_MAX 92 - -extern "C" { - -// This stub never finds anything. -int property_get(const char* key, char* value, const char* default_value); -} - -#endif // GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_CUTILS_PROPERTIES_H_ diff --git a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/define_typeof_and_unused.h b/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/define_typeof_and_unused.h deleted file mode 100644 index 72130907e99b57ce564e2fb1244db91b229bc283..0000000000000000000000000000000000000000 --- a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/define_typeof_and_unused.h +++ /dev/null @@ -1,17 +0,0 @@ -// 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. - -#ifndef GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_DEFINE_TYPEOF_AND_UNUSED_H_ -#define GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_DEFINE_TYPEOF_AND_UNUSED_H_ - -// On android this is a GCC intrinsic, but on Fuchsia we use clang. -#ifndef typeof -#define typeof __typeof__ -#endif - -// On android this would come from bionic/libc/include/sys/cdefs.h, but on -// Fuchsia we don't want to bring in Android's libc. -#define __unused - -#endif // GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_DEFINE_TYPEOF_AND_UNUSED_H_ diff --git a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/log/log.h b/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/log/log.h deleted file mode 100644 index c9ad2a9aaf8cc6a06828c7e694bc5098a68ecfc9..0000000000000000000000000000000000000000 --- a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/log/log.h +++ /dev/null @@ -1,82 +0,0 @@ -// 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. - -#ifndef GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_LOG_LOG_H_ -#define GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_LOG_LOG_H_ - -// Some android source relies on this being pulled in: -#include <unistd.h> - -#include <stdint.h> - -// This is a convenient place to pull in these non-log-related definitions for -// "typeof" and "__unused" without resorting to cflags -// -includedefine_typeof_and_unused.h which editors don't tend to pick up on. -#include "define_typeof_and_unused.h" - -#ifndef LOG_TAG -#define LOG_TAG nullptr -#endif - -enum { - ANDROID_LOG_UNKNOWN = 0, - ANDROID_LOG_DEFAULT, - ANDROID_LOG_VERBOSE, - ANDROID_LOG_DEBUG, - ANDROID_LOG_INFO, - ANDROID_LOG_WARN, - ANDROID_LOG_ERROR, - ANDROID_LOG_FATAL, - ANDROID_LOG_SILENT, -}; - -#define android_errorWriteLog(tag, subTag) \ - __android_log_error_write(tag, subTag, -1, nullptr, 0) - -#define android_printLog(prio, tag, ...) \ - __android_log_print(prio, tag, __VA_ARGS__) - -#define LOG_PRI(priority, tag, ...) android_printLog(priority, tag, __VA_ARGS__) -#define ALOG(priority, tag, ...) LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__) - -#define __android_second(dummy, second, ...) second -#define __android_rest(first, ...) , ##__VA_ARGS__ -#define android_printAssert(condition, tag, ...) \ - __android_log_assert(condition, tag, \ - __android_second(0, ##__VA_ARGS__, NULL) \ - __android_rest(__VA_ARGS__)) - -#define LOG_ALWAYS_FATAL_IF(condition, ...) \ - ((condition) \ - ? ((void)android_printAssert(#condition, LOG_TAG, ##__VA_ARGS__)) \ - : (void)0) - -#define LOG_ALWAYS_FATAL(...) \ - (((void)android_printAssert(NULL, LOG_TAG, ##__VA_ARGS__))) - -#define ALOGV(...) ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) -#define ALOGE(...) ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) -#define ALOGW(...) ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) - -inline void fake_alogi(...) {} -#define ALOGI(...) fake_alogi(__VA_ARGS__) - -#define LOG_FATAL_IF(cond, ...) LOG_ALWAYS_FATAL_IF(cond, ##__VA_ARGS__) - -#define LOG_FATAL(...) LOG_ALWAYS_FATAL(__VA_ARGS__) - -#define ALOG_ASSERT(cond, ...) LOG_FATAL_IF(!(cond), ##__VA_ARGS__) - -extern "C" { - -int __android_log_error_write(int tag, const char* subTag, int32_t uid, - const char* data, uint32_t dataLength); - -int __android_log_print(int priority, const char* tag, const char* format, ...); - -void __android_log_assert(const char* condition, const char* tag, - const char* format, ...); -} - -#endif // GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_LOG_LOG_H_ diff --git a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/media/stagefright/foundation/AHandler.h b/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/media/stagefright/foundation/AHandler.h deleted file mode 100644 index a19207a60e3e92ee456dd3d378f8ffd1a1323096..0000000000000000000000000000000000000000 --- a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/media/stagefright/foundation/AHandler.h +++ /dev/null @@ -1,39 +0,0 @@ -// 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. - -#ifndef GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_MEDIA_STAGEFRIGHT_FOUNDATION_AHANDLER_H_ -#define GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_MEDIA_STAGEFRIGHT_FOUNDATION_AHANDLER_H_ - -// Some android code relies on this being pulled in: -#include <media/stagefright/foundation/ALooper.h> - -#include <utils/RefBase.h> - -namespace android { - -struct AMessage; - -struct AHandler : public RefBase { - public: - AHandler(); - ALooper::handler_id id() const; - wp<ALooper> getLooper() const; - wp<AHandler> getHandler() const; - - protected: - virtual void onMessageReceived(const sp<AMessage>& msg) = 0; - - private: - friend struct AMessage; - friend struct ALooperRoster; - ALooper::handler_id id_; - wp<ALooper> looper_; - void setID(ALooper::handler_id id, const wp<ALooper>& looper); - void deliverMessage(const sp<AMessage>& message); - DISALLOW_EVIL_CONSTRUCTORS(AHandler); -}; - -} // namespace android - -#endif // GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_MEDIA_STAGEFRIGHT_FOUNDATION_AHANDLER_H_ diff --git a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/media/stagefright/foundation/ALooperRoster.h b/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/media/stagefright/foundation/ALooperRoster.h deleted file mode 100644 index 11b35432cd4e7674ae37b07678e4038e027bf955..0000000000000000000000000000000000000000 --- a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/media/stagefright/foundation/ALooperRoster.h +++ /dev/null @@ -1,29 +0,0 @@ -// 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. - -#ifndef GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_MEDIA_STAGEFRIGHT_FOUNDATION_ALOOPERROSTER_H_ -#define GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_MEDIA_STAGEFRIGHT_FOUNDATION_ALOOPERROSTER_H_ - -#include "ALooper.h" - -#include <mutex> - -namespace android { - -struct ALooperRoster { - public: - ALooperRoster(); - ALooper::handler_id registerHandler(const sp<ALooper>& looper, - const sp<AHandler>& handler); - void unregisterHandler(ALooper::handler_id handler_id); - void unregisterStaleHandlers(); - - private: - std::mutex mutex_; - ALooper::handler_id next_handler_id_; -}; - -} // namespace android - -#endif // GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_MEDIA_STAGEFRIGHT_FOUNDATION_ALOOPERROSTER_H_ diff --git a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/media/stagefright/foundation/AString.h b/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/media/stagefright/foundation/AString.h deleted file mode 100644 index 704c649381ec0efc520edb421c6a53808fceb813..0000000000000000000000000000000000000000 --- a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/media/stagefright/foundation/AString.h +++ /dev/null @@ -1,37 +0,0 @@ -// 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. - -#ifndef GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_MEDIA_STAGEFRIGHT_FOUNDATION_ASTRING_H_ -#define GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_MEDIA_STAGEFRIGHT_FOUNDATION_ASTRING_H_ - -#include <string> - -// Some android source relies on this being pulled in: -#include <utils/Errors.h> - -namespace android { - -// For now this uses inheritance, but it should be possible to switch to -// delegation should there be any good reason to do so. -struct AString : public std::string { - public: - AString(); - AString(const char* string); // implicit single-arg constructor intentional - AString(const char* string, size_t size); - AString( - const AString& copy_from); // implicit single-arg constructor intentional - void append(int number_to_append); - void append(const char* string_to_append); - void append(const char* string_to_append, size_t size); - void append(const AString& string_to_append); - AString& operator=(const AString& assign_from); - - private: -}; - -AString AStringPrintf(const char* format, ...); - -} // namespace android - -#endif // GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_MEDIA_STAGEFRIGHT_FOUNDATION_ASTRING_H_ diff --git a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/system/graphics.h b/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/system/graphics.h deleted file mode 100644 index b34b72bdbfe01741650f98adb61e0e865bd21220..0000000000000000000000000000000000000000 --- a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/system/graphics.h +++ /dev/null @@ -1,8 +0,0 @@ -// 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. - -#ifndef GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_SYSTEM_GRAPHICS_H_ -#define GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_SYSTEM_GRAPHICS_H_ - -#endif // GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_SYSTEM_GRAPHICS_H_ diff --git a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/utils/CallStack.h b/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/utils/CallStack.h deleted file mode 100644 index b2124349f79ea147d7040fe2278abe7852ca4034..0000000000000000000000000000000000000000 --- a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/utils/CallStack.h +++ /dev/null @@ -1,14 +0,0 @@ -// 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. - -#ifndef GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_UTILS_CALLSTACK_H_ -#define GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_UTILS_CALLSTACK_H_ - -// Some android source relies on this getting pulled in here. -// -// We might be able to just include log/log.h here instead, but this way is -// analogous to how android picks up its headers. -#include <utils/Vector.h> - -#endif // GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_UTILS_CALLSTACK_H_ diff --git a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/utils/Condition.h b/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/utils/Condition.h deleted file mode 100644 index d8cb595d89016424faf9808cffe88a3bb5244307..0000000000000000000000000000000000000000 --- a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/utils/Condition.h +++ /dev/null @@ -1,34 +0,0 @@ -// 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. - -#ifndef GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_UTILS_CONDITION_H_ -#define GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_UTILS_CONDITION_H_ - -#include <mutex> - -#include <utils/Errors.h> - -typedef int64_t nsecs_t; - -namespace android { - -class Mutex; - -class Condition { - public: - // no timeout - status_t wait(Mutex& to_wait_on); - status_t waitRelative(Mutex& mutex, nsecs_t relative_timeout); - // signal one waiting thread if there are any - void signal(); - // signal all waiting threads - void broadcast(); - - private: - std::condition_variable condition_; -}; - -} // namespace android - -#endif // GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_UTILS_CONDITION_H_ diff --git a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/utils/List.h b/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/utils/List.h deleted file mode 100644 index 028340b1956d3226d3441e14e05dd5d7f3004101..0000000000000000000000000000000000000000 --- a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/utils/List.h +++ /dev/null @@ -1,17 +0,0 @@ -// 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. - -#ifndef GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_UTILS_LIST_H_ -#define GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_UTILS_LIST_H_ - -#include <list> - -namespace android { - -template <typename T> -using List = std::list<T>; - -} // namespace android - -#endif // GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_UTILS_LIST_H_ diff --git a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/utils/Log.h b/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/utils/Log.h deleted file mode 100644 index 768e6b86b17dc9e8043e38cc35ae1395a8e7cabc..0000000000000000000000000000000000000000 --- a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/utils/Log.h +++ /dev/null @@ -1,10 +0,0 @@ -// 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. - -#ifndef GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_UTILS_LOG_H_ -#define GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_UTILS_LOG_H_ - -#include <log/log.h> - -#endif // GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_UTILS_LOG_H_ diff --git a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/utils/Mutex.h b/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/utils/Mutex.h deleted file mode 100644 index e05d20ce24709ac925dfc0d1ddc114686e0d358f..0000000000000000000000000000000000000000 --- a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/utils/Mutex.h +++ /dev/null @@ -1,42 +0,0 @@ -// 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. - -#ifndef GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_UTILS_MUTEX_H_ -#define GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_UTILS_MUTEX_H_ - -#include <mutex> - -namespace android { - -// TODO: Maybe map this to fbl::Mutex instead. - -// This is not meant to be complete - only meant to get OMX code to compile, -// link, and run without editing the OMX files. - -class Mutex { - public: - class Autolock { - public: - explicit Autolock(Mutex& mutex_to_lock) { - mutex_ = &mutex_to_lock; - mutex_->mutex_.lock(); - } - ~Autolock() { mutex_->mutex_.unlock(); } - - private: - Mutex* mutex_; - }; - Mutex(); - explicit Mutex(const char* name); - - private: - friend class Condition; - std::mutex mutex_; - Mutex(const Mutex&) = delete; - Mutex& operator=(const Mutex&) = delete; -}; - -} // namespace android - -#endif // GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_UTILS_MUTEX_H_ diff --git a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/utils/String16.h b/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/utils/String16.h deleted file mode 100644 index 0e5f785d7c6c42f8f3af2082ef7dde94a0728302..0000000000000000000000000000000000000000 --- a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/utils/String16.h +++ /dev/null @@ -1,21 +0,0 @@ -// 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. - -#ifndef GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_UTILS_STRING16_H_ -#define GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_UTILS_STRING16_H_ - -#include <string> - -namespace android { - -class String16 : public std::u16string { - public: - explicit String16(const char* from_string); - - private: -}; - -} // namespace android - -#endif // GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_UTILS_STRING16_H_ diff --git a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/utils/String8.h b/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/utils/String8.h deleted file mode 100644 index 696fc9282b4bb07e7d19174ab4d6033f202ac08d..0000000000000000000000000000000000000000 --- a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/utils/String8.h +++ /dev/null @@ -1,26 +0,0 @@ -// 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. - -#ifndef GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_UTILS_STRING8_H_ -#define GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_UTILS_STRING8_H_ - -#include <utils/Errors.h> -#include <string> - -namespace android { - -class String8 : public std::string { - public: - String8(); - status_t appendFormat(const char* format, ...) - __attribute__((format(printf, 2, 3))); - status_t appendFormatV(const char* format, va_list args); - const char* string() const; - - private: -}; - -} // namespace android - -#endif // GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_UTILS_STRING8_H_ diff --git a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/utils/Thread.h b/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/utils/Thread.h deleted file mode 100644 index 9d7633546cc35628a6d9a7d70b26afaeee55ac35..0000000000000000000000000000000000000000 --- a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/utils/Thread.h +++ /dev/null @@ -1,85 +0,0 @@ -// 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. - -#ifndef GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_UTILS_THREAD_H_ -#define GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_UTILS_THREAD_H_ - -#include <utils/Condition.h> -#include <utils/Errors.h> -#include <utils/RefBase.h> -#include <utils/Timers.h> - -#include <thread> - -extern "C" { - -enum { - ANDROID_PRIORITY_FOREGROUND = -2, -}; -} - -namespace android { - -enum { - PRIORITY_DEFAULT = 0, -}; - -// DO NOT USE FOR NEW CODE - please use std::thread, or something else. This -// shim is only to allow some android code to compile and run on Fuchsia. - -// Intentionally do not support repeated calls to run(), even if the android -// implementation may be trying to support that (unclear). In this -// implementation an instance of this class can only correspond to up to one -// underlying thread lifetime, by design. -// -// The proper way to wait until the thread is really actually fully done running -// is to call requestExitAndWait(), or requestExit() and ~Thread. FWIW, until -// that's done, it's not safe to do something that could change running code -// such as un-load of a shared library that contains an instance of the code of -// this class, since the tail end of _threadLoop() could still be running on the -// thread. We expect libc++.so to remain loaded, so we don't need to analyze -// whether std::thread code itself is robust to code unloading. We don't -// currently expect to un-load any code (including the code of this class), but -// this class should be reasonably ready for code unloading should it be added -// at some point. - -// The virtual inheritance is probably not currently important for the current -// Fuchsia usage, but is here to maximize compatibility for now. -class Thread : virtual public RefBase { - public: - // This Thread shim on Fuchsia only supports can_call_java == false, else - // abort(). - explicit Thread(bool can_call_java = true); - virtual ~Thread(); - virtual status_t run(const char* thread_name, - int32_t thread_priority = PRIORITY_DEFAULT, - size_t stack_size = 0); - virtual void requestExit(); - status_t requestExitAndWait(); - - protected: - // This would be private or completely removed in the Fuchsia implementation - // except for ALooper using it to stash the thread ID. - virtual status_t readyToRun(); - - private: - virtual bool threadLoop() = 0; - void _threadLoop(); - bool isExitRequested() const; - void joinCommon(); - mutable std::mutex lock_; - bool is_run_called_ = false; - std::unique_ptr<std::thread> thread_; - // The status of starting the thread, not anything more. - status_t start_status_ = NO_ERROR; - bool is_exit_requested_ = false; - bool is_joiner_selected_ = false; - bool is_joined_ = false; - std::condition_variable joined_condition_; - sp<Thread> hold_self_; -}; - -} // namespace android - -#endif // GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_UTILS_THREAD_H_ diff --git a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/utils/Vector.h b/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/utils/Vector.h deleted file mode 100644 index 1fe6ef17760d8a7138222a6033804654ac9845bf..0000000000000000000000000000000000000000 --- a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/utils/Vector.h +++ /dev/null @@ -1,121 +0,0 @@ -// 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. - -#ifndef GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_UTILS_VECTOR_H_ -#define GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_UTILS_VECTOR_H_ - -// Some android sources rely on this being pulled in: -#include <log/log.h> - -#include <vector> - -#ifndef __has_attribute -#define __has_attribute(x) 0 -#endif - -#if __has_attribute(no_sanitize) -#define UTILS_VECTOR_NO_CFI __attribute__((no_sanitize("cfi"))) -#else -#define UTILS_VECTOR_NO_CFI -#endif - -namespace android { - -template <typename T> -class Vector { - public: - inline size_t size() const { return vector_.size(); } - ssize_t add(const T& item); - inline const T& itemAt(size_t index) const; - const T& top() const; - inline void pop(); - inline void push(); - void push(const T& item); - T& editItemAt(size_t index); - inline ssize_t removeAt(size_t index); - inline void clear(); - inline bool empty() const; - inline const T& operator[](size_t index) const; - - private: - std::vector<T> vector_; - T dummy_{}; -}; - -template <typename T> -inline ssize_t Vector<T>::add(const T& item) { - ssize_t index_of_new_item = vector_.size(); - vector_.push_back(item); - return index_of_new_item; -} - -template <typename T> -inline const T& Vector<T>::itemAt(size_t index) const { - return vector_.operator[](index); -} - -template <typename T> -inline const T& Vector<T>::top() const { - return vector_[vector_.size() - 1]; -} - -template <typename T> -inline void Vector<T>::pop() { - if (vector_.empty()) { - return; - } - vector_.pop_back(); -} - -template <typename T> -inline void Vector<T>::push() { - vector_.emplace_back(); -} - -template <typename T> -inline void Vector<T>::push(const T& item) { - vector_.push_back(item); -} - -template <typename T> -inline T& Vector<T>::editItemAt(size_t index) { - if (index >= vector_.size()) { - // SoftAAC2.cpp:987 can attempt a read via a reference returned from - // editItemAt(0) despite there being zero items in the vector (for an - // ALOGV). For now, we have editItemAt() provide a reference to a dummy - // here if the caller is asking for a reference to an entry that doesn't - // exist. - // - // TODO(dustingreen): Either fix SoftAAC2.cpp to not do that read, or stop - // using SoftAAC2.cpp. For now we're avoiding making any modifications to - // SoftAAC2.cpp. - return dummy_; - } - return vector_[index]; -} - -template <typename T> -inline ssize_t Vector<T>::removeAt(size_t index) { - vector_.erase(vector_.begin() + index); - return index; -} - -template <typename T> -inline void Vector<T>::clear() { - vector_.clear(); -} - -template <typename T> -inline bool Vector<T>::empty() const { - return vector_.empty(); -} - -template <typename T> -inline const T& Vector<T>::operator[](size_t index) const { - return vector_[index]; -} - -} // namespace android - -#endif // GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_UTILS_VECTOR_H_ diff --git a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/utils/threads.h b/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/utils/threads.h deleted file mode 100644 index 241a8476e318e57bfa78c3cd775450a5e4d79fef..0000000000000000000000000000000000000000 --- a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/include/utils/threads.h +++ /dev/null @@ -1,19 +0,0 @@ -// 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. - -#ifndef GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_UTILS_THREADS_H_ -#define GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_UTILS_THREADS_H_ - -#include <utils/Condition.h> -#include <utils/Mutex.h> -#include <utils/Thread.h> -#include <thread> - -using android_thread_id_t = void*; - -inline android_thread_id_t androidGetThreadId() { - return (android_thread_id_t)pthread_self(); -} - -#endif // GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_INCLUDE_UTILS_THREADS_H_ diff --git a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/not_Parcel.cc b/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/not_Parcel.cc deleted file mode 100644 index f28c4e28467d25eb35d83bdbb30d41b286004639..0000000000000000000000000000000000000000 --- a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/not_Parcel.cc +++ /dev/null @@ -1,61 +0,0 @@ -// 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 "binder/Parcel.h" - -#include <cassert> - -namespace android { - -int32_t Parcel::readInt32() const { - assert(false && "not implemented"); - return 0; -} - -const char* Parcel::readCString() const { - assert(false && "not implemented"); - return nullptr; -} - -int64_t Parcel::readInt64() const { - assert(false && "not implemented"); - return 0; -} - -float Parcel::readFloat() const { - assert(false && "not implemented"); - return 0.0f; -} - -double Parcel::readDouble() const { - assert(false && "not implemented"); - return 0.0l; -} - -status_t Parcel::writeInt32(int32_t value) { - assert(false && "not implemented"); - return UNKNOWN_ERROR; -} - -status_t Parcel::writeCString(const char* str) { - assert(false && "not implemented"); - return UNKNOWN_ERROR; -} - -status_t Parcel::writeInt64(int64_t value) { - assert(false && "not implemented"); - return UNKNOWN_ERROR; -} - -status_t Parcel::writeFloat(float value) { - assert(false && "not implemented"); - return UNKNOWN_ERROR; -} - -status_t Parcel::writeDouble(double value) { - assert(false && "not implemented"); - return UNKNOWN_ERROR; -} - -} // namespace android diff --git a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/port.cc b/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/port.cc deleted file mode 100644 index 60111ed76e193c5ba56d601f3c23e5f61e197a73..0000000000000000000000000000000000000000 --- a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/port.cc +++ /dev/null @@ -1,93 +0,0 @@ -// 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 <cassert> -#include <cstdarg> -#include <cstdint> -#include <cstdio> - -#include "log/log.h" - -#include <src/lib/fxl/debug/debugger.h> -#include <src/lib/fxl/logging.h> - -extern "C" { - -// NOP replacement for Android's property_get: -int property_get(const char* key, char* value, const char* default_value) { - return 0; -} - -int __android_log_print(int priority, const char* tag, const char* format, - ...) { - if (priority == ANDROID_LOG_VERBOSE) { - return 1; - } - // TODO: maybe concatenate before sending to printf in hopes that less output - // would be split (if it starts to become a problem). - const char* local_tag = tag; - if (!local_tag) { - local_tag = "<NO_TAG>"; - } - printf("%d %s ", priority, local_tag); - va_list ap; - va_start(ap, format); - vprintf(format, ap); - va_end(ap); - printf("\n"); - return 1; -} - -void __android_log_assert(const char* condition, const char* tag, - const char* format, ...) { - // TODO: maybe concatenate before sending to printf in hopes that less output - // would be split (if it starts to become a problem). - const char* local_tag = tag; - if (!local_tag) { - local_tag = "<NO_TAG>"; - } - printf("__android_log_assert: condition: %s tag: %s ", condition, local_tag); - if (format) { - va_list ap; - va_start(ap, format); - vprintf(format, ap); - va_end(ap); - } - printf("\n"); - - fxl::BreakDebugger(); - exit(-1); -} - -void __assert2(const char* file, int line, const char* function, - const char* failed_expression) { - printf( - "omx_android_pal assert failed: file: %s line: %d function: %s " - "failed_expression: %s", - file, line, function, failed_expression); - assert(false && "see omx_android_pal assert failure output above"); -} - -int __android_log_error_write(int tag, const char* sub_tag, int32_t uid, - const char* data, uint32_t data_length) { - // For now we drop the data part - if we see any of this happening we may need - // to plumb that part. - printf( - "__android_log_error_write: tag: %d sub_tag: %s uid: %d data_length: " - "%d\n", - tag, sub_tag, uid, data_length); - return 0; -} - -} // extern "C" - -namespace android { - -struct AString; - -void hexdump(const void* data, size_t size, size_t indent, AString* append_to) { - printf("hexdump() requested but not yet implemented\n"); -} - -} // namespace android diff --git a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/so_entry_point.cc b/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/so_entry_point.cc deleted file mode 100644 index c1839010784dacf9b5edaf1763cba0872c286588..0000000000000000000000000000000000000000 --- a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/so_entry_point.cc +++ /dev/null @@ -1,139 +0,0 @@ -// 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 "so_entry_point.h" - -// SoftOMXComponent.h assumes that <log/log.h> is included first. -#include <log/log.h> -#include <media/stagefright/omx/SoftOMXComponent.h> - -#include <lib/fit/defer.h> - -// We intentionally don't have a "using namespace android;" because this is an -// adapter layer and we want to make clear what's raw OMX and what's android -// stuff that we're hiding from the caller of the entry point defined at the -// bottom of this file. - -// In android sources, the per-OMX-codec common entry point signature isn't in -// any header file, so we just declare it here. We're using this symbol locally -// within each per-codec binary we build for Fuchsia, and wrapping it with an -// extern "C" shared_library entry point that doesn't return a C++ object. Only -// the latter is exported from the per-android-codec fuchsia shared lib. -android::SoftOMXComponent *createSoftOMXComponent( - const char *name, const OMX_CALLBACKTYPE *callbacks, OMX_PTR appData, - OMX_COMPONENTTYPE **component); - -namespace { - -// A pointer to this function gets used as an OMX_COMPONENTTYPE.ComponentDeInit. -OMX_ERRORTYPE ComponentDeInit(OMX_IN OMX_HANDLETYPE hComponent) { - auto *me = (android::SoftOMXComponent *)((OMX_COMPONENTTYPE *)hComponent) - ->pComponentPrivate; - me->prepareForDestruction(); - me->decStrong(reinterpret_cast<void *>(ComponentDeInit)); - // It's important that by this point any threads that were created by - // SimpleSoftOMXComponent or by the lower-layer codec core (if any) are - // totally done running any code of the present shared library, as the caller - // of this function will _un-load the code_ of this shared library. - return OMX_ErrorNone; -} - -} // namespace - -extern "C" { -// This interface is not meant to be Fuchsia-wide for SW codecs. For that, see -// the Codec FIDL interface defined elsewhere. This commonality of interface -// here is just for building and loading various SW codecs from the android -// sources. -// -// Sets *component to nullptr if create fails, or to non-nullptr if create -// succeeds. -void entrypoint_createSoftOMXComponent(const char *name, - const OMX_CALLBACKTYPE *callbacks, - OMX_PTR appData, - OMX_COMPONENTTYPE **component) { - // default to reporting failure uness we get far enough - *component = nullptr; - // We use the android::sp to ensure that every path from here forward will - // have at least one strong reference on the SoftOMXComponent (added here if - // not nullptr), including error paths. - android::sp<android::SoftOMXComponent> component_cpp = - createSoftOMXComponent(name, callbacks, appData, component); - if (!component_cpp) { - // TODO: can we log an error somewhere and/or return richer error info from - // a shared_library entry point in Fuchsia-standard ways? - - // assert that we are reporting failure: - assert(!(*component)); - return; - } - // Unfortunately the android code doesn't take advantage of - // RefBase::onLastStrongRef(), and doesn't seem worth making a wrapper that - // does just for the benefit of this source file. - // - // unless cancelled - auto pfd = fit::defer( - [&component_cpp]() { component_cpp->prepareForDestruction(); }); - if (OMX_ErrorNone != component_cpp->initCheck()) { - assert(!(*component)); - return; - } - if (static_cast<OMX_PTR>(component_cpp.get()) != - (*component)->pComponentPrivate) { - // The android code changed to no longer stash SoftOMXComponent* where this - // code expects. At the moment there doesn't seem to be any good way to - // wrap more thoroughly that doesn't also risk getting broken by android - // changes, so if the stashing has changed in android code, fail the create. - - // assert that we are reporting failure: - assert(!(*component)); - // ~pfd - // ~component_cpp - return; - } - - if ((*component)->ComponentDeInit) { - // The android code has changed to fill out this function pointer. Without - // a more thourough wrapping, which would itself be subject to breakage by - // android changes that add more function pointers (to callbacks and/or to - // component), we have no great place to stash the value of ComponentDeInit. - // An alternative would be for the present entry point to fill out a wrapper - // of OMX_COMPONENTTYPE that just points to an OMX_COMPONENTTYPE, but the - // benefit/cost of that doesn't seem high enough, at least for now. So if - // android code changed to start using this function pointer, fail the - // create. - - // assert that we are reporting failure: - assert(!(*component)); - // ~pfd - // ~component_cpp - return; - } - - // This ComponentDeInit will call prepareForDestruction(). - (*component)->ComponentDeInit = ComponentDeInit; - // Don't call prepareForDestruction() during ~pfd. - pfd.cancel(); - - // Prevent ~component_cpp from deleting the codec. This ref will be removed - // by ComponentDeInit, so may as well use that as the void* on the ref. - component_cpp->incStrong(reinterpret_cast<void *>(ComponentDeInit)); - - // The non-use of setLibHandle() and libHandle() is intentional, since the - // loading and un-loading of the shared library is handled in a layer above - // that doesn't see SoftOMXComponent. - - return; -} - -// This function is only used when linked as a static lib, for debug-cycle -// purposes only. -void direct_createSoftOMXComponent(const char *name, - const OMX_CALLBACKTYPE *callbacks, - OMX_PTR appData, - OMX_COMPONENTTYPE **component) { - entrypoint_createSoftOMXComponent(name, callbacks, appData, component); -} - -} // extern "C" diff --git a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/so_entry_point/so_entry_point.h b/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/so_entry_point/so_entry_point.h deleted file mode 100644 index 9f5227578f973e56036d3a95ebd416a0a1e35cae..0000000000000000000000000000000000000000 --- a/garnet/bin/media/codecs/sw/omx/common/omx_android_pal/so_entry_point/so_entry_point.h +++ /dev/null @@ -1,32 +0,0 @@ -// 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. - -#ifndef GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_SO_ENTRY_POINT_SO_ENTRY_POINT_H_ -#define GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_SO_ENTRY_POINT_SO_ENTRY_POINT_H_ - -#include <OMX_Component.h> - -// For __EXPORT -#include <zircon/compiler.h> - -__EXPORT -extern "C" void entrypoint_createSoftOMXComponent( - const char *name, const OMX_CALLBACKTYPE *callbacks, OMX_PTR appData, - OMX_COMPONENTTYPE **component); - -typedef void (*createSoftOMXComponent_fn)(const char *name, - const OMX_CALLBACKTYPE *callbacks, - OMX_PTR appData, - OMX_COMPONENTTYPE **component); - -// This is only available if the .so's code is linked as a static lib instead. -// This is not the normal way, but can be a faster debug cycle than using the -// .so. If linking as an .so, this symbol won't be available, so trying to call -// it will just fail to link. -extern "C" void direct_createSoftOMXComponent(const char *name, - const OMX_CALLBACKTYPE *callbacks, - OMX_PTR appData, - OMX_COMPONENTTYPE **component); - -#endif // GARNET_BIN_MEDIA_CODECS_SW_OMX_COMMON_OMX_ANDROID_PAL_SO_ENTRY_POINT_SO_ENTRY_POINT_H_ diff --git a/garnet/bin/media/codecs/sw/omx/dec/BUILD.gn b/garnet/bin/media/codecs/sw/omx/dec/BUILD.gn deleted file mode 100644 index 1b1a1c28acc8ea45b2b24886abb2939b6059110d..0000000000000000000000000000000000000000 --- a/garnet/bin/media/codecs/sw/omx/dec/BUILD.gn +++ /dev/null @@ -1,10 +0,0 @@ -# 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. - -group("dec") { - testonly = true - deps = [ - "aac", - ] -} diff --git a/garnet/bin/media/codecs/sw/omx/dec/aac/BUILD.gn b/garnet/bin/media/codecs/sw/omx/dec/aac/BUILD.gn deleted file mode 100644 index d3e1b8b2eef1de6a966a44bad984d576d2a2143c..0000000000000000000000000000000000000000 --- a/garnet/bin/media/codecs/sw/omx/dec/aac/BUILD.gn +++ /dev/null @@ -1,27 +0,0 @@ -# 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. - -group("aac") { - testonly = true - deps = [ - ":libcodec_sw_omx_dec_aac", - ] -} - -loadable_module("libcodec_sw_omx_dec_aac") { - visibility = [ - "//garnet/bin/media:codec_runner_sw_omx", - "//garnet/bin/media:codec_runner_sw_omx.manifest", - "//garnet/bin/media/*", # TODO(CF-235): Dep shouldn' be needed - ] - sources = [ - # The AOSP OMX SW AAC decoder code: - "//third_party/android/platform/frameworks/av/media/libstagefright/codecs/aacdec/DrcPresModeWrap.cpp", - "//third_party/android/platform/frameworks/av/media/libstagefright/codecs/aacdec/SoftAAC2.cpp", - ] - deps = [ - "//garnet/bin/media/codecs/sw/omx/common/omx_android_pal:omx_android_pal", - "//garnet/bin/media/codecs/sw/omx/low_layer/aac:libFraunhoferAAC", - ] -} diff --git a/garnet/examples/media/use_media_decoder/use_aac_decoder.cc b/garnet/examples/media/use_media_decoder/use_aac_decoder.cc index 7a84d6d13a14beb9847c6a3c0f332ea227d9e103..22979791f6ee87852e554a8cc0242c593d1b6fdc 100644 --- a/garnet/examples/media/use_media_decoder/use_aac_decoder.cc +++ b/garnet/examples/media/use_media_decoder/use_aac_decoder.cc @@ -59,25 +59,8 @@ std::unique_ptr<uint8_t[]> make_AudioSpecificConfig_from_ADTS_header( std::unique_ptr<uint8_t[]>* input_bytes) { std::unique_ptr<uint8_t[]> asc = std::make_unique<uint8_t[]>(2); - // TODO(dustingreen): Switch from ADTS to .mp4 and fix AAC decoder to not - // require "AudioSpecificConfig()" when fed ADTS. In other words, move the - // stuff here into a shim around the AAC OMX decoder, just next to (above or - // below) the OmxCodecRunner in the codec_runner_sw_omx isolate, probably. - - // For SoftAAC2.cpp, for no particularly good reason, a CODECCONFIG buffer is - // expected, even when running in ADTS mode, despite all the relevant data - // being available from the ADTS header. The CODECCONFIG buffer has an - // AudioSpecificConfig in it. The AudioSpecificConfig has to be created based - // on corresponding fields of the ADTS header - not that requiring this of - // the codec client makes any sense whatsoever... - // - // TODO(dustingreen): maybe add a per-codec compensation layer to un-crazy the - // quirks of each codec. For example, when decoding ADTS, all the needed info - // is there in the ADTS stream directly. No reason to hassle the codec client - // for a pointless translated form of the same info. In contrast, when it's - // an mp4 file (or mkv, or whatever modern container format), the codec config - // info is relevant. But we should only force a client to provide it if - // it's really needed. + // TODO(dustingreen): Remove this function as we don't need to be synthesizing + // oob_bytes from ADTS header data. // First, parse the stuff that's needed from the first ADTS header. uint8_t* adts_header = static_cast<uint8_t*>(input_bytes->get()); @@ -427,11 +410,6 @@ void use_aac_decoder(async::Loop* main_loop, auto cleanup = fit::defer([&codec_client, &packet] { // Using an auto call for this helps avoid losing track of the // output_buffer. - // - // If the omx_state_ or omx_state_desired_ isn't correct, - // UseOutputBuffer() will fail. The only way that can happen here is - // if the OMX codec transitioned states unilaterally without any set - // state command, so if that occurs, exit. codec_client.RecycleOutputPacket(fidl::Clone(packet.header())); }); diff --git a/garnet/examples/media/use_media_decoder/use_video_decoder.cc b/garnet/examples/media/use_media_decoder/use_video_decoder.cc index c4d84c18235e9d7fc2d75a00415bcd3e1de80262..554cb318e0b0a355e1e426bd6df60e22d8e9994d 100644 --- a/garnet/examples/media/use_media_decoder/use_video_decoder.cc +++ b/garnet/examples/media/use_media_decoder/use_video_decoder.cc @@ -414,11 +414,6 @@ static void use_video_decoder( packet_header = fidl::Clone(packet.header())]() mutable { // Using an auto call for this helps avoid losing track of the // output_buffer. - // - // If the omx_state_ or omx_state_desired_ isn't correct, - // UseOutputBuffer() will fail. The only way that can happen here - // is if the OMX codec transitioned states unilaterally without any - // set state command, so if that occurs, exit. codec_client.RecycleOutputPacket(std::move(packet_header)); }); std::shared_ptr<const fuchsia::media::StreamOutputFormat> format = diff --git a/garnet/lib/media/codec_impl/codec_impl.cc b/garnet/lib/media/codec_impl/codec_impl.cc index 58af37237d6b5807bff5de6585f3c1b3d3c21866..8b847390d7f6a8f5d9dfa9d5304213a235bc102d 100644 --- a/garnet/lib/media/codec_impl/codec_impl.cc +++ b/garnet/lib/media/codec_impl/codec_impl.cc @@ -623,14 +623,14 @@ void CodecImpl::FlushEndOfStreamAndCloseStream_StreamControl( // server must do one of those things before long (not allowed to get // stuck while flushing). // - // Some core codecs (such as OMX codecs) have no way to report mid-stream - // input data corruption errors or similar without it being a stream - // failure, so if there's any stream error it turns into OnStreamFailed(). - // It's also permitted for a server to set error_detected_ bool(s) on - // output packets and send OnOutputEndOfStream() despite detected errors, - // but this is only a reasonable behavior for the server if the server - // normally would detect and report mid-stream input corruption errors - // without an OnStreamFailed(). + // Some core codecs have no way to report mid-stream input data corruption + // errors or similar without it being a stream failure, so if there's any + // stream error it turns into OnStreamFailed(). It's also permitted for a + // server to set error_detected_ bool(s) on output packets and send + // OnOutputEndOfStream() despite detected errors, but this is only a + // reasonable behavior for the server if the server normally would detect + // and report mid-stream input corruption errors without an + // OnStreamFailed(). output_end_of_stream_seen_.wait(lock); } @@ -2202,9 +2202,9 @@ bool CodecImpl::StartNewStream(std::unique_lock<std::mutex>& lock, // the "meh" was with respect to the old stream, but just in case a core codec // cares, we move on from the old config before delivering new stream data. // - // Some core codecs (such as OMX codecs) require the output to be configured - // to _something_ as they don't support giving us the real output config - // unless the output is configured to at least something at first. + // Some core codecs may require the output to be configured to _something_ as + // they don't support giving us the real output config unless the output is + // configured to at least something at first. // // Other core codecs (such as some HW-based codecs) can deal with no output // configured while detecting the output format, but even for those codecs, we @@ -2524,9 +2524,6 @@ bool CodecImpl::EnsureFutureStreamFlushSeenLocked( // this method and GenerateAndSendNewOutputConstraints() with // buffer_constraints_action_required true always run in pairs. // -// This is what starts the interval during which -// OmxTryRecycleOutputPacketLocked() won't call OMX. -// // If the client is in the middle of configuring output, we'll start ignoring // the client's messages re. the old buffer_lifetime_ordinal and old // buffer_constraints_version_ordinal until the client catches up to the new @@ -2698,18 +2695,11 @@ void CodecImpl::MidStreamOutputConstraintsChange(uint64_t stream_lifetime_ordina } ZX_DEBUG_ASSERT(stream_lifetime_ordinal == stream_lifetime_ordinal_); - // Now we need to start disabling the port, wait for buffers to come back - // from OMX, free buffer headers, wait for the port to become fully - // disabled, unilaterally de-configure output buffers, demand a new output - // config from the client, wait for the client to configure output (but be - // willing to bail on waiting for the client if we notice future stream - // discard), re-enable the output port, allocate headers, wait for the port - // to be fully enabled, call FillThisBuffer() on the protocol-free buffers. - - // This is what starts the interval during which - // OmxTryRecycleOutputPacketLocked() won't call OMX, and the interval during - // which we'll ignore any in-progress client output config until the client - // catches up. + // We can work through the mid-stream output constraints change step by step + // using this thread. + + // This is what starts the interval during which we'll ignore any + // in-progress client output config until the client catches up. StartIgnoringClientOldOutputConfig(lock); { // scope unlock @@ -3140,9 +3130,7 @@ void CodecImpl::onCoreCodecMidStreamOutputConstraintsChange( // re-config before more output data, this translates to an ordered emit // of a no-action-required OnOutputConstraints() that just updates to the new // format, without demanding output buffer re-config. HDR info can be - // conveyed this way, ordered with respect to output frames. OMX - // requires that we use this thread to collect OMX format info during - // EventHandler(). + // conveyed this way, ordered with respect to output frames. if (!output_re_config_required) { std::unique_lock<std::mutex> lock(lock_); GenerateAndSendNewOutputConstraints( @@ -3161,7 +3149,7 @@ void CodecImpl::onCoreCodecMidStreamOutputConstraintsChange( // talking about the same stream_lifetime_ordinal, and if not, we ignore // the event, because a new stream may or may not have the same output // settings, and we'll be re-generating an OnOutputConstraints() as needed - // from current/later OMX output config anyway. Here are the + // from current/later core codec output constraints anyway. Here are the // possibilities: // * Prior to the client moving to a new stream, we process this event // on StreamControl ordering domain and have bumped @@ -3193,8 +3181,8 @@ void CodecImpl::onCoreCodecMidStreamOutputConstraintsChange( // For asserts. stream_->SetMidStreamOutputConstraintsChangeActive(); - // This part is not speculative. OMX has indicated that it's at least - // meh about the current output config, so ensure we do a required + // This part is not speculative. The core codec has indicated that it's at + // least meh about the current output config, so ensure we do a required // OnOutputConstraints() before the next stream starts, even if the client // moves on to a new stream such that the speculative part below becomes // stale. @@ -3228,8 +3216,8 @@ void CodecImpl::onCoreCodecOutputFormatChange() { } void CodecImpl::onCoreCodecInputPacketDone(CodecPacket* packet) { - // Free/busy coherency from Codec interface to OMX doesn't involve trusting - // the client, so assert we're doing it right server-side. + // Free/busy coherency from Codec interface to core codec doesn't involve + // trusting the client, so assert we're doing it right server-side. { // scope lock std::unique_lock<std::mutex> lock(lock_); // The core codec says the buffer-referening in-flight lifetime of this diff --git a/garnet/lib/media/codec_impl/include/lib/media/codec_impl/codec_impl.h b/garnet/lib/media/codec_impl/include/lib/media/codec_impl/codec_impl.h index ad55ff9ca84f5f8e923f5e0c1fdce380112281c1..9ad9e433dfefabd65c808b323d163b56c84af4cd 100644 --- a/garnet/lib/media/codec_impl/include/lib/media/codec_impl/codec_impl.h +++ b/garnet/lib/media/codec_impl/include/lib/media/codec_impl/codec_impl.h @@ -705,10 +705,8 @@ class CodecImpl : public fuchsia::media::StreamProcessor, // The next value we'll use for output buffer_constraints_version_ordinal and // output format_details_version_ordinal. uint64_t next_output_buffer_constraints_version_ordinal_ = 1; - // For the OMX adapter, if the buffer constraints change, then the format - // details ordinal also changes (since there's not really any benefit to - // detecting lack of change). But for format-only changes that don't require - // buffer re-allocation, we can just increment the format details ordinal. + // For format-only changes that don't require buffer re-allocation, we can + // just increment the format details ordinal. uint64_t next_output_format_details_version_ordinal_ = 1; // Separately from ordinal allocation, we track the most recent ordinal that @@ -755,10 +753,6 @@ class CodecImpl : public fuchsia::media::StreamProcessor, // // Adapter-related // - // TODO(dustingreen): Try to generalize this section more or move anything - // core-codec-specific to a different class, to make fully common between HW - // and OMX cases at least, if not fully general. - // // This is called on Output ordering domain (FIDL thread) any time a message // is received which would be able to start a new stream. diff --git a/garnet/lib/media/codec_impl/include/lib/media/codec_impl/codec_packet.h b/garnet/lib/media/codec_impl/include/lib/media/codec_impl/codec_packet.h index 31668d809443b066345f251aedd1a08fe8a6efa2..bd46ca577f69e2281b8dcaabc40bb235ca5d210a 100644 --- a/garnet/lib/media/codec_impl/include/lib/media/codec_impl/codec_packet.h +++ b/garnet/lib/media/codec_impl/include/lib/media/codec_impl/codec_packet.h @@ -99,8 +99,9 @@ class CodecPacket { // CodecAdapter's point of view. This allows for the CodecAdapter to // determine whether to recycle a packet to the core codec depending on // whether the packet is new or not, on first call to - // CoreCodecRecycleOutputPacket(). Some core codecs want an internal recycle - // call or equivalent for new packets (OMX), and some don't (amlogic-video). + // CoreCodecRecycleOutputPacket(). Some core codecs potentially want an + // internal recycle call or equivalent for new packets, while others don't + // (such as amlogic-video). bool is_new_ = true; CodecPacket() = delete; diff --git a/sdk/fidl/fuchsia.mediacodec/codec_factory.fidl b/sdk/fidl/fuchsia.mediacodec/codec_factory.fidl index 6757887388d14ac6f15ba3915721d72f90641fe6..55492728d49cd7860bff6dd74ddb2eb0583ba814 100644 --- a/sdk/fidl/fuchsia.mediacodec/codec_factory.fidl +++ b/sdk/fidl/fuchsia.mediacodec/codec_factory.fidl @@ -21,16 +21,6 @@ table CreateDecoder_Params { // audio/aac // input_details.oob_bytes must be an AudioSpecificConfig() as defined // by AAC spec. - // audio/aac-adts - // On a temporary basis, input_details.oob_bytes must be an - // AudioSpecificConfig() extracted from the ADTS stream data by the client. - // This is temporary. Later, oob_bytes will be ignored and allowed to - // be null. On a temporary basis, see - // make_AudioSpecificConfig_from_ADTS_header() for some self-contained - // conversion code. - // TODO(dustingreen): fix this up server side and make most of this - // paragraph go away, possibly by also parsing ADTS before it hits OMX - // since the parsing code there doesn't tolerate split ADTS headers. 1: fuchsia.media.FormatDetails input_details; // The settings below nail down more details. diff --git a/sdk/fidl/fuchsia.mediacodec/fuchsia.mediacodec.api b/sdk/fidl/fuchsia.mediacodec/fuchsia.mediacodec.api index 092394b33e6016fa09080f42947512ebe7639ec9..7cbd338a7936dd3b6f294e4932fe1d5f82a7c33a 100644 --- a/sdk/fidl/fuchsia.mediacodec/fuchsia.mediacodec.api +++ b/sdk/fidl/fuchsia.mediacodec/fuchsia.mediacodec.api @@ -1,3 +1,3 @@ { - "fidl/fuchsia.mediacodec/codec_factory.fidl": "1390da2ddeed48bf9d27f47117550751" + "fidl/fuchsia.mediacodec/codec_factory.fidl": "71cb15eb15a3f824955652c938bd22d5" } \ No newline at end of file diff --git a/src/media/playback/mediaplayer/fidl/fidl_decoder.cc b/src/media/playback/mediaplayer/fidl/fidl_decoder.cc index 2a9065f635989d1a3842dd06a3c5f5b79dc217e6..2039651496fd33e59c868a0f96bf38f986732874 100644 --- a/src/media/playback/mediaplayer/fidl/fidl_decoder.cc +++ b/src/media/playback/mediaplayer/fidl/fidl_decoder.cc @@ -21,25 +21,8 @@ static const char kAacAdtsMimeType[] = "audio/aac-adts"; std::vector<uint8_t> MakeOobBytesFromAdtsHeader(const uint8_t* adts_header) { std::vector<uint8_t> asc(2); - // TODO(dustingreen): Switch from ADTS to .mp4 and fix AAC decoder to not - // require "AudioSpecificConfig()" when fed ADTS. In other words, move the - // stuff here into a shim around the AAC OMX decoder, just next to (above or - // below) the OmxCodecRunner in the codec_runner_sw_omx isolate, probably. - - // For SoftAAC2.cpp, for no particularly good reason, a CODECCONFIG buffer is - // expected, even when running in ADTS mode, despite all the relevant data - // being available from the ADTS header. The CODECCONFIG buffer has an - // AudioSpecificConfig in it. The AudioSpecificConfig has to be created based - // on corresponding fields of the ADTS header - not that requiring this of - // the codec client makes any sense whatsoever... - // - // TODO(dustingreen): maybe add a per-codec compensation layer to un-crazy the - // quirks of each codec. For example, when decoding ADTS, all the needed info - // is there in the ADTS stream directly. No reason to hassle the codec client - // for a pointless translated form of the same info. In contrast, when it's - // an mp4 file (or mkv, or whatever modern container format), the codec config - // info is relevant. But we should only force a client to provide it if - // it's really needed. + // TODO(dustingreen): Remove this function, as we don't need to be + // synthesizing oob_bytes from ADTS header data. uint8_t profile_ObjectType; // name in AAC spec in adts_fixed_header uint8_t sampling_frequency_index; // name in AAC spec in adts_fixed_header