Cordova iOS Video tag Local File Source - ios

I have problem playing local video on iOS on my Cordova based app. At the beginning I want to stress out that this problem is happening only when I'm using WKWebView, and if UiWebView is used, video plays fine. This is scenario I have:
-User comes to screen to which video url is passed
-Via FileTransfer I download it to phone and store it at desired location
-Using JS video is loaded to <video> tag and played.
Basically I'm doing everything as described in answer to this SO question.
The problem with UiWebView was that if relative path was set to src, video for some reason couldn't be loaded (no matter which combination I used), so this solution worked great for me, because it is based on this line of code:
entry.toURL()
This returns full path of the downloaded video which is great, at least for the UiWebView.
The problem for WkWebView is that entry.toURL() returns smth. like this:
file:///var/mobile/Containers/Data/Application/3A43AFB5-BEF6-4A0C-BBDB-FC7D2D98BEE9/Documents/videos/Dips.mp4
And WKWebView doesn't work with file:// protocol. Also, neither WKWebView works wit relative paths :(
Can anyone help me to fix this ?

I got this working today with the following but only when deployed to my device in Release mode. When deploying the app in Debug mode to my device it would not work.
iOS 9.3.2
Cordova 4.0.0 (iOS 3.8.0)
Telerik WKWebView Polyfill 0.6.9
Video list load method:
var path = window.cordova.file.documentsDirectory, //iTunes File Sharing directory
href = 'http://localhost:12344/Documents', //WKWebView default server url to documents
list = [];
function fsSuccess(dir) {
var reader = dir.createReader();
reader.readEntries(function (entries) {
for (var i = 0; i < entries.length; i++) {
list.push({ name: entries[i].name, path: href + entries[i].fullPath });
}
});
}
function fsError(error) {
console.log('error', error)
}
window.resolveLocalFileSystemURL(path, fsSuccess, fsError);
Video list click handler:
var video = $('#video')[0],
source = $('#source');
function play(index) {
source.attr('src', list[index].path);
video.load();
video.play();
}
Video player markup:
<video id="video" autoplay controls loop webkit-playsinline>
<source id="source" type="video/mp4" />
</video>
I was banging my head on my desk a la Ren Hoek while debugging until I attempted a release buid and it worked.

Sample snippet that uses cordova file opener plugin to open the download file from device.(Not tested in WKWebView though)
var fileTransfer = new FileTransfer();
var cdr;
if (sessionStorage.platform.toLowerCase() == "android") {
window.resolveLocalFileSystemURL(cordova.file.externalRootDirectory, onFileSystemSuccess, onError);
} else {
// for iOS
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, onFileSystemSuccess, onError);
}
function onError(e) {
navigator.notification.alert("Error : Downloading Failed");
};
function onFileSystemSuccess(fileSystem) {
var entry = "";
if (sessionStorage.platform.toLowerCase() == "android") {
entry = fileSystem;
} else {
entry = fileSystem.root;
}
entry.getDirectory("Cordova", {
create: true,
exclusive: false
}, onGetDirectorySuccess, onGetDirectoryFail);
};
function onGetDirectorySuccess(dir) {
cdr = dir;
dir.getFile(filename, {
create: true,
exclusive: false
}, gotFileEntry, errorHandler);
};
function gotFileEntry(fileEntry) {
// URL in which the pdf is available
var documentUrl = "http://localhost:8080/testapp/test.pdf";
var uri = encodeURI(documentUrl);
fileTransfer.download(uri, cdr.nativeURL + "test.pdf",
function(entry) {
// Logic to open file using file opener plugin
openFile();
},
function(error) {
navigator.notification.alert(ajaxErrorMsg);
},
false
);
};
function openFile() {
cordova.plugins.fileOpener2.open(
cdr.nativeURL + "test.pdf",
"application/pdf", //mimetype
{
error: function(e) {
navigator.notification.alert("Error Opening the File.Unsupported document format.");
},
success: function() {
// success callback handler
}
}
);
};

Related

How to play an audio file through Ionic on iOS?

