美文网首页
音频裁剪

音频裁剪

作者: 毛先森 | 来源:发表于2021-02-19 15:37 被阅读0次

    目的

    准备一个封装格式的文件(mp4.mp3等),从中读取音频轨道的数据,使用 dsp 解码成 pcm 文件后,截取其中一段音频,保存成一个新的文件

    这个练习是为后面的视频混音做准备,熟悉相关 api

    步骤

    1. 通过 MediaExtractor 读取 mp3 文件的轨道信息
    2. 读取音频轨道的 pcm 裸流数据,并且保存成 pcm 文件
    3. 操作 pcm 文件,截取片段

    相关知识

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

    重要方法:


    1. setDataSource(String path) ,设置数据源
    2. getTrackCount(),获取轨道总数,音视频文件的轨道有视频轨道、音频轨道、字母轨道等,该方法用于遍历轨道信息
    3. MediaFormat getTrackFormat(int index),返回一个 MediaFormat 对象,其实内部是一个 Map
    4. seekTo(long timeUs, @SeekMode int mode),seek 到某个时间,对应音视频 app 上的进度条
    5. advance(), 前进到下一个样本,跳过当前
    6. getSampleTime(),获取当前样本的微秒时间
    7. readSampleData(@NonNull ByteBuffer byteBuf, int offset) 读取数据

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

    重要方法:


    相关链接

    用 MediaExtractor 和 MediaMuxer API 解析和封装 mp4 文件

    MediaExtractor与MediaCodec使用方法

    实践代码

    • 从视频文件中分离出音频轨道,并保存为 WAV
    
    /**
     * @author : kai.mao
     * @date :  2021/1/23
     */
    
    public class AudioClipHelper {
    
        private static AudioClipHelper INSTANCE;
        private MediaExtractor mMediaExtractor;
    
        private AudioClipHelper() {
        }
    
        public static AudioClipHelper getInstance() {
            if (INSTANCE == null) {
                INSTANCE = new AudioClipHelper();
            }
            return INSTANCE;
        }
    
        public void setDataSource(String filePath) {
            if (mMediaExtractor == null) {
                mMediaExtractor = new MediaExtractor();
            }
    
            try {
                mMediaExtractor.setDataSource(filePath);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
    
        public void readAuido(int startTime,int endTime) throws IOException {
            Log.e("David", "开始转换");
            int maxBufferSize;
            //1. 查找文件的音频轨道
            int audioIndex = selectAudioTrack(mMediaExtractor);
    
            //2. 指定音频轨道
            mMediaExtractor.selectTrack(audioIndex);
            mMediaExtractor.seekTo(startTime, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
            //3. 解码音频数据,由封转格式 -> PCM 裸数据
            MediaFormat originAudioFormat = mMediaExtractor.getTrackFormat(audioIndex);
            if (originAudioFormat.containsKey(MediaFormat.KEY_MAX_INPUT_SIZE)){
                maxBufferSize = originAudioFormat.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE);
            }else {
                maxBufferSize = 100 * 1000;
            }
    
            ByteBuffer buffer = ByteBuffer.allocateDirect(maxBufferSize);
            // 使用 MediaCodec 解码器解码音频数据
            MediaCodec mediaCodec = MediaCodec.createDecoderByType(originAudioFormat.getString(MediaFormat.KEY_MIME));
            mediaCodec.configure(originAudioFormat,null,null,0);
            File pcmFile = new File(Environment.getExternalStorageDirectory(), "out.pcm");
            FileChannel writeChannel = new FileOutputStream(pcmFile).getChannel();
            mediaCodec.start();
    
            MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
            int outputBufferIndex = -1;
    
            // 读取解码后的数据
            while (true){
                // 获取空闲的写入缓冲区
                int decodeInputIndex = mediaCodec.dequeueInputBuffer(100000);
                // 已经拿到可以使用的写入缓冲区
                if (decodeInputIndex >= 0) {
                    // 将预定时间间隔内的音频数据塞入缓冲区
                    long sampleTimeUs = mMediaExtractor.getSampleTime();
                     if (sampleTimeUs == -1){
                         break;
                     }else if (sampleTimeUs < startTime){
                         mMediaExtractor.advance();
                         continue;
                     }else if (sampleTimeUs > endTime){
                         break;
                     }
                    // 获取数据
                    info.size = mMediaExtractor.readSampleData(buffer, 0);
                    info.presentationTimeUs = sampleTimeUs;
                    info.flags = mMediaExtractor.getSampleFlags();
                    //  通过 remaining 方法高效读取数据
                    byte[] content = new byte[buffer.remaining()];
                    buffer.get(content);
                    FileUtils.writeContent(content);
                    ByteBuffer inputByteBuffer = mediaCodec.getInputBuffer(decodeInputIndex);
                    inputByteBuffer.put(content);
                    mediaCodec.queueInputBuffer(decodeInputIndex,0,info.size,info.presentationTimeUs,info.flags);
                    Log.e("David", "presentationTimeUs:"+sampleTimeUs);
                    mMediaExtractor.advance();
                }
                // 取出解码后的 pcm 裸数据
                outputBufferIndex = mediaCodec.dequeueOutputBuffer(info,100_000);
                while (outputBufferIndex >= 0){
                    ByteBuffer decodeOutputBuffer = mediaCodec.getOutputBuffer(outputBufferIndex);
                    writeChannel.write(decodeOutputBuffer);//MP3  1   pcm2
                    mediaCodec.releaseOutputBuffer(outputBufferIndex, false);
                    outputBufferIndex = mediaCodec.dequeueOutputBuffer(info, 100_000);
                }
            }
            writeChannel.close();
            mMediaExtractor.release();
            mediaCodec.stop();
            mediaCodec.release();
    
            File wavFile = new File(Environment.getExternalStorageDirectory(),"output.mp3" );
            new PcmToWavUtil(44100,  AudioFormat.CHANNEL_IN_STEREO,
                    2, AudioFormat.ENCODING_PCM_16BIT).pcmToWav(pcmFile.getAbsolutePath()
                    , wavFile.getAbsolutePath());
            Log.i("David", "mixAudioTrack: 转换完毕");
    
        }
    
    
    
        private int selectAudioTrack(MediaExtractor mediaExtractor) {
            int trackTotal = mediaExtractor.getTrackCount();
            for (int index = 0; index < trackTotal; index++) {
                MediaFormat mediaFormat = mediaExtractor.getTrackFormat(index);
                // 获取格式类型为 audio 的轨道
                if (mediaFormat.getString(MediaFormat.KEY_MIME).startsWith("audio/")) {
                    return index;
                }
            }
            return -1;
        }
    
    
    }
    
    
    

    相关文章

      网友评论

          本文标题:音频裁剪

          本文链接:https://www.haomeiwen.com/subject/esuaxltx.html