前面利用MediaExtractor提取的aac和.h264文件不经过处理没办法播放,这次利用MediaExtractor和MediaMuxer配合提取合并生成可以播放的文件,ps:aac文件和.h264需要首先利用MediaMuxer生成MP4文件,才能进行合并。
MediaMuxer从api18开始提供,可以封装编码后的视频流和音频流到视频文件中。目前MediaMuxer支持的文件输出格式包括MP4,webm和3gp.
构造函数:
MediaMuxer(String path, int format)
MediaMuxer(FileDescriptor fd, int format)
参数说明:
两个构造函数都需要一个文件路径,和一个format。
**path,fd:**用于存放合成的文件的路径,不能为null。
**format:**输出的文件的格式,OutputFormat中的常量标识。
输出格式:
OutputFormat中包含四种:
MUXER_OUTPUT_3GPP:对应3gp文件,
MUXER_OUTPUT_HEIF:定影HEIF文件,
MUXER_OUTPUT_MPEG_4:对应mp4文件,
MUXER_OUTPUT_WEBM:对应webm文件。
不同版本的api对文件格式的支持:
看上面的表的支持程度知道,一般为了支持更低的版本,合称单音频,单视频的mp4文件在api18时就支持。如果是多个音频和视频的track要怎么处理呢,只能先两个两个的合并,最后剩下两个再合并。
主要函数说明:
addTrack(MediaFormat format):利用MediaFormat添加音频或视频轨道。
release():释放MediaMuxer的资源。
setLocation(float latitude,float longitude):设置并存储地理位置信息到生成文件中。
setOrientationHint(int degrees):设置输出视频回放的方向提示。
start() :开始muxer,等待数据的输入。
Stop():停止muxer,调用这个函数后将生成合成的文件。
writeSampleData(int trackIndex, ByteBuffer byteBuf, MediaCodec.BufferInfo bufferInfo):
往muxer中写入编码的数据。
参数说明:
trackIndex:sample(样本)的track index,可以利用MediaExtractor获取。
byteBuf:写入的数据,不可为null;
bufferInfo:当前sample(样本)相关的buffer的信息,不可为null。
MediaCodec.BufferInfo:
主要包括四个数据:
flags:输入buffer相关的buffer flags。
offset:buffer开始的偏移量,通常设为0。
presentationTimeUs:buffer的时间戳。
size:buffer的数据大小。
MediaMuxer的使用模板,来自Developer:
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();
使用过程介绍:
(1)生成MediaMuxer对象
通过new MediaMuxer(String path, int format)指定视频文件输出路径和文件格式:
MediaMuxer mMediaMuxer = new MediaMuxer(mOutputVideoPath,
MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
(2)addTrack
addTrack(MediaFormat format),添加媒体通道,传入MediaFormat对象,通常从MediaExtractor或者MediaCodec中获取,也可以自己创建,后面会有文章说明。
addTrack会返回trackindex,这个index后面会用到。
(3) 调用start函数
MediaMuxer.start();
(4) 写入数据
调用MediaMuxer.writeSampleData()向mp4文件中写入数据了。每次只能添加一帧视频数据或者单个Sample的音频数据,需要BufferInfo对象作为参数。
BufferInfo info = new BufferInfo();
info.offset = 0;
info.size = sampleSize;
info.flags = MediaCodec.BUFFER_FLAG_SYNC_FRAME;
info.presentationTimeUs = mVideoExtractor.getSampleTime();
mMediaMuxer.writeSampleData(videoTrackIndex, buffer, info);
info.size 必须填入数据的大小
info.flags 需要给出是否为同步帧/关键帧
info.presentationTimeUs 必须给出正确的时间戳,注意单位是 us,第二次getSampleTime()和首次getSampleTime()的时间差。
(5)释放关闭资源
结束写入后关闭以及释放资源:
MediaMuxer.stop();
MediaMuxer.release();
private void muxerdata() {
String srcPath = Environment.getExternalStorageDirectory()
.getPath() + "/video/video.mp4";
String dirP = Environment.getExternalStorageDirectory()
.getPath() + "/demo2";
String fPath1 = Environment.getExternalStorageDirectory()
.getPath() + "/demo2/demo1.mp4";
String fPath2 = Environment.getExternalStorageDirectory()
.getPath() + "/demo2/demo2.mp4";
File file = new File(dirP);
if (!file.exists()){
file.mkdir();
}
File file1 = new File(fPath1);
File file2 = new File(fPath2);
try {
if (file1.exists()){
file1.delete();
}
file1.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (file2.exists()){
file2.delete();
}
file2.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
MediaMuxer mMediaMuxer;
int mVideoTrackIndex = 0;
int mAudioTrackIndex = 0;
long frameRate;
try {
mediaExtractor = new MediaExtractor();//此类可分离视频文件的音轨和视频轨道
mediaExtractor.setDataSource(srcPath);//媒体文件的位置
System.out.println("==========getTrackCount()===================" + mediaExtractor.getTrackCount());
for (int i = 0; i < mediaExtractor.getTrackCount(); i++) {
MediaFormat format = mediaExtractor.getTrackFormat(i);
String mime = format.getString(MediaFormat.KEY_MIME);
if (mime.startsWith("audio")) {//获取音频轨道
ByteBuffer buffer = ByteBuffer.allocate(100 * 1024);
{ mediaExtractor.selectTrack(i);//选择此音频轨道
mediaExtractor.readSampleData(buffer, 0);
long first_sampletime = mediaExtractor.getSampleTime();
mediaExtractor.advance();
long second_sampletime = mediaExtractor.getSampleTime();
frameRate = Math.abs(second_sampletime - first_sampletime);//时间戳
mediaExtractor.unselectTrack(i);
}
mediaExtractor.selectTrack(i);
System.out.println("==============frameRate111=============="+frameRate+"");
mMediaMuxer = new MediaMuxer(fPath2, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
mAudioTrackIndex = mMediaMuxer.addTrack(format);
mMediaMuxer.start();
MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
info.presentationTimeUs = 0;
int sampleSize = 0;
while ((sampleSize = mediaExtractor.readSampleData(buffer, 0)) > 0) {
info.offset = 0;
info.size = sampleSize;
info.flags = mediaExtractor.getSampleFlags();
info.presentationTimeUs += frameRate;
mMediaMuxer.writeSampleData(mAudioTrackIndex, buffer, info);
mediaExtractor.advance();
}
mMediaMuxer.stop();
mMediaMuxer.release();
}
if (mime.startsWith("video")){
mediaExtractor.selectTrack(i);//选择此视频轨道
frameRate = format.getInteger(MediaFormat.KEY_FRAME_RATE);
System.out.println("==============frameRate222=============="+ 1000 * 1000 / frameRate+"");
mMediaMuxer = new MediaMuxer(fPath1, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
mVideoTrackIndex = mMediaMuxer.addTrack(format);
mMediaMuxer.start();
MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
info.presentationTimeUs = 0;
ByteBuffer buffer = ByteBuffer.allocate(100 * 1024);
int sampleSize = 0;
while ((sampleSize = mediaExtractor.readSampleData(buffer, 0)) > 0) {
info.offset = 0;
info.size = sampleSize;
info.flags = mediaExtractor.getSampleFlags();
info.presentationTimeUs += 1000 * 1000 / frameRate;
mMediaMuxer.writeSampleData(mVideoTrackIndex, buffer, info);
mediaExtractor.advance();
}
mMediaMuxer.stop();
mMediaMuxer.release();
}
}
} catch (IOException e) {
e.printStackTrace();
}finally {
mediaExtractor.release();
mediaExtractor = null;
}
}
用到的MP4文件和提取后生成的mp4文件:
https://pan.baidu.com/s/1xyeJEpC_Zff6hgIoZBKt-w
private void muxermergedata() {
String desPath = Environment.getExternalStorageDirectory()
.getPath() + "/demo2/video.mp4";
String dirP = Environment.getExternalStorageDirectory()
.getPath() + "/demo2";
String fPath1 = Environment.getExternalStorageDirectory()
.getPath() + "/demo2/demo1.mp4";
String fPath2 = Environment.getExternalStorageDirectory()
.getPath() + "/demo2/demo2.mp4";
File file = new File(dirP);
if (!file.exists()){
file.mkdir();
}
File file1 = new File(fPath1);
File file2 = new File(fPath2);
File filedes = new File(desPath);
try {
if (filedes.exists()){
filedes.delete();
}
filedes.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
MediaMuxer mMediaMuxer = null;
try {
mMediaMuxer = new MediaMuxer(desPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
} catch (IOException e) {
e.printStackTrace();
}
int mVideoTrackIndex = 0;
int mAudioTrackIndex = 0;
long frameRate1 = 0;
long frameRate2 = 0;
MediaFormat format1;
MediaFormat format2;
try {
mediaExtractor = new MediaExtractor();//此类可分离视频文件的音轨和视频轨道
mediaExtractor.setDataSource(fPath1);//媒体文件的位置
System.out.println("==========getTrackCount()===================" + mediaExtractor.getTrackCount());
for (int i = 0; i < mediaExtractor.getTrackCount(); i++) {
format1 = mediaExtractor.getTrackFormat(i);
String mime = format1.getString(MediaFormat.KEY_MIME);
if (mime.startsWith("video")){
mediaExtractor.selectTrack(i);//选择此视频轨道
frameRate1 = format1.getInteger(MediaFormat.KEY_FRAME_RATE);
System.out.println("==============frameRate222=============="+ 1000 * 1000 / frameRate1+"");
mVideoTrackIndex = mMediaMuxer.addTrack(format1);
}
}
} catch (IOException e) {
e.printStackTrace();
}
try {
mediaExtractor2 = new MediaExtractor();//此类可分离视频文件的音轨和视频轨道
mediaExtractor2.setDataSource(fPath2);//媒体文件的位置
System.out.println("==========getTrackCount()===================" + mediaExtractor2.getTrackCount());
for (int i = 0; i < mediaExtractor2.getTrackCount(); i++) {
format2 = mediaExtractor2.getTrackFormat(i);
String mime = format2.getString(MediaFormat.KEY_MIME);
if (mime.startsWith("audio")) {//获取音频轨道
ByteBuffer buffer = ByteBuffer.allocate(100 * 1024);
{
mediaExtractor2.selectTrack(i);//选择此音频轨道
mediaExtractor2.readSampleData(buffer, 0);
long first_sampletime = mediaExtractor2.getSampleTime();
mediaExtractor2.advance();
long second_sampletime = mediaExtractor2.getSampleTime();
frameRate2 = Math.abs(second_sampletime - first_sampletime);//时间戳
mediaExtractor2.unselectTrack(i);
}
mediaExtractor2.selectTrack(i);
System.out.println("==============frameRate111==============" + frameRate2 + "");
mAudioTrackIndex = mMediaMuxer.addTrack(format2);
}
}
} catch (IOException e) {
e.printStackTrace();
}
mMediaMuxer.start();
MediaCodec.BufferInfo info1 = new MediaCodec.BufferInfo();
info1.presentationTimeUs = 0;
ByteBuffer buffer = ByteBuffer.allocate(100 * 1024);
int sampleSize1 = 0;
while ((sampleSize1 = mediaExtractor.readSampleData(buffer, 0)) > 0) {
info1.offset = 0;
info1.size = sampleSize1;
info1.flags = mediaExtractor.getSampleFlags();
info1.presentationTimeUs += 1000 * 1000 / frameRate1;
mMediaMuxer.writeSampleData(mVideoTrackIndex, buffer, info1);
mediaExtractor.advance();
}
MediaCodec.BufferInfo info2 = new MediaCodec.BufferInfo();
info2.presentationTimeUs = 0;
int sampleSize2 = 0;
while ((sampleSize2 = mediaExtractor2.readSampleData(buffer, 0)) > 0) {
info2.offset = 0;
info2.size = sampleSize2;
info2.flags = mediaExtractor2.getSampleFlags();
info2.presentationTimeUs += frameRate2;
mMediaMuxer.writeSampleData(mAudioTrackIndex, buffer, info2);
mediaExtractor2.advance();
}
try {
mediaExtractor.release();
mediaExtractor = null;
mediaExtractor2.release();
mediaExtractor2 = null;
mMediaMuxer.stop();
mMediaMuxer.release();
} catch (Exception e) {
e.printStackTrace();
}
}
如何利用MediaCodec对视频原始数据进行编解码,敬请期待后面的文章。