iOS平臺基于ffmpeg的視頻直播技術揭秘

現在非常流行直播,相信很多人都跟我一樣十分好奇這個技術是如何實現的,正好最近在做一個ffmpeg的項目,發現這個工具很容易就可以做直播,下面來給大家分享下技術要點:

首先你得編譯出ffmpeg運行所需的靜態庫,這個百度一下有很多內容,這里我就不多說了,建議可以用Github上的一個開源腳本來編譯,簡單粗暴有效率。

地址:GitHub - kewlbear/FFmpeg-iOS-build-script: Shell scripts to build FFmpeg for iOS and tvOS

下載后直接用終端運行build-ffmpeg.sh腳本就行了,大概半個小時就全部編譯好了…反正我覺得速度還行吧(PS:當初編譯Android源碼那叫一個慢啊…),若是報錯就再來一遍,直到提示成功。

視頻直播怎么直播呢?大概流程圖如下:

1.直播人設備端:從攝像頭獲取視頻流,然后使用rtmp服務提交到服務器

2.服務器端:接收直播人提交的rtmp視頻流,并為觀看者提供rtmp源

3.觀看者:用播放器播放rtmp源的視頻.

PS:RTMP是Real Time Messaging Protocol(實時消息傳輸協議)的首字母縮寫。該協議基于TCP,是一個協議族,包括RTMP基本協議及RTMPT/RTMPS/RTMPE等多種變種。

前期準備:

新建一個項目,將所有需要引入的ffmpeg的靜態庫及其他相關庫引入到工程中,配置頭文件搜索路徑,這一步網上有很多教程就不重復敘述了。

我是用上面腳本編譯的最新版,為了后期使用,需要將這些C文件添加到項目:

cmdutils_common_opts.h

cmdutils.h及cmdutils.c

config.h在scratch目錄下取個對應平臺的

ffmpeg_filter.c

ffmpeg_opt.c

ffmpeg_videotoolbox.c

ffmpeg.h及ffmpeg.c

除了config.h文件外,別的文件均在ffmpeg-3.0源碼目錄中

注意問題:

1.編譯會報錯,因為ffmpeg.c文件中包含main函數,請將該函數重命名為ffmpeg_main并在ffmpeg.h中添加ffmpeg_main函數的聲明.

2.ffmpeg任務完成后會結束進程,而iOS設備都是單進程多線程任務,所以需要將cmdutils.c文件中的exit_program方法中的

exit(ret);

改為結束線程,需要引入#include

pthread_exit(NULL);

直播端:用ffmpeg庫抓取直播人設備的攝像頭信息,生成裸數據流stream,注意!!!這里是裸流,裸流意味著什么呢?就是不包含PTS(Presentation Time Stamp。PTS主要用于度量解碼后的視頻幀什么時候被顯示出來)、DTS(Decode Time Stamp。DTS主要是標識讀入內存中的bit流在什么時候開始送入解碼器中進行解碼)等信息的數據流,播放器拿到這種流是無法進行播放的.將這個客戶端只需要將這個數據流以RTMP協議傳到服務器即可。

如何獲取攝像頭信息:

使用libavdevice庫可以打開獲取攝像頭的輸入流,在ffmpeg中獲取攝像頭的輸入流跟打開文件輸入流很類似,示例代碼:

//打開一個文件:

AVFormatContext*pFormatCtx =avformat_alloc_context();

avformat_open_input(&pFormatCtx,"test.h264",NULL,NULL);

//獲取攝像頭輸入:

AVFormatContext*pFormatCtx =avformat_alloc_context();

//多了查找輸入設備的這一步

AVInputFormat*ifmt=av_find_input_format("vfwcap");

//選取vfwcap類型的第一個輸入設別作為輸入流

avformat_open_input(&pFormatCtx,0, ifmt,NULL);

如何使用RTMP上傳視頻流:

使用RTMP上傳文件的指令是:

使用ffmpeg.c中的ffmpeg_main方法直接運行該指令即可,示例代碼:

NSString*command?=@"ffmpeg -re -i temp.h264 -vcodec copy -f flv rtmp://xxx/xxx/livestream";

//根據空格將指令分割為指令數組

NSArray*argv_array=[command_strcomponentsSeparatedByString:(@" ")];

//將OC對象轉換為對應的C對象

intargc=(int)argv_array.count;

char** argv=(char**)malloc(sizeof(char*)*argc);

