Адаптация и анализ потока обработки Android Codec2
Адаптация и анализ потока обработки Android Codec2

1 Введение

Codec2 — это программная платформа для мультимедиа в Android. Это промежуточное программное обеспечение MediaCodec. Оно подключается к уровню MediaCodec Native и предоставляет новые стандарты API для базового кодека чипа. Другими словами, после адаптации к Codec2 вы можете перейти. MediaCodec использует возможности аппаратного кодирования и декодирования чипа для выполнения некоторых функций, связанных с мультимедиа. В этой статье сначала объясняются интерфейсы, которые необходимо реализовать для адаптации Codec2, снизу вверх, а затем анализируется процесс MediaCodec сверху вниз, чтобы проанализировать, как эти интерфейсы вызываются. В основном поймите следующие две основные линии

  1. Как входной буфер отправляется в компонент кодирования и декодирования
  2. Как сообщается о входном и выходном буферах после завершения кодирования и декодирования?

Прежде чем приступить к работе, вам потребуются следующие предварительные знания:

  1. Механизм асинхронного обмена сообщениями Android
  2. Android HIDL
  3. Основной процесс кодирования и декодирования видео

2 Адаптация

Давайте рассмотрим реализацию программного кодировщика Hevc в Android в качестве примера, чтобы проанализировать, как адаптировать интерфейс Codec2. Сначала давайте посмотрим на базовую архитектуру codec2, которая разделена на 4 слоя. Первый уровень — это sfplugin, который отвечает за это. для связи с разгонным блоком. Для стыковки страха ниже приведен HIDL, уровень аппаратной абстракции каждого компонента. Далее идет ядро, которое инкапсулирует интерфейс, который должен реализовать компонент. Наконец, здесь мы берем программный кодировщик Hevc. Ниже приведен пример конкретной библиотеки кодирования и декодирования. Программный кодер вызывает интерфейс, связанный с ihevce.

2.1 Структура каталогов

Androidвcodec2каталог вframeworks/av/media/codec2

Язык кода:shell
копировать
.
├── Android.mk
├── components     # Компоненты приспособления, такие как h264, программный кодек hevc и т.п., называются HIDL.
├── core           # ядро codec2, стыковочный компонент
├── docs
├── faultinjection
├── fuzzer
├── hidl           # реализация полуслойного слоя
├── OWNERS
├── sfplugin       # Стыковочный слой со страхом сцены
├── TEST_MAPPING
├── tests
└── vndk

Базовый уровень организует работу компонентов.,Здесь мы сначала анализируем основной слой,Основные файлы::core/include/C2Component.h,который включает в себяC2ComponentиC2ComponentInterfaceдва класса

2.2 C2Component

C2Componentопределяет интерфейс, который должен реализовать компонент,Определяется следующим образом,Здесь нужнососредоточиться Два важных интерфейса

  1. queue_nb:Может рассматриваться как отправка кадров/Потоковый интерфейс,Перед кодированием и декодированием,Отправьте необработанные данные, которые необходимо обработать,Теперь интерфейс должен быть спроектирован какнеблокирующийиз
  2. onWorkDone_nb:Когда один кадр данных будет обработан, он будетперезвонить Интерфейс
Язык кода:cpp
копировать
class C2Component {
public:
    class Listener {
    public:
        virtual void onWorkDone_nb(std::weak_ptr<C2Component> component,
                                std::list<std::unique_ptr<C2Work>> workItems) = 0;
        virtual void onTripped_nb(std::weak_ptr<C2Component> component,
                               std::vector<std::shared_ptr<C2SettingResult>> settingResult) = 0;
        virtual void onError_nb(std::weak_ptr<C2Component> component,
                             uint32_t errorCode) = 0;
        virtual ~Listener() = default;
    };

    ...
    /* Queues up work for the component. */
    virtual c2_status_t queue_nb(std::list<std::unique_ptr<C2Work>>* const items) = 0;
    /*
     * Announces a work to be queued later for the component. This reserves a slot for the queue
     * to ensure correct work ordering even if the work is queued later.
     */
    virtual c2_status_t announce_nb(const std::vector<C2WorkOutline> &items) = 0;

    enum flush_mode_t : uint32_t {
        /// flush work from this component only
        FLUSH_COMPONENT,

        /// flush work from this component and all components connected downstream from it via
        /// tunneling
        FLUSH_CHAIN = (1 << 16),
    };

    /*
     * Discards and abandons any pending work for the component, and optionally any component
     * downstream.
     */
    virtual c2_status_t flush_sm(flush_mode_t mode, std::list<std::unique_ptr<C2Work>>* const flushedWork) = 0;

    enum drain_mode_t : uint32_t {
        DRAIN_COMPONENT_WITH_EOS,
        DRAIN_COMPONENT_NO_EOS = (1 << 0),
        DRAIN_CHAIN = (1 << 16),
    };

    /*
     * Drains the component, and optionally downstream components. This is a signalling method;
     * as such it does not wait for any work completion.
     * Marks last work item as "drain-till-here", so component is notified not to wait for further
     * work before it processes work already queued. This method can also used to set the
     * end-of-stream flag after work has been queued. Client can continue to queue further work
     * immediately after this method returns.
     */
    virtual c2_status_t drain_nb(drain_mode_t mode) = 0;

