Onkyo ISCP / eISCP: Gerenciando dispositivos Onkyo pela rede

Estou certo de que muitos dos leitores de Habr conhecem, ou pelo menos ouviram falar, sobre o equipamento de áudio da Onkyo. Os players de rede modernos e os receptores A / V possuem Linux integrado, bem como a capacidade de conexões de rede com ou sem fio. A Onkyo fornece seu aplicativo móvel proprietário para controle remoto de um dispositivo como esse - o Controlador Onkyo . Praticamente não há informações sobre como esse aplicativo funciona - existem migalhas nos fóruns, além de vários projetos no github.



Mas você pode encontrar na rede uma descrição do protocolo Integra Serial Communication Protocol via Ethernet (eISCP), subjacente a esse aplicativo. O protocolo é interessante. Em Habré, nenhum artigo sobre este protocolo foi encontrado. Por um lado, não há nada de trágico nisso, pois essa propriedade não é usada em nenhum lugar, exceto Onkyo. Por outro lado, há uma chance de que haja entusiastas que desejam dirigir seu próprio player ou receptor Onkyo por conta própria. Além disso, o artigo pode ser de interesse para aqueles que, puramente por curiosidade teórica, coletam conhecimento sobre vários protocolos de rede. Se estiver interessado, por favor, abaixo do gato.

Há pouca informação oficial sobre o tópico do artigo. Portanto, vou confiar não apenas na documentação encontrada, pois ela descreve apenas comandos de protocolo, mas não diz nada sobre os recursos de seu uso. Conseguimos obter muitas informações da análise do tráfego de rede usando o tcpdump / wireshark, bem como do estudo do firmware do dispositivo. É aqui que eu vou começar.

O modelo específico do meu dispositivo não é importante. Só posso dizer que este é um player de rede, semelhante ao da imagem para atrair atenção. Ele pode tocar música não apenas de portadoras USB externas, mas também de um servidor de música (DLNA), e também suporta serviços de rádio e streaming na Internet, como Spotify, Deezer e outras coisas. Naturalmente, o protocolo deve apoiar toda essa diversidade.

Análise de porta


Para começar a fazer as perguntas certas em um mecanismo de pesquisa, primeiro tive que entender que tipo de protocolo geralmente era usado. Ou seja, o primeiro passo é a análise de portas. Portanto, o dispositivo está na rede, seu endereço é 192.168.1.80. Digitalize toda a gama de portas:

> 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 

Muitas coisas interessantes são descobertas:

  • 80 / tcp está limpo - esta é a página de configuração do dispositivo. No meu modelo, há apenas a configuração da rede e a atualização do firmware. Não há controle de reprodução. Através dele, através de links dinâmicos como “http://192.168.1.80/album_art.cgi”, você pode acessar a imagem da faixa que está sendo reproduzida no momento.
  • 4545 / tcp - apareceu após a atualização de firmware mais recente. Nmap não sabe nada sobre ele. Ao tentar se conectar, ele envia imediatamente o json com o status de reprodução atual e envia uma atualização a cada segundo

    Bloco de dados com status de reprodução
     { "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" } 


    Como eu disse, essa porta apareceu com a atualização mais recente. Não existe documentação da palavra. Pode ser útil para o desenvolvimento de um painel de controle leve. Mas nessa direção ainda não cavei.
  • 5000 / tcp - nmap define como Apple AirTunes. Parece ser verdade, uma vez que o suporte a este protocolo está declarado na documentação.
  • 8008 / tcp, 8009 / tcp - o objetivo não é claro, o nmap não sabe nada sobre eles.
  • 8080 / tcp - http-proxy, cujo objetivo no contexto deste player não é totalmente claro.
  • 8888 / tcp - porta de protocolo Universal Plug and Play (UpnP) . Usando o utilitário gupnp-universal-cp do pacote gupnp-tools, você pode visualizar sua descrição:

    Inicialmente, pensou-se que o gerenciamento no aplicativo oficial fosse implementado com base nesse protocolo específico. Como se viu depois, ele estava enganado. Eu também tentei vários clientes UpnP, móveis e desktop. Todos eles praticamente não estão funcionando: alguns comandos de controle funcionam, outros não, e é completamente caótico.
  • 10001 / tcp - semelhante à porta de configuração do SCP, mas como usá-la não é clara.
  • 60128 / tcp - e, finalmente, o personagem principal deste artigo, a porta do protocolo eISCP. Nmap também não sabe nada sobre ele. Vamos abrir o véu do segredo.

