AG_GST_CHECK_PLUGIN(qtmux)
AG_GST_CHECK_PLUGIN(rawparse)
AG_GST_CHECK_PLUGIN(real)
-AG_GST_CHECK_PLUGIN(rtpmanager)
AG_GST_CHECK_PLUGIN(rtpmux)
AG_GST_CHECK_PLUGIN(scaletempo)
AG_GST_CHECK_PLUGIN(sdp)
gst/qtmux/Makefile
gst/rawparse/Makefile
gst/real/Makefile
-gst/rtpmanager/Makefile
gst/rtpmux/Makefile
gst/scaletempo/Makefile
gst/sdp/Makefile
$(top_srcdir)/gst/pcapparse/gstpcapparse.h \
$(top_srcdir)/gst/rawparse/gstaudioparse.h \
$(top_srcdir)/gst/rawparse/gstvideoparse.h \
- $(top_srcdir)/gst/rtpmanager/gstrtpbin.h \
- $(top_srcdir)/gst/rtpmanager/gstrtpjitterbuffer.h \
- $(top_srcdir)/gst/rtpmanager/gstrtpptdemux.h \
- $(top_srcdir)/gst/rtpmanager/gstrtpsession.h \
- $(top_srcdir)/gst/rtpmanager/gstrtpssrcdemux.h \
$(top_srcdir)/gst/rtpmux/gstrtpmux.h \
$(top_srcdir)/gst/rtpmux/gstrtpdtmfmux.h \
$(top_srcdir)/gst/scaletempo/gstscaletempo.h \
<xi:include href="xml/element-dvdspu.xml" />
<xi:include href="xml/element-festival.xml" />
<xi:include href="xml/element-fpsdisplaysink.xml" />
- <xi:include href="xml/element-gstrtpbin.xml" />
- <xi:include href="xml/element-gstrtpjitterbuffer.xml" />
- <xi:include href="xml/element-gstrtpptdemux.xml" />
- <xi:include href="xml/element-gstrtpsession.xml" />
- <xi:include href="xml/element-gstrtpssrcdemux.xml" />
<xi:include href="xml/element-input-selector.xml" />
<xi:include href="xml/element-ivorbisdec.xml" />
<xi:include href="xml/element-jackaudiosrc.xml" />
<xi:include href="xml/plugin-freeze.xml" />
<xi:include href="xml/plugin-frei0r.xml" />
<xi:include href="xml/plugin-gsm.xml" />
- <xi:include href="xml/plugin-gstrtpmanager.xml" />
<xi:include href="xml/plugin-h264parse.xml" />
<xi:include href="xml/plugin-jack.xml" />
<xi:include href="xml/plugin-kate.xml" />
gst_rtp_session_set_ssrc
</SECTION>
-<SECTION>
-<FILE>element-gstrtpbin</FILE>
-<TITLE>gstrtpbin</TITLE>
-GstRtpBin
-<SUBSECTION Standard>
-GstRtpBinPrivate
-GstRtpBinClass
-GST_RTP_BIN
-GST_IS_RTP_BIN
-GST_TYPE_RTP_BIN
-gst_rtp_bin_get_type
-GST_RTP_BIN_CLASS
-GST_IS_RTP_BIN_CLASS
-</SECTION>
-
-<SECTION>
-<FILE>element-gstrtpjitterbuffer</FILE>
-<TITLE>gstrtpjitterbuffer</TITLE>
-GstRtpJitterBuffer
-<SUBSECTION Standard>
-GstRtpJitterBufferClass
-GstRtpJitterBufferPrivate
-GST_RTP_JITTER_BUFFER
-GST_IS_RTP_JITTER_BUFFER
-GST_TYPE_RTP_JITTER_BUFFER
-gst_rtp_jitter_buffer_get_type
-GST_RTP_JITTER_BUFFER_CLASS
-GST_IS_RTP_JITTER_BUFFER_CLASS
-</SECTION>
-
-<SECTION>
-<FILE>element-gstrtpptdemux</FILE>
-<TITLE>gstrtpptdemux</TITLE>
-GstRtpPtDemux
-<SUBSECTION Standard>
-GstRtpPtDemuxClass
-GstRtpPtDemuxPad
-GST_RTP_PT_DEMUX
-GST_IS_RTP_PT_DEMUX
-GST_TYPE_RTP_PT_DEMUX
-gst_rtp_pt_demux_get_type
-GST_RTP_PT_DEMUX_CLASS
-GST_IS_RTP_PT_DEMUX_CLASS
-</SECTION>
-
-<SECTION>
-<FILE>element-gstrtpsession</FILE>
-<TITLE>gstrtpsession</TITLE>
-GstRtpSession
-<SUBSECTION Standard>
-GstRtpSessionClass
-GstRtpSessionPrivate
-GST_RTP_SESSION
-GST_IS_RTP_SESSION
-GST_TYPE_RTP_SESSION
-gst_rtp_session_get_type
-GST_RTP_SESSION_CLASS
-GST_IS_RTP_SESSION_CLASS
-GST_RTP_SESSION_CAST
-</SECTION>
-
-<SECTION>
-<FILE>element-gstrtpssrcdemux</FILE>
-<TITLE>gstrtpssrcdemux</TITLE>
-GstRtpSsrcDemux
-<SUBSECTION Standard>
-GstRtpSsrcDemuxClass
-GstRtpSsrcDemuxPad
-GST_RTP_SSRC_DEMUX
-GST_IS_RTP_SSRC_DEMUX
-GST_TYPE_RTP_SSRC_DEMUX
-gst_rtp_ssrc_demux_get_type
-GST_RTP_SSRC_DEMUX_CLASS
-GST_IS_RTP_SSRC_DEMUX_CLASS
-</SECTION>
-
<SECTION>
<FILE>element-scaletempo</FILE>
<TITLE>scaletempo</TITLE>
<DEFAULT>25</DEFAULT>
</ARG>
-<ARG>
-<NAME>GstRTPJitterBuffer::drop-on-latency</NAME>
-<TYPE>gboolean</TYPE>
-<RANGE></RANGE>
-<FLAGS>rw</FLAGS>
-<NICK>Drop buffers when maximum latency is reached</NICK>
-<BLURB>Tells the jitterbuffer to never exceed the given latency in size.</BLURB>
-<DEFAULT>FALSE</DEFAULT>
-</ARG>
-
-<ARG>
-<NAME>GstRTPJitterBuffer::latency</NAME>
-<TYPE>guint</TYPE>
-<RANGE></RANGE>
-<FLAGS>rw</FLAGS>
-<NICK>Buffer latency in ms</NICK>
-<BLURB>Amount of ms to buffer.</BLURB>
-<DEFAULT>200</DEFAULT>
-</ARG>
-
-<ARG>
-<NAME>GstRTPBin::latency</NAME>
-<TYPE>guint</TYPE>
-<RANGE></RANGE>
-<FLAGS>rw</FLAGS>
-<NICK>Buffer latency in ms</NICK>
-<BLURB>Default amount of ms to buffer in the jitterbuffers.</BLURB>
-<DEFAULT>200</DEFAULT>
-</ARG>
-
<ARG>
<NAME>GstOSXVideoSink::embed</NAME>
<TYPE>gboolean</TYPE>
<DEFAULT>0</DEFAULT>
</ARG>
-<ARG>
-<NAME>GstRtpSession::ntp-ns-base</NAME>
-<TYPE>guint64</TYPE>
-<RANGE></RANGE>
-<FLAGS>rw</FLAGS>
-<NICK>NTP base time</NICK>
-<BLURB>The NTP base time corresponding to running_time 0.</BLURB>
-<DEFAULT>0</DEFAULT>
-</ARG>
-
-<ARG>
-<NAME>GstRtpSession::bandwidth</NAME>
-<TYPE>gdouble</TYPE>
-<RANGE>>= 0</RANGE>
-<FLAGS>rw</FLAGS>
-<NICK>Bandwidth</NICK>
-<BLURB>The bandwidth of the session.</BLURB>
-<DEFAULT>64000</DEFAULT>
-</ARG>
-
-<ARG>
-<NAME>GstRtpSession::num-active-sources</NAME>
-<TYPE>guint</TYPE>
-<RANGE></RANGE>
-<FLAGS>r</FLAGS>
-<NICK>Num Active Sources</NICK>
-<BLURB>The number of active sources in the session.</BLURB>
-<DEFAULT>0</DEFAULT>
-</ARG>
-
-<ARG>
-<NAME>GstRtpSession::num-sources</NAME>
-<TYPE>guint</TYPE>
-<RANGE></RANGE>
-<FLAGS>r</FLAGS>
-<NICK>Num Sources</NICK>
-<BLURB>The number of sources in the session.</BLURB>
-<DEFAULT>0</DEFAULT>
-</ARG>
-
-<ARG>
-<NAME>GstRtpSession::rtcp-fraction</NAME>
-<TYPE>gdouble</TYPE>
-<RANGE>>= 0</RANGE>
-<FLAGS>rw</FLAGS>
-<NICK>RTCP Fraction</NICK>
-<BLURB>The fraction of the bandwidth used for RTCP.</BLURB>
-<DEFAULT>3000</DEFAULT>
-</ARG>
-
-<ARG>
-<NAME>GstRtpSession::sdes-cname</NAME>
-<TYPE>gchar*</TYPE>
-<RANGE></RANGE>
-<FLAGS>rw</FLAGS>
-<NICK>SDES CNAME</NICK>
-<BLURB>The CNAME to put in SDES messages of this session.</BLURB>
-<DEFAULT>NULL</DEFAULT>
-</ARG>
-
-<ARG>
-<NAME>GstRtpSession::sdes-email</NAME>
-<TYPE>gchar*</TYPE>
-<RANGE></RANGE>
-<FLAGS>rw</FLAGS>
-<NICK>SDES EMAIL</NICK>
-<BLURB>The EMAIL to put in SDES messages of this session.</BLURB>
-<DEFAULT>NULL</DEFAULT>
-</ARG>
-
-<ARG>
-<NAME>GstRtpSession::sdes-location</NAME>
-<TYPE>gchar*</TYPE>
-<RANGE></RANGE>
-<FLAGS>rw</FLAGS>
-<NICK>SDES LOCATION</NICK>
-<BLURB>The LOCATION to put in SDES messages of this session.</BLURB>
-<DEFAULT>NULL</DEFAULT>
-</ARG>
-
-<ARG>
-<NAME>GstRtpSession::sdes-name</NAME>
-<TYPE>gchar*</TYPE>
-<RANGE></RANGE>
-<FLAGS>rw</FLAGS>
-<NICK>SDES NAME</NICK>
-<BLURB>The NAME to put in SDES messages of this session.</BLURB>
-<DEFAULT>NULL</DEFAULT>
-</ARG>
-
-<ARG>
-<NAME>GstRtpSession::sdes-note</NAME>
-<TYPE>gchar*</TYPE>
-<RANGE></RANGE>
-<FLAGS>rw</FLAGS>
-<NICK>SDES NOTE</NICK>
-<BLURB>The NOTE to put in SDES messages of this session.</BLURB>
-<DEFAULT>NULL</DEFAULT>
-</ARG>
-
-<ARG>
-<NAME>GstRtpSession::sdes-phone</NAME>
-<TYPE>gchar*</TYPE>
-<RANGE></RANGE>
-<FLAGS>rw</FLAGS>
-<NICK>SDES PHONE</NICK>
-<BLURB>The PHONE to put in SDES messages of this session.</BLURB>
-<DEFAULT>NULL</DEFAULT>
-</ARG>
-
-<ARG>
-<NAME>GstRtpSession::sdes-tool</NAME>
-<TYPE>gchar*</TYPE>
-<RANGE></RANGE>
-<FLAGS>rw</FLAGS>
-<NICK>SDES TOOL</NICK>
-<BLURB>The TOOL to put in SDES messages of this session.</BLURB>
-<DEFAULT>NULL</DEFAULT>
-</ARG>
-
-<ARG>
-<NAME>GstRtpSession::internal-session</NAME>
-<TYPE>RTPSession*</TYPE>
-<RANGE></RANGE>
-<FLAGS>r</FLAGS>
-<NICK>Internal Session</NICK>
-<BLURB>The internal RTPSession object.</BLURB>
-<DEFAULT></DEFAULT>
-</ARG>
-
-<ARG>
-<NAME>GstRtpSession::sdes</NAME>
-<TYPE>GstStructure*</TYPE>
-<RANGE></RANGE>
-<FLAGS>rw</FLAGS>
-<NICK>SDES</NICK>
-<BLURB>The SDES items of this session.</BLURB>
-<DEFAULT></DEFAULT>
-</ARG>
-
-<ARG>
-<NAME>GstRtpJitterBuffer::drop-on-latency</NAME>
-<TYPE>gboolean</TYPE>
-<RANGE></RANGE>
-<FLAGS>rw</FLAGS>
-<NICK>Drop buffers when maximum latency is reached</NICK>
-<BLURB>Tells the jitterbuffer to never exceed the given latency in size.</BLURB>
-<DEFAULT>FALSE</DEFAULT>
-</ARG>
-
-<ARG>
-<NAME>GstRtpJitterBuffer::latency</NAME>
-<TYPE>guint</TYPE>
-<RANGE></RANGE>
-<FLAGS>rw</FLAGS>
-<NICK>Buffer latency in ms</NICK>
-<BLURB>Amount of ms to buffer.</BLURB>
-<DEFAULT>200</DEFAULT>
-</ARG>
-
-<ARG>
-<NAME>GstRtpJitterBuffer::ts-offset</NAME>
-<TYPE>gint64</TYPE>
-<RANGE></RANGE>
-<FLAGS>rw</FLAGS>
-<NICK>Timestamp Offset</NICK>
-<BLURB>Adjust buffer timestamps with offset in nanoseconds.</BLURB>
-<DEFAULT>0</DEFAULT>
-</ARG>
-
-<ARG>
-<NAME>GstRtpJitterBuffer::do-lost</NAME>
-<TYPE>gboolean</TYPE>
-<RANGE></RANGE>
-<FLAGS>rw</FLAGS>
-<NICK>Do Lost</NICK>
-<BLURB>Send an event downstream when a packet is lost.</BLURB>
-<DEFAULT>FALSE</DEFAULT>
-</ARG>
-
-<ARG>
-<NAME>GstRtpBin::latency</NAME>
-<TYPE>guint</TYPE>
-<RANGE></RANGE>
-<FLAGS>rw</FLAGS>
-<NICK>Buffer latency in ms</NICK>
-<BLURB>Default amount of ms to buffer in the jitterbuffers.</BLURB>
-<DEFAULT>200</DEFAULT>
-</ARG>
-
-<ARG>
-<NAME>GstRtpBin::sdes-cname</NAME>
-<TYPE>gchar*</TYPE>
-<RANGE></RANGE>
-<FLAGS>rw</FLAGS>
-<NICK>SDES CNAME</NICK>
-<BLURB>The CNAME to put in SDES messages of this session.</BLURB>
-<DEFAULT>NULL</DEFAULT>
-</ARG>
-
-<ARG>
-<NAME>GstRtpBin::sdes-email</NAME>
-<TYPE>gchar*</TYPE>
-<RANGE></RANGE>
-<FLAGS>rw</FLAGS>
-<NICK>SDES EMAIL</NICK>
-<BLURB>The EMAIL to put in SDES messages of this session.</BLURB>
-<DEFAULT>NULL</DEFAULT>
-</ARG>
-
-<ARG>
-<NAME>GstRtpBin::sdes-location</NAME>
-<TYPE>gchar*</TYPE>
-<RANGE></RANGE>
-<FLAGS>rw</FLAGS>
-<NICK>SDES LOCATION</NICK>
-<BLURB>The LOCATION to put in SDES messages of this session.</BLURB>
-<DEFAULT>NULL</DEFAULT>
-</ARG>
-
-<ARG>
-<NAME>GstRtpBin::sdes-name</NAME>
-<TYPE>gchar*</TYPE>
-<RANGE></RANGE>
-<FLAGS>rw</FLAGS>
-<NICK>SDES NAME</NICK>
-<BLURB>The NAME to put in SDES messages of this session.</BLURB>
-<DEFAULT>NULL</DEFAULT>
-</ARG>
-
-<ARG>
-<NAME>GstRtpBin::sdes-note</NAME>
-<TYPE>gchar*</TYPE>
-<RANGE></RANGE>
-<FLAGS>rw</FLAGS>
-<NICK>SDES NOTE</NICK>
-<BLURB>The NOTE to put in SDES messages of this session.</BLURB>
-<DEFAULT>NULL</DEFAULT>
-</ARG>
-
-<ARG>
-<NAME>GstRtpBin::sdes-phone</NAME>
-<TYPE>gchar*</TYPE>
-<RANGE></RANGE>
-<FLAGS>rw</FLAGS>
-<NICK>SDES PHONE</NICK>
-<BLURB>The PHONE to put in SDES messages of this session.</BLURB>
-<DEFAULT>NULL</DEFAULT>
-</ARG>
-
-<ARG>
-<NAME>GstRtpBin::sdes-tool</NAME>
-<TYPE>gchar*</TYPE>
-<RANGE></RANGE>
-<FLAGS>rw</FLAGS>
-<NICK>SDES TOOL</NICK>
-<BLURB>The TOOL to put in SDES messages of this session.</BLURB>
-<DEFAULT>NULL</DEFAULT>
-</ARG>
-
-<ARG>
-<NAME>GstRtpBin::do-lost</NAME>
-<TYPE>gboolean</TYPE>
-<RANGE></RANGE>
-<FLAGS>rw</FLAGS>
-<NICK>Do Lost</NICK>
-<BLURB>Send an event downstream when a packet is lost.</BLURB>
-<DEFAULT>FALSE</DEFAULT>
-</ARG>
-
-<ARG>
-<NAME>GstRtpBin::sdes</NAME>
-<TYPE>GstStructure*</TYPE>
-<RANGE></RANGE>
-<FLAGS>rw</FLAGS>
-<NICK>SDES</NICK>
-<BLURB>The SDES items of this session.</BLURB>
-<DEFAULT></DEFAULT>
-</ARG>
-
<ARG>
<NAME>GstGioSrc::location</NAME>
<TYPE>gchar*</TYPE>
GstFPSDisplaySink
GstAutoConvert
GstSDPDemux
- GstRtpBin
GstSignalProcessor
http---calf-sourceforge-net-plugins-Compressor
http---calf-sourceforge-net-plugins-Filter
GstSpeed
GstInputSelector
GstOutputSelector
- GstRtpJitterBuffer
- GstRtpPtDemux
- GstRtpSession
- GstRtpSsrcDemux
GstRealVideoDec
GstRealAudioDec
GstRawParse
gint64 arg3
</SIGNAL>
-<SIGNAL>
-<NAME>GstRtpBin::clear-pt-map</NAME>
-<RETURNS>void</RETURNS>
-<FLAGS>la</FLAGS>
-GstRtpBin *gstrtpbin
-</SIGNAL>
-
-<SIGNAL>
-<NAME>GstRtpBin::on-bye-ssrc</NAME>
-<RETURNS>void</RETURNS>
-<FLAGS>l</FLAGS>
-GstRtpBin *gstrtpbin
-guint arg1
-guint arg2
-</SIGNAL>
-
-<SIGNAL>
-<NAME>GstRtpBin::on-bye-timeout</NAME>
-<RETURNS>void</RETURNS>
-<FLAGS>l</FLAGS>
-GstRtpBin *gstrtpbin
-guint arg1
-guint arg2
-</SIGNAL>
-
-<SIGNAL>
-<NAME>GstRtpBin::on-new-ssrc</NAME>
-<RETURNS>void</RETURNS>
-<FLAGS>l</FLAGS>
-GstRtpBin *gstrtpbin
-guint arg1
-guint arg2
-</SIGNAL>
-
-<SIGNAL>
-<NAME>GstRtpBin::on-ssrc-active</NAME>
-<RETURNS>void</RETURNS>
-<FLAGS>l</FLAGS>
-GstRtpBin *gstrtpbin
-guint arg1
-guint arg2
-</SIGNAL>
-
-<SIGNAL>
-<NAME>GstRtpBin::on-ssrc-collision</NAME>
-<RETURNS>void</RETURNS>
-<FLAGS>l</FLAGS>
-GstRtpBin *gstrtpbin
-guint arg1
-guint arg2
-</SIGNAL>
-
-<SIGNAL>
-<NAME>GstRtpBin::on-ssrc-sdes</NAME>
-<RETURNS>void</RETURNS>
-<FLAGS>l</FLAGS>
-GstRtpBin *gstrtpbin
-guint arg1
-guint arg2
-</SIGNAL>
-
-<SIGNAL>
-<NAME>GstRtpBin::on-ssrc-validated</NAME>
-<RETURNS>void</RETURNS>
-<FLAGS>l</FLAGS>
-GstRtpBin *gstrtpbin
-guint arg1
-guint arg2
-</SIGNAL>
-
-<SIGNAL>
-<NAME>GstRtpBin::on-timeout</NAME>
-<RETURNS>void</RETURNS>
-<FLAGS>l</FLAGS>
-GstRtpBin *gstrtpbin
-guint arg1
-guint arg2
-</SIGNAL>
-
-<SIGNAL>
-<NAME>GstRtpBin::request-pt-map</NAME>
-<RETURNS>GstCaps*</RETURNS>
-<FLAGS>l</FLAGS>
-GstRtpBin *gstrtpbin
-guint arg1
-guint arg2
-</SIGNAL>
-
-<SIGNAL>
-<NAME>GstRtpBin::get-internal-session</NAME>
-<RETURNS>RTPSession*</RETURNS>
-<FLAGS>la</FLAGS>
-GstRtpBin *gstrtpbin
-guint arg1
-</SIGNAL>
-
-<SIGNAL>
-<NAME>GstRtpBin::on-sender-timeout</NAME>
-<RETURNS>void</RETURNS>
-<FLAGS>l</FLAGS>
-GstRtpBin *gstrtpbin
-guint arg1
-guint arg2
-</SIGNAL>
-
-<SIGNAL>
-<NAME>GstRtpBin::reset-sync</NAME>
-<RETURNS>void</RETURNS>
-<FLAGS>la</FLAGS>
-GstRtpBin *gstrtpbin
-</SIGNAL>
-
-<SIGNAL>
-<NAME>GstRtpBin::on-npt-stop</NAME>
-<RETURNS>void</RETURNS>
-<FLAGS>l</FLAGS>
-GstRtpBin *gstrtpbin
-guint arg1
-guint arg2
-</SIGNAL>
-
-<SIGNAL>
-<NAME>GstRtpJitterBuffer::clear-pt-map</NAME>
-<RETURNS>void</RETURNS>
-<FLAGS>la</FLAGS>
-GstRtpJitterBuffer *gstrtpjitterbuffer
-</SIGNAL>
-
-<SIGNAL>
-<NAME>GstRtpJitterBuffer::request-pt-map</NAME>
-<RETURNS>GstCaps*</RETURNS>
-<FLAGS>l</FLAGS>
-GstRtpJitterBuffer *gstrtpjitterbuffer
-guint arg1
-</SIGNAL>
-
-<SIGNAL>
-<NAME>GstRtpJitterBuffer::handle-sync</NAME>
-<RETURNS>void</RETURNS>
-<FLAGS>l</FLAGS>
-GstRtpJitterBuffer *gstrtpjitterbuffer
-GstStructure *arg1
-</SIGNAL>
-
-<SIGNAL>
-<NAME>GstRtpJitterBuffer::on-npt-stop</NAME>
-<RETURNS>void</RETURNS>
-<FLAGS>l</FLAGS>
-GstRtpJitterBuffer *gstrtpjitterbuffer
-</SIGNAL>
-
-<SIGNAL>
-<NAME>GstRtpPtDemux::clear-pt-map</NAME>
-<RETURNS>void</RETURNS>
-<FLAGS>la</FLAGS>
-GstRtpPtDemux *gstrtpptdemux
-</SIGNAL>
-
-<SIGNAL>
-<NAME>GstRtpPtDemux::new-payload-type</NAME>
-<RETURNS>void</RETURNS>
-<FLAGS>l</FLAGS>
-GstRtpPtDemux *gstrtpptdemux
-guint arg1
-GstPad *arg2
-</SIGNAL>
-
-<SIGNAL>
-<NAME>GstRtpPtDemux::payload-type-change</NAME>
-<RETURNS>void</RETURNS>
-<FLAGS>l</FLAGS>
-GstRtpPtDemux *gstrtpptdemux
-guint arg1
-</SIGNAL>
-
-<SIGNAL>
-<NAME>GstRtpPtDemux::request-pt-map</NAME>
-<RETURNS>GstCaps*</RETURNS>
-<FLAGS>l</FLAGS>
-GstRtpPtDemux *gstrtpptdemux
-guint arg1
-</SIGNAL>
-
-<SIGNAL>
-<NAME>GstRtpSession::clear-pt-map</NAME>
-<RETURNS>void</RETURNS>
-<FLAGS>a</FLAGS>
-GstRtpSession *gstrtpsession
-</SIGNAL>
-
-<SIGNAL>
-<NAME>GstRtpSession::on-bye-ssrc</NAME>
-<RETURNS>void</RETURNS>
-<FLAGS>l</FLAGS>
-GstRtpSession *gstrtpsession
-guint arg1
-</SIGNAL>
-
-<SIGNAL>
-<NAME>GstRtpSession::on-bye-timeout</NAME>
-<RETURNS>void</RETURNS>
-<FLAGS>l</FLAGS>
-GstRtpSession *gstrtpsession
-guint arg1
-</SIGNAL>
-
-<SIGNAL>
-<NAME>GstRtpSession::on-new-ssrc</NAME>
-<RETURNS>void</RETURNS>
-<FLAGS>l</FLAGS>
-GstRtpSession *gstrtpsession
-guint arg1
-</SIGNAL>
-
-<SIGNAL>
-<NAME>GstRtpSession::on-ssrc-active</NAME>
-<RETURNS>void</RETURNS>
-<FLAGS>l</FLAGS>
-GstRtpSession *gstrtpsession
-guint arg1
-</SIGNAL>
-
-<SIGNAL>
-<NAME>GstRtpSession::on-ssrc-collision</NAME>
-<RETURNS>void</RETURNS>
-<FLAGS>l</FLAGS>
-GstRtpSession *gstrtpsession
-guint arg1
-</SIGNAL>
-
-<SIGNAL>
-<NAME>GstRtpSession::on-ssrc-sdes</NAME>
-<RETURNS>void</RETURNS>
-<FLAGS>l</FLAGS>
-GstRtpSession *gstrtpsession
-guint arg1
-</SIGNAL>
-
-<SIGNAL>
-<NAME>GstRtpSession::on-ssrc-validated</NAME>
-<RETURNS>void</RETURNS>
-<FLAGS>l</FLAGS>
-GstRtpSession *gstrtpsession
-guint arg1
-</SIGNAL>
-
-<SIGNAL>
-<NAME>GstRtpSession::on-timeout</NAME>
-<RETURNS>void</RETURNS>
-<FLAGS>l</FLAGS>
-GstRtpSession *gstrtpsession
-guint arg1
-</SIGNAL>
-
-<SIGNAL>
-<NAME>GstRtpSession::request-pt-map</NAME>
-<RETURNS>GstCaps*</RETURNS>
-<FLAGS>l</FLAGS>
-GstRtpSession *gstrtpsession
-guint arg1
-</SIGNAL>
-
-<SIGNAL>
-<NAME>GstRtpSession::on-sender-timeout</NAME>
-<RETURNS>void</RETURNS>
-<FLAGS>l</FLAGS>
-GstRtpSession *gstrtpsession
-guint arg1
-</SIGNAL>
-
-<SIGNAL>
-<NAME>GstRtpSsrcDemux::new-ssrc-pad</NAME>
-<RETURNS>void</RETURNS>
-<FLAGS>l</FLAGS>
-GstRtpSsrcDemux *gstrtpssrcdemux
-guint arg1
-GstPad *arg2
-</SIGNAL>
-
-<SIGNAL>
-<NAME>GstRtpSsrcDemux::clear-ssrc</NAME>
-<RETURNS>void</RETURNS>
-<FLAGS>la</FLAGS>
-GstRtpSsrcDemux *gstrtpssrcdemux
-guint arg1
-</SIGNAL>
-
-<SIGNAL>
-<NAME>GstRtpSsrcDemux::removed-ssrc-pad</NAME>
-<RETURNS>void</RETURNS>
-<FLAGS>l</FLAGS>
-GstRtpSsrcDemux *gstrtpssrcdemux
-guint arg1
-GstPad *arg2
-</SIGNAL>
-
<SIGNAL>
<NAME>GstCDAudio::track-change</NAME>
<RETURNS>void</RETURNS>
+++ /dev/null
-<plugin>
- <name>gstrtpmanager</name>
- <description>RTP session management plugin library</description>
- <filename>../../gst/rtpmanager/.libs/libgstrtpmanager.so</filename>
- <basename>libgstrtpmanager.so</basename>
- <version>0.10.13.1</version>
- <license>LGPL</license>
- <source>gst-plugins-bad</source>
- <package>GStreamer Bad Plug-ins git/prerelease</package>
- <origin>http://gstreamer.freedesktop.org</origin>
- <elements>
- <element>
- <name>gstrtpbin</name>
- <longname>RTP Bin</longname>
- <class>Filter/Network/RTP</class>
- <description>Implement an RTP bin</description>
- <author>Wim Taymans <wim.taymans@gmail.com></author>
- <pads>
- <caps>
- <name>send_rtp_src_%d</name>
- <direction>source</direction>
- <presence>sometimes</presence>
- <details>application/x-rtp</details>
- </caps>
- <caps>
- <name>send_rtcp_src_%d</name>
- <direction>source</direction>
- <presence>request</presence>
- <details>application/x-rtcp</details>
- </caps>
- <caps>
- <name>recv_rtp_src_%d_%d_%d</name>
- <direction>source</direction>
- <presence>sometimes</presence>
- <details>application/x-rtp</details>
- </caps>
- <caps>
- <name>send_rtp_sink_%d</name>
- <direction>sink</direction>
- <presence>request</presence>
- <details>application/x-rtp</details>
- </caps>
- <caps>
- <name>recv_rtcp_sink_%d</name>
- <direction>sink</direction>
- <presence>request</presence>
- <details>application/x-rtcp</details>
- </caps>
- <caps>
- <name>recv_rtp_sink_%d</name>
- <direction>sink</direction>
- <presence>request</presence>
- <details>application/x-rtp</details>
- </caps>
- </pads>
- </element>
- <element>
- <name>gstrtpjitterbuffer</name>
- <longname>RTP packet jitter-buffer</longname>
- <class>Filter/Network/RTP</class>
- <description>A buffer that deals with network jitter and other transmission faults</description>
- <author>Philippe Kalaf <philippe.kalaf@collabora.co.uk>, Wim Taymans <wim.taymans@gmail.com></author>
- <pads>
- <caps>
- <name>sink_rtcp</name>
- <direction>sink</direction>
- <presence>request</presence>
- <details>application/x-rtcp</details>
- </caps>
- <caps>
- <name>sink</name>
- <direction>sink</direction>
- <presence>always</presence>
- <details>application/x-rtp, clock-rate=(int)[ 1, 2147483647 ]</details>
- </caps>
- <caps>
- <name>src</name>
- <direction>source</direction>
- <presence>always</presence>
- <details>application/x-rtp</details>
- </caps>
- </pads>
- </element>
- <element>
- <name>gstrtpptdemux</name>
- <longname>RTP Demux</longname>
- <class>Demux/Network/RTP</class>
- <description>Parses codec streams transmitted in the same RTP session</description>
- <author>Kai Vehmanen <kai.vehmanen@nokia.com></author>
- <pads>
- <caps>
- <name>src_%d</name>
- <direction>source</direction>
- <presence>sometimes</presence>
- <details>application/x-rtp, payload=(int)[ 0, 255 ]</details>
- </caps>
- <caps>
- <name>sink</name>
- <direction>sink</direction>
- <presence>always</presence>
- <details>application/x-rtp</details>
- </caps>
- </pads>
- </element>
- <element>
- <name>gstrtpsession</name>
- <longname>RTP Session</longname>
- <class>Filter/Network/RTP</class>
- <description>Implement an RTP session</description>
- <author>Wim Taymans <wim.taymans@gmail.com></author>
- <pads>
- <caps>
- <name>send_rtcp_src</name>
- <direction>source</direction>
- <presence>request</presence>
- <details>application/x-rtcp</details>
- </caps>
- <caps>
- <name>send_rtp_src</name>
- <direction>source</direction>
- <presence>sometimes</presence>
- <details>application/x-rtp</details>
- </caps>
- <caps>
- <name>sync_src</name>
- <direction>source</direction>
- <presence>sometimes</presence>
- <details>application/x-rtcp</details>
- </caps>
- <caps>
- <name>recv_rtp_src</name>
- <direction>source</direction>
- <presence>sometimes</presence>
- <details>application/x-rtp</details>
- </caps>
- <caps>
- <name>send_rtp_sink</name>
- <direction>sink</direction>
- <presence>request</presence>
- <details>application/x-rtp</details>
- </caps>
- <caps>
- <name>recv_rtcp_sink</name>
- <direction>sink</direction>
- <presence>request</presence>
- <details>application/x-rtcp</details>
- </caps>
- <caps>
- <name>recv_rtp_sink</name>
- <direction>sink</direction>
- <presence>request</presence>
- <details>application/x-rtp</details>
- </caps>
- </pads>
- </element>
- <element>
- <name>gstrtpssrcdemux</name>
- <longname>RTP SSRC Demux</longname>
- <class>Demux/Network/RTP</class>
- <description>Splits RTP streams based on the SSRC</description>
- <author>Wim Taymans <wim.taymans@gmail.com></author>
- <pads>
- <caps>
- <name>rtcp_src_%d</name>
- <direction>source</direction>
- <presence>sometimes</presence>
- <details>application/x-rtcp</details>
- </caps>
- <caps>
- <name>src_%d</name>
- <direction>source</direction>
- <presence>sometimes</presence>
- <details>application/x-rtp</details>
- </caps>
- <caps>
- <name>rtcp_sink</name>
- <direction>sink</direction>
- <presence>always</presence>
- <details>application/x-rtcp</details>
- </caps>
- <caps>
- <name>sink</name>
- <direction>sink</direction>
- <presence>always</presence>
- <details>application/x-rtp</details>
- </caps>
- </pads>
- </element>
- </elements>
-</plugin>
\ No newline at end of file
%{_libdir}/gstreamer-%{majorminor}/libgstreal.so
%{_libdir}/gstreamer-%{majorminor}/libgstmve.so
%{_libdir}/gstreamer-%{majorminor}/libgstmpegvideoparse.so
-%{_libdir}/gstreamer-%{majorminor}/libgstrtpmanager.so
%{_libdir}/gstreamer-%{majorminor}/libgstbayer.so
%{_libdir}/gstreamer-%{majorminor}/libgstdvdspu.so
%{_libdir}/gstreamer-%{majorminor}/libgstfestival.so
+++ /dev/null
-gstrtpbin-marshal.h
-gstrtpbin-marshal.c
+++ /dev/null
-plugin_LTLIBRARIES = libgstrtpmanager.la
-
-glib_enum_define = GST_RTP_BIN
-glib_enum_prefix = gst_rtp_bin
-
-include $(top_srcdir)/common/glib-gen.mak
-
-built_sources = gstrtpbin-marshal.c
-built_headers = gstrtpbin-marshal.h
-
-BUILT_SOURCES = $(built_sources) $(built_headers)
-
-libgstrtpmanager_la_SOURCES = gstrtpmanager.c \
- gstrtpbin.c \
- gstrtpjitterbuffer.c \
- gstrtpptdemux.c \
- gstrtpssrcdemux.c \
- rtpjitterbuffer.c \
- rtpsession.c \
- rtpsource.c \
- rtpstats.c \
- gstrtpsession.c
-
-nodist_libgstrtpmanager_la_SOURCES = \
- $(built_sources)
-
-noinst_HEADERS = gstrtpbin.h \
- gstrtpjitterbuffer.h \
- gstrtpptdemux.h \
- gstrtpssrcdemux.h \
- rtpjitterbuffer.h \
- rtpsession.h \
- rtpsource.h \
- rtpstats.h \
- gstrtpsession.h
-
-libgstrtpmanager_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) \
- $(ERROR_CFLAGS)
-libgstrtpmanager_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) \
- -lgstnetbuffer-@GST_MAJORMINOR@ -lgstrtp-@GST_MAJORMINOR@ \
- $(GST_BASE_LIBS) $(GST_LIBS_LIBS)
-libgstrtpmanager_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
-libgstrtpmanager_la_LIBTOOLFLAGS = --tag=disable-static
-
-CLEANFILES = $(BUILT_SOURCES)
-
-EXTRA_DIST = gstrtpbin-marshal.list
-
-
+++ /dev/null
-UINT:UINT
-BOXED:UINT
-BOXED:UINT,UINT
-OBJECT:UINT
-VOID:UINT,OBJECT
-VOID:UINT
-VOID:UINT,UINT
-VOID:OBJECT,OBJECT
+++ /dev/null
-/* GStreamer
- * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-/**
- * SECTION:element-gstrtpbin
- * @see_also: gstrtpjitterbuffer, gstrtpsession, gstrtpptdemux, gstrtpssrcdemux
- *
- * RTP bin combines the functions of #GstRtpSession, #GstRtpsSrcDemux,
- * #GstRtpJitterBuffer and #GstRtpPtDemux in one element. It allows for multiple
- * RTP sessions that will be synchronized together using RTCP SR packets.
- *
- * #GstRtpBin is configured with a number of request pads that define the
- * functionality that is activated, similar to the #GstRtpSession element.
- *
- * To use #GstRtpBin as an RTP receiver, request a recv_rtp_sink_%%d pad. The session
- * number must be specified in the pad name.
- * Data received on the recv_rtp_sink_%%d pad will be processed in the gstrtpsession
- * manager and after being validated forwarded on #GstRtpsSrcDemux element. Each
- * RTP stream is demuxed based on the SSRC and send to a #GstRtpJitterBuffer. After
- * the packets are released from the jitterbuffer, they will be forwarded to a
- * #GstRtpsSrcDemux element. The #GstRtpsSrcDemux element will demux the packets based
- * on the payload type and will create a unique pad recv_rtp_src_%%d_%%d_%%d on
- * gstrtpbin with the session number, SSRC and payload type respectively as the pad
- * name.
- *
- * To also use #GstRtpBin as an RTCP receiver, request a recv_rtcp_sink_%%d pad. The
- * session number must be specified in the pad name.
- *
- * If you want the session manager to generate and send RTCP packets, request
- * the send_rtcp_src_%%d pad with the session number in the pad name. Packet pushed
- * on this pad contain SR/RR RTCP reports that should be sent to all participants
- * in the session.
- *
- * To use #GstRtpBin as a sender, request a send_rtp_sink_%%d pad, which will
- * automatically create a send_rtp_src_%%d pad. If the session number is not provided,
- * the pad from the lowest available session will be returned. The session manager will modify the
- * SSRC in the RTP packets to its own SSRC and wil forward the packets on the
- * send_rtp_src_%%d pad after updating its internal state.
- *
- * The session manager needs the clock-rate of the payload types it is handling
- * and will signal the #GstRtpSession::request-pt-map signal when it needs such a
- * mapping. One can clear the cached values with the #GstRtpSession::clear-pt-map
- * signal.
- *
- * <refsect2>
- * <title>Example pipelines</title>
- * |[
- * gst-launch udpsrc port=5000 caps="application/x-rtp, ..." ! .recv_rtp_sink_0 \
- * gstrtpbin ! rtptheoradepay ! theoradec ! xvimagesink
- * ]| Receive RTP data from port 5000 and send to the session 0 in gstrtpbin.
- * |[
- * gst-launch gstrtpbin name=rtpbin \
- * v4l2src ! ffmpegcolorspace ! ffenc_h263 ! rtph263ppay ! rtpbin.send_rtp_sink_0 \
- * rtpbin.send_rtp_src_0 ! udpsink port=5000 \
- * rtpbin.send_rtcp_src_0 ! udpsink port=5001 sync=false async=false \
- * udpsrc port=5005 ! rtpbin.recv_rtcp_sink_0 \
- * audiotestsrc ! amrnbenc ! rtpamrpay ! rtpbin.send_rtp_sink_1 \
- * rtpbin.send_rtp_src_1 ! udpsink port=5002 \
- * rtpbin.send_rtcp_src_1 ! udpsink port=5003 sync=false async=false \
- * udpsrc port=5007 ! rtpbin.recv_rtcp_sink_1
- * ]| Encode and payload H263 video captured from a v4l2src. Encode and payload AMR
- * audio generated from audiotestsrc. The video is sent to session 0 in rtpbin
- * and the audio is sent to session 1. Video packets are sent on UDP port 5000
- * and audio packets on port 5002. The video RTCP packets for session 0 are sent
- * on port 5001 and the audio RTCP packets for session 0 are sent on port 5003.
- * RTCP packets for session 0 are received on port 5005 and RTCP for session 1
- * is received on port 5007. Since RTCP packets from the sender should be sent
- * as soon as possible and do not participate in preroll, sync=false and
- * async=false is configured on udpsink
- * |[
- * gst-launch -v gstrtpbin name=rtpbin \
- * udpsrc caps="application/x-rtp,media=(string)video,clock-rate=(int)90000,encoding-name=(string)H263-1998" \
- * port=5000 ! rtpbin.recv_rtp_sink_0 \
- * rtpbin. ! rtph263pdepay ! ffdec_h263 ! xvimagesink \
- * udpsrc port=5001 ! rtpbin.recv_rtcp_sink_0 \
- * rtpbin.send_rtcp_src_0 ! udpsink port=5005 sync=false async=false \
- * udpsrc caps="application/x-rtp,media=(string)audio,clock-rate=(int)8000,encoding-name=(string)AMR,encoding-params=(string)1,octet-align=(string)1" \
- * port=5002 ! rtpbin.recv_rtp_sink_1 \
- * rtpbin. ! rtpamrdepay ! amrnbdec ! alsasink \
- * udpsrc port=5003 ! rtpbin.recv_rtcp_sink_1 \
- * rtpbin.send_rtcp_src_1 ! udpsink port=5007 sync=false async=false
- * ]| Receive H263 on port 5000, send it through rtpbin in session 0, depayload,
- * decode and display the video.
- * Receive AMR on port 5002, send it through rtpbin in session 1, depayload,
- * decode and play the audio.
- * Receive server RTCP packets for session 0 on port 5001 and RTCP packets for
- * session 1 on port 5003. These packets will be used for session management and
- * synchronisation.
- * Send RTCP reports for session 0 on port 5005 and RTCP reports for session 1
- * on port 5007.
- * </refsect2>
- *
- * Last reviewed on 2007-08-30 (0.10.6)
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-#include <string.h>
-
-#include <gst/rtp/gstrtpbuffer.h>
-#include <gst/rtp/gstrtcpbuffer.h>
-
-#include "gstrtpbin-marshal.h"
-#include "gstrtpbin.h"
-#include "rtpsession.h"
-#include "gstrtpsession.h"
-#include "gstrtpjitterbuffer.h"
-
-GST_DEBUG_CATEGORY_STATIC (gst_rtp_bin_debug);
-#define GST_CAT_DEFAULT gst_rtp_bin_debug
-
-/* elementfactory information */
-static const GstElementDetails rtpbin_details = GST_ELEMENT_DETAILS ("RTP Bin",
- "Filter/Network/RTP",
- "Implement an RTP bin",
- "Wim Taymans <wim.taymans@gmail.com>");
-
-/* sink pads */
-static GstStaticPadTemplate rtpbin_recv_rtp_sink_template =
-GST_STATIC_PAD_TEMPLATE ("recv_rtp_sink_%d",
- GST_PAD_SINK,
- GST_PAD_REQUEST,
- GST_STATIC_CAPS ("application/x-rtp")
- );
-
-static GstStaticPadTemplate rtpbin_recv_rtcp_sink_template =
-GST_STATIC_PAD_TEMPLATE ("recv_rtcp_sink_%d",
- GST_PAD_SINK,
- GST_PAD_REQUEST,
- GST_STATIC_CAPS ("application/x-rtcp")
- );
-
-static GstStaticPadTemplate rtpbin_send_rtp_sink_template =
-GST_STATIC_PAD_TEMPLATE ("send_rtp_sink_%d",
- GST_PAD_SINK,
- GST_PAD_REQUEST,
- GST_STATIC_CAPS ("application/x-rtp")
- );
-
-/* src pads */
-static GstStaticPadTemplate rtpbin_recv_rtp_src_template =
-GST_STATIC_PAD_TEMPLATE ("recv_rtp_src_%d_%d_%d",
- GST_PAD_SRC,
- GST_PAD_SOMETIMES,
- GST_STATIC_CAPS ("application/x-rtp")
- );
-
-static GstStaticPadTemplate rtpbin_send_rtcp_src_template =
-GST_STATIC_PAD_TEMPLATE ("send_rtcp_src_%d",
- GST_PAD_SRC,
- GST_PAD_REQUEST,
- GST_STATIC_CAPS ("application/x-rtcp")
- );
-
-static GstStaticPadTemplate rtpbin_send_rtp_src_template =
-GST_STATIC_PAD_TEMPLATE ("send_rtp_src_%d",
- GST_PAD_SRC,
- GST_PAD_SOMETIMES,
- GST_STATIC_CAPS ("application/x-rtp")
- );
-
-#define GST_RTP_BIN_GET_PRIVATE(obj) \
- (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_RTP_BIN, GstRtpBinPrivate))
-
-#define GST_RTP_BIN_LOCK(bin) g_mutex_lock ((bin)->priv->bin_lock)
-#define GST_RTP_BIN_UNLOCK(bin) g_mutex_unlock ((bin)->priv->bin_lock)
-
-/* lock to protect dynamic callbacks, like pad-added and new ssrc. */
-#define GST_RTP_BIN_DYN_LOCK(bin) g_mutex_lock ((bin)->priv->dyn_lock)
-#define GST_RTP_BIN_DYN_UNLOCK(bin) g_mutex_unlock ((bin)->priv->dyn_lock)
-
-/* lock for shutdown */
-#define GST_RTP_BIN_SHUTDOWN_LOCK(bin,label) \
-G_STMT_START { \
- if (g_atomic_int_get (&bin->priv->shutdown)) \
- goto label; \
- GST_RTP_BIN_DYN_LOCK (bin); \
- if (g_atomic_int_get (&bin->priv->shutdown)) { \
- GST_RTP_BIN_DYN_UNLOCK (bin); \
- goto label; \
- } \
-} G_STMT_END
-
-/* unlock for shutdown */
-#define GST_RTP_BIN_SHUTDOWN_UNLOCK(bin) \
- GST_RTP_BIN_DYN_UNLOCK (bin); \
-
-struct _GstRtpBinPrivate
-{
- GMutex *bin_lock;
-
- /* lock protecting dynamic adding/removing */
- GMutex *dyn_lock;
-
- /* the time when we went to playing */
- GstClockTime ntp_ns_base;
-
- /* if we are shutting down or not */
- gint shutdown;
-};
-
-/* signals and args */
-enum
-{
- SIGNAL_REQUEST_PT_MAP,
- SIGNAL_CLEAR_PT_MAP,
- SIGNAL_RESET_SYNC,
- SIGNAL_GET_INTERNAL_SESSION,
-
- SIGNAL_ON_NEW_SSRC,
- SIGNAL_ON_SSRC_COLLISION,
- SIGNAL_ON_SSRC_VALIDATED,
- SIGNAL_ON_SSRC_ACTIVE,
- SIGNAL_ON_SSRC_SDES,
- SIGNAL_ON_BYE_SSRC,
- SIGNAL_ON_BYE_TIMEOUT,
- SIGNAL_ON_TIMEOUT,
- SIGNAL_ON_SENDER_TIMEOUT,
- SIGNAL_ON_NPT_STOP,
- LAST_SIGNAL
-};
-
-#define DEFAULT_LATENCY_MS 200
-#define DEFAULT_SDES NULL
-#define DEFAULT_DO_LOST FALSE
-
-enum
-{
- PROP_0,
- PROP_LATENCY,
- PROP_SDES,
- PROP_DO_LOST,
- PROP_LAST
-};
-
-/* helper objects */
-typedef struct _GstRtpBinSession GstRtpBinSession;
-typedef struct _GstRtpBinStream GstRtpBinStream;
-typedef struct _GstRtpBinClient GstRtpBinClient;
-
-static guint gst_rtp_bin_signals[LAST_SIGNAL] = { 0 };
-
-static GstCaps *pt_map_requested (GstElement * element, guint pt,
- GstRtpBinSession * session);
-static void free_stream (GstRtpBinStream * stream);
-
-/* Manages the RTP stream for one SSRC.
- *
- * We pipe the stream (comming from the SSRC demuxer) into a jitterbuffer.
- * If we see an SDES RTCP packet that links multiple SSRCs together based on a
- * common CNAME, we create a GstRtpBinClient structure to group the SSRCs
- * together (see below).
- */
-struct _GstRtpBinStream
-{
- /* the SSRC of this stream */
- guint32 ssrc;
-
- /* parent bin */
- GstRtpBin *bin;
-
- /* the session this SSRC belongs to */
- GstRtpBinSession *session;
-
- /* the jitterbuffer of the SSRC */
- GstElement *buffer;
- gulong buffer_handlesync_sig;
- gulong buffer_ptreq_sig;
- gulong buffer_ntpstop_sig;
-
- /* the PT demuxer of the SSRC */
- GstElement *demux;
- gulong demux_newpad_sig;
- gulong demux_padremoved_sig;
- gulong demux_ptreq_sig;
- gulong demux_pt_change_sig;
-
- /* if we have calculated a valid unix_delta for this stream */
- gboolean have_sync;
- /* mapping to local RTP and NTP time */
- gint64 unix_delta;
-};
-
-#define GST_RTP_SESSION_LOCK(sess) g_mutex_lock ((sess)->lock)
-#define GST_RTP_SESSION_UNLOCK(sess) g_mutex_unlock ((sess)->lock)
-
-/* Manages the receiving end of the packets.
- *
- * There is one such structure for each RTP session (audio/video/...).
- * We get the RTP/RTCP packets and stuff them into the session manager. From
- * there they are pushed into an SSRC demuxer that splits the stream based on
- * SSRC. Each of the SSRC streams go into their own jitterbuffer (managed with
- * the GstRtpBinStream above).
- */
-struct _GstRtpBinSession
-{
- /* session id */
- gint id;
- /* the parent bin */
- GstRtpBin *bin;
- /* the session element */
- GstElement *session;
- /* the SSRC demuxer */
- GstElement *demux;
- gulong demux_newpad_sig;
- gulong demux_padremoved_sig;
-
- GMutex *lock;
-
- /* list of GstRtpBinStream */
- GSList *streams;
-
- /* mapping of payload type to caps */
- GHashTable *ptmap;
-
- /* the pads of the session */
- GstPad *recv_rtp_sink;
- GstPad *recv_rtp_sink_ghost;
- GstPad *recv_rtp_src;
- GstPad *recv_rtcp_sink;
- GstPad *recv_rtcp_sink_ghost;
- GstPad *sync_src;
- GstPad *send_rtp_sink;
- GstPad *send_rtp_sink_ghost;
- GstPad *send_rtp_src;
- GstPad *send_rtp_src_ghost;
- GstPad *send_rtcp_src;
- GstPad *send_rtcp_src_ghost;
-};
-
-/* Manages the RTP streams that come from one client and should therefore be
- * synchronized.
- */
-struct _GstRtpBinClient
-{
- /* the common CNAME for the streams */
- gchar *cname;
- guint cname_len;
-
- /* the streams */
- guint nstreams;
- GSList *streams;
-};
-
-/* find a session with the given id. Must be called with RTP_BIN_LOCK */
-static GstRtpBinSession *
-find_session_by_id (GstRtpBin * rtpbin, gint id)
-{
- GSList *walk;
-
- for (walk = rtpbin->sessions; walk; walk = g_slist_next (walk)) {
- GstRtpBinSession *sess = (GstRtpBinSession *) walk->data;
-
- if (sess->id == id)
- return sess;
- }
- return NULL;
-}
-
-/* find a session with the given request pad. Must be called with RTP_BIN_LOCK */
-static GstRtpBinSession *
-find_session_by_pad (GstRtpBin * rtpbin, GstPad * pad)
-{
- GSList *walk;
-
- for (walk = rtpbin->sessions; walk; walk = g_slist_next (walk)) {
- GstRtpBinSession *sess = (GstRtpBinSession *) walk->data;
-
- if ((sess->recv_rtp_sink_ghost == pad) ||
- (sess->recv_rtcp_sink_ghost == pad) ||
- (sess->send_rtp_sink_ghost == pad)
- || (sess->send_rtcp_src_ghost == pad))
- return sess;
- }
- return NULL;
-}
-
-static void
-on_new_ssrc (GstElement * session, guint32 ssrc, GstRtpBinSession * sess)
-{
- g_signal_emit (sess->bin, gst_rtp_bin_signals[SIGNAL_ON_NEW_SSRC], 0,
- sess->id, ssrc);
-}
-
-static void
-on_ssrc_collision (GstElement * session, guint32 ssrc, GstRtpBinSession * sess)
-{
- g_signal_emit (sess->bin, gst_rtp_bin_signals[SIGNAL_ON_SSRC_COLLISION], 0,
- sess->id, ssrc);
-}
-
-static void
-on_ssrc_validated (GstElement * session, guint32 ssrc, GstRtpBinSession * sess)
-{
- g_signal_emit (sess->bin, gst_rtp_bin_signals[SIGNAL_ON_SSRC_VALIDATED], 0,
- sess->id, ssrc);
-}
-
-static void
-on_ssrc_active (GstElement * session, guint32 ssrc, GstRtpBinSession * sess)
-{
- g_signal_emit (sess->bin, gst_rtp_bin_signals[SIGNAL_ON_SSRC_ACTIVE], 0,
- sess->id, ssrc);
-}
-
-static void
-on_ssrc_sdes (GstElement * session, guint32 ssrc, GstRtpBinSession * sess)
-{
- g_signal_emit (sess->bin, gst_rtp_bin_signals[SIGNAL_ON_SSRC_SDES], 0,
- sess->id, ssrc);
-}
-
-static void
-on_bye_ssrc (GstElement * session, guint32 ssrc, GstRtpBinSession * sess)
-{
- g_signal_emit (sess->bin, gst_rtp_bin_signals[SIGNAL_ON_BYE_SSRC], 0,
- sess->id, ssrc);
-}
-
-static void
-on_bye_timeout (GstElement * session, guint32 ssrc, GstRtpBinSession * sess)
-{
- g_signal_emit (sess->bin, gst_rtp_bin_signals[SIGNAL_ON_BYE_TIMEOUT], 0,
- sess->id, ssrc);
-}
-
-static void
-on_timeout (GstElement * session, guint32 ssrc, GstRtpBinSession * sess)
-{
- g_signal_emit (sess->bin, gst_rtp_bin_signals[SIGNAL_ON_TIMEOUT], 0,
- sess->id, ssrc);
-}
-
-static void
-on_sender_timeout (GstElement * session, guint32 ssrc, GstRtpBinSession * sess)
-{
- g_signal_emit (sess->bin, gst_rtp_bin_signals[SIGNAL_ON_SENDER_TIMEOUT], 0,
- sess->id, ssrc);
-}
-
-static void
-on_npt_stop (GstElement * jbuf, GstRtpBinStream * stream)
-{
- g_signal_emit (stream->bin, gst_rtp_bin_signals[SIGNAL_ON_NPT_STOP], 0,
- stream->session->id, stream->ssrc);
-}
-
-/* must be called with the SESSION lock */
-static GstRtpBinStream *
-find_stream_by_ssrc (GstRtpBinSession * session, guint32 ssrc)
-{
- GSList *walk;
-
- for (walk = session->streams; walk; walk = g_slist_next (walk)) {
- GstRtpBinStream *stream = (GstRtpBinStream *) walk->data;
-
- if (stream->ssrc == ssrc)
- return stream;
- }
- return NULL;
-}
-
-static void
-ssrc_demux_pad_removed (GstElement * element, guint ssrc, GstPad * pad,
- GstRtpBinSession * session)
-{
- GstRtpBinStream *stream = NULL;
-
- GST_RTP_SESSION_LOCK (session);
- if ((stream = find_stream_by_ssrc (session, ssrc)))
- session->streams = g_slist_remove (session->streams, stream);
- GST_RTP_SESSION_UNLOCK (session);
-
- if (stream)
- free_stream (stream);
-}
-
-/* create a session with the given id. Must be called with RTP_BIN_LOCK */
-static GstRtpBinSession *
-create_session (GstRtpBin * rtpbin, gint id)
-{
- GstRtpBinSession *sess;
- GstElement *session, *demux;
- GstState target;
-
- if (!(session = gst_element_factory_make ("gstrtpsession", NULL)))
- goto no_session;
-
- if (!(demux = gst_element_factory_make ("gstrtpssrcdemux", NULL)))
- goto no_demux;
-
- sess = g_new0 (GstRtpBinSession, 1);
- sess->lock = g_mutex_new ();
- sess->id = id;
- sess->bin = rtpbin;
- sess->session = session;
- sess->demux = demux;
- sess->ptmap = g_hash_table_new_full (NULL, NULL, NULL,
- (GDestroyNotify) gst_caps_unref);
- rtpbin->sessions = g_slist_prepend (rtpbin->sessions, sess);
-
- /* set NTP base or new session */
- g_object_set (session, "ntp-ns-base", rtpbin->priv->ntp_ns_base, NULL);
- /* configure SDES items */
- GST_OBJECT_LOCK (rtpbin);
- g_object_set (session, "sdes", rtpbin->sdes, NULL);
- GST_OBJECT_UNLOCK (rtpbin);
-
- /* provide clock_rate to the session manager when needed */
- g_signal_connect (session, "request-pt-map",
- (GCallback) pt_map_requested, sess);
-
- g_signal_connect (sess->session, "on-new-ssrc",
- (GCallback) on_new_ssrc, sess);
- g_signal_connect (sess->session, "on-ssrc-collision",
- (GCallback) on_ssrc_collision, sess);
- g_signal_connect (sess->session, "on-ssrc-validated",
- (GCallback) on_ssrc_validated, sess);
- g_signal_connect (sess->session, "on-ssrc-active",
- (GCallback) on_ssrc_active, sess);
- g_signal_connect (sess->session, "on-ssrc-sdes",
- (GCallback) on_ssrc_sdes, sess);
- g_signal_connect (sess->session, "on-bye-ssrc",
- (GCallback) on_bye_ssrc, sess);
- g_signal_connect (sess->session, "on-bye-timeout",
- (GCallback) on_bye_timeout, sess);
- g_signal_connect (sess->session, "on-timeout", (GCallback) on_timeout, sess);
- g_signal_connect (sess->session, "on-sender-timeout",
- (GCallback) on_sender_timeout, sess);
-
- gst_bin_add (GST_BIN_CAST (rtpbin), session);
- gst_bin_add (GST_BIN_CAST (rtpbin), demux);
-
- GST_OBJECT_LOCK (rtpbin);
- target = GST_STATE_TARGET (rtpbin);
- GST_OBJECT_UNLOCK (rtpbin);
-
- /* change state only to what's needed */
- gst_element_set_state (demux, target);
- gst_element_set_state (session, target);
-
- return sess;
-
- /* ERRORS */
-no_session:
- {
- g_warning ("gstrtpbin: could not create gstrtpsession element");
- return NULL;
- }
-no_demux:
- {
- gst_object_unref (session);
- g_warning ("gstrtpbin: could not create gstrtpssrcdemux element");
- return NULL;
- }
-}
-
-static void
-free_session (GstRtpBinSession * sess, GstRtpBin * bin)
-{
- GST_DEBUG_OBJECT (bin, "freeing session %p", sess);
-
- gst_element_set_state (sess->demux, GST_STATE_NULL);
- gst_element_set_state (sess->session, GST_STATE_NULL);
-
- if (sess->recv_rtp_sink != NULL) {
- gst_element_release_request_pad (sess->session, sess->recv_rtp_sink);
- gst_object_unref (sess->recv_rtp_sink);
- }
- if (sess->recv_rtp_src != NULL)
- gst_object_unref (sess->recv_rtp_src);
- if (sess->recv_rtcp_sink != NULL) {
- gst_element_release_request_pad (sess->session, sess->recv_rtcp_sink);
- gst_object_unref (sess->recv_rtcp_sink);
- }
- if (sess->sync_src != NULL)
- gst_object_unref (sess->sync_src);
- if (sess->send_rtp_sink != NULL) {
- gst_element_release_request_pad (sess->session, sess->send_rtp_sink);
- gst_object_unref (sess->send_rtp_sink);
- }
- if (sess->send_rtp_src != NULL)
- gst_object_unref (sess->send_rtp_src);
- if (sess->send_rtcp_src != NULL) {
- gst_element_release_request_pad (sess->session, sess->send_rtcp_src);
- gst_object_unref (sess->send_rtcp_src);
- }
-
- gst_bin_remove (GST_BIN_CAST (bin), sess->session);
- gst_bin_remove (GST_BIN_CAST (bin), sess->demux);
-
- g_slist_foreach (sess->streams, (GFunc) free_stream, NULL);
- g_slist_free (sess->streams);
-
- g_mutex_free (sess->lock);
- g_hash_table_destroy (sess->ptmap);
-
- g_free (sess);
-}
-
-/* get the payload type caps for the specific payload @pt in @session */
-static GstCaps *
-get_pt_map (GstRtpBinSession * session, guint pt)
-{
- GstCaps *caps = NULL;
- GstRtpBin *bin;
- GValue ret = { 0 };
- GValue args[3] = { {0}, {0}, {0} };
-
- GST_DEBUG ("searching pt %d in cache", pt);
-
- GST_RTP_SESSION_LOCK (session);
-
- /* first look in the cache */
- caps = g_hash_table_lookup (session->ptmap, GINT_TO_POINTER (pt));
- if (caps) {
- gst_caps_ref (caps);
- goto done;
- }
-
- bin = session->bin;
-
- GST_DEBUG ("emiting signal for pt %d in session %d", pt, session->id);
-
- /* not in cache, send signal to request caps */
- g_value_init (&args[0], GST_TYPE_ELEMENT);
- g_value_set_object (&args[0], bin);
- g_value_init (&args[1], G_TYPE_UINT);
- g_value_set_uint (&args[1], session->id);
- g_value_init (&args[2], G_TYPE_UINT);
- g_value_set_uint (&args[2], pt);
-
- g_value_init (&ret, GST_TYPE_CAPS);
- g_value_set_boxed (&ret, NULL);
-
- GST_RTP_SESSION_UNLOCK (session);
-
- g_signal_emitv (args, gst_rtp_bin_signals[SIGNAL_REQUEST_PT_MAP], 0, &ret);
-
- GST_RTP_SESSION_LOCK (session);
-
- g_value_unset (&args[0]);
- g_value_unset (&args[1]);
- g_value_unset (&args[2]);
-
- /* look in the cache again because we let the lock go */
- caps = g_hash_table_lookup (session->ptmap, GINT_TO_POINTER (pt));
- if (caps) {
- gst_caps_ref (caps);
- g_value_unset (&ret);
- goto done;
- }
-
- caps = (GstCaps *) g_value_dup_boxed (&ret);
- g_value_unset (&ret);
- if (!caps)
- goto no_caps;
-
- GST_DEBUG ("caching pt %d as %" GST_PTR_FORMAT, pt, caps);
-
- /* store in cache, take additional ref */
- g_hash_table_insert (session->ptmap, GINT_TO_POINTER (pt),
- gst_caps_ref (caps));
-
-done:
- GST_RTP_SESSION_UNLOCK (session);
-
- return caps;
-
- /* ERRORS */
-no_caps:
- {
- GST_RTP_SESSION_UNLOCK (session);
- GST_DEBUG ("no pt map could be obtained");
- return NULL;
- }
-}
-
-static gboolean
-return_true (gpointer key, gpointer value, gpointer user_data)
-{
- return TRUE;
-}
-
-static void
-gst_rtp_bin_reset_sync (GstRtpBin * rtpbin)
-{
- GSList *clients, *streams;
-
- GST_DEBUG_OBJECT (rtpbin, "Reset sync on all clients");
-
- GST_RTP_BIN_LOCK (rtpbin);
- for (clients = rtpbin->clients; clients; clients = g_slist_next (clients)) {
- GstRtpBinClient *client = (GstRtpBinClient *) clients->data;
-
- /* reset sync on all streams for this client */
- for (streams = client->streams; streams; streams = g_slist_next (streams)) {
- GstRtpBinStream *stream = (GstRtpBinStream *) streams->data;
-
- /* make use require a new SR packet for this stream before we attempt new
- * lip-sync */
- stream->have_sync = FALSE;
- stream->unix_delta = 0;
- }
- }
- GST_RTP_BIN_UNLOCK (rtpbin);
-}
-
-static void
-gst_rtp_bin_clear_pt_map (GstRtpBin * bin)
-{
- GSList *sessions, *streams;
-
- GST_RTP_BIN_LOCK (bin);
- GST_DEBUG_OBJECT (bin, "clearing pt map");
- for (sessions = bin->sessions; sessions; sessions = g_slist_next (sessions)) {
- GstRtpBinSession *session = (GstRtpBinSession *) sessions->data;
-
- GST_DEBUG_OBJECT (bin, "clearing session %p", session);
- g_signal_emit_by_name (session->session, "clear-pt-map", NULL);
-
- GST_RTP_SESSION_LOCK (session);
- g_hash_table_foreach_remove (session->ptmap, return_true, NULL);
-
- for (streams = session->streams; streams; streams = g_slist_next (streams)) {
- GstRtpBinStream *stream = (GstRtpBinStream *) streams->data;
-
- GST_DEBUG_OBJECT (bin, "clearing stream %p", stream);
- g_signal_emit_by_name (stream->buffer, "clear-pt-map", NULL);
- g_signal_emit_by_name (stream->demux, "clear-pt-map", NULL);
- }
- GST_RTP_SESSION_UNLOCK (session);
- }
- GST_RTP_BIN_UNLOCK (bin);
-
- /* reset sync too */
- gst_rtp_bin_reset_sync (bin);
-}
-
-static RTPSession *
-gst_rtp_bin_get_internal_session (GstRtpBin * bin, guint session_id)
-{
- RTPSession *internal_session = NULL;
- GstRtpBinSession *session;
-
- GST_RTP_BIN_LOCK (bin);
- GST_DEBUG_OBJECT (bin, "retrieving internal RTPSession object, index: %d",
- session_id);
- session = find_session_by_id (bin, (gint) session_id);
- if (session) {
- g_object_get (session->session, "internal-session", &internal_session,
- NULL);
- }
- GST_RTP_BIN_UNLOCK (bin);
-
- return internal_session;
-}
-
-static void
-gst_rtp_bin_propagate_property_to_jitterbuffer (GstRtpBin * bin,
- const gchar * name, const GValue * value)
-{
- GSList *sessions, *streams;
-
- GST_RTP_BIN_LOCK (bin);
- for (sessions = bin->sessions; sessions; sessions = g_slist_next (sessions)) {
- GstRtpBinSession *session = (GstRtpBinSession *) sessions->data;
-
- GST_RTP_SESSION_LOCK (session);
- for (streams = session->streams; streams; streams = g_slist_next (streams)) {
- GstRtpBinStream *stream = (GstRtpBinStream *) streams->data;
-
- g_object_set_property (G_OBJECT (stream->buffer), name, value);
- }
- GST_RTP_SESSION_UNLOCK (session);
- }
- GST_RTP_BIN_UNLOCK (bin);
-}
-
-/* get a client with the given SDES name. Must be called with RTP_BIN_LOCK */
-static GstRtpBinClient *
-get_client (GstRtpBin * bin, guint8 len, guint8 * data, gboolean * created)
-{
- GstRtpBinClient *result = NULL;
- GSList *walk;
-
- for (walk = bin->clients; walk; walk = g_slist_next (walk)) {
- GstRtpBinClient *client = (GstRtpBinClient *) walk->data;
-
- if (len != client->cname_len)
- continue;
-
- if (!strncmp ((gchar *) data, client->cname, client->cname_len)) {
- GST_DEBUG_OBJECT (bin, "found existing client %p with CNAME %s", client,
- client->cname);
- result = client;
- break;
- }
- }
-
- /* nothing found, create one */
- if (result == NULL) {
- result = g_new0 (GstRtpBinClient, 1);
- result->cname = g_strndup ((gchar *) data, len);
- result->cname_len = len;
- bin->clients = g_slist_prepend (bin->clients, result);
- GST_DEBUG_OBJECT (bin, "created new client %p with CNAME %s", result,
- result->cname);
- }
- return result;
-}
-
-static void
-free_client (GstRtpBinClient * client, GstRtpBin * bin)
-{
- GST_DEBUG_OBJECT (bin, "freeing client %p", client);
- g_slist_free (client->streams);
- g_free (client->cname);
- g_free (client);
-}
-
-/* associate a stream to the given CNAME. This will make sure all streams for
- * that CNAME are synchronized together.
- * Must be called with GST_RTP_BIN_LOCK */
-static void
-gst_rtp_bin_associate (GstRtpBin * bin, GstRtpBinStream * stream, guint8 len,
- guint8 * data, guint64 last_unix, guint64 last_extrtptime,
- guint64 clock_base, guint64 clock_base_time, guint clock_rate)
-{
- GstRtpBinClient *client;
- gboolean created;
- GSList *walk;
- guint64 local_unix;
- guint64 local_rtp;
-
- /* first find or create the CNAME */
- client = get_client (bin, len, data, &created);
-
- /* find stream in the client */
- for (walk = client->streams; walk; walk = g_slist_next (walk)) {
- GstRtpBinStream *ostream = (GstRtpBinStream *) walk->data;
-
- if (ostream == stream)
- break;
- }
- /* not found, add it to the list */
- if (walk == NULL) {
- GST_DEBUG_OBJECT (bin,
- "new association of SSRC %08x with client %p with CNAME %s",
- stream->ssrc, client, client->cname);
- client->streams = g_slist_prepend (client->streams, stream);
- client->nstreams++;
- } else {
- GST_DEBUG_OBJECT (bin,
- "found association of SSRC %08x with client %p with CNAME %s",
- stream->ssrc, client, client->cname);
- }
-
- /* take the extended rtptime we found in the SR packet and map it to the
- * local rtptime. The local rtp time is used to construct timestamps on the
- * buffers. */
- local_rtp = last_extrtptime - clock_base;
-
- GST_DEBUG_OBJECT (bin,
- "base %" G_GUINT64_FORMAT ", extrtptime %" G_GUINT64_FORMAT
- ", local RTP %" G_GUINT64_FORMAT ", clock-rate %d", clock_base,
- last_extrtptime, local_rtp, clock_rate);
-
- /* calculate local NTP time in gstreamer timestamp, we essentially perform the
- * same conversion that a jitterbuffer would use to convert an rtp timestamp
- * into a corresponding gstreamer timestamp. */
- local_unix = gst_util_uint64_scale_int (local_rtp, GST_SECOND, clock_rate);
- local_unix += clock_base_time;
-
- /* calculate delta between server and receiver. last_unix is created by
- * converting the ntptime in the last SR packet to a gstreamer timestamp. This
- * delta expresses the difference to our timeline and the server timeline. */
- stream->unix_delta = last_unix - local_unix;
- stream->have_sync = TRUE;
-
- GST_DEBUG_OBJECT (bin,
- "local UNIX %" G_GUINT64_FORMAT ", remote UNIX %" G_GUINT64_FORMAT
- ", delta %" G_GINT64_FORMAT, local_unix, last_unix, stream->unix_delta);
-
- /* recalc inter stream playout offset, but only if there is more than one
- * stream. */
- if (client->nstreams > 1) {
- gint64 min;
-
- /* calculate the min of all deltas, ignoring streams that did not yet have a
- * valid unix_delta because we did not yet receive an SR packet for those
- * streams.
- * We calculate the mininum because we would like to only apply positive
- * offsets to streams, delaying their playback instead of trying to speed up
- * other streams (which might be imposible when we have to create negative
- * latencies).
- * The stream that has the smallest diff is selected as the reference stream,
- * all other streams will have a positive offset to this difference. */
- min = G_MAXINT64;
- for (walk = client->streams; walk; walk = g_slist_next (walk)) {
- GstRtpBinStream *ostream = (GstRtpBinStream *) walk->data;
-
- if (!ostream->have_sync)
- continue;
-
- if (ostream->unix_delta < min)
- min = ostream->unix_delta;
- }
-
- GST_DEBUG_OBJECT (bin, "client %p min delta %" G_GINT64_FORMAT, client,
- min);
-
- /* calculate offsets for each stream */
- for (walk = client->streams; walk; walk = g_slist_next (walk)) {
- GstRtpBinStream *ostream = (GstRtpBinStream *) walk->data;
- gint64 ts_offset, prev_ts_offset;
-
- /* ignore streams for which we didn't receive an SR packet yet, we
- * can't synchronize them yet. We can however sync other streams just
- * fine. */
- if (!ostream->have_sync)
- continue;
-
- /* calculate offset to our reference stream, this should always give a
- * positive number. */
- ts_offset = ostream->unix_delta - min;
-
- g_object_get (ostream->buffer, "ts-offset", &prev_ts_offset, NULL);
-
- /* delta changed, see how much */
- if (prev_ts_offset != ts_offset) {
- gint64 diff;
-
- if (prev_ts_offset > ts_offset)
- diff = prev_ts_offset - ts_offset;
- else
- diff = ts_offset - prev_ts_offset;
-
- GST_DEBUG_OBJECT (bin,
- "ts-offset %" G_GUINT64_FORMAT ", prev %" G_GUINT64_FORMAT
- ", diff: %" G_GINT64_FORMAT, ts_offset, prev_ts_offset, diff);
-
- /* only change diff when it changed more than 4 milliseconds. This
- * compensates for rounding errors in NTP to RTP timestamp
- * conversions */
- if (diff > 4 * GST_MSECOND && diff < (3 * GST_SECOND)) {
- g_object_set (ostream->buffer, "ts-offset", ts_offset, NULL);
- }
- }
- GST_DEBUG_OBJECT (bin, "stream SSRC %08x, delta %" G_GINT64_FORMAT,
- ostream->ssrc, ts_offset);
- }
- }
- return;
-}
-
-#define GST_RTCP_BUFFER_FOR_PACKETS(b,buffer,packet) \
- for ((b) = gst_rtcp_buffer_get_first_packet ((buffer), (packet)); (b); \
- (b) = gst_rtcp_packet_move_to_next ((packet)))
-
-#define GST_RTCP_SDES_FOR_ITEMS(b,packet) \
- for ((b) = gst_rtcp_packet_sdes_first_item ((packet)); (b); \
- (b) = gst_rtcp_packet_sdes_next_item ((packet)))
-
-#define GST_RTCP_SDES_FOR_ENTRIES(b,packet) \
- for ((b) = gst_rtcp_packet_sdes_first_entry ((packet)); (b); \
- (b) = gst_rtcp_packet_sdes_next_entry ((packet)))
-
-static void
-gst_rtp_bin_handle_sync (GstElement * jitterbuffer, GstStructure * s,
- GstRtpBinStream * stream)
-{
- GstRtpBin *bin;
- GstRTCPPacket packet;
- guint32 ssrc;
- guint64 ntptime;
- gboolean have_sr, have_sdes;
- gboolean more;
- guint64 clock_base;
- guint64 clock_base_time;
- guint clock_rate;
- guint64 extrtptime;
- GstBuffer *buffer;
-
- bin = stream->bin;
-
- GST_DEBUG_OBJECT (bin, "sync handler called");
-
- /* get the last relation between the rtp timestamps and the gstreamer
- * timestamps. We get this info directly from the jitterbuffer which
- * constructs gstreamer timestamps from rtp timestamps and so it know exactly
- * what the current situation is. */
- clock_base = g_value_get_uint64 (gst_structure_get_value (s, "base-rtptime"));
- clock_base_time =
- g_value_get_uint64 (gst_structure_get_value (s, "base-time"));
- clock_rate = g_value_get_uint (gst_structure_get_value (s, "clock-rate"));
- extrtptime =
- g_value_get_uint64 (gst_structure_get_value (s, "sr-ext-rtptime"));
- buffer = gst_value_get_buffer (gst_structure_get_value (s, "sr-buffer"));
-
- have_sr = FALSE;
- have_sdes = FALSE;
- GST_RTCP_BUFFER_FOR_PACKETS (more, buffer, &packet) {
- /* first packet must be SR or RR or else the validate would have failed */
- switch (gst_rtcp_packet_get_type (&packet)) {
- case GST_RTCP_TYPE_SR:
- /* only parse first. There is only supposed to be one SR in the packet
- * but we will deal with malformed packets gracefully */
- if (have_sr)
- break;
- /* get NTP and RTP times */
- gst_rtcp_packet_sr_get_sender_info (&packet, &ssrc, &ntptime, NULL,
- NULL, NULL);
-
- GST_DEBUG_OBJECT (bin, "received sync packet from SSRC %08x", ssrc);
- /* ignore SR that is not ours */
- if (ssrc != stream->ssrc)
- continue;
-
- have_sr = TRUE;
- break;
- case GST_RTCP_TYPE_SDES:
- {
- gboolean more_items, more_entries;
-
- /* only deal with first SDES, there is only supposed to be one SDES in
- * the RTCP packet but we deal with bad packets gracefully. Also bail
- * out if we have not seen an SR item yet. */
- if (have_sdes || !have_sr)
- break;
-
- GST_RTCP_SDES_FOR_ITEMS (more_items, &packet) {
- /* skip items that are not about the SSRC of the sender */
- if (gst_rtcp_packet_sdes_get_ssrc (&packet) != ssrc)
- continue;
-
- /* find the CNAME entry */
- GST_RTCP_SDES_FOR_ENTRIES (more_entries, &packet) {
- GstRTCPSDESType type;
- guint8 len;
- guint8 *data;
-
- gst_rtcp_packet_sdes_get_entry (&packet, &type, &len, &data);
-
- if (type == GST_RTCP_SDES_CNAME) {
- GST_RTP_BIN_LOCK (bin);
- /* associate the stream to CNAME */
- gst_rtp_bin_associate (bin, stream, len, data,
- gst_rtcp_ntp_to_unix (ntptime), extrtptime,
- clock_base, clock_base_time, clock_rate);
- GST_RTP_BIN_UNLOCK (bin);
- }
- }
- }
- have_sdes = TRUE;
- break;
- }
- default:
- /* we can ignore these packets */
- break;
- }
- }
-}
-
-/* create a new stream with @ssrc in @session. Must be called with
- * RTP_SESSION_LOCK. */
-static GstRtpBinStream *
-create_stream (GstRtpBinSession * session, guint32 ssrc)
-{
- GstElement *buffer, *demux;
- GstRtpBinStream *stream;
- GstRtpBin *rtpbin;
- GstState target;
-
- if (!(buffer = gst_element_factory_make ("gstrtpjitterbuffer", NULL)))
- goto no_jitterbuffer;
-
- if (!(demux = gst_element_factory_make ("gstrtpptdemux", NULL)))
- goto no_demux;
-
- rtpbin = session->bin;
-
- stream = g_new0 (GstRtpBinStream, 1);
- stream->ssrc = ssrc;
- stream->bin = rtpbin;
- stream->session = session;
- stream->buffer = buffer;
- stream->demux = demux;
- stream->have_sync = FALSE;
- stream->unix_delta = 0;
- session->streams = g_slist_prepend (session->streams, stream);
-
- /* provide clock_rate to the jitterbuffer when needed */
- stream->buffer_ptreq_sig = g_signal_connect (buffer, "request-pt-map",
- (GCallback) pt_map_requested, session);
- stream->buffer_ntpstop_sig = g_signal_connect (buffer, "on-npt-stop",
- (GCallback) on_npt_stop, stream);
-
- /* configure latency and packet lost */
- g_object_set (buffer, "latency", rtpbin->latency, NULL);
- g_object_set (buffer, "do-lost", rtpbin->do_lost, NULL);
-
- gst_bin_add (GST_BIN_CAST (rtpbin), demux);
- gst_bin_add (GST_BIN_CAST (rtpbin), buffer);
-
- /* link stuff */
- gst_element_link (buffer, demux);
-
- GST_OBJECT_LOCK (rtpbin);
- target = GST_STATE_TARGET (rtpbin);
- GST_OBJECT_UNLOCK (rtpbin);
-
- /* from sink to source */
- gst_element_set_state (demux, target);
- gst_element_set_state (buffer, target);
-
- return stream;
-
- /* ERRORS */
-no_jitterbuffer:
- {
- g_warning ("gstrtpbin: could not create gstrtpjitterbuffer element");
- return NULL;
- }
-no_demux:
- {
- gst_object_unref (buffer);
- g_warning ("gstrtpbin: could not create gstrtpptdemux element");
- return NULL;
- }
-}
-
-static void
-free_stream (GstRtpBinStream * stream)
-{
- GstRtpBinSession *session;
-
- session = stream->session;
-
- g_signal_handler_disconnect (stream->demux, stream->demux_newpad_sig);
- g_signal_handler_disconnect (stream->demux, stream->demux_ptreq_sig);
- g_signal_handler_disconnect (stream->buffer, stream->buffer_handlesync_sig);
- g_signal_handler_disconnect (stream->buffer, stream->buffer_ptreq_sig);
- g_signal_handler_disconnect (stream->buffer, stream->buffer_ntpstop_sig);
-
- gst_element_set_state (stream->demux, GST_STATE_NULL);
- gst_element_set_state (stream->buffer, GST_STATE_NULL);
-
- /* now remove this signal, we need this while going to NULL because it to
- * do some cleanups */
- g_signal_handler_disconnect (stream->demux, stream->demux_padremoved_sig);
-
- gst_bin_remove (GST_BIN_CAST (session->bin), stream->buffer);
- gst_bin_remove (GST_BIN_CAST (session->bin), stream->demux);
-
- g_free (stream);
-}
-
-/* GObject vmethods */
-static void gst_rtp_bin_dispose (GObject * object);
-static void gst_rtp_bin_finalize (GObject * object);
-static void gst_rtp_bin_set_property (GObject * object, guint prop_id,
- const GValue * value, GParamSpec * pspec);
-static void gst_rtp_bin_get_property (GObject * object, guint prop_id,
- GValue * value, GParamSpec * pspec);
-
-/* GstElement vmethods */
-static GstStateChangeReturn gst_rtp_bin_change_state (GstElement * element,
- GstStateChange transition);
-static GstPad *gst_rtp_bin_request_new_pad (GstElement * element,
- GstPadTemplate * templ, const gchar * name);
-static void gst_rtp_bin_release_pad (GstElement * element, GstPad * pad);
-static void gst_rtp_bin_handle_message (GstBin * bin, GstMessage * message);
-static void gst_rtp_bin_clear_pt_map (GstRtpBin * bin);
-
-GST_BOILERPLATE (GstRtpBin, gst_rtp_bin, GstBin, GST_TYPE_BIN);
-
-static void
-gst_rtp_bin_base_init (gpointer klass)
-{
- GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
-
- /* sink pads */
- gst_element_class_add_pad_template (element_class,
- gst_static_pad_template_get (&rtpbin_recv_rtp_sink_template));
- gst_element_class_add_pad_template (element_class,
- gst_static_pad_template_get (&rtpbin_recv_rtcp_sink_template));
- gst_element_class_add_pad_template (element_class,
- gst_static_pad_template_get (&rtpbin_send_rtp_sink_template));
-
- /* src pads */
- gst_element_class_add_pad_template (element_class,
- gst_static_pad_template_get (&rtpbin_recv_rtp_src_template));
- gst_element_class_add_pad_template (element_class,
- gst_static_pad_template_get (&rtpbin_send_rtcp_src_template));
- gst_element_class_add_pad_template (element_class,
- gst_static_pad_template_get (&rtpbin_send_rtp_src_template));
-
- gst_element_class_set_details (element_class, &rtpbin_details);
-}
-
-static void
-gst_rtp_bin_class_init (GstRtpBinClass * klass)
-{
- GObjectClass *gobject_class;
- GstElementClass *gstelement_class;
- GstBinClass *gstbin_class;
-
- gobject_class = (GObjectClass *) klass;
- gstelement_class = (GstElementClass *) klass;
- gstbin_class = (GstBinClass *) klass;
-
- g_type_class_add_private (klass, sizeof (GstRtpBinPrivate));
-
- gobject_class->dispose = gst_rtp_bin_dispose;
- gobject_class->finalize = gst_rtp_bin_finalize;
- gobject_class->set_property = gst_rtp_bin_set_property;
- gobject_class->get_property = gst_rtp_bin_get_property;
-
- g_object_class_install_property (gobject_class, PROP_LATENCY,
- g_param_spec_uint ("latency", "Buffer latency in ms",
- "Default amount of ms to buffer in the jitterbuffers", 0,
- G_MAXUINT, DEFAULT_LATENCY_MS, G_PARAM_READWRITE));
-
- /**
- * GstRtpBin::request-pt-map:
- * @rtpbin: the object which received the signal
- * @session: the session
- * @pt: the pt
- *
- * Request the payload type as #GstCaps for @pt in @session.
- */
- gst_rtp_bin_signals[SIGNAL_REQUEST_PT_MAP] =
- g_signal_new ("request-pt-map", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, request_pt_map),
- NULL, NULL, gst_rtp_bin_marshal_BOXED__UINT_UINT, GST_TYPE_CAPS, 2,
- G_TYPE_UINT, G_TYPE_UINT);
- /**
- * GstRtpBin::clear-pt-map:
- * @rtpbin: the object which received the signal
- *
- * Clear all previously cached pt-mapping obtained with
- * #GstRtpBin::request-pt-map.
- */
- gst_rtp_bin_signals[SIGNAL_CLEAR_PT_MAP] =
- g_signal_new ("clear-pt-map", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstRtpBinClass,
- clear_pt_map), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE,
- 0, G_TYPE_NONE);
- /**
- * GstRtpBin::reset-sync:
- * @rtpbin: the object which received the signal
- *
- * Reset all currently configured lip-sync parameters and require new SR
- * packets for all streams before lip-sync is attempted again.
- */
- gst_rtp_bin_signals[SIGNAL_RESET_SYNC] =
- g_signal_new ("reset-sync", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstRtpBinClass,
- reset_sync), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE,
- 0, G_TYPE_NONE);
-
- /**
- * GstRtpBin::get-internal-session:
- * @rtpbin: the object which received the signal
- * @id: the session id
- *
- * Request the internal RTPSession object as #GObject in session @id.
- */
- gst_rtp_bin_signals[SIGNAL_GET_INTERNAL_SESSION] =
- g_signal_new ("get-internal-session", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstRtpBinClass,
- get_internal_session), NULL, NULL, gst_rtp_bin_marshal_OBJECT__UINT,
- RTP_TYPE_SESSION, 1, G_TYPE_UINT);
-
- /**
- * GstRtpBin::on-new-ssrc:
- * @rtpbin: the object which received the signal
- * @session: the session
- * @ssrc: the SSRC
- *
- * Notify of a new SSRC that entered @session.
- */
- gst_rtp_bin_signals[SIGNAL_ON_NEW_SSRC] =
- g_signal_new ("on-new-ssrc", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, on_new_ssrc),
- NULL, NULL, gst_rtp_bin_marshal_VOID__UINT_UINT, G_TYPE_NONE, 2,
- G_TYPE_UINT, G_TYPE_UINT);
- /**
- * GstRtpBin::on-ssrc-collision:
- * @rtpbin: the object which received the signal
- * @session: the session
- * @ssrc: the SSRC
- *
- * Notify when we have an SSRC collision
- */
- gst_rtp_bin_signals[SIGNAL_ON_SSRC_COLLISION] =
- g_signal_new ("on-ssrc-collision", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, on_ssrc_collision),
- NULL, NULL, gst_rtp_bin_marshal_VOID__UINT_UINT, G_TYPE_NONE, 2,
- G_TYPE_UINT, G_TYPE_UINT);
- /**
- * GstRtpBin::on-ssrc-validated:
- * @rtpbin: the object which received the signal
- * @session: the session
- * @ssrc: the SSRC
- *
- * Notify of a new SSRC that became validated.
- */
- gst_rtp_bin_signals[SIGNAL_ON_SSRC_VALIDATED] =
- g_signal_new ("on-ssrc-validated", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, on_ssrc_validated),
- NULL, NULL, gst_rtp_bin_marshal_VOID__UINT_UINT, G_TYPE_NONE, 2,
- G_TYPE_UINT, G_TYPE_UINT);
- /**
- * GstRtpBin::on-ssrc-active:
- * @rtpbin: the object which received the signal
- * @session: the session
- * @ssrc: the SSRC
- *
- * Notify of a SSRC that is active, i.e., sending RTCP.
- */
- gst_rtp_bin_signals[SIGNAL_ON_SSRC_ACTIVE] =
- g_signal_new ("on-ssrc-active", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, on_ssrc_active),
- NULL, NULL, gst_rtp_bin_marshal_VOID__UINT_UINT, G_TYPE_NONE, 2,
- G_TYPE_UINT, G_TYPE_UINT);
- /**
- * GstRtpBin::on-ssrc-sdes:
- * @rtpbin: the object which received the signal
- * @session: the session
- * @ssrc: the SSRC
- *
- * Notify of a SSRC that is active, i.e., sending RTCP.
- */
- gst_rtp_bin_signals[SIGNAL_ON_SSRC_SDES] =
- g_signal_new ("on-ssrc-sdes", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, on_ssrc_sdes),
- NULL, NULL, gst_rtp_bin_marshal_VOID__UINT_UINT, G_TYPE_NONE, 2,
- G_TYPE_UINT, G_TYPE_UINT);
-
- /**
- * GstRtpBin::on-bye-ssrc:
- * @rtpbin: the object which received the signal
- * @session: the session
- * @ssrc: the SSRC
- *
- * Notify of an SSRC that became inactive because of a BYE packet.
- */
- gst_rtp_bin_signals[SIGNAL_ON_BYE_SSRC] =
- g_signal_new ("on-bye-ssrc", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, on_bye_ssrc),
- NULL, NULL, gst_rtp_bin_marshal_VOID__UINT_UINT, G_TYPE_NONE, 2,
- G_TYPE_UINT, G_TYPE_UINT);
- /**
- * GstRtpBin::on-bye-timeout:
- * @rtpbin: the object which received the signal
- * @session: the session
- * @ssrc: the SSRC
- *
- * Notify of an SSRC that has timed out because of BYE
- */
- gst_rtp_bin_signals[SIGNAL_ON_BYE_TIMEOUT] =
- g_signal_new ("on-bye-timeout", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, on_bye_timeout),
- NULL, NULL, gst_rtp_bin_marshal_VOID__UINT_UINT, G_TYPE_NONE, 2,
- G_TYPE_UINT, G_TYPE_UINT);
- /**
- * GstRtpBin::on-timeout:
- * @rtpbin: the object which received the signal
- * @session: the session
- * @ssrc: the SSRC
- *
- * Notify of an SSRC that has timed out
- */
- gst_rtp_bin_signals[SIGNAL_ON_TIMEOUT] =
- g_signal_new ("on-timeout", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, on_timeout),
- NULL, NULL, gst_rtp_bin_marshal_VOID__UINT_UINT, G_TYPE_NONE, 2,
- G_TYPE_UINT, G_TYPE_UINT);
- /**
- * GstRtpBin::on-sender-timeout:
- * @rtpbin: the object which received the signal
- * @session: the session
- * @ssrc: the SSRC
- *
- * Notify of a sender SSRC that has timed out and became a receiver
- */
- gst_rtp_bin_signals[SIGNAL_ON_SENDER_TIMEOUT] =
- g_signal_new ("on-sender-timeout", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, on_sender_timeout),
- NULL, NULL, gst_rtp_bin_marshal_VOID__UINT_UINT, G_TYPE_NONE, 2,
- G_TYPE_UINT, G_TYPE_UINT);
-
- /**
- * GstRtpBin::on-npt-stop:
- * @rtpbin: the object which received the signal
- * @session: the session
- * @ssrc: the SSRC
- *
- * Notify that SSRC sender has sent data up to the configured NPT stop time.
- */
- gst_rtp_bin_signals[SIGNAL_ON_NPT_STOP] =
- g_signal_new ("on-npt-stop", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpBinClass, on_npt_stop),
- NULL, NULL, gst_rtp_bin_marshal_VOID__UINT_UINT, G_TYPE_NONE, 2,
- G_TYPE_UINT, G_TYPE_UINT);
-
- g_object_class_install_property (gobject_class, PROP_SDES,
- g_param_spec_boxed ("sdes", "SDES",
- "The SDES items of this session",
- GST_TYPE_STRUCTURE, G_PARAM_READWRITE));
-
- g_object_class_install_property (gobject_class, PROP_DO_LOST,
- g_param_spec_boolean ("do-lost", "Do Lost",
- "Send an event downstream when a packet is lost", DEFAULT_DO_LOST,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-
- gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_rtp_bin_change_state);
- gstelement_class->request_new_pad =
- GST_DEBUG_FUNCPTR (gst_rtp_bin_request_new_pad);
- gstelement_class->release_pad = GST_DEBUG_FUNCPTR (gst_rtp_bin_release_pad);
-
- gstbin_class->handle_message = GST_DEBUG_FUNCPTR (gst_rtp_bin_handle_message);
-
- klass->clear_pt_map = GST_DEBUG_FUNCPTR (gst_rtp_bin_clear_pt_map);
- klass->reset_sync = GST_DEBUG_FUNCPTR (gst_rtp_bin_reset_sync);
- klass->get_internal_session =
- GST_DEBUG_FUNCPTR (gst_rtp_bin_get_internal_session);
-
- GST_DEBUG_CATEGORY_INIT (gst_rtp_bin_debug, "rtpbin", 0, "RTP bin");
-}
-
-static void
-gst_rtp_bin_init (GstRtpBin * rtpbin, GstRtpBinClass * klass)
-{
- gchar *str;
-
- rtpbin->priv = GST_RTP_BIN_GET_PRIVATE (rtpbin);
- rtpbin->priv->bin_lock = g_mutex_new ();
- rtpbin->priv->dyn_lock = g_mutex_new ();
-
- rtpbin->latency = DEFAULT_LATENCY_MS;
- rtpbin->do_lost = DEFAULT_DO_LOST;
-
- /* some default SDES entries */
- str = g_strdup_printf ("%s@%s", g_get_user_name (), g_get_host_name ());
- rtpbin->sdes = gst_structure_new ("application/x-rtp-source-sdes",
- "cname", G_TYPE_STRING, str,
- "name", G_TYPE_STRING, g_get_real_name (),
- "tool", G_TYPE_STRING, "GStreamer", NULL);
- g_free (str);
-}
-
-static void
-gst_rtp_bin_dispose (GObject * object)
-{
- GstRtpBin *rtpbin;
-
- rtpbin = GST_RTP_BIN (object);
-
- GST_DEBUG_OBJECT (object, "freeing sessions");
- g_slist_foreach (rtpbin->sessions, (GFunc) free_session, rtpbin);
- g_slist_free (rtpbin->sessions);
- rtpbin->sessions = NULL;
- GST_DEBUG_OBJECT (object, "freeing clients");
- g_slist_foreach (rtpbin->clients, (GFunc) free_client, rtpbin);
- g_slist_free (rtpbin->clients);
- rtpbin->clients = NULL;
-
- G_OBJECT_CLASS (parent_class)->dispose (object);
-}
-
-static void
-gst_rtp_bin_finalize (GObject * object)
-{
- GstRtpBin *rtpbin;
-
- rtpbin = GST_RTP_BIN (object);
-
- if (rtpbin->sdes)
- gst_structure_free (rtpbin->sdes);
-
- g_mutex_free (rtpbin->priv->bin_lock);
- g_mutex_free (rtpbin->priv->dyn_lock);
-
- G_OBJECT_CLASS (parent_class)->finalize (object);
-}
-
-
-static void
-gst_rtp_bin_set_sdes_struct (GstRtpBin * bin, const GstStructure * sdes)
-{
- GSList *item;
-
- if (sdes == NULL)
- return;
-
- GST_RTP_BIN_LOCK (bin);
-
- GST_OBJECT_LOCK (bin);
- if (bin->sdes)
- gst_structure_free (bin->sdes);
- bin->sdes = gst_structure_copy (sdes);
-
- /* store in all sessions */
- for (item = bin->sessions; item; item = g_slist_next (item))
- g_object_set (item->data, "sdes", sdes, NULL);
- GST_OBJECT_UNLOCK (bin);
-
- GST_RTP_BIN_UNLOCK (bin);
-}
-
-static GstStructure *
-gst_rtp_bin_get_sdes_struct (GstRtpBin * bin)
-{
- GstStructure *result;
-
- GST_OBJECT_LOCK (bin);
- result = gst_structure_copy (bin->sdes);
- GST_OBJECT_UNLOCK (bin);
-
- return result;
-}
-
-static void
-gst_rtp_bin_set_property (GObject * object, guint prop_id,
- const GValue * value, GParamSpec * pspec)
-{
- GstRtpBin *rtpbin;
-
- rtpbin = GST_RTP_BIN (object);
-
- switch (prop_id) {
- case PROP_LATENCY:
- GST_RTP_BIN_LOCK (rtpbin);
- rtpbin->latency = g_value_get_uint (value);
- GST_RTP_BIN_UNLOCK (rtpbin);
- /* propegate the property down to the jitterbuffer */
- gst_rtp_bin_propagate_property_to_jitterbuffer (rtpbin, "latency", value);
- break;
- case PROP_SDES:
- gst_rtp_bin_set_sdes_struct (rtpbin, g_value_get_boxed (value));
- break;
- case PROP_DO_LOST:
- GST_RTP_BIN_LOCK (rtpbin);
- rtpbin->do_lost = g_value_get_boolean (value);
- GST_RTP_BIN_UNLOCK (rtpbin);
- gst_rtp_bin_propagate_property_to_jitterbuffer (rtpbin, "do-lost", value);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-static void
-gst_rtp_bin_get_property (GObject * object, guint prop_id,
- GValue * value, GParamSpec * pspec)
-{
- GstRtpBin *rtpbin;
-
- rtpbin = GST_RTP_BIN (object);
-
- switch (prop_id) {
- case PROP_LATENCY:
- GST_RTP_BIN_LOCK (rtpbin);
- g_value_set_uint (value, rtpbin->latency);
- GST_RTP_BIN_UNLOCK (rtpbin);
- break;
- case PROP_SDES:
- g_value_take_boxed (value, gst_rtp_bin_get_sdes_struct (rtpbin));
- break;
- case PROP_DO_LOST:
- GST_RTP_BIN_LOCK (rtpbin);
- g_value_set_boolean (value, rtpbin->do_lost);
- GST_RTP_BIN_UNLOCK (rtpbin);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-static void
-gst_rtp_bin_handle_message (GstBin * bin, GstMessage * message)
-{
- GstRtpBin *rtpbin;
-
- rtpbin = GST_RTP_BIN (bin);
-
- switch (GST_MESSAGE_TYPE (message)) {
- case GST_MESSAGE_ELEMENT:
- {
- const GstStructure *s = gst_message_get_structure (message);
-
- /* we change the structure name and add the session ID to it */
- if (gst_structure_has_name (s, "application/x-rtp-source-sdes")) {
- GSList *walk;
-
- /* find the session, the message source has it */
- for (walk = rtpbin->sessions; walk; walk = g_slist_next (walk)) {
- GstRtpBinSession *sess = (GstRtpBinSession *) walk->data;
-
- /* if we found the session, change message. else we exit the loop and
- * leave the message unchanged */
- if (GST_OBJECT_CAST (sess->session) == GST_MESSAGE_SRC (message)) {
- message = gst_message_make_writable (message);
- s = gst_message_get_structure (message);
-
- gst_structure_set ((GstStructure *) s, "session", G_TYPE_UINT,
- sess->id, NULL);
- break;
- }
- }
- }
- /* fallthrough to forward the modified message to the parent */
- }
- default:
- {
- GST_BIN_CLASS (parent_class)->handle_message (bin, message);
- break;
- }
- }
-}
-
-static void
-calc_ntp_ns_base (GstRtpBin * bin)
-{
- GstClockTime now;
- GTimeVal current;
- GSList *walk;
-
- /* get the current time and convert it to NTP time in nanoseconds */
- g_get_current_time (¤t);
- now = GST_TIMEVAL_TO_TIME (current);
- now += (2208988800LL * GST_SECOND);
-
- GST_RTP_BIN_LOCK (bin);
- bin->priv->ntp_ns_base = now;
- for (walk = bin->sessions; walk; walk = g_slist_next (walk)) {
- GstRtpBinSession *session = (GstRtpBinSession *) walk->data;
-
- g_object_set (session->session, "ntp-ns-base", now, NULL);
- }
- GST_RTP_BIN_UNLOCK (bin);
-
- return;
-}
-
-static GstStateChangeReturn
-gst_rtp_bin_change_state (GstElement * element, GstStateChange transition)
-{
- GstStateChangeReturn res;
- GstRtpBin *rtpbin;
- GstRtpBinPrivate *priv;
-
- rtpbin = GST_RTP_BIN (element);
- priv = rtpbin->priv;
-
- switch (transition) {
- case GST_STATE_CHANGE_NULL_TO_READY:
- break;
- case GST_STATE_CHANGE_READY_TO_PAUSED:
- GST_LOG_OBJECT (rtpbin, "clearing shutdown flag");
- g_atomic_int_set (&priv->shutdown, 0);
- break;
- case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
- calc_ntp_ns_base (rtpbin);
- break;
- case GST_STATE_CHANGE_PAUSED_TO_READY:
- GST_LOG_OBJECT (rtpbin, "setting shutdown flag");
- g_atomic_int_set (&priv->shutdown, 1);
- /* wait for all callbacks to end by taking the lock. No new callbacks will
- * be able to happen as we set the shutdown flag. */
- GST_RTP_BIN_DYN_LOCK (rtpbin);
- GST_LOG_OBJECT (rtpbin, "dynamic lock taken, we can continue shutdown");
- GST_RTP_BIN_DYN_UNLOCK (rtpbin);
- break;
- default:
- break;
- }
-
- res = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
-
- switch (transition) {
- case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
- break;
- case GST_STATE_CHANGE_PAUSED_TO_READY:
- break;
- case GST_STATE_CHANGE_READY_TO_NULL:
- break;
- default:
- break;
- }
- return res;
-}
-
-/* a new pad (SSRC) was created in @session. This signal is emited from the
- * payload demuxer. */
-static void
-new_payload_found (GstElement * element, guint pt, GstPad * pad,
- GstRtpBinStream * stream)
-{
- GstRtpBin *rtpbin;
- GstElementClass *klass;
- GstPadTemplate *templ;
- gchar *padname;
- GstPad *gpad;
-
- rtpbin = stream->bin;
-
- GST_DEBUG ("new payload pad %d", pt);
-
- GST_RTP_BIN_SHUTDOWN_LOCK (rtpbin, shutdown);
-
- /* ghost the pad to the parent */
- klass = GST_ELEMENT_GET_CLASS (rtpbin);
- templ = gst_element_class_get_pad_template (klass, "recv_rtp_src_%d_%d_%d");
- padname = g_strdup_printf ("recv_rtp_src_%d_%u_%d",
- stream->session->id, stream->ssrc, pt);
- gpad = gst_ghost_pad_new_from_template (padname, pad, templ);
- g_free (padname);
- g_object_set_data (G_OBJECT (pad), "GstRTPBin.ghostpad", gpad);
-
- gst_pad_set_caps (gpad, GST_PAD_CAPS (pad));
- gst_pad_set_active (gpad, TRUE);
- gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), gpad);
- GST_RTP_BIN_SHUTDOWN_UNLOCK (rtpbin);
-
- return;
-
-shutdown:
- {
- GST_DEBUG ("ignoring, we are shutting down");
- return;
- }
-}
-
-static void
-payload_pad_removed (GstElement * element, GstPad * pad,
- GstRtpBinStream * stream)
-{
- GstRtpBin *rtpbin;
- GstPad *gpad;
-
- rtpbin = stream->bin;
-
- GST_DEBUG ("payload pad removed");
-
- GST_RTP_BIN_DYN_LOCK (rtpbin);
- if ((gpad = g_object_get_data (G_OBJECT (pad), "GstRTPBin.ghostpad"))) {
- g_object_set_data (G_OBJECT (pad), "GstRTPBin.ghostpad", NULL);
-
- gst_pad_set_active (gpad, FALSE);
- gst_element_remove_pad (GST_ELEMENT_CAST (rtpbin), gpad);
- }
- GST_RTP_BIN_DYN_UNLOCK (rtpbin);
-}
-
-static GstCaps *
-pt_map_requested (GstElement * element, guint pt, GstRtpBinSession * session)
-{
- GstRtpBin *rtpbin;
- GstCaps *caps;
-
- rtpbin = session->bin;
-
- GST_DEBUG_OBJECT (rtpbin, "payload map requested for pt %d in session %d", pt,
- session->id);
-
- caps = get_pt_map (session, pt);
- if (!caps)
- goto no_caps;
-
- return caps;
-
- /* ERRORS */
-no_caps:
- {
- GST_DEBUG_OBJECT (rtpbin, "could not get caps");
- return NULL;
- }
-}
-
-/* emited when caps changed for the session */
-static void
-caps_changed (GstPad * pad, GParamSpec * pspec, GstRtpBinSession * session)
-{
- GstRtpBin *bin;
- GstCaps *caps;
- gint payload;
- const GstStructure *s;
-
- bin = session->bin;
-
- g_object_get (pad, "caps", &caps, NULL);
-
- if (caps == NULL)
- return;
-
- GST_DEBUG_OBJECT (bin, "got caps %" GST_PTR_FORMAT, caps);
-
- s = gst_caps_get_structure (caps, 0);
-
- /* get payload, finish when it's not there */
- if (!gst_structure_get_int (s, "payload", &payload))
- return;
-
- GST_RTP_SESSION_LOCK (session);
- GST_DEBUG_OBJECT (bin, "insert caps for payload %d", payload);
- g_hash_table_insert (session->ptmap, GINT_TO_POINTER (payload), caps);
- GST_RTP_SESSION_UNLOCK (session);
-}
-
-/* a new pad (SSRC) was created in @session */
-static void
-new_ssrc_pad_found (GstElement * element, guint ssrc, GstPad * pad,
- GstRtpBinSession * session)
-{
- GstRtpBin *rtpbin;
- GstRtpBinStream *stream;
- GstPad *sinkpad, *srcpad;
- gchar *padname;
-
- rtpbin = session->bin;
-
- GST_DEBUG_OBJECT (rtpbin, "new SSRC pad %08x, %s:%s", ssrc,
- GST_DEBUG_PAD_NAME (pad));
-
- GST_RTP_BIN_SHUTDOWN_LOCK (rtpbin, shutdown);
-
- GST_RTP_SESSION_LOCK (session);
-
- /* create new stream */
- stream = create_stream (session, ssrc);
- if (!stream)
- goto no_stream;
-
- /* get pad and link */
- GST_DEBUG_OBJECT (rtpbin, "linking jitterbuffer RTP");
- padname = g_strdup_printf ("src_%d", ssrc);
- srcpad = gst_element_get_static_pad (element, padname);
- g_free (padname);
- sinkpad = gst_element_get_static_pad (stream->buffer, "sink");
- gst_pad_link (srcpad, sinkpad);
- gst_object_unref (sinkpad);
- gst_object_unref (srcpad);
-
- GST_DEBUG_OBJECT (rtpbin, "linking jitterbuffer RTCP");
- padname = g_strdup_printf ("rtcp_src_%d", ssrc);
- srcpad = gst_element_get_static_pad (element, padname);
- g_free (padname);
- sinkpad = gst_element_get_request_pad (stream->buffer, "sink_rtcp");
- gst_pad_link (srcpad, sinkpad);
- gst_object_unref (sinkpad);
- gst_object_unref (srcpad);
-
- /* connect to the RTCP sync signal from the jitterbuffer */
- GST_DEBUG_OBJECT (rtpbin, "connecting sync signal");
- stream->buffer_handlesync_sig = g_signal_connect (stream->buffer,
- "handle-sync", (GCallback) gst_rtp_bin_handle_sync, stream);
-
- /* connect to the new-pad signal of the payload demuxer, this will expose the
- * new pad by ghosting it. */
- stream->demux_newpad_sig = g_signal_connect (stream->demux,
- "new-payload-type", (GCallback) new_payload_found, stream);
- stream->demux_padremoved_sig = g_signal_connect (stream->demux,
- "pad-removed", (GCallback) payload_pad_removed, stream);
-
- /* connect to the request-pt-map signal. This signal will be emited by the
- * demuxer so that it can apply a proper caps on the buffers for the
- * depayloaders. */
- stream->demux_ptreq_sig = g_signal_connect (stream->demux,
- "request-pt-map", (GCallback) pt_map_requested, session);
-
- GST_RTP_SESSION_UNLOCK (session);
- GST_RTP_BIN_SHUTDOWN_UNLOCK (rtpbin);
-
- return;
-
- /* ERRORS */
-shutdown:
- {
- GST_DEBUG_OBJECT (rtpbin, "we are shutting down");
- return;
- }
-no_stream:
- {
- GST_RTP_SESSION_UNLOCK (session);
- GST_RTP_BIN_SHUTDOWN_UNLOCK (rtpbin);
- GST_DEBUG_OBJECT (rtpbin, "could not create stream");
- return;
- }
-}
-
-/* Create a pad for receiving RTP for the session in @name. Must be called with
- * RTP_BIN_LOCK.
- */
-static GstPad *
-create_recv_rtp (GstRtpBin * rtpbin, GstPadTemplate * templ, const gchar * name)
-{
- GstPad *sinkdpad;
- guint sessid;
- GstRtpBinSession *session;
- GstPadLinkReturn lres;
-
- /* first get the session number */
- if (name == NULL || sscanf (name, "recv_rtp_sink_%d", &sessid) != 1)
- goto no_name;
-
- GST_DEBUG_OBJECT (rtpbin, "finding session %d", sessid);
-
- /* get or create session */
- session = find_session_by_id (rtpbin, sessid);
- if (!session) {
- GST_DEBUG_OBJECT (rtpbin, "creating session %d", sessid);
- /* create session now */
- session = create_session (rtpbin, sessid);
- if (session == NULL)
- goto create_error;
- }
-
- /* check if pad was requested */
- if (session->recv_rtp_sink_ghost != NULL)
- return session->recv_rtp_sink_ghost;
-
- GST_DEBUG_OBJECT (rtpbin, "getting RTP sink pad");
- /* get recv_rtp pad and store */
- session->recv_rtp_sink =
- gst_element_get_request_pad (session->session, "recv_rtp_sink");
- if (session->recv_rtp_sink == NULL)
- goto pad_failed;
-
- g_signal_connect (session->recv_rtp_sink, "notify::caps",
- (GCallback) caps_changed, session);
-
- GST_DEBUG_OBJECT (rtpbin, "getting RTP src pad");
- /* get srcpad, link to SSRCDemux */
- session->recv_rtp_src =
- gst_element_get_static_pad (session->session, "recv_rtp_src");
- if (session->recv_rtp_src == NULL)
- goto pad_failed;
-
- GST_DEBUG_OBJECT (rtpbin, "getting demuxer RTP sink pad");
- sinkdpad = gst_element_get_static_pad (session->demux, "sink");
- GST_DEBUG_OBJECT (rtpbin, "linking demuxer RTP sink pad");
- lres = gst_pad_link (session->recv_rtp_src, sinkdpad);
- gst_object_unref (sinkdpad);
- if (lres != GST_PAD_LINK_OK)
- goto link_failed;
-
- /* connect to the new-ssrc-pad signal of the SSRC demuxer */
- session->demux_newpad_sig = g_signal_connect (session->demux,
- "new-ssrc-pad", (GCallback) new_ssrc_pad_found, session);
- session->demux_padremoved_sig = g_signal_connect (session->demux,
- "removed-ssrc-pad", (GCallback) ssrc_demux_pad_removed, session);
-
- GST_DEBUG_OBJECT (rtpbin, "ghosting session sink pad");
- session->recv_rtp_sink_ghost =
- gst_ghost_pad_new_from_template (name, session->recv_rtp_sink, templ);
- gst_pad_set_active (session->recv_rtp_sink_ghost, TRUE);
- gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), session->recv_rtp_sink_ghost);
-
- return session->recv_rtp_sink_ghost;
-
- /* ERRORS */
-no_name:
- {
- g_warning ("gstrtpbin: invalid name given");
- return NULL;
- }
-create_error:
- {
- /* create_session already warned */
- return NULL;
- }
-pad_failed:
- {
- g_warning ("gstrtpbin: failed to get session pad");
- return NULL;
- }
-link_failed:
- {
- g_warning ("gstrtpbin: failed to link pads");
- return NULL;
- }
-}
-
-static void
-remove_recv_rtp (GstRtpBin * rtpbin, GstRtpBinSession * session)
-{
- if (session->demux_newpad_sig) {
- g_signal_handler_disconnect (session->demux, session->demux_newpad_sig);
- session->demux_newpad_sig = 0;
- }
- if (session->demux_padremoved_sig) {
- g_signal_handler_disconnect (session->demux, session->demux_padremoved_sig);
- session->demux_padremoved_sig = 0;
- }
- if (session->recv_rtp_src) {
- gst_object_unref (session->recv_rtp_src);
- session->recv_rtp_src = NULL;
- }
- if (session->recv_rtp_sink) {
- gst_element_release_request_pad (session->session, session->recv_rtp_sink);
- gst_object_unref (session->recv_rtp_sink);
- session->recv_rtp_sink = NULL;
- }
- if (session->recv_rtp_sink_ghost) {
- gst_pad_set_active (session->recv_rtp_sink_ghost, FALSE);
- gst_element_remove_pad (GST_ELEMENT_CAST (rtpbin),
- session->recv_rtp_sink_ghost);
- session->recv_rtp_sink_ghost = NULL;
- }
-}
-
-/* Create a pad for receiving RTCP for the session in @name. Must be called with
- * RTP_BIN_LOCK.
- */
-static GstPad *
-create_recv_rtcp (GstRtpBin * rtpbin, GstPadTemplate * templ,
- const gchar * name)
-{
- guint sessid;
- GstRtpBinSession *session;
- GstPad *sinkdpad;
- GstPadLinkReturn lres;
-
- /* first get the session number */
- if (name == NULL || sscanf (name, "recv_rtcp_sink_%d", &sessid) != 1)
- goto no_name;
-
- GST_DEBUG_OBJECT (rtpbin, "finding session %d", sessid);
-
- /* get or create the session */
- session = find_session_by_id (rtpbin, sessid);
- if (!session) {
- GST_DEBUG_OBJECT (rtpbin, "creating session %d", sessid);
- /* create session now */
- session = create_session (rtpbin, sessid);
- if (session == NULL)
- goto create_error;
- }
-
- /* check if pad was requested */
- if (session->recv_rtcp_sink_ghost != NULL)
- return session->recv_rtcp_sink_ghost;
-
- /* get recv_rtp pad and store */
- GST_DEBUG_OBJECT (rtpbin, "getting RTCP sink pad");
- session->recv_rtcp_sink =
- gst_element_get_request_pad (session->session, "recv_rtcp_sink");
- if (session->recv_rtcp_sink == NULL)
- goto pad_failed;
-
- /* get srcpad, link to SSRCDemux */
- GST_DEBUG_OBJECT (rtpbin, "getting sync src pad");
- session->sync_src = gst_element_get_static_pad (session->session, "sync_src");
- if (session->sync_src == NULL)
- goto pad_failed;
-
- GST_DEBUG_OBJECT (rtpbin, "getting demuxer RTCP sink pad");
- sinkdpad = gst_element_get_static_pad (session->demux, "rtcp_sink");
- lres = gst_pad_link (session->sync_src, sinkdpad);
- gst_object_unref (sinkdpad);
- if (lres != GST_PAD_LINK_OK)
- goto link_failed;
-
- session->recv_rtcp_sink_ghost =
- gst_ghost_pad_new_from_template (name, session->recv_rtcp_sink, templ);
- gst_pad_set_active (session->recv_rtcp_sink_ghost, TRUE);
- gst_element_add_pad (GST_ELEMENT_CAST (rtpbin),
- session->recv_rtcp_sink_ghost);
-
- return session->recv_rtcp_sink_ghost;
-
- /* ERRORS */
-no_name:
- {
- g_warning ("gstrtpbin: invalid name given");
- return NULL;
- }
-create_error:
- {
- /* create_session already warned */
- return NULL;
- }
-pad_failed:
- {
- g_warning ("gstrtpbin: failed to get session pad");
- return NULL;
- }
-link_failed:
- {
- g_warning ("gstrtpbin: failed to link pads");
- return NULL;
- }
-}
-
-static void
-remove_recv_rtcp (GstRtpBin * rtpbin, GstRtpBinSession * session)
-{
- if (session->recv_rtcp_sink_ghost) {
- gst_pad_set_active (session->recv_rtcp_sink_ghost, FALSE);
- gst_element_remove_pad (GST_ELEMENT_CAST (rtpbin),
- session->recv_rtcp_sink_ghost);
- session->recv_rtcp_sink_ghost = NULL;
- }
- if (session->sync_src) {
- /* releasing the request pad should also unref the sync pad */
- gst_object_unref (session->sync_src);
- session->sync_src = NULL;
- }
- if (session->recv_rtcp_sink) {
- gst_element_release_request_pad (session->session, session->recv_rtcp_sink);
- gst_object_unref (session->recv_rtcp_sink);
- session->recv_rtcp_sink = NULL;
- }
-}
-
-/* Create a pad for sending RTP for the session in @name. Must be called with
- * RTP_BIN_LOCK.
- */
-static GstPad *
-create_send_rtp (GstRtpBin * rtpbin, GstPadTemplate * templ, const gchar * name)
-{
- gchar *gname;
- guint sessid;
- GstRtpBinSession *session;
- GstElementClass *klass;
-
- /* first get the session number */
- if (name == NULL || sscanf (name, "send_rtp_sink_%d", &sessid) != 1)
- goto no_name;
-
- /* get or create session */
- session = find_session_by_id (rtpbin, sessid);
- if (!session) {
- /* create session now */
- session = create_session (rtpbin, sessid);
- if (session == NULL)
- goto create_error;
- }
-
- /* check if pad was requested */
- if (session->send_rtp_sink_ghost != NULL)
- return session->send_rtp_sink_ghost;
-
- /* get send_rtp pad and store */
- session->send_rtp_sink =
- gst_element_get_request_pad (session->session, "send_rtp_sink");
- if (session->send_rtp_sink == NULL)
- goto pad_failed;
-
- session->send_rtp_sink_ghost =
- gst_ghost_pad_new_from_template (name, session->send_rtp_sink, templ);
- gst_pad_set_active (session->send_rtp_sink_ghost, TRUE);
- gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), session->send_rtp_sink_ghost);
-
- /* get srcpad */
- session->send_rtp_src =
- gst_element_get_static_pad (session->session, "send_rtp_src");
- if (session->send_rtp_src == NULL)
- goto no_srcpad;
-
- /* ghost the new source pad */
- klass = GST_ELEMENT_GET_CLASS (rtpbin);
- gname = g_strdup_printf ("send_rtp_src_%d", sessid);
- templ = gst_element_class_get_pad_template (klass, "send_rtp_src_%d");
- session->send_rtp_src_ghost =
- gst_ghost_pad_new_from_template (gname, session->send_rtp_src, templ);
- gst_pad_set_active (session->send_rtp_src_ghost, TRUE);
- gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), session->send_rtp_src_ghost);
- g_free (gname);
-
- return session->send_rtp_sink_ghost;
-
- /* ERRORS */
-no_name:
- {
- g_warning ("gstrtpbin: invalid name given");
- return NULL;
- }
-create_error:
- {
- /* create_session already warned */
- return NULL;
- }
-pad_failed:
- {
- g_warning ("gstrtpbin: failed to get session pad for session %d", sessid);
- return NULL;
- }
-no_srcpad:
- {
- g_warning ("gstrtpbin: failed to get rtp source pad for session %d",
- sessid);
- return NULL;
- }
-}
-
-static void
-remove_send_rtp (GstRtpBin * rtpbin, GstRtpBinSession * session)
-{
- if (session->send_rtp_src_ghost) {
- gst_pad_set_active (session->send_rtp_src_ghost, FALSE);
- gst_element_remove_pad (GST_ELEMENT_CAST (rtpbin),
- session->send_rtp_src_ghost);
- session->send_rtp_src_ghost = NULL;
- }
- if (session->send_rtp_src) {
- gst_object_unref (session->send_rtp_src);
- session->send_rtp_src = NULL;
- }
- if (session->send_rtp_sink) {
- gst_element_release_request_pad (GST_ELEMENT_CAST (session->session),
- session->send_rtp_sink);
- gst_object_unref (session->send_rtp_sink);
- session->send_rtp_sink = NULL;
- }
- if (session->send_rtp_sink_ghost) {
- gst_pad_set_active (session->send_rtp_sink_ghost, FALSE);
- gst_element_remove_pad (GST_ELEMENT_CAST (rtpbin),
- session->send_rtp_sink_ghost);
- session->send_rtp_sink_ghost = NULL;
- }
-}
-
-/* Create a pad for sending RTCP for the session in @name. Must be called with
- * RTP_BIN_LOCK.
- */
-static GstPad *
-create_rtcp (GstRtpBin * rtpbin, GstPadTemplate * templ, const gchar * name)
-{
- guint sessid;
- GstRtpBinSession *session;
-
- /* first get the session number */
- if (name == NULL || sscanf (name, "send_rtcp_src_%d", &sessid) != 1)
- goto no_name;
-
- /* get or create session */
- session = find_session_by_id (rtpbin, sessid);
- if (!session)
- goto no_session;
-
- /* check if pad was requested */
- if (session->send_rtcp_src_ghost != NULL)
- return session->send_rtcp_src_ghost;
-
- /* get rtcp_src pad and store */
- session->send_rtcp_src =
- gst_element_get_request_pad (session->session, "send_rtcp_src");
- if (session->send_rtcp_src == NULL)
- goto pad_failed;
-
- session->send_rtcp_src_ghost =
- gst_ghost_pad_new_from_template (name, session->send_rtcp_src, templ);
- gst_pad_set_active (session->send_rtcp_src_ghost, TRUE);
- gst_element_add_pad (GST_ELEMENT_CAST (rtpbin), session->send_rtcp_src_ghost);
-
- return session->send_rtcp_src_ghost;
-
- /* ERRORS */
-no_name:
- {
- g_warning ("gstrtpbin: invalid name given");
- return NULL;
- }
-no_session:
- {
- g_warning ("gstrtpbin: session with id %d does not exist", sessid);
- return NULL;
- }
-pad_failed:
- {
- g_warning ("gstrtpbin: failed to get rtcp pad for session %d", sessid);
- return NULL;
- }
-}
-
-static void
-remove_rtcp (GstRtpBin * rtpbin, GstRtpBinSession * session)
-{
- if (session->send_rtcp_src_ghost) {
- gst_pad_set_active (session->send_rtcp_src_ghost, FALSE);
- gst_element_remove_pad (GST_ELEMENT_CAST (rtpbin),
- session->send_rtcp_src_ghost);
- session->send_rtcp_src_ghost = NULL;
- }
- if (session->send_rtcp_src) {
- gst_element_release_request_pad (session->session, session->send_rtcp_src);
- gst_object_unref (session->send_rtcp_src);
- session->send_rtcp_src = NULL;
- }
-}
-
-/* If the requested name is NULL we should create a name with
- * the session number assuming we want the lowest posible session
- * with a free pad like the template */
-static gchar *
-gst_rtp_bin_get_free_pad_name (GstElement * element, GstPadTemplate * templ)
-{
- gboolean name_found = FALSE;
- gint session = 0;
- GstPad *pad = NULL;
- GstIterator *pad_it = NULL;
- gchar *pad_name = NULL;
-
- GST_DEBUG_OBJECT (element, "find a free pad name for template");
- while (!name_found) {
- g_free (pad_name);
- pad_name = g_strdup_printf (templ->name_template, session++);
- pad_it = gst_element_iterate_pads (GST_ELEMENT (element));
- name_found = TRUE;
- while (gst_iterator_next (pad_it, (gpointer) & pad) == GST_ITERATOR_OK) {
- gchar *name;
-
- name = gst_pad_get_name (pad);
- if (strcmp (name, pad_name) == 0)
- name_found = FALSE;
- g_free (name);
- }
- gst_iterator_free (pad_it);
- }
-
- GST_DEBUG_OBJECT (element, "free pad name found: '%s'", pad_name);
- return pad_name;
-}
-
-/*
- */
-static GstPad *
-gst_rtp_bin_request_new_pad (GstElement * element,
- GstPadTemplate * templ, const gchar * name)
-{
- GstRtpBin *rtpbin;
- GstElementClass *klass;
- GstPad *result;
-
- gchar *pad_name = NULL;
-
- g_return_val_if_fail (templ != NULL, NULL);
- g_return_val_if_fail (GST_IS_RTP_BIN (element), NULL);
-
- rtpbin = GST_RTP_BIN (element);
- klass = GST_ELEMENT_GET_CLASS (element);
-
- GST_RTP_BIN_LOCK (rtpbin);
-
- if (name == NULL) {
- /* use a free pad name */
- pad_name = gst_rtp_bin_get_free_pad_name (element, templ);
- } else {
- /* use the provided name */
- pad_name = g_strdup (name);
- }
-
- GST_DEBUG_OBJECT (rtpbin, "Trying to request a pad with name %s", pad_name);
-
- /* figure out the template */
- if (templ == gst_element_class_get_pad_template (klass, "recv_rtp_sink_%d")) {
- result = create_recv_rtp (rtpbin, templ, pad_name);
- } else if (templ == gst_element_class_get_pad_template (klass,
- "recv_rtcp_sink_%d")) {
- result = create_recv_rtcp (rtpbin, templ, pad_name);
- } else if (templ == gst_element_class_get_pad_template (klass,
- "send_rtp_sink_%d")) {
- result = create_send_rtp (rtpbin, templ, pad_name);
- } else if (templ == gst_element_class_get_pad_template (klass,
- "send_rtcp_src_%d")) {
- result = create_rtcp (rtpbin, templ, pad_name);
- } else
- goto wrong_template;
-
- g_free (pad_name);
- GST_RTP_BIN_UNLOCK (rtpbin);
-
- return result;
-
- /* ERRORS */
-wrong_template:
- {
- g_free (pad_name);
- GST_RTP_BIN_UNLOCK (rtpbin);
- g_warning ("gstrtpbin: this is not our template");
- return NULL;
- }
-}
-
-static void
-gst_rtp_bin_release_pad (GstElement * element, GstPad * pad)
-{
- GstRtpBinSession *session;
- GstRtpBin *rtpbin;
-
- g_return_if_fail (GST_IS_GHOST_PAD (pad));
- g_return_if_fail (GST_IS_RTP_BIN (element));
-
- rtpbin = GST_RTP_BIN (element);
-
- GST_RTP_BIN_LOCK (rtpbin);
- GST_DEBUG_OBJECT (rtpbin, "Trying to release pad %s:%s",
- GST_DEBUG_PAD_NAME (pad));
-
- if (!(session = find_session_by_pad (rtpbin, pad)))
- goto unknown_pad;
-
- if (session->recv_rtp_sink_ghost == pad) {
- remove_recv_rtp (rtpbin, session);
- } else if (session->recv_rtcp_sink_ghost == pad) {
- remove_recv_rtcp (rtpbin, session);
- } else if (session->send_rtp_sink_ghost == pad) {
- remove_send_rtp (rtpbin, session);
- } else if (session->send_rtcp_src_ghost == pad) {
- remove_rtcp (rtpbin, session);
- }
-
- /* no more request pads, free the complete session */
- if (session->recv_rtp_sink_ghost == NULL
- && session->recv_rtcp_sink_ghost == NULL
- && session->send_rtp_sink_ghost == NULL
- && session->send_rtcp_src_ghost == NULL) {
- GST_DEBUG_OBJECT (rtpbin, "no more pads for session %p", session);
- rtpbin->sessions = g_slist_remove (rtpbin->sessions, session);
- free_session (session, rtpbin);
- }
- GST_RTP_BIN_UNLOCK (rtpbin);
-
- return;
-
- /* ERROR */
-unknown_pad:
- {
- GST_RTP_BIN_UNLOCK (rtpbin);
- g_warning ("gstrtpbin: %s:%s is not one of our request pads",
- GST_DEBUG_PAD_NAME (pad));
- return;
- }
-}
+++ /dev/null
-/* GStreamer
- * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#ifndef __GST_RTP_BIN_H__
-#define __GST_RTP_BIN_H__
-
-#include <gst/gst.h>
-
-#include "rtpsession.h"
-
-#define GST_TYPE_RTP_BIN \
- (gst_rtp_bin_get_type())
-#define GST_RTP_BIN(obj) \
- (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_BIN,GstRtpBin))
-#define GST_RTP_BIN_CLASS(klass) \
- (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_BIN,GstRtpBinClass))
-#define GST_IS_RTP_BIN(obj) \
- (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_BIN))
-#define GST_IS_RTP_BIN_CLASS(klass) \
- (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_BIN))
-
-typedef struct _GstRtpBin GstRtpBin;
-typedef struct _GstRtpBinClass GstRtpBinClass;
-typedef struct _GstRtpBinPrivate GstRtpBinPrivate;
-
-struct _GstRtpBin {
- GstBin bin;
-
- /*< private >*/
- /* default latency for sessions */
- guint latency;
- gboolean do_lost;
- /* a list of session */
- GSList *sessions;
-
- /* a list of clients, these are streams with the same CNAME */
- GSList *clients;
-
- /* the default SDES items for sessions */
- GstStructure *sdes;
-
- /*< private >*/
- GstRtpBinPrivate *priv;
-};
-
-struct _GstRtpBinClass {
- GstBinClass parent_class;
-
- /* get the caps for pt */
- GstCaps* (*request_pt_map) (GstRtpBin *rtpbin, guint session, guint pt);
-
- /* action signals */
- void (*clear_pt_map) (GstRtpBin *rtpbin);
- void (*reset_sync) (GstRtpBin *rtpbin);
- RTPSession* (*get_internal_session) (GstRtpBin *rtpbin, guint session_id);
-
- /* session manager signals */
- void (*on_new_ssrc) (GstRtpBin *rtpbin, guint session, guint32 ssrc);
- void (*on_ssrc_collision) (GstRtpBin *rtpbin, guint session, guint32 ssrc);
- void (*on_ssrc_validated) (GstRtpBin *rtpbin, guint session, guint32 ssrc);
- void (*on_ssrc_active) (GstRtpBin *rtpbin, guint session, guint32 ssrc);
- void (*on_ssrc_sdes) (GstRtpBin *rtpbin, guint session, guint32 ssrc);
- void (*on_bye_ssrc) (GstRtpBin *rtpbin, guint session, guint32 ssrc);
- void (*on_bye_timeout) (GstRtpBin *rtpbin, guint session, guint32 ssrc);
- void (*on_timeout) (GstRtpBin *rtpbin, guint session, guint32 ssrc);
- void (*on_sender_timeout) (GstRtpBin *rtpbin, guint session, guint32 ssrc);
- void (*on_npt_stop) (GstRtpBin *rtpbin, guint session, guint32 ssrc);
-};
-
-GType gst_rtp_bin_get_type (void);
-
-#endif /* __GST_RTP_BIN_H__ */
+++ /dev/null
-/*
- * Farsight Voice+Video library
- *
- * Copyright 2007 Collabora Ltd,
- * Copyright 2007 Nokia Corporation
- * @author: Philippe Kalaf <philippe.kalaf@collabora.co.uk>.
- * Copyright 2007 Wim Taymans <wim.taymans@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- */
-
-/**
- * SECTION:element-gstrtpjitterbuffer
- *
- * This element reorders and removes duplicate RTP packets as they are received
- * from a network source. It will also wait for missing packets up to a
- * configurable time limit using the #GstRtpJitterBuffer:latency property.
- * Packets arriving too late are considered to be lost packets.
- *
- * This element acts as a live element and so adds #GstRtpJitterBuffer:latency
- * to the pipeline.
- *
- * The element needs the clock-rate of the RTP payload in order to estimate the
- * delay. This information is obtained either from the caps on the sink pad or,
- * when no caps are present, from the #GstRtpJitterBuffer::request-pt-map signal.
- * To clear the previous pt-map use the #GstRtpJitterBuffer::clear-pt-map signal.
- *
- * This element will automatically be used inside gstrtpbin.
- *
- * <refsect2>
- * <title>Example pipelines</title>
- * |[
- * gst-launch rtspsrc location=rtsp://192.168.1.133:8554/mpeg1or2AudioVideoTest ! gstrtpjitterbuffer ! rtpmpvdepay ! mpeg2dec ! xvimagesink
- * ]| Connect to a streaming server and decode the MPEG video. The jitterbuffer is
- * inserted into the pipeline to smooth out network jitter and to reorder the
- * out-of-order RTP packets.
- * </refsect2>
- *
- * Last reviewed on 2007-05-28 (0.10.5)
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <stdlib.h>
-#include <string.h>
-#include <gst/rtp/gstrtpbuffer.h>
-
-#include "gstrtpbin-marshal.h"
-
-#include "gstrtpjitterbuffer.h"
-#include "rtpjitterbuffer.h"
-#include "rtpstats.h"
-
-GST_DEBUG_CATEGORY (rtpjitterbuffer_debug);
-#define GST_CAT_DEFAULT (rtpjitterbuffer_debug)
-
-/* low and high threshold tell the queue when to start and stop buffering */
-#define LOW_THRESHOLD 0.2
-#define HIGH_THRESHOLD 0.8
-
-/* elementfactory information */
-static const GstElementDetails gst_rtp_jitter_buffer_details =
-GST_ELEMENT_DETAILS ("RTP packet jitter-buffer",
- "Filter/Network/RTP",
- "A buffer that deals with network jitter and other transmission faults",
- "Philippe Kalaf <philippe.kalaf@collabora.co.uk>, "
- "Wim Taymans <wim.taymans@gmail.com>");
-
-/* RTPJitterBuffer signals and args */
-enum
-{
- SIGNAL_REQUEST_PT_MAP,
- SIGNAL_CLEAR_PT_MAP,
- SIGNAL_HANDLE_SYNC,
- SIGNAL_ON_NPT_STOP,
- LAST_SIGNAL
-};
-
-#define DEFAULT_LATENCY_MS 200
-#define DEFAULT_DROP_ON_LATENCY FALSE
-#define DEFAULT_TS_OFFSET 0
-#define DEFAULT_DO_LOST FALSE
-
-enum
-{
- PROP_0,
- PROP_LATENCY,
- PROP_DROP_ON_LATENCY,
- PROP_TS_OFFSET,
- PROP_DO_LOST,
- PROP_LAST
-};
-
-#define JBUF_LOCK(priv) (g_mutex_lock ((priv)->jbuf_lock))
-
-#define JBUF_LOCK_CHECK(priv,label) G_STMT_START { \
- JBUF_LOCK (priv); \
- if (G_UNLIKELY (priv->srcresult != GST_FLOW_OK)) \
- goto label; \
-} G_STMT_END
-
-#define JBUF_UNLOCK(priv) (g_mutex_unlock ((priv)->jbuf_lock))
-#define JBUF_WAIT(priv) (g_cond_wait ((priv)->jbuf_cond, (priv)->jbuf_lock))
-
-#define JBUF_WAIT_CHECK(priv,label) G_STMT_START { \
- JBUF_WAIT(priv); \
- if (G_UNLIKELY (priv->srcresult != GST_FLOW_OK)) \
- goto label; \
-} G_STMT_END
-
-#define JBUF_SIGNAL(priv) (g_cond_signal ((priv)->jbuf_cond))
-
-struct _GstRtpJitterBufferPrivate
-{
- GstPad *sinkpad, *srcpad;
- GstPad *rtcpsinkpad;
-
- RTPJitterBuffer *jbuf;
- GMutex *jbuf_lock;
- GCond *jbuf_cond;
- gboolean waiting;
- gboolean discont;
-
- /* properties */
- guint latency_ms;
- gboolean drop_on_latency;
- gint64 ts_offset;
- gboolean do_lost;
-
- /* the last seqnum we pushed out */
- guint32 last_popped_seqnum;
- /* the next expected seqnum we push */
- guint32 next_seqnum;
- /* last output time */
- GstClockTime last_out_time;
- /* the next expected seqnum we receive */
- guint32 next_in_seqnum;
-
- /* start and stop ranges */
- GstClockTime npt_start;
- GstClockTime npt_stop;
- guint64 ext_timestamp;
- guint64 last_elapsed;
- guint64 estimated_eos;
- GstClockID eos_id;
- gboolean reached_npt_stop;
-
- /* state */
- gboolean eos;
-
- /* clock rate and rtp timestamp offset */
- gint last_pt;
- gint32 clock_rate;
- gint64 clock_base;
- gint64 prev_ts_offset;
-
- /* when we are shutting down */
- GstFlowReturn srcresult;
- gboolean blocked;
-
- /* for sync */
- GstSegment segment;
- GstClockID clock_id;
- gboolean unscheduled;
- /* the latency of the upstream peer, we have to take this into account when
- * synchronizing the buffers. */
- GstClockTime peer_latency;
-
- /* some accounting */
- guint64 num_late;
- guint64 num_duplicates;
-};
-
-#define GST_RTP_JITTER_BUFFER_GET_PRIVATE(o) \
- (G_TYPE_INSTANCE_GET_PRIVATE ((o), GST_TYPE_RTP_JITTER_BUFFER, \
- GstRtpJitterBufferPrivate))
-
-static GstStaticPadTemplate gst_rtp_jitter_buffer_sink_template =
-GST_STATIC_PAD_TEMPLATE ("sink",
- GST_PAD_SINK,
- GST_PAD_ALWAYS,
- GST_STATIC_CAPS ("application/x-rtp, "
- "clock-rate = (int) [ 1, 2147483647 ]"
- /* "payload = (int) , "
- * "encoding-name = (string) "
- */ )
- );
-
-static GstStaticPadTemplate gst_rtp_jitter_buffer_sink_rtcp_template =
-GST_STATIC_PAD_TEMPLATE ("sink_rtcp",
- GST_PAD_SINK,
- GST_PAD_REQUEST,
- GST_STATIC_CAPS ("application/x-rtcp")
- );
-
-static GstStaticPadTemplate gst_rtp_jitter_buffer_src_template =
-GST_STATIC_PAD_TEMPLATE ("src",
- GST_PAD_SRC,
- GST_PAD_ALWAYS,
- GST_STATIC_CAPS ("application/x-rtp"
- /* "payload = (int) , "
- * "clock-rate = (int) , "
- * "encoding-name = (string) "
- */ )
- );
-
-static guint gst_rtp_jitter_buffer_signals[LAST_SIGNAL] = { 0 };
-
-GST_BOILERPLATE (GstRtpJitterBuffer, gst_rtp_jitter_buffer, GstElement,
- GST_TYPE_ELEMENT);
-
-/* object overrides */
-static void gst_rtp_jitter_buffer_set_property (GObject * object,
- guint prop_id, const GValue * value, GParamSpec * pspec);
-static void gst_rtp_jitter_buffer_get_property (GObject * object,
- guint prop_id, GValue * value, GParamSpec * pspec);
-static void gst_rtp_jitter_buffer_finalize (GObject * object);
-
-/* element overrides */
-static GstStateChangeReturn gst_rtp_jitter_buffer_change_state (GstElement
- * element, GstStateChange transition);
-static GstPad *gst_rtp_jitter_buffer_request_new_pad (GstElement * element,
- GstPadTemplate * templ, const gchar * name);
-static void gst_rtp_jitter_buffer_release_pad (GstElement * element,
- GstPad * pad);
-
-/* pad overrides */
-static GstCaps *gst_rtp_jitter_buffer_getcaps (GstPad * pad);
-static GList *gst_rtp_jitter_buffer_internal_links (GstPad * pad);
-
-/* sinkpad overrides */
-static gboolean gst_jitter_buffer_sink_setcaps (GstPad * pad, GstCaps * caps);
-static gboolean gst_rtp_jitter_buffer_sink_event (GstPad * pad,
- GstEvent * event);
-static GstFlowReturn gst_rtp_jitter_buffer_chain (GstPad * pad,
- GstBuffer * buffer);
-
-static gboolean gst_rtp_jitter_buffer_sink_rtcp_event (GstPad * pad,
- GstEvent * event);
-static GstFlowReturn gst_rtp_jitter_buffer_chain_rtcp (GstPad * pad,
- GstBuffer * buffer);
-
-/* srcpad overrides */
-static gboolean gst_rtp_jitter_buffer_src_event (GstPad * pad,
- GstEvent * event);
-static gboolean
-gst_rtp_jitter_buffer_src_activate_push (GstPad * pad, gboolean active);
-static void gst_rtp_jitter_buffer_loop (GstRtpJitterBuffer * jitterbuffer);
-static gboolean gst_rtp_jitter_buffer_query (GstPad * pad, GstQuery * query);
-
-static void
-gst_rtp_jitter_buffer_clear_pt_map (GstRtpJitterBuffer * jitterbuffer);
-
-static void
-gst_rtp_jitter_buffer_base_init (gpointer klass)
-{
- GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
-
- gst_element_class_add_pad_template (element_class,
- gst_static_pad_template_get (&gst_rtp_jitter_buffer_src_template));
- gst_element_class_add_pad_template (element_class,
- gst_static_pad_template_get (&gst_rtp_jitter_buffer_sink_template));
- gst_element_class_add_pad_template (element_class,
- gst_static_pad_template_get (&gst_rtp_jitter_buffer_sink_rtcp_template));
-
- gst_element_class_set_details (element_class, &gst_rtp_jitter_buffer_details);
-}
-
-static void
-gst_rtp_jitter_buffer_class_init (GstRtpJitterBufferClass * klass)
-{
- GObjectClass *gobject_class;
- GstElementClass *gstelement_class;
-
- gobject_class = (GObjectClass *) klass;
- gstelement_class = (GstElementClass *) klass;
-
- g_type_class_add_private (klass, sizeof (GstRtpJitterBufferPrivate));
-
- gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_finalize);
-
- gobject_class->set_property = gst_rtp_jitter_buffer_set_property;
- gobject_class->get_property = gst_rtp_jitter_buffer_get_property;
-
- /**
- * GstRtpJitterBuffer::latency:
- *
- * The maximum latency of the jitterbuffer. Packets will be kept in the buffer
- * for at most this time.
- */
- g_object_class_install_property (gobject_class, PROP_LATENCY,
- g_param_spec_uint ("latency", "Buffer latency in ms",
- "Amount of ms to buffer", 0, G_MAXUINT, DEFAULT_LATENCY_MS,
- G_PARAM_READWRITE));
- /**
- * GstRtpJitterBuffer::drop-on-latency:
- *
- * Drop oldest buffers when the queue is completely filled.
- */
- g_object_class_install_property (gobject_class, PROP_DROP_ON_LATENCY,
- g_param_spec_boolean ("drop-on-latency",
- "Drop buffers when maximum latency is reached",
- "Tells the jitterbuffer to never exceed the given latency in size",
- DEFAULT_DROP_ON_LATENCY, G_PARAM_READWRITE));
- /**
- * GstRtpJitterBuffer::ts-offset:
- *
- * Adjust GStreamer output buffer timestamps in the jitterbuffer with offset.
- * This is mainly used to ensure interstream synchronisation.
- */
- g_object_class_install_property (gobject_class, PROP_TS_OFFSET,
- g_param_spec_int64 ("ts-offset", "Timestamp Offset",
- "Adjust buffer timestamps with offset in nanoseconds", G_MININT64,
- G_MAXINT64, DEFAULT_TS_OFFSET,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-
- /**
- * GstRtpJitterBuffer::do-lost:
- *
- * Send out a GstRTPPacketLost event downstream when a packet is considered
- * lost.
- */
- g_object_class_install_property (gobject_class, PROP_DO_LOST,
- g_param_spec_boolean ("do-lost", "Do Lost",
- "Send an event downstream when a packet is lost", DEFAULT_DO_LOST,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
- /**
- * GstRtpJitterBuffer::request-pt-map:
- * @buffer: the object which received the signal
- * @pt: the pt
- *
- * Request the payload type as #GstCaps for @pt.
- */
- gst_rtp_jitter_buffer_signals[SIGNAL_REQUEST_PT_MAP] =
- g_signal_new ("request-pt-map", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpJitterBufferClass,
- request_pt_map), NULL, NULL, gst_rtp_bin_marshal_BOXED__UINT,
- GST_TYPE_CAPS, 1, G_TYPE_UINT);
- /**
- * GstRtpJitterBuffer::handle-sync:
- * @buffer: the object which received the signal
- * @struct: a GstStructure containing sync values.
- *
- * Be notified of new sync values.
- */
- gst_rtp_jitter_buffer_signals[SIGNAL_HANDLE_SYNC] =
- g_signal_new ("handle-sync", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpJitterBufferClass,
- handle_sync), NULL, NULL, g_cclosure_marshal_VOID__BOXED,
- G_TYPE_NONE, 1, GST_TYPE_STRUCTURE | G_SIGNAL_TYPE_STATIC_SCOPE);
-
- /**
- * GstRtpJitterBuffer::on-npt-stop
- * @buffer: the object which received the signal
- *
- * Signal that the jitterbufer has pushed the RTP packet that corresponds to
- * the npt-stop position.
- */
- gst_rtp_jitter_buffer_signals[SIGNAL_ON_NPT_STOP] =
- g_signal_new ("on-npt-stop", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpJitterBufferClass,
- on_npt_stop), NULL, NULL, g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE, 0, G_TYPE_NONE);
-
- /**
- * GstRtpJitterBuffer::clear-pt-map:
- * @buffer: the object which received the signal
- *
- * Invalidate the clock-rate as obtained with the
- * #GstRtpJitterBuffer::request-pt-map signal.
- */
- gst_rtp_jitter_buffer_signals[SIGNAL_CLEAR_PT_MAP] =
- g_signal_new ("clear-pt-map", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (GstRtpJitterBufferClass, clear_pt_map), NULL, NULL,
- g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, G_TYPE_NONE);
-
- gstelement_class->change_state =
- GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_change_state);
- gstelement_class->request_new_pad =
- GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_request_new_pad);
- gstelement_class->release_pad =
- GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_release_pad);
-
- klass->clear_pt_map = GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_clear_pt_map);
-
- GST_DEBUG_CATEGORY_INIT
- (rtpjitterbuffer_debug, "gstrtpjitterbuffer", 0, "RTP Jitter Buffer");
-}
-
-static void
-gst_rtp_jitter_buffer_init (GstRtpJitterBuffer * jitterbuffer,
- GstRtpJitterBufferClass * klass)
-{
- GstRtpJitterBufferPrivate *priv;
-
- priv = GST_RTP_JITTER_BUFFER_GET_PRIVATE (jitterbuffer);
- jitterbuffer->priv = priv;
-
- priv->latency_ms = DEFAULT_LATENCY_MS;
- priv->drop_on_latency = DEFAULT_DROP_ON_LATENCY;
- priv->do_lost = DEFAULT_DO_LOST;
-
- priv->jbuf = rtp_jitter_buffer_new ();
- priv->jbuf_lock = g_mutex_new ();
- priv->jbuf_cond = g_cond_new ();
-
- priv->srcpad =
- gst_pad_new_from_static_template (&gst_rtp_jitter_buffer_src_template,
- "src");
-
- gst_pad_set_activatepush_function (priv->srcpad,
- GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_src_activate_push));
- gst_pad_set_query_function (priv->srcpad,
- GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_query));
- gst_pad_set_getcaps_function (priv->srcpad,
- GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_getcaps));
- gst_pad_set_event_function (priv->srcpad,
- GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_src_event));
-
- priv->sinkpad =
- gst_pad_new_from_static_template (&gst_rtp_jitter_buffer_sink_template,
- "sink");
-
- gst_pad_set_chain_function (priv->sinkpad,
- GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_chain));
- gst_pad_set_event_function (priv->sinkpad,
- GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_sink_event));
- gst_pad_set_setcaps_function (priv->sinkpad,
- GST_DEBUG_FUNCPTR (gst_jitter_buffer_sink_setcaps));
- gst_pad_set_getcaps_function (priv->sinkpad,
- GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_getcaps));
-
- gst_element_add_pad (GST_ELEMENT (jitterbuffer), priv->srcpad);
- gst_element_add_pad (GST_ELEMENT (jitterbuffer), priv->sinkpad);
-}
-
-static void
-gst_rtp_jitter_buffer_finalize (GObject * object)
-{
- GstRtpJitterBuffer *jitterbuffer;
-
- jitterbuffer = GST_RTP_JITTER_BUFFER (object);
-
- g_mutex_free (jitterbuffer->priv->jbuf_lock);
- g_cond_free (jitterbuffer->priv->jbuf_cond);
-
- g_object_unref (jitterbuffer->priv->jbuf);
-
- G_OBJECT_CLASS (parent_class)->finalize (object);
-}
-
-static GList *
-gst_rtp_jitter_buffer_internal_links (GstPad * pad)
-{
- GstRtpJitterBuffer *jitterbuffer;
- GstRtpJitterBufferPrivate *priv;
- GList *res = NULL;
-
- jitterbuffer = GST_RTP_JITTER_BUFFER (gst_pad_get_parent (pad));
- priv = jitterbuffer->priv;
-
- if (pad == priv->sinkpad) {
- res = g_list_prepend (res, priv->srcpad);
- } else if (pad == priv->srcpad) {
- res = g_list_prepend (res, priv->sinkpad);
- } else if (pad == priv->rtcpsinkpad) {
- res = NULL;
- }
-
- gst_object_unref (jitterbuffer);
-
- return res;
-}
-
-static GstPad *
-create_rtcp_sink (GstRtpJitterBuffer * jitterbuffer)
-{
- GstRtpJitterBufferPrivate *priv;
-
- priv = jitterbuffer->priv;
-
- GST_DEBUG_OBJECT (jitterbuffer, "creating RTCP sink pad");
-
- priv->rtcpsinkpad =
- gst_pad_new_from_static_template
- (&gst_rtp_jitter_buffer_sink_rtcp_template, "sink_rtcp");
- gst_pad_set_chain_function (priv->rtcpsinkpad,
- gst_rtp_jitter_buffer_chain_rtcp);
- gst_pad_set_event_function (priv->rtcpsinkpad,
- (GstPadEventFunction) gst_rtp_jitter_buffer_sink_rtcp_event);
- gst_pad_set_internal_link_function (priv->rtcpsinkpad,
- gst_rtp_jitter_buffer_internal_links);
- gst_pad_set_active (priv->rtcpsinkpad, TRUE);
- gst_element_add_pad (GST_ELEMENT_CAST (jitterbuffer), priv->rtcpsinkpad);
-
- return priv->rtcpsinkpad;
-}
-
-static void
-remove_rtcp_sink (GstRtpJitterBuffer * jitterbuffer)
-{
- GstRtpJitterBufferPrivate *priv;
-
- priv = jitterbuffer->priv;
-
- GST_DEBUG_OBJECT (jitterbuffer, "removing RTCP sink pad");
-
- gst_pad_set_active (priv->rtcpsinkpad, FALSE);
-
- gst_element_remove_pad (GST_ELEMENT_CAST (jitterbuffer), priv->rtcpsinkpad);
- priv->rtcpsinkpad = NULL;
-}
-
-static GstPad *
-gst_rtp_jitter_buffer_request_new_pad (GstElement * element,
- GstPadTemplate * templ, const gchar * name)
-{
- GstRtpJitterBuffer *jitterbuffer;
- GstElementClass *klass;
- GstPad *result;
- GstRtpJitterBufferPrivate *priv;
-
- g_return_val_if_fail (templ != NULL, NULL);
- g_return_val_if_fail (GST_IS_RTP_JITTER_BUFFER (element), NULL);
-
- jitterbuffer = GST_RTP_JITTER_BUFFER (element);
- priv = jitterbuffer->priv;
- klass = GST_ELEMENT_GET_CLASS (element);
-
- GST_DEBUG_OBJECT (element, "requesting pad %s", GST_STR_NULL (name));
-
- /* figure out the template */
- if (templ == gst_element_class_get_pad_template (klass, "sink_rtcp")) {
- if (priv->rtcpsinkpad != NULL)
- goto exists;
-
- result = create_rtcp_sink (jitterbuffer);
- } else
- goto wrong_template;
-
- return result;
-
- /* ERRORS */
-wrong_template:
- {
- g_warning ("gstrtpjitterbuffer: this is not our template");
- return NULL;
- }
-exists:
- {
- g_warning ("gstrtpjitterbuffer: pad already requested");
- return NULL;
- }
-}
-
-static void
-gst_rtp_jitter_buffer_release_pad (GstElement * element, GstPad * pad)
-{
- GstRtpJitterBuffer *jitterbuffer;
- GstRtpJitterBufferPrivate *priv;
-
- g_return_if_fail (GST_IS_RTP_JITTER_BUFFER (element));
- g_return_if_fail (GST_IS_PAD (pad));
-
- jitterbuffer = GST_RTP_JITTER_BUFFER (element);
- priv = jitterbuffer->priv;
-
- GST_DEBUG_OBJECT (element, "releasing pad %s:%s", GST_DEBUG_PAD_NAME (pad));
-
- if (priv->rtcpsinkpad == pad) {
- remove_rtcp_sink (jitterbuffer);
- } else
- goto wrong_pad;
-
- return;
-
- /* ERRORS */
-wrong_pad:
- {
- g_warning ("gstjitterbuffer: asked to release an unknown pad");
- return;
- }
-}
-
-static void
-gst_rtp_jitter_buffer_clear_pt_map (GstRtpJitterBuffer * jitterbuffer)
-{
- GstRtpJitterBufferPrivate *priv;
-
- priv = jitterbuffer->priv;
-
- /* this will trigger a new pt-map request signal, FIXME, do something better. */
- priv->clock_rate = -1;
-}
-
-static GstCaps *
-gst_rtp_jitter_buffer_getcaps (GstPad * pad)
-{
- GstRtpJitterBuffer *jitterbuffer;
- GstRtpJitterBufferPrivate *priv;
- GstPad *other;
- GstCaps *caps;
- const GstCaps *templ;
-
- jitterbuffer = GST_RTP_JITTER_BUFFER (gst_pad_get_parent (pad));
- priv = jitterbuffer->priv;
-
- other = (pad == priv->srcpad ? priv->sinkpad : priv->srcpad);
-
- caps = gst_pad_peer_get_caps (other);
-
- templ = gst_pad_get_pad_template_caps (pad);
- if (caps == NULL) {
- GST_DEBUG_OBJECT (jitterbuffer, "copy template");
- caps = gst_caps_copy (templ);
- } else {
- GstCaps *intersect;
-
- GST_DEBUG_OBJECT (jitterbuffer, "intersect with template");
-
- intersect = gst_caps_intersect (caps, templ);
- gst_caps_unref (caps);
-
- caps = intersect;
- }
- gst_object_unref (jitterbuffer);
-
- return caps;
-}
-
-static gboolean
-gst_jitter_buffer_sink_parse_caps (GstRtpJitterBuffer * jitterbuffer,
- GstCaps * caps)
-{
- GstRtpJitterBufferPrivate *priv;
- GstStructure *caps_struct;
- guint val;
- GstClockTime tval;
-
- priv = jitterbuffer->priv;
-
- /* first parse the caps */
- caps_struct = gst_caps_get_structure (caps, 0);
-
- GST_DEBUG_OBJECT (jitterbuffer, "got caps");
-
- /* we need a clock-rate to convert the rtp timestamps to GStreamer time and to
- * measure the amount of data in the buffer */
- if (!gst_structure_get_int (caps_struct, "clock-rate", &priv->clock_rate))
- goto error;
-
- if (priv->clock_rate <= 0)
- goto wrong_rate;
-
- GST_DEBUG_OBJECT (jitterbuffer, "got clock-rate %d", priv->clock_rate);
-
- /* The clock base is the RTP timestamp corrsponding to the npt-start value. We
- * can use this to track the amount of time elapsed on the sender. */
- if (gst_structure_get_uint (caps_struct, "clock-base", &val))
- priv->clock_base = val;
- else
- priv->clock_base = -1;
-
- priv->ext_timestamp = priv->clock_base;
-
- GST_DEBUG_OBJECT (jitterbuffer, "got clock-base %" G_GINT64_FORMAT,
- priv->clock_base);
-
- if (gst_structure_get_uint (caps_struct, "seqnum-base", &val)) {
- /* first expected seqnum, only update when we didn't have a previous base. */
- if (priv->next_in_seqnum == -1)
- priv->next_in_seqnum = val;
- if (priv->next_seqnum == -1)
- priv->next_seqnum = val;
- }
-
- GST_DEBUG_OBJECT (jitterbuffer, "got seqnum-base %d", priv->next_in_seqnum);
-
- /* the start and stop times. The seqnum-base corresponds to the start time. We
- * will keep track of the seqnums on the output and when we reach the one
- * corresponding to npt-stop, we emit the npt-stop-reached signal */
- if (gst_structure_get_clock_time (caps_struct, "npt-start", &tval))
- priv->npt_start = tval;
- else
- priv->npt_start = 0;
-
- if (gst_structure_get_clock_time (caps_struct, "npt-stop", &tval))
- priv->npt_stop = tval;
- else
- priv->npt_stop = -1;
-
- GST_DEBUG_OBJECT (jitterbuffer,
- "npt start/stop: %" GST_TIME_FORMAT "-%" GST_TIME_FORMAT,
- GST_TIME_ARGS (priv->npt_start), GST_TIME_ARGS (priv->npt_stop));
-
- return TRUE;
-
- /* ERRORS */
-error:
- {
- GST_DEBUG_OBJECT (jitterbuffer, "No clock-rate in caps!");
- return FALSE;
- }
-wrong_rate:
- {
- GST_DEBUG_OBJECT (jitterbuffer, "Invalid clock-rate %d", priv->clock_rate);
- return FALSE;
- }
-}
-
-static gboolean
-gst_jitter_buffer_sink_setcaps (GstPad * pad, GstCaps * caps)
-{
- GstRtpJitterBuffer *jitterbuffer;
- GstRtpJitterBufferPrivate *priv;
- gboolean res;
-
- jitterbuffer = GST_RTP_JITTER_BUFFER (gst_pad_get_parent (pad));
- priv = jitterbuffer->priv;
-
- res = gst_jitter_buffer_sink_parse_caps (jitterbuffer, caps);
-
- /* set same caps on srcpad on success */
- if (res)
- gst_pad_set_caps (priv->srcpad, caps);
-
- gst_object_unref (jitterbuffer);
-
- return res;
-}
-
-static void
-gst_rtp_jitter_buffer_flush_start (GstRtpJitterBuffer * jitterbuffer)
-{
- GstRtpJitterBufferPrivate *priv;
-
- priv = jitterbuffer->priv;
-
- JBUF_LOCK (priv);
- /* mark ourselves as flushing */
- priv->srcresult = GST_FLOW_WRONG_STATE;
- GST_DEBUG_OBJECT (jitterbuffer, "Disabling pop on queue");
- /* this unblocks any waiting pops on the src pad task */
- JBUF_SIGNAL (priv);
- /* unlock clock, we just unschedule, the entry will be released by the
- * locking streaming thread. */
- if (priv->clock_id) {
- gst_clock_id_unschedule (priv->clock_id);
- priv->unscheduled = TRUE;
- }
- JBUF_UNLOCK (priv);
-}
-
-static void
-gst_rtp_jitter_buffer_flush_stop (GstRtpJitterBuffer * jitterbuffer)
-{
- GstRtpJitterBufferPrivate *priv;
-
- priv = jitterbuffer->priv;
-
- JBUF_LOCK (priv);
- GST_DEBUG_OBJECT (jitterbuffer, "Enabling pop on queue");
- /* Mark as non flushing */
- priv->srcresult = GST_FLOW_OK;
- gst_segment_init (&priv->segment, GST_FORMAT_TIME);
- priv->last_popped_seqnum = -1;
- priv->last_out_time = -1;
- priv->next_seqnum = -1;
- priv->next_in_seqnum = -1;
- priv->clock_rate = -1;
- priv->eos = FALSE;
- priv->estimated_eos = -1;
- priv->last_elapsed = 0;
- priv->reached_npt_stop = FALSE;
- priv->ext_timestamp = -1;
- GST_DEBUG_OBJECT (jitterbuffer, "flush and reset jitterbuffer");
- rtp_jitter_buffer_flush (priv->jbuf);
- rtp_jitter_buffer_reset_skew (priv->jbuf);
- JBUF_UNLOCK (priv);
-}
-
-static gboolean
-gst_rtp_jitter_buffer_src_activate_push (GstPad * pad, gboolean active)
-{
- gboolean result = TRUE;
- GstRtpJitterBuffer *jitterbuffer = NULL;
-
- jitterbuffer = GST_RTP_JITTER_BUFFER (gst_pad_get_parent (pad));
-
- if (active) {
- /* allow data processing */
- gst_rtp_jitter_buffer_flush_stop (jitterbuffer);
-
- /* start pushing out buffers */
- GST_DEBUG_OBJECT (jitterbuffer, "Starting task on srcpad");
- gst_pad_start_task (jitterbuffer->priv->srcpad,
- (GstTaskFunction) gst_rtp_jitter_buffer_loop, jitterbuffer);
- } else {
- /* make sure all data processing stops ASAP */
- gst_rtp_jitter_buffer_flush_start (jitterbuffer);
-
- /* NOTE this will hardlock if the state change is called from the src pad
- * task thread because we will _join() the thread. */
- GST_DEBUG_OBJECT (jitterbuffer, "Stopping task on srcpad");
- result = gst_pad_stop_task (pad);
- }
-
- gst_object_unref (jitterbuffer);
-
- return result;
-}
-
-static GstStateChangeReturn
-gst_rtp_jitter_buffer_change_state (GstElement * element,
- GstStateChange transition)
-{
- GstRtpJitterBuffer *jitterbuffer;
- GstRtpJitterBufferPrivate *priv;
- GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
-
- jitterbuffer = GST_RTP_JITTER_BUFFER (element);
- priv = jitterbuffer->priv;
-
- switch (transition) {
- case GST_STATE_CHANGE_NULL_TO_READY:
- break;
- case GST_STATE_CHANGE_READY_TO_PAUSED:
- JBUF_LOCK (priv);
- /* reset negotiated values */
- priv->clock_rate = -1;
- priv->clock_base = -1;
- priv->peer_latency = 0;
- priv->last_pt = -1;
- /* block until we go to PLAYING */
- priv->blocked = TRUE;
- /* reset skew detection initialy */
- rtp_jitter_buffer_reset_skew (priv->jbuf);
- JBUF_UNLOCK (priv);
- break;
- case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
- JBUF_LOCK (priv);
- /* unblock to allow streaming in PLAYING */
- priv->blocked = FALSE;
- JBUF_SIGNAL (priv);
- JBUF_UNLOCK (priv);
- break;
- default:
- break;
- }
-
- ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
-
- switch (transition) {
- case GST_STATE_CHANGE_READY_TO_PAUSED:
- /* we are a live element because we sync to the clock, which we can only
- * do in the PLAYING state */
- if (ret != GST_STATE_CHANGE_FAILURE)
- ret = GST_STATE_CHANGE_NO_PREROLL;
- break;
- case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
- JBUF_LOCK (priv);
- /* block to stop streaming when PAUSED */
- priv->blocked = TRUE;
- JBUF_UNLOCK (priv);
- if (ret != GST_STATE_CHANGE_FAILURE)
- ret = GST_STATE_CHANGE_NO_PREROLL;
- break;
- case GST_STATE_CHANGE_PAUSED_TO_READY:
- break;
- case GST_STATE_CHANGE_READY_TO_NULL:
- break;
- default:
- break;
- }
-
- return ret;
-}
-
-static gboolean
-gst_rtp_jitter_buffer_src_event (GstPad * pad, GstEvent * event)
-{
- gboolean ret = TRUE;
- GstRtpJitterBuffer *jitterbuffer;
- GstRtpJitterBufferPrivate *priv;
-
- jitterbuffer = GST_RTP_JITTER_BUFFER (gst_pad_get_parent (pad));
- priv = jitterbuffer->priv;
-
- GST_DEBUG_OBJECT (jitterbuffer, "received %s", GST_EVENT_TYPE_NAME (event));
-
- switch (GST_EVENT_TYPE (event)) {
- default:
- ret = gst_pad_push_event (priv->sinkpad, event);
- break;
- }
- gst_object_unref (jitterbuffer);
-
- return ret;
-}
-
-static gboolean
-gst_rtp_jitter_buffer_sink_event (GstPad * pad, GstEvent * event)
-{
- gboolean ret = TRUE;
- GstRtpJitterBuffer *jitterbuffer;
- GstRtpJitterBufferPrivate *priv;
-
- jitterbuffer = GST_RTP_JITTER_BUFFER (gst_pad_get_parent (pad));
- priv = jitterbuffer->priv;
-
- GST_DEBUG_OBJECT (jitterbuffer, "received %s", GST_EVENT_TYPE_NAME (event));
-
- switch (GST_EVENT_TYPE (event)) {
- case GST_EVENT_NEWSEGMENT:
- {
- GstFormat format;
- gdouble rate, arate;
- gint64 start, stop, time;
- gboolean update;
-
- gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
- &start, &stop, &time);
-
- /* we need time for now */
- if (format != GST_FORMAT_TIME)
- goto newseg_wrong_format;
-
- GST_DEBUG_OBJECT (jitterbuffer,
- "newsegment: update %d, rate %g, arate %g, start %" GST_TIME_FORMAT
- ", stop %" GST_TIME_FORMAT ", time %" GST_TIME_FORMAT,
- update, rate, arate, GST_TIME_ARGS (start), GST_TIME_ARGS (stop),
- GST_TIME_ARGS (time));
-
- /* now configure the values, we need these to time the release of the
- * buffers on the srcpad. */
- gst_segment_set_newsegment_full (&priv->segment, update,
- rate, arate, format, start, stop, time);
-
- /* FIXME, push SEGMENT in the queue. Sorting order might be difficult. */
- ret = gst_pad_push_event (priv->srcpad, event);
- break;
- }
- case GST_EVENT_FLUSH_START:
- gst_rtp_jitter_buffer_flush_start (jitterbuffer);
- ret = gst_pad_push_event (priv->srcpad, event);
- break;
- case GST_EVENT_FLUSH_STOP:
- ret = gst_pad_push_event (priv->srcpad, event);
- ret = gst_rtp_jitter_buffer_src_activate_push (priv->srcpad, TRUE);
- break;
- case GST_EVENT_EOS:
- {
- /* push EOS in queue. We always push it at the head */
- JBUF_LOCK (priv);
- /* check for flushing, we need to discard the event and return FALSE when
- * we are flushing */
- ret = priv->srcresult == GST_FLOW_OK;
- if (ret && !priv->eos) {
- GST_DEBUG_OBJECT (jitterbuffer, "queuing EOS");
- priv->eos = TRUE;
- JBUF_SIGNAL (priv);
- } else if (priv->eos) {
- GST_DEBUG_OBJECT (jitterbuffer, "dropping EOS, we are already EOS");
- } else {
- GST_DEBUG_OBJECT (jitterbuffer, "dropping EOS, reason %s",
- gst_flow_get_name (priv->srcresult));
- }
- JBUF_UNLOCK (priv);
- gst_event_unref (event);
- break;
- }
- default:
- ret = gst_pad_push_event (priv->srcpad, event);
- break;
- }
-
-done:
- gst_object_unref (jitterbuffer);
-
- return ret;
-
- /* ERRORS */
-newseg_wrong_format:
- {
- GST_DEBUG_OBJECT (jitterbuffer, "received non TIME newsegment");
- ret = FALSE;
- goto done;
- }
-}
-
-static gboolean
-gst_rtp_jitter_buffer_sink_rtcp_event (GstPad * pad, GstEvent * event)
-{
- GstRtpJitterBuffer *jitterbuffer;
- GstRtpJitterBufferPrivate *priv;
-
- jitterbuffer = GST_RTP_JITTER_BUFFER (gst_pad_get_parent (pad));
- priv = jitterbuffer->priv;
-
- GST_DEBUG_OBJECT (jitterbuffer, "received %s", GST_EVENT_TYPE_NAME (event));
-
- switch (GST_EVENT_TYPE (event)) {
- case GST_EVENT_FLUSH_START:
- break;
- case GST_EVENT_FLUSH_STOP:
- break;
- default:
- break;
- }
- gst_event_unref (event);
- gst_object_unref (jitterbuffer);
-
- return TRUE;
-}
-
-static gboolean
-gst_rtp_jitter_buffer_get_clock_rate (GstRtpJitterBuffer * jitterbuffer,
- guint8 pt)
-{
- GValue ret = { 0 };
- GValue args[2] = { {0}, {0} };
- GstCaps *caps;
- gboolean res;
-
- g_value_init (&args[0], GST_TYPE_ELEMENT);
- g_value_set_object (&args[0], jitterbuffer);
- g_value_init (&args[1], G_TYPE_UINT);
- g_value_set_uint (&args[1], pt);
-
- g_value_init (&ret, GST_TYPE_CAPS);
- g_value_set_boxed (&ret, NULL);
-
- g_signal_emitv (args, gst_rtp_jitter_buffer_signals[SIGNAL_REQUEST_PT_MAP], 0,
- &ret);
-
- g_value_unset (&args[0]);
- g_value_unset (&args[1]);
- caps = (GstCaps *) g_value_dup_boxed (&ret);
- g_value_unset (&ret);
- if (!caps)
- goto no_caps;
-
- res = gst_jitter_buffer_sink_parse_caps (jitterbuffer, caps);
-
- gst_caps_unref (caps);
-
- return res;
-
- /* ERRORS */
-no_caps:
- {
- GST_DEBUG_OBJECT (jitterbuffer, "could not get caps");
- return FALSE;
- }
-}
-
-static GstFlowReturn
-gst_rtp_jitter_buffer_chain (GstPad * pad, GstBuffer * buffer)
-{
- GstRtpJitterBuffer *jitterbuffer;
- GstRtpJitterBufferPrivate *priv;
- guint16 seqnum;
- GstFlowReturn ret = GST_FLOW_OK;
- GstClockTime timestamp;
- guint64 latency_ts;
- gboolean tail;
- guint8 pt;
-
- jitterbuffer = GST_RTP_JITTER_BUFFER (gst_pad_get_parent (pad));
-
- if (G_UNLIKELY (!gst_rtp_buffer_validate (buffer)))
- goto invalid_buffer;
-
- priv = jitterbuffer->priv;
-
- pt = gst_rtp_buffer_get_payload_type (buffer);
-
- if (G_UNLIKELY (priv->last_pt != pt)) {
- GstCaps *caps;
-
- GST_DEBUG_OBJECT (jitterbuffer, "pt changed from %u to %u", priv->last_pt,
- pt);
-
- priv->last_pt = pt;
- /* reset clock-rate so that we get a new one */
- priv->clock_rate = -1;
- /* Try to get the clock-rate from the caps first if we can. If there are no
- * caps we must fire the signal to get the clock-rate. */
- if ((caps = GST_BUFFER_CAPS (buffer))) {
- gst_jitter_buffer_sink_parse_caps (jitterbuffer, caps);
- }
- }
-
- if (G_UNLIKELY (priv->clock_rate == -1)) {
- /* no clock rate given on the caps, try to get one with the signal */
- gst_rtp_jitter_buffer_get_clock_rate (jitterbuffer, pt);
- if (G_UNLIKELY (priv->clock_rate == -1))
- goto no_clock_rate;
- }
-
- /* take the timestamp of the buffer. This is the time when the packet was
- * received and is used to calculate jitter and clock skew. We will adjust
- * this timestamp with the smoothed value after processing it in the
- * jitterbuffer. */
- timestamp = GST_BUFFER_TIMESTAMP (buffer);
- /* bring to running time */
- timestamp = gst_segment_to_running_time (&priv->segment, GST_FORMAT_TIME,
- timestamp);
-
- seqnum = gst_rtp_buffer_get_seq (buffer);
-
- GST_DEBUG_OBJECT (jitterbuffer,
- "Received packet #%d at time %" GST_TIME_FORMAT, seqnum,
- GST_TIME_ARGS (timestamp));
-
- JBUF_LOCK_CHECK (priv, out_flushing);
- /* don't accept more data on EOS */
- if (G_UNLIKELY (priv->eos))
- goto have_eos;
-
- /* now check against our expected seqnum */
- if (G_LIKELY (priv->next_in_seqnum != -1)) {
- gint gap;
- gboolean reset = FALSE;
-
- gap = gst_rtp_buffer_compare_seqnum (priv->next_in_seqnum, seqnum);
- if (G_UNLIKELY (gap != 0)) {
- GST_DEBUG_OBJECT (jitterbuffer, "expected #%d, got #%d, gap of %d",
- priv->next_in_seqnum, seqnum, gap);
- /* priv->next_in_seqnum >= seqnum, this packet is too late or the
- * sender might have been restarted with different seqnum. */
- if (gap < -RTP_MAX_MISORDER) {
- GST_DEBUG_OBJECT (jitterbuffer, "reset: buffer too old %d", gap);
- reset = TRUE;
- }
- /* priv->next_in_seqnum < seqnum, this is a new packet */
- else if (G_UNLIKELY (gap > RTP_MAX_DROPOUT)) {
- GST_DEBUG_OBJECT (jitterbuffer, "reset: too many dropped packets %d",
- gap);
- reset = TRUE;
- } else {
- GST_DEBUG_OBJECT (jitterbuffer, "tolerable gap");
- }
- }
- if (G_UNLIKELY (reset)) {
- GST_DEBUG_OBJECT (jitterbuffer, "flush and reset jitterbuffer");
- rtp_jitter_buffer_flush (priv->jbuf);
- rtp_jitter_buffer_reset_skew (priv->jbuf);
- priv->last_popped_seqnum = -1;
- priv->next_seqnum = seqnum;
- }
- }
- priv->next_in_seqnum = (seqnum + 1) & 0xffff;
-
- /* let's check if this buffer is too late, we can only accept packets with
- * bigger seqnum than the one we last pushed. */
- if (G_LIKELY (priv->last_popped_seqnum != -1)) {
- gint gap;
-
- gap = gst_rtp_buffer_compare_seqnum (priv->last_popped_seqnum, seqnum);
-
- /* priv->last_popped_seqnum >= seqnum, we're too late. */
- if (G_UNLIKELY (gap <= 0))
- goto too_late;
- }
-
- /* let's drop oldest packet if the queue is already full and drop-on-latency
- * is set. We can only do this when there actually is a latency. When no
- * latency is set, we just pump it in the queue and let the other end push it
- * out as fast as possible. */
- if (priv->latency_ms && priv->drop_on_latency) {
- latency_ts =
- gst_util_uint64_scale_int (priv->latency_ms, priv->clock_rate, 1000);
-
- if (G_UNLIKELY (rtp_jitter_buffer_get_ts_diff (priv->jbuf) >= latency_ts)) {
- GstBuffer *old_buf;
-
- old_buf = rtp_jitter_buffer_pop (priv->jbuf);
-
- GST_DEBUG_OBJECT (jitterbuffer, "Queue full, dropping old packet #%d",
- gst_rtp_buffer_get_seq (old_buf));
-
- gst_buffer_unref (old_buf);
- }
- }
-
- /* we need to make the metadata writable before pushing it in the jitterbuffer
- * because the jitterbuffer will update the timestamp */
- buffer = gst_buffer_make_metadata_writable (buffer);
-
- /* now insert the packet into the queue in sorted order. This function returns
- * FALSE if a packet with the same seqnum was already in the queue, meaning we
- * have a duplicate. */
- if (G_UNLIKELY (!rtp_jitter_buffer_insert (priv->jbuf, buffer, timestamp,
- priv->clock_rate, &tail)))
- goto duplicate;
-
- /* signal addition of new buffer when the _loop is waiting. */
- if (priv->waiting)
- JBUF_SIGNAL (priv);
-
- /* let's unschedule and unblock any waiting buffers. We only want to do this
- * when the tail buffer changed */
- if (G_UNLIKELY (priv->clock_id && tail)) {
- GST_DEBUG_OBJECT (jitterbuffer,
- "Unscheduling waiting buffer, new tail buffer");
- gst_clock_id_unschedule (priv->clock_id);
- priv->unscheduled = TRUE;
- }
-
- GST_DEBUG_OBJECT (jitterbuffer, "Pushed packet #%d, now %d packets, tail: %d",
- seqnum, rtp_jitter_buffer_num_packets (priv->jbuf), tail);
-
-finished:
- JBUF_UNLOCK (priv);
-
- gst_object_unref (jitterbuffer);
-
- return ret;
-
- /* ERRORS */
-invalid_buffer:
- {
- /* this is not fatal but should be filtered earlier */
- GST_ELEMENT_WARNING (jitterbuffer, STREAM, DECODE, (NULL),
- ("Received invalid RTP payload, dropping"));
- gst_buffer_unref (buffer);
- gst_object_unref (jitterbuffer);
- return GST_FLOW_OK;
- }
-no_clock_rate:
- {
- GST_WARNING_OBJECT (jitterbuffer,
- "No clock-rate in caps!, dropping buffer");
- gst_buffer_unref (buffer);
- gst_object_unref (jitterbuffer);
- return GST_FLOW_OK;
- }
-out_flushing:
- {
- ret = priv->srcresult;
- GST_DEBUG_OBJECT (jitterbuffer, "flushing %s", gst_flow_get_name (ret));
- gst_buffer_unref (buffer);
- goto finished;
- }
-have_eos:
- {
- ret = GST_FLOW_UNEXPECTED;
- GST_WARNING_OBJECT (jitterbuffer, "we are EOS, refusing buffer");
- gst_buffer_unref (buffer);
- goto finished;
- }
-too_late:
- {
- GST_WARNING_OBJECT (jitterbuffer, "Packet #%d too late as #%d was already"
- " popped, dropping", seqnum, priv->last_popped_seqnum);
- priv->num_late++;
- gst_buffer_unref (buffer);
- goto finished;
- }
-duplicate:
- {
- GST_WARNING_OBJECT (jitterbuffer, "Duplicate packet #%d detected, dropping",
- seqnum);
- priv->num_duplicates++;
- gst_buffer_unref (buffer);
- goto finished;
- }
-}
-
-static GstClockTime
-apply_offset (GstRtpJitterBuffer * jitterbuffer, GstClockTime timestamp)
-{
- GstRtpJitterBufferPrivate *priv;
-
- priv = jitterbuffer->priv;
-
- if (timestamp == -1)
- return -1;
-
- /* apply the timestamp offset */
- timestamp += priv->ts_offset;
-
- return timestamp;
-}
-
-static GstClockTime
-get_sync_time (GstRtpJitterBuffer * jitterbuffer, GstClockTime timestamp)
-{
- GstClockTime result;
- GstRtpJitterBufferPrivate *priv;
-
- priv = jitterbuffer->priv;
-
- result = timestamp + GST_ELEMENT_CAST (jitterbuffer)->base_time;
- /* add latency, this includes our own latency and the peer latency. */
- result += (priv->latency_ms * GST_MSECOND);
- result += priv->peer_latency;
-
- return result;
-}
-
-static gboolean
-eos_reached (GstClock * clock, GstClockTime time, GstClockID id,
- GstRtpJitterBuffer * jitterbuffer)
-{
- GstRtpJitterBufferPrivate *priv;
-
- priv = jitterbuffer->priv;
-
- JBUF_LOCK_CHECK (priv, flushing);
- if (priv->waiting) {
- GST_DEBUG_OBJECT (jitterbuffer, "got the NPT timeout");
- priv->reached_npt_stop = TRUE;
- JBUF_SIGNAL (priv);
- }
- JBUF_UNLOCK (priv);
-
- return TRUE;
-
- /* ERRORS */
-flushing:
- {
- JBUF_UNLOCK (priv);
- return FALSE;
- }
-}
-
-/**
- * This funcion will push out buffers on the source pad.
- *
- * For each pushed buffer, the seqnum is recorded, if the next buffer B has a
- * different seqnum (missing packets before B), this function will wait for the
- * missing packet to arrive up to the timestamp of buffer B.
- */
-static void
-gst_rtp_jitter_buffer_loop (GstRtpJitterBuffer * jitterbuffer)
-{
- GstRtpJitterBufferPrivate *priv;
- GstBuffer *outbuf;
- GstFlowReturn result;
- guint16 seqnum;
- guint32 next_seqnum;
- GstClockTime timestamp, out_time;
- gboolean discont = FALSE;
- gint gap;
- GstClock *clock;
- GstClockID id;
- GstClockTime sync_time;
-
- priv = jitterbuffer->priv;
-
- JBUF_LOCK_CHECK (priv, flushing);
-again:
- GST_DEBUG_OBJECT (jitterbuffer, "Peeking item");
- while (TRUE) {
- id = NULL;
- /* always wait if we are blocked */
- if (G_LIKELY (!priv->blocked)) {
- /* if we have a packet, we can exit the loop and grab it */
- if (rtp_jitter_buffer_num_packets (priv->jbuf) > 0)
- break;
- /* no packets but we are EOS, do eos logic */
- if (G_UNLIKELY (priv->eos))
- goto do_eos;
- /* underrun, wait for packets or flushing now if we are expecting an EOS
- * timeout, set the async timer for it too */
- if (priv->estimated_eos != -1 && !priv->reached_npt_stop) {
- sync_time = get_sync_time (jitterbuffer, priv->estimated_eos);
-
- GST_OBJECT_LOCK (jitterbuffer);
- clock = GST_ELEMENT_CLOCK (jitterbuffer);
- if (clock) {
- GST_DEBUG_OBJECT (jitterbuffer, "scheduling timeout");
- id = gst_clock_new_single_shot_id (clock, sync_time);
- gst_clock_id_wait_async (id, (GstClockCallback) eos_reached,
- jitterbuffer);
- }
- GST_OBJECT_UNLOCK (jitterbuffer);
- }
- }
- /* now we wait */
- priv->waiting = TRUE;
- JBUF_WAIT (priv);
- priv->waiting = FALSE;
-
- if (id) {
- /* unschedule any pending async notifications we might have */
- gst_clock_id_unschedule (id);
- gst_clock_id_unref (id);
- }
- if (G_UNLIKELY (priv->srcresult != GST_FLOW_OK))
- goto flushing;
-
- if (id && priv->reached_npt_stop) {
- goto do_npt_stop;
- }
- }
-
- /* peek a buffer, we're just looking at the timestamp and the sequence number.
- * If all is fine, we'll pop and push it. If the sequence number is wrong we
- * wait on the timestamp. In the chain function we will unlock the wait when a
- * new buffer is available. The peeked buffer is valid for as long as we hold
- * the jitterbuffer lock. */
- outbuf = rtp_jitter_buffer_peek (priv->jbuf);
-
- /* get the seqnum and the next expected seqnum */
- seqnum = gst_rtp_buffer_get_seq (outbuf);
- next_seqnum = priv->next_seqnum;
-
- /* get the timestamp, this is already corrected for clock skew by the
- * jitterbuffer */
- timestamp = GST_BUFFER_TIMESTAMP (outbuf);
-
- GST_DEBUG_OBJECT (jitterbuffer,
- "Peeked buffer #%d, expect #%d, timestamp %" GST_TIME_FORMAT
- ", now %d left", seqnum, next_seqnum, GST_TIME_ARGS (timestamp),
- rtp_jitter_buffer_num_packets (priv->jbuf));
-
- /* apply our timestamp offset to the incomming buffer, this will be our output
- * timestamp. */
- out_time = apply_offset (jitterbuffer, timestamp);
-
- /* get the gap between this and the previous packet. If we don't know the
- * previous packet seqnum assume no gap. */
- if (G_LIKELY (next_seqnum != -1)) {
- gap = gst_rtp_buffer_compare_seqnum (next_seqnum, seqnum);
-
- /* if we have a packet that we already pushed or considered dropped, pop it
- * off and get the next packet */
- if (G_UNLIKELY (gap < 0)) {
- GST_DEBUG_OBJECT (jitterbuffer, "Old packet #%d, next #%d dropping",
- seqnum, next_seqnum);
- outbuf = rtp_jitter_buffer_pop (priv->jbuf);
- gst_buffer_unref (outbuf);
- goto again;
- }
- } else {
- GST_DEBUG_OBJECT (jitterbuffer, "no next seqnum known, first packet");
- gap = -1;
- }
-
- /* If we don't know what the next seqnum should be (== -1) we have to wait
- * because it might be possible that we are not receiving this buffer in-order,
- * a buffer with a lower seqnum could arrive later and we want to push that
- * earlier buffer before this buffer then.
- * If we know the expected seqnum, we can compare it to the current seqnum to
- * determine if we have missing a packet. If we have a missing packet (which
- * must be before this packet) we can wait for it until the deadline for this
- * packet expires. */
- if (G_UNLIKELY (gap != 0 && out_time != -1)) {
- GstClockReturn ret;
- GstClockTime duration = GST_CLOCK_TIME_NONE;
-
- if (gap > 0) {
- /* we have a gap */
- GST_WARNING_OBJECT (jitterbuffer,
- "Sequence number GAP detected: expected %d instead of %d (%d missing)",
- next_seqnum, seqnum, gap);
-
- if (priv->last_out_time != -1) {
- GST_DEBUG_OBJECT (jitterbuffer,
- "out_time %" GST_TIME_FORMAT ", last %" GST_TIME_FORMAT,
- GST_TIME_ARGS (out_time), GST_TIME_ARGS (priv->last_out_time));
- /* interpolate between the current time and the last time based on
- * number of packets we are missing, this is the estimated duration
- * for the missing packet based on equidistant packet spacing. Also make
- * sure we never go negative. */
- if (out_time > priv->last_out_time)
- duration = (out_time - priv->last_out_time) / (gap + 1);
- else
- goto lost;
-
- GST_DEBUG_OBJECT (jitterbuffer, "duration %" GST_TIME_FORMAT,
- GST_TIME_ARGS (duration));
- /* add this duration to the timestamp of the last packet we pushed */
- out_time = (priv->last_out_time + duration);
- }
- } else {
- /* we don't know what the next_seqnum should be, wait for the last
- * possible moment to push this buffer, maybe we get an earlier seqnum
- * while we wait */
- GST_DEBUG_OBJECT (jitterbuffer, "First buffer %d, do sync", seqnum);
- }
-
- GST_OBJECT_LOCK (jitterbuffer);
- clock = GST_ELEMENT_CLOCK (jitterbuffer);
- if (!clock) {
- GST_OBJECT_UNLOCK (jitterbuffer);
- /* let's just push if there is no clock */
- goto push_buffer;
- }
-
- GST_DEBUG_OBJECT (jitterbuffer, "sync to timestamp %" GST_TIME_FORMAT,
- GST_TIME_ARGS (out_time));
-
- /* prepare for sync against clock */
- sync_time = get_sync_time (jitterbuffer, out_time);
-
- /* create an entry for the clock */
- id = priv->clock_id = gst_clock_new_single_shot_id (clock, sync_time);
- priv->unscheduled = FALSE;
- GST_OBJECT_UNLOCK (jitterbuffer);
-
- /* release the lock so that the other end can push stuff or unlock */
- JBUF_UNLOCK (priv);
-
- ret = gst_clock_id_wait (id, NULL);
-
- JBUF_LOCK (priv);
- /* and free the entry */
- gst_clock_id_unref (id);
- priv->clock_id = NULL;
-
- /* at this point, the clock could have been unlocked by a timeout, a new
- * tail element was added to the queue or because we are shutting down. Check
- * for shutdown first. */
- if G_UNLIKELY
- ((priv->srcresult != GST_FLOW_OK))
- goto flushing;
-
- /* if we got unscheduled and we are not flushing, it's because a new tail
- * element became available in the queue or we flushed the queue.
- * Grab it and try to push or sync. */
- if (ret == GST_CLOCK_UNSCHEDULED || priv->unscheduled) {
- GST_DEBUG_OBJECT (jitterbuffer,
- "Wait got unscheduled, will retry to push with new buffer");
- goto again;
- }
-
- lost:
- /* we now timed out, this means we lost a packet or finished synchronizing
- * on the first buffer. */
- if (gap > 0) {
- GstEvent *event;
-
- /* we had a gap and thus we lost a packet. Create an event for this. */
- GST_DEBUG_OBJECT (jitterbuffer, "Packet #%d lost", next_seqnum);
- priv->num_late++;
- discont = TRUE;
-
- /* update our expected next packet */
- priv->last_popped_seqnum = next_seqnum;
- priv->last_out_time = out_time;
- priv->next_seqnum = (next_seqnum + 1) & 0xffff;
-
- if (priv->do_lost) {
- /* create paket lost event */
- event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM,
- gst_structure_new ("GstRTPPacketLost",
- "seqnum", G_TYPE_UINT, (guint) next_seqnum,
- "timestamp", G_TYPE_UINT64, out_time,
- "duration", G_TYPE_UINT64, duration, NULL));
-
- JBUF_UNLOCK (priv);
- gst_pad_push_event (priv->srcpad, event);
- JBUF_LOCK_CHECK (priv, flushing);
- }
- /* look for next packet */
- goto again;
- }
-
- /* there was no known gap,just the first packet, exit the loop and push */
- GST_DEBUG_OBJECT (jitterbuffer, "First packet #%d synced", seqnum);
-
- /* get new timestamp, latency might have changed */
- out_time = apply_offset (jitterbuffer, timestamp);
- }
-push_buffer:
-
- /* when we get here we are ready to pop and push the buffer */
- outbuf = rtp_jitter_buffer_pop (priv->jbuf);
-
- if (G_UNLIKELY (discont || priv->discont)) {
- /* set DISCONT flag when we missed a packet. We pushed the buffer writable
- * into the jitterbuffer so we can modify now. */
- GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
- priv->discont = FALSE;
- }
-
- /* apply timestamp with offset to buffer now */
- GST_BUFFER_TIMESTAMP (outbuf) = out_time;
-
- /* update the elapsed time when we need to check against the npt stop time. */
- if (priv->npt_stop != -1 && priv->ext_timestamp != -1
- && priv->clock_base != -1) {
- guint64 ext_time, elapsed, estimated;
- guint32 rtp_time;
-
- rtp_time = gst_rtp_buffer_get_timestamp (outbuf);
-
- GST_LOG_OBJECT (jitterbuffer, "rtp %" G_GUINT32_FORMAT ", ext %"
- G_GUINT64_FORMAT, rtp_time, priv->ext_timestamp);
-
- if (rtp_time < priv->ext_timestamp) {
- ext_time = priv->ext_timestamp;
- } else {
- ext_time = gst_rtp_buffer_ext_timestamp (&priv->ext_timestamp, rtp_time);
- }
-
- if (ext_time > priv->clock_base)
- elapsed = ext_time - priv->clock_base;
- else
- elapsed = 0;
-
- elapsed = gst_util_uint64_scale_int (elapsed, GST_SECOND, priv->clock_rate);
-
- if (elapsed > priv->last_elapsed) {
- guint64 left;
-
- priv->last_elapsed = elapsed;
-
- left = priv->npt_stop - priv->npt_start;
-
- if (elapsed > 0)
- estimated = gst_util_uint64_scale (out_time, left, elapsed);
- else
- estimated = -1;
-
- GST_LOG_OBJECT (jitterbuffer, "elapsed %" GST_TIME_FORMAT ", estimated %"
- GST_TIME_FORMAT, GST_TIME_ARGS (elapsed), GST_TIME_ARGS (estimated));
-
- priv->estimated_eos = estimated;
- }
- }
-
- /* now we are ready to push the buffer. Save the seqnum and release the lock
- * so the other end can push stuff in the queue again. */
- priv->last_popped_seqnum = seqnum;
- priv->last_out_time = out_time;
- priv->next_seqnum = (seqnum + 1) & 0xffff;
- JBUF_UNLOCK (priv);
-
- /* push buffer */
- GST_DEBUG_OBJECT (jitterbuffer,
- "Pushing buffer %d, timestamp %" GST_TIME_FORMAT, seqnum,
- GST_TIME_ARGS (out_time));
- result = gst_pad_push (priv->srcpad, outbuf);
- if (G_UNLIKELY (result != GST_FLOW_OK))
- goto pause;
-
- return;
-
- /* ERRORS */
-do_eos:
- {
- /* store result, we are flushing now */
- GST_DEBUG_OBJECT (jitterbuffer, "We are EOS, pushing EOS downstream");
- priv->srcresult = GST_FLOW_UNEXPECTED;
- gst_pad_pause_task (priv->srcpad);
- JBUF_UNLOCK (priv);
- gst_pad_push_event (priv->srcpad, gst_event_new_eos ());
- return;
- }
-do_npt_stop:
- {
- /* store result, we are flushing now */
- GST_DEBUG_OBJECT (jitterbuffer, "We reached the NPT stop");
- JBUF_UNLOCK (priv);
-
- g_signal_emit (jitterbuffer,
- gst_rtp_jitter_buffer_signals[SIGNAL_ON_NPT_STOP], 0, NULL);
- return;
- }
-flushing:
- {
- GST_DEBUG_OBJECT (jitterbuffer, "we are flushing");
- gst_pad_pause_task (priv->srcpad);
- JBUF_UNLOCK (priv);
- return;
- }
-pause:
- {
- GST_DEBUG_OBJECT (jitterbuffer, "pausing task, reason %s",
- gst_flow_get_name (result));
-
- JBUF_LOCK (priv);
- /* store result */
- priv->srcresult = result;
- /* we don't post errors or anything because upstream will do that for us
- * when we pass the return value upstream. */
- gst_pad_pause_task (priv->srcpad);
- JBUF_UNLOCK (priv);
- return;
- }
-}
-
-static GstFlowReturn
-gst_rtp_jitter_buffer_chain_rtcp (GstPad * pad, GstBuffer * buffer)
-{
- GstRtpJitterBuffer *jitterbuffer;
- GstRtpJitterBufferPrivate *priv;
- GstFlowReturn ret = GST_FLOW_OK;
- guint64 base_rtptime, timestamp;
- guint32 clock_rate;
- guint64 last_rtptime;
- guint32 ssrc;
- GstRTCPPacket packet;
- guint64 ext_rtptime, diff;
- guint32 rtptime;
- gboolean drop = FALSE;
-
- jitterbuffer = GST_RTP_JITTER_BUFFER (gst_pad_get_parent (pad));
-
- if (G_UNLIKELY (!gst_rtcp_buffer_validate (buffer)))
- goto invalid_buffer;
-
- priv = jitterbuffer->priv;
-
- if (!gst_rtcp_buffer_get_first_packet (buffer, &packet))
- goto invalid_buffer;
-
- /* first packet must be SR or RR or else the validate would have failed */
- switch (gst_rtcp_packet_get_type (&packet)) {
- case GST_RTCP_TYPE_SR:
- gst_rtcp_packet_sr_get_sender_info (&packet, &ssrc, NULL, &rtptime,
- NULL, NULL);
- break;
- default:
- goto ignore_buffer;
- }
-
- GST_DEBUG_OBJECT (jitterbuffer, "received RTCP of SSRC %08x", ssrc);
-
- JBUF_LOCK (priv);
- /* convert the RTP timestamp to our extended timestamp, using the same offset
- * we used in the jitterbuffer */
- ext_rtptime = priv->jbuf->ext_rtptime;
- ext_rtptime = gst_rtp_buffer_ext_timestamp (&ext_rtptime, rtptime);
-
- /* get the last values from the jitterbuffer */
- rtp_jitter_buffer_get_sync (priv->jbuf, &base_rtptime, ×tamp,
- &clock_rate, &last_rtptime);
-
- GST_DEBUG_OBJECT (jitterbuffer, "ext SR %" G_GUINT64_FORMAT ", base %"
- G_GUINT64_FORMAT ", clock-rate %" G_GUINT32_FORMAT,
- ext_rtptime, base_rtptime, clock_rate);
-
- if (base_rtptime == -1 || clock_rate == -1 || timestamp == -1) {
- GST_DEBUG_OBJECT (jitterbuffer, "dropping, no RTP values");
- drop = TRUE;
- } else {
- /* we can't accept anything that happened before we did the last resync */
- if (base_rtptime > ext_rtptime) {
- GST_DEBUG_OBJECT (jitterbuffer, "dropping, older than base time");
- drop = TRUE;
- } else {
- /* the SR RTP timestamp must be something close to what we last observed
- * in the jitterbuffer */
- if (ext_rtptime > last_rtptime) {
- /* check how far ahead it is to our RTP timestamps */
- diff = ext_rtptime - last_rtptime;
- /* if bigger than 1 second, we drop it */
- if (diff > clock_rate) {
- GST_DEBUG_OBJECT (jitterbuffer, "dropping, too far ahead");
- drop = TRUE;
- }
- GST_DEBUG_OBJECT (jitterbuffer, "ext last %" G_GUINT64_FORMAT ", diff %"
- G_GUINT64_FORMAT, last_rtptime, diff);
- }
- }
- }
- JBUF_UNLOCK (priv);
-
- if (!drop) {
- GstStructure *s;
-
- s = gst_structure_new ("application/x-rtp-sync",
- "base-rtptime", G_TYPE_UINT64, base_rtptime,
- "base-time", G_TYPE_UINT64, timestamp,
- "clock-rate", G_TYPE_UINT, clock_rate,
- "sr-ext-rtptime", G_TYPE_UINT64, ext_rtptime,
- "sr-buffer", GST_TYPE_BUFFER, buffer, NULL);
-
- GST_DEBUG_OBJECT (jitterbuffer, "signaling sync");
- g_signal_emit (jitterbuffer,
- gst_rtp_jitter_buffer_signals[SIGNAL_HANDLE_SYNC], 0, s);
- gst_structure_free (s);
- } else {
- GST_DEBUG_OBJECT (jitterbuffer, "dropping RTCP packet");
- ret = GST_FLOW_OK;
- }
-
-done:
- gst_buffer_unref (buffer);
- gst_object_unref (jitterbuffer);
-
- return ret;
-
-invalid_buffer:
- {
- /* this is not fatal but should be filtered earlier */
- GST_ELEMENT_WARNING (jitterbuffer, STREAM, DECODE, (NULL),
- ("Received invalid RTCP payload, dropping"));
- ret = GST_FLOW_OK;
- goto done;
- }
-ignore_buffer:
- {
- GST_DEBUG_OBJECT (jitterbuffer, "ignoring RTCP packet");
- ret = GST_FLOW_OK;
- goto done;
- }
-}
-
-static gboolean
-gst_rtp_jitter_buffer_query (GstPad * pad, GstQuery * query)
-{
- GstRtpJitterBuffer *jitterbuffer;
- GstRtpJitterBufferPrivate *priv;
- gboolean res = FALSE;
-
- jitterbuffer = GST_RTP_JITTER_BUFFER (gst_pad_get_parent (pad));
- priv = jitterbuffer->priv;
-
- switch (GST_QUERY_TYPE (query)) {
- case GST_QUERY_LATENCY:
- {
- /* We need to send the query upstream and add the returned latency to our
- * own */
- GstClockTime min_latency, max_latency;
- gboolean us_live;
- GstClockTime our_latency;
-
- if ((res = gst_pad_peer_query (priv->sinkpad, query))) {
- gst_query_parse_latency (query, &us_live, &min_latency, &max_latency);
-
- GST_DEBUG_OBJECT (jitterbuffer, "Peer latency: min %"
- GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
- GST_TIME_ARGS (min_latency), GST_TIME_ARGS (max_latency));
-
- /* store this so that we can safely sync on the peer buffers. */
- JBUF_LOCK (priv);
- priv->peer_latency = min_latency;
- our_latency = ((guint64) priv->latency_ms) * GST_MSECOND;
- JBUF_UNLOCK (priv);
-
- GST_DEBUG_OBJECT (jitterbuffer, "Our latency: %" GST_TIME_FORMAT,
- GST_TIME_ARGS (our_latency));
-
- /* we add some latency but can buffer an infinite amount of time */
- min_latency += our_latency;
- max_latency = -1;
-
- GST_DEBUG_OBJECT (jitterbuffer, "Calculated total latency : min %"
- GST_TIME_FORMAT " max %" GST_TIME_FORMAT,
- GST_TIME_ARGS (min_latency), GST_TIME_ARGS (max_latency));
-
- gst_query_set_latency (query, TRUE, min_latency, max_latency);
- }
- break;
- }
- default:
- res = gst_pad_query_default (pad, query);
- break;
- }
-
- gst_object_unref (jitterbuffer);
-
- return res;
-}
-
-static void
-gst_rtp_jitter_buffer_set_property (GObject * object,
- guint prop_id, const GValue * value, GParamSpec * pspec)
-{
- GstRtpJitterBuffer *jitterbuffer;
- GstRtpJitterBufferPrivate *priv;
-
- jitterbuffer = GST_RTP_JITTER_BUFFER (object);
- priv = jitterbuffer->priv;
-
- switch (prop_id) {
- case PROP_LATENCY:
- {
- guint new_latency, old_latency;
-
- new_latency = g_value_get_uint (value);
-
- JBUF_LOCK (priv);
- old_latency = priv->latency_ms;
- priv->latency_ms = new_latency;
- JBUF_UNLOCK (priv);
-
- /* post message if latency changed, this will inform the parent pipeline
- * that a latency reconfiguration is possible/needed. */
- if (new_latency != old_latency) {
- GST_DEBUG_OBJECT (jitterbuffer, "latency changed to: %" GST_TIME_FORMAT,
- GST_TIME_ARGS (new_latency * GST_MSECOND));
-
- gst_element_post_message (GST_ELEMENT_CAST (jitterbuffer),
- gst_message_new_latency (GST_OBJECT_CAST (jitterbuffer)));
- }
- break;
- }
- case PROP_DROP_ON_LATENCY:
- JBUF_LOCK (priv);
- priv->drop_on_latency = g_value_get_boolean (value);
- JBUF_UNLOCK (priv);
- break;
- case PROP_TS_OFFSET:
- JBUF_LOCK (priv);
- priv->ts_offset = g_value_get_int64 (value);
- /* FIXME, we don't really have a method for signaling a timestamp
- * DISCONT without also making this a data discont. */
- /* priv->discont = TRUE; */
- JBUF_UNLOCK (priv);
- break;
- case PROP_DO_LOST:
- JBUF_LOCK (priv);
- priv->do_lost = g_value_get_boolean (value);
- JBUF_UNLOCK (priv);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-static void
-gst_rtp_jitter_buffer_get_property (GObject * object,
- guint prop_id, GValue * value, GParamSpec * pspec)
-{
- GstRtpJitterBuffer *jitterbuffer;
- GstRtpJitterBufferPrivate *priv;
-
- jitterbuffer = GST_RTP_JITTER_BUFFER (object);
- priv = jitterbuffer->priv;
-
- switch (prop_id) {
- case PROP_LATENCY:
- JBUF_LOCK (priv);
- g_value_set_uint (value, priv->latency_ms);
- JBUF_UNLOCK (priv);
- break;
- case PROP_DROP_ON_LATENCY:
- JBUF_LOCK (priv);
- g_value_set_boolean (value, priv->drop_on_latency);
- JBUF_UNLOCK (priv);
- break;
- case PROP_TS_OFFSET:
- JBUF_LOCK (priv);
- g_value_set_int64 (value, priv->ts_offset);
- JBUF_UNLOCK (priv);
- break;
- case PROP_DO_LOST:
- JBUF_LOCK (priv);
- g_value_set_boolean (value, priv->do_lost);
- JBUF_UNLOCK (priv);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
+++ /dev/null
-/*
- * Farsight Voice+Video library
- *
- * Copyright 2007 Collabora Ltd,
- * Copyright 2007 Nokia Corporation
- * @author: Philippe Kalaf <philippe.kalaf@collabora.co.uk>.
- * Copyright 2007 Wim Taymans <wim.taymans@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- *
- */
-
-#ifndef __GST_RTP_JITTER_BUFFER_H__
-#define __GST_RTP_JITTER_BUFFER_H__
-
-#include <gst/gst.h>
-#include <gst/rtp/gstrtpbuffer.h>
-
-G_BEGIN_DECLS
-
-/* #define's don't like whitespacey bits */
-#define GST_TYPE_RTP_JITTER_BUFFER \
- (gst_rtp_jitter_buffer_get_type())
-#define GST_RTP_JITTER_BUFFER(obj) \
- (G_TYPE_CHECK_INSTANCE_CAST((obj), \
- GST_TYPE_RTP_JITTER_BUFFER,GstRtpJitterBuffer))
-#define GST_RTP_JITTER_BUFFER_CLASS(klass) \
- (G_TYPE_CHECK_CLASS_CAST((klass), \
- GST_TYPE_RTP_JITTER_BUFFER,GstRtpJitterBufferClass))
-#define GST_IS_RTP_JITTER_BUFFER(obj) \
- (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_JITTER_BUFFER))
-#define GST_IS_RTP_JITTER_BUFFER_CLASS(obj) \
- (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_JITTER_BUFFER))
-
-typedef struct _GstRtpJitterBuffer GstRtpJitterBuffer;
-typedef struct _GstRtpJitterBufferClass GstRtpJitterBufferClass;
-typedef struct _GstRtpJitterBufferPrivate GstRtpJitterBufferPrivate;
-
-/**
- * GstRtpJitterBuffer:
- *
- * Opaque jitterbuffer structure.
- */
-struct _GstRtpJitterBuffer
-{
- GstElement parent;
-
- /*< private >*/
- GstRtpJitterBufferPrivate *priv;
-
- gpointer _gst_reserved[GST_PADDING];
-};
-
-struct _GstRtpJitterBufferClass
-{
- GstElementClass parent_class;
-
- /* signals */
- GstCaps* (*request_pt_map) (GstRtpJitterBuffer *buffer, guint pt);
-
- void (*handle_sync) (GstRtpJitterBuffer *buffer, GstStructure *s);
- void (*on_npt_stop) (GstRtpJitterBuffer *buffer);
-
- /* actions */
- void (*clear_pt_map) (GstRtpJitterBuffer *buffer);
-
- /*< private > */
- gpointer _gst_reserved[GST_PADDING];
-};
-
-GType gst_rtp_jitter_buffer_get_type (void);
-
-G_END_DECLS
-
-#endif /* __GST_RTP_JITTER_BUFFER_H__ */
+++ /dev/null
-/* GStreamer
- * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "gstrtpbin.h"
-#include "gstrtpjitterbuffer.h"
-#include "gstrtpptdemux.h"
-#include "gstrtpsession.h"
-#include "gstrtpssrcdemux.h"
-
-static gboolean
-plugin_init (GstPlugin * plugin)
-{
- if (!gst_element_register (plugin, "gstrtpbin", GST_RANK_NONE,
- GST_TYPE_RTP_BIN))
- return FALSE;
-
- if (!gst_element_register (plugin, "gstrtpjitterbuffer", GST_RANK_NONE,
- GST_TYPE_RTP_JITTER_BUFFER))
- return FALSE;
-
- if (!gst_element_register (plugin, "gstrtpptdemux", GST_RANK_NONE,
- GST_TYPE_RTP_PT_DEMUX))
- return FALSE;
-
- if (!gst_element_register (plugin, "gstrtpsession", GST_RANK_NONE,
- GST_TYPE_RTP_SESSION))
- return FALSE;
-
- if (!gst_element_register (plugin, "gstrtpssrcdemux", GST_RANK_NONE,
- GST_TYPE_RTP_SSRC_DEMUX))
- return FALSE;
-
- return TRUE;
-}
-
-GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
- GST_VERSION_MINOR,
- "gstrtpmanager",
- "RTP session management plugin library",
- plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
+++ /dev/null
-/*
- * RTP Demux element
- *
- * Copyright (C) 2005 Nokia Corporation.
- * @author Kai Vehmanen <kai.vehmanen@nokia.com>
- *
- * Loosely based on GStreamer gstdecodebin
- * Copyright (C) <2004> Wim Taymans <wim.taymans@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-/**
- * SECTION:element-gstrtpptdemux
- *
- * gstrtpptdemux acts as a demuxer for RTP packets based on the payload type of
- * the packets. Its main purpose is to allow an application to easily receive
- * and decode an RTP stream with multiple payload types.
- *
- * For each payload type that is detected, a new pad will be created and the
- * #GstRtpPtDemux::new-payload-type signal will be emitted. When the payload for
- * the RTP stream changes, the #GstRtpPtDemux::payload-type-change signal will be
- * emitted.
- *
- * The element will try to set complete and unique application/x-rtp caps on the
- * outgoing buffers and pads based on the result of the
- * #GstRtpPtDemux::request-pt-map signal.
- *
- * <refsect2>
- * <title>Example pipelines</title>
- * |[
- * gst-launch udpsrc caps="application/x-rtp" ! gstrtpptdemux ! fakesink
- * ]| Takes an RTP stream and send the RTP packets with the first detected
- * payload type to fakesink, discarding the other payload types.
- * </refsect2>
- *
- * Last reviewed on 2007-05-28 (0.10.5)
- */
-
-/*
- * Contributors:
- * Andre Moreira Magalhaes <andre.magalhaes@indt.org.br>
- */
-/*
- * Status:
- * - works with the test_rtpdemux.c tool
- *
- * Check:
- * - is emitting a signal enough, or should we
- * use GstEvent to notify downstream elements
- * of the new packet... no?
- *
- * Notes:
- * - emits event both for new PTs, and whenever
- * a PT is changed
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <string.h>
-#include <gst/gst.h>
-#include <gst/rtp/gstrtpbuffer.h>
-
-#include "gstrtpbin-marshal.h"
-#include "gstrtpptdemux.h"
-
-/* generic templates */
-static GstStaticPadTemplate rtp_pt_demux_sink_template =
-GST_STATIC_PAD_TEMPLATE ("sink",
- GST_PAD_SINK,
- GST_PAD_ALWAYS,
- GST_STATIC_CAPS ("application/x-rtp")
- );
-
-static GstStaticPadTemplate rtp_pt_demux_src_template =
-GST_STATIC_PAD_TEMPLATE ("src_%d",
- GST_PAD_SRC,
- GST_PAD_SOMETIMES,
- GST_STATIC_CAPS ("application/x-rtp, " "payload = (int) [ 0, 255 ]")
- );
-
-GST_DEBUG_CATEGORY_STATIC (gst_rtp_pt_demux_debug);
-#define GST_CAT_DEFAULT gst_rtp_pt_demux_debug
-
-/**
- * Item for storing GstPad<->pt pairs.
- */
-struct _GstRtpPtDemuxPad
-{
- GstPad *pad; /**< pointer to the actual pad */
- gint pt; /**< RTP payload-type attached to pad */
- gboolean newcaps;
-};
-
-/* signals */
-enum
-{
- SIGNAL_REQUEST_PT_MAP,
- SIGNAL_NEW_PAYLOAD_TYPE,
- SIGNAL_PAYLOAD_TYPE_CHANGE,
- SIGNAL_CLEAR_PT_MAP,
- LAST_SIGNAL
-};
-
-GST_BOILERPLATE (GstRtpPtDemux, gst_rtp_pt_demux, GstElement, GST_TYPE_ELEMENT);
-
-static void gst_rtp_pt_demux_finalize (GObject * object);
-
-static void gst_rtp_pt_demux_release (GstRtpPtDemux * ptdemux);
-static gboolean gst_rtp_pt_demux_setup (GstRtpPtDemux * ptdemux);
-
-static GstFlowReturn gst_rtp_pt_demux_chain (GstPad * pad, GstBuffer * buf);
-static GstStateChangeReturn gst_rtp_pt_demux_change_state (GstElement * element,
- GstStateChange transition);
-static void gst_rtp_pt_demux_clear_pt_map (GstRtpPtDemux * rtpdemux);
-
-static GstRtpPtDemuxPad *find_pad_for_pt (GstRtpPtDemux * rtpdemux, guint8 pt);
-
-static guint gst_rtp_pt_demux_signals[LAST_SIGNAL] = { 0 };
-
-static GstElementDetails gst_rtp_pt_demux_details = {
- "RTP Demux",
- "Demux/Network/RTP",
- "Parses codec streams transmitted in the same RTP session",
- "Kai Vehmanen <kai.vehmanen@nokia.com>"
-};
-
-static void
-gst_rtp_pt_demux_base_init (gpointer g_class)
-{
- GstElementClass *gstelement_klass = GST_ELEMENT_CLASS (g_class);
-
- gst_element_class_add_pad_template (gstelement_klass,
- gst_static_pad_template_get (&rtp_pt_demux_sink_template));
- gst_element_class_add_pad_template (gstelement_klass,
- gst_static_pad_template_get (&rtp_pt_demux_src_template));
-
- gst_element_class_set_details (gstelement_klass, &gst_rtp_pt_demux_details);
-}
-
-static void
-gst_rtp_pt_demux_class_init (GstRtpPtDemuxClass * klass)
-{
- GObjectClass *gobject_klass;
- GstElementClass *gstelement_klass;
-
- gobject_klass = (GObjectClass *) klass;
- gstelement_klass = (GstElementClass *) klass;
-
- /**
- * GstRtpPtDemux::request-pt-map:
- * @demux: the object which received the signal
- * @pt: the payload type
- *
- * Request the payload type as #GstCaps for @pt.
- */
- gst_rtp_pt_demux_signals[SIGNAL_REQUEST_PT_MAP] =
- g_signal_new ("request-pt-map", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpPtDemuxClass, request_pt_map),
- NULL, NULL, gst_rtp_bin_marshal_BOXED__UINT, GST_TYPE_CAPS, 1,
- G_TYPE_UINT);
-
- /**
- * GstRtpPtDemux::new-payload-type:
- * @demux: the object which received the signal
- * @pt: the payload type
- * @pad: the pad with the new payload
- *
- * Emited when a new payload type pad has been created in @demux.
- */
- gst_rtp_pt_demux_signals[SIGNAL_NEW_PAYLOAD_TYPE] =
- g_signal_new ("new-payload-type", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpPtDemuxClass, new_payload_type),
- NULL, NULL, gst_rtp_bin_marshal_VOID__UINT_OBJECT, G_TYPE_NONE, 2,
- G_TYPE_UINT, GST_TYPE_PAD);
-
- /**
- * GstRtpPtDemux::payload-type-change:
- * @demux: the object which received the signal
- * @pt: the new payload type
- *
- * Emited when the payload type changed.
- */
- gst_rtp_pt_demux_signals[SIGNAL_PAYLOAD_TYPE_CHANGE] =
- g_signal_new ("payload-type-change", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpPtDemuxClass,
- payload_type_change), NULL, NULL, g_cclosure_marshal_VOID__UINT,
- G_TYPE_NONE, 1, G_TYPE_UINT);
-
- /**
- * GstRtpPtDemux::clear-pt-map:
- * @demux: the object which received the signal
- *
- * The application can call this signal to instruct the element to discard the
- * currently cached payload type map.
- */
- gst_rtp_pt_demux_signals[SIGNAL_CLEAR_PT_MAP] =
- g_signal_new ("clear-pt-map", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_ACTION | G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpPtDemuxClass,
- clear_pt_map), NULL, NULL, g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE, 0, G_TYPE_NONE);
-
- gobject_klass->finalize = GST_DEBUG_FUNCPTR (gst_rtp_pt_demux_finalize);
-
- gstelement_klass->change_state =
- GST_DEBUG_FUNCPTR (gst_rtp_pt_demux_change_state);
-
- klass->clear_pt_map = GST_DEBUG_FUNCPTR (gst_rtp_pt_demux_clear_pt_map);
-
- GST_DEBUG_CATEGORY_INIT (gst_rtp_pt_demux_debug,
- "rtpptdemux", 0, "RTP codec demuxer");
-}
-
-static void
-gst_rtp_pt_demux_init (GstRtpPtDemux * ptdemux, GstRtpPtDemuxClass * g_class)
-{
- GstElementClass *klass = GST_ELEMENT_GET_CLASS (ptdemux);
-
- ptdemux->sink =
- gst_pad_new_from_template (gst_element_class_get_pad_template (klass,
- "sink"), "sink");
- g_assert (ptdemux->sink != NULL);
-
- gst_pad_set_chain_function (ptdemux->sink, gst_rtp_pt_demux_chain);
-
- gst_element_add_pad (GST_ELEMENT (ptdemux), ptdemux->sink);
-}
-
-static void
-gst_rtp_pt_demux_finalize (GObject * object)
-{
- gst_rtp_pt_demux_release (GST_RTP_PT_DEMUX (object));
-
- G_OBJECT_CLASS (parent_class)->finalize (object);
-}
-
-static GstCaps *
-gst_rtp_pt_demux_get_caps (GstRtpPtDemux * rtpdemux, guint pt)
-{
- GstCaps *caps;
- GValue ret = { 0 };
- GValue args[2] = { {0}, {0} };
-
- /* figure out the caps */
- g_value_init (&args[0], GST_TYPE_ELEMENT);
- g_value_set_object (&args[0], rtpdemux);
- g_value_init (&args[1], G_TYPE_UINT);
- g_value_set_uint (&args[1], pt);
-
- g_value_init (&ret, GST_TYPE_CAPS);
- g_value_set_boxed (&ret, NULL);
-
- g_signal_emitv (args, gst_rtp_pt_demux_signals[SIGNAL_REQUEST_PT_MAP], 0,
- &ret);
-
- g_value_unset (&args[0]);
- g_value_unset (&args[1]);
- caps = g_value_dup_boxed (&ret);
- g_value_unset (&ret);
- if (caps == NULL) {
- caps = GST_PAD_CAPS (rtpdemux->sink);
- if (caps)
- gst_caps_ref (caps);
- }
-
- GST_DEBUG ("pt %d, got caps %" GST_PTR_FORMAT, pt, caps);
-
- return caps;
-}
-
-static void
-gst_rtp_pt_demux_clear_pt_map (GstRtpPtDemux * rtpdemux)
-{
- GSList *walk;
-
- GST_OBJECT_LOCK (rtpdemux);
- GST_DEBUG ("clearing pt map");
- for (walk = rtpdemux->srcpads; walk; walk = g_slist_next (walk)) {
- GstRtpPtDemuxPad *pad = walk->data;
-
- pad->newcaps = TRUE;
- }
- GST_OBJECT_UNLOCK (rtpdemux);
-}
-
-static GstFlowReturn
-gst_rtp_pt_demux_chain (GstPad * pad, GstBuffer * buf)
-{
- GstFlowReturn ret = GST_FLOW_OK;
- GstRtpPtDemux *rtpdemux;
- GstElement *element = GST_ELEMENT (GST_OBJECT_PARENT (pad));
- guint8 pt;
- GstPad *srcpad;
- GstRtpPtDemuxPad *rtpdemuxpad;
- GstCaps *caps;
-
- rtpdemux = GST_RTP_PT_DEMUX (GST_OBJECT_PARENT (pad));
-
- if (!gst_rtp_buffer_validate (buf))
- goto invalid_buffer;
-
- pt = gst_rtp_buffer_get_payload_type (buf);
-
- GST_DEBUG_OBJECT (rtpdemux, "received buffer for pt %d", pt);
-
- rtpdemuxpad = find_pad_for_pt (rtpdemux, pt);
- if (rtpdemuxpad == NULL) {
- /* new PT, create a src pad */
- GstElementClass *klass;
- GstPadTemplate *templ;
- gchar *padname;
-
- klass = GST_ELEMENT_GET_CLASS (rtpdemux);
- templ = gst_element_class_get_pad_template (klass, "src_%d");
- padname = g_strdup_printf ("src_%d", pt);
- srcpad = gst_pad_new_from_template (templ, padname);
- gst_pad_use_fixed_caps (srcpad);
- g_free (padname);
-
- caps = gst_rtp_pt_demux_get_caps (rtpdemux, pt);
- if (!caps)
- goto no_caps;
-
- caps = gst_caps_make_writable (caps);
- gst_caps_set_simple (caps, "payload", G_TYPE_INT, pt, NULL);
- gst_pad_set_caps (srcpad, caps);
- gst_caps_unref (caps);
-
- GST_DEBUG ("Adding pt=%d to the list.", pt);
- rtpdemuxpad = g_new0 (GstRtpPtDemuxPad, 1);
- rtpdemuxpad->pt = pt;
- rtpdemuxpad->newcaps = FALSE;
- rtpdemuxpad->pad = srcpad;
- GST_OBJECT_LOCK (rtpdemux);
- rtpdemux->srcpads = g_slist_append (rtpdemux->srcpads, rtpdemuxpad);
- GST_OBJECT_UNLOCK (rtpdemux);
-
- gst_pad_set_active (srcpad, TRUE);
- gst_element_add_pad (element, srcpad);
-
- GST_DEBUG ("emitting new-payload-type for pt %d", pt);
- g_signal_emit (G_OBJECT (rtpdemux),
- gst_rtp_pt_demux_signals[SIGNAL_NEW_PAYLOAD_TYPE], 0, pt, srcpad);
- }
-
- srcpad = rtpdemuxpad->pad;
-
- if (pt != rtpdemux->last_pt) {
- gint emit_pt = pt;
-
- /* our own signal with an extra flag that this is the only pad */
- rtpdemux->last_pt = pt;
- GST_DEBUG ("emitting payload-type-changed for pt %d", emit_pt);
- g_signal_emit (G_OBJECT (rtpdemux),
- gst_rtp_pt_demux_signals[SIGNAL_PAYLOAD_TYPE_CHANGE], 0, emit_pt);
- }
-
- if (rtpdemuxpad->newcaps) {
- GST_DEBUG ("need new caps");
- caps = gst_rtp_pt_demux_get_caps (rtpdemux, pt);
- if (!caps)
- goto no_caps;
-
- caps = gst_caps_make_writable (caps);
- gst_caps_set_simple (caps, "payload", G_TYPE_INT, pt, NULL);
- gst_pad_set_caps (srcpad, caps);
- gst_caps_unref (caps);
- rtpdemuxpad->newcaps = FALSE;
- }
-
- gst_buffer_set_caps (buf, GST_PAD_CAPS (srcpad));
-
- /* push to srcpad */
- ret = gst_pad_push (srcpad, buf);
-
- return ret;
-
- /* ERRORS */
-invalid_buffer:
- {
- /* this is fatal and should be filtered earlier */
- GST_ELEMENT_ERROR (rtpdemux, STREAM, DECODE, (NULL),
- ("Dropping invalid RTP payload"));
- gst_buffer_unref (buf);
- return GST_FLOW_ERROR;
- }
-no_caps:
- {
- GST_ELEMENT_ERROR (rtpdemux, STREAM, DECODE, (NULL),
- ("Could not get caps for payload"));
- gst_buffer_unref (buf);
- return GST_FLOW_ERROR;
- }
-}
-
-static GstRtpPtDemuxPad *
-find_pad_for_pt (GstRtpPtDemux * rtpdemux, guint8 pt)
-{
- GstRtpPtDemuxPad *respad = NULL;
- GSList *walk;
-
- for (walk = rtpdemux->srcpads; walk; walk = g_slist_next (walk)) {
- GstRtpPtDemuxPad *pad = walk->data;
-
- if (pad->pt == pt) {
- respad = pad;
- break;
- }
- }
- return respad;
-}
-
-/**
- * Reserves resources for the object.
- */
-static gboolean
-gst_rtp_pt_demux_setup (GstRtpPtDemux * ptdemux)
-{
- ptdemux->srcpads = NULL;
- ptdemux->last_pt = 0xFFFF;
-
- return TRUE;
-}
-
-/**
- * Free resources for the object.
- */
-static void
-gst_rtp_pt_demux_release (GstRtpPtDemux * ptdemux)
-{
- GSList *walk;
-
- for (walk = ptdemux->srcpads; walk; walk = g_slist_next (walk)) {
- GstRtpPtDemuxPad *pad = walk->data;
-
- gst_pad_set_active (pad->pad, FALSE);
- gst_element_remove_pad (GST_ELEMENT_CAST (ptdemux), pad->pad);
- g_free (pad);
- }
- g_slist_free (ptdemux->srcpads);
- ptdemux->srcpads = NULL;
-}
-
-static GstStateChangeReturn
-gst_rtp_pt_demux_change_state (GstElement * element, GstStateChange transition)
-{
- GstStateChangeReturn ret;
- GstRtpPtDemux *ptdemux;
-
- ptdemux = GST_RTP_PT_DEMUX (element);
-
- switch (transition) {
- case GST_STATE_CHANGE_NULL_TO_READY:
- if (gst_rtp_pt_demux_setup (ptdemux) != TRUE)
- ret = GST_STATE_CHANGE_FAILURE;
- break;
- case GST_STATE_CHANGE_READY_TO_PAUSED:
- case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
- default:
- break;
- }
-
- ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
-
- switch (transition) {
- case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
- case GST_STATE_CHANGE_PAUSED_TO_READY:
- break;
- case GST_STATE_CHANGE_READY_TO_NULL:
- gst_rtp_pt_demux_release (ptdemux);
- break;
- default:
- break;
- }
-
- return ret;
-}
+++ /dev/null
-/* GStreamer
- * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#ifndef __GST_RTP_PT_DEMUX_H__
-#define __GST_RTP_PT_DEMUX_H__
-
-#include <gst/gst.h>
-
-#define GST_TYPE_RTP_PT_DEMUX (gst_rtp_pt_demux_get_type())
-#define GST_RTP_PT_DEMUX(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_PT_DEMUX,GstRtpPtDemux))
-#define GST_RTP_PT_DEMUX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_PT_DEMUX,GstRtpPtDemuxClass))
-#define GST_IS_RTP_PT_DEMUX(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_PT_DEMUX))
-#define GST_IS_RTP_PT_DEMUX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_PT_DEMUX))
-
-typedef struct _GstRtpPtDemux GstRtpPtDemux;
-typedef struct _GstRtpPtDemuxClass GstRtpPtDemuxClass;
-typedef struct _GstRtpPtDemuxPad GstRtpPtDemuxPad;
-
-struct _GstRtpPtDemux
-{
- GstElement parent; /**< parent class */
-
- GstPad *sink; /**< the sink pad */
- guint16 last_pt; /**< pt of the last packet 0xFFFF if none */
- GSList *srcpads; /**< a linked list of GstRtpPtDemuxPad objects */
-};
-
-struct _GstRtpPtDemuxClass
-{
- GstElementClass parent_class;
-
- /* get the caps for pt */
- GstCaps* (*request_pt_map) (GstRtpPtDemux *demux, guint pt);
-
- /* signal emmited when a new PT is found from the incoming stream */
- void (*new_payload_type) (GstRtpPtDemux *demux, guint pt, GstPad * pad);
-
- /* signal emitted when the payload type changes */
- void (*payload_type_change) (GstRtpPtDemux *demux, guint pt);
-
- void (*clear_pt_map) (GstRtpPtDemux *demux);
-};
-
-GType gst_rtp_pt_demux_get_type (void);
-
-#endif /* __GST_RTP_PT_DEMUX_H__ */
+++ /dev/null
-/* GStreamer
- * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-/**
- * SECTION:element-gstrtpsession
- * @see_also: gstrtpjitterbuffer, gstrtpbin, gstrtpptdemux, gstrtpssrcdemux
- *
- * The RTP session manager models one participant with a unique SSRC in an RTP
- * session. This session can be used to send and receive RTP and RTCP packets.
- * Based on what REQUEST pads are requested from the session manager, specific
- * functionality can be activated.
- *
- * The session manager currently implements RFC 3550 including:
- * <itemizedlist>
- * <listitem>
- * <para>RTP packet validation based on consecutive sequence numbers.</para>
- * </listitem>
- * <listitem>
- * <para>Maintainance of the SSRC participant database.</para>
- * </listitem>
- * <listitem>
- * <para>Keeping per participant statistics based on received RTCP packets.</para>
- * </listitem>
- * <listitem>
- * <para>Scheduling of RR/SR RTCP packets.</para>
- * </listitem>
- * </itemizedlist>
- *
- * The gstrtpsession will not demux packets based on SSRC or payload type, nor will
- * it correct for packet reordering and jitter. Use #GstRtpsSrcDemux,
- * #GstRtpPtDemux and GstRtpJitterBuffer in addition to #GstRtpSession to
- * perform these tasks. It is usually a good idea to use #GstRtpBin, which
- * combines all these features in one element.
- *
- * To use #GstRtpSession as an RTP receiver, request a recv_rtp_sink pad, which will
- * automatically create recv_rtp_src pad. Data received on the recv_rtp_sink pad
- * will be processed in the session and after being validated forwarded on the
- * recv_rtp_src pad.
- *
- * To also use #GstRtpSession as an RTCP receiver, request a recv_rtcp_sink pad,
- * which will automatically create a sync_src pad. Packets received on the RTCP
- * pad will be used by the session manager to update the stats and database of
- * the other participants. SR packets will be forwarded on the sync_src pad
- * so that they can be used to perform inter-stream synchronisation when needed.
- *
- * If you want the session manager to generate and send RTCP packets, request
- * the send_rtcp_src pad. Packet pushed on this pad contain SR/RR RTCP reports
- * that should be sent to all participants in the session.
- *
- * To use #GstRtpSession as a sender, request a send_rtp_sink pad, which will
- * automatically create a send_rtp_src pad. The session manager will modify the
- * SSRC in the RTP packets to its own SSRC and wil forward the packets on the
- * send_rtp_src pad after updating its internal state.
- *
- * The session manager needs the clock-rate of the payload types it is handling
- * and will signal the #GstRtpSession::request-pt-map signal when it needs such a
- * mapping. One can clear the cached values with the #GstRtpSession::clear-pt-map
- * signal.
- *
- * <refsect2>
- * <title>Example pipelines</title>
- * |[
- * gst-launch udpsrc port=5000 caps="application/x-rtp, ..." ! .recv_rtp_sink gstrtpsession .recv_rtp_src ! rtptheoradepay ! theoradec ! xvimagesink
- * ]| Receive theora RTP packets from port 5000 and send them to the depayloader,
- * decoder and display. Note that the application/x-rtp caps on udpsrc should be
- * configured based on some negotiation process such as RTSP for this pipeline
- * to work correctly.
- * |[
- * gst-launch udpsrc port=5000 caps="application/x-rtp, ..." ! .recv_rtp_sink gstrtpsession name=session \
- * .recv_rtp_src ! rtptheoradepay ! theoradec ! xvimagesink \
- * udpsrc port=5001 caps="application/x-rtcp" ! session.recv_rtcp_sink
- * ]| Receive theora RTP packets from port 5000 and send them to the depayloader,
- * decoder and display. Receive RTCP packets from port 5001 and process them in
- * the session manager.
- * Note that the application/x-rtp caps on udpsrc should be
- * configured based on some negotiation process such as RTSP for this pipeline
- * to work correctly.
- * |[
- * gst-launch videotestsrc ! theoraenc ! rtptheorapay ! .send_rtp_sink gstrtpsession .send_rtp_src ! udpsink port=5000
- * ]| Send theora RTP packets through the session manager and out on UDP port
- * 5000.
- * |[
- * gst-launch videotestsrc ! theoraenc ! rtptheorapay ! .send_rtp_sink gstrtpsession name=session .send_rtp_src \
- * ! udpsink port=5000 session.send_rtcp_src ! udpsink port=5001
- * ]| Send theora RTP packets through the session manager and out on UDP port
- * 5000. Send RTCP packets on port 5001. Note that this pipeline will not preroll
- * correctly because the second udpsink will not preroll correctly (no RTCP
- * packets are sent in the PAUSED state). Applications should manually set and
- * keep (see gst_element_set_locked_state()) the RTCP udpsink to the PLAYING state.
- * </refsect2>
- *
- * Last reviewed on 2007-05-28 (0.10.5)
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <gst/rtp/gstrtpbuffer.h>
-
-#include "gstrtpbin-marshal.h"
-#include "gstrtpsession.h"
-#include "rtpsession.h"
-
-GST_DEBUG_CATEGORY_STATIC (gst_rtp_session_debug);
-#define GST_CAT_DEFAULT gst_rtp_session_debug
-
-/* elementfactory information */
-static const GstElementDetails rtpsession_details =
-GST_ELEMENT_DETAILS ("RTP Session",
- "Filter/Network/RTP",
- "Implement an RTP session",
- "Wim Taymans <wim.taymans@gmail.com>");
-
-/* sink pads */
-static GstStaticPadTemplate rtpsession_recv_rtp_sink_template =
-GST_STATIC_PAD_TEMPLATE ("recv_rtp_sink",
- GST_PAD_SINK,
- GST_PAD_REQUEST,
- GST_STATIC_CAPS ("application/x-rtp")
- );
-
-static GstStaticPadTemplate rtpsession_recv_rtcp_sink_template =
-GST_STATIC_PAD_TEMPLATE ("recv_rtcp_sink",
- GST_PAD_SINK,
- GST_PAD_REQUEST,
- GST_STATIC_CAPS ("application/x-rtcp")
- );
-
-static GstStaticPadTemplate rtpsession_send_rtp_sink_template =
-GST_STATIC_PAD_TEMPLATE ("send_rtp_sink",
- GST_PAD_SINK,
- GST_PAD_REQUEST,
- GST_STATIC_CAPS ("application/x-rtp")
- );
-
-/* src pads */
-static GstStaticPadTemplate rtpsession_recv_rtp_src_template =
-GST_STATIC_PAD_TEMPLATE ("recv_rtp_src",
- GST_PAD_SRC,
- GST_PAD_SOMETIMES,
- GST_STATIC_CAPS ("application/x-rtp")
- );
-
-static GstStaticPadTemplate rtpsession_sync_src_template =
-GST_STATIC_PAD_TEMPLATE ("sync_src",
- GST_PAD_SRC,
- GST_PAD_SOMETIMES,
- GST_STATIC_CAPS ("application/x-rtcp")
- );
-
-static GstStaticPadTemplate rtpsession_send_rtp_src_template =
-GST_STATIC_PAD_TEMPLATE ("send_rtp_src",
- GST_PAD_SRC,
- GST_PAD_SOMETIMES,
- GST_STATIC_CAPS ("application/x-rtp")
- );
-
-static GstStaticPadTemplate rtpsession_send_rtcp_src_template =
-GST_STATIC_PAD_TEMPLATE ("send_rtcp_src",
- GST_PAD_SRC,
- GST_PAD_REQUEST,
- GST_STATIC_CAPS ("application/x-rtcp")
- );
-
-/* signals and args */
-enum
-{
- SIGNAL_REQUEST_PT_MAP,
- SIGNAL_CLEAR_PT_MAP,
-
- SIGNAL_ON_NEW_SSRC,
- SIGNAL_ON_SSRC_COLLISION,
- SIGNAL_ON_SSRC_VALIDATED,
- SIGNAL_ON_SSRC_ACTIVE,
- SIGNAL_ON_SSRC_SDES,
- SIGNAL_ON_BYE_SSRC,
- SIGNAL_ON_BYE_TIMEOUT,
- SIGNAL_ON_TIMEOUT,
- SIGNAL_ON_SENDER_TIMEOUT,
- LAST_SIGNAL
-};
-
-#define DEFAULT_NTP_NS_BASE 0
-#define DEFAULT_BANDWIDTH RTP_STATS_BANDWIDTH
-#define DEFAULT_RTCP_FRACTION RTP_STATS_RTCP_BANDWIDTH
-#define DEFAULT_SDES NULL
-#define DEFAULT_NUM_SOURCES 0
-#define DEFAULT_NUM_ACTIVE_SOURCES 0
-
-enum
-{
- PROP_0,
- PROP_NTP_NS_BASE,
- PROP_BANDWIDTH,
- PROP_RTCP_FRACTION,
- PROP_SDES,
- PROP_NUM_SOURCES,
- PROP_NUM_ACTIVE_SOURCES,
- PROP_INTERNAL_SESSION,
- PROP_LAST
-};
-
-#define GST_RTP_SESSION_GET_PRIVATE(obj) \
- (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_RTP_SESSION, GstRtpSessionPrivate))
-
-#define GST_RTP_SESSION_LOCK(sess) g_mutex_lock ((sess)->priv->lock)
-#define GST_RTP_SESSION_UNLOCK(sess) g_mutex_unlock ((sess)->priv->lock)
-
-struct _GstRtpSessionPrivate
-{
- GMutex *lock;
- GstClock *sysclock;
-
- RTPSession *session;
-
- /* thread for sending out RTCP */
- GstClockID id;
- gboolean stop_thread;
- GThread *thread;
- gboolean thread_stopped;
-
- /* caps mapping */
- GHashTable *ptmap;
-
- /* NTP base time */
- guint64 ntpnsbase;
-};
-
-/* callbacks to handle actions from the session manager */
-static GstFlowReturn gst_rtp_session_process_rtp (RTPSession * sess,
- RTPSource * src, GstBuffer * buffer, gpointer user_data);
-static GstFlowReturn gst_rtp_session_send_rtp (RTPSession * sess,
- RTPSource * src, gpointer data, gpointer user_data);
-static GstFlowReturn gst_rtp_session_send_rtcp (RTPSession * sess,
- RTPSource * src, GstBuffer * buffer, gboolean eos, gpointer user_data);
-static GstFlowReturn gst_rtp_session_sync_rtcp (RTPSession * sess,
- RTPSource * src, GstBuffer * buffer, gpointer user_data);
-static gint gst_rtp_session_clock_rate (RTPSession * sess, guint8 payload,
- gpointer user_data);
-static void gst_rtp_session_reconsider (RTPSession * sess, gpointer user_data);
-
-static RTPSessionCallbacks callbacks = {
- gst_rtp_session_process_rtp,
- gst_rtp_session_send_rtp,
- gst_rtp_session_sync_rtcp,
- gst_rtp_session_send_rtcp,
- gst_rtp_session_clock_rate,
- gst_rtp_session_reconsider
-};
-
-/* GObject vmethods */
-static void gst_rtp_session_finalize (GObject * object);
-static void gst_rtp_session_set_property (GObject * object, guint prop_id,
- const GValue * value, GParamSpec * pspec);
-static void gst_rtp_session_get_property (GObject * object, guint prop_id,
- GValue * value, GParamSpec * pspec);
-
-/* GstElement vmethods */
-static GstStateChangeReturn gst_rtp_session_change_state (GstElement * element,
- GstStateChange transition);
-static GstPad *gst_rtp_session_request_new_pad (GstElement * element,
- GstPadTemplate * templ, const gchar * name);
-static void gst_rtp_session_release_pad (GstElement * element, GstPad * pad);
-
-static void gst_rtp_session_clear_pt_map (GstRtpSession * rtpsession);
-
-static guint gst_rtp_session_signals[LAST_SIGNAL] = { 0 };
-
-static void
-on_new_ssrc (RTPSession * session, RTPSource * src, GstRtpSession * sess)
-{
- g_signal_emit (sess, gst_rtp_session_signals[SIGNAL_ON_NEW_SSRC], 0,
- src->ssrc);
-}
-
-static void
-on_ssrc_collision (RTPSession * session, RTPSource * src, GstRtpSession * sess)
-{
- g_signal_emit (sess, gst_rtp_session_signals[SIGNAL_ON_SSRC_COLLISION], 0,
- src->ssrc);
-}
-
-static void
-on_ssrc_validated (RTPSession * session, RTPSource * src, GstRtpSession * sess)
-{
- g_signal_emit (sess, gst_rtp_session_signals[SIGNAL_ON_SSRC_VALIDATED], 0,
- src->ssrc);
-}
-
-static void
-on_ssrc_active (RTPSession * session, RTPSource * src, GstRtpSession * sess)
-{
- g_signal_emit (sess, gst_rtp_session_signals[SIGNAL_ON_SSRC_ACTIVE], 0,
- src->ssrc);
-}
-
-static void
-on_ssrc_sdes (RTPSession * session, RTPSource * src, GstRtpSession * sess)
-{
- GstStructure *s;
- GstMessage *m;
-
- /* convert the new SDES info into a message */
- RTP_SESSION_LOCK (session);
- g_object_get (src, "sdes", &s, NULL);
- RTP_SESSION_UNLOCK (session);
-
- m = gst_message_new_custom (GST_MESSAGE_ELEMENT, GST_OBJECT (sess), s);
- gst_element_post_message (GST_ELEMENT_CAST (sess), m);
-
- g_signal_emit (sess, gst_rtp_session_signals[SIGNAL_ON_SSRC_SDES], 0,
- src->ssrc);
-}
-
-static void
-on_bye_ssrc (RTPSession * session, RTPSource * src, GstRtpSession * sess)
-{
- g_signal_emit (sess, gst_rtp_session_signals[SIGNAL_ON_BYE_SSRC], 0,
- src->ssrc);
-}
-
-static void
-on_bye_timeout (RTPSession * session, RTPSource * src, GstRtpSession * sess)
-{
- g_signal_emit (sess, gst_rtp_session_signals[SIGNAL_ON_BYE_TIMEOUT], 0,
- src->ssrc);
-}
-
-static void
-on_timeout (RTPSession * session, RTPSource * src, GstRtpSession * sess)
-{
- g_signal_emit (sess, gst_rtp_session_signals[SIGNAL_ON_TIMEOUT], 0,
- src->ssrc);
-}
-
-static void
-on_sender_timeout (RTPSession * session, RTPSource * src, GstRtpSession * sess)
-{
- g_signal_emit (sess, gst_rtp_session_signals[SIGNAL_ON_SENDER_TIMEOUT], 0,
- src->ssrc);
-}
-
-GST_BOILERPLATE (GstRtpSession, gst_rtp_session, GstElement, GST_TYPE_ELEMENT);
-
-static void
-gst_rtp_session_base_init (gpointer klass)
-{
- GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
-
- /* sink pads */
- gst_element_class_add_pad_template (element_class,
- gst_static_pad_template_get (&rtpsession_recv_rtp_sink_template));
- gst_element_class_add_pad_template (element_class,
- gst_static_pad_template_get (&rtpsession_recv_rtcp_sink_template));
- gst_element_class_add_pad_template (element_class,
- gst_static_pad_template_get (&rtpsession_send_rtp_sink_template));
-
- /* src pads */
- gst_element_class_add_pad_template (element_class,
- gst_static_pad_template_get (&rtpsession_recv_rtp_src_template));
- gst_element_class_add_pad_template (element_class,
- gst_static_pad_template_get (&rtpsession_sync_src_template));
- gst_element_class_add_pad_template (element_class,
- gst_static_pad_template_get (&rtpsession_send_rtp_src_template));
- gst_element_class_add_pad_template (element_class,
- gst_static_pad_template_get (&rtpsession_send_rtcp_src_template));
-
- gst_element_class_set_details (element_class, &rtpsession_details);
-}
-
-static void
-gst_rtp_session_class_init (GstRtpSessionClass * klass)
-{
- GObjectClass *gobject_class;
- GstElementClass *gstelement_class;
-
- gobject_class = (GObjectClass *) klass;
- gstelement_class = (GstElementClass *) klass;
-
- g_type_class_add_private (klass, sizeof (GstRtpSessionPrivate));
-
- gobject_class->finalize = gst_rtp_session_finalize;
- gobject_class->set_property = gst_rtp_session_set_property;
- gobject_class->get_property = gst_rtp_session_get_property;
-
- /**
- * GstRtpSession::request-pt-map:
- * @sess: the object which received the signal
- * @pt: the pt
- *
- * Request the payload type as #GstCaps for @pt.
- */
- gst_rtp_session_signals[SIGNAL_REQUEST_PT_MAP] =
- g_signal_new ("request-pt-map", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpSessionClass, request_pt_map),
- NULL, NULL, gst_rtp_bin_marshal_BOXED__UINT, GST_TYPE_CAPS, 1,
- G_TYPE_UINT);
- /**
- * GstRtpSession::clear-pt-map:
- * @sess: the object which received the signal
- *
- * Clear the cached pt-maps requested with #GstRtpSession::request-pt-map.
- */
- gst_rtp_session_signals[SIGNAL_CLEAR_PT_MAP] =
- g_signal_new ("clear-pt-map", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstRtpSessionClass, clear_pt_map),
- NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0, G_TYPE_NONE);
-
- /**
- * GstRtpSession::on-new-ssrc:
- * @sess: the object which received the signal
- * @ssrc: the SSRC
- *
- * Notify of a new SSRC that entered @session.
- */
- gst_rtp_session_signals[SIGNAL_ON_NEW_SSRC] =
- g_signal_new ("on-new-ssrc", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpSessionClass, on_new_ssrc),
- NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT);
- /**
- * GstRtpSession::on-ssrc_collision:
- * @sess: the object which received the signal
- * @ssrc: the SSRC
- *
- * Notify when we have an SSRC collision
- */
- gst_rtp_session_signals[SIGNAL_ON_SSRC_COLLISION] =
- g_signal_new ("on-ssrc-collision", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpSessionClass,
- on_ssrc_collision), NULL, NULL, g_cclosure_marshal_VOID__UINT,
- G_TYPE_NONE, 1, G_TYPE_UINT);
- /**
- * GstRtpSession::on-ssrc_validated:
- * @sess: the object which received the signal
- * @ssrc: the SSRC
- *
- * Notify of a new SSRC that became validated.
- */
- gst_rtp_session_signals[SIGNAL_ON_SSRC_VALIDATED] =
- g_signal_new ("on-ssrc-validated", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpSessionClass,
- on_ssrc_validated), NULL, NULL, g_cclosure_marshal_VOID__UINT,
- G_TYPE_NONE, 1, G_TYPE_UINT);
- /**
- * GstRtpSession::on-ssrc_active:
- * @sess: the object which received the signal
- * @ssrc: the SSRC
- *
- * Notify of a SSRC that is active, i.e., sending RTCP.
- */
- gst_rtp_session_signals[SIGNAL_ON_SSRC_ACTIVE] =
- g_signal_new ("on-ssrc-active", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpSessionClass,
- on_ssrc_active), NULL, NULL, g_cclosure_marshal_VOID__UINT,
- G_TYPE_NONE, 1, G_TYPE_UINT);
- /**
- * GstRtpSession::on-ssrc-sdes:
- * @session: the object which received the signal
- * @src: the SSRC
- *
- * Notify that a new SDES was received for SSRC.
- */
- gst_rtp_session_signals[SIGNAL_ON_SSRC_SDES] =
- g_signal_new ("on-ssrc-sdes", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpSessionClass, on_ssrc_sdes),
- NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT);
-
- /**
- * GstRtpSession::on-bye-ssrc:
- * @sess: the object which received the signal
- * @ssrc: the SSRC
- *
- * Notify of an SSRC that became inactive because of a BYE packet.
- */
- gst_rtp_session_signals[SIGNAL_ON_BYE_SSRC] =
- g_signal_new ("on-bye-ssrc", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpSessionClass, on_bye_ssrc),
- NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT);
- /**
- * GstRtpSession::on-bye-timeout:
- * @sess: the object which received the signal
- * @ssrc: the SSRC
- *
- * Notify of an SSRC that has timed out because of BYE
- */
- gst_rtp_session_signals[SIGNAL_ON_BYE_TIMEOUT] =
- g_signal_new ("on-bye-timeout", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpSessionClass, on_bye_timeout),
- NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT);
- /**
- * GstRtpSession::on-timeout:
- * @sess: the object which received the signal
- * @ssrc: the SSRC
- *
- * Notify of an SSRC that has timed out
- */
- gst_rtp_session_signals[SIGNAL_ON_TIMEOUT] =
- g_signal_new ("on-timeout", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpSessionClass, on_timeout),
- NULL, NULL, g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT);
- /**
- * GstRtpSession::on-sender-timeout:
- * @sess: the object which received the signal
- * @ssrc: the SSRC
- *
- * Notify of a sender SSRC that has timed out and became a receiver
- */
- gst_rtp_session_signals[SIGNAL_ON_SENDER_TIMEOUT] =
- g_signal_new ("on-sender-timeout", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRtpSessionClass,
- on_sender_timeout), NULL, NULL, g_cclosure_marshal_VOID__UINT,
- G_TYPE_NONE, 1, G_TYPE_UINT);
-
- g_object_class_install_property (gobject_class, PROP_NTP_NS_BASE,
- g_param_spec_uint64 ("ntp-ns-base", "NTP base time",
- "The NTP base time corresponding to running_time 0", 0,
- G_MAXUINT64, DEFAULT_NTP_NS_BASE, G_PARAM_READWRITE));
-
- g_object_class_install_property (gobject_class, PROP_BANDWIDTH,
- g_param_spec_double ("bandwidth", "Bandwidth",
- "The bandwidth of the session",
- 0.0, G_MAXDOUBLE, DEFAULT_BANDWIDTH, G_PARAM_READWRITE));
-
- g_object_class_install_property (gobject_class, PROP_RTCP_FRACTION,
- g_param_spec_double ("rtcp-fraction", "RTCP Fraction",
- "The fraction of the bandwidth used for RTCP",
- 0.0, G_MAXDOUBLE, DEFAULT_RTCP_FRACTION, G_PARAM_READWRITE));
-
- g_object_class_install_property (gobject_class, PROP_SDES,
- g_param_spec_boxed ("sdes", "SDES",
- "The SDES items of this session",
- GST_TYPE_STRUCTURE, G_PARAM_READWRITE));
-
- g_object_class_install_property (gobject_class, PROP_NUM_SOURCES,
- g_param_spec_uint ("num-sources", "Num Sources",
- "The number of sources in the session", 0, G_MAXUINT,
- DEFAULT_NUM_SOURCES, G_PARAM_READABLE));
-
- g_object_class_install_property (gobject_class, PROP_NUM_ACTIVE_SOURCES,
- g_param_spec_uint ("num-active-sources", "Num Active Sources",
- "The number of active sources in the session", 0, G_MAXUINT,
- DEFAULT_NUM_ACTIVE_SOURCES, G_PARAM_READABLE));
-
- g_object_class_install_property (gobject_class, PROP_INTERNAL_SESSION,
- g_param_spec_object ("internal-session", "Internal Session",
- "The internal RTPSession object", RTP_TYPE_SESSION,
- G_PARAM_READABLE));
-
- gstelement_class->change_state =
- GST_DEBUG_FUNCPTR (gst_rtp_session_change_state);
- gstelement_class->request_new_pad =
- GST_DEBUG_FUNCPTR (gst_rtp_session_request_new_pad);
- gstelement_class->release_pad =
- GST_DEBUG_FUNCPTR (gst_rtp_session_release_pad);
-
- klass->clear_pt_map = GST_DEBUG_FUNCPTR (gst_rtp_session_clear_pt_map);
-
- GST_DEBUG_CATEGORY_INIT (gst_rtp_session_debug,
- "rtpsession", 0, "RTP Session");
-}
-
-static void
-gst_rtp_session_init (GstRtpSession * rtpsession, GstRtpSessionClass * klass)
-{
- rtpsession->priv = GST_RTP_SESSION_GET_PRIVATE (rtpsession);
- rtpsession->priv->lock = g_mutex_new ();
- rtpsession->priv->sysclock = gst_system_clock_obtain ();
- rtpsession->priv->session = rtp_session_new ();
-
- /* configure callbacks */
- rtp_session_set_callbacks (rtpsession->priv->session, &callbacks, rtpsession);
- /* configure signals */
- g_signal_connect (rtpsession->priv->session, "on-new-ssrc",
- (GCallback) on_new_ssrc, rtpsession);
- g_signal_connect (rtpsession->priv->session, "on-ssrc-collision",
- (GCallback) on_ssrc_collision, rtpsession);
- g_signal_connect (rtpsession->priv->session, "on-ssrc-validated",
- (GCallback) on_ssrc_validated, rtpsession);
- g_signal_connect (rtpsession->priv->session, "on-ssrc-active",
- (GCallback) on_ssrc_active, rtpsession);
- g_signal_connect (rtpsession->priv->session, "on-ssrc-sdes",
- (GCallback) on_ssrc_sdes, rtpsession);
- g_signal_connect (rtpsession->priv->session, "on-bye-ssrc",
- (GCallback) on_bye_ssrc, rtpsession);
- g_signal_connect (rtpsession->priv->session, "on-bye-timeout",
- (GCallback) on_bye_timeout, rtpsession);
- g_signal_connect (rtpsession->priv->session, "on-timeout",
- (GCallback) on_timeout, rtpsession);
- g_signal_connect (rtpsession->priv->session, "on-sender-timeout",
- (GCallback) on_sender_timeout, rtpsession);
- rtpsession->priv->ptmap = g_hash_table_new_full (NULL, NULL, NULL,
- (GDestroyNotify) gst_caps_unref);
-
- gst_segment_init (&rtpsession->recv_rtp_seg, GST_FORMAT_UNDEFINED);
- gst_segment_init (&rtpsession->send_rtp_seg, GST_FORMAT_UNDEFINED);
-
- rtpsession->priv->thread_stopped = TRUE;
-}
-
-static void
-gst_rtp_session_finalize (GObject * object)
-{
- GstRtpSession *rtpsession;
-
- rtpsession = GST_RTP_SESSION (object);
-
- if (rtpsession->recv_rtp_sink != NULL)
- gst_object_unref (rtpsession->recv_rtp_sink);
- if (rtpsession->recv_rtcp_sink != NULL)
- gst_object_unref (rtpsession->recv_rtcp_sink);
- if (rtpsession->send_rtp_sink != NULL)
- gst_object_unref (rtpsession->send_rtp_sink);
- if (rtpsession->send_rtcp_src != NULL)
- gst_object_unref (rtpsession->send_rtcp_src);
-
- g_hash_table_destroy (rtpsession->priv->ptmap);
- g_mutex_free (rtpsession->priv->lock);
- g_object_unref (rtpsession->priv->sysclock);
- g_object_unref (rtpsession->priv->session);
-
- G_OBJECT_CLASS (parent_class)->finalize (object);
-}
-
-static void
-gst_rtp_session_set_property (GObject * object, guint prop_id,
- const GValue * value, GParamSpec * pspec)
-{
- GstRtpSession *rtpsession;
- GstRtpSessionPrivate *priv;
-
- rtpsession = GST_RTP_SESSION (object);
- priv = rtpsession->priv;
-
- switch (prop_id) {
- case PROP_NTP_NS_BASE:
- GST_OBJECT_LOCK (rtpsession);
- priv->ntpnsbase = g_value_get_uint64 (value);
- GST_DEBUG_OBJECT (rtpsession, "setting NTP base to %" GST_TIME_FORMAT,
- GST_TIME_ARGS (priv->ntpnsbase));
- GST_OBJECT_UNLOCK (rtpsession);
- break;
- case PROP_BANDWIDTH:
- rtp_session_set_bandwidth (priv->session, g_value_get_double (value));
- break;
- case PROP_RTCP_FRACTION:
- rtp_session_set_rtcp_fraction (priv->session, g_value_get_double (value));
- break;
- case PROP_SDES:
- rtp_session_set_sdes_struct (priv->session, g_value_get_boxed (value));
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-static void
-gst_rtp_session_get_property (GObject * object, guint prop_id,
- GValue * value, GParamSpec * pspec)
-{
- GstRtpSession *rtpsession;
- GstRtpSessionPrivate *priv;
-
- rtpsession = GST_RTP_SESSION (object);
- priv = rtpsession->priv;
-
- switch (prop_id) {
- case PROP_NTP_NS_BASE:
- GST_OBJECT_LOCK (rtpsession);
- g_value_set_uint64 (value, priv->ntpnsbase);
- GST_OBJECT_UNLOCK (rtpsession);
- break;
- case PROP_BANDWIDTH:
- g_value_set_double (value, rtp_session_get_bandwidth (priv->session));
- break;
- case PROP_RTCP_FRACTION:
- g_value_set_double (value, rtp_session_get_rtcp_fraction (priv->session));
- break;
- case PROP_SDES:
- g_value_take_boxed (value, rtp_session_get_sdes_struct (priv->session));
- break;
- case PROP_NUM_SOURCES:
- g_value_set_uint (value, rtp_session_get_num_sources (priv->session));
- break;
- case PROP_NUM_ACTIVE_SOURCES:
- g_value_set_uint (value,
- rtp_session_get_num_active_sources (priv->session));
- break;
- case PROP_INTERNAL_SESSION:
- g_value_set_object (value, priv->session);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-static void
-get_current_times (GstRtpSession * rtpsession,
- GstClockTime * running_time, guint64 * ntpnstime)
-{
- guint64 ntpns;
- GstClock *clock;
- GstClockTime base_time, ntpnsbase, rt;
-
- GST_OBJECT_LOCK (rtpsession);
- if ((clock = GST_ELEMENT_CLOCK (rtpsession))) {
- base_time = GST_ELEMENT_CAST (rtpsession)->base_time;
- ntpnsbase = rtpsession->priv->ntpnsbase;
- gst_object_ref (clock);
- GST_OBJECT_UNLOCK (rtpsession);
-
- /* get current clock time and convert to running time */
- rt = gst_clock_get_time (clock) - base_time;
- /* add NTP base offset to get NTP ns time */
- ntpns = rt + ntpnsbase;
-
- gst_object_unref (clock);
- } else {
- GST_OBJECT_UNLOCK (rtpsession);
- rt = -1;
- ntpns = -1;
- }
- if (running_time)
- *running_time = rt;
- if (ntpnstime)
- *ntpnstime = ntpns;
-}
-
-static void
-rtcp_thread (GstRtpSession * rtpsession)
-{
- GstClockID id;
- GstClockTime current_time;
- GstClockTime next_timeout;
- guint64 ntpnstime;
-
- GST_DEBUG_OBJECT (rtpsession, "entering RTCP thread");
-
- GST_RTP_SESSION_LOCK (rtpsession);
-
- current_time = gst_clock_get_time (rtpsession->priv->sysclock);
-
- while (!rtpsession->priv->stop_thread) {
- GstClockReturn res;
-
- /* get initial estimate */
- next_timeout =
- rtp_session_next_timeout (rtpsession->priv->session, current_time);
-
- GST_DEBUG_OBJECT (rtpsession, "next check time %" GST_TIME_FORMAT,
- GST_TIME_ARGS (next_timeout));
-
- /* leave if no more timeouts, the session ended */
- if (next_timeout == GST_CLOCK_TIME_NONE)
- break;
-
- id = rtpsession->priv->id =
- gst_clock_new_single_shot_id (rtpsession->priv->sysclock, next_timeout);
- GST_RTP_SESSION_UNLOCK (rtpsession);
-
- res = gst_clock_id_wait (id, NULL);
-
- GST_RTP_SESSION_LOCK (rtpsession);
- gst_clock_id_unref (id);
- rtpsession->priv->id = NULL;
-
- if (rtpsession->priv->stop_thread)
- break;
-
- /* update current time */
- current_time = gst_clock_get_time (rtpsession->priv->sysclock);
-
- /* get current NTP time */
- get_current_times (rtpsession, NULL, &ntpnstime);
-
- /* we get unlocked because we need to perform reconsideration, don't perform
- * the timeout but get a new reporting estimate. */
- GST_DEBUG_OBJECT (rtpsession, "unlocked %d, current %" GST_TIME_FORMAT,
- res, GST_TIME_ARGS (current_time));
-
- /* perform actions, we ignore result. Release lock because it might push. */
- GST_RTP_SESSION_UNLOCK (rtpsession);
- rtp_session_on_timeout (rtpsession->priv->session, current_time, ntpnstime);
- GST_RTP_SESSION_LOCK (rtpsession);
- }
- /* mark the thread as stopped now */
- rtpsession->priv->thread_stopped = TRUE;
- GST_RTP_SESSION_UNLOCK (rtpsession);
-
- GST_DEBUG_OBJECT (rtpsession, "leaving RTCP thread");
-}
-
-static gboolean
-start_rtcp_thread (GstRtpSession * rtpsession)
-{
- GError *error = NULL;
- gboolean res;
-
- GST_DEBUG_OBJECT (rtpsession, "starting RTCP thread");
-
- GST_RTP_SESSION_LOCK (rtpsession);
- rtpsession->priv->stop_thread = FALSE;
- if (rtpsession->priv->thread_stopped) {
- /* if the thread stopped, and we still have a handle to the thread, join it
- * now. We can safely join with the lock held, the thread will not take it
- * anymore. */
- if (rtpsession->priv->thread)
- g_thread_join (rtpsession->priv->thread);
- /* only create a new thread if the old one was stopped. Otherwise we can
- * just reuse the currently running one. */
- rtpsession->priv->thread =
- g_thread_create ((GThreadFunc) rtcp_thread, rtpsession, TRUE, &error);
- rtpsession->priv->thread_stopped = FALSE;
- }
- GST_RTP_SESSION_UNLOCK (rtpsession);
-
- if (error != NULL) {
- res = FALSE;
- GST_DEBUG_OBJECT (rtpsession, "failed to start thread, %s", error->message);
- g_error_free (error);
- } else {
- res = TRUE;
- }
- return res;
-}
-
-static void
-stop_rtcp_thread (GstRtpSession * rtpsession)
-{
- GST_DEBUG_OBJECT (rtpsession, "stopping RTCP thread");
-
- GST_RTP_SESSION_LOCK (rtpsession);
- rtpsession->priv->stop_thread = TRUE;
- if (rtpsession->priv->id)
- gst_clock_id_unschedule (rtpsession->priv->id);
- GST_RTP_SESSION_UNLOCK (rtpsession);
-}
-
-static void
-join_rtcp_thread (GstRtpSession * rtpsession)
-{
- GST_RTP_SESSION_LOCK (rtpsession);
- /* don't try to join when we have no thread */
- if (rtpsession->priv->thread != NULL) {
- GST_DEBUG_OBJECT (rtpsession, "joining RTCP thread");
- GST_RTP_SESSION_UNLOCK (rtpsession);
-
- g_thread_join (rtpsession->priv->thread);
-
- GST_RTP_SESSION_LOCK (rtpsession);
- /* after the join, take the lock and clear the thread structure. The caller
- * is supposed to not concurrently call start and join. */
- rtpsession->priv->thread = NULL;
- }
- GST_RTP_SESSION_UNLOCK (rtpsession);
-}
-
-static GstStateChangeReturn
-gst_rtp_session_change_state (GstElement * element, GstStateChange transition)
-{
- GstStateChangeReturn res;
- GstRtpSession *rtpsession;
- GstRtpSessionPrivate *priv;
-
- rtpsession = GST_RTP_SESSION (element);
- priv = rtpsession->priv;
-
- switch (transition) {
- case GST_STATE_CHANGE_NULL_TO_READY:
- break;
- case GST_STATE_CHANGE_READY_TO_PAUSED:
- break;
- case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
- break;
- case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
- case GST_STATE_CHANGE_PAUSED_TO_READY:
- /* no need to join yet, we might want to continue later. Also, the
- * dataflow could block downstream so that a join could just block
- * forever. */
- stop_rtcp_thread (rtpsession);
- break;
- default:
- break;
- }
-
- res = parent_class->change_state (element, transition);
-
- switch (transition) {
- case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
- if (!start_rtcp_thread (rtpsession))
- goto failed_thread;
- break;
- case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
- break;
- case GST_STATE_CHANGE_PAUSED_TO_READY:
- /* downstream is now releasing the dataflow and we can join. */
- join_rtcp_thread (rtpsession);
- break;
- case GST_STATE_CHANGE_READY_TO_NULL:
- break;
- default:
- break;
- }
- return res;
-
- /* ERRORS */
-failed_thread:
- {
- return GST_STATE_CHANGE_FAILURE;
- }
-}
-
-static gboolean
-return_true (gpointer key, gpointer value, gpointer user_data)
-{
- return TRUE;
-}
-
-static void
-gst_rtp_session_clear_pt_map (GstRtpSession * rtpsession)
-{
- g_hash_table_foreach_remove (rtpsession->priv->ptmap, return_true, NULL);
-}
-
-/* called when the session manager has an RTP packet or a list of packets
- * ready for further processing */
-static GstFlowReturn
-gst_rtp_session_process_rtp (RTPSession * sess, RTPSource * src,
- GstBuffer * buffer, gpointer user_data)
-{
- GstFlowReturn result;
- GstRtpSession *rtpsession;
- GstRtpSessionPrivate *priv;
-
- rtpsession = GST_RTP_SESSION (user_data);
- priv = rtpsession->priv;
-
- if (rtpsession->recv_rtp_src) {
- GST_LOG_OBJECT (rtpsession, "pushing received RTP packet");
- result = gst_pad_push (rtpsession->recv_rtp_src, buffer);
- } else {
- GST_DEBUG_OBJECT (rtpsession, "dropping received RTP packet");
- gst_buffer_unref (buffer);
- result = GST_FLOW_OK;
- }
- return result;
-}
-
-/* called when the session manager has an RTP packet ready for further
- * sending */
-static GstFlowReturn
-gst_rtp_session_send_rtp (RTPSession * sess, RTPSource * src,
- gpointer data, gpointer user_data)
-{
- GstFlowReturn result;
- GstRtpSession *rtpsession;
- GstRtpSessionPrivate *priv;
-
- rtpsession = GST_RTP_SESSION (user_data);
- priv = rtpsession->priv;
-
- if (rtpsession->send_rtp_src) {
- if (GST_IS_BUFFER (data)) {
- GST_LOG_OBJECT (rtpsession, "sending RTP packet");
- result = gst_pad_push (rtpsession->send_rtp_src, GST_BUFFER_CAST (data));
- } else {
- GST_LOG_OBJECT (rtpsession, "sending RTP list");
- result = gst_pad_push_list (rtpsession->send_rtp_src,
- GST_BUFFER_LIST_CAST (data));
- }
- } else {
- gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
- result = GST_FLOW_OK;
- }
- return result;
-}
-
-/* called when the session manager has an RTCP packet ready for further
- * sending. The eos flag is set when an EOS event should be sent downstream as
- * well. */
-static GstFlowReturn
-gst_rtp_session_send_rtcp (RTPSession * sess, RTPSource * src,
- GstBuffer * buffer, gboolean eos, gpointer user_data)
-{
- GstFlowReturn result;
- GstRtpSession *rtpsession;
- GstRtpSessionPrivate *priv;
-
- rtpsession = GST_RTP_SESSION (user_data);
- priv = rtpsession->priv;
-
- if (rtpsession->send_rtcp_src) {
- GstCaps *caps;
-
- /* set rtcp caps on output pad */
- if (!(caps = GST_PAD_CAPS (rtpsession->send_rtcp_src))) {
- caps = gst_caps_new_simple ("application/x-rtcp", NULL);
- gst_pad_set_caps (rtpsession->send_rtcp_src, caps);
- gst_caps_unref (caps);
- }
- gst_buffer_set_caps (buffer, caps);
- GST_LOG_OBJECT (rtpsession, "sending RTCP");
- result = gst_pad_push (rtpsession->send_rtcp_src, buffer);
-
- /* we have to send EOS after this packet */
- if (eos) {
- GST_LOG_OBJECT (rtpsession, "sending EOS");
- gst_pad_push_event (rtpsession->send_rtcp_src, gst_event_new_eos ());
- }
- } else {
- GST_DEBUG_OBJECT (rtpsession, "not sending RTCP, no output pad");
- gst_buffer_unref (buffer);
- result = GST_FLOW_OK;
- }
- return result;
-}
-
-/* called when the session manager has an SR RTCP packet ready for handling
- * inter stream synchronisation */
-static GstFlowReturn
-gst_rtp_session_sync_rtcp (RTPSession * sess,
- RTPSource * src, GstBuffer * buffer, gpointer user_data)
-{
- GstFlowReturn result;
- GstRtpSession *rtpsession;
- GstRtpSessionPrivate *priv;
-
- rtpsession = GST_RTP_SESSION (user_data);
- priv = rtpsession->priv;
-
- if (rtpsession->sync_src) {
- GstCaps *caps;
-
- /* set rtcp caps on output pad */
- if (!(caps = GST_PAD_CAPS (rtpsession->sync_src))) {
- caps = gst_caps_new_simple ("application/x-rtcp", NULL);
- gst_pad_set_caps (rtpsession->sync_src, caps);
- gst_caps_unref (caps);
- }
- gst_buffer_set_caps (buffer, caps);
- GST_LOG_OBJECT (rtpsession, "sending Sync RTCP");
- result = gst_pad_push (rtpsession->sync_src, buffer);
- } else {
- GST_DEBUG_OBJECT (rtpsession, "not sending Sync RTCP, no output pad");
- gst_buffer_unref (buffer);
- result = GST_FLOW_OK;
- }
- return result;
-}
-
-static void
-gst_rtp_session_cache_caps (GstRtpSession * rtpsession, GstCaps * caps)
-{
- GstRtpSessionPrivate *priv;
- const GstStructure *s;
- gint payload;
-
- priv = rtpsession->priv;
-
- GST_DEBUG_OBJECT (rtpsession, "parsing caps");
-
- s = gst_caps_get_structure (caps, 0);
- if (!gst_structure_get_int (s, "payload", &payload))
- return;
-
- if (g_hash_table_lookup (priv->ptmap, GINT_TO_POINTER (payload)))
- return;
-
- g_hash_table_insert (priv->ptmap, GINT_TO_POINTER (payload),
- gst_caps_ref (caps));
-}
-
-/* called when the session manager needs the clock rate */
-static gint
-gst_rtp_session_clock_rate (RTPSession * sess, guint8 payload,
- gpointer user_data)
-{
- gint ipayload, result = -1;
- GstRtpSession *rtpsession;
- GstRtpSessionPrivate *priv;
- GValue ret = { 0 };
- GValue args[2] = { {0}, {0} };
- GstCaps *caps;
- const GstStructure *s;
-
- rtpsession = GST_RTP_SESSION_CAST (user_data);
- priv = rtpsession->priv;
-
- GST_RTP_SESSION_LOCK (rtpsession);
- ipayload = payload; /* make compiler happy */
- caps = g_hash_table_lookup (priv->ptmap, GINT_TO_POINTER (ipayload));
- if (caps) {
- gst_caps_ref (caps);
- goto found;
- }
-
- /* not found in the cache, try to get it with a signal */
- g_value_init (&args[0], GST_TYPE_ELEMENT);
- g_value_set_object (&args[0], rtpsession);
- g_value_init (&args[1], G_TYPE_UINT);
- g_value_set_uint (&args[1], payload);
-
- g_value_init (&ret, GST_TYPE_CAPS);
- g_value_set_boxed (&ret, NULL);
-
- g_signal_emitv (args, gst_rtp_session_signals[SIGNAL_REQUEST_PT_MAP], 0,
- &ret);
-
- g_value_unset (&args[0]);
- g_value_unset (&args[1]);
- caps = (GstCaps *) g_value_dup_boxed (&ret);
- g_value_unset (&ret);
- if (!caps)
- goto no_caps;
-
- gst_rtp_session_cache_caps (rtpsession, caps);
-
-found:
- s = gst_caps_get_structure (caps, 0);
- if (!gst_structure_get_int (s, "clock-rate", &result))
- goto no_clock_rate;
-
- gst_caps_unref (caps);
-
- GST_DEBUG_OBJECT (rtpsession, "parsed clock-rate %d", result);
-
-done:
- GST_RTP_SESSION_UNLOCK (rtpsession);
-
- return result;
-
- /* ERRORS */
-no_caps:
- {
- GST_DEBUG_OBJECT (rtpsession, "could not get caps");
- goto done;
- }
-no_clock_rate:
- {
- gst_caps_unref (caps);
- GST_DEBUG_OBJECT (rtpsession, "No clock-rate in caps!");
- goto done;
- }
-}
-
-/* called when the session manager asks us to reconsider the timeout */
-static void
-gst_rtp_session_reconsider (RTPSession * sess, gpointer user_data)
-{
- GstRtpSession *rtpsession;
-
- rtpsession = GST_RTP_SESSION_CAST (user_data);
-
- GST_RTP_SESSION_LOCK (rtpsession);
- GST_DEBUG_OBJECT (rtpsession, "unlock timer for reconsideration");
- if (rtpsession->priv->id)
- gst_clock_id_unschedule (rtpsession->priv->id);
- GST_RTP_SESSION_UNLOCK (rtpsession);
-}
-
-static gboolean
-gst_rtp_session_event_recv_rtp_sink (GstPad * pad, GstEvent * event)
-{
- GstRtpSession *rtpsession;
- GstRtpSessionPrivate *priv;
- gboolean ret = FALSE;
-
- rtpsession = GST_RTP_SESSION (gst_pad_get_parent (pad));
- priv = rtpsession->priv;
-
- GST_DEBUG_OBJECT (rtpsession, "received event %s",
- GST_EVENT_TYPE_NAME (event));
-
- switch (GST_EVENT_TYPE (event)) {
- case GST_EVENT_FLUSH_STOP:
- gst_segment_init (&rtpsession->recv_rtp_seg, GST_FORMAT_UNDEFINED);
- ret = gst_pad_push_event (rtpsession->recv_rtp_src, event);
- break;
- case GST_EVENT_NEWSEGMENT:
- {
- gboolean update;
- gdouble rate, arate;
- GstFormat format;
- gint64 start, stop, time;
- GstSegment *segment;
-
- segment = &rtpsession->recv_rtp_seg;
-
- /* the newsegment event is needed to convert the RTP timestamp to
- * running_time, which is needed to generate a mapping from RTP to NTP
- * timestamps in SR reports */
- gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
- &start, &stop, &time);
-
- GST_DEBUG_OBJECT (rtpsession,
- "configured NEWSEGMENT update %d, rate %lf, applied rate %lf, "
- "format GST_FORMAT_TIME, "
- "%" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT
- ", time %" GST_TIME_FORMAT ", accum %" GST_TIME_FORMAT,
- update, rate, arate, GST_TIME_ARGS (segment->start),
- GST_TIME_ARGS (segment->stop), GST_TIME_ARGS (segment->time),
- GST_TIME_ARGS (segment->accum));
-
- gst_segment_set_newsegment_full (segment, update, rate,
- arate, format, start, stop, time);
-
- /* push event forward */
- ret = gst_pad_push_event (rtpsession->recv_rtp_src, event);
- break;
- }
- default:
- ret = gst_pad_push_event (rtpsession->recv_rtp_src, event);
- break;
- }
- gst_object_unref (rtpsession);
-
- return ret;
-
-}
-
-static GList *
-gst_rtp_session_internal_links (GstPad * pad)
-{
- GstRtpSession *rtpsession;
- GstRtpSessionPrivate *priv;
- GList *res = NULL;
-
- rtpsession = GST_RTP_SESSION (gst_pad_get_parent (pad));
- priv = rtpsession->priv;
-
- if (pad == rtpsession->recv_rtp_src) {
- res = g_list_prepend (res, rtpsession->recv_rtp_sink);
- } else if (pad == rtpsession->recv_rtp_sink) {
- res = g_list_prepend (res, rtpsession->recv_rtp_src);
- } else if (pad == rtpsession->send_rtp_src) {
- res = g_list_prepend (res, rtpsession->send_rtp_sink);
- } else if (pad == rtpsession->send_rtp_sink) {
- res = g_list_prepend (res, rtpsession->send_rtp_src);
- }
-
- gst_object_unref (rtpsession);
-
- return res;
-}
-
-static gboolean
-gst_rtp_session_sink_setcaps (GstPad * pad, GstCaps * caps)
-{
- GstRtpSession *rtpsession;
- GstRtpSessionPrivate *priv;
-
- rtpsession = GST_RTP_SESSION (gst_pad_get_parent (pad));
- priv = rtpsession->priv;
-
- GST_RTP_SESSION_LOCK (rtpsession);
- gst_rtp_session_cache_caps (rtpsession, caps);
- GST_RTP_SESSION_UNLOCK (rtpsession);
-
- gst_object_unref (rtpsession);
-
- return TRUE;
-}
-
-/* receive a packet from a sender, send it to the RTP session manager and
- * forward the packet on the rtp_src pad
- */
-static GstFlowReturn
-gst_rtp_session_chain_recv_rtp (GstPad * pad, GstBuffer * buffer)
-{
- GstRtpSession *rtpsession;
- GstRtpSessionPrivate *priv;
- GstFlowReturn ret;
- GstClockTime current_time, running_time;
- guint64 ntpnstime;
- GstClockTime timestamp;
-
- rtpsession = GST_RTP_SESSION (gst_pad_get_parent (pad));
- priv = rtpsession->priv;
-
- GST_LOG_OBJECT (rtpsession, "received RTP packet");
-
- /* get NTP time when this packet was captured, this depends on the timestamp. */
- timestamp = GST_BUFFER_TIMESTAMP (buffer);
- if (GST_CLOCK_TIME_IS_VALID (timestamp)) {
- /* convert to running time using the segment values */
- running_time =
- gst_segment_to_running_time (&rtpsession->recv_rtp_seg, GST_FORMAT_TIME,
- timestamp);
- /* add constant to convert running time to NTP time */
- ntpnstime = running_time + priv->ntpnsbase;
- } else {
- get_current_times (rtpsession, &running_time, &ntpnstime);
- }
- current_time = gst_clock_get_time (priv->sysclock);
-
- ret = rtp_session_process_rtp (priv->session, buffer, current_time,
- running_time, ntpnstime);
- if (ret != GST_FLOW_OK)
- goto push_error;
-
-done:
- gst_object_unref (rtpsession);
-
- return ret;
-
- /* ERRORS */
-push_error:
- {
- GST_DEBUG_OBJECT (rtpsession, "process returned %s",
- gst_flow_get_name (ret));
- goto done;
- }
-}
-
-static gboolean
-gst_rtp_session_event_recv_rtcp_sink (GstPad * pad, GstEvent * event)
-{
- GstRtpSession *rtpsession;
- GstRtpSessionPrivate *priv;
- gboolean ret = FALSE;
-
- rtpsession = GST_RTP_SESSION (gst_pad_get_parent (pad));
- priv = rtpsession->priv;
-
- GST_DEBUG_OBJECT (rtpsession, "received event %s",
- GST_EVENT_TYPE_NAME (event));
-
- switch (GST_EVENT_TYPE (event)) {
- default:
- ret = gst_pad_push_event (rtpsession->sync_src, event);
- break;
- }
- gst_object_unref (rtpsession);
-
- return ret;
-}
-
-/* Receive an RTCP packet from a sender, send it to the RTP session manager and
- * forward the SR packets to the sync_src pad.
- */
-static GstFlowReturn
-gst_rtp_session_chain_recv_rtcp (GstPad * pad, GstBuffer * buffer)
-{
- GstRtpSession *rtpsession;
- GstRtpSessionPrivate *priv;
- GstClockTime current_time;
- GstFlowReturn ret;
-
- rtpsession = GST_RTP_SESSION (gst_pad_get_parent (pad));
- priv = rtpsession->priv;
-
- GST_LOG_OBJECT (rtpsession, "received RTCP packet");
-
- current_time = gst_clock_get_time (priv->sysclock);
- ret = rtp_session_process_rtcp (priv->session, buffer, current_time);
-
- gst_object_unref (rtpsession);
-
- return GST_FLOW_OK;
-}
-
-static gboolean
-gst_rtp_session_query_send_rtcp_src (GstPad * pad, GstQuery * query)
-{
- GstRtpSession *rtpsession;
- GstRtpSessionPrivate *priv;
- gboolean ret = FALSE;
-
- rtpsession = GST_RTP_SESSION (gst_pad_get_parent (pad));
- priv = rtpsession->priv;
-
- GST_DEBUG_OBJECT (rtpsession, "received QUERY");
-
- switch (GST_QUERY_TYPE (query)) {
- case GST_QUERY_LATENCY:
- ret = TRUE;
- /* use the defaults for the latency query. */
- gst_query_set_latency (query, FALSE, 0, -1);
- break;
- default:
- /* other queries simply fail for now */
- break;
- }
-
- gst_object_unref (rtpsession);
-
- return ret;
-}
-
-static gboolean
-gst_rtp_session_event_send_rtcp_src (GstPad * pad, GstEvent * event)
-{
- GstRtpSession *rtpsession;
- GstRtpSessionPrivate *priv;
- gboolean ret;
-
- rtpsession = GST_RTP_SESSION (gst_pad_get_parent (pad));
- priv = rtpsession->priv;
-
- GST_DEBUG_OBJECT (rtpsession, "received EVENT");
-
- switch (GST_EVENT_TYPE (event)) {
- case GST_EVENT_SEEK:
- case GST_EVENT_LATENCY:
- gst_event_unref (event);
- ret = TRUE;
- break;
- default:
- /* other events simply fail for now */
- gst_event_unref (event);
- ret = FALSE;
- break;
- }
-
- gst_object_unref (rtpsession);
-
- return ret;
-}
-
-
-static gboolean
-gst_rtp_session_event_send_rtp_sink (GstPad * pad, GstEvent * event)
-{
- GstRtpSession *rtpsession;
- GstRtpSessionPrivate *priv;
- gboolean ret = FALSE;
-
- rtpsession = GST_RTP_SESSION (gst_pad_get_parent (pad));
- priv = rtpsession->priv;
-
- GST_DEBUG_OBJECT (rtpsession, "received event");
-
- switch (GST_EVENT_TYPE (event)) {
- case GST_EVENT_FLUSH_STOP:
- gst_segment_init (&rtpsession->send_rtp_seg, GST_FORMAT_UNDEFINED);
- ret = gst_pad_push_event (rtpsession->send_rtp_src, event);
- break;
- case GST_EVENT_NEWSEGMENT:{
- gboolean update;
- gdouble rate, arate;
- GstFormat format;
- gint64 start, stop, time;
- GstSegment *segment;
-
- segment = &rtpsession->send_rtp_seg;
-
- /* the newsegment event is needed to convert the RTP timestamp to
- * running_time, which is needed to generate a mapping from RTP to NTP
- * timestamps in SR reports */
- gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
- &start, &stop, &time);
-
- GST_DEBUG_OBJECT (rtpsession,
- "configured NEWSEGMENT update %d, rate %lf, applied rate %lf, "
- "format GST_FORMAT_TIME, "
- "%" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT
- ", time %" GST_TIME_FORMAT ", accum %" GST_TIME_FORMAT,
- update, rate, arate, GST_TIME_ARGS (segment->start),
- GST_TIME_ARGS (segment->stop), GST_TIME_ARGS (segment->time),
- GST_TIME_ARGS (segment->accum));
-
- gst_segment_set_newsegment_full (segment, update, rate,
- arate, format, start, stop, time);
-
- /* push event forward */
- ret = gst_pad_push_event (rtpsession->send_rtp_src, event);
- break;
- }
- case GST_EVENT_EOS:{
- GstClockTime current_time;
-
- /* push downstream FIXME, we are not supposed to leave the session just
- * because we stop sending. */
- ret = gst_pad_push_event (rtpsession->send_rtp_src, event);
- current_time = gst_clock_get_time (rtpsession->priv->sysclock);
- GST_DEBUG_OBJECT (rtpsession, "scheduling BYE message");
- rtp_session_schedule_bye (rtpsession->priv->session, "End of stream",
- current_time);
- break;
- }
- default:
- ret = gst_pad_push_event (rtpsession->send_rtp_src, event);
- break;
- }
- gst_object_unref (rtpsession);
-
- return ret;
-}
-
-static GstCaps *
-gst_rtp_session_getcaps_send_rtp (GstPad * pad)
-{
- GstRtpSession *rtpsession;
- GstRtpSessionPrivate *priv;
- GstCaps *result;
- GstStructure *s1, *s2;
- guint ssrc;
-
- rtpsession = GST_RTP_SESSION (gst_pad_get_parent (pad));
- priv = rtpsession->priv;
-
- ssrc = rtp_session_get_internal_ssrc (priv->session);
-
- /* we can basically accept anything but we prefer to receive packets with our
- * internal SSRC so that we don't have to patch it. Create a structure with
- * the SSRC and another one without. */
- s1 = gst_structure_new ("application/x-rtp", "ssrc", G_TYPE_UINT, ssrc, NULL);
- s2 = gst_structure_new ("application/x-rtp", NULL);
-
- result = gst_caps_new_full (s1, s2, NULL);
-
- GST_DEBUG_OBJECT (rtpsession, "getting caps %" GST_PTR_FORMAT, result);
-
- gst_object_unref (rtpsession);
-
- return result;
-}
-
-static gboolean
-gst_rtp_session_setcaps_send_rtp (GstPad * pad, GstCaps * caps)
-{
- GstRtpSession *rtpsession;
- GstRtpSessionPrivate *priv;
- GstStructure *s = gst_caps_get_structure (caps, 0);
- guint ssrc;
-
- rtpsession = GST_RTP_SESSION (gst_pad_get_parent (pad));
- priv = rtpsession->priv;
-
- if (gst_structure_get_uint (s, "ssrc", &ssrc)) {
- GST_DEBUG_OBJECT (rtpsession, "setting internal SSRC to %08x", ssrc);
- rtp_session_set_internal_ssrc (priv->session, ssrc);
- }
-
- gst_object_unref (rtpsession);
-
- return TRUE;
-}
-
-/* Recieve an RTP packet or a list of packets to be send to the receivers,
- * send to RTP session manager and forward to send_rtp_src.
- */
-static GstFlowReturn
-gst_rtp_session_chain_send_rtp_common (GstPad * pad, gpointer data,
- gboolean is_list)
-{
- GstRtpSession *rtpsession;
- GstRtpSessionPrivate *priv;
- GstFlowReturn ret;
- GstClockTime timestamp;
- GstClockTime current_time;
- guint64 ntpnstime;
-
- rtpsession = GST_RTP_SESSION (gst_pad_get_parent (pad));
- priv = rtpsession->priv;
-
- GST_LOG_OBJECT (rtpsession, "received RTP %s", is_list ? "list" : "packet");
-
- /* get NTP time when this packet was captured, this depends on the timestamp. */
- if (is_list) {
- GstBuffer *buffer = NULL;
-
- /* All groups in an list have the same timestamp.
- * So, just take it from the first group. */
- buffer = gst_buffer_list_get (GST_BUFFER_LIST_CAST (data), 0, 0);
- if (buffer)
- timestamp = GST_BUFFER_TIMESTAMP (buffer);
- else
- timestamp = -1;
- } else {
- timestamp = GST_BUFFER_TIMESTAMP (GST_BUFFER_CAST (data));
- }
- if (GST_CLOCK_TIME_IS_VALID (timestamp)) {
- /* convert to running time using the segment start value. */
- ntpnstime =
- gst_segment_to_running_time (&rtpsession->send_rtp_seg, GST_FORMAT_TIME,
- timestamp);
- /* convert to NTP time by adding the NTP base */
- ntpnstime += priv->ntpnsbase;
- } else {
- /* no timestamp, we could take the current running_time and convert it to
- * NTP time. */
- ntpnstime = -1;
- }
-
- current_time = gst_clock_get_time (priv->sysclock);
- ret =
- rtp_session_send_rtp (priv->session, data, is_list, current_time,
- ntpnstime);
- if (ret != GST_FLOW_OK)
- goto push_error;
-
-done:
- gst_object_unref (rtpsession);
-
- return ret;
-
- /* ERRORS */
-push_error:
- {
- GST_DEBUG_OBJECT (rtpsession, "process returned %s",
- gst_flow_get_name (ret));
- goto done;
- }
-}
-
-static GstFlowReturn
-gst_rtp_session_chain_send_rtp (GstPad * pad, GstBuffer * buffer)
-{
- return gst_rtp_session_chain_send_rtp_common (pad, buffer, FALSE);
-}
-
-static GstFlowReturn
-gst_rtp_session_chain_send_rtp_list (GstPad * pad, GstBufferList * list)
-{
- return gst_rtp_session_chain_send_rtp_common (pad, list, TRUE);
-}
-
-/* Create sinkpad to receive RTP packets from senders. This will also create a
- * srcpad for the RTP packets.
- */
-static GstPad *
-create_recv_rtp_sink (GstRtpSession * rtpsession)
-{
- GST_DEBUG_OBJECT (rtpsession, "creating RTP sink pad");
-
- rtpsession->recv_rtp_sink =
- gst_pad_new_from_static_template (&rtpsession_recv_rtp_sink_template,
- "recv_rtp_sink");
- gst_pad_set_chain_function (rtpsession->recv_rtp_sink,
- gst_rtp_session_chain_recv_rtp);
- gst_pad_set_event_function (rtpsession->recv_rtp_sink,
- (GstPadEventFunction) gst_rtp_session_event_recv_rtp_sink);
- gst_pad_set_setcaps_function (rtpsession->recv_rtp_sink,
- gst_rtp_session_sink_setcaps);
- gst_pad_set_internal_link_function (rtpsession->recv_rtp_sink,
- gst_rtp_session_internal_links);
- gst_pad_set_active (rtpsession->recv_rtp_sink, TRUE);
- gst_element_add_pad (GST_ELEMENT_CAST (rtpsession),
- rtpsession->recv_rtp_sink);
-
- GST_DEBUG_OBJECT (rtpsession, "creating RTP src pad");
- rtpsession->recv_rtp_src =
- gst_pad_new_from_static_template (&rtpsession_recv_rtp_src_template,
- "recv_rtp_src");
- gst_pad_set_internal_link_function (rtpsession->recv_rtp_src,
- gst_rtp_session_internal_links);
- gst_pad_use_fixed_caps (rtpsession->recv_rtp_src);
- gst_pad_set_active (rtpsession->recv_rtp_src, TRUE);
- gst_element_add_pad (GST_ELEMENT_CAST (rtpsession), rtpsession->recv_rtp_src);
-
- return rtpsession->recv_rtp_sink;
-}
-
-/* Remove sinkpad to receive RTP packets from senders. This will also remove
- * the srcpad for the RTP packets.
- */
-static void
-remove_recv_rtp_sink (GstRtpSession * rtpsession)
-{
- GST_DEBUG_OBJECT (rtpsession, "removing RTP sink pad");
-
- /* deactivate from source to sink */
- gst_pad_set_active (rtpsession->recv_rtp_src, FALSE);
- gst_pad_set_active (rtpsession->recv_rtp_sink, FALSE);
-
- /* remove pads */
- gst_element_remove_pad (GST_ELEMENT_CAST (rtpsession),
- rtpsession->recv_rtp_sink);
- rtpsession->recv_rtp_sink = NULL;
-
- GST_DEBUG_OBJECT (rtpsession, "removing RTP src pad");
- gst_element_remove_pad (GST_ELEMENT_CAST (rtpsession),
- rtpsession->recv_rtp_src);
- rtpsession->recv_rtp_src = NULL;
-}
-
-/* Create a sinkpad to receive RTCP messages from senders, this will also create a
- * sync_src pad for the SR packets.
- */
-static GstPad *
-create_recv_rtcp_sink (GstRtpSession * rtpsession)
-{
- GST_DEBUG_OBJECT (rtpsession, "creating RTCP sink pad");
-
- rtpsession->recv_rtcp_sink =
- gst_pad_new_from_static_template (&rtpsession_recv_rtcp_sink_template,
- "recv_rtcp_sink");
- gst_pad_set_chain_function (rtpsession->recv_rtcp_sink,
- gst_rtp_session_chain_recv_rtcp);
- gst_pad_set_event_function (rtpsession->recv_rtcp_sink,
- (GstPadEventFunction) gst_rtp_session_event_recv_rtcp_sink);
- gst_pad_set_internal_link_function (rtpsession->recv_rtcp_sink,
- gst_rtp_session_internal_links);
- gst_pad_set_active (rtpsession->recv_rtcp_sink, TRUE);
- gst_element_add_pad (GST_ELEMENT_CAST (rtpsession),
- rtpsession->recv_rtcp_sink);
-
- GST_DEBUG_OBJECT (rtpsession, "creating sync src pad");
- rtpsession->sync_src =
- gst_pad_new_from_static_template (&rtpsession_sync_src_template,
- "sync_src");
- gst_pad_set_internal_link_function (rtpsession->sync_src,
- gst_rtp_session_internal_links);
- gst_pad_use_fixed_caps (rtpsession->sync_src);
- gst_pad_set_active (rtpsession->sync_src, TRUE);
- gst_element_add_pad (GST_ELEMENT_CAST (rtpsession), rtpsession->sync_src);
-
- return rtpsession->recv_rtcp_sink;
-}
-
-static void
-remove_recv_rtcp_sink (GstRtpSession * rtpsession)
-{
- GST_DEBUG_OBJECT (rtpsession, "removing RTCP sink pad");
-
- gst_pad_set_active (rtpsession->sync_src, FALSE);
- gst_pad_set_active (rtpsession->recv_rtcp_sink, FALSE);
-
- gst_element_remove_pad (GST_ELEMENT_CAST (rtpsession),
- rtpsession->recv_rtcp_sink);
- rtpsession->recv_rtcp_sink = NULL;
-
- GST_DEBUG_OBJECT (rtpsession, "removing sync src pad");
- gst_element_remove_pad (GST_ELEMENT_CAST (rtpsession), rtpsession->sync_src);
- rtpsession->sync_src = NULL;
-}
-
-/* Create a sinkpad to receive RTP packets for receivers. This will also create a
- * send_rtp_src pad.
- */
-static GstPad *
-create_send_rtp_sink (GstRtpSession * rtpsession)
-{
- GST_DEBUG_OBJECT (rtpsession, "creating pad");
-
- rtpsession->send_rtp_sink =
- gst_pad_new_from_static_template (&rtpsession_send_rtp_sink_template,
- "send_rtp_sink");
- gst_pad_set_chain_function (rtpsession->send_rtp_sink,
- gst_rtp_session_chain_send_rtp);
- gst_pad_set_chain_list_function (rtpsession->send_rtp_sink,
- gst_rtp_session_chain_send_rtp_list);
- gst_pad_set_getcaps_function (rtpsession->send_rtp_sink,
- gst_rtp_session_getcaps_send_rtp);
- gst_pad_set_setcaps_function (rtpsession->send_rtp_sink,
- gst_rtp_session_setcaps_send_rtp);
- gst_pad_set_event_function (rtpsession->send_rtp_sink,
- (GstPadEventFunction) gst_rtp_session_event_send_rtp_sink);
- gst_pad_set_internal_link_function (rtpsession->send_rtp_sink,
- gst_rtp_session_internal_links);
- gst_pad_set_active (rtpsession->send_rtp_sink, TRUE);
- gst_element_add_pad (GST_ELEMENT_CAST (rtpsession),
- rtpsession->send_rtp_sink);
-
- rtpsession->send_rtp_src =
- gst_pad_new_from_static_template (&rtpsession_send_rtp_src_template,
- "send_rtp_src");
- gst_pad_set_internal_link_function (rtpsession->send_rtp_src,
- gst_rtp_session_internal_links);
- gst_pad_set_active (rtpsession->send_rtp_src, TRUE);
- gst_element_add_pad (GST_ELEMENT_CAST (rtpsession), rtpsession->send_rtp_src);
-
- return rtpsession->send_rtp_sink;
-}
-
-static void
-remove_send_rtp_sink (GstRtpSession * rtpsession)
-{
- GST_DEBUG_OBJECT (rtpsession, "removing pad");
-
- gst_pad_set_active (rtpsession->send_rtp_src, FALSE);
- gst_pad_set_active (rtpsession->send_rtp_sink, FALSE);
-
- gst_element_remove_pad (GST_ELEMENT_CAST (rtpsession),
- rtpsession->send_rtp_sink);
- rtpsession->send_rtp_sink = NULL;
-
- gst_element_remove_pad (GST_ELEMENT_CAST (rtpsession),
- rtpsession->send_rtp_src);
- rtpsession->send_rtp_src = NULL;
-}
-
-/* Create a srcpad with the RTCP packets to send out.
- * This pad will be driven by the RTP session manager when it wants to send out
- * RTCP packets.
- */
-static GstPad *
-create_send_rtcp_src (GstRtpSession * rtpsession)
-{
- GST_DEBUG_OBJECT (rtpsession, "creating pad");
-
- rtpsession->send_rtcp_src =
- gst_pad_new_from_static_template (&rtpsession_send_rtcp_src_template,
- "send_rtcp_src");
- gst_pad_use_fixed_caps (rtpsession->send_rtcp_src);
- gst_pad_set_active (rtpsession->send_rtcp_src, TRUE);
- gst_pad_set_internal_link_function (rtpsession->send_rtcp_src,
- gst_rtp_session_internal_links);
- gst_pad_set_query_function (rtpsession->send_rtcp_src,
- gst_rtp_session_query_send_rtcp_src);
- gst_pad_set_event_function (rtpsession->send_rtcp_src,
- gst_rtp_session_event_send_rtcp_src);
- gst_element_add_pad (GST_ELEMENT_CAST (rtpsession),
- rtpsession->send_rtcp_src);
-
- return rtpsession->send_rtcp_src;
-}
-
-static void
-remove_send_rtcp_src (GstRtpSession * rtpsession)
-{
- GST_DEBUG_OBJECT (rtpsession, "removing pad");
-
- gst_pad_set_active (rtpsession->send_rtcp_src, FALSE);
-
- gst_element_remove_pad (GST_ELEMENT_CAST (rtpsession),
- rtpsession->send_rtcp_src);
- rtpsession->send_rtcp_src = NULL;
-}
-
-static GstPad *
-gst_rtp_session_request_new_pad (GstElement * element,
- GstPadTemplate * templ, const gchar * name)
-{
- GstRtpSession *rtpsession;
- GstElementClass *klass;
- GstPad *result;
-
- g_return_val_if_fail (templ != NULL, NULL);
- g_return_val_if_fail (GST_IS_RTP_SESSION (element), NULL);
-
- rtpsession = GST_RTP_SESSION (element);
- klass = GST_ELEMENT_GET_CLASS (element);
-
- GST_DEBUG_OBJECT (element, "requesting pad %s", GST_STR_NULL (name));
-
- GST_RTP_SESSION_LOCK (rtpsession);
-
- /* figure out the template */
- if (templ == gst_element_class_get_pad_template (klass, "recv_rtp_sink")) {
- if (rtpsession->recv_rtp_sink != NULL)
- goto exists;
-
- result = create_recv_rtp_sink (rtpsession);
- } else if (templ == gst_element_class_get_pad_template (klass,
- "recv_rtcp_sink")) {
- if (rtpsession->recv_rtcp_sink != NULL)
- goto exists;
-
- result = create_recv_rtcp_sink (rtpsession);
- } else if (templ == gst_element_class_get_pad_template (klass,
- "send_rtp_sink")) {
- if (rtpsession->send_rtp_sink != NULL)
- goto exists;
-
- result = create_send_rtp_sink (rtpsession);
- } else if (templ == gst_element_class_get_pad_template (klass,
- "send_rtcp_src")) {
- if (rtpsession->send_rtcp_src != NULL)
- goto exists;
-
- result = create_send_rtcp_src (rtpsession);
- } else
- goto wrong_template;
-
- GST_RTP_SESSION_UNLOCK (rtpsession);
-
- return result;
-
- /* ERRORS */
-wrong_template:
- {
- GST_RTP_SESSION_UNLOCK (rtpsession);
- g_warning ("gstrtpsession: this is not our template");
- return NULL;
- }
-exists:
- {
- GST_RTP_SESSION_UNLOCK (rtpsession);
- g_warning ("gstrtpsession: pad already requested");
- return NULL;
- }
-}
-
-static void
-gst_rtp_session_release_pad (GstElement * element, GstPad * pad)
-{
- GstRtpSession *rtpsession;
-
- g_return_if_fail (GST_IS_RTP_SESSION (element));
- g_return_if_fail (GST_IS_PAD (pad));
-
- rtpsession = GST_RTP_SESSION (element);
-
- GST_DEBUG_OBJECT (element, "releasing pad %s:%s", GST_DEBUG_PAD_NAME (pad));
-
- GST_RTP_SESSION_LOCK (rtpsession);
-
- if (rtpsession->recv_rtp_sink == pad) {
- remove_recv_rtp_sink (rtpsession);
- } else if (rtpsession->recv_rtcp_sink == pad) {
- remove_recv_rtcp_sink (rtpsession);
- } else if (rtpsession->send_rtp_sink == pad) {
- remove_send_rtp_sink (rtpsession);
- } else if (rtpsession->send_rtcp_src == pad) {
- remove_send_rtcp_src (rtpsession);
- } else
- goto wrong_pad;
-
- GST_RTP_SESSION_UNLOCK (rtpsession);
-
- return;
-
- /* ERRORS */
-wrong_pad:
- {
- GST_RTP_SESSION_UNLOCK (rtpsession);
- g_warning ("gstrtpsession: asked to release an unknown pad");
- return;
- }
-}
+++ /dev/null
-/* GStreamer
- * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#ifndef __GST_RTP_SESSION_H__
-#define __GST_RTP_SESSION_H__
-
-#include <gst/gst.h>
-
-#define GST_TYPE_RTP_SESSION \
- (gst_rtp_session_get_type())
-#define GST_RTP_SESSION(obj) \
- (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_SESSION,GstRtpSession))
-#define GST_RTP_SESSION_CLASS(klass) \
- (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_SESSION,GstRtpSessionClass))
-#define GST_IS_RTP_SESSION(obj) \
- (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_SESSION))
-#define GST_IS_RTP_SESSION_CLASS(klass) \
- (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_SESSION))
-#define GST_RTP_SESSION_CAST(obj) ((GstRtpSession *)(obj))
-
-typedef struct _GstRtpSession GstRtpSession;
-typedef struct _GstRtpSessionClass GstRtpSessionClass;
-typedef struct _GstRtpSessionPrivate GstRtpSessionPrivate;
-
-struct _GstRtpSession {
- GstElement element;
-
- /*< private >*/
- GstPad *recv_rtp_sink;
- GstSegment recv_rtp_seg;
- GstPad *recv_rtcp_sink;
- GstPad *send_rtp_sink;
- GstSegment send_rtp_seg;
-
- GstPad *recv_rtp_src;
- GstPad *sync_src;
- GstPad *send_rtp_src;
- GstPad *send_rtcp_src;
-
- GstRtpSessionPrivate *priv;
-};
-
-struct _GstRtpSessionClass {
- GstElementClass parent_class;
-
- /* signals */
- GstCaps* (*request_pt_map) (GstRtpSession *sess, guint pt);
- void (*clear_pt_map) (GstRtpSession *sess);
-
- void (*on_new_ssrc) (GstRtpSession *sess, guint32 ssrc);
- void (*on_ssrc_collision) (GstRtpSession *sess, guint32 ssrc);
- void (*on_ssrc_validated) (GstRtpSession *sess, guint32 ssrc);
- void (*on_ssrc_active) (GstRtpSession *sess, guint32 ssrc);
- void (*on_ssrc_sdes) (GstRtpSession *sess, guint32 ssrc);
- void (*on_bye_ssrc) (GstRtpSession *sess, guint32 ssrc);
- void (*on_bye_timeout) (GstRtpSession *sess, guint32 ssrc);
- void (*on_timeout) (GstRtpSession *sess, guint32 ssrc);
- void (*on_sender_timeout) (GstRtpSession *sess, guint32 ssrc);
-};
-
-GType gst_rtp_session_get_type (void);
-
-void gst_rtp_session_set_ssrc (GstRtpSession *sess, guint32 ssrc);
-
-#endif /* __GST_RTP_SESSION_H__ */
+++ /dev/null
-/* GStreamer
- * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
- *
- * RTP SSRC demuxer
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-/**
- * SECTION:element-gstrtpssrcdemux
- *
- * gstrtpssrcdemux acts as a demuxer for RTP packets based on the SSRC of the
- * packets. Its main purpose is to allow an application to easily receive and
- * decode an RTP stream with multiple SSRCs.
- *
- * For each SSRC that is detected, a new pad will be created and the
- * #GstRtpSsrcDemux::new-ssrc-pad signal will be emitted.
- *
- * <refsect2>
- * <title>Example pipelines</title>
- * |[
- * gst-launch udpsrc caps="application/x-rtp" ! gstrtpssrcdemux ! fakesink
- * ]| Takes an RTP stream and send the RTP packets with the first detected SSRC
- * to fakesink, discarding the other SSRCs.
- * </refsect2>
- *
- * Last reviewed on 2007-05-28 (0.10.5)
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <string.h>
-#include <gst/rtp/gstrtpbuffer.h>
-#include <gst/rtp/gstrtcpbuffer.h>
-
-#include "gstrtpbin-marshal.h"
-#include "gstrtpssrcdemux.h"
-
-GST_DEBUG_CATEGORY_STATIC (gst_rtp_ssrc_demux_debug);
-#define GST_CAT_DEFAULT gst_rtp_ssrc_demux_debug
-
-/* generic templates */
-static GstStaticPadTemplate rtp_ssrc_demux_sink_template =
-GST_STATIC_PAD_TEMPLATE ("sink",
- GST_PAD_SINK,
- GST_PAD_ALWAYS,
- GST_STATIC_CAPS ("application/x-rtp")
- );
-
-static GstStaticPadTemplate rtp_ssrc_demux_rtcp_sink_template =
-GST_STATIC_PAD_TEMPLATE ("rtcp_sink",
- GST_PAD_SINK,
- GST_PAD_ALWAYS,
- GST_STATIC_CAPS ("application/x-rtcp")
- );
-
-static GstStaticPadTemplate rtp_ssrc_demux_src_template =
-GST_STATIC_PAD_TEMPLATE ("src_%d",
- GST_PAD_SRC,
- GST_PAD_SOMETIMES,
- GST_STATIC_CAPS ("application/x-rtp")
- );
-
-static GstStaticPadTemplate rtp_ssrc_demux_rtcp_src_template =
-GST_STATIC_PAD_TEMPLATE ("rtcp_src_%d",
- GST_PAD_SRC,
- GST_PAD_SOMETIMES,
- GST_STATIC_CAPS ("application/x-rtcp")
- );
-
-static GstElementDetails gst_rtp_ssrc_demux_details = {
- "RTP SSRC Demux",
- "Demux/Network/RTP",
- "Splits RTP streams based on the SSRC",
- "Wim Taymans <wim.taymans@gmail.com>"
-};
-
-#define GST_PAD_LOCK(obj) (g_mutex_lock ((obj)->padlock))
-#define GST_PAD_UNLOCK(obj) (g_mutex_unlock ((obj)->padlock))
-
-/* signals */
-enum
-{
- SIGNAL_NEW_SSRC_PAD,
- SIGNAL_REMOVED_SSRC_PAD,
- SIGNAL_CLEAR_SSRC,
- LAST_SIGNAL
-};
-
-GST_BOILERPLATE (GstRtpSsrcDemux, gst_rtp_ssrc_demux, GstElement,
- GST_TYPE_ELEMENT);
-
-
-/* GObject vmethods */
-static void gst_rtp_ssrc_demux_dispose (GObject * object);
-static void gst_rtp_ssrc_demux_finalize (GObject * object);
-
-/* GstElement vmethods */
-static GstStateChangeReturn gst_rtp_ssrc_demux_change_state (GstElement *
- element, GstStateChange transition);
-
-static void gst_rtp_ssrc_demux_clear_ssrc (GstRtpSsrcDemux * demux,
- guint32 ssrc);
-
-/* sinkpad stuff */
-static GstFlowReturn gst_rtp_ssrc_demux_chain (GstPad * pad, GstBuffer * buf);
-static gboolean gst_rtp_ssrc_demux_sink_event (GstPad * pad, GstEvent * event);
-
-static GstFlowReturn gst_rtp_ssrc_demux_rtcp_chain (GstPad * pad,
- GstBuffer * buf);
-static gboolean gst_rtp_ssrc_demux_rtcp_sink_event (GstPad * pad,
- GstEvent * event);
-
-/* srcpad stuff */
-static gboolean gst_rtp_ssrc_demux_src_event (GstPad * pad, GstEvent * event);
-static GList *gst_rtp_ssrc_demux_internal_links (GstPad * pad);
-static gboolean gst_rtp_ssrc_demux_src_query (GstPad * pad, GstQuery * query);
-
-static guint gst_rtp_ssrc_demux_signals[LAST_SIGNAL] = { 0 };
-
-/*
- * Item for storing GstPad <-> SSRC pairs.
- */
-struct _GstRtpSsrcDemuxPad
-{
- guint32 ssrc;
- GstPad *rtp_pad;
- GstCaps *caps;
- GstPad *rtcp_pad;
-};
-
-/* find a src pad for a given SSRC, returns NULL if the SSRC was not found
- */
-static GstRtpSsrcDemuxPad *
-find_demux_pad_for_ssrc (GstRtpSsrcDemux * demux, guint32 ssrc)
-{
- GSList *walk;
-
- for (walk = demux->srcpads; walk; walk = g_slist_next (walk)) {
- GstRtpSsrcDemuxPad *pad = (GstRtpSsrcDemuxPad *) walk->data;
-
- if (pad->ssrc == ssrc)
- return pad;
- }
- return NULL;
-}
-
-/* with PAD_LOCK */
-static GstRtpSsrcDemuxPad *
-create_demux_pad_for_ssrc (GstRtpSsrcDemux * demux, guint32 ssrc,
- GstClockTime timestamp)
-{
- GstPad *rtp_pad, *rtcp_pad;
- GstElementClass *klass;
- GstPadTemplate *templ;
- gchar *padname;
- GstRtpSsrcDemuxPad *demuxpad;
-
- GST_DEBUG_OBJECT (demux, "creating pad for SSRC %08x", ssrc);
-
- klass = GST_ELEMENT_GET_CLASS (demux);
- templ = gst_element_class_get_pad_template (klass, "src_%d");
- padname = g_strdup_printf ("src_%d", ssrc);
- rtp_pad = gst_pad_new_from_template (templ, padname);
- g_free (padname);
-
- templ = gst_element_class_get_pad_template (klass, "rtcp_src_%d");
- padname = g_strdup_printf ("rtcp_src_%d", ssrc);
- rtcp_pad = gst_pad_new_from_template (templ, padname);
- g_free (padname);
-
- /* we use the first timestamp received to calculate the difference between
- * timestamps on all streams */
- GST_DEBUG_OBJECT (demux, "SSRC %08x, first timestamp %" GST_TIME_FORMAT,
- ssrc, GST_TIME_ARGS (timestamp));
-
- /* wrap in structure and add to list */
- demuxpad = g_new0 (GstRtpSsrcDemuxPad, 1);
- demuxpad->ssrc = ssrc;
- demuxpad->rtp_pad = rtp_pad;
- demuxpad->rtcp_pad = rtcp_pad;
-
- GST_DEBUG_OBJECT (demux, "first timestamp %" GST_TIME_FORMAT,
- GST_TIME_ARGS (timestamp));
-
- gst_pad_set_element_private (rtp_pad, demuxpad);
- gst_pad_set_element_private (rtcp_pad, demuxpad);
-
- demux->srcpads = g_slist_prepend (demux->srcpads, demuxpad);
-
- /* copy caps from input */
- gst_pad_set_caps (rtp_pad, GST_PAD_CAPS (demux->rtp_sink));
- gst_pad_use_fixed_caps (rtp_pad);
- gst_pad_set_caps (rtcp_pad, GST_PAD_CAPS (demux->rtcp_sink));
- gst_pad_use_fixed_caps (rtcp_pad);
-
- gst_pad_set_event_function (rtp_pad, gst_rtp_ssrc_demux_src_event);
- gst_pad_set_query_function (rtp_pad, gst_rtp_ssrc_demux_src_query);
- gst_pad_set_internal_link_function (rtp_pad,
- gst_rtp_ssrc_demux_internal_links);
- gst_pad_set_active (rtp_pad, TRUE);
-
- gst_pad_set_internal_link_function (rtcp_pad,
- gst_rtp_ssrc_demux_internal_links);
- gst_pad_set_active (rtcp_pad, TRUE);
-
- gst_element_add_pad (GST_ELEMENT_CAST (demux), rtp_pad);
- gst_element_add_pad (GST_ELEMENT_CAST (demux), rtcp_pad);
-
- g_signal_emit (G_OBJECT (demux),
- gst_rtp_ssrc_demux_signals[SIGNAL_NEW_SSRC_PAD], 0, ssrc, rtp_pad);
-
- return demuxpad;
-}
-
-static void
-gst_rtp_ssrc_demux_base_init (gpointer g_class)
-{
- GstElementClass *gstelement_klass = GST_ELEMENT_CLASS (g_class);
-
- gst_element_class_add_pad_template (gstelement_klass,
- gst_static_pad_template_get (&rtp_ssrc_demux_sink_template));
- gst_element_class_add_pad_template (gstelement_klass,
- gst_static_pad_template_get (&rtp_ssrc_demux_rtcp_sink_template));
- gst_element_class_add_pad_template (gstelement_klass,
- gst_static_pad_template_get (&rtp_ssrc_demux_src_template));
- gst_element_class_add_pad_template (gstelement_klass,
- gst_static_pad_template_get (&rtp_ssrc_demux_rtcp_src_template));
-
- gst_element_class_set_details (gstelement_klass, &gst_rtp_ssrc_demux_details);
-}
-
-static void
-gst_rtp_ssrc_demux_class_init (GstRtpSsrcDemuxClass * klass)
-{
- GObjectClass *gobject_klass;
- GstElementClass *gstelement_klass;
- GstRtpSsrcDemuxClass *gstrtpssrcdemux_klass;
-
- gobject_klass = (GObjectClass *) klass;
- gstelement_klass = (GstElementClass *) klass;
- gstrtpssrcdemux_klass = (GstRtpSsrcDemuxClass *) klass;
-
- gobject_klass->dispose = GST_DEBUG_FUNCPTR (gst_rtp_ssrc_demux_dispose);
- gobject_klass->finalize = GST_DEBUG_FUNCPTR (gst_rtp_ssrc_demux_finalize);
-
- /**
- * GstRtpSsrcDemux::new-ssrc-pad:
- * @demux: the object which received the signal
- * @ssrc: the SSRC of the pad
- * @pad: the new pad.
- *
- * Emited when a new SSRC pad has been created.
- */
- gst_rtp_ssrc_demux_signals[SIGNAL_NEW_SSRC_PAD] =
- g_signal_new ("new-ssrc-pad",
- G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (GstRtpSsrcDemuxClass, new_ssrc_pad),
- NULL, NULL, gst_rtp_bin_marshal_VOID__UINT_OBJECT,
- G_TYPE_NONE, 2, G_TYPE_UINT, GST_TYPE_PAD);
-
- /**
- * GstRtpSsrcDemux::removed-ssrc-pad:
- * @demux: the object which received the signal
- * @ssrc: the SSRC of the pad
- * @pad: the removed pad.
- *
- * Emited when a SSRC pad has been removed.
- */
- gst_rtp_ssrc_demux_signals[SIGNAL_REMOVED_SSRC_PAD] =
- g_signal_new ("removed-ssrc-pad",
- G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (GstRtpSsrcDemuxClass, removed_ssrc_pad),
- NULL, NULL, gst_rtp_bin_marshal_VOID__UINT_OBJECT,
- G_TYPE_NONE, 2, G_TYPE_UINT, GST_TYPE_PAD);
-
- /**
- * GstRtpSsrcDemux::clear-ssrc:
- * @demux: the object which received the signal
- * @ssrc: the SSRC of the pad
- *
- * Action signal to remove the pad for SSRC.
- */
- gst_rtp_ssrc_demux_signals[SIGNAL_CLEAR_SSRC] =
- g_signal_new ("clear-ssrc",
- G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
- G_STRUCT_OFFSET (GstRtpSsrcDemuxClass, clear_ssrc),
- NULL, NULL, gst_rtp_bin_marshal_VOID__UINT, G_TYPE_NONE, 1, G_TYPE_UINT);
-
- gstelement_klass->change_state =
- GST_DEBUG_FUNCPTR (gst_rtp_ssrc_demux_change_state);
- gstrtpssrcdemux_klass->clear_ssrc =
- GST_DEBUG_FUNCPTR (gst_rtp_ssrc_demux_clear_ssrc);
-
- GST_DEBUG_CATEGORY_INIT (gst_rtp_ssrc_demux_debug,
- "rtpssrcdemux", 0, "RTP SSRC demuxer");
-}
-
-static void
-gst_rtp_ssrc_demux_init (GstRtpSsrcDemux * demux,
- GstRtpSsrcDemuxClass * g_class)
-{
- GstElementClass *klass = GST_ELEMENT_GET_CLASS (demux);
-
- demux->rtp_sink =
- gst_pad_new_from_template (gst_element_class_get_pad_template (klass,
- "sink"), "sink");
- gst_pad_set_chain_function (demux->rtp_sink, gst_rtp_ssrc_demux_chain);
- gst_pad_set_event_function (demux->rtp_sink, gst_rtp_ssrc_demux_sink_event);
- gst_element_add_pad (GST_ELEMENT_CAST (demux), demux->rtp_sink);
-
- demux->rtcp_sink =
- gst_pad_new_from_template (gst_element_class_get_pad_template (klass,
- "rtcp_sink"), "rtcp_sink");
- gst_pad_set_chain_function (demux->rtcp_sink, gst_rtp_ssrc_demux_rtcp_chain);
- gst_pad_set_event_function (demux->rtcp_sink,
- gst_rtp_ssrc_demux_rtcp_sink_event);
- gst_element_add_pad (GST_ELEMENT_CAST (demux), demux->rtcp_sink);
-
- demux->padlock = g_mutex_new ();
-
- gst_segment_init (&demux->segment, GST_FORMAT_UNDEFINED);
-}
-
-static void
-gst_rtp_ssrc_demux_reset (GstRtpSsrcDemux * demux)
-{
- GSList *walk;
-
- for (walk = demux->srcpads; walk; walk = g_slist_next (walk)) {
- GstRtpSsrcDemuxPad *dpad = (GstRtpSsrcDemuxPad *) walk->data;
-
- gst_pad_set_active (dpad->rtp_pad, FALSE);
- gst_pad_set_active (dpad->rtcp_pad, FALSE);
-
- gst_element_remove_pad (GST_ELEMENT_CAST (demux), dpad->rtp_pad);
- gst_element_remove_pad (GST_ELEMENT_CAST (demux), dpad->rtcp_pad);
- g_free (dpad);
- }
- g_slist_free (demux->srcpads);
- demux->srcpads = NULL;
-}
-
-static void
-gst_rtp_ssrc_demux_dispose (GObject * object)
-{
- GstRtpSsrcDemux *demux;
-
- demux = GST_RTP_SSRC_DEMUX (object);
-
- gst_rtp_ssrc_demux_reset (demux);
-
- G_OBJECT_CLASS (parent_class)->dispose (object);
-}
-
-static void
-gst_rtp_ssrc_demux_finalize (GObject * object)
-{
- GstRtpSsrcDemux *demux;
-
- demux = GST_RTP_SSRC_DEMUX (object);
- g_mutex_free (demux->padlock);
-
- G_OBJECT_CLASS (parent_class)->finalize (object);
-}
-
-static void
-gst_rtp_ssrc_demux_clear_ssrc (GstRtpSsrcDemux * demux, guint32 ssrc)
-{
- GstRtpSsrcDemuxPad *dpad;
-
- GST_PAD_LOCK (demux);
- dpad = find_demux_pad_for_ssrc (demux, ssrc);
- if (dpad != NULL)
- goto unknown_pad;
-
- GST_DEBUG_OBJECT (demux, "clearing pad for SSRC %08x", ssrc);
-
- demux->srcpads = g_slist_remove (demux->srcpads, dpad);
- GST_PAD_UNLOCK (demux);
-
- gst_pad_set_active (dpad->rtp_pad, FALSE);
- gst_pad_set_active (dpad->rtcp_pad, FALSE);
-
- g_signal_emit (G_OBJECT (demux),
- gst_rtp_ssrc_demux_signals[SIGNAL_REMOVED_SSRC_PAD], 0, ssrc,
- dpad->rtp_pad);
-
- gst_element_remove_pad (GST_ELEMENT_CAST (demux), dpad->rtp_pad);
- gst_element_remove_pad (GST_ELEMENT_CAST (demux), dpad->rtcp_pad);
-
- g_free (dpad);
-
- return;
-
- /* ERRORS */
-unknown_pad:
- {
- g_warning ("unknown SSRC %08x", ssrc);
- return;
- }
-}
-
-static gboolean
-gst_rtp_ssrc_demux_sink_event (GstPad * pad, GstEvent * event)
-{
- GstRtpSsrcDemux *demux;
- gboolean res = FALSE;
-
- demux = GST_RTP_SSRC_DEMUX (gst_pad_get_parent (pad));
-
- switch (GST_EVENT_TYPE (event)) {
- case GST_EVENT_FLUSH_STOP:
- gst_segment_init (&demux->segment, GST_FORMAT_UNDEFINED);
- case GST_EVENT_NEWSEGMENT:
- default:
- {
- GSList *walk;
-
- res = TRUE;
- GST_PAD_LOCK (demux);
- for (walk = demux->srcpads; walk; walk = g_slist_next (walk)) {
- GstRtpSsrcDemuxPad *pad = (GstRtpSsrcDemuxPad *) walk->data;
-
- gst_event_ref (event);
- res &= gst_pad_push_event (pad->rtp_pad, event);
- }
- GST_PAD_UNLOCK (demux);
- gst_event_unref (event);
- break;
- }
- }
-
- gst_object_unref (demux);
- return res;
-}
-
-static gboolean
-gst_rtp_ssrc_demux_rtcp_sink_event (GstPad * pad, GstEvent * event)
-{
- GstRtpSsrcDemux *demux;
- gboolean res = FALSE;
-
- demux = GST_RTP_SSRC_DEMUX (gst_pad_get_parent (pad));
-
- switch (GST_EVENT_TYPE (event)) {
- case GST_EVENT_NEWSEGMENT:
- default:
- {
- GSList *walk;
-
- res = TRUE;
- GST_PAD_LOCK (demux);
- for (walk = demux->srcpads; walk; walk = g_slist_next (walk)) {
- GstRtpSsrcDemuxPad *pad = (GstRtpSsrcDemuxPad *) walk->data;
-
- gst_event_ref (event);
- res &= gst_pad_push_event (pad->rtcp_pad, event);
- }
- GST_PAD_UNLOCK (demux);
- gst_event_unref (event);
- break;
- }
- }
- gst_object_unref (demux);
- return res;
-}
-
-static GstFlowReturn
-gst_rtp_ssrc_demux_chain (GstPad * pad, GstBuffer * buf)
-{
- GstFlowReturn ret;
- GstRtpSsrcDemux *demux;
- guint32 ssrc;
- GstRtpSsrcDemuxPad *dpad;
-
- demux = GST_RTP_SSRC_DEMUX (GST_OBJECT_PARENT (pad));
-
- if (!gst_rtp_buffer_validate (buf))
- goto invalid_payload;
-
- ssrc = gst_rtp_buffer_get_ssrc (buf);
-
- GST_DEBUG_OBJECT (demux, "received buffer of SSRC %08x", ssrc);
-
- GST_PAD_LOCK (demux);
- dpad = find_demux_pad_for_ssrc (demux, ssrc);
- if (dpad == NULL) {
- if (!(dpad =
- create_demux_pad_for_ssrc (demux, ssrc,
- GST_BUFFER_TIMESTAMP (buf))))
- goto create_failed;
- }
- GST_PAD_UNLOCK (demux);
-
- /* push to srcpad */
- ret = gst_pad_push (dpad->rtp_pad, buf);
-
- return ret;
-
- /* ERRORS */
-invalid_payload:
- {
- /* this is fatal and should be filtered earlier */
- GST_ELEMENT_ERROR (demux, STREAM, DECODE, (NULL),
- ("Dropping invalid RTP payload"));
- gst_buffer_unref (buf);
- return GST_FLOW_ERROR;
- }
-create_failed:
- {
- GST_ELEMENT_ERROR (demux, STREAM, DECODE, (NULL),
- ("Could not create new pad"));
- GST_PAD_UNLOCK (demux);
- gst_buffer_unref (buf);
- return GST_FLOW_ERROR;
- }
-}
-
-static GstFlowReturn
-gst_rtp_ssrc_demux_rtcp_chain (GstPad * pad, GstBuffer * buf)
-{
- GstFlowReturn ret;
- GstRtpSsrcDemux *demux;
- guint32 ssrc;
- GstRtpSsrcDemuxPad *dpad;
- GstRTCPPacket packet;
-
- demux = GST_RTP_SSRC_DEMUX (GST_OBJECT_PARENT (pad));
-
- if (!gst_rtcp_buffer_validate (buf))
- goto invalid_rtcp;
-
- if (!gst_rtcp_buffer_get_first_packet (buf, &packet))
- goto invalid_rtcp;
-
- /* first packet must be SR or RR or else the validate would have failed */
- switch (gst_rtcp_packet_get_type (&packet)) {
- case GST_RTCP_TYPE_SR:
- /* get the ssrc so that we can route it to the right source pad */
- gst_rtcp_packet_sr_get_sender_info (&packet, &ssrc, NULL, NULL, NULL,
- NULL);
- break;
- default:
- goto unexpected_rtcp;
- }
-
- GST_DEBUG_OBJECT (demux, "received RTCP of SSRC %08x", ssrc);
-
- GST_PAD_LOCK (demux);
- dpad = find_demux_pad_for_ssrc (demux, ssrc);
- if (dpad == NULL) {
- GST_DEBUG_OBJECT (demux, "creating pad for SSRC %08x", ssrc);
- if (!(dpad = create_demux_pad_for_ssrc (demux, ssrc, -1)))
- goto create_failed;
- }
- GST_PAD_UNLOCK (demux);
-
- /* push to srcpad */
- ret = gst_pad_push (dpad->rtcp_pad, buf);
-
- return ret;
-
- /* ERRORS */
-invalid_rtcp:
- {
- /* this is fatal and should be filtered earlier */
- GST_ELEMENT_ERROR (demux, STREAM, DECODE, (NULL),
- ("Dropping invalid RTCP packet"));
- gst_buffer_unref (buf);
- return GST_FLOW_ERROR;
- }
-unexpected_rtcp:
- {
- GST_DEBUG_OBJECT (demux, "dropping unexpected RTCP packet");
- gst_buffer_unref (buf);
- return GST_FLOW_OK;
- }
-create_failed:
- {
- GST_ELEMENT_ERROR (demux, STREAM, DECODE, (NULL),
- ("Could not create new pad"));
- GST_PAD_UNLOCK (demux);
- gst_buffer_unref (buf);
- return GST_FLOW_ERROR;
- }
-}
-
-static gboolean
-gst_rtp_ssrc_demux_src_event (GstPad * pad, GstEvent * event)
-{
- GstRtpSsrcDemux *demux;
- gboolean res = FALSE;
-
- demux = GST_RTP_SSRC_DEMUX (gst_pad_get_parent (pad));
-
- switch (GST_EVENT_TYPE (event)) {
- case GST_EVENT_SEEK:
- default:
- res = gst_pad_event_default (pad, event);
- break;
- }
- gst_object_unref (demux);
- return res;
-}
-
-static GList *
-gst_rtp_ssrc_demux_internal_links (GstPad * pad)
-{
- GstRtpSsrcDemux *demux;
- GList *res = NULL;
- GSList *walk;
-
- demux = GST_RTP_SSRC_DEMUX (gst_pad_get_parent (pad));
-
- GST_PAD_LOCK (demux);
- for (walk = demux->srcpads; walk; walk = g_slist_next (walk)) {
- GstRtpSsrcDemuxPad *dpad = (GstRtpSsrcDemuxPad *) walk->data;
-
- if (pad == demux->rtp_sink) {
- res = g_list_prepend (res, dpad->rtp_pad);
- } else if (pad == demux->rtcp_sink) {
- res = g_list_prepend (res, dpad->rtcp_pad);
- } else if (pad == dpad->rtp_pad) {
- res = g_list_prepend (res, demux->rtp_sink);
- break;
- } else if (pad == dpad->rtcp_pad) {
- res = g_list_prepend (res, demux->rtcp_sink);
- break;
- }
- }
- GST_PAD_UNLOCK (demux);
-
- gst_object_unref (demux);
- return res;
-}
-
-static gboolean
-gst_rtp_ssrc_demux_src_query (GstPad * pad, GstQuery * query)
-{
- GstRtpSsrcDemux *demux;
- gboolean res = FALSE;
-
- demux = GST_RTP_SSRC_DEMUX (gst_pad_get_parent (pad));
-
- switch (GST_QUERY_TYPE (query)) {
- case GST_QUERY_LATENCY:
- {
-
- if ((res = gst_pad_peer_query (demux->rtp_sink, query))) {
- gboolean live;
- GstClockTime min_latency, max_latency;
- GstRtpSsrcDemuxPad *demuxpad;
-
- demuxpad = gst_pad_get_element_private (pad);
-
- gst_query_parse_latency (query, &live, &min_latency, &max_latency);
-
- GST_DEBUG_OBJECT (demux, "peer min latency %" GST_TIME_FORMAT,
- GST_TIME_ARGS (min_latency));
-
- GST_DEBUG_OBJECT (demux, "latency for SSRC %08x", demuxpad->ssrc);
-
- gst_query_set_latency (query, live, min_latency, max_latency);
- }
- break;
- }
- default:
- res = gst_pad_query_default (pad, query);
- break;
- }
- gst_object_unref (demux);
-
- return res;
-}
-
-static GstStateChangeReturn
-gst_rtp_ssrc_demux_change_state (GstElement * element,
- GstStateChange transition)
-{
- GstStateChangeReturn ret;
- GstRtpSsrcDemux *demux;
-
- demux = GST_RTP_SSRC_DEMUX (element);
-
- switch (transition) {
- case GST_STATE_CHANGE_NULL_TO_READY:
- case GST_STATE_CHANGE_READY_TO_PAUSED:
- case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
- default:
- break;
- }
-
- ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
-
- switch (transition) {
- case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
- break;
- case GST_STATE_CHANGE_PAUSED_TO_READY:
- gst_rtp_ssrc_demux_reset (demux);
- break;
- case GST_STATE_CHANGE_READY_TO_NULL:
- default:
- break;
- }
- return ret;
-}
+++ /dev/null
-/* GStreamer
- * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#ifndef __GST_RTP_SSRC_DEMUX_H__
-#define __GST_RTP_SSRC_DEMUX_H__
-
-#include <gst/gst.h>
-
-#define GST_TYPE_RTP_SSRC_DEMUX (gst_rtp_ssrc_demux_get_type())
-#define GST_RTP_SSRC_DEMUX(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_SSRC_DEMUX,GstRtpSsrcDemux))
-#define GST_RTP_SSRC_DEMUX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_SSRC_DEMUX,GstRtpSsrcDemuxClass))
-#define GST_IS_RTP_SSRC_DEMUX(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_SSRC_DEMUX))
-#define GST_IS_RTP_SSRC_DEMUX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_SSRC_DEMUX))
-
-typedef struct _GstRtpSsrcDemux GstRtpSsrcDemux;
-typedef struct _GstRtpSsrcDemuxClass GstRtpSsrcDemuxClass;
-typedef struct _GstRtpSsrcDemuxPad GstRtpSsrcDemuxPad;
-
-struct _GstRtpSsrcDemux
-{
- GstElement parent;
-
- GstSegment segment;
-
- GstPad *rtp_sink;
- GstPad *rtcp_sink;
-
- GMutex *padlock;
- GSList *srcpads;
-};
-
-struct _GstRtpSsrcDemuxClass
-{
- GstElementClass parent_class;
-
- /* signals */
- void (*new_ssrc_pad) (GstRtpSsrcDemux *demux, guint32 ssrc, GstPad *pad);
- void (*removed_ssrc_pad) (GstRtpSsrcDemux *demux, guint32 ssrc, GstPad *pad);
-
- /* actions */
- void (*clear_ssrc) (GstRtpSsrcDemux *demux, guint32 ssrc);
-};
-
-GType gst_rtp_ssrc_demux_get_type (void);
-
-#endif /* __GST_RTP_SSRC_DEMUX_H__ */
+++ /dev/null
-/* GStreamer
- * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-#include <string.h>
-#include <stdlib.h>
-
-#include <gst/rtp/gstrtpbuffer.h>
-#include <gst/rtp/gstrtcpbuffer.h>
-
-#include "rtpjitterbuffer.h"
-
-GST_DEBUG_CATEGORY_STATIC (rtp_jitter_buffer_debug);
-#define GST_CAT_DEFAULT rtp_jitter_buffer_debug
-
-#define MAX_WINDOW RTP_JITTER_BUFFER_MAX_WINDOW
-#define MAX_TIME (2 * GST_SECOND)
-
-/* signals and args */
-enum
-{
- LAST_SIGNAL
-};
-
-enum
-{
- PROP_0
-};
-
-/* GObject vmethods */
-static void rtp_jitter_buffer_finalize (GObject * object);
-
-/* static guint rtp_jitter_buffer_signals[LAST_SIGNAL] = { 0 }; */
-
-G_DEFINE_TYPE (RTPJitterBuffer, rtp_jitter_buffer, G_TYPE_OBJECT);
-
-static void
-rtp_jitter_buffer_class_init (RTPJitterBufferClass * klass)
-{
- GObjectClass *gobject_class;
-
- gobject_class = (GObjectClass *) klass;
-
- gobject_class->finalize = rtp_jitter_buffer_finalize;
-
- GST_DEBUG_CATEGORY_INIT (rtp_jitter_buffer_debug, "rtpjitterbuffer", 0,
- "RTP Jitter Buffer");
-}
-
-static void
-rtp_jitter_buffer_init (RTPJitterBuffer * jbuf)
-{
- jbuf->packets = g_queue_new ();
-
- rtp_jitter_buffer_reset_skew (jbuf);
-}
-
-static void
-rtp_jitter_buffer_finalize (GObject * object)
-{
- RTPJitterBuffer *jbuf;
-
- jbuf = RTP_JITTER_BUFFER_CAST (object);
-
- rtp_jitter_buffer_flush (jbuf);
- g_queue_free (jbuf->packets);
-
- G_OBJECT_CLASS (rtp_jitter_buffer_parent_class)->finalize (object);
-}
-
-/**
- * rtp_jitter_buffer_new:
- *
- * Create an #RTPJitterBuffer.
- *
- * Returns: a new #RTPJitterBuffer. Use g_object_unref() after usage.
- */
-RTPJitterBuffer *
-rtp_jitter_buffer_new (void)
-{
- RTPJitterBuffer *jbuf;
-
- jbuf = g_object_new (RTP_TYPE_JITTER_BUFFER, NULL);
-
- return jbuf;
-}
-
-void
-rtp_jitter_buffer_reset_skew (RTPJitterBuffer * jbuf)
-{
- jbuf->base_time = -1;
- jbuf->base_rtptime = -1;
- jbuf->base_extrtp = -1;
- jbuf->clock_rate = -1;
- jbuf->ext_rtptime = -1;
- jbuf->last_rtptime = -1;
- jbuf->window_pos = 0;
- jbuf->window_filling = TRUE;
- jbuf->window_min = 0;
- jbuf->skew = 0;
- jbuf->prev_send_diff = -1;
- jbuf->prev_out_time = -1;
- GST_DEBUG ("reset skew correction");
-}
-
-/* For the clock skew we use a windowed low point averaging algorithm as can be
- * found in http://www.grame.fr/pub/TR-050601.pdf. The idea is that the jitter is
- * composed of:
- *
- * J = N + n
- *
- * N : a constant network delay.
- * n : random added noise. The noise is concentrated around 0
- *
- * In the receiver we can track the elapsed time at the sender with:
- *
- * send_diff(i) = (Tsi - Ts0);
- *
- * Tsi : The time at the sender at packet i
- * Ts0 : The time at the sender at the first packet
- *
- * This is the difference between the RTP timestamp in the first received packet
- * and the current packet.
- *
- * At the receiver we have to deal with the jitter introduced by the network.
- *
- * recv_diff(i) = (Tri - Tr0)
- *
- * Tri : The time at the receiver at packet i
- * Tr0 : The time at the receiver at the first packet
- *
- * Both of these values contain a jitter Ji, a jitter for packet i, so we can
- * write:
- *
- * recv_diff(i) = (Cri + D + ni) - (Cr0 + D + n0))
- *
- * Cri : The time of the clock at the receiver for packet i
- * D + ni : The jitter when receiving packet i
- *
- * We see that the network delay is irrelevant here as we can elliminate D:
- *
- * recv_diff(i) = (Cri + ni) - (Cr0 + n0))
- *
- * The drift is now expressed as:
- *
- * Drift(i) = recv_diff(i) - send_diff(i);
- *
- * We now keep the W latest values of Drift and find the minimum (this is the
- * one with the lowest network jitter and thus the one which is least affected
- * by it). We average this lowest value to smooth out the resulting network skew.
- *
- * Both the window and the weighting used for averaging influence the accuracy
- * of the drift estimation. Finding the correct parameters turns out to be a
- * compromise between accuracy and inertia.
- *
- * We use a 2 second window or up to 512 data points, which is statistically big
- * enough to catch spikes (FIXME, detect spikes).
- * We also use a rather large weighting factor (125) to smoothly adapt. During
- * startup, when filling the window, we use a parabolic weighting factor, the
- * more the window is filled, the faster we move to the detected possible skew.
- *
- * Returns: @time adjusted with the clock skew.
- */
-static GstClockTime
-calculate_skew (RTPJitterBuffer * jbuf, guint32 rtptime, GstClockTime time,
- guint32 clock_rate)
-{
- guint64 ext_rtptime;
- guint64 send_diff, recv_diff;
- gint64 delta;
- gint64 old;
- gint pos, i;
- GstClockTime gstrtptime, out_time;
-
- ext_rtptime = gst_rtp_buffer_ext_timestamp (&jbuf->ext_rtptime, rtptime);
-
- gstrtptime = gst_util_uint64_scale_int (ext_rtptime, GST_SECOND, clock_rate);
-
- /* keep track of the last extended rtptime */
- jbuf->last_rtptime = ext_rtptime;
-
- if (jbuf->clock_rate != clock_rate) {
- GST_WARNING ("Clock rate changed from %" G_GUINT32_FORMAT " to %"
- G_GUINT32_FORMAT, jbuf->clock_rate, clock_rate);
- jbuf->base_time = -1;
- jbuf->base_rtptime = -1;
- jbuf->clock_rate = clock_rate;
- jbuf->prev_out_time = -1;
- jbuf->prev_send_diff = -1;
- }
-
- /* first time, lock on to time and gstrtptime */
- if (G_UNLIKELY (jbuf->base_time == -1)) {
- jbuf->base_time = time;
- jbuf->prev_out_time = -1;
- GST_DEBUG ("Taking new base time %" GST_TIME_FORMAT, GST_TIME_ARGS (time));
- }
- if (G_UNLIKELY (jbuf->base_rtptime == -1)) {
- jbuf->base_rtptime = gstrtptime;
- jbuf->base_extrtp = ext_rtptime;
- jbuf->prev_send_diff = -1;
- GST_DEBUG ("Taking new base rtptime %" GST_TIME_FORMAT,
- GST_TIME_ARGS (gstrtptime));
- }
-
- if (G_LIKELY (gstrtptime >= jbuf->base_rtptime))
- send_diff = gstrtptime - jbuf->base_rtptime;
- else {
- /* elapsed time at sender, timestamps can go backwards and thus be smaller
- * than our base time, take a new base time in that case. */
- GST_WARNING ("backward timestamps at server, taking new base time");
- jbuf->base_time = time;
- jbuf->base_rtptime = gstrtptime;
- jbuf->base_extrtp = ext_rtptime;
- jbuf->prev_out_time = -1;
- jbuf->prev_send_diff = -1;
- send_diff = 0;
- }
-
- GST_DEBUG ("extrtp %" G_GUINT64_FORMAT ", gstrtp %" GST_TIME_FORMAT ", base %"
- GST_TIME_FORMAT ", send_diff %" GST_TIME_FORMAT, ext_rtptime,
- GST_TIME_ARGS (gstrtptime), GST_TIME_ARGS (jbuf->base_rtptime),
- GST_TIME_ARGS (send_diff));
-
- /* we don't have an arrival timestamp so we can't do skew detection. we
- * should still apply a timestamp based on RTP timestamp and base_time */
- if (time == -1 || jbuf->base_time == -1)
- goto no_skew;
-
- /* elapsed time at receiver, includes the jitter */
- recv_diff = time - jbuf->base_time;
-
- GST_DEBUG ("time %" GST_TIME_FORMAT ", base %" GST_TIME_FORMAT ", recv_diff %"
- GST_TIME_FORMAT, GST_TIME_ARGS (time), GST_TIME_ARGS (jbuf->base_time),
- GST_TIME_ARGS (recv_diff));
-
- /* measure the diff */
- delta = ((gint64) recv_diff) - ((gint64) send_diff);
-
- /* if the difference between the sender timeline and the receiver timeline
- * changed too quickly we have to resync because the server likely restarted
- * its timestamps. */
- if (ABS (delta - jbuf->skew) > GST_SECOND) {
- GST_WARNING ("delta %" GST_TIME_FORMAT " too big, reset skew",
- GST_TIME_ARGS (delta - jbuf->skew));
- jbuf->base_time = time;
- jbuf->base_rtptime = gstrtptime;
- jbuf->base_extrtp = ext_rtptime;
- jbuf->prev_out_time = -1;
- jbuf->prev_send_diff = -1;
- send_diff = 0;
- delta = 0;
- }
-
- pos = jbuf->window_pos;
-
- if (G_UNLIKELY (jbuf->window_filling)) {
- /* we are filling the window */
- GST_DEBUG ("filling %d, delta %" G_GINT64_FORMAT, pos, delta);
- jbuf->window[pos++] = delta;
- /* calc the min delta we observed */
- if (G_UNLIKELY (pos == 1 || delta < jbuf->window_min))
- jbuf->window_min = delta;
-
- if (G_UNLIKELY (send_diff >= MAX_TIME || pos >= MAX_WINDOW)) {
- jbuf->window_size = pos;
-
- /* window filled */
- GST_DEBUG ("min %" G_GINT64_FORMAT, jbuf->window_min);
-
- /* the skew is now the min */
- jbuf->skew = jbuf->window_min;
- jbuf->window_filling = FALSE;
- } else {
- gint perc_time, perc_window, perc;
-
- /* figure out how much we filled the window, this depends on the amount of
- * time we have or the max number of points we keep. */
- perc_time = send_diff * 100 / MAX_TIME;
- perc_window = pos * 100 / MAX_WINDOW;
- perc = MAX (perc_time, perc_window);
-
- /* make a parabolic function, the closer we get to the MAX, the more value
- * we give to the scaling factor of the new value */
- perc = perc * perc;
-
- /* quickly go to the min value when we are filling up, slowly when we are
- * just starting because we're not sure it's a good value yet. */
- jbuf->skew =
- (perc * jbuf->window_min + ((10000 - perc) * jbuf->skew)) / 10000;
- jbuf->window_size = pos + 1;
- }
- } else {
- /* pick old value and store new value. We keep the previous value in order
- * to quickly check if the min of the window changed */
- old = jbuf->window[pos];
- jbuf->window[pos++] = delta;
-
- if (G_UNLIKELY (delta <= jbuf->window_min)) {
- /* if the new value we inserted is smaller or equal to the current min,
- * it becomes the new min */
- jbuf->window_min = delta;
- } else if (G_UNLIKELY (old == jbuf->window_min)) {
- gint64 min = G_MAXINT64;
-
- /* if we removed the old min, we have to find a new min */
- for (i = 0; i < jbuf->window_size; i++) {
- /* we found another value equal to the old min, we can stop searching now */
- if (jbuf->window[i] == old) {
- min = old;
- break;
- }
- if (jbuf->window[i] < min)
- min = jbuf->window[i];
- }
- jbuf->window_min = min;
- }
- /* average the min values */
- jbuf->skew = (jbuf->window_min + (124 * jbuf->skew)) / 125;
- GST_DEBUG ("delta %" G_GINT64_FORMAT ", new min: %" G_GINT64_FORMAT,
- delta, jbuf->window_min);
- }
- /* wrap around in the window */
- if (G_UNLIKELY (pos >= jbuf->window_size))
- pos = 0;
- jbuf->window_pos = pos;
-
-no_skew:
- /* the output time is defined as the base timestamp plus the RTP time
- * adjusted for the clock skew .*/
- if (jbuf->base_time != -1) {
- out_time = jbuf->base_time + send_diff + jbuf->skew;
- /* check if timestamps are not going backwards, we can only check this if we
- * have a previous out time and a previous send_diff */
- if (G_LIKELY (jbuf->prev_out_time != -1 && jbuf->prev_send_diff != -1)) {
- /* now check for backwards timestamps */
- if (G_UNLIKELY (
- /* if the server timestamps went up and the out_time backwards */
- (send_diff > jbuf->prev_send_diff
- && out_time < jbuf->prev_out_time) ||
- /* if the server timestamps went backwards and the out_time forwards */
- (send_diff < jbuf->prev_send_diff
- && out_time > jbuf->prev_out_time) ||
- /* if the server timestamps did not change */
- send_diff == jbuf->prev_send_diff)) {
- GST_DEBUG ("backwards timestamps, using previous time");
- out_time = jbuf->prev_out_time;
- }
- }
- } else
- out_time = -1;
-
- jbuf->prev_out_time = out_time;
- jbuf->prev_send_diff = send_diff;
-
- GST_DEBUG ("skew %" G_GINT64_FORMAT ", out %" GST_TIME_FORMAT,
- jbuf->skew, GST_TIME_ARGS (out_time));
-
- return out_time;
-}
-
-/**
- * rtp_jitter_buffer_insert:
- * @jbuf: an #RTPJitterBuffer
- * @buf: a buffer
- * @time: a running_time when this buffer was received in nanoseconds
- * @clock_rate: the clock-rate of the payload of @buf
- * @tail: TRUE when the tail element changed.
- *
- * Inserts @buf into the packet queue of @jbuf. The sequence number of the
- * packet will be used to sort the packets. This function takes ownerhip of
- * @buf when the function returns %TRUE.
- * @buf should have writable metadata when calling this function.
- *
- * Returns: %FALSE if a packet with the same number already existed.
- */
-gboolean
-rtp_jitter_buffer_insert (RTPJitterBuffer * jbuf, GstBuffer * buf,
- GstClockTime time, guint32 clock_rate, gboolean * tail)
-{
- GList *list;
- guint32 rtptime;
- guint16 seqnum;
-
- g_return_val_if_fail (jbuf != NULL, FALSE);
- g_return_val_if_fail (buf != NULL, FALSE);
-
- seqnum = gst_rtp_buffer_get_seq (buf);
-
- /* loop the list to skip strictly smaller seqnum buffers */
- for (list = jbuf->packets->head; list; list = g_list_next (list)) {
- guint16 qseq;
- gint gap;
-
- qseq = gst_rtp_buffer_get_seq (GST_BUFFER_CAST (list->data));
-
- /* compare the new seqnum to the one in the buffer */
- gap = gst_rtp_buffer_compare_seqnum (seqnum, qseq);
-
- /* we hit a packet with the same seqnum, notify a duplicate */
- if (G_UNLIKELY (gap == 0))
- goto duplicate;
-
- /* seqnum > qseq, we can stop looking */
- if (G_LIKELY (gap < 0))
- break;
- }
-
- /* do skew calculation by measuring the difference between rtptime and the
- * receive time, this function will retimestamp @buf with the skew corrected
- * running time. */
- rtptime = gst_rtp_buffer_get_timestamp (buf);
- time = calculate_skew (jbuf, rtptime, time, clock_rate);
- GST_BUFFER_TIMESTAMP (buf) = time;
-
- /* It's more likely that the packet was inserted in the front of the buffer */
- if (G_LIKELY (list))
- g_queue_insert_before (jbuf->packets, list, buf);
- else
- g_queue_push_tail (jbuf->packets, buf);
-
- /* tail was changed when we did not find a previous packet, we set the return
- * flag when requested. */
- if (G_LIKELY (tail))
- *tail = (list == NULL);
-
- return TRUE;
-
- /* ERRORS */
-duplicate:
- {
- GST_WARNING ("duplicate packet %d found", (gint) seqnum);
- return FALSE;
- }
-}
-
-/**
- * rtp_jitter_buffer_pop:
- * @jbuf: an #RTPJitterBuffer
- *
- * Pops the oldest buffer from the packet queue of @jbuf. The popped buffer will
- * have its timestamp adjusted with the incomming running_time and the detected
- * clock skew.
- *
- * Returns: a #GstBuffer or %NULL when there was no packet in the queue.
- */
-GstBuffer *
-rtp_jitter_buffer_pop (RTPJitterBuffer * jbuf)
-{
- GstBuffer *buf;
-
- g_return_val_if_fail (jbuf != NULL, FALSE);
-
- buf = g_queue_pop_tail (jbuf->packets);
-
- return buf;
-}
-
-/**
- * rtp_jitter_buffer_peek:
- * @jbuf: an #RTPJitterBuffer
- *
- * Peek the oldest buffer from the packet queue of @jbuf. Register a callback
- * with rtp_jitter_buffer_set_tail_changed() to be notified when an older packet
- * was inserted in the queue.
- *
- * Returns: a #GstBuffer or %NULL when there was no packet in the queue.
- */
-GstBuffer *
-rtp_jitter_buffer_peek (RTPJitterBuffer * jbuf)
-{
- GstBuffer *buf;
-
- g_return_val_if_fail (jbuf != NULL, FALSE);
-
- buf = g_queue_peek_tail (jbuf->packets);
-
- return buf;
-}
-
-/**
- * rtp_jitter_buffer_flush:
- * @jbuf: an #RTPJitterBuffer
- *
- * Flush all packets from the jitterbuffer.
- */
-void
-rtp_jitter_buffer_flush (RTPJitterBuffer * jbuf)
-{
- GstBuffer *buffer;
-
- g_return_if_fail (jbuf != NULL);
-
- while ((buffer = g_queue_pop_head (jbuf->packets)))
- gst_buffer_unref (buffer);
-}
-
-/**
- * rtp_jitter_buffer_num_packets:
- * @jbuf: an #RTPJitterBuffer
- *
- * Get the number of packets currently in "jbuf.
- *
- * Returns: The number of packets in @jbuf.
- */
-guint
-rtp_jitter_buffer_num_packets (RTPJitterBuffer * jbuf)
-{
- g_return_val_if_fail (jbuf != NULL, 0);
-
- return jbuf->packets->length;
-}
-
-/**
- * rtp_jitter_buffer_get_ts_diff:
- * @jbuf: an #RTPJitterBuffer
- *
- * Get the difference between the timestamps of first and last packet in the
- * jitterbuffer.
- *
- * Returns: The difference expressed in the timestamp units of the packets.
- */
-guint32
-rtp_jitter_buffer_get_ts_diff (RTPJitterBuffer * jbuf)
-{
- guint64 high_ts, low_ts;
- GstBuffer *high_buf, *low_buf;
- guint32 result;
-
- g_return_val_if_fail (jbuf != NULL, 0);
-
- high_buf = g_queue_peek_head (jbuf->packets);
- low_buf = g_queue_peek_tail (jbuf->packets);
-
- if (!high_buf || !low_buf || high_buf == low_buf)
- return 0;
-
- high_ts = gst_rtp_buffer_get_timestamp (high_buf);
- low_ts = gst_rtp_buffer_get_timestamp (low_buf);
-
- /* it needs to work if ts wraps */
- if (high_ts >= low_ts) {
- result = (guint32) (high_ts - low_ts);
- } else {
- result = (guint32) (high_ts + G_MAXUINT32 + 1 - low_ts);
- }
- return result;
-}
-
-/**
- * rtp_jitter_buffer_get_sync:
- * @jbuf: an #RTPJitterBuffer
- * @rtptime: result RTP time
- * @timestamp: result GStreamer timestamp
- * @clock_rate: clock-rate of @rtptime
- * @last_rtptime: last seen rtptime.
- *
- * Calculates the relation between the RTP timestamp and the GStreamer timestamp
- * used for constructing timestamps.
- *
- * For extended RTP timestamp @rtptime with a clock-rate of @clock_rate,
- * the GStreamer timestamp is currently @timestamp.
- *
- * The last seen extended RTP timestamp with clock-rate @clock-rate is returned in
- * @last_rtptime.
- */
-void
-rtp_jitter_buffer_get_sync (RTPJitterBuffer * jbuf, guint64 * rtptime,
- guint64 * timestamp, guint32 * clock_rate, guint64 * last_rtptime)
-{
- if (rtptime)
- *rtptime = jbuf->base_extrtp;
- if (timestamp)
- *timestamp = jbuf->base_time + jbuf->skew;
- if (clock_rate)
- *clock_rate = jbuf->clock_rate;
- if (last_rtptime)
- *last_rtptime = jbuf->last_rtptime;
-}
+++ /dev/null
-/* GStreamer
- * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#ifndef __RTP_JITTER_BUFFER_H__
-#define __RTP_JITTER_BUFFER_H__
-
-#include <gst/gst.h>
-#include <gst/rtp/gstrtcpbuffer.h>
-
-typedef struct _RTPJitterBuffer RTPJitterBuffer;
-typedef struct _RTPJitterBufferClass RTPJitterBufferClass;
-
-#define RTP_TYPE_JITTER_BUFFER (rtp_jitter_buffer_get_type())
-#define RTP_JITTER_BUFFER(src) (G_TYPE_CHECK_INSTANCE_CAST((src),RTP_TYPE_JITTER_BUFFER,RTPJitterBuffer))
-#define RTP_JITTER_BUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),RTP_TYPE_JITTER_BUFFER,RTPJitterBufferClass))
-#define RTP_IS_JITTER_BUFFER(src) (G_TYPE_CHECK_INSTANCE_TYPE((src),RTP_TYPE_JITTER_BUFFER))
-#define RTP_IS_JITTER_BUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),RTP_TYPE_JITTER_BUFFER))
-#define RTP_JITTER_BUFFER_CAST(src) ((RTPJitterBuffer *)(src))
-
-/**
- * RTPTailChanged:
- * @jbuf: an #RTPJitterBuffer
- * @user_data: user data specified when registering
- *
- * This callback will be called when the tail buffer of @jbuf changed.
- */
-typedef void (*RTPTailChanged) (RTPJitterBuffer *jbuf, gpointer user_data);
-
-#define RTP_JITTER_BUFFER_MAX_WINDOW 512
-/**
- * RTPJitterBuffer:
- *
- * A JitterBuffer in the #RTPSession
- */
-struct _RTPJitterBuffer {
- GObject object;
-
- GQueue *packets;
-
- /* for calculating skew */
- GstClockTime base_time;
- GstClockTime base_rtptime;
- guint32 clock_rate;
- GstClockTime base_extrtp;
- GstClockTime prev_out_time;
- guint64 ext_rtptime;
- guint64 last_rtptime;
- gint64 window[RTP_JITTER_BUFFER_MAX_WINDOW];
- guint window_pos;
- guint window_size;
- gboolean window_filling;
- gint64 window_min;
- gint64 skew;
- gint64 prev_send_diff;
-};
-
-struct _RTPJitterBufferClass {
- GObjectClass parent_class;
-};
-
-GType rtp_jitter_buffer_get_type (void);
-
-/* managing lifetime */
-RTPJitterBuffer* rtp_jitter_buffer_new (void);
-
-void rtp_jitter_buffer_reset_skew (RTPJitterBuffer *jbuf);
-
-gboolean rtp_jitter_buffer_insert (RTPJitterBuffer *jbuf, GstBuffer *buf,
- GstClockTime time,
- guint32 clock_rate,
- gboolean *tail);
-GstBuffer * rtp_jitter_buffer_peek (RTPJitterBuffer *jbuf);
-GstBuffer * rtp_jitter_buffer_pop (RTPJitterBuffer *jbuf);
-
-void rtp_jitter_buffer_flush (RTPJitterBuffer *jbuf);
-
-guint rtp_jitter_buffer_num_packets (RTPJitterBuffer *jbuf);
-guint32 rtp_jitter_buffer_get_ts_diff (RTPJitterBuffer *jbuf);
-
-void rtp_jitter_buffer_get_sync (RTPJitterBuffer *jbuf, guint64 *rtptime,
- guint64 *timestamp, guint32 *clock_rate,
- guint64 *last_rtptime);
-
-
-#endif /* __RTP_JITTER_BUFFER_H__ */
+++ /dev/null
-/* GStreamer
- * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#include <string.h>
-
-#include <gst/rtp/gstrtpbuffer.h>
-#include <gst/rtp/gstrtcpbuffer.h>
-#include <gst/netbuffer/gstnetbuffer.h>
-
-#include "gstrtpbin-marshal.h"
-#include "rtpsession.h"
-
-GST_DEBUG_CATEGORY_STATIC (rtp_session_debug);
-#define GST_CAT_DEFAULT rtp_session_debug
-
-/* signals and args */
-enum
-{
- SIGNAL_GET_SOURCE_BY_SSRC,
- SIGNAL_ON_NEW_SSRC,
- SIGNAL_ON_SSRC_COLLISION,
- SIGNAL_ON_SSRC_VALIDATED,
- SIGNAL_ON_SSRC_ACTIVE,
- SIGNAL_ON_SSRC_SDES,
- SIGNAL_ON_BYE_SSRC,
- SIGNAL_ON_BYE_TIMEOUT,
- SIGNAL_ON_TIMEOUT,
- SIGNAL_ON_SENDER_TIMEOUT,
- LAST_SIGNAL
-};
-
-#define DEFAULT_INTERNAL_SOURCE NULL
-#define DEFAULT_BANDWIDTH RTP_STATS_BANDWIDTH
-#define DEFAULT_RTCP_FRACTION RTP_STATS_RTCP_BANDWIDTH
-#define DEFAULT_RTCP_MTU 1400
-#define DEFAULT_SDES NULL
-#define DEFAULT_NUM_SOURCES 0
-#define DEFAULT_NUM_ACTIVE_SOURCES 0
-#define DEFAULT_SOURCES NULL
-
-enum
-{
- PROP_0,
- PROP_INTERNAL_SSRC,
- PROP_INTERNAL_SOURCE,
- PROP_BANDWIDTH,
- PROP_RTCP_FRACTION,
- PROP_RTCP_MTU,
- PROP_SDES,
- PROP_NUM_SOURCES,
- PROP_NUM_ACTIVE_SOURCES,
- PROP_SOURCES,
- PROP_LAST
-};
-
-/* update average packet size, we keep this scaled by 16 to keep enough
- * precision. */
-#define UPDATE_AVG(avg, val) \
- if ((avg) == 0) \
- (avg) = (val) << 4; \
- else \
- (avg) = ((val) + (15 * (avg))) >> 4;
-
-/* The number RTCP intervals after which to timeout entries in the
- * collision table
- */
-#define RTCP_INTERVAL_COLLISION_TIMEOUT 10
-
-/* GObject vmethods */
-static void rtp_session_finalize (GObject * object);
-static void rtp_session_set_property (GObject * object, guint prop_id,
- const GValue * value, GParamSpec * pspec);
-static void rtp_session_get_property (GObject * object, guint prop_id,
- GValue * value, GParamSpec * pspec);
-
-static guint rtp_session_signals[LAST_SIGNAL] = { 0 };
-
-G_DEFINE_TYPE (RTPSession, rtp_session, G_TYPE_OBJECT);
-
-static RTPSource *obtain_source (RTPSession * sess, guint32 ssrc,
- gboolean * created, RTPArrivalStats * arrival, gboolean rtp);
-static GstFlowReturn rtp_session_schedule_bye_locked (RTPSession * sess,
- const gchar * reason, GstClockTime current_time);
-static GstClockTime calculate_rtcp_interval (RTPSession * sess,
- gboolean deterministic, gboolean first);
-
-static void
-rtp_session_class_init (RTPSessionClass * klass)
-{
- GObjectClass *gobject_class;
-
- gobject_class = (GObjectClass *) klass;
-
- gobject_class->finalize = rtp_session_finalize;
- gobject_class->set_property = rtp_session_set_property;
- gobject_class->get_property = rtp_session_get_property;
-
- /**
- * RTPSession::get-source-by-ssrc:
- * @session: the object which received the signal
- * @ssrc: the SSRC of the RTPSource
- *
- * Request the #RTPSource object with SSRC @ssrc in @session.
- */
- rtp_session_signals[SIGNAL_GET_SOURCE_BY_SSRC] =
- g_signal_new ("get-source-by-ssrc", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (RTPSessionClass,
- get_source_by_ssrc), NULL, NULL, gst_rtp_bin_marshal_OBJECT__UINT,
- RTP_TYPE_SOURCE, 1, G_TYPE_UINT);
-
- /**
- * RTPSession::on-new-ssrc:
- * @session: the object which received the signal
- * @src: the new RTPSource
- *
- * Notify of a new SSRC that entered @session.
- */
- rtp_session_signals[SIGNAL_ON_NEW_SSRC] =
- g_signal_new ("on-new-ssrc", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (RTPSessionClass, on_new_ssrc),
- NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
- RTP_TYPE_SOURCE);
- /**
- * RTPSession::on-ssrc-collision:
- * @session: the object which received the signal
- * @src: the #RTPSource that caused a collision
- *
- * Notify when we have an SSRC collision
- */
- rtp_session_signals[SIGNAL_ON_SSRC_COLLISION] =
- g_signal_new ("on-ssrc-collision", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (RTPSessionClass, on_ssrc_collision),
- NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
- RTP_TYPE_SOURCE);
- /**
- * RTPSession::on-ssrc-validated:
- * @session: the object which received the signal
- * @src: the new validated RTPSource
- *
- * Notify of a new SSRC that became validated.
- */
- rtp_session_signals[SIGNAL_ON_SSRC_VALIDATED] =
- g_signal_new ("on-ssrc-validated", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (RTPSessionClass, on_ssrc_validated),
- NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
- RTP_TYPE_SOURCE);
- /**
- * RTPSession::on-ssrc-active:
- * @session: the object which received the signal
- * @src: the active RTPSource
- *
- * Notify of a SSRC that is active, i.e., sending RTCP.
- */
- rtp_session_signals[SIGNAL_ON_SSRC_ACTIVE] =
- g_signal_new ("on-ssrc-active", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (RTPSessionClass, on_ssrc_active),
- NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
- RTP_TYPE_SOURCE);
- /**
- * RTPSession::on-ssrc-sdes:
- * @session: the object which received the signal
- * @src: the RTPSource
- *
- * Notify that a new SDES was received for SSRC.
- */
- rtp_session_signals[SIGNAL_ON_SSRC_SDES] =
- g_signal_new ("on-ssrc-sdes", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (RTPSessionClass, on_ssrc_sdes),
- NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
- RTP_TYPE_SOURCE);
- /**
- * RTPSession::on-bye-ssrc:
- * @session: the object which received the signal
- * @src: the RTPSource that went away
- *
- * Notify of an SSRC that became inactive because of a BYE packet.
- */
- rtp_session_signals[SIGNAL_ON_BYE_SSRC] =
- g_signal_new ("on-bye-ssrc", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (RTPSessionClass, on_bye_ssrc),
- NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
- RTP_TYPE_SOURCE);
- /**
- * RTPSession::on-bye-timeout:
- * @session: the object which received the signal
- * @src: the RTPSource that timed out
- *
- * Notify of an SSRC that has timed out because of BYE
- */
- rtp_session_signals[SIGNAL_ON_BYE_TIMEOUT] =
- g_signal_new ("on-bye-timeout", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (RTPSessionClass, on_bye_timeout),
- NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
- RTP_TYPE_SOURCE);
- /**
- * RTPSession::on-timeout:
- * @session: the object which received the signal
- * @src: the RTPSource that timed out
- *
- * Notify of an SSRC that has timed out
- */
- rtp_session_signals[SIGNAL_ON_TIMEOUT] =
- g_signal_new ("on-timeout", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (RTPSessionClass, on_timeout),
- NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
- RTP_TYPE_SOURCE);
- /**
- * RTPSession::on-sender-timeout:
- * @session: the object which received the signal
- * @src: the RTPSource that timed out
- *
- * Notify of an SSRC that was a sender but timed out and became a receiver.
- */
- rtp_session_signals[SIGNAL_ON_SENDER_TIMEOUT] =
- g_signal_new ("on-sender-timeout", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (RTPSessionClass, on_sender_timeout),
- NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
- RTP_TYPE_SOURCE);
-
- g_object_class_install_property (gobject_class, PROP_INTERNAL_SSRC,
- g_param_spec_uint ("internal-ssrc", "Internal SSRC",
- "The internal SSRC used for the session",
- 0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-
- g_object_class_install_property (gobject_class, PROP_INTERNAL_SOURCE,
- g_param_spec_object ("internal-source", "Internal Source",
- "The internal source element of the session",
- RTP_TYPE_SOURCE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
-
- g_object_class_install_property (gobject_class, PROP_BANDWIDTH,
- g_param_spec_double ("bandwidth", "Bandwidth",
- "The bandwidth of the session",
- 0.0, G_MAXDOUBLE, DEFAULT_BANDWIDTH,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-
- g_object_class_install_property (gobject_class, PROP_RTCP_FRACTION,
- g_param_spec_double ("rtcp-fraction", "RTCP Fraction",
- "The fraction of the bandwidth used for RTCP",
- 0.0, G_MAXDOUBLE, DEFAULT_RTCP_FRACTION,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-
- g_object_class_install_property (gobject_class, PROP_RTCP_MTU,
- g_param_spec_uint ("rtcp-mtu", "RTCP MTU",
- "The maximum size of the RTCP packets",
- 16, G_MAXINT16, DEFAULT_RTCP_MTU,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-
- g_object_class_install_property (gobject_class, PROP_SDES,
- g_param_spec_boxed ("sdes", "SDES",
- "The SDES items of this session",
- GST_TYPE_STRUCTURE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-
- g_object_class_install_property (gobject_class, PROP_NUM_SOURCES,
- g_param_spec_uint ("num-sources", "Num Sources",
- "The number of sources in the session", 0, G_MAXUINT,
- DEFAULT_NUM_SOURCES, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
-
- g_object_class_install_property (gobject_class, PROP_NUM_ACTIVE_SOURCES,
- g_param_spec_uint ("num-active-sources", "Num Active Sources",
- "The number of active sources in the session", 0, G_MAXUINT,
- DEFAULT_NUM_ACTIVE_SOURCES,
- G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
- /**
- * RTPSource::sources
- *
- * Get a GValue Array of all sources in the session.
- *
- * <example>
- * <title>Getting the #RTPSources of a session
- * <programlisting>
- * {
- * GValueArray *arr;
- * GValue *val;
- * guint i;
- *
- * g_object_get (sess, "sources", &arr, NULL);
- *
- * for (i = 0; i < arr->n_values; i++) {
- * RTPSource *source;
- *
- * val = g_value_array_get_nth (arr, i);
- * source = g_value_get_object (val);
- * }
- * g_value_array_free (arr);
- * }
- * </programlisting>
- * </example>
- */
- g_object_class_install_property (gobject_class, PROP_SOURCES,
- g_param_spec_boxed ("sources", "Sources",
- "An array of all known sources in the session",
- G_TYPE_VALUE_ARRAY, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
-
- klass->get_source_by_ssrc =
- GST_DEBUG_FUNCPTR (rtp_session_get_source_by_ssrc);
-
- GST_DEBUG_CATEGORY_INIT (rtp_session_debug, "rtpsession", 0, "RTP Session");
-}
-
-static void
-rtp_session_init (RTPSession * sess)
-{
- gint i;
- gchar *str;
-
- sess->lock = g_mutex_new ();
- sess->key = g_random_int ();
- sess->mask_idx = 0;
- sess->mask = 0;
-
- for (i = 0; i < 32; i++) {
- sess->ssrcs[i] =
- g_hash_table_new_full (NULL, NULL, NULL,
- (GDestroyNotify) g_object_unref);
- }
- sess->cnames = g_hash_table_new_full (NULL, NULL, g_free, NULL);
-
- rtp_stats_init_defaults (&sess->stats);
-
- /* create an active SSRC for this session manager */
- sess->source = rtp_session_create_source (sess);
- sess->source->validated = TRUE;
- sess->source->internal = TRUE;
- sess->stats.active_sources++;
-
- /* default UDP header length */
- sess->header_len = 28;
- sess->mtu = DEFAULT_RTCP_MTU;
-
- /* some default SDES entries */
- str = g_strdup_printf ("%s@%s", g_get_user_name (), g_get_host_name ());
- rtp_source_set_sdes_string (sess->source, GST_RTCP_SDES_CNAME, str);
- g_free (str);
-
- rtp_source_set_sdes_string (sess->source, GST_RTCP_SDES_NAME,
- g_get_real_name ());
- rtp_source_set_sdes_string (sess->source, GST_RTCP_SDES_TOOL, "GStreamer");
-
- sess->first_rtcp = TRUE;
-
- GST_DEBUG ("%p: session using SSRC: %08x", sess, sess->source->ssrc);
-}
-
-static void
-rtp_session_finalize (GObject * object)
-{
- RTPSession *sess;
- gint i;
-
- sess = RTP_SESSION_CAST (object);
-
- g_mutex_free (sess->lock);
- for (i = 0; i < 32; i++)
- g_hash_table_destroy (sess->ssrcs[i]);
-
- g_list_foreach (sess->conflicting_addresses, (GFunc) g_free, NULL);
- g_list_free (sess->conflicting_addresses);
-
- g_free (sess->bye_reason);
-
- g_hash_table_destroy (sess->cnames);
- g_object_unref (sess->source);
-
- G_OBJECT_CLASS (rtp_session_parent_class)->finalize (object);
-}
-
-static void
-copy_source (gpointer key, RTPSource * source, GValueArray * arr)
-{
- GValue value = { 0 };
-
- g_value_init (&value, RTP_TYPE_SOURCE);
- g_value_take_object (&value, source);
- /* copies the value */
- g_value_array_append (arr, &value);
-}
-
-static GValueArray *
-rtp_session_create_sources (RTPSession * sess)
-{
- GValueArray *res;
- guint size;
-
- RTP_SESSION_LOCK (sess);
- /* get number of elements in the table */
- size = g_hash_table_size (sess->ssrcs[sess->mask_idx]);
- /* create the result value array */
- res = g_value_array_new (size);
-
- /* and copy all values into the array */
- g_hash_table_foreach (sess->ssrcs[sess->mask_idx], (GHFunc) copy_source, res);
- RTP_SESSION_UNLOCK (sess);
-
- return res;
-}
-
-static void
-rtp_session_set_property (GObject * object, guint prop_id,
- const GValue * value, GParamSpec * pspec)
-{
- RTPSession *sess;
-
- sess = RTP_SESSION (object);
-
- switch (prop_id) {
- case PROP_INTERNAL_SSRC:
- rtp_session_set_internal_ssrc (sess, g_value_get_uint (value));
- break;
- case PROP_BANDWIDTH:
- rtp_session_set_bandwidth (sess, g_value_get_double (value));
- break;
- case PROP_RTCP_FRACTION:
- rtp_session_set_rtcp_fraction (sess, g_value_get_double (value));
- break;
- case PROP_RTCP_MTU:
- sess->mtu = g_value_get_uint (value);
- break;
- case PROP_SDES:
- rtp_session_set_sdes_struct (sess, g_value_get_boxed (value));
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-static void
-rtp_session_get_property (GObject * object, guint prop_id,
- GValue * value, GParamSpec * pspec)
-{
- RTPSession *sess;
-
- sess = RTP_SESSION (object);
-
- switch (prop_id) {
- case PROP_INTERNAL_SSRC:
- g_value_set_uint (value, rtp_session_get_internal_ssrc (sess));
- break;
- case PROP_INTERNAL_SOURCE:
- g_value_take_object (value, rtp_session_get_internal_source (sess));
- break;
- case PROP_BANDWIDTH:
- g_value_set_double (value, rtp_session_get_bandwidth (sess));
- break;
- case PROP_RTCP_FRACTION:
- g_value_set_double (value, rtp_session_get_rtcp_fraction (sess));
- break;
- case PROP_RTCP_MTU:
- g_value_set_uint (value, sess->mtu);
- break;
- case PROP_SDES:
- g_value_take_boxed (value, rtp_session_get_sdes_struct (sess));
- break;
- case PROP_NUM_SOURCES:
- g_value_set_uint (value, rtp_session_get_num_sources (sess));
- break;
- case PROP_NUM_ACTIVE_SOURCES:
- g_value_set_uint (value, rtp_session_get_num_active_sources (sess));
- break;
- case PROP_SOURCES:
- g_value_take_boxed (value, rtp_session_create_sources (sess));
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-static void
-on_new_ssrc (RTPSession * sess, RTPSource * source)
-{
- g_object_ref (source);
- RTP_SESSION_UNLOCK (sess);
- g_signal_emit (sess, rtp_session_signals[SIGNAL_ON_NEW_SSRC], 0, source);
- RTP_SESSION_LOCK (sess);
- g_object_unref (source);
-}
-
-static void
-on_ssrc_collision (RTPSession * sess, RTPSource * source)
-{
- g_object_ref (source);
- RTP_SESSION_UNLOCK (sess);
- g_signal_emit (sess, rtp_session_signals[SIGNAL_ON_SSRC_COLLISION], 0,
- source);
- RTP_SESSION_LOCK (sess);
- g_object_unref (source);
-}
-
-static void
-on_ssrc_validated (RTPSession * sess, RTPSource * source)
-{
- g_object_ref (source);
- RTP_SESSION_UNLOCK (sess);
- g_signal_emit (sess, rtp_session_signals[SIGNAL_ON_SSRC_VALIDATED], 0,
- source);
- RTP_SESSION_LOCK (sess);
- g_object_unref (source);
-}
-
-static void
-on_ssrc_active (RTPSession * sess, RTPSource * source)
-{
- g_object_ref (source);
- RTP_SESSION_UNLOCK (sess);
- g_signal_emit (sess, rtp_session_signals[SIGNAL_ON_SSRC_ACTIVE], 0, source);
- RTP_SESSION_LOCK (sess);
- g_object_unref (source);
-}
-
-static void
-on_ssrc_sdes (RTPSession * sess, RTPSource * source)
-{
- g_object_ref (source);
- GST_DEBUG ("SDES changed for SSRC %08x", source->ssrc);
- RTP_SESSION_UNLOCK (sess);
- g_signal_emit (sess, rtp_session_signals[SIGNAL_ON_SSRC_SDES], 0, source);
- RTP_SESSION_LOCK (sess);
- g_object_unref (source);
-}
-
-static void
-on_bye_ssrc (RTPSession * sess, RTPSource * source)
-{
- g_object_ref (source);
- RTP_SESSION_UNLOCK (sess);
- g_signal_emit (sess, rtp_session_signals[SIGNAL_ON_BYE_SSRC], 0, source);
- RTP_SESSION_LOCK (sess);
- g_object_unref (source);
-}
-
-static void
-on_bye_timeout (RTPSession * sess, RTPSource * source)
-{
- g_object_ref (source);
- RTP_SESSION_UNLOCK (sess);
- g_signal_emit (sess, rtp_session_signals[SIGNAL_ON_BYE_TIMEOUT], 0, source);
- RTP_SESSION_LOCK (sess);
- g_object_unref (source);
-}
-
-static void
-on_timeout (RTPSession * sess, RTPSource * source)
-{
- g_object_ref (source);
- RTP_SESSION_UNLOCK (sess);
- g_signal_emit (sess, rtp_session_signals[SIGNAL_ON_TIMEOUT], 0, source);
- RTP_SESSION_LOCK (sess);
- g_object_unref (source);
-}
-
-static void
-on_sender_timeout (RTPSession * sess, RTPSource * source)
-{
- g_object_ref (source);
- RTP_SESSION_UNLOCK (sess);
- g_signal_emit (sess, rtp_session_signals[SIGNAL_ON_SENDER_TIMEOUT], 0,
- source);
- RTP_SESSION_LOCK (sess);
- g_object_unref (source);
-}
-
-/**
- * rtp_session_new:
- *
- * Create a new session object.
- *
- * Returns: a new #RTPSession. g_object_unref() after usage.
- */
-RTPSession *
-rtp_session_new (void)
-{
- RTPSession *sess;
-
- sess = g_object_new (RTP_TYPE_SESSION, NULL);
-
- return sess;
-}
-
-/**
- * rtp_session_set_callbacks:
- * @sess: an #RTPSession
- * @callbacks: callbacks to configure
- * @user_data: user data passed in the callbacks
- *
- * Configure a set of callbacks to be notified of actions.
- */
-void
-rtp_session_set_callbacks (RTPSession * sess, RTPSessionCallbacks * callbacks,
- gpointer user_data)
-{
- g_return_if_fail (RTP_IS_SESSION (sess));
-
- if (callbacks->process_rtp) {
- sess->callbacks.process_rtp = callbacks->process_rtp;
- sess->process_rtp_user_data = user_data;
- }
- if (callbacks->send_rtp) {
- sess->callbacks.send_rtp = callbacks->send_rtp;
- sess->send_rtp_user_data = user_data;
- }
- if (callbacks->send_rtcp) {
- sess->callbacks.send_rtcp = callbacks->send_rtcp;
- sess->send_rtcp_user_data = user_data;
- }
- if (callbacks->sync_rtcp) {
- sess->callbacks.sync_rtcp = callbacks->sync_rtcp;
- sess->sync_rtcp_user_data = user_data;
- }
- if (callbacks->clock_rate) {
- sess->callbacks.clock_rate = callbacks->clock_rate;
- sess->clock_rate_user_data = user_data;
- }
- if (callbacks->reconsider) {
- sess->callbacks.reconsider = callbacks->reconsider;
- sess->reconsider_user_data = user_data;
- }
-}
-
-/**
- * rtp_session_set_process_rtp_callback:
- * @sess: an #RTPSession
- * @callback: callback to set
- * @user_data: user data passed in the callback
- *
- * Configure only the process_rtp callback to be notified of the process_rtp action.
- */
-void
-rtp_session_set_process_rtp_callback (RTPSession * sess,
- RTPSessionProcessRTP callback, gpointer user_data)
-{
- g_return_if_fail (RTP_IS_SESSION (sess));
-
- sess->callbacks.process_rtp = callback;
- sess->process_rtp_user_data = user_data;
-}
-
-/**
- * rtp_session_set_send_rtp_callback:
- * @sess: an #RTPSession
- * @callback: callback to set
- * @user_data: user data passed in the callback
- *
- * Configure only the send_rtp callback to be notified of the send_rtp action.
- */
-void
-rtp_session_set_send_rtp_callback (RTPSession * sess,
- RTPSessionSendRTP callback, gpointer user_data)
-{
- g_return_if_fail (RTP_IS_SESSION (sess));
-
- sess->callbacks.send_rtp = callback;
- sess->send_rtp_user_data = user_data;
-}
-
-/**
- * rtp_session_set_send_rtcp_callback:
- * @sess: an #RTPSession
- * @callback: callback to set
- * @user_data: user data passed in the callback
- *
- * Configure only the send_rtcp callback to be notified of the send_rtcp action.
- */
-void
-rtp_session_set_send_rtcp_callback (RTPSession * sess,
- RTPSessionSendRTCP callback, gpointer user_data)
-{
- g_return_if_fail (RTP_IS_SESSION (sess));
-
- sess->callbacks.send_rtcp = callback;
- sess->send_rtcp_user_data = user_data;
-}
-
-/**
- * rtp_session_set_sync_rtcp_callback:
- * @sess: an #RTPSession
- * @callback: callback to set
- * @user_data: user data passed in the callback
- *
- * Configure only the sync_rtcp callback to be notified of the sync_rtcp action.
- */
-void
-rtp_session_set_sync_rtcp_callback (RTPSession * sess,
- RTPSessionSyncRTCP callback, gpointer user_data)
-{
- g_return_if_fail (RTP_IS_SESSION (sess));
-
- sess->callbacks.sync_rtcp = callback;
- sess->sync_rtcp_user_data = user_data;
-}
-
-/**
- * rtp_session_set_clock_rate_callback:
- * @sess: an #RTPSession
- * @callback: callback to set
- * @user_data: user data passed in the callback
- *
- * Configure only the clock_rate callback to be notified of the clock_rate action.
- */
-void
-rtp_session_set_clock_rate_callback (RTPSession * sess,
- RTPSessionClockRate callback, gpointer user_data)
-{
- g_return_if_fail (RTP_IS_SESSION (sess));
-
- sess->callbacks.clock_rate = callback;
- sess->clock_rate_user_data = user_data;
-}
-
-/**
- * rtp_session_set_reconsider_callback:
- * @sess: an #RTPSession
- * @callback: callback to set
- * @user_data: user data passed in the callback
- *
- * Configure only the reconsider callback to be notified of the reconsider action.
- */
-void
-rtp_session_set_reconsider_callback (RTPSession * sess,
- RTPSessionReconsider callback, gpointer user_data)
-{
- g_return_if_fail (RTP_IS_SESSION (sess));
-
- sess->callbacks.reconsider = callback;
- sess->reconsider_user_data = user_data;
-}
-
-/**
- * rtp_session_set_bandwidth:
- * @sess: an #RTPSession
- * @bandwidth: the bandwidth allocated
- *
- * Set the session bandwidth in bytes per second.
- */
-void
-rtp_session_set_bandwidth (RTPSession * sess, gdouble bandwidth)
-{
- g_return_if_fail (RTP_IS_SESSION (sess));
-
- RTP_SESSION_LOCK (sess);
- sess->stats.bandwidth = bandwidth;
- RTP_SESSION_UNLOCK (sess);
-}
-
-/**
- * rtp_session_get_bandwidth:
- * @sess: an #RTPSession
- *
- * Get the session bandwidth.
- *
- * Returns: the session bandwidth.
- */
-gdouble
-rtp_session_get_bandwidth (RTPSession * sess)
-{
- gdouble result;
-
- g_return_val_if_fail (RTP_IS_SESSION (sess), 0);
-
- RTP_SESSION_LOCK (sess);
- result = sess->stats.bandwidth;
- RTP_SESSION_UNLOCK (sess);
-
- return result;
-}
-
-/**
- * rtp_session_set_rtcp_fraction:
- * @sess: an #RTPSession
- * @bandwidth: the RTCP bandwidth
- *
- * Set the bandwidth that should be used for RTCP
- * messages.
- */
-void
-rtp_session_set_rtcp_fraction (RTPSession * sess, gdouble bandwidth)
-{
- g_return_if_fail (RTP_IS_SESSION (sess));
-
- RTP_SESSION_LOCK (sess);
- sess->stats.rtcp_bandwidth = bandwidth;
- RTP_SESSION_UNLOCK (sess);
-}
-
-/**
- * rtp_session_get_rtcp_fraction:
- * @sess: an #RTPSession
- *
- * Get the session bandwidth used for RTCP.
- *
- * Returns: The bandwidth used for RTCP messages.
- */
-gdouble
-rtp_session_get_rtcp_fraction (RTPSession * sess)
-{
- gdouble result;
-
- g_return_val_if_fail (RTP_IS_SESSION (sess), 0.0);
-
- RTP_SESSION_LOCK (sess);
- result = sess->stats.rtcp_bandwidth;
- RTP_SESSION_UNLOCK (sess);
-
- return result;
-}
-
-/**
- * rtp_session_set_sdes_string:
- * @sess: an #RTPSession
- * @type: the type of the SDES item
- * @item: a null-terminated string to set.
- *
- * Store an SDES item of @type in @sess.
- *
- * Returns: %FALSE if the data was unchanged @type is invalid.
- */
-gboolean
-rtp_session_set_sdes_string (RTPSession * sess, GstRTCPSDESType type,
- const gchar * item)
-{
- gboolean result;
-
- g_return_val_if_fail (RTP_IS_SESSION (sess), FALSE);
-
- RTP_SESSION_LOCK (sess);
- result = rtp_source_set_sdes_string (sess->source, type, item);
- RTP_SESSION_UNLOCK (sess);
-
- return result;
-}
-
-/**
- * rtp_session_get_sdes_string:
- * @sess: an #RTPSession
- * @type: the type of the SDES item
- *
- * Get the SDES item of @type from @sess.
- *
- * Returns: a null-terminated copy of the SDES item or NULL when @type was not
- * valid. g_free() after usage.
- */
-gchar *
-rtp_session_get_sdes_string (RTPSession * sess, GstRTCPSDESType type)
-{
- gchar *result;
-
- g_return_val_if_fail (RTP_IS_SESSION (sess), NULL);
-
- RTP_SESSION_LOCK (sess);
- result = rtp_source_get_sdes_string (sess->source, type);
- RTP_SESSION_UNLOCK (sess);
-
- return result;
-}
-
-/**
- * rtp_session_get_sdes_struct:
- * @sess: an #RTSPSession
- *
- * Get the SDES data as a #GstStructure
- *
- * Returns: a GstStructure with SDES items for @sess.
- */
-GstStructure *
-rtp_session_get_sdes_struct (RTPSession * sess)
-{
- GstStructure *result;
-
- g_return_val_if_fail (RTP_IS_SESSION (sess), NULL);
-
- RTP_SESSION_LOCK (sess);
- result = rtp_source_get_sdes_struct (sess->source);
- RTP_SESSION_UNLOCK (sess);
-
- return result;
-}
-
-/**
- * rtp_session_set_sdes_struct:
- * @sess: an #RTSPSession
- * @sdes: a #GstStructure
- *
- * Set the SDES data as a #GstStructure.
- */
-void
-rtp_session_set_sdes_struct (RTPSession * sess, const GstStructure * sdes)
-{
- g_return_if_fail (RTP_IS_SESSION (sess));
-
- RTP_SESSION_LOCK (sess);
- rtp_source_set_sdes_struct (sess->source, sdes);
- RTP_SESSION_UNLOCK (sess);
-}
-
-static GstFlowReturn
-source_push_rtp (RTPSource * source, gpointer data, RTPSession * session)
-{
- GstFlowReturn result = GST_FLOW_OK;
-
- if (source == session->source) {
- GST_LOG ("source %08x pushed sender RTP packet", source->ssrc);
-
- RTP_SESSION_UNLOCK (session);
-
- if (session->callbacks.send_rtp)
- result =
- session->callbacks.send_rtp (session, source, data,
- session->send_rtp_user_data);
- else {
- gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
- }
- } else {
- GST_LOG ("source %08x pushed receiver RTP packet", source->ssrc);
- RTP_SESSION_UNLOCK (session);
-
- if (session->callbacks.process_rtp)
- result =
- session->callbacks.process_rtp (session, source,
- GST_BUFFER_CAST (data), session->process_rtp_user_data);
- else
- gst_buffer_unref (GST_BUFFER_CAST (data));
- }
- RTP_SESSION_LOCK (session);
-
- return result;
-}
-
-static gint
-source_clock_rate (RTPSource * source, guint8 pt, RTPSession * session)
-{
- gint result;
-
- RTP_SESSION_UNLOCK (session);
-
- if (session->callbacks.clock_rate)
- result =
- session->callbacks.clock_rate (session, pt,
- session->clock_rate_user_data);
- else
- result = -1;
-
- RTP_SESSION_LOCK (session);
-
- GST_DEBUG ("got clock-rate %d for pt %d", result, pt);
-
- return result;
-}
-
-static RTPSourceCallbacks callbacks = {
- (RTPSourcePushRTP) source_push_rtp,
- (RTPSourceClockRate) source_clock_rate,
-};
-
-/**
- * find_add_conflicting_addresses:
- * @sess: The session to check in
- * @arrival: The arrival stats for the buffer
- *
- * Checks if an address which has a conflict is already known,
- * otherwise remembers it to prevent loops.
- *
- * Returns: TRUE if it was a known conflict, FALSE otherwise
- */
-
-static gboolean
-find_add_conflicting_addresses (RTPSession * sess, RTPArrivalStats * arrival)
-{
- GList *item;
- RTPConflictingAddress *new_conflict;
-
- for (item = g_list_first (sess->conflicting_addresses);
- item; item = g_list_next (item)) {
- RTPConflictingAddress *known_conflict = item->data;
-
- if (gst_netaddress_equal (&arrival->address, &known_conflict->address)) {
- known_conflict->time = arrival->time;
- return TRUE;
- }
- }
-
- new_conflict = g_new0 (RTPConflictingAddress, 1);
-
- memcpy (&new_conflict->address, &arrival->address, sizeof (GstNetAddress));
- new_conflict->time = arrival->time;
-
- sess->conflicting_addresses = g_list_prepend (sess->conflicting_addresses,
- new_conflict);
-
- return FALSE;
-}
-
-static gboolean
-check_collision (RTPSession * sess, RTPSource * source,
- RTPArrivalStats * arrival, gboolean rtp)
-{
- /* If we have no arrival address, we can't do collision checking */
- if (!arrival->have_address)
- return FALSE;
-
- if (sess->source != source) {
- /* This is not our local source, but lets check if two remote
- * source collide
- */
- if (rtp) {
- if (source->have_rtp_from) {
- if (gst_netaddress_equal (&source->rtp_from, &arrival->address))
- /* Address is the same */
- return FALSE;
- } else {
- /* We don't already have a from address for RTP, just set it */
- rtp_source_set_rtp_from (source, &arrival->address);
- return FALSE;
- }
- } else {
- if (source->have_rtcp_from) {
- if (gst_netaddress_equal (&source->rtcp_from, &arrival->address))
- /* Address is the same */
- return FALSE;
- } else {
- /* We don't already have a from address for RTCP, just set it */
- rtp_source_set_rtcp_from (source, &arrival->address);
- return FALSE;
- }
- }
- /* We received RTP or RTCP from this source before but the network address
- * changed. In this case, we have third-party collision or loop */
- GST_DEBUG ("we have a third-party collision or loop");
-
- /* FIXME: Log 3rd party collision somehow
- * Maybe should be done in upper layer, only the SDES can tell us
- * if its a collision or a loop
- */
- } else {
- /* This is sending with our ssrc, is it an address we already know */
-
- if (find_add_conflicting_addresses (sess, arrival)) {
- /* Its a known conflict, its probably a loop, not a collision
- * lets just drop the incoming packet
- */
- GST_DEBUG ("Our packets are being looped back to us, dropping");
- } else {
- /* Its a new collision, lets change our SSRC */
-
- GST_DEBUG ("Collision for SSRC %x", rtp_source_get_ssrc (source));
- on_ssrc_collision (sess, source);
-
- rtp_session_schedule_bye_locked (sess, "SSRC Collision", arrival->time);
-
- sess->change_ssrc = TRUE;
- }
- }
-
- return TRUE;
-}
-
-
-/* must be called with the session lock, the returned source needs to be
- * unreffed after usage. */
-static RTPSource *
-obtain_source (RTPSession * sess, guint32 ssrc, gboolean * created,
- RTPArrivalStats * arrival, gboolean rtp)
-{
- RTPSource *source;
-
- source =
- g_hash_table_lookup (sess->ssrcs[sess->mask_idx], GINT_TO_POINTER (ssrc));
- if (source == NULL) {
- /* make new Source in probation and insert */
- source = rtp_source_new (ssrc);
-
- /* for RTP packets we need to set the source in probation. Receiving RTCP
- * packets of an SSRC, on the other hand, is a strong indication that we
- * are dealing with a valid source. */
- if (rtp)
- source->probation = RTP_DEFAULT_PROBATION;
- else
- source->probation = 0;
-
- /* store from address, if any */
- if (arrival->have_address) {
- if (rtp)
- rtp_source_set_rtp_from (source, &arrival->address);
- else
- rtp_source_set_rtcp_from (source, &arrival->address);
- }
-
- /* configure a callback on the source */
- rtp_source_set_callbacks (source, &callbacks, sess);
-
- g_hash_table_insert (sess->ssrcs[sess->mask_idx], GINT_TO_POINTER (ssrc),
- source);
-
- /* we have one more source now */
- sess->total_sources++;
- *created = TRUE;
- } else {
- *created = FALSE;
- /* check for collision, this updates the address when not previously set */
- if (check_collision (sess, source, arrival, rtp)) {
- return NULL;
- }
- }
- /* update last activity */
- source->last_activity = arrival->time;
- if (rtp)
- source->last_rtp_activity = arrival->time;
- g_object_ref (source);
-
- return source;
-}
-
-/**
- * rtp_session_get_internal_source:
- * @sess: a #RTPSession
- *
- * Get the internal #RTPSource of @sess.
- *
- * Returns: The internal #RTPSource. g_object_unref() after usage.
- */
-RTPSource *
-rtp_session_get_internal_source (RTPSession * sess)
-{
- RTPSource *result;
-
- g_return_val_if_fail (RTP_IS_SESSION (sess), NULL);
-
- result = g_object_ref (sess->source);
-
- return result;
-}
-
-/**
- * rtp_session_set_internal_ssrc:
- * @sess: a #RTPSession
- * @ssrc: an SSRC
- *
- * Set the SSRC of @sess to @ssrc.
- */
-void
-rtp_session_set_internal_ssrc (RTPSession * sess, guint32 ssrc)
-{
- RTP_SESSION_LOCK (sess);
- if (ssrc != sess->source->ssrc) {
- g_hash_table_steal (sess->ssrcs[sess->mask_idx],
- GINT_TO_POINTER (sess->source->ssrc));
-
- GST_DEBUG ("setting internal SSRC to %08x", ssrc);
- /* After this call, any receiver of the old SSRC either in RTP or RTCP
- * packets will timeout on the old SSRC, we could potentially schedule a
- * BYE RTCP for the old SSRC... */
- sess->source->ssrc = ssrc;
- rtp_source_reset (sess->source);
-
- /* rehash with the new SSRC */
- g_hash_table_insert (sess->ssrcs[sess->mask_idx],
- GINT_TO_POINTER (sess->source->ssrc), sess->source);
- }
- RTP_SESSION_UNLOCK (sess);
-
- g_object_notify (G_OBJECT (sess), "internal-ssrc");
-}
-
-/**
- * rtp_session_get_internal_ssrc:
- * @sess: a #RTPSession
- *
- * Get the internal SSRC of @sess.
- *
- * Returns: The SSRC of the session.
- */
-guint32
-rtp_session_get_internal_ssrc (RTPSession * sess)
-{
- guint32 ssrc;
-
- RTP_SESSION_LOCK (sess);
- ssrc = sess->source->ssrc;
- RTP_SESSION_UNLOCK (sess);
-
- return ssrc;
-}
-
-/**
- * rtp_session_add_source:
- * @sess: a #RTPSession
- * @src: #RTPSource to add
- *
- * Add @src to @session.
- *
- * Returns: %TRUE on success, %FALSE if a source with the same SSRC already
- * existed in the session.
- */
-gboolean
-rtp_session_add_source (RTPSession * sess, RTPSource * src)
-{
- gboolean result = FALSE;
- RTPSource *find;
-
- g_return_val_if_fail (RTP_IS_SESSION (sess), FALSE);
- g_return_val_if_fail (src != NULL, FALSE);
-
- RTP_SESSION_LOCK (sess);
- find =
- g_hash_table_lookup (sess->ssrcs[sess->mask_idx],
- GINT_TO_POINTER (src->ssrc));
- if (find == NULL) {
- g_hash_table_insert (sess->ssrcs[sess->mask_idx],
- GINT_TO_POINTER (src->ssrc), src);
- /* we have one more source now */
- sess->total_sources++;
- result = TRUE;
- }
- RTP_SESSION_UNLOCK (sess);
-
- return result;
-}
-
-/**
- * rtp_session_get_num_sources:
- * @sess: an #RTPSession
- *
- * Get the number of sources in @sess.
- *
- * Returns: The number of sources in @sess.
- */
-guint
-rtp_session_get_num_sources (RTPSession * sess)
-{
- guint result;
-
- g_return_val_if_fail (RTP_IS_SESSION (sess), FALSE);
-
- RTP_SESSION_LOCK (sess);
- result = sess->total_sources;
- RTP_SESSION_UNLOCK (sess);
-
- return result;
-}
-
-/**
- * rtp_session_get_num_active_sources:
- * @sess: an #RTPSession
- *
- * Get the number of active sources in @sess. A source is considered active when
- * it has been validated and has not yet received a BYE RTCP message.
- *
- * Returns: The number of active sources in @sess.
- */
-guint
-rtp_session_get_num_active_sources (RTPSession * sess)
-{
- guint result;
-
- g_return_val_if_fail (RTP_IS_SESSION (sess), 0);
-
- RTP_SESSION_LOCK (sess);
- result = sess->stats.active_sources;
- RTP_SESSION_UNLOCK (sess);
-
- return result;
-}
-
-/**
- * rtp_session_get_source_by_ssrc:
- * @sess: an #RTPSession
- * @ssrc: an SSRC
- *
- * Find the source with @ssrc in @sess.
- *
- * Returns: a #RTPSource with SSRC @ssrc or NULL if the source was not found.
- * g_object_unref() after usage.
- */
-RTPSource *
-rtp_session_get_source_by_ssrc (RTPSession * sess, guint32 ssrc)
-{
- RTPSource *result;
-
- g_return_val_if_fail (RTP_IS_SESSION (sess), NULL);
-
- RTP_SESSION_LOCK (sess);
- result =
- g_hash_table_lookup (sess->ssrcs[sess->mask_idx], GINT_TO_POINTER (ssrc));
- if (result)
- g_object_ref (result);
- RTP_SESSION_UNLOCK (sess);
-
- return result;
-}
-
-/**
- * rtp_session_get_source_by_cname:
- * @sess: a #RTPSession
- * @cname: an CNAME
- *
- * Find the source with @cname in @sess.
- *
- * Returns: a #RTPSource with CNAME @cname or NULL if the source was not found.
- * g_object_unref() after usage.
- */
-RTPSource *
-rtp_session_get_source_by_cname (RTPSession * sess, const gchar * cname)
-{
- RTPSource *result;
-
- g_return_val_if_fail (RTP_IS_SESSION (sess), NULL);
- g_return_val_if_fail (cname != NULL, NULL);
-
- RTP_SESSION_LOCK (sess);
- result = g_hash_table_lookup (sess->cnames, cname);
- if (result)
- g_object_ref (result);
- RTP_SESSION_UNLOCK (sess);
-
- return result;
-}
-
-static guint32
-rtp_session_create_new_ssrc (RTPSession * sess)
-{
- guint32 ssrc;
-
- while (TRUE) {
- ssrc = g_random_int ();
-
- /* see if it exists in the session, we're done if it doesn't */
- if (g_hash_table_lookup (sess->ssrcs[sess->mask_idx],
- GINT_TO_POINTER (ssrc)) == NULL)
- break;
- }
- return ssrc;
-}
-
-
-/**
- * rtp_session_create_source:
- * @sess: an #RTPSession
- *
- * Create an #RTPSource for use in @sess. This function will create a source
- * with an ssrc that is currently not used by any participants in the session.
- *
- * Returns: an #RTPSource.
- */
-RTPSource *
-rtp_session_create_source (RTPSession * sess)
-{
- guint32 ssrc;
- RTPSource *source;
-
- RTP_SESSION_LOCK (sess);
- ssrc = rtp_session_create_new_ssrc (sess);
- source = rtp_source_new (ssrc);
- rtp_source_set_callbacks (source, &callbacks, sess);
- /* we need an additional ref for the source in the hashtable */
- g_object_ref (source);
- g_hash_table_insert (sess->ssrcs[sess->mask_idx], GINT_TO_POINTER (ssrc),
- source);
- /* we have one more source now */
- sess->total_sources++;
- RTP_SESSION_UNLOCK (sess);
-
- return source;
-}
-
-/* update the RTPArrivalStats structure with the current time and other bits
- * about the current buffer we are handling.
- * This function is typically called when a validated packet is received.
- * This function should be called with the SESSION_LOCK
- */
-static void
-update_arrival_stats (RTPSession * sess, RTPArrivalStats * arrival,
- gboolean rtp, GstBuffer * buffer, GstClockTime current_time,
- GstClockTime running_time, guint64 ntpnstime)
-{
- /* get time of arrival */
- arrival->time = current_time;
- arrival->running_time = running_time;
- arrival->ntpnstime = ntpnstime;
-
- /* get packet size including header overhead */
- arrival->bytes = GST_BUFFER_SIZE (buffer) + sess->header_len;
-
- if (rtp) {
- arrival->payload_len = gst_rtp_buffer_get_payload_len (buffer);
- } else {
- arrival->payload_len = 0;
- }
-
- /* for netbuffer we can store the IP address to check for collisions */
- arrival->have_address = GST_IS_NETBUFFER (buffer);
- if (arrival->have_address) {
- GstNetBuffer *netbuf = (GstNetBuffer *) buffer;
-
- memcpy (&arrival->address, &netbuf->from, sizeof (GstNetAddress));
- }
-}
-
-/**
- * rtp_session_process_rtp:
- * @sess: and #RTPSession
- * @buffer: an RTP buffer
- * @current_time: the current system time
- * @ntpnstime: the NTP arrival time in nanoseconds
- *
- * Process an RTP buffer in the session manager. This function takes ownership
- * of @buffer.
- *
- * Returns: a #GstFlowReturn.
- */
-GstFlowReturn
-rtp_session_process_rtp (RTPSession * sess, GstBuffer * buffer,
- GstClockTime current_time, GstClockTime running_time, guint64 ntpnstime)
-{
- GstFlowReturn result;
- guint32 ssrc;
- RTPSource *source;
- gboolean created;
- gboolean prevsender, prevactive;
- RTPArrivalStats arrival;
-
- g_return_val_if_fail (RTP_IS_SESSION (sess), GST_FLOW_ERROR);
- g_return_val_if_fail (GST_IS_BUFFER (buffer), GST_FLOW_ERROR);
-
- if (!gst_rtp_buffer_validate (buffer))
- goto invalid_packet;
-
- RTP_SESSION_LOCK (sess);
- /* update arrival stats */
- update_arrival_stats (sess, &arrival, TRUE, buffer, current_time,
- running_time, ntpnstime);
-
- /* ignore more RTP packets when we left the session */
- if (sess->source->received_bye)
- goto ignore;
-
- /* get SSRC and look up in session database */
- ssrc = gst_rtp_buffer_get_ssrc (buffer);
- source = obtain_source (sess, ssrc, &created, &arrival, TRUE);
- if (!source)
- goto collision;
-
- prevsender = RTP_SOURCE_IS_SENDER (source);
- prevactive = RTP_SOURCE_IS_ACTIVE (source);
-
- /* we need to ref so that we can process the CSRCs later */
- gst_buffer_ref (buffer);
-
- /* let source process the packet */
- result = rtp_source_process_rtp (source, buffer, &arrival);
-
- /* source became active */
- if (prevactive != RTP_SOURCE_IS_ACTIVE (source)) {
- sess->stats.active_sources++;
- GST_DEBUG ("source: %08x became active, %d active sources", ssrc,
- sess->stats.active_sources);
- on_ssrc_validated (sess, source);
- }
- if (prevsender != RTP_SOURCE_IS_SENDER (source)) {
- sess->stats.sender_sources++;
- GST_DEBUG ("source: %08x became sender, %d sender sources", ssrc,
- sess->stats.sender_sources);
- }
-
- if (created)
- on_new_ssrc (sess, source);
-
- if (source->validated) {
- guint8 i, count;
- gboolean created;
-
- /* for validated sources, we add the CSRCs as well */
- count = gst_rtp_buffer_get_csrc_count (buffer);
-
- for (i = 0; i < count; i++) {
- guint32 csrc;
- RTPSource *csrc_src;
-
- csrc = gst_rtp_buffer_get_csrc (buffer, i);
-
- /* get source */
- csrc_src = obtain_source (sess, csrc, &created, &arrival, TRUE);
- if (!csrc_src)
- continue;
-
- if (created) {
- GST_DEBUG ("created new CSRC: %08x", csrc);
- rtp_source_set_as_csrc (csrc_src);
- if (RTP_SOURCE_IS_ACTIVE (csrc_src))
- sess->stats.active_sources++;
- on_new_ssrc (sess, csrc_src);
- }
- g_object_unref (csrc_src);
- }
- }
- g_object_unref (source);
- gst_buffer_unref (buffer);
-
- RTP_SESSION_UNLOCK (sess);
-
- return result;
-
- /* ERRORS */
-invalid_packet:
- {
- gst_buffer_unref (buffer);
- GST_DEBUG ("invalid RTP packet received");
- return GST_FLOW_OK;
- }
-ignore:
- {
- gst_buffer_unref (buffer);
- RTP_SESSION_UNLOCK (sess);
- GST_DEBUG ("ignoring RTP packet because we are leaving");
- return GST_FLOW_OK;
- }
-collision:
- {
- gst_buffer_unref (buffer);
- RTP_SESSION_UNLOCK (sess);
- GST_DEBUG ("ignoring packet because its collisioning");
- return GST_FLOW_OK;
- }
-}
-
-static void
-rtp_session_process_rb (RTPSession * sess, RTPSource * source,
- GstRTCPPacket * packet, RTPArrivalStats * arrival)
-{
- guint count, i;
-
- count = gst_rtcp_packet_get_rb_count (packet);
- for (i = 0; i < count; i++) {
- guint32 ssrc, exthighestseq, jitter, lsr, dlsr;
- guint8 fractionlost;
- gint32 packetslost;
-
- gst_rtcp_packet_get_rb (packet, i, &ssrc, &fractionlost,
- &packetslost, &exthighestseq, &jitter, &lsr, &dlsr);
-
- GST_DEBUG ("RB %d: SSRC %08x, jitter %" G_GUINT32_FORMAT, i, ssrc, jitter);
-
- if (ssrc == sess->source->ssrc) {
- /* only deal with report blocks for our session, we update the stats of
- * the sender of the RTCP message. We could also compare our stats against
- * the other sender to see if we are better or worse. */
- rtp_source_process_rb (source, arrival->time, fractionlost, packetslost,
- exthighestseq, jitter, lsr, dlsr);
-
- on_ssrc_active (sess, source);
- }
- }
-}
-
-/* A Sender report contains statistics about how the sender is doing. This
- * includes timing informataion such as the relation between RTP and NTP
- * timestamps and the number of packets/bytes it sent to us.
- *
- * In this report is also included a set of report blocks related to how this
- * sender is receiving data (in case we (or somebody else) is also sending stuff
- * to it). This info includes the packet loss, jitter and seqnum. It also
- * contains information to calculate the round trip time (LSR/DLSR).
- */
-static void
-rtp_session_process_sr (RTPSession * sess, GstRTCPPacket * packet,
- RTPArrivalStats * arrival, gboolean * do_sync)
-{
- guint32 senderssrc, rtptime, packet_count, octet_count;
- guint64 ntptime;
- RTPSource *source;
- gboolean created, prevsender;
-
- gst_rtcp_packet_sr_get_sender_info (packet, &senderssrc, &ntptime, &rtptime,
- &packet_count, &octet_count);
-
- GST_DEBUG ("got SR packet: SSRC %08x, time %" GST_TIME_FORMAT,
- senderssrc, GST_TIME_ARGS (arrival->time));
-
- source = obtain_source (sess, senderssrc, &created, arrival, FALSE);
- if (!source)
- return;
-
- /* don't try to do lip-sync for sources that sent a BYE */
- if (rtp_source_received_bye (source))
- *do_sync = FALSE;
- else
- *do_sync = TRUE;
-
- prevsender = RTP_SOURCE_IS_SENDER (source);
-
- /* first update the source */
- rtp_source_process_sr (source, arrival->time, ntptime, rtptime, packet_count,
- octet_count);
-
- if (prevsender != RTP_SOURCE_IS_SENDER (source)) {
- sess->stats.sender_sources++;
- GST_DEBUG ("source: %08x became sender, %d sender sources", senderssrc,
- sess->stats.sender_sources);
- }
-
- if (created)
- on_new_ssrc (sess, source);
-
- rtp_session_process_rb (sess, source, packet, arrival);
- g_object_unref (source);
-}
-
-/* A receiver report contains statistics about how a receiver is doing. It
- * includes stuff like packet loss, jitter and the seqnum it received last. It
- * also contains info to calculate the round trip time.
- *
- * We are only interested in how the sender of this report is doing wrt to us.
- */
-static void
-rtp_session_process_rr (RTPSession * sess, GstRTCPPacket * packet,
- RTPArrivalStats * arrival)
-{
- guint32 senderssrc;
- RTPSource *source;
- gboolean created;
-
- senderssrc = gst_rtcp_packet_rr_get_ssrc (packet);
-
- GST_DEBUG ("got RR packet: SSRC %08x", senderssrc);
-
- source = obtain_source (sess, senderssrc, &created, arrival, FALSE);
- if (!source)
- return;
-
- if (created)
- on_new_ssrc (sess, source);
-
- rtp_session_process_rb (sess, source, packet, arrival);
- g_object_unref (source);
-}
-
-/* Get SDES items and store them in the SSRC */
-static void
-rtp_session_process_sdes (RTPSession * sess, GstRTCPPacket * packet,
- RTPArrivalStats * arrival)
-{
- guint items, i, j;
- gboolean more_items, more_entries;
-
- items = gst_rtcp_packet_sdes_get_item_count (packet);
- GST_DEBUG ("got SDES packet with %d items", items);
-
- more_items = gst_rtcp_packet_sdes_first_item (packet);
- i = 0;
- while (more_items) {
- guint32 ssrc;
- gboolean changed, created;
- RTPSource *source;
-
- ssrc = gst_rtcp_packet_sdes_get_ssrc (packet);
-
- GST_DEBUG ("item %d, SSRC %08x", i, ssrc);
-
- changed = FALSE;
-
- /* find src, no probation when dealing with RTCP */
- source = obtain_source (sess, ssrc, &created, arrival, FALSE);
- if (!source)
- return;
-
- more_entries = gst_rtcp_packet_sdes_first_entry (packet);
- j = 0;
- while (more_entries) {
- GstRTCPSDESType type;
- guint8 len;
- guint8 *data;
-
- gst_rtcp_packet_sdes_get_entry (packet, &type, &len, &data);
-
- GST_DEBUG ("entry %d, type %d, len %d, data %.*s", j, type, len, len,
- data);
-
- changed |= rtp_source_set_sdes (source, type, data, len);
-
- more_entries = gst_rtcp_packet_sdes_next_entry (packet);
- j++;
- }
-
- source->validated = TRUE;
-
- if (created)
- on_new_ssrc (sess, source);
- if (changed)
- on_ssrc_sdes (sess, source);
-
- g_object_unref (source);
-
- more_items = gst_rtcp_packet_sdes_next_item (packet);
- i++;
- }
-}
-
-/* BYE is sent when a client leaves the session
- */
-static void
-rtp_session_process_bye (RTPSession * sess, GstRTCPPacket * packet,
- RTPArrivalStats * arrival)
-{
- guint count, i;
- gchar *reason;
- gboolean reconsider = FALSE;
-
- reason = gst_rtcp_packet_bye_get_reason (packet);
- GST_DEBUG ("got BYE packet (reason: %s)", GST_STR_NULL (reason));
-
- count = gst_rtcp_packet_bye_get_ssrc_count (packet);
- for (i = 0; i < count; i++) {
- guint32 ssrc;
- RTPSource *source;
- gboolean created, prevactive, prevsender;
- guint pmembers, members;
-
- ssrc = gst_rtcp_packet_bye_get_nth_ssrc (packet, i);
- GST_DEBUG ("SSRC: %08x", ssrc);
-
- /* find src and mark bye, no probation when dealing with RTCP */
- source = obtain_source (sess, ssrc, &created, arrival, FALSE);
- if (!source)
- return;
-
- /* store time for when we need to time out this source */
- source->bye_time = arrival->time;
-
- prevactive = RTP_SOURCE_IS_ACTIVE (source);
- prevsender = RTP_SOURCE_IS_SENDER (source);
-
- /* let the source handle the rest */
- rtp_source_process_bye (source, reason);
-
- pmembers = sess->stats.active_sources;
-
- if (prevactive && !RTP_SOURCE_IS_ACTIVE (source)) {
- sess->stats.active_sources--;
- GST_DEBUG ("source: %08x became inactive, %d active sources", ssrc,
- sess->stats.active_sources);
- }
- if (prevsender && !RTP_SOURCE_IS_SENDER (source)) {
- sess->stats.sender_sources--;
- GST_DEBUG ("source: %08x became non sender, %d sender sources", ssrc,
- sess->stats.sender_sources);
- }
- members = sess->stats.active_sources;
-
- if (!sess->source->received_bye && members < pmembers) {
- /* some members went away since the previous timeout estimate.
- * Perform reverse reconsideration but only when we are not scheduling a
- * BYE ourselves. */
- if (arrival->time < sess->next_rtcp_check_time) {
- GstClockTime time_remaining;
-
- time_remaining = sess->next_rtcp_check_time - arrival->time;
- sess->next_rtcp_check_time =
- gst_util_uint64_scale (time_remaining, members, pmembers);
-
- GST_DEBUG ("reverse reconsideration %" GST_TIME_FORMAT,
- GST_TIME_ARGS (sess->next_rtcp_check_time));
-
- sess->next_rtcp_check_time += arrival->time;
-
- /* mark pending reconsider. We only want to signal the reconsideration
- * once after we handled all the source in the bye packet */
- reconsider = TRUE;
- }
- }
-
- if (created)
- on_new_ssrc (sess, source);
-
- on_bye_ssrc (sess, source);
-
- g_object_unref (source);
- }
- if (reconsider) {
- RTP_SESSION_UNLOCK (sess);
- /* notify app of reconsideration */
- if (sess->callbacks.reconsider)
- sess->callbacks.reconsider (sess, sess->reconsider_user_data);
- RTP_SESSION_LOCK (sess);
- }
- g_free (reason);
-}
-
-static void
-rtp_session_process_app (RTPSession * sess, GstRTCPPacket * packet,
- RTPArrivalStats * arrival)
-{
- GST_DEBUG ("received APP");
-}
-
-/**
- * rtp_session_process_rtcp:
- * @sess: and #RTPSession
- * @buffer: an RTCP buffer
- * @current_time: the current system time
- *
- * Process an RTCP buffer in the session manager. This function takes ownership
- * of @buffer.
- *
- * Returns: a #GstFlowReturn.
- */
-GstFlowReturn
-rtp_session_process_rtcp (RTPSession * sess, GstBuffer * buffer,
- GstClockTime current_time)
-{
- GstRTCPPacket packet;
- gboolean more, is_bye = FALSE, do_sync = FALSE;
- RTPArrivalStats arrival;
- GstFlowReturn result = GST_FLOW_OK;
-
- g_return_val_if_fail (RTP_IS_SESSION (sess), GST_FLOW_ERROR);
- g_return_val_if_fail (GST_IS_BUFFER (buffer), GST_FLOW_ERROR);
-
- if (!gst_rtcp_buffer_validate (buffer))
- goto invalid_packet;
-
- GST_DEBUG ("received RTCP packet");
-
- RTP_SESSION_LOCK (sess);
- /* update arrival stats */
- update_arrival_stats (sess, &arrival, FALSE, buffer, current_time, -1, -1);
-
- if (sess->sent_bye)
- goto ignore;
-
- /* make writable, we might want to change the buffer */
- buffer = gst_buffer_make_metadata_writable (buffer);
-
- /* start processing the compound packet */
- more = gst_rtcp_buffer_get_first_packet (buffer, &packet);
- while (more) {
- GstRTCPType type;
-
- type = gst_rtcp_packet_get_type (&packet);
-
- /* when we are leaving the session, we should ignore all non-BYE messages */
- if (sess->source->received_bye && type != GST_RTCP_TYPE_BYE) {
- GST_DEBUG ("ignoring non-BYE RTCP packet because we are leaving");
- goto next;
- }
-
- switch (type) {
- case GST_RTCP_TYPE_SR:
- rtp_session_process_sr (sess, &packet, &arrival, &do_sync);
- break;
- case GST_RTCP_TYPE_RR:
- rtp_session_process_rr (sess, &packet, &arrival);
- break;
- case GST_RTCP_TYPE_SDES:
- rtp_session_process_sdes (sess, &packet, &arrival);
- break;
- case GST_RTCP_TYPE_BYE:
- is_bye = TRUE;
- /* don't try to attempt lip-sync anymore for streams with a BYE */
- do_sync = FALSE;
- rtp_session_process_bye (sess, &packet, &arrival);
- break;
- case GST_RTCP_TYPE_APP:
- rtp_session_process_app (sess, &packet, &arrival);
- break;
- default:
- GST_WARNING ("got unknown RTCP packet");
- break;
- }
- next:
- more = gst_rtcp_packet_move_to_next (&packet);
- }
-
- /* if we are scheduling a BYE, we only want to count bye packets, else we
- * count everything */
- if (sess->source->received_bye) {
- if (is_bye) {
- sess->stats.bye_members++;
- UPDATE_AVG (sess->stats.avg_rtcp_packet_size, arrival.bytes);
- }
- } else {
- /* keep track of average packet size */
- UPDATE_AVG (sess->stats.avg_rtcp_packet_size, arrival.bytes);
- }
- RTP_SESSION_UNLOCK (sess);
-
- /* notify caller of sr packets in the callback */
- if (do_sync && sess->callbacks.sync_rtcp)
- result = sess->callbacks.sync_rtcp (sess, sess->source, buffer,
- sess->sync_rtcp_user_data);
- else
- gst_buffer_unref (buffer);
-
- return result;
-
- /* ERRORS */
-invalid_packet:
- {
- GST_DEBUG ("invalid RTCP packet received");
- gst_buffer_unref (buffer);
- return GST_FLOW_OK;
- }
-ignore:
- {
- gst_buffer_unref (buffer);
- RTP_SESSION_UNLOCK (sess);
- GST_DEBUG ("ignoring RTP packet because we left");
- return GST_FLOW_OK;
- }
-}
-
-/**
- * rtp_session_send_rtp:
- * @sess: an #RTPSession
- * @data: pointer to either an RTP buffer or a list of RTP buffers
- * @current_time: the current system time
- * @ntpnstime: the NTP time in nanoseconds of when this buffer was captured.
- * This is the buffer timestamp converted to NTP time.
- *
- * Send the RTP buffer in the session manager. This function takes ownership of
- * @buffer.
- *
- * Returns: a #GstFlowReturn.
- */
-GstFlowReturn
-rtp_session_send_rtp (RTPSession * sess, gpointer data, gboolean is_list,
- GstClockTime current_time, guint64 ntpnstime)
-{
- GstFlowReturn result;
- RTPSource *source;
- gboolean prevsender;
- gboolean valid_packet;
-
- g_return_val_if_fail (RTP_IS_SESSION (sess), GST_FLOW_ERROR);
- g_return_val_if_fail (is_list || GST_IS_BUFFER (data), GST_FLOW_ERROR);
-
- if (is_list) {
- valid_packet = gst_rtp_buffer_list_validate (GST_BUFFER_LIST_CAST (data));
- } else {
- valid_packet = gst_rtp_buffer_validate (GST_BUFFER_CAST (data));
- }
-
- if (!valid_packet)
- goto invalid_packet;
-
- GST_LOG ("received RTP %s for sending", is_list ? "list" : "packet");
-
- RTP_SESSION_LOCK (sess);
- source = sess->source;
-
- /* update last activity */
- source->last_rtp_activity = current_time;
-
- prevsender = RTP_SOURCE_IS_SENDER (source);
-
- /* we use our own source to send */
- result = rtp_source_send_rtp (source, data, is_list, ntpnstime);
-
- if (RTP_SOURCE_IS_SENDER (source) && !prevsender)
- sess->stats.sender_sources++;
- RTP_SESSION_UNLOCK (sess);
-
- return result;
-
- /* ERRORS */
-invalid_packet:
- {
- gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
- GST_DEBUG ("invalid RTP packet received");
- return GST_FLOW_OK;
- }
-}
-
-static GstClockTime
-calculate_rtcp_interval (RTPSession * sess, gboolean deterministic,
- gboolean first)
-{
- GstClockTime result;
-
- if (sess->source->received_bye) {
- result = rtp_stats_calculate_bye_interval (&sess->stats);
- } else {
- result = rtp_stats_calculate_rtcp_interval (&sess->stats,
- RTP_SOURCE_IS_SENDER (sess->source), first);
- }
-
- GST_DEBUG ("next deterministic interval: %" GST_TIME_FORMAT ", first %d",
- GST_TIME_ARGS (result), first);
-
- if (!deterministic)
- result = rtp_stats_add_rtcp_jitter (&sess->stats, result);
-
- GST_DEBUG ("next interval: %" GST_TIME_FORMAT, GST_TIME_ARGS (result));
-
- return result;
-}
-
-/* Stop the current @sess and schedule a BYE message for the other members.
- * One must have the session lock to call this function
- */
-static GstFlowReturn
-rtp_session_schedule_bye_locked (RTPSession * sess, const gchar * reason,
- GstClockTime current_time)
-{
- GstFlowReturn result = GST_FLOW_OK;
- RTPSource *source;
- GstClockTime interval;
-
- g_return_val_if_fail (RTP_IS_SESSION (sess), GST_FLOW_ERROR);
-
- source = sess->source;
-
- /* ignore more BYEs */
- if (source->received_bye)
- goto done;
-
- /* we have BYE now */
- source->received_bye = TRUE;
- /* at least one member wants to send a BYE */
- g_free (sess->bye_reason);
- sess->bye_reason = g_strdup (reason);
- sess->stats.avg_rtcp_packet_size = 100;
- sess->stats.bye_members = 1;
- sess->first_rtcp = TRUE;
- sess->sent_bye = FALSE;
-
- /* reschedule transmission */
- sess->last_rtcp_send_time = current_time;
- interval = calculate_rtcp_interval (sess, FALSE, TRUE);
- sess->next_rtcp_check_time = current_time + interval;
-
- GST_DEBUG ("Schedule BYE for %" GST_TIME_FORMAT ", %" GST_TIME_FORMAT,
- GST_TIME_ARGS (interval), GST_TIME_ARGS (sess->next_rtcp_check_time));
-
- RTP_SESSION_UNLOCK (sess);
- /* notify app of reconsideration */
- if (sess->callbacks.reconsider)
- sess->callbacks.reconsider (sess, sess->reconsider_user_data);
- RTP_SESSION_LOCK (sess);
-done:
-
- return result;
-}
-
-/**
- * rtp_session_schedule_bye:
- * @sess: an #RTPSession
- * @reason: a reason or NULL
- * @current_time: the current system time
- *
- * Stop the current @sess and schedule a BYE message for the other members.
- *
- * Returns: a #GstFlowReturn.
- */
-GstFlowReturn
-rtp_session_schedule_bye (RTPSession * sess, const gchar * reason,
- GstClockTime current_time)
-{
- GstFlowReturn result = GST_FLOW_OK;
-
- g_return_val_if_fail (RTP_IS_SESSION (sess), GST_FLOW_ERROR);
-
- RTP_SESSION_LOCK (sess);
- result = rtp_session_schedule_bye_locked (sess, reason, current_time);
- RTP_SESSION_UNLOCK (sess);
-
- return result;
-}
-
-/**
- * rtp_session_next_timeout:
- * @sess: an #RTPSession
- * @current_time: the current system time
- *
- * Get the next time we should perform session maintenance tasks.
- *
- * Returns: a time when rtp_session_on_timeout() should be called with the
- * current system time.
- */
-GstClockTime
-rtp_session_next_timeout (RTPSession * sess, GstClockTime current_time)
-{
- GstClockTime result;
-
- g_return_val_if_fail (RTP_IS_SESSION (sess), GST_FLOW_ERROR);
-
- RTP_SESSION_LOCK (sess);
-
- result = sess->next_rtcp_check_time;
-
- GST_DEBUG ("current time: %" GST_TIME_FORMAT ", next :%" GST_TIME_FORMAT,
- GST_TIME_ARGS (current_time), GST_TIME_ARGS (result));
-
- if (result < current_time) {
- GST_DEBUG ("take current time as base");
- /* our previous check time expired, start counting from the current time
- * again. */
- result = current_time;
- }
-
- if (sess->source->received_bye) {
- if (sess->sent_bye) {
- GST_DEBUG ("we sent BYE already");
- result = GST_CLOCK_TIME_NONE;
- } else if (sess->stats.active_sources >= 50) {
- GST_DEBUG ("reconsider BYE, more than 50 sources");
- /* reconsider BYE if members >= 50 */
- result += calculate_rtcp_interval (sess, FALSE, TRUE);
- }
- } else {
- if (sess->first_rtcp) {
- GST_DEBUG ("first RTCP packet");
- /* we are called for the first time */
- result += calculate_rtcp_interval (sess, FALSE, TRUE);
- } else if (sess->next_rtcp_check_time < current_time) {
- GST_DEBUG ("old check time expired, getting new timeout");
- /* get a new timeout when we need to */
- result += calculate_rtcp_interval (sess, FALSE, FALSE);
- }
- }
- sess->next_rtcp_check_time = result;
-
- GST_DEBUG ("next timeout: %" GST_TIME_FORMAT, GST_TIME_ARGS (result));
- RTP_SESSION_UNLOCK (sess);
-
- return result;
-}
-
-typedef struct
-{
- RTPSession *sess;
- GstBuffer *rtcp;
- GstClockTime current_time;
- guint64 ntpnstime;
- GstClockTime interval;
- GstRTCPPacket packet;
- gboolean is_bye;
- gboolean has_sdes;
-} ReportData;
-
-static void
-session_start_rtcp (RTPSession * sess, ReportData * data)
-{
- GstRTCPPacket *packet = &data->packet;
- RTPSource *own = sess->source;
-
- data->rtcp = gst_rtcp_buffer_new (sess->mtu);
-
- if (RTP_SOURCE_IS_SENDER (own)) {
- guint64 ntptime;
- guint32 rtptime;
- guint32 packet_count, octet_count;
-
- /* we are a sender, create SR */
- GST_DEBUG ("create SR for SSRC %08x", own->ssrc);
- gst_rtcp_buffer_add_packet (data->rtcp, GST_RTCP_TYPE_SR, packet);
-
- /* get latest stats */
- rtp_source_get_new_sr (own, data->ntpnstime, &ntptime, &rtptime,
- &packet_count, &octet_count);
- /* store stats */
- rtp_source_process_sr (own, data->current_time, ntptime, rtptime,
- packet_count, octet_count);
-
- /* fill in sender report info */
- gst_rtcp_packet_sr_set_sender_info (packet, own->ssrc,
- ntptime, rtptime, packet_count, octet_count);
- } else {
- /* we are only receiver, create RR */
- GST_DEBUG ("create RR for SSRC %08x", own->ssrc);
- gst_rtcp_buffer_add_packet (data->rtcp, GST_RTCP_TYPE_RR, packet);
- gst_rtcp_packet_rr_set_ssrc (packet, own->ssrc);
- }
-}
-
-/* construct a Sender or Receiver Report */
-static void
-session_report_blocks (const gchar * key, RTPSource * source, ReportData * data)
-{
- RTPSession *sess = data->sess;
- GstRTCPPacket *packet = &data->packet;
-
- /* create a new buffer if needed */
- if (data->rtcp == NULL) {
- session_start_rtcp (sess, data);
- }
- if (gst_rtcp_packet_get_rb_count (packet) < GST_RTCP_MAX_RB_COUNT) {
- /* only report about other sender sources */
- if (source != sess->source && RTP_SOURCE_IS_SENDER (source)) {
- guint8 fractionlost;
- gint32 packetslost;
- guint32 exthighestseq, jitter;
- guint32 lsr, dlsr;
-
- /* get new stats */
- rtp_source_get_new_rb (source, data->current_time, &fractionlost,
- &packetslost, &exthighestseq, &jitter, &lsr, &dlsr);
-
- /* packet is not yet filled, add report block for this source. */
- gst_rtcp_packet_add_rb (packet, source->ssrc, fractionlost, packetslost,
- exthighestseq, jitter, lsr, dlsr);
- }
- }
-}
-
-/* perform cleanup of sources that timed out */
-static gboolean
-session_cleanup (const gchar * key, RTPSource * source, ReportData * data)
-{
- gboolean remove = FALSE;
- gboolean byetimeout = FALSE;
- gboolean sendertimeout = FALSE;
- gboolean is_sender, is_active;
- RTPSession *sess = data->sess;
- GstClockTime interval;
-
- is_sender = RTP_SOURCE_IS_SENDER (source);
- is_active = RTP_SOURCE_IS_ACTIVE (source);
-
- /* check for our own source, we don't want to delete our own source. */
- if (!(source == sess->source)) {
- if (source->received_bye) {
- /* if we received a BYE from the source, remove the source after some
- * time. */
- if (data->current_time > source->bye_time &&
- data->current_time - source->bye_time > sess->stats.bye_timeout) {
- GST_DEBUG ("removing BYE source %08x", source->ssrc);
- remove = TRUE;
- byetimeout = TRUE;
- }
- }
- /* sources that were inactive for more than 5 times the deterministic reporting
- * interval get timed out. the min timeout is 5 seconds. */
- if (data->current_time > source->last_activity) {
- interval = MAX (data->interval * 5, 5 * GST_SECOND);
- if (data->current_time - source->last_activity > interval) {
- GST_DEBUG ("removing timeout source %08x, last %" GST_TIME_FORMAT,
- source->ssrc, GST_TIME_ARGS (source->last_activity));
- remove = TRUE;
- }
- }
- }
-
- /* senders that did not send for a long time become a receiver, this also
- * holds for our own source. */
- if (is_sender) {
- if (data->current_time > source->last_rtp_activity) {
- interval = MAX (data->interval * 2, 5 * GST_SECOND);
- if (data->current_time - source->last_rtp_activity > interval) {
- GST_DEBUG ("sender source %08x timed out and became receiver, last %"
- GST_TIME_FORMAT, source->ssrc,
- GST_TIME_ARGS (source->last_rtp_activity));
- source->is_sender = FALSE;
- sess->stats.sender_sources--;
- sendertimeout = TRUE;
- }
- }
- }
-
- if (remove) {
- sess->total_sources--;
- if (is_sender)
- sess->stats.sender_sources--;
- if (is_active)
- sess->stats.active_sources--;
-
- if (byetimeout)
- on_bye_timeout (sess, source);
- else
- on_timeout (sess, source);
- } else {
- if (sendertimeout)
- on_sender_timeout (sess, source);
- }
- return remove;
-}
-
-static void
-session_sdes (RTPSession * sess, ReportData * data)
-{
- GstRTCPPacket *packet = &data->packet;
- guint8 *sdes_data;
- guint sdes_len;
-
- /* add SDES packet */
- gst_rtcp_buffer_add_packet (data->rtcp, GST_RTCP_TYPE_SDES, packet);
-
- gst_rtcp_packet_sdes_add_item (packet, sess->source->ssrc);
-
- rtp_source_get_sdes (sess->source, GST_RTCP_SDES_CNAME, &sdes_data,
- &sdes_len);
- gst_rtcp_packet_sdes_add_entry (packet, GST_RTCP_SDES_CNAME, sdes_len,
- sdes_data);
-
- /* other SDES items must only be added at regular intervals and only when the
- * user requests to since it might be a privacy problem */
-#if 0
- gst_rtcp_packet_sdes_add_entry (&packet, GST_RTCP_SDES_NAME,
- strlen (sess->name), (guint8 *) sess->name);
- gst_rtcp_packet_sdes_add_entry (&packet, GST_RTCP_SDES_TOOL,
- strlen (sess->tool), (guint8 *) sess->tool);
-#endif
-
- data->has_sdes = TRUE;
-}
-
-/* schedule a BYE packet */
-static void
-session_bye (RTPSession * sess, ReportData * data)
-{
- GstRTCPPacket *packet = &data->packet;
-
- /* open packet */
- session_start_rtcp (sess, data);
-
- /* add SDES */
- session_sdes (sess, data);
-
- /* add a BYE packet */
- gst_rtcp_buffer_add_packet (data->rtcp, GST_RTCP_TYPE_BYE, packet);
- gst_rtcp_packet_bye_add_ssrc (packet, sess->source->ssrc);
- if (sess->bye_reason)
- gst_rtcp_packet_bye_set_reason (packet, sess->bye_reason);
-
- /* we have a BYE packet now */
- data->is_bye = TRUE;
-}
-
-static gboolean
-is_rtcp_time (RTPSession * sess, GstClockTime current_time, ReportData * data)
-{
- GstClockTime new_send_time, elapsed;
- gboolean result;
-
- /* no need to check yet */
- if (sess->next_rtcp_check_time > current_time) {
- GST_DEBUG ("no check time yet, next %" GST_TIME_FORMAT " > now %"
- GST_TIME_FORMAT, GST_TIME_ARGS (sess->next_rtcp_check_time),
- GST_TIME_ARGS (current_time));
- return FALSE;
- }
-
- /* get elapsed time since we last reported */
- elapsed = current_time - sess->last_rtcp_send_time;
-
- /* perform forward reconsideration */
- new_send_time = rtp_stats_add_rtcp_jitter (&sess->stats, data->interval);
-
- GST_DEBUG ("forward reconsideration %" GST_TIME_FORMAT ", elapsed %"
- GST_TIME_FORMAT, GST_TIME_ARGS (new_send_time), GST_TIME_ARGS (elapsed));
-
- new_send_time += sess->last_rtcp_send_time;
-
- /* check if reconsideration */
- if (current_time < new_send_time) {
- GST_DEBUG ("reconsider RTCP for %" GST_TIME_FORMAT,
- GST_TIME_ARGS (new_send_time));
- result = FALSE;
- /* store new check time */
- sess->next_rtcp_check_time = new_send_time;
- } else {
- result = TRUE;
- new_send_time = calculate_rtcp_interval (sess, FALSE, FALSE);
-
- GST_DEBUG ("can send RTCP now, next interval %" GST_TIME_FORMAT,
- GST_TIME_ARGS (new_send_time));
- sess->next_rtcp_check_time = current_time + new_send_time;
- }
- return result;
-}
-
-/**
- * rtp_session_on_timeout:
- * @sess: an #RTPSession
- * @current_time: the current system time
- * @ntpnstime: the current NTP time in nanoseconds
- *
- * Perform maintenance actions after the timeout obtained with
- * rtp_session_next_timeout() expired.
- *
- * This function will perform timeouts of receivers and senders, send a BYE
- * packet or generate RTCP packets with current session stats.
- *
- * This function can call the #RTPSessionSendRTCP callback, possibly multiple
- * times, for each packet that should be processed.
- *
- * Returns: a #GstFlowReturn.
- */
-GstFlowReturn
-rtp_session_on_timeout (RTPSession * sess, GstClockTime current_time,
- guint64 ntpnstime)
-{
- GstFlowReturn result = GST_FLOW_OK;
- GList *item;
- ReportData data;
- RTPSource *own;
- gboolean notify = FALSE;
-
- g_return_val_if_fail (RTP_IS_SESSION (sess), GST_FLOW_ERROR);
-
- GST_DEBUG ("reporting at %" GST_TIME_FORMAT ", NTP time %" GST_TIME_FORMAT,
- GST_TIME_ARGS (current_time), GST_TIME_ARGS (ntpnstime));
-
- data.sess = sess;
- data.rtcp = NULL;
- data.current_time = current_time;
- data.ntpnstime = ntpnstime;
- data.is_bye = FALSE;
- data.has_sdes = FALSE;
-
- own = sess->source;
-
- RTP_SESSION_LOCK (sess);
- /* get a new interval, we need this for various cleanups etc */
- data.interval = calculate_rtcp_interval (sess, TRUE, sess->first_rtcp);
-
- /* first perform cleanups */
- g_hash_table_foreach_remove (sess->ssrcs[sess->mask_idx],
- (GHRFunc) session_cleanup, &data);
-
- /* see if we need to generate SR or RR packets */
- if (is_rtcp_time (sess, current_time, &data)) {
- if (own->received_bye) {
- /* generate BYE instead */
- GST_DEBUG ("generating BYE message");
- session_bye (sess, &data);
- sess->sent_bye = TRUE;
- } else {
- /* loop over all known sources and do something */
- g_hash_table_foreach (sess->ssrcs[sess->mask_idx],
- (GHFunc) session_report_blocks, &data);
- }
- }
-
- if (data.rtcp) {
- guint size;
-
- /* we keep track of the last report time in order to timeout inactive
- * receivers or senders */
- sess->last_rtcp_send_time = data.current_time;
- sess->first_rtcp = FALSE;
-
- /* add SDES for this source when not already added */
- if (!data.has_sdes)
- session_sdes (sess, &data);
-
- /* update average RTCP size before sending */
- size = GST_BUFFER_SIZE (data.rtcp) + sess->header_len;
- UPDATE_AVG (sess->stats.avg_rtcp_packet_size, size);
- }
-
- /* check for outdated collisions */
- GST_DEBUG ("checking collision list");
- item = g_list_first (sess->conflicting_addresses);
- while (item) {
- RTPConflictingAddress *known_conflict = item->data;
- GList *next_item = g_list_next (item);
-
- if (known_conflict->time < current_time - (data.interval *
- RTCP_INTERVAL_COLLISION_TIMEOUT)) {
- sess->conflicting_addresses =
- g_list_delete_link (sess->conflicting_addresses, item);
- GST_DEBUG ("collision %p timed out", known_conflict);
- g_free (known_conflict);
- }
- item = next_item;
- }
-
- if (sess->change_ssrc) {
- GST_DEBUG ("need to change our SSRC (%08x)", own->ssrc);
- g_hash_table_steal (sess->ssrcs[sess->mask_idx],
- GINT_TO_POINTER (own->ssrc));
-
- own->ssrc = rtp_session_create_new_ssrc (sess);
- rtp_source_reset (own);
-
- g_hash_table_insert (sess->ssrcs[sess->mask_idx],
- GINT_TO_POINTER (own->ssrc), own);
-
- g_free (sess->bye_reason);
- sess->bye_reason = NULL;
- sess->sent_bye = FALSE;
- sess->change_ssrc = FALSE;
- notify = TRUE;
- GST_DEBUG ("changed our SSRC to %08x", own->ssrc);
- }
- RTP_SESSION_UNLOCK (sess);
-
- if (notify)
- g_object_notify (G_OBJECT (sess), "internal-ssrc");
-
- /* push out the RTCP packet */
- if (data.rtcp) {
- /* close the RTCP packet */
- gst_rtcp_buffer_end (data.rtcp);
-
- GST_DEBUG ("sending packet");
- if (sess->callbacks.send_rtcp)
- result = sess->callbacks.send_rtcp (sess, own, data.rtcp,
- sess->sent_bye, sess->send_rtcp_user_data);
- else {
- GST_DEBUG ("freeing packet");
- gst_buffer_unref (data.rtcp);
- }
- }
-
- return result;
-}
+++ /dev/null
-/* GStreamer
- * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#ifndef __RTP_SESSION_H__
-#define __RTP_SESSION_H__
-
-#include <gst/gst.h>
-#include <gst/netbuffer/gstnetbuffer.h>
-
-#include "rtpsource.h"
-
-typedef struct _RTPSession RTPSession;
-typedef struct _RTPSessionClass RTPSessionClass;
-
-#define RTP_TYPE_SESSION (rtp_session_get_type())
-#define RTP_SESSION(sess) (G_TYPE_CHECK_INSTANCE_CAST((sess),RTP_TYPE_SESSION,RTPSession))
-#define RTP_SESSION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),RTP_TYPE_SESSION,RTPSessionClass))
-#define RTP_IS_SESSION(sess) (G_TYPE_CHECK_INSTANCE_TYPE((sess),RTP_TYPE_SESSION))
-#define RTP_IS_SESSION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),RTP_TYPE_SESSION))
-#define RTP_SESSION_CAST(sess) ((RTPSession *)(sess))
-
-#define RTP_SESSION_LOCK(sess) (g_mutex_lock ((sess)->lock))
-#define RTP_SESSION_UNLOCK(sess) (g_mutex_unlock ((sess)->lock))
-
-/**
- * RTPSessionProcessRTP:
- * @sess: an #RTPSession
- * @src: the #RTPSource
- * @buffer: the RTP buffer ready for processing
- * @user_data: user data specified when registering
- *
- * This callback will be called when @sess has @buffer ready for further
- * processing. Processing the buffer typically includes decoding and displaying
- * the buffer.
- *
- * Returns: a #GstFlowReturn.
- */
-typedef GstFlowReturn (*RTPSessionProcessRTP) (RTPSession *sess, RTPSource *src, GstBuffer *buffer, gpointer user_data);
-
-/**
- * RTPSessionSendRTP:
- * @sess: an #RTPSession
- * @src: the #RTPSource
- * @buffer: the RTP buffer ready for sending
- * @user_data: user data specified when registering
- *
- * This callback will be called when @sess has @buffer ready for sending to
- * all listening participants in this session.
- *
- * Returns: a #GstFlowReturn.
- */
-typedef GstFlowReturn (*RTPSessionSendRTP) (RTPSession *sess, RTPSource *src, gpointer data, gpointer user_data);
-
-/**
- * RTPSessionSendRTCP:
- * @sess: an #RTPSession
- * @src: the #RTPSource
- * @buffer: the RTCP buffer ready for sending
- * @eos: if an EOS event should be pushed
- * @user_data: user data specified when registering
- *
- * This callback will be called when @sess has @buffer ready for sending to
- * all listening participants in this session.
- *
- * Returns: a #GstFlowReturn.
- */
-typedef GstFlowReturn (*RTPSessionSendRTCP) (RTPSession *sess, RTPSource *src, GstBuffer *buffer,
- gboolean eos, gpointer user_data);
-
-/**
- * RTPSessionSyncRTCP:
- * @sess: an #RTPSession
- * @src: the #RTPSource
- * @buffer: the RTCP buffer ready for synchronisation
- * @user_data: user data specified when registering
- *
- * This callback will be called when @sess has an SR @buffer ready for doing
- * synchronisation between streams.
- *
- * Returns: a #GstFlowReturn.
- */
-typedef GstFlowReturn (*RTPSessionSyncRTCP) (RTPSession *sess, RTPSource *src, GstBuffer *buffer, gpointer user_data);
-
-/**
- * RTPSessionClockRate:
- * @sess: an #RTPSession
- * @payload: the payload
- * @user_data: user data specified when registering
- *
- * This callback will be called when @sess needs the clock-rate of @payload.
- *
- * Returns: the clock-rate of @pt.
- */
-typedef gint (*RTPSessionClockRate) (RTPSession *sess, guint8 payload, gpointer user_data);
-
-/**
- * RTPSessionReconsider:
- * @sess: an #RTPSession
- * @user_data: user data specified when registering
- *
- * This callback will be called when @sess needs to cancel the current timeout.
- * The currently running timeout should be canceled and a new reporting interval
- * should be requested from @sess.
- */
-typedef void (*RTPSessionReconsider) (RTPSession *sess, gpointer user_data);
-
-/**
- * RTPSessionCallbacks:
- * @RTPSessionProcessRTP: callback to process RTP packets
- * @RTPSessionSendRTP: callback for sending RTP packets
- * @RTPSessionSendRTCP: callback for sending RTCP packets
- * @RTPSessionSyncRTCP: callback for handling SR packets
- * @RTPSessionReconsider: callback for reconsidering the timeout
- *
- * These callbacks can be installed on the session manager to get notification
- * when RTP and RTCP packets are ready for further processing. These callbacks
- * are not implemented with signals for performance reasons.
- */
-typedef struct {
- RTPSessionProcessRTP process_rtp;
- RTPSessionSendRTP send_rtp;
- RTPSessionSyncRTCP sync_rtcp;
- RTPSessionSendRTCP send_rtcp;
- RTPSessionClockRate clock_rate;
- RTPSessionReconsider reconsider;
-} RTPSessionCallbacks;
-
-/**
- * RTPConflictingAddress:
- * @address: #GstNetAddress which conflicted
- * @last_conflict_time: time when the last conflict was seen
- *
- * This structure is used to account for addresses that have conflicted to find
- * loops.
- */
-typedef struct {
- GstNetAddress address;
- GstClockTime time;
-} RTPConflictingAddress;
-
-/**
- * RTPSession:
- * @lock: lock to protect the session
- * @source: the source of this session
- * @ssrcs: Hashtable of sources indexed by SSRC
- * @cnames: Hashtable of sources indexed by CNAME
- * @num_sources: the number of sources
- * @activecount: the number of active sources
- * @callbacks: callbacks
- * @user_data: user data passed in callbacks
- * @stats: session statistics
- * @conflicting_addresses: GList of conflicting addresses
- *
- * The RTP session manager object
- */
-struct _RTPSession {
- GObject object;
-
- GMutex *lock;
-
- guint header_len;
- guint mtu;
-
- RTPSource *source;
-
- /* for sender/receiver counting */
- guint32 key;
- guint32 mask_idx;
- guint32 mask;
- GHashTable *ssrcs[32];
- GHashTable *cnames;
- guint total_sources;
-
- GstClockTime next_rtcp_check_time;
- GstClockTime last_rtcp_send_time;
- gboolean first_rtcp;
-
- gchar *bye_reason;
- gboolean sent_bye;
-
- RTPSessionCallbacks callbacks;
- gpointer process_rtp_user_data;
- gpointer send_rtp_user_data;
- gpointer send_rtcp_user_data;
- gpointer sync_rtcp_user_data;
- gpointer clock_rate_user_data;
- gpointer reconsider_user_data;
-
- RTPSessionStats stats;
-
- GList *conflicting_addresses;
- gboolean change_ssrc;
-};
-
-/**
- * RTPSessionClass:
- * @on_new_ssrc: emited when a new source is found
- * @on_bye_ssrc: emited when a source is gone
- *
- * The session class.
- */
-struct _RTPSessionClass {
- GObjectClass parent_class;
-
- /* action signals */
- RTPSource* (*get_source_by_ssrc) (RTPSession *sess, guint32 ssrc);
-
- /* signals */
- void (*on_new_ssrc) (RTPSession *sess, RTPSource *source);
- void (*on_ssrc_collision) (RTPSession *sess, RTPSource *source);
- void (*on_ssrc_validated) (RTPSession *sess, RTPSource *source);
- void (*on_ssrc_active) (RTPSession *sess, RTPSource *source);
- void (*on_ssrc_sdes) (RTPSession *sess, RTPSource *source);
- void (*on_bye_ssrc) (RTPSession *sess, RTPSource *source);
- void (*on_bye_timeout) (RTPSession *sess, RTPSource *source);
- void (*on_timeout) (RTPSession *sess, RTPSource *source);
- void (*on_sender_timeout) (RTPSession *sess, RTPSource *source);
-};
-
-GType rtp_session_get_type (void);
-
-/* create and configure */
-RTPSession* rtp_session_new (void);
-void rtp_session_set_callbacks (RTPSession *sess,
- RTPSessionCallbacks *callbacks,
- gpointer user_data);
-void rtp_session_set_process_rtp_callback (RTPSession * sess,
- RTPSessionProcessRTP callback,
- gpointer user_data);
-void rtp_session_set_send_rtp_callback (RTPSession * sess,
- RTPSessionSendRTP callback,
- gpointer user_data);
-void rtp_session_set_send_rtcp_callback (RTPSession * sess,
- RTPSessionSendRTCP callback,
- gpointer user_data);
-void rtp_session_set_sync_rtcp_callback (RTPSession * sess,
- RTPSessionSyncRTCP callback,
- gpointer user_data);
-void rtp_session_set_clock_rate_callback (RTPSession * sess,
- RTPSessionClockRate callback,
- gpointer user_data);
-void rtp_session_set_reconsider_callback (RTPSession * sess,
- RTPSessionReconsider callback,
- gpointer user_data);
-void rtp_session_set_bandwidth (RTPSession *sess, gdouble bandwidth);
-gdouble rtp_session_get_bandwidth (RTPSession *sess);
-void rtp_session_set_rtcp_fraction (RTPSession *sess, gdouble fraction);
-gdouble rtp_session_get_rtcp_fraction (RTPSession *sess);
-
-gboolean rtp_session_set_sdes_string (RTPSession *sess, GstRTCPSDESType type,
- const gchar *cname);
-gchar* rtp_session_get_sdes_string (RTPSession *sess, GstRTCPSDESType type);
-
-GstStructure * rtp_session_get_sdes_struct (RTPSession *sess);
-void rtp_session_set_sdes_struct (RTPSession *sess, const GstStructure *sdes);
-
-/* handling sources */
-RTPSource* rtp_session_get_internal_source (RTPSession *sess);
-
-void rtp_session_set_internal_ssrc (RTPSession *sess, guint32 ssrc);
-guint32 rtp_session_get_internal_ssrc (RTPSession *sess);
-
-gboolean rtp_session_add_source (RTPSession *sess, RTPSource *src);
-guint rtp_session_get_num_sources (RTPSession *sess);
-guint rtp_session_get_num_active_sources (RTPSession *sess);
-RTPSource* rtp_session_get_source_by_ssrc (RTPSession *sess, guint32 ssrc);
-RTPSource* rtp_session_get_source_by_cname (RTPSession *sess, const gchar *cname);
-RTPSource* rtp_session_create_source (RTPSession *sess);
-
-/* processing packets from receivers */
-GstFlowReturn rtp_session_process_rtp (RTPSession *sess, GstBuffer *buffer,
- GstClockTime current_time,
- GstClockTime running_time, guint64 ntpnstime);
-GstFlowReturn rtp_session_process_rtcp (RTPSession *sess, GstBuffer *buffer,
- GstClockTime current_time);
-
-/* processing packets for sending */
-GstFlowReturn rtp_session_send_rtp (RTPSession *sess, gpointer data, gboolean is_list,
- GstClockTime current_time, guint64 ntpnstime);
-
-/* stopping the session */
-GstFlowReturn rtp_session_schedule_bye (RTPSession *sess, const gchar *reason,
- GstClockTime current_time);
-
-/* get interval for next RTCP interval */
-GstClockTime rtp_session_next_timeout (RTPSession *sess, GstClockTime current_time);
-GstFlowReturn rtp_session_on_timeout (RTPSession *sess, GstClockTime current_time,
- guint64 ntpnstime);
-
-#endif /* __RTP_SESSION_H__ */
+++ /dev/null
-/* GStreamer
- * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-#include <string.h>
-
-#include <gst/rtp/gstrtpbuffer.h>
-#include <gst/rtp/gstrtcpbuffer.h>
-
-#include "rtpsource.h"
-
-GST_DEBUG_CATEGORY_STATIC (rtp_source_debug);
-#define GST_CAT_DEFAULT rtp_source_debug
-
-#define RTP_MAX_PROBATION_LEN 32
-
-/* signals and args */
-enum
-{
- LAST_SIGNAL
-};
-
-#define DEFAULT_SSRC 0
-#define DEFAULT_IS_CSRC FALSE
-#define DEFAULT_IS_VALIDATED FALSE
-#define DEFAULT_IS_SENDER FALSE
-#define DEFAULT_SDES NULL
-
-enum
-{
- PROP_0,
- PROP_SSRC,
- PROP_IS_CSRC,
- PROP_IS_VALIDATED,
- PROP_IS_SENDER,
- PROP_SDES,
- PROP_STATS,
- PROP_LAST
-};
-
-/* GObject vmethods */
-static void rtp_source_finalize (GObject * object);
-static void rtp_source_set_property (GObject * object, guint prop_id,
- const GValue * value, GParamSpec * pspec);
-static void rtp_source_get_property (GObject * object, guint prop_id,
- GValue * value, GParamSpec * pspec);
-
-/* static guint rtp_source_signals[LAST_SIGNAL] = { 0 }; */
-
-G_DEFINE_TYPE (RTPSource, rtp_source, G_TYPE_OBJECT);
-
-static void
-rtp_source_class_init (RTPSourceClass * klass)
-{
- GObjectClass *gobject_class;
-
- gobject_class = (GObjectClass *) klass;
-
- gobject_class->finalize = rtp_source_finalize;
-
- gobject_class->set_property = rtp_source_set_property;
- gobject_class->get_property = rtp_source_get_property;
-
- g_object_class_install_property (gobject_class, PROP_SSRC,
- g_param_spec_uint ("ssrc", "SSRC",
- "The SSRC of this source", 0, G_MAXUINT, DEFAULT_SSRC,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
-
- g_object_class_install_property (gobject_class, PROP_IS_CSRC,
- g_param_spec_boolean ("is-csrc", "Is CSRC",
- "If this SSRC is acting as a contributing source",
- DEFAULT_IS_CSRC, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
-
- g_object_class_install_property (gobject_class, PROP_IS_VALIDATED,
- g_param_spec_boolean ("is-validated", "Is Validated",
- "If this SSRC is validated", DEFAULT_IS_VALIDATED,
- G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
-
- g_object_class_install_property (gobject_class, PROP_IS_SENDER,
- g_param_spec_boolean ("is-sender", "Is Sender",
- "If this SSRC is a sender", DEFAULT_IS_SENDER,
- G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
-
- /**
- * RTPSource::sdes
- *
- * The current SDES items of the source. Returns a structure with the
- * following fields:
- *
- * 'cname' G_TYPE_STRING : The canonical name
- * 'name' G_TYPE_STRING : The user name
- * 'email' G_TYPE_STRING : The user's electronic mail address
- * 'phone' G_TYPE_STRING : The user's phone number
- * 'location' G_TYPE_STRING : The geographic user location
- * 'tool' G_TYPE_STRING : The name of application or tool
- * 'note' G_TYPE_STRING : A notice about the source
- */
- g_object_class_install_property (gobject_class, PROP_SDES,
- g_param_spec_boxed ("sdes", "SDES",
- "The SDES information for this source",
- GST_TYPE_STRUCTURE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
-
- /**
- * RTPSource::stats
- *
- * The statistics of the source. This property returns a GstStructure with
- * name application/x-rtp-source-stats with the following fields:
- *
- */
- g_object_class_install_property (gobject_class, PROP_STATS,
- g_param_spec_boxed ("stats", "Stats",
- "The stats of this source", GST_TYPE_STRUCTURE,
- G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
-
- GST_DEBUG_CATEGORY_INIT (rtp_source_debug, "rtpsource", 0, "RTP Source");
-}
-
-/**
- * rtp_source_reset:
- * @src: an #RTPSource
- *
- * Reset the stats of @src.
- */
-void
-rtp_source_reset (RTPSource * src)
-{
- src->received_bye = FALSE;
-
- src->stats.cycles = -1;
- src->stats.jitter = 0;
- src->stats.transit = -1;
- src->stats.curr_sr = 0;
- src->stats.curr_rr = 0;
-}
-
-static void
-rtp_source_init (RTPSource * src)
-{
- /* sources are initialy on probation until we receive enough valid RTP
- * packets or a valid RTCP packet */
- src->validated = FALSE;
- src->internal = FALSE;
- src->probation = RTP_DEFAULT_PROBATION;
-
- src->payload = -1;
- src->clock_rate = -1;
- src->packets = g_queue_new ();
- src->seqnum_base = -1;
- src->last_rtptime = -1;
-
- rtp_source_reset (src);
-}
-
-static void
-rtp_source_finalize (GObject * object)
-{
- RTPSource *src;
- GstBuffer *buffer;
- gint i;
-
- src = RTP_SOURCE_CAST (object);
-
- while ((buffer = g_queue_pop_head (src->packets)))
- gst_buffer_unref (buffer);
- g_queue_free (src->packets);
-
- for (i = 0; i < 9; i++)
- g_free (src->sdes[i]);
-
- g_free (src->bye_reason);
-
- gst_caps_replace (&src->caps, NULL);
-
- G_OBJECT_CLASS (rtp_source_parent_class)->finalize (object);
-}
-
-static GstStructure *
-rtp_source_create_stats (RTPSource * src)
-{
- GstStructure *s;
- gboolean is_sender = src->is_sender;
- gboolean internal = src->internal;
- gchar address_str[GST_NETADDRESS_MAX_LEN];
-
- /* common data for all types of sources */
- s = gst_structure_new ("application/x-rtp-source-stats",
- "ssrc", G_TYPE_UINT, (guint) src->ssrc,
- "internal", G_TYPE_BOOLEAN, internal,
- "validated", G_TYPE_BOOLEAN, src->validated,
- "received-bye", G_TYPE_BOOLEAN, src->received_bye,
- "is-csrc", G_TYPE_BOOLEAN, src->is_csrc,
- "is-sender", G_TYPE_BOOLEAN, is_sender, NULL);
-
- /* add address and port */
- if (src->have_rtp_from) {
- gst_netaddress_to_string (&src->rtp_from, address_str,
- sizeof (address_str));
- gst_structure_set (s, "rtp-from", G_TYPE_STRING, address_str, NULL);
- }
- if (src->have_rtcp_from) {
- gst_netaddress_to_string (&src->rtcp_from, address_str,
- sizeof (address_str));
- gst_structure_set (s, "rtcp-from", G_TYPE_STRING, address_str, NULL);
- }
-
- if (internal) {
- /* our internal source */
- if (is_sender) {
- /* if we are sending, report about how much we sent, other sources will
- * have a RB with info on reception. */
- gst_structure_set (s,
- "octets-sent", G_TYPE_UINT64, src->stats.octets_sent,
- "packets-sent", G_TYPE_UINT64, src->stats.packets_sent,
- "bitrate", G_TYPE_UINT64, src->bitrate, NULL);
- } else {
- /* if we are not sending we have nothing more to report */
- }
- } else {
- gboolean have_rb;
- guint8 fractionlost = 0;
- gint32 packetslost = 0;
- guint32 exthighestseq = 0;
- guint32 jitter = 0;
- guint32 lsr = 0;
- guint32 dlsr = 0;
- guint32 round_trip = 0;
-
- /* other sources */
- if (is_sender) {
- gboolean have_sr;
- GstClockTime time = 0;
- guint64 ntptime = 0;
- guint32 rtptime = 0;
- guint32 packet_count = 0;
- guint32 octet_count = 0;
-
- /* this source is sending to us, get the last SR. */
- have_sr = rtp_source_get_last_sr (src, &time, &ntptime, &rtptime,
- &packet_count, &octet_count);
- gst_structure_set (s,
- "octets-received", G_TYPE_UINT64, src->stats.octets_received,
- "packets-received", G_TYPE_UINT64, src->stats.packets_received,
- "have-sr", G_TYPE_BOOLEAN, have_sr,
- "sr-ntptime", G_TYPE_UINT64, ntptime,
- "sr-rtptime", G_TYPE_UINT, (guint) rtptime,
- "sr-octet-count", G_TYPE_UINT, (guint) octet_count,
- "sr-packet-count", G_TYPE_UINT, (guint) packet_count, NULL);
- }
- /* we might be sending to this SSRC so we report about how it is
- * receiving our data */
- have_rb = rtp_source_get_last_rb (src, &fractionlost, &packetslost,
- &exthighestseq, &jitter, &lsr, &dlsr, &round_trip);
-
- gst_structure_set (s,
- "have-rb", G_TYPE_BOOLEAN, have_rb,
- "rb-fractionlost", G_TYPE_UINT, (guint) fractionlost,
- "rb-packetslost", G_TYPE_INT, (gint) packetslost,
- "rb-exthighestseq", G_TYPE_UINT, (guint) exthighestseq,
- "rb-jitter", G_TYPE_UINT, (guint) jitter,
- "rb-lsr", G_TYPE_UINT, (guint) lsr,
- "rb-dlsr", G_TYPE_UINT, (guint) dlsr,
- "rb-round-trip", G_TYPE_UINT, (guint) round_trip, NULL);
- }
-
- return s;
-}
-
-/**
- * rtp_source_get_sdes_struct:
- * @src: an #RTSPSource
- *
- * Get the SDES data as a GstStructure
- *
- * Returns: a GstStructure with SDES items for @src.
- */
-GstStructure *
-rtp_source_get_sdes_struct (RTPSource * src)
-{
- GstStructure *s;
- gchar *str;
-
- s = gst_structure_new ("application/x-rtp-source-sdes",
- "ssrc", G_TYPE_UINT, (guint) src->ssrc, NULL);
-
- if ((str = rtp_source_get_sdes_string (src, GST_RTCP_SDES_CNAME))) {
- gst_structure_set (s, "cname", G_TYPE_STRING, str, NULL);
- g_free (str);
- }
- if ((str = rtp_source_get_sdes_string (src, GST_RTCP_SDES_NAME))) {
- gst_structure_set (s, "name", G_TYPE_STRING, str, NULL);
- g_free (str);
- }
- if ((str = rtp_source_get_sdes_string (src, GST_RTCP_SDES_EMAIL))) {
- gst_structure_set (s, "email", G_TYPE_STRING, str, NULL);
- g_free (str);
- }
- if ((str = rtp_source_get_sdes_string (src, GST_RTCP_SDES_PHONE))) {
- gst_structure_set (s, "phone", G_TYPE_STRING, str, NULL);
- g_free (str);
- }
- if ((str = rtp_source_get_sdes_string (src, GST_RTCP_SDES_LOC))) {
- gst_structure_set (s, "location", G_TYPE_STRING, str, NULL);
- g_free (str);
- }
- if ((str = rtp_source_get_sdes_string (src, GST_RTCP_SDES_TOOL))) {
- gst_structure_set (s, "tool", G_TYPE_STRING, str, NULL);
- g_free (str);
- }
- if ((str = rtp_source_get_sdes_string (src, GST_RTCP_SDES_NOTE))) {
- gst_structure_set (s, "note", G_TYPE_STRING, str, NULL);
- g_free (str);
- }
- return s;
-}
-
-/**
- * rtp_source_set_sdes_struct:
- * @src: an #RTSPSource
- * @sdes: a #GstStructure with SDES info
- *
- * Set the SDES items from @sdes.
- */
-void
-rtp_source_set_sdes_struct (RTPSource * src, const GstStructure * sdes)
-{
- const gchar *str;
-
- if (!gst_structure_has_name (sdes, "application/x-rtp-source-sdes"))
- return;
-
- if ((str = gst_structure_get_string (sdes, "cname"))) {
- rtp_source_set_sdes_string (src, GST_RTCP_SDES_CNAME, str);
- }
- if ((str = gst_structure_get_string (sdes, "name"))) {
- rtp_source_set_sdes_string (src, GST_RTCP_SDES_NAME, str);
- }
- if ((str = gst_structure_get_string (sdes, "email"))) {
- rtp_source_set_sdes_string (src, GST_RTCP_SDES_EMAIL, str);
- }
- if ((str = gst_structure_get_string (sdes, "phone"))) {
- rtp_source_set_sdes_string (src, GST_RTCP_SDES_PHONE, str);
- }
- if ((str = gst_structure_get_string (sdes, "location"))) {
- rtp_source_set_sdes_string (src, GST_RTCP_SDES_LOC, str);
- }
- if ((str = gst_structure_get_string (sdes, "tool"))) {
- rtp_source_set_sdes_string (src, GST_RTCP_SDES_TOOL, str);
- }
- if ((str = gst_structure_get_string (sdes, "note"))) {
- rtp_source_set_sdes_string (src, GST_RTCP_SDES_NOTE, str);
- }
-}
-
-static void
-rtp_source_set_property (GObject * object, guint prop_id,
- const GValue * value, GParamSpec * pspec)
-{
- RTPSource *src;
-
- src = RTP_SOURCE (object);
-
- switch (prop_id) {
- case PROP_SSRC:
- src->ssrc = g_value_get_uint (value);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-static void
-rtp_source_get_property (GObject * object, guint prop_id,
- GValue * value, GParamSpec * pspec)
-{
- RTPSource *src;
-
- src = RTP_SOURCE (object);
-
- switch (prop_id) {
- case PROP_SSRC:
- g_value_set_uint (value, rtp_source_get_ssrc (src));
- break;
- case PROP_IS_CSRC:
- g_value_set_boolean (value, rtp_source_is_as_csrc (src));
- break;
- case PROP_IS_VALIDATED:
- g_value_set_boolean (value, rtp_source_is_validated (src));
- break;
- case PROP_IS_SENDER:
- g_value_set_boolean (value, rtp_source_is_sender (src));
- break;
- case PROP_SDES:
- g_value_take_boxed (value, rtp_source_get_sdes_struct (src));
- break;
- case PROP_STATS:
- g_value_take_boxed (value, rtp_source_create_stats (src));
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-/**
- * rtp_source_new:
- * @ssrc: an SSRC
- *
- * Create a #RTPSource with @ssrc.
- *
- * Returns: a new #RTPSource. Use g_object_unref() after usage.
- */
-RTPSource *
-rtp_source_new (guint32 ssrc)
-{
- RTPSource *src;
-
- src = g_object_new (RTP_TYPE_SOURCE, NULL);
- src->ssrc = ssrc;
-
- return src;
-}
-
-/**
- * rtp_source_set_callbacks:
- * @src: an #RTPSource
- * @cb: callback functions
- * @user_data: user data
- *
- * Set the callbacks for the source.
- */
-void
-rtp_source_set_callbacks (RTPSource * src, RTPSourceCallbacks * cb,
- gpointer user_data)
-{
- g_return_if_fail (RTP_IS_SOURCE (src));
-
- src->callbacks.push_rtp = cb->push_rtp;
- src->callbacks.clock_rate = cb->clock_rate;
- src->user_data = user_data;
-}
-
-/**
- * rtp_source_get_ssrc:
- * @src: an #RTPSource
- *
- * Get the SSRC of @source.
- *
- * Returns: the SSRC of src.
- */
-guint32
-rtp_source_get_ssrc (RTPSource * src)
-{
- guint32 result;
-
- g_return_val_if_fail (RTP_IS_SOURCE (src), 0);
-
- result = src->ssrc;
-
- return result;
-}
-
-/**
- * rtp_source_set_as_csrc:
- * @src: an #RTPSource
- *
- * Configure @src as a CSRC, this will also validate @src.
- */
-void
-rtp_source_set_as_csrc (RTPSource * src)
-{
- g_return_if_fail (RTP_IS_SOURCE (src));
-
- src->validated = TRUE;
- src->is_csrc = TRUE;
-}
-
-/**
- * rtp_source_is_as_csrc:
- * @src: an #RTPSource
- *
- * Check if @src is a contributing source.
- *
- * Returns: %TRUE if @src is acting as a contributing source.
- */
-gboolean
-rtp_source_is_as_csrc (RTPSource * src)
-{
- gboolean result;
-
- g_return_val_if_fail (RTP_IS_SOURCE (src), FALSE);
-
- result = src->is_csrc;
-
- return result;
-}
-
-/**
- * rtp_source_is_active:
- * @src: an #RTPSource
- *
- * Check if @src is an active source. A source is active if it has been
- * validated and has not yet received a BYE packet
- *
- * Returns: %TRUE if @src is an qactive source.
- */
-gboolean
-rtp_source_is_active (RTPSource * src)
-{
- gboolean result;
-
- g_return_val_if_fail (RTP_IS_SOURCE (src), FALSE);
-
- result = RTP_SOURCE_IS_ACTIVE (src);
-
- return result;
-}
-
-/**
- * rtp_source_is_validated:
- * @src: an #RTPSource
- *
- * Check if @src is a validated source.
- *
- * Returns: %TRUE if @src is a validated source.
- */
-gboolean
-rtp_source_is_validated (RTPSource * src)
-{
- gboolean result;
-
- g_return_val_if_fail (RTP_IS_SOURCE (src), FALSE);
-
- result = src->validated;
-
- return result;
-}
-
-/**
- * rtp_source_is_sender:
- * @src: an #RTPSource
- *
- * Check if @src is a sending source.
- *
- * Returns: %TRUE if @src is a sending source.
- */
-gboolean
-rtp_source_is_sender (RTPSource * src)
-{
- gboolean result;
-
- g_return_val_if_fail (RTP_IS_SOURCE (src), FALSE);
-
- result = RTP_SOURCE_IS_SENDER (src);
-
- return result;
-}
-
-/**
- * rtp_source_received_bye:
- * @src: an #RTPSource
- *
- * Check if @src has receoved a BYE packet.
- *
- * Returns: %TRUE if @src has received a BYE packet.
- */
-gboolean
-rtp_source_received_bye (RTPSource * src)
-{
- gboolean result;
-
- g_return_val_if_fail (RTP_IS_SOURCE (src), FALSE);
-
- result = src->received_bye;
-
- return result;
-}
-
-
-/**
- * rtp_source_get_bye_reason:
- * @src: an #RTPSource
- *
- * Get the BYE reason for @src. Check if the source receoved a BYE message first
- * with rtp_source_received_bye().
- *
- * Returns: The BYE reason or NULL when no reason was given or the source did
- * not receive a BYE message yet. g_fee() after usage.
- */
-gchar *
-rtp_source_get_bye_reason (RTPSource * src)
-{
- gchar *result;
-
- g_return_val_if_fail (RTP_IS_SOURCE (src), NULL);
-
- result = g_strdup (src->bye_reason);
-
- return result;
-}
-
-/**
- * rtp_source_update_caps:
- * @src: an #RTPSource
- * @caps: a #GstCaps
- *
- * Parse @caps and store all relevant information in @source.
- */
-void
-rtp_source_update_caps (RTPSource * src, GstCaps * caps)
-{
- GstStructure *s;
- guint val;
- gint ival;
-
- /* nothing changed, return */
- if (caps == NULL || src->caps == caps)
- return;
-
- s = gst_caps_get_structure (caps, 0);
-
- if (gst_structure_get_int (s, "payload", &ival))
- src->payload = ival;
- else
- src->payload = -1;
- GST_DEBUG ("got payload %d", src->payload);
-
- if (gst_structure_get_int (s, "clock-rate", &ival))
- src->clock_rate = ival;
- else
- src->clock_rate = -1;
-
- GST_DEBUG ("got clock-rate %d", src->clock_rate);
-
- if (gst_structure_get_uint (s, "seqnum-base", &val))
- src->seqnum_base = val;
- else
- src->seqnum_base = -1;
-
- GST_DEBUG ("got seqnum-base %" G_GINT32_FORMAT, src->seqnum_base);
-
- gst_caps_replace (&src->caps, caps);
-}
-
-/**
- * rtp_source_set_sdes:
- * @src: an #RTPSource
- * @type: the type of the SDES item
- * @data: the SDES data
- * @len: the SDES length
- *
- * Store an SDES item of @type in @src.
- *
- * Returns: %FALSE if the SDES item was unchanged or @type is unknown.
- */
-gboolean
-rtp_source_set_sdes (RTPSource * src, GstRTCPSDESType type,
- const guint8 * data, guint len)
-{
- guint8 *old;
-
- g_return_val_if_fail (RTP_IS_SOURCE (src), FALSE);
-
- if (type < 0 || type > GST_RTCP_SDES_PRIV)
- return FALSE;
-
- old = src->sdes[type];
-
- /* lengths are the same, check if the data is the same */
- if ((src->sdes_len[type] == len))
- if (data != NULL && old != NULL && (memcmp (old, data, len) == 0))
- return FALSE;
-
- /* NULL data, make sure we store 0 length or if no length is given,
- * take strlen */
- if (data == NULL)
- len = 0;
-
- g_free (src->sdes[type]);
- src->sdes[type] = g_memdup (data, len);
- src->sdes_len[type] = len;
-
- return TRUE;
-}
-
-/**
- * rtp_source_set_sdes_string:
- * @src: an #RTPSource
- * @type: the type of the SDES item
- * @data: the SDES data
- *
- * Store an SDES item of @type in @src. This function is similar to
- * rtp_source_set_sdes() but takes a null-terminated string for convenience.
- *
- * Returns: %FALSE if the SDES item was unchanged or @type is unknown.
- */
-gboolean
-rtp_source_set_sdes_string (RTPSource * src, GstRTCPSDESType type,
- const gchar * data)
-{
- guint len;
- gboolean result;
-
- if (data)
- len = strlen (data);
- else
- len = 0;
-
- result = rtp_source_set_sdes (src, type, (guint8 *) data, len);
-
- return result;
-}
-
-/**
- * rtp_source_get_sdes:
- * @src: an #RTPSource
- * @type: the type of the SDES item
- * @data: location to store the SDES data or NULL
- * @len: location to store the SDES length or NULL
- *
- * Get the SDES item of @type from @src. Note that @data does not always point
- * to a null-terminated string, use rtp_source_get_sdes_string() to retrieve a
- * null-terminated string instead.
- *
- * @data remains valid until the next call to rtp_source_set_sdes().
- *
- * Returns: %TRUE if @type was valid and @data and @len contain valid
- * data. @data can be NULL when the item was unset.
- */
-gboolean
-rtp_source_get_sdes (RTPSource * src, GstRTCPSDESType type, guint8 ** data,
- guint * len)
-{
- g_return_val_if_fail (RTP_IS_SOURCE (src), FALSE);
-
- if (type < 0 || type > GST_RTCP_SDES_PRIV)
- return FALSE;
-
- if (data)
- *data = src->sdes[type];
- if (len)
- *len = src->sdes_len[type];
-
- return TRUE;
-}
-
-/**
- * rtp_source_get_sdes_string:
- * @src: an #RTPSource
- * @type: the type of the SDES item
- *
- * Get the SDES item of @type from @src.
- *
- * Returns: a null-terminated copy of the SDES item or NULL when @type was not
- * valid or the SDES item was unset. g_free() after usage.
- */
-gchar *
-rtp_source_get_sdes_string (RTPSource * src, GstRTCPSDESType type)
-{
- gchar *result;
-
- g_return_val_if_fail (RTP_IS_SOURCE (src), NULL);
-
- if (type < 0 || type > GST_RTCP_SDES_PRIV)
- return NULL;
-
- result = g_strndup ((const gchar *) src->sdes[type], src->sdes_len[type]);
-
- return result;
-}
-
-/**
- * rtp_source_set_rtp_from:
- * @src: an #RTPSource
- * @address: the RTP address to set
- *
- * Set that @src is receiving RTP packets from @address. This is used for
- * collistion checking.
- */
-void
-rtp_source_set_rtp_from (RTPSource * src, GstNetAddress * address)
-{
- g_return_if_fail (RTP_IS_SOURCE (src));
-
- src->have_rtp_from = TRUE;
- memcpy (&src->rtp_from, address, sizeof (GstNetAddress));
-}
-
-/**
- * rtp_source_set_rtcp_from:
- * @src: an #RTPSource
- * @address: the RTCP address to set
- *
- * Set that @src is receiving RTCP packets from @address. This is used for
- * collistion checking.
- */
-void
-rtp_source_set_rtcp_from (RTPSource * src, GstNetAddress * address)
-{
- g_return_if_fail (RTP_IS_SOURCE (src));
-
- src->have_rtcp_from = TRUE;
- memcpy (&src->rtcp_from, address, sizeof (GstNetAddress));
-}
-
-static GstFlowReturn
-push_packet (RTPSource * src, GstBuffer * buffer)
-{
- GstFlowReturn ret = GST_FLOW_OK;
-
- /* push queued packets first if any */
- while (!g_queue_is_empty (src->packets)) {
- GstBuffer *buffer = GST_BUFFER_CAST (g_queue_pop_head (src->packets));
-
- GST_LOG ("pushing queued packet");
- if (src->callbacks.push_rtp)
- src->callbacks.push_rtp (src, buffer, src->user_data);
- else
- gst_buffer_unref (buffer);
- }
- GST_LOG ("pushing new packet");
- /* push packet */
- if (src->callbacks.push_rtp)
- ret = src->callbacks.push_rtp (src, buffer, src->user_data);
- else
- gst_buffer_unref (buffer);
-
- return ret;
-}
-
-static gint
-get_clock_rate (RTPSource * src, guint8 payload)
-{
- if (src->payload == -1) {
- /* first payload received, nothing was in the caps, lock on to this payload */
- src->payload = payload;
- GST_DEBUG ("first payload %d", payload);
- } else if (payload != src->payload) {
- /* we have a different payload than before, reset the clock-rate */
- GST_DEBUG ("new payload %d", payload);
- src->payload = payload;
- src->clock_rate = -1;
- src->stats.transit = -1;
- }
-
- if (src->clock_rate == -1) {
- gint clock_rate = -1;
-
- if (src->callbacks.clock_rate)
- clock_rate = src->callbacks.clock_rate (src, payload, src->user_data);
-
- GST_DEBUG ("got clock-rate %d", clock_rate);
-
- src->clock_rate = clock_rate;
- }
- return src->clock_rate;
-}
-
-/* Jitter is the variation in the delay of received packets in a flow. It is
- * measured by comparing the interval when RTP packets were sent to the interval
- * at which they were received. For instance, if packet #1 and packet #2 leave
- * 50 milliseconds apart and arrive 60 milliseconds apart, then the jitter is 10
- * milliseconds. */
-static void
-calculate_jitter (RTPSource * src, GstBuffer * buffer,
- RTPArrivalStats * arrival)
-{
- guint64 ntpnstime;
- guint32 rtparrival, transit, rtptime;
- gint32 diff;
- gint clock_rate;
- guint8 pt;
-
- /* get arrival time */
- if ((ntpnstime = arrival->ntpnstime) == GST_CLOCK_TIME_NONE)
- goto no_time;
-
- pt = gst_rtp_buffer_get_payload_type (buffer);
-
- GST_LOG ("SSRC %08x got payload %d", src->ssrc, pt);
-
- /* get clockrate */
- if ((clock_rate = get_clock_rate (src, pt)) == -1)
- goto no_clock_rate;
-
- rtptime = gst_rtp_buffer_get_timestamp (buffer);
-
- /* convert arrival time to RTP timestamp units, truncate to 32 bits, we don't
- * care about the absolute value, just the difference. */
- rtparrival = gst_util_uint64_scale_int (ntpnstime, clock_rate, GST_SECOND);
-
- /* transit time is difference with RTP timestamp */
- transit = rtparrival - rtptime;
-
- /* get ABS diff with previous transit time */
- if (src->stats.transit != -1) {
- if (transit > src->stats.transit)
- diff = transit - src->stats.transit;
- else
- diff = src->stats.transit - transit;
- } else
- diff = 0;
-
- src->stats.transit = transit;
-
- /* update jitter, the value we store is scaled up so we can keep precision. */
- src->stats.jitter += diff - ((src->stats.jitter + 8) >> 4);
-
- src->stats.prev_rtptime = src->stats.last_rtptime;
- src->stats.last_rtptime = rtparrival;
-
- GST_LOG ("rtparrival %u, rtptime %u, clock-rate %d, diff %d, jitter: %f",
- rtparrival, rtptime, clock_rate, diff, (src->stats.jitter) / 16.0);
-
- return;
-
- /* ERRORS */
-no_time:
- {
- GST_WARNING ("cannot get current time");
- return;
- }
-no_clock_rate:
- {
- GST_WARNING ("cannot get clock-rate for pt %d", pt);
- return;
- }
-}
-
-static void
-init_seq (RTPSource * src, guint16 seq)
-{
- src->stats.base_seq = seq;
- src->stats.max_seq = seq;
- src->stats.bad_seq = RTP_SEQ_MOD + 1; /* so seq == bad_seq is false */
- src->stats.cycles = 0;
- src->stats.packets_received = 0;
- src->stats.octets_received = 0;
- src->stats.bytes_received = 0;
- src->stats.prev_received = 0;
- src->stats.prev_expected = 0;
-
- GST_DEBUG ("base_seq %d", seq);
-}
-
-/**
- * rtp_source_process_rtp:
- * @src: an #RTPSource
- * @buffer: an RTP buffer
- *
- * Let @src handle the incomming RTP @buffer.
- *
- * Returns: a #GstFlowReturn.
- */
-GstFlowReturn
-rtp_source_process_rtp (RTPSource * src, GstBuffer * buffer,
- RTPArrivalStats * arrival)
-{
- GstFlowReturn result = GST_FLOW_OK;
- guint16 seqnr, udelta;
- RTPSourceStats *stats;
-
- g_return_val_if_fail (RTP_IS_SOURCE (src), GST_FLOW_ERROR);
- g_return_val_if_fail (GST_IS_BUFFER (buffer), GST_FLOW_ERROR);
-
- stats = &src->stats;
-
- seqnr = gst_rtp_buffer_get_seq (buffer);
-
- rtp_source_update_caps (src, GST_BUFFER_CAPS (buffer));
-
- if (stats->cycles == -1) {
- GST_DEBUG ("received first buffer");
- /* first time we heard of this source */
- init_seq (src, seqnr);
- src->stats.max_seq = seqnr - 1;
- src->probation = RTP_DEFAULT_PROBATION;
- }
-
- udelta = seqnr - stats->max_seq;
-
- /* if we are still on probation, check seqnum */
- if (src->probation) {
- guint16 expected;
-
- expected = src->stats.max_seq + 1;
-
- /* when in probation, we require consecutive seqnums */
- if (seqnr == expected) {
- /* expected packet */
- GST_DEBUG ("probation: seqnr %d == expected %d", seqnr, expected);
- src->probation--;
- src->stats.max_seq = seqnr;
- if (src->probation == 0) {
- GST_DEBUG ("probation done!");
- init_seq (src, seqnr);
- } else {
- GstBuffer *q;
-
- GST_DEBUG ("probation %d: queue buffer", src->probation);
- /* when still in probation, keep packets in a list. */
- g_queue_push_tail (src->packets, buffer);
- /* remove packets from queue if there are too many */
- while (g_queue_get_length (src->packets) > RTP_MAX_PROBATION_LEN) {
- q = g_queue_pop_head (src->packets);
- gst_buffer_unref (q);
- }
- goto done;
- }
- } else {
- GST_DEBUG ("probation: seqnr %d != expected %d", seqnr, expected);
- src->probation = RTP_DEFAULT_PROBATION;
- src->stats.max_seq = seqnr;
- goto done;
- }
- } else if (udelta < RTP_MAX_DROPOUT) {
- /* in order, with permissible gap */
- if (seqnr < stats->max_seq) {
- /* sequence number wrapped - count another 64K cycle. */
- stats->cycles += RTP_SEQ_MOD;
- }
- stats->max_seq = seqnr;
- } else if (udelta <= RTP_SEQ_MOD - RTP_MAX_MISORDER) {
- /* the sequence number made a very large jump */
- if (seqnr == stats->bad_seq) {
- /* two sequential packets -- assume that the other side
- * restarted without telling us so just re-sync
- * (i.e., pretend this was the first packet). */
- init_seq (src, seqnr);
- } else {
- /* unacceptable jump */
- stats->bad_seq = (seqnr + 1) & (RTP_SEQ_MOD - 1);
- goto bad_sequence;
- }
- } else {
- /* duplicate or reordered packet, will be filtered by jitterbuffer. */
- GST_WARNING ("duplicate or reordered packet");
- }
-
- src->stats.octets_received += arrival->payload_len;
- src->stats.bytes_received += arrival->bytes;
- src->stats.packets_received++;
- /* the source that sent the packet must be a sender */
- src->is_sender = TRUE;
- src->validated = TRUE;
-
- GST_LOG ("seq %d, PC: %" G_GUINT64_FORMAT ", OC: %" G_GUINT64_FORMAT,
- seqnr, src->stats.packets_received, src->stats.octets_received);
-
- /* calculate jitter for the stats */
- calculate_jitter (src, buffer, arrival);
-
- /* we're ready to push the RTP packet now */
- result = push_packet (src, buffer);
-
-done:
- return result;
-
- /* ERRORS */
-bad_sequence:
- {
- GST_WARNING ("unacceptable seqnum received");
- gst_buffer_unref (buffer);
- return GST_FLOW_OK;
- }
-}
-
-/**
- * rtp_source_process_bye:
- * @src: an #RTPSource
- * @reason: the reason for leaving
- *
- * Notify @src that a BYE packet has been received. This will make the source
- * inactive.
- */
-void
-rtp_source_process_bye (RTPSource * src, const gchar * reason)
-{
- g_return_if_fail (RTP_IS_SOURCE (src));
-
- GST_DEBUG ("marking SSRC %08x as BYE, reason: %s", src->ssrc,
- GST_STR_NULL (reason));
-
- /* copy the reason and mark as received_bye */
- g_free (src->bye_reason);
- src->bye_reason = g_strdup (reason);
- src->received_bye = TRUE;
-}
-
-static GstBufferListItem
-set_ssrc (GstBuffer ** buffer, guint group, guint idx, RTPSource * src)
-{
- *buffer = gst_buffer_make_writable (*buffer);
- gst_rtp_buffer_set_ssrc (*buffer, src->ssrc);
- return GST_BUFFER_LIST_SKIP_GROUP;
-}
-
-/**
- * rtp_source_send_rtp:
- * @src: an #RTPSource
- * @data: an RTP buffer or a list of RTP buffers
- * @is_list: if @data is a buffer or list
- * @ntpnstime: the NTP time when this buffer was captured in nanoseconds. This
- * is the buffer timestamp converted to NTP time.
- *
- * Send @data (an RTP buffer or list of buffers) originating from @src.
- * This will make @src a sender. This function takes ownership of @data and
- * modifies the SSRC in the RTP packet to that of @src when needed.
- *
- * Returns: a #GstFlowReturn.
- */
-GstFlowReturn
-rtp_source_send_rtp (RTPSource * src, gpointer data, gboolean is_list,
- guint64 ntpnstime)
-{
- GstFlowReturn result;
- guint len;
- guint32 rtptime;
- guint64 ext_rtptime;
- guint64 ntp_diff, rtp_diff;
- guint64 elapsed;
- GstBufferList *list = NULL;
- GstBuffer *buffer = NULL;
- guint packets;
- guint32 ssrc;
-
- g_return_val_if_fail (RTP_IS_SOURCE (src), GST_FLOW_ERROR);
- g_return_val_if_fail (is_list || GST_IS_BUFFER (data), GST_FLOW_ERROR);
-
- if (is_list) {
- list = GST_BUFFER_LIST_CAST (data);
-
- /* We can grab the caps from the first group, since all
- * groups of a buffer list have same caps. */
- buffer = gst_buffer_list_get (list, 0, 0);
- if (!buffer)
- goto no_buffer;
- } else {
- buffer = GST_BUFFER_CAST (data);
- }
- rtp_source_update_caps (src, GST_BUFFER_CAPS (buffer));
-
- /* we are a sender now */
- src->is_sender = TRUE;
-
- if (is_list) {
- /* Each group makes up a network packet. */
- packets = gst_buffer_list_n_groups (list);
- len = gst_rtp_buffer_list_get_payload_len (list);
- } else {
- packets = 1;
- len = gst_rtp_buffer_get_payload_len (buffer);
- }
-
- /* update stats for the SR */
- src->stats.packets_sent += packets;
- src->stats.octets_sent += len;
- src->bytes_sent += len;
-
- if (src->prev_ntpnstime) {
- elapsed = ntpnstime - src->prev_ntpnstime;
-
- if (elapsed > (G_GINT64_CONSTANT (1) << 31)) {
- guint64 rate;
-
- rate =
- gst_util_uint64_scale (src->bytes_sent, elapsed,
- (G_GINT64_CONSTANT (1) << 29));
-
- GST_LOG ("Elapsed %" G_GUINT64_FORMAT ", bytes %" G_GUINT64_FORMAT
- ", rate %" G_GUINT64_FORMAT, elapsed, src->bytes_sent, rate);
-
- if (src->bitrate == 0)
- src->bitrate = rate;
- else
- src->bitrate = ((src->bitrate * 3) + rate) / 4;
-
- src->prev_ntpnstime = ntpnstime;
- src->bytes_sent = 0;
- }
- } else {
- GST_LOG ("Reset bitrate measurement");
- src->prev_ntpnstime = ntpnstime;
- src->bitrate = 0;
- }
-
- if (is_list) {
- rtptime = gst_rtp_buffer_list_get_timestamp (list);
- } else {
- rtptime = gst_rtp_buffer_get_timestamp (buffer);
- }
- ext_rtptime = src->last_rtptime;
- ext_rtptime = gst_rtp_buffer_ext_timestamp (&ext_rtptime, rtptime);
-
- GST_LOG ("SSRC %08x, RTP %" G_GUINT64_FORMAT ", NTP %" GST_TIME_FORMAT,
- src->ssrc, ext_rtptime, GST_TIME_ARGS (ntpnstime));
-
- if (ext_rtptime > src->last_rtptime) {
- rtp_diff = ext_rtptime - src->last_rtptime;
- ntp_diff = ntpnstime - src->last_ntpnstime;
-
- /* calc the diff so we can detect drift at the sender. This can also be used
- * to guestimate the clock rate if the NTP time is locked to the RTP
- * timestamps (as is the case when the capture device is providing the clock). */
- GST_LOG ("SSRC %08x, diff RTP %" G_GUINT64_FORMAT ", diff NTP %"
- GST_TIME_FORMAT, src->ssrc, rtp_diff, GST_TIME_ARGS (ntp_diff));
- }
-
- /* we keep track of the last received RTP timestamp and the corresponding
- * NTP timestamp so that we can use this info when constructing SR reports */
- src->last_rtptime = ext_rtptime;
- src->last_ntpnstime = ntpnstime;
-
- /* push packet */
- if (!src->callbacks.push_rtp)
- goto no_callback;
-
- if (is_list) {
- ssrc = gst_rtp_buffer_list_get_ssrc (list);
- } else {
- ssrc = gst_rtp_buffer_get_ssrc (buffer);
- }
-
- if (ssrc != src->ssrc) {
- /* the SSRC of the packet is not correct, make a writable buffer and
- * update the SSRC. This could involve a complete copy of the packet when
- * it is not writable. Usually the payloader will use caps negotiation to
- * get the correct SSRC from the session manager before pushing anything. */
-
- /* FIXME, we don't want to warn yet because we can't inform any payloader
- * of the changes SSRC yet because we don't implement pad-alloc. */
- GST_LOG ("updating SSRC from %08x to %08x, fix the payloader", ssrc,
- src->ssrc);
-
- if (is_list) {
- list = gst_buffer_list_make_writable (list);
- gst_buffer_list_foreach (list, (GstBufferListFunc) set_ssrc, src);
- } else {
- set_ssrc (&buffer, 0, 0, src);
- }
- }
- GST_LOG ("pushing RTP %s %" G_GUINT64_FORMAT, is_list ? "list" : "packet",
- src->stats.packets_sent);
-
- result = src->callbacks.push_rtp (src, data, src->user_data);
-
- return result;
-
- /* ERRORS */
-no_buffer:
- {
- GST_WARNING ("no buffers in buffer list");
- gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
- return GST_FLOW_OK;
- }
-no_callback:
- {
- GST_WARNING ("no callback installed, dropping packet");
- gst_mini_object_unref (GST_MINI_OBJECT_CAST (data));
- return GST_FLOW_OK;
- }
-}
-
-/**
- * rtp_source_process_sr:
- * @src: an #RTPSource
- * @time: time of packet arrival
- * @ntptime: the NTP time in 32.32 fixed point
- * @rtptime: the RTP time
- * @packet_count: the packet count
- * @octet_count: the octect count
- *
- * Update the sender report in @src.
- */
-void
-rtp_source_process_sr (RTPSource * src, GstClockTime time, guint64 ntptime,
- guint32 rtptime, guint32 packet_count, guint32 octet_count)
-{
- RTPSenderReport *curr;
- gint curridx;
-
- g_return_if_fail (RTP_IS_SOURCE (src));
-
- GST_DEBUG ("got SR packet: SSRC %08x, NTP %08x:%08x, RTP %" G_GUINT32_FORMAT
- ", PC %" G_GUINT32_FORMAT ", OC %" G_GUINT32_FORMAT, src->ssrc,
- (guint32) (ntptime >> 32), (guint32) (ntptime & 0xffffffff), rtptime,
- packet_count, octet_count);
-
- curridx = src->stats.curr_sr ^ 1;
- curr = &src->stats.sr[curridx];
-
- /* this is a sender now */
- src->is_sender = TRUE;
-
- /* update current */
- curr->is_valid = TRUE;
- curr->ntptime = ntptime;
- curr->rtptime = rtptime;
- curr->packet_count = packet_count;
- curr->octet_count = octet_count;
- curr->time = time;
-
- /* make current */
- src->stats.curr_sr = curridx;
-}
-
-/**
- * rtp_source_process_rb:
- * @src: an #RTPSource
- * @time: the current time in nanoseconds since 1970
- * @fractionlost: fraction lost since last SR/RR
- * @packetslost: the cumululative number of packets lost
- * @exthighestseq: the extended last sequence number received
- * @jitter: the interarrival jitter
- * @lsr: the last SR packet from this source
- * @dlsr: the delay since last SR packet
- *
- * Update the report block in @src.
- */
-void
-rtp_source_process_rb (RTPSource * src, GstClockTime time, guint8 fractionlost,
- gint32 packetslost, guint32 exthighestseq, guint32 jitter, guint32 lsr,
- guint32 dlsr)
-{
- RTPReceiverReport *curr;
- gint curridx;
- guint32 ntp, A;
-
- g_return_if_fail (RTP_IS_SOURCE (src));
-
- GST_DEBUG ("got RB packet: SSRC %08x, FL %2x, PL %d, HS %" G_GUINT32_FORMAT
- ", jitter %" G_GUINT32_FORMAT ", LSR %04x:%04x, DLSR %04x:%04x",
- src->ssrc, fractionlost, packetslost, exthighestseq, jitter, lsr >> 16,
- lsr & 0xffff, dlsr >> 16, dlsr & 0xffff);
-
- curridx = src->stats.curr_rr ^ 1;
- curr = &src->stats.rr[curridx];
-
- /* update current */
- curr->is_valid = TRUE;
- curr->fractionlost = fractionlost;
- curr->packetslost = packetslost;
- curr->exthighestseq = exthighestseq;
- curr->jitter = jitter;
- curr->lsr = lsr;
- curr->dlsr = dlsr;
-
- /* calculate round trip, round the time up */
- ntp = ((gst_rtcp_unix_to_ntp (time) + 0xffff) >> 16) & 0xffffffff;
- A = dlsr + lsr;
- if (A > 0 && ntp > A)
- A = ntp - A;
- else
- A = 0;
- curr->round_trip = A;
-
- GST_DEBUG ("NTP %04x:%04x, round trip %04x:%04x", ntp >> 16, ntp & 0xffff,
- A >> 16, A & 0xffff);
-
- /* make current */
- src->stats.curr_rr = curridx;
-}
-
-/**
- * rtp_source_get_new_sr:
- * @src: an #RTPSource
- * @ntpnstime: the current time in nanoseconds since 1970
- * @ntptime: the NTP time in 32.32 fixed point
- * @rtptime: the RTP time corresponding to @ntptime
- * @packet_count: the packet count
- * @octet_count: the octect count
- *
- * Get new values to put into a new SR report from this source.
- *
- * Returns: %TRUE on success.
- */
-gboolean
-rtp_source_get_new_sr (RTPSource * src, guint64 ntpnstime,
- guint64 * ntptime, guint32 * rtptime, guint32 * packet_count,
- guint32 * octet_count)
-{
- guint64 t_rtp;
- guint64 t_current_ntp;
- GstClockTimeDiff diff;
-
- g_return_val_if_fail (RTP_IS_SOURCE (src), FALSE);
-
- /* use the sync params to interpolate the date->time member to rtptime. We
- * use the last sent timestamp and rtptime as reference points. We assume
- * that the slope of the rtptime vs timestamp curve is 1, which is certainly
- * sufficient for the frequency at which we report SR and the rate we send
- * out RTP packets. */
- t_rtp = src->last_rtptime;
-
- GST_DEBUG ("last_ntpnstime %" GST_TIME_FORMAT ", last_rtptime %"
- G_GUINT64_FORMAT, GST_TIME_ARGS (src->last_ntpnstime), t_rtp);
-
- if (src->clock_rate != -1) {
- /* get the diff with the SR time */
- diff = GST_CLOCK_DIFF (src->last_ntpnstime, ntpnstime);
-
- /* now translate the diff to RTP time, handle positive and negative cases.
- * If there is no diff, we already set rtptime correctly above. */
- if (diff > 0) {
- GST_DEBUG ("ntpnstime %" GST_TIME_FORMAT ", diff %" GST_TIME_FORMAT,
- GST_TIME_ARGS (ntpnstime), GST_TIME_ARGS (diff));
- t_rtp += gst_util_uint64_scale_int (diff, src->clock_rate, GST_SECOND);
- } else {
- diff = -diff;
- GST_DEBUG ("ntpnstime %" GST_TIME_FORMAT ", diff -%" GST_TIME_FORMAT,
- GST_TIME_ARGS (ntpnstime), GST_TIME_ARGS (diff));
- t_rtp -= gst_util_uint64_scale_int (diff, src->clock_rate, GST_SECOND);
- }
- } else {
- GST_WARNING ("no clock-rate, cannot interpolate rtp time");
- }
-
- /* convert the NTP time in nanoseconds to 32.32 fixed point */
- t_current_ntp = gst_util_uint64_scale (ntpnstime, (1LL << 32), GST_SECOND);
-
- GST_DEBUG ("NTP %08x:%08x, RTP %" G_GUINT32_FORMAT,
- (guint32) (t_current_ntp >> 32), (guint32) (t_current_ntp & 0xffffffff),
- (guint32) t_rtp);
-
- if (ntptime)
- *ntptime = t_current_ntp;
- if (rtptime)
- *rtptime = t_rtp;
- if (packet_count)
- *packet_count = src->stats.packets_sent;
- if (octet_count)
- *octet_count = src->stats.octets_sent;
-
- return TRUE;
-}
-
-/**
- * rtp_source_get_new_rb:
- * @src: an #RTPSource
- * @time: the current time of the system clock
- * @fractionlost: fraction lost since last SR/RR
- * @packetslost: the cumululative number of packets lost
- * @exthighestseq: the extended last sequence number received
- * @jitter: the interarrival jitter
- * @lsr: the last SR packet from this source
- * @dlsr: the delay since last SR packet
- *
- * Get new values to put into a new report block from this source.
- *
- * Returns: %TRUE on success.
- */
-gboolean
-rtp_source_get_new_rb (RTPSource * src, GstClockTime time,
- guint8 * fractionlost, gint32 * packetslost, guint32 * exthighestseq,
- guint32 * jitter, guint32 * lsr, guint32 * dlsr)
-{
- RTPSourceStats *stats;
- guint64 extended_max, expected;
- guint64 expected_interval, received_interval, ntptime;
- gint64 lost, lost_interval;
- guint32 fraction, LSR, DLSR;
- GstClockTime sr_time;
-
- stats = &src->stats;
-
- extended_max = stats->cycles + stats->max_seq;
- expected = extended_max - stats->base_seq + 1;
-
- GST_DEBUG ("ext_max %" G_GUINT64_FORMAT ", expected %" G_GUINT64_FORMAT
- ", received %" G_GUINT64_FORMAT ", base_seq %" G_GUINT32_FORMAT,
- extended_max, expected, stats->packets_received, stats->base_seq);
-
- lost = expected - stats->packets_received;
- lost = CLAMP (lost, -0x800000, 0x7fffff);
-
- expected_interval = expected - stats->prev_expected;
- stats->prev_expected = expected;
- received_interval = stats->packets_received - stats->prev_received;
- stats->prev_received = stats->packets_received;
-
- lost_interval = expected_interval - received_interval;
-
- if (expected_interval == 0 || lost_interval <= 0)
- fraction = 0;
- else
- fraction = (lost_interval << 8) / expected_interval;
-
- GST_DEBUG ("add RR for SSRC %08x", src->ssrc);
- /* we scaled the jitter up for additional precision */
- GST_DEBUG ("fraction %" G_GUINT32_FORMAT ", lost %" G_GINT64_FORMAT
- ", extseq %" G_GUINT64_FORMAT ", jitter %d", fraction, lost,
- extended_max, stats->jitter >> 4);
-
- if (rtp_source_get_last_sr (src, &sr_time, &ntptime, NULL, NULL, NULL)) {
- GstClockTime diff;
-
- /* LSR is middle 32 bits of the last ntptime */
- LSR = (ntptime >> 16) & 0xffffffff;
- diff = time - sr_time;
- GST_DEBUG ("last SR time diff %" GST_TIME_FORMAT, GST_TIME_ARGS (diff));
- /* DLSR, delay since last SR is expressed in 1/65536 second units */
- DLSR = gst_util_uint64_scale_int (diff, 65536, GST_SECOND);
- } else {
- /* No valid SR received, LSR/DLSR are set to 0 then */
- GST_DEBUG ("no valid SR received");
- LSR = 0;
- DLSR = 0;
- }
- GST_DEBUG ("LSR %04x:%04x, DLSR %04x:%04x", LSR >> 16, LSR & 0xffff,
- DLSR >> 16, DLSR & 0xffff);
-
- if (fractionlost)
- *fractionlost = fraction;
- if (packetslost)
- *packetslost = lost;
- if (exthighestseq)
- *exthighestseq = extended_max;
- if (jitter)
- *jitter = stats->jitter >> 4;
- if (lsr)
- *lsr = LSR;
- if (dlsr)
- *dlsr = DLSR;
-
- return TRUE;
-}
-
-/**
- * rtp_source_get_last_sr:
- * @src: an #RTPSource
- * @time: time of packet arrival
- * @ntptime: the NTP time in 32.32 fixed point
- * @rtptime: the RTP time
- * @packet_count: the packet count
- * @octet_count: the octect count
- *
- * Get the values of the last sender report as set with rtp_source_process_sr().
- *
- * Returns: %TRUE if there was a valid SR report.
- */
-gboolean
-rtp_source_get_last_sr (RTPSource * src, GstClockTime * time, guint64 * ntptime,
- guint32 * rtptime, guint32 * packet_count, guint32 * octet_count)
-{
- RTPSenderReport *curr;
-
- g_return_val_if_fail (RTP_IS_SOURCE (src), FALSE);
-
- curr = &src->stats.sr[src->stats.curr_sr];
- if (!curr->is_valid)
- return FALSE;
-
- if (ntptime)
- *ntptime = curr->ntptime;
- if (rtptime)
- *rtptime = curr->rtptime;
- if (packet_count)
- *packet_count = curr->packet_count;
- if (octet_count)
- *octet_count = curr->octet_count;
- if (time)
- *time = curr->time;
-
- return TRUE;
-}
-
-/**
- * rtp_source_get_last_rb:
- * @src: an #RTPSource
- * @fractionlost: fraction lost since last SR/RR
- * @packetslost: the cumululative number of packets lost
- * @exthighestseq: the extended last sequence number received
- * @jitter: the interarrival jitter
- * @lsr: the last SR packet from this source
- * @dlsr: the delay since last SR packet
- * @round_trip: the round trip time
- *
- * Get the values of the last RB report set with rtp_source_process_rb().
- *
- * Returns: %TRUE if there was a valid SB report.
- */
-gboolean
-rtp_source_get_last_rb (RTPSource * src, guint8 * fractionlost,
- gint32 * packetslost, guint32 * exthighestseq, guint32 * jitter,
- guint32 * lsr, guint32 * dlsr, guint32 * round_trip)
-{
- RTPReceiverReport *curr;
-
- g_return_val_if_fail (RTP_IS_SOURCE (src), FALSE);
-
- curr = &src->stats.rr[src->stats.curr_rr];
- if (!curr->is_valid)
- return FALSE;
-
- if (fractionlost)
- *fractionlost = curr->fractionlost;
- if (packetslost)
- *packetslost = curr->packetslost;
- if (exthighestseq)
- *exthighestseq = curr->exthighestseq;
- if (jitter)
- *jitter = curr->jitter;
- if (lsr)
- *lsr = curr->lsr;
- if (dlsr)
- *dlsr = curr->dlsr;
- if (round_trip)
- *round_trip = curr->round_trip;
-
- return TRUE;
-}
+++ /dev/null
-/* GStreamer
- * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#ifndef __RTP_SOURCE_H__
-#define __RTP_SOURCE_H__
-
-#include <gst/gst.h>
-#include <gst/rtp/gstrtcpbuffer.h>
-#include <gst/netbuffer/gstnetbuffer.h>
-
-#include "rtpstats.h"
-
-/* the default number of consecutive RTP packets we need to receive before the
- * source is considered valid */
-#define RTP_NO_PROBATION 0
-#define RTP_DEFAULT_PROBATION 2
-
-#define RTP_SEQ_MOD (1 << 16)
-
-typedef struct _RTPSource RTPSource;
-typedef struct _RTPSourceClass RTPSourceClass;
-
-#define RTP_TYPE_SOURCE (rtp_source_get_type())
-#define RTP_SOURCE(src) (G_TYPE_CHECK_INSTANCE_CAST((src),RTP_TYPE_SOURCE,RTPSource))
-#define RTP_SOURCE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),RTP_TYPE_SOURCE,RTPSourceClass))
-#define RTP_IS_SOURCE(src) (G_TYPE_CHECK_INSTANCE_TYPE((src),RTP_TYPE_SOURCE))
-#define RTP_IS_SOURCE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),RTP_TYPE_SOURCE))
-#define RTP_SOURCE_CAST(src) ((RTPSource *)(src))
-
-/**
- * RTP_SOURCE_IS_ACTIVE:
- * @src: an #RTPSource
- *
- * Check if @src is active. A source is active when it has been validated
- * and has not yet received a BYE packet.
- */
-#define RTP_SOURCE_IS_ACTIVE(src) (src->validated && !src->received_bye)
-
-/**
- * RTP_SOURCE_IS_SENDER:
- * @src: an #RTPSource
- *
- * Check if @src is a sender.
- */
-#define RTP_SOURCE_IS_SENDER(src) (src->is_sender)
-
-/**
- * RTPSourcePushRTP:
- * @src: an #RTPSource
- * @buffer: the RTP buffer ready for processing
- * @user_data: user data specified when registering
- *
- * This callback will be called when @src has @buffer ready for further
- * processing.
- *
- * Returns: a #GstFlowReturn.
- */
-typedef GstFlowReturn (*RTPSourcePushRTP) (RTPSource *src, GstBuffer *buffer,
- gpointer user_data);
-
-/**
- * RTPSourceClockRate:
- * @src: an #RTPSource
- * @payload: a payload type
- * @user_data: user data specified when registering
- *
- * This callback will be called when @src needs the clock-rate of the
- * @payload.
- *
- * Returns: a clock-rate for @payload.
- */
-typedef gint (*RTPSourceClockRate) (RTPSource *src, guint8 payload, gpointer user_data);
-
-/**
- * RTPSourceCallbacks:
- * @push_rtp: a packet becomes available for handling
- * @clock_rate: a clock-rate is requested
- * @get_time: the current clock time is requested
- *
- * Callbacks performed by #RTPSource when actions need to be performed.
- */
-typedef struct {
- RTPSourcePushRTP push_rtp;
- RTPSourceClockRate clock_rate;
-} RTPSourceCallbacks;
-
-/**
- * RTPSource:
- *
- * A source in the #RTPSession
- */
-struct _RTPSource {
- GObject object;
-
- /*< private >*/
- guint32 ssrc;
-
- gint probation;
- gboolean validated;
- gboolean internal;
- gboolean is_csrc;
- gboolean is_sender;
-
- guint8 *sdes[9];
- guint sdes_len[9];
-
- gboolean received_bye;
- gchar *bye_reason;
-
- gboolean have_rtp_from;
- GstNetAddress rtp_from;
- gboolean have_rtcp_from;
- GstNetAddress rtcp_from;
-
- gint payload;
- GstCaps *caps;
- gint clock_rate;
- gint32 seqnum_base;
-
- GstClockTime bye_time;
- GstClockTime last_activity;
- GstClockTime last_rtp_activity;
-
- GstClockTime last_rtptime;
- GstClockTime last_ntpnstime;
-
- /* for bitrate estimation */
- guint64 bitrate;
- GstClockTime prev_ntpnstime;
- guint64 bytes_sent;
-
- GQueue *packets;
-
- RTPSourceCallbacks callbacks;
- gpointer user_data;
-
- RTPSourceStats stats;
-};
-
-struct _RTPSourceClass {
- GObjectClass parent_class;
-};
-
-GType rtp_source_get_type (void);
-
-/* managing lifetime of sources */
-RTPSource* rtp_source_new (guint32 ssrc);
-void rtp_source_set_callbacks (RTPSource *src, RTPSourceCallbacks *cb, gpointer data);
-
-/* properties */
-guint32 rtp_source_get_ssrc (RTPSource *src);
-
-void rtp_source_set_as_csrc (RTPSource *src);
-gboolean rtp_source_is_as_csrc (RTPSource *src);
-
-gboolean rtp_source_is_active (RTPSource *src);
-gboolean rtp_source_is_validated (RTPSource *src);
-gboolean rtp_source_is_sender (RTPSource *src);
-
-gboolean rtp_source_received_bye (RTPSource *src);
-gchar * rtp_source_get_bye_reason (RTPSource *src);
-
-void rtp_source_update_caps (RTPSource *src, GstCaps *caps);
-
-/* SDES info */
-gboolean rtp_source_set_sdes (RTPSource *src, GstRTCPSDESType type,
- const guint8 *data, guint len);
-gboolean rtp_source_set_sdes_string (RTPSource *src, GstRTCPSDESType type,
- const gchar *data);
-gboolean rtp_source_get_sdes (RTPSource *src, GstRTCPSDESType type,
- guint8 **data, guint *len);
-gchar* rtp_source_get_sdes_string (RTPSource *src, GstRTCPSDESType type);
-
-GstStructure * rtp_source_get_sdes_struct (RTPSource * src);
-void rtp_source_set_sdes_struct (RTPSource * src, const GstStructure *sdes);
-
-/* handling network address */
-void rtp_source_set_rtp_from (RTPSource *src, GstNetAddress *address);
-void rtp_source_set_rtcp_from (RTPSource *src, GstNetAddress *address);
-
-/* handling RTP */
-GstFlowReturn rtp_source_process_rtp (RTPSource *src, GstBuffer *buffer, RTPArrivalStats *arrival);
-
-GstFlowReturn rtp_source_send_rtp (RTPSource *src, gpointer data, gboolean is_list, guint64 ntpnstime);
-
-/* RTCP messages */
-void rtp_source_process_bye (RTPSource *src, const gchar *reason);
-void rtp_source_process_sr (RTPSource *src, GstClockTime time, guint64 ntptime,
- guint32 rtptime, guint32 packet_count, guint32 octet_count);
-void rtp_source_process_rb (RTPSource *src, GstClockTime time, guint8 fractionlost,
- gint32 packetslost, guint32 exthighestseq, guint32 jitter,
- guint32 lsr, guint32 dlsr);
-
-gboolean rtp_source_get_new_sr (RTPSource *src, guint64 ntpnstime, guint64 *ntptime,
- guint32 *rtptime, guint32 *packet_count,
- guint32 *octet_count);
-gboolean rtp_source_get_new_rb (RTPSource *src, GstClockTime time, guint8 *fractionlost,
- gint32 *packetslost, guint32 *exthighestseq, guint32 *jitter,
- guint32 *lsr, guint32 *dlsr);
-
-gboolean rtp_source_get_last_sr (RTPSource *src, GstClockTime *time, guint64 *ntptime,
- guint32 *rtptime, guint32 *packet_count,
- guint32 *octet_count);
-gboolean rtp_source_get_last_rb (RTPSource *src, guint8 *fractionlost, gint32 *packetslost,
- guint32 *exthighestseq, guint32 *jitter,
- guint32 *lsr, guint32 *dlsr, guint32 *round_trip);
-
-void rtp_source_reset (RTPSource * src);
-
-#endif /* __RTP_SOURCE_H__ */
+++ /dev/null
-/* GStreamer
- * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#include "rtpstats.h"
-
-/**
- * rtp_stats_init_defaults:
- * @stats: an #RTPSessionStats struct
- *
- * Initialize @stats with its default values.
- */
-void
-rtp_stats_init_defaults (RTPSessionStats * stats)
-{
- stats->bandwidth = RTP_STATS_BANDWIDTH;
- stats->sender_fraction = RTP_STATS_SENDER_FRACTION;
- stats->receiver_fraction = RTP_STATS_RECEIVER_FRACTION;
- stats->rtcp_bandwidth = RTP_STATS_RTCP_BANDWIDTH;
- stats->min_interval = RTP_STATS_MIN_INTERVAL;
- stats->bye_timeout = RTP_STATS_BYE_TIMEOUT;
-}
-
-/**
- * rtp_stats_calculate_rtcp_interval:
- * @stats: an #RTPSessionStats struct
- * @sender: if we are a sender
- * @first: if this is the first time
- *
- * Calculate the RTCP interval. The result of this function is the amount of
- * time to wait (in nanoseconds) before sending a new RTCP message.
- *
- * Returns: the RTCP interval.
- */
-GstClockTime
-rtp_stats_calculate_rtcp_interval (RTPSessionStats * stats, gboolean we_send,
- gboolean first)
-{
- gdouble members, senders, n;
- gdouble avg_rtcp_size, rtcp_bw;
- gdouble interval;
- gdouble rtcp_min_time;
-
- /* Very first call at application start-up uses half the min
- * delay for quicker notification while still allowing some time
- * before reporting for randomization and to learn about other
- * sources so the report interval will converge to the correct
- * interval more quickly.
- */
- rtcp_min_time = stats->min_interval;
- if (first)
- rtcp_min_time /= 2.0;
-
- /* Dedicate a fraction of the RTCP bandwidth to senders unless
- * the number of senders is large enough that their share is
- * more than that fraction.
- */
- n = members = stats->active_sources;
- senders = (gdouble) stats->sender_sources;
- rtcp_bw = stats->rtcp_bandwidth;
-
- if (senders <= members * RTP_STATS_SENDER_FRACTION) {
- if (we_send) {
- rtcp_bw *= RTP_STATS_SENDER_FRACTION;
- n = senders;
- } else {
- rtcp_bw *= RTP_STATS_RECEIVER_FRACTION;
- n -= senders;
- }
- }
-
- avg_rtcp_size = stats->avg_rtcp_packet_size / 16.0;
- /*
- * The effective number of sites times the average packet size is
- * the total number of octets sent when each site sends a report.
- * Dividing this by the effective bandwidth gives the time
- * interval over which those packets must be sent in order to
- * meet the bandwidth target, with a minimum enforced. In that
- * time interval we send one report so this time is also our
- * average time between reports.
- */
- interval = avg_rtcp_size * n / rtcp_bw;
- if (interval < rtcp_min_time)
- interval = rtcp_min_time;
-
- return interval * GST_SECOND;
-}
-
-/**
- * rtp_stats_add_rtcp_jitter:
- * @stats: an #RTPSessionStats struct
- * @interval: an RTCP interval
- *
- * Apply a random jitter to the @interval. @interval is typically obtained with
- * rtp_stats_calculate_rtcp_interval().
- *
- * Returns: the new RTCP interval.
- */
-GstClockTime
-rtp_stats_add_rtcp_jitter (RTPSessionStats * stats, GstClockTime interval)
-{
- gdouble temp;
-
- /* see RFC 3550 p 30
- * To compensate for "unconditional reconsideration" converging to a
- * value below the intended average.
- */
-#define COMPENSATION (2.71828 - 1.5);
-
- temp = (interval * g_random_double_range (0.5, 1.5)) / COMPENSATION;
-
- return (GstClockTime) temp;
-}
-
-
-/**
- * rtp_stats_calculate_bye_interval:
- * @stats: an #RTPSessionStats struct
- *
- * Calculate the BYE interval. The result of this function is the amount of
- * time to wait (in nanoseconds) before sending a BYE message.
- *
- * Returns: the BYE interval.
- */
-GstClockTime
-rtp_stats_calculate_bye_interval (RTPSessionStats * stats)
-{
- gdouble members;
- gdouble avg_rtcp_size, rtcp_bw;
- gdouble interval;
- gdouble rtcp_min_time;
-
- /* no interval when we have less than 50 members */
- if (stats->active_sources < 50)
- return 0;
-
- rtcp_min_time = (stats->min_interval) / 2.0;
-
- /* Dedicate a fraction of the RTCP bandwidth to senders unless
- * the number of senders is large enough that their share is
- * more than that fraction.
- */
- members = stats->bye_members;
- rtcp_bw = stats->rtcp_bandwidth * RTP_STATS_RECEIVER_FRACTION;
-
- avg_rtcp_size = stats->avg_rtcp_packet_size / 16.0;
- /*
- * The effective number of sites times the average packet size is
- * the total number of octets sent when each site sends a report.
- * Dividing this by the effective bandwidth gives the time
- * interval over which those packets must be sent in order to
- * meet the bandwidth target, with a minimum enforced. In that
- * time interval we send one report so this time is also our
- * average time between reports.
- */
- interval = avg_rtcp_size * members / rtcp_bw;
- if (interval < rtcp_min_time)
- interval = rtcp_min_time;
-
- return interval * GST_SECOND;
-}
+++ /dev/null
-/* GStreamer
- * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#ifndef __RTP_STATS_H__
-#define __RTP_STATS_H__
-
-#include <gst/gst.h>
-#include <gst/netbuffer/gstnetbuffer.h>
-
-/**
- * RTPSenderReport:
- *
- * A sender report structure.
- */
-typedef struct {
- gboolean is_valid;
- guint64 ntptime;
- guint32 rtptime;
- guint32 packet_count;
- guint32 octet_count;
- GstClockTime time;
-} RTPSenderReport;
-
-/**
- * RTPReceiverReport:
- *
- * A receiver report structure.
- */
-typedef struct {
- gboolean is_valid;
- guint32 ssrc; /* who the report is from */
- guint8 fractionlost;
- guint32 packetslost;
- guint32 exthighestseq;
- guint32 jitter;
- guint32 lsr;
- guint32 dlsr;
- guint32 round_trip;
-} RTPReceiverReport;
-
-/**
- * RTPArrivalStats:
- * @time: arrival time of a packet according to the system clock
- * @running_time: arrival time of a packet as buffer running_time
- * @ntpnstime: arrival time of a packet as NTP time in nanoseconds
- * @have_address: if the @address field contains a valid address
- * @address: address of the sender of the packet
- * @bytes: bytes of the packet including lowlevel overhead
- * @payload_len: bytes of the RTP payload
- *
- * Structure holding information about the arrival stats of a packet.
- */
-typedef struct {
- GstClockTime time;
- GstClockTime running_time;
- guint64 ntpnstime;
- gboolean have_address;
- GstNetAddress address;
- guint bytes;
- guint payload_len;
-} RTPArrivalStats;
-
-/**
- * RTPSourceStats:
- * @packetsreceived: number of received packets in total
- * @prevpacketsreceived: number of packets received in previous reporting
- * interval
- * @octetsreceived: number of payload bytes received
- * @bytesreceived: number of total bytes received including headers and lower
- * protocol level overhead
- * @max_seqnr: highest sequence number received
- * @transit: previous transit time used for calculating @jitter
- * @jitter: current jitter
- * @prev_rtptime: previous time when an RTP packet was received
- * @prev_rtcptime: previous time when an RTCP packet was received
- * @last_rtptime: time when last RTP packet received
- * @last_rtcptime: time when last RTCP packet received
- * @curr_rr: index of current @rr block
- * @rr: previous and current receiver report block
- * @curr_sr: index of current @sr block
- * @sr: previous and current sender report block
- *
- * Stats about a source.
- */
-typedef struct {
- guint64 packets_received;
- guint64 octets_received;
- guint64 bytes_received;
-
- guint32 prev_expected;
- guint32 prev_received;
-
- guint16 max_seq;
- guint64 cycles;
- guint32 base_seq;
- guint32 bad_seq;
- guint32 transit;
- guint32 jitter;
-
- guint64 packets_sent;
- guint64 octets_sent;
-
- /* when we received stuff */
- GstClockTime prev_rtptime;
- GstClockTime prev_rtcptime;
- GstClockTime last_rtptime;
- GstClockTime last_rtcptime;
-
- /* sender and receiver reports */
- gint curr_rr;
- RTPReceiverReport rr[2];
- gint curr_sr;
- RTPSenderReport sr[2];
-} RTPSourceStats;
-
-#define RTP_STATS_BANDWIDTH 64000.0
-#define RTP_STATS_RTCP_BANDWIDTH 3000.0
-/*
- * Minimum average time between RTCP packets from this site (in
- * seconds). This time prevents the reports from `clumping' when
- * sessions are small and the law of large numbers isn't helping
- * to smooth out the traffic. It also keeps the report interval
- * from becoming ridiculously small during transient outages like
- * a network partition.
- */
-#define RTP_STATS_MIN_INTERVAL 5.0
-/*
- * Fraction of the RTCP bandwidth to be shared among active
- * senders. (This fraction was chosen so that in a typical
- * session with one or two active senders, the computed report
- * time would be roughly equal to the minimum report time so that
- * we don't unnecessarily slow down receiver reports.) The
- * receiver fraction must be 1 - the sender fraction.
- */
-#define RTP_STATS_SENDER_FRACTION (0.25)
-#define RTP_STATS_RECEIVER_FRACTION (1.0 - RTP_STATS_SENDER_FRACTION)
-
-/*
- * When receiving a BYE from a source, remove the source from the database
- * after this timeout.
- */
-#define RTP_STATS_BYE_TIMEOUT (2 * GST_SECOND)
-
-/*
- * The maximum number of missing packets we tollerate. These are packets with a
- * sequence number bigger than the last seen packet.
- */
-#define RTP_MAX_DROPOUT 3000
-/*
- * The maximum number of misordered packets we tollerate. These are packets with
- * a sequence number smaller than the last seen packet.
- */
-#define RTP_MAX_MISORDER 100
-
-/**
- * RTPSessionStats:
- *
- * Stats kept for a session and used to produce RTCP packet timeouts.
- */
-typedef struct {
- gdouble bandwidth;
- gdouble sender_fraction;
- gdouble receiver_fraction;
- gdouble rtcp_bandwidth;
- gdouble min_interval;
- GstClockTime bye_timeout;
- guint sender_sources;
- guint active_sources;
- guint avg_rtcp_packet_size;
- guint bye_members;
-} RTPSessionStats;
-
-void rtp_stats_init_defaults (RTPSessionStats *stats);
-
-GstClockTime rtp_stats_calculate_rtcp_interval (RTPSessionStats *stats, gboolean sender, gboolean first);
-GstClockTime rtp_stats_add_rtcp_jitter (RTPSessionStats *stats, GstClockTime interval);
-GstClockTime rtp_stats_calculate_bye_interval (RTPSessionStats *stats);
-
-#endif /* __RTP_STATS_H__ */
elements/asfmux \
elements/legacyresample \
elements/qtmux \
- elements/rtpbin \
- elements/rtpbin_buffer_list \
elements/selector \
elements/shapewipe \
elements/mxfdemux \
-lgstinterfaces-@GST_MAJORMINOR@
elements_camerabin_SOURCES = elements/camerabin.c
-elements_rtpbin_buffer_list_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) \
- $(ERROR_CFLAGS) $(GST_CHECK_CFLAGS)
-elements_rtpbin_buffer_list_LDADD = $(GST_PLUGINS_BASE_LIBS) \
- -lgstnetbuffer-@GST_MAJORMINOR@ -lgstrtp-@GST_MAJORMINOR@ \
- $(GST_BASE_LIBS) $(GST_LIBS_LIBS) $(GST_CHECK_LIBS)
-elements_rtpbin_buffer_list_SOURCES = elements/rtpbin_buffer_list.c
-
elements_timidity_CFLAGS = $(GST_BASE_CFLAGS) $(AM_CFLAGS)
elements_timidity_LDADD = $(GST_BASE_LIBS) $(LDADD)
rganalysis
rglimiter
rgvolume
-rtpbin
-rtpbin_buffer_list
selector
shapewipe
spectrum
+++ /dev/null
-/* GStreamer
- *
- * unit test for gstrtpbin
- *
- * Copyright (C) <2009> Wim Taymans <wim.taymans@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#include <gst/check/gstcheck.h>
-
-GST_START_TEST (test_cleanup_send)
-{
- GstElement *rtpbin;
- GstPad *rtp_sink, *rtp_src, *rtcp_src;
- GObject *session;
- gint count = 2;
-
- rtpbin = gst_element_factory_make ("gstrtpbin", "rtpbin");
-
- while (count--) {
- /* request session 0 */
- rtp_sink = gst_element_get_request_pad (rtpbin, "send_rtp_sink_0");
- fail_unless (rtp_sink != NULL);
- ASSERT_OBJECT_REFCOUNT (rtp_sink, "rtp_sink", 2);
-
- /* request again */
- rtp_sink = gst_element_get_request_pad (rtpbin, "send_rtp_sink_0");
- fail_unless (rtp_sink != NULL);
- ASSERT_OBJECT_REFCOUNT (rtp_sink, "rtp_sink", 3);
- gst_object_unref (rtp_sink);
-
- /* this static pad should be created automatically now */
- rtp_src = gst_element_get_static_pad (rtpbin, "send_rtp_src_0");
- fail_unless (rtp_src != NULL);
- ASSERT_OBJECT_REFCOUNT (rtp_src, "rtp_src", 2);
-
- /* we should be able to get an internal session 0 now */
- g_signal_emit_by_name (rtpbin, "get-internal-session", 0, &session);
- fail_unless (session != NULL);
- g_object_unref (session);
-
- /* get the send RTCP pad too */
- rtcp_src = gst_element_get_request_pad (rtpbin, "send_rtcp_src_0");
- fail_unless (rtcp_src != NULL);
- ASSERT_OBJECT_REFCOUNT (rtcp_src, "rtcp_src", 2);
-
- /* second time */
- rtcp_src = gst_element_get_request_pad (rtpbin, "send_rtcp_src_0");
- fail_unless (rtcp_src != NULL);
- ASSERT_OBJECT_REFCOUNT (rtcp_src, "rtcp_src", 3);
- gst_object_unref (rtcp_src);
-
- gst_element_release_request_pad (rtpbin, rtp_sink);
- /* we should only have our refs to the pads now */
- ASSERT_OBJECT_REFCOUNT (rtp_sink, "rtp_sink", 1);
- ASSERT_OBJECT_REFCOUNT (rtp_src, "rtp_src", 1);
- ASSERT_OBJECT_REFCOUNT (rtcp_src, "rtp_src", 2);
-
- /* the other pad should be gone now */
- fail_unless (gst_element_get_static_pad (rtpbin, "send_rtp_src_0") == NULL);
-
- /* internal session should still be there */
- g_signal_emit_by_name (rtpbin, "get-internal-session", 0, &session);
- fail_unless (session != NULL);
- g_object_unref (session);
-
- /* release the RTCP pad */
- gst_element_release_request_pad (rtpbin, rtcp_src);
- /* we should only have our refs to the pads now */
- ASSERT_OBJECT_REFCOUNT (rtp_sink, "rtp_sink", 1);
- ASSERT_OBJECT_REFCOUNT (rtp_src, "rtp_src", 1);
- ASSERT_OBJECT_REFCOUNT (rtcp_src, "rtp_src", 1);
-
- /* the session should be gone now */
- g_signal_emit_by_name (rtpbin, "get-internal-session", 0, &session);
- fail_unless (session == NULL);
-
- /* unref the request pad and the static pad */
- gst_object_unref (rtp_sink);
- gst_object_unref (rtp_src);
- gst_object_unref (rtcp_src);
- }
-
- gst_object_unref (rtpbin);
-}
-
-GST_END_TEST;
-
-typedef struct
-{
- guint16 seqnum;
- gboolean pad_added;
- GstPad *pad;
- GMutex *lock;
- GCond *cond;
- GstPad *sinkpad;
- GList *pads;
-} CleanupData;
-
-static void
-init_data (CleanupData * data)
-{
- data->seqnum = 10;
- data->pad_added = FALSE;
- data->lock = g_mutex_new ();
- data->cond = g_cond_new ();
- data->pads = NULL;
-}
-
-static void
-clean_data (CleanupData * data)
-{
- g_list_foreach (data->pads, (GFunc) gst_object_unref, NULL);
- g_list_free (data->pads);
- g_mutex_free (data->lock);
- g_cond_free (data->cond);
-}
-
-static guint8 rtp_packet[] = { 0x80, 0x60, 0x94, 0xbc, 0x8f, 0x37, 0x4e, 0xb8,
- 0x44, 0xa8, 0xf3, 0x7c, 0x06, 0x6a, 0x0c, 0xce,
- 0x13, 0x25, 0x19, 0x69, 0x1f, 0x93, 0x25, 0x9d,
- 0x2b, 0x82, 0x31, 0x3b, 0x36, 0xc1, 0x3c, 0x13
-};
-
-static GstBuffer *
-make_rtp_packet (CleanupData * data)
-{
- static GstCaps *caps = NULL;
- GstBuffer *result;
- guint8 *datap;
-
- if (caps == NULL) {
- caps = gst_caps_from_string ("application/x-rtp,"
- "media=(string)audio, clock-rate=(int)44100, "
- "encoding-name=(string)L16, encoding-params=(string)1, channels=(int)1");
- data->seqnum = 0;
- }
-
- result = gst_buffer_new_and_alloc (sizeof (rtp_packet));
- datap = GST_BUFFER_DATA (result);
- memcpy (datap, rtp_packet, sizeof (rtp_packet));
-
- datap[2] = (data->seqnum >> 8) & 0xff;
- datap[3] = data->seqnum & 0xff;
-
- data->seqnum++;
-
- gst_buffer_set_caps (result, caps);
-
- return result;
-}
-
-static GstFlowReturn
-dummy_chain (GstPad * pad, GstBuffer * buffer)
-{
- gst_buffer_unref (buffer);
-
- return GST_FLOW_OK;
-}
-
-static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
- GST_PAD_SINK,
- GST_PAD_ALWAYS,
- GST_STATIC_CAPS ("application/x-rtp"));
-
-
-static GstPad *
-make_sinkpad (CleanupData * data)
-{
- GstPad *pad;
-
- pad = gst_pad_new_from_static_template (&sink_factory, "sink");
-
- gst_pad_set_chain_function (pad, dummy_chain);
- gst_pad_set_active (pad, TRUE);
-
- data->pads = g_list_prepend (data->pads, pad);
-
- return pad;
-}
-
-static void
-pad_added_cb (GstElement * rtpbin, GstPad * pad, CleanupData * data)
-{
- GstPad *sinkpad;
-
- GST_DEBUG ("pad added %s:%s\n", GST_DEBUG_PAD_NAME (pad));
-
- if (GST_PAD_IS_SINK (pad))
- return;
-
- fail_unless (data->pad_added == FALSE);
-
- sinkpad = make_sinkpad (data);
- fail_unless (gst_pad_link (pad, sinkpad) == GST_PAD_LINK_OK);
-
- g_mutex_lock (data->lock);
- data->pad_added = TRUE;
- data->pad = pad;
- g_cond_signal (data->cond);
- g_mutex_unlock (data->lock);
-}
-
-static void
-pad_removed_cb (GstElement * rtpbin, GstPad * pad, CleanupData * data)
-{
- GST_DEBUG ("pad removed %s:%s\n", GST_DEBUG_PAD_NAME (pad));
-
- if (data->pad != pad)
- return;
-
- fail_unless (data->pad_added == TRUE);
-
- g_mutex_lock (data->lock);
- data->pad_added = FALSE;
- g_cond_signal (data->cond);
- g_mutex_unlock (data->lock);
-}
-
-GST_START_TEST (test_cleanup_recv)
-{
- GstElement *rtpbin;
- GstPad *rtp_sink;
- CleanupData data;
- GstStateChangeReturn ret;
- GstFlowReturn res;
- GstBuffer *buffer;
- gint count = 2;
-
- init_data (&data);
-
- rtpbin = gst_element_factory_make ("gstrtpbin", "rtpbin");
-
- g_signal_connect (rtpbin, "pad-added", (GCallback) pad_added_cb, &data);
- g_signal_connect (rtpbin, "pad-removed", (GCallback) pad_removed_cb, &data);
-
- ret = gst_element_set_state (rtpbin, GST_STATE_PLAYING);
- fail_unless (ret == GST_STATE_CHANGE_SUCCESS);
-
- while (count--) {
- /* request session 0 */
- rtp_sink = gst_element_get_request_pad (rtpbin, "recv_rtp_sink_0");
- fail_unless (rtp_sink != NULL);
- ASSERT_OBJECT_REFCOUNT (rtp_sink, "rtp_sink", 2);
-
- /* no sourcepads are created yet */
- fail_unless (rtpbin->numsinkpads == 1);
- fail_unless (rtpbin->numsrcpads == 0);
-
- buffer = make_rtp_packet (&data);
- res = gst_pad_chain (rtp_sink, buffer);
- GST_DEBUG ("res %d, %s\n", res, gst_flow_get_name (res));
- fail_unless (res == GST_FLOW_OK);
-
- buffer = make_rtp_packet (&data);
- res = gst_pad_chain (rtp_sink, buffer);
- GST_DEBUG ("res %d, %s\n", res, gst_flow_get_name (res));
- fail_unless (res == GST_FLOW_OK);
-
- /* we wait for the new pad to appear now */
- g_mutex_lock (data.lock);
- while (!data.pad_added)
- g_cond_wait (data.cond, data.lock);
- g_mutex_unlock (data.lock);
-
- /* sourcepad created now */
- fail_unless (rtpbin->numsinkpads == 1);
- fail_unless (rtpbin->numsrcpads == 1);
-
- /* remove the session */
- gst_element_release_request_pad (rtpbin, rtp_sink);
- gst_object_unref (rtp_sink);
-
- /* pad should be gone now */
- g_mutex_lock (data.lock);
- while (data.pad_added)
- g_cond_wait (data.cond, data.lock);
- g_mutex_unlock (data.lock);
-
- /* nothing left anymore now */
- fail_unless (rtpbin->numsinkpads == 0);
- fail_unless (rtpbin->numsrcpads == 0);
- }
-
- ret = gst_element_set_state (rtpbin, GST_STATE_NULL);
- fail_unless (ret == GST_STATE_CHANGE_SUCCESS);
-
- gst_object_unref (rtpbin);
-
- clean_data (&data);
-}
-
-GST_END_TEST;
-
-GST_START_TEST (test_cleanup_recv2)
-{
- GstElement *rtpbin;
- GstPad *rtp_sink;
- CleanupData data;
- GstStateChangeReturn ret;
- GstFlowReturn res;
- GstBuffer *buffer;
- gint count = 2;
-
- init_data (&data);
-
- rtpbin = gst_element_factory_make ("gstrtpbin", "rtpbin");
-
- g_signal_connect (rtpbin, "pad-added", (GCallback) pad_added_cb, &data);
- g_signal_connect (rtpbin, "pad-removed", (GCallback) pad_removed_cb, &data);
-
- ret = gst_element_set_state (rtpbin, GST_STATE_PLAYING);
- fail_unless (ret == GST_STATE_CHANGE_SUCCESS);
-
- /* request session 0 */
- rtp_sink = gst_element_get_request_pad (rtpbin, "recv_rtp_sink_0");
- fail_unless (rtp_sink != NULL);
- ASSERT_OBJECT_REFCOUNT (rtp_sink, "rtp_sink", 2);
-
- while (count--) {
- /* no sourcepads are created yet */
- fail_unless (rtpbin->numsinkpads == 1);
- fail_unless (rtpbin->numsrcpads == 0);
-
- buffer = make_rtp_packet (&data);
- res = gst_pad_chain (rtp_sink, buffer);
- GST_DEBUG ("res %d, %s\n", res, gst_flow_get_name (res));
- fail_unless (res == GST_FLOW_OK);
-
- buffer = make_rtp_packet (&data);
- res = gst_pad_chain (rtp_sink, buffer);
- GST_DEBUG ("res %d, %s\n", res, gst_flow_get_name (res));
- fail_unless (res == GST_FLOW_OK);
-
- /* we wait for the new pad to appear now */
- g_mutex_lock (data.lock);
- while (!data.pad_added)
- g_cond_wait (data.cond, data.lock);
- g_mutex_unlock (data.lock);
-
- /* sourcepad created now */
- fail_unless (rtpbin->numsinkpads == 1);
- fail_unless (rtpbin->numsrcpads == 1);
-
- /* change state */
- ret = gst_element_set_state (rtpbin, GST_STATE_NULL);
- fail_unless (ret == GST_STATE_CHANGE_SUCCESS);
-
- /* pad should be gone now */
- g_mutex_lock (data.lock);
- while (data.pad_added)
- g_cond_wait (data.cond, data.lock);
- g_mutex_unlock (data.lock);
-
- /* back to playing for the next round */
- ret = gst_element_set_state (rtpbin, GST_STATE_PLAYING);
- fail_unless (ret == GST_STATE_CHANGE_SUCCESS);
- }
-
- /* remove the session */
- gst_element_release_request_pad (rtpbin, rtp_sink);
- gst_object_unref (rtp_sink);
-
- /* nothing left anymore now */
- fail_unless (rtpbin->numsinkpads == 0);
- fail_unless (rtpbin->numsrcpads == 0);
-
- ret = gst_element_set_state (rtpbin, GST_STATE_NULL);
- fail_unless (ret == GST_STATE_CHANGE_SUCCESS);
-
- gst_object_unref (rtpbin);
-
- clean_data (&data);
-}
-
-GST_END_TEST;
-
-Suite *
-gstrtpbin_suite (void)
-{
- Suite *s = suite_create ("gstrtpbin");
- TCase *tc_chain = tcase_create ("general");
-
- suite_add_tcase (s, tc_chain);
- tcase_add_test (tc_chain, test_cleanup_send);
- tcase_add_test (tc_chain, test_cleanup_recv);
- tcase_add_test (tc_chain, test_cleanup_recv2);
-
- return s;
-}
-
-int
-main (int argc, char **argv)
-{
- int nf;
-
- Suite *s = gstrtpbin_suite ();
- SRunner *sr = srunner_create (s);
-
- gst_check_init (&argc, &argv);
-
- srunner_run_all (sr, CK_NORMAL);
- nf = srunner_ntests_failed (sr);
- srunner_free (sr);
-
- return nf;
-}
+++ /dev/null
-/* GStreamer
- *
- * Unit test for gstrtpbin sending rtp packets using GstBufferList.
- * Copyright (C) 2009 Branko Subasic <branko dot subasic at axis dot com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#include <gst/check/gstcheck.h>
-
-#include <gst/rtp/gstrtpbuffer.h>
-
-
-
-/* This test makes sure that RTP packets sent as buffer lists are sent through
- * the rtpbin as they are supposed to, and not corrupted in any way.
- */
-
-
-#define TEST_CAPS \
- "application/x-rtp, " \
- "media=(string)video, " \
- "clock-rate=(int)90000, " \
- "encoding-name=(string)H264, " \
- "profile-level-id=(string)4d4015, " \
- "payload=(int)96, " \
- "ssrc=(guint)2633237432, " \
- "clock-base=(guint)1868267015, " \
- "seqnum-base=(guint)54229"
-
-
-/* RTP headers and the first 2 bytes of the payload (FU indicator and FU header)
- */
-static const guint8 rtp_header[2][14] = {
- {0x80, 0x60, 0xbb, 0xb7, 0x5c, 0xe9, 0x09,
- 0x0d, 0xf5, 0x9c, 0x43, 0x55, 0x1c, 0x86},
- {0x80, 0x60, 0xbb, 0xb8, 0x5c, 0xe9, 0x09,
- 0x0d, 0xf5, 0x9c, 0x43, 0x55, 0x1c, 0x46}
-};
-
-static const guint rtp_header_len[] = {
- sizeof rtp_header[0],
- sizeof rtp_header[1]
-};
-
-static GstBuffer *header_buffer[2] = { NULL, NULL };
-
-
-/* Some payload.
- */
-static char *payload =
- "0123456789ABSDEF0123456789ABSDEF0123456789ABSDEF0123456789ABSDEF0123456789ABSDEF"
- "0123456789ABSDEF0123456789ABSDEF0123456789ABSDEF0123456789ABSDEF0123456789ABSDEF"
- "0123456789ABSDEF0123456789ABSDEF0123456789ABSDEF0123456789ABSDEF0123456789ABSDEF"
- "0123456789ABSDEF0123456789ABSDEF0123456789ABSDEF0123456789ABSDEF0123456789ABSDEF"
- "0123456789ABSDEF0123456789ABSDEF0123456789ABSDEF0123456789ABSDEF0123456789ABSDEF"
- "0123456789ABSDEF0123456789ABSDEF0123456789ABSDEF0123456789ABSDEF0123456789ABSDEF"
- "0123456789ABSDEF0123456";
-
-static const guint payload_offset[] = {
- 0, 498
-};
-
-static const guint payload_len[] = {
- 498, 5
-};
-
-
-static GstBuffer *original_buffer = NULL;
-
-static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
- GST_PAD_SINK,
- GST_PAD_ALWAYS,
- GST_STATIC_CAPS ("application/x-rtp"));
-
-static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
- GST_PAD_SRC,
- GST_PAD_ALWAYS,
- GST_STATIC_CAPS ("application/x-rtp"));
-
-
-static GstBuffer *
-_create_original_buffer (void)
-{
- GstCaps *caps;
-
- if (original_buffer != NULL)
- return original_buffer;
-
- original_buffer = gst_buffer_new ();
- fail_unless (original_buffer != NULL);
-
- gst_buffer_set_data (original_buffer, (guint8 *) payload, strlen (payload));
- GST_BUFFER_TIMESTAMP (original_buffer) =
- gst_clock_get_internal_time (gst_system_clock_obtain ());
-
- caps = gst_caps_from_string (TEST_CAPS);
- fail_unless (caps != NULL);
- gst_buffer_set_caps (original_buffer, caps);
- gst_caps_unref (caps);
-
- return original_buffer;
-}
-
-static GstBufferList *
-_create_buffer_list (void)
-{
- GstBufferList *list;
- GstBufferListIterator *it;
- GstBuffer *orig_buffer;
- GstBuffer *buffer;
-
- orig_buffer = _create_original_buffer ();
- fail_if (orig_buffer == NULL);
-
- list = gst_buffer_list_new ();
- fail_if (list == NULL);
-
- it = gst_buffer_list_iterate (list);
- fail_if (it == NULL);
-
- /*** First group, i.e. first packet. **/
- gst_buffer_list_iterator_add_group (it);
-
- /* Create buffer with RTP header and add it to the 1st group */
- buffer = gst_buffer_new ();
- GST_BUFFER_MALLOCDATA (buffer) = g_memdup (&rtp_header[0], rtp_header_len[0]);
- GST_BUFFER_DATA (buffer) = GST_BUFFER_MALLOCDATA (buffer);
- GST_BUFFER_SIZE (buffer) = rtp_header_len[0];
- gst_buffer_copy_metadata (buffer, orig_buffer, GST_BUFFER_COPY_ALL);
- header_buffer[0] = buffer;
- gst_buffer_list_iterator_add (it, buffer);
-
- /* Create the payload buffer and add it to the 1st group
- */
- buffer =
- gst_buffer_create_sub (orig_buffer, payload_offset[0], payload_len[0]);
- fail_if (buffer == NULL);
- gst_buffer_list_iterator_add (it, buffer);
-
-
- /*** Second group, i.e. second packet. ***/
-
- /* Create a new group to hold the rtp header and the payload */
- gst_buffer_list_iterator_add_group (it);
-
- /* Create buffer with RTP header and add it to the 2nd group */
- buffer = gst_buffer_new ();
- GST_BUFFER_MALLOCDATA (buffer) = g_memdup (&rtp_header[1], rtp_header_len[1]);
- GST_BUFFER_DATA (buffer) = GST_BUFFER_MALLOCDATA (buffer);
- GST_BUFFER_SIZE (buffer) = rtp_header_len[1];
- gst_buffer_copy_metadata (buffer, orig_buffer, GST_BUFFER_COPY_ALL);
- header_buffer[1] = buffer;
-
- /* Add the rtp header to the buffer list */
- gst_buffer_list_iterator_add (it, buffer);
-
- /* Create the payload buffer and add it to the 2d group
- */
- buffer =
- gst_buffer_create_sub (orig_buffer, payload_offset[1], payload_len[1]);
- fail_if (buffer == NULL);
- gst_buffer_list_iterator_add (it, buffer);
-
- gst_buffer_list_iterator_free (it);
-
- return list;
-}
-
-
-static void
-_check_header (GstBuffer * buffer, guint index)
-{
- guint8 *data;
-
- fail_if (buffer == NULL);
- fail_unless (index < 2);
-
- fail_unless (GST_BUFFER_SIZE (buffer) == rtp_header_len[index]);
-
- /* Can't do a memcmp() on the whole header, cause the SSRC (bytes 8-11) will
- * most likely be changed in gstrtpbin.
- */
- fail_unless ((data = GST_BUFFER_DATA (buffer)) != NULL);
- fail_unless_equals_uint64 (*(guint64 *) data, *(guint64 *) rtp_header[index]);
- fail_unless (*(guint16 *) (data + 12) ==
- *(guint16 *) (rtp_header[index] + 12));
-}
-
-
-static void
-_check_payload (GstBuffer * buffer, guint index)
-{
- fail_if (buffer == NULL);
- fail_unless (index < 2);
-
- fail_unless (GST_BUFFER_SIZE (buffer) == payload_len[index]);
- fail_if (GST_BUFFER_DATA (buffer) !=
- (gpointer) (payload + payload_offset[index]));
- fail_if (memcmp (GST_BUFFER_DATA (buffer), payload + payload_offset[index],
- payload_len[index]));
-}
-
-
-static void
-_check_group (GstBufferListIterator * it, guint index, GstCaps * caps)
-{
- GstBuffer *buffer;
-
- fail_unless (it != NULL);
- fail_unless (gst_buffer_list_iterator_n_buffers (it) == 2);
- fail_unless (caps != NULL);
-
- fail_unless ((buffer = gst_buffer_list_iterator_next (it)) != NULL);
-
- fail_unless (GST_BUFFER_TIMESTAMP (buffer) ==
- GST_BUFFER_TIMESTAMP (original_buffer));
-
- fail_unless (gst_caps_is_equal (GST_BUFFER_CAPS (original_buffer),
- GST_BUFFER_CAPS (buffer)));
-
- _check_header (buffer, index);
-
- fail_unless ((buffer = gst_buffer_list_iterator_next (it)) != NULL);
- _check_payload (buffer, index);
-}
-
-
-static GstFlowReturn
-_sink_chain_list (GstPad * pad, GstBufferList * list)
-{
- GstCaps *caps;
- GstBufferListIterator *it;
-
- caps = gst_caps_from_string (TEST_CAPS);
- fail_unless (caps != NULL);
-
- fail_unless (GST_IS_BUFFER_LIST (list));
- fail_unless (gst_buffer_list_n_groups (list) == 2);
-
- it = gst_buffer_list_iterate (list);
- fail_if (it == NULL);
-
- fail_unless (gst_buffer_list_iterator_next_group (it));
- _check_group (it, 0, caps);
-
- fail_unless (gst_buffer_list_iterator_next_group (it));
- _check_group (it, 1, caps);
-
- gst_caps_unref (caps);
- gst_buffer_list_iterator_free (it);
-
- gst_buffer_list_unref (list);
-
- return GST_FLOW_OK;
-}
-
-
-static void
-_set_chain_functions (GstPad * pad)
-{
- gst_pad_set_chain_list_function (pad, _sink_chain_list);
-}
-
-
-GST_START_TEST (test_bufferlist)
-{
- GstElement *rtpbin;
- GstPad *sinkpad;
- GstPad *srcpad;
- GstBufferList *list;
-
- list = _create_buffer_list ();
- fail_unless (list != NULL);
-
- rtpbin = gst_check_setup_element ("gstrtpbin");
-
- srcpad =
- gst_check_setup_src_pad_by_name (rtpbin, &srctemplate, "send_rtp_sink_0");
- fail_if (srcpad == NULL);
- sinkpad =
- gst_check_setup_sink_pad_by_name (rtpbin, &sinktemplate,
- "send_rtp_src_0");
- fail_if (sinkpad == NULL);
-
- _set_chain_functions (sinkpad);
-
- gst_pad_set_active (sinkpad, TRUE);
- gst_element_set_state (rtpbin, GST_STATE_PLAYING);
- fail_unless (gst_pad_push_list (srcpad, list) == GST_FLOW_OK);
- gst_pad_set_active (sinkpad, FALSE);
-
- gst_check_teardown_pad_by_name (rtpbin, "send_rtp_src_0");
- gst_check_teardown_pad_by_name (rtpbin, "send_rtp_sink_0");
- gst_check_teardown_element (rtpbin);
-}
-
-GST_END_TEST;
-
-
-
-static Suite *
-bufferlist_suite (void)
-{
- Suite *s = suite_create ("BufferList");
-
- TCase *tc_chain = tcase_create ("general");
-
- /* time out after 30s. */
- tcase_set_timeout (tc_chain, 10);
-
- suite_add_tcase (s, tc_chain);
- tcase_add_test (tc_chain, test_bufferlist);
-
- return s;
-}
-
-GST_CHECK_MAIN (bufferlist);