在背景中录制视频,即使屏幕关闭也可以。

7
我正在尝试创建一个Android应用程序,可以在后台录制视频(当应用程序不在前台时),并且在屏幕关闭时也能继续录制。我在PlayStore上找到了一些可以实现此功能的应用程序,所以我相信这是可行的。 PlayStore App 我已经尝试使用Service和Activity来实现这个功能。该Activity有两个按钮和一个SurfaceView。当我点击“开始录制”按钮时,Service会开始使用MediaRecorder录制视频,并将应用程序置于后台。当我再次从后台打开应用程序以停止视频录制并点击“停止录制”按钮时,应用程序崩溃,并显示以下日志。我在这里搜索过,并找到了与此相关的不同解决方案,但没有找到任何对我有帮助的解决方案。
08-13 22:04:50.085 24729-24729/com.meowme.camerarecorder E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.meowme.camerarecorder, PID: 24729
    java.lang.RuntimeException: Unable to stop service com.meowme.camerarecorder.RecorderService@38a64c2: java.lang.IllegalStateException
        at android.app.ActivityThread.handleStopService(ActivityThread.java:3727)
        at android.app.ActivityThread.-wrap27(Unknown Source:0)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1806)
        at android.os.Handler.dispatchMessage(Handler.java:105)
        at android.os.Looper.loop(Looper.java:164)
        at android.app.ActivityThread.main(ActivityThread.java:6940)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)
     Caused by: java.lang.IllegalStateException
        at android.media.MediaRecorder._stop(Native Method)
        at android.media.MediaRecorder.stop(MediaRecorder.java:1306)
        at com.meowme.camerarecorder.RecorderService.stopRecording(RecorderService.java:164)
        at com.meowme.camerarecorder.RecorderService.onDestroy(RecorderService.java:92)
        at android.app.ActivityThread.handleStopService(ActivityThread.java:3709)
        at android.app.ActivityThread.-wrap27(Unknown Source:0) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1806) 
        at android.os.Handler.dispatchMessage(Handler.java:105) 
        at android.os.Looper.loop(Looper.java:164) 
        at android.app.ActivityThread.main(ActivityThread.java:6940) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)

 

CameraRecorder.java

public class CameraRecorder extends Activity implements SurfaceHolder.Callback {

    private static final String TAG = CameraRecorder.class.getSimpleName();

    public static final int RequestPermissionCode = 7;


    public static SurfaceView mSurfaceView;
    public static SurfaceHolder mSurfaceHolder;
    public static Camera mCamera;
    public static boolean mPreviewRunning;

