千家信息网

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

发表于:2025-01-19 作者:千家信息网编辑
千家信息网最后更新 2025年01月19日,这篇文章主要介绍了Android怎么开发MediaCodec和lamemp3多段音频截取拼接的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Android怎么开发Media
千家信息网最后更新 2025年01月19日Android怎么开发MediaCodec和lamemp3多段音频截取拼接

这篇文章主要介绍了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");    }    /**     * pcm文件转换mp3函数     */    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 #include #include "lamemp3/lame.h"#include #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 list){        audioHandler.post(new Runnable() {            @Override            public void run() {                encoders(path,list);            }        });    }    public void start(final String path, final List 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 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 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;    }    /**     * mp3转pcm     */    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;    }    /**     * jni回调的进度条函数,进度条以解码占50,pcm转mp3占50     */    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 list = new ArrayList<>();
audioMerge.start(Environment.getExternalStorageDirectory().getAbsolutePath()+"/HMSDK/test_merge.mp3",list);

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

文件 音频 声道 数据 开发 问题 音质 大小 时候 知识 进度 内容 函数 只有 属性 时间 格式 篇文章 编码 部分 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 数据库中保证数据及语义正确 无锡生鲜类的软件开发 广西专业软件开发排名 清扬软件开发公司 那里有培训网络安全的学校 做文明网民促网络安全 首钢互联网科技园 戴尔t110服务器raid 软件开发意见不合怎么处理 浙江浪潮服务器虚拟化系统 北京警察学院网络安全与执法 网络安全教育知识漫画 成都软件开发培训班哪里有 软件开发面试经验 事业单位服务器硬盘报废办法 服务器崩溃三大征兆 宝山区先进网络技术解决方案 点识网络技术上海有限公司招聘 省公安厅网络安全总队什么级别 山西专业软件开发设施有哪些 软件开发工程师需要做后台吗 网络安全 征文600字 信诚网络技术有限公司 centos7.6安装数据库 网络安全大赛全称 帮我找一些网络安全的手抄报 60经典服务器转服怎么把金带走 厦门创远互联网科技有限公司 公司局域网搭建存储服务器 服务器到底有没有显卡
0