- PVSM.RU - https://www.pvsm.ru -
«Что позволило остаться GIF, это — циклическое проигрывание анимации, которое добавил Netscape. Если бы Netscape не добавил поддержку GIF в свой браузер, GIF умер бы в 1998»
— Александр Тревор (Alexander Trevor),
руководитель команды по созданию GIF в CompuServe
Формат GIF в июне этого года отпраздновал свое 25-летие, и является сегодня самым старым графическим форматом, который распространен в интернете. Посвящая выходные просмотру смешных анимированных гифок понимаешь, что некоторые из них были бы в разы лучше со звуком. Все текущие решения для циклической анимации со звуком (например: coub.com [1], gifsound.com [2]) предлагают отказаться от GIF, но это — не выход. И я решил пожертвовать просмотром гифок на выходных для решения этой крайне важной проблемы.
Первая в интернете гифка со звуком по ссылке [3]. Надо нажать на синюю кнопку, а потом на гифке. Плеер должен работать во всех современных браузерах (тестировался в последнем Firefox и Chrome).
Гифок под катом не будет, а будет процесс создания расширения для стандарта, написания конвертера и плеера.
С 1987 формат GIF пережил всего два существенных изменения:
Как было сказано выше, стандарт GIF 89a позволяет приложениям размещать в GIF файле свои данные. Формат блока расширения для приложений:
Размер, байт | Содержание |
---|---|
2 | Заголовок блока расширения приложения (всегда 0x21, 0xFF) |
1 | Размер блока (всегда 11) |
8 | Идентификатор приложения |
3 | Код аутентификации приложения (может использоваться для проверки, что блок создан конкретным приложением) |
* | Вложенные блоки данных |
1 | Указатель конца блока (0x00) |
Попробуем уместить сюда заголовка WAVE файла:
Размер, байт | Содержание |
---|---|
4 | Заголовок файла (всегда «RIFF») |
4 | Размер данных |
4 | Формат данных (для WAVE фалов — «WAVE») |
Так как размер блока контролируется форматом GIF, выкинем из заголовка поле с размером данных, а в идентификатор приложения запишем «RIFFWAVE». Остаток WAVE файла запишем как вложенные GIF блоки.
Блок со звуком будем вставлять прямо перед первым блоком с изображением (на самом деле можно его вставить в любом месте).
Конвертер на входе принимает GIF и WAVE файлы, а на выходе выдает GIF с RIFFWAVE блоком. Исходный код можно посмотреть посмотреть на google code [4].
Код довольно простой, читаем WAVE файл, создаем из него GIF блок. Потом читаем гиф файл и записываем все блоки, как только находим первый блок с картинкой, перед ним вставляем блок со звуком. Самый важный участок кода — конвертация WAVE файла в GIF блок:
def get_wav_block(file):
# читаем данные из фала
(signature,
size,
format) = unpack('4sI4s', file)
if signature != 'RIFF':
raise Exception('Not a RIFF file')
if format != 'WAVE':
raise Exception('Not a WAVE file')
data = file.read(size - 4)
# Готовим заголовок блока
wave_block_header = struct.pack('BBB8sBBB', 0x21, 0xff, 11, 'RIFFWAVE', 0, 0, 0)
data_subblocks = [wave_block_header];
# разбиваем данные на блоки из 255 байт
for i in range(0, len(data) // 255):
data_subblocks.append(chr(0xff))
data_subblocks.append(data[i*255:(i + 1)*255])
if (len(data) % 255) > 0:
rest = len(data) % 255
data_subblocks.append(chr(rest))
data_subblocks.append(data[-rest:])
# добавляем сивол окончания блока
data_subblocks.append(chr(0))
return ''.join(data_subblocks)
Делаем анимированный гиф:
# ffmpeg получаем отдельные кадры
ffmpeg -ss 0:00:9.73 -t 2.86 -i warlus.webm -s 500x280 -r 10 frames/image%03d.png
# делаем анимированный GIF
convert -delay 10 -loop 0 frames/*.png source.gif
Получаем звук и проверяем, что все выглядит (и звучит), как мы запланировали:
mplayer -ss 0:00:9.73 -endpos 2.86 warlus.webm -ao pcm:file="source.wav" -vo null
mplayer -loop 0 -audiofile source.wav source.gif
Конвертируем в GIF со звуком
python wave2gif.py example/source.gif example/source.wav result.gif
За основу плеера возьмем jsgif [5] — плеер для анимированных GIF на JavaScript. jsgif разбирает gif и проигрывает рисуя каждый кадр на Canvas. Добавим в него функцию, которая при обнаружении «RIFFWAVE» блока:
var doSound = function(sound) {
// Header
var size = sound.data.length + 4
var size_text = String.fromCharCode(size & 255, (size >> 8) & 255, (size >> 16) & 255, (size >> 24) & 255);
var header = [
"RIFF",
size_text, // length
"WAVE"
].join('');
var out = [header, sound.data].join('');
var dataURI = "data:audio/wav;base64," + escape(window.btoa(out));
sound_element = new Audio();
sound_element.src = dataURI;
};
jsgif работает не быстро, а с добавлением звука стал еще медленнее. Так же, чтобы получить данные файла, плеер вызывает XMLHttpRequest, поэтому плеер работает только с картинками с одного домена. Но разве это помехи для искусства.
Радоваться гифкам со звуком. Можно реализовать плагин для браузеров, который бы позволял проигрывать такие гифки без дополнительного XMLHttpRequest, и который возможно будет быстрей работать. Если кто-то сталкивался с подобной задачей, буду признателен за указание в какую сторону смотреть, чтобы написать плагин который обрабатывает определенные типы файлов.
GIF89a Specification [6]
WAVE PCM soundfile format [7]
jsgif: A GIF player in JavaScript [5]
Автор: PatapSmile
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/javascript/14831
Ссылки в тексте:
[1] coub.com: http://coub.com
[2] gifsound.com: http://gifsound.com
[3] по ссылке: http://denyspotapov.com/gif-sound.html
[4] google code: http://code.google.com/p/gif-sound/source/browse/wave2gif/wave2gif.py
[5] jsgif: http://slbkbs.org/jsgif/
[6] GIF89a Specification: http://www.w3.org/Graphics/GIF/spec-gif89a.txt
[7] WAVE PCM soundfile format : https://ccrma.stanford.edu/courses/422/projects/WaveFormat/
Нажмите здесь для печати.