iOS直播集成和問題總結(阿里雲直播)


https://www.jianshu.com/p/714ce954e628

最近接手公司的直播項目,對以前遺留的問題做處理和優化, 於是順便看了下阿里雲直播的文檔,在下面寫下對直播的理解和遇到的問題, 阿里雲售后特別好,一對一解決問題速度很快,如果遇到解決不了的問題可以發工單提問,效率很高產品->視頻直播->文檔&SDK->聯系客服->工單支持然后選擇自己遇到問題的產品類型提交工單即可,一般兩個小時內可以得到回復


工欲善其事必先利其器,先做准備工作

Step1. 訪問 阿里雲官網,點左上角 登錄
Step2. 登錄視頻直播控制台
在 視頻直播服務產品主頁登錄控制台。控制台會檢查所依賴服務的開通狀態,請按頁面引導操作。
Step3. 在 域名管理 中,新建域名。
直播域名需要進行備案審核,審核通過后即可使用,未備案的域名請先進行備案,備案流程
Step4. CNAME綁定
將您添加的直播域名的DNS CNAME紀錄修改為直播域名管理詳情頁面上顯示的CNAME綁定地址。我們需要把阿里雲提供的推流地址和直播域名進行綁定,這樣當推流到直播域名時會推流到我們的直播中心。
Step4. 獲取推流和播放地址
在 域名管理 中,點擊直播加速域名 管理 :獲取推流和直播地址
Step5. 鑒權配置
直播流媒體的推送和播放采用同一套鑒權方案,可以在控制台的鑒權配置中進行配置,詳細了解鑒權配置。

注意
  • 只有進行鑒權配置后,該加速域名才能正常進行推流和播流,直播業務類型僅支持A類型鑒權方式。
  • 推流和播流地址需要分別進行鑒權簽名計算,每一個簽名都是嚴格按照URL計算的,故不可使用推流URL計算得到的簽名應用到播流地址,同理每一種播流地址都會對應不同的鑒權計算結果。

推流

Step1. 獲取鑒權后的推流地址:
直播控制台 - 域名管理 - 直播域名管理詳情頁 - 基本信息 取得推流地址如下: rtmp://video-center.alivecdn.com/AppName/StreamName?vhost=live.aliyun.com 使用直播控制台 - 域名管理 - 直播域名管理詳情頁 - 鑒權配置 頁面的鑒權URL計算器計算鑒權URL: 輸入推流地址(AppName、StreamName可自行修改)、鑒權KEY、有效時間,點擊<生成>按鈕即可得到鑒權URL。
Step2. 推流操作
推流地址:rtmp://video-center.alivecdn.com/APPName/StreamName?vhost=live.aliyun.com

說明
  • video-center.alivecdn.com是直播中心服務器,允許自定義,例如您的域名是live.aliyun.com(注意:該域名不可以和你的直播加速域名相同),可以設置DNS,將您的域名CNAME指向video-center.alivecdn.com即可。
  • APPName是應用名稱,支持自定義,可以更改。
  • StreamName是流名稱,支持自定義,可以更改。
  • vhost參數是最終在邊緣節點播放的域名,即你的直播加速域名。
    直播推流操作可使用第三方推流軟件,這里介紹 OBS 推流軟件的操作方法
    請到OBS官網下載最新軟件 Mac版本
    以下面的推流地址為例,參數設置為:FMS URL / URL: rtmp://video-center.alivecdn.com/AppName 播放路徑/串碼流(如果存在)/ 流秘鑰: StreamName?vhost=live.aliyn.com 如您開啟了鑒權,則鑒權參數也一並放在 Mac版obs的流密鑰與Windows版播放路徑/串碼流(如果存在)中。
 
obs設置.png
 
new_page_1.png
 
new_page_1-1.png
播放

客戶可以根據實際業務場景靈活搭配使用,需要在移動端瀏覽器、移動H5端進行播放,建議使用HLS(M3U8)方式進行播放,無需集成SDK;非移動端或者已集成SDK的,低並發量並需要有更小的延時,可使用RTMP,高並發量建議使用FLV。
Step1. Web頁面后台直接預覽
使用OBS等工具使用鑒權URL推流后,可在 直播控制台 - 流管理 - 正在推流 頁面查詢到正在直播的推流記錄,通過 直播地址 可查詢播放地址,並可預覽播放。
Step2. 通過VLC預覽
下載VLC
默認安裝后無需做額外設置,文件—>打開網絡串流,填寫播放地址並點擊打開后開始播放。


准備工作完成,接下來開始SDK的接入和依賴庫的導入

