diff --git a/gst/rtp/gstrtpg726pay.c b/gst/rtp/gstrtpg726pay.c index babf3a28c7..9903a7c807 100644 --- a/gst/rtp/gstrtpg726pay.c +++ b/gst/rtp/gstrtpg726pay.c @@ -30,6 +30,18 @@ #include "gstrtpg726pay.h" +GST_DEBUG_CATEGORY_STATIC (rtpg726pay_debug); +#define GST_CAT_DEFAULT (rtpg726pay_debug) + +#define DEFAULT_FORCE_AAL2 TRUE + +enum +{ + PROP_0, + PROP_FORCE_AAL2, + PROP_LAST +}; + static const GstElementDetails gst_rtp_g726_pay_details = GST_ELEMENT_DETAILS ("RTP G.726 payloader", "Codec/Payloader/Network", @@ -55,11 +67,19 @@ GST_STATIC_PAD_TEMPLATE ("src", "media = (string) \"audio\", " "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", " "clock-rate = (int) 8000, " - "encoding-name = (string) { \"G726-16\", \"G726-24\", \"G726-32\", \"G726-40\" } ") + "encoding-name = (string) { \"G726-16\", \"G726-24\", \"G726-32\", \"G726-40\", " + " \"AAL2-G726-16\", \"AAL2-G726-24\", \"AAL2-G726-32\", \"AAL2-G726-40\" } ") ); +static void gst_rtp_g726_pay_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); +static void gst_rtp_g726_pay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); + static gboolean gst_rtp_g726_pay_setcaps (GstBaseRTPPayload * payload, GstCaps * caps); +static GstFlowReturn gst_rtp_g726_pay_handle_buffer (GstBaseRTPPayload * + payload, GstBuffer * buffer); GST_BOILERPLATE (GstRtpG726Pay, gst_rtp_g726_pay, GstBaseRTPAudioPayload, GST_TYPE_BASE_RTP_AUDIO_PAYLOAD); @@ -83,9 +103,19 @@ gst_rtp_g726_pay_class_init (GstRtpG726PayClass * klass) gstbasertppayload_class = (GstBaseRTPPayloadClass *) klass; - parent_class = g_type_class_peek_parent (klass); + gobject_class->set_property = gst_rtp_g726_pay_set_property; + gobject_class->get_property = gst_rtp_g726_pay_get_property; + + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_FORCE_AAL2, + g_param_spec_boolean ("force-aal2", "Force AAL2", + "Force AAL2 encoding for compatibility with bad depayloaders", + DEFAULT_FORCE_AAL2, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); gstbasertppayload_class->set_caps = gst_rtp_g726_pay_setcaps; + gstbasertppayload_class->handle_buffer = gst_rtp_g726_pay_handle_buffer; + + GST_DEBUG_CATEGORY_INIT (rtpg726pay_debug, "rtpg726pay", 0, + "G.726 RTP Payloader"); } static void @@ -97,6 +127,8 @@ gst_rtp_g726_pay_init (GstRtpG726Pay * rtpg726pay, GstRtpG726PayClass * klass) GST_BASE_RTP_PAYLOAD (rtpg726pay)->clock_rate = 8000; + rtpg726pay->force_aal2 = DEFAULT_FORCE_AAL2; + /* sample based codec */ gst_base_rtp_audio_payload_set_sample_based (basertpaudiopayload); } @@ -105,16 +137,25 @@ static gboolean gst_rtp_g726_pay_setcaps (GstBaseRTPPayload * payload, GstCaps * caps) { gchar *encoding_name; - GstStructure *structure = gst_caps_get_structure (caps, 0); + GstStructure *structure; GstBaseRTPAudioPayload *basertpaudiopayload; - gint bitrate; + GstRtpG726Pay *pay; + GstCaps *peercaps; basertpaudiopayload = GST_BASE_RTP_AUDIO_PAYLOAD (payload); + pay = GST_RTP_G726_PAY (payload); - if (!gst_structure_get_int (structure, "bitrate", &bitrate)) - bitrate = 32000; + structure = gst_caps_get_structure (caps, 0); - switch (bitrate) { + if (!gst_structure_get_int (structure, "bitrate", &pay->bitrate)) + pay->bitrate = 32000; + + GST_DEBUG_OBJECT (payload, "using bitrate %d", pay->bitrate); + + pay->aal2 = FALSE; + + /* first see what we can do with the bitrate */ + switch (pay->bitrate) { case 16000: encoding_name = g_strdup ("G726-16"); gst_base_rtp_audio_payload_set_samplebits_options (basertpaudiopayload, @@ -139,6 +180,65 @@ gst_rtp_g726_pay_setcaps (GstBaseRTPPayload * payload, GstCaps * caps) goto invalid_bitrate; } + GST_DEBUG_OBJECT (payload, "selected base encoding %s", encoding_name); + + /* now see if we need to produce AAL2 or not */ + peercaps = gst_pad_peer_get_caps (payload->srcpad); + if (peercaps) { + GstCaps *filter, *intersect; + gchar *capsstr; + + GST_DEBUG_OBJECT (payload, "have peercaps %" GST_PTR_FORMAT, peercaps); + + capsstr = g_strdup_printf ("application/x-rtp, " + "media = (string) \"audio\", " + "clock-rate = (int) 8000, " + "encoding-name = (string) %s; " + "application/x-rtp, " + "media = (string) \"audio\", " + "clock-rate = (int) 8000, " + "encoding-name = (string) AAL2-%s", encoding_name, encoding_name); + filter = gst_caps_from_string (capsstr); + g_free (capsstr); + + /* intersect to filter */ + intersect = gst_caps_intersect (peercaps, filter); + gst_caps_unref (peercaps); + + GST_DEBUG_OBJECT (payload, "intersected to %" GST_PTR_FORMAT, intersect); + + if (!intersect) + goto no_format; + if (gst_caps_is_empty (intersect)) { + gst_caps_unref (intersect); + goto no_format; + } + + structure = gst_caps_get_structure (intersect, 0); + + /* now see what encoding name we settled on, we need to dup because the + * string goes away when we unref the intersection below. */ + encoding_name = + g_strdup (gst_structure_get_string (structure, "encoding-name")); + + /* if we managed to negotiate to AAL2, we definatly are going to do AAL2 + * encoding. Else we only encode AAL2 when explicitly set by the + * property. */ + if (g_str_has_prefix (encoding_name, "AAL2-")) + pay->aal2 = TRUE; + else + pay->aal2 = pay->force_aal2; + + GST_DEBUG_OBJECT (payload, "final encoding %s, AAL2 %d", encoding_name, + pay->aal2); + + gst_caps_unref (intersect); + } else { + /* downstream can do anything but we prefer the better supported non-AAL2 */ + pay->aal2 = pay->force_aal2; + GST_DEBUG_OBJECT (payload, "no peer caps, AAL2 %d", pay->aal2); + } + gst_basertppayload_set_options (payload, "audio", TRUE, encoding_name, 8000); gst_basertppayload_set_outcaps (payload, NULL); @@ -149,9 +249,166 @@ gst_rtp_g726_pay_setcaps (GstBaseRTPPayload * payload, GstCaps * caps) /* ERRORS */ invalid_bitrate: { - GST_ERROR_OBJECT (payload, "invalid bitrate %d specified", bitrate); + GST_ERROR_OBJECT (payload, "invalid bitrate %d specified", pay->bitrate); return FALSE; } +no_format: + { + GST_ERROR_OBJECT (payload, "could not negotiate format"); + return FALSE; + } +} + +static GstFlowReturn +gst_rtp_g726_pay_handle_buffer (GstBaseRTPPayload * payload, GstBuffer * buffer) +{ + GstFlowReturn res; + GstRtpG726Pay *pay; + + pay = GST_RTP_G726_PAY (payload); + + if (!pay->aal2) { + guint8 *data, tmp; + guint len; + + /* for non AAL2, we need to reshuffle the bytes, we can do this in-place + * when the buffer is writable. */ + buffer = gst_buffer_make_writable (buffer); + + data = GST_BUFFER_DATA (buffer); + len = GST_BUFFER_SIZE (buffer); + + GST_LOG_OBJECT (pay, "packing %u bytes of data", len); + + /* we need to reshuffle the bytes, output is of the form: + * A B C D .. with the number of bits depending on the bitrate. */ + switch (pay->bitrate) { + case 16000: + { + /* 0 + * 0 1 2 3 4 5 6 7 + * +-+-+-+-+-+-+-+-+- + * |D D|C C|B B|A A| ... + * |0 1|0 1|0 1|0 1| + * +-+-+-+-+-+-+-+-+- + */ + while (len > 0) { + tmp = *data; + *data++ = ((tmp & 0xc0) >> 6) | + ((tmp & 0x30) >> 2) | ((tmp & 0x0c) << 2) | ((tmp & 0x03) << 6); + len--; + } + break; + } + case 24000: + { + /* 0 1 2 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- + * |C C|B B B|A A A|F|E E E|D D D|C|H H H|G G G|F F| ... + * |1 2|0 1 2|0 1 2|2|0 1 2|0 1 2|0|0 1 2|0 1 2|0 1| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- + */ + while (len > 2) { + tmp = *data; + *data++ = ((tmp & 0xc0) >> 6) | + ((tmp & 0x38) >> 1) | ((tmp & 0x07) << 5); + tmp = *data; + *data++ = ((tmp & 0x80) >> 7) | + ((tmp & 0x70) >> 3) | ((tmp & 0x0e) << 4) | ((tmp & 0x01) << 7); + tmp = *data; + *data++ = ((tmp & 0xe0) >> 5) | + ((tmp & 0x1c) >> 2) | ((tmp & 0x03) << 6); + len -= 3; + } + break; + } + case 32000: + { + /* 0 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- + * |B B B B|A A A A|D D D D|C C C C| ... + * |0 1 2 3|0 1 2 3|0 1 2 3|0 1 2 3| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- + */ + while (len > 0) { + tmp = *data; + *data++ = ((tmp & 0xf0) >> 4) | ((tmp & 0x0f) << 4); + len--; + } + break; + } + case 40000: + { + /* 0 1 2 3 4 + * 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 2 3 4 5 6 7 8 9 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- + * |B B B|A A A A A|D|C C C C C|B B|E E E E|D D D D|G G|F F F F F|E|H H H H H|G G G| + * |2 3 4|0 1 2 3 4|4|0 1 2 3 4|0 1|1 2 3 4|0 1 2 3|3 4|0 1 2 3 4|0|0 1 2 3 4|0 1 2| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- + */ + while (len > 4) { + tmp = *data; + *data++ = ((tmp & 0xe0) >> 5) | ((tmp & 0x1f) << 3); + tmp = *data; + *data++ = ((tmp & 0x80) >> 7) | + ((tmp & 0x7c) >> 2) | ((tmp & 0x03) << 6); + tmp = *data; + *data++ = ((tmp & 0xf0) >> 4) | ((tmp & 0x0f) << 4); + tmp = *data; + *data++ = ((tmp & 0xc0) >> 6) | + ((tmp & 0x3e) << 2) | ((tmp & 0x01) << 7); + tmp = *data; + *data++ = ((tmp & 0xf8) >> 3) | ((tmp & 0x07) << 5); + len -= 5; + } + break; + } + } + } + + res = + GST_BASE_RTP_PAYLOAD_CLASS (parent_class)->handle_buffer (payload, + buffer); + + return res; +} + +static void +gst_rtp_g726_pay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstRtpG726Pay *rtpg726pay; + + rtpg726pay = GST_RTP_G726_PAY (object); + + switch (prop_id) { + case PROP_FORCE_AAL2: + rtpg726pay->force_aal2 = g_value_get_boolean (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_rtp_g726_pay_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstRtpG726Pay *rtpg726pay; + + rtpg726pay = GST_RTP_G726_PAY (object); + + switch (prop_id) { + case PROP_FORCE_AAL2: + g_value_set_boolean (value, rtpg726pay->force_aal2); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } } gboolean diff --git a/gst/rtp/gstrtpg726pay.h b/gst/rtp/gstrtpg726pay.h index 43eaf63fc8..1d9856b99d 100644 --- a/gst/rtp/gstrtpg726pay.h +++ b/gst/rtp/gstrtpg726pay.h @@ -36,6 +36,10 @@ typedef struct _GstRtpG726PayClass GstRtpG726PayClass; struct _GstRtpG726Pay { GstBaseRTPAudioPayload audiopayload; + + gboolean aal2; + gboolean force_aal2; + gint bitrate; }; struct _GstRtpG726PayClass