mirror of
https://github.com/obsproject/obs-studio.git
synced 2024-06-30 08:33:32 +00:00
libobs, UI: Normalize encoder group API
Modifies the encoder group API added previously to better follow the existing libobs API naming paradigms. This also produces much more readable code, and allows a few small benefits like only needing to hold a reference to the encoder group, instead of every encoder.
This commit is contained in:
parent
751dbdad10
commit
e215502b62
|
@ -332,7 +332,7 @@ static OBSOutputs
|
|||
SetupOBSOutput(obs_data_t *dump_stream_to_file_config,
|
||||
const GoLiveApi::Config &go_live_config,
|
||||
std::vector<OBSEncoderAutoRelease> &audio_encoders,
|
||||
std::vector<OBSEncoderAutoRelease> &video_encoders,
|
||||
std::shared_ptr<obs_encoder_group_t> &video_encoder_group,
|
||||
const char *audio_encoder_id,
|
||||
std::optional<size_t> vod_track_mixer);
|
||||
static void SetupSignalHandlers(bool recording, MultitrackVideoOutput *self,
|
||||
|
@ -461,10 +461,10 @@ void MultitrackVideoOutput::PrepareStreaming(
|
|||
const auto &output_config = custom ? *custom : *go_live_config;
|
||||
const auto &service_config = go_live_config ? *go_live_config : *custom;
|
||||
|
||||
auto audio_encoders = std::vector<OBSEncoderAutoRelease>();
|
||||
auto video_encoders = std::vector<OBSEncoderAutoRelease>();
|
||||
std::vector<OBSEncoderAutoRelease> audio_encoders;
|
||||
std::shared_ptr<obs_encoder_group_t> video_encoder_group;
|
||||
auto outputs = SetupOBSOutput(dump_stream_to_file_config, output_config,
|
||||
audio_encoders, video_encoders,
|
||||
audio_encoders, video_encoder_group,
|
||||
audio_encoder_id, vod_track_mixer);
|
||||
auto output = std::move(outputs.output);
|
||||
auto recording_output = std::move(outputs.recording_output);
|
||||
|
@ -496,13 +496,6 @@ void MultitrackVideoOutput::PrepareStreaming(
|
|||
start_recording, stop_recording,
|
||||
deactivate_recording);
|
||||
|
||||
decltype(video_encoders) recording_video_encoders;
|
||||
recording_video_encoders.reserve(video_encoders.size());
|
||||
for (auto &encoder : video_encoders) {
|
||||
recording_video_encoders.emplace_back(
|
||||
obs_encoder_get_ref(encoder));
|
||||
}
|
||||
|
||||
decltype(audio_encoders) recording_audio_encoders;
|
||||
recording_audio_encoders.reserve(audio_encoders.size());
|
||||
for (auto &encoder : audio_encoders) {
|
||||
|
@ -515,7 +508,7 @@ void MultitrackVideoOutput::PrepareStreaming(
|
|||
current_stream_dump_mutex};
|
||||
current_stream_dump.emplace(OBSOutputObjects{
|
||||
std::move(recording_output),
|
||||
std::move(recording_video_encoders),
|
||||
video_encoder_group,
|
||||
std::move(recording_audio_encoders),
|
||||
nullptr,
|
||||
std::move(start_recording),
|
||||
|
@ -528,7 +521,7 @@ void MultitrackVideoOutput::PrepareStreaming(
|
|||
const std::lock_guard current_lock{current_mutex};
|
||||
current.emplace(OBSOutputObjects{
|
||||
std::move(output),
|
||||
std::move(video_encoders),
|
||||
video_encoder_group,
|
||||
std::move(audio_encoders),
|
||||
std::move(multitrack_video_service),
|
||||
std::move(start_streaming),
|
||||
|
@ -692,11 +685,10 @@ bool MultitrackVideoOutput::HandleIncompatibleSettings(
|
|||
|
||||
static bool
|
||||
create_video_encoders(const GoLiveApi::Config &go_live_config,
|
||||
std::vector<OBSEncoderAutoRelease> &video_encoders,
|
||||
std::shared_ptr<obs_encoder_group_t> &video_encoder_group,
|
||||
obs_output_t *output, obs_output_t *recording_output)
|
||||
{
|
||||
DStr video_encoder_name_buffer;
|
||||
obs_encoder_t *first_encoder = nullptr;
|
||||
if (go_live_config.encoder_configurations.empty()) {
|
||||
blog(LOG_WARNING,
|
||||
"MultitrackVideoOutput: Missing video encoder configurations");
|
||||
|
@ -704,6 +696,11 @@ create_video_encoders(const GoLiveApi::Config &go_live_config,
|
|||
QTStr("FailedToStartStream.MissingEncoderConfigs"));
|
||||
}
|
||||
|
||||
std::shared_ptr<obs_encoder_group_t> encoder_group(
|
||||
obs_encoder_group_create(), obs_encoder_group_destroy);
|
||||
if (!encoder_group)
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < go_live_config.encoder_configurations.size();
|
||||
i++) {
|
||||
auto encoder = create_video_encoder(
|
||||
|
@ -712,19 +709,16 @@ create_video_encoders(const GoLiveApi::Config &go_live_config,
|
|||
if (!encoder)
|
||||
return false;
|
||||
|
||||
if (!first_encoder)
|
||||
first_encoder = encoder;
|
||||
else
|
||||
obs_encoder_group_keyframe_aligned_encoders(
|
||||
first_encoder, encoder);
|
||||
if (!obs_encoder_set_group(encoder, encoder_group.get()))
|
||||
return false;
|
||||
|
||||
obs_output_set_video_encoder2(output, encoder, i);
|
||||
if (recording_output)
|
||||
obs_output_set_video_encoder2(recording_output, encoder,
|
||||
i);
|
||||
video_encoders.emplace_back(std::move(encoder));
|
||||
}
|
||||
|
||||
video_encoder_group = encoder_group;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -790,7 +784,7 @@ static OBSOutputs
|
|||
SetupOBSOutput(obs_data_t *dump_stream_to_file_config,
|
||||
const GoLiveApi::Config &go_live_config,
|
||||
std::vector<OBSEncoderAutoRelease> &audio_encoders,
|
||||
std::vector<OBSEncoderAutoRelease> &video_encoders,
|
||||
std::shared_ptr<obs_encoder_group_t> &video_encoder_group,
|
||||
const char *audio_encoder_id,
|
||||
std::optional<size_t> vod_track_mixer)
|
||||
{
|
||||
|
@ -801,7 +795,7 @@ SetupOBSOutput(obs_data_t *dump_stream_to_file_config,
|
|||
recording_output =
|
||||
create_recording_output(dump_stream_to_file_config);
|
||||
|
||||
if (!create_video_encoders(go_live_config, video_encoders, output,
|
||||
if (!create_video_encoders(go_live_config, video_encoder_group, output,
|
||||
recording_output))
|
||||
return {nullptr, nullptr};
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ public:
|
|||
private:
|
||||
struct OBSOutputObjects {
|
||||
OBSOutputAutoRelease output_;
|
||||
std::vector<OBSEncoderAutoRelease> video_encoders_;
|
||||
std::shared_ptr<obs_encoder_group_t> video_encoder_group_;
|
||||
std::vector<OBSEncoderAutoRelease> audio_encoders_;
|
||||
OBSServiceAutoRelease multitrack_video_service_;
|
||||
OBSSignal start_signal, stop_signal, deactivate_signal;
|
||||
|
|
|
@ -327,8 +327,8 @@ static void add_connection(struct obs_encoder *encoder)
|
|||
if (encoder->encoder_group) {
|
||||
pthread_mutex_lock(&encoder->encoder_group->mutex);
|
||||
encoder->encoder_group->num_encoders_started += 1;
|
||||
bool ready = encoder->encoder_group->num_encoders_started ==
|
||||
encoder->encoder_group->num_encoders;
|
||||
bool ready = encoder->encoder_group->num_encoders_started >=
|
||||
encoder->encoder_group->encoders.num;
|
||||
pthread_mutex_unlock(&encoder->encoder_group->mutex);
|
||||
if (ready)
|
||||
add_ready_encoder_group(encoder);
|
||||
|
@ -337,6 +337,7 @@ static void add_connection(struct obs_encoder *encoder)
|
|||
set_encoder_active(encoder, true);
|
||||
}
|
||||
|
||||
void obs_encoder_group_actually_destroy(obs_encoder_group_t *group);
|
||||
static void remove_connection(struct obs_encoder *encoder, bool shutdown)
|
||||
{
|
||||
if (encoder->info.type == OBS_ENCODER_AUDIO) {
|
||||
|
@ -352,11 +353,15 @@ static void remove_connection(struct obs_encoder *encoder, bool shutdown)
|
|||
|
||||
if (encoder->encoder_group) {
|
||||
pthread_mutex_lock(&encoder->encoder_group->mutex);
|
||||
encoder->encoder_group->num_encoders_started -= 1;
|
||||
if (encoder->encoder_group->num_encoders_started == 0)
|
||||
if (--encoder->encoder_group->num_encoders_started == 0) {
|
||||
encoder->encoder_group->start_timestamp = 0;
|
||||
if (encoder->encoder_group->destroy_on_stop)
|
||||
obs_encoder_group_actually_destroy(
|
||||
encoder->encoder_group);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&encoder->encoder_group->mutex);
|
||||
if (encoder->encoder_group)
|
||||
pthread_mutex_unlock(&encoder->encoder_group->mutex);
|
||||
}
|
||||
|
||||
/* obs_encoder_shutdown locks init_mutex, so don't call it on encode
|
||||
|
@ -394,23 +399,7 @@ static void obs_encoder_actually_destroy(obs_encoder_t *encoder)
|
|||
blog(LOG_DEBUG, "encoder '%s' destroyed",
|
||||
encoder->context.name);
|
||||
|
||||
if (encoder->encoder_group) {
|
||||
struct obs_encoder_group *group =
|
||||
encoder->encoder_group;
|
||||
bool release = false;
|
||||
|
||||
encoder->encoder_group = NULL;
|
||||
|
||||
pthread_mutex_lock(&group->mutex);
|
||||
group->num_encoders -= 1;
|
||||
release = group->num_encoders == 0;
|
||||
pthread_mutex_unlock(&group->mutex);
|
||||
|
||||
if (release) {
|
||||
pthread_mutex_destroy(&group->mutex);
|
||||
bfree(group);
|
||||
}
|
||||
}
|
||||
obs_encoder_set_group(encoder, NULL);
|
||||
|
||||
free_audio_buffers(encoder);
|
||||
|
||||
|
@ -2058,133 +2047,99 @@ uint32_t obs_encoder_get_roi_increment(const obs_encoder_t *encoder)
|
|||
return encoder->roi_increment;
|
||||
}
|
||||
|
||||
bool obs_encoder_group_keyframe_aligned_encoders(
|
||||
obs_encoder_t *encoder, obs_encoder_t *encoder_to_be_grouped)
|
||||
bool obs_encoder_set_group(obs_encoder_t *encoder, obs_encoder_group_t *group)
|
||||
{
|
||||
if (!obs_encoder_valid(encoder,
|
||||
"obs_encoder_group_keyframe_aligned_encoders") ||
|
||||
!obs_encoder_valid(encoder_to_be_grouped,
|
||||
"obs_encoder_group_keyframe_aligned_encoders"))
|
||||
if (!obs_encoder_valid(encoder, "obs_encoder_set_group"))
|
||||
return false;
|
||||
|
||||
if (obs_encoder_active(encoder) ||
|
||||
obs_encoder_active(encoder_to_be_grouped)) {
|
||||
obs_encoder_t *active = obs_encoder_active(encoder)
|
||||
? encoder
|
||||
: encoder_to_be_grouped;
|
||||
obs_encoder_t *other = active == encoder ? encoder_to_be_grouped
|
||||
: encoder;
|
||||
if (obs_encoder_active(encoder)) {
|
||||
blog(LOG_ERROR,
|
||||
"obs_encoder_group_keyframe_aligned_encoders: encoder '%s' "
|
||||
"is already active, could not group with '%s'",
|
||||
obs_encoder_get_name(active), obs_encoder_get_name(other));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (encoder_to_be_grouped->encoder_group) {
|
||||
blog(LOG_ERROR,
|
||||
"obs_encoder_group_keyframe_aligned_encoders: encoder '%s' "
|
||||
"is already part of a keyframe aligned group while trying "
|
||||
"to group with encoder '%s'",
|
||||
obs_encoder_get_name(encoder_to_be_grouped),
|
||||
"obs_encoder_set_group: encoder '%s' is already active",
|
||||
obs_encoder_get_name(encoder));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool unlock = false;
|
||||
if (!encoder->encoder_group) {
|
||||
encoder->encoder_group =
|
||||
bzalloc(sizeof(struct obs_encoder_group));
|
||||
if (pthread_mutex_init(&encoder->encoder_group->mutex, NULL) <
|
||||
0) {
|
||||
bfree(encoder->encoder_group);
|
||||
encoder->encoder_group = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
encoder->encoder_group->num_encoders = 1;
|
||||
} else {
|
||||
pthread_mutex_lock(&encoder->encoder_group->mutex);
|
||||
unlock = true;
|
||||
if (encoder->encoder_group->num_encoders_started != 0) {
|
||||
if (encoder->encoder_group) {
|
||||
struct obs_encoder_group *old_group = encoder->encoder_group;
|
||||
pthread_mutex_lock(&old_group->mutex);
|
||||
if (old_group->num_encoders_started) {
|
||||
pthread_mutex_unlock(&old_group->mutex);
|
||||
blog(LOG_ERROR,
|
||||
"obs_encoder_group_keyframe_aligned_encoders: "
|
||||
"Can't add encoder '%s' to active group "
|
||||
"from encoder '%s'",
|
||||
obs_encoder_get_name(encoder_to_be_grouped),
|
||||
"obs_encoder_set_group: encoder '%s' existing group has started encoders",
|
||||
obs_encoder_get_name(encoder));
|
||||
pthread_mutex_unlock(&encoder->encoder_group->mutex);
|
||||
return false;
|
||||
}
|
||||
da_erase_item(old_group->encoders, &encoder);
|
||||
obs_encoder_release(encoder);
|
||||
pthread_mutex_unlock(&old_group->mutex);
|
||||
}
|
||||
|
||||
encoder->encoder_group->num_encoders += 1;
|
||||
encoder_to_be_grouped->encoder_group = encoder->encoder_group;
|
||||
if (!group)
|
||||
return true;
|
||||
|
||||
if (unlock)
|
||||
pthread_mutex_unlock(&encoder->encoder_group->mutex);
|
||||
pthread_mutex_lock(&group->mutex);
|
||||
|
||||
if (group->num_encoders_started) {
|
||||
pthread_mutex_unlock(&group->mutex);
|
||||
blog(LOG_ERROR,
|
||||
"obs_encoder_set_group: specified group has started encoders");
|
||||
return false;
|
||||
}
|
||||
|
||||
obs_encoder_t *ref = obs_encoder_get_ref(encoder);
|
||||
if (!ref) {
|
||||
pthread_mutex_unlock(&group->mutex);
|
||||
return false;
|
||||
}
|
||||
da_push_back(group->encoders, &ref);
|
||||
encoder->encoder_group = group;
|
||||
|
||||
pthread_mutex_unlock(&group->mutex);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool obs_encoder_group_remove_keyframe_aligned_encoder(
|
||||
obs_encoder_t *encoder, obs_encoder_t *encoder_to_be_ungrouped)
|
||||
obs_encoder_group_t *obs_encoder_group_create()
|
||||
{
|
||||
if (!obs_encoder_valid(
|
||||
encoder,
|
||||
"obs_encoder_group_remove_keyframe_aligned_encoder") ||
|
||||
!obs_encoder_valid(
|
||||
encoder_to_be_ungrouped,
|
||||
"obs_encoder_group_remove_keyframe_aligned_encoder"))
|
||||
return false;
|
||||
struct obs_encoder_group *group =
|
||||
bzalloc(sizeof(struct obs_encoder_group));
|
||||
|
||||
if (obs_encoder_active(encoder) ||
|
||||
obs_encoder_active(encoder_to_be_ungrouped)) {
|
||||
blog(LOG_ERROR,
|
||||
"obs_encoder_group_remove_keyframe_aligned_encoder: encoders are active, "
|
||||
"could not ungroup encoder '%s' from '%s'",
|
||||
obs_encoder_get_name(encoder_to_be_ungrouped),
|
||||
obs_encoder_get_name(encoder));
|
||||
return false;
|
||||
pthread_mutex_init_value(&group->mutex);
|
||||
if (pthread_mutex_init(&group->mutex, NULL) != 0) {
|
||||
bfree(group);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (encoder->encoder_group != encoder_to_be_ungrouped->encoder_group) {
|
||||
blog(LOG_ERROR,
|
||||
"obs_encoder_group_remove_keyframe_aligned_encoder: "
|
||||
"encoder '%s' does not belong to the same group as encoder '%s'",
|
||||
obs_encoder_get_name(encoder_to_be_ungrouped),
|
||||
obs_encoder_get_name(encoder));
|
||||
return false;
|
||||
}
|
||||
|
||||
struct obs_encoder_group *current_group = encoder->encoder_group;
|
||||
struct obs_encoder_group *free_group = NULL;
|
||||
|
||||
pthread_mutex_lock(¤t_group->mutex);
|
||||
|
||||
if (current_group->num_encoders_started != 0) {
|
||||
blog(LOG_ERROR,
|
||||
"obs_encoder_group_remove_keyframe_aligned_encoder: "
|
||||
"could not ungroup encoder '%s' from '%s' while "
|
||||
"the group contains active encoders",
|
||||
obs_encoder_get_name(encoder_to_be_ungrouped),
|
||||
obs_encoder_get_name(encoder));
|
||||
pthread_mutex_unlock(¤t_group->mutex);
|
||||
return false;
|
||||
}
|
||||
|
||||
current_group->num_encoders -= 1;
|
||||
encoder_to_be_ungrouped->encoder_group = NULL;
|
||||
if (current_group->num_encoders == 1) {
|
||||
free_group = current_group;
|
||||
encoder->encoder_group = NULL;
|
||||
}
|
||||
pthread_mutex_unlock(¤t_group->mutex);
|
||||
|
||||
if (free_group) {
|
||||
pthread_mutex_destroy(&free_group->mutex);
|
||||
bfree(free_group);
|
||||
}
|
||||
|
||||
return true;
|
||||
return group;
|
||||
}
|
||||
|
||||
void obs_encoder_group_actually_destroy(obs_encoder_group_t *group)
|
||||
{
|
||||
for (size_t i = 0; i < group->encoders.num; i++) {
|
||||
struct obs_encoder *encoder = group->encoders.array[i];
|
||||
encoder->encoder_group = NULL;
|
||||
obs_encoder_release(encoder);
|
||||
}
|
||||
|
||||
da_free(group->encoders);
|
||||
pthread_mutex_unlock(&group->mutex);
|
||||
pthread_mutex_destroy(&group->mutex);
|
||||
|
||||
bfree(group);
|
||||
}
|
||||
|
||||
void obs_encoder_group_destroy(obs_encoder_group_t *group)
|
||||
{
|
||||
if (!group)
|
||||
return;
|
||||
|
||||
pthread_mutex_lock(&group->mutex);
|
||||
|
||||
if (group->num_encoders_started) {
|
||||
group->destroy_on_stop = true;
|
||||
pthread_mutex_unlock(&group->mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
obs_encoder_group_actually_destroy(group);
|
||||
}
|
||||
|
|
|
@ -1244,7 +1244,12 @@ struct encoder_callback {
|
|||
|
||||
struct obs_encoder_group {
|
||||
pthread_mutex_t mutex;
|
||||
uint32_t num_encoders;
|
||||
/* allows group to be destroyed even if some encoders are active */
|
||||
bool destroy_on_stop;
|
||||
|
||||
/* holds strong references to all encoders */
|
||||
DARRAY(struct obs_encoder *) encoders;
|
||||
|
||||
uint32_t num_encoders_started;
|
||||
uint64_t start_timestamp;
|
||||
};
|
||||
|
|
|
@ -935,11 +935,10 @@ static inline void video_sleep(struct obs_core_video *video, uint64_t *p_time,
|
|||
struct obs_encoder_group *group =
|
||||
encoder->encoder_group;
|
||||
pthread_mutex_lock(&group->mutex);
|
||||
if (group->num_encoders ==
|
||||
group->num_encoders_started &&
|
||||
!group->start_timestamp) {
|
||||
if (group->num_encoders_started >=
|
||||
group->encoders.num &&
|
||||
!group->start_timestamp)
|
||||
group->start_timestamp = *p_time;
|
||||
}
|
||||
pthread_mutex_unlock(&group->mutex);
|
||||
}
|
||||
obs_encoder_release(encoder);
|
||||
|
|
16
libobs/obs.h
16
libobs/obs.h
|
@ -46,6 +46,7 @@ struct obs_scene;
|
|||
struct obs_scene_item;
|
||||
struct obs_output;
|
||||
struct obs_encoder;
|
||||
struct obs_encoder_group;
|
||||
struct obs_service;
|
||||
struct obs_module;
|
||||
struct obs_fader;
|
||||
|
@ -59,6 +60,7 @@ typedef struct obs_scene obs_scene_t;
|
|||
typedef struct obs_scene_item obs_sceneitem_t;
|
||||
typedef struct obs_output obs_output_t;
|
||||
typedef struct obs_encoder obs_encoder_t;
|
||||
typedef struct obs_encoder_group obs_encoder_group_t;
|
||||
typedef struct obs_service obs_service_t;
|
||||
typedef struct obs_module obs_module_t;
|
||||
typedef struct obs_fader obs_fader_t;
|
||||
|
@ -2607,10 +2609,16 @@ EXPORT void obs_encoder_set_last_error(obs_encoder_t *encoder,
|
|||
|
||||
EXPORT uint64_t obs_encoder_get_pause_offset(const obs_encoder_t *encoder);
|
||||
|
||||
EXPORT bool obs_encoder_group_keyframe_aligned_encoders(
|
||||
obs_encoder_t *encoder, obs_encoder_t *encoder_to_be_grouped);
|
||||
EXPORT bool obs_encoder_group_remove_keyframe_aligned_encoder(
|
||||
obs_encoder_t *encoder, obs_encoder_t *encoder_to_be_ungrouped);
|
||||
/**
|
||||
* Creates an "encoder group", allowing synchronized startup of encoders within
|
||||
* the group. Encoder groups are single owner, and hold strong references to
|
||||
* encoders within the group. Calling destroy on an active group will not actually
|
||||
* destroy the group until it becomes completely inactive.
|
||||
*/
|
||||
EXPORT bool obs_encoder_set_group(obs_encoder_t *encoder,
|
||||
obs_encoder_group_t *group);
|
||||
EXPORT obs_encoder_group_t *obs_encoder_group_create();
|
||||
EXPORT void obs_encoder_group_destroy(obs_encoder_group_t *group);
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* Stream Services */
|
||||
|
|
Loading…
Reference in a new issue