本节主要为Link Visual iOS版本的音视频模块使用说明,包含视频播放、语音对讲的功能。

依赖SDK 概述
API 通道 提供API通道能力
长连通道 P2P需要长连接通道

初始化

在初始化SDK前,需要正确的配置安全图片,请参见集成安全图片

工程配置

  • 通过CocoaPods工具
    1. podfile中添加阿里源
      source 'https://github.com/aliyun/aliyun-specs.git'
    2. 添加库依赖
      pod 'CocoaAsyncSocket', '7.4.2'
      pod 'IMSLinkVisualMedia', '1.0.0'
    3. 执行pod update,则库安装完毕
  • 本地库项目引入(本地包请使用该方式集成,例如本地包为podspec)
    1. podfile中添加本地路径
      pod 'IMSLinkVisualMedia', :path => 'LocalPods/IMSLinkVisualMedia.podspec'
    2. 拷贝以下SDK文件至项目内LocalPods目录下SDK路径
    3. podspec拷贝以下文件至项目内LocalPods目录下
      IMSLinkVisualMedia.podspec
      IMSLinkVisualMedia//文件夹 IMSLinkVisualMedia文件夹内包含以下framework
      
      IMSLinkVisualMedia.framework
      LibRtmp.framework
      LinkVisualClientSDK.framework
      FFmpeg.framework
    4. 执行pod update,则库安装完毕

使用说明

  • 视频播放器

    播放器按功能分为三种,提供了以下功能。

    • 直播播放器

      用于RTMP直播源,具有时延低的特点,支持P2P(需使用接入LinkVisual设备端SDK的摄像头)。

    • 点播播放器

      用于设备录像回放,可调整播放进度。

    • HLS播放器

      用于云端录像回放。

    FFmpeg使用版本为n4.0.1

    音频编码支持AAC_LC和G711a

    视频编码支持H264/H265

    iOS SDK封装AVPlayer,功能列表如下。

    功能 直播播放器 设备录像播放器 HLS播放器
    视频播放
    音频播放
    暂停/恢复 x
    跳至指定位置播放 X
    总时长 x
    当前播放进度 x
    播放器状态变更通知
    静音
    变速播放 x x x
    循环播放 x x x
    画面缩放模式设置 x x x
    播放器截图
    边播边录 x

使用指南

//框架引入
#import <IMSLinkVisualMedia/IMSLinkVisualMedia.h>
  • 直播播放器
     //创建播放器
        IMSLinkVisualPlayerViewController *player = [[IMSLinkVisualPlayerViewController alloc] init];
        [self addChildViewController:player];
        //打开播放器日志  默认为 IMSLinkVisualMediaLogInfo 
        player.playerLog = IMSLinkVisualMediaLogInfo;
        //设置播放器frame
        player.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
        player.view.frame = self.view.bounds;
        //添加播放器view到指定位置
        [self.view insertSubview:player.view atIndex:0];
        //播放器代理
        player.delegate = self;
        //设置播放器参数
        [player setDataSource:self.iotId streamType:IMSLinkVisualPlayerLiveStreamTypeMain encryptType:0 needForceIFrame:true];
        //开始播放
        [player start];
        //停止播放 ,释放播放器,
        [player stop];
  • 点播播放器
    //创建播放器
        IMSLinkVisualPlayerViewController *player = [[IMSLinkVisualPlayerViewController alloc] init];
        [self addChildViewController:player];
        //设置播放器frame
        player.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
        player.view.frame = self.view.bounds;
        //添加播放器view到指定位置
        [self.view insertSubview:player.view atIndex:0];
        //播放器代理
        player.replayDelegate = self;
        player.delegate = self;
        //设置播放器参数 vodStartTime为当天0点时间戳,vodEndTime为当天24点-1秒时间戳 seekTime为本次播放的起始时间(秒)
        [player setDataSource:iotId vodStartTime:timestamp vodEndTime:timestamp + dayOfSecond encryptType:0 seekTime:time];
        //开始播放
        [player start];
        //录像时间跳转 seekTime 为本天需要跳转的秒数
        [player seekToDuration:seekTime];
        //暂停播放
        [player pause];
        //暂停之后恢复播放
        [player restore];
        //停止播放 ,释放播放器,
        [player stop];
  • HLS播放器
    //创建播放器
        hlsPlayer = [IMSLinkVisualHLSPlayer new];
        //设置播放器frame
        hlsPlayer.frame = self.view.bounds;
        hlsPlayer.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
         //添加播放器view到指定位置
        [self.view insertSubview:hlsPlayer atIndex:0];
        //播放器代理
        hlsPlayer.delegate = self;
       //设置播放器参数 file.fileName 为云端录像返回参数
       [hlsPlayer playWithFileName:file.fileName iotId:iotId];
        //开始播放
        [hlsPlayer play];
        //录像时间跳转 seekTime 为本段视频需要跳转的秒数
        [hlsPlayer seekToTime:CMTimeMake(seekTime, 1)];
        //暂停播放
        [hlsPlayer pause];
        //停止播放 ,释放播放器,
        [hlsPlayer stop];
  • 直播与点播的播放器状态
    /// 播放器状态
    typedef NS_ENUM(NSUInteger,IMSLinkVisualPlayerState){
        /// 空闲(播放器未开启,stop,释放)
        IMSLinkVisualPlayerStateIdle,
        /// 缓冲中(播放器start之后未开始播放前的状态)
        IMSLinkVisualPlayerStateBuffering,
        /// 开始播放(播放已经缓冲完毕,正在播放,未暂停)
        IMSLinkVisualPlayerStateStartPlay,
        /// 暂停播放(播放器正在暂停)
        IMSLinkVisualPlayerStatePausePlay
    };
  • HLS的播放器状态
    typedef NS_ENUM(NSInteger, IMSLinkVisualHLSPlayerStatus) {
        /// 空闲
        IMSLinkVisualHLSPlayerIdle,
        /// 加载中
        IMSLinkVisualHLSPlayerLoading,
        /// 加载完毕
        IMSLinkVisualHLSPlayerReady,
        /// 播放中
        IMSLinkVisualHLSPlayerPlaying,
        /// 暂停
        IMSLinkVisualHLSPlayerPause,
        /// 播放结束
        IMSLinkVisualHLSPlayerFinished,
    };
    					

接口说明

  • 设置数据源(URL)
    /**
     设置RTMP数据源、默认不加密
    
     @see IMSLinkVisualPlayerSourceType
     @param rtmpUrl RTMP格式的url
     @param sourceType 播放器类型
     @return 数据源是否设置成功
     */
    
    - (BOOL)setDataSource:(NSString *_Nullable)rtmpUrl
               sourceType:(IMSLinkVisualPlayerSourceType)sourceType;
    					
  • 设置数据源(直播业务)

    视频播放器业务依赖于账号和用户 SDK 的初始化。

    /**
         * 设置IPC直播数据源.
         *
         * @param iotId                    IPC设备的iotId
         * @param streamType               流的类型: 0-主码流,1-辅码流
         * @param encryptType             云转加密类型: 0-AES128
         * @param needForceIFrame          是否需要强制I帧
         */
    [self.player setDataSource:self.iotId streamType:IMSLinkVisualPlayerLiveStreamTypeMain];
    
    - (BOOL)setDataSource:(NSString *)iotId
               streamType:(IMSLinkVisualPlayerLiveStreamType)streamType
              encryptType:(NSInteger)encryptType
          needForceIFrame:(BOOL)needForceIFrame;
  • 设置数据源(录播业务)

    视频播放器业务依赖于账号和用户 SDK 的初始化。

    /**
         * 设置播放源为IPC录像(按录像文件名)
         *
         * @param iotId                         IPC设备的iotId
         * @param vodFileName           请求录像的文件名称
         */
    [self.player setDataSource:self.iotId vodFileName:record.fileName];
    /**
         * 设置播放地址为已接入飞燕的IPC设备指定录像时间段的本地录像文件地址.
         *
         * @param iotId                         IPC设备的iotId
         * @param vodStartTime          录像开始时间,1970年1月1日开始的秒数
         * @param vodEndTime         录像结束时间,1970年1月1日开始的秒数
         * @param encrypted             是否流需要加密, 强烈建议开启,对于出海产品,务必保持开启
         * @param encryptType         云转加密类型: 目前只支持0(AES-128)加密
         * @param seekTime              相对于beginTime的起始播放偏移量,单位为秒
         */
    [player  setDataSource:iotId
                   vodStartTime:self.record.beginTime
                      vodEndTime:self.record.endTime
                      needEncrypt:NO
                      encryptType:0
                           seekTime:seekTime];
  • 设置播放器缓存
    #import <IMSLinkVisualMedia/IMSLinkVisualMedia.h>
    /**
     //设置播放器缓存 缓存越大,延迟越高 一帧缓存延迟40ms 默认为5帧
     @param buffer 缓存大小 0 -- 16
     */
    [self.player setDisplyBuffer:10];
  • 开始播放
    #import <IMSLinkVisualMedia/IMSLinkVisualMedia.h>
    
    // 开始播放
    [self.player start];
  • 停止播放
    #import <IMSLinkVisualMedia/IMSLinkVisualMedia.h>
    
    // 停止播放,结束播放器
    [self.player stop];
    					
  • 暂停播放(仅录播)
    #import <IMSLinkVisualMedia/IMSLinkVisualMedia.h>
    
    // 暂停播放
    [self.player pause];
  • 恢复播放(仅录播)
    #import <IMSLinkVisualMedia/IMSLinkVisualMedia.h>
    
    // 恢复播放
    [self.player restore];
  • 监听播放器状态(代理)
    #pragma mark - IMSLinkVisualDelegate
    #pragma mark 播放器建立连接
    
    -(void)linkVisual:(IMSLinkVisualPlayerViewController *_Nullable)client didConnect:(IMSLinkVisualPlayerError)status {
        if (IMSLinkVisualPlayerSuccess == status) {
        }else{
        }
    }
    
    
    #pragma mark 播放器播放成功
    
    -(void)linkVisual:(IMSLinkVisualPlayerViewController *_Nullable)client didCompletePaly:(BOOL)success {
        if (success) {
        }else{
        }
    }
    #pragma mark 播放器停止
    
    - (void)linkVisual:(IMSLinkVisualPlayerViewController *)client didStop:(BOOL)success{
        if (success) {
        }
    }
    #pragma mark 播放器回到前台,reStart 重新自动播放
    
    - (void)linkVisual:(IMSLinkVisualPlayerViewController *)client becomeActive:(BOOL)reStart{
        if (reStart) {
        }
    }
    #pragma mark 播放器状态变更
    
    - (void)linkVisual:(IMSLinkVisualPlayerViewController * _Nullable)client newStatus:(IMSLinkVisualPlayerState)newStatus oldStatus:(IMSLinkVisualPlayerState)oldStatus {
    
    }
    #pragma mark 播放器错误回调
    
    - (void)linkVisual:(IMSLinkVisualPlayerViewController * _Nullable)client clientError:(NSError*)error {
    
    }
    					
  • 监听录像播放器状态(代理)(录像播放器同样拥有播放器回调)
    #pragma mark - IMSLinkVisualReplayDelegate
    #pragma mark 播放器时间回调
    
    - (void)linkVisualReplay:(IMSLinkVisualPlayerViewController *)player currentTime:(NSInteger)currentTime{
    }
    #pragma mark 播放器seek完毕
    
    - (void)linkVisualReplaySeekComplete:(IMSLinkVisualPlayerViewController *)player{
    }
    #pragma mark 播放器播放完毕
    
    - (void)linkVisualReplayOver:(IMSLinkVisualPlayerViewController * _Nullable)player { 
    }
  • 设置播放地址为IPC云存录像(按文件名,如hls)
    #import <IMSLinkVisualMedia/IMSLinkVisualMedia.h>
    
    // 设置播放路径为云端获得fileName, 设备iotId
     [self.player playWithFileName:file.fileName iotId:iotId];
  • 设置播放URL路径(hls)
    #import <IMSLinkVisualMedia/IMSLinkVisualMedia.h>
    
    // 设置播放路径为获得url地址
     [self.player playWithRequestUrl:requestUrl];
  • 开始播放(hls)
    #import <IMSLinkVisualMedia/IMSLinkVisualMedia.h>
    
    // 播放
    [self.player play];
  • 暂停播放(hls)
    #import <IMSLinkVisualMedia/IMSLinkVisualMedia.h>
    
    // 暂停播放
    [self.player pause];
  • 监听HLS播放器状态(代理)
    #pragma mark - IMSLinkVisualHLSPlayerDelegate
    
    #pragma mark 播放器时间回调
    
    - (void)linkVisualHLSPlayer:(IMSLinkVisualHLSPlayer*)player currentTime:(NSTimeInterval)currentTime {
    
    }
    #pragma mark 播放器加载失败
    
    - (void)linkVisualHLSPlayerFailed:(IMSLinkVisualHLSPlayer *)player {
    }
    #pragma mark 播放器播放完毕
    
    - (void)linkVisualHLSPlayerFinished:(IMSLinkVisualHLSPlayer *)player {
    }
    #pragma mark 播放器加载完成
    
    - (void)linkVisualHLSPlayerReadyToPlay:(IMSLinkVisualHLSPlayer *)player {
    
    }
    #pragma mark 播放器seek完毕
    
    
    - (void)linkVisualHLSPlayerSeek:(IMSLinkVisualHLSPlayer *)player completion:(BOOL)finished {
    
    }
  • 监听播放器状态(通知)
    #import <IMSLinkVisualMedia/IMSLinkVisualMedia.h>
    
    //监听播放器状态变更
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerStateChangeNotification:) name:IMSLinkVisualPlayerStateChangeNotification object:nil];
    
    //监听播放器错误
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerErrorNotification:) name:IMSLinkVisualPlayerErrorNotification object:nil];
    
    //监听播放器结束通知
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerStateEndNotification:) name:IMSLinkVisualPlayerStateEndNotification object:nil];
    
    //播放器状态变更回调
    
    - (void)playerStateChangeNotification:(NSNotification *)notification {
        if (notification.object != self.player) {
            return;
        }
    
        IMSLinkVisualPlayerState newState = [notification.userInfo[IMSLinkVisualPlayerNewStateKey] unsignedIntegerValue];
    
        switch (newState) {
            // 空闲
            case IMSLinkVisualPlayerStateIdle:
            // 缓冲中
            case IMSLinkVisualPlayerStateBuffering:
            // 开始播放
            case IMSLinkVisualPlayerStateStartPlay:
            // 暂停播放
            case IMSLinkVisualPlayerStatePausePlay:
        }    
    }
    
    //播放器错误通知回调
    
    - (void)playerErrorNotification:(NSNotification *)notification {
        if (notification.object != self.player) {
            return;
        }
        // 错误内容
        NSError *error = notification.userInfo[IMSLinkVisualPlayerErrorKey];
        // 错误代码
        IMSLinkVisualPlayerError errorCode = error.code;
    
    }
    
    //播放器播放结束通知回调
    
    - (void)playerStateEndNotification:(NSNotification *)notification {
        if (notification.object != self.player) {
            return;
        }
    }
  • 视频截图
    #import <IMSLinkVisualMedia/IMSLinkVisualMedia.h>
    
    //获取视频截图
    UIImage *image = [self.player videoSnapshot];
  • 边录边播
    #import <IMSLinkVisualMedia/IMSLinkVisualMedia.h>
    
    - (IBAction)recordVideoButtonClick:(UIButton *)sender {
        sender.selected = !sender.selected;
        NSString *tmpPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"tmp.mp4"];
    
        if (sender.selected) {
            [self.player startRecordVideoWithfilePath:tmpPath];
        } else {
            [self.player stopRecordVideo];
        }
    }

