FreeBSD, вещание видео с web-камеры в сеть

12th Апрель 2011 | Метки: , , , , , , , , , , , ,
Опубликовал: Dmitriy [dk] Kalinin

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

Пока дозиметр в пути — решил сделать все необходимое для вещания с веб-камеры.

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

 

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

Изначально, веб-камера была подключена к компьютеру через два usb удлинителя (3м + 5м). Но при таком подключении она ни в какую не хотела определяться. Поэтому было принято решение — купить usb-хаб (D-Link DUB-1040), стоимость которого колеблется в районе 250р. в нашем захолустном городке :) После чего она прекрасно распозналась.

Компьютер работает под управлением ОС FreeBSD. Изначально все проверки на ноутбуке с Windows 7. После успешного получения изображения с веб-камеры начался следующий этап — заставить ее работать под FreeBSD.

mira# uname -a
FreeBSD mira.s25.tmb.local 8.2-STABLE FreeBSD 8.2-STABLE #15 r220493M: Sat Apr  9 17:14:03 MSD 2011     root@mira.s25.tmb.local:/usr/obj/usr/src/sys/mira  amd64
mira# usbconfig
ugen0.1:  at usbus0, cfg=0 md=HOST spd=HIGH (480Mbps) pwr=SAVE
ugen1.1:  at usbus1, cfg=0 md=HOST spd=HIGH (480Mbps) pwr=SAVE
ugen1.2:
 at usbus1, cfg=0 md=HOST spd=HIGH (480Mbps) pwr=SAVE
ugen0.2:
 at usbus0, cfg=0 md=HOST spd=HIGH (480Mbps) pwr=SAVE
ugen0.3:  at usbus0, cfg=0 md=HOST spd=HIGH (480Mbps) pwr=SAVE
ugen0.4:
 at usbus0, cfg=0 md=HOST spd=HIGH (480Mbps) pwr=ON

ugen0.3 — это как раз USB-хаб, а ugen0.4 — это камера, которая подключена через него. Все оборудование определилось и готово к работе.

Следующий этап — это установка драйверов для веб-камеры. Недавно под FreeBSD был портирован набор драйверов для веб-камер из Linux, который входит в порт multimedia/webcamd.
Устанавливаем этот порт:

mira# cd /usr/ports/multimedia/webcamd/
mira# make && make install && make clean
...
1) webcamd requires the cuse4bsd kernel module, please load this
by doing

       # kldload cuse4bsd

or adding

       cuse4bsd_load="YES"

to your /boot/loader.conf.

2) Please restart devd as the configuration changed

        # /etc/rc.d/devd restart

*********************************************************************
===> Installing rc.d startup script(s)
===>   Running ldconfig
/sbin/ldconfig -m /usr/local/lib
===>   Registering installation for webcamd-0.1.23
===>  Cleaning for libv4l-0.8.1
===>  Cleaning for webcamd-0.1.23

Сразу запихиваем в /boot/loader.conf загрузку модуля cuse4bsd и подгружаем его:

mira# echo 'cuse4bsd="yes"' >> /boot/loader.conf
mira# kldload cuse4bsd
mira# kldstat -n cuse4bsd
Id Refs Address            Size     Name
12    1 0xffffffff809b7000 5ad0     cuse4bsd.ko

Родной rc-скрипт для webcamd (/usr/local/etc/rc.d/webcamd) никуда не годится, поэтому приводим его к такому виду:

mira# cat /usr/local/etc/rc.d/webcamd
#!/bin/sh
#
# PROVIDE: webcamd
# REQUIRE: DAEMON
#

name="webcamd"
rcvar=${name}_enable

. /etc/rc.subr

load_rc_config $name
: ${webcamd_enable="no"}
: ${hald_enable="no"}

command=/usr/local/sbin/webcamd
command_args="-B"
start_precmd="${name}_prestart"

webcamd_prestart()
{
  if checkyesno hald_enable ; then
    command_args="$command_args -H"
  fi
}

run_rc_command $1

и добавляем следующие строки в /etc/rc.conf:

webcamd_enable="yes"
webcamd_flags="-d ugen0.4 -i 0 -v 0"

ugen0.4 — это номер устройства на USB-шине, которое соответсвует веб-камере. В моем случае — это ugen0.4, список устройств на USB-шине можно посмотреть с помощью команды usbconfig.

На этом установка webcamd завершена. Запускаем его, и проверяем. В случае успеха должно появится новое устройство /dev/videoN:

mira# /usr/local/etc/rc.d/webcamd start
Starting webcamd.
Attached ugen0.4[0] to cuse unit 0
mira# ls /dev/video*
/dev/video0

Все в порядке, камера готова к работе.

Следующий этап — это получить видео с камеры. Существует несколько программ для этого: pwcbsd, pwcview, vlc, ffmpeg, mencoder
Из этого набора — вещать видео умеют vlc и ffmpeg (ffserver). По списку зависимостей — vlc тянет за собой очень много чего, поэтому решил использовать ffmpeg.

Собираем ffmpeg (/usr/ports/multimedia/ffmpeg), предварительно отключив в нем все, кроме оптимизации и ffserver:

[X] CPUDETECT         Enable runtime CPU detection
[X] FFSERVER          Build and install ffserver
[X] OPTIMIZED_CFLAGS  Additional optimizations
[X] SSSE3             Use binutils from ports (SSSE3 support)

Следует обратить внимание — что по какой-то причине во FreeBSD исключена поддержка video4linux. Для исправления этой ситуации заходим в папку /usr/ports/multimedia/ffmpeg/files и смотрим на файл patch-configure:

mira# cat /usr/ports/multimedia/ffmpeg/files/patch-configure
--- configure.orig      2010-06-15 21:44:30.000000000 +0200
+++ configure   2010-12-12 22:19:20.533163967 +0100
@@ -1469,7 +1469,7 @@
 nm_opts='-g'

 # machine
-arch_default=$(uname -m)
+arch_default=$(uname -p)
 cpu="generic"

 # OS
@@ -1514,7 +1514,7 @@

 CC_O='-o $@'

-host_cflags='-D_ISOC99_SOURCE -D_POSIX_C_SOURCE=200112 -O3 -g -Wall'
+host_cflags='-D_ISOC99_SOURCE -O3 -g -Wall'
 host_libs='-lm'

 target_path='$(CURDIR)'
@@ -2096,7 +2096,7 @@
     die "C compiler test failed."
 fi

-add_cppflags -D_ISOC99_SOURCE -D_POSIX_C_SOURCE=200112
+add_cppflags -D_ISOC99_SOURCE
 check_cflags -std=c99
 check_cc -D_FILE_OFFSET_BITS=64 <
---> @@ -2678,8 +2678,8 @@
--->  fi
---> fi
--->-check_header linux/videodev.h
--->-check_header linux/videodev2.h
--->+#check_header linux/videodev.h
--->+#check_header linux/videodev2.h
--->check_header sys/videoio.h
--->check_func_headers "windows.h vfw.h" capCreateCaptureWindow "$vfwcap_indev_extralibs"
@@ -2986,7 +2986,7 @@
 SRC_PATH="$source_path"
 SRC_PATH_BARE=$source_path
 BUILD_ROOT="$PWD"
-ARCH=$arch
+FFMPEG_ARCH=$arch
 CC=$cc
 AS=$as
 LD=$ld

Все, что выделено — удаляем. Получится что-то вроде этого:

mira# cat /usr/ports/multimedia/ffmpeg/files/patch-configure
--- configure.orig      2010-06-15 21:44:30.000000000 +0200
+++ configure   2010-12-12 22:19:20.533163967 +0100
@@ -1469,7 +1469,7 @@
 nm_opts='-g'

 # machine
-arch_default=$(uname -m)
+arch_default=$(uname -p)
 cpu="generic"

 # OS
@@ -1514,7 +1514,7 @@

 CC_O='-o $@'

-host_cflags='-D_ISOC99_SOURCE -D_POSIX_C_SOURCE=200112 -O3 -g -Wall'
+host_cflags='-D_ISOC99_SOURCE -O3 -g -Wall'
 host_libs='-lm'

 target_path='$(CURDIR)'
@@ -2096,7 +2096,7 @@
     die "C compiler test failed."
 fi

-add_cppflags -D_ISOC99_SOURCE -D_POSIX_C_SOURCE=200112
+add_cppflags -D_ISOC99_SOURCE
 check_cflags -std=c99
 check_cc -D_FILE_OFFSET_BITS=64 <
@@ -2986,7 +2986,7 @@
 SRC_PATH="$source_path"
 SRC_PATH_BARE=$source_path
 BUILD_ROOT="$PWD"
