gst/rtp/gstrtph264depay.*: Add experimental support for outputting quicktime-like AVC output in addition to the exist...

Original commit message from CVS:
* gst/rtp/gstrtph264depay.c: (gst_rtp_h264_depay_class_init),
(gst_rtp_h264_depay_init), (gst_rtp_h264_depay_set_property),
(gst_rtp_h264_depay_get_property), (gst_rtp_h264_depay_setcaps),
(gst_rtp_h264_depay_process):
* gst/rtp/gstrtph264depay.h:
Add experimental support for outputting quicktime-like AVC output in
addition to the existing bytestream output.
* gst/rtp/gstrtph264pay.c: (gst_h264_scan_mode_get_type),
(gst_rtp_h264_pay_class_init), (gst_rtp_h264_pay_init),
(gst_rtp_h264_pay_setcaps), (gst_rtp_h264_pay_payload_nal),
(gst_rtp_h264_pay_handle_buffer), (gst_rtp_h264_pay_set_property),
(gst_rtp_h264_pay_get_property):
* gst/rtp/gstrtph264pay.h:
Make the parsing mode configurable, for some inputs we don't need to
scan every byte for start codes.
Only set the marker bit on ACCESS units.
This commit is contained in:
Wim Taymans 2008-05-20 11:33:05 +00:00
parent 3d3f7cd6da
commit be0e73ee6b
5 changed files with 309 additions and 35 deletions

View file

@ -1,3 +1,23 @@
2008-05-20 Wim Taymans <wim.taymans@collabora.co.uk>
* gst/rtp/gstrtph264depay.c: (gst_rtp_h264_depay_class_init),
(gst_rtp_h264_depay_init), (gst_rtp_h264_depay_set_property),
(gst_rtp_h264_depay_get_property), (gst_rtp_h264_depay_setcaps),
(gst_rtp_h264_depay_process):
* gst/rtp/gstrtph264depay.h:
Add experimental support for outputting quicktime-like AVC output in
addition to the existing bytestream output.
* gst/rtp/gstrtph264pay.c: (gst_h264_scan_mode_get_type),
(gst_rtp_h264_pay_class_init), (gst_rtp_h264_pay_init),
(gst_rtp_h264_pay_setcaps), (gst_rtp_h264_pay_payload_nal),
(gst_rtp_h264_pay_handle_buffer), (gst_rtp_h264_pay_set_property),
(gst_rtp_h264_pay_get_property):
* gst/rtp/gstrtph264pay.h:
Make the parsing mode configurable, for some inputs we don't need to
scan every byte for start codes.
Only set the marker bit on ACCESS units.
2008-05-20 Sebastian Dröge <slomo@circular-chaos.org>
* gst/equalizer/gstiirequalizer.c:

View file

@ -29,6 +29,16 @@
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 };
@ -77,6 +87,10 @@ GST_BOILERPLATE (GstRtpH264Depay, gst_rtp_h264_depay, GstBaseRTPDepayload,
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);
@ -112,6 +126,14 @@ gst_rtp_h264_depay_class_init (GstRtpH264DepayClass * klass)
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;
@ -126,6 +148,7 @@ gst_rtp_h264_depay_init (GstRtpH264Depay * rtph264depay,
GstRtpH264DepayClass * klass)
{
rtph264depay->adapter = gst_adapter_new ();
rtph264depay->byte_stream = DEFAULT_BYTE_STREAM;
}
static void
@ -143,6 +166,42 @@ gst_rtp_h264_depay_finalize (GObject * object)
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,
@ -198,6 +257,9 @@ gst_rtp_h264_depay_setcaps (GstBaseRTPDepayload * depayload, GstCaps * caps)
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);
@ -206,16 +268,18 @@ gst_rtp_h264_depay_setcaps (GstBaseRTPDepayload * depayload, GstCaps * caps)
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
@ -246,14 +310,106 @@ gst_rtp_h264_depay_setcaps (GstBaseRTPDepayload * depayload, GstCaps * caps)
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)
{
@ -337,17 +493,24 @@ 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);
@ -414,7 +577,6 @@ gst_rtp_h264_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf)
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;
@ -445,7 +607,17 @@ gst_rtp_h264_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf)
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 */
@ -469,7 +641,13 @@ gst_rtp_h264_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf)
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);

View file

@ -44,9 +44,12 @@ struct _GstRtpH264Depay
{
GstBaseRTPDepayload depayload;
gboolean byte_stream;
GstBuffer *codec_data;
GstAdapter *adapter;
gboolean wait_start;
};
struct _GstRtpH264DepayClass

View file

@ -64,16 +64,46 @@ GST_STATIC_PAD_TEMPLATE ("src",
"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,
@ -119,19 +149,29 @@ gst_rtp_h264_pay_class_init (GstRtpH264PayClass * klass)
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;
@ -149,6 +189,7 @@ gst_rtp_h264_pay_init (GstRtpH264Pay * rtph264pay, GstRtpH264PayClass * klass)
rtph264pay->profile = 0;
rtph264pay->sps = NULL;
rtph264pay->pps = NULL;
rtph264pay->scan_mode = GST_H264_SCAN_MODE_MULTI_NAL;
}
static void
@ -239,6 +280,8 @@ gst_rtp_h264_pay_setcaps (GstBaseRTPPayload * basepayload, GstCaps * caps)
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 */
@ -277,6 +320,7 @@ gst_rtp_h264_pay_setcaps (GstBaseRTPPayload * basepayload, GstCaps * caps)
if (size < 1)
goto avcc_error;
/* 8 bits numOfPictureParameterSets */
num_pps = data[0];
data += 1;
size -= 1;
@ -579,7 +623,10 @@ gst_rtp_h264_pay_payload_nal (GstBaseRTPPayload * basepayload, guint8 * data,
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);
@ -621,7 +668,9 @@ gst_rtp_h264_pay_payload_nal (GstBaseRTPPayload * basepayload, guint8 * data,
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;
@ -723,14 +772,20 @@ gst_rtp_h264_pay_handle_buffer (GstBaseRTPPayload * basepayload,
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;
@ -804,15 +859,21 @@ gst_rtp_h264_pay_set_property (GObject * object, guint prop_id,
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;
}
}
@ -826,13 +887,17 @@ gst_rtp_h264_pay_get_property (GObject * object, guint prop_id,
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;
}
}

View file

@ -36,6 +36,13 @@ G_BEGIN_DECLS
#define GST_IS_RTP_H264_PAY_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_H264_PAY))
typedef enum
{
GST_H264_SCAN_MODE_BYTESTREAM,
GST_H264_SCAN_MODE_MULTI_NAL,
GST_H264_SCAN_MODE_SINLE_NAL
} GstH264ScanMode;
typedef struct _GstRtpH264Pay GstRtpH264Pay;
typedef struct _GstRtpH264PayClass GstRtpH264PayClass;
@ -53,6 +60,7 @@ struct _GstRtpH264Pay
gchar *profile_level_id;
gchar *sprop_parameter_sets;
gboolean update_caps;
GstH264ScanMode scan_mode;
};
struct _GstRtpH264PayClass