Skip to content

Commit

Permalink
UI: Add additional canvas support to multitrack video output
Browse files Browse the repository at this point in the history
  • Loading branch information
palana committed May 3, 2024
1 parent ea48bbf commit 4e30c28
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 34 deletions.
23 changes: 22 additions & 1 deletion UI/goliveapi-postdata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ nlohmann::json
constructGoLivePost(QString streamKey,
const std::optional<uint64_t> &maximum_aggregate_bitrate,
const std::optional<uint32_t> &maximum_video_tracks,
bool vod_track_enabled)
bool vod_track_enabled,
const std::map<std::string, video_t *> &extra_views)
{
using json = nlohmann::json;

Expand Down Expand Up @@ -38,6 +39,26 @@ constructGoLivePost(QString streamKey,
client["canvas_height"] = ovi.base_height;
}

auto &json_extra_views = capabilities["extra_views"];
for (auto &video_output : extra_views) {
video_t *video = video_output.second;
if (!video)
continue;
const struct video_output_info *voi =
video_output_get_info(video);
if (!voi)
continue;

json view;
view["name"] = video_output.first.c_str();
view["canvas_width"] = voi->width;
view["canvas_height"] = voi->height;
view["fps_numerator"] = voi->fps_num;
view["fps_denominator"] = voi->fps_den;

json_extra_views.push_back(view);
}

auto &preferences = post_data["preferences"];
if (maximum_aggregate_bitrate.has_value())
preferences["maximum_aggregate_bitrate"] =
Expand Down
4 changes: 3 additions & 1 deletion UI/goliveapi-postdata.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

#include <obs.hpp>
#include <optional>
#include <map>
#include <QString>
#include <nlohmann/json_fwd.hpp>

nlohmann::json
constructGoLivePost(QString streamKey,
const std::optional<uint64_t> &maximum_aggregate_bitrate,
const std::optional<uint32_t> &maximum_video_tracks,
bool vod_track_enabled);
bool vod_track_enabled,
const std::map<std::string, video_t *> &extra_views);
109 changes: 80 additions & 29 deletions UI/multitrack-video-output.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include "goliveapi-network.hpp"
#include "multitrack-video-error.hpp"
#include "qt-helpers.hpp"
#include "window-basic-main.hpp"

Qt::ConnectionType BlockingConnectionTypeFor(QObject *object)
{
Expand Down Expand Up @@ -259,25 +260,25 @@ static obs_scale_type load_gpu_scale_type(obs_data_t *encoder_config)
return default_scale_type;
}

static void adjust_video_encoder_scaling(const obs_video_info &ovi,
static void adjust_video_encoder_scaling(const char *view_name,
const obs_video_info *ovi,
const struct video_output_info *voi,
obs_encoder_t *video_encoder,
obs_data_t *encoder_config,
size_t encoder_index)
{
uint64_t requested_width = obs_data_get_int(encoder_config, "width");
uint64_t requested_height = obs_data_get_int(encoder_config, "height");

if (ovi.output_width == requested_width ||
ovi.output_height == requested_height)
return;
auto width = ovi ? ovi->base_width : voi->width;
auto height = ovi ? ovi->base_height : voi->height;

if (ovi.base_width < requested_width ||
ovi.base_height < requested_height) {
if (width < requested_width || height < requested_height) {
blog(LOG_WARNING,
"Requested resolution exceeds canvas/available resolution for encoder %zu: %" PRIu64
"x%" PRIu64 " > %" PRIu32 "x%" PRIu32,
encoder_index, requested_width, requested_height,
ovi.base_width, ovi.base_height);
"x%" PRIu64 " > %" PRIu32 "x%" PRIu32 " (canvas: %s)",
encoder_index, requested_width, requested_height, width,
height, view_name ? view_name : "obs base");
}

obs_encoder_set_scaled_size(video_encoder,
Expand Down Expand Up @@ -348,9 +349,10 @@ static bool encoder_available(const char *type)
}) != std::end(encoders);
}

