0
点赞
收藏
分享

微信扫一扫

SPICE如何开启H.264编码

皮皮球场 2022-04-17 阅读 202
c++云计算

Spice如何开启H.264编码

太长不看:

  1. 保证服务端和客户端均正确安装GStreamer套件,才能启用H.264的编码和解码
  2. 修改SPICE源码中的codecs优先级
// reds.cpp
// 把默认codecs中的第一个改为gstreamer:h264(前置条件是客户端和服务端都正确安装gstreamer)
static const char default_video_codecs[] = "gstreamer:h264;" GSTREAMER_CODECS;

关于流数据编码的设置,入口代码在客户端连接上SPICE-SERVER的回调函数中,即 DisplayChannel::on_connect() 函数中,具体是其中调用的dcc_start()方法。关键的函数调用链路梳理:

细节可自行查看源码中上述函数的实现。

dcc_get_preferred_video_codecs_for_encoding 的源码:

 

GArray *dcc_get_preferred_video_codecs_for_encoding(DisplayChannelClient *dcc)
{
    if (dcc->priv->preferred_video_codecs != nullptr) {
        return dcc->priv->preferred_video_codecs;
    }
    return display_channel_get_video_codecs(DCC_TO_DC(dcc));
}

我们发现,这里依赖了 preferred_video_codecs 字段,这个值有接口动态设置,在初始化过程中没有作用。所以我们主要关注 display_channel_get_video_codecs 接口的实现:

GArray *display_channel_get_video_codecs(DisplayChannel *display)
{
    spice_return_val_if_fail(display, NULL);

    return display->priv->video_codecs;
}

所以现在的问题是:video_codecs 字段的值从哪儿来?

spice_server_new 接口中,执行了video_codecs的初始化逻辑,关键代码是 spice_server_init(),该接口由qemu初始化spice-server时调用,接口实现里使用默认的codecs初始化video_codecs。

关于default_video_codecs有一点需要注意,SPICE的H.264编码基于GStreamer,因此需要正确安装GStreamer。

#if defined(HAVE_GSTREAMER_1_0) || defined(HAVE_GSTREAMER_0_10)
#define GSTREAMER_CODECS "gstreamer:mjpeg;gstreamer:h264;gstreamer:vp8;gstreamer:vp9;"
#else
#define GSTREAMER_CODECS ""
#endif
static const char default_video_codecs[] = "spice:mjpeg;" GSTREAMER_CODECS;
// 开启h264
// static const char default_video_codecs[] = "gstreamer:h264;" GSTREAMER_CODECS;

codecs的顺序代表优先级,要开启h264只需要调整mjpeg和h264的顺序即可。

客户端同样需要确保有GStreamer组件。SPICE-SERVER会检查客户端的支持情况,如果客户端不支持H.264则回退到MJPEG编码。相关代码:

// video-stream.cpp
static VideoEncoder* dcc_create_video_encoder(DisplayChannelClient *dcc,
                                              uint64_t starting_bit_rate,
                                              VideoEncoderRateControlCbs *cbs)
{
    bool client_has_multi_codec = dcc->test_remote_cap(SPICE_DISPLAY_CAP_MULTI_CODEC);
    int i;
    GArray *video_codecs;

    video_codecs = dcc_get_preferred_video_codecs_for_encoding(dcc);
    for (i = 0; i < video_codecs->len; i++) {
        RedVideoCodec* video_codec = &g_array_index (video_codecs, RedVideoCodec, i);

        if (!client_has_multi_codec &&
            video_codec->type != SPICE_VIDEO_CODEC_TYPE_MJPEG) {
            /* Old clients only support MJPEG */
            spice_debug("[*lsf*]client does not support multi-codec");
            continue;
        }
        if (client_has_multi_codec &&
            !dcc->test_remote_cap(video_codec->cap)) {
            spice_debug("[*lsf*]client does not support this codec");
            /* The client is recent but does not support this codec */
            continue;
        }

        VideoEncoder* video_encoder = video_codec->create(video_codec->type, starting_bit_rate, cbs, bitmap_ref, bitmap_unref);
        if (video_encoder) {
            return video_encoder;
        }
    }

    /* Try to use the builtin MJPEG video encoder as a fallback */
    if (!client_has_multi_codec || dcc->test_remote_cap(SPICE_DISPLAY_CAP_CODEC_MJPEG)) {
        return mjpeg_encoder_new(SPICE_VIDEO_CODEC_TYPE_MJPEG, starting_bit_rate, cbs, bitmap_ref, bitmap_unref);
    }

    return nullptr;
}

举报

相关推荐

0 条评论