Análise de tráfego


Agora vamos verificar qual porta e como o aplicativo oficial se comunica com o dispositivo. A maneira mais fácil de fazer isso é em um Android com raiz (mas não no emulador, pois o aplicativo oficial requer um dispositivo gerenciado na mesma sub-rede local). Para fazer isso:

  • Instale o Android tcpdump em um dispositivo Android em que o Onkyo Controlleresteja instalado
  • Vamos para o dispositivo Android via adb como root:

     > adb root && adb shell root@fiber-bs1078:/> 
  • vá para qualquer diretório no cartão SD embutido:

     root@fiber-bs1078:/> cd /sdcard/work root@fiber-bs1078:/sdcard/work> 
  • execute tcpdump (com gravação no arquivo)

     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 
  • Lançamos o aplicativo Onkyo e a partir daí começamos a tocar música
  • Quando algumas centenas de pacotes são digitados, pare o tcpdump por Cttl + C
  • Voltamos ao terminal, onde lançamos o ADB e copiamos o arquivo para um computador em funcionamento

     root@fiber-bs1078:/sdcard/work> exit > adb pull /sdcard/work/onkyo.dump . [100%] /sdcard/work/onkyo.dump 
  • Inicie o wireshark e veja o que acontece lá.

     > wireshark onkyo.dump & 

E, de fato, a comunicação continua na porta 60128. Por exemplo, o aplicativo envia uma solicitação ao jogador:



E ele responde:



Então chegamos à essência do artigo, a saber: o que há nas figuras acima para essas cartas - ISCP? Essa abreviação significa Integra Serial Control Protocol, originalmente projetado para controlar dispositivos Onkyo através da porta RS-232 (há um artigo interessante e antigo sobre esse assunto). Posteriormente, foi expandido adicionando o prefixo “e” e resultou no eISCP - Integra Serial Communication Protocol sobre Ethernet. Ambas as versões do protocolo estão descritas na documentação técnica: Protocolo de comunicação serial integrada para receptor AV. A primeira versão do documento é de 31 de outubro de 2012, a última encontrada foi em 4 de setembro de 2017. Todas as versões que encontrei são coletadas no meu repositório do projeto de demonstração , sobre o qual falarei mais adiante. Outras apresentações serão baseadas neste documento e em experimentos com meu player (que, no entanto, não são mencionados explicitamente neste documento).

Especificação da mensagem


O cliente (por exemplo, um aplicativo móvel) e o dispositivo trocam mensagens de texto curtas. Se houver números nele, eles serão apresentados, em regra, na forma hexadecimal.

O formato da mensagem do cliente para o dispositivo é muito simples:



A mensagem começa com um caractere "!", Depois o código do dispositivo de destino, seguido por três letras do comando e, em seguida, uma sequência de parâmetros de comprimento arbitrário. Termina com um CR (0x0D) ou LF (0x0A) ou uma combinação de CR + LF.

Dependendo do comando, o dispositivo responde com o mesmo tipo de mensagem (se for uma solicitação para algum parâmetro), ou com um tipo diferente ou mesmo uma combinação de mensagens (se for um comando para uma ação complexa, como alternar uma faixa). O formato das mensagens enviadas pelo dispositivo ao cliente é o mesmo. A diferença está apenas no último byte:



O documento de descrição do protocolo contém mais de uma centena de comandos diferentes. Meu dispositivo suporta um pouco mais de 30 comandos deste documento. Ou seja, o conjunto de comandos e os parâmetros válidos dependem do dispositivo específico.

