Cameraview: Recorded videos are not cropped

Created on 1 Oct 2017  路  20Comments  路  Source: natario1/CameraView

I used your application to implement capturing images, I use the crop output in XML to crop the image. This works fine, here is the code.

android:id="@+id/camera"
app:cameraJpegQuality="75"
app:cameraSessionType="video"
app:cameraWhiteBalance="auto"
android:keepScreenOn="true"
app:cameraCropOutput="true"
android:layout_width="wrap_content"
android:soundEffectsEnabled="false"
android:layout_height="@dimen/camera_crop_amount" />

I am now implementing capturing video, using the same XML properties. However, my video is not being cropped to the same size as the preview. Is there any reason why this happens? I am more than happy to provide additional information if required. Please let me know.

Just checked your demo app that doesn't seem to crop video output either.

P.S I am using the Samsung Galaxy S7

Thanks

enhancement fixed in v2

Most helpful comment

I have not released anything as open source but i can tell you how to achieve this. I use FFmpeg. You can either build it yourself and convert it to Android however I used this lib https://github.com/WritingMinds/ffmpeg-android-java. There is one problem with this library though after loading the library it messes with the apps layout, so I downloaded the library and fixed it myself by going to the Util class (fmpeg-android-java/FFmpegAndroid/src/main/java/com/github/hiteshsondhi88/libffmpeg/Util.java)

Change this line:

static boolean isDebug(Context context) {
        return (0 != (context.getApplicationContext().getApplicationInfo().flags &= ApplicationInfo.FLAG_DEBUGGABLE));
    }

to

static boolean isDebug(Context context) {
        return (0 != (context.getApplicationContext().getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE));
    }

Basically remove the '=' sign.

Next use the lib in your class:

`Fmpeg ffmpeg = FFmpeg.getInstance(context);
try {
ffmpeg.loadBinary(new LoadBinaryResponseHandler() {
@Override
public void onSuccess() {

String[] cmd = {
                        "-i",
                        videoPath,
                        "-filter:v",
                        "crop=in_w:in_h-" + cropOutputVideoAmount,
                        "-codec:v",
                        "libx264",
                        "-profile:v",
                        "high",
                        "-preset",
                        "ultrafast",
                        "-c:a", "copy",
                        videoCroppedFile.getAbsolutePath()
                };

try {

ffmpeg.execute(cmd, new ExecuteBinaryResponseHandler() {

@Override
public void onStart() {}

@Override
public void onProgress(String message) {}

@Override
public void onFailure(String message) {}

@Override
public void onSuccess(String message) {
// new cropped video now stored at videoCroppedFile path
}

@Override
public void onFinish() {}

});
} catch (FFmpegCommandAlreadyRunningException e) {
// Handle if FFmpeg is already running
}
}

});
} catch (FFmpegNotSupportedException e) {
// Handle if FFmpeg is not supported by device
}`

feel free to ask any questions. Sorry for messy code structure

All 20 comments

It's true, cropOutput only works with pictures right now.

What logic is required to enable the crop? I could possibly work on something for it. Would I go frame by frame and crop each one as if it were a picture?

I think it's not trivial at all.. it could take a lot of seconds depending on resolution, length and frame rate, and we can't afford that. It probably should be done at the NDK level (but that would be a burden for the library size)

Honestly, I don't know :)

We can consider making it optional. For instance, if we find a well-thought library that does this really fast, we can ask the developer (if he is interested) to add that library to their own project, alongside with CameraView.

Then at runtime CameraView should check if the library is present. If it is, crop the video.

Okay that's fine thanks
You can close this issue if you'd like to now

Let's see, if we make it optional this enhancement would be cool

I ended up cropping the videos with FFmpeg, it actually works very well, I have a question though does your code take into account orientation of the videos too and rotate that accordingly?

Is your solution open source, visible somewhere? Which library are you using? We don't rotate the bytes, we set the location tag using MediaRecoder.setLocation

I have not released anything as open source but i can tell you how to achieve this. I use FFmpeg. You can either build it yourself and convert it to Android however I used this lib https://github.com/WritingMinds/ffmpeg-android-java. There is one problem with this library though after loading the library it messes with the apps layout, so I downloaded the library and fixed it myself by going to the Util class (fmpeg-android-java/FFmpegAndroid/src/main/java/com/github/hiteshsondhi88/libffmpeg/Util.java)

Change this line:

