Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 56 additions & 48 deletions .claude/settings.local.json
Original file line number Diff line number Diff line change
@@ -1,50 +1,58 @@
{
"permissions": {
"allow": [
"Bash(pnpm typecheck:*)",
"Bash(pnpm lint:*)",
"Bash(pnpm build:*)",
"Bash(cargo check:*)",
"Bash(cargo fmt:*)",
"Bash(pnpm format:*)",
"Bash(pnpm exec biome check:*)",
"Bash(grep:*)",
"Bash(cargo metadata:*)",
"Bash(ffprobe:*)",
"Bash(ls:*)",
"Bash(find:*)",
"Bash(cat:*)",
"WebFetch(domain:raw.githubusercontent.com)",
"WebFetch(domain:api.github.com)",
"Bash(cargo doc:*)",
"Bash(cargo clippy:*)",
"Bash(python3:*)",
"Bash(cargo run:*)",
"WebSearch",
"Bash(xargs ls:*)",
"WebFetch(domain:ffmpeg.org)",
"Bash(git log:*)",
"Bash(tree:*)",
"Bash(tail:*)",
"Bash(pnpm typecheck:desktop:*)",
"Bash(pnpm exec tsc:*)",
"Bash(pnpm biome check:*)",
"Bash(pnpm --dir apps/desktop exec tsc:*)",
"Bash(xxd:*)",
"Bash(git checkout:*)",
"WebFetch(domain:www.npmjs.com)",
"Bash(pnpm install:*)",
"Bash(pnpm --dir apps/desktop exec biome check:*)",
"Bash(pnpm --dir apps/desktop exec biome format:*)",
"Bash(echo:*)",
"Bash(pnpm exec biome:*)",
"Bash(rustfmt:*)",
"Bash(cargo tree:*)",
"WebFetch(domain:github.com)",
"WebFetch(domain:docs.rs)",
"WebFetch(domain:gix.github.io)"
],
"deny": [],
"ask": []
}
"permissions": {
"allow": [
"Bash(pnpm typecheck:*)",
"Bash(pnpm lint:*)",
"Bash(pnpm build:*)",
"Bash(cargo check:*)",
"Bash(cargo fmt:*)",
"Bash(pnpm format:*)",
"Bash(pnpm exec biome check:*)",
"Bash(grep:*)",
"Bash(cargo metadata:*)",
"Bash(ffprobe:*)",
"Bash(ls:*)",
"Bash(find:*)",
"Bash(cat:*)",
"WebFetch(domain:raw.githubusercontent.com)",
"WebFetch(domain:api.github.com)",
"Bash(cargo doc:*)",
"Bash(cargo clippy:*)",
"Bash(python3:*)",
"Bash(cargo run:*)",
"WebSearch",
"Bash(xargs ls:*)",
"WebFetch(domain:ffmpeg.org)",
"Bash(git log:*)",
"Bash(tree:*)",
"Bash(tail:*)",
"Bash(pnpm typecheck:desktop:*)",
"Bash(pnpm exec tsc:*)",
"Bash(pnpm biome check:*)",
"Bash(pnpm --dir apps/desktop exec tsc:*)",
"Bash(xxd:*)",
"Bash(git checkout:*)",
"WebFetch(domain:www.npmjs.com)",
"Bash(pnpm install:*)",
"Bash(pnpm --dir apps/desktop exec biome check:*)",
"Bash(pnpm --dir apps/desktop exec biome format:*)",
"Bash(echo:*)",
"Bash(pnpm exec biome:*)",
"Bash(rustfmt:*)",
"Bash(cargo tree:*)",
"WebFetch(domain:github.com)",
"WebFetch(domain:docs.rs)",
"WebFetch(domain:gix.github.io)",
"Bash(ffmpeg:*)",
"Bash(DYLD_LIBRARY_PATH=/opt/homebrew/lib:$DYLD_LIBRARY_PATH ./target/release/examples/memory-leak-detector:*)",
"Bash(ln:*)",
"Bash(./target/release/examples/memory-leak-detector:*)",
"Bash(cargo build:*)",
"Bash(footprint:*)",
"Bash(RUST_LOG=info,cap_recording=debug ./target/release/examples/memory-leak-detector:*)",
"Bash(git rm:*)"
],
"deny": [],
"ask": []
}
}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,4 @@ tauri.windows.conf.json
.cursor
.env*.local
.docs/
.claude/
69 changes: 45 additions & 24 deletions crates/camera-ffmpeg/src/macos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ pub enum AsFFmpegError {
SwscaleFallbackFailed { format: String, reason: String },
#[error("{0}")]
Native(#[from] cidre::os::Error),
#[error("No image buffer available")]
NoImageBuffer,
}

struct FourccInfo {
Expand Down Expand Up @@ -53,17 +55,39 @@ static FALLBACK_WARNING_LOGGED: AtomicBool = AtomicBool::new(false);

impl CapturedFrameExt for CapturedFrame {
fn as_ffmpeg(&self) -> Result<ffmpeg::frame::Video, AsFFmpegError> {
let native = self.native().clone();

let width = native.image_buf().width();
let height = native.image_buf().height();
let native = self.native();

let mut image_buf = native.image_buf().ok_or(AsFFmpegError::NoImageBuffer)?;

let width = image_buf.width();
let height = image_buf.height();
let plane0_stride = image_buf.plane_bytes_per_row(0);
let plane1_stride = image_buf.plane_bytes_per_row(1);
let plane_count = image_buf.plane_count();
let plane_info: [(usize, usize, usize); 3] = [
(
image_buf.plane_bytes_per_row(0),
image_buf.plane_height(0),
image_buf.plane_width(0),
),
(
image_buf.plane_bytes_per_row(1),
image_buf.plane_height(1),
image_buf.plane_width(1),
),
(
image_buf.plane_bytes_per_row(2),
image_buf.plane_height(2),
image_buf.plane_width(2),
),
];

let format_desc = native.sample_buf().format_desc().unwrap();

let mut this = native.image_buf().clone();

let bytes_lock =
ImageBufExt::base_addr_lock(this.as_mut(), cv::pixel_buffer::LockFlags::READ_ONLY)?;
let bytes_lock = ImageBufExt::base_addr_lock(
image_buf.as_mut(),
cv::pixel_buffer::LockFlags::READ_ONLY,
)?;

let res = match cidre::four_cc_to_str(&mut format_desc.media_sub_type().to_be_bytes()) {
"2vuy" => {
Expand All @@ -73,7 +97,7 @@ impl CapturedFrameExt for CapturedFrame {
height as u32,
);

let src_stride = native.image_buf().plane_bytes_per_row(0);
let src_stride = plane0_stride;
let dest_stride = ff_frame.stride(0);

let src_bytes = bytes_lock.plane_data(0);
Expand All @@ -96,7 +120,7 @@ impl CapturedFrameExt for CapturedFrame {
height as u32,
);

let src_stride = native.image_buf().plane_bytes_per_row(0);
let src_stride = plane0_stride;
let dest_stride = ff_frame.stride(0);

let src_bytes = bytes_lock.plane_data(0);
Expand All @@ -110,7 +134,7 @@ impl CapturedFrameExt for CapturedFrame {
dest_row.copy_from_slice(src_row);
}

let src_stride = native.image_buf().plane_bytes_per_row(1);
let src_stride = plane1_stride;
let dest_stride = ff_frame.stride(1);

let src_bytes = bytes_lock.plane_data(1);
Expand All @@ -133,7 +157,7 @@ impl CapturedFrameExt for CapturedFrame {
height as u32,
);

let src_stride = native.image_buf().plane_bytes_per_row(0);
let src_stride = plane0_stride;
let dest_stride = ff_frame.stride(0);

let src_bytes = bytes_lock.plane_data(0);
Expand All @@ -156,7 +180,7 @@ impl CapturedFrameExt for CapturedFrame {
height as u32,
);

let src_stride = native.image_buf().plane_bytes_per_row(0);
let src_stride = plane0_stride;
let dest_stride = ff_frame.stride(0);

let src_bytes = bytes_lock.plane_data(0);
Expand All @@ -179,7 +203,7 @@ impl CapturedFrameExt for CapturedFrame {
height as u32,
);

let src_stride = native.image_buf().plane_bytes_per_row(0);
let src_stride = plane0_stride;
let dest_stride = ff_frame.stride(0);

let src_bytes = bytes_lock.plane_data(0);
Expand All @@ -202,7 +226,7 @@ impl CapturedFrameExt for CapturedFrame {
height as u32,
);

let src_stride = native.image_buf().plane_bytes_per_row(0);
let src_stride = plane0_stride;
let dest_stride = ff_frame.stride(0);

let src_bytes = bytes_lock.plane_data(0);
Expand All @@ -225,7 +249,7 @@ impl CapturedFrameExt for CapturedFrame {
height as u32,
);

let src_stride = native.image_buf().plane_bytes_per_row(0);
let src_stride = plane0_stride;
let dest_stride = ff_frame.stride(0);

let src_bytes = bytes_lock.plane_data(0);
Expand All @@ -248,7 +272,7 @@ impl CapturedFrameExt for CapturedFrame {
height as u32,
);

let src_stride = native.image_buf().plane_bytes_per_row(0);
let src_stride = plane0_stride;
let dest_stride = ff_frame.stride(0);

let src_bytes = bytes_lock.plane_data(0);
Expand All @@ -265,7 +289,6 @@ impl CapturedFrameExt for CapturedFrame {
ff_frame
}
"y420" => {
let plane_count = native.image_buf().plane_count();
if plane_count < 3 {
return Err(AsFFmpegError::InsufficientPlaneCount {
format: "y420".to_string(),
Expand All @@ -280,15 +303,13 @@ impl CapturedFrameExt for CapturedFrame {
height as u32,
);

for plane in 0..3 {
let src_stride = native.image_buf().plane_bytes_per_row(plane);
for (plane, &(src_stride, plane_height, row_width)) in plane_info.iter().enumerate()
{
let dest_stride = ff_frame.stride(plane);
let plane_height = native.image_buf().plane_height(plane);

let src_bytes = bytes_lock.plane_data(plane);
let dest_bytes = &mut ff_frame.data_mut(plane);

let row_width = native.image_buf().plane_width(plane);
for y in 0..plane_height {
let src_row = &src_bytes[y * src_stride..y * src_stride + row_width];
let dest_row =
Expand All @@ -306,7 +327,7 @@ impl CapturedFrameExt for CapturedFrame {
height as u32,
);

let src_stride = native.image_buf().plane_bytes_per_row(0);
let src_stride = plane0_stride;
let dest_stride = ff_frame.stride(0);

let src_bytes = bytes_lock.plane_data(0);
Expand Down Expand Up @@ -334,7 +355,7 @@ impl CapturedFrameExt for CapturedFrame {
let mut src_frame =
ffmpeg::frame::Video::new(info.pixel, width as u32, height as u32);

let src_stride = native.image_buf().plane_bytes_per_row(0);
let src_stride = plane0_stride;
let dest_stride = src_frame.stride(0);
let src_bytes = bytes_lock.plane_data(0);
let dest_bytes = &mut src_frame.data_mut(0);
Expand Down
14 changes: 6 additions & 8 deletions crates/camera/src/macos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,15 +99,13 @@ pub(super) fn start_capturing_impl(
let queue = dispatch::Queue::new();
let delegate =
CallbackOutputDelegate::with(CallbackOutputDelegateInner::new(Box::new(move |data| {
let Some(image_buf) = data.sample_buf.image_buf() else {
if data.sample_buf.image_buf().is_none() {
return;
};

callback(CapturedFrame {
native: NativeCapturedFrame(image_buf.retained(), data.sample_buf.retained()),
// reference_time: Instant::now(),
native: NativeCapturedFrame(data.sample_buf.retained()),
timestamp: data.timestamp,
// capture_begin_time: Some(data.capture_begin_time),
});
})));

Expand Down Expand Up @@ -202,14 +200,14 @@ impl Debug for AVFoundationError {
}

#[derive(Debug, Clone)]
pub struct NativeCapturedFrame(arc::R<cv::ImageBuf>, arc::R<cm::SampleBuf>);
pub struct NativeCapturedFrame(arc::R<cm::SampleBuf>);

impl NativeCapturedFrame {
pub fn image_buf(&self) -> &arc::R<cv::ImageBuf> {
&self.0
pub fn image_buf(&self) -> Option<arc::R<cv::ImageBuf>> {
self.0.image_buf().map(|b| b.retained())
}

pub fn sample_buf(&self) -> &arc::R<cm::SampleBuf> {
&self.1
&self.0
}
}
Loading