-ARCH=$arch
+FFMPEG_ARCH=$arch
 CC=$cc
 AS=$as
 LD=$ld

Собираем и устанавливаем ffmpeg:

mira# cd /usr/ports/multimedia/ffmpeg
mira# make && make install && make clean

И проверяем, что он собран с поддержкой video4linux:

mira# ffmpeg -formats | grep video4linux
 D  video4linux     Video4Linux device grab
 D  video4linux2    Video4Linux2 device grab

Все в порядке.

Теперь приступаем к настройке ffserver. Бегло окинув базовый конфиг для него, пишем свой. Мой приобрел такой вид:

mira# cat /usr/local/etc/ffserver.conf
Port 8090
BindAddress 0.0.0.0
MaxHTTPConnections 200
MaxClients 20
MaxBandwidth 20000
CustomLog /var/log/ffserver.log

    File /tmp/webcam1.ffm
    FileMaxSize 200K
    ACL allow 127.0.0.1

    Format status
    ACL allow localhost
    ACL allow 192.168.25.0 192.168.25.255

    URL http://www.ffmpeg.org/

    Feed webcam1.ffm
    Format swf
    VideoBitRate 1000
    VideoBufferSize 20000
    VideoFrameRate 15
    VideoSize 640x480
    VideoQMin 10
    VideoQMax 20
    PreRoll 3
    NoAudio
    ACL ALLOW 0.0.0.0 255.255.255.255

Приводим в порядок rc-скрипт ffserver:

mira# cat /usr/local/etc/rc.d/ffserver
#!/bin/sh
# PROVIDE: ffserver
# REQUIRE: NETWORKING
# KEYWORD: shutdown

ffserver_enable="${ffserver_enable-NO}"

. /etc/rc.subr

name=ffserver
rcvar=`set_rcvar`

command="/usr/local/bin/ffserver"
required_files=/usr/local/etc/ffserver.conf

load_rc_config ${name}
run_rc_command "$1"

и разрешаем его запуск в /etc/rc.conf следующей строкой:

ffserver_enable="yes"

Запускаем ffserver:

mira# /usr/local/etc/rc.d/ffserver

ffserver готов к работе, теперь осталось скормить ему поток — и он начнет его транслировать. Для этого пишем скрипт, вроде этого:

mira# cat /root/webcam2ffserver.sh
#!/bin/sh
ffmpeg=/usr/local/bin/ffmpeg

env LD_PRELOAD=/usr/local/lib/libv4l/v4l1compat.so \
    $ffmpeg -f video4linux -s 640x480 -r 15 -i /dev/video0 http://127.0.0.1:8090/webcam1.ffm

Несколько примечаний: -f video4linux — будем использовать формат video4linux для источника /dev/video0, именно на него webcamd смонтировал камеру. Т.к. /dev/video0 — это устройство, а мы собрали ffmpeg с поддержкой устройств video4linux — то необходимо сказать устройству, что-же мы от него хотим. Для этого мы говорим, что размер кадра хотим 640×480 (-s 640×480), частота кадров — 15кадр/сек. (-r 15). Полученный поток с камеры мы передаем ffserver-у. Для этого указываем feed нашего ffserverhttp://127.0.0.1:8090/webcam1.ffm

Запускаем скрипт:

mira# ./webcam2ffserver.sh
[video4linux @ 0x802e21010] Estimating duration from bitrate, this may be inaccurate
Input #0, video4linux, from '/dev/video0':
  Duration: N/A, start: 1302610062.643770, bitrate: 55296 kb/s
    Stream #0.0: Video: rawvideo, yuv420p, 640x480, 55296 kb/s, 15 tbr, 1000k tbn, 15 tbc
[buffer @ 0x802eb5110] w:640 h:480 pixfmt:yuv420p
Output #0, ffm, to 'http://127.0.0.1:8090/webcam1.ffm':
  Metadata:
    encoder         : Lavf52.103.0
    Stream #0.0: Video: flv, yuv420p, 640x480, q=2-31, 1000 kb/s, 1000k tbn, 15 tbc
Stream mapping:
  Stream #0.0 -> #0.0
Press [q] to stop encoding
[flv @ 0x802e0da10] rc buffer underflow
frame= 8188 fps= 15 q=6.8 size=   67280kB time=545.87 bitrate=1009.7kbits/s dup=3 drop=0

