pix_video.cpp 24.9 KB
Newer Older
1
2
3
4
5
6
7
8
9
////////////////////////////////////////////////////////
//
// GEM - Graphics Environment for Multimedia
//
// zmoelnig@iem.kug.ac.at
//
// Implementation file
//
//    Copyright (c) 1997-1998 Mark Danks.
zmoelnig's avatar
zmoelnig committed
10
//    Copyright (c) Günther Geiger.
zmoelnig's avatar
zmoelnig committed
11
//    Copyright (c) 2001-2011 IOhannes m zmölnig. forum::für::umläute. IEM. zmoelnig@iem.at
12
13
14
15
//    For information on usage and redistribution, and for a DISCLAIMER OF ALL
//    WARRANTIES, see the file, "GEM.LICENSE.TERMS" in this distribution.
//
/////////////////////////////////////////////////////////
16

17
#include "Gem/GemConfig.h"
18
19
20

#ifndef GEM_VIDEOBACKEND

21
#include "pix_video.h"
22
#include "Gem/State.h"
23
#include "Gem/Exception.h"
zmoelnig's avatar
zmoelnig committed
24
#include "plugins/PluginFactory.h"
25

26
27
#include "RTE/Symbol.h"

28
29
#include <algorithm>

30
CPPEXTERN_NEW_WITH_GIMME(pix_video);
31

IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
32
33
34
35
36
37
38

#if 0
# define MARK std::cerr << __FILE__<<":"<<__LINE__<<" ("<<__FUNCTION__<<")"<<std::endl
#else
# define MARK
#endif

39
40
41
42
43
44
45
46
/////////////////////////////////////////////////////////
//
// pix_video
//
/////////////////////////////////////////////////////////
// Constructor
//
/////////////////////////////////////////////////////////
47
pix_video :: pix_video(int argc, t_atom*argv) :
zmoelnig's avatar
zmoelnig committed
48
  m_videoHandle(NULL), m_driver(-1), m_running(UNKNOWN), m_infoOut(NULL)
49
{
zmoelnig's avatar
zmoelnig committed
50
  gem::PluginFactory<gem::plugins::video>::loadPlugins("video");
51
  std::vector<std::string>ids=gem::PluginFactory<gem::plugins::video>::getIDs();
52
53
54
55

  addHandle(ids, "v4l2");
  addHandle(ids, "v4l");
  addHandle(ids, "dv4l");
IOhannes m zmoelnig's avatar
IOhannes m zmoelnig committed
56
  MARK;
57
58
  addHandle(ids);

IOhannes m zmoelnig's avatar
IOhannes m zmoelnig committed
59
  MARK;
60
61
  m_infoOut = outlet_new(this->x_obj, 0);

62
  /*
63
   * calling driverMess() would immediately startTransfer();
64
65
   * we probably don't want this in initialization phase
   */
IOhannes m zmoelnig's avatar
IOhannes m zmoelnig committed
66
  MARK;
67
  if(m_videoHandles.size()>0) {
68
    m_driver=-1;
69
70
71
  } else {
    error("no video backends found!");
  }
IOhannes m zmoelnig's avatar
IOhannes m zmoelnig committed
72
MARK;
73
  std::string dev=gem::RTE::Symbol(argc, argv);
IOhannes m zmoelnig's avatar
IOhannes m zmoelnig committed
74
MARK;
75
76
  if(!dev.empty())
    deviceMess(dev);
IOhannes m zmoelnig's avatar
IOhannes m zmoelnig committed
77
MARK;
78

79
80
81
82
83
84
}

/////////////////////////////////////////////////////////
// Destructor
//
/////////////////////////////////////////////////////////
85
86
87
88
pix_video :: ~pix_video(){
  /* clean up all video handles;
   * the video-handles have to stop the transfer themselves
   */
zmoelnig's avatar
zmoelnig committed
89
  unsigned int i=0;
90
91
92
93
  for(i=0; i<m_videoHandles.size(); i++) {
    delete m_videoHandles[i];
    m_videoHandles[i]=NULL;
  }
94
95

  outlet_free(m_infoOut); m_infoOut=NULL;
96
97
}

98
99
100
101
102
103

/////////////////////////////////////////////////////////
// render
//
/////////////////////////////////////////////////////////
void pix_video :: render(GemState *state){
104
  if (m_videoHandle)state->set(GemState::_PIX, m_videoHandle->getFrame());
105
106
107
108
109
110
111
}

/////////////////////////////////////////////////////////
// startRendering
//
/////////////////////////////////////////////////////////
void pix_video :: startRendering(){
zmoelnig's avatar
zmoelnig committed
112
113
114
  if(UNKNOWN==m_running)
    m_running=STARTED;

115
116
117
118
119
120
121
122
123
124
  if(m_videoHandles.size()<1) {
    error("do video for this OS");
    return;
  }

  if (!m_videoHandle) {
    if(!restart()) {
      error("no valid video backend found");
      return;
    }
zmoelnig's avatar
zmoelnig committed
125
    return;
126
127
128
  }

  verbose(1, "starting transfer");
129
  m_videoHandle->start();
130
131
132
133
134
135
136
}

