FFmpeg視頻編碼 YUV420P編碼H264

//第一步:注冊(cè)組件->編碼器艇拍、解碼器等等…

? ? av_register_all();

? ? //第二步:初始化封裝格式上下文->視頻編碼->處理為視頻壓縮數(shù)據(jù)格式

? ? AVFormatContext *avformat_context = avformat_alloc_context();

? ? //注意事項(xiàng):FFmepg程序推測(cè)輸出文件類型->視頻壓縮數(shù)據(jù)格式類型

? ? const char *coutFilePath = [outFilePath UTF8String];

? ? //得到視頻壓縮數(shù)據(jù)格式類型(h264桑涎、h265、mpeg2等等...)

? ? AVOutputFormat *avoutput_format = av_guess_format(NULL, coutFilePath, NULL);

? ? //指定類型

? ? avformat_context->oformat = avoutput_format;


? ? //第三步:打開輸出文件

? ? //參數(shù)一:輸出流

? ? //參數(shù)二:輸出文件

? ? //參數(shù)三:權(quán)限->輸出到文件中

? ? if (avio_open(&avformat_context->pb, coutFilePath, AVIO_FLAG_WRITE) < 0) {

? ? ? ? NSLog(@"打開輸出文件失敗");

? ? ? ? return;

? ? }


? ? //第四步:創(chuàng)建輸出碼流->創(chuàng)建了一塊內(nèi)存空間->并不知道他是什么類型流->希望他是視頻流

? ? AVStream *av_video_stream = avformat_new_stream(avformat_context, NULL);


? ? //第五步:查找視頻編碼器

? ? //1、獲取編碼器上下文

? ? AVCodecContext *avcodec_context = av_video_stream->codec;


? ? //2、設(shè)置編解碼器上下文參數(shù)->必需設(shè)置->不可少

? ? //目標(biāo):設(shè)置為是一個(gè)視頻編碼器上下文->指定的是視頻編碼器

? ? //上下文種類:視頻解碼器、視頻編碼器乡洼、音頻解碼器、音頻編碼器

? ? //2.1 設(shè)置視頻編碼器ID

? ? avcodec_context->codec_id = avoutput_format->video_codec;

? ? //2.2 設(shè)置編碼器類型->視頻編碼器

? ? //視頻編碼器->AVMEDIA_TYPE_VIDEO

? ? //音頻編碼器->AVMEDIA_TYPE_AUDIO

? ? avcodec_context->codec_type = AVMEDIA_TYPE_VIDEO;

? ? //2.3 設(shè)置讀取像素?cái)?shù)據(jù)格式->編碼的是像素?cái)?shù)據(jù)格式->視頻像素?cái)?shù)據(jù)格式->YUV420P(YUV422P匕坯、YUV444P等等...)

? ? //注意:這個(gè)類型是根據(jù)你解碼的時(shí)候指定的解碼的視頻像素?cái)?shù)據(jù)格式類型

? ? avcodec_context->pix_fmt = AV_PIX_FMT_YUV420P;

? ? //2.4 設(shè)置視頻寬高->視頻尺寸

? ? avcodec_context->width = 640;

? ? avcodec_context->height = 352;

? ? //2.5 設(shè)置幀率->表示每秒25幀

? ? //視頻信息->幀率 : 25.000 fps

? ? //f表示:幀數(shù)

? ? //ps表示:時(shí)間(單位:每秒)

? ? avcodec_context->time_base.num = 1;

? ? avcodec_context->time_base.den = 25;

? ? //2.6 設(shè)置碼率

? ? //2.6.1 什么是碼率束昵?

? ? //含義:每秒傳送的比特(bit)數(shù)單位為 bps(Bit Per Second),比特率越高葛峻,傳送數(shù)據(jù)速度越快锹雏。

? ? //單位:bps,"b"表示數(shù)據(jù)量术奖,"ps"表示每秒

? ? //目的:視頻處理->視頻碼率

? ? //2.6.2 什么是視頻碼率?

? ? //含義:視頻碼率就是數(shù)據(jù)傳輸時(shí)單位時(shí)間傳送的數(shù)據(jù)位數(shù)礁遵,一般我們用的單位是kbps即千位每秒

? ? //視頻碼率計(jì)算如下?

? ? //基本的算法是:【碼率】(kbps)=【視頻大小 - 音頻大小】(bit位) /【時(shí)間】(秒)

? ? //例如:Test.mov時(shí)間 = 24采记,文件大小(視頻+音頻) = 1.73MB

? ? //視頻大小 = 1.34MB(文件占比:77%) = 1.34MB * 1024 * 1024 * 8 = 字節(jié)大小 = 468365字節(jié) = 468Kbps

? ? //音頻大小 = 376KB(文件占比:21%)

? ? //計(jì)算出來值->碼率 : 468Kbps->表示1000佣耐,b表示位(bit->位)

? ? //總結(jié):碼率越大,視頻越大

? ? avcodec_context->bit_rate = 468000;


? ? //2.7 設(shè)置GOP->影響到視頻質(zhì)量問題->畫面組->一組連續(xù)畫面

? ? //MPEG格式畫面類型:3種類型->分為->I幀唧龄、P幀兼砖、B幀

? ? //I幀->內(nèi)部編碼幀->原始幀(原始視頻數(shù)據(jù))

? ? //? ? 完整畫面->關(guān)鍵幀(必需的有,如果沒有I既棺,那么你無法進(jìn)行編碼讽挟,解碼)

? ? //? ? 視頻第1幀->視頻序列中的第一個(gè)幀始終都是I幀,因?yàn)樗顷P(guān)鍵幀

? ? //P幀->向前預(yù)測(cè)幀->預(yù)測(cè)前面的一幀類型丸冕,處理數(shù)據(jù)(前面->I幀戏挡、B幀)

? ? //? ? P幀數(shù)據(jù)->根據(jù)前面的一幀數(shù)據(jù)->進(jìn)行處理->得到了P幀

? ? //B幀->前后預(yù)測(cè)幀(雙向預(yù)測(cè)幀)->前面一幀和后面一幀

? ? //? ? B幀壓縮率高,但是對(duì)解碼性能要求較高晨仑。

? ? //總結(jié):I只需要考慮自己 = 1幀褐墅,P幀考慮自己+前面一幀 = 2幀,B幀考慮自己+前后幀 = 3幀

? ? //? ? 說白了->P幀和B幀是對(duì)I幀壓縮

? ? //每250幀洪己,插入1個(gè)I幀妥凳,I幀越少,視頻越小->默認(rèn)值->視頻不一樣

? ? avcodec_context->gop_size = 250;


? ? //2.8 設(shè)置量化參數(shù)->數(shù)學(xué)算法(高級(jí)算法)->不講解了

? ? //總結(jié):量化系數(shù)越小答捕,視頻越是清晰

? ? //一般情況下都是默認(rèn)值逝钥,最小量化系數(shù)默認(rèn)值是10,最大量化系數(shù)默認(rèn)值是51

? ? avcodec_context->qmin = 10;

? ? avcodec_context->qmax = 51;

? ? //2.9 設(shè)置b幀最大值->設(shè)置不需要B幀

? ? avcodec_context->max_b_frames = 0;

? ? //第二點(diǎn):查找編碼器->h264

? ? //找不到編碼器->h264

? ? //重要原因是因?yàn)椋壕幾g庫(kù)沒有依賴x264庫(kù)(默認(rèn)情況下FFmpeg沒有編譯進(jìn)行h264庫(kù))

? ? //第一步:編譯h264庫(kù)

? ? AVCodec *avcodec = avcodec_find_encoder(avcodec_context->codec_id);

? ? if (avcodec == NULL) {

? ? ? ? NSLog(@"找不到編碼器");

? ? ? ? return;

? ? }

? ? NSLog(@"編碼器名稱為:%s", avcodec->name);

? ? //第六步:打開h264編碼器

? ? //缺少優(yōu)化步驟拱镐?

? ? //編碼延時(shí)問題

? ? //編碼選項(xiàng)->編碼設(shè)置

? ? AVDictionary *param = 0;

