写点什么

RVB2601 应用开发实战系列六:网络播放器设计 (二)

发布于: 2021 年 09 月 02 日

关键词: RVB2601、RISC-V 开发板、例程,玄铁 E906、低功耗,玄铁 E906,AliOS Things,  RISC-V  MCU,上手,好用,控制,智能控制,开关,WiFi&BLE


1. 前言

本例程基于 YoC 软件平台 av 组件采用 http 协议播放一首网络 mp3 歌曲。当开发板成功通过 sal(底层通过 at 指令连接内置的网卡芯片)连接网络后,可输入相应串口命令行从 web 服务器上拉取 mp3 歌曲实现边拉取音频源数据边播放的功能。开发者可基于该例程实现更为丰富的网络播放功能。 本例程名为 ch2601_webplayer_demo,可以通过 CDK 直接从 OCC 拉取。


2. 如何使用

2.1 下载代码并编译运行

  • 通过 cdk 搜索 ch2601_webplayer_demo 并下载工程代码打开后,会有如下界面。其中框 1 为解决方案组件,框 2 中是该解决方案依赖的子功能组件。

  • 在本例程中,主要依赖 av(音视频软件框架)、pvmp3dec(mp3 解码器)、drv_wifi_at_w800(wifi 驱动)等组件。



  • 在 IDE 上编译通过后,点击下载进行烧录。烧录成功后,复位运行。成功运行后,串口会有如何打印输出:



2.2 网络连接

通过 ifconfig 命令可配置需要连接的热点。具体命令为:


ifconfig ap wifi_ssid wifi_psk


热点配置成功后,会有下图如下打印:



2.3 命令行播放控制

可通过在串口下输入如下命令来控制歌曲的播放


# player help        player play welcom/url[http://]  #播放内置开机音频或网络歌曲        player pause                     #暂停播放        player resume                    #恢复播放        player stop                      #停止播放        player help                      #播放器帮助命令
复制代码


播放 http 歌曲 player play http://yocbook.oss-cn-hangzhou.aliyuncs.com/av_repo/alibaba.mp3,示例如下:


player play http://yocbook.oss-cn-hangzhou.aliyuncs.com/av_repo/alibaba.mp3
# [ 13.620]<E>w800_api domain to ip: 47.110.23.146[ 13.630]<D>sals remote_port -- : 80[ 13.710]<D>WEB http request: GET /av_repo/alibaba.mp3 HTTP/1.0Host: yocbook.oss-cn-hangzhou.aliyuncs.comUser-Agent: CSKY/YOC
[ 15.000]<D>stream upto cache threshold2, pos = 553, cache_pos = 809, diff = 256[ 15.420]<D>avparser find a parser, name = mp3, id = 1[ 15.440]<D>ad find a decode, name = pvmp3dec, id = 1[ 15.450]<D>filter_swr open a avfilter, name = swr[ 15.470]<D>filter_vol open a avfilter, name = vol[ 15.470]<D>ao_alsa ao open[ 15.490]<D>ao ao ref: openref = 1, startref = 0, fun = __ao_open[ 15.510]<D>ao ori sf ==> sf = 90317074, rate = 44100, ch = 2, bits = 16, siged = 1, float = 0, endian = 0[ 15.540]<D>ao ao sf ==> sf = 90316946, rate = 44100, ch = 1, bits = 16, siged = 1, float = 0, endian = 0[ 15.810]<D>ao ao ref: openref = 1, startref = 1, fun = __ao_start[ 15.820]<D>player_demo =====_player_event, 24, type = 2[ 15.820]<D>player player_get_media_info, 809 enter. player = 20009E00[ 15.830]<D>player player_get_media_info, 821 leave. player = 20009E00[ 15.830]<D>player_demo =====rc = 0, duration = 415807ms, bps = 64000, size = 3326462
复制代码


3. 例程开发

3.1 主要代码解析

3.1.1 主函数流程

主函数位于 ch2601_webplayer_demo/app/src/app_main.c 中。详细的解释如下:


