GNOME Bugzilla – Bug 727003
Memory Leak in QtGstreamer
Last modified: 2014-06-21 14:15:23 UTC
I am using QTGstreamer in my application to play media files of .webm & .flv formats. The movies of length 5-10 secs play in a loop continuously. I have observed huge memory leak while playing these small movies. Instead if I play a long (approx 1 hour) movie, the leak is almost negligible. Please suggest some solution. Below is the source code [MAIN.CPP] #include "mediaapp.h" #include <QtGui/QApplication> #include <QGst/init.h> int main (int argc, char *argv [] ) { QApplication app ( argc, argv ); QGst :: init ( &argc, &argv ); MediaApp media; media .show(); media .openFile ("file://XYZ.webm"); return app .exec(); } [MEDIAAPP.H] #ifndef MEDIAAPP_H_ #define MEDIAAPP_H_ #include <QtGui/QWidget> #include <QtGui/QStyle> #include <QtCore/QTimer> class Player; class QBoxLayout; class QTimer; class MediaApp : public QWidget { Q_OBJECT public: MediaApp(QWidget *parent = 0); ~MediaApp(); void openFile(const QString & fileName); private Q_SLOTS: void onStateChanged(); private: void createUI(QBoxLayout *appLayout); Player *m_player; }; [MEDIAAPP.CPP] #include "mediaapp.h" #include "player.h" #include <QtGui/QBoxLayout> MediaApp::MediaApp (QWidget *parent) : QWidget (parent) { //create the player m_player = new Player (this); connect (m_player, SIGNAL ( stateChanged() ), this, SLOT( onStateChanged() ) ); //create the UI QVBoxLayout *appLayout = new QVBoxLayout; appLayout ->setContentsMargins (0, 0, 0, 0); appLayout ->addWidget (m_player); setLayout (appLayout); setWindowFlags (Qt::FramelessWindowHint);/* enable this for frameless window */ } MediaApp::~MediaApp() { delete m_player; } void MediaApp::openFile(const QString & fileName) { m_player->stop(); m_player->setUri(fileName); m_player->play(); } [PLAYER.H] #ifndef PLAYER_H_ #define PLAYER_H_ #include <QtCore/QTimer> #include <QtCore/QTime> #include <QGst/Pipeline> #include <QGst/Ui/VideoWidget> class Player : public QGst :: Ui :: VideoWidget { Q_OBJECT public: Player(QWidget *parent = 0); ~Player(); void setUri(const QString & uri); QGst :: State state() const; public Q_SLOTS: void play(); Q_SIGNALS: void positionChanged(); void stateChanged(); private: void onBusMessage(const QGst :: MessagePtr & message); void handlePipelineStateChange(const QGst :: StateChangedMessagePtr & scm); QGst :: PipelinePtr m_pipeline; QTimer m_positionTimer; }; #endif /* PLAYER_H_ */ [PLAYER.CPP] #include "player.h" #include <QtCore/QDir> #include <QtCore/QUrl> #include <QGlib/Connect> #include <QGlib/Error> #include <QGst/Pipeline> #include <QGst/ElementFactory> #include <QGst/Bus> #include <QGst/Message> #include <QGst/Query> #include <QGst/ClockTime> #include <QGst/Event> #include <QGst/StreamVolume> Player::Player(QWidget *parent) : QGst :: Ui :: VideoWidget(parent) { //this timer is used to tell the ui to change its position slider & label //every 100 ms, but only when the pipeline is playing // connect ( &m_positionTimer, SIGNAL ( timeout () ), this, SIGNAL ( positionChanged () ) ); } Player :: ~Player () { if (m_pipeline) { m_pipeline ->setState (QGst :: StateNull ); stopPipelineWatch (); } } void Player :: setUri (const QString & uri) { QString realUri = uri; //if uri is not a real uri, assume it is a file path if (realUri.indexOf("://") < 0) { realUri = QUrl :: fromLocalFile (realUri) .toEncoded(); } if ( !m_pipeline ) { m_pipeline = QGst :: ElementFactory :: make ("playbin2") .dynamicCast <QGst::Pipeline>(); if (m_pipeline) { //let the video widget watch the pipeline for new video sinks watchPipeline (m_pipeline); //watch the bus for messages QGst :: BusPtr bus = m_pipeline ->bus(); bus ->addSignalWatch(); QGlib :: connect (bus, "message", this, &Player :: onBusMessage); } else { qCritical() << "Failed to create the pipeline"; } } if (m_pipeline) { m_pipeline- >setProperty ("uri", realUri); } } QGst :: State Player :: state() const { return m_pipeline ? m_pipeline->currentState() : QGst :: StateNull; } void Player :: play() { if (m_pipeline) { m_pipeline ->setState(QGst :: StatePlaying); } } void Player::onBusMessage(const QGst::MessagePtr & message) { switch ( message->type()) { case QGst :: MessageEos : //End of stream. We reached the end of the file. { if (m_pipeline) { m_pipeline->setState(QGst::StateNull); m_pipeline->setState(QGst::StatePlaying); } } break; default: break; } }
Please provide a *minimal*, self-contained testcase as attachment.
(In reply to comment #1) > Please provide a *minimal*, self-contained testcase as attachment. if you can build the above code then paste any media file with name XYZ.webm in the same path where your is present. Please let me know if I missed something in code which is creating memory leak.
How much of memory being leaked are we talking about? Also, how do you measure it? Have you used valgrind (with G_SLICE=always-malloc - https://developer.gnome.org/glib/unstable/glib-running.html)? Finally, have you checked if gst-launch leaks as well?
(In reply to comment #3) > How much of memory being leaked are we talking about? > Also, how do you measure it? Have you used valgrind (with G_SLICE=always-malloc > - https://developer.gnome.org/glib/unstable/glib-running.html)? > Finally, have you checked if gst-launch leaks as well? -> valgrind result : ==11520== LEAK SUMMARY: ==11520== definitely lost: 21,075 bytes in 11 blocks ==11520== indirectly lost: 11,328 bytes in 352 blocks ==11520== possibly lost: 3,960,827 bytes in 1,901 blocks ==11520== still reachable: 4,431,810 bytes in 42,335 blocks ==11520== suppressed: 0 bytes in 0 blocks ==11520== Reachable blocks (those to which a pointer was found) are not shown. ==11520== To see them, rerun with: --leak-check=full --show-reachable=yes ==11520== ==11520== For counts of detected and suppressed errors, rerun with: -v ==11520== ERROR SUMMARY: 950 errors from 934 contexts (suppressed: 2 from 2) -> I am checking memory uses using top command which becomes 85% for my application if it run continuously for 1 day.
(In reply to comment #3) > How much of memory being leaked are we talking about? > Also, how do you measure it? Have you used valgrind (with G_SLICE=always-malloc > - https://developer.gnome.org/glib/unstable/glib-running.html)? > Finally, have you checked if gst-launch leaks as well? Can you help me with an example where we play many media files one after one. In my application I need to play 4-5 media files in loops.
(In reply to comment #5) > (In reply to comment #3) > > How much of memory being leaked are we talking about? > > Also, how do you measure it? Have you used valgrind (with G_SLICE=always-malloc > > - https://developer.gnome.org/glib/unstable/glib-running.html)? > > Finally, have you checked if gst-launch leaks as well? > > Can you help me with an example where we play many media files one after one. > In my application I need to play 4-5 media files in loops. after reaching end-of-stream, does setting state to StateNull releases all resources allocated.
Could you add those options to valgrind, and post the output ? --leak-check=full --num-callers=32 --suppressions=$path/gstreamer/common/gst.supp --suppressions=$path/gst-plugins-base/tests/check/gst-plugins-base.supp --suppressions=$path/gst-plugins-good/tests/check/gst-plugins-good.supp --suppressions=$path/gst-plugins-bad/tests/check/gst-plugins-bad.supp --suppressions=$path/gst-plugins-ugly/tests/check/gst-plugins-ugly.supp With $path replaced with wherever you have installed the gstreamer trees.
Closing this since no further details have been provided, and qt-gstreamer has been ported to GStreamer 1.x now. Please feel free to re-open this bug or file a new bug if you get around to re-testing with a 1.x-based qt-gstreamer and can provide the requested information, thanks!