Re: [PATCH v2 spice-streaming-agent 1/1] Adding gstreamer based plugin

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed, 2018-06-20 at 10:04 +0300, Snir Sheriber wrote:
> HI,
> 
> Thanks for reviewing this
> 
> 
> No comment = mostly agreed, will be changed :)
> 
> 
> On 06/18/2018 06:15 PM, Lukáš Hrázký wrote:
> > Hi Snir,
> > 
> > mostly C++ and error handling notes :) I hope it's not too painful :D
> > 
> > 
> > On Sun, 2018-06-10 at 11:14 +0300, Snir Sheriber wrote:
> > > Gstreamer based plugin utilizing gstreamer elements to capture
> > > screen from X, convert and encode into h264/vp8/vp9/mjpeg stream
> > > Configure with --enable-gstreamer, will be built as a separate plugin.
> > > 
> > > The plugin was made for testing purposes, it was mainly tested with
> > > the x264enc (h264 is the defualt codec) encoder.
> > > To choose codec type use: '-c gst.codec=<h264/vp8/vp9/mjpeg>'
> > > To specify a certain plugin use: '-c gst.encoder=<plugin name>' in
> > > addition to its matching codec type (gst.codec).
> > > ---
> > >   configure.ac       |  15 ++
> > >   src/Makefile.am    |  27 +++
> > >   src/gst-plugin.cpp | 430 +++++++++++++++++++++++++++++++++++++++++++++
> > >   3 files changed, 472 insertions(+)
> > >   create mode 100644 src/gst-plugin.cpp
> > > 
> > > diff --git a/configure.ac b/configure.ac
> > > index b59c447..b730bb2 100644
> > > --- a/configure.ac
> > > +++ b/configure.ac
> > > @@ -60,6 +60,20 @@ AC_ARG_WITH(udevrulesdir,
> > >   )
> > >   AC_SUBST(UDEVRULESDIR)
> > >   
> > > +AC_ARG_ENABLE(gstreamer,
> > > +              AS_HELP_STRING([--enable-gstreamer=@<:@auto/yes/no@:>@],
> > > +                             [Enable GStreamer support]),,
> > > +              [enable_gstreamer="no"])
> > > +if test "$enable_gstreamer" != "no"; then
> > > +    PKG_CHECK_MODULES(GST, [gstreamer-1.0 gstreamer-app-1.0], [enable_gstreamer=yes],
> > > +        [if test "$enable_gstreamer" = "yes"; then
> > > +             AC_MSG_ERROR([Gstreamer libs are missing])
> > > +         fi
> > > +         enable_gstreamer=no
> > > +    ])
> > > +fi
> > > +AM_CONDITIONAL([HAVE_GST],[test "$enable_gstreamer" = "yes"])
> > > +
> > >   dnl ===========================================================================
> > >   dnl check compiler flags
> > >   
> > > @@ -129,6 +143,7 @@ AC_MSG_NOTICE([
> > >           prefix:                   ${prefix}
> > >           C compiler:               ${CC}
> > >           C++ compiler:             ${CXX}
> > > +        Gstreamer plugin:         ${enable_gstreamer}
> > >   
> > >           Now type 'make' to build $PACKAGE
> > >   ])
> > > diff --git a/src/Makefile.am b/src/Makefile.am
> > > index 18ed22c..56bae70 100644
> > > --- a/src/Makefile.am
> > > +++ b/src/Makefile.am
> > > @@ -9,6 +9,9 @@ if ENABLE_TESTS
> > >   SUBDIRS = . unittests
> > >   endif
> > >   
> > > +plugin_LTLIBRARIES =
> > > +plugindir = $(pkglibdir)/plugins
> > > +
> > >   AM_CPPFLAGS = \
> > >   	-DSPICE_STREAMING_AGENT_PROGRAM \
> > >   	-I$(top_srcdir)/include \
> > > @@ -61,3 +64,27 @@ spice_streaming_agent_SOURCES = \
> > >   	stream-port.cpp \
> > >   	stream-port.hpp \
> > >   	$(NULL)
> > > +
> > > +if HAVE_GST
> > > +plugin_LTLIBRARIES += gst-plugin.la
> > > +
> > > +gst_plugin_la_LDFLAGS = \
> > > +	-module -avoid-version \
> > > +	$(RELRO_LDFLAGS) \
> > > +	$(NO_INDIRECT_LDFLAGS) \
> > > +	$(NULL)
> > > +
> > > +gst_plugin_la_LIBADD = \
> > > +	$(GST_LIBS) \
> > > +	$(NULL)
> > > +
> > > +gst_plugin_la_SOURCES = \
> > > +	gst-plugin.cpp \
> > > +	$(NULL)
> > > +
> > > +gst_plugin_la_CPPFLAGS = \
> > > +	-I$(top_srcdir)/include \
> > > +	$(SPICE_PROTOCOL_CFLAGS) \
> > > +	$(GST_CFLAGS) \
> > > +	$(NULL)
> > > +endif
> > > diff --git a/src/gst-plugin.cpp b/src/gst-plugin.cpp
> > > new file mode 100644
> > > index 0000000..435f148
> > > --- /dev/null
> > > +++ b/src/gst-plugin.cpp
> > > @@ -0,0 +1,430 @@
> > > +/* Plugin implementation for gstreamer encoder
> > > + *
> > > + * \copyright
> > > + * Copyright 2018 Red Hat Inc. All rights reserved.
> > > + */
> > > +
> > > +#include <config.h>
> > > +#include <cstring>
> > > +#include <exception>
> > > +#include <stdexcept>
> > > +#include <sstream>
> > > +#include <memory>
> > > +#include <syslog.h>
> > > +#include <unistd.h>
> > > +#include <gst/gst.h>
> > > +#include <gst/app/gstappsink.h>
> > > +
> > > +#define XLIB_CAPTURE 1
> > > +#if XLIB_CAPTURE
> > > +#include <X11/Xlib.h>
> > > +#include <gst/app/gstappsrc.h>
> > > +#endif
> > > +
> > > +#include <spice-streaming-agent/plugin.hpp>
> > > +#include <spice-streaming-agent/frame-capture.hpp>
> > > +
> > > +namespace spice {
> > > +namespace streaming_agent {
> > > +namespace gstreamer_plugin {
> > > +
> > > +struct GstreamerEncoderSettings
> > > +{
> > > +    int fps;
> > > +    SpiceVideoCodecType codec;
> > > +    char encoder[32];
> > 
> > You could just use std::string here...
> > 
> > > +};
> > > +
> > > +class GstreamerFrameCapture final: public FrameCapture
> > 
> > Style: space before ":" I think...
> > 
> > > +{
> > > +public:
> > > +    GstreamerFrameCapture(const GstreamerEncoderSettings &settings);
> > > +    GstreamerFrameCapture() = delete;
> > 
> > You don't have to delete the default constructor, if you declare any
> > constructors yourself, the compiler will not generate the default one.
> 
> That's really weird. I'm really remember i added this since i was 
> getting an error
> here, but now it seems to work :p.
> 
> > > +    ~GstreamerFrameCapture();
> > > +    FrameInfo CaptureFrame() override;
> > > +    void Reset() override;
> > > +    SpiceVideoCodecType VideoCodecType() const override {
> > > +        return settings.codec;
> > > +    }
> > > +private:
> > > +    void free_sample();
> > > +    GstElement *get_encoder_plugin(const GstreamerEncoderSettings& settings);
> > > +    GstElement *get_capture_plugin(const GstreamerEncoderSettings& settings);
> > > +    void pipeline_init(const GstreamerEncoderSettings& settings);
> > > +#if XLIB_CAPTURE
> > > +    void xlib_capture();
> > > +    Display *dpy;
> > > +    XImage *image = nullptr;
> > > +#endif
> > > +    GstElement *pipeline, *capture, *sink, *encoder, *convert;
> > > +    GstSample *sample = nullptr;
> > > +    GstCaps *sink_caps;
> > > +    GstMapInfo map = {};
> > > +    uint32_t last_width = ~0u, last_height = ~0u;
> > > +    uint32_t cur_width = 0, cur_height = 0;
> > > +    bool is_first = true;
> > > +    GstreamerEncoderSettings settings; // will be set by plugin settings
> > > +};
> > > +
> > > +class GstreamerPlugin final: public Plugin
> > > +{
> > > +public:
> > > +    FrameCapture *CreateCapture() override;
> > > +    unsigned Rank() override;
> > > +    void ParseOptions(const ConfigureOption *options);
> > > +    SpiceVideoCodecType VideoCodecType() const override {
> > > +        return settings.codec;
> > > +    }
> > > +private:
> > > +    GstreamerEncoderSettings settings = { .fps = 25, .codec = SPICE_VIDEO_CODEC_TYPE_H264, .encoder = {0} };
> > > +};
> > > +}}} //namespacespice::streaming_agent::gstreamer_plugin
> > > +
> > > +using namespacespice::streaming_agent;
> > > +using namespacespice::streaming_agent::gstreamer_plugin;
> > 
> > Ending the namespace scope and adding a "using" clause has the same
> > effect here and only really adds noise. The common practice to enclose
> > everything in the namespace scope.
> > 
> > > +
> > > +GstElement * GstreamerFrameCapture::get_capture_plugin(const GstreamerEncoderSettings& settings)
> > 
> > Style: I belive we don't put a space after "*", you are inconsistent in
> > it too.
> > 
> > > +{
> > > +GstElement *capture = NULL;
> > 
> > Missing indent here. Also, use nullptr consistently (more NULLs below)?
> > 
> > > +
> > > +#if XLIB_CAPTURE
> > > +    capture = gst_element_factory_make("appsrc", "capture");
> > > +#else
> > > +    capture = gst_element_factory_make("ximagesrc", "capture");
> > > +    g_object_set(capture,
> > > +                "use-damage", 0,
> > > +                 NULL);
> > > +
> > > +#endif
> > 
> > Scattering #ifdefs across the code is really not clean in C++. The way
> > this is conventionally supposed to be solved is extension by
> > composition, which means you implement an interface (i.e. a virtual
> > class in C++) for the xlib capture functionality and then implement it
> > in a class.
> > 
> > But in this case I'd be more inclined to use template specialization
> > for a compile-time conditional use of the code (apart from looking a
> > bit better here is saves the virtual function table overhead). A short
> > example:
> > 
> > template<int T>
> > class XlibCapture
> > {
> > public:
> >      void capture() {}
> > };
> > 
> > template<>
> > class XlibCapture<1>
> > {
> > public:
> >      void capture()
> >      {
> >          // do the Xlib capture
> >      }
> > };
> > 
> > // ...
> > 
> > class GstreamerFrameCapture {
> >      // ...
> > private:
> >      XlibCapture<XLIB_CAPTURE> xlib_capture;
> > };
> > 
> > Apart from the capture() method from the example you should use
> > constructor/destructor for initialization/cleanup and obviously you can
> > add more methods.
> > 
> > For the one "#if !XLIB_CAPTURE" check, I belive that is kind-of special
> > and could probably be done as a runtime check, looks like it may not
> > always be Xlib specific anyway?
> > 
> > I know there might be a few more issues to solve with this, so if you
> > go for it and get stuck, feel free to ping me.
> 
> It would make sense, but actually the reason of having the xlib capture
> is mostly that ximagesrc still unable to handle runtime resolution change
> (And i still wonder if it may be possible to workaround it somehow by
> sending eos and restart the stream or similar ).
> So hopefully this part of the code will be dropped :p
> 
> > > +    return capture;
> > > +}
> > > +
> > > +GstElement * GstreamerFrameCapture::get_encoder_plugin(const GstreamerEncoderSettings& settings)
> > > +{
> > > +    GList *encoders;
> > > +    GList *filtered;
> > > +    GstElement *encoder;
> > > +    GstElementFactory *factory = NULL;
> > > +    std::stringstream caps_ss;
> > > +
> > > +    switch (settings.codec) {
> > > +    case SPICE_VIDEO_CODEC_TYPE_H264:
> > > +        caps_ss << "video/x-h264" << ",stream-format=(string)byte-stream";
> > 
> > Nit: you can use one static string instead of two and save the
> > operator.
> 
> It was a static string, but found it is more convenience to add numbers 
> if needed (e.g. fps) .
> 
> > > +        break;
> > > +    case SPICE_VIDEO_CODEC_TYPE_MJPEG:
> > > +        caps_ss << "image/jpeg";
> > > +        break;
> > > +    case SPICE_VIDEO_CODEC_TYPE_VP8:
> > > +        caps_ss << "video/x-vp8";
> > > +        break;
> > > +    case SPICE_VIDEO_CODEC_TYPE_VP9:
> > > +        caps_ss << "video/x-vp9";
> > > +        break;
> > > +    default :
> > > +        syslog(LOG_ERR, "Invalid codec\n");
> > > +        return NULL;
> > > +        break;
> > 
> > Looks like you should be throwing an exception here? You could also
> > include the codec type in the error message. And some context would be
> > useful for the user troubleshooting this (i.e. that this error comes
> > from the gstreamer plugin), but I'm thinking this problem is more
> > universal and we should look for a generic solution across the
> > streaming agent...
> > 
> > > +    }
> > > +    if (!caps_ss.str().empty()) {
> > > +        caps_ss << ",framerate=" << settings.fps << "/1";
> > > +    }
> > 
> > As Uri mentioned, caps_ss can't be empty here.
> 
> True, a stub.
> > > +
> > > +    encoders = gst_element_factory_list_get_elements(GST_ELEMENT_FACTORY_TYPE_VIDEO_ENCODER, GST_RANK_NONE);
> > > +    sink_caps = gst_caps_from_string(caps_ss.str().c_str());
> > > +    filtered = gst_element_factory_list_filter(encoders, sink_caps, GST_PAD_SRC, false);
> > > +    if (filtered) {
> > > +        printf("Available encoders:\n");
> > 
> > The printf() calls should be syslog()s?
> 
> No, Should be printed to user. (but should i use cout maybe? or 
> something else?)

What is the intention with the messages then? They look like debug
messages to me. I still think it should be syslog(), if you run the
agent manually from the terminal, syslog does print to stderr. And I
think stderr is preferable to stdout.

Yeah, otherwise it's probably better to use std::cout/cerr.

> > > +
> > > +        for (GList *l = filtered; l != NULL; l = l->next) {
> > > +            if (!factory && settings.encoder[0] && !g_strcmp0(settings.encoder, GST_ELEMENT_NAME(l->data))) {
> > > +                factory = (GstElementFactory*)l->data;
> > > +            }
> > > +            printf("\t%s\n", GST_ELEMENT_NAME(l->data));
> > > +        }
> > > +        if (!factory && settings.encoder[0]) {
> > > +            printf("\t(Specified encoder '%s' was not found for the requested codec)\n", settings.encoder);
> > > +        }
> > > +        factory = factory ? factory : (GstElementFactory*)filtered->data;
> > > +        printf("\n*** '%s' encoder plugin is used ***\n", GST_ELEMENT_NAME(factory));
> > > +
> > > +    } else {
> > > +        syslog(LOG_ERR, "No suitable encoder was found\n");
> > 
> > Again, if it's an error, throw an exception.
> > 
> > > +    }
> > > +    gst_plugin_feature_list_free(filtered);
> > > +    gst_plugin_feature_list_free(encoders);
> > > +
> > > +    encoder = factory ? gst_element_factory_create(factory, "encoder") : NULL;
> > 
> > Not sure you can rely on factory being != NULL if you throw instead of
> > the syslog above.
> > 
> > I'd check for encoder == NULL here and throw an error straight away.
> > And in general you can throw straight away and not use NULL pointers as
> > an error state ever in C++...
> 
> Ok, actually now looking I'm not sure it could be null.. will do 
> differently.
> 
> > > +    if (encoder) { // Invalid properties will be ignored silently
> > > +        /* x264enc properties */
> > > +        gst_util_set_object_arg(G_OBJECT(encoder), "tune", "zerolatency");// stillimage, fastdecode, zerolatency
> > > +        gst_util_set_object_arg(G_OBJECT(encoder), "bframes", "0");
> > > +        gst_util_set_object_arg(G_OBJECT(encoder), "speed-preset", "1");// 1-ultrafast, 6-med, 9-veryslow
> > > +    }
> > > +    return encoder;
> > > +}
> > > +
> > > +void GstreamerFrameCapture::pipeline_init(const GstreamerEncoderSettings& settings)
> > > +{
> > > +    GstElement *encoder, *convert;
> > > +    GstCaps *caps;
> > > +    gboolean link;
> > > +
> > > +    pipeline = gst_pipeline_new("pipeline");
> > > +    capture = get_capture_plugin(settings);
> > > +    convert = gst_element_factory_make("videoconvert", "convert");
> > > +    encoder = get_encoder_plugin(settings);
> > > +    sink = gst_element_factory_make("appsink", "sink");
> > > +    if (!capture || !convert || !encoder || !sink) {
> > > +        gst_object_unref (GST_OBJECT (pipeline));
> > > +        gst_object_unref (GST_OBJECT (capture));
> > > +        gst_object_unref (GST_OBJECT (convert));
> > > +        gst_object_unref (GST_OBJECT (encoder));
> > > +        gst_object_unref (GST_OBJECT (sink));
> > > +        //TODO: check elements existence at build time (and\or improve error in runtime)
> > > +        throw std::runtime_error("One or more gstreamer element cannot be created");
> > 
> > As I said, throw at the spot of creating each of the pointers if it is
> > NULL, it'll be a more useful error message too.
> 
> Yes, was mentioned before, missed it.
> 
> > We should also be using the error hierarchy since we got it in place. I
> > think we should probably create a PluginError : public Error exception
> > and put it in a public header (though that's not necessary for this
> > plugin).
> > 
> > Also, does gstreamer have any indication mechanism to specify what was
> > the actual problem?
> 
> Debugging the application with gstreamer tools will shed more light on
> the problem, not sure it can be extracted somehow and to be added to our 
> error msgs.

It might be completely off but I found this:
https://gstreamer.freedesktop.org/data/doc/gstreamer/head/gstreamer/html/gstreamer-GstGError.html

> > > +    }
> > > +
> > > +    g_object_set(sink,
> > > +                 "sync", FALSE,
> > > +                 "drop", TRUE,
> > > +                 "max-buffers", 1,
> > > +                 NULL);
> > > +
> > > +    gst_bin_add_many(GST_BIN(pipeline), capture, convert, encoder, sink, NULL);
> > > +
> > > +    caps = gst_caps_from_string("video/x-raw,format=(string)I420");
> > > +    link = gst_element_link(capture, convert) &&
> > > +           gst_element_link_filtered(convert, encoder, caps) &&
> > > +           gst_element_link_filtered(encoder, sink, sink_caps);
> > > +    gst_caps_unref(caps);
> > > +    if (!link) {
> > > +        throw std::runtime_error("Link failed");
> > 
> > This error message is useless, please improve it.
> > 
> > > +    }
> > > +
> > > +#if XLIB_CAPTURE
> > > +    dpy = XOpenDisplay(NULL);
> > > +    if (!dpy) {
> > > +        throw std::runtime_error("Unable to initialize X11");
> > > +    }
> > > +#endif
> > > +
> > > +    gst_element_set_state(pipeline, GST_STATE_PLAYING);
> > > +
> > > +#if !XLIB_CAPTURE
> > > +    /* Some encoders cannot handle odd resolution make sure it's even number of pixels */
> > > +    int sx, sy, ex, ey;
> > > +    g_object_get(capture,
> > > +                 "endx", &ex,
> > > +                 "endy", &ey,
> > > +                 "startx", &sx,
> > > +                 "starty", &sy,
> > > +                 NULL);
> > > +    cur_width = ex - sx;
> > > +    cur_height = ey - sy;
> > > +    if (cur_width < 16 || cur_height < 16) {
> > > +         throw std::runtime_error("Invalid screen size");
> > 
> > So IIUC this code is checking if the frame is smaller than 16x16
> > pixels, which is a check on top of what's mentioned in the comment
> > above, which I find confusing.
> 
> It's in addition to the other check which is few rows after.

Yes :) what I wanted to say is that the comment above it: "Some
encoders cannot handle ..." talks about a different check. If I read
that and continue reading the code, I'm awaiting to see the check the
comment mentions, but find a completely different check first. Might
just be me, but I find it mind-boggling. I go re-reading the comment to
make sure what is says, then re-read the check to make sure what it
does, ... :)

Cheers,
Lukas

> > You could also say "smaller than 16x16" in the error message.
> > > +    }
> > > +    g_object_set(capture,
> > > +                 "endx", cur_width - !(cur_width%2),
> > > +                 "endy", cur_height - !(cur_height%2),
> > 
> > Style: I'd prefer spaces around "%", is there a reason to have them
> > around "-" and not around "%"?
> > 
> > > +                 "startx", 0,
> > > +                 "starty", 0,
> > > +                 NULL);
> > > +#endif
> > > +}
> > > +
> > > +GstreamerFrameCapture::GstreamerFrameCapture(const GstreamerEncoderSettings& settings):
> > > +    settings(settings)
> > > +{
> > > +    try {
> > > +        pipeline_init(settings);
> > 
> > The pipeline_init() method code really can be in the constructor. The
> > only advantage of the method is one level of indent... But still I'd
> > just put the code here.
> > 
> > > +    } catch (const std::exception &e) {
> > > +        if (pipeline)
> > > +            gst_object_unref(pipeline);
> > > +        if (sink_caps)
> > > +            gst_caps_unref(sink_caps);
> > > +
> > > +        throw std::runtime_error(e.what());
> > 
> > You can rethrow the original exception by a simple "throw;" (you can
> > also do "throw e;" but that actually creates a copy of the exception.
> > Not that it matters much here.)
> > 
> > > +    }
> > > +}
> > > +
> > > +void GstreamerFrameCapture::free_sample()
> > > +{
> > > +    if (sample) {
> > > +        gst_buffer_unmap(gst_sample_get_buffer(sample), &map);
> > > +        gst_sample_unref(sample);
> > > +        sample = nullptr;
> > > +    }
> > > +#if XLIB_CAPTURE
> > > +    if(image) {
> > > +        image->f.destroy_image(image);
> > > +        image = nullptr;
> > > +    }
> > > +#endif
> > > +}
> > 
> > It would be nice to have some sort of RAII for this (i.e. wrap in a
> > class, create in constructor and cleanup in destructor). Something like
> > that would be probably needed if you go with the XlibCapture wrapper.
> > 
> > Not sure how the image works though. It seems to be local to the
> > xlib_capture() method, or is it somehow referenced from the sample
> > itself?
> > 
> > > +
> > > +GstreamerFrameCapture::~GstreamerFrameCapture()
> > > +{
> > > +    free_sample();
> > > +    gst_element_set_state(pipeline, GST_STATE_NULL);
> > > +    gst_object_unref(pipeline);
> > > +    gst_caps_unref(sink_caps);
> > > +#if XLIB_CAPTURE
> > > +    XCloseDisplay(dpy);
> > > +#endif
> > > +}
> > > +
> > > +void GstreamerFrameCapture::Reset()
> > > +{
> > > +    //TODO
> > > +}
> > > +
> > > +#if XLIB_CAPTURE
> > > +void GstreamerFrameCapture::xlib_capture()
> > > +{
> > > +    int screen = XDefaultScreen(dpy);
> > > +
> > > +    Window win = RootWindow(dpy, screen);
> > > +    XWindowAttributes win_info;
> > > +    XGetWindowAttributes(dpy, win, &win_info);
> > > +
> > > +    /* Some encoders cannot handle odd resolution */
> > > +    cur_width = win_info.width - win_info.width%2;
> > > +    cur_height =  win_info.height - win_info.height%2;
> > > +
> > > +    if (cur_width != last_width || cur_height != last_height) {
> > > +        last_width = cur_width;
> > > +        last_height = cur_height;
> > > +        is_first = true;
> > > +
> > > +        gst_app_src_end_of_stream (GST_APP_SRC (capture));
> > > +	gst_element_set_state(pipeline, GST_STATE_NULL);//maybe ximagesrc needs eos as well
> > 
> > A stray tab on this line.
> > 
> > > +        gst_element_set_state(pipeline, GST_STATE_PLAYING);
> > > +    }
> > > +
> > > +    // TODO handle errors
> > > +    image = XGetImage(dpy, win, 0, 0,
> > > +                      cur_width, cur_height, AllPlanes, ZPixmap);
> > > +    if (!image)
> > > +        throw std::runtime_error("Cannot capture from X");
> > 
> > Missing curly brackets, once more 3 lines down the code.
> > 
> > > +    GstBuffer *buf;
> > > +    buf = gst_buffer_new_wrapped(image->data, image->height * image->bytes_per_line);
> > > +    if (!buf)
> > > +        throw std::runtime_error("Cannot wrap image");
> > 
> > This message is also quite lacking :)
> > 
> > > +    GstSample *appsrc_sample;
> > > +    GstCaps *caps;
> > > +    std::stringstream ss;
> > > +
> > > +    ss << "video/x-raw,format=BGRx,width=" << image->width << ",height=" << image->height << ",framerate=" << settings.fps << "/1";
> > > +    caps = gst_caps_from_string(ss.str().c_str());
> > > +
> > > +    // Push sample
> > > +    appsrc_sample = gst_sample_new(buf, caps, NULL, NULL);
> > > +    if (gst_app_src_push_sample (GST_APP_SRC (capture), appsrc_sample) != GST_FLOW_OK) {
> > > +        throw std::runtime_error("appsrc cannot push sample");
> > 
> > :)
> > 
> > > +    }
> > > +    gst_sample_unref(appsrc_sample);
> > > +}
> > > +#endif
> > > +
> > > +FrameInfo GstreamerFrameCapture::CaptureFrame()
> > > +{
> > > +    FrameInfo info;
> > > +
> > > +    free_sample(); // free prev if exist
> > > +
> > > +#if XLIB_CAPTURE
> > > +    xlib_capture();
> > > +#endif
> > > +    info.size.width = cur_width;
> > > +    info.size.height = cur_height;
> > > +    info.stream_start = is_first;
> > > +    if (is_first) {
> > > +        is_first = false;
> > > +    }
> > > +
> > > +    // Pull sample
> > > +    sample = gst_app_sink_pull_sample(GST_APP_SINK(sink)); // blocking
> > > +
> > > +    if (sample) { //map after pipeline
> > > +        if (!gst_buffer_map(gst_sample_get_buffer(sample), &map, GST_MAP_READ)) {
> > > +            free_sample();
> > > +            throw std::runtime_error("Buffer mapping failed");
> > > +        }
> > > +
> > > +        info.buffer = map.data;
> > > +        info.buffer_size = map.size;
> > > +    } else {
> > > +        syslog(LOG_ERR, "No sample- EOS or state change\n");
> > 
> > Throw an exception? Also the message :)
> > 
> > > +    }
> > > +
> > > +    return info;
> > > +}
> > > +
> > > +FrameCapture *GstreamerPlugin::CreateCapture()
> > > +{
> > > +    return new GstreamerFrameCapture(settings);
> > > +}
> > > +
> > > +unsigned GstreamerPlugin::Rank()
> > > +{
> > > +    return SoftwareMin;
> > > +}
> > > +
> > > +void GstreamerPlugin::ParseOptions(const ConfigureOption *options)
> > > +{
> > > +    for (; options->name; ++options) {
> > > +        const std::string name = options->name;
> > > +        const std::string value = options->value;
> > > +
> > > +        if (name == "framerate") {
> > > +            try {
> > > +                settings.fps = std::stoi(value);
> > > +            } catch (const std::exception &e) {
> > > +                throw std::runtime_error("Invalid value '" + value + "' for option 'framerate'.");
> > > +            }
> > > +        } else if (name == "gst.codec") {
> > > +            try {
> > > +                if (value == "h264") {
> > > +                    settings.codec = SPICE_VIDEO_CODEC_TYPE_H264;
> > > +                } else if (value == "vp9") {
> > > +                    settings.codec = SPICE_VIDEO_CODEC_TYPE_VP9;
> > > +                } else if (value == "vp8") {
> > > +                    settings.codec = SPICE_VIDEO_CODEC_TYPE_VP8;
> > > +                } else if (value == "mjpeg") {
> > > +                    settings.codec = SPICE_VIDEO_CODEC_TYPE_MJPEG;
> > > +                } else {
> > > +                    throw std::runtime_error("Invalid value '" + value + "' for option 'gst.codec'.");
> > > +                }
> > > +            } catch (const std::exception &e) {
> > > +                throw std::runtime_error(e.what());
> > > +            }
> > 
> > No need to catch and rethrow here, you can drop the try-catch block.
> > 
> > > +        } else if (name == "gst.encoder") {
> > > +            try {
> > > +                if (value.length() < 3 || value.length() > 31) {
> > > +                    syslog(LOG_WARNING, "Encoder name length is invalid, will be ignored");
> > 
> > I think this should be an exception too? Though if you use std::string
> > for the settings.encoder, you can drop this altogether.
> > 
> > > +                } else {
> > > +                    value.copy(settings.encoder, value.length());
> > > +                }
> > > +            } catch (const std::exception &e) {
> > > +                throw std::runtime_error("Invalid value '" + value + "' for option 'gst.encoder'.");
> > > +            }
> > 
> > There is no exception being thrown inside the block that would mean the
> > value is invalid, drop the try-catch block...
> > 
> > Cheers,
> > Lukas
> > 
> > > +
> > > +        }
> > > +    }
> > > +}
> > > +
> > > +SPICE_STREAMING_AGENT_PLUGIN(agent)
> > > +{
> > > +    gst_init(NULL, NULL);
> > > +
> > > +    std::unique_ptr<GstreamerPlugin> plugin(new GstreamerPlugin());
> > > +
> > > +    plugin->ParseOptions(agent->Options());
> > > +
> > > +    agent->Register(*plugin.release());
> > > +
> > > +    return true;
> > > +}
> 
> 
> Thanks, Snir.
_______________________________________________
Spice-devel mailing list
Spice-devel@xxxxxxxxxxxxxxxxxxxxx
https://lists.freedesktop.org/mailman/listinfo/spice-devel




[Index of Archives]     [Linux Virtualization]     [Linux Virtualization]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux OMAP]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]     [Monitors]