本人之前写了一个博客:ffmpeg录制桌面(自己用gdi抓图)
当时设置的帧率是10,设置的比较低,原因在于,设置高了,1秒钟抓不到对应的图片数量,会导致最终生成文件播放起来,时间变短。比如设置帧率为20,但是由于电脑的分辨率比较高,一秒钟只能抓10帧。则录制一分钟时,会发现,最终播放起来只有30秒。这显然是有问题的。

当然,我们设置帧率为10也不安全,因为电脑由于各种原因,可能一秒钟也抓不了10帧,当然,我们也不能再将帧率往下设置了,太憋屈了。

此时修改frame->pkt_dts可以达到这种效果,比如写文件时,AVStream的时间基是1000,帧率设置为20.
第一帧的pkt_dts设置为0,则第二帧正常应该为50,第三帧为100,…,第20帧的pkt_dts为950,第21帧的pkt_dts为1000.
但是现在出问题了,电脑性能不够,一秒钟只能抓10张图片,则第一帧的pkt_dts设置为0,第二张的设置为100,这样才能保证最后生成的文件播放起来是ok的;但是这样有个不好的结果,就是最终显示的文件的帧率只有10,而不是设置的20。

所以本篇采取在设置帧率的前提下,尽力交付。比如设置帧率为10时,则如果实际能抓到的张数大于10,则进行相应的Sleep;如果实际能抓到的张数小于10,比如8.则dts设置正确即可,最终生成的文件的帧率是8.

代码结构如下:

CaptureScreen.cpp的内容如下:

//#include "stdafx.h"
#include "CaptureScreen.h"CCaptureScreen::CCaptureScreen(void)
{m_hdib = NULL;m_hSavedCursor = NULL;hScreenDC = NULL;hMemDC = NULL;hbm = NULL;m_width = 1920;m_height = 1080;FetchCursorHandle();
}
//
// 释放资源
//
CCaptureScreen::~CCaptureScreen(void)
{if (hbm){DeleteObject(hbm);hbm = NULL;}if (m_hdib){free(m_hdib);m_hdib = NULL;}if (hScreenDC){::ReleaseDC(NULL, hScreenDC);}if (hMemDC) {DeleteDC(hMemDC);}
}//
// 初始化
//
int CCaptureScreen::Init(int iPosX, int iPosY, int iWidth, int iHeight)
{HWND hDeskTop = GetDesktopWindow();RECT rc;GetWindowRect(hDeskTop, &rc);hScreenDC = ::GetDC(NULL);int iSizeX = GetDeviceCaps(hScreenDC, HORZRES);if (hScreenDC == NULL) return 0;hMemDC = ::CreateCompatibleDC(NULL);if (hMemDC == NULL) return 0;m_iXPos = iPosX;m_iYPos = iPosY;m_width = iWidth;m_height = iHeight;if (!m_hdib){m_hdib = (PRGBTRIPLE)malloc(m_width * m_height * 3);//24位图像大小}//位图头信息结构体pbi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);pbi.bmiHeader.biWidth = m_width;pbi.bmiHeader.biHeight = m_height;pbi.bmiHeader.biPlanes = 1;pbi.bmiHeader.biBitCount = 24;pbi.bmiHeader.biCompression = BI_RGB;hbm = CreateCompatibleBitmap(hScreenDC, m_width, m_height);SelectObject(hMemDC, hbm);wLineLen = ((m_width * 24 + 31) & 0xffffffe0) / 8;wColSize = sizeof(RGBQUAD)* ((24 <= 8) ? 1 << 24 : 0);dwSize = (DWORD)(UINT)wLineLen * (DWORD)(UINT)m_height;return 1;
}//抓取屏幕数据
BYTE* CCaptureScreen::CaptureImage()
{VOID*  alpbi = CaptureScreenFrame(m_iXPos, m_iYPos, m_width, m_height);return (BYTE*)(alpbi);
}void* CCaptureScreen::CaptureScreenFrame(int left, int top, int width, int height)
{if (hbm == NULL || hMemDC == NULL || hScreenDC == NULL) return NULL;//BitBlt(hMemDC, 0, 0, width, height, hScreenDC, left, top, SRCCOPY);BOOL bRet = StretchBlt(hMemDC, 0, 0, width, height, hScreenDC, left, top, width, height, SRCCOPY);/*-------------------------捕获鼠标-------------------------------*/{POINT xPoint;GetCursorPos(&xPoint);HCURSOR hcur = FetchCursorHandle();xPoint.x -= left;xPoint.y -= top;ICONINFO iconinfo;BOOL ret;ret = GetIconInfo(hcur, &iconinfo);if (ret){xPoint.x -= iconinfo.xHotspot;xPoint.y -= iconinfo.yHotspot;if (iconinfo.hbmMask) DeleteObject(iconinfo.hbmMask);if (iconinfo.hbmColor) DeleteObject(iconinfo.hbmColor);}/*画鼠标*/::DrawIcon(hMemDC, xPoint.x, xPoint.y, hcur);}//动态分配的内存PRGBTRIPLE hdib = m_hdib;if (!hdib)return hdib;GetDIBits(hMemDC, hbm, 0, m_height, hdib, (LPBITMAPINFO)&pbi, DIB_RGB_COLORS);return hdib;
}//
// 获取窗体鼠标光标
//
HCURSOR CCaptureScreen::FetchCursorHandle()
{if (m_hSavedCursor == NULL){m_hSavedCursor = GetCursor();}return m_hSavedCursor;
}

CaptureScreen.h的代码如下:

#ifndef _CCAPTURE_SCREEN_HH
#define _CCAPTURE_SCREEN_HH#include<time.h>
#include <d3d9.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <windows.h>#include <tchar.h>
#include <winbase.h>
#include <winreg.h>
#include <Strsafe.h>//
// ---抓屏类----
//
class CCaptureScreen
{
public:CCaptureScreen(void);~CCaptureScreen(void);public:/*-----------定义外部调用函数-----------*/int Init(int iPosX, int iPosY, int iWidth, int iHeight);//初始化BYTE* CaptureImage(); //抓取屏幕private:/*-----------定义内部调用函数-----------*/void* CaptureScreenFrame(int, int, int, int);//抓屏HCURSOR FetchCursorHandle(); //获取鼠标光标private:/*-----------定义私有变量-----------*/int m_iXPos;int m_iYPos;int m_width;int m_height;UINT   wLineLen;DWORD  dwSize;DWORD  wColSize;//设备句柄HDC hScreenDC;HDC hMemDC;//图像RGB内存缓存PRGBTRIPLE m_hdib;//位图头信息结构体BITMAPINFO pbi;HBITMAP hbm = NULL;//鼠标光标HCURSOR m_hSavedCursor;};#endif //--_CCAPTURE_SCREEN_HH

FfmpegVideoCaptureWithGdi.cpp的代码如下:

// RecordingScreen.cpp : 定义控制台应用程序的入口点。
////#include "stdafx.h"
#include "CaptureScreen.h"
#include <string>extern "C"
{
#include <libavutil/opt.h>
#include <libavutil/mathematics.h>
#include <libavutil\time.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libswresample/swresample.h>
#include <libavutil/imgutils.h>
#include <libavcodec/avcodec.h>
#include <libavdevice\avdevice.h>#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")}int g_iWidth = 2880;
int g_iHeight = 1800;
int g_iFrameRate = 30;
//#include <ipp.h>
#include <chrono>std::string time_tSimpleString(time_t t_time)
{tm temptm = *localtime(&t_time);char strOut[64] = { 0 };sprintf(strOut, "%04d-%02d-%02dT%02d-%02d-%02d", temptm.tm_year + 1900, temptm.tm_mon + 1,temptm.tm_mday, temptm.tm_hour, temptm.tm_min, temptm.tm_sec);return (std::string)strOut;
}unsigned char clip_value(unsigned char x, unsigned char min_val, unsigned char  max_val) {if (x > max_val) {return max_val;}else if (x < min_val) {return min_val;}else {return x;}
}//RGB to YUV420
bool RGB24_TO_YUV420(unsigned char *RgbBuf, int w, int h, unsigned char *yuvBuf)
{unsigned char*ptrY, *ptrU, *ptrV, *ptrRGB;memset(yuvBuf, 0, w*h * 3 / 2);ptrY = yuvBuf;ptrU = yuvBuf + w * h;ptrV = ptrU + (w*h * 1 / 4);unsigned char y, u, v, r, g, b;for (int j = h - 1; j >= 0; j--) {ptrRGB = RgbBuf + w * j * 3;for (int i = 0; i < w; i++) {b = *(ptrRGB++);g = *(ptrRGB++);r = *(ptrRGB++);y = (unsigned char)((66 * r + 129 * g + 25 * b + 128) >> 8) + 16;u = (unsigned char)((-38 * r - 74 * g + 112 * b + 128) >> 8) + 128;v = (unsigned char)((112 * r - 94 * g - 18 * b + 128) >> 8) + 128;*(ptrY++) = clip_value(y, 0, 255);if (j % 2 == 0 && i % 2 == 0) {*(ptrU++) = clip_value(u, 0, 255);}else {if (i % 2 == 0) {*(ptrV++) = clip_value(v, 0, 255);}}}}return true;
}DWORD WINAPI ScreenCapThreadProc(LPVOID lpParam)
{CCaptureScreen* ccs = new CCaptureScreen();//int width = GetSystemMetrics(SM_CXSCREEN);//int height = GetSystemMetrics(SM_CYSCREEN);int width = g_iWidth;int height = g_iHeight;ccs->Init(0, 0, width, height);AVFormatContext* avFormCtx_Out;AVCodecContext*  avCodecCtx_Out;AVCodec*  avCodec;AVStream* avStream;AVFrame* frame;AVPacket* packet;int ret = 0;std::string strFileName = "D:\\learn\\ffmpeg\\FfmpegTest\\x64\\Release\\";strFileName += time_tSimpleString(time(NULL));strFileName += ".mp4";const char* filename = strFileName.c_str();ret = avformat_alloc_output_context2(&avFormCtx_Out, NULL, NULL, filename);if (ret < 0){printf("Init avformat object is faild! \n");return 0;}avCodec = (AVCodec *)avcodec_find_encoder(avFormCtx_Out->oformat->video_codec);if (!avCodec){printf("Init avCodec object is faild! \n");return 0;}avCodecCtx_Out = avcodec_alloc_context3(avCodec);if (!avCodecCtx_Out){printf("Init avCodecCtx_Out object is faild! \n");return 0;}avStream = avformat_new_stream(avFormCtx_Out, avCodec);if (!avStream){printf("Init avStream object is faild! \n");return 0;}avCodecCtx_Out->flags |= AV_CODEC_FLAG_QSCALE;avCodecCtx_Out->bit_rate = 4000000;avCodecCtx_Out->rc_min_rate = 4000000;avCodecCtx_Out->rc_max_rate = 4000000;avCodecCtx_Out->bit_rate_tolerance = 4000000;avCodecCtx_Out->time_base.den = g_iFrameRate;avCodecCtx_Out->time_base.num = 1;avCodecCtx_Out->width = width;avCodecCtx_Out->height = height;//pH264Encoder->pCodecCtx->frame_number = 1;avCodecCtx_Out->gop_size = 12;avCodecCtx_Out->max_b_frames = 0;avCodecCtx_Out->thread_count = 4;avCodecCtx_Out->pix_fmt = AV_PIX_FMT_YUV420P;avCodecCtx_Out->codec_id = AV_CODEC_ID_H264;avCodecCtx_Out->codec_type = AVMEDIA_TYPE_VIDEO;av_opt_set(avCodecCtx_Out->priv_data, "b-pyramid", "none", 0);av_opt_set(avCodecCtx_Out->priv_data, "preset", "superfast", 0);av_opt_set(avCodecCtx_Out->priv_data, "tune", "zerolatency", 0);if (avFormCtx_Out->oformat->flags & AVFMT_GLOBALHEADER)avCodecCtx_Out->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;ret = avcodec_open2(avCodecCtx_Out, avCodec, NULL);if (ret < 0){printf("Open avcodec is faild! \n");return 0;}avcodec_parameters_from_context(avStream->codecpar, avCodecCtx_Out);if (!(avFormCtx_Out->oformat->flags & AVFMT_NOFILE)){ret = avio_open(&avFormCtx_Out->pb, filename, AVIO_FLAG_WRITE);if (ret < 0){printf("Open file is faild! \n");return 0;}}ret = avformat_write_header(avFormCtx_Out, NULL);if (ret < 0){printf("write header is faild! \n");return 0;}frame = av_frame_alloc();if (!frame){printf("Init frame is faild! \n");return 0;}frame->format = AV_PIX_FMT_YUV420P;frame->width = width;frame->height = height;LONG64 frameSize = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, width, height, 1);BYTE* outbuffer = new BYTE[frameSize];ret = av_image_fill_arrays(frame->data,frame->linesize,outbuffer,AV_PIX_FMT_YUV420P,width,height, 1);if (ret < 0){printf("av_image_fill_arrays is faild! \n");return 0;}packet = av_packet_alloc();//av_init_packet(packet);if (!packet){printf("packet is faild! \n");return 0;}int frameNumber = 0;int got_packet = 0;DWORD dwBeginTime = ::GetTickCount();for (;;){BYTE* frameimage = ccs->CaptureImage();RGB24_TO_YUV420(frameimage, width, height, outbuffer);DWORD dwCurrTime = ::GetTickCount();int dts = dwCurrTime - dwBeginTime;AVRational millBase;millBase.den = 1000;millBase.num = 1;frame->pkt_dts = frame->pts = av_rescale_q_rnd(dts, millBase, avStream->time_base, (AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));frame->pkt_duration = 0;frame->pkt_pos = -1;ret = avcodec_send_frame(avCodecCtx_Out, frame);if (ret < 0)continue;ret = avcodec_receive_packet(avCodecCtx_Out, packet);if (ret < 0)continue;static DWORD dwInitTime = ::GetTickCount();if (packet->size > 0){//av_packet_rescale_ts(packet, avCodecCtx_Out->time_base, avStream->time_base);av_write_frame(avFormCtx_Out, packet);frameNumber++;printf("录入第%d帧....\n", frameNumber);}/*if (frameNumber >= 600){break;}*/DWORD dwCurrentTime = ::GetTickCount();if (dwCurrentTime - dwInitTime > 60000){break;}int dwPassedMillSeconds = dwCurrentTime - dwBeginTime;int dwDiff = frameNumber * 1000 / g_iFrameRate - dwPassedMillSeconds;if (dwDiff > 0){Sleep(dwDiff);}}av_write_trailer(avFormCtx_Out);avformat_free_context(avFormCtx_Out);avcodec_close(avCodecCtx_Out);avcodec_free_context(&avCodecCtx_Out);av_free(avCodec);av_packet_free(&packet);av_frame_free(&frame);return 0;
}int _tmain(int argc, _TCHAR* argv[])
{//av_register_all();//avformat_network_init();avdevice_register_all();HANDLE hThread = CreateThread(NULL, 0, ScreenCapThreadProc, 0, 0, NULL);WaitForSingleObject(hThread, INFINITE);return 0;
}

ffmpeg gdi录制桌面视频时注意事项相关推荐

  1. Linux下使用“avconv“捕捉录制桌面视频及音频

    目录 1. 安装avconv工具 2. 开始捕捉录制桌面视频 3. 捕捉录制桌面的音频 4. 同时捕捉录制桌面的视频和音频 Libav是一套跨平台的库和工具,用来处理多媒体文件.流及协议,它原生于ff ...

  2. 电脑技术分享:电脑怎样录制桌面视频

    喜欢在网上搜视频教程的小伙伴就知道现在有很多教程都是用电脑录制的,比方说Excel的使用方法.PS怎么去水印.怎么修改电脑设置和怎么修改电脑分辨率等等.那么电脑怎样录制桌面视频? 迅捷屏幕录像工具原画 ...

  3. javacv录制桌面视频_免费在任何操作系统上录制桌面视频

    javacv录制桌面视频 Sometimes screen shots just aren't enough to explain how to do something or to show the ...

  4. ffmpeg录制桌面视频和麦克风音频(音视频同步)

    vs版本:2017 ffmpeg版本号: ffmpeg version N-102642-g864d1ef2fc Copyright © 2000-2021 the FFmpeg developers ...

  5. ffmpeg录制桌面视频和系统内部声音(音视频同步)

    本文抓取的是电脑内部声音,需要先安装软件screen capture recorder,这个软件大小有50M,太大,安装后,里面有一个脚本文件,如下所示: 打开这个文件,可以看到如下内容: 这个文件比 ...

  6. Android录制桌面视频screenrecord

    Android4.4及以上支持adb命令录制Android桌面视频 1.如果你已经配置了adb的环境变量可以直接在terminal(Mac).cmd(Windows)中键入以下命令 adb shell ...

  7. Android 录制桌面视频 screenrecord

    Android4.4及以上支持adb命令录制Android桌面视频 1.如果你已经配置了adb的环境变量可以直接在terminal(Mac).cmd(Windows)中键入以下命令 adb shell ...

  8. android录制avi视频时aac音频的正确配置方法

    在android上面,用avilib.c开源库录制过avi视频加aac音频的同学们,应该都有一段为了aac音频无法正确录制和播放而抓狂的经历.我本人也经历了这样的折磨,现在将踩过的坑记录下来,让后来者 ...

  9. navigator.mediaDevices.getUserMedia录制桌面视频并保存

    如果,需要同时录制麦克风声音以及桌面声音,请看最后 以下代码中有用到electron的desktopCapturer模块(用于PC应用开发),如果你并不需要,可以直接忽略,直接定义你要录制的视频源和音 ...

最新文章

  1. python二维数组怎么写_python的二维数组操作
  2. 如何让网页弹出确定_电脑去除网页上弹窗广告的操作方法
  3. Linux下修改命令提示符
  4. Java RMI 多个JVM间相互通信
  5. 【爬虫】爬取百度贴吧数据
  6. Yii2 mongodb 扩展的where的条件增加大于 小于号
  7. SpringBoot 自带工具类~ReflectionUtils
  8. jframe大小根据组件变化_Swing JDialog容器和JFrame容器使用教程
  9. win10电脑便签怎么换行 电脑便签记事本换行的两种方法
  10. 图片制作、LOGO制作、ICO文件
  11. Java 面向对象基本特征
  12. datasupport类删除_关于xcode:我可以从iOS DeviceSupport删除数据吗?
  13. 敏捷(Agile)是什么?有哪些优缺点?敏捷落地需不需工具?
  14. PHP的优势是什么?
  15. 这家公司,打造电竞三冠王SKT,无视法律,韩国总统还得给他赔笑脸
  16. 7-2 单词首字母大写 (15 分)
  17. scara 机器人三四轴机械结构
  18. 2017qs世界计算机排名,2017qs世界大学学科排名完整版
  19. Scrapy框架的使用之Scrapy入门
  20. 云端部署 vs 本地化部署

热门文章

  1. 团队管理16--团建要素剖析
  2. 专利申请的流程及好处
  3. pythonjam使用手册图_NumPy的详细教程(官网手册翻译)
  4. 【SQL基础学习】----基础篇(1)
  5. wps兑换优惠券在哪里_你的支付宝积分用了吗,再不用就要过期了,现在还可以兑换红包...
  6. ASP玩转微信公众平台自定义接口(1)—接口申请
  7. 面结构光三角测距原理
  8. 第七周Coreidraw总结
  9. latex语法学习(二)
  10. OpenCV学习笔记(四十二)——Mat数据操作之普通青年、文艺青年、暴力青年