Linux Mint 17 и DLNA

от автора

Зачем это все надо

Linux Mint — отличная домашняя ОС. Богатый выбор графических сред на любой вкус, простая и понятная даже новичку установка, сразу после установки имеем основной набор необходимого софта, удобный менеджер приложений, основные мультимедийные кодеки. В общем, отличная система для перехода и ознакомления с Linux.image
Но возникает проблема для тех, у кого нет дома NAS или чего-то подобного, а смотреть контент с ПК на других устройствах, поддерживающих DLNA хочется. Под Linux существует множество различных DLNA-серверов, но оптимальным, все же, является MiniDLNA. И тому есть несколько причин. Во-первых, это типичный Unix-way: этот сервер является именно DLNA-сервером, а не комбайном с кучей свистелок, при этом контент без проблем воспринимается на любом устройстве. Даже телевизоры Samsung со своим AllShare прекрасно подключаются к нему. Также можно отметить и замечательную скорость работы и нетребовательность к ресурсам этого сервера.
И все бы было хорошо, но вот в с помощью команды

sudo apt-get install minidlna

установить его не получится — нет его в репозитарии.

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

Подготовка

Если будете выполнять команды в консоли, запущенной с правами root, команду sudo писать не нужно.
Для начала нужно установить необходимые библиотеки:
Обновляемся:

sudo apt-get update

И устанавливаем сами библиотеки:

sudo apt-get install libavcodec-dev libavformat-dev libavutil-dev libflac-dev libvorbis-dev libogg-dev libid3tag0-dev libexif-dev libjpeg-dev libsqlite3-dev 

Самая свежая версия MiniDLNA на данный момент — 1.1.3, ее можно скачать здесь, либо загрузить из консоли:

wget --trust-server-names http://sourceforge.net/projects/minidlna/files/latest/download

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

tar -xf minidlna-1.1.3.tar.gz

Теперь перейдем в распакованную папку с исходниками

cd minidlna-1.1.3

Если вы хотите, чтобы на устройстве отображались сразу папки с файлами, нужно немного подправить код:

Открывайте спойлер

Для отключения запроса способа отображения файлов нужно в папке с исходниками (minidlna-1.1.3, мы в нее уже должны были перейти) подправить файл upnpsoap.c, но сначала сделаем его резервную копию:

sudo cp upnpsoap.c upnpsoap.c.old

и откроем в редакторе

sudo nano upnpsoap.c

поиском (F6), ищем процедуру:

