到目前为止,我可以设置一个MediaCodec对视频流进行编码.目的是将我的用户生成的图像保存到视频文件中.
我使用用户图形的Android Bitmap对象将帧推入流.
看到我在这篇文章的底部使用的代码片段(完整的代码没有修剪):
MediaCodec使用ByteBuffer处理视频/音频流.
位图基于int [],如果转换为byte []将需要x4的大小int []
我做了一些研究,以确定在MediaCodec处理视频/音频流时,ByteBuffer有什么合同,但是这些信息几乎接近zilch.
所以,
MediaCodec中的ByteBuffer使用合约是什么?
指定MediaFormat中的框架尺寸是否自动意味着ByteBuffers的width * height * 4字节容量?
(我每次使用位图对象的每一帧)
感谢任何帮助.
import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileOutputStream; import java.nio.ByteBuffer; import android.graphics.Rect; import android.graphics.Bitmap.CompressFormat; import android.media.MediaCodec; import android.media.MediaCodec.BufferInfo; import android.media.CamcorderProfile; import android.media.MediaCodecInfo; import android.media.MediaFormat; import android.util.Log; import android.view.View; public class VideoCaptureManager { private boolean running; private long presentationTime; public void start(View rootView,String saveFilePath){ Log.e("OUT",saveFilePath); this.running = true; this.presentationTime = 0; this.capture(rootView,saveFilePath); } private void capture(final View rootView,String saveFilePath){ if(rootView != null){ rootView.setDrawingCacheEnabled(true); final Rect drawingRect = new Rect(); rootView.getDrawingRect(drawingRect); try{ final File file = new File(saveFilePath); if(file.exists()){ // File exists return return; } else { File parent = file.getParentFile(); if(!parent.exists()){ parent.mkdirs(); } } new Thread(){ public void run(){ try{ DataOutputStream dos = new DataOutputStream(new FileOutputStream(file)); MediaCodec codec = MediaCodec.createEncoderByType("video/mp4v-es"); MediaFormat mediaFormat = null; if(CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_720P)){ mediaFormat = MediaFormat.createVideoFormat("video/mp4v-es",720,1280); } else { mediaFormat = MediaFormat.createVideoFormat("video/mp4v-es",480,720); } mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE,700000); mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE,10); mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT,MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar); mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL,5); codec.configure(mediaFormat,null,MediaCodec.CONFIGURE_FLAG_ENCODE); codec.start(); ByteBuffer[] inputBuffers = codec.getInputBuffers(); ByteBuffer[] outputBuffers = codec.getOutputBuffers(); while(VideoCaptureManager.this.running){ try{ int inputBufferIndex = codec.dequeueInputBuffer(-2); if(inputBufferIndex >= 0){ // Fill in the bitmap bytes // inputBuffers[inputBufferIndex]. ByteArrayOutputStream baos = new ByteArrayOutputStream(); rootView.getDrawingCache().compress(CompressFormat.JPEG,80,baos); inputBuffers[inputBufferIndex].put(baos.toByteArray()); codec.queueInputBuffer(inputBufferIndex,inputBuffers[inputBufferIndex].capacity(),presentationTime,MediaCodec.BUFFER_FLAG_CODEC_CONFIG); presentationTime += 100; } BufferInfo info = new BufferInfo(); int outputBufferIndex = codec.dequeueOutputBuffer(info,-2); if(outputBufferIndex >= 0){ // Write the bytes to file byte[] array = outputBuffers[outputBufferIndex].array(); // THIS THORWS AN EXCEPTION. WHAT IS THE CONTRACT TO DEAL WITH ByteBuffer in this code? if(array != null){ dos.write(array); } codec.releaSEOutputBuffer(outputBufferIndex,false); } else if(outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED){ outputBuffers = codec.getOutputBuffers(); } else if(outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED){ // codec format is changed MediaFormat format = codec.getOutputFormat(); } Thread.sleep(100); }catch(Throwable th){ Log.e("OUT",th.getMessage(),th); } } codec.stop(); codec.release(); codec = null; dos.flush(); dos.close(); }catch(Throwable th){ Log.e("OUT",th); } } }.start(); }catch(Throwable th){ Log.e("OUT",th); } } } public void stop(){ this.running = false; } }
解决方法
ByteBuffer的确切布局由您选择的输入格式的编解码器决定.并非所有设备都支持所有可能的输入格式(例如,一些AVC编码器需要平面420 YUV,其他需要半平面).较旧版本的Android(< = API 17)并没有提供一种可移植的方式来为MediaCodec软件生成视频帧. 在Android 4.3(API 18)中,您有两个选项.首先,MediaCodec现在接受来自Surface的输入,这意味着您可以使用OpenGL ES绘制的任何内容都可以记录为电影.例如,参见
EncodeAndMuxTest sample.
其次,您仍然可以选择使用软件生成的YUV 420缓冲区,但现在他们更有可能工作,因为有CTS测试来锻炼它们.您仍然需要执行平面或半平面的运行时检测,但实际上只有两个布局.参见EncodeDecodeTest的缓冲区到缓冲区变体的一个例子.