C++实现X11桌面录屏为RGB文件

X11服务提供一种共享内存的方式来高效的捕获屏幕。
直接上代码:

头文件

#ifndef SRC_VIDEOCAPTURE_X11DESKTOPCAPTURE_H_
#define SRC_VIDEOCAPTURE_X11DESKTOPCAPTURE_H_

#include "VideoCapture/VideoCapture.h"
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/XShm.h>
#include <sys/shm.h>
#include <string>
#include <vector>
namespace RemoteServer {

class X11DesktopCapture: public VideoCapture {
public:
	X11DesktopCapture(std::shared_ptr<BufferQueue<unsigned char>>& buffer_queue);
	virtual ~X11DesktopCapture();

	void UpdateVideoCapture() final;

//	bool StopVideoCapture() final;

	bool DestroyVideoCapture() final;

private:
	bool InitVideoCapture();

    bool InitShmImages();
    Display* m_display = nullptr;
    Window m_desktop_window = 0;
	XShmSegmentInfo* m_shm_segment_info = nullptr;
	XImage* m_x_shm_image = nullptr;
	int m_screen_size = 0;

};

} /* namespace RemoteServer */

#endif /* SRC_VIDEOCAPTURE_X11DESKTOPCAPTURE_H_ */

实现.cpp

/*
 * X11DesktopCapture.cpp
 *
 *  Created on: Apr 3, 2020
 *      Author: zou
 */

#include "X11DesktopCapture.h"
#include <cstring>
namespace RemoteServer {

X11DesktopCapture::X11DesktopCapture(std::shared_ptr<BufferQueue<unsigned char>>& buffer_queue)
{
	// TODO Auto-generated constructor stub
	m_buffer_queue = buffer_queue;
	InitVideoCapture();
}

X11DesktopCapture::~X11DesktopCapture() {
	// TODO Auto-generated destructor stub
	DestroyVideoCapture();
}

bool X11DesktopCapture::InitVideoCapture(){
	m_display = XOpenDisplay(NULL);
	if(!m_display){
		SIMLOG(SimLogger::Error, "can not connect a display");
		return false;
	}
	m_desktop_window = RootWindow(m_display, 0);
	if(!m_desktop_window){
		SIMLOG(SimLogger::Error, "can not get the root window");
		return false;
	}
	m_video_format->pixel_type = RGBA8888;
	m_video_format->width = DisplayWidth(m_display, 0);
	m_video_format->height = DisplayHeight(m_display, 0);
	XImage* window_image = XGetImage(m_display, m_desktop_window, 0, 0, m_video_format->width, m_video_format->height, AllPlanes, ZPixmap);
	m_video_format->bits_per_pixel = window_image->bits_per_pixel;
	m_video_format->depth = window_image->depth;
	m_screen_size = m_video_format->width * m_video_format->height * m_video_format->bits_per_pixel / 8;
	SIMLOG(SimLogger::Info, "w/h/d/bpp/size: " << m_video_format->width <<"/" << m_video_format->height << "/"
			<< m_video_format->depth << "/" << m_video_format->bits_per_pixel << "/" <<  m_screen_size);
	XDestroyImage(window_image);
	//create share memory image
	return InitShmImages();
}
bool X11DesktopCapture::InitShmImages(){
	m_shm_segment_info = new XShmSegmentInfo();
	m_shm_segment_info->shmid = -1;
	m_x_shm_image = XShmCreateImage(m_display,
		      DefaultVisual(m_display,0), // Use a correct visual. Omitted for brevity
			  m_video_format->depth,   // Determine correct depth from the visual. Omitted for brevity
		      ZPixmap, NULL, m_shm_segment_info, m_video_format->width, m_video_format->height);
	if(!m_x_shm_image){
		SIMLOG(SimLogger::Error, "can not create XshmImage");
		return false;
	}
	m_shm_segment_info->shmid = shmget(IPC_PRIVATE,
			m_x_shm_image->bytes_per_line * m_x_shm_image->height,
		      IPC_CREAT|0777);
	if(m_shm_segment_info->shmid == -1){
		SIMLOG(SimLogger::Error, "shmid failed");
		return false;
	}
	m_shm_segment_info->shmaddr = m_x_shm_image->data = (char* )shmat(m_shm_segment_info->shmid, 0, 0);
//	m_shm_segment_info->readOnly = False;
	if(!XShmAttach(m_display, m_shm_segment_info)){
		SIMLOG(SimLogger::Error, "XShmAttach failed");
		return false;
	}
	SIMLOG(SimLogger::Debug, "initShmImages success");
	return true;
}

void X11DesktopCapture::UpdateVideoCapture(){
	while(m_start_capture){
		if(!XShmGetImage(m_display, m_desktop_window, m_x_shm_image, 0, 0, AllPlanes)){
			SIMLOG(SimLogger::Error, "update shmImage failed");
			return ;
		}
		std::vector<unsigned char> buffer(m_x_shm_image->data, m_x_shm_image->data + m_screen_size);
		m_buffer_queue->WaitePushBuffer(buffer);
	}

//	memcpy(video_data, m_x_shm_image->data, m_screen_size );
}
bool X11DesktopCapture::DestroyVideoCapture(){
	StopVideoCapture();
	if(m_shm_segment_info && m_shm_segment_info->shmid != -1 && m_display){
		if(!XShmDetach(m_display, m_shm_segment_info)){
			SIMLOG(SimLogger::Error, "XShmDetach failed");
			return false;
		}
	}
	if(m_x_shm_image){
		XDestroyImage(m_x_shm_image);
		m_x_shm_image = nullptr;
	}
	if(m_shm_segment_info && m_shm_segment_info->shmaddr != (char*)-1 && m_display){
		shmdt(m_shm_segment_info->shmaddr);
	}
	if(m_shm_segment_info && m_shm_segment_info->shmid != -1 && m_display){
		shmctl(m_shm_segment_info->shmid, IPC_RMID, 0);
		m_display = nullptr;
	}
	if(m_shm_segment_info){
		m_shm_segment_info->shmaddr = (char*)-1;
		m_shm_segment_info->shmid = -1;
		delete m_shm_segment_info;
		m_shm_segment_info = nullptr;
	}

	return true;
}


} /* namespace RemoteServer */

首先是初始化InitVideoCapture
在初始化过程中,首先通过XOpenDisplay(NULL)获取X11的默认DISPLAY。参数可以直接设置为DISPLAY号,如图我的DISPLAY是:1。就可以写成XOpenDisplay(:1)

在这里插入图片描述
然后获取根窗口,也就是你的桌面窗口。

m_desktop_window = RootWindow(m_display, 0);

然后获取屏幕宽高

m_video_format->width = DisplayWidth(m_display, 0);
m_video_format->height = DisplayHeight(m_display, 0);

通过XGetImage获取到当前屏幕信息,包括宽、高、位深等信息。

XImage* window_image = XGetImage(m_display, m_desktop_window, 0, 0, m_video_format->width, m_video_format->height, AllPlanes, ZPixmap);

如果只是想要截屏,把window_image->data数据保存下来就是原始的RGB图像信息了。
使用后记得销毁,不然就内存泄露了

XDestroyImage(window_image);

如果想要录屏的话,用XGetImage的方式效率极低。要使用X共享内存的方式进行捕获。首先对X共享内存初始化。

InitShmImages()

在初始化过程中,对将X共享内存与当前DISPLAY进行绑定,这样以后可以通过读取共享内存数据直接捕获当前DISPLAY的屏幕

XShmAttach(m_display, m_shm_segment_info)

初始化以后,便可以通过XShmGetImage函数直接高效的捕获当前屏幕的数据了

XShmGetImage(m_display, m_desktop_window, m_x_shm_image, 0, 0, AllPlanes)

最后记得要释放掉共享内存

DestroyVideoCapture()

通过测试程序捕获屏幕

#include "VideoCapture/VideoCapture.h"
#include "Logger/Logger.h"
#include <fstream>
#include <memory>
#include <unistd.h>
#include "../../src/Base/BufferQueue/BufferQueue.hpp"

using namespace RemoteServer;
int main(){
	SimLogger::Logger::CreateLoggerInstance();
	SimLogger::Logger::InitLogger(SimLogger::ConsoleType, "LoggerTest", SimLogger::Info);

	std::ofstream file_stream("x11capture.rgb", std::ios::out | std::ios::binary);

	auto buffer_queue = std::make_shared< BufferQueue<unsigned char> >(5);

	auto video_capture = VideoCapture::CreateVideoCapture(X11Desktop, buffer_queue);

	auto format = video_capture->GetVideoFormat();
	int screen_size = format->width * format->height * format->bits_per_pixel / 8;
//	unsigned char* data = new unsigned char[screen_size];
	video_capture->StartVideoCapture();
	int count = 0;
	while(count++ < 500){
		std::vector<unsigned char> buffer;
		buffer_queue->WaitePopBuffer(buffer);
		file_stream.write(reinterpret_cast<char*>(buffer.data()), screen_size);
		usleep(20000);
	}
	video_capture->StopVideoCapture();
	file_stream.close();
//	delete[] data;
	video_capture->DestroyVideoCapture();
	return 0;
}

捕获了500帧RGB原始数据存放在x11capture.rgb中。

githup地址

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值