在Android音视频开发中,网上知识点过于零碎,自学起来难度非常大,不过音视频大牛Jhuster提出了《Android 音视频从入门到提高 - 任务列表》。本文是Android音视频任务列表的第四篇, 对应的要学习的内容是:使用Android平台的MediaExtractor和MediaMuxer API解析和封装mp4文件
音视频任务列表: 点击此处跳转查看
MediaExtractor的作用是把音频和视频的数据进行分离,即通过MediaExtractor把音频或视频给单独抽取出来
主要API介绍:
MediaExtractor把音频或视频给单独抽取出来的步骤:
(1)创建实例
MediaExtractor mediaExtractor = new MediaExtractor();
(2)设置数据源
mediaExtractor.setDataSource(path);
(3)获取数据源的轨道数,切换到想要的轨道
// 轨道索引
int videoIndex = -1;
// 视频轨道格式信息
MediaFormat mediaFormat = null;
// 数据源的轨道数
int trackCount = mediaExtractor.getTrackCount();
for (int i = 0; i < trackCount; i++) {
MediaFormat format = mediaExtractor.getTrackFormat(i);
String mimeType = format.getString(MediaFormat.KEY_MIME);
if (mimeType.startsWith("video/")) {
videoIndex = i;
mediaFormat = format;
break;
}
}
// 切换到想要的轨道
mediaExtractor.selectTrack(videoIndex);
(4)对所需轨道数据循环读取读取每帧,进行处理
while (true) {
// 将样本数据存储到字节缓存区
int readSampleSize = mediaExtractor.readSampleData(byteBuffer, 0);
// 如果没有可获取的样本,退出循环
if (readSampleSize < 0) {
mediaExtractor.unselectTrack(videoIndex);
break;
}
...
...
// 读取下一帧数据
mediaExtractor.advance();
}
(5)完成后释放资源
mediaExtractor.release();
MediaMuxer的作用是生成音频或视频文件;还可以把音频与视频混合成一个音视频文件
相关API介绍:
MediaMuxer生成音频、视频、把音频与视频混合成一个音视频文件的步骤:
(1)创建实例
MediaMuxermediaMuxer = new MediaMuxer(path, format);
path: 输出文件的名称;format: 输出文件的格式,当前只支持 MP4 格式。
(2)将音频轨或视频轨添加到MediaMuxer,返回新的轨道
int trackIndex = mediaMuxer.addTrack(videoFormat);
(3)开始合成
mediaMuxer.start();
(4)循环将音频轨或视频轨的数据写到文件
while (true) {
// 将样本数据存储到字节缓存区
int readSampleSize = mediaExtractor.readSampleData(byteBuffer, 0);
// 如果没有可获取的样本,退出循环
if (readSampleSize < 0) {
mediaExtractor.unselectTrack(videoIndex);
break;
}
bufferInfo.size = readSampleSize;
bufferInfo.flags = mediaExtractor.getSampleFlags();
bufferInfo.offset = 0;
bufferInfo.presentationTimeUs = mediaExtractor.getSampleTime();
mediaMuxer.writeSampleData(trackIndex, byteBuffer, bufferInfo);
// 读取下一帧数据
mediaExtractor.advance();
}
(5)完成后释放资源
mediaMuxer.stop();
mediaMuxer.release();
实例:
/**
* 分离视频
*/
private void extractorVideo() {
// 创建MediaExtractor实例
MediaExtractor mediaExtractor = new MediaExtractor();
// 初始化MediaMuxer
MediaMuxer mediaMuxer = null;
// 轨道索引
int videoIndex = -1;
try {
// 设置数据源
mediaExtractor.setDataSource(SDCARD_PATH + "/input.mp4");
// 数据源的轨道数
int trackCount = mediaExtractor.getTrackCount();
for (int i = 0; i < trackCount; i++) {
// 视频轨道格式信息
MediaFormat trackFormat = mediaExtractor.getTrackFormat(i);
String mimeType = trackFormat.getString(MediaFormat.KEY_MIME);
if (mimeType.startsWith("video/")) {
// 该轨道是视频轨道
videoIndex = i;
}
}
// 切换到想要的轨道
mediaExtractor.selectTrack(videoIndex);
// 视频轨道格式信息
MediaFormat trackFormat = mediaExtractor.getTrackFormat(videoIndex);
// 创建MediaMuxer实例,通过new MediaMuxer(String path, int format)指定视频文件输出路径和文件格式
mediaMuxer = new MediaMuxer(SDCARD_PATH + "/output_video.mp4", MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
// 添加媒体通道
int trackIndex = mediaMuxer.addTrack(trackFormat);
ByteBuffer byteBuffer = ByteBuffer.allocate(1024 * 500);
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
// 添加完所有track后调用start方法,开始音视频合成
mediaMuxer.start();
// 获取帧之间的间隔时间
long videoSampleTime;
{
// 将样本数据存储到字节缓存区
mediaExtractor.readSampleData(byteBuffer, 0);
// skip first I frame
if (mediaExtractor.getSampleFlags() == MediaExtractor.SAMPLE_FLAG_SYNC)
// 读取下一帧数据
mediaExtractor.advance();
mediaExtractor.readSampleData(byteBuffer, 0);
long firstVideoPTS = mediaExtractor.getSampleTime();
mediaExtractor.advance();
mediaExtractor.readSampleData(byteBuffer, 0);
long SecondVideoPTS = mediaExtractor.getSampleTime();
videoSampleTime = Math.abs(SecondVideoPTS - firstVideoPTS);
Log.d("fuck", "videoSampleTime is " + videoSampleTime);
}
mediaExtractor.unselectTrack(videoIndex);
mediaExtractor.selectTrack(videoIndex);
while (true) {
// 将样本数据存储到字节缓存区
int readSampleSize = mediaExtractor.readSampleData(byteBuffer, 0);
// 将样本数据存储到字节缓存区
if (readSampleSize < 0) {
break;
}
// 读取下一帧数据
mediaExtractor.advance();
bufferInfo.size = readSampleSize;
bufferInfo.offset = 0;
bufferInfo.flags = mediaExtractor.getSampleFlags();
bufferInfo.presentationTimeUs += videoSampleTime;
// 调用MediaMuxer.writeSampleData()向mp4文件中写入数据了
mediaMuxer.writeSampleData(trackIndex, byteBuffer, bufferInfo);
}
mediaMuxer.stop();
mediaExtractor.release();
mediaMuxer.release();
Toast.makeText(this, "分离视频完成", Toast.LENGTH_LONG).show();
Log.i("info", "分离视频finish++++++++++++++++++++++++++++++++++++++");
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(this, "分离视频失败", Toast.LENGTH_LONG).show();
Log.i("info", "分离视频失败++++++++++++++++++++++++++++++++++++++" + e.toString());
}
}
实例:
/**
* 分离音频
*/
private void extractorAudio() {
// 创建MediaExtractor实例
MediaExtractor mediaExtractor = new MediaExtractor();
// 初始化MediaMuxer
MediaMuxer mediaMuxer = null;
// 轨道索引
int audioIndex = -1;
try {
mediaExtractor.setDataSource(SDCARD_PATH + "/input.mp4");
int trackCount = mediaExtractor.getTrackCount();
for (int i = 0; i < trackCount; i++) {
MediaFormat trackFormat = mediaExtractor.getTrackFormat(i);
if (trackFormat.getString(MediaFormat.KEY_MIME).startsWith("audio/")) {
audioIndex = i;
}
}
mediaExtractor.selectTrack(audioIndex);
MediaFormat trackFormat = mediaExtractor.getTrackFormat(audioIndex);
mediaMuxer = new MediaMuxer(SDCARD_PATH + "/output_audio.mp3", MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
int writeAudioIndex = mediaMuxer.addTrack(trackFormat);
mediaMuxer.start();
ByteBuffer byteBuffer = ByteBuffer.allocate(500 * 1024);
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
long stampTime = 0;
// 获取帧之间的间隔时间
{
mediaExtractor.readSampleData(byteBuffer, 0);
if (mediaExtractor.getSampleFlags() == MediaExtractor.SAMPLE_FLAG_SYNC) {
mediaExtractor.advance();
}
mediaExtractor.readSampleData(byteBuffer, 0);
long secondTime = mediaExtractor.getSampleTime();
mediaExtractor.advance();
mediaExtractor.readSampleData(byteBuffer, 0);
long thirdTime = mediaExtractor.getSampleTime();
stampTime = Math.abs(thirdTime - secondTime);
Log.e("fuck", stampTime + "");
}
mediaExtractor.unselectTrack(audioIndex);
mediaExtractor.selectTrack(audioIndex);
while (true) {
int readSampleSize = mediaExtractor.readSampleData(byteBuffer, 0);
if (readSampleSize < 0) {
break;
}
mediaExtractor.advance();
bufferInfo.size = readSampleSize;
bufferInfo.flags = mediaExtractor.getSampleFlags();
bufferInfo.offset = 0;
bufferInfo.presentationTimeUs += stampTime;
mediaMuxer.writeSampleData(writeAudioIndex, byteBuffer, bufferInfo);
}
mediaMuxer.stop();
mediaMuxer.release();
mediaExtractor.release();
Toast.makeText(this, "分离音频完成", Toast.LENGTH_LONG).show();
Log.i("info", "分离音频完成++++++++++++++++++++++++++++++++++++++");
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(this, "分离音频失败", Toast.LENGTH_LONG).show();
Log.i("info", "分离音频失败++++++++++++++++++++++++++++++++++++++" + e.toString());
}
}
实例:
/**
* 合成音视频
*/
private void muxerVideoAudio() {
try {
// 以下过程是找到output_video.mp4中视频轨道
MediaExtractor videoExtractor = new MediaExtractor();
videoExtractor.setDataSource(SDCARD_PATH + "/output_video.mp4");
MediaFormat videoFormat = null;
int videoTrackIndex = -1;
int videoTrackCount = videoExtractor.getTrackCount();
for (int i = 0; i < videoTrackCount; i++) {
videoFormat = videoExtractor.getTrackFormat(i);
String mimeType = videoFormat.getString(MediaFormat.KEY_MIME);
if (mimeType.startsWith("video/")) {
videoTrackIndex = i;
break;
}
}
// 以下过程是找到output_audio.mp3中音频轨道
MediaExtractor audioExtractor = new MediaExtractor();
audioExtractor.setDataSource(SDCARD_PATH + "/output_audio.mp3");
MediaFormat audioFormat = null;
int audioTrackIndex = -1;
int audioTrackCount = audioExtractor.getTrackCount();
for (int i = 0; i < audioTrackCount; i++) {
audioFormat = audioExtractor.getTrackFormat(i);
String mimeType = audioFormat.getString(MediaFormat.KEY_MIME);
if (mimeType.startsWith("audio/")) {
audioTrackIndex = i;
break;
}
}
videoExtractor.selectTrack(videoTrackIndex);
audioExtractor.selectTrack(audioTrackIndex);
MediaCodec.BufferInfo videoBufferInfo = new MediaCodec.BufferInfo();
MediaCodec.BufferInfo audioBufferInfo = new MediaCodec.BufferInfo();
// 通过new MediaMuxer(String path, int format)指定视频文件输出路径和文件格式
MediaMuxer mediaMuxer = new MediaMuxer(SDCARD_PATH + "/output-composite.mp4", MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
// MediaMuxer添加媒体通道(视频)
int writeVideoTrackIndex = mediaMuxer.addTrack(videoFormat);
// MediaMuxer添加媒体通道(音频)
int writeAudioTrackIndex = mediaMuxer.addTrack(audioFormat);
// 开始音视频合成
mediaMuxer.start();
ByteBuffer byteBuffer = ByteBuffer.allocate(500 * 1024);
long sampleTime = 0;
{
videoExtractor.readSampleData(byteBuffer, 0);
if (videoExtractor.getSampleFlags() == MediaExtractor.SAMPLE_FLAG_SYNC) {
videoExtractor.advance();
}
videoExtractor.readSampleData(byteBuffer, 0);
long secondTime = videoExtractor.getSampleTime();
videoExtractor.advance();
long thirdTime = videoExtractor.getSampleTime();
sampleTime = Math.abs(thirdTime - secondTime);
}
videoExtractor.unselectTrack(videoTrackIndex);
videoExtractor.selectTrack(videoTrackIndex);
while (true) {
int readVideoSampleSize = videoExtractor.readSampleData(byteBuffer, 0);
if (readVideoSampleSize < 0) {
break;
}
videoBufferInfo.size = readVideoSampleSize;
videoBufferInfo.presentationTimeUs += sampleTime;
videoBufferInfo.offset = 0;
videoBufferInfo.flags = videoExtractor.getSampleFlags();
mediaMuxer.writeSampleData(writeVideoTrackIndex, byteBuffer, videoBufferInfo);
videoExtractor.advance();
}
while (true) {
int readAudioSampleSize = audioExtractor.readSampleData(byteBuffer, 0);
if (readAudioSampleSize < 0) {
break;
}
audioBufferInfo.size = readAudioSampleSize;
audioBufferInfo.presentationTimeUs += sampleTime;
audioBufferInfo.offset = 0;
audioBufferInfo.flags = videoExtractor.getSampleFlags();
mediaMuxer.writeSampleData(writeAudioTrackIndex, byteBuffer, audioBufferInfo);
audioExtractor.advance();
}
mediaMuxer.stop();
mediaMuxer.release();
videoExtractor.release();
audioExtractor.release();
Toast.makeText(this, "合成音视频完成", Toast.LENGTH_LONG).show();
Log.i("info", "合成音视频完成++++++++++++++++++++++++++++++++++++++");
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(this, "合成音视频失败", Toast.LENGTH_LONG).show();
Log.i("info", "合成音视频失败++++++++++++++++++++++++++++++++++++++" + e.toString());
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center">
<Button
android:id="@+id/btn_extractor_video"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="分离视频" />
<Button
android:id="@+id/btn_extractor_audio"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="40dp"
android:text="分离音频" />
<Button android:id="@+id/btn_muxer_videoaudio"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_marginTop="40dp"
android:text="合成音视频"/>
</LinearLayout>
package com.lzacking.mediaextractortest;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import android.Manifest;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.media.MediaCodec;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.media.MediaMuxer;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
public class MainActivity extends AppCompatActivity {
private static final String SDCARD_PATH = Environment.getExternalStorageDirectory().getPath();
Button mExtractorVideo;
Button mExtractorAudio;
Button mMuxerVideoAudio;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 获取权限
verifyStoragePermissions(this);
// 分离视频
mExtractorVideo = (Button) findViewById(R.id.btn_extractor_video);
// 分离音频
mExtractorAudio = (Button) findViewById(R.id.btn_extractor_audio);
// 合成音视频
mMuxerVideoAudio = (Button) findViewById(R.id.btn_muxer_videoaudio);
mExtractorVideo.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
extractorVideo();
}
});
mExtractorAudio.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
extractorAudio();
}
});
mMuxerVideoAudio.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
muxerVideoAudio();
}
});
}
/**
* 分离视频
*/
private void extractorVideo() {
// 创建MediaExtractor实例
MediaExtractor mediaExtractor = new MediaExtractor();
// 初始化MediaMuxer
MediaMuxer mediaMuxer = null;
// 轨道索引
int videoIndex = -1;
try {
// 设置数据源
mediaExtractor.setDataSource(SDCARD_PATH + "/input.mp4");
// 数据源的轨道数
int trackCount = mediaExtractor.getTrackCount();
for (int i = 0; i < trackCount; i++) {
// 视频轨道格式信息
MediaFormat trackFormat = mediaExtractor.getTrackFormat(i);
String mimeType = trackFormat.getString(MediaFormat.KEY_MIME);
if (mimeType.startsWith("video/")) {
// 该轨道是视频轨道
videoIndex = i;
}
}
// 切换到想要的轨道
mediaExtractor.selectTrack(videoIndex);
// 视频轨道格式信息
MediaFormat trackFormat = mediaExtractor.getTrackFormat(videoIndex);
// 创建MediaMuxer实例,通过new MediaMuxer(String path, int format)指定视频文件输出路径和文件格式
mediaMuxer = new MediaMuxer(SDCARD_PATH + "/output_video.mp4", MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
// 添加媒体通道
int trackIndex = mediaMuxer.addTrack(trackFormat);
ByteBuffer byteBuffer = ByteBuffer.allocate(1024 * 500);
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
// 添加完所有track后调用start方法,开始音视频合成
mediaMuxer.start();
// 获取帧之间的间隔时间
long videoSampleTime;
{
// 将样本数据存储到字节缓存区
mediaExtractor.readSampleData(byteBuffer, 0);
// skip first I frame
if (mediaExtractor.getSampleFlags() == MediaExtractor.SAMPLE_FLAG_SYNC)
// 读取下一帧数据
mediaExtractor.advance();
mediaExtractor.readSampleData(byteBuffer, 0);
long firstVideoPTS = mediaExtractor.getSampleTime();
mediaExtractor.advance();
mediaExtractor.readSampleData(byteBuffer, 0);
long SecondVideoPTS = mediaExtractor.getSampleTime();
videoSampleTime = Math.abs(SecondVideoPTS - firstVideoPTS);
Log.d("fuck", "videoSampleTime is " + videoSampleTime);
}
mediaExtractor.unselectTrack(videoIndex);
mediaExtractor.selectTrack(videoIndex);
while (true) {
// 将样本数据存储到字节缓存区
int readSampleSize = mediaExtractor.readSampleData(byteBuffer, 0);
// 将样本数据存储到字节缓存区
if (readSampleSize < 0) {
break;
}
// 读取下一帧数据
mediaExtractor.advance();
bufferInfo.size = readSampleSize;
bufferInfo.offset = 0;
bufferInfo.flags = mediaExtractor.getSampleFlags();
bufferInfo.presentationTimeUs += videoSampleTime;
// 调用MediaMuxer.writeSampleData()向mp4文件中写入数据了
mediaMuxer.writeSampleData(trackIndex, byteBuffer, bufferInfo);
}
mediaMuxer.stop();
mediaExtractor.release();
mediaMuxer.release();
Toast.makeText(this, "分离视频完成", Toast.LENGTH_LONG).show();
Log.i("info", "分离视频finish++++++++++++++++++++++++++++++++++++++");
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(this, "分离视频失败", Toast.LENGTH_LONG).show();
Log.i("info", "分离视频失败++++++++++++++++++++++++++++++++++++++" + e.toString());
}
}
/**
* 分离音频
*/
private void extractorAudio() {
// 创建MediaExtractor实例
MediaExtractor mediaExtractor = new MediaExtractor();
// 初始化MediaMuxer
MediaMuxer mediaMuxer = null;
// 轨道索引
int audioIndex = -1;
try {
mediaExtractor.setDataSource(SDCARD_PATH + "/input.mp4");
int trackCount = mediaExtractor.getTrackCount();
for (int i = 0; i < trackCount; i++) {
MediaFormat trackFormat = mediaExtractor.getTrackFormat(i);
if (trackFormat.getString(MediaFormat.KEY_MIME).startsWith("audio/")) {
audioIndex = i;
}
}
mediaExtractor.selectTrack(audioIndex);
MediaFormat trackFormat = mediaExtractor.getTrackFormat(audioIndex);
mediaMuxer = new MediaMuxer(SDCARD_PATH + "/output_audio.mp3", MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
int writeAudioIndex = mediaMuxer.addTrack(trackFormat);
mediaMuxer.start();
ByteBuffer byteBuffer = ByteBuffer.allocate(500 * 1024);
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
long stampTime = 0;
// 获取帧之间的间隔时间
{
mediaExtractor.readSampleData(byteBuffer, 0);
if (mediaExtractor.getSampleFlags() == MediaExtractor.SAMPLE_FLAG_SYNC) {
mediaExtractor.advance();
}
mediaExtractor.readSampleData(byteBuffer, 0);
long secondTime = mediaExtractor.getSampleTime();
mediaExtractor.advance();
mediaExtractor.readSampleData(byteBuffer, 0);
long thirdTime = mediaExtractor.getSampleTime();
stampTime = Math.abs(thirdTime - secondTime);
Log.e("fuck", stampTime + "");
}
mediaExtractor.unselectTrack(audioIndex);
mediaExtractor.selectTrack(audioIndex);
while (true) {
int readSampleSize = mediaExtractor.readSampleData(byteBuffer, 0);
if (readSampleSize < 0) {
break;
}
mediaExtractor.advance();
bufferInfo.size = readSampleSize;
bufferInfo.flags = mediaExtractor.getSampleFlags();
bufferInfo.offset = 0;
bufferInfo.presentationTimeUs += stampTime;
mediaMuxer.writeSampleData(writeAudioIndex, byteBuffer, bufferInfo);
}
mediaMuxer.stop();
mediaMuxer.release();
mediaExtractor.release();
Toast.makeText(this, "分离音频完成", Toast.LENGTH_LONG).show();
Log.i("info", "分离音频完成++++++++++++++++++++++++++++++++++++++");
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(this, "分离音频失败", Toast.LENGTH_LONG).show();
Log.i("info", "分离音频失败++++++++++++++++++++++++++++++++++++++" + e.toString());
}
}
/**
* 合成音视频
*/
private void muxerVideoAudio() {
try {
// 以下过程是找到output_video.mp4中视频轨道
MediaExtractor videoExtractor = new MediaExtractor();
videoExtractor.setDataSource(SDCARD_PATH + "/output_video.mp4");
MediaFormat videoFormat = null;
int videoTrackIndex = -1;
int videoTrackCount = videoExtractor.getTrackCount();
for (int i = 0; i < videoTrackCount; i++) {
videoFormat = videoExtractor.getTrackFormat(i);
String mimeType = videoFormat.getString(MediaFormat.KEY_MIME);
if (mimeType.startsWith("video/")) {
videoTrackIndex = i;
break;
}
}
// 以下过程是找到output_audio.mp3中音频轨道
MediaExtractor audioExtractor = new MediaExtractor();
audioExtractor.setDataSource(SDCARD_PATH + "/output_audio.mp3");
MediaFormat audioFormat = null;
int audioTrackIndex = -1;
int audioTrackCount = audioExtractor.getTrackCount();
for (int i = 0; i < audioTrackCount; i++) {
audioFormat = audioExtractor.getTrackFormat(i);
String mimeType = audioFormat.getString(MediaFormat.KEY_MIME);
if (mimeType.startsWith("audio/")) {
audioTrackIndex = i;
break;
}
}
videoExtractor.selectTrack(videoTrackIndex);
audioExtractor.selectTrack(audioTrackIndex);
MediaCodec.BufferInfo videoBufferInfo = new MediaCodec.BufferInfo();
MediaCodec.BufferInfo audioBufferInfo = new MediaCodec.BufferInfo();
// 通过new MediaMuxer(String path, int format)指定视频文件输出路径和文件格式
MediaMuxer mediaMuxer = new MediaMuxer(SDCARD_PATH + "/output-composite.mp4", MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
// MediaMuxer添加媒体通道(视频)
int writeVideoTrackIndex = mediaMuxer.addTrack(videoFormat);
// MediaMuxer添加媒体通道(音频)
int writeAudioTrackIndex = mediaMuxer.addTrack(audioFormat);
// 开始音视频合成
mediaMuxer.start();
ByteBuffer byteBuffer = ByteBuffer.allocate(500 * 1024);
long sampleTime = 0;
{
videoExtractor.readSampleData(byteBuffer, 0);
if (videoExtractor.getSampleFlags() == MediaExtractor.SAMPLE_FLAG_SYNC) {
videoExtractor.advance();
}
videoExtractor.readSampleData(byteBuffer, 0);
long secondTime = videoExtractor.getSampleTime();
videoExtractor.advance();
long thirdTime = videoExtractor.getSampleTime();
sampleTime = Math.abs(thirdTime - secondTime);
}
videoExtractor.unselectTrack(videoTrackIndex);
videoExtractor.selectTrack(videoTrackIndex);
while (true) {
int readVideoSampleSize = videoExtractor.readSampleData(byteBuffer, 0);
if (readVideoSampleSize < 0) {
break;
}
videoBufferInfo.size = readVideoSampleSize;
videoBufferInfo.presentationTimeUs += sampleTime;
videoBufferInfo.offset = 0;
videoBufferInfo.flags = videoExtractor.getSampleFlags();
mediaMuxer.writeSampleData(writeVideoTrackIndex, byteBuffer, videoBufferInfo);
videoExtractor.advance();
}
while (true) {
int readAudioSampleSize = audioExtractor.readSampleData(byteBuffer, 0);
if (readAudioSampleSize < 0) {
break;
}
audioBufferInfo.size = readAudioSampleSize;
audioBufferInfo.presentationTimeUs += sampleTime;
audioBufferInfo.offset = 0;
audioBufferInfo.flags = videoExtractor.getSampleFlags();
mediaMuxer.writeSampleData(writeAudioTrackIndex, byteBuffer, audioBufferInfo);
audioExtractor.advance();
}
mediaMuxer.stop();
mediaMuxer.release();
videoExtractor.release();
audioExtractor.release();
Toast.makeText(this, "合成音视频完成", Toast.LENGTH_LONG).show();
Log.i("info", "合成音视频完成++++++++++++++++++++++++++++++++++++++");
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(this, "合成音视频失败", Toast.LENGTH_LONG).show();
Log.i("info", "合成音视频失败++++++++++++++++++++++++++++++++++++++" + e.toString());
}
}
private static final int REQUEST_EXTERNAL_STORAGE = 1;
private static String[] PERMISSIONS_STORAGE = {
"android.permission.READ_EXTERNAL_STORAGE",
"android.permission.WRITE_EXTERNAL_STORAGE" };
public static void verifyStoragePermissions(Activity activity) {
try {
// 检测是否有写的权限
int permission = ActivityCompat.checkSelfPermission(activity,
"android.permission.WRITE_EXTERNAL_STORAGE");
if (permission != PackageManager.PERMISSION_GRANTED) {
// 没有写的权限,去申请写的权限,会弹出对话框
ActivityCompat.requestPermissions(activity, PERMISSIONS_STORAGE, REQUEST_EXTERNAL_STORAGE);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
将input.mp4文件放入手机
源代码:
Android音视频开发基础(四):使用Android平台的MediaExtractor和MediaMuxer API解析和封装mp4文件