背景

我们看过了太多介绍RTSP、RTMP播放相关的技术资料,大多接口设计简约,延迟和扩展能力也受到一定的局限,好多开发者希望我们能从接口设计的角度,大概介绍下大牛直播SDK关于RTMP、RTSP播放器开发设计,本文以Windows平台RTMP、RTSP播放模块为例,大概介绍下常用的接口。

接口设计

Windows平台我们是C接口,对外C++和C#均可正常调用,本文就以C++为例,大概介绍下常用的接口设计。

1. Init/UnInit()接口

Init和UnInit接口,在多个播放实例启动的时候,也仅需调用一次,做基础的初始化/反初始化操作。

/*
flag目前传0,后面扩展用, pReserve传NULL,扩展用,
成功返回 NT_ERC_OK
*/NT_UINT32(NT_API *Init)(NT_UINT32 flag, NT_PVOID pReserve);/*
这个是最后一个调用的接口
成功返回 NT_ERC_OK
*/NT_UINT32(NT_API *UnInit)();

2. Open/Close()接口

Open接口的目的,主要是创建实例,正常返回player实例句柄,如有多路播放诉求,创建多个实例即可。

Close接口,和Open()接口对应,负责释放相应实例的资源,调用Close()接口后,记得实例句柄置0。

注意:比如一个实例既可以实现播放,又可同时录像,亦或拉流(转发),这种情况下,调Close()接口时,需要确保录像、拉流都正常停止后,再调用。

/*
flag目前传0,后面扩展用, pReserve传NULL,扩展用,
NT_HWND hwnd, 绘制画面用的窗口, 可以设置为NULL
获取Handle
成功返回 NT_ERC_OK
*/NT_UINT32(NT_API *Open)(NT_PHANDLE pHandle, NT_HWND hwnd, NT_UINT32 flag, NT_PVOID pReserve);/*
调用这个接口之后handle失效,
成功返回 NT_ERC_OK
*/NT_UINT32(NT_API *Close)(NT_HANDLE handle);

3. 网络状态回调

一个好的播放器,好的状态回调必不可少,比如网络连通状态、快照、录像状态、当前下载速度等实时反馈,可以让上层开发者更好的掌控播放端状态,给用户更好的播放体验。

/*
设置事件回调,如果想监听事件的话,建议调用Open成功后,就调用这个接口
*/NT_UINT32(NT_API *SetEventCallBack)(NT_HANDLE handle,NT_PVOID call_back_data, NT_SP_SDKEventCallBack call_back);

demo实现实例:

