Onkyo ISCP / eISCP: Mengelola perangkat Onkyo melalui jaringan

Saya yakin bahwa banyak pembaca Habr tahu, atau setidaknya pernah mendengar, tentang peralatan audio Onkyo. Pemain jaringan modern dan penerima A / V memiliki Linux, juga kemampuan untuk koneksi jaringan kabel / nirkabel. Onkyo menyediakan aplikasi seluler miliknya untuk kendali jarak jauh perangkat semacam itu - Pengendali Onkyo . Praktis tidak ada informasi tentang cara kerja aplikasi ini - ada remah-remah di forum, serta beberapa proyek di github.



Tetapi Anda dapat menemukan di jaringan deskripsi protokol Protokol Komunikasi Serial atas Ethernet (eISCP), yang mendasari aplikasi ini. Protokolnya menarik. Tentang Habré, tidak ada satu artikel pun tentang protokol ini yang dapat ditemukan. Di satu sisi, tidak ada yang tragis tentang ini, karena kepemilikan ini tampaknya tidak digunakan di mana pun kecuali Onkyo. Di sisi lain, ada kemungkinan bahwa ada penggemar yang ingin mengarahkan pemain mereka sendiri atau penerima Onkyo sendiri. Juga, artikel tersebut mungkin menarik bagi mereka yang, semata-mata karena keingintahuan teoretis, mengumpulkan pengetahuan tentang berbagai protokol jaringan. Jika tertarik, silakan, di bawah kucing.

Ada sedikit informasi resmi tentang topik artikel. Oleh karena itu, saya akan mengandalkan tidak hanya pada dokumentasi yang ditemukan, karena hanya menjelaskan perintah protokol, tetapi tidak mengatakan apa-apa tentang fitur penggunaannya. Kami berhasil mendapatkan banyak informasi dari analisis lalu lintas jaringan menggunakan tcpdump / wireshark, serta studi tentang firmware perangkat. Di sinilah saya akan mulai.

Model spesifik perangkat saya tidak penting. Saya hanya bisa mengatakan bahwa ini adalah pemain jaringan, mirip dengan yang ada di gambar untuk menarik perhatian. Ini dapat memainkan musik tidak hanya dari USB-carrier eksternal, tetapi juga dari server musik (DLNA), dan juga mendukung radio Internet dan layanan streaming seperti Spotify, Deezer dan yang lainnya. Secara alami, protokol harus mendukung semua keragaman ini.

Analisis pelabuhan


Untuk mulai mengajukan pertanyaan yang tepat di mesin pencari, saya harus terlebih dahulu memahami protokol apa yang umumnya digunakan. Artinya, langkah pertama adalah analisis pelabuhan. Jadi, perangkat ada di jaringan, alamatnya 192.168.1.80. Pindai seluruh jajaran port:

> nmap -sS -p0-65535 -T5 192.168.1.80 PORT STATE SERVICE 80/tcp open http 4545/tcp open worldscores 5000/tcp open upnp 8008/tcp open http 8009/tcp open ajp13 8080/tcp open http-proxy 8888/tcp open sun-answerbook 10001/tcp open scp-config 60128/tcp open unknown 

Banyak hal menarik yang ditemukan:

  • 80 / tcp jelas - ini adalah halaman pengaturan perangkat. Dalam model saya, hanya ada pengaturan jaringan dan peningkatan firmware. Tidak ada kontrol pemutaran. Melalui itu, melalui tautan dinamis seperti "http://192.168.1.80/album_art.cgi", Anda dapat mengakses gambar trek yang sedang diputar.
  • 4545 / tcp - muncul setelah pembaruan firmware terbaru. Nmap tidak tahu apa-apa tentang dia. Saat mencoba terhubung, ia segera mengirim json dengan status pemutaran saat ini dan mengirimkan pembaruan setiap detik

    Blok data dengan status pemutaran
     { "data": { "fireCast": false, "status": { "duration": 224893, "playBytes": 0 }, "error": "", "matchingMediaRoles": [], "controls": { "previous": true, "next_": true, "seekBytes": true, "seekTime": true, "pause": true, "seekTrack": true }, "mediaRoles": { "title": "", "asciiTitle": "" }, "playId": { "systemMemberId": "Onkyo NS-6130", "timestamp": 447085 }, "state": "playing", "trackRoles": { "mediaData": { "metaData": { "artist": "Ottawan", "album": "Greatest Hits", "serviceID": "Storage_usb2" } }, "title": "Shalala-Song", "flags": { "file": true }, "path": "storage_file_usb2:sda-94DB-FB8F/flac/Disco/Ottawan/Greatest Hits (2007)/05-Shalala-Song.flac", "optPlayingConentInfo": { "playingTrackTotal": 17, "playingTrackNo": 4 }, "icon": "file:///tmp/temp_data_albumArt_3c70a403584dc761cabc88ac0dfbb95c", "type": "audio" } }, "playTime": { "i64_": 139021, "type": "i64_" }, "senderVolume": {}, "senderMute": {}, "sender": "Onkyo-NS-6130-E1EE7F" } 


    Seperti yang saya katakan, port ini muncul dengan pembaruan terbaru. Tidak ada dokumentasi dari kata sama sekali. Ini mungkin berguna untuk mengembangkan panel kontrol yang ringan. Tetapi ke arah ini saya belum menggali.
  • 5000 / tcp - nmap mendefinisikannya sebagai Apple AirTunes. Tampaknya benar, karena dukungan untuk protokol ini dinyatakan dalam dokumentasi.
  • 8008 / tcp, 8009 / tcp - tujuannya tidak jelas, nmap tidak tahu apa-apa tentang mereka.
  • 8080 / tcp - http-proxy, yang tujuannya dalam konteks pemain ini tidak sepenuhnya jelas.
  • 8888 / tcp - port protokol Universal Plug and Play (UpnP) . Dengan menggunakan utilitas gupnp-universal-cp dari paket gupnp-tools, Anda dapat melihat deskripsinya:

    Pada awalnya dianggap bahwa manajemen dalam aplikasi resmi diimplementasikan berdasarkan protokol khusus ini. Ternyata kemudian, dia salah. Saya juga mencoba beberapa klien UpnP, baik ponsel maupun desktop. Semuanya praktis tidak berfungsi: beberapa perintah kontrol berfungsi, sebagian tidak, dan benar-benar kacau.
  • 10001 / tcp - mirip dengan port konfigurasi SCP, tetapi cara menggunakannya tidak jelas.
  • 60128 / tcp - dan, akhirnya, karakter utama artikel ini, port protokol eISCP. Nmap juga tidak tahu apa-apa tentang dia. Mari kita buka tabir kerahasiaan.

Analisis lalu lintas


Sekarang mari kita periksa port mana dan bagaimana aplikasi resmi berkomunikasi dengan perangkat. Cara termudah untuk melakukan ini adalah pada beberapa Android yang di-rooting (tetapi tidak di emulator, karena aplikasi resmi memerlukan perangkat yang dikelola pada subnet lokal yang sama). Untuk melakukan ini:

  • Instal Android tcpdump pada perangkat Android di mana Onkyo Controller sudah diinstal
  • Kami pergi ke perangkat Android melalui adb sebagai root:

     > adb root && adb shell root@fiber-bs1078:/> 
  • buka direktori apa pun pada kartu SD bawaan:

     root@fiber-bs1078:/> cd /sdcard/work root@fiber-bs1078:/sdcard/work> 
  • jalankan tcpdump (dengan tulis ke file)

     root@fiber-bs1078:/sdcard/work> tcpdump -vX -i any -w onkyo.dump host 192.168.1.80 tcpdump: listening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes 
  • Kami meluncurkan aplikasi Onkyo, dan dari sana kami mulai memutar musik
  • Ketika beberapa ratus paket diketik, hentikan tcpdump dengan Cttl + C
  • Kami kembali ke terminal, tempat kami meluncurkan ADB dan menyalin file ke komputer yang berfungsi

     root@fiber-bs1078:/sdcard/work> exit > adb pull /sdcard/work/onkyo.dump . [100%] /sdcard/work/onkyo.dump 
  • Luncurkan wireshark dan lihat apa yang terjadi di sana.

     > wireshark onkyo.dump & 

Dan memang, komunikasi berjalan di port 60128. Misalnya, aplikasi mengirimkan permintaan kepada pemain:



Dan dia menjawabnya:



Jadi kita sampai pada esensi artikel, yaitu: apa isinya pada gambar di atas untuk surat-surat seperti itu - ISCP? Singkatan ini berarti Integra Serial Control Protocol, awalnya dirancang untuk mengontrol perangkat Onkyo melalui port RS-232 (ada artikel lama yang menarik tentang hal ini). Kemudian diperluas dengan menambahkan awalan "e" dan ternyata eISCP - Integra Serial Communication Protocol melalui Ethernet. Kedua versi protokol dijelaskan dalam Dokumentasi Teknis: Protokol Komunikasi Serial Terintegrasi untuk Penerima AV. Versi pertama dokumen tertanggal 31 Oktober 2012, yang terakhir ditemukan adalah 4 September 2017. Semua versi yang saya temukan dikumpulkan di repositori proyek demo , yang akan saya bicarakan nanti. Presentasi lebih lanjut akan didasarkan pada dokumen ini dan percobaan dengan pemain saya (yang, bagaimanapun, tidak disebutkan secara eksplisit dalam dokumen ini).

Spesifikasi Pesan


Klien (misalnya, aplikasi seluler) dan perangkat bertukar pesan teks singkat. Jika angka ada di dalamnya, maka mereka disajikan, sebagai aturan, dalam bentuk heksadesimal.

