pix_record.cpp 11.3 KB
Newer Older
cclepper's avatar
cclepper committed
1
2
3
4
/*
 *  pix_record.cpp
 *
 */
5
#include "Gem/GemConfig.h"
cclepper's avatar
cclepper committed
6
7
#include "pix_record.h"

8
#include "Gem/State.h"
9
#include "Gem/Exception.h"
10

11
#include "RTE/MessageCallbacks.h"
12
13
14
#include "plugins/PluginFactory.h"

#include <map>
15
#include <algorithm>
16

17
CPPEXTERN_NEW_WITH_GIMME(pix_record);
cclepper's avatar
cclepper committed
18

zmoelnig's avatar
zmoelnig committed
19
20
class pix_record :: PIMPL {
public:
21
22
23
24
  PIMPL(void) {};
  ~PIMPL(void) {};

  struct codechandle {
25
    codechandle(gem::plugins::record*h, const std::string c):handle(h), codec(c) {}
26

27
    gem::plugins::record*handle;
zmoelnig's avatar
zmoelnig committed
28
    std::string codec;
29
  };
zmoelnig's avatar
zmoelnig committed
30
  std::map<std::string, std::vector<codechandle> >m_codechandle;
31
32
  std::vector<std::string>m_codecs;

33
  void addCodecHandle(gem::plugins::record*handle, const std::string codec) {
34
35
36
#ifdef __GNUC__
# warning better handling of duplicate codecs
#endif
zmoelnig's avatar
zmoelnig committed
37
38
    /* FIXME: we should generate a unique codec-ID, e.g. "<handlename>:<codec>" */
    m_codechandle[codec].push_back(codechandle(handle, codec));
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
    m_codecs.push_back(codec);
  }
  void clearCodecHandle(void) {
    m_codecs.clear();
    m_codechandle.clear();
  }

  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;
65

66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
    if(argv->a_type != A_SYMBOL) {
      ::error("no key given...");
      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;
83
    case 0:
84
85
86
87
88
89
90
      {
	gem::any dummy;
	props.set(key, dummy);
      }
      break;
    }
  }
91

92
93
};

94
/////////////////////////////////////////////////////////
cclepper's avatar
cclepper committed
95
96
97
98
99
100
101
//
// pix_record
//
/////////////////////////////////////////////////////////
// Constructor
//
/////////////////////////////////////////////////////////
zmoelnig's avatar
zmoelnig committed
102
pix_record :: pix_record(int argc, t_atom *argv):
103
104
  m_banged(false), m_automatic(true),
  m_outNumFrames(NULL), m_outInfo(NULL),
zmoelnig's avatar
zmoelnig committed
105
  m_currentFrame(-1),
zmoelnig's avatar
zmoelnig committed
106
  m_maxFrames(0),
107
108
109
  m_recording(false),
  m_handle(NULL),
  m_pimpl(new PIMPL())
cclepper's avatar
cclepper committed
110
{
111
112
  if (argc != 0){
    error("ignoring arugments");
cclepper's avatar
cclepper committed
113
  }
cclepper's avatar
cclepper committed
114
  m_outNumFrames = outlet_new(this->x_obj, 0);
115
  m_outInfo      = outlet_new(this->x_obj, 0);
cclepper's avatar
cclepper committed
116

117

IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
118
  m_handle=gem::plugins::record::getInstance();
119
  getCodecList();
cclepper's avatar
cclepper committed
120
121
122
123
124
125
126
127
}

/////////////////////////////////////////////////////////
// Destructor
//
/////////////////////////////////////////////////////////
pix_record :: ~pix_record()
{
128
  if(m_handle)delete m_handle;
129
130
  outlet_free(m_outNumFrames);
  outlet_free(m_outInfo);
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164

  if(m_pimpl)delete m_pimpl;
}


/////////////////////////////////////////////////////////
// add backends
//
/////////////////////////////////////////////////////////
bool pix_record :: addHandle( std::vector<std::string>available, std::string ID)
{
  unsigned int i=0;
  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;
  }

  for(i=0; i<id.size(); i++) {
    std::string key=id[i];
    verbose(2, "trying to add '%s' as backend", key.c_str());
    if(std::find(m_ids.begin(), m_ids.end(), key)==m_ids.end()) {
      // not yet added, do so now!
165
      gem::plugins::record         *handle=NULL;
zmoelnig's avatar
zmoelnig committed
166
      startpost("backend #%d='%s'\t", m_allhandles.size(), key.c_str());
167
      try {
168
        handle=gem::PluginFactory<gem::plugins::record>::getInstance(key);
169
      } catch (GemException&ex) {
170
171
        startpost("(%s) ", ex.what());
        handle=NULL;
172
      }
173
      if(NULL==handle) {
174
175
        post("<--- DISABLED");
        break;
176
177
178
179
      }
      endpost();

      m_ids.push_back(key);
zmoelnig's avatar
zmoelnig committed
180
      m_allhandles.push_back(handle);
181
      count++;
zmoelnig's avatar
zmoelnig committed
182
      verbose(2, "added backend#%d '%s' @ 0x%x", m_allhandles.size()-1, key.c_str(), handle);
183
184
185
186
187
188
189
190
191
192
193
    }
  }

  return (count>0);
}

//
// stops recording into the movie
//
void pix_record :: startRecording()
{
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
194
195
  if(!m_handle)return;

zmoelnig's avatar
zmoelnig committed
196
197
198
199
200
  if(m_filename.empty()) {
    error("start recording requested with no prior open");
    return;
  }

201
202
  // find a handle for the current settings (filename, codec, props)
  const std::string codec=m_codec;
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
203
204
205
  stopRecording();


zmoelnig's avatar
zmoelnig committed
206
  m_currentFrame = 0;
207
  unsigned int i=0;
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
208
209
  // do not re-set the codec, if there is no need...
  /* m_handle->setCodec(codec); */
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
210
  if(m_handle->start(m_filename, m_props)) {
211
212
    m_filename=std::string("");
    m_recording=true;
zmoelnig's avatar
zmoelnig committed
213
214
  } else {
    post("unable to open '%s'", m_filename.c_str());
215
  }
cclepper's avatar
cclepper committed
216
217
218
}

//
219
// stops recording into the movie
cclepper's avatar
cclepper committed
220
221
222
//
void pix_record :: stopRecording()
{
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
223
224
  if(!m_handle)return;

225
  if(m_recording) {
226
    m_handle->stop();
227
    m_currentFrame = 0;
228
    outlet_float(m_outNumFrames,m_currentFrame);
229
    verbose(1, "movie written");
230
  }
cclepper's avatar
cclepper committed
231

232
  m_recording=false;
cclepper's avatar
cclepper committed
233
234
235
236
237
238
239
240
241
}


/////////////////////////////////////////////////////////
// render
//
/////////////////////////////////////////////////////////
void pix_record :: render(GemState *state)
{
242
  if(!m_handle || !m_recording)return;
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
243

244
  //check if state exists
zmoelnig's avatar
zmoelnig committed
245
246
  if(!state)return;
  pixBlock*img=NULL;
247
  state->get(GemState::_PIX, img);
zmoelnig's avatar
zmoelnig committed
248
249

  if(!img || !img->image.data){
zmoelnig's avatar
zmoelnig committed
250
251
    return;
  }
252

253
254
  if(m_banged||m_automatic){
    //      if(m_maxFrames != 0 && m_currentFrame >= m_maxFrames) m_recordStop = 1;
zmoelnig's avatar
zmoelnig committed
255
    bool success=m_handle->write(&img->image);
256
257
258
259
    m_banged=false;

    if(success) {
      m_currentFrame++;
zmoelnig's avatar
zmoelnig committed
260
      outlet_float(m_outNumFrames,m_currentFrame);
261
262
    } else {
      stopRecording();
zmoelnig's avatar
zmoelnig committed
263
264
    }
  }
cclepper's avatar
cclepper committed
265
266
267
}

