ios 知识点

iOS中集成ijkplayer视频直播框架 以及集成过程中遇到的

2019-10-12  本文已影响0人  阿银啊

真的就是,手把手教学哎

零 准备工作

需要 brew,yasm,pkg-config ,用一下命令查询即可

brew -v
git --version
yasm --version

我没有查,因为记得没装yasm ,就直接装了。brew一般都有装的了

brew install yasm

一 下载

  1. 下载地址 Ijkplayer

二 编译

  1. 终端进入到解压后的文件夹


    Snip20190830_17.png
  2. 执行命令 ./init-ios.sh 下载ffmpeg ,等待ing

  3. 执行命令 cd ios 进入ios 文件夹

  4. 编译 ffmpeg ,依次执行: ./compile-ffmpeg.sh clean./compile-ffmpeg.sh all 也是需要等一下,有点慢

遇到的问题,没问题请跳到下一步
  1. 执行````./compile-ffmpeg.sh all ```报错
C compiler test failed.

If you think configure made a mistake, make sure you are using the latest
version from Git.  If the latest version fails, report the problem to the
ffmpeg-user@ffmpeg.org mailing list or IRC #ffmpeg on irc.freenode.net.
Include the log file "ffbuild/config.log" produced by configure as this will help
solve the problem.

参考 [编写shell脚本编译ffmpeg iOS静态库出错] (https://www.jianshu.com/p/985d4c39666a)

第一步
1)检查你的 git 版本
2)确保你使用的是最新的 git 版本

第二步
1)在日志文件(ffmpeg-armv7/ffbuild/config.log)中使用关键字(xcrun: error)搜索错误详情
2)例如:你会发现 “xcrun: error: SDK "iphoneos” 不能被定位到

第三步
1)在终端输入 sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer/
2)输入 mac 登录密码
3)重试
  1. 执行 ./[compile-ffmpeg.sh](compile-ffmpeg.sh) all 报错
./libavutil/arm/asm.S:50:9: error: unknown directive
        .arch armv7-a
        ^
make: *** [libavcodec/arm/aacpsdsp_neon.o] Error 1

报错原因是:因为xcode对32位的支持弱化了,需要进入 compile-ffmpeg.sh 里面修改脚本,正确修改后重新执行 ./[compile-ffmpeg.sh](compile-ffmpeg.sh) all 即可

compile-ffmpeg.sh 在这里


Snip20190830_18.png

修改如下


Snip20190830_19.png

三 打包IJKMediaFramework.framework

  1. 打开工程后,设置工程的 scheme 为release
Snip20190822_2.png Snip20190822_4.png Snip20190830_20.png
  1. 设置完成后,分别选择真机和模拟器进行编译
遇到的问题,没有问题可以跳到下一步

根据报错的路径找到文件


Snip20190830_23.png

直接打开,然后注释掉# include "armv7/avconfig.h"


Snip20190830_24.png
如果是报以下这几个相关的缺失错误,都是类似的解决办法
Snip20190830_26.png
  1. 真机和模拟器编译之后,会多出两个文件夹,查看方法如下:


    Snip20190830_31.png
Snip20190830_27.png
  1. 合并真机和模拟器的framework ,注意要合并的文件是这个


    Snip20190822_21.png

执行命令: lipo -create "真机版本路径" "模拟器版本路径" -output "合并后的文件路径"
tips: 合并后的文件路径记得把新文件的名字加上啊,不然很可能合成之后会变成 .lipo 之类的东西😳

  1. 替换 IJKMediaFramework,很重要


    Snip20190830_32.png

四 集成ijkplayer

  1. 导入 framework,直接将 IJKMediaFramework.framework拖入到工程中即可,注意记得勾选 Copy items if needed和 对应的 target

  2. 添加下列依赖到工程

libc++.tbd( 编译器选 gcc 的请导入 libstdc++.tbd)
libz.tbd
libbz2.tbd
AudioToolbox.framework
UIKit.framework
CoreGraphics.framework
AVFoundation.framework
CoreMedia.framework
CoreVideo.framework
MediaPlayer.framework
MobileCoreServices.framework
OpenGLES.framework
QuartzCore.framework
VideoToolbox.framework
Snip20190822_24.png
Snip20190822_22.png

五 简单的使用测试下

我是参考的 swift之IJKPlayer 的应用,(测试代码原文出处:https://blog.csdn.net/amberoot/article/details/79536817 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
)亲测可用,代码贴出来你们方便测下~

// 初始化变量

    let ViewForPlayer = UIView()
    var player: IJKFFMoviePlayerController!
    ///保存视频流地址的字符串
    var videoStreaming: String!
    //提示播放器的状态,放在播放器正中央
    var videoTip = UILabel()
    //播放器加载或缓存的时候可见并转动
    var indicator = UIActivityIndicatorView()
    //要重启播放器时,把播放器的view存放起来,适当时候删掉
    var oldPlayerView = UIView()

// 初始化控件

    func initView(cgRect: CGRect,url: String,text:String)
    {
        indicator.style = UIActivityIndicatorView.Style.white
        videoTip.text = text
        videoTip.textColor = UIColor.lightGray
        videoStreaming = url
        //添加ViewForPlayer到界面
        ViewForPlayer.frame = cgRect
        ViewForPlayer.backgroundColor = UIColor.darkText
        self.contentView.addSubview(ViewForPlayer)
        //        ViewController.playerContainer.addSubview(ViewForPlayer)
        //初始化播放器
        initPlayer(Url: url)
    }
    
    
    func setLabelViewConstraints() {
        //系统默认会给autoresizing 约束,要关闭autoresizing才能让自定义的约束生效,否则程序崩溃
        videoTip.translatesAutoresizingMaskIntoConstraints = false
        //添加约束:"哪个控件" 的 “什么属性“ "等于/大于/小于" “另一个控件” 的 “什么属性” 乘以 "多少" 加上 "多少"
        let Constraint_centerX = NSLayoutConstraint(item: videoTip, attribute: .centerX, relatedBy: .equal, toItem: ViewForPlayer, attribute: .centerX, multiplier: 1, constant: 0)
        
        let Constraint_centerY = NSLayoutConstraint(item: videoTip, attribute: .centerY, relatedBy: .equal, toItem: ViewForPlayer, attribute: .centerY, multiplier: 1, constant: 0)
        
        let Constraint_width = NSLayoutConstraint(item: videoTip, attribute: .width, relatedBy: .lessThanOrEqual, toItem: ViewForPlayer, attribute: .width, multiplier: 1, constant: 0)
        
        let Constraint_height = NSLayoutConstraint(item: videoTip, attribute: .height, relatedBy: .lessThanOrEqual, toItem: nil, attribute: .height, multiplier: 1, constant: 20)
        NSLayoutConstraint.activate([Constraint_centerX,Constraint_centerY,Constraint_width,Constraint_height])
    }
    
    func setIndicatorViewConstraints() {
        //系统默认会给autoresizing 约束,要关闭autoresizing才能让自定义的约束生效,否则程序崩溃
        indicator.translatesAutoresizingMaskIntoConstraints = false
        //添加约束:"哪个控件" 的 “什么属性“ "等于/大于/小于" “另一个控件” 的 “什么属性” 乘以 "多少" 加上 "多少"
        let Constraint_centerX = NSLayoutConstraint(item: indicator, attribute: .centerX, relatedBy: .equal, toItem: ViewForPlayer, attribute: .centerX, multiplier: 1, constant: 0)
        
        let Constraint_centerY = NSLayoutConstraint(item: indicator, attribute: .centerY, relatedBy: .equal, toItem: ViewForPlayer, attribute: .centerY, multiplier: 1, constant: 0)
        
        let Constraint_width = NSLayoutConstraint(item: indicator, attribute: .width, relatedBy: .lessThanOrEqual, toItem: nil, attribute: .width, multiplier: 1, constant: 20)
        
        let Constraint_height = NSLayoutConstraint(item: indicator, attribute: .height, relatedBy: .lessThanOrEqual, toItem: nil, attribute: .height, multiplier: 1, constant: 20)
        NSLayoutConstraint.activate([Constraint_centerX,Constraint_centerY,Constraint_width,Constraint_height])
    }
    

// 初始化并开启播放器

    func initPlayer(Url: String) {
        //防止重复打开播放器导致内存溢出
        if player != nil {
            player.shutdown()//关闭播放器
            oldPlayerView = player.view
            indicator.removeFromSuperview()
            videoTip.removeFromSuperview()
            player = nil
            print("++++++++重启播放器+++++++")
        }
        
        videoStreaming = Url
        let url = NSURL(string: Url)
        //options是对数据的处理,videotoolbox解码,设置音频视频等属性,都要有这个数据
        let options = IJKFFOptions.byDefault()
        //设置解码方式: 0-软解码;1-硬解码
        //        options?.setPlayerOptionIntValue(1, forKey: "videotoolbox")
        //开启硬解码
        //        options?.setPlayerOptionValue("1", forKey: "videotoolbox")
        //设置播放器缓冲: 0-关闭缓冲;1-开启缓冲;默认是1
        //        options?.setPlayerOptionIntValue(1, forKey: "packet-buffering")
        // 最大缓存是3000(3s),可以依据自己的需求修改
        //        options?.setPlayerOptionIntValue(1000, forKey: "max_cached_duration")
        //设置自动转屏
        //        options?.setFormatOptionIntValue(0, forKey: "auto_convert")
        //设置重连:0-关闭重连;1-开启重连;
        //        options?.setFormatOptionIntValue(1, forKey: "reconnect")
        //如果使用rtsp协议,可以优先用tcp(默认udp)
        //        options?.setPlayerOptionValue("tcp", forKey: "rtsp_transport")
        //开启环路滤波IJK_AVDISCARD(0比48清楚,但解码开销大,48基本没有开启环路滤波,清晰度低,解码开销小)
        //        options?.setCodecOptionIntValue(48, forKey: "skip_loop_filter")
        //帧速率(fps)-可以改,确认非标准桢率会导致音画不同步,所以只能设定为15或者29.97
        //        options?.setPlayerOptionIntValue(29.97, forKey: "r")
        
        //        //设置音量大小,256为标准音量。要设置成两倍音量时则输入512,依此类推
        //        options?.setPlayerOptionIntValue(256, forKey: "vol")
        //设置静音
        options?.setPlayerOptionValue("1", forKey: "an")
        //        IJKFFMoviePlayerController.setLogLevel(IJKLogLevel(rawValue: 3))
        //        IJKFFMoviePlayerController.setLogReport(false)
        //默认是false
        //        player.shouldShowHudView = true
        
        //初始化播放器,播放在线视频或直播
        player = IJKFFMoviePlayerController(contentURL: url as URL?, with: options)
        //播放页面视图宽高自适应
        let autoresize = UIView.AutoresizingMask.flexibleWidth.rawValue | UIView.AutoresizingMask.flexibleHeight.rawValue
        player.view.autoresizingMask = UIView.AutoresizingMask(rawValue: autoresize)
        //设置视频缓存大小,缓存大延迟大,缓存小延迟小
        player.setPlayerOptionIntValue(100, forKey: "framedrop")
        
        player.view.frame = self.ViewForPlayer.bounds
        player.scalingMode = .aspectFit//缩放模式
        player.shouldAutoplay = true //开启自动播放
        //        player.allowsMediaAirPlay = true
        
        //player.view的父控件设置自动适应
        //fatherView.autoresizesSubviews = true
        
        self.ViewForPlayer.addSubview(player.view)
        //添加indicatorView到ViewForPlayer
        ViewForPlayer.addSubview(indicator)
        setIndicatorViewConstraints()
        //添加label到ViewForPlayer
        ViewForPlayer.addSubview(videoTip)
        setLabelViewConstraints()
        /////////////////
        self.player.currentPlaybackTime = 1
        //self.player.playbackState
        DispatchQueue.main.async {
            //开启播放器
            self.player.prepareToPlay()
        }
        
        videoTip.text = " "
        indicator.startAnimating()
        //注册ijk播放器的通知
        initIJKNoti()
    }
    

// 注册ijk播放器的通知

    func initIJKNoti() {
        //注册ijk播放器的通知
        NotificationCenter.default.addObserver(self, selector: #selector(moviePlayBackFinish), name: NSNotification.Name.IJKMPMoviePlayerPlaybackDidFinish, object: player)
        
        NotificationCenter.default.addObserver(self, selector: #selector(loadStateDidChange), name: NSNotification.Name.IJKMPMoviePlayerLoadStateDidChange, object: player)
        
        NotificationCenter.default.addObserver(self, selector: #selector(moviePlayBackStateDidChange), name: NSNotification.Name.IJKMPMoviePlayerPlaybackStateDidChange, object: player)
        
        NotificationCenter.default.addObserver(self, selector: #selector(mediaIsPreparedToPlayDidChange), name: NSNotification.Name.IJKMPMediaPlaybackIsPreparedToPlayDidChange, object: player)
    }

// 接收播放器状态改变的通知

    //视频播放结束
    @objc func moviePlayBackFinish(notifycation:Notification)  {
        let playerN = notifycation.object as! IJKFFMoviePlayerController
        
        if playerN.bufferingProgress == 0 {
            print("视频加载失败")
        }
        let reason = notifycation.userInfo?[IJKMPMoviePlayerPlaybackDidFinishReasonUserInfoKey] as! Int
        NSLog("--------------moviePlayBackFinish:\(reason)")
        
        switch (reason) {
        case 0://playbackEnded
            indicator.startAnimating()
            if !videoStreaming.isEmpty {
                initPlayer(Url: videoStreaming)
            }
            
            break
            
        case 2://userExited
            
            break
            
        case 1://playbackError
            print("播放错误,需要重新播放:\(reason)")
            videoTip.text = "获取视频失败"
            indicator.stopAnimating()
            break
        default:
            break
        }
    }
    
    ///加载状态改变了
    @objc func loadStateDidChange(notifycation:Notification)  {
        let playerN = notifycation.object as! IJKFFMoviePlayerController
        let loadState = player?.loadState.rawValue
        
        NSLog("--------------加载状态loadStateDidChange:\(loadState!)")
        //        IJKMPMovieLoadState
        if loadState == 3 {//开始播放视频
            indicator.stopAnimating()
            self.oldPlayerView.removeFromSuperview()
        }else if loadState == 4 {//网络不好导致了暂停
            indicator.startAnimating()
            if !videoStreaming.isEmpty {
                initPlayer(Url: videoStreaming)
            }
        }
        switch self.player.loadState {
        // 状态为缓冲几乎完成,可以连续播放
        case IJKMPMovieLoadState.playthroughOK:
            
            //            videoTip.text = "加载状态为缓冲几乎完成,可以连续播放"
            break
        // 可以播放状态
        case IJKMPMovieLoadState.playable:
            //            videoTip.text = "加载状态为可以播放状态"
            
            break
        // 缓冲中
        case IJKMPMovieLoadState.stalled:
            
            videoTip.text = "网络不好导致了暂停"
            indicator.startAnimating()
            break
        default:
            print("加载状态-loadStateDidChange: \(loadState!)")//加载状态未知
        }
        
    }
    
    ///准备播放的媒体改变了
    @objc func mediaIsPreparedToPlayDidChange(notifycation:Notification)  {
        _ = notifycation.object as! IJKFFMoviePlayerController
        NSLog("--------------mediaIsPreparedToPlayDidChange")
        videoTip.text = "mediaIsPreparedToPlayDidChange"
        
    }
    ///视频播放状态改变了
    @objc func moviePlayBackStateDidChange(notifycation:Notification)  {
        let playerN = notifycation.object as! IJKFFMoviePlayerController
        
        //播放 1  暂停2  播放完成 0
        NSLog("--------------播放状态改变了:\(player?.playbackState.rawValue ?? 9)")
        //        videoTip.text = "播放状态改变了:\(player?.playbackState.rawValue ?? 9)"
        switch player?.playbackState.rawValue ?? 9 {
        case 0://停止
            //            initPlayer(Url: videoStreaming)
            break
            
        case 1://播放
            
            break
            
        case 2://暂停
            //            initPlayer(Url: videoStreaming)
            
            break
        case 4://播放
            
            break
        default:
            break
            
        }
        
    }
    

总结:集成的过程挺简单的,主要的原理和其他应用我还没有摸透,希望能帮到大家,然后自己也要进步才行啊。

续篇(当我想要打包上架)

坑有千千万万个,你永远不知道你掉到哪里去了

ld: framework not found Pods_XXX clang: error: linker command failed with exit code 1 (use -v to see

clang: error: linker command failed with exit code 1 (use -v to see invocation) 
Undefined symbols for architecture armv7:
 "_avio_open_dyn_buf", referenced from:
     _vtbformat_init in IJKMediaFramework(IJKVideoToolBoxAsync.o)
     _decode_video_internal in IJKMediaFramework(IJKVideoToolBoxAsync.o)
     _vtbformat_init in IJKMediaFramework(IJKVideoToolBoxSync.o)
     _decode_video_internal in IJKMediaFramework(IJKVideoToolBoxSync.o)
 "_avio_close_dyn_buf", referenced from:
     _vtbformat_init in IJKMediaFramework(IJKVideoToolBoxAsync.o)
     _decode_video_internal in IJKMediaFramework(IJKVideoToolBoxAsync.o)
WechatIMG727.jpeg WechatIMG729.jpeg

希望没有后续~

上一篇 下一篇

猜你喜欢

热点阅读