static boolean isDebug(Context context) {
        return (0 != (context.getApplicationContext().getApplicationInfo().flags &= ApplicationInfo.FLAG_DEBUGGABLE));
    }

to

static boolean isDebug(Context context) {
        return (0 != (context.getApplicationContext().getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE));
    }

Basically remove the '=' sign.

Next use the lib in your class:

`Fmpeg ffmpeg = FFmpeg.getInstance(context);
try {
ffmpeg.loadBinary(new LoadBinaryResponseHandler() {
@Override
public void onSuccess() {

String[] cmd = {
                        "-i",
                        videoPath,
                        "-filter:v",
                        "crop=in_w:in_h-" + cropOutputVideoAmount,
                        "-codec:v",
                        "libx264",
                        "-profile:v",
                        "high",
                        "-preset",
                        "ultrafast",
                        "-c:a", "copy",
                        videoCroppedFile.getAbsolutePath()
                };

try {

ffmpeg.execute(cmd, new ExecuteBinaryResponseHandler() {

@Override
public void onStart() {}

@Override
public void onProgress(String message) {}

@Override
public void onFailure(String message) {}

@Override
public void onSuccess(String message) {
// new cropped video now stored at videoCroppedFile path
}

@Override
public void onFinish() {}

});
} catch (FFmpegCommandAlreadyRunningException e) {
// Handle if FFmpeg is already running
}
}

});
} catch (FFmpegNotSupportedException e) {
// Handle if FFmpeg is not supported by device
}`

feel free to ask any questions. Sorry for messy code structure

Is your solution open source, visible somewhere? Which library are you using? We don't rotate the bytes, we set the location tag using MediaRecoder.setLocation

Does this mean I dont have to rotate videos myself? to the correct orientation? So far it looks like you do rotate videos properly

I meant orientation, not location. We use MediaRecorder.setOrientationHint. Generally you shouldn't have to do any rotation, if your video reader respects this orientation tag. Most should do. Thanks for your solution! We'll see if this gets implemented

No problem thanks for the great library

@UGhari I wonder how much time it takes in cropping a video of about 1 minute, that too having 720p of videoQuality. I too used the ffmpeg with similar command to get the cropping done. But it costs me 10 seconds. I wonder how I can improve the cropping.

@natario1 I guess somehow the library can be tweaked in order to capture the video directly in square size (may be by doing something with computeCaptureSize(), say we change the method to use only produce aspectRatio of 1:1).

Please let me know what can be done. Thanks much for the library.

My opinion is that the whole cropOutput thing is a mess. It's just not worth the benefit, and even for pictures it can cause OOM errors and is slow. I'm thinking that probably it should be removed in v2.

The library could improve the video size support, yes. #17 . So devices which support it, might be able to record directly in 1:1. It's just hard to implement that while respecting the VideoQuality attribute semantics and the preview size.

Agree to what you said, and therefore I anyway have't used cropOutput at all. Instead I changed the onMeasure to provide squarePreview and the square photo capture works. Just the video sucks.
More on this, I am using your library to make an app that entertains exactly what Instagram is offering in Custom Video Camera. I record small clips and then join those using ffmpeg, but when I add cropping filter along with joining, it just takes hell lot of time. That's why I too am trying to figure a way out of this square format issue. Will surely let you know if I find something. @natario1

onMeasure is better left as it is. I think I'll make it final because overriding gives lots of issues. You should

  • use setPictureSize(SizeSelectors.aspectRatio(AspectRatio.of(1, 1))) to achieve the desired aspect ratio for pictures
  • set width and height to wrap_content (it'll be 1:1 to match the picture size)

Now you have 1:1 preview and pictures. For video, you should edit mediaRecorder.setVideoSize() inside the Camera1 class so it records videos in 1:1 if supported by the Camera.Parameters. Check params.getSupportedVideoSizes() before. I think that very few devices support that, but I might be wrong.

Any resolutions or idea so far? @natario1
I tried all my ways but the square video record without crop is just not working. And cropping takes hell lot of time.

@natario1

use setPictureSize(SizeSelectors.aspectRatio(AspectRatio.of(1, 1))) to achieve the desired aspect ratio for pictures

This solution didn't work for me, or please tell me where should i use it, may be i used it in wrong place :) Thanks

This is not supported guys, please subscribe to the issue be updated in the future.

@UGhari perfect man , very much appreciated that ffmpeg was missing in layout , that was wired '=' to find out ,thank you very much.

Was this page helpful?
0 / 5 - 0 ratings