Added Opus support. 2nd part

This commit is contained in:
rafael@riseup.net 2013-05-14 16:25:16 +00:00
parent ef5ba44013
commit 9cecd3f493
2 changed files with 1112 additions and 0 deletions

View File

@ -0,0 +1,551 @@
/*------------------------------------------------------------------------------
Copyright (c) 2000-2007 Tyrell Corporation. All rights reserved.
Tyrell DarkIce
File : OpusLibEncoder.cpp
Version : $Revision$
Author : $Author$
Location : $HeadURL$
Copyright notice:
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
------------------------------------------------------------------------------*/
/* ============================================================ include files */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
// compile only if configured for Ogg / Opus
#ifdef HAVE_OPUS_LIB
#include "Exception.h"
#include "Util.h"
#include "OpusLibEncoder.h"
#include "CastSink.h"
/* =================================================== local data structures */
/* ================================================ local constants & macros */
/*------------------------------------------------------------------------------
* File identity
*----------------------------------------------------------------------------*/
static const char fileid[] = "$Id$";
/* =============================================== local function prototypes */
/* ============================================================= module code */
/*------------------------------------------------------------------------------
* Initialize the encoder
*----------------------------------------------------------------------------*/
void
OpusLibEncoder :: init ( unsigned int outMaxBitrate )
throw ( Exception )
{
this->outMaxBitrate = outMaxBitrate;
if ( getInBitsPerSample() != 16 && getInBitsPerSample() != 8 ) {
throw Exception( __FILE__, __LINE__,
"specified bits per sample not supported",
getInBitsPerSample() );
}
if ( getInChannel() != 1 && getInChannel() != 2 ) {
throw Exception( __FILE__, __LINE__,
"unsupported number of channels for the encoder",
getInChannel() );
}
if ( getOutSampleRate() != 48000 ) {
throw Exception( __FILE__, __LINE__,
"unsupported sample rate for this encoder",
getOutSampleRate() );
}
if ( getOutSampleRate() == getInSampleRate() ) {
resampleRatio = 1;
converter = 0;
} else {
resampleRatio = ( (double) getOutSampleRate() /
(double) getInSampleRate() );
// Determine if we can use linear interpolation.
// The inverse of the ratio must be a power of two for linear mode to
// be of sufficient quality.
bool useLinear = true;
double inverse = 1 / resampleRatio;
int integer = (int) inverse;
// Check that the inverse of the ratio is an integer
if( integer == inverse ) {
while( useLinear && integer ) { // Loop through the bits
// If the lowest order bit is not the only one set
if( integer & 1 && integer != 1 ) {
// Not a power of two; cannot use linear
useLinear = false;
} else {
// Shift all the bits over and try again
integer >>= 1;
}
}
} else {
useLinear = false;
}
// If we get here and useLinear is still true, then we have
// a power of two.
// open the aflibConverter in
// - high quality
// - linear or quadratic (non-linear) based on algorithm
// - not filter interpolation
#ifdef HAVE_SRC_LIB
int srcError = 0;
converter = src_new(useLinear == true ? SRC_LINEAR : SRC_SINC_FASTEST,
getInChannel(), &srcError);
if(srcError)
throw Exception (__FILE__, __LINE__, "libsamplerate error: ", src_strerror (srcError));
#else
converter = new aflibConverter( true, useLinear, false);
#endif
}
int bufferSize = (getInBitsPerSample()/8) * getInChannel() * 480;
internalBuffer = new unsigned char[bufferSize];
internalBufferLength = 0;
memset( internalBuffer, 0, bufferSize);
encoderOpen = false;
}
/*------------------------------------------------------------------------------
* Open an encoding session
*----------------------------------------------------------------------------*/
bool
OpusLibEncoder :: open ( void )
throw ( Exception )
{
int ret;
if ( isOpen() ) {
close();
}
// open the underlying sink
if ( !getSink()->open() ) {
throw Exception( __FILE__, __LINE__,
"opus lib opening underlying sink error");
}
int err;
opusEncoder = opus_encoder_create( getOutSampleRate(),
getInChannel(),
OPUS_APPLICATION_AUDIO,
&err);
if( err != OPUS_OK ) {
throw Exception( __FILE__, __LINE__,
"opus encoder creation error",
err);
}
opus_encoder_ctl(opusEncoder, OPUS_SET_COMPLEXITY(10));
opus_encoder_ctl(opusEncoder, OPUS_SET_SIGNAL(OPUS_SIGNAL_MUSIC));
switch ( getOutBitrateMode() ) {
case cbr: {
int maxBitrate = getOutMaxBitrate() * 1000;
if ( !maxBitrate ) {
maxBitrate = 96000;
}
opus_encoder_ctl(opusEncoder, OPUS_SET_BITRATE(maxBitrate));
opus_encoder_ctl(opusEncoder, OPUS_SET_VBR(0));
} break;
case abr: {
int maxBitrate = getOutMaxBitrate() * 1000;
if ( !maxBitrate ) {
maxBitrate = 96000;
}
/* set non-managed VBR around the average bitrate */
opus_encoder_ctl(opusEncoder, OPUS_SET_BITRATE(maxBitrate));
opus_encoder_ctl(opusEncoder, OPUS_SET_VBR(1));
opus_encoder_ctl(opusEncoder, OPUS_SET_VBR_CONSTRAINT(0));
} break;
case vbr:
throw Exception( __FILE__, __LINE__, "vbr is not supported; use cbr/abr");
}
if ( (ret = ogg_stream_init( &oggStreamState, 0)) ) {
throw Exception( __FILE__, __LINE__, "ogg stream init error", ret);
}
// First, we need to assemble and send a OggOpus header.
OpusIdHeader header;
strncpy(header.magic, "OpusHead", 8);
header.version = 1;
header.channels = getOutChannel();
header.preskip = 0;
header.samplerate = getInSampleRate();
header.gain = 0; // technically a fixed-point decimal.
header.chanmap = 0;
// And, now we need to send a Opus comment header.
// Anything after this can be audio.
char vendor[8] = "darkice";
char titlestr[7] = "TITLE=";
OpusCommentHeader::Tags tags[1];
char name[40];
CastSink* sink = dynamic_cast<CastSink*>(getSink().get());
if( sink && sink->getName() ) {
strncpy(name, (char*)sink->getName(), 39);
name[39] = 0;
}
else {
strncpy(name, "Darkice Stream", 39);
}
tags[0].tag_len = strlen(titlestr) + strlen(name);
tags[0].tag_str = (char*) malloc( tags[0].tag_len );
if( tags[0].tag_str == NULL ) {
throw Exception( __FILE__, __LINE__, "malloc failed");
}
strncpy( tags[0].tag_str, titlestr, tags[0].tag_len);
strncat( tags[0].tag_str, name, tags[0].tag_len);
OpusCommentHeader commentHeader;
strncpy(commentHeader.magic, "OpusTags", 8);
commentHeader.vendor_length = strlen(vendor);
commentHeader.vendor_string = vendor;
commentHeader.num_tags = 1;
commentHeader.tags = tags;
ogg_packet oggHeader;
ogg_packet oggCommentHeader;
memset(&oggHeader, 0, sizeof(oggHeader));
memset(&oggCommentHeader, 0, sizeof(oggCommentHeader));
unsigned char* headerData = NULL;
unsigned char* commentData = NULL;
int headerLen = 0;
int commentLen = 0;
headerLen = header.buildPacket( &headerData);
commentLen = commentHeader.buildPacket( &commentData);
oggHeader.packet = headerData;
oggHeader.bytes = headerLen;
oggHeader.b_o_s = 1;
oggHeader.e_o_s = 0;
oggHeader.granulepos = 0;
oggHeader.packetno = 0;
oggCommentHeader.packet = commentData;
oggCommentHeader.bytes = commentLen;
oggCommentHeader.b_o_s = 0;
oggCommentHeader.e_o_s = 0;
oggCommentHeader.granulepos = 0;
oggCommentHeader.packetno = 1;
oggPacketNumber = 2;
oggGranulePosition = 0;
ogg_stream_packetin( &oggStreamState, &oggHeader);
ogg_stream_packetin( &oggStreamState, &oggCommentHeader);
ogg_page oggPage;
while ( ogg_stream_flush( &oggStreamState, &oggPage) ) {
getSink()->write( oggPage.header, oggPage.header_len);
getSink()->write( oggPage.body, oggPage.body_len);
}
free(tags[0].tag_str);
free(headerData);
free(commentData);
// initialize the resampling coverter if needed
if ( converter ) {
#ifdef HAVE_SRC_LIB
converterData.input_frames = 4096/((getInBitsPerSample() / 8) * getInChannel());
converterData.data_in = new float[converterData.input_frames*getInChannel()];
converterData.output_frames = (int) (converterData.input_frames * resampleRatio + 1);
converterData.data_out = new float[getInChannel() * converterData.output_frames];
converterData.src_ratio = resampleRatio;
converterData.end_of_input = 0;
#else
converter->initialize( resampleRatio, getInChannel());
#endif
}
encoderOpen = true;
return true;
}
/*------------------------------------------------------------------------------
* Write data to the encoder
*----------------------------------------------------------------------------*/
unsigned int
OpusLibEncoder :: write ( const void * buf,
unsigned int len ) throw ( Exception )
{
if ( !isOpen() || len == 0 ) {
return 0;
}
unsigned int channels = getInChannel();
unsigned int bitsPerSample = getInBitsPerSample();
unsigned int sampleSize = (bitsPerSample / 8) * channels;
unsigned int i;
if ( getInChannel() == 2 && getOutChannel() == 1 ) {
for ( i = 0; i < len/sampleSize; i++) {
if ( bitsPerSample == 8 ) {
char * buf8 = (char *) buf;
unsigned int ix = sampleSize * i;
unsigned int iix = ix;
buf8[i] = (buf8[ix] + buf8[++iix]) / 2;
}
if ( bitsPerSample == 16 ) {
short * buf16 = (short *) buf;
unsigned int ix = (bitsPerSample >> 3) * i;
unsigned int iix = ix;
buf16[i] = (buf16[ix] + buf16[++iix]) / 2;
}
}
len >>= 1;
channels = 1;
}
sampleSize = (bitsPerSample / 8) * channels;
unsigned int processed = 0;
unsigned int bytesToProcess = len - (len % sampleSize);
unsigned int totalProcessed = 0;
unsigned char * b = (unsigned char*) buf;
unsigned char * tempBuffer = NULL;
if( internalBufferLength > 0 ) {
tempBuffer = new unsigned char[len + internalBufferLength];
memset( tempBuffer, 0, len + internalBufferLength);
if( !tempBuffer ) {
throw Exception( __FILE__, __LINE__, "could not allocate temp buffer");
}
memcpy( tempBuffer, internalBuffer, internalBufferLength);
memcpy( tempBuffer+internalBufferLength, buf, len);
b = tempBuffer;
bytesToProcess += internalBufferLength;
}
while ( bytesToProcess / resampleRatio >= 480 * sampleSize ) {
unsigned int toProcess = bytesToProcess / (resampleRatio * sampleSize);
if( toProcess >= 480 / resampleRatio) {
processed = 480 / resampleRatio;
}
int opusBufferSize = (1275*3+7)*channels;
unsigned char* opusBuffer = new unsigned char[opusBufferSize];
// convert the byte-based raw input into a short buffer
// with channels still interleaved
unsigned int totalSamples = processed * channels;
short int * shortBuffer = new short int[totalSamples];
Util::conv( bitsPerSample, b, processed*sampleSize, shortBuffer, isInBigEndian());
if ( converter && processed > 0 ) {
// resample if needed
int inCount = processed;
int outCount = 480; //(int) (inCount * resampleRatio);
short int * resampledBuffer = new short int[(outCount+1)* channels];
int converted;
#ifdef HAVE_SRC_LIB
(void)inCount;
converterData.input_frames = processed;
src_short_to_float_array (shortBuffer, converterData.data_in, totalSamples);
int srcError = src_process (converter, &converterData);
if (srcError)
throw Exception (__FILE__, __LINE__, "libsamplerate error: ", src_strerror (srcError));
converted = converterData.output_frames_gen;
src_float_to_short_array(converterData.data_out, resampledBuffer, converted*channels);
#else
converted = converter->resample( inCount,
outCount,
shortBuffer,
resampledBuffer );
#endif
if( converted != 480) {
throw Exception( __FILE__, __LINE__, "resampler error: expected 480 samples", converted);
}
int encBytes = opus_encode( opusEncoder, resampledBuffer, 480, opusBuffer, opusBufferSize);
if( encBytes == -1 ) {
throw Exception( __FILE__, __LINE__, "opus encoder error");
}
oggGranulePosition += converted;
opusBlocksOut( encBytes, opusBuffer);
delete[] resampledBuffer;
} else if( processed > 0) {
memset( opusBuffer, 0, opusBufferSize);
int encBytes = opus_encode( opusEncoder, shortBuffer, processed, opusBuffer, opusBufferSize);
if( encBytes == -1 ) {
throw Exception( __FILE__, __LINE__, "opus encoder error");
}
oggGranulePosition += processed;
opusBlocksOut( encBytes, opusBuffer);
}
delete[] shortBuffer;
delete[] opusBuffer;
bytesToProcess -= processed * sampleSize;
totalProcessed += processed * sampleSize;
b = ((unsigned char*)b) + (processed * sampleSize);
}
int newLen = len - (len % sampleSize) + internalBufferLength - totalProcessed;
newLen -= newLen % sampleSize;
if( newLen > 0 ) {
memcpy( internalBuffer, b, newLen);
totalProcessed += newLen;
internalBufferLength = newLen;
} else {
internalBufferLength = 0;
}
if( tempBuffer ) {
delete[] tempBuffer;
tempBuffer = NULL;
}
return totalProcessed;
}
/*------------------------------------------------------------------------------
* Flush the data from the encoder
*----------------------------------------------------------------------------*/
void
OpusLibEncoder :: flush ( void )
throw ( Exception )
{
if ( !isOpen() ) {
return;
}
int opusBufferSize = (1275*3+7)*getOutChannel();
unsigned char * opusBuffer = new unsigned char[opusBufferSize];
short int * shortBuffer = new short int[480];
// Send an empty audio packet along to flush out the stream.
memset( shortBuffer, 0, 480);
memset( opusBuffer, 0, opusBufferSize);
int encBytes = opus_encode( opusEncoder, shortBuffer, 480, opusBuffer, opusBufferSize);
if( encBytes == -1 ) {
throw Exception( __FILE__, __LINE__, "opus encoder error");
}
oggGranulePosition += 480;
// Send the empty block to the Ogg layer, and mark the
// EOS flag. This will trigger any remaining packets to be
// sent.
opusBlocksOut( encBytes, opusBuffer, true);
delete[] opusBuffer;
delete[] shortBuffer;
getSink()->flush();
}
/*------------------------------------------------------------------------------
* Send pending Opus blocks to the underlying stream
*----------------------------------------------------------------------------*/
void
OpusLibEncoder :: opusBlocksOut ( int bytes,
unsigned char* data,
bool eos ) throw ( Exception )
{
ogg_packet oggPacket;
ogg_page oggPage;
oggPacket.packet = data;
oggPacket.bytes = bytes;
oggPacket.b_o_s = 0;
oggPacket.e_o_s = ( eos ) ? 1 : 0;
oggPacket.granulepos = oggGranulePosition;
oggPacket.packetno = oggPacketNumber;
oggPacketNumber++;
if( ogg_stream_packetin( &oggStreamState, &oggPacket) == 0) {
while( ogg_stream_pageout( &oggStreamState, &oggPage) ||
( eos && ogg_stream_flush( &oggStreamState, &oggPage) ) ) {
int written;
written = getSink()->write(oggPage.header, oggPage.header_len);
written += getSink()->write( oggPage.body, oggPage.body_len);
if ( written < oggPage.header_len + oggPage.body_len ) {
// just let go data that could not be written
reportEvent( 2,
"couldn't write full opus data to underlying sink",
oggPage.header_len + oggPage.body_len - written);
}
}
} else {
throw Exception( __FILE__, __LINE__, "internal ogg error");
}
}
/*------------------------------------------------------------------------------
* Close the encoding session
*----------------------------------------------------------------------------*/
void
OpusLibEncoder :: close ( void ) throw ( Exception )
{
if ( isOpen() ) {
flush();
ogg_stream_clear( &oggStreamState);
opus_encoder_destroy( opusEncoder);
opusEncoder = NULL;
encoderOpen = false;
delete[] internalBuffer;
getSink()->close();
}
}
#endif // HAVE_OPUS_LIB