/////////////////////////////////////////////////////////
268
// Properties
cclepper's avatar
cclepper committed
269
270
//
/////////////////////////////////////////////////////////
271
void pix_record :: enumPropertiesMess()
cclepper's avatar
cclepper committed
272
{
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
273
  if(!m_handle)return;
274

IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
  gem::Properties props;
  if(!m_handle->enumProperties(props))
    return;

  int ac=0;
  t_atom ap[3];
  std::vector<std::string>keys=props.keys();

  SETFLOAT(ap+0, keys.size());
  outlet_anything(m_outInfo, gensym("numprops"), 1, ap);

  unsigned int i=0;
  for(i=0; i<keys.size(); i++) {
    ac=2;
    std::string key=keys[i];
    SETSYMBOL(ap+0, gensym(key.c_str()));
    switch(props.type(key)) {
    case gem::Properties::NONE:
      SETSYMBOL(ap+1, gensym("Bang"));
      break;
    case gem::Properties::DOUBLE: {
      double d=-1;
      SETSYMBOL(ap+1, gensym("Float"));
      /* LATER: get and show ranges */
      if(props.get(key, d)) {
	ac=3;
	SETFLOAT(ap+2, d);
302
      }
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
303
304
305
306
307
308
309
310
    }
      break;
    case gem::Properties::STRING: {
      SETSYMBOL(ap+1, gensym("Symbol"));
      std::string s;
      if(props.get(key, s)) {
	ac=3;
	SETSYMBOL(ap+2, gensym(s.c_str()));
311
      }
312
    }
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
313
314
315
316
317
318
      break;
    default:
      SETSYMBOL(ap+1, gensym("unknown"));
      break;
    }
    outlet_anything(m_outInfo, gensym("property"), ac, ap);
319
  }
cclepper's avatar
cclepper committed
320
}
321
void pix_record :: setPropertiesMess(t_symbol*s, int argc, t_atom*argv)
322
323
324
{
  PIMPL::addProperties(m_props, argc, argv);
}
cclepper's avatar
cclepper committed
325

326
void pix_record :: clearPropertiesMess()
cclepper's avatar
cclepper committed
327
{
328
  m_props.clear();
cclepper's avatar
cclepper committed
329
330
}

331
332


333
334
335
336
337
338
/////////////////////////////////////////////////////////
// dialogMess
//
/////////////////////////////////////////////////////////
void pix_record :: dialogMess()
{
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
339
340
341
342
  if(!m_handle)return;

  if(!m_handle->dialog()){
    error("unable to open settings dialog");
343
344
  }
}
345
/////////////////////////////////////////////////////////
346
// recordMess
347
348
349
350
351
//
/////////////////////////////////////////////////////////
void pix_record :: recordMess(bool on)
{
  if (on) {
352
    startRecording();
353
  }else{
354
    stopRecording();
355
356
  }
}
357
358
359
360
361
362
363

/////////////////////////////////////////////////////////
// spits out a list of installed codecs and stores them
//
/////////////////////////////////////////////////////////
void pix_record :: getCodecList()
{
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
  if(!m_handle)return;

  std::vector<std::string>codecs=m_handle->getCodecs();

  unsigned int i;
  for(i=0; i<codecs.size(); i++) {
    const std::string codecname=codecs[i];
    const std::string descr=m_handle->getCodecDescription(codecname);
    t_atom ap[3];

    verbose(2, "codec%d: '%s': %s", i, codecname.c_str(), (descr.empty()?"":descr.c_str()));
    SETFLOAT (ap+0, static_cast<t_float>(i));
    SETSYMBOL(ap+1, gensym(codecname.c_str()));
    SETSYMBOL(ap+2, gensym(descr.c_str()));
    outlet_anything(m_outInfo, gensym("codec"), 3, ap);
379
  }
380
381
382
383
384
385
386
}


/////////////////////////////////////////////////////////
// deals with the name of a codec
//
/////////////////////////////////////////////////////////
387
void pix_record :: codecMess(t_atom *argv)
388
{
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
389
390
  if(!m_handle)return;

391
#ifdef __GNUC__
zmoelnig's avatar
zmoelnig committed
392
#warning codecMess is a mess
393
#endif
zmoelnig's avatar
zmoelnig committed
394
395
396
397
398
399
400
401
402
403
404
405
406
  /*
   * allow setting of codec without handle
   */

  /*
   * codecMess should do the following:
   *  find "valid" handles (those that support the given codec)
   *  copy all valid handles from m_allhandles to m_handles
   *  query all valid handles for settable properties
   *
   * if a special codec is given (e.g. none at all), all handles are valid
   */

407
408
409
410
  std::string sid;

  if (A_SYMBOL==argv->a_type) {
    sid=std::string(atom_getsymbol(argv)->s_name);
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
411
  } else if (A_FLOAT==argv->a_type) {
412
    int id=atom_getint(argv);
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
413
    std::vector<std::string>codecs=m_handle->getCodecs();
414
    if(id>0 && ((unsigned int)id)<codecs.size())
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
415
416
417
418
      sid=codecs[id];
    else {
      error("invalid codec# %d (0..%d)", id, codecs.size());
      return;
419
420
    }
  }
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
421
  if(m_handle->setCodec(sid)) {
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
422
    m_codec=sid;
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
423
    verbose(1, "successfully set codec '%s'", sid.c_str());
424
  } else {
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
425
426
    error("couldn't find a valid backend for codec '%s'", sid.c_str());
    return;
427
  }
IOhannes m zmölnig's avatar
IOhannes m zmölnig committed
428
  enumPropertiesMess();
429
430
}

431
void pix_record :: fileMess(t_symbol*s, int argc, t_atom *argv)
cclepper's avatar
cclepper committed
432
{
zmoelnig's avatar
zmoelnig committed
433
  /* LATER let the record()-handles chose whether they accept an open request
434
435
436
   * and then try other handles (if available)
   * this would allow to use this object for streaming, virtual output devices,...
   */
437
438
  if(argc) {
    m_filename=std::string(atom_getsymbol(argv)->s_name);
cclepper's avatar
cclepper committed
439
440
441
442
443
444
445
446
447
  }
}

/////////////////////////////////////////////////////////
// static member functions
//
/////////////////////////////////////////////////////////
void pix_record :: obj_setupCallback(t_class *classPtr)
{
448
  CPPEXTERN_MSG (classPtr, "file", fileMess);
cclepper's avatar
cclepper committed
449

450
451
  CPPEXTERN_MSG1(classPtr, "auto", autoMess, bool);
  CPPEXTERN_MSG0(classPtr, "bang", bangMess);
452
453
454
  CPPEXTERN_MSG1(classPtr, "record", recordMess, bool);
  CPPEXTERN_MSG0(classPtr, "dialog", dialogMess);
  CPPEXTERN_MSG0(classPtr, "codeclist", getCodecList);
zmoelnig's avatar
zmoelnig committed
455
  class_addmethod(classPtr, reinterpret_cast<t_method>(&pix_record::codecMessCallback),
456
		  gensym("codec"), A_GIMME, A_NULL);
457

458
  CPPEXTERN_MSG0(classPtr, "proplist", enumPropertiesMess);
459
  CPPEXTERN_MSG0(classPtr, "enumProps", enumPropertiesMess);
460
  CPPEXTERN_MSG (classPtr, "set", setPropertiesMess);
cclepper's avatar
cclepper committed
461

462
  CPPEXTERN_MSG0(classPtr, "clearProps", clearPropertiesMess);
463
  CPPEXTERN_MSG0(classPtr, "clearprops", clearPropertiesMess);
cclepper's avatar
cclepper committed
464
465
}

466
void pix_record :: bangMess(void)
467
{
468
  m_banged=true;
469
}
470
void pix_record :: autoMess(bool on)
471
{
472
  m_automatic=on;
473
474
475
476
}

void pix_record :: codecMessCallback(void *data, t_symbol *s, int argc, t_atom *argv)
{
477
478
  if(argc)
    GetMyClass(data)->codecMess(argv);
479
}