for(inti=0;i

{

argv[i]=(char*)malloc(sizeof(char)*1024);

strcpy(argv[i],[[argv_arrayobjectAtIndex:i]UTF8String]);

}

//傳入指令數及指令數組

ffmpeg_main(argc,argv);

//線程已殺死,下方的代碼不會執行

ffmpeg -re -itemp.h264 -vcodec copy -f flvrtmp://xxx/xxx/livestream

這行代碼就是

-re參數是按照幀率發送,否則ffmpeg會按最高速率發送,那么視頻會忽快忽慢,

-itemp.h264是需要上傳的裸h264流

-vcoder copy 這段是復制一份不改變源

-f flvrtmp://xxx/xxx/livestream是指定格式為flv發送到這個url

這里看到輸入是裸流或者是文件,但是我們從攝像頭獲取到的是直接內存流,這怎么解決呢?

當然是有辦法的啦

1.將這串參數中temp.h264參數變為null

2.初始化自定義的AVIOContext,指定自定義的回調函數。示例代碼如下:

//AVIOContext中的緩存

unsignedchar*aviobuffer=(unsignedchar*)av_malloc(32768);

AVIOContext*avio=avio_alloc_context(aviobuffer,32768,0,NULL,read_buffer,NULL,NULL);

pFormatCtx->pb=avio;

if(avformat_open_input(&pFormatCtx,NULL,NULL,NULL)!=0){

printf("Couldn't open inputstream.(無法打開輸入流)\n");

return-1;

}

3.?自己寫回調函數,從輸入源中取數據。示例代碼如下:

//Callback

intread_buffer(void*opaque, uint8_t *buf,intbuf_size){

//休眠,否則會一次性全部發送完

if(pkt.stream_index==videoindex){

AVRational?time_base=ifmt_ctx->streams[videoindex]->time_base;

AVRational time_base_q={1,AV_TIME_BASE};

int64_t pts_time = av_rescale_q(pkt.dts, time_base, time_base_q);

int64_t now_time = av_gettime() - start_time;

if(pts_time > now_time)

av_usleep(pts_time - now_time);

}

//fp_open替換為攝像頭輸入流

if(!feof(fp_open)){

inttrue_size=fread(buf,1,buf_size,fp_open);

returntrue_size;

}else{

return-1;

}

}

服務端:原諒我一個移動開發不懂服務器端,大概應該是獲取直播端上傳的視頻流再進行廣播.所以就略過吧.

播放端:播放端實際上就是一個播放器,可以有很多解決方案,這里提供一種最簡單的,因為很多直播軟件播放端和客戶端都是同一個軟件,所以這里直接使用項目中已經有的ffmpeg進行播放簡單粗暴又省事.

在Github上有個基于ffmpeg的第三方播放器kxmovie,直接用這個就好.

地址:GitHub - kolyvan/kxmovie: movie player for iOS using ffmpeg

當你把kxmovie的播放器部分添加到之前做好的上傳部分,你會發現報錯了......

查找的結果是kxmovie所使用的avpicture_deinterlace方法不存在,我第一個想法就是想辦法屏蔽到這個方法,讓程序能正常使用,結果......當然不能正常播放視頻了,一百度才發現這個方法居然是去交錯,雖然我視頻只是不夠豐富,但是也知道這個方法肯定是不能少的.

沒事,只有改源碼了.從ffmpeg官方源碼庫中可以找到這個方法.

地址:FFmpeg: libavcodec/imgconvert.c Source File

發現這個方法在之前的實現中是在avcodec.h中聲明是AVPicture的方法,然后在avpicture.c中再調用libavcodec/imgconvert.c這個文件中,也就是說這個方法本身就是屬于imgconvert.c的,avpicture.c只是間接調用,查找ffmpeg3.0的imgconvert.c文件,居然沒這個方法,但是官方代碼庫中是有這個方法的,難道是已經移除了?移除不移除關我毛事,我只想能用,所以簡單點直接改avpicture.c

首先添加這幾個宏定義

#define deinterlace_line_inplace deinterlace_line_inplace_c

#define deinterlace_line ????????deinterlace_line_c

#define ff_cropTbl ((uint8_t *)NULL)

然后從網頁上復制這幾個方法到avpicture.c文件中

static void deinterlace_line_c

static void deinterlace_line_inplace_c

static void deinterlace_bottom_field

static void deinterlace_bottom_field_inplace

int avpicture_deinterlace

再在avcodec.h頭文件中,avpicture_alloc方法下面添加聲明:

attribute_deprecated

intavpicture_deinterlace(AVPicture*dst,constAVPicture*src,

enumAVPixelFormatpix_fmt,intwidth,intheight);

保存后再用終端執行build-ffmpeg.sh腳本編譯一次就行了…再次導入項目中kxmovie就不會報錯了,播放視頻的代碼如下:

KxMovieViewController*vc = [KxMovieViewControllermovieViewControllerWithContentPath:pathparameters:nil];

[selfpresentViewController:vcanimated:YEScompletion:nil];

注:其中path可以是以http/rtmp/trsp開始的url

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,333評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,491評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,263評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,946評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,708評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,409評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,939評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,774評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,641評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,872評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,650評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內容