RTOS系统 音频player对比分析

主要分析了乐鑫、MTK、炬芯、全志的RTOS audio player,仅供参考

公司乐鑫MTK炬芯全志
平台ESP32LinkIt/mt2523ATS3503_WIFIXR871
许可证MIT License需要MTK许可Apache License 2必须保留版权信息
工程地址https://github.com/espressif/esp-adf淘宝淘宝淘宝
player主文件(个人理解)https://github.com/espressif/
esp-adf/tree/
3305749c93763b7a83d
030e7f30b28f3aeb1d186/
examples
/player有多种组合
淘宝/mt2523_hdk/apps/
mp3_local_playback/src/
AudioPlayer/audio_player_main.c
淘宝/ATS3503_WIFI/
samples/storyapp/src/
storyplayer/storyplayer_main.c
淘宝/XR871/03_SDK/
xr871sdk/project/
evb_audio/audio_player.c
官方音频文档https://docs.espressif.com/
projects/esp-adf/en/latest/
api-reference/index.html
特点使用Pipeline来连接Elements,可以注册多种Elements组合,比较灵活大部分操作通过decoder提供的api完成。也是story player,playlist部分可参考。流程比较简单,但代码可能不全
缺点差异较大,可能需要大改player似乎只支持MP3,其他格式只有aac提供了demo。代码可能不全,只看到初始化预处理部分,具体decode和output部分不明代码可能不全

乐鑫 ESP32

播放流程简要分析,可参考https://blog.csdn.net/zhejfl/article/details/86477866
主要特点为:使用了Pipeline。主要职责是控制音频数据流stream以及用ring_buffer连接各音频元素elements。它按顺序并启动音频元素,负责从前一个元素检索数据然后传递给后面一个元素,还可从每个元素获取事件、处理事件或将其传递到更高层。
可以注册多种Elements组合,在连接的时候选择一种Elements组合连接起来,也可以在断开再组成其他的Elements组合,则使得使用pipeline非常灵活。
启动audio_pipeline时,会为pipeline中的所有Elements创建Tasks。
audio_pipeline_register(pipeline, mp3_decoder, “mp3”);
audio_pipeline_register(pipeline, i2s_stream_writer, “i2s”);
audio_pipeline_link(pipeline, (const char *[]) {“mp3”, “i2s”}, 2);

audio_pipeline_run(pipeline);//为管道中的所有Elements创建Tasks。

音频框架流程:
在这里插入图片描述

一组链接的元素的动态组合是使用音频管道完成的。您无需处理单个元素,而只需处理一个音频管道。每个元素都通过环形缓冲区连接。音频管道还负责将消息从元素任务转发到应用程序。
下图显示了音频流水线中三个元素的组织,即HTTP读取流,MP3解码器和I2S写入流,已在player / pipeline_http_mp3示例中使用。在这里插入图片描述

音乐播放器或录音机支持音频格式,例如MP3,AAC,FLAC,WAV,OGG,OPUS,AMR,TS,EQ,Downmixer,Sonic,ALC,G.711 …
从以下来源播放音乐:HTTP,HLS(HTTP实时流),SPIFFS,SDCARD,A2DP来源,A2DP接收器,HFP …
集成媒体服务,例如:DLNA,VoIP …
网络广播
语音识别以及与Alexa,DuerOS等在线服务的集成…
在这里插入图片描述

https://github.com/espressif/esp-adf/tree/3305749c93763b7a83d030e7f30b28f3aeb1d186/examples/player
player有多种组合
在这里插入图片描述

以pipelind_sdcard_mp3文件夹下的play_sdcard_mp3_example.c为例
在app_main中
[ 1 ] Mount sdcard
// 初始化外围设备管理
// 初始化SD卡外设
[ 2 ] 启动codec芯片
[3.0] 创建用于播放的音频管道
[3.1]创建fatfs流以从sdcard读取数据
[3.2]创建i2s流以将数据写入codec芯片
[3.3]创建mp3解码器以解码mp3文件
[3.4]将所有元素注册到音频管道
audio_pipeline_register(pipeline, fatfs_stream_reader, “file”);
audio_pipeline_register(pipeline, mp3_decoder, “mp3”);
audio_pipeline_register(pipeline, i2s_stream_writer, “i2s”);
[3.5] 将其链接在一起[sdcard]–>fatfs_stream–>mp3_decoder–>i2s_stream–>[codec_chip]
audio_pipeline_link(pipeline, (const char []) {“file”, “mp3”, “i2s”}, 3);
[3.6] Set up uri (file as fatfs_stream, mp3 as mp3 decoder, and default output is i2s)
[ 4 ] Set up event listener
[4.1]来自管道所有元素的Listening event
[4.2]来自外围设备的Listening event
[ 5 ] 启动音频管道
[ 6 ] 监听所有管道事件
while (1)
/
当最后一个管道元素(在这种情况下为i2s_stream_writer)接收到停止事件时停止*/
[ 7 ] 停止音频管道

以pipelind_sdcard_mp3文件夹下的play_sdcard_mp3_example.c为例

结构体:

typedef struct **audio_player** {
    audio_pipeline_handle_t pipeline;
    audio_element_handle_t http_stream_reader;
    audio_element_handle_t i2s_stream_writer;
    audio_element_handle_t mp3_decoder;
    audio_event_iface_handle_t evt;
    audio_hal_handle_t hal;
    bool run;
    bool playing;
    player_event event_handler;
} audio_player_t;

struct **audio_pipeline** {
    audio_element_list_t        el_list;
    ringbuf_list_t              rb_list;
    audio_element_state_t       state;
    xSemaphoreHandle            lock;
    bool                        linked;
    audio_event_iface_handle_t  listener;
};

struct **audio_element** {
    /* Functions/RingBuffers */
    io_func                     open;
    io_func                     seek;
    process_func                process;
    io_func                     close;
    io_func                     destroy;
    io_type_t                   read_type;
    union {
        ringbuf_handle_t        input_rb;
        io_callback_t           read_cb;
    } in;
    io_type_t                   write_type;
    union {
        ringbuf_handle_t        output_rb;
        io_callback_t           write_cb;
    } out;

    audio_multi_rb_t            multi_in;
    audio_multi_rb_t            multi_out;
    
    /* Properties */
    bool                        is_open;
    audio_element_state_t       state;
    
    events_type_t               events_type;
    audio_event_iface_handle_t  iface_event;
    audio_callback_t            callback_event;
    
    int                         buf_size;
    char                        *buf;
    
    char                        *tag;
    int                         task_stack;
    int                         task_prio;
    int                         task_core;
    xSemaphoreHandle            lock;
    audio_element_info_t        info;
    audio_element_info_t        *report_info;
    
    /* PrivateData */
    void                        *data;
    EventGroupHandle_t          state_event;
    int                         input_wait_time;
    int                         output_wait_time;
    int                         out_buf_size_expect;
    int                         out_rb_size;
    bool                        is_running;
    bool                        task_run;
    bool                        stopping;

};
 * Event message
	*/
	typedef struct {
	int cmd;                /*!< Command id */
	void *data;             /*!< Data pointer */
	int data_len;           /*!< Data length */
	void *source;           /*!< Source event */
	int source_type;        /*!< Source type (To know where it came from) */
	bool need_free_data;    /*!< Need to free data pointer after the event has been processed */
	} audio_event_iface_msg_t;

MTK的LinkIt

player似乎只支持MP3,其他格式的decoder只找到aac\amr,但只有aac提供了demo,amr未找到调用方。
wav_codec.c中有一个wav_codec_deocde_event_handler函数,只在prompt_control.c和bt_sink_srv_am_task.c中被调用。

有两个audio_player_main.c文件,
project/mt2523_hdk/apps/headset_ref_design/src/AudioPlayer/audio_player_main.c
project/mt2523_hdk/apps/mp3_local_playback/src/AudioPlayer/audio_player_main.c

mp3_local_playback文件夹中的audio_player_main.c
audio_player_init()创建audio_player_task_main线程,初始化g_aud_ring_buff

audio_player_task_main线程中,
1、调用audio_player_scan_file(),搜索0:/music里的音频文件
2、调用bt_sink_srv_ami_audio_open函数,open a new audio handler and get the audio ID.同时指定一个callback函数audio_player_ami_hdr,用来write driver media data。
3、调用xSemaphoreCreateBinary创建g_aud_event_semaphore
4、调用bt_sink_srv_audio_setting_init()
5、在while(1)循环中执行audio_player_processing()

audio_player_processing函数在while(1)循环中,获取信号量,然后读文件并写到ring buffer中,再调用audio_player_check_threshold。

audio_player_check_threshold函数判断如果read_byte大于阈值,就根据aud_ply->flag的状态,调用g_file_med_hd->media_handle.mp3.resume 或g_file_med_hd->media_handle.mp3.play
在这里插入图片描述

再往下调用
mp3_codec_play→MP3Dec_Init
→mp3_codec_play_internal→MP3Dec_Decode
MP3Dec_Init和MP3Dec_Decode只在middleware/MTK/audio/mp3_codec/inc/mp3dec_exp.h中有声明,找不到具体函数。

mp3.stop部分提供了一个audio_player_stop函数,但没找到调用方。
mp3.stop←audply_stop←audio_player_stop。

**headset_ref_design文件夹中的audio_player_main.c,上层调用关系有些不一样,**但是audio_player_task_main→audio_player_processing→audio_player_check_threshold 这部分流程大致是一样的

g_file_med_hd结构体为

typedef struct {
    am_file_type_t type;
    union {
        **bt_sink_srv_am_mp3_media_handle_t mp3;**
    } media_handle;
} bt_sink_srv_am_files_media_handle_t;

bt_sink_srv_am_mp3_media_handle_t结构体为

typedef struct {
    void (*get_write_buffer)(bt_sink_srv_am_id_t aud_id, uint8_t **buffer, uint32_t *length);                 
/**< 获取写入共享缓冲区的可用长度和指向共享缓冲区的指针。 */
    void (*get_read_buffer)(bt_sink_srv_am_id_t aud_id, uint8_t **buffer, uint32_t *length);                
/**< 获取写入(注释确实是write)共享缓冲区的可用长度和指向共享缓冲区的指针 */
    void (*write_data_done)(bt_sink_srv_am_id_t aud_id, uint32_t length);  /**<更新指向共享缓冲区的写指针 */
    void (*finish_write_data)(bt_sink_srv_am_id_t aud_id);                 /**< 指示最后一次数据传输。*/
    int32_t (*get_data_count)(bt_sink_srv_am_id_t aud_id);                 /**< 从共享缓冲区获取数据长度. */
    int32_t (*get_free_space)(bt_sink_srv_am_id_t aud_id);                 /**< 从共享缓冲区获取可用长度 */
    int32_t (*play)(bt_sink_srv_am_id_t aud_id);                                                    /**< The MP3 codec play function. */
    int32_t (*pause)(bt_sink_srv_am_id_t aud_id);                                                   /**< The MP3 codec pause function. */
    int32_t (*resume)(bt_sink_srv_am_id_t aud_id);                                                  /**< The MP3 codec resume function. */
    int32_t (*stop)(bt_sink_srv_am_id_t aud_id);                                                    /**< The MP3 codec stop function. */
    int32_t (*close_codec)(bt_sink_srv_am_id_t aud_id);                                             /**< The MP3 codec close_codec function. */
    int32_t (*skip_id3v2_and_reach_next_frame)(bt_sink_srv_am_id_t aud_id, uint32_t file_size);     /**< Skip id3v2 header and reach next frame */
    int32_t (*set_silence_frame_information)(bt_sink_srv_am_id_t aud_id, silence_frame_information_t *frm_info);    
/**< 设置静音帧信息 */
    int32_t (*fill_silence_frame)(bt_sink_srv_am_id_t aud_id, uint8_t *buffer);                                     
/**< 获取静音帧 */
    int32_t (*get_data_status)(bt_sink_srv_am_id_t aud_id, mp3_codec_data_type_t type, int32_t *status);
    int32_t (*flush)(bt_sink_srv_am_id_t aud_id, int32_t flush_data_flag);

#ifdef __BT_AWS_SUPPORT__
    int32_t (*set_aws_flag)(bt_sink_srv_am_id_t aud_id, bool flag);
    int32_t (*set_aws_initial_sync)(bt_sink_srv_am_id_t aud_id);
    int32_t (*aws_init)(void);
    int32_t (*aws_deinit)(void);
    int32_t (*aws_set_clock_skew_compensation_value)(int32_t sample_count);
    int32_t (*aws_get_clock_skew_status)(int32_t *status);
    int32_t (*aws_set_clock_skew)(bool flag);
#endif /* __BT_AWS_SUPPORT__ */
} bt_sink_srv_am_mp3_media_handle_t;

