Onkyo ISCP / eISCP: Gestion des appareils Onkyo sur le réseau

Je suis sûr que de nombreux lecteurs de Habr connaissent, ou du moins ont entendu parler, de l'équipement audio Onkyo. Les lecteurs réseau modernes et les récepteurs A / V ont Linux à bord, ainsi que la possibilité de connexions réseau filaires / sans fil. Onkyo fournit son application mobile propriétaire pour le contrôle à distance d'un tel appareil - Onkyo Controller . Il n'y a pratiquement aucune information sur le fonctionnement de cette application - il y a des miettes sur les forums, ainsi que plusieurs projets sur github.



Mais vous pouvez trouver sur le réseau une description du protocole eISCP (Integra Serial Communication Protocol over Ethernet), qui sous-tend cette application. Le protocole est intéressant. Sur Habré, aucun article sur ce protocole n'a pu être trouvé. D'une part, il n'y a rien de tragique à cela, car cette propriété semble ne pas être utilisée ailleurs qu'à Onkyo. D'un autre côté, il y a une chance qu'il y ait des passionnés qui veulent diriger leur propre joueur ou récepteur Onkyo par eux-mêmes. En outre, l'article peut intéresser ceux qui, par pure curiosité théorique, collectent des connaissances sur divers protocoles réseau. Si vous êtes intéressé, s'il vous plaît, sous cat.

Il y a peu d'informations officielles sur le sujet de l'article. Par conséquent, je ne compterai pas seulement sur la documentation trouvée, car elle ne décrit que les commandes de protocole, mais ne dit rien sur les caractéristiques de leur utilisation. Nous avons réussi à obtenir beaucoup d'informations à partir de l'analyse du trafic réseau à l'aide de tcpdump / Wireshark, ainsi que de l'étude du firmware des appareils. C'est là que je vais commencer.

Le modèle spécifique de mon appareil n'a pas d'importance. Je peux seulement dire qu'il s'agit d'un lecteur réseau, similaire à celui de l'image pour attirer l'attention. Il peut lire de la musique non seulement à partir de supports USB externes, mais également à partir d'un serveur de musique (DLNA), et prend également en charge la radio Internet et les services de streaming tels que Spotify, Deezer et autre chose. Naturellement, le protocole doit supporter toute cette diversité.

Analyse de port


Afin de commencer à poser les bonnes questions dans un moteur de recherche, j'ai d'abord dû comprendre quel type de protocole était généralement utilisé. Autrement dit, la première étape est l'analyse des ports. Ainsi, l'appareil est sur le réseau, son adresse est 192.168.1.80. Analysez toute la gamme de ports:

> 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 

Beaucoup de choses intéressantes sont découvertes:

  • 80 / TCP est clair - c'est la page de configuration de l'appareil. Dans mon modèle, il n'y a que la configuration du réseau et la mise à niveau du firmware. Il n'y a aucun contrôle de lecture. Grâce à lui, grâce à des liens dynamiques du formulaire "http://192.168.1.80/album_art.cgi", vous pouvez accéder à l'image de la piste en cours de lecture.
  • 4545 / TCP - est apparu après la dernière mise à jour du firmware. Nmap ne sait rien de lui. Lorsque vous essayez de vous connecter, il envoie immédiatement json avec l'état de lecture actuel et envoie une mise à jour toutes les secondes

    Bloc de données avec état de lecture
     { "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" } 


    Comme je l'ai dit, ce port est apparu avec la dernière mise à jour. Il n'y a aucune documentation du mot. Il peut être utile pour développer un panneau de commande léger. Mais dans ce sens je n'ai pas encore creusé.
  • 5000 / tcp - nmap le définit comme Apple AirTunes. Cela semble être vrai, car la prise en charge de ce protocole est indiquée dans la documentation.
  • 8008 / tcp, 8009 / tcp - le but n'est pas clair, nmap n'en sait rien.
  • 8080 / tcp - http-proxy, dont le but dans le contexte de ce lecteur n'est pas entièrement clair.
  • 8888 / tcp - port de protocole Universal Plug and Play (UpnP) . À l'aide de l'utilitaire gupnp-universal-cp du package gupnp-tools, vous pouvez afficher sa description:

    Au début, on pensait que la gestion de l'application officielle était mise en œuvre sur la base de ce protocole particulier. Comme il s'est avéré plus tard, il s'est trompé. J'ai également essayé plusieurs clients UpnP, à la fois mobiles et de bureau. Tous ne fonctionnent pratiquement pas: certaines commandes de contrôle fonctionnent, d'autres non, et c'est complètement chaotique.
  • 10001 / tcp - similaire au port de configuration SCP, mais la façon de l'utiliser n'est pas claire.
  • 60128 / tcp - et, enfin, le personnage principal de cet article, le port du protocole eISCP. Nmap ne sait rien de lui non plus. Ouvrons le voile du secret.

Analyse du trafic


Vérifions maintenant quel port et comment l'application officielle communique avec l'appareil. La façon la plus simple de le faire est sur certains Android rootés (mais pas dans l'émulateur, car l'application officielle nécessite un périphérique géré sur le même sous-réseau local). Pour ce faire:

  • Installer Android tcpdump sur un appareil Android sur lequel Onkyo Controller est déjà installé
  • Nous allons sur l'appareil Android via adb en tant que root:

     > adb root && adb shell root@fiber-bs1078:/> 
  • accédez à n'importe quel répertoire de la carte SD intégrée:

     root@fiber-bs1078:/> cd /sdcard/work root@fiber-bs1078:/sdcard/work> 
  • exécuter tcpdump (avec écriture dans un fichier)

     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 
  • Nous lançons l'application Onkyo, et à partir de là, nous commençons à jouer de la musique
  • Lorsque quelques centaines de paquets sont tapés, arrêtez tcpdump par Cttl + C
  • Nous retournons au terminal, où nous avons lancé ADB et copions le fichier sur un ordinateur de travail

     root@fiber-bs1078:/sdcard/work> exit > adb pull /sdcard/work/onkyo.dump . [100%] /sdcard/work/onkyo.dump 
  • Lancez Wirehark et voyez ce qui s'y passe.

     > wireshark onkyo.dump & 

Et en effet, la communication passe sur le port 60128. Par exemple, l'application envoie une demande au joueur:



Et il lui répond:



Nous arrivons donc à l'essence même de l'article, à savoir: qu'est-ce que dans les images ci-dessus pour de telles lettres - ISCP? Cette abréviation signifie Integra Serial Control Protocol, initialement conçu pour contrôler les appareils Onkyo via le port RS-232 (il y a un vieil article intéressant à ce sujet). Plus tard, il a été étendu en ajoutant le préfixe «e» et il s'est avéré que eISCP - Integra Serial Communication Protocol over Ethernet. Les deux versions du protocole sont décrites dans la documentation technique: Protocole de communication série intégré pour récepteur AV. La première version du document est datée du 31 octobre 2012, la dernière qui a été trouvée est le 4 septembre 2017. Toutes les versions que j'ai trouvées sont rassemblées dans mon référentiel du projet de démonstration , dont je parlerai plus tard. La présentation ultérieure sera basée à la fois sur ce document et sur des expériences avec mon lecteur (qui, cependant, ne sont pas explicitement mentionnées dans ce document).

Spécification du message


Le client (par exemple, une application mobile) et l'appareil échangent de courts messages texte. Si des nombres y sont présents, ils sont présentés, en règle générale, sous forme hexadécimale.

Le format de message du client vers l'appareil est très simple:



Le message commence par un caractère "!", Puis le code du périphérique cible, suivi de trois lettres de la commande, puis d'une chaîne de paramètres de longueur arbitraire. Se termine par un CR (0x0D) ou un LF (0x0A), ou une combinaison de CR + LF.

Selon la commande, l'appareil répond soit avec le même type de message (s'il s'agissait d'une demande pour un paramètre), soit avec un type différent ou même une combinaison de messages (s'il s'agissait d'une commande pour une action complexe telle que la commutation d'une piste). Le format des messages envoyés par l'appareil au client est le même. La différence est uniquement dans le dernier octet:



Le document de description du protocole contient plus d'une centaine de commandes différentes, mon appareil prend en charge un peu plus de 30 commandes de ce document. Autrement dit, à la fois l'ensemble des commandes et les paramètres valides dépendent du périphérique spécifique.

Les commandes peuvent être regroupées en groupes logiques. À titre d'exemple, je voudrais souligner ceux-ci:

  1. Gestion générale des appareils:
    • NDN: nom de l'appareil.
    • UPD: vérification et installation d'une mise à jour du firmware.
    • PWR: mise sous / hors tension.
    • NRI: informations étendues sur l'appareil.
    • NTC: commandes d'une télécommande standard (y compris la commande de lecture).
    • CAP: commandes pour contrôler un amplificateur externe connecté au connecteur RI.
  2. Informations sur la piste en cours de lecture:
    • NAL: nom de l'album.
    • NAT: nom de l'artiste.
    • NTI: nom de piste.
    • NFI: informations sur le fichier de suivi (format, débit binaire).
    • NJA: une image attachée à une piste (par exemple, l'emblème d'une station de radio si la radio Internet est sélectionnée).
    • NTM: position temporelle actuelle dans la piste.
    • NTS: état, rembobinage activé ou non (pour la radio Internet, par exemple, non autorisé).
    • NST: contrôle de lecture répétée et aléatoire.
  3. Navigation et gestion de la bibliothèque musicale:

    • SLI: sélection de la source (par exemple USB, services réseau).
    • NSV: sélectionnez un service réseau spécifique (par exemple, radio Internet, serveur de musique). La liste de lecture sur mon appareil concerne également les services réseau, bien que cela ne soit pas tout à fait évident du point de vue de l'interface utilisateur. Et lorsque vous coupez l'alimentation (en tirant sur la prise), cette liste de lecture est supprimée!
    • NLT, NLA: navigation dans les sections (dossiers) de la bibliothèque.
    • PQA, PQR, PQO: gestion de playlist: ajouter, supprimer, réorganiser.

Naturellement, cette liste est loin d'être complète, je ne l'ai donnée que pour montrer la portée et les capacités de ce protocole.

En termes de paramètres, tous les messages peuvent être divisés en deux groupes. Le premier groupe comprend la plupart des messages. Pour ce groupe, la chaîne de paramètres contient des données sous forme alphanumérique ou hexadécimale et est analysée par octet. Par exemple, lors du passage au service TuneIn Radio, le lecteur envoie un message NLT - des informations sur l'en-tête de la liste actuelle avec le paramètre "0E01000000090100FF0E00TuneIn Radio", qui, décodé conformément aux spécifications, donne ces informations:

 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 

Presque tous les messages ont le paramètre «QSTN», par exemple, «! 1NLTQSTN». Cette demande signifie une demande au joueur de retourner les informations d'état actuelles correspondant à ce type de message. Cela fonctionne presque toujours, mais il existe de rares exceptions lorsque le joueur, selon son humeur interne, ignore ces demandes.

Le deuxième groupe est constitué des messages, où le paramètre est XML, qui doit être analysé à l'aide d'un analyseur XML. Dans l'exemple ci-dessus, dans la section Radio TuneIn, vous pouvez envoyer une demande NLA à laquelle les informations sur la liste active en XML seront répondues:

 <?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> 

Autrement dit, le lecteur fournit non seulement des informations textuelles (qui, soit dit en passant, sont actuellement affichées sur l'écran du lecteur), mais conseille également une icône adéquate (dossier, piste musicale, piste en cours de lecture).

Dans certains cas, le joueur souhaite afficher un message texte dans l'application cliente ou demander des paramètres supplémentaires tels que le nom d'utilisateur. Pour ce faire, le joueur envoie un message universel NCP (dialogue universel), où le XML décrit la structure de ce qui doit être montré à l'utilisateur:

 <?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> 

En réponse, le joueur attend le même message avec les champs remplis (ou le bouton pressé).

Également au format XML, un message NRI assez important est présenté - des informations générales sur le lecteur. Le message est assez grand, donc je le cache sous le spoiler.

Informations générales sur le joueur
 <?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> 


L'ensemble des commandes que vous devrez utiliser pour contrôler le périphérique dépend en grande partie de ce qui se trouve dans les sections zonelist et controllist de ce message.

ISCP sur Ethernet (eISCP)


Les messages sous la forme, comme je l'ai écrit ci-dessus, sont destinés à être transmis par câble (RS-232). Les récepteurs plus anciens étaient équipés pour cela d'un connecteur RS-232 à 9 broches. Lorsqu'au lieu de ce connecteur, ils ont commencé à utiliser une connexion réseau (filaire ou sans fil), nous avons dû encapsuler ces messages dans un wrapper pour les transmettre via TCP / IP. Ainsi, le protocole eISCP est apparu, où le message ISCP est enveloppé dans un tel package:



Sous le spoiler se trouve le code de la procédure qui génère complètement un tel package pour un message avec un code donné (code variable), une chaîne de paramètres (paramètres variables) et une version de protocole donnée (version variable). La procédure étant assez simple, il me semble que le code Java en dira beaucoup plus que mille mots.

Procédure de génération d'un message 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; } 


Si quelqu'un est intéressé, voici mon exemple d'implémentation de protocole pour la version de spécification 1.40 . Je donnerai également un lien vers ce référentiel . Il implémente une bibliothèque de messages et un utilitaire de ligne de commande en Python, ainsi que des liens vers d'autres projets similaires.

Mise en œuvre de l'échange d'informations


Les messages eux-mêmes, initialement conçus pour être transmis sur un câble à faible vitesse, sont assez petits. De plus, le joueur lui-même est également plutôt modeste - dans le contexte d'une énorme quantité de statistiques envoyées quelque part au serveur Amazon conditionnel, la quantité d'informations que le joueur donne volontairement au client via ISCP est juste peu abondante. Dans la spécification du protocole, il n'y a pas un mot sur quand et dans quelles conditions le joueur envoie telle ou telle information. Par conséquent, j'ai dû m'expérimenter suffisamment longtemps pour que le client mobile dispose toujours de toutes les informations nécessaires sur l'état actuel de l'appareil.

En général, la communication avec le joueur est basée sur un schéma de demande / réponse. De plus, dans certaines situations, une seule demande ne sera pas limitée. Il y a plusieurs événements clés pour mon joueur qui doivent être gérés:

  • Établissez une connexion. Au moment de la connexion, le lecteur peut être en mode veille ou allumé, peut être en mode lecture ou pause. Il est également important de savoir immédiatement dans quelle position se trouve le commutateur de canal d'entrée - sur les services réseau ou USB.

    Par conséquent, immédiatement après l'établissement de la connexion, il est logique d'envoyer des demandes PWR (actives ou en veille), UPD (y a-t-il une mise à jour du firmware), NRI (informations générales sur l'appareil), SLI (position du commutateur d'entrée), NJA (mode de transmission de l'image de la piste - par lien ou flux). Mon lecteur envoie spécifiquement le statut de lecture et la position actuelle de sa propre initiative.
  • Démarrez la lecture. Dans cette situation, le joueur envoie toutes les informations sur la piste. Mais lors de la connexion, lorsque le joueur joue déjà quelque chose, le joueur n'envoie rien. De plus, lorsque le joueur change de piste, toutes les informations ne sont pas envoyées.

    Une solution universelle, quoique consommatrice de ressources, consistait à suivre le message NST (état de lecture), et si cet état passait à «Lecture», puis à envoyer immédiatement 7 demandes: NAT (artiste), NAL (titre d'album), NTI (titre de piste), NFI (informations sur le fichier), NTR (numéro de plage), NTM (durée de lecture actuelle), NMS (menu plage). Il y a des fonctionnalités dans le firmware du lecteur. Par exemple, lors de la lecture d'une liste de lecture, le lecteur ne souhaite pas divulguer le numéro de la piste en cours de lecture. Mais en général, vous pouvez apprendre suffisamment en détail l'état actuel de la lecture.
  • ( ) . - . , . — 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)] 


    , . , , , .


À titre d'exemple, je vais donner un lien vers mon référentiel avec l'application Android pour le contrôle à distance du lecteur Onkyo NS-6130. Il est possible que cela fonctionne également avec l'Onkyo NS-6170. Mais vous ne pourrez pas l'utiliser avec un récepteur Onkyo, car toute l'interface de l'application est spécialement conçue pour lire et gérer la bibliothèque musicale, qui, en règle générale, ne se trouve pas sur les récepteurs. Par conséquent, je n'ai pas l'intention de distribuer cette application, je n'en parle ici qu'à titre d'exemple de mise en œuvre de ce protocole.

La structure de l'application est simple, le design est minimaliste. Il n'y a que trois onglets:

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









Contrairement à une application propriétaire, elle est 10 fois plus petite, plus réactive, prend en charge l'orientation de l'écran paysage et divers thèmes de conception. Il couvre complètement l'ensemble de mes tâches, bien qu'il y en ait, et où s'étendre. Cependant, contrairement à une application propriétaire, elle n'est pas universelle.

Si après avoir lu cet article, l'un des propriétaires d'appareils Onkyo veut expérimenter sa copie, j'espère que ce matériel et mon exemple d'application abaisseront le seuil d'entrée dans le sujet.

Merci de votre attention!

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


All Articles