    // STATE CHANGE METHODS
    // =============================================================================================
    virtual c2_status_t start() = 0;
    virtual c2_status_t stop() = 0;
    virtual c2_status_t reset() = 0;
    virtual c2_status_t release() = 0;
    virtual std::shared_ptr<C2ComponentInterface> intf() = 0;

    virtual ~C2Component() = default;
};

2.3 C2ComponentInterface

TODO

2.4 SimpleC2Component

SimpleC2Componentпредоставляет компонентизвыполнить,Различные последующие реализации должны только наследовать эту реализацию.,вважныйиз Дизайн интерфейса следующий。можно увидетьSimpleC2ComponentунаследованныйC2Component,И будет реализован механизм асинхронного обмена сообщениями AMessage и AHandle.,Соответствующие функции анализируются ниже.

Язык кода:cpp
копировать
class SimpleC2Component
        : public C2Component, public std::enable_shared_from_this<SimpleC2Component> {
public:
    explicit SimpleC2Component(
            const std::shared_ptr<C2ComponentInterface> &intf);
    virtual ~SimpleC2Component();

    // Установите соответствующий обратный вызов
    virtual c2_status_t setListener_vb(
            const std::shared_ptr<Listener> &listener, c2_blocking_t mayBlock) override;
    // Реализовать интерфейс очереди_nb
    virtual c2_status_t queue_nb(std::list<std::unique_ptr<C2Work>>* const items) override;
    ...
    // Функция, которая фактически обрабатывает кадр данных
    bool processQueue();

protected:
    ...
    // Конкретный процесс кодирования и декодирования реализуется подклассами.
    virtual void process(
            const std::unique_ptr<C2Work> &work,
            const std::shared_ptr<C2BlockPool> &pool) = 0;
    ...

    private:
    const std::shared_ptr<C2ComponentInterface> mIntf;

    class WorkHandler : public AHandler {
    public:
        ...

    protected:
        // Интерфейс асинхронной обработки сообщений
        void onMessageReceived(const sp<AMessage> &msg) override;

    private:
        ...
    };
    ...
    struct ExecState {
        ExecState() : mState(UNINITIALIZED) {}

        int mState;
        std::shared_ptr<C2Component::Listener> mListener;
    };
    // государственная машина
    Mutexed<ExecState> mExecState;

    sp<ALooper> mLooper;
    sp<WorkHandler> mHandler;

    class WorkQueue {
        ...
    }
    // Ожидающая рабочая очередь
    Mutexed<WorkQueue> mWorkQueue;
    ...

2.4.1 setListener_vb

Этот интерфейс устанавливает прослушиватель, переданный выше, в конечный автомат, а затем в подходящее время вызывает соответствующий интерфейс.

Язык кода:cpp
копировать
c2_status_t SimpleC2Component::setListener_vb(
        const std::shared_ptr<C2Component::Listener> &listener, c2_blocking_t mayBlock) {
    mHandler->setComponent(shared_from_this());

    Mutexed<ExecState>::Locked state(mExecState);
    ...
    state->mListener = listener;

    return C2_OK;
}

2.4.2 queue_nb

Эта функция реализуетC2Component.queue_nb,На самом деле это просто ставит работу в очередь,И инициировать асинхронное сообщение и вернуть,Соответствуют требованиям неблокирующего

Язык кода:cpp
копировать
c2_status_t SimpleC2Component::queue_nb(std::list<std::unique_ptr<C2Work>> * const items) {
    ...
    bool queueWasEmpty = false;
    {
        Mutexed<WorkQueue>::Locked queue(mWorkQueue);
        queueWasEmpty = queue->empty();
        while (!items->empty()) {
            queue->push_back(std::move(items->front()));
            items->pop_front();
        }
    }
    if (queueWasEmpty) {
        // Инициирование сообщения обработки данных завершит обработку кадров в mWorkQueue.
        (new AMessage(WorkHandler::kWhatProcess, mHandler))->post();
    }
    return C2_OK;
}

2.4.3 onMessageReceived

Функция Интерфейс асинхронной обработки сообщений,Эта функция в конечном итоге будет вызвана при отправке соответствующего сообщения.,здесьprocessQueue()Функция заключается в обработке одного кадра данных,Затем сообщите, есть ли в текущей очереди необработанные данные.

Язык кода:cpp
копировать
void SimpleC2Component::WorkHandler::onMessageReceived(const sp<AMessage> &msg) {
    ...

    switch (msg->what()) {
        case kWhatProcess: {
            if (mRunning) {
                // Если процессQueue() возвращает true, повторно отправьте сообщение kWhatProcess.
                if (thiz->processQueue()) {
                    (new AMessage(kWhatProcess, this))->post();
                }
            } else {
                ALOGV("Ignore process message as we're not running");
            }
            break;
        }
        ...
    }
}

2.4.4 processQueue

Эта функция завершает фактическую операцию кодирования и декодирования.,Код выглядит следующим образом,вprocessиonWorkDone_nbВсереализовано подклассами,workНачни с того, что было раньшеизmWorkQueueВынуть из очереди,позвони еще разprocessПроцесс,processэто виртуальная функция,реализовано подклассами,Обработка завершенапослепозвони еще разlistener->onWorkDone_nbСобытие завершения обработки уведомленияonWorkDone_nbтакжеэто виртуальная функция,реализовано подклассами

Язык кода:cpp
копировать
bool SimpleC2Component::processQueue() {
    ...
    bool hasQueuedWork = false;

    {
        Mutexed<WorkQueue>::Locked queue(mWorkQueue);
        if (queue->empty()) {
            return false;
        }

        ...
        work = queue->pop_front();
        hasQueuedWork = !queue->empty();
    }

    if (!mOutputBlockPool) {
        c2_status_t err = [this] {
            ...
            std::shared_ptr<C2BlockPool> blockPool;
            err = GetCodec2BlockPool(poolId, shared_from_this(), &blockPool);
            ALOGD("Using output block pool with poolID %llu => got %llu - %d",
                    (unsigned long long)poolId,
                    (unsigned long long)(
                            blockPool ? blockPool->getLocalId() : 111000111),
                    err);
            if (err == C2_OK) {
                // Какова функция mOutputBlockPool?
                mOutputBlockPool = std::make_shared<BlockingBlockPool>(blockPool);
            }
            return err;
        }();
        ...
    }

    ...
    process(work, mOutputBlockPool);
    ...
    // Проверка обработанного количества в рабочей цепочке на самом деле предназначена для того, чтобы увидеть, успешно ли обрабатывается прогресс?
    if (work->workletsProcessed != 0u) {
        queue.unlock();
        Mutexed<ExecState>::Locked state(mExecState);
        ALOGV("returning this work");
        std::shared_ptr<C2Component::Listener> listener = state->mListener;
        state.unlock();
        listener->onWorkDone_nb(shared_from_this(), vec(work));
    } else {
        ...
    }

    return hasQueuedWork;
}

2.5 C2SoftHevcEnc

Анализ нижеprocessизвыполнить,В качестве примера взят программный кодировщик Android Hevc.,Классы наследуются отSimpleC2Component,Реализовано следующим образом,Основной процесс — вынуть входной буфер из работы,Затем закодируйте кадр,Затем установите вывод на работу

Язык кода:cpp
копировать
struct C2SoftHevcEnc : public SimpleC2Component {
    ...
    void process(const std::unique_ptr<C2Work>& work,
                 const std::shared_ptr<C2BlockPool>& pool) override;
    ...
}

void C2SoftHevcEnc::process(const std::unique_ptr<C2Work>& work,
                            const std::shared_ptr<C2BlockPool>& pool) {
    ...

    std::shared_ptr<const C2GraphicView> view;
    std::shared_ptr<C2Buffer> inputBuffer = nullptr;
    ...
    if (!work->input.buffers.empty()) {
        // Получите вклад в работу buffer
        inputBuffer = work->input.buffers[0];
        // Привязать буфер для просмотра
        view = std::make_shared<const C2GraphicView>(
            inputBuffer->data().graphicBlocks().front().map().get());
        ...
    }

    ...
    ihevce_inp_buf_t s_encode_ip{};
    ihevce_out_buf_t s_encode_op{};
    ...
    // Преобразовать представление в s_encode_ip
    status = setEncodeArgs(&s_encode_ip, view.get(), workIndex);

    ...
    memset(&s_encode_op, 0, sizeof(s_encode_op));
    ...
    if (inputBuffer) {
        // Возьмите s_encode_ip в качестве входных данных для завершения одного кадра кодирования hevc.
        err = ihevce_encode(mCodecCtx, &s_encode_ip, &s_encode_op);
        ...
    }

    ...
    if (s_encode_op.i4_bytes_generated) {
        // s_encode_op имеет данные, настройте его в работу
        finishWork(s_encode_op.u8_pts, work, pool, &s_encode_op);
    }
}

Анализ нижеfinishWork,Видно, что выходной буфер мягкого кодирования в конечном итоге копируется в C2Buffer.,Наконец-то вставилwork->worklets.front()->output.buffersочередь

Язык кода:cpp
копировать
void C2SoftHevcEnc::finishWork(uint64_t index,
                               const std::unique_ptr<C2Work>& work,
                               const std::shared_ptr<C2BlockPool>& pool,
                               ihevce_out_buf_t* ps_encode_op) {
    std::shared_ptr<C2LinearBlock> block;
    ...
    // Получить линейный блок
    c2_status_t status =
        pool->fetchLinearBlock(ps_encode_op->i4_bytes_generated, usage, &block);
    ...
    // Блок карты для просмотра
    C2WriteView wView = block->map().get();
    ...
    // Скопируйте содержимое выходного буфера в блок
    memcpy(wView.data(), ps_encode_op->pu1_output_buf,
           ps_encode_op->i4_bytes_generated);
    // Создайте C2Buffer из блока
    std::shared_ptr<C2Buffer> buffer =
        createLinearBuffer(block, 0, ps_encode_op->i4_bytes_generated);

    ...
    // Включите буфер в работу
    auto fillWork = [buffer](const std::unique_ptr<C2Work>& work) {
        work->worklets.front()->output.flags = (C2FrameData::flags_t)0;
        work->worklets.front()->output.buffers.clear();
        work->worklets.front()->output.buffers.push_back(buffer);
        work->worklets.front()->output.ordinal = work->input.ordinal;
        work->workletsProcessed = 1u;
    };

    if (work && c2_cntr64_t(index) == work->input.ordinal.frameIndex) {
        fillWork(work);
        ...
    } else {
       finish(index, fillWork);
    }
}

3 HIDL

3.1 Концепция

C2ComponentизначальствослойдаHIDLслой,Можно понимать как слой HAL Android.,Интерфейс, унаследованный заголовочным файлом этого уровня, динамически генерируется с помощью языка под названием HIDL (язык определения аппаратного интерфейса).,Вывод в выходной каталог,Например, этовIComponentЗаголовочный файл находится по адресу:

out/soong/.intermediates/hardware/interfaces/media/c2/1.0/android.hardware.media.c2@1.0_genc++_headers/gen/android/hardware/media/c2/1.0/IComponent.h

Его определение следующее

Язык кода:cpp
копировать
struct IComponent : public ::android::hidl::base::V1_0::IBase {
    typedef ::android::hardware::details::i_tag _hidl_tag;
    static const char* descriptor;

    virtual bool isRemote() const override { return false; }
    virtual ::android::hardware::Return<::android::hardware::media::c2::V1_0::Status> queue(const ::android::hardware::media::c2::V1_0::WorkBundle& workBundle) = 0;

    using flush_cb = std::function<void(::android::hardware::media::c2::V1_0::Status status, const ::android::hardware::media::c2::V1_0::WorkBundle& flushedWorkBundle)>;
    virtual ::android::hardware::Return<void> flush(flush_cb _hidl_cb) = 0;
    ...
}

переписыватьсяизHIDLФайлhardware/interfaces/media/c2/1.0/IComponent.hal

Язык кода:shell
копировать
interface IComponent {
queue(WorkBundle workBundle) generates (Status status);
flush(
    ) generates (
        Status status,
        WorkBundle flushedWorkBundle
    );
...
}

3.2 Component

ComponentУнаследовано отIComponent.h,Он также будет реализовывать интерфейс,проходитьComponent就可以вызовначальство面讲到изC2Component,Его определение следующее,Давайте сосредоточимся на еговqueueиListener

Язык кода:cpp
копировать
struct Component : public IComponent,
                   public std::enable_shared_from_this<Component> {
    ...
    virtual Return<Status> queue(const WorkBundle& workBundle) override;
    ...
protected:
    ...
    struct Listener;
    ...
}

3.2.1 queue

queueиз Реализовано следующим образом,здесьизmComponentНа самом деле этоC2Component,Как это реализовать здесь пока не показано.,Анализ позже,Возьмите мягкое кодирование в качестве примера.,поэтомуздесьпоследний звонокизqueue_nbна самом делевызовиздаSimpleC2Component.queue_nb

Язык кода:cpp
копировать
Return<Status> Component::queue(const WorkBundle& workBundle) {
    std::list<std::unique_ptr<C2Work>> c2works;

    if (!objcpy(&c2works, workBundle)) {
        return Status::CORRUPTED;
    }

    // Register input buffers.
    for (const std::unique_ptr<C2Work>& work : c2works) {
        if (work) {
            InputBufferManager::
                    registerFrameData(mListener, work->input);
        }
    }

    /* Вызовите C2Component.queue_nb, если компонент наследуется от SimpleC2Component,
     * Затем вызовите SimpleC2Component.queue_nb.
     * Обратите внимание, что это неблокирующий
     */
    return static_cast<Status>(mComponent->queue_nb(&c2works));
}

3.2.1 Listener

Посмотрите еще разListenerизопределение,здесьфактическое наследованиеиздаC2Component::Listener,и чтобыonWorkDone_nbРеализовано,потому чтоonWorkDone_nbдаперезвонитьфункция,поэтому由вызов者выполнитьтакжеда Как и ожидалосьиз。здесь На самом деледавызов了другойперезвонитьlistener->onWorkDone

Язык кода:cpp
копировать
struct Component::Listener : public C2Component::Listener {
    Listener(const sp<Component>& component) :
        mComponent(component),
        mListener(component->mListener) {
    }

    ...
    virtual void onWorkDone_nb(
            std::weak_ptr<C2Component> /* c2component */,
            std::list<std::unique_ptr<C2Work>> c2workItems) override {
        ...

        sp<IComponentListener> listener = mListener.promote();
        if (listener) {
            WorkBundle workBundle;

            // Копировать в работуBundle
            sp<Component> strongComponent = mComponent.promote();
            beginTransferBufferQueueBlocks(c2workItems, true);
            if (!objcpy(&workBundle, c2workItems, strongComponent ?
                    &strongComponent->mBufferPoolSender : nullptr)) {
                ...
            }
            // перезвонить
            Return<void> transStatus = listener->onWorkDone(workBundle);
            ...
            endTransferBufferQueueBlocks(c2workItems, true, true);
        }
    }

Вышеупомянутый прослушиватель - это просто определение,Это также зависит от того, где объявлен слушатель.,И когда он был зарегистрирован. Сначала первый вопрос,ListenerзаявлениедасуществоватьSimpleC2Component::ExecState.mListener,если бы толькоунаследованныйSimpleC2ComponentВнутри есть этот член,второй вопрос,зарегистрироватьсядасуществоватьComponent::initListenerфункциясередина,Определяется следующим образом,такой же Возьмите мягкое кодирование в качестве примера.,здесьmComponent->setListener_vbвызовдействительныйдаSimpleC2Component.setListener_vb

Язык кода:cpp
копировать
void Component::initListener(const sp<Component>& self) {
    std::shared_ptr<C2Component::Listener> c2listener =
            std::make_shared<Listener>(self);
    /* Вызовите C2Component.setListener_vb, если компонент наследуется от SimpleC2Component,
     * Затем вызовите SimpleC2Component.setListener_vb.
     */
    c2_status_t res = mComponent->setListener_vb(c2listener, C2_DONT_BLOCK);
    ...
}

Для HIDL существует длительный процесс вызова. Давайте пока остановимся на этом. Позже мы проанализируем его сверху вниз через MediaCodec, чтобы увидеть, как вызывать HIDL.

4 MediaCodec

MediaCodec — это модуль для кодирования и декодирования мультимедиа на уровне приложения Android. Он разделен на уровень Java и уровень CPP. Здесь мы начинаем только с уровня cpp.

4.1 Процесс вызова

Сначала давайте посмотрим, как используется MediaCodec. Поскольку MediaCodec также основан на механизме AMessage, сначала необходимо создать ALooper, а затем передать его в MediaCodec. Ниже приведен пример создания кодировщика Hevc. Псевдокод выглядит следующим образом.

Язык кода:cpp
копировать
sp<android::ALooper> looper = new android::ALooper;    // Создать ALooper
looper->setName("TestLooper");
looper->start();

// Создайте кодировщик и автоматически найдите подходящие компоненты кодера
mMediaCodec = MediaCodec::CreateByType(looper, "video/hevc", true);
mMediaCodec->start();

for (uint32_t i = 0; i < 100; i++) {
    // Получить индекс входного буфера из MediaCodec
    mMediaCodec->dequeueInputBuffer(&inputBufIdx, sTimeOut);
    // Получить входной буфер
    sp<MediaCodecBuffer> inputBuf;
    mMediaCodec->getInputBuffer(inputBufIdx, &inputBuf);
    // Записать YUV во входной буфер
    int readSize = writeYUVToBuffer(inputBuf->data(), inputBuf->size());
    uint32_t flags = 0;
    if (readSize == 0) {
        flags  = BUFFER_FLAG_END_OF_STREAM;
    }
    // Отправить входной буфер обратно в MediaCodec
    mMediaCodec->queueInputBuffer(inputBufIdx, 0, readSize, getCurTimeUs(), flags);

    // Получить индекс выходного буфера из MediaCodec
    size_t outputBufIdx, outputOffset, outputSize;
    mMediaCodec->dequeueOutputBuffer(&outputBufIdx, &outputOffset, &outputSize,
                            &outputPts, &outputFlags, sTimeOut);
    // Получить выходной буфер
    sp<MediaCodecBuffer> outputBuf;
    mMediaCodec->getOutputBuffer(outputBufIdx, &outputBuf);
    // Записать поток кода в файл
    writeStreamToFile(outputBuf);
    // КMediaCodecОсвободить выходной буфер
    mMediaCodec->releaseOutputBuffer(outputBufIdx)
}

4.2 Процесс отправки входного буфера

4.2.1 queueInputBuffer

Сначала проанализируйтеqueueInputBuffer,Посмотрите, как YUV отправляется на определенные компоненты кодировщика.,Вы можете видеть, что на самом деле здесь отправляется только асинхронное сообщение.,ВоляindexОтправьте это,а потом позвониPostAndAwaitResponseблокировка ожиданияответ на сообщение

Язык кода:cpp
копировать
status_t MediaCodec::queueInputBuffer(
        size_t index,
        size_t offset,
        size_t size,
        int64_t presentationTimeUs,
        uint32_t flags,
        AString *errorDetailMsg) {
    if (errorDetailMsg != NULL) {
        errorDetailMsg->clear();
    }

    sp<AMessage> msg = new AMessage(kWhatQueueInputBuffer, this);
    msg->setSize("index", index);
    msg->setSize("offset", offset);
    msg->setSize("size", size);
    msg->setInt64("timeUs", presentationTimeUs);
    msg->setInt32("flags", flags);
    msg->setPointer("errorDetailMsg", errorDetailMsg);

    sp<AMessage> response;
    // Обратите внимание, что это блокирует ожидание
    return PostAndAwaitResponse(msg, &response);
}

4.2.2 Обработка сообщений QueueInputBuffer

Посмотрите еще раз на функцию обработки сообщений MediaCodec.,потому чтоqueueInputBufferдаблокировка ожиданияиз,поэтомуздесь要вызовPostReplyWithErrorпосле,queueInputBufferТолько что вернулся,здесьпозжедавызов了onQueueInputBufferфункция

Язык кода:cpp
копировать
void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
    switch (msg->what()) {
    ...
    case kWhatQueueInputBuffer:
        {
            sp<AReplyToken> replyID;
            CHECK(msg->senderAwaitsResponse(&replyID));

            ...

            status_t err = UNKNOWN_ERROR;
            if (!mLeftover.empty()) {
                mLeftover.push_back(msg);
                size_t index;
                msg->findSize("index", &index);
                err = handleLeftover(index);
            } else {
                err = onQueueInputBuffer(msg);
            }

            // ответ на сообщение
            PostReplyWithError(replyID, err);
            break;
        }
    ...
    }
}

Анализ нижеonQueueInputBuffer,можно увидетьздесьдействительныйдапроходитьindexПолучатьMediaCodecBuffer,и доставленоmBufferChannel->queueInputBufferсередина,Обратите внимание на выполнение самой внешней части здесь.слойизqueueInputBufferвсе еще ждуответ на сообщение,Поэтому он заблокирован до сих пор

Язык кода:cpp
копировать
struct MediaCodec : public AHandler {
private:
    ...
    // Два порта, вход 0, выход 1
    std::vector<BufferInfo> mPortBuffers[2];
    ...
    std::shared_ptr<BufferChannelBase> mBufferChannel;
}

status_t MediaCodec::onQueueInputBuffer(const sp<AMessage> &msg) {
    size_t index;
    ...

    CHECK(msg->findSize("index", &index));
    ...
    BufferInfo *info = &mPortBuffers[kPortIndexInput][index];
    sp<MediaCodecBuffer> buffer = info->mData;
    ...
    if (hasCryptoOrDescrambler() && !c2Buffer && !memory) {
        // безопасный процесс кодирования
        ...
    } else {
        // Процесс кодирования, не связанный с безопасностью
        mBufferChannel->queueInputBuffer(buffer);
    }
    ...
}

4.3 Процесс получения входного буфера

4.3.1 dequeueInputBuffer

Посмотрите нижеdequeueInputBufferфункция,Эта функция предназначена для получения свободного входного буфера,Базовый компонент должен уведомить о том, что он здесь простаивает.,Следовательно, здесь необходимо проанализировать процесс восходящего уведомления. Вы можете видеть, что эта функция по-прежнему инициирует асинхронное сообщение.,Затем заблокируйте и дождитесь ответа

Язык кода:cpp
копировать
status_t MediaCodec::dequeueInputBuffer(size_t *index, int64_t timeoutUs) {
    sp<AMessage> msg = new AMessage(kWhatDequeueInputBuffer, this);
    msg->setInt64("timeoutUs", timeoutUs);

    sp<AMessage> response;
    status_t err;
    if ((err = PostAndAwaitResponse(msg, &response)) != OK) {
        return err;
    }

    CHECK(response->findSize("index", index));

    return OK;
}

4.3.2 Обработка сообщений kWhatDequeueInputBuffer

снова вернутьсяonMessageReceivedфункция,в конечном итоге будет вызванdequeuePortBufferфункция,можно увидетьMediaCodecсерединаесть одинmAvailPortBuffersсвязанный список,Сохраняет индекс доступного в данный момент буфера.,При удалении из очереди вам нужно удалить только первый индекс из этого связанного списка.

Язык кода:cpp
копировать
void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
    switch (msg->what()) {
    ...
    case kWhatDequeueInputBuffer:
    {
        sp<AReplyToken> replyID;
        CHECK(msg->senderAwaitsResponse(&replyID));

        handleDequeueInputBuffer(replyID, true /* new request */);
        ...
        break;
    }
    ...
    }
}

