Запись видео с экрана Mac OS X средствами open-source

в 13:38, , рубрики: Android x86, ffmpeg, iphone, mac os x, vine, virtualbox, vnc, видео, Работа с видео, Разработка под android, метки: , , , , , ,

Запись видео с экрана Mac OS X средствами open sourceИногда надо записать демо работы программы, но под руками нет подходящих инструментов. Более того, бесплатных утилит для этих целей вообще не найти, а платные еще и не факт, что справятся как надо.
В моем случае возникла необходимость записать работу игры на симуляторе iPhone и Android. Программист внутри меня сразу предложил написать кучу кода, как под iOS/Android, так и под сам Мак, дампить кадры через OpenGL и пр. Остановив эти позывы, я таки решил найти готовые решения, а затем и оформить тут статью, как памятку.

Прелюдия

Поиск по форумам показал, что люди с подобными задачами делятся на две категории: те, кто покупает специализированные программы с возможностью захвата (QuickTime Pro, etc.) и те, кто пользуется взломанными/триальными/бета версиями этих же программ. Копнув чуть глубже, я узнал, что в мире Linux/Unix подобной проблемы нет вообще с момента добавления к ffmpeg модуля x11grab. Модуль этот перехватывает буфер X приложений и затем через ffmpeg видео сжимается в промежуточный формат (qtrle, raw, x264 lossless и пр.). В Mac тоже есть X сервер, но через него выводят информацию только программы, портированные с Linux, но не адаптированные под Cocoa. Это и стало отправной точкой моих экспериментов.

Инструменты: ffmpeg + x11grab + Xvfb

image
В MacPorts в ffmpeg модуль x11grab в принципе отсутствует. Собрать его целиком из исходников с первого раза не вышло, потому я решился пропатчить файл порта для ffmpeg-devel:
/opt/local/var/macports/sources/rsync.macports.org/release/tarballs/ports/multimedia/ffmpeg-devel/Portfile
Туда в конфигурацию nonfree я добавил строки --enable-x11grab и --enable-shared, для активации x11grab.
Забегая наперед скажу, что в этом методе нашелся изъян, потому я не выкладываю Portfile с правками, а описываю его тут только для ознакомления.

Когда X программа отображается в сессии (дисплее) :0.0 по-умолчанию, запись видео дает небольшую частоту кадров, в основном из-за размера экрана (2560x1440 у iMac), а скорее всего еще и из-за отрисовки графики, потому я решил перенаправлять программы в виртуальный дисплей с небольшим разрешением. Это делается через проект Xvfb, который без проблем установился из портов.
Запускается он довольно просто через терминал (размер с запасом):
   Xvfb :1 -screen 0 1024x800x15 -shmem

Так же просто подключается к этому виртуальному дисплею ffmpeg:
   ffmpeg -r 30 -s 1024x768 -f x11grab -i :1.0+0,20 -vcodec qtrle target.mov

На этом этапе меня ждала ошибка с переполнением Shared Memory, которая зачем-то в OS X установлена в неприличное значение в 4mb. Временное увеличение ее размера описывается и в рекомендациях Apple для серверов, и в других источниках:
   sudo sysctl -w kern.sysv.shmmax=67108864
   sudo sysctl -w kern.sysv.shmall=67108864

Запись сессии VirtualBox

Следующим этапом я решил вывести через X сервер что-то полезное. Первой мыслью было собрать X-версию VirtualBox, а уже там в виртуальной машине показывать буквально что-угодно, но VirtualBox для маков давно уже мигрировал на Cocoa, потому это был тупик. Второй здравой мыслью было подключиться по RDP к виртуальной машине и записывать сессию rdesktop, благо он работает именно под X. Активация RDP под VirtualBox довольно простая, но требует установки Extension Pack с официального сайта.
Подключение rdesktop с выводом через экран :0.1
   DISPLAY=:1.0 rdesktop -xl localhost

После этих действий ffmpeg начинает довольно успешно писать видео в .mov файл. В моем случае это был удачно подвернувшийся под руку Android x86:
Запись видео с экрана Mac OS X средствами open source

К сожалению, видео получается довольно дерганное, сказывается сжатие rdp, потому анимацию так снимать не очень хорошо.

Следующим шагом я решил перейти от RDP к VNC. В VirtualBox встроили VNC сервер, но не в публичные билды, а в собранные из портов или исходников. Никаких манипуляций с портами делать не пришлось, после сборки порта virtualbox я получил версию 4.1.14, с которой вполне можно работать.
Запись видео с экрана Mac OS X средствами open source
Неприятным моментом оказалось лишь то, что VNC не доступен через интерфейс, а только при запуске в headless режиме:
   VBoxHeadless -startvm 'Android x86' -v on --vnc
Управлять таким режимом приходится либо через второй сеанс VNC, либо снова через RDP, что не очень удобно, но в целом терпимо. Для захвата VNC потока использовался vncviewer, перенаправленный на виртуальный X дисплей:
   vncviewer localhost -ViewOnly -display :1.0 -PreferredEncoding raw -FullColor

Результатом всех этих изысков стало 5-минутное видео c честными 30 fps в разрешении 1024х768:

(заранее извиняюсь за качество контента, я все-таки не профессиональный игрок)

Если присмотреться, то временами заметны паузы на несколько секунд. К сожалению, эту проблему так и не получилось победить, да и сам подход вышел довольно громоздким. Для простейшей демонстрации игры в Android этого в общем достаточно, потому я переключился на следующую задачу — съемку видео симулятора iPhone.

Захват VNC для всего экрана

