'rtsp-onvif-client.c',
'rtsp-onvif-media-factory.c',
'rtsp-onvif-media.c',
++ 'rtsp-media-ext.c',
++ 'rtsp-media-factory-wfd.c',
++ 'gstwfdmessage-ext.c',
++ 'gstwfdmessage.c',
++ 'rtsp-client-ext.c',
++ 'rtsp-client-wfd.c',
++ 'rtsp-server-wfd.c',
]
rtsp_server_headers = [
'rtsp-onvif-client.h',
'rtsp-onvif-media-factory.h',
'rtsp-onvif-media.h',
++ 'rtsp-media-ext.h',
++ 'rtsp-media-factory-wfd.h',
++ 'gstwfdmessage-ext.h',
++ 'gstwfdmessage.h',
++ 'rtsp-client-ext.h',
++ 'rtsp-client-wfd.h',
++ 'rtsp-server-wfd.h',
]
install_headers(rtsp_server_headers, subdir : 'gstreamer-1.0/gst/rtsp-server')
return result;
}
+ /* Check if the given header of type double is present and, if so,
+ * put it's value in the supplied variable.
+ */
+ static GstRTSPStatusCode
+ parse_header_value_double (GstRTSPClient * client, GstRTSPContext * ctx,
+ GstRTSPHeaderField header, gboolean * present, gdouble * value)
+ {
+ GstRTSPResult res;
+ gchar *str;
+ gchar *end;
+
+ res = gst_rtsp_message_get_header (ctx->request, header, &str, 0);
+ if (res == GST_RTSP_OK) {
+ *value = g_ascii_strtod (str, &end);
+ if (end == str)
+ goto parse_header_failed;
+
+ GST_DEBUG ("client %p: got '%s', value %f", client,
+ gst_rtsp_header_as_text (header), *value);
+ *present = TRUE;
+ } else {
+ *present = FALSE;
+ }
+
+ return GST_RTSP_STS_OK;
+
+ parse_header_failed:
+ {
+ GST_ERROR ("client %p: failed parsing '%s' header", client,
+ gst_rtsp_header_as_text (header));
+ return GST_RTSP_STS_BAD_REQUEST;
+ }
+ }
+
+ /* Parse scale and speed headers, if present, and set the rate to
+ * (rate * scale * speed) */
+ static GstRTSPStatusCode
+ parse_scale_and_speed (GstRTSPClient * client, GstRTSPContext * ctx,
+ gboolean * scale_present, gboolean * speed_present, gdouble * rate,
+ GstSeekFlags * flags)
+ {
+ gdouble scale = 1.0;
+ gdouble speed = 1.0;
+ GstRTSPStatusCode status;
+
+ GST_DEBUG ("got rate %f", *rate);
+
+ status = parse_header_value_double (client, ctx, GST_RTSP_HDR_SCALE,
+ scale_present, &scale);
+ if (status != GST_RTSP_STS_OK)
+ return status;
+
+ if (*scale_present) {
+ GST_DEBUG ("got Scale %f", scale);
+ if (scale == 0)
+ goto bad_scale_value;
+ *rate *= scale;
+
+ if (ABS (scale) != 1.0)
+ *flags |= GST_SEEK_FLAG_TRICKMODE;
+ }
+
+ GST_DEBUG ("rate after parsing Scale %f", *rate);
+
+ status = parse_header_value_double (client, ctx, GST_RTSP_HDR_SPEED,
+ speed_present, &speed);
+ if (status != GST_RTSP_STS_OK)
+ return status;
+
+ if (*speed_present) {
+ GST_DEBUG ("got Speed %f", speed);
+ if (speed <= 0)
+ goto bad_speed_value;
+ *rate *= speed;
+ }
+
+ GST_DEBUG ("rate after parsing Speed %f", *rate);
+
+ return status;
+
+ bad_scale_value:
+ {
+ GST_ERROR ("client %p: bad 'Scale' header value (%f)", client, scale);
+ return GST_RTSP_STS_BAD_REQUEST;
+ }
+ bad_speed_value:
+ {
+ GST_ERROR ("client %p: bad 'Speed' header value (%f)", client, speed);
+ return GST_RTSP_STS_BAD_REQUEST;
+ }
+ }
+
+ static GstRTSPStatusCode
+ setup_play_mode (GstRTSPClient * client, GstRTSPContext * ctx,
+ GstRTSPRangeUnit * unit, gboolean * scale_present, gboolean * speed_present)
+ {
+ gchar *str;
+ GstRTSPResult res;
+ GstRTSPTimeRange *range = NULL;
+ gdouble rate = 1.0;
+ GstSeekFlags flags = GST_SEEK_FLAG_NONE;
+ GstRTSPClientClass *klass = GST_RTSP_CLIENT_GET_CLASS (client);
+ GstRTSPStatusCode rtsp_status_code;
+ GstClockTime trickmode_interval = 0;
+ gboolean enable_rate_control = TRUE;
+
+ /* parse the range header if we have one */
+ res = gst_rtsp_message_get_header (ctx->request, GST_RTSP_HDR_RANGE, &str, 0);
+ if (res == GST_RTSP_OK) {
+ gchar *seek_style = NULL;
+
+ res = gst_rtsp_range_parse (str, &range);
+ if (res != GST_RTSP_OK)
+ goto parse_range_failed;
+
+ *unit = range->unit;
+
+ /* parse seek style header, if present */
+ res = gst_rtsp_message_get_header (ctx->request, GST_RTSP_HDR_SEEK_STYLE,
+ &seek_style, 0);
+
+ if (res == GST_RTSP_OK) {
+ if (g_strcmp0 (seek_style, "RAP") == 0)
+ flags = GST_SEEK_FLAG_ACCURATE;
+ else if (g_strcmp0 (seek_style, "CoRAP") == 0)
+ flags = GST_SEEK_FLAG_KEY_UNIT;
+ else if (g_strcmp0 (seek_style, "First-Prior") == 0)
+ flags = GST_SEEK_FLAG_KEY_UNIT & GST_SEEK_FLAG_SNAP_BEFORE;
+ else if (g_strcmp0 (seek_style, "Next") == 0)
+ flags = GST_SEEK_FLAG_KEY_UNIT & GST_SEEK_FLAG_SNAP_AFTER;
+ else
+ GST_FIXME_OBJECT (client, "Add support for seek style %s", seek_style);
+ } else if (range->min.type == GST_RTSP_TIME_END) {
+ flags = GST_SEEK_FLAG_ACCURATE;
+ } else {
+ flags = GST_SEEK_FLAG_KEY_UNIT;
+ }
+
+ if (seek_style)
+ gst_rtsp_message_add_header (ctx->response, GST_RTSP_HDR_SEEK_STYLE,
+ seek_style);
+ } else {
+ flags = GST_SEEK_FLAG_ACCURATE;
+ }
+
+ /* check for scale and/or speed headers
+ * we will set the seek rate to (speed * scale) and let the media decide
+ * the resulting scale and speed. in the response we will use rate and applied
+ * rate from the resulting segment as values for the speed and scale headers
+ * respectively */
+ rtsp_status_code = parse_scale_and_speed (client, ctx, scale_present,
+ speed_present, &rate, &flags);
+ if (rtsp_status_code != GST_RTSP_STS_OK)
+ goto scale_speed_failed;
+
+ /* give the application a chance to tweak range, flags, or rate */
+ if (klass->adjust_play_mode != NULL) {
+ rtsp_status_code =
+ klass->adjust_play_mode (client, ctx, &range, &flags, &rate,
+ &trickmode_interval, &enable_rate_control);
+ if (rtsp_status_code != GST_RTSP_STS_OK)
+ goto adjust_play_mode_failed;
+ }
+
+ gst_rtsp_media_set_rate_control (ctx->media, enable_rate_control);
+
+ /* now do the seek with the seek options */
+ gst_rtsp_media_seek_trickmode (ctx->media, range, flags, rate,
+ trickmode_interval);
+ if (range != NULL)
+ gst_rtsp_range_free (range);
+
+ if (gst_rtsp_media_get_status (ctx->media) == GST_RTSP_MEDIA_STATUS_ERROR)
+ goto seek_failed;
+
+ return GST_RTSP_STS_OK;
+
+ parse_range_failed:
+ {
+ GST_ERROR ("client %p: failed parsing range header", client);
+ return GST_RTSP_STS_BAD_REQUEST;
+ }
+ scale_speed_failed:
+ {
+ if (range != NULL)
+ gst_rtsp_range_free (range);
+ GST_ERROR ("client %p: failed parsing Scale or Speed headers", client);
+ return rtsp_status_code;
+ }
+ adjust_play_mode_failed:
+ {
+ GST_ERROR ("client %p: sub class returned bad code (%d)", client,
+ rtsp_status_code);
+ if (range != NULL)
+ gst_rtsp_range_free (range);
+ return rtsp_status_code;
+ }
+ seek_failed:
+ {
+ GST_ERROR ("client %p: seek failed", client);
+ return GST_RTSP_STS_SERVICE_UNAVAILABLE;
+ }
+ }
+
static gboolean
-handle_play_request (GstRTSPClient * client, GstRTSPContext * ctx)
+default_handle_play_request (GstRTSPClient * client, GstRTSPContext * ctx)
{
GstRTSPSession *session;
GstRTSPClientClass *klass;
GstRTSPResult (*params_set) (GstRTSPClient *client, GstRTSPContext *ctx);
GstRTSPResult (*params_get) (GstRTSPClient *client, GstRTSPContext *ctx);
gchar * (*make_path_from_uri) (GstRTSPClient *client, const GstRTSPUrl *uri);
+ gboolean (*handle_options_request) (GstRTSPClient * client, GstRTSPContext * ctx, GstRTSPVersion version);
+ gboolean (*handle_set_param_request) (GstRTSPClient * client, GstRTSPContext * ctx);
+ gboolean (*handle_get_param_request) (GstRTSPClient * client, GstRTSPContext * ctx);
+ gboolean (*handle_play_request) (GstRTSPClient * client, GstRTSPContext * ctx);
+ GstRTSPStatusCode (*adjust_play_mode) (GstRTSPClient * client,
+ GstRTSPContext * context,
+ GstRTSPTimeRange ** range,
+ GstSeekFlags * flags,
+ gdouble * rate,
+ GstClockTime * trickmode_interval,
+ gboolean * enable_rate_control);
+ GstRTSPStatusCode (*adjust_play_response) (GstRTSPClient * client,
+ GstRTSPContext * context);
/* signals */
void (*closed) (GstRTSPClient *client);
gst_rtsp_media_signals[SIGNAL_NEW_STATE] =
g_signal_new ("new-state", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (GstRTSPMediaClass, new_state), NULL, NULL,
- g_cclosure_marshal_generic, G_TYPE_NONE, 1, G_TYPE_INT);
+ G_STRUCT_OFFSET (GstRTSPMediaClass, new_state), NULL, NULL, NULL,
+ G_TYPE_NONE, 1, G_TYPE_INT);
+ gst_rtsp_media_signals[SIGNAL_PREPARING] =
+ g_signal_new ("preparing", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GstRTSPMediaClass, preparing), NULL, NULL,
+ g_cclosure_marshal_generic, G_TYPE_NONE, 2, GST_TYPE_RTSP_STREAM,
+ G_TYPE_UINT);
+
+ gst_rtsp_media_signals[SIGNAL_UNPREPARING] =
+ g_signal_new ("unpreparing", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GstRTSPMediaClass, unpreparing), NULL, NULL,
+ g_cclosure_marshal_generic, G_TYPE_NONE, 2, GST_TYPE_RTSP_STREAM,
+ G_TYPE_UINT);
+
GST_DEBUG_CATEGORY_INIT (rtsp_media_debug, "rtspmedia", 0, "GstRTSPMedia");
klass->handle_message = default_handle_message;
GST_DEBUG ("media %p set blocked %d", media, blocked);
priv->blocked = blocked;
g_ptr_array_foreach (priv->streams, (GFunc) stream_update_blocked, media);
- }
- static void
- stream_unblock (GstRTSPStream * stream, GstRTSPMedia * media)
- {
- gst_rtsp_stream_unblock_linked (stream);
- }
-
- static void
- media_unblock_linked (GstRTSPMedia * media)
- {
- GstRTSPMediaPrivate *priv = media->priv;
-
- GST_DEBUG ("media %p unblocking linked streams", media);
- /* media is not blocked any longer, as it contains active streams,
- * streams that are complete */
- priv->blocked = FALSE;
- g_ptr_array_foreach (priv->streams, (GFunc) stream_unblock, media);
+ if (!blocked)
+ priv->blocking_msg_received = 0;
}
-static void
+void
gst_rtsp_media_set_status (GstRTSPMedia * media, GstRTSPMediaStatus status)
{
GstRTSPMediaPrivate *priv = media->priv;
return res;
}
- static void
- new_storage_cb (GstElement * rtpbin, GObject * storage, guint sessid,
- GstRTSPMedia * media)
- {
- g_object_set (storage, "size-time", (media->priv->latency + 50) * GST_MSECOND,
- NULL);
- }
-
static gboolean
-start_prepare (GstRTSPMedia * media)
+default_start_prepare (GstRTSPMedia * media)
{
GstRTSPMediaPrivate *priv = media->priv;
+ GstRTSPMediaClass *klass;
guint i;
GList *walk;
gst_rtsp_media_set_status (media, GST_RTSP_MEDIA_STATUS_PREPARING);
/* at this point the media pipeline has been updated and contain all
* specific transport parts: all active streams contain at least one sink
- * element and it's safe to unblock any blocked streams that are active */
- media_unblock_linked (media);
- if (klass->start_preroll)
+ * element and it's safe to unblock all blocked streams */
+ media_streams_set_blocked (media, FALSE);
- if (!start_preroll (media))
- goto start_failed;
++ if (klass->start_preroll)
+ if (!klass->start_preroll (media))
+ goto start_failed;
+
g_rec_mutex_unlock (&priv->state_lock);
preroll_ok = wait_preroll (media);
g_rec_mutex_lock (&priv->state_lock);
gst_rtsp_media_unprepare (media);
} else {
GST_INFO ("state %s media %p", gst_element_state_get_name (state), media);
- set_target_state (media, state, FALSE);
+ gst_rtsp_media_set_target_state (media, state, FALSE);
+
+ if (state == GST_STATE_PLAYING) {
+ /* make sure pads are not blocking anymore when going to PLAYING */
+ media_streams_set_blocked (media, FALSE);
+ }
+
/* when we are buffering, don't update the state yet, this will be done
* when buffering finishes */
if (priv->buffering) {
return receive_only;
}
+ /**
+ * gst_rtsp_media_has_completed_sender:
+ *
+ * See gst_rtsp_stream_is_complete(), gst_rtsp_stream_is_sender().
+ *
+ * Returns: whether @media has at least one complete sender stream.
+ * Since: 1.18
+ */
+ gboolean
+ gst_rtsp_media_has_completed_sender (GstRTSPMedia * media)
+ {
+ GstRTSPMediaPrivate *priv = media->priv;
+ gboolean sender = FALSE;
+ guint i;
+
+ g_mutex_lock (&priv->lock);
+ for (i = 0; i < priv->streams->len; i++) {
+ GstRTSPStream *stream = g_ptr_array_index (priv->streams, i);
+ if (gst_rtsp_stream_is_complete (stream))
+ if (gst_rtsp_stream_is_sender (stream) ||
+ !gst_rtsp_stream_is_receiver (stream)) {
+ sender = TRUE;
+ break;
+ }
+ }
+ g_mutex_unlock (&priv->lock);
+
+ return sender;
+ }
+
+ /**
+ * gst_rtsp_media_set_rate_control:
+ *
+ * Define whether @media will follow the Rate-Control=no behaviour as specified
+ * in the ONVIF replay spec.
+ *
+ * Since: 1.18
+ */
+ void
+ gst_rtsp_media_set_rate_control (GstRTSPMedia * media, gboolean enabled)
+ {
+ GstRTSPMediaPrivate *priv;
+ guint i;
+
+ g_return_if_fail (GST_IS_RTSP_MEDIA (media));
+
+ GST_LOG_OBJECT (media, "%s rate control", enabled ? "Enabling" : "Disabling");
+
+ priv = media->priv;
+
+ g_mutex_lock (&priv->lock);
+ priv->do_rate_control = enabled;
+ for (i = 0; i < priv->streams->len; i++) {
+ GstRTSPStream *stream = g_ptr_array_index (priv->streams, i);
+
+ gst_rtsp_stream_set_rate_control (stream, enabled);
+
+ }
+ g_mutex_unlock (&priv->lock);
+ }
+
+ /**
+ * gst_rtsp_media_get_rate_control:
+ *
+ * Returns: whether @media will follow the Rate-Control=no behaviour as specified
+ * in the ONVIF replay spec.
+ *
+ * Since: 1.18
+ */
+ gboolean
+ gst_rtsp_media_get_rate_control (GstRTSPMedia * media)
+ {
+ GstRTSPMediaPrivate *priv;
+ gboolean res;
+
+ g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), FALSE);
+
+ priv = media->priv;
+
+ g_mutex_lock (&priv->lock);
+ res = priv->do_rate_control;
+ g_mutex_unlock (&priv->lock);
+
+ return res;
+ }
++
+GstElement *
+gst_rtsp_media_get_pipeline (GstRTSPMedia * media)
+{
+ GstRTSPMediaPrivate *priv;
+
+ g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), NULL);
+
+ priv = media->priv;
+
+ g_mutex_lock (&priv->lock);
+ g_object_ref (priv->pipeline);
+ g_mutex_unlock (&priv->lock);
+
+ return priv->pipeline;
+}
+
+
+GstElement *
+gst_rtsp_media_get_rtpbin (GstRTSPMedia * media)
+{
+ GstRTSPMediaPrivate *priv;
+
+ g_return_val_if_fail (GST_IS_RTSP_MEDIA (media), NULL);
+
+ priv = media->priv;
+
+ g_mutex_lock (&priv->lock);
+ g_object_ref (priv->rtpbin);
+ g_mutex_unlock (&priv->lock);
+
+ return priv->rtpbin;
+}
GstState state);
GST_RTSP_SERVER_API
+GstStateChangeReturn gst_rtsp_media_set_target_state (GstRTSPMedia * media, GstState state, gboolean do_state);
+
+GST_RTSP_SERVER_API
+void gst_rtsp_media_set_status (GstRTSPMedia * media, GstRTSPMediaStatus status);
+
+GST_RTSP_SERVER_API
+GstElement * gst_rtsp_media_get_pipeline (GstRTSPMedia * media);
+
+GST_RTSP_SERVER_API
+GstElement * gst_rtsp_media_get_rtpbin (GstRTSPMedia * media);
+
+GST_RTSP_SERVER_API
gboolean gst_rtsp_media_complete_pipeline (GstRTSPMedia * media, GPtrArray * transports);
+ GST_RTSP_SERVER_API
+ gboolean gst_rtsp_media_is_receive_only (GstRTSPMedia * media);
+
+ GST_RTSP_SERVER_API
+ gboolean gst_rtsp_media_has_completed_sender (GstRTSPMedia * media);
+
+ GST_RTSP_SERVER_API
+ void gst_rtsp_media_set_rate_control (GstRTSPMedia * media, gboolean enabled);
+
+ GST_RTSP_SERVER_API
+ gboolean gst_rtsp_media_get_rate_control (GstRTSPMedia * media);
+
#ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstRTSPMedia, gst_object_unref)
#endif
gst_rtsp_server_signals[SIGNAL_CLIENT_CONNECTED] =
g_signal_new ("client-connected", G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstRTSPServerClass, client_connected),
- NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1,
- GST_TYPE_RTSP_CLIENT);
+ NULL, NULL, NULL, G_TYPE_NONE, 1, GST_TYPE_RTSP_CLIENT);
klass->create_client = default_create_client;
+ klass->create_socket = gst_rtsp_server_create_socket;
GST_DEBUG_CATEGORY_INIT (rtsp_server_debug, "rtspserver", 0, "GstRTSPServer");
}
gst_rtsp_stream_signals[SIGNAL_NEW_RTP_RTCP_DECODER] =
g_signal_new ("new-rtp-rtcp-decoder", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic,
- G_TYPE_NONE, 1, GST_TYPE_ELEMENT);
+ G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, GST_TYPE_ELEMENT);
+ gst_rtsp_stream_signals[SIGNAL_RTCP_STATS] =
+ g_signal_new ("rtcp-statistics", G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic,
+ G_TYPE_NONE, 1, GST_TYPE_STRUCTURE);
+
GST_DEBUG_CATEGORY_INIT (rtsp_stream_debug, "rtspstream", 0, "GstRTSPStream");
ssrc_stream_map_key = g_quark_from_static_string ("GstRTSPServer.stream");
--- /dev/null
- Version: 1.16.2
- Release: 9
+Name: gst-rtsp-server
+Summary: Multimedia Framework Library
- Source100: common.tar.bz2
++Version: 1.19.2
++Release: 0
+Url: http://gstreamer.freedesktop.org/
+Group: System/Libraries
+License: LGPL-2.0+
+Source: http://gstreamer.freedesktop.org/src/gst-rtsp-server/gst-rtsp-server-%{version}.tar.xz
- BuildRequires: pkgconfig(gstreamer-1.0)
- BuildRequires: pkgconfig(gstreamer-plugins-base-1.0)
++Source1001: %{name}.manifest
+Requires(post): /sbin/ldconfig
+Requires(postun): /sbin/ldconfig
- %setup -q -T -D -a 100
++#BuildRequires: pkgconfig(gstreamer-1.0)
++#BuildRequires: pkgconfig(gstreamer-plugins-base-1.0)
++BuildRequires: gstreamer-devel >= %{version}
++BuildRequires: gst-plugins-base-devel >= %{version}
++BuildRequires: meson >= 0.48.0
+
+BuildRoot: %{_tmppath}/%{name}-%{version}-build
+
+%description
+
+%package devel
+Summary: Multimedia Framework RTSP server library (DEV)
+Group: Development/Libraries
+Requires: %{name} = %{version}-%{release}
+
+%description devel
+
+%package factory
+Summary: Multimedia Framework RTSP server Library (Factory)
+Group: Development/Libraries
+Requires: %{name} = %{version}-%{release}
+
+%description factory
+
+%prep
+%setup -q -n gst-rtsp-server-%{version}
-
- NOCONFIGURE=1 ./autogen.sh
++cp %{SOURCE1001} .
+
+%build
- %configure --disable-static --disable-examples
++mkdir -p build
+
+CFLAGS+=" -DEXPORT_API=\"__attribute__((visibility(\\\"default\\\")))\" "; export CFLAGS
+LDFLAGS+="-Wl,--rpath=%{_prefix}/lib -Wl,--hash-style=both -Wl,--as-needed"; export LDFLAGS
+
+# always enable sdk build. This option should go away
+# disable build examples.
- make %{?jobs:-j%jobs}
++export CXXFLAGS+=" -Wno-error"
++
++meson --prefix=/usr --libdir=%{_libdir} --datadir=%{_datadir} build
+
+# Call make instruction with smp support
- rm -rf %{buildroot}
- %make_install
++ninja -C build all %{?jobs:-j%jobs}
+
+%install
- %manifest gst-rtsp-server.manifest
++export DESTDIR=%{buildroot}
++ninja -C build install
+
+%clean
+rm -rf %{buildroot}
+
+%post
+/sbin/ldconfig
+
+%postun
+/sbin/ldconfig
+
+%files
++%manifest %{name}.manifest
+%defattr(-,root,root,-)
+%license COPYING
+%{_libdir}/*.so.*
+%{_libdir}/gstreamer-1.0/libgstrtspclientsink.so
+
+%files devel
+%defattr(-,root,root,-)
+%{_libdir}/*.so
+%{_includedir}/gstreamer-1.0/gst/rtsp-server/rtsp-*.h
+%{_includedir}/gstreamer-1.0/gst/rtsp-server/gstwfd*.h
+%{_libdir}/pkgconfig/*