Simplify renderer interface for live-kit-client

This commit is contained in:
Antonio Scandurra 2022-10-20 09:51:55 +02:00
parent 69472f7823
commit 99aa1219d2
6 changed files with 70 additions and 70 deletions

View file

@ -8,7 +8,6 @@ use collections::{BTreeMap, HashSet};
use futures::StreamExt; use futures::StreamExt;
use gpui::{AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Task}; use gpui::{AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Task};
use live_kit_client::{LocalTrackPublication, LocalVideoTrack, RemoteVideoTrackUpdate}; use live_kit_client::{LocalTrackPublication, LocalVideoTrack, RemoteVideoTrackUpdate};
use postage::watch;
use project::Project; use project::Project;
use std::{mem, os::unix::prelude::OsStrExt, sync::Arc}; use std::{mem, os::unix::prelude::OsStrExt, sync::Arc};
use util::{post_inc, ResultExt}; use util::{post_inc, ResultExt};
@ -409,42 +408,39 @@ impl Room {
.remote_participants .remote_participants
.get_mut(&peer_id) .get_mut(&peer_id)
.ok_or_else(|| anyhow!("subscribed to track by unknown participant"))?; .ok_or_else(|| anyhow!("subscribed to track by unknown participant"))?;
let (mut tx, mut rx) = watch::channel(); let mut frames = track.frames();
track.add_renderer(move |frame| *tx.borrow_mut() = Some(frame));
participant.tracks.insert( participant.tracks.insert(
track_id.clone(), track_id.clone(),
RemoteVideoTrack { RemoteVideoTrack {
frame: None, frame: None,
_live_kit_track: track, _live_kit_track: track,
_maintain_frame: Arc::new(cx.spawn_weak(|this, mut cx| async move { _maintain_frame: Arc::new(cx.spawn_weak(|this, mut cx| async move {
while let Some(frame) = rx.next().await { while let Some(frame) = frames.next().await {
if let Some(frame) = frame { let this = if let Some(this) = this.upgrade(&cx) {
let this = if let Some(this) = this.upgrade(&cx) { this
this } else {
break;
};
let done = this.update(&mut cx, |this, cx| {
if let Some(track) =
this.remote_participants.get_mut(&peer_id).and_then(
|participant| participant.tracks.get_mut(&track_id),
)
{
track.frame = Some(frame);
cx.emit(Event::Frame {
participant_id: peer_id,
track_id: track_id.clone(),
});
false
} else { } else {
break; true
};
let done = this.update(&mut cx, |this, cx| {
if let Some(track) =
this.remote_participants.get_mut(&peer_id).and_then(
|participant| participant.tracks.get_mut(&track_id),
)
{
track.frame = Some(frame);
cx.emit(Event::Frame {
participant_id: peer_id,
track_id: track_id.clone(),
});
false
} else {
true
}
});
if done {
break;
} }
});
if done {
break;
} }
} }
})), })),

View file

@ -13,7 +13,6 @@ name = "test_app"
[features] [features]
test-support = [ test-support = [
"async-broadcast",
"async-trait", "async-trait",
"collections/test-support", "collections/test-support",
"gpui/test-support", "gpui/test-support",
@ -29,12 +28,13 @@ live_kit_server = { path = "../live_kit_server", optional = true }
media = { path = "../media" } media = { path = "../media" }
anyhow = "1.0.38" anyhow = "1.0.38"
async-broadcast = "0.4"
core-foundation = "0.9.3" core-foundation = "0.9.3"
core-graphics = "0.22.3" core-graphics = "0.22.3"
futures = "0.3" futures = "0.3"
log = { version = "0.4.16", features = ["kv_unstable_serde"] }
parking_lot = "0.11.1" parking_lot = "0.11.1"
async-broadcast = { version = "0.4", optional = true }
async-trait = { version = "0.1", optional = true } async-trait = { version = "0.1", optional = true }
lazy_static = { version = "1.4", optional = true } lazy_static = { version = "1.4", optional = true }
nanoid = { version ="0.4", optional = true} nanoid = { version ="0.4", optional = true}
@ -58,7 +58,6 @@ futures = "0.3"
hmac = "0.12" hmac = "0.12"
jwt = "0.16" jwt = "0.16"
lazy_static = "1.4" lazy_static = "1.4"
log = { version = "0.4.16", features = ["kv_unstable_serde"] }
objc = "0.2" objc = "0.2"
parking_lot = "0.11.1" parking_lot = "0.11.1"
postage = { version = "0.4.1", features = ["futures-traits"] } postage = { version = "0.4.1", features = ["futures-traits"] }

View file

@ -28,12 +28,13 @@ class LKRoomDelegate: RoomDelegate {
class LKVideoRenderer: NSObject, VideoRenderer { class LKVideoRenderer: NSObject, VideoRenderer {
var data: UnsafeRawPointer var data: UnsafeRawPointer
var onFrame: @convention(c) (UnsafeRawPointer, CVPixelBuffer) -> Void var onFrame: @convention(c) (UnsafeRawPointer, CVPixelBuffer) -> Bool
var onDrop: @convention(c) (UnsafeRawPointer) -> Void var onDrop: @convention(c) (UnsafeRawPointer) -> Void
var adaptiveStreamIsEnabled: Bool = false var adaptiveStreamIsEnabled: Bool = false
var adaptiveStreamSize: CGSize = .zero var adaptiveStreamSize: CGSize = .zero
weak var track: VideoTrack?
init(data: UnsafeRawPointer, onFrame: @escaping @convention(c) (UnsafeRawPointer, CVPixelBuffer) -> Void, onDrop: @escaping @convention(c) (UnsafeRawPointer) -> Void) { init(data: UnsafeRawPointer, onFrame: @escaping @convention(c) (UnsafeRawPointer, CVPixelBuffer) -> Bool, onDrop: @escaping @convention(c) (UnsafeRawPointer) -> Void) {
self.data = data self.data = data
self.onFrame = onFrame self.onFrame = onFrame
self.onDrop = onDrop self.onDrop = onDrop
@ -50,7 +51,11 @@ class LKVideoRenderer: NSObject, VideoRenderer {
func renderFrame(_ frame: RTCVideoFrame?) { func renderFrame(_ frame: RTCVideoFrame?) {
let buffer = frame?.buffer as? RTCCVPixelBuffer let buffer = frame?.buffer as? RTCCVPixelBuffer
if let pixelBuffer = buffer?.pixelBuffer { if let pixelBuffer = buffer?.pixelBuffer {
self.onFrame(self.data, pixelBuffer) if !self.onFrame(self.data, pixelBuffer) {
DispatchQueue.main.async {
self.track?.remove(videoRenderer: self)
}
}
} }
} }
} }
@ -99,7 +104,7 @@ public func LKRoomPublishVideoTrack(room: UnsafeRawPointer, track: UnsafeRawPoin
public func LKRoomUnpublishTrack(room: UnsafeRawPointer, publication: UnsafeRawPointer) { public func LKRoomUnpublishTrack(room: UnsafeRawPointer, publication: UnsafeRawPointer) {
let room = Unmanaged<Room>.fromOpaque(room).takeUnretainedValue() let room = Unmanaged<Room>.fromOpaque(room).takeUnretainedValue()
let publication = Unmanaged<LocalTrackPublication>.fromOpaque(publication).takeUnretainedValue() let publication = Unmanaged<LocalTrackPublication>.fromOpaque(publication).takeUnretainedValue()
room.localParticipant?.unpublish(publication: publication) let _ = room.localParticipant?.unpublish(publication: publication)
} }
@_cdecl("LKRoomVideoTracksForRemoteParticipant") @_cdecl("LKRoomVideoTracksForRemoteParticipant")
@ -123,7 +128,7 @@ public func LKCreateScreenShareTrackForDisplay(display: UnsafeMutableRawPointer)
} }
@_cdecl("LKVideoRendererCreate") @_cdecl("LKVideoRendererCreate")
public func LKVideoRendererCreate(data: UnsafeRawPointer, onFrame: @escaping @convention(c) (UnsafeRawPointer, CVPixelBuffer) -> Void, onDrop: @escaping @convention(c) (UnsafeRawPointer) -> Void) -> UnsafeMutableRawPointer { public func LKVideoRendererCreate(data: UnsafeRawPointer, onFrame: @escaping @convention(c) (UnsafeRawPointer, CVPixelBuffer) -> Bool, onDrop: @escaping @convention(c) (UnsafeRawPointer) -> Void) -> UnsafeMutableRawPointer {
Unmanaged.passRetained(LKVideoRenderer(data: data, onFrame: onFrame, onDrop: onDrop)).toOpaque() Unmanaged.passRetained(LKVideoRenderer(data: data, onFrame: onFrame, onDrop: onDrop)).toOpaque()
} }
@ -131,6 +136,7 @@ public func LKVideoRendererCreate(data: UnsafeRawPointer, onFrame: @escaping @co
public func LKVideoTrackAddRenderer(track: UnsafeRawPointer, renderer: UnsafeRawPointer) { public func LKVideoTrackAddRenderer(track: UnsafeRawPointer, renderer: UnsafeRawPointer) {
let track = Unmanaged<Track>.fromOpaque(track).takeUnretainedValue() as! VideoTrack let track = Unmanaged<Track>.fromOpaque(track).takeUnretainedValue() as! VideoTrack
let renderer = Unmanaged<LKVideoRenderer>.fromOpaque(renderer).takeRetainedValue() let renderer = Unmanaged<LKVideoRenderer>.fromOpaque(renderer).takeRetainedValue()
renderer.track = track
track.add(videoRenderer: renderer) track.add(videoRenderer: renderer)
} }

View file

@ -60,7 +60,6 @@ fn main() {
let remote_tracks = room_b.remote_video_tracks("test-participant-1"); let remote_tracks = room_b.remote_video_tracks("test-participant-1");
assert_eq!(remote_tracks.len(), 1); assert_eq!(remote_tracks.len(), 1);
assert_eq!(remote_tracks[0].publisher_id(), "test-participant-1"); assert_eq!(remote_tracks[0].publisher_id(), "test-participant-1");
dbg!(track.sid());
assert_eq!(track.publisher_id(), "test-participant-1"); assert_eq!(track.publisher_id(), "test-participant-1");
} else { } else {
panic!("unexpected message"); panic!("unexpected message");

View file

@ -55,7 +55,7 @@ extern "C" {
fn LKVideoRendererCreate( fn LKVideoRendererCreate(
callback_data: *mut c_void, callback_data: *mut c_void,
on_frame: extern "C" fn(callback_data: *mut c_void, frame: CVImageBufferRef), on_frame: extern "C" fn(callback_data: *mut c_void, frame: CVImageBufferRef) -> bool,
on_drop: extern "C" fn(callback_data: *mut c_void), on_drop: extern "C" fn(callback_data: *mut c_void),
) -> *const c_void; ) -> *const c_void;
@ -364,32 +364,43 @@ impl RemoteVideoTrack {
&self.publisher_id &self.publisher_id
} }
pub fn add_renderer<F>(&self, callback: F) pub fn frames(&self) -> async_broadcast::Receiver<Frame> {
where extern "C" fn on_frame(callback_data: *mut c_void, frame: CVImageBufferRef) -> bool {
F: 'static + Send + Sync + FnMut(Frame),
{
extern "C" fn on_frame<F>(callback_data: *mut c_void, frame: CVImageBufferRef)
where
F: FnMut(Frame),
{
unsafe { unsafe {
let tx = Box::from_raw(callback_data as *mut async_broadcast::Sender<Frame>);
let buffer = CVImageBuffer::wrap_under_get_rule(frame); let buffer = CVImageBuffer::wrap_under_get_rule(frame);
let callback = &mut *(callback_data as *mut F); let result = tx.try_broadcast(Frame(buffer));
callback(Frame(buffer)); let _ = Box::into_raw(tx);
match result {
Ok(_) => true,
Err(async_broadcast::TrySendError::Closed(_))
| Err(async_broadcast::TrySendError::Inactive(_)) => {
log::warn!("no active receiver for frame");
false
}
Err(async_broadcast::TrySendError::Full(_)) => {
log::warn!("skipping frame as receiver is not keeping up");
true
}
}
} }
} }
extern "C" fn on_drop<F>(callback_data: *mut c_void) { extern "C" fn on_drop(callback_data: *mut c_void) {
unsafe { unsafe {
let _ = Box::from_raw(callback_data as *mut F); let _ = Box::from_raw(callback_data as *mut async_broadcast::Sender<Frame>);
} }
} }
let callback_data = Box::into_raw(Box::new(callback)); let (tx, rx) = async_broadcast::broadcast(64);
unsafe { unsafe {
let renderer = let renderer = LKVideoRendererCreate(
LKVideoRendererCreate(callback_data as *mut c_void, on_frame::<F>, on_drop::<F>); Box::into_raw(Box::new(tx)) as *mut c_void,
on_frame,
on_drop,
);
LKVideoTrackAddRenderer(self.native_track, renderer); LKVideoTrackAddRenderer(self.native_track, renderer);
rx
} }
} }
} }
@ -422,6 +433,7 @@ impl Drop for MacOSDisplay {
} }
} }
#[derive(Clone)]
pub struct Frame(CVImageBuffer); pub struct Frame(CVImageBuffer);
impl Frame { impl Frame {

View file

@ -1,8 +1,8 @@
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use async_trait::async_trait; use async_trait::async_trait;
use collections::HashMap; use collections::HashMap;
use futures::{Stream, StreamExt}; use futures::Stream;
use gpui::executor::{self, Background}; use gpui::executor::Background;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use live_kit_server::token; use live_kit_server::token;
use media::core_video::CVImageBuffer; use media::core_video::CVImageBuffer;
@ -160,7 +160,6 @@ impl TestServer {
sid: nanoid::nanoid!(17), sid: nanoid::nanoid!(17),
publisher_id: identity.clone(), publisher_id: identity.clone(),
frames_rx: local_track.frames_rx.clone(), frames_rx: local_track.frames_rx.clone(),
background: self.background.clone(),
})); }));
for (id, client_room) in &room.client_rooms { for (id, client_room) in &room.client_rooms {
@ -353,7 +352,6 @@ pub struct RemoteVideoTrack {
sid: Sid, sid: Sid,
publisher_id: Sid, publisher_id: Sid,
frames_rx: async_broadcast::Receiver<Frame>, frames_rx: async_broadcast::Receiver<Frame>,
background: Arc<executor::Background>,
} }
impl RemoteVideoTrack { impl RemoteVideoTrack {
@ -365,18 +363,8 @@ impl RemoteVideoTrack {
&self.publisher_id &self.publisher_id
} }
pub fn add_renderer<F>(&self, mut callback: F) pub fn frames(&self) -> async_broadcast::Receiver<Frame> {
where self.frames_rx.clone()
F: 'static + Send + Sync + FnMut(Frame),
{
let mut frames_rx = self.frames_rx.clone();
self.background
.spawn(async move {
while let Some(frame) = frames_rx.next().await {
callback(frame)
}
})
.detach();
} }
} }