截取很简单,只要用mediacodec进行解码解出pcm格式的数据,再把pcm数据用mediacodec进行编码或者用其他第三方的进行编码 。
上述就是android开发分享Android开发MediaCodec和lamemp3多段音频截取拼接的全部内容,如果对大家有所用处且需要了解更多关于Android学习教程,希望大家多多关注—编程笔记
拼接就比较麻烦,音频的音质会受到采样率,比特率和声道的影响,所以理想的状态是这三个属性要一样进行拼接才能保证音质 。
举个栗子,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。
记录音频的采样率,比特率,声道,截取的开始时间,截取的结束时间,路径和文件名
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; } }
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); }
#include#include #include "lamemp3/lame.h" #include #define inbufsize 4096 #define mp3bufsize (int) (1.25 * inbufsize) + 7200 extern "c" jniexport void jnicall java_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); }
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 listlist){ 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 = 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 = 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[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 listlist = new arraylist<>();
audiomerge.start(environment.getexternalstoragedirectory().getabsolutepath()+"/hmsdk/test_merge.mp3",list);
还有hmsdk这个文件夹自己创建或改成自己的,我都是保存在手机内是为了方便测试
github: https://github.com/a422070876/audiomerge
以上就是android开发mediacodec和lamemp3多段音频截取拼接的详细内容,更多关于mediacodec和lamemp3音频截取拼接的资料请关注<编程笔记>其它相关文章!