代码先锋网 代码片段及技术文章聚合

Android音视频任务列表之(四)——学习 Android 平台的 MediaExtractor 和 MediaMuxer API,知道如何解析和封装 mp4 文件

技术标签: Android音视频  音视频  android  学习

一.主要使用的方法

MediaExtractor

1.setDataSource(String path)

设置数据源

2.getTrackFormat(int index)

获取指定索引通道的数据配置参数

3.selectTrack(int index)

选择轨道

4. getSampleTime()

获取当前pts时间戳,获取不到这返回-1

5.readSampleData(@NonNull ByteBuffer byteBuf, int offset)

数据存到缓存区byteBuf

6. advance()

释放上一帧数据

MediaMuxer

1.构造方法MediaMuxer( String path, int format)

初始化一个视频封装容器

2.addTrack(MediaFormat format)

添加指定格式通道

3.start()

开始合成

4. writeSampleData(int trackIndex, ByteBuffer byteBuf,BufferInfo bufferInfo)

将编码写入容器

二.实现过程

1.申请外置卡权限

2.解析Mp4

   @SuppressLint("WrongConstant")
    public void decodeToPCM(String musicPath, String outPath) throws Exception {
    //创建解压工具
        MediaExtractor mediaExtractor = new MediaExtractor();
        //设置数据源
        mediaExtractor.setDataSource(musicPath);
        //获取音频轨道
        int audioTrack =selectTrack(mediaExtractor,true);
        //选择音频轨道
        mediaExtractor.selectTrack(audioTrack);
        //得到配置信息
        MediaFormat oriAudioFormat = mediaExtractor.getTrackFormat(audioTrack);
        //缓存大小
        int maxBufferSize = 100 * 1000;
        if (oriAudioFormat.containsKey(MediaFormat.KEY_MAX_INPUT_SIZE)) {
            maxBufferSize = oriAudioFormat.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE);
        }
        ByteBuffer buffer = ByteBuffer.allocateDirect(maxBufferSize);
        MediaCodec mediaCodec = MediaCodec.createDecoderByType(oriAudioFormat.getString((MediaFormat.KEY_MIME)));
//        设置解码器信息 
        mediaCodec.configure(oriAudioFormat, null, null, 0);
        File pcmFile = new File(outPath);
        FileChannel writeChannel = new FileOutputStream(pcmFile).getChannel();
        //开始解码
        mediaCodec.start();
        MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
        int outputBufferIndex = -1;
        while (true) {
            int decodeInputIndex = mediaCodec.dequeueInputBuffer(0);
            if (decodeInputIndex >= 0) {
                long sampleTimeUs = mediaExtractor.getSampleTime();
                if (sampleTimeUs == -1) {//结束了
                    break;
                }else if (sampleTimeUs > 6000000) {//截取一段
                    break;
                }
//                数据存到缓冲区
                info.size = mediaExtractor.readSampleData(buffer, 0);
                info.presentationTimeUs = sampleTimeUs;//时间
                info.flags = mediaExtractor.getSampleFlags();
//                下面放数据到content  到dsp解码
                byte[] content = new byte[buffer.remaining()];
                buffer.get(content);
//                解码
                ByteBuffer inputBuffer = mediaCodec.getInputBuffer(decodeInputIndex);
                inputBuffer.put(content);
                mediaCodec.queueInputBuffer(decodeInputIndex, 0, info.size, info.presentationTimeUs, info.flags);
//                释放上一帧的压缩数据
                mediaExtractor.advance();
            }

            outputBufferIndex = mediaCodec.dequeueOutputBuffer(info, 100_000);
            while (outputBufferIndex>=0) {
                ByteBuffer decodeOutputBuffer = mediaCodec.getOutputBuffer(outputBufferIndex);
                writeChannel.write(decodeOutputBuffer);//pcm
                mediaCodec.releaseOutputBuffer(outputBufferIndex, false);
                outputBufferIndex = mediaCodec.dequeueOutputBuffer(info, 100_000);
            }

        }
        writeChannel.close();
        mediaExtractor.release();
        mediaCodec.stop();
        mediaCodec.release();
    }

3.封装MP4

 @SuppressLint("WrongConstant")
    public boolean process(String musicPath, String outPath) throws IOException {
        MediaExtractor   mMediaExtractor = new MediaExtractor();
        //设置音频数据源
        mMediaExtractor.setDataSource(musicPath);
        int mVideoTrackIndex = -1;
        int framerate = 0;
        //选择视频轨道
        int videoIndex= selectTrack(mMediaExtractor,false);
        //获取视频轨道
        MediaFormat format = mMediaExtractor.getTrackFormat(videoIndex);
        framerate = format.getInteger(MediaFormat.KEY_FRAME_RATE);
        //切换到视频通道
        mMediaExtractor.selectTrack(videoIndex);
        //        初始化一个视频封装容器
        mMediaMuxer = new MediaMuxer(outPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
        //        添加一个空的轨道  轨道格式取自 视频文件,跟视频所有信息一样
        mVideoTrackIndex = mMediaMuxer.addTrack(format);
        mMediaMuxer.start();
        if (mMediaMuxer == null) {
            return false;
        }
        int   maxBufferSize = format.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE);

        MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
        ByteBuffer buffer = ByteBuffer.allocate(maxBufferSize);
        //封装容器添加视频轨道信息
        while (true) {
            long sampleTimeUs = mMediaExtractor.getSampleTime();
            if (sampleTimeUs == -1) {
                break;
            }
            if (sampleTimeUs < 20000) {
            //设置一个时间,这个时间之前的数据丢弃
                mMediaExtractor.advance();
                continue;
            }
            if (  sampleTimeUs > 60000000) {
              //大于这个时间结束解析
                break;
            }
//                pts
            info.presentationTimeUs = sampleTimeUs - 20000+600;
            info.flags = mMediaExtractor.getSampleFlags();
//                读取视频文件的数据
            info.size = mMediaExtractor.readSampleData(buffer, 0);
            if (info.size < 0) {
                break;
            }
//                视频轨道 
            mMediaMuxer.writeSampleData(videoIndex, buffer, info);
            mMediaExtractor.advance();
        }
        Log.i("TAG", "mixAudioTrack: 完成");
        mMediaExtractor.release();
        mMediaMuxer.stop();
        mMediaMuxer.release();
        return true;
    }
    

结果

1.获取到PCM数据输出到outPath
2.截取一段MP4输出到outPath

版权声明:本文为u010350891原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/u010350891/article/details/124787450

智能推荐

Android 视频分离和合成(MediaMuxer和MediaExtractor)

原文:https://blog.csdn.net/zhi184816/article/details/52514138#commentsedit 另外导入的项目的时候,需要将工程input.mp4导入sd卡中,放在根目录里,由代码可以知道。  理论上来,我们可以利用的视频的分离和合成可以做很多事,比如裁剪视频大小,拼凑视频,对一段视频加入背景等。 MediaExtractor Media...

Android 视频分离和合成(MediaMuxer和MediaExtractor)

Android 视频的分离和合成这方面的资源网上是少之又少,学习的过程也是各种demo的看,各种demo的实验,各种坑的踩,为了让大家能够android的视频的分离和合成又个了解,写了下面的例子,完整例子再最下面。例子写的比较草,中间有较多的容错处理和代码的重构,也就懒得做了。另外导入的项目的时候,需要将工程input.mp4导入sd卡中,放在根目录里,由代码可以知道。  理论上来,我们...

Android音视频处理之MediaMuxer(MP4)

http://www.jianshu.com/p/aeadf260258a 在Android中,可以使用MediaMuxer来封装编码后的视频流和音频流到mp4容器中: MediaMuxer facilitates muxing elementary streams. Currently supports mp4 or webm file as the output and at most one...

使用 MediaExtractor + MediaMuxer 多个视频合成MP4

在上一篇文章OpenGLES + MediaCodec 短视频分段录制实现与无丢帧录制优化中说到了MediaCodec录制视频的一些优化思路。经过上一步,我们就实现了短视频的分段录制功能以及录制视频帧率的优化。在得到多段视频之后,我们如何将这些细小的视频文件合成一个视频文件呢? 这是有好多种方法,比如通过MP4Parser、FFmpeg等进行合成。这里介绍如何使用Android原生的MediaEx...

使用MediaExtractor+MediaCodec+MediaMuxer实现视频截取和拼接

Android中使用MediaExtractor+MediaCodec+MediaMuxer实现将本地视频解码截取再和另外的视频进行拼接编码合成一个视频,主要有两个类。 下面是解码相关的类VideoDecoder.java 下面是将解码数据编码合成MP4文件的类 VideoEncoderFromBuffer.java 调用方法也很简单,如下: 到时候注意把里面的视频文件路径替换一下就可以了...

猜你喜欢

使用MediaExtractor和MediaMuxer完成视频的抽取并生成新的视频

不多说,上代码 这里是关于对视频抽取的直接操作 这里是简单的利用systemvideoview完成视频的播放,由于需要拷贝到本地存储,所以略微复杂了一些 总结: 1.拷贝到本地存储起来并完成对本地视频文件的读写,需要完成权限的申请。manifest文件里还要申请权限就不列出来了。 2.使用systemvideoview或mediaplayer,直接利用文件的绝对路径也无法完成最后的播放,因为会抛出...

音视频开发之旅(五)MediaExtractor MediaMuxer 实现视频的解封装与合成

目录 MediaExtractor MediaMuxer 能做什么 视频解封装和合成的API以及流程介绍 三个实践(视频解封装提取纯音轨和视频轨文件、再合成新视频、给视频换个背景音) 遇到的问题 收获 一、有什么实际应用 在我们日常使用短视频软件的时候,对视频的裁剪,拼凑,加入背景是很常用的操作,这些功能是如何实现的呐?其实是将视频多信道的分离出来,比如音轨和视频轨道分隔出来,可以做到二次合成。 ...

Android 用MediaCodec ,MediaExtractor解码播放MP4文件

上一篇讲了如何采集摄像头画面并且进行编码,再进行封装成MP4格式文件,如需了解可以看 安卓采集摄像头画面生成MP4文件 本篇博客,主要讲解如何对MP4文件进行解封装,再进行解H264码流,进行画面显示。 Android 使用MediaCodec进行视频编解码工作,这里解码当然还由其来完成,那从MP4文件中提取出H264码流的工作,由MediaExtractor完成 MediaExtractor的使...

Android在NDK JNI中使用MediaMuxer合成mp4的流程

前言:找了很久,很少有NDK层使用合成mp4的例程,由于我们使用的是实时流合成mp4的文件,基本所有的视频流都是在C++操作的。因此写个笔记记录一下对实时流合成mp4格式视频。 1、要使用的对象: 2、创建对象:这里需要注意的是,一定要设置好视频流的vps/sps/pps数据。否则无法生生mp4文件。其中h265只需要使用csd_0。 3、写入数据:循环写入数据即可。这里写入的数据是h264/h2...

Laravel中自定义guard,自定义Auth的attempt方法

这个今天算是踩到坑了。 将普通用户和管理员用户分别放在不同的表里。 那么,前台和后台登陆时, 认证的东东就要分开。 开始还顺利, 等认证完成之后, 却无法获取登陆的用户。 卡了一小时,找到讲法, Auth后要带自定义的guard,才可以得到正确的登陆用户。 OMG。。...