I am using VideoJs player 4.2
When i open a videojs player in safari browser in ios device , it works on native controls by default . When i pause the player, i display overlay buttons (links to go for other page) on video . If i click buttons it does not fire in ios . It works well in desktop browsers .
If i set nativeControlsForTouch to false it displays custom controls . Here overlay buttons and custom controls works but it will be sluggish and i am not able scroll over video and it will be not smooth as compared with native controls .
Please suggest me any idea
If i use native controls , then overlay buttons has to work
If i use custom controls , then video player will be as smooth as compared with native controls
Here is my code
videojs("ex_video",{"controls":true, "preload":"metadata","width":"auto", "height":"auto",
"poster":"/images/test.png"},function(){
var current_player=this;
current_player.imageOverlay({
opacity: 0.5,
height: '100%',
width: '50%'
});
current_player.on("pause", function(){
current_player.bigPlayButton.show();
});
current_player.on("play", function(){
current_player.bigPlayButton.hide();
});
});
overlay button code
(function(vjs) {
var
extend = function(obj) {
var arg, i, k;
for (i = 1; i < arguments.length; i++) {
arg = arguments[i];
for (k in arg) {
if (arg.hasOwnProperty(k)) {
obj[k] = arg[k];
}
}
}
return obj;
},
defaults = {
image_url: '',
click_url: '',
start_time: null,
end_time: null,
opacity: 0.7,
height: '15%',
width: '100%'
},
imageOverlay = function(options) {
var player = this,
settings = extend({}, defaults, options || {}),
showingImage = false;
if (settings.start_time === null)
settings.start_time = 0;
if (settings.end_time === null)
settings.end_time = player.duration() + 1;
overlay = {
showImage: function() {
if (showingImage) {
return;
}
showingImage = true;
var holderDiv = document.createElement('div');
holderDiv.id = 'vjs-image-overlay-holder';
holderDiv.style.height = settings.height;
holderDiv.style.width = settings.width;
holderDiv.style.opacity=settings.opacity;
var overlayLbl1= document.createElement('label');
overlayLbl1.setAttribute('class','heading-overlay-video');
overlayLbl1.innerHTML= "Like this class? Why not:";
var topVideoP=overlay.createEl(overlayLbl1);
topVideoP.className=topVideoP.className+' top-video-control-overlay';
holderDiv.appendChild(topVideoP);
//Save to queue
var overlayBtn1=document.createElement('button');
overlayBtn1.value = 'Save to queue';
overlayBtn1.innerHTML='Save to queue';
overlayBtn1.setAttribute('class','white medium');
holderDiv.appendChild(overlay.createEl(overlayBtn1));
//Add to series button
var overlayBtn2=document.createElement('button');
overlayBtn2.value = 'Add to series';
overlayBtn2.innerHTML='Add to series';
overlayBtn2.setAttribute('class','white medium');
holderDiv.appendChild(overlay.createEl(overlayBtn2));
var overlayLbl2= document.createElement('label');
overlayLbl2.innerHTML= "music credits";
overlayLbl2.setAttribute('class','msg-blue');
var btmVideoP=overlay.createEl(overlayLbl2);
btmVideoP.className=btmVideoP.className+' bottom-video-control';
holderDiv.appendChild(btmVideoP);
player.el().appendChild(holderDiv);
},
hideImage: function() {
if (!showingImage) {
return;
}
showingImage = false;
player.el().removeChild(document.getElementById('vjs-image-overlay-holder'));
},
createEl:function(ele_obj){
var overlayP=document.createElement('p');
overlayP.setAttribute('class','overlay-video-content-in');
overlayP.appendChild(ele_obj);
return overlayP ;
}
};
var setTimeoutControl;
player.on('pause', function(){
setTimeoutControl= setTimeout(function(){overlay.showImage()},5000);
});
player.on('play', function(){
clearTimeout(setTimeoutControl);
overlay.hideImage();
});
};
vjs.plugin('imageOverlay', imageOverlay);
}(window.videojs));
By default HTML5 video in browser will open in native player in iPod,IPhone.
You have to wrap the HTML content in webview and run it like a native application to view video in iPod,Iphone.
See this HTML5 inline video on iPhone vs iPad/Browser
So these two changes has to be done HTML
<video id="xxx" width="xx" height="xx" webkit-playsinline>
Obj-C
webview.allowsInlineMediaPlayback = YES;
In all other devices it will work fine with out these
It is better to use HammerJS(https://github.com/hammerjs/hammer.js). For EmberJS we have an add-on https://github.com/runspired/ember-gestures
Related
I want to generate video thumbnail and preview it while hovering on the progress bar like YouTube video:
I've tried to test with videojs-thumbnails but failed. The README file doesn't contain enough information to fix it.
I've also tried to search on Google with keyword: video thumbnail progress bar. There are some related questions on SO but I can't find the solution for this case.
I found a javascript library videojs which contains event hovering on progress bar:
videojs('video').ready(function () {
$(document).on('mousemove', '.vjs-progress-control', function() {
// How can I generate video thumbnails and preview them here?
});
});
Currently (Dec. 2019), there are not so much javascript (both free version and paid version) library which supports adding thumbnail while hovering on video progress bar.
But you can follow on the road of videojs. They've already supported adding tooltip while hovering on video progress bar. Everything else you can do is Generating video thumbnails and adding them into the control bar for previewing.
In this example, we will explain about how to generate video thumbnail from <input type="file" />. Athough we can use video source with a directly link, in testing period, we have some problem with Tainted canvases may not be exported because of using canvas.toDataURL()
After videojs completes initializing, you can clone a new video from the source and append it to the body. Just to play and catch loadeddata event:
videojs('video').ready(function () {
var that = this;
var videoSource = this.player_.children_[0];
var video = $(videoSource).clone().css('display', 'none').appendTo('body')[0];
video.addEventListener('loadeddata', async function() {
// asynchronous code...
});
video.play();
});
Like YouTube video thumbnail, we will generate thumbnail file as an image. This image has a size:
(horizontalItemCount*thumbnailWidth)x(verticalItemCount*thumbnailHeight) = (5*158)x(5*90)
So 790x450 is the size of the image which contains 25 sub-thumbnails (YouTube uses 158 as the width and 90 as the height of the thumbnail). Like this:
Then, we will take video snapshot based on video duration. In this example, we generate thumbnail per second (each second has a thumbnail).
Because generating video thumbnail needs a long time based on video duration and quality, so we can make a default thumbnail with a dark theme for waiting.
.vjs-control-bar .vjs-thumbnail {
position: absolute;
width: 158px;
height: 90px;
top: -100px;
background-color: #000;
display: none;
}
After getting video duration:
var duration = parseInt(that.duration());
we need to parse it to an int before using in the loop because the value may be 14.036.
Everything else is: Setting the currentTime value of the new video and converting the video to canvas.
Because 1 canvas element can contains maximum 25 thumbnails by default, we have to add 25 thumbnails to the canvas one-by-one (from left to right, from top to bottom). Then we store it in an array.
If there is still another thumbnail, we create another canvas and repeat the action
var thumbnails = [];
var thumbnailWidth = 158;
var thumbnailHeight = 90;
var horizontalItemCount = 5;
var verticalItemCount = 5;
var init = function () {
videojs('video').ready(function() {
var that = this;
var videoSource = this.player_.children_[0];
var video = $(videoSource).clone().css('display', 'none').appendTo('body')[0];
// videojs element
var root = $(videoSource).closest('.video-js');
// control bar element
var controlBar = root.find('.vjs-control-bar');
// thumbnail element
controlBar.append('<div class="vjs-thumbnail"></div>');
//
controlBar.on('mousemove', '.vjs-progress-control', function() {
// getting time
var time = $(this).find('.vjs-mouse-display .vjs-time-tooltip').text();
//
var temp = null;
// format: 09
if (/^\d+$/.test(time)) {
// re-format to: 0:0:09
time = '0:0:' + time;
}
// format: 1:09
else if (/^\d+:\d+$/.test(time)) {
// re-format to: 0:1:09
time = '0:' + time;
}
//
temp = time.split(':');
// calculating to get seconds
time = (+temp[0]) * 60 * 60 + (+temp[1]) * 60 + (+temp[2]);
//
for (var item of thumbnails) {
//
var data = item.sec.find(x => x.index === time);
// thumbnail found
if (data) {
// getting mouse position based on "vjs-mouse-display" element
var position = controlBar.find('.vjs-mouse-display').position();
// updating thumbnail css
controlBar.find('.vjs-thumbnail').css({
'background-image': 'url(' + item.data + ')',
'background-position-x': data.backgroundPositionX,
'background-position-y': data.backgroundPositionY,
'left': position.left + 10,
'display': 'block'
});
// exit
return;
}
}
});
// mouse leaving the control bar
controlBar.on('mouseout', '.vjs-progress-control', function() {
// hidding thumbnail
controlBar.find('.vjs-thumbnail').css('display', 'none');
});
video.addEventListener('loadeddata', async function() {
//
video.pause();
//
var count = 1;
//
var id = 1;
//
var x = 0, y = 0;
//
var array = [];
//
var duration = parseInt(that.duration());
//
for (var i = 1; i <= duration; i++) {
array.push(i);
}
//
var canvas;
//
var i, j;
for (i = 0, j = array.length; i < j; i += horizontalItemCount) {
//
for (var startIndex of array.slice(i, i + horizontalItemCount)) {
//
var backgroundPositionX = x * thumbnailWidth;
//
var backgroundPositionY = y * thumbnailHeight;
//
var item = thumbnails.find(x => x.id === id);
if (!item) {
//
//
canvas = document.createElement('canvas');
//
canvas.width = thumbnailWidth * horizontalItemCount;
canvas.height = thumbnailHeight * verticalItemCount;
//
thumbnails.push({
id: id,
canvas: canvas,
sec: [{
index: startIndex,
backgroundPositionX: -backgroundPositionX,
backgroundPositionY: -backgroundPositionY
}]
});
} else {
//
//
canvas = item.canvas;
//
item.sec.push({
index: startIndex,
backgroundPositionX: -backgroundPositionX,
backgroundPositionY: -backgroundPositionY
});
}
//
var context = canvas.getContext('2d');
//
video.currentTime = startIndex;
//
await new Promise(function(resolve) {
var event = function() {
//
context.drawImage(video, backgroundPositionX, backgroundPositionY,
thumbnailWidth, thumbnailHeight);
//
x++;
// removing duplicate events
video.removeEventListener('canplay', event);
//
resolve();
};
//
video.addEventListener('canplay', event);
});
// 1 thumbnail is generated completely
count++;
}
// reset x coordinate
x = 0;
// increase y coordinate
y++;
// checking for overflow
if (count > horizontalItemCount * verticalItemCount) {
//
count = 1;
//
x = 0;
//
y = 0;
//
id++;
}
}
// looping through thumbnail list to update thumbnail
thumbnails.forEach(function(item) {
// converting canvas to blob to get short url
item.canvas.toBlob(blob => item.data = URL.createObjectURL(blob), 'image/jpeg');
// deleting unused property
delete item.canvas;
});
console.log('done...');
});
// playing video to hit "loadeddata" event
video.play();
});
};
$('[type=file]').on('change', function() {
var file = this.files[0];
$('video source').prop('src', URL.createObjectURL(file));
init();
});
.vjs-control-bar .vjs-thumbnail {
position: absolute;
width: 158px;
height: 90px;
top: -100px;
background-color: #000;
display: none;
}
<link rel="stylesheet" href="https://vjs.zencdn.net/7.5.5/video-js.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<script src="https://vjs.zencdn.net/7.5.5/video.js"></script>
<input type="file" accept=".mp4" />
<video id="video" class="video-js vjs-default-skin" width="500" height="250" controls>
<source src="" type='video/mp4'>
</video>
Fiddle
I struggled with this exact issue, about a year ago. Based on the thread here: How to generate video preview thumbnails for use in VideoJS? I finally concluded to go with offline generation of thumbnails, as it's much easier than trying to extract them on-the-fly.
I did a technical discussion/explanation of that struggle here: http://weasel.firmfriends.us/GeeksHomePages/subj-video-and-audio.html#implementing-video-thumbnails
My prototype example is here: https://weasel.firmfriends.us/Private3-BB/
EDIT:Also, I couldn't solve how to bind to the existing seekBar in video-js, so I added my own dedicated slider to view the thumbnails. That decision was mostly based on the need to use 'hover'/'onMouseOver' if one wants to use video-js's seekbar, and those gestures don't translate well to touch-screens (mobile devices).
EDIT: I've now solved the issue of how to bind the existing seekBar, so
I've added that logic to my prototype example mentioned above.
Cheers. Hope this helps.
For anyone whom is looking for an Angular solution. I have published a npm package for you to create a thumbnail snapshot when video progress bar is on hover.
npm: ngx-thumbnail-video
You can install it by
npm i ngx-thumbnail-video
and include it into your module:
import { NgxThumbnailVideoModule } from 'ngx-thumbnail-video';
#NgModule({
imports: [NgxThumbnailVideoModule]
})
And use it in this way:
<ngx-thumbnail-video url="assets/video.mp4" [options]="options"></ngx-thumbnail-video>
What it looks like:
When I am on my site and I use the middle mouse button to load the page in a new tab, it renders the page as if it was on mobile but it is actually on desktop. It only recently started doing this on Chrome after an update.
As the site was done before I joined they did not use media query's like they should have, but actually used a mobile CSS file and a desktop CSS file. Depending on the size of the screen it loads the one it needs.
What I think is happening is that when the page loads in a new tab it loads it in a smaller screen, making my script believe it is on mobile but actually it is on Desktop.
jQuery:
function ResolutionSwitcher(config) {
this.cutoff = document.cutoff || 980;
this.$head = $(document.querySelector("head"));
$.extend(this, config);
}
ResolutionSwitcher.prototype.appendStyle = function(url) {
this.$head.append($("<link/>", {
type: "text/css",
rel: "stylesheet",
href: url
}));
};
ResolutionSwitcher.prototype.removeStyle = function(url) {
this.$head.find("link[href$='" + url + "']").remove();
};
ResolutionSwitcher.prototype.onDesktop = function() {
this.appendStyle("/static/public/css/desktop.v2.css");
this.removeStyle("mobile.css");
DesktopLayout.init();
};
ResolutionSwitcher.prototype.onMobile = function() {
this.appendStyle("/static/public/css/mobile.v2.css");
this.removeStyle("desktop.v2.css");
MobileLayout.init();
};
ResolutionSwitcher.prototype.checkResolution = function() {
var desktop_cutoff = 460;
// if screen is smaller than cutoff (mobile)
if(this.cutoff >= screen.width) {
this.onMobile();
window.site_version = 'strict_mobile';
}
// if screen is bigger than cutoff but window is smaller than cutoff and version is not already mobile
else if(desktop_cutoff >= window.outerWidth && window.site_version != 'mobile') {
this.onMobile();
window.site_version = 'mobile';
// navScrolling('#secondList');
// navScrolling('#thirdList');
navScrolling();
}
// if screen and window are both wider than cutoff and version is not already desktop
else if(desktop_cutoff < window.outerWidth && window.site_version != 'desktop') {
this.onDesktop();
window.site_version = 'desktop';
// navScrolling('#secondList');
// navScrolling('#thirdList');
navScrolling();
}
};
return ResolutionSwitcher;
I am trying to build a web interface for Subsonic and was trying to switch its default implementation of JWPlayer to Flowplayer.
I tried the default method of loading the video in both JWPlayer and Flowplayer and neither works, so I looked at the existing interface which used the below relevant code:
var player;
var position;
var maxBitRate = 1000;
var timeOffset = 0;
function init() {
var flashvars = {
id:"player1",
skin:"flash/whotube.zip",
screencolor:"000000",
controlbar:"over",
autostart:"false",
bufferlength:3,
backcolor:"EFEFEF",
frontcolor:"000000",
provider:"video"
};
var params = {
allowfullscreen:"true",
allowscriptaccess:"always"
};
var attributes = {
id:"player1",
name:"player1"
};
var width = "100%";
var height = "85%";
swfobject.embedSWF("flash/jw-player-5.6.swf", "placeholder1", width, height, "9.0.0", false, flashvars, params, attributes);
}
function playerReady(thePlayer) {
player = $("player1");
player.addModelListener("TIME", "timeListener");
play();
}
function play() {
var list = new Array();
list[0] = {
file:"http://domain.com:4040/rest/stream.view?u=username&p=password&v=1.6.0&c=appname&id=number",
duration:9999 - timeOffset, //testing value for duration
provider:"video"
};
player.sendEvent("LOAD", list);
player.sendEvent("PLAY");
}
I can't even same to make anything run under flowplayer. Any ideas? I am stuck at:
Getting something equivalent to playerReady();
Getting the video to load using:
flowplayer.addClip({'url':videoURL},0);
flowplayer.play({'url':videoURL},0);
Which gives me: has no method 'addClip' and has no method 'play'
My JS include are for both FlowPlayer and JWPlayer:
flowplayer-3.2.11.min.js
Jquery
Prototype
swfobject
and my own JS file from which above is a snippet.
Help would be very much appreciated.
Thanks,
flowplayer("player", "flowplayer-3.2.15.swf", {
clip: {
scaling: 'orig'
},
plugins: {
controls: null
},
// player events are defined directly to "root" (not inside a clip)
onLoad: function() {
$f().play({url: "http://pseudo01.hddn.com/vod/demo.flowplayervod/Extremists.flv"});
}
});
Ok so i'm learning to write plugins and the jQuery site for plugin authoring is damn confusing. I'm really trying to figure out how to add methods to my plugin that will fire based on a browser event. So basically I have a plugin that builds out a swipeable slide show for a mobile device. When the device is rotated I need it to rebuild the slideshow for the new dimensions. Right now I have it so it builds it the first time but I can't (a) figure out to add methods for different functions and (b) bind events to those specific methods. here's my code.
(function( $ ){
$.fn.jCarousel = function( options ) {
var empty = {}
var defaults = {
height: $(window).height(),
width: $(window).width(),
count: 1,
amount: $('.swiper', this).length,
thisone:this,
};
var options = $.extend(empty, defaults, options);
return this.each(function() {
options.thisone = this;
amount = $('.swiper', this).length;
holdWidth = options.width * options.amount;
$('.swiper', this).height(options.height);
$(this).height(options.height);
$('.swiper', this).width(options.width);
$(this).width(holdWidth);
$('.swipe_slide',this).width(holdWidth);
//==================================================================================================================
//==================================================================================================================
$('.swiper', this).each(function(i) {
var nwidth = options.width*i;
$(this).css('left',nwidth);
});
//==================================================================================================================
//==================================================================================================================
//==================================================================================================================
//==================================================================================================================
$('.swipe_slide', this).swipe(function(evt, data) {
if(data.direction=='left'){
//alert('count: '+options.count+" amount: "+options.amount);
if(options.count != options.amount){
moveWidth = options.count * -options.width;
$('.swipe_slide',options.thisone).css( "left" ,moveWidth );
options.count++
}else{
return
}
}else{
if(options.count!=1){
moveWidth = moveWidth+ options.width;
$('.swipe_slide',options.thisone).css( "left" ,moveWidth );
options.count--
}
else{
return
}
}
});
//==================================================================================================================
//==================================================================================================================
});
};
})( jQuery );
$(window).bind('orientationchange', $.fn.jCarousel);
You'll have to refactor your plugin a bit so that the function stores the options that are passed into it, otherwise running the function again on orientation change will reset the options to the defaults.
I am trying to load images throught javascript. Different browser than IE8 or ie9 it's working fine, but in IE doesn't. If I load the image directly
http://localhost/_eWar/index.php?road=map&createimage=true
it's working fine, but trought javascript
src="index.php?road=map&createimage=true";
this.img_object.object = $("<img>");
this.img_object.object.unbind('load').
removeAttr("src").
removeAttr("width").
removeAttr("height").
attr("class","map newMap").
attr("usemap","#world").
css({ top: 0, left: 0 }).
load(function(){
me.image_loaded = true;
me.img_object.display_width = me.img_object.orig_width = this.width;
me.img_object.display_height = me.img_object.orig_height = this.height;
if(!me.container.hasClass("iviewer_cursor")){
me.container.addClass("iviewer_cursor");
}
if(me.settings.zoom == "fit"){
me.fit();
}
else {
me.set_zoom(me.settings.zoom);
}
if(me.settings.onFinishLoad)
{
me.settings.onFinishLoad.call(me);
}
//src attribute is after setting load event, or it won't work
}).attr("src",src);
I get a "compatible view" message.
I'm not sure why you're resetting and looking at attributes on a new image.
Here's an easy way to load images:
function loadSample() {
var i = new Image()
i.onload = function() {
alert("loaded")
}
i.src = "http://jsfiddle.net/img/logo.png"
}