Sampling frames in ofVideoPlayer in one loop

My final goal is to load and sample a folder of videos in one loop.

But to simplify the problem here are some snippets.

This works:

//keypress 1
fingerMovie.load("movies/output.mp4");
fingerMovie.play();

//keypress 2
fingerMovie.setPaused(true);
fingerMovie.setFrame(200);

//keypress 3
fingerMovie.update();

//keypress 4
ofPixels & pixels = fingerMovie.getPixels();
ofSaveImage(pixels,ofGetTimestampString()+".jpg" );

This doesn’t:
(It saves out black frames- or frames with streaks of noise)

//keypress 1
        fingerMovie.load("movies/output.mp4");
        fingerMovie.play();
        fingerMovie.setPaused(true);
        fingerMovie.setFrame(200);
        fingerMovie.update();
        ofPixels & pixels = fingerMovie.getPixels();
        ofSaveImage(pixels,ofGetTimestampString()+".jpg" );

I assume this is because of some OpenGL refresh that is happening ofMainLoop?

Is that correct? Is there a way around it besides breaking it up into chunks that allow ofMainLoop to run in-between?

Hi @Caroline_Record !
It depends on what you are using as the video player. quicktime does not like to do such thing, use GStreamer instead. On linux it is the default, on Mac and Windows you need to do the following:

you will need ofxGstreamer https://github.com/arturoc/ofxGStreamer
follow the install instructions in its readme;

Include ofxGStreamer to your project using ProjectGenerator

then:

ofApp.h

#pragma once

#include "ofMain.h"
#include "ofxGStreamer.h"
#include "ofGstVideoPlayer.h"

class ofApp : public ofBaseApp{

	public:
		void setup();
		void update();
		void draw();
	
      shared_ptr<ofGstVideoPlayer> gst;
      ofVideoPlayer vid;
    

};

ofApp.cpp

#include "ofApp.h"

//--------------------------------------------------------------
void ofApp::setup(){
    
    gst = make_shared<ofGstVideoPlayer>();    
    
    vid.setPlayer(gst);
    gst->setFrameByFrame(true);
    vid.setUseTexture(false);// call this if you dont need to load the texture.
    if(vid.load("hands.mp4")){
        for(int currentFrame = 0; currentFrame < vid.getTotalNumFrames(); currentFrame++){
        vid.setFrame(currentFrame);
        vid.update();
        for (int i = 0; i < 20; i++) {// this loop is to allow the player to update. kinda ugly hack but works. Took it from an old project. Maybe it is not needed anymore.
            if(vid.isFrameNew()) {
                break;
            }
            vid.update();
        }
        ofPixels & pixels = vid.getPixels();
        ofSaveImage(pixels,ofToString(currentFrame)+".jpg" );
        }
    }
}
//--------------------------------------------------------------
void ofApp::update(){ }
//--------------------------------------------------------------
void ofApp::draw(){ }

hope this is useful

1 Like

Thanks @roymacdonald! Here is how I worked around it, before your post. It’s a bit slow but works.

Using ofxGStreamer looks much cleaner.

   float frm = int(ofMap(percent, 0, 1, 1, testVideo.getDuration()));

    int minutes= int(frm/60);
    string mins ="";
    if(minutes<10){
        mins="0"+ofToString(minutes);
    }else{
        mins=ofToString(minutes);
    }
    float seconds= frm-(minutes*60);
    string secs="";
    if(seconds<10){
        secs="0"+ofToString(seconds);
    }else{
        secs=ofToString(seconds);
    }
    //ffmpeg -i input.mov -ss 00:00:14.435 -vframes 1 out.png
    string cmd = exPath + "ffmpeg -y -i "+aPath+"/"+flPth +" -ss 00:"+mins+":"+secs+" -vframes 1 "+aPath +"/temp/tempImg.png";
    ofSystem(cmd);
    ofImage tempPic;
    tempPic.load("videos/temp/tempImg.png");
    
    ofPixels & pix = tempPic.getPixels();
1 Like