react-native-camera icon indicating copy to clipboard operation
react-native-camera copied to clipboard

Ability to set video resolutions (videoFrameWidth & videoFrameHeight)

Open makzee opened this issue 4 years ago • 18 comments

It adds the ability to set videoFrameWidth and videoFrameHeight.

Test Plan Recorded two videos, 1 with the desired resolutions and other without setting them at all. 2nd test is it make sure that it still works as it used to previously.

Test 1

const video = await cameraRef.recordAsync({ mute: true, quality: '4:3', videoFrameWidth: 1280, videoFrameHeight: 960 });

image

Test 2

const video = await cameraRef.recordAsync({ mute: true, quality: '4:3' });

image

makzee avatar Feb 23 '21 01:02 makzee

Nice! Do you think we can proceed to merge this @MarcoScabbiolo?

MateusAndrade avatar Feb 23 '21 14:02 MateusAndrade

I'm sorry I forgot to mention that it is Android only. I don't have iPhone, I can fix it later for iOS too.

Another thing I would like to mention is that the resolution should be supported by the device otherwise it would fail to record. For that we need to expose getSupportedVideoSizes(). I have looked into OpenCamera source code to see how it generates the list of supported video sizes, I'm currently working on that (Will create another PR for that).

makzee avatar Feb 23 '21 14:02 makzee

I think it will be best if you add the method to get the supported sizes to this PR, so the feature is complete once released. Mainly because of the many differences across Android devices.

MarcoScabbiolo avatar Feb 23 '21 15:02 MarcoScabbiolo

Sure I can do that.

makzee avatar Feb 23 '21 15:02 makzee

Does this replace setting quality='480p' or 720? How does these new props interact with the quality setting? As far as I know, the quality value already sets the video resolution/frame, doesn't it?

cristianoccazinsp avatar Feb 23 '21 16:02 cristianoccazinsp

With this change we can deprecate 4:3. Each quality setting maps to one of the CamcorderProfile. 4:3 (i.e. VIDEO_4x3) is a special quality that maps to QUALITY_480P (which is 480x720) and sets the videoFrameWidth to 640.

As far as I know, the quality value already sets the video resolution/frame, doesn't it?

Correct but all those profiles are 16:9. With this PR the width/height can be overridden to get the desired aspect ratio. For example: Currently it is not possible to record in high quality with aspect ratio of 4:3.

makzee avatar Feb 23 '21 17:02 makzee

I see, I'm slightly confused, but when I set 4:3 I always get consistent videos in either 720p or 480p (with different aspect rations for each quality, basically 4:3 is ignored right now?). Is ratio='4:3' even used when recording videos?

What would happen to iOS?

cristianoccazinsp avatar Feb 23 '21 17:02 cristianoccazinsp

I have added getAvailableVideoSizes(). It can be called like:

const videoSizes = await cameraRef.getAvailableVideoSizes();
console.log(videoSizes);

(Note: The result is for the ratio set in <RNCamera ratio={ratio} ... />)

The sample output is: ["320x240", "640x480", "960x720", "1280x960", "1440x1080"]

I originally wanted to return json (width & height in their own fields) instead of string but decided to keep it consistent with getAvailablePictureSizes() output. I would prefer to return json, I can make the change if that is ok? After change output will be like:

[{"height": 240, "width": 320}, {"height": 480, "width": 640}, {"height": 720, "width": 960}, {"height": 960, "width": 1280}, {"height": 1080, "width": 1440}]

makzee avatar Feb 24 '21 18:02 makzee

Isn't ratio an actual aspect ratio ("4:3", "16:9") ?

Does this mean that we need to we will need to "parse" the results in order to set videoFrameWidth and videoFrameHeight? It's probably a better idea to return a JSON as you mentioned.

cristianoccazinsp avatar Feb 24 '21 18:02 cristianoccazinsp

Isn't ratio an actual aspect ratio ("4:3", "16:9") ?

Yes, it is actual aspect ratio.

