AVFoundation框架

2018-07-24  本文已影响21人  宙斯YY

一.AVAudioRecorder(录音)

1.info.plist配置
1.png
2.代码部分
//录音配置
-(NSDictionary *)getAudioSettingWithPCM {
    
    NSMutableDictionary * setting=[NSMutableDictionary dictionary];
    //录音数据格式-PCM
    setting[AVFormatIDKey]=[NSNumber numberWithInt:kAudioFormatLinearPCM];
    //采样率
    setting[AVSampleRateKey]=[NSNumber numberWithFloat:11025.0];
    //声道数 1为单声道, 2为双声道(立体声)
    setting[AVNumberOfChannelsKey]=[NSNumber numberWithInt:2];
    //录音质量
    setting[AVEncoderAudioQualityKey]=[NSNumber numberWithInt:AVAudioQualityMin];
    return setting;
}

//录音准备
-(void)recordPrepare
{
    //存放录音文件路径
    fullPath = NSTemporaryDirectory();
    fullPath = [fullPath stringByAppendingString:[NSString stringWithFormat:@"%f.caf", [[NSDate date] timeIntervalSince1970]]];
    //NSLog(@"fullPath:%@",fullPath);
    NSError * error;
    recorder=[[AVAudioRecorder alloc]initWithURL:[NSURL fileURLWithPath:fullPath] settings:[self getAudioSettingWithPCM] error:&error];
    [recorder prepareToRecord];
}

//开始录音
-(void)recordStart
{
    [recorder record];
}
//停止录音
-(void)recordStop
{
    [recorder stop];
}

/*
具体代理方法,属性可查看AVAudioRecorder头文件
*/
3.转码mp3(压缩大小)

使用Lame库
A.下载源文件 http://lame.sourceforge.net/download.php
B.下载运行脚本 https://github.com/kewlbear/lame-ios-build

2.png
C.把两个文件放到一个文件夹下,修改文件名Lame,根据github指令
./build-lame.sh运行脚本,会生成三个文件
3.png
其中fat-lame中包含头文件和.a静态库文件。
D.创建工具类LameTool,转码之前的录音文件caf为mp3文件
+ (NSString *)audioToMP3: (NSString *)sourcePath isDeleteSourchFile:(BOOL)isDelete{
    NSString *inPath = sourcePath;
    NSFileManager *fm = [NSFileManager defaultManager];
    if (![fm fileExistsAtPath:sourcePath]){
        NSLog(@"file path error!");
        return @"";
    }
    NSString *outPath = [[sourcePath stringByDeletingPathExtension] stringByAppendingString:@".mp3"];
    @try {
        int read, write;
        FILE *pcm = fopen([inPath cStringUsingEncoding:1], "rb");
        fseek(pcm, 4*1024, SEEK_CUR);
        FILE *mp3 = fopen([outPath cStringUsingEncoding:1], "wb");
        const int PCM_SIZE = 8192;
        const int MP3_SIZE = 8192;
        short int pcm_buffer[PCM_SIZE*2];
        unsigned char mp3_buffer[MP3_SIZE];
        lame_t lame = lame_init();
        lame_set_in_samplerate(lame, 11025.0);
        lame_set_VBR(lame, vbr_default);
        lame_init_params(lame);
        do {
            size_t size = (size_t)(2 * sizeof(short int));
            read = fread(pcm_buffer, size, PCM_SIZE, pcm);
            if (read == 0)
                write = lame_encode_flush(lame, mp3_buffer, MP3_SIZE);
            else
                write = lame_encode_buffer_interleaved(lame, pcm_buffer, read, mp3_buffer, MP3_SIZE);
            fwrite(mp3_buffer, write, 1, mp3);
        } while (read != 0);
        lame_close(lame);
        fclose(mp3);
        fclose(pcm);
    }
    @catch (NSException *exception) {
        NSLog(@"%@",[exception description]);
    }
    @finally {
        NSLog(@"mp3 build success!");
        if (isDelete) {
            NSError *error;
            [fm removeItemAtPath:sourcePath error:&error];
            if (error == nil){
                NSLog(@"source file is deleted!");
            }
        }
        return outPath;
    }
}

二.AudioServicesSystemSoundID(音效播放)

1.要求

音频文件必须打包成.caf、.aif、.wav中的一种(注意这是官方文档的说法,实际测试发现一些.mp3也可以播放)
这个段音效播放不能大于30s,这个30s不是我说的,是苹果的API说的

2.代码
-(void)playaudio:(NSString*)resourceName andCompletion:(void(^)(void))completion
{
    NSString *pathStr = [[NSBundle mainBundle] pathForResource:resourceName ofType:nil];
    if(!pathStr)
    {
        NSLog(@"文件不存在");
        return;
    }
    NSURL *path = [NSURL fileURLWithPath:pathStr];
    SystemSoundID systemsoundid=0;
    AudioServicesCreateSystemSoundID((__bridge CFURLRef _Nonnull)(path), &systemsoundid);
    //播放音效+震动
//    AudioServicesPlayAlertSoundWithCompletion(systemsoundid, ^{
//        completion();
//        //销毁资源
//        AudioServicesDisposeSystemSoundID(systemsoundid);
//    });
    //直接播放音效
    AudioServicesPlaySystemSoundWithCompletion(systemsoundid, ^{
        completion();
        //销毁资源
        //AudioServicesDisposeSystemSoundID(systemsoundid);
    });
}

三.AVAudioPlayer(音乐播放)

//播放
-(void)playmusic
{
    if(!player)
    {
        //后台播放
        //[[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayback error:nil];
        //[[AVAudioSession sharedInstance] setActive: YES error: nil];
        
        NSError * error;
        NSURL *path = [NSURL fileURLWithPath:fullPath];
        player=[[AVAudioPlayer alloc]initWithContentsOfURL:path error:&error];
        [player prepareToPlay];
        NSLog(@"播放错误:%@",error.description);
    }else
    {
        [player play];
        NSLog(@"播放录音:%@",fullPath);
    }
}
//停止播放
-(void)stop
{
    player.currentTime=0;
    [player stop];
}

三.AVPlayer(视频播放)

使用环境 优点 缺点
MPMoviePlayerController MediaPlayer 简单易用 不可定制
AVPlayerViewController AVKit 简单易用 不可定制
AVPlayer AVFoundation 可定制度高,功能强大 不支持流媒体
IJKPlayer IJKMediaFramework 定制度高,支持流媒体播放 使用稍复杂

AVPlayer相关类

看到有小伙伴写的更详细的参数含义:https://www.jianshu.com/p/29bfc7dc401a

我的项目中要实现类似抖音的效果,所以这里直接上核心代码。

1.jpeg
1.UI核心:

UITableView为主体,每个UITableViewCell铺一张视频第一帧图片,且铺一张透明的功能页面。

2.功能核心:

AVPlayer实例有且只有一个,每次滑动到新页面的时候,加载视频资源,切换AVPlayer播放源,当播放的时候,移除第一帧占位图,保证平滑过渡。
相关联的AVPlayerLayer也保证只有一个,使用一个ControlView进行管理AVPlayer的相关状态和播放相关,每次滑动到最新页面,移除之前的ControlView,添加到最新页面上。

获取最新滑动Cell位置

-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
    //获取当前播放位置indexPath
    if(scrollView==self.tableview)
    {
        NSArray<NSIndexPath*> * arr=[self.tableview indexPathsForVisibleRows];
        for (int i=0; i<arr.count; i++) {
            NSIndexPath * indexpath=arr[i];
            if(indexpath.row==self.currentIndex)
            {
                return;
            }
            [self playVideoAtIndex:indexpath];
        }
    }
}

播放当前位置的视频资源

-(void)playVideoAtIndex:(NSIndexPath*)indexpath
{
    //移除AVPlayer控制层和显示层
    if(self.controlview)
    {
        [self.controlview removeFromSuperview];
        self.controlview=nil;
    }
    if(self.playerlayer)
    {
        [self.playerlayer removeFromSuperlayer];
        self.playerlayer=nil;
    }
    
        
    //如果还是原来的页面,则不做加载
    if(indexpath.row>=self.dataList.count)
    {
        return;
    }
    
    VideoMainCell * currentCell=(VideoMainCell*)[self.tableview cellForRowAtIndexPath:indexpath];
    self.currentCell=currentCell;
    self.currentIndex=indexpath.row;
    VideoRelateDYData * data= self.dataList[self.currentIndex];
    [self getCommentData:data.id];
    
    //创建新的AVPlayerItem,AVPlayerLayer,切换AVPlayer当前AVPlayerItem,并监听AVPlayerItem的状态
    AVPlayerItem * item=[AVPlayerItem playerItemWithURL:[NSURL URLWithString:data.video_url]];
    self.playeritem=item;
    [self.playeritem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
    [self.playeritem addObserver:self forKeyPath:@"playbackBufferEmpty" options:NSKeyValueObservingOptionNew context:nil];
    [self.playeritem addObserver:self forKeyPath:@"playbackLikelyToKeepUp" options:NSKeyValueObservingOptionNew context:nil];
    [self.player replaceCurrentItemWithPlayerItem:self.playeritem];
    
    //创建控制层管理当前AVPlayer的播放进度状态
    self.controlview.player=self.player;
    self.controlview.videodata=self.dataList[self.currentIndex];
    self.controlview.videotype=VideoControlType_Comment;
    
    //使用控制层管理功能函数
    @weakify(self);
    self.controlview.block = ^(ControlBtn controlBtn) {
        @strongify(self);
        if(controlBtn==ControlBtn_Goods)
        {
            SDLog(@"商品列表");
            [self.controlview setVideotype:VideoControlType_Nothing];
            VideoRelateDYData * goodsData=self.dataList[self.currentIndex];
            if(goodsData.lists.count==1)
            {
                VideoRelateDYDetailData * data=goodsData.lists[0];
                SearchType searchType=SearchType_tb;
                if ([data.user_type isEqualToString:@"0"] || [data.user_type isEqualToString:@"1"]) {
                        searchType=SearchType_tb;
                }else if ([data.user_type isEqualToString:@"2"]){
                        searchType=SearchType_pdd;
                }else if ([data.user_type isEqualToString:@"3"]){
                        searchType=SearchType_jd;
                }
                if(self.isScreenRight)
                {
                        [self rotateScreen];
                }
                [self pushToStoreDetailViewControler:data.num_iid andSearchType:searchType];
                }else
                {
                    self.storeview=[VideoStoresView shareView];
                    self.storeview.goodsData=goodsData;
                    [self.storeview show:^{
                        [self.controlview setVideotype:VideoControlType_Comment];
                        [self.storeview removeFromSuperview];
                        self.storeview=nil;
                } andGoods:^(VideoRelateDYDetailData *data) {
                        [self.controlview setVideotype:VideoControlType_Comment];
                        [self.storeview removeFromSuperview];
                        self.storeview=nil;
                        SearchType searchType=SearchType_tb;
                        if ([data.user_type isEqualToString:@"0"] || [data.user_type isEqualToString:@"1"]) {
                            searchType=SearchType_tb;
                        }else if ([data.user_type isEqualToString:@"2"]){
                            searchType=SearchType_pdd;
                        }else if ([data.user_type isEqualToString:@"3"]){
                            searchType=SearchType_jd;
                        }
                        if(self.isScreenRight)
                        {
                            [self rotateScreen];
                        }
                        [self pushToStoreDetailViewControler:data.num_iid andSearchType:searchType];
                    }];
                }
            }else if(controlBtn==ControlBtn_Rotate)
            {
    
            }else if(controlBtn==ControlBtn_Input)
            {
                SDLog(@"输入");
                [self presentToLoginController:^(BOOL success) {
                    if(success)
                    {
                        [self.controlview setVideotype:VideoControlType_Nothing];
                        self.inputview=[VideoCommentInputView shareView];
                        [self.inputview show:CGRectMake(0, 0, SDScreenWidth,300) andClose:^{
                            [self.inputview removeFromSuperview];
                            self.inputview=nil;
                        } andText:^(NSString *text) {
                            VideoRelateDYData * videoData=self.dataList[self.currentIndex];
                            [self sendCommentData:videoData.id andContent:text andTime:[NSString stringWithFormat:@"%lld",self.player.currentItem.currentTime.value/self.player.currentItem.currentTime.timescale] andview:self.commentListview];
                        }];
                        [self.inputview.textview becomeFirstResponder];
                    }
                }];
            }
        };
        self.playerlayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
        self.playerlayer.backgroundColor = [UIColor blackColor].CGColor;
        self.playerlayer.frame = [UIScreen mainScreen].bounds;
        [self.currentCell.coverImg.layer addSublayer:self.playerlayer];
    
        //添加第一帧占位图覆盖到当前Cell上
        if(!self.coverImgview)
        {
            self.coverImgview=[[UIImageView alloc]initWithImage:data.firstvideoImg];
            self.coverImgview.frame=[UIScreen mainScreen].bounds;
            self.coverImgview.contentMode=UIViewContentModeScaleAspectFit;
            [self.currentCell.coverImg addSubview:self.coverImgview];
        }
}

监听AVPlayer的状态

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
    if(object==self.playeritem)
    {
        AVPlayerItem *playerItem = (AVPlayerItem *)object;
        if ([keyPath isEqualToString:@"status"]) {
            if ([playerItem status] == AVPlayerStatusReadyToPlay) {
                //播放器准备完成后,移除占位图,添加控制层
                dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                    [self.coverImgview removeFromSuperview];
                    self.coverImgview=nil;
                    [self.currentCell.coverImg addSubview:self.controlview];
                });
            }else if ([playerItem status] == AVPlayerStatusFailed || [playerItem status] == AVPlayerStatusUnknown) {
                [JSXHUD showMessage:@"播放失败"];
                [self.player pause];
            }
        }else if ([keyPath isEqualToString:@"playbackBufferEmpty"]) { //监听播放器在缓冲数据的状态
            SDLog(@"缓冲不足暂停了");
            [self.player pause];
        }else if ([keyPath isEqualToString:@"playbackLikelyToKeepUp"]) {
            SDLog(@"缓冲达到可播放程度了");
            //由于 AVPlayer 缓存不足就会自动暂停,所以缓存充足了需要手动播放,才能继续播放
            [self.player play];
            
        }
    }
}

