Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposed fix for embedded video issue (#56) #66

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 6 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
<meta property="og:image"
content="https://raw.githubusercontent.com/erkserkserks/h264ify/master/icons/icon128.png"/>
<meta property="og:image" content="https://raw.githubusercontent.com/erkserkserks/h264ify/master/icons/icon128.png"/>

# h264ify


![](https://raw.githubusercontent.com/erkserkserks/h264ify/master/noncode/yt_screenshot.png)

# About
Expand All @@ -16,16 +13,13 @@ By default, YouTube streams VP8/VP9 encoded video. However, this can cause probl
In contrast, H.264 is commonly hardware accelerated by GPUs, which usually means smoother video playback and reduced CPU usage.

# Requirements
Google Chrome

Looking for the Firefox version? See: https://github.com/erkserkserks/h264ify-firefox
Google Chrome *or* Mozilla Firefox

# Installation
Install from here: https://chrome.google.com/webstore/detail/h264ify/aleakchihdccplidncghkekgioiakgal

Note: [You may need to enable GPU acceleration as well] (http://www.webupd8.org/2014/01/enable-hardware-acceleration-in-chrome.html)
- [Chrome Web Store](https://chrome.google.com/webstore/detail/h264ify/aleakchihdccplidncghkekgioiakgal)

If all goes well, when you visit https://www.youtube.com/html5, you should see this:
![](https://github.com/erkserkserks/h264ify/blob/master/noncode/html5_video_support.png)
- [Firefox Add-on](https://addons.mozilla.org/en-US/firefox/addon/h264ify/)

To check that it's working, right click on a YouTube video and select "Stats for nerds". Verify that the codec being used is neither 'vp08' nor 'vp09'.

Note: [You may need to enable GPU acceleration as well](http://www.webupd8.org/2014/01/enable-hardware-acceleration-in-chrome.html)
4 changes: 2 additions & 2 deletions manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@
"*://*.youtu.be/*"
],
"js": [
"src/inject/inject.js",
"src/inject/content_script.js"
"src/inject.js",
"src/content_script.js"
],
"run_at": "document_start",
"all_frames": true
Expand Down
2 changes: 1 addition & 1 deletion options.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@ <h1 data-l10n-id="optionsTitle"></h1>
<li><input type="checkbox" id="block_60fps" class="checkbox"><label for="block_60fps" data-l10n-id="optionsBlock60fps"></label></li>
<li><input type="checkbox" id="battery_only" class="checkbox"><label for="battery_only" data-l10n-id="optionsBatteryOnly"></label></li>
</ul>
<script src="options.js"></script>
<script src="src/options.js"></script>
</body>
</html>
42 changes: 8 additions & 34 deletions src/inject/content_script.js → src/content_script.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,39 +26,13 @@
// javascript variables on the youtube page. Thus, we have to inject another
// script into the DOM.

// Set defaults for options stored in localStorage
if (localStorage['h264ify-enable'] === undefined) {
localStorage['h264ify-enable'] = true;
}
if (localStorage['h264ify-block_60fps'] === undefined) {
localStorage['h264ify-block_60fps'] = false;
}
if (localStorage['h264ify-battery_only'] === undefined) {
localStorage['h264ify-battery_only'] = false;
}
chrome.storage.local.get({ enable: true, block_60fps: false, battery_only: false }, options => {
const optionsJson = JSON.stringify(options)

// Cache chrome.storage.local options in localStorage.
// This is needed because chrome.storage.local.get() is async and we want to
// load the injection script immediately.
// See https://bugs.chromium.org/p/chromium/issues/detail?id=54257
chrome.storage.local.get({
// Set defaults
enable: true,
block_60fps: false,
battery_only: false,
}, function(options) {
localStorage['h264ify-enable'] = options.enable;
localStorage['h264ify-block_60fps'] = options.block_60fps;
localStorage['h264ify-battery_only'] = options.battery_only;
}
);

var injectScript = document.createElement('script');
// Use textContent instead of src to run inject() synchronously
injectScript.textContent = inject.toString() + "inject();";
injectScript.onload = function() {
// Remove <script> node after injectScript runs.
this.parentNode.removeChild(this);
};
(document.head || document.documentElement).appendChild(injectScript);
const injectScript = document.createElement('script')
injectScript.textContent = `${inject.toString()}; inject(${optionsJson});` // Use textContent instead of src so it's synchronous
injectScript.onload = () => injectScript.parentNode.removeChild(injectScript) // Remove <script> node after it has run

const injectParent = document.head || document.documentElement
injectParent.appendChild(injectScript)
})
54 changes: 23 additions & 31 deletions src/inject/inject.js → src/inject.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,54 +22,46 @@
* SOFTWARE.
*/

function inject () {
if (localStorage['h264ify-enable'] === 'false') {
return;
}
function inject(options) {
if (!options.enable) return

if (localStorage['h264ify-battery_only'] === 'true' && navigator.getBattery) {
navigator.getBattery().then(function(battery) {
if (!battery.charging) {
override();
}
})
} else {
override();
}
if (options.battery_only && navigator.getBattery) {
navigator.getBattery().then(battery => battery.charging || override())
} else override()

function override() {
// Override video element canPlayType() function
var videoElem = document.createElement('video');
var origCanPlayType = videoElem.canPlayType.bind(videoElem);
videoElem.__proto__.canPlayType = makeModifiedTypeChecker(origCanPlayType);
const videoElem = document.createElement('video')
const origCanPlayType = videoElem.canPlayType.bind(videoElem)
videoElem.__proto__.canPlayType = makeModifiedTypeChecker(origCanPlayType)

// Override media source extension isTypeSupported() function
var mse = window.MediaSource;
// Check for MSE support before use
if (mse === undefined) return;
var origIsTypeSupported = mse.isTypeSupported.bind(mse);
mse.isTypeSupported = makeModifiedTypeChecker(origIsTypeSupported);
if (mse = window.MediaSource) {
const origIsTypeSupported = mse.isTypeSupported.bind(mse)
mse.isTypeSupported = makeModifiedTypeChecker(origIsTypeSupported)
}
}

// return a custom MIME type checker that can defer to the original function
function makeModifiedTypeChecker(origChecker) {
// Check if a video type is allowed
return function (type) {
if (type === undefined) return '';
var disallowed_types = ['webm', 'vp8', 'vp9', 'av01'];
return function(type) {
if (type === undefined) return ''

const disallowed_types = ['webm', 'vp8', 'vp9', 'av01']

// If video type is in disallowed_types, say we don't support them
for (var i = 0; i < disallowed_types.length; i++) {
if (type.indexOf(disallowed_types[i]) !== -1) return '';
if (type.indexOf(disallowed_types[i]) !== -1) return ''
}

if (localStorage['h264ify-block_60fps'] === 'true') {
var match = /framerate=(\d+)/.exec(type);
if (match && match[1] > 30) return '';
if (options.block_60fps) {
const match = /framerate=(\d+)/.exec(type)
if (match && match[1] > 30) return ''
}

// Otherwise, ask the browser
return origChecker(type);
};
return origChecker(type)
}
}
}

28 changes: 12 additions & 16 deletions options.js → src/options.js
Original file line number Diff line number Diff line change
@@ -1,39 +1,35 @@
// Saves options to chrome.storage
function save_options() {
var enable = document.getElementById('enable').checked;
var block_60fps = document.getElementById('block_60fps').checked;
var battery_only = document.getElementById('battery_only').checked;
const enable = document.getElementById('enable').checked
const block_60fps = document.getElementById('block_60fps').checked
const battery_only = document.getElementById('battery_only').checked
chrome.storage.local.set({
enable: enable,
block_60fps: block_60fps,
battery_only: battery_only,
});
})
}

// Restores checkbox state using the options stored in chrome.storage.
function restore_options() {
// Use default value enable = true and block_60fps = false
chrome.storage.local.get({
enable: true,
block_60fps: false,
battery_only: false,
}, function(options) {
document.getElementById('enable').checked = options.enable;
document.getElementById('block_60fps').checked = options.block_60fps;
document.getElementById('battery_only').checked = options.battery_only;
});
chrome.storage.local.get({ enable: true, block_60fps: false, battery_only: false, }, options => {
document.getElementById('enable').checked = options.enable
document.getElementById('block_60fps').checked = options.block_60fps
document.getElementById('battery_only').checked = options.battery_only
})
}

// Restore saved options when extension is loaded
document.addEventListener('DOMContentLoaded', restore_options);
document.addEventListener('DOMContentLoaded', restore_options)

// Save options when checkboxes are clicked
var checkboxes = document.getElementsByClassName('checkbox');
const checkboxes = document.getElementsByClassName('checkbox')
for (var i = 0; i < checkboxes.length; i++) {
checkboxes[i].addEventListener('click', save_options)
}

// l10n
for (let element of document.querySelectorAll('[data-l10n-id]')) {
element.textContent = chrome.i18n.getMessage(element.dataset.l10nId);
element.textContent = chrome.i18n.getMessage(element.dataset.l10nId)
}