linux x11 rgb565 capture (#8580)

Signed-off-by: 21pages <sunboeasy@gmail.com>
This commit is contained in:
21pages 2024-07-03 14:20:41 +08:00 committed by GitHub
parent 6d2e985593
commit d00582e929
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 165 additions and 45 deletions

View File

@ -32,13 +32,16 @@ pub fn convert_to_yuv(
dst_fmt.h dst_fmt.h
); );
} }
if src_pixfmt == crate::Pixfmt::BGRA || src_pixfmt == crate::Pixfmt::RGBA { if src_pixfmt == crate::Pixfmt::BGRA
|| src_pixfmt == crate::Pixfmt::RGBA
|| src_pixfmt == crate::Pixfmt::RGB565LE
{
// stride is calculated, not real, so we need to check it // stride is calculated, not real, so we need to check it
if src_stride[0] < src_width * 4 { if src_stride[0] < src_width * src_pixfmt.bytes_per_pixel() {
bail!( bail!(
"src_stride[0] < src_width * 4: {} < {}", "src_stride too small: {} < {}",
src_stride[0], src_stride[0],
src_width * 4 src_width * src_pixfmt.bytes_per_pixel()
); );
} }
if src.len() < src_stride[0] * src_height { if src.len() < src_stride[0] * src_height {
@ -51,19 +54,26 @@ pub fn convert_to_yuv(
} }
} }
let align = |x: usize| (x + 63) / 64 * 64; let align = |x: usize| (x + 63) / 64 * 64;
let unsupported = format!(
"unsupported pixfmt conversion: {src_pixfmt:?} -> {:?}",
dst_fmt.pixfmt
);
match (src_pixfmt, dst_fmt.pixfmt) { match (src_pixfmt, dst_fmt.pixfmt) {
(crate::Pixfmt::BGRA, crate::Pixfmt::I420) | (crate::Pixfmt::RGBA, crate::Pixfmt::I420) => { (crate::Pixfmt::BGRA, crate::Pixfmt::I420)
| (crate::Pixfmt::RGBA, crate::Pixfmt::I420)
| (crate::Pixfmt::RGB565LE, crate::Pixfmt::I420) => {
let dst_stride_y = dst_fmt.stride[0]; let dst_stride_y = dst_fmt.stride[0];
let dst_stride_uv = dst_fmt.stride[1]; let dst_stride_uv = dst_fmt.stride[1];
dst.resize(dst_fmt.h * dst_stride_y * 2, 0); // waste some memory to ensure memory safety dst.resize(dst_fmt.h * dst_stride_y * 2, 0); // waste some memory to ensure memory safety
let dst_y = dst.as_mut_ptr(); let dst_y = dst.as_mut_ptr();
let dst_u = dst[dst_fmt.u..].as_mut_ptr(); let dst_u = dst[dst_fmt.u..].as_mut_ptr();
let dst_v = dst[dst_fmt.v..].as_mut_ptr(); let dst_v = dst[dst_fmt.v..].as_mut_ptr();
let f = if src_pixfmt == crate::Pixfmt::BGRA { let f = match src_pixfmt {
ARGBToI420 crate::Pixfmt::BGRA => ARGBToI420,
} else { crate::Pixfmt::RGBA => ABGRToI420,
ABGRToI420 crate::Pixfmt::RGB565LE => RGB565ToI420,
_ => bail!(unsupported),
}; };
call_yuv!(f( call_yuv!(f(
src.as_ptr(), src.as_ptr(),
@ -78,7 +88,9 @@ pub fn convert_to_yuv(
src_height as _, src_height as _,
)); ));
} }
(crate::Pixfmt::BGRA, crate::Pixfmt::NV12) | (crate::Pixfmt::RGBA, crate::Pixfmt::NV12) => { (crate::Pixfmt::BGRA, crate::Pixfmt::NV12)
| (crate::Pixfmt::RGBA, crate::Pixfmt::NV12)
| (crate::Pixfmt::RGB565LE, crate::Pixfmt::NV12) => {
let dst_stride_y = dst_fmt.stride[0]; let dst_stride_y = dst_fmt.stride[0];
let dst_stride_uv = dst_fmt.stride[1]; let dst_stride_uv = dst_fmt.stride[1];
dst.resize( dst.resize(
@ -87,14 +99,33 @@ pub fn convert_to_yuv(
); );
let dst_y = dst.as_mut_ptr(); let dst_y = dst.as_mut_ptr();
let dst_uv = dst[dst_fmt.u..].as_mut_ptr(); let dst_uv = dst[dst_fmt.u..].as_mut_ptr();
let f = if src_pixfmt == crate::Pixfmt::BGRA { let (input, input_stride) = match src_pixfmt {
ARGBToNV12 crate::Pixfmt::BGRA => (src.as_ptr(), src_stride[0]),
} else { crate::Pixfmt::RGBA => (src.as_ptr(), src_stride[0]),
ABGRToNV12 crate::Pixfmt::RGB565LE => {
let mid_stride = src_width * 4;
mid_data.resize(mid_stride * src_height, 0);
call_yuv!(RGB565ToARGB(
src.as_ptr(),
src_stride[0] as _,
mid_data.as_mut_ptr(),
mid_stride as _,
src_width as _,
src_height as _,
));
(mid_data.as_ptr(), mid_stride)
}
_ => bail!(unsupported),
};
let f = match src_pixfmt {
crate::Pixfmt::BGRA => ARGBToNV12,
crate::Pixfmt::RGBA => ABGRToNV12,
crate::Pixfmt::RGB565LE => ARGBToNV12,
_ => bail!(unsupported),
}; };
call_yuv!(f( call_yuv!(f(
src.as_ptr(), input,
src_stride[0] as _, input_stride as _,
dst_y, dst_y,
dst_stride_y as _, dst_stride_y as _,
dst_uv, dst_uv,
@ -103,7 +134,9 @@ pub fn convert_to_yuv(
src_height as _, src_height as _,
)); ));
} }
(crate::Pixfmt::BGRA, crate::Pixfmt::I444) | (crate::Pixfmt::RGBA, crate::Pixfmt::I444) => { (crate::Pixfmt::BGRA, crate::Pixfmt::I444)
| (crate::Pixfmt::RGBA, crate::Pixfmt::I444)
| (crate::Pixfmt::RGB565LE, crate::Pixfmt::I444) => {
let dst_stride_y = dst_fmt.stride[0]; let dst_stride_y = dst_fmt.stride[0];
let dst_stride_u = dst_fmt.stride[1]; let dst_stride_u = dst_fmt.stride[1];
let dst_stride_v = dst_fmt.stride[2]; let dst_stride_v = dst_fmt.stride[2];
@ -115,23 +148,39 @@ pub fn convert_to_yuv(
let dst_y = dst.as_mut_ptr(); let dst_y = dst.as_mut_ptr();
let dst_u = dst[dst_fmt.u..].as_mut_ptr(); let dst_u = dst[dst_fmt.u..].as_mut_ptr();
let dst_v = dst[dst_fmt.v..].as_mut_ptr(); let dst_v = dst[dst_fmt.v..].as_mut_ptr();
let src = if src_pixfmt == crate::Pixfmt::BGRA { let (input, input_stride) = match src_pixfmt {
src crate::Pixfmt::BGRA => (src.as_ptr(), src_stride[0]),
} else { crate::Pixfmt::RGBA => {
mid_data.resize(src.len(), 0); mid_data.resize(src.len(), 0);
call_yuv!(ABGRToARGB( call_yuv!(ABGRToARGB(
src.as_ptr(), src.as_ptr(),
src_stride[0] as _, src_stride[0] as _,
mid_data.as_mut_ptr(), mid_data.as_mut_ptr(),
src_stride[0] as _, src_stride[0] as _,
src_width as _, src_width as _,
src_height as _, src_height as _,
)); ));
mid_data (mid_data.as_ptr(), src_stride[0])
}
crate::Pixfmt::RGB565LE => {
let mid_stride = src_width * 4;
mid_data.resize(mid_stride * src_height, 0);
call_yuv!(RGB565ToARGB(
src.as_ptr(),
src_stride[0] as _,
mid_data.as_mut_ptr(),
mid_stride as _,
src_width as _,
src_height as _,
));
(mid_data.as_ptr(), mid_stride)
}
_ => bail!(unsupported),
}; };
call_yuv!(ARGBToI444( call_yuv!(ARGBToI444(
src.as_ptr(), input,
src_stride[0] as _, input_stride as _,
dst_y, dst_y,
dst_stride_y as _, dst_stride_y as _,
dst_u, dst_u,
@ -143,10 +192,7 @@ pub fn convert_to_yuv(
)); ));
} }
_ => { _ => {
bail!( bail!(unsupported);
"convert not support, {src_pixfmt:?} -> {:?}",
dst_fmt.pixfmt
);
} }
} }
Ok(()) Ok(())

View File

@ -59,6 +59,7 @@ pub enum ImageFormat {
ABGR, ABGR,
ARGB, ARGB,
} }
#[repr(C)] #[repr(C)]
pub struct ImageRgb { pub struct ImageRgb {
pub raw: Vec<u8>, pub raw: Vec<u8>,
@ -208,11 +209,27 @@ impl<'a> EncodeInput<'a> {
pub enum Pixfmt { pub enum Pixfmt {
BGRA, BGRA,
RGBA, RGBA,
RGB565LE,
I420, I420,
NV12, NV12,
I444, I444,
} }
impl Pixfmt {
pub fn bpp(&self) -> usize {
match self {
Pixfmt::BGRA | Pixfmt::RGBA => 32,
Pixfmt::RGB565LE => 16,
Pixfmt::I420 | Pixfmt::NV12 => 12,
Pixfmt::I444 => 24,
}
}
pub fn bytes_per_pixel(&self) -> usize {
(self.bpp() + 7) / 8
}
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct EncodeYuvFormat { pub struct EncodeYuvFormat {
pub pixfmt: Pixfmt, pub pixfmt: Pixfmt,

View File

@ -23,9 +23,10 @@ impl TraitCapturer for Capturer {
fn frame<'a>(&'a mut self, _timeout: Duration) -> io::Result<Frame<'a>> { fn frame<'a>(&'a mut self, _timeout: Duration) -> io::Result<Frame<'a>> {
let width = self.width(); let width = self.width();
let height = self.height(); let height = self.height();
let pixfmt = self.0.display().pixfmt();
Ok(Frame::PixelBuffer(PixelBuffer::new( Ok(Frame::PixelBuffer(PixelBuffer::new(
self.0.frame()?, self.0.frame()?,
Pixfmt::BGRA, pixfmt,
width, width,
height, height,
))) )))

View File

@ -17,7 +17,7 @@ impl Capturer {
pub fn new(display: Display) -> io::Result<Capturer> { pub fn new(display: Display) -> io::Result<Capturer> {
// Calculate dimensions. // Calculate dimensions.
let pixel_width = 4; let pixel_width = display.pixfmt().bytes_per_pixel();
let rect = display.rect(); let rect = display.rect();
let size = (rect.w as usize) * (rect.h as usize) * pixel_width; let size = (rect.w as usize) * (rect.h as usize) * pixel_width;

View File

@ -2,6 +2,7 @@ use std::rc::Rc;
use super::ffi::*; use super::ffi::*;
use super::Server; use super::Server;
use crate::Pixfmt;
#[derive(Debug)] #[derive(Debug)]
pub struct Display { pub struct Display {
@ -10,6 +11,7 @@ pub struct Display {
rect: Rect, rect: Rect,
root: xcb_window_t, root: xcb_window_t,
name: String, name: String,
pixfmt: Pixfmt,
} }
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
@ -27,6 +29,7 @@ impl Display {
rect: Rect, rect: Rect,
root: xcb_window_t, root: xcb_window_t,
name: String, name: String,
pixfmt: Pixfmt,
) -> Display { ) -> Display {
Display { Display {
server, server,
@ -34,6 +37,7 @@ impl Display {
rect, rect,
root, root,
name, name,
pixfmt,
} }
} }
@ -59,4 +63,8 @@ impl Display {
pub fn name(&self) -> String { pub fn name(&self) -> String {
self.name.clone() self.name.clone()
} }
pub fn pixfmt(&self) -> Pixfmt {
self.pixfmt
}
} }

View File

@ -82,12 +82,24 @@ extern "C" {
pub fn xcb_get_atom_name_name_length(reply: *const xcb_get_atom_name_reply_t) -> i32; pub fn xcb_get_atom_name_name_length(reply: *const xcb_get_atom_name_reply_t) -> i32;
pub fn xcb_shm_query_version(c: *mut xcb_connection_t) -> xcb_shm_query_version_cookie_t; pub fn xcb_shm_query_version(c: *mut xcb_connection_t) -> xcb_shm_query_version_cookie_t;
pub fn xcb_shm_query_version_reply( pub fn xcb_shm_query_version_reply(
c: *mut xcb_connection_t, c: *mut xcb_connection_t,
cookie: xcb_shm_query_version_cookie_t, cookie: xcb_shm_query_version_cookie_t,
e: *mut *mut xcb_generic_error_t, e: *mut *mut xcb_generic_error_t,
) -> *const xcb_shm_query_version_reply_t; ) -> *const xcb_shm_query_version_reply_t;
pub fn xcb_get_geometry_unchecked(
c: *mut xcb_connection_t,
drawable: xcb_drawable_t,
) -> xcb_get_geometry_cookie_t;
pub fn xcb_get_geometry_reply(
c: *mut xcb_connection_t,
cookie: xcb_get_geometry_cookie_t,
e: *mut *mut xcb_generic_error_t,
) -> *mut xcb_get_geometry_reply_t;
} }
pub const XCB_IMAGE_FORMAT_Z_PIXMAP: u8 = 2; pub const XCB_IMAGE_FORMAT_Z_PIXMAP: u8 = 2;
@ -195,6 +207,12 @@ pub struct xcb_void_cookie_t {
pub sequence: u32, pub sequence: u32,
} }
#[repr(C)]
#[derive(Clone, Copy)]
pub struct xcb_get_geometry_cookie_t {
pub sequence: u32,
}
#[repr(C)] #[repr(C)]
pub struct xcb_generic_error_t { pub struct xcb_generic_error_t {
pub response_type: u8, pub response_type: u8,
@ -248,3 +266,18 @@ pub struct xcb_shm_query_version_reply_t {
pub pixmap_format: u8, pub pixmap_format: u8,
pub pad0: [u8; 15], pub pad0: [u8; 15],
} }
#[repr(C)]
pub struct xcb_get_geometry_reply_t {
pub response_type: u8,
pub depth: u8,
pub sequence: u16,
pub length: u32,
pub root: xcb_window_t,
pub x: i16,
pub y: i16,
pub width: u16,
pub height: u16,
pub border_width: u16,
pub pad0: [u8; 2],
}

View File

@ -2,6 +2,7 @@ use std::ffi::CString;
use std::ptr; use std::ptr;
use std::rc::Rc; use std::rc::Rc;
use crate::Pixfmt;
use hbb_common::libc; use hbb_common::libc;
use super::ffi::*; use super::ffi::*;
@ -66,7 +67,7 @@ impl Iterator for DisplayIter {
unsafe { unsafe {
let data = &*inner.data; let data = &*inner.data;
let name = get_atom_name(self.server.raw(), data.name); let name = get_atom_name(self.server.raw(), data.name);
let pixfmt = get_pixfmt(self.server.raw(), root).unwrap_or(Pixfmt::BGRA);
let display = Display::new( let display = Display::new(
self.server.clone(), self.server.clone(),
data.primary != 0, data.primary != 0,
@ -78,6 +79,7 @@ impl Iterator for DisplayIter {
}, },
root, root,
name, name,
pixfmt,
); );
xcb_randr_monitor_info_next(inner); xcb_randr_monitor_info_next(inner);
@ -102,11 +104,7 @@ fn get_atom_name(conn: *mut xcb_connection_t, atom: xcb_atom_t) -> String {
} }
unsafe { unsafe {
let mut e: *mut xcb_generic_error_t = std::ptr::null_mut(); let mut e: *mut xcb_generic_error_t = std::ptr::null_mut();
let reply = xcb_get_atom_name_reply( let reply = xcb_get_atom_name_reply(conn, xcb_get_atom_name(conn, atom), &mut e as _);
conn,
xcb_get_atom_name(conn, atom),
&mut e as _,
);
if reply == std::ptr::null() { if reply == std::ptr::null() {
return empty; return empty;
} }
@ -121,3 +119,20 @@ fn get_atom_name(conn: *mut xcb_connection_t, atom: xcb_atom_t) -> String {
empty empty
} }
} }
unsafe fn get_pixfmt(conn: *mut xcb_connection_t, root: xcb_window_t) -> Option<Pixfmt> {
let geo_cookie = xcb_get_geometry_unchecked(conn, root);
let geo = xcb_get_geometry_reply(conn, geo_cookie, ptr::null_mut());
if geo.is_null() {
return None;
}
let depth = (*geo).depth;
libc::free(geo as _);
// now only support little endian
// https://github.com/FFmpeg/FFmpeg/blob/a9c05eb657d0d05f3ac79fe9973581a41b265a5e/libavdevice/xcbgrab.c#L519
match depth {
16 => Some(Pixfmt::RGB565LE),
32 => Some(Pixfmt::BGRA),
_ => None,
}
}