您好,登錄后才能下訂單哦!
這篇文章主要介紹了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。
記錄音頻的采樣率,比特率,聲道,截取的開始時間,截取的結束時間,路徑和文件名
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 <jni.h> #include <string> #include "lamemp3/lame.h" #include <sys/stat.h> #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 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; } /** * 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<AudioHolder> list = new ArrayList<>();
audioMerge.start(Environment.getExternalStorageDirectory().getAbsolutePath()+"/HMSDK/test_merge.mp3",list);
關于“Android怎么開發MediaCodec和lamemp3多段音頻截取拼接”這篇文章的內容就介紹到這里,感謝各位的閱讀!相信大家對“Android怎么開發MediaCodec和lamemp3多段音頻截取拼接”知識都有一定的了解,大家如果還想學習更多知識,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。