A multimedia terminal may support multiple audio codec sets. For every logical channel there can be different codec than in other channels. This (management) module was developed to allow many codecs to be used simultaneously (e.g., while transmitting stereo signal, when the first stream may be coded using G.711 and the other using G.723.1), to make playing and recording threads unconscious of audio codecs used, and finally, to simplify process of selecting and changing audio codecs. Notice that if audio codec is compatible with the Codec Management module, it could not be used without his module. The way how to add a new codec is described in Section 2.4.3.
Playing and recording threads get a pointer to a codec structure as a parameter. This structure contains pointers to coding and decoding functions of particular codec as well as all parameters of a given codec. So Capturing and Playing Module thread just call the snd_encode or snd_decode function with a codec object (a pointer to a codec structure).
To select a particular codec (by the use of snd_get_coder and snd_get_decoder), a symbol of the codec is needed. The symbol type is the codec_symbol_t type (enumerated one). The following symbols are defined: g711Alaw64k , g711Alaw56k , g711Ulaw64k , g711Ulaw56k , g722_64k , g722_56k , g722_48k , g7231 , g728 , g729 , is11172AudioCapability , g729AnnexA , is13818AudioCapability . Most of them are not implemented yet. The mentioned functions will return NULL if called with the symbol of an unimplemented codec . The structure that describes codecs is called codec_struct, and the pointer to this structure is defined p_cd.
snd_init_codecs Initiates all global structures for codecs, allocates global tables of codecs (coders and decoders).
This function must be called BEFORE any other snd_ function is used.snd_get_coder and snd_get_decoder Select coder/decoder from the global set of coders/decoders.
When a p_cd pointer is returned by this call it can be used in other calls which require p_cd structure pointer. If coder/decoder cannot be selected this function returns NULL.snd_get_first_coder and snd_get_first_decoder Selects the first coder/decoder from the global set of codecs.
These functions may be used when the user does not care about which codec will be chosen, or to make a scanning over the codecs (used with the snd_get_next_ functions). The first case is a bad idea but until now this case is used in this project.snd_get_next_coder and snd_get_next_decoder Selects the next coder/decoder from the global set of coders/decoders.
These functions select the following codec than the one specified in the codec parameter. This codec is returned by the functions. The current codec (codec) if de-selected (like after calling snd_unget_codec()). If no more codecs are available NULL is returned, and the current codec is left untouched (is not de-selected).snd_unget_codec De-selects a codec (coder or decoder).
This function frees all data connected with the codec.snd_encode and snd_decode Encode/decode audio data.
int snd_encode(p_cd codec, void *input_buffer, void *output_buffer);
int snd_decode(p_cd codec, void *input_buffer, void *output_buffer);
codec in a codec to be used to encode/decode data.
in_buffer The input buffer. Points to a block (vector of samples) to be encoded/decoded.
out_buffer The output buffer. Points to a memory block allocated before, and is filled with encoded/decoded data after successful completion of the snd_encode or snd_decode function.
snd_get_input_block_len and snd_get_output_block_len Returns the length of the input/output data block.
This information is taken from the codec parameter.snd_add_coder and snd_add_decoder Add a new coder/decoder to the global set of coders/decoders.
The codec parameter points to a codec object that is to be added. After successful completion of this function the codec can be used by audio threads.snd_add_coders and snd_add_decoders Add a set of coders/decoders to the global set of coders/decoders.
The codec_p parameter points to a table of codecs finished with the NULL pointer.snd_get_number_of_coders and snd_get_number_of_decoders Returns the number of all coders/decoder ready to be used.
These numbers are taken from the global codec structure, allocated by the snd_init_codecs() function.Example 2-1 illustrates the possible scenario of using codec_mngmnt.
#include <stdio.h>
#include "codec_mngmnt.h"
codec_example()
{
snd_init_codecs(); /* This must be called before any
other snd_ function is called */
snd_add_coders( g711_capabilities() );
snd_add_decoders( g711_capabilities() );
/* These add features from g711 module. Every particular
codec module must be initited in a such way */
printf("Number of coders: %d\n", snd_get_number_of_coders());
printf("Number of decoders: %d\n", snd_get_number_of_decoders());
printf("Getting decoders..\n");
tmp_c = snd_get_first_decoder();
if(!tmp_c)
printf("Cannot get decoder (no decoders present?).");
else
printf("Decoder symbol: %s (%d), name : %s\n",
tmp_c->string_symbol,
tmp_c->symbol, tmp_c->name);
while(tmp_c = snd_get_next_decoder(tmp_c))
printf("Decoder symbol: %s (%d), name : %s\n",
tmp_c->string_symbol,
tmp_c->symbol, tmp_c->name);
snd_unget_codec(tmp_c);
}
Example 2-1. Codec Management Example
To add a new codec to the group of supported codecs the following steps must be performed:
First, the codec must be written down. It must support the interface similar to: encode(void *input, void *output, ...) and decode(void *input, void *output, ...). The first step is the transformation the above interface into:
Now codec must recognize the fields from p_cd codec_ptr. The structure is defined as follows:
struct codec_struct {
enum codec_symbol_t symbol;
char *string_symbol;
char *name; /* in case of named codec */
int bit_rate; /* in bits per second */
float quality; /* scale: 0-5 */
unsigned int dynamic : 1;
unsigned int active : 1;
unsigned int number : 6; /* up to 64 */
int (*setparam)(p_cd, void *param_struct);
int (*encode)(p_cd, void *input, void *output);
int (*decode)(p_cd, void *input, void *output);
struct local_params {
void *param_struct;
int input_buf_len;
int output_buf_len;
} *lp;
};
So the parameters of the current coder or decoder are taken from
this structure (especially from lp). Every codec type
can support its own parameters in lp->param_struct.
The length of the input buffer and output buffer can be stored in
ld->input_buf_len and
ld->output_buf_len, respectively.
The function that fills the structure properly must be written down. symbol is the symbol of the codec. If this symbol cannot be found in enum codec_symbol_t table in codec_mngmnt.h it should be added manually to the list. string_symbol is used to show the readable name to the user. name may be used to add additional description for the codec; if not it should be filled with NULL. bit_rate is the bit-rate generated per second for encoded stream. quality is a quality of the sound (in the codec author's opinion). The scale if from 0 to 5. 64-bit G.711 would have this parameter well over 4, very-low-rate vocoders would have 2 or less. dynamic should be 0, active should be 1. encode should be set as the pointer to encode function prepared earlier, and decode function should be set as decode one. setparam function should be written by the author for the given codec (it sets a special parameters). The parameter structure pointer would be cast into void* and stored in ld->param_struct
The function p_cd *CODEC_NAME_capabilities must be written down. Refer G.711 module (g711.c) for an example.
The following lines should be added at the bottom of snd_init_codecs (in file codec_mngmnt.c):
snd_add_coders(CODEC_NAME_capabilities()); snd_add_decoders(CODEC_NAME_capabilities());
The codec should be tested whether it can be selected, de-selected and re-selected. The codec test suite is included in test_codec_management.c. Feel free to modify it.