[Spice-devel] [spice v14 02/29] server: Enable adding alternative MJPEG video encoders
Frediano Ziglio
fziglio at redhat.com
Wed May 4 09:53:50 UTC 2016
>
> This replaces the original mjpeg_encoder API with a VideoEncoder base
> class which can be reimplemented by other encoders.
> This also renames the members and enums from mjpeg_* to video_*.
>
> Signed-off-by: Francois Gouget <fgouget at codeweavers.com>
Merged
Frediano
> ---
> server/Makefile.am | 2 +-
> server/dcc-send.c | 27 +++++----
> server/dcc.c | 24 ++++----
> server/dcc.h | 2 +-
> server/mjpeg-encoder.c | 85 +++++++++++++++-----------
> server/mjpeg-encoder.h | 102 -------------------------------
> server/stream.c | 42 ++++++-------
> server/stream.h | 4 +-
> server/video-encoder.h | 161
> +++++++++++++++++++++++++++++++++++++++++++++++++
> 9 files changed, 263 insertions(+), 186 deletions(-)
> delete mode 100644 server/mjpeg-encoder.h
> create mode 100644 server/video-encoder.h
>
> diff --git a/server/Makefile.am b/server/Makefile.am
> index a119c86..b548205 100644
> --- a/server/Makefile.am
> +++ b/server/Makefile.am
> @@ -87,7 +87,6 @@ libserver_la_SOURCES = \
> main-channel.c \
> main-channel.h \
> mjpeg-encoder.c \
> - mjpeg-encoder.h \
> red-channel.c \
> red-channel.h \
> red-common.h \
> @@ -125,6 +124,7 @@ libserver_la_SOURCES = \
> sound.h \
> stat.h \
> spicevmc.c \
> + video-encoder.h \
> zlib-encoder.c \
> zlib-encoder.h \
> image-cache.h \
> diff --git a/server/dcc-send.c b/server/dcc-send.c
> index efbc454..fcec8d3 100644
> --- a/server/dcc-send.c
> +++ b/server/dcc-send.c
> @@ -1659,7 +1659,7 @@ static int red_marshall_stream_data(RedChannelClient
> *rcc,
> Stream *stream = drawable->stream;
> SpiceImage *image;
> uint32_t frame_mm_time;
> - int n;
> + uint32_t n;
> int width, height;
> int ret;
>
> @@ -1693,7 +1693,7 @@ static int red_marshall_stream_data(RedChannelClient
> *rcc,
> uint64_t time_now = spice_get_monotonic_time_ns();
> size_t outbuf_size;
>
> - if (!dcc->use_mjpeg_encoder_rate_control) {
> + if (!dcc->use_video_encoder_rate_control) {
> if (time_now - agent->last_send_time < (1000 * 1000 * 1000) /
> agent->fps) {
> agent->frames--;
> #ifdef STREAM_STATS
> @@ -1708,25 +1708,26 @@ static int red_marshall_stream_data(RedChannelClient
> *rcc,
> drawable->red_drawable->mm_time :
> reds_get_mm_time();
> outbuf_size = dcc->send_data.stream_outbuf_size;
> - ret = mjpeg_encoder_encode_frame(agent->mjpeg_encoder,
> - &image->u.bitmap, width, height,
> -
> &drawable->red_drawable->u.copy.src_area,
> - stream->top_down, frame_mm_time,
> - &dcc->send_data.stream_outbuf,
> - &outbuf_size, &n);
> + ret = agent->video_encoder->encode_frame(agent->video_encoder,
> + frame_mm_time,
> + &image->u.bitmap, width,
> height,
> +
> &drawable->red_drawable->u.copy.src_area,
> + stream->top_down,
> + &dcc->send_data.stream_outbuf,
> + &outbuf_size, &n);
> switch (ret) {
> - case MJPEG_ENCODER_FRAME_DROP:
> - spice_assert(dcc->use_mjpeg_encoder_rate_control);
> + case VIDEO_ENCODER_FRAME_DROP:
> + spice_assert(dcc->use_video_encoder_rate_control);
> #ifdef STREAM_STATS
> agent->stats.num_drops_fps++;
> #endif
> return TRUE;
> - case MJPEG_ENCODER_FRAME_UNSUPPORTED:
> + case VIDEO_ENCODER_FRAME_UNSUPPORTED:
> return FALSE;
> - case MJPEG_ENCODER_FRAME_ENCODE_DONE:
> + case VIDEO_ENCODER_FRAME_ENCODE_DONE:
> break;
> default:
> - spice_error("bad return value (%d) from mjpeg_encoder_encode_frame",
> ret);
> + spice_error("bad return value (%d) from VideoEncoder::encode_frame",
> ret);
> return FALSE;
> }
> dcc->send_data.stream_outbuf_size = outbuf_size;
> diff --git a/server/dcc.c b/server/dcc.c
> index 3be1e96..db77cc7 100644
> --- a/server/dcc.c
> +++ b/server/dcc.c
> @@ -346,7 +346,7 @@ static void dcc_init_stream_agents(DisplayChannelClient
> *dcc)
> red_pipe_item_init(&agent->create_item,
> RED_PIPE_ITEM_TYPE_STREAM_CREATE);
> red_pipe_item_init(&agent->destroy_item,
> RED_PIPE_ITEM_TYPE_STREAM_DESTROY);
> }
> - dcc->use_mjpeg_encoder_rate_control =
> + dcc->use_video_encoder_rate_control =
> red_channel_client_test_remote_cap(RED_CHANNEL_CLIENT(dcc),
> SPICE_DISPLAY_CAP_STREAM_REPORT);
> }
>
> @@ -477,9 +477,9 @@ static void
> dcc_destroy_stream_agents(DisplayChannelClient *dcc)
> StreamAgent *agent = &dcc->stream_agents[i];
> region_destroy(&agent->vis_region);
> region_destroy(&agent->clip);
> - if (agent->mjpeg_encoder) {
> - mjpeg_encoder_destroy(agent->mjpeg_encoder);
> - agent->mjpeg_encoder = NULL;
> + if (agent->video_encoder) {
> + agent->video_encoder->destroy(agent->video_encoder);
> + agent->video_encoder = NULL;
> }
> }
> }
> @@ -1393,7 +1393,7 @@ static int
> dcc_handle_stream_report(DisplayChannelClient *dcc,
> }
>
> agent = &dcc->stream_agents[report->stream_id];
> - if (!agent->mjpeg_encoder) {
> + if (!agent->video_encoder) {
> spice_info("stream_report: no encoder for stream id %u. "
> "The stream has probably been destroyed",
> report->stream_id);
> @@ -1407,13 +1407,13 @@ static int
> dcc_handle_stream_report(DisplayChannelClient *dcc,
> return TRUE;
> }
>
> - mjpeg_encoder_client_stream_report(agent->mjpeg_encoder,
> - report->num_frames,
> - report->num_drops,
> - report->start_frame_mm_time,
> - report->end_frame_mm_time,
> - report->last_frame_delay,
> - report->audio_delay);
> + agent->video_encoder->client_stream_report(agent->video_encoder,
> + report->num_frames,
> + report->num_drops,
> + report->start_frame_mm_time,
> + report->end_frame_mm_time,
> + report->last_frame_delay,
> + report->audio_delay);
> return TRUE;
> }
>
> diff --git a/server/dcc.h b/server/dcc.h
> index 509a6c6..1dd53bd 100644
> --- a/server/dcc.h
> +++ b/server/dcc.h
> @@ -109,7 +109,7 @@ struct DisplayChannelClient {
> QRegion surface_client_lossy_region[NUM_SURFACES];
>
> StreamAgent stream_agents[NUM_STREAMS];
> - int use_mjpeg_encoder_rate_control;
> + int use_video_encoder_rate_control;
> uint32_t streams_max_latency;
> uint64_t streams_max_bit_rate;
> bool gl_draw_ongoing;
> diff --git a/server/mjpeg-encoder.c b/server/mjpeg-encoder.c
> index c80febd..e3646db 100644
> --- a/server/mjpeg-encoder.c
> +++ b/server/mjpeg-encoder.c
> @@ -20,7 +20,7 @@
> #endif
>
> #include "red-common.h"
> -#include "mjpeg-encoder.h"
> +#include "video-encoder.h"
> #include "utils.h"
> #include <jerror.h>
> #include <jpeglib.h>
> @@ -154,7 +154,8 @@ typedef struct MJpegEncoderRateControl {
> uint64_t warmup_start_time;
> } MJpegEncoderRateControl;
>
> -struct MJpegEncoder {
> +typedef struct MJpegEncoder {
> + VideoEncoder base;
> uint8_t *row;
> uint32_t row_size;
> int first_frame;
> @@ -166,13 +167,13 @@ struct MJpegEncoder {
> void (*pixel_converter)(void *src, uint8_t *dest);
>
> MJpegEncoderRateControl rate_control;
> - MJpegEncoderRateControlCbs cbs;
> + VideoEncoderRateControlCbs cbs;
>
> /* stats */
> uint64_t starting_bit_rate;
> uint64_t avg_quality;
> uint32_t num_frames;
> -};
> +} MJpegEncoder;
>
> static void mjpeg_encoder_process_server_drops(MJpegEncoder *encoder);
> static uint32_t get_min_required_playback_delay(uint64_t frame_enc_size,
> @@ -184,8 +185,9 @@ static inline int rate_control_is_active(MJpegEncoder*
> encoder)
> return encoder->cbs.get_roundtrip_ms != NULL;
> }
>
> -void mjpeg_encoder_destroy(MJpegEncoder *encoder)
> +static void mjpeg_encoder_destroy(VideoEncoder *video_encoder)
> {
> + MJpegEncoder *encoder = (MJpegEncoder*)video_encoder;
> free(encoder->cinfo.dest);
> jpeg_destroy_compress(&encoder->cinfo);
> free(encoder->row);
> @@ -724,7 +726,7 @@ static int mjpeg_encoder_start_frame(MJpegEncoder
> *encoder,
> interval = (now - rate_control->bit_rate_info.last_frame_time);
>
> if (interval < NSEC_PER_SEC / rate_control->adjusted_fps) {
> - return MJPEG_ENCODER_FRAME_DROP;
> + return VIDEO_ENCODER_FRAME_DROP;
> }
>
> mjpeg_encoder_adjust_params_to_bit_rate(encoder);
> @@ -772,14 +774,14 @@ static int mjpeg_encoder_start_frame(MJpegEncoder
> *encoder,
> break;
> default:
> spice_debug("unsupported format %d", format);
> - return MJPEG_ENCODER_FRAME_UNSUPPORTED;
> + return VIDEO_ENCODER_FRAME_UNSUPPORTED;
> }
>
> if (encoder->pixel_converter != NULL) {
> unsigned int stride = width * 3;
> /* check for integer overflow */
> if (stride < width) {
> - return MJPEG_ENCODER_FRAME_UNSUPPORTED;
> + return VIDEO_ENCODER_FRAME_UNSUPPORTED;
> }
> if (encoder->row_size < stride) {
> encoder->row = spice_realloc(encoder->row, stride);
> @@ -799,7 +801,7 @@ static int mjpeg_encoder_start_frame(MJpegEncoder
> *encoder,
>
> encoder->num_frames++;
> encoder->avg_quality += quality;
> - return MJPEG_ENCODER_FRAME_ENCODE_DONE;
> + return VIDEO_ENCODER_FRAME_ENCODE_DONE;
> }
>
> static int mjpeg_encoder_encode_scanline(MJpegEncoder *encoder,
> @@ -923,27 +925,31 @@ static int encode_frame(MJpegEncoder *encoder, const
> SpiceRect *src,
> return TRUE;
> }
>
> -int mjpeg_encoder_encode_frame(MJpegEncoder *encoder,
> - const SpiceBitmap *bitmap, int width, int
> height,
> - const SpiceRect *src,
> - int top_down, uint32_t frame_mm_time,
> - uint8_t **outbuf, size_t *outbuf_size,
> - int *data_size)
> +static int mjpeg_encoder_encode_frame(VideoEncoder *video_encoder,
> + uint32_t frame_mm_time,
> + const SpiceBitmap *bitmap,
> + int width, int height,
> + const SpiceRect *src, int top_down,
> + uint8_t **outbuf, size_t *outbuf_size,
> + uint32_t *data_size)
> {
> + MJpegEncoder *encoder = (MJpegEncoder*)video_encoder;
> +
> int ret = mjpeg_encoder_start_frame(encoder, bitmap->format,
> - width, height, outbuf, outbuf_size,
> - frame_mm_time);
> - if (ret != MJPEG_ENCODER_FRAME_ENCODE_DONE) {
> + width, height,
> + outbuf, outbuf_size,
> + frame_mm_time);
> + if (ret != VIDEO_ENCODER_FRAME_ENCODE_DONE) {
> return ret;
> }
>
> if (!encode_frame(encoder, src, bitmap, top_down)) {
> - return MJPEG_ENCODER_FRAME_UNSUPPORTED;
> + return VIDEO_ENCODER_FRAME_UNSUPPORTED;
> }
>
> *data_size = mjpeg_encoder_end_frame(encoder);
>
> - return MJPEG_ENCODER_FRAME_ENCODE_DONE;
> + return VIDEO_ENCODER_FRAME_ENCODE_DONE;
> }
>
>
> @@ -1174,14 +1180,15 @@ static uint32_t
> get_min_required_playback_delay(uint64_t frame_enc_size,
> #define MJPEG_VIDEO_VS_AUDIO_LATENCY_FACTOR 1.25
> #define MJPEG_VIDEO_DELAY_TH -15
>
> -void mjpeg_encoder_client_stream_report(MJpegEncoder *encoder,
> - uint32_t num_frames,
> - uint32_t num_drops,
> - uint32_t start_frame_mm_time,
> - uint32_t end_frame_mm_time,
> - int32_t end_frame_delay,
> - uint32_t audio_delay)
> +static void mjpeg_encoder_client_stream_report(VideoEncoder *video_encoder,
> + uint32_t num_frames,
> + uint32_t num_drops,
> + uint32_t start_frame_mm_time,
> + uint32_t end_frame_mm_time,
> + int32_t end_frame_delay,
> + uint32_t audio_delay)
> {
> + MJpegEncoder *encoder = (MJpegEncoder*)video_encoder;
> MJpegEncoderRateControl *rate_control = &encoder->rate_control;
> MJpegEncoderClientState *client_state = &rate_control->client_state;
> uint64_t avg_enc_size = 0;
> @@ -1282,8 +1289,9 @@ void mjpeg_encoder_client_stream_report(MJpegEncoder
> *encoder,
> }
> }
>
> -void mjpeg_encoder_notify_server_frame_drop(MJpegEncoder *encoder)
> +static void mjpeg_encoder_notify_server_frame_drop(VideoEncoder
> *video_encoder)
> {
> + MJpegEncoder *encoder = (MJpegEncoder*)video_encoder;
> encoder->rate_control.server_state.num_frames_dropped++;
> mjpeg_encoder_process_server_drops(encoder);
> }
> @@ -1320,24 +1328,33 @@ static void
> mjpeg_encoder_process_server_drops(MJpegEncoder *encoder)
> server_state->num_frames_dropped = 0;
> }
>
> -uint64_t mjpeg_encoder_get_bit_rate(MJpegEncoder *encoder)
> +static uint64_t mjpeg_encoder_get_bit_rate(VideoEncoder *video_encoder)
> {
> + MJpegEncoder *encoder = (MJpegEncoder*)video_encoder;
> return encoder->rate_control.byte_rate * 8;
> }
>
> -void mjpeg_encoder_get_stats(MJpegEncoder *encoder, MJpegEncoderStats
> *stats)
> +static void mjpeg_encoder_get_stats(VideoEncoder *video_encoder,
> + VideoEncoderStats *stats)
> {
> + MJpegEncoder *encoder = (MJpegEncoder*)video_encoder;
> spice_assert(encoder != NULL && stats != NULL);
> stats->starting_bit_rate = encoder->starting_bit_rate;
> - stats->cur_bit_rate = mjpeg_encoder_get_bit_rate(encoder);
> + stats->cur_bit_rate = mjpeg_encoder_get_bit_rate(video_encoder);
> stats->avg_quality = (double)encoder->avg_quality / encoder->num_frames;
> }
>
> -MJpegEncoder *mjpeg_encoder_new(uint64_t starting_bit_rate,
> - MJpegEncoderRateControlCbs *cbs)
> +VideoEncoder *mjpeg_encoder_new(uint64_t starting_bit_rate,
> + VideoEncoderRateControlCbs *cbs)
> {
> MJpegEncoder *encoder = spice_new0(MJpegEncoder, 1);
>
> + 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->first_frame = TRUE;
> encoder->rate_control.byte_rate = starting_bit_rate / 8;
> encoder->starting_bit_rate = starting_bit_rate;
> @@ -1357,5 +1374,5 @@ MJpegEncoder *mjpeg_encoder_new(uint64_t
> starting_bit_rate,
> encoder->cinfo.err = jpeg_std_error(&encoder->jerr);
> jpeg_create_compress(&encoder->cinfo);
>
> - return encoder;
> + return (VideoEncoder*)encoder;
> }
> diff --git a/server/mjpeg-encoder.h b/server/mjpeg-encoder.h
> deleted file mode 100644
> index 4d871ff..0000000
> --- a/server/mjpeg-encoder.h
> +++ /dev/null
> @@ -1,102 +0,0 @@
> -/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
> -/*
> - Copyright (C) 2009 Red Hat, Inc.
> -
> - This library is free software; you can redistribute it and/or
> - modify it under the terms of the GNU Lesser General Public
> - License as published by the Free Software Foundation; either
> - version 2.1 of the License, or (at your option) any later version.
> -
> - This library is distributed in the hope that it will be useful,
> - but WITHOUT ANY WARRANTY; without even the implied warranty of
> - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> - Lesser General Public License for more details.
> -
> - You should have received a copy of the GNU Lesser General Public
> - License along with this library; if not, see
> <http://www.gnu.org/licenses/>.
> -*/
> -
> -#ifndef _H_MJPEG_ENCODER
> -#define _H_MJPEG_ENCODER
> -
> -#include "red-common.h"
> -
> -enum {
> - MJPEG_ENCODER_FRAME_UNSUPPORTED = -1,
> - MJPEG_ENCODER_FRAME_DROP,
> - MJPEG_ENCODER_FRAME_ENCODE_DONE,
> -};
> -
> -typedef struct MJpegEncoder MJpegEncoder;
> -
> -/*
> - * Callbacks required for controling and adjusting
> - * the stream bit rate:
> - * @opaque: a pointer to be passed to the rate control callbacks.
> - * get_roundtrip_ms: roundtrip time in milliseconds
> - * get_source_fps: the input frame rate (#frames per second), i.e.,
> - * the rate of frames arriving from the guest to spice-server,
> - * before any drops.
> - */
> -typedef struct MJpegEncoderRateControlCbs {
> - void *opaque;
> - uint32_t (*get_roundtrip_ms)(void *opaque);
> - uint32_t (*get_source_fps)(void *opaque);
> - void (*update_client_playback_delay)(void *opaque, uint32_t delay_ms);
> -} MJpegEncoderRateControlCbs;
> -
> -typedef struct MJpegEncoderStats {
> - uint64_t starting_bit_rate;
> - uint64_t cur_bit_rate;
> - double avg_quality;
> -} MJpegEncoderStats;
> -
> -MJpegEncoder *mjpeg_encoder_new(uint64_t starting_bit_rate,
> - MJpegEncoderRateControlCbs *cbs);
> -void mjpeg_encoder_destroy(MJpegEncoder *encoder);
> -
> -int mjpeg_encoder_encode_frame(MJpegEncoder *encoder,
> - const SpiceBitmap *bitmap, int width, int
> height,
> - const SpiceRect *src,
> - int top_down, uint32_t frame_mm_time,
> - uint8_t **outbuf, size_t *outbuf_size,
> - int *data_size);
> -
> -/*
> - * bit rate control
> - */
> -
> -/*
> - * Data that should be periodically obtained from the client. The report
> contains:
> - * num_frames : the number of frames that reached the client during
> the time
> - * the report is referring to.
> - * num_drops : the part of the above frames that was dropped by the
> client due to
> - * late arrival time.
> - * start_frame_mm_time: the mm_time of the first frame included in the
> report
> - * end_frame_mm_time : the mm_time of the last_frame included in the report
> - * end_frame_delay : (end_frame_mm_time - client_mm_time)
> - * audio delay : the latency of the audio playback.
> - * If there is no audio playback, set it to MAX_UINT.
> - *
> - */
> -void mjpeg_encoder_client_stream_report(MJpegEncoder *encoder,
> - uint32_t num_frames,
> - uint32_t num_drops,
> - uint32_t start_frame_mm_time,
> - uint32_t end_frame_mm_time,
> - int32_t end_frame_delay,
> - uint32_t audio_delay);
> -
> -/*
> - * Notify the encoder each time a frame is dropped due to pipe
> - * congestion.
> - * We can deduce the client state by the frame dropping rate in the server.
> - * Monitoring the frame drops can help in fine tuning the playback
> parameters
> - * when the client reports are delayed.
> - */
> -void mjpeg_encoder_notify_server_frame_drop(MJpegEncoder *encoder);
> -
> -uint64_t mjpeg_encoder_get_bit_rate(MJpegEncoder *encoder);
> -void mjpeg_encoder_get_stats(MJpegEncoder *encoder, MJpegEncoderStats
> *stats);
> -
> -#endif
> diff --git a/server/stream.c b/server/stream.c
> index 5020eb0..8884480 100644
> --- a/server/stream.c
> +++ b/server/stream.c
> @@ -32,10 +32,10 @@ void stream_agent_stats_print(StreamAgent *agent)
> #ifdef STREAM_STATS
> StreamStats *stats = &agent->stats;
> double passed_mm_time = (stats->end - stats->start) / 1000.0;
> - MJpegEncoderStats encoder_stats = {0};
> + VideoEncoderStats encoder_stats = {0};
>
> - if (agent->mjpeg_encoder) {
> - mjpeg_encoder_get_stats(agent->mjpeg_encoder, &encoder_stats);
> + if (agent->video_encoder) {
> + agent->video_encoder->get_stats(agent->video_encoder,
> &encoder_stats);
> }
>
> spice_debug("stream=%p dim=(%dx%d) #in-frames=%"PRIu64" #in-avg-fps=%.2f
> #out-frames=%"PRIu64" "
> @@ -79,8 +79,8 @@ void stream_stop(DisplayChannel *display, Stream *stream)
> region_clear(&stream_agent->vis_region);
> region_clear(&stream_agent->clip);
> spice_assert(!red_pipe_item_is_linked(&stream_agent->destroy_item));
> - if (stream_agent->mjpeg_encoder &&
> dcc->use_mjpeg_encoder_rate_control) {
> - uint64_t stream_bit_rate =
> mjpeg_encoder_get_bit_rate(stream_agent->mjpeg_encoder);
> + if (stream_agent->video_encoder &&
> dcc->use_video_encoder_rate_control) {
> + uint64_t stream_bit_rate =
> stream_agent->video_encoder->get_bit_rate(stream_agent->video_encoder);
>
> if (stream_bit_rate > dcc->streams_max_bit_rate) {
> spice_debug("old max-bit-rate=%.2f new=%.2f",
> @@ -338,7 +338,7 @@ static void before_reattach_stream(DisplayChannel
> *display,
> dcc = dpi->dcc;
> agent = &dcc->stream_agents[index];
>
> - if (!dcc->use_mjpeg_encoder_rate_control &&
> + if (!dcc->use_video_encoder_rate_control &&
> !dcc->common.is_low_bandwidth) {
> continue;
> }
> @@ -347,8 +347,8 @@ static void before_reattach_stream(DisplayChannel
> *display,
> #ifdef STREAM_STATS
> agent->stats.num_drops_pipe++;
> #endif
> - if (dcc->use_mjpeg_encoder_rate_control) {
> -
> mjpeg_encoder_notify_server_frame_drop(agent->mjpeg_encoder);
> + if (dcc->use_video_encoder_rate_control) {
> +
> agent->video_encoder->notify_server_frame_drop(agent->video_encoder);
> } else {
> ++agent->drops;
> }
> @@ -361,7 +361,7 @@ static void before_reattach_stream(DisplayChannel
> *display,
>
> agent = &dcc->stream_agents[index];
>
> - if (dcc->use_mjpeg_encoder_rate_control) {
> + if (dcc->use_video_encoder_rate_control) {
> continue;
> }
> if (agent->frames / agent->fps < FPS_TEST_INTERVAL) {
> @@ -594,7 +594,7 @@ static void
> dcc_update_streams_max_latency(DisplayChannelClient *dcc, StreamAgen
> }
> for (i = 0; i < NUM_STREAMS; i++) {
> StreamAgent *other_agent = &dcc->stream_agents[i];
> - if (other_agent == remove_agent || !other_agent->mjpeg_encoder) {
> + if (other_agent == remove_agent || !other_agent->video_encoder) {
> continue;
> }
> if (other_agent->client_required_latency > new_max_latency) {
> @@ -714,19 +714,19 @@ void dcc_create_stream(DisplayChannelClient *dcc,
> Stream *stream)
> agent->fps = MAX_FPS;
> agent->dcc = dcc;
>
> - if (dcc->use_mjpeg_encoder_rate_control) {
> - MJpegEncoderRateControlCbs mjpeg_cbs;
> + if (dcc->use_video_encoder_rate_control) {
> + VideoEncoderRateControlCbs video_cbs;
> uint64_t initial_bit_rate;
>
> - mjpeg_cbs.opaque = agent;
> - mjpeg_cbs.get_roundtrip_ms = get_roundtrip_ms;
> - mjpeg_cbs.get_source_fps = get_source_fps;
> - mjpeg_cbs.update_client_playback_delay =
> update_client_playback_delay;
> + video_cbs.opaque = agent;
> + video_cbs.get_roundtrip_ms = get_roundtrip_ms;
> + video_cbs.get_source_fps = get_source_fps;
> + video_cbs.update_client_playback_delay =
> update_client_playback_delay;
>
> initial_bit_rate = get_initial_bit_rate(dcc, stream);
> - agent->mjpeg_encoder = mjpeg_encoder_new(initial_bit_rate,
> &mjpeg_cbs);
> + agent->video_encoder = mjpeg_encoder_new(initial_bit_rate,
> &video_cbs);
> } else {
> - agent->mjpeg_encoder = mjpeg_encoder_new(0, NULL);
> + agent->video_encoder = mjpeg_encoder_new(0, NULL);
> }
> red_channel_client_pipe_add(RED_CHANNEL_CLIENT(dcc),
> &agent->create_item);
>
> @@ -752,9 +752,9 @@ void stream_agent_stop(StreamAgent *agent)
> DisplayChannelClient *dcc = agent->dcc;
>
> dcc_update_streams_max_latency(dcc, agent);
> - if (agent->mjpeg_encoder) {
> - mjpeg_encoder_destroy(agent->mjpeg_encoder);
> - agent->mjpeg_encoder = NULL;
> + if (agent->video_encoder) {
> + agent->video_encoder->destroy(agent->video_encoder);
> + agent->video_encoder = NULL;
> }
> }
>
> diff --git a/server/stream.h b/server/stream.h
> index bff7ca7..de6a132 100644
> --- a/server/stream.h
> +++ b/server/stream.h
> @@ -20,7 +20,7 @@
>
> #include <glib.h>
> #include "utils.h"
> -#include "mjpeg-encoder.h"
> +#include "video-encoder.h"
> #include "common/region.h"
> #include "red-channel.h"
> #include "image-cache.h"
> @@ -85,7 +85,7 @@ typedef struct StreamAgent {
> RedPipeItem destroy_item;
> Stream *stream;
> uint64_t last_send_time;
> - MJpegEncoder *mjpeg_encoder;
> + VideoEncoder *video_encoder;
> DisplayChannelClient *dcc;
>
> int frames;
> diff --git a/server/video-encoder.h b/server/video-encoder.h
> new file mode 100644
> index 0000000..9833e13
> --- /dev/null
> +++ b/server/video-encoder.h
> @@ -0,0 +1,161 @@
> +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
> +/*
> + Copyright (C) 2009 Red Hat, Inc.
> + Copyright (C) 2015 Jeremy White
> + Copyright (C) 2015 Francois Gouget
> +
> + This library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + This library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with this library; if not, see
> <http://www.gnu.org/licenses/>.
> +*/
> +
> +#ifndef _H_VIDEO_ENCODER
> +#define _H_VIDEO_ENCODER
> +
> +enum {
> + VIDEO_ENCODER_FRAME_UNSUPPORTED = -1,
> + VIDEO_ENCODER_FRAME_DROP,
> + VIDEO_ENCODER_FRAME_ENCODE_DONE,
> +};
> +
> +typedef struct VideoEncoderStats {
> + uint64_t starting_bit_rate;
> + uint64_t cur_bit_rate;
> + double avg_quality;
> +} VideoEncoderStats;
> +
> +typedef struct VideoEncoder VideoEncoder;
> +struct VideoEncoder {
> + /* Releases the video encoder's resources */
> + void (*destroy)(VideoEncoder *encoder);
> +
> + /* Compresses the specified src image area into the outbuf buffer.
> + *
> + * @encoder: The video encoder.
> + * @frame_mm_time: The frame's mm-time timestamp in milliseconds.
> + * @bitmap: The Spice screen.
> + * @src: A rectangle specifying the area occupied by the
> video.
> + * @top_down: If true the first video line is specified by src.top.
> + * @outbuf: The buffer for the compressed frame. This must either
> + * be NULL or point to a buffer allocated by malloc
> + * since it may be reallocated, if its size is too
> small.
> + * @outbuf_size: The size of the outbuf buffer.
> + * @data_size: The size of the compressed frame.
> + * @return:
> + * VIDEO_ENCODER_FRAME_ENCODE_DONE if successful.
> + * VIDEO_ENCODER_FRAME_UNSUPPORTED if the frame cannot be encoded.
> + * VIDEO_ENCODER_FRAME_DROP if the frame was dropped. This value can
> + * only happen if rate control is active.
> + */
> + int (*encode_frame)(VideoEncoder *encoder, uint32_t frame_mm_time,
> + const SpiceBitmap *bitmap, int width, int height,
> + const SpiceRect *src, int top_down,
> + uint8_t **outbuf, size_t *outbuf_size,
> + uint32_t *data_size);
> +
> + /*
> + * Bit rate control methods.
> + */
> +
> + /* When rate control is active statistics are periodically obtained from
> + * the client and sent to the video encoder through this method.
> + *
> + * @encoder: The video encoder.
> + * @num_frames: The number of frames that reached the client during
> + * the time period the report is referring to.
> + * @num_drops: The part of the above frames that was dropped by the
> + * client due to late arrival time.
> + * @start_frame_mm_time: The mm_time of the first frame included in the
> + * report.
> + * @end_frame_mm_time: The mm_time of the last frame included in the
> + * report.
> + * @end_frame_delay: This indicates how long in advance the client
> + * received the last frame before having to display it.
> + * @audio delay: The latency of the audio playback or MAX_UINT if it
> + * is not tracked.
> + */
> + void (*client_stream_report)(VideoEncoder *encoder,
> + uint32_t num_frames, uint32_t num_drops,
> + uint32_t start_frame_mm_time,
> + uint32_t end_frame_mm_time,
> + int32_t end_frame_delay, uint32_t
> audio_delay);
> +
> + /* This notifies the video encoder each time a frame is dropped due to
> + * pipe congestion.
> + *
> + * Note that frames are being dropped before they are encoded and that
> + * there may be any number of encoded frames in the network queue.
> + * The client reports provide richer and typically more reactive
> + * information for fine tuning the playback parameters but this function
> + * provides a fallback when client reports are getting delayed or are
> not
> + * supported by the client.
> + *
> + * @encoder: The video encoder.
> + */
> + void (*notify_server_frame_drop)(VideoEncoder *encoder);
> +
> + /* This queries the video encoder's current bit rate.
> + *
> + * @encoder: The video encoder.
> + * @return: The current bit rate in bits per second.
> + */
> + uint64_t (*get_bit_rate)(VideoEncoder *encoder);
> +
> + /* Collects video statistics.
> + *
> + * @encoder: The video encoder.
> + * @stats: A VideoEncoderStats structure to fill with the collected
> + * statistics.
> + */
> + void (*get_stats)(VideoEncoder *encoder, VideoEncoderStats *stats);
> +};
> +
> +
> +/* When rate control is active the video encoder can use these callbacks to
> + * figure out how to adjust the stream bit rate and adjust some stream
> + * parameters.
> + */
> +typedef struct VideoEncoderRateControlCbs {
> + /* The opaque parameter for the callbacks */
> + void *opaque;
> +
> + /* Returns the stream's estimated roundtrip time in milliseconds. */
> + uint32_t (*get_roundtrip_ms)(void *opaque);
> +
> + /* Returns the estimated input frame rate.
> + *
> + * This is the number of frames per second arriving from the guest to
> + * spice-server, before any drops.
> + */
> + uint32_t (*get_source_fps)(void *opaque);
> +
> + /* Informs the client of the minimum playback delay.
> + *
> + * @delay_ms: The minimum number of milliseconds required for the
> + * frames to reach the client.
> + */
> + void (*update_client_playback_delay)(void *opaque, uint32_t delay_ms);
> +} VideoEncoderRateControlCbs;
> +
> +
> +/* Instantiates the video encoder.
> + *
> + * @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,
> + VideoEncoderRateControlCbs *cbs);
> +
> +#endif
More information about the Spice-devel
mailing list