提交 8830c410 编写于 作者: J jp9000

obs-studio UI: Implement stream settings UI

 - Updated the services API so that it links up with an output and
   the output gets data from that service rather than via settings.
   This allows the service context to have control over how an output is
   used, and makes it so that the URL/key/etc isn't necessarily some
   static setting.

   Also, if the service is attached to an output, it will stick around
   until the output is destroyed.

 - The settings interface has been updated so that it can allow the
   usage of service plugins.  What this means is that now you can create
   a service plugin that can control aspects of the stream, and it
   allows each service to create their own user interface if they create
   a service plugin module.

 - Testing out saving of current service information.  Saves/loads from
   JSON in to obs_data_t, seems to be working quite nicely, and the
   service object information is saved/preserved on exit, and loaded
   again on startup.

 - I agonized over the settings user interface for days, and eventually
   I just decided that the only way that users weren't going to be
   fumbling over options was to split up the settings in to simple/basic
   output, pre-configured, and then advanced for advanced use (such as
   multiple outputs or services, which I'll implement later).

   This was particularly painful to really design right, I wanted more
   features and wanted to include everything in one interface but
   ultimately just realized from experience that users are just not
   technically knowledgable about it and will end up fumbling with the
   settings rather than getting things done.

   Basically, what this means is that casual users only have to enter in
   about 3 things to configure their stream:  Stream key, audio bitrate,
   and video bitrate.  I am really happy with this interface for those
   types of users, but it definitely won't be sufficient for advanced
   usage or for custom outputs, so that stuff will have to be separated.

 - Improved the JSON usage for the 'common streaming services' context,
   I realized that JSON arrays are there to ensure sorting, while
   forgetting that general items are optimized for hashing.  So
   basically I'm just using arrays now to sort items in it.
上级 3cbc711f
{ [
"Twitch / Justin.tv": { {
"servers": { "name": "Twitch / Justin.tv",
"US West: San Francisco, CA" : "rtmp://live.justin.tv/app", "servers": [
"Asia: Singapore" : "rtmp://live-sin-backup.justin.tv/app", {
"EU: Amsterdam, NL" : "rtmp://live-ams.justin.tv/app", "name": "US West: San Francisco, CA",
"EU: Frankfurt, DE" : "rtmp://live-fra.justin.tv/app", "url": "rtmp://live.justin.tv/app"
"EU: London, UK" : "rtmp://live-lhr.justin.tv/app", },
"EU: Paris, FR" : "rtmp://live-cdg.justin.tv/app", {
"EU: Prague, CZ" : "rtmp://live-prg.justin.tv/app", "name": "Asia: Singapore",
"EU: Stockholm, SE" : "rtmp://live-arn.justin.tv/app", "url": "rtmp://live-sin-backup.justin.tv/app"
"US Central: Dallas, TX" : "rtmp://live-dfw.justin.tv/app", },
"US East: Ashburn, VA" : "rtmp://live-iad.justin.tv/app", {
"US East: Miami, FL" : "rtmp://live-mia.justin.tv/app", "name": "EU: Amsterdam, NL",
"US East: New York, NY" : "rtmp://live-jfk.justin.tv/app", "url": "rtmp://live-ams.justin.tv/app"
"US Midwest: Chicago, IL" : "rtmp://live-ord.justin.tv/app", },
"US West: Los Angeles, CA" : "rtmp://live-lax.justin.tv/app" {
}, "name": "EU: Frankfurt, DE",
"recommended": { "url": "rtmp://live-fra.justin.tv/app"
"keyint" : 2, },
"cbr" : true, {
"profile" : "main", "name": "EU: London, UK",
"max video bitrate" : 3500, "url": "rtmp://live-lhr.justin.tv/app"
"max audio bitrate" : 160 },
} {
}, "name": "EU: Paris, FR",
"Youtube": { "url": "rtmp://live-cdg.justin.tv/app"
"servers": { },
"Primary Youtube ingest server" : "rtmp://a.rtmp.youtube.com/live2", {
"Backup Youtube ingest server" : "rtmp://b.rtmp.youtube.com/live2?backup=1" "name": "EU: Prague, CZ",
}, "url": "rtmp://live-prg.justin.tv/app"
"recommended": { },
"keyint" : 2, {
"cbr" : true, "name": "EU: Stockholm, SE",
"profile" : "main", "url": "rtmp://live-arn.justin.tv/app"
"max video bitrate" : 3500, },
"max audio bitrate" : 160 {
} "name": "US Central: Dallas, TX",
}, "url": "rtmp://live-dfw.justin.tv/app"
"hitbox.tv": { },
"servers": { {
"Default" : "rtmp://live.hitbox.tv/push", "name": "US East: Ashburn, VA",
"EU-East" : "rtmp://live.vie.hitbox.tv/push", "url": "rtmp://live-iad.justin.tv/app"
"EU-Central" : "rtmp://live.nbg.hitbox.tv/push", },
"EU-West" : "rtmp://live.fra.hitbox.tv/push", {
"EU-North" : "rtmp://live.ams.hitbox.tv/push", "name": "US East: Miami, FL",
"US-East" : "rtmp://live.vgn.hitbox.tv/push", "url": "rtmp://live-mia.justin.tv/app"
"US-West" : "rtmp://live.lax.hitbox.tv/push", },
"South America" : "rtmp://live.gru.hitbox.tv/push", {
"Asia" : "rtmp://live.lax.hitbox.tv/push" "name": "US East: New York, NY",
}, "url": "rtmp://live-jfk.justin.tv/app"
"recommended": { },
"keyint" : 2, {
"cbr" : true, "name": "US Midwest: Chicago, IL",
"profile" : "main", "url": "rtmp://live-ord.justin.tv/app"
"max video bitrate" : 3500, },
"max audio bitrate" : 160 {
} "name": "US West: Los Angeles, CA",
}, "url": "rtmp://live-lax.justin.tv/app"
"Vaughn Live / iNSTAGIB.tv": { }
"servers": { ],
"US: Primary" : "rtmp://live.vaughnsoft.net:443/live", "recommended": {
"US: Virginia, USA" : "rtmp://live-iad.vaughnsoft.net:443/live", "keyint": 2,
"US: Chicago, IL" : "rtmp://live-ord.vaughnsoft.net:443/live", "cbr": true,
"EU: Frankfurt, Germany" : "rtmp://live-de.vaughnsoft.net:443/live" "profile": "main",
} "max video bitrate": 3500,
}, "max audio bitrate": 160
"DailyMotion": { }
"servers": { },
"Primary" : "rtmp://publish.dailymotion.com/publish-dm" {
} "name": "Youtube",
}, "servers": [
"connectcast.tv": { {
"servers": { "name": "Primary Youtube ingest server",
"Default" : "rtmp://stream.connectcast.tv/live" "url": "rtmp://a.rtmp.youtube.com/live2"
} },
}, {
"iNSTAGIB.tv": { "name": "Backup Youtube ingest server",
"servers": { "url": "rtmp://b.rtmp.youtube.com/live2?backup=1"
"US Chicago (Primary)" : "rtmp://live.instagib.tv:443/live" }
} ],
}, "recommended": {
"GoodGame.ru": { "keyint": 2,
"servers": { "cbr": true,
"Moscow M9" : "rtmp://stream.goodgame.ru:1940/live", "profile": "main",
"Moscow M10" : "rtmp://stream2.goodgame.ru:1940/live", "max video bitrate": 3500,
"Saint-Petersburg" : "rtmp://spb1.goodgame.ru:1940/live" "max audio bitrate": 160
} }
}, },
"CyberGame.TV": { {
"servers": { "name": "hitbox.tv",
"RU Origin" : "rtmp://st.cybergame.tv:1953/live", "servers": [
"RU Premium" : "rtmp://premium.cybergame.tv:1953/premium" {
} "name": "Default",
}, "url": "rtmp://live.hitbox.tv/push"
"CashPlay.tv": { },
"servers": { {
"Primary, UK" : "rtmp://live.cashplay.tv/live", "name": "EU-East",
"Low Priority, DE" : "rtmp://de.live.cashplay.tv/live" "url": "rtmp://live.vie.hitbox.tv/push"
} },
} {
} "name": "EU-Central",
"url": "rtmp://live.nbg.hitbox.tv/push"
},
{
"name": "EU-West",
"url": "rtmp://live.fra.hitbox.tv/push"
},
{
"name": "EU-North",
"url": "rtmp://live.ams.hitbox.tv/push"
},
{
"name": "US-East",
"url": "rtmp://live.vgn.hitbox.tv/push"
},
{
"name": "US-West",
"url": "rtmp://live.lax.hitbox.tv/push"
},
{
"name": "South America",
"url": "rtmp://live.gru.hitbox.tv/push"
},
{
"name": "Asia",
"url": "rtmp://live.lax.hitbox.tv/push"
}
],
"recommended": {
"keyint": 2,
"cbr": true,
"profile": "main",
"max video bitrate": 3500,
"max audio bitrate": 160
}
},
{
"name": "Vaughn Live / iNSTAGIB.tv",
"servers": [
{
"name": "US: Primary",
"url": "rtmp://live.vaughnsoft.net:443/live"
},
{
"name": "US: Virginia, USA",
"url": "rtmp://live-iad.vaughnsoft.net:443/live"
},
{
"name": "US: Chicago, IL",
"url": "rtmp://live-ord.vaughnsoft.net:443/live"
},
{
"name": "EU: Frankfurt, Germany",
"url": "rtmp://live-de.vaughnsoft.net:443/live"
}
]
},
{
"name": "DailyMotion",
"servers": [
{
"name": "Primary",
"url": "rtmp://publish.dailymotion.com/publish-dm"
}
]
},
{
"name": "connectcast.tv",
"servers": [
{
"name": "Default",
"url": "rtmp://stream.connectcast.tv/live"
}
]
},
{
"name": "iNSTAGIB.tv",
"servers": [
{
"name": "US Chicago (Primary)",
"url": "rtmp://live.instagib.tv:443/live"
}
]
},
{
"name": "GoodGame.ru",
"servers": [
{
"name": "Moscow M9",
"url": "rtmp://stream.goodgame.ru:1940/live"
},
{
"name": "Moscow M10",
"url": "rtmp://stream2.goodgame.ru:1940/live"
},
{
"name": "Saint-Petersburg",
"url": "rtmp://spb1.goodgame.ru:1940/live"
}
]
},
{
"name": "CyberGame.TV",
"servers": [
{
"name": "RU Origin",
"url": "rtmp://st.cybergame.tv:1953/live"
},
{
"name": "RU Premium",
"url": "rtmp://premium.cybergame.tv:1953/premium"
}
]
},
{
"name": "CashPlay.tv",
"servers": [
{
"name": "Primary, UK",
"url": "rtmp://live.cashplay.tv/live"
},
{
"name": "Low Priority, DE",
"url": "rtmp://de.live.cashplay.tv/live"
}
]
}
]
Language="English" Language="English"
OK="OK"
Cancel="Cancel"
MainMenu.File="File" MainMenu.File="File"
MainMenu.File.New="New" MainMenu.File.New="New"
...@@ -41,6 +38,12 @@ Settings.Confirm="You have unsaved changes. Save changes?" ...@@ -41,6 +38,12 @@ Settings.Confirm="You have unsaved changes. Save changes?"
Settings.General="General" Settings.General="General"
Settings.General.Language="Language:" Settings.General.Language="Language:"
Settings.Streams="Streams"
Settings.Streams.AddName.Title="Add Stream"
Settings.Streams.AddName.Text="Please enter the name of the stream"
Settings.Streams.Exists.Title="Stream already exists"
Settings.Streams.Exists.Text="The name is already in use by another stream"
Settings.Outputs="Outputs" Settings.Outputs="Outputs"
Settings.Video="Video" Settings.Video="Video"
......
...@@ -224,6 +224,7 @@ set_target_properties(libobs PROPERTIES ...@@ -224,6 +224,7 @@ set_target_properties(libobs PROPERTIES
VERSION "0" VERSION "0"
SOVERSION "0") SOVERSION "0")
target_link_libraries(libobs target_link_libraries(libobs
jansson
${libobs_PLATFORM_DEPS} ${libobs_PLATFORM_DEPS}
${Libswscale_LIBRARIES} ${Libswscale_LIBRARIES}
${Libswresample_LIBRARIES} ${Libswresample_LIBRARIES}
......
...@@ -57,8 +57,7 @@ static bool init_encoder(struct obs_encoder *encoder, const char *name, ...@@ -57,8 +57,7 @@ static bool init_encoder(struct obs_encoder *encoder, const char *name,
static struct obs_encoder *create_encoder(const char *id, static struct obs_encoder *create_encoder(const char *id,
enum obs_encoder_type type, const char *name, enum obs_encoder_type type, const char *name,
obs_data_t settings, void *media, obs_data_t settings)
uint32_t timebase_num, uint32_t timebase_den)
{ {
struct obs_encoder *encoder; struct obs_encoder *encoder;
struct obs_encoder_info *ei = get_encoder_info(id); struct obs_encoder_info *ei = get_encoder_info(id);
...@@ -68,10 +67,7 @@ static struct obs_encoder *create_encoder(const char *id, ...@@ -68,10 +67,7 @@ static struct obs_encoder *create_encoder(const char *id,
return NULL; return NULL;
encoder = bzalloc(sizeof(struct obs_encoder)); encoder = bzalloc(sizeof(struct obs_encoder));
encoder->info = *ei; encoder->info = *ei;
encoder->media = media;
encoder->timebase_num = timebase_num;
encoder->timebase_den = timebase_den;
success = init_encoder(encoder, name, settings); success = init_encoder(encoder, name, settings);
if (!success) { if (!success) {
...@@ -87,29 +83,17 @@ static struct obs_encoder *create_encoder(const char *id, ...@@ -87,29 +83,17 @@ static struct obs_encoder *create_encoder(const char *id,
} }
obs_encoder_t obs_video_encoder_create(const char *id, const char *name, obs_encoder_t obs_video_encoder_create(const char *id, const char *name,
obs_data_t settings, video_t video) obs_data_t settings)
{ {
const struct video_output_info *voi; if (!name || !id) return NULL;
return create_encoder(id, OBS_ENCODER_VIDEO, name, settings);
if (!name || !id || !video)
return NULL;
voi = video_output_getinfo(video);
return create_encoder(id, OBS_ENCODER_VIDEO, name, settings, video,
voi->fps_den, voi->fps_num);
} }
obs_encoder_t obs_audio_encoder_create(const char *id, const char *name, obs_encoder_t obs_audio_encoder_create(const char *id, const char *name,
obs_data_t settings, audio_t audio) obs_data_t settings)
{ {
const struct audio_output_info *aoi; if (!name || !id) return NULL;
return create_encoder(id, OBS_ENCODER_AUDIO, name, settings);
if (!name || !id || !audio)
return NULL;
aoi = audio_output_getinfo(audio);
return create_encoder(id, OBS_ENCODER_AUDIO, name, settings, audio,
1, aoi->samples_per_sec);
} }
static void receive_video(void *param, struct video_data *frame); static void receive_video(void *param, struct video_data *frame);
...@@ -418,6 +402,30 @@ const char *obs_encoder_get_codec(obs_encoder_t encoder) ...@@ -418,6 +402,30 @@ const char *obs_encoder_get_codec(obs_encoder_t encoder)
return encoder ? encoder->info.codec : NULL; return encoder ? encoder->info.codec : NULL;
} }
void obs_encoder_set_video(obs_encoder_t encoder, video_t video)
{
const struct video_output_info *voi;
if (!video || !encoder || encoder->info.type != OBS_ENCODER_VIDEO)
return;
voi = video_output_getinfo(video);
encoder->media = video;
encoder->timebase_num = voi->fps_den;
encoder->timebase_den = voi->fps_num;
}
void obs_encoder_set_audio(obs_encoder_t encoder, audio_t audio)
{
if (!audio || !encoder || encoder->info.type != OBS_ENCODER_AUDIO)
return;
encoder->media = audio;
encoder->timebase_num = 1;
encoder->timebase_den = audio_output_samplerate(audio);
}
video_t obs_encoder_video(obs_encoder_t encoder) video_t obs_encoder_video(obs_encoder_t encoder)
{ {
return (encoder && encoder->info.type == OBS_ENCODER_VIDEO) ? return (encoder && encoder->info.type == OBS_ENCODER_VIDEO) ?
......
...@@ -322,6 +322,7 @@ struct obs_output { ...@@ -322,6 +322,7 @@ struct obs_output {
audio_t audio; audio_t audio;
obs_encoder_t video_encoder; obs_encoder_t video_encoder;
obs_encoder_t audio_encoder; obs_encoder_t audio_encoder;
obs_service_t service;
bool video_conversion_set; bool video_conversion_set;
bool audio_conversion_set; bool audio_conversion_set;
...@@ -404,4 +405,11 @@ extern void obs_encoder_remove_output(struct obs_encoder *encoder, ...@@ -404,4 +405,11 @@ extern void obs_encoder_remove_output(struct obs_encoder *encoder,
struct obs_service { struct obs_service {
struct obs_context_data context; struct obs_context_data context;
struct obs_service_info info; struct obs_service_info info;
bool active;
bool destroy;
struct obs_output *output;
}; };
void obs_service_activate(struct obs_service *service);
void obs_service_deactivate(struct obs_service *service, bool remove);
...@@ -63,7 +63,7 @@ int obs_load_module(const char *path) ...@@ -63,7 +63,7 @@ int obs_load_module(const char *path)
mod.module = os_dlopen(plugin_path); mod.module = os_dlopen(plugin_path);
bfree(plugin_path); bfree(plugin_path);
if (!mod.module) { if (!mod.module) {
blog(LOG_DEBUG, "Module '%s' not found", path); blog(LOG_WARNING, "Module '%s' not found", path);
return MODULE_FILE_NOT_FOUND; return MODULE_FILE_NOT_FOUND;
} }
......
...@@ -108,6 +108,8 @@ void obs_output_destroy(obs_output_t output) ...@@ -108,6 +108,8 @@ void obs_output_destroy(obs_output_t output)
if (output->valid && output->active) if (output->valid && output->active)
output->info.stop(output->context.data); output->info.stop(output->context.data);
if (output->service)
output->service->output = NULL;
free_packets(output); free_packets(output);
...@@ -283,6 +285,22 @@ obs_encoder_t obs_output_get_audio_encoder(obs_output_t output) ...@@ -283,6 +285,22 @@ obs_encoder_t obs_output_get_audio_encoder(obs_output_t output)
return output ? output->audio_encoder : NULL; return output ? output->audio_encoder : NULL;
} }
void obs_output_set_service(obs_output_t output, obs_service_t service)
{
if (!output || output->active || !service || service->active) return;
if (service->output)
service->output->service = NULL;
output->service = service;
service->output = output;
}
obs_service_t obs_output_get_service(obs_output_t output)
{
return output ? output->service : NULL;
}
void obs_output_set_video_conversion(obs_output_t output, void obs_output_set_video_conversion(obs_output_t output,
const struct video_scale_info *conversion) const struct video_scale_info *conversion)
{ {
...@@ -302,7 +320,7 @@ void obs_output_set_audio_conversion(obs_output_t output, ...@@ -302,7 +320,7 @@ void obs_output_set_audio_conversion(obs_output_t output,
} }
static bool can_begin_data_capture(struct obs_output *output, bool encoded, static bool can_begin_data_capture(struct obs_output *output, bool encoded,
bool has_video, bool has_audio) bool has_video, bool has_audio, bool has_service)
{ {
if (has_video) { if (has_video) {
if (encoded) { if (encoded) {
...@@ -324,6 +342,9 @@ static bool can_begin_data_capture(struct obs_output *output, bool encoded, ...@@ -324,6 +342,9 @@ static bool can_begin_data_capture(struct obs_output *output, bool encoded,
} }
} }
if (has_service && !output->service)
return false;
return true; return true;
} }
...@@ -480,7 +501,8 @@ static inline void signal_stop(struct obs_output *output, int code) ...@@ -480,7 +501,8 @@ static inline void signal_stop(struct obs_output *output, int code)
} }
static inline void convert_flags(struct obs_output *output, uint32_t flags, static inline void convert_flags(struct obs_output *output, uint32_t flags,
bool *encoded, bool *has_video, bool *has_audio) bool *encoded, bool *has_video, bool *has_audio,
bool *has_service)
{ {
*encoded = (output->info.flags & OBS_OUTPUT_ENCODED) != 0; *encoded = (output->info.flags & OBS_OUTPUT_ENCODED) != 0;
if (!flags) if (!flags)
...@@ -488,30 +510,34 @@ static inline void convert_flags(struct obs_output *output, uint32_t flags, ...@@ -488,30 +510,34 @@ static inline void convert_flags(struct obs_output *output, uint32_t flags,
else else
flags &= output->info.flags; flags &= output->info.flags;
*has_video = (flags & OBS_OUTPUT_VIDEO) != 0; *has_video = (flags & OBS_OUTPUT_VIDEO) != 0;
*has_audio = (flags & OBS_OUTPUT_AUDIO) != 0; *has_audio = (flags & OBS_OUTPUT_AUDIO) != 0;
*has_service = (flags & OBS_OUTPUT_SERVICE) != 0;
} }
bool obs_output_can_begin_data_capture(obs_output_t output, uint32_t flags) bool obs_output_can_begin_data_capture(obs_output_t output, uint32_t flags)
{ {
bool encoded, has_video, has_audio; bool encoded, has_video, has_audio, has_service;
if (!output) return false; if (!output) return false;
if (output->active) return false; if (output->active) return false;
convert_flags(output, flags, &encoded, &has_video, &has_audio); convert_flags(output, flags, &encoded, &has_video, &has_audio,
&has_service);
return can_begin_data_capture(output, encoded, has_video, has_audio); return can_begin_data_capture(output, encoded, has_video, has_audio,
has_service);
} }
bool obs_output_initialize_encoders(obs_output_t output, uint32_t flags) bool obs_output_initialize_encoders(obs_output_t output, uint32_t flags)
{ {
bool encoded, has_video, has_audio; bool encoded, has_video, has_audio, has_service;
if (!output) return false; if (!output) return false;
if (output->active) return false; if (output->active) return false;
convert_flags(output, flags, &encoded, &has_video, &has_audio); convert_flags(output, flags, &encoded, &has_video, &has_audio,
&has_service);
if (!encoded) if (!encoded)
return false; return false;
...@@ -532,17 +558,23 @@ bool obs_output_initialize_encoders(obs_output_t output, uint32_t flags) ...@@ -532,17 +558,23 @@ bool obs_output_initialize_encoders(obs_output_t output, uint32_t flags)
bool obs_output_begin_data_capture(obs_output_t output, uint32_t flags) bool obs_output_begin_data_capture(obs_output_t output, uint32_t flags)
{ {
bool encoded, has_video, has_audio; bool encoded, has_video, has_audio, has_service;
if (!output) return false; if (!output) return false;
if (output->active) return false; if (output->active) return false;
convert_flags(output, flags, &encoded, &has_video, &has_audio); convert_flags(output, flags, &encoded, &has_video, &has_audio,
&has_service);
if (!can_begin_data_capture(output, encoded, has_video, has_audio)) if (!can_begin_data_capture(output, encoded, has_video, has_audio,
has_service))
return false; return false;
hook_data_capture(output, encoded, has_video, has_audio); hook_data_capture(output, encoded, has_video, has_audio);
if (has_service)
obs_service_activate(output->service);
output->active = true; output->active = true;
signal_start(output); signal_start(output);
return true; return true;
...@@ -550,14 +582,15 @@ bool obs_output_begin_data_capture(obs_output_t output, uint32_t flags) ...@@ -550,14 +582,15 @@ bool obs_output_begin_data_capture(obs_output_t output, uint32_t flags)
void obs_output_end_data_capture(obs_output_t output) void obs_output_end_data_capture(obs_output_t output)
{ {
bool encoded, has_video, has_audio; bool encoded, has_video, has_audio, has_service;
void (*encoded_callback)(void *data, struct encoder_packet *packet); void (*encoded_callback)(void *data, struct encoder_packet *packet);
void *param; void *param;
if (!output) return; if (!output) return;
if (!output->active) return; if (!output->active) return;
convert_flags(output, 0, &encoded, &has_video, &has_audio); convert_flags(output, 0, &encoded, &has_video, &has_audio,
&has_service);
if (encoded) { if (encoded) {
encoded_callback = (has_video && has_audio) ? encoded_callback = (has_video && has_audio) ?
...@@ -582,6 +615,9 @@ void obs_output_end_data_capture(obs_output_t output) ...@@ -582,6 +615,9 @@ void obs_output_end_data_capture(obs_output_t output)
output->context.data); output->context.data);
} }
if (has_service)
obs_service_deactivate(output->service, false);
output->active = false; output->active = false;
} }
......
...@@ -53,6 +53,13 @@ obs_service_t obs_service_create(const char *id, const char *name, ...@@ -53,6 +53,13 @@ obs_service_t obs_service_create(const char *id, const char *name,
service->info = *info; service->info = *info;
service->context.data = service->info.create(service->context.settings,
service);
if (!service->context.data) {
obs_service_destroy(service);
return NULL;
}
obs_context_data_insert(&service->context, obs_context_data_insert(&service->context,
&obs->data.services_mutex, &obs->data.services_mutex,
&obs->data.first_service); &obs->data.first_service);
...@@ -60,19 +67,37 @@ obs_service_t obs_service_create(const char *id, const char *name, ...@@ -60,19 +67,37 @@ obs_service_t obs_service_create(const char *id, const char *name,
return service; return service;
} }
static void actually_destroy_service(struct obs_service *service)
{
if (service->context.data)
service->info.destroy(service->context.data);
if (service->output)
service->output->service = NULL;
obs_context_data_free(&service->context);
bfree(service);
}
void obs_service_destroy(obs_service_t service) void obs_service_destroy(obs_service_t service)
{ {
if (service) { if (service) {
obs_context_data_remove(&service->context); obs_context_data_remove(&service->context);
if (service->context.data) service->destroy = true;
service->info.destroy(service->context.data);
obs_context_data_free(&service->context); /* do NOT destroy the service until the service is no
bfree(service); * longer in use */
if (!service->active)
actually_destroy_service(service);
} }
} }
const char *obs_service_getname(obs_service_t service)
{
return service ? service->context.name : NULL;
}
static inline obs_data_t get_defaults(const struct obs_service_info *info) static inline obs_data_t get_defaults(const struct obs_service_info *info)
{ {
obs_data_t settings = obs_data_create(); obs_data_t settings = obs_data_create();
...@@ -115,6 +140,11 @@ obs_properties_t obs_service_properties(obs_service_t service, ...@@ -115,6 +140,11 @@ obs_properties_t obs_service_properties(obs_service_t service,
return NULL; return NULL;
} }
const char *obs_service_gettype(obs_service_t service)
{
return service ? service->info.id : NULL;
}
void obs_service_update(obs_service_t service, obs_data_t settings) void obs_service_update(obs_service_t service, obs_data_t settings)
{ {
if (!service) return; if (!service) return;
...@@ -156,3 +186,39 @@ const char *obs_service_get_key(obs_service_t service) ...@@ -156,3 +186,39 @@ const char *obs_service_get_key(obs_service_t service)
if (!service || !service->info.get_key) return NULL; if (!service || !service->info.get_key) return NULL;
return service->info.get_key(service->context.data); return service->info.get_key(service->context.data);
} }
const char *obs_service_get_username(obs_service_t service)
{
if (!service || !service->info.get_username) return NULL;
return service->info.get_username(service->context.data);
}
const char *obs_service_get_password(obs_service_t service)
{
if (!service || !service->info.get_password) return NULL;
return service->info.get_password(service->context.data);
}
void obs_service_activate(struct obs_service *service)
{
if (!service || !service->output || service->active) return;
if (service->info.activate)
service->info.activate(service->context.data,
service->context.settings);
service->active = true;
}
void obs_service_deactivate(struct obs_service *service, bool remove)
{
if (!service || !service->output || !service->active) return;
if (service->info.deactivate)
service->info.deactivate(service->context.data);
service->active = false;
if (service->destroy)
actually_destroy_service(service);
else if (remove)
service->output = NULL;
}
...@@ -26,6 +26,9 @@ struct obs_service_info { ...@@ -26,6 +26,9 @@ struct obs_service_info {
void (*destroy)(void *data); void (*destroy)(void *data);
/* optional */ /* optional */
void (*activate)(void *data, obs_data_t settings);
void (*deactivate)(void *data);
void (*update)(void *data, obs_data_t settings); void (*update)(void *data, obs_data_t settings);
void (*defaults)(obs_data_t settings); void (*defaults)(obs_data_t settings);
...@@ -35,6 +38,9 @@ struct obs_service_info { ...@@ -35,6 +38,9 @@ struct obs_service_info {
const char *(*get_url)(void *data); const char *(*get_url)(void *data);
const char *(*get_key)(void *data); const char *(*get_key)(void *data);
const char *(*get_username)(void *data);
const char *(*get_password)(void *data);
/* TODO: more stuff later */ /* TODO: more stuff later */
}; };
......
...@@ -908,6 +908,46 @@ obs_source_t obs_get_source_by_name(const char *name) ...@@ -908,6 +908,46 @@ obs_source_t obs_get_source_by_name(const char *name)
return source; return source;
} }
static inline void *get_context_by_name(void *vfirst, const char *name,
pthread_mutex_t *mutex)
{
struct obs_context_data **first = vfirst;
struct obs_context_data *context;
pthread_mutex_lock(mutex);
context = *first;
while (context) {
if (strcmp(context->name, name) == 0)
break;
context = context->next;
}
pthread_mutex_unlock(mutex);
return context;
}
obs_output_t obs_get_output_by_name(const char *name)
{
if (!obs) return NULL;
return get_context_by_name(&obs->data.first_output, name,
&obs->data.outputs_mutex);
}
obs_encoder_t obs_get_encoder_by_name(const char *name)
{
if (!obs) return NULL;
return get_context_by_name(&obs->data.first_encoder, name,
&obs->data.encoders_mutex);
}
obs_service_t obs_get_service_by_name(const char *name)
{
if (!obs) return NULL;
return get_context_by_name(&obs->data.first_service, name,
&obs->data.services_mutex);
}
effect_t obs_get_default_effect(void) effect_t obs_get_default_effect(void)
{ {
if (!obs) return NULL; if (!obs) return NULL;
......
...@@ -288,6 +288,10 @@ EXPORT void obs_enum_outputs(bool (*enum_proc)(void*, obs_output_t), ...@@ -288,6 +288,10 @@ EXPORT void obs_enum_outputs(bool (*enum_proc)(void*, obs_output_t),
EXPORT void obs_enum_encoders(bool (*enum_proc)(void*, obs_encoder_t), EXPORT void obs_enum_encoders(bool (*enum_proc)(void*, obs_encoder_t),
void *param); void *param);
/** Enumerates encoders */
EXPORT void obs_enum_services(bool (*enum_proc)(void*, obs_service_t),
void *param);
/** /**
* Gets a source by its name. * Gets a source by its name.
* *
...@@ -296,6 +300,15 @@ EXPORT void obs_enum_encoders(bool (*enum_proc)(void*, obs_encoder_t), ...@@ -296,6 +300,15 @@ EXPORT void obs_enum_encoders(bool (*enum_proc)(void*, obs_encoder_t),
*/ */
EXPORT obs_source_t obs_get_source_by_name(const char *name); EXPORT obs_source_t obs_get_source_by_name(const char *name);
/** Gets an output by its name. */
EXPORT obs_output_t obs_get_output_by_name(const char *name);
/** Gets an encoder by its name. */
EXPORT obs_encoder_t obs_get_encoder_by_name(const char *name);
/** Gets an service by its name. */
EXPORT obs_service_t obs_get_service_by_name(const char *name);
/** /**
* Returns the location of a plugin data file. * Returns the location of a plugin data file.
* *
...@@ -737,6 +750,12 @@ EXPORT obs_encoder_t obs_output_get_video_encoder(obs_output_t output); ...@@ -737,6 +750,12 @@ EXPORT obs_encoder_t obs_output_get_video_encoder(obs_output_t output);
/** Returns the current audio encoder associated with this output */ /** Returns the current audio encoder associated with this output */
EXPORT obs_encoder_t obs_output_get_audio_encoder(obs_output_t output); EXPORT obs_encoder_t obs_output_get_audio_encoder(obs_output_t output);
/** Sets the current service associated with this output. */
EXPORT void obs_output_set_service(obs_output_t output, obs_service_t service);
/** Gets the current service associated with this output. */
EXPORT obs_service_t obs_output_get_service(obs_output_t output);
/* ------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------- */
/* Functions used by outputs */ /* Functions used by outputs */
...@@ -793,11 +812,10 @@ EXPORT const char *obs_encoder_getdisplayname(const char *id, ...@@ -793,11 +812,10 @@ EXPORT const char *obs_encoder_getdisplayname(const char *id,
* @param id Video encoder ID * @param id Video encoder ID
* @param name Name to assign to this context * @param name Name to assign to this context
* @param settings Settings * @param settings Settings
* @param video Video output context to encode data from
* @return The video encoder context, or NULL if failed or not found. * @return The video encoder context, or NULL if failed or not found.
*/ */
EXPORT obs_encoder_t obs_video_encoder_create(const char *id, const char *name, EXPORT obs_encoder_t obs_video_encoder_create(const char *id, const char *name,
obs_data_t settings, video_t video); obs_data_t settings);
/** /**
* Creates an audio encoder context * Creates an audio encoder context
...@@ -805,11 +823,10 @@ EXPORT obs_encoder_t obs_video_encoder_create(const char *id, const char *name, ...@@ -805,11 +823,10 @@ EXPORT obs_encoder_t obs_video_encoder_create(const char *id, const char *name,
* @param id Audio Encoder ID * @param id Audio Encoder ID
* @param name Name to assign to this context * @param name Name to assign to this context
* @param settings Settings * @param settings Settings
* @param audio Audio output context to encode data from
* @return The video encoder context, or NULL if failed or not found. * @return The video encoder context, or NULL if failed or not found.
*/ */
EXPORT obs_encoder_t obs_audio_encoder_create(const char *id, const char *name, EXPORT obs_encoder_t obs_audio_encoder_create(const char *id, const char *name,
obs_data_t settings, audio_t audio); obs_data_t settings);
/** Destroys an encoder context */ /** Destroys an encoder context */
EXPORT void obs_encoder_destroy(obs_encoder_t encoder); EXPORT void obs_encoder_destroy(obs_encoder_t encoder);
...@@ -844,6 +861,12 @@ EXPORT bool obs_encoder_get_extra_data(obs_encoder_t encoder, ...@@ -844,6 +861,12 @@ EXPORT bool obs_encoder_get_extra_data(obs_encoder_t encoder,
/** Returns the current settings for this encoder */ /** Returns the current settings for this encoder */
EXPORT obs_data_t obs_encoder_get_settings(obs_encoder_t encoder); EXPORT obs_data_t obs_encoder_get_settings(obs_encoder_t encoder);
/** Sets the video output context to be used with this encoder */
EXPORT void obs_encoder_set_video(obs_encoder_t encoder, video_t video);
/** Sets the audio output context to be used with this encoder */
EXPORT void obs_encoder_set_audio(obs_encoder_t encoder, audio_t audio);
/** /**
* Returns the video output context used with this encoder, or NULL if not * Returns the video output context used with this encoder, or NULL if not
* a video context * a video context
...@@ -873,6 +896,8 @@ EXPORT obs_service_t obs_service_create(const char *id, const char *name, ...@@ -873,6 +896,8 @@ EXPORT obs_service_t obs_service_create(const char *id, const char *name,
obs_data_t settings); obs_data_t settings);
EXPORT void obs_service_destroy(obs_service_t service); EXPORT void obs_service_destroy(obs_service_t service);
EXPORT const char *obs_service_getname(obs_service_t service);
/** Gets the default settings for a service */ /** Gets the default settings for a service */
EXPORT obs_data_t obs_service_defaults(const char *id); EXPORT obs_data_t obs_service_defaults(const char *id);
...@@ -887,11 +912,26 @@ EXPORT obs_properties_t obs_get_service_properties(const char *id, ...@@ -887,11 +912,26 @@ EXPORT obs_properties_t obs_get_service_properties(const char *id,
EXPORT obs_properties_t obs_service_properties(obs_service_t service, EXPORT obs_properties_t obs_service_properties(obs_service_t service,
const char *locale); const char *locale);
/** Gets the service type */
EXPORT const char *obs_service_gettype(obs_service_t service);
/** Updates the settings of the service context */
EXPORT void obs_service_update(obs_service_t service, obs_data_t settings);
/** Returns the current settings for this service */
EXPORT obs_data_t obs_service_get_settings(obs_service_t service);
/** Returns the URL for this service context */ /** Returns the URL for this service context */
const char *obs_service_get_url(obs_service_t service); EXPORT const char *obs_service_get_url(obs_service_t service);
/** Returns the stream key (if any) for this service context */ /** Returns the stream key (if any) for this service context */
const char *obs_service_get_key(obs_service_t service); EXPORT const char *obs_service_get_key(obs_service_t service);
/** Returns the username (if any) for this service context */
EXPORT const char *obs_service_get_username(obs_service_t service);
/** Returns the password (if any) for this service context */
EXPORT const char *obs_service_get_password(obs_service_t service);
/* ------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------- */
......
...@@ -359,8 +359,6 @@ static inline void darray_pop_back(const size_t element_size, ...@@ -359,8 +359,6 @@ static inline void darray_pop_back(const size_t element_size,
static inline void darray_join(const size_t element_size, struct darray *dst, static inline void darray_join(const size_t element_size, struct darray *dst,
struct darray *da) struct darray *da)
{ {
assert(element_size == element_size);
darray_push_back_darray(element_size, dst, da); darray_push_back_darray(element_size, dst, da);
darray_free(da); darray_free(da);
} }
......
...@@ -6,8 +6,8 @@ ...@@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>841</width> <width>1072</width>
<height>479</height> <height>441</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
......
...@@ -47,7 +47,16 @@ ...@@ -47,7 +47,16 @@
</item> </item>
<item> <item>
<property name="text"> <property name="text">
<string>Outputs</string> <string>Stream</string>
</property>
<property name="icon">
<iconset resource="obs.qrc">
<normaloff>:/settings/images/settings/network.png</normaloff>:/settings/images/settings/network.png</iconset>
</property>
</item>
<item>
<property name="text">
<string>Output</string>
</property> </property>
<property name="icon"> <property name="icon">
<iconset resource="obs.qrc"> <iconset resource="obs.qrc">
...@@ -118,95 +127,337 @@ ...@@ -118,95 +127,337 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="outputsPage"> <widget class="QWidget" name="streamPage">
<layout class="QFormLayout" name="formLayout_5"> <layout class="QVBoxLayout" name="verticalLayout_5">
<property name="fieldGrowthPolicy"> <property name="leftMargin">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum> <number>0</number>
</property> </property>
<property name="labelAlignment"> <property name="topMargin">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> <number>0</number>
</property> </property>
<item row="0" column="1"> <property name="rightMargin">
<widget class="QLabel" name="label_16"> <number>0</number>
<property name="text"> </property>
<string>NOTE: This is a test, just some temporary user interface</string> <property name="bottomMargin">
</property> <number>0</number>
<property name="wordWrap"> </property>
<bool>true</bool> <item>
</property> <widget class="QWidget" name="streamContainer" native="true">
</widget> <property name="sizePolicy">
</item> <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<item row="2" column="0"> <horstretch>0</horstretch>
<widget class="QLabel" name="label_17"> <verstretch>0</verstretch>
<property name="minimumSize"> </sizepolicy>
<size>
<width>170</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Video Bitrate:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_18">
<property name="text">
<string>URL/Filename:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="streamURL"/>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_19">
<property name="text">
<string>Stream Key:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="streamKey">
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_6">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item alignment="Qt::AlignTop">
<widget class="QWidget" name="widget_5" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="verticalLayout_7">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QWidget" name="widget_6" native="true">
<layout class="QFormLayout" name="formLayout_8">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_21">
<property name="minimumSize">
<size>
<width>170</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Stream Type</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="streamType"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget> </widget>
</item> </item>
<item row="2" column="1"> </layout>
<widget class="QSpinBox" name="streamVBitrate"> </widget>
<property name="minimum"> <widget class="QWidget" name="outputPage">
<number>100</number> <layout class="QVBoxLayout" name="verticalLayout_2">
</property> <property name="leftMargin">
<property name="maximum"> <number>0</number>
<number>60000</number> </property>
</property> <property name="topMargin">
<property name="value"> <number>0</number>
<number>2500</number> </property>
</property> <property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item alignment="Qt::AlignTop">
<widget class="QWidget" name="widget" native="true">
<layout class="QFormLayout" name="formLayout_5">
<item row="0" column="0">
<widget class="QLabel" name="label_16">
<property name="minimumSize">
<size>
<width>170</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Mode</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="outputMode">
<property name="enabled">
<bool>false</bool>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<item>
<property name="text">
<string>Simple</string>
</property>
</item>
<item>
<property name="text">
<string>Custom</string>
</property>
</item>
</widget>
</item>
</layout>
</widget> </widget>
</item> </item>
<item row="1" column="1"> <item>
<widget class="QSpinBox" name="streamABitrate"> <widget class="Line" name="line_2">
<property name="minimum"> <property name="orientation">
<number>24</number> <enum>Qt::Horizontal</enum>
</property>
<property name="maximum">
<number>320</number>
</property>
<property name="value">
<number>128</number>
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="0"> <item>
<widget class="QLabel" name="label_20"> <widget class="QStackedWidget" name="outputModePages">
<property name="text"> <property name="currentIndex">
<string>Audio Bitrate:</string> <number>0</number>
</property> </property>
<widget class="QWidget" name="easyOutputsPage">
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item alignment="Qt::AlignTop">
<widget class="QWidget" name="simpleOutputContainer" native="true">
<layout class="QVBoxLayout" name="verticalLayout_4">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item alignment="Qt::AlignTop">
<widget class="QWidget" name="widget_2" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QFormLayout" name="formLayout_6">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<property name="labelAlignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_18">
<property name="minimumSize">
<size>
<width>170</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Save Path</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLineEdit" name="simpleOutputPath">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="simpleOutputBrowse">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Browse</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_19">
<property name="text">
<string>Video Bitrate</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="simpleOutputVBitrate">
<property name="minimum">
<number>200</number>
</property>
<property name="maximum">
<number>16000</number>
</property>
<property name="value">
<number>2000</number>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_20">
<property name="text">
<string>Audio Bitrate</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="simpleOutputABitrate">
<property name="currentIndex">
<number>4</number>
</property>
<item>
<property name="text">
<string>32</string>
</property>
</item>
<item>
<property name="text">
<string>64</string>
</property>
</item>
<item>
<property name="text">
<string>96</string>
</property>
</item>
<item>
<property name="text">
<string>128</string>
</property>
</item>
<item>
<property name="text">
<string>160</string>
</property>
</item>
<item>
<property name="text">
<string>192</string>
</property>
</item>
<item>
<property name="text">
<string>256</string>
</property>
</item>
<item>
<property name="text">
<string>320</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="customOutputsPage"/>
</widget> </widget>
</item> </item>
</layout> </layout>
...@@ -721,12 +972,28 @@ ...@@ -721,12 +972,28 @@
<slot>setCurrentIndex(int)</slot> <slot>setCurrentIndex(int)</slot>
<hints> <hints>
<hint type="sourcelabel"> <hint type="sourcelabel">
<x>286</x> <x>329</x>
<y>155</y> <y>168</y>
</hint>
<hint type="destinationlabel">
<x>577</x>
<y>183</y>
</hint>
</hints>
</connection>
<connection>
<sender>outputMode</sender>
<signal>currentIndexChanged(int)</signal>
<receiver>outputModePages</receiver>
<slot>setCurrentIndex(int)</slot>
<hints>
<hint type="sourcelabel">
<x>379</x>
<y>30</y>
</hint> </hint>
<hint type="destinationlabel"> <hint type="destinationlabel">
<x>340</x> <x>294</x>
<y>154</y> <y>427</y>
</hint> </hint>
</hints> </hints>
</connection> </connection>
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
<file>images/up.ico</file> <file>images/up.ico</file>
</qresource> </qresource>
<qresource prefix="settings"> <qresource prefix="settings">
<file>images/settings/network.png</file>
<file>images/settings/video-display-3.png</file> <file>images/settings/video-display-3.png</file>
<file>images/settings/decibel_audio_player.png</file> <file>images/settings/decibel_audio_player.png</file>
<file>images/settings/applications-system-2.png</file> <file>images/settings/applications-system-2.png</file>
......
...@@ -47,7 +47,8 @@ Q_DECLARE_METATYPE(OBSSceneItem); ...@@ -47,7 +47,8 @@ Q_DECLARE_METATYPE(OBSSceneItem);
OBSBasic::OBSBasic(QWidget *parent) OBSBasic::OBSBasic(QWidget *parent)
: OBSMainWindow (parent), : OBSMainWindow (parent),
outputTest (nullptr), streamOutput (nullptr),
service (nullptr),
aac (nullptr), aac (nullptr),
x264 (nullptr), x264 (nullptr),
sceneChanging (false), sceneChanging (false),
...@@ -84,6 +85,111 @@ static inline bool HasAudioDevices(const char *source_id) ...@@ -84,6 +85,111 @@ static inline bool HasAudioDevices(const char *source_id)
return count != 0; return count != 0;
} }
static void OBSStartStreaming(void *data, calldata_t params)
{
UNUSED_PARAMETER(params);
QMetaObject::invokeMethod(static_cast<OBSBasic*>(data),
"StreamingStart");
}
static void OBSStopStreaming(void *data, calldata_t params)
{
int code = (int)calldata_int(params, "errorcode");
QMetaObject::invokeMethod(static_cast<OBSBasic*>(data),
"StreamingStop", Q_ARG(int, code));
}
#define SERVICE_PATH "obs-studio/basic/service.json"
void OBSBasic::SaveService()
{
if (!service)
return;
BPtr<char> serviceJsonPath(os_get_config_path(SERVICE_PATH));
if (!serviceJsonPath)
return;
obs_data_t data = obs_data_create();
obs_data_t settings = obs_service_get_settings(service);
obs_data_setstring(data, "type", obs_service_gettype(service));
obs_data_setobj(data, "settings", settings);
const char *json = obs_data_getjson(data);
os_quick_write_utf8_file(serviceJsonPath, json, strlen(json), false);
obs_data_release(settings);
obs_data_release(data);
}
bool OBSBasic::LoadService()
{
const char *type;
BPtr<char> serviceJsonPath(os_get_config_path(SERVICE_PATH));
if (!serviceJsonPath)
return false;
BPtr<char> jsonText = os_quick_read_utf8_file(serviceJsonPath);
if (!jsonText)
return false;
obs_data_t data = obs_data_create_from_json(jsonText);
obs_data_set_default_string(data, "type", "rtmp_common");
type = obs_data_getstring(data, "type");
obs_data_t settings = obs_data_getobj(data, "settings");
service = obs_service_create(type, "default", settings);
obs_data_release(settings);
obs_data_release(data);
return !!service;
}
bool OBSBasic::InitOutputs()
{
streamOutput = obs_output_create("rtmp_output", "default", nullptr);
if (!streamOutput)
return false;
signal_handler_connect(obs_output_signalhandler(streamOutput),
"start", OBSStartStreaming, this);
signal_handler_connect(obs_output_signalhandler(streamOutput),
"stop", OBSStopStreaming, this);
return true;
}
bool OBSBasic::InitEncoders()
{
aac = obs_audio_encoder_create("ffmpeg_aac", "aac", nullptr);
if (!aac)
return false;
x264 = obs_video_encoder_create("obs_x264", "h264", nullptr);
if (!x264)
return false;
return true;
}
bool OBSBasic::InitService()
{
if (LoadService())
return true;
service = obs_service_create("rtmp_common", nullptr, nullptr);
if (!service)
return false;
return true;
}
bool OBSBasic::InitBasicConfigDefaults() bool OBSBasic::InitBasicConfigDefaults()
{ {
bool hasDesktopAudio = HasAudioDevices(App()->OutputAudioSource()); bool hasDesktopAudio = HasAudioDevices(App()->OutputAudioSource());
...@@ -107,10 +213,10 @@ bool OBSBasic::InitBasicConfigDefaults() ...@@ -107,10 +213,10 @@ bool OBSBasic::InitBasicConfigDefaults()
uint32_t cy = monitors[0].cy; uint32_t cy = monitors[0].cy;
/* TODO: temporary */ /* TODO: temporary */
config_set_default_string(basicConfig, "OutputTemp", "URL", ""); config_set_default_string(basicConfig, "SimpleOutput", "path", "");
config_set_default_string(basicConfig, "OutputTemp", "Key", ""); config_set_default_uint (basicConfig, "SimpleOutput", "VBitrate",
config_set_default_uint (basicConfig, "OutputTemp", "VBitrate", 2500); 2500);
config_set_default_uint (basicConfig, "OutputTemp", "ABitrate", 128); config_set_default_uint (basicConfig, "SimpleOutput", "ABitrate", 128);
config_set_default_uint (basicConfig, "Video", "BaseCX", cx); config_set_default_uint (basicConfig, "Video", "BaseCX", cx);
config_set_default_uint (basicConfig, "Video", "BaseCY", cy); config_set_default_uint (basicConfig, "Video", "BaseCY", cy);
...@@ -186,6 +292,7 @@ void OBSBasic::OBSInit() ...@@ -186,6 +292,7 @@ void OBSBasic::OBSInit()
obs_load_module("obs-ffmpeg"); obs_load_module("obs-ffmpeg");
obs_load_module("obs-x264"); obs_load_module("obs-x264");
obs_load_module("obs-outputs"); obs_load_module("obs-outputs");
obs_load_module("rtmp-services");
#ifdef __APPLE__ #ifdef __APPLE__
obs_load_module("mac-capture"); obs_load_module("mac-capture");
#elif _WIN32 #elif _WIN32
...@@ -196,11 +303,20 @@ void OBSBasic::OBSInit() ...@@ -196,11 +303,20 @@ void OBSBasic::OBSInit()
obs_load_module("linux-pulseaudio"); obs_load_module("linux-pulseaudio");
#endif #endif
if (!InitOutputs())
throw "Failed to initialize outputs";
if (!InitEncoders())
throw "Failed to initialize encoders";
if (!InitService())
throw "Failed to initialize service";
ResetAudioDevices(); ResetAudioDevices();
} }
OBSBasic::~OBSBasic() OBSBasic::~OBSBasic()
{ {
SaveService();
if (properties) if (properties)
delete properties; delete properties;
...@@ -431,6 +547,22 @@ void OBSBasic::RenderMain(void *data, uint32_t cx, uint32_t cy) ...@@ -431,6 +547,22 @@ void OBSBasic::RenderMain(void *data, uint32_t cx, uint32_t cy)
/* Main class functions */ /* Main class functions */
obs_service_t OBSBasic::GetService()
{
if (!service)
service = obs_service_create("rtmp_common", NULL, NULL);
return service;
}
void OBSBasic::SetService(obs_service_t newService)
{
if (newService) {
if (service)
obs_service_destroy(service);
service = newService;
}
}
bool OBSBasic::ResetVideo() bool OBSBasic::ResetVideo()
{ {
struct obs_video_info ovi; struct obs_video_info ovi;
...@@ -446,7 +578,6 @@ bool OBSBasic::ResetVideo() ...@@ -446,7 +578,6 @@ bool OBSBasic::ResetVideo()
"Video", "OutputCX"); "Video", "OutputCX");
ovi.output_height = (uint32_t)config_get_uint(basicConfig, ovi.output_height = (uint32_t)config_get_uint(basicConfig,
"Video", "OutputCY"); "Video", "OutputCY");
//ovi.output_format = VIDEO_FORMAT_I420;
ovi.output_format = VIDEO_FORMAT_NV12; ovi.output_format = VIDEO_FORMAT_NV12;
ovi.adapter = 0; ovi.adapter = 0;
ovi.gpu_conversion = true; ovi.gpu_conversion = true;
...@@ -821,116 +952,50 @@ void OBSBasic::on_actionSourceDown_triggered() ...@@ -821,116 +952,50 @@ void OBSBasic::on_actionSourceDown_triggered()
{ {
} }
void OBSBasic::OutputStop(int errorcode) void OBSBasic::StreamingStart()
{
UNUSED_PARAMETER(errorcode);
ui->streamButton->setText("Start Streaming");
}
void OBSBasic::OutputStart()
{ {
ui->streamButton->setText("Stop Streaming"); ui->streamButton->setText("Stop Streaming");
} }
static void OBSOutputStart(void *data, calldata_t params) void OBSBasic::StreamingStop(int errorcode)
{ {
UNUSED_PARAMETER(params); UNUSED_PARAMETER(errorcode);
QMetaObject::invokeMethod(static_cast<OBSBasic*>(data), "OutputStart"); ui->streamButton->setText("Start Streaming");
}
static void OBSOutputStop(void *data, calldata_t params)
{
int code = (int)calldata_int(params, "errorcode");
QMetaObject::invokeMethod(static_cast<OBSBasic*>(data), "OutputStop",
Q_ARG(int, code));
}
void OBSBasic::TempFileOutput(const char *path, int vBitrate, int aBitrate)
{
obs_data_t data = obs_data_create();
obs_data_setstring(data, "filename", path);
obs_data_setint(data, "audio_bitrate", aBitrate);
obs_data_setint(data, "video_bitrate", vBitrate);
outputTest = obs_output_create("ffmpeg_output", "test", data);
obs_data_release(data);
}
void OBSBasic::TempStreamOutput(const char *url, const char *key,
int vBitrate, int aBitrate)
{
obs_data_t aac_settings = obs_data_create();
obs_data_t x264_settings = obs_data_create();
obs_data_t output_settings = obs_data_create();
stringstream ss;
ss << "filler=1:crf=0:bitrate=" << vBitrate;
obs_data_setint(aac_settings, "bitrate", aBitrate);
obs_data_setint(x264_settings, "bitrate", vBitrate);
obs_data_setint(x264_settings, "buffer_size", vBitrate);
obs_data_setint(x264_settings, "keyint_sec", 2);
obs_data_setstring(x264_settings, "x264opts", ss.str().c_str());
obs_data_setstring(output_settings, "path", url);
obs_data_setstring(output_settings, "key", key);
aac = obs_audio_encoder_create("ffmpeg_aac", "blabla1",
aac_settings, obs_audio());
x264 = obs_video_encoder_create("obs_x264", "blabla2",
x264_settings, obs_video());
outputTest = obs_output_create("rtmp_output", "test", output_settings);
obs_output_set_video_encoder(outputTest, x264);
obs_output_set_audio_encoder(outputTest, aac);
obs_data_release(aac_settings);
obs_data_release(x264_settings);
obs_data_release(output_settings);
} }
/* TODO: lots of temporary code */
void OBSBasic::on_streamButton_clicked() void OBSBasic::on_streamButton_clicked()
{ {
if (obs_output_active(outputTest)) { if (obs_output_active(streamOutput)) {
obs_output_stop(outputTest); obs_output_stop(streamOutput);
} else { } else {
const char *url = config_get_string(basicConfig, "OutputTemp", obs_data_t x264Settings = obs_data_create();
"URL"); obs_data_t aacSettings = obs_data_create();
const char *key = config_get_string(basicConfig, "OutputTemp",
"Key"); int videoBitrate = config_get_uint(basicConfig, "SimpleOutput",
int vBitrate = config_get_uint(basicConfig, "OutputTemp",
"VBitrate"); "VBitrate");
int aBitrate = config_get_uint(basicConfig, "OutputTemp", int audioBitrate = config_get_uint(basicConfig, "SimpleOutput",
"ABitrate"); "ABitrate");
if (!url) SaveService();
return;
obs_output_destroy(outputTest); obs_data_setint(x264Settings, "bitrate", videoBitrate);
obs_encoder_destroy(aac); obs_data_setbool(x264Settings, "cbr", true);
obs_encoder_destroy(x264);
outputTest = nullptr;
aac = nullptr;
x264 = nullptr;
if (strstr(url, "rtmp://") != NULL) obs_data_setint(aacSettings, "bitrate", audioBitrate);
TempStreamOutput(url, key, vBitrate, aBitrate);
else
TempFileOutput(url, vBitrate, aBitrate);
if (!outputTest) { obs_encoder_update(x264, x264Settings);
OutputStop(OBS_OUTPUT_FAIL); obs_encoder_update(aac, aacSettings);
return;
}
signal_handler_connect(obs_output_signalhandler(outputTest), obs_data_release(x264Settings);
"start", OBSOutputStart, this); obs_data_release(aacSettings);
signal_handler_connect(obs_output_signalhandler(outputTest),
"stop", OBSOutputStop, this);
obs_output_start(outputTest); obs_encoder_set_video(x264, obs_video());
obs_encoder_set_audio(aac, obs_audio());
obs_output_set_video_encoder(streamOutput, x264);
obs_output_set_audio_encoder(streamOutput, aac);
obs_output_set_service(streamOutput, service);
obs_output_start(streamOutput);
} }
} }
......
...@@ -36,31 +36,41 @@ class OBSBasic : public OBSMainWindow { ...@@ -36,31 +36,41 @@ class OBSBasic : public OBSMainWindow {
private: private:
std::unordered_map<obs_source_t, int> sourceSceneRefs; std::unordered_map<obs_source_t, int> sourceSceneRefs;
obs_output_t outputTest;
obs_output_t streamOutput;
obs_service_t service;
obs_encoder_t aac; obs_encoder_t aac;
obs_encoder_t x264; obs_encoder_t x264;
bool sceneChanging;
int previewX, previewY; bool sceneChanging;
float previewScale;
int resizeTimer; int previewX, previewY;
float previewScale;
int resizeTimer;
ConfigFile basicConfig; ConfigFile basicConfig;
QPointer<OBSBasicProperties> properties; QPointer<OBSBasicProperties> properties;
void SaveService();
bool LoadService();
bool InitOutputs();
bool InitEncoders();
bool InitService();
bool InitBasicConfigDefaults();
bool InitBasicConfig();
OBSScene GetCurrentScene();
OBSSceneItem GetCurrentSceneItem();
void GetFPSCommon(uint32_t &num, uint32_t &den) const; void GetFPSCommon(uint32_t &num, uint32_t &den) const;
void GetFPSInteger(uint32_t &num, uint32_t &den) const; void GetFPSInteger(uint32_t &num, uint32_t &den) const;
void GetFPSFraction(uint32_t &num, uint32_t &den) const; void GetFPSFraction(uint32_t &num, uint32_t &den) const;
void GetFPSNanoseconds(uint32_t &num, uint32_t &den) const; void GetFPSNanoseconds(uint32_t &num, uint32_t &den) const;
void GetConfigFPS(uint32_t &num, uint32_t &den) const; void GetConfigFPS(uint32_t &num, uint32_t &den) const;
bool InitBasicConfigDefaults();
bool InitBasicConfig();
OBSScene GetCurrentScene();
OBSSceneItem GetCurrentSceneItem();
void UpdateSources(OBSScene scene); void UpdateSources(OBSScene scene);
void InsertSceneItem(obs_sceneitem_t item); void InsertSceneItem(obs_sceneitem_t item);
...@@ -69,8 +79,8 @@ private: ...@@ -69,8 +79,8 @@ private:
int vBitrate, int aBitrate); int vBitrate, int aBitrate);
public slots: public slots:
void OutputStart(); void StreamingStart();
void OutputStop(int errorcode); void StreamingStop(int errorcode);
private slots: private slots:
void AddSceneItem(OBSSceneItem item); void AddSceneItem(OBSSceneItem item);
...@@ -94,6 +104,9 @@ private: ...@@ -94,6 +104,9 @@ private:
void AddSourcePopupMenu(const QPoint &pos); void AddSourcePopupMenu(const QPoint &pos);
public: public:
obs_service_t GetService();
void SetService(obs_service_t service);
bool ResetVideo(); bool ResetVideo();
bool ResetAudio(); bool ResetAudio();
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include "obs-app.hpp" #include "obs-app.hpp"
#include "platform.hpp" #include "platform.hpp"
#include "properties-view.hpp"
#include "qt-wrappers.hpp" #include "qt-wrappers.hpp"
#include "window-basic-main.hpp" #include "window-basic-main.hpp"
#include "window-basic-settings.hpp" #include "window-basic-settings.hpp"
...@@ -78,6 +79,20 @@ static bool ConvertResText(const char *res, uint32_t &cx, uint32_t &cy) ...@@ -78,6 +79,20 @@ static bool ConvertResText(const char *res, uint32_t &cx, uint32_t &cy)
return true; return true;
} }
static inline void SetComboByName(QComboBox *combo, const char *name)
{
int idx = combo->findText(QT_UTF8(name));
if (idx != -1)
combo->setCurrentIndex(idx);
}
static inline void SetComboByValue(QComboBox *combo, const char *name)
{
int idx = combo->findData(QT_UTF8(name));
if (idx != -1)
combo->setCurrentIndex(idx);
}
void OBSBasicSettings::HookWidget(QWidget *widget, const char *signal, void OBSBasicSettings::HookWidget(QWidget *widget, const char *signal,
const char *slot) const char *slot)
{ {
...@@ -99,15 +114,16 @@ void OBSBasicSettings::HookWidget(QWidget *widget, const char *signal, ...@@ -99,15 +114,16 @@ void OBSBasicSettings::HookWidget(QWidget *widget, const char *signal,
#define VIDEO_CHANGED SLOT(VideoChanged()) #define VIDEO_CHANGED SLOT(VideoChanged())
OBSBasicSettings::OBSBasicSettings(QWidget *parent) OBSBasicSettings::OBSBasicSettings(QWidget *parent)
: QDialog (parent), : QDialog (parent),
main (qobject_cast<OBSBasic*>(parent)), main (qobject_cast<OBSBasic*>(parent)),
ui (new Ui::OBSBasicSettings), ui (new Ui::OBSBasicSettings),
generalChanged (false), generalChanged (false),
outputsChanged (false), outputsChanged (false),
audioChanged (false), audioChanged (false),
videoChanged (false), videoChanged (false),
pageIndex (0), pageIndex (0),
loading (true) loading (true),
streamProperties (nullptr)
{ {
string path; string path;
...@@ -118,33 +134,74 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent) ...@@ -118,33 +134,74 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent)
if (localeIni.Open(path.c_str(), CONFIG_OPEN_EXISTING) != 0) if (localeIni.Open(path.c_str(), CONFIG_OPEN_EXISTING) != 0)
throw "Could not open locale.ini"; throw "Could not open locale.ini";
HookWidget(ui->language, COMBO_CHANGED, GENERAL_CHANGED); HookWidget(ui->language, COMBO_CHANGED, GENERAL_CHANGED);
HookWidget(ui->streamVBitrate, SCROLL_CHANGED, OUTPUTS_CHANGED); HookWidget(ui->outputMode, COMBO_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->streamABitrate, SCROLL_CHANGED, OUTPUTS_CHANGED); HookWidget(ui->simpleOutputPath, EDIT_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->streamURL, EDIT_CHANGED, OUTPUTS_CHANGED); HookWidget(ui->simpleOutputVBitrate, SCROLL_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->streamKey, EDIT_CHANGED, OUTPUTS_CHANGED); HookWidget(ui->simpleOutputABitrate, COMBO_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->channelSetup, COMBO_CHANGED, AUDIO_RESTART); HookWidget(ui->channelSetup, COMBO_CHANGED, AUDIO_RESTART);
HookWidget(ui->sampleRate, COMBO_CHANGED, AUDIO_RESTART); HookWidget(ui->sampleRate, COMBO_CHANGED, AUDIO_RESTART);
HookWidget(ui->desktopAudioDevice1, COMBO_CHANGED, AUDIO_CHANGED); HookWidget(ui->desktopAudioDevice1, COMBO_CHANGED, AUDIO_CHANGED);
HookWidget(ui->desktopAudioDevice2, COMBO_CHANGED, AUDIO_CHANGED); HookWidget(ui->desktopAudioDevice2, COMBO_CHANGED, AUDIO_CHANGED);
HookWidget(ui->auxAudioDevice1, COMBO_CHANGED, AUDIO_CHANGED); HookWidget(ui->auxAudioDevice1, COMBO_CHANGED, AUDIO_CHANGED);
HookWidget(ui->auxAudioDevice2, COMBO_CHANGED, AUDIO_CHANGED); HookWidget(ui->auxAudioDevice2, COMBO_CHANGED, AUDIO_CHANGED);
HookWidget(ui->auxAudioDevice3, COMBO_CHANGED, AUDIO_CHANGED); HookWidget(ui->auxAudioDevice3, COMBO_CHANGED, AUDIO_CHANGED);
HookWidget(ui->renderer, COMBO_CHANGED, VIDEO_RESTART); HookWidget(ui->renderer, COMBO_CHANGED, VIDEO_RESTART);
HookWidget(ui->adapter, COMBO_CHANGED, VIDEO_RESTART); HookWidget(ui->adapter, COMBO_CHANGED, VIDEO_RESTART);
HookWidget(ui->baseResolution, CBEDIT_CHANGED, VIDEO_RES); HookWidget(ui->baseResolution, CBEDIT_CHANGED, VIDEO_RES);
HookWidget(ui->outputResolution, CBEDIT_CHANGED, VIDEO_RES); HookWidget(ui->outputResolution, CBEDIT_CHANGED, VIDEO_RES);
HookWidget(ui->downscaleFilter, COMBO_CHANGED, VIDEO_CHANGED); HookWidget(ui->downscaleFilter, COMBO_CHANGED, VIDEO_CHANGED);
HookWidget(ui->fpsType, COMBO_CHANGED, VIDEO_CHANGED); HookWidget(ui->fpsType, COMBO_CHANGED, VIDEO_CHANGED);
HookWidget(ui->fpsCommon, COMBO_CHANGED, VIDEO_CHANGED); HookWidget(ui->fpsCommon, COMBO_CHANGED, VIDEO_CHANGED);
HookWidget(ui->fpsInteger, SCROLL_CHANGED, VIDEO_CHANGED); HookWidget(ui->fpsInteger, SCROLL_CHANGED, VIDEO_CHANGED);
HookWidget(ui->fpsInteger, SCROLL_CHANGED, VIDEO_CHANGED); HookWidget(ui->fpsInteger, SCROLL_CHANGED, VIDEO_CHANGED);
HookWidget(ui->fpsNumerator, SCROLL_CHANGED, VIDEO_CHANGED); HookWidget(ui->fpsNumerator, SCROLL_CHANGED, VIDEO_CHANGED);
HookWidget(ui->fpsDenominator, SCROLL_CHANGED, VIDEO_CHANGED); HookWidget(ui->fpsDenominator, SCROLL_CHANGED, VIDEO_CHANGED);
LoadServiceTypes();
LoadServiceInfo();
LoadSettings(false); LoadSettings(false);
} }
void OBSBasicSettings::LoadServiceTypes()
{
const char *type;
size_t idx = 0;
while (obs_enum_service_types(idx++, &type)) {
const char *name = obs_service_getdisplayname(type,
App()->GetLocale());
QString qName = QT_UTF8(name);
QString qType = QT_UTF8(type);
ui->streamType->addItem(qName, qType);
}
type = obs_service_gettype(main->GetService());
SetComboByValue(ui->streamType, type);
}
void OBSBasicSettings::LoadServiceInfo()
{
QLayout *layout = ui->streamContainer->layout();
obs_service_t service = main->GetService();
obs_data_t settings = obs_service_get_settings(service);
obs_properties_t properties = obs_service_properties(service,
App()->GetLocale());
delete streamProperties;
streamProperties = new OBSPropertiesView(
settings,
properties,
service,
(PropertiesUpdateCallback)obs_service_update,
170);
layout->addWidget(streamProperties);
obs_data_release(settings);
}
void OBSBasicSettings::LoadLanguageList() void OBSBasicSettings::LoadLanguageList()
{ {
const char *currentLang = config_get_string(GetGlobalConfig(), const char *currentLang = config_get_string(GetGlobalConfig(),
...@@ -316,23 +373,27 @@ void OBSBasicSettings::LoadVideoSettings() ...@@ -316,23 +373,27 @@ void OBSBasicSettings::LoadVideoSettings()
loading = false; loading = false;
} }
void OBSBasicSettings::LoadOutputSettings() void OBSBasicSettings::LoadSimpleOutputSettings()
{ {
loading = true; const char *path = config_get_string(main->Config(), "SimpleOutput",
"path");
const char *url = config_get_string(main->Config(), "OutputTemp", int videoBitrate = config_get_uint(main->Config(), "SimpleOutput",
"URL");
const char *key = config_get_string(main->Config(), "OutputTemp",
"Key");
int videoBitrate = config_get_uint(main->Config(), "OutputTemp",
"VBitrate"); "VBitrate");
int audioBitrate = config_get_uint(main->Config(), "OutputTemp", int audioBitrate = config_get_uint(main->Config(), "SimpleOutput",
"ABitrate"); "ABitrate");
ui->streamURL->setText(QT_UTF8(url)); ui->simpleOutputPath->setText(path);
ui->streamKey->setText(QT_UTF8(key)); ui->simpleOutputVBitrate->setValue(videoBitrate);
ui->streamVBitrate->setValue(videoBitrate);
ui->streamABitrate->setValue(audioBitrate); SetComboByName(ui->simpleOutputABitrate,
std::to_string(audioBitrate).c_str());
}
void OBSBasicSettings::LoadOutputSettings()
{
loading = true;
LoadSimpleOutputSettings();
loading = false; loading = false;
} }
...@@ -492,15 +553,16 @@ void OBSBasicSettings::SaveVideoSettings() ...@@ -492,15 +553,16 @@ void OBSBasicSettings::SaveVideoSettings()
/* TODO: Temporary! */ /* TODO: Temporary! */
void OBSBasicSettings::SaveOutputSettings() void OBSBasicSettings::SaveOutputSettings()
{ {
int videoBitrate = ui->streamVBitrate->value(); int videoBitrate = ui->simpleOutputVBitrate->value();
int audioBitrate = ui->streamABitrate->value(); QString audioBitrate = ui->simpleOutputABitrate->currentText();
QString url = ui->streamURL->text(); QString path = ui->simpleOutputPath->text();
QString key = ui->streamKey->text();
config_set_uint(main->Config(), "OutputTemp", "VBitrate", videoBitrate); config_set_uint(main->Config(), "SimpleOutput", "VBitrate",
config_set_uint(main->Config(), "OutputTemp", "ABitrate", audioBitrate); videoBitrate);
config_set_string(main->Config(), "OutputTemp", "URL", QT_TO_UTF8(url)); config_set_string(main->Config(), "SimpleOutput", "ABitrate",
config_set_string(main->Config(), "OutputTemp", "Key", QT_TO_UTF8(key)); QT_TO_UTF8(audioBitrate));
config_set_string(main->Config(), "SimpleOutput", "path",
QT_TO_UTF8(path));
} }
static inline QString GetComboData(QComboBox *combo) static inline QString GetComboData(QComboBox *combo)
...@@ -626,6 +688,29 @@ void OBSBasicSettings::on_buttonBox_clicked(QAbstractButton *button) ...@@ -626,6 +688,29 @@ void OBSBasicSettings::on_buttonBox_clicked(QAbstractButton *button)
} }
} }
void OBSBasicSettings::on_streamType_currentIndexChanged(int idx)
{
QString val = ui->streamType->itemData(idx).toString();
obs_service_t newService;
if (loading)
return;
delete streamProperties;
streamProperties = nullptr;
newService = obs_service_create(QT_TO_UTF8(val), nullptr, nullptr);
if (newService)
main->SetService(newService);
LoadServiceInfo();
}
static inline bool StreamExists(const char *name)
{
return obs_get_service_by_name(name) != nullptr;
}
static bool ValidResolutions(Ui::OBSBasicSettings *ui) static bool ValidResolutions(Ui::OBSBasicSettings *ui)
{ {
QString baseRes = ui->baseResolution->lineEdit()->text(); QString baseRes = ui->baseResolution->lineEdit()->text();
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
class OBSBasic; class OBSBasic;
class QAbstractButton; class QAbstractButton;
class QComboBox; class QComboBox;
class OBSPropertiesView;
#include "ui_OBSBasicSettings.h" #include "ui_OBSBasicSettings.h"
...@@ -44,10 +45,12 @@ private: ...@@ -44,10 +45,12 @@ private:
int pageIndex; int pageIndex;
bool loading; bool loading;
OBSPropertiesView *streamProperties;
inline bool Changed() const inline bool Changed() const
{ {
return generalChanged || outputsChanged || audioChanged || return generalChanged || outputsChanged ||
videoChanged; audioChanged || videoChanged;
} }
inline void ClearChanged() inline void ClearChanged()
...@@ -62,6 +65,9 @@ private: ...@@ -62,6 +65,9 @@ private:
bool QueryChanges(); bool QueryChanges();
void LoadServiceTypes();
void LoadServiceInfo();
void LoadGeneralSettings(); void LoadGeneralSettings();
void LoadOutputSettings(); void LoadOutputSettings();
void LoadAudioSettings(); void LoadAudioSettings();
...@@ -71,6 +77,9 @@ private: ...@@ -71,6 +77,9 @@ private:
/* general */ /* general */
void LoadLanguageList(); void LoadLanguageList();
/* output */
void LoadSimpleOutputSettings();
/* audio */ /* audio */
void LoadListValues(QComboBox *widget, obs_property_t prop, void LoadListValues(QComboBox *widget, obs_property_t prop,
const char *configName); const char *configName);
...@@ -92,6 +101,8 @@ private slots: ...@@ -92,6 +101,8 @@ private slots:
void on_listWidget_itemSelectionChanged(); void on_listWidget_itemSelectionChanged();
void on_buttonBox_clicked(QAbstractButton *button); void on_buttonBox_clicked(QAbstractButton *button);
void on_streamType_currentIndexChanged(int idx);
void on_baseResolution_editTextChanged(const QString &text); void on_baseResolution_editTextChanged(const QString &text);
void GeneralChanged(); void GeneralChanged();
......
...@@ -399,6 +399,7 @@ static void *connect_thread(void *data) ...@@ -399,6 +399,7 @@ static void *connect_thread(void *data)
static bool rtmp_stream_start(void *data) static bool rtmp_stream_start(void *data)
{ {
struct rtmp_stream *stream = data; struct rtmp_stream *stream = data;
obs_service_t service = obs_output_get_service(stream->output);
obs_data_t settings; obs_data_t settings;
if (!obs_output_can_begin_data_capture(stream->output, 0)) if (!obs_output_can_begin_data_capture(stream->output, 0))
...@@ -407,10 +408,10 @@ static bool rtmp_stream_start(void *data) ...@@ -407,10 +408,10 @@ static bool rtmp_stream_start(void *data)
return false; return false;
settings = obs_output_get_settings(stream->output); settings = obs_output_get_settings(stream->output);
dstr_copy(&stream->path, obs_data_getstring(settings, "path")); dstr_copy(&stream->path, obs_service_get_url(service));
dstr_copy(&stream->key, obs_data_getstring(settings, "key")); dstr_copy(&stream->key, obs_service_get_key(service));
dstr_copy(&stream->username, obs_data_getstring(settings, "username")); dstr_copy(&stream->username, obs_service_get_username(service));
dstr_copy(&stream->password, obs_data_getstring(settings, "password")); dstr_copy(&stream->password, obs_service_get_password(service));
stream->drop_threshold_usec = stream->drop_threshold_usec =
(int64_t)obs_data_getint(settings, "drop_threshold"); (int64_t)obs_data_getint(settings, "drop_threshold");
obs_data_release(settings); obs_data_release(settings);
...@@ -555,7 +556,9 @@ static obs_properties_t rtmp_stream_properties(const char *locale) ...@@ -555,7 +556,9 @@ static obs_properties_t rtmp_stream_properties(const char *locale)
struct obs_output_info rtmp_output_info = { struct obs_output_info rtmp_output_info = {
.id = "rtmp_output", .id = "rtmp_output",
.flags = OBS_OUTPUT_AV | OBS_OUTPUT_ENCODED, .flags = OBS_OUTPUT_AV |
OBS_OUTPUT_ENCODED |
OBS_OUTPUT_SERVICE,
.getname = rtmp_stream_getname, .getname = rtmp_stream_getname,
.create = rtmp_stream_create, .create = rtmp_stream_create,
.destroy = rtmp_stream_destroy, .destroy = rtmp_stream_destroy,
......
...@@ -76,6 +76,8 @@ static void obs_x264_defaults(obs_data_t settings) ...@@ -76,6 +76,8 @@ static void obs_x264_defaults(obs_data_t settings)
obs_data_set_default_int (settings, "bitrate", 1000); obs_data_set_default_int (settings, "bitrate", 1000);
obs_data_set_default_int (settings, "buffer_size", 1000); obs_data_set_default_int (settings, "buffer_size", 1000);
obs_data_set_default_int (settings, "keyint_sec", 0); obs_data_set_default_int (settings, "keyint_sec", 0);
obs_data_set_default_int (settings, "crf", 23);
obs_data_set_default_bool (settings, "cbr", false);
obs_data_set_default_string(settings, "preset", "veryfast"); obs_data_set_default_string(settings, "preset", "veryfast");
obs_data_set_default_string(settings, "profile", ""); obs_data_set_default_string(settings, "profile", "");
...@@ -221,6 +223,8 @@ static void update_params(struct obs_x264 *obsx264, obs_data_t settings, ...@@ -221,6 +223,8 @@ static void update_params(struct obs_x264 *obsx264, obs_data_t settings,
int bitrate = (int)obs_data_getint(settings, "bitrate"); int bitrate = (int)obs_data_getint(settings, "bitrate");
int buffer_size = (int)obs_data_getint(settings, "buffer_size"); int buffer_size = (int)obs_data_getint(settings, "buffer_size");
int keyint_sec = (int)obs_data_getint(settings, "keyint_sec"); int keyint_sec = (int)obs_data_getint(settings, "keyint_sec");
int crf = (int)obs_data_getint(settings, "crf");
bool cbr = obs_data_getbool(settings, "cbr");
if (keyint_sec) if (keyint_sec)
obsx264->params.i_keyint_max = obsx264->params.i_keyint_max =
...@@ -237,6 +241,17 @@ static void update_params(struct obs_x264 *obsx264, obs_data_t settings, ...@@ -237,6 +241,17 @@ static void update_params(struct obs_x264 *obsx264, obs_data_t settings,
obsx264->params.pf_log = log_x264; obsx264->params.pf_log = log_x264;
obsx264->params.i_log_level = X264_LOG_WARNING; obsx264->params.i_log_level = X264_LOG_WARNING;
/* use the new filler method for CBR to allow real-time adjusting of
* the bitrate */
if (cbr) {
obsx264->params.rc.b_filler = true;
obsx264->params.rc.f_rf_constant = 0.0f;
obsx264->params.rc.i_rc_method = X264_RC_ABR;
} else {
obsx264->params.rc.i_rc_method = X264_RC_CRF;
obsx264->params.rc.f_rf_constant = (float)crf;
}
if (voi->format == VIDEO_FORMAT_NV12) if (voi->format == VIDEO_FORMAT_NV12)
obsx264->params.i_csp = X264_CSP_NV12; obsx264->params.i_csp = X264_CSP_NV12;
else if (voi->format == VIDEO_FORMAT_I420) else if (voi->format == VIDEO_FORMAT_I420)
......
...@@ -48,19 +48,35 @@ static void *rtmp_common_create(obs_data_t settings, obs_service_t service) ...@@ -48,19 +48,35 @@ static void *rtmp_common_create(obs_data_t settings, obs_service_t service)
return data; return data;
} }
static void add_service(obs_property_t list, const char *name, static inline const char *get_string_val(json_t *service, const char *key)
json_t *service) {
json_t *str_val = json_object_get(service, key);
if (!str_val || !json_is_string(str_val))
return NULL;
return json_string_value(str_val);
}
static void add_service(obs_property_t list, json_t *service)
{ {
json_t *servers; json_t *servers;
const char *name;
if (!json_is_object(service)) { if (!json_is_object(service)) {
blog(LOG_WARNING, "rtmp-common.c: [add_service] service " blog(LOG_WARNING, "rtmp-common.c: [add_service] service "
"'%s' is not an object", name); "is not an object");
return;
}
name = get_string_val(service, "name");
if (!name) {
blog(LOG_WARNING, "rtmp-common.c: [add_service] service "
"has no name");
return; return;
} }
servers = json_object_get(service, "servers"); servers = json_object_get(service, "servers");
if (!servers) { if (!servers || !json_is_array(servers)) {
blog(LOG_WARNING, "rtmp-common.c: [add_service] service " blog(LOG_WARNING, "rtmp-common.c: [add_service] service "
"'%s' has no servers", name); "'%s' has no servers", name);
return; return;
...@@ -72,16 +88,16 @@ static void add_service(obs_property_t list, const char *name, ...@@ -72,16 +88,16 @@ static void add_service(obs_property_t list, const char *name,
static void add_services(obs_property_t list, const char *file, json_t *root) static void add_services(obs_property_t list, const char *file, json_t *root)
{ {
json_t *service; json_t *service;
const char *name; size_t index;
if (!json_is_object(root)) { if (!json_is_array(root)) {
blog(LOG_WARNING, "rtmp-common.c: [add_services] JSON file " blog(LOG_WARNING, "rtmp-common.c: [add_services] JSON file "
"'%s' root is not an object", file); "'%s' root is not an array", file);
return; return;
} }
json_object_foreach (root, name, service) { json_array_foreach (root, index, service) {
add_service(list, name, service); add_service(list, service);
} }
} }
...@@ -115,35 +131,62 @@ static void properties_data_destroy(void *data) ...@@ -115,35 +131,62 @@ static void properties_data_destroy(void *data)
json_decref(root); json_decref(root);
} }
static void fill_servers(obs_property_t servers, json_t *service, static void fill_servers(obs_property_t servers_prop, json_t *service,
const char *name) const char *name)
{ {
json_t *server; json_t *servers, *server;
const char *server_name; size_t index;
obs_property_list_clear(servers); obs_property_list_clear(servers_prop);
if (!json_is_object(service)) { servers = json_object_get(service, "servers");
if (!json_is_array(servers)) {
blog(LOG_WARNING, "rtmp-common.c: [fill_servers] " blog(LOG_WARNING, "rtmp-common.c: [fill_servers] "
"Servers for service '%s' not a valid object", "Servers for service '%s' not a valid object",
name); name);
return; return;
} }
json_object_foreach (service, server_name, server) { json_array_foreach (servers, index, server) {
if (json_is_string(server)) { const char *server_name = get_string_val(server, "name");
obs_property_list_add_string(servers, server_name, const char *url = get_string_val(server, "url");
json_string_value(server));
} if (!server_name || !url)
continue;
obs_property_list_add_string(servers_prop, server_name, url);
} }
} }
static inline json_t *find_service(json_t *root, const char *name)
{
size_t index;
json_t *service;
json_array_foreach (root, index, service) {
const char *cur_name = get_string_val(service, "name");
if (strcmp(name, cur_name) == 0)
return service;
}
return NULL;
}
static bool service_selected(obs_properties_t props, obs_property_t p, static bool service_selected(obs_properties_t props, obs_property_t p,
obs_data_t settings) obs_data_t settings)
{ {
const char *name = obs_data_getstring(settings, "service"); const char *name = obs_data_getstring(settings, "service");
json_t *root = obs_properties_get_param(props); json_t *root = obs_properties_get_param(props);
json_t *service = json_object_get(root, name); json_t *service;
if (!name || !*name)
return false;
service = find_service(root, name);
if (!service)
return false;
fill_servers(obs_properties_get(props, "server"), service, name); fill_servers(obs_properties_get(props, "server"), service, name);
......
...@@ -25,26 +25,26 @@ ...@@ -25,26 +25,26 @@
</PropertyGroup> </PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType> <ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries> <UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v120</PlatformToolset> <PlatformToolset>v120</PlatformToolset>
<CharacterSet>Unicode</CharacterSet> <CharacterSet>Unicode</CharacterSet>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType> <ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries> <UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v120</PlatformToolset> <PlatformToolset>v120</PlatformToolset>
<CharacterSet>Unicode</CharacterSet> <CharacterSet>Unicode</CharacterSet>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType> <ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries> <UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v120</PlatformToolset> <PlatformToolset>v120</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization> <WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet> <CharacterSet>Unicode</CharacterSet>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType> <ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries> <UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v120</PlatformToolset> <PlatformToolset>v120</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization> <WholeProgramOptimization>true</WholeProgramOptimization>
...@@ -94,7 +94,8 @@ ...@@ -94,7 +94,8 @@
<ModuleDefinitionFile>../../../deps/jansson/src/jansson.def</ModuleDefinitionFile> <ModuleDefinitionFile>../../../deps/jansson/src/jansson.def</ModuleDefinitionFile>
</Link> </Link>
<PostBuildEvent> <PostBuildEvent>
<Command>copy "$(OutDir)$(TargetName)$(TargetExt)" "../../../build/bin/32bit/$(TargetName)$(TargetExt)"</Command> <Command>
</Command>
</PostBuildEvent> </PostBuildEvent>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
...@@ -113,7 +114,8 @@ ...@@ -113,7 +114,8 @@
<ModuleDefinitionFile>../../../deps/jansson/src/jansson.def</ModuleDefinitionFile> <ModuleDefinitionFile>../../../deps/jansson/src/jansson.def</ModuleDefinitionFile>
</Link> </Link>
<PostBuildEvent> <PostBuildEvent>
<Command>copy "$(OutDir)$(TargetName)$(TargetExt)" "../../../build/bin/64bit/$(TargetName)$(TargetExt)"</Command> <Command>
</Command>
</PostBuildEvent> </PostBuildEvent>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
...@@ -136,7 +138,8 @@ ...@@ -136,7 +138,8 @@
<ModuleDefinitionFile>../../../deps/jansson/src/jansson.def</ModuleDefinitionFile> <ModuleDefinitionFile>../../../deps/jansson/src/jansson.def</ModuleDefinitionFile>
</Link> </Link>
<PostBuildEvent> <PostBuildEvent>
<Command>copy "$(OutDir)$(TargetName)$(TargetExt)" "../../../build/bin/32bit/$(TargetName)$(TargetExt)"</Command> <Command>
</Command>
</PostBuildEvent> </PostBuildEvent>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
...@@ -159,7 +162,8 @@ ...@@ -159,7 +162,8 @@
<ModuleDefinitionFile>../../../deps/jansson/src/jansson.def</ModuleDefinitionFile> <ModuleDefinitionFile>../../../deps/jansson/src/jansson.def</ModuleDefinitionFile>
</Link> </Link>
<PostBuildEvent> <PostBuildEvent>
<Command>copy "$(OutDir)$(TargetName)$(TargetExt)" "../../../build/bin/64bit/$(TargetName)$(TargetExt)"</Command> <Command>
</Command>
</PostBuildEvent> </PostBuildEvent>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup> <ItemGroup>
......
...@@ -205,7 +205,7 @@ ...@@ -205,7 +205,7 @@
<Optimization>Disabled</Optimization> <Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBOBS_EXPORTS;PTW32_STATIC_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBOBS_EXPORTS;PTW32_STATIC_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ExceptionHandling>false</ExceptionHandling> <ExceptionHandling>false</ExceptionHandling>
<AdditionalIncludeDirectories>../../../libobs/util/vc</AdditionalIncludeDirectories> <AdditionalIncludeDirectories>../../../libobs/util/vc;../../../deps/jansson/src</AdditionalIncludeDirectories>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
</ClCompile> </ClCompile>
<Link> <Link>
...@@ -227,7 +227,7 @@ ...@@ -227,7 +227,7 @@
<Optimization>Disabled</Optimization> <Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBOBS_EXPORTS;PTW32_STATIC_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBOBS_EXPORTS;PTW32_STATIC_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ExceptionHandling>false</ExceptionHandling> <ExceptionHandling>false</ExceptionHandling>
<AdditionalIncludeDirectories>../../../libobs/util/vc</AdditionalIncludeDirectories> <AdditionalIncludeDirectories>../../../libobs/util/vc;../../../deps/jansson/src</AdditionalIncludeDirectories>
</ClCompile> </ClCompile>
<Link> <Link>
<SubSystem>Windows</SubSystem> <SubSystem>Windows</SubSystem>
...@@ -250,7 +250,7 @@ ...@@ -250,7 +250,7 @@
<IntrinsicFunctions>true</IntrinsicFunctions> <IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBOBS_EXPORTS;PTW32_STATIC_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBOBS_EXPORTS;PTW32_STATIC_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ExceptionHandling>false</ExceptionHandling> <ExceptionHandling>false</ExceptionHandling>
<AdditionalIncludeDirectories>../../../libobs/util/vc</AdditionalIncludeDirectories> <AdditionalIncludeDirectories>../../../libobs/util/vc;../../../deps/jansson/src</AdditionalIncludeDirectories>
</ClCompile> </ClCompile>
<Link> <Link>
<SubSystem>Windows</SubSystem> <SubSystem>Windows</SubSystem>
...@@ -275,7 +275,7 @@ ...@@ -275,7 +275,7 @@
<IntrinsicFunctions>true</IntrinsicFunctions> <IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBOBS_EXPORTS;PTW32_STATIC_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBOBS_EXPORTS;PTW32_STATIC_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ExceptionHandling>false</ExceptionHandling> <ExceptionHandling>false</ExceptionHandling>
<AdditionalIncludeDirectories>../../../libobs/util/vc</AdditionalIncludeDirectories> <AdditionalIncludeDirectories>../../../libobs/util/vc;../../../deps/jansson/src</AdditionalIncludeDirectories>
</ClCompile> </ClCompile>
<Link> <Link>
<SubSystem>Windows</SubSystem> <SubSystem>Windows</SubSystem>
......
...@@ -93,6 +93,9 @@ ...@@ -93,6 +93,9 @@
<AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> <AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>jansson.lib;libobs.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalDependencies>jansson.lib;libobs.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link> </Link>
<PostBuildEvent>
<Command>copy "$(OutDir)$(TargetName)$(TargetExt)" "../../../build/obs-plugins/32bit/$(TargetName)$(TargetExt)"</Command>
</PostBuildEvent>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile> <ClCompile>
...@@ -109,6 +112,9 @@ ...@@ -109,6 +112,9 @@
<AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> <AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>jansson.lib;libobs.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalDependencies>jansson.lib;libobs.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link> </Link>
<PostBuildEvent>
<Command>copy "$(OutDir)$(TargetName)$(TargetExt)" "../../../build/obs-plugins/64bit/$(TargetName)$(TargetExt)"</Command>
</PostBuildEvent>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile> <ClCompile>
...@@ -129,6 +135,9 @@ ...@@ -129,6 +135,9 @@
<AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> <AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>jansson.lib;libobs.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalDependencies>jansson.lib;libobs.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link> </Link>
<PostBuildEvent>
<Command>copy "$(OutDir)$(TargetName)$(TargetExt)" "../../../build/obs-plugins/32bit/$(TargetName)$(TargetExt)"</Command>
</PostBuildEvent>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile> <ClCompile>
...@@ -149,6 +158,9 @@ ...@@ -149,6 +158,9 @@
<AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> <AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>jansson.lib;libobs.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalDependencies>jansson.lib;libobs.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link> </Link>
<PostBuildEvent>
<Command>copy "$(OutDir)$(TargetName)$(TargetExt)" "../../../build/obs-plugins/64bit/$(TargetName)$(TargetExt)"</Command>
</PostBuildEvent>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="..\..\..\plugins\rtmp-services\rtmp-common.c" /> <ClCompile Include="..\..\..\plugins\rtmp-services\rtmp-common.c" />
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册