Android 音视频学习:MediaExtractor和MediaMuxer学习

概述

继续学习MediaExtractor和MediaMuxer,主要作用就是音视频的分离和合成,此篇博客仅作为笔记使用,以防之后忘记

MediaExtractor

这个类的主要作用是把音频数据和视频数据分离

主要的API

方法描述
setDataSource(String path)设置源文件,既可以是本地文件也可以是网络文件
getTrackCount()获取源文件的通道数
getTrackFormat(int index)获取指定index的通道格式
getSampleTime()以微秒为单位返回当前样本的呈现时间。
readSampleData(ByteBuffer byteBuf, int offset)把指定通道中的数据,按照偏移量读取到ByteBuffer中
advance()读取下一帧的数据
release()完成后释放资源

MediaMuxer

这个类的组要作用是生成音频视频文件,还可以把音频和视频文件合成一个音视频文件

主要的API

方法描述
MediaMuxer(String path, int format)path:输出文件的名称,format:输出文件的格式
addTrack(MediaFormat format)添加通道,我们可以用MediaCodec.getOutpurForma()或者Extractor.getTrackFormat(int index)来获取MediaFormat,也可以自己建
start()开始合成文件
writeSampleData(int trackIndex, ByteBuffer byteBuf, MediaCodec.BufferInfo bufferInfo)把ByteBuffer的数据写入到文件中
stop()停止合成
release()释放资源

实战

MediaExtractor的使用

private void start() {
        FileOutputStream videooutputStream = null;
        FileOutputStream audiooutputStream = null;
        MediaExtractor mediaExtractor = null;
        try {
            File videofile = new File("/sdcard/aaapcm/video.mp4");
            File audiofile = new File("/sdcard/aaapcm/audio.pcm");
            videooutputStream = new FileOutputStream(videofile);
            audiooutputStream = new FileOutputStream(audiofile);
            mediaExtractor = new MediaExtractor();
            //设置源文件
            mediaExtractor.setDataSource("/sdcard/aaapcm/text.mp4");
            //获取通道数
            int trackCount = mediaExtractor.getTrackCount();
            int audio = -1;
            int video = -1;
            for (int i = 0; i < trackCount; i++) {
                MediaFormat trackFormat = mediaExtractor.getTrackFormat(i);
                String formatString = trackFormat.getString(MediaFormat.KEY_MIME);
                Log.d("mmm", formatString);
                //视频通道
                if (formatString.startsWith("video/")) {
                    video = i;
                    Log.d("mmm", "视频" + video);
                }

                //音频通道
                if (formatString.startsWith("audio/")) {
                    audio = i;
                    Log.d("mmm", "音频" + audio);
                }
            }

            ByteBuffer byteBuffer = ByteBuffer.allocate(500 * 1024);

            //选择视频通道
            mediaExtractor.selectTrack(video);
            int i;
            //拿到视频通道的数据,写入到文件
            while ((i = mediaExtractor.readSampleData(byteBuffer, 0)) > 0) {
                byte[] bytes = new byte[i];
                byteBuffer.get(bytes);
                videooutputStream.write(bytes);
                byteBuffer.clear();
                mediaExtractor.advance();
            }
            videooutputStream.close();

            
            //选择音频通道
            mediaExtractor.selectTrack(audio);
            //拿到音频通道的数据并写入文件
            while ((i = mediaExtractor.readSampleData(byteBuffer, 0)) > 0) {
                byte[] bytes = new byte[i];
                byteBuffer.get(bytes);
                audiooutputStream.write(bytes);
                byteBuffer.clear();
                mediaExtractor.advance();
            }
            audiooutputStream.close();

            Log.d("mmm", "分离结束");


        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (mediaExtractor != null) {
                mediaExtractor.release();
            }
        }
    }

这部分代码主要是分离源文件的音频和视频分别保存在俩个文件中,分离后的文件并不能播放,需要在次合成才行,下面继续看下分析可播放的音频和视频

MediaMuxer的使用

这个需要MediaMuxer和MediaExtractor配合使用

