#![forbid(unsafe_code)]
use std::collections::VecDeque;
use crate::error::PipelineError;
use crate::filter::{FilterError, FrameFilter};
use mediaway_common::{AudioFrame, StreamInfo, VideoFrame, VideoFrameStorage};
use mediaway_container::{ContainerError, Mux, MuxOpen, mp4};
use mediaway_encoder::{AudioEncoder, VideoEncoder};
use mediaway_sw::apm::{AudioProcessor, VoiceActivityDetector};
use smallvec::SmallVec;
struct AudioTrack {
encoder: Box<dyn AudioEncoder>,
track_id: u32,
processor: Option<AudioProcessor>,
vad: Option<VoiceActivityDetector>,
vad_scores: VecDeque<f32>,
}
pub struct EncodeSession<E: VideoEncoder, M: MuxOpen = mp4::Muxer<mp4::Open>> {
encoder: E,
muxer: M::Live,
track_id: u32,
filters: SmallVec<[Box<dyn FrameFilter>; 4]>,
audio: Option<AudioTrack>,
}
impl<E: VideoEncoder> EncodeSession<E, mp4::Muxer<mp4::Open>> {
pub fn open(encoder: E) -> Result<Self, PipelineError> {
Self::open_in(mp4::Muxer::new(), encoder)
}
pub fn open_with_audio(
encoder: E,
audio_encoder: impl AudioEncoder + 'static,
) -> Result<Self, PipelineError> {
Self::open_in_with_audio(mp4::Muxer::new(), encoder, audio_encoder)
}
}
impl<E: VideoEncoder, M: MuxOpen> EncodeSession<E, M>
where
M::Error: Into<ContainerError>,
{
pub fn open_in(mut muxer: M, encoder: E) -> Result<Self, PipelineError> {
let info = encoder.stream_info().clone();
let info = if info.id() < M::FIRST_TRACK_ID {
info.with_id(M::FIRST_TRACK_ID)
} else {
info
};
let track_id = muxer.add_track(info).map_err(Into::into)?;
Ok(Self {
encoder,
muxer: muxer.begin(),
track_id,
filters: SmallVec::new(),
audio: None,
})
}
pub fn open_in_with_audio(
mut muxer: M,
encoder: E,
audio_encoder: impl AudioEncoder + 'static,
) -> Result<Self, PipelineError> {
let video_id = M::FIRST_TRACK_ID;
let audio_id = M::FIRST_TRACK_ID + 1;
let track_id = muxer
.add_track(encoder.stream_info().clone().with_id(video_id))
.map_err(Into::into)?;
let audio_track_id = muxer
.add_track(audio_encoder.stream_info().clone().with_id(audio_id))
.map_err(Into::into)?;
Ok(Self {
encoder,
muxer: muxer.begin(),
track_id,
filters: SmallVec::new(),
audio: Some(AudioTrack {
encoder: Box::new(audio_encoder),
track_id: audio_track_id,
processor: None,
vad: None,
vad_scores: VecDeque::new(),
}),
})
}
pub fn attach_audio_processor(
&mut self,
processor: AudioProcessor,
) -> Result<&mut Self, PipelineError> {
let audio = self.audio.as_mut().ok_or(PipelineError::NoAudioTrack)?;
audio.processor = Some(processor);
Ok(self)
}
pub fn attach_vad(&mut self, vad: VoiceActivityDetector) -> Result<&mut Self, PipelineError> {
let audio = self.audio.as_mut().ok_or(PipelineError::NoAudioTrack)?;
audio.vad = Some(vad);
Ok(self)
}
pub fn push_filter<F: FrameFilter>(&mut self, filter: F) -> &mut Self {
self.filters.push(Box::new(filter));
self
}
pub fn write_frame(&mut self, frame: &VideoFrame) -> Result<(), PipelineError> {
if self.filters.is_empty() {
self.encoder.push_frame(frame)?; } else {
if matches!(frame.storage, VideoFrameStorage::Gpu(_)) {
return Err(PipelineError::Filter(FilterError::GpuFrameUnsupported));
}
let mut current = frame.clone();
for filter in &mut self.filters {
current = filter.process(current)?;
}
self.encoder.push_frame(¤t)?;
}
self.drain()
}
pub fn set_bitrate(&mut self, bitrate_bps: u32) -> Result<(), PipelineError> {
self.encoder.set_bitrate(bitrate_bps)?;
Ok(())
}
pub fn write_audio_frame(&mut self, frame: &AudioFrame) -> Result<(), PipelineError> {
let Self { audio, muxer, .. } = self;
let Some(audio) = audio.as_mut() else {
return Err(PipelineError::NoAudioTrack);
};
if let Some(processor) = audio.processor.as_mut() {
processor.push_capture_frame(frame)?;
while let Some(block) = processor.poll_processed_frame()? {
if let Some(vad) = audio.vad.as_mut() {
if let Ok(score) = vad.analyze(&block) {
audio.vad_scores.push_back(score);
}
}
audio.encoder.push_frame(&block)?;
}
} else {
audio.encoder.push_frame(frame)?;
}
Self::drain_audio(audio, muxer)
}
pub fn write_audio_render_frame(&mut self, frame: &AudioFrame) -> Result<(), PipelineError> {
let Some(audio) = self.audio.as_mut() else {
return Err(PipelineError::NoAudioTrack);
};
if let Some(processor) = audio.processor.as_mut() {
processor.push_render_frame(frame)?;
}
Ok(())
}
pub fn poll_vad_score(&mut self) -> Option<f32> {
self.audio.as_mut()?.vad_scores.pop_front()
}
pub fn poll_bytes(&mut self, out: &mut Vec<u8>) -> usize {
self.muxer.poll_bytes(out)
}
pub fn finish_into(mut self, out: &mut Vec<u8>) -> Result<usize, PipelineError> {
self.encoder.flush()?;
self.drain()?;
if let Some(mut audio) = self.audio.take() {
audio.encoder.flush()?;
Self::drain_audio(&mut audio, &mut self.muxer)?;
}
self.muxer.flush();
Ok(self.muxer.poll_bytes(out))
}
pub fn finish(self) -> Result<Vec<u8>, PipelineError> {
let mut bytes = Vec::new();
self.finish_into(&mut bytes)?;
Ok(bytes)
}
fn drain(&mut self) -> Result<(), PipelineError> {
while let Some(mut pkt) = self.encoder.poll_packet()? {
if let StreamInfo::Video { extra_data, .. } = self.encoder.stream_info()
&& !extra_data.is_empty()
{
self.muxer
.set_track_extra_data(self.track_id, extra_data.clone());
}
pkt.stream_id = self.track_id;
self.muxer.push_packet(&pkt).map_err(Into::into)?;
}
Ok(())
}
fn drain_audio(audio: &mut AudioTrack, muxer: &mut M::Live) -> Result<(), PipelineError> {
while let Some(mut pkt) = audio.encoder.poll_packet()? {
pkt.stream_id = audio.track_id;
muxer.push_packet(&pkt).map_err(Into::into)?;
}
Ok(())
}
}
#[cfg(test)]
#[path = "session_tests.rs"]
mod tests;