一個音視頻文件是由音頻和視頻組成的,我們可以通過MediaExtractor、MediaMuxer把音頻或視頻給單獨抽取出來,抽取出來的音頻和視頻能單獨播放;
一、MediaExtractor API介紹
MediaExtractor的作用是把音頻和視頻的數據進行分離。
主要API介紹:
- setDataSource(String path):即可以設置本地文件又可以設置網絡文件
- getTrackCount():得到源文件通道數
- getTrackFormat(int index):獲取指定(index)的通道格式
- getSampleTime():返回當前的時間戳
- readSampleData(ByteBuffer byteBuf, int offset):把指定通道中的數據按偏移量讀取到ByteBuffer中;
- advance():讀取下一幀數據
- release(): 讀取結束后釋放資源
使用示例:
MediaExtractor extractor = new MediaExtractor(); extractor.setDataSource(...); int numTracks = extractor.getTrackCount(); for (int i = 0; i < numTracks; ++i) { MediaFormat format = extractor.getTrackFormat(i); String mime = format.getString(MediaFormat.KEY_MIME); if (weAreInterestedInThisTrack) { extractor.selectTrack(i); } } ByteBuffer inputBuffer = ByteBuffer.allocate(...) while (extractor.readSampleData(inputBuffer, ...) >= 0) { int trackIndex = extractor.getSampleTrackIndex(); long presentationTimeUs = extractor.getSampleTime(); ... extractor.advance(); } extractor.release(); extractor = null;
二、MediaMuxer API介紹
MediaMuxer的作用是生成音頻或視頻文件;還可以把音頻與視頻混合成一個音視頻文件。
相關API介紹:
- MediaMuxer(String path, int format):path:輸出文件的名稱 format:輸出文件的格式;當前只支持MP4格式;
- addTrack(MediaFormat format):添加通道;我們更多的是使用MediaCodec.getOutpurForma()或Extractor.getTrackFormat(int index)來獲取MediaFormat;也可以自己創建;
- start():開始合成文件
- writeSampleData(int trackIndex, ByteBuffer byteBuf, MediaCodec.BufferInfo bufferInfo):把ByteBuffer中的數據寫入到在構造器設置的文件中;
- stop():停止合成文件
- release():釋放資源
使用示例:
MediaMuxer muxer = new MediaMuxer("temp.mp4", OutputFormat.MUXER_OUTPUT_MPEG_4); // More often, the MediaFormat will be retrieved from MediaCodec.getOutputFormat() // or MediaExtractor.getTrackFormat(). MediaFormat audioFormat = new MediaFormat(...); MediaFormat videoFormat = new MediaFormat(...); int audioTrackIndex = muxer.addTrack(audioFormat); int videoTrackIndex = muxer.addTrack(videoFormat); ByteBuffer inputBuffer = ByteBuffer.allocate(bufferSize); boolean finished = false; BufferInfo bufferInfo = new BufferInfo(); muxer.start(); while(!finished) { // getInputBuffer() will fill the inputBuffer with one frame of encoded // sample from either MediaCodec or MediaExtractor, set isAudioSample to // true when the sample is audio data, set up all the fields of bufferInfo, // and return true if there are no more samples. finished = getInputBuffer(inputBuffer, isAudioSample, bufferInfo); if (!finished) { int currentTrackIndex = isAudioSample ? audioTrackIndex : videoTrackIndex; muxer.writeSampleData(currentTrackIndex, inputBuffer, bufferInfo); } }; muxer.stop(); muxer.release();
三、使用情境
3.1 從MP4文件中提取視頻並生成新的視頻文件
public class MainActivity extends AppCompatActivity { private static final String SDCARD_PATH = Environment.getExternalStorageDirectory().getPath(); private MediaExtractor mMediaExtractor; private MediaMuxer mMediaMuxer; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 獲取權限 int checkWriteExternalPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE); int checkReadExternalPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE);if (checkWriteExternalPermission != PackageManager.PERMISSION_GRANTED || checkReadExternalPermission != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{ Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}, 0); } setContentView(R.layout.activity_main); new Thread(new Runnable() { @Override public void run() { try { process(); } catch (Exception e) { e.printStackTrace(); } } }).start(); } private boolean process() throws IOException { mMediaExtractor = new MediaExtractor(); mMediaExtractor.setDataSource(SDCARD_PATH + "/ss.mp4"); int mVideoTrackIndex = -1; int framerate = 0; for (int i = 0; i < mMediaExtractor.getTrackCount(); i++) { MediaFormat format = mMediaExtractor.getTrackFormat(i); String mime = format.getString(MediaFormat.KEY_MIME); if (!mime.startsWith("video/")) { continue; } framerate = format.getInteger(MediaFormat.KEY_FRAME_RATE); mMediaExtractor.selectTrack(i); mMediaMuxer = new MediaMuxer(SDCARD_PATH + "/ouput.mp4", MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4); mVideoTrackIndex = mMediaMuxer.addTrack(format); mMediaMuxer.start(); } if (mMediaMuxer == null) { return false; } MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); info.presentationTimeUs = 0; ByteBuffer buffer = ByteBuffer.allocate(500 * 1024); int sampleSize = 0; while ((sampleSize = mMediaExtractor.readSampleData(buffer, 0)) > 0) { info.offset = 0; info.size = sampleSize; info.flags = MediaCodec.BUFFER_FLAG_SYNC_FRAME; info.presentationTimeUs += 1000 * 1000 / framerate; mMediaMuxer.writeSampleData(mVideoTrackIndex, buffer, info); mMediaExtractor.advance(); } mMediaExtractor.release(); mMediaMuxer.stop(); mMediaMuxer.release(); return true; } }