mirror of
https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs.git
synced 2024-11-22 19:41:00 +00:00
cea608overlay: expose timeout property
C.9 Automatic Caption Erasure (Preferred) [...] Some manufacturers have suggested building automatic timeout into their decoders. They propose that if no data are received for the selected caption channel within a given time, the decoder should automatically erase the caption. Such erasure may supersede the intentions of the caption service providers and institute one maximum display time for all captioning services. If such a timeout is deemed necessary, however, the time limit should be no less than 16 seconds, an amount of time said by caption service providers to be longer than their most enduring caption. It is preferred, when automatic caption erasure is used in a decoder, that only displayed memory be erased, since some caption service providers may, contrary to recommended practice (see Section B.8.3), send pop-on style caption data to non-displayed memory more than 16 seconds before sending the EOC command which causes the caption to display.
This commit is contained in:
parent
44b4a4eb7e
commit
78d7cbd7dd
1 changed files with 61 additions and 3 deletions
|
@ -45,6 +45,7 @@ const DEFAULT_BLACK_BACKGROUND: bool = false;
|
||||||
struct Settings {
|
struct Settings {
|
||||||
field: i32,
|
field: i32,
|
||||||
black_background: bool,
|
black_background: bool,
|
||||||
|
timeout: Option<gst::ClockTime>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Settings {
|
impl Default for Settings {
|
||||||
|
@ -52,6 +53,7 @@ impl Default for Settings {
|
||||||
Settings {
|
Settings {
|
||||||
field: DEFAULT_FIELD,
|
field: DEFAULT_FIELD,
|
||||||
black_background: DEFAULT_BLACK_BACKGROUND,
|
black_background: DEFAULT_BLACK_BACKGROUND,
|
||||||
|
timeout: gst::ClockTime::NONE,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,6 +66,7 @@ struct State {
|
||||||
left_alignment: i32,
|
left_alignment: i32,
|
||||||
attach: bool,
|
attach: bool,
|
||||||
selected_field: Option<u8>,
|
selected_field: Option<u8>,
|
||||||
|
last_cc_pts: Option<gst::ClockTime>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for State {
|
impl Default for State {
|
||||||
|
@ -76,6 +79,7 @@ impl Default for State {
|
||||||
left_alignment: 0,
|
left_alignment: 0,
|
||||||
attach: false,
|
attach: false,
|
||||||
selected_field: None,
|
selected_field: None,
|
||||||
|
last_cc_pts: gst::ClockTime::NONE,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -331,6 +335,7 @@ impl Cea608Overlay {
|
||||||
element: &super::Cea608Overlay,
|
element: &super::Cea608Overlay,
|
||||||
state: &mut State,
|
state: &mut State,
|
||||||
data: &[u8],
|
data: &[u8],
|
||||||
|
pts: gst::ClockTime,
|
||||||
) {
|
) {
|
||||||
if data.len() % 3 != 0 {
|
if data.len() % 3 != 0 {
|
||||||
gst_warning!(CAT, "cc_data length is not a multiple of 3, truncating");
|
gst_warning!(CAT, "cc_data length is not a multiple of 3, truncating");
|
||||||
|
@ -371,6 +376,8 @@ impl Cea608Overlay {
|
||||||
|
|
||||||
self.overlay_text(element, &text, state);
|
self.overlay_text(element, &text, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.reset_timeout(state, pts);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
|
@ -385,6 +392,7 @@ impl Cea608Overlay {
|
||||||
element: &super::Cea608Overlay,
|
element: &super::Cea608Overlay,
|
||||||
state: &mut State,
|
state: &mut State,
|
||||||
data: &[u8],
|
data: &[u8],
|
||||||
|
pts: gst::ClockTime,
|
||||||
) {
|
) {
|
||||||
if data.len() % 3 != 0 {
|
if data.len() % 3 != 0 {
|
||||||
gst_warning!(CAT, "cc_data length is not a multiple of 3, truncating");
|
gst_warning!(CAT, "cc_data length is not a multiple of 3, truncating");
|
||||||
|
@ -417,10 +425,16 @@ impl Cea608Overlay {
|
||||||
|
|
||||||
self.overlay_text(element, &text, state);
|
self.overlay_text(element, &text, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.reset_timeout(state, pts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn reset_timeout(&self, state: &mut State, pts: gst::ClockTime) {
|
||||||
|
state.last_cc_pts = Some(pts);
|
||||||
|
}
|
||||||
|
|
||||||
fn sink_chain(
|
fn sink_chain(
|
||||||
&self,
|
&self,
|
||||||
pad: &gst::Pad,
|
pad: &gst::Pad,
|
||||||
|
@ -429,6 +443,11 @@ impl Cea608Overlay {
|
||||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||||
gst_log!(CAT, obj: pad, "Handling buffer {:?}", buffer);
|
gst_log!(CAT, obj: pad, "Handling buffer {:?}", buffer);
|
||||||
|
|
||||||
|
let pts = buffer.pts().ok_or_else(|| {
|
||||||
|
gst_error!(CAT, obj: pad, "Require timestamped buffers");
|
||||||
|
gst::FlowError::Error
|
||||||
|
})?;
|
||||||
|
|
||||||
let mut state = self.state.lock().unwrap();
|
let mut state = self.state.lock().unwrap();
|
||||||
|
|
||||||
if self.srcpad.check_reconfigure() {
|
if self.srcpad.check_reconfigure() {
|
||||||
|
@ -443,7 +462,7 @@ impl Cea608Overlay {
|
||||||
if meta.caption_type() == gst_video::VideoCaptionType::Cea708Cdp {
|
if meta.caption_type() == gst_video::VideoCaptionType::Cea708Cdp {
|
||||||
match extract_cdp(meta.data()) {
|
match extract_cdp(meta.data()) {
|
||||||
Ok(data) => {
|
Ok(data) => {
|
||||||
self.decode_cc_data(pad, element, &mut state, data);
|
self.decode_cc_data(pad, element, &mut state, data, pts);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
gst_warning!(CAT, "{}", &e.to_string());
|
gst_warning!(CAT, "{}", &e.to_string());
|
||||||
|
@ -451,9 +470,9 @@ impl Cea608Overlay {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if meta.caption_type() == gst_video::VideoCaptionType::Cea708Raw {
|
} else if meta.caption_type() == gst_video::VideoCaptionType::Cea708Raw {
|
||||||
self.decode_cc_data(pad, element, &mut state, meta.data());
|
self.decode_cc_data(pad, element, &mut state, meta.data(), pts);
|
||||||
} else if meta.caption_type() == gst_video::VideoCaptionType::Cea608S3341a {
|
} else if meta.caption_type() == gst_video::VideoCaptionType::Cea608S3341a {
|
||||||
self.decode_s334_1a(pad, element, &mut state, meta.data());
|
self.decode_s334_1a(pad, element, &mut state, meta.data(), pts);
|
||||||
} else if meta.caption_type() == gst_video::VideoCaptionType::Cea608Raw {
|
} else if meta.caption_type() == gst_video::VideoCaptionType::Cea608Raw {
|
||||||
let data = meta.data();
|
let data = meta.data();
|
||||||
assert!(data.len() % 2 == 0);
|
assert!(data.len() % 2 == 0);
|
||||||
|
@ -476,6 +495,18 @@ impl Cea608Overlay {
|
||||||
|
|
||||||
self.overlay_text(element, &text, &mut state);
|
self.overlay_text(element, &text, &mut state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.reset_timeout(&mut state, pts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(timeout) = self.settings.lock().unwrap().timeout {
|
||||||
|
if let Some(interval) = pts.opt_saturating_sub(state.last_cc_pts) {
|
||||||
|
if interval > timeout {
|
||||||
|
gst_info!(CAT, obj: element, "Reached timeout, clearing overlay");
|
||||||
|
state.composition.take();
|
||||||
|
state.last_cc_pts.take();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -596,6 +627,15 @@ impl ObjectImpl for Cea608Overlay {
|
||||||
DEFAULT_BLACK_BACKGROUND,
|
DEFAULT_BLACK_BACKGROUND,
|
||||||
glib::ParamFlags::READWRITE | gst::PARAM_FLAG_MUTABLE_PLAYING,
|
glib::ParamFlags::READWRITE | gst::PARAM_FLAG_MUTABLE_PLAYING,
|
||||||
),
|
),
|
||||||
|
glib::ParamSpec::new_uint64(
|
||||||
|
"timeout",
|
||||||
|
"Timeout",
|
||||||
|
"Duration after which to erase overlay when no cc data has arrived for the selected field",
|
||||||
|
gst::ClockTime::from_seconds(16).nseconds(),
|
||||||
|
u64::MAX,
|
||||||
|
u64::MAX,
|
||||||
|
glib::ParamFlags::READWRITE | gst::PARAM_FLAG_MUTABLE_PLAYING,
|
||||||
|
),
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -627,6 +667,16 @@ impl ObjectImpl for Cea608Overlay {
|
||||||
settings.black_background = value.get().expect("type checked upstream");
|
settings.black_background = value.get().expect("type checked upstream");
|
||||||
let _ = state.layout.take();
|
let _ = state.layout.take();
|
||||||
}
|
}
|
||||||
|
"timeout" => {
|
||||||
|
let mut settings = self.settings.lock().unwrap();
|
||||||
|
|
||||||
|
let timeout = value.get().expect("type checked upstream");
|
||||||
|
|
||||||
|
settings.timeout = match timeout {
|
||||||
|
u64::MAX => gst::ClockTime::NONE,
|
||||||
|
_ => Some(gst::ClockTime::from_nseconds(timeout)),
|
||||||
|
};
|
||||||
|
}
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -641,6 +691,14 @@ impl ObjectImpl for Cea608Overlay {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
settings.black_background.to_value()
|
settings.black_background.to_value()
|
||||||
}
|
}
|
||||||
|
"timeout" => {
|
||||||
|
let settings = self.settings.lock().unwrap();
|
||||||
|
if let Some(timeout) = settings.timeout {
|
||||||
|
timeout.nseconds().to_value()
|
||||||
|
} else {
|
||||||
|
u64::MAX.to_value()
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue