当我们进行视频通话时,我有建立屏幕录制功能的问题。我的意思是我想同时开始录制屏幕和视频通话。尝试在此https://www.simplifiedcoding.net/video-call-android-tutorial/和此http://www.truiton.com/2015/05/capture-record-android-screen-using-mediaprojection-apis/之间合并源代码,但在视频通话时启动记录屏幕时,应用会突然崩溃。 代码如下所示
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode != REQUEST_CODE) {
Log.e(TAG, "Unknown request code: " + requestCode);
return;
}
if (resultCode != RESULT_OK) {
Toast.makeText(this,
"Screen Cast Permission Denied", Toast.LENGTH_SHORT).show();
mToggleButton.setChecked(false);
return;
}
mMediaProjectionCallback = new MediaProjectionCallback();
mMediaProjection = mProjectionManager.getMediaProjection(resultCode, data);
mMediaProjection.registerCallback(mMediaProjectionCallback, null);
mVirtualDisplay = createVirtualDisplay();
mMediaRecorder.start();
}
这是logcat
11-14 16:51:55.265 13200-13200 / com.example.prasetyo.videocallapp E / AndroidRuntime:FATAL EXCEPTION:main 过程:com.example.prasetyo.videocallapp,PID:13200 java.lang.RuntimeException:将结果ResultInfo {who = null,request = 1000,result = -1,data = Intent {(has extras)}}传递给activity {com.example.prasetyo.videocallapp / com.example.prasetyo .videocallapp.Activities.CallScreenActivity}:java.lang.IllegalStateException 在android.app.ActivityThread.deliverResults(ActivityThread.java:3574) 在android.app.ActivityThread.handleSendResult(ActivityThread.java:3617) 在android.app.ActivityThread.access $ 1300(ActivityThread.java:151) 在android.app.ActivityThread $ H.handleMessage(ActivityThread.java:1352) 在android.os.Handler.dispatchMessage(Handler.java:102) 在android.os.Looper.loop(Looper.java:135) 在android.app.ActivityThread.main(ActivityThread.java:5254) at java.lang.reflect.Method.invoke(Native Method) 在java.lang.reflect.Method.invoke(Method.java:372) 在com.android.internal.os.ZygoteInit $ MethodAndArgsCaller.run(ZygoteInit.java:903) 在com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698) 引起:java.lang.IllegalStateException 在android.media.MediaRecorder.start(Native方法) at com.example.prasetyo.videocallapp.Activities.CallScreenActivity.onActivityResult(CallScreenActivity.java:214) 在android.app.Activity.dispatchActivityResult(Activity.java:6192) 在android.app.ActivityThread.deliverResults(ActivityThread.java:3570) 在android.app.ActivityThread.handleSendResult(ActivityThread.java:3617) 在android.app.ActivityThread.access $ 1300(ActivityThread.java:151) 在android.app.ActivityThread $ H.handleMessage(ActivityThread.java:1352) 在android.os.Handler.dispatchMessage(Handler.java:102) 在android.os.Looper.loop(Looper.java:135) 在android.app.ActivityThread.main(ActivityThread.java:5254) at java.lang.reflect.Method.invoke(Native Method) 在java.lang.reflect.Method.invoke(Method.java:372) 在com.android.internal.os.ZygoteInit $ MethodAndArgsCaller.run(ZygoteInit.java:903) 在com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
最后,这是完整的java类
public class CallScreenActivity extends BaseActivity {
static final String TAG = CallScreenActivity.class.getSimpleName();
static final String CALL_START_TIME = "callStartTime";
static final String ADDED_LISTENER = "addedListener";
private AudioPlayer mAudioPlayer;
private Timer mTimer;
private UpdateCallDurationTask mDurationTask;
private String mCallId;
private long mCallStart = 0;
private boolean mAddedListener = false;
private boolean mVideoViewsAdded = false;
private TextView mCallDuration;
private TextView mCallState;
private TextView mCallerName;
private static final int REQUEST_CODE = 1000;
private int mScreenDensity;
private MediaProjectionManager mProjectionManager;
private static final int DISPLAY_WIDTH = 720;
private static final int DISPLAY_HEIGHT = 1280;
private MediaProjection mMediaProjection;
private VirtualDisplay mVirtualDisplay;
private MediaProjectionCallback mMediaProjectionCallback;
private ToggleButton mToggleButton;
private MediaRecorder mMediaRecorder;
private static final SparseIntArray ORIENTATIONS = new SparseIntArray();
private static final int REQUEST_PERMISSIONS = 10;
static {
ORIENTATIONS.append(Surface.ROTATION_0, 90);
ORIENTATIONS.append(Surface.ROTATION_90, 0);
ORIENTATIONS.append(Surface.ROTATION_180, 270);
ORIENTATIONS.append(Surface.ROTATION_270, 180);
}
private class UpdateCallDurationTask extends TimerTask {
@Override
public void run() {
CallScreenActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
updateCallDuration();
}
});
}
}
@Override
protected void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putLong(CALL_START_TIME, mCallStart);
savedInstanceState.putBoolean(ADDED_LISTENER, mAddedListener);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
mCallStart = savedInstanceState.getLong(CALL_START_TIME);
mAddedListener = savedInstanceState.getBoolean(ADDED_LISTENER);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_call_screen);
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
mScreenDensity = metrics.densityDpi;
mMediaRecorder = new MediaRecorder();
mProjectionManager = (MediaProjectionManager) getSystemService
(Context.MEDIA_PROJECTION_SERVICE);
mAudioPlayer = new AudioPlayer(this);
mCallDuration = (TextView) findViewById(R.id.callDuration);
mCallerName = (TextView) findViewById(R.id.remoteUser);
mCallState = (TextView) findViewById(R.id.callState);
ImageButton endCallButton = (ImageButton) findViewById(R.id.hangupButton);
mToggleButton = (ToggleButton) findViewById(R.id.toggle);
mToggleButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (ContextCompat.checkSelfPermission(CallScreenActivity.this,
Manifest.permission.WRITE_EXTERNAL_STORAGE) + ContextCompat
.checkSelfPermission(CallScreenActivity.this,
Manifest.permission.RECORD_AUDIO)
!= PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale
(CallScreenActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) ||
ActivityCompat.shouldShowRequestPermissionRationale
(CallScreenActivity.this, Manifest.permission.RECORD_AUDIO)) {
mToggleButton.setChecked(false);
Snackbar.make(findViewById(android.R.id.content), R.string.label_permissions,
Snackbar.LENGTH_INDEFINITE).setAction("ENABLE",
new View.OnClickListener() {
@Override
public void onClick(View v) {
ActivityCompat.requestPermissions(CallScreenActivity.this,
new String[]{Manifest.permission
.WRITE_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO},
REQUEST_PERMISSIONS);
}
}).show();
} else {
ActivityCompat.requestPermissions(CallScreenActivity.this,
new String[]{Manifest.permission
.WRITE_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO},
REQUEST_PERMISSIONS);
}
} else {
onToggleScreenShare(v);
}
}
});
mCallId = getIntent().getStringExtra(SinchService.CALL_ID);
if (savedInstanceState == null) {
mCallStart = System.currentTimeMillis();
}
endCallButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
endCall();
mToggleButton.setChecked(false);
onToggleScreenShare(mToggleButton);
}
});
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode != REQUEST_CODE) {
Log.e(TAG, "Unknown request code: " + requestCode);
return;
}
if (resultCode != RESULT_OK) {
Toast.makeText(this,
"Screen Cast Permission Denied", Toast.LENGTH_SHORT).show();
mToggleButton.setChecked(false);
return;
}
mMediaProjectionCallback = new MediaProjectionCallback();
mMediaProjection = mProjectionManager.getMediaProjection(resultCode, data);
mMediaProjection.registerCallback(mMediaProjectionCallback, null);
mVirtualDisplay = createVirtualDisplay();
mMediaRecorder.start();
}
public void onToggleScreenShare(View view) {
if (((ToggleButton) view).isChecked()) {
initRecorder();
shareScreen();
} else {
mMediaRecorder.stop();
mMediaRecorder.reset();
Log.v(TAG, "Stopping Recording");
stopScreenSharing();
}
}
private void shareScreen() {
if (mMediaProjection == null) {
startActivityForResult(mProjectionManager.createScreenCaptureIntent(), REQUEST_CODE);
return;
}
mVirtualDisplay = createVirtualDisplay();
mMediaRecorder.start();
}
private VirtualDisplay createVirtualDisplay() {
return mMediaProjection.createVirtualDisplay("CallScreenActivity",
DISPLAY_WIDTH, DISPLAY_HEIGHT, mScreenDensity,
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
mMediaRecorder.getSurface(), null /*Callbacks*/, null
/*Handler*/);
}
private void initRecorder() {
try {
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
mMediaRecorder.setOutputFile(Environment
.getExternalStorageDirectory().getAbsolutePath() + "/video.mp4");
mMediaRecorder.setVideoSize(DISPLAY_WIDTH, DISPLAY_HEIGHT);
mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
mMediaRecorder.setVideoEncodingBitRate(512 * 1000);
mMediaRecorder.setVideoFrameRate(30);
int rotation = getWindowManager().getDefaultDisplay().getRotation();
int orientation = ORIENTATIONS.get(rotation + 90);
mMediaRecorder.setOrientationHint(orientation);
mMediaRecorder.prepare();
} catch (IOException e) {
e.printStackTrace();
}
}
private class MediaProjectionCallback extends MediaProjection.Callback {
@Override
public void onStop() {
if (mToggleButton.isChecked()) {
mToggleButton.setChecked(false);
mMediaRecorder.stop();
mMediaRecorder.reset();
Log.v(TAG, "Recording Stopped");
}
mMediaProjection = null;
stopScreenSharing();
}
}
private void stopScreenSharing() {
if (mVirtualDisplay == null) {
return;
}
mVirtualDisplay.release();
//mMediaRecorder.release(); //If used: mMediaRecorder object cannot
// be reused again
destroyMediaProjection();
}
@Override
public void onDestroy() {
super.onDestroy();
destroyMediaProjection();
}
private void destroyMediaProjection() {
if (mMediaProjection != null) {
mMediaProjection.unregisterCallback(mMediaProjectionCallback);
mMediaProjection.stop();
mMediaProjection = null;
}
Log.i(TAG, "MediaProjection Stopped");
}
@Override
public void onRequestPermissionsResult(int requestCode,
@NonNull String permissions[],
@NonNull int[] grantResults) {
switch (requestCode) {
case REQUEST_PERMISSIONS: {
if ((grantResults.length > 0) && (grantResults[0] +
grantResults[1]) == PackageManager.PERMISSION_GRANTED) {
onToggleScreenShare(mToggleButton);
} else {
mToggleButton.setChecked(false);
Snackbar.make(findViewById(android.R.id.content), R.string.label_permissions,
Snackbar.LENGTH_INDEFINITE).setAction("ENABLE",
new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setData(Uri.parse("package:" + getPackageName()));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
startActivity(intent);
}
}).show();
}
return;
}
}
}
@Override
public void onServiceConnected() {
Call call = getSinchServiceInterface().getCall(mCallId);
if (call != null) {
if (!mAddedListener) {
call.addCallListener(new SinchCallListener());
mAddedListener = true;
}
} else {
Log.e(TAG, "Started with invalid callId, aborting.");
finish();
}
updateUI();
}
//method to update video feeds in the UI
private void updateUI() {
if (getSinchServiceInterface() == null) {
return; // early
}
Call call = getSinchServiceInterface().getCall(mCallId);
if (call != null) {
mCallerName.setText(call.getRemoteUserId());
mCallState.setText(call.getState().toString());
if (call.getState() == CallState.ESTABLISHED) {
//when the call is established, addVideoViews configures the video to be shown
addVideoViews();
}
}
}
//stop the timer when call is ended
@Override
public void onStop() {
super.onStop();
mDurationTask.cancel();
mTimer.cancel();
removeVideoViews();
}
//start the timer for the call duration here
@Override
public void onStart() {
super.onStart();
mTimer = new Timer();
mDurationTask = new UpdateCallDurationTask();
mTimer.schedule(mDurationTask, 0, 500);
updateUI();
}
@Override
public void onBackPressed() {
// User should exit activity by ending call, not by going back.
}
//method to end the call
private void endCall() {
mAudioPlayer.stopProgressTone();
Call call = getSinchServiceInterface().getCall(mCallId);
if (call != null) {
call.hangup();
}
finish();
}
private String formatTimespan(long timespan) {
long totalSeconds = timespan / 1000;
long minutes = totalSeconds / 60;
long seconds = totalSeconds % 60;
return String.format(Locale.US, "%02d:%02d", minutes, seconds);
}
//method to update live duration of the call
private void updateCallDuration() {
if (mCallStart > 0) {
mCallDuration.setText(formatTimespan(System.currentTimeMillis() - mCallStart));
}
}
//method which sets up the video feeds from the server to the UI of the activity
private void addVideoViews() {
if (mVideoViewsAdded || getSinchServiceInterface() == null) {
return; //early
}
final VideoController vc = getSinchServiceInterface().getVideoController();
if (vc != null) {
RelativeLayout localView = (RelativeLayout) findViewById(R.id.localVideo);
localView.addView(vc.getLocalView());
localView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//this toggles the front camera to rear camera and vice versa
vc.toggleCaptureDevicePosition();
}
});
LinearLayout view = (LinearLayout) findViewById(R.id.remoteVideo);
view.addView(vc.getRemoteView());
mVideoViewsAdded = true;
}
}
//removes video feeds from the app once the call is terminated
private void removeVideoViews() {
if (getSinchServiceInterface() == null) {
return; // early
}
VideoController vc = getSinchServiceInterface().getVideoController();
if (vc != null) {
LinearLayout view = (LinearLayout) findViewById(R.id.remoteVideo);
view.removeView(vc.getRemoteView());
RelativeLayout localView = (RelativeLayout) findViewById(R.id.localVideo);
localView.removeView(vc.getLocalView());
mVideoViewsAdded = false;
}
}
private class SinchCallListener implements VideoCallListener {
@Override
public void onCallEnded(Call call) {
CallEndCause cause = call.getDetails().getEndCause();
Log.d(TAG, "Call ended. Reason: " + cause.toString());
mAudioPlayer.stopProgressTone();
setVolumeControlStream(AudioManager.USE_DEFAULT_STREAM_TYPE);
String endMsg = "Call ended: " + call.getDetails().toString();
Toast.makeText(CallScreenActivity.this, endMsg, Toast.LENGTH_LONG).show();
endCall();
mToggleButton.setChecked(false);
onToggleScreenShare(mToggleButton);
}
@Override
public void onCallEstablished(Call call) {
Log.d(TAG, "Call established");
mAudioPlayer.stopProgressTone();
mCallState.setText(call.getState().toString());
setVolumeControlStream(AudioManager.STREAM_VOICE_CALL);
AudioController audioController = getSinchServiceInterface().getAudioController();
audioController.enableSpeaker();
mCallStart = System.currentTimeMillis();
Log.d(TAG, "Call offered video: " + call.getDetails().isVideoOffered());
}
@Override
public void onCallProgressing(Call call) {
Log.d(TAG, "Call progressing");
mAudioPlayer.playProgressTone();
}
@Override
public void onShouldSendPushNotification(Call call, List pushPairs) {
// Send a push through your push provider here, e.g. GCM
}
@Override
public void onVideoTrackAdded(Call call) {
Log.d(TAG, "Video track added");
addVideoViews();
}
}
}
希望有人可以帮助我,非常感谢!答案 0 :(得分:-1)
我面临着同样的问题。 java.lang.IllegalStateException是由于您正在从另一个线程(//播放拨号音mDialTone.start();)访问主线程视图,从而产生了异常。只需在主线程上调用initRecorder()即可。它将解决您的问题。