2015-02-06 03:17:33 -08:00
|
|
|
#include <string>
|
|
|
|
#include <QMessageBox>
|
2015-07-02 01:00:22 -07:00
|
|
|
#include "audio-encoders.hpp"
|
2015-02-06 03:17:33 -08:00
|
|
|
#include "window-basic-main.hpp"
|
|
|
|
#include "window-basic-main-outputs.hpp"
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
static void OBSStartStreaming(void *data, calldata_t *params)
|
|
|
|
{
|
|
|
|
BasicOutputHandler *output = static_cast<BasicOutputHandler*>(data);
|
|
|
|
QMetaObject::invokeMethod(output->main, "StreamingStart");
|
|
|
|
|
|
|
|
UNUSED_PARAMETER(params);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void OBSStopStreaming(void *data, calldata_t *params)
|
|
|
|
{
|
|
|
|
BasicOutputHandler *output = static_cast<BasicOutputHandler*>(data);
|
|
|
|
int code = (int)calldata_int(params, "code");
|
|
|
|
|
|
|
|
QMetaObject::invokeMethod(output->main,
|
|
|
|
"StreamingStop", Q_ARG(int, code));
|
|
|
|
output->activeRefs--;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void OBSStartRecording(void *data, calldata_t *params)
|
|
|
|
{
|
|
|
|
BasicOutputHandler *output = static_cast<BasicOutputHandler*>(data);
|
|
|
|
|
|
|
|
QMetaObject::invokeMethod(output->main, "RecordingStart");
|
|
|
|
|
|
|
|
UNUSED_PARAMETER(params);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void OBSStopRecording(void *data, calldata_t *params)
|
|
|
|
{
|
|
|
|
BasicOutputHandler *output = static_cast<BasicOutputHandler*>(data);
|
2015-06-09 21:00:22 -07:00
|
|
|
int code = (int)calldata_int(params, "code");
|
2015-02-06 03:17:33 -08:00
|
|
|
|
2015-06-09 21:00:22 -07:00
|
|
|
QMetaObject::invokeMethod(output->main,
|
|
|
|
"RecordingStop", Q_ARG(int, code));
|
2015-02-06 03:17:33 -08:00
|
|
|
output->activeRefs--;
|
|
|
|
|
|
|
|
UNUSED_PARAMETER(params);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
|
|
|
2015-07-02 01:00:22 -07:00
|
|
|
static bool CreateAACEncoder(OBSEncoder &res, string &id, int bitrate,
|
|
|
|
const char *name, size_t idx)
|
2015-06-05 16:52:22 -07:00
|
|
|
{
|
2015-07-02 01:00:22 -07:00
|
|
|
const char *id_ = GetAACEncoderForBitrate(bitrate);
|
|
|
|
if (!id_) {
|
|
|
|
id.clear();
|
|
|
|
res = nullptr;
|
|
|
|
return false;
|
2015-06-05 16:52:22 -07:00
|
|
|
}
|
|
|
|
|
2015-07-02 01:00:22 -07:00
|
|
|
if (id == id_)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
id = id_;
|
|
|
|
res = obs_audio_encoder_create(id_, name, nullptr, idx, nullptr);
|
|
|
|
|
|
|
|
if (res) {
|
|
|
|
obs_encoder_release(res);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
2015-06-05 16:52:22 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
|
|
|
2015-02-06 03:17:33 -08:00
|
|
|
struct SimpleOutput : BasicOutputHandler {
|
|
|
|
OBSEncoder aac;
|
|
|
|
OBSEncoder h264;
|
|
|
|
|
2015-07-02 01:00:22 -07:00
|
|
|
string aacEncoderID;
|
|
|
|
|
2015-02-06 03:17:33 -08:00
|
|
|
SimpleOutput(OBSBasic *main_);
|
|
|
|
|
|
|
|
virtual void Update() override;
|
|
|
|
|
|
|
|
void SetupOutputs();
|
2015-07-02 00:58:28 -07:00
|
|
|
int GetAudioBitrate() const;
|
2015-02-06 03:17:33 -08:00
|
|
|
|
|
|
|
virtual bool StartStreaming(obs_service_t *service) override;
|
|
|
|
virtual bool StartRecording() override;
|
|
|
|
virtual void StopStreaming() override;
|
|
|
|
virtual void StopRecording() override;
|
|
|
|
virtual bool StreamingActive() const override;
|
|
|
|
virtual bool RecordingActive() const override;
|
|
|
|
};
|
|
|
|
|
|
|
|
SimpleOutput::SimpleOutput(OBSBasic *main_) : BasicOutputHandler(main_)
|
|
|
|
{
|
|
|
|
streamOutput = obs_output_create("rtmp_output", "simple_stream",
|
2014-11-01 13:41:17 -07:00
|
|
|
nullptr, nullptr);
|
2015-02-06 03:17:33 -08:00
|
|
|
if (!streamOutput)
|
|
|
|
throw "Failed to create stream output (simple output)";
|
2015-05-03 16:55:43 -07:00
|
|
|
obs_output_release(streamOutput);
|
2015-02-06 03:17:33 -08:00
|
|
|
|
2015-05-28 23:27:06 -07:00
|
|
|
fileOutput = obs_output_create("ffmpeg_muxer", "simple_file_output",
|
2014-11-01 13:41:17 -07:00
|
|
|
nullptr, nullptr);
|
2015-02-06 03:17:33 -08:00
|
|
|
if (!fileOutput)
|
|
|
|
throw "Failed to create recording output (simple output)";
|
2015-05-03 16:55:43 -07:00
|
|
|
obs_output_release(fileOutput);
|
2015-02-06 03:17:33 -08:00
|
|
|
|
2014-11-01 13:41:17 -07:00
|
|
|
h264 = obs_video_encoder_create("obs_x264", "simple_h264", nullptr,
|
|
|
|
nullptr);
|
2015-02-06 03:17:33 -08:00
|
|
|
if (!h264)
|
|
|
|
throw "Failed to create h264 encoder (simple output)";
|
2015-05-03 17:01:38 -07:00
|
|
|
obs_encoder_release(h264);
|
2015-02-06 03:17:33 -08:00
|
|
|
|
2015-07-02 01:00:22 -07:00
|
|
|
if (!CreateAACEncoder(aac, aacEncoderID, GetAudioBitrate(),
|
|
|
|
"simple_aac", 0))
|
2015-02-06 03:17:33 -08:00
|
|
|
throw "Failed to create audio encoder (simple output)";
|
|
|
|
|
2015-07-03 23:00:21 -07:00
|
|
|
startStreaming.Connect(obs_output_get_signal_handler(streamOutput),
|
2015-02-06 03:17:33 -08:00
|
|
|
"start", OBSStartStreaming, this);
|
2015-07-03 23:00:21 -07:00
|
|
|
stopStreaming.Connect(obs_output_get_signal_handler(streamOutput),
|
2015-02-06 03:17:33 -08:00
|
|
|
"stop", OBSStopStreaming, this);
|
|
|
|
|
2015-07-03 23:00:21 -07:00
|
|
|
startRecording.Connect(obs_output_get_signal_handler(fileOutput),
|
2015-02-06 03:17:33 -08:00
|
|
|
"start", OBSStartRecording, this);
|
2015-07-03 23:00:21 -07:00
|
|
|
stopRecording.Connect(obs_output_get_signal_handler(fileOutput),
|
2015-02-06 03:17:33 -08:00
|
|
|
"stop", OBSStopRecording, this);
|
|
|
|
}
|
|
|
|
|
2015-07-02 00:58:28 -07:00
|
|
|
int SimpleOutput::GetAudioBitrate() const
|
|
|
|
{
|
|
|
|
return config_get_uint(main->Config(), "SimpleOutput", "ABitrate");
|
|
|
|
}
|
|
|
|
|
2015-02-06 03:17:33 -08:00
|
|
|
void SimpleOutput::Update()
|
|
|
|
{
|
|
|
|
obs_data_t *h264Settings = obs_data_create();
|
|
|
|
obs_data_t *aacSettings = obs_data_create();
|
|
|
|
|
|
|
|
int videoBitrate = config_get_uint(main->Config(), "SimpleOutput",
|
|
|
|
"VBitrate");
|
|
|
|
int videoBufsize = config_get_uint(main->Config(), "SimpleOutput",
|
2015-04-04 13:14:37 -07:00
|
|
|
"VBufsize");
|
2015-07-02 00:58:28 -07:00
|
|
|
int audioBitrate = GetAudioBitrate();
|
2015-02-06 03:17:33 -08:00
|
|
|
bool advanced = config_get_bool(main->Config(), "SimpleOutput",
|
|
|
|
"UseAdvanced");
|
|
|
|
bool useCBR = config_get_bool(main->Config(), "SimpleOutput",
|
|
|
|
"UseCBR");
|
|
|
|
bool useBufsize = config_get_bool(main->Config(), "SimpleOutput",
|
|
|
|
"UseBufsize");
|
|
|
|
const char *preset = config_get_string(main->Config(),
|
|
|
|
"SimpleOutput", "Preset");
|
|
|
|
const char *custom = config_get_string(main->Config(),
|
|
|
|
"SimpleOutput", "x264Settings");
|
|
|
|
|
|
|
|
obs_data_set_int(h264Settings, "bitrate", videoBitrate);
|
|
|
|
obs_data_set_bool(h264Settings, "use_bufsize", useBufsize);
|
|
|
|
obs_data_set_int(h264Settings, "buffer_size", videoBufsize);
|
|
|
|
|
|
|
|
if (advanced) {
|
|
|
|
obs_data_set_string(h264Settings, "preset", preset);
|
|
|
|
obs_data_set_string(h264Settings, "x264opts", custom);
|
|
|
|
obs_data_set_bool(h264Settings, "cbr", useCBR);
|
|
|
|
} else {
|
|
|
|
obs_data_set_bool(h264Settings, "cbr", true);
|
|
|
|
}
|
|
|
|
|
|
|
|
obs_data_set_int(aacSettings, "bitrate", audioBitrate);
|
|
|
|
|
2015-03-07 16:32:00 -08:00
|
|
|
obs_service_apply_encoder_settings(main->GetService(),
|
|
|
|
h264Settings, aacSettings);
|
|
|
|
|
2015-04-17 02:48:06 -07:00
|
|
|
video_t *video = obs_get_video();
|
|
|
|
enum video_format format = video_output_get_format(video);
|
|
|
|
|
|
|
|
if (format != VIDEO_FORMAT_NV12 && format != VIDEO_FORMAT_I420)
|
|
|
|
obs_encoder_set_preferred_video_format(h264, VIDEO_FORMAT_NV12);
|
|
|
|
|
2015-02-06 03:17:33 -08:00
|
|
|
obs_encoder_update(h264, h264Settings);
|
|
|
|
obs_encoder_update(aac, aacSettings);
|
|
|
|
|
|
|
|
obs_data_release(h264Settings);
|
|
|
|
obs_data_release(aacSettings);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void SimpleOutput::SetupOutputs()
|
|
|
|
{
|
|
|
|
SimpleOutput::Update();
|
|
|
|
obs_encoder_set_video(h264, obs_get_video());
|
|
|
|
obs_encoder_set_audio(aac, obs_get_audio());
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SimpleOutput::StartStreaming(obs_service_t *service)
|
|
|
|
{
|
|
|
|
if (!Active())
|
|
|
|
SetupOutputs();
|
|
|
|
|
|
|
|
obs_output_set_video_encoder(streamOutput, h264);
|
|
|
|
obs_output_set_audio_encoder(streamOutput, aac, 0);
|
|
|
|
obs_output_set_service(streamOutput, service);
|
|
|
|
|
|
|
|
bool reconnect = config_get_bool(main->Config(), "SimpleOutput",
|
|
|
|
"Reconnect");
|
|
|
|
int retryDelay = config_get_uint(main->Config(), "SimpleOutput",
|
|
|
|
"RetryDelay");
|
|
|
|
int maxRetries = config_get_uint(main->Config(), "SimpleOutput",
|
|
|
|
"MaxRetries");
|
|
|
|
if (!reconnect)
|
|
|
|
maxRetries = 0;
|
|
|
|
|
|
|
|
obs_output_set_reconnect_settings(streamOutput, maxRetries,
|
|
|
|
retryDelay);
|
|
|
|
|
|
|
|
if (obs_output_start(streamOutput)) {
|
|
|
|
activeRefs++;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SimpleOutput::StartRecording()
|
|
|
|
{
|
|
|
|
if (!Active())
|
|
|
|
SetupOutputs();
|
|
|
|
|
|
|
|
const char *path = config_get_string(main->Config(),
|
|
|
|
"SimpleOutput", "FilePath");
|
2015-05-29 09:45:54 -07:00
|
|
|
const char *format = config_get_string(main->Config(),
|
|
|
|
"SimpleOutput", "RecFormat");
|
2015-02-06 03:17:33 -08:00
|
|
|
|
|
|
|
os_dir_t *dir = path ? os_opendir(path) : nullptr;
|
|
|
|
|
|
|
|
if (!dir) {
|
|
|
|
QMessageBox::information(main,
|
|
|
|
QTStr("Output.BadPath.Title"),
|
|
|
|
QTStr("Output.BadPath.Text"));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
os_closedir(dir);
|
|
|
|
|
|
|
|
string strPath;
|
|
|
|
strPath += path;
|
|
|
|
|
|
|
|
char lastChar = strPath.back();
|
|
|
|
if (lastChar != '/' && lastChar != '\\')
|
|
|
|
strPath += "/";
|
|
|
|
|
2015-05-29 09:45:54 -07:00
|
|
|
strPath += GenerateTimeDateFilename(format);
|
2015-02-06 03:17:33 -08:00
|
|
|
|
|
|
|
SetupOutputs();
|
|
|
|
|
|
|
|
obs_output_set_video_encoder(fileOutput, h264);
|
|
|
|
obs_output_set_audio_encoder(fileOutput, aac, 0);
|
|
|
|
|
|
|
|
obs_data_t *settings = obs_data_create();
|
|
|
|
obs_data_set_string(settings, "path", strPath.c_str());
|
|
|
|
|
|
|
|
obs_output_update(fileOutput, settings);
|
|
|
|
|
|
|
|
obs_data_release(settings);
|
|
|
|
|
|
|
|
if (obs_output_start(fileOutput)) {
|
|
|
|
activeRefs++;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SimpleOutput::StopStreaming()
|
|
|
|
{
|
|
|
|
obs_output_stop(streamOutput);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SimpleOutput::StopRecording()
|
|
|
|
{
|
|
|
|
obs_output_stop(fileOutput);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SimpleOutput::StreamingActive() const
|
|
|
|
{
|
|
|
|
return obs_output_active(streamOutput);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SimpleOutput::RecordingActive() const
|
|
|
|
{
|
|
|
|
return obs_output_active(fileOutput);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
|
|
|
2015-01-26 13:41:22 -08:00
|
|
|
struct AdvancedOutput : BasicOutputHandler {
|
|
|
|
OBSEncoder aacTrack[4];
|
|
|
|
OBSEncoder h264Streaming;
|
|
|
|
OBSEncoder h264Recording;
|
|
|
|
|
2015-08-18 20:58:24 -07:00
|
|
|
bool ffmpegOutput;
|
2015-01-26 13:41:22 -08:00
|
|
|
bool ffmpegRecording;
|
|
|
|
bool useStreamEncoder;
|
|
|
|
|
2015-07-02 01:00:22 -07:00
|
|
|
string aacEncoderID[4];
|
|
|
|
|
2015-01-26 13:41:22 -08:00
|
|
|
AdvancedOutput(OBSBasic *main_);
|
|
|
|
|
|
|
|
inline void UpdateStreamSettings();
|
|
|
|
inline void UpdateRecordingSettings();
|
2015-03-07 16:32:00 -08:00
|
|
|
inline void UpdateAudioSettings();
|
2015-01-26 13:41:22 -08:00
|
|
|
virtual void Update() override;
|
|
|
|
|
|
|
|
inline void SetupStreaming();
|
|
|
|
inline void SetupRecording();
|
|
|
|
inline void SetupFFmpeg();
|
|
|
|
void SetupOutputs();
|
2015-07-02 00:58:28 -07:00
|
|
|
int GetAudioBitrate(size_t i) const;
|
2015-01-26 13:41:22 -08:00
|
|
|
|
|
|
|
virtual bool StartStreaming(obs_service_t *service) override;
|
|
|
|
virtual bool StartRecording() override;
|
|
|
|
virtual void StopStreaming() override;
|
|
|
|
virtual void StopRecording() override;
|
|
|
|
virtual bool StreamingActive() const override;
|
|
|
|
virtual bool RecordingActive() const override;
|
|
|
|
};
|
|
|
|
|
|
|
|
static OBSData GetDataFromJsonFile(const char *jsonFile)
|
|
|
|
{
|
|
|
|
char fullPath[512];
|
|
|
|
|
2015-06-23 19:38:01 -07:00
|
|
|
int ret = GetProfilePath(fullPath, sizeof(fullPath), jsonFile);
|
2015-01-26 13:41:22 -08:00
|
|
|
if (ret > 0) {
|
|
|
|
BPtr<char> jsonData = os_quick_read_utf8_file(fullPath);
|
|
|
|
if (!!jsonData) {
|
|
|
|
obs_data_t *data = obs_data_create_from_json(jsonData);
|
|
|
|
OBSData dataRet(data);
|
|
|
|
obs_data_release(data);
|
|
|
|
return dataRet;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
AdvancedOutput::AdvancedOutput(OBSBasic *main_) : BasicOutputHandler(main_)
|
|
|
|
{
|
|
|
|
const char *recType = config_get_string(main->Config(), "AdvOut",
|
|
|
|
"RecType");
|
|
|
|
const char *streamEncoder = config_get_string(main->Config(), "AdvOut",
|
|
|
|
"Encoder");
|
|
|
|
const char *recordEncoder = config_get_string(main->Config(), "AdvOut",
|
|
|
|
"RecEncoder");
|
|
|
|
|
2015-08-18 20:58:24 -07:00
|
|
|
ffmpegOutput = astrcmpi(recType, "FFmpeg") == 0;
|
|
|
|
ffmpegRecording = ffmpegOutput &&
|
|
|
|
config_get_bool(main->Config(), "AdvOut", "FFOutputToFile");
|
2015-01-26 13:41:22 -08:00
|
|
|
useStreamEncoder = astrcmpi(recordEncoder, "none") == 0;
|
|
|
|
|
2015-06-23 19:38:01 -07:00
|
|
|
OBSData streamEncSettings = GetDataFromJsonFile("streamEncoder.json");
|
|
|
|
OBSData recordEncSettings = GetDataFromJsonFile("recordEncoder.json");
|
2015-01-26 13:41:22 -08:00
|
|
|
|
|
|
|
streamOutput = obs_output_create("rtmp_output", "adv_stream",
|
2014-11-01 13:41:17 -07:00
|
|
|
nullptr, nullptr);
|
2015-01-26 13:41:22 -08:00
|
|
|
if (!streamOutput)
|
|
|
|
throw "Failed to create stream output (advanced output)";
|
2015-05-03 16:55:43 -07:00
|
|
|
obs_output_release(streamOutput);
|
2015-01-26 13:41:22 -08:00
|
|
|
|
2015-08-18 20:58:24 -07:00
|
|
|
if (ffmpegOutput) {
|
2015-01-26 13:41:22 -08:00
|
|
|
fileOutput = obs_output_create("ffmpeg_output",
|
2014-11-01 13:41:17 -07:00
|
|
|
"adv_ffmpeg_output", nullptr, nullptr);
|
2015-01-26 13:41:22 -08:00
|
|
|
if (!fileOutput)
|
|
|
|
throw "Failed to create recording FFmpeg output "
|
|
|
|
"(advanced output)";
|
2015-05-03 16:55:43 -07:00
|
|
|
obs_output_release(fileOutput);
|
2015-01-26 13:41:22 -08:00
|
|
|
} else {
|
2015-05-28 23:27:06 -07:00
|
|
|
fileOutput = obs_output_create("ffmpeg_muxer",
|
|
|
|
"adv_file_output", nullptr, nullptr);
|
2015-01-26 13:41:22 -08:00
|
|
|
if (!fileOutput)
|
|
|
|
throw "Failed to create recording output "
|
|
|
|
"(advanced output)";
|
2015-05-03 16:55:43 -07:00
|
|
|
obs_output_release(fileOutput);
|
2015-01-26 13:41:22 -08:00
|
|
|
|
|
|
|
if (!useStreamEncoder) {
|
|
|
|
h264Recording = obs_video_encoder_create(recordEncoder,
|
2014-11-01 13:41:17 -07:00
|
|
|
"recording_h264", recordEncSettings,
|
|
|
|
nullptr);
|
2015-01-26 13:41:22 -08:00
|
|
|
if (!h264Recording)
|
|
|
|
throw "Failed to create recording h264 "
|
|
|
|
"encoder (advanced output)";
|
2015-05-03 17:01:38 -07:00
|
|
|
obs_encoder_release(h264Recording);
|
2015-01-26 13:41:22 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
h264Streaming = obs_video_encoder_create(streamEncoder,
|
2014-11-01 13:41:17 -07:00
|
|
|
"streaming_h264", streamEncSettings, nullptr);
|
2015-01-26 13:41:22 -08:00
|
|
|
if (!h264Streaming)
|
|
|
|
throw "Failed to create streaming h264 encoder "
|
|
|
|
"(advanced output)";
|
2015-05-03 17:01:38 -07:00
|
|
|
obs_encoder_release(h264Streaming);
|
2015-01-26 13:41:22 -08:00
|
|
|
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
|
|
char name[9];
|
|
|
|
sprintf(name, "adv_aac%d", i);
|
|
|
|
|
2015-07-02 01:00:22 -07:00
|
|
|
if (!CreateAACEncoder(aacTrack[i], aacEncoderID[i],
|
|
|
|
GetAudioBitrate(i), name, i))
|
2015-01-26 13:41:22 -08:00
|
|
|
throw "Failed to create audio encoder "
|
|
|
|
"(advanced output)";
|
|
|
|
}
|
|
|
|
|
2015-07-03 23:00:21 -07:00
|
|
|
startStreaming.Connect(obs_output_get_signal_handler(streamOutput),
|
2015-01-26 13:41:22 -08:00
|
|
|
"start", OBSStartStreaming, this);
|
2015-07-03 23:00:21 -07:00
|
|
|
stopStreaming.Connect(obs_output_get_signal_handler(streamOutput),
|
2015-01-26 13:41:22 -08:00
|
|
|
"stop", OBSStopStreaming, this);
|
|
|
|
|
2015-07-03 23:00:21 -07:00
|
|
|
startRecording.Connect(obs_output_get_signal_handler(fileOutput),
|
2015-01-26 13:41:22 -08:00
|
|
|
"start", OBSStartRecording, this);
|
2015-07-03 23:00:21 -07:00
|
|
|
stopRecording.Connect(obs_output_get_signal_handler(fileOutput),
|
2015-01-26 13:41:22 -08:00
|
|
|
"stop", OBSStopRecording, this);
|
|
|
|
}
|
|
|
|
|
|
|
|
void AdvancedOutput::UpdateStreamSettings()
|
|
|
|
{
|
2015-03-07 16:32:00 -08:00
|
|
|
bool applyServiceSettings = config_get_bool(main->Config(), "AdvOut",
|
|
|
|
"ApplyServiceSettings");
|
|
|
|
|
2015-06-23 19:38:01 -07:00
|
|
|
OBSData settings = GetDataFromJsonFile("streamEncoder.json");
|
2015-03-07 16:32:00 -08:00
|
|
|
|
|
|
|
if (applyServiceSettings)
|
|
|
|
obs_service_apply_encoder_settings(main->GetService(),
|
|
|
|
settings, nullptr);
|
|
|
|
|
2015-04-17 02:48:06 -07:00
|
|
|
video_t *video = obs_get_video();
|
|
|
|
enum video_format format = video_output_get_format(video);
|
|
|
|
|
|
|
|
if (format != VIDEO_FORMAT_NV12 && format != VIDEO_FORMAT_I420)
|
|
|
|
obs_encoder_set_preferred_video_format(h264Streaming,
|
|
|
|
VIDEO_FORMAT_NV12);
|
|
|
|
|
2015-01-26 13:41:22 -08:00
|
|
|
obs_encoder_update(h264Streaming, settings);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void AdvancedOutput::UpdateRecordingSettings()
|
|
|
|
{
|
2015-06-23 19:38:01 -07:00
|
|
|
OBSData settings = GetDataFromJsonFile("recordEncoder.json");
|
2015-01-26 13:41:22 -08:00
|
|
|
obs_encoder_update(h264Recording, settings);
|
|
|
|
}
|
|
|
|
|
|
|
|
void AdvancedOutput::Update()
|
|
|
|
{
|
|
|
|
UpdateStreamSettings();
|
2015-08-18 20:58:24 -07:00
|
|
|
if (!useStreamEncoder && !ffmpegOutput)
|
2015-01-26 13:41:22 -08:00
|
|
|
UpdateRecordingSettings();
|
2015-03-07 16:32:00 -08:00
|
|
|
UpdateAudioSettings();
|
2015-01-26 13:41:22 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
inline void AdvancedOutput::SetupStreaming()
|
|
|
|
{
|
|
|
|
bool rescale = config_get_bool(main->Config(), "AdvOut",
|
|
|
|
"Rescale");
|
|
|
|
const char *rescaleRes = config_get_string(main->Config(), "AdvOut",
|
|
|
|
"RescaleRes");
|
|
|
|
bool multitrack = config_get_bool(main->Config(), "AdvOut",
|
|
|
|
"Multitrack");
|
|
|
|
int trackIndex = config_get_int(main->Config(), "AdvOut",
|
|
|
|
"TrackIndex");
|
|
|
|
int trackCount = config_get_int(main->Config(), "AdvOut",
|
|
|
|
"TrackCount");
|
|
|
|
unsigned int cx = 0;
|
|
|
|
unsigned int cy = 0;
|
|
|
|
|
2015-03-31 13:48:06 -07:00
|
|
|
if (rescale && rescaleRes && *rescaleRes) {
|
|
|
|
if (sscanf(rescaleRes, "%ux%u", &cx, &cy) != 2) {
|
|
|
|
cx = 0;
|
|
|
|
cy = 0;
|
|
|
|
}
|
2015-01-26 13:41:22 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
obs_encoder_set_scaled_size(h264Streaming, cx, cy);
|
|
|
|
obs_encoder_set_video(h264Streaming, obs_get_video());
|
|
|
|
|
|
|
|
obs_output_set_video_encoder(streamOutput, h264Streaming);
|
|
|
|
|
|
|
|
if (multitrack) {
|
|
|
|
int i = 0;
|
2015-03-07 16:32:00 -08:00
|
|
|
for (; i < trackCount; i++)
|
2015-01-26 13:41:22 -08:00
|
|
|
obs_output_set_audio_encoder(streamOutput, aacTrack[i],
|
|
|
|
i);
|
|
|
|
for (; i < 4; i++)
|
|
|
|
obs_output_set_audio_encoder(streamOutput, nullptr, i);
|
2015-02-10 20:06:00 -08:00
|
|
|
|
2015-01-26 13:41:22 -08:00
|
|
|
} else {
|
|
|
|
obs_output_set_audio_encoder(streamOutput,
|
|
|
|
aacTrack[trackIndex - 1], 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void AdvancedOutput::SetupRecording()
|
|
|
|
{
|
|
|
|
const char *path = config_get_string(main->Config(), "AdvOut",
|
|
|
|
"RecFilePath");
|
|
|
|
bool rescale = config_get_bool(main->Config(), "AdvOut",
|
|
|
|
"RecRescale");
|
|
|
|
const char *rescaleRes = config_get_string(main->Config(), "AdvOut",
|
|
|
|
"RecRescaleRes");
|
2015-05-30 21:45:14 -07:00
|
|
|
int tracks = config_get_int(main->Config(), "AdvOut", "RecTracks");
|
2015-01-26 13:41:22 -08:00
|
|
|
obs_data_t *settings = obs_data_create();
|
|
|
|
unsigned int cx = 0;
|
|
|
|
unsigned int cy = 0;
|
2015-05-30 21:45:14 -07:00
|
|
|
int idx = 0;
|
2015-01-26 13:41:22 -08:00
|
|
|
|
|
|
|
if (useStreamEncoder) {
|
|
|
|
obs_output_set_video_encoder(fileOutput, h264Streaming);
|
|
|
|
} else {
|
2015-03-31 13:48:06 -07:00
|
|
|
if (rescale && rescaleRes && *rescaleRes) {
|
|
|
|
if (sscanf(rescaleRes, "%ux%u", &cx, &cy) != 2) {
|
|
|
|
cx = 0;
|
|
|
|
cy = 0;
|
|
|
|
}
|
2015-01-26 13:41:22 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
obs_encoder_set_scaled_size(h264Recording, cx, cy);
|
|
|
|
obs_encoder_set_video(h264Recording, obs_get_video());
|
|
|
|
obs_output_set_video_encoder(fileOutput, h264Recording);
|
|
|
|
}
|
|
|
|
|
2015-05-30 21:45:14 -07:00
|
|
|
for (int i = 0; i < MAX_AUDIO_MIXES; i++) {
|
|
|
|
if ((tracks & (1<<i)) != 0) {
|
2015-01-26 13:41:22 -08:00
|
|
|
obs_output_set_audio_encoder(fileOutput, aacTrack[i],
|
2015-05-30 21:45:14 -07:00
|
|
|
idx++);
|
|
|
|
}
|
2015-01-26 13:41:22 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
obs_data_set_string(settings, "path", path);
|
|
|
|
obs_output_update(fileOutput, settings);
|
|
|
|
obs_data_release(settings);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void AdvancedOutput::SetupFFmpeg()
|
|
|
|
{
|
|
|
|
const char *url = config_get_string(main->Config(), "AdvOut", "FFURL");
|
|
|
|
int vBitrate = config_get_int(main->Config(), "AdvOut",
|
|
|
|
"FFVBitrate");
|
|
|
|
bool rescale = config_get_bool(main->Config(), "AdvOut",
|
|
|
|
"FFRescale");
|
|
|
|
const char *rescaleRes = config_get_string(main->Config(), "AdvOut",
|
|
|
|
"FFRescaleRes");
|
2015-03-28 00:21:16 -07:00
|
|
|
const char *formatName = config_get_string(main->Config(), "AdvOut",
|
|
|
|
"FFFormat");
|
|
|
|
const char *mimeType = config_get_string(main->Config(), "AdvOut",
|
|
|
|
"FFFormatMimeType");
|
2015-01-26 13:41:22 -08:00
|
|
|
const char *vEncoder = config_get_string(main->Config(), "AdvOut",
|
|
|
|
"FFVEncoder");
|
2015-03-28 00:21:16 -07:00
|
|
|
int vEncoderId = config_get_int(main->Config(), "AdvOut",
|
|
|
|
"FFVEncoderId");
|
2015-01-26 13:41:22 -08:00
|
|
|
const char *vEncCustom = config_get_string(main->Config(), "AdvOut",
|
|
|
|
"FFVCustom");
|
|
|
|
int aBitrate = config_get_int(main->Config(), "AdvOut",
|
|
|
|
"FFABitrate");
|
|
|
|
int aTrack = config_get_int(main->Config(), "AdvOut",
|
|
|
|
"FFAudioTrack");
|
|
|
|
const char *aEncoder = config_get_string(main->Config(), "AdvOut",
|
|
|
|
"FFAEncoder");
|
2015-03-28 00:21:16 -07:00
|
|
|
int aEncoderId = config_get_int(main->Config(), "AdvOut",
|
|
|
|
"FFAEncoderId");
|
2015-01-26 13:41:22 -08:00
|
|
|
const char *aEncCustom = config_get_string(main->Config(), "AdvOut",
|
|
|
|
"FFACustom");
|
|
|
|
obs_data_t *settings = obs_data_create();
|
|
|
|
|
|
|
|
obs_data_set_string(settings, "url", url);
|
2015-03-28 00:21:16 -07:00
|
|
|
obs_data_set_string(settings, "format_name", formatName);
|
|
|
|
obs_data_set_string(settings, "format_mime_type", mimeType);
|
2015-01-26 13:41:22 -08:00
|
|
|
obs_data_set_int(settings, "video_bitrate", vBitrate);
|
|
|
|
obs_data_set_string(settings, "video_encoder", vEncoder);
|
2015-03-28 00:21:16 -07:00
|
|
|
obs_data_set_int(settings, "video_encoder_id", vEncoderId);
|
2015-01-26 13:41:22 -08:00
|
|
|
obs_data_set_string(settings, "video_settings", vEncCustom);
|
|
|
|
obs_data_set_int(settings, "audio_bitrate", aBitrate);
|
|
|
|
obs_data_set_string(settings, "audio_encoder", aEncoder);
|
2015-03-28 00:21:16 -07:00
|
|
|
obs_data_set_int(settings, "audio_encoder_id", aEncoderId);
|
2015-01-26 13:41:22 -08:00
|
|
|
obs_data_set_string(settings, "audio_settings", aEncCustom);
|
|
|
|
|
|
|
|
if (rescale && rescaleRes && *rescaleRes) {
|
|
|
|
int width;
|
|
|
|
int height;
|
|
|
|
int val = sscanf(rescaleRes, "%dx%d", &width, &height);
|
|
|
|
|
|
|
|
if (val == 2 && width && height) {
|
|
|
|
obs_data_set_int(settings, "scale_width", width);
|
|
|
|
obs_data_set_int(settings, "scale_height", height);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
obs_output_set_mixer(fileOutput, aTrack - 1);
|
|
|
|
obs_output_set_media(fileOutput, obs_get_video(), obs_get_audio());
|
|
|
|
obs_output_update(fileOutput, settings);
|
|
|
|
|
|
|
|
obs_data_release(settings);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void SetEncoderName(obs_encoder_t *encoder, const char *name,
|
|
|
|
const char *defaultName)
|
|
|
|
{
|
|
|
|
obs_encoder_set_name(encoder, (name && *name) ? name : defaultName);
|
|
|
|
}
|
|
|
|
|
2015-03-07 16:32:00 -08:00
|
|
|
inline void AdvancedOutput::UpdateAudioSettings()
|
2015-01-26 13:41:22 -08:00
|
|
|
{
|
|
|
|
const char *name1 = config_get_string(main->Config(), "AdvOut",
|
|
|
|
"Track1Name");
|
|
|
|
const char *name2 = config_get_string(main->Config(), "AdvOut",
|
|
|
|
"Track2Name");
|
|
|
|
const char *name3 = config_get_string(main->Config(), "AdvOut",
|
|
|
|
"Track3Name");
|
|
|
|
const char *name4 = config_get_string(main->Config(), "AdvOut",
|
|
|
|
"Track4Name");
|
2015-03-07 16:32:00 -08:00
|
|
|
bool applyServiceSettings = config_get_bool(main->Config(), "AdvOut",
|
|
|
|
"ApplyServiceSettings");
|
2015-03-27 12:38:38 -07:00
|
|
|
int streamTrackIndex = config_get_int(main->Config(), "AdvOut",
|
|
|
|
"TrackIndex");
|
2015-01-26 13:41:22 -08:00
|
|
|
obs_data_t *settings[4];
|
|
|
|
|
2015-07-02 00:58:28 -07:00
|
|
|
for (size_t i = 0; i < 4; i++) {
|
2015-01-26 13:41:22 -08:00
|
|
|
settings[i] = obs_data_create();
|
2015-07-02 00:58:28 -07:00
|
|
|
obs_data_set_int(settings[i], "bitrate", GetAudioBitrate(i));
|
|
|
|
}
|
2015-01-26 13:41:22 -08:00
|
|
|
|
|
|
|
SetEncoderName(aacTrack[0], name1, "Track1");
|
|
|
|
SetEncoderName(aacTrack[1], name2, "Track2");
|
|
|
|
SetEncoderName(aacTrack[2], name3, "Track3");
|
|
|
|
SetEncoderName(aacTrack[3], name4, "Track4");
|
|
|
|
|
|
|
|
for (size_t i = 0; i < 4; i++) {
|
2015-03-27 13:35:43 -07:00
|
|
|
if (applyServiceSettings && (int)(i + 1) == streamTrackIndex)
|
2015-03-07 16:32:00 -08:00
|
|
|
obs_service_apply_encoder_settings(main->GetService(),
|
|
|
|
nullptr, settings[i]);
|
|
|
|
|
2015-01-26 13:41:22 -08:00
|
|
|
obs_encoder_update(aacTrack[i], settings[i]);
|
|
|
|
obs_data_release(settings[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void AdvancedOutput::SetupOutputs()
|
|
|
|
{
|
|
|
|
obs_encoder_set_video(h264Streaming, obs_get_video());
|
|
|
|
if (h264Recording)
|
|
|
|
obs_encoder_set_video(h264Recording, obs_get_video());
|
|
|
|
obs_encoder_set_audio(aacTrack[0], obs_get_audio());
|
|
|
|
obs_encoder_set_audio(aacTrack[1], obs_get_audio());
|
|
|
|
obs_encoder_set_audio(aacTrack[2], obs_get_audio());
|
|
|
|
obs_encoder_set_audio(aacTrack[3], obs_get_audio());
|
|
|
|
|
|
|
|
SetupStreaming();
|
|
|
|
|
2015-08-18 20:58:24 -07:00
|
|
|
if (ffmpegOutput)
|
2015-01-26 13:41:22 -08:00
|
|
|
SetupFFmpeg();
|
|
|
|
else
|
|
|
|
SetupRecording();
|
|
|
|
}
|
|
|
|
|
2015-07-02 00:58:28 -07:00
|
|
|
int AdvancedOutput::GetAudioBitrate(size_t i) const
|
|
|
|
{
|
|
|
|
const char *names[] = {
|
|
|
|
"Track1Bitrate", "Track2Bitrate",
|
|
|
|
"Track3Bitrate", "Track4Bitrate",
|
|
|
|
};
|
|
|
|
return config_get_uint(main->Config(), "AdvOut", names[i]);
|
|
|
|
}
|
|
|
|
|
2015-01-26 13:41:22 -08:00
|
|
|
bool AdvancedOutput::StartStreaming(obs_service_t *service)
|
|
|
|
{
|
2015-02-14 18:10:39 -08:00
|
|
|
if (!useStreamEncoder ||
|
2015-08-18 20:58:24 -07:00
|
|
|
(!ffmpegOutput && !obs_output_active(fileOutput))) {
|
2015-02-14 18:10:39 -08:00
|
|
|
UpdateStreamSettings();
|
|
|
|
}
|
|
|
|
|
2015-03-07 16:32:00 -08:00
|
|
|
UpdateAudioSettings();
|
|
|
|
|
2015-01-26 13:41:22 -08:00
|
|
|
if (!Active())
|
|
|
|
SetupOutputs();
|
|
|
|
|
|
|
|
obs_output_set_service(streamOutput, service);
|
|
|
|
|
|
|
|
bool reconnect = config_get_bool(main->Config(), "AdvOut", "Reconnect");
|
|
|
|
int retryDelay = config_get_int(main->Config(), "AdvOut", "RetryDelay");
|
|
|
|
int maxRetries = config_get_int(main->Config(), "AdvOut", "MaxRetries");
|
|
|
|
if (!reconnect)
|
|
|
|
maxRetries = 0;
|
|
|
|
|
|
|
|
obs_output_set_reconnect_settings(streamOutput, maxRetries,
|
|
|
|
retryDelay);
|
|
|
|
|
|
|
|
if (obs_output_start(streamOutput)) {
|
|
|
|
activeRefs++;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool AdvancedOutput::StartRecording()
|
|
|
|
{
|
2015-08-18 20:58:24 -07:00
|
|
|
const char *path;
|
|
|
|
const char *format;
|
|
|
|
|
2015-02-14 18:10:39 -08:00
|
|
|
if (!useStreamEncoder) {
|
2015-08-18 20:58:24 -07:00
|
|
|
if (!ffmpegOutput) {
|
2015-02-14 18:10:39 -08:00
|
|
|
UpdateRecordingSettings();
|
|
|
|
}
|
|
|
|
} else if (!obs_output_active(streamOutput)) {
|
|
|
|
UpdateStreamSettings();
|
|
|
|
}
|
|
|
|
|
2015-03-07 16:32:00 -08:00
|
|
|
UpdateAudioSettings();
|
|
|
|
|
2015-01-26 13:41:22 -08:00
|
|
|
if (!Active())
|
|
|
|
SetupOutputs();
|
|
|
|
|
2015-08-18 20:58:24 -07:00
|
|
|
if (!ffmpegOutput || ffmpegRecording) {
|
|
|
|
path = config_get_string(main->Config(), "AdvOut",
|
|
|
|
ffmpegRecording ? "FFFilePath" : "RecFilePath");
|
|
|
|
format = config_get_string(main->Config(), "AdvOut",
|
|
|
|
ffmpegRecording ? "FFExtension" : "RecFormat");
|
2015-01-26 13:41:22 -08:00
|
|
|
|
|
|
|
os_dir_t *dir = path ? os_opendir(path) : nullptr;
|
|
|
|
|
|
|
|
if (!dir) {
|
|
|
|
QMessageBox::information(main,
|
|
|
|
QTStr("Output.BadPath.Title"),
|
|
|
|
QTStr("Output.BadPath.Text"));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
os_closedir(dir);
|
|
|
|
|
|
|
|
string strPath;
|
|
|
|
strPath += path;
|
|
|
|
|
|
|
|
char lastChar = strPath.back();
|
|
|
|
if (lastChar != '/' && lastChar != '\\')
|
|
|
|
strPath += "/";
|
|
|
|
|
2015-05-29 09:45:54 -07:00
|
|
|
strPath += GenerateTimeDateFilename(format);
|
2015-01-26 13:41:22 -08:00
|
|
|
|
|
|
|
obs_data_t *settings = obs_data_create();
|
2015-08-18 20:58:24 -07:00
|
|
|
obs_data_set_string(settings,
|
|
|
|
ffmpegRecording ? "url" : "path",
|
|
|
|
strPath.c_str());
|
2015-01-26 13:41:22 -08:00
|
|
|
|
|
|
|
obs_output_update(fileOutput, settings);
|
|
|
|
|
|
|
|
obs_data_release(settings);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (obs_output_start(fileOutput)) {
|
|
|
|
activeRefs++;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void AdvancedOutput::StopStreaming()
|
|
|
|
{
|
|
|
|
obs_output_stop(streamOutput);
|
|
|
|
}
|
|
|
|
|
|
|
|
void AdvancedOutput::StopRecording()
|
|
|
|
{
|
|
|
|
obs_output_stop(fileOutput);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool AdvancedOutput::StreamingActive() const
|
|
|
|
{
|
|
|
|
return obs_output_active(streamOutput);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool AdvancedOutput::RecordingActive() const
|
|
|
|
{
|
|
|
|
return obs_output_active(fileOutput);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
|
|
|
2015-02-06 03:17:33 -08:00
|
|
|
BasicOutputHandler *CreateSimpleOutputHandler(OBSBasic *main)
|
|
|
|
{
|
|
|
|
return new SimpleOutput(main);
|
|
|
|
}
|
2015-01-26 13:41:22 -08:00
|
|
|
|
|
|
|
BasicOutputHandler *CreateAdvancedOutputHandler(OBSBasic *main)
|
|
|
|
{
|
|
|
|
return new AdvancedOutput(main);
|
|
|
|
}
|