static GstFlowReturn gst_h264_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame) { GstH264Parse *h264parse; GstBuffer *buffer; guint av; h264parse = GST_H264_PARSE (parse); buffer = frame->buffer; gst_h264_parse_update_src_caps (h264parse); gst_h264_params_get_timestamp (h264parse->params, &GST_BUFFER_TIMESTAMP (buffer), &GST_BUFFER_DURATION (buffer), h264parse->frame_start); if (h264parse->keyframe) GST_BUFFER_FLAG_UNSET (buffer, GST_BUFFER_FLAG_DELTA_UNIT); else GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT); /* replace with transformed AVC output if applicable */ av = gst_adapter_available (h264parse->frame_out); if (av) { GstBuffer *buf; buf = gst_adapter_take_buffer (h264parse->frame_out, av); gst_buffer_copy_metadata (buf, buffer, GST_BUFFER_COPY_ALL); gst_buffer_replace (&frame->buffer, buf); } return GST_FLOW_OK; }
static void gst_h264_parse_finalize (GObject * object) { GstH264Parse *h264parse = GST_H264_PARSE (object); g_object_unref (h264parse->frame_out); G_OBJECT_CLASS (parent_class)->finalize (object); }
static gboolean gst_h264_parse_stop (GstBaseParse * parse) { GstH264Parse *h264parse = GST_H264_PARSE (parse); GST_DEBUG ("Stop"); gst_h264_parse_reset (h264parse); gst_h264_params_free (h264parse->params); h264parse->params = NULL; return TRUE; }
static gboolean gst_h264_parse_start (GstBaseParse * parse) { GstH264Parse *h264parse = GST_H264_PARSE (parse); GST_DEBUG ("Start"); gst_h264_parse_reset (h264parse); gst_h264_params_create (&h264parse->params, GST_ELEMENT (h264parse)); gst_base_parse_set_min_frame_size (parse, 512); return TRUE; }
static void gst_h264_parse_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { GstH264Parse *parse; parse = GST_H264_PARSE (object); switch (prop_id) { case PROP_CONFIG_INTERVAL: g_value_set_uint (value, parse->interval); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } }
static void gst_h264_parse_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { GstH264Parse *parse; parse = GST_H264_PARSE (object); switch (prop_id) { case PROP_SPLIT_PACKETIZED: parse->split_packetized = g_value_get_boolean (value); break; case PROP_CONFIG_INTERVAL: parse->interval = g_value_get_uint (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } }
static gboolean gst_h264_parse_set_caps (GstBaseParse * parse, GstCaps * caps) { GstH264Parse *h264parse; GstStructure *str; const GValue *value; GstBuffer *buffer = NULL; guint size; h264parse = GST_H264_PARSE (parse); /* reset */ h264parse->push_codec = FALSE; str = gst_caps_get_structure (caps, 0); /* accept upstream info if provided */ gst_structure_get_int (str, "width", &h264parse->width); gst_structure_get_int (str, "height", &h264parse->height); gst_structure_get_fraction (str, "framerate", &h264parse->fps_num, &h264parse->fps_den); /* packetized video has a codec_data */ if ((value = gst_structure_get_value (str, "codec_data"))) { guint8 *data; guint num_sps, num_pps, profile, len; gint i; GST_DEBUG_OBJECT (h264parse, "have packetized h264"); /* make note for optional split processing */ h264parse->packetized = TRUE; buffer = gst_value_get_buffer (value); if (!buffer) goto wrong_type; 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); num_sps = data[5] & 0x1f; data += 6; size -= 6; for (i = 0; i < num_sps; i++) { len = GST_READ_UINT16_BE (data); if (size < len + 2 || len < 2) goto avcc_too_small; /* digest for later reference */ gst_h264_parse_process_nal (h264parse, data, 0, 2, len); data += len + 2; size -= len + 2; } num_pps = data[0]; data++; size++; for (i = 0; i < num_pps; i++) { len = GST_READ_UINT16_BE (data); if (size < len + 2 || len < 2) goto avcc_too_small; /* digest for later reference */ gst_h264_parse_process_nal (h264parse, data, 0, 2, len); data += len + 2; size -= len + 2; } } else { GST_DEBUG_OBJECT (h264parse, "have bytestream h264"); /* nothing to pre-process */ h264parse->packetized = FALSE; /* we have 4 sync bytes */ h264parse->nal_length_size = 4; } if (h264parse->packetized) { if (h264parse->split_packetized) { GST_DEBUG_OBJECT (h264parse, "converting AVC to nal bytestream prior to parsing"); /* negotiate behaviour with upstream */ gst_h264_parse_negotiate (h264parse); if (h264parse->format == GST_H264_PARSE_FORMAT_BYTE) { /* arrange to insert codec-data in-stream if needed */ h264parse->push_codec = h264parse->packetized; } } else { GST_DEBUG_OBJECT (h264parse, "passing on packetized AVC"); /* no choice to negotiate */ h264parse->format = GST_H264_PARSE_FORMAT_AVC; h264parse->align = GST_H264_PARSE_ALIGN_AU; /* fallback codec-data */ h264parse->codec_data = gst_buffer_ref (buffer); /* pass through unharmed, though _chain will parse a bit */ gst_base_parse_set_format (parse, GST_BASE_PARSE_FORMAT_PASSTHROUGH, TRUE); /* we did parse codec-data and might supplement src caps */ gst_h264_parse_update_src_caps (h264parse); } } /* src caps are only arranged for later on */ return TRUE; /* ERRORS */ avcc_too_small: { GST_DEBUG_OBJECT (h264parse, "avcC size %u < 7", size); goto refuse_caps; } wrong_version: { GST_DEBUG_OBJECT (h264parse, "wrong avcC version"); goto refuse_caps; } wrong_type: { GST_DEBUG_OBJECT (h264parse, "wrong codec-data type"); goto refuse_caps; } refuse_caps: { GST_WARNING_OBJECT (h264parse, "refused caps %" GST_PTR_FORMAT, caps); return FALSE; } }
static GstFlowReturn gst_h264_parse_pre_push_frame (GstBaseParse * parse, GstBaseParseFrame * frame) { GstH264Parse *h264parse; GstBuffer *buffer; h264parse = GST_H264_PARSE (parse); buffer = frame->buffer; /* periodic SPS/PPS sending */ if (h264parse->interval > 0 || h264parse->push_codec) { GstClockTime timestamp = GST_BUFFER_TIMESTAMP (buffer); guint64 diff; /* init */ if (!GST_CLOCK_TIME_IS_VALID (h264parse->last_report)) { h264parse->last_report = timestamp; } if (h264parse->idr_pos >= 0) { GST_LOG_OBJECT (h264parse, "IDR nal at offset %d", h264parse->idr_pos); if (timestamp > h264parse->last_report) diff = timestamp - h264parse->last_report; else diff = 0; GST_LOG_OBJECT (h264parse, "now %" GST_TIME_FORMAT ", last SPS/PPS %" GST_TIME_FORMAT, GST_TIME_ARGS (timestamp), GST_TIME_ARGS (h264parse->last_report)); GST_DEBUG_OBJECT (h264parse, "interval since last SPS/PPS %" GST_TIME_FORMAT, GST_TIME_ARGS (diff)); if (GST_TIME_AS_SECONDS (diff) >= h264parse->interval || h264parse->push_codec) { GstBuffer *codec_nal; gint i; GstClockTime new_ts; /* avoid overwriting a perfectly fine timestamp */ new_ts = GST_CLOCK_TIME_IS_VALID (timestamp) ? timestamp : h264parse->last_report; if (h264parse->align == GST_H264_PARSE_ALIGN_NAL) { /* send separate config NAL buffers */ GST_DEBUG_OBJECT (h264parse, "- sending SPS/PPS"); for (i = 0; i < MAX_SPS_COUNT; i++) { if ((codec_nal = h264parse->params->sps_nals[i])) { GST_DEBUG_OBJECT (h264parse, "sending SPS nal"); gst_h264_parse_push_codec_buffer (h264parse, codec_nal, timestamp); h264parse->last_report = new_ts; } } for (i = 0; i < MAX_PPS_COUNT; i++) { if ((codec_nal = h264parse->params->pps_nals[i])) { GST_DEBUG_OBJECT (h264parse, "sending PPS nal"); gst_h264_parse_push_codec_buffer (h264parse, codec_nal, timestamp); h264parse->last_report = new_ts; } } } else { /* insert config NALs into AU */ GstByteWriter bw; GstBuffer *new_buf; const gboolean bs = h264parse->format == GST_H264_PARSE_FORMAT_BYTE; gst_byte_writer_init_with_size (&bw, GST_BUFFER_SIZE (buffer), FALSE); gst_byte_writer_put_data (&bw, GST_BUFFER_DATA (buffer), h264parse->idr_pos); GST_DEBUG_OBJECT (h264parse, "- inserting SPS/PPS"); for (i = 0; i < MAX_SPS_COUNT; i++) { if ((codec_nal = h264parse->params->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)); h264parse->last_report = new_ts; } } for (i = 0; i < MAX_PPS_COUNT; i++) { if ((codec_nal = h264parse->params->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)); h264parse->last_report = new_ts; } } gst_byte_writer_put_data (&bw, GST_BUFFER_DATA (buffer) + h264parse->idr_pos, GST_BUFFER_SIZE (buffer) - h264parse->idr_pos); /* collect result and push */ new_buf = gst_byte_writer_reset_and_get_buffer (&bw); gst_buffer_copy_metadata (new_buf, buffer, GST_BUFFER_COPY_ALL); gst_buffer_replace (&frame->buffer, new_buf); } } /* we pushed whatever we had */ h264parse->push_codec = FALSE; } } gst_h264_parse_reset_frame (h264parse); return GST_FLOW_OK; }
static gboolean gst_h264_parse_check_valid_frame (GstBaseParse * parse, GstBaseParseFrame * frame, guint * framesize, gint * skipsize) { GstH264Parse *h264parse = GST_H264_PARSE (parse); GstBuffer *buffer = frame->buffer; gint sc_pos, nal_pos, next_sc_pos, next_nal_pos; guint8 *data; guint size; gboolean drain; /* expect at least 3 bytes startcode == sc, and 2 bytes NALU payload */ if (G_UNLIKELY (GST_BUFFER_SIZE (buffer) < 5)) return FALSE; /* need to configure aggregation */ if (G_UNLIKELY (h264parse->format == GST_H264_PARSE_FORMAT_NONE)) gst_h264_parse_negotiate (h264parse); data = GST_BUFFER_DATA (buffer); size = GST_BUFFER_SIZE (buffer); GST_LOG_OBJECT (h264parse, "last_nal_pos: %d, last_scan_pos %d", h264parse->last_nal_pos, h264parse->next_sc_pos); nal_pos = h264parse->last_nal_pos; next_sc_pos = h264parse->next_sc_pos; if (!next_sc_pos) { sc_pos = gst_h264_parse_find_sc (buffer, 0); if (sc_pos == -1) { /* SC not found, need more data */ sc_pos = GST_BUFFER_SIZE (buffer) - 3; goto more; } nal_pos = sc_pos + 3; next_sc_pos = nal_pos; /* sc might have 2 or 3 0-bytes */ if (sc_pos > 0 && data[sc_pos - 1] == 00) sc_pos--; GST_LOG_OBJECT (h264parse, "found sc at offset %d", sc_pos); } else { /* previous checks already arrange sc at start */ sc_pos = 0; } drain = GST_BASE_PARSE_FRAME_DRAIN (frame); while (TRUE) { gint prev_sc_pos; next_sc_pos = gst_h264_parse_find_sc (buffer, next_sc_pos); if (next_sc_pos == -1) { GST_LOG_OBJECT (h264parse, "no next sc"); if (drain) { /* FLUSH/EOS, it's okay if we can't find the next frame */ next_sc_pos = size; next_nal_pos = size; } else { next_sc_pos = size - 3; goto more; } } else { next_nal_pos = next_sc_pos + 3; if (data[next_sc_pos - 1] == 00) next_sc_pos--; GST_LOG_OBJECT (h264parse, "found next sc at offset %d", next_sc_pos); /* need at least 1 more byte of next NAL */ if (!drain && (next_nal_pos == size - 1)) goto more; } /* determine nal's sc position */ prev_sc_pos = nal_pos - 3; g_assert (prev_sc_pos >= 0); if (prev_sc_pos > 0 && data[prev_sc_pos - 1] == 0) prev_sc_pos--; /* already consume and gather info from NAL */ gst_h264_parse_process_nal (h264parse, data, prev_sc_pos, nal_pos, next_sc_pos - nal_pos); if (next_nal_pos >= size - 1 || gst_h264_parse_collect_nal (h264parse, data + nal_pos, data + next_nal_pos)) break; /* move along */ next_sc_pos = nal_pos = next_nal_pos; } *skipsize = sc_pos; *framesize = next_sc_pos - sc_pos; return TRUE; more: /* Ask for 1024 bytes more - this is an arbitrary choice */ gst_base_parse_set_min_frame_size (parse, GST_BUFFER_SIZE (buffer) + 1024); /* skip up to initial startcode */ *skipsize = sc_pos; /* resume scanning here next time */ h264parse->last_nal_pos = nal_pos; h264parse->next_sc_pos = next_sc_pos; return FALSE; }
static GstFlowReturn gst_h264_parse_chain (GstPad * pad, GstBuffer * buffer) { GstH264Parse *h264parse = GST_H264_PARSE (GST_PAD_PARENT (pad)); if (h264parse->packetized && buffer) { GstByteReader br; GstBuffer *sub; GstFlowReturn ret = GST_FLOW_OK; guint32 len; const guint nl = h264parse->nal_length_size; GST_LOG_OBJECT (h264parse, "processing packet buffer of size %d", GST_BUFFER_SIZE (buffer)); gst_byte_reader_init_from_buffer (&br, buffer); while (ret == GST_FLOW_OK && gst_byte_reader_get_remaining (&br)) { GST_DEBUG_OBJECT (h264parse, "AVC nal offset %d", gst_byte_reader_get_pos (&br)); if (gst_byte_reader_get_remaining (&br) < nl) goto parse_failed; switch (nl) { case 4: len = gst_byte_reader_get_uint32_be_unchecked (&br); break; case 3: len = gst_byte_reader_get_uint24_be_unchecked (&br); break; case 2: len = gst_byte_reader_get_uint16_be_unchecked (&br); break; case 1: len = gst_byte_reader_get_uint8_unchecked (&br); break; default: goto not_negotiated; break; } GST_DEBUG_OBJECT (h264parse, "AVC nal size %d", len); if (gst_byte_reader_get_remaining (&br) < len) goto parse_failed; if (h264parse->split_packetized) { /* convert to NAL aligned byte stream input */ sub = gst_h264_parse_wrap_nal (h264parse, GST_H264_PARSE_FORMAT_BYTE, (guint8 *) gst_byte_reader_get_data_unchecked (&br, len), len); /* at least this should make sense */ GST_BUFFER_TIMESTAMP (sub) = GST_BUFFER_TIMESTAMP (buffer); GST_LOG_OBJECT (h264parse, "pushing NAL of size %d", len); ret = h264parse->parse_chain (pad, sub); } else { /* pass-through: no looking for frames (and nal processing), * so need to parse to collect data here */ /* NOTE: so if it is really configured to do so, * pre_push can/will still insert codec-data at intervals, * which is not really pure pass-through, but anyway ... */ gst_h264_parse_process_nal (h264parse, GST_BUFFER_DATA (buffer), gst_byte_reader_get_pos (&br) - nl, gst_byte_reader_get_pos (&br), len); gst_byte_reader_skip_unchecked (&br, len); } } if (h264parse->split_packetized) return ret; } exit: /* nal processing in pass-through might have collected stuff; * ensure nothing happens with this later on */ gst_adapter_clear (h264parse->frame_out); return h264parse->parse_chain (pad, buffer); /* ERRORS */ not_negotiated: { GST_DEBUG_OBJECT (h264parse, "insufficient data to split input"); return GST_FLOW_NOT_NEGOTIATED; } parse_failed: { if (h264parse->split_packetized) { GST_ELEMENT_ERROR (h264parse, STREAM, FAILED, (NULL), ("invalid AVC input data")); return GST_FLOW_ERROR; } else { /* do not meddle to much in this case */ GST_DEBUG_OBJECT (h264parse, "parsing packet failed"); goto exit; } } }
static gboolean gst_h264_parse_set_caps (GstBaseParse * parse, GstCaps * caps) { GstH264Parse *h264parse; GstStructure *str; const GValue *value; GstBuffer *codec_data = NULL; guint size, format, align; h264parse = GST_H264_PARSE (parse); /* reset */ h264parse->push_codec = FALSE; str = gst_caps_get_structure (caps, 0); /* accept upstream info if provided */ gst_structure_get_int (str, "width", &h264parse->width); gst_structure_get_int (str, "height", &h264parse->height); gst_structure_get_fraction (str, "framerate", &h264parse->fps_num, &h264parse->fps_den); /* get upstream format and align from caps */ gst_h264_parse_format_from_caps (caps, &format, &align); /* packetized video has a codec_data */ if (format != GST_H264_PARSE_FORMAT_BYTE && (value = gst_structure_get_value (str, "codec_data"))) { guint8 *data; guint num_sps, num_pps, profile, len; gint i; GST_DEBUG_OBJECT (h264parse, "have packetized h264"); /* make note for optional split processing */ h264parse->packetized = TRUE; codec_data = gst_value_get_buffer (value); if (!codec_data) goto wrong_type; data = GST_BUFFER_DATA (codec_data); size = GST_BUFFER_SIZE (codec_data); /* 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); num_sps = data[5] & 0x1f; data += 6; size -= 6; for (i = 0; i < num_sps; i++) { len = GST_READ_UINT16_BE (data); if (size < len + 2 || len < 2) goto avcc_too_small; /* digest for later reference */ gst_h264_parse_process_nal (h264parse, data, 0, 2, len); data += len + 2; size -= len + 2; } num_pps = data[0]; data++; size++; for (i = 0; i < num_pps; i++) { len = GST_READ_UINT16_BE (data); if (size < len + 2 || len < 2) goto avcc_too_small; /* digest for later reference */ gst_h264_parse_process_nal (h264parse, data, 0, 2, len); data += len + 2; size -= len + 2; } h264parse->codec_data = gst_buffer_ref (codec_data); /* 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) { format = GST_H264_PARSE_FORMAT_AVC; align = GST_H264_PARSE_ALIGN_AU; } } else { GST_DEBUG_OBJECT (h264parse, "have bytestream h264"); /* nothing to pre-process */ h264parse->packetized = FALSE; /* we have 4 sync bytes */ h264parse->nal_length_size = 4; if (format == GST_H264_PARSE_FORMAT_NONE) { format = GST_H264_PARSE_FORMAT_BYTE; align = GST_H264_PARSE_ALIGN_AU; } } /* negotiate with downstream, sets ->format and ->align */ gst_h264_parse_negotiate (h264parse); if (format == h264parse->format && align == h264parse->align) { gst_base_parse_set_passthrough (parse, TRUE); /* we did parse codec-data and might supplement src caps */ gst_h264_parse_update_src_caps (h264parse, caps); } else if (format == GST_H264_PARSE_FORMAT_AVC) { /* if input != output, and input is avc, must split before anything else */ /* arrange to insert codec-data in-stream if needed. * src caps are only arranged for later on */ h264parse->push_codec = TRUE; h264parse->split_packetized = TRUE; h264parse->packetized = TRUE; } return TRUE; /* ERRORS */ avcc_too_small: { GST_DEBUG_OBJECT (h264parse, "avcC size %u < 7", size); goto refuse_caps; } wrong_version: { GST_DEBUG_OBJECT (h264parse, "wrong avcC version"); goto refuse_caps; } wrong_type: { GST_DEBUG_OBJECT (h264parse, "wrong codec-data type"); goto refuse_caps; } unknown_input_format: { GST_DEBUG_OBJECT (h264parse, "unknown stream-format and no codec_data"); goto refuse_caps; } refuse_caps: { GST_WARNING_OBJECT (h264parse, "refused caps %" GST_PTR_FORMAT, caps); return FALSE; } }
static gboolean gst_h264_parse_check_valid_frame (GstBaseParse * parse, GstBaseParseFrame * frame, guint * framesize, gint * skipsize) { GstH264Parse *h264parse = GST_H264_PARSE (parse); GstBuffer *buffer = frame->buffer; gint sc_pos, nal_pos, next_sc_pos, next_nal_pos; guint8 *data; guint size; gboolean drain; /* expect at least 3 bytes startcode == sc, and 2 bytes NALU payload */ if (G_UNLIKELY (GST_BUFFER_SIZE (buffer) < 5)) return FALSE; /* need to configure aggregation */ if (G_UNLIKELY (h264parse->format == GST_H264_PARSE_FORMAT_NONE)) gst_h264_parse_negotiate (h264parse); /* avoid stale cached parsing state */ if (!(frame->flags & GST_BASE_PARSE_FRAME_FLAG_PARSING)) { GST_LOG_OBJECT (h264parse, "parsing new frame"); gst_h264_parse_reset_frame (h264parse); frame->flags |= GST_BASE_PARSE_FRAME_FLAG_PARSING; } else { GST_LOG_OBJECT (h264parse, "resuming frame parsing"); } data = GST_BUFFER_DATA (buffer); size = GST_BUFFER_SIZE (buffer); GST_LOG_OBJECT (h264parse, "last_nal_pos: %d, last_scan_pos %d", h264parse->last_nal_pos, h264parse->next_sc_pos); nal_pos = h264parse->last_nal_pos; next_sc_pos = h264parse->next_sc_pos; if (!next_sc_pos) { sc_pos = gst_h264_parse_find_sc (buffer, 0); if (sc_pos == -1) { /* SC not found, need more data */ sc_pos = GST_BUFFER_SIZE (buffer) - 3; /* avoid going < 0 later on */ nal_pos = next_sc_pos = sc_pos; goto more; } nal_pos = sc_pos + 3; next_sc_pos = nal_pos; /* sc might have 2 or 3 0-bytes */ if (sc_pos > 0 && data[sc_pos - 1] == 00) sc_pos--; GST_LOG_OBJECT (h264parse, "found sc at offset %d", sc_pos); } else { /* previous checks already arrange sc at start */ sc_pos = 0; } drain = GST_BASE_PARSE_DRAINING (parse); while (TRUE) { gint prev_sc_pos; next_sc_pos = gst_h264_parse_find_sc (buffer, next_sc_pos); if (next_sc_pos == -1) { GST_LOG_OBJECT (h264parse, "no next sc"); if (drain) { /* FLUSH/EOS, it's okay if we can't find the next frame */ next_sc_pos = size; next_nal_pos = size; } else { next_sc_pos = size - 3; goto more; } } else { next_nal_pos = next_sc_pos + 3; if (data[next_sc_pos - 1] == 00) next_sc_pos--; GST_LOG_OBJECT (h264parse, "found next sc at offset %d", next_sc_pos); /* need at least 1 more byte of next NAL */ if (!drain && (next_nal_pos == size - 1)) goto more; } /* determine nal's sc position */ prev_sc_pos = nal_pos - 3; g_assert (prev_sc_pos >= 0); if (prev_sc_pos > 0 && data[prev_sc_pos - 1] == 0) prev_sc_pos--; /* already consume and gather info from NAL */ if (G_UNLIKELY (next_sc_pos - nal_pos < 2)) { GST_WARNING_OBJECT (h264parse, "input stream is corrupt; " "it contains a NAL unit of length %d", next_sc_pos - nal_pos); /* broken nal at start -> arrange to skip it, * otherwise have it terminate current au * (and so it will be skippd on next frame round) */ if (prev_sc_pos == sc_pos) { *skipsize = sc_pos + 2; return FALSE; } else { next_sc_pos = prev_sc_pos; break; } } gst_h264_parse_process_nal (h264parse, data, prev_sc_pos, nal_pos, next_sc_pos - nal_pos); if (next_nal_pos >= size - 1 || gst_h264_parse_collect_nal (h264parse, data + nal_pos, data + next_nal_pos)) break; /* move along */ next_sc_pos = nal_pos = next_nal_pos; } *skipsize = sc_pos; *framesize = next_sc_pos - sc_pos; return TRUE; more: /* ask for best next available */ *framesize = G_MAXUINT; /* skip up to initial startcode */ *skipsize = sc_pos; /* resume scanning here next time */ h264parse->last_nal_pos = nal_pos - sc_pos; h264parse->next_sc_pos = next_sc_pos - sc_pos; return FALSE; }