Skip to content

Commit 3e9d7bc

Browse files
committed
account for screen capture not providing frames on macos
1 parent 0416b18 commit 3e9d7bc

File tree

4 files changed

+35
-17
lines changed

4 files changed

+35
-17
lines changed

crates/enc-avfoundation/src/mp4.rs

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use cap_media_info::{AudioInfo, VideoInfo};
22
use cidre::{cm::SampleTimingInfo, objc::Obj, *};
33
use ffmpeg::frame;
44
use std::{ops::Sub, path::PathBuf, time::Duration};
5-
use tracing::{debug, info};
5+
use tracing::{debug, error, info};
66

77
// before pausing at all, subtract 0.
88
// on pause, record last frame time.
@@ -16,7 +16,7 @@ pub struct MP4Encoder {
1616
asset_writer: arc::R<av::AssetWriter>,
1717
video_input: arc::R<av::AssetWriterInput>,
1818
audio_input: Option<arc::R<av::AssetWriterInput>>,
19-
most_recent_timestamp: Option<Duration>,
19+
most_recent_frame: Option<(arc::R<cm::SampleBuf>, Duration)>,
2020
pause_timestamp: Option<Duration>,
2121
timestamp_offset: Duration,
2222
is_writing: bool,
@@ -197,7 +197,7 @@ impl MP4Encoder {
197197
audio_input,
198198
asset_writer,
199199
video_input,
200-
most_recent_timestamp: None,
200+
most_recent_frame: None,
201201
pause_timestamp: None,
202202
timestamp_offset: Duration::ZERO,
203203
is_writing: false,
@@ -211,7 +211,7 @@ impl MP4Encoder {
211211
/// They will be made relative when encoding
212212
pub fn queue_video_frame(
213213
&mut self,
214-
frame: &cidre::cm::SampleBuf,
214+
frame: arc::R<cm::SampleBuf>,
215215
timestamp: Duration,
216216
) -> Result<(), QueueVideoFrameError> {
217217
if self.is_paused || !self.video_input.is_ready_for_more_media_data() {
@@ -224,7 +224,7 @@ impl MP4Encoder {
224224
.start_session_at_src_time(cm::Time::new(timestamp.as_millis() as i64, 1_000));
225225
}
226226

227-
self.most_recent_timestamp = Some(timestamp);
227+
self.most_recent_frame = Some((frame.clone(), timestamp));
228228

229229
if let Some(pause_timestamp) = self.pause_timestamp {
230230
self.timestamp_offset += timestamp - pause_timestamp;
@@ -331,7 +331,7 @@ impl MP4Encoder {
331331
return;
332332
}
333333

334-
let Some(timestamp) = self.most_recent_timestamp else {
334+
let Some((_, timestamp)) = self.most_recent_frame else {
335335
return;
336336
};
337337

@@ -347,19 +347,34 @@ impl MP4Encoder {
347347
self.is_paused = false;
348348
}
349349

350-
pub fn finish(&mut self) {
350+
pub fn finish(&mut self, timestamp: Option<Duration>) {
351351
if !self.is_writing {
352352
return;
353353
}
354354

355-
let Some(most_recent_timestamp) = self.most_recent_timestamp else {
355+
let Some(mut most_recent_frame) = self.most_recent_frame.take() else {
356356
return;
357357
};
358358

359+
// We extend the video to the provided timestamp if possible
360+
if let Some(timestamp) = timestamp
361+
&& let Some(diff) = timestamp.checked_sub(most_recent_frame.1)
362+
&& diff > Duration::from_millis(500)
363+
{
364+
match self.queue_video_frame(most_recent_frame.0.clone(), timestamp) {
365+
Ok(()) => {
366+
most_recent_frame = (most_recent_frame.0, timestamp);
367+
}
368+
Err(e) => {
369+
error!("Failed to queue final video frame: {e}");
370+
}
371+
}
372+
}
373+
359374
self.is_writing = false;
360375

361376
self.asset_writer.end_session_at_src_time(cm::Time::new(
362-
most_recent_timestamp.sub(self.timestamp_offset).as_millis() as i64,
377+
most_recent_frame.1.sub(self.timestamp_offset).as_millis() as i64,
363378
1000,
364379
));
365380
self.video_input.mark_as_finished();
@@ -381,7 +396,7 @@ impl MP4Encoder {
381396

382397
impl Drop for MP4Encoder {
383398
fn drop(&mut self) {
384-
self.finish();
399+
self.finish(None);
385400
}
386401
}
387402

crates/recording/src/output_pipeline/core.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ async fn finish_build(
349349
Ok(())
350350
}
351351
.then(async move |res| {
352-
let muxer_res = muxer.lock().await.finish();
352+
let muxer_res = muxer.lock().await.finish(timestamps.instant().elapsed());
353353

354354
let _ = done_tx.send(match (res, muxer_res) {
355355
(Err(e), _) | (_, Err(e)) => Err(e),
@@ -766,7 +766,7 @@ pub trait Muxer: Send + 'static {
766766

767767
fn stop(&mut self) {}
768768

769-
fn finish(&mut self) -> anyhow::Result<()>;
769+
fn finish(&mut self, timestamp: Duration) -> anyhow::Result<()>;
770770
}
771771

772772
pub trait AudioMuxer: Muxer {

crates/recording/src/output_pipeline/ffmpeg.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ impl Muxer for Mp4Muxer {
6565
})
6666
}
6767

68-
fn finish(&mut self) -> anyhow::Result<()> {
68+
fn finish(&mut self, _: Duration) -> anyhow::Result<()> {
6969
if let Some(video_encoder) = self.video_encoder.as_mut() {
7070
video_encoder.finish(&mut self.output);
7171
}
@@ -131,7 +131,7 @@ impl Muxer for OggMuxer {
131131
))
132132
}
133133

134-
fn finish(&mut self) -> anyhow::Result<()> {
134+
fn finish(&mut self, _: Duration) -> anyhow::Result<()> {
135135
self.0.finish();
136136
Ok(())
137137
}

crates/recording/src/output_pipeline/macos.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,11 @@ impl Muxer for AVFoundationMp4Muxer {
4949
))
5050
}
5151

52-
fn finish(&mut self) -> anyhow::Result<()> {
53-
self.0.lock().map_err(|e| anyhow!("{e}"))?.finish();
52+
fn finish(&mut self, timestamp: Duration) -> anyhow::Result<()> {
53+
self.0
54+
.lock()
55+
.map_err(|e| anyhow!("{e}"))?
56+
.finish(Some(timestamp));
5457
Ok(())
5558
}
5659
}
@@ -71,7 +74,7 @@ impl VideoMuxer for AVFoundationMp4Muxer {
7174
mp4.resume();
7275
}
7376

74-
mp4.queue_video_frame(&frame.sample_buf, timestamp)
77+
mp4.queue_video_frame(frame.sample_buf, timestamp)
7578
.map_err(|e| anyhow!("QueueVideoFrame/{e}"))
7679
}
7780
}

0 commit comments

Comments
 (0)