2014-01-16 17:38:35 -08:00
|
|
|
#include <stdlib.h>
|
2014-07-09 22:12:57 -07:00
|
|
|
#include <obs-module.h>
|
2014-03-23 13:05:43 -07:00
|
|
|
#include <util/threading.h>
|
Revamp API and start using doxygen
The API used to be designed in such a way to where it would expect
exports for each individual source/output/encoder/etc. You would export
functions for each and it would automatically load those functions based
on a specific naming scheme from the module.
The idea behind this was that I wanted to limit the usage of structures
in the API so only functions could be used. It was an interesting idea
in theory, but this idea turned out to be flawed in a number of ways:
1.) Requiring exports to create sources/outputs/encoders/etc meant that
you could not create them by any other means, which meant that
things like faruton's .net plugin would become difficult.
2.) Export function declarations could not be checked, therefore if you
created a function with the wrong parameters and parameter types,
the compiler wouldn't know how to check for that.
3.) Required overly complex load functions in libobs just to handle it.
It makes much more sense to just have a load function that you call
manually. Complexity is the bane of all good programs.
4.) It required that you have functions of specific names, which looked
and felt somewhat unsightly.
So, to fix these issues, I replaced it with a more commonly used API
scheme, seen commonly in places like kernels and typical C libraries
with abstraction. You simply create a structure that contains the
callback definitions, and you pass it to a function to register that
definition (such as obs_register_source), which you call in the
obs_module_load of the module.
It will also automatically check the structure size and ensure that it
only loads the required values if the structure happened to add new
values in an API change.
The "main" source file for each module must include obs-module.h, and
must use OBS_DECLARE_MODULE() within that source file.
Also, started writing some doxygen documentation in to the main library
headers. Will add more detailed documentation as I go.
2014-02-12 07:04:50 -08:00
|
|
|
#include <pthread.h>
|
2014-01-16 17:38:35 -08:00
|
|
|
|
Revamp API and start using doxygen
The API used to be designed in such a way to where it would expect
exports for each individual source/output/encoder/etc. You would export
functions for each and it would automatically load those functions based
on a specific naming scheme from the module.
The idea behind this was that I wanted to limit the usage of structures
in the API so only functions could be used. It was an interesting idea
in theory, but this idea turned out to be flawed in a number of ways:
1.) Requiring exports to create sources/outputs/encoders/etc meant that
you could not create them by any other means, which meant that
things like faruton's .net plugin would become difficult.
2.) Export function declarations could not be checked, therefore if you
created a function with the wrong parameters and parameter types,
the compiler wouldn't know how to check for that.
3.) Required overly complex load functions in libobs just to handle it.
It makes much more sense to just have a load function that you call
manually. Complexity is the bane of all good programs.
4.) It required that you have functions of specific names, which looked
and felt somewhat unsightly.
So, to fix these issues, I replaced it with a more commonly used API
scheme, seen commonly in places like kernels and typical C libraries
with abstraction. You simply create a structure that contains the
callback definitions, and you pass it to a function to register that
definition (such as obs_register_source), which you call in the
obs_module_load of the module.
It will also automatically check the structure size and ensure that it
only loads the required values if the structure happened to add new
values in an API change.
The "main" source file for each module must include obs-module.h, and
must use OBS_DECLARE_MODULE() within that source file.
Also, started writing some doxygen documentation in to the main library
headers. Will add more detailed documentation as I go.
2014-02-12 07:04:50 -08:00
|
|
|
#import <CoreGraphics/CGDisplayStream.h>
|
2014-01-16 17:38:35 -08:00
|
|
|
#import <Cocoa/Cocoa.h>
|
|
|
|
|
2014-08-19 15:38:26 -07:00
|
|
|
#include "window-utils.h"
|
|
|
|
|
|
|
|
enum crop_mode {
|
|
|
|
CROP_NONE,
|
|
|
|
CROP_MANUAL,
|
|
|
|
CROP_TO_WINDOW,
|
|
|
|
CROP_TO_WINDOW_AND_MANUAL,
|
|
|
|
CROP_INVALID
|
|
|
|
};
|
|
|
|
|
|
|
|
static inline bool requires_window(enum crop_mode mode)
|
|
|
|
{
|
|
|
|
return mode == CROP_TO_WINDOW || mode == CROP_TO_WINDOW_AND_MANUAL;
|
|
|
|
}
|
|
|
|
|
2014-03-23 13:05:43 -07:00
|
|
|
struct display_capture {
|
2014-09-25 17:44:05 -07:00
|
|
|
obs_source_t *source;
|
2014-05-11 12:47:34 -07:00
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
gs_samplerstate_t *sampler;
|
2014-10-03 14:45:15 -07:00
|
|
|
gs_effect_t *effect;
|
2014-09-25 17:44:05 -07:00
|
|
|
gs_texture_t *tex;
|
|
|
|
gs_vertbuffer_t *vertbuf;
|
2014-01-16 17:38:35 -08:00
|
|
|
|
2014-08-19 15:38:26 -07:00
|
|
|
NSScreen *screen;
|
2014-03-23 13:05:43 -07:00
|
|
|
unsigned display;
|
2014-08-19 15:38:26 -07:00
|
|
|
NSRect frame;
|
2014-04-16 08:20:01 -07:00
|
|
|
bool hide_cursor;
|
Revamp API and start using doxygen
The API used to be designed in such a way to where it would expect
exports for each individual source/output/encoder/etc. You would export
functions for each and it would automatically load those functions based
on a specific naming scheme from the module.
The idea behind this was that I wanted to limit the usage of structures
in the API so only functions could be used. It was an interesting idea
in theory, but this idea turned out to be flawed in a number of ways:
1.) Requiring exports to create sources/outputs/encoders/etc meant that
you could not create them by any other means, which meant that
things like faruton's .net plugin would become difficult.
2.) Export function declarations could not be checked, therefore if you
created a function with the wrong parameters and parameter types,
the compiler wouldn't know how to check for that.
3.) Required overly complex load functions in libobs just to handle it.
It makes much more sense to just have a load function that you call
manually. Complexity is the bane of all good programs.
4.) It required that you have functions of specific names, which looked
and felt somewhat unsightly.
So, to fix these issues, I replaced it with a more commonly used API
scheme, seen commonly in places like kernels and typical C libraries
with abstraction. You simply create a structure that contains the
callback definitions, and you pass it to a function to register that
definition (such as obs_register_source), which you call in the
obs_module_load of the module.
It will also automatically check the structure size and ensure that it
only loads the required values if the structure happened to add new
values in an API change.
The "main" source file for each module must include obs-module.h, and
must use OBS_DECLARE_MODULE() within that source file.
Also, started writing some doxygen documentation in to the main library
headers. Will add more detailed documentation as I go.
2014-02-12 07:04:50 -08:00
|
|
|
|
2014-08-19 15:38:26 -07:00
|
|
|
enum crop_mode crop;
|
|
|
|
CGRect crop_rect;
|
|
|
|
|
|
|
|
struct cocoa_window window;
|
|
|
|
CGRect window_rect;
|
|
|
|
bool on_screen;
|
|
|
|
bool hide_when_minimized;
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
os_event_t *disp_finished;
|
2014-03-23 13:05:43 -07:00
|
|
|
CGDisplayStreamRef disp;
|
Revamp API and start using doxygen
The API used to be designed in such a way to where it would expect
exports for each individual source/output/encoder/etc. You would export
functions for each and it would automatically load those functions based
on a specific naming scheme from the module.
The idea behind this was that I wanted to limit the usage of structures
in the API so only functions could be used. It was an interesting idea
in theory, but this idea turned out to be flawed in a number of ways:
1.) Requiring exports to create sources/outputs/encoders/etc meant that
you could not create them by any other means, which meant that
things like faruton's .net plugin would become difficult.
2.) Export function declarations could not be checked, therefore if you
created a function with the wrong parameters and parameter types,
the compiler wouldn't know how to check for that.
3.) Required overly complex load functions in libobs just to handle it.
It makes much more sense to just have a load function that you call
manually. Complexity is the bane of all good programs.
4.) It required that you have functions of specific names, which looked
and felt somewhat unsightly.
So, to fix these issues, I replaced it with a more commonly used API
scheme, seen commonly in places like kernels and typical C libraries
with abstraction. You simply create a structure that contains the
callback definitions, and you pass it to a function to register that
definition (such as obs_register_source), which you call in the
obs_module_load of the module.
It will also automatically check the structure size and ensure that it
only loads the required values if the structure happened to add new
values in an API change.
The "main" source file for each module must include obs-module.h, and
must use OBS_DECLARE_MODULE() within that source file.
Also, started writing some doxygen documentation in to the main library
headers. Will add more detailed documentation as I go.
2014-02-12 07:04:50 -08:00
|
|
|
IOSurfaceRef current, prev;
|
2014-03-23 13:05:43 -07:00
|
|
|
|
|
|
|
pthread_mutex_t mutex;
|
Revamp API and start using doxygen
The API used to be designed in such a way to where it would expect
exports for each individual source/output/encoder/etc. You would export
functions for each and it would automatically load those functions based
on a specific naming scheme from the module.
The idea behind this was that I wanted to limit the usage of structures
in the API so only functions could be used. It was an interesting idea
in theory, but this idea turned out to be flawed in a number of ways:
1.) Requiring exports to create sources/outputs/encoders/etc meant that
you could not create them by any other means, which meant that
things like faruton's .net plugin would become difficult.
2.) Export function declarations could not be checked, therefore if you
created a function with the wrong parameters and parameter types,
the compiler wouldn't know how to check for that.
3.) Required overly complex load functions in libobs just to handle it.
It makes much more sense to just have a load function that you call
manually. Complexity is the bane of all good programs.
4.) It required that you have functions of specific names, which looked
and felt somewhat unsightly.
So, to fix these issues, I replaced it with a more commonly used API
scheme, seen commonly in places like kernels and typical C libraries
with abstraction. You simply create a structure that contains the
callback definitions, and you pass it to a function to register that
definition (such as obs_register_source), which you call in the
obs_module_load of the module.
It will also automatically check the structure size and ensure that it
only loads the required values if the structure happened to add new
values in an API change.
The "main" source file for each module must include obs-module.h, and
must use OBS_DECLARE_MODULE() within that source file.
Also, started writing some doxygen documentation in to the main library
headers. Will add more detailed documentation as I go.
2014-02-12 07:04:50 -08:00
|
|
|
};
|
2014-01-16 17:38:35 -08:00
|
|
|
|
2014-08-19 15:38:26 -07:00
|
|
|
static inline bool crop_mode_valid(enum crop_mode mode)
|
|
|
|
{
|
|
|
|
return CROP_NONE <= mode && mode < CROP_INVALID;
|
|
|
|
}
|
|
|
|
|
2014-03-23 13:05:43 -07:00
|
|
|
static void destroy_display_stream(struct display_capture *dc)
|
Revamp API and start using doxygen
The API used to be designed in such a way to where it would expect
exports for each individual source/output/encoder/etc. You would export
functions for each and it would automatically load those functions based
on a specific naming scheme from the module.
The idea behind this was that I wanted to limit the usage of structures
in the API so only functions could be used. It was an interesting idea
in theory, but this idea turned out to be flawed in a number of ways:
1.) Requiring exports to create sources/outputs/encoders/etc meant that
you could not create them by any other means, which meant that
things like faruton's .net plugin would become difficult.
2.) Export function declarations could not be checked, therefore if you
created a function with the wrong parameters and parameter types,
the compiler wouldn't know how to check for that.
3.) Required overly complex load functions in libobs just to handle it.
It makes much more sense to just have a load function that you call
manually. Complexity is the bane of all good programs.
4.) It required that you have functions of specific names, which looked
and felt somewhat unsightly.
So, to fix these issues, I replaced it with a more commonly used API
scheme, seen commonly in places like kernels and typical C libraries
with abstraction. You simply create a structure that contains the
callback definitions, and you pass it to a function to register that
definition (such as obs_register_source), which you call in the
obs_module_load of the module.
It will also automatically check the structure size and ensure that it
only loads the required values if the structure happened to add new
values in an API change.
The "main" source file for each module must include obs-module.h, and
must use OBS_DECLARE_MODULE() within that source file.
Also, started writing some doxygen documentation in to the main library
headers. Will add more detailed documentation as I go.
2014-02-12 07:04:50 -08:00
|
|
|
{
|
2014-03-23 13:05:43 -07:00
|
|
|
if (dc->disp) {
|
|
|
|
CGDisplayStreamStop(dc->disp);
|
|
|
|
os_event_wait(dc->disp_finished);
|
|
|
|
}
|
Revamp API and start using doxygen
The API used to be designed in such a way to where it would expect
exports for each individual source/output/encoder/etc. You would export
functions for each and it would automatically load those functions based
on a specific naming scheme from the module.
The idea behind this was that I wanted to limit the usage of structures
in the API so only functions could be used. It was an interesting idea
in theory, but this idea turned out to be flawed in a number of ways:
1.) Requiring exports to create sources/outputs/encoders/etc meant that
you could not create them by any other means, which meant that
things like faruton's .net plugin would become difficult.
2.) Export function declarations could not be checked, therefore if you
created a function with the wrong parameters and parameter types,
the compiler wouldn't know how to check for that.
3.) Required overly complex load functions in libobs just to handle it.
It makes much more sense to just have a load function that you call
manually. Complexity is the bane of all good programs.
4.) It required that you have functions of specific names, which looked
and felt somewhat unsightly.
So, to fix these issues, I replaced it with a more commonly used API
scheme, seen commonly in places like kernels and typical C libraries
with abstraction. You simply create a structure that contains the
callback definitions, and you pass it to a function to register that
definition (such as obs_register_source), which you call in the
obs_module_load of the module.
It will also automatically check the structure size and ensure that it
only loads the required values if the structure happened to add new
values in an API change.
The "main" source file for each module must include obs-module.h, and
must use OBS_DECLARE_MODULE() within that source file.
Also, started writing some doxygen documentation in to the main library
headers. Will add more detailed documentation as I go.
2014-02-12 07:04:50 -08:00
|
|
|
|
2014-03-23 13:05:43 -07:00
|
|
|
if (dc->tex) {
|
2014-08-07 23:42:07 -07:00
|
|
|
gs_texture_destroy(dc->tex);
|
2014-03-23 13:05:43 -07:00
|
|
|
dc->tex = NULL;
|
Revamp API and start using doxygen
The API used to be designed in such a way to where it would expect
exports for each individual source/output/encoder/etc. You would export
functions for each and it would automatically load those functions based
on a specific naming scheme from the module.
The idea behind this was that I wanted to limit the usage of structures
in the API so only functions could be used. It was an interesting idea
in theory, but this idea turned out to be flawed in a number of ways:
1.) Requiring exports to create sources/outputs/encoders/etc meant that
you could not create them by any other means, which meant that
things like faruton's .net plugin would become difficult.
2.) Export function declarations could not be checked, therefore if you
created a function with the wrong parameters and parameter types,
the compiler wouldn't know how to check for that.
3.) Required overly complex load functions in libobs just to handle it.
It makes much more sense to just have a load function that you call
manually. Complexity is the bane of all good programs.
4.) It required that you have functions of specific names, which looked
and felt somewhat unsightly.
So, to fix these issues, I replaced it with a more commonly used API
scheme, seen commonly in places like kernels and typical C libraries
with abstraction. You simply create a structure that contains the
callback definitions, and you pass it to a function to register that
definition (such as obs_register_source), which you call in the
obs_module_load of the module.
It will also automatically check the structure size and ensure that it
only loads the required values if the structure happened to add new
values in an API change.
The "main" source file for each module must include obs-module.h, and
must use OBS_DECLARE_MODULE() within that source file.
Also, started writing some doxygen documentation in to the main library
headers. Will add more detailed documentation as I go.
2014-02-12 07:04:50 -08:00
|
|
|
}
|
2014-03-23 13:05:43 -07:00
|
|
|
|
|
|
|
if (dc->current) {
|
|
|
|
IOSurfaceDecrementUseCount(dc->current);
|
|
|
|
CFRelease(dc->current);
|
|
|
|
dc->current = NULL;
|
|
|
|
}
|
|
|
|
|
2014-03-25 17:24:54 -07:00
|
|
|
if (dc->prev) {
|
|
|
|
IOSurfaceDecrementUseCount(dc->prev);
|
|
|
|
CFRelease(dc->prev);
|
|
|
|
dc->prev = NULL;
|
|
|
|
}
|
|
|
|
|
2014-03-23 13:05:43 -07:00
|
|
|
if (dc->disp) {
|
|
|
|
CFRelease(dc->disp);
|
|
|
|
dc->disp = NULL;
|
|
|
|
}
|
|
|
|
|
2014-08-19 15:38:26 -07:00
|
|
|
if (dc->screen) {
|
|
|
|
[dc->screen release];
|
|
|
|
dc->screen = nil;
|
|
|
|
}
|
|
|
|
|
2014-03-23 13:05:43 -07:00
|
|
|
os_event_destroy(dc->disp_finished);
|
Revamp API and start using doxygen
The API used to be designed in such a way to where it would expect
exports for each individual source/output/encoder/etc. You would export
functions for each and it would automatically load those functions based
on a specific naming scheme from the module.
The idea behind this was that I wanted to limit the usage of structures
in the API so only functions could be used. It was an interesting idea
in theory, but this idea turned out to be flawed in a number of ways:
1.) Requiring exports to create sources/outputs/encoders/etc meant that
you could not create them by any other means, which meant that
things like faruton's .net plugin would become difficult.
2.) Export function declarations could not be checked, therefore if you
created a function with the wrong parameters and parameter types,
the compiler wouldn't know how to check for that.
3.) Required overly complex load functions in libobs just to handle it.
It makes much more sense to just have a load function that you call
manually. Complexity is the bane of all good programs.
4.) It required that you have functions of specific names, which looked
and felt somewhat unsightly.
So, to fix these issues, I replaced it with a more commonly used API
scheme, seen commonly in places like kernels and typical C libraries
with abstraction. You simply create a structure that contains the
callback definitions, and you pass it to a function to register that
definition (such as obs_register_source), which you call in the
obs_module_load of the module.
It will also automatically check the structure size and ensure that it
only loads the required values if the structure happened to add new
values in an API change.
The "main" source file for each module must include obs-module.h, and
must use OBS_DECLARE_MODULE() within that source file.
Also, started writing some doxygen documentation in to the main library
headers. Will add more detailed documentation as I go.
2014-02-12 07:04:50 -08:00
|
|
|
}
|
|
|
|
|
2014-03-23 13:05:43 -07:00
|
|
|
static void display_capture_destroy(void *data)
|
2014-01-16 17:38:35 -08:00
|
|
|
{
|
2014-03-23 13:05:43 -07:00
|
|
|
struct display_capture *dc = data;
|
|
|
|
|
|
|
|
if (!dc)
|
|
|
|
return;
|
2014-01-16 17:38:35 -08:00
|
|
|
|
2014-08-04 05:48:58 -07:00
|
|
|
obs_enter_graphics();
|
2014-01-16 17:38:35 -08:00
|
|
|
|
2014-03-23 13:05:43 -07:00
|
|
|
destroy_display_stream(dc);
|
2014-01-16 17:38:35 -08:00
|
|
|
|
2014-03-23 13:05:43 -07:00
|
|
|
if (dc->sampler)
|
2014-08-07 23:42:07 -07:00
|
|
|
gs_samplerstate_destroy(dc->sampler);
|
2014-08-19 15:38:26 -07:00
|
|
|
if (dc->vertbuf)
|
|
|
|
gs_vertexbuffer_destroy(dc->vertbuf);
|
2014-01-16 17:38:35 -08:00
|
|
|
|
2014-08-04 05:48:58 -07:00
|
|
|
obs_leave_graphics();
|
2014-01-16 17:38:35 -08:00
|
|
|
|
2014-08-19 15:38:26 -07:00
|
|
|
destroy_window(&dc->window);
|
|
|
|
|
2014-03-23 13:05:43 -07:00
|
|
|
pthread_mutex_destroy(&dc->mutex);
|
|
|
|
bfree(dc);
|
|
|
|
}
|
|
|
|
|
2014-08-19 15:38:26 -07:00
|
|
|
static inline void update_window_params(struct display_capture *dc)
|
|
|
|
{
|
|
|
|
if (!requires_window(dc->crop))
|
|
|
|
return;
|
|
|
|
|
|
|
|
NSArray *arr = (NSArray*)CGWindowListCopyWindowInfo(
|
|
|
|
kCGWindowListOptionIncludingWindow,
|
|
|
|
dc->window.window_id);
|
|
|
|
|
|
|
|
if (arr.count) {
|
|
|
|
NSDictionary *dict = arr[0];
|
|
|
|
NSDictionary *ref = dict[(NSString*)kCGWindowBounds];
|
|
|
|
CGRectMakeWithDictionaryRepresentation((CFDictionaryRef)ref,
|
|
|
|
&dc->window_rect);
|
|
|
|
dc->on_screen = dict[(NSString*)kCGWindowIsOnscreen] != nil;
|
|
|
|
dc->window_rect =
|
|
|
|
[dc->screen convertRectToBacking:dc->window_rect];
|
|
|
|
|
|
|
|
} else {
|
|
|
|
if (find_window(&dc->window, NULL, false))
|
|
|
|
update_window_params(dc);
|
|
|
|
else
|
|
|
|
dc->on_screen = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
[arr release];
|
|
|
|
}
|
|
|
|
|
2014-05-11 12:14:40 -07:00
|
|
|
static inline void display_stream_update(struct display_capture *dc,
|
|
|
|
CGDisplayStreamFrameStatus status, uint64_t display_time,
|
|
|
|
IOSurfaceRef frame_surface, CGDisplayStreamUpdateRef update_ref)
|
|
|
|
{
|
|
|
|
UNUSED_PARAMETER(display_time);
|
|
|
|
UNUSED_PARAMETER(update_ref);
|
|
|
|
|
|
|
|
if (status == kCGDisplayStreamFrameStatusStopped) {
|
|
|
|
os_event_signal(dc->disp_finished);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-05-11 12:43:32 -07:00
|
|
|
IOSurfaceRef prev_current = NULL;
|
|
|
|
|
|
|
|
if (frame_surface && !pthread_mutex_lock(&dc->mutex)) {
|
|
|
|
prev_current = dc->current;
|
|
|
|
dc->current = frame_surface;
|
|
|
|
CFRetain(dc->current);
|
|
|
|
IOSurfaceIncrementUseCount(dc->current);
|
2014-08-19 15:38:26 -07:00
|
|
|
|
|
|
|
update_window_params(dc);
|
|
|
|
|
2014-05-11 12:43:32 -07:00
|
|
|
pthread_mutex_unlock(&dc->mutex);
|
2014-05-11 12:14:40 -07:00
|
|
|
}
|
|
|
|
|
2014-05-11 12:43:32 -07:00
|
|
|
if (prev_current) {
|
|
|
|
IOSurfaceDecrementUseCount(prev_current);
|
|
|
|
CFRelease(prev_current);
|
|
|
|
}
|
2014-05-11 12:47:34 -07:00
|
|
|
|
|
|
|
size_t dropped_frames = CGDisplayStreamUpdateGetDropCount(update_ref);
|
|
|
|
if (dropped_frames > 0)
|
|
|
|
blog(LOG_INFO, "%s: Dropped %zu frames",
|
2014-08-04 08:41:15 -07:00
|
|
|
obs_source_get_name(dc->source),
|
|
|
|
dropped_frames);
|
2014-05-11 12:14:40 -07:00
|
|
|
}
|
|
|
|
|
2014-03-23 13:05:43 -07:00
|
|
|
static bool init_display_stream(struct display_capture *dc)
|
|
|
|
{
|
|
|
|
if (dc->display >= [NSScreen screens].count)
|
|
|
|
return false;
|
2014-01-16 17:38:35 -08:00
|
|
|
|
2014-08-19 15:38:26 -07:00
|
|
|
dc->screen = [[NSScreen screens][dc->display] retain];
|
2014-01-16 17:38:35 -08:00
|
|
|
|
2014-08-19 15:38:26 -07:00
|
|
|
dc->frame = [dc->screen convertRectToBacking:dc->screen.frame];
|
2014-01-16 17:38:35 -08:00
|
|
|
|
2014-08-19 15:38:26 -07:00
|
|
|
NSNumber *screen_num = dc->screen.deviceDescription[@"NSScreenNumber"];
|
2014-03-23 13:05:43 -07:00
|
|
|
CGDirectDisplayID disp_id = (CGDirectDisplayID)screen_num.pointerValue;
|
2014-01-16 17:38:35 -08:00
|
|
|
|
2014-04-14 13:35:45 -07:00
|
|
|
NSDictionary *rect_dict = CFBridgingRelease(
|
|
|
|
CGRectCreateDictionaryRepresentation(
|
2014-04-16 05:18:27 -07:00
|
|
|
CGRectMake(0, 0,
|
2014-08-19 15:38:26 -07:00
|
|
|
dc->screen.frame.size.width,
|
|
|
|
dc->screen.frame.size.height)));
|
2014-04-14 13:35:45 -07:00
|
|
|
|
2015-03-30 23:53:52 -07:00
|
|
|
CFBooleanRef show_cursor_cf =
|
|
|
|
dc->hide_cursor ? kCFBooleanFalse : kCFBooleanTrue;
|
|
|
|
|
2014-01-16 17:38:35 -08:00
|
|
|
NSDictionary *dict = @{
|
2014-04-14 13:35:45 -07:00
|
|
|
(__bridge NSString*)kCGDisplayStreamSourceRect: rect_dict,
|
2014-04-16 08:20:01 -07:00
|
|
|
(__bridge NSString*)kCGDisplayStreamQueueDepth: @5,
|
|
|
|
(__bridge NSString*)kCGDisplayStreamShowCursor:
|
2015-03-30 23:53:52 -07:00
|
|
|
(id)show_cursor_cf,
|
2014-01-16 17:38:35 -08:00
|
|
|
};
|
|
|
|
|
2014-03-23 13:05:43 -07:00
|
|
|
os_event_init(&dc->disp_finished, OS_EVENT_TYPE_MANUAL);
|
|
|
|
|
2014-08-19 15:38:26 -07:00
|
|
|
const CGSize *size = &dc->frame.size;
|
2014-03-23 13:05:43 -07:00
|
|
|
dc->disp = CGDisplayStreamCreateWithDispatchQueue(disp_id,
|
2014-08-19 15:38:26 -07:00
|
|
|
size->width, size->height, 'BGRA',
|
2014-01-16 17:38:35 -08:00
|
|
|
(__bridge CFDictionaryRef)dict,
|
2014-01-29 10:42:36 -08:00
|
|
|
dispatch_queue_create(NULL, NULL),
|
2014-04-16 08:20:01 -07:00
|
|
|
^(CGDisplayStreamFrameStatus status,
|
|
|
|
uint64_t displayTime,
|
|
|
|
IOSurfaceRef frameSurface,
|
2014-01-16 17:38:35 -08:00
|
|
|
CGDisplayStreamUpdateRef updateRef)
|
|
|
|
{
|
2014-05-11 12:14:40 -07:00
|
|
|
display_stream_update(dc, status, displayTime,
|
|
|
|
frameSurface, updateRef);
|
2014-01-16 17:38:35 -08:00
|
|
|
}
|
|
|
|
);
|
|
|
|
|
2014-03-23 13:05:43 -07:00
|
|
|
return !CGDisplayStreamStart(dc->disp);
|
|
|
|
}
|
2014-01-16 17:38:35 -08:00
|
|
|
|
2014-08-19 15:38:26 -07:00
|
|
|
bool init_vertbuf(struct display_capture *dc)
|
|
|
|
{
|
|
|
|
struct gs_vb_data *vb_data = gs_vbdata_create();
|
|
|
|
vb_data->num = 4;
|
|
|
|
vb_data->points = bzalloc(sizeof(struct vec3) * 4);
|
|
|
|
if (!vb_data->points)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
vb_data->num_tex = 1;
|
|
|
|
vb_data->tvarray = bzalloc(sizeof(struct gs_tvertarray));
|
|
|
|
if (!vb_data->tvarray)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
vb_data->tvarray[0].width = 2;
|
|
|
|
vb_data->tvarray[0].array = bzalloc(sizeof(struct vec2) * 4);
|
|
|
|
if (!vb_data->tvarray[0].array)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
dc->vertbuf = gs_vertexbuffer_create(vb_data, GS_DYNAMIC);
|
|
|
|
return dc->vertbuf != NULL;
|
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
void load_crop(struct display_capture *dc, obs_data_t *settings);
|
2014-08-19 15:38:26 -07:00
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
static void *display_capture_create(obs_data_t *settings,
|
|
|
|
obs_source_t *source)
|
2014-03-23 13:05:43 -07:00
|
|
|
{
|
2014-02-14 14:13:36 -08:00
|
|
|
UNUSED_PARAMETER(source);
|
|
|
|
UNUSED_PARAMETER(settings);
|
2014-03-23 13:05:43 -07:00
|
|
|
|
|
|
|
struct display_capture *dc = bzalloc(sizeof(struct display_capture));
|
|
|
|
|
2014-05-11 12:47:34 -07:00
|
|
|
dc->source = source;
|
2015-03-31 15:39:00 -07:00
|
|
|
dc->hide_cursor = !obs_data_get_bool(settings, "show_cursor");
|
2014-05-11 12:47:34 -07:00
|
|
|
|
2015-10-16 07:31:52 -07:00
|
|
|
dc->effect = obs_get_base_effect(OBS_EFFECT_DEFAULT_RECT);
|
2014-10-03 14:48:55 -07:00
|
|
|
if (!dc->effect)
|
|
|
|
goto fail;
|
|
|
|
|
2014-08-04 05:48:58 -07:00
|
|
|
obs_enter_graphics();
|
2014-03-23 13:05:43 -07:00
|
|
|
|
|
|
|
struct gs_sampler_info info = {
|
|
|
|
.filter = GS_FILTER_LINEAR,
|
|
|
|
.address_u = GS_ADDRESS_CLAMP,
|
|
|
|
.address_v = GS_ADDRESS_CLAMP,
|
|
|
|
.address_w = GS_ADDRESS_CLAMP,
|
|
|
|
.max_anisotropy = 1,
|
|
|
|
};
|
2014-08-07 23:42:07 -07:00
|
|
|
dc->sampler = gs_samplerstate_create(&info);
|
2014-03-23 13:05:43 -07:00
|
|
|
if (!dc->sampler)
|
|
|
|
goto fail;
|
|
|
|
|
2014-08-19 15:38:26 -07:00
|
|
|
if (!init_vertbuf(dc))
|
|
|
|
goto fail;
|
|
|
|
|
2014-08-04 05:48:58 -07:00
|
|
|
obs_leave_graphics();
|
2014-03-23 13:05:43 -07:00
|
|
|
|
2014-08-19 15:38:26 -07:00
|
|
|
init_window(&dc->window, settings);
|
|
|
|
load_crop(dc, settings);
|
|
|
|
|
2014-08-05 11:09:29 -07:00
|
|
|
dc->display = obs_data_get_int(settings, "display");
|
2014-03-23 13:05:43 -07:00
|
|
|
pthread_mutex_init(&dc->mutex, NULL);
|
|
|
|
|
|
|
|
if (!init_display_stream(dc))
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
return dc;
|
|
|
|
|
|
|
|
fail:
|
2014-08-04 05:48:58 -07:00
|
|
|
obs_leave_graphics();
|
2014-03-23 13:05:43 -07:00
|
|
|
display_capture_destroy(dc);
|
|
|
|
return NULL;
|
2014-01-16 17:38:35 -08:00
|
|
|
}
|
|
|
|
|
2014-08-19 15:38:26 -07:00
|
|
|
static void build_sprite(struct gs_vb_data *data, float fcx, float fcy,
|
|
|
|
float start_u, float end_u, float start_v, float end_v)
|
|
|
|
{
|
|
|
|
struct vec2 *tvarray = data->tvarray[0].array;
|
|
|
|
|
|
|
|
vec3_set(data->points+1, fcx, 0.0f, 0.0f);
|
|
|
|
vec3_set(data->points+2, 0.0f, fcy, 0.0f);
|
|
|
|
vec3_set(data->points+3, fcx, fcy, 0.0f);
|
|
|
|
vec2_set(tvarray, start_u, start_v);
|
|
|
|
vec2_set(tvarray+1, end_u, start_v);
|
|
|
|
vec2_set(tvarray+2, start_u, end_v);
|
|
|
|
vec2_set(tvarray+3, end_u, end_v);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void build_sprite_rect(struct gs_vb_data *data,
|
|
|
|
float origin_x, float origin_y, float end_x, float end_y)
|
|
|
|
{
|
|
|
|
build_sprite(data, fabs(end_x - origin_x), fabs(end_y - origin_y),
|
|
|
|
origin_x, end_x,
|
|
|
|
origin_y, end_y);
|
|
|
|
}
|
|
|
|
|
2014-05-11 12:10:40 -07:00
|
|
|
static void display_capture_video_tick(void *data, float seconds)
|
2014-01-16 17:38:35 -08:00
|
|
|
{
|
2014-05-11 12:10:40 -07:00
|
|
|
UNUSED_PARAMETER(seconds);
|
2014-03-23 13:05:43 -07:00
|
|
|
|
|
|
|
struct display_capture *dc = data;
|
2014-02-14 14:13:36 -08:00
|
|
|
|
2014-05-11 12:43:32 -07:00
|
|
|
if (!dc->current)
|
|
|
|
return;
|
2015-01-03 22:39:21 -08:00
|
|
|
if (!obs_source_showing(dc->source))
|
|
|
|
return;
|
2014-01-16 17:38:35 -08:00
|
|
|
|
2014-05-11 12:43:32 -07:00
|
|
|
IOSurfaceRef prev_prev = dc->prev;
|
|
|
|
if (pthread_mutex_lock(&dc->mutex))
|
|
|
|
return;
|
|
|
|
dc->prev = dc->current;
|
|
|
|
dc->current = NULL;
|
2014-05-11 12:10:40 -07:00
|
|
|
pthread_mutex_unlock(&dc->mutex);
|
2014-05-11 12:43:32 -07:00
|
|
|
|
|
|
|
if (prev_prev == dc->prev)
|
|
|
|
return;
|
|
|
|
|
2014-08-19 15:38:26 -07:00
|
|
|
if (requires_window(dc->crop) && !dc->on_screen)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
CGPoint origin = { 0.f };
|
|
|
|
CGPoint end = { 0.f };
|
|
|
|
|
|
|
|
switch (dc->crop) {
|
|
|
|
float x, y;
|
|
|
|
case CROP_INVALID:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CROP_MANUAL:
|
|
|
|
origin.x += dc->crop_rect.origin.x;
|
|
|
|
origin.y += dc->crop_rect.origin.y;
|
|
|
|
end.y -= dc->crop_rect.size.height;
|
|
|
|
end.x -= dc->crop_rect.size.width;
|
|
|
|
case CROP_NONE:
|
|
|
|
end.y += dc->frame.size.height;
|
|
|
|
end.x += dc->frame.size.width;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CROP_TO_WINDOW_AND_MANUAL:
|
|
|
|
origin.x += dc->crop_rect.origin.x;
|
|
|
|
origin.y += dc->crop_rect.origin.y;
|
|
|
|
end.y -= dc->crop_rect.size.height;
|
|
|
|
end.x -= dc->crop_rect.size.width;
|
|
|
|
case CROP_TO_WINDOW:
|
|
|
|
origin.x += x = dc->window_rect.origin.x - dc->frame.origin.x;
|
|
|
|
origin.y += y = dc->window_rect.origin.y - dc->frame.origin.y;
|
|
|
|
end.y += dc->window_rect.size.height + y;
|
|
|
|
end.x += dc->window_rect.size.width + x;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-08-04 05:48:58 -07:00
|
|
|
obs_enter_graphics();
|
2014-08-19 15:38:26 -07:00
|
|
|
build_sprite_rect(gs_vertexbuffer_get_data(dc->vertbuf),
|
|
|
|
origin.x, origin.y, end.x, end.y);
|
|
|
|
|
2014-05-11 12:43:32 -07:00
|
|
|
if (dc->tex)
|
2014-08-07 23:42:07 -07:00
|
|
|
gs_texture_rebind_iosurface(dc->tex, dc->prev);
|
2014-05-11 12:43:32 -07:00
|
|
|
else
|
2014-08-07 23:42:07 -07:00
|
|
|
dc->tex = gs_texture_create_from_iosurface(dc->prev);
|
2014-08-04 05:48:58 -07:00
|
|
|
obs_leave_graphics();
|
2014-05-11 12:43:32 -07:00
|
|
|
|
2014-08-19 15:38:26 -07:00
|
|
|
cleanup:
|
2014-05-11 12:43:32 -07:00
|
|
|
if (prev_prev) {
|
|
|
|
IOSurfaceDecrementUseCount(prev_prev);
|
|
|
|
CFRelease(prev_prev);
|
|
|
|
}
|
2014-05-11 12:10:40 -07:00
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
static void display_capture_video_render(void *data, gs_effect_t *effect)
|
2014-05-11 12:10:40 -07:00
|
|
|
{
|
|
|
|
UNUSED_PARAMETER(effect);
|
|
|
|
|
|
|
|
struct display_capture *dc = data;
|
|
|
|
|
2014-08-19 15:38:26 -07:00
|
|
|
if (!dc->tex || (requires_window(dc->crop) && !dc->on_screen))
|
2014-05-11 12:10:40 -07:00
|
|
|
return;
|
2014-01-16 17:38:35 -08:00
|
|
|
|
2014-08-19 15:38:26 -07:00
|
|
|
gs_vertexbuffer_flush(dc->vertbuf);
|
|
|
|
gs_load_vertexbuffer(dc->vertbuf);
|
|
|
|
gs_load_indexbuffer(NULL);
|
2014-03-23 13:05:43 -07:00
|
|
|
gs_load_samplerstate(dc->sampler, 0);
|
2014-10-03 14:48:55 -07:00
|
|
|
gs_technique_t *tech = gs_effect_get_technique(dc->effect, "Draw");
|
|
|
|
gs_effect_set_texture(gs_effect_get_param_by_name(dc->effect, "image"),
|
2014-03-23 13:05:43 -07:00
|
|
|
dc->tex);
|
2014-08-07 23:42:07 -07:00
|
|
|
gs_technique_begin(tech);
|
|
|
|
gs_technique_begin_pass(tech, 0);
|
2014-01-16 17:38:35 -08:00
|
|
|
|
2014-08-19 15:38:26 -07:00
|
|
|
gs_draw(GS_TRISTRIP, 0, 4);
|
2014-01-16 17:38:35 -08:00
|
|
|
|
2014-08-07 23:42:07 -07:00
|
|
|
gs_technique_end_pass(tech);
|
|
|
|
gs_technique_end(tech);
|
2014-03-23 13:05:43 -07:00
|
|
|
}
|
2014-02-14 14:13:36 -08:00
|
|
|
|
2015-09-16 01:30:51 -07:00
|
|
|
static const char *display_capture_getname(void *unused)
|
2014-03-23 13:05:43 -07:00
|
|
|
{
|
2015-09-16 01:30:51 -07:00
|
|
|
UNUSED_PARAMETER(unused);
|
2014-07-09 22:12:57 -07:00
|
|
|
return obs_module_text("DisplayCapture");
|
2014-03-23 13:05:43 -07:00
|
|
|
}
|
|
|
|
|
2014-08-19 15:38:26 -07:00
|
|
|
#define CROPPED_LENGTH(rect, origin_, length) \
|
|
|
|
fabs((rect ## .size. ## length - dc->crop_rect.size. ## length) - \
|
|
|
|
(rect ## .origin. ## origin_ + dc->crop_rect.origin. ## origin_))
|
|
|
|
|
2014-03-23 13:05:43 -07:00
|
|
|
static uint32_t display_capture_getwidth(void *data)
|
|
|
|
{
|
|
|
|
struct display_capture *dc = data;
|
2014-08-19 15:38:26 -07:00
|
|
|
|
|
|
|
float crop = dc->crop_rect.origin.x + dc->crop_rect.size.width;
|
|
|
|
switch (dc->crop) {
|
|
|
|
case CROP_NONE:
|
|
|
|
return dc->frame.size.width;
|
|
|
|
|
|
|
|
case CROP_MANUAL:
|
|
|
|
return fabs(dc->frame.size.width - crop);
|
|
|
|
|
|
|
|
case CROP_TO_WINDOW:
|
|
|
|
return dc->window_rect.size.width;
|
|
|
|
|
|
|
|
case CROP_TO_WINDOW_AND_MANUAL:
|
|
|
|
return fabs(dc->window_rect.size.width - crop);
|
|
|
|
|
|
|
|
case CROP_INVALID:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return 0;
|
2014-03-23 13:05:43 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static uint32_t display_capture_getheight(void *data)
|
|
|
|
{
|
|
|
|
struct display_capture *dc = data;
|
2014-08-19 15:38:26 -07:00
|
|
|
|
|
|
|
float crop = dc->crop_rect.origin.y + dc->crop_rect.size.height;
|
|
|
|
switch (dc->crop) {
|
|
|
|
case CROP_NONE:
|
|
|
|
return dc->frame.size.height;
|
|
|
|
|
|
|
|
case CROP_MANUAL:
|
|
|
|
return fabs(dc->frame.size.height - crop);
|
|
|
|
|
|
|
|
case CROP_TO_WINDOW:
|
|
|
|
return dc->window_rect.size.height;
|
|
|
|
|
|
|
|
case CROP_TO_WINDOW_AND_MANUAL:
|
|
|
|
return fabs(dc->window_rect.size.height - crop);
|
|
|
|
|
|
|
|
case CROP_INVALID:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return 0;
|
2014-03-23 13:05:43 -07:00
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
static void display_capture_defaults(obs_data_t *settings)
|
2014-03-23 13:05:43 -07:00
|
|
|
{
|
|
|
|
obs_data_set_default_int(settings, "display", 0);
|
2014-04-16 08:20:01 -07:00
|
|
|
obs_data_set_default_bool(settings, "show_cursor", true);
|
2014-08-19 15:38:26 -07:00
|
|
|
obs_data_set_default_int(settings, "crop_mode", CROP_NONE);
|
|
|
|
|
|
|
|
window_defaults(settings);
|
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
void load_crop_mode(enum crop_mode *mode, obs_data_t *settings)
|
2014-08-19 15:38:26 -07:00
|
|
|
{
|
|
|
|
*mode = obs_data_get_int(settings, "crop_mode");
|
|
|
|
if (!crop_mode_valid(*mode))
|
|
|
|
*mode = CROP_NONE;
|
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
void load_crop(struct display_capture *dc, obs_data_t *settings)
|
2014-08-19 15:38:26 -07:00
|
|
|
{
|
|
|
|
load_crop_mode(&dc->crop, settings);
|
|
|
|
|
|
|
|
#define CROP_VAR_NAME(var, mode) (mode "." #var)
|
|
|
|
#define LOAD_CROP_VAR(var, mode) \
|
|
|
|
dc->crop_rect.var = obs_data_get_double(settings, \
|
|
|
|
CROP_VAR_NAME(var, mode));
|
|
|
|
switch (dc->crop) {
|
|
|
|
case CROP_MANUAL:
|
|
|
|
LOAD_CROP_VAR(origin.x, "manual");
|
|
|
|
LOAD_CROP_VAR(origin.y, "manual");
|
|
|
|
LOAD_CROP_VAR(size.width, "manual");
|
|
|
|
LOAD_CROP_VAR(size.height, "manual");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CROP_TO_WINDOW_AND_MANUAL:
|
|
|
|
LOAD_CROP_VAR(origin.x, "window");
|
|
|
|
LOAD_CROP_VAR(origin.y, "window");
|
|
|
|
LOAD_CROP_VAR(size.width, "window");
|
|
|
|
LOAD_CROP_VAR(size.height, "window");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CROP_NONE:
|
|
|
|
case CROP_TO_WINDOW:
|
|
|
|
case CROP_INVALID:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
#undef LOAD_CROP_VAR
|
2014-01-16 17:38:35 -08:00
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
static void display_capture_update(void *data, obs_data_t *settings)
|
2014-01-16 17:38:35 -08:00
|
|
|
{
|
2014-03-23 13:05:43 -07:00
|
|
|
struct display_capture *dc = data;
|
2014-08-19 15:38:26 -07:00
|
|
|
|
|
|
|
load_crop(dc, settings);
|
|
|
|
|
|
|
|
if (requires_window(dc->crop))
|
|
|
|
update_window(&dc->window, settings);
|
|
|
|
|
2014-08-05 11:09:29 -07:00
|
|
|
unsigned display = obs_data_get_int(settings, "display");
|
|
|
|
bool show_cursor = obs_data_get_bool(settings, "show_cursor");
|
2014-04-16 08:20:01 -07:00
|
|
|
if (dc->display == display && dc->hide_cursor != show_cursor)
|
2014-03-23 13:05:43 -07:00
|
|
|
return;
|
|
|
|
|
2014-08-04 05:48:58 -07:00
|
|
|
obs_enter_graphics();
|
2014-03-23 13:05:43 -07:00
|
|
|
|
|
|
|
destroy_display_stream(dc);
|
|
|
|
dc->display = display;
|
2014-04-16 08:20:01 -07:00
|
|
|
dc->hide_cursor = !show_cursor;
|
2014-03-23 13:05:43 -07:00
|
|
|
init_display_stream(dc);
|
|
|
|
|
2014-08-04 05:48:58 -07:00
|
|
|
obs_leave_graphics();
|
2014-01-16 17:38:35 -08:00
|
|
|
}
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
static bool switch_crop_mode(obs_properties_t *props, obs_property_t *p,
|
|
|
|
obs_data_t *settings)
|
2014-08-19 15:38:26 -07:00
|
|
|
{
|
|
|
|
UNUSED_PARAMETER(p);
|
|
|
|
|
|
|
|
enum crop_mode crop;
|
|
|
|
load_crop_mode(&crop, settings);
|
|
|
|
|
|
|
|
const char *name;
|
|
|
|
bool visible;
|
|
|
|
#define LOAD_CROP_VAR(var, mode) \
|
|
|
|
name = CROP_VAR_NAME(var, mode); \
|
|
|
|
obs_property_set_visible(obs_properties_get(props, name), visible);
|
|
|
|
|
|
|
|
visible = crop == CROP_MANUAL;
|
|
|
|
LOAD_CROP_VAR(origin.x, "manual");
|
|
|
|
LOAD_CROP_VAR(origin.y, "manual");
|
|
|
|
LOAD_CROP_VAR(size.width, "manual");
|
|
|
|
LOAD_CROP_VAR(size.height, "manual");
|
|
|
|
|
|
|
|
visible = crop == CROP_TO_WINDOW_AND_MANUAL;
|
|
|
|
LOAD_CROP_VAR(origin.x, "window");
|
|
|
|
LOAD_CROP_VAR(origin.y, "window");
|
|
|
|
LOAD_CROP_VAR(size.width, "window");
|
|
|
|
LOAD_CROP_VAR(size.height, "window");
|
|
|
|
#undef LOAD_CROP_VAR
|
|
|
|
|
|
|
|
show_window_properties(props, visible || crop == CROP_TO_WINDOW);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char *crop_names[] = {
|
|
|
|
"CropMode.None",
|
|
|
|
"CropMode.Manual",
|
|
|
|
"CropMode.ToWindow",
|
|
|
|
"CropMode.ToWindowAndManual"
|
|
|
|
};
|
|
|
|
|
|
|
|
#ifndef COUNTOF
|
|
|
|
#define COUNTOF(x) (sizeof(x)/sizeof(x[0]))
|
|
|
|
#endif
|
2014-09-29 08:36:13 -07:00
|
|
|
static obs_properties_t *display_capture_properties(void *unused)
|
2014-01-16 17:38:35 -08:00
|
|
|
{
|
2014-09-29 08:36:13 -07:00
|
|
|
UNUSED_PARAMETER(unused);
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
obs_properties_t *props = obs_properties_create();
|
2014-03-23 13:05:43 -07:00
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
obs_property_t *list = obs_properties_add_list(props,
|
2014-07-09 22:12:57 -07:00
|
|
|
"display", obs_module_text("DisplayCapture.Display"),
|
2014-03-23 13:05:43 -07:00
|
|
|
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
|
|
|
|
|
2014-04-04 00:30:37 -07:00
|
|
|
for (unsigned i = 0; i < [NSScreen screens].count; i++) {
|
2014-03-23 13:05:43 -07:00
|
|
|
char buf[10];
|
|
|
|
sprintf(buf, "%u", i);
|
2014-04-04 00:30:37 -07:00
|
|
|
obs_property_list_add_int(list, buf, i);
|
2014-03-23 13:05:43 -07:00
|
|
|
}
|
|
|
|
|
2014-07-09 22:12:57 -07:00
|
|
|
obs_properties_add_bool(props, "show_cursor",
|
|
|
|
obs_module_text("DisplayCapture.ShowCursor"));
|
2014-04-16 08:20:01 -07:00
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
obs_property_t *crop = obs_properties_add_list(props, "crop_mode",
|
2014-08-19 15:38:26 -07:00
|
|
|
obs_module_text("CropMode"),
|
|
|
|
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
|
|
|
|
obs_property_set_modified_callback(crop, switch_crop_mode);
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < COUNTOF(crop_names); i++) {
|
|
|
|
const char *name = obs_module_text(crop_names[i]);
|
|
|
|
obs_property_list_add_int(crop, name, i);
|
|
|
|
}
|
|
|
|
|
|
|
|
add_window_properties(props);
|
|
|
|
show_window_properties(props, false);
|
|
|
|
|
2014-09-25 17:44:05 -07:00
|
|
|
obs_property_t *p;
|
2014-08-19 15:38:26 -07:00
|
|
|
const char *name;
|
|
|
|
float min;
|
|
|
|
#define LOAD_CROP_VAR(var, mode) \
|
|
|
|
name = CROP_VAR_NAME(var, mode); \
|
|
|
|
p = obs_properties_add_float(props, name, \
|
|
|
|
obs_module_text("Crop."#var), min, 4096.f, .5f); \
|
|
|
|
obs_property_set_visible(p, false);
|
|
|
|
|
|
|
|
min = 0.f;
|
|
|
|
LOAD_CROP_VAR(origin.x, "manual");
|
|
|
|
LOAD_CROP_VAR(origin.y, "manual");
|
|
|
|
LOAD_CROP_VAR(size.width, "manual");
|
|
|
|
LOAD_CROP_VAR(size.height, "manual");
|
|
|
|
|
|
|
|
min = -4096.f;
|
|
|
|
LOAD_CROP_VAR(origin.x, "window");
|
|
|
|
LOAD_CROP_VAR(origin.y, "window");
|
|
|
|
LOAD_CROP_VAR(size.width, "window");
|
|
|
|
LOAD_CROP_VAR(size.height, "window");
|
|
|
|
#undef LOAD_CROP_VAR
|
|
|
|
|
2014-03-23 13:05:43 -07:00
|
|
|
return props;
|
2014-01-16 17:38:35 -08:00
|
|
|
}
|
Revamp API and start using doxygen
The API used to be designed in such a way to where it would expect
exports for each individual source/output/encoder/etc. You would export
functions for each and it would automatically load those functions based
on a specific naming scheme from the module.
The idea behind this was that I wanted to limit the usage of structures
in the API so only functions could be used. It was an interesting idea
in theory, but this idea turned out to be flawed in a number of ways:
1.) Requiring exports to create sources/outputs/encoders/etc meant that
you could not create them by any other means, which meant that
things like faruton's .net plugin would become difficult.
2.) Export function declarations could not be checked, therefore if you
created a function with the wrong parameters and parameter types,
the compiler wouldn't know how to check for that.
3.) Required overly complex load functions in libobs just to handle it.
It makes much more sense to just have a load function that you call
manually. Complexity is the bane of all good programs.
4.) It required that you have functions of specific names, which looked
and felt somewhat unsightly.
So, to fix these issues, I replaced it with a more commonly used API
scheme, seen commonly in places like kernels and typical C libraries
with abstraction. You simply create a structure that contains the
callback definitions, and you pass it to a function to register that
definition (such as obs_register_source), which you call in the
obs_module_load of the module.
It will also automatically check the structure size and ensure that it
only loads the required values if the structure happened to add new
values in an API change.
The "main" source file for each module must include obs-module.h, and
must use OBS_DECLARE_MODULE() within that source file.
Also, started writing some doxygen documentation in to the main library
headers. Will add more detailed documentation as I go.
2014-02-12 07:04:50 -08:00
|
|
|
|
2014-03-23 13:05:43 -07:00
|
|
|
struct obs_source_info display_capture_info = {
|
2014-08-04 21:27:52 -07:00
|
|
|
.id = "display_capture",
|
|
|
|
.type = OBS_SOURCE_TYPE_INPUT,
|
|
|
|
.get_name = display_capture_getname,
|
2014-03-23 13:05:43 -07:00
|
|
|
|
2014-08-04 21:27:52 -07:00
|
|
|
.create = display_capture_create,
|
|
|
|
.destroy = display_capture_destroy,
|
2014-03-23 13:05:43 -07:00
|
|
|
|
2016-01-12 16:25:47 -08:00
|
|
|
.output_flags = OBS_SOURCE_VIDEO | OBS_SOURCE_CUSTOM_DRAW |
|
|
|
|
OBS_SOURCE_DO_NOT_DUPLICATE,
|
2014-08-04 21:27:52 -07:00
|
|
|
.video_tick = display_capture_video_tick,
|
|
|
|
.video_render = display_capture_video_render,
|
2014-03-23 13:05:43 -07:00
|
|
|
|
2014-08-04 21:27:52 -07:00
|
|
|
.get_width = display_capture_getwidth,
|
|
|
|
.get_height = display_capture_getheight,
|
2014-03-23 13:05:43 -07:00
|
|
|
|
2014-08-04 21:27:52 -07:00
|
|
|
.get_defaults = display_capture_defaults,
|
|
|
|
.get_properties = display_capture_properties,
|
|
|
|
.update = display_capture_update,
|
Revamp API and start using doxygen
The API used to be designed in such a way to where it would expect
exports for each individual source/output/encoder/etc. You would export
functions for each and it would automatically load those functions based
on a specific naming scheme from the module.
The idea behind this was that I wanted to limit the usage of structures
in the API so only functions could be used. It was an interesting idea
in theory, but this idea turned out to be flawed in a number of ways:
1.) Requiring exports to create sources/outputs/encoders/etc meant that
you could not create them by any other means, which meant that
things like faruton's .net plugin would become difficult.
2.) Export function declarations could not be checked, therefore if you
created a function with the wrong parameters and parameter types,
the compiler wouldn't know how to check for that.
3.) Required overly complex load functions in libobs just to handle it.
It makes much more sense to just have a load function that you call
manually. Complexity is the bane of all good programs.
4.) It required that you have functions of specific names, which looked
and felt somewhat unsightly.
So, to fix these issues, I replaced it with a more commonly used API
scheme, seen commonly in places like kernels and typical C libraries
with abstraction. You simply create a structure that contains the
callback definitions, and you pass it to a function to register that
definition (such as obs_register_source), which you call in the
obs_module_load of the module.
It will also automatically check the structure size and ensure that it
only loads the required values if the structure happened to add new
values in an API change.
The "main" source file for each module must include obs-module.h, and
must use OBS_DECLARE_MODULE() within that source file.
Also, started writing some doxygen documentation in to the main library
headers. Will add more detailed documentation as I go.
2014-02-12 07:04:50 -08:00
|
|
|
};
|