Android录音,录制其他App播放的声音
从Android10(SDK 29)版本开始,可以设置录音App的源为其他App,这样就可以录制其他App播放的声音
此方案有以下注意几点
设置了源为其他App后,就不能设置默认源为麦克风了
录音的线程只能在Service里,这个Service只能是前台Service
具体实现步骤
首先需要在被录音的App的AndroidManifest里面的application元素里设置属性 android:allowAudioPlaybackCapture="true",这样才可以让其他App来录音此App
通过 MediaProjectionManager 获取Intent
// 下面两行代码在onCreate里面,用于在启动时请求MediaProjectionManager
MediaProjectionManager mediaProjectionManager = (MediaProjectionManager) getSystemService(MEDIA_PROJECTION_SERVICE);
startActivityForResult(mediaProjectionManager.createScreenCaptureIntent(), 2000);
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == Activity.RESULT_OK && requestCode == 2000) {
if (data != null) {
code = resultCode;
dataIt = data;
}
}
}
// MediaProjectionPermissionActivity.java
private Intent getMediaProjectionIntent(int uid, String packageName)
throws RemoteException {
IMediaProjection projection = mService.createProjection(uid, packageName,
MediaProjectionManager.TYPE_SCREEN_CAPTURE, false /* permanentGrant */);
Intent intent = new Intent();
intent.putExtra(MediaProjectionManager.EXTRA_MEDIA_PROJECTION, projection.asBinder());
return intent;
}
// MediaProjectionManagerService.java
public IMediaProjection createProjection(int uid, String packageName, int type,
boolean isPermanentGrant) {
if (mContext.checkCallingPermission(Manifest.permission.MANAGE_MEDIA_PROJECTION)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires MANAGE_MEDIA_PROJECTION in order to grant "
+ "projection permission");
}
if (packageName == null || packageName.isEmpty()) {
throw new IllegalArgumentException("package name must not be empty");
}
final UserHandle callingUser = Binder.getCallingUserHandle();
long callingToken = Binder.clearCallingIdentity();
MediaProjection projection;
try {
ApplicationInfo ai;
try {
ai = mPackageManager.getApplicationInfoAsUser(packageName, 0, callingUser);
} catch (NameNotFoundException e) {
throw new IllegalArgumentException("No package matching :" + packageName);
}
projection = new MediaProjection(type, uid, packageName, ai.targetSdkVersion,
ai.isPrivilegedApp());
if (isPermanentGrant) {
mAppOps.setMode(AppOpsManager.OP_PROJECT_MEDIA,
projection.uid, projection.packageName, AppOpsManager.MODE_ALLOWED);
}
} finally {
Binder.restoreCallingIdentity(callingToken);
}
return projection;
}
// MediaProjectionManagerService.java
MediaProjection(int type, int uid, String packageName, int targetSdkVersion,
boolean isPrivileged) {
mType = type;
this.uid = uid;
this.packageName = packageName;
userHandle = new UserHandle(UserHandle.getUserId(uid));
mTargetSdkVersion = targetSdkVersion;
// mIsPrivileged = isPrivileged;
mIsPrivileged = true;
}
开启录音Service
Intent intent = new Intent(this, AudioRecordService.class);
// 传入的code和dataIt是用来从MediaProjectionManager来获取MediaProjection的
intent.putExtra("code", code);
intent.putExtra("data", dataIt);
startService(intent);
在Service里接收传过来的code和DataIt,并且将此Service设置为前台Service
public int onStartCommand(Intent intent, int flags, int startId) {
data = intent.getParcelableExtra("data");
code = intent.getIntExtra("code", 114);
final Notification notification = createNotification();
startForeground(1000, notification);
startRecording();
return START_STICKY;
}
获取MediaProjectionManager,并且从MediaProjectionManager里获取MediaProjection
projectionManager = (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);
if (projectionManager == null)
return;
projection = projectionManager.getMediaProjection(code, data);
配置需要录音的类型以及包名
AudioPlaybackCaptureConfiguration config = new AudioPlaybackCaptureConfiguration.Builder(projection)
.addMatchingUsage(AudioAttributes.USAGE_MEDIA)
.addMatchingUid(Utils.getPkgInfo(getApplicationContext(), "com.xxx").applicationInfo.uid)
.build();
构建AudioRecord,设置上一步生成的config为录音源
audioRecord = new AudioRecord.Builder().setAudioFormat(
new AudioFormat.Builder()
.setEncoding(AudioFormat.ENCODING_PCM_16BIT)
.setSampleRate(44100)
.setChannelMask(AudioFormat.CHANNEL_IN_MONO)
.build())
.setAudioPlaybackCaptureConfig(config)
.build();
最后就是录音了
audioRecord.startRecording();
int minByteBufferSize = AudioRecord.getMinBufferSize(44100, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
new Thread(new Runnable() {
@Override
public void run() {
FileOutputStream fos = null;
try {
fos = new FileOutputStream("/sdcard/record.pcm");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
byte[] bArr = new byte[minByteBufferSize];
int readSize = 0;
while (audioRecord.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) {
readSize = audioRecord.read(bArr, 0, minByteBufferSize, AudioRecord.READ_BLOCKING);
Log.i(utils.LOG_TAG, "读取到音频数量: " + readSize);
try {
fos.write(bArr, 0, readSize);
fos.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
Log.i(utils.LOG_TAG, "录音线程退出");
}
}).start();
以上是录音其他App播放的音频的简单用法,具体细节可留言讨论,或者添加本人QQ一起交流: 1320024819