ffmpeg录制桌面

vs版本:2017
ffmpeg版本号:
ffmpeg version N-102642-g864d1ef2fc Copyright © 2000-2021 the FFmpeg developers
built with gcc 8.1.0 (x86_64-win32-seh-rev0, Built by MinGW-W64 project)
configuration: --arch=x86_64 --prefix=/home/ffmpeg_static_x64 --disable-debug
libavutil 57. 0.100 / 57. 0.100
libavcodec 59. 1.100 / 59. 1.100
libavformat 59. 2.101 / 59. 2.101
libavdevice 59. 0.100 / 59. 0.100
libavfilter 8. 0.101 / 8. 0.101
libswscale 6. 0.100 / 6. 0.100
libswresample 4. 0.100 / 4. 0.100

关于ffmpeg的lib和dll,本人在csdn上上传了相关资源,并且免费下载。

桌面录制时,发现有一些暂不了解的地方,比如avformat_write_header后,time_base发生了改变,这个使得av_packet_rescale_ts(&packet, pCodecCtx_Video->time_base, pFormatCtx_Out->streams[0]->time_base);这句特别重要,若没这句,视频播放速度可能会很快。
今天先在此记录,后面若有其他发现,会继续更新此博客。
代码如下:

// FfmpegTest.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <Windows.h>
#include <conio.h>

#ifdef	__cplusplus

extern "C"
{
    
    
#endif
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libavdevice/avdevice.h"
#include "libavutil/audio_fifo.h"
#include "libavutil/imgutils.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, "avfilter.lib")
	//#pragma comment(lib, "postproc.lib")
	//#pragma comment(lib, "swresample.lib")
#pragma comment(lib, "swscale.lib")
#ifdef __cplusplus
};
#endif

AVFormatContext	*pFormatCtx_Video = NULL, *pFormatCtx_Out = NULL;
AVCodecContext	*pCodecCtx_Video = NULL;
AVCodec			*pCodec_Video = NULL;
AVFifoBuffer	*fifo_video = NULL;
int VideoIndex;


AVCodecContext	*pCodecEncodeCtx_Video = NULL;
AVCodec			*pCodecEncode_Video = NULL;




SwsContext *img_convert_ctx;
int frame_size = 0;

uint8_t *picture_buf = NULL, *frame_buf = NULL;

bool bCap = true;

DWORD WINAPI ScreenCapThreadProc(LPVOID lpParam);



int OpenVideoCapture()
{
    
    
	const AVInputFormat *ifmt = av_find_input_format("gdigrab");
	//这里可以加参数打开,例如可以指定采集帧率
	AVDictionary *options = NULL;
	av_dict_set(&options, "framerate", "25", NULL);
	av_dict_set(&options, "probesize", "50000000", NULL);
	//av_dict_set(&options,"offset_x","20",0);
	//The distance from the top edge of the screen or desktop
	//av_dict_set(&options,"offset_y","40",0);
	//Video frame size. The default is to capture the full screen
	//av_dict_set(&options,"video_size","320x240",0);
	if (avformat_open_input(&pFormatCtx_Video, "desktop", ifmt, &options) != 0)
	{
    
    
		printf("Couldn't open input stream.(无法打开视频输入流)\n");
		return -1;
	}
	if (avformat_find_stream_info(pFormatCtx_Video, NULL)<0)
	{
    
    
		printf("Couldn't find stream information.(无法获取视频流信息)\n");
		return -1;
	}
	if (pFormatCtx_Video->streams[0]->codecpar->codec_type != AVMEDIA_TYPE_VIDEO)
	{
    
    
		printf("Couldn't find video stream information.(无法获取视频流信息)\n");
		return -1;
	}
	pCodec_Video = (AVCodec *)avcodec_find_decoder(pFormatCtx_Video->streams[0]->codecpar->codec_id);

	pCodecCtx_Video = avcodec_alloc_context3(pCodec_Video);

	if (pCodec_Video == NULL)
	{
    
    
		printf("Codec not found.(没有找到解码器)\n");
		return -1;
	}
	if (avcodec_open2(pCodecCtx_Video, pCodec_Video, NULL) < 0)
	{
    
    
		printf("Could not open codec.(无法打开解码器)\n");
		return -1;
	}

	/* put sample parameters */
	pCodecCtx_Video->bit_rate = 400000;
	/* resolution must be a multiple of two */
	//pCodecCtx_Video->width = 352;
	//pCodecCtx_Video->height = 288;
	pCodecCtx_Video->width = 1920;
	pCodecCtx_Video->height = 1080;
	/* frames per second */
	AVRational timeBase;
	timeBase.num = 1;
	timeBase.den = 25;
	pCodecCtx_Video->time_base = timeBase;

	AVRational frameRate;
	frameRate.den = 1;
	frameRate.num = 25;
	pCodecCtx_Video->framerate = frameRate;

	/* emit one intra frame every ten frames
	 * check frame pict_type before passing frame
	 * to encoder, if frame->pict_type is AV_PICTURE_TYPE_I
	 * then gop_size is ignored and the output of encoder
	 * will always be I frame irrespective to gop_size
	 */
	pCodecCtx_Video->gop_size = 25;
	pCodecCtx_Video->max_b_frames = 1;
	pCodecCtx_Video->pix_fmt = AV_PIX_FMT_YUV420P;


	img_convert_ctx = sws_getContext(pCodecCtx_Video->width, pCodecCtx_Video->height, (AVPixelFormat)pFormatCtx_Video->streams[0]->codecpar->format,
		pCodecCtx_Video->width, pCodecCtx_Video->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);

	frame_size = av_image_get_buffer_size(pCodecCtx_Video->pix_fmt, pCodecCtx_Video->width, pCodecCtx_Video->height, 1);
	//申请30帧缓存
	fifo_video = av_fifo_alloc(30 * av_image_get_buffer_size(AV_PIX_FMT_YUV420P, pCodecCtx_Video->width, pCodecCtx_Video->height, 1));

	return 0;
}