image
В OS X встроен удаленный доступ, который работает одновременно под двум протоколам — ARD и VNC. До выхода Lion 10.7 можно было включить Screen Sharing в системных настройках и подключиться к текущей сессии любым VNC клиентом. Начиная с 10.7 начались серьезные изменения: были выброшены все типы сжатия, кроме ZRLE, подключиться могут далеко не все клиенты, да и после подключения мы видим серый экран входа в систему, а уже лишь после ввода пароля пользователя подключаемся к сессии. Это отлично для администраторов, но для моей задачи наоборот создавало только препятствия. Программа vncviewer (он же RealVNC) в последних версиях уже умеет подключаться к OS X, но не умеет вводить пароль пользователя, потому этот путь тоже оказался тупиковым.
В качестве альтернативы я взял бесплатный VNC сервер от TestPlant (от же osxvnc и он же Vine). Версия 3.0 с sourceforge устарела, потому надо собирать новую из исходников или брать с сайта TestPlant.

Несущественный баг этого сервера в том, что изредка клиент отпадает с ошибкой «unknown message type 131». Лечится перезапуском сервера.

С уже налаженной связкой ffmpeg+x11grab+Xvfb и vncviewer получилось снять полноэкранное видео текущей сессии OS X, где был запущен симулятор iPhone:
Запись видео с экрана Mac OS X средствами open source

Размер виртуального буфера я выбрал заведомо меньше разрешения экрана, чтобы снимать верхний левый угол. Результат вышел вполне достойным, но к сожалению, с низким FPS — анимация в игре откровенно тормозила. Более того, паузы, которые были довольно редкими при схемке из VirtualBox стали гораздо более выраженными.
На этом этапе я провел несколько экспериментов, пересобирал VNC сервер и клиент из исходников, ставил минимальное разрешение экрана, но так и не добился хорошего результата. Уже через несколько часов стало понятно, что сам сервер отдает кадры с некоторой задержкой. После копания в коде выяснилось, что сервер намеренно делает паузу между обновлениями экрана:

        /* OK, now, to save bandwidth, wait a little while for more updates to come along. */
        /* REDSTONE - Lets send it right away if no rfbDeferUpdateTime */
        if (rfbDeferUpdateTime > 0 && !cl->immediateUpdate && !cl->needNewScreenSize) {
            pthread_mutex_unlock(&cl->updateMutex);
            usleep(rfbDeferUpdateTime * 1000);
            pthread_mutex_lock(&cl->updateMutex);
        }

Оказалось, что переменная rfbDeferUpdateTime имеет начально значение 40 мс, но она вполне управляема и задается через коммандную строку. В самом Vine Server для этого есть отдельное поле:
Запись видео с экрана Mac OS X средствами open source
Я поставил значение с запасом в 15, что дает максимальную частоту кадров в 66 fps. После этого прекратились лаги, но так и остались заметные паузы в видео. Теоретически, из такого видеоряда их можно повырезать и собрать что-то приемлемое, но хотелось более универсальное решение.

Туз в рукаве: vnc2flv

Теперь у меня был видеопоток в формате vnc отличного качества, оставалось только записать его в файл. Я снова подавил исконно программистское желание написать собственный дампер и нашел в недрах интернета проект vnc2swf, а затем и его наследника vnc2flv. Скептическое отношение к грабберу на Питоне прошло сразу же после первых результатов — программа записывает видеопоток в lossless качестве и разрешении WQHD с 15+ fps! Запускаю я ее без извратов с Xvfb, напрямую подключая к VNC серверу:
   flvrec.py -r 30 127.0.0.1

Для повышения fps достаточно уменьшить разрешение до 1280x720. Что интересно, при этом можно перезапустить Vine VNC, он подхватит текущее разрешение экрана, а затем можно спокойно переключиться на родное разрешение и включать запись.
Установка vnc2flv очень проста и описана на сайте, особых подводных камней тут нет.
Готовое видео можно обработать в любимом видео-редакторе, обрезать лишнее и сконвертировать в нужный формат. Я пользуюсь VirtualDub, запущенным под wine, но это уже дело привычки.
Запись видео с экрана Mac OS X средствами open source

Вот результат всей этой эпопеи:

Видео достаточно четкое, без рывков и лагов. Анимация записана нормально. Как по мне, вполне можно использовать для записи буквально чего-угодно с экрана Mac OS. Не хватает буквально только курсора, но это можно и так пережить.

Послесловие: тупиковые ветви

Перечислю тут тупики и грабли, которые встретил, на случай если кто-то будет искать свой собственный метод.

VLC Player — начиная с OS X 10.7 не умеет записывать видео с экрана
CaptureMe — вылетает, пишет пустое видео
SIMBL плагин для iPhone Simulator — требует модификации версии симулятора в коде и пересборки, не пишет видео iPad, в целом не заработал у меня вообще
osxvnc 3.0 c SourceForge — краш на 10.7
RealVNC Server — требует лицензионный ключ
VirtualBox, режим записи видео — умер 3 года назад, не поддерживается и не работает
ffmpeg+x11grab — перспективно и гибко, но дает неизлечимые паузы в видео. Привожу ссылку на блог, из которого подчерпнул эту идею.
Apple HTTP Live Streaming — не содержит инструментов для стриминга

Автор: Nomad1

  1. Доброжелатель:

    делать скринкаст с экрана можно средствами стандартного, предустановленного QuickTime Player:
    QuickTime Player -> Файл -> Новая запись изображения на экране
    можно выделить определенную область для записи экрана
    Подробно:
    http://www.makeuseof.com/tag/quick-screencasting-using-quicktime-x-in-mac-snow-leopard/ .

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


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