Skip to content

Commit

Permalink
Merge branch 'main' into use_core_foundation_types
Browse files Browse the repository at this point in the history
* main:
  v0.2.6
  Update rust.yml
  chore: fix warnings
  fix error handling (#37)
  cargo fmt (#38)
  docs: add krzykro2 as a contributor for code (#36)
  make safe StreamConfiguration defaults equal to unsafe (#35)
  Revert "test ci"
  test ci
  • Loading branch information
1313 committed Feb 16, 2024
2 parents 579e8de + 3e0df8b commit 1787d63
Show file tree
Hide file tree
Showing 17 changed files with 542 additions and 13 deletions.
9 changes: 9 additions & 0 deletions .all-contributorsrc
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,15 @@
"contributions": [
"code"
]
},
{
"login": "krzykro2",
"name": "Kris Krolak",
"avatar_url": "https://avatars.githubusercontent.com/u/6817875?v=4",
"profile": "https://github.com/krzykro2",
"contributions": [
"code"
]
}
],
"contributorsPerLine": 7,
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ env:

jobs:
build:
runs-on: macos-13
runs-on: macos-14
steps:
- uses: actions/checkout@v3
- name: Update rust
Expand Down
15 changes: 12 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,24 @@ All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.2.6] - 2024-02-06

### Fixed

- [#37](https://github.com/svtlabs/screencapturekit-rs/issues/37) Fix error handling
- [#35](https://github.com/svtlabs/screencapturekit-rs/issues/35) Make safe StreamConfiguration defaults equal to unsafe

## [0.2.5] - 2024-01-25

### Fixed

- 0.2.4 had an issue with the configuration. This is fixed now.

## [0.2.4] - 2024-01-23

### Fixed

- [#34](https://github.com/svtlabs/screencapturekit-rs/issues/34) minimum_frame_interval not working as expected
- [#33](https://github.com/svtlabs/screencapturekit-rs/issues/33) Can no longer import SCFrameStatus

Expand Down Expand Up @@ -50,13 +60,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [0.1.0] - 2023-08-21



### Added

- Initial commit with prototype version

[unreleased]: https://github.com/svtlabs/screencapturekit-rs/compare/v0.2.5...HEAD
[unreleased]: https://github.com/svtlabs/screencapturekit-rs/compare/v0.2.6...HEAD
[0.2.6]: https://github.com/svtlabs/screencapturekit-rs/compare/v0.2.5...v0.2.6
[0.2.5]: https://github.com/svtlabs/screencapturekit-rs/compare/v0.2.4...v0.2.5
[0.2.4]: https://github.com/svtlabs/screencapturekit-rs/compare/v0.2.3...v0.2.4
[0.2.3]: https://github.com/svtlabs/screencapturekit-rs/compare/v0.2.2...v0.2.3
Expand Down
8 changes: 3 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
[package]
name = "screencapturekit"


keywords = ["screencapture", "screencapturekit", "macos"]
description = "Rust wrapper for apple's ScreenCaptureKit"
name = "screencapturekit"
categories = [
"external-ffi-bindings",
"multimedia",
Expand All @@ -14,7 +16,6 @@ homepage = "https://github.com/svtlabs"
edition = "2021"
rust-version = "1.72"
version = "0.3.0"
keywords = ["screencapture", "screencapturekit", "macos"]
license = "MIT OR Apache-2.0"

[lib]
Expand All @@ -30,6 +31,3 @@ dispatch = "0.2"
once_cell = "1"
core-foundation = { version = "0.9", features = ["mac_os_10_8_features"] }
core-graphics = { version = "0.23" }

[[example]]
name = "test_fps"
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# screencapturekit-rs
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
[![All Contributors](https://img.shields.io/badge/all_contributors-4-orange.svg?style=flat-square)](#contributors-)
[![All Contributors](https://img.shields.io/badge/all_contributors-5-orange.svg?style=flat-square)](#contributors-)
<!-- ALL-CONTRIBUTORS-BADGE:END -->
## Introduction

Expand Down Expand Up @@ -87,6 +87,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<td align="center" valign="top" width="14.28%"><a href="http://64p.org/"><img src="https://avatars.githubusercontent.com/u/21084?v=4?s=100" width="100px;" alt="Tokuhiro Matsuno"/><br /><sub><b>Tokuhiro Matsuno</b></sub></a><br /><a href="https://github.com/svtlabs/screencapturekit-rs/commits?author=tokuhirom" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/bigduu"><img src="https://avatars.githubusercontent.com/u/18681616?v=4?s=100" width="100px;" alt="bigduu"/><br /><sub><b>bigduu</b></sub></a><br /><a href="https://github.com/svtlabs/screencapturekit-rs/commits?author=bigduu" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Pranav2612000"><img src="https://avatars.githubusercontent.com/u/20909078?v=4?s=100" width="100px;" alt="Pranav Joglekar"/><br /><sub><b>Pranav Joglekar</b></sub></a><br /><a href="https://github.com/svtlabs/screencapturekit-rs/commits?author=Pranav2612000" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/krzykro2"><img src="https://avatars.githubusercontent.com/u/6817875?v=4?s=100" width="100px;" alt="Kris Krolak"/><br /><sub><b>Kris Krolak</b></sub></a><br /><a href="https://github.com/svtlabs/screencapturekit-rs/commits?author=krzykro2" title="Code">💻</a></td>
</tr>
</tbody>
</table>
Expand Down
13 changes: 13 additions & 0 deletions screencapturekit-sys/src/as_ptr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
pub trait AsPtr {
fn as_ptr(&self) -> *const Self {
self as *const Self
}
}
pub trait AsMutPtr {
fn as_mut_ptr(&self) -> *mut Self {
self as *const _ as *mut Self
}
}

impl<T> AsPtr for T {}
impl<T> AsMutPtr for T {}
16 changes: 16 additions & 0 deletions screencapturekit-sys/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
pub mod as_ptr;
pub mod audio_buffer;
pub mod cm_block_buffer_ref;
pub mod cm_format_description_ref;
pub mod cm_sample_buffer_ref;
pub mod content_filter;
pub mod cv_image_buffer_ref;
pub mod cv_pixel_buffer_ref;
pub mod macros;
pub mod os_types;
pub mod sc_stream_frame_info;
pub mod shareable_content;
pub mod stream;
pub mod stream_configuration;
pub mod stream_error_handler;
pub mod stream_output_handler;
5 changes: 5 additions & 0 deletions screencapturekit-sys/src/os_types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pub mod base;
pub mod four_char_code;
pub mod geometry;
pub mod graphics;
pub mod rc;
78 changes: 78 additions & 0 deletions screencapturekit-sys/src/os_types/four_char_code.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
use std::{
ascii,
fmt::{self},
};

#[repr(transparent)]
#[derive(Copy, Clone, Default, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub struct FourCharCode(u32);

impl fmt::Display for FourCharCode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.format())
}
}

impl fmt::Debug for FourCharCode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "\"")?;
f.write_str(&self.format())?;
write!(f, "\"")
}
}

impl FourCharCode {
#[inline]
fn format(&self) -> String {
// Format as escaped ASCII string.

let raw = self
.into_chars()
.into_iter()
.flat_map(ascii::escape_default)
.collect::<Vec<u8>>();

String::from_utf8(raw).unwrap()
}
/// Returns an instance from the integer value.
#[inline]
pub const fn from_int(int: u32) -> Self {
Self(int)
}

/// Returns an instance from the 4-character code.
#[inline]
pub const fn from_chars(chars: [u8; 4]) -> Self {
Self(u32::from_be_bytes(chars))
}

/// Returns this descriptor's integer value.
#[inline]
pub const fn into_int(self) -> u32 {
self.0
}

/// Returns this descriptor's 4-character code.
#[inline]
pub const fn into_chars(self) -> [u8; 4] {
self.0.to_be_bytes()
}

/// Returns `true` if all of the characters in `self` are ASCII.
#[inline]
pub const fn is_ascii(&self) -> bool {
const NON_ASCII: u32 = u32::from_be_bytes([128; 4]);

self.0 & NON_ASCII == 0
}

/// Returns `true` if all of the characters in `self` are ASCII graphic
/// characters: U+0021 '!' ..= U+007E '~'.
#[inline]
pub const fn is_ascii_graphic(&self) -> bool {
matches!(
self.into_chars(),
[b'!'..=b'~', b'!'..=b'~', b'!'..=b'~', b'!'..=b'~'],
)
}
}
112 changes: 112 additions & 0 deletions screencapturekit-sys/src/stream_error_handler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
use std::sync::Once;

use objc::{
class,
declare::ClassDecl,
runtime::{Class, Object, Sel},
Message, *,
};
use objc_foundation::INSObject;
use objc_id::Id;

pub trait UnsafeSCStreamError: Send + Sync + 'static {
fn handle_error(&self);
}

#[repr(C)]
pub(crate) struct UnsafeSCStreamErrorHandler {}

unsafe impl Message for UnsafeSCStreamErrorHandler {}

use once_cell::sync::Lazy;
use std::collections::HashMap;
use std::sync::RwLock;
static ERROR_HANDLERS: Lazy<RwLock<HashMap<usize, Box<dyn UnsafeSCStreamError + Send + Sync>>>> =
Lazy::new(|| RwLock::new(HashMap::new()));

impl INSObject for UnsafeSCStreamErrorHandler {
fn class() -> &'static Class {
static REGISTER_UNSAFE_SC_ERROR_HANDLER: Once = Once::new();
REGISTER_UNSAFE_SC_ERROR_HANDLER.call_once(|| {
let mut decl = ClassDecl::new("SCStreamErrorHandler", class!(NSObject)).unwrap();
decl.add_ivar::<usize>("_hash");

extern "C" fn stream_error(
this: &mut Object,
_cmd: Sel,
_stream: *mut Object,
_error: *mut Object,
) {
unsafe {
let hash = this.get_ivar::<usize>("_hash");
let lookup = ERROR_HANDLERS.read().unwrap();
let error_handler = lookup.get(hash).unwrap();
error_handler.handle_error();
};
}
unsafe {
let stream_error_method: extern "C" fn(&mut Object, Sel, *mut Object, *mut Object) =
stream_error;

decl.add_method(sel!(stream:didStopWithError:), stream_error_method);
}

decl.register();
});
class!(SCStreamErrorHandler)
}
}

impl UnsafeSCStreamErrorHandler {
fn store_error_handler(&mut self, error_handler: impl UnsafeSCStreamError) {
unsafe {
let obj = &mut *(self as *mut _ as *mut Object);
let hash = self.hash_code();
ERROR_HANDLERS
.write()
.unwrap()
.insert(hash, Box::new(error_handler));
obj.set_ivar("_hash", hash);
}
}
// Error handlers passed into here will currently live forever inside the statically
// allocated map.
// TODO: Remove the handler from the HashMap whenever the associated stream is dropped.
pub fn init(error_handler: impl UnsafeSCStreamError) -> Id<Self> {
let mut handle = Self::new();
handle.store_error_handler(error_handler);
handle
}
}

#[cfg(test)]
mod tests {
use std::ptr;
use std::sync::mpsc::{sync_channel, SyncSender};

use super::*;

struct TestHandler {
error_tx: SyncSender<()>,
}
impl UnsafeSCStreamError for TestHandler {
fn handle_error(&self) {
eprintln!("ERROR!");
if let Err(e) = self.error_tx.send(()) {
panic!("can't send error message back on the channel: {:?}", e);
}
}
}

#[test]
fn test_sc_stream_error_handler() {
let (error_tx, error_rx) = sync_channel(1);
let handle = UnsafeSCStreamErrorHandler::init(TestHandler { error_tx });
unsafe {
msg_send![handle, stream: ptr::null_mut::<Object>() didStopWithError: ptr::null_mut::<Object>()]
}
if let Err(e) = error_rx.recv_timeout(std::time::Duration::from_millis(250)) {
panic!("failed to hear back from the error channel: {:?}", e);
}
}
}
28 changes: 28 additions & 0 deletions screencapturekit/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[package]
name = "screencapturekit"
description = "Safe wrapper for Apple's ScreenCaptureKit"
categories = [
"api-bindings",
"multimedia",
"multimedia::video",
"os::macos-apis",
]
readme = "README.md"
repository = "https://github.com/svtlabs/screencapturekit-rs/tree/main/screencapturekit"

edition.workspace = true
version.workspace = true
authors.workspace = true
license.workspace = true
keywords.workspace = true
homepage.workspace = true
rust-version.workspace = true

[features]
ci = []

[lib]
path = "./src/lib.rs"

[dependencies]
screencapturekit-sys = { version = "0.2.6", path = "../screencapturekit-sys" }
24 changes: 24 additions & 0 deletions screencapturekit/src/sc_error_handler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
use screencapturekit_sys::stream_error_handler::UnsafeSCStreamError;

// TODO: It might make sense to be a little more precise with lifetimes, than 'static.
// The lifetime could be potentially only as long as the relevant Stream, if the
// handler was dropped correctly together with the stream. For now the handler is never
// dropped and lives forever inside a statically allocated HashMap. See the relevant
// code in screencapturekit-sys crate.
pub trait StreamErrorHandler: Send + Sync + 'static {
fn on_error(&self);
}

pub(crate) struct StreamErrorHandlerWrapper<T: StreamErrorHandler>(T);

impl<T: StreamErrorHandler> StreamErrorHandlerWrapper<T> {
pub fn new(error_handler: T) -> Self {
StreamErrorHandlerWrapper(error_handler)
}
}

impl<T: StreamErrorHandler> UnsafeSCStreamError for StreamErrorHandlerWrapper<T> {
fn handle_error(&self) {
self.0.on_error();
}
}
Loading

0 comments on commit 1787d63

Please sign in to comment.