Перекодировать видеопоток с Android медиа-кодом - PullRequest
0 голосов
/ 09 января 2020

Я получаю поток H.264 от RTMP, но битрейт очень большой, я хочу перекодировать поток, чтобы уменьшить битрейт. Я создаю кодировщик с помощью createEncoderByType, после создания кодировщика я создаю поверхность из кодировщика с помощью createInputSurface. Затем я создаю декодер с кодировщиком формы поверхности, он работает нормально, но я не могу получить данные кодирования от кодера, он всегда возвращает -1000. Код следующий:

// config encoder
mEncode = AMediaCodec_createEncoderByType(MIME_TYPE);
if(mEncode == NULL) {
    MW_LOGE("Encoder MediaCodecH264: could not create Encoder");
    return false;
}
AMediaFormat *format = AMediaFormat_new();
AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, MIME_TYPE);
AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_WIDTH, ENCODE_WIDTH);
AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_HEIGHT, ENCODE_HEIGHT);
AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_BIT_RATE, BIT_RATE);
AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_FRAME_RATE, FRAME_RATE);
AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL);
AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_COLOR_FORMAT,
                      0x7F000789 /*COLOR_FormatSurface, COLOR_FormatYUV420Flexible 21*/);

media_status_t status =
    AMediaCodec_configure(mEncode, format, NULL, NULL, AMEDIACODEC_CONFIGURE_FLAG_ENCODE);
if(status != AMEDIA_OK) {
    MW_LOGE("Encoder AMediaCodec_configure() failed with error %i for format %u", (int) status,
            21);
    AMediaFormat_delete(format);
    AMediaCodec_delete(mEncode);
    return false;
}
AMediaFormat_delete(format);

status = AMediaCodec_createInputSurface(mEncode, &surface);
if(status != AMEDIA_OK) {
    MW_LOGE("Encoder AMediaCodec_createInputSurface() failed with error %i", (int) status);
    AMediaCodec_delete(mEncode);
    return false;
}
if((status = AMediaCodec_start(mEncode)) != AMEDIA_OK) {
    MW_LOGE("Encoder AMediaCodec_start: Could not start encoder.");
    AMediaCodec_delete(mEncode);
    return false;
}

// config decoder
mDecode = AMediaCodec_createDecoderByType(MIME_TYPE);
if(nullptr == mDecode) {
    MW_LOGE("cannot create decoder.");
    return false;
}

AMediaFormat *format = AMediaFormat_new();
AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, MIME_TYPE);
AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_WIDTH, ENCODE_WIDTH);
AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_HEIGHT, ENCODE_HEIGHT);
// AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_FRAME_RATE, FRAME_RATE);
// for send a buf to codec at least
// AMediaFormat_setBuffer(format, "csd-0", sps, sizeof(sps));    // sps
// AMediaFormat_setBuffer(format, "csd-1", pps, sizeof(pps));    // pps

media_status_t status = AMediaCodec_configure(mDecode, format, surface, NULL, 0);
if(status != AMEDIA_OK) {
    MW_LOGE("Decoder AMediaCodec_configure error.");
    AMediaCodec_delete(mDecode);
    mDecode = NULL;
    AMediaFormat_delete(format);
    return false;
}
AMediaFormat_delete(format);
status = AMediaCodec_start(mDecode);
if(status != AMEDIA_OK) {
    MW_LOGE("Decoder AMediaCodec_start error.");
    AMediaCodec_delete(mDecode);
    mDecode = NULL;
    return false;
}