/////////////////////////////////////////////////////////
// stopRendering
//
/////////////////////////////////////////////////////////
void pix_video :: stopRendering(){
137
  if (m_videoHandle)m_videoHandle->stop();
138
139
140
141
142
143
144
}

/////////////////////////////////////////////////////////
// postrender
//
/////////////////////////////////////////////////////////
void pix_video :: postrender(GemState *state){
145
  state->set(GemState::_PIX, static_cast<pixBlock*>(NULL));
146
147
148
149
150

  if (m_videoHandle)m_videoHandle->releaseFrame();
}


151
152
153
154
155
156
/////////////////////////////////////////////////////////
// add backends
//
/////////////////////////////////////////////////////////
bool pix_video :: addHandle( std::vector<std::string>available, std::string ID)
{
157
  unsigned int i=0;
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
  int count=0;

  std::vector<std::string>id;
  if(!ID.empty()) {
    // if requested 'cid' is in 'available' add it to the list of 'id's
    if(std::find(available.begin(), available.end(), ID)!=available.end()) {
      id.push_back(ID);
    } else {
      // request for an unavailable ID
      verbose(2, "backend '%s' unavailable", ID.c_str());
      return false;
    }
  } else {
    // no 'ID' given: add all available IDs
    id=available;
173
  }
174
175

  for(i=0; i<id.size(); i++) {
IOhannes m zmoelnig's avatar
IOhannes m zmoelnig committed
176
	  MARK;
177
    std::string key=id[i];
IOhannes m zmoelnig's avatar
IOhannes m zmoelnig committed
178
179
    verbose(2, "trying to add '%s' as backend (%d)", key.c_str(), id.size());
	MARK;
180
181
    if(std::find(m_ids.begin(), m_ids.end(), key)==m_ids.end()) {
      // not yet added, do so now!
182
      gem::plugins::video         *handle=NULL;
183
      startpost("backend #%d='%s'\t", m_videoHandles.size(), key.c_str());
184
      try {
185
        handle=gem::PluginFactory<gem::plugins::video>::getInstance(key);
186
      } catch (GemException&) {
IOhannes m zmoelnig's avatar
IOhannes m zmoelnig committed
187
		  handle=NULL;
188
      }
189
      if(NULL==handle) {
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
190
        post("<--- DISABLED");
191
        continue;
192
193
194
      }
      std::vector<std::string>devs=handle->provides();
      if(devs.size()>0) {
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
195
196
197
198
199
        startpost(": ");
        unsigned int i=0;
        for(i=0; i<devs.size(); i++) {
          startpost("%s ", devs[i].c_str());
        }
200
      }
zmoelnig's avatar
zmoelnig committed
201
      endpost();
202

203
204
205
      m_ids.push_back(key);
      m_videoHandles.push_back(handle);
      count++;
IOhannes m zmoelnig's avatar
IOhannes m zmoelnig committed
206
	  MARK;
207
      verbose(2, "added backend#%d '%s' @ 0x%x", m_videoHandles.size()-1, key.c_str(), handle);
IOhannes m zmoelnig's avatar
IOhannes m zmoelnig committed
208
209
210
	  MARK;
	}
	MARK;
211
  }
IOhannes m zmoelnig's avatar
IOhannes m zmoelnig committed
212
MARK;
213
  return (count>0);
214
215
}

216

217
bool pix_video::restart(void) {
zmoelnig's avatar
zmoelnig committed
218
  verbose(1, "restart");
219
  if(m_videoHandle) {
220
    m_videoHandle->stop();
zmoelnig's avatar
zmoelnig committed
221
    m_videoHandle->close();
222
  }
223

224
225
  if(m_driver<0) {
    // auto mode
226
    unsigned int i=0;
227
    verbose(1, "trying to start driver automatically (%d)", m_running);
228
    for(i=0; i<m_videoHandles.size(); i++) {
229
      if(m_videoHandles[i]->open(m_writeprops)) {
230
        m_videoHandle=m_videoHandles[i];
231

232
        enumPropertyMess();
233

zmoelnig's avatar
zmoelnig committed
234
        if(STARTED==m_running) {
235
          m_videoHandle->start();
236
237
238
239
240
241
        }
        return true;
      }
    }
  } else {
    // enforce selected driver
242
    verbose(1, "trying to start driver#%d (%d)", m_driver, m_running);
243
    m_videoHandle=m_videoHandles[m_driver];
244
245
    if(m_videoHandle->open(m_writeprops)) {
      enumPropertyMess();
zmoelnig's avatar
zmoelnig committed
246
247
      if(STARTED==m_running)
	m_videoHandle->start();
248
249
250
251
252
253
      return true;
    }
  }

  m_videoHandle=NULL;
  return false;
254
255
}

