Поддержка MJPEG в «Опере»
Как я уже писал, «Опера» иначе показывает MJPEG, не как остальные браузеры. Если открыть
А мне хочется смотреть
Вдохновившись библиотекой добавляющей нативную поддержку APNG в браузер, я подумал, что я ничем не хуже и смогу добавить поддержку MJPEG в «Оперу». Получился небольшой userjs, который я, естественно, выложил на «Гитхаб».
Идея такая — находим картинку во всех тегах IMG, у которой расширение «mjpg». Через XHR получаем с сервера видеопоток, копим кадр, подменяем найденную картинку и так далее.
Работает начиная с 12 версии браузера, которая ещё не вышла (кстати, в позавчерашней сборке сломали userjs напрочь) — в предыдущих версиях XHR слабоват по возможностям для этого.
Больше всего я бился с декодированием бинарных данных. Последняя версия «Оперы» поддерживает тип ответа arraybuffer (а вот blob не поддерживается), но если его выбрать, то данные в цикле
Кстати, быстрее всего у меня получилось декодировать не циклом, а регулярными выражениями, дальше тормозит уже не мой код, а способ получения отдельного кадра — каждый раз я соединяюсь с сервером заново. Можно бы накапливать кадры пачкой и потом их сбрасывать, но у меня нет задачи смотреть трансляции футбола, у меня дом, который не бегает и полученной скорости мне вполне хватает
В общем, получился довольно небольшой код — 94 строки. Если вы не в курсе как устанавливаются скрипты userjs, то это очень просто. В интернете есть много инструкций на эту тему. Только напоминаю, что «Опера» нужна двенадцатая, причём в сборке за седьмое октября userjs сломаны.Посмотреть код.
// ==UserScript==
// @name MJPEG+Opera - Add to Opera MJPEG support
// @author Evgeny Stepanischev aka Bolk
// @version 1.00
// @namespace /files/opera-mjpeg.js
// @modified 2011-10-09
// @include *
// ==/UserScript==
opera.addEventListener('BeforeEvent.DOMContentLoaded', function() {
if (opera.version() < 12) return;
var BolkMJpeg = function (url, img) {
var req = new XMLHttpRequest();
img.src = 'about:blank'; // Сброс картинки, чтобы прекратить передачу
// Асинхронно забираем кадр при помощи XHR, нам придёт видеопоток
var start = function (url) {
req.open("GET", url, true);
// Свежепридуманный хитрый хак, позволяет получить данные в UCS-2
req.overrideMimeType('image/jpeg');
req.send(null);
}
// Хитрыми путями восстанавливаем порядок байт в reponse, там сейчас UCS-2,
// получаем просто последовательность байт
var reqbytes = function (n, l) {
return escape(req.response.substr(n, l)).
replace(/([^%]|%[^u].|%u....)/g, '<$1>').
replace(/<%u(..)(..)>/g, '%$2%$1').
replace(/<%(..)>/g, '%$1%00').
replace(/<(.)>/g, '$1%00');
}
// заменяем картинку на бинарные данные
var draw = function (bin) {
img.src = 'data:image/jpeg,' + bin;
}
// На изменение состояние входных данных собираем по частям JPEG
req.onreadystatechange = function () {
// состояние «3» — данные пошли, но ещё не кончились
if (req.readyState === 3) {
var hnd = setInterval(function () {
// по заголовку получаем размер данных
var header = unescape(reqbytes(0, 300)).toLowerCase();
var idx = header.indexOf('content-length:');
if (idx > -1) {
// 15 — длина заголовка
var len = parseInt(header.substr(idx + 15), 10);
var headend = header.indexOf("\r\n\r\n");
// дожидаемся конца заголовка
if (headend > -1) {
var resp = reqbytes(0, len);
// умножаем всё на три, так как reqbytes даёт esc-последовательность
// вида %XX%XX…
len *= 3;
headend = (headend + 4) * 3;
// если собрали весь JPEG, обрываем соединение
// и начинаем новый цикл. Мы могли бы собирать кадры и дальше, но
// это съест всю память со временем
if (resp.length >= len + headend) {
req.abort();
resp = resp.substr(headend, len);
draw(resp);
clearInterval(hnd);
start(url);
}
}
}
}, 100);
}
}
start(url);
};
// ищем картинку, у которой расширение mjpg
var imgs = document.getElementsByTagName('IMG');
for (var i = 0, len = imgs.length; i<len; i++) {
if (/\.mjpg$/.test(imgs[i].src)) {
BolkMJpeg(imgs[i].src, imgs[i]);
break;
}
}
}, true);
Теория относительности
Сейчас много разговоров о том, что в одном из экспериментов скорость нейтирино оказалась выше скорости света. Якобы, это ставит под сомнение теорию относительности Эйнштейна.
Чтобы вспомнить что такое теория относительности, рекомендую
Санскрит
Спасибо Илье Бирману за ссылку на потрясающую лекцию академика Андрея Анатольевича Зализняка про санскрит «О языке древней Индии». Я, кстати, пребывал в заблуждении, что санскрит — прадедушка большинства европейских языков, тех что называют индоевропейскими. Оказывается, нет:
И сейчас прекрасно известно, что
всё-таки санскрит — это просто одна из ветвей индоевропейских языков; он не более праязык, чем любой другой известный нам язык, а настоящий праязык индоевропейцев в подлинном своем виде ни в каких письменных документах не сохранился. И вообще этот праязык на несколько тысяч лет глубже во времени, чем любой самый древний письменный памятник, которым мы располагаем. А все индоевропейские языки к моменту, от которого до нас доходят какие бы то ни было памятники, уже прошли даже не несколько веков, а несколько тысячелетий развития от общего праязыка.
Лекция действительно интересная, я насладился хорошим литературным языком, которым она изложена и провёл много параллелей со своим прежним опытом. Вот истоки восточной витееватости, последствия разделения литературного языка и языка плебеев:
С другой стороны, потребность выражаться изысканно ведет к тому, что в хорошем классическом санскрите корову никто коровой не назовет. А назовут ее обязательно
какой-нибудь «пестроходящей», «молочноглазой» и т. д. Змея никогда не будет названа змеей, хотя для этого есть прекрасное простое слово, родственное русскому. Но это слово простецкое, низкое, в хорошей литературе оно употребляться не должно, это был бы низкий плебейский стиль.
Вот почему в литовском (который очень близок к санскриту) часто слышно окончание
Так вот, литовский занял бы первое место среди живых языков по сохранности древнего числа слогов. Он сохранил окончание именительного падежа -ас. Ростов будет
по-литовски Ростовас. Почему? Потому что это -ас так же необходимо в современном литовском, как и три тысячи лет назад в санскрите.