Browse Source

libobs: Implement and use better scaling function for 64-bit integers

As os_gettime_ns() gets large the current scaling methods, mostly by casting
to uint64_t, may lead to numerical overflows. Sweep the code and use
util_mul_div64() where applicable.

Signed-off-by: Hans Petter Selasky <[email protected]>
Hans Petter Selasky 5 years ago
parent
commit
0e4ea14ba1

+ 6 - 3
UI/window-basic-auto-config-test.cpp

@@ -4,6 +4,7 @@
 
 #include <obs.hpp>
 #include <util/platform.h>
+#include <util/util_uint64.h>
 #include <graphics/vec4.h>
 #include <graphics/graphics.h>
 #include <graphics/math-extra.h>
@@ -413,12 +414,14 @@ void AutoConfigTestPage::TestBandwidthThread()
 		cv.wait(ul);
 
 		uint64_t total_time = os_gettime_ns() - t_start;
+		if (total_time == 0)
+			total_time = 1;
 
 		int total_bytes =
 			(int)obs_output_get_total_bytes(output) - start_bytes;
-		uint64_t bitrate = (uint64_t)total_bytes * 8 * 1000000000 /
-				   total_time / 1000;
-
+		uint64_t bitrate = util_mul_div64(
+			total_bytes, 8ULL * 1000000000ULL / 1000ULL,
+			total_time);
 		if (obs_output_get_frames_dropped(output) ||
 		    (int)bitrate < (wiz->startingBitrate * 75 / 100)) {
 			server.bitrate = (int)bitrate * 70 / 100;

+ 1 - 0
libobs/CMakeLists.txt

@@ -360,6 +360,7 @@ set(libobs_util_HEADERS
 	util/text-lookup.h
 	util/bmem.h
 	util/c99defs.h
+	util/util_uint64.h
 	util/util_uint128.h
 	util/cf-parser.h
 	util/threading.h

+ 5 - 4
libobs/audio-monitoring/win32/wasapi-output.c

@@ -2,6 +2,7 @@
 #include "../../util/circlebuf.h"
 #include "../../util/platform.h"
 #include "../../util/darray.h"
+#include "../../util/util_uint64.h"
 #include "../../obs-internal.h"
 
 #include "wasapi-output.h"
@@ -78,8 +79,8 @@ static bool process_audio_delay(struct audio_monitor *monitor, float **data,
 		monitor->prev_video_ts = last_frame_ts;
 
 	} else if (monitor->prev_video_ts == last_frame_ts) {
-		monitor->time_since_prev += (uint64_t)*frames * 1000000000ULL /
-					    (uint64_t)monitor->sample_rate;
+		monitor->time_since_prev += util_mul_div64(
+			*frames, 1000000000ULL, monitor->sample_rate);
 	} else {
 		monitor->time_since_prev = 0;
 	}
@@ -90,8 +91,8 @@ static bool process_audio_delay(struct audio_monitor *monitor, float **data,
 
 		circlebuf_peek_front(&monitor->delay_buffer, &cur_ts,
 				     sizeof(ts));
-		front_ts = cur_ts - ((uint64_t)pad * 1000000000ULL /
-				     (uint64_t)monitor->sample_rate);
+		front_ts = cur_ts - util_mul_div64(pad, 1000000000ULL,
+						   monitor->sample_rate);
 		diff = (int64_t)front_ts - (int64_t)last_frame_ts;
 		bad_diff = !last_frame_ts || llabs(diff) > 5000000000 ||
 			   monitor->time_since_prev > 100000000ULL;

+ 2 - 5
libobs/media-io/audio-io.c

@@ -23,6 +23,7 @@
 #include "../util/circlebuf.h"
 #include "../util/platform.h"
 #include "../util/profiler.h"
+#include "../util/util_uint64.h"
 
 #include "audio-io.h"
 #include "audio-resampler.h"
@@ -78,11 +79,7 @@ struct audio_output {
 
 static inline double ts_to_frames(const audio_t *audio, uint64_t ts)
 {
-	double audio_offset_d = (double)ts;
-	audio_offset_d /= 1000000000.0;
-	audio_offset_d *= (double)audio->info.samples_per_sec;
-
-	return audio_offset_d;
+	return util_mul_div64(ts, audio->info.samples_per_sec, 1000000000ULL);
 }
 
 static inline double positive_round(double val)

+ 3 - 9
libobs/media-io/audio-io.h

@@ -19,7 +19,7 @@
 
 #include "media-io-defs.h"
 #include "../util/c99defs.h"
-#include "../util/util_uint128.h"
+#include "../util/util_uint64.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -195,18 +195,12 @@ static inline size_t get_audio_size(enum audio_format format,
 
 static inline uint64_t audio_frames_to_ns(size_t sample_rate, uint64_t frames)
 {
-	util_uint128_t val;
-	val = util_mul64_64(frames, 1000000000ULL);
-	val = util_div128_32(val, (uint32_t)sample_rate);
-	return val.low;
+	return util_mul_div64(frames, 1000000000ULL, sample_rate);
 }
 
 static inline uint64_t ns_to_audio_frames(size_t sample_rate, uint64_t frames)
 {
-	util_uint128_t val;
-	val = util_mul64_64(frames, sample_rate);
-	val = util_div128_32(val, 1000000000);
-	return val.low;
+	return util_mul_div64(frames, sample_rate, 1000000000ULL);
 }
 
 #define AUDIO_OUTPUT_SUCCESS 0

+ 3 - 2
libobs/media-io/video-io.c

@@ -22,6 +22,7 @@
 #include "../util/profiler.h"
 #include "../util/threading.h"
 #include "../util/darray.h"
+#include "../util/util_uint64.h"
 
 #include "format-conversion.h"
 #include "video-io.h"
@@ -234,8 +235,8 @@ int video_output_open(video_t **video, struct video_output_info *info)
 		goto fail;
 
 	memcpy(&out->info, info, sizeof(struct video_output_info));
-	out->frame_time = (uint64_t)(1000000000.0 * (double)info->fps_den /
-				     (double)info->fps_num);
+	out->frame_time =
+		util_mul_div64(1000000000ULL, info->fps_den, info->fps_num);
 	out->initialized = false;
 
 	if (pthread_mutexattr_init(&attr) != 0)

+ 4 - 3
libobs/obs-audio.c

@@ -17,6 +17,7 @@
 
 #include <inttypes.h>
 #include "obs-internal.h"
+#include "util/util_uint64.h"
 
 struct ts_info {
 	uint64_t start;
@@ -41,7 +42,7 @@ static void push_audio_tree(obs_source_t *parent, obs_source_t *source, void *p)
 
 static inline size_t convert_time_to_frames(size_t sample_rate, uint64_t t)
 {
-	return (size_t)(t * (uint64_t)sample_rate / 1000000000ULL);
+	return util_mul_div64(t, sample_rate, 1000000000ULL);
 }
 
 static inline void mix_audio(struct audio_output_data *mixes,
@@ -90,8 +91,8 @@ static void ignore_audio(obs_source_t *source, size_t channels,
 					    source->audio_input_buf[ch].size);
 
 		source->last_audio_input_buf_size = 0;
-		source->audio_ts += (uint64_t)num_floats * 1000000000ULL /
-				    (uint64_t)sample_rate;
+		source->audio_ts +=
+			util_mul_div64(num_floats, 1000000000ULL, sample_rate);
 	}
 }
 

+ 4 - 4
libobs/obs-encoder.c

@@ -17,6 +17,7 @@
 
 #include "obs.h"
 #include "obs-internal.h"
+#include "util/util_uint64.h"
 
 #define encoder_active(encoder) os_atomic_load_bool(&encoder->active)
 #define set_encoder_active(encoder, val) \
@@ -1073,8 +1074,7 @@ static inline size_t calc_offset_size(struct obs_encoder *encoder,
 				      uint64_t v_start_ts, uint64_t a_start_ts)
 {
 	uint64_t offset = v_start_ts - a_start_ts;
-	offset = (uint64_t)offset * (uint64_t)encoder->samplerate /
-		 1000000000ULL;
+	offset = util_mul_div64(offset, encoder->samplerate, 1000000000ULL);
 	return (size_t)offset * encoder->blocksize;
 }
 
@@ -1121,8 +1121,8 @@ static bool buffer_audio(struct obs_encoder *encoder, struct audio_data *data)
 
 		/* audio starting point still not synced with video starting
 		 * point, so don't start audio */
-		end_ts += (uint64_t)data->frames * 1000000000ULL /
-			  (uint64_t)encoder->samplerate;
+		end_ts += util_mul_div64(data->frames, 1000000000ULL,
+					 encoder->samplerate);
 		if (end_ts <= v_start_ts) {
 			success = false;
 			goto fail;

+ 5 - 3
libobs/obs-output.c

@@ -17,6 +17,7 @@
 
 #include <inttypes.h>
 #include "util/platform.h"
+#include "util/util_uint64.h"
 #include "obs.h"
 #include "obs-internal.h"
 
@@ -1731,8 +1732,8 @@ static bool prepare_audio(struct obs_output *output,
 	*new = *old;
 
 	if (old->timestamp < output->video_start_ts) {
-		uint64_t duration = (uint64_t)old->frames * 1000000000 /
-				    (uint64_t)output->sample_rate;
+		uint64_t duration = util_mul_div64(old->frames, 1000000000ULL,
+						   output->sample_rate);
 		uint64_t end_ts = (old->timestamp + duration);
 		uint64_t cutoff;
 
@@ -1742,7 +1743,8 @@ static bool prepare_audio(struct obs_output *output,
 		cutoff = output->video_start_ts - old->timestamp;
 		new->timestamp += cutoff;
 
-		cutoff = cutoff * (uint64_t)output->sample_rate / 1000000000;
+		cutoff = util_mul_div64(cutoff, output->sample_rate,
+					1000000000ULL);
 
 		for (size_t i = 0; i < output->planes; i++)
 			new->data[i] += output->audio_size *(uint32_t)cutoff;

+ 5 - 4
libobs/obs-scene.c

@@ -17,6 +17,7 @@
 ******************************************************************************/
 
 #include "util/threading.h"
+#include "util/util_uint64.h"
 #include "graphics/math-defs.h"
 #include "obs-scene.h"
 
@@ -974,8 +975,8 @@ static void apply_scene_item_audio_actions(struct obs_scene_item *item,
 		if (timestamp < ts)
 			timestamp = ts;
 
-		new_frame_num = (timestamp - ts) * (uint64_t)sample_rate /
-				1000000000ULL;
+		new_frame_num = util_mul_div64(timestamp - ts, sample_rate,
+					       1000000000ULL);
 
 		if (ts && new_frame_num >= AUDIO_OUTPUT_FRAMES)
 			break;
@@ -1024,8 +1025,8 @@ static bool apply_scene_item_volume(struct obs_scene_item *item, float **buf,
 	pthread_mutex_unlock(&item->actions_mutex);
 
 	if (actions_pending) {
-		uint64_t duration = (uint64_t)AUDIO_OUTPUT_FRAMES *
-				    1000000000ULL / (uint64_t)sample_rate;
+		uint64_t duration = util_mul_div64(AUDIO_OUTPUT_FRAMES,
+						   1000000000ULL, sample_rate);
 
 		if (!ts || action.timestamp < (ts + duration)) {
 			apply_scene_item_audio_actions(item, buf, ts,

+ 2 - 1
libobs/obs-source-transition.c

@@ -16,6 +16,7 @@
 ******************************************************************************/
 
 #include "obs-internal.h"
+#include "util/util_uint64.h"
 #include "graphics/math-extra.h"
 
 #define lock_transition(transition) \
@@ -866,7 +867,7 @@ static inline float get_sample_time(obs_source_t *transition,
 				    uint64_t ts)
 {
 	uint64_t sample_ts_offset =
-		(uint64_t)sample * 1000000000ULL / (uint64_t)sample_rate;
+		util_mul_div64(sample, 1000000000ULL, sample_rate);
 	uint64_t i_ts = ts + sample_ts_offset;
 	return calc_time(transition, i_ts);
 }

+ 4 - 3
libobs/obs-source.c

@@ -23,6 +23,7 @@
 #include "media-io/audio-io.h"
 #include "util/threading.h"
 #include "util/platform.h"
+#include "util/util_uint64.h"
 #include "callback/calldata.h"
 #include "graphics/matrix3.h"
 #include "graphics/vec3.h"
@@ -1165,13 +1166,13 @@ static inline uint64_t conv_frames_to_time(const size_t sample_rate,
 	if (!sample_rate)
 		return 0;
 
-	return (uint64_t)frames * 1000000000ULL / (uint64_t)sample_rate;
+	return util_mul_div64(frames, 1000000000ULL, sample_rate);
 }
 
 static inline size_t conv_time_to_frames(const size_t sample_rate,
 					 const uint64_t duration)
 {
-	return (size_t)(duration * (uint64_t)sample_rate / 1000000000ULL);
+	return (size_t)util_mul_div64(duration, sample_rate, 1000000000ULL);
 }
 
 /* maximum buffer size */
@@ -1235,7 +1236,7 @@ static inline uint64_t uint64_diff(uint64_t ts1, uint64_t ts2)
 static inline size_t get_buf_placement(audio_t *audio, uint64_t offset)
 {
 	uint32_t sample_rate = audio_output_get_sample_rate(audio);
-	return (size_t)(offset * (uint64_t)sample_rate / 1000000000ULL);
+	return (size_t)util_mul_div64(offset, sample_rate, 1000000000ULL);
 }
 
 static void source_output_audio_place(obs_source_t *source,

+ 24 - 0
libobs/util/util_uint64.h

@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2020 Hans Petter Selasky <[email protected]>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#pragma once
+
+static inline uint64_t util_mul_div64(uint64_t num, uint64_t mul, uint64_t div)
+{
+	const uint64_t rem = num % div;
+
+	return (num / div) * mul + (rem * mul) / div;
+}

+ 4 - 3
plugins/decklink/decklink-device-instance.cpp

@@ -6,6 +6,7 @@
 
 #include <util/platform.h>
 #include <util/threading.h>
+#include <util/util_uint64.h>
 
 #include <sstream>
 #include <algorithm>
@@ -90,8 +91,8 @@ void DeckLinkDeviceInstance::HandleAudioPacket(
 	if (decklink && !static_cast<DeckLinkInput *>(decklink)->buffering) {
 		currentPacket.timestamp = os_gettime_ns();
 		currentPacket.timestamp -=
-			(uint64_t)frameCount * 1000000000ULL /
-			(uint64_t)currentPacket.samples_per_sec;
+			util_mul_div64(frameCount, 1000000000ULL,
+				       currentPacket.samples_per_sec);
 	}
 
 	int maxdevicechannel = device->GetMaxChannel();
@@ -113,7 +114,7 @@ void DeckLinkDeviceInstance::HandleAudioPacket(
 	}
 
 	nextAudioTS = timestamp +
-		      ((uint64_t)frameCount * 1000000000ULL / 48000ULL) + 1;
+		      util_mul_div64(frameCount, 1000000000ULL, 48000ULL) + 1;
 
 	obs_source_output_audio(
 		static_cast<DeckLinkInput *>(decklink)->GetSource(),

+ 5 - 3
plugins/decklink/decklink-output.cpp

@@ -9,6 +9,7 @@
 #include "decklink-devices.hpp"
 
 #include "../../libobs/media-io/video-scaler.h"
+#include "../../libobs/util/util_uint64.h"
 
 static void decklink_output_destroy(void *data)
 {
@@ -127,8 +128,8 @@ static bool prepare_audio(DeckLinkOutput *decklink,
 	*output = *frame;
 
 	if (frame->timestamp < decklink->start_timestamp) {
-		uint64_t duration = (uint64_t)frame->frames * 1000000000 /
-				    (uint64_t)decklink->audio_samplerate;
+		uint64_t duration = util_mul_div64(frame->frames, 1000000000ULL,
+						   decklink->audio_samplerate);
 		uint64_t end_ts = frame->timestamp + duration;
 		uint64_t cutoff;
 
@@ -138,7 +139,8 @@ static bool prepare_audio(DeckLinkOutput *decklink,
 		cutoff = decklink->start_timestamp - frame->timestamp;
 		output->timestamp += cutoff;
 
-		cutoff *= (uint64_t)decklink->audio_samplerate / 1000000000;
+		cutoff = util_mul_div64(cutoff, decklink->audio_samplerate,
+					1000000000ULL);
 
 		for (size_t i = 0; i < decklink->audio_planes; i++)
 			output->data[i] +=

+ 4 - 2
plugins/linux-alsa/alsa-input.c

@@ -20,6 +20,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #include <util/bmem.h>
 #include <util/platform.h>
 #include <util/threading.h>
+#include <util/util_uint64.h>
 #include <obs-module.h>
 
 #include <alsa/asoundlib.h>
@@ -562,8 +563,9 @@ void *_alsa_listen(void *attr)
 		}
 
 		out.frames = frames;
-		out.timestamp = os_gettime_ns() -
-				((frames * NSEC_PER_SEC) / data->rate);
+		out.timestamp =
+			os_gettime_ns() -
+			util_mul_div64(frames, NSEC_PER_SEC, data->rate);
 
 		if (!data->first_ts)
 			data->first_ts = out.timestamp + STARTUP_TIMEOUT_NS;

+ 2 - 1
plugins/linux-pulseaudio/pulse-input.c

@@ -17,6 +17,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 #include <util/platform.h>
 #include <util/bmem.h>
+#include <util/util_uint64.h>
 #include <obs-module.h>
 
 #include "pulse-wrapper.h"
@@ -161,7 +162,7 @@ static pa_channel_map pulse_channel_map(enum speaker_layout layout)
 
 static inline uint64_t samples_to_ns(size_t frames, uint_fast32_t rate)
 {
-	return frames * NSEC_PER_SEC / rate;
+	return util_mul_div64(frames, NSEC_PER_SEC, rate);
 }
 
 static inline uint64_t get_sample_time(size_t frames, uint_fast32_t rate)

+ 3 - 1
plugins/obs-filters/async-delay-filter.c

@@ -1,5 +1,6 @@
 #include <obs-module.h>
 #include <util/circlebuf.h>
+#include <util/util_uint64.h>
 
 #ifndef SEC_TO_NSEC
 #define SEC_TO_NSEC 1000000000ULL
@@ -199,7 +200,8 @@ async_delay_filter_audio(void *data, struct obs_audio_data *audio)
 
 	filter->last_audio_ts = audio->timestamp;
 
-	duration = (uint64_t)audio->frames * SEC_TO_NSEC / filter->samplerate;
+	duration =
+		util_mul_div64(audio->frames, SEC_TO_NSEC, filter->samplerate);
 	end_ts = audio->timestamp + duration;
 
 	for (size_t i = 0; i < MAX_AV_PLANES; i++) {

+ 2 - 2
plugins/obs-filters/gpu-delay.c

@@ -1,5 +1,6 @@
 #include <obs-module.h>
 #include <util/circlebuf.h>
+#include <util/util_uint64.h>
 
 #define S_DELAY_MS "delay_ms"
 #define T_DELAY_MS obs_module_text("DelayMs")
@@ -90,8 +91,7 @@ static inline void check_interval(struct gpu_delay_filter_data *f)
 
 	obs_get_video_info(&ovi);
 
-	interval_ns =
-		(uint64_t)ovi.fps_den * 1000000000ULL / (uint64_t)ovi.fps_num;
+	interval_ns = util_mul_div64(ovi.fps_den, 1000000000ULL, ovi.fps_num);
 
 	if (interval_ns != f->interval_ns)
 		update_interval(f, interval_ns);

+ 3 - 1
plugins/win-capture/game-capture.c

@@ -6,6 +6,7 @@
 #include <windows.h>
 #include <dxgi.h>
 #include <util/sse-intrin.h>
+#include <util/util_uint64.h>
 #include <ipc-util/pipe.h>
 #include "obfuscate.h"
 #include "inject-library.h"
@@ -729,7 +730,8 @@ static inline void reset_frame_interval(struct game_capture *gc)
 	uint64_t interval = 0;
 
 	if (obs_get_video_info(&ovi)) {
-		interval = ovi.fps_den * 1000000000ULL / ovi.fps_num;
+		interval =
+			util_mul_div64(ovi.fps_den, 1000000000ULL, ovi.fps_num);
 
 		/* Always limit capture framerate to some extent.  If a game
 		 * running at 900 FPS is being captured without some sort of

+ 3 - 2
plugins/win-wasapi/win-wasapi.cpp

@@ -8,6 +8,7 @@
 #include <util/windows/WinHandle.hpp>
 #include <util/windows/CoTaskMemPtr.hpp>
 #include <util/threading.h>
+#include <util/util_uint64.h>
 
 using namespace std;
 
@@ -464,8 +465,8 @@ bool WASAPISource::ProcessCaptureData()
 		data.timestamp = useDeviceTiming ? ts * 100 : os_gettime_ns();
 
 		if (!useDeviceTiming)
-			data.timestamp -= (uint64_t)frames * 1000000000ULL /
-					  (uint64_t)sampleRate;
+			data.timestamp -= util_mul_div64(frames, 1000000000ULL,
+							 sampleRate);
 
 		obs_source_output_audio(source, &data);
 

+ 3 - 1
test/test-input/sync-pair-aud.c

@@ -2,6 +2,7 @@
 #include <util/bmem.h>
 #include <util/threading.h>
 #include <util/platform.h>
+#include <util/util_uint64.h>
 #include <obs.h>
 
 struct sync_pair_aud {
@@ -54,7 +55,8 @@ static void *sync_pair_aud_thread(void *pdata)
 
 		for (uint64_t i = 0; i < frames; i++) {
 			uint64_t ts =
-				last_time + i * 1000000000ULL / sample_rate;
+				last_time +
+				util_mul_div64(i, 1000000000ULL, sample_rate);
 
 			if (whitelist_time(ts, interval, fps_num, fps_den)) {
 				cos_val += rate * M_PI_X2;