sdk-hwV1.3/lichee/xr806/appos/project/common/apps/player_app.c

593 lines
16 KiB
C
Executable File

/*
* Copyright (C) 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.
* 2. 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.
* 3. 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.
*/
#ifdef CONFIG_XPLAYER
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include "cedarx/xplayer/include/xplayer.h"
#include "driver/chip/hal_snd_card.h"
#include "audio/manager/audio_manager.h"
#include "kernel/os/os.h"
#include "util/atomic.h"
#include "sys/defs.h"
#include "common/framework/sys_ctrl/sys_ctrl.h"
#include "player_app.h"
#define USER_AGENT "Mozilla/5.0 (FreeRTOS; OS 10.2.1) Xradio Xradio/1.0"
#define PLAYER_LOGD(msg, arg...) //printf("[PLAYER_DBG] <%s : %d> " msg "\n", __func__, __LINE__, ##arg)
#define PLAYER_LOGI(msg, arg...) printf("[PLAYER_INFO] <%s : %d> " msg "\n", __func__, __LINE__, ##arg)
#define PLAYER_LOGW(msg, arg...) printf("[PLAYER_WRN] <%s : %d> " msg "\n", __func__, __LINE__, ##arg)
#define PLAYER_LOGE(msg, arg...) printf("[PLAYER_ERR] <%s : %d> " msg "\n", __func__, __LINE__, ##arg)
#define APLAYER_ID_AND_STATE(id, state) (((id) << 16) | (state))
#define APLAYER_GET_ID(id_state) (((id_state) >> 16) & 0xFFFF)
#define APLAYER_GET_STATE(id_state) ((id_state) & 0xFFFF)
typedef struct player_info {
char *url;
uint32_t size;
} player_info;
typedef struct app_player {
player_base base;
XPlayer *xplayer;
SoundCtrl *sound;
CdxKeyedVectorT *pHeaders;
aplayer_states state;
app_player_callback cb;
player_info info;
OS_Mutex_t lock;
uint8_t mute;
int vol;
uint16_t id;
void *arg;
} app_player;
struct player_handler_param {
app_player *impl;
uint32_t id_state;
};
static void player_handler(event_msg *msg);
static void *wrap_realloc(void *p, uint32_t *osize, uint32_t nsize)
{
if (p == NULL) {
*osize = nsize;
return malloc(nsize);
}
if (*osize >= nsize)
return p;
free(p);
PLAYER_LOGD("free %d, malloc %d", *osize, nsize);
*osize = nsize;
return malloc(nsize);
}
static int set_url(app_player *impl, char *pUrl)
{
struct player_handler_param *handler_param;
/* set url to the AwPlayer. */
if (XPlayerSetDataSourceUrl(impl->xplayer, (const char *)pUrl, NULL, impl->pHeaders) != 0) {
PLAYER_LOGE("setDataSource() return fail.");
return -1;
}
if ((!strncmp(pUrl, "http://", 7)) || (!strncmp(pUrl, "https://", 8)) ||
(!strncmp(pUrl, "fifo://", 7))) {
if (XPlayerPrepareAsync(impl->xplayer) != 0) {
PLAYER_LOGE("prepareAsync() return fail.");
return -1;
}
} else {
handler_param = malloc(sizeof(*handler_param));
if (handler_param == NULL) {
PLAYER_LOGE("memory malloc fail.");
return -1;
}
handler_param->impl = impl;
handler_param->id_state = APLAYER_ID_AND_STATE(impl->id, PLAYER_EVENTS_MEDIA_PREPARED);
sys_handler_send(player_handler, (uint32_t)handler_param, 10000);
}
return 0;
}
static int play(app_player *impl)
{
if (XPlayerStart(impl->xplayer) != 0) {
PLAYER_LOGE("start() return fail.");
return -1;
}
PLAYER_LOGD("playing");
return 0;
}
static int reset(app_player *impl)
{
if (XPlayerReset(impl->xplayer) != 0) {
PLAYER_LOGE("reset() return fail.");
return -1;
}
impl->id++;
return 0;
}
static void player_handler_preprocess(app_player *impl, player_events evt)
{
PLAYER_LOGI("event: %d", (int)evt);
OS_RecursiveMutexLock(&impl->lock, OS_WAIT_FOREVER);
switch (evt) {
case PLAYER_EVENTS_MEDIA_PREPARED:
impl->cb(evt, NULL, impl->arg);
if (impl->state != APLAYER_STATES_PAUSE) {
play(impl);
}
impl->state = APLAYER_STATES_PLAYING;
break;
case PLAYER_EVENTS_MEDIA_PLAYBACK_COMPLETE:
case PLAYER_EVENTS_MEDIA_ERROR:
reset(impl);
impl->state = APLAYER_STATES_STOPPED;
break;
default:
break;
}
OS_RecursiveMutexUnlock(&impl->lock);
if (evt != PLAYER_EVENTS_MEDIA_PREPARED) {
impl->cb(evt, NULL, impl->arg);
}
return;
}
static void player_handler(event_msg *msg)
{
struct player_handler_param *handler_param;
app_player *impl;
player_events state;
uint16_t id;
handler_param = (struct player_handler_param *)msg->data;
impl = handler_param->impl;
state = APLAYER_GET_STATE(handler_param->id_state);
id = APLAYER_GET_ID(handler_param->id_state);
free((void *)msg->data);
OS_RecursiveMutexLock(&impl->lock, OS_WAIT_FOREVER);
if (id != impl->id) {
OS_RecursiveMutexUnlock(&impl->lock);
return;
}
OS_RecursiveMutexUnlock(&impl->lock);
player_handler_preprocess(impl, state);
}
static int player_callback(void *pUserData, int msg, int ext1, void *param)
{
app_player *impl = (app_player *)pUserData;
struct player_handler_param *handler_param;
switch (msg) {
case AWPLAYER_MEDIA_INFO:
switch (ext1) {
case AW_MEDIA_INFO_NOT_SEEKABLE:
PLAYER_LOGI("media source is unseekable.");
break;
default:
break;
}
break;
case AWPLAYER_MEDIA_ERROR:
PLAYER_LOGW("open media source fail.\n"
"reason: maybe the network is bad, or the music file is not good.");
handler_param = malloc(sizeof(*handler_param));
if (handler_param == NULL) {
PLAYER_LOGE("memory malloc fail.");
break;
}
handler_param->impl = impl;
handler_param->id_state = APLAYER_ID_AND_STATE(impl->id, PLAYER_EVENTS_MEDIA_ERROR);
sys_handler_send(player_handler, (uint32_t)handler_param, 10000);
break;
case AWPLAYER_MEDIA_PREPARED:
handler_param = malloc(sizeof(*handler_param));
if (handler_param == NULL) {
PLAYER_LOGE("memory malloc fail.");
break;
}
handler_param->impl = impl;
handler_param->id_state = APLAYER_ID_AND_STATE(impl->id, PLAYER_EVENTS_MEDIA_PREPARED);
sys_handler_send(player_handler, (uint32_t)handler_param, 10000);
break;
case AWPLAYER_MEDIA_PLAYBACK_COMPLETE:
PLAYER_LOGI("playback complete.");
handler_param = malloc(sizeof(*handler_param));
if (handler_param == NULL) {
PLAYER_LOGE("memory malloc fail.");
break;
}
handler_param->impl = impl;
handler_param->id_state = APLAYER_ID_AND_STATE(impl->id, PLAYER_EVENTS_MEDIA_PLAYBACK_COMPLETE);
sys_handler_send(player_handler, (uint32_t)handler_param, 10000);
break;
case AWPLAYER_MEDIA_SEEK_COMPLETE:
PLAYER_LOGI("seek ok.");
break;
default:
PLAYER_LOGD("unknown callback from AwPlayer");
break;
}
PLAYER_LOGI("cedarx cb complete.");
return 0;
}
static int player_stop(player_base *base)
{
app_player *impl = container_of(base, app_player, base);
struct player_handler_param *handler_param;
OS_RecursiveMutexLock(&impl->lock, OS_WAIT_FOREVER);
reset(impl);
impl->state = APLAYER_STATES_STOPPED;
handler_param = malloc(sizeof(*handler_param));
if (handler_param == NULL) {
PLAYER_LOGE("memory malloc fail.");
OS_RecursiveMutexUnlock(&impl->lock);
return -1;
}
handler_param->impl = impl;
handler_param->id_state = APLAYER_ID_AND_STATE(impl->id, PLAYER_EVENTS_MEDIA_STOPPED);
sys_handler_send(player_handler, (uint32_t)handler_param, 10000);
OS_RecursiveMutexUnlock(&impl->lock);
OS_MSleep(10);
return 0;
}
static int player_pause(player_base *base)
{
app_player *impl = container_of(base, app_player, base);
int ret = 0;
OS_RecursiveMutexLock(&impl->lock, OS_WAIT_FOREVER);
ret = XPlayerPause(impl->xplayer);
impl->state = APLAYER_STATES_PAUSE;
OS_RecursiveMutexUnlock(&impl->lock);
return ret;
}
static int player_resume(player_base *base)
{
app_player *impl = container_of(base, app_player, base);
int ret = 0;
OS_RecursiveMutexLock(&impl->lock, OS_WAIT_FOREVER);
ret = play(impl);
impl->state = APLAYER_STATES_PLAYING;
OS_RecursiveMutexUnlock(&impl->lock);
return ret;
}
static int player_seek(player_base *base, int ms)
{
app_player *impl = container_of(base, app_player, base);
int ret = 0;
OS_RecursiveMutexLock(&impl->lock, OS_WAIT_FOREVER);
if (XPlayerSeekTo(impl->xplayer, ms) != 0) {
PLAYER_LOGE("seek() return fail.");
OS_RecursiveMutexUnlock(&impl->lock);
return -1;
}
PLAYER_LOGI("seek to %d ms.", ms);
OS_RecursiveMutexUnlock(&impl->lock);
return ret;
}
static int player_seturl(player_base *base, const char *url)
{
int ret = 0;
app_player *impl = container_of(base, app_player, base);
char *play_url = (char *)url;
if (url == NULL)
return -1;
OS_RecursiveMutexLock(&impl->lock, OS_WAIT_FOREVER);
impl->state = APLAYER_STATES_PREPARING;
PLAYER_LOGI("request to play : %s", url);
impl->info.url = wrap_realloc(impl->info.url, &impl->info.size, strlen(play_url) + 1);
memcpy(impl->info.url, play_url, strlen(play_url) + 1);
ret = set_url(impl, impl->info.url);
if (ret) {
reset(impl);
impl->state = APLAYER_STATES_STOPPED;
}
OS_RecursiveMutexUnlock(&impl->lock);
return ret;
}
static int player_tell(player_base *base)
{
app_player *impl = container_of(base, app_player, base);
int ms;
OS_RecursiveMutexLock(&impl->lock, OS_WAIT_FOREVER);
if (XPlayerGetCurrentPosition(impl->xplayer, &ms) != 0) {
PLAYER_LOGW("tell() return fail.");
OS_RecursiveMutexUnlock(&impl->lock);
return 0;
}
PLAYER_LOGI("tell to %d ms.", ms);
OS_RecursiveMutexUnlock(&impl->lock);
return ms;
}
static int player_size(player_base *base)
{
app_player *impl = container_of(base, app_player, base);
int ms;
OS_RecursiveMutexLock(&impl->lock, OS_WAIT_FOREVER);
if (XPlayerGetDuration(impl->xplayer, &ms) != 0) {
PLAYER_LOGW("size() return fail.");
OS_RecursiveMutexUnlock(&impl->lock);
return 0;
}
PLAYER_LOGI("size to %d ms.", ms);
OS_RecursiveMutexUnlock(&impl->lock);
return ms;
}
static int player_setvol(player_base *base, int vol)
{
app_player *impl = container_of(base, app_player, base);
if (vol > 31) {
PLAYER_LOGW("set vol %d larger than 31", vol);
vol = 31;
} else if (vol < 0) {
PLAYER_LOGW("set vol %d lesser than 0", vol);
vol = 0;
}
impl->vol = vol;
audio_manager_handler(AUDIO_SND_CARD_DEFAULT, AUDIO_MANAGER_SET_VOLUME_LEVEL, AUDIO_OUT_DEV_SPK, vol);
return 0;
}
static int player_getvol(player_base *base)
{
app_player *impl = container_of(base, app_player, base);
return impl->vol;
}
static int player_mute(player_base *base, bool is_mute)
{
app_player *impl = container_of(base, app_player, base);
impl->mute = (uint8_t)is_mute;
audio_manager_handler(AUDIO_SND_CARD_DEFAULT, AUDIO_MANAGER_SET_MUTE, AUDIO_OUT_DEV_SPK, is_mute);
return 0;
}
static int player_is_mute(player_base *base)
{
app_player *impl = container_of(base, app_player, base);
return impl->mute;
}
static aplayer_states player_get_states(player_base *base)
{
app_player *impl = container_of(base, app_player, base);
return impl->state;
}
static int player_control(player_base *base, player_cmd command, void *data)
{
app_player *impl = container_of(base, app_player, base);
switch (command) {
case PLAYER_CMD_SET_OUTPUT_CONFIG:
SoundDeviceControl(impl->sound, SOUND_CONTROL_SET_OUTPUT_CONFIG, data);
break;
case PLAYER_CMD_ADD_OUTPUT_CONFIG:
SoundDeviceControl(impl->sound, SOUND_CONTROL_ADD_OUTPUT_CONFIG, data);
break;
case PLAYER_CMD_CLEAR_OUTPUT_CONFIG:
SoundDeviceControl(impl->sound, SOUND_CONTROL_CLEAR_OUTPUT_CONFIG, data);
break;
case PLAYER_CMD_SET_EQ_MODE:
SoundDeviceControl(impl->sound, SOUND_CONTROL_SET_EQ_MODE, data);
break;
case PLAYER_CMD_CLEAR_EQ_MODE:
SoundDeviceControl(impl->sound, SOUND_CONTROL_CLEAR_EQ_MODE, data);
break;
case PLAYER_CMD_SET_DRC_MODE:
SoundDeviceControl(impl->sound, SOUND_CONTROL_SET_DRC_MODE, data);
break;
case PLAYER_CMD_CLEAR_DRC_MODE:
SoundDeviceControl(impl->sound, SOUND_CONTROL_CLEAR_DRC_MODE, data);
break;
default:
break;
}
return 0;
}
static void player_null_callback(player_events event, void *data, void *arg)
{
PLAYER_LOGI("cb event:%d", event);
}
static void player_setcb(player_base *base, app_player_callback cb, void *arg)
{
app_player *impl = container_of(base, app_player, base);
if (!cb)
impl->cb = player_null_callback;
else
impl->cb = cb;
impl->arg = arg;
}
player_base *player_create()
{
OS_Status status;
app_player *impl = NULL;
#if (CONFIG_CEDARX_HEAP_MODE == 1)
XPlayerBufferConfig xplayerConfig;
HttpStreamBufferConfig httpConfig;
#endif
impl = malloc(sizeof(*impl));
if (impl == NULL) {
return NULL;
}
memset(impl, 0, sizeof(*impl));
impl->xplayer = XPlayerCreate();
if (impl->xplayer == NULL) {
goto err0;
}
#if (CONFIG_CEDARX_HEAP_MODE == 1)
memset(&xplayerConfig, 0, sizeof(XPlayerBufferConfig));
xplayerConfig.maxMovStcoBufferSize = 8 * 1024;
xplayerConfig.maxMovStszBufferSize = 64 * 1024;
XPlayerSetBuffer(impl->xplayer, &xplayerConfig);
memset(&httpConfig, 0, sizeof(HttpStreamBufferConfig));
httpConfig.maxBufferSize = (128 * 1024);
httpConfig.maxProtectAreaSize = (32 * 1024);
XPlayerSetHttpBuffer(impl->xplayer, &httpConfig);
#endif
/* set callback to player. */
XPlayerSetNotifyCallback(impl->xplayer, player_callback, (void *)impl);
/* check if the player work. */
if (XPlayerInitCheck(impl->xplayer) != 0) {
goto err1;
}
SoundCtrl *SoundDeviceCreate();
impl->sound = SoundDeviceCreate();
if (impl->sound == NULL) {
goto err1;
}
XPlayerSetAudioSink(impl->xplayer, (void *)impl->sound);
impl->pHeaders = malloc(sizeof(CdxKeyedVectorT) + 1 * sizeof(KeyValuePairT));
if (impl->pHeaders) {
impl->pHeaders->size = 1;
impl->pHeaders->item[0].key = "User-Agent";
impl->pHeaders->item[0].val = USER_AGENT;
}
impl->base.play = player_seturl;
impl->base.stop = player_stop;
impl->base.pause = player_pause;
impl->base.resume = player_resume;
impl->base.seek = player_seek;
impl->base.tell = player_tell;
impl->base.size = player_size;
impl->base.setvol = player_setvol;
impl->base.getvol = player_getvol;
impl->base.mute = player_mute;
impl->base.is_mute = player_is_mute;
impl->base.control = player_control;
impl->base.set_callback = player_setcb;
impl->base.get_status = player_get_states;
impl->cb = player_null_callback;
status = OS_RecursiveMutexCreate(&impl->lock);
if (status != OS_OK) {
goto err1;
}
impl->state = APLAYER_STATES_INIT;
return &impl->base;
PLAYER_LOGE("create player failed, quit.");
err1:
/* it will destroy SoundDevice too */
XPlayerDestroy(impl->xplayer);
free(impl->pHeaders);
err0:
free(impl);
return NULL;
}
void player_destroy(player_base *base)
{
app_player *impl = container_of(base, app_player, base);
PLAYER_LOGI("destroy AwPlayer.");
player_stop(base);
OS_RecursiveMutexDelete(&impl->lock);
if (impl->xplayer != NULL)
XPlayerDestroy(impl->xplayer);
if (impl->info.url)
free(impl->info.url);
if (impl->pHeaders)
free(impl->pHeaders);
if (impl != NULL)
free(impl);
}
#endif