OPAL  Version 3.10.4
opalplugin.hpp
Go to the documentation of this file.
00001 /*
00002  * opalplugins.hpp
00003  *
00004  * OPAL codec plugins handler (C++ version)
00005  *
00006  * Open Phone Abstraction Library (OPAL)
00007  * Formally known as the Open H323 project.
00008  *
00009  * Copyright (C) 2010 Vox Lucida
00010  *
00011  * Redistribution and use in source and binary forms, with or without
00012  * modification, are permitted provided that the following conditions
00013  * are met:
00014  *
00015  * - Redistributions of source code must retain the above copyright
00016  *   notice, this list of conditions and the following disclaimer.
00017 
00018  * - Redistributions in binary form must reproduce the above copyright
00019  *   notice, this list of conditions and the following disclaimer in the
00020  *   documentation and/or other materials provided with the distribution.
00021  *
00022  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00023  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00024  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
00025  * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
00026  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
00027  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
00028  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
00029  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
00030  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
00031  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00032  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00033  *
00034  * $Revision: 26787 $
00035  * $Author: rjongbloed $
00036  * $Date: 2011-12-08 18:49:50 -0600 (Thu, 08 Dec 2011) $
00037  */
00038 
00039 #ifndef OPAL_CODEC_OPALPLUGIN_HPP
00040 #define OPAL_CODEC_OPALPLUGIN_HPP
00041 
00042 #include "opalplugin.h"
00043 
00044 #include <string.h>
00045 #include <stdlib.h>
00046 #include <limits.h>
00047 
00048 #include <map>
00049 #include <string>
00050 
00051 
00053 
00054 #ifndef PLUGINCODEC_TRACING
00055   #define PLUGINCODEC_TRACING 1
00056 #endif
00057 
00058 #if PLUGINCODEC_TRACING
00059   extern PluginCodec_LogFunction PluginCodec_LogFunctionInstance;
00060   extern int PluginCodec_SetLogFunction(const PluginCodec_Definition *, void *, const char *, void * parm, unsigned * len);
00061 
00062 #define PLUGINCODEC_CONTROL_LOG_FUNCTION_DEF \
00063   PluginCodec_LogFunction PluginCodec_LogFunctionInstance; \
00064   int PluginCodec_SetLogFunction(const PluginCodec_Definition *, void *, const char *, void * parm, unsigned * len) \
00065   { \
00066     if (len == NULL || *len != sizeof(PluginCodec_LogFunction)) \
00067       return false; \
00068  \
00069     PluginCodec_LogFunctionInstance = (PluginCodec_LogFunction)parm; \
00070     if (PluginCodec_LogFunctionInstance != NULL) \
00071       PluginCodec_LogFunctionInstance(4, __FILE__, __LINE__, "Plugin", "Started logging."); \
00072  \
00073     return true; \
00074   } \
00075 
00076   #define PLUGINCODEC_CONTROL_LOG_FUNCTION_INC { PLUGINCODEC_CONTROL_SET_LOG_FUNCTION, PluginCodec_SetLogFunction },
00077 #else
00078   #define PLUGINCODEC_CONTROL_LOG_FUNCTION_DEF
00079   #define PLUGINCODEC_CONTROL_LOG_FUNCTION_INC
00080 #endif
00081 
00082 #if !defined(PTRACE)
00083   #if PLUGINCODEC_TRACING
00084     #include <sstream>
00085     #define PTRACE_CHECK(level) \
00086         (PluginCodec_LogFunctionInstance != NULL && PluginCodec_LogFunctionInstance(level, NULL, 0, NULL, NULL))
00087     #define PTRACE(level, section, args) \
00088       if (PTRACE_CHECK(level)) { \
00089         std::ostringstream strm; strm << args; \
00090         PluginCodec_LogFunctionInstance(level, __FILE__, __LINE__, section, strm.str().c_str()); \
00091       } else (void)0
00092   #else
00093     #define PTRACE_CHECK(level)
00094     #define PTRACE(level, section, expr)
00095   #endif
00096 #endif
00097 
00098 
00100 
00101 class PluginCodec_MediaFormat
00102 {
00103   public:
00104     typedef struct PluginCodec_Option const * const * OptionsTable;
00105     typedef std::map<std::string, std::string> OptionMap;
00106 
00107   protected:
00108     OptionsTable m_options;
00109 
00110   protected:
00111     PluginCodec_MediaFormat(OptionsTable options)
00112       : m_options(options)
00113     {
00114     }
00115 
00116   public:
00117     virtual ~PluginCodec_MediaFormat()
00118     {
00119     }
00120 
00121 
00122     const void * GetOptionsTable() const { return m_options; }
00123 
00125     virtual bool IsValidForProtocol(const char * /*protocol*/)
00126     {
00127       return true;
00128     }
00129 
00130 
00132     bool AdjustOptions(void * parm, unsigned * parmLen, bool (PluginCodec_MediaFormat:: * adjuster)(OptionMap & original, OptionMap & changed))
00133     {
00134       if (parmLen == NULL || parm == NULL || *parmLen != sizeof(char ***)) {
00135         PTRACE(1, "Plugin", "Invalid parameters to AdjustOptions.");
00136         return false;
00137       }
00138 
00139       OptionMap originalOptions;
00140       for (const char * const * option = *(const char * const * *)parm; *option != NULL; option += 2)
00141         originalOptions[option[0]] = option[1];
00142 
00143       OptionMap changedOptions;
00144       if (!(this->*adjuster)(originalOptions, changedOptions)) {
00145         PTRACE(1, "Plugin", "Could not normalise/customise options.");
00146         return false;
00147       }
00148 
00149       char ** options = (char **)calloc(changedOptions.size()*2+1, sizeof(char *));
00150       *(char ***)parm = options;
00151       if (options == NULL) {
00152         PTRACE(1, "Plugin", "Could not allocate new option lists.");
00153         return false;
00154       }
00155 
00156       for (OptionMap::iterator i = changedOptions.begin(); i != changedOptions.end(); ++i) {
00157         *options++ = strdup(i->first.c_str());
00158         *options++ = strdup(i->second.c_str());
00159       }
00160 
00161       return true;
00162     }
00163 
00164 
00166     virtual bool ToNormalised(OptionMap & original, OptionMap & changed) = 0;
00167 
00168 
00169     // Adjust codec specific options calculated from normalised options.
00170     virtual bool ToCustomised(OptionMap & original, OptionMap & changed) = 0;
00171 
00172 
00173     static void Change(const char * value,
00174                        OptionMap  & original,
00175                        OptionMap  & changed,
00176                        const char * option)
00177     {
00178       if (original[option] != value)
00179         changed[option] = value;
00180     }
00181 
00182 
00183     static unsigned String2Unsigned(const std::string & str)
00184     {
00185       return strtoul(str.c_str(), NULL, 10);
00186     }
00187 
00188 
00189     static void AppendUnsigned2String(unsigned value, std::string & str)
00190     {
00191       // Not very efficient, but really, really simple
00192       if (value > 9)
00193         AppendUnsigned2String(value/10, str);
00194       str += (char)(value%10 + '0');
00195     }
00196 
00197 
00198     static void Unsigned2String(unsigned value, std::string & str)
00199     {
00200       str.clear();
00201       AppendUnsigned2String(value,str);
00202     }
00203 
00204 
00205     static void Change(unsigned     value,
00206                        OptionMap  & original,
00207                        OptionMap  & changed,
00208                        const char * option)
00209     {
00210       if (String2Unsigned(original[option]) != value)
00211         Unsigned2String(value, changed[option]);
00212     }
00213 
00214 
00215     static void ClampMax(unsigned     maximum,
00216                          OptionMap  & original,
00217                          OptionMap  & changed,
00218                          const char * option)
00219     {
00220       unsigned value = String2Unsigned(original[option]);
00221       if (value > maximum)
00222         Unsigned2String(maximum, changed[option]);
00223     }
00224 
00225 
00226     static void ClampMin(unsigned     minimum,
00227                          OptionMap  & original,
00228                          OptionMap  & changed,
00229                          const char * option)
00230     {
00231       unsigned value = String2Unsigned(original[option]);
00232       if (value < minimum)
00233         Unsigned2String(minimum, changed[option]);
00234     }
00235 
00236     virtual void AdjustForVersion(unsigned version, const PluginCodec_Definition * /*definition*/)
00237     {
00238       if (version < PLUGIN_CODEC_VERSION_INTERSECT) {
00239         for (PluginCodec_Option ** options = (PluginCodec_Option **)m_options; *options != NULL; ++options) {
00240           if (strcmp((*options)->m_name, PLUGINCODEC_MEDIA_PACKETIZATIONS) == 0) {
00241             *options = NULL;
00242             break;
00243           }
00244         }
00245       }
00246     }
00247 
00248     static void AdjustAllForVersion(unsigned version, const PluginCodec_Definition * definitions, size_t size)
00249     {
00250       while (size-- > 0) {
00251         PluginCodec_MediaFormat * info = (PluginCodec_MediaFormat *)definitions->userData;
00252         if (info != NULL)
00253           info->AdjustForVersion(version, definitions);
00254         ++definitions;
00255       }
00256     }
00257 };
00258 
00259 
00261 
00262 template<typename NAME>
00263 class PluginCodec
00264 {
00265   protected:
00266     PluginCodec(const PluginCodec_Definition * defn)
00267       : m_definition(defn)
00268       , m_optionsSame(false)
00269       , m_maxBitRate(defn->bitsPerSec)
00270       , m_frameTime((defn->sampleRate/1000*defn->usPerFrame)/1000) // Odd way of calculation to avoid 32 bit integer overflow
00271     {
00272       PTRACE(3, "Plugin", "Codec created: \"" << defn->descr
00273              << "\", \"" << defn->sourceFormat << "\" -> \"" << defn->destFormat << '"');
00274     }
00275 
00276 
00277   public:
00278     virtual ~PluginCodec()
00279     {
00280     }
00281 
00282 
00284     virtual bool Construct()
00285     {
00286       return true;
00287     }
00288 
00289 
00294     static bool Terminate()
00295     {
00296       return true;
00297     }
00298 
00299 
00301     virtual bool Transcode(const void * fromPtr,
00302                              unsigned & fromLen,
00303                                  void * toPtr,
00304                              unsigned & toLen,
00305                              unsigned & flags) = 0;
00306 
00307 
00309     virtual bool GetStatistics(char * /*bufferPtr*/, unsigned /*bufferSize*/)
00310     {
00311       return true;
00312     }
00313 
00314 
00316     virtual size_t GetOutputDataSize()
00317     {
00318       return 576-20-16; // Max safe MTU size (576 bytes as per RFC879) minus IP & UDP headers
00319     }
00320 
00321 
00328     virtual bool SetInstanceID(const char * /*idPtr*/, unsigned /*idLen*/)
00329     {
00330       return true;
00331     }
00332 
00333 
00335     virtual bool SetOptions(const char * const * options)
00336     {
00337       m_optionsSame = true;
00338 
00339       // get the media format options after adjustment from protocol negotiation
00340       for (const char * const * option = options; *option != NULL; option += 2) {
00341         if (!SetOption(option[0], option[1])) {
00342           PTRACE(1, "Plugin", "Could not set option \"" << option[0] << "\" to \"" << option[1] << '"');
00343           return false;
00344         }
00345       }
00346 
00347       if (m_optionsSame)
00348         return true;
00349 
00350       return OnChangedOptions();
00351     }
00352 
00353 
00355     virtual bool OnChangedOptions()
00356     {
00357       return true;
00358     }
00359 
00360 
00362     virtual bool SetOption(const char * optionName, const char * optionValue)
00363     {
00364       if (strcasecmp(optionName, PLUGINCODEC_OPTION_TARGET_BIT_RATE) == 0)
00365         return SetOptionUnsigned(m_maxBitRate, optionValue, 1, m_definition->bitsPerSec);
00366 
00367       if (strcasecmp(optionName, PLUGINCODEC_OPTION_FRAME_TIME) == 0)
00368         return SetOptionUnsigned(m_frameTime, optionValue, m_definition->sampleRate/1000, m_definition->sampleRate); // 1ms to 1 second
00369 
00370       return true;
00371     }
00372 
00373 
00374     template <typename T>
00375     bool SetOptionUnsigned(T & oldValue, const char * optionValue, unsigned minimum, unsigned maximum = UINT_MAX)
00376     {
00377       unsigned newValue = oldValue;
00378       if (!SetOptionUnsigned(newValue, optionValue, minimum, maximum))
00379         return false;
00380       oldValue = (T)newValue;
00381       return true;
00382     }
00383 
00384 
00385     bool SetOptionUnsigned(unsigned & oldValue, const char * optionValue, unsigned minimum, unsigned maximum = UINT_MAX)
00386     {
00387       char * end;
00388       unsigned newValue = strtoul(optionValue, &end, 10);
00389       if (*end != '\0')
00390         return false;
00391 
00392       if (newValue < minimum)
00393         newValue = minimum;
00394       else if (newValue > maximum)
00395         newValue = maximum;
00396 
00397       if (oldValue != newValue) {
00398         oldValue = newValue;
00399         m_optionsSame = false;
00400       }
00401 
00402       return true;
00403     }
00404 
00405 
00406     template <typename T>
00407     bool SetOptionBoolean(T & oldValue, const char * optionValue)
00408     {
00409       bool opt = oldValue != 0;
00410       if (!SetOptionBoolean(opt, optionValue))
00411         return false;
00412       oldValue = (T)opt;
00413       return true;
00414     }
00415 
00416 
00417     bool SetOptionBoolean(bool & oldValue, const char * optionValue)
00418     {
00419       bool newValue;
00420       if (     strcasecmp(optionValue, "0") == 0 ||
00421                strcasecmp(optionValue, "n") == 0 ||
00422                strcasecmp(optionValue, "f") == 0 ||
00423                strcasecmp(optionValue, "no") == 0 ||
00424                strcasecmp(optionValue, "false") == 0)
00425         newValue = false;
00426       else if (strcasecmp(optionValue, "1") == 0 ||
00427                strcasecmp(optionValue, "y") == 0 ||
00428                strcasecmp(optionValue, "t") == 0 ||
00429                strcasecmp(optionValue, "yes") == 0 ||
00430                strcasecmp(optionValue, "true") == 0)
00431         newValue = true;
00432       else
00433         return false;
00434 
00435       if (oldValue != newValue) {
00436         oldValue = newValue;
00437         m_optionsSame = false;
00438       }
00439 
00440       return true;
00441     }
00442 
00443 
00444     bool SetOptionBit(int & oldValue, unsigned bit, const char * optionValue)
00445     {
00446       return SetOptionBit((unsigned &)oldValue, bit, optionValue);
00447     }
00448 
00449 
00450     bool SetOptionBit(unsigned & oldValue, unsigned bit, const char * optionValue)
00451     {
00452       bool newValue;
00453       if (strcmp(optionValue, "0") == 0)
00454         newValue = false;
00455       else if (strcmp(optionValue, "1") == 0)
00456         newValue = true;
00457       else
00458         return false;
00459 
00460       if (((oldValue&bit) != 0) != newValue) {
00461         if (newValue)
00462           oldValue |= bit;
00463         else
00464           oldValue &= ~bit;
00465         m_optionsSame = false;
00466       }
00467 
00468       return true;
00469     }
00470 
00471 
00472     template <class CodecClass> static void * Create(const PluginCodec_Definition * defn)
00473     {
00474       CodecClass * codec = new CodecClass(defn);
00475       if (codec != NULL && codec->Construct())
00476         return codec;
00477 
00478       PTRACE(1, "Plugin", "Could not open codec, no context being returned.");
00479       delete codec;
00480       return NULL;
00481     }
00482 
00483 
00484     static void Destroy(const PluginCodec_Definition * /*defn*/, void * context)
00485     {
00486       delete (PluginCodec *)context;
00487     }
00488 
00489 
00490     static int Transcode(const PluginCodec_Definition * /*defn*/,
00491                                                  void * context,
00492                                            const void * fromPtr,
00493                                              unsigned * fromLen,
00494                                                  void * toPtr,
00495                                              unsigned * toLen,
00496                                          unsigned int * flags)
00497     {
00498       if (context != NULL && fromPtr != NULL && fromLen != NULL && toPtr != NULL && toLen != NULL && flags != NULL)
00499         return ((PluginCodec *)context)->Transcode(fromPtr, *fromLen, toPtr, *toLen, *flags);
00500 
00501       PTRACE(1, "Plugin", "Invalid parameter to Transcode.");
00502       return false;
00503     }
00504 
00505 
00506     static int GetOutputDataSize(const PluginCodec_Definition *, void * context, const char *, void *, unsigned *)
00507     {
00508       return context != NULL ? ((PluginCodec *)context)->GetOutputDataSize() : 0;
00509     }
00510 
00511 
00512     static int ToNormalised(const PluginCodec_Definition * defn, void *, const char *, void * parm, unsigned * len)
00513     {
00514       return defn->userData != NULL ? ((PluginCodec_MediaFormat *)defn->userData)->AdjustOptions(parm, len, &PluginCodec_MediaFormat::ToNormalised) : -1;
00515     }
00516 
00517 
00518     static int ToCustomised(const PluginCodec_Definition * defn, void *, const char *, void * parm, unsigned * len)
00519     {
00520       return defn->userData != NULL ? ((PluginCodec_MediaFormat *)defn->userData)->AdjustOptions(parm, len, &PluginCodec_MediaFormat::ToCustomised) : -1;
00521     }
00522 
00523 
00524     static int FreeOptions(const PluginCodec_Definition *, void *, const char *, void * parm, unsigned * len)
00525     {
00526       if (parm == NULL || len == NULL || *len != sizeof(char ***))
00527         return false;
00528 
00529       char ** strings = (char **)parm;
00530       for (char ** string = strings; *string != NULL; string++)
00531         free(*string);
00532       free(strings);
00533       return true;
00534     }
00535 
00536 
00537     static int GetOptions(const struct PluginCodec_Definition * codec, void *, const char *, void * parm, unsigned * len)
00538     {
00539       if (parm == NULL || len == NULL || *len != sizeof(struct PluginCodec_Option **))
00540         return false;
00541 
00542       *(const void **)parm = codec->userData != NULL ? ((PluginCodec_MediaFormat *)codec->userData)->GetOptionsTable() : NULL;
00543       *len = 0;
00544       return true;
00545     }
00546 
00547 
00548     static int SetOptions(const PluginCodec_Definition *, void * context, const char *, void * parm, unsigned * len)
00549     {
00550       PluginCodec * codec = (PluginCodec *)context;
00551       return len != NULL && *len == sizeof(const char **) && parm != NULL &&
00552              codec != NULL && codec->SetOptions((const char * const *)parm);
00553     }
00554 
00555     static int ValidForProtocol(const PluginCodec_Definition * defn, void *, const char *, void * parm, unsigned * len)
00556     {
00557       return len != NULL && *len == sizeof(const char *) && parm != NULL && defn->userData != NULL &&
00558              ((PluginCodec_MediaFormat *)defn->userData)->IsValidForProtocol((const char *)parm);
00559     }
00560 
00561     static int SetInstanceID(const PluginCodec_Definition *, void * context, const char *, void * parm, unsigned * len)
00562     {
00563       PluginCodec * codec = (PluginCodec *)context;
00564       return len != NULL && parm != NULL &&
00565              codec != NULL && codec->SetInstanceID((const char *)parm, *len);
00566     }
00567 
00568     static int GetStatistics(const PluginCodec_Definition *, void * context, const char *, void * parm, unsigned * len)
00569     {
00570       PluginCodec * codec = (PluginCodec *)context;
00571       return len != NULL && parm != NULL &&
00572              codec != NULL && codec->GetStatistics((char *)parm, *len);
00573     }
00574 
00575     static int Terminate(const PluginCodec_Definition *, void * context, const char *, void *, unsigned *)
00576     {
00577       PluginCodec * codec = (PluginCodec *)context;
00578       return codec != NULL && codec->Terminate();
00579     }
00580 
00581     static struct PluginCodec_ControlDefn * GetControls()
00582     {
00583       static PluginCodec_ControlDefn ControlsTable[] = {
00584         { PLUGINCODEC_CONTROL_GET_OUTPUT_DATA_SIZE,  PluginCodec::GetOutputDataSize },
00585         { PLUGINCODEC_CONTROL_TO_NORMALISED_OPTIONS, PluginCodec::ToNormalised },
00586         { PLUGINCODEC_CONTROL_TO_CUSTOMISED_OPTIONS, PluginCodec::ToCustomised },
00587         { PLUGINCODEC_CONTROL_SET_CODEC_OPTIONS,     PluginCodec::SetOptions },
00588         { PLUGINCODEC_CONTROL_GET_CODEC_OPTIONS,     PluginCodec::GetOptions },
00589         { PLUGINCODEC_CONTROL_FREE_CODEC_OPTIONS,    PluginCodec::FreeOptions },
00590         { PLUGINCODEC_CONTROL_VALID_FOR_PROTOCOL,    PluginCodec::ValidForProtocol },
00591         { PLUGINCODEC_CONTROL_SET_INSTANCE_ID,       PluginCodec::SetInstanceID },
00592         { PLUGINCODEC_CONTROL_GET_STATISTICS,        PluginCodec::GetStatistics },
00593         { PLUGINCODEC_CONTROL_TERMINATE_CODEC,       PluginCodec::Terminate },
00594         PLUGINCODEC_CONTROL_LOG_FUNCTION_INC
00595         { NULL }
00596       };
00597       return ControlsTable;
00598     }
00599 
00600   protected:
00601     const PluginCodec_Definition * m_definition;
00602 
00603     bool     m_optionsSame;
00604     unsigned m_maxBitRate;
00605     unsigned m_frameTime;
00606 };
00607 
00608 
00609 #endif // OPAL_CODEC_OPALPLUGIN_HPP