// process data here
while(false == isConsumed) {
    ssize_t bufidx = AMediaCodec_dequeueInputBuffer(mDecode, CODEC_TIMEOUT * 10);
    if(bufidx >= 0) {
        size_t bufsize;
        uint8_t *codecBuf = AMediaCodec_getInputBuffer(mDecode, bufidx, &bufsize);
        if(codecBuf == nullptr) {
            MW_LOGE("AMediaCodec_getInputBuffer returned nullptr, short decode");
            break;
        }
        if(buf->mDataSize > bufsize || nullptr == buf->mData) {
            MW_LOGE("BufferItem info error. bufId:%d size:%d prt:%p", buf->mBufId,
                    buf->mDataSize, buf->mData);
            break;
        }
        // std::string data = std::string((char *) buf->mData, 20);
        // for(int i = 0; i < data.length(); i++) MW_LOGE("    0x%x", data.at(i));
        int size = buf->mDataSize;
        if(mFirstDecode || buf->mConfigChanged) {
            mFirstDecode = false;
            memcpy(codecBuf, buf->mSPS.c_str(), buf->mSPS.size());
            codecBuf += buf->mSPS.size();
            memcpy(codecBuf, buf->mPPS.c_str(), buf->mPPS.size());
            codecBuf += buf->mPPS.size();
            size += buf->mSPS.size() + buf->mPPS.size();
        }
        memcpy(codecBuf, buf->mData, buf->mDataSize);
        media_status_t mstatus =
            AMediaCodec_queueInputBuffer(mDecode, bufidx, 0 /* offset */, size, 0, 0);
        if(mstatus != AMEDIA_OK) {
            // AMEDIA_ERROR_UNKNOWN == { -ERANGE -EINVAL -EACCES }
            MW_LOGE("AMediaCodec_queueInputBuffer returned status %d, short decode",
                    (int) mstatus);
            break;
        }
        isConsumed = true;
        ret = true;
    }

    AMediaCodecBufferInfo info;
    static long nFrameCount = 0;
    MW_LOGD("start AMediaCodec_dequeueOutputBuffer");
    status = AMediaCodec_dequeueOutputBuffer(mEncode, &info, 5000);
    MW_LOGD("end AMediaCodec_dequeueOutputBuffer end!");
    if(status >= 0) {
        MW_LOGD("got encoded buffer[%d] of size=%d @%lld us flags=%x", nFrameCount, info.size,
                (long long) info.presentationTimeUs, info.flags);
        // mStats.add(info);
        AMediaCodec_releaseOutputBuffer(mEncode, status, false);
        ++nFrameCount;

        if(info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
            MW_LOGD("saw EOS");
            break;
        }

    } else if(status == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
    } else if(status == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
        std::shared_ptr<AMediaFormat> format = std::shared_ptr<AMediaFormat>(
            AMediaCodec_getOutputFormat(mEncode), deleter_AMediaFormat);
        // mStats.setOutputFormat(format);
        MW_LOGD("format changed: %s", AMediaFormat_toString(format.get()));
    } else if(status == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
    } else {
        MW_LOGD("Invalid status : %d", status);
    }

    // AMediaCodecBufferInfo info;
    // Get the index of the next available buffer of processed data. Donnot wait.
    status = AMediaCodec_dequeueOutputBuffer(mDecode, &info, 0);
    if(status >= 0) {
        if(info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
            MW_LOGE("output EOS");
            break;
        }
        bool doRender = (info.size != 0);
        media_status_t mstatus = AMediaCodec_releaseOutputBuffer(mDecode, status, doRender);
        if(mstatus != AMEDIA_OK) {
            // AMEDIA_ERROR_UNKNOWN == { -ERANGE -EINVAL -EACCES }
            MW_LOGE("AMediaCodec_releaseOutputBuffer returned status %d, short decode",
                    (int) mstatus);
            break;
        }
        MW_LOGD("AMediaCodec_releaseOutputBuffer bufidx:%d", status);
    } else if(status == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
        MW_LOGD("output buffers changed");
    } else if(status == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
        AMediaFormat *format = AMediaFormat_new();
        format = AMediaCodec_getOutputFormat(mDecode);
        MW_LOGD("format changed to: %s", AMediaFormat_toString(format));
        AMediaFormat_delete(format);
    } else if(status == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
        MW_LOGD("no output buffer right now");
    } else if(status <= AMEDIA_ERROR_BASE) {
        MW_LOGD("decode error: %d", status);
        mDecodeErrorNum = status;
        break;
    } else {
        MW_LOGD("unexpected info code: %d", status);
    }
    usleep(1000 * 10);
}
...