博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android 音视频开发(七): 音视频录制流程总结
阅读量:6996 次
发布时间:2019-06-27

本文共 4501 字,大约阅读时间需要 15 分钟。

在前面我们学习和使用了、、、、、。 学习和使用了上述的API之后,相信对Android系统的音视频处理有一定的经验和心得了。本文及后面的几篇文章做的事情就是将这些知识串联起来,做一些稍微复杂的事情。

一、流程分析

1.1 需求说明

我们需要做的事情就是:串联整个音视频录制流程,完成音视频的采集、编码、封包成 mp4 输出。

1.2 实现方式

Android音视频采集的方法:预览用SurfaceView,视频采集用Camera类,音频采集用AudioRecord。

1.3 数据处理思路

使用MediaCodec 类进行编码压缩,视频压缩为H.264,音频压缩为aac,使用MediaMuxer 将音视频合成为MP4。

二、 实现过程

2.1 收集Camera数据,并转码为H264存储到文件

在收集数据之前,对Camera设置一些参数,方便收集后进行数据处理:

Camera.Parameters parameters = camera.getParameters();parameters.setPreviewFormat(ImageFormat.NV21);parameters.setPreviewSize(1280, 720);

然后设置PreviewCallback:

camera.setPreviewCallback(this);

就可以获取到Camera的原始NV21数据:

onPreviewFrame(byte[] bytes, Camera camera)

在创建一个H264Encoder类,在里面进行编码操作,并将编码后的数据存储到文件:

new Thread(new Runnable() {    @Override    public void run() {        isRuning = true;        byte[] input = null;        long pts = 0;        long generateIndex = 0;        while (isRuning) {            if (yuv420Queue.size() > 0) {                input = yuv420Queue.poll();                byte[] yuv420sp = new byte[width * height * 3 / 2];                // 必须要转格式,否则录制的内容播放出来为绿屏                NV21ToNV12(input, yuv420sp, width, height);                input = yuv420sp;            }            if (input != null) {                try {                    ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers();                    ByteBuffer[] outputBuffers = mediaCodec.getOutputBuffers();                    int inputBufferIndex = mediaCodec.dequeueInputBuffer(-1);                    if (inputBufferIndex >= 0) {                      pts = computePresentationTime(generateIndex);                      ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];                      inputBuffer.clear();                      inputBuffer.put(input);                      mediaCodec.queueInputBuffer(inputBufferIndex, 0, input.length, System.currentTimeMillis(), 0);                      generateIndex += 1;                    }                    MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();                    int outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, TIMEOUT_USEC);                    while (outputBufferIndex >= 0) {                        ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];                        byte[] outData = new byte[bufferInfo.size];                        outputBuffer.get(outData);                        if (bufferInfo.flags == MediaCodec.BUFFER_FLAG_CODEC_CONFIG) {                            configbyte = new byte[bufferInfo.size];                            configbyte = outData;                        } else if (bufferInfo.flags == MediaCodec.BUFFER_FLAG_SYNC_FRAME) {                            byte[] keyframe = new byte[bufferInfo.size + configbyte.length];                            System.arraycopy(configbyte, 0, keyframe, 0, configbyte.length);                            System.arraycopy(outData, 0, keyframe, configbyte.length, outData.length);                            outputStream.write(keyframe, 0, keyframe.length);                        } else {                            outputStream.write(outData, 0, outData.length);                        }                        mediaCodec.releaseOutputBuffer(outputBufferIndex, false);                        outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, TIMEOUT_USEC);                    }                } catch (Throwable t) {                    t.printStackTrace();                }            } else {                try {                    Thread.sleep(500);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }        }        // 停止编解码器并释放资源        try {            mediaCodec.stop();            mediaCodec.release();        } catch (Exception e) {            e.printStackTrace();        }        // 关闭数据流        try {            outputStream.flush();            outputStream.close();        } catch (IOException e) {            e.printStackTrace();        }    }}).start(); 

当结束编码的时候,需要将相关的资源释放掉:

// 停止编解码器并释放资源try {    mediaCodec.stop();    mediaCodec.release();} catch (Exception e) {    e.printStackTrace();}// 关闭数据流try {    outputStream.flush();    outputStream.close();} catch (IOException e) {    e.printStackTrace();}

此时,我们做到了将视频内容采集-->编码-->存储文件。但这个仅仅是对的延伸,但是很有必要。因为在前面学习了如何采集音频,如何使用MediaCodec去处理音视频,如何使用MediaMuxer去混合音视频。

示例代码:

下面我们在当前的的基础上继续完善,即将音视频采集并混合为音视频。

2.2 音视频采集+混合,存储到文件

基本完成思路已经在2.1的结尾处坐了说明,下面贴一下demo的链接:

示例代码:

 

想做更多的事情,只有学习了OpenGL之后才能继续了。录制音视频的学习先到此为止了。

转载地址:http://lgavl.baihongyu.com/

你可能感兴趣的文章
mybatis_helloworld(2)_源码
查看>>
Atitit godaddy 文件权限 root权限设置
查看>>
完整部署CentOS7.2+OpenStack+kvm 云平台环境(3)--为虚拟机指定固定ip
查看>>
用rfkill命令管理蓝牙和wifi
查看>>
BLE 广播数据解析
查看>>
Maven初学
查看>>
Oracle用户密码过期和用户被锁解决方法【转】
查看>>
Android 解决Android的TextView和EditText换行问题
查看>>
CSS效果集锦(持续更新中)
查看>>
通过重建Hosting系统理解HTTP请求在ASP.NET Core管道中的处理流程[中]:管道如何处理请求...
查看>>
Eigen教程(9)
查看>>
无线网卡与本地连接不能同时使用&一机多网络的优先级设置
查看>>
解决Apache长时间占用内存大的问题,Apache 内存优化方法
查看>>
PHP运行环境之IIS FastCGI 进程意外退出解决办法
查看>>
单元测试
查看>>
JetBrains Rider 2018.1 汉化
查看>>
sql server 带有OUTPUT的INSERT,DELETE,UPDATE
查看>>
nginx rewrite语法格式
查看>>
文件系统管理 之 实例解说 fdisk 使用方法
查看>>
常见HTTP状态码
查看>>