mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-09-09 05:38:58 +00:00
gst/multipart/multipartdemux.c: Uses GstAdapter instead of own buffering.
Original commit message from CVS: Patch by: Sjoerd Simons <sjoerd at luon dot net> * gst/multipart/multipartdemux.c: (gst_multipart_demux_base_init), (gst_multipart_demux_class_init), (gst_multipart_demux_init), (gst_multipart_demux_finalize), (get_line_end), (multipart_parse_header), (multipart_find_boundary), (gst_multipart_demux_chain), (gst_multipart_demux_change_state), (gst_multipart_set_property), (gst_multipart_get_property): Uses GstAdapter instead of own buffering. Actually parses the mime-type correctly (In tests the mime-type was always "" with the old version). Uses the Content-length header if available to speed up things. Reliably autoscans the boundary name by default. Fixes #349068. * gst/multipart/multipartmux.c: (gst_multipart_mux_collected): Don't start the stream with a \n.
This commit is contained in:
parent
da5c3416e7
commit
4441dc23ee
3 changed files with 297 additions and 208 deletions
20
ChangeLog
20
ChangeLog
|
@ -1,3 +1,23 @@
|
||||||
|
2006-07-28 Wim Taymans <wim@fluendo.com>
|
||||||
|
|
||||||
|
Patch by: Sjoerd Simons <sjoerd at luon dot net>
|
||||||
|
|
||||||
|
* gst/multipart/multipartdemux.c: (gst_multipart_demux_base_init),
|
||||||
|
(gst_multipart_demux_class_init), (gst_multipart_demux_init),
|
||||||
|
(gst_multipart_demux_finalize), (get_line_end),
|
||||||
|
(multipart_parse_header), (multipart_find_boundary),
|
||||||
|
(gst_multipart_demux_chain), (gst_multipart_demux_change_state),
|
||||||
|
(gst_multipart_set_property), (gst_multipart_get_property):
|
||||||
|
Uses GstAdapter instead of own buffering.
|
||||||
|
Actually parses the mime-type correctly (In tests the mime-type was
|
||||||
|
always "" with the old version).
|
||||||
|
Uses the Content-length header if available to speed up things.
|
||||||
|
Reliably autoscans the boundary name by default.
|
||||||
|
Fixes #349068.
|
||||||
|
|
||||||
|
* gst/multipart/multipartmux.c: (gst_multipart_mux_collected):
|
||||||
|
Don't start the stream with a \n.
|
||||||
|
|
||||||
2006-07-28 Tim-Philipp Müller <tim at centricular dot net>
|
2006-07-28 Tim-Philipp Müller <tim at centricular dot net>
|
||||||
|
|
||||||
Patch by: Brian Cameron <brian dot cameron at sun com>
|
Patch by: Brian Cameron <brian dot cameron at sun com>
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
/* GStreamer
|
/* GStreamer
|
||||||
|
* Copyright (C) 2006 Sjoerd Simons <sjoerd@luon.net>
|
||||||
* Copyright (C) 2004 Wim Taymans <wim@fluendo.com>
|
* Copyright (C) 2004 Wim Taymans <wim@fluendo.com>
|
||||||
*
|
*
|
||||||
* gstmultipartdemux.c: multipart stream demuxer
|
* gstmultipartdemux.c: multipart stream demuxer
|
||||||
|
@ -46,8 +47,8 @@
|
||||||
* </para>
|
* </para>
|
||||||
* <para>
|
* <para>
|
||||||
* the content in multipart files is separated with a boundary string that can be
|
* the content in multipart files is separated with a boundary string that can be
|
||||||
* configured specifically with the "boundary" property or can be autodetected by
|
* configured specifically with the "boundary" property otherwise it will be
|
||||||
* setting the "autoscan" property to TRUE.
|
* autodetected.
|
||||||
* </para>
|
* </para>
|
||||||
* </refsect2>
|
* </refsect2>
|
||||||
*/
|
*/
|
||||||
|
@ -57,6 +58,7 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <gst/gst.h>
|
#include <gst/gst.h>
|
||||||
|
#include <gst/base/gstadapter.h>
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
@ -69,7 +71,9 @@
|
||||||
typedef struct _GstMultipartDemux GstMultipartDemux;
|
typedef struct _GstMultipartDemux GstMultipartDemux;
|
||||||
typedef struct _GstMultipartDemuxClass GstMultipartDemuxClass;
|
typedef struct _GstMultipartDemuxClass GstMultipartDemuxClass;
|
||||||
|
|
||||||
#define MAX_LINE_LEN 500
|
#define MULTIPART_NEED_MORE_DATA -1
|
||||||
|
#define MULTIPART_DATA_ERROR -2
|
||||||
|
#define MULTIPART_DATA_EOS -3
|
||||||
|
|
||||||
/* all information needed for one multipart stream */
|
/* all information needed for one multipart stream */
|
||||||
typedef struct
|
typedef struct
|
||||||
|
@ -77,11 +81,6 @@ typedef struct
|
||||||
GstPad *pad; /* reference for this pad is held by element we belong to */
|
GstPad *pad; /* reference for this pad is held by element we belong to */
|
||||||
|
|
||||||
gchar *mime;
|
gchar *mime;
|
||||||
|
|
||||||
guint64 offset; /* end offset of last buffer */
|
|
||||||
guint64 known_offset; /* last known offset */
|
|
||||||
|
|
||||||
guint flags;
|
|
||||||
}
|
}
|
||||||
GstMultipartPad;
|
GstMultipartPad;
|
||||||
|
|
||||||
|
@ -100,17 +99,20 @@ struct _GstMultipartDemux
|
||||||
GSList *srcpads;
|
GSList *srcpads;
|
||||||
gint numpads;
|
gint numpads;
|
||||||
|
|
||||||
gchar *parsing_mime;
|
GstAdapter *adapter;
|
||||||
gchar *buffer;
|
|
||||||
gint maxlen;
|
|
||||||
gint bufsize;
|
|
||||||
gint scanpos;
|
|
||||||
gint lastpos;
|
|
||||||
|
|
||||||
gchar *prefix;
|
/* Header information of the current frame */
|
||||||
gint prefixLen;
|
gboolean header_completed;
|
||||||
gboolean first_frame;
|
gchar *boundary;
|
||||||
|
guint boundary_len;
|
||||||
|
gchar *mime_type;
|
||||||
|
gint content_length;
|
||||||
|
|
||||||
|
/* deprecated, unused */
|
||||||
gboolean autoscan;
|
gboolean autoscan;
|
||||||
|
|
||||||
|
/* Index inside the current data when manually looking for the boundary */
|
||||||
|
gint scanpos;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _GstMultipartDemuxClass
|
struct _GstMultipartDemuxClass
|
||||||
|
@ -126,7 +128,7 @@ static const GstElementDetails gst_multipart_demux_details =
|
||||||
GST_ELEMENT_DETAILS ("Multipart demuxer",
|
GST_ELEMENT_DETAILS ("Multipart demuxer",
|
||||||
"Codec/Demuxer",
|
"Codec/Demuxer",
|
||||||
"demux multipart streams",
|
"demux multipart streams",
|
||||||
"Wim Taymans <wim@fluendo.com>");
|
"Wim Taymans <wim@fluendo.com>, Sjoerd Simons <sjoerd@luon.net>");
|
||||||
|
|
||||||
|
|
||||||
/* signals and args */
|
/* signals and args */
|
||||||
|
@ -136,14 +138,14 @@ enum
|
||||||
LAST_SIGNAL
|
LAST_SIGNAL
|
||||||
};
|
};
|
||||||
|
|
||||||
#define DEFAULT_BOUNDARY "ThisRandomString"
|
#define DEFAULT_AUTOSCAN FALSE
|
||||||
#define DEFAULT_AUTOSCAN FALSE
|
#define DEFAULT_BOUNDARY NULL
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
PROP_0,
|
PROP_0,
|
||||||
PROP_BOUNDARY,
|
PROP_AUTOSCAN,
|
||||||
PROP_AUTOSCAN
|
PROP_BOUNDARY
|
||||||
/* FILL ME */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static GstStaticPadTemplate multipart_demux_src_template_factory =
|
static GstStaticPadTemplate multipart_demux_src_template_factory =
|
||||||
|
@ -172,7 +174,6 @@ static void gst_multipart_get_property (GObject * object, guint prop_id,
|
||||||
|
|
||||||
static void gst_multipart_demux_finalize (GObject * object);
|
static void gst_multipart_demux_finalize (GObject * object);
|
||||||
|
|
||||||
|
|
||||||
GST_BOILERPLATE (GstMultipartDemux, gst_multipart_demux, GstElement,
|
GST_BOILERPLATE (GstMultipartDemux, gst_multipart_demux, GstElement,
|
||||||
GST_TYPE_ELEMENT);
|
GST_TYPE_ELEMENT);
|
||||||
|
|
||||||
|
@ -180,37 +181,35 @@ static void
|
||||||
gst_multipart_demux_base_init (gpointer g_class)
|
gst_multipart_demux_base_init (gpointer g_class)
|
||||||
{
|
{
|
||||||
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
|
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
|
||||||
GObjectClass *gobject_class = G_OBJECT_CLASS (g_class);
|
|
||||||
|
|
||||||
gst_element_class_set_details (element_class, &gst_multipart_demux_details);
|
|
||||||
gobject_class->set_property = gst_multipart_set_property;
|
|
||||||
gobject_class->get_property = gst_multipart_get_property;
|
|
||||||
|
|
||||||
gst_element_class_add_pad_template (element_class,
|
gst_element_class_add_pad_template (element_class,
|
||||||
gst_static_pad_template_get (&multipart_demux_sink_template_factory));
|
gst_static_pad_template_get (&multipart_demux_sink_template_factory));
|
||||||
gst_element_class_add_pad_template (element_class,
|
gst_element_class_add_pad_template (element_class,
|
||||||
gst_static_pad_template_get (&multipart_demux_src_template_factory));
|
gst_static_pad_template_get (&multipart_demux_src_template_factory));
|
||||||
|
gst_element_class_set_details (element_class, &gst_multipart_demux_details);
|
||||||
g_object_class_install_property (gobject_class, PROP_BOUNDARY,
|
|
||||||
g_param_spec_string ("boundary", "Boundary",
|
|
||||||
"The boundary string separating data", DEFAULT_BOUNDARY,
|
|
||||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
|
|
||||||
|
|
||||||
g_object_class_install_property (gobject_class, PROP_AUTOSCAN,
|
|
||||||
g_param_spec_boolean ("autoscan", "autoscan",
|
|
||||||
"Try to autofind the prefix", DEFAULT_AUTOSCAN,
|
|
||||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_multipart_demux_class_init (GstMultipartDemuxClass * klass)
|
gst_multipart_demux_class_init (GstMultipartDemuxClass * klass)
|
||||||
{
|
{
|
||||||
GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
|
|
||||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||||
|
GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
|
||||||
|
|
||||||
|
gobject_class->finalize = gst_multipart_demux_finalize;
|
||||||
|
gobject_class->set_property = gst_multipart_set_property;
|
||||||
|
gobject_class->get_property = gst_multipart_get_property;
|
||||||
|
|
||||||
|
g_object_class_install_property (gobject_class, PROP_BOUNDARY,
|
||||||
|
g_param_spec_string ("boundary", "Boundary",
|
||||||
|
"The boundary string separating data, automatic if NULL",
|
||||||
|
DEFAULT_BOUNDARY, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
|
||||||
|
|
||||||
|
g_object_class_install_property (gobject_class, PROP_AUTOSCAN,
|
||||||
|
g_param_spec_boolean ("autoscan", "autoscan",
|
||||||
|
"Try to autofind the prefix (deprecated unused, see boundary)",
|
||||||
|
DEFAULT_AUTOSCAN, G_PARAM_READWRITE));
|
||||||
|
|
||||||
gstelement_class->change_state = gst_multipart_demux_change_state;
|
gstelement_class->change_state = gst_multipart_demux_change_state;
|
||||||
gobject_class->finalize = gst_multipart_demux_finalize;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -225,15 +224,24 @@ gst_multipart_demux_init (GstMultipartDemux * multipart,
|
||||||
gst_pad_set_chain_function (multipart->sinkpad,
|
gst_pad_set_chain_function (multipart->sinkpad,
|
||||||
GST_DEBUG_FUNCPTR (gst_multipart_demux_chain));
|
GST_DEBUG_FUNCPTR (gst_multipart_demux_chain));
|
||||||
|
|
||||||
multipart->maxlen = 4096;
|
multipart->adapter = gst_adapter_new ();
|
||||||
|
multipart->boundary = DEFAULT_BOUNDARY;
|
||||||
|
multipart->mime_type = NULL;
|
||||||
|
multipart->content_length = -1;
|
||||||
|
multipart->header_completed = FALSE;
|
||||||
|
multipart->scanpos = 0;
|
||||||
|
multipart->autoscan = DEFAULT_AUTOSCAN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_multipart_demux_finalize (GObject * object)
|
gst_multipart_demux_finalize (GObject * object)
|
||||||
{
|
{
|
||||||
GstMultipartDemux *demux = GST_MULTIPART_DEMUX (object);
|
GstMultipartDemux *demux = GST_MULTIPART_DEMUX (object);
|
||||||
|
|
||||||
g_free (demux->prefix);
|
g_object_unref (demux->adapter);
|
||||||
|
g_free (demux->boundary);
|
||||||
|
g_free (demux->mime_type);
|
||||||
|
|
||||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||||
}
|
}
|
||||||
|
@ -292,162 +300,226 @@ gst_multipart_find_pad_by_mime (GstMultipartDemux * demux, gchar * mime,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
get_line_end (const guint8 * data, const guint8 * dataend, guint8 ** end,
|
||||||
|
guint8 ** next)
|
||||||
|
{
|
||||||
|
guint8 *x;
|
||||||
|
gboolean foundr = FALSE;
|
||||||
|
|
||||||
|
for (x = (guint8 *) data; x < dataend; x++) {
|
||||||
|
if (*x == '\r') {
|
||||||
|
foundr = TRUE;
|
||||||
|
} else if (*x == '\n') {
|
||||||
|
*end = x - (foundr ? 1 : 0);
|
||||||
|
*next = x + 1;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gint
|
||||||
|
multipart_parse_header (GstMultipartDemux * multipart)
|
||||||
|
{
|
||||||
|
const guint8 *data;
|
||||||
|
const guint8 *dataend;
|
||||||
|
gchar *boundary;
|
||||||
|
int boundary_len;
|
||||||
|
int datalen;
|
||||||
|
guint8 *pos;
|
||||||
|
guint8 *end, *next;
|
||||||
|
|
||||||
|
datalen = gst_adapter_available (multipart->adapter);
|
||||||
|
data = gst_adapter_peek (multipart->adapter, datalen);
|
||||||
|
dataend = data + datalen;
|
||||||
|
|
||||||
|
/* First the boundary */
|
||||||
|
if (!get_line_end (data, dataend, &end, &next))
|
||||||
|
return MULTIPART_NEED_MORE_DATA;
|
||||||
|
|
||||||
|
/* Ignore the leading -- */
|
||||||
|
boundary_len = end - data - 2;
|
||||||
|
boundary = (gchar *) data + 2;
|
||||||
|
if (boundary_len < 1) {
|
||||||
|
GST_DEBUG_OBJECT (multipart, "No boundary available");
|
||||||
|
goto wrong_header;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (G_UNLIKELY (multipart->boundary == NULL)) {
|
||||||
|
/* First time we see the boundary, copy it */
|
||||||
|
multipart->boundary = g_strndup (boundary, boundary_len);
|
||||||
|
multipart->boundary_len = boundary_len;
|
||||||
|
} else if (G_UNLIKELY (boundary_len != multipart->boundary_len)) {
|
||||||
|
/* Something odd is going on, either the boundary indicated EOS or it's
|
||||||
|
* invalid */
|
||||||
|
if (G_UNLIKELY (boundary_len == multipart->boundary_len + 2 &&
|
||||||
|
!strncmp (boundary, multipart->boundary, multipart->boundary_len) &&
|
||||||
|
!strncmp (boundary + multipart->boundary_len, "--", 2))) {
|
||||||
|
return MULTIPART_DATA_EOS;
|
||||||
|
}
|
||||||
|
GST_DEBUG_OBJECT (multipart,
|
||||||
|
"Boundary length doesn't match detected boundary (%d <> %d",
|
||||||
|
boundary_len, multipart->boundary_len);
|
||||||
|
goto wrong_header;
|
||||||
|
} else if (G_UNLIKELY (strncmp (boundary, multipart->boundary, boundary_len))) {
|
||||||
|
GST_DEBUG_OBJECT (multipart, "Boundary doesn't match previous boundary");
|
||||||
|
goto wrong_header;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pos = next;
|
||||||
|
while (get_line_end (pos, dataend, &end, &next)) {
|
||||||
|
guint len = end - pos;
|
||||||
|
|
||||||
|
if (len == 0) {
|
||||||
|
/* empty line, data starts behind us */
|
||||||
|
GST_DEBUG_OBJECT (multipart,
|
||||||
|
"Parsed the header - boundary: %s, mime-type: %s, content-length: %d",
|
||||||
|
multipart->boundary, multipart->mime_type, multipart->content_length);
|
||||||
|
return next - data;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len >= 14 && !g_ascii_strncasecmp ("content-type:", (gchar *) pos, 13)) {
|
||||||
|
g_free (multipart->mime_type);
|
||||||
|
multipart->mime_type = g_strndup ((gchar *) pos + 14, len - 14);
|
||||||
|
} else if (len >= 15 &&
|
||||||
|
!g_ascii_strncasecmp ("content-length:", (gchar *) pos, 15)) {
|
||||||
|
multipart->content_length =
|
||||||
|
g_ascii_strtoull ((gchar *) pos + 15, NULL, 10);
|
||||||
|
}
|
||||||
|
pos = next;
|
||||||
|
}
|
||||||
|
GST_DEBUG_OBJECT (multipart, "Need more data for the header");
|
||||||
|
return MULTIPART_NEED_MORE_DATA;
|
||||||
|
|
||||||
|
wrong_header:
|
||||||
|
{
|
||||||
|
GST_ELEMENT_ERROR (multipart, STREAM, DEMUX, (NULL),
|
||||||
|
("Boundary not found in the multipart header"));
|
||||||
|
return MULTIPART_DATA_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static gint
|
||||||
|
multipart_find_boundary (GstMultipartDemux * multipart, gint * datalen)
|
||||||
|
{
|
||||||
|
/* Adaptor is positioned at the start of the data */
|
||||||
|
const guint8 *data, *pos;
|
||||||
|
const guint8 *dataend;
|
||||||
|
gint len;
|
||||||
|
|
||||||
|
if (multipart->content_length >= 0) {
|
||||||
|
/* fast path, known content length :) */
|
||||||
|
len = multipart->content_length;
|
||||||
|
if (gst_adapter_available (multipart->adapter) >= len + 2) {
|
||||||
|
*datalen = len;
|
||||||
|
data = gst_adapter_peek (multipart->adapter, len + 1);
|
||||||
|
|
||||||
|
/* If data[len] contains \r then assume a newline is \r\n */
|
||||||
|
if (data[len] == '\r')
|
||||||
|
len += 2;
|
||||||
|
else if (data[len] == '\n')
|
||||||
|
len += 1;
|
||||||
|
/* Don't check if boundary is actually there, but let the header parsing
|
||||||
|
* bail out if it isn't */
|
||||||
|
return len;
|
||||||
|
} else {
|
||||||
|
/* need more data */
|
||||||
|
return MULTIPART_NEED_MORE_DATA;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
len = gst_adapter_available (multipart->adapter);
|
||||||
|
if (len == 0)
|
||||||
|
return MULTIPART_NEED_MORE_DATA;
|
||||||
|
data = gst_adapter_peek (multipart->adapter, len);
|
||||||
|
dataend = data + len;
|
||||||
|
|
||||||
|
for (pos = data + multipart->scanpos;
|
||||||
|
pos <= dataend - multipart->boundary_len - 2; pos++) {
|
||||||
|
if (*pos == '-' && pos[1] == '-' &&
|
||||||
|
!strncmp ((gchar *) pos + 2,
|
||||||
|
multipart->boundary, multipart->boundary_len)) {
|
||||||
|
/* Found the boundary! Check if there was a newline before the boundary */
|
||||||
|
len = pos - data;
|
||||||
|
if (pos - 2 > data && pos[-2] == '\r')
|
||||||
|
len -= 2;
|
||||||
|
else if (pos - 1 > data && pos[-1] == '\n')
|
||||||
|
len -= 1;
|
||||||
|
*datalen = len;
|
||||||
|
|
||||||
|
multipart->scanpos = 0;
|
||||||
|
return pos - data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
multipart->scanpos = pos - data;
|
||||||
|
return MULTIPART_NEED_MORE_DATA;
|
||||||
|
}
|
||||||
|
|
||||||
static GstFlowReturn
|
static GstFlowReturn
|
||||||
gst_multipart_demux_chain (GstPad * pad, GstBuffer * buf)
|
gst_multipart_demux_chain (GstPad * pad, GstBuffer * buf)
|
||||||
{
|
{
|
||||||
GstMultipartDemux *multipart;
|
GstMultipartDemux *multipart;
|
||||||
gint size, matchpos;
|
GstAdapter *adapter;
|
||||||
guchar *data;
|
gint size = 1;
|
||||||
GstFlowReturn ret = GST_FLOW_OK;
|
|
||||||
|
|
||||||
multipart = GST_MULTIPART_DEMUX (gst_pad_get_parent (pad));
|
multipart = GST_MULTIPART_DEMUX (gst_pad_get_parent (pad));
|
||||||
|
adapter = multipart->adapter;
|
||||||
|
|
||||||
data = GST_BUFFER_DATA (buf);
|
if (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DISCONT)) {
|
||||||
size = GST_BUFFER_SIZE (buf);
|
gst_adapter_clear (adapter);
|
||||||
|
|
||||||
/* first make sure our buffer is long enough */
|
|
||||||
if (multipart->bufsize + size > multipart->maxlen) {
|
|
||||||
gint newsize = (multipart->bufsize + size) * 2;
|
|
||||||
|
|
||||||
multipart->buffer = g_realloc (multipart->buffer, newsize);
|
|
||||||
multipart->maxlen = newsize;
|
|
||||||
}
|
}
|
||||||
/* copy bytes into the buffer */
|
gst_adapter_push (adapter, buf);
|
||||||
memcpy (multipart->buffer + multipart->bufsize, data, size);
|
|
||||||
multipart->bufsize += size;
|
|
||||||
|
|
||||||
if (multipart->first_frame && multipart->autoscan) {
|
while (gst_adapter_available (adapter) > 0) {
|
||||||
/* find the prefix if this is the first buffer */
|
GstMultipartPad *srcpad;
|
||||||
/* the prefix is like --prefix\r\n */
|
GstBuffer *outbuf;
|
||||||
size_t i, start;
|
gboolean created;
|
||||||
|
gint datalen;
|
||||||
|
|
||||||
i = 0;
|
if (G_UNLIKELY (!multipart->header_completed)) {
|
||||||
start = -1;
|
if ((size = multipart_parse_header (multipart)) < 0) {
|
||||||
|
goto nodata;
|
||||||
while ((i + 1) < multipart->bufsize) {
|
|
||||||
|
|
||||||
if (-1 == start) {
|
|
||||||
if ((multipart->buffer[i] == '-') && (multipart->buffer[i + 1] == '-')) {
|
|
||||||
start = i + 2; /* discart -- */
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
/* look for \r\n or \n\n */
|
gst_adapter_flush (adapter, size);
|
||||||
if ((multipart->buffer[i] == '\n') ||
|
multipart->header_completed = TRUE;
|
||||||
((multipart->buffer[i] == '\r') &&
|
|
||||||
(multipart->buffer[i + 1] == '\n'))) {
|
|
||||||
|
|
||||||
/* found first \r\n, the prefix is from 0 to i */
|
|
||||||
|
|
||||||
g_free (multipart->prefix);
|
|
||||||
multipart->prefix =
|
|
||||||
g_strndup (multipart->buffer + start, (i - start));
|
|
||||||
multipart->prefixLen = strlen (multipart->prefix);
|
|
||||||
GST_DEBUG_OBJECT (multipart,
|
|
||||||
"set prefix to [%s]\n", multipart->prefix);
|
|
||||||
|
|
||||||
multipart->first_frame = FALSE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
i++;
|
|
||||||
}
|
}
|
||||||
|
if ((size = multipart_find_boundary (multipart, &datalen)) < 0) {
|
||||||
}
|
goto nodata;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* find \n */
|
|
||||||
while (multipart->scanpos < multipart->bufsize) {
|
|
||||||
if (multipart->buffer[multipart->scanpos] == '\n') {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
multipart->scanpos++;
|
srcpad =
|
||||||
}
|
gst_multipart_find_pad_by_mime (multipart,
|
||||||
|
multipart->mime_type, &created);
|
||||||
|
outbuf = gst_adapter_take_buffer (adapter, datalen);
|
||||||
|
gst_adapter_flush (adapter, size - datalen);
|
||||||
|
|
||||||
/* then scan for the boundary */
|
/* Invalidate header info */
|
||||||
for (matchpos = 0;
|
multipart->header_completed = FALSE;
|
||||||
multipart->scanpos + multipart->prefixLen + MAX_LINE_LEN - matchpos <
|
multipart->content_length = -1;
|
||||||
multipart->bufsize; multipart->scanpos++) {
|
|
||||||
if (multipart->buffer[multipart->scanpos] == multipart->prefix[matchpos]) {
|
|
||||||
|
|
||||||
matchpos++;
|
gst_buffer_set_caps (outbuf, GST_PAD_CAPS (srcpad->pad));
|
||||||
if (matchpos == multipart->prefixLen) {
|
if (created) {
|
||||||
int datalen;
|
GstEvent *event;
|
||||||
int i, start;
|
|
||||||
gchar *mime_type;
|
|
||||||
|
|
||||||
multipart->scanpos++;
|
/* Push new segment, first buffer has 0 timestamp */
|
||||||
|
event = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME, 0, -1, 0);
|
||||||
start = multipart->scanpos;
|
gst_pad_push_event (srcpad->pad, event);
|
||||||
/* find \n */
|
GST_BUFFER_TIMESTAMP (outbuf) = 0;
|
||||||
for (i = 0; i < MAX_LINE_LEN; i++) {
|
|
||||||
if (multipart->buffer[multipart->scanpos] == '\n')
|
|
||||||
break;
|
|
||||||
multipart->scanpos++;
|
|
||||||
matchpos++;
|
|
||||||
}
|
|
||||||
mime_type =
|
|
||||||
g_strndup (multipart->buffer + start, multipart->scanpos - start);
|
|
||||||
multipart->scanpos += 2;
|
|
||||||
matchpos += 3;
|
|
||||||
|
|
||||||
datalen = multipart->scanpos - matchpos;
|
|
||||||
if (datalen > 0 && multipart->parsing_mime) {
|
|
||||||
GstBuffer *outbuf;
|
|
||||||
GstMultipartPad *srcpad;
|
|
||||||
gboolean created = FALSE;
|
|
||||||
|
|
||||||
srcpad =
|
|
||||||
gst_multipart_find_pad_by_mime (multipart,
|
|
||||||
multipart->parsing_mime, &created);
|
|
||||||
if (srcpad != NULL) {
|
|
||||||
ret =
|
|
||||||
gst_pad_alloc_buffer_and_set_caps (srcpad->pad,
|
|
||||||
GST_BUFFER_OFFSET_NONE, datalen, GST_PAD_CAPS (srcpad->pad),
|
|
||||||
&outbuf);
|
|
||||||
if (ret != GST_FLOW_OK) {
|
|
||||||
GST_WARNING_OBJECT (multipart, "failed allocating a %d bytes "
|
|
||||||
"buffer", datalen);
|
|
||||||
} else {
|
|
||||||
memcpy (GST_BUFFER_DATA (outbuf), multipart->buffer, datalen);
|
|
||||||
if (created) {
|
|
||||||
GstEvent *event;
|
|
||||||
|
|
||||||
/* Push new segment */
|
|
||||||
event = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME,
|
|
||||||
0, -1, 0);
|
|
||||||
if (GST_IS_EVENT (event)) {
|
|
||||||
gst_pad_push_event (srcpad->pad, event);
|
|
||||||
}
|
|
||||||
GST_BUFFER_TIMESTAMP (outbuf) = 0;
|
|
||||||
} else {
|
|
||||||
GST_BUFFER_TIMESTAMP (outbuf) = -1;
|
|
||||||
}
|
|
||||||
ret = gst_pad_push (srcpad->pad, outbuf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* move rest downward */
|
|
||||||
multipart->bufsize -= multipart->scanpos;
|
|
||||||
memmove (multipart->buffer, multipart->buffer + multipart->scanpos,
|
|
||||||
multipart->bufsize);
|
|
||||||
|
|
||||||
g_free (multipart->parsing_mime);
|
|
||||||
multipart->parsing_mime = mime_type;
|
|
||||||
multipart->scanpos = 0;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
matchpos = 0;
|
GST_BUFFER_TIMESTAMP (outbuf) = -1;
|
||||||
}
|
}
|
||||||
if (ret != GST_FLOW_OK)
|
gst_pad_push (srcpad->pad, outbuf);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gst_buffer_unref (buf);
|
nodata:
|
||||||
gst_object_unref (multipart);
|
gst_object_unref (multipart);
|
||||||
|
if (G_UNLIKELY (size == MULTIPART_DATA_ERROR))
|
||||||
return ret;
|
return GST_FLOW_ERROR;
|
||||||
|
if (G_UNLIKELY (size == MULTIPART_DATA_EOS))
|
||||||
|
return GST_FLOW_UNEXPECTED;
|
||||||
|
return GST_FLOW_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static GstStateChangeReturn
|
static GstStateChangeReturn
|
||||||
|
@ -459,23 +531,6 @@ gst_multipart_demux_change_state (GstElement * element,
|
||||||
|
|
||||||
multipart = GST_MULTIPART_DEMUX (element);
|
multipart = GST_MULTIPART_DEMUX (element);
|
||||||
|
|
||||||
switch (transition) {
|
|
||||||
case GST_STATE_CHANGE_NULL_TO_READY:
|
|
||||||
break;
|
|
||||||
case GST_STATE_CHANGE_READY_TO_PAUSED:
|
|
||||||
multipart->buffer = g_malloc (multipart->maxlen);
|
|
||||||
multipart->first_frame = TRUE;
|
|
||||||
multipart->parsing_mime = NULL;
|
|
||||||
multipart->numpads = 0;
|
|
||||||
multipart->scanpos = 0;
|
|
||||||
multipart->lastpos = 0;
|
|
||||||
break;
|
|
||||||
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
|
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
|
||||||
if (ret == GST_STATE_CHANGE_FAILURE)
|
if (ret == GST_STATE_CHANGE_FAILURE)
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -484,10 +539,12 @@ gst_multipart_demux_change_state (GstElement * element,
|
||||||
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
|
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
|
||||||
break;
|
break;
|
||||||
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
||||||
g_free (multipart->parsing_mime);
|
multipart->header_completed = FALSE;
|
||||||
multipart->parsing_mime = NULL;
|
g_free (multipart->boundary);
|
||||||
g_free (multipart->buffer);
|
multipart->boundary = NULL;
|
||||||
multipart->buffer = NULL;
|
g_free (multipart->mime_type);
|
||||||
|
multipart->mime_type = NULL;
|
||||||
|
gst_adapter_clear (multipart->adapter);
|
||||||
break;
|
break;
|
||||||
case GST_STATE_CHANGE_READY_TO_NULL:
|
case GST_STATE_CHANGE_READY_TO_NULL:
|
||||||
break;
|
break;
|
||||||
|
@ -509,9 +566,12 @@ gst_multipart_set_property (GObject * object, guint prop_id,
|
||||||
|
|
||||||
switch (prop_id) {
|
switch (prop_id) {
|
||||||
case PROP_BOUNDARY:
|
case PROP_BOUNDARY:
|
||||||
g_free (filter->prefix);
|
/* Not really that usefull anymore as we can reliably autoscan */
|
||||||
filter->prefix = g_value_dup_string (value);
|
g_free (filter->boundary);
|
||||||
filter->prefixLen = strlen (filter->prefix);
|
filter->boundary = g_value_dup_string (value);
|
||||||
|
if (filter->boundary != NULL) {
|
||||||
|
filter->boundary_len = strlen (filter->boundary);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case PROP_AUTOSCAN:
|
case PROP_AUTOSCAN:
|
||||||
filter->autoscan = g_value_get_boolean (value);
|
filter->autoscan = g_value_get_boolean (value);
|
||||||
|
@ -533,7 +593,7 @@ gst_multipart_get_property (GObject * object, guint prop_id,
|
||||||
|
|
||||||
switch (prop_id) {
|
switch (prop_id) {
|
||||||
case PROP_BOUNDARY:
|
case PROP_BOUNDARY:
|
||||||
g_value_set_string (value, filter->prefix);
|
g_value_set_string (value, filter->boundary);
|
||||||
break;
|
break;
|
||||||
case PROP_AUTOSCAN:
|
case PROP_AUTOSCAN:
|
||||||
g_value_set_boolean (value, filter->autoscan);
|
g_value_set_boolean (value, filter->autoscan);
|
||||||
|
|
|
@ -445,6 +445,8 @@ gst_multipart_mux_collected (GstCollectPads * pads, GstMultipartMux * mux)
|
||||||
size_t newlen, headerlen;
|
size_t newlen, headerlen;
|
||||||
GstBuffer *newbuf = NULL;
|
GstBuffer *newbuf = NULL;
|
||||||
GstStructure *structure = NULL;
|
GstStructure *structure = NULL;
|
||||||
|
guint8 *data;
|
||||||
|
guint datasize;
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (mux, "all pads are collected");
|
GST_DEBUG_OBJECT (mux, "all pads are collected");
|
||||||
|
|
||||||
|
@ -483,11 +485,14 @@ gst_multipart_mux_collected (GstCollectPads * pads, GstMultipartMux * mux)
|
||||||
goto beach;
|
goto beach;
|
||||||
}
|
}
|
||||||
|
|
||||||
header = g_strdup_printf ("\n--%s\nContent-type: %s\n\n",
|
header = g_strdup_printf ("--%s\nContent-type: %s\n\n",
|
||||||
mux->boundary, gst_structure_get_name (structure));
|
mux->boundary, gst_structure_get_name (structure));
|
||||||
|
|
||||||
|
datasize = GST_BUFFER_SIZE (best->buffer);
|
||||||
|
|
||||||
headerlen = strlen (header);
|
headerlen = strlen (header);
|
||||||
newlen = headerlen + GST_BUFFER_SIZE (best->buffer);
|
/* header, data, trailing \n */
|
||||||
|
newlen = headerlen + datasize + 1;
|
||||||
|
|
||||||
ret =
|
ret =
|
||||||
gst_pad_alloc_buffer_and_set_caps (mux->srcpad, GST_BUFFER_OFFSET_NONE,
|
gst_pad_alloc_buffer_and_set_caps (mux->srcpad, GST_BUFFER_OFFSET_NONE,
|
||||||
|
@ -498,9 +503,13 @@ gst_multipart_mux_collected (GstCollectPads * pads, GstMultipartMux * mux)
|
||||||
goto beach;
|
goto beach;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy (GST_BUFFER_DATA (newbuf), header, headerlen);
|
data = GST_BUFFER_DATA (newbuf);
|
||||||
memcpy (GST_BUFFER_DATA (newbuf) + headerlen,
|
|
||||||
GST_BUFFER_DATA (best->buffer), GST_BUFFER_SIZE (best->buffer));
|
memcpy (data, header, headerlen);
|
||||||
|
memcpy (data + headerlen, GST_BUFFER_DATA (best->buffer), datasize);
|
||||||
|
|
||||||
|
/* trailing \n */
|
||||||
|
data[headerlen + datasize] = '\n';
|
||||||
|
|
||||||
gst_buffer_stamp (newbuf, best->buffer);
|
gst_buffer_stamp (newbuf, best->buffer);
|
||||||
GST_BUFFER_OFFSET (newbuf) = mux->offset;
|
GST_BUFFER_OFFSET (newbuf) = mux->offset;
|
||||||
|
|
Loading…
Reference in a new issue