分离视频数据并合成到新的MP4文件,新的MP4可播放
    public void video() {
        MediaMuxer mediaMuxer = null;
        try {
            MediaExtractor mediaExtractor = new MediaExtractor();
            //设置源文件
            mediaExtractor.setDataSource("/sdcard/aaapcm/text.mp4");
            //获取通道数
            int trackCount = mediaExtractor.getTrackCount();
            int videoTrackindex = -1;
            int framerate = -1;
            for (int i = 0; i < trackCount; i++) {
                MediaFormat trackFormat = mediaExtractor.getTrackFormat(i);
                String formatString = trackFormat.getString(MediaFormat.KEY_MIME);
                //找到视频通道
                if (formatString.startsWith("video/")) {
                    framerate = trackFormat.getInteger(MediaFormat.KEY_FRAME_RATE);
                    //选择视频通道
                    mediaExtractor.selectTrack(i);
                    //创建MediaMuxer
                    mediaMuxer = new MediaMuxer("/sdcard/aaapcm/video11.mp4", MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
                    //添加通道
                    videoTrackindex = mediaMuxer.addTrack(trackFormat);
                    //开始合成
                    mediaMuxer.start();
                    break;
                }
            }

            if (mediaMuxer == null) {
                return;
            }

            MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
            bufferInfo.presentationTimeUs = 0;

            ByteBuffer byteBuffer = ByteBuffer.allocate(500 * 1024);

            int i = 0;
            //从源文件读取视频数据
            while ((i = mediaExtractor.readSampleData(byteBuffer, 0)) > 0) {
                bufferInfo.offset = 0;
                bufferInfo.size = i;
                bufferInfo.flags = mediaExtractor.getSampleFlags();
                bufferInfo.presentationTimeUs += 1000 * 1000 / framerate;
                //把读取到的视频数据重新合成到新的文件
                mediaMuxer.writeSampleData(videoTrackindex, byteBuffer, bufferInfo);
                mediaExtractor.advance();
            }

            mediaExtractor.release();

            mediaMuxer.stop();
            mediaMuxer.release();

            Log.d("mmm", "分离视频结束");

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
分离音频数据并合成到新的wav文件,新的wav可播放
  public void audio() {
        MediaMuxer mediaMuxer = null;
        try {
            MediaExtractor mediaExtractor = new MediaExtractor();
            //添加源文件
            mediaExtractor.setDataSource("/sdcard/aaapcm/text.mp4");
            //获取通道数
            int trackCount = mediaExtractor.getTrackCount();
            int audioTrackindex = -1;
            int framerate = -1;
            for (int i = 0; i < trackCount; i++) {
                MediaFormat trackFormat = mediaExtractor.getTrackFormat(i);
                String formatString = trackFormat.getString(MediaFormat.KEY_MIME);
                //找到音频通道
                if (formatString.startsWith("audio/")) {
                    mediaExtractor.selectTrack(i);
                    //创建MediaMuxer
                    mediaMuxer = new MediaMuxer("/sdcard/aaapcm/video11.wav", MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
                    //添加通道
                    audioTrackindex = mediaMuxer.addTrack(trackFormat);
                    //开始合成
                    mediaMuxer.start();
                }

                if (formatString.startsWith("video/")) {
                    framerate = trackFormat.getInteger(MediaFormat.KEY_FRAME_RATE);
                    break;
                }


            }

            if (mediaMuxer == null) {
                return;
            }

            MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
            bufferInfo.presentationTimeUs = 0;

            ByteBuffer byteBuffer = ByteBuffer.allocate(500 * 1024);

            int i = 0;
            //分离音频数据
            while ((i = mediaExtractor.readSampleData(byteBuffer, 0)) > 0) {
                bufferInfo.offset = 0;
                bufferInfo.size = i;
                bufferInfo.flags = mediaExtractor.getSampleFlags();
                bufferInfo.presentationTimeUs += 1000 * 1000 / framerate;
                //把分离的音频数据合成到新的文件
                mediaMuxer.writeSampleData(audioTrackindex, byteBuffer, bufferInfo);
                mediaExtractor.advance();
            }

            mediaExtractor.release();

            mediaMuxer.stop();
            mediaMuxer.release();

            Log.d("mmm", "分离音频结束");

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
把刚才分离出来的视频文件和音频文件重新合成为音视频文件
    public void hecheng() {
        try {
            MediaExtractor videoExtractor = new MediaExtractor();
            videoExtractor.setDataSource("/sdcard/aaapcm/video11.mp4");
            int trackCount = videoExtractor.getTrackCount();
            int framerate = 0;
            MediaFormat videotrackFormat = null;
            for (int i = 0; i < trackCount; i++) {
                videotrackFormat = videoExtractor.getTrackFormat(i);
                String formatString = videotrackFormat.getString(MediaFormat.KEY_MIME);
                if (formatString.startsWith("video/")) {
                    videoExtractor.selectTrack(i);
                    framerate = videotrackFormat.getInteger(MediaFormat.KEY_FRAME_RATE);
                }
            }

            MediaExtractor audioExtractor = new MediaExtractor();
            audioExtractor.setDataSource("/sdcard/aaapcm/video11.wav");
            MediaFormat audiotrackFormat = null;
            int trackCount1 = audioExtractor.getTrackCount();
            for (int i = 0; i < trackCount1; i++) {
                audiotrackFormat = audioExtractor.getTrackFormat(i);
                String formatString = audiotrackFormat.getString(MediaFormat.KEY_MIME);
                if (formatString.startsWith("audio/")) {
                    audioExtractor.selectTrack(i);
                }
            }

            MediaCodec.BufferInfo videobufferInfo = new MediaCodec.BufferInfo();
            MediaCodec.BufferInfo audiobufferInfo = new MediaCodec.BufferInfo();


            MediaMuxer mediaMuxer = new MediaMuxer("sdcard/aaapcm/hecheng.mp4", MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
            int writeAudioTrackIndex = mediaMuxer.addTrack(audiotrackFormat);
            int writeVideoTrackIndex = mediaMuxer.addTrack(videotrackFormat);
            mediaMuxer.start();

            ByteBuffer byteBuffer = ByteBuffer.allocate(500 * 1024);
            int i;
            while ((i = videoExtractor.readSampleData(byteBuffer, 0)) > 0) {
                videobufferInfo.size = i;
                videobufferInfo.offset = 0;
                videobufferInfo.flags = videoExtractor.getSampleFlags();
                videobufferInfo.presentationTimeUs += 1000 * 1000 / framerate;
                mediaMuxer.writeSampleData(writeVideoTrackIndex, byteBuffer, videobufferInfo);
                videoExtractor.advance();
            }

            while ((i = audioExtractor.readSampleData(byteBuffer, 0)) > 0) {
                audiobufferInfo.size = i;
                audiobufferInfo.offset = 0;
                audiobufferInfo.flags = audioExtractor.getSampleFlags();
                audiobufferInfo.presentationTimeUs += 1000 * 1000 / framerate;
                mediaMuxer.writeSampleData(writeAudioTrackIndex, byteBuffer, audiobufferInfo);
                audioExtractor.advance();
            }

            videoExtractor.release();
            audioExtractor.release();
            mediaMuxer.stop();
            mediaMuxer.release();

            Log.d("mmm", "合成完毕");


        } catch (IOException e) {
            e.printStackTrace();
        }

    }

参考

https://blog.csdn.net/zhi184816/article/details/52514138
https://www.cnblogs.com/renhui/p/7478527.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值