FFmpeg 264編碼示例
阿新 • • 發佈:2018-12-21
示例程式碼:
encoder_work::encoder_work()
{
mWidth = 0;
mHeight = 0;
mFPS = 0;
mYSize = 0;
mUVSize = 0;
mPTS = 0;
mFmtCtx = NULL;
mEncCtx = NULL;
mYUVFrm = NULL;
mEncoder = NULL;
}
int encoder_work::init(int w, int h, int fps, int bit_rate, char *outfile_name)
{
printf("encoder work init========>\n" );
uninit();
mWidth = w;
mHeight = h;
mFPS = fps;
mYSize = w*h;
mUVSize = mYSize / 4;
//register file package like mp4 and all others
av_register_all();
//register all codecs
avcodec_register_all();
//1 create encoder
mEncoder = avcodec_find_encoder(AV_CODEC_ID_H264);
if (!mEncoder)
{
cout << " avcodec_find_encoder AV_CODEC_ID_H264 failed!" << endl;
return -1;
}
//get encoder contex
mEncCtx = avcodec_alloc_context3(mEncoder);
if (!mEncCtx)
{
cout << " avcodec_alloc_context3 for encoder contx failed!" << endl;
return -1;
}
//set encoder params
//bit rate
mEncCtx-> bit_rate = bit_rate;
mEncCtx->width = mWidth;
mEncCtx->height = mHeight;
mEncCtx->time_base = { 1,mFPS };
//set gop size, in another way I frame gap
mEncCtx->gop_size = 50;
mEncCtx->max_b_frames = 0;
mEncCtx->pix_fmt = AV_PIX_FMT_YUV420P;
mEncCtx->codec_id = AV_CODEC_ID_H264;
mEncCtx->thread_count = 4;
mEncCtx->qmin = 10;
mEncCtx->qmax = 51;
mEncCtx->qcompress = 0.6;
//av_opt_set(mEncCtx->priv_data, "preset", "ultrafast", 0);
av_opt_set(mEncCtx->priv_data, "tune", "zerolatency", 0);
//global header info
mEncCtx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
//open encoder
int ret = avcodec_open2(mEncCtx,mEncoder,NULL);
if (ret < 0)
{
cout << " avcodec_open2 encoder failed!" << endl;
return -1;
}
cout << "avcodec_open2 encoder success!" << endl;
//2 create out context
avformat_alloc_output_context2(&mFmtCtx, 0, 0, outfile_name);
//3 add video stream
mOutStm = avformat_new_stream(mFmtCtx,NULL);
mOutStm->id = 0;
mOutStm->codecpar->codec_tag = 0;
avcodec_parameters_from_context(mOutStm->codecpar,mEncCtx);
cout << "===============================================" << endl;
av_dump_format(mFmtCtx, 0, outfile_name, 1);
cout << "===============================================" << endl;
//alloc output yuv frame
mYUVFrm = av_frame_alloc();
mYUVFrm->format = AV_PIX_FMT_YUV420P;
mYUVFrm->width = mWidth;
mYUVFrm->height = mHeight;
//alloc frame buffer
ret = av_frame_get_buffer(mYUVFrm,32);
if (ret < 0)
{
av_frame_free(&mYUVFrm);
mYUVFrm = NULL;
cout << " av_frame_get_buffer failed!" << endl;
return -1;
}
//5 write mp4 head
ret = avio_open(&mFmtCtx->pb,outfile_name,AVIO_FLAG_WRITE);
if (ret < 0)
{
cout << " avio_open failed!" << endl;
return -1;
}
ret = avformat_write_header(mFmtCtx, NULL);
if (ret < 0)
{
cout << " avformat_write_header failed!" << endl;
return -1;
}
}
int encoder_work::uninit()
{
printf("encoder_work uninit-----\n");
if(mFmtCtx)
{
//wirte file trailer
av_write_trailer(mFmtCtx);
//close file IO
avio_close(mFmtCtx->pb);
//clean context
avformat_free_context(mFmtCtx);
mFmtCtx = NULL;
}
if(mEncCtx)
{
//close encoder
avcodec_close(mEncCtx);
//clean encoder contex
avcodec_free_context(&mEncCtx);
mEncCtx = NULL;
}
if(mYUVFrm)
{
av_frame_free(&mYUVFrm);
mYUVFrm = NULL;
}
mWidth = 0;
mHeight = 0;
mFPS = 0;
mYSize = 0;
mUVSize = 0;
mPTS = 0;
return 0;
}
int encoder_work::process(cv::Mat &rgb)
{
cv::Mat yuv;
cv::cvtColor(rgb, yuv, cv::COLOR_BGR2YUV_I420);
return process(yuv.data);
}
int encoder_work::process(unsigned char *yuvData)
{
if(!mYUVFrm)
{
printf("not ready env\n");
return -1;
}
memcpy(mYUVFrm->data[0], yuvData, mYSize);
memcpy(mYUVFrm->data[1], yuvData+mYSize, mUVSize);
memcpy(mYUVFrm->data[2], yuvData+mYSize+mUVSize, mUVSize);
mYUVFrm->pts = mPTS;
mPTS = mPTS + 4000;
//mPTS = mPTS + 1000000;
//send to encoder
int ret = avcodec_send_frame(mEncCtx, mYUVFrm);
if (ret != 0)
{
char err[64] = {0};
av_strerror(ret, err, 64);
printf("encoder send frame %d err:%s\n",mPTS-4000, err);
return -1;
}
AVPacket pkt;
av_init_packet(&pkt);
//receive from encoder
//note that frame will not be received immediately
ret = avcodec_receive_packet(mEncCtx,&pkt);
if (ret != 0)
{
printf("encoder recieve frame %d err\n",mPTS-4000);
return -1;
}
//write encoded frame to file
av_interleaved_write_frame(mFmtCtx,&pkt);
av_free_packet(&pkt);
return 0;
}
int encoder_work::flushOut()
{
//as frames will be hysteresis, we need flush all remained frames
AVPacket pkt;
av_init_packet(&pkt);
int ret = 0;
while(ret >= 0)
{
//flush out rest frames, send NULL frame data
avcodec_send_frame(mEncCtx, NULL);
//receive frame from encoder
ret = avcodec_receive_packet(mEncCtx,&pkt);
if (ret != 0)
{
printf("encoder recieve frame %d err\n",mPTS-4000);
break;
}
//wirte encoded frame to file
av_interleaved_write_frame(mFmtCtx,&pkt);
av_free_packet(&pkt);
}
return 0;
}
標頭檔案定義
class encoder_work
{
public:
encoder_work();
~encoder_work() { uninit(); }
int init(int w, int h, int fps, int bit_rate, char *outfile_name);
int uninit();
int process(unsigned char *yuvData);
int process(cv::Mat &rgb);
int flushOut();
private:
int mWidth;
int mHeight;
int mFPS;
int mYSize;
int mUVSize;
int mPTS;
AVCodec *mEncoder;
AVCodecContext *mEncCtx;
AVFormatContext *mFmtCtx;
AVStream *mOutStm;
AVFrame *mYUVFrm;
};