序言
一個音視頻文件是由音頻和視頻組成的,Android 提供了 MediaExtractor 和 MediaMuxer 類,用來把音頻或視頻單獨抽取出來,然後合成新的視頻。
我們分別看一下 API 的使用,實現分離 MP4 視頻文件,然後再合成分離的音視頻功能。
MediaExtractor 的使用主要有這麼幾步:
- 設置數據源
- 獲取通道數,找到想要的軌道和通道格式,並切換過去
- 循環讀取每幀的樣本數據
- 完成後釋放資源
MediaMuxer 的使用和 MediaExtractor 類似:
- 設置目標文件路徑和音視頻格式
- 添加感興趣的通道
- 開始合成,循環寫入樣本數據
- 完成後釋放資源
下面是個提取併合成視頻的示例,MediaExtractor 和 MediaMuxer 一起使用,相當於把原視頻的聲音通道去掉,生成一個無聲的視頻文件。
// 分離視頻的純視頻 輸入視頻 input.mp4,分離保存的視頻 output_video.mp4
private void extractVideo() {
MediaExtractor mediaExtractor = new MediaExtractor();
MediaMuxer mediaMuxer = null;
try {
File fileDir = FileUtils.getFileDir(this);
// 設置視頻源
mediaExtractor.setDataSource(new File(fileDir, VIDEO_SOURCE_PATH).getAbsolutePath());
int videoIndex = -1;
int maxInputSize = -1;
int frameRate = -1;
// 獲取數據源的軌道數
int trackCount = mediaExtractor.getTrackCount();
// 循環軌道數,找到我們想要的視頻軌
for (int i = 0; i < trackCount; i++) {
MediaFormat trackFormat = mediaExtractor.getTrackFormat(i);
// 主要描述mime類型的媒體格式
String mimeType = trackFormat.getString(MediaFormat.KEY_MIME);
// //找到視頻軌
if (mimeType.startsWith("video/")) {
videoIndex = i;
// 獲取視頻最大的輸入大小
maxInputSize = trackFormat.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE);
// 獲取視頻的幀率
frameRate = trackFormat.getInteger(MediaFormat.KEY_FRAME_RATE);
}
}
logger.info("frameRate:{}, maxInputSize:{}", frameRate, maxInputSize);
//切換視頻的信道
mediaExtractor.selectTrack(videoIndex);
MediaFormat trackFormat = mediaExtractor.getTrackFormat(videoIndex);
mediaMuxer = new MediaMuxer(new File(fileDir, OUTPUT_VIDEO).getAbsolutePath(), MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
//將視頻軌添加到 MediaMuxer,並返回新的軌道
int trackIndex = mediaMuxer.addTrack(trackFormat);
ByteBuffer byteBuffer = ByteBuffer.allocate(maxInputSize);
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
// 開始合成
mediaMuxer.start();
while (true) {
// 檢索當前編碼的樣本並將其存儲在字節緩衝區中
int readSampleSize = mediaExtractor.readSampleData(byteBuffer, 0);
// 如果沒有可獲取的樣本則退出循環
if (readSampleSize < 0) {
mediaExtractor.unselectTrack(videoIndex);
break;
}
// 設置樣本編碼信息
bufferInfo.size = readSampleSize;
bufferInfo.offset = 0;
bufferInfo.flags = mediaExtractor.getSampleFlags();
bufferInfo.presentationTimeUs += 1000 * 1000 / frameRate;
//寫入樣本數據
mediaMuxer.writeSampleData(trackIndex, byteBuffer, bufferInfo);
//推進到下一個樣本,類似快進
mediaExtractor.advance();
}
logger.info("finish extract video");
} catch (IOException e) {
logger.error(e);
} finally {
if (mediaMuxer != null) {
mediaMuxer.stop();
mediaMuxer.release();
}
mediaExtractor.release();
}
}
同樣地,分離音頻和合成音視頻的過程和上面差不多,代碼在 GitHub 上,歡迎各位參閱。
【附錄】
需要資料的朋友可以加入Android架構交流QQ羣聊:513088520
點擊鏈接加入羣聊【Android移動架構總羣】:加入羣聊
獲取免費學習視頻,學習大綱另外還有像高級UI、性能優化、架構師課程、NDK、混合式開發(ReactNative+Weex)等Android高階開發資料免費分享。