View File

@ -0,0 +1,561 @@
/*------------------------------------------------------------------------------
Copyright (c) 2000-2007 Tyrell Corporation. All rights reserved.
Tyrell DarkIce
File : OpusLibEncoder.h
Version : $Revision$
Author : $Author$
Location : $HeadURL$
Copyright notice:
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
------------------------------------------------------------------------------*/
#ifndef OPUS_LIB_ENCODER_H
#define OPUS_LIB_ENCODER_H
#ifndef __cplusplus
#error This is a C++ include file
#endif
/* ============================================================ include files */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef HAVE_OPUS_LIB
#include <opus/opus.h>
#include <ogg/ogg.h>
#else
#error configure for Ogg Opus
#endif
#include "Ref.h"
#include "Exception.h"
#include "Reporter.h"
#include "AudioEncoder.h"
#include "CastSink.h"
#ifdef HAVE_SRC_LIB
#include <samplerate.h>
#else
#include "aflibConverter.h"
#endif
/* ================================================================ constants */
/* =================================================================== macros */
/* =============================================================== data types */
/**
* A struct for containing the ogg opus header format, sent at the stream
* beginning.
*
* @author $Author$
* @version $Revision$
*/
struct OpusIdHeader {
char magic[8]; /* "OpusHead" */
unsigned char version; /* Version (currently 1) */
unsigned char channels; /* Number of channels */
unsigned short preskip; /* Pre-skip */
unsigned int samplerate; /* Original samplerate; informational only */
unsigned short gain; /* Output gain, stored in Q7.8 in dB */
unsigned char chanmap; /* 0 = mono or stereo L/R, 1=vorbis spec order, 2..254=reserved, 255=undef */
/**
* Build an Ogg packet from the header.
* @param packet output pointer, must be freed by caller
*
* @return number of bytes in packet.
*/
inline int buildPacket( unsigned char** packet) throw ( Exception ) {
int i = 0;
// FIXME - doesn't support multistream
unsigned char* out = (unsigned char*)malloc(15);
if( out == NULL ) {
throw Exception( __FILE__, __LINE__, "cannot alloc buffer");
}
for( ; i < 8; i++) {
out[i] = magic[i];
}
out[i++] = version;
out[i++] = channels;
out[i++] = preskip & 0xff;
out[i++] = (preskip >> 8) & 0xff;
out[i++] = samplerate & 0xff;
out[i++] = (samplerate >> 8) & 0xff;
out[i++] = (samplerate >> 16) & 0xff;
out[i++] = (samplerate >> 24) & 0xff;
out[i++] = gain & 0xff;
out[i++] = (gain >> 8) & 0xff;
out[i++] = chanmap;
*packet = out;
return i;
}
};
/**
* A struct for containing the ogg opus comment header format
*
* @author $Author$
* @version $Revision$
*/
struct OpusCommentHeader {
/**
* Struct for each tag pair, in the form of a UTF-8
* string, "TAG=value"
*
* @author $Author$
* @version $Revision$
*/
struct Tags {
unsigned int tag_len; /* For each tag, how long the following tag is */
char* tag_str; /* For each tag, UTF-8 encoded string, in tag=value */
};
char magic[8]; /* "OpusTags" */
unsigned int vendor_length; /* Length of the vendor string */
char* vendor_string; /* Vendor string -- parsed as utf-8 */
unsigned int num_tags; /* Number of tags following */
Tags* tags; /* Pointer to allocated tag array */
/**
* Build an Ogg packet from the header.
* @param packet output pointer, must be freed by caller
*
* @return number of bytes in packet.
*/
inline int buildPacket( unsigned char** packet) throw ( Exception ) {
int len = 8 + sizeof(unsigned int) * 2 + vendor_length;
int pos = 0;
for( unsigned int i = 0; i < num_tags; i++ )
{
len += sizeof(unsigned int) + tags[i].tag_len;
}
unsigned char* out = (unsigned char*)malloc(len);
if( out == NULL ) {
throw Exception( __FILE__, __LINE__, "cannot alloc buffer");
}
for( ; pos < 8; pos++) {
out[pos] = magic[pos];
}
out[pos++] = vendor_length & 0xff;
out[pos++] = (vendor_length >> 8) & 0xff;
out[pos++] = (vendor_length >> 16) & 0xff;
out[pos++] = (vendor_length >> 24) & 0xff;
for( unsigned int i = 0; i < vendor_length; i++ ) {
out[pos++] = vendor_string[i];
}
out[pos++] = num_tags & 0xff;
out[pos++] = (num_tags >> 8) & 0xff;
out[pos++] = (num_tags >> 16) & 0xff;
out[pos++] = (num_tags >> 24) & 0xff;
for( unsigned int i = 0; i < num_tags; i++ ) {
out[pos++] = tags[i].tag_len & 0xff;
out[pos++] = (tags[i].tag_len >> 8) & 0xff;
out[pos++] = (tags[i].tag_len >> 16) & 0xff;
out[pos++] = (tags[i].tag_len >> 24) & 0xff;
for( unsigned int j = 0; j < tags[i].tag_len; j++ ) {
out[pos++] = tags[i].tag_str[j];
}
}
*packet = out;
return len;
}
};
/**
* A class representing the ogg opus encoder linked as a shared object or
* as a static library.
*
* @author $Author$
* @version $Revision$
*/
class OpusLibEncoder : public AudioEncoder, public virtual Reporter
{
private:
/**
* Value indicating if the encoding process is going on
*/
bool encoderOpen;
/**
* Ogg Opus library global info
*/
OpusEncoder* opusEncoder;
/**
* Ogg library global stream state
*/
ogg_stream_state oggStreamState;
ogg_int64_t oggGranulePosition;
ogg_int64_t oggPacketNumber;
unsigned char* internalBuffer;
int internalBufferLength;
/**
* Maximum bitrate of the output in kbits/sec. If 0, don't care.
*/
unsigned int outMaxBitrate;
/**
* Resample ratio
*/
double resampleRatio;
/**
* sample rate converter object for possible resampling
*/
#ifdef HAVE_SRC_LIB
SRC_STATE * converter;
SRC_DATA converterData;
#else
aflibConverter * converter;
#endif
/**
* Initialize the object.
*
* @param the maximum bit rate
* @exception Exception
*/
void
init ( unsigned int outMaxBitrate ) throw ( Exception );
/**
* De-initialize the object.
*
* @exception Exception
*/
inline void
strip ( void ) throw ( Exception )
{
if ( converter ) {
#ifdef HAVE_SRC_LIB
delete [] converterData.data_in;
delete [] converterData.data_out;
src_delete (converter);
#else
delete converter;
#endif
}
}
/**
* Send pending Opus blocks to the underlying stream
*/
void
opusBlocksOut( int bytes,
unsigned char* data,
bool eos = false ) throw ( Exception );
protected:
/**
* Default constructor. Always throws an Exception.
*
* @exception Exception
*/
inline
OpusLibEncoder ( void ) throw ( Exception )
{
throw Exception( __FILE__, __LINE__);
}
public:
/**
* Constructor.
*
* @param sink the sink to send encoded output to
* @param inSampleRate sample rate of the input.
* @param inBitsPerSample number of bits per sample of the input.
* @param inChannel number of channels of the input.
* @param inBigEndian shows if the input is big or little endian
* @param outBitrateMode the bit rate mode of the output.
* @param outBitrate bit rate of the output (kbits/sec).
* @param outQuality the quality of the stream.
* @param outSampleRate sample rate of the output.
* If 0, inSampleRate is used.
* @param outMaxBitrate maximum output bitrate.
* 0 if not used.
* @param outChannel number of channels of the output.
* If 0, inChannel is used.
* @exception Exception
*/
inline
OpusLibEncoder ( CastSink * sink,
unsigned int inSampleRate,
unsigned int inBitsPerSample,
unsigned int inChannel,
bool inBigEndian,
BitrateMode outBitrateMode,
unsigned int outBitrate,
double outQuality,
unsigned int outSampleRate = 0,
unsigned int outChannel = 0,
unsigned int outMaxBitrate = 0 )
throw ( Exception )
: AudioEncoder ( sink,
inSampleRate,
inBitsPerSample,
inChannel,
inBigEndian,
outBitrateMode,
outBitrate,
outQuality,
outSampleRate,
outChannel )
{
init( outMaxBitrate);
}
/**
* Constructor.
*
* @param sink the sink to send encoded output to
* @param as get input sample rate, bits per sample and channels
* from this AudioSource.
* @param outBitrateMode the bit rate mode of the output.
* @param outBitrate bit rate of the output (kbits/sec).
* @param outQuality the quality of the stream.
* @param outSampleRate sample rate of the output.
* If 0, input sample rate is used.
* @param outMaxBitrate maximum output bitrate.
* 0 if not used.
* @param outChannel number of channels of the output.
* If 0, input channel is used.
* @exception Exception
*/
inline
OpusLibEncoder ( CastSink * sink,
const AudioSource * as,
BitrateMode outBitrateMode,
unsigned int outBitrate,
double outQuality,
unsigned int outSampleRate = 0,
unsigned int outChannel = 0,
unsigned int outMaxBitrate = 0 )
throw ( Exception )
: AudioEncoder ( sink,
as,
outBitrateMode,
outBitrate,
outQuality,
outSampleRate,
outChannel )
{
init( outMaxBitrate);
}
/**
* Copy constructor.
*
* @param encoder the OpusLibEncoder to copy.
*/
inline
OpusLibEncoder ( const OpusLibEncoder & encoder )
throw ( Exception )
: AudioEncoder( encoder )
{
if( encoder.isOpen() ) {
throw Exception(__FILE__, __LINE__, "don't copy open encoders");
}
init( encoder.getOutMaxBitrate() );
}
/**
* Destructor.
*
* @exception Exception
*/
inline virtual
~OpusLibEncoder ( void ) throw ( Exception )
{
if ( isOpen() ) {
close();
}
strip();
}
/**
* Assignment operator.
*
* @param encoder the OpusLibEncoder to assign this to.
* @return a reference to this OpusLibEncoder.
* @exception Exception
*/
inline virtual OpusLibEncoder &
operator= ( const OpusLibEncoder & encoder ) throw ( Exception )
{
if( encoder.isOpen() ) {
throw Exception(__FILE__, __LINE__, "don't copy open encoders");
}
if ( this != &encoder ) {
strip();
AudioEncoder::operator=( encoder);
init( encoder.getOutMaxBitrate() );
}
return *this;
}
/**
* Get the maximum bit rate of the output in kbits/sec,
* for fixed / average bitrate encodings.
*
* @return the maximum bit rate of the output, or 0 if not set.
*/
inline unsigned int
getOutMaxBitrate ( void ) const throw ()
{
return outMaxBitrate;
}
/**
* Check wether encoding is in progress.
*
* @return true if encoding is in progress, false otherwise.
*/
inline virtual bool
isRunning ( void ) const throw ()
{
return isOpen();
}
/**
* Start encoding. This function returns as soon as possible,
* with encoding started in the background.
*
* @return true if encoding has started, false otherwise.
* @exception Exception
*/
inline virtual bool
start ( void ) throw ( Exception )
{
return open();
}
/**
* Stop encoding. Stops the encoding running in the background.
*
* @exception Exception
*/
inline virtual void
stop ( void ) throw ( Exception )
{
return close();
}
/**
* Open an encoding session.
*
* @return true if opening was successfull, false otherwise.
* @exception Exception
*/
virtual bool
open ( void ) throw ( Exception );
/**
* Check if the encoding session is open.
*
* @return true if the encoding session is open, false otherwise.
*/
inline virtual bool
isOpen ( void ) const throw ()
{
return encoderOpen;
}
/**
* Check if the encoder is ready to accept data.
*
* @param sec the maximum seconds to block.
* @param usec micro seconds to block after the full seconds.
* @return true if the encoder is ready to accept data,
* false otherwise.
* @exception Exception
*/
inline virtual bool
canWrite ( unsigned int sec,
unsigned int usec ) throw ( Exception )
{
if ( !isOpen() ) {
return false;
}
return getSink()->canWrite(sec, usec);
}
/**
* Write data to the encoder.
* Buf is expected to be a sequence of big-endian 16 bit values,
* with left and right channels interleaved. Len is the number of
* bytes, must be a multiple of 4.
*
* @param buf the data to write.
* @param len number of bytes to write from buf.
* @return the number of bytes written (may be less than len).
* @exception Exception
*/
virtual unsigned int
write ( const void * buf,
unsigned int len ) throw ( Exception );
/**
* Flush all data that was written to the encoder to the underlying
* connection.
*
* @exception Exception
*/
virtual void
flush ( void ) throw ( Exception );
/**
* Close the encoding session.
*
* @exception Exception
*/
virtual void
close ( void ) throw ( Exception );
};
/* ================================================= external data structures */
/* ====================================================== function prototypes */
#endif /* OPUS_LIB_ENCODER_H */