A friend of mine (@jan_ekholm/twitter and chakie/@freenode) developed a cool C++ VideoWidget using Qt a some time ago. The idea of making this kind of own custom widget, was that Qt’s multimedia isn’t the best multimedia kit on earth. The problem is that it’s not really a cross platform because it depends on underlying platform multimedia kit. For example on linux it may depend on streamer or phonon backend, on Windows it depends on DirectShow and on Mac on QuickTime. This also means that something that works on one platform, may not work on another platform.
One weird thing about Qt’s multimedia is that QML bindings are provided only by Qt Mobility’s multimedia module. Then again Phonon isn’t very efficient and it doesn’t have QML bindings. If using VLC with Qt, it’s almost impossible to make overlays (text) visible on a video. So Chakie made a solution, which doesn’t depend any of these, is a cross platform (Windows, Mac, Linux) and works quite nicely.
I thought that it would be great to port that QWidget/C++ based solution to QML. Actually, it was quite straight forward to do that, thanks to Chakie’s original solution, it took only a few hours to get the first version to work. Of course, fine tuning takes time, but it didn’t really take more than two hours to make the QML extension to show a video.
QML Extension Plugin for QtQuick 1.x and older
First I created a QML extension plugin using Qt Creator’s project template wizard. One you have the initial extension stuff, then modify it to suit your purposes.
The basic stuff what was required for my custom QML VideoWidget Element is listed below.
- Inherit QDeclarativeItem
- Add paint() method where all the video frames are drawn
- Added geometryChanged() for handling resizing of the widget
- Source property because we want to be able to set the source for the video
- Once everything is there, build and install it
class VideoWidget : public QDeclarativeItem
{
Q_OBJECT
Q_DISABLE_COPY(VideoWidget)
Q_PROPERTY(QUrl source READ source WRITE setSource)
public:
VideoWidget(QDeclarativeItem *parent = 0);
~VideoWidget();
void paint ( QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget = 0 );
void setSource(const QUrl &source);
QUrl source() const;
signals:
void playingChanged(bool playing);
protected:
void geometryChanged ( const QRectF & newGeometry, const QRectF & oldGeometry );
};
In order to be able to draw anything remember to add the following line of code to the constructor.
setFlag(ItemHasNoContents, false);
What it does, is that it enables custom drawing for your extension. In other words it makes paint() method to be called when needed or when forced. This is pretty much what you need for custom QML extension. Of course you can add there bunch of properties or invokable methods if you want.
Porting QML Extension to Qt5 / QtQuick 2.0
First make sure that you have declarative module build in your Qt5 setup. If you are not sure how to do it, you can follow the instruction from here. There are two ways to port QML extension to Qt5. I will explain the easier way now because I haven’t done the second way yet. It’s quite straight forward to port the extension to Qt5.
- Include QQuickPaintedItem and QQuickItem headers
- Replace inherited QDeclarativeItem with QQuickPaintedItem
- Replace the parent QDeclarativeItem with QQuickItem
- Change paint method signature as paint( QPainter * painter) i.e. remove other parameters.
- Change setFlag(ItemHasNoContents, false) to setFlag(ItemHasContents, true)
- Make sure to have line: “QT += quick” in your project (pro) file
So, now you should have something like below:
class VideoWidget : public QQuickPaintedItem
{
Q_OBJECT
Q_DISABLE_COPY(VideoWidget)
Q_PROPERTY(QUrl source READ source WRITE setSource)
public:
VideoWidget(QQuickItem *parent = 0);
~VideoWidget();
void paint (QPainter * painter);
void setSource(const QUrl &source);
QUrl source() const;
signals:
void playingChanged(bool playing);
protected:
void geometryChanged ( const QRectF & newGeometry, const QRectF & oldGeometry );
};
And the flag for the item should be set a bit differently.
setFlag(ItemHasContents, true);
This is basically all you need! I think that Trolls have once again made a really good job when it comes to backward compatibility. Ok, it’s not backward compatible if you need to refactor your code, but still, this is quite minimal effort to gain much more performance. Actually, the code is simpler now.
How about perf then?
As I said in the beginning, I ported this QML VideoWidget element from QtQuick 1.x version to QtQuick 2.0. Unfortunately, I don’t have a good comparison video where the perf difference would be shown, but I might have another video available which might be interesting. On the next video, I run 35 instances of VideoWidget QML Element and all of them are streaming RTSP video stream simultaneously. In addition to that, I borrowed the cool tilt-effect from a QUitCoding’s 5-in-a-row game and added zooming feature there too.
As you can see from the video, decoding and displaying 35 videos in addition to other effects, the demo runs quite nicely. I run the demo on MacBook Pro, which nowadays isn’t anymore that high end laptop, but still pretty powerful. Personally, I think what Qt 5 provides when it comes to performance improvements is really great. The porting was damn easy and didn’t cause any headache. So, good work once again!
Thanks for reading my blog!
Tags: Multimedia, QML, Qt, Qt5, QtQuick2.0, Video