Skip to content

Instantly share code, notes, and snippets.

@jvcleave
Last active December 26, 2018 21:51
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jvcleave/315456c2c5dd7a8a4ddf158410af6572 to your computer and use it in GitHub Desktop.
Save jvcleave/315456c2c5dd7a8a4ddf158410af6572 to your computer and use it in GitHub Desktop.
videocore fast pixel access
#pragma once
#include "ofMain.h"
#include "ofAppEGLWindow.h"
#include "user-vcsm.h"
#include "ofxOMXPlayer.h"
#include "TerminalListener.h"
class ofApp : public ofBaseApp, public KeyListener{
public:
ofxOMXPlayer omxPlayer;
unsigned char* videoCorePixels;
int videoCoreBufferDim;
int videoCorePixelDataSize;
ofTexture videoCoreTexture;
EGLImageKHR eglFbImage;
ofFbo eglEnabledFBO;
ofTexture readPixelsTexture;
void setup();
void update();
void draw();
TerminalListener consoleListener;
void keyPressed(int key);
void onCharacterReceived(KeyListenerEventData& e)
{
keyPressed((int)e.character);
}
};
#include "ofApp.h"
#define GLCHK(X) \
do { \
GLenum err = GL_NO_ERROR; \
X; \
while ((err = glGetError())) \
{ \
ofLog(OF_LOG_ERROR, "GL error 0x%x in " #X "file %s line %d", err, __FILE__,__LINE__); \
vcos_assert(err == GL_NO_ERROR); \
} \
} \
while(0)
egl_image_brcm_vcsm_info vcsm_info;
vector<unsigned char> videoCorePixelVector;
bool doSaveImage = false;
bool doEnablePixels = true;
bool doUseReadPixels = false;
bool doDrawPixels = true;
void ofApp::setup()
{
ofSetLogLevel(OF_LOG_VERBOSE);
consoleListener.setup(this);
omxPlayer.loadMovie("/home/pi/videos/current/Timecoded_Big_bunny_1.mov");
readPixelsTexture.allocate(omxPlayer.getWidth(), omxPlayer.getHeight(), GL_RGBA);
vcsm_init();
/*
videoCoreBufferDim must be one of:
64
128
256
512
1024
2048
*/
videoCoreBufferDim = 1024;
eglEnabledFBO.allocate(videoCoreBufferDim, videoCoreBufferDim);
videoCorePixelDataSize = videoCoreBufferDim * videoCoreBufferDim * 4;
videoCorePixelVector.resize(videoCorePixelDataSize);
videoCorePixels = &videoCorePixelVector[0];
memset(videoCorePixels, 0xff, videoCorePixelDataSize); //set to white
videoCoreTexture.loadData(videoCorePixels, videoCoreBufferDim, videoCoreBufferDim, GL_RGBA);
vcsm_info.width = videoCoreBufferDim;
vcsm_info.height = videoCoreBufferDim;
ofAppEGLWindow* appEGLWindow = (ofAppEGLWindow *) ofGetWindowPtr();
eglEnabledFBO.begin();
eglFbImage = eglCreateImageKHR(appEGLWindow->getEglDisplay(), EGL_NO_CONTEXT,
EGL_IMAGE_BRCM_VCSM, &vcsm_info, NULL);
if (eglFbImage == EGL_NO_IMAGE_KHR)
{
ofLog() << "Create EGLImage FAIL <---------------- :(";
}
else
{
ofLog() << "Create EGLImage PASS <---------------- :)";
ofLog() << "vcsm_info.vcsm_handle: " << vcsm_info.vcsm_handle;
}
eglEnabledFBO.end();
eglEnabledFBO.getTexture().bind();
GLCHK(glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, eglFbImage));
eglEnabledFBO.getTexture().unbind();
}
void ofApp::update()
{
}
void ofApp::draw()
{
if(doEnablePixels)
{
if(omxPlayer.isFrameNew())
{
if(doUseReadPixels)
{
omxPlayer.updatePixels();
if(doDrawPixels)
{
readPixelsTexture.loadData(omxPlayer.getPixels(), omxPlayer.getWidth(), omxPlayer.getHeight(), GL_RGBA);
}
}else
{
unsigned char *vcsm_buffer = NULL;
VCSM_CACHE_TYPE_T cache_type;
eglEnabledFBO.begin();
omxPlayer.draw(0, 0, videoCoreBufferDim, videoCoreBufferDim);
eglEnabledFBO.end();
// Make the buffer CPU addressable with host cache enabled
vcsm_buffer = (unsigned char *) vcsm_lock_cache(vcsm_info.vcsm_handle, VCSM_CACHE_TYPE_HOST, &cache_type);
if (!vcsm_buffer) {
ofLog(OF_LOG_ERROR,"Failed to lock VCSM buffer for handle %d\n", vcsm_info.vcsm_handle);
}else
{
memcpy(videoCorePixels, vcsm_buffer, videoCorePixelDataSize);
//int randomStart = ofRandom(pixelVector.size()/4);
//random_shuffle(pixelVector.begin()+randomStart, pixelVector.end();
// Release the locked texture memory to flush the CPU cache and allow GPU to read it
vcsm_unlock_ptr(vcsm_buffer);
}
//
if(doDrawPixels)
{
videoCoreTexture.loadData(videoCorePixels, videoCoreBufferDim, videoCoreBufferDim, GL_RGBA);
}
}
}
}
stringstream description;
if(doEnablePixels)
{
description << "Pixels are enabled." << endl;
if(doDrawPixels)
{
description << "Reloading pixels into a texture which is the biggest bottleneck." << endl;
if(doUseReadPixels)
{
description << "Using glReadPixels which is the slowest option." << endl;
readPixelsTexture.draw(0, 0, omxPlayer.getWidth(), omxPlayer.getHeight());
}else
{
description << "Using videocore direct access which is the faster option." << endl;
videoCoreTexture.draw(0, 0, omxPlayer.getWidth(), omxPlayer.getHeight());
}
}else
{
if(doUseReadPixels)
{
description << "Using glReadPixels which is still taxing even though not being drawn" << endl;
}else
{
description << "Using videocore direct access which captures pixels with little performance hit" << endl;
}
omxPlayer.draw(0, 0, omxPlayer.getWidth(), omxPlayer.getHeight());
}
}else
{
description << "Pixels are are disabled." << endl;
omxPlayer.draw(0, 0, omxPlayer.getWidth(), omxPlayer.getHeight());
}
stringstream info;
info << "DESCRIPTION: " << endl;
info << description.str() << endl;
info << omxPlayer.getInfo() << endl;
info << "PRESS 1 TO ENABLE PIXELS: " << doEnablePixels << endl;
info << "PRESS 2 TO TOGGLE doUseReadPixels: " << doUseReadPixels << endl;
info << "PRESS 3 TO TOGGLE doDrawPixels: " << doDrawPixels << endl;
ofDrawBitmapStringHighlight(info.str(), 100, 60, ofColor(ofColor::black, 90), ofColor::yellow);
}
void ofApp::keyPressed (int key)
{
ofLog(OF_LOG_VERBOSE, "%c keyPressed", key);
switch(key)
{
case '1':
{
doEnablePixels = !doEnablePixels;
break;
}
case '2':
{
doUseReadPixels = !doUseReadPixels;
break;
}
case '3':
{
doDrawPixels = !doDrawPixels;
break;
}
case 's':
{
doSaveImage = !doSaveImage;
break;
}
case ' ':
{
omxPlayer.togglePause();
break;
}
}
}
@jvcleave
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment