Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

invalid preview surface android video recording

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?

like image 353
eladex Avatar asked Aug 26 '12 15:08

eladex


1 Answers

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
}

}
like image 183
Sani Elfishawy Avatar answered Nov 09 '22 17:11

Sani Elfishawy