OpusEncoder *opus_encoder_create(int Fs, int channels) { char *raw_state = malloc(opus_encoder_get_size(channels)); if (raw_state == NULL) return NULL; return opus_encoder_init((OpusEncoder*)raw_state, Fs, channels); }
JNIEXPORT void JNICALL Java_org_echocat_jopus_OpusEncoderJNI_init (JNIEnv *env, jclass thisClass, jlong handle, jint samplingRateInHz, jint numberOfChannels, jint application) { Java_org_echocat_jogg_OggJNISupport_checkResponse(env, opus_encoder_init((OpusEncoder*) handle, samplingRateInHz, numberOfChannels, application) ); }
int opus_multistream_encoder_init( OpusMSEncoder *st, opus_int32 Fs, int channels, int streams, int coupled_streams, const unsigned char *mapping, int application ) { int coupled_size; int mono_size; int i; char *ptr; if ((channels>255) || (coupled_streams>streams) || (coupled_streams+streams>255) || (streams<1) || (coupled_streams<0)) return OPUS_BAD_ARG; st->layout.nb_channels = channels; st->layout.nb_streams = streams; st->layout.nb_coupled_streams = coupled_streams; for (i=0;i<st->layout.nb_channels;i++) st->layout.mapping[i] = mapping[i]; if (!validate_layout(&st->layout) || !validate_encoder_layout(&st->layout)) return OPUS_BAD_ARG; ptr = (char*)st + align(sizeof(OpusMSEncoder)); coupled_size = opus_encoder_get_size(2); mono_size = opus_encoder_get_size(1); for (i=0;i<st->layout.nb_coupled_streams;i++) { opus_encoder_init((OpusEncoder*)ptr, Fs, 2, application); ptr += align(coupled_size); } for (;i<st->layout.nb_streams;i++) { opus_encoder_init((OpusEncoder*)ptr, Fs, 1, application); ptr += align(mono_size); } return OPUS_OK; }
bool FVoiceEncoderOpus::Init(int32 InSampleRate, int32 InNumChannels) { UE_LOG(LogVoiceEncode, Display, TEXT("EncoderVersion: %s"), ANSI_TO_TCHAR(opus_get_version_string())); SampleRate = InSampleRate; NumChannels = InNumChannels; // 20ms frame sizes are a good choice for most applications (1000ms / 20ms = 50) FrameSize = SampleRate / NUM_OPUS_FRAMES_PER_SEC; //MaxFrameSize = FrameSize * MAX_OPUS_FRAMES; int32 EncError = 0; #if USE_UE4_MEM_ALLOC int32 EncSize = opus_encoder_get_size(NumChannels); Encoder = (OpusEncoder*)FMemory::Malloc(EncSize); EncError = opus_encoder_init(Encoder, SampleRate, NumChannels, OPUS_APPLICATION_VOIP); #else Encoder = opus_encoder_create(SampleRate, NumChannels, OPUS_APPLICATION_VOIP, &EncError); #endif if (EncError != OPUS_OK) { UE_LOG(LogVoiceEncode, Warning, TEXT("Failed to init Opus Encoder: %s"), ANSI_TO_TCHAR(opus_strerror(EncError))); Destroy(); } // Turn on variable bit rate encoding int32 UseVbr = 1; opus_encoder_ctl(Encoder, OPUS_SET_VBR(UseVbr)); // Turn off constrained VBR int32 UseCVbr = 0; opus_encoder_ctl(Encoder, OPUS_SET_VBR_CONSTRAINT(UseCVbr)); // Complexity (1-10) int32 Complexity = 1; opus_encoder_ctl(Encoder, OPUS_SET_COMPLEXITY(Complexity)); // Forward error correction int32 InbandFEC = 0; opus_encoder_ctl(Encoder, OPUS_SET_INBAND_FEC(InbandFEC)); #if DEBUG_OPUS DebugEncoderInfo(Encoder); #endif // DEBUG_OPUS return EncError == OPUS_OK; }
int init_send_audio(codec_state *cs) { cs->support_send_audio = 0; const ALchar *pDeviceList = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER); int i = 0; const ALchar *device_names[20]; if (pDeviceList) { printf("\nAvailable Capture Devices are:\n"); while (*pDeviceList) { device_names[i] = pDeviceList; printf("%d) %s\n", i, device_names[i]); pDeviceList += strlen(pDeviceList) + 1; ++i; } } printf("enter capture device number: \n"); char dev[2]; fgets(dev, sizeof(dev), stdin); cs->audio_capture_device = alcCaptureOpenDevice(device_names[dev[0] - 48], AUDIO_SAMPLE_RATE, AL_FORMAT_MONO16, AUDIO_FRAME_SIZE * 4); if (alcGetError(cs->audio_capture_device) != AL_NO_ERROR) { printf("could not start capture device! %d\n", alcGetError(cs->audio_capture_device)); return 0; } int err = OPUS_OK; cs->audio_bitrate = AUDIO_BITRATE; cs->audio_encoder = opus_encoder_create(AUDIO_SAMPLE_RATE, 1, OPUS_APPLICATION_VOIP, &err); err = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_BITRATE(cs->audio_bitrate)); err = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_COMPLEXITY(10)); err = opus_encoder_ctl(cs->audio_encoder, OPUS_SET_SIGNAL(OPUS_SIGNAL_VOICE)); opus_encoder_init(cs->audio_encoder, AUDIO_SAMPLE_RATE, 1, OPUS_APPLICATION_VOIP); int nfo; err = opus_encoder_ctl(cs->audio_encoder, OPUS_GET_LOOKAHEAD(&nfo)); /* printf("Encoder lookahead delay : %d\n", nfo); */ printf("init audio encoder successful\n"); return 1; }
/* * Open codec. */ static pj_status_t codec_open( pjmedia_codec *codec, pjmedia_codec_param *attr ) { struct opus_data *opus_data = (struct opus_data *)codec->codec_data; int idx, err; PJ_ASSERT_RETURN(codec && attr && opus_data, PJ_EINVAL); pj_mutex_lock (opus_data->mutex); TRACE_((THIS_FILE, "%s:%d: - TRACE", __FUNCTION__, __LINE__)); opus_data->cfg.sample_rate = attr->info.clock_rate; opus_data->cfg.channel_cnt = attr->info.channel_cnt; opus_data->ptime = attr->info.frm_ptime; /* Allocate memory used by the codec */ if (!opus_data->enc) { /* Allocate memory for max 2 channels */ opus_data->enc = pj_pool_zalloc(opus_data->pool, opus_encoder_get_size(2)); } if (!opus_data->dec) { /* Allocate memory for max 2 channels */ opus_data->dec = pj_pool_zalloc(opus_data->pool, opus_decoder_get_size(2)); } if (!opus_data->enc_packer) { opus_data->enc_packer = pj_pool_zalloc(opus_data->pool, opus_repacketizer_get_size()); } if (!opus_data->dec_packer) { opus_data->dec_packer = pj_pool_zalloc(opus_data->pool, opus_repacketizer_get_size()); } if (!opus_data->enc || !opus_data->dec || !opus_data->enc_packer || !opus_data->dec_packer) { PJ_LOG(2, (THIS_FILE, "Unable to allocate memory for the codec")); pj_mutex_unlock (opus_data->mutex); return PJ_ENOMEM; } /* Check max average bit rate */ idx = find_fmtp(&attr->setting.enc_fmtp, &STR_MAX_BIT_RATE, PJ_FALSE); if (idx >= 0) { unsigned rate; rate = (unsigned)pj_strtoul(&attr->setting.enc_fmtp.param[idx].val); if (rate < attr->info.avg_bps) attr->info.avg_bps = rate; } /* Check plc */ idx = find_fmtp(&attr->setting.enc_fmtp, &STR_INBAND_FEC, PJ_FALSE); if (idx >= 0) { unsigned plc; plc = (unsigned) pj_strtoul(&attr->setting.enc_fmtp.param[idx].val); attr->setting.plc = plc > 0? PJ_TRUE: PJ_FALSE; } /* Check vad */ idx = find_fmtp(&attr->setting.enc_fmtp, &STR_DTX, PJ_FALSE); if (idx >= 0) { unsigned vad; vad = (unsigned) pj_strtoul(&attr->setting.enc_fmtp.param[idx].val); attr->setting.vad = vad > 0? PJ_TRUE: PJ_FALSE; } /* Check cbr */ idx = find_fmtp(&attr->setting.enc_fmtp, &STR_CBR, PJ_FALSE); if (idx >= 0) { unsigned cbr; cbr = (unsigned) pj_strtoul(&attr->setting.enc_fmtp.param[idx].val); opus_data->cfg.cbr = cbr > 0? PJ_TRUE: PJ_FALSE; } /* Check max average bit rate */ idx = find_fmtp(&attr->setting.dec_fmtp, &STR_MAX_BIT_RATE, PJ_FALSE); if (idx >= 0) { unsigned rate; rate = (unsigned) pj_strtoul(&attr->setting.dec_fmtp.param[idx].val); if (rate < attr->info.avg_bps) attr->info.avg_bps = rate; } TRACE_((THIS_FILE, "%s:%d: sample_rate: %u", __FUNCTION__, __LINE__, opus_data->cfg.sample_rate)); /* Initialize encoder */ err = opus_encoder_init(opus_data->enc, opus_data->cfg.sample_rate, attr->info.channel_cnt, OPUS_APPLICATION_VOIP); if (err != OPUS_OK) { PJ_LOG(2, (THIS_FILE, "Unable to create encoder")); return PJMEDIA_CODEC_EFAILED; } /* Set signal type */ opus_encoder_ctl(opus_data->enc, OPUS_SET_SIGNAL(OPUS_SIGNAL_VOICE)); /* Set bitrate */ opus_encoder_ctl(opus_data->enc, OPUS_SET_BITRATE(attr->info.avg_bps)); /* Set VAD */ opus_encoder_ctl(opus_data->enc, OPUS_SET_DTX(attr->setting.vad ? 1 : 0)); /* Set PLC */ opus_encoder_ctl(opus_data->enc, OPUS_SET_INBAND_FEC(attr->setting.plc ? 1 : 0)); /* Set bandwidth */ opus_encoder_ctl(opus_data->enc, OPUS_SET_MAX_BANDWIDTH(get_opus_bw_constant( opus_data->cfg.sample_rate))); /* Set expected packet loss */ opus_encoder_ctl(opus_data->enc, OPUS_SET_PACKET_LOSS_PERC(opus_data->cfg.packet_loss)); /* Set complexity */ opus_encoder_ctl(opus_data->enc, OPUS_SET_COMPLEXITY(opus_data->cfg.complexity)); /* Set constant bit rate */ opus_encoder_ctl(opus_data->enc, OPUS_SET_VBR(opus_data->cfg.cbr ? 0 : 1)); PJ_LOG(5, (THIS_FILE, "Initialize Opus encoder, sample rate: %d, " "avg bitrate: %d, vad: %d, plc: %d, pkt loss: %d, " "complexity: %d, constant bit rate: %d", opus_data->cfg.sample_rate, attr->info.avg_bps, attr->setting.vad?1:0, attr->setting.plc?1:0, opus_data->cfg.packet_loss, opus_data->cfg.complexity, opus_data->cfg.cbr?1:0)); /* Initialize decoder */ err = opus_decoder_init (opus_data->dec, opus_data->cfg.sample_rate, attr->info.channel_cnt); if (err != OPUS_OK) { PJ_LOG(2, (THIS_FILE, "Unable to initialize decoder")); return PJMEDIA_CODEC_EFAILED; } /* Initialize temporary decode frames used for FEC */ opus_data->dec_frame[0].type = PJMEDIA_FRAME_TYPE_NONE; opus_data->dec_frame[0].buf = pj_pool_zalloc(opus_data->pool, (opus_data->cfg.sample_rate / 1000) * 60 * attr->info.channel_cnt * 2 /* bytes per sample */); opus_data->dec_frame[1].type = PJMEDIA_FRAME_TYPE_NONE; opus_data->dec_frame[1].buf = pj_pool_zalloc(opus_data->pool, (opus_data->cfg.sample_rate / 1000) * 60 * attr->info.channel_cnt * 2 /* bytes per sample */); opus_data->dec_frame_index = -1; /* Initialize the repacketizers */ opus_repacketizer_init(opus_data->enc_packer); opus_repacketizer_init(opus_data->dec_packer); pj_mutex_unlock (opus_data->mutex); return PJ_SUCCESS; }
/* * Open codec. */ static pj_status_t opus_codec_open(pjmedia_codec *codec, pjmedia_codec_param *attr) { pj_status_t status; struct opus_private *opus; int id, ret = 0; unsigned i; int structSizeBytes; int tmpFmtpVal = 0; unsigned max_nsamples; const pj_str_t STR_FMTP_USE_INBAND_FEC = {"useinbandfec", 12}; const pj_str_t STR_FMTP_MAX_AVERAGE_BITRATE = {"maxaveragebitrate", 17}; const pj_str_t STR_FMTP_MAX_CODED_AUDIO_BANDWIDTH = {"maxplaybackrate", 15}; const pj_str_t STR_FMTP_USE_DTX = {"usedtx", 6}; opus = (struct opus_private *)codec->codec_data; pj_assert(opus != NULL); pj_assert(opus->enc_ready == PJ_FALSE && opus->dec_ready == PJ_FALSE); PJ_LOG(4, (THIS_FILE, "Clock rate is %d ", attr->info.clock_rate)); opus->externalFs = attr->info.clock_rate; /* Create Encoder */ structSizeBytes = opus_encoder_get_size(attr->info.channel_cnt); opus->psEnc = pj_pool_zalloc(opus->pool, structSizeBytes); ret = opus_encoder_init(opus->psEnc, opus->externalFs, attr->info.channel_cnt, OPUS_APPLICATION_VOIP); if (ret) { PJ_LOG(1, (THIS_FILE, "Unable to init encoder : %d", ret)); return PJ_EINVAL; } /* * Set Encoder parameters * TODO : have it configurable */ opus_encoder_ctl(opus->psEnc, OPUS_SET_COMPLEXITY(2)); opus_encoder_ctl(opus->psEnc, OPUS_SET_SIGNAL(OPUS_SIGNAL_VOICE)); /* Apply fmtp params to Encoder */ for (i = 0; i < attr->setting.enc_fmtp.cnt; ++i) { if (pj_stricmp(&attr->setting.enc_fmtp.param[i].name, &STR_FMTP_USE_INBAND_FEC) == 0) { tmpFmtpVal = (int)(pj_strtoul(&attr->setting.enc_fmtp.param[i].val)); opus_encoder_ctl(opus->psEnc, OPUS_SET_INBAND_FEC(tmpFmtpVal)); break; } else if (pj_stricmp(&attr->setting.enc_fmtp.param[i].name, &STR_FMTP_MAX_AVERAGE_BITRATE) == 0) { tmpFmtpVal = (int)(pj_strtoul(&attr->setting.enc_fmtp.param[i].val)); if (tmpFmtpVal >= 6000 && tmpFmtpVal <= 510000) { opus_encoder_ctl(opus->psEnc, OPUS_SET_BITRATE(tmpFmtpVal)); } } else if (pj_stricmp(&attr->setting.enc_fmtp.param[i].name, &STR_FMTP_MAX_CODED_AUDIO_BANDWIDTH) == 0) { tmpFmtpVal = (int)(pj_strtoul(&attr->setting.enc_fmtp.param[i].val)); if (tmpFmtpVal <= 8000) { opus_encoder_ctl(opus->psEnc, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_NARROWBAND)); } else if (tmpFmtpVal <= 12000) { opus_encoder_ctl(opus->psEnc, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_MEDIUMBAND)); } else if (tmpFmtpVal <= 16000) { opus_encoder_ctl(opus->psEnc, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_WIDEBAND)); } else if (tmpFmtpVal <= 24000) { opus_encoder_ctl(opus->psEnc, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_SUPERWIDEBAND)); } else if (tmpFmtpVal <= 48000) { opus_encoder_ctl(opus->psEnc, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_FULLBAND)); } } else if (pj_stricmp(&attr->setting.enc_fmtp.param[i].name, &STR_FMTP_USE_DTX) == 0) { tmpFmtpVal = (int)(pj_strtoul(&attr->setting.enc_fmtp.param[i].val)); opus_encoder_ctl(opus->psEnc, OPUS_SET_DTX(tmpFmtpVal)); } } opus->enc_ready = PJ_TRUE; /* Decoder buffer */ opus->pcm_bytes_per_sample = attr->info.pcm_bits_per_sample / 8; max_nsamples = 120 * OPUS_CLOCK_RATE / 1000; /* 120ms is max frame time */ opus->dec_buf_max_size = max_nsamples * opus->pcm_bytes_per_sample; opus->dec_buf = pj_pool_alloc(opus->pool, opus->dec_buf_max_size); /* Create decoder */ structSizeBytes = opus_decoder_get_size(attr->info.channel_cnt); opus->psDec = pj_pool_zalloc(opus->pool, structSizeBytes); ret = opus_decoder_init(opus->psDec, opus->externalFs, attr->info.channel_cnt); if (ret) { PJ_LOG(1, (THIS_FILE, "Unable to init decoder : %d", ret)); return PJ_EINVAL; } opus->dec_ready = PJ_TRUE; return PJ_SUCCESS; }
virtual bool Cook(FName Format, const TArray<uint8>& SrcBuffer, FSoundQualityInfo& QualityInfo, TArray<uint8>& CompressedDataStore) const override { check(Format == NAME_OPUS); // Get best compatible sample rate const uint16 kOpusSampleRate = GetBestOutputSampleRate(QualityInfo.SampleRate); // Frame size must be one of 2.5, 5, 10, 20, 40 or 60 ms const int32 kOpusFrameSizeMs = 60; // Calculate frame size required by Opus const int32 kOpusFrameSizeSamples = (kOpusSampleRate * kOpusFrameSizeMs) / 1000; const uint32 kSampleStride = SAMPLE_SIZE * QualityInfo.NumChannels; const int32 kBytesPerFrame = kOpusFrameSizeSamples * kSampleStride; // Check whether source has compatible sample rate TArray<uint8> SrcBufferCopy; if (QualityInfo.SampleRate != kOpusSampleRate) { if (!ResamplePCM(QualityInfo.NumChannels, SrcBuffer, QualityInfo.SampleRate, SrcBufferCopy, kOpusSampleRate)) { return false; } } else { // Take a copy of the source regardless SrcBufferCopy = SrcBuffer; } // Initialise the Opus encoder OpusEncoder* Encoder = NULL; int32 EncError = 0; #if USE_UE4_MEM_ALLOC int32 EncSize = opus_encoder_get_size(QualityInfo.NumChannels); Encoder = (OpusEncoder*)FMemory::Malloc(EncSize); EncError = opus_encoder_init(Encoder, kOpusSampleRate, QualityInfo.NumChannels, OPUS_APPLICATION_AUDIO); #else Encoder = opus_encoder_create(kOpusSampleRate, QualityInfo.NumChannels, OPUS_APPLICATION_AUDIO, &EncError); #endif if (EncError != OPUS_OK) { Destroy(Encoder); return false; } int32 BitRate = GetBitRateFromQuality(QualityInfo); opus_encoder_ctl(Encoder, OPUS_SET_BITRATE(BitRate)); // Create a buffer to store compressed data CompressedDataStore.Empty(); FMemoryWriter CompressedData(CompressedDataStore); int32 SrcBufferOffset = 0; // Calc frame and sample count int32 FramesToEncode = SrcBufferCopy.Num() / kBytesPerFrame; uint32 TrueSampleCount = SrcBufferCopy.Num() / kSampleStride; // Pad the end of data with zeroes if it isn't exactly the size of a frame. if (SrcBufferCopy.Num() % kBytesPerFrame != 0) { int32 FrameDiff = kBytesPerFrame - (SrcBufferCopy.Num() % kBytesPerFrame); SrcBufferCopy.AddZeroed(FrameDiff); FramesToEncode++; } check(QualityInfo.NumChannels <= MAX_uint8); check(FramesToEncode <= MAX_uint16); SerializeHeaderData(CompressedData, kOpusSampleRate, TrueSampleCount, QualityInfo.NumChannels, FramesToEncode); // Temporary storage with more than enough to store any compressed frame TArray<uint8> TempCompressedData; TempCompressedData.AddUninitialized(kBytesPerFrame); while (SrcBufferOffset < SrcBufferCopy.Num()) { int32 CompressedLength = opus_encode(Encoder, (const opus_int16*)(SrcBufferCopy.GetData() + SrcBufferOffset), kOpusFrameSizeSamples, TempCompressedData.GetData(), TempCompressedData.Num()); if (CompressedLength < 0) { const char* ErrorStr = opus_strerror(CompressedLength); UE_LOG(LogAudio, Warning, TEXT("Failed to encode: [%d] %s"), CompressedLength, ANSI_TO_TCHAR(ErrorStr)); Destroy(Encoder); CompressedDataStore.Empty(); return false; } else { // Store frame length and copy compressed data before incrementing pointers check(CompressedLength < MAX_uint16); SerialiseFrameData(CompressedData, TempCompressedData.GetData(), CompressedLength); SrcBufferOffset += kBytesPerFrame; } } Destroy(Encoder); return CompressedDataStore.Num() > 0; }
static int opus_multistream_encoder_init_impl( OpusMSEncoder *st, opus_int32 Fs, int channels, int streams, int coupled_streams, const unsigned char *mapping, int application, MappingType mapping_type ) { int coupled_size; int mono_size; int i, ret; char *ptr; if ((channels>255) || (channels<1) || (coupled_streams>streams) || (streams<1) || (coupled_streams<0) || (streams>255-coupled_streams)) return OPUS_BAD_ARG; st->arch = opus_select_arch(); st->layout.nb_channels = channels; st->layout.nb_streams = streams; st->layout.nb_coupled_streams = coupled_streams; if (mapping_type != MAPPING_TYPE_SURROUND) st->lfe_stream = -1; st->bitrate_bps = OPUS_AUTO; st->application = application; st->variable_duration = OPUS_FRAMESIZE_ARG; for (i=0;i<st->layout.nb_channels;i++) st->layout.mapping[i] = mapping[i]; if (!validate_layout(&st->layout) || !validate_encoder_layout(&st->layout)) return OPUS_BAD_ARG; ptr = (char*)st + align(sizeof(OpusMSEncoder)); coupled_size = opus_encoder_get_size(2); mono_size = opus_encoder_get_size(1); for (i=0;i<st->layout.nb_coupled_streams;i++) { ret = opus_encoder_init((OpusEncoder*)ptr, Fs, 2, application); if(ret!=OPUS_OK)return ret; if (i==st->lfe_stream) opus_encoder_ctl((OpusEncoder*)ptr, OPUS_SET_LFE(1)); ptr += align(coupled_size); } for (;i<st->layout.nb_streams;i++) { ret = opus_encoder_init((OpusEncoder*)ptr, Fs, 1, application); if (i==st->lfe_stream) opus_encoder_ctl((OpusEncoder*)ptr, OPUS_SET_LFE(1)); if(ret!=OPUS_OK)return ret; ptr += align(mono_size); } if (mapping_type == MAPPING_TYPE_SURROUND) { OPUS_CLEAR(ms_get_preemph_mem(st), channels); OPUS_CLEAR(ms_get_window_mem(st), channels*120); } st->mapping_type = mapping_type; return OPUS_OK; }
int main(int argc, char *argv[]) { struct hostent *server_host; char *server_host_str = "localhost"; char *certificate_file = NULL; char *key_file = NULL; char *password_file = NULL; char *token_file = NULL; char *username = "******"; int port = 64738; int ret; int development_mode = 0; int socket_fd; struct sockaddr_in server_addr; SSLRead socket_watcher; ev_io user_thread_watcher; ev_timer ping_watcher; ev_signal signal_watcher; ev_loop_main = EV_DEFAULT; /* * Lua initialization */ lua = luaL_newstate(); if (lua == NULL) { fprintf(stderr, "%s: could not initialize Lua\n", PIEPAN_NAME); return 1; } luaL_openlibs(lua); if (luaL_loadbuffer(lua, (const char *)src_piepan_impl_luac, src_piepan_impl_luac_len, "piepan_impl") != LUA_OK) { fprintf(stderr, "%s: could not load piepan implementation\n", PIEPAN_NAME); return 1; } lua_call(lua, 0, 0); lua_getglobal(lua, "piepan"); lua_getfield(lua, -1, "internal"); lua_getfield(lua, -1, "api"); lua_pushcfunction(lua, api_init); lua_setfield(lua, -2, "apiInit"); lua_settop(lua, 0); /* * Argument parsing */ { int opt; int i; int show_help = 0; int show_version = 0; lua_getglobal(lua, "piepan"); lua_getfield(lua, -1, "internal"); lua_getfield(lua, -1, "events"); lua_getfield(lua, -1, "onArgument"); opterr = 0; while ((opt = getopt(argc, argv, "u:c:k:s:t:p:-:dhv")) != -1) { switch (opt) { case 'u': username = optarg; break; case 'c': certificate_file = optarg; if (key_file == NULL) { key_file = certificate_file; } break; case 'k': key_file = optarg; break; case 's': { char *port_str; server_host_str = optarg; port_str = strrchr(server_host_str, ':'); if (port_str != NULL) { *port_str = '\0'; port = atoi(++port_str); } break; } case 't': token_file = optarg; break; case 'p': password_file = optarg; break; case '-': { char *key = optarg; char *value = strchr(key, '='); if (key == value) { break; } if (value != NULL) { *value++ = 0; } lua_pushvalue(lua, -1); lua_pushstring(lua, key); lua_pushstring(lua, value); lua_call(lua, 2, 0); break; } case 'd': development_mode = 1; break; case 'h': usage(stdout); return 0; case 'v': printf("%s %s (compiled on " __DATE__ " " __TIME__ ")\n", PIEPAN_NAME, PIEPAN_VERSION); return 0; default: fprintf(stderr, "%s: unknown or incomplete option '%c'\n", PIEPAN_NAME, optopt); return 1; } } lua_settop(lua, 0); } /* * Load user scripts */ { int i; lua_getglobal(lua, "piepan"); lua_getfield(lua, -1, "internal"); lua_getfield(lua, -1, "events"); lua_getfield(lua, -1, "onLoadScript"); for (i = optind; i < argc; i++) { lua_pushvalue(lua, -1); lua_pushstring(lua, argv[i]); if (development_mode) { lua_newuserdata(lua, sizeof(ScriptStat)); } else { lua_pushnil(lua); } lua_call(lua, 2, 3); if (lua_toboolean(lua, -3)) { if (development_mode) { ScriptStat *item = lua_touserdata(lua, -1); item->lua = lua; item->id = lua_tointeger(lua, -2); item->filename = argv[i]; ev_stat_init(&item->ev, script_stat_event, item->filename, 0); ev_stat_start(ev_loop_main, &item->ev); } } else { fprintf(stderr, "%s: %s\n", PIEPAN_NAME, lua_tostring(lua, -2)); } lua_pop(lua, 3); } lua_settop(lua, 0); } /* * Initialize Opus */ { OpusEncoder *encoder; int error; lua_getglobal(lua, "piepan"); lua_getfield(lua, -1, "internal"); lua_getfield(lua, -1, "opus"); encoder = lua_newuserdata(lua, opus_encoder_get_size(1)); lua_setfield(lua, -2, "encoder"); error = opus_encoder_init(encoder, 48000, 1, OPUS_APPLICATION_AUDIO); if (error != OPUS_OK) { fprintf(stderr, "%s: could not initialize the Opus encoder: %s\n", PIEPAN_NAME, opus_strerror(error)); return 1; } opus_encoder_ctl(encoder, OPUS_SET_VBR(0)); /* TODO: set this to the server's max bitrate */ opus_encoder_ctl(encoder, OPUS_SET_BITRATE(40000)); lua_settop(lua, 0); } /* * SSL initialization */ SSL_library_init(); ssl_context = SSL_CTX_new(SSLv23_client_method()); if (ssl_context == NULL) { fprintf(stderr, "%s: could not create SSL context\n", PIEPAN_NAME); return 1; } if (certificate_file != NULL) { if (!SSL_CTX_use_certificate_chain_file(ssl_context, certificate_file) || !SSL_CTX_use_PrivateKey_file(ssl_context, key_file, SSL_FILETYPE_PEM) || !SSL_CTX_check_private_key(ssl_context)) { fprintf(stderr, "%s: could not load certificate and/or key file\n", PIEPAN_NAME); return 1; } } /* * Socket initialization and connection */ socket_fd = socket(AF_INET, SOCK_STREAM, 0); if (socket_fd < 0) { fprintf(stderr, "%s: could not create socket\n", PIEPAN_NAME); return 1; } memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(port); server_host = gethostbyname(server_host_str); if (server_host == NULL || server_host->h_addr_list[0] == NULL || server_host->h_addrtype != AF_INET) { fprintf(stderr, "%s: could not parse server address\n", PIEPAN_NAME); return 1; } memmove(&server_addr.sin_addr, server_host->h_addr_list[0], server_host->h_length); ret = connect(socket_fd, (struct sockaddr *) &server_addr, sizeof(server_addr)); if (ret != 0) { fprintf(stderr, "%s: could not connect to server\n", PIEPAN_NAME); return 1; } ssl = SSL_new(ssl_context); if (ssl == NULL) { fprintf(stderr, "%s: could not create SSL object\n", PIEPAN_NAME); return 1; } if (SSL_set_fd(ssl, socket_fd) == 0) { fprintf(stderr, "%s: could not set SSL file descriptor\n", PIEPAN_NAME); return 1; } if (SSL_connect(ssl) != 1) { fprintf(stderr, "%s: could not create secure connection\n", PIEPAN_NAME); return 1; } /* * User thread pipe */ if (pipe(user_thread_pipe) != 0) { fprintf(stderr, "%s: could not create user thread pipe\n", PIEPAN_NAME); return 1; } /* * Trigger initial event */ lua_getglobal(lua, "piepan"); lua_getfield(lua, -1, "internal"); lua_getfield(lua, -1, "initialize"); lua_newtable(lua); lua_pushstring(lua, username); lua_setfield(lua, -2, "username"); if (password_file != NULL) { lua_pushstring(lua, password_file); lua_setfield(lua, -2, "passwordFile"); } if (token_file != NULL) { lua_pushstring(lua, token_file); lua_setfield(lua, -2, "tokenFile"); } lua_pushlightuserdata(lua, lua); lua_setfield(lua, -2, "state"); lua_call(lua, 1, 0); lua_settop(lua, 0); /* * Event loop */ ev_signal_init(&signal_watcher, signal_event, SIGINT); ev_signal_start(ev_loop_main, &signal_watcher); ev_io_init(&socket_watcher.ev, socket_read_event, socket_fd, EV_READ); socket_watcher.lua = lua; socket_watcher.ssl = ssl; ev_io_start(ev_loop_main, &socket_watcher.ev); ev_io_init(&user_thread_watcher, user_thread_event, user_thread_pipe[0], EV_READ); ev_io_start(ev_loop_main, &user_thread_watcher); ev_timer_init(&ping_watcher, ping_event, PING_TIMEOUT, PING_TIMEOUT); ev_timer_start(ev_loop_main, &ping_watcher); ev_run(ev_loop_main, 0); /* * Cleanup */ lua_getglobal(lua, "piepan"); lua_getfield(lua, -1, "internal"); lua_getfield(lua, -1, "events"); lua_getfield(lua, -1, "onDisconnect"); if (lua_isfunction(lua, -1)) { lua_newtable(lua); lua_call(lua, 1, 0); } SSL_shutdown(ssl); /* TODO: sigpipe is triggered here if connection breaks */ close(socket_fd); lua_close(lua); return 0; }