Многие пользователи медиацентра KODI, входящего в состав RetroOrangePi, наверняка заметили, что там используется внешний плеер MPV, который, в отличии от штатного, имеет поддержку аппаратного декодирования. Это позволяет проигрывать контент 1080P без лагов, однако есть и большая проблема — единственный способ управлять плеером это клавиатура. Более того — у плеера нет даже минимального GUI, так что какой продолжительности фильм вы смотрите, и сколько еще осталось до конца — увы, не узнать.

Я, признаюсь, очень ленивый человек, и таскать за собой еще и клавиатуру, с учетом того, что сам KODI управляется с телефона — очень не удобно. Погуглив пару минут, я с удивлением узнал, что не существует готовых решений для удаленного управления MPV с смартфона. Ну что делать — придется написать самому. Кстати, как оказалось, в центре Витебска купить недорогую беспроводную клавиатуру за 1 час обеденного перерыва — тот еще квест.

MPV поддерживает несколько языков для написания скриптов, один из них Lua. На Гитхабе я нашел проект mpv-network-commands, написанный на Lua и позволяющий управлять минимум функций MPV, при помощи команд, передаваемых посредством UDP. Убедившись, что он работает, я во время обеденного перерыва написал простейшее приложение, которое реализовывало максимально примитивный функционал — по сути, оно позволяло поставить на паузу, запустить воспроизведение с паузы и закрыть плеер.


Выглядело оно примерно так.

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

function send_name()
local title =mp.get_property("media-title");
local length = math.floor(mp.get_property("length"));
local pos=math.floor(mp.get_property("time-pos"));
conn_up:sendto(title.."$"..length.."$"..pos, "192.168.100.168", 756);
end

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

И это еще без кода таймера
 thread {
            // this thread receives incoming massages from MPV and updates views accordingly to received info

            try {
                chnl.socket().bind(InetSocketAddress(756))

                chnl.configureBlocking(false)
                var timerStarted: Boolean = false
                var timer = Timer()
                while (true) {
                    var buf = ByteBuffer.allocate(1024)
                    buf.clear()
                    var last_pos = ""
                    if (!timerStarted) {
                        timer = Timer()
                        timer.schedule(myTimerTask(textView5, textView6, textView4, seekBar3), 2000, 1000)
                        //this timer will clear views, if no info received in last 2 seconds
                        timerStarted = true
                    }
                    if (chnl.isOpen) {
                        if (chnl.receive(buf) != null) {
                            if (timerStarted) {
                                timer.cancel()
                                timerStarted = false
                            }
                            var data_length = buf.position()
                            buf.flip()
                            var str = String(buf.array(), 0, data_length, UTF_8)
                            Log.d("received data", str)
                            val arr = str.split("$")
                            if (arr.size >= 3) {
                                media_length = arr[1].toInt()
                                runOnUiThread {
                                    if (textView4.text != arr[0]) textView4.text = arr[0]
                                    if (last_pos != arr[1]) {
                                        last_pos = arr[1]
                                        textView6.text = convertSecsToFullTime(last_pos)
                                    }
                                    if (!blockview) {
                                        textView5.text = convertSecsToFullTime(arr[2])
                                    seekBar3.max = arr[1].toInt()
                                        seekBar3.progress = arr[2].toInt()
                                    }

                                }
                            }
                        }
                    }
                }
            } catch (e: Exception) {
                runOnUiThread {
                    Toast.makeText(this, e.message, Toast.LENGTH_SHORT).show()
                }
                Log.d("In MPVremote", e.message.toString())
            }
        }


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


Исходные коды, а также apk, лежат на Гитхабе.

Сразу предупреждаю — могут быть точно есть баги.

Основной недостаток — требуется фиксированный IP для телефона в вашей локальной сети, в которой работает OrangePi. Он необходим, что бы скрипт знал, куда ему отправлять инфу о воспроизводимом файле. Самый простой способ — в настройках DHCP сервера вашего роутера указать выдавать один и тот же IP устройствам с МАК-адресом телефона.

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

Кстати, использование обмена данными через UDP безо всяких подтверждений порождает побочный эффект — поскольку прием входящих данных от плеера крутится в отдельном потоке, то вы можете ставить/снимать паузу и закрывать плеер, даже если не указали серверному скрипту правильный IP вашего телефона. Главное, что бы вы знали IP вашего медиацентра.

Установка

Перейдите в Desktop RetroOrangePi. Сначала надо установить Lua и LuaSocket.

