Android提供了一個MediaExtractor類,可以用來分離容器中的視頻track和音頻track,下面的例子展示了使用MediaExtractor和MediaMuxer來實現(xiàn)視頻的換音:
private void muxingAudioAndVideo() throws IOException {
MediaMuxer mMediaMuxer = new MediaMuxer(mOutputVideoPath,
MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
// 視頻的MediaExtractor
MediaExtractor mVideoExtractor = new MediaExtractor();
mVideoExtractor.setDataSource(mVideoPath);
int videoTrackIndex = -1;
for (int i = 0; i < mVideoExtractor.getTrackCount(); i++) {
MediaFormat format = mVideoExtractor.getTrackFormat(i);
if (format.getString(MediaFormat.KEY_MIME).startsWith("video/")) {
mVideoExtractor.selectTrack(i);
videoTrackIndex = mMediaMuxer.addTrack(format);
break;
}
}
// 音頻的MediaExtractor
MediaExtractor mAudioExtractor = new MediaExtractor();
mAudioExtractor.setDataSource(mAudioPath);
int audioTrackIndex = -1;
for (int i = 0; i < mAudioExtractor.getTrackCount(); i++) {
MediaFormat format = mAudioExtractor.getTrackFormat(i);
if (format.getString(MediaFormat.KEY_MIME).startsWith("audio/")) {
mAudioExtractor.selectTrack(i);
audioTrackIndex = mMediaMuxer.addTrack(format);
}
}
// 添加完所有軌道后start
mMediaMuxer.start();
// 封裝視頻track
if (-1 != videoTrackIndex) {
MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
info.presentationTimeUs = 0;
ByteBuffer buffer = ByteBuffer.allocate(100 * 1024);
while (true) {
int sampleSize = mVideoExtractor.readSampleData(buffer, 0);
if (sampleSize < 0) {
break;
}
info.offset = 0;
info.size = sampleSize;
info.flags = MediaCodec.BUFFER_FLAG_SYNC_FRAME;
info.presentationTimeUs = mVideoExtractor.getSampleTime();
mMediaMuxer.writeSampleData(videoTrackIndex, buffer, info);
mVideoExtractor.advance();
}
}
// 封裝音頻track
if (-1 != audioTrackIndex) {
MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
info.presentationTimeUs = 0;
ByteBuffer buffer = ByteBuffer.allocate(100 * 1024);
while (true) {
int sampleSize = mAudioExtractor.readSampleData(buffer, 0);
if (sampleSize < 0) {
break;
}
info.offset = 0;
info.size = sampleSize;
info.flags = MediaCodec.BUFFER_FLAG_SYNC_FRAME;
info.presentationTimeUs = mAudioExtractor.getSampleTime();
mMediaMuxer.writeSampleData(audioTrackIndex, buffer, info);
mAudioExtractor.advance();
}
}
// 釋放MediaExtractor
mVideoExtractor.release();
mAudioExtractor.release();
// 釋放MediaMuxer
mMediaMuxer.stop();
mMediaMuxer.release();
}
MediaExtractor的接口比較簡單,首先通過setDataSource()設(shè)置數(shù)據(jù)源,數(shù)據(jù)源可以是本地文件地址,也可以是網(wǎng)絡(luò)地址:
MediaExtractor mVideoExtractor = new MediaExtractor();
mVideoExtractor.setDataSource(mVideoPath);
然后可以通過getTrackFormat(int index)來獲取各個track的MediaFormat,通過MediaFormat來獲取track的詳細信息,如:MimeType、分辨率、采樣頻率、幀率等等:
for (int i = 0; i < mVideoExtractor.getTrackCount(); i++) {
MediaFormat format = mVideoExtractor.getTrackFormat(i);
}
獲取到track的詳細信息后,通過selectTrack(int index)選擇指定的通道:
if (format.getString(MediaFormat.KEY_MIME).startsWith("video/")) {
mVideoExtractor.selectTrack(i);
break;
}
指定通道之后就可以從MediaExtractor中讀取數(shù)據(jù)了:
while (true) {
int sampleSize = mVideoExtractor.readSampleData(buffer, 0);
if (sampleSize < 0) {
break;
}
// do something
mVideoExtractor.advance(); // 移動到下一幀
}
在讀取結(jié)束之后,記得釋放資源:
mVideoExtractor.release();