功能说明
更新时间:2019-06-26
视频流
代码目录
文件 | 说明 |
---|---|
BDSSVListViewController | 视频流控制器 |
BDSSVListCollectionViewCell | 视频流数据单元 |
BDSSVCollectionWaterfallLayout | 视频流瀑布流布局 |
获取视频媒资
下拉刷新
摘录BDSSVListViewController.m文件
self.collectionView.mj_header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{
if (self.isRefresh) {
return;
}
index = 0;
self.isRefresh = YES;
[self.videos removeAllObjects];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.01f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSMutableDictionary *dic = [NSMutableDictionary dictionary];
if (self.model.author.user_id) {
dic[@"url"] = [NSString stringWithFormat:@"%@%@",kHost,@"/v1/video/getSpecificVideoList"];
dic[@"user_id"] = self.model.author.user_id;
} else {
dic[@"url"] = [NSString stringWithFormat:@"%@%@",kHost,@"/v1/video/getVideoList"];
}
dic[@"index"] = [NSNumber numberWithInteger:index];
@weakify(self);
[self.listModel refreshNewListWithParam:dic success:^(NSArray * _Nonnull list) {
@strongify(self);
if (self.delegate && [self.delegate respondsToSelector:@selector(sourceDataSuccess:)]) {
[self.delegate sourceDataSuccess:list];
}
[self.collectionView.mj_header endRefreshing];
if (list.count > 0) {
[self.videos addObjectsFromArray:list];
[self.collectionView reloadData];
[self.collectionView.mj_footer resetNoMoreData];
} else {
[self.collectionView.mj_footer endRefreshingWithNoMoreData];
}
self.collectionView.mj_footer.hidden = NO;
self.isRefresh = NO;
} failure:^(NSError * _Nonnull error) {
@strongify(self);
if (self.delegate && [self.delegate respondsToSelector:@selector(sourceDataFailure:)]) {
[self.delegate sourceDataFailure:error];
}
[self.collectionView.mj_header endRefreshing];
self.isRefresh = NO;
[self.collectionView reloadData];
[self.collectionView.mj_footer endRefreshingWithNoMoreData];
}];
});
}];
加载更多
摘录BDSSVListViewController.m文件
self.collectionView.mj_footer = [MJRefreshAutoNormalFooter footerWithRefreshingBlock:^{
// 进入刷新状态后会自动调用这个block
if (self.isRefresh) {
return;
}
index++;
NSLog(@"downrefresh %ld",(long)index);
self.isRefresh = YES;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.01f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSMutableDictionary *dic = [NSMutableDictionary dictionary];
if (self.model.author.user_id) {
dic[@"url"] = [NSString stringWithFormat:@"%@%@",kHost,@"/v1/video/getSpecificVideoList"];
dic[@"user_id"] = self.model.author.user_id;
} else {
dic[@"url"] = [NSString stringWithFormat:@"%@%@",kHost,@"/v1/video/getVideoList"];
}
dic[@"index"] = [NSNumber numberWithInteger:index];
@weakify(self);
[self.listModel refreshNewListWithParam:dic success:^(NSArray * _Nonnull list) {
@strongify(self);
if (self.delegate && [self.delegate respondsToSelector:@selector(sourceDataSuccess:)]) {
[self.delegate sourceDataSuccess:list];
}
[self.collectionView.mj_footer endRefreshing];
if (list.count > 0) {
[self.videos addObjectsFromArray:list];
[self.collectionView reloadData];
}else {
index--;
[self.collectionView reloadData];
[self.collectionView.mj_footer endRefreshingWithNoMoreData];
}
self.isRefresh = NO;
} failure:^(NSError * _Nonnull error) {
@strongify(self);
index--;
if (self.delegate && [self.delegate respondsToSelector:@selector(sourceDataFailure:)]) {
[self.delegate sourceDataFailure:error];
}
[self.collectionView.mj_footer endRefreshing];
self.isRefresh = NO;
[self.collectionView reloadData];
[self.collectionView.mj_footer endRefreshingWithNoMoreData];
}];
});
}];
视频采集
代码目录
文件 | 说明 |
---|---|
DARFiltersController | 拍摄贴纸控制器 |
VideoCapture | 视频拍摄storyBoard |
CaptureViewController | 视频拍摄主控制器 |
LiveModel | 视频拍摄模型,负责开启、结束、开始预览、结束预览及手电筒、切换摄像头 |
开启摄像头采集
摘录CaptureViewController.swift文件,会自动向用户获取摄像头及麦克风系统权限
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
navigationController?.setNavigationBarHidden(true, animated: false)
navigationController?.interactivePopGestureRecognizer?.isEnabled = false
// 还原到拍摄器初始美颜滤镜
if (model?.session.imageFilterSetting.colorFilter != nil) {
model?.session.imageFilterSetting.removeFilter(with: .colorAdjust)
}
model?.startPreview()
}
自动开启美颜
开启视频采集,回调中添加美颜能力,摘录CaptureViewController.swift文件
extension CaptureViewController : BDCloudAVRecordSessionDelegate {
func previewStarted() {
model?.filterS.imageSetting.setBeautyLevel(0.7);
model?.filterS.imageSetting.setSkinLevel(0.8);
}
}
数据流图
视频录制
代码目录
与视频采集相同
开启摄像头录制
摘录CaptureViewController.swift文件,写入录制文件时,会提供开始写入和结束写入回调给开发者,便于业务二次开发
extension CaptureViewController : BDCloudAVRecordSessionDelegate {
func writeStarted(at time: CMTime) {
isCapturing = true
timeCounter = NSDate.init()
DispatchQueue.main.async {
self.timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(self.onTimerCount(timer:)), userInfo: nil, repeats: true)
}
}
func writeStopped(at time: CMTime, error: Error!) {
if self.timer != nil {
if (self.timer?.isValid)! {
timer?.invalidate()
timer = nil
timeCounter = nil
}
}
DispatchQueue.main.async {
self.isCapturing = false
self.circleProgressBar.clearProgress()
if (!self.isCancelled) {
self.enterEdit()
}
self.isCancelled = false;
}
}
}
数据流图
视频预览
代码目录
文件 | 说明 |
---|---|
EditSubtitleViewController | 字幕控制器 |
EditViewController.storyboard | 视频预览storyBoard |
EditSettingViewController | 视频处理控制器,包括颜色调整、滤镜、美颜和添加音乐 |
EditViewController | 视频预览控制器,包括预览设置及视频处理设置 |
PreviewModel | 视频预览模型,包括预览开始、结束控制及设置预览文件地址、速度、正反放、区间 |
- 颜色调整,参见ImageFilterView.swift文件
- 滤镜切换,参见StyleFilterView.swift文件
- 美颜调整,参加BeautyFilterView.swift文件
- 背景音乐,参加MusicFilterView.swift文件
初始化视频预览文件
摘录PreviewModel.m文件
- (instancetype)initWithSrcFile:(NSString*)file {
if (self = [super init]) {
_sourceS = [PartialSourceSetting new];
_sourceS.file = file;
_filterS = [FilterSetting new];
_destS = nil;
_speed = 1.0;
BDCloudAVPreviewSession *session = [[BDCloudAVPreviewSession alloc] initWithURL:[NSURL fileURLWithPath:_sourceS.file]];
session.fillMode = BDCloudAVImagePlayerFillModePreserveAspectRatio;
_session = session;
[_filterS setEffectSession:_session];
}
return self;
}
- 开始预览视频文件 摘录PreviewModel.m文件,BDCloudAVPreviewSession类实现隐藏在BDCloudAVKit.framework中
- (BDCloudAVPreviewSession*)previewSession {
return (BDCloudAVPreviewSession*)self.session;
}
- (void)start {
[self.previewSession start];
}
数据流图
视频特效
支持特效的类型
- 基础美颜(美白、磨皮)
- 高级美颜(大眼、瘦脸)
-
风格滤镜
- 颜色调整(对比度、亮度、曝光度、色温、饱和度、锐度)
- 智能调整(回忆、少女、红润、都市、微光、红唇)
-
人脸贴纸,只有少数贴纸使用内置,其他需要使用网络下载
- 2D贴纸
- 3D贴纸
- 手势贴纸
使用方式,可参见
- 颜色调整,参见ImageFilterView.swift文件
- 滤镜切换,参见StyleFilterView.swift文件
- 美颜调整,参加BeautyFilterView.swift文件
视频合成&&上传
代码目录
文件 | 说明 |
---|---|
SynSuccessViewController | 视频上传结果控制器 |
UploadViewController | 视频上传控制器 |
ProcessViewController | 视频合成控制器 |
Process | 视频合成storyboard |
ProcessModel | 视频合成模型 |
视频合成创建、开始及结束
- (instancetype)initWithSource:(NSString *)src
destination:(NSString *)dest
videoSettings:(BDCloudAVVideoOutputSettings *)videoSettings
audioSettings:(BDCloudAVAudioOutputSettings *)audioSettings {
if (self = [super init]) {
_session = [[BDCloudAVProcessSession alloc] initWithSource:[NSURL fileURLWithPath:src]
destination:[NSURL fileURLWithPath:dest]
videoSettings:videoSettings
audioSettings:audioSettings];
}
return self;
}
- (void)start {
[self.session start];
}
- (void)stop {
[self.session stop];
}
视频上传
摘录UploadVideoActionModel.m文件,使用百度云BOS SDK上传
- (void)start {
__weak typeof(self) wself = self;
dispatch_async(dispatch_get_global_queue(0, 0), ^{
__strong typeof(wself) sself = wself;
sself.stage = ActionStageOngoing;
if (sself.actionStartEvent) {
sself.actionStartEvent();
}
__block NSError* error;
// 从后台获取上传临时token
__block NSMutableDictionary *dic = [NSMutableDictionary new];
BDSSVLoginParam *loginParam = [BDSSVLoginParam shareInstance];
dic[@"user_id"] = loginParam.userid;
dic[@"file_type"] = @"0";//视频
dic[@"file_id"] = self.file.lastPathComponent;
dic[@"location"] = @"nolocation";
sself.uploadConf = [[ClientModel sharedInstance] fetchClientConf:dic];
dic[@"video_id"] = sself.uploadConf[@"video_id"];
if (sself.actionProgressEvent) {
sself.actionProgressEvent(0.1);
}
BOSObjectContent* content = [BOSObjectContent new];
content.objectData.file = sself.file;
BOSPutObjectRequest* putReq = [BOSPutObjectRequest new];
putReq.bucket = self.uploadConf[@"bucket"];
putReq.key = self.uploadConf[@"bos_key"];//sself.response.sourceKey;
putReq.objectContent = content;
sself.bosClient = [ClientModel sharedInstance].bosClient;
BCETask* task = [sself.bosClient putObject:putReq];
task.then(^(BCEOutput* output) {
if (output.progress) {
if (sself.actionProgressEvent) {
sself.actionProgressEvent(0.1 + output.progress.floatValue * 0.008);
}
}
if (output.response) {
if (sself.actionProgressEvent) {
sself.actionProgressEvent(0.9);
dic[@"status"] = @"11"; //success
}
}
if (output.error) {
error = output.error;
dic[@"status"] = @"12"; //failure
}
});
if ([sself monitorTaskCancel:task]) {
return;
}
if ([sself checkError:error]) {
return;
}
[[ClientModel sharedInstance] uploadClientFinish:dic];
if (sself.actionProgressEvent) {
sself.actionProgressEvent(1);
}
sself.stage = ActionStageAfter;
self.leftButtonTitle = @"返回首页";
self.message = @"上传成功!";
if (sself.actionFinishEvent) {
sself.actionFinishEvent(nil);
}
});
}