# Description: Fixes failure to decode many common video files encoded as AVC 1 Baseline - L2.1, Baseline - L1.1 & others # Forwarded: not-needed # Origin: upstream # Bug-Ubuntu: https://bugs.launchpad.net/bugs/973014 --- gst-plugins-bad0.10-0.10.23.orig/gst/videoparsers/gsth264parse.c +++ gst-plugins-bad0.10-0.10.23/gst/videoparsers/gsth264parse.c @@ -177,11 +177,7 @@ gst_h264_parse_reset_frame (GstH264Parse GST_DEBUG_OBJECT (h264parse, "reset frame"); /* done parsing; reset state */ - h264parse->nalu.valid = FALSE; - h264parse->nalu.offset = 0; - h264parse->nalu.sc_offset = 0; - h264parse->nalu.size = 0; - h264parse->current_off = 0; + h264parse->current_off = -1; h264parse->picture_start = FALSE; h264parse->update_caps = FALSE; @@ -213,6 +209,8 @@ gst_h264_parse_reset (GstH264Parse * h26 h264parse->last_report = GST_CLOCK_TIME_NONE; h264parse->push_codec = FALSE; + h264parse->have_pps = FALSE; + h264parse->have_sps = FALSE; h264parse->dts = GST_CLOCK_TIME_NONE; h264parse->ts_trn_nb = GST_CLOCK_TIME_NONE; @@ -468,8 +466,15 @@ gst_h264_parse_process_nal (GstH264Parse GST_DEBUG_OBJECT (h264parse, "triggering src caps check"); h264parse->update_caps = TRUE; - /* found in stream, no need to forcibly push at start */ - h264parse->push_codec = FALSE; + h264parse->have_sps = TRUE; + if (h264parse->push_codec && h264parse->have_pps) { + /* SPS and PPS found in stream before the first pre_push_frame, no need + * to forcibly push at start */ + GST_INFO_OBJECT (h264parse, "have SPS/PPS in stream"); + h264parse->push_codec = FALSE; + h264parse->have_sps = FALSE; + h264parse->have_pps = FALSE; + } gst_h264_parser_store_nal (h264parse, sps.id, nal_type, nalu); break; @@ -478,8 +483,15 @@ gst_h264_parse_process_nal (GstH264Parse /* parameters might have changed, force caps check */ GST_DEBUG_OBJECT (h264parse, "triggering src caps check"); h264parse->update_caps = TRUE; - /* found in stream, no need to forcibly push at start */ - h264parse->push_codec = FALSE; + h264parse->have_pps = TRUE; + if (h264parse->push_codec && h264parse->have_sps) { + /* SPS and PPS found in stream before the first pre_push_frame, no need + * to forcibly push at start */ + GST_INFO_OBJECT (h264parse, "have SPS/PPS in stream"); + h264parse->push_codec = FALSE; + h264parse->have_sps = FALSE; + h264parse->have_pps = FALSE; + } gst_h264_parser_store_nal (h264parse, pps.id, nal_type, nalu); break; @@ -640,10 +652,12 @@ gst_h264_parse_check_valid_frame (GstBas GstH264Parse *h264parse = GST_H264_PARSE (parse); GstBuffer *buffer = frame->buffer; guint8 *data; - guint size, current_off = 0; - gboolean drain; + guint size; + gint current_off = 0; + gboolean drain, nonext; GstH264NalParser *nalparser = h264parse->nalparser; GstH264NalUnit nalu; + GstH264ParserResult pres; /* expect at least 3 bytes startcode == sc, and 2 bytes NALU payload */ if (G_UNLIKELY (GST_BUFFER_SIZE (buffer) < 5)) @@ -665,16 +679,39 @@ gst_h264_parse_check_valid_frame (GstBas data = GST_BUFFER_DATA (buffer); size = GST_BUFFER_SIZE (buffer); - drain = FALSE; - nalu = h264parse->nalu; + drain = GST_BASE_PARSE_DRAINING (parse); + nonext = FALSE; + current_off = h264parse->current_off; + if (current_off < 0) + current_off = 0; g_assert (current_off < size); + GST_DEBUG_OBJECT (h264parse, "last parse position %d", current_off); - GST_DEBUG_OBJECT (h264parse, "last parse position %u", current_off); - while (TRUE) { - GstH264ParserResult pres; + /* check for initial skip */ + if (h264parse->current_off == -1) { + pres = + gst_h264_parser_identify_nalu_unchecked (nalparser, data, current_off, + size, &nalu); + switch (pres) { + case GST_H264_PARSER_OK: + if (nalu.sc_offset > 0) { + *skipsize = nalu.sc_offset; + goto skip; + } + break; + case GST_H264_PARSER_NO_NAL: + *skipsize = size - 3; + goto skip; + break; + default: + g_assert_not_reached (); + break; + } + } + while (TRUE) { if (h264parse->packetized_chunked) pres = gst_h264_parser_identify_nalu_unchecked (nalparser, data, current_off, @@ -686,99 +723,85 @@ gst_h264_parse_check_valid_frame (GstBas switch (pres) { case GST_H264_PARSER_OK: - GST_DEBUG_OBJECT (h264parse, "complete nal found. " - "current offset: %u, Nal offset: %u, Nal Size: %u", - current_off, nalu.offset, nalu.size); - - GST_DEBUG_OBJECT (h264parse, "current off. %u", - nalu.offset + nalu.size); - - if (!h264parse->nalu.size && !h264parse->nalu.valid) - h264parse->nalu = nalu; - - /* need 2 bytes of next nal */ - if (!h264parse->packetized_chunked && - (nalu.offset + nalu.size + 4 + 2 > size)) { - if (GST_BASE_PARSE_DRAINING (parse)) { - drain = TRUE; - } else { - GST_DEBUG_OBJECT (h264parse, "need more bytes of next nal"); - current_off = nalu.sc_offset; - goto more; - } - } else if (h264parse->packetized_chunked) { - /* normal next nal based collection not possible, - * _chain will have to tell us whether this was last one for AU */ - drain = h264parse->packetized_last; - } + GST_DEBUG_OBJECT (h264parse, "complete nal (offset, size): (%u, %u) ", + nalu.offset, nalu.size); break; + case GST_H264_PARSER_NO_NAL_END: + GST_DEBUG_OBJECT (h264parse, "not a complete nal found at offset %u", + nalu.offset); + /* if draining, accept it as complete nal */ + if (drain) { + nonext = TRUE; + nalu.size = size - nalu.offset; + GST_DEBUG_OBJECT (h264parse, "draining, accepting with size %u", + nalu.size); + /* if it's not too short at least */ + if (nalu.size < 2) + goto broken; + break; + } + /* otherwise need more */ + goto more; case GST_H264_PARSER_BROKEN_LINK: - return FALSE; + g_assert_not_reached (); + break; case GST_H264_PARSER_ERROR: - current_off = size - 3; - goto parsing_error; + /* should not really occur either */ + GST_DEBUG_OBJECT (h264parse, "error parsing Nal Unit"); + /* fall-through */ case GST_H264_PARSER_NO_NAL: - /* don't expect to have found any NAL so far */ - g_assert (h264parse->nalu.size == 0); - current_off = h264parse->nalu.sc_offset = size - 3; - goto more; + g_assert_not_reached (); + break; case GST_H264_PARSER_BROKEN_DATA: GST_WARNING_OBJECT (h264parse, "input stream is corrupt; " - "it contains a NAL unit of length %d", nalu.size); - + "it contains a NAL unit of length %u", nalu.size); + broken: /* broken nal at start -> arrange to skip it, * otherwise have it terminate current au * (and so it will be skipped on next frame round) */ - if (nalu.sc_offset == h264parse->nalu.sc_offset) { - *skipsize = nalu.offset; - + if (current_off == 0) { GST_DEBUG_OBJECT (h264parse, "skipping broken nal"); - goto invalid; + *skipsize = nalu.offset; + goto skip; } else { + GST_DEBUG_OBJECT (h264parse, "terminating au"); nalu.size = 0; + nalu.offset = nalu.sc_offset; goto end; } - case GST_H264_PARSER_NO_NAL_END: - GST_DEBUG_OBJECT (h264parse, "not a complete nal found at offset %u", - nalu.offset); - - current_off = nalu.sc_offset; - /* We keep the reference to this nal so we start over the parsing - * here */ - if (!h264parse->nalu.size && !h264parse->nalu.valid) - h264parse->nalu = nalu; - - if (GST_BASE_PARSE_DRAINING (parse)) { - drain = TRUE; - GST_DEBUG_OBJECT (h264parse, "draining NAL %u %u %u", size, - h264parse->nalu.offset, h264parse->nalu.size); - /* Can't parse the nalu */ - if (size - h264parse->nalu.offset < 2) { - *skipsize = nalu.offset; - goto invalid; - } - - /* We parse it anyway */ - nalu.size = size - nalu.offset; - break; - } - goto more; + break; + default: + g_assert_not_reached (); + break; } - current_off = nalu.offset + nalu.size; - GST_DEBUG_OBJECT (h264parse, "%p complete nal found. Off: %u, Size: %u", data, nalu.offset, nalu.size); + /* simulate no next nal if none needed */ + nonext = nonext || (h264parse->align == GST_H264_PARSE_ALIGN_NAL); + + if (!nonext && !h264parse->packetized_chunked) { + if (nalu.offset + nalu.size + 4 + 2 > size) { + GST_DEBUG_OBJECT (h264parse, "not enough data for next NALU"); + if (drain) { + GST_DEBUG_OBJECT (h264parse, "but draining anyway"); + nonext = TRUE; + } else { + goto more; + } + } + } + gst_h264_parse_process_nal (h264parse, &nalu); - /* simulate no next nal if none needed */ - drain = drain || (h264parse->align == GST_H264_PARSE_ALIGN_NAL); + if (nonext) + break; /* In packetized mode we know there's only on NALU in each input packet, * but we may not have seen the whole AU already, possibly need more */ if (h264parse->packetized_chunked) { - if (drain) + if (h264parse->packetized_last) break; /* next NALU expected at end of current data */ current_off = size; @@ -786,42 +809,30 @@ gst_h264_parse_check_valid_frame (GstBas } /* if no next nal, we know it's complete here */ - if (drain || gst_h264_parse_collect_nal (h264parse, data, size, &nalu)) + if (gst_h264_parse_collect_nal (h264parse, data, size, &nalu)) break; GST_DEBUG_OBJECT (h264parse, "Looking for more"); + current_off = nalu.offset + nalu.size; } end: - *skipsize = h264parse->nalu.sc_offset; - *framesize = nalu.offset + nalu.size - h264parse->nalu.sc_offset; - h264parse->current_off = current_off; - + *framesize = nalu.offset + nalu.size; return TRUE; -parsing_error: - GST_DEBUG_OBJECT (h264parse, "error parsing Nal Unit"); - more: /* ask for best next available */ *framesize = G_MAXUINT; - if (!h264parse->nalu.size) { - /* skip up to initial startcode */ - *skipsize = h264parse->nalu.sc_offset; - /* but mind some stuff will have been skipped */ - g_assert (current_off >= *skipsize); - current_off -= *skipsize; - h264parse->nalu.sc_offset = 0; - } else { - *skipsize = 0; - } + *skipsize = 0; /* Restart parsing from here next time */ - h264parse->current_off = current_off; + if (current_off > 0) + h264parse->current_off = current_off; return FALSE; -invalid: +skip: + GST_DEBUG_OBJECT (h264parse, "skipping %d", *skipsize); gst_h264_parse_reset_frame (h264parse); return FALSE; } @@ -835,6 +846,7 @@ gst_h264_parse_make_codec_data (GstH264P guint8 profile_idc = 0, profile_comp = 0, level_idc = 0; gboolean found = FALSE; guint8 *data; + gint nl; /* only nal payload in stored nals */ @@ -867,12 +879,13 @@ gst_h264_parse_make_codec_data (GstH264P buf = gst_buffer_new_and_alloc (5 + 1 + sps_size + 1 + pps_size); data = GST_BUFFER_DATA (buf); + nl = h264parse->nal_length_size; 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[4] = 0xfc | (nl - 1); /* nal_length_size_minus1 */ data[5] = 0xe0 | num_sps; /* number of SPSs */ data += 6; @@ -1341,12 +1354,10 @@ check_pending_key_unit_event (GstEvent * running_time < pending_key_unit_ts) goto out; -#if 0 if (flags & GST_BUFFER_FLAG_DELTA_UNIT) { GST_DEBUG ("pending force key unit, waiting for keyframe"); goto out; } -#endif stream_time = gst_segment_to_stream_time (segment, GST_FORMAT_TIME, timestamp); @@ -1479,32 +1490,38 @@ gst_h264_parse_pre_push_frame (GstBasePa GstByteWriter bw; GstBuffer *new_buf; const gboolean bs = h264parse->format == GST_H264_PARSE_FORMAT_BYTE; + const gint nls = 4 - h264parse->nal_length_size; + gboolean ok; gst_byte_writer_init_with_size (&bw, GST_BUFFER_SIZE (buffer), FALSE); - gst_byte_writer_put_data (&bw, GST_BUFFER_DATA (buffer), + ok = gst_byte_writer_put_data (&bw, GST_BUFFER_DATA (buffer), h264parse->idr_pos); GST_DEBUG_OBJECT (h264parse, "- inserting SPS/PPS"); for (i = 0; i < GST_H264_MAX_SPS_COUNT; i++) { if ((codec_nal = h264parse->sps_nals[i])) { GST_DEBUG_OBJECT (h264parse, "inserting SPS nal"); - gst_byte_writer_put_uint32_be (&bw, - bs ? 1 : GST_BUFFER_SIZE (codec_nal)); - gst_byte_writer_put_data (&bw, GST_BUFFER_DATA (codec_nal), - GST_BUFFER_SIZE (codec_nal)); + ok &= gst_byte_writer_put_uint32_be (&bw, + bs ? 1 : (GST_BUFFER_SIZE (codec_nal) << (nls * 8))); + ok &= gst_byte_writer_set_pos (&bw, + gst_byte_writer_get_pos (&bw) - nls); + ok &= gst_byte_writer_put_data (&bw, + GST_BUFFER_DATA (codec_nal), GST_BUFFER_SIZE (codec_nal)); h264parse->last_report = new_ts; } } for (i = 0; i < GST_H264_MAX_PPS_COUNT; i++) { if ((codec_nal = h264parse->pps_nals[i])) { GST_DEBUG_OBJECT (h264parse, "inserting PPS nal"); - gst_byte_writer_put_uint32_be (&bw, - bs ? 1 : GST_BUFFER_SIZE (codec_nal)); - gst_byte_writer_put_data (&bw, GST_BUFFER_DATA (codec_nal), - GST_BUFFER_SIZE (codec_nal)); + ok &= gst_byte_writer_put_uint32_be (&bw, + bs ? 1 : (GST_BUFFER_SIZE (codec_nal) << (nls * 8))); + ok &= gst_byte_writer_set_pos (&bw, + gst_byte_writer_get_pos (&bw) - nls); + ok &= gst_byte_writer_put_data (&bw, + GST_BUFFER_DATA (codec_nal), GST_BUFFER_SIZE (codec_nal)); h264parse->last_report = new_ts; } } - gst_byte_writer_put_data (&bw, + ok &= gst_byte_writer_put_data (&bw, GST_BUFFER_DATA (buffer) + h264parse->idr_pos, GST_BUFFER_SIZE (buffer) - h264parse->idr_pos); /* collect result and push */ @@ -1515,10 +1532,16 @@ gst_h264_parse_pre_push_frame (GstBasePa GST_BUFFER_FLAG_UNSET (new_buf, GST_BUFFER_FLAG_DELTA_UNIT); gst_buffer_replace (&frame->buffer, new_buf); gst_buffer_unref (new_buf); + /* some result checking seems to make some compilers happy */ + if (G_UNLIKELY (!ok)) { + GST_ERROR_OBJECT (h264parse, "failed to insert SPS/PPS"); + } } } /* we pushed whatever we had */ h264parse->push_codec = FALSE; + h264parse->have_sps = FALSE; + h264parse->have_pps = FALSE; } } @@ -1623,10 +1646,10 @@ gst_h264_parse_set_caps (GstBaseParse * /* if upstream sets codec_data without setting stream-format and alignment, we * assume stream-format=avc,alignment=au */ - if (format == GST_H264_PARSE_FORMAT_NONE) { + if (format == GST_H264_PARSE_FORMAT_NONE) format = GST_H264_PARSE_FORMAT_AVC; + if (align == GST_H264_PARSE_ALIGN_NONE) align = GST_H264_PARSE_ALIGN_AU; - } } else { GST_DEBUG_OBJECT (h264parse, "have bytestream h264"); /* nothing to pre-process */ @@ -1665,6 +1688,8 @@ gst_h264_parse_set_caps (GstBaseParse * /* arrange to insert codec-data in-stream if needed. * src caps are only arranged for later on */ h264parse->push_codec = TRUE; + h264parse->have_sps = FALSE; + h264parse->have_pps = FALSE; h264parse->split_packetized = TRUE; h264parse->packetized = TRUE; } --- gst-plugins-bad0.10-0.10.23.orig/gst/videoparsers/gsth264parse.h +++ gst-plugins-bad0.10-0.10.23/gst/videoparsers/gsth264parse.h @@ -67,15 +67,16 @@ struct _GstH264Parse /* state */ GstH264NalParser *nalparser; - GstH264NalUnit nalu; guint align; guint format; - guint current_off; + gint current_off; gboolean packetized_last; gboolean packetized_chunked; GstClockTime last_report; gboolean push_codec; + gboolean have_sps; + gboolean have_pps; /* collected SPS and PPS NALUs */ GstBuffer *sps_nals[GST_H264_MAX_SPS_COUNT];