AudioPlayer音频播放

摘要

AudioPlayer指令从技能发送给DuerOS,对端上进行音乐播放控制。在DuerOS收到该指令后,会经过一系列处理解析为端上对应的播放指令。在云端下发AudioPlayer指令后,端上开始音乐播放操作。音乐播放会触发一系列事件上报到云端。这些指令和事件分别是:

  • AudioPlayer.Play指令
    • 播放。
  • AudioPlayer.Stop指令
    • 停止。
  • AudioPlayer.PlaybackStarted事件
    • 播放开始。
  • AudioPlayer.PlaybackStopped事件
    • 播放暂停。
  • AudioPlayer.PlaybackFinished事件
    • 播放结束,即一首歌播放完后上报的事件。
  • AudioPlayer.PlaybackFailed事件
    • 当设备端播放Audio item发生错误时上报此事件。
  • AudioPlayer.PlaybackNearlyFinished事件
    • 在播放即将结束时上报的事件。
  • AudioPlayer.ProgressReportIntervalElapsed事件
    • AudioPlayer用来定期上报播放状态的事件。

AudioPlayer.Play指令

用于在设备端播放一个音频。

消息样例

{
    "type": "AudioPlayer.Play",
    "playBehavior": "{{STRING}}"
    "audioItem": {
        "stream": {
            "url": "{{STRING}}",
            "streamFormat": "{{STRING}}",
            "offsetInMilliSeconds": {{LONG}},
            "token": "{{STRING}}",
            "progressReportIntervalMs": {{LONG}}
        },
        "playerInfo": {
            "content": {
                "title": "{{STRING}}",
                "titleSubtext1": "{{STRING}}",
                "titleSubtext2": "{{STRING}}",
                "language": "{{STRING}}",
                "effect": {
                    "bpm": {{INT}},
                    "landscapeVideoUrl": "{{STRING}}",
                    "portraitVideoUrl": "{{STRING}}",
                    "fontColor": "{{STRING}}",
                    "fontFamily": "{{STRING}}",
                    "fontSize": "{{STRING}}",
                    "landscapeBackgroundImageUrl": "{{STRING}}",
                    "portraitBackgroundImageUrl": "{{STRING}}",
                    "animation": {
                        "id": "{{STRING}}",
                        "url": "{{STRING}}",
                        "md5": "{{STRING}}" 
                    },
                },
                "lyric": {{LyricStructure}},
                "mediaLengthInMilliseconds": {{LONG}},
                "art": {{ImageStructure}},
                "provider": {
                    "name": "{{STRING}}",
                    "logo": {{ImageStructure}}
                }
            }
            "controls": [
                {{ControlStructure}},
                {{ControlStructure}},
                ......
            ]
        }
    }
}

