libobs: Refactor obs-output encoded use of mixes

There was quite a bit of conflated usage of mixes (which refers
to raw audio) and encoder counts. This fully separates the two
and makes a distinct separation when iterating over mixes vs
encoders.
This commit is contained in:
John Bradley 2023-01-17 00:48:34 -06:00 committed by tt2468
parent d70171daa6
commit 61284cf9ba
3 changed files with 108 additions and 113 deletions

View file

@ -1024,7 +1024,7 @@ struct obs_output {
volatile bool data_active;
volatile bool end_data_capture_thread_active;
int64_t video_offset;
int64_t audio_offsets[MAX_AUDIO_MIXES];
int64_t audio_offsets[MAX_OUTPUT_AUDIO_ENCODERS];
int64_t highest_audio_ts;
int64_t highest_video_ts;
pthread_t end_data_capture_thread;
@ -1054,7 +1054,7 @@ struct obs_output {
video_t *video;
audio_t *audio;
obs_encoder_t *video_encoder;
obs_encoder_t *audio_encoders[MAX_AUDIO_MIXES];
obs_encoder_t *audio_encoders[MAX_OUTPUT_AUDIO_ENCODERS];
obs_service_t *service;
size_t mixer_mask;

View file

@ -180,7 +180,7 @@ static inline void free_packets(struct obs_output *output)
da_free(output->interleaved_packets);
}
static inline void clear_audio_buffers(obs_output_t *output)
static inline void clear_raw_audio_buffers(obs_output_t *output)
{
for (size_t i = 0; i < MAX_AUDIO_MIXES; i++) {
for (size_t j = 0; j < MAX_AV_PLANES; j++) {
@ -215,14 +215,14 @@ void obs_output_destroy(obs_output_t *output)
output);
}
for (size_t i = 0; i < MAX_AUDIO_MIXES; i++) {
for (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) {
if (output->audio_encoders[i]) {
obs_encoder_remove_output(
output->audio_encoders[i], output);
}
}
clear_audio_buffers(output);
clear_raw_audio_buffers(output);
os_event_destroy(output->stopping_event);
pthread_mutex_destroy(&output->pause.mutex);
@ -572,16 +572,16 @@ static inline bool pause_can_stop(struct pause_data *pause)
static bool obs_encoded_output_pause(obs_output_t *output, bool pause)
{
obs_encoder_t *venc;
obs_encoder_t *aenc[MAX_AUDIO_MIXES];
obs_encoder_t *aenc[MAX_OUTPUT_AUDIO_ENCODERS];
uint64_t closest_v_ts;
bool success = false;
venc = output->video_encoder;
for (size_t i = 0; i < MAX_AUDIO_MIXES; i++)
for (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++)
aenc[i] = output->audio_encoders[i];
pthread_mutex_lock(&venc->pause.mutex);
for (size_t i = 0; i < MAX_AUDIO_MIXES; i++) {
for (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) {
if (aenc[i]) {
pthread_mutex_lock(&aenc[i]->pause.mutex);
}
@ -595,7 +595,7 @@ static bool obs_encoded_output_pause(obs_output_t *output, bool pause)
if (!pause_can_start(&venc->pause)) {
goto fail;
}
for (size_t i = 0; i < MAX_AUDIO_MIXES; i++) {
for (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) {
if (aenc[i] && !pause_can_start(&aenc[i]->pause)) {
goto fail;
}
@ -604,7 +604,7 @@ static bool obs_encoded_output_pause(obs_output_t *output, bool pause)
os_atomic_set_bool(&venc->paused, true);
venc->pause.ts_start = closest_v_ts;
for (size_t i = 0; i < MAX_AUDIO_MIXES; i++) {
for (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) {
if (aenc[i]) {
os_atomic_set_bool(&aenc[i]->paused, true);
aenc[i]->pause.ts_start = closest_v_ts;
@ -614,7 +614,7 @@ static bool obs_encoded_output_pause(obs_output_t *output, bool pause)
if (!pause_can_stop(&venc->pause)) {
goto fail;
}
for (size_t i = 0; i < MAX_AUDIO_MIXES; i++) {
for (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) {
if (aenc[i] && !pause_can_stop(&aenc[i]->pause)) {
goto fail;
}
@ -623,7 +623,7 @@ static bool obs_encoded_output_pause(obs_output_t *output, bool pause)
os_atomic_set_bool(&venc->paused, false);
end_pause(&venc->pause, closest_v_ts);
for (size_t i = 0; i < MAX_AUDIO_MIXES; i++) {
for (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) {
if (aenc[i]) {
os_atomic_set_bool(&aenc[i]->paused, false);
end_pause(&aenc[i]->pause, closest_v_ts);
@ -636,7 +636,7 @@ static bool obs_encoded_output_pause(obs_output_t *output, bool pause)
success = true;
fail:
for (size_t i = MAX_AUDIO_MIXES; i > 0; i--) {
for (size_t i = MAX_OUTPUT_AUDIO_ENCODERS; i > 0; i--) {
if (aenc[i - 1]) {
pthread_mutex_unlock(&aenc[i - 1]->pause.mutex);
}
@ -801,8 +801,9 @@ void obs_output_remove_encoder_internal(struct obs_output *output,
if (output->video_encoder == encoder) {
output->video_encoder = NULL;
} else {
for (size_t i = 0; i < MAX_AUDIO_MIXES; i++) {
if (output->audio_encoders[i] == encoder)
for (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) {
obs_encoder_t *audio = output->audio_encoders[i];
if (audio == encoder)
output->audio_encoders[i] = NULL;
}
}
@ -870,7 +871,7 @@ void obs_output_set_audio_encoder(obs_output_t *output, obs_encoder_t *encoder,
}
if ((output->info.flags & OBS_OUTPUT_MULTI_TRACK) != 0) {
if (idx >= MAX_AUDIO_MIXES) {
if (idx >= MAX_OUTPUT_AUDIO_ENCODERS) {
return;
}
} else {
@ -900,15 +901,8 @@ obs_encoder_t *obs_output_get_audio_encoder(const obs_output_t *output,
if (!obs_output_valid(output, "obs_output_get_audio_encoder"))
return NULL;
if ((output->info.flags & OBS_OUTPUT_MULTI_TRACK) != 0) {
if (idx >= MAX_AUDIO_MIXES) {
return NULL;
}
} else {
if (idx > 0) {
return NULL;
}
}
if (idx >= MAX_OUTPUT_AUDIO_ENCODERS)
return NULL;
return output->audio_encoders[idx];
}
@ -1054,42 +1048,18 @@ void obs_output_set_audio_conversion(
output->audio_conversion_set = true;
}
static inline size_t num_audio_mixes(const struct obs_output *output)
{
size_t mix_count = 1;
if ((output->info.flags & OBS_OUTPUT_MULTI_TRACK) != 0) {
mix_count = 0;
for (size_t i = 0; i < MAX_AUDIO_MIXES; i++) {
if (!output->audio_encoders[i])
break;
mix_count++;
}
}
return mix_count;
}
static inline bool audio_valid(const struct obs_output *output, bool encoded)
{
if (encoded) {
size_t mix_count = num_audio_mixes(output);
if (!mix_count)
return false;
for (size_t i = 0; i < mix_count; i++) {
if (!output->audio_encoders[i]) {
return false;
for (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) {
if (output->audio_encoders[i]) {
return true;
}
}
} else {
if (!output->audio)
return false;
return false;
}
return true;
return output->audio != NULL;
}
static bool can_begin_data_capture(const struct obs_output *output,
@ -1166,10 +1136,10 @@ get_audio_conversion(struct obs_output *output)
static size_t get_track_index(const struct obs_output *output,
struct encoder_packet *pkt)
{
for (size_t i = 0; i < MAX_AUDIO_MIXES; i++) {
for (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) {
struct obs_encoder *encoder = output->audio_encoders[i];
if (pkt->encoder == encoder)
if (encoder && pkt->encoder == encoder)
return i;
}
@ -1420,7 +1390,6 @@ static size_t get_interleaved_start_idx(struct obs_output *output)
static int prune_premature_packets(struct obs_output *output)
{
size_t audio_mixes = num_audio_mixes(output);
struct encoder_packet *video;
int video_idx;
int max_idx;
@ -1438,10 +1407,13 @@ static int prune_premature_packets(struct obs_output *output)
video = &output->interleaved_packets.array[video_idx];
duration_usec = video->timebase_num * 1000000LL / video->timebase_den;
for (size_t i = 0; i < audio_mixes; i++) {
for (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) {
struct encoder_packet *audio;
int audio_idx;
if (!output->audio_encoders[i])
continue;
audio_idx = find_first_packet_type_idx(output,
OBS_ENCODER_AUDIO, i);
if (audio_idx == -1) {
@ -1565,18 +1537,20 @@ find_last_packet_type(struct obs_output *output, enum obs_encoder_type type,
static bool get_audio_and_video_packets(struct obs_output *output,
struct encoder_packet **video,
struct encoder_packet **audio,
size_t audio_mixes)
struct encoder_packet **audio)
{
*video = find_first_packet_type(output, OBS_ENCODER_VIDEO, 0);
if (!*video)
output->received_video = false;
for (size_t i = 0; i < audio_mixes; i++) {
audio[i] = find_first_packet_type(output, OBS_ENCODER_AUDIO, i);
if (!audio[i]) {
output->received_audio = false;
return false;
for (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) {
if (output->audio_encoders[i]) {
audio[i] = find_first_packet_type(output,
OBS_ENCODER_AUDIO, i);
if (!audio[i]) {
output->received_audio = false;
return false;
}
}
}
@ -1587,26 +1561,50 @@ static bool get_audio_and_video_packets(struct obs_output *output,
return true;
}
static bool get_first_audio_encoder_index(const struct obs_output *output,
size_t *index)
{
if (!index)
return false;
for (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) {
if (output->audio_encoders[i]) {
*index = i;
return true;
}
}
return false;
}
static bool initialize_interleaved_packets(struct obs_output *output)
{
struct encoder_packet *video;
struct encoder_packet *audio[MAX_AUDIO_MIXES];
struct encoder_packet *last_audio[MAX_AUDIO_MIXES];
size_t audio_mixes = num_audio_mixes(output);
struct encoder_packet *audio[MAX_OUTPUT_AUDIO_ENCODERS];
struct encoder_packet *last_audio[MAX_OUTPUT_AUDIO_ENCODERS];
size_t start_idx;
size_t first_audio_idx;
if (!get_audio_and_video_packets(output, &video, audio, audio_mixes))
if (!get_first_audio_encoder_index(output, &first_audio_idx))
return false;
for (size_t i = 0; i < audio_mixes; i++)
last_audio[i] =
find_last_packet_type(output, OBS_ENCODER_AUDIO, i);
if (!get_audio_and_video_packets(output, &video, audio))
return false;
for (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) {
if (output->audio_encoders[i]) {
last_audio[i] = find_last_packet_type(
output, OBS_ENCODER_AUDIO, i);
}
}
/* ensure that there is audio past the first video packet */
for (size_t i = 0; i < audio_mixes; i++) {
if (last_audio[i]->dts_usec < video->dts_usec) {
output->received_audio = false;
return false;
for (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) {
if (output->audio_encoders[i]) {
if (last_audio[i]->dts_usec < video->dts_usec) {
output->received_audio = false;
return false;
}
}
}
@ -1614,19 +1612,20 @@ static bool initialize_interleaved_packets(struct obs_output *output)
start_idx = get_interleaved_start_idx(output);
if (start_idx) {
discard_to_idx(output, start_idx);
if (!get_audio_and_video_packets(output, &video, audio,
audio_mixes))
if (!get_audio_and_video_packets(output, &video, audio))
return false;
}
/* get new offsets */
output->video_offset = video->pts;
for (size_t i = 0; i < audio_mixes; i++)
output->audio_offsets[i] = audio[i]->dts;
for (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) {
if (output->audio_encoders[i]) {
output->audio_offsets[i] = audio[i]->dts;
}
}
#if DEBUG_STARTING_PACKETS == 1
int64_t v = video->dts_usec;
int64_t a = audio[0]->dts_usec;
int64_t a = audio[first_audio_idx]->dts_usec;
int64_t diff = v - a;
blog(LOG_DEBUG,
@ -1636,7 +1635,7 @@ static bool initialize_interleaved_packets(struct obs_output *output)
#endif
/* subtract offsets from highest TS offset variables */
output->highest_audio_ts -= audio[0]->dts_usec;
output->highest_audio_ts -= audio[first_audio_idx]->dts_usec;
output->highest_video_ts -= video->dts_usec;
/* apply new offsets to all existing packet DTS/PTS values */
@ -1885,11 +1884,11 @@ static void default_raw_audio_callback(void *param, size_t mix_idx,
static inline void start_audio_encoders(struct obs_output *output,
encoded_callback_t encoded_callback)
{
size_t num_mixes = num_audio_mixes(output);
for (size_t i = 0; i < num_mixes; i++) {
obs_encoder_start(output->audio_encoders[i], encoded_callback,
output);
for (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) {
if (output->audio_encoders[i]) {
obs_encoder_start(output->audio_encoders[i],
encoded_callback, output);
}
}
}
@ -1919,7 +1918,7 @@ static void reset_packet_data(obs_output_t *output)
output->highest_video_ts = 0;
output->video_offset = 0;
for (size_t i = 0; i < MAX_AUDIO_MIXES; i++)
for (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++)
output->audio_offsets[i] = 0;
free_packets(output);
@ -2049,14 +2048,14 @@ bool obs_output_can_begin_data_capture(const obs_output_t *output,
has_service);
}
static inline bool initialize_audio_encoders(obs_output_t *output,
size_t num_mixes)
static inline bool initialize_audio_encoders(obs_output_t *output)
{
for (size_t i = 0; i < num_mixes; i++) {
if (!obs_encoder_initialize(output->audio_encoders[i])) {
for (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) {
obs_encoder_t *audio = output->audio_encoders[i];
if (audio && !obs_encoder_initialize(audio)) {
obs_output_set_last_error(
output, obs_encoder_get_last_error(
output->audio_encoders[i]));
output, obs_encoder_get_last_error(audio));
return false;
}
}
@ -2064,10 +2063,9 @@ static inline bool initialize_audio_encoders(obs_output_t *output,
return true;
}
static inline obs_encoder_t *find_inactive_audio_encoder(obs_output_t *output,
size_t num_mixes)
static inline obs_encoder_t *find_inactive_audio_encoder(obs_output_t *output)
{
for (size_t i = 0; i < num_mixes; i++) {
for (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) {
struct obs_encoder *audio = output->audio_encoders[i];
if (audio && !audio->active && !audio->paired_encoder)
@ -2077,11 +2075,10 @@ static inline obs_encoder_t *find_inactive_audio_encoder(obs_output_t *output,
return NULL;
}
static inline void pair_encoders(obs_output_t *output, size_t num_mixes)
static inline void pair_encoders(obs_output_t *output)
{
struct obs_encoder *video = output->video_encoder;
struct obs_encoder *audio =
find_inactive_audio_encoder(output, num_mixes);
struct obs_encoder *audio = find_inactive_audio_encoder(output);
if (video && audio) {
pthread_mutex_lock(&audio->init_mutex);
@ -2103,7 +2100,6 @@ static inline void pair_encoders(obs_output_t *output, size_t num_mixes)
bool obs_output_initialize_encoders(obs_output_t *output, uint32_t flags)
{
bool encoded, has_video, has_audio, has_service;
size_t num_mixes = num_audio_mixes(output);
if (!obs_output_valid(output, "obs_output_initialize_encoders"))
return false;
@ -2122,7 +2118,7 @@ bool obs_output_initialize_encoders(obs_output_t *output, uint32_t flags)
obs_encoder_get_last_error(output->video_encoder));
return false;
}
if (has_audio && !initialize_audio_encoders(output, num_mixes))
if (has_audio && !initialize_audio_encoders(output))
return false;
return true;
@ -2150,7 +2146,7 @@ static bool begin_delayed_capture(obs_output_t *output)
static void reset_raw_output(obs_output_t *output)
{
clear_audio_buffers(output);
clear_raw_audio_buffers(output);
if (output->audio) {
const struct audio_output_info *aoi =
@ -2187,7 +2183,6 @@ static void reset_raw_output(obs_output_t *output)
bool obs_output_begin_data_capture(obs_output_t *output, uint32_t flags)
{
bool encoded, has_video, has_audio, has_service;
size_t num_mixes;
if (!obs_output_valid(output, "obs_output_begin_data_capture"))
return false;
@ -2210,9 +2205,8 @@ bool obs_output_begin_data_capture(obs_output_t *output, uint32_t flags)
has_service))
return false;
num_mixes = num_audio_mixes(output);
if (has_video && has_audio)
pair_encoders(output, num_mixes);
pair_encoders(output);
os_atomic_set_bool(&output->data_active, true);
hook_data_capture(output, encoded, has_video, has_audio);
@ -2240,11 +2234,10 @@ bool obs_output_begin_data_capture(obs_output_t *output, uint32_t flags)
static inline void stop_audio_encoders(obs_output_t *output,
encoded_callback_t encoded_callback)
{
size_t num_mixes = num_audio_mixes(output);
for (size_t i = 0; i < num_mixes; i++) {
obs_encoder_stop(output->audio_encoders[i], encoded_callback,
output);
for (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) {
obs_encoder_t *audio = output->audio_encoders[i];
if (audio)
obs_encoder_stop(audio, encoded_callback, output);
}
}
@ -2650,7 +2643,7 @@ const char *obs_output_get_last_error(obs_output_t *output)
return vencoder->last_error_message;
}
for (size_t i = 0; i < MAX_AUDIO_MIXES; i++) {
for (size_t i = 0; i < MAX_OUTPUT_AUDIO_ENCODERS; i++) {
obs_encoder_t *aencoder = output->audio_encoders[i];
if (aencoder && aencoder->last_error_message) {
return aencoder->last_error_message;

View file

@ -29,6 +29,8 @@ extern "C" {
#define OBS_OUTPUT_MULTI_TRACK (1 << 4)
#define OBS_OUTPUT_CAN_PAUSE (1 << 5)
#define MAX_OUTPUT_AUDIO_ENCODERS 6
struct encoder_packet;
struct obs_output_info {