linux x11 rgb565 capture (#8580)
Signed-off-by: 21pages <sunboeasy@gmail.com>
This commit is contained in:
parent
6d2e985593
commit
d00582e929
@ -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(())
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
)))
|
)))
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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],
|
||||||
|
}
|
||||||
|
@ -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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user