//第一步:注冊(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);