Skip to content

Commit 7e7b139

Browse files
committed
[+] feat: finished capture stream api
1 parent 6a3718f commit 7e7b139

File tree

11 files changed

+453
-73
lines changed

11 files changed

+453
-73
lines changed

lib/mp4m/examples/mp4_processor_two_audios_demo.rs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
use hound::WavReader;
22
use image::{ImageBuffer, Rgb};
33
use mp4m::mp4_processor::{
4-
AudioConfig, AudioFrameType, Mp4Processor, Mp4ProcessorConfigBuilder, VideoConfig,
5-
VideoFrameType,
4+
AudioConfig, Mp4Processor, Mp4ProcessorConfigBuilder, VideoConfig, VideoFrameType,
65
};
76
use recorder::{EncodedFrame, FPS, VideoEncoder};
87
use std::{path::PathBuf, thread, time::Duration};
@@ -88,7 +87,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
8887

8988
// Start processing in a separate thread
9089
let processor_thread = thread::spawn(move || {
91-
if let Err(e) = processor.run_processing_loop() {
90+
if let Err(e) = processor.run_processing_loop(None) {
9291
log::warn!("MP4 processing error: {}", e);
9392
}
9493
});
@@ -120,7 +119,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
120119
// Use f32 samples directly for AAC encoding
121120
let f32_chunk: Vec<f32> = chunk.to_vec();
122121

123-
if let Err(e) = audio_sender1.send(AudioFrameType::Frame(f32_chunk)) {
122+
if let Err(e) = audio_sender1.send(f32_chunk) {
124123
log::warn!("audio sender 1 failed: {e}");
125124
break;
126125
}
@@ -162,7 +161,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
162161
// Use f32 samples directly for AAC encoding
163162
let f32_chunk: Vec<f32> = chunk.to_vec();
164163

165-
if let Err(e) = audio_sender2.send(AudioFrameType::Frame(f32_chunk)) {
164+
if let Err(e) = audio_sender2.send(f32_chunk) {
166165
log::warn!("audio sender 2 failed: {e}");
167166
break;
168167
}
@@ -178,7 +177,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
178177
}
179178

180179
// Generate and send video frames
181-
let mut h264_encoder = VideoEncoder::new(width, height, fps)?;
180+
let mut h264_encoder = VideoEncoder::new(width, height, fps, false)?;
182181
let headers_data = h264_encoder.headers()?.entirety().to_vec();
183182
if let Err(e) = video_sender.send(VideoFrameType::Frame(headers_data)) {
184183
panic!("video sender h264 header failed: {e}");
@@ -228,8 +227,6 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
228227
thread::sleep(Duration::from_secs(1));
229228

230229
_ = video_sender.send(VideoFrameType::End);
231-
_ = audio_sender1.send(AudioFrameType::End);
232-
_ = audio_sender2.send(AudioFrameType::End);
233230

234231
drop(video_sender);
235232
drop(audio_sender1);

lib/recorder/examples/cursor_tracking_basic_demo.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
7373
)?
7474
.with_stable_radius(30)
7575
.with_fast_moving_duration(Duration::from_millis(200))
76-
.with_linear_transition_duration(Duration::from_millis(800))
76+
.with_zoom_transition_duration(Duration::from_millis(800))
7777
.with_max_stable_region_duration(Duration::from_secs(3));
7878

7979
let cursor_tracker = CursorTracker::new(cursor_tracker_config)?;

lib/recorder/examples/cursor_tracking_stable_fast_movement.rs

Lines changed: 95 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,51 @@
11
/*
22
* cursor_tracking_stable_fast_movement.rs - Cursor Tracking Stable State Fast Movement Test
3-
*
3+
*
44
* This example tests the cursor tracking system's behavior during fast movements in stable state,
55
* verifying the following key characteristics:
6-
*
6+
*
77
* Test Flow (3 phases, 10 seconds total):
8-
*
8+
*
99
* 1. Phase 1 (0-3s): Fast movement to trigger zoom_in
10-
* - Large circular fast movements
10+
* - Large circular fast movements
1111
* - Transition from fullscreen (1920x1080) to target size (400x300)
12-
*
12+
*
1313
* 2. Phase 2 (3-6s): Stable cursor in target size state
1414
* - Stable cursor at offset position
1515
* - Maintain target size (400x300)
16-
*
16+
*
1717
* 3. Phase 3 (6-10s): Fast movement in target size state
1818
* - Fast movements while in target size state
1919
* - Verify that zoom_out is NOT triggered back to screen size
20-
*
20+
*
2121
* Test Results:
2222
* - Collected 142 crop region data points
2323
* - Size distribution:
2424
* - Target size (400x300): 122 occurrences (85.9%)
2525
* - Screen size (1920x1080): 1 occurrence (0.7%)
2626
* - Transition sizes: 19 occurrences (13.4%)
27-
*
27+
*
2828
* Key validation: Phase 1 had 120 regions all at target size (400x300), proving that
2929
* fast movements in target size state do NOT trigger zoom_out
30-
*
30+
*
3131
* This example validates the cursor tracker's state stability: once zoomed in to target size,
3232
* even fast movements will keep the system stable and not trigger zoom_out.
3333
*/
3434

3535
use recorder::{CursorTracker, CursorTrackerConfig, bounded};
3636
use screen_capture::{CursorPosition, LogicalSize, Rectangle};
3737
use std::{
38-
sync::{Arc, atomic::AtomicBool, Mutex},
38+
sync::{Arc, Mutex, atomic::AtomicBool},
3939
thread,
4040
time::{Duration, Instant},
4141
};
4242

4343
fn main() -> Result<(), Box<dyn std::error::Error>> {
4444
env_logger::init();
4545

46-
log::info!("Starting Stable Fast Movement test: Fast movement in stable state should maintain target_size...");
46+
log::info!(
47+
"Starting Stable Fast Movement test: Fast movement in stable state should maintain target_size..."
48+
);
4749

4850
let screen_size = LogicalSize {
4951
width: 1920,
@@ -59,17 +61,22 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
5961
let (crop_sender, crop_receiver) = bounded(1000);
6062

6163
let stop_sig = Arc::new(AtomicBool::new(false));
62-
64+
6365
let crop_regions = Arc::new(Mutex::new(Vec::new()));
6466
let crop_regions_for_validation = crop_regions.clone();
6567

6668
// Configuration identical to successful Phase 3
67-
let cursor_tracker_config =
68-
CursorTrackerConfig::new(screen_size, target_size, crop_sender, cursor_receiver, stop_sig.clone())?
69-
.with_stable_radius(30)
70-
.with_fast_moving_duration(Duration::from_millis(200))
71-
.with_linear_transition_duration(Duration::from_millis(800))
72-
.with_max_stable_region_duration(Duration::from_secs(3));
69+
let cursor_tracker_config = CursorTrackerConfig::new(
70+
screen_size,
71+
target_size,
72+
crop_sender,
73+
cursor_receiver,
74+
stop_sig.clone(),
75+
)?
76+
.with_stable_radius(30)
77+
.with_fast_moving_duration(Duration::from_millis(200))
78+
.with_zoom_transition_duration(Duration::from_millis(800))
79+
.with_max_stable_region_duration(Duration::from_secs(3));
7380

7481
let cursor_tracker = CursorTracker::new(cursor_tracker_config)?;
7582

@@ -95,7 +102,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
95102
region.width as f64,
96103
region.height as f64
97104
);
98-
105+
99106
// Collect data for validation
100107
if let Ok(mut regions) = crop_regions_for_logging.lock() {
101108
regions.push((Instant::now(), region));
@@ -110,7 +117,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
110117
}
111118

112119
log::info!("Stable Fast Movement test completed!");
113-
120+
114121
// Perform validation
115122
validate_stable_fast_movement_results(
116123
&crop_regions_for_validation,
@@ -133,13 +140,13 @@ fn simulate_stable_fast_movement(
133140
let center_y = screen_size.height as f64 / 2.0;
134141

135142
log::info!("🎯 Testing cursor tracking: fast movement → stop → fast movement");
136-
143+
137144
// Phase 1 (0-3 seconds): Fast cursor movement to trigger zoom_in
138145
log::info!("🏃 Phase 1: Fast movement for 3 seconds to trigger zoom_in");
139146
let phase1_end = start_time + Duration::from_secs(3);
140147
while Instant::now() < phase1_end {
141148
let current_time = Instant::now();
142-
149+
143150
// Rapid cursor movement in large circles
144151
let progress = current_time.duration_since(start_time).as_secs_f64();
145152
let angle = progress * std::f64::consts::PI * 6.0; // Fast rotation
@@ -159,7 +166,7 @@ fn simulate_stable_fast_movement(
159166
let _ = sender.send((current_time, cursor_pos));
160167
thread::sleep(frame_interval);
161168
}
162-
169+
163170
// Phase 2 (3-6 seconds): Stable cursor in target_size state
164171
log::info!("⏸️ Phase 2: Stable cursor in target_size state");
165172
let phase2_end = start_time + Duration::from_secs(6);
@@ -176,15 +183,15 @@ fn simulate_stable_fast_movement(
176183
};
177184
let _ = sender.send((Instant::now(), cursor_pos));
178185
}
179-
186+
180187
thread::sleep(frame_interval);
181188
}
182-
189+
183190
// Phase 3 (6-10 seconds): Fast movement in target_size state (should NOT trigger zoom_out)
184191
log::info!("🏃 Phase 3: Fast movement in target_size state - should stay stable");
185192
while Instant::now() < start_time + Duration::from_secs(10) {
186193
let current_time = Instant::now();
187-
194+
188195
// Fast movement but system should stay in target_size
189196
let progress = current_time.duration_since(start_time).as_secs_f64();
190197
let angle = progress * std::f64::consts::PI * 8.0;
@@ -214,80 +221,106 @@ fn validate_stable_fast_movement_results(
214221
target_size: &LogicalSize,
215222
) -> Result<(), Box<dyn std::error::Error>> {
216223
log::info!("Starting stable fast movement validation...");
217-
224+
218225
let regions = crop_regions.lock().unwrap();
219-
226+
220227
log::info!("Collected {} crop region data points", regions.len());
221-
228+
222229
if regions.is_empty() {
223230
return Err("No crop regions collected".into());
224231
}
225-
232+
226233
// Analyze the size distribution and timeline
227234
let mut target_size_count = 0;
228235
let mut screen_size_count = 0;
229236
let mut transition_count = 0;
230237
let mut size_timeline = Vec::new();
231238
let mut phase_regions = std::collections::HashMap::new();
232-
239+
233240
for (timestamp, region) in regions.iter() {
234241
let size = (region.width, region.height);
235242
let time_point = timestamp.elapsed().as_secs_f32();
236-
243+
237244
if size == (target_size.width, target_size.height) {
238245
target_size_count += 1;
239246
} else if size == (screen_size.width, screen_size.height) {
240247
screen_size_count += 1;
241248
} else {
242249
transition_count += 1;
243250
}
244-
251+
245252
size_timeline.push((time_point, size));
246-
253+
247254
// Categorize by phases based on actual observed timing
248-
let phase = if time_point < 5.0 { "Phase 1" } // Most of the test (including zoom_in transition)
249-
else if time_point < 9.8 { "Phase 2" } // Stable target size phase
250-
else { "Phase 3" }; // Final screen size region
251-
252-
phase_regions.entry(phase).or_insert_with(Vec::new).push(size);
255+
let phase = if time_point < 5.0 {
256+
"Phase 1"
257+
}
258+
// Most of the test (including zoom_in transition)
259+
else if time_point < 9.8 {
260+
"Phase 2"
261+
}
262+
// Stable target size phase
263+
else {
264+
"Phase 3"
265+
}; // Final screen size region
266+
267+
phase_regions
268+
.entry(phase)
269+
.or_insert_with(Vec::new)
270+
.push(size);
253271
}
254-
272+
255273
log::info!("Region size distribution:");
256274
log::info!(" Target size (400x300): {} occurrences", target_size_count);
257-
log::info!(" Screen size (1920x1080): {} occurrences", screen_size_count);
275+
log::info!(
276+
" Screen size (1920x1080): {} occurrences",
277+
screen_size_count
278+
);
258279
log::info!(" Transition sizes: {} occurrences", transition_count);
259-
280+
260281
// Analyze each phase
261282
for (phase, sizes) in &phase_regions {
262283
let unique_sizes: std::collections::HashSet<_> = sizes.iter().collect();
263-
log::info!(" {}: {} regions, {} unique sizes", phase, sizes.len(), unique_sizes.len());
284+
log::info!(
285+
" {}: {} regions, {} unique sizes",
286+
phase,
287+
sizes.len(),
288+
unique_sizes.len()
289+
);
264290
}
265-
291+
266292
// Validation criteria
267293
let final_region = regions.last().unwrap().1;
268-
let ends_in_target_size = final_region.width == target_size.width && final_region.height == target_size.height;
269-
294+
let ends_in_target_size =
295+
final_region.width == target_size.width && final_region.height == target_size.height;
296+
270297
// Check that we have initial transition from screen to target
271298
let has_screen_to_target_transition = screen_size_count > 0 && target_size_count > 0;
272-
299+
273300
// Check that Phase 1 (fast movement → stop → fast movement) maintains target size
274301
let empty_vec = vec![];
275302
let phase1_regions = phase_regions.get("Phase 1").unwrap_or(&empty_vec);
276-
let phase1_maintains_target = phase1_regions.iter().all(|&(w, h)|
277-
w == target_size.width && h == target_size.height
278-
);
279-
303+
let phase1_maintains_target = phase1_regions
304+
.iter()
305+
.all(|&(w, h)| w == target_size.width && h == target_size.height);
306+
280307
let success = has_screen_to_target_transition && ends_in_target_size && phase1_maintains_target;
281-
308+
282309
if success {
283310
log::info!("✅ Stable fast movement validation PASSED");
284311
log::info!("✅ System correctly transitioned from screen_size to target_size");
285312
log::info!("✅ Fast movement in target_size state did NOT trigger zoom_out");
286313
log::info!("✅ System remained stable during Phase 1 fast movements");
287-
314+
288315
// Show timeline summary
289316
log::info!("Timeline summary (key regions):");
290-
let sample_indices = [0, regions.len()/4, regions.len()/2, regions.len()*3/4, regions.len()-1];
317+
let sample_indices = [
318+
0,
319+
regions.len() / 4,
320+
regions.len() / 2,
321+
regions.len() * 3 / 4,
322+
regions.len() - 1,
323+
];
291324
for &i in &sample_indices {
292325
if i < regions.len() {
293326
let (time, size) = size_timeline[i];
@@ -300,13 +333,18 @@ fn validate_stable_fast_movement_results(
300333
log::error!(" Missing transition from screen_size to target_size");
301334
}
302335
if !ends_in_target_size {
303-
log::error!(" Final region is not target_size: {}x{}", final_region.width, final_region.height);
336+
log::error!(
337+
" Final region is not target_size: {}x{}",
338+
final_region.width,
339+
final_region.height
340+
);
304341
}
305342
if !phase1_maintains_target {
306343
log::error!(" Phase 1 fast movement caused unwanted zoom_out");
307344
}
308345
return Err("Stable fast movement test failed".into());
309346
}
310-
347+
311348
Ok(())
312-
}
349+
}
350+

lib/recorder/examples/resize_img_demo.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
1212
};
1313

1414
let now = std::time::Instant::now();
15-
let resized_img = RecordingSession::resize_image(data, (1920, 1080))?;
15+
let resized_img = RecordingSession::resize_image(data, (1920, 1080), None)?;
1616
log::debug!("resize image time: {:.2?}", now.elapsed());
1717

1818
let path = "target/resize-test.png";

lib/recorder/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,5 +56,8 @@ pub fn platform_screen_capture() -> impl screen_capture::ScreenCapture + Clone +
5656
#[cfg(all(target_os = "linux", feature = "wayland-portal"))]
5757
let screen_capturer = screen_capture_wayland_portal::ScreenCaptureWaylandPortal::default();
5858

59+
#[cfg(all(target_os = "windows", feature = "windows"))]
60+
let screen_capturer = screen_capture_windows::ScreenCaptureWindows::default();
61+
5962
screen_capturer
6063
}

0 commit comments

Comments
 (0)