topical media & game development

talk show tell print

lib-of-vs-libs-openFrameworks-video-ofUCUtils.cpp / cpp



  include <ofUCUtils.h>
  include <ofConstants.h>
  
  ifdef TARGET_LINUX
  
  include <stdio.h>
  include <stdint.h>
  include <ofUtils.h>
  
  // ugly hack to make this work with
  // new ffmpeg include path in ubuntu jaunty
  // with -MMD flag in the compiler
  // this will throw a warning instead of an error
  // comment out the two lines that doesn't work for you
  // to avoid the warnings
  extern "C"
  {
  include <avformat.h>
  include <swscale.h>
  include <libavformat/avformat.h>
  include <libswscale/swscale.h>
  }
  
  define FOURCC(a,b,c,d) (unsigned int)((((unsigned int)a))+(((unsigned int)b)<<8)+(((unsigned int)c)<<16)+(((unsigned int)d)<<24))
  
  typedef struct PixelFormatTag {
          PixelFormat pix_fmt;
      unsigned int fourcc;
  } PixelFormatTag;
  
  static const PixelFormatTag pixelFormatTags[] = {
          /* Planar formats */
      { PIX_FMT_YUV420P,         FOURCC('I', '4', '2', '0') },
      { PIX_FMT_YUV420P,         FOURCC('I', 'Y', 'U', 'V') },
      { PIX_FMT_YUV420P,         FOURCC('Y', 'V', '1', '2') },
      { PIX_FMT_YUV410P,         FOURCC('Y', 'U', 'V', '9') },
      { PIX_FMT_YUV411P,         FOURCC('Y', '4', '1', 'B') },
      { PIX_FMT_YUV422P,         FOURCC('Y', '4', '2', 'B') },
      { PIX_FMT_GRAY8,           FOURCC('Y', '8', '0', '0') },
      { PIX_FMT_GRAY8,           FOURCC(' ', ' ', 'Y', '8') },
  
      //v4l
      { PIX_FMT_GRAY8,           FOURCC('G', 'R', 'E', 'Y') },
      //{ PIX_FMT_...         FOURCC('H', '2', '4', '0') },        //HI420 BT848, seems to not exist in ffmpeg
      { PIX_FMT_RGB565,          FOURCC('R', 'G', 'B', '6') },        //RGB565
      { PIX_FMT_RGB555 ,         FOURCC('R', 'G', 'B', '5') },        //RGB15
      { PIX_FMT_RGB32,           FOURCC('R', 'G', 'B', '4') },        //RGB32
      { PIX_FMT_YUYV422,         FOURCC('Y', 'U', 'Y', 'V') },
      { PIX_FMT_UYYVYY411,FOURCC('Y', '4', '1', '1') },        //YUV411 is this correct??
      //FOURCC( 'R', 'A', 'W', ' ' );                                                //BT848, seems to not exist in ffmpeg
      { PIX_FMT_YUV422P,         FOURCC('Y', '4', '2', 'P') },        //YUV422P
      { PIX_FMT_YUV411P,         FOURCC('4', '1', '1', 'P') },        //YUV411P
      { PIX_FMT_YUV420P,         FOURCC('Y', 'U', '1', '2') },
  
      //v4l2
      { PIX_FMT_UYVY422,         FOURCC('U', 'Y', 'V', 'Y') },
      { PIX_FMT_YUV411P,         FOURCC('Y', '4', '1', 'P') },
      //{ PIX_FMT_YVU410P, FOURCC('Y', 'V', 'U', '9') },
      { PIX_FMT_YUV410P,         FOURCC('Y', 'U', 'V', '9') },         // is this correct or YUV410 non-planar
      { PIX_FMT_YUV422P,         FOURCC('4', '2', '2', 'P') },
      { PIX_FMT_YUV411P,         FOURCC('4', '1', '1', 'P') },
      { PIX_FMT_NV12,            FOURCC('N', 'V', '1', '2') },
      { PIX_FMT_NV21,            FOURCC('N', 'V', '2', '1') },
      { PIX_FMT_YUV422,          FOURCC('Y', 'Y', 'U', 'V') },
      { PIX_FMT_RGB32_1,         FOURCC('R', 'G', 'B', '1') },        // is this correct?
      { PIX_FMT_RGB555,          FOURCC('R', 'G', 'B', 'O') },
      { PIX_FMT_RGB565,          FOURCC('R', 'G', 'B', 'P') },
      //{ PIX_FMT_RGB555X,  FOURCC('R', 'G', 'B', 'Q') },
      //{ PIX_FMT_RGB565X,  FOURCC('R', 'G', 'B', 'R') },
      { PIX_FMT_BGR32,           FOURCC('B', 'G', 'R', '4') },
  
      //vid21394
      { PIX_FMT_YUYV422,         FOURCC('Y', 'U', 'Y', '2') },        // is this correct?
      { PIX_FMT_GRAY8,           FOURCC('Y', '8', '0', '0') },        // is this correct?
  
      /* dcam */
      { PIX_FMT_YUV444P,         FOURCC('Y', '4', '4', '4') },         // is this correct or non-planar?
      { PIX_FMT_GRAY16,          FOURCC('Y', '1', '6', '0') },         // is this correct?
      { PIX_FMT_RGB24,           FOURCC('R', 'G', 'B', ' ') },
  
      /* Packed formats */
      { PIX_FMT_YUYV422,         FOURCC('Y', 'U', 'Y', '2') },
      { PIX_FMT_YUYV422,         FOURCC('Y', '4', '2', '2') },
      { PIX_FMT_UYVY422,         FOURCC('U', 'Y', 'V', 'Y') },
      { PIX_FMT_GRAY8,           FOURCC('G', 'R', 'E', 'Y') },
      { PIX_FMT_RGB555,          FOURCC('R', 'G', 'B', 15) },
      { PIX_FMT_BGR555,          FOURCC('B', 'G', 'R', 15) },
      { PIX_FMT_RGB565,          FOURCC('R', 'G', 'B', 16) },
      { PIX_FMT_BGR565,          FOURCC('B', 'G', 'R', 16) },
      { PIX_FMT_BGR24,           FOURCC('B', 'G', 'R', '3') },
      { PIX_FMT_RGB24,           FOURCC('R', 'G', 'B', '3') },
  
      /* quicktime */
      { PIX_FMT_UYVY422,         FOURCC('2', 'v', 'u', 'y') },
      { PIX_FMT_UYVY422,         FOURCC('A', 'V', 'U', 'I') },
  
      { PIX_FMT_NONE, 0 },
  };
  
  //--------------------------------------------------------------------
  PixelFormat fourcc_to_pix_fmt( unsigned int fourcc)
  {
          const PixelFormatTag * tags = pixelFormatTags;
          while (tags->pix_fmt >= 0) {
                  if (tags->fourcc == fourcc)
                          return tags->pix_fmt;
          tags++;
      }
      return PIX_FMT_NONE;
  }
  
  //--------------------------------------------------------------------
  void new_frame_cb (unicap_event_t event, unicap_handle_t handle,
              unicap_data_buffer_t * buffer, void *usr_data){
  
          ((ofUCUtils*)usr_data)->new_frame(buffer);
  }
  
  //--------------------------------------------------------------------
  ofUCUtils::ofUCUtils(){
  
          verbose                        = false;
          bUCFrameNew                = false;
          pixels                        = NULL;
          src                                = NULL;
          dst                                = NULL;
          deviceReady                = false;
          doConversion        = false;
          pthread_mutex_init(&capture_mutex,NULL);
  }
  
  //--------------------------------------------------------------------
  ofUCUtils::~ofUCUtils(){
  
          close_unicap();
          pthread_mutex_destroy(&capture_mutex);
  }
  
  //--------------------------------------------------------------------
  bool ofUCUtils::open_device(int d) {
  
          if (!SUCCESS(unicap_enumerate_devices (NULL, &device, d))) {
                  ofLog(OF_LOG_ERROR,"ofUCUtils: Error selecting device \%d", d);
                  return false;
          } else {
  
                  if (!SUCCESS(unicap_open (&handle, &device))) {
                          ofLog(OF_LOG_ERROR,"ofUCUtils: Error opening device \%d: \%s", d,        device.identifier);
                          return false;
                  }
                  deviceReady = true;
          }
          ofLog(OF_LOG_NOTICE,"ofUCUtils: Using device \%s",device.device);
          ofLog(OF_LOG_NOTICE,"ofUCUtils: Using module \%s",device.vendor_name);
  
          return true;
  }
  
  //--------------------------------------------------------------------
  char * ofUCUtils::device_identifier(void){
  
          return device.identifier;
  
  }
  
  //--------------------------------------------------------------------
  // If a 24 bit video format is founded this is the preferred one, if not, the first
  // returned by unicap is selected.
  //
  // Then it tries to set the desired width and height, if these fails, tries find the
  // nearest size or to set the default width and height.
  //
  // On V4L devices 24 bit format is always BGR, so it needs conversion.
  // On some V4L devices using non-default width/heigth it reports BGR but returns RGB.
  // ffmpeg color conversion
  void ofUCUtils::set_format(int w, int h) {
          if(!deviceReady)
                  return;
          d_width=w;
          d_height=h;
          unicap_format_t formats[MAX_FORMATS];
          int format_count;
          unicap_status_t status = STATUS_SUCCESS;
          int rgb24 = -1;
  
          ofLog(OF_LOG_NOTICE,"ofUCUtils : Available formats for this device:");
          for (format_count = 0; SUCCESS (status) && (format_count < MAX_FORMATS); format_count++) {
                  status = unicap_enumerate_formats (handle, NULL, &formats[format_count], format_count);
                  if (SUCCESS (status)) {
                          if (formats[format_count].bpp == 24) {
                                  rgb24 = format_count;
                          }
                          ofLog(OF_LOG_NOTICE,
                                          "ofUCUtils : \%d: \%s, min size: \%dx\%d, max size:\%dx\%d, default size: \%dx\%d",
                                          format_count, formats[format_count].identifier,
                                          formats[format_count].min_size.width,
                                          formats[format_count].min_size.height,
                                          formats[format_count].max_size.width,
                                          formats[format_count].max_size.height,
                                          formats[format_count].size.width,
                                          formats[format_count].size.height);
                          ofLog(OF_LOG_VERBOSE,"ofUCUtils: available sizes for this format:");
                          for(int i=0; i<formats[format_count].size_count;i++){
                                  ofLog(OF_LOG_VERBOSE,"          \%dx\%d",formats[format_count].sizes[i].width,formats[format_count].sizes[i].height);
                          }
                  }
          }
  
          if (format_count > 0) {
                  int selected_format = 0;
                  if (rgb24 != -1)
                          selected_format = rgb24;
                  else{
                          for(selected_format=0;selected_format<format_count;selected_format++){
                                  format = formats[selected_format];
                                  if(fourcc_to_pix_fmt(format.fourcc)!=-1)
                                          break;
                          }
                  }
                  format = formats[selected_format];
  
                  src_pix_fmt=fourcc_to_pix_fmt(format.fourcc);
                  if( src_pix_fmt==-1){
                          ofLog(OF_LOG_ERROR,"ofUCUtils : Format not suported\n");
                          return;
                  }
  
                  bool exactMatch  = false;
                  int sizeDiff = 99999999;
                  int mostAproxSize = -1;
  
                  for(int i=0; i<format.size_count;i++){
                          if(format.sizes[i].width == w && format.sizes[i].height==h){
                                  exactMatch=true;
                                  format.size.width  = format.sizes[i].width;
                                  format.size.height = format.sizes[i].height;
                                  break;
                          }else{
                                  if(abs(format.sizes[i].width-w)+abs(format.sizes[i].height-h)<sizeDiff){
                                          sizeDiff=abs(format.sizes[i].width-w)+abs(format.sizes[i].height-h);
                                          mostAproxSize=i;
                                  }
                          }
                  }
                  if(!exactMatch && mostAproxSize!=-1){
                          format.size.width  = format.sizes[mostAproxSize].width;
                          format.size.height = format.sizes[mostAproxSize].height;
  
                          ofLog(OF_LOG_WARNING, "ofUCUtils : Can't set video format \%s, with size \%dx\%d, will use \%dx\%d",
                                                          format.identifier, w, h,
                                                          format.size.width, format.size.height);
                  }else if(format.size_count==0){
                          int defaultFormatWidth = format.size.width;
                          int defaultFormatHeight = format.size.height;
  
                          format.size.width  = w;
                          format.size.height = h;
  
                          ofLog(OF_LOG_WARNING, "ofUCUtils : Can't recognize supported video sizes for \%s, trying with requested size: \%i,\%i",
                                                                                  format.identifier, format.size.width, format.size.height);
  
                          if ( !SUCCESS ( unicap_set_format (handle, &format) ) ) {
                                  format.size.width  = defaultFormatWidth;
                                  format.size.height = defaultFormatHeight;
  
                                  ofLog(OF_LOG_WARNING, "ofUCUtils : Can't set requested size, trying with format defaults: \%i,\%i",
                                                  defaultFormatWidth, defaultFormatHeight);
                          }else{
                                  exactMatch=true; //if we can set the requested size -> exactMatch
                          }
                          ofLog(OF_LOG_WARNING, "ofUCUtils : If this doesn't work try using the reported default size in initGrabber:",
                                          defaultFormatWidth, defaultFormatHeight);
                  }
                  if ( !SUCCESS ( unicap_set_format (handle, &format) ) ) {
                          ofLog(OF_LOG_ERROR, "ofUCUtils : Failed to set alternative video format!");
                          return;
                  }
                  ofLog(OF_LOG_NOTICE,"ofUCUtils : Selected format: \%s, with size \%dx\%d\n", format.identifier,
                                  format.size.width, format.size.height);
  
                  if(src_pix_fmt!=PIX_FMT_RGB24 || !exactMatch){
                          doConversion = true;
                          src=new AVPicture;
                          avpicture_alloc(src,src_pix_fmt,format.size.width,format.size.height);
                          dst=new AVPicture;
                          avpicture_alloc(dst,PIX_FMT_RGB24,d_width,d_height);
  
                          toRGB_convert_ctx = sws_getContext(
                                                          format.size.width, format.size.height, src_pix_fmt,
                                                          d_width, d_height, PIX_FMT_RGB24,
                                                          VIDEOGRABBER_RESIZE_FLAGS, NULL, NULL, NULL);
  
                          ofLog(OF_LOG_NOTICE,"ofUCUtils: Converting to RGB24 (\%i,\%i)\n",w,h);
  
                          pixels=new unsigned char[d_width*d_height*3];
                  }
  
             if( !SUCCESS( unicap_get_format( handle, &format ) ) )
             {
                     ofLog(OF_LOG_ERROR, "can't get format" );
                     return;
             }
  
                  format.buffer_type = UNICAP_BUFFER_TYPE_SYSTEM;
  
             if( !SUCCESS( unicap_set_format( handle, &format ) ) )
             {
                     ofLog(OF_LOG_WARNING, "ofUCUtils: Failed to activate SYSTEM_BUFFERS" );
             }
          }
  }
  
  //--------------------------------------------------------------------
  void ofUCUtils::start_capture() {
  
          if(!deviceReady)
                  return;
  
          int status = STATUS_SUCCESS;
          if (!SUCCESS ( status = unicap_register_callback (handle, UNICAP_EVENT_NEW_FRAME, (unicap_callback_t) new_frame_cb, (void *) this) ) )
                  ofLog(OF_LOG_ERROR,"ofUCUtils: error registering callback");
          if (!SUCCESS ( status = unicap_start_capture (handle) ) )
                  ofLog(OF_LOG_ERROR,"ofUCUtils: error starting capture: \%i,\%i",status,STATUS_INVALID_HANDLE);
  }
  
  //--------------------------------------------------------------------
  void ofUCUtils::queryUC_imageProperties(void) {
  
          unicap_property_t property;
          ofLog(OF_LOG_NOTICE,"ofUCUtils : Video settings:");
          const int PPTY_TYPE_MAPPED_FLOAT=UNICAP_PROPERTY_TYPE_UNKNOWN + 1;
          int status = STATUS_SUCCESS;
          int ppty_type;
          for (int i=0; i<MAX_PROPERTIES; i++) {
                  status = unicap_enumerate_properties(handle, NULL, &property, i);
                  if ( !SUCCESS( status ))
                          return;
  
                  status = unicap_get_property(handle, &property );
                  if ( !SUCCESS( status )) {
                          ofLog(OF_LOG_ERROR,"ofUCUtils : Error getting \%s value\n", property.identifier);
                          return;
                  }
  
                  ppty_type = property.type;
                  switch (ppty_type) {
                  case PPTY_TYPE_MAPPED_FLOAT:
                  case UNICAP_PROPERTY_TYPE_RANGE:
                  case UNICAP_PROPERTY_TYPE_VALUE_LIST:
                          if (property.value>0 && property.value<1) {
                                  ofLog(OF_LOG_NOTICE,"\t\%s: 1/%.0f \n", property.identifier, 1/property.value);
                          } else {
                                  ofLog(OF_LOG_NOTICE,"\t\%s: %.2f \n", property.identifier, property.value);
                          }
                          break;
                  case UNICAP_PROPERTY_TYPE_MENU:
                          printf("\t\%s: \%s \n", property.identifier, property.menu_item);
                          break;
                  default:
                          break;
                  }
  
          }
  
  }
  
  //--------------------------------------------------------------------
  void ofUCUtils::new_frame (unicap_data_buffer_t * buffer)
  {
          if(!deviceReady)
                  return;
  
          if(doConversion){
                  avpicture_fill(src,buffer->data,src_pix_fmt,format.size.width,format.size.height);
  
                  if(sws_scale(toRGB_convert_ctx,
                          src->data, src->linesize, 0, buffer->format.size.height,
                          dst->data, dst->linesize)<0)
                                  ofLog(OF_LOG_ERROR,"ofUCUtils: can't convert colorspaces");
  
                  lock_buffer();
                          avpicture_layout(dst,PIX_FMT_RGB24,d_width,d_height,pixels,d_width*d_height*3);
  
          }else{
                  lock_buffer();
                          pixels=buffer->data;
          }
          bUCFrameNew = true;
          unlock_buffer();
  }
  
  //--------------------------------------------------------------------
  bool ofUCUtils::getFrameUC(unsigned char ** _pixels) {
          if(bUCFrameNew){
                  lock_buffer();
                  bUCFrameNew = false;
                  memcpy(*_pixels,pixels,d_width*d_height*3);
                  //*_pixels=pixels;
                  unlock_buffer();
                  return true;
          }else{
                  return false;
          }
  }
  
  //--------------------------------------------------------------------
  void ofUCUtils::listUCDevices() {
  
          int status = STATUS_SUCCESS;
          int dev_count;
          unicap_device_t devices[MAX_DEVICES];
          printf("listing available capture devices\n");
          printf("---\n");
          for (dev_count = 0; SUCCESS (status) && (dev_count < MAX_DEVICES); dev_count++) {
                  status = unicap_enumerate_devices (NULL, &devices[dev_count], dev_count);
                  if (SUCCESS (status))
                          printf ("Video device \%d: \%s\n", dev_count,
                                          devices[dev_count].identifier);
          }
          printf("---\n");
  
  }
  
  void ofUCUtils::close_unicap() {
  
          if(!deviceReady)
                  return;
  
          unicap_stop_capture(handle);
          bUCFrameNew=false;
  
          if( src_pix_fmt != PIX_FMT_RGB24 ){
  
                  if ( dst != NULL ){
                          avpicture_free(dst);
                          delete dst;
                  }
  
                  if ( pixels != NULL ) {
                          delete[] pixels;
                  }
  
                  if ( src != NULL ){
                          //avpicture_free(src);
                          delete src;
                  }
  
          }
          deviceReady=false;
  
  }
  
  //--------------------------------------------------------------------
  void ofUCUtils::lock_buffer(){
          pthread_mutex_lock( &capture_mutex );
  }
  
  //--------------------------------------------------------------------
  void ofUCUtils::unlock_buffer(){
          pthread_mutex_unlock( &capture_mutex );
  }
  
  endif
  


(C) Æliens 04/09/2009

You may not copy or print any of this material without explicit permission of the author or the publisher. In case of other copyright issues, contact the author.