cordova-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From sgreb...@apache.org
Subject [2/2] cordova-plugin-media git commit: CB-9238 Media plugin cannot record audio on windows
Date Tue, 04 Aug 2015 14:13:58 GMT
CB-9238 Media plugin cannot record audio on windows

Reworked Windows proxy to support ms-appdata file paths (temporaryFolder and localFolder)
Fixed release method to avoid Access is denied exception
Added MEDIA_RUNNING and MEDIA_ERROR states firing to record-related methods
Reworked Windows proxy to not to create Audio object in case of record mode
Adds releaseAudio to record manual test to avoid Access denied errors

github close #57
github close #58


Project: http://git-wip-us.apache.org/repos/asf/cordova-plugin-media/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-plugin-media/commit/8cf9a96d
Tree: http://git-wip-us.apache.org/repos/asf/cordova-plugin-media/tree/8cf9a96d
Diff: http://git-wip-us.apache.org/repos/asf/cordova-plugin-media/diff/8cf9a96d

Branch: refs/heads/master
Commit: 8cf9a96d9bbceb1141b7638347a2ee6bc685a4b6
Parents: 5f8738c
Author: daserge <daserge@yandex.ru>
Authored: Mon Jul 6 18:52:08 2015 +0300
Committer: sgrebnov <v-segreb@microsoft.com>
Committed: Tue Aug 4 17:13:48 2015 +0300

----------------------------------------------------------------------
 src/windows/MediaProxy.js | 202 ++++++++++++++++++++++++++++++++++-------
 tests/tests.js            |   5 +-
 2 files changed, 173 insertions(+), 34 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-plugin-media/blob/8cf9a96d/src/windows/MediaProxy.js
----------------------------------------------------------------------
diff --git a/src/windows/MediaProxy.js b/src/windows/MediaProxy.js
index 3768ebd..6806dd6 100644
--- a/src/windows/MediaProxy.js
+++ b/src/windows/MediaProxy.js
@@ -25,6 +25,13 @@ var Media = require('cordova-plugin-media.Media');
 var MediaError = require('cordova-plugin-media.MediaError');
 
 var recordedFile;