iOS 推流SDK開發包
iOS推流使用說明

     目前SDK的橫屏推流需要再推流界面的Controller中將手機豎屏鎖定(只允許Portrait一個方向),如果需要橫豎屏切換,需要對UI做一套橫豎屏的適配,監控手機的狀態並作出相應的布局  

推流的橫豎屏設置為推流的方向,不是手機的方向(下面是推流,直播界面的代碼和設置)

注冊通知,根據不同情況做不同判斷

    [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleDeviceOrientationDidChange:) name:UIDeviceOrientationDidChangeNotification object:nil];//監控手機感應狀態 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appResignActive) name:UIApplicationWillResignActiveNotification object:nil];//退入后台停止推流 因為iOS后台機制,不能滿足充分的攝像頭采集和GPU渲染 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appBecomeActive) name:UIApplicationDidBecomeActiveNotification object:nil];// 回到前台重新推流``` ` #pragma mark - 推流Session 創建 銷毀` 
  • (void)createSession{

    AlivcLConfiguration *configuration = [[AlivcLConfiguration alloc] init];
    configuration.url = @"rtmp://video-center.alivecdn.com/3b42277fcb90445096dd72d4a11ef420/";
    configuration.videoMaxBitRate = 1500 * 1000;
    configuration.videoBitRate = 600 * 1000;
    configuration.videoMinBitRate = 400 * 1000;
    configuration.audioBitRate = 64 * 1000;
    configuration.videoSize = CGSizeMake(360, 640);// 橫屏狀態寬高不需要互換
    configuration.fps = 20;
    configuration.preset = AVCaptureSessionPresetiFrame1280x720;
    configuration.screenOrientation =
    // AlivcLiveScreenVertical = 0,
    // AlivcLiveScreenHorizontal = 1,;
    // 重連時長
    configuration.reconnectTimeout = 5;
    // 水印
    configuration.waterMaskImage = [UIImage imageNamed:@"watermask"];
    configuration.waterMaskLocation = 1;
    configuration.waterMaskMarginX = 10;
    configuration.waterMaskMarginY = 10;
    // 攝像頭方向
    if (self.currentPosition) {
    configuration.position = self.currentPosition;
    // AVCaptureDevicePositionBack 后置
    // AVCaptureDevicePositionFront 前置
    } else {
    configuration.position = AVCaptureDevicePositionFront;
    self.currentPosition = AVCaptureDevicePositionFront;
    }
    configuration.frontMirror = YES;

    // alloc session
    self.liveSession = [[AlivcLiveSession alloc] initWithConfiguration:configuration];
    self.liveSession.delegate = self;
    // 是否靜音推流
    self.liveSession.enableMute = self.muteButton.selected;
    // 開始預覽
    [self.liveSession alivcLiveVideoStartPreview];
    // 開始推流
    [self.liveSession alivcLiveVideoConnectServer];

    NSLog(@"開始推流");

    dispatch_async(dispatch_get_main_queue(), ^{
    // 預覽view
    [self.view insertSubview:[self.liveSession previewView] atIndex:0];
    });

    self.exposureValue = 0;
    }

  • (void)destroySession{
    [self.liveSession alivcLiveVideoDisconnectServer];
    [self.liveSession alivcLiveVideoStopPreview];
    [self.liveSession.previewView removeFromSuperview];
    self.liveSession = nil;
    NSLog(@"銷毀推流");
    }```
    #pragma mark - Notification通知響應

- (void)appResignActive{ // 退入后台停止推流 因為iOS后台機制,不能滿足充分的攝像頭采集和GPU渲染 [self destroySession]; // 監聽電話 _callCenter = [[CTCallCenter alloc] init]; _isCTCallStateDisconnected = NO; _callCenter.callEventHandler = ^(CTCall* call) { if ([call.callState isEqualToString:CTCallStateDisconnected]) { _isCTCallStateDisconnected = YES; } else if([call.callState isEqualToString:CTCallStateConnected]) { _callCenter = nil; } }; NSLog(@"退入后台"); } - (void)appBecomeActive{ if (_isCTCallStateDisconnected) { sleep(2); } // 回到前台重新推流 [self createSession]; NSLog(@"回到前台"); } - (void)handleDeviceOrientationDidChange:(UIInterfaceOrientation)interfaceOrientation { UIDevice *device = [UIDevice currentDevice] ; switch (device.orientation) { case UIDeviceOrientationFaceUp: NSLog(@"屏幕朝上平躺"); break; case UIDeviceOrientationFaceDown: NSLog(@"屏幕朝下平躺"); break; case UIDeviceOrientationUnknown: NSLog(@"未知方向"); break; case UIDeviceOrientationLandscapeLeft: { NSLog(@"屏幕向左橫置"); [self destroySession];//摧毀推流 _isScreenHorizontal = YES; // 橫屏 YES [self createSession]; //重新推流 } break; case UIDeviceOrientationLandscapeRight: NSLog(@"屏幕向右橫置"); break; case UIDeviceOrientationPortrait: { NSLog(@"屏幕直立"); [self destroySession]; // 摧毀推流 _isScreenHorizontal = NO; //豎屏為NO [self createSession]; // 重新推流 } break; case UIDeviceOrientationPortraitUpsideDown: NSLog(@" 屏幕直立 上下顛倒"); break; default: NSLog(@"無法辨認"); break; } }``` `//推流代理,根據實際情況做出反應` 