"BrowseContentDirectory"static void BrowseContentDirectory(struct upnphttp * h, const char * action) {

В начале процедуры выполняется разбор SOAP/XML-запроса, затем его обработка и возврат результата. Разбор запроса завершается формированием отладочного сообщения:

DPRINTF(E_DEBUG, L_HTTP, "Browsing ContentDirectory:\n" 			 " * ObjectID: %s\n" 			 " * Count: %d\n" 			 " * StartingIndex: %d\n" 			 " * BrowseFlag: %s\n" 			 " * Filter: %s\n" 			 " * SortCriteria: %s\n", 			ObjectID, RequestedCount, StartingIndex, 			BrowseFlag, Filter, SortCriteria);

Далее реализован выбор корневого контейнера в зависимости от конфигурационного параметра root_container:

	// Если запрошен корневой контейнер 	if( strcmp(ObjectID, "0") == 0 ) 	{ 		// Выставляем флаг корневого контейнера 		args.flags |= FLAG_ROOT_CONTAINER; 		// Если в конфиге задан корневой контейнер 		if( runtime_vars.root_container ) 		{ 			// Если клиентом является аудио плейер и в конфиге задан обзор папок 			// Отправляем его в папку с музыкой 			if( (args.flags & FLAG_AUDIO_ONLY) && (strcmp(runtime_vars.root_container, BROWSEDIR_ID) == 0) ) 				ObjectID = MUSIC_DIR_ID; 			else // Иначе выставляем контейнер, заданный в конфиге 				ObjectID = runtime_vars.root_container; 		} 		else // Если контейнер в конфиге не задан 		{ 			// Если клиентом является аудио плейер 			// Отправляем его в папку с музыкой 			if( args.flags & FLAG_AUDIO_ONLY ) 				ObjectID = MUSIC_ID; 		} 	}

Сразу после этого блока и перед блоком с запросами к БД добавляем код перенаправления папок для видео, музыки и изображений:

	//Redirect video to folder 	if( strcmp(ObjectID, VIDEO_ID) == 0 ) 	{ 		ObjectID = VIDEO_DIR_ID; 	}  	//Redirect music to folder 	if( strcmp(ObjectID, MUSIC_ID) == 0 ) 	{ 		ObjectID = MUSIC_DIR_ID; 	}  	//Redirect images to folder 	if( strcmp(ObjectID, IMAGE_ID) == 0 ) 	{ 		ObjectID = IMAGE_DIR_ID; 	}

В итоге правленный фрагмент должен выглядеть следующим образом:

        if( strcmp(ObjectID, "0") == 0 )         {                 args.flags |= FLAG_ROOT_CONTAINER;                 if( runtime_vars.root_container )                 {                         if( (args.flags & FLAG_AUDIO_ONLY) && (strcmp(runtime_vars.root_containe$                                 ObjectID = MUSIC_DIR_ID;                         else                                 ObjectID = runtime_vars.root_container;                 }                 else                 {                         if( args.flags & FLAG_AUDIO_ONLY )                                 ObjectID = MUSIC_ID;                 }         }          //Redirect video to folder         if( strcmp(ObjectID, VIDEO_ID) == 0 )         {                 ObjectID = VIDEO_DIR_ID;         }          //Redirect music to folder         if( strcmp(ObjectID, MUSIC_ID) == 0 )         {                 ObjectID = MUSIC_DIR_ID;         }          //Redirect images to folder         if( strcmp(ObjectID, IMAGE_ID) == 0 )         {                 ObjectID = IMAGE_DIR_ID;         }          if( strcmp(BrowseFlag+6, "Metadata") == 0 )         {                 args.requested = 1;                 sql = sqlite3_mprintf("SELECT %s, " COLUMNS                                       "from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID$                                       " where OBJECT_ID = '%q';",                                       (args.flags & FLAG_ROOT_CONTAINER) ? "0, -1" : "o.OBJECT_I$                                       ObjectID);                 ret = sqlite3_exec(db, sql, callback, (void *) &args, &zErrMsg);                 totalMatches = args.returned;         } 

Чтобы упростить процедуру последующего обновления, сохраним патч с внесенными изменениями:

diff -u upnpsoap.c.old upnpsoap.c > ../minidlna-folders.patch

Теперь вместо редактирования файла, достаточно применить патч:

cd ~/src/minidlna/minidlna-НоваяВерсия patch < ../minidlna-folders.patch

Теперь можно переходить к конфигурированию.

Если вам это не нужно, переходите сразу к конфигурированию:

sudo ./configure

И компилируем:

sudo make

Установка

1) Для тех, кто в дальнейшем захочет обновить версию, предыдущую можно удалить (конфигурационные файлы при этом сохраняются), если этого не сделать, то в случае, если в новой версии состав установочных файлов изменится, в /usr/local могут остаться старые файлы, при первой установке — переходим сразу к пункту 2.
Для удаления заходим в папку с предыдущей установкой и выполняем команду:

sudo make uninstall

2) Запускаем установку:

sudo make install

3)Если мы производим первоначальную установку, нужно создать init-скрипт:

sudo nano /etc/init.d/minidlna

если мы обновляем версию, больше ничего делать не нужно, при первоначальной установке у нас откроется пустой файл, в который нужно внести следующий код:

Листинг

#!/bin/sh # # MiniDLNA initscript # # Based on the mediatomb debian package. # Original authors: Tor Krill <tor@excito.com> #                   Leonhard Wimmer <leo@mediatomb.cc> #                   Andres Mejia <mcitadel@gmail.com> # # Modified by: Benoit Knecht <benoit.knecht@fsfe.org> # ### BEGIN INIT INFO # Provides:          minidlna # Required-Start:    $network $local_fs $remote_fs # Required-Stop::    $network $local_fs $remote_fs # Should-Start:      $all # Should-Stop:       $all # Default-Start:     2 3 4 5 # Default-Stop:      0 1 6 # Short-Description: Start minidlna at boot time # Description:       Manage the minidlna daemon, a DLNA/UPnP-AV media server. ### END INIT INFO  unset USER  # PATH should only include /usr/* if it runs after the mountnfs.sh script PATH=/sbin:/usr/sbin:/bin:/usr/bin DESC="DLNA/UPnP-AV media server" NAME=minidlnad DAEMON=/usr/local/sbin/minidlnad PIDDIR=/run/$NAME PIDFILE=$PIDDIR/$NAME.pid SCRIPTNAME=/etc/init.d/minidlna DEFAULT=/etc/default/minidlna  # Exit if the package is not installed [ -x $DAEMON ] || exit 0  # Read configuration variable file if it is present [ -r $DEFAULT ] && . $DEFAULT  # Load the VERBOSE setting and other rcS variables . /lib/init/vars.sh  # Define LSB log_* functions. # Depend on lsb-base (>= 3.0-6) to ensure that this file is present. . /lib/lsb/init-functions  # Do not start the daemon if NO_START is enabled in DEFAULT if [ "$START_DAEMON" != "yes" ] && [ "$1" != "stop" ]; then         log_warning_msg "$NAME: Not starting $DESC."         log_warning_msg "$NAME: Disabled in $DEFAULT."         exit 0 fi  # Set the default configuration file if [ -z $CONFIGFILE ]; then         CONFIGFILE=/etc/minidlna.conf fi  # Set the default log file if [ -z $LOGFILE ]; then         LOGFILE=/var/log/minidlna.log fi  # Run as `minidlna' if USER is not specified or is `root' if [ -z $USER ]; then         USER=minidlna fi  # If no group is specified, use USER if [ -z $GROUP ]; then         GROUP=$USER fi  DAEMON_ARGS="-f $CONFIGFILE -P $PIDFILE $DAEMON_OPTS"  # # Function that starts the daemon/service # do_start() {         # Return         #   0 if daemon has been started         #   1 if daemon was already running         #   2 if daemon could not be started         touch $LOGFILE && chown $USER:$GROUP $LOGFILE || return 2         if [ ! -d $PIDDIR ]; then             mkdir $PIDDIR || return 2         fi         chown $USER:$GROUP $PIDDIR || return 2          start-stop-daemon --start --quiet --pidfile $PIDFILE \                 --chuid $USER:$GROUP --exec $DAEMON --test > /dev/null \                 || return 1         start-stop-daemon --start --quiet --pidfile $PIDFILE \                 --chuid $USER:$GROUP --exec $DAEMON -- \                 $DAEMON_ARGS \                 || return 2 }  # # Function that stops the daemon/service # do_stop() {         # Return         #   0 if daemon has been stopped         #   1 if daemon was already stopped         #   2 if daemon could not be stopped         #   other if a failure occurred         start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME         RETVAL="$?"         [ "$RETVAL" = 2 ] && return 2         # Wait for children to finish too if this is a daemon that forks         # and if the daemon is only ever run from this initscript.         start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON         [ "$?" = 2 ] && return 2         # Many daemons don't delete their pidfiles when they exit.         rm -rf $PIDDIR         return "$RETVAL" }  case "$1" in   start)     [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC " "$NAME"     do_start     case "$?" in                 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;                 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;         esac   ;;   stop)         [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"         do_stop         case "$?" in                 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;                 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;         esac         ;;   status)        status_of_proc -p "$PIDFILE" "$DAEMON" "$NAME" && exit 0 || exit $?        ;;   restart|force-reload)         log_daemon_msg "Restarting $DESC" "$NAME"         do_stop         case "$?" in           0|1)                 if [ "$1" = "force-reload" ]; then                         # Rescan the collection                         DAEMON_ARGS="$DAEMON_ARGS -R"                 fi                 do_start                 case "$?" in                         0) log_end_msg 0 ;;                         1) log_end_msg 1 ;; # Old process is still running                         *) log_end_msg 1 ;; # Failed to start                 esac                 ;;           *)                 # Failed to stop                 log_end_msg 1                 ;;         esac         ;;   *)         echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2         exit 3         ;; esac  :

, сохранить комбинацией клавиш Ctrl+O и выйти из редактора — Ctrl+X.
4) После этого даем права на запуск скрипта:

sudo chmod 755 /etc/init.d/minidlna

5) И активируем запуск init-скрипта:

sudo update-rc.d minidlna defaults

6) Так как по умолчанию служба запускается под пользователем minidlna, нам нужно создать соответствующую учетную запись:

sudo adduser --system --home /var/lib/minidlna --group --gecos "MiniDLNA server" minidlna

7) Правим файл конфигурации сервера:

sudo nano /etc/minidlna.conf

В нем нам нужно следующее содержимое (здесь подправлен интервал, с которым сервер себя анонсирует, так как не все телевизоры опрашивают сервер, также вам надо прописать путь к папке с медиа, к примеру так: «media_dir=V,/mnt/sdb6/Video/Alex/» в соответствующем разделе файла):

Листинг minidlna.conf

# Порт сервера port=8200  # Привязка к сетевому интерфейсу # Можно задать несколько интерфейсов # в формате network_interface=eth0,eth1 #network_interface=eth0  # Имя пользователя или UID, под которым будет работать служба # Добавлен в версии 1.1.0 # В Debian задается в параметрах init-скрипта #user=jmaggard  # Путь к папке с медиа-файлами # Для сканирования нескольких папок, укажите несколько параметров media_dir # Чтобы сканировать файлы определенного типа, укажите соответствующий префикс: #   A - аудио: media_dir=A,/home/jmaggard/Music #   V - видео: media_dir=V,/home/jmaggard/Videos #   P - изображения: media_dir=P,/home/jmaggard/Pictures # Начиная с версии 1.1.0, можно задать несколько типов: #   PV - изображения и видео: media_dir=AV,/var/lib/minidlna/digital_camera # # При изменении параметра, потребуется повторное сканирование файлов. # Необходимо выполнить команду "service minidlna force-reload" от имени root. # Начиная с версии 1.1.0, при изменении параметра, сканирование выполняется автоматически. media_dir=/var/lib/minidlna  # Имя DLNA-сервера, отображаемое клиентом # По умолчанию: "$HOSTNAME:$USER" #friendly_name=  # Путь к папке для хранения базы данных и кэша обложек альбомов db_dir=/var/lib/minidlna  # Путь к папке с лог-файлами log_dir=/var/log  # Уровень детальности лога # В формате log_level=источник1,источник2=значение1,источник3,источник4=значение2 ... # Доступные источники: "general", "artwork", "database", "inotify", "scanner", "metadata", "http", "ssdp", "tivo" # Возможные значения: "off", "fatal", "error", "warn", "info" or "debug" #log_level=general,artwork,database,inotify,scanner,metadata,info,ssdp,tivo=warn  # Перечень имен файлов-обложек альбомов, разделитель: "/" album_art_names=Cover.jpg/cover.jpg/AlbumArtSmall.jpg/albumartsmall.jpg/AlbumArt.jpg/albumart.jpg/Album.jpg/album.jpg/Folder.jpg/folder.jpg/Thumb.jpg/thumb.jpg  # Автообнаружение новых файлов # Включено по умолчанию inotify=yes  # Поддержка устройств TiVo #enable_tivo=no  # Строго следовать DLNA-стандарту # Использовать серверное масштабирование для очень больших JPEG-изображений # Что может снизить скорость их обработки. #strict_dlna=no  # Адрес веб-страницы устройства # По умолчанию IP-адрес и заданный порт сервера #presentation_url=http://www.mylan/index.php  # Интервал отправки SSDP-уведомлений, в секундах notify_interval=30  # Серийный номер и номер модели DLNA-сервера, сообщаемый клиенту serial=12345678 model_number=1  # Путь к сокету MiniSSDPd, если установлен # Требуется для обеспечения работы нескольких DLNA/UPnP служб на одном сервере #minissdpdsocket=/run/minissdpd.sock  # Контейнер, используемый в качестве корневой папки для клиентов #   * "." - стандартный контейнер #   * "B" - "Обзор папки" #   * "M" - "Музыка" #   * "V" - "Видео" #   * "P" - "Изображения" # Если задано "B" и клиент представится как аудиоплеер, в качестве корня будет использована папка "Music/Folders" #root_container=.  # Всегда использовать заданный критерий сортировки, вместо значения, запрошенного клиентом #force_sort_criteria=+upnp:class,+upnp:originalTrackNumber,+dc:title  # Максимальное число одновременных подключений # Учтите: многие клиенты открывают несколько подключений одновременно #max_connections=50

8)Затем проверяем параметры init-скрипта /etc/default/minidlna:

sudo nano /etc/default/minidlna

Обычно корректировка не требуется. Если файл отсутствует, при первичной установке из исходников, копируем листинг:

# Запускать демон, если задано "yes" START_DAEMON="yes"  # Путь к файлу конфигурации #CONFIGFILE="/etc/minidlna.conf"  # Путь к лог-файлу #LOGFILE="/var/log/minidlna.log"  # Запуск от имени заданного пользователя и группы # По умолчанию: minidlna #USER="minidlna" #GROUP="minidlna"  # Дополнительные ключи запуска DAEMON_OPTS=""

Обычно после этого не требуется дополнительных настроек, но если есть проблемы с доступом к медиа (или хотите перестраховаться), можно настроить права доступа:

Настройка прав доступа:

Поскольку служба работает под пользователем с ограниченными правами, публикуемые папки и файлы должны быть доступны на чтение для всех пользователей, следовательно, иметь разрешения 644: «rw- r— r—», для файлов и 755: «rwx r-x r-x», для папок.
Проверяем доступность для каждой папки, заданной в minidlna.conf, командой:

sudo -u minidlna ls -l папка

Если папка недоступна, задаем права доступа:

sudo chmod -R 755 папка

Вышестоящие папки также должны быть доступны на чтение всем пользователям. Проверяем доступность на чтение каждой папки, указанной в пути. Для вышестоящих папок используем chmod без ключа -R, если не требуется сброс разрешений для всех дочерних файлов и папок.
В качестве альтернативы смене разрешений, можно запустить MiniDLNA от имени пользователя или группы-владельца файлов. Для этого необходимо задать параметры USER и GROUP в /etc/default/minidlna, и сменить владельца папки /var/lib/minidlna командой:

