Get device labels onpageshow - javascript

I'm coding a web app, where user can use his camera (and choose which one to use). The problem is that I want user to be available to choose the camera before it is enabled. In the current code, when user turns on a page, he sees an empty list of cameras and when he enables the camera stream, dropdown list populates with camera names. I want the dropdown list populate when he turns on that web page.
P.S. when I stop() the camera, it disables camera and gives just a black screen. Why it is black instead of background colour?
CameraStreamView.cshtml
#using Api.Models
#{
ViewBag.Title = "Smart Vision";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="~/Content/Contact-Form-Clean.css">
</head>
<body onpageshow="Init()">
<div id="container">
<video id="video" style="display: block; margin: 0 auto; margin-top: 30px;" width="300" height="400" autoplay></video>
<button id="enableStream" style="display: block; margin: 0 auto; margin-top: 20px; height: 70px; width: 200px" onclick="start()">Enable camera</button>
<button id="disableStream" style="display: block; margin: 0 auto; margin-top: 20px; height: 70px; width: 200px" onclick="stop()">Disable camera</button>
<label for="videoSource">Video source: </label><select id="videoSource"></select>
</div>
<script src="~/Scripts/GetCameraFeed.js"></script>
</body>
</html>
GetCameraFeed.js
const videoSelect = document.querySelector('select#videoSource');
const selectors = [videoSelect];
function gotDevices(deviceInfos) {
// Handles being called several times to update labels. Preserve values.
const values = selectors.map(select => select.value);
selectors.forEach(select => {
while (select.firstChild) {
select.removeChild(select.firstChild);
}
});
for (let i = 0; i !== deviceInfos.length; ++i) {
const deviceInfo = deviceInfos[i];
const option = document.createElement('option');
option.value = deviceInfo.deviceId;
if (deviceInfo.kind === 'videoinput') {
option.text = deviceInfo.label;
videoSelect.appendChild(option);
}
}
selectors.forEach((select, selectorIndex) => {
if (Array.prototype.slice.call(select.childNodes).some(n => n.value === values[selectorIndex])) {
select.value = values[selectorIndex];
}
});
}
function Init() {
navigator.mediaDevices.enumerateDevices().then(gotDevices).catch(handleError);
}
function gotStream(stream) {
window.stream = stream; // make stream available to console
video.srcObject = stream;
// Refresh button list in case labels have become available
return navigator.mediaDevices.enumerateDevices();
}
function handleError(error) {
console.log('navigator.getUserMedia error: ', error);
}
function start() {
const videoSource = videoSelect.value;
const constraints = {
video: { deviceId: videoSource ? { exact: videoSource } : undefined }
};
navigator.mediaDevices.getUserMedia(constraints).then(gotStream).then(gotDevices).catch(handleError);
}
function stop() {
video.pause();
video.src = "";
stream.getTracks().forEach(track => track.stop());
console.log("Stopping stream");
}

What you want is explicitly disallowed, due to fingerprinting concerns. Details about a user's setup let web sites identify them uniquely on the web, a privacy concern.
Once users trust your site with their camera and microphone, this information is considered relevant to share.
The working group determined this to be a reasonable trade-off, for several reasons:
Most desktop users have only one camera or none.
Most phone users have two, but you can use the facingMode constraint to pick.
Given 1 and 2, an up-front choice is arguably an inferior user experience for most.
I would consider changing your code to ask for the default camera the first time, and give users a choice to change it after the fact, should they need to. It's what most WebRTC sites do.
Note that this should only be a problem the first time a user visits your site. Provided they've granted camera or microphone just once in the past, you should be able to see the labels, at least in Chrome.
Unlike Chrome, Firefox does not persist permission implicitly, so you'd need a little more work to get labels on page-load on repeat visits:
enumerateDevices returns a deviceId for each device, which is persisted for your site provided the user has granted (or will grant within this session) camera or microphone at least once. You can use cookies or local storage to correlate deviceIds to device labels. This also survives people manually revoking permission in Chrome.

Related

Viber not redirect to main page if desktop app isn't installed [duplicate]

