跳到主要内容

音视频播放

更新时间:2025-04-22 14:25:13

RTCXMedia 提供设备的直播,点播,截图,录制,音频对讲等相关功能

// 导入媒体播放SDK头文件
#import <RTCXMedia/RTCXMedia.h>

播放器初始化

@interface LVPlayInfo : NSObject
// 设备型号 如:xLeWcoelZbyOePbJludl
@property (nonatomic, copy) NSString *productKey;
// 设备序列号SN 如:TEST20240820002
@property (nonatomic, copy) NSString *deviceName;
// 平台为设备颁发的ID,设备的唯一标识符
@property (nonatomic, copy) NSString *iotId;

@end

@interface LVPlayerView : UIView
/**
player相关接口调用
*/
@property (nonatomic, strong) LVPlayerController *playerController;

/**
初始化View
@param frame 需要展示视频的frame
@param playInfo 播放设备信息
*/
- (id)initWithFrame:(CGRect)frame withPlayInfo:(LVPlayInfo *)playInfo;
@end

调用示例如下:

LVPlayInfo *playInfo = [[LVPlayInfo alloc] init];
playInfo.iotId = self.curDevice.iotId;
playInfo.productKey = self.curDevice.productKey;
playInfo.deviceName = self.curDevice.deviceName;
// 初始化播放器,传入frame和设备model
self.playerView = [[LVPlayerView alloc] initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_WIDTH * 9.0 / 16.0) withDevice:playInfo];
self.playerView.playerController.delegate = self;// 创建player控制类并设置代理监听player状态,playeview.playerController会懒加载创建控制类
self.playerView.playerController.dataSource = self;// 设置playeview.playerController的dataSource
// 初始化player播放的视图,可以把该playerView加到任意view上
[self.view addSubview:self.playerView];

直播

@protocol LVPlayerInterface <NSObject>
/**
直播调用
*/
- (void)start;
@end

调用示例如下:

// 开始直播
[self.playerView.playerController start];

云/SD 卡回放

@protocol LVPlayerInterface <NSObject>
/**
回放调用

@param playbackInfo 回放参数
*/
- (void)seekToTime:(LVPlaybackInfo*)playbackInfo;
@end

调用示例如下:

// 云回放或SD卡回放
LVPlaybackInfo *playbackInfo = [[LVPlaybackInfo alloc] init];
playbackInfo.startTime = [startTime timeIntervalSince1970];
playbackInfo.endTime = [startTime timeIntervalSince1970];
playbackInfo.playbackType = _isPlaybackSDCard?IOTPlaybackTypeSDCardRecord:IOTPlaybackTypeCloudRecord;
[self.playerView.playerController seekToTime:playbackInfo];

监听播放状态

/// 播放器状态回调
typedef NS_ENUM(NSInteger, LVCameraPlayerState) {
// 播放初始化播放未开始
LVCameraPlayerStateInitial = -1,
// 播放准备就绪
LVCameraPlayerStatePrepared = 0,
// 播放状态
LVCameraPlayerStatePlaying,
// 开始图像渲染
LVCameraPlayerStateRenderingStart,
// 暂停状态
LVCameraPlayerStatePaused,
// 缓冲状态开始
LVCameraPlayerStateBuffingStart,
// 缓冲状态结束
LVCameraPlayerStateBuffingEnd,
// 事件,剪辑视频等,播放完毕
LVCameraPlayerStatePlaybackCompleted,
// 播放停止
LVCameraPlayerStateStopped,
/// 播放错误
LVCameraPlayerStateError
};

@protocol LVPlayerDelegate <NSObject>
/**
player播放状态回调

@param playState player状态(LVCameraPlayerState)
@param code player错误码
*/
- (void)onPlayStateChanged:(LVCameraPlayerState)playState code:(int)code;
@end

调用示例如下:

