热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

android开发分享Android开发MediaCodec和lamemp3多段音频截取拼接

截取很简单,只要用mediacodec进行解码解出pcm格式的数据,再把pcm数据用mediacodec进行编码或者用其他第三方的进行编码。拼接就比较麻烦,音频的音质会受到采样率

截取很简单,只要用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。

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) + 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);  }

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 = 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 list list = new arraylist<>();
audiomerge.start(environment.getexternalstoragedirectory().getabsolutepath()+"/hmsdk/test_merge.mp3",list);  

还有hmsdk这个文件夹自己创建或改成自己的,我都是保存在手机内是为了方便测试

github: https://github.com/a422070876/audiomerge

以上就是android开发mediacodec和lamemp3多段音频截取拼接的详细内容,更多关于mediacodec和lamemp3音频截取拼接的资料请关注<编程笔记>其它相关文章!


推荐阅读
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • 1,关于死锁的理解死锁,我们可以简单的理解为是两个线程同时使用同一资源,两个线程又得不到相应的资源而造成永无相互等待的情况。 2,模拟死锁背景介绍:我们创建一个朋友 ... [详细]
  • Android源码深入理解JNI技术的概述和应用
    本文介绍了Android源码中的JNI技术,包括概述和应用。JNI是Java Native Interface的缩写,是一种技术,可以实现Java程序调用Native语言写的函数,以及Native程序调用Java层的函数。在Android平台上,JNI充当了连接Java世界和Native世界的桥梁。本文通过分析Android源码中的相关文件和位置,深入探讨了JNI技术在Android开发中的重要性和应用场景。 ... [详细]
  • Java在运行已编译完成的类时,是通过java虚拟机来装载和执行的,java虚拟机通过操作系统命令JAVA_HOMEbinjava–option来启 ... [详细]
  • 本文介绍了在使用FIS配置过程中遇到的问题以及解决方法。作者发现在配置roadmap时使用命令行参数出现了诡异现象,uglify了js文件后,html中对js的引用没有被修改。经过多次尝试和验证,联系了FIS开发人员后才得知,使用fis.config.merge会导致一些问题。通过将fis.config.merge改为fis.config.get('roadmap.path').unshift()来添加配置,问题得以解决。文章指出FIS官方文档解释不够详细,提供了解决问题的方法。 ... [详细]
  • 如何用JNI技术调用Java接口以及提高Java性能的详解
    本文介绍了如何使用JNI技术调用Java接口,并详细解析了如何通过JNI技术提高Java的性能。同时还讨论了JNI调用Java的private方法、Java开发中使用JNI技术的情况以及使用Java的JNI技术调用C++时的运行效率问题。文章还介绍了JNIEnv类型的使用方法,包括创建Java对象、调用Java对象的方法、获取Java对象的属性等操作。 ... [详细]
  • 本文介绍了GTK+中的GObject对象系统,该系统是基于GLib和C语言完成的面向对象的框架,提供了灵活、可扩展且易于映射到其他语言的特性。其中最重要的是GType,它是GLib运行时类型认证和管理系统的基础,通过注册和管理基本数据类型、用户定义对象和界面类型来实现对象的继承。文章详细解释了GObject系统中对象的三个部分:唯一的ID标识、类结构和实例结构。 ... [详细]
  • 本文概述了JNI的原理以及常用方法。JNI提供了一种Java字节码调用C/C++的解决方案,但引用类型不能直接在Native层使用,需要进行类型转化。多维数组(包括二维数组)都是引用类型,需要使用jobjectArray类型来存取其值。此外,由于Java支持函数重载,根据函数名无法找到对应的JNI函数,因此介绍了JNI函数签名信息的解决方案。 ... [详细]
  • C语言自带的快排和二分查找
    Author🚹:CofCaiEmail✉️:cai.dongjunnexuslink.cnQQ😙:1664866311personalPage&#x ... [详细]
  • C语言的经典程序有哪些
    本篇内容介绍了“C语言的经典程序有哪些”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何 ... [详细]
  • C语言注释工具及快捷键,删除C语言注释工具的实现思路
    本文介绍了C语言中注释的两种方式以及注释的作用,提供了删除C语言注释的工具实现思路,并分享了C语言中注释的快捷键操作方法。 ... [详细]
  • 本文介绍了使用PHP实现断点续传乱序合并文件的方法和源码。由于网络原因,文件需要分割成多个部分发送,因此无法按顺序接收。文章中提供了merge2.php的源码,通过使用shuffle函数打乱文件读取顺序,实现了乱序合并文件的功能。同时,还介绍了filesize、glob、unlink、fopen等相关函数的使用。阅读本文可以了解如何使用PHP实现断点续传乱序合并文件的具体步骤。 ... [详细]
  • 本文介绍了Linux Shell中括号和整数扩展的使用方法,包括命令组、命令替换、初始化数组以及算术表达式和逻辑判断的相关内容。括号中的命令将会在新开的子shell中顺序执行,括号中的变量不能被脚本余下的部分使用。命令替换可以用于将命令的标准输出作为另一个命令的输入。括号中的运算符和表达式符合C语言运算规则,可以用在整数扩展中进行算术计算和逻辑判断。 ... [详细]
  • 本文介绍了在Windows系统上使用C语言命令行参数启动程序并传递参数的方法,包括接收参数程序的代码和bat文件的编写方法,同时给出了程序运行的结果。 ... [详细]
  • 树莓派语音控制的配置方法和步骤
    本文介绍了在树莓派上实现语音控制的配置方法和步骤。首先感谢博主Eoman的帮助,文章参考了他的内容。树莓派的配置需要通过sudo raspi-config进行,然后使用Eoman的控制方法,即安装wiringPi库并编写控制引脚的脚本。具体的安装步骤和脚本编写方法在文章中详细介绍。 ... [详细]
author-avatar
H801_597
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有