I am using cordovo media plugin to play an audio file on button click. It is working fine on Android but I don't understand the problem with iOS. Here is the code inside a controller:
var url = '';
if(ionic.Platform.isAndroid() == true){
url = "/android_asset/www/sound.wav";
}else{
url = "sound.wav";
}
$scope.playAudio = function() {
// Play the audio file at url
var my_media = new Media(url,
// success callback
function () {
console.log("playAudio():Audio Success");
},
// error callback
function (err) {
console.log("playAudio():Audio Error: " + JSON.stringify(err));
}
);
console.log("Play");
// Play audio
my_media.play();
}
I have spent many of my hours to figure out the problem, but I have no idea what has gone wrong. I will be genuinely thankful to you if you can help me out.
Thanks in advance.

Cordova: What is the right location to store recorded Audio files in iOS?

I am using media plugin to record sounds.
var my_media = new Media('test.wav');
my_media.startRecord();
my_media.stopRecord() // after few seconds
my_media.release();
my_media.play();//works fine
// But when I try to Check if test.wav file exist I always get false as status.
//File name of our important data file we didn't ship with the app
var fileName = "test.wav";
function init() {
console.log("Checking for data file.");
//Check for the file.
window.resolveLocalFileSystemURL(fileName, onSuccess, onFail);
}
function onSuccess() {
console.log("Great! This file exists");
}
function onFail() {
console.log('Sorry! File not Found');
}
// I always get this message: Sorry! File not Found
What is right way to do this? After recording I want user to be able to see list of available recorded sounds and user should be able to play it. For that I need to check if file exists or not.
So how should we implement that?
You can display list of recorded files as:
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function(fileSystem) {
fileSystem.root.getDirectory(YOUR_PATH, {
create: true
}, function(directory) {
root_path = fileSystem.root.toURL();
var directoryReader = directory.createReader();
directoryReader.readEntries(function(entries) {
var i;
for (i = 0; i < entries.length; i++) {
//Get file URL as: entries[i].toURL()); & display using creating dynamic tag.
}
}, function(error) {
alert(error.code);
});
});
}, function(error) {
alert("can't even get the file system: " + error.code);
});

Cordova / Ionic - Download file from InAppBrowser

The scenario goes like this: I open a website in InAppBrowser, after the user ends with the work over there, the site generates a .pdf for the user to download, the problem is that the pdf does not download, it opens it in the browser.
Is there a way to make it download from the InAppBrowser? I'm currently working on an iOS app, so the solution would be better for iOS.
Thanks in advance.
Following #jcesarmobile advices this is what I came up with:
First I had to install the cordova-plugin-file-transfer
Open URL
var url = "http://mi-fancy-url.com";
var windowref = window.open(url, '_blank', 'location=no,closebuttoncaption=Cerrar,toolbar=yes,enableViewportScale=yes');
Create a listener on that windowref for a loadstart event and check if what's being loaded is a pdf (that's my case).
windowref.addEventListener('loadstart', function(e) {
var url = e.url;
var extension = url.substr(url.length - 4);
if (extension == '.pdf') {
var targetPath = cordova.file.documentsDirectory + "receipt.pdf";
var options = {};
var args = {
url: url,
targetPath: targetPath,
options: options
};
windowref.close(); // close window or you get exception
document.addEventListener('deviceready', function () {
setTimeout(function() {
downloadReceipt(args); // call the function which will download the file 1s after the window is closed, just in case..
}, 1000);
});
}
});
Create the function that will handle the file download and then open it:
function downloadReceipt(args) {
var fileTransfer = new FileTransfer();
var uri = encodeURI(args.url);
fileTransfer.download(
uri, // file's uri
args.targetPath, // where will be saved
function(entry) {
console.log("download complete: " + entry.toURL());
window.open(entry.toURL(), '_blank', 'location=no,closebuttoncaption=Cerrar,toolbar=yes,enableViewportScale=yes');
},
function(error) {
console.log("download error source " + error.source);
console.log("download error target " + error.target);
console.log("upload error code" + error.code);
},
true,
args.options
);
}
The problem i'm facing now is the path where it downloads, I just can't open it. But well, at least file is now downloaded. I will have to create a localStorage item to save the paths for different files.
Many validations are missing in this steps, this was just an example I made quickly to check if it works. Further validations are needed.
Open you window using IAB plugin and add an event listener
ref = window.open(url, "_blank");
ref.addEventListener('loadstop', loadStopCallBack);
In the InAppBrowser window call the action using https://xxx.pdf">documentName
Implement the loadStopCallBack function
function loadStopCallBack(refTemp) {
if(refTemp.url.includes('downloadDoc')) {
rtaParam = getURLParams('downloadDoc', refTemp.url);
if(rtaParam != null)
downloadFileFromServer(rtaParam);
return;
}
}
function getURLParams( name, url ) {
try {
if (!url)
url = location.href;
name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
var regexS = "[\\?&]" + name + "=([^&#]*)";
var regex = new RegExp(regexS);
var results = regex.exec(url);
return results == null ? null : results[1];
} catch (e) {
showSMS(e);
return null;
}
}
After create a download method
function downloadFileFromServer(fileServerURL){
try {
var Downloader = window.plugins.Downloader;
var fileName = fileServerURL.substring(fileServerURL.lastIndexOf("/") + 1);
var downloadSuccessCallback = function(result) {
console.log(result.path);
};
var downloadErrorCallback = function(error) {
// error: string
console.log(error);
};
//TODO cordova.file.documentsDirectory for iOS
var options = {
title: 'Descarga de '+ fileName, // Download Notification Title
url: fileServerURL, // File Url
path: fileName, // The File Name with extension
description: 'La descarga del archivo esta lista', // Download description Notification String
visible: true, // This download is visible and shows in the notifications while in progress and after completion.
folder: "Download" // Folder to save the downloaded file, if not exist it will be created
};
Downloader.download(options, downloadSuccessCallback, downloadErrorCallback);
} catch (e) {
console.log(e);
}
}
you can get the plugin here https://github.com/ogarzonm85/cordova-plugin-downloader
it Works and was too easy

