[Spice-devel] [spice v15 16/21] streaming: Give up after a while if GStreamer cannot handle the video
Francois Gouget
fgouget at codeweavers.com
Thu May 26 15:18:20 UTC 2016
This typically happens when sending very small frames (less than
16 pixels in one dimension) to the x264enc encoder.
This avoids repeatedly wasting time rebuilding the pipeline.
Signed-off-by: Francois Gouget <fgouget at codeweavers.com>
---
server/gstreamer-encoder.c | 61 ++++++++++++++++++++++++++++++++++------------
1 file changed, 46 insertions(+), 15 deletions(-)
diff --git a/server/gstreamer-encoder.c b/server/gstreamer-encoder.c
index 6129560..f6fb87e 100644
--- a/server/gstreamer-encoder.c
+++ b/server/gstreamer-encoder.c
@@ -82,6 +82,9 @@ typedef struct SpiceGstEncoder {
const SpiceFormatForGStreamer *format;
SpiceBitmapFmt spice_format;
+ /* Number of consecutive frame encoding errors. */
+ uint32_t errors;
+
/* ---------- GStreamer pipeline ---------- */
/* Pointers to the GStreamer pipeline elements. If pipeline is NULL the
@@ -830,15 +833,35 @@ static int get_physical_core_count(void)
return physical_core_count;
}
-/* A helper for spice_gst_encoder_encode_frame() */
+static const gchar* get_gst_codec_name(SpiceGstEncoder *encoder)
+{
+ switch (encoder->base.codec_type)
+ {
+ case SPICE_VIDEO_CODEC_TYPE_MJPEG:
+ return "avenc_mjpeg";
+ case SPICE_VIDEO_CODEC_TYPE_VP8:
+ return "vp8enc";
+ case SPICE_VIDEO_CODEC_TYPE_H264:
+ return "x264enc";
+ default:
+ /* gstreamer_encoder_new() should have rejected this codec type */
+ spice_warning("unsupported codec type %d", encoder->base.codec_type);
+ return NULL;
+ }
+}
+
static gboolean create_pipeline(SpiceGstEncoder *encoder)
{
- gchar *gstenc;
+ const gchar* gstenc_name = get_gst_codec_name(encoder);
+ if (!gstenc_name) {
+ return FALSE;
+ }
+ gchar* gstenc_opts;
switch (encoder->base.codec_type)
{
case SPICE_VIDEO_CODEC_TYPE_MJPEG:
/* Set max-threads to ensure zero-frame latency */
- gstenc = g_strdup("avenc_mjpeg max-threads=1");
+ gstenc_opts = g_strdup("max-threads=1");
break;
case SPICE_VIDEO_CODEC_TYPE_VP8: {
/* See http://www.webmproject.org/docs/encoder-parameters/
@@ -862,7 +885,7 @@ static gboolean create_pipeline(SpiceGstEncoder *encoder)
*/
int threads = get_physical_core_count();
int parts = threads < 2 ? 0 : threads < 4 ? 1 : threads < 8 ? 2 : 3;
- gstenc = g_strdup_printf("vp8enc end-usage=cbr min-quantizer=10 error-resilient=default lag-in-frames=0 deadline=1 cpu-used=4 threads=%d token-partitions=%d", threads, parts);
+ gstenc_opts = g_strdup_printf("end-usage=cbr min-quantizer=10 error-resilient=default lag-in-frames=0 deadline=1 cpu-used=4 threads=%d token-partitions=%d", threads, parts);
break;
}
case SPICE_VIDEO_CODEC_TYPE_H264:
@@ -872,7 +895,7 @@ static gboolean create_pipeline(SpiceGstEncoder *encoder)
* - Set intra-refresh to get more uniform compressed frame sizes,
* thus helping with streaming.
*/
- gstenc = g_strdup("x264enc byte-stream=true aud=true qp-min=15 tune=4 sliced-threads=true speed-preset=ultrafast intra-refresh=true");
+ gstenc_opts = g_strdup("byte-stream=true aud=true qp-min=15 tune=4 sliced-threads=true speed-preset=ultrafast intra-refresh=true");
break;
default:
/* gstreamer_encoder_new() should have rejected this codec type */
@@ -881,10 +904,10 @@ static gboolean create_pipeline(SpiceGstEncoder *encoder)
}
GError *err = NULL;
- gchar *desc = g_strdup_printf("appsrc is-live=true format=time do-timestamp=true name=src ! videoconvert ! %s name=encoder ! appsink name=sink", gstenc);
+ gchar *desc = g_strdup_printf("appsrc is-live=true format=time do-timestamp=true name=src ! videoconvert ! %s %s name=encoder ! appsink name=sink", gstenc_name, gstenc_opts);
spice_debug("GStreamer pipeline: %s", desc);
encoder->pipeline = gst_parse_launch_full(desc, NULL, GST_PARSE_FLAG_FATAL_ERRORS, &err);
- g_free(gstenc);
+ g_free(gstenc_opts);
g_free(desc);
if (!encoder->pipeline || err) {
spice_warning("GStreamer error: %s", err->message);
@@ -899,20 +922,12 @@ static gboolean create_pipeline(SpiceGstEncoder *encoder)
encoder->gstenc = gst_bin_get_by_name(GST_BIN(encoder->pipeline), "encoder");
encoder->appsink = GST_APP_SINK(gst_bin_get_by_name(GST_BIN(encoder->pipeline), "sink"));
-#ifdef HAVE_GSTREAMER_0_10
- GstAppSinkCallbacks appsink_cbs = {NULL, NULL, &new_sample, NULL, {NULL}};
-#else
GstAppSinkCallbacks appsink_cbs = {NULL, NULL, &new_sample, {NULL}};
-#endif
gst_app_sink_set_callbacks(encoder->appsink, &appsink_cbs, encoder, NULL);
/* Hook into the bus so we can handle errors */
GstBus *bus = gst_element_get_bus(encoder->pipeline);
-#ifdef HAVE_GSTREAMER_0_10
- gst_bus_set_sync_handler(bus, handle_pipeline_message, encoder);
-#else
gst_bus_set_sync_handler(bus, handle_pipeline_message, encoder, NULL);
-#endif
gst_object_unref(bus);
if (encoder->base.codec_type == SPICE_VIDEO_CODEC_TYPE_MJPEG) {
@@ -1327,6 +1342,7 @@ static int spice_gst_encoder_encode_frame(VideoEncoder *video_encoder,
encoder->format = map_format(bitmap->format);
if (!encoder->format) {
spice_warning("unable to map format type %d", bitmap->format);
+ encoder->errors = 4;
return VIDEO_ENCODER_FRAME_UNSUPPORTED;
}
encoder->spice_format = bitmap->format;
@@ -1342,6 +1358,19 @@ static int spice_gst_encoder_encode_frame(VideoEncoder *video_encoder,
} else if (encoder->pipeline) {
set_pipeline_changes(encoder, SPICE_GST_VIDEO_PIPELINE_CAPS);
}
+ encoder->errors = 0;
+ } else if (encoder->errors >= 3) {
+ /* The pipeline keeps failing to handle the frames we send it, which
+ * is usually because they are too small (mouse pointer-sized).
+ * So give up until something changes.
+ */
+ if (encoder->errors == 3) {
+ spice_debug("%s cannot compress %dx%d:%dbpp frames",
+ get_gst_codec_name(encoder), encoder->width,
+ encoder->height, encoder->format->bpp);
+ encoder->errors++;
+ }
+ return VIDEO_ENCODER_FRAME_UNSUPPORTED;
}
if (rate_control_is_active(encoder) &&
@@ -1352,6 +1381,7 @@ static int spice_gst_encoder_encode_frame(VideoEncoder *video_encoder,
}
if (!configure_pipeline(encoder)) {
+ encoder->errors++;
return VIDEO_ENCODER_FRAME_UNSUPPORTED;
}
@@ -1366,6 +1396,7 @@ static int spice_gst_encoder_encode_frame(VideoEncoder *video_encoder,
* from scratch.
*/
free_pipeline(encoder);
+ encoder->errors++;
}
}
--
2.8.1
More information about the Spice-devel
mailing list