1. 程式人生 > >ffmpeg實現windows螢幕錄製YUV420P並實現H264編碼

ffmpeg實現windows螢幕錄製YUV420P並實現H264編碼

完整工程下載
https://download.csdn.net/download/iot_shun/10313142

首先之前我們的程式碼

利用gdigrab

已經實現了螢幕錄製功能,可是我們發現,儲存的YYU42OP檔案實現太大,100張150M,在音視訊實時傳輸的過程中

我們需要利用軟硬體實現編碼壓縮,視訊編碼的方式有很多,例如h263,MPEG-2, H264等,這裡我麼採用的是H264編碼

實現效果:

        YYU音視訊檔案(150M)壓縮成只有491k


視訊錄製效果:

    首先看Yvu檔案----->下載一個yuv播放器


然後看h264檔案


兩者效果一致;

比之前螢幕錄製YYU240P,這裡只需要配置H264的編碼器



        //查詢h264編碼器
        pH264Codec = avcodec_find_encoder(AV_CODEC_ID_H264);
        if(!pH264Codec)
        {
          fprintf(stderr, "---------h264 codec not found----\n");
          exit(1);
        }
        pH264CodecCtx = avcodec_alloc_context3(pH264Codec);
        pH264CodecCtx->codec_id = AV_CODEC_ID_H264;
        pH264CodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
        pH264CodecCtx->pix_fmt = PIX_FMT_YUV420P;
        pH264CodecCtx->width = pCodecCtx->width;
        pH264CodecCtx->height = pCodecCtx->height;
        pH264CodecCtx->time_base.num = 1;
        pH264CodecCtx->time_base.den = 15;//幀率(既一秒鐘多少張圖片)
        pH264CodecCtx->bit_rate = 400000; //位元率(調節這個大小可以改變編碼後視訊的質量)
        pH264CodecCtx->gop_size=12;
        //H264 還可以設定很多引數 自行研究吧
        pH264CodecCtx->qmin = 10;
        pH264CodecCtx->qmax = 51;
        // some formats want stream headers to be separate
        if (pH264CodecCtx->flags & AVFMT_GLOBALHEADER)
            pH264CodecCtx->flags |= CODEC_FLAG_GLOBAL_HEADER;
        // Set Option
        AVDictionary *param = 0;
        //H.264
        av_dict_set(&param, "preset", "superfast", 0);
        av_dict_set(&param, "tune", "zerolatency", 0);  //實現實時編碼

        if (avcodec_open2(pH264CodecCtx, pH264Codec,&param) < 0){
          printf("Failed to open video encoder1! 編碼器開啟失敗!\n");
          return false;
        }

------------------------------------------------分割線------------------------------------------------------

然後再獲取螢幕視訊流後用H264編碼

sws_scale(img_convert_ctx,(constuint8_t*const*)pFrame->data,pFrame->linesize,0,pCodecCtx->height,pFrameYUV->data,pFrameYUV->linesize);
inty_size=pCodecCtx->width
*pCodecCtx->height;
fwrite(pFrameYUV->data[0],1,y_size,fp_yuv);//Y
fwrite(pFrameYUV->data[1],1,y_size/4,fp_yuv);//U
fwrite(pFrameYUV->data[2],1,y_size/4,fp_yuv);//V
//編碼成h264檔案
intret1=avcodec_encode_video2(pH264CodecCtx,packet,pFrameYUV,&got_picture);
if(ret1<0){
printf("Failedtoencode!\n");
return-1;
}
ret1=fwrite(packet->data,1,packet->size,fp_h264);
if(0>ret1)
{
printf("writeintooutput.h264fail\n");
}

--------------------------------------------以下為完整程式碼---------------------------------------

#include<stdio.h>
extern"C"
{
#include"libavcodec/avcodec.h"
#include"libavformat/avformat.h"
#include"libswscale/swscale.h"
#include"libavdevice/avdevice.h"
}
//'1'UseDshow
//'0'UseVFW
#defineUSE_DSHOW0
//ShowDshowDevice
voidshow_dshow_device()
{
AVFormatContext*pFormatCtx=avformat_alloc_context();
AVDictionary*options=NULL;
av_dict_set(&options,"list_devices","true",0);
AVInputFormat*iformat=av_find_input_format("dshow");
printf("========DeviceInfo=============\n");
avformat_open_input(&pFormatCtx,"video=dummy",iformat,&options);
printf("================================\n");
}
//ShowDshowDeviceOption
voidshow_dshow_device_option()
{
AVFormatContext*pFormatCtx=avformat_alloc_context();
AVDictionary*options=NULL;
av_dict_set(&options,"list_options","true",0);
AVInputFormat*iformat=av_find_input_format("dshow");
printf("========DeviceOptionInfo======\n");
avformat_open_input(&pFormatCtx,"video=IntegratedCamera",iformat,&options);
printf("================================\n");
}
//ShowVFWDevice
voidshow_vfw_device()
{
AVFormatContext*pFormatCtx=avformat_alloc_context();
AVInputFormat*iformat=av_find_input_format("vfwcap");
printf("========VFWDeviceInfo======\n");
avformat_open_input(&pFormatCtx,"list",iformat,NULL);
printf("=============================\n");
}
//ShowAVFoundationDevice
voidshow_avfoundation_device()
{
AVFormatContext*pFormatCtx=avformat_alloc_context();
AVDictionary*options=NULL;
av_dict_set(&options,"list_devices","true",0);
AVInputFormat*iformat=av_find_input_format("avfoundation");
printf("==AVFoundationDeviceInfo===\n");
avformat_open_input(&pFormatCtx,"",iformat,&options);
printf("=============================\n");
}
#defineUSE_DSHOW1
intmain(intargc,char*argv[])
{
AVFormatContext*pFormatCtx;
inti,videoindex;
AVCodecContext*pCodecCtx;
AVCodec*pCodec;
AVCodecContext*pH264CodecCtx;
AVCodec*pH264Codec;
av_register_all();
avformat_network_init();
avdevice_register_all();//RegisterDevice
pFormatCtx=avformat_alloc_context();
//抓取螢幕
AVInputFormat*ifmt=av_find_input_format("gdigrab");
if(avformat_open_input(&pFormatCtx,"desktop",ifmt,NULL)!=0){
printf("Couldn'topeninputstream.");
return-1;
}
if(avformat_find_stream_info(pFormatCtx,NULL)<0)
{
printf("Couldn'tfindstreaminformation.\n");
return-1;
}
videoindex=-1;
for(i=0;i<pFormatCtx->nb_streams;i++)
{
if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO)
{
videoindex=i;
}
}
if(videoindex==-1)
{
printf("Couldn'tfindavideostream.\n");
return-1;
}
//根據視訊中的流開啟選擇解碼器
pCodecCtx=pFormatCtx->streams[videoindex]->codec;
pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
if(pCodec==NULL)
{
printf("Codecnotfound.\n");
return-1;
}
//開啟解碼器
if(avcodec_open2(pCodecCtx,pCodec,NULL)<0)
{
printf("Couldnotopencodec.\n");
return-1;
}
AVFrame*pFrame,*pFrameYUV;
pFrame=av_frame_alloc();
pFrameYUV=av_frame_alloc();
uint8_t*out_buffer=(uint8_t*)av_malloc(avpicture_get_size(PIX_FMT_YUV420P,pCodecCtx->width,pCodecCtx->height));
avpicture_fill((AVPicture*)pFrameYUV,out_buffer,PIX_FMT_YUV420P,pCodecCtx->width,pCodecCtx->height);
intret,got_picture;
AVPacket*packet=(AVPacket*)av_malloc(sizeof(AVPacket));
AVPacket*packetH264=(AVPacket*)av_malloc(sizeof(AVPacket));
FILE*fp_yuv=fopen("output.yuv","wb");
FILE*fp_h264=fopen("output.h264","wb");
structSwsContext*img_convert_ctx;
img_convert_ctx=sws_getContext(pCodecCtx->width,pCodecCtx->height,pCodecCtx->pix_fmt,pCodecCtx->width,pCodecCtx->height,PIX_FMT_YUV420P,SWS_BICUBIC,NULL,NULL,NULL);
///這裡打印出視訊的寬高
fprintf(stderr,"w=%dh=%d\n",pCodecCtx->width,pCodecCtx->height);
///我們就讀取100張影象
//查詢h264編碼器
pH264Codec=avcodec_find_encoder(AV_CODEC_ID_H264);
if(!pH264Codec)
{
fprintf(stderr,"---------h264codecnotfound----\n");
exit(1);
}
pH264CodecCtx=avcodec_alloc_context3(pH264Codec);
pH264CodecCtx->codec_id=AV_CODEC_ID_H264;
pH264CodecCtx->codec_type=AVMEDIA_TYPE_VIDEO;
pH264CodecCtx->pix_fmt=PIX_FMT_YUV420P;
pH264CodecCtx->width=pCodecCtx->width;
pH264CodecCtx->height=pCodecCtx->height;
pH264CodecCtx->time_base.num=1;
pH264CodecCtx->time_base.den=15;//幀率(既一秒鐘多少張圖片)
pH264CodecCtx->bit_rate=400000;//位元率(調節這個大小可以改變編碼後視訊的質量)
pH264CodecCtx->gop_size=12;
//H264還可以設定很多引數自行研究吧
pH264CodecCtx->qmin=10;
pH264CodecCtx->qmax=51;
//someformatswantstreamheaderstobeseparate
if(pH264CodecCtx->flags&AVFMT_GLOBALHEADER)
pH264CodecCtx->flags|=CODEC_FLAG_GLOBAL_HEADER;
//SetOption
AVDictionary*param=0;
//H.264
av_dict_set(&param,"preset","superfast",0);
av_dict_set(&param,"tune","zerolatency",0);//實現實時編碼
if(avcodec_open2(pH264CodecCtx,pH264Codec,&param)<0){
printf("Failedtoopenvideoencoder1!編碼器開啟失敗!\n");
returnfalse;
}
for(inti=0;i<100;i++)
{
//讀取截圖中的資料--->packet
if(av_read_frame(pFormatCtx,packet)<0)
{
break;
}
if(packet->stream_index==videoindex)
{
ret=avcodec_decode_video2(pCodecCtx,pFrame,&got_picture,packet);
if(ret<0){
printf("DecodeError.\n");
return-1;
}
if(got_picture)
{
sws_scale(img_convert_ctx,(constuint8_t*const*)pFrame->data,pFrame->linesize,0,pCodecCtx->height,pFrameYUV->data,pFrameYUV->linesize);
inty_size=pCodecCtx->width*pCodecCtx->height;
fwrite(pFrameYUV->data[0],1,y_size,fp_yuv);//Y
fwrite(pFrameYUV->data[1],1,y_size/4,fp_yuv);//U
fwrite(pFrameYUV->data[2],1,y_size/4,fp_yuv);//V
//編碼成h264檔案
intret1=avcodec_encode_video2(pH264CodecCtx,packet,pFrameYUV,&got_picture);
if(ret1<0){
printf("Failedtoencode!\n");
return-1;
}
ret1=fwrite(packet->data,1,packet->size,fp_h264);
if(0>ret1)
{
printf("writeintooutput.h264fail\n");
}
}
}
av_free_packet(packet);
}
sws_freeContext(img_convert_ctx);
fclose(fp_yuv);
av_free(out_buffer);
av_free(pFrameYUV);
avcodec_close(pCodecCtx);
avformat_close_input(&pFormatCtx);
return0;
}