? ? if (avcodec_context->codec_id == AV_CODEC_ID_H264) {

? ? ? ? //需要查看x264源碼->x264.c文件

? ? ? ? //第一個(gè)值:預(yù)備參數(shù)

? ? ? ? //key: preset

? ? ? ? //value: slow->慢

? ? ? ? //value: superfast->超快

? ? ? ? av_dict_set(?m, "preset", "slow", 0);

? ? ? ? //第二個(gè)值:調(diào)優(yōu)

? ? ? ? //key: tune->調(diào)優(yōu)

? ? ? ? //value: zerolatency->零延遲

? ? ? ? av_dict_set(?m, "tune", "zerolatency", 0);

? ? }

? ? if (avcodec_open2(avcodec_context, avcodec, ?m) < 0) {

? ? ? ? NSLog(@"打開編碼器失敗");

? ? ? ? return;

? ? }


? ? //第七步:寫入文件頭信息

? ? avformat_write_header(avformat_context, NULL);


? ? //第8步:循環(huán)編碼yuv文件->視頻像素?cái)?shù)據(jù)(yuv格式)->編碼->視頻壓縮數(shù)據(jù)(h264格式)

? ? //8.1 定義一個(gè)緩沖區(qū)

? ? //作用:緩存一幀視頻像素?cái)?shù)據(jù)

? ? //8.1.1 獲取緩沖區(qū)大小

? ? int buffer_size = av_image_get_buffer_size(avcodec_context->pix_fmt,

?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? avcodec_context->width,

?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? avcodec_context->height,

?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 1);


? ? //8.1.2 創(chuàng)建一個(gè)緩沖區(qū)

? ? int y_size = avcodec_context->width * avcodec_context->height;

? ? uint8_t *out_buffer = (uint8_t *) av_malloc(buffer_size);


? ? //8.1.3 打開輸入文件

? ? const char *cinFilePath = [inFilePath UTF8String];

? ? FILE *in_file = fopen(cinFilePath, "rb");

? ? if (in_file == NULL) {

? ? ? ? NSLog(@"文件不存在");

? ? ? ? return;

? ? }


? ? //8.2.1 開辟一塊內(nèi)存空間->av_frame_alloc

? ? //開辟了一塊內(nèi)存空間

? ? AVFrame *av_frame = av_frame_alloc();

? ? //8.2.2 設(shè)置緩沖區(qū)和AVFrame類型保持一直->填充數(shù)據(jù)

? ? av_image_fill_arrays(av_frame->data,

?? ? ? ? ? ? ? ? ? ? ? ? av_frame->linesize,

?? ? ? ? ? ? ? ? ? ? ? ? out_buffer,

?? ? ? ? ? ? ? ? ? ? ? ? avcodec_context->pix_fmt,

?? ? ? ? ? ? ? ? ? ? ? ? avcodec_context->width,

?? ? ? ? ? ? ? ? ? ? ? ? avcodec_context->height,

?? ? ? ? ? ? ? ? ? ? ? ? 1);


? ? int i = 0;


? ? //9.2 接收一幀視頻像素?cái)?shù)據(jù)->編碼為->視頻壓縮數(shù)據(jù)格式

? ? AVPacket *av_packet = (AVPacket *) av_malloc(buffer_size);

? ? int result = 0;

? ? int current_frame_index = 1;

? ? while (true) {

? ? ? ? //8.1 從yuv文件里面讀取緩沖區(qū)

? ? ? ? //讀取大兴铱睢:y_size * 3 / 2

? ? ? ? if (fread(out_buffer, 1, y_size * 3 / 2, in_file) <= 0) {

? ? ? ? ? ? NSLog(@"讀取完畢...");

? ? ? ? ? ? break;

? ? ? ? }else if (feof(in_file)) {

? ? ? ? ? ? break;

? ? ? ? }


? ? ? ? //8.2 將緩沖區(qū)數(shù)據(jù)->轉(zhuǎn)成AVFrame類型

? ? ? ? //給AVFrame填充數(shù)據(jù)

? ? ? ? //8.2.3 void * restrict->->轉(zhuǎn)成->AVFrame->ffmpeg數(shù)據(jù)類型

? ? ? ? //Y值

? ? ? ? av_frame->data[0] = out_buffer;

? ? ? ? //U值

? ? ? ? av_frame->data[1] = out_buffer + y_size;

? ? ? ? //V值

? ? ? ? av_frame->data[2] = out_buffer + y_size * 5 / 4;

? ? ? ? av_frame->pts = i;

? ? ? ? //注意時(shí)間戳

? ? ? ? i++;

? ? ? ? //總結(jié):這樣一來我們的AVFrame就有數(shù)據(jù)了


? ? ? ? //第9步:視頻編碼處理

? ? ? ? //9.1 發(fā)送一幀視頻像素?cái)?shù)據(jù)

? ? ? ? avcodec_send_frame(avcodec_context, av_frame);

? ? ? ? //9.2 接收一幀視頻像素?cái)?shù)據(jù)->編碼為->視頻壓縮數(shù)據(jù)格式

? ? ? ? result =avcodec_receive_packet(avcodec_context, av_packet);

? ? ? ? //9.3 判定是否編碼成功

? ? ? ? if (result == 0) {

? ? ? ? ? ? //編碼成功

? ? ? ? ? ? //第10步:將視頻壓縮數(shù)據(jù)->寫入到輸出文件中->outFilePath

? ? ? ? ? ? av_packet->stream_index = av_video_stream->index;

? ? ? ? ? ? result =av_write_frame(avformat_context, av_packet);

? ? ? ? ? ? NSLog(@"當(dāng)前是第%d幀", current_frame_index);

? ? ? ? ? ? current_frame_index++;

? ? ? ? ? ? //是否輸出成功

? ? ? ? ? ? if (result < 0) {

? ? ? ? ? ? ? ? NSLog(@"輸出一幀數(shù)據(jù)失敗");

? ? ? ? ? ? ? ? return;

? ? ? ? ? ? }

? ? ? ? }

? ? }


? ? //第11步:寫入剩余幀數(shù)據(jù)->可能沒有

? ? flush_encoder(avformat_context, 0);


? ? //第12步:寫入文件尾部信息

? ? av_write_trailer(avformat_context);


? ? //第13步:釋放內(nèi)存

? ? avcodec_close(avcodec_context);

? ? av_free(av_frame);

? ? av_free(out_buffer);

? ? av_packet_free(&av_packet);

? ? avio_close(avformat_context->pb);

? ? avformat_free_context(avformat_context);

? ? fclose(in_file);

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末持际,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子哗咆,更是在濱河造成了極大的恐慌蜘欲,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,548評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件晌柬,死亡現(xiàn)場(chǎng)離奇詭異姥份,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)年碘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門澈歉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人屿衅,你說我怎么就攤上這事埃难。” “怎么了涤久?”我有些...
    開封第一講書人閱讀 167,990評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵凯砍,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我拴竹,道長(zhǎng),這世上最難降的妖魔是什么剧罩? 我笑而不...
    開封第一講書人閱讀 59,618評(píng)論 1 296
  • 正文 為了忘掉前任栓拜,我火速辦了婚禮,結(jié)果婚禮上惠昔,老公的妹妹穿的比我還像新娘幕与。我一直安慰自己,他們只是感情好镇防,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,618評(píng)論 6 397
  • 文/花漫 我一把揭開白布啦鸣。 她就那樣靜靜地躺著,像睡著了一般来氧。 火紅的嫁衣襯著肌膚如雪诫给。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,246評(píng)論 1 308
  • 那天啦扬,我揣著相機(jī)與錄音中狂,去河邊找鬼。 笑死扑毡,一個(gè)胖子當(dāng)著我的面吹牛胃榕,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播瞄摊,決...
    沈念sama閱讀 40,819評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼勋又,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼苦掘!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起楔壤,我...
    開封第一講書人閱讀 39,725評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤鹤啡,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后挺邀,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體揉忘,經(jīng)...
    沈念sama閱讀 46,268評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,356評(píng)論 3 340
  • 正文 我和宋清朗相戀三年端铛,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了泣矛。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,488評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡禾蚕,死狀恐怖您朽,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情换淆,我是刑警寧澤哗总,帶...
    沈念sama閱讀 36,181評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站倍试,受9級(jí)特大地震影響讯屈,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜县习,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,862評(píng)論 3 333
  • 文/蒙蒙 一涮母、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧躁愿,春花似錦叛本、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至逸雹,卻和暖如春营搅,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背梆砸。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工剧防, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人辫樱。 一個(gè)月前我還...
    沈念sama閱讀 48,897評(píng)論 3 376
  • 正文 我出身青樓峭拘,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子鸡挠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,500評(píng)論 2 359

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