控制层VideoMainControlView.m

@interface VideoMainControlView()
@end
@implementation VideoMainControlView

-(void)awakeFromNib
{
    [super awakeFromNib];
    [self.playBtn addTarget:self action:@selector(clickPlay:) forControlEvents:UIControlEventTouchUpInside];
    [self.bottomview addSubview:self.bottom1];
    [self.bottomview addSubview:self.bottom2];
    [self setUserInteractionEnabled:YES];
    UITapGestureRecognizer * tap=[[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(clickBG)];
    [self addGestureRecognizer:tap];
}

-(void)layoutSubviews
{
    [super layoutSubviews];
    [self.bottom1 mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.top.right.bottom.offset(0);
    }];
    
    [self.bottom2 mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.top.right.bottom.offset(0);
    }];
}

-(void)clickBG
{
    if(self.videotype==VideoControlType_Play)
    {
        self.videotype=VideoControlType_Comment;
    }else if(self.videotype==VideoControlType_Comment)
    {
        self.videotype=VideoControlType_Play;
    }else
    {
        self.videotype=VideoControlType_Comment;
    }
}

-(void)clickPlay:(UIButton*)btn
{
    if(btn.selected)
    {
        [self.player play];
    }else
    {
        [self.player pause];
    }
    btn.selected=!btn.selected;
}

/** 拖动视频进度 */
- (void)seekPlayerTimeTo:(NSTimeInterval)time
{
    [self.player pause];
    [self.player seekToTime:CMTimeMake(time,10) completionHandler:^(BOOL finished) {
        [self.player play];
    }];
    
}

-(void)sliderValueChanged:(UISlider*)slider
{
    if(self.player)
    {
        CGFloat duration = self.player.currentItem.duration.value / self.player.currentItem.duration.timescale; //视频总时间
        [self seekPlayerTimeTo:slider.value*duration];
    }
}

-(void)refreshProgress
{
    if(self.bottom2&&self.player)
    {
        CGFloat duration = CMTimeGetSeconds(self.player.currentItem.duration); //视频总时间
        CGFloat currentTime = CMTimeGetSeconds(self.player.currentItem.currentTime); //视频当前时间
        self.bottom2.currentTime.text=[self convert:currentTime];
        self.bottom2.totalTime.text=[self convert:duration];
        self.bottom2.progressview.value=currentTime/duration;
        [self.bottom2.progressview addTarget:self action:@selector(sliderValueChanged:) forControlEvents:UIControlEventValueChanged];
    }
    
    if(self.bottom1&&self.player)
    {
        CGFloat duration = CMTimeGetSeconds(self.player.currentItem.duration); //视频总时间
        CGFloat currentTime = CMTimeGetSeconds(self.player.currentItem.currentTime); //视频当前时间
        self.bottom1.progressview.progress=currentTime/duration;
    }
}

- (NSString *)convert:(CGFloat)time{
    int minute = time / 60;
    int second = time - minute * 60;
    NSString *minuteString;
    NSString *secondString;
    if(minute < 10){
        minuteString = [NSString stringWithFormat:@"0%d", minute];
    }else{
        minuteString = [NSString stringWithFormat:@"%d", minute];
    }
    if(second < 10){
        secondString = [NSString stringWithFormat:@"0%d", second];
    }else{
        secondString = [NSString stringWithFormat:@"%d", second];
    }
    return [NSString stringWithFormat:@"%@:%@", minuteString, secondString];
}

#pragma mark - Setter

-(void)setPlayer:(AVPlayer *)player
{
    _player=player;
    [player addPeriodicTimeObserverForInterval:CMTimeMake(1,10) queue:NULL usingBlock:^(CMTime time) {
        [self refreshProgress];
    }];
}

-(void)setVideodata:(VideoRelateDYData *)videodata
{
    _videodata=videodata;
    //[self.coverImageView setImageWithURLString:videodata.coverImg placeholder:[UIImage imageNamed:PlaceHolderImg_TTG]];
    self.bottom1.goodsTitleLab.text=videodata.title;
    if(videodata.lists.count>0)
    {
        self.bottom1.goodPicUrl.hidden=NO;
        self.bottom1.goodsDescLab.hidden=NO;
        VideoRelateDYDetailData * detailData=videodata.lists[0];
        [self.bottom1.goodPicUrl sd_setImageWithURL:[NSURL URLWithString:detailData.pict_url] placeholderImage:[UIImage imageNamed:PlaceHolderImg_TTG]];
        self.bottom1.goodsDescLab.text=[NSString stringWithFormat:@"%ld件商品",videodata.lists.count];
    }else
    {
        self.bottom1.goodPicUrl.hidden=YES;
        self.bottom1.goodsDescLab.hidden=YES;
    }
}

-(void)setVideotype:(VideoControlType)videotype
{
    _videotype=videotype;
    if(videotype==VideoControlType_Comment)
    {
        self.playBtn.hidden=YES;
        self.bottom1.hidden=NO;
        self.bottom2.hidden=YES;
    }else if(videotype==VideoControlType_Play)
    {
        self.playBtn.hidden=NO;
        self.bottom1.hidden=YES;
        self.bottom2.hidden=NO;
    }else if(videotype==VideoControlType_Nothing)
    {
        self.playBtn.hidden=YES;
        self.bottom1.hidden=YES;
        self.bottom2.hidden=YES;
    }
}

#pragma mark - Getter

-(VideoMainBottomView *)bottom1
{
    if(!_bottom1)
    {
        _bottom1=[VideoMainBottomView shareView];
        @weakify(self);
        _bottom1.block = ^(int type) {
            @strongify(self);
            if(type==0)
            {
                //点击输入框
                if(self.block)
                {
                    self.block(ControlBtn_Input);
                }
            }
            else if(type==1)
            {
                //点击商品
                if(self.block)
                {
                    self.block(ControlBtn_Goods);
                }
            }
        };
    }
    return _bottom1;
}

-(VideoMainBottomView2 *)bottom2
{
    if(!_bottom2)
    {
        _bottom2=[VideoMainBottomView2 shareView];
        @weakify(self);
        _bottom2.block = ^{
            @strongify(self);
            if(self.block)
            {
                self.block(ControlBtn_Rotate);
            }
        };
    }
    return _bottom2;
}

-(void)dealloc
{
    
}

显示层VideoMainCell.m

@interface VideoMainCell()<FDanmakuViewProtocol>
{
    NSTimeInterval clickLikeTime;
}
@end

@implementation VideoMainCell

- (void)awakeFromNib {
    [super awakeFromNib];
    
    self.commentBtn.titleLabel.textAlignment=NSTextAlignmentCenter;
    [self.commentBtn addTarget:self action:@selector(clickComment) forControlEvents:UIControlEventTouchUpInside];
    self.likeBtn.titleLabel.textAlignment=NSTextAlignmentCenter;
    [self.likeBtn addTarget:self action:@selector(clickLike:) forControlEvents:UIControlEventTouchUpInside];
    self.shareBtn.titleLabel.textAlignment=NSTextAlignmentCenter;
    [self.shareBtn addTarget:self action:@selector(clickShare) forControlEvents:UIControlEventTouchUpInside];
    self.dmview.delegate=self;
}

-(void)clickComment
{
    if(self.block)
    {
        self.block(FuncButtton_Comment);
    }
}

-(void)clickLike:(UIButton*)btn
{
    btn.selected=!btn.selected;
    if(!btn.selected)
    {
        self.commentdata.zanNum=[NSString stringWithFormat:@"%d",[self.commentdata.zanNum intValue]-1];
        self.commentdata.isLaud=@"0";
    }else
    {
        self.commentdata.zanNum=[NSString stringWithFormat:@"%d",[self.commentdata.zanNum intValue]+1];
        self.commentdata.isLaud=@"1";
    }
    [self.likeBtn setTitle:self.commentdata.zanNum forState:UIControlStateNormal];
    [self.likeBtn setTitle:self.commentdata.zanNum forState:UIControlStateSelected];
    btn.transform=CGAffineTransformMakeScale(0.6, 0.6);
    [UIView animateWithDuration:0.3 animations:^{
        btn.transform=CGAffineTransformMakeScale(1.4,1.4);
    } completion:^(BOOL finished) {
        btn.transform=CGAffineTransformIdentity;
    }];
    
    if([[NSDate date]timeIntervalSince1970]-clickLikeTime>2)
    {
        if(self.block)
        {
            self.block(FuncButtton_Like);
        }
    }
    clickLikeTime=[[NSDate date]timeIntervalSince1970];
}

-(void)clickShare
{
    if(self.block)
    {
        self.block(FuncButtton_Share);
    }
}


#pragma mark - Setter

-(void)setCommentdata:(VideoRelateCommentDYData *)commentdata
{
    _commentdata=commentdata;
    if([commentdata.isLaud intValue]==0)
    {
        self.likeBtn.selected=NO;
    }else
    {
        self.likeBtn.selected=YES;
    }
    
    [self.commentBtn setTitle:commentdata.commentNum forState:UIControlStateNormal];
    [self.likeBtn setTitle:commentdata.zanNum forState:UIControlStateNormal];
    [self.likeBtn setTitle:commentdata.zanNum forState:UIControlStateSelected];
    [self.shareBtn setTitle:commentdata.zfNum forState:UIControlStateNormal];
    
    if(commentdata.commentNum.length==0)
    {
        [self.commentBtn setTitle:@"0" forState:UIControlStateNormal];
    }
    
    if(commentdata.zanNum.length==0)
    {
        [self.likeBtn setTitle:@"0" forState:UIControlStateNormal];
        [self.likeBtn setTitle:@"0" forState:UIControlStateSelected];
    }
    if(commentdata.zfNum.length==0)
    {
        [self.shareBtn setTitle:@"0" forState:UIControlStateNormal];
    }
    
    if(self.dmview.modelsArr.count==0)
    {
        for (int i=0; i<commentdata.comment_list.count; i++) {
            VideoRelateCommentDYData_detail * detailData=commentdata.comment_list[i];
            FDanmakuModel *model = [[FDanmakuModel alloc]init];
            model.beginTime = [detailData.playTime doubleValue];
            model.liveTime = 5;
            model.content = detailData.content;
            [self.dmview.modelsArr addObject:model];
        }
    }
}

#pragma mark - FDanmakuView相关

-(NSTimeInterval)currentTime {
    static double time = 0;
    time += 0.3 ;
    return time;
}

- (UIView *)danmakuViewWithModel:(FDanmakuModel*)model {
    UILabel *label = [UILabel new];
    label.text = model.content;
    label.textColor=[UIColor whiteColor];
    [label sizeToFit];
    return label;
    
}
- (void)danmuViewDidClick:(UIView *)danmuView at:(CGPoint)point {
    NSLog(@"%@ %@",danmuView,NSStringFromCGPoint(point));
}

视频控制器VideoMainViewController.m

@interface VideoMainViewController ()<UITableViewDelegate,UITableViewDataSource>

@property(nonatomic,strong)NSMutableArray * dataList;
@property(nonatomic,strong)VideoMainControlView * controlview;
@property(nonatomic,assign)NSInteger currentIndex;
@property(nonatomic,strong)AVPlayer *player;
@property(nonatomic,strong)AVPlayerItem *playeritem;
@property(nonatomic,strong)AVPlayerLayer * playerlayer;
@property(nonatomic,strong)UIImageView * coverImgview;

//数据相关
@property(nonatomic,strong) VideoRelateCommentDYData *commentData;

//UI相关
@property(nonatomic,weak)VideoMainCell *currentCell;
@property(nonatomic,strong) VideoCommentListView *commentListview;
@property(nonatomic,strong) VideoCommentInputView *inputview;
@property(nonatomic,strong) VideoStoresView *storeview;

@property(nonatomic,assign) BOOL isScreenRight;
@property(nonatomic, assign) int currentPage;

@end

@implementation VideoMainViewController


#pragma mark - 生命周期

-(void)addSubViews
{
    self.tableview.pagingEnabled=YES;
    self.tableview.scrollsToTop=NO;
    self.tableview.separatorStyle=UITableViewCellSeparatorStyleNone;
    [self.tableview registerNib:[UINib nibWithNibName:@"VideoMainCell" bundle:nil] forCellReuseIdentifier:@"videoMainCell"];
    @weakify(self);
    [[self.backBtn rac_signalForControlEvents:UIControlEventTouchUpInside]subscribeNext:^(__kindof UIControl * _Nullable x) {
        @strongify(self);
        if(self.isScreenRight)
        {
            [[UIDevice currentDevice]setValue:@(UIInterfaceOrientationPortrait) forKey:@"orientation"];
            self.isScreenRight=!self.isScreenRight;
        }else
        {
            if(self.playerlayer)
            {
                [self.playerlayer removeFromSuperlayer];
            }
            if(self.playeritem)
            {
                self.playeritem=nil;
            }
            if(self.player)
            {
                [self.player pause];
                self.player=nil;
            }
            [self.navigationController popViewControllerAnimated:YES];
        }
    }];
    self.currentPage=1;
}

-(void)registerNotification
{
    [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(keyboardWasShown:) name:UIKeyboardDidShowNotification object:nil];
    
    [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(keyboardWasHidden:) name:UIKeyboardDidHideNotification object:nil];
    
    [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(change:) name:UIDeviceOrientationDidChangeNotification object:nil];
    
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerItemDidPlayToEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
}

-(void)dealloc
{
    [self.playeritem removeObserver:self forKeyPath:@"status"];
    [self.playeritem removeObserver:self forKeyPath:@"playbackBufferEmpty"];
    [self.playeritem removeObserver:self forKeyPath:@"playbackLikelyToKeepUp"];
    [[NSNotificationCenter defaultCenter]removeObserver:self];
}


-(void)keyboardWasShown:(NSNotification*)notification
{
    NSDictionary *info = [notification userInfo];
    NSValue *value = [info objectForKey:UIKeyboardFrameBeginUserInfoKey];
    CGSize keyboardSize = [value CGRectValue].size;
    if(self.inputview!=nil)
    {
        self.inputview.frame=CGRectMake(0, 0, SDScreenWidth,self.view.height-keyboardSize.height);
    }
}

-(void)keyboardWasHidden:(NSNotification*)notification
{
    if(self.inputview!=nil)
    {
        [self.inputview removeFromSuperview];
        self.inputview=nil;
    }
}

-(void)change:(NSNotification*)notification
{
    CGFloat width=[UIScreen mainScreen].bounds.size.width;
    CGFloat height=[UIScreen mainScreen].bounds.size.height;
    if(width/height<1.0)
    {
        //SDLog(@"竖屏:%ld",self.player.currentPlayIndex);
        //[self playTheIndex:self.player.currentPlayIndex];
        //[self playTheVideoAtIndexPath:[NSIndexPath indexPathForRow:self.player.currentPlayIndex inSection:0] scrollToTop:YES];
    }else
    {
        //SDLog(@"横屏:%ld",self.player.currentPlayIndex);
        //[self playTheIndex:self.player.currentPlayIndex];
        //[self playTheVideoAtIndexPath:[NSIndexPath indexPathForRow:self.player.currentPlayIndex inSection:0] scrollToTop:YES];
    }
}

-(void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    self.fd_interactivePopDisabled = YES;
    self.navigationController.fd_fullscreenPopGestureRecognizer.enabled = NO;
    [self.navigationController setNavigationBarHidden:YES animated:animated];
}

-(void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    if(self.player)
    {
        [self.player play];
    }
}

-(void)viewDidDisappear:(BOOL)animated
{
    [super viewDidDisappear:animated];
    if(self.player)
    {
        [self.player pause];
    }
}

#pragma mark - ScrollView代理

-(void)rotateScreen
{
    if(self.isScreenRight)
    {
        [[UIDevice currentDevice]setValue:@(UIInterfaceOrientationPortrait) forKey:@"orientation"];
    }else
    {
        [[UIDevice currentDevice]setValue:@(UIInterfaceOrientationLandscapeRight) forKey:@"orientation"];
    }
    //点击旋转
    self.isScreenRight=!self.isScreenRight;
}

-(void)scrollViewDidScroll:(UIScrollView *)scrollView{
    
}

-(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
    if(scrollView==self.tableview)
    {
        CGPoint point =  [scrollView.panGestureRecognizer translationInView:self.view];
        if (point.y>0) {
            SDLog(@"------往上滚动");
        }else{
            SDLog(@"------往下滚动");
        }
    }
}

-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
    if(scrollView==self.tableview)
    {
        NSArray<NSIndexPath*> * arr=[self.tableview indexPathsForVisibleRows];
        for (int i=0; i<arr.count; i++) {
            NSIndexPath * indexpath=arr[i];
            if(indexpath.row==self.currentIndex)
            {
                return;
            }
            [self playVideoAtIndex:indexpath];
        }
    }
    
//    if(self.dataList.count-1==self.currentIndex)
//    {
//        self.currentPage++;
//        [self getVideoData];
//    }
}

#pragma mark - TableView代理和数据源

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return self.dataList.count;
}

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return SDScreenHeight;
}

-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    VideoMainCell * cell= [tableView dequeueReusableCellWithIdentifier:@"videoMainCell"];
    cell.selectionStyle=UITableViewCellSelectionStyleNone;
    VideoRelateDYData * data=self.dataList[indexPath.row];
    [cell.coverImg setImage:data.firstvideoImg];
    cell.commentdata=self.commentData;
    @weakify(self);
    cell.block = ^(FuncButtton funcBtn) {
        @strongify(self);
        if(funcBtn==FuncButtton_Comment)
        {
            SDLog(@"评论列表");
            self.commentListview=[VideoCommentListView shareView];
            VideoRelateDYData * videoData=self.dataList[indexPath.row];
            self.commentListview.vid=videoData.id;
            [self.commentListview show:^{
                [self.commentListview removeFromSuperview];
                self.commentListview=nil;
            } andtextBlock:^{
                [self presentToLoginController:^(BOOL success) {
                    if(success)
                    {
                        self.inputview=[VideoCommentInputView shareView];
                        [self.inputview show:CGRectMake(0, 0, SDScreenWidth,300) andClose:^{
                            [self.inputview removeFromSuperview];
                            self.inputview=nil;
                        } andText:^(NSString *text) {
                            VideoRelateDYData * videoData=self.dataList[indexPath.row];
                            [self sendCommentData:videoData.id andContent:text andTime:[NSString stringWithFormat:@"%lld",self.player.currentItem.currentTime.value/self.player.currentItem.currentTime.timescale] andview:self.commentListview];
                        }];
                        [self.inputview.textview becomeFirstResponder];
                    }
                }];
            }];
        }else if(funcBtn==FuncButtton_Like)
        {
            SDLog(@"点赞");
            [self presentToLoginController:^(BOOL success) {
                if(success)
                {
                    VideoRelateDYData * videoData=self.dataList[indexPath.row];
                    [self sendLikeData:videoData.id];
                }
            }];
            
        }else if(funcBtn==FuncButtton_Share)
        {
            SDLog(@"分享");
            [self presentToLoginController:^(BOOL success) {
                if(success)
                {
                    VideoRelateDYData * videoData=self.dataList[indexPath.row];
                    [self sendShareData:videoData.id];
                    NSString * url=[NSString stringWithFormat:@"https://gl.koalamom.net/h5/video.html?videoid=%@",videoData.id];
                    [self shareWebOfThirdparty:InvateFriendsTitle andDesc:InvateFriendsDesc andIcon:[UIImage imageNamed:@"logo3"] andwebUrl:url];
                }
            }];
            
        }
    };
    return cell;
}


#pragma mark - 数据源

-(void)getInitData
{
    [self getVideoData];
}


-(void)dealLastData
{
    [SVProgressHUD showWithStatus:@"加载中..."];
    for (int i=0; i<self.videoArr.count; i++) {
        videoModel * originData=self.videoArr[i];
        VideoRelateDYData * resData=[[VideoRelateDYData alloc]init];
        resData.id=[NSString stringWithFormat:@"%ld",(long)originData.ID] ;
        resData.vid=originData.vid;
        resData.title=originData.title;
        resData.desc=originData.desc;
        resData.coverImg=originData.coverImg;
        resData.addtime=originData.addtime;
        resData.video_url=originData.video_url;
        NSMutableArray * tempList=[NSMutableArray array];
        for (int i=0; i<originData.lists.count; i++) {
            VideoRelateDYDetailData * data=originData.lists[i];
            [tempList addObject:data];
        }
        resData.lists=tempList;
        resData.firstvideoImg=[self getVideoPreViewImage:[NSURL URLWithString:originData.video_url]];
        [self.dataList addObject:resData];
    }
    [SVProgressHUD dismiss];
    [self.tableview reloadData];
    if(self.dataList.count>0)
    {
        [self.tableview scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:self.index inSection:0] atScrollPosition:UITableViewScrollPositionNone animated:NO];
        [self playVideoAtIndex:[NSIndexPath indexPathForRow:self.index inSection:0]];
    }
}

-(void)getVideoData
{
    [SVProgressHUD showWithStatus:@"加载中..."];
    NSMutableDictionary * params = [NSMutableDictionary dictionary];
    params[@"pageno"]=@(self.currentPage);
    params[@"pagesize"]=@10;
    [JSXHttpTool Post:Interface_VideoList params:params success:^(id json) {
        [SVProgressHUD dismiss];
        NSString * returnCode = json[@"status"];
        NSMutableArray * data = json[@"data"];
        if([returnCode intValue]==40001)
        {
            for (int i=0; i<data.count; i++) {
                NSDictionary * dict=data[i];
                VideoRelateDYData * data=[VideoRelateDYData mj_objectWithKeyValues:dict];
                [self.dataList addObject:data];
                //self.playeritem=[AVPlayerItem playerItemWithURL:[NSURL URLWithString:data.video_url]];
                data.firstvideoImg=[self getVideoPreViewImage:[NSURL URLWithString:data.video_url]];
            }
            [self.tableview reloadData];
        }
        
        if(data.count>0)
        {
            [self.tableview scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:self.index inSection:0] atScrollPosition:UITableViewScrollPositionNone animated:NO];
            [self playVideoAtIndex:[NSIndexPath indexPathForRow:self.index inSection:0]];
        }
    } failure:^(NSError *error) {
        [SVProgressHUD dismiss];
    }];
}
-(void)getCommentData:(NSString*)vid
{
    NSMutableDictionary * params = [NSMutableDictionary dictionary];
    params[@"vid"]=vid;
    params[@"uid"]=[loginUserDataTools getUserInfo].uid;
    params[@"pageno"]=@1;
    params[@"pagesize"]=@30;
    [JSXHttpTool Post:Interface_VideoCommentList params:params success:^(id json) {
        NSString * returnCode = json[@"status"];
        NSDictionary * data = json[@"data"];
        if([returnCode intValue]==40001)
        {
            self.commentData=[VideoRelateCommentDYData mj_objectWithKeyValues:data];
        }else
        {
            self.commentData=[[VideoRelateCommentDYData alloc]init];
        }
        [self.tableview reloadData];
    } failure:^(NSError *error) {
    }];
}

-(void)sendCommentData:(NSString*)vid andContent:(NSString*)content andTime:(NSString*)time andview:(VideoCommentListView*)commentListview
{
    NSMutableDictionary * params = [NSMutableDictionary dictionary];
    params[@"vid"]=vid;
    params[@"uid"]=[loginUserDataTools getUserInfo].uid;
    params[@"content"]=content;
    params[@"ptime"]=time;
    [JSXHttpTool Post:Interface_VideoAddComment params:params success:^(id json) {
        NSString * returnCode = json[@"status"];
        if([returnCode intValue]==40001)
        {
            if(commentListview)
            {
                commentListview.vid=vid;
            }
            [self getCommentData:vid];
        }
    } failure:^(NSError *error) {
    }];
}

-(void)sendLikeData:(NSString*)vid
{
    NSMutableDictionary * params = [NSMutableDictionary dictionary];
    params[@"vid"]=vid;
    params[@"uid"]=[loginUserDataTools getUserInfo].uid;
    [JSXHttpTool Post:Interface_VideoAddLike params:params success:^(id json) {
        NSString * returnCode = json[@"status"];
        if([returnCode intValue]==40001)
        {
        }
    } failure:^(NSError *error) {
    }];
}

-(void)sendShareData:(NSString*)vid
{
    NSMutableDictionary * params = [NSMutableDictionary dictionary];
    params[@"vid"]=vid;
    [JSXHttpTool Post:Interface_VideoAddShare params:params success:^(id json) {
        NSString * returnCode = json[@"status"];
        if([returnCode intValue]==40001)
        {
        }
    } failure:^(NSError *error) {
    }];
}

// 获取视频第一帧
- (UIImage*)getVideoPreViewImage:(NSURL *)path
{
    AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:path options:nil];
    AVAssetImageGenerator *assetGen = [[AVAssetImageGenerator alloc] initWithAsset:asset];
    assetGen.appliesPreferredTrackTransform = YES;
    CMTime time = CMTimeMakeWithSeconds(0.0, 600);
    NSError *error = nil;
    CMTime actualTime;
    CGImageRef image = [assetGen copyCGImageAtTime:time actualTime:&actualTime error:&error];
    UIImage *videoImage = [[UIImage alloc] initWithCGImage:image];
    CGImageRelease(image);
    return videoImage;
}

#pragma mark - 播放器相关

-(void)playVideoAtIndex:(NSIndexPath*)indexpath
{
    //移除AVPlayer控制层和显示层
    if(self.controlview)
    {
        [self.controlview removeFromSuperview];
        self.controlview=nil;
    }
    if(self.playerlayer)
    {
        [self.playerlayer removeFromSuperlayer];
        self.playerlayer=nil;
    }
    
        
    //如果还是原来的页面,则不做加载
    if(indexpath.row>=self.dataList.count)
    {
        return;
    }
    
    VideoMainCell * currentCell=(VideoMainCell*)[self.tableview cellForRowAtIndexPath:indexpath];
    self.currentCell=currentCell;
    self.currentIndex=indexpath.row;
    VideoRelateDYData * data= self.dataList[self.currentIndex];
    [self getCommentData:data.id];
    
    //创建新的AVPlayerItem,AVPlayerLayer,切换AVPlayer当前AVPlayerItem,并监听AVPlayerItem的状态
    AVPlayerItem * item=[AVPlayerItem playerItemWithURL:[NSURL URLWithString:data.video_url]];
    self.playeritem=item;
    [self.playeritem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
    [self.playeritem addObserver:self forKeyPath:@"playbackBufferEmpty" options:NSKeyValueObservingOptionNew context:nil];
    [self.playeritem addObserver:self forKeyPath:@"playbackLikelyToKeepUp" options:NSKeyValueObservingOptionNew context:nil];
    [self.player replaceCurrentItemWithPlayerItem:self.playeritem];
    
    //创建控制层管理当前AVPlayer的播放进度状态
    self.controlview.player=self.player;
    self.controlview.videodata=self.dataList[self.currentIndex];
    self.controlview.videotype=VideoControlType_Comment;
    
    //使用控制层管理功能函数
    @weakify(self);
    self.controlview.block = ^(ControlBtn controlBtn) {
        @strongify(self);
        if(controlBtn==ControlBtn_Goods)
        {
            SDLog(@"商品列表");
            [self.controlview setVideotype:VideoControlType_Nothing];
            VideoRelateDYData * goodsData=self.dataList[self.currentIndex];
            if(goodsData.lists.count==1)
            {
                VideoRelateDYDetailData * data=goodsData.lists[0];
                SearchType searchType=SearchType_tb;
                if ([data.user_type isEqualToString:@"0"] || [data.user_type isEqualToString:@"1"]) {
                        searchType=SearchType_tb;
                }else if ([data.user_type isEqualToString:@"2"]){
                        searchType=SearchType_pdd;
                }else if ([data.user_type isEqualToString:@"3"]){
                        searchType=SearchType_jd;
                }
                if(self.isScreenRight)
                {
                        [self rotateScreen];
                }
                [self pushToStoreDetailViewControler:data.num_iid andSearchType:searchType];
                }else
                {
                    self.storeview=[VideoStoresView shareView];
                    self.storeview.goodsData=goodsData;
                    [self.storeview show:^{
                        [self.controlview setVideotype:VideoControlType_Comment];
                        [self.storeview removeFromSuperview];
                        self.storeview=nil;
                } andGoods:^(VideoRelateDYDetailData *data) {
                        [self.controlview setVideotype:VideoControlType_Comment];
                        [self.storeview removeFromSuperview];
                        self.storeview=nil;
                        SearchType searchType=SearchType_tb;
                        if ([data.user_type isEqualToString:@"0"] || [data.user_type isEqualToString:@"1"]) {
                            searchType=SearchType_tb;
                        }else if ([data.user_type isEqualToString:@"2"]){
                            searchType=SearchType_pdd;
                        }else if ([data.user_type isEqualToString:@"3"]){
                            searchType=SearchType_jd;
                        }
                        if(self.isScreenRight)
                        {
                            [self rotateScreen];
                        }
                        [self pushToStoreDetailViewControler:data.num_iid andSearchType:searchType];
                    }];
                }
            }else if(controlBtn==ControlBtn_Rotate)
            {
    
            }else if(controlBtn==ControlBtn_Input)
            {
                SDLog(@"输入");
                [self presentToLoginController:^(BOOL success) {
                    if(success)
                    {
                        [self.controlview setVideotype:VideoControlType_Nothing];
                        self.inputview=[VideoCommentInputView shareView];
                        [self.inputview show:CGRectMake(0, 0, SDScreenWidth,300) andClose:^{
                            [self.inputview removeFromSuperview];
                            self.inputview=nil;
                        } andText:^(NSString *text) {
                            VideoRelateDYData * videoData=self.dataList[self.currentIndex];
                            [self sendCommentData:videoData.id andContent:text andTime:[NSString stringWithFormat:@"%lld",self.player.currentItem.currentTime.value/self.player.currentItem.currentTime.timescale] andview:self.commentListview];
                        }];
                        [self.inputview.textview becomeFirstResponder];
                    }
                }];
            }
        };
        self.playerlayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
        self.playerlayer.backgroundColor = [UIColor blackColor].CGColor;
        self.playerlayer.frame = [UIScreen mainScreen].bounds;
        [self.currentCell.coverImg.layer addSublayer:self.playerlayer];
    
        //添加第一帧占位图覆盖到当前Cell上
        if(!self.coverImgview)
        {
            self.coverImgview=[[UIImageView alloc]initWithImage:data.firstvideoImg];
            self.coverImgview.frame=[UIScreen mainScreen].bounds;
            self.coverImgview.contentMode=UIViewContentModeScaleAspectFit;
            [self.currentCell.coverImg addSubview:self.coverImgview];
        }
}

- (void)playerItemDidPlayToEnd:(NSNotification *)notification{
    [self rerunPlayVideo];
}
-(void)rerunPlayVideo{
    if (!self.player) {
        return;
    }
    CGFloat a=0;
    NSInteger dragedSeconds = floorf(a);
    CMTime dragedCMTime = CMTimeMake(dragedSeconds, 1);
    [self.player seekToTime:dragedCMTime];
    [self.player play];
}



//监听获得消息
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
    if(object==self.playeritem)
    {
        AVPlayerItem *playerItem = (AVPlayerItem *)object;
        if ([keyPath isEqualToString:@"status"]) {
            if ([playerItem status] == AVPlayerStatusReadyToPlay) {
                //播放器准备完成后,移除占位图,添加控制层
                dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                    [self.coverImgview removeFromSuperview];
                    self.coverImgview=nil;
                    [self.currentCell.coverImg addSubview:self.controlview];
                });
            }else if ([playerItem status] == AVPlayerStatusFailed || [playerItem status] == AVPlayerStatusUnknown) {
                [JSXHUD showMessage:@"播放失败"];
                [self.player pause];
            }
        }else if ([keyPath isEqualToString:@"playbackBufferEmpty"]) { //监听播放器在缓冲数据的状态
            SDLog(@"缓冲不足暂停了");
            [self.player pause];
        }else if ([keyPath isEqualToString:@"playbackLikelyToKeepUp"]) {
            SDLog(@"缓冲达到可播放程度了");
            //由于 AVPlayer 缓存不足就会自动暂停,所以缓存充足了需要手动播放,才能继续播放
            [self.player play];
            
        }
    }
}


#pragma mark - Getter

-(VideoMainControlView *)controlview
{
    if(!_controlview)
    {
        _controlview=[VideoMainControlView shareView];
        [_controlview setUserInteractionEnabled:YES];
        _controlview.frame=[UIScreen mainScreen].bounds;
    }
    return _controlview;
}

-(NSMutableArray *)dataList
{
    if(!_dataList)
    {
        _dataList=[NSMutableArray array];
    }
    return _dataList;
}

-(AVPlayer *)player
{
    if(!_player&&self.playeritem)
    {
        _player=[AVPlayer playerWithPlayerItem:self.playeritem];
    }
    return _player;
}
上一篇下一篇

猜你喜欢

热点阅读