sudo apt-get install lua5.1 luasocket

Затем перейдите в директорию /home/pi/.config/mpv/ и создайте там директорию lua.

В lua скопируйте server.lua.

Откройте server.lua тем же nano и в строке

conn_up:sendto(title.."$"..length.."$"..pos, "192.168.100.168", 756);

замените IP адрес на адрес вашего телефона.

Сохраните и перезагрузите OrangePi.

Установите приложение на телефон. Собранный apk лежит на Гитхабе. Запустите его, перейдите в Settings. Задайте там IP вашего медиацентра и порт 755. Нажмите Save.

Всё, можно пользоваться.

Комментарии (12)


  1. Laney1
    19.09.2018 15:53

    можно не писать велосипеды, а поставить плагин https://github.com/hoyon/mpv-mpris и рулить mpv с андроида через KDE Connect


    1. pavelchavyr
      19.09.2018 16:37

      Ставить KDE Connect, чтобы управлять плеером — из пушки по воробьям


      1. Laney1
        19.09.2018 17:04

        лолчто? Это небольшой демон, которому, несмотря на название, не нужен остальной KDE для работы. Его даже под windows можно собрать (хотя там уже вряд ли будет работать dbus-интерфейс). А еще там нет недостатков, описанных автором статьи


        1. pavelchavyr
          19.09.2018 17:29

          Не знаю, где как, а в Арче KDE Connect тянет несколько десятков пакетов зависимостей, которые после установки занимают на диске более 150 МБ. Скрипт на Lua явно менее требователен к ресурсам.


    1. BiW Автор
      19.09.2018 17:14

      Да. Я написал еще один велосипед. И?


      1. Laney1
        19.09.2018 17:26

        Погуглив пару минут, я с удивлением узнал, что не существует готовых решений для удаленного управления MPV с смартфона

        возможно, прежде чем бросаться писать статью, стоит погуглить немножко больше чем пару минут?


        1. BiW Автор
          20.09.2018 10:12

          Дорогой мой. Скрипт размером 3 кб, выполняющийся только пока выполняется плеер и 150-ти мегабайтная софтина с постоянно висящем в ОЗУ демоном — несколько разные вещие. Особенно на одноплатном ПК.
          Если вас не устраивает мое решение — проходите мимо, вас никто не заставляет им пользоваться. Используйте KDE connect, да что угодно. На OrangePi PC можно вообще использовать ИК-порт. У меня вообще такое впечатление, что вы решили, будто я вас под дулом автомата заставляю его использовать. Вы ошибаетесь, это не так.
          Ваша забота о ресурсах серверов хабра достойна похвалы.
          Большое спасибо за содержательный диалог.


  1. safari2012
    19.09.2018 17:43

    Как только вышла новая Raspberry Pi 2 (сколько то лет назад, я её купил на али за ~2100руб.), старую малину тут же продал на авито за 1500руб. Покупатель был не гик и в малинах не разбирался совсем, ему был нужен плеер, который жуёт всё и управляется от штатного пульта телевизора. Вот чем хороша стандартная малина, так это тем, что сама себя неплохо продаёт.

    А кубиборд я тоже покупал (стоил в два раза дороже), годится только на опыты.


    1. N0Good
      21.09.2018 02:40

      Собственно, OrangePi PC стоит в два раза дешевле малины, но требует чуть более грамотного пользователя. Возможности +- те же.


      1. safari2012
        21.09.2018 09:18

        Дьявол, как говорится, кроется в деталях. Судя по форумам 4pda и armbian, hdmi-cec толком так и не работает (кому-то удалось активировать включение/выключение телевизора и только). У меня детеныш уже в 4 года мог самостоятельно включать тв, выбирать и смотреть мультики с единого пульта.
        Но у малины есть версия мини ещё дешевле (можно за 6€ купить, если знать как). Со слабеньким процессором, но как full-hd медиаплеер, вполне годная. И никаких проблем с поддержкой кодека под linux и hdmi-cec.


        1. N0Good
          21.09.2018 18:59

          Так, как бы, насколько мне известно, именно у OrangePi PC с HDMI-CEC проблем нет — в образах LibreELEC работает из коробки. Ну, а для остальных — потому я и написал про более грамотного пользователя — есть патчи к ядру.


  1. RuK
    19.09.2018 18:43

    Иногда и велосипед написать хочется. Не вижу ничего страшного.