diff options
Diffstat (limited to 'gst/rtp/gstrtph264pay.c')
-rw-r--r-- | gst/rtp/gstrtph264pay.c | 189 |
1 files changed, 172 insertions, 17 deletions
diff --git a/gst/rtp/gstrtph264pay.c b/gst/rtp/gstrtph264pay.c index a4830e6..1d0721e 100644 --- a/gst/rtp/gstrtph264pay.c +++ b/gst/rtp/gstrtph264pay.c @@ -22,8 +22,10 @@ #endif #include <string.h> +#include <stdlib.h> #include <gst/rtp/gstrtpbuffer.h> +#include <gst/pbutils/pbutils.h> #include "gstrtph264pay.h" @@ -108,6 +110,8 @@ static void gst_rtp_h264_pay_set_property (GObject * object, guint prop_id, static void gst_rtp_h264_pay_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); +static GstCaps *gst_rtp_h264_pay_getcaps (GstBaseRTPPayload * payload, + GstPad * pad); static gboolean gst_rtp_h264_pay_setcaps (GstBaseRTPPayload * basepayload, GstCaps * caps); static GstFlowReturn gst_rtp_h264_pay_handle_buffer (GstBaseRTPPayload * pad, @@ -124,10 +128,10 @@ gst_rtp_h264_pay_base_init (gpointer klass) { GstElementClass *element_class = GST_ELEMENT_CLASS (klass); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&gst_rtp_h264_pay_src_template)); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&gst_rtp_h264_pay_sink_template)); + gst_element_class_add_static_pad_template (element_class, + &gst_rtp_h264_pay_src_template); + gst_element_class_add_static_pad_template (element_class, + &gst_rtp_h264_pay_sink_template); gst_element_class_set_details_simple (element_class, "RTP H264 payloader", "Codec/Payloader/Network/RTP", @@ -191,6 +195,7 @@ gst_rtp_h264_pay_class_init (GstRtpH264PayClass * klass) gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_basertppayload_change_state); + gstbasertppayload_class->get_caps = gst_rtp_h264_pay_getcaps; gstbasertppayload_class->set_caps = gst_rtp_h264_pay_setcaps; gstbasertppayload_class->handle_buffer = gst_rtp_h264_pay_handle_buffer; gstbasertppayload_class->handle_event = gst_rtp_h264_pay_handle_event; @@ -243,6 +248,133 @@ gst_rtp_h264_pay_finalize (GObject * object) G_OBJECT_CLASS (parent_class)->finalize (object); } +static const gchar *all_levels[] = { + "1", + "1b", + "1.1", + "1.2", + "1.3", + "2", + "2.1", + "2.2", + "3", + "3.1", + "3.2", + "4", + "4.1", + "4.2", + "5", + "5.1", + NULL +}; + +static GstCaps * +gst_rtp_h264_pay_getcaps (GstBaseRTPPayload * payload, GstPad * pad) +{ + GstCaps *allowed_caps; + + allowed_caps = + gst_pad_peer_get_caps_reffed (GST_BASE_RTP_PAYLOAD_SRCPAD (payload)); + + if (allowed_caps) { + GstCaps *caps = NULL; + guint i; + + if (gst_caps_is_any (allowed_caps)) { + gst_caps_unref (allowed_caps); + goto any; + } + + if (gst_caps_is_empty (allowed_caps)) + return allowed_caps; + + caps = gst_caps_new_empty (); + + for (i = 0; i < gst_caps_get_size (allowed_caps); i++) { + GstStructure *s = gst_caps_get_structure (allowed_caps, i); + GstStructure *new_s = gst_structure_new ("video/x-h264", NULL); + const gchar *profile_level_id; + + profile_level_id = gst_structure_get_string (s, "profile-level-id"); + + if (profile_level_id && strlen (profile_level_id) == 6) { + const gchar *profile; + const gchar *level; + long int spsint; + guint8 sps[3]; + + spsint = strtol (profile_level_id, NULL, 16); + sps[0] = spsint >> 16; + sps[1] = spsint >> 8; + sps[2] = spsint; + + profile = gst_codec_utils_h264_get_profile (sps, 3); + level = gst_codec_utils_h264_get_level (sps, 3); + + if (profile && level) { + GST_LOG_OBJECT (payload, "In caps, have profile %s and level %s", + profile, level); + + if (!strcmp (profile, "constrained-baseline")) + gst_structure_set (new_s, "profile", G_TYPE_STRING, profile, NULL); + else { + GValue val = { 0, }; + GValue profiles = { 0, }; + + g_value_init (&profiles, GST_TYPE_LIST); + g_value_init (&val, G_TYPE_STRING); + + g_value_set_static_string (&val, profile); + gst_value_list_append_value (&profiles, &val); + + g_value_set_static_string (&val, "constrained-baseline"); + gst_value_list_append_value (&profiles, &val); + + gst_structure_take_value (new_s, "profile", &profiles); + } + + if (!strcmp (level, "1")) + gst_structure_set (new_s, "level", G_TYPE_STRING, level, NULL); + else { + GValue levels = { 0, }; + GValue val = { 0, }; + int j; + + g_value_init (&levels, GST_TYPE_LIST); + g_value_init (&val, G_TYPE_STRING); + + for (j = 0; all_levels[j]; j++) { + g_value_set_static_string (&val, all_levels[j]); + gst_value_list_prepend_value (&levels, &val); + if (!strcmp (level, all_levels[j])) + break; + } + gst_structure_take_value (new_s, "level", &levels); + } + } else { + /* Invalid profile-level-id means baseline */ + + gst_structure_set (new_s, + "profile", G_TYPE_STRING, "constrained-baseline", NULL); + } + } else { + /* No profile-level-id also means baseline */ + + gst_structure_set (new_s, + "profile", G_TYPE_STRING, "constrained-baseline", NULL); + } + + gst_caps_merge_structure (caps, new_s); + } + + gst_caps_unref (allowed_caps); + return caps; + } + +any: + return gst_caps_new_simple ("video/x-h264", NULL); +} + /* take the currently configured SPS and PPS lists and set them on the caps as * sprop-parameter-sets */ static gboolean @@ -298,6 +430,7 @@ gst_rtp_h264_pay_setcaps (GstBaseRTPPayload * basepayload, GstCaps * caps) const GValue *value; guint8 *data; guint size; + const gchar *alignment; rtph264pay = GST_RTP_H264_PAY (basepayload); @@ -307,7 +440,12 @@ gst_rtp_h264_pay_setcaps (GstBaseRTPPayload * basepayload, GstCaps * caps) * NALs */ gst_basertppayload_set_options (basepayload, "video", TRUE, "H264", 90000); -#if 0 // yoserb yi - not to check sps/pps when savsenc_h264 and rtph264pay are linked + alignment = gst_structure_get_string (str, "alignment"); + if (alignment && !strcmp (alignment, "au")) + rtph264pay->au_alignment = TRUE; + else + rtph264pay->au_alignment = FALSE; + /* packetized AVC video has a codec_data */ if ((value = gst_structure_get_value (str, "codec_data"))) { GstBuffer *buffer; @@ -409,10 +547,6 @@ gst_rtp_h264_pay_setcaps (GstBaseRTPPayload * basepayload, GstCaps * caps) GST_DEBUG_OBJECT (rtph264pay, "have bytestream h264"); rtph264pay->packetized = FALSE; } -#else - GST_DEBUG_OBJECT (rtph264pay, "have bytestream h264"); - rtph264pay->packetized = FALSE; -#endif return TRUE; @@ -646,7 +780,7 @@ gst_rtp_h264_pay_decode_nal (GstRtpH264Pay * payloader, static GstFlowReturn gst_rtp_h264_pay_payload_nal (GstBaseRTPPayload * basepayload, const guint8 * data, guint size, GstClockTime timestamp, - GstBuffer * buffer_orig); + GstBuffer * buffer_orig, gboolean end_of_au); static GstFlowReturn gst_rtp_h264_pay_send_sps_pps (GstBaseRTPPayload * basepayload, @@ -662,7 +796,7 @@ gst_rtp_h264_pay_send_sps_pps (GstBaseRTPPayload * basepayload, /* resend SPS */ ret = gst_rtp_h264_pay_payload_nal (basepayload, GST_BUFFER_DATA (sps_buf), GST_BUFFER_SIZE (sps_buf), timestamp, - sps_buf); + sps_buf, FALSE); /* Not critical here; but throw a warning */ if (ret != GST_FLOW_OK) GST_WARNING ("Problem pushing SPS"); @@ -674,7 +808,7 @@ gst_rtp_h264_pay_send_sps_pps (GstBaseRTPPayload * basepayload, /* resend PPS */ ret = gst_rtp_h264_pay_payload_nal (basepayload, GST_BUFFER_DATA (pps_buf), GST_BUFFER_SIZE (pps_buf), timestamp, - pps_buf); + pps_buf, FALSE); /* Not critical here; but throw a warning */ if (ret != GST_FLOW_OK) GST_WARNING ("Problem pushing PPS"); @@ -689,7 +823,7 @@ gst_rtp_h264_pay_send_sps_pps (GstBaseRTPPayload * basepayload, static GstFlowReturn gst_rtp_h264_pay_payload_nal (GstBaseRTPPayload * basepayload, const guint8 * data, guint size, GstClockTime timestamp, - GstBuffer * buffer_orig) + GstBuffer * buffer_orig, gboolean end_of_au) { GstRtpH264Pay *rtph264pay; GstFlowReturn ret; @@ -768,7 +902,7 @@ gst_rtp_h264_pay_payload_nal (GstBaseRTPPayload * basepayload, } /* only set the marker bit on packets containing access units */ - if (IS_ACCESS_UNIT (nalType)) { + if (IS_ACCESS_UNIT (nalType) && end_of_au) { gst_rtp_buffer_set_marker (outbuf, 1); } @@ -858,7 +992,7 @@ gst_rtp_h264_pay_payload_nal (GstBaseRTPPayload * basepayload, end = 1; } if (IS_ACCESS_UNIT (nalType)) { - gst_rtp_buffer_set_marker (outbuf, end); + gst_rtp_buffer_set_marker (outbuf, end && end_of_au); } /* FU indicator */ @@ -958,6 +1092,7 @@ gst_rtp_h264_pay_handle_buffer (GstBaseRTPPayload * basepayload, while (size > nal_length_size) { gint i; + gboolean end_of_au = FALSE; nal_len = 0; for (i = 0; i < nal_length_size; i++) { @@ -976,9 +1111,16 @@ gst_rtp_h264_pay_handle_buffer (GstBaseRTPPayload * basepayload, nal_len); } + /* If we're at the end of the buffer, then we're at the end of the + * access unit + */ + if (rtph264pay->au_alignment && size - nal_len <= nal_length_size) { + end_of_au = TRUE; + } + ret = gst_rtp_h264_pay_payload_nal (basepayload, data, nal_len, timestamp, - buffer); + buffer, end_of_au); if (ret != GST_FLOW_OK) break; @@ -1080,6 +1222,7 @@ gst_rtp_h264_pay_handle_buffer (GstBaseRTPPayload * basepayload, for (i = 0; i < nal_queue->len; i++) { guint size; + gboolean end_of_au = FALSE; nal_len = g_array_index (nal_queue, guint, i); /* skip start code */ @@ -1095,10 +1238,22 @@ gst_rtp_h264_pay_handle_buffer (GstBaseRTPPayload * basepayload, for (; size > 1 && data[size - 1] == 0x0; size--) /* skip */ ; + /* If it's the last nal unit we have in non-bytestream mode, we can + * assume it's the end of an access-unit + * + * FIXME: We need to wait until the next packet or EOS to + * actually payload the NAL so we can know if the current NAL is + * the last one of an access unit or not if we are in bytestream mode + */ + if (rtph264pay->au_alignment && + rtph264pay->scan_mode != GST_H264_SCAN_MODE_BYTESTREAM && + i == nal_queue->len - 1) + end_of_au = TRUE; + /* put the data in one or more RTP packets */ ret = gst_rtp_h264_pay_payload_nal (basepayload, data, size, timestamp, - buffer); + buffer, end_of_au); if (ret != GST_FLOW_OK) { break; } |