Пишем приложение для удаленного управления плеером MPV из RetroOrangePi

    Многие пользователи медиацентра 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
      0

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

      • НЛО прилетело и опубликовало эту надпись здесь
          0

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

          • НЛО прилетело и опубликовало эту надпись здесь
          –1
          Да. Я написал еще один велосипед. И?
            0
            Погуглив пару минут, я с удивлением узнал, что не существует готовых решений для удаленного управления MPV с смартфона

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

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

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

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

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

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

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

            Самое читаемое