@@ -1637,6 +1637,60 @@ static void red_release_video_encoder_buffer(uint8_t *data, void *opaque)
buffer->free(buffer);
}
+static void red_init_display_stream_data(DisplayChannelClient *dcc,
+ SpiceMarshaller *base_marshaller,
+ Drawable *drawable, int stream_id,
+ VideoBuffer *outbuf, int is_sized)
+{
+ SpiceMsgDisplayStreamData stream_data;
+ SpiceMsgDisplayStreamDataSized stream_data_sized;
+ SpiceStreamDataHeader *base;
+ SpiceCopy *copy;
+ uint32_t frame_mm_time = reds_get_mm_time();
+ uint16_t msg_type = is_sized ? SPICE_MSG_DISPLAY_STREAM_DATA_SIZED :
+ SPICE_MSG_DISPLAY_STREAM_DATA;
+
+ dcc->init_send_data(msg_type);
+
+ base = is_sized ? &stream_data_sized.base : &stream_data.base;
+ base->id = stream_id;
+ base->multi_media_time = frame_mm_time;
+ stream_data.data_size = outbuf->size;
+
+ if (is_sized) {
+ copy = &drawable->red_drawable->u.copy;
+ frame_mm_time = drawable->red_drawable->mm_time ?
+ drawable->red_drawable->mm_time :
+ reds_get_mm_time();
+
+ stream_data_sized.base.multi_media_time = frame_mm_time;
+ stream_data_sized.width = copy->src_area.right - copy->src_area.left;
+ stream_data_sized.height = copy->src_area.bottom - copy->src_area.top;
+ stream_data_sized.dest = drawable->red_drawable->bbox;
+ stream_data_sized.data_size = outbuf->size;
+
+ spice_debug("stream %d: sized frame: dest ==> ",
+ stream_data_sized.base.id);
+ rect_debug(&stream_data_sized.dest);
+ spice_marshall_msg_display_stream_data_sized(base_marshaller,
+ &stream_data_sized);
+ } else {
+ spice_marshall_msg_display_stream_data(base_marshaller, &stream_data);
+ }
+
+ spice_marshaller_add_by_ref_full(base_marshaller, outbuf->data,
+ outbuf->size,
+ &red_release_video_encoder_buffer,
+ outbuf);
+#ifdef STREAM_STATS
+ VideoStreamAgent *agent = &dcc->priv->stream_agents[stream_id];
+
+ agent->stats.num_frames_sent++;
+ agent->stats.size_sent += outbuf->size;
+ agent->stats.end = frame_mm_time;
+#endif
+}
+
static bool red_marshall_stream_data(DisplayChannelClient *dcc,
SpiceMarshaller *base_marshaller,
Drawable *drawable)
@@ -1693,41 +1747,81 @@ static bool red_marshall_stream_data(DisplayChannelClient *dcc,
return FALSE;
}
- if (!is_sized) {
- SpiceMsgDisplayStreamData stream_data;
+ red_init_display_stream_data(dcc, base_marshaller, drawable,
+ stream_id, outbuf, is_sized);
+ return TRUE;
+}
- dcc->init_send_data(SPICE_MSG_DISPLAY_STREAM_DATA);
+static void red_free_cb(VideoEncoderDmabufData *dmabuf_data)
+{
+ auto dcc = static_cast<DisplayChannelClient *>(dmabuf_data->dcc);
+ DisplayChannel *display = DCC_TO_DC(dcc);
- stream_data.base.id = stream_id;
- stream_data.base.multi_media_time = frame_mm_time;
- stream_data.data_size = outbuf->size;
+ dcc->priv->gl_draw_ongoing = false;
+ display_channel_gl_draw_done(display);
+ delete dmabuf_data;
+}
- spice_marshall_msg_display_stream_data(base_marshaller, &stream_data);
- } else {
- SpiceMsgDisplayStreamDataSized stream_data;
+static void red_marshall_gl_draw_stream(DisplayChannelClient *dcc,
+ SpiceMarshaller *base_marshaller)
+{
+ DisplayChannel *display = DCC_TO_DC(dcc);
+ VideoStream *stream = display->priv->gl_draw_stream;
+ int stream_id = display_channel_get_video_stream_id(display, stream);
+ VideoStreamAgent *agent = &dcc->priv->stream_agents[stream_id];
- dcc->init_send_data(SPICE_MSG_DISPLAY_STREAM_DATA_SIZED);
+ if (!agent->video_encoder || !agent->video_encoder->encode_dmabuf) {
+ spice_warning("No video encoder available for this stream");
+ return;
+ }
- stream_data.base.id = stream_id;
- stream_data.base.multi_media_time = frame_mm_time;
- stream_data.data_size = outbuf->size;
- stream_data.width = copy->src_area.right - copy->src_area.left;
- stream_data.height = copy->src_area.bottom - copy->src_area.top;
- stream_data.dest = drawable->red_drawable->bbox;
+ VideoEncoderDmabufData *dmabuf_data = new VideoEncoderDmabufData;
+ if (!dmabuf_data) {
+ spice_warning("Cannot create memory for dmabuf data");
+ return;
+ }
- spice_debug("stream %d: sized frame: dest ==> ", stream_data.base.id);
- rect_debug(&stream_data.dest);
- spice_marshall_msg_display_stream_data_sized(base_marshaller, &stream_data);
+ QXLInstance* qxl = display->priv->qxl;
+ SpiceMsgDisplayGlScanoutUnix *scanout = red_qxl_get_gl_scanout(qxl);
+ if (!scanout) {
+ spice_warning("Cannot access scanout");
+ delete dmabuf_data;
+ return;
}
- spice_marshaller_add_by_ref_full(base_marshaller, outbuf->data, outbuf->size,
- &red_release_video_encoder_buffer, outbuf);
+
+ dmabuf_data->drm_dma_buf_fd = scanout->drm_dma_buf_fd;
+ dmabuf_data->width = stream->width;
+ dmabuf_data->height = stream->height;
+ dmabuf_data->stride = stream->stride;
+ dmabuf_data->dcc = dcc;
+ dmabuf_data->free = red_free_cb;
+ red_qxl_put_gl_scanout(qxl, scanout);
+
+ VideoBuffer *outbuf;
+ VideoEncodeResults ret;
+
+ ret = agent->video_encoder->encode_dmabuf(agent->video_encoder,
+ reds_get_mm_time(),
+ dmabuf_data, &outbuf);
+
+ if (ret != VIDEO_ENCODER_FRAME_ENCODE_DONE) {
+ if (ret == VIDEO_ENCODER_FRAME_DROP) {
#ifdef STREAM_STATS
- agent->stats.num_frames_sent++;
- agent->stats.size_sent += outbuf->size;
- agent->stats.end = frame_mm_time;
+ agent->stats.num_drops_fps++;
#endif
+ } else {
+ spice_warning("bad ret value (%d) from VideoEncoder::encode_dmabuf",
+ ret);
+ }
- return TRUE;
+ dcc->priv->gl_draw_ongoing = false;
+ display_channel_gl_draw_done(display);
+ delete dmabuf_data;
+ return;
+ }
+
+ red_init_display_stream_data(dcc, base_marshaller, nullptr,
+ stream_id, outbuf, false);
}
static inline void marshall_inval_palette(RedChannelClient *rcc,
@@ -2126,6 +2220,8 @@ static void marshall_stream_start(DisplayChannelClient *dcc,
if (stream->current) {
RedDrawable *red_drawable = stream->current->red_drawable.get();
stream_create.clip = red_drawable->clip;
+ } else if (stream == DCC_TO_DC(dcc)->priv->gl_draw_stream){
+ stream_create.clip.type = SPICE_CLIP_TYPE_NONE;
} else {
stream_create.clip.type = SPICE_CLIP_TYPE_RECTS;
clip_rects.num_rects = 0;
@@ -2275,14 +2371,20 @@ static void marshall_gl_scanout(DisplayChannelClient *dcc,
red_qxl_put_gl_scanout(qxl, scanout);
}
-static void marshall_gl_draw(RedChannelClient *rcc,
+static void marshall_gl_draw(DisplayChannelClient *dcc,
SpiceMarshaller *m,
RedPipeItem *item)
{
auto p = static_cast<RedGlDrawItem*>(item);
- rcc->init_send_data(SPICE_MSG_DISPLAY_GL_DRAW);
- spice_marshall_msg_display_gl_draw(m, &p->draw);
+ if (dcc->is_gl_client()) {
+ dcc->init_send_data(SPICE_MSG_DISPLAY_GL_DRAW);
+ spice_marshall_msg_display_gl_draw(m, &p->draw);
+ } else if (DCC_TO_DC(dcc)->priv->gl_draw_stream) {
+ red_marshall_gl_draw_stream(dcc, m);
+ } else {
+ spice_warning("nothing to send to the client");
+ }
}
@@ -56,6 +56,15 @@ typedef struct VideoEncoderStats {
double avg_quality;
} VideoEncoderStats;
+typedef struct VideoEncoderDmabufData {
+ int drm_dma_buf_fd;
+ uint32_t width;
+ uint32_t height;
+ uint32_t stride;
+ void *dcc;
+ void (*free)(struct VideoEncoderDmabufData*);
+} VideoEncoderDmabufData;
+
typedef struct VideoEncoder VideoEncoder;
struct VideoEncoder {
/* Releases the video encoder's resources */
@@ -84,6 +93,10 @@ struct VideoEncoder {
const SpiceRect *src, int top_down,
gpointer bitmap_opaque, VideoBuffer** outbuf);
+ VideoEncodeResults (*encode_dmabuf)(VideoEncoder *encoder, uint32_t frame_mm_time,
+ VideoEncoderDmabufData *dmabuf_data,
+ VideoBuffer** outbuf);
+
/*
* Bit rate control methods.
*/