├── README.md ├── index.html ├── js ├── inline-video.js └── requestAnimationFrame.js └── video ├── buck.mp4 ├── buck.ogv ├── buck.webm ├── inertia.mp4 └── inertia.webm /README.md: -------------------------------------------------------------------------------- 1 | ### UPDATE: This library is DEPRECATED. The [new policies](https://webkit.org/blog/6784/new-video-policies-for-ios/) for iOS 10, make this repo [thankfully) obsolete. If you need something this week, take a look at what [these guys](https://medium.com/this-also/whitewater-9b47f1e32ffe#.tzy7ko5su) did. It's a pretty good idea, though they didn't come up with it ;) 2 | 3 | ## Synopsis 4 | 5 | InlineVideo.js is intended to allow users on an iphone to play videos inline in the safari browser, circumventing the default fullscreen behavior. 6 | 7 | [DEMO](http://codepen.io/newshorts/pen/yNxNKR) 8 | ---- 9 | 10 | ## Code Example 11 | 12 | When you embed a video element in your page be sure to include the "playsinline" attribute. Then add a source attribute with an mp3 of the audio in your video. 13 | 14 | InlineVideo.js will automatically detect video elements with this attribute and replace them with the InlineVideo.js player if your users are on an iPhone. COMING SOON: It will also play the audio track along with the video frames. To the user, the experience will be an inline video. 15 | 16 | ```html 17 | 20 | ``` 21 | 22 | InlineVideo.js works by detecting videos that should play inline for users on an iPhone, then transforming the video elements into canvas elements. InlineVideo.js draws each frame of the video to this canvas element instead of playing the video, that way IOS never tries to go into "full screen native player mode." 23 | 24 | ## Motivation 25 | 26 | I believe developers should have to freedom to choose if their videos are meant to play inline or not. I understand the reasoning behind Apple's choices for the iphone, but sometimes a design or interactive experience calls for an inline video and iPhone users shouldn't be left out. Also, with the introduction of IOS 8 and webGL enabled by default, you would think we'd be able to use video textures... 27 | 28 | ## Installation 29 | 30 | Make sure you include jQuery, a request animationframe poly and the InlineVideo.js file in your scripts: 31 | 32 | ```html 33 | 34 | 35 | 36 | ``` 37 | 38 | ## API Reference 39 | 40 | Depending on the size of the project, if it is small and simple enough the reference docs can be added to the README. For medium size to larger projects it is important to at least provide a link to where the API reference docs live. 41 | 42 | ## Tests 43 | 44 | Describe and show how to run the tests with code examples. 45 | 46 | ## Contributors 47 | 48 | Contributors are always welcome. This library is nowhere near finished, I'd love the help of the community to make this video player a reality. 49 | 50 | There's some important conversation happening here: [Chromium developers forum](https://code.google.com/p/chromium/issues/detail?id=395206) 51 | 52 | Also there are a couple of example uses of inline video being used in the wild for webgl textures (however, these sources are not using this library): 53 | 54 | [KRPano Cloud Demo](http://krpano.com/krpanocloud/video/airpano/index.html) 55 | 56 | [takeyourdose.com](http://www.takeyourdose.com/en) 57 | 58 | If you'd like to get involved message me, submit a pull request or issue. 59 | 60 | ## TODO 61 | 62 | There's still much work to be done: 63 | 64 | * automatically detect and play audio along with the video 65 | * give a 'production ready' style to the player interface 66 | * refactoring and reducing dependecies on libraries like jquery and requestAnimationFrame 67 | 68 | ## License 69 | 70 | The MIT License (MIT) 71 | 72 | Copyright (c) 2015 Mike Newell 73 | 74 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 75 | 76 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 77 | 78 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | TODO supply a title 10 | 11 | 12 | 76 | 77 | 78 | 79 | 93 | 100 | 101 | 102 | 103 |
104 | 107 |
108 | 109 | 110 | -------------------------------------------------------------------------------- /js/inline-video.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Inline Video Player v0.0.1 3 | * http://iwearshorts.com/ 4 | * 5 | * Includes jQuery js 6 | * https://jquery.com/ 7 | * 8 | * Copyright 2015 Mike Newell 9 | * Released under the MIT license 10 | * https://tldrlegal.com/license/mit-license 11 | * 12 | * Date: 2015-18-07 13 | * 14 | * TODO: look for the webkit-playsinline playsinline attributes and replace videos on iphones with canvas 15 | * 16 | */ 17 | 18 | 'use strict'; 19 | 20 | var InlineVideo = function(elem) { 21 | 22 | // global 23 | var __ = this; 24 | var lastTime; 25 | 26 | // private 27 | (function init() { 28 | // set self 29 | __ = elem; 30 | 31 | // grab the poster and replace the background of the inlineVideo element 32 | 33 | __.width = $(elem).width(); 34 | __.height = $(elem).height(); 35 | 36 | var poster = $(elem).attr('poster'); 37 | __.posterLink = document.createElement('a'); 38 | $(__.posterLink).addClass('inlineVideoPoster').html(''); 39 | 40 | // replace the video with inlineVideo element 41 | var inlineVideo = document.createElement('inlineVideo'); 42 | $(inlineVideo).addClass('inlineVideoContainer').html(__.posterLink); 43 | $(elem).replaceWith(inlineVideo); 44 | __.container = $(inlineVideo); 45 | 46 | // set canvas 47 | __.canvasElem = document.createElement('canvas'); 48 | $(__.canvasElem).addClass('inlineVideoPlayer'); 49 | __.canvasElemCtx = __.canvasElem.getContext('2d'); 50 | 51 | // controls 52 | __.playButton = document.createElement('button'); 53 | $(__.playButton).text('play').addClass('inlineVideoPlayButton'); 54 | 55 | __.pauseButton = document.createElement('button'); 56 | $(__.pauseButton).text('pause').addClass('inlineVideoPauseButton'); 57 | 58 | })(); 59 | 60 | function handleOnLoadedMetaData() { 61 | console.log(__.duration); 62 | 63 | // resize & inject our video player into the dom 64 | var player = [__.canvasElem, __.playButton, __.pauseButton]; 65 | __.container.width(__.width).height(__.height).append(player); 66 | 67 | console.log("Start: " + __.seekable.start(0) + " End: " + __.seekable.end(0)); 68 | } 69 | 70 | function handleProgress() { 71 | var end = __.buffered.end(0); 72 | var sofar = parseInt(((end / __.duration) * 100)); 73 | console.log(sofar + '%'); 74 | } 75 | 76 | function handlePlayButtonClicked() { 77 | lastTime = Date.now(); 78 | __.animationRequest = requestAnimationFrame(loop); 79 | } 80 | 81 | function handlePauseButtonClicked() { 82 | cancelAnimationFrame(__.animationRequest); 83 | __.animationRequest = null; 84 | } 85 | 86 | function handlePosterLinkClicked() { 87 | __.load(); 88 | console.log('handlePosterLinkClicked clicked') 89 | } 90 | 91 | function renderFrame(elapsed) { 92 | __.currentTime = __.currentTime + elapsed; 93 | __.canvasElemCtx.drawImage(__, 0, 0, __.videoWidth, __.videoHeight); 94 | } 95 | 96 | function loop() { 97 | var time = Date.now(); 98 | var elapsed = (time - lastTime) / 1000; 99 | 100 | // render 101 | if(elapsed >= ((1000/25)/1000)) { 102 | renderFrame(elapsed); 103 | lastTime = time; 104 | } 105 | 106 | // if we are at the end of the video stop 107 | var currentTime = (Math.round(parseFloat(__.currentTime)*10000)/10000); 108 | var duration = (Math.round(parseFloat(__.duration)*10000)/10000); 109 | if(currentTime >= duration) { 110 | console.log('currentTime: ' + currentTime + ' duration: ' + duration); 111 | return; 112 | } 113 | 114 | __.animationRequest = requestAnimationFrame(loop); 115 | } 116 | 117 | // public 118 | 119 | 120 | // events 121 | __.onloadstart = function() { console.log(arguments); }; // fires when the loading starts 122 | __.onloadedmetadata = function() { console.log(arguments); handleOnLoadedMetaData(arguments); }; // when we have metadata about the video 123 | __.onloadeddata = function() { console.log(arguments); }; // when we have the first frame 124 | __.onprogress = function() { handleProgress(arguments); }; 125 | $(__.playButton).on('click', handlePlayButtonClicked); 126 | $(__.pauseButton).on('click', handlePauseButtonClicked); 127 | $(__.posterLink).on('click touchstart', handlePosterLinkClicked); 128 | 129 | return __; 130 | }; 131 | 132 | (function($) { 133 | $(window).load(function() { 134 | var iOS = /iPhone|iPod/.test( navigator.userAgent ); 135 | if(iOS || debug) { 136 | 137 | setTimeout(function() { 138 | var videos = $('video[playsinline], video[webkit-playsinline]'); 139 | var inlineVideos = []; 140 | 141 | for(var i = 0, len = videos.length; i < len; i++) { 142 | var video = new InlineVideo(videos[i]); 143 | inlineVideos.push(video); 144 | } 145 | 146 | console.log('inline videos:'); 147 | console.dir(inlineVideos); 148 | }, 1000); 149 | 150 | 151 | } 152 | }); 153 | })(jQuery); -------------------------------------------------------------------------------- /js/requestAnimationFrame.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Provides requestAnimationFrame/cancelRequestAnimation in a cross browser way. 3 | * from paul irish + jerome etienne 4 | * - http://paulirish.com/2011/requestanimationframe-for-smart-animating/ 5 | * - http://notes.jetienne.com/2011/05/18/cancelRequestAnimFrame-for-paul-irish-requestAnimFrame.html 6 | */ 7 | 8 | if ( !window.requestAnimationFrame ) { 9 | 10 | window.requestAnimationFrame = ( function() { 11 | 12 | return window.webkitRequestAnimationFrame || 13 | window.mozRequestAnimationFrame || 14 | window.oRequestAnimationFrame || 15 | window.msRequestAnimationFrame || 16 | function( /* function FrameRequestCallback */ callback, /* DOMElement Element */ element ) { 17 | 18 | return window.setTimeout( callback, 1000 / 60 ); 19 | 20 | }; 21 | 22 | } )(); 23 | 24 | } 25 | 26 | if ( !window.cancelRequestAnimationFrame ) { 27 | 28 | window.cancelRequestAnimationFrame = ( function() { 29 | 30 | return window.webkitCancelRequestAnimationFrame || 31 | window.mozCancelRequestAnimationFrame || 32 | window.oCancelRequestAnimationFrame || 33 | window.msCancelRequestAnimationFrame || 34 | clearTimeout 35 | 36 | } )(); 37 | 38 | } 39 | -------------------------------------------------------------------------------- /video/buck.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/newshorts/InlineVideo/757b6f1ab2d15b8af12aa5eb208a818696e03adc/video/buck.mp4 -------------------------------------------------------------------------------- /video/buck.ogv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/newshorts/InlineVideo/757b6f1ab2d15b8af12aa5eb208a818696e03adc/video/buck.ogv -------------------------------------------------------------------------------- /video/buck.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/newshorts/InlineVideo/757b6f1ab2d15b8af12aa5eb208a818696e03adc/video/buck.webm -------------------------------------------------------------------------------- /video/inertia.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/newshorts/InlineVideo/757b6f1ab2d15b8af12aa5eb208a818696e03adc/video/inertia.mp4 -------------------------------------------------------------------------------- /video/inertia.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/newshorts/InlineVideo/757b6f1ab2d15b8af12aa5eb208a818696e03adc/video/inertia.webm --------------------------------------------------------------------------------