利用ffmpeg压缩屏幕图像为avi(录屏、压制)

来源:互联网 发布:linux小红帽镜像 编辑:程序博客网 时间:2024/05/21 11:10

看了雷神关于ffmpeg方面的文章,结合自己最近做的一个基与cdc抓屏、vfw压制的录屏工具,就想改为采用ffmpeg来压制avi。

关于如何用ffmpeg压制视频相信大家都有一些见解,这里写这篇文章最核心的东西是如何把采集的来的LPBITMAPINFOHEADER图像数据转换为ffmpeg所需要的AVFrame数据。

核心思想是利用sws_scale把图像从PIX_FMT_RGB32格式转换为PIX_FMT_YUV420P格式,下面是详细代码。

LPBITMAPINFOHEADER bmp;memset(buffer, 0, numBytes);uint8_t* tmpBuf = (uint8_t*)(bmp + 1);for (int i = 0; i < pCodecCtx->height;i++)memcpy(buffer + i * (pCodecCtx->width * 4), (tmpBuf + (pCodecCtx->width*4)*(pCodecCtx->height - i - 1)), (pCodecCtx->width * 4));int ret1 = avpicture_fill((AVPicture *)pFrame, buffer, PIX_FMT_RGB32, pCodecCtx->width, pCodecCtx->height);SwsContext *img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, PIX_FMT_RGB32, pCodecCtx->width, pCodecCtx->height, PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, picture->data, picture->linesize);

这里LPBITMAPINFOHEADER bmp;是通过采集得到的bmp图像数据。

for (int i = 0; i < pCodecCtx->height;i++)memcpy(buffer + i * (pCodecCtx->width * 4), (tmpBuf + (pCodecCtx->width*4)*(pCodecCtx->height - i - 1)), (pCodecCtx->width * 4));

这句代码主要是把图像数据上下颠倒过来,因为bmp里面数据是上下颠倒的。

int ret1 = avpicture_fill((AVPicture *)pFrame, buffer, PIX_FMT_RGB32, pCodecCtx->width, pCodecCtx->height);SwsContext *img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, PIX_FMT_RGB32, pCodecCtx->width, pCodecCtx->height, PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, picture->data, picture->linesize);

这三句就是整个代码的核心了,

先用avpicture_fill把buffer里面的图像数据填充到AVFrame *pFrame;里面去。

然后利用sws_getContext得到SwsContext *img_convert_ctx;

然后用得到的img_convert_ctx参数利用sws_sacle把pFrame数据转到AVFrame* picture;里面去,此时picture里面的数据就已经是满足编码的PIX_FMT_YUV420P格式了。

用avcodec_encode_video2(pCodecCtx, &pkt,picture, &got_picture);得到编码后的数据。

通过av_write_frame(pFormatCtx, &pkt);把编码后的数据写到文件中去。

至此一帧的采集、编码、写入就完成了。


下面把几个核心函数贴上已做备忘。

屏幕采集函数:

LPBITMAPINFOHEADER CScreenCaptureDlg::captureScreenFrame( int left,int top,int width, int height,int tempDisableRect ){HDC hScreenDC = CreateDC("DISPLAY", NULL, NULL, NULL);HDC hMemDC = ::CreateCompatibleDC(hScreenDC);     HBITMAP hbm;hbm = CreateCompatibleBitmap(hScreenDC, width, height);HBITMAP oldbm = (HBITMAP) SelectObject(hMemDC, hbm);//BitBlt(hMemDC, 0, 0, width, height, hScreenDC, left, top, SRCCOPY);//ver 1.6DWORD bltFlags = SRCCOPY;//bltFlags |= CAPTUREBLT;BitBlt(hMemDC, 0, 0, width, height, hScreenDC, left, top, SRCCOPY|CAPTUREBLT);SelectObject(hMemDC,oldbm);LPBITMAPINFOHEADER pBM_HEADER = (LPBITMAPINFOHEADER)GlobalLock(Bitmap2Dib(hbm, 32));//LPBITMAPINFOHEADER pBM_HEADER = (LPBITMAPINFOHEADER)GlobalLock(Bitmap2Dib(hbm, 24));if (pBM_HEADER == NULL) { return NULL;}    DeleteObject(hbm);DeleteDC(hMemDC);::ReleaseDC(NULL,hScreenDC);return pBM_HEADER;}HANDLE CScreenCaptureDlg::Bitmap2Dib( HBITMAP hbitmap, UINT bits ){HANDLE              hdib = NULL ;HDC                 hdc ;BITMAP              bitmap ;UINT                wLineLen ;DWORD               dwSize ;DWORD               wColSize ;LPBITMAPINFOHEADER  lpbi ;LPBYTE              lpBits ;GetObject(hbitmap,sizeof(BITMAP),&bitmap) ;//// DWORD align the width of the DIB// Figure out the size of the colour table// Calculate the size of the DIB//wLineLen = (bitmap.bmWidth*bits+(bits - 1))/(bits) * (bits/8);wColSize = sizeof(RGBQUAD)*((bits <= 8) ? 1<<bits : 0);dwSize = sizeof(BITMAPINFOHEADER) + wColSize +(DWORD)(UINT)wLineLen*(DWORD)(UINT)bitmap.bmHeight;//// Allocate room for a DIB and set the LPBI fields//hdib = GlobalAlloc(GHND,dwSize);if (!hdib)return hdib ;lpbi = (LPBITMAPINFOHEADER)GlobalLock(hdib) ;lpbi->biSize = sizeof(BITMAPINFOHEADER) ;lpbi->biWidth = bitmap.bmWidth ;lpbi->biHeight = bitmap.bmHeight ;lpbi->biPlanes = 1 ;lpbi->biBitCount = (WORD) bits ;lpbi->biCompression = BI_RGB ;lpbi->biSizeImage = dwSize - sizeof(BITMAPINFOHEADER) - wColSize ;lpbi->biXPelsPerMeter = 0 ;lpbi->biYPelsPerMeter = 0 ;lpbi->biClrUsed = (bits <= 8) ? 1<<bits : 0;lpbi->biClrImportant = 0 ;//// Get the bits from the bitmap and stuff them after the LPBI//lpBits = (LPBYTE)(lpbi+1)+wColSize ;hdc = CreateCompatibleDC(NULL) ;GetDIBits(hdc,hbitmap,0,bitmap.bmHeight,lpBits,(LPBITMAPINFO)lpbi, DIB_RGB_COLORS);lpbi->biClrUsed = (bits <= 8) ? 1<<bits : 0;DeleteDC(hdc) ;GlobalUnlock(hdib);return hdib ;}初始化BOOL CScreenCaptureDlg::InitOutFile(){av_register_all();AVOutputFormat* fmt;AVCodec* pCodec;const char* out_file = "src01.avi";pFormatCtx = avformat_alloc_context();fmt = av_guess_format(NULL, out_file, NULL);pFormatCtx->oformat = fmt;//注意输出路径if (avio_open(&pFormatCtx->pb,out_file, AVIO_FLAG_READ_WRITE) < 0){MessageBox("输出文件打开失败");return FALSE;}video_st = av_new_stream(pFormatCtx, 0);if (video_st==NULL){return FALSE;}pCodecCtx = video_st->codec;pCodecCtx->codec_id = CODEC_ID_MPEG4;//pCodecCtx->vcodec = ;pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;pCodecCtx->pix_fmt = PIX_FMT_YUV420P;//AV_PIX_FMT_RGB24;pCodecCtx->width = m_width;  pCodecCtx->height = m_height;pCodecCtx->time_base.num = 1;  pCodecCtx->time_base.den = 8;  //   pCodecCtx->bit_rate = 3000000;// pCodecCtx->global_quality = 300;// pCodecCtx->gop_size=80;// pCodecCtx->qmin = 10;// pCodecCtx->qmax = 51;//输出格式信息av_dump_format(pFormatCtx, 0, out_file, 1);pCodec = avcodec_find_encoder(CODEC_ID_MPEG4);if (!pCodec){MessageBox("没有找到合适的编码器!\n");return FALSE;}if (avcodec_open2(pCodecCtx, pCodec,NULL) < 0){MessageBox("编码器打开失败!\n");return FALSE;}//写文件头avformat_write_header(pFormatCtx,NULL);int size;picture = avcodec_alloc_frame();size = avpicture_get_size(pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);picture_buf = new uint8_t[size];avpicture_fill((AVPicture *)picture, picture_buf, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);pFrame=avcodec_alloc_frame();numBytes=avpicture_get_size(PIX_FMT_RGB32, pCodecCtx->width,pCodecCtx->height);   buffer = new uint8_t[numBytes];int y_size = pCodecCtx->width * pCodecCtx->height;av_new_packet(&pkt,y_size*3);//video_outbuf= (unsigned char *) malloc(y_size*3);}写入一帧:BOOL CScreenCaptureDlg::WriteFrame(LPBITMAPINFOHEADER bmp, int pts){memset(buffer, 0, numBytes);uint8_t* tmpBuf = (uint8_t*)(bmp + 1);for (int i = 0; i < pCodecCtx->height;i++)memcpy(buffer + i * (pCodecCtx->width * 4), (tmpBuf + (pCodecCtx->width*4)*(pCodecCtx->height - i - 1)), (pCodecCtx->width * 4));int ret1 = avpicture_fill((AVPicture *)pFrame, buffer, PIX_FMT_RGB32, pCodecCtx->width, pCodecCtx->height);SwsContext *img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, PIX_FMT_RGB32, pCodecCtx->width, pCodecCtx->height, PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, picture->data, picture->linesize);//PTSpicture->pts=pts;int got_picture=0;int ret = avcodec_encode_video2(pCodecCtx, &pkt,picture, &got_picture);if(ret < 0){MessageBox("编码错误!\n");return -1;}if (got_picture==1){pkt.stream_index = video_st->index;ret = av_write_frame(pFormatCtx, &pkt);av_free_packet(&pkt);}return TRUE;}收尾工作:BOOL CScreenCaptureDlg::CloseCaptureFile(){// free(video_outbuf);//Flush Encoderint ret = flush_encoder(pFormatCtx,0);if (ret < 0) {printf("Flushing encoder failed\n");return -1;}//写文件尾av_write_trailer(pFormatCtx);//清理if (video_st){avcodec_close(video_st->codec);}avio_close(pFormatCtx->pb);avformat_free_context(pFormatCtx);av_free(picture);delete[] picture_buf;av_free(pFrame);delete[] buffer;}//如果不加这个函数极有可能少帧int flush_encoder(AVFormatContext *fmt_ctx,unsigned int stream_index){int ret;int got_frame;AVPacket enc_pkt;if (!(fmt_ctx->streams[stream_index]->codec->codec->capabilities &CODEC_CAP_DELAY))return 0;while (1) {printf("Flushing stream #%u encoder\n", stream_index);//ret = encode_write_frame(NULL, stream_index, &got_frame);enc_pkt.data = NULL;enc_pkt.size = 0;av_init_packet(&enc_pkt);ret = avcodec_encode_video2 (fmt_ctx->streams[stream_index]->codec, &enc_pkt,NULL, &got_frame);av_frame_free(NULL);if (ret < 0)break;if (!got_frame){ret=0;break;}printf("编码成功1帧!\n");/* mux encoded frame */ret = av_write_frame(fmt_ctx, &enc_pkt);if (ret < 0)break;}return ret;}差点把这些搞忘了:extern "C"{#include "libavcodec\avcodec.h"#include "libavformat\avformat.h"#include "libswscale\swscale.h"}extern "C"{#pragma comment (lib, "avcodec.lib")#pragma comment (lib, "avformat.lib")#pragma comment (lib, "avutil.lib")#pragma comment (lib, "avdevice.lib")#pragma comment (lib, "avfilter.lib")#pragma comment (lib, "postproc.lib")#pragma comment (lib, "swresample.lib")#pragma comment (lib, "swscale.lib")};



工程下载地址:http://download.csdn.net/detail/dancing_night/8671911

0 0