sudo chown -R пользователь:группа /var/lib/minidlna

Запуск сервера

Запускаем:

sudo service minidlna force-reload

Проверяем, работает ли служба, также смотрим параметры запуска:

ps ax | grep minidlna

Проверяем, слушается ли порт:

sudo ss -4lnp | grep minidlna

Проверяем лог:

cat /var/log/minidlna.log

В случае успешного старта, лог должен быть примерно следующим:

Gothician gothician # cat /var/log/minidlna.log [2014/07/27 10:05:31] minidlna.c:1014: warn: Starting MiniDLNA version 1.1.3. [2014/07/27 10:05:31] minidlna.c:355: warn: Creating new database at /var/lib/minidlna/files.db [2014/07/27 10:05:31] minidlna.c:1053: warn: HTTP listening on port 8200 [2014/07/27 10:05:31] scanner.c:706: warn: Scanning /var/lib/minidlna [2014/07/27 10:05:31] scanner.c:793: warn: Scanning /var/lib/minidlna finished (0 files)! [2014/07/27 10:05:31] playlist.c:125: warn: Parsing playlists... [2014/07/27 10:05:31] playlist.c:259: warn: Finished parsing playlists. [2014/08/03 09:25:35] minidlna.c:1053: warn: HTTP listening on port 8200

Если видим: «WARNING: Inotify max_user_watches [8192] is low.», необходимо увеличить число дескрипторов слежения inotify до 100 000. Для этого в файл /etc/sysctl.conf добавим строки:

#MiniDLNA warning fix fs.inotify.max_user_watches = 100000

Вручную редактором:

sudo nano /etc/sysctl.conf

Или копипастом команды:

sudo sh -c 'printf "\n\n#MiniDLNA warning fix\nfs.inotify.max_user_watches = 100000\n" >> /etc/sysctl.conf && cat /etc/sysctl.conf'

Изменение параметра вступит в силу после перезагрузки системы.
Открываем в браузере адрес.сервера:8200, видим количество файлов в библиотеке, начиная с версии 1.1.2, также отображается список подключенных клиентов.
Настройка DLNA/UPnP-AV сервера завершена.

При появлении ошибок в каталоге, необходимо выполнить повторное сканирование файлов.
При использовании приведенного выше init-скрипта, для запуска сканирования медиа-библиотеки используем команду:

sudo service minidlna force-reload

Служба при этом будет перезапущена, подключенные клиенты будут отключены.

Для сканирования медиа-библиотеки при каждом старте системы можно задать ключ запуска: "-R" в параметре DAEMON_OPTS в файле /etc/default/minidlna. Сканирование большой медиа-библиотеки существенно нагружает диск, что может замедлить загрузку системы.

Полное формирование каталога может занять несколько минут. Сканирование папок выполняется в порядке их объявления в конфиге. Файлы из небольших папок, объявленных в конфиге выше, появятся в каталоге в самом начале процесса сканирования. Крупные папки с редко воспроизводимым контентом имеет смысл размещать в конфиге последними.

Все это опробовано на своей системе, пользуйтесь на здоровье. Надеюсь, количество пользователей Linux будет постоянно увеличиваться.

По материалам http://itadept.ru/, wiki.archlinux.org.

ссылка на оригинал статьи http://habrahabr.ru/post/231653/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *