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 线程上,这应该会导致您的界面在您开始录制后立即冻结。
我尝试使用新的 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 线程上,这应该会导致您的界面在您开始录制后立即冻结。