Os comandos podem ser agrupados em grupos lógicos. Como exemplo, eu destacaria estes:

  1. Gerenciamento geral de dispositivos:
    • NDN: nome do dispositivo.
    • UPD: verificando e instalando uma atualização de firmware.
    • PWR: liga / desliga.
    • NRI: informações estendidas do dispositivo.
    • NTC: comandos de um controle remoto padrão (incluindo controle de reprodução).
    • CAP: comandos para controlar um amplificador externo conectado ao conector RI.
  2. Informações sobre a faixa que está sendo reproduzida:
    • NAL: nome do álbum.
    • NAT: nome do artista.
    • NTI: nome da faixa.
    • NFI: informações do arquivo da faixa (formato, taxa de bits).
    • NJA: uma imagem anexada a uma faixa (por exemplo, o emblema de uma estação de rádio se o rádio da Internet estiver selecionado).
    • NTM: posição atual na faixa.
    • NTS: status, rebobinamento ativado ou não (para rádio na Internet, por exemplo, não permitido).
    • NST: controle de reprodução repetida e aleatória.
  3. Navegação e Gerenciamento de Biblioteca de Música:

    • SLI: seleção de fonte (por exemplo, USB, serviços de rede).
    • NSV: selecione um serviço de rede específico (por exemplo, rádio na Internet, servidor de música). A lista de reprodução no meu dispositivo também se refere a serviços de rede, embora isso não seja totalmente óbvio do ponto de vista da interface do usuário. E quando você desliga a energia (retirando da tomada), esta lista de reprodução é excluída!
    • NLT, NLA: navegação pelas seções (pastas) da biblioteca.
    • PQA, PQR, PQO: gerenciamento de lista de reprodução: adicione, exclua, reordene.

Naturalmente, esta lista está longe de estar completa, eu a dei apenas para mostrar o escopo e as capacidades deste protocolo.

Em termos de parâmetros, todas as mensagens podem ser divididas em dois grupos. O primeiro grupo inclui a maioria das mensagens. Para esse grupo, a sequência de parâmetros contém dados em formato alfabético ou hexadecimal e é analisada por byte. Por exemplo, ao alternar para o serviço TuneIn Radio, o player envia uma mensagem NLT - informações sobre o cabeçalho da lista atual com o parâmetro "0E01000000090100FF0E00TuneIn Radio", que, sendo decodificado de acordo com a especificação, fornece essas informações:

 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 

Quase todas as mensagens têm o parâmetro "QSTN", por exemplo, "! 1NLTQSTN". Esta solicitação significa uma solicitação ao jogador para retornar as informações de status atuais correspondentes a este tipo de mensagem. Funciona quase sempre, mas há raras exceções quando o jogador, dependendo do seu humor interno, ignora esses pedidos.

O segundo grupo são mensagens, em que o parâmetro é XML, que deve ser analisado usando um analisador XML. No exemplo acima, na seção TuneIn Radio, você pode enviar uma solicitação NLA para a qual as informações sobre a lista ativa em XML serão respondidas:

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

Ou seja, o reprodutor não apenas fornece informações textuais (que, aliás, atualmente são exibidas no visor do reprodutor), mas também aconselha um ícone adequado (pasta, faixa de música, faixa atualmente em reprodução).

Em alguns casos, o player deseja exibir uma mensagem de texto no aplicativo cliente ou solicitar parâmetros adicionais, como nome de usuário. Para fazer isso, o player envia uma mensagem universal NCP (diálogo universal), onde o XML descreve a estrutura do que precisa ser mostrado ao usuário:

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

Em resposta, o jogador espera a mesma mensagem com os campos preenchidos (ou o botão pressionado).

Também no formato XML, é apresentada uma mensagem NRI bastante importante - informações gerais sobre o player. A mensagem é grande o suficiente, então eu a escondo sob o spoiler.

Informações Gerais do Jogador
 <?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> 


O conjunto de comandos que você precisará usar para controlar o dispositivo depende em grande parte do que está nas seções zonelist e controllerist desta mensagem.

ISCP sobre Ethernet (eISCP)


As mensagens no formulário, como escrevi acima, destinam-se à transmissão por cabo (RS-232). Os receptores mais antigos foram equipados com um conector RS-232 de 9 pinos. Quando, em vez desse conector, eles começaram a usar uma conexão de rede (com ou sem fio), tivemos que agrupar essas mensagens em um invólucro para transmissão por TCP / IP. Portanto, o protocolo eISCP apareceu, onde a mensagem ISCP está agrupada em um pacote:



