Idea Analysis
Intercepting is very simple. Just use MediaCodec to decode and decode the data in pcm format, and then encode the pcm data with MediaCodec or other third-party ones.
Splicing is more troublesome. The sound quality of the audio will be affected by the sampling rate, bit rate and channel. Therefore, the ideal state is that these three attributes must be spliced the same to ensure the sound quality.
For example, a and b are two songs with different sampling rates and bit rate and channel. To splice it into c, you must first set the sampling rate and bit rate and channel of c. Here you use a to set it, and then splice it. When playing c, you will find that the sound quality of part a is fine, and when it reaches part b, there will be problems with the sound quality.
It is very simple to solve this problem. Just convert the sampling rates, bit rates and channels of a and b into the same. This problem is easy to solve for audio and video developers, so I will write a tool that converts the sampling rate, bit rate and channel, or use ffmpeg.
I found several of them through github. After testing, I finally chose lamemp3. lamemp3 is written in C language. I won’t talk about how to compile it on the Internet. OK, let’s start with the topic.
First, let’s talk about the idea. First, decode the several audios to be processed into the pcm file through MediaCodec, then convert these pcm files into the sampling rate through lamemp3 and the bit rate is the same as the channel. Then combine these mp3s into one pcm data through MediaCodec. Finally, convert the pcm data into the format you want. You can use MediaCodec to convert it to aac or use lamemp3 and then convert it to mp3.
Attribute Class
Record audio sampling rate, bit rate, channel, intercept start time, end time of intercept, path and file name
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) { = file; } public String getName() { return name; } public void setName(String name) { = name; } public double getStart() { return start; } public void setStart(double start) { = start; } public double getEnd() { return end; } public void setEnd(double end) { = end; } public int getSampleRate() { return sampleRate; } public void setSampleRate(int sampleRate) { = sampleRate; } public int getChannelCount() { return channelCount; } public void setChannelCount(int channelCount) { = channelCount; } public int getBitRate() { return bitRate; } public void setBitRate(int bitRate) { = bitRate; } }
Call lamemp3 class
public class SimpleLame { static { ("native-lib"); } /** * pcm file conversion mp3 function */ public static native void convert(AudioEncoder encoder,String jwav, String jmp3, int inSampleRate, int outChannel, int outSampleRate, int outBitrate, int quality); }
#include <> #include <string> #include "lamemp3/" #include <sys/> #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]; // Get file size 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); }
Splicing operation category
public class AudioMerge { private static final String AUDIO = "audio/"; private Handler audioHandler; private HandlerThread audioThread; public AudioMerge(){ audioThread = new HandlerThread("AudioMerge"); (); audioHandler = new Handler(()); } private OnAudioEncoderListener encoderListener; public void setEncoderListener(OnAudioEncoderListener encoderListener) { = encoderListener; } public void start(final String path, final List<AudioHolder> list){ (new Runnable() { @Override public void run() { encoders(path,list); } }); } public void start(final String path, final List<AudioHolder> list,OnAudioEncoderListener 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; /** * Perform decoding and splicing */ private void encoders(String path,List<AudioHolder> list){ File file = new File(path); if(()){ (); } //Unified sampling rate, bit rate and channel int bitRate = (0).getBitRate(); int sampleRate = (0).getSampleRate(); int channelCount = (0).getChannelCount(); if(() != 1){ for (AudioHolder holder:list){ bitRate = (bitRate,()); sampleRate = (sampleRate,()); channelCount = (channelCount,()); } 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); } } //Temporary pcm file String pcm = ().getAbsolutePath()+"/HMSDK/"+()+".pcm"; List<String> mp3s = new ArrayList<>(); //Total duration, used to calculate progress long duration = 0; for (AudioHolder holder :list){ //When there is only 1 audio, it is directly transferred to mp3 String mp3; if(() == 1){ mp3 = path; decoderHolder = null; }else{ decoderHolder = holder; mp3 = ().getAbsolutePath()+"/HMSDK/"+()+".mp3"; } //Decode the audio into a pcm file duration += decoderPCM(holder,pcm); //Convert pcm file to mp3 (this,pcm,mp3 ,(), channelCount,sampleRate,bitRate, 1 ); (mp3); } //Open the operation with only one audio if(() == 1){ if(encoderListener != null){ (path); } return; } //The following can be replaced with other codes, such as converting MediaCodec to aac, because the sampling rate, bit rate and channel are the same file. decoderHolder = null; File f = new File(pcm); if(()){ (); } OutputStream pcmos = null; try { pcmos = new FileOutputStream(pcm); } catch (FileNotFoundException e) { (); } //Total file size long total = 0; for (String mp3 : mp3s){ //Convert mp3 to pcm file to return the size of the converted data total += encoderMP3(mp3,pcmos,total,duration); } try { (); (); } catch (IOException e) { (); } //Convert pcm file to mp3 (this,pcm,path ,sampleRate, channelCount,sampleRate,bitRate, 1 ); if(encoderListener != null){ (path); } } /** * Decode */ private long decoderPCM(AudioHolder holder,String pcm){ long startTime = (long) (()*1000*1000); long endTime = (long) (()*1000*1000); //Initialize MediaExtractor and MediaCodec MediaExtractor audioExtractor = new MediaExtractor(); MediaCodec audioDecoder = null; try { (()); for (int i = 0; i < (); i++) { MediaFormat format = (i); String mime = (MediaFormat.KEY_MIME); if((AUDIO)){ (i); audioTrackIndex = i; if(startTime != 0){ (startTime,audioTrackIndex); } audioDecoder = (mime); (format, null, null, 0); (); break; } } } catch (IOException e) { (); } File f = new File(pcm); if(()){ (); } //pcm file OutputStream pcmos = null; try { pcmos = new FileOutputStream(f); } catch (FileNotFoundException e) { (); } //The duration of this audio long duration = endTime - startTime; info = new (); while (true) { extractorInputBuffer(audioExtractor, audioDecoder); int outIndex = (info, 50000); if (outIndex >= 0) { ByteBuffer data = (outIndex); if (( & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) { = 0; } if ( != 0) { //Discern whether the decoded data is within the intercepted range if( >= startTime && <= endTime){ byte[] bytes = new byte[()]; (bytes,0,); (); //Write to pcm file try { (bytes); } catch (IOException e) { (); } //Progress bar if(encoderListener != null){ int progress = (int) ((( - startTime)*50)/duration); if(decoderHolder == null){ (progress); }else{ (decoderHolder,progress); } } } } (outIndex, false); //The decoding ends after the interception time is over if( >= endTime){ break; } } if (( & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { break; } } try { (); (); } catch (IOException e) { (); } (); (); (); return duration; } /** * mp3 to pcm */ private long encoderMP3(String mp3,OutputStream pcmos,long startTime,long duration){ long d = 0; MediaExtractor audioExtractor = new MediaExtractor(); MediaCodec audioDecoder = null; try { (mp3); for (int i = 0; i < (); i++) { MediaFormat format = (i); String mime = (MediaFormat.KEY_MIME); if((AUDIO)){ d = (MediaFormat.KEY_DURATION); (i); audioDecoder = (mime); (format, null, null, 0); (); break; } } } catch (IOException e) { (); } info = new (); while (true) { extractorInputBuffer(audioExtractor, audioDecoder); int outIndex = (info, 50000); if (outIndex >= 0) { ByteBuffer data = (outIndex); if (( & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) { = 0; } if ( != 0) { byte[] bytes = new byte[()]; (bytes,0,); (); try { (bytes); } catch (IOException e) { (); } if(encoderListener != null){ int progress = (int) ((( + startTime)*50)/duration); (progress); } } (outIndex, false); } if (( & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { break; } } (); (); (); return d; } private void extractorInputBuffer(MediaExtractor mediaExtractor, MediaCodec mediaCodec) { int inputIndex = (50000); if (inputIndex >= 0) { ByteBuffer inputBuffer = (inputIndex); long sampleTime = (); int sampleSize = (inputBuffer, 0); if (()) { (inputIndex, 0, sampleSize, sampleTime, 0); } else { if (sampleSize > 0) { (inputIndex, 0, sampleSize, sampleTime, MediaCodec.BUFFER_FLAG_END_OF_STREAM); } else { (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[ - 1]){ return fs[ - 1]; }else{ for (int i = 1; i < ;i++){ if(f >= fs[i]){ return fs[i]; } } } return -1; } /** * The progress bar function of jni callback, the progress bar accounts for 50 by decoding, and the pcm to mp3 accounts for 50 */ public void setProgress(long size,long total){ if(encoderListener != null){ int progress = 50 + (int) ((total*50)/size); if(decoderHolder == null){ (progress); }else{ (decoderHolder,progress); } } } public interface OnAudioEncoderListener{ void onDecoder(AudioHolder decoderHolder,int progress); void onEncoder(int progress); void onOver(String path); } }
use
private AudioMerge audioMerge = new AudioMerge(); private List<AudioHolder> list = new ArrayList<>();
(().getAbsolutePath()+"/HMSDK/test_merge.mp3",list);
There is also the HMSDK folder created or changed to my own. I saved it on my phone for the convenience of testing.
GitHub: /a422070876/AudioMerge
The above is the detailed content of the multi-segment audio interception and splicing of MediaCodec and lamemp3 developed by Android. For more information about MediaCodec and lamemp3 audio interception and splicing, please pay attention to my other related articles!