Added Opus support. 2nd part
This commit is contained in:
parent
ef5ba44013
commit
9cecd3f493
|
@ -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
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
Loading…
Reference in New Issue