use derive_more::{IsVariant, TryUnwrap, Unwrap};
use ffmpeg_next::{codec::Parameters, ffi::avsubtitle_free};
use mediadecode::{
Timebase, decoder::SubtitleDecoder, frame::SubtitleFrame, packet::SubtitlePacket,
};
use crate::{
DecoderLimits, Error, Ffmpeg, boundary,
convert::{self, ConvertError},
decoder::build_codec_context,
extras::{SubtitleFrameExtra, SubtitlePacketExtra},
};
struct ScratchSubtitle {
inner: ffmpeg_next::Subtitle,
}
impl ScratchSubtitle {
fn new() -> Self {
Self {
inner: ffmpeg_next::Subtitle::new(),
}
}
fn clear(&mut self) {
unsafe { avsubtitle_free(self.inner.as_mut_ptr()) };
}
}
impl Drop for ScratchSubtitle {
fn drop(&mut self) {
self.clear();
}
}
pub struct CarrierSubtitleStreamDecoder<C: crate::FfmpegCarrier> {
decoder: ffmpeg_next::decoder::Subtitle,
scratch: ScratchSubtitle,
scratch_pending: bool,
time_base: Timebase,
limits: DecoderLimits,
_callback_state: Box<crate::ffi::CallbackState>,
_carrier: core::marker::PhantomData<C>,
}
impl<C: crate::FfmpegCarrier + crate::CarrierOps> CarrierSubtitleStreamDecoder<C> {
pub(crate) fn open_impl(
parameters: Parameters,
time_base: Timebase,
limits: DecoderLimits,
) -> Result<Self, SubtitleDecodeError> {
let (ctx, callback_state) =
build_codec_context(¶meters, limits).map_err(SubtitleDecodeError::Decode)?;
let codec = crate::decoder::find_decoder(¶meters).map_err(SubtitleDecodeError::Decode)?;
let opened = ctx
.decoder()
.open_as(codec)
.map_err(|e| SubtitleDecodeError::Decode(Error::Ffmpeg(e)))?;
crate::decoder::ensure_codec_type(
&opened,
ffmpeg_next::ffi::AVMediaType::AVMEDIA_TYPE_SUBTITLE,
)
.map_err(SubtitleDecodeError::Decode)?;
let decoder = ffmpeg_next::decoder::Subtitle(opened);
Ok(Self {
decoder,
scratch: ScratchSubtitle::new(),
scratch_pending: false,
time_base,
limits,
_callback_state: callback_state,
_carrier: core::marker::PhantomData,
})
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) const fn time_base_impl(&self) -> Timebase {
self.time_base
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) const fn limits_impl(&self) -> DecoderLimits {
self.limits
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub(crate) const fn inner_impl(&self) -> &ffmpeg_next::decoder::Subtitle {
&self.decoder
}
}
impl<C: crate::FfmpegCarrier + crate::CarrierOps> CarrierSubtitleStreamDecoder<C> {
pub(crate) fn send_packet_impl(
&mut self,
packet: &SubtitlePacket<SubtitlePacketExtra, C::Buffer>,
) -> Result<(), SubtitleDecodeError> {
if self.scratch_pending {
return Err(SubtitleDecodeError::FramePending);
}
self.scratch.clear();
let state: *const crate::ffi::CallbackState = &*self._callback_state;
let decoder = &mut self.decoder;
let scratch = &mut self.scratch.inner;
let got = boundary::with_ffmpeg_subtitle_packet::<C, _>(
packet,
self.limits.packet_limits(),
crate::carrier::BodyRoute::Submission,
|av_pkt| {
decoder.decode(av_pkt, scratch).map_err(|e| {
SubtitleDecodeError::Decode(crate::decoder::software_exit(unsafe { &*state }, e))
})
},
)
.map_err(|e| SubtitleDecodeError::Decode(Error::PacketBuild(e)))??;
self.scratch_pending = got;
Ok(())
}
pub(crate) fn receive_frame_impl(
&mut self,
dst: &mut SubtitleFrame<SubtitleFrameExtra, C::Buffer>,
) -> Result<(), SubtitleDecodeError> {
if !self.scratch_pending {
return Err(SubtitleDecodeError::NoFrameReady);
}
let converted = unsafe {
convert::av_subtitle_to_subtitle_frame_as::<C>(self.scratch.inner.as_ptr(), self.time_base)
};
match converted {
Ok(frame) => {
self.scratch.clear();
self.scratch_pending = false;
*dst = frame;
Ok(())
}
Err(e) if e.parks_in_decode() => {
Err(SubtitleDecodeError::Convert(e))
}
Err(e) => {
self.scratch.clear();
self.scratch_pending = false;
Err(SubtitleDecodeError::Convert(e))
}
}
}
pub(crate) fn send_eof_impl(&mut self) -> Result<(), SubtitleDecodeError> {
Ok(())
}
pub(crate) fn flush_impl(&mut self) -> Result<(), SubtitleDecodeError> {
self.decoder.flush();
self.scratch_pending = false;
self.scratch.clear();
Ok(())
}
}
macro_rules! subtitle_lane_face {
($($lane:ty),+ $(,)?) => { $(
impl CarrierSubtitleStreamDecoder<$lane> {
pub fn open(
parameters: Parameters,
time_base: Timebase,
limits: DecoderLimits,
) -> Result<Self, SubtitleDecodeError> {
Self::open_impl(parameters, time_base, limits)
}
pub const fn time_base(&self) -> Timebase {
self.time_base_impl()
}
pub const fn limits(&self) -> DecoderLimits {
self.limits_impl()
}
pub const fn inner(&self) -> &ffmpeg_next::decoder::Subtitle {
self.inner_impl()
}
}
impl SubtitleDecoder for CarrierSubtitleStreamDecoder<$lane> {
type Adapter = Ffmpeg;
type Buffer = <$lane as crate::FfmpegCarrier>::Buffer;
type Error = SubtitleDecodeError;
fn send_packet(
&mut self,
packet: &SubtitlePacket<SubtitlePacketExtra, Self::Buffer>,
) -> Result<(), Self::Error> {
self.send_packet_impl(packet)
}
fn receive_frame(
&mut self,
dst: &mut SubtitleFrame<SubtitleFrameExtra, Self::Buffer>,
) -> Result<(), Self::Error> {
self.receive_frame_impl(dst)
}
fn send_eof(&mut self) -> Result<(), Self::Error> {
self.send_eof_impl()
}
fn flush(&mut self) -> Result<(), Self::Error> {
self.flush_impl()
}
}
)+ };
}
subtitle_lane_face!(crate::View, crate::Owned);
#[derive(thiserror::Error, Debug, IsVariant, Unwrap, TryUnwrap)]
#[unwrap(ref, ref_mut)]
#[try_unwrap(ref, ref_mut)]
pub enum SubtitleDecodeError {
#[error(transparent)]
Decode(#[from] Error),
#[error(transparent)]
Convert(#[from] ConvertError),
#[error("no subtitle frame ready; send another packet first")]
NoFrameReady,
#[error("subtitle frame already pending; drain via receive_frame first")]
FramePending,
}