| // Copyright 2024 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifdef UNSAFE_BUFFERS_BUILD |
| // TODO(crbug.com/390223051): Remove C-library calls to fix the errors. |
| #pragma allow_unsafe_libc_calls |
| #endif |
| |
| #ifndef MEDIA_BASE_CODEC_WORKER_IMPL_H_ |
| #define MEDIA_BASE_CODEC_WORKER_IMPL_H_ |
| |
| #include <cstring> |
| |
| #include "base/functional/bind.h" |
| #include "base/synchronization/lock.h" |
| #include "base/threading/thread.h" |
| #include "base/threading/thread_restrictions.h" |
| |
| // This file contains guts of thread wrappers for libvpx and libaom to help |
| // avoid code duplication. |
| namespace media { |
| |
| // Template class for returning codec worker implementations |
| template <class WorkerInterface, |
| class WorkerImpl, |
| class Worker, |
| class WorkerStatus, |
| WorkerStatus StatusNotOk, |
| WorkerStatus StatusOk, |
| WorkerStatus StatusWork> |
| class CodecWorkerImpl { |
| public: |
| // Returns the codec worker interface. |
| static constexpr WorkerInterface GetCodecWorkerInterface() { |
| return WorkerInterface{.init = Init, |
| .reset = Reset, |
| .sync = Sync, |
| .launch = Launch, |
| .execute = Execute, |
| .end = End}; |
| } |
| |
| private: |
| CodecWorkerImpl() |
| : thread_("CodecWorker"), |
| event_(base::WaitableEvent::ResetPolicy::AUTOMATIC) { |
| thread_.Start(); |
| } |
| |
| static CodecWorkerImpl* GetImpl(Worker* const worker) { |
| return reinterpret_cast<CodecWorkerImpl*>(worker->impl_); |
| } |
| |
| static void Init(Worker* const worker) { |
| memset(worker, 0, sizeof(*worker)); |
| worker->status_ = StatusNotOk; |
| worker->impl_ = nullptr; |
| } |
| |
| static void Execute(Worker* const worker) { |
| if (worker->hook) { |
| worker->had_error |= !worker->hook(worker->data1, worker->data2); |
| } |
| } |
| |
| void ChangeStateImpl(Worker* const worker, WorkerStatus new_status) { |
| // Await work complete if necessary before setting the new state. |
| bool deplete_work = false; |
| { |
| base::AutoLock lock(mutex_); |
| deplete_work = worker->status_ == StatusWork; |
| if (!deplete_work) { |
| worker->status_ = new_status; |
| } |
| } |
| if (deplete_work) { |
| thread_.task_runner()->PostTask( |
| FROM_HERE, |
| base::BindOnce([](base::WaitableEvent* event) { event->Signal(); }, |
| // Unretained is safe because End waits until |
| // work is complete (see `deplete_work` above). |
| base::Unretained(&event_))); |
| // Sequences calling into libvpx & libaom with a threading configuration |
| // need to be annotated with base::MayBlock. This is because the threading |
| // interface requires Launch/Sync/End to deplete scheduled work before |
| // completion. |
| base::ScopedAllowBaseSyncPrimitives allow_wait; |
| event_.Wait(); |
| base::AutoLock lock(mutex_); |
| worker->status_ = new_status; |
| } |
| // Schedule new work. |
| if (new_status == StatusWork) { |
| thread_.task_runner()->PostTask( |
| FROM_HERE, |
| base::BindOnce(&CodecWorkerImpl::ExecuteOnTaskRunner, |
| // Unretained is safe because End waits until |
| // work is complete (see `deplete_work` above). |
| base::Unretained(this), base::Unretained(worker))); |
| } |
| } |
| |
| void ExecuteOnTaskRunner(Worker* worker) { |
| base::AutoLock lock(mutex_); |
| CHECK_EQ(worker->status_, StatusWork); |
| Execute(worker); |
| worker->status_ = StatusOk; |
| } |
| |
| static void ChangeState(Worker* const worker, WorkerStatus new_status) { |
| // No-op when attempting to change state on a thread that didn't come up |
| // with VpxWorkerReset. |
| if (!worker->impl_) { |
| return; |
| } |
| CodecWorkerImpl* impl = GetImpl(worker); |
| impl->ChangeStateImpl(worker, new_status); |
| } |
| |
| static int Sync(Worker* const worker) { |
| ChangeState(worker, StatusOk); |
| return !worker->had_error; |
| } |
| |
| static int Reset(Worker* const worker) { |
| if (worker->status_ < StatusOk) { |
| worker->had_error = false; |
| worker->impl_ = reinterpret_cast<WorkerImpl*>(new CodecWorkerImpl()); |
| worker->status_ = StatusOk; |
| return 1; |
| } |
| int ok = Sync(worker); |
| worker->had_error = false; |
| CHECK(!ok || worker->status_ == StatusOk); |
| return ok; |
| } |
| |
| static void Launch(Worker* const worker) { ChangeState(worker, StatusWork); } |
| |
| static void End(Worker* const worker) { |
| if (worker->impl_) { |
| ChangeState(worker, StatusNotOk); |
| CodecWorkerImpl* impl = GetImpl(worker); |
| base::ScopedAllowBaseSyncPrimitives allow_wait; |
| delete impl; |
| worker->impl_ = nullptr; |
| } |
| } |
| |
| // Protects `Worker::status_`. The other attributes in `Worker` are expected |
| // to be updated in isolation. |
| base::Lock mutex_; |
| // TODO(crbug.com/41486982): consider using sequenced task runner instead of |
| // dedicated threads. |
| base::Thread thread_; |
| // Avoids creating a WaitableEvent on stack when work needs to be depleted. |
| // Sequences calling into libvpx & libaom with a threading configuration need |
| // to be annotated with base::MayBlock. This is because the threading |
| // interface requires Launch/Sync/End to deplete scheduled work before |
| // completion. |
| base::WaitableEvent event_; |
| }; |
| |
| } // namespace media |
| |
| #endif // MEDIA_BASE_CODEC_WORKER_IMPL_H_ |