    /**
     * Called when the activity is first created.
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (CheckingPermissionIsEnabledOrNot()) {
            initializeMainActivity();
        } else {
            RequestMultiplePermission();
        }


    }

    private void initializeMainActivity() {
        setContentView(R.layout.main);

        mSurfaceView = findViewById(R.id.surfaceView1);
        mSurfaceHolder = mSurfaceView.getHolder();
        mSurfaceHolder.addCallback(this);
        mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

        Button btnStart = findViewById(R.id.StartService);
        btnStart.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                Intent intent = new Intent(CameraRecorder.this, RecorderService.class);
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                startService(intent);
                finish();
            }
        });

        Button btnStop = findViewById(R.id.StopService);
        btnStop.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                stopService(new Intent(CameraRecorder.this, RecorderService.class));
            }
        });

    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {

    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
    }


    public boolean CheckingPermissionIsEnabledOrNot() {

        int FirstPermissionResult = ContextCompat.checkSelfPermission(getApplicationContext(), CAMERA);
        int SecondPermissionResult = ContextCompat.checkSelfPermission(getApplicationContext(), RECORD_AUDIO);
        int ThirdPermissionResult = ContextCompat.checkSelfPermission(getApplicationContext(), WRITE_EXTERNAL_STORAGE);


        return FirstPermissionResult == PackageManager.PERMISSION_GRANTED &&
                SecondPermissionResult == PackageManager.PERMISSION_GRANTED &&
                ThirdPermissionResult == PackageManager.PERMISSION_GRANTED;
    }


    @Override
    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
        switch (requestCode) {

            case RequestPermissionCode:

                if (grantResults.length > 0) {

                    boolean CameraPermission = grantResults[0] == PackageManager.PERMISSION_GRANTED;
                    boolean RecordAudioPermission = grantResults[1] == PackageManager.PERMISSION_GRANTED;
                    boolean ExternalStoragePermission = grantResults[2] == PackageManager.PERMISSION_GRANTED;

                    if (CameraPermission && RecordAudioPermission && ExternalStoragePermission) {

                        Toast.makeText(CameraRecorder.this, "Permission Granted", Toast.LENGTH_LONG).show();
                        // TODO show activity here..
                        initializeMainActivity();
                    }
                     else {
                        Toast.makeText(CameraRecorder.this, "Permission Denied", Toast.LENGTH_LONG).show();
                        finish();
                    }
                }

                break;
        }
    }

    private void RequestMultiplePermission() {

        // Creating String Array with Permissions.
        ActivityCompat.requestPermissions(CameraRecorder.this, new String[]
                {
                        CAMERA,
                        RECORD_AUDIO,
                        WRITE_EXTERNAL_STORAGE,
                }, RequestPermissionCode);

    }

}

RecorderService.java

public class RecorderService extends Service {

    private static final String TAG = "RecorderService";
    private SurfaceView mSurfaceView;
    private SurfaceHolder mSurfaceHolder;
    private static Camera mServiceCamera;
    private boolean mRecordingStatus;
    private MediaRecorder mMediaRecorder;

    @Override
    public void onCreate() {
        mRecordingStatus = false;
        mServiceCamera = CameraRecorder.mCamera;
        mSurfaceView = CameraRecorder.mSurfaceView;
        mSurfaceHolder = CameraRecorder.mSurfaceHolder;

        super.onCreate();
    }

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

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        super.onStartCommand(intent, flags, startId);

        if (mRecordingStatus == false)
            startRecording();

        return START_STICKY;
    }

    @Override
    public void onDestroy() {
        stopRecording();
        mRecordingStatus = false;

        super.onDestroy();
    }

    public boolean startRecording() {
        try {
            Toast.makeText(getBaseContext(), "Recording Started", Toast.LENGTH_SHORT).show();

            mServiceCamera = Camera.open();
            Camera.Parameters params = mServiceCamera.getParameters();
            mServiceCamera.setParameters(params);
            Camera.Parameters p = mServiceCamera.getParameters();

            final List<Size> listPreviewSize = p.getSupportedPreviewSizes();
            for (Size size : listPreviewSize) {
                Log.i(TAG, String.format("Supported Preview Size (%d, %d)", size.width, size.height));
            }

            Size previewSize = listPreviewSize.get(0);
            p.setPreviewSize(previewSize.width, previewSize.height);
            mServiceCamera.setParameters(p);

            try {
                mServiceCamera.setPreviewDisplay(mSurfaceHolder);
                mServiceCamera.startPreview();
            } catch (IOException e) {
                Log.e(TAG, e.getMessage());
                e.printStackTrace();
            }

            mServiceCamera.unlock();

            mMediaRecorder = new MediaRecorder();
            mMediaRecorder.setCamera(mServiceCamera);
            mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
            mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
            mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
            mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
            mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
            mMediaRecorder.setOutputFile(Environment.getExternalStorageDirectory().getPath() + "video" + System.currentTimeMillis() + ".mp4");
            mMediaRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());

            mMediaRecorder.prepare();
            mMediaRecorder.start();

            mRecordingStatus = true;

            return true;

        } catch (IllegalStateException e) {
            Log.d(TAG, e.getMessage());
            e.printStackTrace();
            return false;

        } catch (IOException e) {
            Log.d(TAG, e.getMessage());
            e.printStackTrace();
            return false;
        }
    }

    public void stopRecording() {
        Toast.makeText(getBaseContext(), "Recording Stopped", Toast.LENGTH_SHORT).show();
        try {
            mServiceCamera.reconnect();

        } catch (IOException e) {
            e.printStackTrace();
        }

        mMediaRecorder.stop();
        mMediaRecorder.reset();

        mServiceCamera.stopPreview();
        mMediaRecorder.release();

        mServiceCamera.release();
        mServiceCamera = null;
    }
}

main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical">

    <Button
        android:id="@+id/StartService"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginTop="5dp"
        android:background="#5699e6"
        android:padding="16dp"
        android:text="Start Recording"
        android:textColor="#fff"
        android:textSize="15sp">

    </Button>

    <Button
        android:id="@+id/StopService"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginTop="5dp"
        android:background="#5699e6"
        android:layout_marginBottom="16dp"
        android:padding="16dp"
        android:text="Stop Recording"
        android:textColor="#fff"
        android:textSize="15sp"></Button>

    <SurfaceView
        android:id="@+id/surfaceView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">

    </SurfaceView>

</LinearLayout>

如果有人能指导我如何解决这个问题,那真的会非常有帮助。

1
请注意,在Android 9.0+上,后台应用程序无法访问相机。 - egoldx
2个回答

0
快速查看您的崩溃日志告诉我,很可能是在关闭服务时发生了这种情况。在“stopRecording()”中,您调用了mMediaRecorder.stop(),看起来它已经停止了,因为它抛出了一个IllegalStateException。
在停止之前,请检查媒体记录器是否正在运行。

我想要实现的是在按下停止录制按钮之前不停止MediaRecorder。我希望应用程序即使在后台或屏幕关闭时也能进行录制。这就是为什么我使用了Service的原因。 - Ebad Ali
1
我明白,我只是告诉你崩溃的原因。如果你对此不感兴趣,那么我建议你删除崩溃日志,因为它没有添加任何有用的信息。如果您确定媒体记录器在按下开始按钮时开始录制,我不知道为什么会停止。也很可能是您的服务被系统停止了。在最新版本的Android中,创建一个不被杀死以节省待机电源的后台服务要困难得多。 - The Science Boy
也许由于负载过大,您的服务已被终止。为了防止系统杀掉您的进程,请参考 https://dev59.com/1V8d5IYBdhLWcg3w1E90#34865760。 - Raffael Bechara Rameh

0
请使用这段代码,它对我有效。
        mMediaRecorder.setOutputFile(
                Environment.getExternalStorageDirectory()+"/"+
                        DateFormat.format("yyyy-MM-dd_kk-mm-ss", new Date().getTime())+
                        ".mp4"
        );

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接