Android音视频开发基础(四):使用Android平台的MediaExtractor和MediaMuxer API解析和封装mp4文件

前言

在Android音视频开发中,网上知识点过于零碎,自学起来难度非常大,不过音视频大牛Jhuster提出了《Android 音视频从入门到提高 - 任务列表》。本文是Android音视频任务列表的第四篇, 对应的要学习的内容是:使用Android平台的MediaExtractor和MediaMuxer API解析和封装mp4文件


音视频任务列表

音视频任务列表: 点击此处跳转查看


一、目录

Android音视频开发基础(四):使用Android平台的MediaExtractor和MediaMuxer API解析和封装mp4文件_第1张图片


(一)MediaExtractor介绍

MediaExtractor的作用是把音频和视频的数据进行分离,即通过MediaExtractor把音频或视频给单独抽取出来

主要API介绍:

  • setDataSource(String path):即可以设置本地文件又可以设置网络文件
  • getTrackCount():得到源文件通道数
  • getTrackFormat(int index):获取指定(index)的通道格式
  • getSampleTime():返回当前的时间戳
  • readSampleData(ByteBuffer byteBuf, int offset):把指定通道中的数据按偏移量读取到ByteBuffer中;
  • advance():读取下一帧数据
  • release():读取结束后释放资源

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介绍

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生成音频、视频、把音频与视频混合成一个音视频文件的步骤:
(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();

(三)通过MediaExtractor把mp4文件中的视频单独抽取出来,通过MediaMuxer合成新的视频文件

实例:

/**
* 分离视频
*/
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());
    }

}

(四)通过MediaExtractor把mp4文件中的音频单独抽取出来,通过MediaMuxer合成新的音频文件

实例:

/**
* 分离音频
*/
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());
    }

}

(五)通过MediaExtractor分别把mp3文件中的音频、mp4文件中的视频抽取出来,通过MediaMuxer合成新的视频文件

实例:

/**
* 合成音视频
*/
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());
    }
}

(六)完整代码

(1)布局

<?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>

(2)代码

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();
        }
    }

}

(3)权限

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

(4)文件

将input.mp4文件放入手机

(5)结果

Android音视频开发基础(四):使用Android平台的MediaExtractor和MediaMuxer API解析和封装mp4文件_第2张图片

Android音视频开发基础(四):使用Android平台的MediaExtractor和MediaMuxer API解析和封装mp4文件_第3张图片


源代码:
Android音视频开发基础(四):使用Android平台的MediaExtractor和MediaMuxer API解析和封装mp4文件

你可能感兴趣的:(Android音视频,android)