Android音频录制方案,Android录音,录制其他App播放的声音

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