一.AVAudioRecorder(录音)
1.info.plist配置
1.png2.代码部分
//录音配置
-(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
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相关类
- AVPlayer:控制播放器的播放,暂停,播放速度
- AVURLAsset : AVAsset 的一个子类,使用 URL 进行实例化,实例化对象包换 URL 对应视频资源的所有信息。
- AVPlayerItem:管理资源对象,提供播放数据源
- AVPlayerLayer:负责显示视频,如果没有添加该类,只有声音没有画面
看到有小伙伴写的更详细的参数含义:https://www.jianshu.com/p/29bfc7dc401a
我的项目中要实现类似抖音的效果,所以这里直接上核心代码。
1.jpeg1.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;
}
网友评论