播放器错误列表

错误主码(PlayerMainError) 描述 子码(解析userInfo中subCode获取) 描述
ErrorSource 数据源相关错误 IMSLinkVisualPlayerErrorConnect (1005) 与数据源建立连接失败
IMSLinkVisualPlayerErrorEncrypt(1006) 无效的解密密钥
IMSLinkVisualPlayerErrorUrl (1007) 无效的播放地址
IMSLinkVisualPlayerErrorDataSource(1008) 数据源错误或未设置
IMSLinkVisualPlayerErrorStart (1009) 请求播放地址失败
ErrorRender 渲染相关错误渲染相关错误 IMSLinkVisualPlayerErrorDecode(1000) 解码错误
ErrorUnexpected 不符合预期错误 IMSLinkVisualPlayerErrorStream(1100) 拉流失败,8S未拉取到流或连接被异常断开

HLS播放器错误列表

hls播放器错误码 描述
IMSLinkVisualPlayerErrorUrl (_1007)_ 无效的播放地址
IMSLinkVisualPlayerErrorStart (_1009)_ 请求播放地址失败
IMSLinkVisualPlayerErrorDataSource (_1008)_ 数据源错误或未设置
IMSLinkVisualPlayerErrorConnect (_1005)_ 拉流失败,未拉取到流或连接被异常断开
IMSLinkVisualPlayerErrorStream(_1100)_ 流连接失败(一段时间内没收到流,如20秒)

