[Spice-devel] [spice v12 04/26] server: Let the administrator pick the video encoder and codec
Francois Gouget
fgouget at codeweavers.com
Tue Apr 5 15:15:23 UTC 2016
The Spice server administrator can specify the encoder and codec
preferences to optimize for CPU or bandwidth usage. Preferences are
described in a semi-colon separated list of encoder:codec pairs.
The server has a default preference list which can explicitly be
selected by specifying 'auto'.
The server then picks a codec supported by the client based on the
following new client capabilities:
* SPICE_DISPLAY_CAP_MULTI_CODEC which denotes a recent client that
supports multiple codecs. This capability is needed to not have to
hardcode that MJPEG is supported. This makes it possible to write
clients that don't support MJPEG.
* SPICE_DISPLAY_CAP_CODEC_XXX, where XXX is a supported codec. Note
that for now the server only supports the MJPEG codec.
Signed-off-by: Francois Gouget <fgouget at codeweavers.com>
---
server/dcc-send.c | 2 +-
server/display-channel.c | 10 +++
server/display-channel.h | 4 ++
server/gstreamer-encoder.c | 6 +-
server/mjpeg-encoder.c | 6 +-
server/red-qxl.c | 9 +++
server/red-qxl.h | 6 ++
server/red-worker.c | 18 +++++-
server/reds-private.h | 1 +
server/reds.c | 155 ++++++++++++++++++++++++++++++++++++++++-----
server/reds.h | 1 +
server/spice-server.h | 8 +++
server/spice-server.syms | 5 ++
server/stream.c | 29 +++++++--
server/video-encoder.h | 20 +++++-
15 files changed, 251 insertions(+), 29 deletions(-)
diff --git a/server/dcc-send.c b/server/dcc-send.c
index ebbc3e5..91d6352 100644
--- a/server/dcc-send.c
+++ b/server/dcc-send.c
@@ -2151,7 +2151,7 @@ static void marshall_stream_start(RedChannelClient *rcc,
stream_create.surface_id = 0;
stream_create.id = get_stream_id(DCC_TO_DC(dcc), stream);
stream_create.flags = stream->top_down ? SPICE_STREAM_FLAGS_TOP_DOWN : 0;
- stream_create.codec_type = SPICE_VIDEO_CODEC_TYPE_MJPEG;
+ stream_create.codec_type = agent->video_encoder->codec_type;
stream_create.src_width = stream->width;
stream_create.src_height = stream->height;
diff --git a/server/display-channel.c b/server/display-channel.c
index a6d90cf..5a0c6fb 100644
--- a/server/display-channel.c
+++ b/server/display-channel.c
@@ -230,6 +230,14 @@ void display_channel_set_stream_video(DisplayChannel *display, int stream_video)
display->stream_video = stream_video;
}
+void display_channel_set_video_codecs(DisplayChannel *display, GArray *video_codecs)
+{
+ spice_return_if_fail(display);
+
+ g_array_unref(display->video_codecs);
+ display->video_codecs = g_array_ref(video_codecs);
+}
+
static void stop_streams(DisplayChannel *display)
{
Ring *ring = &display->streams;
@@ -2029,6 +2037,7 @@ static SpiceCanvas *image_surfaces_get(SpiceImageSurfaces *surfaces, uint32_t su
DisplayChannel* display_channel_new(SpiceServer *reds, RedWorker *worker,
int migrate, int stream_video,
+ GArray *video_codecs,
uint32_t n_surfaces)
{
DisplayChannel *display;
@@ -2082,6 +2091,7 @@ DisplayChannel* display_channel_new(SpiceServer *reds, RedWorker *worker,
drawables_init(display);
image_cache_init(&display->image_cache);
display->stream_video = stream_video;
+ display->video_codecs = g_array_ref(video_codecs);
display_channel_init_streams(display);
return display;
diff --git a/server/display-channel.h b/server/display-channel.h
index 6b053de..cf3709a 100644
--- a/server/display-channel.h
+++ b/server/display-channel.h
@@ -183,6 +183,7 @@ struct DisplayChannel {
uint32_t glz_drawable_count;
int stream_video;
+ GArray *video_codecs;
uint32_t stream_count;
Stream streams_buf[NUM_STREAMS];
Stream *free_streams;
@@ -254,6 +255,7 @@ DisplayChannel* display_channel_new (SpiceServe
RedWorker *worker,
int migrate,
int stream_video,
+ GArray *video_codecs,
uint32_t n_surfaces);
void display_channel_create_surface (DisplayChannel *display, uint32_t surface_id,
uint32_t width, uint32_t height,
@@ -275,6 +277,8 @@ void display_channel_update (DisplayCha
void display_channel_free_some (DisplayChannel *display);
void display_channel_set_stream_video (DisplayChannel *display,
int stream_video);
+void display_channel_set_video_codecs (DisplayChannel *display,
+ GArray *video_codecs);
int display_channel_get_streams_timeout (DisplayChannel *display);
void display_channel_compress_stats_print (const DisplayChannel *display);
void display_channel_compress_stats_reset (DisplayChannel *display);
diff --git a/server/gstreamer-encoder.c b/server/gstreamer-encoder.c
index e5c044e..d6eb1eb 100644
--- a/server/gstreamer-encoder.c
+++ b/server/gstreamer-encoder.c
@@ -507,9 +507,12 @@ static void spice_gst_encoder_get_stats(VideoEncoder *video_encoder,
}
}
-VideoEncoder *gstreamer_encoder_new(uint64_t starting_bit_rate,
+VideoEncoder *gstreamer_encoder_new(SpiceVideoCodecType codec_type,
+ uint64_t starting_bit_rate,
VideoEncoderRateControlCbs *cbs)
{
+ spice_return_val_if_fail(codec_type == SPICE_VIDEO_CODEC_TYPE_MJPEG, NULL);
+
GError *err = NULL;
if (!gst_init_check(NULL, NULL, &err)) {
spice_warning("GStreamer error: %s", err->message);
@@ -524,6 +527,7 @@ VideoEncoder *gstreamer_encoder_new(uint64_t starting_bit_rate,
encoder->base.notify_server_frame_drop = spice_gst_encoder_notify_server_frame_drop;
encoder->base.get_bit_rate = spice_gst_encoder_get_bit_rate;
encoder->base.get_stats = spice_gst_encoder_get_stats;
+ encoder->base.codec_type = codec_type;
if (cbs) {
encoder->cbs = *cbs;
diff --git a/server/mjpeg-encoder.c b/server/mjpeg-encoder.c
index 5ec0753..41237a7 100644
--- a/server/mjpeg-encoder.c
+++ b/server/mjpeg-encoder.c
@@ -1344,17 +1344,21 @@ static void mjpeg_encoder_get_stats(VideoEncoder *video_encoder,
stats->avg_quality = (double)encoder->avg_quality / encoder->num_frames;
}
-VideoEncoder *mjpeg_encoder_new(uint64_t starting_bit_rate,
+VideoEncoder *mjpeg_encoder_new(SpiceVideoCodecType codec_type,
+ uint64_t starting_bit_rate,
VideoEncoderRateControlCbs *cbs)
{
MJpegEncoder *encoder = spice_new0(MJpegEncoder, 1);
+ spice_return_val_if_fail(codec_type == SPICE_VIDEO_CODEC_TYPE_MJPEG, NULL);
+
encoder->base.destroy = mjpeg_encoder_destroy;
encoder->base.encode_frame = mjpeg_encoder_encode_frame;
encoder->base.client_stream_report = mjpeg_encoder_client_stream_report;
encoder->base.notify_server_frame_drop = mjpeg_encoder_notify_server_frame_drop;
encoder->base.get_bit_rate = mjpeg_encoder_get_bit_rate;
encoder->base.get_stats = mjpeg_encoder_get_stats;
+ encoder->base.codec_type = codec_type;
encoder->first_frame = TRUE;
encoder->rate_control.byte_rate = starting_bit_rate / 8;
encoder->starting_bit_rate = starting_bit_rate;
diff --git a/server/red-qxl.c b/server/red-qxl.c
index abde0ba..55a77ef 100644
--- a/server/red-qxl.c
+++ b/server/red-qxl.c
@@ -1045,6 +1045,15 @@ void red_qxl_on_sv_change(QXLInstance *qxl, int sv)
&payload);
}
+void red_qxl_on_vc_change(QXLInstance *qxl, GArray *video_codecs)
+{
+ RedWorkerMessageSetVideoCodecs payload;
+ payload.video_codecs = g_array_ref(video_codecs);
+ dispatcher_send_message(qxl->st->dispatcher,
+ RED_WORKER_MESSAGE_SET_VIDEO_CODECS,
+ &payload);
+}
+
void red_qxl_set_mouse_mode(QXLInstance *qxl, uint32_t mode)
{
RedWorkerMessageSetMouseMode payload;
diff --git a/server/red-qxl.h b/server/red-qxl.h
index b1ebbe1..2dcfbb7 100644
--- a/server/red-qxl.h
+++ b/server/red-qxl.h
@@ -28,6 +28,7 @@ void red_qxl_init(SpiceServer *reds, QXLInstance *qxl);
void red_qxl_on_ic_change(QXLInstance *qxl, SpiceImageCompression ic);
void red_qxl_on_sv_change(QXLInstance *qxl, int sv);
+void red_qxl_on_vc_change(QXLInstance *qxl, GArray* video_codecs);
void red_qxl_set_mouse_mode(QXLInstance *qxl, uint32_t mode);
void red_qxl_attach_worker(QXLInstance *qxl);
void red_qxl_set_compression_level(QXLInstance *qxl, int level);
@@ -114,6 +115,7 @@ enum {
RED_WORKER_MESSAGE_DRIVER_UNLOAD,
RED_WORKER_MESSAGE_GL_SCANOUT,
RED_WORKER_MESSAGE_GL_DRAW_ASYNC,
+ RED_WORKER_MESSAGE_SET_VIDEO_CODECS,
RED_WORKER_MESSAGE_COUNT // LAST
};
@@ -251,6 +253,10 @@ typedef struct RedWorkerMessageSetStreamingVideo {
uint32_t streaming_video;
} RedWorkerMessageSetStreamingVideo;
+typedef struct RedWorkerMessageSetVideoCodecs {
+ GArray* video_codecs;
+} RedWorkerMessageSetVideoCodecs;
+
typedef struct RedWorkerMessageSetMouseMode {
uint32_t mode;
} RedWorkerMessageSetMouseMode;
diff --git a/server/red-worker.c b/server/red-worker.c
index 241c300..1992b20 100644
--- a/server/red-worker.c
+++ b/server/red-worker.c
@@ -1076,6 +1076,15 @@ static void handle_dev_set_streaming_video(void *opaque, void *payload)
display_channel_set_stream_video(worker->display_channel, msg->streaming_video);
}
+void handle_dev_set_video_codecs(void *opaque, void *payload)
+{
+ RedWorkerMessageSetVideoCodecs *msg = payload;
+ RedWorker *worker = opaque;
+
+ display_channel_set_video_codecs(worker->display_channel, msg->video_codecs);
+ g_array_unref(msg->video_codecs);
+}
+
static void handle_dev_set_mouse_mode(void *opaque, void *payload)
{
RedWorkerMessageSetMouseMode *msg = payload;
@@ -1349,6 +1358,11 @@ static void register_callbacks(Dispatcher *dispatcher)
sizeof(RedWorkerMessageSetStreamingVideo),
DISPATCHER_NONE);
dispatcher_register_handler(dispatcher,
+ RED_WORKER_MESSAGE_SET_VIDEO_CODECS,
+ handle_dev_set_video_codecs,
+ sizeof(RedWorkerMessageSetVideoCodecs),
+ DISPATCHER_NONE);
+ dispatcher_register_handler(dispatcher,
RED_WORKER_MESSAGE_SET_MOUSE_MODE,
handle_dev_set_mouse_mode,
sizeof(RedWorkerMessageSetMouseMode),
@@ -1541,7 +1555,9 @@ RedWorker* red_worker_new(QXLInstance *qxl,
reds_register_channel(reds, channel);
// TODO: handle seemless migration. Temp, setting migrate to FALSE
- worker->display_channel = display_channel_new(reds, worker, FALSE, reds_get_streaming_video(reds),
+ worker->display_channel = display_channel_new(reds, worker, FALSE,
+ reds_get_streaming_video(reds),
+ reds_get_video_codecs(reds),
init_info.n_surfaces);
channel = RED_CHANNEL(worker->display_channel);
diff --git a/server/reds-private.h b/server/reds-private.h
index 8842aad..7a69aed 100644
--- a/server/reds-private.h
+++ b/server/reds-private.h
@@ -170,6 +170,7 @@ struct RedsState {
gboolean ticketing_enabled;
uint32_t streaming_video;
+ GArray* video_codecs;
SpiceImageCompression image_compression;
spice_wan_compression_t jpeg_state;
spice_wan_compression_t zlib_glz_state;
diff --git a/server/reds.c b/server/reds.c
index 23df51a..a270fa6 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -71,6 +71,7 @@
#include "utils.h"
#include "reds-private.h"
+#include "video-encoder.h"
static SpiceCoreInterface *core_public = NULL;
@@ -231,6 +232,7 @@ static void reds_remove_char_device(RedsState *reds, RedCharDevice *dev);
static void reds_send_mm_time(RedsState *reds);
static void reds_on_ic_change(RedsState *reds);
static void reds_on_sv_change(RedsState *reds);
+static void reds_on_vc_change(RedsState *reds);
static void reds_on_vm_stop(RedsState *reds);
static void reds_on_vm_start(RedsState *reds);
static void reds_set_mouse_mode(RedsState *reds, uint32_t mode);
@@ -3480,6 +3482,7 @@ err:
}
static const char default_renderer[] = "sw";
+static const char default_video_codecs[] = "spice:mjpeg;gstreamer:mjpeg";
/* new interface */
SPICE_GNUC_VISIBLE SpiceServer *spice_server_new(void)
@@ -3500,6 +3503,7 @@ SPICE_GNUC_VISIBLE SpiceServer *spice_server_new(void)
memset(reds->spice_uuid, 0, sizeof(reds->spice_uuid));
reds->ticketing_enabled = TRUE; /* ticketing enabled by default */
reds->streaming_video = SPICE_STREAM_VIDEO_FILTER;
+ reds->video_codecs = g_array_new(FALSE, FALSE, sizeof(RedVideoCodec));
reds->image_compression = SPICE_IMAGE_COMPRESSION_AUTO_GLZ;
reds->jpeg_state = SPICE_WAN_COMPRESSION_AUTO;
reds->zlib_glz_state = SPICE_WAN_COMPRESSION_AUTO;
@@ -3510,37 +3514,129 @@ SPICE_GNUC_VISIBLE SpiceServer *spice_server_new(void)
return reds;
}
-typedef struct RendererInfo {
- int id;
+typedef struct {
+ uint32_t id;
const char *name;
-} RendererInfo;
+} EnumNames;
-static const RendererInfo renderers_info[] = {
+static gboolean get_name_index(const EnumNames names[], const char *name, uint32_t *index)
+{
+ if (name) {
+ int i;
+ for (i = 0; names[i].name; i++) {
+ if (strcmp(name, names[i].name) == 0) {
+ *index = i;
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+
+static const EnumNames renderer_names[] = {
{RED_RENDERER_SW, "sw"},
{RED_RENDERER_INVALID, NULL},
};
-static const RendererInfo *find_renderer(const char *name)
+static gboolean reds_add_renderer(RedsState *reds, const char *name)
+{
+ uint32_t index;
+
+ if (reds->renderers->len == RED_RENDERER_LAST ||
+ !get_name_index(renderer_names, name, &index)) {
+ return FALSE;
+ }
+ g_array_append_val(reds->renderers, renderer_names[index].id);
+ return TRUE;
+}
+
+static const EnumNames video_encoder_names[] = {
+ {0, "spice"},
+ {1, "gstreamer"},
+ {0, NULL},
+};
+
+static new_video_encoder_t video_encoder_procs[] = {
+ &mjpeg_encoder_new,
+#ifdef HAVE_GSTREAMER_1_0
+ &gstreamer_encoder_new,
+#else
+ NULL,
+#endif
+};
+
+static const EnumNames video_codec_names[] = {
+ {SPICE_VIDEO_CODEC_TYPE_MJPEG, "mjpeg"},
+ {0, NULL},
+};
+
+static int video_codec_caps[] = {
+ SPICE_DISPLAY_CAP_CODEC_MJPEG,
+};
+
+
+/* Expected string: encoder:codec;encoder:codec */
+static const char* parse_video_codecs(const char *codecs, char **encoder,
+ char **codec)
{
- const RendererInfo *inf = renderers_info;
- while (inf->name) {
- if (strcmp(name, inf->name) == 0) {
- return inf;
+ if (!codecs) {
+ return NULL;
+ }
+ while (*codecs == ';') {
+ codecs++;
+ }
+ if (!*codecs) {
+ return NULL;
+ }
+ int n;
+ *encoder = *codec = NULL;
+ if (sscanf(codecs, "%m[0-9a-zA-Z_]:%m[0-9a-zA-Z_]%n", encoder, codec, &n) != 2) {
+ while (*codecs != '\0' && *codecs != ';') {
+ codecs++;
}
- inf++;
+ return codecs;
}
- return NULL;
+ return codecs + n;
}
-static int reds_add_renderer(RedsState *reds, const char *name)
+static void reds_set_video_codecs(RedsState *reds, const char *codecs)
{
- const RendererInfo *inf;
+ char *encoder_name, *codec_name;
- if (reds->renderers->len == RED_RENDERER_LAST || !(inf = find_renderer(name))) {
- return FALSE;
+ if (strcmp(codecs, "auto") == 0) {
+ codecs = default_video_codecs;
+ }
+
+ /* The video_codecs array is immutable */
+ g_array_unref(reds->video_codecs);
+ reds->video_codecs = g_array_new(FALSE, FALSE, sizeof(RedVideoCodec));
+ const char *c = codecs;
+ while ( (c = parse_video_codecs(c, &encoder_name, &codec_name)) ) {
+ uint32_t encoder_index, codec_index;
+ if (!encoder_name || !codec_name) {
+ spice_warning("spice: invalid encoder:codec value at %s", codecs);
+
+ } else if (!get_name_index(video_encoder_names, encoder_name, &encoder_index)){
+ spice_warning("spice: unknown video encoder %s", encoder_name);
+
+ } else if (!get_name_index(video_codec_names, codec_name, &codec_index)) {
+ spice_warning("spice: unknown video codec %s", codec_name);
+
+ } else if (!video_encoder_procs[encoder_index]) {
+ spice_warning("spice: unsupported video encoder %s", encoder_name);
+
+ } else {
+ RedVideoCodec new_codec;
+ new_codec.create = video_encoder_procs[encoder_index];
+ new_codec.type = video_codec_names[codec_index].id;
+ new_codec.cap = video_codec_caps[codec_index];
+ g_array_append_val(reds->video_codecs, new_codec);
+ }
+
+ free(encoder_name);
+ free(codec_name);
+ codecs = c;
}
- g_array_append_val(reds->renderers, inf->id);
- return TRUE;
}
SPICE_GNUC_VISIBLE int spice_server_init(SpiceServer *reds, SpiceCoreInterface *core)
@@ -3551,12 +3647,16 @@ SPICE_GNUC_VISIBLE int spice_server_init(SpiceServer *reds, SpiceCoreInterface *
if (reds->renderers->len == 0) {
reds_add_renderer(reds, default_renderer);
}
+ if (reds->video_codecs->len == 0) {
+ reds_set_video_codecs(reds, default_video_codecs);
+ }
return ret;
}
SPICE_GNUC_VISIBLE void spice_server_destroy(SpiceServer *reds)
{
g_array_unref(reds->renderers);
+ g_array_unref(reds->video_codecs);
if (reds->main_channel) {
main_channel_close(reds->main_channel);
}
@@ -3857,6 +3957,18 @@ uint32_t reds_get_streaming_video(const RedsState *reds)
return reds->streaming_video;
}
+SPICE_GNUC_VISIBLE int spice_server_set_video_codecs(SpiceServer *reds, const char *video_codecs)
+{
+ reds_set_video_codecs(reds, video_codecs);
+ reds_on_vc_change(reds);
+ return 0;
+}
+
+GArray* reds_get_video_codecs(const RedsState *reds)
+{
+ return reds->video_codecs;
+}
+
SPICE_GNUC_VISIBLE int spice_server_set_playback_compression(SpiceServer *reds, int enable)
{
snd_set_playback_compression(enable);
@@ -4244,6 +4356,15 @@ void reds_on_sv_change(RedsState *reds)
}
}
+void reds_on_vc_change(RedsState *reds)
+{
+ GList *l;
+
+ for (l = reds->qxl_instances; l != NULL; l = l->next) {
+ red_qxl_on_vc_change(l->data, reds_get_video_codecs(reds));
+ }
+}
+
void reds_on_vm_stop(RedsState *reds)
{
GList *l;
diff --git a/server/reds.h b/server/reds.h
index 83618e9..cfd5723 100644
--- a/server/reds.h
+++ b/server/reds.h
@@ -106,6 +106,7 @@ void reds_on_char_device_state_destroy(RedsState *reds, RedCharDevice *dev);
void reds_set_client_mm_time_latency(RedsState *reds, RedClient *client, uint32_t latency);
uint32_t reds_get_streaming_video(const RedsState *reds);
+GArray* reds_get_video_codecs(const RedsState *reds);
spice_wan_compression_t reds_get_jpeg_state(const RedsState *reds);
spice_wan_compression_t reds_get_zlib_glz_state(const RedsState *reds);
SpiceCoreInterfaceInternal* reds_get_core_interface(RedsState *reds);
diff --git a/server/spice-server.h b/server/spice-server.h
index c2ff61d..10b50a2 100644
--- a/server/spice-server.h
+++ b/server/spice-server.h
@@ -107,6 +107,14 @@ enum {
};
int spice_server_set_streaming_video(SpiceServer *s, int value);
+
+enum {
+ SPICE_STREAMING_INVALID,
+ SPICE_STREAMING_SPICE,
+ SPICE_STREAMING_GSTREAMER
+};
+
+int spice_server_set_video_codecs(SpiceServer *s, const char* video_codecs);
int spice_server_set_playback_compression(SpiceServer *s, int enable);
int spice_server_set_agent_mouse(SpiceServer *s, int enable);
int spice_server_set_agent_copypaste(SpiceServer *s, int enable);
diff --git a/server/spice-server.syms b/server/spice-server.syms
index 5c3e53c..edf04a4 100644
--- a/server/spice-server.syms
+++ b/server/spice-server.syms
@@ -168,3 +168,8 @@ global:
spice_qxl_gl_scanout;
spice_qxl_gl_draw_async;
} SPICE_SERVER_0.12.6;
+
+SPICE_SERVER_0.13.2 {
+global:
+ spice_server_set_video_codecs;
+} SPICE_SERVER_0.13.1;
diff --git a/server/stream.c b/server/stream.c
index 64d4a90..0aa10c3 100644
--- a/server/stream.c
+++ b/server/stream.c
@@ -701,17 +701,34 @@ static VideoEncoder* dcc_create_video_encoder(DisplayChannelClient *dcc,
uint64_t starting_bit_rate,
VideoEncoderRateControlCbs *cbs)
{
+ DisplayChannel *display = DCC_TO_DC(dcc);
RedChannelClient *rcc = RED_CHANNEL_CLIENT(dcc);
int client_has_multi_codec = red_channel_client_test_remote_cap(rcc, SPICE_DISPLAY_CAP_MULTI_CODEC);
- if (!client_has_multi_codec || red_channel_client_test_remote_cap(rcc, SPICE_DISPLAY_CAP_CODEC_MJPEG)) {
-#ifdef HAVE_GSTREAMER_1_0
- VideoEncoder* video_encoder = gstreamer_encoder_new(starting_bit_rate, cbs);
+ int i;
+
+ for (i = 0; i < display->video_codecs->len; i++) {
+ RedVideoCodec* video_codec = &g_array_index (display->video_codecs, RedVideoCodec, i);
+
+ if (!client_has_multi_codec &&
+ video_codec->type != SPICE_VIDEO_CODEC_TYPE_MJPEG) {
+ /* Old clients only support MJPEG */
+ continue;
+ }
+ if (client_has_multi_codec &&
+ !red_channel_client_test_remote_cap(rcc, video_codec->cap)) {
+ /* The client is recent but does not support this codec */
+ continue;
+ }
+
+ VideoEncoder* video_encoder = video_codec->create(video_codec->type, starting_bit_rate, cbs);
if (video_encoder) {
return video_encoder;
}
-#endif
- /* Use the builtin MJPEG video encoder as a fallback */
- return mjpeg_encoder_new(starting_bit_rate, cbs);
+ }
+
+ /* Try to use the builtin MJPEG video encoder as a fallback */
+ if (!client_has_multi_codec || red_channel_client_test_remote_cap(rcc, SPICE_DISPLAY_CAP_CODEC_MJPEG)) {
+ return mjpeg_encoder_new(SPICE_VIDEO_CODEC_TYPE_MJPEG, starting_bit_rate, cbs);
}
return NULL;
diff --git a/server/video-encoder.h b/server/video-encoder.h
index 104f3b5..8f3807b 100644
--- a/server/video-encoder.h
+++ b/server/video-encoder.h
@@ -116,6 +116,9 @@ struct VideoEncoder {
* statistics.
*/
void (*get_stats)(VideoEncoder *encoder, VideoEncoderStats *stats);
+
+ /* The codec being used by the video encoder */
+ SpiceVideoCodecType codec_type;
};
@@ -148,17 +151,30 @@ typedef struct VideoEncoderRateControlCbs {
/* Instantiates the video encoder.
*
+ * @codec_type: The codec to use.
* @starting_bit_rate: An initial estimate of the available stream bit rate
* or zero if the client does not support rate control.
* @cbs: A set of callback methods to be used for rate control.
* @return: A pointer to a structure implementing the VideoEncoder
* methods.
*/
-VideoEncoder* mjpeg_encoder_new(uint64_t starting_bit_rate,
+typedef VideoEncoder* (*new_video_encoder_t)(SpiceVideoCodecType codec_type,
+ uint64_t starting_bit_rate,
+ VideoEncoderRateControlCbs *cbs);
+
+VideoEncoder* mjpeg_encoder_new(SpiceVideoCodecType codec_type,
+ uint64_t starting_bit_rate,
VideoEncoderRateControlCbs *cbs);
#ifdef HAVE_GSTREAMER_1_0
-VideoEncoder* gstreamer_encoder_new(uint64_t starting_bit_rate,
+VideoEncoder* gstreamer_encoder_new(SpiceVideoCodecType codec_type,
+ uint64_t starting_bit_rate,
VideoEncoderRateControlCbs *cbs);
#endif
+typedef struct RedVideoCodec {
+ new_video_encoder_t create;
+ SpiceVideoCodecType type;
+ uint32_t cap;
+} RedVideoCodec;
+
#endif
--
2.8.0.rc3
More information about the Spice-devel
mailing list