#pragma mark -- LVPlayerDelegate --
- (void)onPlayStateChanged:(LVCameraPlayerState)playState code:(int)code {
COMMON_PRINTF(@"+++++++直播页播放状态:%d, code: %d",playState,code);
dispatch_async(dispatch_get_main_queue(), ^{
switch (playState) {
case LVCameraPlayerStatePrepared:
[self.livePlayerView setWaitingAnimation:YES];
break;
case LVCameraPlayerStatePlaying:
[self.livePlayerView setWaitingAnimation:NO];
break;
case LVCameraPlayerStateRenderingStart:
[self.livePlayerView setWaitingAnimation:NO];
break;

case LVCameraPlayerStateBuffingStart:
[self.livePlayerView setWaitingAnimation:YES];
break;
case LVCameraPlayerStateBuffingEnd:
[self.livePlayerView setWaitingAnimation:NO];
break;
case LVCameraPlayerStateStopped:
[self.livePlayerView setWaitingAnimation:NO];
break;
default:
break;
}
});
}

播放器销毁

当不再需要播放的时候,要销毁播放器

@protocol LVPlayerInterface <NSObject>
/**
释放player(不再需要时调用释放)
*/
- (void)releasePlayer;
@end

调用示例如下:

// 销毁播放器
[self.playerView.playerController releasePlayer];

截图

@protocol LVPlayerInterface <NSObject>
/**
截图

@param bOriginalPic 是否原始尺寸
@param isAdd 是否添加时间水印
@return image
*/
- (UIImage *)snapshot:(BOOL)bOriginalPic isAddTimeWatermark:(BOOL)isAdd;
@end

调用示例如下:

// 截图
UIImage *image = [self.playerView.playerController snapshot:YES isAddTimeWatermark:NO];

静音

@protocol LVPlayerInterface <NSObject>
/**
设置播放是否静音

@param mute 是否静音 YES:静音 NO:非静音
*/
- (void)setMuteRemoteAudio:(BOOL)mute;
@end

调用示例如下:

// 是否静音 YES/NO
[self.mainPlayer.playerController setMuteRemoteAudio:!on];

倍速播放

注意
  1. SD卡非1倍速播放时,是不支持静音设置和视屏录制的
@protocol LVPlayerInterface <NSObject>
/**
倍速播放

@param rate 0.5、1、2、4等
*/
- (void)setPlaybackSpeed:(float)rate;
@end

调用示例如下:

 // 设置倍速
[self.mainPlayer.playerController setPlaybackSpeed:rate];

边播边录

@protocol LVPlayerInterface <NSObject>
/**
边播边录

@param path 保存文件夹
@param isAdd 是否添加时间水印
@return image
*/
- (BOOL)startRecord:(NSString *)path isAddTimeWatermark:(BOOL)isAdd;
/**
停止边播边录
*/
- (BOOL)stopRecord;
@end

@protocol LVPlayerDelegate <NSObject>
@optional
/**
视频录制回调

@param currentTime 回调当前录制时间
@param filePath 回调数据路径
@param error 视频录制错误
*/
- (void)onPlayerMp4Record:(NSInteger)currentTime filePath:(NSString *)filePath error:(NSError *)error;
@end

调用示例如下:

// 边播边录
- (BOOL)functionRecordingWhilePlaying:(BOOL)on {
if (on) {
BOOL result = [self.playerView.playerController startRecord:[self fileName] isAddTimeWatermark:NO];
[self showToast:result? @"边播边录成功":@"边播边录失败"];
return result;
} else {
return [self.playerView.playerController stopRecord];
}
}

#pragma mark -- LVPlayerDelegate --
- (void)onPlayerMp4Record:(NSInteger)currentTime filePath:(NSString *)filePath error:(NSError *)error {
COMMON_PRINTF(@"视屏录制中。。。:%d, filePath: %@, error:%@",currentTime,filePath,error);
if (filePath) {
//录制完成,保存到手机相册
self.recordTimeL.hidden = YES;
self.recordTimeL.text = @"";
UISaveVideoAtPathToSavedPhotosAlbum(filePath, nil, nil, nil);
[self showToast:[NSString stringWithFormat:@"record save success"]];
} else {
//显示录制进度
self.recordTimeL.hidden = NO;
self.recordTimeL.text = millisecondsToTimeString(currentTime);
}
}

音频通话(语音对讲)

typedef NS_ENUM(NSInteger, LVAudioVideoTalkType) {
LVAudioVideoTalkType_Audio, // 音频通话
LVAudioVideoTalkType_Video, // 视频通话
};

@protocol LVAudioVideoTalkInterface <NSObject>

