#include #include #include #include #include "../deps/opus/include/opus.h" #include "common.h" #include #include using namespace node; using namespace v8; #define FRAME_SIZE 960 #define MAX_FRAME_SIZE 6*960 #define MAX_PACKET_SIZE (3*1276) #define BITRATE 64000 const char* getDecodeError( int decodedSamples ) { switch( decodedSamples ) { case OPUS_BAD_ARG: return "One or more invalid/out of range arguments"; case OPUS_BUFFER_TOO_SMALL: return "The mode struct passed is invalid"; case OPUS_INTERNAL_ERROR: return "An internal error was detected"; case OPUS_INVALID_PACKET: return "The compressed data passed is corrupted"; case OPUS_UNIMPLEMENTED: return "Invalid/unsupported request number."; case OPUS_INVALID_STATE: return "An encoder or decoder structure is invalid or already freed."; case OPUS_ALLOC_FAIL: return "Memory allocation has failed"; default: return "Unknown OPUS error"; } } class OpusEncoder : public ObjectWrap { private: OpusEncoder* encoder; OpusDecoder* decoder; opus_int32 rate; int channels; int application; unsigned char outOpus[ MAX_PACKET_SIZE ]; opus_int16* outPcm; protected: int EnsureEncoder() { if( encoder != NULL ) return 0; int error; encoder = opus_encoder_create( rate, channels, application, &error ); return error; } int EnsureDecoder() { if( decoder != NULL ) return 0; int error; decoder = opus_decoder_create( rate, channels, &error ); return error; } public: OpusEncoder( opus_int32 rate, int channels, int application ): encoder( NULL ), decoder( NULL ), rate( rate ), channels( channels ), application( application ) { outPcm = new opus_int16[ channels * MAX_FRAME_SIZE ]; } ~OpusEncoder() { if( encoder != NULL ) opus_encoder_destroy( encoder ); if( decoder != NULL ) opus_decoder_destroy( decoder ); encoder = NULL; decoder = NULL; delete outPcm; outPcm = NULL; } static void Encode( const Nan::FunctionCallbackInfo< v8::Value >& info ) { // Unwrap the encoder. OpusEncoder* self = ObjectWrap::Unwrap( info.This() ); if( self->EnsureEncoder() != OPUS_OK ) { Nan::ThrowError( "Could not create encoder. Check the encoder parameters" ); return; } // Read the function arguments REQ_OBJ_ARG( 0, pcmBuffer ); OPT_INT_ARG( 1, maxPacketSize, MAX_PACKET_SIZE ); // Read the PCM data. char* pcmData = Buffer::Data(pcmBuffer); opus_int16* pcm = reinterpret_cast( pcmData ); int frameSize = Buffer::Length( pcmBuffer ) / 2 / self->channels; // Encode the samples. int compressedLength = opus_encode( self->encoder, pcm, frameSize, &(self->outOpus[0]), maxPacketSize ); // Create a new result buffer. Nan::MaybeLocal actualBuffer = Nan::CopyBuffer(reinterpret_cast(self->outOpus), compressedLength ); if( !actualBuffer.IsEmpty() ) info.GetReturnValue().Set( actualBuffer.ToLocalChecked() ); } static void Decode( const Nan::FunctionCallbackInfo< v8::Value >& info ) { REQ_OBJ_ARG( 0, compressedBuffer ); // Read the compressed data. unsigned char* compressedData = (unsigned char*)Buffer::Data( compressedBuffer ); size_t compressedDataLength = Buffer::Length( compressedBuffer ); OpusEncoder* self = ObjectWrap::Unwrap( info.This() ); if( self->EnsureDecoder() != OPUS_OK ) { Nan::ThrowError( "Could not create decoder. Check the decoder parameters" ); return; } // Encode the samples. int decodedSamples = opus_decode( self->decoder, compressedData, compressedDataLength, &(self->outPcm[0]), MAX_FRAME_SIZE, /* decode_fex */ 0 ); if( decodedSamples < 0 ) { return Nan::ThrowTypeError( getDecodeError( decodedSamples ) ); } // Create a new result buffer. int decodedLength = decodedSamples * 2 * self->channels; Nan::MaybeLocal actualBuffer = Nan::CopyBuffer( reinterpret_cast(self->outPcm), decodedLength ); if( !actualBuffer.IsEmpty() ) info.GetReturnValue().Set( actualBuffer.ToLocalChecked() ); } static void ApplyEncoderCTL( const Nan::FunctionCallbackInfo< v8::Value >&info ) { REQ_INT_ARG( 0, ctl ); REQ_INT_ARG( 1, value ); OpusEncoder* self = ObjectWrap::Unwrap( info.This() ); if ( self->EnsureEncoder() != OPUS_OK ) { Nan::ThrowError( "Could not create encoder. Check the encoder parameters" ); return; } if( opus_encoder_ctl( self->encoder, ctl, value ) != OPUS_OK ) return Nan::ThrowError( "Invalid ctl/value" ); } static void ApplyDecoderCTL( const Nan::FunctionCallbackInfo< v8::Value >&info ) { REQ_INT_ARG( 0, ctl ); REQ_INT_ARG( 1, value ); OpusEncoder* self = ObjectWrap::Unwrap( info.This() ); if ( self->EnsureDecoder() != OPUS_OK ) { Nan::ThrowError( "Could not create decoder. Check the decoder parameters" ); return; } if ( opus_decoder_ctl( self->decoder, ctl, value ) != OPUS_OK) return Nan::ThrowError( "Invalid ctl/value" ); } static void SetBitrate( const Nan::FunctionCallbackInfo< v8::Value >& info ) { REQ_INT_ARG( 0, bitrate ); OpusEncoder* self = ObjectWrap::Unwrap( info.This() ); if( self->EnsureEncoder() != OPUS_OK ) { Nan::ThrowError( "Could not create encoder. Check the encoder parameters" ); return; } if( opus_encoder_ctl( self->encoder, OPUS_SET_BITRATE( bitrate ) ) != OPUS_OK ) return Nan::ThrowError( "Invalid bitrate" ); } static void GetBitrate( const Nan::FunctionCallbackInfo< v8::Value >& info ) { OpusEncoder* self = ObjectWrap::Unwrap( info.This() ); if( self->EnsureEncoder() != OPUS_OK ) { Nan::ThrowError( "Could not create encoder. Check the encoder parameters" ); return; } opus_int32 bitrate; opus_encoder_ctl( self->encoder, OPUS_GET_BITRATE( &bitrate ) ); info.GetReturnValue().Set( Nan::New( bitrate ) ); } static void New( const Nan::FunctionCallbackInfo< v8::Value >& info ) { if( !info.IsConstructCall() ) { return Nan::ThrowTypeError("Use the new operator to construct the OpusEncoder."); } OPT_INT_ARG( 0, rate, 48000 ); OPT_INT_ARG( 1, channels, 1 ); OPT_INT_ARG( 2, application, OPUS_APPLICATION_AUDIO ); OpusEncoder* encoder = new OpusEncoder( rate, channels, application ); encoder->Wrap( info.This() ); info.GetReturnValue().Set( info.This() ); } static void Init( Local exports ) { Nan::HandleScope scope; Local tpl = Nan::New( New ); tpl->SetClassName(Nan::New("OpusEncoder").ToLocalChecked()); tpl->InstanceTemplate()->SetInternalFieldCount( 1 ); Nan::SetPrototypeMethod( tpl, "encode", Encode ); Nan::SetPrototypeMethod( tpl, "decode", Decode ); Nan::SetPrototypeMethod( tpl, "applyEncoderCTL", ApplyEncoderCTL ); Nan::SetPrototypeMethod( tpl, "applyDecoderCTL", ApplyDecoderCTL ); Nan::SetPrototypeMethod( tpl, "setBitrate", SetBitrate ); Nan::SetPrototypeMethod( tpl, "getBitrate", GetBitrate ); //v8::Persistent constructor; //Nan::AssignPersistent(constructor, tpl); exports->Set( Nan::New("OpusEncoder").ToLocalChecked(), tpl->GetFunction() ); } }; void NodeInit( Local< Object > exports ) { OpusEncoder::Init( exports ); } NODE_MODULE(node_opus, NodeInit)