有關(guān)ffmpeg------>直播

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

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

參考:iOS中集成ijkplayer視頻直播框架:http://www.reibang.com/p/1f06b27b3ac0

參考直播demo:http://www.reibang.com/p/b8db6c142aad

參考:http://www.reibang.com/u/b09c3959ab3b

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

參考:http://www.reibang.com/p/bd42bacbe4cc(直播原理)

視頻直播脱衙,可以分為 采集,前處理,編碼谬擦,傳輸, 服務(wù)器處理,解碼,渲染

直播前期準(zhǔn)備為:

1.推流用優(yōu)酷開源的LFLiveKit框架。

2.拉流(實(shí)際上就是一個(gè)播放器)用ijkplayer 框架庆杜,當(dāng)然這個(gè)也是開源的。

3.創(chuàng)建本地rtmp服務(wù)器碟摆。

推流:

推流用的是一個(gè)第三方的IFliveKit框架晃财。這個(gè)框架基于rtmp協(xié)議。IFLiveKit內(nèi)部集成了GPUIImage典蜕。內(nèi)部實(shí)現(xiàn)了圖片渲染等美艷效果断盛。減少了開發(fā)時(shí)候美艷效果的調(diào)試。

推流端工作將它細(xì)分為以下幾個(gè)部分(基本上是依次執(zhí)行的):

  一:相機(jī)相冊權(quán)限檢查并作出相應(yīng)的處理方法愉舔。

  二:音頻視頻信息配置(碼率钢猛,采樣率,質(zhì)量等信息)

  三:音頻視頻采集及編碼前的濾鏡等效果(GPUIImage)

  四:音頻視頻編碼轩缤。這里需要注意的是iOS8以上支持硬件編碼命迈,如果不能適配iOS8以上是需要做處理的(參考LFLiveKit)

  五:上傳數(shù)據(jù)(rtmp)

一個(gè)簡單的推流頁面應(yīng)該包含以下幾個(gè)功能:

1.推流狀態(tài)監(jiān)聽。2.切換攝像頭典奉。3.切換美艷效果躺翻。4.開關(guān)推流丧叽。

一.推流之前需要檢查攝像頭和麥克風(fēng)等權(quán)限是否開啟卫玖,并啟動(dòng)攝像頭,核心代碼如下:

//判斷是否有攝像頭if(![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]){

? ? ? ? [self showInfo:@"您的設(shè)備沒有攝像頭或者相關(guān)的驅(qū)動(dòng), 不能進(jìn)行直播"];

? ? ? ? return;

? ? }

? ? //判斷是否有攝像頭權(quán)限AVAuthorizationStatus? authorizationStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];

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

? ? ? ? [self showInfo:@"app需要訪問您的攝像頭踊淳。\n請(qǐng)啟用攝像頭-設(shè)置/隱私/攝像頭"];

? ? ? ? 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需要訪問您的麥克風(fēng)假瞬。\n請(qǐng)啟用麥克風(fēng)-設(shè)置/隱私/麥克風(fēng)"];

? ? ? ? ? ? ? ? return NO;

? ? ? ? ? ? }

? ? ? ? }];

? ? }

//檢查麥克風(fēng)權(quán)限- (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需要訪問您的麥克風(fēng)陕靠。\n請(qǐng)啟用麥克風(fēng)-設(shè)置/隱私/麥克風(fēng)");}

? ? ? ? }];

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

? ? ? ? succ(YES,nil);

? ? }else{

? ? ? ? succ(NO,@"app需要訪問您的麥克風(fēng)。\n請(qǐng)啟用麥克風(fēng)-設(shè)置/隱私/麥克風(fēng)");

? ? }

}

二.創(chuàng)建一個(gè)按鈕.點(diǎn)擊開始推流代碼如下:

- (LFLiveSession*)session{

? ? if(!_session){

? ? ? ? /***? ?默認(rèn)分辨率368 * 640? 音頻:44.1 iphone6以上48? 雙聲道? 方向豎屏 ***/? ? ? ? _session = [[LFLiveSession alloc] initWithAudioConfiguration:[LFLiveAudioConfiguration defaultConfiguration] videoConfiguration:[LFLiveVideoConfiguration defaultConfigurationForQuality:LFLiveVideoQuality_Medium2]];


? ? ? ? /**? ??自己定制高質(zhì)量音頻128K 分辨率設(shè)置為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];

? ? ? ? */// 設(shè)置代理_session.delegate= self;

? ? ? ? _session.running = YES;

? ? ? ? _session.preView = self.livingPreView;

? ? }

? ? return _session;

}

//給服務(wù)器推流