bool MediaCodec::handleDequeueInputBuffer(const sp<AReplyToken> &replyID, bool newRequest) {
    ...

    ssize_t index = dequeuePortBuffer(kPortIndexInput);
    ...

    sp<AMessage> response = new AMessage;
    response->setSize("index", index);
    // ответное сообщение
    response->postReply(replyID);

    return true;
}

struct MediaCodec : public AHandler {
private:
    ...
    // Хранить доступный индекс
    List<size_t> mAvailPortBuffers[2];
    ...
}

ssize_t MediaCodec::dequeuePortBuffer(int32_t portIndex) {
    ...
    List<size_t> *availBuffers = &mAvailPortBuffers[portIndex];
    size_t index = *availBuffers->begin();
    ...
    availBuffers->erase(availBuffers->begin());

    return index;
}

4.3.3 updateBuffers

Продолжить анализavailBuffersдакогда обновлятьиз,ПроверятьupdateBuffersфункция,Было обнаружено, что об индексе сообщалось посредством асинхронных сообщений.,updateBuffersввод можно обновитьочередьивыходочередь,Мы смотрим только на входную очередь,Обнаружитьдасуществовать收到kWhatFillThisBufferОбновление новостейиз

Язык кода:cpp
копировать
size_t MediaCodec::updateBuffers(
        int32_t portIndex, const sp<AMessage> &msg) {
    CHECK(portIndex == kPortIndexInput || portIndex == kPortIndexOutput);
    size_t index;
    // Получить индекс из сообщения
    CHECK(msg->findSize("index", &index));
    sp<RefBase> obj;
    CHECK(msg->findObject("buffer", &obj));
    // Получить объект буфера
    sp<MediaCodecBuffer> buffer = static_cast<MediaCodecBuffer *>(obj.get());

    {
        Mutex::Autolock al(mBufferLock);
        // Если индекс больше размера очереди, расширьте очередь.
        if (mPortBuffers[portIndex].size() <= index) {
            mPortBuffers[portIndex].resize(align(index + 1, kNumBuffersAlign));
        }
        // Поместите буфер в очередь с индексом index
        mPortBuffers[portIndex][index].mData = buffer;
    }
    // Поместить индекс в доступный связанный список
    mAvailPortBuffers[portIndex].push_back(index);

    return index;
}

