这是我关于堆栈溢出的第一个问题,我真的是这里的新人。
我想在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领域的专家。希望能得到你的帮助。 感谢。