Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c0c259c8 authored by Automerger Merge Worker's avatar Automerger Merge Worker
Browse files

Merge "Benchmark: Add test for Codec2 encoders" am: c1a82b91 am: 7685d32a

Change-Id: I6d646d77c08f071f6b5b92e07c27b56436e2f3b2
parents bca2ff7a 7685d32a
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -145,3 +145,12 @@ Setup steps are same as [extractor](#extractor).
```
adb shell /data/local/tmp/C2DecoderTest -P /data/local/tmp/MediaBenchmark/res/
```
## C2 Encoder

The test encodes input stream and benchmarks the codec2 encoders available in device.

Setup steps are same as [extractor](#extractor).

```
adb shell /data/local/tmp/C2EncoderTest -P /data/local/tmp/MediaBenchmark/res/
```
+1 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@
#include <mutex>
#include <queue>
#include <thread>
#include <iostream>

#include <media/NdkMediaCodec.h>
#include <media/NdkMediaError.h>
+20 −0
Original line number Diff line number Diff line
@@ -31,3 +31,23 @@ cc_library_static {

    ldflags: ["-Wl,-Bsymbolic"]
}

cc_library_static {
    name: "libmediabenchmark_codec2_encoder",
    defaults: [
        "libmediabenchmark_common-defaults",
        "libmediabenchmark_codec2_common-defaults",
    ],

    srcs: ["C2Encoder.cpp"],

    static_libs: [
        "libmediabenchmark_codec2_common",
        "libmediabenchmark_extractor",
        "libmediabenchmark_decoder",
    ],

    export_include_dirs: ["."],

    ldflags: ["-Wl,-Bsymbolic"]
}
+264 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

//#define LOG_NDEBUG 0
#define LOG_TAG "C2Encoder"

#include "C2Encoder.h"

int32_t C2Encoder::createCodec2Component(string compName, AMediaFormat *format) {
    ALOGV("In %s", __func__);
    mListener.reset(new CodecListener(
            [this](std::list<std::unique_ptr<C2Work>> &workItems) { handleWorkDone(workItems); }));
    if (!mListener) return -1;

    const char *mime = nullptr;
    AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime);
    if (!mime) {
        ALOGE("Error in AMediaFormat_getString");
        return -1;
    }
    // Configure the plugin with Input properties
    std::vector<C2Param *> configParam;
    if (!strncmp(mime, "audio/", 6)) {
        mIsAudioEncoder = true;
        int32_t numChannels;
        if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE, &mSampleRate)) {
            ALOGE("AMEDIAFORMAT_KEY_SAMPLE_RATE not set");
            return -1;
        }
        if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_CHANNEL_COUNT, &numChannels)) {
            ALOGE("AMEDIAFORMAT_KEY_CHANNEL_COUNT not set");
            return -1;
        }
        C2StreamSampleRateInfo::input sampleRateInfo(0u, mSampleRate);
        C2StreamChannelCountInfo::input channelCountInfo(0u, numChannels);
        configParam.push_back(&sampleRateInfo);
        configParam.push_back(&channelCountInfo);
    } else {
        mIsAudioEncoder = false;
        if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_WIDTH, &mWidth)) {
            ALOGE("AMEDIAFORMAT_KEY_WIDTH not set");
            return -1;
        }
        if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_HEIGHT, &mHeight)) {
            ALOGE("AMEDIAFORMAT_KEY_HEIGHT not set");
            return -1;
        }
        C2StreamPictureSizeInfo::input inputSize(0u, mWidth, mHeight);
        configParam.push_back(&inputSize);

        if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_FRAME_RATE, &mFrameRate) ||
            (mFrameRate <= 0)) {
            mFrameRate = KDefaultFrameRate;
        }
    }

    int64_t sTime = mStats->getCurTime();
    mComponent = mClient->CreateComponentByName(compName.c_str(), mListener, &mClient);
    if (mComponent == nullptr) {
        ALOGE("Create component failed for %s", compName.c_str());
        return -1;
    }
    std::vector<std::unique_ptr<C2SettingResult>> failures;
    int32_t status = mComponent->config(configParam, C2_DONT_BLOCK, &failures);
    if (failures.size() != 0) {
        ALOGE("Invalid Configuration");
        return -1;
    }

    status |= mComponent->start();
    int64_t eTime = mStats->getCurTime();
    int64_t timeTaken = mStats->getTimeDiff(sTime, eTime);
    mStats->setInitTime(timeTaken);
    return status;
}

