mirror of https://github.com/neutrinolabs/xrdp
Merge pull request #2686 from matt335672/update_fifo_code
Re-work FIFO code
This commit is contained in:
commit
dab1443e87
334
common/fifo.c
334
common/fifo.c
|
@ -1,7 +1,7 @@
|
|||
/**
|
||||
* xrdp: A Remote Desktop Protocol server.
|
||||
*
|
||||
* Copyright (C) Laxmikant Rashinkar 2004-2014
|
||||
* Copyright (C) Matt Burt 2023
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -14,174 +14,250 @@
|
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* FIFO implementation to store pointer to data struct
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file common/fifo.c
|
||||
* @brief Fifo for storing generic pointers
|
||||
*
|
||||
* Defines an unbounded FIFO-queue for void * pointers
|
||||
*
|
||||
* The stored pointers are called 'items' below.
|
||||
*
|
||||
* Items are stored in groups called 'chunks'. Chunks are linked together
|
||||
* in a chain:-
|
||||
*
|
||||
* +-------------+ +--------+ +--------+ +--------+
|
||||
* | first_chunk |--->| next |--->| next |--->| NULL |<-+
|
||||
* | last_chunk |-+ +--------+ +--------+ +--------+ |
|
||||
* | . . . | | | item.0 | | item.0 | | item.0 | |
|
||||
* +-------------+ | | ... | | ... | | ... | |
|
||||
* | | item.n | | item.n | | item.n | |
|
||||
* | +--------+ +--------+ +--------+ |
|
||||
* | |
|
||||
* +------------------------------------------+
|
||||
*
|
||||
* This allows items to be added to the FIFO by allocating blocks
|
||||
* as each one fills up.
|
||||
*
|
||||
* The code to read from the FIFO de-allocates blocks as each one is
|
||||
* consumed.
|
||||
*
|
||||
* There is always at least one chunk in the FIFO.
|
||||
*/
|
||||
|
||||
|
||||
#if defined(HAVE_CONFIG_H)
|
||||
#include <config_ac.h>
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "fifo.h"
|
||||
#include "os_calls.h"
|
||||
|
||||
/**
|
||||
* Create new fifo data struct
|
||||
*
|
||||
* @return pointer to new FIFO or NULL if system out of memory
|
||||
*****************************************************************************/
|
||||
#define ITEMS_PER_CHUNK 31
|
||||
|
||||
FIFO *
|
||||
fifo_create(void)
|
||||
struct chunk
|
||||
{
|
||||
return (FIFO *) g_malloc(sizeof(FIFO), 1);
|
||||
struct chunk *next;
|
||||
void *items[ITEMS_PER_CHUNK];
|
||||
};
|
||||
|
||||
struct fifo
|
||||
{
|
||||
struct chunk *first_chunk;
|
||||
struct chunk *last_chunk;
|
||||
/** Next address to write in 'last_chunk' */
|
||||
unsigned short writer;
|
||||
/** Next address to read in 'first_chunk' */
|
||||
unsigned short reader;
|
||||
/** Item destructor function, or NULL */
|
||||
fifo_item_destructor item_destructor;
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
struct fifo *
|
||||
fifo_create(fifo_item_destructor item_destructor)
|
||||
{
|
||||
struct fifo *result = NULL;
|
||||
struct chunk *cptr = (struct chunk *)malloc(sizeof(struct chunk));
|
||||
if (cptr != NULL)
|
||||
{
|
||||
/* 'next' pointer in last block is always NULL */
|
||||
cptr->next = NULL;
|
||||
result = (struct fifo *)malloc(sizeof(struct fifo));
|
||||
if (result == NULL)
|
||||
{
|
||||
free(cptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
result->first_chunk = cptr;
|
||||
result->last_chunk = cptr;
|
||||
result->writer = 0;
|
||||
result->reader = 0;
|
||||
result->item_destructor = item_destructor;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/**
|
||||
* Delete specified FIFO
|
||||
*****************************************************************************/
|
||||
* Internal function to call the destructor function on all items in the fifo
|
||||
*
|
||||
* @param self fifo. Can't be NULL
|
||||
* @param closure Additional argument to destructor function
|
||||
*/
|
||||
static void
|
||||
call_item_destructor(struct fifo *self, void *closure)
|
||||
{
|
||||
if (self->item_destructor != NULL)
|
||||
{
|
||||
struct chunk *cptr = self->first_chunk;
|
||||
unsigned int i = self->reader;
|
||||
|
||||
// Process all the chunks up to the last one
|
||||
while (cptr != self->last_chunk)
|
||||
{
|
||||
(*self->item_destructor)(cptr->items[i++], closure);
|
||||
if (i == ITEMS_PER_CHUNK)
|
||||
{
|
||||
cptr = cptr->next;
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Process all the items in the last chunk
|
||||
while (i < self->writer)
|
||||
{
|
||||
(*self->item_destructor)(cptr->items[i++], closure);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
void
|
||||
fifo_delete(FIFO *self)
|
||||
fifo_delete(struct fifo *self, void *closure)
|
||||
{
|
||||
USER_DATA *udp;
|
||||
|
||||
if (!self)
|
||||
if (self != NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
call_item_destructor(self, closure);
|
||||
|
||||
if (!self->head)
|
||||
{
|
||||
/* FIFO is empty */
|
||||
g_free(self);
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->head == self->tail)
|
||||
{
|
||||
/* only one item in FIFO */
|
||||
if (self->auto_free)
|
||||
// Now free all the chunks
|
||||
struct chunk *cptr = self->first_chunk;
|
||||
while (cptr != NULL)
|
||||
{
|
||||
g_free(self->head->item);
|
||||
struct chunk *next = cptr->next;
|
||||
free(cptr);
|
||||
cptr = next;
|
||||
}
|
||||
|
||||
g_free(self->head);
|
||||
g_free(self);
|
||||
return;
|
||||
free(self);
|
||||
}
|
||||
|
||||
/* more then one item in FIFO */
|
||||
while (self->head)
|
||||
{
|
||||
udp = self->head;
|
||||
|
||||
if (self->auto_free)
|
||||
{
|
||||
g_free(udp->item);
|
||||
}
|
||||
|
||||
self->head = udp->next;
|
||||
g_free(udp);
|
||||
}
|
||||
|
||||
g_free(self);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an item to the specified FIFO
|
||||
*
|
||||
* @param self FIFO to operate on
|
||||
* @param item item to add to specified FIFO
|
||||
*
|
||||
* @return 0 on success, -1 on error
|
||||
*****************************************************************************/
|
||||
|
||||
/*****************************************************************************/
|
||||
void
|
||||
fifo_clear(struct fifo *self, void *closure)
|
||||
{
|
||||
if (self != NULL)
|
||||
{
|
||||
call_item_destructor(self, closure);
|
||||
|
||||
// Now free all the chunks except the last one
|
||||
struct chunk *cptr = self->first_chunk;
|
||||
while (cptr->next != NULL)
|
||||
{
|
||||
struct chunk *next = cptr->next;
|
||||
free(cptr);
|
||||
cptr = next;
|
||||
}
|
||||
|
||||
// Re-initialise fifo fields
|
||||
self->first_chunk = cptr;
|
||||
self->last_chunk = cptr;
|
||||
self->reader = 0;
|
||||
self->writer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
int
|
||||
fifo_add_item(FIFO *self, void *item)
|
||||
fifo_add_item(struct fifo *self, void *item)
|
||||
{
|
||||
USER_DATA *udp;
|
||||
|
||||
if (!self || !item)
|
||||
int rv = 0;
|
||||
if (self != NULL && item != NULL)
|
||||
{
|
||||
return -1;
|
||||
if (self->writer == ITEMS_PER_CHUNK)
|
||||
{
|
||||
// Add another chunk to the chain
|
||||
struct chunk *cptr;
|
||||
cptr = (struct chunk *)malloc(sizeof(struct chunk));
|
||||
if (cptr == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
cptr->next = NULL;
|
||||
self->last_chunk->next = cptr;
|
||||
self->last_chunk = cptr;
|
||||
self->writer = 0;
|
||||
}
|
||||
|
||||
self->last_chunk->items[self->writer++] = item;
|
||||
rv = 1;
|
||||
}
|
||||
|
||||
if ((udp = (USER_DATA *) g_malloc(sizeof(USER_DATA), 0)) == 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
udp->item = item;
|
||||
udp->next = 0;
|
||||
|
||||
/* if fifo is empty, add to head */
|
||||
if (!self->head)
|
||||
{
|
||||
self->head = udp;
|
||||
self->tail = udp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* add to tail */
|
||||
self->tail->next = udp;
|
||||
self->tail = udp;
|
||||
|
||||
return 0;
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an item from top of FIFO
|
||||
*
|
||||
* @param self FIFO to operate on
|
||||
*
|
||||
* @return top item from FIFO or NULL if FIFO is empty
|
||||
*****************************************************************************/
|
||||
|
||||
/*****************************************************************************/
|
||||
void *
|
||||
fifo_remove_item(FIFO *self)
|
||||
fifo_remove_item(struct fifo *self)
|
||||
{
|
||||
void *item;
|
||||
USER_DATA *udp;
|
||||
|
||||
if (!self || !self->head)
|
||||
void *item = NULL;
|
||||
if (self != NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
// More than one chunk in the fifo?
|
||||
if (self->first_chunk != self->last_chunk)
|
||||
{
|
||||
/* We're not reading the last chunk. There
|
||||
* must be something in the fifo */
|
||||
item = self->first_chunk->items[self->reader++];
|
||||
|
||||
if (self->head == self->tail)
|
||||
{
|
||||
/* only one item in FIFO */
|
||||
item = self->head->item;
|
||||
g_free(self->head);
|
||||
self->head = 0;
|
||||
self->tail = 0;
|
||||
return item;
|
||||
/* At the end of this chunk? */
|
||||
if (self->reader == ITEMS_PER_CHUNK)
|
||||
{
|
||||
struct chunk *old_chunk = self->first_chunk;
|
||||
self->first_chunk = old_chunk->next;
|
||||
free(old_chunk);
|
||||
self->reader = 0;
|
||||
}
|
||||
}
|
||||
else if (self->reader < self->writer)
|
||||
{
|
||||
/* We're reading the last chunk */
|
||||
item = self->first_chunk->items[self->reader++];
|
||||
if (self->reader == self->writer)
|
||||
{
|
||||
// fifo is now empty. We can reset the pointers
|
||||
// to prevent unnecessary allocations in the future.
|
||||
self->reader = 0;
|
||||
self->writer = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* more then one item in FIFO */
|
||||
udp = self->head;
|
||||
item = self->head->item;
|
||||
self->head = self->head->next;
|
||||
g_free(udp);
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return FIFO status
|
||||
*
|
||||
* @param self FIFO to operate on
|
||||
*
|
||||
* @return true if FIFO is empty, false otherwise
|
||||
*****************************************************************************/
|
||||
/*****************************************************************************/
|
||||
|
||||
int
|
||||
fifo_is_empty(FIFO *self)
|
||||
fifo_is_empty(struct fifo *self)
|
||||
{
|
||||
if (!self)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return (self->head == 0);
|
||||
return (self == NULL ||
|
||||
(self->first_chunk == self->last_chunk &&
|
||||
self->reader == self->writer));
|
||||
}
|
||||
|
|
|
@ -14,34 +14,85 @@
|
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file common/fifo.h
|
||||
* @brief Fifo for storing generic pointers
|
||||
*
|
||||
* FIFO implementation to store pointer to data struct
|
||||
* Declares an unbounded FIFO-queue for void * pointers
|
||||
*/
|
||||
|
||||
#ifndef _FIFO_H
|
||||
#define _FIFO_H
|
||||
|
||||
#include "arch.h"
|
||||
struct fifo;
|
||||
|
||||
typedef struct user_data USER_DATA;
|
||||
/**
|
||||
* Function used by fifo_clear()/fifo_delete() to destroy items
|
||||
*
|
||||
* @param item Item being deleted
|
||||
* @param closure Additional argument to function
|
||||
*
|
||||
* Use this function to free any allocated storage (e.g. if the items
|
||||
* are dynamically allocated)
|
||||
*/
|
||||
typedef void (*fifo_item_destructor)(void *item, void *closure);
|
||||
|
||||
struct user_data
|
||||
{
|
||||
USER_DATA *next;
|
||||
void *item;
|
||||
};
|
||||
/**
|
||||
* Create new fifo
|
||||
*
|
||||
* @param item_destructor Destructor for fifo items, or NULL for none
|
||||
* @return fifo, or NULL if no memory
|
||||
*/
|
||||
struct fifo *
|
||||
fifo_create(fifo_item_destructor item_destructor);
|
||||
|
||||
typedef struct fifo
|
||||
{
|
||||
USER_DATA *head;
|
||||
USER_DATA *tail;
|
||||
int auto_free;
|
||||
} FIFO;
|
||||
/**
|
||||
* Delete an existing fifo
|
||||
*
|
||||
* Any existing entries on the fifo are passed in order to the
|
||||
* item destructor specified when the fifo was created.
|
||||
*
|
||||
* @param self fifo to delete (may be NULL)
|
||||
* @param closure Additional parameter for fifo item destructor
|
||||
*/
|
||||
void
|
||||
fifo_delete(struct fifo *self, void *closure);
|
||||
|
||||
FIFO *fifo_create(void);
|
||||
void fifo_delete(FIFO *self);
|
||||
int fifo_add_item(FIFO *self, void *item);
|
||||
void *fifo_remove_item(FIFO *self);
|
||||
int fifo_is_empty(FIFO *self);
|
||||
/**
|
||||
* Clear(empty) an existing fifo
|
||||
*
|
||||
* Any existing entries on the fifo are passed in order to the
|
||||
* item destructor specified when the fifo was created.
|
||||
*
|
||||
* @param self fifo to clear (may be NULL)
|
||||
* @param closure Additional parameter for fifo item destructor
|
||||
*/
|
||||
void
|
||||
fifo_clear(struct fifo *self, void *closure);
|
||||
|
||||
/** Add an item to a fifo
|
||||
* @param self fifo
|
||||
* @param item Item to add
|
||||
* @return 1 if successful, 0 for no memory, or tried to add NULL
|
||||
*/
|
||||
int
|
||||
fifo_add_item(struct fifo *self, void *item);
|
||||
|
||||
/** Remove an item from a fifo
|
||||
* @param self fifo
|
||||
* @return item if successful, NULL for no items in FIFO
|
||||
*/
|
||||
void *
|
||||
fifo_remove_item(struct fifo *self);
|
||||
|
||||
/** Is fifo empty?
|
||||
*
|
||||
* @param self fifo
|
||||
* @return 1 if fifo is empty, 0 if not
|
||||
*/
|
||||
int
|
||||
fifo_is_empty(struct fifo *self);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -61,8 +61,6 @@ xrdp_chansrv_SOURCES = \
|
|||
clipboard_file.h \
|
||||
devredir.c \
|
||||
devredir.h \
|
||||
fifo.c \
|
||||
fifo.h \
|
||||
irp.c \
|
||||
irp.h \
|
||||
rail.c \
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
#define AUDIN_NAME "AUDIO_INPUT"
|
||||
#define AUDIN_FLAGS 1 /* WTS_CHANNEL_OPTION_DYNAMIC */
|
||||
|
||||
extern FIFO g_in_fifo; /* in sound.c */
|
||||
extern struct fifo *g_in_fifo; /* in sound.c */
|
||||
extern int g_bytes_in_fifo; /* in sound.c */
|
||||
|
||||
struct xr_wave_format_ex
|
||||
|
@ -346,7 +346,7 @@ audin_process_data(int chan_id, struct stream *s)
|
|||
g_memcpy(ls->data, s->p, data_bytes);
|
||||
ls->p += data_bytes;
|
||||
s_mark_end(ls);
|
||||
fifo_insert(&g_in_fifo, (void *) ls);
|
||||
fifo_add_item(g_in_fifo, (void *) ls);
|
||||
g_bytes_in_fifo += data_bytes;
|
||||
|
||||
return 0;
|
||||
|
@ -514,19 +514,15 @@ int
|
|||
audin_start(void)
|
||||
{
|
||||
int error;
|
||||
struct stream *s;
|
||||
|
||||
LOG_DEVEL(LOG_LEVEL_INFO, "audin_start:");
|
||||
if (g_audin_chanid != 0)
|
||||
if (g_audin_chanid != 0 || g_in_fifo == NULL)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* if there is any data in FIFO, discard it */
|
||||
while ((s = (struct stream *) fifo_remove(&g_in_fifo)) != NULL)
|
||||
{
|
||||
xstream_free(s);
|
||||
}
|
||||
fifo_clear(g_in_fifo, NULL);
|
||||
g_bytes_in_fifo = 0;
|
||||
|
||||
error = chansrv_drdynvc_open(AUDIN_NAME, AUDIN_FLAGS,
|
||||
|
|
|
@ -1,265 +0,0 @@
|
|||
/**
|
||||
* xrdp: A Remote Desktop Protocol server.
|
||||
*
|
||||
* Copyright (C) Laxmikant Rashinkar 2013 LK.Rashinkar@gmail.com
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* FIFO implementation to store a pointer to a user struct */
|
||||
|
||||
/* module based logging */
|
||||
#if defined(HAVE_CONFIG_H)
|
||||
#include <config_ac.h>
|
||||
#endif
|
||||
|
||||
#define MODULE_NAME "FIFO "
|
||||
#define LOCAL_DEBUG
|
||||
|
||||
#include "chansrv.h"
|
||||
#include "fifo.h"
|
||||
#include "os_calls.h"
|
||||
|
||||
/**
|
||||
* Initialize a FIFO that grows as required
|
||||
*
|
||||
* @param fp pointer to a FIFO
|
||||
* @param num_entries initial size
|
||||
*
|
||||
* @return 0 on success, -1 on failure
|
||||
*****************************************************************************/
|
||||
int
|
||||
fifo_init(FIFO *fp, int num_entries)
|
||||
{
|
||||
LOG_DEVEL(LOG_LEVEL_TRACE, "entered");
|
||||
|
||||
/* validate params */
|
||||
if (!fp)
|
||||
{
|
||||
LOG_DEVEL(LOG_LEVEL_TRACE, "invalid parameters");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (num_entries < 1)
|
||||
{
|
||||
num_entries = 10;
|
||||
}
|
||||
|
||||
fp->rd_ptr = 0;
|
||||
fp->wr_ptr = 0;
|
||||
fp->user_data = (long *) g_malloc(sizeof(long) * num_entries, 1);
|
||||
|
||||
if (fp->user_data)
|
||||
{
|
||||
fp->entries = num_entries;
|
||||
LOG_DEVEL(LOG_LEVEL_DEBUG, "FIFO created; rd_ptr=%d wr_ptr=%d entries=%d",
|
||||
fp->rd_ptr, fp->wr_ptr, fp->entries);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_DEVEL(LOG_LEVEL_ERROR, "FIFO create error; system out of memory");
|
||||
fp->entries = 0;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deinit FIFO and release resources
|
||||
*
|
||||
* @param fp FIFO to deinit
|
||||
*
|
||||
* @return 0 on success, -1 on error
|
||||
*****************************************************************************/
|
||||
int
|
||||
fifo_deinit(FIFO *fp)
|
||||
{
|
||||
LOG_DEVEL(LOG_LEVEL_TRACE, "entered");
|
||||
|
||||
if (!fp)
|
||||
{
|
||||
LOG_DEVEL(LOG_LEVEL_TRACE, "FIFO is null");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fp->user_data)
|
||||
{
|
||||
g_free(fp->user_data);
|
||||
fp->user_data = 0;
|
||||
}
|
||||
|
||||
fp->rd_ptr = 0;
|
||||
fp->wr_ptr = 0;
|
||||
fp->entries = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if FIFO is empty
|
||||
*
|
||||
* @param fp FIFO
|
||||
*
|
||||
* @return 1 if FIFO is empty, 0 otherwise
|
||||
*****************************************************************************/
|
||||
int
|
||||
fifo_is_empty(FIFO *fp)
|
||||
{
|
||||
LOG_DEVEL(LOG_LEVEL_TRACE, "entered");
|
||||
|
||||
if (!fp)
|
||||
{
|
||||
LOG_DEVEL(LOG_LEVEL_TRACE, "FIFO is null");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (fp->rd_ptr == fp->wr_ptr) ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert an item at the end
|
||||
*
|
||||
* @param fp FIFO
|
||||
* @param data data to insert into FIFO
|
||||
*
|
||||
* @param 0 on success, -1 on error
|
||||
*****************************************************************************/
|
||||
|
||||
int
|
||||
fifo_insert(FIFO *fp, void *data)
|
||||
{
|
||||
long *lp;
|
||||
int next_val; /* next value for wr_ptr */
|
||||
int i;
|
||||
|
||||
LOG_DEVEL(LOG_LEVEL_TRACE, "entered");
|
||||
|
||||
if (!fp)
|
||||
{
|
||||
LOG_DEVEL(LOG_LEVEL_TRACE, "FIFO is null");
|
||||
return -1;
|
||||
}
|
||||
|
||||
next_val = fp->wr_ptr + 1;
|
||||
if (next_val >= fp->entries)
|
||||
{
|
||||
next_val = 0;
|
||||
}
|
||||
|
||||
if (next_val == fp->rd_ptr)
|
||||
{
|
||||
/* FIFO is full, expand it by 10 entries */
|
||||
lp = (long *) g_malloc(sizeof(long) * (fp->entries + 10), 1);
|
||||
if (!lp)
|
||||
{
|
||||
LOG_DEVEL(LOG_LEVEL_DEBUG, "FIFO full; cannot expand, no memory");
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOG_DEVEL(LOG_LEVEL_DEBUG, "FIFO full, expanding by 10 entries");
|
||||
|
||||
/* copy old data new location */
|
||||
for (i = 0; i < (fp->entries - 1); i++)
|
||||
{
|
||||
lp[i] = fp->user_data[fp->rd_ptr++];
|
||||
if (fp->rd_ptr >= fp->entries)
|
||||
{
|
||||
fp->rd_ptr = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* update pointers */
|
||||
fp->rd_ptr = 0;
|
||||
fp->wr_ptr = fp->entries - 1;
|
||||
next_val = fp->entries;
|
||||
fp->entries += 10;
|
||||
|
||||
/* free old data */
|
||||
g_free(fp->user_data);
|
||||
fp->user_data = lp;
|
||||
}
|
||||
|
||||
LOG_DEVEL(LOG_LEVEL_DEBUG, "inserting data at index %d", fp->wr_ptr);
|
||||
|
||||
fp->user_data[fp->wr_ptr] = (long) data;
|
||||
fp->wr_ptr = next_val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an item from the head
|
||||
*
|
||||
* @param fp FIFO
|
||||
*
|
||||
* @param data on success, NULL on error
|
||||
*****************************************************************************/
|
||||
void *
|
||||
fifo_remove(FIFO *fp)
|
||||
{
|
||||
long data;
|
||||
|
||||
LOG_DEVEL(LOG_LEVEL_TRACE, "entered");
|
||||
|
||||
if (!fp)
|
||||
{
|
||||
LOG_DEVEL(LOG_LEVEL_TRACE, "FIFO is null");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (fp->rd_ptr == fp->wr_ptr)
|
||||
{
|
||||
LOG_DEVEL(LOG_LEVEL_TRACE, "FIFO is empty");
|
||||
return 0;
|
||||
}
|
||||
|
||||
LOG_DEVEL(LOG_LEVEL_DEBUG, "removing data at index %d", fp->rd_ptr);
|
||||
|
||||
data = fp->user_data[fp->rd_ptr++];
|
||||
|
||||
if (fp->rd_ptr >= fp->entries)
|
||||
{
|
||||
LOG_DEVEL(LOG_LEVEL_TRACE, "FIFO rd_ptr wrapped around");
|
||||
fp->rd_ptr = 0;
|
||||
}
|
||||
|
||||
return (void *) data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return item from head, but do not remove it
|
||||
*
|
||||
* @param fp FIFO
|
||||
*
|
||||
* @param data on success, NULL on error
|
||||
*****************************************************************************/
|
||||
void *
|
||||
fifo_peek(FIFO *fp)
|
||||
{
|
||||
LOG_DEVEL(LOG_LEVEL_TRACE, "entered");
|
||||
|
||||
if (!fp)
|
||||
{
|
||||
LOG_DEVEL(LOG_LEVEL_TRACE, "FIFO is null");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (fp->rd_ptr == fp->wr_ptr)
|
||||
{
|
||||
LOG_DEVEL(LOG_LEVEL_TRACE, "FIFO is empty");
|
||||
return 0;
|
||||
}
|
||||
|
||||
LOG_DEVEL(LOG_LEVEL_DEBUG, "peeking data at index %d", fp->rd_ptr);
|
||||
|
||||
return (void *) fp->user_data[fp->rd_ptr];
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
/**
|
||||
* xrdp: A Remote Desktop Protocol server.
|
||||
*
|
||||
* Copyright (C) Laxmikant Rashinkar 2013 LK.Rashinkar@gmail.com
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef FIFO_H
|
||||
#define FIFO_H
|
||||
|
||||
/* FIFO implementation to store a pointer to a user struct */
|
||||
|
||||
typedef struct fifo
|
||||
{
|
||||
long *user_data;
|
||||
int rd_ptr;
|
||||
int wr_ptr;
|
||||
int entries;
|
||||
} FIFO;
|
||||
|
||||
int fifo_init(FIFO *fp, int num_entries);
|
||||
int fifo_deinit(FIFO *fp);
|
||||
int fifo_is_empty(FIFO *fp);
|
||||
int fifo_insert(FIFO *fp, void *data);
|
||||
void *fifo_remove(FIFO *fp);
|
||||
void *fifo_peek(FIFO *fp);
|
||||
|
||||
#endif // FIFO_H
|
|
@ -74,7 +74,7 @@ static struct trans *g_audio_c_trans_in = 0; /* connection */
|
|||
static int g_training_sent_time = 0;
|
||||
static int g_cBlockNo = 0;
|
||||
static int g_bytes_in_stream = 0;
|
||||
FIFO g_in_fifo;
|
||||
struct fifo *g_in_fifo;
|
||||
int g_bytes_in_fifo = 0;
|
||||
static int g_time_diff = 0;
|
||||
static int g_best_time_diff = 0;
|
||||
|
@ -1259,6 +1259,15 @@ sound_sndsrvr_source_conn_in(struct trans *trans, struct trans *new_trans)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Item destructor for g_in_fifo
|
||||
*/
|
||||
static void
|
||||
in_fifo_item_destructor(void *item, void *closure)
|
||||
{
|
||||
xstream_free((struct stream *)item);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
int
|
||||
sound_init(void)
|
||||
|
@ -1276,7 +1285,7 @@ sound_init(void)
|
|||
sound_start_source_listener();
|
||||
|
||||
/* save data from sound_server_source */
|
||||
fifo_init(&g_in_fifo, 100);
|
||||
g_in_fifo = fifo_create(in_fifo_item_destructor);
|
||||
|
||||
g_client_does_fdk_aac = 0;
|
||||
g_client_fdk_aac_index = 0;
|
||||
|
@ -1340,7 +1349,7 @@ sound_deinit(void)
|
|||
}
|
||||
#endif
|
||||
|
||||
fifo_deinit(&g_in_fifo);
|
||||
fifo_delete(g_in_fifo, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1660,7 +1669,6 @@ sound_process_input_formats(struct stream *s, int size)
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
@ -1673,10 +1681,7 @@ sound_input_start_recording(void)
|
|||
LOG_DEVEL(LOG_LEVEL_DEBUG, "sound_input_start_recording:");
|
||||
|
||||
/* if there is any data in FIFO, discard it */
|
||||
while ((s = (struct stream *) fifo_remove(&g_in_fifo)) != NULL)
|
||||
{
|
||||
xstream_free(s);
|
||||
}
|
||||
fifo_clear(g_in_fifo, NULL);
|
||||
g_bytes_in_fifo = 0;
|
||||
|
||||
xstream_new(s, 1024);
|
||||
|
@ -1752,7 +1757,7 @@ sound_process_input_data(struct stream *s, int bytes)
|
|||
g_memcpy(ls->data, s->p, bytes);
|
||||
ls->p += bytes;
|
||||
s_mark_end(ls);
|
||||
fifo_insert(&g_in_fifo, (void *) ls);
|
||||
fifo_add_item(g_in_fifo, (void *) ls);
|
||||
g_bytes_in_fifo += bytes;
|
||||
|
||||
return 0;
|
||||
|
@ -1805,7 +1810,7 @@ sound_sndsrvr_source_data_in(struct trans *trans)
|
|||
{
|
||||
if (g_stream_inp == NULL)
|
||||
{
|
||||
g_stream_inp = (struct stream *) fifo_remove(&g_in_fifo);
|
||||
g_stream_inp = (struct stream *) fifo_remove_item(g_in_fifo);
|
||||
if (g_stream_inp != NULL)
|
||||
{
|
||||
g_bytes_in_fifo -= g_stream_inp->size;
|
||||
|
|
|
@ -14,6 +14,7 @@ check_PROGRAMS = test_common
|
|||
test_common_SOURCES = \
|
||||
test_common.h \
|
||||
test_common_main.c \
|
||||
test_fifo_calls.c \
|
||||
test_list_calls.c \
|
||||
test_string_calls.c \
|
||||
test_os_calls.c \
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
char *
|
||||
bin_to_hex(const char *input, int length);
|
||||
|
||||
Suite *make_suite_test_fifo(void);
|
||||
Suite *make_suite_test_list(void);
|
||||
Suite *make_suite_test_string(void);
|
||||
Suite *make_suite_test_os_calls(void);
|
||||
|
|
|
@ -46,7 +46,8 @@ int main (void)
|
|||
int number_failed;
|
||||
SRunner *sr;
|
||||
|
||||
sr = srunner_create (make_suite_test_list());
|
||||
sr = srunner_create (make_suite_test_fifo());
|
||||
srunner_add_suite(sr, make_suite_test_list());
|
||||
srunner_add_suite(sr, make_suite_test_string());
|
||||
srunner_add_suite(sr, make_suite_test_os_calls());
|
||||
srunner_add_suite(sr, make_suite_test_ssl_calls());
|
||||
|
|
|
@ -0,0 +1,216 @@
|
|||
#if defined(HAVE_CONFIG_H)
|
||||
#include "config_ac.h"
|
||||
#endif
|
||||
|
||||
#include "fifo.h"
|
||||
|
||||
#include "test_common.h"
|
||||
#include "os_calls.h"
|
||||
#include "string_calls.h"
|
||||
|
||||
static const char *strings[] =
|
||||
{
|
||||
"one",
|
||||
"two",
|
||||
"three",
|
||||
"four",
|
||||
"five",
|
||||
"six",
|
||||
"seven",
|
||||
"eight",
|
||||
"nine",
|
||||
"ten",
|
||||
"eleven",
|
||||
"twelve",
|
||||
NULL
|
||||
};
|
||||
|
||||
#define LARGE_TEST_SIZE 10000
|
||||
|
||||
/******************************************************************************/
|
||||
/* Item destructor function for fifo tests involving allocated strings */
|
||||
static void
|
||||
string_item_destructor(void *item, void *closure)
|
||||
{
|
||||
free(item);
|
||||
|
||||
if (closure != NULL)
|
||||
{
|
||||
/* Count the free operation */
|
||||
int *c = (int *)closure;
|
||||
++(*c);
|
||||
}
|
||||
}
|
||||
/******************************************************************************/
|
||||
|
||||
START_TEST(test_fifo__null)
|
||||
{
|
||||
struct fifo *f = NULL;
|
||||
void *vp;
|
||||
int status;
|
||||
|
||||
// These calls should not crash!
|
||||
fifo_delete(f, NULL);
|
||||
fifo_clear(f, NULL);
|
||||
|
||||
status = fifo_add_item(f, NULL);
|
||||
ck_assert_int_eq(status, 0);
|
||||
|
||||
vp = fifo_remove_item(f);
|
||||
ck_assert_ptr_eq(vp, NULL);
|
||||
|
||||
status = fifo_is_empty(f);
|
||||
ck_assert_int_eq(status, 1);
|
||||
}
|
||||
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_fifo__simple)
|
||||
{
|
||||
struct fifo *f = fifo_create(NULL);
|
||||
ck_assert_ptr_ne(f, NULL);
|
||||
|
||||
int empty = fifo_is_empty(f);
|
||||
ck_assert_int_eq(empty, 1);
|
||||
|
||||
// Check we can't add NULL to the fifo
|
||||
int success = fifo_add_item(f, NULL);
|
||||
ck_assert_int_eq(success, 0);
|
||||
|
||||
// Check we can't remove anything from an empty fifo
|
||||
void *vp = fifo_remove_item(f);
|
||||
ck_assert_ptr_eq(vp, NULL);
|
||||
|
||||
// Add some static strings to the FIFO
|
||||
const char **s;
|
||||
unsigned int n = 0;
|
||||
for (s = &strings[0] ; *s != NULL; ++s)
|
||||
{
|
||||
fifo_add_item(f, (void *)*s);
|
||||
++n;
|
||||
}
|
||||
|
||||
empty = fifo_is_empty(f);
|
||||
ck_assert_int_eq(empty, 0);
|
||||
|
||||
unsigned int i;
|
||||
for (i = 0 ; i < n ; ++i)
|
||||
{
|
||||
const char *p = (const char *)fifo_remove_item(f);
|
||||
ck_assert_ptr_eq(p, strings[i]);
|
||||
}
|
||||
|
||||
empty = fifo_is_empty(f);
|
||||
ck_assert_int_eq(empty, 1);
|
||||
|
||||
fifo_delete(f, NULL);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
|
||||
START_TEST(test_fifo__strdup)
|
||||
{
|
||||
struct fifo *f = fifo_create(string_item_destructor);
|
||||
ck_assert_ptr_ne(f, NULL);
|
||||
|
||||
// Add some dynamically allocated strings to the FIFO
|
||||
const char **s;
|
||||
unsigned int n = 0;
|
||||
for (s = &strings[0] ; *s != NULL; ++s)
|
||||
{
|
||||
int ok = fifo_add_item(f, (void *)strdup(*s));
|
||||
ck_assert_int_eq(ok, 1);
|
||||
++n;
|
||||
}
|
||||
|
||||
// Delete the fifo, freeing the allocated strings. Check free() is called
|
||||
// the expected number of times.
|
||||
int c = 0;
|
||||
fifo_delete(f, &c);
|
||||
ck_assert_int_eq(c, n);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_fifo__large_test_1)
|
||||
{
|
||||
struct fifo *f = fifo_create(string_item_destructor);
|
||||
ck_assert_ptr_ne(f, NULL);
|
||||
|
||||
// Fill the fifo with dynamically allocated strings
|
||||
int i;
|
||||
for (i = 0; i < LARGE_TEST_SIZE; ++i)
|
||||
{
|
||||
int ok = fifo_add_item(f, (void *)strdup("test item"));
|
||||
ck_assert_int_eq(ok, 1);
|
||||
}
|
||||
|
||||
// Clear the fifo, checking free is called the expected number of times
|
||||
int c = 0;
|
||||
fifo_clear(f, &c);
|
||||
ck_assert_int_eq(c, LARGE_TEST_SIZE);
|
||||
|
||||
int empty = fifo_is_empty(f);
|
||||
ck_assert_int_eq(empty, 1);
|
||||
|
||||
// Finally delete the fifo, checking free is not called this time
|
||||
c = 0;
|
||||
fifo_delete(f, &c);
|
||||
ck_assert_int_eq(c, 0);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_fifo__large_test_2)
|
||||
{
|
||||
char buff[64];
|
||||
|
||||
struct fifo *f = fifo_create(string_item_destructor);
|
||||
ck_assert_ptr_ne(f, NULL);
|
||||
|
||||
// Fill the fifo with dynamically allocated strings
|
||||
int i;
|
||||
for (i = 0; i < LARGE_TEST_SIZE; ++i)
|
||||
{
|
||||
g_snprintf(buff, sizeof(buff), "%d", i);
|
||||
int ok = fifo_add_item(f, (void *)strdup(buff));
|
||||
ck_assert_int_eq(ok, 1);
|
||||
}
|
||||
|
||||
// Extract all the strings from the fifo, making sure they're
|
||||
// as expected
|
||||
for (i = 0; i < LARGE_TEST_SIZE; ++i)
|
||||
{
|
||||
g_snprintf(buff, sizeof(buff), "%d", i);
|
||||
char *s = fifo_remove_item(f);
|
||||
ck_assert_ptr_ne(s, NULL);
|
||||
ck_assert_str_eq(s, buff);
|
||||
free(s);
|
||||
}
|
||||
|
||||
int empty = fifo_is_empty(f);
|
||||
ck_assert_int_eq(empty, 1);
|
||||
|
||||
int c = 0;
|
||||
fifo_delete(f, &c);
|
||||
ck_assert_int_eq(c, 0);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/******************************************************************************/
|
||||
Suite *
|
||||
make_suite_test_fifo(void)
|
||||
{
|
||||
Suite *s;
|
||||
TCase *tc_simple;
|
||||
|
||||
s = suite_create("Fifo");
|
||||
|
||||
tc_simple = tcase_create("simple");
|
||||
suite_add_tcase(s, tc_simple);
|
||||
tcase_add_test(tc_simple, test_fifo__null);
|
||||
tcase_add_test(tc_simple, test_fifo__simple);
|
||||
tcase_add_test(tc_simple, test_fifo__strdup);
|
||||
tcase_add_test(tc_simple, test_fifo__large_test_1);
|
||||
tcase_add_test(tc_simple, test_fifo__large_test_2);
|
||||
|
||||
return s;
|
||||
}
|
|
@ -46,6 +46,26 @@ process_enc_rfx(struct xrdp_encoder *self, XRDP_ENC_DATA *enc);
|
|||
static int
|
||||
process_enc_h264(struct xrdp_encoder *self, XRDP_ENC_DATA *enc);
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Item destructor for self->fifo_to_proc */
|
||||
static void
|
||||
xrdp_enc_data_destructor(void *item, void *closure)
|
||||
{
|
||||
XRDP_ENC_DATA *enc = (XRDP_ENC_DATA *)item;
|
||||
g_free(enc->drects);
|
||||
g_free(enc->crects);
|
||||
g_free(enc);
|
||||
}
|
||||
|
||||
/* Item destructor for self->fifo_processed */
|
||||
static void
|
||||
xrdp_enc_data_done_destructor(void *item, void *closure)
|
||||
{
|
||||
XRDP_ENC_DATA_DONE *enc_done = (XRDP_ENC_DATA_DONE *)item;
|
||||
g_free(enc_done->comp_pad_data);
|
||||
g_free(enc_done);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
struct xrdp_encoder *
|
||||
xrdp_encoder_create(struct xrdp_mm *mm)
|
||||
|
@ -114,8 +134,8 @@ xrdp_encoder_create(struct xrdp_mm *mm)
|
|||
LOG_DEVEL(LOG_LEVEL_INFO, "init_xrdp_encoder: initializing encoder codec_id %d", self->codec_id);
|
||||
|
||||
/* setup required FIFOs */
|
||||
self->fifo_to_proc = fifo_create();
|
||||
self->fifo_processed = fifo_create();
|
||||
self->fifo_to_proc = fifo_create(xrdp_enc_data_destructor);
|
||||
self->fifo_processed = fifo_create(xrdp_enc_data_done_destructor);
|
||||
self->mutex = tc_mutex_create();
|
||||
|
||||
pid = g_getpid();
|
||||
|
@ -141,10 +161,6 @@ xrdp_encoder_create(struct xrdp_mm *mm)
|
|||
void
|
||||
xrdp_encoder_delete(struct xrdp_encoder *self)
|
||||
{
|
||||
XRDP_ENC_DATA *enc;
|
||||
XRDP_ENC_DATA_DONE *enc_done;
|
||||
FIFO *fifo;
|
||||
|
||||
LOG_DEVEL(LOG_LEVEL_INFO, "xrdp_encoder_delete:");
|
||||
if (self == 0)
|
||||
{
|
||||
|
@ -175,40 +191,9 @@ xrdp_encoder_delete(struct xrdp_encoder *self)
|
|||
g_delete_wait_obj(self->xrdp_encoder_event_processed);
|
||||
g_delete_wait_obj(self->xrdp_encoder_term);
|
||||
|
||||
/* cleanup fifo_to_proc */
|
||||
fifo = self->fifo_to_proc;
|
||||
if (fifo)
|
||||
{
|
||||
while (!fifo_is_empty(fifo))
|
||||
{
|
||||
enc = (XRDP_ENC_DATA *) fifo_remove_item(fifo);
|
||||
if (enc == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
g_free(enc->drects);
|
||||
g_free(enc->crects);
|
||||
g_free(enc);
|
||||
}
|
||||
fifo_delete(fifo);
|
||||
}
|
||||
|
||||
/* cleanup fifo_processed */
|
||||
fifo = self->fifo_processed;
|
||||
if (fifo)
|
||||
{
|
||||
while (!fifo_is_empty(fifo))
|
||||
{
|
||||
enc_done = (XRDP_ENC_DATA_DONE *) fifo_remove_item(fifo);
|
||||
if (enc_done == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
g_free(enc_done->comp_pad_data);
|
||||
g_free(enc_done);
|
||||
}
|
||||
fifo_delete(fifo);
|
||||
}
|
||||
/* cleanup fifos */
|
||||
fifo_delete(self->fifo_to_proc, NULL);
|
||||
fifo_delete(self->fifo_processed, NULL);
|
||||
tc_mutex_delete(self->mutex);
|
||||
g_free(self);
|
||||
}
|
||||
|
@ -229,7 +214,7 @@ process_enc_jpg(struct xrdp_encoder *self, XRDP_ENC_DATA *enc)
|
|||
int count;
|
||||
char *out_data;
|
||||
XRDP_ENC_DATA_DONE *enc_done;
|
||||
FIFO *fifo_processed;
|
||||
struct fifo *fifo_processed;
|
||||
tbus mutex;
|
||||
tbus event_processed;
|
||||
|
||||
|
@ -322,7 +307,7 @@ process_enc_rfx(struct xrdp_encoder *self, XRDP_ENC_DATA *enc)
|
|||
int finished;
|
||||
char *out_data;
|
||||
XRDP_ENC_DATA_DONE *enc_done;
|
||||
FIFO *fifo_processed;
|
||||
struct fifo *fifo_processed;
|
||||
tbus mutex;
|
||||
tbus event_processed;
|
||||
struct rfx_tile *tiles;
|
||||
|
@ -455,7 +440,7 @@ THREAD_RV THREAD_CC
|
|||
proc_enc_msg(void *arg)
|
||||
{
|
||||
XRDP_ENC_DATA *enc;
|
||||
FIFO *fifo_to_proc;
|
||||
struct fifo *fifo_to_proc;
|
||||
tbus mutex;
|
||||
tbus event_to_proc;
|
||||
tbus term_obj;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#define _XRDP_ENCODER_H
|
||||
|
||||
#include "arch.h"
|
||||
#include "fifo.h"
|
||||
struct fifo;
|
||||
|
||||
struct xrdp_enc_data;
|
||||
|
||||
|
@ -18,8 +18,8 @@ struct xrdp_encoder
|
|||
tbus xrdp_encoder_event_to_proc;
|
||||
tbus xrdp_encoder_event_processed;
|
||||
tbus xrdp_encoder_term;
|
||||
FIFO *fifo_to_proc;
|
||||
FIFO *fifo_processed;
|
||||
struct fifo *fifo_to_proc;
|
||||
struct fifo *fifo_processed;
|
||||
tbus mutex;
|
||||
int (*process_enc)(struct xrdp_encoder *self, struct xrdp_enc_data *enc);
|
||||
void *codec_handle;
|
||||
|
|
Loading…
Reference in New Issue