Альтернативы для генерации видео из скриншотов - PullRequest
6 голосов
/ 27 января 2012

Я работаю в проекте удаленного администрирования игрушек На данный момент я могу делать снимки экрана и управлять мышью с помощью класса Robot. Снимки экрана: BufferedImage экземпляры.

Прежде всего, мои требования: - Только сервер и клиент. - Производительность важна, поскольку клиент может быть приложением Android.

Я думал об открытии двух сокетных подключений, одно для мыши и системных команд, а второе для видеопотока.

Как я могу преобразовать снимки экрана в видеопоток? Должен ли я преобразовать их в известный видеоформат или было бы хорошо просто отправить серию сериализованных изображений?

Сжатие - еще одна проблема. Согласно моим предварительным тестам, отправка снимков экрана в полном разрешении приведет к низкой частоте кадров. Я думаю, что мне нужно, по крайней мере, 24 кадра в секунду, чтобы воспринимать движение, поэтому я должен и уменьшить, и сжать. Я мог бы преобразовать BufferedImages в jpg файлы и затем установить степень сжатия, но я не хочу хранить файлы на диске, они должны жить только в оперативной памяти. Другой возможностью будет сериализация экземпляров (представляющих несжатый снимок экрана) в GZipOutputStream. Каков правильный подход к этому?

Подведем итог:

  • Если вы порекомендуете подход "серии изображений", как бы вы сериализовали их в сокет OutputStream?
  • Если ваше предложение заключается в преобразовании в известный видеоформат, какие классы или библиотеки доступны?

Заранее спасибо.

ОБНОВЛЕНИЕ: мои тесты, клиент и сервер на одной машине
-Полный экранный сериализованный BufferedImages (только размер, тип и int []), без сжатия: 1,9 кадров в секунду.
-полные изображения на экране через потоки GZip: 2,6 кадра в секунду.
- Уменьшенные изображения (ширина 640) и потоки GZip: 6,56 кадра в секунду.
Полноэкранные изображения и кодировка RLE: 4,14 кадра в секунду.
- Уменьшенные изображения и кодировка RLE: 7,29 кадров в секунду.

Ответы [ 5 ]

6 голосов
/ 14 февраля 2012

Если бы это были только снимки экрана, я бы не сжимал их, используя схему сжатия видео, скорее всего, вам не нужно сжатие с потерями (размытые детали в мелком тексте и т. Д. Являются наиболее распространенными дефектами). Чтобы получить работоспособное ощущение «удаленного рабочего стола», запомните ранее отправленный снимок экрана и отправьте только разницу , чтобы перейти к следующему. Если между кадрами ничего не меняется (или очень мало), это очень эффективно. Однако в некоторых ситуациях это не будет хорошо работать, например, при воспроизведении видео, игре или прокрутке документа.

Сжать разницу между двумя BufferedImage можно с помощью более или менее сложных методов, очень простой, но достаточно эффективный метод - просто вычесть одно изображение из другого (в результате чего нули везде одинаковы) и сжать результат с помощью простая RLE (кодировка длины пробега).

Снижение точности цвета можно использовать для дальнейшего уменьшения объема данных (в зависимости от варианта использования вы можете опускать наименее значимые N битов каждого цветового канала, поскольку большинство приложений с графическим интерфейсом выглядят не сильно отличающимися, если вы уменьшаете цвета с 24 бит до 15 бит).

5 голосов
/ 17 февраля 2012
  • Разбейте экран на квадраты сетки (или полосы)
  • Отправлять квадрат сетки только если он отличается от предыдущего

// server start

sendScreenMetaToClient(); // width, height, how many grid squares
...

// server loop ImageBuffer[] prevScrnGrid while(isRunning) {

ImageBuffer scrn = captureScreen();
ImageBuffer[] scrnGrid = screenToGrid(scrn);
for(int i = 0; i < scrnGrid.length; i++) {
    if(isSameImage(scrnGrid[i], prevScrnGrid[i]) == false) {
        prevScrnGrid[i] = scrnGrid[i];
        sendGridSquareToClient(i, scrnGrid[i]); // send the client a message saying it will get grid square (i) then send the bytes for grid square (i)
    }
} }

Не отправляйте сериализованные объекты Java, просто отправляйте данные изображения.

ByteArrayOutputStream imgBytes = new ByteArrayOutputStream();
ImageIO.write( bufferedImage, "jpg", imgBytes );
imgBytes.flush();
3 голосов
/ 10 февраля 2012

Во-первых, я мог бы предложить захватить только небольшую часть экрана, а не уменьшать масштаб и потенциально потерять информацию, возможно, с чем-то вроде скользящего окна, которое можно перемещать, нажимая края курсором. Это на самом деле просто небольшое дизайнерское предложение.

Что касается сжатия, я думаю, что серия изображений не будет сжиматься отдельно, а также с приличной схемой сжатия видео, тем более что кадры, вероятно, останутся согласованными между захватами в этом сценарии.

Одним из вариантов может быть использование Xuggle, который способен захватывать рабочий стол через Robot в нескольких форматах видео, но я не могу сказать, можете ли вы передавать и декодировать с этим.

Для захвата JPEG-файлов и их конвертации вы также можете использовать this .

Потоковое видео, похоже, немного сложнее.

Кроме того, похоже, что заброшенная Java Media Framework поддерживает эту функцию.

Мои знания в этой области не фантастические, поэтому извините, если я потратил впустую ваше время, но похоже, что была скомпилирована некоторая более полезная информация о возможности использования Xuggle в качестве скриншара здесь . По-видимому, это также связано с их собственными заметками о существующих подходах.

Если это не обязательно должна быть чистая Java, я считаю, что все было бы намного проще использовать, просто взаимодействуя с собственным инструментом захвата экрана ...

Может быть, было бы проще всего просто отправить видео в виде серии jpegs! Вы всегда можете реализовать свою собственную схему сжатия, если вы чувствуете себя немного сумасшедшим ...

2 голосов
/ 10 февраля 2012

Я думаю, вы описали хорошее решение в своем вопросе.Преобразуйте изображения в формат JPEG, но не записывайте их в виде файлов на диск.Если вы хотите, чтобы это был известный видеоформат, используйте M-JPEG. M-JPEG - это поток кадров JPEG в стандартном формате.Многие цифровые камеры, особенно старые, сохраняют видео в этом формате.

Информацию о воспроизведении потока M-JPEG можно получить из ответов на этот вопрос: Android и MJPEG

Если пропускная способность сети является проблемой, тогда вы захотите использовать систему межкадрового сжатия, такую ​​как MPEG-2, h.264 или аналогичную.Это требует намного больше обработки, чем M-JPEG, но гораздо эффективнее.

0 голосов
/ 17 февраля 2012

Если вы пытаетесь получить видео со скоростью 24 кадра в секунду, то нет причин не использовать современные видеокодеки. Зачем пытаться воссоздать это колесо?

Xuggler отлично работает для кодирования видео h264 и звучит так, как будто бы он удовлетворит ваши потребности.

...