256
257
258



zmoelnig's avatar
zmoelnig committed
259
#define WITH_VIDEOHANDLES_DO(x) do {unsigned int _i=0; for(_i=0; _i<m_videoHandles.size(); _i++)m_videoHandles[_i]->x;} while(false)
260

261
/////////////////////////////////////////////////////////
262
// driverMess
263
264
//
/////////////////////////////////////////////////////////
265
void pix_video :: driverMess(std::string s)
266
{
267
  if("auto"==s) {
268
    driverMess(-1);
269
    return;
270
  } else {
271
    unsigned int dev;
272
    for(dev=0; dev<m_videoHandles.size(); dev++) {
273
      if(m_videoHandles[dev]->provides(s)) {
274
275
276
277
        driverMess(dev);
        return;
      }
    }
278
  }
279
  error("could not find a backend for driver '%s'", s.c_str());
280
281
282
283
}
void pix_video :: driverMess(int dev)
{
  if(dev>=0) {
284
285
286
    unsigned int udev=(unsigned int)dev;
	if(udev>=m_videoHandles.size()){
		error("driverID (%d) must not exceed %d", udev, m_videoHandles.size());
287
288
289
		return;
	}

290
    if(m_videoHandle){
291
      m_videoHandle->stop();
292
      m_videoHandle->close();
293
    }
294
    m_videoHandle=m_videoHandles[udev];
295
    if(m_videoHandle){
296
297
      if(m_videoHandle->open(m_writeprops)) {
        enumPropertyMess();
zmoelnig's avatar
zmoelnig committed
298
        if(STARTED==m_running)
IOhannes m zmoelnig's avatar
IOhannes m zmoelnig committed
299
	       m_videoHandle->start();
300
301
302
303
304
305
      }
    }
  } else {
    post("automatic driver selection");
  }
  m_driver=dev;
306
307
}

308
void pix_video :: driverMess() {
zmoelnig's avatar
zmoelnig committed
309
  // a little bit of info
310
311
  t_atom at;
  t_atom*ap=&at;
312
  if(m_videoHandle) {
zmoelnig's avatar
zmoelnig committed
313
    post("current driver: '%s'", m_videoHandle->getName().c_str());
314
315
316

    SETSYMBOL(ap+0, gensym(m_videoHandle->getName().c_str()));
    outlet_anything(m_infoOut, gensym("currentdriver"), 1, ap);
zmoelnig's avatar
zmoelnig committed
317
  }
318
  if(m_videoHandles.size()>0) {
zmoelnig's avatar
zmoelnig committed
319
    unsigned int i=0;
320
321
    SETFLOAT(ap+0, m_videoHandles.size());
    outlet_anything(m_infoOut, gensym("drivers"), 1, ap);
zmoelnig's avatar
zmoelnig committed
322
323
    post("available drivers:");
    for(i=0; i<m_videoHandles.size(); i++) {
324
      gem::plugins::video*handle= m_videoHandles[i];
zmoelnig's avatar
zmoelnig committed
325
326
327
      if(NULL==handle)continue;
      startpost("\t'%s' provides ", handle->getName().c_str());
      std::vector<std::string>backends=handle->provides();
zmoelnig's avatar
zmoelnig committed
328
      unsigned int j=0;
329
330
331
332
333
334
335
336
337
338
339

      unsigned int asize=0;
      if(backends.size()>0) {
        asize=backends.size();
        ap=new t_atom[asize];
      } else {
        asize=1;
        ap=new t_atom[1];
        SETSYMBOL(ap, gensym(handle->getName().c_str()));
      }

zmoelnig's avatar
zmoelnig committed
340
341
      for(j=0; j<backends.size(); j++) {
        startpost("'%s' ", backends[j].c_str());
342
        SETSYMBOL(ap+j, gensym(backends[j].c_str()));
zmoelnig's avatar
zmoelnig committed
343
344
345
      }
      if(j==0)startpost("<nothing>");
      endpost();
346
347
348
349
      
      outlet_anything(m_infoOut, gensym("driver"), asize, ap);
      delete[]ap;ap =NULL;
      asize=0;
350
351
352
    }
  }
}
353

354
/////////////////////////////////////////////////////////
355
// deviceMess
356
357
//
/////////////////////////////////////////////////////////
358
359
360
361
void pix_video :: deviceMess(int dev)
{
  WITH_VIDEOHANDLES_DO(setDevice(dev));
  restart();
362
}
363
void pix_video :: deviceMess(std::string s)
364
{
365
  WITH_VIDEOHANDLES_DO(setDevice(s));
366
  restart();
367
}
368

zmoelnig's avatar
zmoelnig committed
369
370
371
372
373
374
375
376
void pix_video :: closeMess()
{
  if(m_videoHandle) {
    m_videoHandle->stop();
    m_videoHandle->close();
  }
  m_videoHandle=NULL;
}
377