pragma mark - AlivcLiveVideo Delegate

  • (void)alivcLiveVideoLiveSession:(AlivcLiveSession *)session error:(NSError *)error{

    dispatch_async(dispatch_get_main_queue(), ^{
    NSString *msg = [NSString stringWithFormat:@"%zd %@",error.code, error.localizedDescription];
    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Live Error" message:msg delegate:nil cancelButtonTitle:@"取消" otherButtonTitles:@"重新連接", nil];
    alertView.delegate = self;
    [alertView show];
    });

    NSLog(@"liveSession Error : %@", error);
    }

  • (void)alivcLiveVideoLiveSessionNetworkSlow:(AlivcLiveSession *)session {

    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"提示" message:@"當前網絡環境較差" delegate:nil cancelButtonTitle:@"確定" otherButtonTitles: nil];
    [alertView show];
    self.textView.text = @"網速過慢,影響推流效果,拉流端會造成卡頓等,建議暫停直播";
    NSLog(@"網速過慢");

}

  • (void)alivcLiveVideoLiveSessionConnectSuccess:(AlivcLiveSession *)session {

    NSLog(@"推流 connect success!");
    }

  • (void)alivcLiveVideoReconnectTimeout:(AlivcLiveSession *)session error:(NSError *)error {
    dispatch_async(dispatch_get_main_queue(), ^{
    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"提示" message:[NSString stringWithFormat:@"重連超時-error:%ld", error.code] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil];

      [alertView show]; 

    });
    NSLog(@"重連超時");
    }

  • (void)alivcLiveVideoOpenAudioSuccess:(AlivcLiveSession *)session {
    // dispatch_async(dispatch_get_main_queue(), ^{
    // UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"YES" message:@"麥克風打開成功" delegate:nil cancelButtonTitle:@"確定" otherButtonTitles: nil];
    // [alertView show];
    // });
    }

  • (void)alivcLiveVideoOpenVideoSuccess:(AlivcLiveSession *)session {
    // dispatch_async(dispatch_get_main_queue(), ^{
    // UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"YES" message:@"攝像頭打開成功" delegate:nil cancelButtonTitle:@"確定" otherButtonTitles: nil];
    // [alertView show];
    // });
    }

  • (void)alivcLiveVideoLiveSession:(AlivcLiveSession *)session openAudioError:(NSError *)error {
    dispatch_async(dispatch_get_main_queue(), ^{
    // UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Error" message:@"麥克風獲取失敗" delegate:nil cancelButtonTitle:@"確定" otherButtonTitles: nil];
    // [alertView show];
    });
    }

  • (void)alivcLiveVideoLiveSession:(AlivcLiveSession *)session openVideoError:(NSError *)error {

    // dispatch_async(dispatch_get_main_queue(), ^{
    // UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Error" message:@"攝像頭獲取失敗" delegate:nil cancelButtonTitle:@"確定" otherButtonTitles: nil];
    // [alertView show];
    // });
    }

  • (void)alivcLiveVideoLiveSession:(AlivcLiveSession *)session encodeAudioError:(NSError *)error {
    // dispatch_async(dispatch_get_main_queue(), ^{
    // UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Error" message:@"音頻編碼初始化失敗" delegate:nil cancelButtonTitle:@"確定" otherButtonTitles: nil];
    // [alertView show];
    // });

}

  • (void)alivcLiveVideoLiveSession:(AlivcLiveSession *)session encodeVideoError:(NSError *)error {
    // dispatch_async(dispatch_get_main_queue(), ^{
    // UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Error" message:@"視頻編碼初始化失敗" delegate:nil cancelButtonTitle:@"確定" otherButtonTitles: nil];
    // [alertView show];
    // });
    }

  • (void)alivcLiveVideoLiveSession:(AlivcLiveSession *)session bitrateStatusChange:(ALIVC_LIVE_BITRATE_STATUS)bitrateStatus {

    // dispatch_async(dispatch_get_main_queue(), ^{
    // UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"YES" message:[NSString stringWithFormat:@"ALIVC_LIVE_BITRATE_STATUS = %ld", bitrateStatus] delegate:nil cancelButtonTitle:@"確定" otherButtonTitles: nil];
    // [alertView show];
    // });
    NSLog(@"碼率變化 %ld", bitrateStatus);
    }

  • (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
    if (buttonIndex != alertView.cancelButtonIndex) {
    [self.liveSession alivcLiveVideoConnectServer];
    } else {
    [self.liveSession alivcLiveVideoDisconnectServer];
    }
    }