void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
    switch (msg->what()) {
    ...
    case kWhatFillThisBuffer:
    {
        updateBuffers(kPortIndexInput, msg);
        ...
        break;
    }
    ...
}

4.3.4 BufferCallback

Пока что вы можете найти,Доступность входных и выходных буферов в MediaCodec уведомляется асинхронными сообщениями.,И асинхронные сообщениядаконецслойизкомпонентыпроходитьперезвонитьMediaCodecиз Отправка интерфейсаиз,Эта часть кода находится вBufferCallbackсередина,на самом делеBufferCallbackдаунаследованныйCodecBase::BufferCallbackдобрый,и реализовал интерфейс,можно увидетьвходитьивыходbufferВседасуществоватьздесьперезвонитьиз

Язык кода:cpp
копировать
class BufferCallback : public CodecBase::BufferCallback {
public:
    explicit BufferCallback(const sp<AMessage> &notify);
    virtual ~BufferCallback() = default;

    virtual void onInputBufferAvailable(
            size_t index, const sp<MediaCodecBuffer> &buffer) override;
    virtual void onOutputBufferAvailable(
            size_t index, const sp<MediaCodecBuffer> &buffer) override;
private:
    const sp<AMessage> mNotify;
};

BufferCallback::BufferCallback(const sp<AMessage> &notify)
    : mNotify(notify) {}

