iOS集成IJKPlayer播放器

IJKPlayer是一款基于ffmpeg/ffplay的开源播放器,可支持rtmp/rtsp/hls等多种媒体协议,支持Android/IOS等移动平台。

一、帧

在进行IJKPlayer的学习之前,我们先来了解下帧的概念。视频压缩中,每帧代表一幅静止的图像。而在实际压缩时,会采取各种算法减少数据的容量,其中IPB就是最常见的。
I帧是关键帧,属于帧内压缩。就是和AVI的压缩是一样的。 P是向前搜索的意思。B是双向搜索。他们都是基于I帧来压缩数据。
I帧表示关键帧,你可以理解为这一帧画面的完整保留;解码时只需要本帧数据就可以完成(因为包含完整画面,记得以前的播放软件暂停的时候的图片是模糊的,这就是非关键帧,但现在的播放软件暂停的时候显示的是关键帧,是一张清晰的图片)。
P帧表示的是这一帧跟之前的一个关键帧(或P帧)的差别,解码时需要用之前缓存的画面叠加上本帧定义的差别,生成最终画面。(也就是差别帧,P帧没有完整画面数据,只有与前一帧的画面差别的数据)

B帧是双向差别帧,也就是B帧记录的是本帧与前后帧的差别(具体比较复杂,有4种情况),换言之,要解码B帧,不仅要取得之前的缓存画面,还要解码之后的画面,通过前后画面的与本帧数据的叠加取得最终的画面。B帧压缩率高,但是解码时CPU会比较累~。

从上面的解释看,我们知道I和P的解码算法比较简单,资源占用也比较少,I只要自己完成就行了,P呢,也只需要解码器把前一个画面缓存一下,遇到P时就使用之前缓存的画面就好了,如果视频流只有I和P,解码器可以不管后面的数据,边读边解码,线性前进,观看视频也会很舒服。

二、IJKplayer属性参数设置IJKFFOptions

IJKFFOptions *options = [IJKFFOptions optionsByDefault];
IJKFFOptions的默认值
===== options =====
player-opts : start-on-prepared            = 1
player-opts : overlay-format               = fcc-i420
player-opts : max-fps                      = 60
player-opts : framedrop                    = 0
player-opts : videotoolbox-max-frame-width = 960
player-opts : videotoolbox                 = 1
player-opts : video-pictq-size             = 3
format-opts : ijkinject-opaque             = 140449007406288
format-opts : user-agent                   = ijkplayer
format-opts : auto_convert                 = 0
format-opts : timeout                      = 30000000
format-opts : reconnect                    = 1
format-opts : safe                         = 0
codec-opts  : skip_frame                   = 0
codec-opts  : skip_loop_filter             = 0
===================
//1为开启硬件解码,用特定方法把数字编码还原成它所代表的内容或将电脉冲信号转换成它所代表的信息、数据等;
//硬件解码其实就是用GPU的专门模块编码来解(建议使用硬解码)。
//0为软件解码,更稳定,是cpu进行解码(如果使用软解码可能导致cpu占用率高)
[options setPlayerOptionIntValue:1 forKey:@"videotoolbox"];
// 设置音量大小,256为标准音量。(要设置成两倍音量时则输入512,依此类推)
[options setPlayerOptionIntValue:512 forKey:@"vol"];
// 最大fps
[options setPlayerOptionIntValue:30 forKey:@"max-fps"];

// 跳帧开关,如果cpu解码能力不足,可以设置成5,否则
// 会引起音视频不同步,也可以通过设置它来跳帧达到倍速播放
[options setPlayerOptionIntValue:0 forKey:@"framedrop"];

// 指定最大宽度
[options setPlayerOptionIntValue:960 forKey:@"videotoolbox-max-frame-width"];

// 自动转屏开关
[options setFormatOptionIntValue:0 forKey:@"auto_convert"];

// 重连次数
[options setFormatOptionIntValue:1 forKey:@"reconnect"];

// 超时时间,timeout参数只对http设置有效,若果你用rtmp设置timeout,ijkplayer内部会忽略timeout参数。rtmp的timeout参数含义和http的不一样。
[options setFormatOptionIntValue:30 * 1000 * 1000 forKey:@"timeout"];
// 帧速率(fps) (可以改,确认非标准桢率会导致音画不同步,所以只能设定为15或者29.97)帧速率越大,画质越好,但太大了,有些机器版本不支持,反而有些卡。
[options setPlayerOptionIntValue:29.97 forKey:@"r"];
//如果是rtsp协议,可以优先用tcp(默认是用udp)
 [options setFormatOptionValue:@"tcp" forKey:@"rtsp_transport"];