>    視頻播放
>> AlivcMediaPlayer
`iOS媒體播放器SDK是在iOS平台上使用的軟件開發工具包(Software Developement Kit),為iOS開發者提供簡單易用的接口,幫助開發者實現iOS平台上的媒體播放應用開發。該SDK對目前主流的視頻格式都提供了良好的支持,支持本地和網絡媒體的播放,彌補了系統播放器在媒體格式上的不足。(記得添加AliyunPlayerSDK.framework)
如果需要適配橫豎屏要對UI做適配,只做橫屏的話可以將頁面旋轉90度即可 ` ###### 播放器通知定義: AliVcMediaPlayerLoadDidPreparedNotification:播放器初始化視頻文件完成通知,調用prepareToPlay函數,會發送該通知,代表視頻文件已經准備完成,此時可以在這個通知中獲取到視頻的相關信息,如視頻分辨率,視頻時長等。 AliVcMediaPlayerPlaybackDidFinishNotification:播放完成通知。當視頻播放完成后會收到此通知。播放完成會有幾種情況, 1. 當用戶調用stop后視頻結束完成。 2. 視頻正常播放結束。 ` AliVcMediaPlayerStartCachingNotification:播放器開始緩沖視頻時發送該通知,當播放網絡文件時,網絡狀態不佳或者調用seekTo時,此通知告訴用戶網絡下載數據已經開始緩沖。 AliVcMediaPlayerEndCachingNotification:播放器結束緩沖視頻時發送該通知,當數據已經緩沖完,告訴用戶已經緩沖結束,來更新相關UI顯示。 AliVcMediaPlayerPlaybackErrorNotification:播放器播放失敗發送該通知,並在該通知中可以獲取到錯誤碼。 AliVcMediaPlayerSeekingDidFinishNotification:播放器位置改變完成后發送該通知。 AliVcMediaPlayerFirstFrameNotification:播放器狀態首幀顯示后發送的通知。` 播放器通知發送邏輯: 1. 調用prepareToPlay成功后發送AliVcMediaPlayerLoadDidPreparedNotification通知,失敗則會發送AliVcMediaPlayerPlaybackErrorNotification。 2. 調用play、pause、stop、prepareToPlay、seekTo失敗后發送AliVcMediaPlayerPlaybackErrorNotification通知。 3. 調用stop/reset成功后播放視頻結束發送AliVcMediaPlayerPlaybackDidFinishNotification通知,播放器自動播放結束也會發送AliVcMediaPlayerPlaybackDidFinishNotification通知。 4. 調用seekTo成功后發送AliVcMediaPlayerSeekingDidFinishNotification通知。 5. AliVcMediaPlayerStartCachingNotification和AliVcMediaPlayerEndCachingNotification通知,這個是在網絡視頻緩沖數據不足以夠播放后會發送此通知,一般網絡視頻在調用seekTo后會發送此通知。` ` //初始化播放器的類` 

-(void) playVideo
{

player = [[AliVcMediaPlayer alloc] init];
//創建播放器,傳入顯示窗口
/**

  • 功能:創建播放器,並設置播放器顯示窗口。播放器內部會新建各個播放器變量並初始化,並啟動播放器內部流水線線程等。
  • 參數:UIView* view,播放器顯示窗口
  • 備注:如果創建播放器的時候view沒有,則可以傳遞nil,可以在后續需要設置view。
    */
    [player create:mShowView];
    //注冊准備完成通知
    [[NSNotificationCenter defaultCenter] addObserver:self
    selector:@selector(OnVideoPrepared:) name:AliVcMediaPlayerLoadDidPreparedNotification object:player];
    //注冊錯誤通知
    [[NSNotificationCenter defaultCenter] addObserver:self
    selector:@selector(OnVideoError:) name:AliVcMediaPlayerPlaybackErrorNotification object:player];
    //傳入播放地址,准備播放
    [player prepareToPlay:mUrl];
    //開始播放
    [player play];
    }


作者:daihz
鏈接:https://www.jianshu.com/p/714ce954e628
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯系作者獲得授權並注明出處。


免責聲明!

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



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