in src/windows/CaptureProxy.js [33:296]
function MediaCaptureProxy () {
let previewContainer;
let capturePreview = null;
let captureCancelButton = null; // eslint-disable-line no-unused-vars
let captureSettings = null;
let captureStarted = false;
let capturedPictureFile;
let capturedVideoFile;
let capture = null;
const CaptureNS = Windows.Media.Capture;
/**
* Helper function that toggles visibility of DOM elements with provided ids
* @param {String} variable number of elements' ids which visibility needs to be toggled
*/
function toggleElements () {
// convert arguments to array
const args = Array.prototype.slice.call(arguments);
args.forEach(function (buttonId) {
const buttonEl = document.getElementById(buttonId);
if (buttonEl) {
const curDisplayStyle = buttonEl.style.display;
buttonEl.style.display = curDisplayStyle === 'none' ? 'block' : 'none';
}
});
}
/**
* Creates basic camera UI with preview 'video' element and 'Cancel' button
* Capture starts, when you clicking on preview.
*/
function createCameraUI () {
const buttonStyle = 'margin: 7px; border: 2.5px solid white; width: 45%; height: 35px; color: white; background-color: black;';
previewContainer = document.createElement('div');
previewContainer.style.cssText =
'background-position: 50% 50%; background-repeat: no-repeat; background-size: contain; background-color: black; left: 0px; top: 0px; width: 100%; height: 100%; position: fixed; z-index: 9999';
previewContainer.innerHTML =
'<video id="capturePreview" style="width: 100%; height: 100%"></video>' +
'<div id="previewButtons" style="width: 100%; bottom: 0px; display: flex; position: absolute; justify-content: space-around; background-color: black;">' +
'<button id="takePicture" style="' +
buttonStyle +
'">Capture</button>' +
'<button id="cancelCapture" style="' +
buttonStyle +
'">Cancel</button>' +
'<button id="selectPicture" style="display: none; ' +
buttonStyle +
'">Accept</button>' +
'<button id="retakePicture" style="display: none; ' +
buttonStyle +
'">Retake</button>' +
'</div>';
document.body.appendChild(previewContainer);
// Create fullscreen preview
capturePreview = document.getElementById('capturePreview');
// Create cancel button
captureCancelButton = document.getElementById('cancelCapture');
capture = new CaptureNS.MediaCapture();
captureSettings = new CaptureNS.MediaCaptureInitializationSettings();
captureSettings.streamingCaptureMode = CaptureNS.StreamingCaptureMode.audioAndVideo;
}
/**
* Starts camera preview and binds provided callbacks to controls
* @param {function} takeCallback Callback for Take button
* @param {function} errorCallback Callback for Cancel button + default error callback
* @param {function} selectCallback Callback for Select button
* @param {function} retakeCallback Callback for Retake button
*/
function startCameraPreview (takeCallback, errorCallback, selectCallback, retakeCallback) {
// try to select appropriate device for capture
// rear camera is preferred option
const expectedPanel = Windows.Devices.Enumeration.Panel.back;
Windows.Devices.Enumeration.DeviceInformation.findAllAsync(Windows.Devices.Enumeration.DeviceClass.videoCapture).done(function (
devices
) {
if (devices.length > 0) {
devices.forEach(function (currDev) {
if (currDev.enclosureLocation && currDev.enclosureLocation.panel && currDev.enclosureLocation.panel === expectedPanel) {
captureSettings.videoDeviceId = currDev.id;
}
});
capture.initializeAsync(captureSettings).done(
function () {
// This is necessary since WP8.1 MediaCapture outputs video stream rotated 90 degrees CCW
// TODO: This can be not consistent across devices, need additional testing on various devices
// msdn.microsoft.com/en-us/library/windows/apps/hh452807.aspx
capture.setPreviewRotation(Windows.Media.Capture.VideoRotation.clockwise90Degrees);
capturePreview.msZoom = true;
capturePreview.src = URL.createObjectURL(capture); // eslint-disable-line no-undef
capturePreview.play();
previewContainer.style.display = 'block';
// Bind events to controls
capturePreview.onclick = takeCallback;
document.getElementById('takePicture').onclick = takeCallback;
document.getElementById('cancelCapture').onclick = function () {
errorCallback(CaptureError.CAPTURE_NO_MEDIA_FILES);
};
document.getElementById('selectPicture').onclick = selectCallback;
document.getElementById('retakePicture').onclick = retakeCallback;
},
function (err) {
destroyCameraPreview();
errorCallback(CaptureError.CAPTURE_INTERNAL_ERR, err);
}
);
} else {
// no appropriate devices found
destroyCameraPreview();
errorCallback(CaptureError.CAPTURE_INTERNAL_ERR);
}
});
}
/**
* Destroys camera preview, removes all elements created
*/
function destroyCameraPreview () {
capturePreview.pause();
capturePreview.src = null;
if (previewContainer) {
document.body.removeChild(previewContainer);
}
if (capture) {
capture.stopRecordAsync();
capture = null;
}
}
return {
/**
* Initiate video capture using MediaCapture class
* @param {function} successCallback Called, when user clicked on preview, with captured file object
* @param {function} errorCallback Called on any error
*/
captureVideo: function (successCallback, errorCallback) {
try {
createCameraUI();
startCameraPreview(function () {
// This callback called twice: whem video capture started and when it ended
// so we need to check capture status
if (!captureStarted) {
// remove cancel button and rename 'Take' button to 'Stop'
toggleElements('cancelCapture');
document.getElementById('takePicture').text = 'Stop';
const encodingProperties = Windows.Media.MediaProperties.MediaEncodingProfile.createMp4(
Windows.Media.MediaProperties.VideoEncodingQuality.auto
);
const generateUniqueCollisionOption = Windows.Storage.CreationCollisionOption.generateUniqueName;
const localFolder = Windows.Storage.ApplicationData.current.localFolder;
localFolder.createFileAsync('cameraCaptureVideo.mp4', generateUniqueCollisionOption).done(
function (capturedFile) {
capture.startRecordToStorageFileAsync(encodingProperties, capturedFile).done(
function () {
capturedVideoFile = capturedFile;
captureStarted = true;
},
function (err) {
destroyCameraPreview();
errorCallback(CaptureError.CAPTURE_INTERNAL_ERR, err);
}
);
},
function (err) {
destroyCameraPreview();
errorCallback(CaptureError.CAPTURE_INTERNAL_ERR, err);
}
);
} else {
capture.stopRecordAsync().done(function () {
destroyCameraPreview();
successCallback(capturedVideoFile);
});
}
}, errorCallback);
} catch (ex) {
destroyCameraPreview();
errorCallback(CaptureError.CAPTURE_INTERNAL_ERR, ex);
}
},
/**
* Initiate image capture using MediaCapture class
* @param {function} successCallback Called, when user clicked on preview, with captured file object
* @param {function} errorCallback Called on any error
*/
capturePhoto: function (successCallback, errorCallback) {
try {
createCameraUI();
startCameraPreview(
// Callback for Take button - captures intermediate image file.
function () {
const encodingProperties = Windows.Media.MediaProperties.ImageEncodingProperties.createJpeg();
const overwriteCollisionOption = Windows.Storage.CreationCollisionOption.replaceExisting;
const tempFolder = Windows.Storage.ApplicationData.current.temporaryFolder;
tempFolder.createFileAsync('cameraCaptureImage.jpg', overwriteCollisionOption).done(
function (capturedFile) {
capture.capturePhotoToStorageFileAsync(encodingProperties, capturedFile).done(
function () {
// store intermediate result in object's global variable
capturedPictureFile = capturedFile;
// show pre-captured image and toggle visibility of all buttons
previewContainer.style.backgroundImage = 'url("' + 'ms-appdata:///temp/' + capturedFile.name + '")';
toggleElements('capturePreview', 'takePicture', 'cancelCapture', 'selectPicture', 'retakePicture');
},
function (err) {
destroyCameraPreview();
errorCallback(CaptureError.CAPTURE_INTERNAL_ERR, err);
}
);
},
function (err) {
destroyCameraPreview();
errorCallback(CaptureError.CAPTURE_INTERNAL_ERR, err);
}
);
},
// error + cancel callback
function (err) {
destroyCameraPreview();
errorCallback(err);
},
// Callback for Select button - copies intermediate file into persistent application's storage
function () {
const generateUniqueCollisionOption = Windows.Storage.CreationCollisionOption.generateUniqueName;
const localFolder = Windows.Storage.ApplicationData.current.localFolder;
capturedPictureFile.copyAsync(localFolder, capturedPictureFile.name, generateUniqueCollisionOption).done(
function (copiedFile) {
destroyCameraPreview();
successCallback(copiedFile);
},
function (err) {
destroyCameraPreview();
errorCallback(err);
}
);
},
// Callback for retake button - just toggles visibility of necessary elements
function () {
toggleElements('capturePreview', 'takePicture', 'cancelCapture', 'selectPicture', 'retakePicture');
}
);
} catch (ex) {
destroyCameraPreview();
errorCallback(CaptureError.CAPTURE_INTERNAL_ERR, ex);
}
}
};
}