在数字化转型的浪潮中,无纸化同屏技术正逐渐成为会议、教育、培训等众多场景的核心需求。而大牛直播SDK凭借其深厚的技术积淀和卓越的性能优势,与到家无纸化龙头企业紧密合作,为无纸化同屏行业的发展注入了强大动力。
一、技术自主可控
-
避免技术依赖风险:在当前复杂的国际形势下,自主研发技术的重要性不言而喻。大牛直播SDK通过自主研发,避免了因外部因素导致的技术供应中断风险,如美国制裁等。同时,也避免了被国外技术限制的问题,确保了技术的稳定性和可靠性。
-
适应国产化生态:随着统信UOS、麒麟OS等国产Linux操作系统的逐渐普及,大牛直播SDK积极适配这些国产化操作系统,确保与国产软硬件的无缝配合。这不仅提升了系统的兼容性和稳定性,也推动了国产化生态的发展。
-
保障数据安全:自主研发使得大牛直播SDK能够从底层设计保障数据安全,避免数据被恶意获取或泄露。通过严格控制数据采集、编码和推流过程,确保敏感信息的安全。
二、跨平台兼容性
大牛直播SDK支持Windows、Linux(x64_64与aarch64架构)、Android及iOS等多个主流平台,满足了不同场景下的使用需求。这种跨平台兼容性使得用户可以在多种设备上无缝切换,极大地提高了设备的利用率和用户的便利性。例如,在无纸化会议中,参会人员可以使用自己熟悉的设备进行投屏展示,而无需担心兼容性问题。
三、低延迟与高效传输
-
自适应算法:大牛直播SDK采用全自研框架和自适应算法,能够根据网络状况自动调整参数,实现更低的延迟和更高的采集编码传输效率。这在无纸化同屏场景中尤为重要,因为实时性是影响用户体验的关键因素之一。
-
硬件编码支持:支持H.264/H.265硬件编码,利用GPU加速视频编码,降低CPU负担,提升编码效率,进而提高播放流畅度。硬件编码不仅能够提供更高的视频质量,还能有效减少资源占用,确保系统在长时间运行时的稳定性。
四、功能丰富与易用性
-
丰富的功能支持:大牛直播SDK提供了多种功能支持,如实时快照、实时录像、实时回调YUV数据、实时音量调节等。这些功能在无纸化同屏场景中可以满足不同用户的需求,例如在会议中可以实时记录重要信息,或在教学中可以灵活调整音量和画面。
-
易用的接口与文档:大牛直播SDK提供了简单易用的接口和丰富的文档,降低了开发门槛,使开发者能够快速上手并集成相关功能。这对于企业快速部署无纸化同屏解决方案具有重要意义,能够节省开发时间和成本。
五、实际应用案例
无纸化影音系统产品制造商,通过使用大牛直播的Windows、Android、iOS的RTMP推流、RTMP播放SDK,成功提升了其无纸化会议类产品的质量。这不仅体现了大牛直播SDK在技术上的优势,也展示了其在实际应用中的可靠性和稳定性。他们的成功案例为其他企业提供了宝贵的参考,推动了无纸化同屏技术在更多领域的应用。
六、技术实现
以大牛直播SDK的国产化Linux系统RTSP|RTMP播放器及时实现为例,以单个窗体播放为例,相关代码如下:
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());
}
}
结语
大牛直播SDK凭借其技术自主可控、跨平台兼容性、低延迟与高效传输、功能丰富与易用性以及数据安全与隐私保护等多方面的优势,成为了无纸化同屏行业的技术引领者。未来,随着技术的不断进步和创新,大牛直播SDK将继续为无纸化同屏行业的发展提供更强大的技术支持和解决方案。