前言
在Android
开发过程中,我们偶尔会遇到需要从一个mp4
视频文件中提取出mp3
音频用于单独播放的需求。这里我封装了一个工具类——SplitMediaFile
。此类可用于从一个有声mp4视频文件
中提取出无声mp4视频文件
和mp3音频
文件。下面就来学习下它的使用吧
今天涉及内容:
- 视频分离基础
- 用户权限
- SplitMediaFile方法简介
- SplitMediaFile 在 Activity 中的使用
- 效果图和项目结构图
- SplitMediaFile 源码
来波生成的文件图

一. 视频分离基础
1.1 MediaExtractor
MediaExtractor
可用于分离视频文件的音轨和视频轨道。用selectTrack
方法选中 视频/音频
轨道,然后用readSampleData
读出数据,就可以得到一个没有声音的视频或者一个音频。
1.2 MediaFormat
MediaFormat
封装了Media data
的描述信息,通过描述信息可以分辨Meida data
是一个音频还是视频。描述信息是一个键值对,可以通过MediaFormat
的getXX
函数获取。
通过String mime = mediaFormat.getString(MediaFormat.KEY_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
内常量。
1.3 MediaMuxer
MediaMuxer
主要用于对 视频/音频
的封装。其主要有以下几个方法:
//添加一个视频或音频轨道
int addTrack(@NonNull MediaFormat format)
//开始写入数据(此方法调用后,无法再调用`addTrack`)
start()
//向Muxer写入编码后的音视频数据
writeSampleData(int trackIndex, @NonNull ByteBuffer byteBuf,
@NonNull BufferInfo bufferInfo)
//停止写入数据,并生成文件
stop()
//释放Muxer资源
release()
1.4 需要注意的问题
MediaExtractor
分离出的文件根据类型不同,可能生成的文件为.aac
格式的音频,或者是.h264
格式的视频文件或者其他的格式,当生成的.aac
还涉及到是否添加adts
字节头的问题(adts
字节头添加的话,大家可以自行搜索),由于我加了后生成的.aac
文件仍没法播放,所以我采用MediaMuxer
对分离出的音频/视频
再度处理,最后生成了可直接播放的.mp4
和.mp3
文件。
二.用户权限
涉及到文件读写问题,先是要在你项目的androidmainfast.xml
中添加读写权限,如下:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
然后需要添加手动权限库,这里我使用的是PermissionsDispatcher
,手动库的添加大家可以参考以下文章:
PermissionsDispatcher动态权限申请kotlin版
接着需要添加FileProvider
相关处理,大家可参考以下文章
SpUtil多样加密存储,兼容android9.0
三. SplitMediaFile方法简介
SplitMediaFile
作为一个视频分离帮助类
,拥有以下方法:
//初始化
SplitMediaFile()
/****
* 分离视频的方法
*
* @param sourceFilePath 要分离成音频和无声视频的视频源文件地址
* 路径中含文件后缀。
*
* @param videoFilePath 要存储的无声视频文件地址,为 null 表示不分离出无声视频文件
* 路径中不含文件后缀。(生成新的视频文件格式为 MP4)
*
* @param audioFilePath 要存储的音频文件地址,为 null 表示不分离出音频文件
* 路径中不含文件后缀。(生成新的视频文件格式为 MP3)
*/
public void split(String sourceFilePath,String videoFilePath,String audioFilePath)
需要在注意的是,split(String sourceFilePath,String videoFilePath,String audioFilePath)
方法再生成 音/视 频
的调用如下:
//仅分离出音频(mp3格式)
mSplitMediaFile.split(path,null,audioFilePath);
//仅分离出视频(无声MP4)
mSplitMediaFile.split(path,videoFilePath,null);
//分离出视频和音频
mSplitMediaFile.split(path,videoFilePath,audioFilePath);
四.SplitMediaFile 在 Activity 中的使用
SplitMediaFile
在 TempActivity
中的使用代码如下:
public class TempActivity extends AppCompatActivity {
private TextView mTvTest;
private Button mBtnTest;
private Button mBtnTest2;
private VideoView mVideoView;
private SplitMediaFile mSplitMediaFile;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_temp);
//初始化控件
initView();
//初始化数据
initData();
//控件监听
setListener();
}
/**
* 初始化控件
**/
private void initView() {
mTvTest = findViewById(R.id.mTvTest);
mBtnTest = findViewById(R.id.mBtnTest);
mBtnTest2 = findViewById(R.id.mBtnTest2);
mVideoView = findViewById(R.id.mVideoView);
}
private void initData() {
mSplitMediaFile=new SplitMediaFile();
}
/**
* 控件监听
**/
private void setListener() {
//播放视频
mBtnTest.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
//分离 视频/音频
mBtnTest2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String path="/data/user/0/com.testdemo/cache/test.mp4";
LogUtil.i("========path="+path);
//设置分离文件路径
String tempPath=path.substring(0,path.lastIndexOf("/")+1);
//视频文件路径
String videoFilePath=tempPath+"test_video";
//音频文件路径
String audioFilePath=tempPath+"test_audio";
// //仅分离出音频(mp3格式)
// mSplitMediaFile.split(path,null,audioFilePath);
// //仅分离出视频(无声MP4)
// mSplitMediaFile.split(path,videoFilePath,null);
//分离出视频和音频
mSplitMediaFile.split(path,videoFilePath,audioFilePath);
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
}
}
五.效果图和项目结构图


测试使用视频地址如下(当然,大家也可以自己下一个mp4
文件测试):
测试视频
六.SplitMediaFile 源码
下面贴出SplitMediaFile
源码:
网友评论