LRESULT CSmartPlayerDlg::OnSDKEvent(WPARAM wParam, LPARAM lParam){if (!is_playing_ && !is_recording_){return S_OK;}NT_UINT32 event_id = (NT_UINT32)(wParam);if ( NT_SP_E_EVENT_ID_PLAYBACK_REACH_EOS == event_id ){StopPlayback();return S_OK;}elseif ( NT_SP_E_EVENT_ID_RECORDER_REACH_EOS == event_id ){StopRecorder();return S_OK;}elseif ( NT_SP_E_EVENT_ID_RTSP_STATUS_CODE == event_id ){int status_code = (int)lParam;if ( 401 == status_code ){HandleVerification();}return S_OK;}elseif (NT_SP_E_EVENT_ID_NEED_KEY == event_id){HandleKeyEvent(false);return S_OK;}elseif (NT_SP_E_EVENT_ID_KEY_ERROR == event_id){HandleKeyEvent(true);return S_OK;}elseif ( NT_SP_E_EVENT_ID_PULLSTREAM_REACH_EOS == event_id ){if (player_handle_ != NULL){player_api_.StopPullStream(player_handle_);}return S_OK;}elseif ( NT_SP_E_EVENT_ID_DURATION == event_id ){NT_INT64 duration = (NT_INT64)(lParam);edit_duration_.SetWindowTextW(GetHMSMsFormatStr(duration, false, false).c_str());return S_OK;}if ( NT_SP_E_EVENT_ID_CONNECTING == event_id|| NT_SP_E_EVENT_ID_CONNECTION_FAILED == event_id|| NT_SP_E_EVENT_ID_CONNECTED == event_id|| NT_SP_E_EVENT_ID_DISCONNECTED == event_id|| NT_SP_E_EVENT_ID_NO_MEDIADATA_RECEIVED == event_id){if ( NT_SP_E_EVENT_ID_CONNECTING == event_id ){OutputDebugStringA("connection status: connecting\r\n");}elseif ( NT_SP_E_EVENT_ID_CONNECTION_FAILED == event_id ){OutputDebugStringA("connection status: connection failed\r\n");}elseif ( NT_SP_E_EVENT_ID_CONNECTED == event_id ){OutputDebugStringA("connection status: connected\r\n");}elseif (NT_SP_E_EVENT_ID_DISCONNECTED == event_id){OutputDebugStringA("connection status: disconnected\r\n");}elseif (NT_SP_E_EVENT_ID_NO_MEDIADATA_RECEIVED == event_id){OutputDebugStringA("connection status: no mediadata received\r\n");}connection_status_ = event_id;}if ( NT_SP_E_EVENT_ID_START_BUFFERING == event_id|| NT_SP_E_EVENT_ID_BUFFERING == event_id|| NT_SP_E_EVENT_ID_STOP_BUFFERING == event_id ){buffer_status_ = event_id;if ( NT_SP_E_EVENT_ID_BUFFERING == event_id ){buffer_percent_ = (NT_INT32)lParam;std::wostringstream ss;ss << L"buffering:" << buffer_percent_ << "%";OutputDebugStringW(ss.str().c_str());OutputDebugStringW(L"\r\n");}}if ( NT_SP_E_EVENT_ID_DOWNLOAD_SPEED == event_id ){download_speed_ = (NT_INT32)lParam;/*std::wostringstream ss;ss << L"downloadspeed:" << download_speed_ << L"\r\n";OutputDebugStringW(ss.str().c_str());*/}CString show_str = base_title_;if ( connection_status_ != 0 ){show_str += _T("--链接状态: ");if ( NT_SP_E_EVENT_ID_CONNECTING == connection_status_ ){show_str += _T("链接中");}elseif ( NT_SP_E_EVENT_ID_CONNECTION_FAILED == connection_status_ ){show_str += _T("链接失败");}elseif ( NT_SP_E_EVENT_ID_CONNECTED == connection_status_ ){show_str += _T("链接成功");}elseif ( NT_SP_E_EVENT_ID_DISCONNECTED == connection_status_ ){show_str += _T("链接断开");}elseif (NT_SP_E_EVENT_ID_NO_MEDIADATA_RECEIVED == connection_status_){show_str += _T("收不到数据");}}if (download_speed_ != -1){std::wostringstream ss;ss << L"--下载速度:" << (download_speed_ * 8 / 1000) << "kbps"<< L"(" << (download_speed_ / 1024) << "KB/s)";show_str += ss.str().c_str();}if ( buffer_status_ != 0 ){show_str += _T("--缓冲状态: ");if ( NT_SP_E_EVENT_ID_START_BUFFERING == buffer_status_ ){show_str += _T("开始缓冲");}elseif (NT_SP_E_EVENT_ID_BUFFERING == buffer_status_){std::wostringstream ss;ss << L"缓冲中" << buffer_percent_ << "%";show_str += ss.str().c_str();}elseif (NT_SP_E_EVENT_ID_STOP_BUFFERING == buffer_status_){show_str += _T("结束缓冲");}}SetWindowText(show_str);return S_OK;
}

4. 软解码还是硬解码?

一般来说,Windows平台如果同时播放的实例不多或者分辨率不是太高的话,考虑到播放体验,建议优先考虑软解码,如果特定设备需要多路播放,也可以考虑硬解,需要注意的是,如果调用硬解码,需要先做是否支持硬解码检测,接口如下:

