所有文档

          智能小视频 Smartsmallvideo

          功能说明

          视频流

          代码目录

          image.png

          文件 说明
          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];
                      }];
                  });
              }];

          视频采集

          代码目录

          image.png

          文件 说明
          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);
              }
          }

          数据流图

          shipinzhencaijichuliliucheng.png

          视频录制

          代码目录

          与视频采集相同

          开启摄像头录制

          摘录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;
                  }
              }
          }

          数据流图

          shipinluzhiliucheng.png

          视频预览

          代码目录

          image.png

          文件 说明
          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];
          }

          数据流图

          shipinyulanliucheng.png

          视频特效

          支持特效的类型

          • 基础美颜(美白、磨皮)
          • 高级美颜(大眼、瘦脸)
          • 风格滤镜

            • 颜色调整(对比度、亮度、曝光度、色温、饱和度、锐度)
            • 智能调整(回忆、少女、红润、都市、微光、红唇)
          • 人脸贴纸,只有少数贴纸使用内置,其他需要使用网络下载

            • 2D贴纸
            • 3D贴纸
            • 手势贴纸

          使用方式,可参见

          • 颜色调整,参见ImageFilterView.swift文件
          • 滤镜切换,参见StyleFilterView.swift文件
          • 美颜调整,参加BeautyFilterView.swift文件

          视频合成&&上传

          代码目录

          image.png

          文件 说明
          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);
                  }
              });
          }
          上一篇
          整体流程
          下一篇
          后端服务集成说明