Sob o spoiler, está o código do procedimento que gera completamente esse pacote para uma mensagem com um determinado código (código variável), uma sequência de parâmetros (parâmetros variáveis) e uma determinada versão de protocolo (versão variável). Como o procedimento é bastante simples, parece-me que o código Java dirá muito mais que mil palavras.

Procedimento para gerar uma mensagem 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; } 


Se alguém estiver interessado, aqui está o meu exemplo de implementação de protocolo para a versão 1.40 da especificação . Também darei um link para este repositório . Ele implementa uma biblioteca de mensagens e um utilitário de linha de comando no Python, além de links para outros projetos semelhantes.

Implementando o intercâmbio de informações


As próprias mensagens, originalmente projetadas para transmissão por cabo de baixa velocidade, são bastante pequenas. Além disso, o próprio jogador também é bastante modesto - no contexto de uma enorme quantidade de estatísticas enviadas para o servidor condicional da Amazon, a quantidade de informações que o jogador voluntariamente fornece ao cliente via ISCP é escassa. Na especificação do protocolo, não há uma palavra sobre quando e sob que condições o jogador envia esta ou aquela informação. Portanto, aqui eu tive que me testar por tempo suficiente para que o cliente móvel sempre tivesse todas as informações necessárias sobre o estado atual do dispositivo.

Em geral, a comunicação com o jogador é baseada em um esquema de solicitação / resposta. Além disso, em determinadas situações, uma única solicitação não será limitada. Existem vários eventos importantes para o meu player que precisam ser tratados:

  • Estabeleça uma conexão. No momento da conexão, o player pode estar no modo de espera ou ativado, pode estar no modo de reprodução ou pausa. Também é importante descobrir imediatamente em que posição o switch do canal de entrada está localizado - nos serviços de rede ou USB.

    Portanto, imediatamente após a conexão ser estabelecida, faz sentido enviar solicitações de PWR (ativo ou em espera), UPD (há uma atualização de firmware), NRI (informações gerais sobre o dispositivo), SLI (posição da chave de entrada), NJA (modo de transmissão da imagem da trilha - por link ou fluxo). Meu player envia especificamente o status de reprodução e a posição atual por sua própria iniciativa.
  • Inicie a reprodução. Nesta situação, o jogador envia todas as informações sobre a pista. Mas ao conectar, quando o jogador já está tocando alguma coisa, o jogador não envia nada. Além disso, quando o player muda de faixa, nem todas as informações são enviadas.

    Uma solução universal, embora demorada em recursos, era rastrear a mensagem NST (status de reprodução) e, se esse estado mudar para "Play", envie imediatamente 7 solicitações: NAT (artista), NAL (título do álbum), NTI (título da faixa), NFI (informações do arquivo), NTR (número da faixa), NTM (tempo atual de reprodução), NMS (menu da faixa). Existem recursos no firmware do player. Por exemplo, ao reproduzir uma lista de reprodução, o jogador não deseja divulgar o número da faixa que está sendo reproduzida. Mas, em geral, você pode aprender com detalhes suficientes o estado atual da reprodução.
  • ( ) . - . , . — 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)] 


    , . , , , .


Como exemplo, darei um link ao meu repositório com o aplicativo Android para controle remoto do Onkyo NS-6130 player. Há uma chance de que ele também funcione com o Onkyo NS-6170. Mas você não poderá usá-lo com nenhum receptor Onkyo, pois toda a interface do aplicativo é voltada especificamente para reproduzir e gerenciar a biblioteca de músicas, que, por regra, não está nos receptores. Portanto, não tenho planos de distribuir esse aplicativo de alguma forma, aqui escrevo apenas como um exemplo da implementação deste protocolo.

A estrutura do aplicativo é simples, o design é minimalista. Existem apenas três guias:

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









Ao contrário de um aplicativo proprietário, é 10 vezes menor, mais responsivo, suporta a orientação da tela em paisagem e vários temas de design. Ele cobre toda a gama de minhas tarefas completamente, embora exista e onde expandir. No entanto, também diferente de um aplicativo proprietário, ele não é universal.

Se, depois de ler este artigo, um dos proprietários de dispositivos Onkyo quiser experimentar sua cópia, espero que este material e meu aplicativo de amostra abaixem o limite de entrada no tópico.

Obrigado pela atenção!

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


All Articles