AudioPlaybackCapture (Android 10) 不工作并录制空声音

AudioPlaybackCapture (Android 10) not working and recording empty sounds

我尝试使用新的 AudioPlaybackCapture 方法在 android 10 设备中录制一些媒体。但不幸的是,我使用此 API 的代码似乎运行不佳。

在这里,我使用了一个 activity 来启动一个单独的媒体录制服务。该服务已注册到广播接收器以开始和停止录制。广播意图是通过单击按钮(开始、停止)使用我的主要 activity 触发的

没有打印异常。该文件也会在所需位置创建。但没有内容(0 字节)。已提供所有必需的清单和运行时权限。我在这里做错了什么。

这是我的服务

public class MediaCaptureService extends Service {
    public static final String ACTION_ALL = "ALL";
    public static final String ACTION_START = "ACTION_START";
    public static final String ACTION_STOP = "ACTION_STOP";
    public static final String EXTRA_RESULT_CODE = "EXTRA_RESULT_CODE";
    public static final String EXTRA_ACTION_NAME = "ACTION_NAME";

    private static final int RECORDER_SAMPLERATE = 8000;
    private static final int RECORDER_CHANNELS = AudioFormat.CHANNEL_IN_MONO;
    private static final int RECORDER_AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT;

    NotificationCompat.Builder _notificationBuilder;
    NotificationManager _notificationManager;
    private String NOTIFICATION_CHANNEL_ID = "ChannelId";
    private String NOTIFICATION_CHANNEL_NAME = "Channel";
    private String NOTIFICATION_CHANNEL_DESC = "ChannelDescription";
    private int NOTIFICATION_ID = 1000;
    private static final String ONGING_NOTIFICATION_TICKER = "RecorderApp";

    int BufferElements2Rec = 1024; // want to play 2048 (2K) since 2 bytes we use only 1024
    int BytesPerElement = 2; // 2 bytes in 16bit format

    AudioRecord _recorder;
    private boolean isRecording = false;

    private MediaProjectionManager _mediaProjectionManager;
    private MediaProjection _mediaProjection;

    Intent _callingIntent;

    public MediaCaptureService() {
    }

