使用微信做网站第三方登录,湖北现代城市建设集团网站,响应式自适应网站,本地高端网站建设信息大全技术背景
在写这篇文章之前#xff0c;实际上几年之前#xff0c;我们就有非常稳定的无纸化同屏的模块#xff0c;本文借demo更新#xff0c;算是做个新的总结#xff0c;废话不多说#xff0c;先看图#xff0c;本文以Android平台屏幕实时采集推送#xff0c;Windows…技术背景
在写这篇文章之前实际上几年之前我们就有非常稳定的无纸化同屏的模块本文借demo更新算是做个新的总结废话不多说先看图本文以Android平台屏幕实时采集推送Windows播放为例和大家做个技术分享。 技术考量指标
本文以大牛直播SDK前些年实现的Android同屏采集推送为例大概介绍下一些技术考量指标。 1. 轻量级RTSP服务还是RTMP
我们在做无纸化同屏的时候问的最多的是能不能不要自建服务直接主讲人或教师端直接启动轻量级RTSP服务其他终端拉流如果是小并发比如5人内的小范围的同屏Windows平台走轻量级RTSP无可厚非如果是30-60甚至100人的会议室建议走RTMP。
2. 推送分辨率和码率选择
我们接触到好多设备性能一般但是屏幕是高分屏甚至可以采集到4K的考虑到实时编码和并发环境下AP的承载能力一般建议选择适合自己的分辨率码率即可不要只追求高分辨率高码率导致组网困难单个或双通道AP压力大一般建议控制在1920*1080分辨率内码率控制在1-5M。
3. 软编码还是硬编码
Windows平台一般优先考虑软编因为大多Windows性能瓶颈不太大超过1080P可以考虑硬编Android平台建议直接硬编码。
4. 高分屏采集编码效率低怎么办
高分屏不管是Windows还是Android采集后的数据建议先压缩再编码Windows平台我们可以设置压缩比例scale rateAndroid平台亦可比如采集原始屏幕或者缩放后的屏幕具体见下图 /* BackgroudService.java* Author: daniusdk.com*/ private void createScreenEnvironment() {sreenWindowWidth mWindowManager.getDefaultDisplay().getWidth();screenWindowHeight mWindowManager.getDefaultDisplay().getHeight();Log.i(TAG, screenWindowWidth: sreenWindowWidth ,screenWindowHeight: screenWindowHeight);if (sreenWindowWidth 800){if (screen_resolution_type_ SCREEN_RESOLUTION_STANDARD){scale_rate SCALE_RATE_HALF;sreenWindowWidth align(sreenWindowWidth / 2, 16);screenWindowHeight align(screenWindowHeight / 2, 16);}else if(screen_resolution_type_ SCREEN_RESOLUTION_LOW){scale_rate SCALE_RATE_TWO_FIFTHS;sreenWindowWidth align(sreenWindowWidth * 2 / 5, 16);screenWindowHeight align(screenWindowHeight * 2 / 5, 16);}}Log.i(TAG, After adjust mWindowWidth: sreenWindowWidth , mWindowHeight: screenWindowHeight);int pf mWindowManager.getDefaultDisplay().getPixelFormat();Log.i(TAG, display format: pf);DisplayMetrics displayMetrics new DisplayMetrics();mWindowManager.getDefaultDisplay().getMetrics(displayMetrics);mScreenDensity displayMetrics.densityDpi;mImageReader ImageReader.newInstance(sreenWindowWidth,screenWindowHeight, 0x1, 6);mMediaProjectionManager (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);}
5. Android横竖屏自动适配
Android平台如果是pad采集基本就是横屏采集如果手机端需要确保横竖屏模式下都可以正常采集。
4. 为什么要考虑补帧
Android的时候一定的采集模式下屏幕如果没有变化不会一直有实时屏幕数据回调下来这时候为了保持帧率或数据采集的完整性建议补帧。
5. 异常网络处理、事件回调机制
网络状态不管是推送端还是播放端都是需要有实时的状态回调确保客户端可以实时感知网络状态。
backgroudService.SetEventListener(new EventListener() {Overridepublic void onPublisherEventCallback(long handle, int id, long param1, long param2, String param3, String param4, Object param5) {String publisher_event ;switch (id) {case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_STARTED:publisher_event 开始..;break;case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_CONNECTING:publisher_event 连接中..;break;case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_CONNECTION_FAILED:publisher_event 连接失败..;break;case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_CONNECTED:publisher_event 连接成功..;break;case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_DISCONNECTED:publisher_event 连接断开..;break;case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_STOP:publisher_event 关闭..;break;case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_RECORDER_START_NEW_FILE:publisher_event 开始一个新的录像文件 : param3;break;case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_ONE_RECORDER_FILE_FINISHED:publisher_event 已生成一个录像文件 : param3;break;case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_SEND_DELAY:publisher_event 发送时延: param1 帧数: param2;break;case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_CAPTURE_IMAGE:publisher_event 快照: param1 路径 param3;if (param1 0) {publisher_event publisher_event 截取快照成功..;} else {publisher_event publisher_event 截取快照失败..;}break;case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_RTSP_URL:publisher_event RTSP服务URL: param3;break;case NTSmartEventID.EVENT_DANIULIVE_ERC_PUSH_RTSP_SERVER_RESPONSE_STATUS_CODE:publisher_event RTSP status code received, codeID: param1 , RTSP URL: param3;break;case NTSmartEventID.EVENT_DANIULIVE_ERC_PUSH_RTSP_SERVER_NOT_SUPPORT:publisher_event 服务器不支持RTSP推送, 推送的RTSP URL: param3;break;}String str 当前状态 publisher_event;Log.i(TAG, str);if (handler_ ! null) {Message message new Message();message.what PUBLISHER_EVENT_MSG;message.obj publisher_event;handler_.sendMessage(message);}}});
6. 采集到的数据可以按需录像吗
可以而且很有必要同屏的时候如果需要把开会或教授内容实时保存下来可以随时启动录像。 public boolean startRecorder(){Log.i(TAG, onClick startRecorder..);if(!stream_publisher_.is_publishing()){startCaptureScreen();}if (layer_post_thread_ ! null)layer_post_thread_.update_layers();if (stream_publisher_.is_recording()) {stopRecorder();return false;}InitAndSetConfig();ConfigRecorderParam();boolean start_ret stream_publisher_.StartRecorder();if (!start_ret) {stream_publisher_.try_release();Log.e(TAG, Failed to start recorder.);return false;}startAudioRecorder();startLayerPostThread();return true;}//停止录像public void stopRecorder() {stream_publisher_.StopRecorder();stream_publisher_.try_release();if (!stream_publisher_.is_publishing())stopAudioRecorder();}
7. 文字、图片水印
需要而且建议支持比如实时时间、学校或公司logo等。 //水印效果选择watermarkSelctor (Spinner) findViewById(R.id.watermarkSelctor);watermarkSelctor.setEnabled(false);final String[] watermarks new String[]{图片水印, 全部水印, 文字水印, 不加水印};ArrayAdapterString adapterWatermark new ArrayAdapterString(this,android.R.layout.simple_spinner_item, watermarks);adapterWatermark.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);watermarkSelctor.setAdapter(adapterWatermark);watermarkSelctor.setSelection(3,true);watemarkType 3; //默认不加水印watermarkSelctor.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {Overridepublic void onItemSelected(AdapterView? parent, View view,int position, long id) {watemarkType position;Log.i(TAG, [水印类型]Currently choosing: watermarks[position] , watemarkType: watemarkType);if(backgroudService !null) {backgroudService.updateWatermarker(watemarkType);}}Overridepublic void onNothingSelected(AdapterView? parent) {}});
8. 可以同时启动轻量级RTSP服务吗 public boolean startRtspService(int port){Log.i(TAG, startRtspService);rtsp_handle_ lib_publisher_.OpenRtspServer(0);if (rtsp_handle_ 0) {Log.e(TAG, 创建rtsp server实例失败! 请检查SDK有效性);} else {if (lib_publisher_.SetRtspServerPort(rtsp_handle_, port) ! 0) {lib_publisher_.CloseRtspServer(rtsp_handle_);rtsp_handle_ 0;Log.e(TAG, 创建rtsp server端口失败! 请检查端口是否重复或者端口不在范围内!);}if (lib_publisher_.StartRtspServer(rtsp_handle_, 0) 0) {Log.i(TAG, 启动rtsp server 成功!);} else {lib_publisher_.CloseRtspServer(rtsp_handle_);rtsp_handle_ 0;Log.e(TAG, 启动rtsp server失败! 请检查设置的端口是否被占用!);}isRTSPServiceRunning true;}return true;}//停止RTSP服务public void stopRtspService() {Log.i(TAG, stopRtspService);if(!isRTSPServiceRunning){return;}if (lib_publisher_ ! null rtsp_handle_ ! 0) {lib_publisher_.StopRtspServer(rtsp_handle_);lib_publisher_.CloseRtspServer(rtsp_handle_);rtsp_handle_ 0;}isRTSPServiceRunning false;}public boolean startRtspPublisher(){Log.i(TAG, startRtspPublisher);if(!stream_publisher_.is_publishing()){startCaptureScreen();}InitAndSetConfig();String rtsp_stream_name stream1;stream_publisher_.SetRtspStreamName(rtsp_stream_name);stream_publisher_.ClearRtspStreamServer();stream_publisher_.AddRtspStreamServer(rtsp_handle_);if (!stream_publisher_.StartRtspStream()) {stream_publisher_.try_release();Log.e(TAG, 调用发布rtsp流接口失败!);return false;}startAudioRecorder();startLayerPostThread();return true;}//停止发布RTSP流public void stopRtspPublisher() {Log.i(TAG, stopRtspPublisher);stream_publisher_.StopRtspStream();stream_publisher_.try_release();if (!stream_publisher_.is_publishing())stopAudioRecorder();}public int getRtspSessionNumbers(){int session_numbers 0;if (lib_publisher_ ! null rtsp_handle_ ! 0) {session_numbers lib_publisher_.GetRtspServerClientSessionNumbers(rtsp_handle_);Log.i(TAG, GetRtspSessionNumbers: session_numbers);}return session_numbers;}
9. 同屏延迟能不能做到毫秒级
废话不多说上视频延迟毫秒级。 安卓采集屏幕至轻量级RTSP服务|推送RTMP整体毫秒级延迟 10. 能不能采集到扬声器的audio
Windows不在话下Android平台需要高版本支持高版本是可以采集到扬声器数据的我们也实现了相关的demo可以同时采集麦克风和扬声器的audio单独推送或者同时混音输出。
11. 同屏过程中重点画面可以快照吗
当然可以我们同屏采集端支持采集编码png或jpg格式输出。
总结
其实一个好的无纸化同屏系统需要考虑的有整体组网、分辨率、码率、实时延迟、音视频同步和连续性等各个指标做容易做好难上述抛砖引玉未能面面俱到感兴趣的开发者可以跟我单独交流。