GST_DEBUG_CATEGORY_STATIC (rtph264depay_debug);
#define GST_CAT_DEFAULT (rtph264depay_debug)
+#define DEFAULT_BYTE_STREAM TRUE
+
+enum
+{
+ PROP_0,
+ PROP_BYTE_STREAM,
+ PROP_LAST
+};
+
+
/* 3 zero bytes syncword */
static const guint8 sync_bytes[] = { 0, 0, 0, 1 };
GST_TYPE_BASE_RTP_DEPAYLOAD);
static void gst_rtp_h264_depay_finalize (GObject * object);
+static void gst_rtp_h264_depay_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void gst_rtp_h264_depay_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
static GstStateChangeReturn gst_rtp_h264_depay_change_state (GstElement *
element, GstStateChange transition);
gobject_class->finalize = gst_rtp_h264_depay_finalize;
+ gobject_class->set_property = gst_rtp_h264_depay_set_property;
+ gobject_class->get_property = gst_rtp_h264_depay_get_property;
+
+ g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BYTE_STREAM,
+ g_param_spec_boolean ("byte-stream", "Byte Stream",
+ "Generate byte stream format of NALU", DEFAULT_BYTE_STREAM,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
gstelement_class->change_state = gst_rtp_h264_depay_change_state;
gstbasertpdepayload_class->process = gst_rtp_h264_depay_process;
GstRtpH264DepayClass * klass)
{
rtph264depay->adapter = gst_adapter_new ();
+ rtph264depay->byte_stream = DEFAULT_BYTE_STREAM;
}
static void
G_OBJECT_CLASS (parent_class)->finalize (object);
}
+static void
+gst_rtp_h264_depay_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstRtpH264Depay *rtph264depay;
+
+ rtph264depay = GST_RTP_H264_DEPAY (object);
+
+ switch (prop_id) {
+ case PROP_BYTE_STREAM:
+ rtph264depay->byte_stream = g_value_get_boolean (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gst_rtp_h264_depay_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GstRtpH264Depay *rtph264depay;
+
+ rtph264depay = GST_RTP_H264_DEPAY (object);
+
+ switch (prop_id) {
+ case PROP_BYTE_STREAM:
+ g_value_set_boolean (value, rtph264depay->byte_stream);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
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,
gint clock_rate = 90000;
GstStructure *structure = gst_caps_get_structure (caps, 0);
GstRtpH264Depay *rtph264depay;
+ const gchar *ps, *profile;
+ GstBuffer *codec_data;
+ guint8 *b64;
rtph264depay = GST_RTP_H264_DEPAY (depayload);
srccaps = gst_caps_new_simple ("video/x-h264", NULL);
- if (gst_structure_has_field (structure, "sprop-parameter-sets")) {
- const gchar *ps;
+ /* Base64 encoded, comma separated config NALs */
+ ps = gst_structure_get_string (structure, "sprop-parameter-sets");
+ /* hex: AVCProfileIndication:8 | profile_compat:8 | AVCLevelIndication:8 */
+ profile = gst_structure_get_string (structure, "profile-level-id");
+
+ if (rtph264depay->byte_stream && ps != NULL) {
+ /* for bytestream we only need the parameter sets but we don't error out
+ * when they are not there, we assume they are in the stream. */
gchar **params;
guint len, total;
gint i;
- GstBuffer *codec_data;
- guint8 *b64;
- /* Base64 encoded, comma separated config NALs */
- ps = gst_structure_get_string (structure, "sprop-parameter-sets");
params = g_strsplit (ps, ",", 0);
/* count total number of bytes in base64. Also include the sync bytes in
if (rtph264depay->codec_data)
gst_buffer_unref (rtph264depay->codec_data);
rtph264depay->codec_data = codec_data;
+ } else if (!rtph264depay->byte_stream) {
+ gchar **params;
+ guint8 **sps, **pps;
+ guint len, num_sps, num_pps;
+ gint i;
+ guint8 *data;
+ guint32 profile_id;
+
+ if (ps == NULL || profile == NULL)
+ goto incomplete_caps;
+
+ params = g_strsplit (ps, ",", 0);
+ len = g_strv_length (params);
+
+ GST_DEBUG_OBJECT (depayload, "we have %d params", len);
+
+ sps = g_new0 (guint8 *, len + 1);
+ pps = g_new0 (guint8 *, len + 1);
+ num_sps = num_pps = 0;
+
+ /* start with 7 bytes header */
+ len = 7;
+ for (i = 0; params[i]; i++) {
+ gint nal_len;
+ guint8 *nalp;
+
+ nal_len = strlen (params[i]);
+ nalp = g_malloc (nal_len + 2);
+ nal_len = decode_base64 (params[i], nalp + 2);
+ nalp[0] = (nal_len >> 8) & 0xff;
+ nalp[1] = nal_len & 0xff;
+ len += nal_len + 2;
+
+ /* copy to the right list */
+ if ((nalp[2] & 0x1f) == 7) {
+ GST_DEBUG_OBJECT (depayload, "adding param %d as SPS %d", i, num_sps);
+ sps[num_sps++] = nalp;
+ } else {
+ GST_DEBUG_OBJECT (depayload, "adding param %d as PPS %d", i, num_pps);
+ pps[num_pps++] = nalp;
+ }
+ }
+ g_strfreev (params);
+
+ codec_data = gst_buffer_new_and_alloc (len);
+ data = GST_BUFFER_DATA (codec_data);
+
+ /* 8 bits version == 1 */
+ *data++ = 1;
+ /* hex: AVCProfileIndication:8 | profile_compat:8 | AVCLevelIndication:8 */
+ sscanf (profile, "%6x", &profile_id);
+ *data++ = (profile_id >> 16) & 0xff;
+ *data++ = (profile_id >> 8) & 0xff;
+ *data++ = profile_id & 0xff;
+ /* 6 bits reserved | 2 bits lengthSizeMinusOn */
+ *data++ = 0xff;
+ /* 3 bits reserved | 5 bits numOfSequenceParameterSets */
+ *data++ = 0xe0 | (num_sps & 0x1f);
+
+ /* copy all SPS */
+ for (i = 0; sps[i]; i++) {
+ len = ((sps[i][0] << 8) | sps[i][1]) + 2;
+ GST_DEBUG_OBJECT (depayload, "copy SPS %d of length %d", i, len);
+ memcpy (data, sps[i], len);
+ g_free (sps[i]);
+ data += len;
+ }
+ g_free (sps);
+ /* 8 bits numOfPictureParameterSets */
+ *data++ = num_pps;
+ /* copy all PPS */
+ for (i = 0; pps[i]; i++) {
+ len = ((pps[i][0] << 8) | pps[i][1]) + 2;
+ GST_DEBUG_OBJECT (depayload, "copy PPS %d of length %d", i, len);
+ memcpy (data, pps[i], len);
+ g_free (pps[i]);
+ data += len;
+ }
+ g_free (pps);
+ GST_BUFFER_SIZE (codec_data) = data - GST_BUFFER_DATA (codec_data);
+
+ gst_caps_set_simple (srccaps,
+ "codec_data", GST_TYPE_BUFFER, codec_data, NULL);
}
gst_pad_set_caps (depayload->srcpad, srccaps);
gst_caps_unref (srccaps);
return TRUE;
+
+ /* ERRORS */
+incomplete_caps:
+ {
+ GST_DEBUG_OBJECT (depayload, "we have incomplete caps");
+ return FALSE;
+ }
}
+/* FIXME, non-bytestream handling is freaking out ffmpeg. Apparently we need to
+ * group all NAL units belonging to one frame together */
static GstBuffer *
gst_rtp_h264_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf)
{
*/
nalu_size = (payload[0] << 8) | payload[1];
- /* strip NALU size */
- payload += 2;
- payload_len -= 2;
-
if (nalu_size > payload_len)
nalu_size = payload_len;
outsize = nalu_size + sizeof (sync_bytes);
outbuf = gst_buffer_new_and_alloc (outsize);
outdata = GST_BUFFER_DATA (outbuf);
- memcpy (outdata, sync_bytes, sizeof (sync_bytes));
+ if (rtph264depay->byte_stream) {
+ memcpy (outdata, sync_bytes, sizeof (sync_bytes));
+ } else {
+ outdata[0] = outdata[1] = 0;
+ outdata[2] = payload[0];
+ outdata[3] = payload[1];
+ }
+
+ /* strip NALU size */
+ payload += 2;
+ payload_len -= 2;
+
outdata += sizeof (sync_bytes);
memcpy (outdata, payload, nalu_size);
outsize = nalu_size + sizeof (sync_bytes);
outbuf = gst_buffer_new_and_alloc (outsize);
outdata = GST_BUFFER_DATA (outbuf);
- memcpy (outdata, sync_bytes, sizeof (sync_bytes));
outdata += sizeof (sync_bytes);
memcpy (outdata, payload, nalu_size);
outdata[0] = nal_header;
outsize = gst_adapter_available (rtph264depay->adapter);
outbuf = gst_adapter_take_buffer (rtph264depay->adapter, outsize);
+ outdata = GST_BUFFER_DATA (outbuf);
+ if (rtph264depay->byte_stream) {
+ memcpy (outdata, sync_bytes, sizeof (sync_bytes));
+ } else {
+ outsize -= 4;
+ outdata[0] = (outsize >> 24);
+ outdata[1] = (outsize >> 16);
+ outdata[2] = (outsize >> 8);
+ outdata[3] = (outsize);
+ }
gst_buffer_set_caps (outbuf, GST_PAD_CAPS (depayload->srcpad));
/* push codec_data first */
outsize = nalu_size + sizeof (sync_bytes);
outbuf = gst_buffer_new_and_alloc (outsize);
outdata = GST_BUFFER_DATA (outbuf);
- memcpy (outdata, sync_bytes, sizeof (sync_bytes));
+ if (rtph264depay->byte_stream) {
+ memcpy (outdata, sync_bytes, sizeof (sync_bytes));
+ } else {
+ outdata[0] = outdata[1] = 0;
+ outdata[2] = nalu_size >> 8;
+ outdata[3] = nalu_size & 0xff;
+ }
outdata += sizeof (sync_bytes);
memcpy (outdata, payload, nalu_size);
"clock-rate = (int) 90000, " "encoding-name = (string) \"H264\"")
);
+#define GST_TYPE_H264_SCAN_MODE (gst_h264_scan_mode_get_type())
+static GType
+gst_h264_scan_mode_get_type (void)
+{
+ static GType h264_scan_mode_type = 0;
+ static const GEnumValue h264_scan_modes[] = {
+ {GST_H264_SCAN_MODE_BYTESTREAM,
+ "Scan complete bytestream for NALUs (not implemented)",
+ "bytestream"},
+ {GST_H264_SCAN_MODE_MULTI_NAL, "Buffers contain multiple complete NALUs",
+ "multiple"},
+ {GST_H264_SCAN_MODE_SINLE_NAL, "Buffers contain a single complete NALU",
+ "single"},
+ {0, NULL, NULL},
+ };
+
+ if (!h264_scan_mode_type) {
+ h264_scan_mode_type =
+ g_enum_register_static ("GstH264PayScanMode", h264_scan_modes);
+ }
+ return h264_scan_mode_type;
+}
+
#define DEFAULT_PROFILE_LEVEL_ID NULL
#define DEFAULT_SPROP_PARAMETER_SETS NULL
+#define DEFAULT_SPROP_PARAMETER_SETS NULL
+#define DEFAULT_SCAN_MODE GST_H264_SCAN_MODE_MULTI_NAL
enum
{
- ARG_0,
- ARG_PROFILE_LEVEL_ID,
- ARG_SPROP_PARAMETER_SETS
+ PROP_0,
+ PROP_PROFILE_LEVEL_ID,
+ PROP_SPROP_PARAMETER_SETS,
+ PROP_SCAN_MODE,
+ PROP_LAST
};
+
+#define IS_ACCESS_UNIT(x) (((x) > 0x00) && ((x) < 0x06))
+
static void gst_rtp_h264_pay_finalize (GObject * object);
static GstStateChangeReturn gst_rtp_h264_pay_change_state (GstElement * element,
gobject_class->set_property = gst_rtp_h264_pay_set_property;
gobject_class->get_property = gst_rtp_h264_pay_get_property;
- g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_PROFILE_LEVEL_ID,
- g_param_spec_string ("profile-level-id", "profile-level-id",
- "The base64 profile-level-id to set in out caps (set to NULL to extract from stream)",
+ g_object_class_install_property (G_OBJECT_CLASS (klass),
+ PROP_PROFILE_LEVEL_ID, g_param_spec_string ("profile-level-id",
+ "profile-level-id",
+ "The base64 profile-level-id to set in out caps (set to NULL to "
+ "extract from stream)",
DEFAULT_PROFILE_LEVEL_ID,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (G_OBJECT_CLASS (klass),
- ARG_SPROP_PARAMETER_SETS, g_param_spec_string ("sprop-parameter-sets",
+ PROP_SPROP_PARAMETER_SETS, g_param_spec_string ("sprop-parameter-sets",
"sprop-parameter-sets",
- "The base64 sprop-parameter-sets to set in out caps (set to NULL to extract from stream)",
+ "The base64 sprop-parameter-sets to set in out caps (set to NULL to "
+ "extract from stream)",
DEFAULT_SPROP_PARAMETER_SETS,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_SCAN_MODE,
+ g_param_spec_enum ("scan-mode", "Scan Mode",
+ "How to scan the input buffers for NAL units. Performance can be "
+ "increased when certain assumptions are made about the input buffers",
+ GST_TYPE_H264_SCAN_MODE, DEFAULT_SCAN_MODE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
gobject_class->finalize = gst_rtp_h264_pay_finalize;
gstelement_class->change_state = gst_rtp_h264_pay_change_state;
rtph264pay->profile = 0;
rtph264pay->sps = NULL;
rtph264pay->pps = NULL;
+ rtph264pay->scan_mode = GST_H264_SCAN_MODE_MULTI_NAL;
}
static void
GST_DEBUG_OBJECT (rtph264pay, "profile %06x", profile);
/* 6 bits reserved | 2 bits lengthSizeMinusOne */
+ /* this is the number of bytes in front of the NAL units to mark their
+ * length */
rtph264pay->nal_length_size = (data[4] & 0x03) + 1;
GST_DEBUG_OBJECT (rtph264pay, "nal length %u", rtph264pay->nal_length_size);
/* 3 bits reserved | 5 bits numOfSequenceParameterSets */
if (size < 1)
goto avcc_error;
+ /* 8 bits numOfPictureParameterSets */
num_pps = data[0];
data += 1;
size -= 1;
outbuf = gst_rtp_buffer_new_allocate (size, 0, 0);
GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
- gst_rtp_buffer_set_marker (outbuf, 1);
+ /* only set the marker bit on packets containing access units */
+ if (IS_ACCESS_UNIT (nalType)) {
+ gst_rtp_buffer_set_marker (outbuf, 1);
+ }
payload = gst_rtp_buffer_get_payload (outbuf);
GST_DEBUG_OBJECT (basepayload, "Copying %d bytes to outbuf", size);
GST_DEBUG_OBJECT (basepayload, "end size=%d iteration=%d", size, ii);
end = 1;
}
- gst_rtp_buffer_set_marker (outbuf, end);
+ if (IS_ACCESS_UNIT (nalType)) {
+ gst_rtp_buffer_set_marker (outbuf, end);
+ }
/* FU indicator */
payload[0] = (nalHeader & 0x60) | 28;
data += 4;
size -= 4;
- /* use next_start_code() to scan buffer.
- * next_start_code() returns the offset in data,
- * starting from zero to the first byte of 0.0.0.1
- * If no start code is found, it returns the value of the
- * 'size' parameter.
- * data is unchanged by the call to next_start_code()
- */
- next = next_start_code (data, size);
+ if (rtph264pay->scan_mode == GST_H264_SCAN_MODE_SINLE_NAL) {
+ /* we are told that there is only a single NAL in this packet so that we
+ * can avoid scanning for the next NAL. */
+ next = size;
+ } else {
+ /* use next_start_code() to scan buffer.
+ * next_start_code() returns the offset in data,
+ * starting from zero to the first byte of 0.0.0.1
+ * If no start code is found, it returns the value of the
+ * 'size' parameter.
+ * data is unchanged by the call to next_start_code()
+ */
+ next = next_start_code (data, size);
+ }
/* nal length is distance to next start code */
nal_len = next;
rtph264pay = GST_RTP_H264_PAY (object);
switch (prop_id) {
- case ARG_PROFILE_LEVEL_ID:
+ case PROP_PROFILE_LEVEL_ID:
+ g_free (rtph264pay->profile_level_id);
rtph264pay->profile_level_id = g_value_dup_string (value);
rtph264pay->update_caps = TRUE;
break;
- case ARG_SPROP_PARAMETER_SETS:
+ case PROP_SPROP_PARAMETER_SETS:
+ g_free (rtph264pay->sprop_parameter_sets);
rtph264pay->sprop_parameter_sets = g_value_dup_string (value);
rtph264pay->update_caps = TRUE;
break;
+ case PROP_SCAN_MODE:
+ rtph264pay->scan_mode = g_value_get_enum (value);
+ break;
default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
rtph264pay = GST_RTP_H264_PAY (object);
switch (prop_id) {
- case ARG_PROFILE_LEVEL_ID:
+ case PROP_PROFILE_LEVEL_ID:
g_value_set_string (value, rtph264pay->profile_level_id);
break;
- case ARG_SPROP_PARAMETER_SETS:
+ case PROP_SPROP_PARAMETER_SETS:
g_value_set_string (value, rtph264pay->sprop_parameter_sets);
break;
+ case PROP_SCAN_MODE:
+ g_value_set_enum (value, rtph264pay->scan_mode);
+ break;
default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}