/*
检查是否支持H264硬解码
如果支持的话返回NT_ERC_OK
*/NT_UINT32(NT_API *IsSupportH264HardwareDecoder)();/*
检查是否支持H265硬解码
如果支持的话返回NT_ERC_OK
*/NT_UINT32(NT_API *IsSupportH265HardwareDecoder)();/*
*设置H264硬解
*is_hardware_decoder: 1:表示硬解, 0:表示不用硬解
*reserve: 保留参数, 当前传0就好
*成功返回NT_ERC_OK
*/NT_UINT32(NT_API *SetH264HardwareDecoder)(NT_HANDLE handle, NT_INT32 is_hardware_decoder, NT_INT32 reserve);/*
*设置H265硬解
*is_hardware_decoder: 1:表示硬解, 0:表示不用硬解
*reserve: 保留参数, 当前传0就好
*成功返回NT_ERC_OK
*/NT_UINT32(NT_API *SetH265HardwareDecoder)(NT_HANDLE handle, NT_INT32 is_hardware_decoder, NT_INT32 reserve);

5.只解关键帧

移动端,一般对只播放关键帧真正场景,需求不大,但是window端,好多场景下,因为需要播放非常多路,但是又不想占用太多的系统资源,如果全帧播放,路数过多,全部解码、绘制,系统资源占用会加大,如果能灵活的处理,可以随时只播放关键帧,全帧播放切换,对系统性能要求大幅降低,想全帧播放的时候,随时切换全帧绘制。

/*
*设置只解码视频关键帧
*is_only_dec_key_frame: 1:表示只解码关键帧, 0:表示都解码, 默认是0
*成功返回NT_ERC_OK
*/NT_UINT32(NT_API *SetOnlyDecodeVideoKeyFrame)(NT_HANDLE handle, NT_INT32 is_only_dec_key_frame);

6. 缓冲时间设置

缓冲时间,顾名思义,缓存多少数据才开始播放,比如设置2000ms的buffer time,直播模式下,收到2秒数据后,才正常播放。

加大buffer time,会增大播放延迟,好处是,网络抖动的时候,流畅性更好。

/*
设置buffer,最小0ms
*/NT_UINT32(NT_API *SetBuffer)(NT_HANDLE handle, NT_INT32 buffer);

7. 实时静音、实时音量调节

实时静音、实时音量调节顾名思义,播放端可以实时调整播放音量,或者直接静音掉,特别是多路播放场景下,非常有必要。

/*
静音接口,1为静音,0为不静音
*/NT_UINT32(NT_API *SetMute)(NT_HANDLE handle, NT_INT32 is_mute);/*
设置播放音量, 范围是[0, 100], 0是静音,100是最大音量, 默认是100
调用正确返回NT_ERC_OK
*/NT_UINT32(NT_API *SetAudioVolume)(NT_HANDLE handle, NT_INT32 volume);

8. RTSP TCP-UDP模式设置、超时时间设置或模式切换

有的RTSP服务器或摄像机,只支持RTSP TCP模式或者UDP模式,这个时候,默认设置TCP、UDP模式就至关重要,此外,我们还设计支持如TCP或UDP模式收不到数据,在超时时间后,可以自动切换到UDP或TCP。

/*
设置RTSP TCP 模式, 1为TCP, 0为UDP, 仅RTSP有效
*/NT_UINT32(NT_API* SetRTSPTcpMode)(NT_HANDLE handle, NT_INT32 isUsingTCP);/*
设置RTSP超时时间, timeout单位为秒,必须大于0
*/NT_UINT32 (NT_API* SetRtspTimeout)(NT_HANDLE handle, NT_INT32 timeout);/*
对于RTSP来说,有些可能支持rtp over udp方式,有些可能支持使用rtp over tcp方式.
为了方便使用,有些场景下可以开启自动尝试切换开关, 打开后如果udp无法播放,sdk会自动尝试tcp, 如果tcp方式播放不了,sdk会自动尝试udp.
is_auto_switch_tcp_udp: 如果设置1的话, sdk将在tcp和udp之间尝试切换播放,如果设置为0,则不尝试切换.
*/NT_UINT32 (NT_API* SetRtspAutoSwitchTcpUdp)(NT_HANDLE handle, NT_INT32 is_auto_switch_tcp_udp);

