Camera video not showing on iOS - ios

I'm attempting to access the camera of a current user device and display the camera's video before they can take a picture. It works perfectly on Android and browsers, but not on iPhones for some strange reason. Even when the screen is black, I can still capture a picture. It's just a dark video camera.
Snippet of the code
useEffect(() => {
navigator.mediaDevices
.enumerateDevices()
.then(function (devices) {
devices.forEach(function (device) {
if (device.kind == "videoinput") {
let temp = videoInputs;
temp.push(
device.kind + ": " + device.label + " id = " + device.deviceId
);
setVideoInputs(temp);
}
});
})
.catch(function (err) {
console.log(err.name + ": " + err.message);
});
}, []);
const getVideo = () => {
navigator.mediaDevices
.getUserMedia({
video: {
// width: 1920,
// height: 1080,
facingMode: orientation ? "environment" : "user",
},
})
.then((stream) => {
console.log(stream);
setError("");
setVideoStream(stream);
let video = videoRef.current;
video.srcObject = stream;
video.play();
})
.catch((err) => {
setError(err.toString())
// setError("Permission denied. Turn on your camera to continue");
console.error(err);
});
};
useEffect(() => {
getVideo();
return () => {
stopVideo();
};
}, [orientation]);

Related

react-native-webrtc Mic not closing after video call on iOS

Our iOS app has audio video calling implemented using the following technologies:
"react-native": "0.63.4"
"react": "16.13.1"
"react-native-webrtc": "^1.87.3"
"react-native-incall-manager": "^3.3.0"
iOS version 14.4.1
Our calling module works like the following:
First request and initiate audio call
Then request and initiate video call
On the code side things work like this:
We call the getStream() function which gets the user media for audio call i.e Audio only
Then we call the startStream() function which connects the peer connection
On requesting video we call the getVideoStream() method to get Audio and Video streams
Call startStream() again to start peer connection with video
The scenario is as follows:
We start off by connecting an audio call. On success the audio call is connected and works fine as expected
We request for video and connect video call, all works fine as expected and I receive video on both ends
When I disconnect the call and stop tracks using this.state.localStream.getTracks(), the mic does not close. An orange indicator for mic is visible on iOS.
Important Notes:
Disconnecting from the audio call closes the mic just fine
Even if we get video stream on audio call and disconnect without connecting video it still works fine and closes both tracks
Its only when I connect the video is when the issue arises
Calling InCallManager.stop() closes the mic but does not open it on second call. The mic does not open on second call and the orange mic indicator on iOS is not shown.
Get User Media Audio Call
getStream() {
InCallManager.setSpeakerphoneOn(false);
InCallManager.setMicrophoneMute(false);
mediaDevices.enumerateDevices().then((sourceInfos) => {
let videoSourceId;
for (let i = 0; i < sourceInfos.length; i++) {
const sourceInfo = sourceInfos[i];
if (
sourceInfo.kind === 'videoinput' &&
sourceInfo.facing === (true ? 'front' : 'back')
) {
videoSourceId = sourceInfo.deviceId;
}
}
mediaDevices
.getUserMedia({
audio: true,
})
.then((stream) => {
this.setState({
localStream: stream,
});
})
.catch((error) => {
// Log error
console.log('stream get error', error);
});
});
}
Get User Media for Video Call
getVideoStream() {
this.state.peerConn.removeStream(this.state.localStream);
InCallManager.setSpeakerphoneOn(false);
InCallManager.setMicrophoneMute(false);
mediaDevices.enumerateDevices().then((sourceInfos) => {
let videoSourceId;
for (let i = 0; i < sourceInfos.length; i++) {
const sourceInfo = sourceInfos[i];
if (
sourceInfo.kind === 'videoinput' &&
sourceInfo.facing === (true ? 'front' : 'back')
) {
videoSourceId = sourceInfo.deviceId;
}
}
mediaDevices
.getUserMedia({
audio: true,
mirror: true,
video: {
mandatory: {
minWidth: 500,
minHeight: 300,
minFrameRate: 30,
},
facingMode: true ? 'user' : 'environment',
optional: videoSourceId ? [{sourceId: videoSourceId}] : [],
},
})
.then((stream) => {
this.setState(
{
localStream: stream,
},
() => {
this.startStream();
},
);
})
.catch((error) => {
// Log error
console.log('stream get error', error);
});
});
}
Start Stream Function
startStream() {
console.log('start Stream');
this.newPeerConnection();
setTimeout(() => {
this.state.peerConn
.createOffer()
.then((sessionDescription) =>
this.setLocalAndSendMessage(sessionDescription),
)
.catch((error) => this.defaultErrorCallback(error));
}, 3000);
}
newPeerConnection()
newPeerConnection() {
var peerConn = new RTCPeerConnection({
iceServers: turnServer,
});
peerConn.onicecandidate = (evt) => {
console.log(`OnIceCan`);
if (evt.candidate) {
this.state.connection.invoke(
'addIceCandidate',
parseInt(this.state.ticket.pkTicketId),
JSON.stringify({
type: 'candidate',
sdpMLineIndex: evt.candidate.sdpMLineIndex,
sdpMid: evt.candidate.sdpMid,
candidate: evt.candidate.candidate,
}),
);
}
};
peerConn.addStream(this.state.localStream);
peerConn.addEventListener(
'addstream',
(stream) => {
InCallManager.setForceSpeakerphoneOn(false);
this.setState({
isSpeakerEnabled: false,
});
this.setState({
remoteStream: stream,
showAudioCallTimer: true,
});
},
false,
);
this.setState(
{
peerConn,
});
}
Close Tracks
if (this.state.localStream) {
const tracks = this.state.localStream.getTracks();
tracks.map((track, index) => {
track.stop();
});
}
if (this.state.peerConn) {
this.state.peerConn.removeStream(this.state.localStream);
this.state.peerConn.close();
if (!this.state.callRatingSubmitted && this.state.remoteStream) {
this._handleCallFeedbackModal(true);
}
}

