Browse Source

libobs: Track keyframe alignment for keyframe aligned encoder groups

Ruwen Hahn 2 years ago
parent
commit
5e1909d54f
2 changed files with 100 additions and 0 deletions
  1. 15 0
      libobs/obs-internal.h
  2. 85 0
      libobs/obs-output.c

+ 15 - 0
libobs/obs-internal.h

@@ -1094,6 +1094,20 @@ extern bool audio_pause_check(struct pause_data *pause, struct audio_data *data,
 			      size_t sample_rate);
 extern void pause_reset(struct pause_data *pause);
 
+enum keyframe_group_track_status {
+	KEYFRAME_TRACK_STATUS_NOT_SEEN = 0,
+	KEYFRAME_TRACK_STATUS_SEEN = 1,
+	KEYFRAME_TRACK_STATUS_SKIPPED = 2,
+};
+
+struct keyframe_group_data {
+	uintptr_t group_id;
+	int64_t pts;
+	uint32_t required_tracks;
+	enum keyframe_group_track_status
+		seen_on_track[MAX_OUTPUT_VIDEO_ENCODERS];
+};
+
 struct obs_output {
 	struct obs_context_data context;
 	struct obs_output_info info;
@@ -1102,6 +1116,7 @@ struct obs_output {
 	bool owns_info_id;
 
 	bool received_video[MAX_OUTPUT_VIDEO_ENCODERS];
+	DARRAY(struct keyframe_group_data) keyframe_group_tracking;
 	bool received_audio;
 	volatile bool data_active;
 	volatile bool end_data_capture_thread_active;

+ 85 - 0
libobs/obs-output.c

@@ -290,6 +290,8 @@ void obs_output_destroy(obs_output_t *output)
 			}
 		}
 
+		da_free(output->keyframe_group_tracking);
+
 		clear_raw_audio_buffers(output);
 
 		os_event_destroy(output->stopping_event);
@@ -475,6 +477,8 @@ void obs_output_actual_stop(obs_output_t *output, bool force, uint64_t ts)
 		bfree(output->caption_head);
 		output->caption_head = output->caption_tail;
 	}
+
+	da_clear(output->keyframe_group_tracking);
 }
 
 void obs_output_stop(obs_output_t *output)
@@ -1989,6 +1993,85 @@ static void discard_unused_audio_packets(struct obs_output *output,
 		discard_to_idx(output, idx);
 }
 
+static bool purge_encoder_group_keyframe_data(obs_output_t *output, size_t idx)
+{
+	struct keyframe_group_data *data =
+		&output->keyframe_group_tracking.array[idx];
+	uint32_t modified_count = 0;
+	for (size_t i = 0; i < MAX_OUTPUT_VIDEO_ENCODERS; i++) {
+		if (data->seen_on_track[i] != KEYFRAME_TRACK_STATUS_NOT_SEEN)
+			modified_count += 1;
+	}
+
+	if (modified_count == data->required_tracks) {
+		da_erase(output->keyframe_group_tracking, idx);
+		return true;
+	}
+	return false;
+}
+
+/* Check whether keyframes are emitted from all grouped encoders, and log
+ * if keyframes haven't been emitted from all grouped encoders. */
+static void
+check_encoder_group_keyframe_alignment(obs_output_t *output,
+				       struct encoder_packet *packet)
+{
+	size_t idx = 0;
+	struct keyframe_group_data insert_data = {0};
+
+	if (!packet->keyframe || packet->type != OBS_ENCODER_VIDEO ||
+	    !packet->encoder->encoder_group)
+		return;
+
+	for (; idx < output->keyframe_group_tracking.num;) {
+		struct keyframe_group_data *data =
+			&output->keyframe_group_tracking.array[idx];
+		if (data->pts > packet->pts)
+			break;
+		if (data->group_id !=
+		    (uintptr_t)packet->encoder->encoder_group) {
+			idx += 1;
+			continue;
+		}
+
+		if (data->pts < packet->pts) {
+			if (data->seen_on_track[packet->track_idx] ==
+			    KEYFRAME_TRACK_STATUS_NOT_SEEN) {
+				blog(LOG_WARNING,
+				     "obs-output '%s': Missing keyframe with pts %" PRIi64
+				     " for encoder '%s' (track: %zu)",
+				     obs_output_get_name(output), data->pts,
+				     obs_encoder_get_name(packet->encoder),
+				     packet->track_idx);
+			}
+
+			data->seen_on_track[packet->track_idx] =
+				KEYFRAME_TRACK_STATUS_SKIPPED;
+
+			if (!purge_encoder_group_keyframe_data(output, idx))
+				idx += 1;
+			continue;
+		}
+
+		data->seen_on_track[packet->track_idx] =
+			KEYFRAME_TRACK_STATUS_SEEN;
+		purge_encoder_group_keyframe_data(output, idx);
+		return;
+	}
+
+	insert_data.group_id = (uintptr_t)packet->encoder->encoder_group;
+	insert_data.pts = packet->pts;
+	insert_data.seen_on_track[packet->track_idx] =
+		KEYFRAME_TRACK_STATUS_SEEN;
+
+	pthread_mutex_lock(&packet->encoder->encoder_group->mutex);
+	insert_data.required_tracks =
+		packet->encoder->encoder_group->encoders_started;
+	pthread_mutex_unlock(&packet->encoder->encoder_group->mutex);
+
+	da_insert(output->keyframe_group_tracking, idx, &insert_data);
+}
+
 static void interleave_packets(void *data, struct encoder_packet *packet)
 {
 	struct obs_output *output = data;
@@ -2021,6 +2104,8 @@ static void interleave_packets(void *data, struct encoder_packet *packet)
 					 output->received_video[i];
 	}
 
+	check_encoder_group_keyframe_alignment(output, packet);
+
 	was_started = output->received_audio && received_video;
 
 	if (output->active_delay_ns)