I'm attempting to stream video using MediaRecorder on Android with the screen fixed to portrait mode (android:screenOrientation="portrait"). The camera hardware is naturally aligned to landscape mode. I can rotate the preview video display 90 degrees so the local preview displays correctly in portrait mode. However the captured video is still 90 degrees out:
Camera mCamera;
MediaRecorder mMediaRecorder;
...
mCamera.setDisplayOrientation(90);
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
Parameters params = mCamera.getParameters();
params.setRotation(90);
mCamera.setParameters(params);
mCamera.unlock();
mMediaRecorder.setCamera(mCamera);
The params.setRotation seems to have no effect whatsoever on the captured video. My target API is Android 2.2. My test hardware is Android 3.1.
Any ideas on how to rotate the captured video? Or is it not even possible?
Try using this:
mediaRecorder.setOrientationHint(rotation); // eg rotation=270
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
Related
When we will trying to record the video using MediaRecorder video is recorded properly in android and in device it will display as like recording, but when we can play video in VLC or other player in Desktop that time it will rotate the video and it will not display properly.
and i can set the MediaRecorder setOrientationHint to 90 degree.
what's the problem for changing the orientation and Why?
we Can not directly apply fix orientation while capturing video. I mean to say that you used fix 90 degree orientation in MediaRecorder setOrientationHint. you need to set setOrientationHint(dynamic degree);
First of all you need to get display rotation and get angle using display rotation. after then set That Degree to setOrientationHint method. That will work for all. Here is code.
Display display = getWindowManager().getDefaultDisplay();
int mDisplayRotation = display.getRotation();
public int getDisplayOrientationAngle() {
Log.e("", "setDisplayOrientationAngle is call");
int angle;
// switch (MeasurementNativeActivity.DisplayRotation) {
switch (mDisplayRotation) {
case Surface.ROTATION_0: // This is display orientation
angle = 90; // This is camera orientation
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;
}
Log.v("", "media recorder displayRotation: " + mDisplayRotation);
Log.v("", "media recorder angle: " + angle);
return angle;
}
mMediaRecorder.setOrientationHint(getDisplayOrientationAngle());
Extracted from the Android Documentation for MediaRecorder's setOrientationHint(int degrees) function:
This method will not trigger the source video frame to rotate during video recording, but to add a composition matrix containing the rotation angle in the output video if the output format is OutputFormat.THREE_GPP or OutputFormat.MPEG_4 so that a video player can choose the proper orientation for playback.
To sum it up, setOrientationHint just adds some sort of header to the video file that "tells" video players that they should rotate the video when playing it. In my experience, VLC player ignores this header and plays the video as it was recorded.
The only workaround I can think of would be to post-process the video by rotating it to your needs, although it seems quite a bad decision resource-wise.
I custom a camera app:
I set camera display portrait as:
mCamera.setDisplayOrientation(90);
Camera display portrait Ok, But file result (Video) not display portrait .
I try code:
Camera.Parameters parameters = mCamera.getParameters();
parameters.setRotation(90);
mCamera.setParameters(parameters);
But it not ok.
Why ?
How fix this problem? Thanks.
Manual for setDisplayOrientation() says:
Set the clockwise rotation of preview display in degrees
and then
This does not affect the order of byte array passed in onPreviewFrame(byte[], Camera), JPEG pictures, or recorded videos.
You cannot rotate saved video stream, only preview.
here is a demo which work with both portrait and landscape see here
source
https://github.com/pikanji/CameraPreviewSample
demo
https://play.google.com/store/apps/details?id=net.pikanji.camerapreviewsample&hl=en
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
I tried to modify the FacePreview.java sample provided for Android and since that code always open your camera in landscape mode, I added orientation logic in Preview.java, once we opens the camera:
mCamera.setDisplayOrientation(90);
When I ran the program I found always the Object detection i.e. cvHaarDetectObjects only works if the phone is in landscape mode, in other modes mainly in portrait, it's not at all detecting any face.
What is the reason behind this?
faces = cvHaarDetectObjects(
grayImage,
classifier,
storage,
1.1,
3,
CV_HAAR_FEATURE_MAX
| CV_HAAR_SCALE_IMAGE
| CV_HAAR_FIND_BIGGEST_OBJECT
| CV_HAAR_DO_ROUGH_SEARCH
| CV_HAAR_DO_CANNY_PRUNING);
faces.total always returns 0 when the phone is in portrait mode or orientation is different that landscape.
I tried modifying places but didn't find the expected.
It seems that in OpenCV/JavaCV Haar classifier (xml file) was trained using landscape images. You can rotate your Mat object before face detection:
public static opencv_core.Mat rotate(opencv_core.Mat src, int angle) {
opencv_core.IplImage iplSrc = src.asIplImage();
opencv_core.IplImage iplRes = opencv_core.IplImage.create(
iplSrc.height(),
iplSrc.width(),
iplSrc.depth(),
iplSrc.nChannels()
);
cvTranspose(iplSrc, iplRes);
cvFlip(iplRes, iplRes, angle);
return new opencv_core.Mat(iplRes);
So if you are loading your Mat from some image file, you can do something like that:
opencv_core.Mat mat = imread(imagePath, CV_LOAD_IMAGE_GRAYSCALE);
int orientation = ExifInterface.ORIENTATION_UNDEFINED;
try {
ExifInterface exif = new ExifInterface(imagePath);
orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1);
} catch (IOException e) {
e.printStackTrace();
}
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
mat = MatUtils.rotate(mat, 90);
break;
}
My camera app processes raw camera frames from onPreviewFrame() with OpenCV and then displays them on the screen. However, the raw frames are oriented the way the camera is mounted on the phone, which is usually not right-side up. To fix this, I rotate them manually with OpenCV, which is time-consuming.
I've looked into using setDisplayOrientation, but the documentation states that
This does not affect the order of byte array passed in onPreviewFrame
and I need the data to actually be right-side up, not just display right-side up. How can I correctly orient the raw camera data? If this is impossible, can I efficiently rotate the byte array passed to me in onPreviewFrame(), say by using OpenGL?
No, there's no way to get the API to do this for you, but to figure out how you need to rotate the data, take a look at the sample code at Camera.Parameters.setRotation. While setRotation() only affects JPEGs, you want to apply the exact same amount of rotation to the data you get in onPreviewFrame() as you would to the JPEGs.
Reproducing the sample code here, with minor changes:
public void onOrientationChanged(int orientation) {
if (orientation == ORIENTATION_UNKNOWN) return;
android.hardware.Camera.CameraInfo info =
new android.hardware.Camera.CameraInfo();
android.hardware.Camera.getCameraInfo(cameraId, 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;
}
mCameraOrientation = rotation; // Store rotation for later use
}
...
void onPreviewFrame(byte[] data, Camera camera) {
switch(mCameraOrientation) {
case 0:
// data is correctly rotated
break;
case 90:
// rotate data by 90 degrees clockwise
case 180:
// rotate data upside down
case 270:
// rotate data by 90 degrees counterclockwise
}
}
So you need to inherit from OrientationEventListener and override onOrientationChanged as above, and then use the calculated orientation value from there to rotate the preview frames when they come in.
setDisplayOrientation only rotates the camera vs. surface you are drawing into, if I recall correctly.
I believe you want to rotate the camera interpretation, what you may could achieve with setRotation
The documentation page for Camera.setDisplayOrientation contains the following code sample stating that using it will "make the camera image show in the same orientation as the display":
public static void 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);
}
However, I had problems using it, sometimes the image would appear upside down. After some trial-and-error, I found that the correct code would be (replacing the last 8 lines of the method):
int result = (360 + info.orientation - degrees) % 360;
camera.setDisplayOrientation(result);
(Which means the calculation for back-facing cameras is correct for front cameras too.) The "compensate the mirror" comment is a bit weird since mirroring cannot be undone by rotating, that operation only swaps 90° and 270° rotations which doesn't really make sense to me.
So the question is: is the sample wrong indeed or am I missing something? I tried it on multiple devices, both back and front cameras and all supported orientations, so I know that my code IS working. One little detail that might be worth mentioning: all my devices returned 90° as info.orientation.
EDIT: Here is my camera-related code, I have tested it on a Nexus One and a Samsung Galaxy S Plus. It is used in my head-tracking 3D app, the preview is shown in the lower left corner and should always have the correct orientation.
SOLUTION (sort of): It looks like the code is correct, but my testing phone (Samsung Galaxy S Plus) returns an incorrect value for the front camera's CameraInfo.orientation. There are many related discussions about the preview being shown upside down on this model (examples here and here). A way to fix is to include an option to manually rotate the image.
The snippet you've quoted, which I used and applied in my project, is no problem in my circumstances.
For all the devices (Galaxy Note, Galaxy S III, Galaxy Nexus, Galaxy Ace II, Nexus S) I used for test, info.Orientation all return 270 on front camera, and 90 on back camera.
After a few discuss with the question raiser, I found I misunderstood the questions, so I divide the answer in two parts.
For the wrong orientation in camera preview, please refer to this solution:
Solution for wrong orientation in camera preview:
First please ensure info.Orientation will return 270 on front camera, 90 on back camera.
Then please try set your camera preview activity (or similar class that handles the preview) orientation to landscape.
Thus, when you go through the code, you'll find:
degree = 90 for screen orientation, info.Orientation = 270 for the front camera. Then you'll get result = (270 - 90 + 360) % 360, result = 180, which means it will rotate clock wise 180 for your front camera view that will correct the front camera upside-down issue.
Solution for wrong orientation in camera photo results:
If this info.Orientation applies to you, then the problem may be:
For some Samsung (or other) devices (Like Galaxy Note, Galaxy SIII), the camera will only write the Orientation Tag instead of rotate the real pixels.
If you're using the front camera and using the code above, it will display the preview with correct orientation, but will show u the "upside down" picture if you take the shot.
Solution:
/**
*
* Get the orientation from EXIF
* #param filepath
* #return orientation
*/
public int getExifOrientation(String filepath) {
int degree = 0;
ExifInterface exif = null;
try {
exif = new ExifInterface(filepath);
} catch (IOException ex) {
Log.e("EXIF info", "cannot read exif", ex);
}
if (exif != null) {
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, -1);
if (orientation != -1) {
// We only recognize a subset of orientation tag values.
switch(orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
degree = 90;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
degree = 180;
break;
case ExifInterface.ORIENTATION_ROTATE_270:
degree = 270;
break;
}
}
} else {
degree = 1;
}
Log.i("EXIF info", "Orientation degrees: " + degree);
return degree;
}
Then
if (isFromCamera) {
if (fromFrontCam) {
// try change here if the orientation still wrong, -90 means rotate counter-clockwise 90 degrees.
matrix.preRotate(-90);
} else {
matrix.preRotate(90);
}
} else {
// read EXIF data
getExifOrientation(path)
// don't forget to handle the situation that some devices won't write exif data
matrix.preRotate(degree);
}