微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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

到目前为止,我可以设置一个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的缓冲区到缓冲区变体的一个例子.

原文地址:https://www.jb51.cc/android/311683.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