//播放前的探测Size,默认是1M, 改小一点会出画面更快
 [options setFormatOptionIntValue:1024 * 16 forKey:@"probesize"];
//播放前的探测时间
 [options setFormatOptionIntValue:50000 forKey:@"analyzeduration"];

 [options setCodecOptionIntValue:IJK_AVDISCARD_DEFAULT forKey:@"skip_loop_filter"];
 [options setCodecOptionIntValue:IJK_AVDISCARD_DEFAULT forKey:@"skip_frame"];
 if (_isLive) {
        // 直播参数
        [options setPlayerOptionIntValue:3000 forKey:@"max_cached_duration"];   // 最大缓存大小是3秒,可以依据自己的需求修改
//设置无极限的播放器buffer,这个选项常见于实时流媒体播放场景

        [options setPlayerOptionIntValue:1 forKey:@"infbuf"];  // 无限读
//播放器缓冲可以避免因为丢帧引入花屏的,因为丢帧都是丢到I帧之前的P/B帧为止。我之前也写过一个类似的,思路都是一样,但这个代码更精简。
//A:如果你想要实时性,可以去掉缓冲区,一句代码:
//B: 如果你这样试过,发现你的项目中播放频繁卡顿,
//你想留1-2秒缓冲区,让数据更平缓一些,
//那你可以选择保留缓冲区,不设置上面那个就行。
        [options setPlayerOptionIntValue:0 forKey:@"packet-buffering"];  //  关闭播放器缓冲
    } else {
        // 如果不判断是否是关键帧会导致视频画面花屏。但是这样会导致全部清空的可能也会出现花屏
        // 所以这里推流端设置好 GOP(画面组,一个GOP就是一组连续的画面。MPEG编码将画面(即帧)分为I、P、B三种) 的大小,如果 max_cached_duration > 2 * GOP,可以尽可能规避全部清空
        // 也可以在调用control_queue_duration之前判断新进来的视频pkt是否是关键帧,这样即使全部清空了也不会花屏。
        [options setPlayerOptionIntValue:0 forKey:@"max_cached_duration"];
        [options setPlayerOptionIntValue:0 forKey:@"infbuf"];
//播放时开启,推流时关闭
        [options setPlayerOptionIntValue:1 forKey:@"packet-buffering"];
    }

2.1 skip_loop_filter

//  'skip_loop_filter' 的 'skip_frame'编码
typedef enum IJKAVDiscard {
    /* We leave some space between them for extensions (drop some
     * keyframes for intra-only or drop just some bidir frames). */
    IJK_AVDISCARD_NONE    =-16, ///< 什么也不丢弃
    IJK_AVDISCARD_DEFAULT =  0, ///< 丢弃无用的数据包,像0大小的数据包在avi
    IJK_AVDISCARD_NONREF  =  8, ///< 抛弃非参考帧(P帧)
    IJK_AVDISCARD_BIDIR   = 16, ///< 抛弃所有的双向帧
    IJK_AVDISCARD_NONKEY  = 32, ///< 抛弃除关键帧以外的帧,比如B,P帧
    IJK_AVDISCARD_ALL     = 48, ///< 抛弃所有的帧
} IJKAVDiscard;

skip_loop_filter和skip_frame的对象要过滤哪些帧类型。

skip_loop_filter这个是解码的一个参数,叫环路滤波,环路滤波主要用于滤除方块效应(块内模糊, 图像信号的低频区是用来反应一个图像的细节程度的,要是去掉低频部分,只用高频来描述图像的轮廓肯定很明显的出现方块效应 。)decode_slice()在解码完一行宏块之后,会调用loop_filter()函数完成环路滤波功能。
设置成48和0,图像清晰度对比,0比48清楚,理解起来就是,
0是开启了环路滤波,过滤的是大部分,
而48基本没启用环路滤波,所以清晰度更低,但是解码性能开销小

skip_loop_filter(环路滤波)简言之:
a:环路滤波器可以保证不同水平的图像质量。
b:环路滤波器更能增加视频流的主客观质量,同时降低解码器的复杂度。

三、IJKFFMoviePlayerController

了解了IJKFFOptions,接下来我们创建IJKFFMoviePlayerController,专门用来直播,传入拉流地址,设置IJKFFOptions。

