iOS平台基於ffmpeg的ApsaraVideo for Live技術揭秘

來源:互聯網
上載者:User

標籤:

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

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

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

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

ApsaraVideo for Live怎麼直播呢?大概流程圖如下:

1.直播人裝置端:從網路攝影機擷取視頻流,然後使用rtmp服務提交到伺服器

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

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

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

前期準備:

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

我是用上面指令碼編譯的最新版,為了後期使用,需要將這些C檔案添加到項目:

cmdutils_common_opts.hcmdutils.h及cmdutils.cconfig.h在scratch目錄下取個對應平台的ffmpeg_filter.cffmpeg_opt.cffmpeg_videotoolbox.cffmpeg.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,指定自訂的回呼函數。範例程式碼如下:

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

 

3. 自己寫回呼函數,從輸入源中取資料。範例程式碼如下:

//Callbackintread_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.org/doxygen/1.0/imgconvert_8c-source.html#l00940

發現這個方法在之前的實現中是在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_cstatic void deinterlace_line_inplace_cstatic void deinterlace_bottom_fieldstatic void deinterlace_bottom_field_inplaceint avpicture_deinterlace

 

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

attribute_deprecatedintavpicture_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

 

iOS平台基於ffmpeg的ApsaraVideo for Live技術揭秘

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.