mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-11-03 16:32:22 +00:00
fix(android): copy captured frames before the Image is released (#16827)
O FrameRaw guardava apenas o ponteiro do ByteBuffer do Image recebido via JNI. O Kotlin fecha o Image logo após onVideoFrameUpdate, devolvendo o buffer ao ImageReader; quando a thread de vídeo chamava take(), o SurfaceFlinger já podia estar desenhando o quadro seguinte no mesmo buffer, gerando regiões pretas/parciais na imagem remota em qualquer codec (visível em Android x86, ex.: Bliss OS 13 em Intel UHD). Agora update() copia o quadro para um buffer próprio enquanto o Image ainda é válido, e take() copia desse buffer. O áudio usa a mesma estrutura e passa a ter a mesma garantia.
This commit is contained in:
1 parent
a85ad89ffc
commit
41f182e636
1 file changed
+22
-36
@@ -16,7 +16,6 @@ use lazy_static::lazy_static;
|
||||
use serde::Deserialize;
|
||||
use std::ops::Not;
|
||||
use std::os::raw::c_void;
|
||||
use std::sync::atomic::{AtomicPtr, Ordering::SeqCst};
|
||||
use std::sync::{Mutex, RwLock};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
@@ -38,8 +37,8 @@ const MAX_AUDIO_FRAME_TIMEOUT: Duration = Duration::from_millis(1000);
|
||||
|
||||
struct FrameRaw {
|
||||
name: &'static str,
|
||||
ptr: AtomicPtr<u8>,
|
||||
len: usize,
|
||||
data: Vec<u8>,
|
||||
ready: bool,
|
||||
last_update: Instant,
|
||||
timeout: Duration,
|
||||
enable: bool,
|
||||
@@ -49,8 +48,8 @@ impl FrameRaw {
|
||||
fn new(name: &'static str, timeout: Duration) -> Self {
|
||||
FrameRaw {
|
||||
name,
|
||||
ptr: AtomicPtr::default(),
|
||||
len: 0,
|
||||
data: Vec::new(),
|
||||
ready: false,
|
||||
last_update: Instant::now(),
|
||||
timeout,
|
||||
enable: false,
|
||||
@@ -59,49 +58,36 @@ impl FrameRaw {
|
||||
|
||||
fn set_enable(&mut self, value: bool) {
|
||||
self.enable = value;
|
||||
self.ptr.store(std::ptr::null_mut(), SeqCst);
|
||||
self.len = 0;
|
||||
self.ready = false;
|
||||
}
|
||||
|
||||
fn update(&mut self, data: *mut u8, len: usize) {
|
||||
if self.enable.not() {
|
||||
if self.enable.not() || data.is_null() || len == 0 {
|
||||
return;
|
||||
}
|
||||
self.len = len;
|
||||
self.ptr.store(data, SeqCst);
|
||||
let src = unsafe { std::slice::from_raw_parts(data, len) };
|
||||
self.data.clear();
|
||||
self.data.extend_from_slice(src);
|
||||
self.ready = true;
|
||||
self.last_update = Instant::now();
|
||||
}
|
||||
|
||||
// take inner data as slice
|
||||
// release when success
|
||||
fn take<'a>(&mut self, dst: &mut Vec<u8>, last: &mut Vec<u8>) -> Option<()> {
|
||||
if self.enable.not() {
|
||||
if self.enable.not() || !self.ready {
|
||||
return None;
|
||||
}
|
||||
let ptr = self.ptr.load(SeqCst);
|
||||
if ptr.is_null() || self.len == 0 {
|
||||
None
|
||||
} else {
|
||||
if self.last_update.elapsed() > self.timeout {
|
||||
log::trace!("Failed to take {} raw,timeout!", self.name);
|
||||
return None;
|
||||
}
|
||||
let slice = unsafe { std::slice::from_raw_parts(ptr, self.len) };
|
||||
self.release();
|
||||
if last.len() == slice.len() && crate::would_block_if_equal(last, slice).is_err() {
|
||||
return None;
|
||||
}
|
||||
dst.resize(slice.len(), 0);
|
||||
unsafe {
|
||||
std::ptr::copy_nonoverlapping(slice.as_ptr(), dst.as_mut_ptr(), slice.len());
|
||||
}
|
||||
Some(())
|
||||
if self.last_update.elapsed() > self.timeout {
|
||||
log::trace!("Failed to take {} raw,timeout!", self.name);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
fn release(&mut self) {
|
||||
self.len = 0;
|
||||
self.ptr.store(std::ptr::null_mut(), SeqCst);
|
||||
self.ready = false;
|
||||
if last.len() == self.data.len() && crate::would_block_if_equal(last, &self.data).is_err()
|
||||
{
|
||||
return None;
|
||||
}
|
||||
dst.clear();
|
||||
dst.extend_from_slice(&self.data);
|
||||
Some(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user