Free memory of ofVideoPlayer

Hi,

I need to load and unload videos for an installation. (upon user request) ofVideoPlayer is within an ofThread class.

I’m off course using load() play(), stop() and close()

I see my memory usage grow a lot. Is there a memory leak? how can I completely free the memory used by ofVideoPlayer?

ubuntu 16.04
OF098

I would test other parts of your application for leaks, the video player in linux has been extensively tested for leaks and shouldn’t have any. Using it from a thread shouldn’t make any difference in terms of leaking memory but it also usually doesn’t have any advantage to use it from a thread since the player itself uses threads internally to playback the video.

I’ve just tested the video player in 0.10 and i can’t see any leaks, you don’t even need to stop and close just call load and it’ll free up the previous video. 0.9.8 shouldn’t have any leaks either but it’s not maintained anymore so i can’t tell for sure.

The installation has some content with videos and some without. Content without videos works perfect and the memory footprint stays stable.

However, After I load content with videos the memory usage increases. Upon unloading the video it shrinks but not to the original state. It keeps increasing as I load/unload other videos. Then I get a a lot of memory allocated to /usr/locale/locale-archive…

any ideas?
Help is really appreciated

here is the whole code of ofThread. don’t know if I’m missing something:

//--------------------------------------------------------------
project::videoPlayerThreaded::videoPlayerThreaded() : ofThread() {
    return;
}


//--------------------------------------------------------------
project::videoPlayerThreaded::~videoPlayerThreaded() {

    mycPlayer.stop();
    mycPlayer.closeMovie();


    vector<ofPixels>().swap(myPixels);  cout << "threadedVideo myPixels.capacity(): " << myPixels.capacity() << endl;
    vector<ofFile>().swap(myFiles);     cout << "threadedVideo myFiles.capacity():  " << myFiles.capacity()  << endl;

    switches.clear();
    values.clear();
    parameters.clear();

}



//--------------------------------------------------------------
void project::videoPlayerThreaded::setup(vector <ofFile> myFiles, int heightVal) {

    this->myFiles = myFiles;
    switches["haveFrames"] = false;
    switches["videosDone"] = false;
    values["height"] = heightVal;

    mycPlayer.setUseTexture(false);
    mycPlayer.load( myFiles[0].getAbsolutePath() );
    mycPlayer.setLoopState( OF_LOOP_NONE );

    while ( mycPlayer.isLoaded() == false ) {

        ;

    }

    cout << "mycPlayer.isLoaded(): " << mycPlayer.isLoaded() << endl;

    if ( (int) mycPlayer.getHeight() > values["height"] ) {

        float scaleAmt = values["height"] / mycPlayer.getHeight();

        values["videoSizeX"]  = mycPlayer.getWidth() * scaleAmt;
        values["videoSizeY"]  = values["height"];
        values["videoPosX"]   = 0;
        values["videoPosY"]   = 0;

    } else {

        values["videoSizeX"] = mycPlayer.getWidth();
        values["videoSizeY"] = mycPlayer.getHeight();
        values["videoPosX"] = 0;
        values["videoPosY"] = (values["height"] / 2) - (values["videoSizeY"] / 2);

    }

    mycPlayer.play();

}

//--------------------------------------------------------------
void project::videoPlayerThreaded::stop(){

    mycPlayer.stop();
    mycPlayer.close();

    vector<ofPixels>().swap (  myPixels );     cout << "threadedVideo myPixels.capacity(): " << myPixels.capacity() << endl;
    vector<ofFile>().swap   (  myFiles  );     cout << "threadedVideo myFiles.capacity():  " << myFiles.capacity()  << endl;

    switches.clear();
    values.clear();
    parameters.clear();

    switches["finished"] = false;

}

