Android对接实现内网无纸化会议|智慧教室|实时同屏功能

背景

本文主要讲的是基于Android平台实现RTMP的技术方案设计,基础架构图如下:

组网注意事项

1. 组网:无线组网,需要好的AP模块才能撑得住大的并发流量,推送端到AP,最好是有线网链接;

2. 服务器部署:SRS或NGINX,服务器可以和Windows平台的教师机部署在一台机器;

3. 教师端:如教师有移动的PAD,可以直接推到RTMP服务器,然后共享出去;

4. 学生端:直接拉取服务端的RTMP流播放即可;

5. 教师和学生互动:学生端如需作为示范案例,屏幕数据共享给其他同学,只需请求同屏,数据反推到RTMP服务器,其他学生查看即可。

6. 扩展监控:如果需要更进一步的技术方案,如教师端想监控学生端的屏幕情况,可以有两种方案,如学生端直接推RTMP过来,或者,学生端启动内置RTSP服务,教师端想看的时候,随时看即可(亦可轮询播放)。

Android端对接

推送分辨率如何设定或缩放?

Android设备,特别是高分屏,拿到的视频原始宽高非常大,如果推原始分辨率,编码和上行压力大,所以,一般建议,适当缩放,比如宽高缩放至2/3,缩放一般建议等比例缩放,缩放宽高建议16字节对齐。

    private void createScreenEnvironment() {sreenWindowWidth = mWindowManager.getDefaultDisplay().getWidth();screenWindowHeight = mWindowManager.getDefaultDisplay().getHeight();Log.i(TAG, "screenWindowWidth: " + sreenWindowWidth + ",screenWindowHeight: "+ screenWindowHeight);if (sreenWindowWidth > 800){if (screenResolution == SCREEN_RESOLUTION_STANDARD){scale_rate = SCALE_RATE_HALF;sreenWindowWidth = align(sreenWindowWidth / 2, 16);screenWindowHeight = align(screenWindowHeight / 2, 16);}else if(screenResolution == SCREEN_RESOLUTION_LOW){scale_rate = SCALE_RATE_TWO_FIFTHS;sreenWindowWidth = align(sreenWindowWidth * 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);}

横竖屏自动适配

横竖屏状态下,采集的屏幕宽高不一样,如果横竖屏切换,这个时候,需要考虑到横竖屏适配问题,确保比如竖屏状态下,切换到横屏时,推拉流两端可以自动适配,横竖屏自动适配,编码器需要重启,拉流端,需要能自动适配宽高变化,自动播放。

    public void onConfigurationChanged(Configuration newConfig) {try {super.onConfigurationChanged(newConfig);if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {Log.i(TAG, "onConfigurationChanged cur: LANDSCAPE");} else if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {Log.i(TAG, "onConfigurationChanged cur: PORTRAIT");}if(isPushingRtmp || isRecording || isRTSPPublisherRunning){stopScreenCapture();clearAllImages();createScreenEnvironment();setupVirtualDisplay();}} catch (Exception ex) {}}

补帧策略

好多人不理解为什么要补帧,实际上,屏幕采集的时候,屏幕不动的话,不会一直有数据下去,这个时候,比较好的做法是,保存最后一帧数据,设定一定的补帧间隔,确保不会因为帧间距太大,导致播放端几秒都收不到数据,当然,如果服务器可以缓存GOP,这个问题迎刃而解。

异常网络处理、事件回调机制

回答:如果是走RTMP,网络抖动或者其他网络异常,需要有好重连机制和状态回馈机制。

    class EventHandeV2 implements NTSmartEventCallbackV2 {@Overridepublic void onNTSmartEventCallbackV2(long handle, int id, long param1, long param2, String param3, String param4, Object param5) {Log.i(TAG, "EventHandeV2: handle=" + handle + " id:" + id);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);Message message = new Message();message.what = PUBLISHER_EVENT_MSG;message.obj = publisher_event;handler.sendMessage(message);}}

部分屏幕数据采集

回答:我们遇到的好多场景下,教室端,会拿出来3/4的区域用来投递给学生看,1/4的区域,用来做一些指令等操作,这个时候,就需要考虑屏幕区域裁剪:

	/*** 投递裁剪过的RGBA数据** @param data: RGBA data** @param rowStride: stride information** @param width: width** @param height: height** @param clipedLeft: 左;  clipedTop: 上; clipedwidth: 裁剪后的宽; clipedHeight: 裁剪后的高; 确保传下去裁剪后的宽、高均为偶数** @return {0} if successful*/public native int SmartPublisherOnCaptureVideoClipedRGBAData(long handle,  ByteBuffer data, int rowStride, int width, int height, int clipedLeft, int clipedTop, int clipedWidth, int clipedHeight);

文字、图片水印

好多场景下,同屏者会把公司logo,和一定的文字信息展示在推送端,这个时候,需要考虑到文字和图片水印问题:

   /*** Set Text water-mark(设置文字水印)* * @param fontSize: it should be "MEDIUM", "SMALL", "BIG"* * @param waterPostion: it should be "TOPLEFT", "TOPRIGHT", "BOTTOMLEFT", "BOTTOMRIGHT".* * @param xPading, yPading: the distance of the original picture.* * <pre> The interface is only used for setting font water-mark when publishing stream. </pre>  * * @return {0} if successful*/public native int SmartPublisherSetTextWatermark(long handle, String waterText, int isAppendTime, int fontSize, int waterPostion, int xPading, int yPading);/*** Set Text water-mark font file name(设置文字水印字体路径)** @param fontFileName:  font full file name, e.g: /system/fonts/DroidSansFallback.ttf** @return {0} if successful*/public native int SmartPublisherSetTextWatermarkFontFileName(long handle, String fontFileName);/*** Set picture water-mark(设置png图片水印)* 											* @param picPath: the picture working path, e.g: /sdcard/logo.png* * @param waterPostion: it should be "TOPLEFT", "TOPRIGHT", "BOTTOMLEFT", "BOTTOMRIGHT".* * @param picWidth, picHeight: picture width & height* * @param xPading, yPading: the distance of the original picture.* * <pre> The interface is only used for setting picture(logo) water-mark when publishing stream, with "*.png" format </pre>  * * @return {0} if successful*/public native int SmartPublisherSetPictureWatermark(long handle, String picPath, int waterPostion, int picWidth, int picHeight, int xPading, int yPading);

屏幕权限获取|数据采集

采集推送之前,需要获取屏幕权限,拿到屏幕数据后,调用SDK接口,完成推送或录像操作即可:

   @TargetApi(Build.VERSION_CODES.LOLLIPOP)private boolean startScreenCapture() {Log.i(TAG, "startScreenCapture..");setupMediaProjection();setupVirtualDisplay();return true;}private int align(int d, int a) {return (((d) + (a - 1)) & ~(a - 1));}@SuppressWarnings("deprecation")@SuppressLint("NewApi")private void createScreenEnvironment() {sreenWindowWidth = mWindowManager.getDefaultDisplay().getWidth();screenWindowHeight = mWindowManager.getDefaultDisplay().getHeight();Log.i(TAG, "screenWindowWidth: " + sreenWindowWidth + ",screenWindowHeight: "+ screenWindowHeight);if (sreenWindowWidth > 800){if (screenResolution == SCREEN_RESOLUTION_STANDARD){scale_rate = SCALE_RATE_HALF;sreenWindowWidth = align(sreenWindowWidth / 2, 16);screenWindowHeight = align(screenWindowHeight / 2, 16);}else if(screenResolution == 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);}@SuppressLint("NewApi")private void setupMediaProjection() {mMediaProjection = mMediaProjectionManager.getMediaProjection(MainActivity.mResultCode, MainActivity.mResultData);}@SuppressLint("NewApi")private void setupVirtualDisplay() {mVirtualDisplay = mMediaProjection.createVirtualDisplay("ScreenCapture", sreenWindowWidth, screenWindowHeight,mScreenDensity,DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,mImageReader.getSurface(), null, null);mImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {@Overridepublic void onImageAvailable(ImageReader reader) {Image image = mImageReader.acquireLatestImage();if (image != null) {processScreenImage(image);//image.close();}}}, null);}private void startRecorderScreen() {Log.i(TAG, "start recorder screen..");if (startScreenCapture()) {new Thread() {@Overridepublic void run() {Log.i(TAG, "start record..");}}.start();}}private ByteBuffer deepCopy(ByteBuffer source) {int sourceP = source.position();int sourceL = source.limit();ByteBuffer target = ByteBuffer.allocateDirect(source.remaining());target.put(source);target.flip();source.position(sourceP);source.limit(sourceL);return target;}/*** Process image data as desired.*/@SuppressLint("NewApi")private void processScreenImage(Image image) {if(!isPushingRtmp && !isRecording &&!isRTSPPublisherRunning){image.close();return;}/*final Image.Plane[] planes = image.getPlanes();width_ = image.getWidth();height_ = image.getHeight();row_stride_ = planes[0].getRowStride();ByteBuffer buf = deepCopy(planes[0].getBuffer());*/// Log.i("OnScreenImage", "new image");pushImage(image);}@SuppressLint("NewApi")private void stopScreenCapture() {if (mVirtualDisplay != null) {mVirtualDisplay.release();mVirtualDisplay = null;}}

基础初始化

   private void InitAndSetConfig() {//开始要不要采集音频或视频,请自行设置publisherHandle = libPublisher.SmartPublisherOpen(this.getApplicationContext(),audio_opt, video_opt, sreenWindowWidth,screenWindowHeight);if ( publisherHandle == 0 ){return;}Log.i(TAG, "publisherHandle=" + publisherHandle);libPublisher.SetSmartPublisherEventCallbackV2(publisherHandle, new EventHandeV2());if(videoEncodeType == 1){int h264HWKbps = setHardwareEncoderKbps(true, sreenWindowWidth,screenWindowHeight);Log.i(TAG, "h264HWKbps: " + h264HWKbps);int isSupportH264HWEncoder = libPublisher.SetSmartPublisherVideoHWEncoder(publisherHandle, h264HWKbps);if (isSupportH264HWEncoder == 0) {Log.i(TAG, "Great, it supports h.264 hardware encoder!");}}else if (videoEncodeType == 2){int hevcHWKbps = setHardwareEncoderKbps(false, sreenWindowWidth,screenWindowHeight);Log.i(TAG, "hevcHWKbps: " + hevcHWKbps);int isSupportHevcHWEncoder = libPublisher.SetSmartPublisherVideoHevcHWEncoder(publisherHandle, hevcHWKbps);if (isSupportHevcHWEncoder == 0) {Log.i(TAG, "Great, it supports hevc hardware encoder!");}}if(is_sw_vbr_mode){int is_enable_vbr = 1;int video_quality = CalVideoQuality(sreenWindowWidth,screenWindowHeight, true);int vbr_max_bitrate = CalVbrMaxKBitRate(sreenWindowWidth,screenWindowHeight);libPublisher.SmartPublisherSetSwVBRMode(publisherHandle, is_enable_vbr, video_quality, vbr_max_bitrate);}//音频相关可以参考SmartPublisher工程/*if (!is_speex){// set AAC encoderlibPublisher.SmartPublisherSetAudioCodecType(publisherHandle, 1);}else{// set Speex encoderlibPublisher.SmartPublisherSetAudioCodecType(publisherHandle, 2);libPublisher.SmartPublisherSetSpeexEncoderQuality(publisherHandle, 8);}libPublisher.SmartPublisherSetNoiseSuppression(publisherHandle, is_noise_suppression ? 1: 0);libPublisher.SmartPublisherSetAGC(publisherHandle, is_agc ? 1 : 0);*/// libPublisher.SmartPublisherSetClippingMode(publisherHandle, 0);//libPublisher.SmartPublisherSetSWVideoEncoderProfile(publisherHandle, sw_video_encoder_profile);//libPublisher.SmartPublisherSetSWVideoEncoderSpeed(publisherHandle, sw_video_encoder_speed);// libPublisher.SetRtmpPublishingType(publisherHandle, 0);libPublisher.SmartPublisherSetFPS(publisherHandle, 18);    //帧率可调libPublisher.SmartPublisherSetGopInterval(publisherHandle, 18*3);//libPublisher.SmartPublisherSetSWVideoBitRate(publisherHandle, 1200, 2400); //针对软编码有效,一般最大码率是平均码率的二倍libPublisher.SmartPublisherSetSWVideoEncoderSpeed(publisherHandle, 3);//libPublisher.SmartPublisherSaveImageFlag(publisherHandle, 1);}

准备推送|录像|启动RTSP服务

    @SuppressWarnings("deprecation")@Overridepublic void onStart(Intent intent, int startId) {// TODO Auto-generated method stubsuper.onStart(intent, startId);Log.i(TAG, "onStart++");if (libPublisher == null)return;clearAllImages();screenResolution = intent.getExtras().getInt("SCREENRESOLUTION");videoEncodeType = intent.getExtras().getInt("VIDEOENCODETYPE");push_type = intent.getExtras().getInt("PUSHTYPE");Log.i(TAG, "push_type: " + push_type);mWindowManager = (WindowManager) getSystemService(Service.WINDOW_SERVICE);// 窗口管理者createScreenEnvironment();startRecorderScreen();//如果同时推送和录像,设置一次就可以InitAndSetConfig();if ( publisherHandle == 0 ){stopScreenCapture();return;}if(push_type == PUSH_TYPE_RTMP){String publishURL = intent.getStringExtra("PUBLISHURL");Log.i(TAG, "publishURL: " + publishURL);if (libPublisher.SmartPublisherSetURL(publisherHandle, publishURL) != 0) {stopScreenCapture();Log.e(TAG, "Failed to set publish stream URL..");if (publisherHandle != 0) {if (libPublisher != null) {libPublisher.SmartPublisherClose(publisherHandle);publisherHandle = 0;}}return;}}//启动传递数据线程post_data_thread = new Thread(new DataRunnable());Log.i(TAG, "new post_data_thread..");is_post_data_thread_alive = true;post_data_thread.start();//录像相关++is_need_local_recorder = intent.getExtras().getBoolean("RECORDER");if(is_need_local_recorder){ConfigRecorderParam();int startRet = libPublisher.SmartPublisherStartRecorder(publisherHandle);if( startRet != 0 ){isRecording = false;Log.e(TAG, "Failed to start recorder..");}else{isRecording = true;}}//录像相关——if(push_type == PUSH_TYPE_RTMP){Log.i(TAG, "RTMP Pusher mode..");//推流相关++int startRet = libPublisher.SmartPublisherStartPublisher(publisherHandle);if (startRet != 0) {isPushingRtmp = false;Log.e(TAG, "Failed to start push rtmp stream..");return;}else{isPushingRtmp = true;}//推流相关--}else if(push_type == PUSH_TYPE_RTSP){Log.i(TAG, "RTSP Internal Server mode..");rtsp_handle_ = libPublisher.OpenRtspServer(0);if (rtsp_handle_ == 0) {Log.e(TAG, "创建rtsp server实例失败! 请检查SDK有效性");} else {int port = 8554;if (libPublisher.SetRtspServerPort(rtsp_handle_, port) != 0) {libPublisher.CloseRtspServer(rtsp_handle_);rtsp_handle_ = 0;Log.e(TAG, "创建rtsp server端口失败! 请检查端口是否重复或者端口不在范围内!");}//String user_name = "admin";//String password = "12345";//libPublisher.SetRtspServerUserNamePassword(rtsp_handle_, user_name, password);if (libPublisher.StartRtspServer(rtsp_handle_, 0) == 0) {Log.i(TAG, "启动rtsp server 成功!");} else {libPublisher.CloseRtspServer(rtsp_handle_);rtsp_handle_ = 0;Log.e(TAG, "启动rtsp server失败! 请检查设置的端口是否被占用!");return;}isRTSPServiceRunning = true;}if(isRTSPServiceRunning){Log.i(TAG, "onClick start rtsp publisher..");String rtsp_stream_name = "stream1";libPublisher.SetRtspStreamName(publisherHandle, rtsp_stream_name);libPublisher.ClearRtspStreamServer(publisherHandle);libPublisher.AddRtspStreamServer(publisherHandle, rtsp_handle_, 0);if (libPublisher.StartRtspStream(publisherHandle, 0) != 0) {Log.e(TAG, "调用发布rtsp流接口失败!");return;}isRTSPPublisherRunning = true;}}//如果同时推送和录像,Audio启动一次就可以了CheckInitAudioRecorder();Log.i(TAG, "onStart--");}private void stopPush() {if(!isPushingRtmp){return;}if (!isRecording && !isRTSPPublisherRunning) {if (audioRecord_ != null) {Log.i(TAG, "stopPush, call audioRecord_.StopRecording..");audioRecord_.Stop();if (audioRecordCallback_ != null) {audioRecord_.RemoveCallback(audioRecordCallback_);audioRecordCallback_ = null;}audioRecord_ = null;}}if (libPublisher != null) {libPublisher.SmartPublisherStopPublisher(publisherHandle);}if (!isRecording && !isRTSPPublisherRunning) {if (publisherHandle != 0) {if (libPublisher != null) {libPublisher.SmartPublisherClose(publisherHandle);publisherHandle = 0;}}}}

停止推送|录像|RTSP服务

   private void stopRecorder() {if(!isRecording){return;}if (!isPushingRtmp && !isRTSPPublisherRunning) {if (audioRecord_ != null) {Log.i(TAG, "stopRecorder, call audioRecord_.StopRecording..");audioRecord_.Stop();if (audioRecordCallback_ != null) {audioRecord_.RemoveCallback(audioRecordCallback_);audioRecordCallback_ = null;}audioRecord_ = null;}}if (libPublisher != null) {libPublisher.SmartPublisherStopRecorder(publisherHandle);}if (!isPushingRtmp && !isRTSPPublisherRunning) {if (publisherHandle != 0) {if (libPublisher != null) {libPublisher.SmartPublisherClose(publisherHandle);publisherHandle = 0;}}}}//停止发布RTSP流private void stopRtspPublisher() {if(!isRTSPPublisherRunning){return;}if (!isPushingRtmp && !isRecording) {if (audioRecord_ != null) {Log.i(TAG, "stopRtspPublisher, call audioRecord_.StopRecording..");audioRecord_.Stop();if (audioRecordCallback_ != null) {audioRecord_.RemoveCallback(audioRecordCallback_);audioRecordCallback_ = null;}audioRecord_ = null;}}if (libPublisher != null) {libPublisher.StopRtspStream(publisherHandle);}if (!isPushingRtmp && !isRecording) {if (publisherHandle != 0) {if (libPublisher != null) {libPublisher.SmartPublisherClose(publisherHandle);publisherHandle = 0;}}}}//停止RTSP服务private void stopRtspService() {if(!isRTSPServiceRunning){return;}if (libPublisher != null && rtsp_handle_ != 0) {libPublisher.StopRtspServer(rtsp_handle_);libPublisher.CloseRtspServer(rtsp_handle_);rtsp_handle_ = 0;}}

感兴趣的开发者可酌情参考。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/553053.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

bilibili里的硬币有什么用 硬币的作用详细介绍

很多bilibili中的用户发现自己的账户中拥有很多的硬币&#xff0c;却不知道这些硬币有什么用&#xff0c;下面小编就为大家带来硬币的作用介绍。 bilibili里的硬币有什么用 硬币的作用详细介绍 1、视频评分。向喜欢的视频投硬币以示鼓励(一个视频收到的硬币多了就会被推荐) …

Windows平台RTMP推送|轻量级RTSP服务实现本地摄像头|屏幕|叠加数据预览

背景 大家在做Windows平台RTMP推送或轻量级RTSP服务的时候&#xff0c;不管是采集屏幕还是采集摄像头&#xff0c;亦或屏幕摄像头的叠加模式&#xff0c;总会有这样的诉求&#xff0c;采集到的数据&#xff0c;希望能本地看看具体采集的数据或者图像实际效果&#xff0c;也就是…

Outlook2016怎么修改默认数据库

Outlook2016中想要修改默认数据库&#xff0c;该怎么修改呢?下面我们就来看看详细的教程。 Outlook2016怎么修改默认数据库? 1、下载安装outlook软件。 Outlook2016怎么修改默认数据库? 2、双击打开outlook软件。 Outlook2016怎么修改默认数据库? 3、点击“文件”——…

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

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

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

我们在Windows平台实现RTSP或者RTMP播放的时候&#xff0c;有个功能是绕不开的&#xff0c;那就是播放窗口全屏。本文就以大牛直播SDK&#xff08;官方&#xff09;的Windows播放器为例&#xff0c;大概讲下大概实现&#xff1a; 全屏播放需要考虑的点不多&#xff1a; 第一&…

tim怎么设置检测到新版本自动安装 tim安全自动更新的开启方法

TIM想要开启自动检测新版本并下载安装&#xff0c;该怎么设置呢?下面我们就来看看详细的教程。 1、首先&#xff0c;在你的 电脑中找到TIM; tim怎么设置检测到新版本自动安装?tim安全自动更新的开启方法 2、打开TIM&#xff0c;登录你的个人账号&#xff0c;登陆之后&…

Android平台实现Unity3D下RTMP推送

像Unity3D下的RTMP或RTSP播放器一样&#xff0c;好多开发者苦于在Unity环境下&#xff0c;如何高效率低延迟的把数据采集并编码实时推送到流媒体服务器&#xff0c;实现Unity场景下的低延迟推拉流方案。 关于屏幕采集&#xff0c;有两种方案&#xff1a; 1. 直接封装Android原…

Windows平台实现Unity下窗体|摄像头|屏幕采集推送

技术背景 随着Unity3D的应用范围越来越广&#xff0c;越来越多的行业开始基于Unity3D开发产品&#xff0c;如传统行业中虚拟仿真教育、航空工业、室内设计、城市规划、工业仿真等领域。 基于此&#xff0c;好多开发者苦于在Unity环境下&#xff0c;没有低延迟的推拉流解决方案…

钉钉调岗申请单怎么写 钉钉申请调岗的教程

钉钉中想要申请调岗申请单&#xff0c;调岗申请单在哪&#xff0c;该怎么填写呢?下面我们就来看看详细的教程。 1、首先打开钉钉客户端&#xff0c;点击下方的工作。 钉钉调岗申请单怎么写? 钉钉申请调岗的教程 2、向下滑动找到并打开调岗申请单应用。 钉钉调岗申请单怎么…

Android平台RTMP推送端实现外部数据对接推送和录像

背景 好多开发者在做Android平台RTMP推送对接的同时&#xff0c;除了编码前的数据外&#xff0c;还有些外部编码数据推送诉求&#xff0c;他们希望外部的编码音视频数据不止可以实现RTMP推送&#xff0c;还可以同时在推送端实时录制下来&#xff0c;本文以我们&#xff08;官方…

excel怎么删除浏览记录 方法介绍

Excel 2013工作表删除浏览记录的步骤&#xff1a; 1、鼠标左键双击计算机桌面Excel2013程序图标&#xff0c;将其打开运行。在打开的Excel2013程序窗口&#xff0c;点击“空白工作薄”选项&#xff0c;新建一个空白Excel工作薄。如图所示; excel怎么删除浏览记录&#xff1f;…

爱奇艺怎么开启数据流量自动播放

1、打开手机&#xff0c;点击爱奇艺 爱奇艺怎么开启数据流量自动播放 2、进入爱奇艺&#xff0c;点击我的 爱奇艺怎么开启数据流量自动播放 3、然后在我的界面&#xff0c;点击设置 爱奇艺怎么开启数据流量自动播放 4、进入设置界面&#xff0c;点击播放与下载 爱奇艺怎…

Win7系统账户被禁用的解决方法

我们都知道Win7系统中可以建立多个账户来进行不同的登录使用&#xff0c;十分的方便&#xff0c;但是有的用户建立了多个账户&#xff0c;却发现所有的账号都被禁用&#xff0c;那遇到这个问题应该怎么解决呢&#xff0c;下面就为小伙伴们带来 了Win7系统账户被禁用的解决方法&…

Win11系统如何隐藏快速搜索

近日网络中已经出现了很多Windows 11的泄露版本&#xff0c;意味着Win11版本正式确定&#xff0c;现在只是等待微软正式发布了。Windows系统中菜单栏都会默认放一个快速搜索的图标&#xff0c;快速搜索一直是Windows系统中的一个重要功能&#xff0c;那么在Win11系统中该如何隐…

网易邮箱广告标签怎么关 163邮箱去广告标签的教程

1、打开百度输入网易邮箱 网易邮箱广告标签怎么关? 163邮箱去广告标签的教程 网易邮箱广告标签怎么关? 163邮箱去广告标签的教程 2、输入账号和密码&#xff0c;进入邮箱主页&#xff0c;在标签栏看到广告标签 网易邮箱广告标签怎么关? 163邮箱去广告标签的教程 3、选中…

Linux|麒麟操作系统实现多路RTMP|RTSP播放

技术背景 无论是Windows平台还是Linux&#xff0c;多路播放诉求非常普遍&#xff0c;比如针对智慧工地、展馆、教育等宏观场景下的摄像头展示&#xff0c;关于RTSP或RTMP直播播放器开发需要注意的点&#xff0c;可参考之前博客&#xff0c;总的来说有以下一些点&#xff1a; …

TIM提示“个人文件夹被占用,请稍候再登录”怎么解决

在登录TIM的时候&#xff0c;弹出提示“个人文件夹被占用&#xff0c;请稍后再登录”的提示&#xff0c;无法正常登录TIM TIM提示“个人文件夹被占用&#xff0c;请稍候再登录”怎么解决&#xff1f; 这个时候&#xff0c;我们可以直接关闭上面的登录窗口&#xff0c;然后过一…

数据推送选择GB28181、RTSP还是RTMP?

GB/T28181 国标GB/T28181协议全称《安全防范视频监控联网系统信息传输、交换、控制技术要求》&#xff0c;是一个定义视频联网传输和设备控制标准的白皮书&#xff0c;由公安部科技信息化局提出&#xff0c;该标准规定了城市监控报警联网系统中信息传输、交换、控制的互联结构…

Windows平台RTMP推送摄像头对接介绍

背景 好多开发者在对接大牛直播SDK&#xff08;官方&#xff09;的Windows平台RTMP推送时&#xff0c;不熟悉摄像头调用&#xff0c;实际上&#xff0c;摄像头调用逻辑并不复杂&#xff0c;以下是大概流程&#xff1a; 首先调用我们sdk接口获取摄像头个数&#xff0c;调用接口…

Outlook2016未读邮件怎么设置字体颜色

Outlook2016中想要设置未读邮件的字体颜色&#xff0c;该怎么设置呢?下面我们就来看看详细的教程。 Outlook2016未读邮件怎么设置字体颜色? 1、下载安装outlook软件。 Outlook2016未读邮件怎么设置字体颜色? 2、双击打开outlook软件&#xff0c;登入邮箱账户。 Outlook…