音视频开发路线:

Android 音视频开发入门指南_Jhuster的专栏的技术博客_51CTO博客_android 音视频开发入门

demo地址:

GitHub - wygsqsj/videoPath: 音视频学习路线demo

MediaCodec

使用MediaCodec编解码实际是通过底层的硬件来对我们的音视频数据进行处理的,俗称硬编硬解,ffmpeg编解码是软解,效率不如MediaCodec,MediaCodec的主要实现是通过Native层去访问dsp芯片,让dsp芯片去编/解码流,整个流程按我的理解就是类似一个火腿肠加工厂,我给他一车猪,他拿走两头,一顿加工输入一筐火腿肠,我把这筐火腿肠取走,工厂收回筐,再抓两头猪进行加工,更详细的说明:MediaCodec API完成音频 AAC 硬编、硬解 - 简书

API说明

  • 构建MediaCodec

    1. MediaCodec.createDecoderByType("要解码的类型")构建解码器;
    2. MediaCodec.createEncoderByType("要编码码的类型")构建编码码器;配置config
  • 配置config
configure(@Nullable MediaFormat format//media格式控制  ;参数3 ; @Nullable Surface surface,//surface用于渲染编解码后的视频@Nullable MediaCrypto crypto,//加密用的对象,可根据自身需要定制@ConfigureFlag int flags)//标志位,解码为0,编码为1 
  • start开启编解码
  • dequeueInputBuffer() 可以理解为往工厂运猪的小推车,返回-1代表没有车子,返回大于-1代表有车子,获取到序号后就可以通过getInputBuffers()[序号]获取到小车子,再把猪放到车子里就可以了
  • queueInputBuffer  理解成把小推车里的猪运进工厂
void queueInputBuffer(int index,//小推车序号int offset, //偏移量,一般为0,表示从猪的那个地方开始加工int size, //猪的大小long presentationTimeUs, //时间戳,理解当前猪在一车猪中的顺序int flags//屠宰的标志,0进行加工 4就是没猪了)
  • dequeueOutputBuffer(“延时获取的微秒值”) 返回已处理好的数据buffer序号,理解成装火腿肠的筐序号,通过getOutputBuffers()[序号]获取装载数据的Buffer; 他除了序号的作用,还可以用作标志位,大于等于0表示序号,小于0表示当前codec返回的一些标志:

    • MediaCodec.INFO_OUTPUT_FORMAT_CHANGED 输出的format已更改
    • MediaCodec.INFO_TRY_AGAIN_LATER 超时,没获取到
    • MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED 输出缓冲区已更改
  • releaseOutputBuffer(筐的序号, 标志位) 表示把筐还给工厂,第二个参数为true时将数据放到我们配置的surface里面
  • stop() release()释放资源

总之同步的方式就是配置完MediaCodec后开启while循环不断的dequeueInputBuffer -> queueInputBuffer填充数据 -> dequeueOutputBuffer -> releaseOutputBuffer

使用

  • input数据
//所有的猪都运进猪厂后不再添加
if (hasAudio) {//从猪肉工厂获取装猪的小推车,填充数据后发送到猪肉工厂进行处理ByteBuffer[] inputBuffers = decodeCodec.getInputBuffers();//所有的小推车int inputIndex = decodeCodec.dequeueInputBuffer(0);//返回当前可用的小推车标号if (inputIndex != -1) {Log.i(LOG_TAG, "找到了input 小推车" + inputIndex);//将MediaCodec数据取出来放到这个缓冲区里ByteBuffer inputBuffer = inputBuffers[inputIndex];//拿到小推车inputBuffer.clear();//扔出去里面旧的东西//将audioExtractor里面的猪装载到小推车里面int readSize = audioExtractor.readSampleData(inputBuffer, 0);//audioExtractor没猪了,也要告知一下if (readSize < 0) {Log.i(LOG_TAG, "当前音频已经读取完了");decodeCodec.queueInputBuffer(inputIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);hasAudio = false;} else {//拿到猪Log.i(LOG_TAG, "读取到了音频数据,当前音频数据的数据长度为:" + readSize);//告诉工厂这头猪的小推车序号、猪的大小、猪在这群猪里的排行、屠宰的标志decodeCodec.queueInputBuffer(inputIndex, 0, readSize, audioExtractor.getSampleTime(), 0);//读取音频的下一帧audioExtractor.advance();}} else {Log.i(LOG_TAG, "没有可用的input 小推车");}
}
  • output
int outputIndex = decodeCodec.dequeueOutputBuffer(decodeBufferInfo, 0);//返回当前筐的标记
switch (outputIndex) {case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:Log.i(LOG_TAG, "输出的format已更改" + decodeCodec.getOutputFormat());break;case MediaCodec.INFO_TRY_AGAIN_LATER:Log.i(LOG_TAG, "超时,没获取到");break;case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:Log.i(LOG_TAG, "输出缓冲区已更改");break;default:Log.i(LOG_TAG, "获取到解码后的数据了,当前解析后的数据长度为:" + decodeBufferInfo.size);//获取所有的筐ByteBuffer[] outputBuffers = decodeCodec.getOutputBuffers();//拿到当前装满火腿肠的筐ByteBuffer outputBuffer;if (Build.VERSION.SDK_INT >= 21) {outputBuffer = decodeCodec.getOutputBuffer(outputIndex);} else {outputBuffer = outputBuffers[outputIndex];}//将火腿肠放到新的容器里,便于后期装车运走byte[] pcmData = new byte[decodeBufferInfo.size];outputBuffer.get(pcmData);//写入到字节数组中outputBuffer.clear();//清空当前筐//装车fos.write(pcmData);//数据写入文件中fos.flush();//把筐放回工厂里面decodeCodec.releaseOutputBuffer(outputIndex, false);break;
}
  • 完整代码
package com.wish.videopath.demo5;import android.content.Context;
import android.media.MediaCodec;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.os.Build;
import android.os.Environment;
import android.util.Log;import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;import com.wish.videopath.R;import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;import static com.wish.videopath.MainActivity.LOG_TAG;/*** 类名称:EncodeAACThread* 类描述:将AAC通过MediaCodec接码成PCM文件*/
class DecodeAACThread extends Thread {private Context context;private MediaFormat audioFormat;private File pcmFile;private boolean hasAudio = true;private FileOutputStream fos = null;public DecodeAACThread(Demo5Activity demo5Activity) {context = demo5Activity;pcmFile = new File(context.getExternalFilesDir(Environment.DIRECTORY_MUSIC), "demo5.pcm");try {pcmFile.createNewFile();} catch (IOException e) {e.printStackTrace();}}@RequiresApi(api = Build.VERSION_CODES.N)@Overridepublic void run() {super.run();//通过MediaExtractor获取音频通道MediaExtractor audioExtractor = new MediaExtractor();MediaCodec decodeCodec = null;//pcm文件输出
//        FileOutputStream fos = null;try {fos = new FileOutputStream(pcmFile.getAbsoluteFile());audioExtractor.setDataSource(context.getResources().openRawResourceFd(R.raw.demo5));int count = audioExtractor.getTrackCount();for (int i = 0; i < count; i++) {audioFormat = audioExtractor.getTrackFormat(i);if (audioFormat.getString(MediaFormat.KEY_MIME).startsWith("audio/")) {audioExtractor.selectTrack(i);Log.i(LOG_TAG, "aac 找到了通道" + i);break;}}//初始化MiediaCodecdecodeCodec = MediaCodec.createDecoderByType(audioFormat.getString(MediaFormat.KEY_MIME));//数据格式,surface用来渲染解析出来的数据;加密用的对象;标志 encode :1 decode:0decodeCodec.configure(audioFormat, null, null, 0);MediaCodec.BufferInfo decodeBufferInfo = new MediaCodec.BufferInfo();//用于描述解码得到的byte[]数据的相关信息//启动解码decodeCodec.start();/** 同步方式,流程是在while中* dequeueInputBuffer -> queueInputBuffer填充数据 -> dequeueOutputBuffer -> releaseOutputBuffer显示画面*/boolean hasAudio = true;while (true) {//所有的猪都运进猪厂后不再添加if (hasAudio) {//从猪肉工厂获取装猪的小推车,填充数据后发送到猪肉工厂进行处理ByteBuffer[] inputBuffers = decodeCodec.getInputBuffers();//所有的小推车int inputIndex = decodeCodec.dequeueInputBuffer(0);//返回当前可用的小推车标号if (inputIndex != -1) {Log.i(LOG_TAG, "找到了input 小推车" + inputIndex);//将MediaCodec数据取出来放到这个缓冲区里ByteBuffer inputBuffer = inputBuffers[inputIndex];//拿到小推车inputBuffer.clear();//扔出去里面旧的东西//将audioExtractor里面的猪装载到小推车里面int readSize = audioExtractor.readSampleData(inputBuffer, 0);//audioExtractor没猪了,也要告知一下if (readSize < 0) {Log.i(LOG_TAG, "当前音频已经读取完了");decodeCodec.queueInputBuffer(inputIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);hasAudio = false;} else {//拿到猪Log.i(LOG_TAG, "读取到了音频数据,当前音频数据的数据长度为:" + readSize);//告诉工厂这头猪的小推车序号、猪的大小、猪在这群猪里的排行、屠宰的标志decodeCodec.queueInputBuffer(inputIndex, 0, readSize, audioExtractor.getSampleTime(), 0);//读取音频的下一帧audioExtractor.advance();}} else {Log.i(LOG_TAG, "没有可用的input 小推车");}}//工厂已经把猪运进去了,但是是否加工成火腿肠还是未知的,我们要通过装火腿肠的筐来判断是否已经加工完了int outputIndex = decodeCodec.dequeueOutputBuffer(decodeBufferInfo, 0);//返回当前筐的标记switch (outputIndex) {case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:Log.i(LOG_TAG, "输出的format已更改" + decodeCodec.getOutputFormat());break;case MediaCodec.INFO_TRY_AGAIN_LATER:Log.i(LOG_TAG, "超时,没获取到");break;case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:Log.i(LOG_TAG, "输出缓冲区已更改");break;default:Log.i(LOG_TAG, "获取到解码后的数据了,当前解析后的数据长度为:" + decodeBufferInfo.size);//获取所有的筐ByteBuffer[] outputBuffers = decodeCodec.getOutputBuffers();//拿到当前装满火腿肠的筐ByteBuffer outputBuffer;if (Build.VERSION.SDK_INT >= 21) {outputBuffer = decodeCodec.getOutputBuffer(outputIndex);} else {outputBuffer = outputBuffers[outputIndex];}//将火腿肠放到新的容器里,便于后期装车运走byte[] pcmData = new byte[decodeBufferInfo.size];outputBuffer.get(pcmData);//写入到字节数组中outputBuffer.clear();//清空当前筐//装车fos.write(pcmData);//数据写入文件中fos.flush();//把筐放回工厂里面decodeCodec.releaseOutputBuffer(outputIndex, false);break;}if ((decodeBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {Log.i(LOG_TAG, "表示当前编解码已经完事了");break;}}} catch (IOException e) {e.printStackTrace();} finally {if (audioExtractor != null) {audioExtractor.release();}if (decodeCodec != null) {decodeCodec.stop();decodeCodec.release();}if (fos != null) {try {fos.close();} catch (IOException e) {e.printStackTrace();}}}}
}

音视频5.1——MediaCodec 同步方式完成AAC硬解成PCM相关推荐

  1. sync是同步还是非同步_音视频是怎么保持同步的?(四)

    今天我们来继续讲音视频是怎么保持同步的. 常用同步策略 前面已经说了,实现音视频同步,在播放时,需要进行选定一个重要参考时钟,读取帧上的时间戳,同时企业根据的参考时钟来动态管理调节播放.它是目前已知的 ...

  2. Android 音视频编解码 MediaCodec

    MediaCodec 简介 Android中的MediaCodec是一个用于音视频编解码功能的API,使用它可以实现对音视频数据进行压缩.解压缩.编辑和转换.以下是MediaCodec的主要功能: 支 ...

  3. 研究Android音视频-3-在Android设备上采集音视频并使用MediaCodec编码为H.264

    原文 : https://juejin.cn/post/69601302052266311754 本文解决的问题 本文主要使用 MediaCodec 硬编码器对 Android 设备采集的音视频编码 ...

  4. 深入了解Android系统中的音视频编解码器:MediaCodec

    Media内核源码 Media内核是Android系统中负责音视频处理的核心模块,包括音视频采集.编解码.传输.播放等功能.Media内核源码位于Android源码树的/frameworks/av目录 ...

  5. 多媒体库SDL以及实时音视频库WebRTC中的多线程问题实战详解

    目录 1.概述 2.开源跨平台多媒体库SDL介绍 3.开源音视频实时通信库WebRTC介绍 4.在国产化Linux桌面系统中遇到的SDL多线程问题 5.在给WebRTC新增外部音频插件库时遇到的多线程 ...

  6. 【Anychat音视频开发】相对路径与绝对路径详解

    表示文件路径的方式有两种,相对路径和绝对路径.在网页设计中通过路径可以表示链接,插入图像.Flash.CSS文件的位置. 相对路径:以当前文件所在位置为参考点,而建立出的目录路径. 绝对路径:以Web ...

  7. 【学习】从零开发的Android音视频开发(13)——MediaCodec到OMX框架过程及其硬解码

    MediaCodec到OMX框架过程 在讲NuPlayer时,NuPlayer解码部分会创建MediaCodec,并且最终到达OMX框架,先看MediaCodec的init函数 从init函数中可以看 ...

  8. java中实现线程互斥的关键词_简单的互斥同步方式——synchronized关键字详解

    2. synchronized的原理和实现细节 2.1 synchronized可以用在那些地方 静态方法,锁对象为当前类的class对象,不用显式指定 实例方法,锁对象为当前实例对象,不用显式指定 ...

  9. Android音视频【十二】使用opensles和audiotrack进行播放pcm

    人间观察 年龄到了,有些事就妥协了,这个世界上没有人可以随心所欲,生活会逼着你选择答案--最困难的是你什么都改变不了-- 介绍 播放pcm的两种方式 本节我们学习下如何播放pcm数据,在Android ...

  10. [笔记]音视频学习之SDL篇《五》裁剪图片成子图片(裁剪精灵表)

    第五节:裁剪精灵表 文章目录 目的 初始化 创建窗口 载入图片 确认裁剪坐标 确认裁剪显示坐标 确认裁剪大小的圆为100*100 按下1,2,3,4显示不同裁剪图片 总结 ApplySurface S ...

最新文章

  1. Oracle Golden Gate体系架构详解(原创) - CzmMiao的博客生活 - ITeye技术网站
  2. 慎用MySQL replace语句
  3. 修复IE下相对容器中绝对定位Bug
  4. CSS——行内元素的margin与padding
  5. 你可能不知道的shell、bash二三事(Centos 7)
  6. 2020年写字楼行业发展现状及趋势分析
  7. fastapi+tortoise单元测试
  8. 最小二乘法拟合圆心和半径 python实现
  9. 高能手办团服务器维护了,《高能手办团》4月2日例行维护公告
  10. Unity制作圆环进度条加载场景资源
  11. 数值计算大作业:非线性方程求根(二分法、牛顿法、弦截法在Matlab实现)
  12. 嵌入式系统基础及知识及接口技术总结
  13. 如何使营销变得年轻化
  14. VLC接收网络串流缓冲时间的计算
  15. MongoDB 基本操作语句及实例
  16. Photoshop画笔工具的使用
  17. 输入输出管理 用户管理
  18. #详细说明# 解决在Python虚拟化境下,使用pip安装相应开发包 pipfile和pipfile.lock 文件不同步更新的问题
  19. ESP8266 TCP客户端代码(自动连接,断开识别)
  20. Archlinux安装UEFI Grub

热门文章

  1. MathJax 支持的 Latex 符号总结(集合运算)
  2. 快速学习四步法:如何用20小时,快速学习
  3. ubuntu-20.04.3-详细安装教程(图文)附下载地址
  4. Ubuntu | ubuntu下安装edge
  5. 示波器探头各种作用及工作原理,你都理解清楚了吗?
  6. 魔兽世界3D版启动方法 教你做红蓝3D眼镜
  7. APM 页面加载耗时校准
  8. 制作TTF格式的字体
  9. C++17新属性详解
  10. 智能商场管理系统(可当做毕设)