我相信,许多Habr的读者都知道或至少听说过有关Onkyo音频设备的信息。 现代网络播放器和A / V接收器具有Linux板载功能,以及有线/无线网络连接的功能。 Onkyo提供其专有的移动应用程序来远程控制此类设备
-Onkyo Controller 。 几乎没有有关此应用程序如何工作的信息-论坛上有碎屑,而github上有几个项目。

但是您可以在网络上找到基于以太网的集成串行通信协议(eISCP)协议的说明,该协议是该应用程序的基础。 该协议很有趣。 在Habré上找不到关于此协议的任何文章。 一方面,对此没有什么可悲的,因为除了安桥之外,这种所有权没有被使用过。 另一方面,有一些发烧友希望自己操纵自己的播放器或Onkyo接收器。 同样,对于那些纯粹出于理论好奇心而收集各种网络协议知识的人,可能会感兴趣。 如果有兴趣,请在目录下。
关于本文主题的官方信息很少。 因此,我将不仅依赖所找到的文档,因为它仅描述协议命令,而没有说明其使用功能。 通过使用tcpdump / wireshark分析网络流量以及研究设备固件,我们设法获得了很多信息。 这就是我要开始的地方。
我的设备的具体型号并不重要。 我只能说这是一个网络播放器,类似于图中的那个吸引眼球。 它不仅可以播放来自外部USB载体的音乐,还可以播放来自音乐服务器(DLNA)的音乐,并且还支持Internet广播和流媒体服务,例如Spotify,Deezer等。 自然,该协议应支持所有这些多样性。
端口分析
为了开始在搜索引擎中提出正确的问题,我必须首先了解通常使用哪种协议。 也就是说,第一步是端口分析。 因此,该设备在网络上,其地址为192.168.1.80。 扫描整个端口范围:
> 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
发现了很多有趣的东西:
- 80 / tcp清除-这是设备设置页面。 在我的模型中,只有网络设置和固件升级。 没有播放控件。 通过它,通过“ http://192.168.1.80/album_art.cgi”之类的动态链接,您可以访问当前正在播放的曲目的图片。
- 4545 / tcp-在最新的固件更新之后出现。 Nmap对他一无所知。 尝试连接时,它将立即发送具有当前播放状态的json并每秒发送一次更新
具有播放状态的数据块 { "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" }
就像我说的那样,此端口随最新更新一起出现。 这个词根本没有任何文档。 这对于开发轻型控制面板可能很有用。 但是朝这个方向我还没有挖掘。 - 5000 / tcp-nmap将其定义为Apple AirTunes。 这似乎是事实,因为在文档中说明了对此协议的支持。
- 8008 / tcp,8009 / tcp-目的不明确,nmap对它们一无所知。
- 8080 / tcp-http-proxy,在此播放器的上下文中其目的尚不完全清楚。
- 8888 / tcp协议端口通用即插即用(UpnP) 。 使用gupnp-tools软件包中的gupnp-universal-cp实用程序,可以查看其描述:

最初,人们认为官方应用程序中的管理是基于此特定协议实施的。 后来发现,他弄错了。 我还尝试了几个UpnP客户端,包括移动和台式机。 它们实际上几乎都不起作用:有些控制命令起作用,有些不起作用,这完全是混乱的。 - 10001 / tcp-与SCP配置端口相似,但如何使用尚不清楚。
- 60128 / tcp-最后,本文的主要特征是eISCP协议端口。 Nmap也不了解他。 让我们打开保密的面纱。
流量分析
现在,让我们检查哪个端口以及官方应用程序如何与设备通信。 最简单的方法是在一些根植的Android上(但不能在模拟器中使用,因为官方应用程序需要在同一本地子网上的受管设备)。 为此:
- 在已经安装了Onkyo Controller的Android设备上安装Android tcpdump
- 我们以root身份通过adb转到Android设备:
> adb root && adb shell root@fiber-bs1078:/>
- 转到内置SD卡上的任何目录:
root@fiber-bs1078:/> cd /sdcard/work root@fiber-bs1078:/sdcard/work>
- 运行tcpdump(带有写入文件)
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
- 我们启动Onkyo应用程序,然后从那里开始播放音乐
- 输入几百个数据包后,请按Cttl + C停止tcpdump
- 我们回到终端,在这里启动ADB并将文件复制到工作的计算机上
root@fiber-bs1078:/sdcard/work> exit > adb pull /sdcard/work/onkyo.dump . [100%] /sdcard/work/onkyo.dump
- 启动wireshark ,看看那里发生了什么。
> wireshark onkyo.dump &
实际上,通信在端口60128上进行。 例如,应用程序向播放器发送一个请求:

他回答他:

因此,我们来到了本文的本质,即:上图中这些字母-ISCP是什么? 该缩写表示Integra串行控制协议,该协议最初旨在通过RS-232端口控制Onkyo设备(此主题上有
一篇有趣的老
文章 )。 后来通过添加前缀“ e”进行了扩展,结果是eISCP-以太网集成串行通信协议。 该协议的两个版本在《技术文档:用于AV接收机的集成串行通信协议》中进行了描述。 该文档的第一个版本日期为2012年10月31日,找到的最后一个版本为2017年9月4日。我找到的所有版本都收集在
演示项目的存储库中 ,我将在后面讨论。 进一步的介绍将基于本文档以及与我的播放器进行的实验(但是,本文档中未明确提及)。
讯息规格
客户端(例如,移动应用程序)和设备交换短文本消息。 如果数字存在,则通常以十六进制形式显示它们。
从客户端到设备的消息格式非常简单:

该消息以“!”字符开头,然后是目标设备代码,然后是命令的三个字母,然后是任意长度的参数字符串。 以CR(0x0D)或LF(0x0A)或CR + LF的组合结尾。
根据命令的不同,设备将使用相同类型的消息(如果是对某些参数的请求)或不同类型甚至消息的组合(如果是用于执行复杂操作(如切换音轨)的命令)进行应答。 设备发送给客户端的消息格式相同。 区别仅在于最后一个字节:

协议描述文档包含一百多个不同的命令,我的设备支持该文档中的三十多个命令。 即,命令集和有效参数都取决于特定设备。
可以将命令分为逻辑组。 例如,我将突出显示以下内容:
- 常规设备管理:
- NDN:设备名称。
- UPD:检查并安装固件更新。
- PWR:打开/关闭电源。
- NRI:扩展的设备信息。
- NTC:标准遥控器(包括播放控制)的命令。
- CAP:用于控制连接到RI连接器的外部放大器的命令。
- 有关正在播放的曲目的信息:
- NAL:专辑名称。
- NAT:艺术家的名字。
- NTI:曲目名称。
- NFI:跟踪文件信息(格式,比特率)。
- NJA:附在轨道上的图片(例如,如果选择了Internet广播,则为广播电台的标志)。
- NTM:轨道中的当前时间位置。
- NTS:状态,是否启用倒带(例如,对于Internet广播,不允许)。
- NST:重复播放和随机播放控制。
- 音乐库导航和管理:
- SLI:来源选择(例如USB,网络服务)。
- NSV:选择特定的网络服务(例如,互联网广播,音乐服务器)。 我的设备上的播放列表也与网络服务有关,尽管从用户界面的角度来看这并不完全明显。 而且,当您关闭电源(从插座中拔出)时,该播放列表将被删除!
- NLT,NLA:浏览库的各个部分(文件夹)。
- PQA,PQR,PQO:播放列表管理:添加,删除,重新排序。
自然,这个列表远非完整,我给出它只是为了显示此协议的范围和功能。
就参数而言,所有消息都可以分为两组。 第一组包括大多数消息。 对于此组,参数字符串包含字母数字或十六进制形式的数据,并按字节进行解析。 例如,当切换到TuneIn Radio服务时,播放器会发送一条NLT消息-有关当前列表的标头信息,其参数为“ 0E01000000090100FF0E00TuneIn Radio”,并根据规范对其进行解码,从而提供以下信息:
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
几乎所有消息都具有“ QSTN”参数,例如“!1NLTQSTN”。 该请求意味着要求玩家返回与该消息类型相对应的当前状态信息。 它几乎总是可以工作,但是当玩家根据其内部心情忽略了此类请求时,很少有例外。
第二组是消息,其中参数为XML,必须使用XML解析器对其进行解析。 在上面的示例中,在“ TuneIn Radio”部分中,您可以发送一个NLA请求,有关XML中活动列表的信息将被响应到该NLA请求:
<?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>
也就是说,播放器不仅提供文本信息(顺便说一下,当前显示在播放器本身的显示屏上),而且还建议使用适当的图标(文件夹,音乐曲目,当前正在播放的曲目)。
在某些情况下,播放器希望在客户端应用程序中显示文本消息或请求其他参数,例如用户名。 为此,播放器发送通用消息NCP(通用对话),其中XML描述了需要向用户显示的内容的结构:
<?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>
作为响应,播放器希望输入相同的消息,并填写字段(或按下的按钮)。
同样以XML格式显示了相当重要的NRI消息-有关播放器的常规信息。 该消息足够大,因此我将其隐藏在扰流板下方。
玩家一般信息 <?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>
您必须用来控制设备的命令集在很大程度上取决于此消息的zonelist和controllist部分的内容。
以太网ISCP(eISCP)
如我上面所写,消息形式旨在通过电缆(RS-232)进行传输。 较旧的接收器为此配备了9针RS-232连接器。 当他们开始使用网络连接(有线或无线)而不是此连接器时,我们不得不将这些消息包装在包装中,以通过TCP / IP进行传输。 因此出现了eISCP协议,其中ISCP消息包装在这样的包中:

破坏者下的代码是使用给定代码(可变代码),参数字符串(可变参数)和给定协议版本(可变版本)为消息完全生成此类程序包的代码。 由于该过程非常简单,所以在我看来Java代码会说出一千多个单词。
生成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);
如果有人感兴趣,
这是我针对
规范版本1.40的示例协议实现。 我还将提供
此存储库的链接。 它在Python中实现了消息库和命令行实用程序,以及与其他类似项目的链接。
实施信息交流
最初设计用于通过低速电缆传输的消息本身很小。 此外,玩家本身也相当谦虚-在大量数据发送到有条件的Amazon服务器的背景下,玩家通过ISCP自愿提供给客户端的信息量很少。 在协议规范中,没有关于玩家何时以及在什么条件下发送此或那个信息的字眼。 因此,在这里,我必须进行足够长的实验,以便移动客户端始终拥有有关设备当前状态的所有必要信息。
通常,与玩家的通信基于请求/响应方案。 而且,在某些情况下,单个请求将不受限制。 我的播放器需要处理几个关键事件:
- 建立连接。 在连接时,播放器可能处于待机模式或开机状态,可能处于播放模式或暂停状态。 立即找出输入通道开关在网络服务或USB上的位置也很重要。
因此,在建立连接之后,立即发送PWR请求(活动或处于待机状态),UPD(是否有固件更新),NRI(有关设备的一般信息),SLI(输入开关的位置),NJA(轨道图片的传输模式)是有意义的链接或流)。 我的播放器会主动发送播放状态和当前位置。 - 开始播放。 在这种情况下,播放器将发送有关曲目的所有信息。 但是在连接时,如果播放器已经在播放某些内容,则播放器不会发送任何内容。 另外,当播放器切换音轨时,不会发送所有信息。
一个通用的解决方案是跟踪NST消息(播放状态),但会消耗资源,如果此状态切换为“播放”,则立即发送7个请求:NAT(艺术家),NAL(专辑标题),NTI(曲目标题),NFI (文件信息),NTR(曲目编号),NTM(当前播放时间),NMS(曲目菜单)。 播放器的固件中有功能。 例如,在播放播放列表时,播放器不想放弃正在播放的曲目的编号。 但通常,您可以充分了解当前的播放状态。 - ( ) . - . , . — 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)]
, . , , , .
例如,我将使用Android应用程序提供到我的存储库的链接,以远程控制Onkyo NS-6130播放器。有机会也可以与Onkyo NS-6170一起使用。但是您无法将其与任何Onkyo接收器一起使用,因为整个应用程序界面专门用于播放和管理音乐库,通常,音乐库通常不在接收器上。因此,我没有计划以某种方式分发此应用程序,在这里我仅将其编写为该协议实现示例。应用程序的结构简单,设计简单。只有三个标签:- . , . , RI Onkyo. , , , .



与专有应用程序不同,它体积小10倍,响应速度更快,支持横向屏幕方向和各种设计主题。尽管存在并且可以扩展,但它完全涵盖了我的全部任务。但是,也不同于专有应用程序,它不是通用的。如果在阅读完本文后,Onkyo设备的所有者之一想尝试使用他的副本,我希望本材料和我的示例应用程序可以降低进入主题的门槛。感谢您的关注!