WebRTC - how to switch between getUserMedia and getDisplayMedia tracks inside RTCPeerConnection

I'm trying to develop an app where users can can video call to each other and share their screens using WebRTC technology. I have succeed with either video call or screen sharing app and now I'm trying to make it to be able to switch between getUserMedia and getDisplayMedia on button click during a call inside the same RTCPeerConnection but it doesn't work.
This is how I thought it could work:
function onLogin(success) {
var configuration = { offerToReceiveAudio: true, offerToReceiveVideo: true, "iceServers" : [ { "url" : "stun:stun.1.google.com:19302" } ] };
myConnection = window.RTCPeerConnection ? new RTCPeerConnection(configuration, { optional: [] }) : new RTCPeerConnection(configuration, { optional: [] });
myConnection.onicecandidate = function (event) {
console.log("onicecandidate");
if (event.candidate) send({ type: "candidate", candidate: event.candidate });
};
myConnection.ontrack=function(e){
try{remoteVideo.src = window.webkitURL?window.webkitURL.createObjectURL(e.streams[0]):window.URL.createObjectURL(e.streams[0])}
catch(err){remoteVideo.srcObject=e.streams[0]}
}
myConnection.ondatachannel=openDataChannel
openDataChannel();
startAVStream();
//startSStream()
};
function startAVStream(enable){
if(sStream)sStream.getTracks().forEach( function (track) {
try{myConnection.removeTrack( track, sStream );}
catch(e){}
} );
navigator.mediaDevices.getUserMedia({ video: true, audio: true }).then(s => {
if(!avStream){
avStream = s;
avStream.getTracks().forEach( function (track) {
myConnection.addTrack( track, avStream );
} );
}
}, function (error) { console.log(error); });
}
function startSStream(enable){
if(avStream)avStream.getTracks().forEach( function (track) {
try{myConnection.removeTrack( track, avStream );}
catch(e){}
} );
navigator.mediaDevices.getDisplayMedia({ video: true }).then(s => {
if(!sStream){
sStream = s;
sStream.getTracks().forEach( function (track) {
myConnection.addTrack( track, sStream );
} );
}
}, function (error) { console.log(error); });
}
Can anyone tell me how I can switch between tracks inside the same RTCPeerConnection or should I create 2 separate RTCPeerConnection - one for video/audio streaming and another for screen sharing?
Any help appreciated! Thanks!
You could use RTCRtpSender.replaceTrack to splice the screen capture track. This doesn't require renegotiation, and therefore has very low latency.
let newstream = navigator.mediaDevices.getDisplayMedia({});
let newtrack = newstream.getTracks()[1];
if(newtrack.kind !== 'video')
throw new Error('Eek!?');
pc.getSenders().forEach(async s => {
if(s.track && s.track.kind === 'video')
await s.replaceTrack(newtrack);
});
The test for s.track not being null deals with the case where you previously called replaceTrack(..., null).
shareScreen = () =>{
const success = (stream) => {
window.localStream = stream
// this.localVideoref.current.srcObject = stream
// localStream.replaceStream(stream);
this.setState({
localStream: stream
})
Object.values(this.state.peerConnections).forEach(pc => {
pc.getSenders().forEach(async s => {
console.log("s.track ",s.track);
if(s.track && s.track.kind === 'video'){
stream.getTracks().forEach(track => {
// pc.addTrack(track, this.state.localStream)
s.replaceTrack(track);
});
}
});
});
}
const failure = (e) => {
console.log('getUserMedia Error: ', e)
}
navigator.mediaDevices.getDisplayMedia({ cursor: true }).then(success).catch(failure)}