Does this mean that we need to we will need to "parse" the results in order to set videoFrameWidth and videoFrameHeight? It's probably a better idea to return a JSON as you mentioned.

Right, I did it to be consistent with getAvailablePictureSizes(). Let me change it to json.

makzee avatar Feb 24 '21 19:02 makzee

@makzee what will be the behaviour if invalid values are set? Have you also tried with all available cameras (front, back)?

cristianoccazinsp avatar Mar 02 '21 02:03 cristianoccazinsp

@makzee what will be the behaviour if invalid values are set? Have you also tried with all available cameras (front, back)?

When invalid values are set, it returns an error. Yes I have tested with both the cameras, it works fine.

I have added getAvailableVideoSizes(). Call it like:

const sizes = await cameraRef.getAvailableVideoSizes();

Reponse: [{"height": 240, "width": 320}, {"height": 480, "width": 640}, {"height": 720, "width": 960}, {"height": 960, "width": 1280}, {"height": 1080, "width": 1440}]

It is ready to be reviewed and merged.

makzee avatar Mar 02 '21 16:03 makzee

@cristianoccazinsp, @MarcoScabbiolo do you think we can go on merging this?

MateusAndrade avatar Mar 02 '21 19:03 MateusAndrade

It looks pretty good to me! Although I'm still not sure what's the benefit of setting the frame width/height, if you already "control" that with the quality parameter. For example, what would happen if the following options are used:

// change quality but use a fixed width/height ? {quality: '480p', videoFrameWidth: 1280, videoFrameHeight: 960} {quality: '720p', videoFrameWidth: 1280, videoFrameHeight: 960} {quality: '1080p', videoFrameWidth: 1280, videoFrameHeight: 960}

// The other way around {quality: '720p', videoFrameWidth: 1280, videoFrameHeight: 480} {quality: '720p', videoFrameWidth: 1280, videoFrameHeight: 720} {quality: '720p', videoFrameWidth: 1920, videoFrameHeight: 1080}

Looks like it may end up causing some confusion because the pixel dimension is usually strictly tied to the quality name.

cristianoccazinsp avatar Mar 02 '21 20:03 cristianoccazinsp

Each quality maps to one of the CamcorderProfile (https://developer.android.com/reference/android/media/CamcorderProfile). A profile has a fixed set of settings like fps, bitrate, resolution, codec etc but you are still allowed to change them as per your requirement. The reason for these changes was that we needed to record video in 4:3 at a higher resolution (not just 480p). Most of all these profiles correspond to 16:9, for example: QUALITY_1080P gives you 1920x1080. If I want to record 1080p in 4:3 (i.e. 1440x1080), this PR allows to you do that.

{quality: '1080p', videoFrameWidth: 1440}

It means "Use 1080p profile but override the width to 1440.".

Quality represents a baseline profile and we need to allow flexibility to change specific attributes of that profile.

makzee avatar Mar 02 '21 20:03 makzee

@makzee that greatly explains it, and makes complete sense. Good work!

Do we need this for iOS as well?

cristianoccazinsp avatar Mar 02 '21 21:03 cristianoccazinsp

Thanks @cristianoccazinsp.

Yes we need this for iOS as well but I don't have a bandwidth for it right now. I'm planning to implement this in a month or so.

makzee avatar Mar 02 '21 21:03 makzee

I see, I'm slightly confused, but when I set 4:3 I always get consistent videos in either 720p or 480p (with different aspect rations for each quality, basically 4:3 is ignored right now?). Is ratio='4:3' even used when recording videos? What would happen to iOS?

4:3 is actually 640:480 in iOS. Implementing this on iOS should be doable, AVCaptureMovieFileOutput settings include width and height keys, havent used/tested it myself though, they might be read-only. We should have in mind setting these outputsettings (including codecType) leads to the video being re-encoded as its recorded and might mean a performance hit as more settings are set.

(Note for v4): The options that make a difference between raw recording and re-encoding should be made clear even if they change across devices, mostly on Android. Remove 4:3

MarcoScabbiolo avatar Mar 03 '21 00:03 MarcoScabbiolo