Example #1
0
static void noise_gate_update(void *data, obs_data_t *s)
{
	struct noise_gate_data *ng = data;
	float open_threshold_db;
	float close_threshold_db;
	float sample_rate;
	int attack_time_ms;
	int hold_time_ms;
	int release_time_ms;

	open_threshold_db = (float)obs_data_get_double(s, S_OPEN_THRESHOLD);
	close_threshold_db = (float)obs_data_get_double(s, S_CLOSE_THRESHOLD);
	attack_time_ms = (int)obs_data_get_int(s, S_ATTACK_TIME);
	hold_time_ms = (int)obs_data_get_int(s, S_HOLD_TIME);
	release_time_ms = (int)obs_data_get_int(s, S_RELEASE_TIME);
	sample_rate = (float)audio_output_get_sample_rate(obs_get_audio());

	ng->sample_rate_i = 1.0f / sample_rate;
	ng->channels = audio_output_get_channels(obs_get_audio());
	ng->open_threshold = db_to_mul(open_threshold_db);
	ng->close_threshold = db_to_mul(close_threshold_db);
	ng->attack_rate = 1.0f / (ms_to_secf(attack_time_ms) * sample_rate);
	ng->release_rate = 1.0f / (ms_to_secf(release_time_ms) * sample_rate);

	const float threshold_diff = ng->open_threshold - ng->close_threshold;
	const float min_decay_period = (1.0f / 75.0f) * sample_rate;

	ng->decay_rate = threshold_diff / min_decay_period;
	ng->hold_time = ms_to_secf(hold_time_ms);
	ng->is_open = false;
	ng->attenuation = 0.0f;
	ng->level = 0.0f;
	ng->held_time = 0.0f;
}
Example #2
0
static void expander_update(void *data, obs_data_t *s)
{
	struct expander_data *cd = data;
	const char *presets = obs_data_get_string(s, S_PRESETS);
	if (strcmp(presets, "expander") == 0 && cd->is_gate) {
		obs_data_clear(s);
		obs_data_set_string(s, S_PRESETS, "expander");
		expander_defaults(s);
		cd->is_gate = false;
	}
	if (strcmp(presets, "gate") == 0 && !cd->is_gate) {
		obs_data_clear(s);
		obs_data_set_string(s, S_PRESETS, "gate");
		expander_defaults(s);
		cd->is_gate = true;
	}

	const uint32_t sample_rate =
			audio_output_get_sample_rate(obs_get_audio());
	const size_t num_channels =
			audio_output_get_channels(obs_get_audio());
	const float attack_time_ms =
			(float)obs_data_get_int(s, S_ATTACK_TIME);
	const float release_time_ms =
			(float)obs_data_get_int(s, S_RELEASE_TIME);
	const float output_gain_db =
			(float)obs_data_get_double(s, S_OUTPUT_GAIN);

	cd->ratio = (float)obs_data_get_double(s, S_RATIO);

	cd->threshold = (float)obs_data_get_double(s, S_THRESHOLD);
	cd->attack_gain = gain_coefficient(sample_rate,
			attack_time_ms / MS_IN_S_F);
	cd->release_gain = gain_coefficient(sample_rate,
			release_time_ms / MS_IN_S_F);
	cd->output_gain = db_to_mul(output_gain_db);
	cd->num_channels = num_channels;
	cd->sample_rate = sample_rate;
	cd->slope = 1.0f - cd->ratio;

	const char *detect_mode = obs_data_get_string(s, S_DETECTOR);
	if (strcmp(detect_mode, "RMS") == 0)
		cd->detector = RMS_DETECT;
	if (strcmp(detect_mode, "peak") == 0)
		cd->detector = PEAK_DETECT;
	if (strcmp(detect_mode, "none") == 0)
		cd->detector = NO_DETECT;

	size_t sample_len = sample_rate * DEFAULT_AUDIO_BUF_MS / MS_IN_S;
	if (cd->envelope_buf_len == 0)
		resize_env_buffer(cd, sample_len);
	if (cd->runaverage_len == 0)
		resize_runaverage_buffer(cd, sample_len);
	if (cd->maxspl_len == 0)
		resize_maxspl_buffer(cd, sample_len);
	if (cd->env_in_len == 0)
		resize_env_in_buffer(cd, sample_len);
}
Example #3
0
static void add_audio_encoder_params(struct dstr *cmd, obs_encoder_t *aencoder)
{
	obs_data_t *settings = obs_encoder_get_settings(aencoder);
	int bitrate = (int)obs_data_get_int(settings, "bitrate");
	audio_t *audio = obs_get_audio();
	struct dstr name = {0};

	obs_data_release(settings);

	dstr_copy(&name, obs_encoder_get_name(aencoder));
	dstr_replace(&name, "\"", "\"\"");

	dstr_catf(cmd, "\"%s\" %d %d %d ",
			name.array,
			bitrate,
			(int)obs_encoder_get_sample_rate(aencoder),
			(int)audio_output_get_channels(audio));

	dstr_free(&name);
}
Example #4
0
static void *aac_create(obs_data_t *settings, obs_encoder_t *encoder)
{
	struct aac_encoder *enc;
	int                bitrate = (int)obs_data_get_int(settings, "bitrate");
	audio_t            *audio   = obs_encoder_audio(encoder);

	avcodec_register_all();

	enc          = bzalloc(sizeof(struct aac_encoder));
	enc->encoder = encoder;
	enc->aac     = avcodec_find_encoder(AV_CODEC_ID_AAC);

	blog(LOG_INFO, "---------------------------------");

	if (!enc->aac) {
		warn("Couldn't find encoder");
		goto fail;
	}

	if (!bitrate) {
		warn("Invalid bitrate specified");
		return NULL;
	}

	enc->context = avcodec_alloc_context3(enc->aac);
	if (!enc->context) {
		warn("Failed to create codec context");
		goto fail;
	}

	enc->context->bit_rate    = bitrate * 1000;
	enc->context->channels    = (int)audio_output_get_channels(audio);
	enc->context->sample_rate = audio_output_get_sample_rate(audio);
	enc->context->sample_fmt  = enc->aac->sample_fmts ?
		enc->aac->sample_fmts[0] : AV_SAMPLE_FMT_FLTP;

	/* if using FFmpeg's AAC encoder, at least set a cutoff value
	 * (recommended by konverter) */
	if (strcmp(enc->aac->name, "aac") == 0) {
		int cutoff1 = 4000 + (int)enc->context->bit_rate / 8;
		int cutoff2 = 12000 + (int)enc->context->bit_rate / 8;
		int cutoff3 = enc->context->sample_rate / 2;
		int cutoff;

		cutoff = MIN(cutoff1, cutoff2);
		cutoff = MIN(cutoff, cutoff3);
		enc->context->cutoff = cutoff;
	}

	info("bitrate: %" PRId64 ", channels: %d",
			enc->context->bit_rate / 1000, enc->context->channels);

	init_sizes(enc, audio);

	/* enable experimental FFmpeg encoder if the only one available */
	enc->context->strict_std_compliance = -2;

	enc->context->flags = CODEC_FLAG_GLOBAL_HEADER;

	if (initialize_codec(enc))
		return enc;

fail:
	aac_destroy(enc);
	return NULL;
}
Example #5
0
static void *libfdk_create(obs_data_t settings, obs_encoder_t encoder)
{
	bool hasFdkHandle = false;
	libfdk_encoder_t *enc = 0;
	int bitrate = (int)obs_data_get_int(settings, "bitrate") * 1000;
	int afterburner = obs_data_get_bool(settings, "afterburner") ? 1 : 0;
	audio_t audio = obs_encoder_audio(encoder);
	int mode = 0;
	AACENC_ERROR err;

	if (!bitrate) {
		blog(LOG_ERROR, "Invalid bitrate");
		return NULL;
	}

	enc = bzalloc(sizeof(libfdk_encoder_t));
	enc->encoder = encoder;

	enc->channels = (int)audio_output_get_channels(audio);
	enc->sample_rate = audio_output_get_sample_rate(audio);

	switch(enc->channels) {
	case 1:
		mode = MODE_1;
		break;
	case 2:
		mode = MODE_2;
		break;
	case 3:
		mode = MODE_1_2;
		break;
	case 4:
		mode = MODE_1_2_1;
		break;
	case 5:
		mode = MODE_1_2_2;
		break;
	case 6:
		mode = MODE_1_2_2_1;
		break;
	default:
		blog(LOG_ERROR, "Invalid channel count");
		goto fail;
	}

	CHECK_LIBFDK(aacEncOpen(&enc->fdkhandle, 0, enc->channels));
	hasFdkHandle = true;

	CHECK_LIBFDK(aacEncoder_SetParam(enc->fdkhandle, AACENC_AOT,
	                                 2)); // MPEG-4 AAC-LC
	CHECK_LIBFDK(aacEncoder_SetParam(enc->fdkhandle, AACENC_SAMPLERATE,
	                                 enc->sample_rate));
	CHECK_LIBFDK(aacEncoder_SetParam(enc->fdkhandle, AACENC_CHANNELMODE, mode));
	CHECK_LIBFDK(aacEncoder_SetParam(enc->fdkhandle, AACENC_CHANNELORDER, 1));
	CHECK_LIBFDK(aacEncoder_SetParam(enc->fdkhandle, AACENC_BITRATEMODE, 0));
	CHECK_LIBFDK(aacEncoder_SetParam(enc->fdkhandle, AACENC_BITRATE, bitrate));
	CHECK_LIBFDK(aacEncoder_SetParam(enc->fdkhandle, AACENC_TRANSMUX, 0));
	CHECK_LIBFDK(aacEncoder_SetParam(enc->fdkhandle, AACENC_AFTERBURNER,
	                                 afterburner));

	CHECK_LIBFDK(aacEncEncode(enc->fdkhandle, NULL, NULL, NULL, NULL));

	CHECK_LIBFDK(aacEncInfo(enc->fdkhandle, &enc->info));

	enc->frame_size_bytes = enc->info.frameLength * 2 * enc->channels;

	enc->packet_buffer_size = enc->channels * 768;
	if(enc->packet_buffer_size < 8192)
		enc->packet_buffer_size = 8192;

	enc->packet_buffer = bmalloc(enc->packet_buffer_size);

	blog(LOG_INFO, "libfdk_aac encoder created");

	blog(LOG_INFO, "libfdk_aac bitrate: %d, channels: %d",
			bitrate / 1000, enc->channels);

	return enc;

fail:

	if(hasFdkHandle)
		aacEncClose(&enc->fdkhandle);

	if(enc->packet_buffer)
		bfree(enc->packet_buffer);

	if(enc)
		bfree(enc);

	blog(LOG_WARNING, "libfdk_aac encoder creation failed");

	return 0;
}
Example #6
0
static bool build_flv_meta_data(obs_output_t *context,
		uint8_t **output, size_t *size, size_t a_idx)
{
	obs_encoder_t *vencoder = obs_output_get_video_encoder(context);
	obs_encoder_t *aencoder = obs_output_get_audio_encoder(context, a_idx);
	video_t       *video    = obs_encoder_video(vencoder);
	audio_t       *audio    = obs_encoder_audio(aencoder);
	char buf[4096];
	char *enc = buf;
	char *end = enc+sizeof(buf);
	struct dstr encoder_name = {0};

	if (a_idx > 0 && !aencoder)
		return false;

	enc_str(&enc, end, "onMetaData");

	*enc++ = AMF_ECMA_ARRAY;
	enc    = AMF_EncodeInt32(enc, end, a_idx == 0 ? 14 : 9);

	enc_num_val(&enc, end, "duration", 0.0);
	enc_num_val(&enc, end, "fileSize", 0.0);

	if (a_idx == 0) {
		enc_num_val(&enc, end, "width",
				(double)obs_encoder_get_width(vencoder));
		enc_num_val(&enc, end, "height",
				(double)obs_encoder_get_height(vencoder));

		enc_str_val(&enc, end, "videocodecid", "avc1");
		enc_num_val(&enc, end, "videodatarate",
				encoder_bitrate(vencoder));
		enc_num_val(&enc, end, "framerate",
				video_output_get_frame_rate(video));
	}

	enc_str_val(&enc, end, "audiocodecid", "mp4a");
	enc_num_val(&enc, end, "audiodatarate", encoder_bitrate(aencoder));
	enc_num_val(&enc, end, "audiosamplerate",
			(double)obs_encoder_get_sample_rate(aencoder));
	enc_num_val(&enc, end, "audiosamplesize", 16.0);
	enc_num_val(&enc, end, "audiochannels",
			(double)audio_output_get_channels(audio));

	enc_bool_val(&enc, end, "stereo",
			audio_output_get_channels(audio) == 2);
	enc_bool_val(&enc, end, "2.1",
			audio_output_get_channels(audio) == 3);
	enc_bool_val(&enc, end, "3.1",
			audio_output_get_channels(audio) == 4);
	enc_bool_val(&enc, end, "4.0",
			audio_output_get_channels(audio) == 4);
	enc_bool_val(&enc, end, "4.1",
			audio_output_get_channels(audio) == 5);
	enc_bool_val(&enc, end, "5.1",
			audio_output_get_channels(audio) == 6);
	enc_bool_val(&enc, end, "7.1",
			audio_output_get_channels(audio) == 8);

	dstr_printf(&encoder_name, "%s (libobs version ",
			MODULE_NAME);

#ifdef HAVE_OBSCONFIG_H
	dstr_cat(&encoder_name, OBS_VERSION);
#else
	dstr_catf(&encoder_name, "%d.%d.%d",
			LIBOBS_API_MAJOR_VER,
			LIBOBS_API_MINOR_VER,
			LIBOBS_API_PATCH_VER);
#endif

	dstr_cat(&encoder_name, ")");

	enc_str_val(&enc, end, "encoder", encoder_name.array);
	dstr_free(&encoder_name);

	*enc++  = 0;
	*enc++  = 0;
	*enc++  = AMF_OBJECT_END;

	*size   = enc-buf;
	*output = bmemdup(buf, *size);
	return true;
}
static void *enc_create(obs_data_t *settings, obs_encoder_t *encoder,
		const char *type, const char *alt)
{
	struct enc_encoder *enc;
	int                bitrate = (int)obs_data_get_int(settings, "bitrate");
	audio_t            *audio   = obs_encoder_audio(encoder);

	avcodec_register_all();

	enc          = bzalloc(sizeof(struct enc_encoder));
	enc->encoder = encoder;
	enc->codec   = avcodec_find_encoder_by_name(type);
	enc->type    = type;

	if (!enc->codec && alt) {
		enc->codec = avcodec_find_encoder_by_name(alt);
		enc->type  = alt;
	}

	blog(LOG_INFO, "---------------------------------");

	if (!enc->codec) {
		warn("Couldn't find encoder");
		goto fail;
	}

	if (!bitrate) {
		warn("Invalid bitrate specified");
		return NULL;
	}

	enc->context = avcodec_alloc_context3(enc->codec);
	if (!enc->context) {
		warn("Failed to create codec context");
		goto fail;
	}

	enc->context->bit_rate    = bitrate * 1000;
	enc->context->channels    = (int)audio_output_get_channels(audio);
	enc->context->sample_rate = audio_output_get_sample_rate(audio);
	enc->context->sample_fmt  = enc->codec->sample_fmts ?
		enc->codec->sample_fmts[0] : AV_SAMPLE_FMT_FLTP;

	/* check to make sure sample rate is supported */
	if (enc->codec->supported_samplerates) {
		const int *rate = enc->codec->supported_samplerates;
		int cur_rate = enc->context->sample_rate;
		int closest = 0;

		while (*rate) {
			int dist = abs(cur_rate - *rate);
			int closest_dist = abs(cur_rate - closest);

			if (dist < closest_dist)
				closest = *rate;
			rate++;
		}

		if (closest)
			enc->context->sample_rate = closest;
	}

	/* if using FFmpeg's AAC encoder, at least set a cutoff value
	 * (recommended by konverter) */
	if (strcmp(enc->codec->name, "aac") == 0) {
		int cutoff1 = 4000 + (int)enc->context->bit_rate / 8;
		int cutoff2 = 12000 + (int)enc->context->bit_rate / 8;
		int cutoff3 = enc->context->sample_rate / 2;
		int cutoff;

		cutoff = MIN(cutoff1, cutoff2);
		cutoff = MIN(cutoff, cutoff3);
		enc->context->cutoff = cutoff;
	}

	info("bitrate: %" PRId64 ", channels: %d",
			enc->context->bit_rate / 1000, enc->context->channels);

	init_sizes(enc, audio);

	/* enable experimental FFmpeg encoder if the only one available */
	enc->context->strict_std_compliance = -2;

	enc->context->flags = CODEC_FLAG_GLOBAL_HEADER;

	if (initialize_codec(enc))
		return enc;

fail:
	enc_destroy(enc);
	return NULL;
}
Example #8
0
bool audio_callback(void *param,
		uint64_t start_ts_in, uint64_t end_ts_in, uint64_t *out_ts,
		uint32_t mixers, struct audio_output_data *mixes)
{
	struct obs_core_data *data = &obs->data;
	struct obs_core_audio *audio = &obs->audio;
	struct obs_source *source;
	size_t sample_rate = audio_output_get_sample_rate(audio->audio);
	size_t channels = audio_output_get_channels(audio->audio);
	struct ts_info ts = {start_ts_in, end_ts_in};
	size_t audio_size;
	uint64_t min_ts;

	da_resize(audio->render_order, 0);
	da_resize(audio->root_nodes, 0);

	circlebuf_push_back(&audio->buffered_timestamps, &ts, sizeof(ts));
	circlebuf_peek_front(&audio->buffered_timestamps, &ts, sizeof(ts));
	min_ts = ts.start;

	audio_size = AUDIO_OUTPUT_FRAMES * sizeof(float);

#if DEBUG_AUDIO == 1
	blog(LOG_DEBUG, "ts %llu-%llu", ts.start, ts.end);
#endif

	/* ------------------------------------------------ */
	/* build audio render order
	 * NOTE: these are source channels, not audio channels */
	for (uint32_t i = 0; i < MAX_CHANNELS; i++) {
		obs_source_t *source = obs_get_output_source(i);
		if (source) {
			obs_source_enum_active_tree(source, push_audio_tree,
					audio);
			push_audio_tree(NULL, source, audio);
			da_push_back(audio->root_nodes, &source);
			obs_source_release(source);
		}
	}

	pthread_mutex_lock(&data->audio_sources_mutex);

	source = data->first_audio_source;
	while (source) {
		push_audio_tree(NULL, source, audio);
		source = (struct obs_source*)source->next_audio_source;
	}

	pthread_mutex_unlock(&data->audio_sources_mutex);

	/* ------------------------------------------------ */
	/* render audio data */
	for (size_t i = 0; i < audio->render_order.num; i++) {
		obs_source_t *source = audio->render_order.array[i];
		obs_source_audio_render(source, mixers, channels, sample_rate,
				audio_size);
	}

	/* ------------------------------------------------ */
	/* get minimum audio timestamp */
	pthread_mutex_lock(&data->audio_sources_mutex);
	calc_min_ts(data, sample_rate, &min_ts);
	pthread_mutex_unlock(&data->audio_sources_mutex);

	/* ------------------------------------------------ */
	/* if a source has gone backward in time, buffer */
	if (min_ts < ts.start)
		add_audio_buffering(audio, sample_rate, &ts, min_ts);

	/* ------------------------------------------------ */
	/* mix audio */
	if (!audio->buffering_wait_ticks) {
		for (size_t i = 0; i < audio->root_nodes.num; i++) {
			obs_source_t *source = audio->root_nodes.array[i];

			if (source->audio_pending)
				continue;

			pthread_mutex_lock(&source->audio_buf_mutex);

			if (source->audio_output_buf[0][0] && source->audio_ts)
				mix_audio(mixes, source, channels, sample_rate,
						&ts);

			pthread_mutex_unlock(&source->audio_buf_mutex);
		}
	}

	/* ------------------------------------------------ */
	/* discard audio */
	pthread_mutex_lock(&data->audio_sources_mutex);

	source = data->first_audio_source;
	while (source) {
		pthread_mutex_lock(&source->audio_buf_mutex);
		discard_audio(audio, source, channels, sample_rate, &ts);
		pthread_mutex_unlock(&source->audio_buf_mutex);

		source = (struct obs_source*)source->next_audio_source;
	}

	pthread_mutex_unlock(&data->audio_sources_mutex);

	/* ------------------------------------------------ */
	/* release audio sources */
	release_audio_sources(audio);

	circlebuf_pop_front(&audio->buffered_timestamps, NULL, sizeof(ts));

	*out_ts = ts.start;

	if (audio->buffering_wait_ticks) {
		audio->buffering_wait_ticks--;
		return false;
	}

	UNUSED_PARAMETER(param);
	return true;
}