function MediaCaptureProxy()

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);
            }
        }
    };
}