From e0e75f715e4d6594a9804fac7a660f35f3107fd0 Mon Sep 17 00:00:00 2001 From: Christian Schaller Date: Tue, 7 Nov 2006 01:43:06 +0000 Subject: [PATCH] gst/rtp/Makefile.am: We depend on gsttag to generate the vorbis comments. Original commit message from CVS: * gst/rtp/Makefile.am: We depend on gsttag to generate the vorbis comments. * gst/rtp/gstrtpvorbisdepay.c: (gst_rtp_vorbis_depay_parse_configuration), (gst_rtp_vorbis_depay_setcaps), (gst_rtp_vorbis_depay_switch_codebook), (gst_rtp_vorbis_depay_process): * gst/rtp/gstrtpvorbisdepay.h: Parse configuration string in the depayloader. Implement selecting and switching to a new codebook. Receiving vorbis over RTP now works. * gst/rtp/gstrtpvorbispay.c: (gst_rtp_vorbis_pay_reset_packet), (gst_rtp_vorbis_pay_init_packet), (gst_rtp_vorbis_pay_finish_headers), (gst_rtp_vorbis_pay_handle_buffer): * gst/rtp/gstrtpvorbispay.h: Set timestamps on outgoing buffers and RTP packets. Fix configuration string, prepend number of Packet headers. Fix encoding of ident string. Add delivery-method to caps. Streaming vorbis over RTP now works. --- ChangeLog | 26 ++++ gst/rtp/Makefile.am | 1 + gst/rtp/gstrtpvorbisdepay.c | 240 +++++++++++++++++++++++++++++++++++- gst/rtp/gstrtpvorbisdepay.h | 12 +- gst/rtp/gstrtpvorbispay.c | 45 ++++--- gst/rtp/gstrtpvorbispay.h | 1 + 6 files changed, 302 insertions(+), 23 deletions(-) diff --git a/ChangeLog b/ChangeLog index f22954dfef..73c72d2ec4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,29 @@ +2006-11-06 Wim Taymans + + * gst/rtp/Makefile.am: + We depend on gsttag to generate the vorbis comments. + + * gst/rtp/gstrtpvorbisdepay.c: + (gst_rtp_vorbis_depay_parse_configuration), + (gst_rtp_vorbis_depay_setcaps), + (gst_rtp_vorbis_depay_switch_codebook), + (gst_rtp_vorbis_depay_process): + * gst/rtp/gstrtpvorbisdepay.h: + Parse configuration string in the depayloader. + Implement selecting and switching to a new codebook. + Receiving vorbis over RTP now works. + + * gst/rtp/gstrtpvorbispay.c: (gst_rtp_vorbis_pay_reset_packet), + (gst_rtp_vorbis_pay_init_packet), + (gst_rtp_vorbis_pay_finish_headers), + (gst_rtp_vorbis_pay_handle_buffer): + * gst/rtp/gstrtpvorbispay.h: + Set timestamps on outgoing buffers and RTP packets. + Fix configuration string, prepend number of Packet headers. + Fix encoding of ident string. + Add delivery-method to caps. + Streaming vorbis over RTP now works. + 2006-11-06 Wim Taymans * gst/rtp/gstrtpvorbispay.c: (gst_rtp_vorbis_pay_setcaps), diff --git a/gst/rtp/Makefile.am b/gst/rtp/Makefile.am index e1916c77cd..70b8e18365 100644 --- a/gst/rtp/Makefile.am +++ b/gst/rtp/Makefile.am @@ -41,6 +41,7 @@ endif libgstrtp_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) libgstrtp_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) $(GST_BASE_LIBS) \ + -lgsttag-@GST_MAJORMINOR@ \ -lgstrtp-@GST_MAJORMINOR@ $(WINSOCK2_LIBS) libgstrtp_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) diff --git a/gst/rtp/gstrtpvorbisdepay.c b/gst/rtp/gstrtpvorbisdepay.c index ba29413008..f2434e41f4 100644 --- a/gst/rtp/gstrtpvorbisdepay.c +++ b/gst/rtp/gstrtpvorbisdepay.c @@ -21,6 +21,7 @@ # include "config.h" #endif +#include #include #include @@ -75,6 +76,9 @@ GST_STATIC_PAD_TEMPLATE ("src", GST_STATIC_CAPS ("audio/x-vorbis") ); +/* 30 bytes for the vorbis identification packet length */ +#define VORBIS_ID_LEN 30 + GST_BOILERPLATE (GstRtpVorbisDepay, gst_rtp_vorbis_depay, GstBaseRTPDepayload, GST_TYPE_BASE_RTP_DEPAYLOAD); @@ -146,21 +150,172 @@ gst_rtp_vorbis_depay_finalize (GObject * object) G_OBJECT_CLASS (parent_class)->finalize (object); } +static gboolean +gst_rtp_vorbis_depay_parse_configuration (GstRtpVorbisDepay * rtpvorbisdepay, + const gchar * configuration) +{ + GValue v = { 0 }; + GstBuffer *buf; + guint32 num_headers; + guint8 *data; + guint size; + gint i; + + /* deserialize base16 to buffer */ + g_value_init (&v, GST_TYPE_BUFFER); + if (!gst_value_deserialize (&v, configuration)) + goto wrong_configuration; + + buf = gst_value_get_buffer (&v); + gst_buffer_ref (buf); + g_value_unset (&v); + + data = GST_BUFFER_DATA (buf); + size = GST_BUFFER_SIZE (buf); + + GST_DEBUG_OBJECT (rtpvorbisdepay, "config size %u", size); + + /* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Number of packed headers | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Packed header | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Packed header | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | .... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + if (size < 4) + goto too_small; + + num_headers = GST_READ_UINT32_BE (data); + size -= 4; + data += 4; + + GST_DEBUG_OBJECT (rtpvorbisdepay, "have %u headers", num_headers); + + /* 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Ident | .. + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * .. length | Identification Header .. + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * .. Identification Header | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Setup Header .. + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * .. Setup Header | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + */ + for (i = 0; i < num_headers; i++) { + guint32 ident; + guint16 length; + GstRtpVorbisConfig *conf; + GstTagList *list; + + if (size < 5) + goto too_small; + + ident = (data[0] << 16) | (data[1] << 8) | data[2]; + length = (data[3] << 8) | data[4]; + size -= 5; + data += 5; + + GST_DEBUG_OBJECT (rtpvorbisdepay, "header %d, ident %08x, length %u", i, + ident, length); + + if (size < length + VORBIS_ID_LEN) + goto too_small; + + GST_DEBUG_OBJECT (rtpvorbisdepay, "preparing headers"); + + conf = g_new0 (GstRtpVorbisConfig, 1); + conf->ident = ident; + + buf = gst_buffer_new_and_alloc (VORBIS_ID_LEN); + memcpy (GST_BUFFER_DATA (buf), data, VORBIS_ID_LEN); + conf->headers = g_list_append (conf->headers, buf); + data += VORBIS_ID_LEN; + size -= VORBIS_ID_LEN; + + /* create a dummy comment */ + list = gst_tag_list_new (); + buf = + gst_tag_list_to_vorbiscomment_buffer (list, (guint8 *) "\003vorbis", 7, + "Vorbis RTP depayloader"); + conf->headers = g_list_append (conf->headers, buf); + gst_tag_list_free (list); + + buf = gst_buffer_new_and_alloc (length); + memcpy (GST_BUFFER_DATA (buf), data, length); + conf->headers = g_list_append (conf->headers, buf); + data += length; + size -= length; + + rtpvorbisdepay->configs = g_list_append (rtpvorbisdepay->configs, conf); + } + + return TRUE; + + /* ERRORS */ +wrong_configuration: + { + GST_DEBUG_OBJECT (rtpvorbisdepay, "error parsing configuration"); + return FALSE; + } +too_small: + { + GST_DEBUG_OBJECT (rtpvorbisdepay, "configuration too small"); + return FALSE; + } +} + static gboolean gst_rtp_vorbis_depay_setcaps (GstBaseRTPDepayload * depayload, GstCaps * caps) { GstStructure *structure; GstRtpVorbisDepay *rtpvorbisdepay; GstCaps *srccaps; + const gchar *delivery_method; + const gchar *configuration; gint clock_rate; rtpvorbisdepay = GST_RTP_VORBIS_DEPAY (depayload); structure = gst_caps_get_structure (caps, 0); + /* get clockrate */ if (!gst_structure_get_int (structure, "clock-rate", &clock_rate)) goto no_rate; + /* see how the configuration parameters will be transmitted */ + delivery_method = gst_structure_get_string (structure, "delivery-method"); + if (delivery_method == NULL) + goto no_delivery_method; + + if (g_strcasecmp (delivery_method, "inline")) { + /* configure string is in the caps */ + } else if (g_strcasecmp (delivery_method, "in_band")) { + /* headers will (also) be transmitted in the RTP packets */ + } else if (g_str_has_prefix (delivery_method, "out_band/")) { + /* some other method of header delivery. */ + goto unsupported_delivery_method; + } else + goto unsupported_delivery_method; + + /* read and parse configuration string */ + configuration = gst_structure_get_string (structure, "configuration"); + if (configuration == NULL) + goto no_configuration; + + if (!gst_rtp_vorbis_depay_parse_configuration (rtpvorbisdepay, configuration)) + goto invalid_configuration; + /* caps seem good, configure element */ depayload->clock_rate = clock_rate; @@ -171,6 +326,28 @@ gst_rtp_vorbis_depay_setcaps (GstBaseRTPDepayload * depayload, GstCaps * caps) return TRUE; + /* ERRORS */ +unsupported_delivery_method: + { + GST_ERROR_OBJECT (rtpvorbisdepay, + "unsupported delivery-method \"%s\" specified", delivery_method); + return FALSE; + } +no_delivery_method: + { + GST_ERROR_OBJECT (rtpvorbisdepay, "no delivery-method specified"); + return FALSE; + } +no_configuration: + { + GST_ERROR_OBJECT (rtpvorbisdepay, "no configuration specified"); + return FALSE; + } +invalid_configuration: + { + GST_ERROR_OBJECT (rtpvorbisdepay, "invalid configuration specified"); + return FALSE; + } no_rate: { GST_ERROR_OBJECT (rtpvorbisdepay, "no clock-rate specified"); @@ -178,6 +355,41 @@ no_rate: } } +static gboolean +gst_rtp_vorbis_depay_switch_codebook (GstRtpVorbisDepay * rtpvorbisdepay, + guint32 ident) +{ + GList *walk; + gboolean res = FALSE; + + for (walk = rtpvorbisdepay->configs; walk; walk = g_list_next (walk)) { + GstRtpVorbisConfig *conf = (GstRtpVorbisConfig *) walk->data; + + if (conf->ident == ident) { + GList *headers; + + /* FIXME, remove pads, create new pad.. */ + + /* push out all the headers */ + for (headers = conf->headers; headers; headers = g_list_next (headers)) { + GstBuffer *header = GST_BUFFER_CAST (headers->data); + + gst_buffer_ref (header); + gst_base_rtp_depayload_push (GST_BASE_RTP_DEPAYLOAD (rtpvorbisdepay), + header); + } + /* remember the current config */ + rtpvorbisdepay->config = conf; + res = TRUE; + } + } + if (!res) { + /* we don't know about the headers, figure out an alternative method for + * getting the codebooks. FIXME, fail for now. */ + } + return res; +} + static GstBuffer * gst_rtp_vorbis_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf) { @@ -228,7 +440,21 @@ gst_rtp_vorbis_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf) packets = (header & 0xf); if (VDT == 0) { - /* FIXME, if we have a raw payload, we need the codebook for the ident */ + gboolean do_switch = FALSE; + + /* we have a raw payload, find the codebook for the ident */ + if (!rtpvorbisdepay->config) { + /* we don't have an active codebook, find the codebook and + * activate it */ + do_switch = TRUE; + } else if (rtpvorbisdepay->config->ident != ident) { + /* codebook changed */ + do_switch = TRUE; + } + if (do_switch) { + if (!gst_rtp_vorbis_depay_switch_codebook (rtpvorbisdepay, ident)) + goto switch_failed; + } } /* skip header */ @@ -352,13 +578,19 @@ no_output: bad_packet: { GST_ELEMENT_WARNING (rtpvorbisdepay, STREAM, DECODE, - ("Packet did not validate"), (NULL)); + (NULL), ("Packet did not validate")); + return NULL; + } +switch_failed: + { + GST_ELEMENT_ERROR (rtpvorbisdepay, STREAM, DECODE, + (NULL), ("Could not switch codebooks")); return NULL; } packet_short: { GST_ELEMENT_WARNING (rtpvorbisdepay, STREAM, DECODE, - ("Packet was too short (%d < 4)", payload_len), (NULL)); + (NULL), ("Packet was too short (%d < 4)", payload_len)); return NULL; } ignore_reserved: @@ -369,7 +601,7 @@ ignore_reserved: length_short: { GST_ELEMENT_WARNING (rtpvorbisdepay, STREAM, DECODE, - ("Packet contains invalid data"), (NULL)); + (NULL), ("Packet contains invalid data")); return NULL; } } diff --git a/gst/rtp/gstrtpvorbisdepay.h b/gst/rtp/gstrtpvorbisdepay.h index b95e902053..35f4d12dfd 100644 --- a/gst/rtp/gstrtpvorbisdepay.h +++ b/gst/rtp/gstrtpvorbisdepay.h @@ -40,12 +40,20 @@ G_BEGIN_DECLS typedef struct _GstRtpVorbisDepay GstRtpVorbisDepay; typedef struct _GstRtpVorbisDepayClass GstRtpVorbisDepayClass; +typedef struct _GstRtpVorbisConfig { + guint32 ident; + GList *headers; +} GstRtpVorbisConfig; + struct _GstRtpVorbisDepay { GstBaseRTPDepayload parent; - GstAdapter *adapter; - gboolean assembling; + GList *configs; + GstRtpVorbisConfig *config; + + GstAdapter *adapter; + gboolean assembling; }; struct _GstRtpVorbisDepayClass diff --git a/gst/rtp/gstrtpvorbispay.c b/gst/rtp/gstrtpvorbispay.c index 58705417f8..af6b951b11 100644 --- a/gst/rtp/gstrtpvorbispay.c +++ b/gst/rtp/gstrtpvorbispay.c @@ -139,14 +139,14 @@ gst_rtp_vorbis_pay_reset_packet (GstRtpVorbisPay * rtpvorbispay, guint8 VDT) payload_len = gst_rtp_buffer_get_payload_len (rtpvorbispay->packet); rtpvorbispay->payload_left = payload_len - 4; rtpvorbispay->payload_duration = 0; - rtpvorbispay->payload_ident = 0; rtpvorbispay->payload_F = 0; rtpvorbispay->payload_VDT = VDT; rtpvorbispay->payload_pkts = 0; } static void -gst_rtp_vorbis_pay_init_packet (GstRtpVorbisPay * rtpvorbispay, guint8 VDT) +gst_rtp_vorbis_pay_init_packet (GstRtpVorbisPay * rtpvorbispay, guint8 VDT, + GstClockTime timestamp) { GST_DEBUG_OBJECT (rtpvorbispay, "starting new packet, VDT: %d", VDT); @@ -158,6 +158,7 @@ gst_rtp_vorbis_pay_init_packet (GstRtpVorbisPay * rtpvorbispay, guint8 VDT) gst_rtp_buffer_new_allocate_len (GST_BASE_RTP_PAYLOAD_MTU (rtpvorbispay), 0, 0); gst_rtp_vorbis_pay_reset_packet (rtpvorbispay, VDT); + GST_BUFFER_TIMESTAMP (rtpvorbispay->packet) = timestamp; } static GstFlowReturn @@ -266,24 +267,31 @@ gst_rtp_vorbis_pay_finish_headers (GstBaseRTPPayload * basepayload) length += GST_BUFFER_SIZE (buf); } - /* total config length is the size of the headers + 2 bytes length + - * 3 bytes for the ident */ - config = gst_buffer_new_and_alloc (length + 2 + 3); + /* total config length is 4 bytes header number + size of the + * headers + 2 bytes length + 3 bytes for the ident */ + config = gst_buffer_new_and_alloc (length + 4 + 2 + 3); data = GST_BUFFER_DATA (config); + /* number of packed headers, we only pack 1 header */ + data[0] = 0; + data[1] = 0; + data[2] = 0; + data[3] = 1; + /* we generate a random ident for this configuration */ - ident = g_random_int (); + ident = rtpvorbispay->payload_ident = g_random_int (); /* take lower 3 bytes */ - data[0] = ident & 0xff; - data[1] = (ident >> 8) & 0xff; - data[2] = (ident >> 16) & 0xff; + data[4] = (ident >> 16) & 0xff; + data[5] = (ident >> 8) & 0xff; + data[6] = ident & 0xff; - data[3] = (length >> 8) & 0xff; - data[4] = length & 0xff; + /* store length minus the length of the fixed vorbis header */ + data[7] = ((length - 30) >> 8) & 0xff; + data[8] = (length - 30) & 0xff; /* copy header data */ - data += 5; + data += 9; for (walk = rtpvorbispay->headers; walk; walk = g_list_next (walk)) { GstBuffer *buf = GST_BUFFER_CAST (walk->data); @@ -302,7 +310,8 @@ gst_rtp_vorbis_pay_finish_headers (GstBaseRTPPayload * basepayload) rtpvorbispay->rate); gst_basertppayload_set_outcaps (basepayload, "encoding-params", G_TYPE_STRING, cstr, "configuration", G_TYPE_STRING, configuration, - /* don't set the defaults + "delivery-method", G_TYPE_STRING, "inline", + /* don't set the other defaults */ NULL); g_free (cstr); @@ -393,7 +402,7 @@ gst_rtp_vorbis_pay_handle_buffer (GstBaseRTPPayload * basepayload, guint size, newsize; guint8 *data; guint packet_len; - GstClockTime duration, newduration; + GstClockTime duration, newduration, timestamp; gboolean flush; guint8 VDT; guint plen; @@ -405,6 +414,7 @@ gst_rtp_vorbis_pay_handle_buffer (GstBaseRTPPayload * basepayload, size = GST_BUFFER_SIZE (buffer); data = GST_BUFFER_DATA (buffer); duration = GST_BUFFER_DURATION (buffer); + timestamp = GST_BUFFER_TIMESTAMP (buffer); GST_DEBUG_OBJECT (rtpvorbispay, "size %u, duration %" GST_TIME_FORMAT, size, GST_TIME_ARGS (duration)); @@ -471,8 +481,9 @@ gst_rtp_vorbis_pay_handle_buffer (GstBaseRTPPayload * basepayload, ret = gst_rtp_vorbis_pay_flush_packet (rtpvorbispay); /* create new packet if we must */ - if (!rtpvorbispay->packet) - gst_rtp_vorbis_pay_init_packet (rtpvorbispay, VDT); + if (!rtpvorbispay->packet) { + gst_rtp_vorbis_pay_init_packet (rtpvorbispay, VDT, timestamp); + } payload = gst_rtp_buffer_get_payload (rtpvorbispay->packet); ppos = payload + rtpvorbispay->payload_pos; @@ -521,7 +532,7 @@ gst_rtp_vorbis_pay_handle_buffer (GstBaseRTPPayload * basepayload, if (size > 0) { /* start new packet and get pointers. VDT stays the same. */ gst_rtp_vorbis_pay_init_packet (rtpvorbispay, - rtpvorbispay->payload_VDT); + rtpvorbispay->payload_VDT, timestamp); payload = gst_rtp_buffer_get_payload (rtpvorbispay->packet); ppos = payload + rtpvorbispay->payload_pos; } diff --git a/gst/rtp/gstrtpvorbispay.h b/gst/rtp/gstrtpvorbispay.h index 52ba0c16d8..af7b006f78 100644 --- a/gst/rtp/gstrtpvorbispay.h +++ b/gst/rtp/gstrtpvorbispay.h @@ -56,6 +56,7 @@ struct _GstRtpVorbisPay guint8 payload_F; guint8 payload_VDT; guint payload_pkts; + GstClockTime payload_timestamp; GstClockTime payload_duration; gint rate;