当前位置: 首页 > 知识库问答 >
问题:

启动mediaRecorder onActivityResult()时引发IllegalStatException

侯博裕
2023-03-14

当我们在视频通话时,我有问题建立屏幕录制功能。我是说我想同时开始录屏和视频通话。尝试将https://www.simplifiedcoding.net/video-call-android-tutorial/和http://www.truiton.com/2015/05/capture-record-android-screen-using-mediaprojection-apis/but应用程序在视频通话时启动记录屏幕时突然崩溃。如下所示的代码

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

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();
    }
}

}

希望有人能帮助我,谢谢大家!

共有1个答案

柯河
2023-03-14

我也面临着同样的问题。IllegalStateException是由于您从另一个线程访问主线程视图,//Play拨号音mdialtone.start();,它给出了异常。只需在主线程上调用initRecorder()即可。它会解决你的问题。

 类似资料:
  • 问题内容: (下面的示例代码是独立且可运行的,您可以尝试一下,它不会使系统崩溃:) Tom Hawtin在这里评论了这个问题:为什么人们在事件队列上运行JavaGUI 那: EDT不太可能崩溃。 EDT调度中抛出的未经检查的异常将被捕获,转储并且线程继续运行。 有人可以解释一下这是怎么回事(每次您单击 “引发未经检查的异常” 按钮时,都会有意除以零): 我收到以下消息(这是我期望的): 对我来说,

  • 问题内容: 任何node.js专家都可以告诉我如何在机器启动时配置节点JS以自动启动服务器吗?我在Windows上 问题答案: 根本不需要在node.js中进行配置,这完全是操作系统的职责(在您的情况下为Windows)。实现此目的的最可靠方法是通过Windows服务。 有一个 超级简单的 模块,它可以将节点脚本安装为Windows服务,称为 节点窗口 (npm,github,documentat

  • 问题内容: 我创建了一个简单的java“ echo”应用程序,该应用程序接收用户的输入并将其显示给他们以演示该问题。我可以使用IntelliJ的内部“运行”命令运行该应用程序,并且在执行产生的已编译Java文件时也可以正常运行。但是,如果尝试使用执行应用程序,则会从扫描仪抛出NoSuchElementException异常。 我认为gradle或应用程序插件正在对系统IO做一些奇怪的事情。 应用

  • 我们将使用IntelliJ IDEA/Android Studio来创建这个工程,因此你会对截图看起来比较熟悉。 让我们开始创建一个新的Android工程。你可以创建你自己的工程或者用本书中提供的导入。选择你自己喜欢的创建方式这取决于你。 如果你想用Android Studio创建一个新的工程,通常你可以参考官方文档:http://developer.android.com/intl/zh-cn/

  • 启动引导是指:在应用开始解析并处理新接受请求之前,一个预先准备环境的过程。 启动引导会在两个地方具体进行:入口脚本(Entry Script) 和 应用主体(application)。 在入口脚本里,需注册各个类库的类文件自动加载器(Class Autoloader,简称自动加载器)。 这主要包括通过其 autoload.php 文件加载的Composer 自动加载器,以及通过 Yii 类加载的

  • 该协议通过命令行-c参数提供,并作为Envoy v2根配置。有关更多详细信息,请参阅v2配置概述。 Bootstrap Bootstrap.StaticResources Bootstrap.DynamicResources Bootstrap.DynamicResources.DeprecatedV1 LightstepConfig ZipkinConfig Tracing Tracing.Ht