Все работает, входной поток )Stream #0.0: Video: rawvideo, yuv420p, 640×480, 55296 kb/s, 15 tbr, 1000k tbn, 15 tbc) пережимается в исходящий (Stream #0.0: Video: flv, yuv420p, 640×480, q=2-31, 1000 kb/s, 1000k tbn, 15 tbc). Используется кодек flv.
Нижняя строка показывает оперативную информацию, сколько кадров обработано, частота кадров, качество, размер потока, время и прочие …

Посмотреть, что получилось у меня — можно здесь.

PS:
ffserver — довольно сырой продукт, в котором еще существует довольно много косяков. Например постоянная рассинхронизация видео, на что разработчики лаконично отвечают:

mira# man ffserver
FFSERVER(1)                                                        FFSERVER(1)

NAME
       ffserver - FFserver video server
...

  The audio and video loose sync after a while.

       Yes, they do.
...

Но тем не менее, проект постоянно развивается и надеюсь в скором времени эти косяки будут устранены.

На текущий момент в стабильной ветке ffmpeg под FreeBSD существует косяк с блокировкой сокета, из-за чего процессор загружается на 100%, решение данной проблемы можно посмотреть здесь.

Если кому интересно — могу выложить фото, как все разместилось это вживую :)

  1. Yaakov Tooth
    14th Апрель 2011 в 03:40

    Отличная статья. :)

    Я с камеры, правда снимал motion, и дальше скриптами парсил эвенты вместе с netpbm, выдавая с апача красивые картинки. Но сейчас перекручу на ffmpeg, чтобы иметь ещё и живой поток, а с него уже пусть motion кушает. :)

  2. Алексндр
    17th Апрель 2011 в 16:22

    За статью спасибо, да.
    У меня тут вопросик нарисовался: можно глянуть, какие у Вас библиотечки на тему v4l установлены? А то отредактировать patch-configure я и сам догадался, но пересобранный ffmpeg всё равно молчит про video4linux в выводе -formats.
    Да, у меня установлены libv4l-0.8.1, v4l_compat-1.0.20101027_1. И система пропатчена: http://lists.freebsd.org/pipermail/freebsd-multimedia/2011-April/011888.html и далее по нити.

  3. Алексндр
    17th Апрель 2011 в 17:00

    @Алексндр
    А. Не. Отбой. Собралось как надо.

  4. 18th Апрель 2011 в 13:11

    судя по configure — для поддержки video4linux он требует только наличия зоголовчных файлов в
    check_header linux/videodev.h
    check_header linux/videodev2.h
    Которые ставятся вместе с libv4l, поэтому надо смотреть на что матюгается в момент компиляции :(

  5. 19th Апрель 2011 в 05:54

    Спасибо Дмитрию за статью, очень познавательно. Скоро буду у себя вебку ставить :)

  6. Артем
    7th Январь 2012 в 02:56

    Спасибо за статью!

    А кто-нить может подсказать, как собрать ffmpeg из исходников самостоятельно? Чтобы была поддержка v4l? А то у меня ./configure пишет, что библиотека такая не найдена и pr: Not found..

  7. 7th Январь 2012 в 07:34

    для поддержки video4linux нужно установить порт libv4l, драйверы для вебкамер — webcamd.

    Можете полностью привести сообщения об ошибке? (CC ……)

  8. 29th Январь 2012 в 09:22

    Отличная статья, наконец то вывел камеру с кормушки в веб :)

  9. lemz
    11th Февраль 2012 в 10:42

    Дорогой автор, а почему ряд твоих изысканий не направить админам соответствующих портов?

  10. emoxam
    25th Февраль 2012 в 19:35

    «Все, что выделено — удаляем.»

    а что выделено то ? тот бардак что одной строкой ? что-то я не могу пройти этот момент )

  11. 25th Февраль 2012 в 19:44

    @@ -2678,8 +2678,8 @@
    fi
    fi
    -check_header linux/videodev.h
    -check_header linux/videodev2.h
    +#check_header linux/videodev.h
    +#check_header linux/videodev2.h
    check_header sys/videoio.h
    check_func_headers «windows.h vfw.h» capCreateCaptureWindow «$vfwcap_indev_extralibs»

    Вот этот блок. С разными версиями в портах он может меняться, но смысл прежний:
    -check_header linux/videodev.h
    -check_header linux/videodev2.h
    +#check_header linux/videodev.h
    +#check_header linux/videodev2.h

    вот это надо убирать

Вы должны авторизоваться для отправки комментария.