Android提供了一個MediaExtractor類,可以用來分離容器中的視頻track和音頻track,下面的例子展示了使用MediaExtractor和MediaMuxer來實現視頻的換音:
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()設置數據源,數據源可以是本地文件地址,也可以是網絡地址:
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中讀取數據了:
while (true) { int sampleSize = mVideoExtractor.readSampleData(buffer, 0); if (sampleSize < 0) { break; } // do something mVideoExtractor.advance(); // 移動到下一幀 }
在讀取結束之后,記得釋放資源:
mVideoExtractor.release();