Android-音视频(6):用 MediaExtractor 和 MediaMuxer API 解析和封装 mp4 文件

1.MediaExtractor API的作用


作用:

  • 可以把音视频文件的音频和视频分离,并抽取相应的数据通道,然后进行操作。

如何使用:

  1. 先要知道是针对哪个文件操作,所以要用 setDataSource(String filePath) 设置目标文件。
  2. 然后需要知道这个文件所有的通道数,用 getTrackCount() 得到,通过遍历,得到需要的通道 int i。
  3. 根据得到的通道,用 getTrackFirmat(int i ) 得到这个通道的数据格式(MediaFormat mediaFormat)。
  4. 然后把MediaExtractor对准这个通道 用 selectTack(int i),准备读取数据。
  5. readSampleData(ByteBuffer byteBuffer , int offset) 把指定通道中的数据按偏移量读取到byteBuffer中,这只是一帧的数据。
  6. 有了这个byteBuffer数据,之后的事就交给MediaMuxer去操作这一帧的数据,之后调用 adVance()调取下一帧,重复5、6。
  7. 操作完之后,记得释放 release()。

代码示例将在下面和MediaMuxer一起给出。

2.MediaMuxer API的作用


作用:

  • 生成一个音频或视频文件;还可以把音频与视频混合成一个音视频文件

如何使用:

  1. 因为是生成一个文件,所以构造一个MediaMuxer的时候需要传入文件的路径,和文件的格式如:new MediaMuxer(String newfilepath, int format)  格式一般为: MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4
  2. 然后需要添加通道,记录一个数据通道的格式(MediaExtracktor第3步得到) addTrack(MediaFormat format)并得到一个trackIndex,之后用这个判断用哪个通道写入数据。
  3. start():开始合成文件
  4. 每当MediaExtracktor的第5步后,用 writeSampleData(int trackIndex, ByteBuffer byteBuf, MediaCodec.BufferInfo bufferInfo):把ByteBuffer中的数据写入之前设置的文件中。
  5. 数据写入完成,stop():停止合成文件   release():释放资源

3.结合的示例代码如下:


String mp4FilePath = Environment.getExternalStorageDirectory().getAbsolutePath()+ File.separator+"test.mp4";
String newVideoFilePath = Environment.getExternalStorageDirectory().getAbsolutePath()+File.separator+"testVideo.mp4";

private void startGetVideo() { //抽取mp4文件的视频数据,并生成新的mp4文件,但是没有声音。
        File videofile = new File(newVideoFilePath);
        if (videofile.exists()){
            videofile.delete();
            try {
                videofile.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        getVideoThread = new Thread(){
            @Override
            public void run() {
                try {
                    mediaExtractor = new MediaExtractor();
                    //设置视频文件,用于提取视频轨道数据
                    mediaExtractor.setDataSource(mp4FilePath);
                    int videoTrackIndex = -1;
                    int framerate = 0;
                    for (int i = 0 ; i < mediaExtractor.getTrackCount() ; i++){
                        MediaFormat mediaformat = mediaExtractor.getTrackFormat(i);
                        //跳过不是视频轨道的轨道
                        if (!(mediaformat.getString(MediaFormat.KEY_MIME)).startsWith("video/")){
                            continue;
                        }
                        //几帧/秒
                        framerate = mediaformat.getInteger(MediaFormat.KEY_FRAME_RATE);
                        //选择视频轨道
                        mediaExtractor.selectTrack(i);
                        //设置视频轨道的输出位置和格式
                        mediaMuxer = new MediaMuxer(newVideoFilePath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
                        //记录视频轨道值,之后要用这个值判断写入数据
                        videoTrackIndex = mediaMuxer.addTrack(mediaformat);
                        mediaMuxer.start();
                    }
                    if (mediaMuxer == null){
                        return;
                    }
                    //读取轨道信息时,以及向新文件写入轨道信息时。用于保存每一帧的信息(如时长,size,flag)
                    MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
                    //当前演示时长 初始化为0
                    info.presentationTimeUs = 0;
                    int sampleSize = 0;
                    //申请一个空间保存每一帧视频数据,然后写入新文件
                    ByteBuffer buffer = ByteBuffer.allocate(500*1024);
                    while((sampleSize = mediaExtractor.readSampleData(buffer,0)) > 0){
                        //保存帧信息
                        info.size = sampleSize;
                        info.flags = MediaCodec.BUFFER_FLAG_SYNC_FRAME;
                        info.presentationTimeUs += 1000*1000 / framerate; // (1000*1000 / framerate) 是一帧的微秒时长
                        //保存帧数据(向指定的视频轨道写入)
                        mediaMuxer.writeSampleData(videoTrackIndex,buffer,info);
                        mediaExtractor.advance(); //下一帧
                    }
                    mediaExtractor.release();
                    mediaMuxer.stop();
                    mediaMuxer.release();
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            showInfor.append("\n"+"testVideo.mp4 视频轨道数据提取成功");
                        }
                    });
                } catch (IOException e) {
                    e.printStackTrace();
                }finally {
                    getVideoThread = null;
                }
            }
        };
        getVideoThread.start();
    }

猜你喜欢

转载自blog.csdn.net/qq_38261174/article/details/82924414