/** * gst_adapter_masked_scan_uint32: * @adapter: a #GstAdapter * @mask: mask to apply to data before matching against @pattern * @pattern: pattern to match (after mask is applied) * @offset: offset into the adapter data from which to start scanning, returns * the last scanned position. * @size: number of bytes to scan from offset * * Scan for pattern @pattern with applied mask @mask in the adapter data, * starting from offset @offset. * * The bytes in @pattern and @mask are interpreted left-to-right, regardless * of endianness. All four bytes of the pattern must be present in the * adapter for it to match, even if the first or last bytes are masked out. * * It is an error to call this function without making sure that there is * enough data (offset+size bytes) in the adapter. * * This function calls gst_adapter_masked_scan_uint32_peek() passing NULL * for value. * * Returns: offset of the first match, or -1 if no match was found. * * Example: * <programlisting> * // Assume the adapter contains 0x00 0x01 0x02 ... 0xfe 0xff * * gst_adapter_masked_scan_uint32 (adapter, 0xffffffff, 0x00010203, 0, 256); * // -> returns 0 * gst_adapter_masked_scan_uint32 (adapter, 0xffffffff, 0x00010203, 1, 255); * // -> returns -1 * gst_adapter_masked_scan_uint32 (adapter, 0xffffffff, 0x01020304, 1, 255); * // -> returns 1 * gst_adapter_masked_scan_uint32 (adapter, 0xffff, 0x0001, 0, 256); * // -> returns -1 * gst_adapter_masked_scan_uint32 (adapter, 0xffff, 0x0203, 0, 256); * // -> returns 0 * gst_adapter_masked_scan_uint32 (adapter, 0xffff0000, 0x02030000, 0, 256); * // -> returns 2 * gst_adapter_masked_scan_uint32 (adapter, 0xffff0000, 0x02030000, 0, 4); * // -> returns -1 * </programlisting> * * Since: 0.10.24 */ guint gst_adapter_masked_scan_uint32 (GstAdapter * adapter, guint32 mask, guint32 pattern, guint offset, guint size) { return gst_adapter_masked_scan_uint32_peek (adapter, mask, pattern, offset, size, NULL); }
/* returns image length in bytes if parsed successfully, * otherwise 0 if more data needed, * if < 0 the absolute value needs to be flushed */ static gint gst_jpeg_parse_get_image_length (GstJpegParse * parse) { guint size; gboolean resync; GstAdapter *adapter = parse->priv->adapter; gint offset, noffset; size = gst_adapter_available (adapter); /* we expect at least 4 bytes, first of which start marker */ if (gst_adapter_masked_scan_uint32 (adapter, 0xffff0000, 0xffd80000, 0, 4)) return 0; GST_DEBUG ("Parsing jpeg image data (%u bytes)", size); GST_DEBUG ("Parse state: offset=%d, resync=%d, entropy len=%d", parse->priv->last_offset, parse->priv->last_resync, parse->priv->last_entropy_len); /* offset is 2 less than actual offset; * - adapter needs at least 4 bytes for scanning, * - start and end marker ensure at least that much */ /* resume from state offset */ offset = parse->priv->last_offset; while (1) { guint frame_len; guint32 value; noffset = gst_adapter_masked_scan_uint32_peek (adapter, 0x0000ff00, 0x0000ff00, offset, size - offset, &value); /* lost sync if 0xff marker not where expected */ if ((resync = (noffset != offset))) { GST_DEBUG ("Lost sync at 0x%08x, resyncing", offset + 2); } /* may have marker, but could have been resyncng */ resync = resync || parse->priv->last_resync; /* Skip over extra 0xff */ while ((noffset >= 0) && ((value & 0xff) == 0xff)) { noffset++; noffset = gst_adapter_masked_scan_uint32_peek (adapter, 0x0000ff00, 0x0000ff00, noffset, size - noffset, &value); } /* enough bytes left for marker? (we need 0xNN after the 0xff) */ if (noffset < 0) { GST_DEBUG ("at end of input and no EOI marker found, need more data"); goto need_more_data; } /* now lock on the marker we found */ offset = noffset; value = value & 0xff; if (value == 0xd9) { GST_DEBUG ("0x%08x: EOI marker", offset + 2); /* clear parse state */ parse->priv->last_resync = FALSE; parse->priv->last_offset = 0; return (offset + 4); } else if (value == 0xd8) { /* Skip this frame if we found another SOI marker */ GST_DEBUG ("0x%08x: SOI marker before EOI, skipping", offset + 2); /* clear parse state */ parse->priv->last_resync = FALSE; parse->priv->last_offset = 0; return -(offset + 2); } if (value >= 0xd0 && value <= 0xd7) frame_len = 0; else { /* peek tag and subsequent length */ if (offset + 2 + 4 > size) goto need_more_data; else gst_adapter_masked_scan_uint32_peek (adapter, 0x0, 0x0, offset + 2, 4, &frame_len); frame_len = frame_len & 0xffff; } GST_DEBUG ("0x%08x: tag %02x, frame_len=%u", offset + 2, value, frame_len); /* the frame length includes the 2 bytes for the length; here we want at * least 2 more bytes at the end for an end marker */ if (offset + 2 + 2 + frame_len + 2 > size) { goto need_more_data; } if (gst_jpeg_parse_parse_tag_has_entropy_segment (value)) { guint eseglen = parse->priv->last_entropy_len; GST_DEBUG ("0x%08x: finding entropy segment length", offset + 2); noffset = offset + 2 + frame_len + eseglen; while (1) { noffset = gst_adapter_masked_scan_uint32_peek (adapter, 0x0000ff00, 0x0000ff00, noffset, size - noffset, &value); if (noffset < 0) { /* need more data */ parse->priv->last_entropy_len = size - offset - 4 - frame_len - 2; goto need_more_data; } if ((value & 0xff) != 0x00) { eseglen = noffset - offset - frame_len - 2; break; } noffset++; } parse->priv->last_entropy_len = 0; frame_len += eseglen; GST_DEBUG ("entropy segment length=%u => frame_len=%u", eseglen, frame_len); } if (resync) { /* check if we will still be in sync if we interpret * this as a sync point and skip this frame */ noffset = offset + frame_len + 2; noffset = gst_adapter_masked_scan_uint32 (adapter, 0x0000ff00, 0x0000ff00, noffset, 4); if (noffset < 0) { /* ignore and continue resyncing until we hit the end * of our data or find a sync point that looks okay */ offset++; continue; } GST_DEBUG ("found sync at 0x%x", offset + 2); } offset += frame_len + 2; } /* EXITS */ need_more_data: { parse->priv->last_offset = offset; parse->priv->last_resync = resync; return 0; } }
static GstFlowReturn gst_jpeg_dec_parse (GstVideoDecoder * bdec, GstVideoCodecFrame * frame, GstAdapter * adapter, gboolean at_eos) { guint size; gint toadd = 0; gboolean resync; gint offset = 0, noffset; GstJpegDec *dec = (GstJpegDec *) bdec; /* FIXME : The overhead of using scan_uint32 is massive */ size = gst_adapter_available (adapter); GST_DEBUG ("Parsing jpeg image data (%u bytes)", size); if (at_eos) { GST_DEBUG ("Flushing all data out"); toadd = size; /* If we have leftover data, throw it away */ if (!dec->saw_header) goto drop_frame; goto have_full_frame; } if (size < 8) goto need_more_data; if (!dec->saw_header) { gint ret; /* we expect at least 4 bytes, first of which start marker */ ret = gst_adapter_masked_scan_uint32 (adapter, 0xffff0000, 0xffd80000, 0, size - 4); GST_DEBUG ("ret:%d", ret); if (ret < 0) goto need_more_data; if (ret) { gst_adapter_flush (adapter, ret); size -= ret; } dec->saw_header = TRUE; } while (1) { guint frame_len; guint32 value; GST_DEBUG ("offset:%d, size:%d", offset, size); noffset = gst_adapter_masked_scan_uint32_peek (adapter, 0x0000ff00, 0x0000ff00, offset, size - offset, &value); /* lost sync if 0xff marker not where expected */ if ((resync = (noffset != offset))) { GST_DEBUG ("Lost sync at 0x%08x, resyncing", offset + 2); } /* may have marker, but could have been resyncng */ resync = resync || dec->parse_resync; /* Skip over extra 0xff */ while ((noffset >= 0) && ((value & 0xff) == 0xff)) { noffset++; noffset = gst_adapter_masked_scan_uint32_peek (adapter, 0x0000ff00, 0x0000ff00, noffset, size - noffset, &value); } /* enough bytes left for marker? (we need 0xNN after the 0xff) */ if (noffset < 0) { GST_DEBUG ("at end of input and no EOI marker found, need more data"); goto need_more_data; } /* now lock on the marker we found */ offset = noffset; value = value & 0xff; if (value == 0xd9) { GST_DEBUG ("0x%08x: EOI marker", offset + 2); /* clear parse state */ dec->saw_header = FALSE; dec->parse_resync = FALSE; toadd = offset + 4; goto have_full_frame; } if (value == 0xd8) { /* Skip this frame if we found another SOI marker */ GST_DEBUG ("0x%08x: SOI marker before EOI, skipping", offset + 2); dec->parse_resync = FALSE; /* FIXME : Need to skip data */ toadd -= offset + 2; goto have_full_frame; } if (value >= 0xd0 && value <= 0xd7) frame_len = 0; else { /* peek tag and subsequent length */ if (offset + 2 + 4 > size) goto need_more_data; else gst_adapter_masked_scan_uint32_peek (adapter, 0x0, 0x0, offset + 2, 4, &frame_len); frame_len = frame_len & 0xffff; } GST_DEBUG ("0x%08x: tag %02x, frame_len=%u", offset + 2, value, frame_len); /* the frame length includes the 2 bytes for the length; here we want at * least 2 more bytes at the end for an end marker */ if (offset + 2 + 2 + frame_len + 2 > size) { goto need_more_data; } if (gst_jpeg_dec_parse_tag_has_entropy_segment (value)) { guint eseglen = dec->parse_entropy_len; GST_DEBUG ("0x%08x: finding entropy segment length (eseglen:%d)", offset + 2, eseglen); if (size < offset + 2 + frame_len + eseglen) goto need_more_data; noffset = offset + 2 + frame_len + dec->parse_entropy_len; while (1) { GST_DEBUG ("noffset:%d, size:%d, size - noffset:%d", noffset, size, size - noffset); noffset = gst_adapter_masked_scan_uint32_peek (adapter, 0x0000ff00, 0x0000ff00, noffset, size - noffset, &value); if (noffset < 0) { /* need more data */ dec->parse_entropy_len = size - offset - 4 - frame_len - 2; goto need_more_data; } if ((value & 0xff) != 0x00) { eseglen = noffset - offset - frame_len - 2; break; } noffset++; } dec->parse_entropy_len = 0; frame_len += eseglen; GST_DEBUG ("entropy segment length=%u => frame_len=%u", eseglen, frame_len); } if (resync) { /* check if we will still be in sync if we interpret * this as a sync point and skip this frame */ noffset = offset + frame_len + 2; noffset = gst_adapter_masked_scan_uint32 (adapter, 0x0000ff00, 0x0000ff00, noffset, 4); if (noffset < 0) { /* ignore and continue resyncing until we hit the end * of our data or find a sync point that looks okay */ offset++; continue; } GST_DEBUG ("found sync at 0x%x", offset + 2); } /* Add current data to output buffer */ toadd += frame_len + 2; offset += frame_len + 2; } need_more_data: if (toadd) gst_video_decoder_add_to_frame (bdec, toadd); return GST_VIDEO_DECODER_FLOW_NEED_DATA; have_full_frame: if (toadd) gst_video_decoder_add_to_frame (bdec, toadd); return gst_video_decoder_have_frame (bdec); drop_frame: gst_adapter_flush (adapter, size); return GST_FLOW_OK; }