rtph264pay: push NALs only after SPS/PPS

parse complete (bytestream) buffer for SPS/PPS before pushing NALs.

Fixes #564501.
This commit is contained in:
Mark Nauwelaerts 2009-08-03 18:55:19 +02:00 committed by Wim Taymans
parent 198604c108
commit 2bfb42c5f8
2 changed files with 68 additions and 42 deletions

View File

@ -184,6 +184,7 @@ gst_rtp_h264_pay_class_init (GstRtpH264PayClass * klass)
static void static void
gst_rtp_h264_pay_init (GstRtpH264Pay * rtph264pay, GstRtpH264PayClass * klass) gst_rtp_h264_pay_init (GstRtpH264Pay * rtph264pay, GstRtpH264PayClass * klass)
{ {
rtph264pay->queue = g_queue_new ();
rtph264pay->profile = 0; rtph264pay->profile = 0;
rtph264pay->sps = NULL; rtph264pay->sps = NULL;
rtph264pay->pps = NULL; rtph264pay->pps = NULL;
@ -198,6 +199,8 @@ gst_rtp_h264_pay_finalize (GObject * object)
rtph264pay = GST_RTP_H264_PAY (object); rtph264pay = GST_RTP_H264_PAY (object);
g_queue_free (rtph264pay->queue);
g_free (rtph264pay->sps); g_free (rtph264pay->sps);
g_free (rtph264pay->pps); g_free (rtph264pay->pps);
@ -437,17 +440,18 @@ is_nal_equal (const guint8 * nal1, const guint8 * nal2, guint len)
} }
} }
static void static gboolean
gst_rtp_h264_pay_decode_nal (GstRtpH264Pay * payloader, gst_rtp_h264_pay_decode_nal (GstRtpH264Pay * payloader,
guint8 * data, guint size, gboolean * updated) guint8 * data, guint size)
{ {
guint8 *sps = NULL, *pps = NULL; guint8 *sps = NULL, *pps = NULL;
guint sps_len = 0, pps_len = 0; guint sps_len = 0, pps_len = 0;
guint8 header, type; guint8 header, type;
guint len; guint len;
gboolean updated;
/* default is no update */ /* default is no update */
*updated = FALSE; updated = FALSE;
GST_DEBUG ("NAL payload len=%u", size); GST_DEBUG ("NAL payload len=%u", size);
@ -479,7 +483,7 @@ gst_rtp_h264_pay_decode_nal (GstRtpH264Pay * payloader,
/* If we encountered an SPS and/or a PPS, check if it's the /* If we encountered an SPS and/or a PPS, check if it's the
* same as the one we have. If not, update our version and * same as the one we have. If not, update our version and
* set *updated to TRUE * set updated to TRUE
*/ */
if (sps_len > 0) { if (sps_len > 0) {
if ((payloader->sps_len != sps_len) if ((payloader->sps_len != sps_len)
@ -494,7 +498,7 @@ gst_rtp_h264_pay_decode_nal (GstRtpH264Pay * payloader,
payloader->sps = sps_len ? g_new (guint8, sps_len) : NULL; payloader->sps = sps_len ? g_new (guint8, sps_len) : NULL;
memcpy (payloader->sps, sps, sps_len); memcpy (payloader->sps, sps, sps_len);
payloader->sps_len = sps_len; payloader->sps_len = sps_len;
*updated = TRUE; updated = TRUE;
} }
} }
@ -507,53 +511,47 @@ gst_rtp_h264_pay_decode_nal (GstRtpH264Pay * payloader,
payloader->pps = pps_len ? g_new (guint8, pps_len) : NULL; payloader->pps = pps_len ? g_new (guint8, pps_len) : NULL;
memcpy (payloader->pps, pps, pps_len); memcpy (payloader->pps, pps, pps_len);
payloader->pps_len = pps_len; payloader->pps_len = pps_len;
*updated = TRUE; updated = TRUE;
} }
} }
return updated;
} }
static void static void
gst_rtp_h264_pay_parse_sps_pps (GstBaseRTPPayload * basepayload, gst_rtp_h264_pay_set_sps_pps (GstBaseRTPPayload * basepayload)
guint8 * data, guint size)
{ {
gboolean update = FALSE;
GstRtpH264Pay *payloader = GST_RTP_H264_PAY (basepayload); GstRtpH264Pay *payloader = GST_RTP_H264_PAY (basepayload);
gchar *profile;
gchar *sps;
gchar *pps;
gchar *sprops;
gst_rtp_h264_pay_decode_nal (payloader, data, size, &update); /* profile is 24 bit. Force it to respect the limit */
profile = g_strdup_printf ("%06x", payloader->profile & 0xffffff);
/* if has new SPS & PPS, update the output caps */ /* build the sprop-parameter-sets */
if (update) { sps = (payloader->sps_len > 0)
gchar *profile; ? g_base64_encode (payloader->sps, payloader->sps_len) : NULL;
gchar *sps; pps = (payloader->pps_len > 0)
gchar *pps; ? g_base64_encode (payloader->pps, payloader->pps_len) : NULL;
gchar *sprops;
/* profile is 24 bit. Force it to respect the limit */ if (sps)
profile = g_strdup_printf ("%06x", payloader->profile & 0xffffff); sprops = g_strjoin (",", sps, pps, NULL);
else
sprops = g_strdup (pps);
/* build the sprop-parameter-sets */ gst_basertppayload_set_outcaps (basepayload, "profile-level-id",
sps = (payloader->sps_len > 0) G_TYPE_STRING, profile,
? g_base64_encode (payloader->sps, payloader->sps_len) : NULL; "sprop-parameter-sets", G_TYPE_STRING, sprops, NULL);
pps = (payloader->pps_len > 0)
? g_base64_encode (payloader->pps, payloader->pps_len) : NULL;
if (sps) GST_DEBUG ("outcaps udpate: profile=%s, sps=%s, pps=%s",
sprops = g_strjoin (",", sps, pps, NULL); profile, GST_STR_NULL (sps), GST_STR_NULL (pps));
else
sprops = g_strdup (pps);
gst_basertppayload_set_outcaps (basepayload, "profile-level-id", g_free (sprops);
G_TYPE_STRING, profile, g_free (profile);
"sprop-parameter-sets", G_TYPE_STRING, sprops, NULL); g_free (sps);
g_free (pps);
GST_DEBUG ("outcaps udpate: profile=%s, sps=%s, pps=%s",
profile, GST_STR_NULL (sps), GST_STR_NULL (pps));
g_free (sprops);
g_free (profile);
g_free (sps);
g_free (pps);
}
} }
static GstFlowReturn static GstFlowReturn
@ -735,8 +733,9 @@ gst_rtp_h264_pay_handle_buffer (GstBaseRTPPayload * basepayload,
GstRtpH264Pay *rtph264pay; GstRtpH264Pay *rtph264pay;
GstFlowReturn ret; GstFlowReturn ret;
guint size, nal_len; guint size, nal_len;
guint8 *data; guint8 *data, *nal_data;
GstClockTime timestamp; GstClockTime timestamp;
GQueue *nal_queue;
rtph264pay = GST_RTP_H264_PAY (basepayload); rtph264pay = GST_RTP_H264_PAY (basepayload);
@ -788,6 +787,7 @@ gst_rtp_h264_pay_handle_buffer (GstBaseRTPPayload * basepayload,
} }
} else { } else {
guint next; guint next;
gboolean update = FALSE;
/* get offset of first start code */ /* get offset of first start code */
next = next_start_code (data, size); next = next_start_code (data, size);
@ -796,10 +796,13 @@ gst_rtp_h264_pay_handle_buffer (GstBaseRTPPayload * basepayload,
* will not collect data. */ * will not collect data. */
data += next; data += next;
size -= next; size -= next;
nal_data = data;
nal_queue = rtph264pay->queue;
GST_DEBUG_OBJECT (basepayload, "found first start at %u, bytes left %u", GST_DEBUG_OBJECT (basepayload, "found first start at %u, bytes left %u",
next, size); next, size);
/* first pass to locate NALs and parse SPS/PPS */
while (size > 4) { while (size > 4) {
/* skip start code */ /* skip start code */
data += 4; data += 4;
@ -842,21 +845,43 @@ gst_rtp_h264_pay_handle_buffer (GstBaseRTPPayload * basepayload,
} else { } else {
/* We know our stream is a valid H264 NAL packet, /* We know our stream is a valid H264 NAL packet,
* go parse it for SPS/PPS to enrich the caps */ * go parse it for SPS/PPS to enrich the caps */
gst_rtp_h264_pay_parse_sps_pps (basepayload, data, nal_len); /* order: make sure to check nal */
update = gst_rtp_h264_pay_decode_nal (rtph264pay, data, nal_len) ||
update;
} }
/* move to next NAL packet */
data += nal_len;
size -= nal_len;
g_queue_push_tail (nal_queue, GUINT_TO_POINTER (nal_len));
}
/* if has new SPS & PPS, update the output caps */
if (G_UNLIKELY (update))
gst_rtp_h264_pay_set_sps_pps (basepayload);
/* second pass to payload and push */
data = nal_data;
while ((nal_len = GPOINTER_TO_UINT (g_queue_pop_head (nal_queue))) > 0) {
/* skip start code */
data += 4;
/* put the data in one or more RTP packets */ /* put the data in one or more RTP packets */
ret = ret =
gst_rtp_h264_pay_payload_nal (basepayload, data, nal_len, timestamp, gst_rtp_h264_pay_payload_nal (basepayload, data, nal_len, timestamp,
buffer); buffer);
if (ret != GST_FLOW_OK) if (ret != GST_FLOW_OK) {
g_queue_clear (nal_queue);
break; break;
}
/* move to next NAL packet */ /* move to next NAL packet */
data += nal_len; data += nal_len;
size -= nal_len; size -= nal_len;
} }
} }
gst_buffer_unref (buffer); gst_buffer_unref (buffer);
return ret; return ret;

View File

@ -56,6 +56,7 @@ struct _GstRtpH264Pay
gboolean packetized; gboolean packetized;
guint nal_length_size; guint nal_length_size;
GQueue *queue;
gchar *profile_level_id; gchar *profile_level_id;
gchar *sprop_parameter_sets; gchar *sprop_parameter_sets;