文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Android怎么开发MediaCodec和lamemp3多段音频截取拼接

2023-06-30 10:57

关注

这篇文章主要介绍了Android怎么开发MediaCodec和lamemp3多段音频截取拼接的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Android怎么开发MediaCodec和lamemp3多段音频截取拼接文章都会有所收获,下面我们一起来看看吧。

截取很简单,只要用MediaCodec进行解码解出pcm格式的数据,再把pcm数据用MediaCodec进行编码或者用其他第三方的进行编码 。

拼接就比较麻烦,音频的音质会受到采样率,比特率和声道的影响,所以理想的状态是这三个属性要一样进行拼接才能保证音质 。

举个栗子,a和b是两首采样率,比特率和声道都不一样的歌,要拼接成c,首先要设置c的采样率,比特率和声道,这里用a的来进行设置,然后拼接,播放c的时候会发现a部分的音质是没问题的,到了b部分的时候音质就会出现问题。

解决这个问题很简单,先把a和b的采样率,比特率和声道都转成一样就可以了。对于音视频开发的人来说这个问题很好解决,就写个转换采样率,比特率和声道的工具,或者使用 ffmpeg。

通过github找到了几个,经过测试最后选择了lamemp3,lamemp3是c语言写的,怎么编译网上很多就不说了,好了开始正题 。

首先说说思路,先通过MediaCodec把要处理的几个音频解码出pcm文件,再把这些pcm文件通过lamemp3转成采样率,比特率和声道一样的mp3,再通过MediaCodec把这些mp3合并成一个pcm数据,最后就是把这个pcm数据转成自己想要的格式,可以用MediaCodec转成aac或者用lamemp3再转成mp3。

AudioHolder.java属性类

记录音频的采样率,比特率,声道,截取的开始时间,截取的结束时间,路径和文件名