Format pesan dari klien ke perangkat sangat sederhana:



Pesan dimulai dengan karakter “!”, Lalu kode perangkat target, diikuti oleh tiga huruf dari perintah, dan kemudian serangkaian parameter dengan panjang sewenang-wenang. Berakhir dengan CR (0x0D) atau LF (0x0A), atau kombinasi CR + LF.

Bergantung pada perintah, perangkat merespons dengan jenis pesan yang sama (jika itu permintaan untuk beberapa parameter), atau dengan jenis yang berbeda atau bahkan kombinasi pesan (jika itu adalah perintah untuk tindakan kompleks seperti mengganti trek). Format pesan yang dikirim oleh perangkat ke klien adalah sama. Perbedaannya hanya pada byte terakhir:



Dokumen uraian protokol berisi lebih dari seratus perintah berbeda, perangkat saya mendukung lebih dari 30 perintah dari dokumen ini. Yaitu, kedua set perintah dan parameter yang valid tergantung pada perangkat tertentu.

Perintah dapat dikelompokkan ke dalam kelompok logis. Sebagai contoh, saya akan menyoroti ini:

  1. Manajemen perangkat umum:
    • NDN: nama perangkat.
    • UPD: memeriksa dan menginstal pembaruan firmware.
    • PWR: daya hidup / mati.
    • NRI: informasi perangkat yang diperluas.
    • NTC: perintah dari remote control standar (termasuk kontrol playback).
    • CAP: perintah untuk mengontrol amplifier eksternal yang terhubung ke konektor RI.
  2. Informasi tentang trek yang sedang diputar:
    • NAL: nama album.
    • NAT: nama artis.
    • NTI: nama trek.
    • NFI: melacak informasi file (format, bit rate).
    • NJA: gambar yang dilampirkan ke trek (misalnya, lambang stasiun radio jika radio Internet dipilih).
    • NTM: posisi waktu saat ini di trek.
    • NTS: status, mundur diaktifkan atau tidak (untuk radio Internet, misalnya, tidak diizinkan).
    • NST: Ulangi dan Kontrol Putar Acak.
  3. Navigasi dan Manajemen Perpustakaan Musik:

    • SLI: pemilihan sumber (mis. USB, layanan jaringan).
    • NSV: Pilih layanan jaringan tertentu (mis. Radio Internet, server musik). Daftar putar pada perangkat saya juga terkait dengan layanan jaringan, meskipun ini tidak sepenuhnya jelas dari sudut pandang antarmuka pengguna. Dan ketika Anda mematikan daya (menarik keluar dari stopkontak), daftar putar ini dihapus!
    • NLT, NLA: navigasi melalui bagian (folder) perpustakaan.
    • PQA, PQR, PQO: manajemen daftar putar: tambah, hapus, susun ulang.

Secara alami, daftar ini jauh dari lengkap, saya berikan hanya untuk menunjukkan ruang lingkup dan kemampuan protokol ini.

Dalam hal parameter, semua pesan dapat dibagi menjadi dua kelompok. Grup pertama mencakup sebagian besar pesan. Untuk grup ini, string parameter berisi data dalam bentuk alfabet atau heksadesimal dan diuraikan oleh byte. Misalnya, ketika beralih ke layanan TuneIn Radio, pemain mengirim pesan NLT - informasi tentang tajuk daftar saat ini dengan parameter "0E01000000090100FF0E00TuneIn Radio", yang, yang diterjemahkan sesuai dengan spesifikasi, memberikan informasi seperti:

 SERVICE=TUNEIN_RADIO; UI=LIST; LAYER=SERVICE_TOP; CURSOR=0; ITEMS=9; LAYERS=1; START=NOT_FIRST; LEFT_ICON=NONE; RIGHT_ICON=TUNEIN_RADIO; STATUS=NONE; title=TuneIn Radio 

Hampir semua pesan memiliki parameter "QSTN", misalnya, "! 1NLTQSTN". Permintaan ini berarti permintaan kepada pemain untuk mengembalikan informasi status saat ini yang terkait dengan jenis pesan ini. Ini berfungsi hampir selalu, tetapi ada pengecualian langka ketika pemain, tergantung pada suasana hatinya, mengabaikan permintaan tersebut.

Grup kedua adalah pesan, di mana parameternya adalah XML, yang harus diuraikan menggunakan pengurai XML. Dari contoh di atas, saat berada di bagian TuneIn Radio, Anda dapat mengirim permintaan NLA ke mana informasi tentang daftar aktif dalam XML akan dijawab:

 <?xml version="1.0" encoding="utf-8"?> <response status="ok"> <items offset="0" totalitems="9"> <item icontype="F" iconid="29" title="My Presets" selectable="1" /> <item icontype="F" iconid="29" title="Local Radio" selectable="1" /> <item icontype="F" iconid="29" title="Music" selectable="1" /> <item icontype="F" iconid="29" title="Talk" selectable="1" /> <item icontype="F" iconid="29" title="Sports" selectable="1" /> <item icontype="F" iconid="29" title="By Location" selectable="1" /> <item icontype="F" iconid="29" title="By Language" selectable="1" /> <item icontype="F" iconid="29" title="Podcasts" selectable="1" /> <item icontype="F" iconid="29" title="Login" selectable="1" /> </items> </response> 

Artinya, pemain tidak hanya memberikan informasi tekstual (yang, kebetulan, saat ini ditampilkan di layar pemain itu sendiri), tetapi juga menyarankan ikon yang memadai (folder, trek musik, trek yang sedang diputar).

Dalam beberapa kasus, pemain ingin menampilkan pesan teks dalam aplikasi klien atau meminta parameter tambahan seperti nama pengguna. Untuk melakukan ini, pemain mengirim pesan universal NCP (dialog universal), di mana XML menjelaskan struktur yang ingin Anda tampilkan kepada pengguna:

 <?xml version="1.0" encoding="utf-8"?> <popup title="Try Deezer Premium+" align="center" type="custom" time="0" uri="resource:///popup"> <label title="" align="center" total="1" uri="resource:///popup/label:0"> <line text="Listening is limited to 30-second clips. Subscribe to enjoy unlimited music!" align="left" uri="resource:///popup/label/line:0" order="0" /> </label> <buttongroup title="" align="center" total="1" uri="resource:///popup/buttongroup:0"> <button text="OK" align="center" uri="/button:0" selected="false" index="0" www="" order="1" /> </buttongroup> </popup> 

Sebagai tanggapan, pemain mengharapkan pesan yang sama dengan isian yang terisi (atau tombol yang ditekan).

Juga dalam format XML pesan NRI yang agak penting disajikan - informasi umum tentang pemain. Pesannya cukup besar, jadi saya menyembunyikannya di bawah spoiler.

