如何在android的MediaCodec上下文中使用ByteBuffer

到目前为止,我能够设置一个MediaCodec来编码videostream。 目的是将我的用户生成的图稿保存到video文件中。

我使用用户图稿的android位图对象将帧推入stream中。

查看我在这篇文章底部使用的代码片段(这是完整的代码没有任何修剪):

MediaCodec使用ByteBuffer来处理video/audiostream。

位图基于int [],如果转换为byte []将需要x4大小的int []

我做了一些研究,找出在处理MediaCodec中的video/audiostream时ByteBuffer有什么契约,但是信息几乎接近zilch。

那么,MediaCodec中的ByteBuffer使用约定是什么?

在MediaFormat中指定帧尺寸是否意味着ByteBuffers的宽度*高度* 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, 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, 0, 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.getMessage(), th); } } }.start(); }catch(Throwable th){ Log.e("OUT", th.getMessage(), th); } } } public void stop(){ this.running = false; } } 

Solutions Collecting From Web of "如何在android的MediaCodec上下文中使用ByteBuffer"

ByteBuffer的确切布局由您select的input格式的编解码器决定。 并非所有设备都支持所有可能的input格式(例如,某些AVC编码器需要平面420 YUV,其他需要半平面)。 旧版本的Android(<= API 17)并没有真正提供一种便携的方式来为软件生成MediaCodecvideo帧。

在Android 4.3(API 18)中,有两个选项。 首先, MediaCodec现在接受来自Surface的input,这意味着您可以使用OpenGL ES绘制的任何东西都可以被logging为电影。 例如,请参阅EncodeAndMuxTest示例 。

其次,您仍然可以select使用软件生成的YUV 420缓冲区,但现在他们更有可能工作,因为有CTStesting可以使用它们。 您仍然必须执行平面或半平面的运行时检测,但实际上只有两种布局。 查看EncodeDecodeTest的缓冲区到缓冲区变体的例子。