# 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: 27366 $
    00035  * $Author: rjongbloed $
    00036  * $Date: 2012-03-29 00:58:03 -0500 (Thu, 29 Mar 2012) $
    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       OptionMap::iterator it = original.find(option);
    00179       if (it != original.end() && it->second != value)
    00180         changed[option] = value;
    00181     }
    00182
    00183
    00184     static unsigned String2Unsigned(const std::string & str)
    00185     {
    00186       return strtoul(str.c_str(), NULL, 10);
    00187     }
    00188
    00189
    00190     static void AppendUnsigned2String(unsigned value, std::string & str)
    00191     {
    00192       // Not very efficient, but really, really simple
    00193       if (value > 9)
    00194         AppendUnsigned2String(value/10, str);
    00195       str += (char)(value%10 + '0');
    00196     }
    00197
    00198
    00199     static void Unsigned2String(unsigned value, std::string & str)
    00200     {
    00201       str.clear();
    00202       AppendUnsigned2String(value,str);
    00203     }
    00204
    00205
    00206     static void Change(unsigned     value,
    00207                        OptionMap  & original,
    00208                        OptionMap  & changed,
    00209                        const char * option)
    00210     {
    00211       if (String2Unsigned(original[option]) != value)
    00212         Unsigned2String(value, changed[option]);
    00213     }
    00214
    00215
    00216     static void ClampMax(unsigned     maximum,
    00217                          OptionMap  & original,
    00218                          OptionMap  & changed,
    00219                          const char * option)
    00220     {
    00221       unsigned value = String2Unsigned(original[option]);
    00222       if (value > maximum)
    00223         Unsigned2String(maximum, changed[option]);
    00224     }
    00225
    00226
    00227     static void ClampMin(unsigned     minimum,
    00228                          OptionMap  & original,
    00229                          OptionMap  & changed,
    00230                          const char * option)
    00231     {
    00232       unsigned value = String2Unsigned(original[option]);
    00233       if (value < minimum)
    00234         Unsigned2String(minimum, changed[option]);
    00235     }
    00236
    00237     virtual void AdjustForVersion(unsigned version, const PluginCodec_Definition * /*definition*/)
    00238     {
    00239       if (version < PLUGIN_CODEC_VERSION_INTERSECT) {
    00240         for (PluginCodec_Option ** options = (PluginCodec_Option **)m_options; *options != NULL; ++options) {
    00241           if (strcmp((*options)->m_name, PLUGINCODEC_MEDIA_PACKETIZATIONS) == 0) {
    00242             *options = NULL;
    00243             break;
    00244           }
    00245         }
    00246       }
    00247     }
    00248
    00249     static void AdjustAllForVersion(unsigned version, const PluginCodec_Definition * definitions, size_t size)
    00250     {
    00251       while (size-- > 0) {
    00252         PluginCodec_MediaFormat * info = (PluginCodec_MediaFormat *)definitions->userData;
    00253         if (info != NULL)
    00254           info->AdjustForVersion(version, definitions);
    00255         ++definitions;
    00256       }
    00257     }
    00258 };
    00259
    00260
    00262
    00263 template<typename NAME>
    00264 class PluginCodec
    00265 {
    00266   protected:
    00267     PluginCodec(const PluginCodec_Definition * defn)
    00268       : m_definition(defn)
    00269       , m_optionsSame(false)
    00270       , m_maxBitRate(defn->bitsPerSec)
    00271       , m_frameTime((defn->sampleRate/1000*defn->usPerFrame)/1000) // Odd way of calculation to avoid 32 bit integer overflow
    00272     {
    00273       PTRACE(3, "Plugin", "Codec created: \"" << defn->descr
    00274              << "\", \"" << defn->sourceFormat << "\" -> \"" << defn->destFormat << '"');
    00275     }
    00276
    00277
    00278   public:
    00279     virtual ~PluginCodec()
    00280     {
    00281     }
    00282
    00283
    00285     virtual bool Construct()
    00286     {
    00287       return true;
    00288     }
    00289
    00290
    00295     static bool Terminate()
    00296     {
    00297       return true;
    00298     }
    00299
    00300
    00302     virtual bool Transcode(const void * fromPtr,
    00303                              unsigned & fromLen,
    00304                                  void * toPtr,
    00305                              unsigned & toLen,
    00306                              unsigned & flags) = 0;
    00307
    00308
    00310     virtual bool GetStatistics(char * /*bufferPtr*/, unsigned /*bufferSize*/)
    00311     {
    00312       return true;
    00313     }
    00314
    00315
    00317     virtual size_t GetOutputDataSize()
    00318     {
    00319       return 576-20-16; // Max safe MTU size (576 bytes as per RFC879) minus IP & UDP headers
    00320     }
    00321
    00322
    00329     virtual bool SetInstanceID(const char * /*idPtr*/, unsigned /*idLen*/)
    00330     {
    00331       return true;
    00332     }
    00333
    00334
    00336     virtual bool SetOptions(const char * const * options)
    00337     {
    00338       m_optionsSame = true;
    00339
    00340       // get the media format options after adjustment from protocol negotiation
    00341       for (const char * const * option = options; *option != NULL; option += 2) {
    00342         if (!SetOption(option[0], option[1])) {
    00343           PTRACE(1, "Plugin", "Could not set option \"" << option[0] << "\" to \"" << option[1] << '"');
    00344           return false;
    00345         }
    00346       }
    00347
    00348       if (m_optionsSame)
    00349         return true;
    00350
    00351       return OnChangedOptions();
    00352     }
    00353
    00354
    00356     virtual bool OnChangedOptions()
    00357     {
    00358       return true;
    00359     }
    00360
    00361
    00363     virtual bool SetOption(const char * optionName, const char * optionValue)
    00364     {
    00365       if (strcasecmp(optionName, PLUGINCODEC_OPTION_TARGET_BIT_RATE) == 0)
    00366         return SetOptionUnsigned(m_maxBitRate, optionValue, 1, m_definition->bitsPerSec);
    00367
    00368       if (strcasecmp(optionName, PLUGINCODEC_OPTION_FRAME_TIME) == 0)
    00369         return SetOptionUnsigned(m_frameTime, optionValue, m_definition->sampleRate/1000, m_definition->sampleRate); // 1ms to 1 second
    00370
    00371       return true;
    00372     }
    00373
    00374
    00375     template <typename T>
    00376     bool SetOptionUnsigned(T & oldValue, const char * optionValue, unsigned minimum, unsigned maximum = UINT_MAX)
    00377     {
    00378       unsigned newValue = oldValue;
    00379       if (!SetOptionUnsigned(newValue, optionValue, minimum, maximum))
    00380         return false;
    00381       oldValue = (T)newValue;
    00382       return true;
    00383     }
    00384
    00385
    00386     bool SetOptionUnsigned(unsigned & oldValue, const char * optionValue, unsigned minimum, unsigned maximum = UINT_MAX)
    00387     {
    00388       char * end;
    00389       unsigned newValue = strtoul(optionValue, &end, 10);
    00390       if (*end != '\0')
    00391         return false;
    00392
    00393       if (newValue < minimum)
    00394         newValue = minimum;
    00395       else if (newValue > maximum)
    00396         newValue = maximum;
    00397
    00398       if (oldValue != newValue) {
    00399         oldValue = newValue;
    00400         m_optionsSame = false;
    00401       }
    00402
    00403       return true;
    00404     }
    00405
    00406
    00407     template <typename T>
    00408     bool SetOptionBoolean(T & oldValue, const char * optionValue)
    00409     {
    00410       bool opt = oldValue != 0;
    00411       if (!SetOptionBoolean(opt, optionValue))
    00412         return false;
    00413       oldValue = (T)opt;
    00414       return true;
    00415     }
    00416
    00417
    00418     bool SetOptionBoolean(bool & oldValue, const char * optionValue)
    00419     {
    00420       bool newValue;
    00421       if (     strcasecmp(optionValue, "0") == 0 ||
    00422                strcasecmp(optionValue, "n") == 0 ||
    00423                strcasecmp(optionValue, "f") == 0 ||
    00424                strcasecmp(optionValue, "no") == 0 ||
    00425                strcasecmp(optionValue, "false") == 0)
    00426         newValue = false;
    00427       else if (strcasecmp(optionValue, "1") == 0 ||
    00428                strcasecmp(optionValue, "y") == 0 ||
    00429                strcasecmp(optionValue, "t") == 0 ||
    00430                strcasecmp(optionValue, "yes") == 0 ||
    00431                strcasecmp(optionValue, "true") == 0)
    00432         newValue = true;
    00433       else
    00434         return false;
    00435
    00436       if (oldValue != newValue) {
    00437         oldValue = newValue;
    00438         m_optionsSame = false;
    00439       }
    00440
    00441       return true;
    00442     }
    00443
    00444
    00445     bool SetOptionBit(int & oldValue, unsigned bit, const char * optionValue)
    00446     {
    00447       return SetOptionBit((unsigned &)oldValue, bit, optionValue);
    00448     }
    00449
    00450
    00451     bool SetOptionBit(unsigned & oldValue, unsigned bit, const char * optionValue)
    00452     {
    00453       bool newValue;
    00454       if (strcmp(optionValue, "0") == 0)
    00455         newValue = false;
    00456       else if (strcmp(optionValue, "1") == 0)
    00457         newValue = true;
    00458       else
    00459         return false;
    00460
    00461       if (((oldValue&bit) != 0) != newValue) {
    00462         if (newValue)
    00463           oldValue |= bit;
    00464         else
    00465           oldValue &= ~bit;
    00466         m_optionsSame = false;
    00467       }
    00468
    00469       return true;
    00470     }
    00471
    00472
    00473     template <class CodecClass> static void * Create(const PluginCodec_Definition * defn)
    00474     {
    00475       CodecClass * codec = new CodecClass(defn);
    00476       if (codec != NULL && codec->Construct())
    00477         return codec;
    00478
    00479       PTRACE(1, "Plugin", "Could not open codec, no context being returned.");
    00480       delete codec;
    00481       return NULL;
    00482     }
    00483
    00484
    00485     static void Destroy(const PluginCodec_Definition * /*defn*/, void * context)
    00486     {
    00487       delete (PluginCodec *)context;
    00488     }
    00489
    00490
    00491     static int Transcode(const PluginCodec_Definition * /*defn*/,
    00492                                                  void * context,
    00493                                            const void * fromPtr,
    00494                                              unsigned * fromLen,
    00495                                                  void * toPtr,
    00496                                              unsigned * toLen,
    00497                                          unsigned int * flags)
    00498     {
    00499       if (context != NULL && fromPtr != NULL && fromLen != NULL && toPtr != NULL && toLen != NULL && flags != NULL)
    00500         return ((PluginCodec *)context)->Transcode(fromPtr, *fromLen, toPtr, *toLen, *flags);
    00501
    00502       PTRACE(1, "Plugin", "Invalid parameter to Transcode.");
    00503       return false;
    00504     }
    00505
    00506
    00507     static int GetOutputDataSize(const PluginCodec_Definition *, void * context, const char *, void *, unsigned *)
    00508     {
    00509       return context != NULL ? ((PluginCodec *)context)->GetOutputDataSize() : 0;
    00510     }
    00511
    00512
    00513     static int ToNormalised(const PluginCodec_Definition * defn, void *, const char *, void * parm, unsigned * len)
    00514     {
    00515       return defn->userData != NULL ? ((PluginCodec_MediaFormat *)defn->userData)->AdjustOptions(parm, len, &PluginCodec_MediaFormat::ToNormalised) : -1;
    00516     }
    00517
    00518
    00519     static int ToCustomised(const PluginCodec_Definition * defn, void *, const char *, void * parm, unsigned * len)
    00520     {
    00521       return defn->userData != NULL ? ((PluginCodec_MediaFormat *)defn->userData)->AdjustOptions(parm, len, &PluginCodec_MediaFormat::ToCustomised) : -1;
    00522     }
    00523
    00524
    00525     static int FreeOptions(const PluginCodec_Definition *, void *, const char *, void * parm, unsigned * len)
    00526     {
    00527       if (parm == NULL || len == NULL || *len != sizeof(char ***))
    00528         return false;
    00529
    00530       char ** strings = (char **)parm;
    00531       for (char ** string = strings; *string != NULL; string++)
    00532         free(*string);
    00533       free(strings);
    00534       return true;
    00535     }
    00536
    00537
    00538     static int GetOptions(const struct PluginCodec_Definition * codec, void *, const char *, void * parm, unsigned * len)
    00539     {
    00540       if (parm == NULL || len == NULL || *len != sizeof(struct PluginCodec_Option **))
    00541         return false;
    00542
    00543       *(const void **)parm = codec->userData != NULL ? ((PluginCodec_MediaFormat *)codec->userData)->GetOptionsTable() : NULL;
    00544       *len = 0;
    00545       return true;
    00546     }
    00547
    00548
    00549     static int SetOptions(const PluginCodec_Definition *, void * context, const char *, void * parm, unsigned * len)
    00550     {
    00551       PluginCodec * codec = (PluginCodec *)context;
    00552       return len != NULL && *len == sizeof(const char **) && parm != NULL &&
    00553              codec != NULL && codec->SetOptions((const char * const *)parm);
    00554     }
    00555
    00556     static int ValidForProtocol(const PluginCodec_Definition * defn, void *, const char *, void * parm, unsigned * len)
    00557     {
    00558       return len != NULL && *len == sizeof(const char *) && parm != NULL && defn->userData != NULL &&
    00559              ((PluginCodec_MediaFormat *)defn->userData)->IsValidForProtocol((const char *)parm);
    00560     }
    00561
    00562     static int SetInstanceID(const PluginCodec_Definition *, void * context, const char *, void * parm, unsigned * len)
    00563     {
    00564       PluginCodec * codec = (PluginCodec *)context;
    00565       return len != NULL && parm != NULL &&
    00566              codec != NULL && codec->SetInstanceID((const char *)parm, *len);
    00567     }
    00568
    00569     static int GetStatistics(const PluginCodec_Definition *, void * context, const char *, void * parm, unsigned * len)
    00570     {
    00571       PluginCodec * codec = (PluginCodec *)context;
    00572       return len != NULL && parm != NULL &&
    00573              codec != NULL && codec->GetStatistics((char *)parm, *len);
    00574     }
    00575
    00576     static int Terminate(const PluginCodec_Definition *, void * context, const char *, void *, unsigned *)
    00577     {
    00578       PluginCodec * codec = (PluginCodec *)context;
    00579       return codec != NULL && codec->Terminate();
    00580     }
    00581
    00582     static struct PluginCodec_ControlDefn * GetControls()
    00583     {
    00584       static PluginCodec_ControlDefn ControlsTable[] = {
    00585         { PLUGINCODEC_CONTROL_GET_OUTPUT_DATA_SIZE,  PluginCodec::GetOutputDataSize },
    00586         { PLUGINCODEC_CONTROL_TO_NORMALISED_OPTIONS, PluginCodec::ToNormalised },
    00587         { PLUGINCODEC_CONTROL_TO_CUSTOMISED_OPTIONS, PluginCodec::ToCustomised },
    00588         { PLUGINCODEC_CONTROL_SET_CODEC_OPTIONS,     PluginCodec::SetOptions },
    00589         { PLUGINCODEC_CONTROL_GET_CODEC_OPTIONS,     PluginCodec::GetOptions },
    00590         { PLUGINCODEC_CONTROL_FREE_CODEC_OPTIONS,    PluginCodec::FreeOptions },
    00591         { PLUGINCODEC_CONTROL_VALID_FOR_PROTOCOL,    PluginCodec::ValidForProtocol },
    00592         { PLUGINCODEC_CONTROL_SET_INSTANCE_ID,       PluginCodec::SetInstanceID },
    00593         { PLUGINCODEC_CONTROL_GET_STATISTICS,        PluginCodec::GetStatistics },
    00594         { PLUGINCODEC_CONTROL_TERMINATE_CODEC,       PluginCodec::Terminate },
    00595         PLUGINCODEC_CONTROL_LOG_FUNCTION_INC
    00596         { NULL }
    00597       };
    00598       return ControlsTable;
    00599     }
    00600
    00601   protected:
    00602     const PluginCodec_Definition * m_definition;
    00603
    00604     bool     m_optionsSame;
    00605     unsigned m_maxBitRate;
    00606     unsigned m_frameTime;
    00607 };
    00608
    00609
    00610 #endif // OPAL_CODEC_OPALPLUGIN_HPP