Distorted audio in iOS 7.1 with WebAudio API

On iOS 7.1, I keep getting a buzzing / noisy / distorted sound when playing back audio using the Web Audio API. It sounds distorted like this, in place of normal like this.
The same files are fine when using HTML5 audio. It all works fine on desktop (Firefox, Chrome, Safari.)
EDIT:
The audio is distorted in the iOS Simulator versions iOS 7.1, 8.1, 8.2. The buzzing sound often starts before I even playback anything.
The audio is distorted on a physical iPhone running iOS 7.1, in both Chrome and Safari.
The audio is fine on a physical iPhone running iOS 8.1, in both Chrome and Safari.
i.e.: the buzzing audio is on iOS 7.1. only.
Howler.js is not the issue. The problem is still there using pure JS like so:
var context;
var sound;
var extension = '.' + ( new Audio().canPlayType( 'audio/ogg' ) !== '' ? 'ogg' : 'mp3');
/** Test for WebAudio API support **/
try {
// still needed for Safari
window.AudioContext = window.AudioContext || window.webkitAudioContext;
// create an AudioContext
context = new AudioContext();
} catch(e) {
// API not supported
throw new Error( 'Web Audio API not supported.' );
}
function loadSound( url ) {
var request = new XMLHttpRequest();
request.open( 'GET', url, true );
request.responseType = 'arraybuffer';
request.onload = function() {
// request.response is encoded... so decode it now
context.decodeAudioData( request.response, function( buffer ) {
sound = buffer;
}, function( err ) {
throw new Error( err );
});
}
request.send();
}
function playSound(buffer) {
var source = context.createBufferSource();
source.buffer = buffer;
source.connect(context.destination);
source.start(0);
}
loadSound( '/tests/Assets/Audio/En-us-hello' + extension );
$(document).ready(function(){
$( '#clickme' ).click( function( event ) {
playSound(sound);
});
}); /* END .ready() */
A live version of this code is available here: Web Audio API - Hello world
Google did not bring up any result about such a distorted sound issue on iOS 7.1.
Has anyone else run into it? Should I file a bug report to Apple?
I believe the issue is caused due to resetting the audioContext.sampleRate prop, which seem to happen after the browser/OS plays something recorded in a different sampling rate.
I've devised the following workaround, which basically silently plays a short wav file recorded in the sampling rate that the device currently does playback on:
"use strict";
var getData = function( context, filePath, callback ) {
var source = context.createBufferSource(),
request = new XMLHttpRequest();
request.open( "GET", filePath, true );
request.responseType = "arraybuffer";
request.onload = function() {
var audioData = request.response;
context.decodeAudioData(
audioData,
function( buffer ) {
source.buffer = buffer;
callback( source );
},
function( e ) {
console.log( "Error with decoding audio data" + e.err );
}
);
};
request.send();
};
module.exports = function() {
var AudioContext = window.AudioContext || window.webkitAudioContext,
context = new AudioContext();
getData(
context,
"path/to/short/file.wav",
function( bufferSource ) {
var gain = context.createGain();
gain.gain.value = 0;
bufferSource.connect( gain );
gain.connect( context.destination );
bufferSource.start( 0 );
}
);
};
Obviously, if some of the devices have different sampling rates, you would need to detect and use a specific file for every rate.
it looks like iOS6+ Safari defaults to a sample rate of 48000. If you type this into the developer console when you first open mobile safari, you'll get 48000:
var ctx = new window.webkitAudioContext();
console.log(ctx.sampleRate);
Further Reference: https://forums.developer.apple.com/thread/20677
Then if you close the initial context on load: ctx.close(), the next created context will use the sample rate most other browsers use (44100) and sound will play without distortion.
Credit to this for pointing me in the right direction (and in case the above no longer works in the future): https://github.com/Jam3/ios-safe-audio-context/blob/master/index.js
function as of post date:
function createAudioContext (desiredSampleRate) {
var AudioCtor = window.AudioContext || window.webkitAudioContext
desiredSampleRate = typeof desiredSampleRate === 'number'
? desiredSampleRate
: 44100
var context = new AudioCtor()
// Check if hack is necessary. Only occurs in iOS6+ devices
// and only when you first boot the iPhone, or play a audio/video
// with a different sample rate
if (/(iPhone|iPad)/i.test(navigator.userAgent) &&
context.sampleRate !== desiredSampleRate) {
var buffer = context.createBuffer(1, 1, desiredSampleRate)
var dummy = context.createBufferSource()
dummy.buffer = buffer
dummy.connect(context.destination)
dummy.start(0)
dummy.disconnect()
context.close() // dispose old context
context = new AudioCtor()
}
return context
}

Media is not playing in iOS phonegap 1.5 app

I am developing iOS app using phonegap 1.5. In this app I am using phonegap's Media API to play mp3 files. But it's not working on iOS. Same codebase works on Android without any issue.
Code:
Audio.js
//
var my_media = null;
var mediaTimer = null;
function playAudio(src) {
//alert("inside");
//if (my_media == null) {
// Create Media object from src
my_media = new Media(src, onSuccess, onError);
//alert(my_media);
//} // else play current audio
// Play audio
my_media.play();
//alert("Played");
}
function onSuccess() {
console.log("playAudio():Audio Success");
}
// onError Callback
//
function onError(error) {
alert('code: ' + error.code + '\n' +
'message: ' + error.message + '\n');
}
I am including Audio.js in my html file and then calling function
playAudio("audio/welcome.mp3");
All of my mp3 files reside in www/audio folder.
I debugged using alert statements, call comes till this line of code
my_media = new Media(src, onSuccess, onError);
No error callback or no errors in console of xcode.
Am I missing something here? What could be the issue?
Phonegap is pretty difficult with having stuff behaving differently that it should, especially when it comes to media and plugins. You need to have something that changes the file path based off of the device (using the Cordova Device Plugin):
//Gets Device Platform
document.addEventListener("deviceready", getDevicePlatform, false);
function getDevicePlatform() {
$scope.devicePlatform = device.platform;
console.log(device.platform);
}
Then to set the media player file path correctly:
if ($scope.devicePlatform == "Android") {
var songFilePath = '/android_asset/www/res/' + song.name + '.mp3';
}
else {
var songFilePath = 'res/' + song.name + '.mp3';
}
$scope.song = new Media(songFilePath);
The song .name is something special I had to dynamically get the path, but the main thing is that for Android devices you need the /android_asset/, but for other devices like iOS and PhoneGap you can use normal file paths to media inside the /www folder.

Resources