/**
初始化对象
@param deviceInfo 设备信息
@param avTalkType 音频、视频通话话类型
*/
- (id)initWithDeviceInfo:(LVPlayInfo *)deviceInfo avTalkType:(LVAudioVideoTalkType)avTalkType;
/**
开始通话
*/
- (void)startTalk;
/**
结束通话
*/
- (void)endTalk;
@end

调用示例如下:

// 初始化音频通话引擎
-(LVAudioVideoTalk *)audioVideoTalkEngin {
if (_audioVideoTalkEngin == nil) {
LVPlayInfo *deviceInfo = [[LVPlayInfo alloc] init];
deviceInfo.iotId = self.curDevice.iotId;
deviceInfo.productKey = self.curDevice.productKey;
deviceInfo.deviceName = self.curDevice.deviceName;
_audioVideoTalkEngin = [[LVAudioVideoTalk alloc] initWithDeviceInfo:deviceInfo avTalkType:LVAudioVideoTalkType_Audio];
}
return _audioVideoTalkEngin;
}
// 开始音频通话
[self.audioVideoTalkEngin startTalk];
// 结束音频通话
[self.audioVideoTalkEngin endTalk];

直播预连

调用物模型接口直播预连接

[[[kRTCXThingManager buildThing:obj.iotId] getThingActions] invokeServiceV1:@"PreConnect" params:@{@"Stream":obj.deviceName,@"Channel":@(0),@"Protocol":@"rtc",@"App":obj.productKey} extraData:@{} responseHandler:^(RTCXThingActionsResponse * _Nullable response) {
if (response.success) {
NSLog(@"PreConnect succeed -- %@",obj.nickName?:obj.deviceName);
} else {
NSString *errorMsg = response.responseError.userInfo[NSLocalizedFailureReasonErrorKey];
[self showToast:errorMsg];
}
}];

多目设备播放

注意
  1. 多目设备播放接入需要创建多个播放器LVPlayerView
  2. 初始化LVPlayerView时,需要设置lensId(镜头ID)区分不同播放器
  3. 截图、录制、静音等操作需要同时操作两个播放器对应能力方法(根据实际业务情况来定)
  • 判断是否是双目设备

通过调用物模型属性接口,获取镜头数量LensCount对应的值,如果LensCount = 2,则为双目设备

[[[kRTCXThingManager buildThing:self.device.productKey deviceName:self.device.deviceName iotId:self.device.iotId] getThingActions] getPropertiesFull:^(RTCXThingActionsResponse * _Nullable response) {
if (response.success) {
NSDictionary *propertyMap = response.dataObject[@"propertyMap"];
int lensCount = [[[propertyMap valueForKey:@"LensCount"] valueForKey:@"value"] intValue];// 设备镜头数量
if (lensCount == 2) {
// 当前设备为双目设备
}
} else {
[weakSelf showToast:@"获取属性失败"];
}
}];
  • 初始化双目播放器

调用示例如下:

ifself.lensCount == 2{// 判断为双目设备时
LVPlayInfo *playInfo = [[LVPlayInfo alloc] init];
playInfo.iotId = self.curDevice.iotId;
playInfo.productKey = self.curDevice.productKey;
playInfo.deviceName = self.curDevice.deviceName;
// 云台镜头播放器
playInfo.lensId = @(0);// 云台镜头ID(非双目不需要设置此参数)
self.mainPlayerView = [[LVPlayerView alloc] initWithFrame:CGRectMake(0, 0, self.playerRect.size.width, self.playerRect.size.height) withPlayInfo:playInfo];
self.mainPlayerView.playerController.delegate = self;
self.mainPlayerView.playerController.dataSource = self;
[self.view addSubview:self.mainPlayerView];
// 广角镜头播放器
playInfo.lensId = @(1);// 广角镜头ID(非双目不需要设置此参数)
self.secondPlayerView = [[LVPlayerView alloc] initWithFrame:CGRectMake(0, 0, self.playerRect.size.width, self.playerRect.size.height) withPlayInfo:playInfo];
self.secondPlayerView.playerController.delegate = self;
self.secondPlayerView.playerController.dataSource = self;
[self.view addSubview:self.secondPlayerView];
}