13

I record a video using the below code and it records perfectly, but when it plays the video, it plays it upside down.

I tried settings mrec.setOrientationHint(180) before mrec.prepare(); but it was useless. Any hints?

import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import android.app.Activity;
import android.hardware.Camera;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.Window;

/**
 * @author SANA HASSAN
 */
public class CameraSurfaceView extends Activity {

    private Preview mPreview;
    private MediaRecorder mrec = new MediaRecorder();
    private int cameraId = 0;
    private Camera mCamera;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        requestWindowFeature(Window.FEATURE_NO_TITLE);

        mPreview = new Preview(this);
        setContentView(mPreview);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        menu.add(0, 0, 0, "Start");
        menu.add(0, 1, 0, "Stop");
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case 0:
                try {
                    startRecording();
                } 
                catch (Exception e) {
                    e.printStackTrace();
                    mrec.release();
                }
                break;

            case 1: 
                mrec.stop();
                mrec.release();
                mrec = null;
                break;

            default:
                break;
        }
        return super.onOptionsItemSelected(item);
    }

    protected void startRecording() throws IOException  {

        mrec = new MediaRecorder();
        mrec.setCamera(mCamera);
        mCamera.unlock();
        File directory = new File(Environment.getExternalStorageDirectory()+"/NICUVideos");
        directory.mkdirs();
        mrec.setAudioSource( MediaRecorder.AudioSource.MIC);
        mrec.setVideoSource(MediaRecorder.VideoSource.CAMERA);
        mrec.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
        mrec.setOutputFile(Environment.getExternalStorageDirectory()+"/NICUVideos/"+System.currentTimeMillis()+".mp4"); 
        mrec.setPreviewDisplay(mPreview.getHolder().getSurface());
        mrec.setVideoSize(640, 480);

        Method[] methods = mrec.getClass().getMethods();
        for (Method method: methods){
            try{
                if(method.getName().equals("setAudioEncodingBitRate")){
                    method.invoke(mrec, 12200);
                }
                else if(method.getName().equals("setVideoEncodingBitRate")){
                    method.invoke(mrec, 800000);
                }
                else if(method.getName().equals("setAudioSamplingRate")){
                    method.invoke(mrec, 8000);
                }
                else if(method.getName().equals("setVideoFrameRate")){
                    method.invoke(mrec, 20);
                }
            }
            catch (IllegalArgumentException e) {
                e.printStackTrace();
            } 
            catch (IllegalAccessException e) {
                e.printStackTrace();
            } 
            catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
        mrec.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
        mrec.setVideoEncoder(MediaRecorder.VideoEncoder.MPEG_4_SP);
        mrec.setMaxDuration(60000); // 60 seconds
        mrec.setMaxFileSize(10000000); // Approximately 10 megabytes
        mrec.prepare();
        mrec.start();
    }

    protected void stopRecording() {
        mrec.stop();
        mrec.release();
        mCamera.release();
    }

    class Preview extends SurfaceView implements SurfaceHolder.Callback {
        SurfaceHolder mHolder;
        Activity activity;

        Preview(Activity activity) {
            super(activity);
            mHolder = getHolder();
            mHolder.addCallback(this);
            mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        }

        public void surfaceCreated(SurfaceHolder holder) {

            Camera.CameraInfo info=new Camera.CameraInfo();
            for (int i=0; i < Camera.getNumberOfCameras(); i++) {
                Camera.getCameraInfo(i, info);
                if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
                    mCamera=Camera.open(i);
                    cameraId = i;
                }
            }

            try {
               mCamera.setPreviewDisplay(holder);
            } catch (IOException exception) {
                mCamera.release();
                mCamera = null;
            }
        }

        public void surfaceDestroyed(SurfaceHolder holder) {
            mCamera.stopPreview();
            mCamera.release();
            mCamera = null;
        }

        public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
            setCameraDisplayOrientation(mCamera);
            mCamera.startPreview();
        }

        public void setCameraDisplayOrientation(Camera camera) {
            Camera.CameraInfo info = new Camera.CameraInfo();
            Camera.getCameraInfo(cameraId, info);

            int rotation = CameraSurfaceView.this.getWindowManager().getDefaultDisplay().getRotation();
            int degrees = 0;
            switch (rotation) {
                case Surface.ROTATION_0: degrees = 0; break;
                case Surface.ROTATION_90: degrees = 90; break;
                case Surface.ROTATION_180: degrees = 180; break;
                case Surface.ROTATION_270: degrees = 270; break;
            }

            int result;
            if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
                result = (info.orientation + degrees) % 360;
                result = (360 - result) % 360;  // compensate the mirror
            } else {  // back-facing
                result = (info.orientation - degrees + 360) % 360;
            }
            Log.d(Vars.TAG, "Result = "+result);
            camera.setDisplayOrientation(result);
        }
    }

}
Sana
  • 9,895
  • 15
  • 59
  • 87
  • Does this happen only on your device? Have u tried on different one? – Ron Jul 25 '12 at 14:58
  • Are you sure its not the media player settings.? Other videos playback in correct orientation? – Ron Jul 25 '12 at 15:00
  • Yes I am 100% sure that it is not media player settings. My phones are Atrix2, I am not bothered about other phones now. – Sana Jul 26 '12 at 02:28

