使用android ndk方式编码mediacodec

时间:2016-10-21 04:43:52

标签: android encoding android-ndk mediacodec

这是我关于堆栈溢出的第一个问题,我真的是这里的新人。

我想在Android 5.0中使用NDK方式将yuv(I420)文件编码为H264文件。实际上yuv文件包含从屏幕(1440x2560)捕获的原始数据,用于我将数据格式从ARGB转换为I420,并将其调整为固定维度,如720x1280。以下是一些示例,如“sceenrecord”,我使用了MediaCodec queueInputbuffer 方法代替 createInputSurface 作为数据源推送方式。

为什么我不直接使用createInputSurface是因为在某些情况下,特别是对于静态或缓慢变化的屏幕场景,编码器 dequeueOutputbuffer 不会实时暴露缓冲区。它会首先返回EAGAIN几次,然后返回NO_ERROR(带数据)。这可能会导致我们的应用程序中不允许的延迟。所以我尝试用自己的方式实现它。 适用于某些Android手机,但对其他手机则无效以下是我的代码:

sp<ProcessState> self = ProcessState::self();
self->startThreadPool();

get_displayinfo();
int fd0 = open("/data/local/tmp/test.yuv",O_RDWR ,0664);
if(fd0 < 0) {
    printf("test.yuv open fail\n");
} else {
    printf("test.yuv open ok\n");
}
int fd1 = open("/data/local/tmp/test.h264",O_RDWR | O_CREAT,0664);
if(fd1 < 0) {
    printf("test.h264 open fail\n");
} else {
    printf("test.h264 open ok\n");
}
int readBytes;

status_t err;
sp<AMessage> format = new AMessage;
format->setInt32("width", gVideoWidth);
format->setInt32("height", gVideoHeight);
format->setString("mime", kMimeTypeAvc);
format->setInt32("color-format", OMX_COLOR_FormatAndroidOpaque);
format->setInt32("bitrate", 4000000);
format->setFloat("frame-rate",25);
format->setInt32("i-frame-interval", 10);
//format->setInt32("repeat-previous-frame-after",1000000/(25*4));

sp<ALooper> looper = new ALooper;
looper->setName("streamer_looper");
looper->start();
printf("Creating encoder\n");

sp<MediaCodec> encoder = MediaCodec::CreateByType(looper, kMimeTypeAvc, true);
if (encoder == NULL) {
    printf("unable to create %s encoder instance\n",kMimeTypeAvc);
    return (void*)err;
}
err = encoder->configure(format, NULL, NULL,MediaCodec::CONFIGURE_FLAG_ENCODE);
if (err != NO_ERROR) {
    printf("unable to configure %s encoder at %dx%d (err=%d)\n",
            kMimeTypeAvc, gVideoWidth, gVideoHeight, err);
    encoder->release();
    return (void*)err;
}
printf("Starting encoder\n");
err = encoder->start();
if(err != NO_ERROR){
    printf("unable to start enoder (err=%d)\n",err);
    return (void*)err;
}

//no more createInputSurface
Vector<sp<ABuffer>> Inbuffers;
Vector<sp<ABuffer>> Outbuffers;

err = encoder->getInputBuffers(&Inbuffers);
if(err != NO_ERROR){
    printf("unable to getInputBuffers (err=%d)\n",err);
    return (void*)err;
}

size_t inputIndex = 0;
int frameSize = gVideoWidth * gVideoHeight * 3 / 2;
unsigned char* base = (unsigned char*)malloc(frameSize);

