rtpg729pay: avoid basertppayload perfect-rtptime mode

G729 packets may only occur intermittently (e.g. cn packets), and as such
do not allow for perfect-rtptime calculating rtp times based on frame or byte
count.  In particular, do not use rtp audio base payloader as base class, but
rather base payloader directly.
This commit is contained in:
Mark Nauwelaerts 2010-08-02 12:56:29 +02:00
parent 6405df0c50
commit f1fe0e7157
2 changed files with 112 additions and 23 deletions

View File

@ -48,6 +48,9 @@ gst_rtp_g729_pay_set_caps (GstBaseRTPPayload * payload, GstCaps * caps);
static GstFlowReturn static GstFlowReturn
gst_rtp_g729_pay_handle_buffer (GstBaseRTPPayload * payload, GstBuffer * buf); gst_rtp_g729_pay_handle_buffer (GstBaseRTPPayload * payload, GstBuffer * buf);
static GstStateChangeReturn
gst_rtp_g729_pay_change_state (GstElement * element, GstStateChange transition);
static GstStaticPadTemplate gst_rtp_g729_pay_sink_template = static GstStaticPadTemplate gst_rtp_g729_pay_sink_template =
GST_STATIC_PAD_TEMPLATE ("sink", GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK, GST_PAD_SINK,
@ -71,8 +74,8 @@ static GstStaticPadTemplate gst_rtp_g729_pay_src_template =
"clock-rate = (int) 8000, " "encoding-name = (string) \"G729\"") "clock-rate = (int) 8000, " "encoding-name = (string) \"G729\"")
); );
GST_BOILERPLATE (GstRTPG729Pay, gst_rtp_g729_pay, GstBaseRTPAudioPayload, GST_BOILERPLATE (GstRTPG729Pay, gst_rtp_g729_pay, GstBaseRTPPayload,
GST_TYPE_BASE_RTP_AUDIO_PAYLOAD); GST_TYPE_BASE_RTP_PAYLOAD);
static void static void
gst_rtp_g729_pay_base_init (gpointer klass) gst_rtp_g729_pay_base_init (gpointer klass)
@ -92,11 +95,27 @@ gst_rtp_g729_pay_base_init (gpointer klass)
"G.729 RTP Payloader"); "G.729 RTP Payloader");
} }
static void
gst_rtp_g729_pay_finalize (GObject * object)
{
GstRTPG729Pay *pay = GST_RTP_G729_PAY (object);
g_object_unref (pay->adapter);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void static void
gst_rtp_g729_pay_class_init (GstRTPG729PayClass * klass) gst_rtp_g729_pay_class_init (GstRTPG729PayClass * klass)
{ {
GObjectClass *gobject_class = (GObjectClass *) klass;
GstElementClass *gstelement_class = (GstElementClass *) klass;
GstBaseRTPPayloadClass *payload_class = GST_BASE_RTP_PAYLOAD_CLASS (klass); GstBaseRTPPayloadClass *payload_class = GST_BASE_RTP_PAYLOAD_CLASS (klass);
gobject_class->finalize = gst_rtp_g729_pay_finalize;
gstelement_class->change_state = gst_rtp_g729_pay_change_state;
payload_class->set_caps = gst_rtp_g729_pay_set_caps; payload_class->set_caps = gst_rtp_g729_pay_set_caps;
payload_class->handle_buffer = gst_rtp_g729_pay_handle_buffer; payload_class->handle_buffer = gst_rtp_g729_pay_handle_buffer;
} }
@ -105,15 +124,18 @@ static void
gst_rtp_g729_pay_init (GstRTPG729Pay * pay, GstRTPG729PayClass * klass) gst_rtp_g729_pay_init (GstRTPG729Pay * pay, GstRTPG729PayClass * klass)
{ {
GstBaseRTPPayload *payload = GST_BASE_RTP_PAYLOAD (pay); GstBaseRTPPayload *payload = GST_BASE_RTP_PAYLOAD (pay);
GstBaseRTPAudioPayload *audiopayload = GST_BASE_RTP_AUDIO_PAYLOAD (pay);
payload->pt = GST_RTP_PAYLOAD_G729; payload->pt = GST_RTP_PAYLOAD_G729;
gst_basertppayload_set_options (payload, "audio", FALSE, "G729", 8000); gst_basertppayload_set_options (payload, "audio", FALSE, "G729", 8000);
gst_base_rtp_audio_payload_set_frame_based (audiopayload); pay->adapter = gst_adapter_new ();
gst_base_rtp_audio_payload_set_frame_options (audiopayload, }
G729_FRAME_DURATION_MS, G729_FRAME_SIZE);
static void
gst_rtp_g729_pay_reset (GstRTPG729Pay * pay)
{
gst_adapter_clear (pay->adapter);
pay->discont = FALSE;
} }
static gboolean static gboolean
@ -135,12 +157,49 @@ gst_rtp_g729_pay_set_caps (GstBaseRTPPayload * payload, GstCaps * caps)
return res; return res;
} }
static GstFlowReturn
gst_rtp_g729_pay_push (GstRTPG729Pay * rtpg729pay,
const guint8 * data, guint payload_len, GstClockTime timestamp,
GstClockTime duration)
{
GstBaseRTPPayload *basepayload;
GstBuffer *outbuf;
guint8 *payload;
GstFlowReturn ret;
basepayload = GST_BASE_RTP_PAYLOAD (rtpg729pay);
GST_DEBUG_OBJECT (rtpg729pay, "Pushing %d bytes ts %" GST_TIME_FORMAT,
payload_len, GST_TIME_ARGS (timestamp));
/* create buffer to hold the payload */
outbuf = gst_rtp_buffer_new_allocate (payload_len, 0, 0);
/* copy payload */
payload = gst_rtp_buffer_get_payload (outbuf);
memcpy (payload, data, payload_len);
/* set metadata */
GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
GST_BUFFER_DURATION (outbuf) = duration;
if (G_UNLIKELY (rtpg729pay->discont)) {
GST_DEBUG_OBJECT (basepayload, "discont, setting marker bit");
GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
gst_rtp_buffer_set_marker (outbuf, TRUE);
rtpg729pay->discont = FALSE;
}
ret = gst_basertppayload_push (basepayload, outbuf);
return ret;
}
static GstFlowReturn static GstFlowReturn
gst_rtp_g729_pay_handle_buffer (GstBaseRTPPayload * payload, GstBuffer * buf) gst_rtp_g729_pay_handle_buffer (GstBaseRTPPayload * payload, GstBuffer * buf)
{ {
GstFlowReturn ret = GST_FLOW_OK; GstFlowReturn ret = GST_FLOW_OK;
GstBaseRTPAudioPayload *basertpaudiopayload = GstRTPG729Pay *rtpg729pay = GST_RTP_G729_PAY (payload);
GST_BASE_RTP_AUDIO_PAYLOAD (payload);
GstAdapter *adapter = NULL; GstAdapter *adapter = NULL;
guint payload_len; guint payload_len;
guint available; guint available;
@ -164,7 +223,7 @@ gst_rtp_g729_pay_handle_buffer (GstBaseRTPPayload * payload, GstBuffer * buf)
(int) (ptime_ms / G729_FRAME_DURATION_MS); (int) (ptime_ms / G729_FRAME_DURATION_MS);
if (maxptime_octets < G729_FRAME_SIZE) { if (maxptime_octets < G729_FRAME_SIZE) {
GST_WARNING_OBJECT (basertpaudiopayload, "Given ptime %" G_GINT64_FORMAT GST_WARNING_OBJECT (payload, "Given ptime %" G_GINT64_FORMAT
" is smaller than minimum %d ns, overwriting to minimum", " is smaller than minimum %d ns, overwriting to minimum",
payload->max_ptime, G729_FRAME_DURATION_MS); payload->max_ptime, G729_FRAME_DURATION_MS);
maxptime_octets = G729_FRAME_SIZE; maxptime_octets = G729_FRAME_SIZE;
@ -174,7 +233,8 @@ gst_rtp_g729_pay_handle_buffer (GstBaseRTPPayload * payload, GstBuffer * buf)
max_payload_len = MIN ( max_payload_len = MIN (
/* MTU max */ /* MTU max */
(int) (gst_rtp_buffer_calc_payload_len (GST_BASE_RTP_PAYLOAD_MTU (int) (gst_rtp_buffer_calc_payload_len (GST_BASE_RTP_PAYLOAD_MTU
(basertpaudiopayload), 0, 0) / G729_FRAME_SIZE) * G729_FRAME_SIZE, (payload), 0, 0) / G729_FRAME_SIZE)
* G729_FRAME_SIZE,
/* ptime max */ /* ptime max */
maxptime_octets); maxptime_octets);
@ -207,25 +267,26 @@ gst_rtp_g729_pay_handle_buffer (GstBaseRTPPayload * payload, GstBuffer * buf)
min_payload_len = max_payload_len = ptime_in_bytes; min_payload_len = max_payload_len = ptime_in_bytes;
} }
GST_LOG_OBJECT (basertpaudiopayload, GST_LOG_OBJECT (payload,
"Calculated min_payload_len %u and max_payload_len %u", "Calculated min_payload_len %u and max_payload_len %u",
min_payload_len, max_payload_len); min_payload_len, max_payload_len);
adapter = gst_base_rtp_audio_payload_get_adapter (basertpaudiopayload); if (GST_BUFFER_IS_DISCONT (buf))
rtpg729pay->discont = TRUE;
adapter = rtpg729pay->adapter;
/* let's reset the base timestamp when the adapter is empty */ /* let's reset the base timestamp when the adapter is empty */
if (gst_adapter_available (adapter) == 0) if (gst_adapter_available (adapter) == 0)
basertpaudiopayload->base_ts = GST_BUFFER_TIMESTAMP (buf); rtpg729pay->next_ts = GST_BUFFER_TIMESTAMP (buf);
if (gst_adapter_available (adapter) == 0 && if (gst_adapter_available (adapter) == 0 &&
GST_BUFFER_SIZE (buf) >= min_payload_len && GST_BUFFER_SIZE (buf) >= min_payload_len &&
GST_BUFFER_SIZE (buf) <= max_payload_len) { GST_BUFFER_SIZE (buf) <= max_payload_len) {
ret = gst_base_rtp_audio_payload_push (basertpaudiopayload, ret = gst_rtp_g729_pay_push (rtpg729pay,
GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf), GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf),
GST_BUFFER_TIMESTAMP (buf)); GST_BUFFER_TIMESTAMP (buf), GST_BUFFER_DURATION (buf));
gst_buffer_unref (buf); gst_buffer_unref (buf);
g_object_unref (adapter);
return ret; return ret;
} }
@ -236,7 +297,7 @@ gst_rtp_g729_pay_handle_buffer (GstBaseRTPPayload * payload, GstBuffer * buf)
/* this loop will push all available buffers till the last frame */ /* this loop will push all available buffers till the last frame */
while (available >= min_payload_len || while (available >= min_payload_len ||
available % G729_FRAME_SIZE == G729B_CN_FRAME_SIZE) { available % G729_FRAME_SIZE == G729B_CN_FRAME_SIZE) {
guint num; GstClockTime duration;
/* We send as much as we can */ /* We send as much as we can */
if (available <= max_payload_len) { if (available <= max_payload_len) {
@ -246,17 +307,16 @@ gst_rtp_g729_pay_handle_buffer (GstBaseRTPPayload * payload, GstBuffer * buf)
(available / G729_FRAME_SIZE) * G729_FRAME_SIZE); (available / G729_FRAME_SIZE) * G729_FRAME_SIZE);
} }
ret = gst_base_rtp_audio_payload_flush (basertpaudiopayload, payload_len, duration = (payload_len / G729_FRAME_SIZE) * G729_FRAME_DURATION;
basertpaudiopayload->base_ts); rtpg729pay->next_ts += duration;
num = payload_len / G729_FRAME_SIZE; ret = gst_rtp_g729_pay_push (rtpg729pay,
basertpaudiopayload->base_ts += G729_FRAME_DURATION * num; gst_adapter_take (adapter, payload_len), payload_len,
rtpg729pay->next_ts, duration);
available = gst_adapter_available (adapter); available = gst_adapter_available (adapter);
} }
g_object_unref (adapter);
return ret; return ret;
/* ERRORS */ /* ERRORS */
@ -272,6 +332,31 @@ invalid_size:
} }
} }
static GstStateChangeReturn
gst_rtp_g729_pay_change_state (GstElement * element, GstStateChange transition)
{
GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
/* handle upwards state changes here */
switch (transition) {
default:
break;
}
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
/* handle downwards state changes */
switch (transition) {
case GST_STATE_CHANGE_PAUSED_TO_READY:
gst_rtp_g729_pay_reset (GST_RTP_G729_PAY (element));
break;
default:
break;
}
return ret;
}
gboolean gboolean
gst_rtp_g729_pay_plugin_init (GstPlugin * plugin) gst_rtp_g729_pay_plugin_init (GstPlugin * plugin)
{ {

View File

@ -43,6 +43,10 @@ typedef struct _GstRTPG729PayClass GstRTPG729PayClass;
struct _GstRTPG729Pay struct _GstRTPG729Pay
{ {
GstBaseRTPAudioPayload audiopayload; GstBaseRTPAudioPayload audiopayload;
GstAdapter *adapter;
GstClockTime next_ts;
gboolean discont;
}; };
struct _GstRTPG729PayClass struct _GstRTPG729PayClass