OPAL  Version 3.12.9
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: 31090 $
35  * $Author: rjongbloed $
36  * $Date: 2013-12-15 14:50:42 +1100 (Sun, 15 Dec 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(4096, 2304), // 36864 - Cinema 16:9 (H.264 Level 5.1 - 5.2)
302  OPAL_PLUGIN_CLAMPED_RESOLUTION(4096, 2160), // 34560 - DCI HD
303  OPAL_PLUGIN_CLAMPED_RESOLUTION(3840, 2160), // 32400 - 4k UHDTV
304  OPAL_PLUGIN_CLAMPED_RESOLUTION(3672, 1536), // 22080 - Cinema 2.39:1 (H.264 Level 5)
305  OPAL_PLUGIN_CLAMPED_RESOLUTION(2048, 1080), // 8704 - 2Kx1080 (H.264 Level 4.2)
306  OPAL_PLUGIN_CLAMPED_RESOLUTION(2048, 1024), // 8192 - 2Kx1K (H.264 Level 4 - 4.1)
307  OPAL_PLUGIN_CLAMPED_RESOLUTION(1920, 1080), // 8100 - 1080p HD
308  OPAL_PLUGIN_CLAMPED_RESOLUTION(1408, 1152), // 6336 - 16CIF
309  OPAL_PLUGIN_CLAMPED_RESOLUTION(1280, 1024), // 5120 - SXGA (H.264 Level 3.2)
310  OPAL_PLUGIN_CLAMPED_RESOLUTION(1280, 720), // 3600 - 720p HD (H.264 Level 3.1)
311  OPAL_PLUGIN_CLAMPED_RESOLUTION( 720, 576), // 1620 - 625 SD (H.264 Level 2.2 - 3)
312  OPAL_PLUGIN_CLAMPED_RESOLUTION( 704, 576), // 1584 - 4CIF
313  OPAL_PLUGIN_CLAMPED_RESOLUTION( 640, 480), // 1200 - VGA
314  OPAL_PLUGIN_CLAMPED_RESOLUTION( 352, 576), // 792 - 625 HHR (H.264 Level 2.1)
315  OPAL_PLUGIN_CLAMPED_RESOLUTION( 352, 288), // 396 - CIF (H.264 Level 1.1 - 2)
316  OPAL_PLUGIN_CLAMPED_RESOLUTION( 320, 240), // 300 - QVGA
317  OPAL_PLUGIN_CLAMPED_RESOLUTION( 176, 144), // 99 - QCIF (H.264 Level 1, 1b)
318  OPAL_PLUGIN_CLAMPED_RESOLUTION( 128, 96) // 48 - SQCIF
319  };
320  static size_t const LastMaxVideoResolutions = sizeof(MaxVideoResolutions)/sizeof(MaxVideoResolutions[0]) - 1;
321 
322  size_t index = 0;
323 
324  if (maxFrameSize > 0) {
325  static unsigned const MinWidth = 4*16; // Four macroblocks wide
326  static unsigned const MinHeight = 3*16; // Three macroblocks high
327 
328  unsigned maxWidth = maxFrameSize*16*16/MinHeight;
329  unsigned maxHeight = maxFrameSize*16*16/MinWidth;
330 
331  // Check if total frame size below threshold total of macroblocks.
332  unsigned macroBlocks = GetMacroBlocks(width, height);
333  if (macroBlocks <= maxFrameSize &&
334  width >= MinWidth && width <= maxWidth &&
335  height >= MinHeight && height <= maxHeight)
336  return false;
337 
338  while (index < LastMaxVideoResolutions &&
339  (MaxVideoResolutions[index].m_macroblocks > maxFrameSize ||
340  MaxVideoResolutions[index].m_width > maxWidth ||
341  MaxVideoResolutions[index].m_height > maxHeight))
342  ++index;
343  }
344 
345  width = MaxVideoResolutions[index].m_width;
346  height = MaxVideoResolutions[index].m_height;
347  maxFrameSize = MaxVideoResolutions[index].m_macroblocks;
348  return true;
349  }
350 };
351 
352 
354 {
355  public:
356  PluginCodec_OptionMap(const char * const * * options = NULL)
357  {
358  if (options != NULL) {
359  for (const char * const * option = *options; *option != NULL; option += 2)
360  insert(value_type(option[0], option[1]));
361  }
362  }
363 
364 
365  unsigned GetUnsigned(const char * key, unsigned dflt = 0) const
366  {
367  const_iterator it = find(key);
368  return it == end() ? dflt : String2Unsigned(it->second);
369  }
370 
371  void SetUnsigned(unsigned value, const char * key)
372  {
373  Unsigned2String(value, operator[](key));
374  }
375 
376 
377  char ** GetOptions() const
378  {
379  char ** options = (char **)calloc(size()*2+1, sizeof(char *));
380  if (options == NULL) {
381  PTRACE(1, "Plugin", "Could not allocate new option lists.");
382  return NULL;
383  }
384 
385  char ** opt = options;
386  for (const_iterator it = begin(); it != end(); ++it) {
387  *opt++ = strdup(it->first.c_str());
388  *opt++ = strdup(it->second.c_str());
389  }
390 
391  return options;
392  }
393 };
394 
395 
396 template<typename NAME>
398 {
399  public:
400  typedef struct PluginCodec_Option const * const * OptionsTable;
402 
403  protected:
404  const char * m_formatName;
405  const char * m_payloadName;
406  unsigned m_payloadType;
407  const char * m_description;
408  unsigned m_maxBandwidth;
410  const void * m_h323CapabilityData;
411  unsigned m_flags;
413 
414  protected:
416  const char * formatName,
417  const char * payloadName,
418  const char * description,
419  unsigned maxBandwidth,
420  OptionsTable options)
421  : m_formatName(formatName)
422  , m_payloadName(payloadName)
423  , m_payloadType(0)
424  , m_description(description)
425  , m_maxBandwidth(maxBandwidth)
427  , m_h323CapabilityData(NULL)
429  , m_options(options)
430  {
431  }
432 
433  public:
435  {
436  }
437 
438 
439  __inline const char * GetFormatName() const { return this->m_formatName; }
440  __inline const char * GetPayloadName() const { return this->m_payloadName; }
441  __inline unsigned char GetPayloadType() const { return (unsigned char)this->m_payloadType; }
442  __inline const char * GetDescription() const { return this->m_description; }
443  __inline unsigned GetMaxBandwidth() const { return this->m_maxBandwidth; }
444  __inline unsigned char GetH323CapabilityType() const { return (unsigned char)this->m_h323CapabilityType; }
445  __inline const void * GetH323CapabilityData() const { return this->m_h323CapabilityData; }
446  __inline unsigned GetFlags() const { return this->m_flags; }
447  __inline const void * GetOptionsTable() const { return this->m_options; }
448 
449 
451  virtual bool IsValidForProtocol(const char * /*protocol*/)
452  {
453  return true;
454  }
455 
456 
458  bool AdjustOptions(void * parm, unsigned * parmLen, bool (PluginCodec_MediaFormat:: * adjuster)(OptionMap & original, OptionMap & changed))
459  {
460  if (parmLen == NULL || parm == NULL || *parmLen != sizeof(char ***)) {
461  PTRACE(1, "Plugin", "Invalid parameters to AdjustOptions.");
462  return false;
463  }
464 
465  OptionMap originalOptions((const char * const * *)parm);
466  OptionMap changedOptions;
467  if (!(this->*adjuster)(originalOptions, changedOptions)) {
468  PTRACE(1, "Plugin", "Could not normalise/customise options.");
469  return false;
470  }
471 
472  return (*(char ***)parm = changedOptions.GetOptions()) != NULL;
473  }
474 
475 
477  virtual bool ToNormalised(OptionMap & /*original*/, OptionMap & /*changed*/)
478  {
479  return true;
480  }
481 
482 
483  // Adjust codec specific options calculated from normalised options.
484  virtual bool ToCustomised(OptionMap & /*original*/, OptionMap & /*changed*/)
485  {
486  return true;
487  }
488 
489 
490  virtual void AdjustForVersion(unsigned version, const PluginCodec_Definition * /*definition*/)
491  {
492  if (version < PLUGIN_CODEC_VERSION_INTERSECT) {
493  for (PluginCodec_Option ** options = (PluginCodec_Option **)m_options; *options != NULL; ++options) {
494  if (strcmp((*options)->m_name, PLUGINCODEC_MEDIA_PACKETIZATIONS) == 0) {
495  *options = NULL;
496  break;
497  }
498  }
499  }
500  }
501 
502 
503  static void AdjustAllForVersion(unsigned version, const PluginCodec_Definition * definitions, size_t size)
504  {
505  while (size-- > 0) {
507  if (info != NULL)
508  info->AdjustForVersion(version, definitions);
509  ++definitions;
510  }
511  }
512 };
513 
514 
515 template<typename NAME>
517 {
518  protected:
520  unsigned m_bytesPerFrame;
521  unsigned m_sampleRate;
524 
525  public:
528 
530  const char * formatName,
531  const char * payloadName,
532  const char * description,
533  unsigned samplesPerFrame,
534  unsigned bytesPerFrame,
535  unsigned sampleRate,
536  OptionsTable options
537  ) : Parent(formatName, payloadName, description, bytesPerFrame*8 * samplesPerFrame*1000000/sampleRate, options)
538  , m_samplesPerFrame(samplesPerFrame)
539  , m_bytesPerFrame(bytesPerFrame)
540  , m_sampleRate(sampleRate)
541  , m_recommendedFramesPerPacket((50*sampleRate)/(1000*samplesPerFrame))
542  , m_maxFramesPerPacket((120*sampleRate)/(1000*samplesPerFrame))
543  {
544  this->m_flags = PluginCodec_MediaTypeAudio /* audio codec */
545  | PluginCodec_InputTypeRaw /* raw input data */
546  | PluginCodec_OutputTypeRaw; /* raw output data */
547 
548  }
549 
550 
551  __inline unsigned GetSamplesPerFrame() const { return this->m_samplesPerFrame; }
552  __inline unsigned GetBytesPerFrame() const { return this->m_bytesPerFrame; }
553  __inline unsigned GetSampleRate() const { return this->m_sampleRate; }
554  __inline unsigned GetFrameTime() const { return this->m_samplesPerFrame*1000000/this->m_sampleRate; }
555  __inline unsigned GetRecommendedFramesPerPacket() const { return this->m_recommendedFramesPerPacket; }
556  __inline unsigned GetMaxFramesPerPacket() const { return this->m_maxFramesPerPacket; }
557 };
558 
559 
560 template<typename NAME>
562 {
563  protected:
564  unsigned m_maxWidth;
565  unsigned m_maxHeight;
566 
567  public:
570 
572  const char * formatName,
573  const char * payloadName,
574  const char * description,
575  unsigned maxBandwidth,
576  OptionsTable options
577  ) : Parent(formatName, payloadName, description, maxBandwidth, options)
578  , m_maxWidth(1920)
579  , m_maxHeight(1200)
580  {
581  this->m_flags = PluginCodec_MediaTypeVideo /* audio codec */
582  | PluginCodec_InputTypeRTP /* raw input data */
583  | PluginCodec_OutputTypeRTP; /* raw output data */
584  }
585 
586 
587  __inline unsigned GetMaxWidth() const { return this->m_maxWidth; }
588  __inline unsigned GetMaxHeight() const { return this->m_maxHeight; }
589 };
590 
591 
593 
594 template<typename NAME>
596 {
597  protected:
599  : m_definition(defn)
600  , m_optionsSame(false)
601  , m_maxBitRate(defn->bitsPerSec)
602  , m_frameTime((defn->sampleRate/1000*defn->usPerFrame)/1000) // Odd way of calculation to avoid 32 bit integer overflow
603  {
604  PTRACE(3, "Plugin", "Codec created: \"" << defn->descr
605  << "\", \"" << defn->sourceFormat << "\" -> \"" << defn->destFormat << '"');
606  }
607 
608 
609  public:
610  virtual ~PluginCodec()
611  {
612  }
613 
614 
616  virtual bool Construct()
617  {
618  return true;
619  }
620 
621 
626  static bool Terminate()
627  {
628  return true;
629  }
630 
631 
633  virtual bool Transcode(const void * fromPtr,
634  unsigned & fromLen,
635  void * toPtr,
636  unsigned & toLen,
637  unsigned & flags) = 0;
638 
639 
641  virtual bool GetStatistics(char * /*bufferPtr*/, unsigned /*bufferSize*/)
642  {
643  return true;
644  }
645 
646 
648  virtual size_t GetOutputDataSize()
649  {
650  return 576-20-16; // Max safe MTU size (576 bytes as per RFC879) minus IP & UDP headers
651  }
652 
653 
660  virtual bool SetInstanceID(const char * /*idPtr*/, unsigned /*idLen*/)
661  {
662  return true;
663  }
664 
665 
667  virtual bool GetActiveOptions(PluginCodec_OptionMap & /*options*/)
668  {
669  return false;
670  }
671 
672 
674  virtual bool SetOptions(const char * const * options)
675  {
676  this->m_optionsSame = true;
677 
678  // get the media format options after adjustment from protocol negotiation
679  for (const char * const * option = options; *option != NULL; option += 2) {
680  if (!this->SetOption(option[0], option[1])) {
681  PTRACE(1, "Plugin", "Could not set option \"" << option[0] << "\" to \"" << option[1] << '"');
682  return false;
683  }
684  }
685 
686  if (this->m_optionsSame)
687  return true;
688 
689  return this->OnChangedOptions();
690  }
691 
692 
694  virtual bool OnChangedOptions()
695  {
696  return true;
697  }
698 
699 
701  virtual bool SetOption(const char * optionName, const char * optionValue)
702  {
703  if (strcasecmp(optionName, PLUGINCODEC_OPTION_TARGET_BIT_RATE) == 0)
704  return this->SetOptionUnsigned(this->m_maxBitRate, optionValue, 1, this->m_definition->bitsPerSec);
705 
706  if (strcasecmp(optionName, PLUGINCODEC_OPTION_FRAME_TIME) == 0)
707  return this->SetOptionUnsigned(this->m_frameTime, optionValue,
708  this->m_definition->sampleRate/1000, this->m_definition->sampleRate); // 1ms to 1 second
709 
710  return true;
711  }
712 
713 
714  template <typename T>
715  bool SetOptionUnsigned(T & oldValue, const char * optionValue, unsigned minimum, unsigned maximum = UINT_MAX)
716  {
717  unsigned newValue = oldValue;
718  if (!this->SetOptionUnsigned(newValue, optionValue, minimum, maximum))
719  return false;
720  oldValue = (T)newValue;
721  return true;
722  }
723 
724 
725  bool SetOptionUnsigned(unsigned & oldValue, const char * optionValue, unsigned minimum, unsigned maximum = UINT_MAX)
726  {
727  char * end;
728  unsigned newValue = strtoul(optionValue, &end, 10);
729  if (*end != '\0')
730  return false;
731 
732  if (newValue < minimum)
733  newValue = minimum;
734  else if (newValue > maximum)
735  newValue = maximum;
736 
737  if (oldValue != newValue) {
738  oldValue = newValue;
739  this->m_optionsSame = false;
740  }
741 
742  return true;
743  }
744 
745 
746  template <typename T>
747  bool SetOptionBoolean(T & oldValue, const char * optionValue)
748  {
749  bool opt = oldValue != 0;
750  if (!this->SetOptionBoolean(opt, optionValue))
751  return false;
752  oldValue = (T)opt;
753  return true;
754  }
755 
756 
757  bool SetOptionBoolean(bool & oldValue, const char * optionValue)
758  {
759  bool newValue;
760  if ( strcasecmp(optionValue, "0") == 0 ||
761  strcasecmp(optionValue, "n") == 0 ||
762  strcasecmp(optionValue, "f") == 0 ||
763  strcasecmp(optionValue, "no") == 0 ||
764  strcasecmp(optionValue, "false") == 0)
765  newValue = false;
766  else if (strcasecmp(optionValue, "1") == 0 ||
767  strcasecmp(optionValue, "y") == 0 ||
768  strcasecmp(optionValue, "t") == 0 ||
769  strcasecmp(optionValue, "yes") == 0 ||
770  strcasecmp(optionValue, "true") == 0)
771  newValue = true;
772  else
773  return false;
774 
775  if (oldValue != newValue) {
776  oldValue = newValue;
777  this->m_optionsSame = false;
778  }
779 
780  return true;
781  }
782 
783 
784  bool SetOptionBit(int & oldValue, unsigned bit, const char * optionValue)
785  {
786  return this->SetOptionBit((unsigned &)oldValue, bit, optionValue);
787  }
788 
789 
790  bool SetOptionBit(unsigned & oldValue, unsigned bit, const char * optionValue)
791  {
792  bool newValue;
793  if (strcmp(optionValue, "0") == 0)
794  newValue = false;
795  else if (strcmp(optionValue, "1") == 0)
796  newValue = true;
797  else
798  return false;
799 
800  if (((oldValue&bit) != 0) != newValue) {
801  if (newValue)
802  oldValue |= bit;
803  else
804  oldValue &= ~bit;
805  this->m_optionsSame = false;
806  }
807 
808  return true;
809  }
810 
811 
812  template <class CodecClass> static void * Create_s(const PluginCodec_Definition * defn)
813  {
814  CodecClass * codec = new CodecClass(defn);
815  if (codec != NULL && codec->Construct())
816  return codec;
817 
818  PTRACE(1, "Plugin", "Could not open codec, no context being returned.");
819  delete codec;
820  return NULL;
821  }
822 
823 
824  static void Destroy_s(const PluginCodec_Definition * /*defn*/, void * context)
825  {
826  delete (PluginCodec *)context;
827  }
828 
829 
830  static int Transcode_s(const PluginCodec_Definition * /*defn*/,
831  void * context,
832  const void * fromPtr,
833  unsigned * fromLen,
834  void * toPtr,
835  unsigned * toLen,
836  unsigned int * flags)
837  {
838  if (context != NULL && fromPtr != NULL && fromLen != NULL && toPtr != NULL && toLen != NULL && flags != NULL)
839  return ((PluginCodec *)context)->Transcode(fromPtr, *fromLen, toPtr, *toLen, *flags);
840 
841  PTRACE(1, "Plugin", "Invalid parameter to Transcode.");
842  return false;
843  }
844 
845 
846  static int GetOutputDataSize_s(const PluginCodec_Definition *, void * context, const char *, void *, unsigned *)
847  {
848  return context != NULL ? ((PluginCodec *)context)->GetOutputDataSize() : 0;
849  }
850 
852 
853  static int ToNormalised_s(const PluginCodec_Definition * defn, void *, const char *, void * parm, unsigned * len)
854  {
855  return defn->userData != NULL ? ((MediaFormat *)defn->userData)->AdjustOptions(parm, len, &MediaFormat::ToNormalised) : -1;
856  }
857 
858 
859  static int ToCustomised_s(const PluginCodec_Definition * defn, void *, const char *, void * parm, unsigned * len)
860  {
861  return defn->userData != NULL ? ((MediaFormat *)defn->userData)->AdjustOptions(parm, len, &MediaFormat::ToCustomised) : -1;
862  }
863 
864 
865  static int GetActiveOptions_s(const PluginCodec_Definition *, void * context, const char *, void * parm, unsigned * parmLen)
866  {
867  if (context == NULL || parmLen == NULL || parm == NULL || *parmLen != sizeof(char ***)) {
868  PTRACE(1, "Plugin", "Invalid parameters to GetActiveOptions.");
869  return false;
870  }
871 
872  PluginCodec_OptionMap activeOptions;
873  if (!((PluginCodec *)context)->GetActiveOptions(activeOptions))
874  return false;
875 
876  return (*(char ***)parm = activeOptions.GetOptions()) != NULL;
877  }
878 
879 
880  static int FreeOptions_s(const PluginCodec_Definition *, void *, const char *, void * parm, unsigned * len)
881  {
882  if (parm == NULL || len == NULL || *len != sizeof(char ***))
883  return false;
884 
885  char ** strings = (char **)parm;
886  for (char ** string = strings; *string != NULL; string++)
887  free(*string);
888  free(strings);
889  return true;
890  }
891 
892 
893  static int GetOptions_s(const struct PluginCodec_Definition * codec, void *, const char *, void * parm, unsigned * len)
894  {
895  if (parm == NULL || len == NULL || *len != sizeof(struct PluginCodec_Option **))
896  return false;
897 
898  *(const void **)parm = codec->userData != NULL ? ((MediaFormat *)codec->userData)->GetOptionsTable() : NULL;
899  *len = 0;
900  return true;
901  }
902 
903 
904  static int SetOptions_s(const PluginCodec_Definition *, void * context, const char *, void * parm, unsigned * len)
905  {
906  PluginCodec * codec = (PluginCodec *)context;
907  return len != NULL && *len == sizeof(const char **) && parm != NULL &&
908  codec != NULL && codec->SetOptions((const char * const *)parm);
909  }
910 
911  static int ValidForProtocol_s(const PluginCodec_Definition * defn, void *, const char *, void * parm, unsigned * len)
912  {
913  return len != NULL && *len == sizeof(const char *) && parm != NULL && defn->userData != NULL &&
914  ((MediaFormat *)defn->userData)->IsValidForProtocol((const char *)parm);
915  }
916 
917  static int SetInstanceID_s(const PluginCodec_Definition *, void * context, const char *, void * parm, unsigned * len)
918  {
919  PluginCodec * codec = (PluginCodec *)context;
920  return len != NULL && parm != NULL &&
921  codec != NULL && codec->SetInstanceID((const char *)parm, *len);
922  }
923 
924  static int GetStatistics_s(const PluginCodec_Definition *, void * context, const char *, void * parm, unsigned * len)
925  {
926  PluginCodec * codec = (PluginCodec *)context;
927  return len != NULL && parm != NULL &&
928  codec != NULL && codec->GetStatistics((char *)parm, *len);
929  }
930 
931  static int Terminate_s(const PluginCodec_Definition *, void * context, const char *, void *, unsigned *)
932  {
933  PluginCodec * codec = (PluginCodec *)context;
934  return codec != NULL && codec->Terminate();
935  }
936 
938  {
939  static PluginCodec_ControlDefn ControlsTable[] = {
952  { NULL }
953  };
954  return ControlsTable;
955  }
956 
957  protected:
959 
961  unsigned m_maxBitRate;
962  unsigned m_frameTime;
963 };
964 
965 
967 
968 template<typename NAME>
969 class PluginVideoCodec : public PluginCodec<NAME>
970 {
972 
973  public:
974  enum {
975  DefaultWidth = 352, // CIF size
979  };
980 
981 
983  : BaseClass(defn)
984  {
985  }
986 
987 
988  virtual size_t GetRawFrameSize(unsigned width, unsigned height)
989  {
990  return width*height*3/2; // YUV420P
991  }
992 };
993 
994 
996 
997 template<typename NAME>
999 {
1001 
1002  protected:
1003  unsigned m_width;
1004  unsigned m_height;
1005  unsigned m_maxRTPSize;
1006  unsigned m_tsto;
1008 
1009  public:
1011  : BaseClass(defn)
1015  , m_tsto(31)
1016  , m_keyFramePeriod(0) // Indicates auto/default
1017  {
1018  }
1019 
1020 
1021  virtual bool SetOption(const char * optionName, const char * optionValue)
1022  {
1023  if (strcasecmp(optionName, PLUGINCODEC_OPTION_FRAME_WIDTH) == 0)
1024  return this->SetOptionUnsigned(this->m_width, optionValue, 16, BaseClass::MaxWidth);
1025 
1026  if (strcasecmp(optionName, PLUGINCODEC_OPTION_FRAME_HEIGHT) == 0)
1027  return this->SetOptionUnsigned(this->m_height, optionValue, 16, BaseClass::MaxHeight);
1028 
1029  if (strcasecmp(optionName, PLUGINCODEC_OPTION_MAX_TX_PACKET_SIZE) == 0)
1030  return this->SetOptionUnsigned(this->m_maxRTPSize, optionValue, 256, 8192);
1031 
1032  if (strcasecmp(optionName, PLUGINCODEC_OPTION_TEMPORAL_SPATIAL_TRADE_OFF) == 0)
1033  return this->SetOptionUnsigned(this->m_tsto, optionValue, 1, 31);
1034 
1035  if (strcasecmp(optionName, PLUGINCODEC_OPTION_TX_KEY_FRAME_PERIOD) == 0)
1036  return this->SetOptionUnsigned(this->m_keyFramePeriod, optionValue, 0);
1037 
1038  // Base class sets bit rate and frame time
1039  return BaseClass::SetOption(optionName, optionValue);
1040  }
1041 
1042 
1044  virtual bool GetActiveOptions(PluginCodec_OptionMap & options)
1045  {
1047  return true;
1048  }
1049 
1050 
1051  virtual size_t GetPacketSpace(const PluginCodec_RTP & rtp, size_t total)
1052  {
1053  size_t space = rtp.GetMaxSize();
1054  if (space > this->m_maxRTPSize)
1055  space = this->m_maxRTPSize;
1056  space -= rtp.GetHeaderSize();
1057  if (space > total)
1058  space = total;
1059  return space;
1060  }
1061 };
1062 
1063 
1065 
1066 template<typename NAME>
1068 {
1070 
1071  protected:
1073 
1074  public:
1076  : BaseClass(defn)
1078  {
1079  }
1080 
1081 
1082  virtual size_t GetOutputDataSize()
1083  {
1084  return this->m_outputSize;
1085  }
1086 
1087 
1088  virtual bool CanOutputImage(unsigned width, unsigned height, PluginCodec_RTP & rtp, unsigned & flags)
1089  {
1090  size_t newSize = this->GetRawFrameSize(width, height) + sizeof(PluginCodec_Video_FrameHeader);
1091  if (!rtp.SetPayloadSize(newSize)) {
1092  m_outputSize = newSize + rtp.GetHeaderSize();
1094  return false;
1095  }
1096 
1097  PluginCodec_Video_FrameHeader * videoHeader = rtp.GetVideoHeader();
1098  videoHeader->x = 0;
1099  videoHeader->y = 0;
1100  videoHeader->width = width;
1101  videoHeader->height = height;
1102 
1104  rtp.SetMarker(true);
1105  return true;
1106  }
1107 
1108 
1110  {
1111  unsigned m_width;
1112  unsigned m_height;
1113  unsigned m_raster;
1114  unsigned char * m_source;
1115  unsigned char * m_destination;
1116 
1117  void Copy()
1118  {
1119  for (unsigned y = 0; y < m_height; ++y) {
1120  memcpy(m_destination, m_source, m_width);
1121  this->m_source += m_raster;
1122  this->m_destination += m_width;
1123  }
1124  }
1125  };
1126 
1127  virtual unsigned OutputImage(unsigned char * planes[3], int raster[3],
1128  unsigned width, unsigned height, PluginCodec_RTP & rtp, unsigned & flags)
1129  {
1130  if (!CanOutputImage(width, height, rtp, flags))
1131  return 0;
1132 
1133  size_t ySize = width*height;
1134  size_t uvSize = ySize/4;
1135  if (planes[1] == planes[0]+ySize && planes[2] == planes[1]+uvSize)
1136  memcpy(rtp.GetVideoFrameData(), planes[0], ySize+uvSize*2);
1137  else {
1138  OutputImagePlaneInfo planeInfo[3] = {
1139  { width, height, raster[0], planes[0], rtp.GetVideoFrameData() },
1140  { width/2, height/2, raster[1], planes[1], planeInfo[0].m_destination + ySize },
1141  { width/2, height/2, raster[2], planes[2], planeInfo[1].m_destination + uvSize }
1142  };
1143 
1144  for (unsigned plane = 0; plane < 3; ++plane)
1145  planeInfo[plane].Copy();
1146  }
1147 
1148  return rtp.GetPacketSize();
1149  }
1150 };
1151 
1152 
1154 
1156 #define PLUGINCODEC_AUDIO_CODEC_CXX(MediaFormat, \
1157  EncoderClass, \
1158  DecoderClass \
1159  ) \
1160  PLUGINCODEC_CODEC_PAIR(MediaFormat.GetFormatName(), \
1161  MediaFormat.GetPayloadName(), \
1162  MediaFormat.GetDescription(), \
1163  MediaFormat.GetSampleRate(), \
1164  MediaFormat.GetMaxBandwidth(), \
1165  MediaFormat.GetFrameTime(), \
1166  MediaFormat.GetSamplesPerFrame(), \
1167  MediaFormat.GetBytesPerFrame(), \
1168  MediaFormat.GetRecommendedFramesPerPacket(), \
1169  MediaFormat.GetMaxFramesPerPacket(), \
1170  MediaFormat.GetPayloadType(), \
1171  MediaFormat.GetH323CapabilityType(), \
1172  MediaFormat.GetH323CapabilityData(), \
1173  EncoderClass::Create_s<EncoderClass>, \
1174  EncoderClass::Destroy_s, \
1175  EncoderClass::Transcode_s, \
1176  DecoderClass::Create_s<DecoderClass>, \
1177  DecoderClass::Destroy_s, \
1178  DecoderClass::Transcode_s, \
1179  DecoderClass::GetControls(), /* Note doesn't matter if encoder or decoder */ \
1180  MediaFormat.GetFlags(), \
1181  PLUGINCODEC_RAW_AUDIO, \
1182  &MediaFormat)
1183 
1185 #define PLUGINCODEC_VIDEO_CODEC_CXX(MediaFormat, \
1186  EncoderClass, \
1187  DecoderClass \
1188  ) \
1189  PLUGINCODEC_CODEC_PAIR(MediaFormat.GetFormatName(), \
1190  MediaFormat.GetPayloadName(), \
1191  MediaFormat.GetDescription(), \
1192  PLUGINCODEC_VIDEO_CLOCK, \
1193  MediaFormat.GetMaxBandwidth(), \
1194  1000000/PLUGINCODEC_MAX_FRAME_RATE, \
1195  MediaFormat.GetMaxWidth(), \
1196  MediaFormat.GetMaxHeight(), \
1197  0,PLUGINCODEC_MAX_FRAME_RATE, \
1198  MediaFormat.GetPayloadType(), \
1199  MediaFormat.GetH323CapabilityType(), \
1200  MediaFormat.GetH323CapabilityData(), \
1201  EncoderClass::Create_s<EncoderClass>, \
1202  EncoderClass::Destroy_s, \
1203  EncoderClass::Transcode_s, \
1204  DecoderClass::Create_s<DecoderClass>, \
1205  DecoderClass::Destroy_s, \
1206  DecoderClass::Transcode_s, \
1207  DecoderClass::GetControls(), /* Note doesn't matter if encoder or decoder */ \
1208  MediaFormat.GetFlags(), \
1209  PLUGINCODEC_RAW_VIDEO, \
1210  &MediaFormat)
1211 
1212 
1213 #define PLUGIN_CODEC_IMPLEMENT_CXX(NAME, table) \
1214  extern "C" { \
1215  PLUGIN_CODEC_IMPLEMENT(NAME) \
1216  PLUGIN_CODEC_DLL_API struct PluginCodec_Definition * PLUGIN_CODEC_GET_CODEC_FN(unsigned * count, unsigned version) { \
1217  if (version < PLUGIN_CODEC_VERSION_OPTIONS) return NULL; \
1218  *count = sizeof(table)/sizeof(struct PluginCodec_Definition); \
1219  PluginCodec_MediaFormat<NAME>::AdjustAllForVersion(version, table, *count); \
1220  return table; \
1221  } \
1222  }
1223 
1224 
1225 #endif // OPAL_CODEC_OPALPLUGIN_HPP