int queueflag;
size_t bufIndex, offset, size;
int64_t ptsUsec;
uint32_t flags;
size_t  orientation;
static size_t i = 0;
int64_t startWhenUsec = systemTime(CLOCK_MONOTONIC)/1000;
//int64_t startWhenUsec = 0;
while (!gStopRequested){
    err = encoder->dequeueInputBuffer(&inputIndex,-1);
    if(err != NO_ERROR){
        printf("encoder dequeueuInputBuffer err=%s\n",strerror(err));
        return (void*)err;
    }

    Inbuffers[inputIndex]->setRange(0,frameSize);
    printf("InputBuffer inputIndex = %d cap = %d\n",inputIndex,
            Inbuffers[inputIndex]->capacity());
    readBytes = 0;
    readBytes = read(fd0,Inbuffers[inputIndex]->data(),frameSize);
    printf("readBytes = %d  i=%d\n",readBytes,i++);
    if(readBytes)
        queueflag = 0;
    else
        queueflag = MediaCodec::BUFFER_FLAG_EOS ;

    //memcpy(Inbuffers[inputIndex]->data(),(void*)base,frameSize);


    err = encoder->getOutputBuffers(&Outbuffers);
    if(err != NO_ERROR){
        printf("unable to getOutputBuffers (err=%d)\n",err);
        return (void*)err;
    }

    printf("timestamp = %lld\n",startWhenUsec);
    err = encoder->queueInputBuffer(
                inputIndex,
                0,
                frameSize,
                startWhenUsec,//timestamp needs to be set up
                //MediaCodec::BUFFER_FLAG_CODECCONFIG);
                queueflag);
    if(err != NO_ERROR){
        printf("encoder queueuInputBuffer err=%s\n",strerror(err));
        return (void*)err;
    }

    //sleep(1);
    startWhenUsec += 33000;
    err = encoder->dequeueOutputBuffer(&bufIndex, &offset, &size, &ptsUsec,
            &flags, 250000);
    //printf("dequeueoutputbuffer err = %d, %s\n",err,strerror(errno));
    switch (err) {
        case NO_ERROR:
            //printf("& = %d\n",(flags & MediaCodec::BUFFER_FLAG_CODECCONFIG));
            if ((flags & MediaCodec::BUFFER_FLAG_CODECCONFIG) != 0) {
                printf("Got sps/pps in buffer %zu, size=%zu, pts=%lld\n",bufIndex, size, ptsUsec);
                //dump_l("encodered data",Outbuffers[bufIndex]->data(),size);
                write(fd1,Outbuffers[bufIndex]->data(),size);
                size = 0;
                }
            if (size != 0) {
                printf("Got data in buffer %zu, size=%zu, pts=%lld\n",bufIndex, size, ptsUsec);
                //dump_l("encodered data",Outbuffers[bufIndex]->data(),size);
                write(fd1,Outbuffers[bufIndex]->data(),size);
                err = encoder->releaseOutputBuffer(bufIndex);
                if (err != NO_ERROR) {
                    printf("Unable to release output buffer (err=%d)\n",err);
                    return (void*)err;
                }
                if ((flags & MediaCodec::BUFFER_FLAG_EOS) != 0) {
                    printf("Received end-of-stream\n");
                    gStopRequested = true;
                }
            }
            break;
        case -EAGAIN:                       // INFO_TRY_AGAIN_LATER
            printf("Got -EAGAIN, looping\n");
            break;
        case INFO_FORMAT_CHANGED:           // INFO_OUTPUT_FORMAT_CHANGED
            break;
        case INFO_OUTPUT_BUFFERS_CHANGED:   // INFO_OUTPUT_BUFFERS_CHANGED
            // Not expected for an encoder; handle it anyway.
            printf("Encoder buffers changed\n");
            err = encoder->getOutputBuffers(&Outbuffers);
            if (err != NO_ERROR) {
                printf("Unable to get new output buffers (err=%d)\n", err);
                return (void*)err;
            }
            break;
        case INVALID_OPERATION:
            printf("dequeueOutputBuffer returned INVALID_OPERATION\n");
            return (void*)err;
        default:
            printf("Got weird result %d from dequeueOutputBuffer\n", err);
            return (void*)err;
    }
}

encoder->stop();
encoder->release();

失败的案例有以下错误:

  

test.yuv打开确定

     

test.h264 open ok

     

创建编码器

     

启动编码器

     

InputBuffer inputIndex = 0 cap = 1425408

     

readBytes = 1382400 i = 0

     

时间戳= 51771085711

     

dequeueOutputBuffer返回了INVALID_OPERATION

也许我错过了编码器的一些关键设置?或者忽略了一些平台差异?(就像MTK与Qualcomm Snapdragon相比......) 我知道@fadden是这个Android领域的专家。希望能得到你的帮助。 感谢。

0 个答案:

没有答案