static void network_event(uint32_t event_id, const void *param, void *context){    switch(event_id) {    case EVENT_NETMGR_GOT_IP:        LOGD(TAG, "net got ip");        break;    case EVENT_NETMGR_NET_DISCON:        LOGD(TAG, "net disconnect");        break;    }      /*do exception process */    app_exception_event(event_id);}
int main(void){ board_yoc_init(); // 板级配置、kv文件系统、声卡驱动、网卡驱动等初始化 player_init(); // 播放器模块初始化 cli_reg_cmd_player(); // 播放器命令行注册 /* Subscribe */ event_subscribe(EVENT_NETMGR_GOT_IP, network_event, NULL); // 订阅网络连接事件 event_subscribe(EVENT_NETMGR_NET_DISCON, network_event, NULL); // 订阅网络断开事件}
复制代码


3.1.2 声卡、网卡驱动注册等

代码位于 ch2601_webplayer_demo/app/src/init.c 中。


static void network_init(){    w800_wifi_param_t w800_param;    /* init wifi driver and network */    w800_param.reset_pin      = PA21;    w800_param.baud           = 1*1000000;    w800_param.cs_pin         = PA15;    w800_param.wakeup_pin     = PA25;    w800_param.int_pin        = PA22;    w800_param.channel_id     = 0;    w800_param.buffer_size    = 4*1024;      wifi_w800_register(NULL, &w800_param);    app_netmgr_hdl = netmgr_dev_wifi_init();      if (app_netmgr_hdl) {        utask_t *task = utask_new("netmgr", 2 * 1024, QUEUE_MSG_COUNT, AOS_DEFAULT_APP_PRI);        netmgr_service_init(task);        netmgr_start(app_netmgr_hdl);    }}
void board_yoc_init(void){ board_init(); // 板级初始化 event_service_init(NULL); // 发布订阅服务初始化 console_init(CONSOLE_UART_IDX, 115200, 512); // 串口初始化 ulog_init(); // 日志初始化 aos_set_log_level(AOS_LL_DEBUG); // 配置默认日志打印级别 int ret = partition_init(); // 分区初始化 if (ret <= 0) { LOGE(TAG, "partition init failed"); } else { LOGI(TAG, "find %d partitions", ret); } aos_kv_init("kv"); // kv文件系统初始化,可用于保存网络ssid&psk snd_card_alkaid_register(NULL); // 声卡初始化,可用于播放&采集 network_init(); // 网络初始化 board_cli_init(); // 命令行初始化并注册默认的命令}
复制代码


3.1.3 网络底层通信

2601 主芯片是通过 spi 与无线网卡芯片 w800 通信的。w800 中运行有完整的 lwip 网络协议栈。 drv_wifi_at_w800 组件将底层 spi 收到的网络数据(采用 at 协议封装)处理后递交到 sal(socket abstract layer)组件中。2601 通过 sal 来屏蔽底层网卡驱动的差异,向上提供标准的 BSD 网络套接字接口。 此部分代码位于 components/drv_wifi_at_w800/w800_at_port.c 中。


static int spi_resp_len(void){    uint16_t temp = 0;    uint8_t a,b;    uint8_t cmd = SPI_REG_INT_STTS;    int recv_len = 0;      while (1) {        CS_LOW;        csi_spi_send(&spi_handle, &cmd, 1, AOS_WAIT_FOREVER);		// 检查是否存在有效数据        csi_spi_receive(&spi_handle, &a, 1, AOS_WAIT_FOREVER);        csi_spi_receive(&spi_handle, &b, 1, AOS_WAIT_FOREVER);        CS_HIGH;              temp = a | (b << 8);        if((temp != 0xffff) && (temp & 0x01)) {            cmd = SPI_REG_RX_DAT_LEN;            CS_LOW;            csi_spi_send(&spi_handle, &cmd, 1, AOS_WAIT_FOREVER);   // 获取接收数据长度             csi_spi_receive(&spi_handle, &a, 1, AOS_WAIT_FOREVER);            csi_spi_receive(&spi_handle, &b, 1, AOS_WAIT_FOREVER);            CS_HIGH;            recv_len = a | (b << 8);                      // printf("recv len:%d\r\n", recv_len);            break;        }        aos_msleep(100);    }      return recv_len;}
static void at_spi_recv_task(void *priv){ int len = 0; uint8_t *recv = NULL; while(1) { aos_sem_wait(&spi_recv_sem, AOS_WAIT_FOREVER); // 是否有中断过来,通过GIIO来触发中断 len = spi_resp_len(); // 获取对端发送过来的数据长度 if (len) recv = aos_malloc_check(len); else continue; spi_recv(recv, len); // 获取实际有效数据 while (ringbuffer_available_write_space(&spi_ringbuffer) < (len -1)) { aos_msleep(100); } int w_len = ringbuffer_write(&spi_ringbuffer, recv, len-1); // 写入到环形缓冲中 if (w_len != (len-1)) { LOGD(TAG, "spi buffer is full\r\n"); } else { spi_channel_cb(AT_CHANNEL_EVENT_READ, spi_channel_priv); } if (recv) { aos_free(recv); recv = NULL; } }}
static void *at_spi_init(const char *name, void *config){ int ret = 0; csi_pin_set_mux(PA16, PA16_SPI0_SCK); // 配置管脚复用 csi_pin_set_mux(PA17, PA17_SPI0_MOSI); csi_pin_set_mux(PA18, PA18_SPI0_MISO); // csi_pin_set_mux(PA15, PA15_SPI0_CS); // CS csi_pin_set_mux(PA15, PIN_FUNC_GPIO); // CS csi_pin_set_mux(PA22, PIN_FUNC_GPIO); // INT csi_gpio_pin_init(&spi_int_pin, PA22); // gpio配置 csi_gpio_pin_dir(&spi_int_pin,GPIO_DIRECTION_INPUT); csi_gpio_pin_mode(&spi_int_pin,GPIO_MODE_PULLNONE); csi_gpio_pin_debounce(&spi_int_pin, true); csi_gpio_pin_attach_callback(&spi_int_pin, spi_in_int_cb, NULL); // 根据gpio来通知是否存在网络数据 csi_gpio_pin_irq_mode(&spi_int_pin,GPIO_IRQ_MODE_FALLING_EDGE); csi_gpio_pin_irq_enable(&spi_int_pin, 1); csi_gpio_pin_init(&spi_cs_pin, PA15); csi_gpio_pin_mode(&spi_cs_pin,GPIO_MODE_PULLUP); csi_gpio_pin_dir(&spi_cs_pin,GPIO_DIRECTION_OUTPUT); CS_HIGH; csi_gpio_pin_init(&spi_wakeup_pin, PA25); csi_gpio_pin_mode(&spi_wakeup_pin,GPIO_MODE_PULLUP); csi_gpio_pin_dir(&spi_wakeup_pin,GPIO_DIRECTION_OUTPUT); csi_gpio_pin_write(&spi_wakeup_pin, GPIO_PIN_HIGH); ret = csi_spi_init(&spi_handle, 0); if (ret < 0) { printf("csi spi init failed\r\n"); return NULL; } csi_spi_mode(&spi_handle, SPI_MASTER); // 2601侧作为master ret = csi_spi_baud(&spi_handle, 1*1000000); // 波特率配置默认1M LOGD(TAG, "#######################spi speed:%d\r\n", ret); csi_spi_cp_format(&spi_handle, SPI_FORMAT_CPOL0_CPHA0); csi_spi_frame_len(&spi_handle, SPI_FRAME_LEN_8); csi_spi_select_slave(&spi_handle, 0); // 建立与w800间的spi通信,w800网卡作为slave 0 aos_task_t task; ret = aos_sem_new(&spi_recv_sem, 0); // 用于gpio中断通知 // aos_check(ret, NULL); ret = aos_task_new_ext(&task, "spi_recv", at_spi_recv_task, NULL, 1536, 9); // aos_check(ret, NULL); spi_recv_buffer = (char *)aos_malloc_check(SPI_RX_BUFFER_LEN); // 创建环形buffer,用于接收网络数据 ringbuffer_create(&spi_ringbuffer, spi_recv_buffer, SPI_RX_BUFFER_LEN); return (void*)1;}
at_channel_t spi_channel = { .init = at_spi_init, .set_event = at_spi_set_event, .send = at_spi_send, .recv = at_spi_recv,};
复制代码


3.2 网络播放器使用及配置

YoC 平台中的播放器可以支持 wav、mp3、m4a、amrnb、amrwb、flac、adts 等多种音频格式的播放。同时也支持 sd 卡、http(s)、fifo、mem 等多种取流方式。url 格式的详细定义如下:



播放器相关组件详细的设计和使用方法请访问以下链接: https://yoc.docs.t-head.cn/yocbook/Chapter5-%E7%BB%84%E4%BB%B6/%E5%A4%9A%E5%AA%92%E4%BD%93%E6%92%AD%E6%94%BE%E5%99%A8/av.html


3.2.1 网络播放器在 2601 芯片上的应用

网络播放器典型代码解析如下:


static player_t *g_player;
static void _player_event(player_t *player, uint8_t type, const void *data, uint32_t len){ int rc; UNUSED(len); UNUSED(data); UNUSED(handle); LOGD(TAG, "=====%s, %d, type = %d", __FUNCTION__, __LINE__, type); switch (type) { case PLAYER_EVENT_ERROR: // 播放出错事件 rc = player_stop(player); break; case PLAYER_EVENT_START: { // 开始播放事件 media_info_t minfo; memset(&minfo, 0, sizeof(media_info_t)); rc = player_get_media_info(player, &minfo); // 获取媒体时长、大小等信息 LOGD(TAG, "=====rc = %d, duration = %llums, bps = %llu, size = %u", rc, minfo.duration, minfo.bps, minfo.size); break; } case PLAYER_EVENT_FINISH: // 播放结束事件 player_stop(player); // 停止播放 break; default: break; }}
player_t *get_player_demo(){ if (!g_player) { ply_conf_t ply_cnf;
player_conf_init(&ply_cnf); // 初始化播放器默认配置 ply_cnf.vol_en = 1; // 使能数字音量功能 ply_cnf.vol_index = 160; // 0~255 ply_cnf.event_cb = _player_event; // 播放事件回调函数 ply_cnf.period_num = 12; // 底层音频输出缓冲周期,用于控制音频输出缓冲大小 ply_cnf.cache_size = 32 * 1024; // 网络时的播放缓冲大小 g_player = player_new(&ply_cnf); // 创建播放器 } return g_player;}
复制代码


3.2.2 网络播放器相关宏配置

鉴于 2601 的硬件资源比较受限,而网络播放器又提供了很多的功能。所以不太可能将播放器提供的所有功能都能够包含进去。此时就需要开发根据具体产品需要开启或配置相关功能。 例程中典型宏定义配置如下:


CONFIG_AEFXER_IPC=0                     #音效处理,2601不涉及CONFIG_AEFXER_SONA=0                    #音效处理,2601不涉及CONFIG_AO_MIXER_SUPPORT=0               #混音播放,默认关闭CONFIG_ATEMPOER_IPC=0                   #核间变速播放,2601不涉及CONFIG_ATEMPOER_SONIC=1                 #变速播放CONFIG_AV_AO_CHANNEL_NUM=1              #单声道音频输出CONFIG_AV_PROBE_SIZE_MAX=1024           #音频格式探测最大长度CONFIG_AV_SAMPLE_NUM_PER_FRAME_MAX=80   #控制wav音频帧的最大采样数CONFIG_AV_STREAM_INNER_BUF_SIZE=256     #stream内部buf大小,用于性能优化CONFIG_DECODER_ADPCM_MS=0               #adpcm_ms解码CONFIG_DECODER_ALAW=0                   #alaw解码CONFIG_DECODER_AMRNB=0                  #amrnb解码CONFIG_DECODER_AMRWB=0                  #amrwb解码CONFIG_DECODER_FLAC=0                   #flac解码CONFIG_DECODER_IPC=0                    #核间解码,2601不涉及CONFIG_DECODER_MULAW=0                  #ulaw解码CONFIG_DECODER_OPUS=0                   #opus解码CONFIG_DECODER_PCM=1                    #pcm裸流解码CONFIG_DECODER_PVMP3=1                  #mp3解码CONFIG_DECODER_SPEEX=0                  #speex解码CONFIG_DEMUXER_ADTS=0                   #adts解复用CONFIG_DEMUXER_AMR=0                    #amr解复用CONFIG_DEMUXER_ASF=0                    #asf解复用CONFIG_DEMUXER_FLAC=0                   #flac解复用CONFIG_DEMUXER_MP3=1                    #mp3解复用CONFIG_DEMUXER_MP4=0                    #mp4解复用CONFIG_DEMUXER_OGG=0                    #ogg解复用CONFIG_DEMUXER_RAWAUDIO=0               #rawaudio解复用CONFIG_DEMUXER_TS=0                     #ts解复用CONFIG_DEMUXER_WAV=0                    #wav解复用CONFIG_EQXER_IPC=0                      #量化器,2601不涉及CONFIG_EQXER_SILAN=0                    #量化器,2601不涉及CONFIG_FFTXER_IPC=0                     #fft变换,2601不涉及CONFIG_FFTXER_SPEEX=0                   #fft变换,2601不涉及CONFIG_PLAYER_TASK_STACK_SIZE=2048      #播放器任务栈大小CONFIG_RESAMPLER_IPC=0                  #核间音频重采样    CONFIG_RESAMPLER_SPEEX=0                #speex重采样CONFIG_STREAMER_CRYPTO=0                #加密流CONFIG_STREAMER_FIFO=0                  #队列流CONFIG_STREAMER_FILE=0                  #文件流CONFIG_STREAMER_HLS=0                   #http live streamCONFIG_STREAMER_HTTP=1                  #http网络流CONFIG_STREAMER_MEM=1                   #内存流CONFIG_WEB_CACHE_TASK_STACK_SIZE=2048   #网络流缓冲任务栈大小
复制代码


AV 组件中宏配置的具体说明请参考此链接中的功能配置与裁剪小节。该链接中同时会介绍典型音频播放场景的相关配置。


3.2.3 在 CDK 中如何配置宏

  • 在解决方案名称上右击,选择弹出框中第一项,如下图所示:



  • 在弹出框中选中 Compile 选项卡,单击下图中的红色框可配置相关宏



  • 在弹出框中,根据功能需要配置对应的宏,保存后重新编译



注意事项:

  • Package 中的子功能组件在 Options 选项中会有默认的配置项(如果存在)

  • 解决方案在依赖子功能组件时,可通过 Options 选项自行重新配置相关的宏。其在编译时会覆盖子功能组件的默认配置


4. 参考资料


YoC 软件平台:https://yoc.docs.t-head.cn/yocbook/


多媒体播放器组件:https://yoc.docs.t-head.cn/yocbook/Chapter5-%E7%BB%84%E4%BB%B6/%E5%A4%9A%E5%AA%92%E4%BD%93%E6%92%AD%E6%94%BE%E5%99%A8/


SAL 组件:https://yoc.docs.t-head.cn/yocbook/Chapter4-%E6%A0%B8%E5%BF%83%E6%A8%A1%E5%9D%97/%E7%BD%91%E7%BB%9C%E8%BF%9E%E6%8E%A5/%E5%A5%97%E6%8E%A5%E5%AD%97%E9%80%82%E9%85%8D%E5%B1%82SAL.html


AT 组件:https://yoc.docs.t-head.cn/yocbook/Chapter4-%E6%A0%B8%E5%BF%83%E6%A8%A1%E5%9D%97/AT%E5%91%BD%E4%BB%A4/


网络管理器组件:https://yoc.docs.t-head.cn/yocbook/Chapter4-%E6%A0%B8%E5%BF%83%E6%A8%A1%E5%9D%97/%E7%BD%91%E7%BB%9C%E8%BF%9E%E6%8E%A5/%E7%BD%91%E7%BB%9C%E7%AE%A1%E7%90%86%E5%99%A8.html


本文转自平头哥芯片开放社区(occ),更多详情请点击https://occ.t-head.cn/store/board?channelName=1

用户头像

还未添加个人签名 2021.08.24 加入

还未添加个人简介

评论

发布
暂无评论
RVB2601应用开发实战系列六:网络播放器设计(二)