    @Override
    public void onCreate() {
        super.onCreate();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            //Call Start foreground with notification
            Intent notificationIntent = new Intent(this, MediaCaptureService.class);
            PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
            _notificationBuilder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
                    .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher_foreground))
                    .setSmallIcon(R.drawable.ic_launcher_foreground)
                    .setContentTitle("Starting Service")
                    .setContentText("Starting monitoring service")
                    .setTicker(ONGING_NOTIFICATION_TICKER)
                    .setContentIntent(pendingIntent);
            Notification notification = _notificationBuilder.build();
            NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT);
            channel.setDescription(NOTIFICATION_CHANNEL_DESC);
            _notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
            _notificationManager.createNotificationChannel(channel);
            startForeground(NOTIFICATION_ID, notification);
        }

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            _mediaProjectionManager = (MediaProjectionManager) getSystemService(MEDIA_PROJECTION_SERVICE);
        }

    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        _callingIntent = intent;

        IntentFilter filter = new IntentFilter();
        filter.addAction(ACTION_ALL);
        registerReceiver(_actionReceiver, filter);

        return START_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    private void startRecording(Intent intent) {
        //final int resultCode = intent.getIntExtra(EXTRA_RESULT_CODE, 0);
        _mediaProjection = _mediaProjectionManager.getMediaProjection(-1, intent);
        startRecording(_mediaProjection);
    }

    @TargetApi(29)
    private void startRecording(MediaProjection mediaProjection ) {
        AudioPlaybackCaptureConfiguration config =
                new AudioPlaybackCaptureConfiguration.Builder(mediaProjection)
                        .addMatchingUsage(AudioAttributes.USAGE_MEDIA)
                        .build();
        AudioFormat audioFormat = new AudioFormat.Builder()
                .setEncoding(RECORDER_AUDIO_ENCODING)
                .setSampleRate(RECORDER_SAMPLERATE)
                .setChannelMask(RECORDER_CHANNELS)
                .build();
        _recorder = new AudioRecord.Builder()
 //               .setAudioSource(MediaRecorder.AudioSource.MIC)
                .setAudioFormat(audioFormat)
                .setBufferSizeInBytes(BufferElements2Rec * BytesPerElement)
                .setAudioPlaybackCaptureConfig(config)
                .build();

        _recorder.startRecording();
        writeAudioDataToFile();
    }

    private byte[] short2byte(short[] sData) {
        int shortArrsize = sData.length;
        byte[] bytes = new byte[shortArrsize * 2];
        for (int i = 0; i < shortArrsize; i++) {
            bytes[i * 2] = (byte) (sData[i] & 0x00FF);
            bytes[(i * 2) + 1] = (byte) (sData[i] >> 8);
            sData[i] = 0;
        }
        return bytes;

    }

    private void writeAudioDataToFile() {
        // Write the output audio in byte
        Log.i(MainActivity.LOG_PREFIX, "Recording started. Computing output file name");
        File sampleDir = new File(getExternalFilesDir(null), "/TestRecordingDasa1");
        if (!sampleDir.exists()) {
            sampleDir.mkdirs();
        }
        String fileName = "Record-" + new SimpleDateFormat("dd-MM-yyyy-hh-mm-ss").format(new Date()) + ".pcm";
        String filePath = sampleDir.getAbsolutePath() + "/" + fileName;
        //String filePath = "/sdcard/voice8K16bitmono.pcm";
        short sData[] = new short[BufferElements2Rec];

        FileOutputStream os = null;
        try {
            os = new FileOutputStream(filePath);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

        while (isRecording) {
            // gets the voice output from microphone to byte format
            _recorder.read(sData, 0, BufferElements2Rec);
            System.out.println("Short wirting to file" + sData.toString());
            try {
                // // writes the data to file from buffer
                // // stores the voice buffer
                byte bData[] = short2byte(sData);
                os.write(bData, 0, BufferElements2Rec * BytesPerElement);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        try {
            os.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        Log.i(MainActivity.LOG_PREFIX, String.format("Recording finished. File saved to '%s'", filePath));
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    private void stopRecording() {
        // stops the recording activity
        if (null != _recorder) {
            isRecording = false;
            _recorder.stop();
            _recorder.release();
            _recorder = null;
        }

        _mediaProjection.stop();

        stopSelf();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        unregisterReceiver(_actionReceiver);
    }

    BroadcastReceiver _actionReceiver = new BroadcastReceiver() {
        @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (action.equalsIgnoreCase(ACTION_ALL)) {
                String actionName = intent.getStringExtra(EXTRA_ACTION_NAME);
                if (actionName != null && !actionName.isEmpty()) {
                    if (actionName.equalsIgnoreCase(ACTION_START)) {
                        startRecording(_callingIntent);
                    } else if (actionName.equalsIgnoreCase(ACTION_STOP)){
                        stopRecording();
                    }
                }

            }
        }
    };



这是主要 activity 的摘录,其中启动了服务和启动/停止操作。



public class MainActivity extends AppCompatActivity {
    public static final String LOG_PREFIX = "CALL_FUNCTION_TEST";

    private static final int ALL_PERMISSIONS_PERMISSION_CODE = 1000;
    private static final int CREATE_SCREEN_CAPTURE = 1001;

    Button _btnInitCapture;
    Button _btnStartCapture;
    Button _btnStopCapture;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        _btnGetOkPermissions = findViewById(R.id.btnGetOkPermissions);
        _btnGetOkPermissions.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                checkOkPermissions();
            }
        });

        _btnInitCapture = findViewById(R.id.btnInitCapture);
        _btnInitCapture.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                initAudioCapture();
            }
        });

        _btnStartCapture = findViewById(R.id.btnStartCapture);
        _btnStartCapture.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                startRecording();
            }
        });

        _btnStopCapture = findViewById(R.id.btnStopAudioCapture);
        _btnStopCapture.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                stopRecording();
            }
        });
    }

    ...

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    private void initAudioCapture() {
        _manager = (MediaProjectionManager) getSystemService(MEDIA_PROJECTION_SERVICE);
        Intent intent = _manager.createScreenCaptureIntent();
        startActivityForResult(intent, CREATE_SCREEN_CAPTURE);
    }

    private void stopRecording() {
        Intent broadCastIntent = new Intent();
        broadCastIntent.setAction(MediaCaptureService.ACTION_ALL);
        broadCastIntent.putExtra(MediaCaptureService.EXTRA_ACTION_NAME, MediaCaptureService.ACTION_STOP);
        this.sendBroadcast(broadCastIntent);
    }

    private void startRecording() {
        Intent broadCastIntent = new Intent();
        broadCastIntent.setAction(MediaCaptureService.ACTION_ALL);
        broadCastIntent.putExtra(MediaCaptureService.EXTRA_ACTION_NAME, MediaCaptureService.ACTION_START);
        this.sendBroadcast(broadCastIntent);
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent intent) {
        super.onActivityResult(requestCode, resultCode, intent);
        if (CREATE_SCREEN_CAPTURE == requestCode) {
            if (resultCode == RESULT_OK) {
                Intent i = new Intent(this, MediaCaptureService.class);
                i.setAction(MediaCaptureService.ACTION_START);
                i.putExtra(MediaCaptureService.EXTRA_RESULT_CODE, resultCode);
                i.putExtras(intent);
                this.startService(i);
            } else {
                // user did not grant permissions
            }
        }
    }
}


好吧,没有什么能让 isRecording 成立。此外,您正在以阻塞方式进行录制,但您在 UI 线程上,这应该会导致您的界面在您开始录制后立即冻结。