Informasi Umum Pemain
 <?xml version="1.0" encoding="utf-8"?> <response status="ok"> <device id="NS-6130"> <brand>ONKYO</brand> <category>NAP-O</category> <year>2016</year> <model>NS-6130</model> <destination>xx</destination> <macaddress>0009B0E1EE7F</macaddress> <modeliconurl>http://192.168.1.80/icon/OAVR_120.jpg</modeliconurl> <friendlyname></friendlyname> <firmwareversion>2110-0000-0000-0010-0000</firmwareversion> <ecosystemversion>200</ecosystemversion> <netservicelist count="9"> <netservice id="0e" value="1" name="TuneIn Radio" account="Username" password="Password" zone="01" enable="01" /> <netservice id="0a" value="1" name="Spotify" zone="01" enable="01" /> <netservice id="12" value="1" name="Deezer" account="Email address" password="Password" zone="01" enable="01" /> <netservice id="18" value="1" name="AirPlay" zone="01" enable="01" /> <netservice id="1b" value="1" name="TIDAL" account="Username" password="Password" zone="01" enable="01" /> <netservice id="00" value="1" name="Music Server" zone="01" enable="01" addqueue="1" sort="1" /> <netservice id="43" value="1" name="FlareConnect" zone="07" enable="0e" /> <netservice id="40" value="1" name="Chromecast built-in" zone="01" enable="01" /> <netservice id="1d" value="1" name="Play Queue" zone="01" enable="01" /> </netservicelist> <zonelist count="4"> <zone id="1" value="1" name="Main" volmax="0" volstep="0" src="1" dst="1" lrselect="0" /> <zone id="2" value="0" name="Zone2" volmax="0" volstep="0" src="0" dst="0" lrselect="0" /> <zone id="3" value="0" name="Zone3" volmax="0" volstep="0" src="0" dst="0" lrselect="0" /> <zone id="4" value="0" name="Zone4" volmax="0" volstep="0" src="0" dst="0" lrselect="0" /> </zonelist> <selectorlist count="3"> <selector id="2b" value="1" name="NET" zone="01" iconid="2b" /> <selector id="29" value="1" name="USB(F)" zone="01" iconid="29" addqueue="1" /> <selector id="2a" value="1" name="USB(R)" zone="01" iconid="2a" addqueue="1" /> </selectorlist> <presetlist count="40"> <preset id="01" band="0" freq="0" name="" /> <preset id="02" band="0" freq="0" name="" /> <preset id="03" band="0" freq="0" name="" /> <preset id="04" band="0" freq="0" name="" /> <preset id="05" band="0" freq="0" name="" /> <preset id="06" band="0" freq="0" name="" /> <preset id="07" band="0" freq="0" name="" /> <preset id="08" band="0" freq="0" name="" /> <preset id="09" band="0" freq="0" name="" /> <preset id="0a" band="0" freq="0" name="" /> <preset id="0b" band="0" freq="0" name="" /> <preset id="0c" band="0" freq="0" name="" /> <preset id="0d" band="0" freq="0" name="" /> <preset id="0e" band="0" freq="0" name="" /> <preset id="0f" band="0" freq="0" name="" /> <preset id="10" band="0" freq="0" name="" /> <preset id="11" band="0" freq="0" name="" /> <preset id="12" band="0" freq="0" name="" /> <preset id="13" band="0" freq="0" name="" /> <preset id="14" band="0" freq="0" name="" /> <preset id="15" band="0" freq="0" name="" /> <preset id="16" band="0" freq="0" name="" /> <preset id="17" band="0" freq="0" name="" /> <preset id="18" band="0" freq="0" name="" /> <preset id="19" band="0" freq="0" name="" /> <preset id="1a" band="0" freq="0" name="" /> <preset id="1b" band="0" freq="0" name="" /> <preset id="1c" band="0" freq="0" name="" /> <preset id="1d" band="0" freq="0" name="" /> <preset id="1e" band="0" freq="0" name="" /> <preset id="1f" band="0" freq="0" name="" /> <preset id="20" band="0" freq="0" name="" /> <preset id="21" band="0" freq="0" name="" /> <preset id="22" band="0" freq="0" name="" /> <preset id="23" band="0" freq="0" name="" /> <preset id="24" band="0" freq="0" name="" /> <preset id="25" band="0" freq="0" name="" /> <preset id="26" band="0" freq="0" name="" /> <preset id="27" band="0" freq="0" name="" /> <preset id="28" band="0" freq="0" name="" /> </presetlist> <controllist count="61"> <control id="Bass" value="0" zone="1" min="-10" max="10" step="2" /> <control id="Treble" value="0" zone="1" min="-10" max="10" step="2" /> <control id="Center Level" value="0" zone="1" min="-12" max="12" step="1" /> <control id="Subwoofer Level" value="0" zone="1" min="-15" max="12" step="1" /> <control id="Subwoofer1 Level" value="0" zone="1" min="-15" max="12" step="1" /> <control id="Subwoofer2 Level" value="0" zone="1" min="-15" max="12" step="1" /> <control id="Phase Matching Bass" value="0" /> <control id="LMD Movie/TV" value="0" code="MOVIE" position="1" /> <control id="LMD Music" value="0" code="MUSIC" position="2" /> <control id="LMD Game" value="0" code="GAME" position="3" /> <control id="LMD THX" value="0" code="04" position="4" /> <control id="LMD Stereo" value="0" code="00" position="4" /> <control id="LMD Direct" value="0" code="01" position="1" /> <control id="LMD Pure Audio" value="0" code="11" position="2" /> <control id="LMD Pure Direct" value="0" code="11" position="1" /> <control id="LMD Auto/Direct" value="0" code="AUTO" position="2" /> <control id="LMD Stereo G" value="0" code="STEREO" position="3" /> <control id="LMD Surround" value="0" code="SURR" position="4" /> <control id="TUNER Control" value="0" /> <control id="TUNER Freq Control" value="0" /> <control id="Info" value="2" /> <control id="Cursor" value="1" /> <control id="Home" value="0" code="HOME" position="2" /> <control id="Setup" value="1" code="MENU" position="2" /> <control id="Quick" value="0" code="QUICK" position="1" /> <control id="Menu" value="0" code="MENU" position="1" /> <control id="AMP Control(RI)" value="1" /> <control id="CD Control(RI)" value="1" /> <control id="CD Control" value="0" /> <control id="BD Control(CEC)" value="0" /> <control id="TV Control(CEC)" value="0" /> <control id="NoPowerButton" value="0" /> <control id="DownSample" value="0" /> <control id="Dimmer" value="1" /> <control id="time_hhmmss" value="1" /> <control id="Zone2 Control(CEC)" value="0" /> <control id="Sub Control(CEC)" value="0" /> <control id="NoNetworkStandby" value="0" /> <control id="NJAREQ" value="1" /> <control id="Music Optimizer" value="0" /> <control id="NoVideoInfo" value="1" /> <control id="NoAudioInfo" value="1" /> <control id="AV Adjust" value="0" /> <control id="Audio Scalar" value="0" /> <control id="Hi-Bit" value="0" /> <control id="Upsampling" value="0" /> <control id="Digital Filter" value="1" /> <control id="DolbyAtmos" value="0" /> <control id="DTS:X" value="0" /> <control id="MCACC" value="0" /> <control id="Dialog Enhance" value="0" /> <control id="PQLS" value="0" /> <control id="CD Control(NewRemote)" value="0" /> <control id="NoVolume" value="1" /> <control id="Auto Sound Retriever" value="0" /> <control id="Lock Range Adjust" value="0" /> <control id="P.BASS" value="0" /> <control id="Tone Direct" value="0" /> <control id="DetailedFileInfo" value="1" /> <control id="NoDABPresetFunc" value="0" /> <control id="S.BASS" value="0" /> </controllist> <functionlist count="10"> <function id="UsbUpdate" value="0" /> <function id="NetUpdate" value="1" /> <function id="WebSetup" value="1" /> <function id="WifiSetup" value="1" /> <function id="Nettune" value="0" /> <function id="Initialize" value="0" /> <function id="Battery" value="0" /> <function id="AutoStandbySetting" value="0" /> <function id="e-onkyo" value="0" /> <function id="UsbDabDongle" value="0" /> </functionlist> <tuners count="0"></tuners> </device> </response> 


