【FFmpeg+SDL】使用FFmpeg捕获屏幕,SDL显示

FFMpeg版本:7.1.1

SDL版本:2.32.6

直接上代码,工程文件放在最后

  • 其中的define TT是显示窗口和屏幕像素大小的比例,如果按屏幕原本大小显示,不太好观察.
    (命名不规范,define这样用也不太安全,不建议这样,我是为了方便观察随便搞的)
cpp 复制代码
#include <iostream>

extern "C" {
#include <libavdevice/avdevice.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>
#include <libavutil/mem.h>
#include <libavutil/imgutils.h>
#include "SDL2/SDL.h"
}
#undef main

#define TT *9/10

int main()
{
	AVFormatContext *fmt_ctx = NULL;
	AVCodecContext *codec_ctx = NULL;
	int video_stream_index = -1;
	SDL_Window *window = NULL;
	SDL_Renderer *renderer = NULL;
	SDL_Texture *texture = NULL;
	struct SwsContext *sws_ctx = NULL;
	AVFrame *frame = NULL;
	AVPacket pkt;

	avdevice_register_all();
	avformat_network_init();

	AVDictionary *options = NULL;
	if (avformat_open_input(&fmt_ctx, "desktop", av_find_input_format("gdigrab"), &options) < 0)
	{
		fprintf(stderr, "无法打开输入设备\n");
		return -1;
	}

	// 获取流信息
	if (avformat_find_stream_info(fmt_ctx, NULL) < 0) {
		fprintf(stderr, "无法获取流信息\n");
		avformat_close_input(&fmt_ctx);
		return -1;
	}

	// 查找视频流
	for (int i = 0; i < fmt_ctx->nb_streams; i++) {
		if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
			video_stream_index = i;
			break;
		}
	}
	if (video_stream_index == -1) {
		fprintf(stderr, "未找到视频流\n");
		avformat_close_input(&fmt_ctx);
		return -1;
	}

	// 获取编解码参数并创建解码器上下文
	AVCodecParameters *codec_par = fmt_ctx->streams[video_stream_index]->codecpar;
	const AVCodec *codec = avcodec_find_decoder(codec_par->codec_id);
	if (!codec) {
		fprintf(stderr, "解码器未找到\n");
		avformat_close_input(&fmt_ctx);
		return -1;
	}
	printf("%s\n", avcodec_get_name(codec_par->codec_id));
	codec_ctx = avcodec_alloc_context3(codec);
	if (avcodec_parameters_to_context(codec_ctx, codec_par) < 0) {
		fprintf(stderr, "无法复制编解码参数\n");
		avcodec_free_context(&codec_ctx);
		avformat_close_input(&fmt_ctx);
		return -1;
	}
	if (avcodec_open2(codec_ctx, codec, NULL) < 0) {
		fprintf(stderr, "无法打开解码器\n");
		avcodec_free_context(&codec_ctx);
		avformat_close_input(&fmt_ctx);
		return -1;
	}

	// 初始化SDL
	if (SDL_Init(SDL_INIT_VIDEO) < 0) {
		fprintf(stderr, "SDL初始化失败: %s\n", SDL_GetError());
		avcodec_free_context(&codec_ctx);
		avformat_close_input(&fmt_ctx);
		return -1;
	}

	window = SDL_CreateWindow("桌面捕获", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
		codec_ctx->width TT, codec_ctx->height TT, SDL_WINDOW_SHOWN);
	if (!window) {
		fprintf(stderr, "无法创建窗口: %s\n", SDL_GetError());
		SDL_Quit();
		avcodec_free_context(&codec_ctx);
		avformat_close_input(&fmt_ctx);
		return -1;
	}

	renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
	if (!renderer) {
		fprintf(stderr, "无法创建渲染器: %s\n", SDL_GetError());
		SDL_DestroyWindow(window);
		SDL_Quit();
		avcodec_free_context(&codec_ctx);
		avformat_close_input(&fmt_ctx);
		return -1;
	}

	// 根据FFmpeg的像素格式设置SDL纹理格式
	Uint32 sdl_pix_fmt;
	AVPixelFormat ffmpeg_pix_fmt = codec_ctx->pix_fmt;
	if (ffmpeg_pix_fmt == AV_PIX_FMT_BGRA) {
		sdl_pix_fmt = SDL_PIXELFORMAT_ARGB8888;
	}
	else {
		// 需要转换到支持的格式,此处示例转换为RGB24
		sdl_pix_fmt = SDL_PIXELFORMAT_RGB24;
		sws_ctx = sws_getContext(codec_ctx->width, codec_ctx->height, ffmpeg_pix_fmt,
			codec_ctx->width TT, codec_ctx->height TT, AV_PIX_FMT_RGB24,
			SWS_BILINEAR, NULL, NULL, NULL);
	}

	texture = SDL_CreateTexture(renderer, sdl_pix_fmt,
		SDL_TEXTUREACCESS_STREAMING,
		codec_ctx->width, codec_ctx->height);
	if (!texture) {
		fprintf(stderr, "无法创建纹理: %s\n", SDL_GetError());
		SDL_DestroyRenderer(renderer);
		SDL_DestroyWindow(window);
		SDL_Quit();
		avcodec_free_context(&codec_ctx);
		avformat_close_input(&fmt_ctx);
		return -1;
	}

	frame = av_frame_alloc();
	if (!frame) {
		fprintf(stderr, "无法分配帧\n");
		SDL_DestroyTexture(texture);
		SDL_DestroyRenderer(renderer);
		SDL_DestroyWindow(window);
		SDL_Quit();
		avcodec_free_context(&codec_ctx);
		avformat_close_input(&fmt_ctx);
		return -1;
	}

	SDL_Event event;
	int quit = 0;

	while (!quit) {
		// 处理SDL事件
		while (SDL_PollEvent(&event)) {
			if (event.type == SDL_QUIT) {
				quit = 1;
			}
		}

		if (av_read_frame(fmt_ctx, &pkt) < 0) {
			break; // 结束读取
		}

		if (pkt.stream_index == video_stream_index) {
			// 发送数据包到解码器
			if (avcodec_send_packet(codec_ctx, &pkt) < 0) {
				fprintf(stderr, "发送数据包失败\n");
				av_packet_unref(&pkt);
				continue;
			}

			// 接收解码后的帧
			while (avcodec_receive_frame(codec_ctx, frame) == 0) {
				// 转换帧格式(如果需要)
				if (sws_ctx) {
					AVFrame *rgb_frame = av_frame_alloc();
					rgb_frame->format = AV_PIX_FMT_RGB24;
					rgb_frame->width = frame->width TT;
					rgb_frame->height = frame->height TT;
					av_frame_get_buffer(rgb_frame, 0);
					sws_scale(sws_ctx, frame->data, frame->linesize, 0, frame->height,
						rgb_frame->data, rgb_frame->linesize);
					// 更新纹理
					SDL_UpdateTexture(texture, NULL, rgb_frame->data[0], rgb_frame->linesize[0]);
					av_frame_free(&rgb_frame);
				}
				else {
					// 直接使用原始数据
					SDL_UpdateTexture(texture, NULL, frame->data[0], frame->linesize[0]);
				}

				// 渲染
				SDL_RenderClear(renderer);
				SDL_RenderCopy(renderer, texture, NULL, NULL);
				SDL_RenderPresent(renderer);
			}
		}
		av_packet_unref(&pkt);
	}

	// 清理资源
	av_frame_free(&frame);
	sws_freeContext(sws_ctx);
	SDL_DestroyTexture(texture);
	SDL_DestroyRenderer(renderer);
	SDL_DestroyWindow(window);
	SDL_Quit();
	avcodec_free_context(&codec_ctx);
	avformat_close_input(&fmt_ctx);

}

链接: https://pan.baidu.com/s/1J2v6pXlkPZJ2r3psVbQX0A?pwd=c4p7 提取码: c4p7

相关推荐
happybasic13 小时前
在CMD下使用FFmpeg将.wav文件转换成指定的格式~
ffmpeg
shao91851617 小时前
第10章 Streaming(上):初级音频应用(1)——项目三:自建服务器的Mini-Omni实时语音聊天机器人
ffmpeg·whisper·asr·mini-omni·自建语音服务器
Leon_Chenl1 天前
【已开源】【嵌入式 Linux 音视频+ AI 实战项目】瑞芯微 Rockchip 系列 RK3588-基于深度学习的人脸门禁+ IPC 智能安防监控系统
深度学习·opencv·yolo·ffmpeg·音视频·边缘计算·人脸识别+检测
antzou2 天前
视频图片/文字水印
ffmpeg·视频水印·批量水印
AC赳赳老秦3 天前
DBA 专属方案:用 OpenClaw 实现 SQL 语句优化、慢查询分析、数据库备份巡检全自动化
服务器·前端·数据库·ffmpeg·自动化·deepseek·openclaw
小叮当⇔3 天前
M4A 转 MP3 桌面转换器(PyQt5 + FFmpeg)
开发语言·qt·ffmpeg
aovenus7 天前
FFmpeg 官网及文档
ffmpeg
aovenus7 天前
FFmpeg 工具介绍
ffmpeg
jr-create(•̀⌄•́)7 天前
简单视频编辑tools
python·ffmpeg
山栀shanzhi7 天前
在做直播时,I帧的间隔(GOP)一般是多少?
网络·c++·面试·ffmpeg