國產操做系統多爲以Linux爲基礎二次開發的操做系統。2014年4月8日起,美國微軟公司中止了對Windows XP SP3操做系統提供服務支持,這引發了社會和廣大用戶的普遍關注和對信息安全的擔心。而2020年對Windows7服務支持的終止再一次推進了國產系統的發展。
工信部對此表示,將繼續加大力度,支持Linux的國產操做系統的研發和應用,並但願用戶可使用國產操做系統。隨着信息技術和互聯網的快速發展普及,電子商務已經成爲不可抗拒的現代商業潮流,雲計算、大數據應用日趨成熟,但隨之帶來了許多問題和挑戰。爲全面響應國家「互聯網+」戰略的提出和深刻貫徹落實國家「十二五」規劃綱要,幫助傳統企業開展「商務智慧轉型」,增強電子商務深刻應用,特別是移動電子商務發展中的環境保障建設,促進電子商務行業健康有序發展,使電子商務相關的技術和經濟、法律和規則、誠信和信譽及如何創建一個安全、可靠、可信的電子商務環境,保障電子商務活動中系統、交易的安全性,信息的保密性,已經成爲當前亟待須要探討和解決的重要課題。api
在發佈國產操做系統|Linux平臺的RTMP|RTSP直播播放SDK以前,大牛直播SDK(官方)的直播播放SDK無需贅述,採用自研內核框架,功能齊全、高穩定、超低延遲、超低資源佔用,覆蓋Windows、Android和iOS平臺。緩存
本次發佈的可用於國產操做系統和Linux上的的RTMP|RTSP直播播放SDK, 視頻繪製使用XLib相關庫實現, 音頻輸出使用PulseAudio和Alsa Lib實現,除了常規功能如實時靜音、快照、buffer time設定、網絡自動重連等,RTMP支持擴展H265播放, RTSP也支持H265播放。安全
播放器接口和調用都比較簡單,集成複雜度低,且不依賴於QT。網絡
大牛直播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; }
窗體相關ide
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播放器,本文拋磚引玉,感興趣的開發者可酌情參考。