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

参考: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------>直播)