// // Copyright (C) 2023 Sebastian Dröge // // 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 // . // // SPDX-License-Identifier: MPL-2.0 use atomic_refcell::AtomicRefCell; use gst::{glib, prelude::*, subclass::prelude::*}; use gst_rtp::prelude::*; use once_cell::sync::Lazy; use std::{ collections::{BTreeMap, VecDeque}, ops::{Bound, RangeBounds}, sync::Mutex, }; use super::{PacketToBufferRelation, TimestampOffset}; static CAT: Lazy = Lazy::new(|| { gst::DebugCategory::new( "rtpbasedepay2", gst::DebugColorFlags::empty(), Some("RTP Base Depayloader 2"), ) }); #[derive(Clone, Debug)] struct Settings { max_reorder: u32, source_info: bool, auto_header_extensions: bool, } impl Default for Settings { fn default() -> Self { Self { max_reorder: 100, source_info: false, auto_header_extensions: true, } } } /// Metadata of a pending packet for tracking purposes. struct PendingPacket { /// Extended sequence number of the packet. ext_seqnum: u64, /// Extended RTP timestamp of the packet. ext_timestamp: u64, /// SSRC of the packet. ssrc: u32, /// CSRC of the packet. csrc: [u32; rtp_types::RtpPacket::MAX_N_CSRCS], n_csrc: u8, /// Packet contains header extensions. have_header_extensions: bool, /// Buffer containing the packet. buffer: gst::Buffer, } /// A pending buffer that still has to be sent downstream. struct PendingBuffer { /// If no metadata is set then this buffer must be pushed together with /// the next buffer where it is actually known, or at EOS with the /// last known one. metadata_set: bool, buffer: gst::Buffer, } /// Information about the current stream that is handled. struct CurrentStream { /// SSRC of the last received packet. ssrc: u32, /// Payload type of the last received packet. pt: u8, /// Extended sequence number of the last received packet. ext_seqnum: u64, /// Extended RTP time of the last received packet. ext_rtptime: u64, } struct State { segment: Option<(gst::Seqnum, gst::FormattedSegment)>, /// Set when a new segment event should be sent downstream before the next buffer. pending_segment: bool, sink_caps: Option, src_caps: Option, /// Set when the sinkpad caps are known. clock_rate: Option, // NPT start from old-style RTSP caps. npt_start: Option, // NPT stop from old-style RTSP caps. npt_stop: Option, // Play scale from old-style RTSP caps. play_speed: f64, // Play speed from old-style RTSP caps. play_scale: f64, // Clock base from old-style RTSP caps. clock_base: Option, // Initial PTS / ext_rtptime after a caps event where NPT start was set. npt_start_times: Option<(Option, u64)>, /// Set when the next outgoing buffer should have the discont flag set. discont_pending: bool, /// Current stream configuration. Set on first packet and whenever it changes. current_stream: Option, /// Last extended seqnum that was used for a buffer. last_used_ext_seqnum: Option, /// PTS of the last outgoing buffer. last_pts: Option, /// DTS of the last outgoing buffer. last_dts: Option, /// Pending input packets that were not completely used up for outgoing buffers yet. pending_packets: VecDeque, /// Pending buffers that have to be pushed downstream. Some of them might not have a PTS yet, /// i.e. were created without corresponding seqnums. pending_buffers: VecDeque, } impl Default for State { fn default() -> Self { Self { segment: None, pending_segment: false, sink_caps: None, src_caps: None, clock_rate: None, npt_start: None, npt_stop: None, play_scale: 1.0, play_speed: 1.0, clock_base: None, npt_start_times: None, discont_pending: true, current_stream: None, last_used_ext_seqnum: None, last_pts: None, last_dts: None, pending_packets: VecDeque::new(), pending_buffers: VecDeque::new(), } } } #[derive(Clone, Debug)] struct Stats { ssrc: u32, clock_rate: u32, running_time_dts: Option, running_time_pts: Option, seqnum: u16, timestamp: u32, npt_start: Option, npt_stop: Option, play_speed: f64, play_scale: f64, } pub struct RtpBaseDepay2 { sink_pad: gst::Pad, src_pad: gst::Pad, state: AtomicRefCell, settings: Mutex, stats: Mutex>, extensions: Mutex>, } /// Wrapper around `gst::Caps` that implements `Ord` around the structure name. struct CapsOrd(gst::Caps); impl CapsOrd { fn new(caps: gst::Caps) -> Self { assert!(caps.is_fixed()); CapsOrd(caps) } } impl PartialEq for CapsOrd { fn eq(&self, other: &Self) -> bool { self.cmp(other) == std::cmp::Ordering::Equal } } impl Eq for CapsOrd {} impl PartialOrd for CapsOrd { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl Ord for CapsOrd { fn cmp(&self, other: &Self) -> std::cmp::Ordering { self.0 .structure(0) .unwrap() .name() .cmp(other.0.structure(0).unwrap().name()) } } /// Wrappers for public methods and associated helper functions. #[allow(dead_code)] impl RtpBaseDepay2 { pub(super) fn set_src_caps(&self, src_caps: &gst::Caps) { gst::debug!(CAT, imp: self, "Setting caps {src_caps:?}"); let mut state = self.state.borrow_mut(); if Some(src_caps) == state.src_caps.as_ref() { gst::debug!(CAT, imp: self, "Setting same caps {src_caps:?} again"); return; } let seqnum = if let Some((seqnum, _)) = state.segment { seqnum } else { gst::Seqnum::next() }; state.src_caps = Some(src_caps.clone()); let segment_event = self.retrieve_pending_segment_event(&mut state); drop(state); // Rely on the actual flow return later when pushing a buffer downstream. The bool // return from pushing the event does not allow to distinguish between different kinds of // errors. let _ = self .src_pad .push_event(gst::event::Caps::builder(src_caps).seqnum(seqnum).build()); if let Some(segment_event) = segment_event { let _ = self.src_pad.push_event(segment_event); } } pub(super) fn drop_packets(&self, ext_seqnum: impl RangeBounds) { gst::trace!(CAT, imp: self, "Dropping packets up to ext seqnum {:?}", ext_seqnum.end_bound()); let mut state = self.state.borrow_mut(); state.discont_pending = true; let end = match ext_seqnum.end_bound() { Bound::Included(end) => *end, Bound::Excluded(end) if *end == 0 => return, Bound::Excluded(end) => *end - 1, Bound::Unbounded => { state.pending_buffers.clear(); return; } }; if let Some(back) = state.pending_packets.back() { if back.ext_seqnum <= end { state.pending_packets.clear(); return; } } else { return; } while state .pending_packets .front() .map_or(false, |p| p.ext_seqnum <= end) { let _ = state.pending_packets.pop_front(); } } fn retrieve_pts_dts( &self, state: &State, ext_seqnum: u64, ) -> (Option, Option) { let mut pts = None; let mut dts = None; for front in state .pending_packets .iter() .take_while(|p| p.ext_seqnum <= ext_seqnum) { // Remember first PTS/DTS if pts.is_none() || dts.is_none() { pts = front.buffer.pts(); dts = front.buffer.dts(); } if pts.is_some() || dts.is_some() { break; } } (pts, dts) } fn copy_metas( &self, settings: &Settings, state: &mut State, ext_seqnum: u64, buffer: &mut gst::BufferRef, ) -> bool { let mut extension_wants_caps_update = false; let extensions = self.extensions.lock().unwrap(); let mut ssrc = None; let mut csrc = [0u32; rtp_types::RtpPacket::MAX_N_CSRCS]; let mut n_csrc = 0; // Copy over metas and other metadata from the packets that made up this buffer let obj = self.obj(); for front in state .pending_packets .iter() .take_while(|p| p.ext_seqnum <= ext_seqnum) { // Filter out reference timestamp metas that have the same timestamps let mut reference_timestamp_metas = BTreeMap::new(); front.buffer.foreach_meta(|meta| { use std::ops::ControlFlow::*; // Do not copy metas that are memory specific if meta.has_tag::() || meta.has_tag::() { return Continue(()); } // Actual filtering of reference timestamp metas with same timestamp if let Some(meta) = meta.downcast_ref::() { let mut same = false; reference_timestamp_metas .entry(CapsOrd::new(meta.reference_owned())) .and_modify(|timestamp| { same = *timestamp == meta.timestamp(); *timestamp = meta.timestamp(); }) .or_insert(meta.timestamp()); if same { return Continue(()); } } (obj.class().as_ref().transform_meta)(&obj, &front.buffer, &meta, buffer); Continue(()) }); if !extensions.is_empty() && front.have_header_extensions { let map = front.buffer.map_readable().unwrap(); let packet = rtp_types::RtpPacket::parse(&map).unwrap(); let (extension_pattern, mut extensions_data) = packet.extension().unwrap(); let extension_flags = match extension_pattern { 0xBEDE => gst_rtp::RTPHeaderExtensionFlags::ONE_BYTE, x if x >> 4 == 0x100 => gst_rtp::RTPHeaderExtensionFlags::TWO_BYTE, _ => { gst::trace!(CAT, imp: self, "Unknown extension pattern {extension_pattern:04X}"); continue; } }; while !extensions_data.is_empty() { let (id, len) = if extension_flags == gst_rtp::RTPHeaderExtensionFlags::ONE_BYTE { let b = extensions_data[0]; extensions_data = &extensions_data[1..]; let id = b >> 4; let len = (b & 0x0f) + 1; // Padding if id == 0 { continue; } if id == 15 { // Special ID break; } (id, len as usize) } else { let id = extensions_data[0]; extensions_data = &extensions_data[1..]; // Padding if id == 0 { continue; } if extensions_data.is_empty() { break; } let len = extensions_data[1]; extensions_data = &extensions_data[1..]; (id, len as usize) }; if extensions_data.len() < len { break; } gst::trace!(CAT, imp: self, "Handling RTP header extension with id {id} and length {len}"); let (extension_data, remainder) = extensions_data.split_at(len); extensions_data = remainder; let Some(extension) = extensions.get(&id) else { continue; }; if !extension.read(extension_flags, extension_data, buffer) { gst::warning!(CAT, imp: self, "Failed reading RTP header extension with id {id} and length {len}"); continue; } extension_wants_caps_update |= extension.wants_update_non_rtp_src_caps(); } } if settings.source_info { if ssrc.is_none() { ssrc = Some(front.ssrc); } for c in &front.csrc[..front.n_csrc as usize] { if !csrc[..n_csrc].contains(c) && n_csrc < rtp_types::RtpPacket::MAX_N_CSRCS { csrc[n_csrc] = *c; n_csrc += 1; } } } } if settings.source_info && ssrc.is_some() { gst_rtp::RTPSourceMeta::add(buffer, ssrc, &csrc[..n_csrc]); } extension_wants_caps_update } pub(super) fn queue_buffer( &self, packet_to_buffer_relation: PacketToBufferRelation, mut buffer: gst::Buffer, ) -> Result { let settings = self.settings.lock().unwrap().clone(); { // Reset some buffer metadata, it shouldn't have been set let buffer_ref = buffer.make_mut(); buffer_ref.set_pts(None); buffer_ref.set_dts(None); } gst::trace!(CAT, imp: self, "Queueing buffer {buffer:?} for seqnum range {packet_to_buffer_relation:?}"); let mut state = self.state.borrow_mut(); if state.src_caps.is_none() { gst::error!(CAT, imp: self, "No source pad caps negotiated yet"); return Err(gst::FlowError::NotNegotiated); } if matches!(packet_to_buffer_relation, PacketToBufferRelation::OutOfBand) { gst::trace!(CAT, imp: self, "Keeping buffer without associated seqnums until next buffer or EOS"); state.pending_buffers.push_back(PendingBuffer { metadata_set: false, buffer, }); return Ok(gst::FlowSuccess::Ok); }; let (seqnums, timestamp_offset) = match packet_to_buffer_relation { PacketToBufferRelation::Seqnums(seqnums) => (seqnums.clone(), None), PacketToBufferRelation::SeqnumsWithOffset { seqnums: ids, timestamp_offset, } => (ids.clone(), Some(timestamp_offset)), PacketToBufferRelation::OutOfBand => unreachable!(), }; if seqnums.is_empty() { gst::error!(CAT, imp: self, "Empty packet ext seqnum range provided"); return Err(gst::FlowError::Error); } let seqnum_start = *seqnums.start(); let seqnum_end = *seqnums.end(); // Drop all older seqnums while state .pending_packets .front() .map_or(false, |p| p.ext_seqnum < seqnum_start) { let p = state.pending_packets.pop_front().unwrap(); gst::trace!(CAT, imp: self, "Dropping packet with extended seqnum {}", p.ext_seqnum); } if state.pending_packets.is_empty() { gst::error!(CAT, imp: self, "Queueing buffers for future ext seqnums not allowed"); return Err(gst::FlowError::Error); }; if seqnum_end > state.pending_packets.back().unwrap().ext_seqnum { gst::error!(CAT, imp: self, "Queueing buffers for future ext seqnums not allowed"); return Err(gst::FlowError::Error); } let mut discont_pending = state.discont_pending; state.discont_pending = false; let mut pts = state .pending_packets .iter() .take_while(|p| p.ext_seqnum <= seqnum_end) .find_map(|p| p.buffer.pts()); let mut dts = None; if let Some(timestamp_offset) = timestamp_offset { match timestamp_offset { TimestampOffset::Pts(pts_offset) => { if let Some(ts) = pts { let pts_signed = ts.into_positive() + pts_offset; if let Some(ts) = pts_signed.positive() { pts = Some(ts); } else { gst::warning!(CAT, imp: self, "Negative PTS {} calculated, not supported", pts_signed); } } } TimestampOffset::PtsAndDts(pts_offset, dts_offset) => { if let Some(ts) = pts { let pts_signed = ts.into_positive() + pts_offset; if let Some(ts) = pts_signed.positive() { pts = Some(ts); } else { gst::warning!(CAT, imp: self, "Negative PTS {} calculated, not supported", pts_signed); } } if let Some(pts) = pts { let dts_signed = pts.into_positive() + dts_offset; if let Some(ts) = dts_signed.positive() { dts = Some(ts); } else { gst::warning!(CAT, imp: self, "Negative DTS {} calculated, not supported", dts_signed); } } } } } else if state .last_used_ext_seqnum .map_or(false, |last_used_ext_seqnum| { seqnum_end <= last_used_ext_seqnum }) { // If we have no timestamp offset and this is not the first time a buffer for this // packet is queued then unset the PTS. It's not going to be correct. pts = None; } // Decorate all previously queued buffers metadata for buffer in state .pending_buffers .iter_mut() .skip_while(|b| b.metadata_set) { assert!(!buffer.metadata_set); buffer.metadata_set = true; let buffer = &mut buffer.buffer; let buffer_ref = buffer.make_mut(); buffer_ref.set_pts(pts); buffer_ref.set_dts(dts); if discont_pending { buffer_ref.set_flags(gst::BufferFlags::DISCONT); discont_pending = false; } } // Decorate the new buffer { let buffer_ref = buffer.make_mut(); buffer_ref.set_pts(pts); buffer_ref.set_dts(dts); let needs_caps_update = self.copy_metas(&settings, &mut state, seqnum_end, buffer_ref); if discont_pending { buffer_ref.set_flags(gst::BufferFlags::DISCONT); } // If a caps update is needed then let's do that *before* outputting this buffer. if needs_caps_update { let old_src_caps = state.src_caps.as_ref().unwrap(); let mut src_caps = old_src_caps.copy(); { let src_caps = src_caps.get_mut().unwrap(); let extensions = self.extensions.lock().unwrap(); for extension in extensions.values() { if !extension.update_non_rtp_src_caps(src_caps) { gst::error!(CAT, imp: self, "RTP header extension {} could not update caps", extension.name()); return Err(gst::FlowError::Error); } } } if &src_caps != old_src_caps { let seqnum = state.segment.as_ref().unwrap().0; drop(state); self.finish_pending_buffers()?; let _ = self .src_pad .push_event(gst::event::Caps::builder(&src_caps).seqnum(seqnum).build()); state = self.state.borrow_mut(); state.src_caps = Some(src_caps); } } } state.pending_buffers.push_back(PendingBuffer { metadata_set: true, buffer, }); state.last_used_ext_seqnum = Some(seqnum_end); if pts.is_some() { state.last_pts = pts; state.last_dts = dts; } Ok(gst::FlowSuccess::Ok) } pub(super) fn finish_pending_buffers(&self) -> Result { let mut state = self.state.borrow_mut(); // As long as there are buffers that can be finished, take all with the same PTS (or no // PTS) and put them into a buffer list. while state.pending_buffers.iter().any(|b| b.metadata_set) { let pts = state.pending_buffers.front().unwrap().buffer.pts(); // First count number of pending packets so we can allocate a buffer list with the correct // size instead of re-allocating for every added buffer, or simply push a single buffer // downstream instead of allocating a buffer list just for one buffer. let num_buffers = state .pending_buffers .iter() .take_while(|b| { b.metadata_set && (b.buffer.pts() == pts || b.buffer.pts().is_none()) }) .count(); assert_ne!(num_buffers, 0); gst::trace!(CAT, imp: self, "Flushing {num_buffers} buffers"); let segment_event = self.retrieve_pending_segment_event(&mut state); enum BufferOrList { Buffer(gst::Buffer), List(gst::BufferList), } let buffers = if num_buffers == 1 { let buffer = state.pending_buffers.pop_front().unwrap().buffer; gst::trace!(CAT, imp: self, "Finishing buffer {buffer:?}"); BufferOrList::Buffer(buffer) } else { let mut list = gst::BufferList::new_sized(num_buffers); { let list = list.get_mut().unwrap(); while state.pending_buffers.front().map_or(false, |b| { b.metadata_set && (b.buffer.pts() == pts || b.buffer.pts().is_none()) }) { let buffer = state.pending_buffers.pop_front().unwrap().buffer; gst::trace!(CAT, imp: self, "Finishing buffer {buffer:?}"); list.add(buffer); } } BufferOrList::List(list) }; drop(state); if let Some(segment_event) = segment_event { let _ = self.src_pad.push_event(segment_event); } let res = match buffers { BufferOrList::Buffer(buffer) => self.src_pad.push(buffer), BufferOrList::List(list) => self.src_pad.push_list(list), }; if let Err(err) = res { if ![gst::FlowError::Flushing, gst::FlowError::Eos].contains(&err) { gst::warning!(CAT, imp: self, "Failed pushing buffers: {err:?}"); } else { gst::debug!(CAT, imp: self, "Failed pushing buffers: {err:?}"); } } state = self.state.borrow_mut(); } Ok(gst::FlowSuccess::Ok) } pub(super) fn sink_pad(&self) -> &gst::Pad { &self.sink_pad } pub(super) fn src_pad(&self) -> &gst::Pad { &self.src_pad } } /// Default virtual method implementations. impl RtpBaseDepay2 { fn start_default(&self) -> Result<(), gst::ErrorMessage> { Ok(()) } fn stop_default(&self) -> Result<(), gst::ErrorMessage> { Ok(()) } fn set_sink_caps_default(&self, _caps: &gst::Caps) -> bool { true } fn handle_packet_default( &self, _packet: &super::Packet, ) -> Result { unimplemented!() } fn drain_default(&self) -> Result { Ok(gst::FlowSuccess::Ok) } fn flush_default(&self) {} fn sink_event_default(&self, event: gst::Event) -> Result { match event.view() { gst::EventView::Caps(event) => { let caps = event.caps_owned(); return self.set_sink_caps(caps); } gst::EventView::Segment(event) => { let segment = event.segment(); let seqnum = event.seqnum(); let state = self.state.borrow(); if state.sink_caps.is_none() { gst::warning!(CAT, imp: self, "Received segment before caps"); return Err(gst::FlowError::NotNegotiated); } let same_segment = state .segment .as_ref() .map(|(old_seqnum, old_segment)| { (&seqnum, segment) == (old_seqnum, old_segment.upcast_ref()) }) .unwrap_or(false); // Only drain if the segment changed and we actually had one before let drain = !same_segment && state.segment.is_some(); drop(state); if !same_segment { gst::debug!(CAT, imp: self, "Received segment {segment:?}"); if drain { if let Err(err) = self.drain() { // Just continue here. The depayloader is now flushed and can proceed below, // and if there was a serious error downstream it will happen on the next // buffer pushed downstream gst::debug!(CAT, imp: self, "Draining failed: {err:?}"); } } let mut state = self.state.borrow_mut(); if let Some(segment) = segment.downcast_ref::() { state.segment = Some((event.seqnum(), segment.clone())); state.pending_segment = true; } else { gst::error!(CAT, imp: self, "Segments in non-TIME format are not supported"); state.segment = None; // FIXME: Forget everything here? return Err(gst::FlowError::Error); } return Ok(gst::FlowSuccess::Ok); } } gst::EventView::Eos(_) => { if let Err(err) = self.drain() { gst::debug!(CAT, imp: self, "Draining on EOS failed: {err:?}"); } } gst::EventView::FlushStop(_) => { self.flush(); let mut state = self.state.borrow_mut(); state.segment = None; state.pending_segment = false; state.current_stream = None; state.last_pts = None; state.last_dts = None; drop(state); *self.stats.lock().unwrap() = None; } _ => (), } gst::debug!(CAT, imp: self, "Forwarding event: {event:?}"); if self.src_pad.push_event(event) { Ok(gst::FlowSuccess::Ok) } else if self.src_pad.pad_flags().contains(gst::PadFlags::FLUSHING) { Err(gst::FlowError::Flushing) } else { Err(gst::FlowError::Error) } } fn src_event_default(&self, event: gst::Event) -> Result { if gst::Pad::event_default(&self.src_pad, Some(&*self.obj()), event) { Ok(gst::FlowSuccess::Ok) } else if self.sink_pad.pad_flags().contains(gst::PadFlags::FLUSHING) { Err(gst::FlowError::Flushing) } else { Err(gst::FlowError::Error) } } fn sink_query_default(&self, query: &mut gst::QueryRef) -> bool { gst::Pad::query_default(&self.sink_pad, Some(&*self.obj()), query) } fn src_query_default(&self, query: &mut gst::QueryRef) -> bool { gst::Pad::query_default(&self.src_pad, Some(&*self.obj()), query) } fn transform_meta_default( &self, _in_buf: &gst::BufferRef, meta: &gst::MetaRef, out_buf: &mut gst::BufferRef, ) { let tags = meta.tags(); if tags.len() > 1 { gst::trace!( CAT, imp: self, "Not copying meta {}: has multiple tags {tags:?}", meta.api(), ); return; } let allowed_tags = self.obj().class().as_ref().allowed_meta_tags; if tags.len() == 1 { let meta_tag = &tags[0]; if !allowed_tags.iter().copied().any(|tag| tag == *meta_tag) { gst::trace!( CAT, imp: self, "Not copying meta {}: tag '{meta_tag}' not allowed", meta.api(), ); return; } } gst::trace!(CAT, imp: self, "Copying meta {}", meta.api()); if let Err(err) = meta.transform(out_buf, &gst::meta::MetaTransformCopy::new(false, ..)) { gst::trace!(CAT, imp: self, "Could not copy meta {}: {err}", meta.api()); } } fn add_extension(&self, ext: &gst_rtp::RTPHeaderExtension) { assert_ne!(ext.id(), 0); let mut extensions = self.extensions.lock().unwrap(); extensions.insert(ext.id() as u8, ext.clone()); // FIXME: Needs caps update! drop(extensions); self.obj().notify("extensions"); } fn clear_extensions(&self) { let mut extensions = self.extensions.lock().unwrap(); extensions.clear(); // FIXME: Needs caps update! drop(extensions); self.obj().notify("extensions"); } fn request_extension(&self, ext_id: u32, uri: &str) -> Option { let settings = self.settings.lock().unwrap(); if !settings.auto_header_extensions { return None; } drop(settings); let Some(ext) = gst_rtp::RTPHeaderExtension::create_from_uri(uri) else { gst::debug!( CAT, imp: self, "Didn't find any extension implementing URI {uri}", ); return None; }; gst::debug!( CAT, imp: self, "Automatically enabling extension {} for URI {uri}", ext.name(), ); ext.set_id(ext_id); Some(ext) } } /// Pad functions and associated helper functions. impl RtpBaseDepay2 { fn sink_chain( &self, _pad: &gst::Pad, buffer: gst::Buffer, ) -> Result { gst::trace!(CAT, imp: self, "Received buffer {buffer:?}"); let settings = self.settings.lock().unwrap().clone(); self.handle_buffer(&settings, buffer) } fn sink_chain_list( &self, _pad: &gst::Pad, list: gst::BufferList, ) -> Result { gst::trace!(CAT, imp: self, "Received buffer list {list:?}"); let settings = self.settings.lock().unwrap().clone(); for buffer in list.iter_owned() { self.handle_buffer(&settings, buffer)?; } Ok(gst::FlowSuccess::Ok) } fn sink_event( &self, pad: &gst::Pad, event: gst::Event, ) -> Result { gst::debug!(CAT, obj: pad, "Received event: {event:?}"); let obj = self.obj(); (obj.class().as_ref().sink_event)(&obj, event) } fn src_event( &self, pad: &gst::Pad, event: gst::Event, ) -> Result { gst::debug!(CAT, obj: pad, "Received event: {event:?}"); let obj = self.obj(); (obj.class().as_ref().src_event)(&obj, event) } fn sink_query(&self, pad: &gst::Pad, query: &mut gst::QueryRef) -> bool { gst::trace!(CAT, obj: pad, "Received query: {query:?}"); let obj = self.obj(); (obj.class().as_ref().sink_query)(&obj, query) } fn src_query(&self, pad: &gst::Pad, query: &mut gst::QueryRef) -> bool { gst::trace!(CAT, obj: pad, "Received query: {query:?}"); let obj = self.obj(); (obj.class().as_ref().src_query)(&obj, query) } fn retrieve_pending_segment_event(&self, state: &mut State) -> Option { // If a segment event was pending, take it now and push it out later. if !state.pending_segment { return None; } state.src_caps.as_ref()?; let (seqnum, segment) = state.segment.as_ref().unwrap(); let mut segment = segment.clone(); // If npt-start is set, all the other values are also valid. if let Some(npt_start) = state.npt_start { let (mut npt_start_pts, npt_start_ext_rtptime) = state.npt_start_times?; let clock_rate = state.clock_rate.unwrap(); let mut start = segment.start().unwrap(); if let Some((clock_base, pts)) = Option::zip(state.clock_base, npt_start_pts) { let ext_clock_base = clock_base as u64 + (1 << 32); let gap = npt_start_ext_rtptime .saturating_sub(ext_clock_base) .mul_div_floor(*gst::ClockTime::SECOND, clock_rate as u64) .map(gst::ClockTime::from_nseconds); // Account for lost packets if let Some(gap) = gap { if pts > gap { gst::debug!( CAT, imp: self, "Found gap of {}, adjusting start: {} = {} - {}", gap.display(), (pts - gap).display(), pts.display(), gap.display(), ); start = pts - gap; } } } let mut stop = segment.stop(); if let Some(npt_stop) = state.npt_stop { stop = Some(start + npt_stop.saturating_sub(npt_start)); } if npt_start_pts.is_none() { npt_start_pts = Some(start); } let running_time = segment.to_running_time(start); segment.reset(); segment.set_rate(state.play_speed); segment.set_applied_rate(state.play_scale); segment.set_start(start); segment.set_stop(stop); segment.set_time(npt_start); segment.set_position(npt_start_pts); segment.set_base(running_time); } let segment_event = gst::event::Segment::builder(&segment) .seqnum(*seqnum) .build(); state.pending_segment = false; gst::debug!( CAT, imp: self, "Created segment event {segment:?} with seqnum {seqnum:?}", ); Some(segment_event) } fn handle_buffer( &self, settings: &Settings, buffer: gst::Buffer, ) -> Result { let mut state = self.state.borrow_mut(); if state.sink_caps.is_none() { gst::error!(CAT, imp: self, "No sink pad caps"); gst::element_imp_error!( self, gst::CoreError::Negotiation, ("No RTP caps were negotiated"), [ "Input buffers need to have RTP caps set on them. This is usually \ achieved by setting the 'caps' property of the upstream source \ element (often udpsrc or appsrc), or by putting a capsfilter \ element before the depayloader and setting the 'caps' property \ on that. Also see https://gitlab.freedesktop.org/gstreamer/gstreamer/-/ \ blob/main/subprojects/gst-plugins-good/gst/rtp/README", ] ); return Err(gst::FlowError::NotNegotiated); } // Always set if the caps are set. let clock_rate = state.clock_rate.unwrap(); // Check for too many pending packets if let Some((front, back)) = Option::zip(state.pending_packets.front(), state.pending_packets.back()) { let rtp_diff = back .ext_timestamp .saturating_sub(front.ext_timestamp) .mul_div_floor(*gst::ClockTime::SECOND, clock_rate as u64) .map(gst::ClockTime::from_nseconds); let pts_diff = back.buffer.pts().opt_saturating_sub(front.buffer.pts()); if let Some(rtp_diff) = rtp_diff { if rtp_diff > gst::ClockTime::SECOND { gst::warning!(CAT, imp: self, "More than {rtp_diff} of RTP time queued, probably a bug in the subclass"); } } if let Some(pts_diff) = pts_diff { if pts_diff > gst::ClockTime::SECOND { gst::warning!(CAT, imp: self, "More than {pts_diff} of PTS time queued, probably a bug in the subclass"); } } } let buffer = match buffer.into_mapped_buffer_readable() { Ok(buffer) => buffer, Err(_) => { gst::error!(CAT, imp: self, "Failed to map buffer"); gst::element_imp_error!(self, gst::StreamError::Failed, ["Failed to map buffer"]); return Err(gst::FlowError::Error); } }; // Parse RTP packet and extract the relevant fields we will need later. let packet = match rtp_types::RtpPacket::parse(&buffer) { Ok(packet) => packet, Err(err) => { gst::warning!(CAT, imp: self, "Failed to parse RTP packet: {err:?}"); return Ok(gst::FlowSuccess::Ok); } }; let seqnum = packet.sequence_number(); let ssrc = packet.ssrc(); let pt = packet.payload_type(); let rtptime = packet.timestamp(); let marker = packet.marker_bit(); let have_header_extensions = packet.extension_len() != 0; let csrc = { let mut csrc = packet.csrc(); std::array::from_fn::(|_idx| { csrc.next().unwrap_or_default() }) }; let n_csrc = packet.n_csrcs(); let payload_range = { let payload = packet.payload(); if payload.is_empty() { 0..0 } else { let offset = packet.payload_offset(); offset..(offset + payload.len()) } }; // FIXME: Allow subclasses to decide not to flush on DISCONTs and handle the situation // themselves, e.g. to be able to continue reconstructing frames despite missing data. if buffer.buffer().flags().contains(gst::BufferFlags::DISCONT) { gst::info!(CAT, imp: self, "Discont received"); state.current_stream = None; } // Check if there was a stream discontinuity, e.g. based on the SSRC changing or the seqnum // having a gap compared to the previous packet. if let Some(ref mut current_stream) = state.current_stream { if current_stream.ssrc != ssrc { gst::info!(CAT, imp: self, "Stream SSRC changed from {:08x} to {:08x}", current_stream.ssrc, ssrc); state.current_stream = None; } else if current_stream.pt != pt { gst::info!(CAT, imp: self, "Stream payload type changed from {} to {}", current_stream.pt, pt); state.current_stream = None; } else { let expected_seqnum = (current_stream.ext_seqnum + 1) & 0xffff; if expected_seqnum != seqnum as u64 { gst::info!(CAT, imp: self, "Got seqnum {seqnum} but expected {expected_seqnum}"); let mut ext_seqnum = seqnum as u64 + (current_stream.ext_seqnum & !0xffff); if ext_seqnum < current_stream.ext_seqnum { let diff = current_stream.ext_seqnum - ext_seqnum; if diff > 0x7fff { ext_seqnum += 1 << 16; } } else { let diff = ext_seqnum - current_stream.ext_seqnum; if diff > 0x7fff { ext_seqnum -= 1 << 16; } } let diff = if ext_seqnum > current_stream.ext_seqnum { (ext_seqnum - current_stream.ext_seqnum) as i32 } else { -((current_stream.ext_seqnum - ext_seqnum) as i32) }; if diff > 0 { gst::info!(CAT, imp: self, "{diff} missing packets or sender restart"); state.current_stream = None; } else if diff >= -(settings.max_reorder as i32) { gst::info!(CAT, imp: self, "Got old packet, dropping"); return Ok(gst::FlowSuccess::Ok); } else { gst::info!(CAT, imp: self, "Sender restart"); state.current_stream = None; } } else { // Calculate extended RTP time let ext_rtptime = { let mut ext_rtptime = rtptime as u64 + (current_stream.ext_rtptime & !0xffff_ffff); // Check for wraparound if ext_rtptime < current_stream.ext_rtptime { let diff = current_stream.ext_rtptime - ext_rtptime; if diff > 0x7fff_ffff { ext_rtptime += 1 << 32; } } else { let diff = ext_rtptime - current_stream.ext_rtptime; if diff > 0x7fff_ffff { ext_rtptime -= 1 << 32; } } ext_rtptime }; current_stream.ext_seqnum += 1; current_stream.ext_rtptime = ext_rtptime; gst::trace!( CAT, imp: self, "Handling packet with extended seqnum {} and extended RTP time {}", current_stream.ext_seqnum, current_stream.ext_rtptime, ); } } } // Drain if there was any discontinuity. let discont = state.current_stream.is_none(); if discont && !state.pending_packets.is_empty() { drop(state); gst::info!(CAT, imp: self, "Got discontinuity, draining"); if let Err(err) = self.drain() { if ![gst::FlowError::Flushing, gst::FlowError::Eos].contains(&err) { gst::warning!(CAT, imp: self, "Error while draining: {err:?}"); } else { gst::debug!(CAT, imp: self, "Error while draining: {err:?}"); } return Err(err); } state = self.state.borrow_mut(); } // Update current stream state at this point. let ext_seqnum; let ext_rtptime; if let Some(ref current_stream) = state.current_stream { ext_seqnum = current_stream.ext_seqnum; ext_rtptime = current_stream.ext_rtptime; } else { ext_seqnum = seqnum as u64 + (1 << 16); ext_rtptime = rtptime as u64 + (1 << 32); gst::info!( CAT, imp: self, "Starting stream with SSRC {ssrc:08x} at extended seqnum {ext_seqnum} with extended RTP time {ext_rtptime}", ); state.current_stream = Some(CurrentStream { ssrc, pt, ext_seqnum, ext_rtptime, }) } // Remember the initial PTS/rtp_time mapping if old-style RTSP caps are used. if state.npt_start.is_some() && state.npt_start_times.is_none() { state.npt_start_times = Some((buffer.buffer().pts(), ext_rtptime)); } // If a segment event was pending, take it now and push it out later. let segment_event = self.retrieve_pending_segment_event(&mut state); // Remember current packet. state.pending_packets.push_back(PendingPacket { ext_seqnum, ext_timestamp: ext_rtptime, ssrc, csrc, n_csrc, have_header_extensions, buffer: buffer.buffer_owned(), }); // Remember if the next buffer that has to be pushed out should have the DISCONT flag set. state.discont_pending |= discont; // Update stats with the current packet. *self.stats.lock().unwrap() = { let (_seqnum, segment) = state.segment.as_ref().unwrap(); let running_time_pts = segment.to_running_time(buffer.buffer().pts()); let running_time_dts = segment.to_running_time(buffer.buffer().dts()); Some(Stats { ssrc, clock_rate, running_time_dts, running_time_pts, seqnum, timestamp: rtptime, npt_start: state.npt_start, npt_stop: state.npt_stop, play_speed: state.play_speed, play_scale: state.play_scale, }) }; drop(state); if let Some(segment_event) = segment_event { let _ = self.src_pad.push_event(segment_event); } // Now finally handle the packet. let obj = self.obj(); let mut res = (obj.class().as_ref().handle_packet)( &obj, &super::Packet { buffer, discont, ext_seqnum, ext_timestamp: ext_rtptime, marker, payload_range, }, ); if let Err(err) = res { gst::error!(CAT, imp: self, "Failed handling packet: {err:?}"); } else { res = self.finish_pending_buffers(); if let Err(err) = res { gst::debug!(CAT, imp: self, "Failed finishing pending buffers: {err:?}"); } } // Now drop all pending packets that were used for producing buffers // except for the very last one as that might still be used. let mut state = self.state.borrow_mut(); if let Some(last_used_ext_seqnum) = state.last_used_ext_seqnum { while state .pending_packets .front() .map_or(false, |b| b.ext_seqnum < last_used_ext_seqnum) { let _ = state.pending_packets.pop_front(); } } res } } /// Other methods. impl RtpBaseDepay2 { fn update_extensions_from_sink_caps( &self, sink_caps: &gst::Caps, ) -> Result { let s = sink_caps.structure(0).unwrap(); // Check which header extensions to enable/disable based on the caps. let mut caps_extensions = BTreeMap::new(); for (k, v) in s.iter() { let Some(ext_id) = k.strip_prefix("extmap-") else { continue; }; let Ok(ext_id) = ext_id.parse::() else { gst::error!(CAT, imp: self, "Can't parse RTP header extension id from caps {sink_caps:?}"); return Err(gst::FlowError::NotNegotiated); }; let uri = if let Ok(uri) = v.get::() { uri } else if let Ok(arr) = v.get::() { if let Some(uri) = arr.get(1).and_then(|v| v.get::().ok()) { uri } else { gst::error!(CAT, imp: self, "Couldn't get URI for RTP header extension id {ext_id} from caps {sink_caps:?}"); return Err(gst::FlowError::NotNegotiated); } } else { gst::error!(CAT, imp: self, "Couldn't get URI for RTP header extension id {ext_id} from caps {sink_caps:?}"); return Err(gst::FlowError::NotNegotiated); }; caps_extensions.insert(ext_id, uri); } let mut extensions = self.extensions.lock().unwrap(); let mut extensions_changed = false; for (ext_id, uri) in caps_extensions { if let Some(extension) = extensions.get(&ext_id) { if extension.uri().as_deref() == Some(&uri) { // Same extension, nothing to do here, we will update it with the new caps // later continue; } gst::debug!( CAT, imp: self, "Extension ID {ext_id} changed from {:?} to {uri}", extension.uri(), ); extensions_changed |= true; extensions.remove(&ext_id); } gst::debug!(CAT, imp: self, "Requesting extension {uri} for ID {ext_id}"); let ext = self .obj() .emit_by_name::>( "request-extension", &[&(ext_id as u32), &uri], ); let Some(ext) = ext else { gst::warning!(CAT, imp: self, "Couldn't create extension for {uri} with ID {ext_id}"); continue; }; if ext.id() != ext_id as u32 { gst::warning!(CAT, imp: self, "Created extension has wrong ID"); continue; } extensions.insert(ext_id, ext); extensions_changed |= true; } let mut to_remove = vec![]; for (ext_id, ext) in extensions.iter() { if !ext.set_attributes_from_caps(sink_caps) { gst::warning!(CAT, imp: self, "Failed to configure extension {ext_id} from caps {sink_caps}"); to_remove.push(*ext_id); } } for ext_id in to_remove { extensions.remove(&ext_id); extensions_changed |= true; } drop(extensions); if extensions_changed { self.obj().notify("extensions"); } Ok(gst::FlowSuccess::Ok) } fn set_sink_caps(&self, sink_caps: gst::Caps) -> Result { gst::debug!(CAT, imp: self, "Received caps {sink_caps:?}"); let s = sink_caps.structure(0).unwrap(); if !s.has_name("application/x-rtp") { gst::error!(CAT, imp: self, "Non-RTP caps {sink_caps:?} not supported"); return Err(gst::FlowError::NotNegotiated); } let clock_rate = match s.get::("clock-rate") { Ok(clock_rate) if clock_rate > 0 => clock_rate, _ => { gst::error!(CAT, imp: self, "RTP caps {sink_caps:?} without 'clock-rate'"); return Err(gst::FlowError::NotNegotiated); } }; let npt_start = s.get::("npt-start").ok(); let npt_stop = s.get::("npt-stop").ok(); let play_scale = s.get::("play-scale").unwrap_or(1.0); let play_speed = s.get::("play-speed").unwrap_or(1.0); let clock_base = s.get::("clock-base").ok(); let onvif_mode = s.get::("onvif-mode").unwrap_or_default(); self.update_extensions_from_sink_caps(&sink_caps)?; let state = self.state.borrow_mut(); let same_caps = state .sink_caps .as_ref() .map(|old_caps| &sink_caps == old_caps) .unwrap_or(false); drop(state); if !same_caps { let obj = self.obj(); if let Err(err) = self.drain() { // Just continue here. The depayloader is now flushed and can proceed below, // and if there was a serious error downstream it will happen on the next // buffer pushed downstream gst::debug!(CAT, imp: self, "Draining failed: {err:?}"); } let set_sink_caps_res = if (obj.class().as_ref().set_sink_caps)(&obj, &sink_caps) { gst::debug!(CAT, imp: self, "Caps {sink_caps:?} accepted"); Ok(gst::FlowSuccess::Ok) } else { gst::warning!(CAT, imp: self, "Caps {sink_caps:?} not accepted"); Err(gst::FlowError::NotNegotiated) }; let mut state = self.state.borrow_mut(); if let Err(err) = set_sink_caps_res { state.sink_caps = None; // FIXME: Forget everything here? return Err(err); } state.sink_caps = Some(sink_caps); state.clock_rate = Some(clock_rate as u32); // If npt-start is set, all the other values are also valid. // // In ONVIF mode these are all ignored and instead upstream is supposed to provide the // correct segment already. if npt_start.is_some() && !onvif_mode { state.npt_start = npt_start; state.npt_stop = npt_stop; state.play_speed = play_speed; state.play_scale = play_scale; state.clock_base = clock_base; state.npt_start_times = None; state.pending_segment = true; } else { if state.npt_start.is_some() { state.pending_segment = true; } state.npt_start = None; state.npt_stop = None; state.play_speed = 1.0; state.play_scale = 1.0; state.clock_base = None; state.npt_start_times = None; } drop(state); } Ok(gst::FlowSuccess::Ok) } fn drain(&self) -> Result { let obj = self.obj(); if let Err(err) = (obj.class().as_ref().drain)(&obj) { if ![gst::FlowError::Flushing, gst::FlowError::Eos].contains(&err) { gst::warning!(CAT, imp: self, "Draining failed: {err:?}"); } else { gst::debug!(CAT, imp: self, "Draining failed: {err:?}"); } self.flush(); return Err(err); } let mut state = self.state.borrow_mut(); state.pending_packets.clear(); // Update all pending buffers without metadata with the last buffer's PTS/DTS let last_pts = state.last_pts; let last_dts = state.last_dts; let mut discont_pending = state.discont_pending; state.discont_pending = false; for buffer in state .pending_buffers .iter_mut() .skip_while(|b| b.metadata_set) { assert!(!buffer.metadata_set); buffer.metadata_set = true; let buffer = &mut buffer.buffer; let buffer_ref = buffer.make_mut(); buffer_ref.set_pts(last_pts); buffer_ref.set_dts(last_dts); if discont_pending { buffer_ref.set_flags(gst::BufferFlags::DISCONT); discont_pending = false; } } drop(state); // Forward all buffers let res = self.finish_pending_buffers(); self.flush(); res } fn flush(&self) { let obj = self.obj(); (obj.class().as_ref().flush)(&obj); let mut state = self.state.borrow_mut(); state.pending_packets.clear(); state.pending_buffers.clear(); state.discont_pending = true; } fn create_stats(&self) -> gst::Structure { let stats = self.stats.lock().unwrap().clone(); if let Some(stats) = stats { gst::Structure::builder("application/x-rtp-depayload-stats") .field("ssrc", stats.ssrc) .field("clock-rate", stats.clock_rate) .field("running-time-dts", stats.running_time_dts) .field("running-time-pts", stats.running_time_pts) .field("seqnum", stats.seqnum as u32) .field("timestamp", stats.timestamp) .field("npt-start", stats.npt_start) .field("npt-stop", stats.npt_stop) .field("play-speed", stats.play_speed) .field("play-scale", stats.play_scale) .build() } else { gst::Structure::builder("application/x-rtp-depayload-stats").build() } } } #[glib::object_subclass] impl ObjectSubclass for RtpBaseDepay2 { const NAME: &'static str = "GstRtpBaseDepay2"; const ABSTRACT: bool = true; type Type = super::RtpBaseDepay2; type ParentType = gst::Element; type Class = super::Class; fn class_init(class: &mut Self::Class) { class.start = |obj| obj.imp().start_default(); class.stop = |obj| obj.imp().stop_default(); class.set_sink_caps = |obj, caps| obj.imp().set_sink_caps_default(caps); class.handle_packet = |obj, packet| obj.imp().handle_packet_default(packet); class.drain = |obj| obj.imp().drain_default(); class.flush = |obj| obj.imp().flush_default(); class.sink_event = |obj, event| obj.imp().sink_event_default(event); class.src_event = |obj, event| obj.imp().src_event_default(event); class.sink_query = |obj, query| obj.imp().sink_query_default(query); class.src_query = |obj, query| obj.imp().src_query_default(query); class.transform_meta = |obj, in_buf, meta, out_buf| obj.imp().transform_meta_default(in_buf, meta, out_buf); class.allowed_meta_tags = &[]; } fn with_class(class: &Self::Class) -> Self { let templ = class .pad_template("sink") .expect("Subclass did not provide a \"sink\" pad template"); let sink_pad = gst::Pad::builder_from_template(&templ) .chain_function(|pad, parent, buffer| { Self::catch_panic_pad_function( parent, || Err(gst::FlowError::Error), |imp| imp.sink_chain(pad, buffer), ) }) .chain_list_function(|pad, parent, list| { Self::catch_panic_pad_function( parent, || Err(gst::FlowError::Error), |imp| imp.sink_chain_list(pad, list), ) }) .event_full_function(|pad, parent, event| { Self::catch_panic_pad_function( parent, || Err(gst::FlowError::Error), |imp| imp.sink_event(pad, event), ) }) .query_function(|pad, parent, query| { Self::catch_panic_pad_function(parent, || false, |imp| imp.sink_query(pad, query)) }) .build(); let templ = class .pad_template("src") .expect("Subclass did not provide a \"src\" pad template"); let src_pad = gst::Pad::builder_from_template(&templ) .event_full_function(|pad, parent, event| { Self::catch_panic_pad_function( parent, || Err(gst::FlowError::Error), |imp| imp.src_event(pad, event), ) }) .query_function(|pad, parent, query| { Self::catch_panic_pad_function(parent, || false, |imp| imp.src_query(pad, query)) }) .flags(gst::PadFlags::FIXED_CAPS) .build(); Self { src_pad, sink_pad, state: AtomicRefCell::default(), settings: Mutex::default(), stats: Mutex::default(), extensions: Mutex::default(), } } } impl ObjectImpl for RtpBaseDepay2 { fn properties() -> &'static [glib::ParamSpec] { static PROPERTIES: Lazy> = Lazy::new(|| { vec![ glib::ParamSpecBoxed::builder::("stats") .nick("Statistics") .blurb("Various statistics") .read_only() .build(), glib::ParamSpecUInt::builder("max-reorder") .nick("Maximum Reorder") .blurb("Maximum seqnum reorder before assuming sender has restarted") .default_value(Settings::default().max_reorder) .minimum(0) .maximum(0x7fff) .mutable_playing() .build(), glib::ParamSpecBoolean::builder("source-info") .nick("RTP Source Info") .blurb("Add RTP source information as buffer metadata") .default_value(Settings::default().source_info) .mutable_playing() .build(), glib::ParamSpecBoolean::builder("auto-header-extensions") .nick("Automatic RTP Header Extensions") .blurb("Whether RTP header extensions should be automatically enabled, if an implementation is available") .default_value(Settings::default().auto_header_extensions) .mutable_ready() .build(), gst::ParamSpecArray::builder("extensions") .nick("RTP Header Extensions") .blurb("List of enabled RTP header extensions") .element_spec(&glib::ParamSpecObject::builder::("extension") .nick("RTP Header Extension") .blurb("Enabled RTP header extension") .read_only() .build() ) .read_only() .build(), ] }); PROPERTIES.as_ref() } fn signals() -> &'static [glib::subclass::Signal] { static SIGNALS: Lazy> = Lazy::new(|| { vec![ glib::subclass::Signal::builder("add-extension") .action() .param_types([gst_rtp::RTPHeaderExtension::static_type()]) .class_handler(|_token, args| { let s = args[0].get::().unwrap(); let ext = args[1].get::<&gst_rtp::RTPHeaderExtension>().unwrap(); s.imp().add_extension(ext); None }) .build(), glib::subclass::Signal::builder("request-extension") .param_types([u32::static_type(), String::static_type()]) .return_type::() .accumulator(|_hint, acc, val| { if matches!(val.get::>(), Ok(Some(_))) { *acc = val.clone(); false } else { true } }) .class_handler(|_token, args| { let s = args[0].get::().unwrap(); let ext_id = args[1].get::().unwrap(); let uri = args[2].get::<&str>().unwrap(); let ext = s.imp().request_extension(ext_id, uri); Some(ext.to_value()) }) .build(), glib::subclass::Signal::builder("clear-extensions") .action() .class_handler(|_token, args| { let s = args[0].get::().unwrap(); s.imp().clear_extensions(); None }) .build(), ] }); SIGNALS.as_ref() } fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) { match pspec.name() { "max-reorder" => { self.settings.lock().unwrap().max_reorder = value.get().unwrap(); } "source-info" => { self.settings.lock().unwrap().source_info = value.get().unwrap(); } "auto-header-extensions" => { self.settings.lock().unwrap().auto_header_extensions = value.get().unwrap(); } _ => unimplemented!(), }; } fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { match pspec.name() { "stats" => self.create_stats().to_value(), "max-reorder" => self.settings.lock().unwrap().max_reorder.to_value(), "source-info" => self.settings.lock().unwrap().source_info.to_value(), "auto-header-extensions" => self .settings .lock() .unwrap() .auto_header_extensions .to_value(), "extensions" => gst::Array::new(self.extensions.lock().unwrap().values()).to_value(), _ => unimplemented!(), } } fn constructed(&self) { self.parent_constructed(); let obj = self.obj(); obj.add_pad(&self.sink_pad).unwrap(); obj.add_pad(&self.src_pad).unwrap(); } } impl GstObjectImpl for RtpBaseDepay2 {} impl ElementImpl for RtpBaseDepay2 { fn change_state( &self, transition: gst::StateChange, ) -> Result { gst::debug!(CAT, imp: self, "Changing state: {transition}"); if transition == gst::StateChange::ReadyToPaused { *self.state.borrow_mut() = State::default(); *self.stats.lock().unwrap() = None; let obj = self.obj(); (obj.class().as_ref().start)(&obj).map_err(|err_msg| { self.post_error_message(err_msg); gst::StateChangeError })?; } let ret = self.parent_change_state(transition)?; if transition == gst::StateChange::PausedToReady { let obj = self.obj(); (obj.class().as_ref().stop)(&obj).map_err(|err_msg| { self.post_error_message(err_msg); gst::StateChangeError })?; *self.state.borrow_mut() = State::default(); *self.stats.lock().unwrap() = None; } Ok(ret) } }