语音对讲

提供App和IPC设备之间端到端的双向实时音频传输能力。

语音对讲支持双工模式,App端需要实现以下流程。

App端需实现的流程
支持的音频格式 编码 解码
PCM - -
AAC_LC
G711A
G711U

使用指南

  • 创建语音对讲编码参数

    参数为录制语音数据的参数,用来与设备端对齐。

    IMSLinkVisualAudioParams *intercomEncodeParams = [[IMSLinkVisualAudioParams alloc] init];
        intercomEncodeParams.sampleRate = 8000;
        intercomEncodeParams.channel = 1;
        intercomEncodeParams.bitsPerSample = 16;
        intercomEncodeParams.format = IMSLinkVisualAudioFormatG711a;
  • 设置语音对讲参数

    创建编码器用于编码语音数据。

    //     player 为播放器实例 IMSLinkVisualPlayerViewController
        player.intercomEncodeParams = intercomEncodeParams;
  • 设置语音对讲回调代理

    用于语音对讲开始与结束流程的消息回调代理,开始对讲前,先增加语音对讲状态回调方法。

     player.intercomDelegate = self;
  • 开语音对讲

    设置播放器为对讲模式并开启对讲功能。

    [player startIntercom:IMSLinkVisualIntercomAudioModeIntercom];
  • 停止语音对讲
     [player stopIntercom];

语音对讲状态回调方法与触发规则如下。

#pragma mark - IMSLinkVisualIntercomDelegate
#pragma mark 语音对讲连接服务器
//语音对讲与设备端建立连接时触发

- (void)linkVisualIntercomDidConnect:(IMSLinkVisualPlayerViewController *)player {
    //语音对讲连接服务器成功
}
#pragma mark 语音对讲ready
//当对讲连接于服务端连接建立后,若对端已就绪,会告知本端事件talk ready. 此时向对端发送音频数据都会被对端收到并处理.

- (void)linkVisualIntercomDidReady:(IMSLinkVisualPlayerViewController *_Nullable)player
                             error:(NSError *_Nullable)error{
}

#pragma mark 语音对讲停止
//语音对讲结束时触发

- (void)linkVisualIntercomDidStop:(IMSLinkVisualPlayerViewController *)player
                            error:(NSError *)error {
    //语音对讲结束
    if (error) {
    //关闭对讲发生错误
    }
}

#pragma mark 语音对讲出错
//在语音对讲通道建立以及对讲中时都可能发生错误,需要处理,错误类型详见错误列表.

- (void)linkVisualIntercom:(IMSLinkVisualPlayerViewController *)player
                 errorOccurred:(NSError *)error {
    //语音对讲出错
    if (error) {
        //code错误码分析错误发生原因
    }
}

#pragma mark 对讲接收到设备端音频参数
//当语音对讲通道建立后,若对端支持录音,会先收到对端发送过来的音频参数信息,
//后续对端发送的音频数据按照此音频参数来做解码. 该事件是语音对讲通道建立成功的标志. 
//可以在此时构建音频播放器实例用于对端采集音频的实时播放.

- (void)linkVisualIntercom:(IMSLinkVisualPlayerViewController *)player
     didReceiveAudioParams:(IMSLinkVisualAudioParams *)params {
}

#pragma mark 对讲接收到设备端音频数据
//若对端支持录音,语音对讲中会持续不断的收到对端发送过来的音频数据.

- (void)linkVisualIntercom:(IMSLinkVisualPlayerViewController *)intercom didReceiveData:(NSData *)data {
    if (data) {
        //playBuffers为自定义可变数组收集数据
        [self.playBuffers addObject:data];
    }
}

#pragma mark 录音数据完成回调
//语音对讲开始后,会收集不断收到mic采集的数据,根据对讲的状态开始发送的数据

- (void)linkVisualIntercom:(IMSLinkVisualPlayerViewController *)intercom didRecordData:(NSData *)data{
    if (data) {
        //发送录音数据 audioBuffers为自定义可变数组收集数据
        [player sendAudioData:audioData];
    }
}   

错误列表

语音对讲错误状态码 描述
IMSLinkVisualIntercomErrorParams 对讲参数错误
IMSLinkVisualIntercomErrorStart 启动对讲失败
IMSLinkVisualIntercomErrorConnect 语音流建立失败
IMSLinkVisualIntercomErrorSendData 发送语音数据失败
IMSLinkVisualIntercomErrorReceiveData 接收语音数据失败