three.js fontloader not work on ios

loadFont() {
return new Promise((resolve, reject) => {
this.FontLoader.load(
fontParams.url,
(response) => {
console.log('loadfont')
this.font = response
this.createTextMaterials()
this.createNumberList()
console.log('loadfont')
resolve(response)
}, (xhr) => {
console.log((xhr.loaded / xhr.total * 100) + '% loaded');
}, (err) => {
console.log(err)
})
})
}
it worked on android,but not on IOS,i test it on iphone5 and iphone6.
the debugging Always display:"request begin"
i print the err:
{"target":{"onabort":null, "onloadstart":null, "ontimeout":null,
"onloadend":null, "onreadystatechange":null, "readyState":1,
"response":null, "responseText":null, "responseType":"",
"responseXML":null, "status":0, "statusText":"", "upload":{},
"withCredentials":false}}
err mseeage

ionic 2 how to HTTP.post image send the server side in ionic 2 / 3

I'm building an app with Ionic 2. I need to take a photo from gallery or camera and upload this picture to my server. I have this code that opens the Gallery and takes a picture. without base64Image, how can upload image.
private accessGallery(): void {
let options = {
quality: 75,
// sourceType: this.camera.PictureSourceType.SAVEDPHOTOALBUM,
// destinationType: this.camera.DestinationType.DATA_URL,
destinationType: this.camera.DestinationType.FILE_URI,
sourceType: this.camera.PictureSourceType.PHOTOLIBRARY,
// encodingType: this.camera.EncodingType.JPEG,
mediaType: this.camera.MediaType.PICTURE
}
this.imagePicker.getPictures(options).then((results) => {
this.imageURI = new Array();
for (var i = 0; i < results.length; i++) {
console.log('Image URI: ' + results[i]);
this.imageURI.push(normalizeURL(results[i]));
}
console.log("Body images Name*******:-=" + this.imageURI);
this.uploadFile();
}, (err) => { });
}
uploadFile() {
let body:any = new FormData();
body = {
images: this.imageURI
}
let headers = new Headers({
'token': this.token,
'sid': this.sid,
'user': this.user,
'to': this.to,
'node': this.node,
'type': 'image'
});
let options = new RequestOptions({ headers: headers });
console.log("header ----" + JSON.stringify(headers));
console.log("images data body----" + JSON.stringify(body));
this.http.post(this.apiURL, body, options)
.map(res => res.json())
.subscribe(
data => {
console.log(data);
},
err => {
console.log("ERROR!: ", err);
}
);
}
Error :- ERROR!: Response with status: 0 for URL: null

