- Add some temporary streaming code using FFmpeg. FFmpeg itself is not very ideal for streaming; lack of direct control of the sockets and no framedrop handling means that FFmpeg is definitely not something you want to use without wrapper code. I'd prefer writing my own network framework in this particular case just because you give away so much control of the network interface. Wasted an entire day trying to go through FFmpeg issues. There's just no way FFmpeg should be used for real streaming (at least without being patched or submitting some sort of patch, but I'm sort of feeling "meh" on that idea) I had to end up writing multiple threads just to handle both connecting and writing, because av_interleaved_write_frame blocks every call, stalling the main encoder thread, and thus also stalling draw signals. - Add some temporary user interface for streaming settings. This is just temporary for the time being. It's in the outputs section of the basic-mode settings - Make it so that dynamic arrays do not free all their data when the size just happens to be reduced to 0. This prevents constant reallocation when an array keeps going from 1 item to 0 items. Also, it was bad to become dependent upon that functionality. You must now always explicitly call "free" on it to ensure the data is free, and that's how it should be. Implicit functionality can lead to confusion and maintainability issues.
240 lines
5.4 KiB
C++
240 lines
5.4 KiB
C++
/******************************************************************************
|
|
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
******************************************************************************/
|
|
|
|
#include <sstream>
|
|
#include <util/bmem.h>
|
|
#include <util/dstr.h>
|
|
#include <util/platform.h>
|
|
#include <obs.hpp>
|
|
|
|
#include <QProxyStyle>
|
|
|
|
#include "qt-wrappers.hpp"
|
|
#include "obs-app.hpp"
|
|
#include "window-basic-main.hpp"
|
|
#include "platform.hpp"
|
|
|
|
#ifdef _WIN32
|
|
#include <windows.h>
|
|
#endif
|
|
|
|
using namespace std;
|
|
|
|
static void do_log(int log_level, const char *msg, va_list args)
|
|
{
|
|
#ifdef _WIN32
|
|
char bla[4096];
|
|
vsnprintf(bla, 4095, msg, args);
|
|
|
|
OutputDebugStringA(bla);
|
|
OutputDebugStringA("\n");
|
|
|
|
if (log_level <= LOG_ERROR && IsDebuggerPresent())
|
|
__debugbreak();
|
|
#else
|
|
vprintf(msg, args);
|
|
printf("\n");
|
|
|
|
UNUSED_PARAMETER(log_level);
|
|
#endif
|
|
}
|
|
|
|
bool OBSApp::InitGlobalConfigDefaults()
|
|
{
|
|
config_set_default_string(globalConfig, "General", "Language", "en");
|
|
|
|
#if _WIN32
|
|
config_set_default_string(globalConfig, "Video", "Renderer",
|
|
"Direct3D 11");
|
|
#else
|
|
config_set_default_string(globalConfig, "Video", "Renderer", "OpenGL");
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool do_mkdir(const char *path)
|
|
{
|
|
if (os_mkdir(path) == MKDIR_ERROR) {
|
|
OBSErrorBox(NULL, "Failed to create directory %s", path);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool MakeUserDirs()
|
|
{
|
|
BPtr<char> configPath;
|
|
|
|
configPath = os_get_config_path("obs-studio");
|
|
if (!do_mkdir(configPath))
|
|
return false;
|
|
|
|
configPath = os_get_config_path("obs-studio/basic");
|
|
if (!do_mkdir(configPath))
|
|
return false;
|
|
|
|
configPath = os_get_config_path("obs-studio/studio");
|
|
if (!do_mkdir(configPath))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool OBSApp::InitGlobalConfig()
|
|
{
|
|
BPtr<char> path(os_get_config_path("obs-studio/global.ini"));
|
|
|
|
int errorcode = globalConfig.Open(path, CONFIG_OPEN_ALWAYS);
|
|
if (errorcode != CONFIG_SUCCESS) {
|
|
OBSErrorBox(NULL, "Failed to open global.ini: %d", errorcode);
|
|
return false;
|
|
}
|
|
|
|
return InitGlobalConfigDefaults();
|
|
}
|
|
|
|
#define DEFAULT_LANG "en"
|
|
|
|
bool OBSApp::InitLocale()
|
|
{
|
|
const char *lang = config_get_string(globalConfig, "General",
|
|
"Language");
|
|
|
|
locale = lang;
|
|
|
|
stringstream file;
|
|
file << "locale/" << lang << ".txt";
|
|
|
|
string englishPath;
|
|
if (!GetDataFilePath("locale/" DEFAULT_LANG ".txt", englishPath)) {
|
|
OBSErrorBox(NULL, "Failed to find locale/" DEFAULT_LANG ".txt");
|
|
return false;
|
|
}
|
|
|
|
textLookup = text_lookup_create(englishPath.c_str());
|
|
if (!textLookup) {
|
|
OBSErrorBox(NULL, "Failed to create locale from file '%s'",
|
|
englishPath.c_str());
|
|
return false;
|
|
}
|
|
|
|
if (astrcmpi(lang, DEFAULT_LANG) == 0)
|
|
return true;
|
|
|
|
string path;
|
|
if (GetDataFilePath(file.str().c_str(), path)) {
|
|
if (!text_lookup_add(textLookup, path.c_str()))
|
|
blog(LOG_ERROR, "Failed to add locale file '%s'",
|
|
path.c_str());
|
|
} else {
|
|
blog(LOG_ERROR, "Could not find locale file '%s'",
|
|
file.str().c_str());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
OBSApp::OBSApp(int &argc, char **argv)
|
|
: QApplication(argc, argv)
|
|
{
|
|
if (!InitApplicationBundle())
|
|
throw "Failed to initialize application bundle";
|
|
if (!MakeUserDirs())
|
|
throw "Failed to created required user directories";
|
|
if (!InitGlobalConfig())
|
|
throw "Failed to initialize global config";
|
|
if (!InitLocale())
|
|
throw "Failed to load locale";
|
|
|
|
mainWindow = move(unique_ptr<OBSBasic>(new OBSBasic()));
|
|
}
|
|
|
|
const char *OBSApp::GetRenderModule() const
|
|
{
|
|
const char *renderer = config_get_string(globalConfig, "Video",
|
|
"Renderer");
|
|
|
|
if (astrcmpi(renderer, "Direct3D 11") == 0)
|
|
return "libobs-d3d11";
|
|
else
|
|
return "libobs-opengl";
|
|
}
|
|
|
|
void OBSApp::OBSInit()
|
|
{
|
|
mainWindow->OBSInit();
|
|
}
|
|
|
|
#ifdef __APPLE__
|
|
#define INPUT_AUDIO_SOURCE "coreaudio_input_capture"
|
|
#define OUTPUT_AUDIO_SOURCE "coreaudio_output_capture"
|
|
#elif _WIN32
|
|
#define INPUT_AUDIO_SOURCE "wasapi_input_capture"
|
|
#define OUTPUT_AUDIO_SOURCE "wasapi_output_capture"
|
|
#else
|
|
#define INPUT_AUDIO_SOURCE ""
|
|
#define OUTPUT_AUDIO_SOURCE ""
|
|
#endif
|
|
|
|
const char *OBSApp::InputAudioSource() const
|
|
{
|
|
return INPUT_AUDIO_SOURCE;
|
|
}
|
|
|
|
const char *OBSApp::OutputAudioSource() const
|
|
{
|
|
return OUTPUT_AUDIO_SOURCE;
|
|
}
|
|
|
|
struct NoFocusFrameStyle : QProxyStyle
|
|
{
|
|
void drawControl(ControlElement element, const QStyleOption *option,
|
|
QPainter *painter, const QWidget *widget=nullptr)
|
|
const override
|
|
{
|
|
if (element == CE_FocusFrame)
|
|
return;
|
|
|
|
QProxyStyle::drawControl(element, option, painter, widget);
|
|
}
|
|
};
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
int ret = -1;
|
|
QCoreApplication::addLibraryPath(".");
|
|
#ifdef _WIN32
|
|
base_set_log_handler(do_log);
|
|
#endif
|
|
|
|
try {
|
|
OBSApp program(argc, argv);
|
|
program.setStyle(new NoFocusFrameStyle);
|
|
program.OBSInit();
|
|
ret = program.exec();
|
|
|
|
} catch (const char *error) {
|
|
blog(LOG_ERROR, "%s", error);
|
|
}
|
|
|
|
blog(LOG_INFO, "Number of memory leaks: %llu",
|
|
(unsigned long long int)bnum_allocs());
|
|
return ret;
|
|
}
|