378
379
380
381
382
383
/////////////////////////////////////////////////////////
// dimenMess
//
/////////////////////////////////////////////////////////
void pix_video :: dimenMess(int x, int y, int leftmargin, int rightmargin,
			       int topmargin, int bottommargin)
384
{
385
386
387
388
389
390
391
392
393
394
  m_writeprops.set("width", x);
  m_writeprops.set("height", y);

  m_writeprops.set("leftmargin", leftmargin);
  m_writeprops.set("rightmargin", rightmargin);
  m_writeprops.set("topmargin", topmargin);
  m_writeprops.set("bottommargin", bottommargin);

  if(m_videoHandle)
    m_videoHandle->setProperties(m_writeprops);
395
396
397
}

/////////////////////////////////////////////////////////
398
// channelMess
399
400
//
/////////////////////////////////////////////////////////
401
void pix_video :: channelMess(int channel, t_float freq)
402
{
403
404
405
406
407
  m_writeprops.set("channel", channel);
  m_writeprops.set("frequency", freq);

  if(m_videoHandle)
    m_videoHandle->setProperties(m_writeprops);
408
409
}
/////////////////////////////////////////////////////////
410
// normMess
411
412
//
/////////////////////////////////////////////////////////
413
void pix_video :: normMess(std::string s)
414
{
415
  m_writeprops.set("norm", std::string(s));
416
417
418

  if(m_videoHandle)
    m_videoHandle->setProperties(m_writeprops);
419
420
}
/////////////////////////////////////////////////////////
421
// colorMess
422
423
//
/////////////////////////////////////////////////////////
424
void pix_video :: colorMess(t_atom*a)
425
{
426
427
428
429
430
431
432
433
434
435
436
  int format=0;
  if (a->a_type==A_SYMBOL){
      char c =*atom_getsymbol(a)->s_name;
      // we only have 3 colour-spaces: monochrome (GL_LUMINANCE), yuv (GL_YCBCR_422_GEM), and rgba (GL_RGBA)
      // if you don't need colour, i suggest, take monochrome
      // if you don't need alpha,  i suggest, take yuv
      // else take rgba
      switch (c){
      case 'g': case 'G': format=GL_LUMINANCE; break;
      case 'y': case 'Y': format=GL_YCBCR_422_GEM; break;
      case 'r': case 'R':
437
      default: format=GL_RGBA_GEM;
438
439
      }
  } else format=atom_getint(a);
440
441

  WITH_VIDEOHANDLES_DO(setColor(format));
442
}
443

444
445
446
447
448
449
/////////////////////////////////////////////////////////
// enumerate devices
//
/////////////////////////////////////////////////////////
void pix_video :: enumerateMess()
{
zmoelnig's avatar
zmoelnig committed
450
  std::vector<std::string>data;
451
  std::vector<std::string>backends;
452
  unsigned int i=0;
zmoelnig's avatar
zmoelnig committed
453
454
455
  for(i=0; i<m_videoHandles.size(); i++) {
    //    a.insert(a.end(), b.begin(), b.end());
    if(m_videoHandles[i]) {
456
      std::string name=m_videoHandles[i]->getName();
zmoelnig's avatar
zmoelnig committed
457
      verbose(1, "enumerating: %s", name.c_str());
zmoelnig's avatar
zmoelnig committed
458
      std::vector<std::string>temp=m_videoHandles[i]->enumerate();
zmoelnig's avatar
zmoelnig committed
459
      unsigned int i=0;
460
      for(i=0; i<temp.size(); i++) {
zmoelnig's avatar
zmoelnig committed
461
462
        backends.push_back(name);
        data.push_back(temp[i]);
463
      }
zmoelnig's avatar
zmoelnig committed
464
465
466
467
468
    }
  }
  if(data.size()<=0) {
    error("no devices found");
  }
469
470
471
472

  t_atom ap[2];
  SETFLOAT(ap, data.size());
  outlet_anything(m_infoOut, gensym("devices"), 1, ap);
zmoelnig's avatar
zmoelnig committed
473
  for(i=0; i<data.size(); i++) {
474
475
476
477
    SETSYMBOL(ap+0, gensym(data[i].c_str()));
    SETSYMBOL(ap+1, gensym(backends[i].c_str()));
    outlet_anything(m_infoOut, gensym("device"), 2, ap);
    //    post("%d: %s", i, data[i].c_str());
zmoelnig's avatar
zmoelnig committed
478
  }
479
480
481
482
483
484
485
}
/////////////////////////////////////////////////////////
// dialog
//
/////////////////////////////////////////////////////////
void pix_video :: dialogMess(int argc, t_atom*argv)
{
zmoelnig's avatar
zmoelnig committed
486
487
488
489
  if(m_videoHandle) {
    std::vector<std::string>data;
    while(argc>0) {
      data.push_back(std::string(atom_getsymbol(argv)->s_name));
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
490
      argv++;argc--;
zmoelnig's avatar
zmoelnig committed
491
492
493
494
495
496
    }

    if(!m_videoHandle->dialog(data)) {
      post("no dialog for current backend");
    }
  }
497
498
}

zmoelnig's avatar
zmoelnig committed
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
/////////////////////////////////////////////////////////
// set properties
//
// example: "set width 640, set name foo, set"
//   will first set the properties "width" to 640 annd "name" to "foo"
//   and then will apply these properties to the currently opened device
//
/////////////////////////////////////////////////////////

static gem::any atom2any(t_atom*ap) {
  gem::any result;
  if(ap) {
    switch(ap->a_type) {
    case A_FLOAT:
      result=atom_getfloat(ap);
      break;
    case A_SYMBOL:
      result=atom_getsymbol(ap)->s_name;
      break;
    default:
      result=ap->a_w.w_gpointer;
    }
  }
  return result;
}
static void addProperties(gem::Properties&props, int argc, t_atom*argv)
{
  if(!argc)return;

    if(argv->a_type != A_SYMBOL) {
529
      error("no key given...");
zmoelnig's avatar
zmoelnig committed
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
      return;
    }
    std::string key=std::string(atom_getsymbol(argv)->s_name);
    std::vector<gem::any> values;
    argc--; argv++;
    while(argc-->0) {
      values.push_back(atom2any(argv++));
    }
    switch(values.size()) {
    default:
      props.set(key, values);
      break;
    case 1:
      props.set(key, values[0]);
      break;
545
    case 0:
zmoelnig's avatar
zmoelnig committed
546
547
548
549
550
551
552
553
554
555
      {
	gem::any dummy;
	props.set(key, dummy);
      }
      break;
    }
}

void pix_video :: setPropertyMess(int argc, t_atom*argv)
{
zmoelnig's avatar
zmoelnig committed
556
557
558
559
560
  if(!argc) {
    error("no property specified!");
    return;
  }
  addProperties(m_writeprops, argc, argv);
561

zmoelnig's avatar
zmoelnig committed
562
563
  if(m_videoHandle) {
    m_videoHandle->setProperties(m_writeprops);
zmoelnig's avatar
zmoelnig committed
564
565
566
567
568
569
  }
}

void pix_video :: getPropertyMess(int argc, t_atom*argv)
{
  if(argc) {
570
571
572
573
574
575
    int i=0;
    m_readprops.clear();

    for(i=0; i<argc; i++) {
      addProperties(m_readprops, 1, argv+i);
    }
576

zmoelnig's avatar
zmoelnig committed
577
  } else {
578
579
580
581
582
583
584
    /* LATER: read all properties */
  }

  t_atom ap[4];
  if(m_videoHandle) {
    m_videoHandle->getProperties(m_readprops);
    std::vector<std::string>keys=m_readprops.keys();
zmoelnig's avatar
zmoelnig committed
585
    unsigned int i=0;
586
587
588
    for(i=0; i<keys.size(); i++) {
      std::string key=keys[i];
      SETSYMBOL(ap+0, gensym(key.c_str()));
589
      int ac=0;
590
591
      switch(m_readprops.type(key)) {
      default:
592
593
      case gem::Properties::UNSET:
        ac=0;
zmoelnig's avatar
zmoelnig committed
594
        break;
595
596
      case gem::Properties::NONE:
        ac=1;
zmoelnig's avatar
zmoelnig committed
597
        break;
598
      case gem::Properties::DOUBLE:
zmoelnig's avatar
zmoelnig committed
599
600
601
602
603
604
605
606
        do {
          double d=0;
          if(m_readprops.get(key, d)) {
            ac=2;
            SETFLOAT(ap+1, d);
          }
        } while(0);
        break;
607
      case gem::Properties::STRING:
zmoelnig's avatar
zmoelnig committed
608
609
610
611
612
613
614
615
        do {
          std::string s;
          if(m_readprops.get(key, s)) {
            ac=2;
            SETSYMBOL(ap+1, gensym(s.c_str()));
          }
        } while(0);
        break;
616
      }
617
      if(ac) {
zmoelnig's avatar
zmoelnig committed
618
        outlet_anything(m_infoOut, gensym("prop"), ac, ap);
619
      } else {
zmoelnig's avatar
zmoelnig committed
620
        post("oops: %s", key.c_str());
zmoelnig's avatar
zmoelnig committed
621
622
      }
    }
623
624
  } else {
    verbose(1, "no open videodevice...remembering properties...");
zmoelnig's avatar
zmoelnig committed
625
626
627
628
629
630
  }
}

