基於智慧教室|無紙化會議的新選擇:RTMP解決方案


基於智慧教室或是會議的技術方案,一般主要是涉及到屏幕采集和推送,整體技術方案這塊,一般建議走RTMP,說到這里,好人開發者提到,市面上也有RTSP的技術方案,甚至RTSP組播方案,這塊,大牛直播SDK Github 也做過相關對比,總的來說60人智慧教室或類似同屏場景下,最可靠的還是RTMP的解決方案(不贅述,具體可自行測試對比)。

有人說,RTMP延遲大,這種說法,相對片面,好多是由於推拉流模塊本身問題導致(如果服務器系NIGNX或SRS,基本可排除服務器轉發導致的大時延,不要再賴服務器了),從我們官方和實際場景來看,RTMP整體技術方案,延遲可做到1秒內,毫秒級。

整體設計方案如下

注意事項

1. 組網:無線組網,需要好的AP模塊才能撐得住大的並發流量,推送端到AP,最好是有線網鏈接;

2. 服務器部署:如果Windows平台,可以考慮NGINX,如果是Linux,可以考慮SRS或NGINX,服務器可以和Windows平台的教師機部署在一台機器;

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

4. 學生端:直接拉取RTMP流播放即可;

5. 教師和學生互動:學生端如需作為示范案例,屏幕數據共享給其他同學,只需請求同屏,數據反推到RTMP服務器,其他學生查看即可。

6. 擴展監控:如果需要更進一步的技術方案,如教師端想監控學生端的屏幕情況,可以有兩種方案,如學生端直接推RTMP過來,或者,學生端啟動內置RTSP服務,教師端想看的時候,隨時看即可(亦可輪詢播放)。

以下分平台介紹相關配置選項

Windows平台RTMP推送端

對應DEMO:SmartPublisherDemo.exe

1. 如果采集屏幕,只要采集部分區域的話,可以點擊“選取屏幕區域”按鈕,選擇需要采集的區域,采集推送過程中,可以移動采集區域;

2. 如果是高分屏(如有些采集設備,是4K屏,原始分辨率過高),用戶又不想推這么高的分辨率的話,可以選中“縮放屏幕大小”,並指定縮放比例,可以先縮放,后編碼推送數據;

3. 設置采集幀率:如果是PPT/Word文檔類,一般8-12幀足矣,如果是電影之類,可以設置到20-30幀不等,關鍵幀間隔一般設置到幀率的2-4倍,屏幕推送的話,建議平均碼率模式;

4. 如果需要采集電腦端輸出的聲音,可以選中“采集揚聲器”,如果需要采集外部麥克風的音頻,選擇“采集麥克風”即可,並選擇對應的采集設備;

5. 設置下推送的RTMP URL,然后,點擊“推送”,就可以了;

6. 如果想預覽推送出去的數據,點擊“預覽”即可,想停止預覽的話,點擊“停止預覽”即可。

Android平台RTMP屏幕推送端

對應工程:SmartServicePublisherV2

需要注意的事項:

1. Android 8.0及以上版本設備,需要加入省電優化白名單,6.0以上版本,需要動態獲取audio權限,具體代碼如下:

        //加入省電優化白名單,以免8.0及以上版本設備后台運行超過一分鍾被自動停掉 //if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) if (Build.VERSION.SDK_INT >=26) { if(!isIgnoringBatteryOptimizations()) { gotoSettingIgnoringBatteryOptimizations(); } } //6.0及以上版本,動態獲取Audio權限 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { RequestAudioPermission(); } //拉起請求加入省電白名單彈窗 private void gotoSettingIgnoringBatteryOptimizations() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { try { Intent intent = new Intent(); String packageName = getPackageName(); intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS); intent.setData(Uri.parse("package:" + packageName)); startActivityForResult(intent, REQUEST_IGNORE_BATTERY_CODE); } catch (Exception e) { e.printStackTrace(); } } } //動態獲取Audio權限 private void RequestAudioPermission() { if (PackageManager.PERMISSION_GRANTED == ContextCompat.checkSelfPermission(this.getApplicationContext(), android.Manifest.permission.RECORD_AUDIO)) { } else { //提示用戶開戶權限音頻 String[] perms = {"android.permission.RECORD_AUDIO"}; ActivityCompat.requestPermissions(this, perms, RESULT_CODE_STARTAUDIO); } }

2. 持續的補幀策略,防止屏幕不動,沒數據下去;

3.  如果需要傳部分區域下去,可以用 SmartPublisherOnCaptureVideoClipedRGBAData() 接口;

4. 橫豎屏切換,上層無需過問,底層會自動切。

iOS平台RTMP屏幕推送端

對應工程: SmartServiceCameraPublisherV2

注意事項:ReplayKit2 的直播擴展目前是有50M的內存使用限制,超過此限制系統會直接殺死擴展進程,因此 ReplayKit2 上建議推流分辨率和幀率、碼率不要太高。

以下是核心processSampleBuffer() 處理,iOS 11.0以上 加入了橫豎屏自動切換適配:

- (void)processSampleBuffer:(CMSampleBufferRef)sampleBuffer withType:(RPSampleBufferType)sampleBufferType { CGFloat cur_memory = [self GetCurUsedMemoryInMB]; if( cur_memory > 20.0f) { //NSLog(@"processSampleBuffer cur: %.2fM", cur_memory); return; } switch (sampleBufferType) { case RPSampleBufferTypeVideo: { if (!CMSampleBufferIsValid(sampleBuffer)) return; NSInteger rotation_degress = 0; //11.1以上支持自動旋轉 #ifdef __IPHONE_11_1 if (UIDevice.currentDevice.systemVersion.floatValue > 11.1) { CGImagePropertyOrientation orientation = ((__bridge NSNumber*)CMGetAttachment(sampleBuffer, (__bridge CFStringRef)RPVideoSampleOrientationKey , NULL)).unsignedIntValue; //NSLog(@"cur org: %d", orientation); switch (orientation) { //豎屏 case kCGImagePropertyOrientationUp:{ rotation_degress = 0; } break; case kCGImagePropertyOrientationDown:{ rotation_degress = 180; break; } case kCGImagePropertyOrientationLeft: { //靜音鍵那邊向上 所需轉90度 rotation_degress = 90; } break; case kCGImagePropertyOrientationRight:{ //關機鍵那邊向上 所需轉270 rotation_degress = 270; } break; default: break; } } #endif //NSLog(@"RPSampleBufferTypeVideo"); if(_smart_publisher_sdk) { //[_smart_publisher_sdk SmartPublisherPostVideoSampleBuffer:sampleBuffer]; [_smart_publisher_sdk SmartPublisherPostVideoSampleBufferV2:sampleBuffer rotateDegress:rotation_degress]; } //NSLog(@"video ts:%.2f", CMTimeGetSeconds(CMSampleBufferGetPresentationTimeStamp(sampleBuffer))); } break; case RPSampleBufferTypeAudioApp: //NSLog(@"RPSampleBufferTypeAudioApp"); if (CMSampleBufferDataIsReady(sampleBuffer) != NO) { if(_smart_publisher_sdk) { NSInteger type = 2; [_smart_publisher_sdk SmartPublisherPostAudioSampleBuffer:sampleBuffer inputType:type]; } } //NSLog(@"App ts:%.2f", CMTimeGetSeconds(CMSampleBufferGetPresentationTimeStamp(sampleBuffer))); break; case RPSampleBufferTypeAudioMic: //NSLog(@"RPSampleBufferTypeAudioMic"); if(_smart_publisher_sdk) { NSInteger type = 1; [_smart_publisher_sdk SmartPublisherPostAudioSampleBuffer:sampleBuffer inputType:type]; } //NSLog(@"Mic ts:%.2f", CMTimeGetSeconds(CMSampleBufferGetPresentationTimeStamp(sampleBuffer))); break; default: break; } }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM