DXGI高帧率屏幕录像软件源码解析(声音捕获,抓屏,ffmpeg录像,MP4录像,flv录像,麦克风采集)(第4篇编码,录像部分)

这篇具有很好参考价值的文章主要介绍了DXGI高帧率屏幕录像软件源码解析(声音捕获,抓屏,ffmpeg录像,MP4录像,flv录像,麦克风采集)(第4篇编码,录像部分)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本文DEMO源码下载:

https://download.csdn.net/download/xjb2006/85109025

dxgi桌面屏幕录像(windows屏幕录像,硬件编码,声音捕获,音视频同步)

由于篇幅有限,分为4篇发表:

1、SDK接口一览:

2、声音采集部分:

3、屏幕捕获部分:

4、编码,录像部分:

距离上篇文章已经过了快1年了,才有时间把正式DEMO传上来,直接上个截图看看吧:

DXGI高帧率屏幕录像软件源码解析(声音捕获,抓屏,ffmpeg录像,MP4录像,flv录像,麦克风采集)(第4篇编码,录像部分)

该DEMO演示了win10屏幕录像的核心功能,包含音源选择(支持麦克风,计算机声音和2者混合),屏幕选择(主副屏选择),鼠标,帧率,码率,硬件编码,实时预览,双录制(同时录制为flv,mp4)等基本功能。为了扩展需要,程序核心模块做成DLL动态库,可以多语言扩展,适用于C++,C#,JAVA,VB,Python等等其他语言。

视频编码:

DXGI输出一般是RGB32,需要将颜色空间转换为YV12或者NV12等才能进行编码,现支持libx264,h264_qsv,h264_nvenc,至于h264_amf,由于暂无测试环境,无法确认是否能用。

颜色空间转换采用libyuv或者ipp进行高效转换,经比较,效率较ffmpeg更高。

优先选择硬件加速(GPU)编码时,会自动判断是否有h264_qsv,h264_nvenc,如果没有则选择libx264,当然也可以手动指定编码器。

将RGB32进行缩放的libyuv算法:

static void ResizeRGB32(BYTE *pIn,int scx,int scy,BYTE *pOut,int dcx,int dcy)
{
    int ret=libyuv::ARGBScale(pIn,scx*4,scx,scy,pOut,dcx*4,dcx,dcy,libyuv::kFilterLinear);
    ret=0;
}


验证编码器的函数:

bool CFFFindEncoder::Find(char* szName)
{
    //MessageBox(0,L"1",0,0);
    AVCodecContext *c= NULL;
    //AVFrame *frame;
    AVPacket *pkt;
    AVCodec *codec=avcodec_find_encoder_by_name(szName);
    if(codec==0)
        return false;
    //MessageBox(0,L"2",0,0);
    c = avcodec_alloc_context3(codec);
    if (!c) 
    {
        return false;
    }
    //MessageBox(0,L"3",0,0);
    pkt = av_packet_alloc();
    if (!pkt)
    {
        return false;
    }
    c->bit_rate = 8000000;
    c->width = 1920;
    c->height = 1080;
    c->time_base.num=1;
    c->time_base.den=25;
    c->framerate.num=25;
    c->framerate.den=1;
    
    c->gop_size = 10;
    c->max_b_frames = 0;
    c->pix_fmt = AV_PIX_FMT_NV12;//AV_PIX_FMT_YUV420P;

    if (codec->id == AV_CODEC_ID_H264)
        av_opt_set(c->priv_data, "preset", "slow", 0);

    int ret = avcodec_open2(c, codec, NULL);
    if (ret < 0) {
        return false;
    }
    //MessageBox(0,L"4",0,0);

    avcodec_free_context(&c);
    av_packet_free(&pkt);
    return true;
}

音频编码:

包含了音频转码,混音,编码等,由于本人不太喜欢ffmpeg的AAC编码必须输入为AV_SAMPLE_FMT_FLTP,我直接用了libfaac库进行编码,输入为S16。ACM比较简单,网上也很好搜到,我贴ffmpeg的音频转码封装吧。对于AAC编码,libfaac也比较简单,就不贴代码了。

转码代码:

#pragma once
#include "stdafx.h"
class CAudioACM
{
private:
	char *audio_out_buffer=0;
	SwrContext* audio_convert_ctx = 0;
	int m_nInSampleRate;
	int m_nInChannel;
	int m_nOutSampleRate;
	int m_nOutChannel;
	AVSampleFormat in_sample_fmt;
	AVSampleFormat out_sample_fmt;
	int Getbytes_per_sample(int sampleFormat)
	{
		int bytes_per_sample = 2;
		switch (sampleFormat)
		{
		case AV_SAMPLE_FMT_U8P:
		case AV_SAMPLE_FMT_U8:
			bytes_per_sample = 8 >> 3;
			break;
		case AV_SAMPLE_FMT_S16P:
		case AV_SAMPLE_FMT_S16:
			bytes_per_sample = 16 >> 3;
			break;
		case AV_SAMPLE_FMT_S32:
		case AV_SAMPLE_FMT_S32P:
		case AV_SAMPLE_FMT_FLT:
		case AV_SAMPLE_FMT_FLTP:
			bytes_per_sample = 32 >> 3;
			break;
		case AV_SAMPLE_FMT_DBL:
		case AV_SAMPLE_FMT_DBLP:
			bytes_per_sample = 64 >> 3;
			break;
		default:
			bytes_per_sample = 0;
			break;
		}
		return bytes_per_sample;
	}



public:
	void Init(int nInSampleRate, int nInChannel,int nInFMT, int nOutSampleRate, int nOutChannel, int nOutFMT)//nFMT:0S16  1FLTP
	{
		if (audio_out_buffer)
		{
			delete[]audio_out_buffer;
			audio_out_buffer = 0;
		}
		audio_out_buffer = new char[1024 * 1024];
		m_nInSampleRate = nInSampleRate;
		m_nInChannel = nInChannel;
		m_nOutSampleRate = nOutSampleRate;
		m_nOutChannel = nOutChannel;
		in_sample_fmt = nInFMT==0?AV_SAMPLE_FMT_S16: AV_SAMPLE_FMT_FLTP;
		out_sample_fmt = nOutFMT == 0 ? AV_SAMPLE_FMT_S16 : AV_SAMPLE_FMT_FLTP;
		audio_convert_ctx = swr_alloc_set_opts(0, av_get_default_channel_layout(nOutChannel), out_sample_fmt, nOutSampleRate,
			av_get_default_channel_layout(nInChannel), in_sample_fmt, nInSampleRate, 0, NULL);//配置源音频参数和目标音频参数  
		int n1 = av_get_default_channel_layout(nOutChannel);
		int n4 = av_get_default_channel_layout(nInChannel);
		swr_init(audio_convert_ctx);


	}
	void InitFrame(AVFrame ** srcFrame,char* pPCM,int nSize,int samplerate,int channel,int format= AV_SAMPLE_FMT_S16)
	{
		*srcFrame = av_frame_alloc();
		(*srcFrame)->nb_samples = nSize/ channel/2;
		(*srcFrame)->channels = channel;
		(*srcFrame)->channel_layout = av_get_default_channel_layout(channel);
		(*srcFrame)->format = format;
		(*srcFrame)->sample_rate = samplerate;
		(*srcFrame)->data[0] = (*srcFrame)->extended_data[0] = (uint8_t*)pPCM;
		avcodec_fill_audio_frame(*srcFrame, channel, (AVSampleFormat)format, (const uint8_t*)pPCM, nSize, 0);
	}

	int Resample(char *pIn,int nLen, AVFrame* pOutFrame)
	{
		AVFrame* srcFrame = 0;
		InitFrame(&srcFrame, pIn, nLen, m_nInSampleRate, m_nInChannel);
		Resample(srcFrame, pOutFrame);
		av_frame_free(&srcFrame);
		return 0;
	}

	int Resample(AVFrame * pFrame_A, AVFrame* pOutFrame)
	{
		if (!audio_convert_ctx)
			return 0;
		int bytes_per_sample = Getbytes_per_sample(out_sample_fmt);
		int in_samples_per_channel = pFrame_A->nb_samples;
		if (pFrame_A->sample_rate < 8000)
			pFrame_A->sample_rate = 8000;
		int out_samples_per_channel = av_rescale_rnd(128 + in_samples_per_channel, m_nOutSampleRate, pFrame_A->sample_rate, AV_ROUND_UP);
		int size_per_sample_with_channels = m_nOutChannel * bytes_per_sample;
		int out_size = out_samples_per_channel * size_per_sample_with_channels;
		unsigned char* out[] = { (unsigned char*)audio_out_buffer };
		//int converted_samplers_per_channel = swr_convert(audio_convert_ctx, out, out_samples_per_channel, (const uint8_t**)pFrame_A->extended_data, in_samples_per_channel);
		int converted_samplers_per_channel = swr_convert(audio_convert_ctx, out, in_samples_per_channel, (const uint8_t**)pFrame_A->extended_data, in_samples_per_channel);
		if (converted_samplers_per_channel > 0)
		{
			pOutFrame->nb_samples = converted_samplers_per_channel;
			pOutFrame->sample_rate = m_nOutSampleRate;
			pOutFrame->channels = m_nOutChannel;
			pOutFrame->extended_data[0]=pOutFrame->data[0]= out[0];
//			pOutFrame->linesize[0] = pFrame_A->linesize[0];
			
			//m_pcmfifo.push((BYTE*)audio_out_buffer, converted_samplers_per_channel * size_per_sample_with_channels);
		}

		return 0;
	}

	void close()
	{
		if (audio_out_buffer)
		{
			delete[]audio_out_buffer;
			audio_out_buffer = 0;
		}
		if (audio_convert_ctx)
		{
			swr_close(audio_convert_ctx);	//xjb2018
			swr_free(&audio_convert_ctx);
		}
	}

public:
	CAudioACM()
	{
		audio_convert_ctx = 0;
		m_nInSampleRate = 44100;
		m_nInChannel = 2;
		m_nOutSampleRate = 44100;
		m_nOutChannel = 2;
	}
	~CAudioACM()
	{
		close();
	}



};

再贴一个简单的混音代码,因为要同时录制麦克风声音+计算机声音,就需要同时转换为44100,双声道,16位,然后进行混音,算法也比较简单,就是两个声音相加,我这里减半相加,都差不多,看你自己选择!

static void WaveMix(BYTE *pBuf0,BYTE *pBuf1,BYTE *pBufOut,int nLen)
{	
	int all=0;
	short w,w1;
	WORD wOK;
	for(int i=0;i<nLen;i+=2)
	{
		w=MAKEWORD(pBuf0[i],pBuf0[i+1]);
		w=w/2;
		w1=MAKEWORD(pBuf1[i],pBuf1[i+1]);
		w1=w1/2;
		all=w+w1;
		if(all>32767)
			all=32767;
		if(all<-32767)
			all=-32767;
		wOK= all;
		(pBufOut)[i]=LOBYTE(wOK);
		(pBufOut)[i+1]=HIBYTE(wOK);
	}
}

 录像:

前面已经得到了AAC和H264的数据了,我们就可以mix成MP4文件了,合成按照ffmpeg例子步骤来,我贴音视频同步的代码吧,先说说音视频同步,其实很简单的,很多人搞不懂。我们现实中用的是时分秒的计时单位,音视频中常用是毫秒(ms)。音视频中也是一样的,不过时间戳单位并不是ms,以时间戳AVRational time_base这个为例子,    pAVCodecContext->time_base.num = 1;    pAVCodecContext->time_base.den = 90000;一般会看到这种表达方式,这里的时间戳表示的单位就不是1ms了,而是1/90000秒为单位。

音频的时间戳单位:time_base.den = 44100;

视频的时间戳单位:time_base.den = 90000;

上面的时间我们给她个名字吧,叫音视频编码器时间而下面说的是ms时间。我们要做的事情就是把ms时间转成音视频编码器时间。

音视频同步:一般我是以音频为参考,因为音频不能丢,可以想象,如果音频丢了一帧比视频丢了一帧影响大得多,视频多一帧少一帧看不出来的。视频去同步音频,我们认为音频硬件是可以信任的时间计时单位,读取1024个sample就是23ms。视频去同步音频,视频可以用帧率去计算时间,公式就变成这样:__int64 vtime = m_nVideoCount * 1000/m_fps;但是有个问题,你能保证编码帧率绝对30帧?显然不可能,所以我们用系统时间去计算,公式就变成这样:__int64 vtime = (av_gettime() - m_nStartVideoTime) / 1000;(m_nStartVideoTime是开始编码时间)。

音频ms时间的代码:

__int64 COutPutStream::GetAudioTime()
{
    if (m_samplerate > 0)
        return (double)(m_nAudioCount) * (double)1024 * (double)1000 / (double)m_samplerate;
    return 0;
}    

我们演算下,输入m_nAudioCount=1时,得到的时间为23.22ms,是吧!

视频ms时间的代码:

__int64 COutPutStream::GetVideoTime()
{
    if (m_nStartVideoTime > 0)
    {
        __int64 vtime = (av_gettime() - m_nStartVideoTime) / 1000;
        //__int64 vtime = m_nVideoCount * 1000/m_fps;//
        return vtime;
    }
    return 0;
}
这就好理解了,时间戳那里直接填:

视频时间戳:

    int  lTimeStamp=this->GetVideoTime()*1000;

    enc_pkt.stream_index = pAVStream_Video->index;
    enc_pkt.dts = enc_pkt.pts = (INT64)90000 * (lTimeStamp) / AV_TIME_BASE;//转换为视频编码器时间
    AVRational timebase;
    timebase.den = 90000;    timebase.num = 1;
    av_packet_rescale_ts(&enc_pkt, timebase, pAVStream_Video->time_base);//转换为mux时间

    av_interleaved_write_frame(pOutAVFormatContext, &enc_pkt);

音频时间戳:

	pkt.size = nAACSize;
	pkt.pts = pkt.dts = 1024 * m_nAudioCount;//转为音频编码时间(看得懂吗?多理解下,呵呵)
	AVRational timebase;
	timebase.den = m_samplerate;//xiao
	timebase.num = 1;
	av_packet_rescale_ts(&pkt, timebase, pAVStream_Audio->time_base);//转为mux时间
	m_nAudioCount++;
    
    ...
     
    av_interleaved_write_frame(pOutAVFormatContext, &pkt);
    

 这样就高枕无忧了?理论上是,但是实际上,音频和视频同步上可能会有偏差,我们就要进行调节,我是这样做的,在H264视频写入代码段内,如果音频时间-视频时间>100ms,那么视频时间戳m_nStartVideoTime-=(5*1000);代码实现:(如果你有更好的方法请告诉我!)

	if (audioMS - dbMS > 100 || audioMS - dbMS < -100)//强制同步
	{
		if(audioMS - dbMS > 100)
		{
			m_nStartVideoTime-=(5*1000);//10ms
		}
		else
		{
			m_nStartVideoTime+=(5*1000);//10ms
		}
	}

这样就可以高枕无忧了,万事大吉,今晚吃鸡! 

补充下为什么要双录(录成mp4和flv),这一切都是为了保险而生!因为mp4并不是为流媒体而生的,她的音视频重要信息都存储在文件结尾,而且要停止录像时才存储,所以中途一旦断电或者程序崩溃,mp4是无法播放的。而flv就不同了,他是为流媒体而设计的,随时中断,崩溃,不会影响视频文件播放。

 本文DEMO源码下载:

https://download.csdn.net/download/xjb2006/85109025

​​​​​​dxgi桌面屏幕录像(windows屏幕录像,硬件编码,声音捕获,音视频同步)

QQ35744025   萧萧工作室文章来源地址https://www.toymoban.com/news/detail-415970.html

到了这里,关于DXGI高帧率屏幕录像软件源码解析(声音捕获,抓屏,ffmpeg录像,MP4录像,flv录像,麦克风采集)(第4篇编码,录像部分)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 帧率与曝光的时间对于手机屏幕采集图像的影响

    图像的采集大致可以分为以下两个阶段:曝光时间以及图像的读取时间,所以采集一幅图像的时间,可以近似地认为是 Frame Period = Exposure Time +Readout Time 根据两种排布方式的不一致有两种常见的方法:“non-overlapped”的曝光和“overlapped”的曝光。 在非重叠(“non-overlapped”)模式

    2024年02月09日
    浏览(30)
  • 用JS实现简单的屏幕录像机

    本文将介绍如何用JS实现简单的屏幕录像机。 创建一个按钮 书写JavaScript 看起来内容很多,但实际上,只是向按钮添加一个事件侦听器来开始和停止记录并相应地更改文本。 在写功能函数之前,声明 3 个全局变量(在函数之外)。 现在,开始屏幕录制 在用户屏幕之外创建媒

    2024年02月04日
    浏览(38)
  • 电脑屏幕录制:录制高清视频画面以及声音

    电脑办公的流行,录屏的需求也逐渐广泛,同时录制出高清画质的视频文件并且带声音成为一种常见的需求,那么有哪些软件可以实现这些功能呢? 其实能够录制高清视频并且带声音的软件有很多,特别是Windows系统上,电脑屏幕录制软件更是数不胜数,下面为大家推荐两款

    2024年02月07日
    浏览(33)
  • 电脑怎么录屏幕视频带声音?我来告诉你!

    在数字化的时代,录制屏幕视频不仅仅是游戏玩家的专利,越来越多的人在工作、学习中也需要通过屏幕录制分享信息。那么,电脑怎么录屏幕视频带声音呢?本文将详细介绍两种电脑录屏带声音的方法,通过这两种工具,你可以轻松地捕捉到屏幕上的每一个细节,并且同时

    2024年01月17日
    浏览(29)
  • Qt音视频开发43-采集屏幕桌面并推流(支持分辨率/矩形区域/帧率等设置/实时性极高)

    采集电脑屏幕桌面并推流一般是用来做共享桌面、远程协助、投屏之类的应用,最简单入门的做法可能会采用开个定时器或者线程抓图,将整个屏幕截图下来,然后将图片传出去,这种方式很简单但是性能要低不少,一般采用ffmpeg来做桌面推流的居多,毕竟如果不采用代码直

    2024年02月03日
    浏览(32)
  • TechSmith Camtasia for Mac 2023.0.3 中文破解版 Win/Mac上强大的屏幕录像工具

    Camtasia2023是Win/Mac上最强大屏幕录像的工具之一,该软件集成了视频录制、编辑、导出等一系列功能,支持鼠标光标样式、草绘示意插图、冰冻区域等实用的功能,还具有移动客户端让你录制视频,然后通过无线传输到 Camtasia 中进行编辑,如果你正在找一款Mac上的屏幕录像软

    2024年02月10日
    浏览(38)
  • win11如何录屏幕视频和声音?教你三招录屏方法

    大部分人在使用的电脑的时候,因为不常用到录屏功能所以根本就不知道原来Windows电脑中是有自带的录屏工具的!今天小编就给大家介绍下Windows自带录屏工具,win11如何录屏幕视频和声音?一起来看一下吧!   一、Windows自带录屏工具 无论是工作还是生活,我们在使用电脑过

    2023年04月22日
    浏览(36)
  • Python视频软件解析教程【源码可送】

    快放假了… 有的人出去玩~ 有的人在家里呆着看电视~ 这次就来康康怎么做一个好玩的小软件~ (嘘~自己用) 瓜子花生小零食准备好了吗? 我们先看看效果 这是本次要写的界面 主流视频都可以看,目前试过的有:优、腾、爱 其他的就没试过了,直接把视频地址复制粘贴到播

    2023年04月25日
    浏览(23)
  • ios查看帧率的软件_iOS实时流畅性监控

    iOS查看帧率的软件有: FPSMonitor:一款 iOS 平台上用于监测手机帧率的工具,可实时查看手机在某一时间段内的帧数变化及平均帧数。 刘雪松的监测帧率:通过 skinstatement 和 printscreen 截取屏幕,然后自动计算出屏幕的平均帧数、帧数变化、屏幕截图等。 苹果监控:用于实时监

    2024年02月16日
    浏览(33)
  • 电脑录像软件推荐?分享3款,简单好用且方便

    ​在日常生活中,我们经常会遇到临时有事情需要外出处理的时候,但在如果正好在上网课或者开会议、听讲座的时候,这时候外出很容易会错过一些重要的内容。这个时候,就需要借助电脑录像软件了。电脑录像软件推荐什么?今天小编就分享3款,简单好用且方便的电脑录

    2024年02月09日
    浏览(27)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包