void BufferCallback::onInputBufferAvailable(
        size_t index, const sp<MediaCodecBuffer> &buffer) {
    sp<AMessage> notify(mNotify->dup());
    notify->setInt32("what", kWhatFillThisBuffer);
    notify->setSize("index", index);
    notify->setObject("buffer", buffer);
    notify->post();
}

CodecBaseна самом делесновадаcodec2винтерфейс,Принадлежит модулю sfplugin,Из-за нехватки места мы пока не будем здесь анализировать дальше.

4.4 Процесс получения выходного буфера

4.4.1 dequeueOutputBuffer

Получение выходного буфера и получение входного буфера — это процесс. Они оба отправляют сообщение и ждут ответа. Здесь выходной буфер — это данные, обрабатываемые базовым компонентом, поэтому базовый компонент также необходимо уведомить.

Язык кода:cpp
копировать
status_t MediaCodec::dequeueOutputBuffer(
        size_t *index,
        size_t *offset,
        size_t *size,
        int64_t *presentationTimeUs,
        uint32_t *flags,
        int64_t timeoutUs) {
    sp<AMessage> msg = new AMessage(kWhatDequeueOutputBuffer, this);
    msg->setInt64("timeoutUs", timeoutUs);

    sp<AMessage> response;
    status_t err;
    if ((err = PostAndAwaitResponse(msg, &response)) != OK) {
        return err;
    }

    CHECK(response->findSize("index", index));
    CHECK(response->findSize("offset", offset));
    CHECK(response->findSize("size", size));
    CHECK(response->findInt64("timeUs", presentationTimeUs));
    CHECK(response->findInt32("flags", (int32_t *)flags));

    return OK;
}

