有关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];

}

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

推荐阅读更多精彩内容