static OBSEncoderAutoRelease create_video_encoder(DStr &name_buffer,
size_t encoder_index,
obs_data_t *encoder_config)
static OBSEncoderAutoRelease
create_video_encoder(DStr &name_buffer, size_t encoder_index,
obs_data_t *encoder_config,
const std::map<std::string, video_t *> &extra_views)
{
auto encoder_type = obs_data_get_string(encoder_config, "type");
if (!encoder_available(encoder_type)) {
Expand All @@ -375,22 +377,41 @@ static OBSEncoderAutoRelease create_video_encoder(DStr &name_buffer,
QTStr("FailedToStartStream.FailedToCreateVideoEncoder")
.arg(name_buffer->array, encoder_type));
}
obs_encoder_set_video(video_encoder, obs_get_video());

obs_video_info ovi;
if (!obs_get_video_info(&ovi)) {
obs_video_info ovi_storage;
if (!obs_get_video_info(&ovi_storage)) {
blog(LOG_WARNING,
"Failed to get obs video info while creating encoder %zu",
encoder_index);
throw MultitrackVideoError::warning(
QTStr("FailedToStartStream.FailedToGetOBSVideoInfo")
.arg(name_buffer->array, encoder_type));
}
obs_video_info *ovi = &ovi_storage;

const char *view_name = nullptr;
video_t *video = nullptr;
if (obs_data_has_user_value(encoder_config, "view")) {
view_name = obs_data_get_string(encoder_config, "view");
auto it = extra_views.find(view_name);
if (it != extra_views.end()) {
video = it->second;
ovi = nullptr;
} else {
blog(LOG_WARNING,
"MultitrackVideoOutput: View '%s' not found for encoder %zu",
view_name, encoder_index);
return nullptr;
}
}

obs_encoder_set_video(video_encoder, video ? video : obs_get_video());

adjust_video_encoder_scaling(ovi, video_encoder, encoder_config,
encoder_index);
adjust_encoder_frame_rate_divisor(ovi, video_encoder, encoder_config,
encoder_index);
auto voi = video ? video_output_get_info(video) : nullptr;
adjust_video_encoder_scaling(view_name, ovi, voi, video_encoder,
encoder_config, encoder_index);
adjust_encoder_frame_rate_divisor(ovi_storage, video_encoder,
encoder_config, encoder_index);

return video_encoder;
}
Expand Down Expand Up @@ -426,7 +447,8 @@ SetupOBSOutput(obs_data_t *dump_stream_to_file_config,
std::vector<OBSEncoderAutoRelease> &audio_encoders,
std::vector<OBSEncoderAutoRelease> &video_encoders,
const char *audio_encoder_id, std::optional<int> audio_bitrate,
std::optional<size_t> vod_track_mixer);
std::optional<size_t> vod_track_mixer,
const std::map<std::string, video_t *> &extra_views);
static void SetupSignalHandlers(bool recording, MultitrackVideoOutput *self,
obs_output_t *output, OBSSignal &start,
OBSSignal &stop, OBSSignal &deactivate);
Expand Down Expand Up @@ -494,10 +516,19 @@ void MultitrackVideoOutput::PrepareStreaming(
vod_track_info_storage->array ? vod_track_info_storage->array
: "No");

auto go_live_post = constructGoLivePost(stream_key,
maximum_aggregate_bitrate,
maximum_video_tracks,
vod_track_mixer.has_value());
auto extra_views_ = std::make_shared<ExtraViewsGuard>();
for (auto &video_output :
OBSBasic::Get()->GetAdditionalMultitrackVideoViews()) {
video_t *video = video_output.start_video(video_output.param);
if (!video)
continue;

extra_views_->views_[video_output.name] = video;
}

auto go_live_post = constructGoLivePost(
stream_key, maximum_aggregate_bitrate, maximum_video_tracks,
vod_track_mixer.has_value(), extra_views_->views_);

go_live_config =
DownloadGoLiveConfig(parent, auto_config_url, go_live_post);
Expand Down Expand Up @@ -559,7 +590,8 @@ void MultitrackVideoOutput::PrepareStreaming(
auto outputs = SetupOBSOutput(dump_stream_to_file_config,
go_live_config, audio_encoders,
video_encoders, audio_encoder_id,
audio_bitrate, vod_track_mixer);
audio_bitrate, vod_track_mixer,
extra_views_->views_);
auto output = std::move(outputs.output);
auto recording_output = std::move(outputs.recording_output);
if (!output)
Expand Down Expand Up @@ -615,6 +647,7 @@ void MultitrackVideoOutput::PrepareStreaming(
std::move(start_recording),
std::move(stop_recording),
std::move(deactivate_recording),
extra_views_,
});
}
}
Expand All @@ -628,6 +661,7 @@ void MultitrackVideoOutput::PrepareStreaming(
std::move(start_streaming),
std::move(stop_streaming),
std::move(deactivate_stream),
extra_views_,
});
}

Expand Down Expand Up @@ -788,7 +822,8 @@ bool MultitrackVideoOutput::HandleIncompatibleSettings(
static bool
create_video_encoders(obs_data_t *go_live_config,
std::vector<OBSEncoderAutoRelease> &video_encoders,
obs_output_t *output, obs_output_t *recording_output)
obs_output_t *output, obs_output_t *recording_output,
const std::map<std::string, video_t *> &extra_views)
{
OBSDataArrayAutoRelease encoder_configs =
obs_data_get_array(go_live_config, "encoder_configurations");
Expand All @@ -807,7 +842,8 @@ create_video_encoders(obs_data_t *go_live_config,
OBSDataAutoRelease encoder_config =
obs_data_array_item(encoder_configs, i);
auto encoder = create_video_encoder(video_encoder_name_buffer,
i, encoder_config);
i, encoder_config,
extra_views);
if (!encoder)
return false;

Expand Down Expand Up @@ -910,7 +946,8 @@ SetupOBSOutput(obs_data_t *dump_stream_to_file_config,
std::vector<OBSEncoderAutoRelease> &audio_encoders,
std::vector<OBSEncoderAutoRelease> &video_encoders,
const char *audio_encoder_id, std::optional<int> audio_bitrate,
std::optional<size_t> vod_track_mixer)
std::optional<size_t> vod_track_mixer,
const std::map<std::string, video_t *> &extra_views)
{

auto output = create_output();
Expand All @@ -920,7 +957,7 @@ SetupOBSOutput(obs_data_t *dump_stream_to_file_config,
create_recording_output(dump_stream_to_file_config);

if (!create_video_encoders(go_live_config, video_encoders, output,
recording_output))
recording_output, extra_views))
return {nullptr, nullptr};

create_audio_encoders(go_live_config, audio_encoders, output,
Expand Down Expand Up @@ -1038,3 +1075,17 @@ void RecordingDeactivateHandler(void *arg, calldata_t * /*data*/)
MultitrackVideoOutput::ReleaseOnMainThread(
self->take_current_stream_dump());
}

MultitrackVideoOutput::ExtraViewsGuard::~ExtraViewsGuard()
{
auto additional_views =
OBSBasic::Get()->GetAdditionalMultitrackVideoViews();
for (auto &view : views_) {
for (auto &v : additional_views) {
if (v.name != view.first)
continue;

v.stop_video(view.second, v.param);
}
}
}
9 changes: 9 additions & 0 deletions UI/multitrack-video-output.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,22 @@ struct MultitrackVideoOutput {
return current ? obs_output_get_ref(current->output_) : nullptr;
}

const std::vector<OBSEncoderAutoRelease> &VideoEncoders() const;

private:
struct ExtraViewsGuard {
std::map<std::string, video_t *> views_;

~ExtraViewsGuard();
};

struct OBSOutputObjects {
OBSOutputAutoRelease output_;
std::vector<OBSEncoderAutoRelease> video_encoders_;
std::vector<OBSEncoderAutoRelease> audio_encoders_;
OBSServiceAutoRelease multitrack_video_service_;
OBSSignal start_signal, stop_signal, deactivate_signal;
std::shared_ptr<ExtraViewsGuard> extra_views_;
};

std::optional<OBSOutputObjects> take_current();
Expand Down
7 changes: 4 additions & 3 deletions UI/window-basic-auto-config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -420,9 +420,10 @@ bool AutoConfigStreamPage::validatePage()
if (wiz->service == AutoConfig::Service::Twitch) {
wiz->testMultitrackVideo = ui->useMultitrackVideo->isChecked();

auto postData =
constructGoLivePost(QString::fromStdString(wiz->key),
std::nullopt, std::nullopt, false);
std::map<std::string, video_t *> extra_views;
auto postData = constructGoLivePost(
QString::fromStdString(wiz->key), std::nullopt,
std::nullopt, false, extra_views);

try {
auto config = DownloadGoLiveConfig(
Expand Down

0 comments on commit 4e30c28

Please sign in to comment.