参数说明

  • type
    • AudioPlayer指令类型,即"AudioPlayer.Play"。
  • playBehavior
    • REPLACE_ALL
      • 清空播放列表,立即播放指令关联的音频。例如,"播放周杰伦的歌曲"、"播放郭德纲的相声"、"下一首"、"上一首"。
    • REPLACE_ENQUEUED
      • 清空播放列表,同时把指令关联的音频加到播放列表,但当前播放的歌曲不受影响。例如,"这首歌播放完后播放周杰伦的歌曲",“单曲循环、列表循环、随机播放”。
    • ENQUEUE
      • 把指令关联的音频添加到播放列表末尾,当前播放的音频不受影响,下一首预取。
  • audioItem
    • 音频的详细信息。
  • audioItem.stream
    • 音频流相关的信息。
  • audioItem.stream.url
    • 音频流地址。
  • audioItem.stream.streamFormat
    • 播放的音频流的格式,例如"MP3"。
  • audioItem.stream.offsetInMilliSeconds
    • 音频播放起始点。
  • audioItem.stream.token
    • 唯一标识此次directive中播放的音频流,设备端会存储这个值,在音频播放期间,产生的所有事件的端状态中会携带这个值。
  • audioItem.stream.progressReportIntervalMs
    • 定时上报ProgressReportIntervalElapsed事件的间隔时间。
  • audioItem.playInfo
    • 音频展现的相关信息,该项可选。在有屏设备端播放音频资源时,会对playInfo的信息进行展现,如播放音乐时,展现歌词、歌手图片等。在无屏设备端playInfo不生效。
  • audioItem.playInfo.content
    • 音频展现信息的具体内容。
  • audioItem.playInfo.content.title
    • 音频的标题,比如歌曲名“告白气球”。
  • audioItem.playInfo.content.titleSubtext1
    • 音频子标题1,比如歌手名“周杰伦”。
  • audioItem.playInfo.content.titleSubtext2
    • 音频子标题2,比如专辑名“周杰伦的床边故事”。
  • (optional) audioItem.playInfo.content.language
    • 语言,当前支持两种语言:zh(中文),en(英文)。参考 ISO 639-1
  • (optional) audioItem.playInfo.content.effect.bpm
    • 音乐每分钟节拍数,参考BPM
  • (optional) audioItem.playInfo.content.effect.landscapeVideoUrl
    • 播放器横屏模式特效背景视频URL
  • (optional) audioItem.playInfo.content.effect.portraitVideoUrl
    • 播放器竖屏模式特效背景视频URL
  • (optional) audioItem.playInfo.content.effect.fontColor
    • 播放器特效歌词字体颜色,格式为#RGB,如#AABBCC,端上默认颜色#1A1A1A
  • (optional) effect.fontFamily
    • 播放器特效歌词字体,如果该字段不存在或者值为空,则端上使用系统字体。当前可选值:
      • duer_cang_er_yu_mo
      • duer_qian_tu_ma_ke
      • duer_qian_tu_xian_mo
      • duer_rui_zhi_zhen_yan
      • duer_zhuang_jia_ming_chao
  • (optional) effect.fontSize
    • 播放器特效歌词字体大小,如果该字段不存在或值为空,则端上自行计算
  • (optional) effect.landscapeBackgroundImageUrl
    • 播放器横屏模式特效背景图片URL,当有背景动效时,通常为背景动效的首帧截图
  • (optional) effect.portraitBackgroundImageUrl
    • 播放器竖屏模式特效背景图片URL,当有背景动效时,通常为背景动效的首帧截图
  • (optional) effect.animation.id
    • 当前逐句歌词pag动效模板集合id
  • (optional) effect.animation.url
    • 动效集合的压缩包地址
  • (optional) effect.animation.md5
    • 动效集合压缩包的md5校验值(两个作用:1.校验压缩包下载是否完整,2.动效是否有更新)
  • audioItem.playInfo.content.lyric
  • audioItem.playInfo.content.mediaLengthInMilliseconds
    • 音频流的长度,单位为ms。优先使用设备端获取的音频流的实际长度,这里的音频流长度主要应用Companion App等场景的渲染。
  • audioItem.playInfo.content.art
    • 音频封面图片,比如音乐里专辑的封面图,请参考ImageStructure的定义。
  • audioItem.playInfo.content.provider
    • 资源提供方信息。
  • audioItem.playInfo.content.provider.name
    • 资源提供方的名字,比如百度音乐、QQ音乐、虾米音乐等。
  • audioItem.playInfo.content.provider.logo
  • audioItem.playInfo.controls
    • 音频播放使用的控件列表。点击链接了解音频播放支持的控件类型。

说明:

  1. 该指令需要在设备端麦克关闭的情况下使用。下面两种情况下麦克风会关闭,可以返回AudioPlayer.Play指令。
    • response中的shouldEndSession字段值为true,技能结束会话并退出时,设备端关闭麦克风,此时可以返回AudioPlayer.Play指令。当设备端播放音频时,技能已经退出。
    • response的shouldEndSession字段和expectSpeech字段同时为false,技能还在会话中,但是不需要用户进行回复,设备端会关闭麦克风,此时可以返回AudioPlayer.Play指令。
  2. audioItem.playInfo用于渲染音频播放器的主界面,在有屏设备端播放音频资源时,展现音频相关信息,如歌词、歌手图片等。该字段在无屏设备端不生效。

LyricStructure

定义歌词的结构。

消息样例

{
  "url": "{{STRING}}",
  "format": {{ENUM}}
}