//创建IJKFFMoviePlayerController
[[IJKFFMoviePlayerController alloc] initWithContentURL:url withOptions:options];

视频内容的拉伸样式

/*
*IJKMPMovieScalingModeNone 不拉伸
*IJKMPMovieScalingModeAspectFit  均匀拉伸直到一个尺寸合适
*IJKMPMovieScalingModeAspectFill  均匀拉伸,直到电影填充可见边界。一个维度可能有被裁剪的内容
*IJKMPMovieScalingModeFill 不均匀伸。渲染维度将完全匹配可见边界
*/
[self.player setScalingMode:IJKMPMovieScalingModeAspectFit];
 //设置缓存大小,太大了没啥用,太小了视频就处于边播边加载的状态,目前是10M,后期可以调整
  [self.player setPlayerOptionIntValue:10* 1024 *1024 forKey:@"max-buffer-size"];
//如果是直播,最好不让他自动播放,如果YES,那么就会自动播放电影,不需要通过[self.player play];就可以播放了,
 //但是如果NO,我们需要注册通知,然后到响应比较合适的地方去检测通知,然后必须通过[self.player play];手动播放
 self.player.shouldAutoplay = NO;

创建好了IJKFFMoviePlayerController之后,真正有用的是他的view,我们获取到他的view,并将它添加到我们自定义的viewController的view之上,创建一个自定义的PlayerController,或者是PlayerView。

四、IJKFFMoviePlayerController通知

视频的播放的消息传递是能过通知来完成的

//  MPMediaPlayback.h

// 当准备状态的对象改变符合MPMediaPlayback协议时发布。
//他支持 MPMoviePlayerContentPreloadDidFinishNotification.
IJKMPMediaPlaybackIsPreparedToPlayDidChangeNotification;

该通知说明视屏已经准备好马上就要开始播放,这时候我们可以取到当前视屏的总时长,当前播放时间行信息,所以在该通知产生时我们可以对界面视屏数据进行初始化。

//  MPMoviePlayerController.h
//  Movie Player Notifications

//当Player缩放模式发生变化时发布。
IJKMPMoviePlayerScalingModeDidChangeNotification;

//当视频播放结束或用户退出播放时发布。
 JKMPMoviePlayerPlaybackDidFinishNotification;
// 在播放状态发生变化时发布,无论是编程改变的还是用户事件触发的
IJKMPMoviePlayerPlaybackStateDidChangeNotification;

我们可以通过当前player的playbackState获取当前播放状态:

IJKMPMoviePlaybackStateStopped, //停止状态
IJKMPMoviePlaybackStatePlaying,//播放状态
IJKMPMoviePlaybackStatePaused,//暂停状态
IJKMPMoviePlaybackStateInterrupted,//中断状态
IJKMPMoviePlaybackStateSeekingForward,//向前拖动状态
IJKMPMoviePlaybackStateSeekingBackward//向后拖动状态
//在网络加载状态更改时发布。
IJKMPMoviePlayerLoadStateDidChangeNotification;

// 当播放器开始或结束正在通过AirPlay播放的视频时发送
IJKMPMoviePlayerIsAirPlayVideoActiveDidChangeNotification;

// -----------------------------------------------------------------------------
// Movie Property Notifications

// 在视频播放器上调用- preparetoplay将开始异步确定视频属性。相关的视频属性可用时,这些通知就会被发布。
 IJKMPMovieNaturalSizeAvailableNotification;

// -----------------------------------------------------------------------------
//  Extend Notifications

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

推荐阅读更多精彩内容

  • 方块效应产生的原因:1.变换、量化 的误差2.帧间预测的运动补偿过程为了消除或减轻块效应,我们可以用环路滤波器(L...
    Persistently阅读 3,120评论 0 0
  • 在保证视频图像质量的前提下,HEVC通过增加一定的计算复杂度,可以实现码流在H.264/AVC的基础上降低50%。...
    加刘景长阅读 7,763评论 0 6
  • 教程一:视频截图(Tutorial 01: Making Screencaps) 首先我们需要了解视频文件的一些基...
    90后的思维阅读 4,627评论 0 3
  • FFmpeg X264编码参数 目录 码率控制 1 X264的preset和tune 2 编码延时建议...
    古则阅读 29,324评论 1 15
  • 雨,沉沦在漫无目的, 风,难寻一点踪迹, 我,又坠落在同一个梦里。 曾经,又转过一个冰季, 静静抒写任性。 风铃,...
    水月沐風阅读 170评论 0 2