mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 12:11:13 +00:00
rtptheorapay: add config-interval parameter to re-insert config in stream
Add a new config-interval property to instruct the payloader to insert configuration headers at periodic intervals in the stream (when a keyframe is countered).
This commit is contained in:
parent
14b14fdf7a
commit
af6fc84377
2 changed files with 144 additions and 7 deletions
|
@ -68,6 +68,14 @@ GST_STATIC_PAD_TEMPLATE ("sink",
|
|||
GST_STATIC_CAPS ("video/x-theora")
|
||||
);
|
||||
|
||||
#define DEFAULT_CONFIG_INTERVAL 0
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_CONFIG_INTERVAL
|
||||
};
|
||||
|
||||
GST_BOILERPLATE (GstRtpTheoraPay, gst_rtp_theora_pay, GstBaseRTPPayload,
|
||||
GST_TYPE_BASE_RTP_PAYLOAD);
|
||||
|
||||
|
@ -78,6 +86,11 @@ static GstStateChangeReturn gst_rtp_theora_pay_change_state (GstElement *
|
|||
static GstFlowReturn gst_rtp_theora_pay_handle_buffer (GstBaseRTPPayload * pad,
|
||||
GstBuffer * buffer);
|
||||
|
||||
static void gst_rtp_theora_pay_set_property (GObject * object, guint prop_id,
|
||||
const GValue * value, GParamSpec * pspec);
|
||||
static void gst_rtp_theora_pay_get_property (GObject * object, guint prop_id,
|
||||
GValue * value, GParamSpec * pspec);
|
||||
|
||||
static void
|
||||
gst_rtp_theora_pay_base_init (gpointer klass)
|
||||
{
|
||||
|
@ -97,9 +110,11 @@ gst_rtp_theora_pay_base_init (gpointer klass)
|
|||
static void
|
||||
gst_rtp_theora_pay_class_init (GstRtpTheoraPayClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class;
|
||||
GstElementClass *gstelement_class;
|
||||
GstBaseRTPPayloadClass *gstbasertppayload_class;
|
||||
|
||||
gobject_class = (GObjectClass *) klass;
|
||||
gstelement_class = (GstElementClass *) klass;
|
||||
gstbasertppayload_class = (GstBaseRTPPayloadClass *) klass;
|
||||
|
||||
|
@ -108,15 +123,26 @@ gst_rtp_theora_pay_class_init (GstRtpTheoraPayClass * klass)
|
|||
gstbasertppayload_class->set_caps = gst_rtp_theora_pay_setcaps;
|
||||
gstbasertppayload_class->handle_buffer = gst_rtp_theora_pay_handle_buffer;
|
||||
|
||||
gobject_class->set_property = gst_rtp_theora_pay_set_property;
|
||||
gobject_class->get_property = gst_rtp_theora_pay_get_property;
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (rtptheorapay_debug, "rtptheorapay", 0,
|
||||
"Theora RTP Payloader");
|
||||
|
||||
g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_CONFIG_INTERVAL,
|
||||
g_param_spec_uint ("config-interval", "Config Send Interval",
|
||||
"Send Config Insertion Interval in seconds (configuration headers "
|
||||
"will be multiplexed in the data stream when detected.) (0 = disabled)",
|
||||
0, 3600, DEFAULT_CONFIG_INTERVAL,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)
|
||||
);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtp_theora_pay_init (GstRtpTheoraPay * rtptheorapay,
|
||||
GstRtpTheoraPayClass * klass)
|
||||
{
|
||||
/* needed because of GST_BOILERPLATE */
|
||||
rtptheorapay->last_config = GST_CLOCK_TIME_NONE;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -129,6 +155,11 @@ gst_rtp_theora_pay_cleanup (GstRtpTheoraPay * rtptheorapay)
|
|||
if (rtptheorapay->packet)
|
||||
gst_buffer_unref (rtptheorapay->packet);
|
||||
rtptheorapay->packet = NULL;
|
||||
|
||||
if (rtptheorapay->config_data)
|
||||
g_free (rtptheorapay->config_data);
|
||||
rtptheorapay->config_data = NULL;
|
||||
rtptheorapay->last_config = GST_CLOCK_TIME_NONE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
|
@ -230,7 +261,7 @@ gst_rtp_theora_pay_finish_headers (GstBaseRTPPayload * basepayload)
|
|||
{
|
||||
GstRtpTheoraPay *rtptheorapay = GST_RTP_THEORA_PAY (basepayload);
|
||||
GList *walk;
|
||||
guint length, size, n_headers, configlen;
|
||||
guint length, size, n_headers, configlen, extralen;
|
||||
gchar *wstr, *hstr, *configuration;
|
||||
guint8 *data, *config;
|
||||
guint32 ident;
|
||||
|
@ -291,6 +322,7 @@ gst_rtp_theora_pay_finish_headers (GstBaseRTPPayload * basepayload)
|
|||
length = 0;
|
||||
n_headers = 0;
|
||||
ident = fnv1_hash_32_new ();
|
||||
extralen = 1;
|
||||
for (walk = rtptheorapay->headers; walk; walk = g_list_next (walk)) {
|
||||
GstBuffer *buf = GST_BUFFER_CAST (walk->data);
|
||||
|
||||
|
@ -305,6 +337,7 @@ gst_rtp_theora_pay_finish_headers (GstBaseRTPPayload * basepayload)
|
|||
if (g_list_next (walk)) {
|
||||
do {
|
||||
size++;
|
||||
extralen++;
|
||||
bsize >>= 7;
|
||||
} while (bsize);
|
||||
}
|
||||
|
@ -383,6 +416,14 @@ gst_rtp_theora_pay_finish_headers (GstBaseRTPPayload * basepayload)
|
|||
|
||||
/* serialize to base64 */
|
||||
configuration = g_base64_encode (config, configlen);
|
||||
|
||||
/* store for later re-sending */
|
||||
rtptheorapay->config_size = configlen - 4 - 3 - 2;
|
||||
rtptheorapay->config_data = g_malloc (rtptheorapay->config_size);
|
||||
rtptheorapay->config_extra_len = extralen;
|
||||
memcpy (rtptheorapay->config_data, config + 4 + 3 + 2,
|
||||
rtptheorapay->config_size);
|
||||
|
||||
g_free (config);
|
||||
|
||||
/* configure payloader settings */
|
||||
|
@ -471,7 +512,8 @@ invalid_version:
|
|||
|
||||
static GstFlowReturn
|
||||
gst_rtp_theora_pay_payload_buffer (GstRtpTheoraPay * rtptheorapay, guint8 TDT,
|
||||
guint8 * data, guint size, GstClockTime timestamp, GstClockTime duration)
|
||||
guint8 * data, guint size, GstClockTime timestamp, GstClockTime duration,
|
||||
guint not_in_length)
|
||||
{
|
||||
GstFlowReturn ret = GST_FLOW_OK;
|
||||
guint newsize;
|
||||
|
@ -518,10 +560,14 @@ gst_rtp_theora_pay_payload_buffer (GstRtpTheoraPay * rtptheorapay, guint8 TDT,
|
|||
GST_DEBUG_OBJECT (rtptheorapay, "append %u bytes", plen);
|
||||
|
||||
/* data is copied in the payload with a 2 byte length header */
|
||||
ppos[0] = (plen >> 8) & 0xff;
|
||||
ppos[1] = (plen & 0xff);
|
||||
ppos[0] = ((plen - not_in_length) >> 8) & 0xff;
|
||||
ppos[1] = ((plen - not_in_length) & 0xff);
|
||||
memcpy (&ppos[2], data, plen);
|
||||
|
||||
/* only first (only) configuration cuts length field */
|
||||
/* NOTE: spec (if any) is not clear on this ... */
|
||||
not_in_length = 0;
|
||||
|
||||
size -= plen;
|
||||
data += plen;
|
||||
|
||||
|
@ -577,6 +623,7 @@ gst_rtp_theora_pay_handle_buffer (GstBaseRTPPayload * basepayload,
|
|||
guint8 *data;
|
||||
GstClockTime duration, timestamp;
|
||||
guint8 TDT;
|
||||
gboolean keyframe = FALSE;
|
||||
|
||||
rtptheorapay = GST_RTP_THEORA_PAY (basepayload);
|
||||
|
||||
|
@ -608,9 +655,11 @@ gst_rtp_theora_pay_handle_buffer (GstBaseRTPPayload * basepayload,
|
|||
TDT = 1;
|
||||
} else
|
||||
goto unknown_header;
|
||||
} else
|
||||
} else {
|
||||
/* data */
|
||||
TDT = 0;
|
||||
keyframe = ((data[0] & 0x40) == 0);
|
||||
}
|
||||
|
||||
if (rtptheorapay->need_headers) {
|
||||
/* we need to collect the headers and construct a config string from them */
|
||||
|
@ -627,8 +676,55 @@ gst_rtp_theora_pay_handle_buffer (GstBaseRTPPayload * basepayload,
|
|||
}
|
||||
}
|
||||
|
||||
/* there is a config request, see if we need to insert it */
|
||||
if (keyframe && (rtptheorapay->config_interval > 0) &&
|
||||
rtptheorapay->config_data) {
|
||||
gboolean send_config = FALSE;
|
||||
|
||||
if (rtptheorapay->last_config != -1) {
|
||||
guint64 diff;
|
||||
|
||||
GST_LOG_OBJECT (rtptheorapay,
|
||||
"now %" GST_TIME_FORMAT ", last VOP-I %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (timestamp), GST_TIME_ARGS (rtptheorapay->last_config));
|
||||
|
||||
/* calculate diff between last config in milliseconds */
|
||||
if (timestamp > rtptheorapay->last_config) {
|
||||
diff = timestamp - rtptheorapay->last_config;
|
||||
} else {
|
||||
diff = 0;
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (rtptheorapay,
|
||||
"interval since last config %" GST_TIME_FORMAT, GST_TIME_ARGS (diff));
|
||||
|
||||
/* bigger than interval, queue config */
|
||||
/* FIXME should convert timestamps to running time */
|
||||
if (GST_TIME_AS_SECONDS (diff) >= rtptheorapay->config_interval) {
|
||||
GST_DEBUG_OBJECT (rtptheorapay, "time to send config");
|
||||
send_config = TRUE;
|
||||
}
|
||||
} else {
|
||||
/* no known previous config time, send now */
|
||||
GST_DEBUG_OBJECT (rtptheorapay, "no previous config time, send now");
|
||||
send_config = TRUE;
|
||||
}
|
||||
|
||||
if (send_config) {
|
||||
/* we need to send config now first */
|
||||
/* different TDT type forces flush */
|
||||
gst_rtp_theora_pay_payload_buffer (rtptheorapay, 1,
|
||||
rtptheorapay->config_data, rtptheorapay->config_size,
|
||||
timestamp, GST_CLOCK_TIME_NONE, rtptheorapay->config_extra_len);
|
||||
|
||||
if (timestamp != -1) {
|
||||
rtptheorapay->last_config = timestamp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret = gst_rtp_theora_pay_payload_buffer (rtptheorapay, TDT, data, size,
|
||||
timestamp, duration);
|
||||
timestamp, duration, 0);
|
||||
gst_buffer_unref (buffer);
|
||||
|
||||
done:
|
||||
|
@ -690,6 +786,40 @@ gst_rtp_theora_pay_change_state (GstElement * element,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtp_theora_pay_set_property (GObject * object, guint prop_id,
|
||||
const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstRtpTheoraPay *rtptheorapay;
|
||||
|
||||
rtptheorapay = GST_RTP_THEORA_PAY (object);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_CONFIG_INTERVAL:
|
||||
rtptheorapay->config_interval = g_value_get_uint (value);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtp_theora_pay_get_property (GObject * object, guint prop_id,
|
||||
GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstRtpTheoraPay *rtptheorapay;
|
||||
|
||||
rtptheorapay = GST_RTP_THEORA_PAY (object);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_CONFIG_INTERVAL:
|
||||
g_value_set_uint (value, rtptheorapay->config_interval);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_rtp_theora_pay_plugin_init (GstPlugin * plugin)
|
||||
{
|
||||
|
|
|
@ -59,6 +59,13 @@ struct _GstRtpTheoraPay
|
|||
GstClockTime payload_timestamp;
|
||||
GstClockTime payload_duration;
|
||||
|
||||
/* config (re-sending) */
|
||||
guint8 *config_data;
|
||||
guint config_size;
|
||||
guint config_extra_len;
|
||||
guint config_interval;
|
||||
GstClockTime last_config;
|
||||
|
||||
gint width;
|
||||
gint height;
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue