ブラウザでビデオをストレッチ





多くの場合、オンライン映画館のビデオのアスペクト比はモニターのアスペクト比とは異なります。そのため、端を少しトリミングして全体のスケールを少し大きくしたい場合があります。または、画像を画像の小さい側の画面サイズに合わせます。これは、小さな画面だけでなく、古い4:3モニターにも特に当てはまります。元のビデオは一般的に片側に引き伸ばすことができ、これを何らかの方法で修正する必要があるという事実については沈黙しています。



この問題を解決するために、ChromeとFirefox用のブラウザ拡張機能を作成することにしました。アイデアは次のとおりです。ブラウザのビデオを再生すると、画面上のメニューが呼び出され、画像のスケールとアスペクト比を任意に変更できます。



iframe



私が遭遇した最初の問題は、Webサイトのビデオが必ずしもメインページに配置されているとは限らず、ネストされたiframeの奥深くに隠されている可能性があることです。すべてのiframeをスキャンして、それぞれのすべてのビデオ要素を見つけることにしました。ちなみに、これは別の問題も解決します-広告ビデオがどこにあるのか、そして映画自体がどこにあるのかはわかりません。それらすべてを最初に見つけましょう。



getVideos関数は、すべてのビデオ要素が最後のiframeで見つかるまで、それ自体を再帰的に呼び出します。すべてのビデオがap_ext_space.videos配列に追加されます。getVideos関数は、現在のページのドキュメントを入力パラメーターとして受け取ります。最初の起動時に、メインドキュメントが取得されます。途中で、ハンドラーは各ビデオにハングアップしますが、それについては以下で詳しく説明します。



getVideos: function (srcDoc) {
	if (!srcDoc) {
		srcDoc = document;
		window.onkeydown = function (event) {
			var e = event || window.event;
			ap_ext_space.keyDn(e);
		};
	};

	var els = srcDoc.getElementsByTagName('video');
	for (var i = 0; i < els.length; i++) {
		els[i].addEventListener("seeked", function () {ap_ext_space.zoomw(); console.log('seeked'); }, true);
		els[i].addEventListener("abort", function () {ap_ext_space.zoomw(); console.log('abort'); }, true);
		els[i].addEventListener("pause", function () {ap_ext_space.zoomw(); console.log('pause'); }, true);
		els[i].addEventListener("play", function () {ap_ext_space.zoomw(); console.log('play'); }, true);
		els[i].addEventListener("playing", function () {ap_ext_space.zoomw(); console.log('playing'); }, true);
		els[i].addEventListener("seeked", function () {ap_ext_space.zoomw(); console.log('seeked'); }, true);

		ap_ext_space.videos.push(els[i]);
		ap_ext_space.menu(els[i], srcDoc);
	};
	console.log('all videos:', ap_ext_space.videos);

	var ifrs = srcDoc.getElementsByTagName("iframe");
	console.log('iframes:', ifrs);

	var ifr;
	for (var i = 0; i < ifrs.length; i++) {
		ifr = ifrs[i];
		try {
			var innerDoc = (ifr.contentDocument || ifr.contentWindow.document);
			var innerWindow = (ifr.contentWindow || ifr);
			innerWindow.onkeydown = function (event) {
				var e = event || window.event;
				ap_ext_space.keyDn(e);
			};
			ap_ext_space.getVideos(innerDoc);
		} catch (err) {
			console.log('err', err);
		};
	};
},


OSDメニュー





さて、すべてのビデオ要素のリストがあります。では、OSDメニューを表示する方法は?そのブロック要素を各ビデオに追加してみましょう。はい、画面には多くのメニューが表示されますが、ある時点では、コマーシャルまたは映画自体の1つのビデオのみが表示されます。また、1つのメニューのみが表示されます。



ビデオは通常、親divにあります。最後の子としてmenudiv要素を追加しましょう。したがって、OSDは常にビデオの上に表示されます。



OSDイメージは、透明なアルファチャネルを使用してpng形式でbase64でエンコードされ、ap_ext_space.imgURに配置されます。これは、ブラウザーが別のドメインからイメージをロードできないためです。各ビデオのメニューを作成します。



menu: function(videoEl, doc) {

	//  div   video 
	//  ,       ( menuInside)
	var els = videoEl.parentNode.getElementsByTagName('div');
	var menuInside = false;
	for (var j = 0; j < els.length; j++) {
		if (els[j].id == 'ap_ext_space_container') {
			menuInside = true;
			ap_ext_space.menus.push(els[j]);
		};
	};

	if (menuInside == false) {

		//   
		var div = doc.createElement('div');
		div.innerHTML = ap_ext_space.html();
		videoEl.parentNode.appendChild(div);
		div.style.width = '520px';
		div.style.height = '410px';
		div.style.display = 'block';
		div.style.position = 'absolute';
		div.id = 'ap_ext_space_container';
		var url = "url('" + ap_ext_space.imgURL + "')";
		div.style.backgroundImage = url;
		div.style.opacity = 0.95;
		ap_ext_space.menus.push(div);

		//   
		div.addEventListener("dblclick", function(e) {
			e.preventDefault();
			e.stopPropagation();
		}, true);

		div.addEventListener("mouseover", function(e) {
			e.preventDefault();
			e.stopPropagation();

			var elem, evt = e ? e : event;
			if (evt.srcElement) {
				elem = evt.srcElement;
			} else if (evt.target) {
				elem = evt.target;
			};

			//     
			var pos = {
				ap_ext_space_num7: [520 + 134, 82],
				ap_ext_space_num8: [520 + 134 + 90, 82],
				ap_ext_space_num9: [520 + 134 + 90 + 90, 82],
				ap_ext_space_num4: [520 + 134, 82 + 90],
				ap_ext_space_num5: [520 + 134 + 90, 82 + 90],
				ap_ext_space_num6: [520 + 134 + 90 + 90, 82 + 90],
				ap_ext_space_num1: [520 + 134, 82 + 90 + 90],
				ap_ext_space_num2: [520 + 134 + 90, 82 + 90 + 90],
				ap_ext_space_num3: [520 + 134 + 90 + 90, 82 + 90 + 90]
			};
			var key, el;
			for (var j = 1; j < 10; j++) {
				key = 'ap_ext_space_num' + j;
				if (elem.id == key) {
					elem.style.backgroundImage = "url('" + ap_ext_space.imgURL + "')";
					elem.style.backgroundPosition = -pos[key][0] + 'px ' + -pos[key][1] + 'px';
				};
			};
		}, true);

		div.addEventListener("mouseout", function(e) {
			e.preventDefault();
			e.stopPropagation();

			var elem, evt = e ? e : event;
			if (evt.srcElement) {
				elem = evt.srcElement;
			} else if (evt.target) {
				elem = evt.target;
			};

			var key, el;
			for (var j = 1; j < 10; j++) {
				key = 'ap_ext_space_num' + j;
				if (elem.id == key) {
					elem.style.backgroundImage = "none";
				};
			};
		}, true);

		div.addEventListener("click", function(e) {
			e.preventDefault();
			e.stopPropagation();
			var elem, evt = e ? e : event;
			if (evt.srcElement) {
				elem = evt.srcElement;
			} else if (evt.target) {
				elem = evt.target;
			};
			ap_ext_space.clickHandler(elem);
		}, true);

		div.addEventListener("touchstart", function(e) {
			e.preventDefault();
			e.stopPropagation();
			var elem, evt = e ? e : event;
			if (evt.srcElement) {
				elem = evt.srcElement;
			} else if (evt.target) {
				elem = evt.target;
			};
			ap_ext_space.clickHandler(elem);
		}, true);

		div.addEventListener("touchend", function(e) {
			e.preventDefault();
		}, true);

		div.addEventListener("touchmove", function(e) {
			e.preventDefault();
		}, true);

		//     ( )
		ap_ext_space.menuPos();

	};
	console.log('all menus:', ap_ext_space.menus);
},


次のようにOSDdivをビデオに追加すると:videoEl.parentNode.appendChild(div)、フルスクリーンモードでもビデオの上に表示されます。それはそれを中央に配置するだけであり、むしろ、ビデオ要素に添付されたすべてのブロックメニュー項目でそれを行うために残っています(それらは520x410のサイズを持っています):



menuPos: function() {

	if (ap_ext_space.isFullScreen()) {

		var sc = ap_ext_space.scale;
		var iw = window.innerWidth,
			ih = window.innerHeight;
		var w = iw * sc;
		var h = w / 16 * 9;

		for (var i = 0; i < ap_ext_space.menus.length; i++) {
			ap_ext_space.menus[i].style.marginLeft = (iw - 520) / 2 + 'px';
			ap_ext_space.menus[i].style.marginTop = (-h - 410) / 2 + 'px';
		};

	} else {

		ap_ext_space.scale = 1;

		for (var i = 0; i < ap_ext_space.menus.length; i++) {
			ap_ext_space.menus[i].style.marginLeft = '0px';
			ap_ext_space.menus[i].style.marginTop = '0px';
		};
	};

},

isFullScreen: function() {
	return !!(document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement);
},


ちなみに、結局、ウィンドウモードではメニューを完全に非表示にし、フルスクリーンモードでのみビデオサイズの制御を許可することにしました。ウィンドウでは、それは意味がありません。



ハンドラー



ここでは、すべてが明確だと思います。画面上のメニューの各ボタンで、クリックハンドラー、手押し車、および対応するキーの組み合わせを押して、メニューが非表示の場合でもビデオを制御します。ボタンは、スケール値ap_ext_space.scale、ap_ext_space.scalew、およびap_ext_space.scalehを制御し、これらの値を増減してから、上記の各ビデオ要素のサイズを次のように変更します。



var sc = ap_ext_space.scale;
var iw = window.innerWidth,
	ih = window.innerHeight;
var w = iw * sc;
var h = w / 16 * 9;

for (var i = 0; i < ap_ext_space.videos.length; i++) {
	el = ap_ext_space.videos[i];
	el.style.position = 'initial';
	el.style.width = (w) + 'px';
	el.style.height = (h) + 'px';
	el.style.marginLeft = -(w - iw) / 2 + 'px';
	el.style.marginTop = -(h - ih) / 2 + 'px';
	el.style.transform = 'scaleX(' + ap_ext_space.scalew + ') scaleY(' + ap_ext_space.scaleh + ')';
};


さらに、(上記のgetVideos()関数で)各ビデオ要素に対してシーク、アボート、一時停止、再生、再生、シークされたビデオイベントハンドラーを使用して、座標を再計算して画面上のメニューを再描画する唯一の関数を呼び出しました。ユーザーの操作で「離れる」。ブラウザウィンドウのサイズ変更イベントについても同じことを行いました。



ネームスペース



一般的に、これはどのようなap_ext_spaceですか?実際には、ビデオのサイズ変更に使用されるすべての機能は、対応するページ(メインページまたはiframeのいずれか)に埋め込まれている必要があります。そこで、これらの関数とbase64OSDバックグラウンドを1つの名前空間に結合しました。これはすべて、次のようにバックグラウンドスクリプトから現在のブラウザタブのコードに挿入されます。



var codeString = ap_ext_space_f.toString() + '; ap_ext_space_f(); ap_ext_space.init()';
chrome.tabs.executeScript({
	code: codeString
});

function ap_ext_space_f() {

	ap_ext_space = {

		init: function() {
			//...
		},

		//...
	};

};


さて、ap_ext_space内で、すべてのiframeの検索がトリガーされ、次に各iframe内のすべてのビデオ、ハンドラーを含む画面上のメニューなどが作成されます。



使い方



動画を再生します。拡張機能アイコンをクリックします。ビデオを全画面に拡大します。スケールとアスペクト比を調整します。メニューは、キーボードショートカットctrl +0で非表示にできます。



結果



この拡張機能はBrowserVideo Tunerと呼ばれ、無料で、現在ChromeおよびFirefox拡張機能ストアで入手できます。また、もちろん、Opera、YandexBrowserなどのすべてのChrome互換ブラウザにインストールできます。拡張機能はすべてのビデオサイトで機能するわけではないことに注意してください。外部からのiframe要素へのアクセスがセキュリティポリシーによって保護されている場合、ビデオは単に見つかりません。そして、これに関する対応する警告がコンソールに表示されます。この場合、メニューは表示されません。しかし、Youtubeや多くのオンラインシネマでは、すべてが機能します。



一部のブラウザで軽微な問題が発生しました。たとえば、Yandex Browserでは、表示される画像がどういうわけか劣化し、高度に圧縮されたjpegに似ています。ただし、これは機能にはまったく影響しません。





ブラウザのセキュリティポリシーに依存せず、ドキュメント全体のサイズを制御するために、iframe内に埋め込まずに、ドキュメント全体の上にフルスクリーンモードでオンスクリーンメニューを表示する方法を探していましたが、今のところ成功していません。将来的には、拡張機能が新しい機能で補完されると思います。



All Articles