mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-30 12:10:37 +00:00
h264parse: optional transforming from bytestream to sample format (or vice versa)
This commit is contained in:
parent
7f291456e7
commit
b4d9b81b8a
2 changed files with 335 additions and 33 deletions
|
@ -55,14 +55,42 @@ GST_ELEMENT_DETAILS ("H264Parse",
|
||||||
|
|
||||||
#define DEFAULT_SPLIT_PACKETIZED FALSE
|
#define DEFAULT_SPLIT_PACKETIZED FALSE
|
||||||
#define DEFAULT_ACCESS_UNIT FALSE
|
#define DEFAULT_ACCESS_UNIT FALSE
|
||||||
|
#define DEFAULT_OUTPUT_FORMAT GST_H264_PARSE_FORMAT_INPUT
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
PROP_0,
|
PROP_0,
|
||||||
PROP_SPLIT_PACKETIZED,
|
PROP_SPLIT_PACKETIZED,
|
||||||
PROP_ACCESS_UNIT
|
PROP_ACCESS_UNIT,
|
||||||
|
PROP_OUTPUT_FORMAT
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
GST_H264_PARSE_FORMAT_SAMPLE = 0,
|
||||||
|
GST_H264_PARSE_FORMAT_BYTE,
|
||||||
|
GST_H264_PARSE_FORMAT_INPUT
|
||||||
|
};
|
||||||
|
|
||||||
|
#define GST_H264_PARSE_FORMAT_TYPE (gst_h264_parse_format_get_type())
|
||||||
|
static GType
|
||||||
|
gst_h264_parse_format_get_type (void)
|
||||||
|
{
|
||||||
|
static GType format_type = 0;
|
||||||
|
|
||||||
|
static const GEnumValue format_types[] = {
|
||||||
|
{GST_H264_PARSE_FORMAT_SAMPLE, "AVC Sample Format", "sample"},
|
||||||
|
{GST_H264_PARSE_FORMAT_BYTE, "Bytestream Format", "byte"},
|
||||||
|
{GST_H264_PARSE_FORMAT_INPUT, "Input Format", "input"},
|
||||||
|
{0, NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!format_type) {
|
||||||
|
format_type = g_enum_register_static ("GstH264ParseFormat", format_types);
|
||||||
|
}
|
||||||
|
return format_type;
|
||||||
|
}
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
NAL_UNKNOWN = 0,
|
NAL_UNKNOWN = 0,
|
||||||
|
@ -899,6 +927,11 @@ gst_h264_parse_class_init (GstH264ParseClass * klass)
|
||||||
g_param_spec_boolean ("access-unit", "Access Units",
|
g_param_spec_boolean ("access-unit", "Access Units",
|
||||||
"Output Acess Units rather than NALUs", DEFAULT_ACCESS_UNIT,
|
"Output Acess Units rather than NALUs", DEFAULT_ACCESS_UNIT,
|
||||||
G_PARAM_READWRITE));
|
G_PARAM_READWRITE));
|
||||||
|
g_object_class_install_property (gobject_class, PROP_OUTPUT_FORMAT,
|
||||||
|
g_param_spec_enum ("output-format", "Output Format",
|
||||||
|
"Output Format of stream (bytestream or otherwise)",
|
||||||
|
GST_H264_PARSE_FORMAT_TYPE, DEFAULT_OUTPUT_FORMAT,
|
||||||
|
G_PARAM_READWRITE));
|
||||||
|
|
||||||
gstelement_class->change_state = gst_h264_parse_change_state;
|
gstelement_class->change_state = gst_h264_parse_change_state;
|
||||||
}
|
}
|
||||||
|
@ -924,6 +957,8 @@ gst_h264_parse_init (GstH264Parse * h264parse, GstH264ParseClass * g_class)
|
||||||
h264parse->merge = DEFAULT_ACCESS_UNIT;
|
h264parse->merge = DEFAULT_ACCESS_UNIT;
|
||||||
h264parse->picture_adapter = gst_adapter_new ();
|
h264parse->picture_adapter = gst_adapter_new ();
|
||||||
|
|
||||||
|
h264parse->format = GST_H264_PARSE_FORMAT_INPUT;
|
||||||
|
|
||||||
gst_h264_parse_reset (h264parse);
|
gst_h264_parse_reset (h264parse);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -931,14 +966,17 @@ static void
|
||||||
gst_h264_parse_reset (GstH264Parse * h264parse)
|
gst_h264_parse_reset (GstH264Parse * h264parse)
|
||||||
{
|
{
|
||||||
gint i;
|
gint i;
|
||||||
|
GSList *list;
|
||||||
|
|
||||||
for (i = 0; i < MAX_SPS_COUNT; i++) {
|
for (i = 0; i < MAX_SPS_COUNT; i++) {
|
||||||
if (h264parse->sps_buffers[i])
|
if (h264parse->sps_buffers[i])
|
||||||
g_slice_free (GstH264Sps, h264parse->sps_buffers[i]);
|
g_slice_free (GstH264Sps, h264parse->sps_buffers[i]);
|
||||||
|
gst_buffer_replace (&h264parse->sps_nals[i], NULL);
|
||||||
}
|
}
|
||||||
for (i = 0; i < MAX_PPS_COUNT; i++) {
|
for (i = 0; i < MAX_PPS_COUNT; i++) {
|
||||||
if (h264parse->pps_buffers[i])
|
if (h264parse->pps_buffers[i])
|
||||||
g_slice_free (GstH264Pps, h264parse->pps_buffers[i]);
|
g_slice_free (GstH264Pps, h264parse->pps_buffers[i]);
|
||||||
|
gst_buffer_replace (&h264parse->pps_nals[i], NULL);
|
||||||
}
|
}
|
||||||
h264parse->sps = NULL;
|
h264parse->sps = NULL;
|
||||||
h264parse->pps = NULL;
|
h264parse->pps = NULL;
|
||||||
|
@ -961,6 +999,16 @@ gst_h264_parse_reset (GstH264Parse * h264parse)
|
||||||
h264parse->ts_trn_nb = GST_CLOCK_TIME_NONE;
|
h264parse->ts_trn_nb = GST_CLOCK_TIME_NONE;
|
||||||
h264parse->cur_duration = 0;
|
h264parse->cur_duration = 0;
|
||||||
h264parse->last_outbuf_dts = GST_CLOCK_TIME_NONE;
|
h264parse->last_outbuf_dts = GST_CLOCK_TIME_NONE;
|
||||||
|
|
||||||
|
list = h264parse->codec_nals;
|
||||||
|
while (list) {
|
||||||
|
gst_buffer_unref (list->data);
|
||||||
|
list = g_slist_next (list);
|
||||||
|
}
|
||||||
|
g_slist_free (h264parse->codec_nals);
|
||||||
|
h264parse->codec_nals = NULL;
|
||||||
|
|
||||||
|
gst_caps_replace (&h264parse->src_caps, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -1002,6 +1050,9 @@ gst_h264_parse_set_property (GObject * object, guint prop_id,
|
||||||
case PROP_ACCESS_UNIT:
|
case PROP_ACCESS_UNIT:
|
||||||
parse->merge = g_value_get_boolean (value);
|
parse->merge = g_value_get_boolean (value);
|
||||||
break;
|
break;
|
||||||
|
case PROP_OUTPUT_FORMAT:
|
||||||
|
parse->format = g_value_get_enum (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;
|
||||||
|
@ -1023,23 +1074,116 @@ gst_h264_parse_get_property (GObject * object, guint prop_id, GValue * value,
|
||||||
case PROP_ACCESS_UNIT:
|
case PROP_ACCESS_UNIT:
|
||||||
g_value_set_boolean (value, parse->merge);
|
g_value_set_boolean (value, parse->merge);
|
||||||
break;
|
break;
|
||||||
|
case PROP_OUTPUT_FORMAT:
|
||||||
|
g_value_set_enum (value, parse->format);
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* make a buffer consisting of a 4-byte start code following by
|
||||||
|
* (a copy of) given nal data */
|
||||||
|
static GstBuffer *
|
||||||
|
gst_h264_parse_make_nal (GstH264Parse * h264parse, const guint8 * data,
|
||||||
|
guint len)
|
||||||
|
{
|
||||||
|
GstBuffer *buf;
|
||||||
|
|
||||||
|
buf = gst_buffer_new_and_alloc (4 + len);
|
||||||
|
GST_WRITE_UINT32_BE (GST_BUFFER_DATA (buf), 1);
|
||||||
|
memcpy (GST_BUFFER_DATA (buf) + 4, data, len);
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* byte together avc codec data based on collected pps and sps so far */
|
||||||
|
static GstBuffer *
|
||||||
|
gst_h264_parse_make_codec_data (GstH264Parse * h264parse)
|
||||||
|
{
|
||||||
|
GstBuffer *buf, *nal;
|
||||||
|
gint i, sps_size = 0, pps_size = 0, num_sps = 0, num_pps = 0;
|
||||||
|
guint8 profile_idc = 0, profile_comp = 0, level_idc = 0;
|
||||||
|
gboolean found = FALSE;
|
||||||
|
guint8 *data;
|
||||||
|
|
||||||
|
/* sps_nals and pps_nals contain start code */
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_SPS_COUNT; i++) {
|
||||||
|
if ((nal = h264parse->sps_nals[i])) {
|
||||||
|
num_sps++;
|
||||||
|
/* size bytes also count */
|
||||||
|
sps_size += GST_BUFFER_SIZE (nal) - 4 + 2;
|
||||||
|
if (GST_BUFFER_SIZE (nal) >= 7) {
|
||||||
|
found = TRUE;
|
||||||
|
profile_idc = (GST_BUFFER_DATA (nal))[4];
|
||||||
|
profile_comp = (GST_BUFFER_DATA (nal))[5];
|
||||||
|
level_idc = (GST_BUFFER_DATA (nal))[6];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i = 0; i < MAX_PPS_COUNT; i++) {
|
||||||
|
if ((nal = h264parse->pps_nals[i])) {
|
||||||
|
num_pps++;
|
||||||
|
/* size bytes also count */
|
||||||
|
pps_size += GST_BUFFER_SIZE (nal) - 4 + 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (h264parse,
|
||||||
|
"constructing codec_data: num_sps=%d, num_pps=%d", num_sps, num_pps);
|
||||||
|
|
||||||
|
if (!found || !num_pps)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
buf = gst_buffer_new_and_alloc (5 + 1 + sps_size + 1 + pps_size);
|
||||||
|
data = GST_BUFFER_DATA (buf);
|
||||||
|
|
||||||
|
data[0] = 1; /* AVC Decoder Configuration Record ver. 1 */
|
||||||
|
data[1] = profile_idc; /* profile_idc */
|
||||||
|
data[2] = profile_comp; /* profile_compability */
|
||||||
|
data[3] = level_idc; /* level_idc */
|
||||||
|
data[4] = 0xfc | (4 - 1); /* nal_length_size_minus1 */
|
||||||
|
data[5] = 0xe0 | num_sps; /* number of SPSs */
|
||||||
|
|
||||||
|
data += 6;
|
||||||
|
for (i = 0; i < MAX_SPS_COUNT; i++) {
|
||||||
|
if ((nal = h264parse->sps_nals[i])) {
|
||||||
|
GST_WRITE_UINT16_BE (data, GST_BUFFER_SIZE (nal) - 4);
|
||||||
|
memcpy (data + 2, GST_BUFFER_DATA (nal) + 4, GST_BUFFER_SIZE (nal) - 4);
|
||||||
|
data += 2 + GST_BUFFER_SIZE (nal) - 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data[0] = num_pps;
|
||||||
|
data++;
|
||||||
|
for (i = 0; i < MAX_PPS_COUNT; i++) {
|
||||||
|
if ((nal = h264parse->pps_nals[i])) {
|
||||||
|
GST_WRITE_UINT16_BE (data, GST_BUFFER_SIZE (nal) - 4);
|
||||||
|
memcpy (data + 2, GST_BUFFER_DATA (nal) + 4, GST_BUFFER_SIZE (nal) - 4);
|
||||||
|
data += 2 + GST_BUFFER_SIZE (nal) - 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
gst_h264_parse_update_src_caps (GstH264Parse * h264parse, GstCaps * caps)
|
gst_h264_parse_update_src_caps (GstH264Parse * h264parse, GstCaps * caps)
|
||||||
{
|
{
|
||||||
GstH264Sps *sps;
|
GstH264Sps *sps;
|
||||||
GstCaps *src_caps = NULL;
|
GstCaps *src_caps = NULL;
|
||||||
|
|
||||||
g_return_val_if_fail (caps != NULL, FALSE);
|
|
||||||
|
|
||||||
sps = h264parse->sps;
|
sps = h264parse->sps;
|
||||||
|
|
||||||
src_caps = gst_caps_ref (caps);
|
src_caps = h264parse->src_caps;
|
||||||
|
if (G_UNLIKELY (src_caps == NULL))
|
||||||
|
src_caps = caps;
|
||||||
|
|
||||||
|
g_return_val_if_fail (src_caps != NULL, FALSE);
|
||||||
|
gst_caps_ref (src_caps);
|
||||||
|
caps = src_caps;
|
||||||
|
|
||||||
/* if some upstream metadata missing, fill in from parsed stream */
|
/* if some upstream metadata missing, fill in from parsed stream */
|
||||||
/* width / height */
|
/* width / height */
|
||||||
|
@ -1075,12 +1219,39 @@ gst_h264_parse_update_src_caps (GstH264Parse * h264parse, GstCaps * caps)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* release extra ref */
|
/* transforming to non-bytestream needs to make codec-data */
|
||||||
if (src_caps == caps)
|
if (h264parse->format == GST_H264_PARSE_FORMAT_SAMPLE) {
|
||||||
|
GstBuffer *buf;
|
||||||
|
GstStructure *structure;
|
||||||
|
const GValue *value = NULL;
|
||||||
|
const GstBuffer *codec_data = NULL;
|
||||||
|
|
||||||
|
structure = gst_caps_get_structure (caps, 0);
|
||||||
|
value = gst_structure_get_value (structure, "codec_data");
|
||||||
|
if (value != NULL)
|
||||||
|
codec_data = gst_value_get_buffer (value);
|
||||||
|
buf = gst_h264_parse_make_codec_data (h264parse);
|
||||||
|
if (buf) {
|
||||||
|
if (!codec_data || GST_BUFFER_SIZE (buf) != GST_BUFFER_SIZE (codec_data)
|
||||||
|
|| memcmp (GST_BUFFER_DATA (buf), GST_BUFFER_DATA (codec_data),
|
||||||
|
GST_BUFFER_SIZE (buf))) {
|
||||||
|
GST_DEBUG_OBJECT (h264parse, "setting new codec_data");
|
||||||
|
gst_caps_replace (&src_caps, gst_caps_copy (src_caps));
|
||||||
|
gst_caps_unref (src_caps);
|
||||||
|
gst_caps_set_simple (src_caps, "codec_data", GST_TYPE_BUFFER, buf,
|
||||||
|
NULL);
|
||||||
|
gst_buffer_unref (buf);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
GST_DEBUG_OBJECT (h264parse, "no codec_data yet");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* save as new caps, caps will be set when pushing data */
|
||||||
|
gst_caps_replace (&h264parse->src_caps, src_caps);
|
||||||
gst_caps_unref (src_caps);
|
gst_caps_unref (src_caps);
|
||||||
|
|
||||||
return src_caps != GST_PAD_CAPS (h264parse->srcpad) ?
|
return TRUE;
|
||||||
gst_pad_set_caps (h264parse->srcpad, src_caps) : TRUE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
|
@ -1108,6 +1279,7 @@ gst_h264_parse_sink_setcaps (GstPad * pad, GstCaps * caps)
|
||||||
gint profile;
|
gint profile;
|
||||||
GstNalBs bs;
|
GstNalBs bs;
|
||||||
gint i, len;
|
gint i, len;
|
||||||
|
GSList *nlist = NULL;
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (h264parse, "have packetized h264");
|
GST_DEBUG_OBJECT (h264parse, "have packetized h264");
|
||||||
h264parse->packetized = TRUE;
|
h264parse->packetized = TRUE;
|
||||||
|
@ -1144,6 +1316,9 @@ gst_h264_parse_sink_setcaps (GstPad * pad, GstCaps * caps)
|
||||||
goto avcc_too_small;
|
goto avcc_too_small;
|
||||||
gst_nal_bs_init (&bs, data + 2 + 1, len - 1);
|
gst_nal_bs_init (&bs, data + 2 + 1, len - 1);
|
||||||
gst_nal_decode_sps (h264parse, &bs);
|
gst_nal_decode_sps (h264parse, &bs);
|
||||||
|
if (h264parse->format == GST_H264_PARSE_FORMAT_BYTE)
|
||||||
|
nlist = g_slist_append (nlist,
|
||||||
|
gst_h264_parse_make_nal (h264parse, data + 2, len));
|
||||||
data += len + 2;
|
data += len + 2;
|
||||||
size -= len + 2;
|
size -= len + 2;
|
||||||
}
|
}
|
||||||
|
@ -1156,9 +1331,13 @@ gst_h264_parse_sink_setcaps (GstPad * pad, GstCaps * caps)
|
||||||
goto avcc_too_small;
|
goto avcc_too_small;
|
||||||
gst_nal_bs_init (&bs, data + 2 + 1, len - 1);
|
gst_nal_bs_init (&bs, data + 2 + 1, len - 1);
|
||||||
gst_nal_decode_pps (h264parse, &bs);
|
gst_nal_decode_pps (h264parse, &bs);
|
||||||
|
if (h264parse->format == GST_H264_PARSE_FORMAT_BYTE)
|
||||||
|
nlist = g_slist_append (nlist,
|
||||||
|
gst_h264_parse_make_nal (h264parse, data + 2, len));
|
||||||
data += len + 2;
|
data += len + 2;
|
||||||
size -= len + 2;
|
size -= len + 2;
|
||||||
}
|
}
|
||||||
|
h264parse->codec_nals = nlist;
|
||||||
} else {
|
} else {
|
||||||
GST_DEBUG_OBJECT (h264parse, "have bytestream h264");
|
GST_DEBUG_OBJECT (h264parse, "have bytestream h264");
|
||||||
h264parse->packetized = FALSE;
|
h264parse->packetized = FALSE;
|
||||||
|
@ -1182,6 +1361,87 @@ wrong_version:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* if forced output mode,
|
||||||
|
* ensures that NALU @nal starts with start code or length
|
||||||
|
* takes ownership of nal and returns buffer
|
||||||
|
*/
|
||||||
|
static GstBuffer *
|
||||||
|
gst_h264parse_write_nal_prefix (GstH264Parse * h264parse, GstBuffer * nal)
|
||||||
|
{
|
||||||
|
guint nal_length = h264parse->nal_length_size;
|
||||||
|
|
||||||
|
g_assert (nal_length <= 4);
|
||||||
|
|
||||||
|
/* ensure proper transformation on prefix if needed */
|
||||||
|
if (h264parse->format == GST_H264_PARSE_FORMAT_SAMPLE) {
|
||||||
|
nal = gst_buffer_make_writable (nal);
|
||||||
|
switch (nal_length) {
|
||||||
|
case 1:
|
||||||
|
GST_WRITE_UINT8 (GST_BUFFER_DATA (nal),
|
||||||
|
GST_BUFFER_SIZE (nal) - nal_length);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
GST_WRITE_UINT16_BE (GST_BUFFER_DATA (nal),
|
||||||
|
GST_BUFFER_SIZE (nal) - nal_length);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
GST_WRITE_UINT24_BE (GST_BUFFER_DATA (nal),
|
||||||
|
GST_BUFFER_SIZE (nal) - nal_length);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
GST_WRITE_UINT32_BE (GST_BUFFER_DATA (nal),
|
||||||
|
GST_BUFFER_SIZE (nal) - nal_length);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
g_assert_not_reached ();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (h264parse->format == GST_H264_PARSE_FORMAT_BYTE) {
|
||||||
|
if (nal_length == 4) {
|
||||||
|
nal = gst_buffer_make_writable (nal);
|
||||||
|
GST_WRITE_UINT32_BE (GST_BUFFER_DATA (nal), 0x01);
|
||||||
|
} else {
|
||||||
|
gst_buffer_replace (&nal,
|
||||||
|
gst_h264_parse_make_nal (h264parse,
|
||||||
|
GST_BUFFER_DATA (nal) + nal_length,
|
||||||
|
GST_BUFFER_SIZE (nal) - nal_length));
|
||||||
|
gst_buffer_unref (nal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* in any case, ensure metadata can be messed with later on */
|
||||||
|
nal = gst_buffer_make_metadata_writable (nal);
|
||||||
|
|
||||||
|
return nal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* sends buffer downstream, inserting codec_data NALUs if needed */
|
||||||
|
static GstFlowReturn
|
||||||
|
gst_h264_parse_push_buffer (GstH264Parse * h264parse, GstBuffer * buf)
|
||||||
|
{
|
||||||
|
GstFlowReturn ret = GST_FLOW_OK;
|
||||||
|
|
||||||
|
/* start of picture is good time to slip in codec_data NALUs
|
||||||
|
* (when outputting NALS and transforming to bytestream) */
|
||||||
|
if (G_UNLIKELY (h264parse->codec_nals && h264parse->picture_start)) {
|
||||||
|
GSList *nals = h264parse->codec_nals;
|
||||||
|
while (nals) {
|
||||||
|
GST_DEBUG_OBJECT (h264parse, "pushing codec_nal of size %d",
|
||||||
|
GST_BUFFER_SIZE (nals->data));
|
||||||
|
GST_BUFFER_TIMESTAMP (nals->data) = GST_BUFFER_TIMESTAMP (buf);
|
||||||
|
GST_BUFFER_DURATION (nals->data) = 0;
|
||||||
|
|
||||||
|
gst_buffer_set_caps (nals->data, h264parse->src_caps);
|
||||||
|
ret = gst_pad_push (h264parse->srcpad, nals->data);
|
||||||
|
nals = g_slist_delete_link (nals, nals);
|
||||||
|
}
|
||||||
|
h264parse->codec_nals = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
gst_buffer_set_caps (buf, h264parse->src_caps);
|
||||||
|
return gst_pad_push (h264parse->srcpad, buf);
|
||||||
|
}
|
||||||
|
|
||||||
/* takes over ownership of nal and returns fresh buffer */
|
/* takes over ownership of nal and returns fresh buffer */
|
||||||
static GstBuffer *
|
static GstBuffer *
|
||||||
gst_h264_parse_push_nal (GstH264Parse * h264parse, GstBuffer * nal,
|
gst_h264_parse_push_nal (GstH264Parse * h264parse, GstBuffer * nal,
|
||||||
|
@ -1202,6 +1462,7 @@ gst_h264_parse_push_nal (GstH264Parse * h264parse, GstBuffer * nal,
|
||||||
|
|
||||||
/* determine if AU complete */
|
/* determine if AU complete */
|
||||||
nal_type = data[nal_length] & 0x1f;
|
nal_type = data[nal_length] & 0x1f;
|
||||||
|
GST_LOG_OBJECT (h264parse, "nal type: %d", nal_type);
|
||||||
h264parse->picture_start |= (nal_type == 1 || nal_type == 2 || nal_type == 5);
|
h264parse->picture_start |= (nal_type == 1 || nal_type == 2 || nal_type == 5);
|
||||||
if (G_UNLIKELY (!next_nal)) {
|
if (G_UNLIKELY (!next_nal)) {
|
||||||
complete = TRUE;
|
complete = TRUE;
|
||||||
|
@ -1214,13 +1475,62 @@ gst_h264_parse_push_nal (GstH264Parse * h264parse, GstBuffer * nal,
|
||||||
* and also works with broken frame_num in NAL
|
* and also works with broken frame_num in NAL
|
||||||
* (where spec-wise would fail) */
|
* (where spec-wise would fail) */
|
||||||
nal_type = next_nal[nal_length] & 0x1f;
|
nal_type = next_nal[nal_length] & 0x1f;
|
||||||
|
GST_LOG_OBJECT (h264parse, "next nal type: %d", nal_type);
|
||||||
complete = h264parse->picture_start && (nal_type >= 6 && nal_type <= 9);
|
complete = h264parse->picture_start && (nal_type >= 6 && nal_type <= 9);
|
||||||
complete |= h264parse->picture_start &&
|
complete |= h264parse->picture_start &&
|
||||||
(nal_type == 1 || nal_type == 2 || nal_type == 5) &&
|
(nal_type == 1 || nal_type == 2 || nal_type == 5) &&
|
||||||
(next_nal[nal_length + 1] & 0x80);
|
(next_nal[nal_length + 1] & 0x80);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* collect SPS and PPS NALUs to make up codec_data, if so needed */
|
||||||
|
nal_type = data[nal_length] & 0x1f;
|
||||||
|
if (G_UNLIKELY (nal_type == NAL_SPS)) {
|
||||||
|
GstNalBs bs;
|
||||||
|
guint id;
|
||||||
|
|
||||||
|
gst_nal_bs_init (&bs, data + nal_length + 1, size - nal_length - 1);
|
||||||
|
gst_nal_bs_read (&bs, 24); /* profile_idc, profile_compatibility, level_idc */
|
||||||
|
id = gst_nal_bs_read_ue (&bs);
|
||||||
|
if (!gst_nal_bs_eos (&bs) && id < MAX_SPS_COUNT) {
|
||||||
|
GST_DEBUG_OBJECT (h264parse, "storing SPS id %d", id);
|
||||||
|
gst_buffer_replace (&h264parse->sps_nals[id], NULL);
|
||||||
|
h264parse->sps_nals[id] =
|
||||||
|
gst_h264_parse_make_nal (h264parse, data + nal_length,
|
||||||
|
size - nal_length);
|
||||||
|
gst_h264_parse_update_src_caps (h264parse, NULL);
|
||||||
|
}
|
||||||
|
} else if (G_UNLIKELY (nal_type == NAL_PPS)) {
|
||||||
|
GstNalBs bs;
|
||||||
|
guint id;
|
||||||
|
|
||||||
|
gst_nal_bs_init (&bs, data + nal_length + 1, size - nal_length - 1);
|
||||||
|
id = gst_nal_bs_read_ue (&bs);
|
||||||
|
if (!gst_nal_bs_eos (&bs) && id < MAX_PPS_COUNT) {
|
||||||
|
GST_DEBUG_OBJECT (h264parse, "storing PPS id %d", id);
|
||||||
|
gst_buffer_replace (&h264parse->pps_nals[id], NULL);
|
||||||
|
h264parse->pps_nals[id] =
|
||||||
|
gst_h264_parse_make_nal (h264parse, data + nal_length,
|
||||||
|
size - nal_length);
|
||||||
|
gst_h264_parse_update_src_caps (h264parse, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (h264parse->merge) {
|
if (h264parse->merge) {
|
||||||
|
/* proper prefix */
|
||||||
|
nal = gst_h264parse_write_nal_prefix (h264parse, nal);
|
||||||
|
|
||||||
|
/* start of a picture is a good time to insert codec SPS and PPS */
|
||||||
|
if (G_UNLIKELY (h264parse->codec_nals && h264parse->picture_start)) {
|
||||||
|
while (h264parse->codec_nals) {
|
||||||
|
GST_DEBUG_OBJECT (h264parse, "inserting codec_nal of size %d into AU",
|
||||||
|
GST_BUFFER_SIZE (h264parse->codec_nals->data));
|
||||||
|
gst_adapter_push (h264parse->picture_adapter,
|
||||||
|
h264parse->codec_nals->data);
|
||||||
|
h264parse->codec_nals =
|
||||||
|
g_slist_delete_link (h264parse->codec_nals, h264parse->codec_nals);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* regardless, collect this NALU */
|
/* regardless, collect this NALU */
|
||||||
gst_adapter_push (h264parse->picture_adapter, nal);
|
gst_adapter_push (h264parse->picture_adapter, nal);
|
||||||
|
|
||||||
|
@ -1231,19 +1541,16 @@ gst_h264_parse_push_nal (GstH264Parse * h264parse, GstBuffer * nal,
|
||||||
ts = gst_adapter_prev_timestamp (h264parse->picture_adapter, NULL);
|
ts = gst_adapter_prev_timestamp (h264parse->picture_adapter, NULL);
|
||||||
outsize = gst_adapter_available (h264parse->picture_adapter);
|
outsize = gst_adapter_available (h264parse->picture_adapter);
|
||||||
outbuf = gst_adapter_take_buffer (h264parse->picture_adapter, outsize);
|
outbuf = gst_adapter_take_buffer (h264parse->picture_adapter, outsize);
|
||||||
|
outbuf = gst_buffer_make_metadata_writable (outbuf);
|
||||||
GST_BUFFER_TIMESTAMP (outbuf) = ts;
|
GST_BUFFER_TIMESTAMP (outbuf) = ts;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
outbuf = nal;
|
outbuf = gst_h264parse_write_nal_prefix (h264parse, nal);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_start)
|
if (_start)
|
||||||
*_start = (h264parse->picture_start != 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;
|
return outbuf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1401,8 +1708,6 @@ gst_h264_parse_chain_forward (GstH264Parse * h264parse, gboolean discont,
|
||||||
case NAL_SPS:
|
case NAL_SPS:
|
||||||
GST_DEBUG_OBJECT (h264parse, "we have an SPS NAL");
|
GST_DEBUG_OBJECT (h264parse, "we have an SPS NAL");
|
||||||
gst_nal_decode_sps (h264parse, &bs);
|
gst_nal_decode_sps (h264parse, &bs);
|
||||||
gst_h264_parse_update_src_caps (h264parse,
|
|
||||||
GST_PAD_CAPS (h264parse->srcpad));
|
|
||||||
break;
|
break;
|
||||||
case NAL_PPS:
|
case NAL_PPS:
|
||||||
GST_DEBUG_OBJECT (h264parse, "we have a PPS NAL");
|
GST_DEBUG_OBJECT (h264parse, "we have a PPS NAL");
|
||||||
|
@ -1572,8 +1877,7 @@ gst_h264_parse_chain_forward (GstH264Parse * h264parse, gboolean discont,
|
||||||
else
|
else
|
||||||
GST_BUFFER_FLAG_UNSET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT);
|
GST_BUFFER_FLAG_UNSET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT);
|
||||||
|
|
||||||
gst_buffer_set_caps (outbuf, GST_PAD_CAPS (h264parse->srcpad));
|
res = gst_h264_parse_push_buffer (h264parse, outbuf);
|
||||||
res = gst_pad_push (h264parse->srcpad, outbuf);
|
|
||||||
} else {
|
} else {
|
||||||
/* NALU can not be parsed yet, we wait for more data in the adapter. */
|
/* NALU can not be parsed yet, we wait for more data in the adapter. */
|
||||||
break;
|
break;
|
||||||
|
@ -1624,6 +1928,7 @@ gst_h264_parse_flush_decode (GstH264Parse * h264parse)
|
||||||
GST_DEBUG_OBJECT (h264parse, "pushing buffer %p, ts %" GST_TIME_FORMAT,
|
GST_DEBUG_OBJECT (h264parse, "pushing buffer %p, ts %" GST_TIME_FORMAT,
|
||||||
buf, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
|
buf, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
|
||||||
|
|
||||||
|
gst_buffer_set_caps (buf, h264parse->src_caps);
|
||||||
res = gst_pad_push (h264parse->srcpad, buf);
|
res = gst_pad_push (h264parse->srcpad, buf);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1889,22 +2194,18 @@ gst_h264_parse_chain (GstPad * pad, GstBuffer * buffer)
|
||||||
|
|
||||||
h264parse = GST_H264PARSE (GST_PAD_PARENT (pad));
|
h264parse = GST_H264PARSE (GST_PAD_PARENT (pad));
|
||||||
|
|
||||||
if (!GST_PAD_CAPS (h264parse->srcpad)) {
|
if (!h264parse->src_caps) {
|
||||||
/* Set default caps if the sink caps were not negotiated, this is when we
|
/* Set default caps if the sink caps were not negotiated, this is when we
|
||||||
* are reading from a file or so */
|
* are reading from a file or so */
|
||||||
caps = gst_caps_new_simple ("video/x-h264", NULL);
|
caps = gst_caps_new_simple ("video/x-h264", NULL);
|
||||||
|
|
||||||
/* Set source caps */
|
|
||||||
if (!gst_pad_set_caps (h264parse->srcpad, caps))
|
|
||||||
goto caps_failed;
|
|
||||||
|
|
||||||
/* we assume the bytestream format. If the data turns out to be packetized,
|
/* we assume the bytestream format. If the data turns out to be packetized,
|
||||||
* we have a problem because we don't know the length of the nalu_size
|
* we have a problem because we don't know the length of the nalu_size
|
||||||
* indicator. Packetized input MUST set the codec_data. */
|
* indicator. Packetized input MUST set the codec_data. */
|
||||||
h264parse->packetized = FALSE;
|
h264parse->packetized = FALSE;
|
||||||
h264parse->nal_length_size = 4;
|
h264parse->nal_length_size = 4;
|
||||||
|
|
||||||
gst_caps_unref (caps);
|
h264parse->src_caps = caps;
|
||||||
}
|
}
|
||||||
|
|
||||||
discont = GST_BUFFER_IS_DISCONT (buffer);
|
discont = GST_BUFFER_IS_DISCONT (buffer);
|
||||||
|
@ -1918,15 +2219,6 @@ gst_h264_parse_chain (GstPad * pad, GstBuffer * buffer)
|
||||||
res = gst_h264_parse_chain_reverse (h264parse, discont, buffer);
|
res = gst_h264_parse_chain_reverse (h264parse, discont, buffer);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
/* ERRORS */
|
|
||||||
caps_failed:
|
|
||||||
{
|
|
||||||
GST_ELEMENT_ERROR (GST_ELEMENT (h264parse),
|
|
||||||
CORE, NEGOTIATION, (NULL), ("failed to set caps"));
|
|
||||||
gst_caps_unref (caps);
|
|
||||||
return GST_FLOW_ERROR;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
|
@ -2005,6 +2297,7 @@ gst_h264_parse_change_state (GstElement * element, GstStateChange transition)
|
||||||
switch (transition) {
|
switch (transition) {
|
||||||
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
||||||
gst_h264_parse_clear_queues (h264parse);
|
gst_h264_parse_clear_queues (h264parse);
|
||||||
|
gst_h264_parse_reset (h264parse);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -68,6 +68,7 @@ struct _GstH264Parse
|
||||||
gboolean split_packetized;
|
gboolean split_packetized;
|
||||||
gboolean merge;
|
gboolean merge;
|
||||||
guint nal_length_size;
|
guint nal_length_size;
|
||||||
|
guint format;
|
||||||
|
|
||||||
GstSegment segment;
|
GstSegment segment;
|
||||||
gboolean packetized;
|
gboolean packetized;
|
||||||
|
@ -124,6 +125,14 @@ struct _GstH264Parse
|
||||||
/* NALU AU */
|
/* NALU AU */
|
||||||
GstAdapter *picture_adapter;
|
GstAdapter *picture_adapter;
|
||||||
gboolean picture_start;
|
gboolean picture_start;
|
||||||
|
|
||||||
|
/* codec data NALUs to be inserted into stream */
|
||||||
|
GSList *codec_nals;
|
||||||
|
/* SPS and PPS NALUs collected from stream to form codec_data in caps */
|
||||||
|
GstBuffer *sps_nals[MAX_SPS_COUNT];
|
||||||
|
GstBuffer *pps_nals[MAX_PPS_COUNT];
|
||||||
|
|
||||||
|
GstCaps *src_caps;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _GstH264ParseClass
|
struct _GstH264ParseClass
|
||||||
|
|
Loading…
Reference in a new issue