麒麟操作系统|Linux下低延时RTMP|RTSP直播播放实现
背景
????????國產操作系統多為以Linux為基礎二次開發的操作系統。2014年4月8日起,美國微軟公司停止了對Windows XP SP3操作系統提供服務支持,這引起了社會和廣大用戶的廣泛關注和對信息安全的擔憂。而2020年對Windows7服務支持的終止再一次推動了國產系統的發展。
????????工信部對此表示,將繼續加大力度,支持Linux的國產操作系統的研發和應用,并希望用戶可以使用國產操作系統。隨著信息技術和互聯網的快速發展普及,電子商務已經成為不可抗拒的現代商業潮流,云計算、大數據應用日趨成熟,但隨之帶來了許多問題和挑戰。為全面響應國家“互聯網+”戰略的提出和深入貫徹落實國家“十二五”規劃綱要,幫助傳統企業開展“商務智慧轉型”,加強電子商務深入應用,特別是移動電子商務發展中的環境保障建設,促進電子商務行業健康有序發展,使電子商務相關的技術和經濟、法律和規則、誠信和信譽及如何建立一個安全、可靠、可信的電子商務環境,保障電子商務活動中系統、交易的安全性,信息的保密性,已經成為當前亟待需要探討和解決的重要課題。
國產操作系統|Linux下RTMP|RTSP直播播放
????????在發布國產操作系統|Linux平臺的RTMP|RTSP直播播放SDK之前,大牛直播SDK(官方)的直播播放SDK無需贅述,采用自研內核框架,功能齊全、高穩定、超低延遲、超低資源占用,覆蓋Windows、Android和iOS平臺。
? ? ? ? 本次發布的可用于國產操作系統和Linux上的的RTMP|RTSP直播播放SDK, 視頻繪制使用XLib相關庫實現, 音頻輸出使用PulseAudio和Alsa Lib實現,除了常規功能如實時靜音、快照、buffer time設定、網絡自動重連等,RTMP支持擴展H265播放, RTSP也支持H265播放。
????????播放器接口和調用都比較簡單,集成復雜度低,且不依賴于QT。
相關DEMO
? ? ? ? 大牛直播SDK發布的Linux平臺播放器SDK支持多實例播放,以單個窗體播放為例,相關代碼如下:
const char* player_url_ = "rtsp://admin:daniulive12345@192.168.0.120:554/h264/ch1/main/av_stream";int main(int argc, char *argv[]) {XInitThreads(); // X支持多線程, 必須調用NT_SDKLogInit();// SDK初始化SmartPlayerSDKAPI player_api;if (!NT_PlayerSDKInit(player_api)){fprintf(stderr, "SDK init failed.\n");return 0;}auto display = XOpenDisplay(nullptr);if (!display){fprintf(stderr, "Cannot connect to X server\n");player_api.UnInit();return 0;}display_ = display;auto screen = DefaultScreen(display);auto root = XRootWindow(display, screen);XWindowAttributes root_win_att;if (!XGetWindowAttributes(display, root, &root_win_att)){fprintf(stderr, "Get Root window attri failed\n");player_api.UnInit();XCloseDisplay(display);return 0;}if (root_win_att.width < 100 || root_win_att.height < 100){fprintf(stderr, "Root window size error.\n");player_api.UnInit();XCloseDisplay(display);return 0;}fprintf(stdout, "Root Window Size:%d*%d\n", root_win_att.width, root_win_att.height);int main_w = root_win_att.width / 2, main_h = root_win_att.height / 2;auto black_pixel = BlackPixel(display, screen);auto white_pixel = WhitePixel(display, screen);main_wid_ = XCreateSimpleWindow(display, root, 0, 0, main_w, main_h, 0, white_pixel, black_pixel);if (!main_wid_){fprintf(stderr, "Cannot Create Main Window\n");player_api.UnInit();XCloseDisplay(display);return 0;}XSelectInput(display, main_wid_, StructureNotifyMask | KeyPressMask);auto sub_wid = CreateSubWindow(display, screen, main_wid_);if (!sub_wid){fprintf(stderr, "Cannot Create Render Window\n");player_api.UnInit();XDestroyWindow(display, main_wid_);XCloseDisplay(display);return 0;}XMapWindow(display, main_wid_);XStoreName(display, main_wid_, win_base_title);XMapWindow(display, sub_wid);NT_HANDLE handle = nullptr;// 打開一個播放實例,可以Open多個播放實例, 然后播放多路if (NT_ERC_OK != player_api.Open(&handle, 0, nullptr)){player_api.UnInit();fprintf(stderr, "player_api.Open failed!\n");XDestroyWindow(display, sub_wid);XDestroyWindow(display, main_wid_);XCloseDisplay(display);return 0;}player_api.SetEventCallBack(handle, nullptr, &NT_OnSDKEventHandle);player_api.SetVideoSizeCallBack(handle, nullptr, &NT_SDKVideoSizeHandle);player_api.SetReportDownloadSpeed(handle, 1, 5); // 5秒上報一次下載速度player_api.SetRtspTimeout(handle, 15);player_api.SetRtspAutoSwitchTcpUdp(handle, 1);player_api.SetBuffer(handle, 0); // 設置緩存player_api.SetIsOutputAudioDevice(handle, 1);player_api.SetAudioOutputLayer(handle, 0); // 使用pluse 或者 alsa播放, 兩個可以選擇一個//player_api.SetAudioVolume(handle, 100);player_api.SetURL(handle, player_url_); // 設置播放地址, rtsp或者rtmp地址player_api.SetXDisplay(handle, display);player_api.SetXScreenNumber(handle, screen);player_api.SetRenderXWindow(handle, sub_wid); // 設置繪制的X窗口player_api.SetRenderScaleMode(handle, 1); // 按比例繪制或者全填充player_api.SetRenderTextureScaleFilterMode(handle, 3); player_api.SetFastStartup(handle, 1);player_api.SetLowLatencyMode(handle, 0);if (NT_ERC_OK != player_api.StartPlay(handle)){player_api.Close(handle);handle = nullptr;player_api.UnInit();fprintf(stderr, "player_api.StartPlay failed!\n");XDestroyWindow(display, sub_wid);XDestroyWindow(display, main_wid_);XCloseDisplay(display);return 0;}while (true){while (MY_X11_Pending(display, 10)){XEvent xev;memset(&xev, 0, sizeof(xev));XNextEvent(display, &xev);if (xev.type == ConfigureNotify){if (xev.xconfigure.window == main_wid_){if (xev.xconfigure.width != main_w || xev.xconfigure.height != main_h){main_w = xev.xconfigure.width;main_h = xev.xconfigure.height;XMoveResizeWindow(display, sub_wid, 0, 0, main_w-4, main_h-4);}}else{if (sub_wid == xev.xconfigure.window){player_api.OnWindowSize(handle, xev.xconfigure.width, xev.xconfigure.height);}}}else if (xev.type == KeyPress){if (xev.xkey.keycode == XKeysymToKeycode(display, XK_Escape)){fprintf(stdout, "ESC Key Press\n");if (handle != nullptr){player_api.StopPlay(handle); // 停止播放player_api.Close(handle);handle = nullptr;}XDestroyWindow(display, sub_wid);XDestroyWindow(display, main_wid_);XCloseDisplay(display);player_api.UnInit();fprintf(stdout, "Close Player....\n");return 0;}}}} }日志設置和SDK Init相關
void NT_SDKLogInit() {SmartLogAPI log_api;memset(&log_api, 0, sizeof(log_api));GetSmartLogAPI(&log_api);log_api.SetLevel(SL_INFO_LEVEL);log_api.SetPath((NT_PVOID)"./"); }bool NT_PlayerSDKInit(SmartPlayerSDKAPI& player_api) {memset(&player_api, 0, sizeof(player_api));GetSmartPlayerSDKAPI(&player_api);auto ret = player_api.Init(0, nullptr);if (NT_ERC_OK != ret){fprintf(stderr, "player_api.Init failed!\n");return false;}else{fprintf(stdout, "player_api.Init ok!\n");}return true; }窗體相關
Display* display_ = nullptr; Window main_wid_ = None;const char* win_base_title = "Rtmp/Rtsp Live Player Demo";int EventPoll(int fd, bool is_write, int timeout_ms) {int result;do{struct pollfd info;info.fd = fd;if (is_write){info.events = POLLOUT;}else{info.events = POLLIN | POLLPRI;}result = poll(&info, 1, timeout_ms);} while (result < 0 && errno == EINTR);return result; }bool MY_X11_Pending(Display* display, int timeout_ms) {XFlush(display);if (XEventsQueued(display, QueuedAlready) > 0){return true;}if (EventPoll(ConnectionNumber(display), false, timeout_ms)){if (XPending(display) > 0){return true;}}return false; }Window CreateSubWindow(Display* display, int screen, Window parent) {XWindowAttributes parent_win_att;XGetWindowAttributes(display, parent, &parent_win_att);fprintf(stdout, "parent w:%d, h:%d\n", parent_win_att.width, parent_win_att.height);XSetWindowAttributes swa;swa.border_pixel = WhitePixel(display, screen);swa.event_mask = KeyPressMask | StructureNotifyMask;return XCreateWindow(display, parent, 0, 0, parent_win_att.width-4, parent_win_att.height-4,2, parent_win_att.depth, InputOutput, parent_win_att.visual, CWEventMask | CWBorderPixel, &swa); }Event回調
void NT_OnSDKEventHandle(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) {if (NT_SP_E_EVENT_ID_DOWNLOAD_SPEED == event_id){fprintf(stdout, "OnSDKEventHandle handle:%p speed:%lldkbps, %lldKB/s. \r", handle, (param1 * 8) / 1000, param1 / 1024);fflush(stdout);} }視頻分辨率回調
void NT_SDKVideoSizeHandle(NT_HANDLE handle, NT_PVOID userData,NT_INT32 width, NT_INT32 height) {if (display_ && main_wid_){std::ostringstream ss;ss << win_base_title << " [Video Size: " << width << "*" << height << " ]";XStoreName(display_, main_wid_, ss.str().c_str());} }相關界面
?總結
國產操作系統|Linux下的RTMP、RTSP直播播放,經實際測試,延遲和Windows平臺一樣,毫秒級,隨著國產操作系統在無紙化同屏等行業的推進,越來越多的場景需要這樣一款穩定性高延遲低的RTMP|RTSP播放器,本文拋磚引玉,感興趣的開發者可酌情參考。
總結
以上是生活随笔為你收集整理的麒麟操作系统|Linux下低延时RTMP|RTSP直播播放实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Python】利用 Python 实现
- 下一篇: 【深度学习】从梯度下降到 Adam!一文