+var tempFolderAppDataBasePath = 'ms-appdata:///temp/',
+    localFolderAppDataBasePath = 'ms-appdata:///local/',
+    tempFolderFullPath = Windows.Storage.ApplicationData.current.temporaryFolder.path,
+    localFolderFullPath = Windows.Storage.ApplicationData.current.localFolder.path;
+
+var PARAMETER_IS_INCORRECT = -2147024809;
+var SUPPORTED_EXTENSIONS = ['.mp3', '.wma', '.wav', '.cda', '.adx', '.wm', '.m3u', '.wmx',
'.m4a'];
 
 module.exports = {
     mediaCaptureMrg:null,
@@ -32,17 +39,24 @@ module.exports = {
     // Initiates the audio file
     create:function(win, lose, args) {
         var id = args[0];
-        var src = args[1];
+
+        var srcUri = processUri(args[1]);
+
+        var createAudioNode = !!args[2];
         var thisM = Media.get(id);
 
         Media.prototype.node = null;
 
-        var fn = src.split('.').pop(); // gets the file extension
+        var extension = srcUri.extension;
         if (thisM.node === null) {
-            if (fn === 'mp3' || fn === 'wma' || fn === 'wav' ||
-                fn === 'cda' || fn === 'adx' || fn === 'wm' ||
-                fn === 'm3u' || fn === 'wmx' || fn === 'm4a') {
-                thisM.node = new Audio(src);
+            if (SUPPORTED_EXTENSIONS.indexOf(extension) === -1) {
+                lose && lose({ code: MediaError.MEDIA_ERR_ABORTED });
+                return false; // unable to create
+            }
+
+            // Don't create Audio object in case of record mode
+            if (createAudioNode === true) {
+                thisM.node = new Audio(srcUri.absoluteCanonicalUri);
 
                 thisM.node.onloadstart = function () {
                     Media.onStatus(id, Media.MEDIA_STATE, Media.MEDIA_STARTING);
@@ -60,7 +74,7 @@ module.exports = {
                     Media.onStatus(id, Media.MEDIA_DURATION, e.target.duration || -1);
                 };
 
-                thisM.node.onerror = function(e) {
+                thisM.node.onerror = function (e) {
                     // Due to media.spec.15 It should return MediaError for bad filename
                     var err = e.target.error.code === MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED
?
                         { code: MediaError.MEDIA_ERR_ABORTED } :
@@ -72,10 +86,6 @@ module.exports = {
                 thisM.node.onended = function () {
                     Media.onStatus(id, Media.MEDIA_STATE, Media.MEDIA_STOPPED);
                 };
-
-            } else {
-                lose && lose({ code: MediaError.MEDIA_ERR_ABORTED });
-                return false; // unable to create
             }
         }
 
@@ -91,6 +101,7 @@ module.exports = {
         var thisM = Media.get(id);
         // if Media was released, then node will be null and we need to create it again
         if (!thisM.node) {
+            args[2] = true; // Setting createAudioNode to true
             if (!module.exports.create(win, lose, args)) {
                 // there is no reason to continue if we can't create media
                 // corresponding callback has been invoked in create so we don't need to
call it here
@@ -157,11 +168,18 @@ module.exports = {
     // Start recording audio
     startRecordingAudio:function(win, lose, args) {
         var id = args[0];
-        var src = args[1];
+        var srcUri = processUri(args[1]);
+
+        var dest = parseUriToPathAndFilename(srcUri);
+        var destFileName = dest.fileName;
+
+        var success = function () {
+            Media.onStatus(id, Media.MEDIA_STATE, Media.MEDIA_RUNNING);
+        };
 
-        var normalizedSrc = src.replace(/\//g, '\\');
-        var destPath = normalizedSrc.substr(0, normalizedSrc.lastIndexOf('\\'));
-        var destFileName = normalizedSrc.replace(destPath + '\\', '');
+        var error = function (reason) {
+            Media.onStatus(id, Media.MEDIA_ERROR, reason);
+        };
 
         // Initialize device
         Media.prototype.mediaCaptureMgr = null;
@@ -169,12 +187,12 @@ module.exports = {
         var captureInitSettings = new Windows.Media.Capture.MediaCaptureInitializationSettings();
         captureInitSettings.streamingCaptureMode = Windows.Media.Capture.StreamingCaptureMode.audio;
         thisM.mediaCaptureMgr = new Windows.Media.Capture.MediaCapture();
-        thisM.mediaCaptureMgr.addEventListener("failed", lose);
+        thisM.mediaCaptureMgr.addEventListener("failed", error);
 
         thisM.mediaCaptureMgr.initializeAsync(captureInitSettings).done(function (result)
{
-            thisM.mediaCaptureMgr.addEventListener("recordlimitationexceeded", lose);
-            thisM.mediaCaptureMgr.addEventListener("failed", lose);
-            
+            thisM.mediaCaptureMgr.addEventListener("recordlimitationexceeded", error);
+            thisM.mediaCaptureMgr.addEventListener("failed", error);
+
             // Start recording
             Windows.Storage.ApplicationData.current.temporaryFolder.createFileAsync(destFileName,
Windows.Storage.CreationCollisionOption.replaceExisting).done(function (newFile) {
                 recordedFile = newFile;
@@ -190,33 +208,54 @@ module.exports = {
                         encodingProfile = Windows.Media.MediaProperties.MediaEncodingProfile.createWma(Windows.Media.MediaProperties.AudioEncodingQuality.auto);
                         break;
                     default:
-                        lose("Invalid file type for record");
+                        error("Invalid file type for record");
                         break;
                 }
-                thisM.mediaCaptureMgr.startRecordToStorageFileAsync(encodingProfile, newFile).done(win,
lose);
-            }, lose);
-        }, lose);
+                thisM.mediaCaptureMgr.startRecordToStorageFileAsync(encodingProfile, newFile).done(success,
error);
+            }, error);
+        }, error);
     },
 
     // Stop recording audio
     stopRecordingAudio:function(win, lose, args) {
         var id = args[0];
         var thisM = Media.get(id);
+        var srcUri = processUri(thisM.src);
 
-        var normalizedSrc = thisM.src.replace(/\//g, '\\');
-        var destPath = normalizedSrc.substr(0, normalizedSrc.lastIndexOf('\\'));
-        var destFileName = normalizedSrc.replace(destPath + '\\', '');
+        var dest = parseUriToPathAndFilename(srcUri);
+        var destPath = dest.path;
+        var destFileName = dest.fileName;
+        var fsType = dest.fsType;
+
+        var success = function () {
+            Media.onStatus(id, Media.MEDIA_STATE, Media.MEDIA_STOPPED);
+        };
+
+        var error = function (reason) {
+            Media.onStatus(id, Media.MEDIA_ERROR, reason);
+        };
 
         thisM.mediaCaptureMgr.stopRecordAsync().done(function () {
-            if (destPath) {
-                Windows.Storage.StorageFolder.getFolderFromPathAsync(destPath).done(function(destFolder)
{
-                    recordedFile.copyAsync(destFolder, destFileName, Windows.Storage.CreationCollisionOption.replaceExisting).done(win,
lose);
-                }, lose);
+            if (fsType === fsTypes.TEMPORARY) {
+                if (!destPath) {
+                    // if path is not defined, we leave recorded file in temporary folder
(similar to iOS)
+                    success();
+                } else {
+                    Windows.Storage.ApplicationData.current.temporaryFolder.getFolderAsync(destPath).done(function
(destFolder) {
+                        recordedFile.copyAsync(destFolder, destFileName, Windows.Storage.CreationCollisionOption.replaceExisting).done(success,
error);
+                    }, error);
+                }
             } else {
-                // if path is not defined, we leave recorded file in temporary folder (similar
to iOS)
-                win();
+                // Copying file to persistent storage
+                if (!destPath) {
+                    recordedFile.copyAsync(Windows.Storage.ApplicationData.current.localFolder,
destFileName, Windows.Storage.CreationCollisionOption.replaceExisting).done(success, error);
+                } else {
+                    Windows.Storage.ApplicationData.current.localFolder.getFolderAsync(destPath).done(function
(destFolder) {
+                        recordedFile.copyAsync(destFolder, destFileName, Windows.Storage.CreationCollisionOption.replaceExisting).done(success,
error);
+                    }, error);
+                }
             }
-        }, lose);
+        }, error);
     },
 
     // Release the media object
@@ -226,6 +265,10 @@ module.exports = {
         try {
             if (thisM.node) {
                 thisM.node.onloadedmetadata = null;
+                // Unsubscribing as the media object is being released
+                thisM.node.onerror = null;
+                // Needed to avoid "0x80070005 - JavaScript runtime error: Access is denied."
on copyAsync
+                thisM.node.src = null;
                 delete thisM.node;
             }
         } catch (err) {
@@ -240,4 +283,97 @@ module.exports = {
     }
 };
 
+/**
+ * Converts a path to Windows.Foundation.Uri basing on App data temporary folder 
+ * if scheme is not defined, e.g.: path/to/file.m4a -> ms-appdata:///temp/path/to/file.m4a
+ * @param  {String} src Input path
+ * @return {Object}     Windows.Foundation.Uri
+ */
+function setTemporaryFsByDefault(src) {
+    var uri;
+    try {
+        uri = new Windows.Foundation.Uri(src);
+    } catch (e) {
+        if (e.number === PARAMETER_IS_INCORRECT) {
+            // Use TEMPORARY fs there is no 'scheme:'
+            uri = new Windows.Foundation.Uri(tempFolderAppDataBasePath, src);
+        } else {
+            throw e;
+        }
+    } finally {
+        return uri;
+    }
+}
+
+/**
+ * Convert native full path to ms-appdata path
+ * @param  {Object} uri Windows.Foundation.Uri
+ * @return {Object}     ms-appdata Windows.Foundation.Uri
+ */
+function fullPathToAppData(uri) {
+    if (uri.schemeName === 'file') {
+        if (uri.rawUri.indexOf(Windows.Storage.ApplicationData.current.localFolder.path)
!== -1) {
+            // Also remove path' beginning slash to avoid losing folder name part
+            uri = new Windows.Foundation.Uri(localFolderAppDataBasePath, uri.rawUri.replace(localFolderFullPath,
'').replace(/^[\\\/]{1,2}/, ''));
+        } else if (uri.rawUri.indexOf(Windows.Storage.ApplicationData.current.temporaryFolder.path)
!== -1) {
+            uri = new Windows.Foundation.Uri(tempFolderAppDataBasePath, uri.rawUri.replace(tempFolderFullPath,
'').replace(/^[\\\/]{1,2}/, ''));
+        } else {
+            throw new Error('Not supported file uri: ' + uri.rawUri);
+        }
+    }
+
+    return uri;
+}
+
+/**
+ * Prepares media src for internal usage
+ * @param  {String} src Input media path
+ * @return {Object}     Windows.Foundation.Uri
+ */
+function processUri(src) {
+    // Collapse double slashes (File plugin issue): ms-appdata:///temp//recs/memos/media.m4a
=> ms-appdata:///temp/recs/memos/media.m4a
+    src = src.replace(/([^\/:])(\/\/)([^\/])/g, '$1/$3');
+
+    // Remove beginning slashes
+    src = src.replace(/^[\\\/]{1,2}/, '');
+
+    var uri = setTemporaryFsByDefault(src);
+
+    uri = fullPathToAppData(uri);
+
+    return uri;
+}
+
+var fsTypes = {
+    PERSISTENT: 'PERSISTENT',
+    TEMPORARY: 'TEMPORARY'
+};
+
+/**
+ * Extracts path, filename and filesystem type from Uri
+ * @param  {Object} uri Windows.Foundation.Uri
+ * @return {Object}     Object containing path, filename and filesystem type
+ */
+function parseUriToPathAndFilename(uri) {
+    // Removing scheme and location, using backslashes: ms-appdata:///local/path/to/file.m4a
-> path\\to\\file.m4a
+    var normalizedSrc = uri.path.split('/').slice(2).join('\\');
+
+    var path = normalizedSrc.substr(0, normalizedSrc.lastIndexOf('\\'));
+    var fileName = normalizedSrc.replace(path + '\\', '');
+
+    var fsType;
+
+    if (uri.path.split('/')[1] === 'local') {
+        fsType = fsTypes.PERSISTENT;
+    } else if (uri.path.split('/')[1] === 'temp') {
+        fsType = fsTypes.TEMPORARY;
+    }
+
+    return {
+        path: path,
+        fileName: fileName,
+        fsType: fsType
+    };
+}
+
 require("cordova/exec/proxy").add("Media",module.exports);

http://git-wip-us.apache.org/repos/asf/cordova-plugin-media/blob/8cf9a96d/tests/tests.js
----------------------------------------------------------------------
diff --git a/tests/tests.js b/tests/tests.js
index fc6d4a9..8f54a9e 100644
--- a/tests/tests.js
+++ b/tests/tests.js
@@ -520,6 +520,9 @@ exports.defineManualTests = function (contentEl, createActionButton) {
     function recordAudio() {
         console.log("recordAudio()");
         console.log(" -- media=" + mediaRec);
+
+        releaseAudio();
+
         if (!mediaRec) {
             var src = recordSrc;
             mediaRec = new Media(src,
@@ -601,7 +604,7 @@ exports.defineManualTests = function (contentEl, createActionButton) {
             console.log("error creating file for Win recording");
         };
         var gotFile = function (file) {
-            recordSrc = file.fullPath.replace(/\//g, '\\');
+            recordSrc = file.name;
         };
         var gotFS = function (fileSystem) {
             fileSystem.root.getFile("WinRecording.m4a", {


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cordova.apache.org
For additional commands, e-mail: commits-help@cordova.apache.org


Mime
View raw message