Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MediaRecorder video capturing in portrait mode

I'm try to make custom video app. Iwork using settings in manifest 2.2 only (API 8).

All goes well but I don't understand why portrait mode video does not differ from lanscape one.

To make detection of device changed orientation I use this code within surfaceChanged()

        if (mCamera != null) {

        Camera.Parameters p = mCamera.getParameters();

        try {
            mCamera.stopPreview();
        } catch (Exception e) {
            // TODO: handle exception
        }

        int previewWidth = 0;
        int previewHeight = 0;

        if (mPreviewSize != null) {
            Display display = ((WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
            int rotation = display.getRotation();

            switch (rotation) {
            case Surface.ROTATION_0:
                previewWidth = mPreviewSize.height;
                previewHeight = mPreviewSize.width;
                mCamera.setDisplayOrientation(90);
                break;

            case Surface.ROTATION_90:
                previewWidth = mPreviewSize.width;
                previewHeight = mPreviewSize.height;
                mCamera.setDisplayOrientation(0);
                break;

            case Surface.ROTATION_180:
                previewWidth = mPreviewSize.height;
                previewHeight = mPreviewSize.width;
                mCamera.setDisplayOrientation(270);
                break;

            case Surface.ROTATION_270:
                previewWidth = mPreviewSize.width;
                previewHeight = mPreviewSize.height;
                mCamera.setDisplayOrientation(180);
                break;
            }

            p.setPreviewSize(previewWidth, previewHeight);
            mCamera.setParameters(p);
        }
    try {
        mCamera.setPreviewDisplay(mHolder);
        mCamera.startPreview();
    } catch (Exception e) {
        Log.d(TAG, "Cannot start preview.", e);
    }
}

Works like a charm. If I rotate device surface change orientation, calling surfaceChanged, where camera is set to appropriate DisplayRotation.

The question is how to determine later if the video captured either in lanscape mode or in portrait one. As I got all the videos are captured in landscape orientation. It does not depend of setDisplayOrientation which affect only preview process.

Also exploring the problem I noticed that if to use standard Camera app it writes special tag to video file (seen in MediaInfo): Rotation : 90 for the portrait captured videos.

But MediaRecorder class does not.

Seems that is the problem. Anybody got to solve this?

like image 854
Oleg Karakoz Avatar asked Aug 16 '12 18:08

Oleg Karakoz


2 Answers

Found it ! Indeed, you can change the preview, you can tag the video, but there's no way to actually change the video... (maybe a speed issue or something)

camera.setDisplayOrientation(90);

To rotate the preview, then

recorder.setOrientationHint(90);

To tag the video as having a 90° rotation, then the phone will automatically rotate it when reading.

So all you have to do is

            camera = Camera.open();
        //Set preview with a 90° ortientation
        camera.setDisplayOrientation(90);
        camera.unlock();

        holder = getHolder();
        holder.addCallback(this);
        holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

        recorder = new MediaRecorder();
        recorder.setCamera(camera);
        recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
        recorder.setVideoSource(MediaRecorder.VideoSource.DEFAULT);
        recorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
        recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
        recorder.setVideoEncoder(MediaRecorder.VideoEncoder.MPEG_4_SP);
        recorder.setOutputFile(getVideoFolder()+rnd.nextString()+".mp4");
        recorder.setPreviewDisplay(holder.getSurface());
        //Tags the video with a 90° angle in order to tell the phone how to display it
        recorder.setOrientationHint(90);

        if (recorder != null) {
            try {
                recorder.prepare();
            } catch (IllegalStateException e) {
                Log.e("IllegalStateException", e.toString());
            } catch (IOException e) {
                Log.e("IOException", e.toString());
            }
        }

        recorder.start();

Hope it helps ;-)

like image 130
Taiko Avatar answered Nov 09 '22 16:11

Taiko


camera.setDisplayOrientation(90) does not work in all devices. Following solution work perfectly in different devices and also handling marshmallow runtime permission.

See setCameraRotation method

public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
    private SurfaceHolder mHolder;
    private Camera mCamera;
    public static int rotate;
    private Context mContext;

    public CameraPreview(Context context, Camera camera) {
        super(context);
        mCamera = camera;
        mHolder = getHolder();
        mHolder.addCallback(this);
        // deprecated setting, but required on Android versions prior to 3.0
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        mContext = context;
    }

    public void surfaceCreated(SurfaceHolder holder) {
        try {
            // create the surface and start camera preview
            if (mCamera != null) {
                mCamera.setPreviewDisplay(holder);
                mCamera.startPreview();
            }
        } catch (IOException e) {
            Log.d(VIEW_LOG_TAG, "Error setting camera preview: " + e.getMessage());
        }
    }

    public void refreshCamera(Camera camera) {
        if (mHolder.getSurface() == null) {
            // preview surface does not exist
            return;
        }
        // stop preview before making changes
        stopPreview();
        // set preview size and make any resize, rotate or
        // reformatting changes here
        setCamera(camera);

        // start preview with new settings
        startPreview();
    }

    public void stopPreview(){
        try {
            if(mCamera != null)
            mCamera.stopPreview();
        } catch (Exception e) {
            // ignore: tried to stop a non-existent preview
            e.printStackTrace();
        }
    }

    public void startPreview(){
        try {
            if(mCamera != null) {
                mCamera.setPreviewDisplay(mHolder);
                mCamera.startPreview();
            }else{
                Log.d(VIEW_LOG_TAG, "Error starting camera preview: " );
            }
        } catch (Exception e) {
            Log.d(VIEW_LOG_TAG, "Error starting camera preview: " + e.getMessage());
        }
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        // If your preview can change or rotate, take care of those events here.
        // Make sure to stop the preview before resizing or reformatting it.
        refreshCamera(mCamera);
    }

    public void setCamera(Camera camera) {
        //method to set a camera instance
        mCamera = camera;
        /**
         * add camera orientation and display rotation according to screen landscape or portrait
         */
        setCameraRotation();
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        // TODO Auto-generated method stub
        if(mCamera != null){
            mCamera.release();
        }

    }

    public void setCameraRotation() {
        try {

            Camera.CameraInfo camInfo = new Camera.CameraInfo();

            if (VideoCaptureActivity.cameraId == 0)
                Camera.getCameraInfo(Camera.CameraInfo.CAMERA_FACING_BACK, camInfo);
            else
                Camera.getCameraInfo(Camera.CameraInfo.CAMERA_FACING_FRONT, camInfo);
            int cameraRotationOffset = camInfo.orientation;
            // ...

            Camera.Parameters parameters = mCamera.getParameters();


            int rotation = ((Activity)mContext).getWindowManager().getDefaultDisplay().getRotation();
            int degrees = 0;
            switch (rotation) {
                case Surface.ROTATION_0:
                    degrees = 0;
                    break; // Natural orientation
                case Surface.ROTATION_90:
                    degrees = 90;
                    break; // Landscape left
                case Surface.ROTATION_180:
                    degrees = 180;
                    break;// Upside down
                case Surface.ROTATION_270:
                    degrees = 270;
                    break;// Landscape right
            }
            int displayRotation;
            if (camInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
                displayRotation = (cameraRotationOffset + degrees) % 360;
                displayRotation = (360 - displayRotation) % 360; // compensate
                // the
                // mirror
            } else { // back-facing
                displayRotation = (cameraRotationOffset - degrees + 360) % 360;
            }

            mCamera.setDisplayOrientation(displayRotation);


            if (camInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
                rotate = (360 + cameraRotationOffset + degrees) % 360;
            } else {
                rotate = (360 + cameraRotationOffset - degrees) % 360;
            }

            parameters.set("orientation", "portrait");
            parameters.setRotation(rotate);
            mCamera.setParameters(parameters);

        } catch (Exception e) {

        }
    }



}

Now prepare media recorder with correct rotation so that recorded video play in right orientation.

mediaRecorder.setOrientationHint(CameraPreview.rotate);

private boolean prepareMediaRecorder() {

    mediaRecorder = new MediaRecorder();

    mCamera.unlock();
    mediaRecorder.setCamera(mCamera);

    mediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
    mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
    mediaRecorder.setOrientationHint(CameraPreview.rotate);

    mediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_LOW));

    mediaRecorder.setOutputFile(filePath);
    mediaRecorder.setMaxDuration(15000); // Set max duration 15 sec.
    mediaRecorder.setMaxFileSize(10000000); // Set max file size 1M

    try {
        mediaRecorder.prepare();
    } catch (IllegalStateException e) {
        releaseMediaRecorder();
        return false;
    } catch (IOException e) {
        releaseMediaRecorder();
        return false;
    }
    return true;

}

You can download complete sample https://github.com/umesh-kushwaha/Android_Video_Recording_portrait

like image 8
USKMobility Avatar answered Nov 09 '22 16:11

USKMobility