void pix_video :: enumPropertyMess()
{
  if(m_videoHandle) {
631
632
633
634
635
    gem::Properties readable, writeable;
    std::vector<std::string>readkeys, writekeys;
    t_atom ap[4];
    int ac=3;

zmoelnig's avatar
zmoelnig committed
636
637
638

    m_videoHandle->enumProperties(readable, writeable);

639
640
641
642
    readkeys=readable.keys();

    SETSYMBOL(ap+0, gensym("numread"));
    SETFLOAT(ap+1, readkeys.size());
zmoelnig's avatar
zmoelnig committed
643
    outlet_anything(m_infoOut, gensym("proplist"), 2, ap);
644
645

    SETSYMBOL(ap+0, gensym("read"));
zmoelnig's avatar
zmoelnig committed
646
    unsigned int i=0;
647
648
649
650
651
652
    for(i=0; i<readkeys.size(); i++) {
      ac=3;
      std::string key=readkeys[i];
      SETSYMBOL(ap+1, gensym(key.c_str()));
      switch(readable.type(key)) {
      case gem::Properties::NONE:
653
654
        SETSYMBOL(ap+2, gensym("bang"));
        break;
655
      case gem::Properties::DOUBLE: {
656
657
658
659
660
661
662
        double d=-1;
        SETSYMBOL(ap+2, gensym("float"));
        /* LATER: get and show ranges */
        if(readable.get(key, d)) {
          ac=4;
          SETFLOAT(ap+3, d);
        }
663
      }
664
        break;
665
      case gem::Properties::STRING: {
666
667
668
669
670
671
        SETSYMBOL(ap+2, gensym("symbol"));
        std::string s;
        if(readable.get(key, s)) {
          ac=4;
          SETSYMBOL(ap+3, gensym(s.c_str()));
        }
672
      }
673
        break;
674
      default:
675
676
        SETSYMBOL(ap+2, gensym("unknown"));
        break;
677
      }
zmoelnig's avatar
zmoelnig committed
678
      outlet_anything(m_infoOut, gensym("proplist"), ac, ap);
zmoelnig's avatar
zmoelnig committed
679
    }
680
681
682
683
684
685


    writekeys=writeable.keys();

    SETSYMBOL(ap+0, gensym("numwrite"));
    SETFLOAT(ap+1, writekeys.size());
zmoelnig's avatar
zmoelnig committed
686
    outlet_anything(m_infoOut, gensym("proplist"), 2, ap);
687
688
689

    SETSYMBOL(ap+0, gensym("write"));
    for(i=0; i<writekeys.size(); i++) {
690
      ac=3;
691
692
693
694
      std::string key=writekeys[i];
      SETSYMBOL(ap+1, gensym(key.c_str()));
      switch(writeable.type(key)) {
      case gem::Properties::NONE:
695
696
        SETSYMBOL(ap+2, gensym("bang"));
        break;
697
      case gem::Properties::DOUBLE: {
698
699
700
701
702
703
704
        double d=-1;
        SETSYMBOL(ap+2, gensym("float"));
        /* LATER: get and show ranges */
        ac=4;
        if(writeable.get(key, d)) {
          SETFLOAT(ap+3, d);
        }
705
      }
706
        break;
707
      case gem::Properties::STRING: {
708
709
710
711
712
713
        ac=4;
        SETSYMBOL(ap+2, gensym("symbol"));
        std::string s;
        if(writeable.get(key, s)) {
          SETSYMBOL(ap+3, gensym(s.c_str()));
        }
714
      }
715
        break;
716
      default:
717
718
        SETSYMBOL(ap+2, gensym("unknown"));
        break;
719
      }
zmoelnig's avatar
zmoelnig committed
720
      outlet_anything(m_infoOut, gensym("proplist"), ac, ap);
zmoelnig's avatar
zmoelnig committed
721
722
    }
  } else {
723
    error("cannot enumerate properties without a valid video-device");
zmoelnig's avatar
zmoelnig committed
724
725
726
  }
}

zmoelnig's avatar
zmoelnig committed
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
void pix_video :: setPropertiesMess(int argc, t_atom*argv)
{
  addProperties(m_writeprops, argc, argv);
}

void pix_video :: applyPropertiesMess()
{
  if(m_videoHandle) {
    m_videoHandle->setProperties(m_writeprops);
  } else {
    verbose(1, "no open videodevice...remembering properties...");
  }
}

void pix_video :: clearPropertiesMess()
{
  m_writeprops.clear();
}





750
void pix_video :: asynchronousMess(bool state)
751
{
zmoelnig's avatar
zmoelnig committed
752
  unsigned int i;
zmoelnig's avatar
zmoelnig committed
753
754
  for(i=0; i<m_videoHandles.size(); i++) {
    if(m_videoHandles[i])
755
      m_videoHandles[i]->grabAsynchronous(state);
zmoelnig's avatar
zmoelnig committed
756
  }
757
758
}

759
760
761
762
/////////////////////////////////////////////////////////
// qualityMess
//
/////////////////////////////////////////////////////////
763
764
765
766
767
void pix_video :: qualityMess(int q) {
  m_writeprops.set("quality", q);

  if(m_videoHandle)
    m_videoHandle->setProperties(m_writeprops);
768
}
769

