//////////////////////////////////////////////////////// // // 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 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::vectords; 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; inewFrame(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::vectorformatid; if(default_format>=0) { formatid.push_back(default_format); } else { for(int i=0; i0)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 %f", format_index, size_index, w, h, format.sizes[size_index].width, format.sizes[size_index].height, p, penalty); if(pformat.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 videoUNICAP::enumerate(void) { std::vector 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 keys=props.keys(); bool getwidth=false, getheight=false; int i=0; for(i=0; i keys=props.keys(); int i=0; for(i=0; i=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; ivideoUNICAP::provides(void) { std::vectorresult; for(unsigned int i=0; i