// In encoder components, fetch the size of input buffer allocated
int32_t C2Encoder::getInputMaxBufSize() {
    int32_t bitStreamInfo[1] = {0};
    std::vector<std::unique_ptr<C2Param>> inParams;
    c2_status_t status = mComponent->query({}, {C2StreamMaxBufferSizeInfo::input::PARAM_TYPE},
                                           C2_DONT_BLOCK, &inParams);
    if (status != C2_OK && inParams.size() == 0) {
        ALOGE("Query MaxBufferSizeInfo failed => %d", status);
        return status;
    } else {
        size_t offset = sizeof(C2Param);
        for (size_t i = 0; i < inParams.size(); ++i) {
            C2Param *param = inParams[i].get();
            bitStreamInfo[i] = *(int32_t *)((uint8_t *)param + offset);
        }
    }
    mInputMaxBufSize = bitStreamInfo[0];
    if (mInputMaxBufSize < 0) {
        ALOGE("Invalid mInputMaxBufSize %d\n", mInputMaxBufSize);
        return -1;
    }
    return status;
}

int32_t C2Encoder::encodeFrames(ifstream &eleStream, size_t inputBufferSize) {
    ALOGV("In %s", __func__);
    int32_t frameSize = 0;
    if (!mIsAudioEncoder) {
        frameSize = mWidth * mHeight * 3 / 2;
    } else {
        frameSize = DEFAULT_AUDIO_FRAME_SIZE;
        if (getInputMaxBufSize() != 0) return -1;
        if (frameSize > mInputMaxBufSize) {
            frameSize = mInputMaxBufSize;
        }
    }
    int32_t numFrames = (inputBufferSize + frameSize - 1) / frameSize;
    // Temporary buffer to read data from the input file
    char *data = (char *)malloc(frameSize);
    if (!data) {
        ALOGE("Insufficient memory to read from input file");
        return -1;
    }

    typedef std::unique_lock<std::mutex> ULock;
    uint64_t presentationTimeUs = 0;
    size_t offset = 0;
    c2_status_t status = C2_OK;

    mStats->setStartTime();
    while (numFrames > 0) {
        std::unique_ptr<C2Work> work;
        // Prepare C2Work
        {
            ULock l(mQueueLock);
            if (mWorkQueue.empty()) mQueueCondition.wait_for(l, MAX_RETRY * TIME_OUT);
            if (!mWorkQueue.empty()) {
                mStats->addInputTime();
                work.swap(mWorkQueue.front());
                mWorkQueue.pop_front();
            } else {
                cout << "Wait for generating C2Work exceeded timeout" << endl;
                return -1;
            }
        }

        if (mIsAudioEncoder) {
            presentationTimeUs = mNumInputFrame * frameSize * (1000000 / mSampleRate);
        } else {
            presentationTimeUs = mNumInputFrame * (1000000 / mFrameRate);
        }
        uint32_t flags = 0;
        if (numFrames == 1) flags |= C2FrameData::FLAG_END_OF_STREAM;

        work->input.flags = (C2FrameData::flags_t)flags;
        work->input.ordinal.timestamp = presentationTimeUs;
        work->input.ordinal.frameIndex = mNumInputFrame;
        work->input.buffers.clear();

        if (inputBufferSize - offset < frameSize) {
            frameSize = inputBufferSize - offset;
        }
        eleStream.read(data, frameSize);
        if (eleStream.gcount() != frameSize) {
            ALOGE("read() from file failed. Incorrect bytes read");
            return -1;
        }
        offset += frameSize;

        if (frameSize) {
            if (mIsAudioEncoder) {
                std::shared_ptr<C2LinearBlock> block;
                status = mLinearPool->fetchLinearBlock(
                        frameSize, {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}, &block);
                if (status != C2_OK || !block) {
                    cout << "fetchLinearBlock failed : " << status << endl;
                    return status;
                }
                C2WriteView view = block->map().get();
                if (view.error() != C2_OK) {
                    cout << "C2LinearBlock::map() failed : " << view.error() << endl;
                    return view.error();
                }

                memcpy(view.base(), data, frameSize);
                work->input.buffers.emplace_back(new LinearBuffer(block));
            } else {
                std::shared_ptr<C2GraphicBlock> block;
                status = mGraphicPool->fetchGraphicBlock(
                        mWidth, mHeight, HAL_PIXEL_FORMAT_YV12,
                        {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}, &block);
                if (status != C2_OK || !block) {
                    cout << "fetchGraphicBlock failed : " << status << endl;
                    return status;
                }
                C2GraphicView view = block->map().get();
                if (view.error() != C2_OK) {
                    cout << "C2GraphicBlock::map() failed : " << view.error() << endl;
                    return view.error();
                }

                uint8_t *pY = view.data()[C2PlanarLayout::PLANE_Y];
                uint8_t *pU = view.data()[C2PlanarLayout::PLANE_U];
                uint8_t *pV = view.data()[C2PlanarLayout::PLANE_V];
                memcpy(pY, data, mWidth * mHeight);
                memcpy(pU, data + mWidth * mHeight, (mWidth * mHeight >> 2));
                memcpy(pV, data + (mWidth * mHeight * 5 >> 2), mWidth * mHeight >> 2);
                work->input.buffers.emplace_back(new GraphicBuffer(block));
            }
            mStats->addFrameSize(frameSize);
        }

        work->worklets.clear();
        work->worklets.emplace_back(new C2Worklet);

        std::list<std::unique_ptr<C2Work>> items;
        items.push_back(std::move(work));
        // queue() invokes process() function of C2 Plugin.
        status = mComponent->queue(&items);
        if (status != C2_OK) {
            ALOGE("queue failed");
            return status;
        }
        ALOGV("Frame #%d size = %d queued", mNumInputFrame, frameSize);
        numFrames--;
        mNumInputFrame++;
    }
    free(data);
    return status;
}