aac的play demo,是从sd卡里打开文件,然后调用decoder的api进行处理
函数调用如下,看函数名即可。
aac_play_demo
├──play
│ ├──test_open_file_from_sd(&fdst, _T(“SD:/AAC/v1.aac”)
│ ├──hal_audio_set_stream_out_volume
│ ├──hal_audio_set_stream_out_device
│ ├──hdl = aac_deocder_api_open(test_sd_event_callback);
│ ├──hdl->set_share_buffer(hdl, share_buffer, SHARE_BUF_SIZE);
│ ├──hdl->get_write_buffer(hdl, &share_buf, &share_buf_len);
│ ├──f_read(&fdst, share_buf, share_buf_len, &length_read);
│ ├──hdl->write_data_done(hdl, length_read);
│ ├──hdl->finish_write_data(hdl);
│ ├──hdl->play(hdl);
│ │ ├──aac_decoder_api_play
│ │ │ ├──aac_decoder_api_event_register_callback
│ │ │ ├──aac_decoder_api_reset_pcm_out_buffer
│ │ │ ├──aac_decoder_api_request_data
│ │ │ ├──aac_decoder_init
│ │ │ ├──aac_decoder_api_decode
│ │ │ ├──hal_audio_set_stream_out_sampling_rate
│ │ │ ├──hal_audio_set_stream_out_channel_number
│ │ │ ├──hal_audio_register_stream_out_callback
│ │ │ ├──hal_audio_start_stream_out
//注释写的是打开模拟和数字音频硬件的电源以输出音频
│ │ │ ├──aac_decoder_api_event_send_from_isr

主要操作都通过调用decoder提供的api进行。

/** @brief HE-AAC audio handle structure type.*/
typedef struct _aac_decoder_api_media_handle_t {
    aac_decoder_api_state_t     state;      /**< The AAC decoder state. */
	aac_decoder_api_share_buffer_t   share_buffer;        
/**<  AAC decoder的共享缓存区. */
	bool                     underflow;
   /**< The AAC decoder data underflow occurred. */
	bool             waiting;             			
/**< AAC decoder正在等待填充数据 */
	uint16_t           audio_id;           
/**< The audio ID of the AAC decoder. */
void (*handler)(struct _aac_decoder_api_media_handle_t *handle, aac_decoder_api_media_event_t event_idd);
/**< The AAC decoder handler. */
void (*set_share_buffer)(struct _aac_decoder_api_media_handle_t *handle, uint8_t  *buffer, uint32_t  length); 
/**<设置bitstream的共享缓冲区。 可以通过共享缓冲区填充音频流数据*/
void (*get_write_buffer)(struct _aac_decoder_api_media_handle_t *handle, uint8_t **buffer, uint32_t *length);   
/**<获取向共享缓冲区写入的可用长度和指向共享缓冲区的指针。 */
void (*get_read_buffer)(struct _aac_decoder_api_media_handle_t *handle, uint8_t **buffer, uint32_t *length); 
/**< 获取从共享缓冲区读取的可用长度和指向共享缓冲区的指针。 */
void (*write_data_done)(struct _aac_decoder_api_media_handle_t *handle, uint32_t length); 
/**< 更新指向共享缓冲区的写指针. */
void (*finish_write_data)(struct _aac_decoder_api_media_handle_t *handle);           
/**< 最后一次数据传输. */
void (*reset_share_buffer)(struct _aac_decoder_api_media_handle_t *handle);                               
/**< 重置共享缓冲区的信息. */
void (*read_data_done)(struct _aac_decoder_api_media_handle_t *handle, uint32_t length);               
/**< 更新指向共享缓冲区的读指针. */
int32_t (*get_free_space)(struct _aac_decoder_api_media_handle_t *handle);          
/**<获取共享缓冲区中可用的可用空间长度。 (以字节为单位)*/
int32_t (*get_data_count)(struct _aac_decoder_api_media_handle_t *handle);        
/**< 获取共享缓冲区的可用数据量。 */
aac_decoder_api_status_t (*play)(struct _aac_decoder_api_media_handle_t *handle);                        
/**< The AAC decoder play function. */
aac_decoder_api_status_t (*pause)(struct _aac_decoder_api_media_handle_t *handle);                      
/**< The AAC decoder pause function. */
aac_decoder_api_status_t (*resume)(struct _aac_decoder_api_media_handle_t *handle);      
/**< The AAC decoder resume function. */
aac_decoder_api_status_t (*process)(struct _aac_decoder_api_media_handle_t *handle, aac_decoder_api_media_event_t event_id);  
/**< The AAC decoder process function. */
aac_decoder_api_status_t (*stop)(struct _aac_decoder_api_media_handle_t *handle);                
/**< The AAC decoder stop function. */
    aac_decoder_api_status_t (*flush)(struct _aac_decoder_api_media_handle_t *handle,int32_t flush_data_flag);
} aac_decoder_api_media_handle_t;

炬芯的ATS3503_WIFI

淘宝/ATS3503_WIFI/samples/storyapp/src/storyplayer/storyplayer_main.c

主要文件为storyplayer_main.c和storyplayer_play.c

在storyplayer_main.c中
storyapp_main函数调用k_thread_spawn创建主线程,运行storyapp_main_loop

在storyapp_main_loop循环中,switch (msg.type),调用不同的函数。
case类型为
MSG_EXIT_APP:退出app
MSG_PLAY_DEFAULT_MUSIC:播放默认音乐
MSG_PLAY_NEW_LIST:播放新的列表
MSG_PLAY_PAUSE_RESUME:播放暂停/恢复
MSG_PLAY_NEXT_MUSIC:播放下一首
MSG_PLAY_PREVIOUS_MUSIC:播放上一首
MSG_PLAY_CUR_MUSIC:播放当前音乐
MSG_PLAY_URL
MSG_PLAY_SEEK
MSG_PLAY_HANDLE_CARD_EVENT
MSG_PLAY_MUSIC_ERROR
在这里插入图片描述

以MSG_PLAY_DEFAULT_MUSIC播放默认音乐为例,会调用 handle_default_play_msg,进而调用storyplayer_play.c中的storyplay_default_music。
忽略playlist的相关操作,会调用storyplay_cur_music→play_cur_local_music,获取播放流的type和url。其中stream type有五种,但就播放部分应该只用到了file stream和net stream。
然后会调用send_msg_to_storyplayer(MSG_PLAY_URL, &parm),发送消息给storyplayer,再次在loop的switch中进行判断,case MSG_PLAY_URL。
调用handle_play_url_msg→storyplay_play_url,根据播放流的type和url,创建play_stream,再调用mplayer_start_stream。

但是stream_create和mplayer_star_stream这两个函数只在app_common_api.h中找到函数声明,且decode部分也未找到,代码可能不全?
相当于只有预处理部分,decode和output流程不明。

在ext/lib/actions/libAL/有找到一些.a文件,在arch/mips/soc/ats3503/linker.ld 有用到,但具体流程不明。
./ext/lib/actions/libAL/libdecaac.a
./ext/lib/actions/libAL/libdecact.a
./ext/lib/actions/libAL/libdecamr.a
./ext/lib/actions/libAL/libdecape.a
./ext/lib/actions/libAL/libdecflac.a
./ext/lib/actions/libAL/libdecmp3.a
./ext/lib/actions/libAL/libdecwav.a
./ext/lib/actions/libAL/libdecwma.a

全志的XR871

在这里插入图片描述

先调用player_task_init,用OS_ThreadCreate创建player_task线程,同时创建一个observer,事件发生时回调将被触发,是用来监控gpio button的。
在player_task线程中,while (player_task_run)进行循环。player_task_run在player_task_init中设为1,在player_task_deinit中设为0,当退出循环时线程就自我删除。
线程中调用read_payer_ctrl_cmd,根据按键状态获取cmd。switch (cmd),然后调用不同的函数。
case共五种
case CMD_PLAYER_NEXT:
case CMD_PLAYER_PERV:
case CMD_PLAYER_VOLUME_UP:
case CMD_PLAYER_VOLUME_DOWN:
case CMD_PLAYER_PAUSE:
以CMD_PLAYER_NEXT为例,忽略ui部分,调用player_read_songs,读取mp3文件名,再调用play_songs,根据传入参数song_name,去播放file://music/song_name。
再往下调用play→XPlayerStart→XPlayerStart。只在xplayer.h中找到声明,找不到具体函数,似乎也是代码不全?
https://github.com/XradioTech/XR871SDK 中也没有找到具体函数。

解码部分找到一个cedarx库,在03_SDK\xr871sdk\src\smartlink\voice_print中被调用,似乎是用作声纹识别。
http://linux-sunxi.org/CedarX

版权问题:

乐鑫ESP32
esp-adf的LICENSE

ESPRESSIF MIT License

Copyright © 2018 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>

Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case,
it is free of charge, to any person obtaining a copy of this software and associated
documentation files (the “Software”), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

LinkIt的player
/* Copyright Statement:
*

  • © 2005-2016 MediaTek Inc. All rights reserved.
  • This software/firmware and related documentation (“MediaTek Software”) are
  • protected under relevant copyright laws. The information contained herein
  • is confidential and proprietary to MediaTek Inc. (“MediaTek”) and/or its licensors.
  • Without the prior written permission of MediaTek and/or its licensors,
  • any reproduction, modification, use or disclosure of MediaTek Software,
  • and information contained herein, in whole or in part, shall be strictly prohibited.
  • You may only use, reproduce, modify, or distribute (as applicable) MediaTek Software
  • if you have agreed to and been bound by the applicable license agreement with
  • MediaTek (“License Agreement”) and been granted explicit permission to do so within
  • the License Agreement (“Permitted User”). If you are not a Permitted User,
  • please cease any access or use of MediaTek Software immediately.
  • BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
  • THAT MEDIATEK SOFTWARE RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES
  • ARE PROVIDED TO RECEIVER ON AN “AS-IS” BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL
  • WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
  • MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT.
  • NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH RESPECT TO THE
  • SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, INCORPORATED IN, OR
  • SUPPLIED WITH MEDIATEK SOFTWARE, AND RECEIVER AGREES TO LOOK ONLY TO SUCH
  • THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. RECEIVER EXPRESSLY ACKNOWLEDGES
  • THAT IT IS RECEIVER’S SOLE RESPONSIBILITY TO OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES
  • CONTAINED IN MEDIATEK SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK
  • SOFTWARE RELEASES MADE TO RECEIVER’S SPECIFICATION OR TO CONFORM TO A PARTICULAR
  • STANDARD OR OPEN FORUM. RECEIVER’S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK’S ENTIRE AND
  • CUMULATIVE LIABILITY WITH RESPECT TO MEDIATEK SOFTWARE RELEASED HEREUNDER WILL BE,
  • AT MEDIATEK’S OPTION, TO REVISE OR REPLACE MEDIATEK SOFTWARE AT ISSUE,
  • OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE CHARGE PAID BY RECEIVER TO
  • MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE.
    */

炬芯的ATS3503_WIFI的story_player.c
/*

  • Copyright © 2017 Actions Semi Co., Ltd.
  • Licensed under the Apache License, Version 2.0 (the “License”);
  • you may not use this file except in compliance with the License.
  • You may obtain a copy of the License at
  • http://www.apache.org/licenses/LICENSE-2.0
  • Unless required by applicable law or agreed to in writing, software
  • distributed under the License is distributed on an “AS IS” BASIS,
  • WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  • See the License for the specific language governing permissions and
  • limitations under the License.

全志的XR817的audio_player.c

  • Copyright © 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved.
  • Redistribution and use in source and binary forms, with or without
  • modification, are permitted provided that the following conditions
  • are met:
    1. Redistributions of source code must retain the above copyright
  • notice, this list of conditions and the following disclaimer.
    1. Redistributions in binary form must reproduce the above copyright
  • notice, this list of conditions and the following disclaimer in the
  • documentation and/or other materials provided with the
  • distribution.
    1. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of
  • its contributors may be used to endorse or promote products derived
  • from this software without specific prior written permission.
  • THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  • “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  • LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  • A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  • OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  • SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  • LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  • DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  • THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  • (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  • OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值