770

771
772
773
774
775
776
777
778
void pix_video :: resetMess(void) {
  if(m_videoHandle)
    m_videoHandle->reset();
  else
	    WITH_VIDEOHANDLES_DO(reset());
}


779
780
781
782
783
/////////////////////////////////////////////////////////
// transferMess
//
/////////////////////////////////////////////////////////
void pix_video :: runningMess(bool state) {
zmoelnig's avatar
zmoelnig committed
784
  m_running=state?STARTED:STOPPED;
785
  if(m_videoHandle) {
zmoelnig's avatar
zmoelnig committed
786
    if(STARTED==m_running)
787
788
789
790
791
792
793
794
      m_videoHandle->start();
    else
      m_videoHandle->stop();
  }
}



795
796
797
798
799
800
/////////////////////////////////////////////////////////
// static member function
//
/////////////////////////////////////////////////////////
void pix_video :: obj_setupCallback(t_class *classPtr)
{
801
    CPPEXTERN_MSG0(classPtr, "enumerate", enumerateMess);
zmoelnig's avatar
zmoelnig committed
802
803
804
805
806
807

    class_addmethod(classPtr, reinterpret_cast<t_method>(&pix_video::driverMessCallback),
    	    gensym("driver"), A_GIMME, A_NULL);
    class_addmethod(classPtr, reinterpret_cast<t_method>(&pix_video::deviceMessCallback),
    	    gensym("device"), A_GIMME, A_NULL);

808
809
    CPPEXTERN_MSG0(classPtr, "close", closeMess);

zmoelnig's avatar
zmoelnig committed
810
811
812
    class_addmethod(classPtr, reinterpret_cast<t_method>(&pix_video::openMessCallback),
	    gensym("open"), A_GIMME, A_NULL);

813
    CPPEXTERN_MSG1(classPtr, "float", runningMess, bool);
zmoelnig's avatar
zmoelnig committed
814
815
816
817
818
819
820
821

    class_addmethod(classPtr, reinterpret_cast<t_method>(&pix_video::dialogMessCallback),
    	    gensym("dialog"), A_GIMME, A_NULL);

    class_addmethod(classPtr, reinterpret_cast<t_method>(&pix_video::setPropertyMessCallback),
    	    gensym("set"), A_GIMME, A_NULL);
    class_addmethod(classPtr, reinterpret_cast<t_method>(&pix_video::getPropertyMessCallback),
    	    gensym("get"), A_GIMME, A_NULL);
zmoelnig's avatar
zmoelnig committed
822

zmoelnig's avatar
zmoelnig committed
823
    CPPEXTERN_MSG0(classPtr, "enumProps", enumPropertyMess);
824
    class_addmethod(classPtr,
zmoelnig's avatar
zmoelnig committed
825
826
		    reinterpret_cast<t_method>(&pix_video::  setPropertiesMessCallback),
		    gensym("setProps"), A_GIMME, A_NULL);
827
828
    CPPEXTERN_MSG0(classPtr, "applyProps", applyPropertiesMess);
    CPPEXTERN_MSG0(classPtr, "clearProps", clearPropertiesMess);
zmoelnig's avatar
zmoelnig committed
829

830
    CPPEXTERN_MSG1(classPtr, "async", asynchronousMess, bool);
831
832


zmoelnig's avatar
zmoelnig committed
833
834
835
    class_addmethod(classPtr, reinterpret_cast<t_method>(&pix_video::colorMessCallback),
    	    gensym("colorspace"), A_GIMME, A_NULL);

zmoelnig's avatar
zmoelnig committed
836
    class_addmethod(classPtr, reinterpret_cast<t_method>(&pix_video::dimenMessCallback),
837
    	    gensym("dimen"), A_GIMME, A_NULL);
838
839
    CPPEXTERN_MSG1(classPtr, "norm", normMess, std::string);

zmoelnig's avatar
zmoelnig committed
840
    class_addmethod(classPtr, reinterpret_cast<t_method>(&pix_video::channelMessCallback),
841
    	    gensym("channel"), A_GIMME, A_NULL);
zmoelnig's avatar
zmoelnig committed
842
    class_addmethod(classPtr, reinterpret_cast<t_method>(&pix_video::modeMessCallback),
843
    	    gensym("mode"), A_GIMME, A_NULL);
zmoelnig's avatar
zmoelnig committed
844
    class_addmethod(classPtr, reinterpret_cast<t_method>(&pix_video::colorMessCallback),
845
    	    gensym("color"), A_GIMME, A_NULL);
846
847
    CPPEXTERN_MSG1(classPtr, "quality", qualityMess, int);

848
849
	CPPEXTERN_MSG0(classPtr, "reset", resetMess);

850
851
852
}
void pix_video :: dimenMessCallback(void *data, t_symbol *s, int ac, t_atom *av)
{
853
854
855
856
857
858
  GetMyClass(data)->dimenMess(static_cast<int>(atom_getfloatarg(0, ac, av)),
			      static_cast<int>(atom_getfloatarg(1, ac, av)),
			      static_cast<int>(atom_getfloatarg(2, ac, av)),
			      static_cast<int>(atom_getfloatarg(3, ac, av)),
			      static_cast<int>(atom_getfloatarg(4, ac, av)),
			      static_cast<int>(atom_getfloatarg(5, ac, av)) );
859
}
860
861
862
863
864
void pix_video :: channelMessCallback(void *data, t_symbol*s, int argc, t_atom*argv)
{
  if (argc!=1&&argc!=2)return;
  int chan = atom_getint(argv);
  t_float freq = (argc==1)?0:atom_getfloat(argv+1);
865
  GetMyClass(data)->channelMess(static_cast<int>(chan), freq);
866
867
868
869
870
871
872

}
void pix_video :: modeMessCallback(void *data, t_symbol* nop, int argc, t_atom *argv)
{
  switch (argc){
  case 1:
    if      (A_FLOAT ==argv->a_type)GetMyClass(data)->channelMess(atom_getint(argv));
873
    else if (A_SYMBOL==argv->a_type)GetMyClass(data)->normMess(atom_getsymbol(argv)->s_name);
874
875
876
877
    else goto mode_error;
    break;
  case 2:
    if (A_SYMBOL==argv->a_type && A_FLOAT==(argv+1)->a_type){
878
      GetMyClass(data)->normMess(atom_getsymbol(argv)->s_name);
879
      GetMyClass(data)->channelMess(atom_getint(argv+1));
880
    } else goto mode_error;
881
882
883
884
885
886
887
888
889
890
    break;
  default:
  mode_error:
    ::post("invalid arguments for message \"mode [<norm>] [<channel>]\"");
  }
}
void pix_video :: colorMessCallback(void *data, t_symbol* nop, int argc, t_atom *argv){
  if (argc==1)GetMyClass(data)->colorMess(argv);
  else GetMyClass(data)->error("invalid number of arguments (must be 1)");
}
891