9. 快速启动

快速启动,主要是针对服务器缓存GOP的场景下,快速刷到最新的数据,确保画面的持续性。

/*
设置秒开, 1为秒开, 0为不秒开
*/NT_UINT32(NT_API* SetFastStartup)(NT_HANDLE handle, NT_INT32 isFastStartup);

10. 低延迟模式

低延迟模式下,设置buffer time为0,延迟更低,适用于比如需要操控控制的超低延迟场景下。

/*
设置低延时播放模式,默认是正常播放模式
mode: 1为低延时模式, 0为正常模式,其他只无效
接口调用成功返回NT_ERC_OK
*/NT_UINT32(NT_API* SetLowLatencyMode)(NT_HANDLE handle, NT_INT32 mode);

11. 视频view旋转、水平|垂直翻转

接口主要用于,比如原始的视频倒置等场景下,设备端无法调整时,通过播放端完成图像的正常角度播放。

/*
*上下反转(垂直反转)
*is_flip: 1:表示反转, 0:表示不反转
*/NT_UINT32(NT_API *SetFlipVertical)(NT_HANDLE handle, NT_INT32 is_flip);/*
*水平反转
*is_flip: 1:表示反转, 0:表示不反转
*/NT_UINT32(NT_API *SetFlipHorizontal)(NT_HANDLE handle, NT_INT32 is_flip);/*
设置旋转,顺时针旋转
degress: 设置0, 90, 180, 270度有效,其他值无效
注意:除了0度,其他角度播放会耗费更多CPU
接口调用成功返回NT_ERC_OK
*/NT_UINT32(NT_API* SetRotation)(NT_HANDLE handle, NT_INT32 degress);

12. 设置实时回调下载速度

调用实时下载速度接口,通过设置下载速度时间间隔,和是否需要上报当前下载速度,实现APP层和底层SDK更友好的交互。

/*
设置下载速度上报, 默认不上报下载速度
is_report: 上报开关, 1: 表上报. 0: 表示不上报. 其他值无效.
report_interval: 上报时间间隔(上报频率),单位是秒,最小值是1秒1次. 如果小于1且设置了上报,将调用失败
注意:如果设置上报的话,请设置SetEventCallBack, 然后在回调函数里面处理这个事件.
上报事件是:NT_SP_E_EVENT_ID_DOWNLOAD_SPEED
这个接口必须在StartXXX之前调用
成功返回NT_ERC_OK
*/NT_UINT32(NT_API *SetReportDownloadSpeed)(NT_HANDLE handle,
NT_INT32 is_report, NT_INT32 report_interval);/*
主动获取下载速度
speed: 返回下载速度,单位是Byte/s
(注意:这个接口必须在startXXX之后调用,否则会失败)
成功返回NT_ERC_OK
*/NT_UINT32(NT_API *GetDownloadSpeed)(NT_HANDLE handle, NT_INT32* speed);

13. 实时快照

简单来说,播放过程中,是不是要存取当前的播放画面。

/*
捕获图片
file_name_utf8: 文件名称,utf8编码
call_back_data: 回调时用户自定义数据
call_back: 回调函数,用来通知用户截图已经完成或者失败
成功返回 NT_ERC_OK
只有在播放时调用才可能成功,其他情况下调用,返回错误.
因为生成PNG文件比较耗时,一般需要几百毫秒,为防止CPU过高,SDK会限制截图请求数量,当超过一定数量时,
调用这个接口会返回NT_ERC_SP_TOO_MANY_CAPTURE_IMAGE_REQUESTS. 这种情况下, 请延时一段时间,等SDK处理掉一些请求后,再尝试.
*/NT_UINT32(NT_API* CaptureImage)(NT_HANDLE handle, NT_PCSTR file_name_utf8,
NT_PVOID call_back_data, SP_SDKCaptureImageCallBack call_back);