Ionic app image upload from camera / photo library

I'm working on a ionic chat app where the user can upload a photo as part of their message. I'm looking for a way to upload the image to my webhost server so I can retrieve it later via a URL.
The problem is that I'm not able to get it to upload to my web server.
I'm using these two plugins:
org.apache.cordova.file-transfer
cordova-plugin-camera
When I run the app in xcode simulator and select a picture from the device photolibrary, the console gives me the following messages:
File Transfer Finished with response code 200
void SendDelegateMessage(NSInvocation *): delegate (webView:runJavaScriptAlertPanelWithMessage:initiatedByFrame:) failed to return after waiting 10 seconds. main run loop mode: kCFRunLoopDefaultMode>
SUCCESS: ""
This is the code I currently use:
app.controller('HomeController', function($rootScope, $scope, $cordovaCamera, $ionicActionSheet, $cordovaFileTransfer){ ...
// open PhotoLibrary
$scope.openPhotoLibrary = function() {
var options = {
quality: 100,
destinationType: Camera.DestinationType.FILE_URI,
sourceType: Camera.PictureSourceType.PHOTOLIBRARY,
allowEdit: true,
encodingType: Camera.EncodingType.JPEG,
popoverOptions: CameraPopoverOptions,
saveToPhotoAlbum: false
};
$cordovaCamera.getPicture(options).then(function(imageData) {
//console.log(imageData);
//console.log(options);
var url = "http://mydomein.com/upload.php";
//target path may be local or url
var targetPath = imageData;
var filename = targetPath.split("/").pop();
var options = {
fileKey: "file",
fileName: filename,
chunkedMode: false,
mimeType: "image/jpg"
};
$cordovaFileTransfer.upload(url, targetPath, options).then(function(result) {
console.log("SUCCESS: " + JSON.stringify(result.response));
alert("success");
alert(JSON.stringify(result.response));
}, function(err) {
console.log("ERROR: " + JSON.stringify(err));
alert(JSON.stringify(err));
}, function (progress) {
// constant progress updates
$timeout(function () {
$scope.downloadProgress = (progress.loaded / progress.total) * 100;
})
});
}, function(err) {
// error
console.log(err);
});
}
This is my upload.php file:
<?php
// move_uploaded_file($_FILES["file"]["tmp_name"], $cwd . '/files/images/');
move_uploaded_file($_FILES["file"]["tmp_name"], "/files/images");
?>
After some digging around and lot's of trying I finally got it working.
This is the code I came up with:
// open PhotoLibrary
$scope.openPhotoLibrary = function() {
var options = {
quality: 50,
destinationType: Camera.DestinationType.FILE_URI,
sourceType: Camera.PictureSourceType.PHOTOLIBRARY,
allowEdit: true,
encodingType: Camera.EncodingType.JPEG,
popoverOptions: CameraPopoverOptions,
saveToPhotoAlbum: false
};
$cordovaCamera.getPicture(options).then(function(imageData) {
//console.log(imageData);
//console.log(options);
var image = document.getElementById('tempImage');
image.src = imageData;
var server = "http://yourdomain.com/upload.php",
filePath = imageData;
var date = new Date();
var options = {
fileKey: "file",
fileName: imageData.substr(imageData.lastIndexOf('/') + 1),
chunkedMode: false,
mimeType: "image/jpg"
};
$cordovaFileTransfer.upload(server, filePath, options).then(function(result) {
console.log("SUCCESS: " + JSON.stringify(result.response));
console.log('Result_' + result.response[0] + '_ending');
alert("success");
alert(JSON.stringify(result.response));
}, function(err) {
console.log("ERROR: " + JSON.stringify(err));
//alert(JSON.stringify(err));
}, function (progress) {
// constant progress updates
});
}, function(err) {
// error
console.log(err);
});
}
And the code in upload.php on the domain server:
<?php
// if you want to find the root path of a folder use the line of code below:
//echo $_SERVER['DOCUMENT_ROOT']
if ($_FILES["file"]["error"] > 0){
echo "Error Code: " . $_FILES["file"]["error"] . "<br />";
}
else
{
echo "Uploaded file: " . $_FILES["file"]["name"] . "<br />";
echo "Type: " . $_FILES["file"]["type"] . "<br />";
echo "Size: " . ($_FILES["file"]["size"] / 1024) . " kilobytes<br />";
if (file_exists("/files/".$_FILES["file"]["name"]))
{
echo $_FILES["file"]["name"] . " already exists. No joke-- this error is almost <i><b>impossible</b></i> to get. Try again, I bet 1 million dollars it won't ever happen again.";
}
else
{
move_uploaded_file($_FILES["file"]["tmp_name"],"/var/www/vhosts/yourdomain.com/subdomains/domainname/httpdocs/foldername/images/".$_FILES["file"]["name"]);
echo "Done";
}
}
?>
the app I am building for a company had the same issue, what we did is we just posted the image to our server as a base64 string. Then you can simple pull the string from the database and display it in a div. We used the NgCordova camera and then just pass in the data from the takePhoto function.
$scope.takePhoto = function () {
$ionicScrollDelegate.scrollTop();
console.log('fired camera');
$scope.uploadList = false;
$ionicPlatform.ready(function() {
var options = {
quality: 100,
destinationType: Camera.DestinationType.DATA_URL,
sourceType: Camera.PictureSourceType.CAMERA,
allowEdit: false,
encodingType: Camera.EncodingType.PNG,
targetWidth: 800,
targetHeight: 1100,
popoverOptions: CameraPopoverOptions,
saveToPhotoAlbum: false
};
$cordovaCamera.getPicture(options).then(function (imageData) {
$ionicLoading.show({
template: 'Processing Image',
duration: 2000
});
$scope.image = "data:image/png;base64," + imageData;
if (ionic.Platform.isAndroid() === true) {
$scope.Data.Image = LZString.compressToUTF16($scope.image);
$scope.Data.isCompressed = 1;
} else {
$scope.Data.Image = $scope.image;
$scope.Data.isCompressed = 0;
}
if ($scope.tutorial) {
$scope.showAlert("Instructions: Step 3", '<div class="center">Now that you have taken a photo of the POD form, you must upload it to the server. Press the upload doc button in the bottom right of the screen.</div>');
}
$scope.on('')
}, function (err) {
console.log(err);
});
}, false);
};
$scope.UploadDoc = function () {
var req = {
method: 'POST',
url: ffService.baseUrlAuth + 'cc/upload',
headers: {
'x-access-token': ffService.token
},
data: $scope.Data
};
if ($scope.Data.Image === null || $scope.Data.Value === '') {
$scope.showAlert("Uh Oh!", '<div class="center">Please take a photo of your document before attempting an upload.</div>');
} else {
$http(req).success(function (data, status, headers, config) {
localStorage.setItem('tutorial', false);
$scope.tutorial = false;
$scope.getUploads($scope.PODOrder.OrderNo);
$scope.showAlert("Success!", '<div class="center">Your Document has been successfully uploaded!</div>');
$scope.uploadList = true;
}).error(function (data, status, headers, config) {
$rootScope.$broadcast('loading:hide');
$scope.showAlert("Something went wrong!", '<div class="center">Please make sure you have an internet connection and try again.</div>');
}).then(function(data, status, headers, config){
$scope.Data.Image = null;
});
}
};

Resources