diff --git a/ChangeLog b/ChangeLog index 7354e9a23b..d9471eafda 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +2007-05-11 Wim Taymans + + * gst/rtp/README: + Update README with new RTP variables that will be used for + synchronisation. + + * gst/rtp/gstrtpvorbisdepay.c: (decode_base64), + (gst_rtp_vorbis_depay_parse_configuration), + (gst_rtp_vorbis_depay_process): + * gst/rtp/gstrtpvorbispay.c: (encode_base64), + (gst_rtp_vorbis_pay_finish_headers), + (gst_rtp_vorbis_pay_handle_buffer): + Update vorbis pay and depayloader to draft-04. + 2007-05-11 Wim Taymans * gst/rtsp/rtsptransport.c: diff --git a/common b/common index 1b4fb5836a..b5971d76cc 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 1b4fb5836a9e290fe13895643d41e0166de8a94c +Subproject commit b5971d76ccd216c27e095c02c3a369a9d05cb36d diff --git a/gst/rtp/README b/gst/rtp/README index 8f2cb73cbf..a4438cdaf6 100644 --- a/gst/rtp/README +++ b/gst/rtp/README @@ -22,16 +22,41 @@ The following fields can or must (*) be specified in the structure: set. * clock-rate: (int) [0 - MAXINT] - the RTP clock rate + The RTP clock rate. ssrc: (uint) [0 - MAXINT] - The ssrc value currently in use. + The ssrc value currently in use. (default = the SSRC of the first RTP + packet) + + npt-start: (uint64) [0 - MAXINT] + The Normal Play Time for clock-base. This is the position in the stream and + is between 0 and the duration of the stream. This value is expressed in + nanoseconds GstClockTime. (default = 0) + + npt-stop: (uint64) [0 - MAXINT] + The last position in the stream. This value is expressed in nanoseconds + GstClockTime. (default = -1, stop unknown) clock-base: (uint) [0 - MAXINT] - The RTP time representing time 0 + The RTP time representing time npt-start. (default = rtptime of first RTP + packet). + + play-speed: (gdouble) [-MIN - MAX] + The intended playback speed of the stream. The client is delivered data at + the adjusted speed. The client should adjust its playback speed with this + value and thus corresponds to the GStreamer rate field in the NEWSEGMENT + event. (default = 1.0) + + play-scale: (gdouble) [-MIN - MAX] + The rate already applied to the stream. The client is delivered a stream + that is scaled by this amount. This value is used to adjust position + reporting and corresponds to the GStream applied-rate field in the + NEWSEGMENT event. (default = 1.0) seqnum-base: (uint) [0 - MAXINT] - The RTP sequence number representing the first rtp packet + The RTP sequence number representing the first rtp packet. When this + parameter is given, all sequence numbers below this seqnum should be + ignored. (default = seqnum of first RTP packet). encoding-name: (String) ANY typically second part of the mime type. ex. MP4V-ES. only required if @@ -50,15 +75,15 @@ The following fields can or must (*) be specified in the structure: Example: "application/x-rtp", - "media", G_TYPE_STRING, "audio", -] - "payload", G_TYPE_INT, 96, ] - required - "clock-rate", G_TYPE_INT, 8000, -] - "encoding-name", G_TYPE_STRING, "AMR", -] - required since payload >= 96 - "encoding-params", G_TYPE_STRING, "1", -] - optional param for AMR - "octet-align", G_TYPE_STRING, "1", -] - "crc", G_TYPE_STRING, "0", ] - "robust-sorting", G_TYPE_STRING, "0", ] AMR specific params. - "interleaving", G_TYPE_STRING, "0", -] + "media", G_TYPE_STRING, "audio", -. + "payload", G_TYPE_INT, 96, | - required + "clock-rate", G_TYPE_INT, 8000, -' + "encoding-name", G_TYPE_STRING, "AMR", -. - required since payload >= 96 + "encoding-params", G_TYPE_STRING, "1", -' - optional param for AMR + "octet-align", G_TYPE_STRING, "1", -. + "crc", G_TYPE_STRING, "0", | + "robust-sorting", G_TYPE_STRING, "0", | AMR specific params. + "interleaving", G_TYPE_STRING, "0", -' Mapping of caps to and from SDP fields: @@ -79,6 +104,18 @@ The following fields can or must (*) be specified in the structure: always use the lowercase names so that the SDP -> caps mapping remains possible. + Mapping of caps to NEWSEGMENT: + + rate: + applied-rate: + format: GST_FORMAT_TIME + start: * GST_SECOND / + stop: if != -1 + - + start + else + -1 + time: + usage with UDP -------------- diff --git a/gst/rtp/gstrtpvorbisdepay.c b/gst/rtp/gstrtpvorbisdepay.c index d71c72ac0b..545d3b1834 100644 --- a/gst/rtp/gstrtpvorbisdepay.c +++ b/gst/rtp/gstrtpvorbisdepay.c @@ -30,11 +30,15 @@ GST_DEBUG_CATEGORY_STATIC (rtpvorbisdepay_debug); #define GST_CAT_DEFAULT (rtpvorbisdepay_debug) +/* references: + * http://svn.xiph.org/trunk/vorbis/doc/draft-ietf-avt-rtp-vorbis-04.txt + */ + /* elementfactory information */ static const GstElementDetails gst_rtp_vorbis_depay_details = GST_ELEMENT_DETAILS ("RTP packet depayloader", "Codec/Depayloader/Network", - "Extracts Vorbis Audio from RTP packets (draft-01 of RFC XXXX)", + "Extracts Vorbis Audio from RTP packets (draft-04 of RFC XXXX)", "Wim Taymans "); /* RtpVorbisDepay signals and args */ @@ -151,28 +155,70 @@ gst_rtp_vorbis_depay_finalize (GObject * object) G_OBJECT_CLASS (parent_class)->finalize (object); } +static const guint8 a2bin[256] = { + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64, + 64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64, + 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64 +}; + +static guint +decode_base64 (const gchar * in, guint8 * out) +{ + guint8 v1, v2; + guint len = 0; + + v1 = a2bin[(gint) * in]; + while (v1 <= 63) { + /* read 4 bytes, write 3 bytes, invalid base64 are zeroes */ + v2 = a2bin[(gint) * ++in]; + *out++ = (v1 << 2) | ((v2 & 0x3f) >> 4); + v1 = (v2 > 63 ? 64 : a2bin[(gint) * ++in]); + *out++ = (v2 << 4) | ((v1 & 0x3f) >> 2); + v2 = (v1 > 63 ? 64 : a2bin[(gint) * ++in]); + *out++ = (v1 << 6) | (v2 & 0x3f); + v1 = (v2 > 63 ? 64 : a2bin[(gint) * ++in]); + len += 3; + } + /* move to '\0' */ + while (*in != '\0') + in++; + + /* subtract padding */ + while (len > 0 && *--in == '=') + len--; + + return len; +} + 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; + gint i, j; - /* deserialize base16 to buffer */ - g_value_init (&v, GST_TYPE_BUFFER); - if (!gst_value_deserialize (&v, configuration)) - goto wrong_configuration; + /* deserialize base64 to buffer */ + size = strlen (configuration); + GST_DEBUG_OBJECT (rtpvorbisdepay, "base64 config size %u", size); - buf = gst_value_get_buffer (&v); - gst_buffer_ref (buf); - g_value_unset (&v); - - data = GST_BUFFER_DATA (buf); - size = GST_BUFFER_SIZE (buf); + data = g_malloc (size); + size = decode_base64 (configuration, data); GST_DEBUG_OBJECT (rtpvorbisdepay, "config size %u", size); @@ -216,59 +262,73 @@ gst_rtp_vorbis_depay_parse_configuration (GstRtpVorbisDepay * rtpvorbisdepay, for (i = 0; i < num_headers; i++) { guint32 ident; guint16 length; + guint8 n_headers, b; GstRtpVorbisConfig *conf; - GstTagList *list; + guint *h_sizes; - if (size < 5) + if (size < 6) goto too_small; ident = (data[0] << 16) | (data[1] << 8) | data[2]; length = (data[3] << 8) | data[4]; - size -= 5; - data += 5; + n_headers = data[5]; + size -= 6; + data += 6; - GST_DEBUG_OBJECT (rtpvorbisdepay, "header %d, ident 0x%08x, length %u", i, - ident, length); + GST_DEBUG_OBJECT (rtpvorbisdepay, + "header %d, ident 0x%08x, length %u, left %u", i, ident, length, size); - if (size < length + VORBIS_ID_LEN) + if (size < length) goto too_small; - GST_DEBUG_OBJECT (rtpvorbisdepay, "preparing headers"); + /* read header sizes we read 2 sizes, the third size (for which we allocate + * space) must be derived from the total packed header length. */ + h_sizes = g_newa (guint, n_headers + 1); + for (j = 0; j < n_headers; j++) { + guint h_size; + h_size = 0; + do { + if (size < 1) + goto too_small; + b = *data++; + size--; + h_size = (h_size << 7) | (b & 0x7f); + } while (b & 0x80); + GST_DEBUG_OBJECT (rtpvorbisdepay, "headers %d: size: %u", j, h_size); + h_sizes[j] = h_size; + length -= h_size; + } + /* last header length is the remaining space */ + GST_DEBUG_OBJECT (rtpvorbisdepay, "last header size: %u", length); + h_sizes[j] = length; + + 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; + for (j = 0; j <= n_headers; j++) { + guint h_size; - /* 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); + h_size = h_sizes[j]; + if (size < h_size) + goto too_small; - 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; + GST_DEBUG_OBJECT (rtpvorbisdepay, "reading header %d, size %u", j, + h_size); + buf = gst_buffer_new_and_alloc (h_size); + memcpy (GST_BUFFER_DATA (buf), data, h_size); + conf->headers = g_list_append (conf->headers, buf); + data += h_size; + size -= h_size; + } 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"); @@ -421,6 +481,8 @@ gst_rtp_vorbis_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf) payload = gst_rtp_buffer_get_payload (buf); free_payload = FALSE; + gst_util_dump_mem (GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf)); + header = GST_READ_UINT32_BE (payload); /* * 0 1 2 3 diff --git a/gst/rtp/gstrtpvorbispay.c b/gst/rtp/gstrtpvorbispay.c index 35478a27c1..f85ee94cc9 100644 --- a/gst/rtp/gstrtpvorbispay.c +++ b/gst/rtp/gstrtpvorbispay.c @@ -32,7 +32,7 @@ GST_DEBUG_CATEGORY_STATIC (rtpvorbispay_debug); #define GST_CAT_DEFAULT (rtpvorbispay_debug) /* references: - * http://svn.xiph.org/trunk/vorbis/doc/draft-ietf-avt-rtp-vorbis-01.txt + * http://svn.xiph.org/trunk/vorbis/doc/draft-ietf-avt-rtp-vorbis-04.txt */ /* elementfactory information */ @@ -208,27 +208,47 @@ gst_rtp_vorbis_pay_flush_packet (GstRtpVorbisPay * rtpvorbispay) return ret; } +static gchar * +encode_base64 (const guint8 * in, guint size, guint * len) +{ + gchar *ret, *d; + static const gchar *v = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + *len = ((size + 2) / 3) * 4; + d = ret = (gchar *) g_malloc (*len + 1); + for (; size; in += 3) { /* process tuplets */ + *d++ = v[in[0] >> 2]; /* byte 1: high 6 bits (1) */ + /* byte 2: low 2 bits (1), high 4 bits (2) */ + *d++ = v[((in[0] << 4) + (--size ? (in[1] >> 4) : 0)) & 0x3f]; + /* byte 3: low 4 bits (2), high 2 bits (3) */ + *d++ = size ? v[((in[1] << 2) + (--size ? (in[2] >> 6) : 0)) & 0x3f] : '='; + /* byte 4: low 6 bits (3) */ + *d++ = size ? v[in[2] & 0x3f] : '='; + if (size) + size--; /* count third character if processed */ + } + *d = '\0'; /* tie off string */ + + return ret; /* return the resulting string */ +} + + static gboolean gst_rtp_vorbis_pay_finish_headers (GstBaseRTPPayload * basepayload) { GstRtpVorbisPay *rtpvorbispay = GST_RTP_VORBIS_PAY (basepayload); GList *walk; - guint length; + guint length, size, n_headers, configlen; gchar *cstr, *configuration; - guint8 *data; + guint8 *data, *config; guint32 ident; - GValue v = { 0 }; - GstBuffer *config; GST_DEBUG_OBJECT (rtpvorbispay, "finish headers"); if (!rtpvorbispay->headers) goto no_headers; - /* we need exactly 2 header packets */ - if (g_list_length (rtpvorbispay->headers) != 2) - goto no_headers; - /* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Number of packed headers | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ @@ -247,35 +267,60 @@ gst_rtp_vorbis_pay_finish_headers (GstBaseRTPPayload * basepayload) * 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 | .. + * | Ident | length .. * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * .. length | Identification Header .. + * .. | n. of headers | length1 | length2 .. * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * .. Identification Header | + * .. | Identification Header .. + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ................................................................. + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * .. | Comment Header .. + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ................................................................. + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * .. Comment Header | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Setup Header .. * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ................................................................. + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * .. Setup Header | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * */ - /* count the size of the headers first */ + /* we need 4 bytes for the number of headers (which is always 1), 3 bytes for + * the ident, 2 bytes for length, 1 byte for n. of headers. */ + size = 4 + 3 + 2 + 1; + + /* count the size of the headers first and update the hash */ length = 0; + n_headers = 0; ident = fnv1_hash_32_new (); for (walk = rtpvorbispay->headers; walk; walk = g_list_next (walk)) { GstBuffer *buf = GST_BUFFER_CAST (walk->data); + guint bsize; - ident = - fnv1_hash_32_update (ident, GST_BUFFER_DATA (buf), + bsize = GST_BUFFER_SIZE (buf); + length += bsize; + n_headers++; + + /* count number of bytes needed for length fields, we don't need this for + * the last header. */ + if (g_list_next (walk)) { + do { + size++; + bsize >>= 7; + } while (bsize); + } + /* update hash */ + ident = fnv1_hash_32_update (ident, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf)); - length += GST_BUFFER_SIZE (buf); } - /* 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); + /* packet length is header size + packet length */ + configlen = size + length; + config = data = g_malloc (configlen); /* number of packed headers, we only pack 1 header */ data[0] = 0; @@ -292,12 +337,44 @@ gst_rtp_vorbis_pay_finish_headers (GstBaseRTPPayload * basepayload) data[5] = (ident >> 8) & 0xff; data[6] = ident & 0xff; - /* store length minus the length of the fixed vorbis header */ - data[7] = ((length - 30) >> 8) & 0xff; - data[8] = (length - 30) & 0xff; + /* store length of all vorbis headers */ + data[7] = ((length) >> 8) & 0xff; + data[8] = (length) & 0xff; + + /* store number of headers minus one. */ + data[9] = n_headers - 1; + data += 10; + + /* store length for each header */ + for (walk = rtpvorbispay->headers; walk; walk = g_list_next (walk)) { + GstBuffer *buf = GST_BUFFER_CAST (walk->data); + guint bsize, size, temp; + + /* only need to store the length when it's not the last header */ + if (!g_list_next (walk)) + break; + + bsize = GST_BUFFER_SIZE (buf); + + /* calc size */ + size = 0; + do { + size++; + bsize >>= 7; + } while (bsize); + temp = size; + + bsize = GST_BUFFER_SIZE (buf); + /* write the size backwards */ + while (size) { + size--; + data[size] = bsize & 0x7f; + bsize >>= 7; + } + data += temp; + } /* copy header data */ - data += 9; for (walk = rtpvorbispay->headers; walk; walk = g_list_next (walk)) { GstBuffer *buf = GST_BUFFER_CAST (walk->data); @@ -305,10 +382,9 @@ gst_rtp_vorbis_pay_finish_headers (GstBaseRTPPayload * basepayload) data += GST_BUFFER_SIZE (buf); } - /* serialize buffer to base16 */ - g_value_init (&v, GST_TYPE_BUFFER); - gst_value_take_buffer (&v, config); - configuration = gst_value_serialize (&v); + /* serialize to base64 */ + configuration = encode_base64 (config, configlen, &size); + g_free (config); /* configure payloader settings */ cstr = g_strdup_printf ("%d", rtpvorbispay->channels); @@ -322,7 +398,6 @@ gst_rtp_vorbis_pay_finish_headers (GstBaseRTPPayload * basepayload) NULL); g_free (cstr); g_free (configuration); - g_value_unset (&v); return TRUE; @@ -450,12 +525,7 @@ gst_rtp_vorbis_pay_handle_buffer (GstBaseRTPPayload * basepayload, if (rtpvorbispay->need_headers) { /* we need to collect the headers and construct a config string from them */ - if (VDT == 2) { - GST_DEBUG_OBJECT (rtpvorbispay, - "discard comment packet while collecting headers"); - ret = GST_FLOW_OK; - goto done; - } else if (VDT != 0) { + if (VDT != 0) { GST_DEBUG_OBJECT (rtpvorbispay, "collecting header"); /* append header to the list of headers */ rtpvorbispay->headers = g_list_append (rtpvorbispay->headers, buffer);