in Titanium, how to take a thumbnail from video in android? - android

I'm trying to get a thumbnail image from a video player that I set the Url when I choose a video from gallery or capture a video, so this my code in controller:-
var intent = Titanium.Android.createIntent({
action: Ti.Android.ACTION_PICK,
type : "video/*"
});
intent.addCategory(Ti.Android.CATEGORY_DEFAULT);
var curActivity = $.createPost.getActivity();
curActivity.startActivityForResult(intent, function(event) {
if (event.resultCode == Ti.Android.RESULT_OK) {
if (event.intent.data != null) {
// If everything went OK, save a reference to the video URI
Ti.API.info('here '+ event.intent.data);
Ti.API.info('video: '+ event.intent);
var videoPath = event.intent.data;
video = Ti.Filesystem.getFile(videoPath);
$[e.source.fileView].setUrl(event.intent.data);
// $[e.source.fileView].setMedia(file);
$[e.source.fileView].setVisible(true);
$[e.source.removeBtn].setVisible(true);
$.video.requestThumbnailImagesAtTimes([0,1,2,6,12], Titanium.Media.VIDEO_TIME_OPTION_NEAREST_KEYFRAME, function (response) {
alert('text');
Ti.API.info("Thumbnail callback called, success = " + response.success);
Ti.API.info("Thumbnail callback called, time = " + response.time);
Ti.API.info("Thumbnail callback called, code = " + response.code);
if(response.success) {
videoThumb = response.image;
}
});
}
else {
Ti.API.error('Could not retrieve media URL!');
}
}
else if (event.resultCode == Ti.Android.RESULT_CANCELED) {
Ti.API.trace('User cancelled video capture session.');
}
else {
Ti.API.error('Could not record video!');
}
});
I get the video and it appears in the video player ($.video) but I can't get the thumbnail at all.
this code works just fine in IOS
$.video.requestThumbnailImagesAtTimes([0,1,2,6,12], Titanium.Media.VIDEO_TIME_OPTION_NEAREST_KEYFRAME, function (response) {
alert('text');
Ti.API.info("Thumbnail callback called, success = " + response.success);
Ti.API.info("Thumbnail callback called, time = " + response.time);
Ti.API.info("Thumbnail callback called, code = " + response.code);
if(response.success) {
videoThumb = response.image;
}
});
but not in android.

It appears that at this time you cannot do this: it's broken. Here's the Jira ticket for the issue:
Android: Get image frames at times out of local video
It indicates that it's fixed in 6.1.0. Apparently now you can do this with a remote video, so if that fits your workflow you might be able to accomplish it.
Does this answer your question?

Related

PlatformChannel Already open error when I try to cast a second video

I have a receiver app (V2) that works fine when you show the first video, but when you go to show a second video I get this:
[cast.receiver.platform.WebSocket] PlatformChannel Already open
I am unloading and loading the player each time. I can't see any way to explicitly ask the PlatformChannel to close. Here's the relevant code from the function that starts play:
this.receiverManager.start()
this.host = new cast.player.api.Host({'mediaElement':this.refs.video, 'url':source})
this.host.onError = function(errorCode) {
console.log("Fatal Error - " + errorCode)
if (window.player) {
window.player.unload()
window.player = null
}
}
this.host.updateSegmentRequestInfo = function(requestInfo) {
requestInfo.withCredentials = false;
}
if(!window.player) {
window.player = new cast.player.api.Player(this.host)
}
this.receiverManager.setApplicationState('Ready To Cast');
this.protocol = cast.player.api.CreateDashStreamingProtocol(this.host)
window.player.load(this.protocol, 0)
We highly recommend that you move to a CAF receiver. Also, CAF has a new queueing API that will handle a playlist of videos.

Select camera on Android Chrome

I came across several questions on this subject. I'm trying to select the rear camera on an Android device running Chrome.
So, after some reading :
var selector = document.getElementById('video-source-selector');
navigator.mediaDevices.enumerateDevices()
.then(function(devices) {
var videoDevices = devices.map(function (item) {
if(item.kind === 'videoinput'){
return item;
}
}).filter(function( element ) {
return element !== undefined;
});
var max = videoDevices.length;
videoDevices.forEach(function(device, i) {
var html = '';
var div = document.createElement('div');
if(i === max-1){ // last element reached
html += '<option value="'+device.deviceId+'" selected>'+ device.label +'</option>';
}
else {
html += '<option value="'+device.deviceId+'">'+ device.label +'</option>';
}
div.innerHTML = html;
selector.appendChild(div.childNodes[0]);
console.log(device.kind + ": " + device.label +
" id = " + device.deviceId);
});
})
.catch(function(err) {
console.log(err.name + ": " + err.message);
});
selector.addEventListener("change", function(){
console.log(selector.value); // Works as supposed : returns the ID of the selected device
});
Then, as I'm using Three.js in this app, I'm binding this ID to Jerome Etienne three extension WebcamGrabbing (https://github.com/jeromeetienne/threex.webar):
var videoGrabbing = new THREEx.WebcamGrabbing(selector.value);
Then I had to modify THREEx.WebcamGrabbing class this way (I removed the irrelevant parts):
THREEx.WebcamGrabbing = function(sourceDeviceId){
...
console.log('webcamgrabbing : ', sourceDeviceId); // returns the expected ID
var constraints = {
video: {
optional: [{
sourceId: sourceDeviceId
}]
}
}
// try to get user media
navigator.getUserMedia( constraints, function(stream){
domElement.src = URL.createObjectURL(stream);
}, function(error) {
console.error("Cant getUserMedia()! due to ", error);
});
...
}
But still, Chrome on Android is still giving me the stream of the face camera, whatever device I select...
What do I miss?
EDIT : Based on this topic (GetUserMedia - facingmode), I came up with some logs to see what's happening here :
var constraints = {
audio: false,
video: { facingMode: { exact: "environment" } }
}
console.log('Try to get stream with constraints:', constraints);
navigator.getUserMedia( constraints, function(stream){
var videoTracks = stream.getVideoTracks();
console.log('Got stream with constraints:', constraints); // Ok
console.log('Using video device: ' + videoTracks[0].label); // > Using video device: camera 0, facing back
for(var i = 0; i < videoTracks.length; i++){
console.log('Found video device with contraints : ', videoTracks[i].label); // Found video device with contraints : camera 0, facing back
}
domElement.src = URL.createObjectURL(stream);
}, function(error) {
console.error("Cant getUserMedia()! due to ", error);
});
An alternative way to select the back camera on chrome is to use the enumerateDevices method.
First get all the video input id's:
navigator.mediaDevices.enumerateDevices().then(function(devices) {
devices.forEach(function(device) {
if(device.kind=="videoinput"){
//If device is a video input add to array.
}
});
Then the first element of the array will contain the id of the front camera, the second element will contain the id of the back camera.
Finally put the id of the camera that you want to use
navigator.getUserMedia({audio: false, video: { sourceId: VideoId } }, successCallback, errorCallback);

Recording audio using cordova media plugin results in empty file

I'm using the following code to record audio using Cordova Media plugin on android devices. This results in an empty audio file despite returning successfully on stopRecord(). Can somebody point to what might be wrong with this code?
$cordovaFile.createFile(cordova.file.dataDirectory, 'new-rec.amr'), false)
.then(function (fileObj) {
console.log('File created', fileObj);
var nativePath = fileObj.nativeURL;
resolveLocalFileSystemURL(nativePath, function (entry) {
var internalPath = entry.toInternalURL();
var mediaRec = new Media(internalPath,
//success callback
function (success) {
console.log("record success", success);
},
//error callback
function (err) {
console.log("record error: " + JSON.stringify(err));
});
// Start recording audio
mediaRec.startRecord();
});
}, function (error) {
console.log('Cannot create a file to initiate a new recording', error);
});
I was having this exact same problem today. Solved it by releasing the Media object. I'm not sure why this helped, but I have a feeling it may be that the OS is holding onto the Media object instead of saving it and then once it's released it's properly saved?
Not 100% sure though. It works without releasing in other Cordova directories (e.g. cordova.file.externalDataDirectory).
Here's my code:
var fileName = randomFileName();
fileName = fileName + ".aac";
var store = cordova.file.dataDirectory;
window.resolveLocalFileSystemURL(store, function(dir){
var directoryToSave = dir.nativeURL + fileName;
$scope.audioRecording = new Media(directoryToSave, audioRecordingSuccess, audioRecordingFail, audioStatus);
$scope.audioRecording.startRecord();
setTimeout(function(){
$scope.audioRecording.stopRecord();
$scope.audioRecording.release();
}, 5000);
});

LeafletJS - Cached tiles with local filesystem

I'm implementing a Cordova application (3.2) where I want to use LeafletJS and a map tile provider together with a local filesystem cache of the tiles.
My approach in an overview is the following:
Extend the Leaflet TileLayer
Overwrite the _loadTile method to retrieve the tile either from local filesystem or from remote
My code:
var StorageTileLayer = L.TileLayer.extend({
log: function (text) {
if (this.options.log)
this.options.log(text);
else
console.log("[StorageTileLayer]: " + text);
},
_setUpTile: function (tile, key, value, cache) {
try {
tile._layer = this;
tile.onload = this._tileOnLoad;
tile.onerror = this._tileOnError;
this._adjustTilePoint(tile);
tile.src = value;
this.fire('tileloadstart', {
tile: tile,
url: tile.src
});
this.log("Setting url to " + tile.src);
}
catch (e) {
this.log("ERROR in setUpTile: " + e.message);
}
},
_loadTile: function (tile, tilePoint) {
this._adjustTilePoint(tilePoint);
var key = tilePoint.z + ',' + tilePoint.y + ',' + tilePoint.x;
var self = this;
var tileUrl = self.getTileUrl(tilePoint);
console.log(tileUrl);
console.log(typeof tileUrl);
if (this.options.storage) {
this.log("Load Tile with storage");
this.options.storage.get(key, tileUrl).then(function (value) {
self.log("Tile URL to load: " + value.url);
self._setUpTile(tile, key, value.url, true);
});
} else {
this.log("Load Tile without storage");
self._setUpTile(tile, key, tileUrl, false);
}
}
});
options.storage is a storage which has the method get(key, remoteUrl) and returns either the cached tile from local filestorage (this implementation actual works fine, so here is not the problem) or the remote url but downloads the tile in the background, so that it will be available from local file storage on the next call.
Unfortunately I can see on my device when I use Charles (Web Debugging Proxy) that although the local map tiles are loaded (I can see it from the logs) that there are still a couple of requests to the map tiles provider.
Does anyone have an idea what I am doing wrong and what else I have to overwrite in my StorageTileLayer to prevent the calls to the remote? The real problem is, that the map should work in offline mode as well, but it is not.
Thanks for your help.
Libraries in the environment:
Leaflet (0.7.3)
angularJS (1.2.16)
Cordova (3.2)
I basically fixed it with this code (angular js):
(function (window, L) {
var isDebug = false;
var StorageTileLayer = L.TileLayer.extend({
log: function (text) {
if (!isDebug)
return;
if (this.options.log)
this.options.log(text);
else
console.log("[StorageTileLayer]: " + text);
},
_setUpTile: function (tile, key, value, cache) {
try {
tile._layer = this;
tile.onload = this._tileOnLoad;
tile.onerror = this._tileOnError;
this._adjustTilePoint(tile);
tile.src = value;
this.fire('tileloadstart', {
tile: tile,
url: tile.src
});
}
catch (e) {
this.log("ERROR in setUpTile: " + e.message);
}
},
_loadTile: function (tile, tilePoint) {
this._adjustTilePoint(tilePoint);
var key = tilePoint.z + ',' + tilePoint.y + ',' + tilePoint.x;
var self = this;
var tileUrl = self.getTileUrl(tilePoint);
if (isNaN(tilePoint.x) || isNaN(tilePoint.y)) {
this.log("TilePoint x or y is nan: " + tilePoint.x + "-" + tilePoint.y);
return;
}
if (this.options.storage) {
this.options.storage.get(key, tileUrl).then(function (value) {
self.log("Tile URL to load: " + value.url);
self._setUpTile(tile, key, value.url, true);
});
} else {
this.log("Load Tile without storage");
self._setUpTile(tile, key, tileUrl, false);
}
}
});
window.StorageTileLayer = StorageTileLayer;
})(window, L);
Adding the tile layer to the leaflet map is the important part! you have to prevent the load balancer from getting different urls for each tile. I did it by setting the url of the tole layer to a fixed value:
var url = 'https://a.tiles.mapbox.com/v3/<<YOUR ACCESS CODE>>/{z}/{x}/{y}.png';
var layer = new StorageTileLayer(url, {
storage: TileStorage
});
Of course you still have to implement the TileStorage in my case it has a single method get(key, url) and returns a $q-defer which is resolved with either the local available file. If the file is not available in the local storage it will be downloaded and then the promise is resolved.
Unfortunately this TileStorage is not public available because its an in-house development of my company so I can't share it.
Nevertheless I hope this helps you.

Dowloading images random error

I have the following piece of code:
function download_img(imgToDownload, imgToRemove){
var url = remote_url+imgToDownload; // image url
root_path = get_root_path();
var flag = "working";
var flag_delete = false;
var imageToDownloadPath = root_path + "/" + imgToDownload; // full file path
var imageToRemovePath = root_path + "/" + imgToRemove; // full file path
try{
var fileTransfer = new FileTransfer();
fileTransfer.download(url, imageToDownloadPath,
function () {
if(imgToRemove != "" && imgToRemove != null){
var entry = new FileEntry("foo", imageToRemovePath);
entry.remove(function (){alert("fine");flag_delete = true;}, function (){alert("marron");flag_delete = true;});
}
else{
flag_delete = true;
}
flag = "done";
},
function (error) {
flag = "done";
flag_delete = true;
}
);
}catch(error){
alert("Error capturado: "+error.message);
}
while(flag=="working" && !flag_delete){
try{
setTimeout(
function() {
/* Código */
},
300
);
}
catch(error){
alert("Error en el bucle: " + error.message);
}
}
}
I have had problems downloading the files which apparently seemed to be a sync conflict, I mean, looks like something was avorting the execution before the file/s was/were downloaded.
I have used two flags to make sure not to continue until files are downloaded (and old ones deleted if necessary). The idea is to change flag's values when the action is completed and keep the code waiting in a loop.
The results this code is giving is as follows:
It never seems to enter the success fileTransfer.download sucess method (I used an alert which never triggered) even though the first file downloads properly.
The flags are never changed so the code stays stuck in the loop, and it does not continue downloading other files.
I think it might be a very basic jQuery behaviour but I am just starting with this technologies and I am a little lost. If anyone could give me a clue on that I would really appreciate it.
Thanks!!

Categories

Resources