美文网首页
有关ffmpeg------>直播

有关ffmpeg------>直播

作者: 二维码码 | 来源:发表于2018-04-16 16:52 被阅读0次

    参考:https://blog.csdn.net/leixiaohua1020/article/details/15811977/

    《基于 FFmpeg + SDL 的视频播放器的制作》课程的视频:https://blog.csdn.net/leixiaohua1020/article/details/47068015

    参考:iOS中集成ijkplayer视频直播框架:https://www.jianshu.com/p/1f06b27b3ac0

    参考直播demo:https://www.jianshu.com/p/b8db6c142aad

    参考:https://www.jianshu.com/u/b09c3959ab3b

    参考:http://www.cnblogs.com/fusheng-it/p/7911000.html(直播)

    参考:https://www.jianshu.com/p/bd42bacbe4cc(直播原理)

    视频直播,可以分为 采集,前处理,编码,传输, 服务器处理,解码,渲染

    直播前期准备为:

    1.推流用优酷开源的LFLiveKit框架。

    2.拉流(实际上就是一个播放器)用ijkplayer 框架,当然这个也是开源的。

    3.创建本地rtmp服务器。

    推流:

    推流用的是一个第三方的IFliveKit框架。这个框架基于rtmp协议。IFLiveKit内部集成了GPUIImage。内部实现了图片渲染等美艳效果。减少了开发时候美艳效果的调试。

    推流端工作将它细分为以下几个部分(基本上是依次执行的):

      一:相机相册权限检查并作出相应的处理方法。

      二:音频视频信息配置(码率,采样率,质量等信息)

      三:音频视频采集及编码前的滤镜等效果(GPUIImage)

      四:音频视频编码。这里需要注意的是iOS8以上支持硬件编码,如果不能适配iOS8以上是需要做处理的(参考LFLiveKit)

      五:上传数据(rtmp)

    一个简单的推流页面应该包含以下几个功能:

    1.推流状态监听。2.切换摄像头。3.切换美艳效果。4.开关推流。

    一.推流之前需要检查摄像头和麦克风等权限是否开启,并启动摄像头,核心代码如下:

    //判断是否有摄像头if(![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]){

            [self showInfo:@"您的设备没有摄像头或者相关的驱动, 不能进行直播"];

            return;

        }

        //判断是否有摄像头权限AVAuthorizationStatus  authorizationStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];

        if(authorizationStatus == AVAuthorizationStatusRestricted|| authorizationStatus == AVAuthorizationStatusDenied) {

            [self showInfo:@"app需要访问您的摄像头。\n请启用摄像头-设置/隐私/摄像头"];

            return ;

        }AVAudioSession *audioSession = [AVAudioSession sharedInstance];

        if ([audioSession respondsToSelector:@selector(requestRecordPermission:)]) {

            [audioSession performSelector:@selector(requestRecordPermission:) withObject:^(BOOL granted) {

                if (granted) {

                    return YES;

                }

                else {

                    [self showInfo:@"app需要访问您的麦克风。\n请启用麦克风-设置/隐私/麦克风"];

                    return NO;

                }

            }];

        }

    //检查麦克风权限- (void)checkCaptureAudioDeviceEnableCheckCaptureVideo:(void(^)(BOOL isAutioSucc,NSString * err))succ{

        if([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio]==AVAuthorizationStatusNotDetermined) {

            [AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio completionHandler:^(BOOL granted) {

                if (granted) {succ(granted,nil);}

                else{succ(NO,@"app需要访问您的麦克风。\n请启用麦克风-设置/隐私/麦克风");}

            }];

        }elseif([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio]==AVAuthorizationStatusAuthorized){

            succ(YES,nil);

        }else{

            succ(NO,@"app需要访问您的麦克风。\n请启用麦克风-设置/隐私/麦克风");

        }

    }

    二.创建一个按钮.点击开始推流代码如下:

    - (LFLiveSession*)session{

        if(!_session){

            /***   默认分辨率368 * 640  音频:44.1 iphone6以上48  双声道  方向竖屏 ***/        _session = [[LFLiveSession alloc] initWithAudioConfiguration:[LFLiveAudioConfiguration defaultConfiguration] videoConfiguration:[LFLiveVideoConfiguration defaultConfigurationForQuality:LFLiveVideoQuality_Medium2]];

            /**    自己定制高质量音频128K 分辨率设置为720*1280 方向竖屏 *//*        LFLiveAudioConfiguration *audioConfiguration = [LFLiveAudioConfiguration new];

            audioConfiguration.numberOfChannels = 2;

            audioConfiguration.audioBitrate = LFLiveAudioBitRate_128Kbps;

            audioConfiguration.audioSampleRate = LFLiveAudioSampleRate_44100Hz;

            LFLiveVideoConfiguration *videoConfiguration = [LFLiveVideoConfiguration new];

            videoConfiguration.videoSize = CGSizeMake(720, 1280);

            videoConfiguration.videoBitRate = 800*1024;

            videoConfiguration.videoMaxBitRate = 1000*1024;

            videoConfiguration.videoMinBitRate = 500*1024;

            videoConfiguration.videoFrameRate = 15;

            videoConfiguration.videoMaxKeyframeInterval = 30;

            videoConfiguration.orientation = UIInterfaceOrientationPortrait;

            videoConfiguration.sessionPreset = LFCaptureSessionPreset720x1280;

            _session = [[LFLiveSession alloc] initWithAudioConfiguration:audioConfiguration videoConfiguration:videoConfiguration liveType:LFLiveRTMP];

            */// 设置代理_session.delegate= self;

            _session.running = YES;

            _session.preView = self.livingPreView;

        }

        return _session;

    }

    //给服务器推流

    - (IBAction)startTouched:(id)sender {

        LFLiveStreamInfo *stream = [LFLiveStreamInfonew];

        // 本地推流地址stream.url =@"rtmp://192.168.199.131:1935/rtmplive/room";

        self.rtmpUrl = stream.url;

        [self.session startLive:stream];

    }

    3.创建一个按钮点击关闭推流,代码如下:

    - (IBAction)endTouched:(id)sender {

        // 结束直播    [self.session stopLive];

        self.stateLable.text = [NSString stringWithFormat:@"状态: 直播被关闭\nRTMP: %@", self.rtmpUrl];

    }

    4.创建一个按钮点击切换前后摄像头,代码如下:

    - (IBAction)camaBtnTouched:(id)sender {

        AVCaptureDevicePosition devicePositon = self.session.captureDevicePosition;

        self.session.captureDevicePosition = (devicePositon == AVCaptureDevicePositionBack) ? AVCaptureDevicePositionFront : AVCaptureDevicePositionBack;

        NSLog(@"切换前置/后置摄像头");

    }

    5.创建一个按钮设置美艳功能,代码如下:

    - (IBAction)beautiyBtnTouched:(id)sender {

        ((UIButton*)sender).selected = !((UIButton*)sender).selected;

        // 默认是开启了美颜功能的self.session.beautyFace = !self.session.beautyFace;

    }

    6.推流状态监听,接受代理,代码如下:

    #pragmamark -- LFStreamingSessionDelegate/** live status changed will callback */- (void)liveSession:(nullable LFLiveSession *)session liveStateDidChange:(LFLiveState)state{

        NSString *tempStatus;

        switch (state) {

            case LFLiveReady:

                tempStatus =@"准备中";

                break;

            case LFLivePending:

                tempStatus =@"连接中";

                break;

            case LFLiveStart:

                tempStatus =@"已连接";

                break;

            case LFLiveStop:

                tempStatus =@"已断开";

                break;

            case LFLiveError:

                tempStatus =@"连接出错";

                break;

            default:

                break;

        }

        self.stateLable.text = [NSString stringWithFormat:@"状态: %@\nRTMP: %@", tempStatus, self.rtmpUrl];

    }/** live debug info callback */- (void)liveSession:(nullable LFLiveSession *)session debugInfo:(nullable LFLiveDebug*)debugInfo{

    }/** callback socket errorcode */- (void)liveSession:(nullable LFLiveSession*)session errorCode:(LFLiveSocketErrorCode)errorCode{

    }

    6.自己也需要看到自己的推流画面,并观察美艳效果,代码如下:

    - (UIView *)livingPreView

    {

        if(!_livingPreView) {

            UIView *livingPreView = [[UIView alloc] initWithFrame:self.view.bounds];

            livingPreView.backgroundColor = [UIColor clearColor];

            livingPreView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

            [self.view insertSubview:livingPreView atIndex:0];

            _livingPreView = livingPreView;

        }

        return _livingPreView;

    }

    至此,推流完成。


    拉流:

    推流完成后,需要拉流才能进行完整的直播。拉流我们采用的也是一个开源的第三方库IJKMediaFramework。

    这个库本质是一个播放器,能播放flv格式的播放器。用起来和ios自带的AVPlayer很相似。

     实现功能:1.拉流播放。2.监听。

    一.创建占位图和卡顿占位动效,代码如下:

    //直播前的占位图片

    - (UIImageView *)placeHolderView

    {

        if(!_placeHolderView) {

            _placeHolderView = [[UIImageView alloc] init];

            _placeHolderView.frame = self.view.bounds;

            _placeHolderView.image = [UIImage imageNamed:@"profile_user_414x414"];

            // 强制布局        [_placeHolderView layoutIfNeeded];

        }

        return_placeHolderView;}

    //卡顿占位动效

    - (void)showActivityView{

        if(!_activity) {

            _activity= [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];

            _activity.frame = CGRectMake((SCREAM_WEIGHT-100)*0.5, (SCREAM_HIGHT-100)*0.5,100,100);

        }

        [self.activity startAnimating];

        [self.view addSubview:self.activity];

    }

    //关闭卡顿占位动效- (void)stopActivityView{

        if ([_activity isAnimating]) {

            [_activity startAnimating];

        }

        [_activity removeFromSuperview];

        _activity = nil;

    }

    二.拉流播放(创建播放器播放),代码如下:

    - (void)viewDidLoad {

        [super viewDidLoad];

        [self.view addSubview:self.placeHolderView];

        [self showActivityView];

        IJKFFOptions *options = [IJKFFOptions optionsByDefault];

        [options setPlayerOptionIntValue:1forKey:@"videotoolbox"];

        // 帧速率(fps) (可以改,确认非标准桢率会导致音画不同步,所以只能设定为15或者29.97)[options setPlayerOptionIntValue:29.97forKey:@"r"];

        // -vol——设置音量大小,256为标准音量。(要设置成两倍音量时则输入512,依此类推[options setPlayerOptionIntValue:512forKey:@"vol"];

        IJKFFMoviePlayerController *moviePlayer = [[IJKFFMoviePlayerController alloc] initWithContentURLString:PLAY_URL withOptions:options];

        moviePlayer.view.frame = self.view.bounds;

        moviePlayer.scalingMode = IJKMPMovieScalingModeAspectFill;

        // 设置自动播放(必须设置为NO, 防止自动播放, 才能更好的控制直播的状态)moviePlayer.shouldAutoplay = NO;

        // 默认不显示moviePlayer.shouldShowHudView = NO;

        [self.view insertSubview:moviePlayer.view atIndex:0];

        [moviePlayer prepareToPlay];

        self.moviePlayer = moviePlayer;

        // 设置监听    [self addObserver];

        [self.view addSubview:self.outBtn];

    }

    三.设置监听(主要是监听缓存情况),代码如下:

    - (void)addObserver

    {

        //监听加载状态改变通知[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(loadStateDidChange:) name:IJKMPMoviePlayerLoadStateDidChangeNotificationobject:self.moviePlayer];

    }- (void)loadStateDidChange:(NSNotification *) notification

    {

        //状态为缓冲几乎完成,可以连续播放if((self.moviePlayer.loadState & IJKMPMovieLoadStatePlaythroughOK) !=0) {

            if(!self.moviePlayer.isPlaying) {

                //开始播放            [self.moviePlayer play];

                dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1* NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

                    if (_placeHolderView) {

                        [_placeHolderView removeFromSuperview];

                        _placeHolderView = nil;

                    }

                    [self stopActivityView];

                });

            }else{

                // 如果是网络状态不好, 断开后恢复, 也需要去掉加载if ([_activity isAnimating]) {

                    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1* NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

                        [self stopActivityView];

                    });

                }

            }

        }

        //缓冲中elseif(self.moviePlayer.loadState & IJKMPMovieLoadStateStalled){

            [self showActivityView];

            /* 

                这里主播可能已经结束直播了。我们需要请求服务器查看主播是否已经结束直播。

                方法:

                1、从服务器获取主播是否已经关闭直播。

                    优点:能够正确的获取主播端是否正在直播。

                    缺点:主播端异常crash的情况下是没有办法通知服务器该直播关闭的。

                2、用户http请求该地址,若请求成功表示直播未结束,否则结束

                    优点:能够真实的获取主播端是否有推流数据

                    缺点:如果主播端丢包率太低,但是能够恢复的情况下,数据请求同样是失败的。

            */    }

    }

    四.记得关闭前释放:

    - (void)dealloc{

        if (_moviePlayer) {

            [_moviePlayer shutdown];

            [_moviePlayer.view removeFromSuperview];

            _moviePlayer = nil;

        }

        [[NSNotificationCenter defaultCenter]removeObserver:self];

    }

    相关文章

      网友评论

          本文标题:有关ffmpeg------>直播

          本文链接:https://www.haomeiwen.com/subject/fbpekftx.html