Serangkaian perintah yang harus Anda gunakan untuk mengontrol perangkat, sebagian besar tergantung pada apa yang ada dengan bagian zonelist dan pengontrol pesan ini.

ISCP over Ethernet (eISCP)


Pesan dalam bentuk, seperti yang saya tulis di atas, dimaksudkan untuk pengiriman melalui kabel (RS-232). Receiver yang lebih tua dilengkapi untuk ini dengan konektor RS-232 9-pin. Ketika alih-alih konektor ini mereka mulai menggunakan koneksi jaringan (kabel atau nirkabel), kami harus membungkus pesan-pesan ini dalam pembungkus untuk transmisi melalui TCP / IP. Jadi protokol eISCP muncul, tempat pesan ISCP terbungkus dalam paket seperti itu:



Di bawah spoiler adalah kode prosedur yang sepenuhnya menghasilkan paket untuk pesan dengan kode yang diberikan (kode variabel), serangkaian parameter (parameter variabel) dan versi protokol yang diberikan (versi variabel). Karena prosedurnya cukup sederhana, menurut saya kode Java akan mengatakan lebih dari seribu kata.

Prosedur untuk menghasilkan pesan eISCP
 private final static int MIN_MSG_LENGTH = 22; private final static String MSG_START = "ISCP"; private final static Character START_CHAR = '!'; private final static int LF = 0x0A; ... byte[] getBytes() { if (headerSize + dataSize < MIN_MSG_LENGTH) { return null; } final byte[] bytes = new byte[headerSize + dataSize]; Arrays.fill(bytes, (byte) 0); // Message header for (int i = 0; i < MSG_START.length(); i++) { bytes[i] = (byte) MSG_START.charAt(i); } // Header size byte[] size = ByteBuffer.allocate(4).putInt(headerSize).array(); System.arraycopy(size, 0, bytes, 4, size.length); // Data size size = ByteBuffer.allocate(4).putInt(dataSize).array(); System.arraycopy(size, 0, bytes, 8, size.length); // Version bytes[12] = (byte) version; // CMD bytes[16] = (byte) START_CHAR.charValue(); bytes[17] = (byte) '1'; for (int i = 0; i < code.length(); i++) { bytes[i + 18] = (byte) code.charAt(i); } // Parameters for (int i = 0; i < parameters.length(); i++) { bytes[i + 21] = (byte) parameters.charAt(i); } // End char bytes[21 + parameters.length()] = (byte) LF; return bytes; } 


Jika ada yang tertarik, inilah contoh implementasi protokol saya untuk spesifikasi versi 1.40 . Saya juga akan memberikan tautan ke repositori ini . Ini mengimplementasikan pustaka pesan dan utilitas baris perintah dengan Python, serta tautan ke proyek serupa lainnya.

Mengimplementasikan Pertukaran Informasi


Pesan itu sendiri, awalnya dirancang untuk transmisi melalui kabel berkecepatan rendah, cukup kecil. Selain itu, pemain itu sendiri juga agak sederhana - dengan latar belakang sejumlah besar statistik yang dikirim ke suatu tempat ke server Amazon bersyarat, jumlah informasi yang pemain berikan secara sukarela kepada klien melalui ISCP hanya sedikit. Dalam spesifikasi protokol tidak ada kata tentang kapan dan dalam kondisi apa pemain mengirim informasi ini atau itu. Oleh karena itu, di sini saya harus bereksperimen sendiri cukup lama sehingga klien seluler selalu memiliki semua informasi yang diperlukan tentang keadaan perangkat saat ini.

Secara umum, komunikasi dengan pemain didasarkan pada skema permintaan / respons. Selain itu, dalam situasi tertentu, satu permintaan tidak akan dibatasi. Ada beberapa acara utama untuk pemain saya yang perlu ditangani:

  • Buat koneksi. Pada saat koneksi, pemain mungkin dalam mode siaga atau aktif, mungkin dalam mode pemutaran atau jeda. Penting juga untuk segera mencari tahu di posisi apa saklar saluran input berada - pada layanan jaringan atau USB.

    Oleh karena itu, segera setelah koneksi dibuat, masuk akal untuk mengirim permintaan PWR (aktif atau dalam keadaan siaga), UPD (apakah ada pembaruan firmware), NRI (informasi umum tentang perangkat), SLI (posisi sakelar input), NJA (mode transmisi gambar trek - oleh tautan atau streaming). Pemain saya secara khusus mengirimkan status pemutaran dan posisi saat ini atas inisiatifnya sendiri.
  • Mulai pemutaran. Dalam situasi ini, pemain mengirim semua informasi tentang trek. Tetapi ketika menghubungkan, ketika pemain sudah memainkan sesuatu, pemain tidak mengirim apa pun. Selain itu, ketika pemain mengganti trek, tidak semua informasi dikirim.

    Solusi universal, meskipun menghabiskan sumber daya, adalah untuk melacak pesan NST (status pemutaran), dan jika keadaan ini beralih ke "Play", maka segera kirim 7 permintaan: NAT (artis), NAL (judul album), NTI (judul lagu), NFI (informasi file), NTR (nomor trek), NTM (waktu pemutaran saat ini), NMS (menu trek). Ada fitur dalam firmware pemain. Misalnya, saat memainkan daftar putar, pemain tidak ingin memberikan jumlah trek yang sedang diputar. Namun secara umum, Anda dapat mempelajari secara cukup rinci kondisi pemutaran saat ini.
  • ( ) . - . , . — 14 !

     10-27 16:12:20.272: NLU[00080011; 8/17] 10-27 16:12:27.338: NTI[09-Roses Are Red.flac] 10-27 16:12:27.342: NAL[] 10-27 16:12:27.342: NAT[] 10-27 16:12:27.342: NDN[] 10-27 16:12:27.343: NJA/1937[2-...; TYPE=URL; PACKET=NOT_USED; URL=http://192.168.1.80/album_art.cgi; RAW(null)] 10-27 16:12:27.649: NMS[xxxxxS1f1; TRACK_MENU=DISABLE; POS_FEED=DISABLE; NEG_FEED=DISABLE; TIME_SEEK=ENABLE; TIME_DISPLAY=ELAPSED_TOTAL; ICON=USB_REAR] 10-27 16:12:27.649: NTR[0009; 0011] 10-27 16:12:27.649: NFI[/44.1kHz/16bit; FORMAT=; FREQUENCY=44.1kHz; BITRATE=16bit] 10-27 16:12:27.649: NLT[F1020000000B060002FF00Aquarium (1997); SERVICE=USB_REAR; UI=LIST; LAYER=UNDER_2ND_LAYER; CURSOR=0; ITEMS=11; LAYERS=6; START=NOT_FIRST; LEFT_ICON=USB; RIGHT_ICON=NONE; STATUS=NONE; title=Aquarium (1997)] 10-27 16:12:27.724: NLS[C0P; INF_TYPE=CURSOR; LINE_INFO=0; PROPERTY=NO; UPD_TYPE=PAGE; LIST_DATA=null] 10-27 16:12:27.727: NLS[U0-Happy Boys & Girls; INF_TYPE=UNICODE; LINE_INFO=0; PROPERTY=NO; UPD_TYPE=NO; LIST_DATA=Happy Boys & Girls] 10-27 16:12:27.734: NLS[U1-My Oh My; INF_TYPE=UNICODE; LINE_INFO=1; PROPERTY=NO; UPD_TYPE=NO; LIST_DATA=My Oh My] 10-27 16:12:27.737: NLS[U2-Barbie Girl; INF_TYPE=UNICODE; LINE_INFO=2; PROPERTY=NO; UPD_TYPE=NO; LIST_DATA=Barbie Girl] 10-27 16:12:27.740: NLS[U3-Good Morning Sunshine; INF_TYPE=UNICODE; LINE_INFO=3; PROPERTY=NO; UPD_TYPE=NO; LIST_DATA=Good Morning Sunshine] 10-27 16:12:27.760: NLA[X0002S000...; RESP=X; SEQ_NR=2; STATUS=S; UI=LIST; XML=<?xml version="1.0" encoding="utf-8"?><response status="ok"><items offset="0" totalitems="11" ><item icontype="M" iconid="2d" title="Happy Boys & Girls" selectable="1" /><item icontype="M" iconid="2d" title="My Oh My" selectable="1" /><item icontype="M" iconid="2d" title="Barbie Girl" selectable="1" /><item icontype="M" iconid="2d" title="Good Morning Sunshine" selectable="1" /><item icontype="M" iconid="2d" title="Doctor Jones" selectable="1" /><item icontype="M" iconid="2d" title="Heat Of The Night" selectable="1" /><item icontype="M" iconid="2d" title="Be A Man" selectable="1" /><item icontype="M" iconid="2d" title="Lollipop (Candyman)" selectable="1" /><item icontype="0" iconid="36" title="Roses Are Red" selectable="1" /><item icontype="M" iconid="2d" title="Turn Back Time" selectable="1" /><item icontype="M" iconid="2d" title="Calling You" selectable="1" /></items></response>] 10-27 16:12:29.697: NTI[Roses Are Red] 10-27 16:12:29.718: NJA/1952[2-...; TYPE=URL; PACKET=NOT_USED; URL=http://192.168.1.80/album_art.cgi; RAW(null)] 10-27 16:12:30.248: NAL[Aquarium] 10-27 16:12:30.248: NAT[Aqua] 10-27 16:12:30.248: NDN[] 10-27 16:12:30.248: NMS[xxxxxS1f1; TRACK_MENU=DISABLE; POS_FEED=DISABLE; NEG_FEED=DISABLE; TIME_SEEK=ENABLE; TIME_DISPLAY=ELAPSED_TOTAL; ICON=USB_REAR] 10-27 16:12:30.248: NTR[0009; 0011] 10-27 16:12:30.248: NFI[FLAC/44.1kHz/16bit; FORMAT=FLAC; FREQUENCY=44.1kHz; BITRATE=16bit] 10-27 16:12:30.248: NLT[F1020000000B060002FF00Aquarium (1997); SERVICE=USB_REAR; UI=LIST; LAYER=UNDER_2ND_LAYER; CURSOR=0; ITEMS=11; LAYERS=6; START=NOT_FIRST; LEFT_ICON=USB; RIGHT_ICON=NONE; STATUS=NONE; title=Aquarium (1997)] 10-27 16:12:30.248: NMS[xxxxxS1f1; TRACK_MENU=DISABLE; POS_FEED=DISABLE; NEG_FEED=DISABLE; TIME_SEEK=ENABLE; TIME_DISPLAY=ELAPSED_TOTAL; ICON=USB_REAR] 10-27 16:12:30.248: NFI[FLAC/44.1kHz/16bit; FORMAT=FLAC; FREQUENCY=44.1kHz; BITRATE=16bit] 10-27 16:12:30.248: NLT[F1020000000B060002FF00Aquarium (1997); SERVICE=USB_REAR; UI=LIST; LAYER=UNDER_2ND_LAYER; CURSOR=0; ITEMS=11; LAYERS=6; START=NOT_FIRST; LEFT_ICON=USB; RIGHT_ICON=NONE; STATUS=NONE; title=Aquarium (1997)] 10-27 16:12:34.815: NMS[xxxxxS1f1; TRACK_MENU=DISABLE; POS_FEED=DISABLE; NEG_FEED=DISABLE; TIME_SEEK=ENABLE; TIME_DISPLAY=ELAPSED_TOTAL; ICON=USB_REAR] 10-27 16:12:34.819: NFI[FLAC/44.1kHz/16bit; FORMAT=FLAC; FREQUENCY=44.1kHz; BITRATE=16bit] 10-27 16:12:34.860: NLT[F1020000000B060002FF00Aquarium (1997); SERVICE=USB_REAR; UI=LIST; LAYER=UNDER_2ND_LAYER; CURSOR=0; ITEMS=11; LAYERS=6; START=NOT_FIRST; LEFT_ICON=USB; RIGHT_ICON=NONE; STATUS=NONE; title=Aquarium (1997)] 


    , . , , , .


Sebagai contoh, saya akan memberikan tautan ke repositori saya dengan aplikasi Android untuk kendali jarak jauh pemain Onkyo NS-6130. Ada kemungkinan bahwa itu juga akan bekerja dengan Onkyo NS-6170. Tetapi Anda tidak akan dapat menggunakannya dengan penerima Onkyo mana pun, karena seluruh antarmuka aplikasi diarahkan khusus untuk memutar dan mengelola perpustakaan musik, yang, sebagai suatu peraturan, tidak ada pada penerima. Oleh karena itu, saya tidak punya rencana untuk mendistribusikan aplikasi ini, di sini saya menulis hanya sebagai contoh implementasi protokol ini.

Struktur aplikasi sederhana, desainnya minimalis. Hanya ada tiga tab:

  • . , . , RI Onkyo. , , , .









Tidak seperti aplikasi eksklusif, aplikasi ini 10 kali lebih kecil, lebih responsif, mendukung orientasi layar lanskap, dan berbagai tema desain. Ini mencakup seluruh jajaran tugas saya sepenuhnya, meskipun ada, dan di mana akan berkembang. Namun, juga tidak seperti aplikasi berpemilik, itu tidak universal.

Jika setelah membaca artikel ini, salah satu pemilik perangkat Onkyo ingin bereksperimen dengan salinannya, saya harap materi ini dan aplikasi sampel saya akan menurunkan ambang untuk memasukkan topik.

Terima kasih atas perhatian anda!

Source: https://habr.com/ru/post/id427985/


All Articles