Конвертируем видео… в SVG

в 12:33, , рубрики: javascript, svg, метки: , ,

Конвертируем видео… в SVGТак уж сложилось, что испокон веков единственный кроссбраузерный способ показать анимацию в браузере без JS — анимированный gif. Был бы у него JPEG-based аналог — и интернет мог бы быть совсем другим… Современные альтернативы, например APNG — также работают не везде и со столь же небольшим сжатием, а долгожданный тэг <video> страдает от патентов.

Хочу поделится результатами небольшого академического эксперимента о конвертировании видео в формат SVG (которое затем при везении можно просто показать через <img src="">). Академического — потому что проблемы кросс-браузерной совместимости далеки от решения, и потому в нынешнем виде это едва-ли где-то применимо.

Сначала результат

Счастливые обладатели Firefox сразу могли бы видеть видео не отходя от кассы, Chrome — при встраивании падает, потому картинку пришлось убрать из статьи.

Открыть SVG в отдельном окне. IE требует и открытия в новом окне, и применения встроенного в SVG JS (такой вариант в отдельном окне работает во всех 3-х браузерах, но во встроенном через <img src=""> виде не работает нигде...).

Подходы к реализации

foreignObject
SVG позволяет вставлять внутрь себя произвольный HTML (и не только) код через foreignObject. С его помощью можно вставить внутрь SVG и тэг video/flash-плеер, но работает это все совершенно по разному в разных браузерах. В FF например video работает, а flash ничего не показывает, но играет звук. В Chrome — наоборот, flash работает а video-нет.

Похоже так ничего не выйдет…

base64-jpeg
Уже достаточно давно используют включение base64-encoded картинок внутрь css и svg файлов:

<image id="frame0" width="480" height="201" xlink:href="data:image/jpeg;base64,/9j//gAPTGF2YzU0Lj[...]"></image>

Попробуем это использовать. Осталось лишь найти способ показывать картинки по очереди…

SVG SMIL
SMIL (Synchronized Multimedia Integration Language) позволяет нам реализовать нужную анимацию множества встроенных в SVG кадров:

<svg version="1.1" baseProfile="tiny" id="svg-root"
  width="100%" height="100%" viewBox="0 0 480 360"
  xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
 
   <image id='frame0' width="320" height="240" xlink:href="data:image/jpeg;base64,/9j//gAPTGF2YzU0Lj[...]" display='inline'>
<set id="show1" attributeName="display" to="inline" begin="0s;show4.end" dur="1s" fill="freeze"/>
<set id="hide1" attributeName="display" to="none"  begin="show1.end" dur="0.01s" fill="freeze"/>      
   </image>
   <image id='frame1' width="320" height="240" xlink:href="data:image/jpeg;base64,/9j//gAPTGF2YzU0Lj[...]" display='none'>
<set id="show2" attributeName="display" to="inline" begin="show1.end" dur="1s" fill="freeze"/>
<set id="hide2" attributeName="display" to="none"  begin="show2.end" dur="0.01s" fill="freeze"/>      
   </image>
   <image id='frame2' width="320" height="240" xlink:href="data:image/jpeg;base64,/9j//gAPTGF2YzU0Lj[...]" display='none'>
<set id="show3" attributeName="display" to="inline" begin="show2.end" dur="1s" fill="freeze"/>
<set id="hide3" attributeName="display" to="none"  begin="show3.end" dur="0.01s" fill="freeze"/>      
   </image>
   <image id='frame3' width="320" height="240" xlink:href="data:image/jpeg;base64,/9j//gAPTGF2YzU0Lj[...]" display='none'>
<set id="show4" attributeName="display" to="inline" begin="show3.end" dur="1s" fill="freeze"/>
<set id="hide4" attributeName="display" to="none"  begin="show4.end" dur="0.01s" fill="freeze"/>      
   </image>
</svg>

Очевидно, это не работает в IE (ни 9 ни 10) — т.к. он не поддерживает SMIL. В Firefox/Chrome это уже работает в отдельном окне, а в FireFox — и в случае встраивания через тэг img.

Встроенный в SVG JavaScript
Сама возможность встраивать JavaScript внутрь картинки меня несколько покоробила.
Тем не менее, такая возможность есть:

<svg>
[....]
<script type="text/ecmascript"><![CDATA[
  var svgDoc;
  var desiredFramesPerSecond=12;
  var msPerFrame = 1000/desiredFramesPerSecond;
  var numFrames = 4;
  
  var frameCt=0;  

  svgDoc = document.getElementsByTagName("svg")[0];
  setTimeout("AnimateEm()",msPerFrame);

  function AnimateEm(){
    if (frameCt==0) startTime = new Date();
    setTimeout("AnimateEm()",msPerFrame);

    svgDoc.getElementById('frame'+frameCt.toString()).style.display='none';
    frameCt=(frameCt+1)%numFrames;
    svgDoc.getElementById('frame'+frameCt.toString()).style.display='inline';
  }

]]></script>
</svg>

При открытии svg в отдельном окне это работает во всех 3-х браузерах, но при встраивании — ни в одном. Возможно специалисты JS смогут это исправить…

Генерируем наше SVG видео
1. Раздираем avi-файл на изображения:

ffmpeg -i "atari.avi" -r 12 -y -qscale 5 -vf scale=480:-1 -f image2 atari%%03d.jpg

2. Генерируем svg

php -q convert.php >convert.svg

<svg version="1.1" baseProfile="tiny" id="svg-root"
  width="100%" height="100%" viewBox="0 0 480 201"
  xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
 <?

$numFrames = 217;
$FPS = 12;

for($i=0;$i<=$numFrames-1;$i++)
{
?>
<image id="frame<?=$i?>" width="480" height="201" xlink:href="data:image/jpeg;base64,<?=base64_encode(file_get_contents("atari".str_pad(($i+1),3,"0",STR_PAD_LEFT).".jpg"))?>" display="<?=($i==0)?"inline":"none"?>">
<set id="show<?=$i?>" attributeName="display" to="inline" begin="<?=($i==0?"0s;":"")?>show<?=($i+$numFrames-1)%$numFrames?>.end" dur="<?=1/$FPS?>s" fill="freeze"/>
<set id="hide<?=$i?>" attributeName="display" to="none"  begin="show<?=$i?>.end" dur="0.01s" fill="freeze"/>      
</image>
<?
}
?>
</svg>

Скачать тестовые картинки и скрипты можно тут.

Надеюсь, более светлые умы хабра смогут улучшить кросс-браузерную совместимость svg-видео, и мы сможем забыть мегабайтные анимированные gif-ы как страшный сон 90-х…

Автор: BarsMonster

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js