mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-30 04:00:37 +00:00
h264parse: optionally output AUs rather than NALUs
That is, Access Units (frames/fields) instead of (possibly) parts thereof.
This commit is contained in:
parent
af1b0afa04
commit
47041f610d
2 changed files with 114 additions and 5 deletions
|
@ -51,11 +51,13 @@ GST_ELEMENT_DETAILS ("H264Parse",
|
||||||
"Wim Taymans <wim.taymans@gmail.com>");
|
"Wim Taymans <wim.taymans@gmail.com>");
|
||||||
|
|
||||||
#define DEFAULT_SPLIT_PACKETIZED FALSE
|
#define DEFAULT_SPLIT_PACKETIZED FALSE
|
||||||
|
#define DEFAULT_ACCESS_UNIT FALSE
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
PROP_0,
|
PROP_0,
|
||||||
PROP_SPLIT_PACKETIZED
|
PROP_SPLIT_PACKETIZED,
|
||||||
|
PROP_ACCESS_UNIT
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
|
@ -740,6 +742,8 @@ gst_nal_decode_slice_header (GstH264Parse * h, GstNalBs * bs)
|
||||||
/* FIXME: note that pps might be uninitialized */
|
/* FIXME: note that pps might be uninitialized */
|
||||||
sps_id = h->pps->sps_id;
|
sps_id = h->pps->sps_id;
|
||||||
h->sps = gst_h264_parse_get_sps (h, sps_id);
|
h->sps = gst_h264_parse_get_sps (h, sps_id);
|
||||||
|
if (!h->sps)
|
||||||
|
return FALSE;
|
||||||
/* FIXME: in some streams sps/pps may not be ready before the first slice
|
/* FIXME: in some streams sps/pps may not be ready before the first slice
|
||||||
* header. In this case it is not a good idea to _get_sps()/_pps() at this
|
* header. In this case it is not a good idea to _get_sps()/_pps() at this
|
||||||
* point
|
* point
|
||||||
|
@ -808,6 +812,10 @@ gst_h264_parse_class_init (GstH264ParseClass * klass)
|
||||||
g_param_spec_boolean ("split-packetized", "Split packetized",
|
g_param_spec_boolean ("split-packetized", "Split packetized",
|
||||||
"Split NAL units of packetized streams", DEFAULT_SPLIT_PACKETIZED,
|
"Split NAL units of packetized streams", DEFAULT_SPLIT_PACKETIZED,
|
||||||
G_PARAM_READWRITE));
|
G_PARAM_READWRITE));
|
||||||
|
g_object_class_install_property (gobject_class, PROP_ACCESS_UNIT,
|
||||||
|
g_param_spec_boolean ("access-unit", "Access Units",
|
||||||
|
"Output Acess Units rather than NALUs", DEFAULT_ACCESS_UNIT,
|
||||||
|
G_PARAM_READWRITE));
|
||||||
|
|
||||||
gstelement_class->change_state = gst_h264_parse_change_state;
|
gstelement_class->change_state = gst_h264_parse_change_state;
|
||||||
}
|
}
|
||||||
|
@ -831,6 +839,9 @@ gst_h264_parse_init (GstH264Parse * h264parse, GstH264ParseClass * g_class)
|
||||||
h264parse->split_packetized = DEFAULT_SPLIT_PACKETIZED;
|
h264parse->split_packetized = DEFAULT_SPLIT_PACKETIZED;
|
||||||
h264parse->adapter = gst_adapter_new ();
|
h264parse->adapter = gst_adapter_new ();
|
||||||
|
|
||||||
|
h264parse->merge = DEFAULT_ACCESS_UNIT;
|
||||||
|
h264parse->picture_adapter = gst_adapter_new ();
|
||||||
|
|
||||||
for (i = 0; i < MAX_SPS_COUNT; i++)
|
for (i = 0; i < MAX_SPS_COUNT; i++)
|
||||||
h264parse->sps_buffers[i] = NULL;
|
h264parse->sps_buffers[i] = NULL;
|
||||||
h264parse->sps = NULL;
|
h264parse->sps = NULL;
|
||||||
|
@ -864,6 +875,7 @@ gst_h264_parse_finalize (GObject * object)
|
||||||
h264parse = GST_H264PARSE (object);
|
h264parse = GST_H264PARSE (object);
|
||||||
|
|
||||||
g_object_unref (h264parse->adapter);
|
g_object_unref (h264parse->adapter);
|
||||||
|
g_object_unref (h264parse->picture_adapter);
|
||||||
|
|
||||||
for (i = 0; i < MAX_SPS_COUNT; i++) {
|
for (i = 0; i < MAX_SPS_COUNT; i++) {
|
||||||
if (h264parse->sps_buffers[i] != NULL)
|
if (h264parse->sps_buffers[i] != NULL)
|
||||||
|
@ -890,6 +902,9 @@ gst_h264_parse_set_property (GObject * object, guint prop_id,
|
||||||
case PROP_SPLIT_PACKETIZED:
|
case PROP_SPLIT_PACKETIZED:
|
||||||
parse->split_packetized = g_value_get_boolean (value);
|
parse->split_packetized = g_value_get_boolean (value);
|
||||||
break;
|
break;
|
||||||
|
case PROP_ACCESS_UNIT:
|
||||||
|
parse->merge = g_value_get_boolean (value);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
break;
|
break;
|
||||||
|
@ -908,6 +923,9 @@ gst_h264_parse_get_property (GObject * object, guint prop_id, GValue * value,
|
||||||
case PROP_SPLIT_PACKETIZED:
|
case PROP_SPLIT_PACKETIZED:
|
||||||
g_value_set_boolean (value, parse->split_packetized);
|
g_value_set_boolean (value, parse->split_packetized);
|
||||||
break;
|
break;
|
||||||
|
case PROP_ACCESS_UNIT:
|
||||||
|
g_value_set_boolean (value, parse->merge);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
break;
|
break;
|
||||||
|
@ -985,6 +1003,71 @@ wrong_version:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* takes over ownership of nal and returns fresh buffer */
|
||||||
|
static GstBuffer *
|
||||||
|
gst_h264_parse_push_nal (GstH264Parse * h264parse, GstBuffer * nal,
|
||||||
|
guint8 * next_nal, gboolean * _start)
|
||||||
|
{
|
||||||
|
gint nal_type;
|
||||||
|
guint8 *data;
|
||||||
|
GstBuffer *outbuf = NULL;
|
||||||
|
guint outsize, size, nal_length = h264parse->nal_length_size;
|
||||||
|
gboolean start = h264parse->picture_start;
|
||||||
|
gboolean complete;
|
||||||
|
|
||||||
|
data = GST_BUFFER_DATA (nal);
|
||||||
|
size = GST_BUFFER_SIZE (nal);
|
||||||
|
|
||||||
|
/* caller ensures number of bytes available */
|
||||||
|
g_return_val_if_fail (size >= nal_length + 1, NULL);
|
||||||
|
|
||||||
|
/* determine if AU complete */
|
||||||
|
nal_type = data[nal_length] & 0x1f;
|
||||||
|
h264parse->picture_start |= (nal_type == 1 || nal_type == 2 || nal_type == 5);
|
||||||
|
if (G_UNLIKELY (!next_nal)) {
|
||||||
|
complete = TRUE;
|
||||||
|
} else {
|
||||||
|
/* consider a coded slices (IDR or not) to start a picture,
|
||||||
|
* (so ending the previous one) if first_mb_in_slice == 0
|
||||||
|
* (non-0 is part of previous one) */
|
||||||
|
/* NOTE this is not entirely according to Access Unit specs in 7.4.1.2.4,
|
||||||
|
* but in practice it works in sane cases, needs not much parsing,
|
||||||
|
* and also works with broken frame_num in NAL
|
||||||
|
* (where spec-wise would fail) */
|
||||||
|
nal_type = next_nal[nal_length] & 0x1f;
|
||||||
|
complete = h264parse->picture_start && (nal_type >= 6 && nal_type <= 9);
|
||||||
|
complete |= h264parse->picture_start &&
|
||||||
|
(nal_type == 1 || nal_type == 2 || nal_type == 5) &&
|
||||||
|
(next_nal[nal_length + 1] & 0x80);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (h264parse->merge) {
|
||||||
|
/* regardless, collect this NALU */
|
||||||
|
gst_adapter_push (h264parse->picture_adapter, nal);
|
||||||
|
|
||||||
|
if (complete) {
|
||||||
|
GstClockTime ts;
|
||||||
|
|
||||||
|
h264parse->picture_start = FALSE;
|
||||||
|
ts = gst_adapter_prev_timestamp (h264parse->picture_adapter, NULL);
|
||||||
|
outsize = gst_adapter_available (h264parse->picture_adapter);
|
||||||
|
outbuf = gst_adapter_take_buffer (h264parse->picture_adapter, outsize);
|
||||||
|
GST_BUFFER_TIMESTAMP (outbuf) = ts;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
outbuf = nal;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_start)
|
||||||
|
*_start = (h264parse->picture_start != start);
|
||||||
|
|
||||||
|
/* ensure metadata can be messed with later on */
|
||||||
|
if (G_LIKELY (outbuf))
|
||||||
|
outbuf = gst_buffer_make_metadata_writable (outbuf);
|
||||||
|
|
||||||
|
return outbuf;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_h264_parse_clear_queues (GstH264Parse * h264parse)
|
gst_h264_parse_clear_queues (GstH264Parse * h264parse)
|
||||||
{
|
{
|
||||||
|
@ -1003,6 +1086,8 @@ gst_h264_parse_clear_queues (GstH264Parse * h264parse)
|
||||||
}
|
}
|
||||||
gst_adapter_clear (h264parse->adapter);
|
gst_adapter_clear (h264parse->adapter);
|
||||||
h264parse->have_i_frame = FALSE;
|
h264parse->have_i_frame = FALSE;
|
||||||
|
gst_adapter_clear (h264parse->picture_adapter);
|
||||||
|
h264parse->picture_start = FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static GstFlowReturn
|
static GstFlowReturn
|
||||||
|
@ -1027,7 +1112,7 @@ gst_h264_parse_chain_forward (GstH264Parse * h264parse, gboolean discont,
|
||||||
gboolean got_frame = FALSE;
|
gboolean got_frame = FALSE;
|
||||||
|
|
||||||
avail = gst_adapter_available (h264parse->adapter);
|
avail = gst_adapter_available (h264parse->adapter);
|
||||||
if (avail < h264parse->nal_length_size + 1)
|
if (avail < h264parse->nal_length_size + 2)
|
||||||
break;
|
break;
|
||||||
data = gst_adapter_peek (h264parse->adapter, avail);
|
data = gst_adapter_peek (h264parse->adapter, avail);
|
||||||
|
|
||||||
|
@ -1155,10 +1240,23 @@ gst_h264_parse_chain_forward (GstH264Parse * h264parse, gboolean discont,
|
||||||
if (next_nalu_pos > 0) {
|
if (next_nalu_pos > 0) {
|
||||||
GstBuffer *outbuf;
|
GstBuffer *outbuf;
|
||||||
GstClockTime outbuf_dts = GST_CLOCK_TIME_NONE;
|
GstClockTime outbuf_dts = GST_CLOCK_TIME_NONE;
|
||||||
|
gboolean start;
|
||||||
|
guint8 *next_data;
|
||||||
|
|
||||||
outbuf = gst_adapter_take_buffer (h264parse->adapter, next_nalu_pos);
|
outbuf = gst_adapter_take_buffer (h264parse->adapter, next_nalu_pos);
|
||||||
outbuf_dts = gst_adapter_prev_timestamp (h264parse->adapter, NULL); /* Better value for the second parameter? */
|
outbuf_dts = gst_adapter_prev_timestamp (h264parse->adapter, NULL); /* Better value for the second parameter? */
|
||||||
|
|
||||||
|
/* packetized will have no next data, which serves fine here */
|
||||||
|
next_data = (guint8 *) gst_adapter_peek (h264parse->adapter, 6);
|
||||||
|
outbuf = gst_h264_parse_push_nal (h264parse, outbuf, next_data, &start);
|
||||||
|
if (!outbuf) {
|
||||||
|
/* no complete unit yet, go for next round */
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
if (h264parse->merge)
|
||||||
|
start = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
/* Ignore upstream dts that stalls or goes backward. Upstream elements
|
/* Ignore upstream dts that stalls or goes backward. Upstream elements
|
||||||
* like filesrc would keep on writing timestamp=0. XXX: is this correct?
|
* like filesrc would keep on writing timestamp=0. XXX: is this correct?
|
||||||
* TODO: better way to detect whether upstream timstamps are useful */
|
* TODO: better way to detect whether upstream timstamps are useful */
|
||||||
|
@ -1167,7 +1265,7 @@ gst_h264_parse_chain_forward (GstH264Parse * h264parse, gboolean discont,
|
||||||
&& outbuf_dts <= h264parse->last_outbuf_dts)
|
&& outbuf_dts <= h264parse->last_outbuf_dts)
|
||||||
outbuf_dts = GST_CLOCK_TIME_NONE;
|
outbuf_dts = GST_CLOCK_TIME_NONE;
|
||||||
|
|
||||||
if (got_frame || delta_unit) {
|
if ((got_frame || delta_unit) && start) {
|
||||||
GstH264Sps *sps = h264parse->sps;
|
GstH264Sps *sps = h264parse->sps;
|
||||||
gint duration = 1;
|
gint duration = 1;
|
||||||
|
|
||||||
|
@ -1316,9 +1414,18 @@ gst_h264_parse_flush_decode (GstH264Parse * h264parse)
|
||||||
link = h264parse->decode;
|
link = h264parse->decode;
|
||||||
buf = link->buffer;
|
buf = link->buffer;
|
||||||
|
|
||||||
|
h264parse->decode = gst_nal_list_delete_head (h264parse->decode);
|
||||||
|
h264parse->decode_len--;
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (h264parse, "have type: %d, I frame: %d", link->nal_type,
|
GST_DEBUG_OBJECT (h264parse, "have type: %d, I frame: %d", link->nal_type,
|
||||||
link->i_frame);
|
link->i_frame);
|
||||||
|
|
||||||
|
buf = gst_h264_parse_push_nal (h264parse, buf,
|
||||||
|
h264parse->decode ? GST_BUFFER_DATA (h264parse->decode->buffer) : NULL,
|
||||||
|
NULL);
|
||||||
|
if (!buf)
|
||||||
|
continue;
|
||||||
|
|
||||||
if (first) {
|
if (first) {
|
||||||
/* first buffer has discont */
|
/* first buffer has discont */
|
||||||
GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
|
GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
|
||||||
|
@ -1338,8 +1445,6 @@ gst_h264_parse_flush_decode (GstH264Parse * h264parse)
|
||||||
|
|
||||||
res = gst_pad_push (h264parse->srcpad, buf);
|
res = gst_pad_push (h264parse->srcpad, buf);
|
||||||
|
|
||||||
h264parse->decode = gst_nal_list_delete_head (h264parse->decode);
|
|
||||||
h264parse->decode_len--;
|
|
||||||
}
|
}
|
||||||
/* the i frame is gone now */
|
/* the i frame is gone now */
|
||||||
h264parse->have_i_frame = FALSE;
|
h264parse->have_i_frame = FALSE;
|
||||||
|
|
|
@ -116,6 +116,10 @@ struct _GstH264Parse
|
||||||
|
|
||||||
/* for debug purpose */
|
/* for debug purpose */
|
||||||
guint32 frame_cnt;
|
guint32 frame_cnt;
|
||||||
|
|
||||||
|
/* NALU AU */
|
||||||
|
GstAdapter *picture_adapter;
|
||||||
|
gboolean picture_start;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _GstH264ParseClass
|
struct _GstH264ParseClass
|
||||||
|
|
Loading…
Reference in a new issue