OPAL  Version 3.12.5
opalplugin.hpp
Go to the documentation of this file.
1 /*
2  * opalplugins.hpp
3  *
4  * OPAL codec plugins handler (C++ version)
5  *
6  * Open Phone Abstraction Library (OPAL)
7  * Formally known as the Open H323 project.
8  *
9  * Copyright (C) 2010 Vox Lucida
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  *
15  * - Redistributions of source code must retain the above copyright
16  * notice, this list of conditions and the following disclaimer.
17 
18  * - Redistributions in binary form must reproduce the above copyright
19  * notice, this list of conditions and the following disclaimer in the
20  * documentation and/or other materials provided with the distribution.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
26  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
27  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
28  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
29  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
30  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
31  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  *
34  * $Revision: 29223 $
35  * $Author: rjongbloed $
36  * $Date: 2013-03-07 18:00:29 -0600 (Thu, 07 Mar 2013) $
37  */
38 
39 #ifndef OPAL_CODEC_OPALPLUGIN_HPP
40 #define OPAL_CODEC_OPALPLUGIN_HPP
41 
42 #include "opalplugin.h"
43 
44 #include <string.h>
45 #include <stdlib.h>
46 #include <limits.h>
47 
48 #include <map>
49 #include <string>
50 
51 
53 
54 #ifndef PLUGINCODEC_TRACING
55  #define PLUGINCODEC_TRACING 1
56 #endif
57 
58 #if PLUGINCODEC_TRACING
60  extern int PluginCodec_SetLogFunction(const PluginCodec_Definition *, void *, const char *, void * parm, unsigned * len);
61 
62 #define PLUGINCODEC_CONTROL_LOG_FUNCTION_DEF \
63  PluginCodec_LogFunction PluginCodec_LogFunctionInstance; \
64  int PluginCodec_SetLogFunction(const PluginCodec_Definition *, void *, const char *, void * parm, unsigned * len) \
65  { \
66  if (len == NULL || *len != sizeof(PluginCodec_LogFunction)) \
67  return false; \
68  \
69  PluginCodec_LogFunctionInstance = (PluginCodec_LogFunction)parm; \
70  if (PluginCodec_LogFunctionInstance != NULL) \
71  PluginCodec_LogFunctionInstance(4, __FILE__, __LINE__, "Plugin", "Started logging."); \
72  \
73  return true; \
74  } \
75 
76  #define PLUGINCODEC_CONTROL_LOG_FUNCTION_INC { PLUGINCODEC_CONTROL_SET_LOG_FUNCTION, PluginCodec_SetLogFunction },
77 #else
78  #define PLUGINCODEC_CONTROL_LOG_FUNCTION_DEF
79  #define PLUGINCODEC_CONTROL_LOG_FUNCTION_INC
80 #endif
81 
82 #if !defined(PTRACE)
83  #if PLUGINCODEC_TRACING
84  #include <sstream>
85  #define PTRACE_CHECK(level) \
86  (PluginCodec_LogFunctionInstance != NULL && PluginCodec_LogFunctionInstance(level, NULL, 0, NULL, NULL))
87  #define PTRACE(level, section, args) \
88  if (PTRACE_CHECK(level)) { \
89  std::ostringstream strm; strm << args; \
90  PluginCodec_LogFunctionInstance(level, __FILE__, __LINE__, section, strm.str().c_str()); \
91  } else (void)0
92  #else
93  #define PTRACE_CHECK(level)
94  #define PTRACE(level, section, expr)
95  #endif
96 #endif
97 
98 
100 
102 {
103  unsigned char * m_packet;
104  unsigned m_maxSize;
105  unsigned m_headerSize;
106  unsigned m_payloadSize;
107 
108  public:
109  PluginCodec_RTP(const void * packet, unsigned size)
110  : m_packet((unsigned char *)packet)
111  , m_maxSize(size)
112  , m_headerSize(PluginCodec_RTP_GetHeaderLength(m_packet))
113  , m_payloadSize(size - m_headerSize)
114  {
115  }
116 
117  __inline unsigned GetMaxSize() const { return m_maxSize; }
118  __inline unsigned GetPacketSize() const { return m_headerSize+m_payloadSize; }
119  __inline unsigned GetHeaderSize() const { return m_headerSize; }
120  __inline unsigned GetPayloadSize() const { return m_payloadSize; }
121  __inline bool SetPayloadSize(unsigned size)
122  {
123  if (m_headerSize+size > m_maxSize)
124  return false;
125  m_payloadSize = size;
126  return true;
127  }
128 
129  __inline unsigned GetPayloadType() const { return PluginCodec_RTP_GetPayloadType(m_packet); }
130  __inline void SetPayloadType(unsigned type) { PluginCodec_RTP_SetPayloadType(m_packet, type); }
131  __inline bool GetMarker() const { return PluginCodec_RTP_GetMarker(m_packet); }
132  __inline void SetMarker(bool mark) { PluginCodec_RTP_SetMarker(m_packet, mark); }
133  __inline unsigned GetTimestamp() const { return PluginCodec_RTP_GetTimestamp(m_packet); }
134  __inline void SetTimestamp(unsigned ts) { PluginCodec_RTP_SetTimestamp(m_packet, ts); }
135  __inline unsigned GetSequenceNumber() const { return PluginCodec_RTP_GetSequenceNumber(m_packet); }
136  __inline void SetSequenceNumber(unsigned sn) { PluginCodec_RTP_SetSequenceNumber(m_packet, sn); }
137  __inline unsigned GetSSRC() const { return PluginCodec_RTP_GetSSRC(m_packet); }
138  __inline void SetSSRC(unsigned ssrc) { PluginCodec_RTP_SetSSRC(m_packet, ssrc); }
139 
140  __inline unsigned char * SetExtended(unsigned id, unsigned len)
141  {
142  m_packet[0] |= 0x10;
143 
144  unsigned char * ptr = m_packet + PluginCodec_RTP_GetCSRCHdrLength(m_packet);
145  switch (id >> 16) {
146  case 0 :
147  PluginCodec_RTP_SetWORD(ptr, 0, id);
148  PluginCodec_RTP_SetWORD(ptr, 2, (len+3)/4);
149  ptr += 4;
150  break;
151 
152  case 1 :
153  *ptr++ = 0xbe;
154  *ptr++ = 0xde;
155  PluginCodec_RTP_SetWORD(ptr, 0, (len+7)/4); ptr += 2;
156  *ptr++ = (unsigned char)(((id&0xf) << 4)|(len-1));
157  break;
158 
159  case 2 :
160  *ptr++ = 0x10;
161  *ptr++ = 0x00;
162  PluginCodec_RTP_SetWORD(ptr, 0, (len+8)/4); ptr += 2;
163  *ptr++ = (unsigned char)(id&0xff);
164  *ptr++ = (unsigned char)(len&0xff);
165  }
166 
167  m_headerSize = PluginCodec_RTP_GetHeaderLength(m_packet);
168  return ptr;
169  }
170 
171  __inline unsigned char * GetExtendedHeader(unsigned & id, size_t & len) const
172  {
173  if ((m_packet[0]&0x10) == 0)
174  return NULL;
175 
176  unsigned char * ptr = m_packet + PluginCodec_RTP_GetCSRCHdrLength(m_packet);
177  id = PluginCodec_RTP_GetWORD(ptr, 0);
178 
179  if (id == 0xbede) {
180  id = (0x10000|(ptr[4] >> 4));
181  len = (ptr[4] & 0xf)+1;
182  return ptr + 5;
183  }
184 
185  if ((id&0xfff0) == 0x1000) {
186  id = 0x20000 | ptr[4];
187  len = ptr[5];
188  return ptr + 6;
189  }
190 
191  len = PluginCodec_RTP_GetWORD(ptr, 2)*4;
192  return ptr + 4;
193  }
194 
195  __inline unsigned char * GetPayloadPtr() const { return m_packet + m_headerSize; }
196  __inline unsigned char & operator[](size_t offset) { return m_packet[m_headerSize + offset]; }
197  __inline unsigned const char & operator[](size_t offset) const { return m_packet[m_headerSize + offset]; }
198  __inline bool CopyPayload(const void * data, size_t size, size_t offset = 0)
199  {
200  if (!SetPayloadSize(offset + size))
201  return false;
202  memcpy(GetPayloadPtr()+offset, data, size);
203  return true;
204  }
205 
207  __inline unsigned char * GetVideoFrameData() const { return m_packet + m_headerSize + sizeof(PluginCodec_Video_FrameHeader); }
208 };
209 
210 
212 
213 typedef std::map<std::string, std::string> PluginCodec_OptionMapBase;
214 
216 {
217  public:
218  static unsigned String2Unsigned(const std::string & str)
219  {
220  return strtoul(str.c_str(), NULL, 10);
221  }
222 
223 
224  static void AppendUnsigned2String(unsigned value, std::string & str)
225  {
226  // Not very efficient, but really, really simple
227  if (value > 9)
228  AppendUnsigned2String(value/10, str);
229  str += (char)(value%10 + '0');
230  }
231 
232 
233  static void Unsigned2String(unsigned value, std::string & str)
234  {
235  str.clear();
236  AppendUnsigned2String(value,str);
237  }
238 
239 
240  static void Change(const char * value,
241  PluginCodec_OptionMapBase & original,
242  PluginCodec_OptionMapBase & changed,
243  const char * option)
244  {
245  PluginCodec_OptionMapBase::iterator it = original.find(option);
246  if (it != original.end() && it->second != value)
247  changed[option] = value;
248  }
249 
250 
251  static void Change(unsigned value,
252  PluginCodec_OptionMapBase & original,
253  PluginCodec_OptionMapBase & changed,
254  const char * option)
255  {
256  if (String2Unsigned(original[option]) != value)
257  Unsigned2String(value, changed[option]);
258  }
259 
260 
261  static void ClampMax(unsigned maximum,
262  PluginCodec_OptionMapBase & original,
263  PluginCodec_OptionMapBase & changed,
264  const char * option,
265  bool forceIfZero = false)
266  {
267  unsigned value = String2Unsigned(original[option]);
268  if (value > maximum || (forceIfZero && value == 0))
269  Unsigned2String(maximum, changed[option]);
270  }
271 
272 
273  static void ClampMin(unsigned minimum,
274  PluginCodec_OptionMapBase & original,
275  PluginCodec_OptionMapBase & changed,
276  const char * option)
277  {
278  unsigned value = String2Unsigned(original[option]);
279  if (value < minimum)
280  Unsigned2String(minimum, changed[option]);
281  }
282 
283 
284  static unsigned GetMacroBlocks(unsigned width, unsigned height)
285  {
286  return ((width+15)/16) * ((height+15)/16);
287  }
288 
289 
290  static bool ClampResolution(
291  unsigned & width,
292  unsigned & height,
293  unsigned & maxFrameSize)
294  {
295  static struct {
296  unsigned m_width;
297  unsigned m_height;
298  unsigned m_macroblocks;
299  } MaxVideoResolutions[] = {
300  #define OPAL_PLUGIN_CLAMPED_RESOLUTION(width, height) { width, height, ((width+15)/16) * ((height+15)/16) }
301  OPAL_PLUGIN_CLAMPED_RESOLUTION(2816, 2304),
302  OPAL_PLUGIN_CLAMPED_RESOLUTION(1920, 1080),
303  OPAL_PLUGIN_CLAMPED_RESOLUTION(1408, 1152),
311  };
312  static size_t const LastMaxVideoResolutions = sizeof(MaxVideoResolutions)/sizeof(MaxVideoResolutions[0]) - 1;
313 
314  size_t index = 0;
315 
316  if (maxFrameSize > 0) {
317  static unsigned const MinWidth = 4*16; // Four macroblocks wide
318  static unsigned const MinHeight = 3*16; // Three macroblocks high
319 
320  unsigned maxWidth = maxFrameSize*16*16/MinHeight;
321  unsigned maxHeight = maxFrameSize*16*16/MinWidth;
322 
323  // Check if total frame size below threshold total of macroblocks.
324  unsigned macroBlocks = GetMacroBlocks(width, height);
325  if (macroBlocks <= maxFrameSize &&
326  width >= MinWidth && width <= maxWidth &&
327  height >= MinHeight && height <= maxHeight)
328  return false;
329 
330  while (index < LastMaxVideoResolutions &&
331  (MaxVideoResolutions[index].m_macroblocks > maxFrameSize ||
332  MaxVideoResolutions[index].m_width > maxWidth ||
333  MaxVideoResolutions[index].m_height > maxHeight))
334  ++index;
335  }
336 
337  width = MaxVideoResolutions[index].m_width;
338  height = MaxVideoResolutions[index].m_height;
339  maxFrameSize = MaxVideoResolutions[index].m_macroblocks;
340  return true;
341  }
342 };
343 
344 
346 {
347  public:
348  PluginCodec_OptionMap(const char * const * * options = NULL)
349  {
350  if (options != NULL) {
351  for (const char * const * option = *options; *option != NULL; option += 2)
352  insert(value_type(option[0], option[1]));
353  }
354  }
355 
356 
357  unsigned GetUnsigned(const char * key, unsigned dflt = 0) const
358  {
359  const_iterator it = find(key);
360  return it == end() ? dflt : String2Unsigned(it->second);
361  }
362 
363  void SetUnsigned(unsigned value, const char * key)
364  {
365  Unsigned2String(value, operator[](key));
366  }
367 
368 
369  char ** GetOptions() const
370  {
371  char ** options = (char **)calloc(size()*2+1, sizeof(char *));
372  if (options == NULL) {
373  PTRACE(1, "Plugin", "Could not allocate new option lists.");
374  return NULL;
375  }
376 
377  char ** opt = options;
378  for (const_iterator it = begin(); it != end(); ++it) {
379  *opt++ = strdup(it->first.c_str());
380  *opt++ = strdup(it->second.c_str());
381  }
382 
383  return options;
384  }
385 };
386 
387 
388 template<typename NAME>
390 {
391  public:
392  typedef struct PluginCodec_Option const * const * OptionsTable;
394 
395  protected:
396  const char * m_formatName;
397  const char * m_payloadName;
398  unsigned m_payloadType;
399  const char * m_description;
400  unsigned m_maxBandwidth;
402  const void * m_h323CapabilityData;
403  unsigned m_flags;
405 
406  protected:
408  const char * formatName,
409  const char * payloadName,
410  const char * description,
411  unsigned maxBandwidth,
412  OptionsTable options)
413  : m_formatName(formatName)
414  , m_payloadName(payloadName)
415  , m_payloadType(0)
416  , m_description(description)
417  , m_maxBandwidth(maxBandwidth)
419  , m_h323CapabilityData(NULL)
421  , m_options(options)
422  {
423  }
424 
425  public:
427  {
428  }
429 
430 
431  __inline const char * GetFormatName() const { return this->m_formatName; }
432  __inline const char * GetPayloadName() const { return this->m_payloadName; }
433  __inline unsigned char GetPayloadType() const { return (unsigned char)this->m_payloadType; }
434  __inline const char * GetDescription() const { return this->m_description; }
435  __inline unsigned GetMaxBandwidth() const { return this->m_maxBandwidth; }
436  __inline unsigned char GetH323CapabilityType() const { return (unsigned char)this->m_h323CapabilityType; }
437  __inline const void * GetH323CapabilityData() const { return this->m_h323CapabilityData; }
438  __inline unsigned GetFlags() const { return this->m_flags; }
439  __inline const void * GetOptionsTable() const { return this->m_options; }
440 
441 
443  virtual bool IsValidForProtocol(const char * /*protocol*/)
444  {
445  return true;
446  }
447 
448 
450  bool AdjustOptions(void * parm, unsigned * parmLen, bool (PluginCodec_MediaFormat:: * adjuster)(OptionMap & original, OptionMap & changed))
451  {
452  if (parmLen == NULL || parm == NULL || *parmLen != sizeof(char ***)) {
453  PTRACE(1, "Plugin", "Invalid parameters to AdjustOptions.");
454  return false;
455  }
456 
457  OptionMap originalOptions((const char * const * *)parm);
458  OptionMap changedOptions;
459  if (!(this->*adjuster)(originalOptions, changedOptions)) {
460  PTRACE(1, "Plugin", "Could not normalise/customise options.");
461  return false;
462  }
463 
464  return (*(char ***)parm = changedOptions.GetOptions()) != NULL;
465  }
466 
467 
469  virtual bool ToNormalised(OptionMap & /*original*/, OptionMap & /*changed*/)
470  {
471  return true;
472  }
473 
474 
475  // Adjust codec specific options calculated from normalised options.
476  virtual bool ToCustomised(OptionMap & /*original*/, OptionMap & /*changed*/)
477  {
478  return true;
479  }
480 
481 
482  virtual void AdjustForVersion(unsigned version, const PluginCodec_Definition * /*definition*/)
483  {
484  if (version < PLUGIN_CODEC_VERSION_INTERSECT) {
485  for (PluginCodec_Option ** options = (PluginCodec_Option **)m_options; *options != NULL; ++options) {
486  if (strcmp((*options)->m_name, PLUGINCODEC_MEDIA_PACKETIZATIONS) == 0) {
487  *options = NULL;
488  break;
489  }
490  }
491  }
492  }
493 
494 
495  static void AdjustAllForVersion(unsigned version, const PluginCodec_Definition * definitions, size_t size)
496  {
497  while (size-- > 0) {
499  if (info != NULL)
500  info->AdjustForVersion(version, definitions);
501  ++definitions;
502  }
503  }
504 };
505 
506 
507 template<typename NAME>
509 {
510  protected:
512  unsigned m_bytesPerFrame;
513  unsigned m_sampleRate;
516 
517  public:
520 
522  const char * formatName,
523  const char * payloadName,
524  const char * description,
525  unsigned samplesPerFrame,
526  unsigned bytesPerFrame,
527  unsigned sampleRate,
528  OptionsTable options
529  ) : Parent(formatName, payloadName, description, bytesPerFrame*8 * samplesPerFrame*1000000/sampleRate, options)
530  , m_samplesPerFrame(samplesPerFrame)
531  , m_bytesPerFrame(bytesPerFrame)
532  , m_sampleRate(sampleRate)
533  , m_recommendedFramesPerPacket((50*sampleRate)/(1000*samplesPerFrame))
534  , m_maxFramesPerPacket((120*sampleRate)/(1000*samplesPerFrame))
535  {
536  this->m_flags = PluginCodec_MediaTypeAudio /* audio codec */
537  | PluginCodec_InputTypeRaw /* raw input data */
538  | PluginCodec_OutputTypeRaw; /* raw output data */
539 
540  }
541 
542 
543  __inline unsigned GetSamplesPerFrame() const { return this->m_samplesPerFrame; }
544  __inline unsigned GetBytesPerFrame() const { return this->m_bytesPerFrame; }
545  __inline unsigned GetSampleRate() const { return this->m_sampleRate; }
546  __inline unsigned GetFrameTime() const { return this->m_samplesPerFrame*1000000/this->m_sampleRate; }
547  __inline unsigned GetRecommendedFramesPerPacket() const { return this->m_recommendedFramesPerPacket; }
548  __inline unsigned GetMaxFramesPerPacket() const { return this->m_maxFramesPerPacket; }
549 };
550 
551 
552 template<typename NAME>
554 {
555  protected:
556  unsigned m_maxWidth;
557  unsigned m_maxHeight;
558 
559  public:
562 
564  const char * formatName,
565  const char * payloadName,
566  const char * description,
567  unsigned maxBandwidth,
568  OptionsTable options
569  ) : Parent(formatName, payloadName, description, maxBandwidth, options)
570  , m_maxWidth(1920)
571  , m_maxHeight(1200)
572  {
573  this->m_flags = PluginCodec_MediaTypeVideo /* audio codec */
574  | PluginCodec_InputTypeRTP /* raw input data */
575  | PluginCodec_OutputTypeRTP; /* raw output data */
576  }
577 
578 
579  __inline unsigned GetMaxWidth() const { return this->m_maxWidth; }
580  __inline unsigned GetMaxHeight() const { return this->m_maxHeight; }
581 };
582 
583 
585 
586 template<typename NAME>
588 {
589  protected:
591  : m_definition(defn)
592  , m_optionsSame(false)
593  , m_maxBitRate(defn->bitsPerSec)
594  , m_frameTime((defn->sampleRate/1000*defn->usPerFrame)/1000) // Odd way of calculation to avoid 32 bit integer overflow
595  {
596  PTRACE(3, "Plugin", "Codec created: \"" << defn->descr
597  << "\", \"" << defn->sourceFormat << "\" -> \"" << defn->destFormat << '"');
598  }
599 
600 
601  public:
602  virtual ~PluginCodec()
603  {
604  }
605 
606 
608  virtual bool Construct()
609  {
610  return true;
611  }
612 
613 
618  static bool Terminate()
619  {
620  return true;
621  }
622 
623 
625  virtual bool Transcode(const void * fromPtr,
626  unsigned & fromLen,
627  void * toPtr,
628  unsigned & toLen,
629  unsigned & flags) = 0;
630 
631 
633  virtual bool GetStatistics(char * /*bufferPtr*/, unsigned /*bufferSize*/)
634  {
635  return true;
636  }
637 
638 
640  virtual size_t GetOutputDataSize()
641  {
642  return 576-20-16; // Max safe MTU size (576 bytes as per RFC879) minus IP & UDP headers
643  }
644 
645 
652  virtual bool SetInstanceID(const char * /*idPtr*/, unsigned /*idLen*/)
653  {
654  return true;
655  }
656 
657 
659  virtual bool GetActiveOptions(PluginCodec_OptionMap & /*options*/)
660  {
661  return false;
662  }
663 
664 
666  virtual bool SetOptions(const char * const * options)
667  {
668  this->m_optionsSame = true;
669 
670  // get the media format options after adjustment from protocol negotiation
671  for (const char * const * option = options; *option != NULL; option += 2) {
672  if (!this->SetOption(option[0], option[1])) {
673  PTRACE(1, "Plugin", "Could not set option \"" << option[0] << "\" to \"" << option[1] << '"');
674  return false;
675  }
676  }
677 
678  if (this->m_optionsSame)
679  return true;
680 
681  return this->OnChangedOptions();
682  }
683 
684 
686  virtual bool OnChangedOptions()
687  {
688  return true;
689  }
690 
691 
693  virtual bool SetOption(const char * optionName, const char * optionValue)
694  {
695  if (strcasecmp(optionName, PLUGINCODEC_OPTION_TARGET_BIT_RATE) == 0)
696  return this->SetOptionUnsigned(this->m_maxBitRate, optionValue, 1, this->m_definition->bitsPerSec);
697 
698  if (strcasecmp(optionName, PLUGINCODEC_OPTION_FRAME_TIME) == 0)
699  return this->SetOptionUnsigned(this->m_frameTime, optionValue,
700  this->m_definition->sampleRate/1000, this->m_definition->sampleRate); // 1ms to 1 second
701 
702  return true;
703  }
704 
705 
706  template <typename T>
707  bool SetOptionUnsigned(T & oldValue, const char * optionValue, unsigned minimum, unsigned maximum = UINT_MAX)
708  {
709  unsigned newValue = oldValue;
710  if (!this->SetOptionUnsigned(newValue, optionValue, minimum, maximum))
711  return false;
712  oldValue = (T)newValue;
713  return true;
714  }
715 
716 
717  bool SetOptionUnsigned(unsigned & oldValue, const char * optionValue, unsigned minimum, unsigned maximum = UINT_MAX)
718  {
719  char * end;
720  unsigned newValue = strtoul(optionValue, &end, 10);
721  if (*end != '\0')
722  return false;
723 
724  if (newValue < minimum)
725  newValue = minimum;
726  else if (newValue > maximum)
727  newValue = maximum;
728 
729  if (oldValue != newValue) {
730  oldValue = newValue;
731  this->m_optionsSame = false;
732  }
733 
734  return true;
735  }
736 
737 
738  template <typename T>
739  bool SetOptionBoolean(T & oldValue, const char * optionValue)
740  {
741  bool opt = oldValue != 0;
742  if (!this->SetOptionBoolean(opt, optionValue))
743  return false;
744  oldValue = (T)opt;
745  return true;
746  }
747 
748 
749  bool SetOptionBoolean(bool & oldValue, const char * optionValue)
750  {
751  bool newValue;
752  if ( strcasecmp(optionValue, "0") == 0 ||
753  strcasecmp(optionValue, "n") == 0 ||
754  strcasecmp(optionValue, "f") == 0 ||
755  strcasecmp(optionValue, "no") == 0 ||
756  strcasecmp(optionValue, "false") == 0)
757  newValue = false;
758  else if (strcasecmp(optionValue, "1") == 0 ||
759  strcasecmp(optionValue, "y") == 0 ||
760  strcasecmp(optionValue, "t") == 0 ||
761  strcasecmp(optionValue, "yes") == 0 ||
762  strcasecmp(optionValue, "true") == 0)
763  newValue = true;
764  else
765  return false;
766 
767  if (oldValue != newValue) {
768  oldValue = newValue;
769  this->m_optionsSame = false;
770  }
771 
772  return true;
773  }
774 
775 
776  bool SetOptionBit(int & oldValue, unsigned bit, const char * optionValue)
777  {
778  return this->SetOptionBit((unsigned &)oldValue, bit, optionValue);
779  }
780 
781 
782  bool SetOptionBit(unsigned & oldValue, unsigned bit, const char * optionValue)
783  {
784  bool newValue;
785  if (strcmp(optionValue, "0") == 0)
786  newValue = false;
787  else if (strcmp(optionValue, "1") == 0)
788  newValue = true;
789  else
790  return false;
791 
792  if (((oldValue&bit) != 0) != newValue) {
793  if (newValue)
794  oldValue |= bit;
795  else
796  oldValue &= ~bit;
797  this->m_optionsSame = false;
798  }
799 
800  return true;
801  }
802 
803 
804  template <class CodecClass> static void * Create_s(const PluginCodec_Definition * defn)
805  {
806  CodecClass * codec = new CodecClass(defn);
807  if (codec != NULL && codec->Construct())
808  return codec;
809 
810  PTRACE(1, "Plugin", "Could not open codec, no context being returned.");
811  delete codec;
812  return NULL;
813  }
814 
815 
816  static void Destroy_s(const PluginCodec_Definition * /*defn*/, void * context)
817  {
818  delete (PluginCodec *)context;
819  }
820 
821 
822  static int Transcode_s(const PluginCodec_Definition * /*defn*/,
823  void * context,
824  const void * fromPtr,
825  unsigned * fromLen,
826  void * toPtr,
827  unsigned * toLen,
828  unsigned int * flags)
829  {
830  if (context != NULL && fromPtr != NULL && fromLen != NULL && toPtr != NULL && toLen != NULL && flags != NULL)
831  return ((PluginCodec *)context)->Transcode(fromPtr, *fromLen, toPtr, *toLen, *flags);
832 
833  PTRACE(1, "Plugin", "Invalid parameter to Transcode.");
834  return false;
835  }
836 
837 
838  static int GetOutputDataSize_s(const PluginCodec_Definition *, void * context, const char *, void *, unsigned *)
839  {
840  return context != NULL ? ((PluginCodec *)context)->GetOutputDataSize() : 0;
841  }
842 
844 
845  static int ToNormalised_s(const PluginCodec_Definition * defn, void *, const char *, void * parm, unsigned * len)
846  {
847  return defn->userData != NULL ? ((MediaFormat *)defn->userData)->AdjustOptions(parm, len, &MediaFormat::ToNormalised) : -1;
848  }
849 
850 
851  static int ToCustomised_s(const PluginCodec_Definition * defn, void *, const char *, void * parm, unsigned * len)
852  {
853  return defn->userData != NULL ? ((MediaFormat *)defn->userData)->AdjustOptions(parm, len, &MediaFormat::ToCustomised) : -1;
854  }
855 
856 
857  static int GetActiveOptions_s(const PluginCodec_Definition *, void * context, const char *, void * parm, unsigned * parmLen)
858  {
859  if (context == NULL || parmLen == NULL || parm == NULL || *parmLen != sizeof(char ***)) {
860  PTRACE(1, "Plugin", "Invalid parameters to GetActiveOptions.");
861  return false;
862  }
863 
864  PluginCodec_OptionMap activeOptions;
865  if (!((PluginCodec *)context)->GetActiveOptions(activeOptions))
866  return false;
867 
868  return (*(char ***)parm = activeOptions.GetOptions()) != NULL;
869  }
870 
871 
872  static int FreeOptions_s(const PluginCodec_Definition *, void *, const char *, void * parm, unsigned * len)
873  {
874  if (parm == NULL || len == NULL || *len != sizeof(char ***))
875  return false;
876 
877  char ** strings = (char **)parm;
878  for (char ** string = strings; *string != NULL; string++)
879  free(*string);
880  free(strings);
881  return true;
882  }
883 
884 
885  static int GetOptions_s(const struct PluginCodec_Definition * codec, void *, const char *, void * parm, unsigned * len)
886  {
887  if (parm == NULL || len == NULL || *len != sizeof(struct PluginCodec_Option **))
888  return false;
889 
890  *(const void **)parm = codec->userData != NULL ? ((MediaFormat *)codec->userData)->GetOptionsTable() : NULL;
891  *len = 0;
892  return true;
893  }
894 
895 
896  static int SetOptions_s(const PluginCodec_Definition *, void * context, const char *, void * parm, unsigned * len)
897  {
898  PluginCodec * codec = (PluginCodec *)context;
899  return len != NULL && *len == sizeof(const char **) && parm != NULL &&
900  codec != NULL && codec->SetOptions((const char * const *)parm);
901  }
902 
903  static int ValidForProtocol_s(const PluginCodec_Definition * defn, void *, const char *, void * parm, unsigned * len)
904  {
905  return len != NULL && *len == sizeof(const char *) && parm != NULL && defn->userData != NULL &&
906  ((MediaFormat *)defn->userData)->IsValidForProtocol((const char *)parm);
907  }
908 
909  static int SetInstanceID_s(const PluginCodec_Definition *, void * context, const char *, void * parm, unsigned * len)
910  {
911  PluginCodec * codec = (PluginCodec *)context;
912  return len != NULL && parm != NULL &&
913  codec != NULL && codec->SetInstanceID((const char *)parm, *len);
914  }
915 
916  static int GetStatistics_s(const PluginCodec_Definition *, void * context, const char *, void * parm, unsigned * len)
917  {
918  PluginCodec * codec = (PluginCodec *)context;
919  return len != NULL && parm != NULL &&
920  codec != NULL && codec->GetStatistics((char *)parm, *len);
921  }
922 
923  static int Terminate_s(const PluginCodec_Definition *, void * context, const char *, void *, unsigned *)
924  {
925  PluginCodec * codec = (PluginCodec *)context;
926  return codec != NULL && codec->Terminate();
927  }
928 
930  {
931  static PluginCodec_ControlDefn ControlsTable[] = {
944  { NULL }
945  };
946  return ControlsTable;
947  }
948 
949  protected:
951 
953  unsigned m_maxBitRate;
954  unsigned m_frameTime;
955 };
956 
957 
959 
960 template<typename NAME>
961 class PluginVideoCodec : public PluginCodec<NAME>
962 {
964 
965  public:
966  enum {
967  DefaultWidth = 352, // CIF size
971  };
972 
973 
975  : BaseClass(defn)
976  {
977  }
978 
979 
980  virtual size_t GetRawFrameSize(unsigned width, unsigned height)
981  {
982  return width*height*3/2; // YUV420P
983  }
984 };
985 
986 
988 
989 template<typename NAME>
991 {
993 
994  protected:
995  unsigned m_width;
996  unsigned m_height;
997  unsigned m_maxRTPSize;
998  unsigned m_tsto;
1000 
1001  public:
1003  : BaseClass(defn)
1007  , m_tsto(31)
1008  , m_keyFramePeriod(0) // Indicates auto/default
1009  {
1010  }
1011 
1012 
1013  virtual bool SetOption(const char * optionName, const char * optionValue)
1014  {
1015  if (strcasecmp(optionName, PLUGINCODEC_OPTION_FRAME_WIDTH) == 0)
1016  return this->SetOptionUnsigned(this->m_width, optionValue, 16, BaseClass::MaxWidth);
1017 
1018  if (strcasecmp(optionName, PLUGINCODEC_OPTION_FRAME_HEIGHT) == 0)
1019  return this->SetOptionUnsigned(this->m_height, optionValue, 16, BaseClass::MaxHeight);
1020 
1021  if (strcasecmp(optionName, PLUGINCODEC_OPTION_MAX_TX_PACKET_SIZE) == 0)
1022  return this->SetOptionUnsigned(this->m_maxRTPSize, optionValue, 256, 8192);
1023 
1024  if (strcasecmp(optionName, PLUGINCODEC_OPTION_TEMPORAL_SPATIAL_TRADE_OFF) == 0)
1025  return this->SetOptionUnsigned(this->m_tsto, optionValue, 1, 31);
1026 
1027  if (strcasecmp(optionName, PLUGINCODEC_OPTION_TX_KEY_FRAME_PERIOD) == 0)
1028  return this->SetOptionUnsigned(this->m_keyFramePeriod, optionValue, 0);
1029 
1030  // Base class sets bit rate and frame time
1031  return BaseClass::SetOption(optionName, optionValue);
1032  }
1033 
1034 
1036  virtual bool GetActiveOptions(PluginCodec_OptionMap & options)
1037  {
1039  return true;
1040  }
1041 
1042 
1043  virtual size_t GetPacketSpace(const PluginCodec_RTP & rtp, size_t total)
1044  {
1045  size_t space = rtp.GetMaxSize();
1046  if (space > this->m_maxRTPSize)
1047  space = this->m_maxRTPSize;
1048  space -= rtp.GetHeaderSize();
1049  if (space > total)
1050  space = total;
1051  return space;
1052  }
1053 };
1054 
1055 
1057 
1058 template<typename NAME>
1060 {
1062 
1063  protected:
1065 
1066  public:
1068  : BaseClass(defn)
1070  {
1071  }
1072 
1073 
1074  virtual size_t GetOutputDataSize()
1075  {
1076  return this->m_outputSize;
1077  }
1078 
1079 
1080  virtual bool CanOutputImage(unsigned width, unsigned height, PluginCodec_RTP & rtp, unsigned & flags)
1081  {
1082  size_t newSize = this->GetRawFrameSize(width, height) + sizeof(PluginCodec_Video_FrameHeader) + rtp.GetHeaderSize();
1083  if (newSize > rtp.GetMaxSize() || !rtp.SetPayloadSize(newSize)) {
1084  m_outputSize = newSize;
1086  return false;
1087  }
1088 
1089  PluginCodec_Video_FrameHeader * videoHeader = rtp.GetVideoHeader();
1090  videoHeader->x = 0;
1091  videoHeader->y = 0;
1092  videoHeader->width = width;
1093  videoHeader->height = height;
1094 
1096  rtp.SetMarker(true);
1097  return true;
1098  }
1099 
1100 
1102  {
1103  unsigned m_width;
1104  unsigned m_height;
1105  unsigned m_raster;
1106  unsigned char * m_source;
1107  unsigned char * m_destination;
1108 
1109  void Copy()
1110  {
1111  for (unsigned y = 0; y < m_height; ++y) {
1112  memcpy(m_destination, m_source, m_width);
1113  this->m_source += m_raster;
1114  this->m_destination += m_width;
1115  }
1116  }
1117  };
1118 
1119  virtual unsigned OutputImage(unsigned char * planes[3], int raster[3],
1120  unsigned width, unsigned height, PluginCodec_RTP & rtp, unsigned & flags)
1121  {
1122  if (!CanOutputImage(width, height, rtp, flags))
1123  return 0;
1124 
1125  size_t ySize = width*height;
1126  size_t uvSize = ySize/4;
1127  if (planes[1] == planes[0]+ySize && planes[2] == planes[1]+uvSize)
1128  memcpy(rtp.GetVideoFrameData(), planes[0], ySize+uvSize*2);
1129  else {
1130  OutputImagePlaneInfo planeInfo[3] = {
1131  { width, height, raster[0], planes[0], rtp.GetVideoFrameData() },
1132  { width/2, height/2, raster[1], planes[1], planeInfo[0].m_destination + ySize },
1133  { width/2, height/2, raster[2], planes[2], planeInfo[1].m_destination + uvSize }
1134  };
1135 
1136  for (unsigned plane = 0; plane < 3; ++plane)
1137  planeInfo[plane].Copy();
1138  }
1139 
1140  return rtp.GetPacketSize();
1141  }
1142 };
1143 
1144 
1146 
1148 #define PLUGINCODEC_AUDIO_CODEC_CXX(MediaFormat, \
1149  EncoderClass, \
1150  DecoderClass \
1151  ) \
1152  PLUGINCODEC_CODEC_PAIR(MediaFormat.GetFormatName(), \
1153  MediaFormat.GetPayloadName(), \
1154  MediaFormat.GetDescription(), \
1155  MediaFormat.GetSampleRate(), \
1156  MediaFormat.GetMaxBandwidth(), \
1157  MediaFormat.GetFrameTime(), \
1158  MediaFormat.GetSamplesPerFrame(), \
1159  MediaFormat.GetBytesPerFrame(), \
1160  MediaFormat.GetRecommendedFramesPerPacket(), \
1161  MediaFormat.GetMaxFramesPerPacket(), \
1162  MediaFormat.GetPayloadType(), \
1163  MediaFormat.GetH323CapabilityType(), \
1164  MediaFormat.GetH323CapabilityData(), \
1165  EncoderClass::Create_s<EncoderClass>, \
1166  EncoderClass::Destroy_s, \
1167  EncoderClass::Transcode_s, \
1168  DecoderClass::Create_s<DecoderClass>, \
1169  DecoderClass::Destroy_s, \
1170  DecoderClass::Transcode_s, \
1171  DecoderClass::GetControls(), /* Note doesn't matter if encoder or decoder */ \
1172  MediaFormat.GetFlags(), \
1173  PLUGINCODEC_RAW_AUDIO, \
1174  &MediaFormat)
1175 
1177 #define PLUGINCODEC_VIDEO_CODEC_CXX(MediaFormat, \
1178  EncoderClass, \
1179  DecoderClass \
1180  ) \
1181  PLUGINCODEC_CODEC_PAIR(MediaFormat.GetFormatName(), \
1182  MediaFormat.GetPayloadName(), \
1183  MediaFormat.GetDescription(), \
1184  PLUGINCODEC_VIDEO_CLOCK, \
1185  MediaFormat.GetMaxBandwidth(), \
1186  1000000/PLUGINCODEC_MAX_FRAME_RATE, \
1187  MediaFormat.GetMaxWidth(), \
1188  MediaFormat.GetMaxHeight(), \
1189  0,PLUGINCODEC_MAX_FRAME_RATE, \
1190  MediaFormat.GetPayloadType(), \
1191  MediaFormat.GetH323CapabilityType(), \
1192  MediaFormat.GetH323CapabilityData(), \
1193  EncoderClass::Create_s<EncoderClass>, \
1194  EncoderClass::Destroy_s, \
1195  EncoderClass::Transcode_s, \
1196  DecoderClass::Create_s<DecoderClass>, \
1197  DecoderClass::Destroy_s, \
1198  DecoderClass::Transcode_s, \
1199  DecoderClass::GetControls(), /* Note doesn't matter if encoder or decoder */ \
1200  MediaFormat.GetFlags(), \
1201  PLUGINCODEC_RAW_VIDEO, \
1202  &MediaFormat)
1203 
1204 
1205 #define PLUGIN_CODEC_IMPLEMENT_CXX(NAME, table) \
1206  extern "C" { \
1207  PLUGIN_CODEC_IMPLEMENT(NAME) \
1208  PLUGIN_CODEC_DLL_API struct PluginCodec_Definition * PLUGIN_CODEC_GET_CODEC_FN(unsigned * count, unsigned version) { \
1209  if (version < PLUGIN_CODEC_VERSION_OPTIONS) return NULL; \
1210  *count = sizeof(table)/sizeof(struct PluginCodec_Definition); \
1211  PluginCodec_MediaFormat<NAME>::AdjustAllForVersion(version, table, *count); \
1212  return table; \
1213  } \
1214  }
1215 
1216 
1217 #endif // OPAL_CODEC_OPALPLUGIN_HPP