首先,虽然本文是介绍IE浏览器下OCX控件播放RTSP或RTMP,但这种方式并不推荐,毕竟它只能用于IE浏览器环境下,局限太大,而且随着微软IE浏览器的更新,不确定后续支持情况。当然,话说回来,如果是在特定的使用场景下,只需要某些版本IE浏览器支持,但对延迟和稳定性要求非常高,OCX控件方式也不失为一个好的选择。
如果需要浏览器场景下有更好的兼容性,对于RTSP流来说,好多公司通常的做法是把RTSP转RTMP,然后分发到RTMP服务器,然后服务器转http-flv出来,浏览器直接播放http-flv流,或者直接播放hls流(如果可以忍受几秒甚至十几秒延迟的话)。
本文基于大牛直播SDK https://github.com/daniulive/SmarterStreaming 现有RTSP、RTMP播放接口的基础上,二次封装,扩展了ocx控件,用于IE浏览器下的低延迟RTMP或RTSP播放,支持RTMP/RTSP H.265(hevc)播放。
页面展示
图一:
图二:
设计注意事项:
1. 接口透传,不再赘述,注意数据类型即可;
2. Event回调传递,特别是多窗口多实例播放模式下,需要区分处理不同实例回调上来的event(如分辨率、实时码率、网络状态等);
3. OCX控件正常注册、反注册(运行网页播放端之前,请确保以管理员权限注册ocx控件:regplayerocx.bat右键-->“以管理员身份运行(A)”,同理,反注册也是需要管理员身份);
4. 浏览器模式下,如滚动条拖动,避免因此带来的闪烁;
5. 等比例绘制,这块最好做到底层SDK里面去,要么,比较笨的办法是,底层回调上来视频宽高信息,上层根据比例二次处理;
6. 窗口显示模式和全屏模式切换等。
对应封装接口
ULONG NT_Open(); ULONG NT_Close(); ULONG NT_StartPlay(); ULONG NT_StopPlay(); ULONG NT_SetMute(LONG is_mute); ULONG NT_SetURL(LPCTSTR url); ULONG NT_SetBuffer(LONG buffer); ULONG NT_SetRTSPTcpMode(LONG isUsingTCP); ULONG NT_SetRtspTimeout(LONG timeout); ULONG NT_SetRtspAutoSwitchTcpUdp(LONG is_auto_switch_tcp_udp); ULONG NT_SetFastStartup(LONG isFastStartup); ULONG NT_SetVideoHardwareDecoder(LONG isVideoHardwareDecoder); ULONG NT_SetLowLatencyMode(LONG mode); ULONG NT_SetFlipVertical(LONG is_flip); ULONG NT_SetFlipHorizontal(LONG is_flip); ULONG NT_SetRotation(LONG degress); ULONG NT_SwitchURL(LPCTSTR url); ULONG NT_SetCaptureImagePath(LPCTSTR path); ULONG NT_CaptureImage(); ULONG NT_SetRecorderDirectory(LPCTSTR dir); ULONG NT_SetRecorderFileMaxSize(ULONG size); ULONG NT_NT_SP_RecorderFileNameRuler(ULONG type, LPCTSTR file_name_prefix, LONG append_date, LONG append_time); ULONG NT_SetRecorderAudioTranscodeAAC(LONG is_transcode); ULONG NT_SetRecorderVideo(LONG is_record_video); ULONG NT_SetRecorderAudio(LONG is_record_audio); ULONG NT_StartRecorder(); ULONG NT_StopRecorder(); ULONG NT_FullScreen(); void OnSDKEventReceived(BSTR object_id, ULONG event_id, ULONG param1); void OnVideoSizeReceived(ULONG width, ULONG height); ULONG NT_SetLogPath(LPCTSTR log_path); ULONG NT_SetObjectID(LPCTSTR obj_id_str); ULONG NT_SetSDKClientKey(LPCTSTR cid, LPCTSTR key); ULONG NT_SetAudioVolume(LONG audio_volume); ULONG NT_SetRenderScaleMode(LONG mode);
设置LOG存放路径:
ULONG CSmartPlayerActiveXCtrl::NT_SetLogPath(LPCTSTR log_path)
请于NT_Open() 之前调用,代码示例:
var obj = document.getElementById("SmartPlayerActiveX"); //如需记录log文件,请确保log路径存在, 如多级目录, 可按照"D:\\Daniulive\\log"类似格式设定, 记录文件名: smart_sdk.log obj.NT_SetLogPath("D:\\");
接口说明:
1. ULONG NT_Open();
打开player实例;
2. ULONG NT_Close();
关闭player实例;
3. ULONG NT_StartPlay();
开始播放;
4. ULONG NT_StopPlay();
停止播放;
5. ULONG NT_SetMute(LONG is_mute);
设置实时静音;
6. ULONG NT_SetURL(LPCTSTR url);
设置播放的RTMP或RTSP url;
7. ULONG NT_SetBuffer(LONG buffer);
设置buffer time,缓冲时间,单位:毫秒;
8. ULONG NT_SetRTSPTcpMode(LONG isUsingTCP);
设置RTSP TCP/UDP播放模式;
9. ULONG NT_SetRtspTimeout(LONG timeout);
设置RTSP超时时间;
10. ULONG NT_SetRtspAutoSwitchTcpUdp(LONG is_auto_switch_tcp_udp);
设置是否自动切换TCP/UDP模式;
11. ULONG NT_SetFastStartup(LONG isFastStartup);
设置是否快速启动;
12. ULONG NT_SetLowLatencyMode(LONG mode);
设置是否低延迟模式播放;
13. ULONG NT_SetFlipVertical(LONG is_flip);
设置垂直反转模式图像;
14. ULONG NT_SetFlipHorizontal(LONG is_flip);
设置水平反转图像;
15. ULONG NT_SetRotation(LONG degress);
设置旋转图像,可设定角度:0度 90度 180度 270度;
16. ULONG NT_SwitchURL(LPCTSTR url);
设置快速切换RTSP/RTMP url;
17. ULONG NT_SetCaptureImagePath(LPCTSTR path);
设置快照保存位置;
18. ULONG NT_CaptureImage();
设置实时快照功能;
19. ULONG NT_SetRecorderDirectory(LPCTSTR dir);
设置录像保存位置;
20. ULONG NT_SetRecorderFileMaxSize(ULONG size);
设置单个录像文件最大size,单位:兆;
21. ULONG NT_NT_SP_RecorderFileNameRuler(ULONG type, LPCTSTR file_name_prefix, LONG append_date, LONG append_time);
设置录像文件命名规则:是否需要前缀、是否添加日期、是否添加时间;
22. ULONG NT_SetRecorderAudioTranscodeAAC(LONG is_transcode);
设置录像音频文件是否转AAC后录制,支持PCMA/PCMU/SPEEX转AAC后录制文件;
23. ULONG NT_SetRecorderVideo(LONG is_record_video);
设置是否录制视频;
24. ULONG NT_SetRecorderAudio(LONG is_record_audio);
设置是否录制音频;
25. ULONG NT_StartRecorder();
开始录像;
26. ULONG NT_StopRecorder();
停止录像;
27. ULONG NT_FullScreen();
全屏显示窗口。
28. ULONG NT_SetObjectID();
设置Object ID,用于区分不同实例。
29. ULONG NT_SetSDKClientKey();
设置license key,用于授权。
30. ULONG NT_SetAudioVolume();
播放端实时音量调节。
31. ULONG NT_SetRenderScaleMode();
设置是否等比例显示窗体。
事件Event:
1. void OnSDKEventReceived;
回调网络状态、buffering状态、下载速度等;
事件类型:
<script> var NT_EVENT_ID_SMART_PLAYER_SDK = 0x01000000; var NT_SP_E_EVENT_ID_BASE = NT_EVENT_ID_SMART_PLAYER_SDK; var NT_SP_E_EVENT_ID_CONNECTING = NT_SP_E_EVENT_ID_BASE | 0x2; /*连接中*/ var NT_SP_E_EVENT_ID_CONNECTION_FAILED = NT_SP_E_EVENT_ID_BASE | 0x3; /*连接失败*/ var NT_SP_E_EVENT_ID_CONNECTED = NT_SP_E_EVENT_ID_BASE | 0x4; /*已连接*/ var NT_SP_E_EVENT_ID_DISCONNECTED = NT_SP_E_EVENT_ID_BASE | 0x5; /*断开连接*/ var NT_SP_E_EVENT_ID_NO_MEDIADATA_RECEIVED = NT_SP_E_EVENT_ID_BASE | 0x8; /*收不到RTMP数据*/ var NT_SP_E_EVENT_ID_RTSP_STATUS_CODE = NT_SP_E_EVENT_ID_BASE | 0xB; /*rtsp status code上报, 目前只上报401, param1表示status code*/ /* 接下来请从0x81开始*/ var NT_SP_E_EVENT_ID_START_BUFFERING = NT_SP_E_EVENT_ID_BASE | 0x81; /*开始缓冲*/ var NT_SP_E_EVENT_ID_BUFFERING = NT_SP_E_EVENT_ID_BASE | 0x82; /*缓冲中, param1 表示百分比进度*/ var NT_SP_E_EVENT_ID_STOP_BUFFERING = NT_SP_E_EVENT_ID_BASE | 0x83; /*停止缓冲*/ var NT_SP_E_EVENT_ID_DOWNLOAD_SPEED = NT_SP_E_EVENT_ID_BASE | 0x91; /*下载速度, param1表示下载速度,单位是(Byte/s)*/ var NT_SP_E_EVENT_ID_PLAYBACK_REACH_EOS = NT_SP_E_EVENT_ID_BASE | 0xa1; /*播放结束, 直播流没有这个事件,点播流才有*/ var NT_SP_E_EVENT_ID_RECORDER_REACH_EOS = NT_SP_E_EVENT_ID_BASE | 0xa2; /*录像结束, 直播流没有这个事件, 点播流才有*/ var NT_SP_E_EVENT_ID_PULLSTREAM_REACH_EOS = NT_SP_E_EVENT_ID_BASE | 0xa3; /*拉流结束, 直播流没有这个事件,点播流才有*/ var NT_SP_E_EVENT_ID_DURATION = NT_SP_E_EVENT_ID_BASE | 0xa8; /*视频时长,如果是直播,则不上报,如果是点播的话, 若能从视频源获取视频时长的话,则上报, param1表示视频时长,单位是毫秒(ms)*/ </script>
调用展示:
<script language='javascript' for="SmartPlayerActiveX" event="OnSDKEventReceived(object_id, event_id, param1)"> // Test 1 - statically load the script (This is the basis for the hack) // Works on IE8, IE9, IE10 and IE11 var show_str = ""; var connection_status = event_id; if (connection_status != 0) { show_str += "链接状态: "; if (NT_SP_E_EVENT_ID_CONNECTING == connection_status) { show_str += "链接中"; } else if (NT_SP_E_EVENT_ID_CONNECTION_FAILED == connection_status) { show_str += "链接失败"; } else if (NT_SP_E_EVENT_ID_CONNECTED == connection_status) { show_str += "链接成功"; } else if (NT_SP_E_EVENT_ID_DISCONNECTED == connection_status) { show_str += "链接断开"; } else if (NT_SP_E_EVENT_ID_NO_MEDIADATA_RECEIVED == connection_status) { show_str += "收不到数据"; } } var download_speed = -1; if (NT_SP_E_EVENT_ID_DOWNLOAD_SPEED == event_id) { download_speed = param1; } if (download_speed != -1) { show_str += "下载速度:" + (download_speed * 8 / 1000).toFixed(0) + "kbps " + (download_speed / 1024).toFixed(0) + "KB/s"; } var buffer_status = 0; 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 (buffer_status != 0) { show_str += "缓冲状态: "; if (NT_SP_E_EVENT_ID_START_BUFFERING == buffer_status) { show_str += "开始缓冲"; } else if (NT_SP_E_EVENT_ID_BUFFERING == buffer_status) { show_str += "缓冲中" + param1 + "%"; } else if (NT_SP_E_EVENT_ID_STOP_BUFFERING == buffer_status) { show_str += "结束缓冲"; } } var EventMsgText = document.getElementById("EventMsg"); EventMsgText.innerHTML = show_str; </script>
2. void OnVideoSizeReceived(ULONG width, ULONG height);
回调视频宽高信息。
调用展示:
<script language='javascript' for="SmartPlayerActiveX" event="OnVideoSizeReceived(width, height)"> // Test 1 - statically load the script (This is the basis for the hack) // Works on IE8, IE9, IE10 and IE11 var VideoResolutionText = document.getElementById("VideoResolution"); VideoResolutionText.innerHTML = width + "*" + height; </script>
SDK接口调用实例:
播放和录像调用示例:
function OpenPlayer() { if(is_player_opened) { return; } var obj = document.getElementById("SmartPlayerActiveX"); //如需记录log文件,请确保log路径存在, 如多级目录, 可按照"D:\\Daniulive\\log"类似格式设定, 记录文件名: smart_sdk.log obj.NT_SetLogPath("D:\\"); obj.NT_SetObjectID("SmartPlayerActiveX"); var ret = obj.NT_Open(); if(ret == 0) { //设置TCP/UDP模式 var rtsp_tcp_mode = document.getElementById("rtspTcpMode").checked ? 1 : 0; obj.NT_SetRTSPTcpMode(rtsp_tcp_mode); //设置RTSP超时时间 var rtsp_timeout = document.getElementById("rtspTimeout").value; obj.NT_SetRtspTimeout(rtsp_timeout); //设置是否自动切换TCP-UDP模式 var rtsp_auto_switch_tcp_udp = document.getElementById("rtspAutoSwitchTcpUdp").checked ? 1 : 0; obj.NT_SetRtspAutoSwitchTcpUdp(rtsp_auto_switch_tcp_udp); //设置是否快速启动 var fast_startup_mode = document.getElementById("fastStartupMode").checked ? 1 : 0; obj.NT_SetFastStartup(fast_startup_mode); //设置是否硬解码 var is_video_hardware_decoder = document.getElementById("videoHardwareDecoder").checked ? 1 : 0; obj.NT_SetVideoHardwareDecoder(is_video_hardware_decoder); //设置需要播放或录像的RTSP/RTMP url var url = document.getElementById("playorReocordUrl").value; obj.NT_SetURL(url); //设置实时截图路径(可自行设置或选取系统存在的文件夹), 如多级目录可按照"D:\\Daniulive\\image"类似格式设定 var image_path = "D:\\"; obj.NT_SetCaptureImagePath(image_path); is_player_opened = true; } } function ClosePlayer() { if(is_player_opened) { var obj = document.getElementById("SmartPlayerActiveX"); obj.NT_Close(); var EventMsgText = document.getElementById("EventMsg"); EventMsgText.innerHTML = ""; is_player_opened = false; } } function OnBnClickedPlay() { if(!isIE()) { alert("非IE浏览器,请用IE打开播放控件.."); return; } if(!isActiveXInstalled()) { alert("控件未加载,请先加载控件.."); return; } if(is_playing) { StopPlayback(); } else { StartPlayback(); } } //开始播放 function StartPlayback() { if(!is_playing && !is_recording) { OpenPlayer(); } var obj = document.getElementById("SmartPlayerActiveX"); //设置是否启用低延迟模式 var low_latency_mode = document.getElementById("lowlatencyMode").checked ? 1 : 0; obj.NT_SetLowLatencyMode(low_latency_mode); //设置缓冲时间 var buffer_time = document.getElementById("bufferTime").value; obj.NT_SetBuffer(buffer_time); //设置播放音量, 范围是[0, 100], 0是静音,100是最大音量, 默认是100 //var audio_volume = 0; //obj.NT_SetAudioVolume(audio_volume); //设置视频画面的填充模式,如填充整个绘制窗口、等比例填充绘制窗口,如不设置,默认填充整个绘制窗口 //mode: 0: 填充整个绘制窗口; 1: 等比例填充绘制窗口, 默认值是0 var render_scale_mode = 1; obj.NT_SetRenderScaleMode(render_scale_mode); var ret = obj.NT_StartPlay(); if(ret == 0) { is_playing = true; var playBtnText = document.getElementById("playBtn"); playBtnText.innerHTML = "停止播放"; } } //停止播放 function StopPlayback() { if(!is_playing) { return; } var obj = document.getElementById("SmartPlayerActiveX"); obj.NT_StopPlay(); is_playing = false; var playBtnText = document.getElementById("playBtn"); playBtnText.innerHTML = "开始播放"; if(!is_recording) { ClosePlayer(); } var VideoResolutionText = document.getElementById("VideoResolution"); VideoResolutionText.innerHTML = ""; } function OnBnClickedRecord() { if(!isIE()) { alert("非IE浏览器,请用IE打开播放控件.."); return; } if(!isActiveXInstalled()) { alert("控件未加载,请先加载控件.."); return; } if(is_recording) { StopRecorder(); } else { StartRecorder(); } } //开始录像 function StartRecorder() { if(!is_playing && !is_recording) { OpenPlayer(); } var obj = document.getElementById("SmartPlayerActiveX"); //设置实时录像存放路径(可自行设置或选取系统存在的文件夹), 如多级目录可按照"D:\\Daniulive\\rec"类似格式设定 var rec_dir = "D:\\"; obj.NT_SetRecorderDirectory(rec_dir); var rec_max_size = 200; obj.NT_SetRecorderFileMaxSize(rec_max_size); var type = 0; var file_name_prefix = "daniulive"; var append_date = 1; var append_time = 1; obj.NT_NT_SP_RecorderFileNameRuler(type, file_name_prefix, append_date, append_time); var is_transcode = 1; obj.NT_SetRecorderAudioTranscodeAAC(is_transcode); var is_record_video = 1; obj.NT_SetRecorderVideo(is_record_video); var is_record_audio = 1; obj.NT_SetRecorderAudio(is_record_audio); var ret = obj.NT_StartRecorder(); if(ret == 0) { is_recording = true; var recordBtnText = document.getElementById("recordBtn"); recordBtnText.innerHTML = "停止录像"; } } //停止录像 function StopRecorder() { if(!is_recording) { return; } var obj = document.getElementById("SmartPlayerActiveX"); obj.NT_StopRecorder(); is_recording = false; var recordBtnText = document.getElementById("recordBtn"); recordBtnText.innerHTML = "开始录像"; if(!is_playing) { ClosePlayer(); } }
快速切换URL调用示例:
//快速切换播放URL function SwitchUrl() { if(!is_playing) { return; } var obj = document.getElementById("SmartPlayerActiveX"); var switch_url = document.getElementById("playorReocordUrl").value; obj.NT_SwitchURL(switch_url); }
实时静音调用示例:
//实时静音 var is_mute = 1; function SetMute() { var obj = document.getElementById("SmartPlayerActiveX"); obj.NT_SetMute(is_mute); var muteText = document.getElementById("MuteBtn"); if(is_mute == 1 ) { is_mute = 0; muteText.innerHTML = "取消静音"; } else { is_mute = 1; muteText.innerHTML = "实时静音"; } }
视频view垂直反转、水平反转、旋转调用示例:
//垂直反转 var is_flip_vertical = 1; function SetFlipVertical() { var obj = document.getElementById("SmartPlayerActiveX"); obj.NT_SetFlipVertical(is_flip_vertical); var flipVerticalText = document.getElementById("FlipVerticalBtn"); if(is_flip_vertical == 1 ) { is_flip_vertical = 0; flipVerticalText.innerHTML = "取消反转"; } else { is_flip_vertical = 1; flipVerticalText.innerHTML = "垂直反转"; } } //水平反转 var is_flip_horizontal = 1; function SetFlipHorizontal() { var obj = document.getElementById("SmartPlayerActiveX"); obj.NT_SetFlipHorizontal(is_flip_horizontal); var flipHorizontalText = document.getElementById("FlipHorizontalBtn"); if(is_flip_horizontal == 1 ) { is_flip_horizontal = 0; flipHorizontalText.innerHTML = "取消反转"; } else { is_flip_horizontal = 1; flipHorizontalText.innerHTML = "水平反转"; } } //视频view旋转 var rotate_degrees_ = 0; function SetRotation() { rotate_degrees_ += 90; rotate_degrees_ = rotate_degrees_ % 360; var obj = document.getElementById("SmartPlayerActiveX"); obj.NT_SetRotation(rotate_degrees_); var rotateText = document.getElementById("RotateBtn"); if (0 == rotate_degrees_) { rotateText.innerHTML = "旋转90度"; } else if (90 == rotate_degrees_) { rotateText.innerHTML = "旋转180度"; } else if (180 == rotate_degrees_) { rotateText.innerHTML = "旋转270度"; } else if (270 == rotate_degrees_) { rotateText.innerHTML = "不旋转"; } }
实时截图调用示例:
function CaptureImage() { var obj = document.getElementById("SmartPlayerActiveX"); obj.NT_CaptureImage(); }
全屏显示窗口调用示例:
//全屏显示窗口 function FullScreen() { var obj = document.getElementById("SmartPlayerActiveX"); obj.NT_FullScreen(); }