调用实例如下:

voidCSmartPlayerDlg::OnBnClickedButtonCaptureImage(){if ( capture_image_path_.empty() ){AfxMessageBox(_T("请先设置保存截图文件的目录! 点击截图左边的按钮设置!"));return;}if ( player_handle_ == NULL ){return;}if ( !is_playing_ ){return;}std::wostringstream ss;ss << capture_image_path_;if ( capture_image_path_.back() != L'\\' ){ss << L"\\";}SYSTEMTIME sysTime;::GetLocalTime(&sysTime);ss << L"SmartPlayer-"<< std::setfill(L'0') << std::setw(4) << sysTime.wYear<< std::setfill(L'0') << std::setw(2) << sysTime.wMonth<< std::setfill(L'0') << std::setw(2) << sysTime.wDay<< L"-"<< std::setfill(L'0') << std::setw(2) << sysTime.wHour<< std::setfill(L'0') << std::setw(2) << sysTime.wMinute<< std::setfill(L'0') << std::setw(2) << sysTime.wSecond;ss << L"-" << std::setfill(L'0') << std::setw(3) << sysTime.wMilliseconds<< L".png";std::wstring_convert<std::codecvt_utf8<wchar_t> > conv;auto val_str = conv.to_bytes(ss.str());auto ret = player_api_.CaptureImage(player_handle_, val_str.c_str(), NULL, &SM_SDKCaptureImageHandle);if (NT_ERC_OK == ret){// 发送截图请求成功}elseif (NT_ERC_SP_TOO_MANY_CAPTURE_IMAGE_REQUESTS == ret){// 通知用户延时OutputDebugStringA("Too many capture image requests!!!\r\n");}else{// 其他失败}
}

14. 扩展录像操作

播放端录像,我们做的非常细化,比如可以只录制音频或者只录制视频,设置录像存储路径,设置单个文件size,如果非AAC数据,可以转AAC后再录像。

/*
* 设置是否录视频,默认的话,如果视频源有视频就录,没有就没得录, 但有些场景下可能不想录制视频,只想录音频,所以增加个开关
* is_record_video: 1 表示录制视频, 0 表示不录制视频, 默认是1
*/NT_UINT32(NT_API *SetRecorderVideo)(NT_HANDLE handle, NT_INT32 is_record_video);/*
* 设置是否录音频,默认的话,如果视频源有音频就录,没有就没得录, 但有些场景下可能不想录制音频,只想录视频,所以增加个开关
* is_record_audio: 1 表示录制音频, 0 表示不录制音频, 默认是1
*/NT_UINT32(NT_API *SetRecorderAudio)(NT_HANDLE handle, NT_INT32 is_record_audio);/*
设置本地录像目录, 必须是英文目录,否则会失败
*/NT_UINT32(NT_API *SetRecorderDirectory)(NT_HANDLE handle, NT_PCSTR dir);/*
设置单个录像文件最大大小, 当超过这个值的时候,将切割成第二个文件
size: 单位是KB(1024Byte), 当前范围是 [5MB-800MB], 超出将被设置到范围内
*/NT_UINT32(NT_API *SetRecorderFileMaxSize)(NT_HANDLE handle, NT_UINT32 size);/*
设置录像文件名生成规则
*/NT_UINT32(NT_API *SetRecorderFileNameRuler)(NT_HANDLE handle, NT_SP_RecorderFileNameRuler* ruler);/*
设置录像回调接口
*/NT_UINT32(NT_API *SetRecorderCallBack)(NT_HANDLE handle,
NT_PVOID call_back_data, SP_SDKRecorderCallBack call_back);/*
设置录像时音频转AAC编码的开关, aac比较通用,sdk增加其他音频编码(比如speex, pcmu, pcma等)转aac的功能.
is_transcode: 设置为1的话,如果音频编码不是aac,则转成aac, 如果是aac,则不做转换. 设置为0的话,则不做任何转换. 默认是0.
注意: 转码会增加性能消耗
*/NT_UINT32(NT_API *SetRecorderAudioTranscodeAAC)(NT_HANDLE handle, NT_INT32 is_transcode);/*
启动录像
*/NT_UINT32(NT_API *StartRecorder)(NT_HANDLE handle);/*
停止录像
*/NT_UINT32(NT_API *StopRecorder)(NT_HANDLE handle);

15. 拉流回调编码后的数据(配合转发模块使用)

拉流回调编码后的数据,主要是为了配合转发模块使用,比如拉取rtsp流数据,直接转RTMP推送到RTMP服务。

/*
* 设置拉流时,吐视频数据的回调
*/NT_UINT32(NT_API *SetPullStreamVideoDataCallBack)(NT_HANDLE handle,
NT_PVOID call_back_data, SP_SDKPullStreamVideoDataCallBack call_back);/*
* 设置拉流时,吐音频数据的回调
*/NT_UINT32(NT_API *SetPullStreamAudioDataCallBack)(NT_HANDLE handle,
NT_PVOID call_back_data, SP_SDKPullStreamAudioDataCallBack call_back);/*
设置拉流时音频转AAC编码的开关, aac比较通用,sdk增加其他音频编码(比如speex, pcmu, pcma等)转aac的功能.
is_transcode: 设置为1的话,如果音频编码不是aac,则转成aac, 如果是aac,则不做转换. 设置为0的话,则不做任何转换. 默认是0.
注意: 转码会增加性能消耗
*/NT_UINT32(NT_API *SetPullStreamAudioTranscodeAAC)(NT_HANDLE handle, NT_INT32 is_transcode);/*
启动拉流
*/NT_UINT32(NT_API *StartPullStream)(NT_HANDLE handle);/*
停止拉流
*/NT_UINT32(NT_API *StopPullStream)(NT_HANDLE handle);

16. H264用户数据回调或SEI数据回调

如发送端在264编码时,加了自定义的user data数据,可以通过以下接口实现数据回调,如需直接回调SEI数据,调下面SEI回调接口即可。

/*
设置用户数据回调
*/NT_UINT32(NT_API *SetUserDataCallBack)(NT_HANDLE handle,
NT_PVOID call_back_data, NT_SP_SDKUserDataCallBack call_back);

调用实例如下:

extern"C"NT_VOID NT_CALLBACK NT_SP_SDKUserDataHandle(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){if ( 1 == data_type ){std::wostringstream oss;oss << L"userdata ";const NT_BYTE* byte_data = reinterpret_cast<const NT_BYTE*>(data);if ( byte_data != nullptr && size > 0 ){oss << L" byte data size=" << size;}std::wstring_convert<std::codecvt_utf8<wchar_t> > conv;oss << L" t:" << timestamp << L"\r\n";OutputDebugStringW(oss.str().c_str());}elseif ( 2 == data_type ){const NT_CHAR* str_data = reinterpret_cast<const NT_CHAR*>(data);if (str_data != nullptr && size > 0){std::unique_ptr<std::string> s(new std::string(str_data, str_data + size));// oss << L" utf8 string:" << conv.from_bytes(*s);// oss << L" size=" << size;if ( !s->empty() ){HWND hwnd = reinterpret_cast<HWND>(user_data);if ( hwnd != nullptr && ::IsWindow(hwnd) ){::PostMessage(hwnd, WM_USER_SDK_SP_RECV_USER_DATA, (WPARAM)s.release(), (LPARAM)timestamp);}}}}}

17. 设置回调解码后YUV、RGB数据

如需对解码后的yuv或rgb数据,进行二次处理,如人脸识别等,可以通回调yuv rgb接口实现数据二次处理,对于Windows平台来说,如果设备不支持D3D,也可以数据回调上来GDI模式绘制:

player_api_.SetVideoFrameCallBack(player_handle_, NT_SP_E_VIDEO_FRAME_FORMAT_RGB32,
GetSafeHwnd(), SM_SDKVideoFrameHandle);extern"C"NT_VOID NT_CALLBACK SM_SDKVideoFrameHandle(NT_HANDLE handle, NT_PVOID userData, NT_UINT32 status,const NT_SP_VideoFrame* frame){/*if (frame != NULL){std::ostringstream ss;ss << "Receive frame time_stamp:" << frame->timestamp_ << "ms" << "\r\n";OutputDebugStringA(ss.str().c_str());}*/if ( frame != NULL ){if ( NT_SP_E_VIDEO_FRAME_FORMAT_RGB32 == frame->format_&& frame->plane0_ != NULL&& frame->stride0_ > 0&& frame->height_ > 0 ){std::unique_ptr<nt_rgb32_image > pImage(new nt_rgb32_image());pImage->size_ = frame->stride0_* frame->height_;pImage->data_ = new NT_BYTE[pImage->size_];memcpy(pImage->data_, frame->plane0_, pImage->size_);pImage->width_  = frame->width_;pImage->height_ = frame->height_;pImage->stride_ = frame->stride0_;HWND hwnd = (HWND)userData;if ( hwnd != NULL && ::IsWindow(hwnd) ){::PostMessage(hwnd, WM_USER_SDK_RGB32_IMAGE, (WPARAM)handle, (LPARAM)pImage.release());}}}
}

18. 设置视频画面填充模式

设置视频画面的填充模式,如填充整个view、等比例填充view,如不设置,默认填充整个view。

相关接口设计如下:

player_api_.SetRenderScaleMode(player_handle_, btn_check_render_scale_mode_.GetCheck() == BST_CHECKED ? 1 : 0);

总结

以上就是大牛直播SDK(官网)关于Windows平台RTSP、RTMP播放器接口设计需要参考的点,其他还有些,比如如果不支持D3D,GDI模式绘制,播放界面叠加实时文字,播放画面全屏等,这里就不再赘述,除Windows平台外,我们还同步开发了Linux、Android、iOS平台的RTSP、RTMP播放器,大多常规接口四个平台基本统一,延迟也都做到了毫秒级。对于大多数开发者来说,不一定需要实现上述所有部分,只要按照产品诉求,实现其中的30-40%就足够满足特定场景使用了。

一个好的播放器,特别是要满足低延迟稳定的播放(毫秒级延迟),需要注意的点远不止如此,感兴趣的开发者,可以参考blog其他文章。

【技术分享】Windows平台低延迟RTMP、RTSP播放器接口设计探讨相关推荐

  1. RTMP/RTSP推送端和RTMP/RTSP播放端录像设计探讨

    好多开发者认为,无论是RTSP/RTMP推送端还是RTSP/RTMP播放端,涉及到录像,只要2个接口足矣:开始录像.停止录像. 实际场景下,一个好的录像模块,2个接口远远不够, 本文以大牛直播SDK( ...

  2. Windows平台RTMP|RTSP播放器为什么要兼容GDI绘制

    为什么要支持GDI 先说结论,Windows平台播放渲染这块,一般来说99%以上的机器都是支持D3D的,实现GDI模式绘制,除了为了好的兼容性外,在远程连接的场景下,D3D创建不成功,需要使用GDI模 ...

  3. Android平台RTMP/RTSP播放器开发系列之解码和绘制

    本文主要抛砖引玉,粗略介绍下Android平台RTMP/RTSP播放器中解码和绘制相关的部分(Github). 解码 提到解码,大家都知道软硬解,甚至一些公司觉得硬解码已经足够通用,慢慢抛弃软解了,如 ...

  4. android 直播流服务器,视频-Android上的实时流RTMP / RTSP播放器,无需使用webview(WOWZA服务器)...

    视频-Android上的实时流RTMP / RTSP播放器,无需使用webview(WOWZA服务器) 我正在开发一个Android应用程序,我想在其中发布和播放视频... 我想要的是: 我的应用记录 ...

  5. Windows平台RTMP/RTSP播放器如何实现实时音量调节

    为什么要做实时音量调节 RTMP或RTSP直播播放音量调节,主要用于多实例(多窗口)播放场景下,比如同时播放4路RTMP或RTSP流,如果音频全部打开,几路audio同时打开,可能会影响用户体验,我们 ...

  6. Android、iOS平台RTMP/RTSP播放器实现实时音量调节

    介绍移动端RTMP.RTSP播放器实时音量调节之前,我们之前也写过,为什么windows播放端加这样的接口,windows端播放器在多窗口大屏显示的场景下尤其需要,尽管我们老早就有了实时静音接口,相对 ...

  7. Web直播送走flash,迎来直播,h5全平台低延时,webFlv播放器方案(适用RTSP监控,RTMP直播)

    视频直播从国内兴起就是RTMP协议主导,Adobe开发协议,Web-Flash播放器绑定协议,方便web接入,便于业务集成,一直是行业标准,随移动兴起,Flash插件方式日益退出舞台,怎么样在Web上 ...

  8. Windows平台RTMP|RTSP播放器实现画面全屏功能

    我们在Windows平台实现RTSP或者RTMP播放的时候,有个功能是绕不开的,那就是播放窗口全屏.本文就以大牛直播SDK(官方)的Windows播放器为例,大概讲下大概实现: 全屏播放需要考虑的点不 ...

  9. rtmp/rtsp播放器

    相信大家在做rtmp.rtsp直播的时候,最大的困惑就是选个靠谱的播放器,直播的延迟,一定意义上说,90%的取决于播放器的好坏. 那啥样的播放器才是靠谱的播放器呢?笔者以为,稳定.低延迟.支持路数多, ...

最新文章

  1. 搜狗浏览器验证码无法显示_逃离塔科夫账号注册-验证码不显示
  2. python高效办公_Python高效办公|自动分发任务
  3. 把服务器文件,如何把文件放在服务器
  4. 基于VUE+TS中引用ECharts的中国地图和世界地图密度表
  5. 麦克纳姆轮运动特性分析(图片版)
  6. 【语音合成】基于matlab GUI语音合成【含Matlab源码 293期】
  7. CorelDRAWX4的VBA插件开发(二十)创建进度条来显示程序运行进度
  8. IEC61850简要介绍
  9. matlab读取图片排序1 10 2出错解决方法/图片批量重命名
  10. 人工智能的认知技术,主要包含哪些?
  11. 前端:标准删除确认提示
  12. python编译 pyd 工具_windows平台 python生成 pyd文件
  13. 数据科学 IPython 笔记本 9.8 比较,掩码和布尔逻辑
  14. 浅谈对Vue框架中MVVM的认识
  15. 类和对象:类与对象定义
  16. Bootstrap学习(六)响应式嵌入、well、分页符、label、徽章、巨幕、警告框、进度条
  17. 七种数学亲子智能提升游戏
  18. 【云原生】设备入云之FStudio 2.0通讯操作经验
  19. 【文献阅读】Fixed-Point-GAN,不动点GAN(2019,ICCV)
  20. c语言程序设计 徐庆生,C语言中循环结构程序课的教学设计与探讨.doc

热门文章

  1. HMS Core 成长相伴,为孩子“戴”来安全的六一节礼物
  2. python从零学习:商品列表及购物清单
  3. WeChat_微信小程序跳一跳辅助
  4. 面试总结:新浪微博视频0914
  5. 《Java技术及应用》课程标准
  6. 【AI】Genetic Algorithm 遗传算法
  7. 算法复杂度下界证明---对手论证(adversary argument)基础篇
  8. mysql+远程连接1130_Navicat for MySQL远程连接ERROR 1130
  9. 【CANN训练营】新手班结业考核
  10. 你的双十一购物槽点,都在这期脱口秀大会里了