- (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.創(chuàng)建一個(gè)按鈕點(diǎn)擊關(guān)閉推流脱茉,代碼如下:

- (IBAction)endTouched:(id)sender {

? ? // 結(jié)束直播? ? [self.session stopLive];

? ? self.stateLable.text = [NSString stringWithFormat:@"狀態(tài): 直播被關(guān)閉\nRTMP: %@", self.rtmpUrl];

}

4.創(chuàng)建一個(gè)按鈕點(diǎn)擊切換前后攝像頭剪芥,代碼如下:

- (IBAction)camaBtnTouched:(id)sender {

? ? AVCaptureDevicePosition devicePositon = self.session.captureDevicePosition;

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

? ? NSLog(@"切換前置/后置攝像頭");

}

5.創(chuàng)建一個(gè)按鈕設(shè)置美艷功能,代碼如下:

- (IBAction)beautiyBtnTouched:(id)sender {

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

? ? // 默認(rèn)是開啟了美顏功能的self.session.beautyFace = !self.session.beautyFace;

}

6.推流狀態(tài)監(jiān)聽琴许,接受代理税肪,代碼如下:

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

? ? NSString *tempStatus;

? ? switch (state) {

? ? ? ? case LFLiveReady:

? ? ? ? ? ? tempStatus =@"準(zhǔn)備中";

? ? ? ? ? ? break;

? ? ? ? case LFLivePending:

? ? ? ? ? ? tempStatus =@"連接中";

? ? ? ? ? ? break;

? ? ? ? case LFLiveStart:

? ? ? ? ? ? tempStatus =@"已連接";

? ? ? ? ? ? break;

? ? ? ? case LFLiveStop:

? ? ? ? ? ? tempStatus =@"已斷開";

? ? ? ? ? ? break;

? ? ? ? case LFLiveError:

? ? ? ? ? ? tempStatus =@"連接出錯(cuò)";

? ? ? ? ? ? break;

? ? ? ? default:

? ? ? ? ? ? break;

? ? }

? ? self.stateLable.text = [NSString stringWithFormat:@"狀態(tài): %@\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;

}

至此益兄,推流完成。


拉流:

推流完成后箭券,需要拉流才能進(jìn)行完整的直播净捅。拉流我們采用的也是一個(gè)開源的第三方庫IJKMediaFramework。

這個(gè)庫本質(zhì)是一個(gè)播放器辩块,能播放flv格式的播放器蛔六。用起來和ios自帶的AVPlayer很相似。

 實(shí)現(xiàn)功能:1.拉流播放废亭。2.監(jiān)聽国章。

一.創(chuàng)建占位圖和卡頓占位動(dòng)效,代碼如下:

//直播前的占位圖片

- (UIImageView *)placeHolderView

{

? ? if(!_placeHolderView) {

? ? ? ? _placeHolderView = [[UIImageView alloc] init];

? ? ? ? _placeHolderView.frame = self.view.bounds;

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

? ? ? ? // 強(qiáng)制布局? ? ? ? [_placeHolderView layoutIfNeeded];

? ? }

? ? return_placeHolderView;}

//卡頓占位動(dòng)效

- (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];

}

//關(guān)閉卡頓占位動(dòng)效- (void)stopActivityView{

? ? if ([_activity isAnimating]) {

? ? ? ? [_activity startAnimating];

? ? }

? ? [_activity removeFromSuperview];

? ? _activity = nil;

}

二.拉流播放(創(chuàng)建播放器播放)豆村,代碼如下:

- (void)viewDidLoad {

? ? [super viewDidLoad];

? ? [self.view addSubview:self.placeHolderView];

? ? [self showActivityView];

? ? IJKFFOptions *options = [IJKFFOptions optionsByDefault];

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

? ? // 幀速率(fps) (可以改捉腥,確認(rèn)非標(biāo)準(zhǔn)楨率會(huì)導(dǎo)致音畫不同步,所以只能設(shè)定為15或者29.97)[options setPlayerOptionIntValue:29.97forKey:@"r"];

? ? // -vol——設(shè)置音量大小你画,256為標(biāo)準(zhǔn)音量抵碟。(要設(shè)置成兩倍音量時(shí)則輸入512,依此類推[options setPlayerOptionIntValue:512forKey:@"vol"];

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

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

? ? moviePlayer.scalingMode = IJKMPMovieScalingModeAspectFill;

? ? // 設(shè)置自動(dòng)播放(必須設(shè)置為NO, 防止自動(dòng)播放, 才能更好的控制直播的狀態(tài))moviePlayer.shouldAutoplay = NO;

? ? // 默認(rèn)不顯示moviePlayer.shouldShowHudView = NO;

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

? ? [moviePlayer prepareToPlay];

? ? self.moviePlayer = moviePlayer;

? ? // 設(shè)置監(jiān)聽? ? [self addObserver];

? ? [self.view addSubview:self.outBtn];

}

三.設(shè)置監(jiān)聽(主要是監(jiān)聽緩存情況)坏匪,代碼如下:

- (void)addObserver

{

? ? //監(jiān)聽加載狀態(tài)改變通知[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(loadStateDidChange:) name:IJKMPMoviePlayerLoadStateDidChangeNotificationobject:self.moviePlayer];

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

{

? ? //狀態(tài)為緩沖幾乎完成拟逮,可以連續(xù)播放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{

? ? ? ? ? ? // 如果是網(wǎng)絡(luò)狀態(tài)不好, 斷開后恢復(fù), 也需要去掉加載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];

? ? ? ? /*?

? ? ? ? ? ? 這里主播可能已經(jīng)結(jié)束直播了。我們需要請(qǐng)求服務(wù)器查看主播是否已經(jīng)結(jié)束直播适滓。

? ? ? ? ? ? 方法:

? ? ? ? ? ? 1敦迄、從服務(wù)器獲取主播是否已經(jīng)關(guān)閉直播。

? ? ? ? ? ? ? ? 優(yōu)點(diǎn):能夠正確的獲取主播端是否正在直播凭迹。

? ? ? ? ? ? ? ? 缺點(diǎn):主播端異常crash的情況下是沒有辦法通知服務(wù)器該直播關(guān)閉的罚屋。

? ? ? ? ? ? 2、用戶http請(qǐng)求該地址嗅绸,若請(qǐng)求成功表示直播未結(jié)束脾猛,否則結(jié)束

? ? ? ? ? ? ? ? 優(yōu)點(diǎn):能夠真實(shí)的獲取主播端是否有推流數(shù)據(jù)

? ? ? ? ? ? ? ? 缺點(diǎn):如果主播端丟包率太低,但是能夠恢復(fù)的情況下鱼鸠,數(shù)據(jù)請(qǐng)求同樣是失敗的猛拴。

? ? ? ? */? ? }

}

四.記得關(guān)閉前釋放:

- (void)dealloc{

? ? if (_moviePlayer) {

? ? ? ? [_moviePlayer shutdown];

? ? ? ? [_moviePlayer.view removeFromSuperview];

? ? ? ? _moviePlayer = nil;

? ? }

? ? [[NSNotificationCenter defaultCenter]removeObserver:self];

}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末羹铅,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子愉昆,更是在濱河造成了極大的恐慌职员,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,406評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件跛溉,死亡現(xiàn)場離奇詭異焊切,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)芳室,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,395評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門蛛蒙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人渤愁,你說我怎么就攤上這事牵祟。” “怎么了抖格?”我有些...
    開封第一講書人閱讀 167,815評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵诺苹,是天一觀的道長。 經(jīng)常有香客問我雹拄,道長收奔,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,537評(píng)論 1 296
  • 正文 為了忘掉前任滓玖,我火速辦了婚禮坪哄,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘势篡。我一直安慰自己翩肌,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,536評(píng)論 6 397
  • 文/花漫 我一把揭開白布禁悠。 她就那樣靜靜地躺著念祭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪碍侦。 梳的紋絲不亂的頭發(fā)上粱坤,一...
    開封第一講書人閱讀 52,184評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音瓷产,去河邊找鬼站玄。 笑死,一個(gè)胖子當(dāng)著我的面吹牛濒旦,可吹牛的內(nèi)容都是我干的株旷。 我是一名探鬼主播,決...
    沈念sama閱讀 40,776評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼疤估,長吁一口氣:“原來是場噩夢啊……” “哼灾常!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起铃拇,我...
    開封第一講書人閱讀 39,668評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤钞瀑,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后慷荔,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體雕什,經(jīng)...
    沈念sama閱讀 46,212評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,299評(píng)論 3 340
  • 正文 我和宋清朗相戀三年筛圆,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了恐锦。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片头镊。...
    茶點(diǎn)故事閱讀 40,438評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖偿警,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情唯笙,我是刑警寧澤螟蒸,帶...
    沈念sama閱讀 36,128評(píng)論 5 349
  • 正文 年R本政府宣布,位于F島的核電站崩掘,受9級(jí)特大地震影響七嫌,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜苞慢,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,807評(píng)論 3 333
  • 文/蒙蒙 一诵原、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧挽放,春花似錦绍赛、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,279評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至航闺,卻和暖如春褪测,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背潦刃。 一陣腳步聲響...
    開封第一講書人閱讀 33,395評(píng)論 1 272
  • 我被黑心中介騙來泰國打工侮措, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人乖杠。 一個(gè)月前我還...
    沈念sama閱讀 48,827評(píng)論 3 376
  • 正文 我出身青樓分扎,卻偏偏與公主長得像,于是被迫代替她去往敵國和親胧洒。 傳聞我的和親對(duì)象是個(gè)殘疾皇子畏吓,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,446評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容