RTOS 多媒體解碼
介紹 FreeRTOS 下如何使用 rtplayer 的接口來(lái)開(kāi)發(fā)播放器應(yīng)用程序,方便播放器開(kāi)發(fā)人員快速正確地開(kāi)發(fā),以及播放器測(cè)試人員如何根據(jù)該文檔對(duì) rtplayer 播放器進(jìn)行驗(yàn)證測(cè)試。
RTPLAYER 狀態(tài)圖
這張狀態(tài)轉(zhuǎn)換圖清晰地描述了 rtlayer 的各個(gè)狀態(tài),也列舉了主要的方法的調(diào)用時(shí)序,每種方法只能在一些特定的狀態(tài)下使用,否則會(huì)出錯(cuò)。另外,只有在 Prepared、Started、Paused、Play?backCompleted 這四種狀態(tài)下可以進(jìn)行 seekTo() 操作,并且 seekTo() 之后,狀態(tài)不變。
Idle 狀態(tài)
Idle 狀態(tài):當(dāng)調(diào)用 player_init() 創(chuàng)建一個(gè) rtplayer 或者調(diào)用了其 reset() 方法時(shí),rtplayer 處于 idle狀態(tài)。
Initialized 狀態(tài)
這個(gè)狀態(tài)比較簡(jiǎn)單,調(diào)用 setDateSource_url() 方法就進(jìn)入 Initialized 狀態(tài),表示此時(shí)要播放的文件已經(jīng)設(shè)置好了
Preparing 狀態(tài)
調(diào)用 prepare() 函數(shù)還沒(méi)返回或者是調(diào)用 prepareAsync() 并且還沒(méi)收到 RTPLAYER_NOTIFY_PREPARED 這個(gè)回調(diào)消息的時(shí)候就處于 Preparing 狀態(tài)
Prepared 狀態(tài)
調(diào)用 prepare() 函數(shù)已經(jīng)返回或者是調(diào)用 prepareAsync() 并且已經(jīng)收到 RTPLAYER_NOTIFY_PREPARED 這個(gè)回調(diào)消息之后的狀態(tài)就處于 Prepared 狀態(tài)。在這個(gè)狀態(tài)下說(shuō)明所有的資源都已經(jīng)就緒了,調(diào)用 start() 函數(shù)就可以播放了。
Started 狀態(tài)
rtplayer 一旦 prepare 完成,就可以調(diào)用 start() 方法,這樣 rtplayer 就處于 Started 狀態(tài),這表明 rtplayer 正在播放文件過(guò)程中。可以使用 XPlayerIsPlaying() 測(cè)試 rtplayer 是否處于了 Started 狀態(tài)。如果播放完畢,而又設(shè)置了循環(huán)播放,則 rtplayer 仍然會(huì)處于 Started 狀態(tài)。
Paused 狀態(tài)
Started 狀態(tài)下可以調(diào)用 pause_l() 方法暫停 rtplayer,從而進(jìn)入 Paused 狀態(tài),rtplayer 暫停后再次調(diào)用 start() 則可以繼續(xù) TPlayer 的播放,轉(zhuǎn)到 Started 狀態(tài)。
Stopped 狀態(tài)
Started 或者 Paused 狀態(tài)下均可調(diào)用 stop() 停止 rtplayer,而處于 Stop 狀態(tài)的 rtplayer 要想重新播放,需要通過(guò) prepareAsync() 和 prepare() 回到先前的 Prepared 狀態(tài)重新開(kāi)始才可以。
PlaybackCompleted 狀態(tài)
文件正常播放完畢,而又沒(méi)有設(shè)置循環(huán)播放的話就進(jìn)入該狀態(tài),并且會(huì)通過(guò) RTPLAYER_NOTIFY_PLAYBACK_COMPLETE 這個(gè)消息回調(diào)給應(yīng)用。此時(shí)可以調(diào)用 start() 方法重新從頭播放文件,也可以 stop() 停止 rtplayer,或者也可以 seekTo() 來(lái)重新定位播放位置。
Error 狀態(tài)
由于某種原因 rtplayer 出現(xiàn)了錯(cuò)誤,就會(huì)進(jìn)入該狀態(tài),并且會(huì)通過(guò) RTPLAYER_NOTIFY_MEDIA_ERROR 這個(gè)消息回調(diào)給應(yīng)用。如果 rtplayer 進(jìn)入了 Error 狀態(tài),可以通過(guò)調(diào)用 reset() 來(lái)恢復(fù),使得 rtplayer 重新返回到 Idle 狀態(tài)。
End 狀態(tài)
通過(guò) plater_deinit() 的方法可以進(jìn)入 End 狀態(tài),只要 rtplayer 不再被使用,就應(yīng)當(dāng)盡快將其 destroy 掉。
rtplayer 層接口
創(chuàng)建一個(gè) RTPLAYER
函數(shù)原型
uint32_t player_init(void)
- 無(wú)
返回值:
- 成功返回 rtplayer 的指針,失敗返回 NULL
銷毀一個(gè) RTPLAYER
函數(shù)原型
void player_deinit(void* handle)
參數(shù):
- handle: 通過(guò) player_init() 函數(shù)創(chuàng)建的 rtplayer 指針
返回值:
- 無(wú)
設(shè)置 RTPLAYER 的消息回調(diào)函數(shù)
函數(shù)原型
void registerCallback(void* handle, void* userData, player_callback_t fn)
參數(shù):
- handle: 通過(guò) player_init() 函數(shù)創(chuàng)建的 rtplayer 指針
- userData: 回調(diào)消息處理對(duì)象
- fn: 回調(diào)消息處理函數(shù)指針,需要由應(yīng)用實(shí)現(xiàn)
返回值:
- 無(wú)
創(chuàng)建完 rtplayer 播放器之后,就要調(diào)用該函數(shù)設(shè)置回調(diào)消息處理函數(shù)。
設(shè)置播放文件的 URL
可以是本地文件也可以是網(wǎng)絡(luò)源
函數(shù)原型
status_t setDataSource_url(void* handle,void* userData, const char * url, int id)
參數(shù):
- handle: 通過(guò) player_init() 函數(shù)創(chuàng)建的 rtplayer 指針
- userData: 回調(diào)消息處理對(duì)象
- url: 需要播放的文件的 url
- id: 回調(diào)時(shí)使用的播放索引, 為 0 即可
返回值:
- 成功返回 0,失敗返回?1 或錯(cuò)誤碼
解析文件頭部信息,獲取元數(shù)據(jù)
函數(shù)原型
status_t prepare(void* handle)
參數(shù):
- handle: 通過(guò) player_init() 函數(shù)創(chuàng)建的 rtplayer 指針;
返回值:
- 成功返回 0,失敗返回?1 或錯(cuò)誤碼
異步解析文件頭部信息,獲取元數(shù)據(jù)
函數(shù)原型
status_t prepareAsync(void* handle)
參數(shù):
- handle: 通過(guò) player_init() 函數(shù)創(chuàng)建的 rtplayer 指針;
返回值:
- 成功返回 0,失敗返回?1
該函數(shù)是非阻塞函數(shù),需要等到 RTPLAYER_NOTIFY_P? REPARED 消息回調(diào)之后才能調(diào) start() 函數(shù)進(jìn)行播放,而且 start() 函數(shù)不能在回調(diào)函數(shù)中調(diào)用
開(kāi)始播放
函數(shù)原型
status_t start(void* handle)
參數(shù):
- handle: 通過(guò) player_init() 函數(shù)創(chuàng)建的 rtplayer 指針;
返回值:
- 成功返回 0,失敗返回?1
暫停播放
函數(shù)原型
status_t pause_l(void* handle)
參數(shù):
- handle: 通過(guò) player_init() 函數(shù)創(chuàng)建的 rtplayer 指針;
返回值:
- 成功返回 0,失敗返回?1
停止播放
函數(shù)原型
status_t stop(void* handle)
參數(shù):
- handle: 通過(guò) player_init() 函數(shù)創(chuàng)建的 rtplayer 指針
返回值:
- 成功返回 0,失敗返回?1
重置播放器
函數(shù)原型
status_t reset(void* handle)
參數(shù):
- handle: 通過(guò) player_init() 函數(shù)創(chuàng)建的 rtplayer 指針;
返回值:
- 成功返回 0,失敗返回?1
在任何狀態(tài)下都可以調(diào)用該函數(shù),每次播放不同的音頻之前,都需要調(diào)用該函數(shù)重置播放器,另外,一般收到 RTPLAYER_NOTIFY_MEDIA_ERROR 這個(gè)消息的時(shí)候,也需要通過(guò)調(diào)用該函數(shù)來(lái)重置播放器。但是不能在回調(diào)函數(shù)中調(diào)用該函數(shù),否則會(huì)出現(xiàn)死鎖
跳播
函數(shù)原型
status_t seekTo(void* handle, int sec)
參數(shù):
- handle: 通過(guò) player_init() 函數(shù)創(chuàng)建的 rtplayer 指針
- sec: 跳播的位置,單位是:s
返回值:
- 成功返回 0,失敗返回?1
獲取當(dāng)前播放的位置
函數(shù)原型
status_t getCurrentPosition(void* handle, int * sec)
參數(shù):
- handle: 通過(guò) player_init() 函數(shù)創(chuàng)建的 rtplayer 指針;
- sec: 存放當(dāng)前播放的位置值,單位:s
返回值:
- 成功返回 0,失敗返回?1
獲取播放的文件總時(shí)長(zhǎng)
函數(shù)原型
status_t getDuration(void* handle, int * sec)
參數(shù):
- handle: 通過(guò) player_init() 函數(shù)創(chuàng)建的 rtplayer 指針;
- sec: 存儲(chǔ)文件總時(shí)長(zhǎng),單位:s
返回值:
- 成功返回 0,失敗返回?1
需要在 prepared 狀態(tài)之后才可以調(diào)用該函數(shù)
獲取播放的文件信息
函數(shù)原型
MediaInfo* getMediaInfo(void* handle)
參數(shù):
- handle: 通過(guò) player_init() 函數(shù)創(chuàng)建的 rtplayer 指針;
返回值:
- 成功返回 0,失敗返回?1
需要在 prepared 狀態(tài)之后才可以調(diào)用該函數(shù)
設(shè)置循環(huán)播放模式
函數(shù)原型
status_t setLooping(void* handle, int loop)
參數(shù):
- handle: 通過(guò) player_init() 函數(shù)創(chuàng)建的 rtplayer 指針;
- loop:1 表示單曲循環(huán),0 表示不會(huì)單曲循環(huán)
返回值:
- 成功返回 0,失敗返回?1
XPlayer 層播放接口
創(chuàng)建一個(gè) XPLAYER
函數(shù)原型
XPlayer* XPlayerCreate()
參數(shù):
- 無(wú)
返回值:
- 成功: XPlayer 指針; 失敗: NULL
設(shè)置 XPLAYER 的回調(diào)通知
函數(shù)原型
int XPlayerSetNotifyCallback(XPlayer* p, XPlayerNotifyCallback notifier, void* pUserData)
參數(shù):
- P:通過(guò) XPlayerCreate 創(chuàng)建的 Xplayer 指針
- notifier:回調(diào)通知
- pUserData:應(yīng)用程序傳下來(lái)的自定義數(shù)據(jù)
返回值:
- 成功:XPlayer 指針;失敗:NULL
Xplayer 將接收來(lái)自下層的回調(diào)通知,進(jìn)行相應(yīng)的操作
創(chuàng)建指向音頻播放設(shè)備管理模塊的指針,用于播放音頻
函數(shù)原型
SoundCtrl* RTSoundDeviceCreate(int card)
參數(shù):
- card:聲卡序號(hào)0:default;1:sw:audio1;2:sw:audio2;3:sw:audio3;4:sw:audio4;5:sw:audio5
返回值:
- 成功:音頻播放設(shè)備管理模塊的指針;失敗:NULL
創(chuàng)建指向音頻播放設(shè)備管理模塊的指針,用于播放音頻
函數(shù)原型
int XPlayerSetDataSourceUrl(XPlayer* p, const char* pUrl, void* httpService, const CdxKeyedVectorT* pHeaders)
參數(shù):
- pUrl:url 地址
- httpService:服務(wù)器信息
- pHeaders:頭文件信息
返回值:
- 返回值: 成功:0;失敗:?1 或線程響應(yīng)設(shè)置數(shù)據(jù)源命令的返回值或線程響應(yīng) xplayer prepare 命令的返回值
調(diào)用說(shuō)明: 發(fā)送 SetDataSource 命令,獲取需要播放的音頻數(shù)據(jù)內(nèi)容
將 XPLAYER 置為準(zhǔn)備狀態(tài), 解析頭部信息,獲取元數(shù)據(jù)
函數(shù)原型
int XPlayerPrepare(XPlayer* p)
參數(shù):
- p:通過(guò) XPlayerCreate 創(chuàng)建的 Xplayer 指針
返回值:
- 成功:線程響應(yīng)異步 Prepare 命令的返回值;失敗:NULL
該函數(shù)是阻塞函數(shù),調(diào)用完返回之后就進(jìn)入了 Prepared 狀態(tài),此時(shí)可調(diào) XPlayerStart() 函數(shù)進(jìn)行播放
將 XPLAYER 置為異步準(zhǔn)備狀態(tài)
函數(shù)原型
int XPlayerPrepareAsync(XPlayer* p)
參數(shù):
- p:通過(guò) XPlayerCreate 創(chuàng)建的 XPlayer 指針
返回值:
- 成功:線程響應(yīng)異步 Prepare 命令的返回值;失敗:NULL
網(wǎng)絡(luò)播放源一般采用 PrepareAsync,而不是 Prepare 命令,PrepareAsync 命令的返回值為 0 時(shí)說(shuō)明響應(yīng)成功,播放器準(zhǔn)備工作已經(jīng)完成可以開(kāi)始播放,為?1 時(shí)說(shuō)明響應(yīng)失敗
將 XPLAYER 置為啟動(dòng)狀態(tài)
函數(shù)原型
int XPlayerStart(XPlayer* p)
參數(shù):
- p:通過(guò) XPlayerCreate 創(chuàng)建的 XPlayer 指針
返回值:
- 成功:線程響應(yīng) start 命令的返回值;失敗:NULL
Start 命令的返回值為 0 時(shí)說(shuō)明響應(yīng)成功,為?1 時(shí)說(shuō)明響應(yīng)失敗
將 XPLAYER 置為暫停狀態(tài)
函數(shù)原型
int XPlayerPause(XPlayer* p)
參數(shù):
- p:通過(guò) XPlayerCreate 創(chuàng)建的 XPlayer 指針
返回值:
- 成功:線程響應(yīng) pause 命令的返回值;失敗:NULL
在 XPlayer 處于 start 狀態(tài)時(shí)可調(diào)用此接口,Pause 命令的返回值為 0 時(shí)說(shuō)明響應(yīng)成功,為?1 時(shí)說(shuō)明響應(yīng)失敗
將 XPLAYER 置為停止?fàn)顟B(tài)
函數(shù)原型
int XPlayerStop(XPlayer* p)
參數(shù):
- p:通過(guò) XPlayerCreate 創(chuàng)建的 XPlayer 指針
返回值:
- 成功:返回 0;失敗:返回?1
重置 XPLAYER
將相關(guān)變量復(fù)位,并銷毀各模塊,如音頻解碼模塊、音頻解碼數(shù)據(jù)接收模塊等
函數(shù)原型
int XPlayerReset(XPlayer* p)
參數(shù):
- p:通過(guò) XPlayerCreate 創(chuàng)建的 XPlayer 指針
返回值:
- 成功:線程響應(yīng) Reset 命令的返回值;失敗:NULL
Reset 命令的返回值為 0 時(shí)說(shuō)明響應(yīng)成功,為?1 時(shí)說(shuō)明響應(yīng)失敗
獲取節(jié)目時(shí)長(zhǎng)
函數(shù)原型
int XPlayerGetDuration(XPlayer* p, int *msec)
參數(shù):
- p:通過(guò) XPlayerCreate 創(chuàng)建的 Xplayer 指針
- msec:保存節(jié)目時(shí)長(zhǎng)
返回值:
- 成功:0;失敗:?1
在 XPlayer 處于 PREPARED、STARTED、PAUSED、STOPPED 或 COMPLETE 狀態(tài)下才可調(diào)用此接口,否則操作無(wú)效
SEEK 到給定的時(shí)間點(diǎn)
函數(shù)原型
int XPlayerSeekTo(XPlayer* p, int nSeekTimeMs)
參數(shù):
- p:通過(guò) XPlayerCreate 創(chuàng)建的 Xplayer 指針
- nSeekTimeMs:跳轉(zhuǎn)的時(shí)間點(diǎn)
返回值:
- 成功:線程響應(yīng) Seek 命令的返回值;失敗:NULL
如果跳轉(zhuǎn)前播放處于暫停狀態(tài),則跳轉(zhuǎn)后將保持在暫停狀態(tài)
獲取媒體文件的總時(shí)長(zhǎng)
函數(shù)原型
int XPlayerGetDuration(XPlayer* p, int *msec)
參數(shù):
- p:通過(guò) XPlayerCreate 創(chuàng)建的 XPlayer 指針
- msec:保存媒體文件的總時(shí)長(zhǎng)
返回值:
- 成功:0;失敗:?1
需要在 prepared 狀態(tài)之后才可以調(diào)用該函數(shù)
獲取當(dāng)前的播放時(shí)間點(diǎn)(即播放位置)
在 XPlayer 處于 PREPARED、STARTED、PAUSED、STOPPED 或 COMPLETE 狀態(tài)下才可調(diào)用此接口,否則操作無(wú)效,在 complete 狀態(tài)下,可能會(huì)調(diào)用 prepare 方法并更改媒體信息,獲取的播放時(shí)間以 ms 為單位
函數(shù)原型
int XPlayerGetCurrentPosition(XPlayer* p, int* msec)
參數(shù):
- p:通過(guò) XPlayerCreate 創(chuàng)建的 XPlayer 指針
- msec:保存當(dāng)前的播放時(shí)間
返回值:
- 成功:0;失敗:?1
獲取媒體信息
函數(shù)原型
MediaInfo* XPlayerGetMediaInfo(XPlayer* p)
參數(shù):
- p:通過(guò) XPlayerCreate 創(chuàng)建的 XPlayer 指針
返回值:
- 成功返回 0,失敗返回?1。如果失敗,則 mediaInfo 指針為 NULL
需要在 prepared 狀態(tài)之后才可以調(diào)用該函數(shù)
設(shè)置循環(huán)播放模式
函數(shù)原型
int XPlayerSetLooping(XPlayer* p, int loop)
參數(shù):
- p:通過(guò) XPlayerCreate 創(chuàng)建的 XPlayer 指針
- loop: 1: 表示單曲循環(huán)模式;0:表示不會(huì)循環(huán)
返回值:
- 無(wú)
查詢是否正在播放
函數(shù)原型
int XPlayerIsPlaying(XPlayer* p)
參數(shù):
- p:通過(guò) XPlayerCreate 創(chuàng)建的 XPlayer 指針
返回值:
- 1:正在播放;0:未播放
銷毀一個(gè) XPLAYER
函數(shù)原型
void XPlayerDestroy(XPlayer* p)
參數(shù):
- p:通過(guò) XPlayerCreate 創(chuàng)建的 XPlayer 指針
返回值:
- 無(wú)
播放器開(kāi)發(fā)示例
player_init() 創(chuàng)建一個(gè)播放器
registerCallback() 設(shè)置消息回調(diào)函數(shù)
setDataSource_url() 設(shè)置 url
prepare() 或 prepareAsync() 解析頭部信息,獲取元數(shù)據(jù),并根據(jù)元數(shù)據(jù)的信息初始化對(duì)應(yīng)的解碼器
start() 播放 (注: 如果是用 prepareAsync() 函數(shù),則需要等到 RTPLAYER_NOTIFY_PREPARED 消息回調(diào)之后才可以調(diào)用 start() 函數(shù)進(jìn)行播放)
如果需要跳播,則可以調(diào)用 seekTo() 函數(shù)
如果需要暫停,則調(diào)用 pause_l() 函數(shù)進(jìn)行暫停
如果需要停止,則可以調(diào)用 stop() 或 reset() 函數(shù)進(jìn)行停止 (注:建議用 reset() 函數(shù)進(jìn)行停止,因?yàn)槿魏螤顟B(tài)下都可以調(diào)用 reset() 函數(shù))
如果需要播放下一個(gè)或其他的,則可以先調(diào)用 reset() 函數(shù)使播放器進(jìn)入 idle 狀態(tài),然后再重復(fù) (3)(4)(5) 的步驟
include
include
include
include
include
include
include
include
include
include
include
include
include
include "FreeRTOS_POSIX/utils.h"
include "rtplayer.h"
include "xplayer.h"
define PAUSE_CMD 'P'
define PING_CMD 'p'
define STOP_CMD 'S'
define SEEK_TO_CMD 's'
define SEEK_TO_CMD2 'j'
define BACKGROUND_CMD 'b'
define SHOW_BUFFER_CMD 'B'
define QUIT_CMD 'q'
define LOOP_CMD 'l'
define GET_DURATION_CMD 'G'
define GET_POSITION_CMD 'g'
define HELP_CMD 'h'
define INFO_CMD 'i'
define REPLAY_CMD 'r'
define RETRY_CMD 256
define USE_PREPARE_ASYNC 0
define LOGD(msg, arg...) //printf("[PLAYER_DBG] <%s : %d> " msg "n", func , LINE , ##arg)
define LOGI(msg, arg...) //printf("[PLAYER_INFO] <%s : %d> " msg "n", func , LINE , ##arg)
define LOGW(msg, arg...) printf("[PLAYER_WRN] <%s : %d> " msg "n", func , LINE , ##arg)
define LOGE(msg, arg...) printf("[PLAYER_ERR] <%s : %d> " msg "n", func , LINE , ##arg)
typedef struct DemoPlayerContext
{RTPlayer* mRTplayer; sem_t mPreparedSem; mqd_t mRTplayerMq; pthread_t mThreadId; char *pUrl; int mSeekable; char isPlayingFlag; char mError; int inputMode; int isSetLoop; char quitFlag;//no_shell_input mode quitFlag int testMode; MediaInfo* mMediaInfo; int SoundCard;
}DemoPlayerContext;
typedef struct DemoPlayerMsg
{int msg; int data;
}DemoPlayerMsg;
define INVALID_MQD ( ( mqd_t ) -1 )
define DEFAULT_MODE 0600
static const char *pcRTplayerMqName = "/rtplayerMq";
static volatile mqd_t mRTplayerMq = INVALID_MQD;
static int mRTplayerUserInput = 0;
static struct mq_attr xRTplayerMqAttr ={ .mq_flags = 0, .mq_maxmsg = 3, .mq_msgsize = sizeof(DemoPlayerMsg), .mq_curmsgs = 0 };
static void showHelp(){
printf("n"); printf("**************************n"); printf("* This is a simple audio player, when it is started, you can input commands to telln"); printf("* what you want it to do.n"); printf("* Usage: n"); printf("* tplayer_demo /data/test.mp3 : this means play test.mp3n"); printf("* P :this will Pause if in playing status,or Play in paused status n"); printf("* S :this means Stop n"); printf("* s :this means seek to 10s n"); printf("* B :show buffer n"); printf("* b :this means player will run in the background n"); printf("* q :this means quit the player n"); printf("* l :this means loop play n"); printf("* G :this means Get duration n"); printf("* g :this means get position n"); printf("* i :this means show media info n"); printf("* h :this means show the help information n"); printf("* r : replay the current audion"); printf("**************************n");
}
static int rtplayer_clear_cmd(mqd_t mq){struct timespec cur, delay, abstime; clock_gettime( CLOCK_REALTIME, &cur ); delay.tv_sec = 0; delay.tv_nsec = 5*1000*1000; UTILS_TimespecAdd(&cur, &delay, &abstime); DemoPlayerMsg msg; while(mq_timedreceive(mq, (char *)&msg, sizeof(msg), NULL, &abstime)!=-1); return 0;
}
static int rtplayer_send_cmd(mqd_t mq, int msg, int data){DemoPlayerMsg pmsg = {msg, data}; struct timespec tsn, ts; clock_gettime(CLOCK_REALTIME, &tsn); UTILS_TimespecAddNanoseconds(&tsn, 20*1000*1000, &ts); int status = mq_timedsend(mq, (char *)&pmsg, sizeof(pmsg), 0, &ts); if(status) LOGE("send cmd %c,%d failed!", pmsg.msg, pmsg.data); return status;
}
static int rtplayer_send_cmd_force(mqd_t mq, int msg, int data){
int try_times = 0; DemoPlayerMsg pmsg = {msg, data}; struct timespec tsn, ts; int status;
try_send:
clock_gettime(CLOCK_REALTIME, &tsn); UTILS_TimespecAddNanoseconds(&tsn, 20*1000*1000, &ts); status = mq_timedsend(mq, (char *)&pmsg, sizeof(pmsg), 0, &ts); if(status){ try_times++; if(try_times< 5){ LOGE("send cmd %c,%d failed, retry...", pmsg.msg, pmsg.data); goto try_send; } else if(try_times< 10){ DemoPlayerMsg tmp; LOGE("send cmd %c,%d failed, retry...", pmsg.msg, pmsg.data); clock_gettime(CLOCK_REALTIME, &tsn); UTILS_TimespecAddNanoseconds(&tsn, 20*1000*1000, &ts); status = mq_timedreceive(mq, (char *)&tmp, sizeof(tmp), NULL, &ts); if(status< 0){ LOGE("mq_receive fail %d", status); goto fail_exit; } LOGW("drop: %c, %d", tmp.msg, tmp.data); goto try_send; } goto fail_exit; } return status;
fail_exit:
LOGE("send cmd %c,%d failed!n", pmsg.msg, pmsg.data); return status;
}
static void callbackFromRTplayer(void* userData,int msg, int id, int ext1, int ext2);
static void* RTplayerThread(void* arg){DemoPlayerContext* demoPlayer = (DemoPlayerContext*)arg; char quitFlag = 0; if(demoPlayer- >inputMode) { while(1) { if(demoPlayer- >quitFlag) { if(demoPlayer- >mRTplayer != NULL) { printf("player finsh, quit the rtplayern"); mRTplayerMq = INVALID_MQD;
if USE_PREPARE_ASYNC
sem_destroy(&demoPlayer- >mPreparedSem);
endif
player_deinit(demoPlayer- >mRTplayer); free(demoPlayer- >pUrl); free(demoPlayer); } break; } usleep(50*1000); } return NULL; } while(!quitFlag){ int cRxed = 0; int data = 0; DemoPlayerMsg msg; ssize_t status; if(demoPlayer- >mRTplayerMq!=INVALID_MQD){ usleep(50*1000); ssize_t status = mq_receive(demoPlayer- >mRTplayerMq, (char *)&msg, sizeof(msg), NULL); if(status<=-1){ LOGE("mq_receive fail %d", status); usleep(1*1000*1000); continue; } printf("receive %c,%dn", msg.msg, msg.data); cRxed = msg.msg; data = msg.data; } else{ cRxed = QUIT_CMD; } switch(cRxed){ case PAUSE_CMD: { if(demoPlayer- >isPlayingFlag){ printf("pause the rtplayern"); pause_l(demoPlayer- >mRTplayer); demoPlayer- >isPlayingFlag = 0; }else{ printf("play the rtplayern"); start(demoPlayer- >mRTplayer); demoPlayer- >isPlayingFlag = 1; } break; } case STOP_CMD: { printf("stop the rtplayern"); stop(demoPlayer- >mRTplayer); demoPlayer- >isPlayingFlag = 0; break; } case SEEK_TO_CMD: { printf("rtplayer seek to 10 secondn"); seekTo(demoPlayer- >mRTplayer,10); break; } case SEEK_TO_CMD2: { printf("rtplayer seek to %d secondn", data); seekTo(demoPlayer- >mRTplayer,data); break; } case QUIT_CMD: { printf("quit the rtplayern"); mRTplayerMq = INVALID_MQD; //mq_close(demoPlayer- >mRTplayerMq);
if USE_PREPARE_ASYNC
sem_destroy(&demoPlayer- >mPreparedSem);
endif
player_deinit(demoPlayer- >mRTplayer); free(demoPlayer- >pUrl); free(demoPlayer); quitFlag = 1; break; } case LOOP_CMD: { printf("let the rtplayer loop playn"); demoPlayer- >isSetLoop = 1; setLooping(demoPlayer- >mRTplayer,1); break; } case GET_DURATION_CMD: { printf("get the audio durationn"); int duration; getDuration(demoPlayer- >mRTplayer,&duration); printf("duration:%d sn",duration); break; } case GET_POSITION_CMD: { printf("get the current positionn"); int position; getCurrentPosition(demoPlayer- >mRTplayer,&position); printf("current position:%d sn",position); break; } case HELP_CMD: { printf("show the help informationn"); showHelp(); break; } case INFO_CMD: { printf("**************************n"); printf("* show media information:n"); MediaInfo* mi = NULL; demoPlayer- >mMediaInfo = getMediaInfo(demoPlayer- >mRTplayer); if(demoPlayer- >mMediaInfo != NULL){ mi = demoPlayer- >mMediaInfo; printf("* file size = %lld KBn",mi- >nFileSize/1024); printf("* duration = %lld msn",mi- >nDurationMs); printf("* bitrate = %d Kbpsn",mi- >nBitrate/1024); printf("* container type = %dn",mi- >eContainerType); printf("* audio stream num = %dn",mi- >nAudioStreamNum); if(mi- >pAudioStreamInfo != NULL){ printf("* audio codec tpye = %dn",mi- >pAudioStreamInfo- >eCodecFormat); printf("* audio channel num = %dn",mi- >pAudioStreamInfo- >nChannelNum); printf("* audio BitsPerSample = %dn",mi- >pAudioStreamInfo- >nBitsPerSample); printf("* audio sample rate = %dn",mi- >pAudioStreamInfo- >nSampleRate); } printf("**************************n"); } break; } case SHOW_BUFFER_CMD: { printf("**************************n"); printf("* show buffer information:n"); player_show_buffer(); printf("**************************n"); break; } case REPLAY_CMD: { printf("replay %sn", demoPlayer- >pUrl); int ret; if(demoPlayer- >testMode){ printf("test mode: destroy & create instead of resetn"); player_deinit(demoPlayer- >mRTplayer); usleep(50*1000); demoPlayer- >mRTplayer = (RTPlayer*)(uintptr_t)player_init(); printf("demoPlayer.mRTplayer = %pn",demoPlayer- >mRTplayer); if(!demoPlayer- >mRTplayer){ printf("init rtplayer failn"); free(demoPlayer- >pUrl); free(demoPlayer); quitFlag = 1; continue; } registerCallback(demoPlayer- >mRTplayer, demoPlayer, callbackFromRTplayer); } else reset(demoPlayer- >mRTplayer); ret = setDataSource_url(demoPlayer- >mRTplayer, demoPlayer, demoPlayer- >pUrl, 0); if(ret){ printf("setDataSource_url failedn"); break; } ret = prepare(demoPlayer- >mRTplayer); if(ret){ printf("prepare failedn"); break; } start(demoPlayer- >mRTplayer); demoPlayer- >isPlayingFlag = 1; if(demoPlayer- >isSetLoop) { setLooping(demoPlayer- >mRTplayer,1); } break; } case RETRY_CMD: { int position = data; if(data==-1) getCurrentPosition(demoPlayer- >mRTplayer,&position); printf("retry %sn", demoPlayer- >pUrl); int ret; if(demoPlayer- >testMode){ printf("test mode: destroy & create instead of resetn"); player_deinit(demoPlayer- >mRTplayer); usleep(50*1000); demoPlayer- >mRTplayer = (RTPlayer*)(uintptr_t)player_init(); printf("demoPlayer.mRTplayer = %pn",demoPlayer- >mRTplayer); if(!demoPlayer- >mRTplayer){ LOGE("init rtplayer fail"); free(demoPlayer- >pUrl); free(demoPlayer); quitFlag = 1; continue; } registerCallback(demoPlayer- >mRTplayer, demoPlayer, callbackFromRTplayer); } else reset(demoPlayer- >mRTplayer); ret = setDataSource_url(demoPlayer- >mRTplayer, demoPlayer, demoPlayer- >pUrl, 0); if(ret){ LOGE("setDataSource_url failed"); rtplayer_send_cmd_force(demoPlayer- >mRTplayerMq, RETRY_CMD, position); usleep(500*1000); break; } ret = prepare(demoPlayer- >mRTplayer); if(ret){ LOGE("prepare failed"); rtplayer_send_cmd_force(demoPlayer- >mRTplayerMq, RETRY_CMD, position); usleep(500*1000); break; } start(demoPlayer- >mRTplayer); demoPlayer- >isPlayingFlag = 1; //seekTo(demoPlayer- >mRTplayer, position); if(demoPlayer- >isSetLoop) setLooping(demoPlayer- >mRTplayer,1); break; } default: { LOGW("warning: unknown command,cmd = %d",cRxed); break; } } if(quitFlag){ return NULL; } } return NULL;
}
static void callbackFromRTplayer(void* userData,int msg, int id, int ext1, int ext2){LOGI("call back from RTplayer,msg = %d,id = %d,ext1 = %d,ext2 = %dn",msg,id,ext1,ext2); DemoPlayerContext* pDemoPlayer = (DemoPlayerContext*)userData; switch(msg) { case RTPLAYER_NOTIFY_PREPARED: { printf("RTPLAYER_NOTIFY_PREPARED:has prepared.n");
#if USE_PREPARE_ASYNC
sem_post(&pDemoPlayer- >mPreparedSem); pDemoPlayer- >mPreparedFlag = 1;
#endif
break; } case RTPLAYER_NOTIFY_PLAYBACK_COMPLETE: { printf("RTPLAYER_NOTIFY_PLAYBACK_COMPLETE:play completen"); pDemoPlayer- >isPlayingFlag = 0; if(pDemoPlayer- >inputMode) { pDemoPlayer- >quitFlag = 1; } break; } case RTPLAYER_NOTIFY_SEEK_COMPLETE: { printf("RTPLAYER_NOTIFY_SEEK_COMPLETE:seek okn"); break; } case RTPLAYER_NOTIFY_MEDIA_ERROR: { switch (ext1) { case RTPLAYER_MEDIA_ERROR_UNKNOWN: { printf("erro type:TPLAYER_MEDIA_ERROR_UNKNOWNn"); break; } case RTPLAYER_MEDIA_ERROR_UNSUPPORTED: { printf("erro type:TPLAYER_MEDIA_ERROR_UNSUPPORTEDn"); break; } case RTPLAYER_MEDIA_ERROR_IO: { printf("erro type:TPLAYER_MEDIA_ERROR_IOn"); break; } } printf("RTPLAYER_NOTIFY_MEDIA_ERRORn"); pDemoPlayer- >mError = 1;
if USE_PREPARE_ASYNC
if(pDemoPlayer- >mPreparedFlag == 0){ printf("recive err when preparingn"); sem_post(&pDemoPlayer- >mPreparedSem); }
endif
if( pDemoPlayer- >mRTplayerMq!=INVALID_MQD ){ rtplayer_send_cmd_force(pDemoPlayer- >mRTplayerMq, RETRY_CMD, -1); } else{ printf("io error, mqueue not existn"); } break; } case RTPLAYER_NOTIFY_NOT_SEEKABLE: { pDemoPlayer- >mSeekable = 0; printf("info: media source is unseekable.n"); break; } case RTPLAYER_NOTIFY_DETAIL_INFO: { int flag = *(int *)(uintptr_t)ext2; //printf("detail info: %dn", flag); break; } default: { printf("warning: unknown callback from RTplayer.n"); break; } }
}
int cmd_rtplayer_test(int argc, char ** argv)
{int inputMode = 0; int testMode = 0; /* printf("argc = %dn",argc); for(int i=0; i < argc;i++){ printf("argv[%d]=%sn",i,argv[i]); } */ printf("rtplayer source:%sn", argv[1]); if(argc == 3){ if( !strncmp("no_shell_input", argv[2], sizeof("no_shell_input")-1) ){ argc--; inputMode = 1; } else if( !strncmp("test_mode", argv[2], sizeof("test_mode")-1) ){ argc--; testMode = 1; } } if(argc != 2){ LOGW("the parameter is error,usage is as following:"); showHelp(); goto rtp_failed; }
if USE_PREPARE_ASYNC
int waitErr = 0;
endif
DemoPlayerContext* demoPlayer = (DemoPlayerContext*)malloc(sizeof(DemoPlayerContext)); if(demoPlayer == NULL){ LOGE("malloc DemoPlayerContext fail"); goto rtp_failed; } memset(demoPlayer, 0, sizeof(DemoPlayerContext)); demoPlayer- >mSeekable = 1; demoPlayer- >mRTplayerMq = INVALID_MQD; demoPlayer- >inputMode = inputMode; demoPlayer- >testMode = testMode; demoPlayer- >quitFlag = 0; demoPlayer- >mMediaInfo = NULL; if(strlen(argv[1])<=0){ LOGE("url error"); goto rtp_url_failed; } demoPlayer- >pUrl = malloc(strlen(argv[1])+1); if(!demoPlayer- >pUrl){ LOGE("pUrl malloc fail"); goto rtp_url_failed; } memset(demoPlayer- >pUrl, 0, strlen(argv[1])); strcpy(demoPlayer- >pUrl, argv[1]);
if USE_PREPARE_ASYNC
sem_init(&demoPlayer- >mPreparedSem, 0, 0);
endif
demoPlayer- >mRTplayer = (RTPlayer*)(uintptr_t)player_init(); LOGI("demoPlayer.mRTplayer = %p",demoPlayer- >mRTplayer); if(!demoPlayer- >mRTplayer){ LOGE("init rtplayer fail"); goto rtp_init_failed; } registerCallback(demoPlayer- >mRTplayer, demoPlayer, callbackFromRTplayer); status_t ret = setDataSource_url(demoPlayer- >mRTplayer,demoPlayer,demoPlayer- >pUrl, 0); if(ret){ LOGE("set DataSource url fail"); goto rtp_prepare_failed; }
if USE_PREPARE_ASYNC
demoPlayer- >mPreparedFlag = 0; if(prepareAsync(demoPlayer- >mRTplayer) != 0) { printf("TPlayerPrepareAsync() return fail.n"); }else{ printf("preparing...n"); } struct timespec t; t.tv_nsec = 0; t.tv_sec = 30; waitErr = sem_timedwait(&demoPlayer- >mPreparedSem, &t); if(waitErr == -1){ printf("prepare timeout,has wait %d sn",t.tv_sec); sem_destroy(&demoPlayer- >mPreparedSem); goto rtp_prepare_failed; }else if(demoPlayer.mError == 1){ printf("prepare failn"); sem_destroy(&demoPlayer- >mPreparedSem); goto rtp_prepare_failed; } printf("prepared okn");
else
ret = prepare(demoPlayer- >mRTplayer); if(ret){ LOGE("prepare fail"); goto rtp_prepare_failed; }
endif
start(demoPlayer- >mRTplayer); demoPlayer- >isPlayingFlag = 1; if( mRTplayerMq==INVALID_MQD ){ mRTplayerMq = mq_open( pcRTplayerMqName, O_CREAT | O_RDWR, DEFAULT_MODE, &xRTplayerMqAttr ); if(mRTplayerMq==INVALID_MQD){ LOGE("mq_open fail"); } } demoPlayer- >mRTplayerMq = mRTplayerMq; rtplayer_clear_cmd(demoPlayer- >mRTplayerMq); pthread_attr_t attr; pthread_attr_init(&attr); struct sched_param sched; sched.sched_priority = 4; pthread_attr_setschedparam(&attr, &sched); pthread_attr_setstacksize(&attr, 32768); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); if( pthread_create(&demoPlayer- >mThreadId, &attr, RTplayerThread, demoPlayer) ){ LOGE("pthread_create failed, quit the rtplayer"); mRTplayerMq = INVALID_MQD; //mq_close(demoPlayer- >mRTplayerMq);
if USE_PREPARE_ASYNC
sem_destroy(&demoPlayer- >mPreparedSem);
endif
goto rtp_prepare_failed; } pthread_setname_np(demoPlayer- >mThreadId, "RTplayerThread"); if(demoPlayer- >inputMode) goto rtp_succeed; while(1){ char cRxed = getchar(); if(cRxed==BACKGROUND_CMD){ printf("shell input exit, rtplayer will run in the backgroundn"); break; } rtplayer_send_cmd(demoPlayer- >mRTplayerMq, cRxed, 0); if(cRxed==QUIT_CMD) break; usleep(50*1000); }
rtp_succeed:
return 0;
rtp_prepare_failed:
player_deinit(demoPlayer- >mRTplayer);
rtp_init_failed:
free(demoPlayer- >pUrl);
rtp_url_failed:
free(demoPlayer);
rtp_failed:
return -1;
}
FINSH_FUNCTION_EXPORT_CMD(cmd_rtplayer_test, rtplayer_test, test the rtplayer);static int cmd_rtplayer_controller(int argc, char ** argv){
if(mRTplayerMq==INVALID_MQD){ printf("mRTplayerMq = INVALID_MQD!n"); return -1; } if( (argc!=2) && (argc!=3) ){ printf("usage:rtpc < cmd > [data]n"); return -1; } int data = 0; if(argc==3) data = atoi(argv[2]); rtplayer_send_cmd(mRTplayerMq, argv[1][0], data); return 0;
}
FINSH_FUNCTION_EXPORT_CMD(cmd_rtplayer_controller, rtpc, control the rtplayer);
注意事項(xiàng)
- 目前 rtplayer/xplayer 僅支持音頻解碼,且不支持對(duì)視頻文件進(jìn)行解封裝,因此 rtplayer 播放器應(yīng)用只支持音頻文件的播放。
void registerCallback(void* handle, void* userData, player_callback_t fn)
函數(shù)必須要調(diào)用,而且 fn 不能為 NULL。- 回調(diào)函數(shù)中不能調(diào)用 rtplayer 的任何一個(gè)接口,如:reset、stop、start 等這些接口不能在回調(diào)函數(shù)中調(diào)用。
- 播放本地文件的情況下,set url 時(shí),XPlayer 會(huì)進(jìn)行一次同步 prepare,用于提前獲取信息給 parser,因此異步 prepare 前,應(yīng)對(duì) XPlayerSetDataSourceUrl 的返回值進(jìn)行判斷。
- 改變播放器的狀態(tài),應(yīng)滿足狀態(tài)圖中的對(duì)應(yīng)的函數(shù)調(diào)用流程,如播放結(jié)束后需要播放下一首歌,應(yīng)調(diào)用 reset 清空信息,進(jìn)入 idle 狀態(tài),再調(diào)用 setDataSource_Url 進(jìn)行填充下一首歌到播放器中
- 采取異步 prepare 時(shí)(prepareAsync),應(yīng)注意添加信號(hào)量進(jìn)行同步。
-
播放器
+關(guān)注
關(guān)注
5文章
394瀏覽量
37382 -
URL
+關(guān)注
關(guān)注
0文章
139瀏覽量
15311 -
FreeRTOS
+關(guān)注
關(guān)注
12文章
483瀏覽量
62000 -
狀態(tài)機(jī)
+關(guān)注
關(guān)注
2文章
492瀏覽量
27477 -
音頻解碼器
+關(guān)注
關(guān)注
0文章
31瀏覽量
22231 -
R128
+關(guān)注
關(guān)注
0文章
41瀏覽量
96
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論