一、前言
最近写一个Android项目,想使用AudioRecord录制手机内部声音,但是调用开始录音的方法startRecording() 时,总是遇到这个错误:
startRecording() called on an uninitialized AudioRecord.
在此总结一下。
二、分析
1.百度发现,报这个错可能是AudioRecord没有初始化 (没有new)。正确写法可以这样写:
private AudioRecord mRecord = null;
private final int kFrameSize = 2048;
private final int kSampleRate = 44100;
private final int kChannelMode = AudioFormat.CHANNEL_IN_STEREO;
private final int kEncodeFormat = AudioFormat.ENCODING_PCM_16BIT;
//标志位,通过修改这个,决定录音是否停止
public boolean mReqStop = true;
public void recordAndPlay(File f) {
//修改标志位,开始录音
mReqStop = false;
FileOutputStream os = null;
if(mRecord == null) {
int minBufferSize = AudioRecord.getMinBufferSize(kSampleRate, kChannelMode, kEncodeFormat);
mRecord = new AudioRecord(MediaRecorder.AudioSource.REMOTE_SUBMIX, kSampleRate, kChannelMode, kEncodeFormat, minBufferSize * 2);
//mRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, kSampleRate, kChannelMode, kEncodeFormat, minBufferSize * 2);
}
try {
mRecord.startRecording();
}catch (Exception e){
Log.e("错误!",e.getMessage());
return;
}
try {
os = new FileOutputStream(f);
byte[] buffer = new byte[kFrameSize];
int num = 0;
while (!mReqStop) {
num = mRecord.read(buffer, 0, kFrameSize);
os.write(buffer, 0, num);
}
os.close();
} catch (IOException e) {
Log.e("IO错误!",e.getMessage());
}
mRecord.stop();
mRecord.release();
mRecord = null;
}
这个方法可以开始录音,保存为pcm文件。 这个方法已经确实把AudioRecord初始化了,但是还是会报错 。
2.继续百度,发现没有权限 也会报这个错,需要在AndroidManifest.xml增加权限
<uses-permission android:name="android.permission.CAPTURE_AUDIO_OUTPUT" />
<uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT"/>
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-sdk
android:minSdkVersion="19"
android:targetSdkVersion="19" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
3.为了保险,又在Android代码中增加了申请权限的方法
@SuppressLint("NewApi")
private boolean checkAudio2(){
if (checkSelfPermission(Manifest.permission.CAPTURE_AUDIO_OUTPUT) == PackageManager.PERMISSION_GRANTED){
return true;
}
else{
return false;
}
}
@SuppressLint("NewApi")
private void requestAudio2Permission() {
if (!checkAudio2()) {
requestPermissions(new String[]{Manifest.permission.CAPTURE_AUDIO_OUTPUT}, 0);
} else {
}
}
然后给录音按钮增加了个功能,点击按钮时,调用requestAudio2Permission()方法,应该就出现申请权限的对话框才对,但是点击后没有反应 。
3.继续百度发现,MediaRecorder.AudioSource.REMOTE_SUBMIX 功能只有系统应用才能使用,不开放给其它应用。 因此按照教程,把apk重新签名为系统应用。
(1)在AndroidManifest.xml文件的application 标签中,增加配置:android:sharedUserId="android.uid.system"
(2)打release包,然后使用signapk.jar重新签名为系统应用。
●signapk.jar下载地址:
https://gitee.com/code_to_success/signapk.git
其中包含了platform.x509.pem与platform.pk8这2个文件。
●可以把自己打的release包改名为test.apk,然后执行下面的命令:
java -jar signapk.jar platform.x509.pem platform.pk8 ./test.apk final.apk
需要把相关文件放在命令中对应的路径下,然后生成的final.apk就是有系统签名的apk了。
备注: 那2个文件可以从android官网下载,platform.x509.pem和platform.pk8在Android源码的build/target/product/security目录下。 signapk.jar可以从https://code.google.com/p/signapk/下载。
注意这种方法录音时扬声器不能输出。
(3)安装apk测试,发现没有用,还是报错 。 不知道是不是由于platform.x509.pem和platform.pk8不是最新版的问题。
4.经过多次测试,使用MediaRecorder.AudioSource.MIC 、申请 Manifest.permission.RECORD_AUDIO 权限后,是可以正常使用手机录音功能的,不过录制的是麦克风 ; 而使用MediaRecorder.AudioSource.REMOTE_SUBMIX ,无法申请Manifest.permission.CAPTURE_AUDIO_OUTPUT 权限、并且无法录音,报错:startRecording() called on an uninitialized AudioRecord 。
5.所以最后暂时没有实现录制手机内部声音的功能,改为了使用MIC参数、录制麦克风的声音。
三、总结
●AudioRecord报错startRecording() called on an uninitialized AudioRecord,可能是没有初始化AudioRecord 、没有申请权限 、使用REMOTE_SUBMIX等不开放给非系统应用的参数 导致的。 ●目前暂时没有解决REMOTE_SUBMIX无法使用、无法录制手机内部声音的问题。 待后续研究。
四、后记
2021.9.26 1.关于录制手机内部声音问题 现在是android11版本,第三方软件实现手机内录功能比较困难,因此还是使用系统自带的录屏软件->选择录制内部声音 ,然后使用视频音频提取APP,提取录屏视频中的音频为mp3的方式好些。
相关网址: https://zhidao.baidu.com/question/396545963009870525.html https://www.zhihu.com/question/265086051
2.关于AudioRecord使用的参数 可以使用以下参数:
//声音来源等参数
Audio Source: MIC
Sample rate: 16000
Number of channels: CHANNEL_CONFIGURATION_MONO
Format: ENCODING_PCM_16BIT
Buffer size: 16000 * 30 (30 second buffer)
//Java中写法
AudioRecord mRecord = new AudioRecord(
MediaRecorder.AudioSource.MIC,
16000,
AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT,
16000*30);
|