use log::warn;
use crate::{AMediaFormat, BufferInfo, MediaFormat, MediaStatus};
#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct AMediaMuxer {
_data: [u8; 0],
_marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub enum OutputFormat {
Mpeg4 = 0,
Webm = 1,
ThreeGpp = 2,
}
#[link(name = "mediandk")]
extern "C" {
pub fn AMediaMuxer_new(fd: i32, format: OutputFormat) -> *mut AMediaMuxer;
pub fn AMediaMuxer_delete(muxer: *mut AMediaMuxer) -> MediaStatus;
pub fn AMediaMuxer_setLocation(
muxer: *mut AMediaMuxer,
latitude: f32,
longitude: f32,
) -> MediaStatus;
pub fn AMediaMuxer_setOrientationHint(muxer: *mut AMediaMuxer, degrees: i32) -> MediaStatus;
pub fn AMediaMuxer_addTrack(muxer: *mut AMediaMuxer, format: *const AMediaFormat) -> isize;
pub fn AMediaMuxer_start(muxer: *mut AMediaMuxer) -> MediaStatus;
pub fn AMediaMuxer_stop(muxer: *mut AMediaMuxer) -> MediaStatus;
pub fn AMediaMuxer_writeSampleData(
muxer: *mut AMediaMuxer,
track_index: usize,
data: *const u8,
info: *const BufferInfo,
) -> MediaStatus;
}
#[derive(Debug, Eq, PartialEq)]
enum MuxerState {
Uninitialized,
Started,
}
#[derive(Debug)]
pub struct MediaMuxer {
inner: *mut AMediaMuxer,
latitude: f32,
longitude: f32,
orientation_hint: i32,
track_formats: Vec<MediaFormat>,
state: MuxerState,
}
impl MediaMuxer {
pub fn new(fd: i32, output_format: OutputFormat) -> Option<Self> {
let value = unsafe { AMediaMuxer_new(fd, output_format) };
if value.is_null() {
return None;
}
Some(Self {
inner: value,
latitude: 0f32,
longitude: 0f32,
orientation_hint: 0,
track_formats: vec![],
state: MuxerState::Uninitialized,
})
}
pub fn set_location(&mut self, latitude: f32, longitude: f32) -> &mut Self {
match latitude {
value if value >= -90.0 && value <= 90.0 => self.latitude = latitude,
_ => {}
}
match longitude {
value if value >= -180.0 && value <= 180.0 => self.longitude = longitude,
_ => {}
}
self
}
pub fn set_orientation_hint(&mut self, degrees: i32) -> &mut Self {
match degrees {
0 | 90 | 180 | 270 => self.orientation_hint = degrees,
hint => warn!("Unsupported orientation hint passed to MediaMuxer: {hint}"),
}
self
}
pub fn add_track(&mut self, format: MediaFormat) -> Result<isize, MediaStatus> {
let result = unsafe { AMediaMuxer_addTrack(self.inner, format.inner) };
let result = MediaStatus::make_result(result)?;
self.track_formats.push(format);
Ok(result)
}
pub fn track_count(&self) -> usize {
self.track_formats.len()
}
pub fn format(&self, index: usize) -> Option<&MediaFormat> {
if index >= self.track_formats.len() {
return None;
}
Some(&self.track_formats[index])
}
pub fn start(&mut self) -> Result<(), MediaStatus> {
if let MuxerState::Started = self.state {
return Ok(());
}
if self.track_formats.is_empty() {
return Err(MediaStatus::ErrorInvalidOperation);
}
unsafe {
AMediaMuxer_setLocation(self.inner, self.latitude, self.longitude).result()?;
AMediaMuxer_setOrientationHint(self.inner, self.orientation_hint).result()?;
AMediaMuxer_start(self.inner).result()?;
self.state = MuxerState::Started;
}
Ok(())
}
pub fn stop(mut self) -> Result<(), MediaStatus> {
if let MuxerState::Uninitialized = self.state {
return Ok(());
}
self.state = MuxerState::Uninitialized;
unsafe { AMediaMuxer_stop(self.inner) }.result().map(|_| ())
}
pub fn write_sample_data(
&mut self,
track_index: usize,
data: &[u8],
buffer_info: &BufferInfo,
) -> Result<(), MediaStatus> {
if let MuxerState::Uninitialized = self.state {
return Err(MediaStatus::ErrorInvalidOperation);
}
unsafe { AMediaMuxer_writeSampleData(self.inner, track_index, data.as_ptr(), buffer_info) }
.result()?;
Ok(())
}
}
unsafe impl Send for MediaMuxer {}
unsafe impl Sync for MediaMuxer {}
impl Drop for MediaMuxer {
fn drop(&mut self) {
unsafe {
AMediaMuxer_delete(self.inner);
}
}
}