本文代码示例采用的Kotlin语法,但是没什么高级特性 MediaExtractor:视频文件的提取器,能将视频和音频分离 MediaMuxer:音视频文件合成器,能将提取到的视频和音频合成新的视频 MediaFormat:提取器获取到的媒体格式类,保存了获取到的媒体的信息(媒体类型,帧率等)1。设置需要权限 一。如果是本地文件,会涉及到读取和写入,需要在Manifest配置 6。0以上要在代码中动态申请,这里就省略了2。获取需要的视频文件 本文是直接通过contentResolver查询多媒体文件valcursorcontentResolver。query( MediaStore。Video。Media。EXTERNALCONTENTURI, null,null,null,null ) 查询到的数据存储到了自建的Song类中,存了文件名(name)和路径(path),然后用ListView展示, 为了省事,直接使用的就是点击的文件和相邻下一个文件做为提取视频和音频的原材料如果不想麻烦去找文件,也可以直接把视频文件放到resraw文件夹下,raw要自己创建然后获取文件,MediaExtractor。setDataSource支持很多方式填充 【更多音视频学习资料,点击下方链接免费领取,先码住不迷路】 点击领取音视频开发基础知识和资料包3。提取音视频 配置音视频提取器 paramposition点击的文件下标 funconfigureVideoAndAudioExtractor(position:Int){ try{ 1。设置要提取视频的文件 MediaExtractor反复提示初始化失败 1。检查文件访问权限 2。检查视频文件大小是否大于0!!!!!!!!!!!!!!! 3。最好不要用拼接路径,比如 mVideoMediaExtractor。setDataSource(context。getFilesDir()xxx。mp4) 最好添加路径变量或完整路径地址,比如下面的变量或 mntsdcard。。。。。。xxx。mp4这种的 初始化解析器和合成器对象,合成输入的格式是mp4, outputVideoPath是合成后输出的路径,自己构建就好 我的是fileDirvideo。mp4 mMediaMuxerMediaMuxer(outputVideoPath, MediaMuxer。OutputFormat。MUXEROUTPUTMPEG4) mVideoMediaExtractorMediaExtractor() mAudioMediaExtractorMediaExtractor() songList是一个集合,存着Song类, 设置要提取出视频的原材料文件 mVideoMediaExtractor。setDataSource(songList〔position〕。path) 设置要提取出音频的文件 mAudioMediaExtractor。setDataSource(songList〔position〕。path) 获取轨道,找到视频轨道 for(iin0untilmVideoMediaExtractor。trackCount){ valmediaFormatmVideoMediaExtractor。getTrackFormat(i) if(mediaFormat。getString(MediaFormat。KEYMIME)。startsWith(video)){ 获取到视频轨道 mVideoMediaExtractor。selectTrack(i) 获取添加到Muxer后生成的新的视频轨道下标 videoMuxerTrackIndexmMediaMuxer。addTrack(mediaFormat) 获取视频帧最大值,为了后面合成新视频,读取文件时候设定缓冲区大小 maxFrameSizemediaFormat。getInteger(MediaFormat。KEYMAXINPUTSIZE) 获取视频帧率,为了后面计算获取到的文件处于的播放时间 frameRatemediaFormat。getInteger(MediaFormat。KEYFRAMERATE) } } 找到视频文件中的音频轨道,方法和获取视频差不多 for(jin0untilmAudioMediaExtractor。trackCount){ valmediaFormatmAudioMediaExtractor。getTrackFormat(j) if(mediaFormat。getString(MediaFormat。KEYMIME)。startsWith(audio)){ 获取音轨 mAudioMediaExtractor。selectTrack(j) 添加音轨到Muxer audioMuxerTrackIndexmMediaMuxer。addTrack(mediaFormat) 获取音频最大输入,为了计算缓冲区大小 maxAudioSizemediaFormat。getInteger(MediaFormat。KEYMAXINPUTSIZE) } } 这个方法内执行MediaMuxer的合成操作,下面会贴出来 compoundVideoAndAudioWithPermissionCheck() }catch(e:IOException){ Log。i(exception,e。message) }finally{ 释放资源,切记最后要是放资源,重新添加合成文件时候要新建MediaExtractor和MediaMuxer if(mMediaMuxer!null){ mMediaMuxer。release() } if(mVideoMediaExtractor!null){ mVideoMediaExtractor。release() } if(mAudioMediaExtractor!null){ mAudioMediaExtractor。release() } } } 总结一下步骤就是 1。创建MediaExtractor和MediaMuxer对象 2。为MediaExtractor对象添加需要的文件setDataSource() 3。for循环获取视频轨道和音频轨道MediaExtractorselectTrack(),并添加到MediaMuxer中MediaMuxeraddTrack() 4。开始准备合并4。合成音视频 合成视频和音频 funcompoundVideoAndAudio(){ 1。开始合成 mMediaMuxer。start() 2。输入提取到的视频,videoMuxerTrackIndex是之前addTrack生成的下标, 如果为1就是添加失败 if(1!videoMuxerTrackIndex){ 描述缓冲区数据信息类,最后Muxer合成要求有的东西 valvideoBufferInfoMediaCodec。BufferInfo() 创建缓冲区,最后Muxer合成要求有的东西 valvideoByteBufferByteBuffer。allocate(maxFrameSize) while(true){ 3。获取样本大小 valvideoSampleSizemVideoMediaExtractor。readSampleData(videoByteBuffer,0) if(videoSampleSize0){ break } 4。设置样本信息 videoBufferInfo。offset0堆buffer缓冲区写入时的字节偏移 videoBufferInfo。sizevideoSampleSize videoBufferInfo。flagsmVideoMediaExtractor。sampleFlags 读取到的文件的时间戳,单位是微秒 videoBufferInfo。presentationTimeUs10001000frameRate每次加每帧的微秒数 MediaMuxer写入样本数据 videoMuxerTrackIndex之前加入Muxer的视频轨下标 videoByteBuffer上面创建的Buffer对象 videoBufferInfo上面创建的BufferInfo对象 mMediaMuxer。writeSampleData(videoMuxerTrackIndex,videoByteBuffer,videoBufferInfo) 5。推进到下个样本类似快进 mVideoMediaExtractor。advance() } } 合成音频,和视频类似 if(1!audioMuxerTrackIndex){ valaudioBufferInfoMediaCodec。BufferInfo() valaudioByteBufferByteBuffer。allocate(maxAudioSize) while(true){ valaudioSampleSizemAudioMediaExtractor。readSampleData(audioByteBuffer,0) if(audioSampleSize0){ break } audioBufferInfo。offset0 audioBufferInfo。sizeaudioSampleSize audioBufferInfo。flagsmAudioMediaExtractor。sampleFlags audioBufferInfo。presentationTimeUs10001000frameRate mMediaMuxer。writeSampleData(audioMuxerTrackIndex,audioByteBuffer,audioBufferInfo) mAudioMediaExtractor。advance() } } } } } 这个方法是在之前的方法内调用,所以try。。catch写在上面的方法中,这些步骤都可以在子线程中去做,最后可以通过系统的VideoView控件来播放outputVideoPath路径的视频,查看是否合成成功 【更多音视频学习资料,点击下方链接免费领取,先码住不迷路】 点击领取音视频开发基础知识和资料包总结流程创建Buffer对象和MediaCodecBufferInfo对象读取样本数据,验证数据大小填写bufferInfo的信息将缓冲区内容写入Muxer调用MediaExtractoradvance(),推进到下个样本数据,循环步骤25释放资源 需要注意的地方!!!!!!!!!!!!!!! 1。权限获取 2。验证要提取的文件是否合法,比如大小,本人遇到MediaExtractor。setDataSource()一直无法初始化MediaExtractor,结果发现添加的视频大小为0KB 3。时间戳会影响合成后效果,需要注意计算 如果你对音视频开发感兴趣,觉得文章对您有帮助,别忘了点赞、收藏哦!或者对本文的一些阐述有自己的看法,有任何问题,欢迎在下方评论区讨论!