音频播放全是噪声。思考:1、解码处没有正确解码;2、内存拷贝处地址有问题。
验证:1、解码是否正确
方法:将解码后的数据写入PCM文件中,再测试
结果:下载了cool edit这个软件,将PCM文件直接转换为mp3文件,可正常播放,说明不是解码的问题。
2、内存拷贝处 memcpy(audioBuf,audioBuffer+audio_offset,size);
仔细分析了一下len=avcodec_decode_audio2(ffmpeg_audio.pCodecCtx,(int16_t *)audioBuffer,&out_size,pktdata,pktsize)这句话,发现自己居然不知道audioBuffer是
这次解码的一段内存,还是所有内存。查阅资料,发现audioBuffer只是一段内存,avcodec_decode_audio2每次只解码audioBuffer大小的数据,所以在内存拷贝处犯了错误。
解决办法:额,非常复杂。。。
鉴于此,想到换一种思路,在c层直接利用AudioTrack播放。如果直接在底层播放的话,就不需要把这段内存再传给JAVA层,也就是说这段代码多余了,去掉,也节省了相当大的内存空间。
每次解码一个audioBuffer大小的数据,解码完成后,直接传给AudioTrack播放,每次解码就播放一次,不用等到所有解码完成后再播放,延时性大大提高,但是,问题是这样解一段播一段中间会不会有很大的间隙?
先看GetMethodID,最后一个参数是方法的定义,也就是"参数,返回类型"。
直接播放PCM文件,噪声,用cool edit播放,正确。修改cool edit设置的参数:采样率、声道数等,发觉也不能播放,且是相同的噪声,所以,基本断定是由于AudioTrack的初始化参数设置问题。按照cool edit能正常播放的参数来初始化AudioTrack,修改后能正确播放。现在思考:那对任一个音频文件,怎么拿到它的采样率、声道数了?
1 void Java_com_churnlabs_ffmpegsample_MainActivity_processAudio(JNIEnv* env,jobject this){ 2 static jbyteArray buffer; 3 static jobject audio_track; 4 static jint buffer_size; 5 static jmethodID method_write; 6 //根据FindClass找到AudioTrack这个类 7 jclass audio_track_cls = (*env)->FindClass(env,"android/media/AudioTrack"); 8 //根据GetStaticMethodID找到类的静态方法 9 //类定义、方法名称和方法的定义,方法的定义可以用jdk中带的javap工具反编译class文件获取 10 jmethodID min_buff_size_id = (*env)->GetStaticMethodID( 11 env, 12 audio_track_cls, 13 "getMinBufferSize", 14 "(III)I"); 15 //根据CallStaticIntMethod得到此静态方法的返回值 16 buffer_size = (*env)->CallStaticIntMethod(env,audio_track_cls,min_buff_size_id, 17 44100, 18 3, /*CHANNEL_CONFIGURATION_STEREO*/ 19 2); /*ENCODING_PCM_16BIT*/ 20 LOGI("buffer_size=%i",buffer_size); 21 22 //在C中新建一个ByteArray数组,名为buffer 23 buffer = (*env)->NewByteArray(env,buffer_size/4); 24 25 char buf[buffer_size/4]; 26 27 //找到此类的初始化构造方法 28 jmethodID constructor_id = (*env)->GetMethodID(env,audio_track_cls, "<init>", 29 "(IIIIII)V"); 30 //新建一个类对象 31 //在AudioFomart这个类里找每个类型的数值 32 audio_track = (*env)->NewObject(env,audio_track_cls, 33 constructor_id, 34 3, /*AudioManager.STREAM_MUSIC*/ 35 44100, /*sampleRateInHz*/ 36 3, /*CHANNEL_CONFIGURATION_STEREO*/ 37 2, /*ENCODING_PCM_16BIT*/ 38 buffer_size, /*bufferSizeInBytes*/ 39 1 /*AudioTrack.MODE_STREAM*/ 40 ); 41 42 //setvolume find是找到该方法,call是调用该方法 43 jmethodID setStereoVolume = (*env)->GetMethodID(env,audio_track_cls,"setStereoVolume","(FF)I"); 44 (*env)->CallIntMethod(env,audio_track,setStereoVolume,1.0,1.0); 45 46 //play 47 jmethodID method_play = (*env)->GetMethodID(env,audio_track_cls, "play", "()V"); 48 (*env)->CallVoidMethod(env,audio_track, method_play); 49 50 //write 51 method_write = (*env)->GetMethodID(env,audio_track_cls,"write","([BII)I"); 52 53 FILE* fp = fopen("/sdcard/result.pcm","rb"); 54 LOGE("after open"); 55 while(!feof(fp)){ 56 //feof()这个函数是用来判断指针是否已经到达文件尾部的。 57 //fread从文件流fp中读取200个元素,每个元素sizeof(char)个字节,读取的数据存入buf中;返回值是200 58 jint read= fread(buf,sizeof(char),200,fp); 59 //将本地类型的buf转换成jbyteArray类型的buffer 60 (*env)->SetByteArrayRegion(env,buffer, 0,read,(jbyte *)buf); 61 //CallIntMethod中Int是方法的返回值,也可能是CallVoidMethod等类型的 62 (*env)->CallIntMethod(env,audio_track,method_write,buffer,0,read); 63 } 64 fclose(fp); 65 }