看了雷神关于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.6
DWORD 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);
//PTS
picture->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 Encoder
int 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

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

  1. ffmpeg,rtmpdump和nginx rtmp实现录屏,直播和录制

    ffmpeg,rtmpdump和nginx rtmp实现录屏,直播和录制 2014年 四月 19日 周六 | tags: ffmpeg, rtmp, rtmpdump, nginx, -- (perm ...

  2. 苹果屏幕录制5831_苹果录屏功能在哪?教你轻松开启iPhone录屏

    苹果录屏功能在哪?很多刚使用苹果手机的小伙伴都不知道苹果手机里的录屏功能怎么使用,今天小编就给大家分享一下苹果手机的录屏功能. 详细操作步骤: 1.打开苹果手机的设置界面 2.下拉找到控制中心的选项进 ...

  3. 电脑上如何进行屏幕录制,笔记本电脑录屏怎么录

    电脑上如何进行屏幕录制?如果你在日常工作娱乐中有录屏的需求,并且想进一步了解电脑录屏的相关操作步骤,请认真看以下内容!小编会给大家分享几种简单的电脑录屏方法. 方法一:PPT屏幕录制 PPT想必大家都 ...

  4. 苹果手机、电脑如何进行屏幕录制?苹果录屏功能在哪?

    随着人们生活水平的提高,不少小伙伴都会选择苹果手机.苹果电脑作为主要的设备.因为使用苹果电脑进行办公,不仅仅能够提升效率,对于文件的安全性也是有一些保障的.那么,在使用苹果电脑的时候,如果需要有录屏的 ...

  5. C# 用鼠标框选屏幕一个范围进行录屏

    实现方案基于OMCS+MFile构建,网上有很多的案例,但是很少有用鼠标框选范围的来进行录制的, 主要的录屏方法如下图 , 传入的参数是录制屏幕的矩形大小 接下来主要的就是获取在屏幕中鼠标框选的范围, ...

  6. android 屏幕录制方案,Android录屏的三种解决方案

    本文总结三种用于安卓录屏的解决方案: adb shell命令screenrecord MediaRecorder, MediaProjection MediaProjection , MediaCod ...

  7. android 屏幕录制方案,Android录屏的三种方案

    本文总结三种用于安卓录屏的解决方案: adb shell命令screenrecord MediaRecorder, MediaProjection MediaProjection , MediaCod ...

  8. adams2015怎么把工具栏打开_怎么在电脑上进行屏幕录像?电脑录屏的方法

    以下方法针对WIN10操作系统,步骤如下: 1,点击开始菜单,选择所有应用.找到Xbox应用,点击打开. 2,打开Xbox应用后,找到左边工具栏的第四项,点击一下(会提示Win + G). 3,按键盘 ...

  9. Android利用ffmpeg压缩视频

    因为整个ffmpeg是很大的,我这边只需要对mp4格式的视频进行压缩,具体情况是拍摄10秒的视频,给它命名的时候就设置成mp4的格式.我用的ffmpeg是3.0版本,生成的so库比原来的小很多,具体怎 ...

最新文章

  1. python比c语言开发速度快多少倍_Python语言其实很慢,为什么机器学习这种快速算法步骤通常还是用呢?...
  2. 最小割 ---- 集合冲突模型 ---- AGC038 F - Two Permutations[详解]
  3. 微生物所高程-郭良栋组(内附招聘)在菌根适应策略研究中取得进展
  4. fusion 360安装程序的多个实例正在同时运行。_SpringMVC运行原理
  5. Android之解决在scrollview中嵌套ListView切换界面时scrollview整体向下滑动
  6. 谈谈Koa 中的next
  7. hbase删除表失败的解决方法
  8. java 矩阵题目_java练习本(原每日一练)(20190517)
  9. c datatable导入mysql_《项目经验》–简单三层使用DataTable向数据库表批量导入数据—向SqlServer一张表中导入数据 | 学步园...
  10. Scheduler:Event UID not valid(转)
  11. 开发人员生产力指南,细节决定成败!
  12. Linux下编译redis及配置
  13. Vue3.0中文地址文档
  14. vue axios轮询更新echarts 页面崩溃问题
  15. win10单机修复计算机在哪,win10如何进入高级修复选项
  16. (域名、主机名、服务名、端口号)名字与地址的转换 (gethostbyname、getservbyname、getaddrinfo、getnameinfo等)
  17. H12-821题库详解
  18. [HAOI2010]软件安装 [Tarjan + 树形DP]
  19. SQL:数据去重的三种方法
  20. bitdefender比特梵德全方位安全杀毒软件-----完整保护您的计算机

热门文章

  1. 蓝海创意云丨产品日志:安捷秀(Agileshot)4.0 版本全新上线
  2. word文档添加目录
  3. 旋转变换(一)旋转矩阵
  4. C#编辑、打印Excel文件不依赖Office
  5. 【计算机网络】第七章:[网络层]网络协议(Part1.网络层首部)
  6. 运营方案要包括哪些内容_一份完整的运营方案应该包括哪些方面?
  7. 刘奇-豌豆荚分布式redis的设计与实现
  8. 百度网盘 for Mac官方版哪里下?来未来软件园
  9. 企业级SSD产品对比
  10. uni-app开发中,使用ThorUI中的图片上传,自定义接口返回数据修改