static char *dup_wchar_to_utf8(wchar_t *w)
{
    
    
	char *s = NULL;
	int l = WideCharToMultiByte(CP_UTF8, 0, w, -1, 0, 0, 0, 0);
	s = (char *)av_malloc(l);
	if (s)
		WideCharToMultiByte(CP_UTF8, 0, w, -1, s, l, 0, 0);
	return s;
}



int OpenOutPut()
{
    
    
	AVStream *pVideoStream = NULL, *pAudioStream = NULL;
	const char *outFileName = "test.mp4";
	avformat_alloc_output_context2(&pFormatCtx_Out, NULL, NULL, outFileName);

	
	if (pFormatCtx_Video->streams[0]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
	{
    
    
		VideoIndex = 0;
		pVideoStream = avformat_new_stream(pFormatCtx_Out, NULL);

		if (!pVideoStream)
		{
    
    
			printf("can not new stream for output!\n");
			return -1;
		}
		
		AVRational timeBase;
		timeBase.num = 1;
		timeBase.den = 50;
		pVideoStream->time_base = timeBase;
		

		pCodecEncode_Video = (AVCodec *)avcodec_find_encoder(pFormatCtx_Out->oformat->video_codec);
		if (!(pCodecEncode_Video)) {
    
    
			fprintf(stderr, "Could not find encoder for '%s'\n",
				avcodec_get_name(AV_CODEC_ID_MPEG4));
			exit(1);
		}

		pCodecEncodeCtx_Video = avcodec_alloc_context3(pCodecEncode_Video);
		if (!pCodecEncodeCtx_Video) {
    
    
			fprintf(stderr, "Could not alloc an encoding context\n");
			exit(1);
		}

		pCodecEncodeCtx_Video->time_base = timeBase;
		pCodecEncodeCtx_Video->codec_id = pFormatCtx_Out->oformat->video_codec;

		pCodecEncodeCtx_Video->bit_rate = 400000;
		/* Resolution must be a multiple of two. */
		//pCodecEncodeCtx_Video->width = 352;
		//pCodecEncodeCtx_Video->height = 288;
		pCodecEncodeCtx_Video->width = 1920;
		pCodecEncodeCtx_Video->height = 1080;
		/* timebase: This is the fundamental unit of time (in seconds) in terms
		 * of which frame timestamps are represented. For fixed-fps content,
		 * timebase should be 1/framerate and timestamp increments should be
		 * identical to 1. */

		pCodecEncodeCtx_Video->gop_size = 25; /* emit one intra frame every twelve frames at most */
		pCodecEncodeCtx_Video->pix_fmt = AV_PIX_FMT_YUV420P;

		if ((avcodec_open2(pCodecEncodeCtx_Video, pCodecEncode_Video, NULL)) < 0)
		{
    
    
			printf("can not open the encoder\n");
			return -1;
		}
	}
	

	if (!(pFormatCtx_Out->oformat->flags & AVFMT_NOFILE))
	{
    
    
		if (avio_open2(&pFormatCtx_Out->pb, outFileName, AVIO_FLAG_WRITE, nullptr, nullptr) < 0)
		{
    
    
			printf("can not open output file handle!\n");
			return -1;
		}
	}

	pCodecCtx_Video->codec_type = AVMEDIA_TYPE_VIDEO;
	pCodecCtx_Video->codec_id = pFormatCtx_Out->oformat->video_codec;
	pCodecCtx_Video->pix_fmt = AV_PIX_FMT_YUV420P;//受codec->pix_fmts数组限制

	avcodec_parameters_from_context(pVideoStream->codecpar, pCodecCtx_Video);
	

	int iWriteResult = avformat_write_header(pFormatCtx_Out, NULL);
	if (iWriteResult < 0)
	{
    
    
		printf("can not write the header of the output file!\n");
		return -1;
	}

	return 0;
}

int Yuv420ToFile(AVFrame *avFrame, int width, int height, FILE *pFile)
{
    
    
	if (!pFile || !avFrame)
	{
    
    
		return -1;
	}

	for (int j = 0; j < height; j++)
	{
    
    
		fwrite(avFrame->data[0] + j * avFrame->linesize[0], 1, width, pFile);
	}

	for (int j = 0; j < height / 2; j++)
	{
    
    
		fwrite(avFrame->data[1] + j * avFrame->linesize[1], 1, width / 2, pFile);
	}

	for (int j = 0; j < height / 2; j++)
	{
    
    
		fwrite(avFrame->data[2] + j * avFrame->linesize[2], 1, width / 2, pFile);

	}

	return 0;
}



int _tmain(int argc, _TCHAR* argv[])
{
    
    
	avdevice_register_all();
	if (OpenVideoCapture() < 0)
	{
    
    
		return -1;
	}

	if (OpenOutPut() < 0)
	{
    
    
		return -1;
	}

	AVFrame *pFrame = av_frame_alloc();
	AVFrame *pFrameYUV = av_frame_alloc();


	int frame_size = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, pCodecCtx_Video->width, pCodecCtx_Video->height, 1);

	uint8_t *out_buffer_yuv420 = (uint8_t *)av_malloc(frame_size);

	av_image_fill_arrays(pFrameYUV->data, pFrameYUV->linesize, out_buffer_yuv420, AV_PIX_FMT_YUV420P, pCodecCtx_Video->width, pCodecCtx_Video->height, 1);

	static int iPicCount = 0;

	AVPacket packet = {
    
     0 };
	for (;;)
	{
    
    
		av_packet_unref(&packet);
		if (av_read_frame(pFormatCtx_Video, &packet) < 0)
		{
    
    
			break;
		}
		
		int ret = 0;
		if (packet.stream_index == VideoIndex)
		{
    
    
			ret = avcodec_send_packet(pCodecCtx_Video, &packet);

			iPicCount++;

			while (ret >= 0) 
			{
    
    
				ret = avcodec_receive_frame(pCodecCtx_Video, pFrame);
				if (ret == AVERROR(EAGAIN))
				{
    
    
					break;
				}
				else if (ret == AVERROR_EOF)
				{
    
    
					return 0;
				}
				else if (ret < 0) {
    
    
					fprintf(stderr, "Error during decoding\n");
					exit(1);
				}


				int iScale = sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecEncodeCtx_Video->height, pFrameYUV->data, pFrameYUV->linesize);

				packet.pts = iPicCount;
				packet.dts = iPicCount;
				av_packet_rescale_ts(&packet, pCodecCtx_Video->time_base, pFormatCtx_Out->streams[0]->time_base);

				pFrameYUV->width = pFrame->width;
				pFrameYUV->height = pFrame->height;
				pFrameYUV->format = AV_PIX_FMT_YUV420P;

				pFrameYUV->pts = packet.pts;
				pFrameYUV->pkt_dts = packet.pts;


				av_packet_unref(&packet);

				ret = avcodec_send_frame(pCodecEncodeCtx_Video, pFrameYUV);

				ret = avcodec_receive_packet(pCodecEncodeCtx_Video, &packet);

				ret = av_interleaved_write_frame(pFormatCtx_Out, &packet);
				avio_flush(pFormatCtx_Out->pb);

			}

			if (iPicCount > 1000)
			{
    
    
				break;
			}

			if (ret == AVERROR(EAGAIN))
			{
    
    
				continue;
			}
		}
	}

	printf("main end\n");
	av_write_trailer(pFormatCtx_Out);
	avio_close(pFormatCtx_Out->pb);
	avformat_free_context(pFormatCtx_Out);
	
	return 0;
}










Guess you like

Origin blog.csdn.net/tusong86/article/details/121408286