2021-06-22 20:08:08 +00:00
// Copyright (C) 2021 Mathieu Duponchelle <mathieu@centricular.com>
//
2022-01-15 18:40:12 +00:00
// This Source Code Form is subject to the terms of the Mozilla Public License, v2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at
// <https://mozilla.org/MPL/2.0/>.
2021-06-22 20:08:08 +00:00
//
2022-01-15 18:40:12 +00:00
// SPDX-License-Identifier: MPL-2.0
2021-06-22 20:08:08 +00:00
use crate ::ttutils ::Cea608Mode ;
2023-03-24 23:14:46 +00:00
use anyhow ::{ anyhow , Error } ;
2021-06-22 20:08:08 +00:00
use gst ::glib ;
use gst ::prelude ::* ;
use gst ::subclass ::prelude ::* ;
2023-03-24 23:14:46 +00:00
use std ::collections ::HashMap ;
2021-06-22 20:08:08 +00:00
use std ::sync ::Mutex ;
use once_cell ::sync ::Lazy ;
2022-03-07 16:06:12 +00:00
use super ::CaptionSource ;
2021-06-22 20:08:08 +00:00
static CAT : Lazy < gst ::DebugCategory > = Lazy ::new ( | | {
gst ::DebugCategory ::new (
" transcriberbin " ,
gst ::DebugColorFlags ::empty ( ) ,
Some ( " Transcribe and inject closed captions " ) ,
)
} ) ;
const DEFAULT_PASSTHROUGH : bool = false ;
const DEFAULT_LATENCY : gst ::ClockTime = gst ::ClockTime ::from_seconds ( 4 ) ;
2023-03-24 23:14:46 +00:00
const DEFAULT_TRANSLATE_LATENCY : gst ::ClockTime = gst ::ClockTime ::from_mseconds ( 500 ) ;
2021-06-22 20:08:08 +00:00
const DEFAULT_ACCUMULATE : gst ::ClockTime = gst ::ClockTime ::ZERO ;
const DEFAULT_MODE : Cea608Mode = Cea608Mode ::RollUp2 ;
2022-03-07 16:06:12 +00:00
const DEFAULT_CAPTION_SOURCE : CaptionSource = CaptionSource ::Both ;
2021-06-22 20:08:08 +00:00
2023-03-24 23:14:46 +00:00
const CEA608MUX_LATENCY : gst ::ClockTime = gst ::ClockTime ::from_mseconds ( 100 ) ;
/* One per language, including original */
struct TranscriptionChannel {
queue : gst ::Element ,
textwrap : gst ::Element ,
tttocea608 : gst ::Element ,
language : String ,
}
impl TranscriptionChannel {
fn link_transcriber ( & self , transcriber : & gst ::Element ) -> Result < ( ) , Error > {
let transcriber_src_pad = match self . language . as_str ( ) {
" transcript " = > transcriber
. static_pad ( " src " )
. ok_or ( anyhow! ( " Failed to retrieve transcription source pad " ) ) ? ,
language = > {
let pad = transcriber
. request_pad_simple ( " translate_src_%u " )
. ok_or ( anyhow! ( " Failed to request translation source pad " ) ) ? ;
pad . set_property ( " language-code " , language ) ;
pad
}
} ;
transcriber_src_pad . link ( & self . queue . static_pad ( " sink " ) . unwrap ( ) ) ? ;
Ok ( ( ) )
}
}
2021-06-22 20:08:08 +00:00
struct State {
framerate : Option < gst ::Fraction > ,
tearing_down : bool ,
internal_bin : gst ::Bin ,
audio_queue_passthrough : gst ::Element ,
video_queue : gst ::Element ,
audio_tee : gst ::Element ,
transcriber_aconv : gst ::Element ,
transcriber : gst ::Element ,
cccombiner : gst ::Element ,
transcription_bin : gst ::Bin ,
2023-03-24 23:14:46 +00:00
transcription_channels : HashMap < String , TranscriptionChannel > ,
2021-06-22 20:08:08 +00:00
cccapsfilter : gst ::Element ,
2022-03-07 16:06:12 +00:00
transcription_valve : gst ::Element ,
2021-06-22 20:08:08 +00:00
}
struct Settings {
cc_caps : gst ::Caps ,
latency : gst ::ClockTime ,
2023-03-24 23:14:46 +00:00
translate_latency : gst ::ClockTime ,
2021-06-22 20:08:08 +00:00
passthrough : bool ,
accumulate_time : gst ::ClockTime ,
mode : Cea608Mode ,
2022-03-07 16:06:12 +00:00
caption_source : CaptionSource ,
2023-03-24 23:14:46 +00:00
translation_languages : Option < gst ::Structure > ,
2021-06-22 20:08:08 +00:00
}
impl Default for Settings {
fn default ( ) -> Self {
Self {
cc_caps : gst ::Caps ::builder ( " closedcaption/x-cea-608 " )
2021-11-06 07:34:10 +00:00
. field ( " format " , " raw " )
2021-06-22 20:08:08 +00:00
. build ( ) ,
passthrough : DEFAULT_PASSTHROUGH ,
latency : DEFAULT_LATENCY ,
2023-03-24 23:14:46 +00:00
translate_latency : DEFAULT_TRANSLATE_LATENCY ,
2021-06-22 20:08:08 +00:00
accumulate_time : DEFAULT_ACCUMULATE ,
mode : DEFAULT_MODE ,
2022-03-07 16:06:12 +00:00
caption_source : DEFAULT_CAPTION_SOURCE ,
2023-03-24 23:14:46 +00:00
translation_languages : None ,
2021-06-22 20:08:08 +00:00
}
}
}
// Struct containing all the element data
pub struct TranscriberBin {
audio_srcpad : gst ::GhostPad ,
video_srcpad : gst ::GhostPad ,
audio_sinkpad : gst ::GhostPad ,
video_sinkpad : gst ::GhostPad ,
state : Mutex < Option < State > > ,
settings : Mutex < Settings > ,
}
impl TranscriberBin {
2022-10-09 13:06:59 +00:00
fn construct_transcription_bin ( & self , state : & mut State ) -> Result < ( ) , Error > {
gst ::debug! ( CAT , imp : self , " Building transcription bin " ) ;
2021-06-22 20:08:08 +00:00
2022-10-19 16:18:43 +00:00
let aqueue_transcription = gst ::ElementFactory ::make ( " queue " )
. name ( " transqueue " )
. property ( " max-size-buffers " , 0 u32 )
. property ( " max-size-bytes " , 0 u32 )
. property ( " max-size-time " , 5_000_000_000 u64 )
. property_from_str ( " leaky " , " downstream " )
. build ( ) ? ;
2023-04-01 13:58:20 +00:00
let ccmux = gst ::ElementFactory ::make ( " cea608mux " )
. property_from_str ( " start-time-selection " , " first " )
. build ( ) ? ;
2022-10-19 16:18:43 +00:00
let ccconverter = gst ::ElementFactory ::make ( " ccconverter " ) . build ( ) ? ;
2021-06-22 20:08:08 +00:00
2023-03-09 15:30:57 +00:00
state . transcription_bin . add_many ( [
2021-06-22 20:08:08 +00:00
& aqueue_transcription ,
& state . transcriber_aconv ,
& state . transcriber ,
2023-03-24 23:14:46 +00:00
& ccmux ,
2021-06-22 20:08:08 +00:00
& ccconverter ,
& state . cccapsfilter ,
2022-03-07 16:06:12 +00:00
& state . transcription_valve ,
2021-06-22 20:08:08 +00:00
] ) ? ;
2023-03-09 15:30:57 +00:00
gst ::Element ::link_many ( [
2021-06-22 20:08:08 +00:00
& aqueue_transcription ,
& state . transcriber_aconv ,
& state . transcriber ,
2023-03-24 23:14:46 +00:00
] ) ? ;
gst ::Element ::link_many ( [
& ccmux ,
2021-06-22 20:08:08 +00:00
& ccconverter ,
& state . cccapsfilter ,
2022-03-07 16:06:12 +00:00
& state . transcription_valve ,
2021-06-22 20:08:08 +00:00
] ) ? ;
2023-03-24 23:14:46 +00:00
for ( padname , channel ) in & state . transcription_channels {
let channel_capsfilter = gst ::ElementFactory ::make ( " capsfilter " ) . build ( ) ? ;
let channel_converter = gst ::ElementFactory ::make ( " ccconverter " ) . build ( ) ? ;
state . transcription_bin . add_many ( [
& channel . queue ,
& channel . textwrap ,
& channel . tttocea608 ,
& channel_capsfilter ,
& channel_converter ,
] ) ? ;
channel . link_transcriber ( & state . transcriber ) ? ;
gst ::Element ::link_many ( [
& channel . queue ,
& channel . textwrap ,
& channel . tttocea608 ,
& channel_capsfilter ,
& channel_converter ,
] ) ? ;
let ccmux_pad = ccmux
. request_pad_simple ( padname )
. ok_or ( anyhow! ( " Failed to request ccmux sink pad " ) ) ? ;
channel_converter
. static_pad ( " src " )
. unwrap ( )
. link ( & ccmux_pad ) ? ;
channel_capsfilter . set_property (
" caps " ,
gst ::Caps ::builder ( " closedcaption/x-cea-608 " )
. field ( " format " , " raw " )
. field ( " framerate " , gst ::Fraction ::new ( 30000 , 1001 ) )
. build ( ) ,
) ;
channel . queue . set_property ( " max-size-buffers " , 0 u32 ) ;
channel . queue . set_property ( " max-size-time " , 0 u64 ) ;
channel . textwrap . set_property ( " lines " , 2 u32 ) ;
}
ccmux . set_property ( " latency " , CEA608MUX_LATENCY ) ;
2021-06-22 20:08:08 +00:00
let transcription_audio_sinkpad = gst ::GhostPad ::with_target (
Some ( " sink " ) ,
& aqueue_transcription . static_pad ( " sink " ) . unwrap ( ) ,
) ? ;
let transcription_audio_srcpad = gst ::GhostPad ::with_target (
Some ( " src " ) ,
2022-03-07 16:06:12 +00:00
& state . transcription_valve . static_pad ( " src " ) . unwrap ( ) ,
2021-06-22 20:08:08 +00:00
) ? ;
state
. transcription_bin
. add_pad ( & transcription_audio_sinkpad ) ? ;
state
. transcription_bin
. add_pad ( & transcription_audio_srcpad ) ? ;
state . internal_bin . add ( & state . transcription_bin ) ? ;
state . transcription_bin . set_locked_state ( true ) ;
Ok ( ( ) )
}
2022-10-09 13:06:59 +00:00
fn construct_internal_bin ( & self , state : & mut State ) -> Result < ( ) , Error > {
2022-10-19 16:18:43 +00:00
let aclocksync = gst ::ElementFactory ::make ( " clocksync " ) . build ( ) ? ;
2021-06-22 20:08:08 +00:00
2022-10-19 16:18:43 +00:00
let vclocksync = gst ::ElementFactory ::make ( " clocksync " ) . build ( ) ? ;
2021-06-22 20:08:08 +00:00
2023-03-09 15:30:57 +00:00
state . internal_bin . add_many ( [
2021-06-22 20:08:08 +00:00
& aclocksync ,
& state . audio_tee ,
& state . audio_queue_passthrough ,
& vclocksync ,
& state . video_queue ,
& state . cccombiner ,
] ) ? ;
aclocksync . link ( & state . audio_tee ) ? ;
state
. audio_tee
. link_pads ( Some ( " src_%u " ) , & state . audio_queue_passthrough , Some ( " sink " ) ) ? ;
vclocksync . link ( & state . video_queue ) ? ;
state
. video_queue
. link_pads ( Some ( " src " ) , & state . cccombiner , Some ( " sink " ) ) ? ;
let internal_audio_sinkpad = gst ::GhostPad ::with_target (
Some ( " audio_sink " ) ,
& aclocksync . static_pad ( " sink " ) . unwrap ( ) ,
) ? ;
let internal_audio_srcpad = gst ::GhostPad ::with_target (
Some ( " audio_src " ) ,
& state . audio_queue_passthrough . static_pad ( " src " ) . unwrap ( ) ,
) ? ;
let internal_video_sinkpad = gst ::GhostPad ::with_target (
Some ( " video_sink " ) ,
& vclocksync . static_pad ( " sink " ) . unwrap ( ) ,
) ? ;
let internal_video_srcpad = gst ::GhostPad ::with_target (
Some ( " video_src " ) ,
& state . cccombiner . static_pad ( " src " ) . unwrap ( ) ,
) ? ;
state . internal_bin . add_pad ( & internal_audio_sinkpad ) ? ;
state . internal_bin . add_pad ( & internal_audio_srcpad ) ? ;
state . internal_bin . add_pad ( & internal_video_sinkpad ) ? ;
state . internal_bin . add_pad ( & internal_video_srcpad ) ? ;
2022-10-09 13:06:59 +00:00
let imp_weak = self . downgrade ( ) ;
2022-03-07 16:06:12 +00:00
let comp_sinkpad = & state . cccombiner . static_pad ( " sink " ) . unwrap ( ) ;
// Drop caption meta from video buffer if user preference is transcription
comp_sinkpad . add_probe ( gst ::PadProbeType ::BUFFER , move | _ , probe_info | {
2022-10-09 13:06:59 +00:00
let imp = match imp_weak . upgrade ( ) {
2022-03-07 16:06:12 +00:00
None = > return gst ::PadProbeReturn ::Remove ,
2022-10-09 13:06:59 +00:00
Some ( imp ) = > imp ,
2022-03-07 16:06:12 +00:00
} ;
2022-10-09 13:06:59 +00:00
let settings = imp . settings . lock ( ) . unwrap ( ) ;
2022-03-07 16:06:12 +00:00
if settings . caption_source ! = CaptionSource ::Transcription {
return gst ::PadProbeReturn ::Pass ;
}
if let Some ( gst ::PadProbeData ::Buffer ( buffer ) ) = & mut probe_info . data {
let buffer = buffer . make_mut ( ) ;
while let Some ( meta ) = buffer . meta_mut ::< gst_video ::VideoCaptionMeta > ( ) {
meta . remove ( ) . unwrap ( ) ;
}
}
gst ::PadProbeReturn ::Ok
} ) ;
2022-10-23 20:03:22 +00:00
self . obj ( ) . add ( & state . internal_bin ) ? ;
2021-06-22 20:08:08 +00:00
2022-10-17 17:48:43 +00:00
state . cccombiner . set_property ( " latency " , 100. mseconds ( ) ) ;
2021-06-22 20:08:08 +00:00
self . audio_sinkpad
. set_target ( Some ( & state . internal_bin . static_pad ( " audio_sink " ) . unwrap ( ) ) ) ? ;
self . audio_srcpad
. set_target ( Some ( & state . internal_bin . static_pad ( " audio_src " ) . unwrap ( ) ) ) ? ;
self . video_sinkpad
. set_target ( Some ( & state . internal_bin . static_pad ( " video_sink " ) . unwrap ( ) ) ) ? ;
self . video_srcpad
. set_target ( Some ( & state . internal_bin . static_pad ( " video_src " ) . unwrap ( ) ) ) ? ;
2022-10-09 13:06:59 +00:00
self . construct_transcription_bin ( state ) ? ;
2021-06-22 20:08:08 +00:00
Ok ( ( ) )
}
2022-10-09 13:06:59 +00:00
fn setup_transcription ( & self , state : & State ) {
2021-06-22 20:08:08 +00:00
let settings = self . settings . lock ( ) . unwrap ( ) ;
let mut cc_caps = settings . cc_caps . clone ( ) ;
let cc_caps_mut = cc_caps . make_mut ( ) ;
let s = cc_caps_mut . structure_mut ( 0 ) . unwrap ( ) ;
2022-11-01 08:27:48 +00:00
s . set ( " framerate " , state . framerate . unwrap ( ) ) ;
2021-06-22 20:08:08 +00:00
2021-11-08 09:55:40 +00:00
state . cccapsfilter . set_property ( " caps " , & cc_caps ) ;
2021-06-22 20:08:08 +00:00
2023-03-24 23:14:46 +00:00
let max_size_time = settings . latency
+ settings . translate_latency
+ settings . accumulate_time
+ CEA608MUX_LATENCY ;
2021-06-22 20:08:08 +00:00
2022-11-01 08:27:48 +00:00
for queue in [ & state . audio_queue_passthrough , & state . video_queue ] {
2021-11-08 09:55:40 +00:00
queue . set_property ( " max-size-bytes " , 0 u32 ) ;
queue . set_property ( " max-size-buffers " , 0 u32 ) ;
queue . set_property ( " max-size-time " , max_size_time ) ;
2021-06-22 20:08:08 +00:00
}
let latency_ms = settings . latency . mseconds ( ) as u32 ;
2021-11-08 09:55:40 +00:00
state . transcriber . set_property ( " latency " , latency_ms ) ;
2021-06-22 20:08:08 +00:00
2023-03-24 23:14:46 +00:00
let translate_latency_ms = settings . translate_latency . mseconds ( ) as u32 ;
state
. transcriber
. set_property ( " translate-latency " , translate_latency_ms ) ;
2021-06-22 20:08:08 +00:00
if ! settings . passthrough {
state
. transcription_bin
. link_pads ( Some ( " src " ) , & state . cccombiner , Some ( " caption " ) )
. unwrap ( ) ;
state . transcription_bin . set_locked_state ( false ) ;
state . transcription_bin . sync_state_with_parent ( ) . unwrap ( ) ;
2023-03-22 22:39:32 +00:00
let audio_tee_pad = state . audio_tee . request_pad_simple ( " src_%u " ) . unwrap ( ) ;
let transcription_sink_pad = state . transcription_bin . static_pad ( " sink " ) . unwrap ( ) ;
audio_tee_pad . link ( & transcription_sink_pad ) . unwrap ( ) ;
2021-06-22 20:08:08 +00:00
}
drop ( settings ) ;
2022-10-09 13:06:59 +00:00
self . setup_cc_mode ( state ) ;
2021-06-22 20:08:08 +00:00
}
2022-10-09 13:06:59 +00:00
fn disable_transcription_bin ( & self ) {
2021-06-22 20:08:08 +00:00
let mut state = self . state . lock ( ) . unwrap ( ) ;
if let Some ( ref mut state ) = state . as_mut ( ) {
state . tearing_down = false ;
// At this point, we want to check whether passthrough
// has been unset in the meantime
let passthrough = self . settings . lock ( ) . unwrap ( ) . passthrough ;
if passthrough {
2022-10-09 13:06:59 +00:00
gst ::debug! ( CAT , imp : self , " disabling transcription bin " ) ;
2021-07-15 20:38:07 +00:00
2021-06-22 20:08:08 +00:00
let bin_sink_pad = state . transcription_bin . static_pad ( " sink " ) . unwrap ( ) ;
if let Some ( audio_tee_pad ) = bin_sink_pad . peer ( ) {
audio_tee_pad . unlink ( & bin_sink_pad ) . unwrap ( ) ;
state . audio_tee . release_request_pad ( & audio_tee_pad ) ;
}
let bin_src_pad = state . transcription_bin . static_pad ( " src " ) . unwrap ( ) ;
if let Some ( cccombiner_pad ) = bin_src_pad . peer ( ) {
bin_src_pad . unlink ( & cccombiner_pad ) . unwrap ( ) ;
state . cccombiner . release_request_pad ( & cccombiner_pad ) ;
}
state . transcription_bin . set_locked_state ( true ) ;
state . transcription_bin . set_state ( gst ::State ::Null ) . unwrap ( ) ;
}
}
}
2022-10-09 13:06:59 +00:00
fn block_and_update ( & self , passthrough : bool ) {
2021-06-22 20:08:08 +00:00
let mut s = self . state . lock ( ) . unwrap ( ) ;
if let Some ( ref mut state ) = s . as_mut ( ) {
if passthrough {
let sinkpad = state . transcription_bin . static_pad ( " sink " ) . unwrap ( ) ;
2022-10-09 13:06:59 +00:00
let imp_weak = self . downgrade ( ) ;
2021-06-22 20:08:08 +00:00
state . tearing_down = true ;
drop ( s ) ;
let _ = sinkpad . add_probe (
gst ::PadProbeType ::IDLE
| gst ::PadProbeType ::BUFFER
| gst ::PadProbeType ::EVENT_DOWNSTREAM ,
move | _pad , _info | {
2022-10-09 13:06:59 +00:00
let imp = match imp_weak . upgrade ( ) {
2021-06-22 20:08:08 +00:00
None = > return gst ::PadProbeReturn ::Pass ,
2022-10-09 13:06:59 +00:00
Some ( imp ) = > imp ,
2021-06-22 20:08:08 +00:00
} ;
2022-10-09 13:06:59 +00:00
imp . disable_transcription_bin ( ) ;
2021-06-22 20:08:08 +00:00
gst ::PadProbeReturn ::Remove
} ,
) ;
} else if state . tearing_down {
// Do nothing, wait for the previous transcription bin
// to finish tearing down
} else {
state
. transcription_bin
. link_pads ( Some ( " src " ) , & state . cccombiner , Some ( " caption " ) )
. unwrap ( ) ;
state . transcription_bin . set_locked_state ( false ) ;
state . transcription_bin . sync_state_with_parent ( ) . unwrap ( ) ;
2022-04-07 12:05:30 +00:00
let audio_tee_pad = state . audio_tee . request_pad_simple ( " src_%u " ) . unwrap ( ) ;
let transcription_sink_pad = state . transcription_bin . static_pad ( " sink " ) . unwrap ( ) ;
audio_tee_pad . link ( & transcription_sink_pad ) . unwrap ( ) ;
2021-06-22 20:08:08 +00:00
}
}
}
2022-10-09 13:06:59 +00:00
fn setup_cc_mode ( & self , state : & State ) {
2021-06-22 20:08:08 +00:00
let mode = self . settings . lock ( ) . unwrap ( ) . mode ;
2022-10-09 13:06:59 +00:00
gst ::debug! ( CAT , imp : self , " setting CC mode {:?} " , mode ) ;
2021-06-22 20:08:08 +00:00
2023-03-24 23:14:46 +00:00
for channel in state . transcription_channels . values ( ) {
channel . tttocea608 . set_property ( " mode " , mode ) ;
2021-06-22 20:08:08 +00:00
2023-03-24 23:14:46 +00:00
if mode . is_rollup ( ) {
channel . textwrap . set_property ( " accumulate-time " , 0 u64 ) ;
} else {
let accumulate_time = self . settings . lock ( ) . unwrap ( ) . accumulate_time ;
2021-06-22 20:08:08 +00:00
2023-03-24 23:14:46 +00:00
channel
. textwrap
. set_property ( " accumulate-time " , accumulate_time ) ;
}
2021-06-22 20:08:08 +00:00
}
}
/* We make no ceremonies here because the function can only
* be called in READY * /
fn relink_transcriber (
& self ,
state : & mut State ,
old_transcriber : & gst ::Element ,
) -> Result < ( ) , Error > {
2023-03-24 23:14:46 +00:00
gst ::debug! (
2021-06-22 20:08:08 +00:00
CAT ,
2022-10-09 13:06:59 +00:00
imp : self ,
2021-06-22 20:08:08 +00:00
" Relinking transcriber, old: {:?}, new: {:?} " ,
old_transcriber ,
state . transcriber
) ;
state . transcriber_aconv . unlink ( old_transcriber ) ;
2023-03-24 23:14:46 +00:00
for channel in state . transcription_channels . values ( ) {
old_transcriber . unlink ( & channel . queue ) ;
}
2021-06-22 20:08:08 +00:00
state . transcription_bin . remove ( old_transcriber ) . unwrap ( ) ;
old_transcriber . set_state ( gst ::State ::Null ) . unwrap ( ) ;
state . transcription_bin . add ( & state . transcriber ) ? ;
state . transcriber . sync_state_with_parent ( ) . unwrap ( ) ;
2023-03-24 23:14:46 +00:00
state . transcriber_aconv . link ( & state . transcriber ) ? ;
for channel in state . transcription_channels . values ( ) {
channel . link_transcriber ( & state . transcriber ) ? ;
}
2021-06-22 20:08:08 +00:00
Ok ( ( ) )
}
2021-09-08 12:33:31 +00:00
#[ allow(clippy::single_match) ]
2022-10-09 13:06:59 +00:00
fn src_query ( & self , pad : & gst ::Pad , query : & mut gst ::QueryRef ) -> bool {
2022-01-19 13:07:45 +00:00
use gst ::QueryViewMut ;
2021-06-22 20:08:08 +00:00
2022-02-21 17:43:46 +00:00
gst ::log! ( CAT , obj : pad , " Handling query {:?} " , query ) ;
2021-06-22 20:08:08 +00:00
match query . view_mut ( ) {
2022-01-19 13:07:45 +00:00
QueryViewMut ::Latency ( q ) = > {
2021-06-22 20:08:08 +00:00
let mut upstream_query = gst ::query ::Latency ::new ( ) ;
2022-10-23 20:03:22 +00:00
let ret = gst ::Pad ::query_default ( pad , Some ( & * self . obj ( ) ) , & mut upstream_query ) ;
2021-06-22 20:08:08 +00:00
if ret {
let ( _ , mut min , _ ) = upstream_query . result ( ) ;
2023-03-24 23:14:46 +00:00
let ( received_framerate , translating ) = {
2021-06-22 20:08:08 +00:00
let state = self . state . lock ( ) . unwrap ( ) ;
if let Some ( state ) = state . as_ref ( ) {
2023-03-24 23:14:46 +00:00
(
state . framerate ,
state
. transcription_channels
. values ( )
. any ( | c | c . language ! = " transcript " ) ,
)
2021-06-22 20:08:08 +00:00
} else {
2023-03-24 23:14:46 +00:00
( None , false )
2021-06-22 20:08:08 +00:00
}
} ;
2022-03-08 13:38:55 +00:00
let settings = self . settings . lock ( ) . unwrap ( ) ;
2023-03-24 23:14:46 +00:00
if settings . passthrough | | received_framerate . is_none ( ) {
min + = settings . latency + settings . accumulate_time + CEA608MUX_LATENCY ;
if translating {
min + = settings . translate_latency ;
}
/* The sub latency introduced by cea608mux */
if let Some ( framerate ) = received_framerate {
min + = gst ::ClockTime ::SECOND
. mul_div_floor ( framerate . denom ( ) as u64 , framerate . numer ( ) as u64 )
. unwrap ( ) ;
}
2021-06-22 20:08:08 +00:00
} else if settings . mode . is_rollup ( ) {
min + = settings . accumulate_time ;
}
q . set ( true , min , gst ::ClockTime ::NONE ) ;
}
ret
}
2022-10-23 20:03:22 +00:00
_ = > gst ::Pad ::query_default ( pad , Some ( & * self . obj ( ) ) , query ) ,
2021-06-22 20:08:08 +00:00
}
}
fn build_state ( & self ) -> Result < State , Error > {
2022-10-22 16:06:29 +00:00
let internal_bin = gst ::Bin ::builder ( ) . name ( " internal " ) . build ( ) ;
let transcription_bin = gst ::Bin ::builder ( ) . name ( " transcription-bin " ) . build ( ) ;
2022-10-19 16:18:43 +00:00
let audio_tee = gst ::ElementFactory ::make ( " tee " )
// Protect passthrough enable (and resulting dynamic reconfigure)
// from non-streaming thread
. property ( " allow-not-linked " , true )
. build ( ) ? ;
let cccombiner = gst ::ElementFactory ::make ( " cccombiner " )
. name ( " cccombiner " )
. build ( ) ? ;
let transcriber_aconv = gst ::ElementFactory ::make ( " audioconvert " ) . build ( ) ? ;
let transcriber = gst ::ElementFactory ::make ( " awstranscriber " )
. name ( " transcriber " )
. build ( ) ? ;
let audio_queue_passthrough = gst ::ElementFactory ::make ( " queue " ) . build ( ) ? ;
let video_queue = gst ::ElementFactory ::make ( " queue " ) . build ( ) ? ;
let cccapsfilter = gst ::ElementFactory ::make ( " capsfilter " ) . build ( ) ? ;
let transcription_valve = gst ::ElementFactory ::make ( " valve " )
. property_from_str ( " drop-mode " , " transform-to-gap " )
. build ( ) ? ;
2021-06-22 20:08:08 +00:00
2023-03-24 23:14:46 +00:00
let mut transcription_channels = HashMap ::new ( ) ;
if let Some ( ref map ) = self . settings . lock ( ) . unwrap ( ) . translation_languages {
for ( key , value ) in map . iter ( ) {
let channel = key . to_lowercase ( ) ;
if ! [ " cc1 " , " cc3 " ] . contains ( & channel . as_str ( ) ) {
anyhow ::bail! ( " Unknown 608 channel {}, valid values are cc1, cc3 " , channel ) ;
}
let language_code = value . get ::< String > ( ) ? ;
transcription_channels . insert (
channel . to_owned ( ) ,
TranscriptionChannel {
queue : gst ::ElementFactory ::make ( " queue " ) . build ( ) ? ,
textwrap : gst ::ElementFactory ::make ( " textwrap " )
. name ( format! ( " textwrap_ {channel} " ) )
. build ( ) ? ,
tttocea608 : gst ::ElementFactory ::make ( " tttocea608 " )
. name ( format! ( " tttocea608_ {channel} " ) )
. build ( ) ? ,
language : language_code ,
} ,
) ;
}
} else {
transcription_channels . insert (
" cc1 " . to_string ( ) ,
TranscriptionChannel {
queue : gst ::ElementFactory ::make ( " queue " ) . build ( ) ? ,
textwrap : gst ::ElementFactory ::make ( " textwrap " )
. name ( " textwrap " . to_string ( ) )
. build ( ) ? ,
tttocea608 : gst ::ElementFactory ::make ( " tttocea608 " )
. name ( " tttocea608 " . to_string ( ) )
. build ( ) ? ,
language : " transcript " . to_string ( ) ,
} ,
) ;
}
2021-06-22 20:08:08 +00:00
Ok ( State {
framerate : None ,
internal_bin ,
audio_queue_passthrough ,
video_queue ,
transcriber_aconv ,
transcriber ,
audio_tee ,
cccombiner ,
transcription_bin ,
2023-03-24 23:14:46 +00:00
transcription_channels ,
2021-06-22 20:08:08 +00:00
cccapsfilter ,
2022-03-07 16:06:12 +00:00
transcription_valve ,
2021-06-22 20:08:08 +00:00
tearing_down : false ,
} )
}
2021-09-08 12:33:31 +00:00
#[ allow(clippy::single_match) ]
2022-10-09 13:06:59 +00:00
fn video_sink_event ( & self , pad : & gst ::Pad , event : gst ::Event ) -> bool {
2021-06-22 20:08:08 +00:00
use gst ::EventView ;
2022-02-21 17:43:46 +00:00
gst ::log! ( CAT , obj : pad , " Handling event {:?} " , event ) ;
2021-06-22 20:08:08 +00:00
match event . view ( ) {
EventView ::Caps ( e ) = > {
let mut state = self . state . lock ( ) . unwrap ( ) ;
if let Some ( ref mut state ) = state . as_mut ( ) {
let caps = e . caps ( ) ;
let s = caps . structure ( 0 ) . unwrap ( ) ;
let had_framerate = state . framerate . is_some ( ) ;
if let Ok ( framerate ) = s . get ::< gst ::Fraction > ( " framerate " ) {
state . framerate = Some ( framerate ) ;
} else {
state . framerate = Some ( gst ::Fraction ::new ( 30 , 1 ) ) ;
}
if ! had_framerate {
2022-02-21 17:43:46 +00:00
gst ::info! (
2021-06-22 20:08:08 +00:00
CAT ,
2022-10-09 13:06:59 +00:00
imp : self ,
2021-06-22 20:08:08 +00:00
" Received video caps, setting up transcription "
) ;
2022-10-09 13:06:59 +00:00
self . setup_transcription ( state ) ;
2021-06-22 20:08:08 +00:00
}
}
2022-10-23 20:03:22 +00:00
gst ::Pad ::event_default ( pad , Some ( & * self . obj ( ) ) , event )
2021-06-22 20:08:08 +00:00
}
2022-10-23 20:03:22 +00:00
_ = > gst ::Pad ::event_default ( pad , Some ( & * self . obj ( ) ) , event ) ,
2021-06-22 20:08:08 +00:00
}
}
}
#[ glib::object_subclass ]
impl ObjectSubclass for TranscriberBin {
2022-10-23 15:42:58 +00:00
const NAME : & 'static str = " GstTranscriberBin " ;
2021-06-22 20:08:08 +00:00
type Type = super ::TranscriberBin ;
type ParentType = gst ::Bin ;
fn with_class ( klass : & Self ::Class ) -> Self {
let templ = klass . pad_template ( " sink_audio " ) . unwrap ( ) ;
let audio_sinkpad = gst ::GhostPad ::from_template ( & templ , Some ( " sink_audio " ) ) ;
let templ = klass . pad_template ( " src_audio " ) . unwrap ( ) ;
let audio_srcpad = gst ::GhostPad ::builder_with_template ( & templ , Some ( " src_audio " ) )
. query_function ( | pad , parent , query | {
TranscriberBin ::catch_panic_pad_function (
parent ,
| | false ,
2022-10-09 13:06:59 +00:00
| transcriber | transcriber . src_query ( pad . upcast_ref ( ) , query ) ,
2021-06-22 20:08:08 +00:00
)
} )
. build ( ) ;
let templ = klass . pad_template ( " sink_video " ) . unwrap ( ) ;
let video_sinkpad = gst ::GhostPad ::builder_with_template ( & templ , Some ( " sink_video " ) )
. event_function ( | pad , parent , event | {
TranscriberBin ::catch_panic_pad_function (
parent ,
| | false ,
2022-10-09 13:06:59 +00:00
| transcriber | transcriber . video_sink_event ( pad . upcast_ref ( ) , event ) ,
2021-06-22 20:08:08 +00:00
)
} )
. build ( ) ;
let templ = klass . pad_template ( " src_video " ) . unwrap ( ) ;
let video_srcpad = gst ::GhostPad ::builder_with_template ( & templ , Some ( " src_video " ) )
. query_function ( | pad , parent , query | {
TranscriberBin ::catch_panic_pad_function (
parent ,
| | false ,
2022-10-09 13:06:59 +00:00
| transcriber | transcriber . src_query ( pad . upcast_ref ( ) , query ) ,
2021-06-22 20:08:08 +00:00
)
} )
. build ( ) ;
Self {
audio_srcpad ,
video_srcpad ,
audio_sinkpad ,
video_sinkpad ,
state : Mutex ::new ( None ) ,
settings : Mutex ::new ( Settings ::default ( ) ) ,
}
}
}
impl ObjectImpl for TranscriberBin {
fn properties ( ) -> & 'static [ glib ::ParamSpec ] {
static PROPERTIES : Lazy < Vec < glib ::ParamSpec > > = Lazy ::new ( | | {
vec! [
2022-08-18 12:04:15 +00:00
glib ::ParamSpecBoolean ::builder ( " passthrough " )
. nick ( " Passthrough " )
. blurb ( " Whether transcription should occur " )
. default_value ( DEFAULT_PASSTHROUGH )
. mutable_playing ( )
. build ( ) ,
glib ::ParamSpecUInt ::builder ( " latency " )
. nick ( " Latency " )
. blurb ( " Amount of milliseconds to allow the transcriber " )
. default_value ( DEFAULT_LATENCY . mseconds ( ) as u32 )
. mutable_ready ( )
. build ( ) ,
glib ::ParamSpecUInt ::builder ( " accumulate-time " )
. nick ( " accumulate-time " )
. blurb ( " Cut-off time for textwrap accumulation, in milliseconds (0=do not accumulate). \
Set this to a non - default value if you plan to switch to pop - on mode " )
. default_value ( DEFAULT_ACCUMULATE . mseconds ( ) as u32 )
. mutable_ready ( )
. build ( ) ,
2023-01-21 16:13:48 +00:00
glib ::ParamSpecEnum ::builder_with_default ( " mode " , DEFAULT_MODE )
2022-08-18 12:04:15 +00:00
. nick ( " Mode " )
. blurb ( " Which closed caption mode to operate in " )
. mutable_playing ( )
. build ( ) ,
2022-09-05 08:45:47 +00:00
glib ::ParamSpecBoxed ::builder ::< gst ::Caps > ( " cc-caps " )
2022-08-18 12:04:15 +00:00
. nick ( " Closed Caption caps " )
. blurb ( " The expected format of the closed captions " )
. mutable_ready ( )
. build ( ) ,
2022-09-05 08:45:47 +00:00
glib ::ParamSpecObject ::builder ::< gst ::Element > ( " transcriber " )
2022-08-18 12:04:15 +00:00
. nick ( " Transcriber " )
. blurb ( " The transcriber element to use " )
. mutable_ready ( )
. build ( ) ,
2023-01-21 16:13:48 +00:00
glib ::ParamSpecEnum ::builder_with_default ( " caption-source " , DEFAULT_CAPTION_SOURCE )
2022-08-18 12:04:15 +00:00
. nick ( " Caption source " )
. blurb ( " Caption source to use. \
2022-03-07 16:06:12 +00:00
If \ " Transcription \" or \" Inband \" is selected, the caption meta \
2022-08-18 12:04:15 +00:00
of the other source will be dropped by transcriberbin " )
. mutable_playing ( )
. build ( ) ,
2023-03-24 23:14:46 +00:00
glib ::ParamSpecBoxed ::builder ::< gst ::Structure > ( " translation-languages " )
. nick ( " Translation languages " )
. blurb ( " A map of CEA 608 channels to language codes, eg translation-languages= \" languages, CC1=fr, CC3=transcript \" will map the French translation to CC1 and the original transcript to CC3 " )
. construct_only ( )
. build ( ) ,
glib ::ParamSpecUInt ::builder ( " translate-latency " )
. nick ( " Translation Latency " )
. blurb ( " Amount of extra milliseconds to allow for translating " )
. default_value ( DEFAULT_TRANSLATE_LATENCY . mseconds ( ) as u32 )
. mutable_ready ( )
. build ( ) ,
2021-06-22 20:08:08 +00:00
]
} ) ;
PROPERTIES . as_ref ( )
}
2022-10-09 13:06:59 +00:00
fn set_property ( & self , _id : usize , value : & glib ::Value , pspec : & glib ::ParamSpec ) {
2021-06-22 20:08:08 +00:00
match pspec . name ( ) {
" passthrough " = > {
let mut settings = self . settings . lock ( ) . unwrap ( ) ;
let old_passthrough = settings . passthrough ;
let new_passthrough = value . get ( ) . expect ( " type checked upstream " ) ;
settings . passthrough = new_passthrough ;
if old_passthrough ! = new_passthrough {
drop ( settings ) ;
2022-10-09 13:06:59 +00:00
self . block_and_update ( new_passthrough ) ;
2021-06-22 20:08:08 +00:00
}
}
" latency " = > {
let mut settings = self . settings . lock ( ) . unwrap ( ) ;
settings . latency = gst ::ClockTime ::from_mseconds (
value . get ::< u32 > ( ) . expect ( " type checked upstream " ) . into ( ) ,
) ;
}
" accumulate-time " = > {
let mut settings = self . settings . lock ( ) . unwrap ( ) ;
settings . accumulate_time = gst ::ClockTime ::from_mseconds (
value . get ::< u32 > ( ) . expect ( " type checked upstream " ) . into ( ) ,
) ;
}
" mode " = > {
let mut settings = self . settings . lock ( ) . unwrap ( ) ;
let old_mode = settings . mode ;
let new_mode = value . get ( ) . expect ( " type checked upstream " ) ;
settings . mode = new_mode ;
if old_mode ! = new_mode {
drop ( settings ) ;
2022-10-09 13:06:59 +00:00
self . setup_cc_mode ( self . state . lock ( ) . unwrap ( ) . as_ref ( ) . unwrap ( ) ) ;
2021-06-22 20:08:08 +00:00
}
}
" cc-caps " = > {
let mut settings = self . settings . lock ( ) . unwrap ( ) ;
settings . cc_caps = value . get ( ) . expect ( " type checked upstream " ) ;
}
" transcriber " = > {
let mut s = self . state . lock ( ) . unwrap ( ) ;
if let Some ( ref mut state ) = s . as_mut ( ) {
let old_transcriber = state . transcriber . clone ( ) ;
state . transcriber = value . get ( ) . expect ( " type checked upstream " ) ;
if old_transcriber ! = state . transcriber {
2022-10-09 13:06:59 +00:00
match self . relink_transcriber ( state , & old_transcriber ) {
2021-06-22 20:08:08 +00:00
Ok ( ( ) ) = > ( ) ,
Err ( err ) = > {
2022-02-21 17:43:46 +00:00
gst ::error! ( CAT , " invalid transcriber: {} " , err ) ;
2021-06-22 20:08:08 +00:00
drop ( s ) ;
* self . state . lock ( ) . unwrap ( ) = None ;
}
}
}
}
}
2022-03-07 16:06:12 +00:00
" caption-source " = > {
let mut settings = self . settings . lock ( ) . unwrap ( ) ;
settings . caption_source = value . get ( ) . expect ( " type checked upstream " ) ;
let s = self . state . lock ( ) . unwrap ( ) ;
if let Some ( state ) = s . as_ref ( ) {
if settings . caption_source = = CaptionSource ::Inband {
2022-10-09 13:06:59 +00:00
gst ::debug! ( CAT , imp : self , " Use inband caption, dropping transcription " ) ;
2022-03-07 16:06:12 +00:00
state . transcription_valve . set_property ( " drop " , true ) ;
} else {
2022-10-09 13:06:59 +00:00
gst ::debug! ( CAT , imp : self , " Stop dropping transcription " ) ;
2022-03-07 16:06:12 +00:00
state . transcription_valve . set_property ( " drop " , false ) ;
}
}
}
2023-03-24 23:14:46 +00:00
" translation-languages " = > {
let mut settings = self . settings . lock ( ) . unwrap ( ) ;
settings . translation_languages = value
. get ::< Option < gst ::Structure > > ( )
. expect ( " type checked upstream " )
}
" translate-latency " = > {
let mut settings = self . settings . lock ( ) . unwrap ( ) ;
settings . translate_latency = gst ::ClockTime ::from_mseconds (
value . get ::< u32 > ( ) . expect ( " type checked upstream " ) . into ( ) ,
) ;
}
2021-06-22 20:08:08 +00:00
_ = > unimplemented! ( ) ,
}
}
2022-10-09 13:06:59 +00:00
fn property ( & self , _id : usize , pspec : & glib ::ParamSpec ) -> glib ::Value {
2021-06-22 20:08:08 +00:00
match pspec . name ( ) {
" passthrough " = > {
let settings = self . settings . lock ( ) . unwrap ( ) ;
settings . passthrough . to_value ( )
}
" latency " = > {
let settings = self . settings . lock ( ) . unwrap ( ) ;
( settings . latency . mseconds ( ) as u32 ) . to_value ( )
}
" accumulate-time " = > {
let settings = self . settings . lock ( ) . unwrap ( ) ;
( settings . accumulate_time . mseconds ( ) as u32 ) . to_value ( )
}
" mode " = > {
let settings = self . settings . lock ( ) . unwrap ( ) ;
settings . mode . to_value ( )
}
" cc-caps " = > {
let settings = self . settings . lock ( ) . unwrap ( ) ;
settings . cc_caps . to_value ( )
}
" transcriber " = > {
let state = self . state . lock ( ) . unwrap ( ) ;
2021-07-30 10:53:35 +00:00
if let Some ( state ) = state . as_ref ( ) {
2021-06-22 20:08:08 +00:00
state . transcriber . to_value ( )
} else {
let ret : Option < gst ::Element > = None ;
ret . to_value ( )
}
}
2022-03-07 16:06:12 +00:00
" caption-source " = > {
let settings = self . settings . lock ( ) . unwrap ( ) ;
settings . caption_source . to_value ( )
}
2023-03-24 23:14:46 +00:00
" translation-languages " = > {
let settings = self . settings . lock ( ) . unwrap ( ) ;
settings . translation_languages . to_value ( )
}
" translate-latency " = > {
let settings = self . settings . lock ( ) . unwrap ( ) ;
( settings . translate_latency . mseconds ( ) as u32 ) . to_value ( )
}
2021-06-22 20:08:08 +00:00
_ = > unimplemented! ( ) ,
}
}
2022-10-09 13:06:59 +00:00
fn constructed ( & self ) {
self . parent_constructed ( ) ;
2021-06-22 20:08:08 +00:00
2022-10-23 20:03:22 +00:00
let obj = self . obj ( ) ;
2021-06-22 20:08:08 +00:00
obj . add_pad ( & self . audio_srcpad ) . unwrap ( ) ;
obj . add_pad ( & self . audio_sinkpad ) . unwrap ( ) ;
obj . add_pad ( & self . video_srcpad ) . unwrap ( ) ;
obj . add_pad ( & self . video_sinkpad ) . unwrap ( ) ;
* self . state . lock ( ) . unwrap ( ) = match self . build_state ( ) {
2022-10-09 13:06:59 +00:00
Ok ( mut state ) = > match self . construct_internal_bin ( & mut state ) {
2021-06-22 20:08:08 +00:00
Ok ( ( ) ) = > Some ( state ) ,
Err ( err ) = > {
2022-02-21 17:43:46 +00:00
gst ::error! ( CAT , " Failed to build internal bin: {} " , err ) ;
2021-06-22 20:08:08 +00:00
None
}
} ,
Err ( err ) = > {
2022-02-21 17:43:46 +00:00
gst ::error! ( CAT , " Failed to build state: {} " , err ) ;
2021-06-22 20:08:08 +00:00
None
}
}
}
}
2021-10-23 08:57:31 +00:00
impl GstObjectImpl for TranscriberBin { }
2021-06-22 20:08:08 +00:00
impl ElementImpl for TranscriberBin {
fn metadata ( ) -> Option < & 'static gst ::subclass ::ElementMetadata > {
static ELEMENT_METADATA : Lazy < gst ::subclass ::ElementMetadata > = Lazy ::new ( | | {
gst ::subclass ::ElementMetadata ::new (
" TranscriberBin " ,
" Audio / Video / Text " ,
" Transcribes audio and adds it as closed captions " ,
" Mathieu Duponchelle <mathieu@centricular.com> " ,
)
} ) ;
Some ( & * ELEMENT_METADATA )
}
fn pad_templates ( ) -> & 'static [ gst ::PadTemplate ] {
static PAD_TEMPLATES : Lazy < Vec < gst ::PadTemplate > > = Lazy ::new ( | | {
2021-11-06 07:34:10 +00:00
let caps = gst ::Caps ::builder ( " video/x-raw " ) . build ( ) ;
2021-06-22 20:08:08 +00:00
let video_src_pad_template = gst ::PadTemplate ::new (
" src_video " ,
gst ::PadDirection ::Src ,
gst ::PadPresence ::Always ,
& caps ,
)
. unwrap ( ) ;
let video_sink_pad_template = gst ::PadTemplate ::new (
" sink_video " ,
gst ::PadDirection ::Sink ,
gst ::PadPresence ::Always ,
& caps ,
)
. unwrap ( ) ;
2021-11-06 07:34:10 +00:00
let caps = gst ::Caps ::builder ( " audio/x-raw " ) . build ( ) ;
2021-06-22 20:08:08 +00:00
let audio_src_pad_template = gst ::PadTemplate ::new (
" src_audio " ,
gst ::PadDirection ::Src ,
gst ::PadPresence ::Always ,
& caps ,
)
. unwrap ( ) ;
let audio_sink_pad_template = gst ::PadTemplate ::new (
" sink_audio " ,
gst ::PadDirection ::Sink ,
gst ::PadPresence ::Always ,
& caps ,
)
. unwrap ( ) ;
vec! [
video_src_pad_template ,
video_sink_pad_template ,
audio_src_pad_template ,
audio_sink_pad_template ,
]
} ) ;
PAD_TEMPLATES . as_ref ( )
}
2021-09-08 12:33:31 +00:00
#[ allow(clippy::single_match) ]
2021-06-22 20:08:08 +00:00
fn change_state (
& self ,
transition : gst ::StateChange ,
) -> Result < gst ::StateChangeSuccess , gst ::StateChangeError > {
2022-10-09 13:06:59 +00:00
gst ::trace! ( CAT , imp : self , " Changing state {:?} " , transition ) ;
2021-06-22 20:08:08 +00:00
match transition {
gst ::StateChange ::ReadyToPaused = > {
let mut state = self . state . lock ( ) . unwrap ( ) ;
if let Some ( ref mut state ) = state . as_mut ( ) {
if state . framerate . is_some ( ) {
2022-02-21 17:43:46 +00:00
gst ::info! (
2021-06-22 20:08:08 +00:00
CAT ,
2022-10-09 13:06:59 +00:00
imp : self ,
2021-06-22 20:08:08 +00:00
" Received video caps, setting up transcription "
) ;
2022-10-09 13:06:59 +00:00
self . setup_transcription ( state ) ;
2021-06-22 20:08:08 +00:00
}
} else {
2023-03-24 22:35:10 +00:00
drop ( state ) ;
2022-10-09 13:06:59 +00:00
gst ::element_imp_error! (
self ,
2021-06-22 20:08:08 +00:00
gst ::StreamError ::Failed ,
[ " Can't change state with no state " ]
) ;
return Err ( gst ::StateChangeError ) ;
}
}
_ = > ( ) ,
}
2022-10-09 13:06:59 +00:00
self . parent_change_state ( transition )
2021-06-22 20:08:08 +00:00
}
}
2021-09-27 23:50:49 +00:00
impl BinImpl for TranscriberBin {
2022-10-09 13:06:59 +00:00
fn handle_message ( & self , msg : gst ::Message ) {
2021-09-27 23:50:49 +00:00
use gst ::MessageView ;
match msg . view ( ) {
2022-01-19 13:07:45 +00:00
MessageView ::Error ( m ) = > {
2021-09-27 23:50:49 +00:00
/* We must have a state here */
let s = self . state . lock ( ) . unwrap ( ) ;
if let Some ( state ) = s . as_ref ( ) {
2023-01-05 10:32:01 +00:00
if msg . src ( ) = = Some ( state . transcriber . upcast_ref ( ) ) {
2022-02-21 17:43:46 +00:00
gst ::error! (
2021-09-27 23:50:49 +00:00
CAT ,
2022-10-09 13:06:59 +00:00
imp : self ,
2021-09-27 23:50:49 +00:00
" Transcriber has posted an error ({:?}), going back to passthrough " ,
m
) ;
drop ( s ) ;
let mut settings = self . settings . lock ( ) . unwrap ( ) ;
settings . passthrough = true ;
drop ( settings ) ;
2022-10-23 20:03:22 +00:00
self . obj ( ) . notify ( " passthrough " ) ;
self . obj ( ) . call_async ( move | bin | {
2022-01-17 17:36:41 +00:00
let thiz = bin . imp ( ) ;
2022-10-09 13:06:59 +00:00
thiz . block_and_update ( true ) ;
2021-09-27 23:50:49 +00:00
} ) ;
} else {
drop ( s ) ;
2022-10-09 13:06:59 +00:00
self . parent_handle_message ( msg ) ;
2021-09-27 23:50:49 +00:00
}
} else {
drop ( s ) ;
2022-10-09 13:06:59 +00:00
self . parent_handle_message ( msg ) ;
2021-09-27 23:50:49 +00:00
}
}
2022-10-09 13:06:59 +00:00
_ = > self . parent_handle_message ( msg ) ,
2021-09-27 23:50:49 +00:00
}
}
}