diff --git a/libs/scrap/src/common/convert.rs b/libs/scrap/src/common/convert.rs index ba246c694..d38831928 100644 --- a/libs/scrap/src/common/convert.rs +++ b/libs/scrap/src/common/convert.rs @@ -32,13 +32,16 @@ pub fn convert_to_yuv( 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 - if src_stride[0] < src_width * 4 { + if src_stride[0] < src_width * src_pixfmt.bytes_per_pixel() { bail!( - "src_stride[0] < src_width * 4: {} < {}", + "src_stride too small: {} < {}", src_stride[0], - src_width * 4 + src_width * src_pixfmt.bytes_per_pixel() ); } 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 unsupported = format!( + "unsupported pixfmt conversion: {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_uv = dst_fmt.stride[1]; 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_u = dst[dst_fmt.u..].as_mut_ptr(); let dst_v = dst[dst_fmt.v..].as_mut_ptr(); - let f = if src_pixfmt == crate::Pixfmt::BGRA { - ARGBToI420 - } else { - ABGRToI420 + let f = match src_pixfmt { + crate::Pixfmt::BGRA => ARGBToI420, + crate::Pixfmt::RGBA => ABGRToI420, + crate::Pixfmt::RGB565LE => RGB565ToI420, + _ => bail!(unsupported), }; call_yuv!(f( src.as_ptr(), @@ -78,7 +88,9 @@ pub fn convert_to_yuv( 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_uv = dst_fmt.stride[1]; dst.resize( @@ -87,14 +99,33 @@ pub fn convert_to_yuv( ); let dst_y = dst.as_mut_ptr(); let dst_uv = dst[dst_fmt.u..].as_mut_ptr(); - let f = if src_pixfmt == crate::Pixfmt::BGRA { - ARGBToNV12 - } else { - ABGRToNV12 + let (input, input_stride) = match src_pixfmt { + crate::Pixfmt::BGRA => (src.as_ptr(), src_stride[0]), + crate::Pixfmt::RGBA => (src.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), + }; + let f = match src_pixfmt { + crate::Pixfmt::BGRA => ARGBToNV12, + crate::Pixfmt::RGBA => ABGRToNV12, + crate::Pixfmt::RGB565LE => ARGBToNV12, + _ => bail!(unsupported), }; call_yuv!(f( - src.as_ptr(), - src_stride[0] as _, + input, + input_stride as _, dst_y, dst_stride_y as _, dst_uv, @@ -103,7 +134,9 @@ pub fn convert_to_yuv( 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_u = dst_fmt.stride[1]; 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_u = dst[dst_fmt.u..].as_mut_ptr(); let dst_v = dst[dst_fmt.v..].as_mut_ptr(); - let src = if src_pixfmt == crate::Pixfmt::BGRA { - src - } else { - mid_data.resize(src.len(), 0); - call_yuv!(ABGRToARGB( - src.as_ptr(), - src_stride[0] as _, - mid_data.as_mut_ptr(), - src_stride[0] as _, - src_width as _, - src_height as _, - )); - mid_data + let (input, input_stride) = match src_pixfmt { + crate::Pixfmt::BGRA => (src.as_ptr(), src_stride[0]), + crate::Pixfmt::RGBA => { + mid_data.resize(src.len(), 0); + call_yuv!(ABGRToARGB( + src.as_ptr(), + src_stride[0] as _, + mid_data.as_mut_ptr(), + src_stride[0] as _, + src_width as _, + src_height as _, + )); + (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( - src.as_ptr(), - src_stride[0] as _, + input, + input_stride as _, dst_y, dst_stride_y as _, dst_u, @@ -143,10 +192,7 @@ pub fn convert_to_yuv( )); } _ => { - bail!( - "convert not support, {src_pixfmt:?} -> {:?}", - dst_fmt.pixfmt - ); + bail!(unsupported); } } Ok(()) diff --git a/libs/scrap/src/common/mod.rs b/libs/scrap/src/common/mod.rs index 085d1e501..164f7157d 100644 --- a/libs/scrap/src/common/mod.rs +++ b/libs/scrap/src/common/mod.rs @@ -59,6 +59,7 @@ pub enum ImageFormat { ABGR, ARGB, } + #[repr(C)] pub struct ImageRgb { pub raw: Vec, @@ -208,11 +209,27 @@ impl<'a> EncodeInput<'a> { pub enum Pixfmt { BGRA, RGBA, + RGB565LE, I420, NV12, 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)] pub struct EncodeYuvFormat { pub pixfmt: Pixfmt, diff --git a/libs/scrap/src/common/x11.rs b/libs/scrap/src/common/x11.rs index 56beaa46e..2a7c19c47 100644 --- a/libs/scrap/src/common/x11.rs +++ b/libs/scrap/src/common/x11.rs @@ -23,9 +23,10 @@ impl TraitCapturer for Capturer { fn frame<'a>(&'a mut self, _timeout: Duration) -> io::Result> { let width = self.width(); let height = self.height(); + let pixfmt = self.0.display().pixfmt(); Ok(Frame::PixelBuffer(PixelBuffer::new( self.0.frame()?, - Pixfmt::BGRA, + pixfmt, width, height, ))) diff --git a/libs/scrap/src/x11/capturer.rs b/libs/scrap/src/x11/capturer.rs index 4b0e00604..550bfb346 100644 --- a/libs/scrap/src/x11/capturer.rs +++ b/libs/scrap/src/x11/capturer.rs @@ -17,7 +17,7 @@ impl Capturer { pub fn new(display: Display) -> io::Result { // Calculate dimensions. - let pixel_width = 4; + let pixel_width = display.pixfmt().bytes_per_pixel(); let rect = display.rect(); let size = (rect.w as usize) * (rect.h as usize) * pixel_width; diff --git a/libs/scrap/src/x11/display.rs b/libs/scrap/src/x11/display.rs index a33903caa..0bc18b3b8 100644 --- a/libs/scrap/src/x11/display.rs +++ b/libs/scrap/src/x11/display.rs @@ -2,6 +2,7 @@ use std::rc::Rc; use super::ffi::*; use super::Server; +use crate::Pixfmt; #[derive(Debug)] pub struct Display { @@ -10,6 +11,7 @@ pub struct Display { rect: Rect, root: xcb_window_t, name: String, + pixfmt: Pixfmt, } #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] @@ -27,6 +29,7 @@ impl Display { rect: Rect, root: xcb_window_t, name: String, + pixfmt: Pixfmt, ) -> Display { Display { server, @@ -34,6 +37,7 @@ impl Display { rect, root, name, + pixfmt, } } @@ -59,4 +63,8 @@ impl Display { pub fn name(&self) -> String { self.name.clone() } + + pub fn pixfmt(&self) -> Pixfmt { + self.pixfmt + } } diff --git a/libs/scrap/src/x11/ffi.rs b/libs/scrap/src/x11/ffi.rs index 3337d6e44..370ce78b7 100644 --- a/libs/scrap/src/x11/ffi.rs +++ b/libs/scrap/src/x11/ffi.rs @@ -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_shm_query_version(c: *mut xcb_connection_t) -> xcb_shm_query_version_cookie_t; - + pub fn xcb_shm_query_version_reply( c: *mut xcb_connection_t, cookie: xcb_shm_query_version_cookie_t, e: *mut *mut xcb_generic_error_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; @@ -195,6 +207,12 @@ pub struct xcb_void_cookie_t { pub sequence: u32, } +#[repr(C)] +#[derive(Clone, Copy)] +pub struct xcb_get_geometry_cookie_t { + pub sequence: u32, +} + #[repr(C)] pub struct xcb_generic_error_t { pub response_type: u8, @@ -248,3 +266,18 @@ pub struct xcb_shm_query_version_reply_t { pub pixmap_format: u8, 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], +} diff --git a/libs/scrap/src/x11/iter.rs b/libs/scrap/src/x11/iter.rs index 387687847..f800d5aa8 100644 --- a/libs/scrap/src/x11/iter.rs +++ b/libs/scrap/src/x11/iter.rs @@ -2,6 +2,7 @@ use std::ffi::CString; use std::ptr; use std::rc::Rc; +use crate::Pixfmt; use hbb_common::libc; use super::ffi::*; @@ -66,7 +67,7 @@ impl Iterator for DisplayIter { unsafe { let data = &*inner.data; 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( self.server.clone(), data.primary != 0, @@ -78,6 +79,7 @@ impl Iterator for DisplayIter { }, root, name, + pixfmt, ); 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 { let mut e: *mut xcb_generic_error_t = std::ptr::null_mut(); - let reply = xcb_get_atom_name_reply( - conn, - xcb_get_atom_name(conn, atom), - &mut e as _, - ); + let reply = xcb_get_atom_name_reply(conn, xcb_get_atom_name(conn, atom), &mut e as _); if reply == std::ptr::null() { return empty; } @@ -121,3 +119,20 @@ fn get_atom_name(conn: *mut xcb_connection_t, atom: xcb_atom_t) -> String { empty } } + +unsafe fn get_pixfmt(conn: *mut xcb_connection_t, root: xcb_window_t) -> Option { + 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, + } +}