10 Answers10

15

This problem is due to Android handling rotation by just setting some metadata instead of actually rotating the video, and some playback software then ignoring that setting.

As noted in the docs:

Note that some video players may choose to ignore the compostion matrix in a video during playback.

Your options are to either use different playback software that understands the metadata being set, or to re-encode the video after it's been recorded to the correct orientation. It's not clear from your description which of those would be the better solution in your case.

blahdiblah
  • 33,069
  • 21
  • 98
  • 152
7

This should be called before the mrec.prepare(); method

setOrientationHint(degrees);

Link

EDIT: Try mCamera.setDisplayOrientation(degrees);

0 for landscape
90 for portrait
180 & 270 don't work very well and give weird results.

Some older players and encoders do not interpret this flag which is why the video plays upside down.

Anirudh Ramanathan
  • 46,179
  • 22
  • 132
  • 191
  • I did, but that doesn't work, I already said that in my question. – Sana Jul 11 '12 at 05:50
  • Did you try `mrec.setDisplayOrientation(0);`? You shouldn't specify 180 there, as that messes with many video players. Also, try playing the video using a different video player which can interpret the composition matrix. – Anirudh Ramanathan Jul 11 '12 at 05:52
  • Instead of 180 what do I try then? and there is nothing of `mrec.setDisplayOrientation(0);` types, I think you are referring to the camera adjusting angle, I already take care of that if you see my code!!! I am targeting 2.3 devices – Sana Jul 11 '12 at 05:54
  • I already tried mCamera.setDisplayOrientation(90) for my front facing camera and it records perfectly, but when I go to the file present under one of my folders in /mnt/sdcard/folder/video.mp4 and click on it, it plays it 180 degrees rotated. – Sana Jul 11 '12 at 05:58
  • Already tried, last part of my code does that. Check my method `setCameraDisplayOrientation` – Sana Jul 11 '12 at 05:59
  • I have already tried that in the method `setCameraDisplayOrientation` and this takes care if the camera is front mode so sets `mCamera.setDisplayOrientation(90)` or if the back camera and sets `mCamera.setDisplayOrientation(0)' accordingly. – Sana Jul 11 '12 at 06:06
  • API level 10, that is Android 2.3.3 and I am specifically designing for Motorola Atrix2 devices. – Sana Jul 11 '12 at 06:15
  • this worked for me. now my recorded video is in portrait mode. – Manish Gupta Jul 16 '18 at 09:25
2

I know your issue,

Video use Media Recorder from Camera, so you need rotate Media Recorder. use below codes should be fixed your issue.

/**
 * 
 * @param mMediaRecorder
 * @return
 */
public static MediaRecorder rotateBackVideo(MediaRecorder mMediaRecorder) {
    /**
     * Define Orientation of video in here,
     * if in portrait mode, use value = 90,
     * if in landscape mode, use value = 0
     */
    switch (CustomCamera.current_orientation) {
        case 0:
            mMediaRecorder.setOrientationHint(90);
            break;
        case 90:
            mMediaRecorder.setOrientationHint(180);
            break;
        case 180:
            mMediaRecorder.setOrientationHint(270);
            break;
        case 270:
            mMediaRecorder.setOrientationHint(0);
            break;
    }

    return mMediaRecorder;
}

Should add before prepare() method :

// Step 5: Set the preview output
    /**
     * Define Orientation of image in here,
     * if in portrait mode, use value = 90,
     * if in landscape mode, use value = 0
     */
    CustomCamera.mMediaRecorder = Utils.rotateBackVideo(CustomCamera.mMediaRecorder);
    CustomCamera.mMediaRecorder.setPreviewDisplay(mCameraPreview.getHolder().getSurface());

Thank you

Huy Tower
  • 7,769
  • 16
  • 61
  • 86
2

I have the same problem, I note that camera preview orientation angle and recording video angle aren't the same. So I use this method for change orientation on video recording:

public static int getVideoOrientationAngle(Activity activity, int cameraId) { //The param cameraId is the number of the camera.
    int angle;
    Display display = activity.getWindowManager().getDefaultDisplay();
    int degrees = display.getRotation();
    android.hardware.Camera.CameraInfo info =
            new android.hardware.Camera.CameraInfo();
    android.hardware.Camera.getCameraInfo(cameraId, info);
    switch (degrees) {
        case Surface.ROTATION_0: 
            angle = 90; 
            break;
        case Surface.ROTATION_90:
            angle = 0;
            break;
        case Surface.ROTATION_180:
            angle = 270;
            break;
        case Surface.ROTATION_270:
            angle = 180;
            break;
        default:
            angle = 90;
            break;
    }
    if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT)
        angle = (angle + 180) % 360;

    return angle;
}

And that for change orientation on camera preview:

 public static int setCameraDisplayOrientation(Activity activity,
                                              int cameraId, android.hardware.Camera camera) {
    android.hardware.Camera.CameraInfo info =
            new android.hardware.Camera.CameraInfo();
    android.hardware.Camera.getCameraInfo(cameraId, info);
    int rotation = activity.getWindowManager().getDefaultDisplay()
            .getRotation();
    int degrees = 0;
    switch (rotation) {
        case Surface.ROTATION_0:
            degrees = 0;
            break;
        case Surface.ROTATION_90:
            degrees = 90;
            break;
        case Surface.ROTATION_180:
            degrees = 180;
            break;
        case Surface.ROTATION_270:
            degrees = 270;
            break;
    }

    int result;
    if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
        result = (info.orientation + degrees) % 360;
        result = (360 - result) % 360;  // compensate the mirror
    } else {  // back-facing
        result = (info.orientation - degrees + 360) % 360;
    }
    camera.setDisplayOrientation(result);
    return result;
}

Note that's important to know if the camera is front or back.

1

Use OrientationEventListener and track rotation value when device is rotated code here.This code apply rotation to camera but for recording you need apply rotation to MediaRecorder.When you start recording just mMediaRecorder.setOrientationHint(rotation) before mMediaRecorder.prepare(). This solve my problem.

Abu Yousuf
  • 5,729
  • 3
  • 31
  • 50
0

Finally I have found out that Motorola phones have problem in playing the video which is recorded in portrait mode.

In order to overcome the rotation of the video, the best solution I have adopted was to upload the video to the server and run ffmpeg on using the command ffmpeg -i input.mp4 -c:v mpeg4 -c:a copy -c:s copy -vf "transpose=2" output.mp4

If you feel there was another way, please do let me know.

Sana
  • 9,895
  • 15
  • 59
  • 87
0

one more solution is to rotate your activity so that its orientation is the same as sensor orientation. That is, landscape for back camera and upside-down portrait for front camera. This btw will not fix mirror effect for the front camera. One more difficulty is that you will have to implement your UI in those rotated activities in a way to follow the activity orientation.

Cynichniy Bandera
  • 5,991
  • 2
  • 29
  • 33
0

rotate your MediaRecorder like below corresponding the degree you used in Camera display orientation for front facing camera video recording

Display display = ((WindowManager)getContext().getSystemService(WINDOW_SERVICE)).getDefaultDisplay();
        parameters.setPreviewSize(height, width);
        if(display.getRotation() == Surface.ROTATION_0)
        {

            mCamera.setDisplayOrientation(90);
            mMediaRecorder.setOrientationHint(270);
        }

        if(display.getRotation() == Surface.ROTATION_90)
        {
            mMediaRecorder.setOrientationHint(180);
        }

        if(display.getRotation() == Surface.ROTATION_180)
        {
            mMediaRecorder.setOrientationHint(270);
        }

        if(display.getRotation() == Surface.ROTATION_270)
        {
            mCamera.setDisplayOrientation(180);
            mMediaRecorder.setOrientationHint(0);
        }
Shahab Rauf
  • 3,651
  • 29
  • 38
0

Here is the code for custom portrait camera, will set the correct rotation of picture and video:


private OrientationEventListener orientationEventListener;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    //...
    orientationEventListener = new OrientationEventListener(this) {
        @Override
        public void onOrientationChanged(int orientation) {
            if (orientation == ORIENTATION_UNKNOWN) return;

            flashButton.setRotation(-(orientation));
            cameraButton.setRotation(-(orientation));

            if (camera != null) {
                Parameters parameters = camera.getParameters();
                CameraInfo info = new CameraInfo();
                Camera.getCameraInfo(selectedCamera, info);
                orientation = (orientation + 45) / 90 * 90;
                int rotation = 0;
                if (info.facing == CameraInfo.CAMERA_FACING_FRONT) {
                    rotation = (info.orientation - orientation + 360) % 360;
                } else {  // back-facing camera
                    rotation = (info.orientation + orientation) % 360;
                }
                parameters.setRotation(rotation);
                if (!isRecording) {
                    mediaRecorder.setOrientationHint(rotation);
                }

                camera.setParameters(parameters);
            }
        }
    };
}


@Override
protected void onResume() {
    super.onResume();
    //...
    orientationEventListener.enable();
}

@Override
protected void onPause() {
    super.onPause();
    orientationEventListener.disable();
    //...
}

Teste with portrait orientation. Remeber to put it in your manifest to test code. I dont know if work in landscape.

<activity android:name=".activities.CameraActivity"
          android:screenOrientation="portrait">
IgniteCoders
  • 4,834
  • 3
  • 44
  • 62
  • This handle back and front camera pictures, but only back camera videos. The front camera videos crush now in my code. I will post the code for that case if i found the problem. – IgniteCoders Nov 18 '16 at 13:53
  • I dont know if the code work in landscape activity, if doesn't, all what you need is to +90 or -90 degrees to final rotation to fix it. – IgniteCoders Nov 18 '16 at 13:55
0

for portrait mode set your mediaRecorder.setOrientationHint(90); degree is same as your camera orientation myCamera.setDisplayOrientation(90);