4.4.2 Обработка сообщений kWhatDequeueOutputBuffer

Получить выходной буфери Получить входной буфертакой же,ВседавызовизdequeuePortBufferфункция,Просто параметры передаваемые разные,также就даздесьвсе ещедаотmAvailPortBuffersочередьсередина获取以及Обработка завершенаизbuffer index

Язык кода:cpp
копировать
void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
    switch (msg->what()) {
    ...
    case kWhatDequeueOutputBuffer:
    {
        sp<AReplyToken> replyID;
        CHECK(msg->senderAwaitsResponse(&replyID));
        ...
        handleDequeueOutputBuffer(replyID, true /* new request */))
        ...
        break;
    }
    ...
    }
}

bool MediaCodec::handleDequeueOutputBuffer(const sp<AReplyToken> &replyID, bool newRequest) {
    ...
    } else {
        sp<AMessage> response = new AMessage;
        ...
        dequeuePortBuffer(kPortIndexOutput);
        ...
        response->postReply(replyID);
    }
}

4.4.3 Обновления mAvailPortBuffers

Получить входной буфервсе ещедавызовupdateBuffersруководитьmAvailPortBuffersизвозобновлять,толькоздесьдаотkWhatDrainThisBufferвозобновлятьиз,и отправь это сообщениеиз地方такжедадругойonOutputBufferAvailableперезвонить

Язык кода:cpp
копировать
void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
    switch (msg->what()) {
    ...
    case kWhatDrainThisBuffer:
    {
        ...
        updateBuffers(kPortIndexInput, msg);
        ...
        break;
    }
    ...
}

void BufferCallback::onOutputBufferAvailable(
        size_t index, const sp<MediaCodecBuffer> &buffer) {
    sp<AMessage> notify(mNotify->dup());
    notify->setInt32("what", kWhatDrainThisBuffer);
    notify->setSize("index", index);
    notify->setObject("buffer", buffer);
    notify->post();
}

4.5 Освободить выходной буфер

4.5.1 releaseOutputBuffer

Освободить выходной Процесс относительно прост,Также отправьте асинхронное сообщение, а затем заблокируйте и подождите.,существовать消息处理середина ВоляmPortBuffersочередьпереписыватьсяизbufferПрозрачный,и наконец позвониmBufferChannel->discardBuffer通知конецслойкомпоненты

Язык кода:cpp
копировать
status_t MediaCodec::releaseOutputBuffer(size_t index) {
    sp<AMessage> msg = new AMessage(kWhatReleaseOutputBuffer, this);
    msg->setSize("index", index);

    sp<AMessage> response;
    return PostAndAwaitResponse(msg, &response);
}

void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
    switch (msg->what()) {
    ...
    case kWhatDrainThisBuffer:
    {   
        ...
        onReleaseOutputBuffer(msg);
        PostReplyWithError(replyID, err);
        break;
    }
    ...
}

status_t MediaCodec::onReleaseOutputBuffer(const sp<AMessage> &msg) {
    size_t index;
    CHECK(msg->findSize("index", &index));

    ...
    BufferInfo *info = &mPortBuffers[kPortIndexOutput][index];
    ...
    sp<MediaCodecBuffer> buffer;
    {
        Mutex::Autolock al(mBufferLock);
        info->mOwnedByClient = false;
        buffer = info->mData;
        info->mData.clear();
    }

    if (render && buffer->size() != 0) {
        ...
    } else {
        mBufferChannel->discardBuffer(buffer);
    }
}

5 Резюме

Подводя итог вышеописанному процессу, помимо HIDL и sfplugin, ниже представлен общий анализ процесса использования MediaCodec для вызова codec2 для кодирования и декодирования. Сначала рассмотрим общий процесс работы и взаимосвязь реализации следующим образом.

boy illustration
Учебное пособие по Jetpack Compose для начинающих, базовые элементы управления и макет
boy illustration
Код js веб-страницы, фон частицы, код спецэффектов
boy illustration
【новый! Суперподробное】Полное руководство по свойствам компонентов Figma.
boy illustration
🎉Обязательно к прочтению новичкам: полное руководство по написанию мини-программ WeChat с использованием программного обеспечения Cursor.
boy illustration
[Забавный проект Docker] VoceChat — еще одно приложение для мгновенного чата (IM)! Может быть встроен в любую веб-страницу!
boy illustration
Как реализовать переход по странице в HTML (html переходит на указанную страницу)
boy illustration
Как решить проблему зависания и низкой скорости при установке зависимостей с помощью npm. Существуют ли доступные источники npm, которые могут решить эту проблему?
boy illustration
Серия From Zero to Fun: Uni-App WeChat Payment Practice WeChat авторизует вход в систему и украшает страницу заказа, создает интерфейс заказа и инициирует запрос заказа
boy illustration
Серия uni-app: uni.navigateЧтобы передать скачок значения
boy illustration
Апплет WeChat настраивает верхнюю панель навигации и адаптируется к различным моделям.
boy illustration
JS-время конвертации
boy illustration
Обеспечьте бесперебойную работу ChromeDriver 125: советы по решению проблемы chromedriver.exe не найдены
boy illustration
Поле комментария, щелчок мышью, специальные эффекты, js-код
boy illustration
Объект массива перемещения объекта JS
boy illustration
Как открыть разрешение на позиционирование апплета WeChat_Как использовать WeChat для определения местонахождения друзей
boy illustration
Я даю вам два набора из 18 простых в использовании фонов холста Power BI, так что вам больше не придется возиться с цветами!
boy illustration
Получить текущее время в js_Как динамически отображать дату и время в js
boy illustration
Вам необходимо изучить сочетания клавиш vsCode для форматирования и организации кода, чтобы вам больше не приходилось настраивать формат вручную.
boy illustration
У ChatGPT большое обновление. Всего за 45 минут пресс-конференция показывает, что OpenAI сделал еще один шаг вперед.
boy illustration
Copilot облачной разработки — упрощение разработки
boy illustration
Микросборка xChatGPT с низким кодом, создание апплета чат-бота с искусственным интеллектом за пять шагов
boy illustration
CUDA Out of Memory: идеальное решение проблемы нехватки памяти CUDA
boy illustration
Анализ кластеризации отдельных ячеек, который должен освоить каждый&MarkerгенетическийВизуализация
boy illustration
vLLM: мощный инструмент для ускорения вывода ИИ
boy illustration
CodeGeeX: мощный инструмент генерации кода искусственного интеллекта, который можно использовать бесплатно в дополнение к второму пилоту.
boy illustration
Машинное обучение Реальный бой LightGBM + настройка параметров случайного поиска: точность 96,67%
boy illustration
Бесшовная интеграция, мгновенный интеллект [1]: платформа больших моделей Dify-LLM, интеграция без кодирования и встраивание в сторонние системы, более 42 тысяч звезд, чтобы стать свидетелями эксклюзивных интеллектуальных решений.
boy illustration
LM Studio для создания локальных больших моделей
boy illustration
Как определить количество слоев и нейронов скрытых слоев нейронной сети?
boy illustration
[Отслеживание целей] Подробное объяснение ByteTrack и детали кода