892
void pix_video :: deviceMessCallback(void *data, t_symbol*,int argc, t_atom*argv)
893
{
894
895
896
897
898
899
  if(argc==1){
    switch(argv->a_type){
    case A_FLOAT:
      GetMyClass(data)->deviceMess(atom_getint(argv));
      break;
    case A_SYMBOL:
900
      GetMyClass(data)->deviceMess(atom_getsymbol(argv)->s_name);
901
902
903
904
905
906
907
908
      break;
    default:
      GetMyClass(data)->error("device must be integer or symbol");
    }
  } else {
    GetMyClass(data)->error("can only set to 1 device at a time");
  }
}
909
void pix_video :: driverMessCallback(void *data, t_symbol*s, int argc, t_atom*argv)
910
{
911
  if(!argc) {
912
913
914
    GetMyClass(data)->driverMess();
    return;
  }
915
916
917
918
919
  if(argc!=1) {
    GetMyClass(data)->error("'driver' takes a single numeric or symbolic driver ID");
  } else if (argv->a_type == A_FLOAT) {
    GetMyClass(data)->driverMess(atom_getint(argv));
  } else if (argv->a_type == A_SYMBOL) {
920
    GetMyClass(data)->driverMess(atom_getsymbol(argv)->s_name);
921
922
923
  } else {
    GetMyClass(data)->error("'driver' takes a single numeric or symbolic driver ID");
  }
924
925
926
927
928
}
void pix_video :: dialogMessCallback(void *data, t_symbol*s, int argc, t_atom*argv)
{
  GetMyClass(data)->dialogMess(argc, argv);
}
zmoelnig's avatar
zmoelnig committed
929
930
931
932
void pix_video :: getPropertyMessCallback(void *data, t_symbol*s, int argc, t_atom*argv)
{
  GetMyClass(data)->getPropertyMess(argc, argv);
}
zmoelnig's avatar
zmoelnig committed
933
934
935
936
937
938
939
940
941
942
void pix_video :: setPropertyMessCallback(void *data, t_symbol*s, int argc, t_atom*argv)
{
  GetMyClass(data)->setPropertyMess(argc, argv);
}

void pix_video :: setPropertiesMessCallback(void *data, t_symbol*s, int argc, t_atom*argv)
{
  GetMyClass(data)->setPropertiesMess(argc, argv);
}

943
void pix_video :: openMessCallback(void *data, t_symbol*s, int argc, t_atom*argv)
zmoelnig's avatar
zmoelnig committed
944
{
945
946
947
  if(argc)driverMessCallback(data, s, argc, argv);
  else
    GetMyClass(data)->startRendering();
zmoelnig's avatar
zmoelnig committed
948
}
949
#endif /* no OS-specific GEM_VIDEOBACKEND */