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(i32)]
#[derive(Clone, Copy, Debug)]
pub enum OutputFormat {
Mpeg4 = 0,
Webm = 1,
ThreeGpp = 2,
}
#[link(name = "mediandk")]
unsafe extern "C" {
fn AMediaMuxer_new(fd: i32, format: OutputFormat) -> *mut AMediaMuxer;
fn AMediaMuxer_delete(muxer: *mut AMediaMuxer) -> i32;
fn AMediaMuxer_setLocation(muxer: *mut AMediaMuxer, latitude: f32, longitude: f32) -> i32;
fn AMediaMuxer_setOrientationHint(muxer: *mut AMediaMuxer, degrees: i32) -> i32;
fn AMediaMuxer_addTrack(muxer: *mut AMediaMuxer, format: *const AMediaFormat) -> isize;
fn AMediaMuxer_start(muxer: *mut AMediaMuxer) -> i32;
fn AMediaMuxer_stop(muxer: *mut AMediaMuxer) -> i32;
fn AMediaMuxer_writeSampleData(
muxer: *mut AMediaMuxer,
track_index: usize,
data: *const u8,
info: *const BufferInfo,
) -> i32;
}
#[derive(Debug, Eq, PartialEq)]
enum MuxerState {
Uninitialized,
Started,
}
#[derive(Debug)]
pub struct MediaMuxer {
inner: *mut AMediaMuxer,
latitude: f32,
longitude: f32,
location_set: bool,
orientation_hint: i32,
track_formats: Vec<MediaFormat>,
state: MuxerState,
}
impl MediaMuxer {
pub fn new(fd: i32, output_format: OutputFormat) -> Result<Self, MediaStatus> {
let value = unsafe { AMediaMuxer_new(fd, output_format) };
if value.is_null() {
return Err(MediaStatus::ErrorUnknown);
}
Ok(Self {
inner: value,
latitude: 0f32,
longitude: 0f32,
location_set: false,
orientation_hint: 0,
track_formats: vec![],
state: MuxerState::Uninitialized,
})
}
pub fn set_location(
&mut self,
latitude: f32,
longitude: f32,
) -> Result<&mut Self, MediaStatus> {
if !latitude.is_finite()
|| !longitude.is_finite()
|| !(-90.0..=90.0).contains(&latitude)
|| !(-180.0..=180.0).contains(&longitude)
{
return Err(MediaStatus::ErrorInvalidParameter);
}
self.latitude = latitude;
self.longitude = longitude;
self.location_set = true;
Ok(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<i32, MediaStatus> {
let result = unsafe { AMediaMuxer_addTrack(self.inner, format.inner.ptr) };
if result < 0 {
return Err(MediaStatus::from_i32(result as i32));
}
let track_index = result as i32;
self.track_formats.push(format);
Ok(track_index)
}
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 {
if self.location_set {
MediaStatus::make_result(AMediaMuxer_setLocation(
self.inner,
self.latitude,
self.longitude,
))?;
}
if self.orientation_hint != 0 {
MediaStatus::make_result(AMediaMuxer_setOrientationHint(
self.inner,
self.orientation_hint,
))?;
}
MediaStatus::make_result(AMediaMuxer_start(self.inner))?;
self.state = MuxerState::Started;
}
Ok(())
}
pub fn stop(self) -> Result<(), MediaStatus> {
if let MuxerState::Uninitialized = self.state {
return Err(MediaStatus::ErrorInvalidOperation);
}
unsafe { MediaStatus::make_result(AMediaMuxer_stop(self.inner)) }
}
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);
}
if track_index >= self.track_formats.len() {
return Err(MediaStatus::ErrorInvalidParameter);
}
if buffer_info.offset < 0 || buffer_info.size < 0 {
return Err(MediaStatus::ErrorInvalidParameter);
}
let end = (buffer_info.offset as usize)
.checked_add(buffer_info.size as usize)
.ok_or(MediaStatus::ErrorInvalidParameter)?;
if end > data.len() {
return Err(MediaStatus::ErrorInvalidParameter);
}
unsafe {
MediaStatus::make_result(AMediaMuxer_writeSampleData(
self.inner,
track_index,
data.as_ptr(),
buffer_info,
))
}
}
}
impl Drop for MediaMuxer {
fn drop(&mut self) {
if !self.inner.is_null() {
unsafe {
AMediaMuxer_delete(self.inner);
}
}
}
}