Android 音视频开发(五):使用 MediaExtractor 和 MediaMuxer API 解析和封装 mp4 文件

时间:2023-03-10 06:43:15
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;
}
}