//--------------------------------------------------------------
void project::videoPlayerThreaded::threadedFunction() {

    while(isThreadRunning()) {

        lock();  ofLogVerbose() << "Thread lock()";

        if( mycPlayer.isLoaded() ) {

            if ( mycPlayer.getIsMovieDone() ){

                mycPlayer.stop();
                mycPlayer.close();

                myFiles.erase( myFiles.begin() );

                if ( myFiles.size() ) {

                    mycPlayer.load( myFiles[0].getAbsolutePath() );
                    ofLogNotice() << "New video loaded: " << myFiles[0].getAbsolutePath();
                    mycPlayer.play();
                    mycPlayer.setLoopState( OF_LOOP_NONE );

                } else {

                    switches["haveFrames"] = false;
                    switches["finished"] = true;
                    stopThread();

                }

            }

            mycPlayer.update();

            if(mycPlayer.isFrameNew()) {

                ofLogVerbose() << "mycPlayer.isFrameNew(): " << mycPlayer.isFrameNew() << endl;

                vector<ofPixels>().swap(myPixels);
                myPixels.push_back ( mycPlayer.getPixels() );
                myPixels[0].resize( values["videoSizeX"] , values["videoSizeY"], OF_INTERPOLATE_NEAREST_NEIGHBOR );
                switches["haveFrames"]= true;
                stopThread();

            }

        }

        unlock();
        ofLogVerbose() << "Thread unlocked, isThreadRunning: " << isThreadRunning();

    }

}

Why are you doing these swaps?
Have you tried disabling

if(mycPlayer.isFrameNew()) {

                ofLogVerbose() << "mycPlayer.isFrameNew(): " << mycPlayer.isFrameNew() << endl;

                vector<ofPixels>().swap(myPixels);
                myPixels.push_back ( mycPlayer.getPixels() );
                myPixels[0].resize( values["videoSizeX"] , values["videoSizeY"], OF_INTERPOLATE_NEAREST_NEIGHBOR );
                switches["haveFrames"]= true;
                stopThread();

            }

As there might be some issue there.
All the rest seems fine.
I would recommend to make some function for loading the first video, as there is some code that is duplicated.
cheers

@roymacdonald the swaps free the memory of the vectors. Basically they create an empty vector and swap immediately with the existing one. see https://prateekvjoshi.com/2013/10/20/c-vector-memory-release/
I made this to make sure that the leak was not in the vectors.

The reason to have the player in an ofThread is that I have another video playing simultaneosly. And scaling the frames to the right size takes some computing time.

do you have any hints about threading the ofVideoPlayer?

Hi,
this block looks a bit weird.

            if(mycPlayer.isFrameNew()) {

                ofLogVerbose() << "mycPlayer.isFrameNew(): " << mycPlayer.isFrameNew() << endl;

                vector<ofPixels>().swap(myPixels);
                myPixels.push_back ( mycPlayer.getPixels() );
                myPixels[0].resize( values["videoSizeX"] , values["videoSizeY"], OF_INTERPOLATE_NEAREST_NEIGHBOR );
                switches["haveFrames"]= true;
                stopThread();

            }

On each new frame you are emptying the myPixels vector, then pushing back the players pixels, which will create a copy and then resizing it. what is the use for having it in a vector, and do you need to resize using the pixels instead of the texture? resizing the pixels will take CPU time but resizing the texture is done almost instantly in the GPU.
Take a look at the threadChannel example in examples/threads/threadChannelExample
It might be a much more straight frowards way to deal with it and you would not need to create a separate class for the player, as it already uses a different thread as arturo pointed out.

  • I exchanged the vector with a single ofPixels object. Still leaking
  • I completely rolled back to a “normal” instance of ofVideoPlayer (not within an ofThread class), as you suggested. Basically the implementation of the documentation. It is still allocating large chunks of memory only upon video playback. They stay there until they cause a core dump.

Is there a way to flush unused frames?

I’ll try to compile it with OF 100. However I really think that ofVideoPlayer is leaking. My hunch is that there are frames that the player thread reads but can’t pass to my application. These frames just remain “somewhere”.

I can confirm that there is a memory leak in ofVideoPlayer in Linux see:

I ran the example provided with two different (large) videos.

@arturo @roymacdonald

I’m checking using that example and it works fine for me. Are you in 0.10? Memory usage will grow at the beginning but will stay constant after a while. For me using a 1280x720 video i see it going up until around 300Mb then stays around that going up and down between 270 - 330Mb but it doesn’t go beyond that after around 100 iterations

That behaviour is expected as gstreamer will allocate memory as it needs it to optimize the playback.

i’ve had now that example running for over 2 hours and 500 iterations and the memory doesn’t ever go up from ~300Mb

If you still see any leak with that simple example apart from that initial build up and you are using 0.10 (although i’m almost sure that 0.9.8 was also well tested for leaks) there might be a problem with your version of gstreamer. i would try using a newer version of ubuntu in that case if possible

Ok,

the behavior that you describe is similar to the one that I have.
For large videos the memory usage of gstreamer is not very efficient. Specially switching between different different videos is not efficient at all.

I’ll run ffmpeg to scale down the videos.