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

原创
04/20 15:58
阅读数 1K

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

从Android10(SDK 29)版本开始,可以设置录音App的源为其他App,这样就可以录制其他App播放的声音

此方案有以下注意几点

  • 设置了源为其他App后,就不能设置默认源为麦克风了
  • 录音的线程只能在Service里,这个Service只能是前台Service

具体实现步骤

首先需要在被录音的App的AndroidManifest里面的application元素里设置属性 android:allowAudioPlaybackCapture="true",这样才可以让其他App来录音此App

  1. 通过 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;
        }


  1. 开启录音Service
Intent intent = new Intent(this, AudioRecordService.class);
// 传入的code和dataIt是用来从MediaProjectionManager来获取MediaProjection的
intent.putExtra("code", code);
intent.putExtra("data", dataIt);
startService(intent);
  1. 在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;
}
  1. 获取MediaProjectionManager,并且从MediaProjectionManager里获取MediaProjection
projectionManager = (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);
if (projectionManager == null)
    return;
projection = projectionManager.getMediaProjection(code, data);
  1. 配置需要录音的类型以及包名
AudioPlaybackCaptureConfiguration config = new AudioPlaybackCaptureConfiguration.Builder(projection)
        .addMatchingUsage(AudioAttributes.USAGE_MEDIA)
        .addMatchingUid(Utils.getPkgInfo(getApplicationContext(), "com.xxx").applicationInfo.uid)
        .build();
  1. 构建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();
  1. 最后就是录音了
	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

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部