参数说明

  • url
    • 歌词url地址,支持http协议和cid格式(即内容直接附加在指令中)。 在AudioPlayer.Play指令中只支持http地址。
  • format
    • 歌词的格式,支持的格式类型:
    • LRC: 参考LRC
    • DRC: 逐字/词歌词

ImageStructure

定义图片结构。

消息样例

{
  "src": "{{STRING}}"
}

参数说明

  • src
    • 图片地址。

AudioPlayer.Stop指令

用于停止设备端的音频播放。

消息样例

{
    "type": "AudioPlayer.Stop"
}

参数说明

  • type:
    • AudioPlayer指令类型,即"AudioPlayer.Stop"。

AudioPlayer.PlaybackStarted事件

客户端开始播放的时候,需要上报此事件。

消息样例

{
    "type": "AudioPlayer.PlaybackStarted",
    "requestId": "{{STRING}}",
    "timestamp": {{STRING}},
    "token": "{{STRING}}",
    "offsetInMilliSeconds": {{LONG}}
}

参数说明

  • type
    • 请求类型,即字符串"AudioPlayer.PlaybackStarted"。
  • requestId
    • 标识本次请求的唯一ID。如果有问题反馈,可以附带提供给DuerOS开发人员。
  • timestamp
    • 请求时间戳,单位是秒,一个全部是数字的字符串。
  • token
    • 对应到播放的音频id。
  • offsetInMilliSeconds
    • 事件上报时音频流的播放点。

PlaybackStopped事件

用户说"暂停播放"、 "停止播放"后,会收到Stop指令,客户端执行完Stop指令后,即暂停播放后,需要上报此事件,云端会保存断点,供下一次继续播放使用。

消息样例

{
    "type": "AudioPlayer.PlaybackStopped",
    "requestId": "{{STRING}}",
    "timestamp": {{STRING}},
    "token": "{{STRING}}",
    "offsetInMilliSeconds": {{LONG}}
}

参数说明

  • type
    • 请求类型,即字符串"AudioPlayer.PlaybackStopped"。
  • requestId
    • 标识本次请求的唯一ID。如果有问题反馈,可以附带提供给DuerOS开发人员。
  • timestamp
    • 请求时间戳,单位是秒,一个全部是数字的字符串。
  • token
    • 对应到播放的音频id。
  • offsetInMilliSeconds
    • 事件上报时音频流的播放点。

PlaybackNearlyFinished事件

这个事件用来获取下一首歌曲,云端收到这个事件后,返回下一首歌曲对应的Play指令,playBehavior选项设为ENQUEUE, 指示客户端加入到本地列表中,利用此事件,客户端可以实现下一首的预取。这个事件的上报时机取决于客户端,可以在歌曲播放的中间发送,也可以剩余几秒钟的时候发送。如果不需要预取,也可以在歌曲播放完毕后上报此事件。但必须保证:

  • PlaybackNearlyFinished必须在PlaybackStarted事件发送完毕后才上报。
  • 一首歌曲只发送一次PlaybackNearlyFinished事件。

消息样例

{
    "type": "AudioPlayer.PlaybackNearlyFinished",
    "requestId": "{{STRING}}",
    "timestamp": {{STRING}},
    "token": "{{STRING}}",
    "offsetInMilliSeconds": {{LONG}}
}

参数说明

  • type
    • 请求类型,即字符串"AudioPlayer.PlaybackNearlyFinished"。
  • requestId
    • 标识本次请求的唯一ID。如果有问题反馈,可以附带提供给DuerOS开发人员。
  • timestamp
    • 请求时间戳,单位是秒,是一个全部是数字的字符串。
  • token
    • 对应到播放的音频id。
  • offsetInMilliSeconds
    • 事件上报时音频流的播放点。

PlaybackFinished事件

当且仅当歌曲正常播放到末尾后,上报此事件。注意如果被其它指令打断比如“下一首”、“上一首”导致没有播放到末尾的,不上报此事件。

消息样例

{
    "type": "AudioPlayer.PlaybackFinished",
    "requestId": "{{STRING}}",
    "timestamp": {{STRING}},
    "token": "{{STRING}}",
    "offsetInMilliSeconds": {{LONG}}
}