public class AudioHolder {    private String file;    private String name;    private double start;    private double end;    private int sampleRate;    private int channelCount;    private int bitRate;    private String mp3;    public void setMp3(String mp3) {        this.mp3 = mp3;    }    public String getMp3() {        return mp3;    }    public String getFile() {        return file;    }    public void setFile(String file) {        this.file = file;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public double getStart() {        return start;    }    public void setStart(double start) {        this.start = start;    }    public double getEnd() {        return end;    }    public void setEnd(double end) {        this.end = end;    }    public int getSampleRate() {        return sampleRate;    }    public void setSampleRate(int sampleRate) {        this.sampleRate = sampleRate;    }    public int getChannelCount() {        return channelCount;    }    public void setChannelCount(int channelCount) {        this.channelCount = channelCount;    }    public int getBitRate() {        return bitRate;    }    public void setBitRate(int bitRate) {        this.bitRate = bitRate;    }}

SimpleLame.java调用lamemp3类

public class SimpleLame {    static {        System.loadLibrary("native-lib");    }        public static native void convert(AudioEncoder encoder,String jwav, String jmp3,                                      int inSampleRate, int outChannel, int outSampleRate, int outBitrate,                                      int quality);}

native-lib.cpp

#include <jni.h>#include <string>#include "lamemp3/lame.h"#include <sys/stat.h>#define INBUFSIZE 4096#define MP3BUFSIZE (int) (1.25 * INBUFSIZE) + 7200extern "C"JNIEXPORT void JNICALLJava_com_hyq_hm_audiomerge_lame_SimpleLame_convert(JNIEnv *env, jclass type, jobject encoder,                                                   jstring jwav_,jstring jmp3_,                                                   jint inSampleRate,jint outChannel,                                                   jint outSampleRate,jint outBitrate,                                                   jint quality) {    const char *jwav = env->GetStringUTFChars(jwav_, 0);    const char *jmp3 = env->GetStringUTFChars(jmp3_, 0);    // TODO    short int wav_buffer[INBUFSIZE*outChannel];    unsigned char mp3_buffer[MP3BUFSIZE];//    获取文件大小    struct stat st;    stat(jwav, &st );    jclass cls = env->GetObjectClass(encoder);    jmethodID mid = env->GetMethodID(cls, "setProgress", "(JJ)V");    FILE* fwav = fopen(jwav,"rb");    FILE* fmp3 = fopen(jmp3,"wb");    lame_t lameConvert =  lame_init();    lame_set_in_samplerate(lameConvert , inSampleRate);    lame_set_out_samplerate(lameConvert, outSampleRate);    lame_set_num_channels(lameConvert,outChannel);//    lame_set_VBR(lameConvert,vbr_mtrh);//    lame_set_VBR_mean_bitrate_kbps(lameConvert,outBitrate);    lame_set_brate(lameConvert,outBitrate);    lame_set_quality(lameConvert, quality);    lame_init_params(lameConvert);    int read ; int write;    long total=0;    do{        read = (int) fread(wav_buffer, sizeof(short int) * outChannel, INBUFSIZE, fwav);        total +=  read* sizeof(short int)*outChannel;        env->CallVoidMethod(encoder,mid,(long)st.st_size,total);        if(read!=0){            if (outChannel == 2){                write = lame_encode_buffer_interleaved(lameConvert,wav_buffer,read,mp3_buffer,MP3BUFSIZE);            }else{                write = lame_encode_buffer(lameConvert,wav_buffer,wav_buffer,read,mp3_buffer,MP3BUFSIZE);            }        } else{            write = lame_encode_flush(lameConvert,mp3_buffer,MP3BUFSIZE);        }        fwrite(mp3_buffer, sizeof(unsigned char), (size_t) write, fmp3);    }while (read!=0);    lame_mp3_tags_fid(lameConvert,fmp3);    lame_close(lameConvert);    fclose(fwav);    fclose(fmp3);    env->ReleaseStringUTFChars(jwav_, jwav);    env->ReleaseStringUTFChars(jmp3_, jmp3);}

AudioMerge.java拼接操作类

public class AudioMerge {    private static final String AUDIO = "audio/";    private Handler audioHandler;    private HandlerThread audioThread;    public AudioMerge(){        audioThread = new HandlerThread("AudioMerge");        audioThread.start();        audioHandler = new Handler(audioThread.getLooper());    }    private OnAudioEncoderListener encoderListener;    public void setEncoderListener(OnAudioEncoderListener encoderListener) {        this.encoderListener = encoderListener;    }    public void start(final String path, final List<AudioHolder> list){        audioHandler.post(new Runnable() {            @Override            public void run() {                encoders(path,list);            }        });    }    public void start(final String path, final List<AudioHolder> list,OnAudioEncoderListener encoderListener){        this.encoderListener = encoderListener;        start(path,list);    }    private static int[] SampleRates = {48000,44100,32000,24000,22050,16000,12000,11025,8000};    private static int[] Mpeg1BitRates = {320,256,224,192,160,128,112,96,80,64,56,48,40,32};    private static int[] Mpeg2BitRates = {160,144,128,112,96,80,64,56,48,40,32,24,16,8};    private static int[] Mpeg25BitRates = {64,56,48,40,32,24,16,8};    private int audioTrackIndex;    private AudioHolder decoderHolder = null;        private void encoders(String path,List<AudioHolder> list){        File file = new File(path);        if(file.exists()){            file.delete();        }        //统一采样率,比特率和声道        int bitRate = list.get(0).getBitRate();        int sampleRate = list.get(0).getSampleRate();        int channelCount = list.get(0).getChannelCount();        if(list.size() != 1){            for (AudioHolder holder:list){                bitRate = Math.min(bitRate,holder.getBitRate());                sampleRate = Math.min(sampleRate,holder.getSampleRate());                channelCount = Math.min(channelCount,holder.getChannelCount());            }            sampleRate = format(sampleRate,SampleRates);            if(sampleRate >= SampleRates[2]){                bitRate = format(bitRate,Mpeg1BitRates);            }else if(sampleRate <= SampleRates[6]){                bitRate = format(bitRate,Mpeg25BitRates);            }else{                bitRate = format(bitRate,Mpeg2BitRates);            }        }        //临时用的pcm文件        String pcm = Environment.getExternalStorageDirectory().getAbsolutePath()+"/HMSDK/"+System.currentTimeMillis()+".pcm";        List<String> mp3s = new ArrayList<>();        //总时长,用来计算进度用的        long duration = 0;        for (AudioHolder holder :list){            //只有1个音频的时候直接转mp3            String mp3;            if(list.size() == 1){                mp3 = path;                decoderHolder = null;            }else{                decoderHolder = holder;                mp3 = Environment.getExternalStorageDirectory().getAbsolutePath()+"/HMSDK/"+System.currentTimeMillis()+".mp3";            }            //将音频解码成pcm文件            duration += decoderPCM(holder,pcm);            //把pcm文件转成mp3            SimpleLame.convert(this,pcm,mp3                    ,holder.getSampleRate(),                    channelCount,sampleRate,bitRate,                    1            );            mp3s.add(mp3);        }        //只有一个音频就完成操作        if(list.size() == 1){            if(encoderListener != null){                encoderListener.onOver(path);            }            return;        }        //以下可换成其他代码,比如用MediaCodec转成aac,因为采样率,比特率和声道都是一样的文件        decoderHolder = null;        File f = new File(pcm);        if(f.exists()){            f.delete();        }        OutputStream pcmos = null;        try {            pcmos = new FileOutputStream(pcm);        } catch (FileNotFoundException e) {            e.printStackTrace();        }        //文件总大小        long total = 0;        for (String mp3 : mp3s){            //将mp3转成pcm文件返回转换数据的大小            total += encoderMP3(mp3,pcmos,total,duration);        }        try {            pcmos.flush();            pcmos.close();        } catch (IOException e) {            e.printStackTrace();        }        //把pcm文件转成mp3        SimpleLame.convert(this,pcm,path                ,sampleRate,                channelCount,sampleRate,bitRate,                1        );        if(encoderListener != null){            encoderListener.onOver(path);        }    }        private long decoderPCM(AudioHolder holder,String pcm){        long startTime = (long) (holder.getStart()*1000*1000);        long endTime = (long) (holder.getEnd()*1000*1000);        //初始化MediaExtractor和MediaCodec        MediaExtractor audioExtractor = new MediaExtractor();        MediaCodec audioDecoder = null;        try {            audioExtractor.setDataSource(holder.getFile());            for (int i = 0; i < audioExtractor.getTrackCount(); i++) {                MediaFormat format = audioExtractor.getTrackFormat(i);                String mime = format.getString(MediaFormat.KEY_MIME);                if(mime.startsWith(AUDIO)){                    audioExtractor.selectTrack(i);                    audioTrackIndex = i;                    if(startTime != 0){                        audioExtractor.seekTo(startTime,audioTrackIndex);                    }                    audioDecoder = MediaCodec.createDecoderByType(mime);                    audioDecoder.configure(format, null, null, 0);                    audioDecoder.start();                    break;                }            }        } catch (IOException e) {            e.printStackTrace();        }        File f = new File(pcm);        if(f.exists()){            f.delete();        }        //pcm文件        OutputStream pcmos = null;        try {            pcmos = new FileOutputStream(f);        } catch (FileNotFoundException e) {            e.printStackTrace();        }        //这段音频的时长        long duration = endTime - startTime;        MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();        while (true) {            extractorInputBuffer(audioExtractor, audioDecoder);            int outIndex = audioDecoder.dequeueOutputBuffer(info, 50000);            if (outIndex >= 0) {                ByteBuffer data = audioDecoder.getOutputBuffer(outIndex);                if ((info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {                    info.size = 0;                }                if (info.size != 0) {                    //判断解码出来的数据是否在截取的范围内                    if(info.presentationTimeUs >= startTime && info.presentationTimeUs <= endTime){                        byte[] bytes = new byte[data.remaining()];                        data.get(bytes,0,bytes.length);                        data.clear();                        //写入pcm文件                        try {                            pcmos.write(bytes);                        } catch (IOException e) {                            e.printStackTrace();                        }                        //进度条                        if(encoderListener != null){                            int progress = (int) (((info.presentationTimeUs - startTime)*50)/duration);                            if(decoderHolder == null){                                encoderListener.onEncoder(progress);                            }else{                                encoderListener.onDecoder(decoderHolder,progress);                            }                        }                    }                }                audioDecoder.releaseOutputBuffer(outIndex, false);                //超过截取时间结束解码                if(info.presentationTimeUs >= endTime){                    break;                }            }            if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {                break;            }        }        try {            pcmos.flush();            pcmos.close();        } catch (IOException e) {            e.printStackTrace();        }        audioDecoder.stop();        audioDecoder.release();        audioExtractor.release();        return duration;    }        private long encoderMP3(String mp3,OutputStream pcmos,long startTime,long duration){        long d = 0;        MediaExtractor audioExtractor = new MediaExtractor();        MediaCodec audioDecoder = null;        try {            audioExtractor.setDataSource(mp3);            for (int i = 0; i < audioExtractor.getTrackCount(); i++) {                MediaFormat format = audioExtractor.getTrackFormat(i);                String mime = format.getString(MediaFormat.KEY_MIME);                if(mime.startsWith(AUDIO)){                    d = format.getLong(MediaFormat.KEY_DURATION);                    audioExtractor.selectTrack(i);                    audioDecoder = MediaCodec.createDecoderByType(mime);                    audioDecoder.configure(format, null, null, 0);                    audioDecoder.start();                    break;                }            }        } catch (IOException e) {            e.printStackTrace();        }        MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();        while (true) {            extractorInputBuffer(audioExtractor, audioDecoder);            int outIndex = audioDecoder.dequeueOutputBuffer(info, 50000);            if (outIndex >= 0) {                ByteBuffer data = audioDecoder.getOutputBuffer(outIndex);                if ((info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {                    info.size = 0;                }                if (info.size != 0) {                    byte[] bytes = new byte[data.remaining()];                    data.get(bytes,0,bytes.length);                    data.clear();                    try {                        pcmos.write(bytes);                    } catch (IOException e) {                        e.printStackTrace();                    }                    if(encoderListener != null){                        int progress = (int) (((info.presentationTimeUs + startTime)*50)/duration);                        encoderListener.onEncoder(progress);                    }                }                audioDecoder.releaseOutputBuffer(outIndex, false);            }            if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {                break;            }        }        audioDecoder.stop();        audioDecoder.release();        audioExtractor.release();        return d;    }    private void extractorInputBuffer(MediaExtractor mediaExtractor, MediaCodec mediaCodec) {        int inputIndex = mediaCodec.dequeueInputBuffer(50000);        if (inputIndex >= 0) {            ByteBuffer inputBuffer = mediaCodec.getInputBuffer(inputIndex);            long sampleTime = mediaExtractor.getSampleTime();            int sampleSize = mediaExtractor.readSampleData(inputBuffer, 0);            if (mediaExtractor.advance()) {                mediaCodec.queueInputBuffer(inputIndex, 0, sampleSize, sampleTime, 0);            } else {                if (sampleSize > 0) {                    mediaCodec.queueInputBuffer(inputIndex, 0, sampleSize, sampleTime, MediaCodec.BUFFER_FLAG_END_OF_STREAM);                } else {                    mediaCodec.queueInputBuffer(inputIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);                }            }        }    }    private int format(int f,int[] fs){        if(f >= fs[0]){            return fs[0];        }else if(f <= fs[fs.length - 1]){            return fs[fs.length - 1];        }else{            for (int i = 1; i < fs.length;i++){                if(f >= fs[i]){                    return fs[i];                }            }        }        return -1;    }        public void setProgress(long size,long total){        if(encoderListener != null){            int progress = 50 + (int) ((total*50)/size);            if(decoderHolder == null){                encoderListener.onEncoder(progress);            }else{                encoderListener.onDecoder(decoderHolder,progress);            }        }    }    public interface OnAudioEncoderListener{        void onDecoder(AudioHolder decoderHolder,int progress);        void onEncoder(int progress);        void onOver(String path);    }}

使用

private AudioMerge audioMerge = new AudioMerge();private List<AudioHolder> list = new ArrayList<>();
audioMerge.start(Environment.getExternalStorageDirectory().getAbsolutePath()+"/HMSDK/test_merge.mp3",list);

关于“Android怎么开发MediaCodec和lamemp3多段音频截取拼接”这篇文章的内容就介绍到这里,感谢各位的阅读!相信大家对“Android怎么开发MediaCodec和lamemp3多段音频截取拼接”知识都有一定的了解,大家如果还想学习更多知识,欢迎关注编程网行业资讯频道。

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