Android 音视频开发(五):使用 MediaExtractor 和 MediaMuxer API 解析和封装 mp4 文件
一个音视频文件是由音频和视频组成的,我们可以通过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; } }