在浏览器中拉伸视频





在线电影院中的视频通常具有与监视器不同的宽高比。因此,有时希望通过在边缘处进行一些裁剪来使整体比例更大。甚至-使图像适合图片较小一侧的屏幕尺寸。对于小屏幕以及较旧的4:3显示器,尤其如此。我已经对原始视频通常可以在一侧拉伸并且必须以某种方式纠正这一事实感到沉默。



为了解决这个问题,我决定为Chrome和Firefox编写一个浏览器扩展。这个想法是这样的:播放任何浏览器视频时,都会调用一个屏幕菜单,您可以随意更改图片的比例和纵横比。



iframe



我遇到的第一个问题是网站上的视频不一定位于主页上,而是可以隐藏在嵌套的iframe中。我决定扫描所有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菜单?让我们将其block元素添加到每个视频中。是的,然后我们将有很多屏幕菜单,但一次只显示一个视频:商业广告或电影本身。并且只有一个菜单将与它们一起显示。



该视频通常位于父div中。让我们将菜单div元素添加为最后一个子元素。这样,OSD将始终显示在视频上。



OSD图像使用透明的Alpha通道以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);
},


如果将OSD div添加到以下视频中: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中)。因此,我只是将这些功能以及base64 OSD背景与它们组合到一个名称空间中。所有这些都从后台脚本注入到当前浏览器选项卡的代码中,如下所示:



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隐藏菜单。



结果



该扩展名为“浏览器视频调谐器”,它是免费的,目前可在Chrome和Firefox扩展商店中使用。而且,当然,它可以安装在所有与Chrome兼容的浏览器中,例如Opera,Yandex Browser等。应当注意,该扩展名不适用于所有视频站点。如果从外部访问iframe元素受到安全策略的保护,那么就不会发现任何视频。与此相关的警告将出现在控制台中。在这种情况下,将不会显示菜单。但是在YouTube和许多在线电影院上,一切正常。



在某些浏览器中发现了一些小问题。例如,在Yandex浏览器中,显示的图像会以某种方式变差并类似于严重压缩的jpeg。但这不会以任何方式影响功能。





我一直在寻找一种在整个文档的顶部仅以全屏模式显示屏幕菜单的方法,而不是将其嵌入到iframe中,以免依赖于浏览器的安全策略,并试图控制整个文档的大小,但是到目前为止我还没有成功。我认为,将来的扩展将补充新功能。



All Articles