Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WebRTC Webcam constraints don't adapt to device orientation

I am implementing a photo-taking application in html and javascript (actually Angular 1.x). The app is mostly used on Android tablets but also phones and Windows computers.

My Problem
when the user flips the tablet (portrait / landscape), the camera "flips" as well. Meaning, when you hold your tablet in landscape mode the camera is in landscape mode as well and when you flip the tablet, the camera is in portrait mode. But the system keeps the width and height parameters of the camera.

enter image description here

This wouldn't be much of a problem if I where to display the video only, but I need to copy the image, crop it, scale it etc. So I need to be sure that the camera's width is actually the width.

My implementation
To give you an idea, I tried to extract the code that is responsible for the camera:

// I have a static array with possible camera resolutions
private resolutions = [
    {width: 320, height: 240},
    {width: 600, height: 480}
];

// and the camera constraints that I initialize like this
this.constraints = {
    audio: false,                  // no audio needed
    video: {
        facingMode: "environment", // I want the back camera
        width: {
            exact: 4096            // initial width 
        },
        height: {
            exact: 2160            // initial height
        }
    }
};

// I use that array to query the system for a camera that fits these resolution constraints (it's recursive, so I call this function with the last index of my resolutions-array)
testCameraResolution(resolutionIndex: number) {
    if (this.checking) {
        this.constraints.video.width.exact = this.resolutions[resolutionIndex].width;    // overwrite the width value of the constraints
        this.constraints.video.height.exact = this.resolutions[resolutionIndex].height;   // overwrite the height value of the constraints
        navigator.mediaDevices.getUserMedia(this.constraints).then((stream: MediaStream) => {
            // when the navigator returns a camera with these constraints, I found my resolution and can stop testing.
            this.checking = false;  
            for (let track of stream.getTracks()) {
                track.stop();   // I stop the camera stream
            }
            this.videoResolution.width = this.constraints.video.width.exact;
            this.videoResolution.height = this.constraints.video.height.exact;
            this.startWebCam();  // and start the webcam
        }).catch((error) => {
            // no camera was found that matches these constraints, continue testing
            if (resolutionIndex > 0) {
                this.testCameraResolution(resolutionIndex - 1);
            }
        });
    }
}

How it works
The page loads and my script will try to start the webcam with {width: 600, height: 480}, if the navigator cannot return a camera with these constraints, the script will continue and test {width: 320, height: 240}.

Let's say the navigator can return a camera with 320x240, then the page will finish loading and I can display and manipulate the image. Nice. Always taking it for granted, that the video's width is the number of pixels from "left to right" and it's height spans from "top" to "bottom".

But when I load the page on a tablet and I flip the tablet to "portrait" mode, the navigator still gives me the camera with {width: 320, height: 240}, although it displays it with width=240 and height=320 (in portrait mode). So now all my image manipulation doesn't work anymore because width and height are inversed.

My Solution
So I figured, when the page is in "portrait" mode (the browser window is higher than it is wide), then I just inverse width and height of the camera. And this actually works on a tablet - but of course it doesn't work on a desktop computer.

So here is my actual question Is there a way to query the "navigator" if the whole device is in "portrait" or "landscape" mode without relying on the browsers width and height?

like image 330
GameDroids Avatar asked Jun 04 '19 08:06

GameDroids


2 Answers

If you are sure it doesn't necessarily need to work on safari. You can use the Screen API.

https://developer.mozilla.org/en-US/docs/Web/API/Screen/orientation

In that page you have an example.

like image 121
Nicolas Nobile Avatar answered Sep 19 '22 15:09

Nicolas Nobile


In short: There is not a solution for all devices. Like @Nicolas Nobile pointed out, the Screen API is the most useful tool. However, Safari still does not support it so you can use the workaround as described here: https://stackoverflow.com/a/36650956/10408987 (all Credit goes to him)

  1. Screen API
screen.orientation.onchange = function (){
    // logs 'portrait' or 'landscape'
    console.log(screen.orientation.type.match(/\w+/)[0]);
};
  1. Detect yourself (on safari):
 function getOrientation(){
    var orientation = window.innerWidth > window.innerHeight ? "Landscape" : "Portrait";
    return orientation;
}

 window.onresize = function(){ getOrientation(); }

Just noticed that you asked 2 years ago. I came across the same problem so I thought I answer on how I solved it.

like image 45
Throvn Avatar answered Sep 20 '22 15:09

Throvn