Skip to content

Instantly share code, notes, and snippets.

@roxlu
Created October 11, 2013 20:07
Show Gist options
  • Save roxlu/6941190 to your computer and use it in GitHub Desktop.
Save roxlu/6941190 to your computer and use it in GitHub Desktop.
ofxLinkDeck
#include <ofxLinkDeck/ofxLinkDeck.h>
#include <iostream>
//#include <libyuv/libyuv.h>
ofxLinkDeck::ofxLinkDeck()
:pixel_format(0)
,display_mode(0)
,pixels(NULL)
,w(0)
,h(0)
,sws(NULL)
,has_new_frame(false)
{
img.setUseTexture(false);
uv_mutex_init(&mutex);
}
ofxLinkDeck::~ofxLinkDeck() {
pixel_format = 0;
display_mode = 0;
if(pixels) {
delete[] pixels;
pixels = NULL;
}
}
#if 0
void ofxLinkDeck::setFrameCallback(decklink_capture_callback frameCB, void* userCB) {
cb_frame = frameCB;
cb_user = userCB;
}
#endif
bool ofxLinkDeck::setup(int device, BMDPixelFormat pixelFormat, BMDDisplayMode displayMode) {
if(!dl.setup(device)) {
printf("error: cannot setup the decklink device.\n");
return false;
}
if(!dl.setCallback(ofx_linkdeck_frame, this)) {
printf("error: cannot setup ofxLinkDeck, failed settings the frame callbabck.\n");
return false;
}
pixel_format = pixelFormat;
display_mode = displayMode;
if(pixelFormat != bmdFormat8BitYUV) {
printf("error: this addons has only tested bmdFormat8BitYUV.\n");
return false;
}
if(!dl.setVideoMode(displayMode, pixelFormat)) {
printf("error: cannot set the video mode.\n");
return false;
}
w = dl.getWidth(display_mode);
if(w < 0) {
printf("error: cannot get the width for the current display mode, so we can't continue.\n");
return false;
}
h = dl.getHeight(display_mode);
if(h < 0) {
printf("error: cannot get the height for the current display mode so we can't continue.\n");
return false;
}
nbytes = w * h * 3;
pixels = new uint8_t[nbytes];
if(!pixels) {
printf("error: cannot allocate the buffer for the converted image.\n");
return false;
}
sws = sws_getContext(w, h, AV_PIX_FMT_UYVY422,
w, h, AV_PIX_FMT_RGB24,
SWS_FAST_BILINEAR, NULL, NULL, NULL);
if(!sws) {
printf("error: cannot setup the sws conversion context.\n");
return false;
}
return true;
}
void ofxLinkDeck::listDevices() {
dl.printDevices();
}
// --------------------------------------------
void ofx_linkdeck_frame(IDeckLinkVideoInputFrame* vframe, IDeckLinkAudioInputPacket* aframe, void* user) {
printf("frame callback.\n");
if(!vframe) {
printf("error:invalid vframe.\n");
return;
}
#if !defined(NDEBUG)
if(!user) {
printf("error: no user pointer set in the ofx_linkdeck_frame, which we need to validate the frame size for the memcpy/conversion.\n");
return;
}
ofxLinkDeck* ld = static_cast<ofxLinkDeck*>(user);
if(!ld) {
printf("error: cannot cast the user pointer to ofxLinkDeck.\n");
return;
}
#endif
uint32_t w = vframe->GetWidth();
uint32_t h = vframe->GetHeight();
uint32_t stride = vframe->GetRowBytes();
uint8_t* uyvy422 = NULL;
#if !defined(NDEBUG)
if(w != ld->w || h != ld->h) {
printf("error: the input size is not the same as the one defined.\n");
::exit(EXIT_FAILURE);
}
#endif
HRESULT r = vframe->GetBytes((void**)&uyvy422);
if(r != S_OK) {
printf("error: cannot get yuv422 bytes.\n");
return ;
}
uint64_t n = uv_hrtime();
uv_mutex_lock(&ld->mutex);
{
unsigned char* dest_planes[1] = { ld->pixels } ;
int in_stride = stride;
int dest_stride = ld->w * 3;
int sr = sws_scale(ld->sws,
(uint8_t**)&uyvy422,
&in_stride,
0,
h,
dest_planes,
&dest_stride);
if(sr != h) {
printf("error: the scaling went wrong...\n");
}
else {
ld->has_new_frame = true;
}
}
uv_mutex_unlock(&ld->mutex);
uint64_t d = uv_hrtime() - n;
printf("took: %lld ns, %f ms to convert.\n", d, double(d)/1000000.0);
}
/*
---------------------------------------------------------------------------------
oooo
`888
oooo d8b .ooooo. oooo ooo 888 oooo oooo
`888""8P d88' `88b `88b..8P' 888 `888 `888
888 888 888 Y888' 888 888 888
888 888 888 .o8"'88b 888 888 888
d888b `Y8bod8P' o88' 888o o888o `V88V"V8P'
www.roxlu.com
www.apollomedia.nl
www.twitter.com/roxlu
---------------------------------------------------------------------------------
# ofxLinkDeck
There are a couple of other Black Magic Decklinks but from what I read
those were only Windows... I had to get this working on Mac in at most
"no time". So this code isnt heavily tested but it seems to work prety well.
Make sure that if you use this and you want to get the pixels from the decklink
they are converted from UYVY422 to RGB24 (this is not efficient but it had to
work like this for the project I created this for).
Also the frame data is given to used by the decklink sdk in a separate thread,
therefore you need to call 'lock()' and 'unlock()' when you retrieve the
pixels.
EXAMPLE:
See: https://gist.github.com/roxlu/6941174#file-testapp-cpp-L29-L34
IMPORTANT:
- call lock()/unlock() when retrieving pixels
- call resetHasNewFrame()
*/
#ifndef ROXLU_OFX_LINK_DECK_H
#define ROXLU_OFX_LINK_DECK_H
#include <decklink/DeckLink.h>
#include <libyuv/libyuv.h>
#include <stdint.h>
#include "ofMain.h"
extern "C" {
# include <libswscale/swscale.h>
# include <uv.h>
}
void ofx_linkdeck_frame(IDeckLinkVideoInputFrame* vframe, IDeckLinkAudioInputPacket* aframe, void* user);
class ofxLinkDeck {
public:
ofxLinkDeck();
~ofxLinkDeck();
bool setup(int device, BMDDisplayMode displayMode, BMDPixelFormat pixelFormat);
void listDevices(); /* list the available decklinks */
bool start(); /* start getting the video data */
bool hasNewFrame(); /* check if there is a new frame in the pixels member; make sure to call resetHasNewFrame() when you grabbed them */
void resetHasNewFrame(); /* call this to reset the "has_new_frame" flag */
int getWidth();
int getHeight();
void lock(); /* we're getting the pixels from another thread so need to lock/unlock ths pixels member */
void unlock(); /* "" */
unsigned char* getPixels(); /* MAKE SURE TO LOCK() AND UNLOCK() around getPixels() */
private:
DeckLink dl; /* this does all the work with the decklink sdk */
BMDPixelFormat pixel_format; /* the used decklink pixel format */
BMDDisplayMode display_mode; /* the used display mode */
public:
uint8_t* pixels; /* pixels that stores the video */
uint32_t nbytes; /* number of bytes in pixels */
int w; /* width of the captured frame */
int h; /* height o fhte captured frame */
SwsContext* sws; /* used to convert the incoming uyvy422 stream to rgb24*/
bool has_new_frame; /* is set to "true" when we get a new frame from the decklink sdk, is set in ofx_linkdeck_frame */
uv_mutex_t mutex; /* mutex used to protect the ofImge + has_new_frame */
};
inline bool ofxLinkDeck::start() {
return dl.start();
}
inline void ofxLinkDeck::resetHasNewFrame() {
uv_mutex_lock(&mutex);
has_new_frame = false;
uv_mutex_unlock(&mutex);
}
inline bool ofxLinkDeck::hasNewFrame() {
uv_mutex_lock(&mutex);
bool h = has_new_frame;
uv_mutex_unlock(&mutex);
return h;
}
inline int ofxLinkDeck::getWidth() {
return w;
}
inline int ofxLinkDeck::getHeight() {
return h;
}
inline void ofxLinkDeck::lock() {
uv_mutex_lock(&mutex);
}
inline void ofxLinkDeck::unlock() {
uv_mutex_unlock(&mutex);
}
// make sure to call lock() / unlock()
inline unsigned char* ofxLinkDeck::getPixels() {
return (unsigned char*)pixels;
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment