video: migrate to new ClockTime design

This commit is contained in:
François Laignel 2021-06-04 19:06:24 +02:00
parent c2de0649a7
commit e16cad7c8f
24 changed files with 677 additions and 342 deletions

View file

@ -186,7 +186,12 @@ impl VideoDecoderImpl for CdgDec {
}
}
gst_debug!(CAT, obj: element, "Finish frame pts={}", frame.pts());
gst_debug!(
CAT,
obj: element,
"Finish frame pts={}",
frame.pts().display()
);
element.finish_frame(frame)
}

View file

@ -10,7 +10,6 @@ use gst::format::Bytes;
use gst::glib;
use gst::gst_debug;
use gst::subclass::prelude::*;
use gst::SECOND_VAL;
use gst_base::prelude::*;
use gst_base::subclass::prelude::*;
use once_cell::sync::Lazy;
@ -94,23 +93,22 @@ impl ElementImpl for CdgParse {
}
fn bytes_to_time(bytes: Bytes) -> gst::ClockTime {
match bytes {
Bytes(Some(bytes)) => {
let nb = bytes / CDG_PACKET_SIZE as u64;
gst::ClockTime(nb.mul_div_round(SECOND_VAL, CDG_PACKET_PERIOD))
}
Bytes(None) => gst::ClockTime::none(),
}
let nb = bytes.0 / CDG_PACKET_SIZE as u64;
gst::ClockTime::from_nseconds(
nb.mul_div_round(*gst::ClockTime::SECOND, CDG_PACKET_PERIOD)
.unwrap(),
)
}
fn time_to_bytes(time: gst::ClockTime) -> Bytes {
match time.nseconds() {
Some(time) => {
let bytes = time.mul_div_round(CDG_PACKET_PERIOD * CDG_PACKET_SIZE as u64, SECOND_VAL);
Bytes(bytes)
}
None => Bytes(None),
}
Bytes(
time.nseconds()
.mul_div_round(
CDG_PACKET_PERIOD * CDG_PACKET_SIZE as u64,
*gst::ClockTime::SECOND,
)
.unwrap(),
)
}
impl BaseParseImpl for CdgParse {
@ -122,8 +120,8 @@ impl BaseParseImpl for CdgParse {
let pad = element.src_pad();
if pad.query(&mut query) {
let size = query.result();
let duration = bytes_to_time(size.try_into().unwrap());
element.set_duration(duration, 0);
let bytes: Option<Bytes> = size.try_into().unwrap();
element.set_duration(bytes.map(bytes_to_time), 0);
}
Ok(())
@ -198,7 +196,7 @@ impl BaseParseImpl for CdgParse {
}
};
let pts = bytes_to_time(Bytes(Some(frame.offset())));
let pts = bytes_to_time(Bytes(frame.offset()));
let buffer = frame.buffer_mut().unwrap();
buffer.set_pts(pts);
@ -226,10 +224,10 @@ impl BaseParseImpl for CdgParse {
match (src_val, dest_format) {
(gst::GenericFormattedValue::Bytes(bytes), gst::Format::Time) => {
Some(bytes_to_time(bytes).into())
Some(bytes.map(bytes_to_time).into())
}
(gst::GenericFormattedValue::Time(time), gst::Format::Bytes) => {
Some(time_to_bytes(time).into())
Some(time.map(time_to_bytes).into())
}
_ => None,
}

View file

@ -84,7 +84,7 @@ fn test_cdgdec() {
.expect("Unable to set the pipeline to the `Playing` state");
let bus = pipeline.bus().unwrap();
for msg in bus.iter_timed(gst::CLOCK_TIME_NONE) {
for msg in bus.iter_timed(gst::ClockTime::NONE) {
use gst::MessageView;
match msg.view() {
MessageView::Error(err) => {

View file

@ -35,13 +35,13 @@ static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
)
});
const DEFAULT_WINDOW: u64 = 10 * gst::SECOND_VAL;
const DEFAULT_WINDOW: gst::ClockTime = gst::ClockTime::from_seconds(10);
const DEFAULT_CC608: bool = false;
const DEFAULT_CC708: bool = false;
#[derive(Debug, Clone, Copy)]
struct Settings {
pub window: u64,
pub window: gst::ClockTime,
pub cc608: bool,
pub cc708: bool,
}
@ -65,8 +65,8 @@ enum CCFormat {
#[derive(Debug, Clone, Copy)]
struct State {
format: CCFormat,
last_cc608_change: gst::ClockTime,
last_cc708_change: gst::ClockTime,
last_cc608_change: Option<gst::ClockTime>,
last_cc708_change: Option<gst::ClockTime>,
}
#[derive(Default)]
@ -177,27 +177,27 @@ impl CCDetect {
);
if cc_packet.cc608 != settings.cc608 {
if state.last_cc608_change.is_none()
|| ts - state.last_cc608_change > settings.window.into()
{
if state.last_cc608_change.map_or(true, |last_cc608_change| {
ts > last_cc608_change + settings.window
}) {
settings.cc608 = cc_packet.cc608;
state.last_cc608_change = ts;
state.last_cc608_change = Some(ts);
notify_cc608 = true;
}
} else {
state.last_cc608_change = ts;
state.last_cc608_change = Some(ts);
}
if cc_packet.cc708 != settings.cc708 {
if state.last_cc708_change.is_none()
|| ts - state.last_cc708_change > settings.window.into()
{
if state.last_cc708_change.map_or(true, |last_cc708_change| {
ts > last_cc708_change + settings.window
}) {
settings.cc708 = cc_packet.cc708;
state.last_cc708_change = ts;
state.last_cc708_change = Some(ts);
notify_cc708 = true;
}
} else {
state.last_cc708_change = ts;
state.last_cc708_change = Some(ts);
}
gst_trace!(CAT, "changed to settings {:?} state {:?}", settings, state);
@ -230,8 +230,8 @@ impl ObjectImpl for CCDetect {
"Window",
"Window of time (in ns) to determine if captions exist in the stream",
0,
u64::MAX,
DEFAULT_WINDOW,
u64::MAX - 1,
DEFAULT_WINDOW.nseconds(),
glib::ParamFlags::READWRITE | gst::PARAM_FLAG_MUTABLE_PLAYING,
),
glib::ParamSpec::new_boolean(
@ -264,7 +264,8 @@ impl ObjectImpl for CCDetect {
match pspec.name() {
"window" => {
let mut settings = self.settings.lock().unwrap();
settings.window = value.get().expect("type checked upstream");
settings.window =
gst::ClockTime::from_nseconds(value.get().expect("type checked upstream"));
}
_ => unimplemented!(),
}
@ -274,7 +275,7 @@ impl ObjectImpl for CCDetect {
match pspec.name() {
"window" => {
let settings = self.settings.lock().unwrap();
settings.window.to_value()
settings.window.nseconds().to_value()
}
"cc608" => {
let settings = self.settings.lock().unwrap();
@ -350,14 +351,14 @@ impl BaseTransformImpl for CCDetect {
) -> Result<gst::FlowSuccess, gst::FlowError> {
let map = buf.map_readable().map_err(|_| gst::FlowError::Error)?;
if buf.pts().is_none() {
let pts = buf.pts().ok_or_else(|| {
gst::element_error!(
element,
gst::ResourceError::Read,
["Input buffers must have valid timestamps"]
);
return Err(gst::FlowError::Error);
}
gst::FlowError::Error
})?;
let format = {
let mut state_guard = self.state.lock().unwrap();
@ -377,7 +378,7 @@ impl BaseTransformImpl for CCDetect {
}
};
self.maybe_update_properties(element, buf.pts(), cc_packet)
self.maybe_update_properties(element, pts, cc_packet)
.map_err(|_| gst::FlowError::Error)?;
Ok(gst::FlowSuccess::Ok)
@ -428,8 +429,8 @@ impl BaseTransformImpl for CCDetect {
*self.state.lock().unwrap() = Some(State {
format: cc_format,
last_cc608_change: gst::ClockTime::none(),
last_cc708_change: gst::ClockTime::none(),
last_cc608_change: gst::ClockTime::NONE,
last_cc708_change: gst::ClockTime::NONE,
});
Ok(())

View file

@ -42,8 +42,8 @@ use std::collections::BTreeMap;
#[derive(Debug)]
struct TimestampedLines {
lines: Lines,
pts: gst::ClockTime,
duration: gst::ClockTime,
pts: Option<gst::ClockTime>,
duration: Option<gst::ClockTime>,
}
struct Cursor {
@ -182,9 +182,9 @@ struct State {
mode: Option<Cea608Mode>,
last_cc_data: Option<u16>,
rows: BTreeMap<u32, Row>,
first_pts: gst::ClockTime,
current_pts: gst::ClockTime,
current_duration: gst::ClockTime,
first_pts: Option<gst::ClockTime>,
current_pts: Option<gst::ClockTime>,
current_duration: Option<gst::ClockTime>,
carriage_return: Option<bool>,
clear: Option<bool>,
cursor: Cursor,
@ -197,9 +197,9 @@ impl Default for State {
mode: None,
last_cc_data: None,
rows: BTreeMap::new(),
first_pts: gst::CLOCK_TIME_NONE,
current_pts: gst::CLOCK_TIME_NONE,
current_duration: gst::CLOCK_TIME_NONE,
first_pts: gst::ClockTime::NONE,
current_pts: gst::ClockTime::NONE,
current_duration: gst::ClockTime::NONE,
carriage_return: None,
clear: None,
cursor: Cursor {
@ -377,20 +377,31 @@ fn eia608_to_text(cc_data: u16) -> String {
fn dump(
element: &super::Cea608ToJson,
cc_data: u16,
pts: gst::ClockTime,
duration: gst::ClockTime,
pts: impl Into<Option<gst::ClockTime>>,
duration: impl Into<Option<gst::ClockTime>>,
) {
let pts = pts.into();
let end = pts
.zip(duration.into())
.map(|(pts, duration)| pts + duration);
if cc_data != 0x8080 {
gst_debug!(
CAT,
obj: element,
"{} -> {}: {}",
pts,
pts + duration,
pts.display(),
end.display(),
eia608_to_text(cc_data)
);
} else {
gst_trace!(CAT, obj: element, "{} -> {}: padding", pts, pts + duration);
gst_trace!(
CAT,
obj: element,
"{} -> {}: padding",
pts.display(),
end.display()
);
}
}
@ -438,11 +449,16 @@ impl State {
let pts = self.first_pts;
let duration = match self.mode {
Some(Cea608Mode::PopOn) => gst::CLOCK_TIME_NONE,
_ => self.current_pts + self.current_duration - self.first_pts,
Some(Cea608Mode::PopOn) => gst::ClockTime::NONE,
_ => self
.current_pts
.zip(self.current_duration)
.map(|(cur_pts, cur_duration)| cur_pts + cur_duration)
.zip(self.first_pts)
.and_then(|(cur_end, first_pts)| cur_end.checked_sub(first_pts)),
};
self.first_pts = gst::CLOCK_TIME_NONE;
self.first_pts = gst::ClockTime::NONE;
let mut lines: Vec<Line> = vec![];
@ -477,7 +493,7 @@ impl State {
clear,
},
pts: self.current_pts,
duration: 0.into(),
duration: Some(gst::ClockTime::ZERO),
})
} else {
None
@ -487,7 +503,12 @@ impl State {
fn drain_pending(&mut self, element: &super::Cea608ToJson) -> Option<TimestampedLines> {
if let Some(mut pending) = self.pending_lines.take() {
gst_log!(CAT, obj: element, "Draining pending");
pending.duration = self.current_pts + self.current_duration - pending.pts;
pending.duration = self
.current_pts
.zip(self.current_duration)
.map(|(cur_pts, cur_dur)| cur_pts + cur_dur)
.zip(pending.pts)
.and_then(|(cur_end, pending_pts)| cur_end.checked_sub(pending_pts));
Some(pending)
} else {
None
@ -675,8 +696,8 @@ impl State {
fn handle_cc_data(
&mut self,
element: &super::Cea608ToJson,
pts: gst::ClockTime,
duration: gst::ClockTime,
pts: Option<gst::ClockTime>,
duration: Option<gst::ClockTime>,
cc_data: u16,
) -> Option<TimestampedLines> {
if (is_specialna(cc_data) || is_control(cc_data)) && Some(cc_data) == self.last_cc_data {

View file

@ -76,12 +76,12 @@ impl Cea608ToTt {
}
};
let buffer_pts = buffer.pts();
if buffer_pts.is_none() {
let buffer_pts = buffer.pts().ok_or_else(|| {
gst_error!(CAT, obj: pad, "Require timestamped buffers");
return Err(gst::FlowError::Error);
}
let pts = (buffer_pts.unwrap() as f64) / 1_000_000_000.0;
gst::FlowError::Error
})?;
let pts = (buffer_pts.nseconds() as f64) / 1_000_000_000.0;
let data = buffer.map_readable().map_err(|_| {
gst_error!(CAT, obj: pad, "Can't map buffer readable");
@ -130,11 +130,7 @@ impl Cea608ToTt {
}
};
let duration = if buffer_pts > previous_text.0 {
buffer_pts - previous_text.0
} else {
0.into()
};
let duration = buffer_pts.saturating_sub(previous_text.0);
let (timestamp, text) = previous_text;
@ -181,7 +177,7 @@ impl Cea608ToTt {
}
fn split_time(time: gst::ClockTime) -> (u64, u8, u8, u16) {
let time = time.unwrap();
let time = time.nseconds();
let mut s = time / 1_000_000_000;
let mut m = s / 60;
@ -347,11 +343,18 @@ impl Cea608ToTt {
};
let buffer = match format {
Format::Vtt => Self::create_vtt_buffer(timestamp, 0.into(), text),
Format::Srt => {
Self::create_srt_buffer(timestamp, 0.into(), state.index, text)
Format::Vtt => {
Self::create_vtt_buffer(timestamp, gst::ClockTime::ZERO, text)
}
Format::Srt => Self::create_srt_buffer(
timestamp,
gst::ClockTime::ZERO,
state.index,
text,
),
Format::Raw => {
Self::create_raw_buffer(timestamp, gst::ClockTime::ZERO, text)
}
Format::Raw => Self::create_raw_buffer(timestamp, 0.into(), text),
};
state.index += 1;
drop(state);

View file

@ -53,7 +53,7 @@ struct PullState {
need_stream_start: bool,
stream_id: String,
offset: u64,
duration: gst::ClockTime,
duration: Option<gst::ClockTime>,
}
impl PullState {
@ -62,7 +62,7 @@ impl PullState {
need_stream_start: true,
stream_id: pad.create_stream_id(element, Some("src")).to_string(),
offset: 0,
duration: gst::CLOCK_TIME_NONE,
duration: gst::ClockTime::NONE,
}
}
}
@ -74,8 +74,8 @@ struct State {
format: Option<Format>,
need_segment: bool,
pending_events: Vec<gst::Event>,
start_position: gst::ClockTime,
last_position: gst::ClockTime,
start_position: Option<gst::ClockTime>,
last_position: Option<gst::ClockTime>,
last_timecode: Option<gst_video::ValidVideoTimeCode>,
timecode_rate: Option<(u8, bool)>,
segment: gst::FormattedSegment<gst::ClockTime>,
@ -100,11 +100,11 @@ impl Default for State {
format: None,
need_segment: true,
pending_events: Vec::new(),
start_position: gst::CLOCK_TIME_NONE,
last_position: gst::CLOCK_TIME_NONE,
start_position: None,
last_position: None,
last_timecode: None,
timecode_rate: None,
segment: gst::FormattedSegment::<gst::ClockTime>::new(),
segment: gst::FormattedSegment::new(),
pull: None,
seeking: false,
discont: false,
@ -221,33 +221,35 @@ impl State {
element: &super::MccParse,
timecode: &gst_video::ValidVideoTimeCode,
) {
let nsecs = gst::ClockTime::from(timecode.nsec_since_daily_jam());
let nsecs = timecode.time_since_daily_jam();
if self.start_position.is_none() {
self.start_position = nsecs;
self.start_position = Some(nsecs);
}
let start_position = self.start_position.expect("checked above");
let nsecs = if nsecs < self.start_position {
let nsecs = nsecs.checked_sub(start_position).unwrap_or_else(|| {
gst_fixme!(
CAT,
obj: element,
"New position {} < start position {}",
nsecs,
self.start_position
start_position,
);
self.start_position
} else {
nsecs - self.start_position
};
start_position
});
if self.last_position.is_none() || nsecs >= self.last_position {
self.last_position = nsecs;
if self
.last_position
.map_or(true, |last_position| nsecs >= last_position)
{
self.last_position = Some(nsecs);
} else {
gst_fixme!(
CAT,
obj: element,
"New position {} < last position {}",
nsecs,
self.last_position
self.last_position.display(),
);
}
}
@ -272,9 +274,8 @@ impl State {
}
buffer.set_duration(
gst::SECOND
.mul_div_ceil(*framerate.denom() as u64, *framerate.numer() as u64)
.unwrap_or(gst::CLOCK_TIME_NONE),
gst::ClockTime::SECOND
.mul_div_ceil(*framerate.denom() as u64, *framerate.numer() as u64),
);
}
@ -518,11 +519,15 @@ impl MccParse {
) -> Result<MutexGuard<State>, gst::FlowError> {
let (framerate, drop_frame) = parse_timecode_rate(state.timecode_rate)?;
let timecode = state.handle_timecode(element, framerate, drop_frame, tc)?;
let nsecs = gst::ClockTime::from(timecode.nsec_since_daily_jam());
let nsecs = timecode.time_since_daily_jam();
state.last_timecode = Some(timecode);
if nsecs >= state.segment.start() {
if state
.segment
.start()
.map_or(false, |seg_start| nsecs >= seg_start)
{
state.seeking = false;
state.discont = true;
state.replay_last_line = true;
@ -558,8 +563,12 @@ impl MccParse {
// Update the last_timecode to the current one
state.last_timecode = Some(timecode);
let send_eos = state.segment.stop().is_some()
&& buffer.pts() + buffer.duration() >= state.segment.stop();
let send_eos = state.segment.stop().map_or(false, |stop| {
buffer
.pts()
.zip(buffer.duration())
.map_or(false, |(pts, duration)| pts + duration >= stop)
});
// Drop our state mutex while we push out buffers or events
drop(state);
@ -670,8 +679,8 @@ impl MccParse {
}
let size = match q.result().try_into().unwrap() {
gst::format::Bytes(Some(size)) => size,
gst::format::Bytes(None) => {
Some(gst::format::Bytes(size)) => size,
None => {
return Err(loggable_error!(CAT, "Failed to query upstream duration"));
}
};
@ -820,12 +829,12 @@ impl MccParse {
Ok(Some(tc)) => {
let mut state = self.state.lock().unwrap();
let mut pull = state.pull.as_mut().unwrap();
pull.duration = tc.nsec_since_daily_jam().into();
pull.duration = Some(tc.time_since_daily_jam());
}
Ok(None) => {
let mut state = self.state.lock().unwrap();
let mut pull = state.pull.as_mut().unwrap();
pull.duration = 0.into();
pull.duration = Some(gst::ClockTime::ZERO);
}
Err(err) => {
err.log();
@ -886,11 +895,11 @@ impl MccParse {
if let Some(pull) = &mut state.pull {
pull.offset = 0;
}
state.segment = gst::FormattedSegment::<gst::ClockTime>::new();
state.segment = gst::FormattedSegment::new();
state.need_segment = true;
state.pending_events.clear();
state.start_position = 0.into();
state.last_position = 0.into();
state.start_position = Some(gst::ClockTime::ZERO);
state.last_position = None;
state.last_timecode = None;
state.timecode_rate = None;
state.last_raw_line = [].to_vec();
@ -954,7 +963,7 @@ impl MccParse {
let (rate, flags, start_type, start, stop_type, stop) = event.get();
let mut start: gst::ClockTime = match start.try_into() {
let mut start: Option<gst::ClockTime> = match start.try_into() {
Ok(start) => start,
Err(_) => {
gst_error!(CAT, obj: element, "seek has invalid format");
@ -962,7 +971,7 @@ impl MccParse {
}
};
let mut stop: gst::ClockTime = match stop.try_into() {
let mut stop: Option<gst::ClockTime> = match stop.try_into() {
Ok(stop) => stop,
Err(_) => {
gst_error!(CAT, obj: element, "seek has invalid format");
@ -1002,11 +1011,17 @@ impl MccParse {
let pull = state.pull.as_ref().unwrap();
if start_type == gst::SeekType::Set {
start = start.min(pull.duration).unwrap_or(start);
start = start
.zip(pull.duration)
.map(|(start, duration)| start.min(duration))
.or(start);
}
if stop_type == gst::SeekType::Set {
stop = stop.min(pull.duration).unwrap_or(stop);
stop = stop
.zip(pull.duration)
.map(|(stop, duration)| stop.min(duration))
.or(stop);
}
state.seeking = true;
@ -1069,7 +1084,7 @@ impl MccParse {
if let Some(pull) = state.pull.as_ref() {
q.set(
true,
gst::GenericFormattedValue::Time(0.into()),
gst::GenericFormattedValue::Time(gst::ClockTime::ZERO.into()),
gst::GenericFormattedValue::Time(pull.duration),
);
true

View file

@ -194,12 +194,10 @@ impl State {
assert!(self.framerate.is_some());
let framerate = self.framerate.unwrap();
let dur = gst::SECOND
.mul_div_floor(
self.internal_buffer.len() as u64 * *framerate.denom() as u64,
*framerate.numer() as u64,
)
.unwrap_or(gst::CLOCK_TIME_NONE);
let dur = gst::ClockTime::SECOND.mul_div_floor(
self.internal_buffer.len() as u64 * *framerate.denom() as u64,
*framerate.numer() as u64,
);
buf_mut.set_duration(dur);
// Copy the metadata of the first buffer

View file

@ -47,7 +47,7 @@ struct PullState {
need_stream_start: bool,
stream_id: String,
offset: u64,
duration: gst::ClockTime,
duration: Option<gst::ClockTime>,
}
impl PullState {
@ -56,7 +56,7 @@ impl PullState {
need_stream_start: true,
stream_id: pad.create_stream_id(element, Some("src")).to_string(),
offset: 0,
duration: gst::CLOCK_TIME_NONE,
duration: gst::ClockTime::NONE,
}
}
}
@ -68,7 +68,7 @@ struct State {
need_segment: bool,
pending_events: Vec<gst::Event>,
framerate: Option<gst::Fraction>,
last_position: gst::ClockTime,
last_position: Option<gst::ClockTime>,
last_timecode: Option<gst_video::ValidVideoTimeCode>,
segment: gst::FormattedSegment<gst::ClockTime>,
@ -90,9 +90,9 @@ impl Default for State {
need_segment: true,
pending_events: Vec::new(),
framerate: None,
last_position: gst::CLOCK_TIME_NONE,
last_position: None,
last_timecode: None,
segment: gst::FormattedSegment::<gst::ClockTime>::new(),
segment: gst::FormattedSegment::new(),
pull: None,
seeking: false,
discont: false,
@ -196,17 +196,20 @@ impl State {
timecode: &gst_video::ValidVideoTimeCode,
element: &super::SccParse,
) {
let nsecs = gst::ClockTime::from(timecode.nsec_since_daily_jam());
let nsecs = timecode.time_since_daily_jam();
if self.last_position.is_none() || nsecs >= self.last_position {
self.last_position = nsecs;
if self
.last_position
.map_or(true, |last_position| nsecs >= last_position)
{
self.last_position = Some(nsecs);
} else {
gst_fixme!(
CAT,
obj: element,
"New position {} < last position {}",
nsecs,
self.last_position
self.last_position.display(),
);
}
}
@ -225,9 +228,8 @@ impl State {
buffer.set_pts(self.last_position);
buffer.set_duration(
gst::SECOND
.mul_div_ceil(*framerate.denom() as u64, *framerate.numer() as u64)
.unwrap_or(gst::CLOCK_TIME_NONE),
gst::ClockTime::SECOND
.mul_div_ceil(*framerate.denom() as u64, *framerate.numer() as u64),
);
}
@ -371,7 +373,7 @@ impl SccParse {
};
let mut timecode = state.handle_timecode(&tc, framerate, element)?;
let start_time = gst::ClockTime::from(timecode.nsec_since_daily_jam());
let start_time = timecode.time_since_daily_jam();
let segment_start = state.segment.start();
let clip_buffers = if state.seeking {
// If we are in the middle of seeking, check whether this line
@ -380,7 +382,7 @@ impl SccParse {
let mut end_timecode = timecode.clone();
// add one more frame here so that add duration of the last frame
end_timecode.add_frames(num_bufs + 1);
let stop_time = gst::ClockTime::from(end_timecode.nsec_since_daily_jam());
let stop_time = end_timecode.time_since_daily_jam();
gst_trace!(
CAT,
@ -388,11 +390,11 @@ impl SccParse {
"Checking inside of segment, line start {} line stop {} segment start {} num bufs {}",
start_time,
stop_time,
segment_start,
segment_start.display(),
num_bufs,
);
if stop_time > segment_start {
if segment_start.map_or(false, |seg_start| stop_time > seg_start) {
state.seeking = false;
state.discont = true;
state.need_flush_stop = true;
@ -427,8 +429,14 @@ impl SccParse {
timecode.increment_frame();
if clip_buffers {
let end_time = buffer.pts() + buffer.duration();
if end_time < segment_start {
let end_time = buffer
.pts()
.zip(buffer.duration())
.map(|(pts, duration)| pts + duration);
if end_time
.zip(segment_start)
.map_or(false, |(end_time, segment_start)| end_time < segment_start)
{
gst_trace!(
CAT,
obj: element,
@ -440,8 +448,12 @@ impl SccParse {
}
}
send_eos = state.segment.stop().is_some()
&& buffer.pts() + buffer.duration() >= state.segment.stop();
send_eos = state.segment.stop().map_or(false, |stop| {
buffer
.pts()
.zip(buffer.duration())
.map_or(false, |(pts, duration)| pts + duration >= stop)
});
let buffers = buffers.get_mut().unwrap();
buffers.add(buffer);
@ -564,8 +576,8 @@ impl SccParse {
}
let size = match q.result().try_into().unwrap() {
gst::format::Bytes(Some(size)) => size,
gst::format::Bytes(None) => {
Some(gst::format::Bytes(size)) => size,
None => {
return Err(loggable_error!(CAT, "Failed to query upstream duration"));
}
};
@ -704,12 +716,12 @@ impl SccParse {
Ok(Some(tc)) => {
let mut state = self.state.lock().unwrap();
let mut pull = state.pull.as_mut().unwrap();
pull.duration = tc.nsec_since_daily_jam().into();
pull.duration = Some(tc.time_since_daily_jam());
}
Ok(None) => {
let mut state = self.state.lock().unwrap();
let mut pull = state.pull.as_mut().unwrap();
pull.duration = 0.into();
pull.duration = Some(gst::ClockTime::ZERO);
}
Err(err) => {
err.log();
@ -770,10 +782,10 @@ impl SccParse {
if let Some(pull) = &mut state.pull {
pull.offset = 0;
}
state.segment = gst::FormattedSegment::<gst::ClockTime>::new();
state.segment = gst::FormattedSegment::new();
state.need_segment = true;
state.pending_events.clear();
state.last_position = 0.into();
state.last_position = None;
state.last_timecode = None;
drop(state);
@ -835,7 +847,7 @@ impl SccParse {
let (rate, flags, start_type, start, stop_type, stop) = event.get();
let mut start: gst::ClockTime = match start.try_into() {
let mut start: Option<gst::ClockTime> = match start.try_into() {
Ok(start) => start,
Err(_) => {
gst_error!(CAT, obj: element, "seek has invalid format");
@ -843,7 +855,7 @@ impl SccParse {
}
};
let mut stop: gst::ClockTime = match stop.try_into() {
let mut stop: Option<gst::ClockTime> = match stop.try_into() {
Ok(stop) => stop,
Err(_) => {
gst_error!(CAT, obj: element, "seek has invalid format");
@ -883,11 +895,17 @@ impl SccParse {
let pull = state.pull.as_ref().unwrap();
if start_type == gst::SeekType::Set {
start = start.min(pull.duration).unwrap_or(start);
start = start
.zip(pull.duration)
.map(|(start, duration)| start.min(duration))
.or(start);
}
if stop_type == gst::SeekType::Set {
stop = stop.min(pull.duration).unwrap_or(stop);
stop = stop
.zip(pull.duration)
.map(|(stop, duration)| stop.min(duration))
.or(stop);
}
state.seeking = true;
@ -950,7 +968,7 @@ impl SccParse {
if let Some(pull) = state.pull.as_ref() {
q.set(
true,
gst::GenericFormattedValue::Time(0.into()),
gst::GenericFormattedValue::Time(gst::ClockTime::ZERO.into()),
gst::GenericFormattedValue::Time(pull.duration),
);
true

View file

@ -218,7 +218,7 @@ impl State {
*self.framerate.denom() as u64,
);
let pts = (self.last_frame_no * gst::SECOND)
let pts = (self.last_frame_no * gst::ClockTime::SECOND)
.mul_div_round(fps_d, fps_n)
.unwrap();
@ -228,7 +228,7 @@ impl State {
gst_debug!(CAT, obj: element, "More text than bandwidth!");
}
let next_pts = (self.last_frame_no * gst::SECOND)
let next_pts = (self.last_frame_no * gst::ClockTime::SECOND)
.mul_div_round(fps_d, fps_n)
.unwrap();
@ -567,10 +567,12 @@ impl TtToCea608 {
*state.framerate.denom() as u64,
);
let frame_no = (pts.mul_div_round(fps_n, fps_d).unwrap() / gst::SECOND).unwrap();
let frame_no = pts.mul_div_round(fps_n, fps_d).unwrap().seconds();
state.max_frame_no =
((pts + duration).mul_div_round(fps_n, fps_d).unwrap() / gst::SECOND).unwrap();
state.max_frame_no = (pts + duration)
.mul_div_round(fps_n, fps_d)
.unwrap()
.seconds();
state.pad(element, mut_list, frame_no);
@ -792,10 +794,8 @@ impl TtToCea608 {
state.column = col;
if state.mode == Cea608Mode::PopOn {
state.erase_display_frame_no = Some(
state.last_frame_no
+ (duration.mul_div_round(fps_n, fps_d).unwrap() / gst::SECOND).unwrap(),
);
state.erase_display_frame_no =
Some(state.last_frame_no + duration.mul_div_round(fps_n, fps_d).unwrap().seconds());
}
state.pad(element, mut_list, state.max_frame_no);
@ -809,29 +809,23 @@ impl TtToCea608 {
element: &super::TtToCea608,
buffer: gst::Buffer,
) -> Result<gst::FlowSuccess, gst::FlowError> {
let pts = match buffer.pts() {
gst::CLOCK_TIME_NONE => {
gst::element_error!(
element,
gst::StreamError::Format,
["Stream with timestamped buffers required"]
);
Err(gst::FlowError::Error)
}
pts => Ok(pts),
}?;
let pts = buffer.pts().ok_or_else(|| {
gst::element_error!(
element,
gst::StreamError::Format,
["Stream with timestamped buffers required"]
);
gst::FlowError::Error
})?;
let duration = match buffer.duration() {
gst::CLOCK_TIME_NONE => {
gst::element_error!(
element,
gst::StreamError::Format,
["Buffers of stream need to have a duration"]
);
Err(gst::FlowError::Error)
}
duration => Ok(duration),
}?;
let duration = buffer.duration().ok_or_else(|| {
gst::element_error!(
element,
gst::StreamError::Format,
["Buffers of stream need to have a duration"]
);
gst::FlowError::Error
})?;
let data = buffer.map_readable().map_err(|_| {
gst_error!(CAT, obj: pad, "Can't map buffer readable");
@ -947,9 +941,10 @@ impl TtToCea608 {
);
let (timestamp, duration) = e.get();
let frame_no = ((timestamp + duration).mul_div_round(fps_n, fps_d).unwrap()
/ gst::SECOND)
.unwrap();
let frame_no = (timestamp + duration.unwrap())
.mul_div_round(fps_n, fps_d)
.unwrap()
.seconds();
state.max_frame_no = frame_no;
let mut bufferlist = gst::BufferList::new();

View file

@ -16,6 +16,7 @@
// Boston, MA 02110-1335, USA.
use gst::prelude::*;
use gst::ClockTime;
use std::sync::{Arc, Mutex};
@ -94,16 +95,37 @@ fn test_have_cc_data_notify() {
});
/* valid cc608 data moves cc608 property to true */
assert_push_data!(h, state, valid_cc608_data, 0.into(), 1, 0);
assert_push_data!(h, state, valid_cc608_data, ClockTime::ZERO, 1, 0);
/* invalid cc608 data moves cc608 property to false */
assert_push_data!(h, state, invalid_cc608_data, 1_000_000_000.into(), 2, 0);
assert_push_data!(
h,
state,
invalid_cc608_data,
ClockTime::from_nseconds(1_000_000_000),
2,
0
);
/* valid cc708 data moves cc708 property to true */
assert_push_data!(h, state, valid_cc708_data, 2_000_000_000.into(), 2, 1);
assert_push_data!(
h,
state,
valid_cc708_data,
ClockTime::from_nseconds(2_000_000_000),
2,
1
);
/* invalid cc708 data moves cc708 property to false */
assert_push_data!(h, state, invalid_cc708_data, 3_000_000_000.into(), 2, 2);
assert_push_data!(
h,
state,
invalid_cc708_data,
ClockTime::from_nseconds(3_000_000_000),
2,
2
);
}
#[test]
@ -137,36 +159,57 @@ fn test_cc_data_window() {
});
/* valid cc608 data moves cc608 property to true */
assert_push_data!(h, state, valid_cc608_data.clone(), 0.into(), 1, 0);
assert_push_data!(h, state, valid_cc608_data.clone(), ClockTime::ZERO, 1, 0);
/* valid cc608 data moves within window */
assert_push_data!(h, state, valid_cc608_data.clone(), 300_000_000.into(), 1, 0);
assert_push_data!(
h,
state,
valid_cc608_data.clone(),
ClockTime::from_nseconds(300_000_000),
1,
0
);
/* invalid cc608 data before window expires, no change */
assert_push_data!(
h,
state,
invalid_cc608_data.clone(),
600_000_000.into(),
ClockTime::from_nseconds(600_000_000),
1,
0
);
/* invalid cc608 data after window expires, cc608 changes to false */
assert_push_data!(h, state, invalid_cc608_data, 1_000_000_000.into(), 2, 0);
assert_push_data!(
h,
state,
invalid_cc608_data,
ClockTime::from_nseconds(1_000_000_000),
2,
0
);
/* valid cc608 data before window expires, no change */
assert_push_data!(
h,
state,
valid_cc608_data.clone(),
1_300_000_000.into(),
ClockTime::from_nseconds(1_300_000_000),
2,
0
);
/* valid cc608 data after window expires, property changes */
assert_push_data!(h, state, valid_cc608_data, 1_600_000_000.into(), 3, 0);
assert_push_data!(
h,
state,
valid_cc608_data,
ClockTime::from_nseconds(1_600_000_000),
3,
0
);
}
#[test]
@ -215,10 +258,17 @@ fn test_have_cdp_notify() {
});
/* valid cc608 data moves cc608 property to true */
assert_push_data!(h, state, valid_cc608_data, 0.into(), 1, 0);
assert_push_data!(h, state, valid_cc608_data, ClockTime::ZERO, 1, 0);
/* invalid cc608 data moves cc608 property to false */
assert_push_data!(h, state, invalid_cc608_data, 1_000_000_000.into(), 2, 0);
assert_push_data!(
h,
state,
invalid_cc608_data,
ClockTime::from_nseconds(1_000_000_000),
2,
0
);
}
#[test]
@ -276,14 +326,56 @@ fn test_malformed_cdp_notify() {
});
/* all invalid data does not change properties */
assert_push_data!(h, state, too_short, 0.into(), 0, 0);
assert_push_data!(h, state, wrong_magic, 1_000.into(), 0, 0);
assert_push_data!(h, state, length_too_long, 2_000.into(), 0, 0);
assert_push_data!(h, state, length_too_short, 3_000.into(), 0, 0);
assert_push_data!(h, state, wrong_cc_data_header_byte, 4_000.into(), 0, 0);
assert_push_data!(h, state, big_cc_count, 5_000.into(), 0, 0);
assert_push_data!(h, state, wrong_cc_count_reserved_bits, 6_000.into(), 0, 0);
assert_push_data!(h, state, cc608_after_cc708, 7_000.into(), 0, 0);
assert_push_data!(h, state, too_short, ClockTime::from_nseconds(0), 0, 0);
assert_push_data!(h, state, wrong_magic, ClockTime::from_nseconds(1_000), 0, 0);
assert_push_data!(
h,
state,
length_too_long,
ClockTime::from_nseconds(2_000),
0,
0
);
assert_push_data!(
h,
state,
length_too_short,
ClockTime::from_nseconds(3_000),
0,
0
);
assert_push_data!(
h,
state,
wrong_cc_data_header_byte,
ClockTime::from_nseconds(4_000),
0,
0
);
assert_push_data!(
h,
state,
big_cc_count,
ClockTime::from_nseconds(5_000),
0,
0
);
assert_push_data!(
h,
state,
wrong_cc_count_reserved_bits,
ClockTime::from_nseconds(6_000),
0,
0
);
assert_push_data!(
h,
state,
cc608_after_cc708,
ClockTime::from_nseconds(7_000),
0,
0
);
}
#[test]
@ -316,11 +408,14 @@ fn test_gap_events() {
});
/* valid cc608 data moves cc608 property to true */
assert_push_data!(h, state, valid_cc608_data, 0.into(), 1, 0);
assert_push_data!(h, state, valid_cc608_data, ClockTime::ZERO, 1, 0);
/* pushing gap event within the window changes nothing */
assert_eq!(
h.push_event(gst::event::Gap::builder(100_000_000.into(), 1.into()).build()),
h.push_event(gst::event::Gap::new(
ClockTime::from_nseconds(100_000_000),
ClockTime::from_nseconds(1)
)),
true
);
@ -332,7 +427,10 @@ fn test_gap_events() {
/* pushing gap event outside the window moves cc608 property to false */
assert_eq!(
h.push_event(gst::event::Gap::builder(1_000_000_000.into(), 1.into()).build()),
h.push_event(gst::event::Gap::new(
ClockTime::from_nseconds(1_000_000_000),
ClockTime::from_nseconds(1)
)),
true
);

View file

@ -16,6 +16,7 @@
// Boston, MA 02110-1335, USA.
use gst::prelude::*;
use gst::ClockTime;
use pretty_assertions::assert_eq;
@ -42,25 +43,25 @@ fn test_parse() {
assert_eq!(h.push(buf), Ok(gst::FlowSuccess::Ok));
// Check the first 4 output buffers
let expected: [(gst::ClockTime, gst::ClockTime, &'static str); 4] = [
let expected: [(ClockTime, ClockTime, &'static str); 4] = [
(
15_048_366_666.into(),
3_236_566_667.into(),
ClockTime::from_nseconds(15_048_366_666),
ClockTime::from_nseconds(3_236_566_667),
"From New York,\r\nthis is Democracy Now!",
),
(
18_985_633_333.into(),
1_234_566_667.into(),
ClockTime::from_nseconds(18_985_633_333),
ClockTime::from_nseconds(1_234_566_667),
"Yes, Im supporting\r\nDonald Trump.",
),
(
20_220_200_000.into(),
2_168_833_333.into(),
ClockTime::from_nseconds(20_220_200_000),
ClockTime::from_nseconds(2_168_833_333),
"Im doing so as enthusiastically\r\nas I can,",
),
(
22_389_033_333.into(),
2_235_566_667.into(),
ClockTime::from_nseconds(22_389_033_333),
ClockTime::from_nseconds(2_235_566_667),
"even the fact I think\r\nhes a terrible human being.",
),
];
@ -68,10 +69,15 @@ fn test_parse() {
for (i, e) in expected.iter().enumerate() {
let buf = h.try_pull().unwrap();
assert_eq!(e.0, buf.pts(), "Unexpected PTS for {}th buffer", i + 1);
assert_eq!(
e.0,
buf.pts().unwrap(),
"Unexpected PTS for {}th buffer",
i + 1
);
assert_eq!(
e.1,
buf.duration(),
buf.duration().unwrap(),
"Unexpected duration for {}th buffer",
i + 1
);

View file

@ -136,8 +136,8 @@ Time Code Rate=30DF\r\n\
.tc();
assert_eq!(timecode, tc);
let pts = buf.pts();
assert_eq!(pts, gst::ClockTime::from_seconds(0));
let pts = buf.pts().unwrap();
assert_eq!(pts, gst::ClockTime::ZERO);
let map = buf.map_readable().expect("Couldn't map buffer readable");
assert_eq!(

View file

@ -175,9 +175,9 @@ fn test_pull() {
1.0,
gst::SeekFlags::FLUSH,
gst::SeekType::Set,
gst::GenericFormattedValue::Time(gst::SECOND),
gst::GenericFormattedValue::Time(Some(gst::ClockTime::SECOND)),
gst::SeekType::Set,
gst::GenericFormattedValue::Time(2 * gst::SECOND),
gst::GenericFormattedValue::Time(Some(2 * gst::ClockTime::SECOND)),
));
loop {
@ -185,8 +185,8 @@ fn test_pull() {
while h.buffers_in_queue() != 0 {
if let Ok(buffer) = h.pull() {
let pts = buffer.pts();
assert!(pts > gst::SECOND && pts < 2 * gst::SECOND);
let pts = buffer.pts().unwrap();
assert!(pts > gst::ClockTime::SECOND && pts < 2 * gst::ClockTime::SECOND);
}
}

View file

@ -69,8 +69,8 @@ fn test_encode_single_packet() {
.tc();
assert_eq!(timecode, tc);
let pts = buf.pts();
assert_eq!(pts, gst::ClockTime::from_seconds(0));
let pts = buf.pts().unwrap();
assert_eq!(pts, gst::ClockTime::ZERO);
let map = buf.map_readable().expect("Couldn't map buffer readable");
assert_eq!(
@ -168,8 +168,8 @@ fn test_encode_multiple_packets() {
.tc();
assert_eq!(timecode, tc1);
let pts = buf.pts();
assert_eq!(pts, gst::ClockTime::from_seconds(0));
let pts = buf.pts().unwrap();
assert_eq!(pts, gst::ClockTime::ZERO);
let map = buf.map_readable().expect("Couldn't map buffer readable");
@ -186,8 +186,8 @@ fn test_encode_multiple_packets() {
.tc();
assert_eq!(timecode, tc2);
// let pts = buf.get_pts();
// assert_eq!(pts, gst::ClockTime::from_seconds(0));
// let pts = buf.get_pts().unwrap();
// assert_eq!(pts, gst::ClockTime::ZERO);
let map = buf.map_readable().expect("Couldn't map buffer readable");
assert_eq!(
@ -215,8 +215,8 @@ fn test_encode_multiple_packets() {
.tc();
assert_eq!(timecode, tc3);
// let pts = buf.get_pts();
// assert_eq!(pts, gst::ClockTime::from_seconds(0));
// let pts = buf.get_pts().unwrap();
// assert_eq!(pts, gst::ClockTime::ZERO);
let map = buf.map_readable().expect("Couldn't map buffer readable");
assert_eq!(

View file

@ -245,9 +245,9 @@ fn test_pull() {
1.0,
gst::SeekFlags::FLUSH,
gst::SeekType::Set,
gst::GenericFormattedValue::Time(18 * gst::SECOND),
gst::GenericFormattedValue::Time(Some(18 * gst::ClockTime::SECOND)),
gst::SeekType::Set,
gst::GenericFormattedValue::Time(19 * gst::SECOND),
gst::GenericFormattedValue::Time(Some(19 * gst::ClockTime::SECOND)),
));
loop {
@ -255,10 +255,12 @@ fn test_pull() {
while h.buffers_in_queue() != 0 {
if let Ok(buffer) = h.pull() {
let pts = buffer.pts();
let end_time = pts + buffer.duration();
let pts = buffer.pts().unwrap();
let end_time = pts + buffer.duration().unwrap();
assert!(end_time >= 18 * gst::SECOND && pts < 19 * gst::SECOND);
assert!(
end_time >= 18 * gst::ClockTime::SECOND && pts < 19 * gst::ClockTime::SECOND
);
}
}

View file

@ -15,6 +15,7 @@
// Free Software Foundation, Inc., 51 Franklin Street, Suite 500,
// Boston, MA 02110-1335, USA.
use gst::ClockTime;
use pretty_assertions::assert_eq;
fn init() {
@ -29,8 +30,8 @@ fn init() {
fn new_timed_buffer<T: AsRef<[u8]> + Send + 'static>(
slice: T,
timestamp: gst::ClockTime,
duration: gst::ClockTime,
timestamp: ClockTime,
duration: ClockTime,
) -> gst::buffer::Buffer {
let mut buf = gst::Buffer::from_slice(slice);
let buf_ref = buf.get_mut().unwrap();
@ -63,13 +64,13 @@ fn test_one_timed_buffer_and_eos() {
let _event = h.pull_event().unwrap();
}
let inbuf = new_timed_buffer(&"Hello", gst::SECOND, gst::SECOND);
let inbuf = new_timed_buffer(&"Hello", ClockTime::SECOND, ClockTime::SECOND);
assert_eq!(h.push(inbuf), Ok(gst::FlowSuccess::Ok));
loop {
let outbuf = h.pull().unwrap();
if outbuf.pts() + outbuf.duration() >= gst::SECOND {
if outbuf.pts().unwrap() + outbuf.duration().unwrap() >= ClockTime::SECOND {
break;
}
@ -77,23 +78,56 @@ fn test_one_timed_buffer_and_eos() {
assert_eq!(&*data, &[0x80, 0x80]);
}
let expected: [(gst::ClockTime, gst::ClockTime, [u8; 2usize]); 7] = [
(1_000_000_000.into(), 33_333_333.into(), [0x94, 0x20]), /* resume_caption_loading */
(1_033_333_333.into(), 33_333_334.into(), [0x94, 0xae]), /* erase_non_displayed_memory */
(1_066_666_667.into(), 33_333_333.into(), [0x94, 0x70]), /* preamble */
(1_100_000_000.into(), 33_333_333.into(), [0xc8, 0xe5]), /* H e */
(1_133_333_333.into(), 33_333_334.into(), [0xec, 0xec]), /* l l */
(1_166_666_667.into(), 33_333_333.into(), [0xef, 0x80]), /* o, nil */
(1_200_000_000.into(), 33_333_333.into(), [0x94, 0x2f]), /* end_of_caption */
let expected: [(ClockTime, ClockTime, [u8; 2usize]); 7] = [
(
ClockTime::from_nseconds(1_000_000_000),
ClockTime::from_nseconds(33_333_333),
[0x94, 0x20],
), /* resume_caption_loading */
(
ClockTime::from_nseconds(1_033_333_333),
ClockTime::from_nseconds(33_333_334),
[0x94, 0xae],
), /* erase_non_displayed_memory */
(
ClockTime::from_nseconds(1_066_666_667),
ClockTime::from_nseconds(33_333_333),
[0x94, 0x70],
), /* preamble */
(
ClockTime::from_nseconds(1_100_000_000),
ClockTime::from_nseconds(33_333_333),
[0xc8, 0xe5],
), /* H e */
(
ClockTime::from_nseconds(1_133_333_333),
ClockTime::from_nseconds(33_333_334),
[0xec, 0xec],
), /* l l */
(
ClockTime::from_nseconds(1_166_666_667),
ClockTime::from_nseconds(33_333_333),
[0xef, 0x80],
), /* o, nil */
(
ClockTime::from_nseconds(1_200_000_000),
ClockTime::from_nseconds(33_333_333),
[0x94, 0x2f],
), /* end_of_caption */
];
for (i, e) in expected.iter().enumerate() {
let outbuf = h.try_pull().unwrap();
assert_eq!(e.0, outbuf.pts(), "Unexpected PTS for {}th buffer", i + 1);
assert_eq!(
e.0,
outbuf.pts().unwrap(),
"Unexpected PTS for {}th buffer",
i + 1
);
assert_eq!(
e.1,
outbuf.duration(),
outbuf.duration().unwrap(),
"Unexpected duration for {}th buffer",
i + 1
);
@ -110,7 +144,7 @@ fn test_one_timed_buffer_and_eos() {
loop {
let outbuf = h.try_pull().unwrap();
let data = outbuf.map_readable().unwrap();
if outbuf.pts() == 2_200_000_000.into() {
if outbuf.pts().unwrap() == ClockTime::from_nseconds(2_200_000_000) {
assert_eq!(&*data, &[0x94, 0x2c]);
break;
} else {
@ -139,10 +173,18 @@ fn test_erase_display_memory_non_spliced() {
let _event = h.pull_event().unwrap();
}
let inbuf = new_timed_buffer(&"Hello", 1_000_000_000.into(), gst::SECOND);
let inbuf = new_timed_buffer(
&"Hello",
ClockTime::from_nseconds(1_000_000_000),
ClockTime::SECOND,
);
assert_eq!(h.push(inbuf), Ok(gst::FlowSuccess::Ok));
let inbuf = new_timed_buffer(&"World", 3_000_000_000.into(), gst::SECOND);
let inbuf = new_timed_buffer(
&"World",
ClockTime::from_nseconds(3_000_000_000),
ClockTime::SECOND,
);
assert_eq!(h.push(inbuf), Ok(gst::FlowSuccess::Ok));
let mut erase_display_buffers = 0;
@ -150,7 +192,7 @@ fn test_erase_display_memory_non_spliced() {
while h.buffers_in_queue() > 0 {
let outbuf = h.pull().unwrap();
if outbuf.pts() == 2_200_000_000.into() {
if outbuf.pts().unwrap() == ClockTime::from_nseconds(2_200_000_000) {
let data = outbuf.map_readable().unwrap();
assert_eq!(&*data, &[0x94, 0x2c]);
erase_display_buffers += 1;
@ -175,28 +217,37 @@ fn test_erase_display_memory_spliced() {
let _event = h.pull_event().unwrap();
}
let inbuf = new_timed_buffer(&"Hello", 1_000_000_000.into(), gst::SECOND);
let inbuf = new_timed_buffer(
&"Hello",
ClockTime::from_nseconds(1_000_000_000),
ClockTime::SECOND,
);
assert_eq!(h.push(inbuf), Ok(gst::FlowSuccess::Ok));
let inbuf = new_timed_buffer(&"World", 2_000_000_000.into(), gst::SECOND);
let inbuf = new_timed_buffer(
&"World",
ClockTime::from_nseconds(2_000_000_000),
ClockTime::SECOND,
);
assert_eq!(h.push(inbuf), Ok(gst::FlowSuccess::Ok));
let mut erase_display_buffers = 0;
let mut prev_pts: gst::ClockTime = 0.into();
let mut prev_pts: ClockTime = ClockTime::ZERO;
while h.buffers_in_queue() > 0 {
let outbuf = h.pull().unwrap();
/* Check that our timestamps are strictly ascending */
assert!(outbuf.pts() >= prev_pts);
let pts = outbuf.pts().unwrap();
assert!(pts >= prev_pts);
if outbuf.pts() == 2_000_000_000.into() {
if pts == ClockTime::from_nseconds(2_000_000_000) {
let data = outbuf.map_readable().unwrap();
assert_eq!(&*data, &[0x94, 0x2c]);
erase_display_buffers += 1;
}
prev_pts = outbuf.pts();
prev_pts = pts;
}
assert_eq!(erase_display_buffers, 1);
@ -216,10 +267,18 @@ fn test_output_gaps() {
let _event = h.pull_event().unwrap();
}
let inbuf = new_timed_buffer(&"Hello", 1_000_000_000.into(), gst::SECOND);
let inbuf = new_timed_buffer(
&"Hello",
ClockTime::from_nseconds(1_000_000_000),
ClockTime::SECOND,
);
assert_eq!(h.push(inbuf), Ok(gst::FlowSuccess::Ok));
let inbuf = new_timed_buffer(&"World", 3_000_000_000.into(), gst::SECOND);
let inbuf = new_timed_buffer(
&"World",
ClockTime::from_nseconds(3_000_000_000),
ClockTime::SECOND,
);
assert_eq!(h.push(inbuf), Ok(gst::FlowSuccess::Ok));
h.push_event(gst::event::Eos::new());
@ -227,7 +286,7 @@ fn test_output_gaps() {
/* Padding */
loop {
let outbuf = h.pull().unwrap();
if outbuf.pts() + outbuf.duration() >= gst::SECOND {
if outbuf.pts().unwrap() + outbuf.duration().unwrap() >= ClockTime::SECOND {
break;
}
@ -238,7 +297,9 @@ fn test_output_gaps() {
/* Hello */
loop {
let outbuf = h.pull().unwrap();
if outbuf.pts() + outbuf.duration() >= 1_233_333_333.into() {
if outbuf.pts().unwrap() + outbuf.duration().unwrap()
>= ClockTime::from_nseconds(1_233_333_333)
{
break;
}
@ -249,12 +310,14 @@ fn test_output_gaps() {
/* Padding */
loop {
let outbuf = h.pull().unwrap();
if outbuf.pts() + outbuf.duration() >= 3_000_000_000.into() {
if outbuf.pts().unwrap() + outbuf.duration().unwrap()
>= ClockTime::from_nseconds(3_000_000_000)
{
break;
}
let data = outbuf.map_readable().unwrap();
if outbuf.pts() == 2_200_000_000.into() {
if outbuf.pts().unwrap() == ClockTime::from_nseconds(2_200_000_000) {
/* Erase display one second after Hello */
assert_eq!(&*data, &[0x94, 0x2C]);
} else {
@ -265,7 +328,9 @@ fn test_output_gaps() {
/* World */
loop {
let outbuf = h.pull().unwrap();
if outbuf.pts() + outbuf.duration() >= 3_233_333_333.into() {
if outbuf.pts().unwrap() + outbuf.duration().unwrap()
>= ClockTime::from_nseconds(3_233_333_333)
{
break;
}
@ -290,16 +355,16 @@ fn test_one_timed_buffer_and_eos_roll_up2() {
let _event = h.pull_event().unwrap();
}
let inbuf = new_timed_buffer(&"Hello", gst::SECOND, gst::SECOND);
let inbuf = new_timed_buffer(&"Hello", ClockTime::SECOND, ClockTime::SECOND);
assert_eq!(h.push(inbuf), Ok(gst::FlowSuccess::Ok));
let inbuf = new_timed_buffer(&"World", 2 * gst::SECOND, 1.into());
let inbuf = new_timed_buffer(&"World", 2 * ClockTime::SECOND, ClockTime::from_nseconds(1));
assert_eq!(h.push(inbuf), Ok(gst::FlowSuccess::Ok));
/* Padding */
loop {
let outbuf = h.pull().unwrap();
if outbuf.pts() + outbuf.duration() >= gst::SECOND {
if outbuf.pts().unwrap() + outbuf.duration().unwrap() >= ClockTime::SECOND {
break;
}
@ -307,21 +372,46 @@ fn test_one_timed_buffer_and_eos_roll_up2() {
assert_eq!(&*data, &[0x80, 0x80]);
}
let expected: [(gst::ClockTime, gst::ClockTime, [u8; 2usize]); 5] = [
(1_000_000_000.into(), 33_333_333.into(), [0x94, 0x25]), /* roll_up_2 */
(1_033_333_333.into(), 33_333_334.into(), [0x94, 0x70]), /* preamble */
(1_066_666_667.into(), 33_333_333.into(), [0xc8, 0xe5]), /* H e */
(1_100_000_000.into(), 33_333_333.into(), [0xec, 0xec]), /* l l */
(1_133_333_333.into(), 33_333_334.into(), [0xef, 0x80]), /* o nil */
let expected: [(ClockTime, ClockTime, [u8; 2usize]); 5] = [
(
ClockTime::from_nseconds(1_000_000_000),
ClockTime::from_nseconds(33_333_333),
[0x94, 0x25],
), /* roll_up_2 */
(
ClockTime::from_nseconds(1_033_333_333),
ClockTime::from_nseconds(33_333_334),
[0x94, 0x70],
), /* preamble */
(
ClockTime::from_nseconds(1_066_666_667),
ClockTime::from_nseconds(33_333_333),
[0xc8, 0xe5],
), /* H e */
(
ClockTime::from_nseconds(1_100_000_000),
ClockTime::from_nseconds(33_333_333),
[0xec, 0xec],
), /* l l */
(
ClockTime::from_nseconds(1_133_333_333),
ClockTime::from_nseconds(33_333_334),
[0xef, 0x80],
), /* o nil */
];
for (i, e) in expected.iter().enumerate() {
let outbuf = h.try_pull().unwrap();
assert_eq!(e.0, outbuf.pts(), "Unexpected PTS for {}th buffer", i + 1);
assert_eq!(
e.0,
outbuf.pts().unwrap(),
"Unexpected PTS for {}th buffer",
i + 1
);
assert_eq!(
e.1,
outbuf.duration(),
outbuf.duration().unwrap(),
"Unexpected duration for {}th buffer",
i + 1
);
@ -333,7 +423,7 @@ fn test_one_timed_buffer_and_eos_roll_up2() {
/* Padding */
loop {
let outbuf = h.pull().unwrap();
if outbuf.pts() + outbuf.duration() >= 2 * gst::SECOND {
if outbuf.pts().unwrap() + outbuf.duration().unwrap() >= 2 * ClockTime::SECOND {
break;
}
@ -341,19 +431,36 @@ fn test_one_timed_buffer_and_eos_roll_up2() {
assert_eq!(&*data, &[0x80, 0x80]);
}
let expected: [(gst::ClockTime, gst::ClockTime, [u8; 2usize]); 3] = [
(2_000_000_000.into(), 0.into(), [0x20, 0x57]), /* SPACE W */
(2_000_000_000.into(), 0.into(), [0xef, 0xf2]), /* o r */
(2_000_000_000.into(), 0.into(), [0xec, 0x64]), /* l d */
let expected: [(ClockTime, ClockTime, [u8; 2usize]); 3] = [
(
ClockTime::from_nseconds(2_000_000_000),
ClockTime::ZERO,
[0x20, 0x57],
), /* SPACE W */
(
ClockTime::from_nseconds(2_000_000_000),
ClockTime::ZERO,
[0xef, 0xf2],
), /* o r */
(
ClockTime::from_nseconds(2_000_000_000),
ClockTime::ZERO,
[0xec, 0x64],
), /* l d */
];
for (i, e) in expected.iter().enumerate() {
let outbuf = h.try_pull().unwrap();
assert_eq!(e.0, outbuf.pts(), "Unexpected PTS for {}th buffer", i + 1);
assert_eq!(
e.0,
outbuf.pts().unwrap(),
"Unexpected PTS for {}th buffer",
i + 1
);
assert_eq!(
e.1,
outbuf.duration(),
outbuf.duration().unwrap(),
"Unexpected duration for {}th buffer",
i + 1
);
@ -387,13 +494,13 @@ fn test_word_wrap_roll_up() {
let _event = h.pull_event().unwrap();
}
let inbuf = new_timed_buffer(&"Hello World", gst::SECOND, gst::SECOND);
let inbuf = new_timed_buffer(&"Hello World", ClockTime::SECOND, ClockTime::SECOND);
assert_eq!(h.push(inbuf), Ok(gst::FlowSuccess::Ok));
/* Padding */
loop {
let outbuf = h.pull().unwrap();
if outbuf.pts() + outbuf.duration() >= gst::SECOND {
if outbuf.pts().unwrap() + outbuf.duration().unwrap() >= ClockTime::SECOND {
break;
}
@ -401,27 +508,76 @@ fn test_word_wrap_roll_up() {
assert_eq!(&*data, &[0x80, 0x80]);
}
let expected: [(gst::ClockTime, gst::ClockTime, [u8; 2usize]); 11] = [
(1_000_000_000.into(), 33_333_333.into(), [0x94, 0x25]), /* roll_up_2 */
(1_033_333_333.into(), 33_333_334.into(), [0x94, 0x7c]), /* preamble */
(1_066_666_667.into(), 33_333_333.into(), [0xc8, 0xe5]), /* H e */
(1_100_000_000.into(), 33_333_333.into(), [0xec, 0xec]), /* l l */
(1_133_333_333.into(), 33_333_334.into(), [0xef, 0x20]), /* o SPACE */
(1_166_666_667.into(), 33_333_333.into(), [0x94, 0xad]), /* carriage return */
(1_200_000_000.into(), 33_333_333.into(), [0x94, 0x25]), /* roll_up_2 */
(1_233_333_333.into(), 33_333_334.into(), [0x94, 0x7c]), /* preamble */
(1_266_666_667.into(), 33_333_333.into(), [0x57, 0xef]), /* W o */
(1_300_000_000.into(), 33_333_333.into(), [0xf2, 0xec]), /* r l */
(1_333_333_333.into(), 33_333_334.into(), [0x64, 0x80]), /* d nil */
let expected: [(ClockTime, ClockTime, [u8; 2usize]); 11] = [
(
ClockTime::from_nseconds(1_000_000_000),
ClockTime::from_nseconds(33_333_333),
[0x94, 0x25],
), /* roll_up_2 */
(
ClockTime::from_nseconds(1_033_333_333),
ClockTime::from_nseconds(33_333_334),
[0x94, 0x7c],
), /* preamble */
(
ClockTime::from_nseconds(1_066_666_667),
ClockTime::from_nseconds(33_333_333),
[0xc8, 0xe5],
), /* H e */
(
ClockTime::from_nseconds(1_100_000_000),
ClockTime::from_nseconds(33_333_333),
[0xec, 0xec],
), /* l l */
(
ClockTime::from_nseconds(1_133_333_333),
ClockTime::from_nseconds(33_333_334),
[0xef, 0x20],
), /* o SPACE */
(
ClockTime::from_nseconds(1_166_666_667),
ClockTime::from_nseconds(33_333_333),
[0x94, 0xad],
), /* carriage return */
(
ClockTime::from_nseconds(1_200_000_000),
ClockTime::from_nseconds(33_333_333),
[0x94, 0x25],
), /* roll_up_2 */
(
ClockTime::from_nseconds(1_233_333_333),
ClockTime::from_nseconds(33_333_334),
[0x94, 0x7c],
), /* preamble */
(
ClockTime::from_nseconds(1_266_666_667),
ClockTime::from_nseconds(33_333_333),
[0x57, 0xef],
), /* W o */
(
ClockTime::from_nseconds(1_300_000_000),
ClockTime::from_nseconds(33_333_333),
[0xf2, 0xec],
), /* r l */
(
ClockTime::from_nseconds(1_333_333_333),
ClockTime::from_nseconds(33_333_334),
[0x64, 0x80],
), /* d nil */
];
for (i, e) in expected.iter().enumerate() {
let outbuf = h.try_pull().unwrap();
assert_eq!(e.0, outbuf.pts(), "Unexpected PTS for {}th buffer", i + 1);
assert_eq!(
e.0,
outbuf.pts().unwrap(),
"Unexpected PTS for {}th buffer",
i + 1
);
assert_eq!(
e.1,
outbuf.duration(),
outbuf.duration().unwrap(),
"Unexpected duration for {}th buffer",
i + 1
);

View file

@ -138,8 +138,8 @@ impl Dav1dDec {
frame: &gst_video::VideoCodecFrame,
) -> Result<Vec<(dav1d::Picture, gst_video::VideoFormat)>, gst::FlowError> {
let mut decoder = self.decoder.lock().unwrap();
let timestamp = frame.dts().0.map(|ts| ts as i64);
let duration = frame.duration().0.map(|d| d as i64);
let timestamp = frame.dts().map(|ts| *ts as i64);
let duration = frame.duration().map(|d| *d as i64);
let frame_number = Some(frame.system_frame_number() as i64);
let input_data = input_buffer

View file

@ -72,7 +72,7 @@ struct StreamingState {
video: Option<VideoFormat>,
expect_video: bool,
got_all_streams: bool,
last_position: gst::ClockTime,
last_position: Option<gst::ClockTime>,
metadata: Option<Metadata>,
@ -103,7 +103,7 @@ struct VideoFormat {
#[derive(Debug, PartialEq, Eq, Clone, Default)]
struct Metadata {
duration: gst::ClockTime,
duration: Option<gst::ClockTime>,
creation_date: Option<String>,
creator: Option<String>,
@ -683,7 +683,7 @@ impl StreamingState {
video: None,
expect_video: video,
got_all_streams: false,
last_position: gst::CLOCK_TIME_NONE,
last_position: gst::ClockTime::NONE,
metadata: None,
aac_sequence_header: None,
avc_sequence_header: None,
@ -1197,12 +1197,16 @@ impl StreamingState {
}
fn update_position(&mut self, buffer: &gst::Buffer) {
if buffer.pts().is_some() {
let pts = buffer.pts();
self.last_position = self.last_position.max(pts).unwrap_or(pts);
} else if buffer.dts().is_some() {
let dts = buffer.dts();
self.last_position = self.last_position.max(dts).unwrap_or(dts);
if let Some(pts) = buffer.pts() {
self.last_position = self
.last_position
.map(|last_pos| last_pos.max(pts))
.or(Some(pts));
} else if let Some(dts) = buffer.dts() {
self.last_position = self
.last_position
.map(|last_pos| last_pos.max(dts))
.or(Some(dts));
}
}
}
@ -1521,7 +1525,9 @@ impl Metadata {
for arg in args {
match (arg.name, &arg.data) {
("duration", &flavors::ScriptDataValue::Number(duration)) => {
metadata.duration = ((duration * 1000.0 * 1000.0 * 1000.0) as u64).into();
metadata.duration = Some(gst::ClockTime::from_nseconds(
(duration * 1000.0 * 1000.0 * 1000.0) as u64,
));
}
("creationdate", &flavors::ScriptDataValue::String(date)) => {
metadata.creation_date = Some(String::from(date));

View file

@ -30,7 +30,7 @@ fn main() {
.set_state(gst::State::Playing)
.expect("Failed to set pipeline state to playing");
for msg in bus.iter_timed(gst::CLOCK_TIME_NONE) {
for msg in bus.iter_timed(gst::ClockTime::NONE) {
use gst::MessageView;
match msg.view() {

View file

@ -84,7 +84,7 @@ struct State {
video_info: gst_video::VideoInfo,
cache: Arc<CacheBuffer>,
gif_pts: Option<gst::ClockTime>,
last_actual_pts: gst::ClockTime,
last_actual_pts: Option<gst::ClockTime>,
context: Option<gif::Encoder<CacheBufferWriter>>,
}
@ -94,14 +94,14 @@ impl State {
video_info,
cache: Arc::new(CacheBuffer::new()),
gif_pts: None,
last_actual_pts: gst::ClockTime::none(),
last_actual_pts: None,
context: None,
}
}
pub fn reset(&mut self, settings: Settings) {
self.cache.clear();
self.gif_pts = None;
self.last_actual_pts = gst::ClockTime::none();
self.last_actual_pts = None;
// initialize and configure encoder with a CacheBufferWriter pointing
// to our CacheBuffer instance
let mut encoder = gif::Encoder::new(
@ -321,20 +321,30 @@ impl VideoEncoderImpl for GifEnc {
// Calculate delay to new frame by calculating the difference between the current actual
// presentation timestamp of the last frame within the gif, and the pts of the new frame.
// This results in variable frame delays in the gif - but an overall constant fps.
state.last_actual_pts = in_frame.buffer().pts();
let pts = in_frame.buffer().pts();
state.last_actual_pts = pts;
if state.gif_pts.is_none() {
// First frame: use pts of first input frame as origin
state.gif_pts = Some(in_frame.buffer().pts());
state.gif_pts = pts;
}
let frame_delay = in_frame.buffer().pts() - state.gif_pts.unwrap();
if frame_delay.is_none() {
let pts = pts.ok_or_else(|| {
gst::element_error!(
element,
gst::CoreError::Failed,
["No PTS set on input frame. Unable to calculate proper frame timing."]
);
return Err(gst::FlowError::Error);
}
gst::FlowError::Error
})?;
let frame_delay = pts
.checked_sub(state.gif_pts.expect("checked above"))
.ok_or_else(|| {
gst::element_error!(
element,
gst::CoreError::Failed,
["Input frame PTS is greater than gif_pts. Unable to calculate proper frame timing."]
);
gst::FlowError::Error
})?;
let mut raw_frame = tightly_packed_framebuffer(&in_frame);
let mut gif_frame = match in_frame.info().format() {
@ -361,10 +371,10 @@ impl VideoEncoderImpl for GifEnc {
// use float arithmetic with rounding for this calculation, since small stuttering
// is probably less visible than the large stuttering when a complete 10ms have to
// "catch up".
gif_frame.delay = (frame_delay.mseconds().unwrap() as f32 / 10.0).round() as u16;
state.gif_pts.replace(
state.gif_pts.unwrap() + gst::ClockTime::from_mseconds(gif_frame.delay as u64 * 10),
);
gif_frame.delay = (frame_delay.mseconds() as f32 / 10.0).round() as u16;
state.gif_pts = state.gif_pts.map(|gif_pts| {
gif_pts + gst::ClockTime::from_mseconds(gif_frame.delay as u64 * 10)
});
// encode new frame
let context = state.context.as_mut().unwrap();

View file

@ -8,7 +8,7 @@
use gst::prelude::*;
const ENCODE_PIPELINE: &str = "videotestsrc is-live=false num-buffers=1 ! videoconvert ! video/x-raw, format=RGB, width=160, height=120 !
const ENCODE_PIPELINE: &str = "videotestsrc is-live=false num-buffers=1 ! videoconvert ! video/x-raw, format=RGB, width=160, height=120 !
rspngenc compression-level=2 filter=4 ! filesink location=frame.png";
fn main() {
@ -22,7 +22,7 @@ fn main() {
.set_state(gst::State::Playing)
.expect("Failed to set pipeline state to playing");
for msg in bus.iter_timed(gst::CLOCK_TIME_NONE) {
for msg in bus.iter_timed(gst::ClockTime::NONE) {
use gst::MessageView;
match msg.view() {

View file

@ -169,7 +169,7 @@ impl WebPDec {
}
fn decode(&self, _element: &super::WebPDec) -> Result<(), gst::ErrorMessage> {
let mut prev_timestamp: gst::ClockTime = 0.into();
let mut prev_timestamp: Option<gst::ClockTime> = Some(gst::ClockTime::ZERO);
let mut state = self.state.lock().unwrap();
if state.buffers.is_empty() {
@ -223,8 +223,9 @@ impl WebPDec {
gst::error_msg!(gst::StreamError::Decode, ["Failed to get next frame"])
})?;
let timestamp = frame.timestamp as u64 * gst::MSECOND;
let duration = timestamp - prev_timestamp;
let timestamp = frame.timestamp as u64 * gst::ClockTime::MSECOND;
let duration =
prev_timestamp.and_then(|prev_timestamp| timestamp.checked_sub(prev_timestamp));
let mut out_buf =
gst::Buffer::with_size((info.width * info.height * 4) as usize).unwrap();
@ -235,7 +236,7 @@ impl WebPDec {
out_buf_mut.set_duration(duration);
}
prev_timestamp = timestamp;
prev_timestamp = Some(timestamp);
match self.srcpad.push(out_buf) {
Ok(_) => (),

View file

@ -40,15 +40,17 @@ fn test_decode() {
assert_eq!(h.push(buf), Ok(gst::FlowSuccess::Ok));
h.push_event(gst::event::Eos::new());
let mut expected_timestamp: gst::ClockTime = 0.into();
let mut expected_timestamp: Option<gst::ClockTime> = Some(gst::ClockTime::ZERO);
let mut count = 0;
let expected_duration: gst::ClockTime = 40_000_000.into();
let expected_duration: Option<gst::ClockTime> = Some(gst::ClockTime::from_nseconds(40_000_000));
while let Some(buf) = h.try_pull() {
assert_eq!(buf.pts(), expected_timestamp);
assert_eq!(buf.duration(), expected_duration);
expected_timestamp += expected_duration;
expected_timestamp = expected_timestamp
.zip(expected_duration)
.map(|(ts, duration)| ts + duration);
count += 1;
}