1use std::collections::VecDeque;
4use std::fs;
5use std::io::{BufWriter, Seek, SeekFrom, Write};
6use std::path::PathBuf;
7use std::sync::mpsc::{self, Receiver, SyncSender};
8use std::sync::{Arc, Mutex};
9use std::thread::{self, JoinHandle};
10use std::time::{Duration, Instant};
11
12use crate::audio_input::{AudioStreamConfig, EncoderAudioInput, open_audio_encoder};
13use crate::capabilities::{
14 AvEncodeConfig, NativeVideoEncoderSelection, VideoEncoderOpenRequest, open_video_encoder,
15};
16use crate::convert::{
17 codec_thread_budget, configure_rgba_to_yuv, encode_adapter_error, encode_adapter_error_at,
18 tick_base,
19};
20use crate::partial::{OutputTarget, PartialOutput};
21use alpha2_ffmpeg_bridge::OutputFile;
22use alpha2_ffmpeg_bridge::Yuv420pFrameBuffer;
23use alpha2_media::{
24 AudioBlock, AudioBlockSink, CancelToken, DecodedVideoFrame, EncodeError, EncodeEvent,
25 EncodeSink, MAX_AUDIO_CHANNELS, PixelFormat, SpeakerLayout,
26};
27use alpha2_time::MediaTime;
28use rsmpeg::avcodec::{AVCodecContext, AVPacket};
29use rsmpeg::avutil::{AVFrameWithImage, AVImage, AVRational, av_rescale_q};
30use rsmpeg::error::RsmpegError;
31use rsmpeg::ffi;
32use rsmpeg::swscale::SwsContext;
33
34#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
41pub struct EncodeStageTimings {
42 pub video_input_ready_wait: Duration,
46 pub video_send_frame: Duration,
48 pub video_receive_packet: Duration,
50 pub video_mux_write: Duration,
52 pub audio_send_frame: Duration,
54 pub audio_receive_packet: Duration,
56 pub audio_mux_write: Duration,
58}
59
60impl EncodeStageTimings {
61 #[must_use]
63 pub fn since(self, earlier: Self) -> Self {
64 Self {
65 video_input_ready_wait: self
66 .video_input_ready_wait
67 .saturating_sub(earlier.video_input_ready_wait),
68 video_send_frame: self
69 .video_send_frame
70 .saturating_sub(earlier.video_send_frame),
71 video_receive_packet: self
72 .video_receive_packet
73 .saturating_sub(earlier.video_receive_packet),
74 video_mux_write: self.video_mux_write.saturating_sub(earlier.video_mux_write),
75 audio_send_frame: self
76 .audio_send_frame
77 .saturating_sub(earlier.audio_send_frame),
78 audio_receive_packet: self
79 .audio_receive_packet
80 .saturating_sub(earlier.audio_receive_packet),
81 audio_mux_write: self.audio_mux_write.saturating_sub(earlier.audio_mux_write),
82 }
83 }
84}
85
86thread_local! {
87 static ENCODE_CALL_WATCH: std::cell::RefCell<Option<Box<dyn Fn()>>> =
90 const { std::cell::RefCell::new(None) };
91}
92
93#[must_use = "the watch ends when the guard drops"]
104pub fn watch_encode_calls(beat: impl Fn() + 'static) -> EncodeCallWatch {
105 let previous = ENCODE_CALL_WATCH.with(|watch| watch.replace(Some(Box::new(beat))));
106 EncodeCallWatch {
107 previous,
108 thread: std::marker::PhantomData,
109 }
110}
111
112pub struct EncodeCallWatch {
114 previous: Option<Box<dyn Fn()>>,
115 thread: std::marker::PhantomData<*const ()>,
117}
118
119impl Drop for EncodeCallWatch {
120 fn drop(&mut self) {
121 let previous = self.previous.take();
122 ENCODE_CALL_WATCH.with(|watch| {
123 watch.replace(previous);
124 });
125 }
126}
127
128pub(crate) fn beat_encode_call() {
130 ENCODE_CALL_WATCH.with(|watch| {
131 if let Some(beat) = watch.borrow().as_ref() {
132 beat();
133 }
134 });
135}
136
137pub(crate) fn timed<T>(total: &mut Duration, body: impl FnOnce() -> T) -> T {
140 beat_encode_call();
141 let started = Instant::now();
142 let value = body();
143 *total = total.saturating_add(started.elapsed());
144 value
145}
146
147pub struct NativeWavEncodeSink {
149 pub(crate) target: OutputTarget,
150 pub(crate) partial_path: PathBuf,
151 pub(crate) _partial_lock: crate::partial::PartialLock,
153 pub(crate) file: Option<BufWriter<fs::File>>,
154 pub(crate) sample_rate: u32,
155 pub(crate) channels: u16,
156 pub(crate) speakers: SpeakerLayout,
158 pub(crate) channel_mask: Option<u32>,
162 pub(crate) plane_order: Vec<usize>,
165 pub(crate) frames_written: u64,
166 pub(crate) complete: bool,
167 pub(crate) interleaved: Vec<u8>,
169}
170
171const WAV_WRITE_BUFFER: usize = 1 << 20;
174
175pub struct NativeAvEncodeSink {
180 pub(crate) target: OutputTarget,
181 pub(crate) partial_path: PathBuf,
182 pub(crate) _partial_lock: crate::partial::PartialLock,
184 pub(crate) output: Option<OutputFile>,
185 pub(crate) video_encoder: crate::video_backend::VideoEncoderBackend,
186 pub(crate) video_stream_index: i32,
187 pub(crate) video_encoder_time_base: AVRational,
188 pub(crate) video_frame_units: i64,
190 pub(crate) video_stream_time_base: AVRational,
191 pub(crate) video_scaler: SwsContext,
192 pub(crate) yuv_input: crate::convert::Yuv420pConverter,
194 pub(crate) width: i32,
195 pub(crate) height: i32,
196 pub(crate) last_video_pts: Option<i64>,
197 pub(crate) audio: AudioEncodeThread,
200 pub(crate) audio_input: EncoderAudioInput,
202 pub(crate) video_packets: VecDeque<QueuedPacket>,
203 pub(crate) last_input_ticks: Option<i64>,
204 pub(crate) complete: bool,
205 pub(crate) video_selection: NativeVideoEncoderSelection,
206 pub(crate) stages: EncodeStageTimings,
207}
208
209pub(crate) enum AudioCommand {
211 Block(Vec<Vec<f32>>, i64),
215 Finish(i64),
218}
219
220#[derive(Default)]
225pub(crate) struct AudioQueueState {
226 pub(crate) packets: VecDeque<QueuedPacket>,
228 pub(crate) consumed_ticks: i64,
236 pub(crate) finished: bool,
238 pub(crate) failure: Option<String>,
240 pub(crate) stages: EncodeStageTimings,
242}
243
244pub(crate) struct AudioEncodeThread {
246 commands: Option<SyncSender<AudioCommand>>,
247 join: Option<JoinHandle<()>>,
248 state: Arc<Mutex<AudioQueueState>>,
249}
250
251impl AudioEncodeThread {
252 pub(crate) fn lock(&self) -> std::sync::MutexGuard<'_, AudioQueueState> {
253 self.state
254 .lock()
255 .unwrap_or_else(std::sync::PoisonError::into_inner)
256 }
257
258 pub(crate) fn send(&self, command: AudioCommand) -> Result<(), EncodeError> {
261 let sent = match self.commands.as_ref() {
262 Some(sender) => sender.send(command).is_ok(),
263 None => return Err(EncodeError::Adapter("audio encoder is closed".into())),
264 };
265 if sent {
266 return Ok(());
267 }
268 let failure = self.lock().failure.clone();
269 Err(EncodeError::Adapter(failure.unwrap_or_else(|| {
270 "audio encoder stopped before accepting a block".into()
271 })))
272 }
273
274 pub(crate) fn shutdown(&mut self) {
276 drop(self.commands.take());
277 if let Some(join) = self.join.take() {
278 let _ = join.join();
279 }
280 }
281}
282
283impl Drop for AudioEncodeThread {
284 fn drop(&mut self) {
285 self.shutdown();
286 }
287}
288
289pub(crate) struct QueuedPacket {
291 pub(crate) sort_ticks: i64,
292 pub(crate) packet: AVPacket,
293}
294
295impl NativeAvEncodeSink {
296 pub fn create(
303 path: impl Into<OutputTarget>,
304 config: AvEncodeConfig,
305 ) -> Result<Self, EncodeError> {
306 Self::create_selected(path, config, None, &codec_thread_budget)
307 }
308
309 pub fn create_with_video_encoder(
315 path: impl Into<OutputTarget>,
316 config: AvEncodeConfig,
317 encoder_name: &str,
318 ) -> Result<Self, EncodeError> {
319 Self::create_selected(path, config, Some(encoder_name), &codec_thread_budget)
320 }
321
322 pub fn create_with_video_encoder_threads(
338 path: impl Into<OutputTarget>,
339 config: AvEncodeConfig,
340 encoder_name: Option<&str>,
341 software_threads: &dyn Fn() -> i64,
342 ) -> Result<Self, EncodeError> {
343 Self::create_selected(path, config, encoder_name, software_threads)
344 }
345
346 #[allow(clippy::too_many_lines)]
347 pub(crate) fn create_selected(
348 path: impl Into<OutputTarget>,
349 config: AvEncodeConfig,
350 encoder_name: Option<&str>,
351 software_threads: &dyn Fn() -> i64,
352 ) -> Result<Self, EncodeError> {
353 crate::video_encode::check_video_config(&config.video)?;
354 if !config
355 .video
356 .container
357 .offers_audio(config.audio_codec.model())
358 {
359 return Err(EncodeError::Adapter(format!(
360 "{} does not offer audio codec {}",
361 config.video.container,
362 config.audio_codec.as_str()
363 )));
364 }
365 if config.audio_sample_rate == 0
366 || config.audio_channels == 0
367 || config.audio_channels > 8
368 || config.audio_bit_rate == 0
369 {
370 return Err(EncodeError::Adapter(format!(
371 "the audio output ({} channels at {} Hz, {} bit/s) is refused: 1 to 8 channels, a positive rate and bitrate",
372 config.audio_channels, config.audio_sample_rate, config.audio_bit_rate
373 )));
374 }
375 let target = path.into();
376 let partial = PartialOutput::claim(&target)?;
377 let partial_path = partial.path().to_owned();
378 let mut output = partial.open_muxer(config.video.container)?;
379 let global_header =
380 output.oformat().flags & i32::try_from(ffi::AVFMT_GLOBALHEADER).unwrap_or(0) != 0;
381
382 let width = i32::try_from(config.video.width)
383 .map_err(|_| EncodeError::Adapter("width is out of range".into()))?;
384 let height = i32::try_from(config.video.height)
385 .map_err(|_| EncodeError::Adapter("height is out of range".into()))?;
386 let (video_encoder, video_selection) = open_video_encoder(&VideoEncoderOpenRequest {
387 config: &config.video,
388 requested_name: encoder_name,
389 threads: crate::capabilities::SoftwareEncoderThreads::new(software_threads),
390 global_header,
391 width,
392 height,
393 })?;
394 let video_encoder_time_base = video_encoder.time_base();
396
397 let audio = open_audio_encoder(
398 AudioStreamConfig {
399 codec: config.audio_codec,
400 sample_rate: config.audio_sample_rate,
401 channels: config.audio_channels,
402 bit_rate: config.audio_bit_rate,
403 },
404 global_header,
405 )?;
406 let audio_encoder_time_base = audio.encoder_time_base;
407
408 let video_stream_index;
409 {
410 let mut stream = output.new_stream();
411 stream.set_time_base(video_encoder_time_base);
412 stream.set_codecpar(video_encoder.codec_parameters());
413 crate::video_encode::describe_output_stream(
414 &mut stream,
415 video_encoder.codec_id(),
416 config.video.display,
417 config.video.container,
418 )?;
419 video_stream_index = stream.index;
420 }
421 let audio_stream_index;
422 {
423 let mut stream = output.new_stream();
424 stream.set_time_base(audio_encoder_time_base);
425 stream.set_codecpar(audio.encoder.extract_codecpar());
426 audio_stream_index = stream.index;
427 }
428 output
429 .write_header(&mut crate::convert::muxer_header_options(
430 config.video.container,
431 ))
432 .map_err(encode_adapter_error)?;
433 let video_stream_time_base =
434 output.streams()[usize::try_from(video_stream_index).unwrap_or(0)].time_base;
435 let audio_stream_time_base =
436 output.streams()[usize::try_from(audio_stream_index).unwrap_or(0)].time_base;
437 let mut video_scaler = SwsContext::get_context(
438 width,
439 height,
440 ffi::AV_PIX_FMT_RGBA,
441 width,
442 height,
443 ffi::AV_PIX_FMT_YUV420P,
444 ffi::SWS_BILINEAR,
445 None,
446 None,
447 None,
448 )
449 .ok_or_else(|| EncodeError::Adapter("cannot create RGBA to YUV scaler".into()))?;
450 configure_rgba_to_yuv(&mut video_scaler, config.video.color)
451 .map_err(encode_adapter_error)?;
452 let audio_input = audio.input;
453 let audio = spawn_audio_encoder(AudioEncoderConfig {
454 encoder: audio.encoder,
455 stream_index: audio_stream_index,
456 encoder_time_base: audio_encoder_time_base,
457 stream_time_base: audio_stream_time_base,
458 sample_rate: audio.encoder_rate,
459 sample_format: audio.sample_format,
460 })?;
461 let partial_lock = partial.keep();
462 Ok(Self {
463 target,
464 partial_path,
465 _partial_lock: partial_lock,
466 output: Some(output),
467 video_encoder,
468 video_stream_index,
469 video_encoder_time_base,
470 video_frame_units: crate::mux::frame_units(
471 config.video.frame_rate,
472 video_encoder_time_base,
473 ),
474 video_stream_time_base,
475 video_scaler,
476 yuv_input: crate::convert::Yuv420pConverter::new(config.video.color),
477 width,
478 height,
479 last_video_pts: None,
480 audio,
481 audio_input,
482 video_packets: VecDeque::new(),
483 last_input_ticks: None,
484 complete: false,
485 video_selection,
486 stages: EncodeStageTimings::default(),
487 })
488 }
489
490 #[cfg(test)]
492 pub(crate) fn queued_audio_packets(&self) -> usize {
493 self.audio.lock().packets.len()
494 }
495
496 #[must_use]
498 pub fn stage_timings(&self) -> EncodeStageTimings {
499 let audio = self.audio.lock().stages;
500 EncodeStageTimings {
501 audio_send_frame: audio.audio_send_frame,
502 audio_receive_packet: audio.audio_receive_packet,
503 ..self.stages
504 }
505 }
506
507 #[must_use]
509 pub fn video_selection(&self) -> &NativeVideoEncoderSelection {
510 &self.video_selection
511 }
512
513 pub(crate) fn cleanup(&mut self) -> Result<(), EncodeError> {
514 self.audio.shutdown();
517 drop(self.output.take());
518 match fs::remove_file(&self.partial_path) {
519 Ok(()) => Ok(()),
520 Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(()),
521 Err(error) => Err(encode_adapter_error(error)),
522 }
523 }
524
525 fn queue_video_packet(&mut self, mut packet: AVPacket) {
527 packet.set_stream_index(self.video_stream_index);
528 crate::mux::fill_frame_duration(&mut packet, self.video_frame_units);
529 packet.rescale_ts(self.video_encoder_time_base, self.video_stream_time_base);
530 let timestamp = if packet.dts == ffi::AV_NOPTS_VALUE {
531 packet.pts
532 } else {
533 packet.dts
534 };
535 self.video_packets.push_back(QueuedPacket {
536 sort_ticks: av_rescale_q(timestamp, self.video_stream_time_base, tick_base()),
537 packet,
538 });
539 }
540
541 fn pop_next_packet(&mut self, flushing: bool) -> Result<Option<QueuedPacket>, EncodeError> {
548 let mut state = self.audio.lock();
549 if let Some(error) = state.failure.clone() {
550 return Err(EncodeError::Adapter(error));
551 }
552 let take_audio = match (self.video_packets.front(), state.packets.front()) {
553 (Some(video), Some(audio)) => audio.sort_ticks < video.sort_ticks,
554 (None, Some(_)) => true,
555 (_, None) => false,
556 };
557 if take_audio {
558 return Ok(state.packets.pop_front());
559 }
560 let Some(video) = self.video_packets.front() else {
561 return Ok(None);
562 };
563 if !flushing && !state.finished && state.consumed_ticks < video.sort_ticks {
564 return Ok(None);
565 }
566 drop(state);
567 Ok(self.video_packets.pop_front())
568 }
569
570 pub(crate) fn write_ready_packets(&mut self, flushing: bool) -> Result<(), EncodeError> {
571 while let Some(mut queued) = self.pop_next_packet(flushing)? {
572 let video = queued.packet.stream_index == self.video_stream_index;
574 let total = if video {
575 &mut self.stages.video_mux_write
576 } else {
577 &mut self.stages.audio_mux_write
578 };
579 let output = self
580 .output
581 .as_mut()
582 .ok_or_else(|| EncodeError::Adapter("muxer is already closed".into()))?;
583 timed(total, || output.interleaved_write_frame(&mut queued.packet))
584 .map_err(|error| encode_adapter_error_at("packet write failed", error))?;
585 }
586 Ok(())
587 }
588
589 pub(crate) fn drain_video_packets(&mut self, flushing: bool) -> Result<(), EncodeError> {
590 loop {
591 let received = timed(&mut self.stages.video_receive_packet, || {
592 self.video_encoder.receive_packet()
593 });
594 match received {
595 Ok(packet) => self.queue_video_packet(packet),
596 Err(RsmpegError::EncoderDrainError) if !flushing => return Ok(()),
597 Err(RsmpegError::EncoderFlushedError | RsmpegError::EncoderDrainError)
598 if flushing =>
599 {
600 return Ok(());
601 }
602 Err(error) => {
603 return Err(encode_adapter_error_at(
604 "video encoder packet drain failed",
605 error,
606 ));
607 }
608 }
609 }
610 }
611
612 pub fn write_prepared_video(
621 &mut self,
622 frame: &mut Yuv420pFrameBuffer,
623 pts: MediaTime,
624 ) -> Result<(), EncodeError> {
625 let (width, height) = frame.size();
626 if width != usize::try_from(self.width).unwrap_or(0)
627 || height != usize::try_from(self.height).unwrap_or(0)
628 {
629 return Err(EncodeError::Adapter(
630 "encoder input dimensions do not match its configuration".into(),
631 ));
632 }
633 let pts = av_rescale_q(pts.ticks(), tick_base(), self.video_encoder_time_base);
634 if self.last_video_pts.is_some_and(|previous| pts <= previous) {
635 return Err(EncodeError::InvalidTimestamp);
636 }
637 frame.set_presentation(pts, self.video_encoder_time_base);
638 timed(&mut self.stages.video_send_frame, || {
639 self.video_encoder.send_frame(Some(frame.frame()))
640 })
641 .map_err(|error| {
642 encode_adapter_error_at("video encoder rejected a YUV420P frame", error)
643 })?;
644 self.last_video_pts = Some(pts);
645 self.drain_video_packets(false)?;
646 self.write_ready_packets(false)
647 }
648
649 pub(crate) fn write_video(&mut self, frame: &DecodedVideoFrame) -> Result<(), EncodeError> {
650 if frame.visible_rect.width != u32::try_from(self.width).unwrap_or(0)
651 || frame.visible_rect.height != u32::try_from(self.height).unwrap_or(0)
652 {
653 return Err(EncodeError::Adapter(
654 "encoder input dimensions do not match its configuration".into(),
655 ));
656 }
657 let pts = av_rescale_q(frame.pts.ticks(), tick_base(), self.video_encoder_time_base);
658 if self.last_video_pts.is_some_and(|previous| pts <= previous) {
659 return Err(EncodeError::InvalidTimestamp);
660 }
661 if let Some(mut yuv) = self.yuv_input.convert(frame, self.width, self.height)? {
662 yuv.set_pts(pts);
663 yuv.set_time_base(self.video_encoder_time_base);
664 timed(&mut self.stages.video_send_frame, || {
665 self.video_encoder.send_frame(Some(&yuv))
666 })
667 .map_err(|error| {
668 encode_adapter_error_at("video encoder rejected a YUV420P frame", error)
669 })?;
670 self.last_video_pts = Some(pts);
671 self.drain_video_packets(false)?;
672 return self.write_ready_packets(false);
673 }
674 if frame.pixel_format != PixelFormat::Rgba8 || frame.planes.len() != 1 {
675 return Err(EncodeError::Adapter(
676 "encoder expects YUV planes or one RGBA8 plane".into(),
677 ));
678 }
679 let plane = &frame.planes[0];
680 let expected = usize::try_from(self.width)
681 .ok()
682 .and_then(|width| {
683 usize::try_from(self.height)
684 .ok()
685 .and_then(|height| width.checked_mul(height))
686 })
687 .and_then(|pixels| pixels.checked_mul(4))
688 .ok_or_else(|| EncodeError::Adapter("frame dimensions overflow".into()))?;
689 if plane.bytes.len() != expected
690 || plane.stride != u32::try_from(self.width).unwrap_or(0) * 4
691 {
692 return Err(EncodeError::Adapter(
693 "RGBA plane is not tightly packed".into(),
694 ));
695 }
696 let rgba = alpha2_ffmpeg_bridge::rgba_frame(&plane.bytes, self.width, self.height)
700 .map_err(encode_adapter_error)?;
701 let yuv_image = AVImage::new(ffi::AV_PIX_FMT_YUV420P, self.width, self.height, 1)
702 .ok_or_else(|| EncodeError::Adapter("cannot allocate YUV input".into()))?;
703 let mut yuv = AVFrameWithImage::new(yuv_image);
704 self.video_scaler
705 .scale_frame(&rgba, 0, self.height, &mut yuv)
706 .map_err(encode_adapter_error)?;
707 yuv.set_pts(pts);
708 yuv.set_time_base(self.video_encoder_time_base);
709 timed(&mut self.stages.video_send_frame, || {
710 self.video_encoder.send_frame(Some(&yuv))
711 })
712 .map_err(|error| {
713 encode_adapter_error_at("video encoder rejected a converted YUV420P frame", error)
714 })?;
715 self.last_video_pts = Some(pts);
716 self.drain_video_packets(false)?;
717 self.write_ready_packets(false)
718 }
719
720 pub(crate) fn write_audio(&mut self, block: &AudioBlock) -> Result<(), EncodeError> {
723 let frames = self.audio_input.push(block)?;
724 let covered = self.audio_input.received_ticks();
725 for frame in frames {
726 self.audio.send(AudioCommand::Block(frame, covered))?;
727 }
728 self.write_ready_packets(false)
730 }
731}
732
733pub(crate) struct AudioEncoderConfig {
735 pub(crate) encoder: AVCodecContext,
736 pub(crate) stream_index: i32,
737 pub(crate) encoder_time_base: AVRational,
738 pub(crate) stream_time_base: AVRational,
739 pub(crate) sample_rate: u32,
741 pub(crate) sample_format: ffi::AVSampleFormat,
743}
744
745pub(crate) fn spawn_audio_encoder(
755 config: AudioEncoderConfig,
756) -> Result<AudioEncodeThread, EncodeError> {
757 let (commands, receiver) = mpsc::sync_channel(2);
758 let state = Arc::new(Mutex::new(AudioQueueState::default()));
759 let worker_state = Arc::clone(&state);
760 let run = move || run_audio_encoder(config, &receiver, &worker_state);
761 let builder = thread::Builder::new().name("alpha2-audio-encode".to_owned());
762 #[cfg(test)]
763 let spawned = if REFUSE_AUDIO_ENCODE_THREAD.with(std::cell::Cell::take) {
764 drop(run);
765 Err(std::io::Error::from(std::io::ErrorKind::WouldBlock))
766 } else {
767 builder.spawn(run)
768 };
769 #[cfg(not(test))]
770 let spawned = builder.spawn(run);
771 let join = spawned.map_err(|error| {
772 encode_adapter_error_at("failed to start the audio encode thread", error)
773 })?;
774 Ok(AudioEncodeThread {
775 commands: Some(commands),
776 join: Some(join),
777 state,
778 })
779}
780
781#[cfg(test)]
782thread_local! {
783 pub(crate) static REFUSE_AUDIO_ENCODE_THREAD: std::cell::Cell<bool> =
786 const { std::cell::Cell::new(false) };
787}
788
789fn run_audio_encoder(
791 mut config: AudioEncoderConfig,
792 commands: &Receiver<AudioCommand>,
793 state: &Arc<Mutex<AudioQueueState>>,
794) {
795 let mut encoded_samples = 0_u64;
796 loop {
797 let Ok(command) = commands.recv() else {
798 return;
801 };
802 let (channels, covered, last) = match command {
803 AudioCommand::Block(channels, covered) => (Some(channels), covered, false),
804 AudioCommand::Finish(covered) => (None, covered, true),
805 };
806 let mut stages = EncodeStageTimings::default();
807 let outcome = encode_audio_block(
808 &mut config,
809 channels.as_deref(),
810 &mut encoded_samples,
811 &mut stages,
812 );
813 let mut guard = state
814 .lock()
815 .unwrap_or_else(std::sync::PoisonError::into_inner);
816 guard.stages.audio_send_frame = guard
817 .stages
818 .audio_send_frame
819 .saturating_add(stages.audio_send_frame);
820 guard.stages.audio_receive_packet = guard
821 .stages
822 .audio_receive_packet
823 .saturating_add(stages.audio_receive_packet);
824 match outcome {
825 Ok(packets) => guard.packets.extend(packets),
826 Err(error) => {
827 if guard.failure.is_none() {
828 guard.failure = Some(error.to_string());
829 }
830 guard.finished = true;
831 return;
832 }
833 }
834 guard.consumed_ticks = guard.consumed_ticks.max(covered);
835 if last {
836 guard.finished = true;
837 return;
838 }
839 }
840}
841
842fn encode_audio_block(
846 config: &mut AudioEncoderConfig,
847 channels: Option<&[Vec<f32>]>,
848 encoded_samples: &mut u64,
849 stages: &mut EncodeStageTimings,
850) -> Result<Vec<QueuedPacket>, EncodeError> {
851 let last = channels.is_none();
852 if let Some(channels) = channels {
853 let pts = i64::try_from(*encoded_samples).map_err(|_| EncodeError::InvalidTimestamp)?;
854 let frame = alpha2_ffmpeg_bridge::audio_frame(
855 channels,
856 config.sample_format,
857 i32::try_from(config.sample_rate).unwrap_or(i32::MAX),
858 pts,
859 )
860 .map_err(encode_adapter_error)?;
861 timed(&mut stages.audio_send_frame, || {
862 config.encoder.send_frame(Some(&frame))
863 })
864 .map_err(|error| encode_adapter_error_at("audio encoder rejected a frame", error))?;
865 let samples = channels.first().map_or(0, Vec::len);
866 *encoded_samples =
867 encoded_samples.saturating_add(u64::try_from(samples).unwrap_or(u64::MAX));
868 } else {
869 timed(&mut stages.audio_send_frame, || {
870 config.encoder.send_frame(None)
871 })
872 .map_err(encode_adapter_error)?;
873 }
874 let mut packets = Vec::new();
875 loop {
876 let received = timed(&mut stages.audio_receive_packet, || {
877 config.encoder.receive_packet()
878 });
879 match received {
880 Ok(mut packet) => {
881 packet.set_stream_index(config.stream_index);
882 packet.rescale_ts(config.encoder_time_base, config.stream_time_base);
883 let timestamp = if packet.dts == ffi::AV_NOPTS_VALUE {
884 packet.pts
885 } else {
886 packet.dts
887 };
888 packets.push(QueuedPacket {
889 sort_ticks: av_rescale_q(timestamp, config.stream_time_base, tick_base()),
890 packet,
891 });
892 }
893 Err(RsmpegError::EncoderDrainError) if !last => return Ok(packets),
894 Err(RsmpegError::EncoderFlushedError | RsmpegError::EncoderDrainError) if last => {
895 return Ok(packets);
896 }
897 Err(error) => {
898 return Err(encode_adapter_error_at(
899 "audio encoder packet drain failed",
900 error,
901 ));
902 }
903 }
904 }
905}
906
907impl EncodeSink for NativeAvEncodeSink {
908 fn write(&mut self, event: EncodeEvent, cancel: &CancelToken) -> Result<(), EncodeError> {
909 if cancel.is_cancelled() {
910 self.cleanup()?;
911 return Err(EncodeError::Cancelled);
912 }
913 let ticks = match &event {
914 EncodeEvent::Video(frame) => frame.pts.ticks(),
915 EncodeEvent::Audio(block) => block.start_time().ticks(),
916 };
917 if self
918 .last_input_ticks
919 .is_some_and(|previous| ticks < previous)
920 {
921 return Err(EncodeError::InvalidTimestamp);
922 }
923 match event {
924 EncodeEvent::Video(frame) => self.write_video(&frame)?,
925 EncodeEvent::Audio(block) => self.write_audio(&block)?,
926 }
927 self.last_input_ticks = Some(ticks);
928 Ok(())
929 }
930 fn finish(&mut self, cancel: &CancelToken) -> Result<(), EncodeError> {
931 if cancel.is_cancelled() {
932 self.cleanup()?;
933 return Err(EncodeError::Cancelled);
934 }
935 let covered = self.audio_input.received_ticks();
938 for frame in self.audio_input.finish() {
939 self.audio.send(AudioCommand::Block(frame, covered))?;
940 }
941 self.audio.send(AudioCommand::Finish(covered))?;
942 self.audio.shutdown();
943 if let Some(error) = self.audio.lock().failure.clone() {
944 return Err(EncodeError::Adapter(error));
945 }
946 timed(&mut self.stages.video_send_frame, || {
947 self.video_encoder.send_frame(None)
948 })
949 .map_err(encode_adapter_error)?;
950 self.drain_video_packets(true)?;
951 self.write_ready_packets(true)?;
952 beat_encode_call();
953 self.output
954 .as_mut()
955 .ok_or_else(|| EncodeError::Adapter("muxer is already closed".into()))?
956 .write_trailer()
957 .map_err(encode_adapter_error)?;
958 if let Some(output) = self.output.take() {
960 beat_encode_call();
961 output
962 .close()
963 .map_err(|error| encode_adapter_error_at("closing the output", error))?;
964 }
965 crate::partial::publish(&self.partial_path, &self.target)?;
966 self.complete = true;
967 Ok(())
968 }
969 fn abort(&mut self) -> Result<(), EncodeError> {
970 self.cleanup()
971 }
972}
973
974impl Drop for NativeAvEncodeSink {
975 fn drop(&mut self) {
976 if !self.complete {
977 let _ = self.cleanup();
978 }
979 }
980}
981
982impl NativeWavEncodeSink {
983 pub fn create(
990 path: impl Into<OutputTarget>,
991 sample_rate: u32,
992 channels: u16,
993 ) -> Result<Self, EncodeError> {
994 let speakers = SpeakerLayout::default_for(usize::from(channels))
995 .ok_or_else(|| EncodeError::Adapter("invalid WAV format".into()))?;
996 Self::create_with_speakers(path, sample_rate, speakers)
997 }
998
999 pub fn create_with_speakers(
1011 path: impl Into<OutputTarget>,
1012 sample_rate: u32,
1013 speakers: SpeakerLayout,
1014 ) -> Result<Self, EncodeError> {
1015 let channels = u16::try_from(speakers.channels())
1016 .ok()
1017 .filter(|_| sample_rate != 0 && speakers.channels() <= MAX_AUDIO_CHANNELS)
1018 .ok_or_else(|| EncodeError::Adapter("invalid WAV format".into()))?;
1019 let (channel_mask, plane_order) = wav_channel_layout(&speakers)?;
1020 let target = path.into();
1021 let partial = PartialOutput::claim(&target)?;
1022 let partial_path = partial.path().to_owned();
1023 let mut file = BufWriter::with_capacity(WAV_WRITE_BUFFER, partial.open_file()?);
1024 file.write_all(&wav_header_with_mask(
1025 sample_rate,
1026 channels,
1027 channel_mask,
1028 0,
1029 )?)
1030 .map_err(encode_adapter_error)?;
1031 let partial_lock = partial.keep();
1032 Ok(Self {
1033 target,
1034 partial_path,
1035 _partial_lock: partial_lock,
1036 file: Some(file),
1037 sample_rate,
1038 channels,
1039 speakers,
1040 channel_mask,
1041 plane_order,
1042 frames_written: 0,
1043 complete: false,
1044 interleaved: Vec::new(),
1045 })
1046 }
1047
1048 pub(crate) fn write_block(&mut self, block: &AudioBlock) -> Result<(), EncodeError> {
1049 if block.sample_rate != self.sample_rate
1050 || block.channels.len() != usize::from(self.channels)
1051 || block.channels.is_empty()
1052 {
1053 return Err(EncodeError::Adapter("audio block format changed".into()));
1054 }
1055 if block.speakers != self.speakers {
1056 return Err(EncodeError::Adapter("audio block speakers changed".into()));
1057 }
1058 let frames = block.channels[0].len();
1059 if block.channels.iter().any(|channel| channel.len() != frames) {
1060 return Err(EncodeError::Adapter("audio channel lengths differ".into()));
1061 }
1062 if u64::try_from(block.start_sample).ok() != Some(self.frames_written) {
1063 return Err(EncodeError::InvalidTimestamp);
1064 }
1065 let frames_after = self
1067 .frames_written
1068 .saturating_add(u64::try_from(frames).unwrap_or(u64::MAX));
1069 self.data_bytes(frames_after)?;
1070 let file = self
1071 .file
1072 .as_mut()
1073 .ok_or_else(|| EncodeError::Adapter("WAV sink is closed".into()))?;
1074 self.interleaved.clear();
1075 for frame in 0..frames {
1076 for plane in &self.plane_order {
1077 self.interleaved
1078 .extend_from_slice(&block.channels[*plane][frame].to_le_bytes());
1079 }
1080 }
1081 file.write_all(&self.interleaved)
1082 .map_err(encode_adapter_error)?;
1083 self.frames_written = self
1084 .frames_written
1085 .saturating_add(u64::try_from(frames).unwrap_or(u64::MAX));
1086 Ok(())
1087 }
1088
1089 fn data_bytes(&self, frames: u64) -> Result<u32, EncodeError> {
1092 let overhead = if self.channel_mask.is_some() {
1093 WAV_EXTENSIBLE_OVERHEAD
1094 } else {
1095 WAV_PLAIN_OVERHEAD
1096 };
1097 #[cfg(test)]
1098 let limit = RIFF_LIMIT.with(std::cell::Cell::get);
1099 #[cfg(not(test))]
1100 let limit = u64::from(u32::MAX);
1101 frames
1102 .checked_mul(u64::from(self.channels))
1103 .and_then(|samples| samples.checked_mul(4))
1104 .filter(|bytes| bytes.saturating_add(u64::from(overhead)) <= limit)
1105 .and_then(|bytes| u32::try_from(bytes).ok())
1106 .ok_or_else(|| {
1107 EncodeError::Adapter(format!(
1108 "{frames} frames of {} channels are more than the 4 GiB a WAV file holds",
1109 self.channels
1110 ))
1111 })
1112 }
1113
1114 pub(crate) fn commit(&mut self) -> Result<(), EncodeError> {
1115 let data_bytes = self.data_bytes(self.frames_written)?;
1116 let header = wav_header_with_mask(
1117 self.sample_rate,
1118 self.channels,
1119 self.channel_mask,
1120 data_bytes,
1121 )?;
1122 let mut file = self
1123 .file
1124 .take()
1125 .ok_or_else(|| EncodeError::Adapter("WAV sink is closed".into()))?;
1126 file.seek(SeekFrom::Start(0))
1127 .and_then(|_| file.write_all(&header))
1128 .and_then(|()| file.flush())
1129 .map_err(encode_adapter_error)?;
1130 drop(file);
1131 crate::partial::publish(&self.partial_path, &self.target)?;
1132 self.complete = true;
1133 Ok(())
1134 }
1135}
1136
1137impl AudioBlockSink for NativeWavEncodeSink {
1138 fn write_audio(&mut self, block: &AudioBlock, cancel: &CancelToken) -> Result<(), EncodeError> {
1139 if cancel.is_cancelled() {
1140 self.abort_audio()?;
1141 return Err(EncodeError::Cancelled);
1142 }
1143 self.write_block(block)
1144 }
1145 fn finish_audio(&mut self, cancel: &CancelToken) -> Result<(), EncodeError> {
1146 if cancel.is_cancelled() {
1147 self.abort_audio()?;
1148 return Err(EncodeError::Cancelled);
1149 }
1150 self.commit()
1151 }
1152 fn abort_audio(&mut self) -> Result<(), EncodeError> {
1153 drop(self.file.take().map(BufWriter::into_parts));
1155 match fs::remove_file(&self.partial_path) {
1156 Ok(()) => Ok(()),
1157 Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(()),
1158 Err(error) => Err(encode_adapter_error(error)),
1159 }
1160 }
1161}
1162
1163impl EncodeSink for NativeWavEncodeSink {
1164 fn write(&mut self, event: EncodeEvent, cancel: &CancelToken) -> Result<(), EncodeError> {
1165 match event {
1166 EncodeEvent::Audio(block) => self.write_audio(&block, cancel),
1167 EncodeEvent::Video(_) => {
1168 Err(EncodeError::Adapter("WAV sink accepts audio only".into()))
1169 }
1170 }
1171 }
1172 fn finish(&mut self, cancel: &CancelToken) -> Result<(), EncodeError> {
1173 self.finish_audio(cancel)
1174 }
1175 fn abort(&mut self) -> Result<(), EncodeError> {
1176 self.abort_audio()
1177 }
1178}
1179
1180impl Drop for NativeWavEncodeSink {
1181 fn drop(&mut self) {
1182 if !self.complete {
1183 let _ = self.abort_audio();
1184 }
1185 }
1186}
1187
1188fn wav_channel_layout(speakers: &SpeakerLayout) -> Result<(Option<u32>, Vec<usize>), EncodeError> {
1191 let identity = (0..speakers.channels()).collect::<Vec<_>>();
1192 if SpeakerLayout::default_for(speakers.channels()) == Some(*speakers) {
1193 return Ok((None, identity));
1194 }
1195 let mut mask = 0_u32;
1196 for speaker in speakers.speakers() {
1197 let bit = speaker.ffmpeg_channel();
1198 if bit >= 18 || mask & (1 << bit) != 0 {
1199 return Err(EncodeError::Adapter(format!(
1200 "a WAVE channel mask cannot say the speakers {}",
1201 speakers.describe()
1202 )));
1203 }
1204 mask |= 1 << bit;
1205 }
1206 let mut order = identity;
1207 order.sort_by_key(|plane| speakers.speakers()[*plane].ffmpeg_channel());
1208 Ok((Some(mask), order))
1209}
1210
1211const WAV_PLAIN_OVERHEAD: u32 = 36;
1213const WAV_EXTENSIBLE_OVERHEAD: u32 = 60;
1215
1216#[cfg(test)]
1217thread_local! {
1218 pub(crate) static RIFF_LIMIT: std::cell::Cell<u64> =
1221 const { std::cell::Cell::new(u32::MAX as u64) };
1222}
1223
1224fn riff_size(data_bytes: u32, overhead: u32) -> Result<u32, EncodeError> {
1225 data_bytes
1226 .checked_add(overhead)
1227 .ok_or_else(|| EncodeError::Adapter("WAV exceeds the RIFF size limit".into()))
1228}
1229
1230pub(crate) fn wav_header_with_mask(
1236 sample_rate: u32,
1237 channels: u16,
1238 channel_mask: Option<u32>,
1239 data_bytes: u32,
1240) -> Result<Vec<u8>, EncodeError> {
1241 let Some(mask) = channel_mask else {
1242 return Ok(wav_header(sample_rate, channels, data_bytes)?.to_vec());
1243 };
1244 let plain = wav_header(sample_rate, channels, 0)?;
1245 let mut header = Vec::with_capacity(68);
1246 header.extend_from_slice(b"RIFF");
1247 header.extend_from_slice(&riff_size(data_bytes, WAV_EXTENSIBLE_OVERHEAD)?.to_le_bytes());
1248 header.extend_from_slice(b"WAVEfmt ");
1249 header.extend_from_slice(&40_u32.to_le_bytes());
1250 header.extend_from_slice(&0xfffe_u16.to_le_bytes());
1251 header.extend_from_slice(&plain[22..36]);
1253 header.extend_from_slice(&22_u16.to_le_bytes());
1254 header.extend_from_slice(&32_u16.to_le_bytes());
1255 header.extend_from_slice(&mask.to_le_bytes());
1256 header.extend_from_slice(&[
1258 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b,
1259 0x71,
1260 ]);
1261 header.extend_from_slice(b"data");
1262 header.extend_from_slice(&data_bytes.to_le_bytes());
1263 Ok(header)
1264}
1265
1266pub(crate) fn wav_header(
1271 sample_rate: u32,
1272 channels: u16,
1273 data_bytes: u32,
1274) -> Result<[u8; 44], EncodeError> {
1275 let mut header = [0_u8; 44];
1276 header[0..4].copy_from_slice(b"RIFF");
1277 header[4..8].copy_from_slice(&riff_size(data_bytes, WAV_PLAIN_OVERHEAD)?.to_le_bytes());
1278 header[8..12].copy_from_slice(b"WAVE");
1279 header[12..16].copy_from_slice(b"fmt ");
1280 header[16..20].copy_from_slice(&16_u32.to_le_bytes());
1281 header[20..22].copy_from_slice(&3_u16.to_le_bytes());
1282 header[22..24].copy_from_slice(&channels.to_le_bytes());
1283 header[24..28].copy_from_slice(&sample_rate.to_le_bytes());
1284 let byte_rate = sample_rate
1285 .saturating_mul(u32::from(channels))
1286 .saturating_mul(4);
1287 header[28..32].copy_from_slice(&byte_rate.to_le_bytes());
1288 header[32..34].copy_from_slice(&channels.saturating_mul(4).to_le_bytes());
1289 header[34..36].copy_from_slice(&32_u16.to_le_bytes());
1290 header[36..40].copy_from_slice(b"data");
1291 header[40..44].copy_from_slice(&data_bytes.to_le_bytes());
1292 Ok(header)
1293}