lms-video/Gem/plugins/videoUNICAP/videoUNICAP.cpp
Santi Noreña e85d191b46 - Reestructuración de ficheros y directorios general
- merge v0.01 --> Añadido fileselector
- Añadidas fuentes de Gem y Pure Data
- pix2jpg incluído en Gem. Archivos de construcción de Gem modificados.
- Añadido fichero ompiling.txt con instrucciones de compilación
2013-02-04 18:00:17 +01:00

778 lines
20 KiB
C++

////////////////////////////////////////////////////////
//
// GEM - Graphics Environment for Multimedia
//
// zmoelnig@iem.kug.ac.at
//
// Implementation file
//
// Copyright (c) 2001-2012 IOhannes m zmölnig. forum::für::umläute. IEM. zmoelnig@iem.at
// For information on usage and redistribution, and for a DISCLAIMER OF ALL
// WARRANTIES, see the file, "GEM.LICENSE.TERMS" in this distribution.
//
/////////////////////////////////////////////////////////
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#if defined HAVE_LIBUNICAP && !defined HAVE_UNICAP
# define HAVE_UNICAP
#endif
#ifdef HAVE_UNICAP
#include "videoUNICAP.h"
#include "plugins/PluginFactory.h"
using namespace gem::plugins;
// for debugging...
#include "Gem/RTE.h"
/* debugging helpers */
#define debugPost
#define debugThread
#if 0
# undef debugPost
# define debugPost ::startpost("%s:%s[%d]", __FILE__, __FUNCTION__, __LINE__), ::post
#endif
#if 0
# undef debugThread
# define debugThread ::startpost("%s:%s[%d]", __FILE__, __FUNCTION__, __LINE__), ::post
#endif
/////////////////////////////////////////////////////////
//
// videoUNICAP
//
/////////////////////////////////////////////////////////
// Constructor
//
/////////////////////////////////////////////////////////
#include <sys/stat.h>
REGISTER_VIDEOFACTORY("unicap", videoUNICAP);
namespace {
static const std::string s_name = std::string("unicap");
}
videoUNICAP :: videoUNICAP(void) :
m_width(-1), m_height(-1),
m_handle(NULL),
m_devicenum(-1),
m_reqFormat(GL_RGBA_GEM),
m_running(false)
{
enumerate();
}
////////////////////////////////////////////////////////
// Destructor
//
////////////////////////////////////////////////////////
videoUNICAP :: ~videoUNICAP(void)
{
close();
}
//////////////////
// this reads the data that was captured by capturing() and returns it within a pixBlock
pixBlock *videoUNICAP :: getFrame(void) {
mutex.lock();
return &m_pix;
}
void videoUNICAP::releaseFrame(void) {
mutex.unlock();
}
bool videoUNICAP :: open(gem::Properties&props) {
std::vector<unsigned int>ds;
if(m_devicename.empty()) {
if(m_devicenum>=0)
ds.push_back(m_devicenum);
else
ds.push_back(0);
} else {
ds=m_name2devices[m_devicename];
}
unicap_status_t status = -1;
int i=0;
for(i=0; i<ds.size(); i++) {
unicap_device_t device;
unsigned int d=ds[i];
status = unicap_enumerate_devices (NULL, &device, d); // (1)
if(SUCCESS(status)) {
status = unicap_open (&m_handle, &device);
if(SUCCESS(status)) {
continue;
}
}
}
if(!SUCCESS(status))
return false;
setProperties(props);
return true;
}
void videoUNICAP :: close(void) {
if(m_handle) {
unicap_close(m_handle);
m_handle=NULL;
}
}
void videoUNICAP::newFrameCB (unicap_event_t event,
unicap_handle_t handle,
unicap_data_buffer_t * buffer,
void *usr_data)
{
videoUNICAP*v=(videoUNICAP*)usr_data;
debugPost("newFrameCB");
if(v && UNICAP_EVENT_NEW_FRAME==event)
v->newFrame(handle, buffer);
}
namespace {
typedef enum {
FIRST=0,
RGB,
RGBA,
BGR,
BGRA,
RGB16,
ABGR,
ARGB,
GREY,
GREY16,
UYVY,
YUY2,
YVYU,
YV12,
YU12,
ILLEGAL
} fourcc_t;
#ifdef FOURCC
# undef FOURCC
#endif
#define FOURCC(a,b,c,d) (unsigned int)((((unsigned int)d)<<24)+(((unsigned int)c)<<16)+(((unsigned int)b)<<8)+a)
/*
#define FOURCC(a) (unsigned int) (
((((unsigned char*)a)[0])<<24)+ \
((((unsigned char*)a)[1])<<16)+ \
((((unsigned char*)a)[2])<< 8)+ \
((((unsigned char*)a)[3])<< 0))
*/
static void post_fmt(unicap_format_t*fmt) {
if(!fmt)return;
const char*buftype="unknown";
if(UNICAP_BUFFER_TYPE_USER==fmt->buffer_type)buftype="USER";
if(UNICAP_BUFFER_TYPE_SYSTEM==fmt->buffer_type)buftype="SYSTEM";
debugPost("format '%s': ((%dx%d+%d+%d)) < (%dx%d+%d+%d) < ((%dx%d+%d+%d)) in +%d+%d steps\n"
"\t%d sizes\n"
"\tbuffers: %d types, %d systembuffers, %d size, type=%s"
"",
fmt->identifier,
fmt->min_size.width, fmt->min_size.height,fmt->min_size.x, fmt->min_size.y,
fmt->size.width, fmt->size.height,fmt->size.x, fmt->size.y,
fmt->max_size.width, fmt->max_size.height,fmt->max_size.x, fmt->max_size.y,
fmt->h_stepping, fmt->v_stepping,
fmt->size_count,
fmt->buffer_types, fmt->system_buffer_count, fmt->buffer_size, buftype,
0);
}
fourcc_t fourcc2fmt(unsigned int fourcc) {
fourcc_t format=ILLEGAL;
switch(fourcc) {
default: format=ILLEGAL; break;
case (FOURCC('R', 'G', 'B', '2')): format=RGB; break;
case (FOURCC('R', 'G', 'B', ' ')): format=RGB; break;
case (FOURCC('R', 'G', 'B', 'A')): format=RGBA; break;
case (FOURCC('B', 'G', 'R', ' ')): format=BGR; break;
case (FOURCC('B', 'G', 'R', 'A')): format=BGRA; break;
case (FOURCC('A', 'B', 'G', 'R')): format=ABGR; break;
case (FOURCC('A', 'R', 'G', 'B')): format=ARGB; break;
case (FOURCC('Y', '8', '0', '0')):
case (FOURCC('Y', ' ', ' ', ' ')):
case (FOURCC('G', 'R', 'E', 'Y')): format=GREY; break;
case (FOURCC('Y', '1', '6', ' ')): format=GREY16; break;
case (FOURCC('U', 'Y', 'N', 'V')):
case (FOURCC('Y', '4', '2', '2')):
case (FOURCC('H', 'D', 'Y', 'C')): // ?
case (FOURCC('U', 'Y', 'V', 'Y')): format=UYVY; break;
case (FOURCC('Y', 'U', 'Y', 'V')):
case (FOURCC('Y', 'U', 'N', 'V')):
case (FOURCC('Y', 'U', 'Y', '2')): format=YUY2; break;
case (FOURCC('Y', 'V', 'Y', 'U')): format=YVYU; break;
}
return format;
}
float dimension_penalty(int w, int h, unicap_rect_t&size) {
if(w>0 && h>0) {
if(1) {
// compare diagonals (penalty for very stretched formats
double r=sqrt(w*w+h*h);
double r0=sqrt(size.width*size.width+size.height*size.height);
return fabs(r-r0);
} else {
// compare area
double r=sqrt(w*h);
double r0=sqrt(size.width*size.height);
return fabs(r-r0);
}
} else if (w>0) {
return fabs(w-size.width);
} else if (h>0) {
return fabs(h-size.height);
}
return 0.;
}
};
void videoUNICAP::newFrame (unicap_handle_t handle,
unicap_data_buffer_t * buffer) {
unicap_format_t*fmt=&(buffer->format);
post_fmt(fmt);
fourcc_t format=fourcc2fmt(fmt->fourcc);
if(ILLEGAL==format) {
verbose(1, "unsupported format '%s'", fmt->identifier);
return;
}
unsigned char*data=buffer->data;
debugPost("data: %p", data);
mutex.lock();
m_pix.image.xsize=fmt->size.width;
m_pix.image.ysize=fmt->size.height;
m_pix.image.upsidedown=true;
m_pix.image.reallocate(); // actually this is also done in from*()
switch (format) {
case RGB: m_pix.image.fromRGB(data); break;
case RGBA: m_pix.image.fromRGBA(data); break;
case BGR: m_pix.image.fromBGR(data); break;
case BGRA: m_pix.image.fromBGRA(data); break;
case RGB16: m_pix.image.fromRGB16(data); break;
case ABGR: m_pix.image.fromABGR(data); break;
case ARGB: m_pix.image.fromARGB(data); break;
case GREY: m_pix.image.fromGray(data); break;
case GREY16: m_pix.image.fromGray((short*)data); break;
case UYVY: m_pix.image.fromUYVY(data); break;
case YUY2: m_pix.image.fromYUY2(data); break;
case YVYU: m_pix.image.fromYVYU(data); break;
case YV12: m_pix.image.fromYV12(data); break;
case YU12: m_pix.image.fromYU12(data); break;
default:
verbose(1, "cannot convert from given format");
break;
}
m_pix.newimage=1;
mutex.unlock();
}
/////////////////////////////////////////////////////////
// startTransfer
//
/////////////////////////////////////////////////////////
bool videoUNICAP :: start(void)
{
if(m_running)stop();
unicap_status_t status = 0;
unicap_format_t format;
// defaultFormat();
unicap_format_t format_spec;
unicap_void_format( &format_spec );
int default_format=0 ;
int default_size=0;
unsigned int count_format=0;
std::string s;
if(m_props.get("format", s)) {
for( unsigned i = 0; SUCCESS( unicap_enumerate_formats( m_handle, NULL, &format, i ) ); i++) {
count_format++;
if(s == format.identifier) {
default_format=i;
break;
}
}
}
std::vector<int>formatid;
if(default_format>=0) {
formatid.push_back(default_format);
} else {
for(int i=0; i<count_format; i++) {
formatid.push_back(i);
}
}
int w=-1;
int h=-1;
double d;
if(m_props.get("width" , d) && d>0)w=d;
if(m_props.get("height", d) && d>0)h=d;
double penalty=-1.;
debugPost("dimensions: %dx%d\t%f", w, h, penalty);
if ((w>0 || h>0)) {
/* try to find a format that matches the desired width/height best */
for(unsigned int formatid_index=0; formatid_index<formatid.size(); formatid_index++) {
int format_index=formatid[formatid_index];
if( !SUCCESS( unicap_enumerate_formats( m_handle, &format_spec, &format, format_index) ) ) {
post("Failed to get video format %d", format_index);
continue;
}
/* If a video format has more than one size, try to find the best match */
if(format.size_count) {
if(penalty<0) {
penalty=dimension_penalty(w, h, format.sizes[0]);
default_format=format_index;
default_size=0;
}
debugPost("current penalty=%f", penalty);
for( unsigned size_index = 0; size_index < format.size_count; size_index++ ) {
double p=dimension_penalty(w, h, format.sizes[size_index]);
debugPost("[%d/%d] penalty for (%dx%d) vs (%dx%d)=%f <> %f", format_index, size_index,
w, h, format.sizes[size_index].width, format.sizes[size_index].height,
p, penalty);
if(p<penalty) { penalty=p;
default_format=format_index;
default_size=size_index;
debugPost("new champion format=%d\tsize=%d", default_format, default_size);
}
}
}
}
}
unicap_void_format( &format_spec );
if( !SUCCESS( unicap_enumerate_formats( m_handle, &format_spec, &format, default_format) ) ) {
return false;
}
post_fmt(&format);
if(default_size>format.size_count) {
debugPost("oops: want size #%d of %d", default_size, format.size_count);
default_size=0;
}
format.size.width = format.sizes[default_size].width;
format.size.height = format.sizes[default_size].height;
format.buffer_type = UNICAP_BUFFER_TYPE_SYSTEM;
debugPost("setting format (%d/%d)", default_format, default_size);
post_fmt(&format);
if( !SUCCESS( unicap_set_format( m_handle, &format ) ) ) {
verbose(1, "failed to set format");
return false;
}
unicap_unregister_callback(m_handle, UNICAP_EVENT_NEW_FRAME);
status=unicap_register_callback (m_handle,
UNICAP_EVENT_NEW_FRAME,
(unicap_callback_t) newFrameCB,
(void *) this);
debugPost("registered callback: %x", status);
if(!SUCCESS(status)) {
debugPost("callback miserably failed");
return false;
}
status=unicap_start_capture (m_handle);
debugPost("start capture: %x", status);
if(!SUCCESS(status))
return false;
m_running=true;
return m_running;
}
/////////////////////////////////////////////////////////
// stopTransfer
//
/////////////////////////////////////////////////////////
bool videoUNICAP :: stop(void)
{
bool wasrunning=m_running;
if(m_running)
unicap_stop_capture (m_handle); // (3)
m_running=false;
return wasrunning;
}
bool videoUNICAP :: reset(void) {
#warning reset
bool wasrunning=stop();
close();
enumerate();
if(wasrunning) {
open(m_props);
start();
}
return true;
}
bool videoUNICAP :: setColor(int format)
{
if (format<=0 || format==m_reqFormat)return false;
m_reqFormat=format;
// restartTransfer();
return true;
}
std::vector<std::string> videoUNICAP::enumerate(void) {
std::vector<std::string> result;
int devcount=0;
unicap_status_t status = 0;
int i=0;
m_providers.clear();
m_providers.push_back(s_name);
m_providers.push_back("analog");
status = unicap_reenumerate_devices(&devcount);
if(!SUCCESS(status))
return result;
m_devices.clear();
m_name2devices.clear();
for(i=0; i<devcount; i++) {
unicap_device_t device;
status = unicap_enumerate_devices (NULL, &device, i); // (1)
if(SUCCESS(status)) {
const unsigned int cur=m_devices.size();
#if 0
post("ID='%s'\tmodel='%s'\tvendor='%s'\tdevice='%s'\tCPI='%s'",
device.identifier,
device.model_name,
device.vendor_name,
device.device,
device.cpi_layer);
#endif
m_devices.push_back(device);
m_name2devices[device.identifier ].push_back(cur);
m_name2devices[device.model_name ].push_back(cur);
m_name2devices[device.vendor_name].push_back(cur);
if(device.device[0])
m_name2devices[device.device].push_back(cur);
/* hmm, think about this .... */
std::string cpi=device.cpi_layer;
cpi=cpi.substr(cpi.rfind("/")+1);
cpi=cpi.substr(0, cpi.find("."));
provide(cpi);
result.push_back(device.identifier);
}
}
return result;
}
bool videoUNICAP::setDevice(int ID) {
m_devicename.clear();
m_devicenum=ID;
return true;
}
bool videoUNICAP::setDevice(const std::string ID) {
m_devicename=ID;
m_devicenum=-1;
return true;
}
bool videoUNICAP :: defaultFormat(void) {
if(!m_handle)return false;
int count=0;
unicap_status_t status= unicap_reenumerate_formats(m_handle, &count );
if(!SUCCESS(status))
return false;
debugPost("got %d formats", count);
if(count==0)return true;
unicap_format_t format;
for(int i=0; i<count; i++) {
status=unicap_enumerate_formats( m_handle, NULL, &format, i );
post_fmt(&format);
#if 0
if(count==1) {
status= unicap_set_format(m_handle, &format);
return SUCCESS(status);
}
#endif
}
return true;
}
bool videoUNICAP :: enumProperties(gem::Properties&readable,
gem::Properties&writeable) {
readable.clear();
writeable.clear();
if(m_handle) {
int count=0;
unicap_status_t status= unicap_reenumerate_properties(m_handle, &count );
if(!SUCCESS(status))
return false;
int id=0;
for(id=0; id<count; id++) {
unicap_property_t prop;
gem::any typ;
status = unicap_enumerate_properties(m_handle, NULL, &prop, id);
if(!SUCCESS(status))
continue;
debugPost("id='%s'\tcat='%s'\tunit='%s'\tflags=%d",
prop.identifier,
prop.category,
prop.unit,
prop.flags);
switch(prop.type) {
case UNICAP_PROPERTY_TYPE_RANGE:
debugPost("range %f-%f", prop.range.min, prop.range.min);
typ=prop.range.max;
break;
case UNICAP_PROPERTY_TYPE_VALUE_LIST:
debugPost("value_list %d", prop.value_list.value_count);
typ=prop.value_list.value_count;
break;
case UNICAP_PROPERTY_TYPE_MENU:
debugPost("menu '%s' of %d", prop.menu_item, prop.menu.menu_item_count);
typ=std::string(prop.menu_item);//prop.menu.menu_item_count;
break;
case UNICAP_PROPERTY_TYPE_FLAGS:
debugPost("flags");
break;
default:
debugPost("unknown");
// ?
break;
}
readable.set(prop.identifier, typ);
if(!(prop.flags & UNICAP_FLAGS_READ_ONLY))
writeable.set(prop.identifier, typ);
#warning check UNICAP_FLAGS_ON_OFF & UNICAP_FLAGS_ONE_PUSH
}
}
return true;
}
void videoUNICAP :: getProperties(gem::Properties&props) {
if(!m_handle)return;
unicap_status_t status=0;
std::vector<std::string> keys=props.keys();
bool getwidth=false, getheight=false;
int i=0;
for(i=0; i<keys.size(); i++) {
std::string key=keys[i];
unicap_property_t prop;
strncpy(prop.identifier, key.c_str(), 128);
if("width"==key) {
getwidth=true;
continue;
}
if("height"==key) {
getheight=true;
continue;
}
status= unicap_get_property(m_handle, &prop );
if(SUCCESS(status)) {
switch(prop.type) {
case UNICAP_PROPERTY_TYPE_VALUE_LIST:
case UNICAP_PROPERTY_TYPE_FLAGS:
case UNICAP_PROPERTY_TYPE_RANGE:
props.set(key, prop.value);
break;
case UNICAP_PROPERTY_TYPE_MENU:
props.set(key, std::string(prop.menu_item));
break;
default:
props.erase(key);
// ?
break;
}
}
}
if(getwidth||getheight) {
unicap_format_t fmt;
status=unicap_get_format(m_handle, &fmt);
post("getting dimen");post_fmt(&fmt);
if(SUCCESS(status)) {
if(getwidth )props.set("width" , fmt.size.width);
if(getheight)props.set("height", fmt.size.height);
}
}
}
void videoUNICAP :: setProperties(gem::Properties&props) {
m_props=props;
debugPost("handle=%p", m_handle);
if(!m_handle)
return;
unicap_status_t status = 0;
bool restart=false;
unsigned int width=0, height=0;
std::vector<std::string> keys=props.keys();
int i=0;
for(i=0; i<keys.size(); i++) {
std::string key=keys[i];
double d=0;
std::string s;
if(("width"==key) && props.get(key, d)) {
width=d;
if(m_width!=width) {
m_width=width;
restart=true;
}
continue;
}
if(("height"==key) && props.get(key, d)) {
height=d;
if(m_height!=height) {
m_height=height;
restart=true;
}
continue;
}
unicap_property_t prop;
strncpy(prop.identifier, key.c_str(), 128);
status=unicap_get_property(m_handle, &prop );
if(SUCCESS(status)) {
switch(prop.type) {
case UNICAP_PROPERTY_TYPE_VALUE_LIST:
case UNICAP_PROPERTY_TYPE_FLAGS:
case UNICAP_PROPERTY_TYPE_RANGE:
if(props.get(key, d)) {
prop.value=d;
status= unicap_set_property(m_handle, &prop );
}
break;
case UNICAP_PROPERTY_TYPE_MENU:
if(props.get(key, d)) {
if(d>=0 && d < prop.menu.menu_item_count) {
int i=d;
/* unfortunately we must use the symbolic value and cannot simply set using the index... */
strncpy(prop.menu_item, prop.menu.menu_items[i], 128);
status= unicap_set_property(m_handle, &prop );
}
} else if (props.get(key, s)) {
strncpy(prop.menu_item, s.c_str(), 128);
status= unicap_set_property(m_handle, &prop );
}
break;
default:
// ?
break;
}
if(!SUCCESS(status)) {
verbose(1, "could not set property '%s'", key.c_str());
#if 0
} else {
verbose(1, "successfully set property '%s'", key.c_str());
#endif
}
}
}
while(restart) { restart=false;
debugPost("restarting stream due to property change");
bool running=stop();
debugPost("running=%d", running);
if (running)start();
}
}
void videoUNICAP::provide(const std::string name) {
if(!provides(name))
m_providers.push_back(name);
}
bool videoUNICAP::provides(const std::string name) {
for(unsigned int i=0; i<m_providers.size(); i++) {
if(name == m_providers[i])
return true;
}
return (false);
}
std::vector<std::string>videoUNICAP::provides(void) {
std::vector<std::string>result;
for(unsigned int i=0; i<m_providers.size(); i++) {
result.push_back(m_providers[i]);
}
return result;
}
const std::string videoUNICAP::getName(void) {
return s_name;
}
#endif /* HAVE_UNICAP */