screenrecording

I grabbed Screeny when it was free in the Mac App Store, and it works well, but doesn’t hide the mouse pointer. So for my last project I used ScreenFlick.

More alternatives: http://mac.appstorm.net/roundups/utilities-roundups/10-screen-recording-tools-for-mac/

Not the most cheap possible, but you can use Camtasia Studio in Win 7 http://www.techsmith.com/camtasia/

I prefer saving images every frame, or whenever it is necessary. This way you don’t lose any frames like you most likely will with an external screen grabbing program.

I’ve come up with a fairly efficient method of doing this because I needed to write a program that could record live video from up to three cameras at once.

Here’s what I’ve learned form doing so:
I started with memo’s threaded image saver, and initially extended it to use a linked list of queued up frames to save just in case the disk gets slowed down for a bit, so it’s essentially saving images to memory first, in the form of unsigned char arrays, then writing them to disk as jpgs. If you have a slow disk, then you’ll eventually run out of memory doing this, so in some cases you will have to drop a frame here or there. Or, get an SSD and you’ll be golden.

The second enhancement was to stop saving each frame as an individual file. This can drastically reduce the amount of IO calls because you no longer need to create a new file every time you want to save a frame, instead it gets tacked on to an existing file that is kept open while the program is recording. I did this by using the freeImage library to get compressed jpg binary data into memory and then send that into a file stream. The great thing about this is that mashing a bunch of JPGs together one after another is very similar to an MJPEG video file. All you need to do to make it into a real MJPEG .avi or .mov is run it through ffmpeg. you can even use the ‘-vcodec copy’ option and the re-encoding takes no time at all.

The code I’ve got right now is terribly messy and specialized but this should be a working version, I may package this up into a nice addon soon. You’ll need ffmpeg installed to use the encodeVideo() function:

threadedVidSaver.h

It would be nice to wrap ffmpeg somehow and just throw frames at it, I wonder if that would work any better than this method?

i’ve been using something similar to this lately but instead of storing the whole bitmap in memory i compress it as jpg before, store the jpeg in the queue and then have a thread saving from that queue or if you are recording a short clip just save everything at the end:

  
  
.h  
  
ofPixels pixels;  
ofFbo fbo;  
queue<ofBuffer> frames;  
  
  
.cpp  
  
fbo.begin();  
//draw...  
fbo.end();  
  
fbo.readToPixels(pixels);  
frames.push(ofBuffer());  
ofSaveImage(pixels,queue.back(),OF_IMAGE_FORMAT_JPEG);  
  

then at the end f the recording or in a thread:

  
  
int i=0;  
while(!frames.empty()){  
    ofFile frame(ofToString(i)+".jpg",ofFile::WriteOnly,true);  
    frame << frames.front();  
    frame.close();  
    frames.pop();  
    i++;  
}  
  

2 Likes

Cool, arturo. I wasn’t aware that you could use an ofBuffer with ofSaveImage. This is a very elegant way of getting the compressed data. I will use this approach in the future.

The one potential problem I see is with using the stl library for the queue, I thought these were not thread safe? no problem if you’re buffering and saving at the end, but if you want to save on the fly in a separate thread it could introduce some problems. That is the only reason I bothered to make my own thread safe linked list class.

yes, if you are using a thread then you’ll need to lock at some point, in that case i’ll use a queue of pointers to ofBuffers so the locking doesn’t take too much time.

  
  
queue<ofBuffer*> frames;  
  
// saving to memory:  
ofBufer * buffer = new ofBuffer;  
ofSaveImage(pixels,*buffer,OF_IMAGE_FORMAT_JPEG);  
saverThread.lock();  
frames.push(buffer);  
saverThread.unlock();  
  
// saving to disk:  
while(isThreadRunning()){  
ofBuffer * frame = NULL;  
lock();  
if(!frames.empty()){  
    frame = frames.front();  
    frames.pop();  
}  
unlock();  
if(frame){  
    // save frame to disk  
}else{  
    ofSleepMillis(x);  
}  
}  
  

Arturo, I’ve been incorporating some of your methods into my code, which is helping clean things up quite a bit. using the stl queue seems to have no performance impact over my own linked list, and is much cleaner. I also tried putting the compression before adding to the queue as you sugest, but this actually makes things much slower in the main thread because you are then waiting the extra time it takes to compress before the addFrame() function (or whatever it might be) returns. Similarly, using the compression method I posted seems to be much faster than using ofBuffer and ofSaveImage, I’m guessing due to decreased overhead.

Anyway, I’m now able to record 640x480@60fps from a webcam in very close to real time, with a queue growth rate of only 0.5 - 2 frames/second. Using ofBuffer and ofSaveImage I was getting a growth rate of 10-15 frames/second.

https://github.com/timscaffidi/ofxVideoRecorder

Let me know if you have any suggestions or optimizations

Hi, I’m also looking for a way to record the screen of my app. I use OF_FULLSCREEN to make my app fullscreen. I tried Camstudio and Camtasia and both of them can’t record the screen when I’m in fullscreen mode and return black screens. I’ll appreciate if you can help me with this.
Thanks very much

1 Like

Popular OBS, FoneLab, TinyTake can all word on OSX. Now typically I use FoneLab to record screen and it works fine so far

I used ofxMacScreenRecorder and it worked fine. I’m not used others btw

I like rendering the output to an FBO and then reading to pixels with ofxFastFboReader and saving to a video file with ofxFFmpegRecorder.
With this method, you can have a gui visible in your OF window to tweak parameters while recording, but you don’t have to render that to the fbo so it won’t show up in the saved video. This also allows you to create an fbo that is larger than the resolution of your screen.

3 Likes

Is this approach mutliplatform? I usually use ofxVideoRecorder but I am not really satisfied because the resolution is limited. I have see that in your repository there is an example, but it is not including ofxFastFboReader

Hi @edapx, yeah it uses ffmpeg which is multiplatform and ofxFastFboReader uses pixel buffer objects, which should be as well? I have tested on OSX and Windows and included static libs for both so that it is portable. It should work on Linux if you install ffmpeg and set ffmpeg in your systems path, though I don’t have much experience with Linux.
There is an option to change the bitrate if you want higher quality.

All you need to do is change the pixel reading to use ofxFastFboReader like below.

mReader.readToPixels(mCapFbo, mPixels, OF_IMAGE_COLOR);
		if (mPixels.getWidth() > 0 && mPixels.getHeight() > 0) {
			m_Recorder.addFrame(mPixels);
		}

OBS is a free screen recorder. I used it before, but as for me, it is a bit complex to use. Now I am using Joyoshare Screen Recorder. It is free for 5 minues.

Hey I am trying to use ofxFastFboReader and with ofxFFmpegRecorder OF/0.11 under Windows10.

It works, but the recorded video file goes to full-screen-grey if I enabled antialias on the FBO settings…
If I do not enables the antialias samples the lines don’t look fine. I tried to use GL_RGB32F but is not working either.

(I would like to use an OF internal method bc I tried some external apps like OBS and Bandicam and it’s losing some frames and does not go fluently…)

Any idea?

This is the code I am using:

ofApp.h

    #include "ofxFFmpegRecorder.h"
    #include "ofxFastFboReader.h"

	ofxFFmpegRecorder cap_Recorder;
	ofFbo cap_Fbo;
	ofPixels cap_Pix;
	ofxFastFboReader cap_Reader;
	ofFbo::Settings cap_Fbo_Settings;

ofApp::setup()

    //ofFbo settings
    float w, h;
	w = ofGetWidth();
	h = ofGetHeight();
	cap_Fbo_Settings.width = w;
	cap_Fbo_Settings.height = h;

	cap_Fbo_Settings.internalformat = GL_RGB;
	//cap_Fbo_Settings.internalformat = GL_RGBA;
	//cap_Fbo_Settings.internalformat = GL_RGB32F;
	cap_Fbo_Settings.useDepth = true;//3d depth
	cap_Fbo_Settings.useStencil = true;//?
    //cap_Fbo_Settings.depthStencilAsTexture = true;//?
	
    //The problem: Can't antialias -->

    //->this breakes the recording...grey screen
	//cap_Fbo_Settings.numSamples = 16;// ! 

	cap_Fbo.allocate(cap_Fbo_Settings);

    //--

	//hq
	cap_Recorder.setBitRate(80000);
	cap_Recorder.setup(true, false, glm::vec2(w, h), 60);

	//options
	cap_Recorder.setOverWrite(true);
	cap_Recorder.setFFmpegPathToAddonsPath();
	//cap_Recorder.addAdditionalOutputArgument("-f rawvideo");
	//cap_Recorder.setVideoCodec("libx264");
	//cap_Reader.setAsync(true);

ofApp::draw()

cap_Fbo.begin();
{
  //ofSetColor(255);
  //ofEnableDepthTest();
      drawMyScene();
}
cap_Fbo.end();

//-

if (cap_Recorder.isRecording())
		{
			//A. faster
			//ofxFastFboReader can be used to speed this up:
			cap_Reader.readToPixels(cap_Fbo, cap_Pix, OF_IMAGE_COLOR);
			if (cap_Pix.getWidth() > 0 && cap_Pix.getHeight() > 0) {
				cap_Recorder.addFrame(cap_Pix);
			}

			////B. standard
			//cap_Fbo.readToPixels(cap_Pix);
			//if (cap_Pix.getWidth() > 0 && cap_Pix.getHeight() > 0) {
			//	cap_Recorder.addFrame(cap_Pix);
			//}
		}

		//-

		cap_Fbo.draw(0, 0);//drawing is required outside fbo
}

This is the log with samples enabled after recording a grey video:

ffmpeg version N-84679-gd65b595 Copyright (c) 2000-2017 the FFmpeg developers
  built with gcc 6.3.0 (GCC)
  configuration: --enable-gpl --enable-version3 --enable-cuda --enable-cuvid --enable-d3d11va --enable-dxva2 --enable-libmfx --enable-nvenc --enable-avisynth --enable-bzlib --enable-fontconfig --enable-frei0r --enable-gnutls --enable-iconv --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libfreetype --enable-libgme --enable-libgsm --enable-libilbc --enable-libmodplug --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenh264 --enable-libopenjpeg --enable-libopus --enable-librtmp --enable-libsnappy --enable-libsoxr --enable-libspeex --enable-libtheora --enable-libtwolame --enable-libvidstab --enable-libvo-amrwbenc --enable-libvorbis --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxavs --enable-libxvid --enable-libzimg --enable-lzma --enable-zlib
  libavutil      55. 51.100 / 55. 51.100
  libavcodec     57. 86.103 / 57. 86.103
  libavformat    57. 67.100 / 57. 67.100
  libavdevice    57.  3.101 / 57.  3.101
  libavfilter     6. 78.100 /  6. 78.100
  libswscale      4.  3.101 /  4.  3.101
  libswresample   2.  4.100 /  2.  4.100
  libpostproc    54.  2.100 / 54.  2.100
Input #0, rawvideo, from 'pipe:':
  Duration: N/A, start: 0.000000, bitrate: 2922393 kb/s
    Stream #0:0: Video: rawvideo (RGB[24] / 0x18424752), rgb24, 1920x1057, 2922393 kb/s, 60 tbr, 60 tbn, 60 tbc
Stream mapping:
  Stream #0:0 -> #0:0 (rawvideo (native) -> mpeg4 (native))
Output #0, avi, to 'F:\openFrameworks\addons\ofxSphereCam2\ofxSphereCam2-example\bin\data\captures/2020-08-12-15-38-45-781.avi':
  Metadata:
    ISFT            : Lavf57.67.100
    Stream #0:0: Video: mpeg4 (FMP4 / 0x34504D46), yuv420p, 1920x1057, q=2-31, 2000 kb/s, 60 fps, 60 tbn, 60 tbc
    Metadata:
      encoder         : Lavc57.86.103 mpeg4
    Side data:
      cpb: bitrate max/min/avg: 0/0/2000000 buffer size: 0 vbv_delay: -1
frame=  491 fps= 60 q=2.0 Lsize=    1360kB time=00:00:08.18 bitrate=1361.6kbits/s speed=   1x
video:1343kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 1.303381%

Have you tried loading the cap_Pix into a texture and drawing that? It could be an issue with anti aliasing and ofxFastFboReader?
If anti aliasing is causing you issues, you could create a fbo just for blitting the other fbo into.
ie blitFbo.allocate( cap_Fbo.getWidth(), cap_Fbo.getHeight, GL_RGB );
Then draw the cap_fbo into the blitFbo and use the fast fbo reader on the blit fbo.
You should be able to get the benefits of aliasing and have it still work in the recorder.

nick

thanks @NickHardeman,
your trick worked well.

As you said, loading the cap_Pix into a texture and drawing goes black too.
(So if I understand you well, maybe it’s an ofxFastFboReader bug. )
But your blitting trick worked perfectly.

        cap_w = ofGetWidth();
		cap_h = ofGetHeight();
		cap_Fbo_Settings.internalformat = GL_RGB;
		cap_Fbo_Settings.width = cap_w;
		cap_Fbo_Settings.height = cap_h;
		cap_Fbo_Settings.useDepth = true;
		cap_Fbo_Settings.numSamples = 32;
		//cap_Fbo_Settings.useStencil = true;//?
		//cap_Fbo_Settings.depthStencilAsTexture = true;//?
		cap_Fbo.allocate(cap_Fbo_Settings);

		//TEST: BUG: antialias
		blitFbo.allocate(cap_Fbo.getWidth(), cap_Fbo.getHeight(), GL_RGB);
			if (cap_Recorder.isRecording())
			{
				////A. faster
				////ofxFastFboReader can be used to speed this up:
				//cap_Reader.readToPixels(cap_Fbo, cap_Pix, OF_IMAGE_COLOR);
				//if (cap_Pix.getWidth() > 0 && cap_Pix.getHeight() > 0) {
				//	cap_Recorder.addFrame(cap_Pix);
				//}

				////B. standard
				//cap_Fbo.readToPixels(cap_Pix);
				//if (cap_Pix.getWidth() > 0 && cap_Pix.getHeight() > 0) {
				//	cap_Recorder.addFrame(cap_Pix);
				//}

				//TEST: BUG: antialias
				//cap_Tex.allocate(cap_Pix);

				//-

				//TEST: BUG: antialias
				//C. blitting test
				blitFbo.begin();
				ofClear(0);
				cap_Fbo.draw(0, 0, cap_w, cap_h);
				blitFbo.end();

				cap_Reader.readToPixels(blitFbo, cap_Pix, OF_IMAGE_COLOR);
				if (cap_Pix.getWidth() > 0 && cap_Pix.getHeight() > 0) {
					cap_Recorder.addFrame(cap_Pix);
				}
			}

Hey @moebiussurfing thanks for sharing.
Just wondering how about recording a 4k (e.g. 3840 x 1080) video?
I’ve tried to record some simple motion (e.g ofDrawCircle) with a 4K fbo setting but the fps drops dramatically to ~20 fps (MBP i5 2.3G, 16G ram).
Do you have any suggestion to increase fps performance?

sorry I don’t know. Are you using Release or Debug? Not sure on macOS, but on Windows, Debug runs slower… Maybe you can set the FFmpeg encoder to record raw video (or just the store the still frames and join them externally) without any encoding.

No worries @moebiussurfing, I’m using Debug, I will give it a try with different settings for FFmpeg. Thanks!