Android 使用MediaCodec将一系列图像保存为视频

Android 使用MediaCodec将一系列图像保存为视频,android,video,android-mediacodec,Android,Video,Android Mediacodec,我正在尝试使用MediaCodec将一系列图像(保存为文件中的字节数组)保存到视频文件中。我已经在SurfaceView上测试了这些图像(连续播放),我可以很好地看到它们。我看过许多使用MediaCodec的例子,以下是我的理解(如果我错了,请纠正我): 从MediaCodec对象获取InputBuffers->用帧的 图像数据->将输入缓冲区排队->获取编码输出缓冲区-> 将其写入文件->增加演示时间并重复 然而,我已经对此进行了大量测试,最终得到了两个案例中的一个: 我尝试模拟的所有示例项

我正在尝试使用
MediaCodec
将一系列图像(保存为文件中的字节数组)保存到视频文件中。我已经在
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;

    }
mediacodecodec=MediaCodec.createEncoderByType(MIMETYPE);
MediaFormat MediaFormat=null;
if(摄像机档案文件hasProfile(摄像机档案文件质量_720P)){
mediaFormat=mediaFormat.createVideoFormat(MIMETYPE,1280720);
}否则{
mediaFormat=mediaFormat.createVideoFormat(MIMETYPE,720480);
}
mediaFormat.setInteger(mediaFormat.KEY\u比特率,700000);
mediaFormat.setInteger(mediaFormat.KEY\u FRAME\u RATE,10);
mediaFormat.setInteger(mediaFormat.KEY\u COLOR\u FORMAT,MediaCodecInfo.CodecCapabilities.COLOR\u formatyuv420semipular);
mediaFormat.setInteger(mediaFormat.KEY\u I\u FRAME\u INTERVAL,5);
codec.configure(mediaFormat,null,null,MediaCodec.configure\u FLAG\u ENCODE);
codec.start();
ByteBuffer[]inputBuffers=codec.getInputBuffers();
ByteBuffer[]outputBuffers=codec.getOutputBuffers();
布尔值sawInputEOS=false;
int-inputBufferIndex=-1,outputBufferIndex=-1;
BufferInfo=null;
//循环从文件中读取YUV字节数组
inputBufferIndex=codec.dequeueInputBuffer(WAITTIME);
如果(字节读取=0){
如果(!sawInputEOS){
int samplesiz=dat.length;
inputBuffers[inputBufferIndex].put(dat);
queueInputBuffer(inputBufferIndex,0,samplesiz,presentationTime,0);
presentationTime+=100;
info=新的BufferInfo();
outputBufferIndex=codec.dequeueOutputBuffer(信息,等待时间);
Log.i(“BATA”、“outputBufferIndex=“+outputBufferIndex”);
如果(outputBufferIndex>=0){
字节[]数组=新字节[info.size];
outputBuffers[outputBufferIndex].get(数组);
if(数组!=null){
试一试{
写(数组);
}捕获(IOE异常){
e、 printStackTrace();
}
}
codec.releaseOutputBuffer(outputBufferIndex,false);
inputBuffers[inputBufferIndex].clear();
outputBuffers[outputBufferIndex].clear();
如果(锯入)断裂;
}
}否则{
codec.queueInputBuffer(inputBufferIndex,0,0,presentationTime,MediaCodec.BUFFER\u标志\u结束\u流);
info=新的BufferInfo();
outputBufferIndex=codec.dequeueOutputBuffer(信息,等待时间);
如果(outputBufferIndex>=0){
字节[]数组=新字节[info.size];
outputBuffers[outputBufferIndex].get(数组);
if(数组!=null){
试一试{
写(数组);
}捕获(IOE异常){
e、 printStackTrace();
}
}
codec.releaseOutputBuffer(outputBufferIndex,false);
inputBuffers[inputBufferIndex].clear();
outputBuffers[outputBufferIndex].clear();
打破
}
}
}
}
codec.flush();
试一试{
fstream2.close();
dos.flush();
dos.close();
}捕获(IOE异常){
e、 printStackTrace();
}
codec.stop();
codec.release();
编解码器=空;
返回true;
}
我的问题是,如何使用MediaCodec从图像流中获取工作视频。我做错了什么

另一个问题(如果我不是太贪心的话),我想在这个视频中添加一个音频曲目,它也可以用MediaCodec完成,还是必须使用FFmpeg

注意:我知道Android 4.3中的
MediaMux
,但是,这不是我的选择,因为我的应用程序必须在Android 4.1+上运行

更新 多亏了fadden的回答,我能够在媒体服务器不死掉的情况下访问EOS(上面的代码是经过修改的)。然而,我得到的文件却在胡言乱语。这是我得到的视频快照(仅作为.h264文件使用)


我的输入图像格式是YUV图像(相机预览中的NV21)。我不能让它成为任何可播放的格式。我尝试了所有的颜色格式Yuv420格式和相同的胡言乱语输出。我仍然无法找到(使用MediaCodec)添加音频的方法。

我认为您的总体思路是正确的。需要注意的一些事项:

  • 并非所有设备都支持
    COLOR\u FormatYUV420