视频播放与音频播放

苹果提供了AVFAudio与AVFoundation作为音频与视频源生框架,能够满足绝大部分音视频的需求,其中AVFoundation框架中的AVPlayer控件,甚至可以作为直播播放器,.mp4,.avi,.m3u8格式都能顺利解码,音频框架除了常见的.mp3,,苹果的.caf也能解码。

音视频播放都分为本地文件和远程文件,下面会详细介绍:

代码参考:https://github.com/xinsun001/AVandAudio

一:视频播放
1:使用AVPlayer
视频播放器,使用AVPlayer播放器来解码,通过AVPlayerLayer来绘制到想要展示的控件上面,用来展示视频的窗口可以自己随便定义

    NSString *audioPath = [[NSBundle mainBundle] pathForResource:@"chenyifaer" ofType:@"mp4"];
        NSURL *url = [NSURL fileURLWithPath:audioPath];

        self.playerItem = [AVPlayerItem playerItemWithURL:url];
    self.player = [AVPlayer playerWithPlayerItem:self.playerItem];
    // 初始化播放器的Layer
    self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
    // layer的frame
    self.playerLayer.frame = self.xsVideoView.videoBgView.bounds;
    
    // layer的填充属性 和UIImageView的填充属性类似
    // AVLayerVideoGravityResizeAspect 等比例拉伸,会留白
    // AVLayerVideoGravityResizeAspectFill // 等比例拉伸,会裁剪
    // AVLayerVideoGravityResize // 保持原有大小拉伸
    self.playerLayer.videoGravity = AVLayerVideoGravityResizeAspect;
    // 把Layer加到底部View上
    [self.xsVideoView.videoBgView.layer addSublayer:self.playerLayer];

视频播放往往涉及到全屏的问题,这就会造成视频展示的窗口会有UI的变化,UI每变换一次,addSublayer就需要重新执行一次,如果是用的相对布局的方式,则需要[self.viewlayoutIfNeeded]之后,待控件有了对应的frame值,再去执行addSublayer。如果addSublayer加载的不对,就会出现只有声音,画面黑屏的情况。
2:两个重要的监听属性
AVPlayer中存在两个非常重要的监听属性,由这两个监听才完善视频的缓冲,播放,进度条和快进等功能。分别是播放器的状态变化:NSKeyValueObservingOptionNew 和视频缓冲状态:NSKeyValueObservingOptionNew

    // 监听播放器状态变化
    [self.playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
    // 监听缓冲
    [self.playerItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];

在之后的

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context

方法中,通过keyPath的来区分监听。
播放器的状态变化是一个枚举AVPlayerItemStatus,用[change[NSKeyValueChangeNewKey] integerValue]来读取,

AVPlayerItemStatusReadyToPlay:表示视频缓冲完成,已经准备好进行播放了,这个可以直接使用[self.player play]播放视频

AVPlayerItemStatusFailed:表示视频解码错误,或者缓冲失败,推流或者网速都会对此产生影响

AVPlayerItemStatusUnknown:未知故障
缓存变化的监听,则能够提供视频的时长,和缓冲进度的数据

//缓冲进度
- (NSTimeInterval)availableDuration {
    NSArray *loadedTimeRanges = [self.playerItem loadedTimeRanges];
    CMTimeRange timeRange     = [loadedTimeRanges.firstObject CMTimeRangeValue];// 获取缓冲区域
    float startSeconds        = CMTimeGetSeconds(timeRange.start); // 开始的点
    float durationSeconds     = CMTimeGetSeconds(timeRange.duration); // 已缓存的时间点
    NSTimeInterval result     = startSeconds + durationSeconds;// 计算缓冲总进度
    return result;
}
//视频总长度
CMTime duration = self.playerItem.duration;
CGFloat durationTime = CMTimeGetSeconds(duration);

3:视频暂停和重复播放
AVPlayer中只有播放暂停,没有停止的指令,
播放:[self.player play]
暂停:[self.player pause]
视频播放完毕的通知AVPlayerItemDidPlayToEndTimeNotification,可以在改通知内把进度条归零,来实现重复播放视频的方法

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playToEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];

-(void)playToEnd:(NSNotification *)nottification{
    //进度条归零
    [self.playerItem seekToTime:kCMTimeZero completionHandler:^(BOOL finished) {
        
    }];
}

如果想停止,则需要直接移除掉AVPlayer的所有相关内容,并且销毁监听属性和通知。特别是在退出页面是,播放器是不会随着页面的退出而停止媒体流的播放,必须要先销毁掉AVPlayer,再退出页面

    [self.playerItem removeObserver:self forKeyPath:@"status"];
    [self.playerItem removeObserver:self forKeyPath:@"loadedTimeRanges"];
    
    [[NSNotificationCenter defaultCenter]removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:[self.player currentItem]];
    
    //    加上这句话,避免反复进入视频播放时,后面导致视频不加载的情况,
    [self.player replaceCurrentItemWithPlayerItem:nil];
        
    if (self.player) {
        [self.player pause];
        self.playerItem = nil;
        self.player = nil;
    }

播放远程视频和本地视频没有什么不同,只要把URL换成远程连接就行

    NSString *playString = @"https://cloud.video.taobao.com/play/u/1723456468/p/2/e/6/t/1/244497031401.mp4?appKey=38829";
    NSURL *url = [NSURL URLWithString:playString];

二:播放本地音频
1:使用AVAudioPlayer
本地音频用AVPlayer同样可以实现播放的效果,但是AVPlayer需要使用完手动销毁,无法停止,相对来说AVAudioPlayer就是比较完美的音频播放器,并且可以设置音量大小,循环次数

    NSString *playAudio = [[NSBundle mainBundle] pathForResource:@"Ice.mp3" ofType:nil];
    NSURL *url = [NSURL URLWithString:playAudio];
    self.musicplayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];
    self.musicplayer.volume = 0.8;    //音量
    self.musicplayer.numberOfLoops = 0;  //循环次数,-1表示无限循环,0不循环

    self.musicplayer.delegate = self;
    [self.musicplayer prepareToPlay]; //预加载
    
//    self.musicplayer.currentTime = 18;  //从18秒开始播放
    [self.musicplayer play];

currentTime要结合play属性一起使用,实现快进或者指定位置跳跃播放的效果。AVAudioPlayer有三个基本属性:
开始播放:[self.musicplayer play]
暂停播放:[self.musicplayer pause]
停止播放:[self.musicplayer stop]
AVAudioPlayer在声明的时候必须是强引用属性,否则播放音频文件的时候会没有声音发出,也不能再局部声明AVAudioPlayer。如果是单个页面使用的AVAudioPlayer,退出页面时不需要可以去销毁播放器。
AVAudioPlayer在预加载之后就可以读取到本地音频的长度self.musicplayer.duration。
2:两个代理
要在文件中声明AVAudioPlayerDelegate,可以使用以下两个代理

//音频播放完毕
-(void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag{
    
}
//播放出错
-(void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player error:(NSError *)error{
    
}

经过测试,AVAudioPlayer是无法播放远程音频的,所以远程音频还是得用AVPlayer,只是不想要加载画面,所以layer属性不再需要。但是要保留两个重要的监听和播放完成的通知

    NSURL *url = [NSURL URLWithString:@"http://downsc.chinaz.net/Files/DownLoad/sound1/201906/11582.mp3"];
    self.playerItem = [AVPlayerItem playerItemWithURL:url];

    self.avplayer = [AVPlayer playerWithPlayerItem:self.playerItem];
    
    // 监听播放器状态变化
    [self.playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
    // 监听缓存大小
    [self.playerItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];

        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playToEnd:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,295评论 6 512
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,928评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,682评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,209评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,237评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,965评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,586评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,487评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,016评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,136评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,271评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,948评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,619评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,139评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,252评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,598评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,267评论 2 358

推荐阅读更多精彩内容