参数说明

  • type
    • 请求类型,即字符串"AudioPlayer.PlaybackFinished"。
  • requestId
    • 标识本次请求的唯一ID。如果有问题反馈,可以附带提供给DuerOS开发人员。
  • timestamp
    • 请求时间戳,单位是秒,是一个全部是数字的字符串。
  • token
    • 对应到播放的音频id。
  • offsetInMilliSeconds
    • 事件上报时音频流的播放点。

PlaybackFailed事件

当设备端播放audio item发生错误时上报此事件。

事件中的token有可能与正在播放中的audio item的token不一致,比如正在播放一个audio item,并要开始缓冲下一个audio item时下一个audio item的缓冲出现错误。

消息样例

{
    "type": "AudioPlayer.PlaybackFailed",
    "requestId": "{{STRING}}",
    "token": "{{STRING}}",
    "error": {
        "type": "{{STRING}}",
        "message": "{{STRING}}"
    }
}

参数说明

  • type
    • 请求类型,即字符串"AudioPlayer.PlaybackFailed"。
  • requestId
    • 标识本次请求的唯一ID。如果有问题反馈,可以附带提供给DuerOS开发人员。
  • token
    • 发生错误的audio item的token
  • error.type
    • 错误类型,取值如下
      • "MEDIA_ERROR_UNKNOWN": 未知错误
      • "MEDIA_ERROR_INVALID_REQUEST": stream服务端返回请求无效 (可能的情况有bad request, unauthorized, forbidden, not found等等)
      • "MEDIA_ERROR_SERVICE_UNAVAILABLE": 设备端无法连接stream服务端
      • "MEDIA_ERROR_INTERNAL_SERVER_ERROR": stream服务端接受请求,但未能正确处理
      • "MEDIA_ERROR_INTERNAL_DEVICE_ERROR": 设备端内部错误
  • error.message:
    • 错误描述,仅用于打印日志;如果有HTTP相关的错误,也应该包含在本错误消息中,以便排查问题。

ProgressReportIntervalElapsed事件

如果Play指令设置了progressReportIntervalMs的值,那么客户端需要周期性的每隔一段时间上报一次当前的播放进度,直到歌曲播放结束。注意歌曲开始播放和歌曲结束播放的时候,可以不上报这个事件。

下面两种情况,上报事件时所处的播放位置是一样的,跟offsetInMilliSeconds的值没有关系,因为是从音频流的0位置算起。云端收到这个事件后,可能会返回给客户端一条指令,需要客户端执行之,比如AdjustVolume指令、Stop指令等。利用这个特性可以实现助眠模式。

  • 情况1:假如progressReportIntervalMs的值为3000,Play指令的offsetInMilliSeconds值为0,从音频流的0位置算起,如果播放位置到达3000的整数倍,就上报一次事件。
  • 情况2:假如progressReportIntervalMs的值为3000,Play指令的offsetInMilliSeconds值为1000,从音频流的0位置算起,如果播放位置到达3000的整数倍,就上报一次事件。

消息样例

{
    "type": "AudioPlayer.ProgressReportIntervalElapsed",
    "requestId": "{{STRING}}",
    "timestamp": {{STRING}},
    "token": "{{STRING}}",
    "offsetInMilliSeconds": {{LONG}}
}

参数说明

  • type
    • 请求类型,即字符串"AudioPlayer.ProgressReportIntervalElapsed"。
  • requestId
    • 标识本次请求的唯一ID。如果有问题反馈,可以附带提供给DuerOS开发人员。
  • timestamp
    • 请求时间戳,单位是秒,是一个全部是数字的字符串。
  • token
    • 对应到播放的音频id。
  • offsetInMilliSeconds
    • 事件上报时音频流的播放点。

音频播放示例

{
    "version": "2.0",
    "context": {
        "user": {
            "userId": "xxx"
        },
        "System": {
            "application": {
                "applicationId": "audioplayer"
            }     
        }
    },
    "request": {
        "type": "AudioPlayer.PlaybackStarted",
        "requestId": "33c78c8f-22db-4d6a-bfd5-075d264377b7",
        "timestamp": "1501127440",
        "token": "12329898321",
        "offsetInMilliSeconds": 1000
    }
}