跨平台低延迟的RTMP/RTSP直播播放器设计实现
開發(fā)背景
2015年,當(dāng)我們?cè)噲D在市面上找一款專供直播播放使用的低延遲播放器,來配合測(cè)試我們的RTMP推送模塊使用時(shí),居然發(fā)現(xiàn)沒有一款好用的,市面上的,如VLC或Vitamio,說白了都是基于FFMPEG,在點(diǎn)播這塊支持格式很多,也非常優(yōu)異,但是直播這塊,特別是RTMP,延遲要幾秒鐘,對(duì)如純音頻、純視頻播放,快速啟播、網(wǎng)絡(luò)異常狀態(tài)處理、集成復(fù)雜度等各方面,支持非常差,而且因?yàn)楣δ軓?qiáng)大,bug很多,除了行業(yè)內(nèi)資深的開發(fā)者能駕馭,好多開發(fā)者甚至連編譯整體環(huán)境,都要耗費(fèi)很大的精力。
我們的直播播放器,始于Windows平臺(tái),Android和iOS同步開發(fā),基于上述開源播放器的各種缺點(diǎn),我們考慮全自研框架,確保整體設(shè)計(jì)跨平臺(tái),再保障播放流程度的前提下,盡可能的做到毫秒級(jí)延遲,接口設(shè)計(jì)三個(gè)平臺(tái)統(tǒng)一化,確保多平臺(tái)集成復(fù)雜度降到最低。
整體方案架構(gòu)
RTMP或RTSP直播播放器,目標(biāo)很明確,從RTMP服務(wù)器(自建服務(wù)器或CDN)或RTSP服務(wù)器(或NVR/IPC/編碼器等)拉取流數(shù)據(jù),完成數(shù)據(jù)解析、解碼、音視頻數(shù)據(jù)同步、繪制。
具體對(duì)應(yīng)下圖“接收端”部分:
初期模塊設(shè)計(jì)目標(biāo)
- 自有框架,易于擴(kuò)展,自適應(yīng)算法讓延遲更低、解碼繪制效率更高;
- 支持各種異常網(wǎng)絡(luò)狀態(tài)處理,如斷網(wǎng)重連、網(wǎng)絡(luò)抖動(dòng)等控制;
- 有Event狀態(tài)回調(diào),確保開發(fā)者可以了解到播放端整體的狀態(tài),從純黑盒不可控,到更智能的了解到整體播放狀態(tài);
- 支持多實(shí)例播放;
- 視頻支持H.264,音頻支持AAC/PCMA/PCMU;
- 支持緩沖時(shí)間設(shè)置(buffer time);
- 實(shí)時(shí)靜音。
經(jīng)過迭代后的功能
- [支持播放協(xié)議]RTSP、RTMP,毫秒級(jí)延遲;
- ?[多實(shí)例播放]支持多實(shí)例播放;
- ?[事件回調(diào)]支持網(wǎng)絡(luò)狀態(tài)、buffer狀態(tài)等回調(diào);
- ?[視頻格式]支持RTMP擴(kuò)展H.265,H.264;
- ?[音頻格式]支持AAC/PCMA/PCMU/Speex;
- ?[H.264/H.265軟解碼]支持H.264/H.265軟解;
- ?[H.264硬解碼]Windows/Android/iOS支持H.264硬解;
- ?[H.265硬解]Windows/Android/iOS支持H.265硬解;
- ?[H.264/H.265硬解碼]Android支持設(shè)置Surface模式硬解和普通模式硬解碼;
- ?[緩沖時(shí)間設(shè)置]支持buffer time設(shè)置;
- ?[首屏秒開]支持首屏秒開模式;
- ?[低延遲模式]支持類似于線上娃娃機(jī)等直播方案的超低延遲模式設(shè)置(公網(wǎng)200~400ms);
- ?[復(fù)雜網(wǎng)絡(luò)處理]支持?jǐn)嗑W(wǎng)重連等各種網(wǎng)絡(luò)環(huán)境自動(dòng)適配;
- ?[快速切換URL]支持播放過程中,快速切換其他URL,內(nèi)容切換更快;
- ?[音視頻多種render機(jī)制]Android平臺(tái),視頻:surfaceview/OpenGL ES,音頻:AudioTrack/OpenSL ES;
- ?[實(shí)時(shí)靜音]支持播放過程中,實(shí)時(shí)靜音/取消靜音;
- ?[實(shí)時(shí)快照]支持播放過程中截取當(dāng)前播放畫面;
- ?[只播關(guān)鍵幀]Windows平臺(tái)支持實(shí)時(shí)設(shè)置是否只播放關(guān)鍵幀;
- ?[渲染角度]支持0°,90°,180°和270°四個(gè)視頻畫面渲染角度設(shè)置;
- ?[渲染鏡像]支持水平反轉(zhuǎn)、垂直反轉(zhuǎn)模式設(shè)置;
- ?[實(shí)時(shí)下載速度更新]支持當(dāng)前下載速度實(shí)時(shí)回調(diào)(支持設(shè)置回調(diào)時(shí)間間隔);
- ?[ARGB疊加]Windows平臺(tái)支持ARGB圖像疊加到顯示視頻(參看C++的DEMO);
- ?[解碼前視頻數(shù)據(jù)回調(diào)]支持H.264/H.265數(shù)據(jù)回調(diào);
- ?[解碼后視頻數(shù)據(jù)回調(diào)]支持解碼后YUV/RGB數(shù)據(jù)回調(diào);
- ?[解碼后視頻數(shù)據(jù)縮放回調(diào)]Windows平臺(tái)支持指定回調(diào)圖像大小的接口(可以對(duì)原視圖像縮放后再回調(diào)到上層);
- ?[解碼前音頻數(shù)據(jù)回調(diào)]支持AAC/PCMA/PCMU/SPEEX數(shù)據(jù)回調(diào);
- ?[音視頻自適應(yīng)]支持播放過程中,音視頻信息改變后自適應(yīng);
- ?[擴(kuò)展錄像功能]支持RTSP/RTMP H.264、擴(kuò)展H.265流錄制,支持PCMA/PCMU/Speex轉(zhuǎn)AAC后錄制,支持設(shè)置只錄制音頻或視頻等;
RTMP、RTSP直播播放開發(fā)設(shè)計(jì)考慮的點(diǎn)
1. 低延遲:大多數(shù)RTSP的播放都面向直播場(chǎng)景,所以,如果延遲過大,嚴(yán)重影響體驗(yàn),所以,低延遲是衡量一個(gè)好的RTSP播放器非常重要的指標(biāo),目前大牛直播SDK的RTSP直播播放延遲比開源播放器更優(yōu)異,而且長(zhǎng)時(shí)間運(yùn)行下,不會(huì)造成延遲累積;
2. 音視頻同步處理:有些播放器為了追求低延遲,甚至不做音視頻同步,拿到audio video直接播放,導(dǎo)致a/v不同步,還有就是時(shí)間戳亂跳等各種問題,大牛直播SDK提供的播放器,具備好的時(shí)間戳同步和異常時(shí)間戳矯正機(jī)制;
3. 支持多實(shí)例:大牛直播SDK提供的播放器支持同時(shí)播放多路音視頻數(shù)據(jù),比如4-8-9窗口,大多開源播放器對(duì)多實(shí)例支持不太友好;
4. 支持buffer time設(shè)置:在一些有網(wǎng)絡(luò)抖動(dòng)的場(chǎng)景,播放器需要支持buffer time設(shè)置,一般來說,以毫秒計(jì),開源播放器對(duì)此支持不夠友好;
5. TCP/UDP模式設(shè)定、自動(dòng)切換:考慮到好多服務(wù)器僅支持TCP或UDP模式,一個(gè)好的RTSP播放器需要支持TCP/UDP模式設(shè)置,如鏈接不支持TCP或UDP,大牛直播SDK可自動(dòng)切換,,開源播放器不具備自動(dòng)切換TCP/UDP能力;
6. 實(shí)時(shí)靜音:比如,多窗口播放RTSP流,如果每個(gè)audio都播放出來,體驗(yàn)非常不好,所以實(shí)時(shí)靜音功能非常必要,開源播放器不具備實(shí)時(shí)靜音功能;
7. 視頻view旋轉(zhuǎn):好多攝像頭由于安裝限制,導(dǎo)致圖像倒置,所以一個(gè)好的RTSP播放器應(yīng)該支持如視頻view實(shí)時(shí)旋轉(zhuǎn)(0° 90° 180° 270°)、水平反轉(zhuǎn)、垂直反轉(zhuǎn),開源播放器不具備此功能;
8. 支持解碼后audio/video數(shù)據(jù)輸出:大牛直播SDK接觸到好多開發(fā)者,希望能在播放的同時(shí),獲取到Y(jié)UV或RGB數(shù)據(jù),進(jìn)行人臉匹配等算法分析,開源播放器不具備此功能;
9. 實(shí)時(shí)快照:感興趣或重要的畫面,實(shí)時(shí)截取下來非常必要,一般播放器不具備快照能力,開源播放器不具備此功能;
10. 網(wǎng)絡(luò)抖動(dòng)處理(如斷網(wǎng)重連):穩(wěn)定的網(wǎng)絡(luò)處理機(jī)制、支持如斷網(wǎng)重連等,開源播放器對(duì)網(wǎng)絡(luò)異常處理支持較差;
11. 長(zhǎng)期運(yùn)行穩(wěn)定性:不同于市面上的開源播放器,大牛直播SDK提供的Windows平臺(tái)RTSP直播播放SDK適用于數(shù)天長(zhǎng)時(shí)間運(yùn)行,開源播放器對(duì)長(zhǎng)時(shí)間運(yùn)行穩(wěn)定性支持較差;
12. log信息記錄:整體流程機(jī)制記錄到LOG文件,確保出問題時(shí),有據(jù)可依,開源播放器幾無log記錄。
13. 實(shí)時(shí)下載速度反饋:大牛直播SDK提供音視頻流實(shí)時(shí)下載回調(diào),并可設(shè)置回調(diào)時(shí)間間隔,確保實(shí)時(shí)下載速度反饋,以此來監(jiān)聽網(wǎng)絡(luò)狀態(tài),開源播放器不具備此能力;
14. 異常狀態(tài)處理、Event狀態(tài)回調(diào):如播放的過程中,斷網(wǎng)、網(wǎng)絡(luò)抖動(dòng)、等各種場(chǎng)景,大牛直播SDK提供的播放器可實(shí)時(shí)回調(diào)相關(guān)狀態(tài),確保上層模塊感知處理,開源播放器對(duì)此支持不好;
15. 關(guān)鍵幀/全幀播放實(shí)時(shí)切換:特別是播放多路畫面的時(shí)候,如果路數(shù)過多,全部解碼、繪制,系統(tǒng)資源占用會(huì)加大,如果能靈活的處理,可以隨時(shí)只播放關(guān)鍵幀,全幀播放切換,對(duì)系統(tǒng)性能要求大幅降低。
接口設(shè)計(jì)
好多開發(fā)者,在初期設(shè)計(jì)接口的時(shí)候,如果沒有足夠的音視頻背景,很容易反復(fù)推翻之前的設(shè)計(jì),我們以Windows平臺(tái)為例,共享我們的設(shè)計(jì)思路,如需要下載demo工程源碼,可以到 GitHub 下載參考:
smart_player_sdk.h
#ifdef __cplusplus extern "C"{ #endiftypedef struct _SmartPlayerSDKAPI{/*flag目前傳0,后面擴(kuò)展用, pReserve傳NULL,擴(kuò)展用,成功返回 NT_ERC_OK*/NT_UINT32(NT_API *Init)(NT_UINT32 flag, NT_PVOID pReserve);/*這個(gè)是最后一個(gè)調(diào)用的接口成功返回 NT_ERC_OK*/NT_UINT32(NT_API *UnInit)();/*flag目前傳0,后面擴(kuò)展用, pReserve傳NULL,擴(kuò)展用,NT_HWND hwnd, 繪制畫面用的窗口, 可以設(shè)置為NULL獲取Handle成功返回 NT_ERC_OK*/NT_UINT32(NT_API *Open)(NT_PHANDLE pHandle, NT_HWND hwnd, NT_UINT32 flag, NT_PVOID pReserve);/*調(diào)用這個(gè)接口之后handle失效,成功返回 NT_ERC_OK*/NT_UINT32(NT_API *Close)(NT_HANDLE handle);/*設(shè)置事件回調(diào),如果想監(jiān)聽事件的話,建議調(diào)用Open成功后,就調(diào)用這個(gè)接口*/NT_UINT32(NT_API *SetEventCallBack)(NT_HANDLE handle,NT_PVOID call_back_data, NT_SP_SDKEventCallBack call_back);/*設(shè)置視頻大小回調(diào)接口*/NT_UINT32(NT_API *SetVideoSizeCallBack)(NT_HANDLE handle, NT_PVOID call_back_data, SP_SDKVideoSizeCallBack call_back);/*設(shè)置視頻回調(diào), 吐視頻數(shù)據(jù)出來frame_format: 只能是NT_SP_E_VIDEO_FRAME_FORMAT_RGB32, NT_SP_E_VIDEO_FRAME_FROMAT_I420*/NT_UINT32(NT_API *SetVideoFrameCallBack)(NT_HANDLE handle,NT_INT32 frame_format,NT_PVOID call_back_data, SP_SDKVideoFrameCallBack call_back);/*設(shè)置視頻回調(diào), 吐視頻數(shù)據(jù)出來, 可以指定吐出來的視頻寬高*handle: 播放句柄*scale_width:縮放寬度(必須是偶數(shù),建議是 16 的倍數(shù))*scale_height:縮放高度(必須是偶數(shù)*scale_filter_mode: 縮放質(zhì)量, 0 的話 SDK 將使用默認(rèn)值, 目前可設(shè)置范圍為[1, 3], 值越大 縮放質(zhì)量越好,但越耗性能*frame_format: 只能是NT_SP_E_VIDEO_FRAME_FORMAT_RGB32, NT_SP_E_VIDEO_FRAME_FROMAT_I420成功返回NT_ERC_OK*/NT_UINT32(NT_API *SetVideoFrameCallBackV2)(NT_HANDLE handle,NT_INT32 scale_width, NT_INT32 scale_height,NT_INT32 scale_filter_mode, NT_INT32 frame_format,NT_PVOID call_back_data, SP_SDKVideoFrameCallBack call_back);/**設(shè)置繪制視頻幀時(shí),視頻幀時(shí)間戳回調(diào)*注意如果當(dāng)前播放流是純音頻,那么將不會(huì)回調(diào),這個(gè)僅在有視頻的情況下才有效*/NT_UINT32(NT_API *SetRenderVideoFrameTimestampCallBack)(NT_HANDLE handle,NT_PVOID call_back_data, SP_SDKRenderVideoFrameTimestampCallBack call_back);/*設(shè)置音頻PCM幀回調(diào), 吐PCM數(shù)據(jù)出來,目前每幀大小是10ms.*/NT_UINT32(NT_API *SetAudioPCMFrameCallBack)(NT_HANDLE handle,NT_PVOID call_back_data, NT_SP_SDKAudioPCMFrameCallBack call_back);/*設(shè)置用戶數(shù)據(jù)回調(diào)*/NT_UINT32(NT_API *SetUserDataCallBack)(NT_HANDLE handle,NT_PVOID call_back_data, NT_SP_SDKUserDataCallBack call_back);/*設(shè)置視頻sei數(shù)據(jù)回調(diào)*/NT_UINT32(NT_API *SetSEIDataCallBack)(NT_HANDLE handle,NT_PVOID call_back_data, NT_SP_SDKSEIDataCallBack call_back);/*開始播放,傳URL進(jìn)去注意:這個(gè)接口目前不再推薦使用,請(qǐng)使用StartPlay. 為方便老客戶升級(jí),暫時(shí)保留. */NT_UINT32(NT_API *Start)(NT_HANDLE handle, NT_PCSTR url, NT_PVOID call_back_data, SP_SDKStartPlayCallBack call_back);/*停止播放注意: 這個(gè)接口目前不再推薦使用,請(qǐng)使用StopPlay. 為方便老客戶升級(jí),暫時(shí)保留. */NT_UINT32(NT_API *Stop)(NT_HANDLE handle);/**提供一組新接口++*//**設(shè)置URL成功返回NT_ERC_OK*/NT_UINT32(NT_API *SetURL)(NT_HANDLE handle, NT_PCSTR url);/*** 設(shè)置解密key,目前只用來解密rtmp加密流* key: 解密密鑰* size: 密鑰長(zhǎng)度* 成功返回NT_ERC_OK*/NT_UINT32(NT_API *SetKey)(NT_HANDLE handle, const NT_BYTE* key, NT_UINT32 size);/*** 設(shè)置解密向量,目前只用來解密rtmp加密流* iv: 解密向量* size: 向量長(zhǎng)度* 成功返回NT_ERC_OK*/NT_UINT32(NT_API *SetDecryptionIV)(NT_HANDLE handle, const NT_BYTE* iv, NT_UINT32 size);/*handle: 播放句柄hwnd: 這個(gè)要傳入真正用來繪制的窗口句柄is_support: 如果支持的話 *is_support 為1, 不支持的話為0接口調(diào)用成功返回NT_ERC_OK*/NT_UINT32(NT_API *IsSupportD3DRender)(NT_HANDLE handle, NT_HWND hwnd, NT_INT32* is_support);/*設(shè)置繪制窗口句柄,如果在調(diào)用Open時(shí)設(shè)置過,那這個(gè)接口可以不調(diào)用如果在調(diào)用Open時(shí)設(shè)置為NULL,那么這里可以設(shè)置一個(gè)繪制窗口句柄給播放器成功返回NT_ERC_OK*/NT_UINT32(NT_API *SetRenderWindow)(NT_HANDLE handle, NT_HWND hwnd);/** 設(shè)置是否播放出聲音,這個(gè)和靜音接口是有區(qū)別的* 這個(gè)接口的主要目的是為了用戶設(shè)置了外部PCM回調(diào)接口后,又不想讓SDK播放出聲音時(shí)使用* is_output_auido_device: 1: 表示允許輸出到音頻設(shè)備,默認(rèn)是1, 0:表示不允許輸出. 其他值接口返回失敗* 成功返回NT_ERC_OK*/NT_UINT32(NT_API *SetIsOutputAudioDevice)(NT_HANDLE handle, NT_INT32 is_output_auido_device);/**開始播放, 注意StartPlay和Start不能混用,要么使用StartPlay, 要么使用Start.* Start和Stop是老接口,不推薦用。請(qǐng)使用StartPlay和StopPlay新接口*/NT_UINT32(NT_API *StartPlay)(NT_HANDLE handle);/**停止播放*/NT_UINT32(NT_API *StopPlay)(NT_HANDLE handle);/** 設(shè)置是否錄視頻,默認(rèn)的話,如果視頻源有視頻就錄,沒有就沒得錄, 但有些場(chǎng)景下可能不想錄制視頻,只想錄音頻,所以增加個(gè)開關(guān)* is_record_video: 1 表示錄制視頻, 0 表示不錄制視頻, 默認(rèn)是1*/NT_UINT32(NT_API *SetRecorderVideo)(NT_HANDLE handle, NT_INT32 is_record_video);/** 設(shè)置是否錄音頻,默認(rèn)的話,如果視頻源有音頻就錄,沒有就沒得錄, 但有些場(chǎng)景下可能不想錄制音頻,只想錄視頻,所以增加個(gè)開關(guān)* is_record_audio: 1 表示錄制音頻, 0 表示不錄制音頻, 默認(rèn)是1*/NT_UINT32(NT_API *SetRecorderAudio)(NT_HANDLE handle, NT_INT32 is_record_audio);/*設(shè)置本地錄像目錄, 必須是英文目錄,否則會(huì)失敗*/NT_UINT32(NT_API *SetRecorderDirectory)(NT_HANDLE handle, NT_PCSTR dir);/*設(shè)置單個(gè)錄像文件最大大小, 當(dāng)超過這個(gè)值的時(shí)候,將切割成第二個(gè)文件size: 單位是KB(1024Byte), 當(dāng)前范圍是 [5MB-800MB], 超出將被設(shè)置到范圍內(nèi)*/NT_UINT32(NT_API *SetRecorderFileMaxSize)(NT_HANDLE handle, NT_UINT32 size);/*設(shè)置錄像文件名生成規(guī)則*/NT_UINT32(NT_API *SetRecorderFileNameRuler)(NT_HANDLE handle, NT_SP_RecorderFileNameRuler* ruler);/*設(shè)置錄像回調(diào)接口*/NT_UINT32(NT_API *SetRecorderCallBack)(NT_HANDLE handle,NT_PVOID call_back_data, SP_SDKRecorderCallBack call_back);/*設(shè)置錄像時(shí)音頻轉(zhuǎn)AAC編碼的開關(guān), aac比較通用,sdk增加其他音頻編碼(比如speex, pcmu, pcma等)轉(zhuǎn)aac的功能.is_transcode: 設(shè)置為1的話,如果音頻編碼不是aac,則轉(zhuǎn)成aac, 如果是aac,則不做轉(zhuǎn)換. 設(shè)置為0的話,則不做任何轉(zhuǎn)換. 默認(rèn)是0.注意: 轉(zhuǎn)碼會(huì)增加性能消耗*/NT_UINT32(NT_API *SetRecorderAudioTranscodeAAC)(NT_HANDLE handle, NT_INT32 is_transcode);/*啟動(dòng)錄像*/NT_UINT32(NT_API *StartRecorder)(NT_HANDLE handle);/*停止錄像*/NT_UINT32(NT_API *StopRecorder)(NT_HANDLE handle);/** 設(shè)置拉流時(shí),吐視頻數(shù)據(jù)的回調(diào)*/NT_UINT32(NT_API *SetPullStreamVideoDataCallBack)(NT_HANDLE handle,NT_PVOID call_back_data, SP_SDKPullStreamVideoDataCallBack call_back);/** 設(shè)置拉流時(shí),吐音頻數(shù)據(jù)的回調(diào)*/NT_UINT32(NT_API *SetPullStreamAudioDataCallBack)(NT_HANDLE handle,NT_PVOID call_back_data, SP_SDKPullStreamAudioDataCallBack call_back);/*設(shè)置拉流時(shí)音頻轉(zhuǎn)AAC編碼的開關(guān), aac比較通用,sdk增加其他音頻編碼(比如speex, pcmu, pcma等)轉(zhuǎn)aac的功能.is_transcode: 設(shè)置為1的話,如果音頻編碼不是aac,則轉(zhuǎn)成aac, 如果是aac,則不做轉(zhuǎn)換. 設(shè)置為0的話,則不做任何轉(zhuǎn)換. 默認(rèn)是0.注意: 轉(zhuǎn)碼會(huì)增加性能消耗*/NT_UINT32(NT_API *SetPullStreamAudioTranscodeAAC)(NT_HANDLE handle, NT_INT32 is_transcode);/*啟動(dòng)拉流*/NT_UINT32(NT_API *StartPullStream)(NT_HANDLE handle);/*停止拉流*/NT_UINT32(NT_API *StopPullStream)(NT_HANDLE handle);/**提供一組新接口--*//*繪制窗口大小改變時(shí),必須調(diào)用*/NT_UINT32(NT_API* OnWindowSize)(NT_HANDLE handle, NT_INT32 cx, NT_INT32 cy);/*萬能接口, 設(shè)置參數(shù), 大多數(shù)問題, 這些接口都能解決*/NT_UINT32(NT_API *SetParam)(NT_HANDLE handle, NT_UINT32 id, NT_PVOID pData);/*萬能接口, 得到參數(shù), 大多數(shù)問題,這些接口都能解決*/NT_UINT32(NT_API *GetParam)(NT_HANDLE handle, NT_UINT32 id, NT_PVOID pData);/*設(shè)置buffer,最小0ms*/NT_UINT32(NT_API *SetBuffer)(NT_HANDLE handle, NT_INT32 buffer);/*靜音接口,1為靜音,0為不靜音*/NT_UINT32(NT_API *SetMute)(NT_HANDLE handle, NT_INT32 is_mute);/*設(shè)置RTSP TCP 模式, 1為TCP, 0為UDP, 僅RTSP有效*/NT_UINT32(NT_API* SetRTSPTcpMode)(NT_HANDLE handle, NT_INT32 isUsingTCP);/*設(shè)置RTSP超時(shí)時(shí)間, timeout單位為秒,必須大于0*/NT_UINT32 (NT_API* SetRtspTimeout)(NT_HANDLE handle, NT_INT32 timeout);/*對(duì)于RTSP來說,有些可能支持rtp over udp方式,有些可能支持使用rtp over tcp方式. 為了方便使用,有些場(chǎng)景下可以開啟自動(dòng)嘗試切換開關(guān), 打開后如果udp無法播放,sdk會(huì)自動(dòng)嘗試tcp, 如果tcp方式播放不了,sdk會(huì)自動(dòng)嘗試udp.is_auto_switch_tcp_udp: 如果設(shè)置1的話, sdk將在tcp和udp之間嘗試切換播放,如果設(shè)置為0,則不嘗試切換.*/NT_UINT32 (NT_API* SetRtspAutoSwitchTcpUdp)(NT_HANDLE handle, NT_INT32 is_auto_switch_tcp_udp);/*設(shè)置秒開, 1為秒開, 0為不秒開*/NT_UINT32(NT_API* SetFastStartup)(NT_HANDLE handle, NT_INT32 isFastStartup);/*設(shè)置低延時(shí)播放模式,默認(rèn)是正常播放模式mode: 1為低延時(shí)模式, 0為正常模式,其他只無效接口調(diào)用成功返回NT_ERC_OK*/NT_UINT32(NT_API* SetLowLatencyMode)(NT_HANDLE handle, NT_INT32 mode);/*檢查是否支持H264硬解碼如果支持的話返回NT_ERC_OK*/NT_UINT32(NT_API *IsSupportH264HardwareDecoder)();/*檢查是否支持H265硬解碼如果支持的話返回NT_ERC_OK*/NT_UINT32(NT_API *IsSupportH265HardwareDecoder)();/**設(shè)置H264硬解*is_hardware_decoder: 1:表示硬解, 0:表示不用硬解*reserve: 保留參數(shù), 當(dāng)前傳0就好*成功返回NT_ERC_OK*/NT_UINT32(NT_API *SetH264HardwareDecoder)(NT_HANDLE handle, NT_INT32 is_hardware_decoder, NT_INT32 reserve);/**設(shè)置H265硬解*is_hardware_decoder: 1:表示硬解, 0:表示不用硬解*reserve: 保留參數(shù), 當(dāng)前傳0就好*成功返回NT_ERC_OK*/NT_UINT32(NT_API *SetH265HardwareDecoder)(NT_HANDLE handle, NT_INT32 is_hardware_decoder, NT_INT32 reserve);/**設(shè)置只解碼視頻關(guān)鍵幀*is_only_dec_key_frame: 1:表示只解碼關(guān)鍵幀, 0:表示都解碼, 默認(rèn)是0*成功返回NT_ERC_OK*/NT_UINT32(NT_API *SetOnlyDecodeVideoKeyFrame)(NT_HANDLE handle, NT_INT32 is_only_dec_key_frame);/**上下反轉(zhuǎn)(垂直反轉(zhuǎn))*is_flip: 1:表示反轉(zhuǎn), 0:表示不反轉(zhuǎn)*/NT_UINT32(NT_API *SetFlipVertical)(NT_HANDLE handle, NT_INT32 is_flip);/**水平反轉(zhuǎn)*is_flip: 1:表示反轉(zhuǎn), 0:表示不反轉(zhuǎn)*/NT_UINT32(NT_API *SetFlipHorizontal)(NT_HANDLE handle, NT_INT32 is_flip);/*設(shè)置旋轉(zhuǎn),順時(shí)針旋轉(zhuǎn)degress: 設(shè)置0, 90, 180, 270度有效,其他值無效注意:除了0度,其他角度播放會(huì)耗費(fèi)更多CPU接口調(diào)用成功返回NT_ERC_OK*/NT_UINT32(NT_API* SetRotation)(NT_HANDLE handle, NT_INT32 degress);/** 在使用D3D繪制的情況下,給繪制窗口上畫一個(gè)logo, logo的繪制由視頻幀驅(qū)動(dòng), 必須傳入argb圖像* argb_data: argb圖像數(shù)據(jù), 如果傳null的話,將清除之前設(shè)置的logo* argb_stride: argb圖像每行的步長(zhǎng)(一般都是image_width*4)* image_width: argb圖像寬度* image_height: argb圖像高度* left: 繪制位置的左邊x* top: 繪制位置的頂部y* render_width: 繪制的寬度* render_height: 繪制的高度*/NT_UINT32(NT_API* SetRenderARGBLogo)(NT_HANDLE handle, const NT_BYTE* argb_data, NT_INT32 argb_stride,NT_INT32 image_width, NT_INT32 image_height,NT_INT32 left, NT_INT32 top,NT_INT32 render_width, NT_INT32 render_height);/*設(shè)置下載速度上報(bào), 默認(rèn)不上報(bào)下載速度is_report: 上報(bào)開關(guān), 1: 表上報(bào). 0: 表示不上報(bào). 其他值無效.report_interval: 上報(bào)時(shí)間間隔(上報(bào)頻率),單位是秒,最小值是1秒1次. 如果小于1且設(shè)置了上報(bào),將調(diào)用失敗注意:如果設(shè)置上報(bào)的話,請(qǐng)?jiān)O(shè)置SetEventCallBack, 然后在回調(diào)函數(shù)里面處理這個(gè)事件.上報(bào)事件是:NT_SP_E_EVENT_ID_DOWNLOAD_SPEED這個(gè)接口必須在StartXXX之前調(diào)用成功返回NT_ERC_OK*/NT_UINT32(NT_API *SetReportDownloadSpeed)(NT_HANDLE handle,NT_INT32 is_report, NT_INT32 report_interval);/*主動(dòng)獲取下載速度speed: 返回下載速度,單位是Byte/s(注意:這個(gè)接口必須在startXXX之后調(diào)用,否則會(huì)失敗)成功返回NT_ERC_OK*/NT_UINT32(NT_API *GetDownloadSpeed)(NT_HANDLE handle, NT_INT32* speed);/*獲取視頻時(shí)長(zhǎng)對(duì)于直播的話,沒有時(shí)長(zhǎng),調(diào)用結(jié)果未定義點(diǎn)播的話,如果獲取成功返回NT_ERC_OK, 如果SDK還在解析中,則返回NT_ERC_SP_NEED_RETRY*/NT_UINT32(NT_API *GetDuration)(NT_HANDLE handle, NT_INT64* duration);/*獲取當(dāng)前播放時(shí)間戳, 單位是毫秒(ms)注意:這個(gè)時(shí)間戳是視頻源的時(shí)間戳,只支持點(diǎn)播. 直播不支持.成功返回NT_ERC_OK*/NT_UINT32(NT_API *GetPlaybackPos)(NT_HANDLE handle, NT_INT64* pos);/*獲取當(dāng)前拉流時(shí)間戳, 單位是毫秒(ms)注意:這個(gè)時(shí)間戳是視頻源的時(shí)間戳,只支持點(diǎn)播. 直播不支持.成功返回NT_ERC_OK*/NT_UINT32(NT_API *GetPullStreamPos)(NT_HANDLE handle, NT_INT64* pos);/*設(shè)置播放位置,單位是毫秒(ms)注意:直播不支持,這個(gè)接口用于點(diǎn)播成功返回NT_ERC_OK*/NT_UINT32(NT_API *SetPos)(NT_HANDLE handle, NT_INT64 pos);/*暫停播放isPause: 1表示暫停, 0表示恢復(fù)播放, 其他錯(cuò)誤注意:直播不存在暫停的概念,所以直播不支持,這個(gè)接口用于點(diǎn)播成功返回NT_ERC_OK*/NT_UINT32(NT_API *Pause)(NT_HANDLE handle, NT_INT32 isPause);/*切換URLurl:要切換的urlswitch_pos: 切換到新url以后,設(shè)置的播放位置, 默認(rèn)請(qǐng)?zhí)?, 這個(gè)只對(duì)設(shè)置播放位置的點(diǎn)播url有效, 直播url無效reserve: 保留參數(shù)注意: 1. 如果切換的url和正在播放的url相同,sdk則不做任何處理調(diào)用前置條件: 已經(jīng)成功調(diào)用了 StartPlay, StartRecorder, StartPullStream 三個(gè)中的任意一個(gè)接口成功返回NT_ERC_OK*/NT_UINT32(NT_API *SwitchURL)(NT_HANDLE handle, NT_PCSTR url, NT_INT64 switch_pos, NT_INT32 reserve);/*捕獲圖片file_name_utf8: 文件名稱,utf8編碼call_back_data: 回調(diào)時(shí)用戶自定義數(shù)據(jù)call_back: 回調(diào)函數(shù),用來通知用戶截圖已經(jīng)完成或者失敗成功返回 NT_ERC_OK只有在播放時(shí)調(diào)用才可能成功,其他情況下調(diào)用,返回錯(cuò)誤.因?yàn)樯蒔NG文件比較耗時(shí),一般需要幾百毫秒,為防止CPU過高,SDK會(huì)限制截圖請(qǐng)求數(shù)量,當(dāng)超過一定數(shù)量時(shí),調(diào)用這個(gè)接口會(huì)返回NT_ERC_SP_TOO_MANY_CAPTURE_IMAGE_REQUESTS. 這種情況下, 請(qǐng)延時(shí)一段時(shí)間,等SDK處理掉一些請(qǐng)求后,再嘗試.*/NT_UINT32(NT_API* CaptureImage)(NT_HANDLE handle, NT_PCSTR file_name_utf8,NT_PVOID call_back_data, SP_SDKCaptureImageCallBack call_back);/** 使用GDI繪制RGB32數(shù)據(jù)* 32位的rgb格式, r, g, b各占8, 另外一個(gè)字節(jié)保留, 內(nèi)存字節(jié)格式為: bb gg rr xx, 主要是和windows位圖匹配, 在小端模式下,按DWORD類型操作,最高位是xx, 依次是rr, gg, bb* 為了保持和windows位圖兼容,步長(zhǎng)(image_stride)必須是width_*4* handle: 播放器句柄* hdc: 繪制dc* x_dst: 繪制面左上角x坐標(biāo)* y_dst: 繪制面左上角y坐標(biāo)* dst_width: 要繪制的寬度* dst_height: 要繪制的高度* x_src: 源圖像x位置* y_src: 原圖像y位置* rgb32_data: rgb32數(shù)據(jù),格式參見前面的注釋說明* rgb32_data_size: 數(shù)據(jù)大小* image_width: 圖像實(shí)際寬度* image_height: 圖像實(shí)際高度* image_stride: 圖像步長(zhǎng)*/NT_UINT32(NT_API *GDIDrawRGB32)(NT_HANDLE handle, NT_HDC hdc,NT_INT32 x_dst, NT_INT32 y_dst,NT_INT32 dst_width, NT_INT32 dst_height,NT_INT32 x_src, NT_INT32 y_src,NT_INT32 src_width, NT_INT32 src_height,const NT_BYTE* rgb32_data, NT_UINT32 rgb32_data_size,NT_INT32 image_width, NT_INT32 image_height,NT_INT32 image_stride);/** 使用GDI繪制ARGB數(shù)據(jù)* 內(nèi)存字節(jié)格式為: bb gg rr alpha, 主要是和windows位圖匹配, 在小端模式下,按DWORD類型操作,最高位是alpha, 依次是rr, gg, bb* 為了保持和windows位圖兼容,步長(zhǎng)(image_stride)必須是width_*4* hdc: 繪制dc* x_dst: 繪制面左上角x坐標(biāo)* y_dst: 繪制面左上角y坐標(biāo)* dst_width: 要繪制的寬度* dst_height: 要繪制的高度* x_src: 源圖像x位置* y_src: 原圖像y位置* argb_data: argb圖像數(shù)據(jù), 格式參見前面的注釋說明* image_stride: 圖像每行步長(zhǎng)* image_width: 圖像實(shí)際寬度* image_height: 圖像實(shí)際高度*/NT_UINT32(NT_API *GDIDrawARGB)(NT_HDC hdc,NT_INT32 x_dst, NT_INT32 y_dst,NT_INT32 dst_width, NT_INT32 dst_height,NT_INT32 x_src, NT_INT32 y_src,NT_INT32 src_width, NT_INT32 src_height,const NT_BYTE* argb_data, NT_INT32 image_stride,NT_INT32 image_width, NT_INT32 image_height);} SmartPlayerSDKAPI;NT_UINT32 NT_API GetSmartPlayerSDKAPI(SmartPlayerSDKAPI* pAPI);/*reserve1: 請(qǐng)傳0NT_PVOID: 請(qǐng)傳NULL成功返回: NT_ERC_OK*/NT_UINT32 NT_API NT_SP_SetSDKClientKey(NT_PCSTR cid, NT_PCSTR key, NT_INT32 reserve1, NT_PVOID reserve2);#ifdef __cplusplus } #endifsmart_player_define.h
#ifndef SMART_PLAYER_DEFINE_H_ #define SMART_PLAYER_DEFINE_H_#ifdef WIN32 #include <windows.h> #endif#ifdef SMART_HAS_COMMON_DIC #include "../../topcommon/nt_type_define.h" #include "../../topcommon/nt_base_code_define.h" #else #include "nt_type_define.h" #include "nt_base_code_define.h" #endif#ifdef __cplusplus extern "C"{ #endif#ifndef NT_HWND_ #define NT_HWND_ #ifdef WIN32 typedef HWND NT_HWND; #else typedef void* NT_HWND; #endif #endif#ifndef NT_HDC_ #define NT_HDC_#ifdef _WIN32 typedef HDC NT_HDC; #else typedef void* NT_HDC; #endif#endif/*錯(cuò)誤碼*/ typedef enum _SP_E_ERROR_CODE {NT_ERC_SP_HWND_IS_NULL = (NT_ERC_SMART_PLAYER_SDK | 0x1), // 窗口句柄是空NT_ERC_SP_HWND_INVALID = (NT_ERC_SMART_PLAYER_SDK | 0x2), // 窗口句柄無效NT_ERC_SP_TOO_MANY_CAPTURE_IMAGE_REQUESTS = (NT_ERC_SMART_PLAYER_SDK | 0x3), // 太多的截圖請(qǐng)求NT_ERC_SP_WINDOW_REGION_INVALID = (NT_ERC_SMART_PLAYER_SDK | 0x4), // 窗口區(qū)域無效,可能窗口寬或者高小于1NT_ERC_SP_DIR_NOT_EXIST = (NT_ERC_SMART_PLAYER_SDK | 0x5), // 目錄不存在NT_ERC_SP_NEED_RETRY = (NT_ERC_SMART_PLAYER_SDK | 0x6), // 需要重試 } SP_E_ERROR_CODE;/*設(shè)置參數(shù)ID, 這個(gè)目前這么寫,SmartPlayerSDK 已經(jīng)劃分范圍*/ typedef enum _SP_E_PARAM_ID {SP_PARAM_ID_BASE = NT_PARAM_ID_SMART_PLAYER_SDK,} SP_E_PARAM_ID;/*事件ID*/ typedef enum _NT_SP_E_EVENT_ID {NT_SP_E_EVENT_ID_BASE = NT_EVENT_ID_SMART_PLAYER_SDK,NT_SP_E_EVENT_ID_CONNECTING = NT_SP_E_EVENT_ID_BASE | 0x2, /*連接中*/NT_SP_E_EVENT_ID_CONNECTION_FAILED = NT_SP_E_EVENT_ID_BASE | 0x3, /*連接失敗*/NT_SP_E_EVENT_ID_CONNECTED = NT_SP_E_EVENT_ID_BASE | 0x4, /*已連接*/NT_SP_E_EVENT_ID_DISCONNECTED = NT_SP_E_EVENT_ID_BASE | 0x5, /*斷開連接*/NT_SP_E_EVENT_ID_NO_MEDIADATA_RECEIVED = NT_SP_E_EVENT_ID_BASE | 0x8, /*收不到RTMP數(shù)據(jù)*/NT_SP_E_EVENT_ID_RTSP_STATUS_CODE = NT_SP_E_EVENT_ID_BASE | 0xB, /*rtsp status code上報(bào), 目前只上報(bào)401, param1表示status code*/NT_SP_E_EVENT_ID_NEED_KEY = NT_SP_E_EVENT_ID_BASE | 0xC, /*需要輸入解密key才能播放*/NT_SP_E_EVENT_ID_KEY_ERROR = NT_SP_E_EVENT_ID_BASE | 0xD, /*解密key不正確*//* 接下來請(qǐng)從0x81開始*/NT_SP_E_EVENT_ID_START_BUFFERING = NT_SP_E_EVENT_ID_BASE | 0x81, /*開始緩沖*/NT_SP_E_EVENT_ID_BUFFERING = NT_SP_E_EVENT_ID_BASE | 0x82, /*緩沖中, param1 表示百分比進(jìn)度*/NT_SP_E_EVENT_ID_STOP_BUFFERING = NT_SP_E_EVENT_ID_BASE | 0x83, /*停止緩沖*/NT_SP_E_EVENT_ID_DOWNLOAD_SPEED = NT_SP_E_EVENT_ID_BASE | 0x91, /*下載速度, param1表示下載速度,單位是(Byte/s)*/NT_SP_E_EVENT_ID_PLAYBACK_REACH_EOS = NT_SP_E_EVENT_ID_BASE | 0xa1, /*播放結(jié)束, 直播流沒有這個(gè)事件,點(diǎn)播流才有*/NT_SP_E_EVENT_ID_RECORDER_REACH_EOS = NT_SP_E_EVENT_ID_BASE | 0xa2, /*錄像結(jié)束, 直播流沒有這個(gè)事件, 點(diǎn)播流才有*/NT_SP_E_EVENT_ID_PULLSTREAM_REACH_EOS = NT_SP_E_EVENT_ID_BASE | 0xa3, /*拉流結(jié)束, 直播流沒有這個(gè)事件,點(diǎn)播流才有*/NT_SP_E_EVENT_ID_DURATION = NT_SP_E_EVENT_ID_BASE | 0xa8, /*視頻時(shí)長(zhǎng),如果是直播,則不上報(bào),如果是點(diǎn)播的話, 若能從視頻源獲取視頻時(shí)長(zhǎng)的話,則上報(bào), param1表示視頻時(shí)長(zhǎng),單位是毫秒(ms)*/} NT_SP_E_EVENT_ID;//定義視頻幀圖像格式 typedef enum _NT_SP_E_VIDEO_FRAME_FORMAT {NT_SP_E_VIDEO_FRAME_FORMAT_RGB32 = 1, // 32位的rgb格式, r, g, b各占8, 另外一個(gè)字節(jié)保留, 內(nèi)存字節(jié)格式為: bb gg rr xx, 主要是和windows位圖匹配, 在小端模式下,按DWORD類型操作,最高位是xx, 依次是rr, gg, bbNT_SP_E_VIDEO_FRAME_FORMAT_ARGB = 2, // 32位的argb格式,內(nèi)存字節(jié)格式是: bb gg rr aa 這種類型,和windows位圖匹配NT_SP_E_VIDEO_FRAME_FROMAT_I420 = 3, // YUV420格式, 三個(gè)分量保存在三個(gè)面上 } NT_SP_E_VIDEO_FRAME_FORMAT;// 定義視頻幀結(jié)構(gòu). typedef struct _NT_SP_VideoFrame {NT_INT32 format_; // 圖像格式, 請(qǐng)參考NT_SP_E_VIDEO_FRAME_FORMATNT_INT32 width_; // 圖像寬NT_INT32 height_; // 圖像高NT_UINT64 timestamp_; // 時(shí)間戳, 一般是0,不使用, 以ms為單位的// 具體的圖像數(shù)據(jù), argb和rgb32只用第一個(gè), I420用前三個(gè)NT_UINT8* plane0_;NT_UINT8* plane1_;NT_UINT8* plane2_;NT_UINT8* plane3_;// 每一個(gè)平面的每一行的字節(jié)數(shù),對(duì)于argb和rgb32,為了保持和windows位圖兼容,必須是width_*4// 對(duì)于I420, stride0_ 是y的步長(zhǎng), stride1_ 是u的步長(zhǎng), stride2_ 是v的步長(zhǎng),NT_INT32 stride0_;NT_INT32 stride1_;NT_INT32 stride2_;NT_INT32 stride3_;} NT_SP_VideoFrame;// 如果三項(xiàng)都是0的話,將不能啟動(dòng)錄像 typedef struct _NT_SP_RecorderFileNameRuler {NT_UINT32 type_; // 這個(gè)值目前默認(rèn)是0,將來擴(kuò)展用NT_PCSTR file_name_prefix_; // 設(shè)置一個(gè)錄像文件名前綴, 例如:daniuliveNT_INT32 append_date_; // 如果是1的話,將在文件名上加日期, 例如:daniulive-2017-01-17NT_INT32 append_time_; // 如果是1的話,將增加時(shí)間,例如:daniulive-2017-01-17-17-10-36 } NT_SP_RecorderFileNameRuler;/* *拉流吐視頻數(shù)據(jù)時(shí),一些相關(guān)的數(shù)據(jù) */ typedef struct _NT_SP_PullStreamVideoDataInfo {NT_INT32 is_key_frame_; /* 1:表示關(guān)鍵幀, 0:表示非關(guān)鍵幀 */NT_UINT64 timestamp_; /* 解碼時(shí)間戳, 單位是毫秒 */NT_INT32 width_; /* 一般是0 */NT_INT32 height_; /* 一般也是0 */NT_BYTE* parameter_info_; /* 一般是NULL */NT_UINT32 parameter_info_size_; /* 一般是0 */NT_UINT64 presentation_timestamp_; /*顯示時(shí)間戳, 這個(gè)值要大于或等于timestamp_, 單位是毫秒*/} NT_SP_PullStreamVideoDataInfo;/* *拉流吐音頻數(shù)據(jù)時(shí),一些相關(guān)的數(shù)據(jù) */ typedef struct _NT_SP_PullStreamAuidoDataInfo {NT_INT32 is_key_frame_; /* 1:表示關(guān)鍵幀, 0:表示非關(guān)鍵幀 */NT_UINT64 timestamp_; /* 單位是毫秒 */NT_INT32 sample_rate_; /* 一般是0 */NT_INT32 channel_; /* 一般是0 */NT_BYTE* parameter_info_; /* 如果是AAC的話,這個(gè)是有值的, 其他編碼一般忽略 */NT_UINT32 parameter_info_size_; /*如果是AAC的話,這個(gè)是有值的, 其他編碼一般忽略 */NT_UINT64 reserve_; /* 保留 */} NT_SP_PullStreamAuidoDataInfo;/* 當(dāng)播放器得到時(shí)候視頻大小后,會(huì)回調(diào) */ typedef NT_VOID(NT_CALLBACK *SP_SDKVideoSizeCallBack)(NT_HANDLE handle, NT_PVOID user_data,NT_INT32 width, NT_INT32 height);/* 調(diào)用Start時(shí)傳入, 回調(diào)接口 */ typedef NT_VOID(NT_CALLBACK *SP_SDKStartPlayCallBack)(NT_HANDLE handle, NT_PVOID user_data, NT_UINT32 result);/* 視頻圖像回調(diào) status:目前不用,默認(rèn)是0,將來可能會(huì)用 */ typedef NT_VOID(NT_CALLBACK* SP_SDKVideoFrameCallBack)(NT_HANDLE handle, NT_PVOID user_data, NT_UINT32 status,const NT_SP_VideoFrame* frame);/* 音頻PCM數(shù)據(jù)回調(diào), 目前每幀長(zhǎng)度是10ms status:目前不用,默認(rèn)是0,將來可能會(huì)用 data: PCM 數(shù)據(jù) size: 數(shù)據(jù)大小 sample_rate: 采樣率 channel: 通道數(shù) per_channel_sample_number: 每個(gè)通道的采樣數(shù) */ typedef NT_VOID(NT_CALLBACK* NT_SP_SDKAudioPCMFrameCallBack)(NT_HANDLE handle, NT_PVOID user_data, NT_UINT32 status,NT_BYTE* data, NT_UINT32 size,NT_INT32 sample_rate, NT_INT32 channel, NT_INT32 per_channel_sample_number);/* 截屏回調(diào) result: 如果截屏成功的話,result是NT_ERC_OK,其他錯(cuò)誤 */ typedef NT_VOID(NT_CALLBACK* SP_SDKCaptureImageCallBack)(NT_HANDLE handle, NT_PVOID user_data, NT_UINT32 result,NT_PCSTR file_name);/* 繪制視頻時(shí),視頻幀時(shí)間戳回調(diào), 這個(gè)用在一些特殊場(chǎng)景下,沒有特殊需求的用戶不需要關(guān)注 timestamp: 單位是毫秒 reserve1: 保留參數(shù) reserve2: 保留參數(shù) */ typedef NT_VOID(NT_CALLBACK* SP_SDKRenderVideoFrameTimestampCallBack)(NT_HANDLE handle, NT_PVOID user_data, NT_UINT64 timestamp,NT_UINT64 reserve1, NT_PVOID reserve2);/* 錄像回調(diào) status: 1:表示開始寫一個(gè)新錄像文件. 2:表示已經(jīng)寫好一個(gè)錄像文件 file_name: 實(shí)際錄像文件名 */ typedef NT_VOID(NT_CALLBACK* SP_SDKRecorderCallBack)(NT_HANDLE handle, NT_PVOID user_data, NT_UINT32 status,NT_PCSTR file_name);/* *拉流時(shí),視頻數(shù)據(jù)回調(diào) video_codec_id: 請(qǐng)參考NT_MEDIA_CODEC_ID data: 視頻數(shù)據(jù) size: 視頻數(shù)據(jù)大小 info: 視頻數(shù)據(jù)相關(guān)信息 reserve: 保留參數(shù) */ typedef NT_VOID(NT_CALLBACK* SP_SDKPullStreamVideoDataCallBack)(NT_HANDLE handle, NT_PVOID user_data,NT_UINT32 video_codec_id, NT_BYTE* data, NT_UINT32 size, NT_SP_PullStreamVideoDataInfo* info,NT_PVOID reserve);/* *拉流時(shí),音頻數(shù)據(jù)回調(diào) auido_codec_id: 請(qǐng)參考NT_MEDIA_CODEC_ID data: 音頻數(shù)據(jù) size: 音頻數(shù)據(jù)大小 info: 音頻數(shù)據(jù)相關(guān)信息 reserve: 保留參數(shù) */ typedef NT_VOID(NT_CALLBACK* SP_SDKPullStreamAudioDataCallBack)(NT_HANDLE handle, NT_PVOID user_data,NT_UINT32 auido_codec_id, NT_BYTE* data, NT_UINT32 size, NT_SP_PullStreamAuidoDataInfo* info,NT_PVOID reserve);/* *播放器事件回調(diào) event_id: 事件ID,請(qǐng)參考NT_SP_E_EVENT_ID param1 到 param6, 值的意義和具體事件ID相關(guān), 注意如果具體事件ID沒有說明param1-param6的含義,那說明這個(gè)事件不帶參數(shù) */ typedef NT_VOID(NT_CALLBACK* NT_SP_SDKEventCallBack)(NT_HANDLE handle, NT_PVOID user_data,NT_UINT32 event_id,NT_INT64 param1,NT_INT64 param2,NT_UINT64 param3,NT_PCSTR param4,NT_PCSTR param5,NT_PVOID param6);/* * * 用戶數(shù)據(jù)回調(diào),目前是推送端發(fā)送過來的 * data_type: 數(shù)據(jù)類型,1:表示二進(jìn)制字節(jié)類型. 2:表示utf8字符串 * data:實(shí)際數(shù)據(jù), 如果data_type是1的話,data類型是const NT_BYTE*, 如果data_type是2的話,data類型是 const NT_CHAR* * size: 數(shù)據(jù)大小 * timestamp: 視頻時(shí)間戳 * reserve1: 保留 * reserve2: 保留 * reserve3: 保留 */ typedef NT_VOID(NT_CALLBACK* NT_SP_SDKUserDataCallBack)(NT_HANDLE handle, NT_PVOID user_data,NT_INT32 data_type,NT_PVOID data,NT_UINT32 size,NT_UINT64 timestamp,NT_UINT64 reserve1,NT_INT64 reserve2,NT_PVOID reserve3);/* * * 視頻的sei數(shù)據(jù)回調(diào) * data: sei 數(shù)據(jù) * size: sei 數(shù)據(jù)大小 * timestamp:視頻時(shí)間戳 * reserve1: 保留 * reserve2: 保留 * reserve3: 保留 * 注意: 目前測(cè)試發(fā)現(xiàn)有些視頻有好幾個(gè)sei nal, 為了方便用戶處理,我們把解析到的所有sei都吐出來,sei nal之間還是用 00 00 00 01 分隔, 這樣方便解析 * 吐出來的sei數(shù)據(jù)目前加了 00 00 00 01 前綴 */ typedef NT_VOID(NT_CALLBACK* NT_SP_SDKSEIDataCallBack)(NT_HANDLE handle, NT_PVOID user_data,NT_BYTE* data,NT_UINT32 size,NT_UINT64 timestamp,NT_UINT64 reserve1,NT_INT64 reserve2,NT_PVOID reserve3);#ifdef __cplusplus } #endif#endif總結(jié)
總的來說,無論是基于開源播放器二次開發(fā),還是全自研,一個(gè)好的RTMP播放器或RTSP播放器,設(shè)計(jì)的時(shí)候,更多考慮的應(yīng)該是如何做的更靈活、穩(wěn)定,單純的幾個(gè)接口,很難滿足通用化的產(chǎn)品訴求。
以下共勉:厚積薄發(fā),登上山頂,不是為了飽覽風(fēng)光,是為了尋找更高的山峰!
總結(jié)
以上是生活随笔為你收集整理的跨平台低延迟的RTMP/RTSP直播播放器设计实现的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何理解面向过程和面向对象?
- 下一篇: 【机器学习】深刻理解决策树-动手计算ID