Android音频学习之MediaExtractor,提取音频视频轨道数据(从视频中分离音频视频数据)
一个音视频文件是由音频和视频组成的,我们可以通过MediaExtractor、MediaMuxer把音频或视频给单独抽取出来,抽取出来的音频和视频能单独播放;
1 MediaExtractor 说明
MediaExtractor从api16开始添加,可用于分离视频文件的音轨和视频轨道,如果你只想要视频,那么用selectTrack方法选中视频轨道,然后用readSampleData读出数据,这样你就得到了一个没有声音的视频,想得到音频也可以用同样的方法。
MediaExtractor构造函数:
MediaExtractor()
MediaExtractor 主要方法说明:
设置数据源:
setDataSource(AssetFileDescriptor afd)
setDataSource(Context context, Uri uri, Map<String, String> headers)
setDataSource(FileDescriptor fd)
setDataSource(MediaDataSource dataSource)
setDataSource(FileDescriptor fd, long offset, long length)
void setDataSource(String path)//既可以是文件路径,也可以是文件url网址。
void setDataSource(String path, Map<String, String> headers)//既可以是文件路径,也可以是文件url网址。
获取源文件轨道数(包括视频和音频)
getTrackCount()
获取某个轨道的MediaFormat:
MediaFormat getTrackFormat(int index),MediaFormat包含该轨道的很多配置信息。
选定轨道:
selectTrack(int index):选定特定的轨道,会影响 readSampleData(ByteBuffer, int), getSampleTrackIndex() and getSampleTime()的输出,这三个函数输出的是选定轨道的信息。
读取信息:
readSampleData(ByteBuffer byteBuf, int offset):读取数据到bytebuffer中,从offset偏移开始。选定轨道之后可以读取该轨道的数据。
获取下一帧数据:
advance()获取下一帧数据
释放资源:
release()释放资源。
2 MediaFormat
MediaFormat封装了Media data的描述信息,通过描述信息可以分辨Meida data 是一个音频还是视频。描述信息是一个键值对,可以通过MediaFormat的getXX函数获取。
MediaFormat 可以通过MediaExtractor 的getTrackFormat(Index) 获取,也可以通过构造函数自己构造。
MediaFormat构造函数:
MediaFormat()
createAudioFormat(String mime, int sampleRate, int channelCount)
创建一个音频的MediaFormat
参数说明:
- mime:文件类型
- sampleRate:采样率
- channelCount:声音轨道数
Mime文件类型:
常见的MIME类型多媒体格式如下(以audio开头的是音频,以video开头的是视频):
- “video/x-vnd.on2.vp8” - VP8 video (i.e. video in .webm)
- “video/x-vnd.on2.vp9” - VP9 video (i.e. video in .webm)
- “video/avc” - H.264/AVC video
- “video/mp4v-es” - MPEG4 video
- “video/3gpp” - H.263 video
- “audio/3gpp” - AMR narrowband audio
- “audio/amr-wb” - AMR wideband audio
- “audio/mpeg” - MPEG1/2 audio layer III
- “audio/mp4a-latm” - AAC audio (note, this is raw AAC packets, not packaged in LATM!)
- “audio/vorbis” - vorbis audio
- “audio/g711-alaw” - G.711 alaw audio
- “audio/g711-mlaw” - G.711 ulaw audio
- 。。。还有很多格式请参考MediaFormat中的格式常量
字幕Track格式:
MIMETYPE_TEXT_VTT = “text/vtt”;
MIMETYPE_TEXT_CEA_608 = “text/cea-608”;
MIMETYPE_TEXT_CEA_708 = “text/cea-708”;
MediaFormat中mime有对应的常量,MIMETYPE_AUDIO_AAC对应audio/mp4a-latm,MIMETYPE_VIDEO_AVC对应 "video/avc"等等,其他对应参考MediaFormat内常量。
至于这些类型对应什么类型的文件这里举几个例子:mp3为audio/mpeg;aac为audio/mp4a-latm;mp4为video/mp4v-es 。
createVideoFormat(String mime, int width, int height)
创建一个视频的MediaFormat,mime含义和上面相同,width,height含义为视频宽高。
MediaExtractor的getTrackFormat也可以获取MediaFormat。
通过key值获取信息的getXXX函数:
getFloat(String name)
getInteger(String name)
getLong(String name)
getString(String name)
利用MediaFormat的key常量获取值。
常见的key值包括:
KEY_MIME,KEY_CHANNEL_COUNT,KEY_SAMPLE_RATE,KEY_DURATION,KEY_WIDTH,KEY_HEIGHT等。
MediaExtractor基本用法(来自Developers):
MediaExtractor extractor = new MediaExtractor();extractor.setDataSource(...);int numTracks = extractor.getTrackCount();for (int i = 0; i < numTracks; ++i) {MediaFormat format = extractor.getTrackFormat(i);String mime = format.getString(MediaFormat.KEY_MIME);if (weAreInterestedInThisTrack) {extractor.selectTrack(i);}}ByteBuffer inputBuffer = ByteBuffer.allocate(...)while (extractor.readSampleData(inputBuffer, ...) >= 0) {int trackIndex = extractor.getSampleTrackIndex();long presentationTimeUs = extractor.getSampleTime();...extractor.advance();}extractor.release();extractor = null;
3 从mp4文件中获取信息并提取音频文件和视频文件
从mp4文件中提取音频和视频轨道的数据,得到aac音频数据和.h264视频数据。
代码示例:为测试代码,只展示使用过程,要使用需要优化
//提取的文件为什么是aac后面会说明
private String pcmPath = Environment.getExternalStorageDirectory().getPath() + "/demo/test.aac";//为什么是.h264 后面会说明
private String mp4Path = Environment.getExternalStorageDirectory().getPath() + "/demo/test.h264";
private String dirPath = Environment.getExternalStorageDirectory().getPath() + "/demo";private MediaExtractor mediaExtractor;private void initMediaDecode() {String srcPath = Environment.getExternalStorageDirectory().getPath() + "/video/video.mp4";File file = new File(dirPath);if (!file.exists()){file.mkdir();}File file1 = new File(pcmPath);File file2 = new File(mp4Path);try {if (file1.exists()){file1.delete();}file1.createNewFile();} catch (IOException e) {e.printStackTrace();}try {if (file2.exists()){file2.delete();}file2.createNewFile();} catch (IOException e) {e.printStackTrace();}try {mediaExtractor = new MediaExtractor();//此类可分离视频文件的音轨和视频轨道mediaExtractor.setDataSource(srcPath);//媒体文件的位置System.out.println("==========getTrackCount()===================" + mediaExtractor.getTrackCount());for (int i = 0; i < mediaExtractor.getTrackCount(); i++) {//遍历媒体轨道,包括视频和音频轨道MediaFormat format = mediaExtractor.getTrackFormat(i);String mime = format.getString(MediaFormat.KEY_MIME);if (mime.startsWith("audio")) {//获取音频轨道mediaExtractor.selectTrack(i);//选择此音频轨道System.out.println("====audio=====KEY_MIME========="+format.getString(MediaFormat.KEY_MIME)); System.out.println("====audio=====KEY_CHANNEL_COUNT======="+format.getInteger(MediaFormat.KEY_CHANNEL_COUNT)+""); System.out.println("====audio=====KEY_SAMPLE_RATE==========="+format.getInteger(MediaFormat.KEY_SAMPLE_RATE)+"");
System.out.println("====audio=====KEY_DURATION==========="+format.getLong(MediaFormat.KEY_DURATION)+"");System.out.println("====audio=====getSampleFlags==========="+mediaExtractor.getSampleFlags()+"");
System.out.println("====audio=====getSampleTime==========="+mediaExtractor.getSampleTime()+"");
// System.out.println("====audio=====getSampleSize==========="+mediaExtractor.getSampleSize()+"");api28System.out.println("====audio=====getSampleTrackIndex==========="+ mediaExtractor.getSampleTrackIndex()+"");try {ByteBuffer inputBuffer = ByteBuffer.allocate(100 * 1024);FileOutputStream fe=new FileOutputStream(file1,true);while ( true) {int readSampleCount = mediaExtractor.readSampleData(inputBuffer, 0);if (readSampleCount < 0) {break;}byte[] buffer = new byte[readSampleCount];inputBuffer.get(buffer);fe.write(buffer);inputBuffer.clear();mediaExtractor.advance();}fe.flush();fe.close();} catch (IOException e) {e.printStackTrace();}finally {}}if (mime.startsWith("video")){mediaExtractor.selectTrack(i);//选择此视频轨道
System.out.println("====video=====KEY_MIME==========="+format.getString(MediaFormat.KEY_MIME)); System.out.println("====video=====KEY_DURATION==========="+format.getLong(MediaFormat.KEY_DURATION)+"");System.out.println("====video=====KEY_WIDTH==========="+format.getInteger(MediaFormat.KEY_WIDTH)+""); System.out.println("====video=====KEY_HEIGHT==========="+format.getInteger(MediaFormat.KEY_HEIGHT)+"");System.out.println("====video=====getSampleFlags==========="+mediaExtractor.getSampleFlags()+"");System.out.println("====video=====getSampleTime==========="+mediaExtractor.getSampleTime()+"");
// System.out.println("====video=====getSampleSize==========="+mediaExtractor.getSampleSize()+"");api28
System.out.println("====video=====getSampleTrackIndex==========="+ mediaExtractor.getSampleTrackIndex()+"");try {ByteBuffer inputBuffer = ByteBuffer.allocate(100 * 1024);FileOutputStream fe=new FileOutputStream(file2,true);while ( true) {int readSampleCount = mediaExtractor.readSampleData(inputBuffer, 0);if (readSampleCount < 0) {break;}byte[] buffer = new byte[readSampleCount];inputBuffer.get(buffer);fe.write(buffer);inputBuffer.clear();mediaExtractor.advance();}fe.flush();fe.close();} catch (IOException e) {e.printStackTrace();}finally {}}}} catch (IOException e) {e.printStackTrace();}finally {mediaExtractor.release();mediaExtractor = null;}
}
结果:
获取到的视频轨道信息:
getTrackCount()=2
video=KEY_MIME=video/avc
video=KEY_DURATION=52208333
video=KEY_WIDTH=854
video=KEY_HEIGHT=480
video=getSampleFlags=1
video=getSampleTime=83333
video=getSampleTrackIndex===0
获取到的音频轨道信息:
audio=KEY_MIME=audio/mp4a-latm
audio=KEY_CHANNEL_COUNT=2
audio=KEY_SAMPLE_RATE=48000
audio=KEY_DURATION=51946666
audio=getSampleFlags=1
audio=getSampleTime=0
audio=getSampleTrackIndex===========1
由于获取到的视频轨道的mime为video/avc,所以知道视频编码格式为.h264,获取到的音频轨道的mime为audio/mp4a-latm,对应的音频编码格式为aac。
上面虽然获取了视频,音频数据,但是要怎么编码,解码为常用格式呢?
代码中用到的video.mp4文件,在源码中随意找的。https://pan.baidu.com/s/1ANs0DacuTMZgnTfjKoWjtg
4 aac 和ADTS
AAC(Advanced Audio Coding)高级音频编码,有基于MPEG-2和MPEG-4标准两种音频编码技术。aac音频格式有两种编码方式ADIF和ADTS,两者之间的区别是ADIF在所有aac数据的开始添加一个ADIF头,可以确定音频数据的开始,aac文件有且只有这一个头信息。ADTS则把aac数据分成多个es帧,在每一帧的前面添加一个ADTS头信息,所以adts可以任意帧解码,因为每一帧都有头部信息。
上面通过MediaExtractor分离MP4文件得到的aac音频文件是没有添加adts头的原数据,所以保存的aac文件是没法播放的,只有添加了adts头才能播放。
合成的时候,MP4,flv,rtmp都不需要adts头,hls,rtp,ts是需要adts头,所以这里分离出来的aac是没有adts头信息的。想要能够播放需要手动添加adts头。
Android音频学习之MediaExtractor,提取音频视频轨道数据(从视频中分离音频视频数据)相关推荐
- Android视频编辑器(五)音频编解码、从视频中分离音频、音频混音、音频音量调节等
前言 这篇博客,主要讲解的是android端的音频处理,在开发Android视频编辑器的时候,有一个非常重要的点就是音频的相关处理.比如如何从视频中分离音频(保存为mp3文件),然后分离出来的音频如何 ...
- 如何从视频中分离音频/提取音频/提取视频
我已经做成了在线服务,免费使用:https://www.raoyunsoft.com/videotools/split_video 要想分离音频,提取音频,或者提取视频,那么不得不推荐FFmpeg,F ...
- 剪辑视频,按分秒分割多个视频,并保存片断中的音频
如何剪辑视频,多个视频需要分割时,如何快速分割多个视频呢?并保留每个分割出来片段的音频,下面一起看批量剪辑分割的技巧.来看操作步骤吧,希望以后可以用到. 准备哪些工具: 下载一个媒体梦工厂 多个视频 ...
- ios上传音频文件到服务器,IOS开发:iPod的音乐库中的音频如何上传到服务器中...
最近在做的项目里有一个功能,就是拿到手机媒体库中的音频文件,并实现APP中的播放,已经转成MP3格式上传到服务器上. 首先是要能获取到ipod library中的音频.这里我用的是MPMediaQue ...
- 浏览器中的音视频知识总结(工作中需要和视频打交道必看!)
前端瓶子君,关注公众号 回复算法,加入前端编程面试算法每日一题群 视频是什么 视频,其实就是一系列连续播放的图片,如果1s钟播放24张图片,那么人眼看到的就不再是一张张独立的图片,而是动起来的画面.其 ...
- 音视频从入门到精通——FFmpeg分离出PCM数据实战
什么是PCM? PCM(Pulse Code Modulation,脉冲编码调制)音频数据是未经压缩的音频采样数据裸流,它是由模拟信号经过采样.量化.编码转换成的标准数字音频数据. 描述PCM数据的6 ...
- html怎么导入flash视频,DW如何在网页中插入Flash视频?
在 Dreamweaver 的"文档"窗口中打开 index.html 页面,插入一个三列的表格,在由三列组成的表格的中间一列中放置的图形之上单击一次. 选择"插入&qu ...
- idea中连接mysql插入成功数据 在navicat中刷新表格没有数据_第九篇 数据分析的进阶学习-SQL入门...
1.SQL的学习阶段 1.1 背景 随着互联网的不断发展,数据的不断累积,现在单单使用Excel已经不能满足数据分析的需求了,因此作为一个数据分析工作者,掌握Excel是基础,而SQL作为主流数据库查 ...
- 计算机主机视频介绍,如何在win10中查看计算机视频内存win10中的计算机视频内存简介...
我相信许多用户都知道计算机图形卡的大小决定了图形卡的处理能力. 当然,显存越大,图形卡的处理能力越强. 最近,win10系统用户希望在使用计算机时查看计算机的视频内存.win10怎么看电脑显存,但是我 ...
最新文章
- saltstack(十二)job管理
- vmware horizon view 添加vcenter server
- 导入python库linux_Linux下Python安装PyMySQL成功,但无法导入的问题
- Python中比元组更好用的namedtuple
- JS实现上下左右四方向无间隙滚动
- Tomcat 打开一闪而过
- 剑指Offer之复杂链表的复制
- 删除或添加最大化、最小化按钮 - 回复 Tommy the CAT 的问题
- python IMAP4命令详解
- The 'mode' option has not been set, webpack will fallback to 'production' for th is value
- EasyUI【DataGrid数据查询】
- SpringBoot application.properties读取属性配置文件中文显示为乱码问题的解决
- Response总结
- php单链表检测有没有环,PHP找出链表中环入口节点步骤详解
- 生日悖论MATLAB仿真
- 学NLP的人跑去CVPR投稿!中了顶会一作,还是一位本科生...
- 老子、王阳明、曾国藩教你怎样做人做事!
- 开发新闻类APP需要准备什么
- Tomcat Ajp(CVE-2020-1938) 漏洞复现与修复
- springboot中药知识分享网站设计 毕业设计-附源码201818