html5 camera is blurry - getuserMedia autofocus not working - html

Did somebody try HTML5 video for capturing image from camera on mobile and have problem with blurry image? I try it on Samsung phone where image was sharp, and on LG G4 where image was blurry (android browser and Chrome on both mobile phones).
Has somebody this experience or can explain solution? Can some phones has these problems?
There is snippet of code which I use:
function startCamera(newSource) {
if (newSource) {
var constraints = {
audio: false,
video: {
optional: [
{ sourceId: newSource },
]
}
};
addVideoAndCanvas();
navigator.getUserMedia(constraints, onMediaSuccess, onMediaError);
}
}
function addVideoAndCanvas() {
self.video = $('<video muted autoplay>');
self.canvas = $('<canvas id="qr-canvas" class="hide">');
$('.modal-body .video-container', self.root).append(self.video).append(self.canvas);
}
function onMediaSuccess(stream) {
self.stream = stream;
self.video[0].src = (window.URL && window.URL.createObjectURL(stream)) || stream;
self.video[0].onloadeddata = function () {
self.canvas[0].height = self.video[0].videoHeight;
self.canvas[0].width = self.video[0].videoWidth;
scan();
}
}
function onMediaError(error) {
}
function scan() {
if (self.stream) {
try {
var ctx = self.canvas[0].getContext('2d');
ctx.drawImage(self.video[0], 0, 0);
} catch (e) {
setTimeout(scan, 20);
}
} else {
setTimeout(scan, 20);
}
}

Probably waiting on folowing spec: https://w3c.github.io/mediacapture-image/index.html#FocusMode

Related

How can I get lock screen controls to show up on iPhone when using multiple HTML audio elements?

I'm using four HTML audio elements in a page in a React application and would like to get the lock screen media controls (play, pause, next, previous etc.) to show up on iPhone, which I'd like to customise with Media Session API. It does show up sometimes but for some reason it's intermittent, maybe coming up one in four times. When I only use one audio element it always comes up.
And for some reason it will always come up when I click play, pause and then play again before locking the screen.
This is the code (I've removed the URLs for the audio sources but they're correct on my side)
class App extends Component<IAppProps, IAppState> {
private vocals: HTMLAudioElement;
private other: HTMLAudioElement;
private bass: HTMLAudioElement;
private drums: HTMLAudioElement;
constructor(props: any) {
super(props);
this.vocals = new Audio();
this.other = new Audio();
this.bass = new Audio();
this.drums = new Audio();
this.vocals.src = '';
this.other.src = '';
this.bass.src = '';
this.drums.src = '';
this.vocals.load();
this.other.load();
this.bass.load();
this.drums.load();
if ('mediaSession' in navigator) {
navigator.mediaSession.metadata = new MediaMetadata({
title: 'Unforgettable',
artist: 'Nat King Cole',
album: 'The Ultimate Collection (Remastered)',
artwork: [
{ src: 'https://dummyimage.com/96x96', sizes: '96x96', type: 'image/png' },
{ src: 'https://dummyimage.com/128x128', sizes: '128x128', type: 'image/png' },
{ src: 'https://dummyimage.com/192x192', sizes: '192x192', type: 'image/png' },
{ src: 'https://dummyimage.com/256x256', sizes: '256x256', type: 'image/png' },
{ src: 'https://dummyimage.com/384x384', sizes: '384x384', type: 'image/png' },
{ src: 'https://dummyimage.com/512x512', sizes: '512x512', type: 'image/png' },
]
});
}
this.state = {
playbackState: 'paused',
}
}
handlePlayClick = () => {
const { playbackState } = this.state;
if (playbackState === 'paused') {
this.setState({ playbackState: 'playing'});
navigator.mediaSession.playbackState = 'playing';
this.vocals.play();
this.other.play();
this.bass.play();
this.drums.play();
} else {
this.setState({ playbackState: 'paused'});
navigator.mediaSession.playbackState = 'paused';
this.vocals.pause();
this.other.pause();
this.bass.pause();
this.drums.pause();
}
}
render() {
return (
<div className="App">
<button onClick={this.handlePlayClick}>{this.state.playbackState === 'paused' ? 'PLAY' : 'PAUSE'}</button>
</div>
);
}
}

how do I release the camera and microphone after a WebRTC call?

After I end a WebRTC call, nothing I seem to do removes the red icon on the browser tab that says the camera or microphone are in use.
I iterate the tracks from videoElement.srcObject.getTracks() and call track.stop() on each one. I then delete the videoElement from the DOM, but still I have the red icon.
In my case, the problem was caused by a bug in my code due to my misunderstanding WebRTC and getUserMedia(). I was actually calling getUserMedia() twice, once for the local <video> element and a second time for adding to the RTCPeerConnection.
The fix was of course to only call getuserMedia() once and use the returned stream in both places.
(BroadcastChannel could not be used with stack overflow snippet, so provide sample code with Code Pen)
(I confirmed the operation on Chrome and Firefox)
Open the link in multiple tabs, check the WebRTC connection by clicking the Cnnect button on either side, and switch to the Close button, so clicking the Close button releases the Cam
https://codepen.io/gtk2k/pen/NWxzgKo?editors=1111
// open 2 tabs this page
const signalingChannel = new BroadcastChannel('signalingChannel');
let pc = null;
signalingChannel.onmessage = async evt => {
const msg = JSON.parse(evt.data);
if(msg.close) {
releaseStream();
return;
}
if(!pc)
await setupPC();
if(msg.sdp) {
console.log(`Receive ${msg.type}`);
await pc.setRemoteDescription(msg);
if(msg.type === 'offer') {
const answer = await pc.createAnswer();
await pc.setLocalDescription(answer);
sendSignaling(answer);
}
} else if(msg.candidate) {
console.log(`Receive candidate`);
await pc.addIceCandidate(msg);
}
}
async function setupPC(isCaller) {
pc = new RTCPeerConnection();
pc.onconnectionstatechange = evt => {
console.log(pc.connectionState);
if(pc.connectionState === 'disconnected')
{
releaseStream();
}
}
pc.onicecandidate = evt => {
if(evt.candidate)
sendSignaling(evt.candidate);
}
pc.ontrack = evt => {
vidRemote.srcObject = evt.streams[0];
}
const stream = await navigator.mediaDevices.getUserMedia({video:true});
stream.getTracks().forEach(track => pc.addTrack(track, stream));
vidLocal.srcObject = stream;
if(isCaller) {
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
sendSignaling(offer);
}
}
(async _ => {
const stream = await navigator.mediaDevices.getUserMedia({video:true});
vidLocal.srcObject = stream;
});
btnConnect.onclick = evt => {
if(btnConnect.textContent === 'Connect') {
btnConnect.textContent = 'Close';
setupPC(true);
} else {
btnConnect.textContent = 'Connect';
pc.close();
pc = null;
releaseStream();
sendSignaling({close: true});
}
}
function sendSignaling(data) {
signalingChannel.postMessage(JSON.stringify(data));
}
function releaseStream() {
[vidLocal, vidRemote].forEach(vid => {
if(!vid.srcObject) return;
let stream = vid.srcObject;
vid.pause();
vid.srcObject = null;
stream.getTracks().forEach(track => track.stop());
stream = null;
});
}
video {
width: 360px;
height: 240px;
}
<button id="btnConnect">Connect</button>
<div>
<video id="vidLocal" muted autoplay></video>
<video id="vidRemote" muted autoplay></video>
</div>

How to choose camera for qrscan

What is the best strategy to choose camera for qrcode scanning?
Currently modern devices have few back cameras.
For example huawei mate 20 have 4 camera (3 physical and 1 virtual based on physical ones)
Currently my algorithm just selecting first camera with "back" in label.
Is there any better strategy for best readability of qr code?
Here is my code:
this.qrScannerComponent.getMediaDevices().then(devices => {
// this.info = devices.map((dev, i) => `${i}. ${dev.label}`).join('\n');
const videoDevices: MediaDeviceInfo[] = [];
for (const device of devices) {
if (device.kind.toString() === 'videoinput') {
videoDevices.push(device);
}
}
if (videoDevices.length > 0) {
let choosenDev;
for (const dev of videoDevices) {
if (dev.label.includes('back')) {
choosenDev = dev;
break;
}
}
if (choosenDev) {
this.qrScannerComponent.chooseCamera.next(choosenDev);
} else {
this.qrScannerComponent.chooseCamera.next(videoDevices[0]);
}
}
});

React Native Map (Airbnb) + Markers animation

I want to animate the React-native-maps {Google} markers.
I tried with the animated module, but the markers do not allow complex styles.
Is there any function to modify the coordinates of the marker and give it animation?, like a:
marker.setAnimation(google.maps.Animation.BOUNCE);
I have tried with:
<MapView.Marker.Animated>
But I can not create the effect. Is there a function that edits the coordinates as an animation drop?
React native map marker is by default "not animated", it can not accept gif images, sprites, animation api and so on . . However, I was able to animate it the tough way through image transition. Here is my example:
constructor(props, context) {
super(props, context);
this.state = {
startTransition: 1,
endTransition: 4,
};
}
componentDidMount() {
this.animate();
}
animate() {
const {startTransition, endTransition} = this.state;
if(startTransition < endTransition){
let currentTransition = startTransition + 1;
this.setState({startTransition: currentTransition});
} else {
this.setState({startTransition: 1});
}
let x = setTimeout(()=>{
this.animate()
}, 500);
}
renderImg(imgTrans) {
if(imgTrans === 1) {
return require('./walk1.png');
}
if(imgTrans === 2) {
return require('./walk2.png');
}
if(imgTrans === 3) {
return require('./walk3.png');
}
if(imgTrans === 4) {
return require('./walk4.png');
}
}
render() {
const {startTransition} = this.state;
return (
<MapView.Marker
coordinate={tapCoords}
image={this.renderImg(startTransition)}
>
)
}
This is how I did the animation for now.

Determine DRM system supported by browser

I've trying to find out how to determine which DRM system browser is using. And in fact, only chrome say it is use 'com.widevine.alpha' where IE & Safari (Win) throw error on 'requestMediaKeySystemAccess', and firefox do not even try to say it use 'com.adobe.acccess' =]
function isKeySystemSupported(keySystem) {
var dfd = Q.defer();
console.log('check: ', keySystem);
navigator.requestMediaKeySystemAccess(keySystem, [{contentType: 'video/webm; codecs="vp9"'}]).then(function() {
dfd.resolve(true);
}, function() { dfd.resolve(false); } );
return dfd.promise;
}
is there any solution, like Modernizr or similar to get which keySystem I should use?
There are several websites offering such a check, like dash-player.com/browser-capabilities/ After having a closer look at how it is done, one can use something similar to:
// EME Check
var keySystems = {
widevine: ['com.widevine.alpha'],
playready: ['com.microsoft.playready', 'com.youtube.playready'],
clearkey: ['webkit-org.w3.clearkey', 'org.w3.clearkey'],
primetime: ['com.adobe.primetime', 'com.adobe.access'],
fairplay: ['com.apple.fairplay']
};
var keySystemsCount = (function () {
var count = 0;
for (keysys in keySystems) {
if (keySystems.hasOwnProperty(keysys)) {
count += keySystems[keysys].length;
}
}
return count;
})();
var testVideoElement = document.createElement('video');
var supportedSystems = [];
var unsupportedSystems = [];
var supportsEncryptedMediaExtension = function () {
if (!testVideoElement.mediaKeys) {
if (window.navigator.requestMediaKeySystemAccess) {
if (typeof window.navigator.requestMediaKeySystemAccess === 'function') {
console.log('found default EME');
hasEME = true;
var isKeySystemSupported = function (keySystem) {
var config = [{initDataTypes: ['cenc']}];
if (window.navigator.requestMediaKeySystemAccess) {
window.navigator.requestMediaKeySystemAccess(keySystem, config).then(function (keySystemAccess) {
supportedSystems.push(keySystem);
}).catch(function () {
unsupportedSystems.push(keySystem);
});
}
};
var keysys, dummy, i;
for (keysys in keySystems) {
if (keySystems.hasOwnProperty(keysys)) {
for (dummy in keySystems[keysys]) {
isKeySystemSupported(keySystems[keysys][dummy]);
}
}
}
}
} else if (window.MSMediaKeys) {
if (typeof window.MSMediaKeys === 'function') {
console.log('found MS-EME');
hasEME = true;
var keysys, dummy, i;
for (keysys in keySystems) {
if (keySystems.hasOwnProperty(keysys)) {
for (dummy in keySystems[keysys]) {
if (MSMediaKeys.isTypeSupported(keySystems[keysys][dummy])) {
supportedSystems.push(keySystems[keysys][dummy]);
} else {
unsupportedSystems.push(keySystems[keysys][dummy]);
}
}
}
}
}
} else if (testVideoElement.webkitGenerateKeyRequest) {
if (typeof testVideoElement.webkitGenerateKeyRequest === 'function') {
console.log('found WebKit EME');
hasEME = true;
var keysys, dummy, i;
for (keysys in keySystems) {
if (keySystems.hasOwnProperty(keysys)) {
for (dummy in keySystems[keysys]) {
if (testVideoElement.canPlayType('video/mp4', keySystems[keysys][dummy])) {
supportedSystems.push(keySystems[keysys][dummy]);
} else {
unsupportedSystems.push(keySystems[keysys][dummy]);
}
}
}
}
}
} else {
console.log('no supported EME implementation found');
hasEME = false;
}
}
}
Simply run supportsEncryptedMediaExtension() and supportedSystems will be filled with the desired information.
Note that the config object should be extended to include the specific codec claims respective to your particular media. It isn't enough to just detect the key system as codec support sometimes depends on Guest OS dependencies.
var config = [{
"initDataTypes": ["cenc"],
"audioCapabilities": [{
"contentType": "audio/mp4;codecs=\"mp4a.40.2\""
}],
"videoCapabilities": [{
"contentType": "video/mp4;codecs=\"avc1.42E01E\""
}]
}];
In addition to the information listed here, I want to mention that in Chrome, whether you are using https or not will affect the availability of navigator.requestMediaKeySystemAccess function.
In your development environment that probably is running on http, navigator.requestMediaKeySystemAccess will return undefined for Chrome whereas the same code will return a function in Firefox.
In your prod environment that has https, navigator.requestMediaKeySystemAccess will return a function both in Chrome and Firefox.
I had to give videoCapabilities flags to make this work.
function testEME() {
// https://shaka-player-demo.appspot.com/support.html
var keySysConfig = [{
"initDataTypes": ["cenc"]
//,"persistentState": "required" // don't use or MacSafari "not supported"
//,"persistentState": "required", "distinctiveIdentifier": "required"
//,"audioCapabilities": [{
// "contentType": "audio/mp4;codecs=\"mp4a.40.2\""
//}]
,"videoCapabilities": [{
"contentType": "video/mp4;codecs=\"avc1.4D401E\"" // avc1.42E01E = ConstrainedLevel3, 4D401E=MainLevel3
//,"robustness": "3000"
}]
}];
var keySystems = {
playready: ['com.microsoft.playready.recommendation', 'com.microsoft.playready'
, 'com.microsoft.playready.hardware', 'com.youtube.playready'],
clearkey: ['webkit-org.w3.clearkey', 'org.w3.clearkey'],
widevine: ['com.widevine.alpha'],
primetime: ['com.adobe.primetime', 'com.adobe.access'],
fairplay: ['com.apple.fairplay','com.apple.fps'
, 'com.apple.fps.1_0', 'com.apple.fps.2_0', 'com.apple.fps.3_0']
};
for(keyArr in keySystems) {
for(forItemIdx in keySystems[keyArr]) {
let keySys = keySystems[keyArr][forItemIdx];
try {
navigator.requestMediaKeySystemAccess(keySys, keySysConfig).
then(function(mediaKeySystemAccess) {
//let mkConfig = mediaKeySystemAccess.getConfiguration();
//let sFlags = "persistentState="+mkConfig.persistentState
// + ", distinctiveIdentifier="+mkConfig.distinctiveIdentifier;
console.log(keySys + " supported"); //+" ("+sFlags+")");
}).catch(function(ex) {
console.log(keySys+" not supported (" + ex.name+" "+ex.message+")." );
});
} catch (ex) {
console.log(keySys+" not supported (" + ex.name+" "+ex.message+").." );
}
}
}
}