From 10d104fcaee69109357bdefabca69cffeb01638c Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 3 Jun 2008 11:10:32 +0000 Subject: gst/h264parse/gsth264parse.*: Parse codec_data and use the nalu_size_length field to get the NALU length in packetize... Original commit message from CVS: * gst/h264parse/gsth264parse.c: (gst_nal_bs_init), (gst_h264_parse_sink_setcaps), (gst_h264_parse_chain_forward), (gst_h264_parse_queue_buffer), (gst_h264_parse_chain_reverse), (gst_h264_parse_chain): * gst/h264parse/gsth264parse.h: Parse codec_data and use the nalu_size_length field to get the NALU length in packetized h264. When queueing a packetized buffer in reverse mode, don't unref the buffer twice. Avoid accessing the buffer TIMESTAMP field after we pushed it on the adaptor. --- gst/h264parse/gsth264parse.c | 127 ++++++++++++++++++++++++++++++++----------- gst/h264parse/gsth264parse.h | 1 + 2 files changed, 97 insertions(+), 31 deletions(-) (limited to 'gst') diff --git a/gst/h264parse/gsth264parse.c b/gst/h264parse/gsth264parse.c index 45d63379..91c75734 100644 --- a/gst/h264parse/gsth264parse.c +++ b/gst/h264parse/gsth264parse.c @@ -127,14 +127,14 @@ gst_nal_list_delete_head (GstNalList * list) * emulation_prevention_three_bytes. */ typedef struct { - guint8 *data; - guint8 *end; + const guint8 *data; + const guint8 *end; gint head; /* bitpos in the cache of next bit */ guint64 cache; /* cached bytes */ } GstNalBs; static void -gst_nal_bs_init (GstNalBs * bs, guint8 * data, guint size) +gst_nal_bs_init (GstNalBs * bs, const guint8 * data, guint size) { bs->data = data; bs->end = data + size; @@ -335,6 +335,8 @@ gst_h264_parse_sink_setcaps (GstPad * pad, GstCaps * caps) GstH264Parse *h264parse; GstStructure *str; const GValue *value; + guint8 *data; + guint size; h264parse = GST_H264PARSE (GST_PAD_PARENT (pad)); @@ -342,18 +344,59 @@ gst_h264_parse_sink_setcaps (GstPad * pad, GstCaps * caps) /* packetized video has a codec_data */ if ((value = gst_structure_get_value (str, "codec_data"))) { + GstBuffer *buffer; + gint profile; + GST_DEBUG_OBJECT (h264parse, "have packetized h264"); h264parse->packetized = TRUE; + + buffer = gst_value_get_buffer (value); + data = GST_BUFFER_DATA (buffer); + size = GST_BUFFER_SIZE (buffer); + + /* parse the avcC data */ + if (size < 7) + goto avcc_too_small; + /* parse the version, this must be 1 */ + if (data[0] != 1) + goto wrong_version; + + /* AVCProfileIndication */ + /* profile_compat */ + /* AVCLevelIndication */ + profile = (data[1] << 16) | (data[2] << 8) | data[3]; + GST_DEBUG_OBJECT (h264parse, "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 */ + h264parse->nal_length_size = (data[4] & 0x03) + 1; + GST_DEBUG_OBJECT (h264parse, "nal length %u", h264parse->nal_length_size); + /* FIXME, PPS, SPS have vital info for detecting new I-frames */ } else { GST_DEBUG_OBJECT (h264parse, "have bytestream h264"); h264parse->packetized = FALSE; + /* we have 4 sync bytes */ + h264parse->nal_length_size = 4; } /* forward the caps */ res = gst_pad_set_caps (h264parse->srcpad, caps); return res; + + /* ERRORS */ +avcc_too_small: + { + GST_ERROR_OBJECT (h264parse, "avcC size %u < 7", size); + return FALSE; + } +wrong_version: + { + GST_ERROR_OBJECT (h264parse, "wrong avcC version"); + return FALSE; + } } static void @@ -382,31 +425,30 @@ gst_h264_parse_chain_forward (GstH264Parse * h264parse, gboolean discont, { GstFlowReturn res = GST_FLOW_OK; const guint8 *data; + GstClockTime timestamp; if (discont) { gst_adapter_clear (h264parse->adapter); h264parse->discont = TRUE; } + timestamp = GST_BUFFER_TIMESTAMP (buffer); + gst_adapter_push (h264parse->adapter, buffer); while (res == GST_FLOW_OK) { gint i; gint next_nalu_pos = -1; - guint32 nalu_size; gint avail; gboolean delta_unit = TRUE; avail = gst_adapter_available (h264parse->adapter); - if (avail < 5) + if (avail < h264parse->nal_length_size + 1) break; data = gst_adapter_peek (h264parse->adapter, avail); - nalu_size = (data[0] << 24) - + (data[1] << 16) + (data[2] << 8) + data[3]; - - if (nalu_size == 1) { - /* Bytestream format */ + if (!h264parse->packetized) { + /* Bytestream format, first 4 bytes are sync code */ /* Find next NALU header */ for (i = 1; i < avail - 4; ++i) { if (data[i + 0] == 0 && data[i + 1] == 0 && data[i + 2] == 0 @@ -416,21 +458,32 @@ gst_h264_parse_chain_forward (GstH264Parse * h264parse, gboolean discont, } } } else { + guint32 nalu_size; + + nalu_size = 0; + for (i = 0; i < h264parse->nal_length_size; i++) + nalu_size = (nalu_size << 8) | data[i]; + /* Packetized format, see if we have to split it, usually splitting is not * a good idea as decoders have no way of handling it. */ if (h264parse->split_packetized) { - if (nalu_size + 4 <= avail) - next_nalu_pos = nalu_size + 4; + if (nalu_size + h264parse->nal_length_size <= avail) + next_nalu_pos = nalu_size + h264parse->nal_length_size; } else { next_nalu_pos = avail; } } + + /* skip nalu_size bytes or sync */ + data += h264parse->nal_length_size; + avail -= h264parse->nal_length_size; + /* Figure out if this is a delta unit */ { gint nal_type, nal_ref_idc; - nal_type = (data[4] & 0x1f); - nal_ref_idc = (data[4] & 0x60) >> 5; + nal_type = (data[0] & 0x1f); + nal_ref_idc = (data[0] & 0x60) >> 5; GST_DEBUG_OBJECT (h264parse, "NAL type: %d, ref_idc: %d", nal_type, nal_ref_idc); @@ -439,9 +492,8 @@ gst_h264_parse_chain_forward (GstH264Parse * h264parse, gboolean discont, if (nal_type >= NAL_SLICE && nal_type <= NAL_SLICE_IDR) { GstNalBs bs; gint first_mb_in_slice, slice_type; - guint8 *bs_data = (guint8 *) data + 5; - gst_nal_bs_init (&bs, bs_data, avail - 5); + gst_nal_bs_init (&bs, data + 1, avail - 1); first_mb_in_slice = gst_nal_bs_read_ue (&bs); slice_type = gst_nal_bs_read_ue (&bs); @@ -495,12 +547,13 @@ gst_h264_parse_chain_forward (GstH264Parse * h264parse, gboolean discont, h264parse->discont = FALSE; } - if (delta_unit) { + if (delta_unit) GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT); - } + else + GST_BUFFER_FLAG_UNSET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT); gst_buffer_set_caps (outbuf, GST_PAD_CAPS (h264parse->srcpad)); - GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buffer); + GST_BUFFER_TIMESTAMP (outbuf) = timestamp; res = gst_pad_push (h264parse->srcpad, outbuf); } else { /* NALU can not be parsed yet, we wait for more data in the adapter. */ @@ -584,19 +637,29 @@ gst_h264_parse_queue_buffer (GstH264Parse * parse, GstBuffer * buffer) /* now parse all the NAL units in this buffer, for bytestream we only have one * NAL unit but for packetized streams we can have multiple ones */ - while (size >= 5) { - nalu_size = (data[0] << 24) - + (data[1] << 16) + (data[2] << 8) + data[3]; + while (size >= parse->nal_length_size + 1) { + gint i; + + nalu_size = 0; + if (parse->packetized) { + for (i = 0; i < parse->nal_length_size; i++) + nalu_size = (nalu_size << 8) | data[i]; + } + + /* skip nalu_size or sync bytes */ + data += parse->nal_length_size; + size -= parse->nal_length_size; - link->nal_ref_idc = (data[4] & 0x60) >> 5; - link->nal_type = (data[4] & 0x1f); + link->nal_ref_idc = (data[0] & 0x60) >> 5; + link->nal_type = (data[0] & 0x1f); + /* nalu_size is 0 for bytestream, we have a complete packet */ GST_DEBUG_OBJECT (parse, "size: %u, NAL type: %d, ref_idc: %d", nalu_size, link->nal_type, link->nal_ref_idc); /* first parse some things needed to get to the frame type */ if (link->nal_type >= NAL_SLICE && link->nal_type <= NAL_SLICE_IDR) { - gst_nal_bs_init (&bs, data + 5, size - 5); + gst_nal_bs_init (&bs, data + 1, size - 1); link->first_mb_in_slice = gst_nal_bs_read_ue (&bs); link->slice_type = gst_nal_bs_read_ue (&bs); @@ -629,11 +692,11 @@ gst_h264_parse_queue_buffer (GstH264Parse * parse, GstBuffer * buffer) } } /* bytestream, we can exit now */ - if (nalu_size == 1) + if (!parse->packetized) break; - /* packetized format, continue parsing all packets, skip size */ - nalu_size += 4; + /* packetized format, continue parsing all packets, skip size, we already + * skipped the nal_length_size bytes */ data += nalu_size; size -= nalu_size; } @@ -717,6 +780,7 @@ gst_h264_parse_chain_reverse (GstH264Parse * h264parse, gboolean discont, * store them */ GST_DEBUG_OBJECT (h264parse, "copied packetized buffer"); res = gst_h264_parse_queue_buffer (h264parse, gbuf); + gbuf = NULL; } else { /* bytestream, we have to split the NALUs on the sync markers */ code = 0xffffffff; @@ -808,10 +872,11 @@ gst_h264_parse_chain (GstPad * pad, GstBuffer * buffer) if (!gst_pad_set_caps (h264parse->srcpad, caps)) goto caps_failed; - /* we assume the bytestream format but won't really fail otherwise if the - * data turns out to be a nicely aligned packetized format (except we don't - * do the codec_data caps with the PPS and SPS). */ + /* 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 + * indicator. Packetized input MUST set the codec_data. */ h264parse->packetized = FALSE; + h264parse->nal_length_size = 4; gst_caps_unref (caps); } diff --git a/gst/h264parse/gsth264parse.h b/gst/h264parse/gsth264parse.h index 4dc74228..2bae3f8c 100644 --- a/gst/h264parse/gsth264parse.h +++ b/gst/h264parse/gsth264parse.h @@ -52,6 +52,7 @@ struct _GstH264Parse GstPad *srcpad; gboolean split_packetized; + guint nal_length_size; GstSegment segment; gboolean packetized; -- cgit v1.2.1