mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-30 05:31:15 +00:00
gst/tcp/: 0.8 backporting.
Original commit message from CVS: * gst/tcp/gstfdset.c: (gst_fdset_free): * gst/tcp/gstmultifdsink.c: (gst_multifdsink_init), (gst_multifdsink_add), (gst_multifdsink_remove), (gst_multifdsink_clear), (gst_multifdsink_get_stats), (gst_multifdsink_remove_client_link), (gst_multifdsink_client_queue_data), (gst_multifdsink_client_queue_caps), (gst_multifdsink_client_queue_buffer), (gst_multifdsink_queue_buffer), (gst_multifdsink_handle_clients), (gst_multifdsink_stop): * gst/tcp/gstmultifdsink.h: 0.8 backporting. * sys/ximage/ximagesink.c: (gst_ximagesink_show_frame): Also draw image when not from a pool.
This commit is contained in:
parent
3e81aabeea
commit
66b4961d7d
5 changed files with 73 additions and 36 deletions
18
ChangeLog
18
ChangeLog
|
@ -1,3 +1,21 @@
|
||||||
|
2005-07-14 Wim Taymans <wim@fluendo.com>
|
||||||
|
|
||||||
|
* gst/tcp/gstfdset.c: (gst_fdset_free):
|
||||||
|
* gst/tcp/gstmultifdsink.c: (gst_multifdsink_init),
|
||||||
|
(gst_multifdsink_add), (gst_multifdsink_remove),
|
||||||
|
(gst_multifdsink_clear), (gst_multifdsink_get_stats),
|
||||||
|
(gst_multifdsink_remove_client_link),
|
||||||
|
(gst_multifdsink_client_queue_data),
|
||||||
|
(gst_multifdsink_client_queue_caps),
|
||||||
|
(gst_multifdsink_client_queue_buffer),
|
||||||
|
(gst_multifdsink_queue_buffer), (gst_multifdsink_handle_clients),
|
||||||
|
(gst_multifdsink_stop):
|
||||||
|
* gst/tcp/gstmultifdsink.h:
|
||||||
|
0.8 backporting.
|
||||||
|
|
||||||
|
* sys/ximage/ximagesink.c: (gst_ximagesink_show_frame):
|
||||||
|
Also draw image when not from a pool.
|
||||||
|
|
||||||
2005-07-14 Wim Taymans <wim@fluendo.com>
|
2005-07-14 Wim Taymans <wim@fluendo.com>
|
||||||
|
|
||||||
* gst/playback/gstplaybasebin.c: (check_queue), (probe_triggered),
|
* gst/playback/gstplaybasebin.c: (check_queue), (probe_triggered),
|
||||||
|
|
|
@ -152,6 +152,7 @@ gst_fdset_free (GstFDSet * set)
|
||||||
case GST_FDSET_MODE_SELECT:
|
case GST_FDSET_MODE_SELECT:
|
||||||
break;
|
break;
|
||||||
case GST_FDSET_MODE_POLL:
|
case GST_FDSET_MODE_POLL:
|
||||||
|
g_free (set->testpollfds);
|
||||||
g_free (set->pollfds);
|
g_free (set->pollfds);
|
||||||
g_mutex_free (set->poll_lock);
|
g_mutex_free (set->poll_lock);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -409,7 +409,7 @@ gst_multifdsink_init (GstMultiFdSink * this)
|
||||||
this->protocol = DEFAULT_PROTOCOL;
|
this->protocol = DEFAULT_PROTOCOL;
|
||||||
this->mode = DEFAULT_MODE;
|
this->mode = DEFAULT_MODE;
|
||||||
|
|
||||||
this->clientslock = g_mutex_new ();
|
CLIENTS_LOCK_INIT (this);
|
||||||
this->clients = NULL;
|
this->clients = NULL;
|
||||||
this->fd_hash = g_hash_table_new (g_int_hash, g_int_equal);
|
this->fd_hash = g_hash_table_new (g_int_hash, g_int_equal);
|
||||||
|
|
||||||
|
@ -453,13 +453,13 @@ gst_multifdsink_add (GstMultiFdSink * sink, int fd)
|
||||||
/* send last activity time to connect time */
|
/* send last activity time to connect time */
|
||||||
client->last_activity_time = GST_TIMEVAL_TO_TIME (now);
|
client->last_activity_time = GST_TIMEVAL_TO_TIME (now);
|
||||||
|
|
||||||
g_mutex_lock (sink->clientslock);
|
CLIENTS_LOCK (sink);
|
||||||
|
|
||||||
/* check the hash to find a duplicate fd */
|
/* check the hash to find a duplicate fd */
|
||||||
clink = g_hash_table_lookup (sink->fd_hash, &client->fd.fd);
|
clink = g_hash_table_lookup (sink->fd_hash, &client->fd.fd);
|
||||||
if (clink != NULL) {
|
if (clink != NULL) {
|
||||||
client->status = GST_CLIENT_STATUS_DUPLICATE;
|
client->status = GST_CLIENT_STATUS_DUPLICATE;
|
||||||
g_mutex_unlock (sink->clientslock);
|
CLIENTS_UNLOCK (sink);
|
||||||
GST_WARNING_OBJECT (sink, "[fd %5d] duplicate client found, refusing", fd);
|
GST_WARNING_OBJECT (sink, "[fd %5d] duplicate client found, refusing", fd);
|
||||||
g_signal_emit (G_OBJECT (sink),
|
g_signal_emit (G_OBJECT (sink),
|
||||||
gst_multifdsink_signals[SIGNAL_CLIENT_REMOVED], 0, fd, client->status);
|
gst_multifdsink_signals[SIGNAL_CLIENT_REMOVED], 0, fd, client->status);
|
||||||
|
@ -489,7 +489,7 @@ gst_multifdsink_add (GstMultiFdSink * sink, int fd)
|
||||||
|
|
||||||
SEND_COMMAND (sink, CONTROL_RESTART);
|
SEND_COMMAND (sink, CONTROL_RESTART);
|
||||||
|
|
||||||
g_mutex_unlock (sink->clientslock);
|
CLIENTS_UNLOCK (sink);
|
||||||
|
|
||||||
g_signal_emit (G_OBJECT (sink),
|
g_signal_emit (G_OBJECT (sink),
|
||||||
gst_multifdsink_signals[SIGNAL_CLIENT_ADDED], 0, fd);
|
gst_multifdsink_signals[SIGNAL_CLIENT_ADDED], 0, fd);
|
||||||
|
@ -502,7 +502,7 @@ gst_multifdsink_remove (GstMultiFdSink * sink, int fd)
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (sink, "[fd %5d] removing client", fd);
|
GST_DEBUG_OBJECT (sink, "[fd %5d] removing client", fd);
|
||||||
|
|
||||||
g_mutex_lock (sink->clientslock);
|
CLIENTS_LOCK (sink);
|
||||||
clink = g_hash_table_lookup (sink->fd_hash, &fd);
|
clink = g_hash_table_lookup (sink->fd_hash, &fd);
|
||||||
if (clink != NULL) {
|
if (clink != NULL) {
|
||||||
GstTCPClient *client = (GstTCPClient *) clink->data;
|
GstTCPClient *client = (GstTCPClient *) clink->data;
|
||||||
|
@ -513,7 +513,7 @@ gst_multifdsink_remove (GstMultiFdSink * sink, int fd)
|
||||||
} else {
|
} else {
|
||||||
GST_WARNING_OBJECT (sink, "[fd %5d] no client with this fd found!", fd);
|
GST_WARNING_OBJECT (sink, "[fd %5d] no client with this fd found!", fd);
|
||||||
}
|
}
|
||||||
g_mutex_unlock (sink->clientslock);
|
CLIENTS_UNLOCK (sink);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -523,7 +523,7 @@ gst_multifdsink_clear (GstMultiFdSink * sink)
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (sink, "clearing all clients");
|
GST_DEBUG_OBJECT (sink, "clearing all clients");
|
||||||
|
|
||||||
g_mutex_lock (sink->clientslock);
|
CLIENTS_LOCK (sink);
|
||||||
for (clients = sink->clients; clients; clients = next) {
|
for (clients = sink->clients; clients; clients = next) {
|
||||||
GstTCPClient *client;
|
GstTCPClient *client;
|
||||||
|
|
||||||
|
@ -534,7 +534,7 @@ gst_multifdsink_clear (GstMultiFdSink * sink)
|
||||||
gst_multifdsink_remove_client_link (sink, clients);
|
gst_multifdsink_remove_client_link (sink, clients);
|
||||||
}
|
}
|
||||||
SEND_COMMAND (sink, CONTROL_RESTART);
|
SEND_COMMAND (sink, CONTROL_RESTART);
|
||||||
g_mutex_unlock (sink->clientslock);
|
CLIENTS_UNLOCK (sink);
|
||||||
}
|
}
|
||||||
|
|
||||||
GValueArray *
|
GValueArray *
|
||||||
|
@ -544,7 +544,7 @@ gst_multifdsink_get_stats (GstMultiFdSink * sink, int fd)
|
||||||
GValueArray *result = NULL;
|
GValueArray *result = NULL;
|
||||||
GList *clink;
|
GList *clink;
|
||||||
|
|
||||||
g_mutex_lock (sink->clientslock);
|
CLIENTS_LOCK (sink);
|
||||||
clink = g_hash_table_lookup (sink->fd_hash, &fd);
|
clink = g_hash_table_lookup (sink->fd_hash, &fd);
|
||||||
client = (GstTCPClient *) clink->data;
|
client = (GstTCPClient *) clink->data;
|
||||||
if (client != NULL) {
|
if (client != NULL) {
|
||||||
|
@ -582,7 +582,7 @@ gst_multifdsink_get_stats (GstMultiFdSink * sink, int fd)
|
||||||
g_value_set_uint64 (&value, client->last_activity_time);
|
g_value_set_uint64 (&value, client->last_activity_time);
|
||||||
result = g_value_array_append (result, &value);
|
result = g_value_array_append (result, &value);
|
||||||
}
|
}
|
||||||
g_mutex_unlock (sink->clientslock);
|
CLIENTS_UNLOCK (sink);
|
||||||
|
|
||||||
/* python doesn't like a NULL pointer yet */
|
/* python doesn't like a NULL pointer yet */
|
||||||
if (result == NULL) {
|
if (result == NULL) {
|
||||||
|
@ -650,19 +650,24 @@ gst_multifdsink_remove_client_link (GstMultiFdSink * sink, GList * link)
|
||||||
|
|
||||||
/* unlock the mutex before signaling because the signal handler
|
/* unlock the mutex before signaling because the signal handler
|
||||||
* might query some properties */
|
* might query some properties */
|
||||||
g_mutex_unlock (sink->clientslock);
|
CLIENTS_UNLOCK (sink);
|
||||||
|
|
||||||
g_signal_emit (G_OBJECT (sink),
|
g_signal_emit (G_OBJECT (sink),
|
||||||
gst_multifdsink_signals[SIGNAL_CLIENT_REMOVED], 0, fd, client->status);
|
gst_multifdsink_signals[SIGNAL_CLIENT_REMOVED], 0, fd, client->status);
|
||||||
|
|
||||||
/* lock again before we remove the client completely */
|
/* lock again before we remove the client completely */
|
||||||
g_mutex_lock (sink->clientslock);
|
CLIENTS_LOCK (sink);
|
||||||
|
|
||||||
if (!g_hash_table_remove (sink->fd_hash, &client->fd.fd)) {
|
if (!g_hash_table_remove (sink->fd_hash, &client->fd.fd)) {
|
||||||
GST_WARNING_OBJECT (sink,
|
GST_WARNING_OBJECT (sink,
|
||||||
"[fd %5d] error removing client %p from hash", client->fd.fd, client);
|
"[fd %5d] error removing client %p from hash", client->fd.fd, client);
|
||||||
}
|
}
|
||||||
sink->clients = g_list_delete_link (sink->clients, link);
|
/* after releasing the lock above, the link could be invalid, more
|
||||||
|
* precisely, the next and prev pointers could point to invalid list
|
||||||
|
* links. One optimisation could be to add a cookie to the linked list
|
||||||
|
* and take a shortcut when it did not change between unlocking and locking
|
||||||
|
* our mutex. For now we just walk the list again. */
|
||||||
|
sink->clients = g_list_remove (sink->clients, client);
|
||||||
|
|
||||||
if (fclass->removed)
|
if (fclass->removed)
|
||||||
fclass->removed (sink, client->fd.fd);
|
fclass->removed (sink, client->fd.fd);
|
||||||
|
@ -740,12 +745,12 @@ gst_multifdsink_handle_client_read (GstMultiFdSink * sink,
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
gst_multifdsink_client_queue_data (GstMultiFdSink * sink, GstTCPClient * client,
|
gst_multifdsink_client_queue_data (GstMultiFdSink * sink, GstTCPClient * client,
|
||||||
guint8 * data, gint len)
|
gchar * data, gint len)
|
||||||
{
|
{
|
||||||
GstBuffer *buf;
|
GstBuffer *buf;
|
||||||
|
|
||||||
buf = gst_buffer_new ();
|
buf = gst_buffer_new ();
|
||||||
GST_BUFFER_DATA (buf) = data;
|
GST_BUFFER_DATA (buf) = (guint8 *) data;
|
||||||
GST_BUFFER_SIZE (buf) = len;
|
GST_BUFFER_SIZE (buf) = len;
|
||||||
|
|
||||||
GST_LOG_OBJECT (sink, "[fd %5d] queueing data of length %d",
|
GST_LOG_OBJECT (sink, "[fd %5d] queueing data of length %d",
|
||||||
|
@ -774,10 +779,10 @@ gst_multifdsink_client_queue_caps (GstMultiFdSink * sink, GstTCPClient * client,
|
||||||
GST_DEBUG_OBJECT (sink, "Could not create GDP packet from caps");
|
GST_DEBUG_OBJECT (sink, "Could not create GDP packet from caps");
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
gst_multifdsink_client_queue_data (sink, client, header, length);
|
gst_multifdsink_client_queue_data (sink, client, (gchar *) header, length);
|
||||||
|
|
||||||
length = gst_dp_header_payload_length (header);
|
length = gst_dp_header_payload_length (header);
|
||||||
gst_multifdsink_client_queue_data (sink, client, payload, length);
|
gst_multifdsink_client_queue_data (sink, client, (gchar *) payload, length);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
@ -806,7 +811,7 @@ gst_multifdsink_client_queue_buffer (GstMultiFdSink * sink,
|
||||||
"[fd %5d] could not create header, removing client", client->fd.fd);
|
"[fd %5d] could not create header, removing client", client->fd.fd);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
gst_multifdsink_client_queue_data (sink, client, header, len);
|
gst_multifdsink_client_queue_data (sink, client, (gchar *) header, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
GST_LOG_OBJECT (sink, "[fd %5d] queueing buffer of length %d",
|
GST_LOG_OBJECT (sink, "[fd %5d] queueing buffer of length %d",
|
||||||
|
@ -1171,7 +1176,7 @@ gst_multifdsink_queue_buffer (GstMultiFdSink * sink, GstBuffer * buf)
|
||||||
g_get_current_time (&nowtv);
|
g_get_current_time (&nowtv);
|
||||||
now = GST_TIMEVAL_TO_TIME (nowtv);
|
now = GST_TIMEVAL_TO_TIME (nowtv);
|
||||||
|
|
||||||
g_mutex_lock (sink->clientslock);
|
CLIENTS_LOCK (sink);
|
||||||
/* add buffer to queue */
|
/* add buffer to queue */
|
||||||
g_array_prepend_val (sink->bufqueue, buf);
|
g_array_prepend_val (sink->bufqueue, buf);
|
||||||
queuelen = sink->bufqueue->len;
|
queuelen = sink->bufqueue->len;
|
||||||
|
@ -1271,7 +1276,7 @@ gst_multifdsink_queue_buffer (GstMultiFdSink * sink, GstBuffer * buf)
|
||||||
}
|
}
|
||||||
/* save for stats */
|
/* save for stats */
|
||||||
sink->buffers_queued = max_buffer_usage;
|
sink->buffers_queued = max_buffer_usage;
|
||||||
g_mutex_unlock (sink->clientslock);
|
CLIENTS_UNLOCK (sink);
|
||||||
|
|
||||||
/* and send a signal to thread if fd_set changed */
|
/* and send a signal to thread if fd_set changed */
|
||||||
if (need_signal) {
|
if (need_signal) {
|
||||||
|
@ -1316,7 +1321,7 @@ gst_multifdsink_handle_clients (GstMultiFdSink * sink)
|
||||||
if (errno == EBADF) {
|
if (errno == EBADF) {
|
||||||
/* ok, so one or more of the fds is invalid. We loop over them to find
|
/* ok, so one or more of the fds is invalid. We loop over them to find
|
||||||
* the ones that give an error to the F_GETFL fcntl. */
|
* the ones that give an error to the F_GETFL fcntl. */
|
||||||
g_mutex_lock (sink->clientslock);
|
CLIENTS_LOCK (sink);
|
||||||
for (clients = sink->clients; clients; clients = next) {
|
for (clients = sink->clients; clients; clients = next) {
|
||||||
GstTCPClient *client;
|
GstTCPClient *client;
|
||||||
int fd;
|
int fd;
|
||||||
|
@ -1338,7 +1343,7 @@ gst_multifdsink_handle_clients (GstMultiFdSink * sink)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
g_mutex_unlock (sink->clientslock);
|
CLIENTS_UNLOCK (sink);
|
||||||
/* after this, go back in the select loop as the read/writefds
|
/* after this, go back in the select loop as the read/writefds
|
||||||
* are not valid */
|
* are not valid */
|
||||||
try_again = TRUE;
|
try_again = TRUE;
|
||||||
|
@ -1400,7 +1405,7 @@ gst_multifdsink_handle_clients (GstMultiFdSink * sink)
|
||||||
fclass->wait (sink, sink->fdset);
|
fclass->wait (sink, sink->fdset);
|
||||||
|
|
||||||
/* Check the clients */
|
/* Check the clients */
|
||||||
g_mutex_lock (sink->clientslock);
|
CLIENTS_LOCK (sink);
|
||||||
for (clients = sink->clients; clients; clients = next) {
|
for (clients = sink->clients; clients; clients = next) {
|
||||||
GstTCPClient *client;
|
GstTCPClient *client;
|
||||||
|
|
||||||
|
@ -1438,7 +1443,7 @@ gst_multifdsink_handle_clients (GstMultiFdSink * sink)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
g_mutex_unlock (sink->clientslock);
|
CLIENTS_UNLOCK (sink);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* we handle the client communication in another thread so that we do not block
|
/* we handle the client communication in another thread so that we do not block
|
||||||
|
@ -1717,6 +1722,8 @@ gst_multifdsink_stop (GstBaseSink * bsink)
|
||||||
this->fdset = NULL;
|
this->fdset = NULL;
|
||||||
}
|
}
|
||||||
GST_FLAG_UNSET (this, GST_MULTIFDSINK_OPEN);
|
GST_FLAG_UNSET (this, GST_MULTIFDSINK_OPEN);
|
||||||
|
CLIENTS_LOCK_FREE (this);
|
||||||
|
g_hash_table_destroy (this->fd_hash);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
|
@ -116,13 +116,18 @@ typedef struct {
|
||||||
|
|
||||||
} GstTCPClient;
|
} GstTCPClient;
|
||||||
|
|
||||||
|
#define CLIENTS_LOCK_INIT(fdsink) (g_static_rec_mutex_init(&fdsink->clientslock))
|
||||||
|
#define CLIENTS_LOCK_FREE(fdsink) (g_static_rec_mutex_free(&fdsink->clientslock))
|
||||||
|
#define CLIENTS_LOCK(fdsink) (g_static_rec_mutex_lock(&fdsink->clientslock))
|
||||||
|
#define CLIENTS_UNLOCK(fdsink) (g_static_rec_mutex_unlock(&fdsink->clientslock))
|
||||||
|
|
||||||
struct _GstMultiFdSink {
|
struct _GstMultiFdSink {
|
||||||
GstBaseSink element;
|
GstBaseSink element;
|
||||||
|
|
||||||
guint64 bytes_to_serve; /* how much bytes we must serve */
|
guint64 bytes_to_serve; /* how much bytes we must serve */
|
||||||
guint64 bytes_served; /* how much bytes have we served */
|
guint64 bytes_served; /* how much bytes have we served */
|
||||||
|
|
||||||
GMutex *clientslock; /* lock to protect the clients list */
|
GStaticRecMutex clientslock; /* lock to protect the clients list */
|
||||||
GList *clients; /* list of clients we are serving */
|
GList *clients; /* list of clients we are serving */
|
||||||
GHashTable *fd_hash; /* index on fd to client */
|
GHashTable *fd_hash; /* index on fd to client */
|
||||||
|
|
||||||
|
|
|
@ -1234,19 +1234,14 @@ gst_ximagesink_show_frame (GstBaseSink * bsink, GstBuffer * buf)
|
||||||
ximagesink->ximage = gst_ximagesink_ximage_new (ximagesink,
|
ximagesink->ximage = gst_ximagesink_ximage_new (ximagesink,
|
||||||
GST_VIDEO_SINK_WIDTH (ximagesink),
|
GST_VIDEO_SINK_WIDTH (ximagesink),
|
||||||
GST_VIDEO_SINK_HEIGHT (ximagesink));
|
GST_VIDEO_SINK_HEIGHT (ximagesink));
|
||||||
if (!ximagesink->ximage) {
|
if (!ximagesink->ximage)
|
||||||
/* No image available. That's very bad ! */
|
goto no_ximage;
|
||||||
gst_buffer_unref (buf);
|
|
||||||
GST_ELEMENT_ERROR (ximagesink, CORE, NEGOTIATION, (NULL),
|
|
||||||
("Failed creating an XImage in ximagesink chain function."));
|
|
||||||
return GST_FLOW_ERROR;
|
|
||||||
}
|
}
|
||||||
memcpy (ximagesink->ximage->ximage->data,
|
memcpy (ximagesink->ximage->ximage->data,
|
||||||
GST_BUFFER_DATA (buf),
|
GST_BUFFER_DATA (buf),
|
||||||
MIN (GST_BUFFER_SIZE (buf), ximagesink->ximage->size));
|
MIN (GST_BUFFER_SIZE (buf), ximagesink->ximage->size));
|
||||||
gst_ximagesink_ximage_put (ximagesink, ximagesink->ximage);
|
gst_ximagesink_ximage_put (ximagesink, ximagesink->ximage);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
gst_ximagesink_handle_xevents (ximagesink);
|
gst_ximagesink_handle_xevents (ximagesink);
|
||||||
#if 0
|
#if 0
|
||||||
|
@ -1256,6 +1251,17 @@ gst_ximagesink_show_frame (GstBaseSink * bsink, GstBuffer * buf)
|
||||||
g_mutex_unlock (ximagesink->stream_lock);
|
g_mutex_unlock (ximagesink->stream_lock);
|
||||||
|
|
||||||
return GST_FLOW_OK;
|
return GST_FLOW_OK;
|
||||||
|
|
||||||
|
/* ERRORS */
|
||||||
|
no_ximage:
|
||||||
|
{
|
||||||
|
/* No image available. That's very bad ! */
|
||||||
|
g_mutex_unlock (ximagesink->stream_lock);
|
||||||
|
gst_buffer_unref (buf);
|
||||||
|
GST_ELEMENT_ERROR (ximagesink, CORE, NEGOTIATION, (NULL),
|
||||||
|
("Failed creating an XImage in ximagesink chain function."));
|
||||||
|
return GST_FLOW_ERROR;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Buffer management */
|
/* Buffer management */
|
||||||
|
|
Loading…
Reference in a new issue