I want to create a web-page, a page that will redirect an iPhone to the App Store if the iPhone does not have the application installed, but if the iPhone has the app installed I want it to open the application.
I have already implemented a custom URL in the iPhone application, so I have a URL for the application that is something like:
myapp://
And if this URL is invalid, I want the page to redirect to the App Store. Is this possible at all?
If I don't have the application installed on the phone and write the myapp:// URL in Safari, all I get is an error message.
Even if there exists an ugly hack with JavaScript, I would really like to know.
As far as I know you can not, from a browser, check if an app is installed or not.
But you can try redirecting the phone to the app, and if nothing happens redirect the phone to a specified page, like this:
setTimeout(function () { window.location = "https://itunes.apple.com/appdir"; }, 25);
window.location = "appname://";
If the second line of code gives a result then the first row is never executed.
Similar questions:
iPhone browser: Checking if iPhone app is installed from browser
Is it possible to register a http+domain-based URL Scheme for iPhone apps, like YouTube and Maps?
To further the accepted answer, you sometimes need to add extra code to handle people returning to the browser after launching the app - that the setTimeout function will run whenever they do. So, I do something like this:
var now = new Date().valueOf();
setTimeout(function () {
if (new Date().valueOf() - now > 100) return;
window.location = "https://itunes.apple.com/appdir";
}, 25);
window.location = "appname://";
That way, if there has been a freeze in code execution (i.e., app switching), it won't run.
iOS Safari has a feature that allows you to add a "smart" banner to your webpage that will link either to your app, if it is installed, or to the App Store.
You do this by adding a meta tag to the page. You can even specify a detailed app URL if you want the app to do something special when it loads.
Details are at Apple's Promoting Apps with Smart App Banners page.
The mechanism has the advantages of being easy and presenting a standardized banner. The downside is that you don't have much control over the look or location. Also, all bets are off if the page is viewed in a browser other than Safari.
As of 2017, it seems there's no reliable way to detect an app is installed, and the redirection trick won't work everywhere.
For those like me who need to deep-link directly from emails (quite common), it is worth noting the following:
Sending emails with appScheme:// won't work fine because the links will be filtered in Gmail
Redirecting automatically to appScheme:// is blocked by Chrome: I suspect Chrome requires the redirection to be synchronous to user interaction (like a click)
You can now deep link without appScheme:// and it's better but it requires a modern platform and additional setup. Android iOS
It is worth noting that other people already thought about this in-depth. If you look at how Slack implements his "magic link" feature, you can notice that:
It sends an email with a regular HTTP link (ok with Gmail)
The web page has a big button that links to appScheme:// (ok with Chrome)
#Alistair pointed out in this answer that sometimes users will return to the browser after opening the app. A commenter to that answer indicated that the times values used had to be changed depending on iOS version.
When our team had to deal with this, we found that the time values for the initial timeout and telling whether we had returned to the browser had to be tuned, and often didn't work for all users and devices.
Rather than using an arbitrary time difference threshold to determine whether we had returned to the browser, it made sense to detect the "pagehide" and "pageshow" events.
I developed the following web page to help diagnose what was going on. It adds HTML diagnostics as the events unfold, mainly because using techniques like console logging, alerts, or Web Inspector, jsfiddle.net, etc. all had their drawbacks in this work flow. Rather than using a time threshold, the JavaScript counts the number of "pagehide" and "pageshow" events to see whether they have occurred. And I found that the most robust strategy was to use an initial timeout of 1000 (rather than the 25, 50, or 100 reported and suggested by others).
This can be served on a local server, e.g. python -m SimpleHTTPServer and viewed on iOS Safari.
To play with it, press either the "Open an installed app" or "App not installed" links. These links should cause respectively the Maps app or the App Store to open. You can then return to Safari to see the sequence and timing of the events.
(Note: this will work for Safari only. For other browsers (like Chrome) you'd have to install handlers for the pagehide/show-equivalent events).
Update: As #Mikko has pointed out in the comments, the pageshow/pagehide events we are using are apparently no longer supported in iOS8.
<html>
<head>
</head>
<body>
Open an installed app
<br/><br/>
App not installed
<br/>
<script>
var hideShowCount = 0 ;
window.addEventListener("pagehide", function() {
hideShowCount++;
showEventTime('pagehide');
});
window.addEventListener("pageshow", function() {
hideShowCount++;
showEventTime('pageshow');
});
function clickHandler(){
var hideShowCountAtClick = hideShowCount;
showEventTime('click');
setTimeout(function () {
showEventTime('timeout function ' + (hideShowCount-hideShowCountAtClick) + ' hide/show events');
if (hideShowCount == hideShowCountAtClick){
// app is not installed, go to App Store
window.location = 'http://itunes.apple.com/app';
}
}, 1000);
}
function currentTime()
{
return Date.now()/1000;
}
function showEventTime(event){
var time = currentTime() ;
document.body.appendChild(document.createElement('br'));
document.body.appendChild(document.createTextNode(time + ' ' + event));
}
</script>
</body>
</html>
You can check out this plugin that tries to solve the problem. It is based on the same approach as described by missemisa and Alastair etc, but uses a hidden iframe instead.
https://github.com/hampusohlsson/browser-deeplink
I needed to do something like this, and I ended up going with the following solution.
I have a specific website URL that will open a page with two buttons
Button one go to the website
Button two go to the application (iPhone / Android phone / tablet). You can fall back to a default location from here if the app is not installed (like another URL or an app store)
Cookie to remember the user's choice
<head>
<title>Mobile Router Example </title>
<script type="text/javascript">
function set_cookie(name,value)
{
// JavaScript code to write a cookie
}
function read_cookie(name) {
// JavaScript code to read a cookie
}
function goToApp(appLocation) {
setTimeout(function() {
window.location = appLocation;
// This is a fallback if the app is not installed.
// It could direct to an app store or a website
// telling user how to get the app
}, 25);
window.location = "custom-uri://AppShouldListenForThis";
}
function goToWeb(webLocation) {
window.location = webLocation;
}
if (readCookie('appLinkIgnoreWeb') == 'true' ) {
goToWeb('http://somewebsite');
}
else if (readCookie('appLinkIgnoreApp') == 'true') {
goToApp('http://fallbackLocation');
}
</script>
</head>
<body>
<div class="iphone_table_padding">
<table border="0" cellspacing="0" cellpadding="0" style="width:100%;">
<tr>
<td class="iphone_table_leftRight"> </td>
<td>
<!-- Intro -->
<span class="iphone_copy_intro">Check out our new app or go to website</span>
</td>
<td class="iphone_table_leftRight"> </td>
</tr>
<tr>
<td class="iphone_table_leftRight"> </td>
<td>
<div class="iphone_btn_padding">
<!-- Get iPhone app button -->
<table border="0" cellspacing="0" cellpadding="0" class="iphone_btn" onclick="set_cookie('appLinkIgnoreApp',document.getElementById('chkDontShow').checked);goToApp('http://getappfallback')">
<tr>
<td class="iphone_btn_on_left"> </td>
<td class="iphone_btn_on_mid">
<span class="iphone_copy_btn">
Get The Mobile Applications
</span>
</td>
<td class="iphone_btn_on_right"> </td>
</tr>
</table>
</div>
</td>
<td class="iphone_table_leftRight"> </td>
</tr>
<tr>
<td class="iphone_table_leftRight"> </td>
<td>
<div class="iphone_btn_padding">
<table border="0" cellspacing="0" cellpadding="0" class="iphone_btn" onclick="set_cookie('appLinkIgnoreWeb',document.getElementById('chkDontShow').checked);goToWeb('http://www.website.com')">
<tr>
<td class="iphone_btn_left"> </td>
<td class="iphone_btn_mid">
<span class="iphone_copy_btn">
Visit Website.com
</span>
</td>
<td class="iphone_btn_right"> </td>
</tr>
</table>
</div>
</td>
<td class="iphone_table_leftRight"> </td>
</tr>
<tr>
<td class="iphone_table_leftRight"> </td>
<td>
<div class="iphone_chk_padding">
<!-- Check box -->
<table border="0" cellspacing="0" cellpadding="0">
<tr>
<td><input type="checkbox" id="chkDontShow" /></td>
<td>
<span class="iphone_copy_chk">
<label for="chkDontShow"> Don’t show this screen again.</label>
</span>
</td>
</tr>
</table>
</div>
</td>
<td class="iphone_table_leftRight"> </td>
</tr>
</table>
</div>
</body>
</html>
After compiling a few answers, I've come up with the following code. What surprised me was that the timer does not get frozen on a PC (Chrome and Firefox) or Android Chrome - the trigger worked in the background, and the visibility check was the only reliable information.
var timestamp = new Date().getTime();
var timerDelay = 5000;
var processingBuffer = 2000;
var redirect = function(url) {
//window.location = url;
log('ts: ' + timestamp + '; redirecting to: ' + url);
}
var isPageHidden = function() {
var browserSpecificProps = {hidden:1, mozHidden:1, msHidden:1, webkitHidden:1};
for (var p in browserSpecificProps) {
if(typeof document[p] !== "undefined"){
return document[p];
}
}
return false; // Actually inconclusive, assuming not
}
var elapsedMoreTimeThanTimerSet = function(){
var elapsed = new Date().getTime() - timestamp;
log('elapsed: ' + elapsed);
return timerDelay + processingBuffer < elapsed;
}
var redirectToFallbackIfBrowserStillActive = function() {
var elapsedMore = elapsedMoreTimeThanTimerSet();
log('hidden:' + isPageHidden() + '; time: ' + elapsedMore);
if (isPageHidden() || elapsedMore) {
log('not redirecting');
}else{
redirect('appStoreUrl');
}
}
var log = function(msg){
document.getElementById('log').innerHTML += msg + "<br>";
}
setTimeout(redirectToFallbackIfBrowserStillActive, timerDelay);
redirect('nativeApp://');
JS Fiddle
The following answer still works, tested on iOS 10 through 14. It builds upon earlier answers. I added window.close() to get rid of the empty tab window that was left behind in browsers after the redirects or page return. If fixes 2 of the 4 scenarios where a blank tab would be left behind....maybe someone else can fix the 3rd & 4th
<script>
var now = new Date().valueOf();
setTimeout(function () {
// time stamp comaprison prevents redirecting to app store a 2nd time
if (new Date().valueOf() - now > 100) {
window.close() ; // scenario #4
// old way - "return" - but this would just leave a blank page in users browser
//return;
}
if (isIOS == 1) {
// still can't avoid the "invalid address" safari pops up
// but at least we can explain it to users
var msg = "'invalid address' = MyApp NOT DETECTED.\n\nREDIRECTING TO APP STORE" ;
} else {
var msg = "MyApp NOT DETECTED\n\nREDIRECTING TO APP STORE" ;
}
if (window.confirm(msg)) {
window.location = "<?=$storeUrl?>";
// scenario #2 - will leave a blank tab in browser
} else {
window.close() ; // scenario #3
}
}, 50);
window.location = "<?=$mobileUrl?>";
// scenario #1 - this will leave a blank tab
</script>
I have been trying to achieve the same in a Safari extension for iOS15. It seems that all previous strategies fail - the "Open in" dialog and the "Invalid address" one are completely equal, both non-blocking, so the timer-based solutions offer inconsistent results, depending on the time it takes to load the page.
My workaround was to create an app store redirect message within a modal popup that imitates the appearance of the system prompt, hide it behind the system prompt, and dismiss it with an event listener when the tab loses focus. There are two remaining problems with the UX:
There is no way to suppress the "Invalid address" prompt. All we can do (if we don't go the Universal Links path) is to explain it afterwards with our own prompt.
If the user chooses "Cancel" from the "Open in" prompt, he or she is still presented with our redirect prompt.
The following code benefitted both from the answers above and from this SO code for creating a modal popup.
// Change the following vars to suit your needs
var my_app_name = "My App";
var my_app_id = "id1438151717"
var my_app_scheme = "myapp://do.this"
function toggleModal(isModal, inputs, elems, msg) {
for (const input of inputs) input.disabled = isModal;
modal.style.display = isModal ? "block" : "none";
elems[0].textContent = isModal ? msg : "";
}
function myConfirm(msg) {
const inputs = [...document.querySelectorAll("input, textarea, select")].filter(input => !input.disabled);
const modal = document.getElementById("modal");
const elems = modal.children[0].children;
return new Promise((resolve) => {
toggleModal(true, inputs, elems, msg);
elems[3].onclick = () => resolve(true);
elems[4].onclick = () => resolve(false);
}).then(result => {
toggleModal(false, inputs, elems, msg);
return result;
});
}
function redirectMessage() {
var r = myConfirm("To download " + my_app_name + ", tap OK.");
return r.then(ok => {
if (ok) {
console.log("Redirecting to the App Store...");
window.location = "itms-apps://itunes.apple.com/app/" + my_app_id;
} else {
console.log("User cancelled redirect to the App Store");
}
return ok;
});
}
function prepareListener() {
document.addEventListener("visibilitychange", function() {
const inputs = [...document.querySelectorAll("input, textarea, select")].filter(input => !input.disabled);
const modal = document.getElementById("modal");
const elems = modal.children[0].children;
if (!document.hasFocus()) {
console.log("User left tab. Closing modal popup")
toggleModal(false, inputs, elems, "");
}
});
}
function onTap() {
setTimeout(function() {
// We can't avoid the "invalid address" Safari popup,
// but at least we can explain it to users.
// We will create a modal popup behind it, which the
// event listener will close automatically if the app
// opens and we leave the tab
redirectMessage()
}, 50);
window.location = my_app_scheme;
}
prepareListener()
#modal {
display: none;
position: fixed;
z-index: 1;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background: rgb(0, 0, 0);
background: rgba(0, 0, 0, 0.4);
font-family: "ms sans serif", arial, sans-serif;
font-size: medium;
border-radius: 15px;
}
#modal>div {
position: relative;
padding: 10px;
width: 320px;
height: 60px;
margin: 0 auto;
top: 50%;
margin-top: -45px;
background: white;
border: 2px outset;
border-radius: 15px;
}
#cancel_button {
position: fixed;
right: 50%;
margin-right: -95px;
bottom: 50%;
margin-bottom: -32px;
padding: 0;
border: none;
background: none;
color: rgb(0, 122, 255);
font-size: medium;
font-weight: normal;
}
#ok_button {
position: fixed;
right: 50%;
margin-right: -140px;
bottom: 50%;
margin-bottom: -32px;
padding: 0;
border: none;
background: none;
color: rgb(0, 122, 255);
font-size: medium;
font-weight: semi-bold;
}
<div id="modal">
<div>
<div></div><br><br>
<button id="ok_button">OK</button>
<button id="cancel_button">Cancel</button>
</div>
</div>
<p> Tap here to open app </p>
The date solution is much better than others. I had to increment the time to 50 like that.
This is a Twitter example:
// On click of your event handler...
var twMessage = "Your Message to share";
var now = new Date().valueOf();
setTimeout(function () {
if (new Date().valueOf() - now > 100)
return;
var twitterUrl = "https://twitter.com/share?text=" + twMessage;
window.open(twitterUrl, '_blank');
}, 50);
window.location = "twitter://post?message=" + twMessage;
The only problem on mobile iOS Safari is when you don't have the app installed on the device, and so Safari shows an alert that autodismisses when the new URL is opened. Anyway, it is a good solution for now!
I didn't read all of these answers, but you may be use an iframe and adding the source to, "my app://whatever".
Then check regularly on a set interval of the page is 404 or not.
You could also use an Ajax call. If there is a 404 response then the app is not installed.

Is there a way to analyze the amplitude from a spotify song using p5.js in browser?

I am attempting to make a Spotify visualizer as a personal project, but can't seem to find a way to analyze the sound before it is played through the browser. I have the Spotify player playing music through the browser, and the visualizer is working with mp3 files stored on my machine. I just don't have the slightest clue on how to connect the two. Additionally, I looked through the Spotify API and couldn't seem to find anything there either.
If there is no way to analyze a Dpotify track directly, is there any way to capture the sound played through the browser first, then play through the p5.js loadSound() function?
Here are the code snippets for reference:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.2/p5.js">
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.2/addons/p5.dom.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.2/addons/p5.sound.js"></script>
</head>
<body>
<script src="https://sdk.scdn.co/spotify-player.js"></script>
<script>
window.onSpotifyWebPlaybackSDKReady = () => {
const token =
'redacted';
const player = new Spotify.Player({
name: 'Web Playback SDK Quick Start Player',
getOAuthToken: cb => {
cb(token);
}
});
// Error handling
player.addListener('initialization_error', ({
message
}) => {
console.error(message);
});
player.addListener('authentication_error', ({
message
}) => {
console.error(message);
});
player.addListener('account_error', ({
message
}) => {
console.error(message);
});
player.addListener('playback_error', ({
message
}) => {
console.error(message);
});
// Playback status updates
player.addListener('player_state_changed', state => {
console.log(state);
});
// Ready
player.addListener('ready', ({
device_id
}) => {
console.log('Ready with Device ID', device_id);
});
// Not Ready
player.addListener('not_ready', ({
device_id
}) => {
console.log('Device ID has gone offline', device_id);
});
// Connect to the player!
player.connect();
};
</script>
<script src="visualizer.js"></script>
var spotifyToken = ""
//["streaming", "user-read-birthdate", "user-read-email", "user-read-private"]
var song;
var amp;
var button;
var volHistory = [];
function toggleSong(){
if (song.isPlaying()){
song.pause();
} else {
song.play();
}
}
function preload(){
soundFormats('mp3');
song = loadSound('backgroundsong.mp3')
}
function setup(){
var canvas = createCanvas(window.innerWidth/2, 100);
//canvas.parent('div id')
canvas.position(0, (windowHeight - height) / 2)
//var canvasLeft = createCanvas(window.innerWidth/2, 100);
//canvasLeft.position(windowWidth/2, (windowHeight - height) / 2)
//createCanvas(window.innerWidth, window.innerHeight);
masterVolume(0.002,0,0);
button = createButton('toggle');
button.mousePressed(toggleSong);
song.loop();
amp = new p5.Amplitude();
}
function draw(){
background(0);
var volume = amp.getLevel();
volHistory.push(volume * 400);
beginShape();
stroke(0, 255, 0);
strokeWeight(3);
strokeCap(ROUND);
strokeJoin(ROUND);
noFill();
for (var i = 0; i < volHistory.length; i++) {
var y = map(volHistory[i], 0, 1, height, 0);
vertex(i*1.5, y);
if (i*1.5 > width) {
volHistory.splice(0, 1);
}
}
endShape();
//ellipse(400, 400, volume * 800, volume * 800);
}
Processing raw data samples is not possible using Spotify's Web Playback SDK. As seen in the following quote Spotify uses Encrypted Media Extensions in order to make playback possible within the browser.
The Web Playback SDK is client-side JavaScript library which allows you to create a new player in Spotify Connect and play any audio track from Spotify in the browser via Encrypted Media Extensions.
You could use this Spotify Web API endpoint and try to visualize one of those properties. There are no actual samples (which would be the amplitude you asked for) but maybe something those data sets include will fit your needs.
I found a reddit thread where some people showed off some visualizations mostly using the before mentioned API endpoint.
Another option is to use Spotify's Android or iOS SDK where you have access to raw data samples though I am not sure if it is allowed by Spotify's terms of service to process those samples. But due to you are looking for something client-sided within the browser this won't fit your needs.

Dynamic timed text track for captions on canvas video

I have researched and experimented my brains out on this one. I have built a working application that uses the HTML5 canvas to play videos and interact with users through a web page (a game, sort of). Works like a charm, but I want to add captions to the video to make it more accessible. I don't even seem to be able to get the WebVTT file to load and I've reached the stage of voodoo programming by trying examples from the web and am making no progress. I have tried to distill the application down to its bare minimum here in hopes someone can provide some insight into what I'm doing wrong.
The video plays when I click on the canvas, the "...waiting for cuechange event..." message stays up while it plays, and then it goes to the "video complete ... reload to try again" message when done (kept it simple by requiring a page reload to try the test again), so the basic mechanics seem to be working. It never gets into the "load" function for the text track (never displays "... loaded text track ..."), and the updateSubtitle() function is never invoked. If I insert a trackElement.readyStatus() call, it always returns 0 (unsurprisingly). As a further note, if I add a testStatus.innerHTML = "...loaded metadata..."; statement in the "loadedmetadata" listener, it does get there.
var canvasContext, videoElement, intervalHandle, testStatus, trackElement;
function processFrame() {
canvasContext.drawImage(videoElement, 193, 50, 256, 194);
}
function videoEnded() {
clearInterval(intervalHandle);
testStatus.innerHTML = "video complete ... reload to try again";
}
var count = 0;
function updateSubtitle(event) {
count = count + 1;
testStatus.innerHTML = "count" + count;
}
function clickHandler(event) {
testStatus.innerHTML = "...waiting for cuechange event...";
videoElement.setAttribute("src", "start.ogg");
videoElement.load();
intervalHandle = setInterval(processFrame, 25);
videoElement.play();
}
function init() {
var canvasElement, textTrack;
canvasElement = document.createElement("canvas");
videoElement = document.createElement("video");
videoElement.addEventListener("loadedmetadata", function() {
trackElement = document.createElement("track");
trackElement.kind = "captions";
trackElement.label = "English";
trackElement.srclang = "en";
trackElement.src = "start_en.vtt";
trackElement.addEventListener("load", function() {
testStatus.innerHTML = "... loaded text track ...";
trackElement.mode = "showing";
});
videoElement.appendChild(trackElement);
trackElement.addEventListener("cuechange", updateSubtitle, false);
});
var mainDiv = document.getElementById("arghMainDiv");
canvasElement.setAttribute("id", "mediaScreen");
canvasElement.width = 640;
canvasElement.height = 480;
var firstChild;
if(mainDiv.hasChildNodes()) {
firstChild = mainDiv.firstChild;
mainDiv.insertBefore(canvasElement, firstChild);
} else {
firstChild = mainDiv.appendChild(canvasElement);
}
testStatus = document.createElement("p");
testStatus.setAttribute("id", "testStatus");
mainDiv.insertBefore(testStatus, firstChild);
testStatus.innerHTML = "click on canvas to test";
canvasContext = canvasElement.getContext('2d');
canvasElement.addEventListener("click", clickHandler);
videoElement.addEventListener("ended", videoEnded);
}
#fatalError {
color: red;
}
#arghMainDiv {
text-align: center;
}
#mediaScreen {
border: 5px solid #303030;
margin-left: auto;
margin-right: auto;
display: block;
cursor: default;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Problems With TextTrack Object</title>
<link rel="stylesheet" type="text/css" href="subargh.css">
<script src="subargh.js"></script>
</head>
<body onload="init()">
<div id="arghMainDiv">
</div>
</body>
</html>
If you want the video and WebVTT files I'm using (just a few seconds long), they are here and here (respectively). I should also mention that if I play the video using VLC, that it recognizes and plays the .vtt file as subtitles properly on the video (so it appears to be well-formed). I am running my tests on Firefox 57.0.4 (64-bit) on a Windows 7 system if that makes any difference (but I am under the impression that Firefox is mostly fixed where Timed Text Tracks are concerned now).

Why won't my webstream show?

I tried to test the code below on an android device, it didn't work (the switch button appeared but the camera output didn't.). I then decided to test it on a Mac and it worked (it just showed the camera output and the button, the button didn't do anything because there is no back camera.). Here is my code (the javascript portion of it.):
var constraints = {
audio: true,
video: {
width: 1280,
height: 720
}
};
navigator.mediaDevices.getUserMedia(constraints).then(function(mediaStream) {
var video = document.querySelector('#webcam');
video.srcObject = mediaStream;
video.onloadedmetadata = function(e) {
video.play();
};
}).catch(function(err) {
console.log(err.name + ": " + err.message);
});
var front = false;
document.getElementById('flip-button').onclick = function() {
front = !front;
};
var constraints = {
video: {
facingMode: (front ? "user" : "environment")
}
};
Here's the HTML portion of my code:
<video id="webcam">
</video>
<button Id="flip-button">switch
</button>
here's the css portion of my code:
#webcam {} #flip-button {
background-color: #202060;
height: 15%;
width: 20%;
border: none;
margin-left: 40%;
}
Thanks for your time.
You got your id wrong. Replace #webcam with #video.
Or just remove this line:
var video = document.querySelector('#webcam');
The latter works because ids are implicitly available in global scope for backwards compatibility. Some people frown on this, but it's neat for tidy fiddles.
Some beginner's advice: Always check your browser's web console for errors. Here it said:
TypeError: video is null
which was the clue that the result from querySelector was null.
PS: You also have two competing definitions of constraints, leaving your facingMode unused. Lastly, flipping front won't do much, unless front is used again, which it is not.

How can I use the YouTube API to support HTML5 and iOS devices?

I have been working with the demo provided at http://code.google.com/apis/ajax/playground/?exp=youtube#chromeless_player. What I am wondering is if there is a way to make the player HTML5 or compatible with ios devices. I can't seem to figure out how I would do that? I don't think the code works out of the box as I have tried it from my iphone and it doesn't seem to work... The biggest reason I am using this is because I need to be able to adjust volume and get the video event changes...
<!--
You are free to copy and use this sample in accordance with the terms of the
Apache license (http://www.apache.org/licenses/LICENSE-2.0.html)
-->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
<title>YouTube Player API Sample</title>
<style type="text/css">
#videoDiv {
margin-right: 3px;
}
#videoInfo {
margin-left: 3px;
}
</style>
<script src="http://www.google.com/jsapi" type="text/javascript"></script>
<script type="text/javascript">
google.load("swfobject", "2.1");
</script>
<script type="text/javascript">
/*
* Chromeless player has no controls.
*/
// Update a particular HTML element with a new value
function updateHTML(elmId, value) {
document.getElementById(elmId).innerHTML = value;
}
// This function is called when an error is thrown by the player
function onPlayerError(errorCode) {
alert("An error occured of type:" + errorCode);
}
// This function is called when the player changes state
function onPlayerStateChange(newState) {
updateHTML("playerState", newState);
}
// Display information about the current state of the player
function updatePlayerInfo() {
// Also check that at least one function exists since when IE unloads the
// page, it will destroy the SWF before clearing the interval.
if(ytplayer && ytplayer.getDuration) {
updateHTML("videoDuration", ytplayer.getDuration());
updateHTML("videoCurrentTime", ytplayer.getCurrentTime());
updateHTML("bytesTotal", ytplayer.getVideoBytesTotal());
updateHTML("startBytes", ytplayer.getVideoStartBytes());
updateHTML("bytesLoaded", ytplayer.getVideoBytesLoaded());
updateHTML("volume", ytplayer.getVolume());
}
}
// Allow the user to set the volume from 0-100
function setVideoVolume() {
var volume = parseInt(document.getElementById("volumeSetting").value);
if(isNaN(volume) || volume < 0 || volume > 100) {
alert("Please enter a valid volume between 0 and 100.");
}
else if(ytplayer){
ytplayer.setVolume(volume);
}
}
function playVideo() {
if (ytplayer) {
ytplayer.playVideo();
}
}
function pauseVideo() {
if (ytplayer) {
ytplayer.pauseVideo();
}
}
function muteVideo() {
if(ytplayer) {
ytplayer.mute();
}
}
function unMuteVideo() {
if(ytplayer) {
ytplayer.unMute();
}
}
// This function is automatically called by the player once it loads
function onYouTubePlayerReady(playerId) {
ytplayer = document.getElementById("ytPlayer");
// This causes the updatePlayerInfo function to be called every 250ms to
// get fresh data from the player
setInterval(updatePlayerInfo, 250);
updatePlayerInfo();
ytplayer.addEventListener("onStateChange", "onPlayerStateChange");
ytplayer.addEventListener("onError", "onPlayerError");
//Load an initial video into the player
ytplayer.cueVideoById("ylLzyHk54Z0");
}
// The "main method" of this sample. Called when someone clicks "Run".
function loadPlayer() {
// Lets Flash from another domain call JavaScript
var params = { allowScriptAccess: "always" };
// The element id of the Flash embed
var atts = { id: "ytPlayer" };
// All of the magic handled by SWFObject (http://code.google.com/p/swfobject/)
swfobject.embedSWF("http://www.youtube.com/apiplayer?" +
"version=3&enablejsapi=1&playerapiid=player1",
"videoDiv", "480", "295", "9", null, null, params, atts);
}
function _run() {
loadPlayer();
}
google.setOnLoadCallback(_run);
</script>
</head>
<body style="font-family: Arial;border: 0 none;">
<table>
<tr>
<td><div id="videoDiv">Loading...</div></td>
<td valign="top">
<div id="videoInfo">
<p>Player state: <span id="playerState">--</span></p>
<p>Current Time: <span id="videoCurrentTime">--:--</span> | Duration: <span id="videoDuration">--:--</span></p>
<p>Bytes Total: <span id="bytesTotal">--</span> | Start Bytes: <span id="startBytes">--</span> | Bytes Loaded: <span id="bytesLoaded">--</span></p>
<p>Controls: Play | Pause | Mute | Unmute</p>
<p><input id="volumeSetting" type="text" size="3" /> <- Set Volume | Volume: <span id="volume">--</span></p>
</div>
</td></tr>
</table>
</body>
</html>
​
As of today (2-28-2012) there is no way to play video in-line within a browser on iOS devices. You can only proide links that will either play the video in the iOS video player, or in the youtube app. The Youtube HTML5 player does this. On HTML5 enabled devices that do not support flash, it will serve an HTML5 tag.
March 7 is some big announcement for iOS, so maybe this will change then. But I do not expect it will as this is a long-standing way of operating on iOS devices.
EDIT - Here's how to do it.
To make this work for iOS, use the new HTML5 youtube player. Instructions and code found here in the YouTube API Blog:
http://apiblog.youtube.com/2010/07/new-way-to-embed-youtube-videos.html

Categories

Resources