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

Commit 3184abdd authored by Wonsik Kim's avatar Wonsik Kim
Browse files

CCodec: Episode III --- Video Encoding

- Implement createInputSurface()
- Handle RGB format in C2AllocatorGralloc
- Define picture type mask and handle it
- Process onWorkDone_nb() callbacks in a separate thread
- Add an unofficial option to take component name directly in screenrecord
- Force array mode for allocation latency; to be removed once proper
  buffer pool is in place.

Test: setprop debug.stagefright.ccodec yes
Test: stagefright -S -N c2.google.avc.decoder /sdcard/a.mp4
Change-Id: Iba225350e2eb2a1e6f4d45a02628d0f7ba188df3
parent c0f23e94
Loading
Loading
Loading
Loading
+29 −5
Original line number Diff line number Diff line
@@ -73,6 +73,7 @@ static bool gMonotonicTime = false; // use system monotonic time for timesta
static enum {
    FORMAT_MP4, FORMAT_H264, FORMAT_FRAMES, FORMAT_RAW_FRAMES
} gOutputFormat = FORMAT_MP4;           // data format for output
static AString gCodecName = "";         // codec name override
static bool gSizeSpecified = false;     // was size explicitly requested?
static bool gWantInfoScreen = false;    // do we want initial info screen?
static bool gWantFrameTime = false;     // do we want times on each frame?
@@ -154,6 +155,7 @@ static status_t prepareEncoder(float displayFps, sp<MediaCodec>* pCodec,
    if (gVerbose) {
        printf("Configuring recorder for %dx%d %s at %.2fMbps\n",
                gVideoWidth, gVideoHeight, kMimeTypeAvc, gBitRate / 1000000.0);
        fflush(stdout);
    }

    sp<AMessage> format = new AMessage;
@@ -169,12 +171,22 @@ static status_t prepareEncoder(float displayFps, sp<MediaCodec>* pCodec,
    looper->setName("screenrecord_looper");
    looper->start();
    ALOGV("Creating codec");
    sp<MediaCodec> codec = MediaCodec::CreateByType(looper, kMimeTypeAvc, true);
    sp<MediaCodec> codec;
    if (gCodecName.empty()) {
        codec = MediaCodec::CreateByType(looper, kMimeTypeAvc, true);
        if (codec == NULL) {
            fprintf(stderr, "ERROR: unable to create %s codec instance\n",
                    kMimeTypeAvc);
            return UNKNOWN_ERROR;
        }
    } else {
        codec = MediaCodec::CreateByComponentName(looper, gCodecName);
        if (codec == NULL) {
            fprintf(stderr, "ERROR: unable to create %s codec instance\n",
                    gCodecName.c_str());
            return UNKNOWN_ERROR;
        }
    }

    err = codec->configure(format, NULL, NULL,
            MediaCodec::CONFIGURE_FLAG_ENCODE);
@@ -275,9 +287,11 @@ static status_t setDisplayProjection(
        if (gRotate) {
            printf("Rotated content area is %ux%u at offset x=%d y=%d\n",
                    outHeight, outWidth, offY, offX);
            fflush(stdout);
        } else {
            printf("Content area is %ux%u at offset x=%d y=%d\n",
                    outWidth, outHeight, offX, offY);
            fflush(stdout);
        }
    }

@@ -346,6 +360,7 @@ static status_t runEncoder(const sp<MediaCodec>& encoder,
        if (systemTime(CLOCK_MONOTONIC) > endWhenNsec) {
            if (gVerbose) {
                printf("Time limit reached\n");
                fflush(stdout);
            }
            break;
        }
@@ -483,6 +498,7 @@ static status_t runEncoder(const sp<MediaCodec>& encoder,
        printf("Encoder stopping; recorded %u frames in %" PRId64 " seconds\n",
                debugNumFrames, nanoseconds_to_seconds(
                        systemTime(CLOCK_MONOTONIC) - startWhenNsec));
        fflush(stdout);
    }
    return NO_ERROR;
}
@@ -556,6 +572,7 @@ static status_t recordScreen(const char* fileName) {
        printf("Main display is %dx%d @%.2ffps (orientation=%u)\n",
                mainDpyInfo.w, mainDpyInfo.h, mainDpyInfo.fps,
                mainDpyInfo.orientation);
        fflush(stdout);
    }

    bool rotated = isDeviceRotated(mainDpyInfo.orientation);
@@ -623,6 +640,7 @@ static status_t recordScreen(const char* fileName) {
        }
        if (gVerbose) {
            printf("Bugreport overlay created\n");
            fflush(stdout);
        }
    } else {
        // Use the encoder's input surface as the virtual display surface.
@@ -715,6 +733,7 @@ static status_t recordScreen(const char* fileName) {

        if (gVerbose) {
            printf("Stopping encoder and muxer\n");
            fflush(stdout);
        }
    }

@@ -761,6 +780,7 @@ static status_t notifyMediaScanner(const char* fileName) {
            printf(" %s", argv[i]);
        }
        putchar('\n');
        fflush(stdout);
    }

    pid_t pid = fork();
@@ -898,6 +918,7 @@ int main(int argc, char* const argv[]) {
        { "show-frame-time",    no_argument,        NULL, 'f' },
        { "rotate",             no_argument,        NULL, 'r' },
        { "output-format",      required_argument,  NULL, 'o' },
        { "codec-name",         required_argument,  NULL, 'N' },
        { "monotonic-time",     no_argument,        NULL, 'm' },
        { NULL,                 0,                  NULL, 0 }
    };
@@ -978,6 +999,9 @@ int main(int argc, char* const argv[]) {
                return 2;
            }
            break;
        case 'N':
            gCodecName = optarg;
            break;
        case 'm':
            gMonotonicTime = true;
            break;
+1 −0
Original line number Diff line number Diff line
@@ -105,6 +105,7 @@ cc_library_shared {
        "libmedia_helper",
        "libstagefright_codec2",
        "libstagefright_foundation",
        "libstagefright_gbs",
        "libstagefright_omx",
        "libstagefright_omx_utils",
        "libstagefright_xmlparser",
+154 −59
Original line number Diff line number Diff line
@@ -23,7 +23,9 @@
#include <C2PlatformSupport.h>

#include <gui/Surface.h>
#include <media/stagefright/BufferProducerWrapper.h>
#include <media/stagefright/CCodec.h>
#include <media/stagefright/PersistentSurface.h>

#include "include/CCodecBufferChannel.h"

@@ -111,15 +113,17 @@ Mutexed<sp<CCodecWatchdog>> CCodecWatchdog::sInstance;

class CCodecListener : public C2Component::Listener {
public:
    CCodecListener(const std::shared_ptr<CCodecBufferChannel> &channel)
        : mChannel(channel) {
    }
    explicit CCodecListener(const wp<CCodec> &codec) : mCodec(codec) {}

    virtual void onWorkDone_nb(
            std::weak_ptr<C2Component> component,
            std::vector<std::unique_ptr<C2Work>> workItems) override {
        (void)component;
        mChannel->onWorkDone(std::move(workItems));
        sp<CCodec> codec(mCodec.promote());
        if (!codec) {
            return;
        }
        codec->onWorkDone(workItems);
    }

    virtual void onTripped_nb(
@@ -137,7 +141,7 @@ public:
    }

private:
    std::shared_ptr<CCodecBufferChannel> mChannel;
    wp<CCodec> mCodec;
};

}  // namespace
@@ -159,11 +163,11 @@ std::shared_ptr<BufferChannelBase> CCodec::getBufferChannel() {
void CCodec::initiateAllocateComponent(const sp<AMessage> &msg) {
    {
        Mutexed<State>::Locked state(mState);
        if (state->mState != RELEASED) {
        if (state->get() != RELEASED) {
            mCallback->onError(INVALID_OPERATION, ACTION_CODE_FATAL);
            return;
        }
        state->mState = ALLOCATING;
        state->set(ALLOCATING);
    }

    AString componentName;
@@ -178,14 +182,14 @@ void CCodec::initiateAllocateComponent(const sp<AMessage> &msg) {

void CCodec::allocate(const AString &componentName) {
    // TODO: use C2ComponentStore to create component
    mListener.reset(new CCodecListener(mChannel));
    mListener.reset(new CCodecListener(this));

    std::shared_ptr<C2Component> comp;
    c2_status_t err = GetCodec2PlatformComponentStore()->createComponent(
            componentName.c_str(), &comp);
    if (err != C2_OK) {
        Mutexed<State>::Locked state(mState);
        state->mState = RELEASED;
        state->set(RELEASED);
        state.unlock();
        mCallback->onError(err, ACTION_CODE_FATAL);
        state.lock();
@@ -194,15 +198,15 @@ void CCodec::allocate(const AString &componentName) {
    comp->setListener_vb(mListener, C2_MAY_BLOCK);
    {
        Mutexed<State>::Locked state(mState);
        if (state->mState != ALLOCATING) {
            state->mState = RELEASED;
        if (state->get() != ALLOCATING) {
            state->set(RELEASED);
            state.unlock();
            mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
            state.lock();
            return;
        }
        state->mState = ALLOCATED;
        state->mComp = comp;
        state->set(ALLOCATED);
        state->comp = comp;
    }
    mChannel->setComponent(comp);
    mCallback->onComponentAllocated(comp->intf()->getName().c_str());
@@ -211,7 +215,7 @@ void CCodec::allocate(const AString &componentName) {
void CCodec::initiateConfigureComponent(const sp<AMessage> &format) {
    {
        Mutexed<State>::Locked state(mState);
        if (state->mState != ALLOCATED) {
        if (state->get() != ALLOCATED) {
            mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
            return;
        }
@@ -252,6 +256,9 @@ void CCodec::configure(const sp<AMessage> &msg) {
                inputFormat->setInt32("sample-rate", 44100);
                outputFormat->setInt32("channel-count", 1);
                outputFormat->setInt32("sample-rate", 44100);
            } else {
                outputFormat->setInt32("width", 1080);
                outputFormat->setInt32("height", 1920);
            }
        } else {
            inputFormat->setString("mime", mime);
@@ -272,32 +279,81 @@ void CCodec::configure(const sp<AMessage> &msg) {

    {
        Mutexed<Formats>::Locked formats(mFormats);
        formats->mInputFormat = inputFormat;
        formats->mOutputFormat = outputFormat;
        formats->inputFormat = inputFormat;
        formats->outputFormat = outputFormat;
    }
    mCallback->onComponentConfigured(inputFormat, outputFormat);
}


void CCodec::initiateCreateInputSurface() {
    // TODO
    (new AMessage(kWhatCreateInputSurface, this))->post();
}

void CCodec::createInputSurface() {
    sp<IGraphicBufferProducer> producer;
    sp<GraphicBufferSource> source(new GraphicBufferSource);

    status_t err = source->initCheck();
    if (err != OK) {
        ALOGE("Failed to initialize graphic buffer source: %d", err);
        mCallback->onInputSurfaceCreationFailed(err);
        return;
    }
    producer = source->getIGraphicBufferProducer();

    err = setupInputSurface(source);
    if (err != OK) {
        ALOGE("Failed to set up input surface: %d", err);
        mCallback->onInputSurfaceCreationFailed(err);
        return;
    }

    sp<AMessage> inputFormat;
    sp<AMessage> outputFormat;
    {
        Mutexed<Formats>::Locked formats(mFormats);
        inputFormat = formats->inputFormat;
        outputFormat = formats->outputFormat;
    }
    mCallback->onInputSurfaceCreated(
            inputFormat,
            outputFormat,
            new BufferProducerWrapper(producer));
}

status_t CCodec::setupInputSurface(const sp<GraphicBufferSource> &source) {
    status_t err = mChannel->setGraphicBufferSource(source);
    if (err != OK) {
        return err;
    }

    // TODO: configure |source| with other settings.
    return OK;
}

void CCodec::initiateSetInputSurface(const sp<PersistentSurface> &surface) {
    sp<AMessage> msg = new AMessage(kWhatSetInputSurface, this);
    msg->setObject("surface", surface);
    msg->post();
}

void CCodec::setInputSurface(const sp<PersistentSurface> &surface) {
    // TODO
    (void)surface;

    mCallback->onInputSurfaceDeclined(ERROR_UNSUPPORTED);
}

void CCodec::initiateStart() {
    {
        Mutexed<State>::Locked state(mState);
        if (state->mState != ALLOCATED) {
        if (state->get() != ALLOCATED) {
            state.unlock();
            mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
            state.lock();
            return;
        }
        state->mState = STARTING;
        state->set(STARTING);
    }

    (new AMessage(kWhatStart, this))->post();
@@ -307,13 +363,13 @@ void CCodec::start() {
    std::shared_ptr<C2Component> comp;
    {
        Mutexed<State>::Locked state(mState);
        if (state->mState != STARTING) {
        if (state->get() != STARTING) {
            state.unlock();
            mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
            state.lock();
            return;
        }
        comp = state->mComp;
        comp = state->comp;
    }
    c2_status_t err = comp->start();
    if (err != C2_OK) {
@@ -325,20 +381,20 @@ void CCodec::start() {
    sp<AMessage> outputFormat;
    {
        Mutexed<Formats>::Locked formats(mFormats);
        inputFormat = formats->mInputFormat;
        outputFormat = formats->mOutputFormat;
        inputFormat = formats->inputFormat;
        outputFormat = formats->outputFormat;
    }
    mChannel->start(inputFormat, outputFormat);

    {
        Mutexed<State>::Locked state(mState);
        if (state->mState != STARTING) {
        if (state->get() != STARTING) {
            state.unlock();
            mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
            state.lock();
            return;
        }
        state->mState = RUNNING;
        state->set(RUNNING);
    }
    mCallback->onStartCompleted();
}
@@ -354,17 +410,17 @@ void CCodec::initiateShutdown(bool keepComponentAllocated) {
void CCodec::initiateStop() {
    {
        Mutexed<State>::Locked state(mState);
        if (state->mState == ALLOCATED
                || state->mState  == RELEASED
                || state->mState == STOPPING
                || state->mState == RELEASING) {
        if (state->get() == ALLOCATED
                || state->get()  == RELEASED
                || state->get() == STOPPING
                || state->get() == RELEASING) {
            // We're already stopped, released, or doing it right now.
            state.unlock();
            mCallback->onStopCompleted();
            state.lock();
            return;
        }
        state->mState = STOPPING;
        state->set(STOPPING);
    }

    (new AMessage(kWhatStop, this))->post();
@@ -374,19 +430,19 @@ void CCodec::stop() {
    std::shared_ptr<C2Component> comp;
    {
        Mutexed<State>::Locked state(mState);
        if (state->mState == RELEASING) {
        if (state->get() == RELEASING) {
            state.unlock();
            // We're already stopped or release is in progress.
            mCallback->onStopCompleted();
            state.lock();
            return;
        } else if (state->mState != STOPPING) {
        } else if (state->get() != STOPPING) {
            state.unlock();
            mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
            state.lock();
            return;
        }
        comp = state->mComp;
        comp = state->comp;
    }
    mChannel->stop();
    status_t err = comp->stop();
@@ -397,8 +453,8 @@ void CCodec::stop() {

    {
        Mutexed<State>::Locked state(mState);
        if (state->mState == STOPPING) {
            state->mState = ALLOCATED;
        if (state->get() == STOPPING) {
            state->set(ALLOCATED);
        }
    }
    mCallback->onStopCompleted();
@@ -407,7 +463,7 @@ void CCodec::stop() {
void CCodec::initiateRelease(bool sendCallback /* = true */) {
    {
        Mutexed<State>::Locked state(mState);
        if (state->mState == RELEASED || state->mState == RELEASING) {
        if (state->get() == RELEASED || state->get() == RELEASING) {
            // We're already released or doing it right now.
            if (sendCallback) {
                state.unlock();
@@ -416,8 +472,8 @@ void CCodec::initiateRelease(bool sendCallback /* = true */) {
            }
            return;
        }
        if (state->mState == ALLOCATING) {
            state->mState = RELEASING;
        if (state->get() == ALLOCATING) {
            state->set(RELEASING);
            // With the altered state allocate() would fail and clean up.
            if (sendCallback) {
                state.unlock();
@@ -426,7 +482,7 @@ void CCodec::initiateRelease(bool sendCallback /* = true */) {
            }
            return;
        }
        state->mState = RELEASING;
        state->set(RELEASING);
    }

    std::thread([this, sendCallback] { release(sendCallback); }).detach();
@@ -436,7 +492,7 @@ void CCodec::release(bool sendCallback) {
    std::shared_ptr<C2Component> comp;
    {
        Mutexed<State>::Locked state(mState);
        if (state->mState == RELEASED) {
        if (state->get() == RELEASED) {
            if (sendCallback) {
                state.unlock();
                mCallback->onReleaseCompleted();
@@ -444,15 +500,15 @@ void CCodec::release(bool sendCallback) {
            }
            return;
        }
        comp = state->mComp;
        comp = state->comp;
    }
    mChannel->stop();
    comp->release();

    {
        Mutexed<State>::Locked state(mState);
        state->mState = RELEASED;
        state->mComp.reset();
        state->set(RELEASED);
        state->comp.reset();
    }
    if (sendCallback) {
        mCallback->onReleaseCompleted();
@@ -466,11 +522,11 @@ status_t CCodec::setSurface(const sp<Surface> &surface) {
void CCodec::signalFlush() {
    {
        Mutexed<State>::Locked state(mState);
        if (state->mState != RUNNING) {
        if (state->get() != RUNNING) {
            mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
            return;
        }
        state->mState = FLUSHING;
        state->set(FLUSHING);
    }

    (new AMessage(kWhatFlush, this))->post();
@@ -480,13 +536,13 @@ void CCodec::flush() {
    std::shared_ptr<C2Component> comp;
    {
        Mutexed<State>::Locked state(mState);
        if (state->mState != FLUSHING) {
        if (state->get() != FLUSHING) {
            state.unlock();
            mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
            state.lock();
            return;
        }
        comp = state->mComp;
        comp = state->comp;
    }

    mChannel->stop();
@@ -502,7 +558,7 @@ void CCodec::flush() {

    {
        Mutexed<State>::Locked state(mState);
        state->mState = FLUSHED;
        state->set(FLUSHED);
    }
    mCallback->onFlushCompleted();
}
@@ -510,26 +566,26 @@ void CCodec::flush() {
void CCodec::signalResume() {
    {
        Mutexed<State>::Locked state(mState);
        if (state->mState != FLUSHED) {
        if (state->get() != FLUSHED) {
            state.unlock();
            mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
            state.lock();
            return;
        }
        state->mState = RESUMING;
        state->set(RESUMING);
    }

    mChannel->start(nullptr, nullptr);

    {
        Mutexed<State>::Locked state(mState);
        if (state->mState != RESUMING) {
        if (state->get() != RESUMING) {
            state.unlock();
            mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
            state.lock();
            return;
        }
        state->mState = RUNNING;
        state->set(RUNNING);
    }
}

@@ -545,6 +601,14 @@ void CCodec::signalRequestIDRFrame() {
    // TODO
}

void CCodec::onWorkDone(std::vector<std::unique_ptr<C2Work>> &workItems) {
    Mutexed<std::list<std::unique_ptr<C2Work>>>::Locked queue(mWorkDoneQueue);
    for (std::unique_ptr<C2Work> &item : workItems) {
        queue->push_back(std::move(item));
    }
    (new AMessage(kWhatWorkDone, this))->post();
}

void CCodec::onMessageReceived(const sp<AMessage> &msg) {
    TimePoint now = std::chrono::steady_clock::now();
    switch (msg->what()) {
@@ -582,6 +646,37 @@ void CCodec::onMessageReceived(const sp<AMessage> &msg) {
            flush();
            break;
        }
        case kWhatCreateInputSurface: {
            // Surface operations may be briefly blocking.
            setDeadline(now + 100ms);
            createInputSurface();
            break;
        }
        case kWhatSetInputSurface: {
            // Surface operations may be briefly blocking.
            setDeadline(now + 100ms);
            sp<RefBase> obj;
            CHECK(msg->findObject("surface", &obj));
            sp<PersistentSurface> surface(static_cast<PersistentSurface *>(obj.get()));
            setInputSurface(surface);
            break;
        }
        case kWhatWorkDone: {
            std::unique_ptr<C2Work> work;
            {
                Mutexed<std::list<std::unique_ptr<C2Work>>>::Locked queue(mWorkDoneQueue);
                if (queue->empty()) {
                    break;
                }
                work.swap(queue->front());
                queue->pop_front();
                if (!queue->empty()) {
                    (new AMessage(kWhatWorkDone, this))->post();
                }
            }
            mChannel->onWorkDone(work);
            break;
        }
        default: {
            ALOGE("unrecognized message");
            break;
+530 −123

File changed.

Preview size limit exceeded, changes collapsed.

+7 −0
Original line number Diff line number Diff line
@@ -67,6 +67,7 @@ enum C2ParamIndexKind : C2Param::type_index_t {
    kParamIndexVideoSizeTuning,

    kParamIndexCsd,
    kParamIndexPictureTypeMask,

    // video info

@@ -133,6 +134,12 @@ typedef C2PortParam<C2Tuning, C2Uint64Array, kParamIndexBlockPools> C2PortBlockP

typedef C2StreamParam<C2Info, C2BlobValue, kParamIndexCsd> C2StreamCsdInfo;

C2ENUM(C2PictureTypeMask, uint32_t,
    C2PictureTypeKeyFrame = (1u << 0),
)

typedef C2StreamParam<C2Info, C2Uint32Value, kParamIndexPictureTypeMask> C2StreamPictureTypeMaskInfo;

/*
   Component description fields:

Loading