ios-short-video 是又拍云推出的一款适用于 iOS 平台的短视频 SDK,它包含短视频拍摄、编辑、合成、上传,还包含短视频播放器,再结合又拍云存储和 CDN,您就可以开启您的短视频业务了。https://www.upyun.com/products/short-video
1.提供短视频的拍摄、编辑、合成、上传等基础功能。
2.提供播放器支持。
注:该 SDK 不支持模拟器运行,请使用真机
1 短视频
2 上传
3 播放器
8.0
以上版本。1、将示例工程源码中 Resources
文件夹内的文件拖入到 Xcode 项目中中。TuSDK
包含项目运行所需要的 framework 文件。 Resources
包含项目运行所需要的资源文件。
UPLiveSDK.framework
注: 播放器,不需要可不添加UpYunSDK 文件夹
注: 上传视频必需GPUImage.framework
注: 播放器所需, 不需要播放器可不添加libyuv.framework
TuSDK.framework
TuSDKVideo.framework
TuSDKFace.framework
TuSDK.strings
为 SDK 中使用的语言文件。 Localizable.strings
为 demo 展示项目的语言文件。TuSDK.bundle
为项目资源文件,包含滤镜,动态贴纸等文件。Mp3
文件为 demo
中 「MV」
和「配音」
中使用到的音频文件,可自定义更换。2、勾选 Copy items if needed,点击 Finish。
3、打开项目 app target,查看 Build Settings 中 Linking - Other Linker Flags 选项,确保含有 -ObjC
,若没有则添加。用户使用 Cocoapod 进行第三方依赖库管理,需要在 -ObjC
前添加 $(inherited)
。目前短视频 SDK 暂不支持 Cocoapod。
4、在项目的 app target 中,查看 Build Phases 中的 Linking - Link Binary With Libraries 选项中,手动添加 Photos.framework
,并将 Photos.framework 的 Status 设置成 Optional
, 手动添加 Accelerate.framework
, libresolv.tbd
。
5、用户联系 UPYUN
服务支持, 需要的滤镜资源。资源文件中包含 others
, textures
和stickers
需要将这些文件夹替换到 TuSDK.bundle 中对应位置。
6、用户可以在 TuSDKVideoDemo/Resources
文件中找到 TuSDK.bundle 文件。文件中包含 others
,textures
,stickers
和ui_default
这些文件夹。用户需要替换 others/lsq_tusdk_configs.json
文件 textures
和stickers
整个文件夹。
1、在需要使用 TuSDK 的类的文件中引入头文件 #import "TuSDKFramework.h"
。
2、在 AppDelegate.m
的 didFinishLaunchingWithOptions
方法中添加初始化代码,用户如果需求同一应用不同版本发布,请 联系我们
3、为便于开发,可打开 TuSDK 的调试日志,在初始化方法的同一位置添加以下代码:[TuSDK setLogLevel:lsqLogLevelDEBUG];
发布应用时请关闭日志。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// 初始化SDK (请前往 联系UPYUN 获取您的 APP 开发密钥)
[TuSDK initSdkWithAppKey:@"828d700d182dd469-04-ewdjn1"];
// 多 masterkey 方式启动方法
if ([[[NSBundle mainBundle] bundleIdentifier] isEqualToString:@"com.XXXXXXXX.XXXX"]) {
[TuSDK initSdkWithAppKey:@"714f0a1265b39708-02-xie0p1" devType:@"release"];
}
// 开发时请打开调试日志输出
[TuSDK setLogLevel:lsqLogLevelDEBUG];
}
4、添加位置权限,在 viewDidLoad
协议方法中添加以下代码,并在项目的 Info.plist
文件中加入获取位置信息字段。例如:
- (void)viewDidLoad {
// 启动GPS
// 不需要定位功能,可注释该代码,即不再申请定位权限
[[TuSDKTKLocation shared] requireAuthorWithController:self];
}
5、检查滤镜管理器的初始化,在使用滤镜前的某个界面的 viewDidLoad
协议方法中添加以下代码,即可检查滤镜管理器的初始化是否完成。
- (void)viewDidLoad {
// 异步方式初始化滤镜管理器
// 需要等待滤镜管理器初始化完成,才能使用所有功能
[TuSDK checkManagerWithDelegate:self];
// 库文件的版本号
NSLog(@"TuSDK.framework 的版本号 : %@",lsqSDKVersion);
NSLog(@"TuSDKVideo.framework 的版本号 : %@",lsqVideoVersion);
}
#pragma mark - TuSDKFilterManagerDelegate
/**
* 滤镜管理器初始化完成(代理方法)
*
* @param manager
* 滤镜管理器
*/
- (void)onTuSDKFilterManagerInited:(TuSDKFilterManager *)manager;
{
}
6、需要在 info.plist 中添加的权限字段
<!-- 相册 -->
<key>NSPhotoLibraryUsageDescription</key>
<string>Recording requires to use your photo library</string>
<!-- 相机 -->
<key>NSCameraUsageDescription</key>
<string>Recording requires to use your camera</string>
<!-- 麦克风 -->
<key>NSMicrophoneUsageDescription</key>
<string>Recording requires to use your microphone</string>
<!-- 在使用期间访问位置 -->
<key>NSLocationWhenInUseUsageDescription</key>
<string>Recording requires your location</string>
7、前期库文件的安装和 TuSDK 初始化已完成,后期功能代码调用见 TuSDKVideoDemo
。
// 录制相机对象
TuSDKRecordVideoCamera *_camera;
TuSDKRecordVideoCameraDelegate
// 开启访问相机权限
[TuSDKTSDeviceSettings checkAllowWithController:self
type:lsqDeviceSettingsCamera
completed:^(lsqDeviceSettingsType type, BOOL openSetting)
{
if (openSetting) {
lsqLError(@"Can not open camera");
return;
}
// 启动相机的方法
// ...
}];
// 滤镜列表,获取滤镜前往 TuSDK.bundle/others/lsq_tusdk_configs.json
// TuSDK 滤镜信息介绍 @see-https://tusdk.com/docs/ios/self-customize-filter
_videoFilters = @[@"Normal", @"SkinSugar", @"DeepWhitening"];
// 事件处理队列
_sessionQueue = dispatch_queue_create("org.lasque.tusdkvideo", DISPATCH_QUEUE_SERIAL);
// 测试相册访问权限
[TuSDKTSAssetsManager testLibraryAuthor:^(NSError *error)
{
if (error) {
[TuSDKTSAssetsManager showAlertWithController:self loadFailure:error];
}else{
NSLog(@"已经获得了相册的权限");
}
}];
创建相机预览画面的视图
if (!_cameraView) {
_cameraView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, self.view.getSizeWidth, self.view.getSizeHeight)];
[self.view addSubview:_cameraView];
[self.view insertSubview:_cameraView atIndex:0];
}
启动相机对象
// SessionPreset :采集画面的清晰度,建议 AVCaptureSessionPresetHigh
// 设置参考:AVFoundation/AVCaptureSession.h
// cameraPosition:默认前置摄像头,[AVCaptureDevice lsqFirstFrontCameraPosition]
_camera = [TuSDKRecordVideoCamera initWithSessionPreset:AVCaptureSessionPresetMedium
cameraPosition:[AVCaptureDevice lsqFirstFrontCameraPosition]
cameraView:_cameraView];
设置录制文件格式(默认:lsqFileTypeQuickTimeMovie),可输出 MP4
_camera.fileType = lsqFileTypeMPEG4;
输出视频的画质,影响保存视频文件的体积 (默认采用系统设置,自定义设置请参考「自定义使用5」)
_camera.videoQuality = [TuSDKVideoQuality makeQualityWith:TuSDKRecordVideoQuality_Medium2];
设置委托
_camera.videoDelegate = self;
相机预览画面区域显示算法
_camera.regionHandler = [[CustomTuSDKCPRegionDefaultHandler alloc]init];
预览画面画幅设置,_cameraView 设置为全屏尺寸后,调节比例可以输出 1:1 画幅视频
_camera.cameraViewRatio = 1.0;
指定比例后,建议输出画面尺寸和设定 cameraViewRatio 保持一致,SDK 会根据设备情况自动输出最合适的尺寸
_camera.outputSize = CGSizeMake(640, 640);
禁用持续自动对焦
_camera.disableContinueFoucs = NO;
视频覆盖区域颜色 (默认:[UIColor blackColor])
_camera.regionViewColor = [UIColor blackColor];
禁用前置摄像头自动水平镜像 (默认: NO,前置摄像头拍摄结果自动进行水平镜像)
_camera.disableMirrorFrontFacing = NO;
闪光灯模式(默认是关闭:AVCaptureFlashModeOff)
[_camera flashWithMode:AVCaptureFlashModeOff];
相机采集帧率,默认20帧; 开启智能贴纸时,帧率建议范围:12 ~ 20; 关闭智能贴纸时,帧率建议范围:12 ~ 30
_camera.frameRate = 30;
不保存到相册,可在代理方法中获取 result.videoPath(默认:YES,录制完成自动保存到相册)
_camera.saveToAlbum = NO;
保存到指定相册(需要将 saveToAlbum 置为 YES 后生效)
_camera.saveToAlbumName = @"TuSDK";
启用智能贴纸
_camera.enableFaceAutoBeauty = YES;
设置图片水印,默认为空
_camera.waterMarkImage = [UIImage imageNamed:@"sample.png"];
设置水印图片的位置
_camera.waterMarkPosition = lsqWaterMarkBottomRight;
最大录制时长 8s
_camera.maxRecordingTime = 8;
最小录制时长 2s
_camera.minRecordingTime = 2;
续拍模式(若沿用 demo 示例 UI 相机和底部栏的 recordMode 配置,需要保持一致)
_camera.recordMode = lsqRecordModeKeep;
设置默认滤镜(若沿用 demo 示例 UI,filterView 初始化设置 currentFilterTag 需要对应,可参考「自定义使用7」)
[_camera switchFilterWithCode:_videoFilters[1]];
设置使用录制相机最小空间限制,开发者可根据需要自行设置(单位:字节 默认:50M)
_camera.minAvailableSpaceBytes = 1024.f*1024.f*50.f;
启动相机
[_camera tryStartCameraCapture];
初始化相机时,以下几点需要注意
_camera.outputSize 和 _camera.videoQuality,影响保存视频的文件大小。
Demo 默认设置 outputSize 尺寸 CGSizeMake(640, 640),需要设置为 16 的倍数。
_camera.regionHandler 是用来调整预览画面距离顶端距离
如果采用全屏录制需要将相机配置参数中的代码注释掉,代码如下
_camera.regionHandler = [[CustomTuSDKCPRegionDefaultHandler alloc]init];
需要设置预览画面距离顶端的距离, SDK 会自动调整预览画面位置
/**
* 选区范围百分比
*/
- (CGRect)rectPercent;
{
// 设置距离屏幕顶部的距离
NSUInteger topBarHeight = 74;
CGRect rect = [UIScreen mainScreen].bounds;
return CGRectMake(0, topBarHeight/rect.size.height, 1.0, (rect.size.width)/rect.size.height);
}
直播相机初始化前需要配置滤镜列表
引入头文件 #import “FilterView.h”
Demo 提供使用范例,用户可根据接口自定义修改相关使用。
// 滤镜列表,获取滤镜前往 TuSDK.bundle/others/lsq_tusdk_configs.json
// TuSDK 滤镜信息介绍 @see-https://tusdk.com/docs/ios/self-customize-filter
_videoFilters = @[@"Normal",@"VideoFair", @"VideoWhiteSkin"];
初始化滤镜栏
_filterView = [[FilterView alloc]initWithFrame:CGRectMake(0, (_bottomBackView.lsqGetSizeHeight - filterViewHeight)/2, self.view.lsqGetSizeWidth, filterViewHeight)];
_filterView.canAdjustParameter = true;
_filterView.filterEventDelegate = self;
// 应与 相机初始化加载的默认滤镜所 对应的下标保持一致
_filterView.currentFilterTag = 201;
_filterView.backgroundColor = [UIColor whiteColor];
[_filterView createFilterWith:_videoFilters];
[_filterView refreshAdjustParameterViewWith:_currentFilter.code filterArgs:_currentFilter.filterParameter.args];
[_bottomBackView addSubview:_filterView];
遵守代理 FilterViewEventDelegate,实现代理方法。
#pragma mark -- 滤镜栏点击代理方法 FilterEventDelegate
- (void)filterViewParamChangedWith:(TuSDKICSeekBar *)seekbar changedProgress:(CGFloat)progress{
//根据tag获得当前滤镜的对应参数,修改precent;
NSInteger index = seekbar.tag;
TuSDKFilterArg *arg = _currentFilter.filterParameter.args[index];
arg.precent = progress;
//设置滤镜参数;
[_currentFilter submitParameter];
}
// 启动后默认选择滤镜,请参考「自定义使用7」
切换滤镜
- (void)filterViewSwitchFilterWithCode:(NSString *)filterCode{
//切换滤镜
[_camera switchFilterWithCode:filterCode];
}
// 相机调用 switchFilterWithCode 之后的回调方法
/**
* 相机滤镜改变 (如需操作UI线程, 请检查当前线程是否为主线程)
*
* @param camera 相机对象
* @param newFilter 新的滤镜对象
*/
- (void)onVideoCamera:(id<TuSDKVideoCameraInterface>)camera filterChanged:(TuSDKFilterWrap *)newFilter;
{
//赋值新滤镜 同事刷新新滤镜的参数配置;
_currentFilter = newFilter;
[_filterView refreshAdjustParameterViewWith:newFilter.code filterArgs:newFilter.filterParameter.args];
}
引入头文件 #import “StickerScrollView.h”
Demo 提供使用范例,用户可根据接口自定义修改相关使用。
使用非标准版直播服务,请跳过该部分介绍
初始化贴纸栏
CGFloat stickerViewHeight = _bottomBackView.lsqGetSizeHeight - 10;
_stickerView = [[StickerScrollView alloc]initWithFrame:CGRectMake(0, (_bottomBackView.lsqGetSizeHeight - stickerViewHeight), self.view.lsqGetSizeWidth, stickerViewHeight)];
_stickerView.stickerDelegate = self;
_stickerView.backgroundColor = [UIColor whiteColor];
[_bottomBackView addSubview:_stickerView];
遵守代理 StickerViewClickDelegate,实现代理方法。
#pragma mark -- 贴纸栏点击代理方法 StickerViewClickDelegate
- (void)clickStickerViewWith:(TuSDKPFStickerGroup *)stickGroup{
if (!stickGroup) {
//为nil时 移除已有贴纸组;
[_camera removeAllLiveSticker];
return;
}
//展示对应贴纸组;
[_camera showGroupSticker:stickGroup];
}
#pragma mark -- 录制相关代理方法 TuSDKVideoCameraDelegate
/**
* 视频录制完成
*
* @param camerea 相机
* @param result TuSDKVideoResult 对象
*/
- (void)onVideoCamera:(TuSDKRecordVideoCamera *)camerea result:(TuSDKVideoResult *)result;
{
// 通过相机初始化设置 _camera.saveToAlbum = NO; result.videoPath 拿到视频的临时文件路径
NSLog(@"TuSDK complete %@",result.videoPath);
if (result.videoPath) {
// 进行自定义操作,例如保存到相册
UISaveVideoAtPathToSavedPhotosAlbum(result.videoPath, nil, nil, nil);
[[TuSDK shared].messageHub showSuccess:LSQString(@"lsq_save_saveToAlbum_succeed", @"保存成功")];
}else{
// _camera.saveToAlbum = YES; (默认为 :YES)将自动保存到相册
[[TuSDK shared].messageHub showSuccess:LSQString(@"lsq_save_saveToAlbum_succeed", @"保存成功")];
}
// 引入头文件 #import "MoivePreviewAndCutController.h"
// result.videoPath 转换格式
// NSURL *inputURL = [NSURL fileURLWithPath:result.videoPath];
// MoivePreviewAndCutController *vc = [MoivePreviewAndCutController new];
// vc.inputURL = inputURL;
// [self pushViewController:vc animated:YES];
}
saveToAlbum
置为 YES,在录制达到最大时长或中途保存后,会自动保存到相册中。saveToAlbum
置为 NO,在录制达到最大时长或中途保存后,会通过 result.videoPath 获取到对应视频的临时文件,可进行自定义操作。录制出现错误
/**
* 视频录制出错
*
* @param camerea 相机
* @param error 错误对象
*/
- (void)onVideoCamera:(TuSDKRecordVideoCamera *)camerea failedWithError:(NSError*)error;
{
if (error.code == lsqRecordVideoErrorUnknow) {
NSLog(@"录制失败:未知原因失败");
}else if (error.code == lsqRecordVideoErrorSaveFailed){
// 取消录制 同时 重置UI
[self resetRecordUI];
[[TuSDK shared].messageHub showError:NSLocalizedString(@"lsq_record_failed", @"录制失败")];
NSLog(@"录制失败:保存视频失败");
}else if (error.code == lsqRecordVideoErrorLessMinDuration){
NSLog(@"录制失败:小于最小时长");
}else if (error.code == lsqRecordVideoErrorMoreMaxDuration){
NSLog(@"录制失败:大于最大时长 请保存视频后继续录制");
[[TuSDK shared].messageHub showError:NSLocalizedString(@"lsq_record_moreMaxDuration", @"大于最大时长,请保存视频后继续录制")];
}else if (error.code == lsqRecordVideoErrorNotEnoughSpace){
NSLog(@"手机可用空间不足,请清理手机");
}
}
录制操作过程中的状态改变
/**
录制状态
@param camerea camerea TuSDKRecordVideoCamera
@param state state lsqRecordState
*/
- (void)onVideoCamera:(TuSDKRecordVideoCamera *)camerea recordStateChanged:(lsqRecordState)state;
{
if (state == lsqRecordStateRecordingCompleted) {
[[TuSDK shared].messageHub showSuccess:NSLocalizedString(@"lsq_record_complete", @"录制完成")];
}else if (state == lsqRecordStateRecording){
}else if (state == lsqRecordStatePaused){
// 暂停录制
}else if (state == lsqRecordStateMerging){
// 正在合成视频
}else if (state == lsqRecordStateCanceled){
// 取消录制
}else if (state == lsqRecordStateSaveing){
// 正在保存
}
}
SDK 目前采用了系统的图片选择器 UIImagePickerController
并在对应的代理方法中将选择好的视频地址传给裁剪编辑器
#pragma mark - UIImagePickerControllerDelegate
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info;
{
[picker dismissViewControllerAnimated:NO completion:^{
NSURL *url = [info objectForKey:UIImagePickerControllerMediaURL];
[self openMovieEditor:url];
}];
}
裁剪功能控制器 MoivePreviewAndCutController
开启控制器需要传入一个视频的相册地址
// 视频URL
@property (nonatomic) NSURL *inputURL;
用户可以选择在完成录制之后,直接开启视频裁剪的控制器
录制完成后可以获取到 result.videoPath
,经过转换后传输给裁剪控制器
NSURL *inputURL = [NSURL fileURLWithPath:result.videoPath];
获取视频裁剪栏缩略图,用于裁剪栏的展示
// 获取视频缩略图
__weak MoivePreviewAndCutController * wSelf = self;
TuSDKVideoImageExtractor *imageExtractor = [TuSDKVideoImageExtractor createExtractor];
imageExtractor.videoPath = wSelf.inputURL;
// 设置返回展示缩略图的数量,默认设置为 10,否则缩略图过少无法铺满整个裁剪栏
imageExtractor.extractFrameCount = 10;
[imageExtractor asyncExtractImageList:^(NSArray<UIImage *> * images) {
NSLog(@"get images = %@",images);
wSelf.cutVideoView.thumbnails = images;
}];
裁剪控制器并未对视频本身进行裁剪,而是获取到需要裁剪的时间,以待后续处理。
TuSDKVideoImageExtractor 可用于获取视频的缩略图,例如返回视频首帧画面作为展示封面。
视频裁剪完成后,获取到对应的裁剪的时间段后会开启 MovieEditorViewController
开启后,会将裁剪的时间范围和视频的地址,传输到视频编辑的控制器
MovieEditorViewController *vc = [MovieEditorViewController new];
vc.inputURL = _inputURL;
vc.startTime = _startTime;
vc.endTime = _endTime;
[self pushViewController:vc animated:true];
遵守代理 TuSDKMovieEditorDelegate
初始化视频编辑的参数设置
TuSDKMovieEditorOptions *options = [TuSDKMovieEditorOptions defaultOptions];
设置视频的 inoutURL 地址
options.inputURL = self.inputURL;
设置视频截取范围
options.cutTimeRange = [TuSDKTimeRange makeTimeRangeWithStartSeconds:_startTime endSeconds:_endTime];
是否按照正常速度播放
options.playAtActualSpeed = YES;
设置裁剪范围 注:该参数对应的值均为比例值,即:若视频展示View总高度800,此时截取时y从200开始,则cropRect的 originY = 偏移位置/总高度, 应为 0.25, 其余三个值同理
options.cropRect = _cropRect;
设置编码视频的画质
options.encodeVideoQuality = [TuSDKVideoQuality makeQualityWith:TuSDKRecordVideoQuality_High1];
是否保留视频原音(置为 NO,视频中的原因就被去除)
options.enableVideoSound = YES;
初始化视频编辑器
_movieEditor = [[TuSDKMovieEditor alloc]initWithPreview:_videoView options:options];
遵守代理
_movieEditor.delegate = self;
贴纸出现的默认时间范围(1.7.0 之后已不再使用)
/*设置贴纸出现的默认时间范围 (开始时间~结束时间,注:基于裁剪范围,如原视频8秒,裁剪第2~第7秒的内容,此时贴纸时间范围为1~2,即原视频的第3~第4秒展示贴纸)
*/
// _movieEditor.mvItemTimeRange = [[TuSDKMVEffectData alloc]initEffectInfoWithStart:_mvStartTime end:_mvEndTime type:lsqMVEffectDataTypeStickerAudio];
保存到系统相册 默认为YES
_movieEditor.saveToAlbum = NO;
保存到指定相册(需要将 saveToAlbum 置为 YES后生效)
_movieEditor.saveToAlbumName = @"TuSDK";
设置录制文件格式(默认:lsqFileTypeQuickTimeMovie)
_movieEditor.fileType = lsqFileTypeMPEG4;
是否开启美颜
_movieEditor.enableBeauty = YES;
设置水印,默认为空
_movieEditor.waterMarkImage = [UIImage imageNamed:@"sample_watermark.png"];
设置水印图片的位置
_movieEditor.waterMarkPosition = lsqWaterMarkTopRight;
视频播放音量设置,0 ~ 1.0 仅在 enableVideoSound 为 YES 时有效
_movieEditor.videoSoundVolume = 0.8;
设置默认镜
[_movieEditor switchFilterWithCode:_videoFilters[1]];
加载视频,显示第一帧
[_movieEditor loadVideo];
#pragma mark - 视频播放的代理方法 TuSDKMovieEditorDelegate
// 视频保存完成
- (void)onMovieEditor:(TuSDKMovieEditor *)editor result:(TuSDKVideoResult *)result
{
//保存成功后取消提示框 同时返回到root
// 通过相机初始化设置 _movieEditor.saveToAlbum = NO; result.videoPath 拿到视频的临时文件路径
NSLog(@"视频保存的临时文件路径:%@",result.videoPath);
if (result.videoPath) {
// 进行自定义操作,例如保存到相册
UISaveVideoAtPathToSavedPhotosAlbum(result.videoPath, nil, nil, nil);
[[TuSDK shared].messageHub showSuccess:LSQString(@"lsq_save_saveToAlbum_succeed", @"保存成功")];
}else{
// _movieEditor.saveToAlbum = YES;(默认为 :YES)将自动保存到相册
[[TuSDK shared].messageHub showSuccess:LSQString(@"lsq_save_saveToAlbum_succeed", @"保存成功")];
}
}
#pragma mark - 视频播放的代理方法 TuSDKMovieEditorDelegate
// 视频处理状态改变
- (void)onMovieEditor:(TuSDKMovieEditor *)editor statusChanged:(lsqMovieEditorStatus)status
{
if (status == lsqMovieEditorStatusLoadFailed) {
lsqLDebug(@"加载失败");
}else if (status == lsqMovieEditorStatusPreviewingCompleted){
}
}
直播相机初始化前需要配置滤镜列表
引入头文件 #import “FilterView.h”
Demo 提供使用范例,用户可根据接口自定义修改相关使用。
// 滤镜列表,获取滤镜前往 TuSDK.bundle/others/lsq_tusdk_configs.json
// TuSDK 滤镜信息介绍 @see-https://tusdk.com/docs/ios/self-customize-filter
_videoFilters = @[@"Normal", @"DeepWhitening",@"WarmSunshine", @"Leica", @"Newborn", @"Morning",@"VideoYoungGirl", @"VideoJelly", @"AmericaPast", @"SkinSugar"];
初始化滤镜栏
// 底部滤镜控制栏
_filterView = [[FilterView alloc]initWithFrame:CGRectMake(0, rect.size.width + 61, rect.size.width , rect.size.height - rect.size.width - 61)];
_filterView.canAdjustParameter = true;
_filterView.filterEventDelegate = self;
[_filterView setBackgroundColor:[UIColor whiteColor]];
[_filterView createFilterWith:_videoFilters];
[self.view addSubview:_filterView];
遵守代理 FilterViewEventDelegate
,实现代理方法。
#pragma mark - 滤镜View代理方法 FilterViewEventDelegate
// 改变滤镜参数
- (void)filterViewParamChangedWith:(TuSDKICSeekBar *)seekbar changedProgress:(CGFloat)progress
{
// 调整滤镜参数 根据tag判断是当前滤镜的哪一个参数
NSInteger index = seekbar.tag;
TuSDKFilterArg *arg = _currentFilter.filterParameter.args[index];
arg.precent = progress;
[_currentFilter submitParameter];
}
切换滤镜
- (void)filterViewSwitchFilterWithCode:(NSString *)filterCode
{
// 更换滤镜
[_movieEditor switchFilterWithCode:filterCode];
}
// 视频编辑器 switchFilterWithCode 调用之后的回调方法
// 滤镜改变方法
- (void)onMovieEditor:(TuSDKMovieEditor *)editor filterChanged:(TuSDKFilterWrap *)newFilter
{
_currentFilter = newFilter;
[_filterView refreshAdjustParameterViewWith:newFilter.code filterArgs:newFilter.filterParameter.args];
}
1.如果用户需求调整滤镜效果,效果确认完毕后,用户需要在控制台重新打包下载资源文件,然后替换到对应位置。参考 framework 安装 第 5 步
。
2.如果用户工程已使用 GPUImage
的部分功能,建议用户使用 Github 提供的 完整版GPUImage 。如果在使用完整版 GPUImage 的过程中有什么问题,可以参考文档GPUImage 的相关错误。
3.用户可以在 TuSDK.bundle/others/config.json 文件中查看自己的滤镜资源 "name":"lsq_filter_VideoFair"
,VideoFair
就是该滤镜的filterCode ,在_videoFilters = @[@"VideoFair"];
可以进行选择使用滤镜的设置。
4.录制相机进行全屏录制需要进行以下设置
// 不启动相机预览画面区域显示算法,注释掉该行代码
// _camera.regionHandler = [[CustomTuSDKCPRegionDefaultHandler alloc]init];
// 比例设置为 1.0 ,预览画面为 1:1 方形画幅。
// 比例设置为 0,预览画面为全屏画幅
_camera.cameraViewRatio = 0;
// 最终输出的视频尺寸由 outputSize 控制
// 需要将输出尺寸修改为均为 16 的倍数
// _camera.outputSize = CGSizeMake(640, 640);
5.录制相机控制输出的文件大小
// 输出视频的画质,主要包含码率、分辨率等参数 (默认为空,采用系统设置)
_camera.videoQuality = [TuSDKVideoQuality makeQualityWith:TuSDKRecordVideoQuality_Low1];
// 可以进行输出视频文件的码率自定义,配置参数请参考 TuSDKVideo/TuSDKVideoQuality.h
TuSDKVideoQuality *customVideoQuality = [[TuSDKVideoQuality alloc]init];
customVideoQuality.lsqVideoBitRate = 1200 * 1000;
_camera.videoQuality = customVideoQuality;
6.录制相机的模式切换
// 需要配置相机的录制模式,如果沿用 demo 提供的示例 UI,需要同时配置底部栏的 recordMode
/**
录制模式
*/
typedef NS_ENUM(NSInteger,lsqRecordMode)
{
/** 正常模式 */
lsqRecordModeNormal,
/** 续拍模式 */
lsqRecordModeKeep,
};
_camera.recordMode = _inputRecordMode;
_bottomBar.recordMode = _inputRecordMode;
// 拍照录制相机的录制模式为正常模式
7.录制相机初始化,默认选择一款滤镜
// 需要配置相机的默认选择滤镜,如果沿用 demo 提供的示例 UI ,需要同时配置滤镜栏的默认滤镜
// 设置默认滤镜 对应filterView创建时默认的 currentFilterTag 和相机保持一致
[_camera switchFilterWithCode:_videoFilters[1]];
// 注: currentFilterTag 基于200 即:200 + 滤镜列表中某滤镜的对应下标
_filterView.currentFilterTag = 201;
8.MoviePreviewAndCutViewController 全屏/视频比例自适应展示设置
// 可以设置全屏/视频比例自适应展示,如果沿用 demo 提供的示例 UI ,需要进行以下更改
// 需要将控制器内 - (void)initPlayerView 方法中的部分代码注释
// _videoScroll 和 _videoView 默认是 frame 为屏幕宽度的 1:1 区域
// 设置全屏展示时,需要将两者的 frame 设置为屏幕宽高,如不设置将视频按照比例自适应展示。
// 调整其他控件的背景色的 alpha 值,以防止遮挡视觉效果
// 需要注释代码如下
/*
AVAssetTrack *videoTrack = [_item.asset tracksWithMediaType:AVMediaTypeVideo][0];
CGSize videoSize = videoTrack.naturalSize;
// 根据朝向判断是否需要交换宽高
CGAffineTransform transform = videoTrack.preferredTransform;
BOOL isNeedSwopWH = NO;
if (transform.a == 0 && transform.b == 1.0 && transform.c == -1.0 && transform.d == 0) {
// Right
isNeedSwopWH = YES;
}else if (transform.a == 0 && transform.b == -1.0 && transform.c == 1.0 && transform.d == 0){
// Left
isNeedSwopWH = YES;
}else if (transform.a == -1.0 && transform.b == 0 && transform.c == 0 && transform.d == -1.0){
// Down
isNeedSwopWH = NO;
}else{
// Up
isNeedSwopWH = NO;
}
if (isNeedSwopWH) {
// 交换宽高
videoSize = CGSizeMake(videoSize.height, videoSize.width);
}
// 如需比例自适应,需要注释下方代码块
// 此处的宽高计算仅适用于 1:1 情况下,若有其他的适配,请重新修改计算方案
if (videoSize.width > videoSize.height) {
// 定高适配宽
CGSize newSize = CGSizeMake(_videoView.lsqGetSizeHeight*videoSize.width/videoSize.height, _videoView.lsqGetSizeHeight);
CGFloat offset = (newSize.width - _videoView.lsqGetSizeWidth)/2;
[_videoView lsqSetSize:newSize];
_videoScroll.contentSize = newSize;
_videoScroll.contentOffset = CGPointMake(offset, 0);
}else{
// 定宽适配高
CGSize newSize = CGSizeMake(_videoView.lsqGetSizeWidth, _videoView.lsqGetSizeWidth*videoSize.height/videoSize.width);
CGFloat offset = (newSize.height - _videoView.lsqGetSizeHeight)/2;
[_videoView lsqSetSize:newSize];
_videoScroll.contentSize = newSize;
_videoScroll.contentOffset = CGPointMake(0, offset);
}
*/
9.MovieEditorViewController 全屏/视频比例自适应展示设置
// 可以设置全屏/视频比例自适应展示,如果沿用 demo 提供的示例 UI ,需要进行以下更改
// 启动时候需要设置参数如下
MovieEditorFullScreenController *vc = [MovieEditorFullScreenController new];
vc.inputURL = _inputURL;
vc.startTime = _startTime;
vc.endTime = _endTime;
vc.cropRect = CGRectMake(0, 0, 0, 0);
// 同时将控制器内 - (void)lsqInitView 中视频展示 _previewView 的 frame 设置为屏幕宽高,画面即为全屏展示
// 如不设置 _previewView 的 frame 将进行视频比例的自适应的展示
// 调整其他控件的背景色的 alpha 值,以防止遮挡视觉效果
10.拍照录制相机/断点续拍相机,连接其他控制器使用
// 拍照录制相机连接视频编辑功能,需要进行一些调整
-(void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
// 进行页面跳转需要注释销毁相机的代码
// 相机页面销毁的时候,不要忘记销毁相机
// [self destroyCamera];
[self destroyVideoPlayer];
}
// 在保存照片或录制的视频的方法中,进行相关控制器的开启
- (void)savePictureOrVideo
{
if (!_takePictureIV.hidden) {
// 保存照片
[TuSDKTSAssetsManager saveWithImage:_takePictureIV.image compress:0 metadata:nil toAblum:nil completionBlock:^(id<TuSDKTSAssetInterface> asset, NSError *error) {
if (!error) {
_takePictureIV.image = nil;
_takePictureIV.hidden = YES;
[[TuSDK shared].messageHub showSuccess:NSLocalizedString(@"lsq_save_saveToAlbum_succeed", @"保存成功")];
}
} ablumCompletionBlock:nil];
}
if (_videoPlayer && _videoPath) {
// 保存视频,同时删除临时文件
[TuSDKTSAssetsManager saveWithVideo:[NSURL fileURLWithPath:_videoPath] toAblum:nil completionBlock:^(id<TuSDKTSAssetInterface> asset, NSError *error) {
if (!error) {
// 需要注释销毁临时文件的方法
// 相机最终销毁的时候,不要忘记将临时文件删除
// [TuSDKTSFileManager deletePath:_videoPath];
// _videoPath = nil;
[self destroyVideoPlayer];
// [[TuSDK shared].messageHub showSuccess:NSLocalizedString(@"lsq_save_saveToAlbum_succeed", @"保存成功")];
}
} ablumCompletionBlock:nil];
// 开启时间裁剪
MoviePreviewAndCutRatioAdaptedController *vc = [MoviePreviewAndCutRatioAdaptedController new];
vc.inputURL = [NSURL fileURLWithPath:_videoPath];
[self.navigationController pushViewController:vc animated:YES];
}
_preView.hidden = YES;
}
11.断点续拍相机,连接其他控制器使用
// 断点续拍相机连接视频编辑功能,需要进行一些调整
-(void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
// 进行页面跳转的时候,需要注释下方销毁相机的方法。
// 相机页面销毁,不要忘记销毁相机
// [self destroyCamera];
}
// 在视频录制完成的代理方法中,进行相关控制器的开启
- (void)onVideoCamera:(TuSDKRecordVideoCamera *)camerea result:(TuSDKVideoResult *)result;
{
// 通过相机初始化设置 _camera.saveToAlbum = NO; result.videoPath 拿到视频的临时文件路径
if (result.videoPath) {
// 进行自定义操作,例如保存到相册
// UISaveVideoAtPathToSavedPhotosAlbum(result.videoPath, nil, nil, nil);
// [[TuSDK shared].messageHub showSuccess:NSLocalizedString(@"lsq_save_saveToAlbum_succeed", @"保存成功")];
// 开启视频编辑添加滤镜
MovieEditorFullScreenController *vc = [MovieEditorFullScreenController new];
vc.inputURL = [NSURL fileURLWithPath:result.videoPath];
// 视频编辑如需全屏展示,参数需要设置 vc.cropRect = CGRectMake(0, 0, 0, 0); 画面展示会进行比例自适应
vc.cropRect = CGRectMake(0, 0, 0, 0);
vc.startTime = 0;
vc.endTime = result.duration;
[self.navigationController pushViewController:vc animated:true];
}else{
// _camera.saveToAlbum = YES; (默认为 :YES)将自动保存到相册
[[TuSDK shared].messageHub showSuccess:NSLocalizedString(@"lsq_save_saveToAlbum_succeed", @"保存成功")];
}
if (_camera && _camera.recordMode == lsqRecordModeNormal) {
[_bottomBar recordBtnIsRecordingStatu:NO];
}
// 自动保存后设置为 恢复进度条状态
[self changeNodeViewWithLocation:0];
}
12.获取到视频的临时文件后,需要保存到指定的相册
// 录制完成后,拿到临时文件地址可通过以下方法,保存到指定位置
if (_videoPlayer && _videoPath) {
// 保存视频,同时删除临时文件
[TuSDKTSAssetsManager saveWithVideo:[NSURL fileURLWithPath:_videoPath] toAblum:@"自定义相册" completionBlock:^(id<TuSDKTSAssetInterface> asset, NSError *error) {
if (!error) {
// 删除临时文件地址
[TuSDKTSFileManager deletePath:_videoPath];
_videoPath = nil;
}
} ablumCompletionBlock:nil];
}
遵守代理 TuSDKTSAudioMixerDelegate
获取音频文件地址
NSURL *mainAudioURL = [self filePathName:@"sound_cat.mp3"];
创建音频数据对象
mainAudio = [[TuSDKTSAudio alloc]initWithAudioURL:mainAudioURL];
设置音频对象的混合输出的音量大小(demo 为了展示将初始化设置为 0,最终输出是在 seekBar 代理方法中重新赋值)
mainAudio.audioVolume = 0;
设置需要裁剪的时间范围
mainAudio.atTimeRange = [TuSDKTimeRange makeTimeRangeWithStartSeconds:0 endSeconds:6];
音乐素材1
NSURL *firstMixAudioURL = [self filePathName:@"sound_children.mp3"];
firstMixAudio = [[TuSDKTSAudio alloc]initWithAudioURL:firstMixAudioURL];
firstMixAudio.audioVolume = 0;
firstMixAudio.atTimeRange = [TuSDKTimeRange makeTimeRangeWithStartSeconds:0 endSeconds:3];
音乐素材2
NSURL *secondMixAudioURL = [self filePathName:@"sound_tangyuan.mp3"];
secondMixAudio = [[TuSDKTSAudio alloc]initWithAudioURL:secondMixAudioURL];
secondMixAudio.audioVolume = 0;
secondMixAudio.atTimeRange = [TuSDKTimeRange makeTimeRangeWithStartSeconds:0 endSeconds:3];
初始化音乐混合器对象,并设置相关参数
_audiomixer = [[TuSDKTSAudioMixer alloc]init];
遵守代理
_audiomixer.mixDelegate = self;
设置主音轨
_audiomixer.mainAudio = mainAudio;
是否允许素材循环添加 (默认 NO,即主音轨时长 9 秒,音乐素材时长 3 秒,音乐素材将会循环添加三次)
_audiomixer.enableCycleAdd = YES;
音频混合的点击事件
_audiomixer.mixAudios = [NSArray arrayWithObjects:firstMixAudio, secondMixAudio,nil];
[_audiomixer startMixingAudioWithCompletion:^(NSURL *fileURL, lsqAudioMixStatus status) {
_resultURL = fileURL;
}];
遵守代理 TuSDKTSMovieMixerDelegate
获取背景音乐素材地址
NSURL *firstAudioURL = [self filePathName:@"sound_cat.mp3"];
转换为 AVURLAsset 对象
AVURLAsset *firstMixAudioAsset = [AVURLAsset URLAssetWithURL:firstAudioURL options:nil];
构建音频数据对象
firstMixAudio = [[TuSDKTSAudio alloc]initWithAsset:firstMixAudioAsset];
设置添加音乐素材的时间范围
firstMixAudio.atTimeRange = [TuSDKTimeRange makeTimeRangeWithStartSeconds:1 endSeconds:6];
获取另一段背景音乐素材地址
NSURL *secondAudioURL = [self filePathName:@"sound_children.mp3"];
// 同样可以使用 URL 地址进行初始化
secondMixAudio = [[TuSDKTSAudio alloc]initWithAudioURL:secondAudioURL];
secondMixAudio.atTimeRange = [TuSDKTimeRange makeTimeRangeWithStartSeconds:1 endSeconds:6];
获取视频素材地址
NSURL *videoURL = [self filePathName:@"tusdk_sample_video.mov"];
初始化音视频混合器的对象
movieMixer = [[TuSDKTSMovieMixer alloc]initWithMoviePath:videoURL.path];
遵守代理
movieMixer.mixDelegate = self;
是否允许音频循环( 默认 NO,即视频素材时长 9 秒,音频素材 3 秒,将音频素材循环添加三次)
movieMixer.enableCycleAdd = YES;
混合时视频的选中时间
movieMixer.videoTimeRange = [TuSDKTimeRange makeTimeRangeWithStartSeconds:1 endSeconds:20];
是否保留视频原音
movieMixer.enableVideoSound = NO;
将背景音乐素材赋值给音视频混合器
movieMixer.mixAudios = [NSArray arrayWithObjects:firstMixAudio, secondMixAudio, nil];
进行混合
[movieMixer startMovieMixWithCompletionHandler:^(NSString *filePath, lsqMovieMixStatus status) {
if (status == lsqMovieMixStatusCompleted) {
// 操作成功 保存到相册
UISaveVideoAtPathToSavedPhotosAlbum(filePath, nil, nil, nil);
}else{
// 提示失败
NSLog(@"保存失败");
}
}];
获取视频的地址
NSURL *videoURL = [self filePathName:@"tusdk_sample_video.mov"];
初始化视频画面提取的工具类
imageExtractor = [TuSDKVideoImageExtractor createExtractor];
将视频地址赋值给对象
imageExtractor.videoPath = videoURL;
设置需要返回的缩略图的数量
imageExtractor.extractFrameCount = 15;
需要输出缩略图的最大尺寸,(默认设置 : 80 * 80 )
imageExtractor.outputMaxImageSize = CGSizeMake(100, 100);
返回 NSArray<UIImage *>,数组中即为视频的缩略图
[imageExtractor asyncExtractImageList:^(NSArray<UIImage *> * images) {
// 获取到返回的视频的缩率图
thumbnailsArr = images;
}];
遵守代理 TuSDKMovieSplicerDelegate
初始化视频合成器
_movieSplicer = [TuSDKTSMovieSplicer createSplicer];
遵守代理
_movieSplicer.splicerDelegate = self;
获取视频素材的地址
NSURL *sampleOneURL = [self filePathName:@"tusdk_sample_splice_video.mov"];
NSURL *sampleTwoURL = [self filePathName:@"tusdk_sample_video.mov"];
将视频素材的地址赋值给 TuSDKMoiveFragment,并设置拼接部分的时间范围
NSString *moviePath1 = sampleOneURL.path;
TuSDKTimeRange *timeRange1 = [TuSDKTimeRange makeTimeRangeWithStartSeconds:0 endSeconds:8];
TuSDKMoiveFragment *fragment1 = [[TuSDKMoiveFragment alloc]initWithMoviePath:moviePath1 atTimeRange:timeRange1];
NSString *moviePath2 = sampleTwoURL.path;
TuSDKTimeRange *timeRange2 = [TuSDKTimeRange makeTimeRangeWithStartSeconds:10 endSeconds:14];
TuSDKMoiveFragment *fragment2 = [[TuSDKMoiveFragment alloc]initWithMoviePath:moviePath2 atTimeRange:timeRange2];
将赋值过得拼接片段添加到对应的数组中
_movieSplicer.movies = [NSArray arrayWithObjects:fragment1, fragment2, nil];
将多段视频片段合并为一个视频
[_movieSplicer startSplicingWithCompletionHandler:^(NSString *filePath, lsqMovieSplicerSessionStatus status) {
if (status == lsqMovieSplicerSessionStatusCompleted){
// 操作成功 保存到相册
UISaveVideoAtPathToSavedPhotosAlbum(filePath, nil, nil, nil);
}else if(status == lsqMovieSplicerSessionStatusFailed || status == lsqMovieSplicerSessionStatusCancelled || status == lsqMovieSplicerSessionStatusUnknown){
// 其他操作
}
}];
遵守代理 TuSDKMovieClipperDelegate
创建「开始时间」和「结束时间」
NSMutableArray *dropArr = [[NSMutableArray alloc]init];
if (_startTime >= 0) {
TuSDKTimeRange *cutTimeRange1 = [TuSDKTimeRange makeTimeRangeWithStartSeconds:0 endSeconds:_startTime];
[dropArr addObject:cutTimeRange1];
}
if (_endTime < CMTimeGetSeconds(_item.duration)) {
TuSDKTimeRange *cutTimeRange2 = [TuSDKTimeRange makeTimeRangeWithStartSeconds:_endTime endSeconds:CMTimeGetSeconds(_item.duration)];
[dropArr addObject:cutTimeRange2];
}
初始化视频裁剪工具,并赋值 inputURL
_movieClipper = [[TuSDKMovieClipper alloc] initWithMoviePath:_inputURL.path];
是否保留视频原音
_movieClipper.enableVideoSound = YES;
遵守代理
_movieClipper.clipDelegate = self;
输出视频的文件格式,可输出 MP4
_movieClipper.outputFileType = lsqFileTypeQuickTimeMovie;
将时间范围的数组赋值给视频裁剪工具
_movieClipper.dropTimeRangeArr = dropArr;
进行视频裁剪
[_movieClipper startClippingWithCompletionHandler:^(NSString *outputFilePath, lsqMovieClipperSessionStatus status) {
if (status == lsqMovieClipperSessionStatusCompleted){
// 操作成功 保存到相册
UISaveVideoAtPathToSavedPhotosAlbum(outputFilePath, nil, nil, nil);
}else if(status == lsqMovieClipperSessionStatusFailed || status == lsqMovieClipperSessionStatusCancelled || status == lsqMovieClipperSessionStatusUnknown){
}
}];
遵守代理 TuSDKTSAudioRecoderDelegate
初始化录音机
_audioRecorder = [[TuSDKTSAudioRecorder alloc]init];
设置可以录制的最大时长
_audioRecorder.maxRecordingTime = 30;
遵守代理
_audioRecorder.recordDelegate = self;
开始录音
[_audioRecorder startRecording];
暂停录音(暂停后,再次调用 startRecording,会从暂停处继续录制)
[_audioRecorder pauseRecording];
结束录音 (停止录音,并保存输出音频的临时文件地址)
[_audioRecorder finishRecording];
取消录音 (停止录音,删除已录制的音频临时文件)
[_audioRecorder cancelRecording];
获取保存文件的地址
/**
结果通知代理
@param recoder recoder TuSDKTSAudioRecorder
@param result result TuSDKAudioResult
*/
- (void)onAudioRecoder:(TuSDKTSAudioRecorder *)recoder result:(TuSDKAudioResult *)result;
{
NSLog(@"result path: %@ duration : %f",result.audioPath,result.duration);
}
录音过程被打断的状态判断
// 中断开始
- (void)onAudioRecoderBeginInterruption:(TuSDKTSAudioRecorder *)recoder;
{
// 有中断时会取消录制内容 可在此做一些其他操作 如来电中断
}
// 中断已结束
- (void)onAudioRecoderEndInterruption:(TuSDKTSAudioRecorder *)recoder;
{
// 中断结束后,可进行恢复操作
}
UpYunSDK
文件夹#import "UPYUNConfig.h"
AppDelegate.m
的 didFinishLaunchingWithOptions
方法中添加上传的配置 // 上传 服务名 即 空间 bucket
[UPYUNConfig sharedInstance].DEFAULT_BUCKET = @"";
// 上传 操作员的名字
[UPYUNConfig sharedInstance].OPERATOR_NAME = @"";
// 上传 操作员的密码
[UPYUNConfig sharedInstance].OPERATOR_PWD = @"";
[picker dismissViewControllerAnimated:NO completion:^{
NSURL *url = [info objectForKey:UIImagePickerControllerMediaURL];
/// 从相册中上传视频,图片等
/// saveKey 就是文件的保存路径
NSString *saveKey = [NSString stringWithFormat:@"short_video_lib_test_%d.mp4", arc4random() % 10];
[[UPYUNConfig sharedInstance] uploadFilePath:url.path saveKey:saveKey success:^(NSHTTPURLResponse *response, NSDictionary *responseBody) {
[[TuSDK shared].messageHub showSuccess:@"上传成功"];
NSLog(@"保存地址。%@", saveKey);
} failure:^(NSError *error, NSHTTPURLResponse *response, NSDictionary *responseBody) {
NSLog(@"上传失败 error:%@", error);
NSLog(@"上传失败 code=%ld, responseHeader:%@", (long)response.statusCode, response.allHeaderFields);
NSLog(@"上传失败 message:%@", responseBody);
} progress:^(int64_t completedBytesCount, int64_t totalBytesCount) {
}];
}];
if (result.videoPath) {
/// 上传到UPYUN 存储空间
NSString *saveKey = [NSString stringWithFormat:@"short_video_test_%d.mp4", arc4random() % 10];
/// 上传操作
[[UPYUNConfig sharedInstance] uploadFilePath:result.videoPath saveKey:saveKey success:^(NSHTTPURLResponse *response, NSDictionary *responseBody) {
/// 注意 bucketname 应该是实际的 bucket 名
NSLog(@"file url:https://%@.b0.upaiyun.com/%@", @"bucketname", saveKey);
} failure:^(NSError *error, NSHTTPURLResponse *response, NSDictionary *responseBody) {
NSLog(@"上传失败 error:%@", error);
NSLog(@"上传失败 code=%ld, responseHeader:%@", (long)response.statusCode, response.allHeaderFields);
NSLog(@"上传失败 message:%@", responseBody);
} progress:^(int64_t completedBytesCount, int64_t totalBytesCount) {
}];
}
支持播放网络视频,支持播放本地视频文件。
支持视频格式:FLV
,mp4
等视频格式
播放器支持单音频流播放,支持 speex 解码,可以配合浏览器 Flex 推流的播放
支持自定义窗口大小和全屏设置
支持音量调节,静音设置
支持亮度调整
支持缓冲大小设置,缓冲进度回调
支持自动音画同步调整
又拍云
播放器 SDK
。功能完备接口简练,可以快速安装使用, 灵活性强可以满足复杂定制需求。
UPLiveSDK.framework
支持 iOS 8
及以上系统版本;
支持 ARMv7
,ARM64
架构。请使用真机进行开发和测试。
直接将示例工程源码中 UPLiveSDK.framework
文件夹拖拽到目标工程目录。
打开项目 app target,查看 Build Settings 中 Enable bitcode , 设置为 NO
注: 如果需要 app 退出后台仍然不间断推流直播,需要设置 TARGET -> Capabilities -> Backgroud Modes:ON √ Audio, AirPlay,and Picture in Picture
在项目的 app target 中,查看 Build Phases 中的 Linking - Link Binary With Libraries 选项中,手动添加
VideoToolbox.framework
libbz2.1.0.tbd
libiconv.tbd
libz.tbd
libc++.tbd
注: 此 SDK
已经包含 FFMPEG 3.0
, 不建议自行再添加 FFMPEG
库 , 如有特殊需求, 请联系我们
使用 UPAVPlayer
需要引入头文件 #import <UPLiveSDKDll/UPAVPlayer.h>
UPAVPlayer
使用接口类似 AVFoundation
的 AVPlayer
。
//1. 初始化播放器
_player = [[UPAVPlayer alloc] initWithURL:@"http://uprocess.b0.upaiyun.com/demo/short_video/UPYUN_0.mp4"];
//2. 设置代理,接收状态回调信息
_player.delegate = self;
//3. 设置播放器 playView Frame
[_player setFrame:self.view.bounds];
//4. 添加播放器 playView
[self.view insertSubview:_player.playView atIndex:0];
//5. 开始播放
[_player play];
//6. 停止播放
[_player stop];
_player.bufferingTime = 5;
_player.playView;
_player.bufferingTime = 5;
_player.volume = 0.5;
_player. bright = 0.5;
// 静音
_player.mute = YES;
_player.timeoutForBuffering = 60;
_player.timeoutForOpenFile = 10;
_player.maxNumForReopenFile = 1;
_player.delegate = self;
_player.lipSynchOn = YES;
_player.lipSynchMode = 1;
设置画面的frame
连接方法
开始播放
暂停
停止, 会清除播放信息
拖拽功能 秒为单位