Étirer la vidéo dans le navigateur





Très souvent, la vidéo dans les cinémas en ligne a un rapport hauteur / largeur différent de celui du moniteur. Par conséquent, il est parfois souhaitable d'agrandir un peu l'échelle globale en recadrant un peu les bords. Ou même - ajustez l'image dans la taille de l'écran sur le côté le plus petit de l'image. Cela est particulièrement vrai pour les petits écrans, ainsi que pour les anciens moniteurs 4: 3. Je garde déjà le silence sur le fait que la vidéo originale peut généralement être étirée d'un côté et cela doit être corrigé d'une manière ou d'une autre.



Pour résoudre ce problème, j'ai décidé d'écrire une extension de navigateur pour Chrome et Firefox. L'idée est la suivante: lors de la lecture de n'importe quelle vidéo du navigateur, un menu à l'écran est appelé, ce qui vous permet de changer arbitrairement l'échelle et le rapport hauteur / largeur de l'image.



iframe



Le premier problème que j'ai rencontré est que les vidéos sur les sites Web ne sont pas nécessairement situées sur la page principale, mais peuvent être cachées profondément dans des iframes imbriquées. J'ai décidé de scanner tous les iframes et de trouver tous les éléments vidéo dans chacun d'eux. En passant, cela résout également un autre problème: vous ne savez jamais où se trouve la vidéo publicitaire et où se trouve le film lui-même. Trouvons-les tous d'abord.



La fonction getVideos s'appelle elle-même de manière récursive jusqu'à ce que tous les éléments vidéo soient trouvés dans la dernière iframe. Toutes les vidéos sont ajoutées au tableau ap_ext_space.videos. La fonction getVideos prend le document de la page actuelle comme paramètre d'entrée. Au premier lancement, le document principal est pris. En cours de route, des gestionnaires sont ajoutés à chaque vidéo, mais plus à ce sujet ci-dessous.



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);
		};
	};
},


Menu OSD





D'accord, nous avons une liste de tous les éléments vidéo. Maintenant, comment afficher le menu OSD? Ajoutons simplement son élément de bloc à chaque vidéo. Oui, nous aurons alors de nombreux menus à l'écran, mais à un moment donné, une seule vidéo est affichée: l'une des publicités ou le film lui-même. Et un seul menu sera affiché avec eux.



La vidéo est généralement située dans la division parent. Ajoutons-y notre élément menu div en tant que dernier enfant. Ainsi, l'OSD sera toujours affiché sur la vidéo.



Nous encoderons l'image OSD en base64 au format png avec un canal alpha transparent et la placerons dans ap_ext_space.imgUR, car le navigateur ne nous permettra pas de charger l'image d'un autre domaine. Créez un menu pour chaque vidéo:



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);
},


Si vous ajoutez un div OSD à une vidéo comme celle-ci: videoEl.parentNode.appendChild (div), il apparaîtra au-dessus de la vidéo même en mode plein écran. Il ne reste plus qu'à le centrer, ou plutôt, à le faire avec tous les éléments de menu de bloc attachés aux éléments vidéo (ils ont une taille de 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);
},


Au fait, à la fin, j'ai décidé de masquer complètement le menu en mode fenêtré et de n'autoriser le contrôle de la taille de la vidéo qu'en mode plein écran. Dans la fenêtre, cela n'a aucun sens.



Manutentionnaires



Ici, je pense, tout est clair. Sur chaque bouton du menu à l'écran, des gestionnaires de clics, une brouette, etc. - en appuyant sur la combinaison de touches correspondante sont suspendus pour contrôler la vidéo même avec le menu masqué. Les boutons contrôlent les valeurs d'échelle: ap_ext_space.scale, ap_ext_space.scalew et ap_ext_space.scaleh, en augmentant ou en diminuant ces valeurs, puis en redimensionnant chaque élément vidéo trouvé ci-dessus comme suit:



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 + ')';
};


De plus, j'ai également accroché sur les gestionnaires d'événements vidéo recherché, abandonner, mettre en pause, lire, jouer, rechercher pour chaque élément vidéo (dans la fonction getVideos () ci-dessus) un appel à la seule fonction qui redessine le menu à l'écran en recalculant ses coordonnées, car parfois il "part" avec une action de l'utilisateur. J'ai fait la même chose pour l'événement de redimensionnement de la fenêtre du navigateur.



Espace de noms



En général, de quel type d'ap_ext_space s'agit-il? Le fait est que toutes les fonctions utilisées pour redimensionner la vidéo doivent être intégrées dans la page correspondante (soit dans la page principale, soit dans l'iframe). Je viens donc de combiner ces fonctions, et avec elles, l'arrière-plan OSD base64 en un seul espace de noms. Tout cela est injecté dans le code de l'onglet actuel du navigateur à partir du script d'arrière-plan comme suit:



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() {
			//...
		},

		//...
	};

};


Eh bien, dans ap_ext_space, une recherche de toutes les iframes est déclenchée, puis toutes les vidéos à l'intérieur de chacune d'elles, un menu à l'écran avec des gestionnaires est construit, etc.



Comment utiliser



Lire la vidéo. Cliquez sur l'icône d'extension. Développez la vidéo en plein écran. Ajustez l'échelle et le rapport hauteur / largeur. Le menu peut être masqué avec le raccourci clavier ctrl + 0.



Résultat



L'extension s'appelle Browser Video Tuner, elle est gratuite et est actuellement disponible dans les magasins d'extensions Chrome et Firefox. De plus, bien sûr, il peut être installé dans tous les navigateurs compatibles Chrome tels que Opera, Yandex Browser, etc. Il est à noter que l'extension ne fonctionne pas sur tous les sites vidéo. Lorsque l'accès aux éléments iframe de l'extérieur est protégé par une politique de sécurité, aucune vidéo ne sera simplement trouvée. Et un avertissement correspondant à ce sujet apparaîtra dans la console. Dans ce cas, le menu ne sera tout simplement pas affiché. Mais sur Youtube et dans de nombreux cinémas en ligne, tout fonctionne.



Des problèmes mineurs ont été constatés avec certains navigateurs. Par exemple, dans le navigateur Yandex, l'image affichée se détériore d'une manière ou d'une autre et ressemble à du jpeg fortement compressé. Mais cela n'affecte en rien la fonctionnalité.





Je cherchais un moyen d'afficher le menu à l'écran en mode plein écran simplement au-dessus du document entier sans l'incorporer dans des iframes, afin de ne pas dépendre de la politique de sécurité du navigateur, et d'essayer de contrôler la taille de l'ensemble du document dans son ensemble, mais jusqu'à présent, je n'ai pas réussi. Je pense qu'à l'avenir, l'extension sera complétée par de nouvelles fonctions.



All Articles