我正在尝试将MediaCodec
一系列图像保存为文件中的字节数组,并保存到视频文件中。我已经在 a 上测试了这些图像SurfaceView
(连续播放它们),我可以很好地看到它们。我查看了许多使用 的示例MediaCodec
,以下是我的理解(如果我错了,请纠正我):
从 MediaCodec 对象获取 InputBuffers -> 用帧的图像数据填充它 -> 将输入缓冲区排队 -> 获取编码的输出缓冲区 -> 将其写入文件 -> 增加演示时间并重复
但是,我对此进行了很多测试,最终得到以下两种情况之一:
- 我尝试模仿的所有示例项目都导致媒体服务器
queueInputBuffer
在第二次调用时死机。 - 我尝试在最后调用
codec.flush()
(在将输出缓冲区保存到文件之后,虽然我看到的例子都没有这样做)并且媒体服务器没有死,但是,我无法用任何媒体播放器打开输出视频文件,所以出了点问题。
这是我的代码:
MediaCodec codec = MediaCodec.createEncoderByType(MIMETYPE);
MediaFormat mediaFormat = null;
if(CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_720P)){
mediaFormat = MediaFormat.createVideoFormat(MIMETYPE, 1280 , 720);
} else {
mediaFormat = MediaFormat.createVideoFormat(MIMETYPE, 720, 480);
}
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, 700000);
mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 10);
mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar);
mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 5);
codec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
codec.start();
ByteBuffer[] inputBuffers = codec.getInputBuffers();
ByteBuffer[] outputBuffers = codec.getOutputBuffers();
boolean sawInputEOS = false;
int inputBufferIndex=-1,outputBufferIndex=-1;
BufferInfo info=null;
//loop to read YUV byte array from file
inputBufferIndex = codec.dequeueInputBuffer(WAITTIME);
if(bytesread<=0)sawInputEOS=true;
if(inputBufferIndex >= 0){
if(!sawInputEOS){
int samplesiz=dat.length;
inputBuffers[inputBufferIndex].put(dat);
codec.queueInputBuffer(inputBufferIndex, 0, samplesiz, presentationTime, 0);
presentationTime += 100;
info = new BufferInfo();
outputBufferIndex = codec.dequeueOutputBuffer(info, WAITTIME);
Log.i("BATA", "outputBufferIndex="+outputBufferIndex);
if(outputBufferIndex >= 0){
byte[] array = new byte[info.size];
outputBuffers[outputBufferIndex].get(array);
if(array != null){
try {
dos.write(array);
} catch (IOException e) {
e.printStackTrace();
}
}
codec.releaseOutputBuffer(outputBufferIndex, false);
inputBuffers[inputBufferIndex].clear();
outputBuffers[outputBufferIndex].clear();
if(sawInputEOS) break;
}
}else{
codec.queueInputBuffer(inputBufferIndex, 0, 0, presentationTime, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
info = new BufferInfo();
outputBufferIndex = codec.dequeueOutputBuffer(info, WAITTIME);
if(outputBufferIndex >= 0){
byte[] array = new byte[info.size];
outputBuffers[outputBufferIndex].get(array);
if(array != null){
try {
dos.write(array);
} catch (IOException e) {
e.printStackTrace();
}
}
codec.releaseOutputBuffer(outputBufferIndex, false);
inputBuffers[inputBufferIndex].clear();
outputBuffers[outputBufferIndex].clear();
break;
}
}
}
}
codec.flush();
try {
fstream2.close();
dos.flush();
dos.close();
} catch (IOException e) {
e.printStackTrace();
}
codec.stop();
codec.release();
codec = null;
return true;
}
我的问题是,如何使用 MediaCodec 从图像流中获取工作视频。我究竟做错了什么?
另一个问题(如果我不太贪心的话),我想在这个视频中添加一个音轨,也可以用 MediaCodec 来完成,还是必须使用 FFmpeg?
注意:我知道MediaMux
在 Android 4.3 中,但是,这不是我的选择,因为我的应用程序必须在 Android 4.1+ 上运行。
更新 感谢fadden的回答,我能够在没有媒体服务器死亡的情况下访问EOS(上面的代码是修改后的)。但是,我得到的文件正在产生乱码。这是我得到的视频快照(仅作为 .h264 文件使用)。
我的输入图像格式是 YUV 图像(来自相机预览的 NV21)。我无法让它成为任何可播放的格式。我尝试了所有 COLOR_FormatYUV420 格式和相同的乱码输出。而且我仍然找不到(使用 MediaCodec)添加音频。