Commit 37595ae3 authored by Alexandre Savard's avatar Alexandre Savard

[#5571] Integrate mime types and codec factory

parent edcccf85
/*
* Copyright (C) 2004, 2005, 2006, 2009, 2008, 2009, 2010 Savoir-Faire Linux Inc.
* Author: Pierre-Luc Bacon <pierre-luc.bacon@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Additional permission under GNU GPL version 3 section 7:
*
* If you modify this program, or any covered work, by linking or
* combining it with the OpenSSL project's OpenSSL library (or a
* modified version of that library), containing parts covered by the
* terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
* grants you additional permission to convey the resulting work.
* Corresponding Source for a non-source form of such a combination
* shall include the source code for the parts of OpenSSL used as well
* as that of the covered work.
*/
#ifndef __SFL_CODEC_H__
#define __SFL_CODEC_H__
#include "MimeParameters.h" // TODO Move to some higher directory
#include <tr1/memory>
#include <cc++/digest.h>
/**
* Interface for both audio codecs as well as video codecs.
*/
namespace sfl {
class Codec : public virtual MimeParameters
{
public:
Codec() {};
virtual inline ~Codec() {}
/**
* @return The bitrate for which this codec is configured // TODO deal with VBR case.
*/
virtual double getBitRate() const = 0;
/**
* @return The expected bandwidth used by this codec.
*/
virtual double getBandwidth() const = 0;
/**
* @return Additional information (description) about this codec. This is meant to be shown to the user.
*/
virtual std::string getDescription() const = 0;
/**
* @return A copy of the current codec.
*/
virtual Codec* clone() const = 0;
/**
* Build a unique hash code for identifying the codec uniquely.
* Note that if multiple implementations of codec are provided,
* one should override this function in order to avoid conflicting
* hash codes.
*
* Hash code is a CRC32 digest made from :
* MIME "." MIME SUBTYPE "." PAYLOAD TYPE "." PAYLOAD CLOCKRATE
*
* @return a unique hash code identifying this codec.
*/
virtual std::string hashCode() const {
ost::CRC32Digest digest;
std::ostringstream os;
os << getMimeType()
<< "." << getMimeSubtype()
<< "." << getPayloadType()
<< "." << getClockRate();
std::string concat = os.str();
digest.putDigest ( (const unsigned char*) concat.c_str(), concat.length());
std::ostringstream buffer;
buffer << digest;
return buffer.str();
}
void setState(bool state) { _state = state; }
bool getState(void) { return _state; }
private:
bool _state;
};
}
typedef sfl::Codec* create_t();
typedef void destroy_t (sfl::Codec*);
#endif
/*
* Copyright (C) 2004, 2005, 2006, 2009, 2008, 2009, 2010 Savoir-Faire Linux Inc.
* Author: Pierre-Luc Bacon <pierre-luc.bacon@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Additional permission under GNU GPL version 3 section 7:
*
* If you modify this program, or any covered work, by linking or
* combining it with the OpenSSL project's OpenSSL library (or a
* modified version of that library), containing parts covered by the
* terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
* grants you additional permission to convey the resulting work.
* Corresponding Source for a non-source form of such a combination
* shall include the source code for the parts of OpenSSL used as well
* as that of the covered work.
*/
#ifndef __SFL_MIME_PARAMETERS_H__
#define __SFL_MIME_PARAMETERS_H__
/**
* Start a new payload format definition.
*/
#define MIME_PAYLOAD_FORMAT_DEFINITION( mime, subtype, payloadType, clock ) \
private: \
uint8 payload; \
public: \
inline virtual ~MimeParameters##subtype() {}; \
std::string getMimeType() const { \
return std::string( mime ); \
} \
std::string getMimeSubtype() const { \
return std::string( #subtype ); \
} \
uint8 getPayloadType() const { \
return payload; \
} \
void setPayloadType(uint8 pt) { \
payload = pt; \
} \
uint32 getClockRate() const { \
return clock; \
} \
MimeParameters##subtype() : payload(payloadType) {
/**
* An alias for MIME_PARAMETER_OPTIONAL
*/
#define MIME_PARAMETER(name, handler) \
addOptionalParameter( name ); \
addHandler( name, handler );
/**
* Defines an optional parameter.
*/
#define MIME_PARAMETER_OPTIONAL(name, handler) \
addOptionalParameter( name ); \
addHandler( name, handler );
/**
* Defines a required parameter. The value of this parameter
* should be obtained when sending the initial SDP offer.
*/
#define MIME_PARAMETER_REQUIRED(name, handler) \
addRequiredParameter( name ); \
addHandler( name, handler );
/**
* End a payload format definition.
*/
#define MIME_PAYLOAD_FORMAT_DEFINITION_END() \
}
#define MIME_PARAMETER_KEEP_IF_EQUAL MimeParameters::addParameterIfEqual
#define MIME_PARAMETER_KEEP_MINIMUM MimeParameters::addParameterMinimum
#define MIME_PARAMETER_KEEP_REMOTE MimeParameters::addParameterRemote
#include "sip/Fmtp.h"
#include <ccrtp/rtp.h>
#include <algorithm>
#include <stdexcept>
#include <vector>
#include <map>
#include <errno.h>
namespace sfl
{
/**
* This exception is thrown in cases where a given format cannot
* be parsed, or required pieces of information are missing.
*/
class SdpFormatNegotiationException: public std::runtime_error
{
public:
SdpFormatNegotiationException (const std::string& msg) : std::runtime_error (msg) {}
};
/**
* Interface for exposing MIME parameters in SDP offer/answer model.
*/
class MimeParameters
{
public:
/**
* @return The mimesubtype for this codec. Eg. : "video"
*/
virtual std::string getMimeType() const = 0;
/**
* @return The mimesubtype for this codec. Eg. : "theora"
*/
virtual std::string getMimeSubtype() const = 0;
/**
* @return payload type numeric identifier.
*/
virtual uint8 getPayloadType() const = 0;
/**
* @param payload The new payload to set
*/
virtual void setPayloadType(uint8 payload) = 0;
/**
* @return RTP clock rate in Hz.
*/
virtual uint32 getClockRate() const = 0;
/**
* @param name The name that identifies the MIME parameter.
* @param value The value this parameter should have.
*/
virtual void setParameter (const std::string& name, const std::string& value) = 0;
/**
* @param name The name that identifies the MIME parameter.
* @return The value that is set for this parameter.
*/
virtual std::string getParameter (const std::string& name) = 0;
/**
* @return A string containing the codec specific parameters, formatted by default as :
* "PARAM_LIST : PARAM_NAME = VALUE SEMI_COLON PARAM_LIST | PARAM_END
* PARAM_END : empty"
*/
virtual std::string getParametersFormatted() {
// TODO Instead of putting everything into the same vector,
// enforce the required vs optional aspect. Unfilled required params. should
// result in exception throwing.
std::vector<std::string> paramList = requiredParameters;
std::copy(optionalParameters.begin(), optionalParameters.end(), std::back_inserter(paramList));
std::string output("");
std::vector<std::string>::iterator it;
int numberParamsAppended = 0;
for (it = paramList.begin(); it != paramList.end(); it++) {
std::string name = *it;
std::string value = getParameter(name);
if (value != "") {
if (numberParamsAppended != 0) {
output.append("; ");
}
output.append(name + "=" + value);
numberParamsAppended += 1;
}
}
return output;
}
/**
* Calls #setParameter() in a loop for every parameters of the format object.
* @param format The format object containing the parameters to set.
*/
void setParameters(const sfl::Fmtp& format) {
sfl::Fmtp::const_iterator it;
for (it = format.begin(); it != format.end(); it++) {
_info("****************** Setting parameters");
setParameter((*it).first, (*it).second);
}
}
void setNegotiatedParameters(const sfl::Fmtp& format) {
negotiatedFormat = format;
}
void applyNegotiatedParameters() {
setParameters(negotiatedFormat);
}
protected:
sfl::Fmtp negotiatedFormat;
/**
* @param name The name for the required parameter to add.
*/
void addRequiredParameter(const std::string& name) {
requiredParameters.push_back(name);
}
std::vector<std::string> requiredParameters;
/**
* @param name The name for the optional parameter to add.
*/
void addOptionalParameter(const std::string& name) {
optionalParameters.push_back(name);
}
std::vector<std::string> optionalParameters;
/**
* Negotiation handler type.
* @param localParam The local parameter.
* @param remoteParam The remote parameter.
* @param offerer The specified format for the offerer.
* @param answerer The specified format for the answerer.
* @param negotiatedFmtp The format in which to make changes, if any.
* @throw SdpFormatNegotiationException if the format can't be processed because of missing options,
* or wrong formatting.
*/
typedef void (*ParameterNegotiationHandler)(
const sfl::SdpParameter& localParam,
const sfl::SdpParameter& remoteParam,
const sfl::Fmtp& offerer,
const sfl::Fmtp& answerer,
sfl::Fmtp& negotiatedFmtp);
std::map<std::string, ParameterNegotiationHandler> parameterHandlerTable;
typedef std::pair<std::string, ParameterNegotiationHandler> ParameterHandlerEntry;
typedef std::map<std::string, ParameterNegotiationHandler>::iterator ParameterHandlerIterator;
/**
* Add a negotiation handler for the given property.
* @param name The parameter name (eg: "profile-level-id")
* @param handler A pointer to the negotiation handler function;
*/
void addHandler(const std::string& name, ParameterNegotiationHandler handler) {
parameterHandlerTable.insert(ParameterHandlerEntry(name, handler));
}
public:
/**
* The default behavior if this method is not overriden is to return the format from
* the answerer.
* @param offerer The specified format for the offerer.
* @param answerer The specified format for the answerer.
* @return A format object containing the negotiated format.
* @throw SdpFormatNegotiationException if the format can't be processed because of missing options,
* or wrong formatting.
*/
virtual sfl::Fmtp negotiate(const sfl::Fmtp& offerer, const sfl::Fmtp& answerer) throw(SdpFormatNegotiationException) {
sfl::Fmtp::iterator itAnswerer;
// This object will be built iteratively
sfl::Fmtp negotiatedFmtp(offerer.getPayloadType());
sfl::Fmtp::const_iterator it;
// Iterate over all of the properties in the answer
for (it = answerer.begin(); it != answerer.end(); it++) {
std::string propertyName = (*it).first;
std::string valueAnswerer = (*it).second;
// Find the corresponding property in the offer
std::string valueOfferer;
sfl::Fmtp::const_iterator itOfferer = offerer.getParameter(propertyName);
if (it == offerer.end()) {
valueOfferer = "";
} else {
valueOfferer = (*itOfferer).second;
}
// Find a suitable property handler
ParameterHandlerIterator itHandler = parameterHandlerTable.find(propertyName);
if (itHandler != parameterHandlerTable.end()) {
((*itHandler).second)(sfl::SdpParameter(propertyName, valueOfferer),
sfl::SdpParameter(propertyName, valueAnswerer),
offerer, answerer, negotiatedFmtp);
} else {
_error("Could not find any handler for property \"%s\"", propertyName.c_str());
}
}
return negotiatedFmtp;
}
protected:
/**
* This handler makes
*/
static void addParameterIfEqual(
const sfl::SdpParameter& localParam,
const sfl::SdpParameter& remoteParam,
const sfl::Fmtp& offerer,
const sfl::Fmtp& answerer,
sfl::Fmtp& negotiatedFmtp)
{
if (localParam.getValue() == remoteParam.getValue()) {
negotiatedFmtp[localParam.getName()] = localParam.getValue();
}
}
static void addParameterMinimum(
const sfl::SdpParameter& localParam,
const sfl::SdpParameter& remoteParam,
const sfl::Fmtp& offerer,
const sfl::Fmtp& answerer,
sfl::Fmtp& negotiatedFmtp)
{
long int localValue = strtol(localParam.getValue().c_str(), NULL, 16);
if (localValue == 0 && errno == EINVAL) {
// Throw
}
long int remoteValue = strtol(remoteParam.getValue().c_str(), NULL, 16);
if (remoteValue == 0 && errno == EINVAL) {
// Throw
}
long int minimumValue = std::min(localValue, remoteValue);
negotiatedFmtp[localParam.getName()] = minimumValue;
}
static void addParameterRemote(
const sfl::SdpParameter& localParam,
const sfl::SdpParameter& remoteParam,
const sfl::Fmtp& offerer,
const sfl::Fmtp& answerer,
sfl::Fmtp& negotiatedFmtp)
{
negotiatedFmtp[remoteParam.getName()] = remoteParam.getValue();
}
};
}
#endif
......@@ -130,7 +130,7 @@ void AudioRtpRecordHandler::setRtpMedia (AudioCodec* audioCodec)
// Set varios codec info to reduce indirection
_audioRtpRecord._audioCodec = audioCodec;
_audioRtpRecord._codecPayloadType = audioCodec->getPayload();
_audioRtpRecord._codecPayloadType = audioCodec->getPayloadType();
_audioRtpRecord._codecSampleRate = audioCodec->getClockRate();
_audioRtpRecord._codecFrameSize = audioCodec->getFrameSize();
_audioRtpRecord._hasDynamicPayloadType = audioCodec->hasDynamicPayload();
......@@ -151,7 +151,7 @@ void AudioRtpRecordHandler::updateRtpMedia (AudioCodec *audioCodec)
}
_audioRtpRecord._audioCodec = audioCodec;
_audioRtpRecord._codecPayloadType = audioCodec->getPayload();
_audioRtpRecord._codecPayloadType = audioCodec->getPayloadType();
_audioRtpRecord._codecSampleRate = audioCodec->getClockRate();
_audioRtpRecord._codecFrameSize = audioCodec->getFrameSize();
_audioRtpRecord._hasDynamicPayloadType = audioCodec->hasDynamicPayload();
......@@ -297,7 +297,7 @@ int AudioRtpRecordHandler::processDataEncode (void)
_audioRtpRecord.audioCodecMutex.enter();
compSize = _audioRtpRecord._audioCodec->codecEncode (micDataEncoded, micDataConverted, nbSample * sizeof (SFLDataFormat));
compSize = _audioRtpRecord._audioCodec->encode (micDataEncoded, micDataConverted, nbSample * sizeof (SFLDataFormat));
_audioRtpRecord.audioCodecMutex.leave();
......@@ -313,7 +313,7 @@ int AudioRtpRecordHandler::processDataEncode (void)
_audioRtpRecord.audioCodecMutex.enter();
// no resampling required
compSize = _audioRtpRecord._audioCodec->codecEncode (micDataEncoded, micData, nbSample * sizeof (SFLDataFormat));
compSize = _audioRtpRecord._audioCodec->encode (micDataEncoded, micData, nbSample * sizeof (SFLDataFormat));
_audioRtpRecord.audioCodecMutex.leave();
}
......@@ -333,7 +333,7 @@ void AudioRtpRecordHandler::processDataDecode (unsigned char *spkrData, unsigned
_audioRtpRecord.audioCodecMutex.enter();
// Return the size of data in bytes
int expandedSize = _audioRtpRecord._audioCodec->codecDecode (spkrDataDecoded , spkrData , size);
int expandedSize = _audioRtpRecord._audioCodec->decode (spkrDataDecoded , spkrData , size);
_audioRtpRecord.audioCodecMutex.leave();
......
......@@ -2,7 +2,7 @@ include $(top_srcdir)/globals.mak
noinst_LTLIBRARIES = libcodecdescriptor.la
libcodecdescriptor_la_SOURCES = codecDescriptor.cpp
libcodecdescriptor_la_SOURCES = codecDescriptor.cpp audiocodec.cpp
if BUILD_GSM
GSM_LIB = libcodec_gsm.so
......@@ -10,6 +10,7 @@ libcodec_gsm_so_SOURCES = gsmcodec.cpp
libcodec_gsm_so_CFLAGS = -fPIC -g -Wall
libcodec_gsm_so_CXXFLAGS = -fPIC -g -Wall
libcodec_gsm_so_LDFLAGS = --shared -lc -lgsm
libcodec_gsm_so_LDADD = libcodecdescriptor.la
INSTALL_GSM_RULE = install-libcodec_gsm_so
endif
......@@ -19,6 +20,7 @@ libcodec_speex_nb_so_SOURCES = speexcodec_nb.cpp
libcodec_speex_nb_so_CFLAGS = -fPIC -g -Wall
libcodec_speex_nb_so_CXXFLAGS = -fPIC -g -Wall $(SPEEXDSP)
libcodec_speex_nb_so_LDFLAGS = --shared -lc -lspeex $(SPEEX_NIMP)
libcodec_speex_nb_so_LDADD = libcodecdescriptor.la
INSTALL_SPEEX_NB_RULE = install-libcodec_speex_nb_so
endif
......@@ -28,6 +30,7 @@ libcodec_speex_wb_so_SOURCES = speexcodec_wb.cpp
libcodec_speex_wb_so_CFLAGS = -fPIC -g -Wall
libcodec_speex_wb_so_CXXFLAGS = -fPIC -g -Wall $(SPEEXDSP)
libcodec_speex_wb_so_LDFLAGS = --shared -lc -lspeex $(SPEEX_NIMP)
libcodec_speex_wb_so_LDADD = libcodecdescriptor.la
INSTALL_SPEEX_WB_RULE = install-libcodec_speex_wb_so
endif
......@@ -37,6 +40,7 @@ libcodec_speex_ub_so_SOURCES = speexcodec_ub.cpp
libcodec_speex_ub_so_CFLAGS = -fPIC -g -Wall
libcodec_speex_ub_so_CXXFLAGS = -fPIC -g -Wall $(SPEEXDSP)
libcodec_speex_ub_so_LDFLAGS = --shared -lc -lspeex $(SPEEX_NIMP)
libcodec_speex_ub_so_LDADD = libcodecdescriptor.la
INSTALL_SPEEX_UB_RULE = install-libcodec_speex_ub_so
endif
......@@ -46,6 +50,7 @@ libcodec_celt_so_SOURCES = celtcodec.cpp
libcodec_celt_so_CFLAGS = -fPIC -g -Wall -DBUILD_CELT_91
libcodec_celt_so_CXXFLAGS = -fPIC -g -Wall -DBUILD_CELT_91
libcodec_celt_so_LDFLAGS = --shared -lc -lcelt0 $(CELT_NIMP)
libcodec_celt_so_LDADD = libcodecdescriptor.la
INSTALL_CELT_RULE = install-libcodec_celt_so
endif
......@@ -55,6 +60,7 @@ libcodec_celt_so_SOURCES = celtcodec.cpp
libcodec_celt_so_CFLAGS = -fPIC -g -Wall -DBUILD_CELT_71
libcodec_celt_so_CXXFLAGS = -fPIC -g -Wall -DBUILD_CELT_71
libcodec_celt_so_LDFLAGS = --shared -lc -lcelt0 $(CELT_NIMP)
libcodec_celt_so_LDADD = libcodecdescriptor.la
INSTALL_CELT_RULE = install-libcodec_celt_so
endif
......@@ -65,16 +71,19 @@ noinst_HEADERS = audiocodec.h codecDescriptor.h
libcodec_ulaw_so_SOURCES = ulaw.cpp
libcodec_ulaw_so_CFLAGS = -fPIC -g -Wall
libcodec_ulaw_so_CXXFLAGS = -fPIC -g -Wall
libcodec_ulaw_so_LDADD = libcodecdescriptor.la
libcodec_ulaw_so_LDFLAGS = --shared -lc
libcodec_alaw_so_SOURCES = alaw.cpp
libcodec_alaw_so_CFLAGS = -fPIC -g -Wall
libcodec_alaw_so_CXXFLAGS = -fPIC -g -Wall
libcodec_alaw_so_LDADD = libcodecdescriptor.la
libcodec_alaw_so_LDFLAGS = --shared -lc
libcodec_g722_so_SOURCES = g722.cpp
libcodec_g722_so_CFLAGS = -fPIC -g -Wall
libcodec_g722_so_CXXFLAGS = -fPIC -g -Wall
libcodec_g722_so_LDADD = libcodecdescriptor.la
libcodec_g722_so_LDFLAGS = --shared -lc
install-exec-local: install-libcodec_ulaw_so install-libcodec_alaw_so install-libcodec_g722_so $(INSTALL_GSM_RULE) $(INSTALL_SPEEX_NB_RULE) $(INSTALL_SPEEX_WB_RULE) $(INSTALL_SPEEX_UB_RULE) $(INSTALL_CELT_RULE) $(INSTALL_ILBC_RULE)
......
......@@ -49,7 +49,7 @@ class Alaw : public AudioCodec
virtual ~Alaw() {}
virtual int codecDecode (short *dst, unsigned char *src, unsigned int size) {
virtual int decode (short *dst, unsigned char *src, unsigned int size) {
int16* end = dst+size;
while (dst<end)
......@@ -58,7 +58,7 @@ class Alaw : public AudioCodec
return size<<1;
}
virtual int codecEncode (unsigned char *dst, short *src, unsigned int size) {
virtual int encode (unsigned char *dst, short *src, unsigned int size) {
size >>= 1;
uint8* end = dst+size;
......@@ -129,16 +129,30 @@ class Alaw : public AudioCodec
return a^0x55; // A-law has alternate bits inverted for transmission
}
/**
* @Override
*/
std::string getDescription() const {
return "audio/PCMA 8000 (\"alaw\") codec.";
}
/**
* @Override
*/
Alaw* clone() const {
return new Alaw (*this);
}
};
// the class factories
extern "C" AudioCodec* create()
extern "C" sfl::Codec* create()
{
return new Alaw (8);
}
extern <