void C2Encoder::deInitCodec() {
    ALOGV("In %s", __func__);
    if (!mComponent) return;

    int64_t sTime = mStats->getCurTime();
    mComponent->stop();
    mComponent->release();
    mComponent = nullptr;
    int64_t eTime = mStats->getCurTime();
    int64_t timeTaken = mStats->getTimeDiff(sTime, eTime);
    mStats->setDeInitTime(timeTaken);
}

void C2Encoder::dumpStatistics(string inputReference, int64_t durationUs) {
    string operation = "c2encode";
    mStats->dumpStatistics(operation, inputReference, durationUs);
}

void C2Encoder::resetEncoder() {
    mIsAudioEncoder = false;
    mNumInputFrame = 0;
    mEos = false;
    if (mStats) mStats->reset();
}
+66 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef __C2_ENCODER_H__
#define __C2_ENCODER_H__

#include <stdio.h>
#include <algorithm>
#include <fstream>

#include "BenchmarkC2Common.h"

#define DEFAULT_AUDIO_FRAME_SIZE 4096

constexpr int32_t KDefaultFrameRate = 25;

class C2Encoder : public BenchmarkC2Common {
  public:
    C2Encoder()
        : mIsAudioEncoder(false),
          mWidth(0),
          mHeight(0),
          mNumInputFrame(0),
          mComponent(nullptr) {}

    int32_t createCodec2Component(string codecName, AMediaFormat *format);

    int32_t encodeFrames(ifstream &eleStream, size_t inputBufferSize);

    int32_t getInputMaxBufSize();

    void deInitCodec();

    void dumpStatistics(string inputReference, int64_t durationUs);

    void resetEncoder();

  private:
    bool mIsAudioEncoder;

    int32_t mWidth;
    int32_t mHeight;
    int32_t mFrameRate;
    int32_t mSampleRate;

    int32_t mNumInputFrame;
    int32_t mInputMaxBufSize;

    std::shared_ptr<android::Codec2Client::Listener> mListener;
    std::shared_ptr<android::Codec2Client::Component> mComponent;
};

#endif  // __C2_ENCODER_H__
Loading