開發介紹
- libavcodec/avcodec.h
- 常用的數據結構
- AVCodec 編碼器結構體
- AVCodecContext 編碼器上下文
- AVFrame 解碼后的幀
- 結構體內存的分配和釋放
- av_frame_alloc 申請
- av_frame_free() 釋放
- avcodec_alloc_context3() 創建編碼器上下文
- avcodec_free_context() 釋放編碼器上下文
- 解碼步驟
- avcodec_find_decoder 查找解碼器
- avcodec_open2 打開解碼器
- avcodec_decode_video2 解碼
FFMpegH264編碼
- avcodec_find_encoder_by_name 查找編碼器
- avcodec_open3? 設置編碼參數(分辨率、高、寬),并打開編碼器,(解碼的時候直接拷貝對應參數即可,無需再次設置)
- avcodec_encode_video2 編碼
- 真正執行編碼的由第三方庫進行
- 如libx264 libopenh264
- 注意
- 通過ID? ? ? ?查找編/解碼器
- 通過Name?查找編/解碼器
- 參考鏈接:FFmpeg h264編碼 - 簡書
代碼
#include <cstdio>
#include <cstdlib>
#include <cstring>extern "C" {#include<libavutil/opt.h>#include<libavutil/imgutils.h>#include<libavcodec/avcodec.h>
}//對每一幀數據進行編碼
static void encode(AVCodecContext *enc_ctx,AVFrame *frame,AVPacket *pkt,FILE *outfile){int ret = 0;//send the frame to the encoderif (frame){printf("Send frame %3"PRId64"\n",frame->pts);}ret = avcodec_send_frame(enc_ctx,frame);if (ret < 0){fprintf(stderr,"Error sending a frame for encoding\n");exit(1);}while (ret >= 0){ret = avcodec_receive_packet(enc_ctx,pkt);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF){return;} else if (ret < 0){fprintf(stderr,"Error during encoding\n");exit(1);}printf("Write packet %3"PRId64" (size=%5d)\n",pkt->pts,pkt->size);fwrite(pkt->data,1,pkt->size,outfile);av_packet_unref(pkt);}
}
int main(int argc,char** argv){const char *file_name,*codec_name;//輸出文件路徑和編碼器名字,由運行程序時傳入參數(要編碼的內容是從攝像頭獲取的)const AVCodec *codec; //編碼器AVCodecContext *codec_context = nullptr;//編碼上下文環境int i,ret,x,y,got_output; //got_output用于標記一幀是否壓縮成功FILE *file;AVFrame *frame; //原始幀(未壓縮的數據)AVPacket pkt;uint8_t endcode[]={0,0,1,0xb7};if (argc <= 2){fprintf(stderr,"Usage: %s <output file> <codec name>\n",argv[0]);exit(0);}file_name = argv[1];codec_name = argv[2]; //h264編碼器名字是libx264//avcodec_register_all() //delete//通過名字查找編碼器codec = avcodec_find_encoder_by_name(codec_name);if (!codec){fprintf(stderr,"Codec not found\n");exit(1);}//生成編碼上下文環境codec_context = avcodec_alloc_context3(codec);if (!codec_context){fprintf(stderr,"Could not allocate video codec context\n");exit(1);}// 設置碼率codec_context->bit_rate = 400000;// 設置視頻寬高codec_context->width = 352;codec_context->height = 288;// 設置時間基、幀率(時間基根據幀率而變化)codec_context->time_base = (AVRational){1,25}; //一秒鐘25幀,刻度就是1/25codec_context->framerate = (AVRational){25,1}; //時間基根據幀率進行變化// 設置多少幀產生一個關鍵幀,也就是一組幀是多少幀// 如果同一個鏡頭沒有變化,只需要設定一個關鍵幀,一組幀以這個關鍵幀作為參照,從而降低數據存儲codec_context->gop_size = 10;// 設置b幀(前后參考幀)// P幀 向前參考幀codec_context->max_b_frames = 1;// 要編碼的原始數據的YUV格式codec_context->pix_fmt = AV_PIX_FMT_YUV420P;// 如果編碼器id是h264if (codec->id == AV_CODEC_ID_H264){// preset表示采用一個預先設定好的參數集,級別是slow// slow表示壓縮速度是慢的,慢的可以保證視頻質量,用快的會降低視頻質量av_opt_set(codec_context->priv_data,"preset","slow",0);}// 打開編碼器if (avcodec_open2(codec_context,codec,NULL) < 0){fprintf(stderr,"Could not open codec\n");exit(1);}file = fopen(file_name,"wb");if (!file){fprintf(stderr,"Could not open %s\n",file_name);exit(1);}// 初始化幀并設置幀的YUV格式和分辨率frame = av_frame_alloc();if (!frame){fprintf(stderr,"Could not allocate video frame\n");exit(1);}frame->format = codec_context->pix_fmt;frame->width = codec_context->width;frame->height = codec_context->height;ret = av_frame_get_buffer(frame,32);if (ret < 0){fprintf(stderr,"Could not allocate the video frame data\n");exit(1);}// 這里是人工添加數據模擬生成1秒鐘(25幀)的視頻(真實應用中是從攝像頭獲取的原始數據,攝像頭拿到數據后會傳給編碼器,然后編碼器進行編碼形成一幀幀數據。)for (i = 0; i < 25; i++) {av_init_packet(&pkt);//packet data will be allocated by the encoderpkt.data = NULL;pkt.size = 0;// 強制輸出寫入文件fflush(stdout);/* make sure the frame data is writable */ret = av_frame_make_writable(frame);if (ret < 0){exit(1);}// 下面2個循環是人工往frame里面添的數據/* Y */for (int y = 0; y < codec_context->height; y++) {for (int x = 0; x < codec_context->width; x++) {frame->data[0][y * frame->linesize[0] + x] = x + y + i*3;}}/* Cb and Cr */for (int y = 0; y < codec_context->height/2; y++) {for (int x = 0; x < codec_context->width/2; x++) {frame->data[1][y * frame->linesize[1] +x] = 128 + y + i*2;frame->data[2][y * frame->linesize[2] +x] = 64 + x + i*5;}}frame->pts = i;// 進行編碼壓縮encode(codec_context,frame,&pkt,file);}/* flush the encoder */encode(codec_context,NULL,&pkt,file);/* add sequence end code to have a real MPEG file */fwrite(endcode,1,sizeof (endcode),file);fclose(file);avcodec_free_context(&codec_context);av_frame_free(&frame);return 0;
}
- build不出現錯誤之后,點擊run,彈出提示信息,需要輸入指定的參數
- 進入終端頁面,進入cmake-build-debug文件夾下
- 使用如下命令進行數據編碼?./learn_ffmpeg 1.h264 libx264
- 生成1.h264文件
- 使用ffplay 1.h264 進行播放
- 本人使用開源軟件 PotPlayer進行視頻播放?



- ?ffplay 1.h264進行視頻播放,輸出數據的相關描述信息
- Stream #0:0: 流的ID
- 視頻流是 h264 high
- 數據先前的格式是 yuv420p 分辨率是353x288 幀率是25 時間基是25 流的時間基是1200 編碼的時間基是 50
