invalid preview surface android video recording

2019-06-21 03:05发布

问题:

I have a service, and I try to make the service record video to file.

The Activity that starts the Service:

public static SurfaceView mSurfaceView;
public static SurfaceHolder mSurfaceHolder;
public static Camera mCamera;
@Override
public void onCreate(Bundle savedInstanceState) 
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    mSurfaceView = (SurfaceView) findViewById(R.id.surfaceView1);
    mSurfaceHolder = mSurfaceView.getHolder();
    mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    mSurfaceHolder.addCallback(this);
    Intent intent = new Intent(MainActivity.this, RecordService.class);
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    startService(intent);
    finish();
}

the service:

private MediaRecorder mMediaRecorder;
private SurfaceView mSurfaceView;
private SurfaceHolder mSurfaceHolder;
private boolean mRecordingStatus;
private Camera mServiceCamera;
private Size  mPreviewSize;

@Override
public void onCreate() 
{
    mRecordingStatus = false;
    mSurfaceView = MainActivity.mSurfaceView;
    mSurfaceHolder = MainActivity.mSurfaceHolder;
    mServiceCamera=MainActivity.mCamera;
    InitCamera();
    super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) 
{
    super.onStartCommand(intent, flags, startId);
    if (!mRecordingStatus)
        StartRecord();
    else
        StopRecord();

    return START_STICKY;
}
private void InitCamera()
{
    mServiceCamera = Camera.open();
    Camera.Parameters p = mServiceCamera.getParameters();

    final List<Size> listSize = p.getSupportedPreviewSizes();
    mPreviewSize = listSize.get(2);
    p.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
    p.setPreviewFormat(PixelFormat.YCbCr_420_SP);
    mServiceCamera.setParameters(p);
    try 
    {
        mServiceCamera.setPreviewDisplay(mSurfaceHolder);
        mServiceCamera.startPreview();
    }
    catch (IOException e) 
    {

        e.printStackTrace();
    }

    mServiceCamera.unlock();
}
private void StartRecord()
{
    try
    {
        mMediaRecorder = new MediaRecorder();
        mMediaRecorder.setCamera(mServiceCamera);
        mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
        mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
        mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
        mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
        mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);
        mMediaRecorder.setOutputFile(Environment.getExternalStorageDirectory().getPath()+File.separator+ "video.mp4");
        mMediaRecorder.setVideoFrameRate(30);
        mMediaRecorder.setVideoSize(mPreviewSize.width, mPreviewSize.height);
        mMediaRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());

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

        mRecordingStatus = true;

        Toast toast = Toast.makeText(getBaseContext(), "Recording",1000);
        toast.show();
    }
    catch (IOException e) 
    {
        Toast toast = Toast.makeText(getBaseContext(), e.getMessage(),2000);
        toast.show();
    }
}

when I debug the code on my Galaxy s I get the toast(in the StartRecord() method) "invalid preview surface"

How it can be fixed?

回答1:

I had to make the MediaRecorder startup be executed with some delay. That means in onResume, I created a Handler and invoked postDelayed on that. It's still the same Thread, I know, but it worked with me.



回答2:

The reason you were seeing is because you executed mMediaRecorder.setPreviewDisplay(mSurfaceHolder.getSurface()); before the surface was created. Adding a delay happens to work but is not the correct way to handle this.

The line mSurfaceHolder = mSurfaceView.getHolder(); does not create the surface immediately. In fact SurfaceHolder provides a callback interface to let you know when the surface has been created.

The line mSurfaceHolder.addCallback(this); should be in the constructor of a class you create which extends SurfaceView and implements SurfaceHolder.Callback. You can then override the callback methods of the SurfaceHolder.Callback interface so your code executes after the surface has been created. Here is an example:

public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {

private SurfaceHolder surfaceHolder;


public CameraPreview(Context context, VideoHandler vh) {
    super(context);
    videoHandler = vh;

    // Install a SurfaceHolder.Callback so we get notified when the
    // underlying surface is created and destroyed.
    surfaceHolder = getHolder();
    surfaceHolder.addCallback(this);
    // deprecated setting, but required on Android versions prior to 3.0
    if(Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB)
        surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}

@Override
public void surfaceCreated(SurfaceHolder holder) {
    // Perform actions that require Surface to have been created here e.g.
    // mMediaRecorder.setPreviewDisplay(holder.getSurface()); etc.

}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
    // Take care of releasing camera preview etc.
}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
    // Handle changes
}

}