Implement encoder interface (still preliminary)
- Implement OBS encoder interface. It was previously incomplete, but now is reaching some level of completion, though probably should still be considered preliminary. I had originally implemented it so that encoders only have a 'reset' function to reset their parameters, but I felt that having both a 'start' and 'stop' function would be useful. Encoders are now assigned to a specific video/audio media output each rather than implicitely assigned to the main obs video/audio contexts. This allows separate encoder contexts that aren't necessarily assigned to the main video/audio context (which is useful for things such as recording specific sources). Will probably have to do this for regular obs outputs as well. When creating an encoder, you must now explicitely state whether that encoder is an audio or video encoder. Audio and video can optionally be automatically converted depending on what the encoder specifies. When something 'attaches' to an encoder, the first attachment starts the encoder, and the encoder automatically attaches to the media output context associated with it. Subsequent attachments won't have the same effect, they will just start receiving the same encoder data when the next keyframe plays (along with SEI if any). When detaching from the encoder, the last detachment will fully stop the encoder and detach the encoder from the media output context associated with the encoder. SEI must actually be exported separately; because new encoder attachments may not always be at the beginning of the stream, the first keyframe they get must have that SEI data in it. If the encoder has SEI data, it needs only add one small function to simply query that SEI data, and then that data will be handled automatically by libobs for all subsequent encoder attachments. - Implement x264 encoder plugin, move x264 files to separate plugin to separate necessary dependencies. - Change video/audio frame output structures to not use const qualifiers to prevent issues with non-const function usage elsewhere. This was an issue when writing the x264 encoder, as the x264 encoder expects non-const frame data. Change stagesurf_map to return a non-const data type to prevent this as well. - Change full range parameter of video scaler to be an enum rather than boolean
This commit is contained in:
parent
04d07831cc
commit
fd37d9e9a8
@ -150,7 +150,7 @@ EXPORT void stagesurface_destroy(stagesurf_t stagesurf);
|
||||
EXPORT uint32_t stagesurface_getwidth(stagesurf_t stagesurf);
|
||||
EXPORT uint32_t stagesurface_getheight(stagesurf_t stagesurf);
|
||||
EXPORT enum gs_color_format stagesurface_getcolorformat(stagesurf_t stagesurf);
|
||||
EXPORT bool stagesurface_map(stagesurf_t stagesurf, const uint8_t **data,
|
||||
EXPORT bool stagesurface_map(stagesurf_t stagesurf, uint8_t **data,
|
||||
uint32_t *linesize);
|
||||
EXPORT void stagesurface_unmap(stagesurf_t stagesurf);
|
||||
|
||||
|
@ -1552,8 +1552,7 @@ enum gs_color_format stagesurface_getcolorformat(stagesurf_t stagesurf)
|
||||
return stagesurf->format;
|
||||
}
|
||||
|
||||
bool stagesurface_map(stagesurf_t stagesurf, const uint8_t **data,
|
||||
uint32_t *linesize)
|
||||
bool stagesurface_map(stagesurf_t stagesurf, uint8_t **data, uint32_t *linesize)
|
||||
{
|
||||
D3D11_MAPPED_SUBRESOURCE map;
|
||||
if (FAILED(stagesurf->device->context->Map(stagesurf->texture, 0,
|
||||
|
@ -145,7 +145,7 @@ EXPORT void stagesurface_destroy(stagesurf_t stagesurf);
|
||||
EXPORT uint32_t stagesurface_getwidth(stagesurf_t stagesurf);
|
||||
EXPORT uint32_t stagesurface_getheight(stagesurf_t stagesurf);
|
||||
EXPORT enum gs_color_format stagesurface_getcolorformat(stagesurf_t stagesurf);
|
||||
EXPORT bool stagesurface_map(stagesurf_t stagesurf, const uint8_t **data,
|
||||
EXPORT bool stagesurface_map(stagesurf_t stagesurf, uint8_t **data,
|
||||
uint32_t *linesize);
|
||||
EXPORT void stagesurface_unmap(stagesurf_t stagesurf);
|
||||
|
||||
|
@ -199,8 +199,7 @@ enum gs_color_format stagesurface_getcolorformat(stagesurf_t stagesurf)
|
||||
return stagesurf->format;
|
||||
}
|
||||
|
||||
bool stagesurface_map(stagesurf_t stagesurf, const uint8_t **data,
|
||||
uint32_t *linesize)
|
||||
bool stagesurface_map(stagesurf_t stagesurf, uint8_t **data, uint32_t *linesize)
|
||||
{
|
||||
if (!gl_bind_buffer(GL_PIXEL_PACK_BUFFER, stagesurf->pack_buffer))
|
||||
goto fail;
|
||||
|
@ -158,7 +158,7 @@ struct gs_exports {
|
||||
enum gs_color_format (*stagesurface_getcolorformat)(
|
||||
stagesurf_t stagesurf);
|
||||
bool (*stagesurface_map)(stagesurf_t stagesurf,
|
||||
const uint8_t **data, uint32_t *linesize);
|
||||
uint8_t **data, uint32_t *linesize);
|
||||
void (*stagesurface_unmap)(stagesurf_t stagesurf);
|
||||
|
||||
void (*zstencil_destroy)(zstencil_t zstencil);
|
||||
|
@ -1805,8 +1805,7 @@ enum gs_color_format stagesurface_getcolorformat(stagesurf_t stagesurf)
|
||||
return graphics->exports.stagesurface_getcolorformat(stagesurf);
|
||||
}
|
||||
|
||||
bool stagesurface_map(stagesurf_t stagesurf, const uint8_t **data,
|
||||
uint32_t *linesize)
|
||||
bool stagesurface_map(stagesurf_t stagesurf, uint8_t **data, uint32_t *linesize)
|
||||
{
|
||||
graphics_t graphics = thread_graphics;
|
||||
if (!graphics || !stagesurf) return false;
|
||||
|
@ -659,7 +659,7 @@ EXPORT void stagesurface_destroy(stagesurf_t stagesurf);
|
||||
EXPORT uint32_t stagesurface_getwidth(stagesurf_t stagesurf);
|
||||
EXPORT uint32_t stagesurface_getheight(stagesurf_t stagesurf);
|
||||
EXPORT enum gs_color_format stagesurface_getcolorformat(stagesurf_t stagesurf);
|
||||
EXPORT bool stagesurface_map(stagesurf_t stagesurf, const uint8_t **data,
|
||||
EXPORT bool stagesurface_map(stagesurf_t stagesurf, uint8_t **data,
|
||||
uint32_t *linesize);
|
||||
EXPORT void stagesurface_unmap(stagesurf_t stagesurf);
|
||||
|
||||
|
@ -34,7 +34,7 @@ struct audio_input {
|
||||
struct audio_convert_info conversion;
|
||||
audio_resampler_t resampler;
|
||||
|
||||
void (*callback)(void *param, const struct audio_data *data);
|
||||
void (*callback)(void *param, struct audio_data *data);
|
||||
void *param;
|
||||
};
|
||||
|
||||
@ -337,7 +337,8 @@ static bool resample_audio_output(struct audio_input *input,
|
||||
|
||||
success = audio_resampler_resample(input->resampler,
|
||||
output, &frames, &offset,
|
||||
data->data, data->frames);
|
||||
(const uint8_t *const *)data->data,
|
||||
data->frames);
|
||||
|
||||
for (size_t i = 0; i < MAX_AV_PLANES; i++)
|
||||
data->data[i] = output[i];
|
||||
@ -455,7 +456,7 @@ static void *audio_thread(void *param)
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static size_t audio_get_input_idx(audio_t video,
|
||||
void (*callback)(void *param, const struct audio_data *data),
|
||||
void (*callback)(void *param, struct audio_data *data),
|
||||
void *param)
|
||||
{
|
||||
for (size_t i = 0; i < video->inputs.num; i++) {
|
||||
@ -500,7 +501,7 @@ static inline bool audio_input_init(struct audio_input *input,
|
||||
|
||||
bool audio_output_connect(audio_t audio,
|
||||
const struct audio_convert_info *conversion,
|
||||
void (*callback)(void *param, const struct audio_data *data),
|
||||
void (*callback)(void *param, struct audio_data *data),
|
||||
void *param)
|
||||
{
|
||||
bool success = false;
|
||||
@ -542,7 +543,7 @@ bool audio_output_connect(audio_t audio,
|
||||
}
|
||||
|
||||
void audio_output_disconnect(audio_t audio,
|
||||
void (*callback)(void *param, const struct audio_data *data),
|
||||
void (*callback)(void *param, struct audio_data *data),
|
||||
void *param)
|
||||
{
|
||||
if (!audio) return;
|
||||
|
@ -63,7 +63,7 @@ enum speaker_layout {
|
||||
};
|
||||
|
||||
struct audio_data {
|
||||
const uint8_t *data[MAX_AV_PLANES];
|
||||
uint8_t *data[MAX_AV_PLANES];
|
||||
uint32_t frames;
|
||||
uint64_t timestamp;
|
||||
float volume;
|
||||
@ -174,10 +174,10 @@ EXPORT void audio_output_close(audio_t audio);
|
||||
|
||||
EXPORT bool audio_output_connect(audio_t video,
|
||||
const struct audio_convert_info *conversion,
|
||||
void (*callback)(void *param, const struct audio_data *data),
|
||||
void (*callback)(void *param, struct audio_data *data),
|
||||
void *param);
|
||||
EXPORT void audio_output_disconnect(audio_t video,
|
||||
void (*callback)(void *param, const struct audio_data *data),
|
||||
void (*callback)(void *param, struct audio_data *data),
|
||||
void *param);
|
||||
|
||||
EXPORT bool audio_output_active(audio_t audio);
|
||||
|
@ -34,7 +34,7 @@ struct video_input {
|
||||
struct video_frame frame[MAX_CONVERT_BUFFERS];
|
||||
int cur_frame;
|
||||
|
||||
void (*callback)(void *param, const struct video_data *frame);
|
||||
void (*callback)(void *param, struct video_data *frame);
|
||||
void *param;
|
||||
};
|
||||
|
||||
@ -91,7 +91,8 @@ static inline bool scale_video_output(struct video_input *input,
|
||||
|
||||
success = video_scaler_scale(input->scaler,
|
||||
frame->data, frame->linesize,
|
||||
data->data, data->linesize);
|
||||
(const uint8_t * const*)data->data,
|
||||
data->linesize);
|
||||
|
||||
if (success) {
|
||||
for (size_t i = 0; i < MAX_AV_PLANES; i++) {
|
||||
@ -209,7 +210,7 @@ void video_output_close(video_t video)
|
||||
}
|
||||
|
||||
static size_t video_get_input_idx(video_t video,
|
||||
void (*callback)(void *param, const struct video_data *frame),
|
||||
void (*callback)(void *param, struct video_data *frame),
|
||||
void *param)
|
||||
{
|
||||
for (size_t i = 0; i < video->inputs.num; i++) {
|
||||
@ -259,7 +260,7 @@ static inline bool video_input_init(struct video_input *input,
|
||||
|
||||
bool video_output_connect(video_t video,
|
||||
const struct video_scale_info *conversion,
|
||||
void (*callback)(void *param, const struct video_data *frame),
|
||||
void (*callback)(void *param, struct video_data *frame),
|
||||
void *param)
|
||||
{
|
||||
bool success = false;
|
||||
@ -300,7 +301,7 @@ bool video_output_connect(video_t video,
|
||||
}
|
||||
|
||||
void video_output_disconnect(video_t video,
|
||||
void (*callback)(void *param, const struct video_data *frame),
|
||||
void (*callback)(void *param, struct video_data *frame),
|
||||
void *param)
|
||||
{
|
||||
if (!video || !callback)
|
||||
|
@ -47,7 +47,7 @@ enum video_format {
|
||||
};
|
||||
|
||||
struct video_data {
|
||||
const uint8_t *data[MAX_AV_PLANES];
|
||||
uint8_t *data[MAX_AV_PLANES];
|
||||
uint32_t linesize[MAX_AV_PLANES];
|
||||
uint64_t timestamp;
|
||||
};
|
||||
@ -82,24 +82,30 @@ static inline bool format_is_yuv(enum video_format format)
|
||||
}
|
||||
|
||||
enum video_scale_type {
|
||||
VIDEO_SCALE_POINT = 0,
|
||||
VIDEO_SCALE_FAST_BILINEAR = 1,
|
||||
VIDEO_SCALE_DEFAULT = VIDEO_SCALE_FAST_BILINEAR,
|
||||
VIDEO_SCALE_BILINEAR = 2,
|
||||
VIDEO_SCALE_BICUBIC = 3,
|
||||
VIDEO_SCALE_DEFAULT,
|
||||
VIDEO_SCALE_POINT,
|
||||
VIDEO_SCALE_FAST_BILINEAR,
|
||||
VIDEO_SCALE_BILINEAR,
|
||||
VIDEO_SCALE_BICUBIC,
|
||||
};
|
||||
|
||||
enum video_colorspace {
|
||||
VIDEO_CS_601 = 0,
|
||||
VIDEO_CS_DEFAULT = VIDEO_CS_601,
|
||||
VIDEO_CS_709 = 1,
|
||||
VIDEO_CS_DEFAULT,
|
||||
VIDEO_CS_601,
|
||||
VIDEO_CS_709,
|
||||
};
|
||||
|
||||
enum video_range_type {
|
||||
VIDEO_RANGE_DEFAULT,
|
||||
VIDEO_RANGE_PARTIAL,
|
||||
VIDEO_RANGE_FULL
|
||||
};
|
||||
|
||||
struct video_scale_info {
|
||||
enum video_format format;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
bool full_range;
|
||||
enum video_range_type range;
|
||||
enum video_colorspace colorspace;
|
||||
};
|
||||
|
||||
@ -112,10 +118,10 @@ EXPORT void video_output_close(video_t video);
|
||||
|
||||
EXPORT bool video_output_connect(video_t video,
|
||||
const struct video_scale_info *conversion,
|
||||
void (*callback)(void *param, const struct video_data *frame),
|
||||
void (*callback)(void *param, struct video_data *frame),
|
||||
void *param);
|
||||
EXPORT void video_output_disconnect(video_t video,
|
||||
void (*callback)(void *param, const struct video_data *frame),
|
||||
void (*callback)(void *param, struct video_data *frame),
|
||||
void *param);
|
||||
|
||||
EXPORT bool video_output_active(video_t video);
|
||||
|
@ -46,6 +46,7 @@ static inline enum AVPixelFormat get_ffmpeg_video_format(
|
||||
static inline int get_ffmpeg_scale_type(enum video_scale_type type)
|
||||
{
|
||||
switch (type) {
|
||||
case VIDEO_SCALE_DEFAULT: return SWS_FAST_BILINEAR;
|
||||
case VIDEO_SCALE_POINT: return SWS_POINT;
|
||||
case VIDEO_SCALE_FAST_BILINEAR: return SWS_FAST_BILINEAR;
|
||||
case VIDEO_SCALE_BILINEAR: return SWS_BILINEAR | SWS_AREA;
|
||||
@ -58,13 +59,25 @@ static inline int get_ffmpeg_scale_type(enum video_scale_type type)
|
||||
static inline const int *get_ffmpeg_coeffs(enum video_colorspace cs)
|
||||
{
|
||||
switch (cs) {
|
||||
case VIDEO_CS_601: return sws_getCoefficients(SWS_CS_ITU601);
|
||||
case VIDEO_CS_709: return sws_getCoefficients(SWS_CS_ITU709);
|
||||
case VIDEO_CS_DEFAULT: return sws_getCoefficients(SWS_CS_ITU601);
|
||||
case VIDEO_CS_601: return sws_getCoefficients(SWS_CS_ITU601);
|
||||
case VIDEO_CS_709: return sws_getCoefficients(SWS_CS_ITU709);
|
||||
}
|
||||
|
||||
return sws_getCoefficients(SWS_CS_ITU601);
|
||||
}
|
||||
|
||||
static inline int get_ffmpeg_range_type(enum video_range_type type)
|
||||
{
|
||||
switch (type) {
|
||||
case VIDEO_RANGE_DEFAULT: return 0;
|
||||
case VIDEO_RANGE_PARTIAL: return 0;
|
||||
case VIDEO_RANGE_FULL: return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define FIXED_1_0 (1<<16)
|
||||
|
||||
int video_scaler_create(video_scaler_t *scaler_out,
|
||||
@ -77,6 +90,8 @@ int video_scaler_create(video_scaler_t *scaler_out,
|
||||
int scale_type = get_ffmpeg_scale_type(type);
|
||||
const int *coeff_src = get_ffmpeg_coeffs(src->colorspace);
|
||||
const int *coeff_dst = get_ffmpeg_coeffs(dst->colorspace);
|
||||
int range_src = get_ffmpeg_range_type(src->range);
|
||||
int range_dst = get_ffmpeg_range_type(dst->range);
|
||||
struct video_scaler *scaler;
|
||||
int ret;
|
||||
|
||||
@ -101,8 +116,8 @@ int video_scaler_create(video_scaler_t *scaler_out,
|
||||
}
|
||||
|
||||
ret = sws_setColorspaceDetails(scaler->swscale,
|
||||
coeff_src, src->full_range,
|
||||
coeff_dst, dst->full_range,
|
||||
coeff_src, range_src,
|
||||
coeff_dst, range_dst,
|
||||
0, FIXED_1_0, FIXED_1_0);
|
||||
if (ret < 0) {
|
||||
blog(LOG_DEBUG, "video_scaler_create: "
|
||||
|
@ -33,37 +33,22 @@ static inline struct obs_encoder_info *get_encoder_info(const char *id)
|
||||
const char *obs_encoder_getdisplayname(const char *id, const char *locale)
|
||||
{
|
||||
struct obs_encoder_info *ei = get_encoder_info(id);
|
||||
if (!ei)
|
||||
return NULL;
|
||||
|
||||
return ei->getname(locale);
|
||||
return ei ? ei->getname(locale) : NULL;
|
||||
}
|
||||
|
||||
obs_encoder_t obs_encoder_create(const char *id, const char *name,
|
||||
static bool init_encoder(struct obs_encoder *encoder, const char *name,
|
||||
obs_data_t settings)
|
||||
{
|
||||
struct obs_encoder *encoder;
|
||||
struct obs_encoder_info *ei = get_encoder_info(id);
|
||||
|
||||
if (!ei)
|
||||
return NULL;
|
||||
|
||||
encoder = bzalloc(sizeof(struct obs_encoder));
|
||||
encoder->info = *ei;
|
||||
|
||||
if (pthread_mutex_init(&encoder->data_callbacks_mutex, NULL) != 0) {
|
||||
bfree(encoder);
|
||||
return NULL;
|
||||
}
|
||||
if (pthread_mutex_init(&encoder->callbacks_mutex, NULL) != 0)
|
||||
return false;
|
||||
|
||||
encoder->settings = obs_data_newref(settings);
|
||||
encoder->data = ei->create(encoder->settings, encoder);
|
||||
encoder->data = encoder->info.create(encoder->settings, encoder);
|
||||
|
||||
if (!encoder->data) {
|
||||
pthread_mutex_destroy(&encoder->data_callbacks_mutex);
|
||||
pthread_mutex_destroy(&encoder->callbacks_mutex);
|
||||
obs_data_release(encoder->settings);
|
||||
bfree(encoder);
|
||||
return NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&obs->data.encoders_mutex);
|
||||
@ -71,18 +56,137 @@ obs_encoder_t obs_encoder_create(const char *id, const char *name,
|
||||
pthread_mutex_unlock(&obs->data.encoders_mutex);
|
||||
|
||||
encoder->name = bstrdup(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct obs_encoder *create_encoder(const char *id,
|
||||
enum obs_encoder_type type, const char *name,
|
||||
obs_data_t settings, void *output,
|
||||
uint32_t timebase_num, uint32_t timebase_den)
|
||||
{
|
||||
struct obs_encoder *encoder;
|
||||
struct obs_encoder_info *ei = get_encoder_info(id);
|
||||
bool success;
|
||||
|
||||
if (!ei || ei->type != type)
|
||||
return NULL;
|
||||
|
||||
encoder = bzalloc(sizeof(struct obs_encoder));
|
||||
encoder->info = *ei;
|
||||
encoder->output = output;
|
||||
encoder->timebase_num = timebase_num;
|
||||
encoder->timebase_den = timebase_den;
|
||||
|
||||
success = init_encoder(encoder, name, settings);
|
||||
if (!success) {
|
||||
bfree(encoder);
|
||||
encoder = NULL;
|
||||
}
|
||||
|
||||
return encoder;
|
||||
}
|
||||
|
||||
obs_encoder_t obs_encoder_create_video(const char *id, const char *name,
|
||||
obs_data_t settings, video_t video)
|
||||
{
|
||||
const struct video_output_info *voi;
|
||||
|
||||
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_encoder_create_audio(const char *id, const char *name,
|
||||
obs_data_t settings, audio_t audio)
|
||||
{
|
||||
const struct audio_output_info *aoi;
|
||||
|
||||
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_audio(void *param, struct audio_data *data);
|
||||
|
||||
static inline struct audio_convert_info *get_audio_info(
|
||||
struct obs_encoder *encoder, struct audio_convert_info *info)
|
||||
{
|
||||
if (encoder->info.audio_info)
|
||||
if (encoder->info.audio_info(encoder->data, info))
|
||||
return info;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline struct video_scale_info *get_video_info(
|
||||
struct obs_encoder *encoder, struct video_scale_info *info)
|
||||
{
|
||||
if (encoder->info.video_info)
|
||||
if (encoder->info.video_info(encoder->data, info))
|
||||
return info;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void add_connection(struct obs_encoder *encoder)
|
||||
{
|
||||
struct audio_convert_info audio_info = {0};
|
||||
struct video_scale_info video_info = {0};
|
||||
|
||||
if (encoder->info.type == OBS_ENCODER_AUDIO) {
|
||||
struct audio_convert_info *info = NULL;
|
||||
|
||||
info = get_audio_info(encoder, &audio_info);
|
||||
audio_output_connect(encoder->output, info, receive_audio,
|
||||
encoder);
|
||||
} else {
|
||||
struct video_scale_info *info = NULL;
|
||||
|
||||
info = get_video_info(encoder, &video_info);
|
||||
video_output_connect(encoder->output, info, receive_video,
|
||||
encoder);
|
||||
}
|
||||
}
|
||||
|
||||
static void remove_connection(struct obs_encoder *encoder)
|
||||
{
|
||||
if (encoder->info.type == OBS_ENCODER_AUDIO)
|
||||
audio_output_disconnect(encoder->output, receive_audio,
|
||||
encoder);
|
||||
else
|
||||
video_output_disconnect(encoder->output, receive_video,
|
||||
encoder);
|
||||
}
|
||||
|
||||
static void full_stop(struct obs_encoder *encoder)
|
||||
{
|
||||
if (encoder) {
|
||||
pthread_mutex_lock(&encoder->callbacks_mutex);
|
||||
da_free(encoder->callbacks);
|
||||
remove_connection(encoder);
|
||||
pthread_mutex_unlock(&encoder->callbacks_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
void obs_encoder_destroy(obs_encoder_t encoder)
|
||||
{
|
||||
if (encoder) {
|
||||
full_stop(encoder);
|
||||
|
||||
pthread_mutex_lock(&obs->data.encoders_mutex);
|
||||
da_erase_item(obs->data.encoders, &encoder);
|
||||
pthread_mutex_unlock(&obs->data.encoders_mutex);
|
||||
|
||||
encoder->info.destroy(encoder->data);
|
||||
obs_data_release(encoder->settings);
|
||||
pthread_mutex_destroy(&encoder->callbacks_mutex);
|
||||
bfree(encoder->name);
|
||||
bfree(encoder);
|
||||
}
|
||||
@ -114,33 +218,15 @@ void obs_encoder_update(obs_encoder_t encoder, obs_data_t settings)
|
||||
if (!encoder) return;
|
||||
|
||||
obs_data_apply(encoder->settings, settings);
|
||||
encoder->info.update(encoder->data, encoder->settings);
|
||||
}
|
||||
|
||||
bool obs_encoder_reset(obs_encoder_t encoder, obs_data_t settings)
|
||||
{
|
||||
if (!encoder) return false;
|
||||
|
||||
return encoder->info.reset(encoder->data, settings);
|
||||
}
|
||||
|
||||
bool obs_encoder_encode(obs_encoder_t encoder,
|
||||
const struct encoder_frame *frame,
|
||||
struct encoder_packet *packet, bool *received_packet)
|
||||
{
|
||||
if (!encoder) return false;
|
||||
|
||||
return encoder->info.encode(encoder->data, frame, packet,
|
||||
received_packet);
|
||||
if (encoder->info.update)
|
||||
encoder->info.update(encoder->data, encoder->settings);
|
||||
}
|
||||
|
||||
bool obs_encoder_get_extra_data(obs_encoder_t encoder, uint8_t **extra_data,
|
||||
size_t *size)
|
||||
{
|
||||
if (!encoder) return false;
|
||||
|
||||
if (encoder->info.get_extra_data)
|
||||
return encoder->info.get_extra_data(encoder, extra_data, size);
|
||||
if (encoder && encoder->info.extra_data)
|
||||
return encoder->info.extra_data(encoder, extra_data, size);
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -153,24 +239,216 @@ obs_data_t obs_encoder_get_settings(obs_encoder_t encoder)
|
||||
return encoder->settings;
|
||||
}
|
||||
|
||||
static inline size_t get_callback_idx(
|
||||
struct obs_encoder *encoder,
|
||||
void (*new_packet)(void *param, struct encoder_packet *packet),
|
||||
void *param)
|
||||
{
|
||||
for (size_t i = 0; i < encoder->callbacks.num; i++) {
|
||||
struct encoder_callback *cb = encoder->callbacks.array+i;
|
||||
|
||||
if (cb->new_packet == new_packet && cb->param == param)
|
||||
return i;
|
||||
}
|
||||
|
||||
return DARRAY_INVALID;
|
||||
}
|
||||
|
||||
bool obs_encoder_start(obs_encoder_t encoder,
|
||||
void (*new_packet)(void *param, struct encoder_packet *packet),
|
||||
void *param)
|
||||
{
|
||||
/* TODO: implement */
|
||||
UNUSED_PARAMETER(encoder);
|
||||
UNUSED_PARAMETER(new_packet);
|
||||
UNUSED_PARAMETER(param);
|
||||
return false;
|
||||
struct encoder_callback cb = {false, new_packet, param};
|
||||
bool success = true;
|
||||
bool first = false;
|
||||
|
||||
if (!encoder || !new_packet) return false;
|
||||
|
||||
pthread_mutex_lock(&encoder->callbacks_mutex);
|
||||
|
||||
first = (encoder->callbacks.num == 0);
|
||||
if (first)
|
||||
success = encoder->info.start(encoder->data, encoder->settings);
|
||||
|
||||
if (success) {
|
||||
size_t idx = get_callback_idx(encoder, new_packet, param);
|
||||
if (idx == DARRAY_INVALID)
|
||||
da_push_back(encoder->callbacks, &cb);
|
||||
else
|
||||
success = false;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&encoder->callbacks_mutex);
|
||||
|
||||
if (first) {
|
||||
encoder->cur_pts = 0;
|
||||
add_connection(encoder);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void obs_encoder_stop(obs_encoder_t encoder,
|
||||
void (*new_packet)(void *param, struct encoder_packet *packet),
|
||||
void *param)
|
||||
{
|
||||
/* TODO: implement */
|
||||
UNUSED_PARAMETER(encoder);
|
||||
UNUSED_PARAMETER(new_packet);
|
||||
UNUSED_PARAMETER(param);
|
||||
return;
|
||||
bool last = false;
|
||||
size_t idx;
|
||||
|
||||
if (!encoder) return;
|
||||
|
||||
pthread_mutex_lock(&encoder->callbacks_mutex);
|
||||
|
||||
idx = get_callback_idx(encoder, new_packet, param);
|
||||
if (idx != DARRAY_INVALID) {
|
||||
da_erase(encoder->callbacks, idx);
|
||||
last = (encoder->callbacks.num == 0);
|
||||
|
||||
if (last)
|
||||
encoder->info.stop(encoder->data);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&encoder->callbacks_mutex);
|
||||
|
||||
if (last)
|
||||
remove_connection(encoder);
|
||||
}
|
||||
|
||||
video_t obs_encoder_video(obs_encoder_t encoder)
|
||||
{
|
||||
return (encoder && encoder->info.type == OBS_ENCODER_VIDEO) ?
|
||||
encoder->output : NULL;
|
||||
}
|
||||
|
||||
audio_t obs_encoder_audio(obs_encoder_t encoder)
|
||||
{
|
||||
return (encoder && encoder->info.type == OBS_ENCODER_AUDIO) ?
|
||||
encoder->output : NULL;
|
||||
}
|
||||
|
||||
static inline bool get_sei(struct obs_encoder *encoder,
|
||||
uint8_t **sei, size_t *size)
|
||||
{
|
||||
if (encoder->info.sei_data)
|
||||
return encoder->info.sei_data(encoder->data, sei, size);
|
||||
return false;
|
||||
}
|
||||
|
||||
static void send_first_video_packet(struct obs_encoder *encoder,
|
||||
struct encoder_callback *cb, struct encoder_packet *packet)
|
||||
{
|
||||
struct encoder_packet first_packet;
|
||||
DARRAY(uint8_t) data;
|
||||
uint8_t *sei;
|
||||
size_t size;
|
||||
|
||||
/* always wait for first keyframe */
|
||||
if (!packet->keyframe)
|
||||
return;
|
||||
|
||||
da_init(data);
|
||||
|
||||
if (!get_sei(encoder, &sei, &size)) {
|
||||
cb->new_packet(cb->param, packet);
|
||||
return;
|
||||
}
|
||||
|
||||
da_push_back_array(data, sei, size);
|
||||
da_push_back_array(data, packet->data, packet->size);
|
||||
|
||||
first_packet = *packet;
|
||||
first_packet.data = data.array;
|
||||
first_packet.size = data.num;
|
||||
|
||||
cb->new_packet(cb->param, &first_packet);
|
||||
cb->sent_first_packet = true;
|
||||
|
||||
da_free(data);
|
||||
}
|
||||
|
||||
static inline void send_packet(struct obs_encoder *encoder,
|
||||
struct encoder_callback *cb, struct encoder_packet *packet)
|
||||
{
|
||||
/* include SEI in first video packet */
|
||||
if (encoder->info.type == OBS_ENCODER_VIDEO && !cb->sent_first_packet)
|
||||
send_first_video_packet(encoder, cb, packet);
|
||||
else
|
||||
cb->new_packet(cb->param, packet);
|
||||
}
|
||||
|
||||
static inline void do_encode(struct obs_encoder *encoder,
|
||||
struct encoder_frame *frame, struct encoder_packet *packet)
|
||||
{
|
||||
bool received = false;
|
||||
bool success;
|
||||
|
||||
packet->timebase_num = encoder->timebase_num;
|
||||
packet->timebase_den = encoder->timebase_den;
|
||||
|
||||
success = encoder->info.encode(encoder->data, frame, packet, &received);
|
||||
if (!success) {
|
||||
full_stop(encoder);
|
||||
blog(LOG_ERROR, "Error encoding with encoder '%s'",
|
||||
encoder->name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (received) {
|
||||
pthread_mutex_lock(&encoder->callbacks_mutex);
|
||||
|
||||
for (size_t i = 0; i < encoder->callbacks.num; i++) {
|
||||
struct encoder_callback *cb;
|
||||
|
||||
cb = encoder->callbacks.array+i;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&encoder->callbacks_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
static void receive_video(void *param, struct video_data *frame)
|
||||
{
|
||||
struct obs_encoder *encoder = param;
|
||||
struct encoder_packet packet = {0};
|
||||
struct encoder_frame enc_frame;
|
||||
|
||||
memset(&enc_frame, 0, sizeof(struct encoder_frame));
|
||||
|
||||
for (size_t i = 0; i < MAX_AV_PLANES; i++) {
|
||||
enc_frame.data[i] = frame->data[i];
|
||||
enc_frame.linesize[i] = frame->linesize[i];
|
||||
}
|
||||
|
||||
enc_frame.frames = 1;
|
||||
enc_frame.pts = encoder->cur_pts;
|
||||
|
||||
do_encode(encoder, &enc_frame, &packet);
|
||||
|
||||
encoder->cur_pts += encoder->timebase_num;
|
||||
}
|
||||
|
||||
static void receive_audio(void *param, struct audio_data *data)
|
||||
{
|
||||
struct obs_encoder *encoder = param;
|
||||
struct encoder_packet packet = {0};
|
||||
struct encoder_frame enc_frame;
|
||||
size_t data_size;
|
||||
|
||||
memset(&enc_frame, 0, sizeof(struct encoder_frame));
|
||||
|
||||
data_size = audio_output_blocksize(encoder->output) * data->frames;
|
||||
|
||||
for (size_t i = 0; i < MAX_AV_PLANES; i++) {
|
||||
if (data->data[i]) {
|
||||
enc_frame.data[i] = data->data[i];
|
||||
enc_frame.linesize[i] = (uint32_t)data_size;
|
||||
}
|
||||
}
|
||||
|
||||
enc_frame.frames = data->frames;
|
||||
enc_frame.pts = encoder->cur_pts;
|
||||
|
||||
do_encode(encoder, &enc_frame, &packet);
|
||||
|
||||
encoder->cur_pts += data->frames;
|
||||
}
|
||||
|
@ -19,34 +19,38 @@
|
||||
|
||||
/** Specifies the encoder type */
|
||||
enum obs_encoder_type {
|
||||
OBS_PACKET_AUDIO,
|
||||
OBS_PACKET_VIDEO
|
||||
OBS_ENCODER_AUDIO,
|
||||
OBS_ENCODER_VIDEO
|
||||
};
|
||||
|
||||
/** Encoder output packet */
|
||||
struct encoder_packet {
|
||||
uint8_t *data; /**< Packet data */
|
||||
size_t size; /**< Packet size */
|
||||
uint8_t *data; /**< Packet data */
|
||||
size_t size; /**< Packet size */
|
||||
|
||||
int64_t pts; /**< Presentation timestamp */
|
||||
int64_t dts; /**< Decode timestamp */
|
||||
int64_t pts; /**< Presentation timestamp */
|
||||
int64_t dts; /**< Decode timestamp */
|
||||
|
||||
enum obs_encoder_type type; /**< Encoder type */
|
||||
int32_t timebase_num; /**< Timebase numerator */
|
||||
int32_t timebase_den; /**< Timebase denominator */
|
||||
|
||||
enum obs_encoder_type type; /**< Encoder type */
|
||||
|
||||
bool keyframe; /**< Is a keyframe */
|
||||
|
||||
/**
|
||||
* Packet priority
|
||||
*
|
||||
* This is generally use by video encoders to specify the priority
|
||||
* of the packet. If this frame is dropped, it will have to wait for
|
||||
* another packet of drop_priority.
|
||||
* of the packet.
|
||||
*/
|
||||
int priority;
|
||||
|
||||
/**
|
||||
* Dropped packet priority
|
||||
*
|
||||
* If this packet is dropped, the next packet must be of this priority
|
||||
* or higher to continue transmission.
|
||||
* If this packet needs to be dropped, the next packet must be of this
|
||||
* priority or higher to continue transmission.
|
||||
*/
|
||||
int drop_priority;
|
||||
};
|
||||
@ -83,6 +87,12 @@ struct obs_encoder_info {
|
||||
/** Specifies the named identifier of this encoder */
|
||||
const char *id;
|
||||
|
||||
/** Specifies the encoder type (video or audio) */
|
||||
enum obs_encoder_type type;
|
||||
|
||||
/** Specifies the codec */
|
||||
const char *codec;
|
||||
|
||||
/**
|
||||
* Gets the full translated name of this encoder
|
||||
*
|
||||
@ -108,13 +118,13 @@ struct obs_encoder_info {
|
||||
void (*destroy)(void *data);
|
||||
|
||||
/**
|
||||
* Resets the encoder with the specified settings
|
||||
* Starts the encoder
|
||||
*
|
||||
* @param data Data associated with this encoder context
|
||||
* @param settings New settings for the encoder
|
||||
* @param settings Settings for the encoder
|
||||
* @return true if successful, false otherwise
|
||||
*/
|
||||
bool (*reset)(void *data, obs_data_t settings);
|
||||
bool (*start)(void *data, obs_data_t settings);
|
||||
|
||||
/**
|
||||
* Encodes frame(s), and outputs encoded packets as they become
|
||||
@ -128,7 +138,7 @@ struct obs_encoder_info {
|
||||
* false otherwise
|
||||
* @return true if successful, false otherwise.
|
||||
*/
|
||||
int (*encode)(void *data, const struct encoder_frame *frame,
|
||||
bool (*encode)(void *data, struct encoder_frame *frame,
|
||||
struct encoder_packet *packet, bool *received_packet);
|
||||
|
||||
/* ----------------------------------------------------------------- */
|
||||
@ -141,6 +151,13 @@ struct obs_encoder_info {
|
||||
*/
|
||||
void (*defaults)(obs_data_t settings);
|
||||
|
||||
/**
|
||||
* Stops the encoder
|
||||
*
|
||||
* @param data Data associated with this encoder context
|
||||
*/
|
||||
void (*stop)(void *data);
|
||||
|
||||
/**
|
||||
* Gets the property information of this encoder
|
||||
*
|
||||
@ -150,21 +167,56 @@ struct obs_encoder_info {
|
||||
obs_properties_t (*properties)(const char *locale);
|
||||
|
||||
/**
|
||||
* Updates the settings for this encoder
|
||||
* Updates the settings for this encoder (usually used for things like
|
||||
* changeing birate while active)
|
||||
*
|
||||
* @param data Data associated with this encoder context
|
||||
* @param settings New settings for this encoder
|
||||
* @return true if successful, false otherwise
|
||||
*/
|
||||
void (*update)(void *data, obs_data_t settings);
|
||||
bool (*update)(void *data, obs_data_t settings);
|
||||
|
||||
/**
|
||||
* Returns extra data associated with this encoder (usually header)
|
||||
*
|
||||
* @param data Data associated with this encoder context
|
||||
* @param extra_data Pointer to receive the extra data
|
||||
* @param size Pointer to receive the size of the extra data
|
||||
* @param data Data associated with this encoder context
|
||||
* @param[out] extra_data Pointer to receive the extra data
|
||||
* @param[out] size Pointer to receive the size of the extra
|
||||
* data
|
||||
* @return true if extra data available, false
|
||||
* otherwise
|
||||
*/
|
||||
bool (*get_extra_data)(void *data, uint8_t **extra_data, size_t *size);
|
||||
bool (*extra_data)(void *data, uint8_t **extra_data, size_t *size);
|
||||
|
||||
/**
|
||||
* Gets the SEI data, if any
|
||||
*
|
||||
* @param data Data associated with this encoder context
|
||||
* @param[out] sei_data Pointer to receive the SEI data
|
||||
* @param[out] size Pointer to receive the SEI data size
|
||||
* @return true if SEI data available, false otherwise
|
||||
*/
|
||||
bool (*sei_data)(void *data, uint8_t **sei_data, size_t *size);
|
||||
|
||||
/**
|
||||
* Returns desired audio format and sample information
|
||||
*
|
||||
* @param data Data associated with this encoder context
|
||||
* @param[out] info Audio format information
|
||||
* @return true if specific format is desired, false
|
||||
* otherwise
|
||||
*/
|
||||
bool (*audio_info)(void *data, struct audio_convert_info *info);
|
||||
|
||||
/**
|
||||
* Returns desired video format information
|
||||
*
|
||||
* @param data Data associated with this encoder context
|
||||
* @param[out] info Video format information
|
||||
* @return true if specific format is desired, false
|
||||
* otherwise
|
||||
*/
|
||||
bool (*video_info)(void *data, struct video_scale_info *info);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -276,6 +276,7 @@ struct obs_output {
|
||||
/* encoders */
|
||||
|
||||
struct encoder_callback {
|
||||
bool sent_first_packet;
|
||||
void (*new_packet)(void *param, struct encoder_packet *packet);
|
||||
void *param;
|
||||
};
|
||||
@ -286,6 +287,13 @@ struct obs_encoder {
|
||||
struct obs_encoder_info info;
|
||||
obs_data_t settings;
|
||||
|
||||
pthread_mutex_t data_callbacks_mutex;
|
||||
DARRAY(struct encoder_callback) data_callbacks;
|
||||
uint32_t timebase_num;
|
||||
uint32_t timebase_den;
|
||||
|
||||
int64_t cur_pts;
|
||||
|
||||
void *output;
|
||||
|
||||
pthread_mutex_t callbacks_mutex;
|
||||
DARRAY(struct encoder_callback) callbacks;
|
||||
};
|
||||
|
@ -204,7 +204,7 @@ void obs_register_encoder(const struct obs_encoder_info *info)
|
||||
CHECK_REQUIRED_VAL(info, getname, obs_register_encoder);
|
||||
CHECK_REQUIRED_VAL(info, create, obs_register_encoder);
|
||||
CHECK_REQUIRED_VAL(info, destroy, obs_register_encoder);
|
||||
CHECK_REQUIRED_VAL(info, reset, obs_register_encoder);
|
||||
CHECK_REQUIRED_VAL(info, start, obs_register_encoder);
|
||||
CHECK_REQUIRED_VAL(info, encode, obs_register_encoder);
|
||||
|
||||
REGISTER_OBS_DEF(cur_encoder_info_size, obs_encoder_info,
|
||||
|
@ -154,7 +154,7 @@ obs_data_t obs_output_get_settings(obs_output_t output)
|
||||
|
||||
bool obs_output_canpause(obs_output_t output)
|
||||
{
|
||||
return (output != NULL) ? output->info.pause != NULL : false;
|
||||
return output ? (output->info.pause != NULL) : false;
|
||||
}
|
||||
|
||||
void obs_output_pause(obs_output_t output)
|
||||
@ -165,10 +165,10 @@ void obs_output_pause(obs_output_t output)
|
||||
|
||||
signal_handler_t obs_output_signalhandler(obs_output_t output)
|
||||
{
|
||||
return output->signals;
|
||||
return output ? output->signals : NULL;
|
||||
}
|
||||
|
||||
proc_handler_t obs_output_prochandler(obs_output_t output)
|
||||
{
|
||||
return output->procs;
|
||||
return output ? output->procs : NULL;
|
||||
}
|
||||
|
67
libobs/obs.h
67
libobs/obs.h
@ -686,23 +686,54 @@ EXPORT proc_handler_t obs_output_prochandler(obs_output_t output);
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* Encoders */
|
||||
|
||||
EXPORT const char *obs_encoder_getdisplayname(const char *id,
|
||||
const char *locale);
|
||||
|
||||
EXPORT obs_encoder_t obs_encoder_create(const char *id, const char *name,
|
||||
obs_data_t settings);
|
||||
/**
|
||||
* Creates a video encoder context
|
||||
*
|
||||
* @param id Video encoder ID
|
||||
* @param name Name to assign to this context
|
||||
* @param settings Settings
|
||||
* @param video Video output context to encode data from
|
||||
* @return The video encoder context, or NULL if failed or not found.
|
||||
*/
|
||||
EXPORT obs_encoder_t obs_encoder_create_video(const char *id, const char *name,
|
||||
obs_data_t settings, video_t video);
|
||||
|
||||
/**
|
||||
* Creates an audio encoder context
|
||||
*
|
||||
* @param id Audio Encoder ID
|
||||
* @param name Name to assign to this context
|
||||
* @param settings Settings
|
||||
* @param audio Audio output context to encode data from
|
||||
* @return The video encoder context, or NULL if failed or not found.
|
||||
*/
|
||||
EXPORT obs_encoder_t obs_encoder_create_audio(const char *id, const char *name,
|
||||
obs_data_t settings, audio_t audio);
|
||||
|
||||
/** Destroys an encoder context */
|
||||
EXPORT void obs_encoder_destroy(obs_encoder_t encoder);
|
||||
|
||||
EXPORT bool obs_encoder_reset(obs_encoder_t encoder, obs_data_t settings);
|
||||
|
||||
EXPORT bool obs_encoder_encode(obs_encoder_t encoder,
|
||||
const struct encoder_frame *frame,
|
||||
struct encoder_packet *packet,
|
||||
bool *received_packet);
|
||||
|
||||
/**
|
||||
* Starts encoding. This function can be called more than once, and each
|
||||
* callback will receive the same encoder data.
|
||||
*
|
||||
* @param encoder Encoder context
|
||||
* @param new_packet Callback that receives encoded packets
|
||||
* @param param Callback parameter
|
||||
*/
|
||||
EXPORT bool obs_encoder_start(obs_encoder_t encoder,
|
||||
void (*new_packet)(void *param, struct encoder_packet *packet),
|
||||
void *param);
|
||||
|
||||
/**
|
||||
* Stops encoding. You must use the same callback/parameter combination that
|
||||
* was used for obs_encoder_start. Only when the last callback has been
|
||||
* removed will all encoding stop.
|
||||
*/
|
||||
EXPORT void obs_encoder_stop(obs_encoder_t encoder,
|
||||
void (*new_packet)(void *param, struct encoder_packet *packet),
|
||||
void *param);
|
||||
@ -714,13 +745,31 @@ EXPORT obs_data_t obs_encoder_defaults(const char *id);
|
||||
EXPORT obs_properties_t obs_encoder_properties(const char *id,
|
||||
const char *locale);
|
||||
|
||||
/**
|
||||
* Updates the settings of the encoder context. Usually used for changing
|
||||
* bitrate while active
|
||||
*/
|
||||
EXPORT void obs_encoder_update(obs_encoder_t encoder, obs_data_t settings);
|
||||
|
||||
/** Gets extra data (headers) associated with this context */
|
||||
EXPORT bool obs_encoder_get_extra_data(obs_encoder_t encoder,
|
||||
uint8_t **extra_data, size_t *size);
|
||||
|
||||
/** Returns the current settings for this encoder */
|
||||
EXPORT obs_data_t obs_encoder_get_settings(obs_encoder_t encoder);
|
||||
|
||||
/**
|
||||
* Returns the video output context used with this encoder, or NULL if not
|
||||
* a video context
|
||||
*/
|
||||
EXPORT video_t obs_encoder_video(obs_encoder_t encoder);
|
||||
|
||||
/**
|
||||
* Returns the audio output context used with this encoder, or NULL if not
|
||||
* a audio context
|
||||
*/
|
||||
EXPORT audio_t obs_encoder_audio(obs_encoder_t encoder);
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
/* Stream Services */
|
||||
|
@ -66,7 +66,7 @@ static inline char *bstrdup_n(const char *str, size_t n)
|
||||
static inline wchar_t *bwstrdup_n(const wchar_t *str, size_t n)
|
||||
{
|
||||
wchar_t *dup;
|
||||
if (!str || !*str)
|
||||
if (!str || (!*str && n > 0))
|
||||
return NULL;
|
||||
|
||||
dup = (wchar_t*)bmemdup(str, (n+1) * sizeof(wchar_t));
|
||||
|
@ -22,8 +22,10 @@
|
||||
#include <ctype.h>
|
||||
#include <wchar.h>
|
||||
#include <wctype.h>
|
||||
|
||||
#include "c99defs.h"
|
||||
#include "dstr.h"
|
||||
#include "darray.h"
|
||||
#include "bmem.h"
|
||||
#include "utf8.h"
|
||||
#include "lexer.h"
|
||||
@ -235,6 +237,53 @@ wchar_t *wcsdepad(wchar_t *str)
|
||||
return str;
|
||||
}
|
||||
|
||||
char **strlist_split(const char *str, char split_ch, bool include_empty)
|
||||
{
|
||||
const char *cur_str = str;
|
||||
const char *next_str;
|
||||
const char *new_str;
|
||||
DARRAY(char*) list;
|
||||
|
||||
da_init(list);
|
||||
|
||||
if (str) {
|
||||
next_str = strchr(str, split_ch);
|
||||
|
||||
while (next_str) {
|
||||
size_t size = next_str - cur_str;
|
||||
|
||||
if (size || include_empty) {
|
||||
new_str = bstrdup_n(cur_str, size);
|
||||
da_push_back(list, &new_str);
|
||||
}
|
||||
|
||||
cur_str = next_str;
|
||||
next_str = strchr(cur_str, split_ch);
|
||||
}
|
||||
|
||||
if (*cur_str || include_empty) {
|
||||
new_str = bstrdup(cur_str);
|
||||
da_push_back(list, &new_str);
|
||||
}
|
||||
}
|
||||
|
||||
new_str = NULL;
|
||||
da_push_back(list, &new_str);
|
||||
|
||||
return list.array;
|
||||
}
|
||||
|
||||
void strlist_free(char **strlist)
|
||||
{
|
||||
if (strlist) {
|
||||
char **temp = strlist;
|
||||
while (*temp)
|
||||
bfree(*(temp++));
|
||||
|
||||
bfree(strlist);
|
||||
}
|
||||
}
|
||||
|
||||
void dstr_init_strref(struct dstr *dst, const struct strref *src)
|
||||
{
|
||||
dstr_init(dst);
|
||||
|
@ -51,6 +51,9 @@ EXPORT char *astrstri(char *str, const char *find);
|
||||
EXPORT char *strdepad(char *str);
|
||||
EXPORT wchar_t *wcsdepad(wchar_t *str);
|
||||
|
||||
EXPORT char **strlist_split(const char *str, char split_ch, bool include_empty);
|
||||
EXPORT void strlist_free(char **strlist);
|
||||
|
||||
static inline void dstr_init(struct dstr *dst);
|
||||
static inline void dstr_init_move(struct dstr *dst, struct dstr *src);
|
||||
static inline void dstr_init_move_array(struct dstr *dst, char *str);
|
||||
|
@ -11,5 +11,6 @@ elseif("${CMAKE_SYSTEM_NAME}" MATCHES "Linux")
|
||||
add_subdirectory(linux-pulseaudio)
|
||||
endif()
|
||||
|
||||
add_subdirectory(obs-x264)
|
||||
add_subdirectory(obs-ffmpeg)
|
||||
add_subdirectory(obs-outputs)
|
||||
# add_subdirectory(obs-outputs)
|
||||
|
@ -513,7 +513,7 @@ static inline void copy_data(AVPicture *pic, const struct video_data *frame,
|
||||
}
|
||||
}
|
||||
|
||||
static void receive_video(void *param, const struct video_data *frame)
|
||||
static void receive_video(void *param, struct video_data *frame)
|
||||
{
|
||||
struct ffmpeg_output *output = param;
|
||||
struct ffmpeg_data *data = &output->ff_data;
|
||||
@ -527,7 +527,7 @@ static void receive_video(void *param, const struct video_data *frame)
|
||||
data->start_timestamp = frame->timestamp;
|
||||
|
||||
if (context->pix_fmt != AV_PIX_FMT_YUV420P)
|
||||
sws_scale(data->swscale, frame->data,
|
||||
sws_scale(data->swscale, (const uint8_t *const *)frame->data,
|
||||
(const int*)frame->linesize,
|
||||
0, context->height, data->dst_picture.data,
|
||||
data->dst_picture.linesize);
|
||||
@ -599,7 +599,7 @@ static inline void encode_audio(struct ffmpeg_output *output,
|
||||
context->sample_fmt, data->samples[0],
|
||||
(int)total_size, 1);
|
||||
if (ret < 0) {
|
||||
blog(LOG_WARNING, "receive_audio: avcodec_fill_audio_frame "
|
||||
blog(LOG_WARNING, "encode_audio: avcodec_fill_audio_frame "
|
||||
"failed: %s", av_err2str(ret));
|
||||
return;
|
||||
}
|
||||
@ -609,7 +609,7 @@ static inline void encode_audio(struct ffmpeg_output *output,
|
||||
ret = avcodec_encode_audio2(context, &packet, data->aframe,
|
||||
&got_packet);
|
||||
if (ret < 0) {
|
||||
blog(LOG_WARNING, "receive_audio: Error encoding audio: %s",
|
||||
blog(LOG_WARNING, "encode_audio: Error encoding audio: %s",
|
||||
av_err2str(ret));
|
||||
return;
|
||||
}
|
||||
@ -655,7 +655,7 @@ static bool prepare_audio(struct ffmpeg_data *data,
|
||||
return true;
|
||||
}
|
||||
|
||||
static void receive_audio(void *param, const struct audio_data *frame)
|
||||
static void receive_audio(void *param, struct audio_data *frame)
|
||||
{
|
||||
struct ffmpeg_output *output = param;
|
||||
struct ffmpeg_data *data = &output->ff_data;
|
||||
|
@ -5,17 +5,10 @@ include_directories(${Libx264_INCLUDE_DIR})
|
||||
|
||||
set(obs-outputs_SOURCES
|
||||
obs-outputs.c
|
||||
obs-x264.c
|
||||
rtmp-stream.c)
|
||||
|
||||
set(obs-outputs_HEADERS
|
||||
obs-outputs.h
|
||||
obs-x264.h
|
||||
rtmp-stream.h)
|
||||
|
||||
add_library(obs-outputs MODULE
|
||||
${obs-outputs_SOURCES}
|
||||
${obs-outputs_HEADERS})
|
||||
${obs-outputs_SOURCES})
|
||||
target_link_libraries(obs-outputs
|
||||
libobs
|
||||
${Libx264_LIBRARIES})
|
||||
|
@ -1,12 +1,10 @@
|
||||
#include <string.h>
|
||||
#include "obs-outputs.h"
|
||||
#include <obs-module.h>
|
||||
|
||||
static const char *outputs[] = {"rtmp_stream"};
|
||||
OBS_DECLARE_MODULE()
|
||||
|
||||
const char *enum_outputs(size_t idx)
|
||||
|
||||
bool obs_module_load(uint32_t libobs_ver)
|
||||
{
|
||||
if (idx >= sizeof(outputs)/sizeof(const char*))
|
||||
return NULL;
|
||||
|
||||
return outputs[idx];
|
||||
UNUSED_PARAMETER(libobs_ver);
|
||||
return true;
|
||||
}
|
||||
|
@ -1,5 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <util/c99defs.h>
|
||||
|
||||
EXPORT const char *enum_outputs(size_t idx);
|
@ -1,58 +0,0 @@
|
||||
/******************************************************************************
|
||||
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#include "obs-x264.h"
|
||||
|
||||
const char *obs_x264_getname(const char *locale)
|
||||
{
|
||||
/* TODO locale lookup */
|
||||
return "x264 (Software)";
|
||||
}
|
||||
|
||||
struct obs_x264 *obs_x264_create(obs_data_t settings, obs_encoder_t encoder)
|
||||
{
|
||||
struct obs_x264 *data = bmalloc(sizeof(struct obs_x264));
|
||||
}
|
||||
|
||||
void obs_x264_destroy(struct obs_x264 *data)
|
||||
{
|
||||
}
|
||||
|
||||
void obs_x264_update(struct obs_x264 *data, obs_data_t settings)
|
||||
{
|
||||
}
|
||||
|
||||
void obs_x264_reset(struct obs_x264 *data)
|
||||
{
|
||||
}
|
||||
|
||||
int obs_x264_encode(struct obs_x264 *data, struct encoder_packet **packets)
|
||||
{
|
||||
}
|
||||
|
||||
int obs_x264_getheader(struct obs_x264 *data, struct encoder_packet **packets)
|
||||
{
|
||||
}
|
||||
|
||||
void obs_x264_setbitrate(struct obs_x264 *data, uint32_t bitrate,
|
||||
uint32_t buffersize)
|
||||
{
|
||||
}
|
||||
|
||||
void obs_x264_request_keyframe(struct obs_x264 *data)
|
||||
{
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
/******************************************************************************
|
||||
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <util/c99defs.h>
|
||||
#include <obs.h>
|
||||
#include <x264.h>
|
||||
|
||||
struct obs_x264 {
|
||||
obs_encoder_t encoder;
|
||||
|
||||
x264_param_t params;
|
||||
x264_t *context;
|
||||
x264_picture_t pic_out;
|
||||
};
|
||||
|
||||
EXPORT const char *obs_x264_getname(const char *locale);
|
||||
|
||||
EXPORT struct obs_x264 *obs_x264_create(obs_data_t settings,
|
||||
obs_encoder_t encoder);
|
||||
EXPORT void obs_x264_destroy(struct obs_x264 *data);
|
||||
|
||||
EXPORT void obs_x264_update(struct obs_x264 *data, obs_data_t settings);
|
||||
|
||||
EXPORT void obs_x264_reset(struct obs_x264 *data);
|
||||
|
||||
EXPORT int obs_x264_encode(struct obs_x264 *data,
|
||||
struct encoder_packet **packets);
|
||||
EXPORT int obs_x264_getheader(struct obs_x264 *data,
|
||||
struct encoder_packet **packets);
|
||||
|
||||
EXPORT void obs_x264_setbitrate(struct obs_x264 *data, uint32_t bitrate,
|
||||
uint32_t buffersize);
|
||||
EXPORT void obs_x264_request_keyframe(struct obs_x264 *data);
|
@ -15,36 +15,45 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#include "rtmp-stream.h"
|
||||
#include <obs.h>
|
||||
|
||||
const char *rtmp_stream_getname(const char *locale)
|
||||
struct rtmp_stream {
|
||||
obs_output_t output;
|
||||
obs_encoder_t video_encoder;
|
||||
obs_encoder_t audio_encoder;
|
||||
obs_service_t service;
|
||||
|
||||
bool active;
|
||||
};
|
||||
|
||||
static const char *rtmp_stream_getname(const char *locale)
|
||||
{
|
||||
/* TODO: locale stuff */
|
||||
return "RTMP Stream";
|
||||
}
|
||||
|
||||
void *rtmp_stream_create(obs_data_t settings, obs_output_t output)
|
||||
static void *rtmp_stream_create(obs_data_t settings, obs_output_t output)
|
||||
{
|
||||
struct rtmp_stream *stream = bmalloc(sizeof(struct rtmp_stream));
|
||||
memset(stream, 0, sizeof(struct rtmp_stream));
|
||||
}
|
||||
|
||||
void rtmp_stream_destroy(struct rtmp_stream *stream)
|
||||
static void rtmp_stream_destroy(struct rtmp_stream *stream)
|
||||
{
|
||||
}
|
||||
|
||||
void rtmp_stream_update(struct rtmp_stream *stream, obs_data_t settings)
|
||||
static void rtmp_stream_update(struct rtmp_stream *stream, obs_data_t settings)
|
||||
{
|
||||
}
|
||||
|
||||
bool rtmp_stream_start(struct rtmp_stream *stream)
|
||||
static bool rtmp_stream_start(struct rtmp_stream *stream)
|
||||
{
|
||||
}
|
||||
|
||||
void rtmp_stream_stop(struct rtmp_stream *stream)
|
||||
static void rtmp_stream_stop(struct rtmp_stream *stream)
|
||||
{
|
||||
}
|
||||
|
||||
bool rtmp_stream_active(struct rtmp_stream *stream)
|
||||
static bool rtmp_stream_active(struct rtmp_stream *stream)
|
||||
{
|
||||
}
|
||||
|
@ -1,38 +0,0 @@
|
||||
/******************************************************************************
|
||||
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <util/c99defs.h>
|
||||
#include <obs.h>
|
||||
|
||||
struct rtmp_stream {
|
||||
obs_output_t output;
|
||||
obs_encoder_t video_encoder;
|
||||
obs_encoder_t audio_encoder;
|
||||
obs_service_t service;
|
||||
|
||||
bool active;
|
||||
};
|
||||
|
||||
EXPORT const char *rtmp_stream_getname(const char *locale);
|
||||
EXPORT void *rtmp_stream_create(obs_data_t settings, obs_output_t output);
|
||||
EXPORT void rtmp_stream_destroy(struct rtmp_stream *stream);
|
||||
EXPORT void rtmp_stream_update(struct rtmp_stream *stream, obs_data_t settings);
|
||||
EXPORT bool rtmp_stream_start(struct rtmp_stream *stream);
|
||||
EXPORT void rtmp_stream_stop(struct rtmp_stream *stream);
|
||||
EXPORT bool rtmp_stream_active(struct rtmp_stream *stream);
|
19
plugins/obs-x264/CMakeLists.txt
Normal file
19
plugins/obs-x264/CMakeLists.txt
Normal file
@ -0,0 +1,19 @@
|
||||
project(obs-x264)
|
||||
|
||||
find_package(Libx264 REQUIRED)
|
||||
include_directories(${Libx264_INCLUDE_DIR})
|
||||
add_definitions(${Libx264_DEFINITIONS})
|
||||
|
||||
set(obs-x264_SOURCES
|
||||
obs-x264.c
|
||||
obs-x264-plugin-main.c)
|
||||
|
||||
add_library(obs-x264 MODULE
|
||||
${obs-x264_SOURCES})
|
||||
target_link_libraries(obs-x264
|
||||
libobs
|
||||
${Libx264_LIBRARIES})
|
||||
|
||||
install_obs_plugin(obs-x264)
|
||||
|
||||
obs_fixup_install_target(obs-x264 PATH ${Libx264_LIBRARIES})
|
13
plugins/obs-x264/obs-x264-plugin-main.c
Normal file
13
plugins/obs-x264/obs-x264-plugin-main.c
Normal file
@ -0,0 +1,13 @@
|
||||
#include <obs-module.h>
|
||||
|
||||
OBS_DECLARE_MODULE()
|
||||
|
||||
extern struct obs_encoder_info obs_x264_encoder;
|
||||
|
||||
bool obs_module_load(uint32_t libobs_ver)
|
||||
{
|
||||
obs_register_encoder(&obs_x264_encoder);
|
||||
|
||||
UNUSED_PARAMETER(libobs_ver);
|
||||
return true;
|
||||
}
|
506
plugins/obs-x264/obs-x264.c
Normal file
506
plugins/obs-x264/obs-x264.c
Normal file
@ -0,0 +1,506 @@
|
||||
/******************************************************************************
|
||||
Copyright (C) 2014 by Hugh Bailey <obs.jim@gmail.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
#include <util/dstr.h>
|
||||
#include <util/darray.h>
|
||||
#include <obs.h>
|
||||
#include <x264.h>
|
||||
|
||||
struct obs_x264 {
|
||||
obs_encoder_t encoder;
|
||||
|
||||
x264_param_t params;
|
||||
x264_t *context;
|
||||
|
||||
DARRAY(uint8_t) packet_data;
|
||||
|
||||
uint8_t *extra_data;
|
||||
uint8_t *sei;
|
||||
|
||||
size_t extra_data_size;
|
||||
size_t sei_size;
|
||||
};
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static const char *obs_x264_getname(const char *locale)
|
||||
{
|
||||
/* TODO locale lookup */
|
||||
UNUSED_PARAMETER(locale);
|
||||
return "x264";
|
||||
}
|
||||
|
||||
static void obs_x264_stop(void *data);
|
||||
|
||||
static void obs_x264_destroy(void *data)
|
||||
{
|
||||
struct obs_x264 *obsx264 = data;
|
||||
|
||||
if (obsx264) {
|
||||
obs_x264_stop(obsx264);
|
||||
da_free(obsx264->packet_data);
|
||||
bfree(obsx264);
|
||||
}
|
||||
}
|
||||
|
||||
static void obs_x264_defaults(obs_data_t settings)
|
||||
{
|
||||
obs_data_set_default_int (settings, "bitrate", 1000);
|
||||
obs_data_set_default_int (settings, "buffer_size", 1000);
|
||||
obs_data_set_default_int (settings, "keyint_sec", 0);
|
||||
|
||||
obs_data_set_default_string(settings, "preset", "veryfast");
|
||||
obs_data_set_default_string(settings, "profile", "");
|
||||
obs_data_set_default_string(settings, "tune", "");
|
||||
obs_data_set_default_string(settings, "x264opts", "");
|
||||
}
|
||||
|
||||
static inline void add_strings(obs_property_t list, const char *const *strings)
|
||||
{
|
||||
while (*strings) {
|
||||
obs_property_list_add_item(list, *strings, *strings);
|
||||
strings++;
|
||||
}
|
||||
}
|
||||
|
||||
static obs_properties_t obs_x264_props(const char *locale)
|
||||
{
|
||||
UNUSED_PARAMETER(locale);
|
||||
/* TODO: locale */
|
||||
|
||||
obs_properties_t props = obs_properties_create();
|
||||
obs_property_t list;
|
||||
|
||||
obs_properties_add_int(props, "bitrate", "Bitrate", 50, 100000, 1);
|
||||
obs_properties_add_int(props, "buffer_size", "Buffer Size", 50, 100000,
|
||||
1);
|
||||
obs_properties_add_int(props,
|
||||
"keyint_sec", "Keyframe interval (seconds, 0=auto)",
|
||||
0, 20, 1);
|
||||
|
||||
list = obs_properties_add_list(props,
|
||||
"preset", "CPU Usage Preset (encoder speed)",
|
||||
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING);
|
||||
add_strings(list, x264_preset_names);
|
||||
|
||||
list = obs_properties_add_list(props, "profile", "Profile",
|
||||
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING);
|
||||
obs_property_list_add_item(list, "baseline", "baseline");
|
||||
obs_property_list_add_item(list, "main", "main");
|
||||
obs_property_list_add_item(list, "high", "high");
|
||||
|
||||
list = obs_properties_add_list(props, "tune", "Tune",
|
||||
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING);
|
||||
add_strings(list, x264_tune_names);
|
||||
|
||||
obs_properties_add_text(props, "x264opts",
|
||||
"x264 encoder options (separated by ':')");
|
||||
|
||||
return props;
|
||||
}
|
||||
|
||||
static bool getparam(const char *param, char **name, const char **value)
|
||||
{
|
||||
const char *assign;
|
||||
|
||||
if (!param || !*param || (*param == '='))
|
||||
return false;
|
||||
|
||||
assign = strchr(param, '=');
|
||||
if (!assign || !*assign || !*(assign+1))
|
||||
return false;
|
||||
|
||||
*name = bstrdup_n(param, assign-param);
|
||||
*value = assign+1;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void override_base_param(const char *param,
|
||||
char **preset, char **profile, char **tune)
|
||||
{
|
||||
char *name;
|
||||
const char *val;
|
||||
|
||||
if (getparam(param, &name, &val)) {
|
||||
if (astrcmpi(name, "preset") == 0) {
|
||||
bfree(*preset);
|
||||
*preset = bstrdup(val);
|
||||
|
||||
} else if (astrcmpi(name, "profile") == 0) {
|
||||
bfree(*profile);
|
||||
*profile = bstrdup(val);
|
||||
|
||||
} else if (astrcmpi(name, "tune") == 0) {
|
||||
bfree(*tune);
|
||||
*tune = bstrdup(val);
|
||||
}
|
||||
|
||||
bfree(name);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void override_base_params(char **params,
|
||||
char **preset, char **profile, char **tune)
|
||||
{
|
||||
while (*params)
|
||||
override_base_param(*(params++), preset, profile, tune);
|
||||
}
|
||||
|
||||
static inline void set_param(struct obs_x264 *obsx264, const char *param)
|
||||
{
|
||||
char *name;
|
||||
const char *val;
|
||||
|
||||
if (getparam(param, &name, &val)) {
|
||||
if (x264_param_parse(&obsx264->params, name, val) != 0)
|
||||
blog(LOG_WARNING, "x264 param: %s failed", param);
|
||||
|
||||
bfree(name);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void apply_x264_profile(struct obs_x264 *obsx264,
|
||||
const char *profile)
|
||||
{
|
||||
if (!*profile) profile = NULL;
|
||||
|
||||
if (!obsx264->context && profile) {
|
||||
int ret = x264_param_apply_profile(&obsx264->params, profile);
|
||||
if (ret != 0)
|
||||
blog(LOG_WARNING, "Failed to set x264 "
|
||||
"profile '%s'", profile);
|
||||
}
|
||||
}
|
||||
|
||||
static bool reset_x264_params(struct obs_x264 *obsx264,
|
||||
const char *preset, const char *tune)
|
||||
{
|
||||
if (!*preset) preset = NULL;
|
||||
if (!*tune) tune = NULL;
|
||||
|
||||
return x264_param_default_preset(&obsx264->params, preset, tune) == 0;
|
||||
}
|
||||
|
||||
static void log_x264(void *param, int level, const char *format, va_list args)
|
||||
{
|
||||
blogva(LOG_INFO, format, args);
|
||||
|
||||
UNUSED_PARAMETER(param);
|
||||
UNUSED_PARAMETER(level);
|
||||
}
|
||||
|
||||
static void update_params(struct obs_x264 *obsx264, obs_data_t settings,
|
||||
char **params)
|
||||
{
|
||||
video_t video = obs_encoder_video(obsx264->encoder);
|
||||
const struct video_output_info *voi = video_output_getinfo(video);
|
||||
|
||||
int bitrate = (int)obs_data_getint(settings, "bitrate");
|
||||
int buffer_size = (int)obs_data_getint(settings, "buffer_size");
|
||||
int keyint_sec = (int)obs_data_getint(settings, "keyint_sec");
|
||||
|
||||
if (keyint_sec)
|
||||
obsx264->params.i_keyint_max =
|
||||
keyint_sec * voi->fps_num / voi->fps_den;
|
||||
|
||||
obsx264->params.rc.i_vbv_max_bitrate = bitrate;
|
||||
obsx264->params.rc.i_vbv_buffer_size = buffer_size;
|
||||
obsx264->params.rc.i_bitrate = bitrate;
|
||||
obsx264->params.i_width = voi->width;
|
||||
obsx264->params.i_height = voi->height;
|
||||
obsx264->params.i_fps_num = voi->fps_num;
|
||||
obsx264->params.i_fps_den = voi->fps_den;
|
||||
obsx264->params.i_timebase_num = voi->fps_den;
|
||||
obsx264->params.i_timebase_den = voi->fps_num;
|
||||
obsx264->params.pf_log = log_x264;
|
||||
obsx264->params.i_log_level = X264_LOG_WARNING;
|
||||
|
||||
if (voi->format == VIDEO_FORMAT_NV12)
|
||||
obsx264->params.i_csp = X264_CSP_NV12;
|
||||
else if (voi->format == VIDEO_FORMAT_I420)
|
||||
obsx264->params.i_csp = X264_CSP_I420;
|
||||
else
|
||||
obsx264->params.i_csp = X264_CSP_NV12;
|
||||
|
||||
while (*params)
|
||||
set_param(obsx264, *(params++));
|
||||
}
|
||||
|
||||
static bool update_settings(struct obs_x264 *obsx264, obs_data_t settings)
|
||||
{
|
||||
char *preset = bstrdup(obs_data_getstring(settings, "preset"));
|
||||
char *profile = bstrdup(obs_data_getstring(settings, "profile"));
|
||||
char *tune = bstrdup(obs_data_getstring(settings, "tune"));
|
||||
const char *opts = obs_data_getstring(settings, "x264opts");
|
||||
|
||||
char **paramlist;
|
||||
bool success = true;
|
||||
|
||||
paramlist = strlist_split(opts, ':', false);
|
||||
|
||||
if (!obsx264->context) {
|
||||
override_base_params(paramlist, &preset, &tune, &profile);
|
||||
success = reset_x264_params(obsx264, preset, tune);
|
||||
}
|
||||
|
||||
if (success) {
|
||||
update_params(obsx264, settings, paramlist);
|
||||
|
||||
if (!obsx264->context)
|
||||
apply_x264_profile(obsx264, profile);
|
||||
}
|
||||
|
||||
strlist_free(paramlist);
|
||||
bfree(preset);
|
||||
bfree(profile);
|
||||
bfree(tune);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool obs_x264_update(void *data, obs_data_t settings)
|
||||
{
|
||||
struct obs_x264 *obsx264 = data;
|
||||
bool success = update_settings(obsx264, settings);
|
||||
int ret;
|
||||
|
||||
if (success) {
|
||||
ret = x264_encoder_reconfig(obsx264->context, &obsx264->params);
|
||||
if (ret != 0)
|
||||
blog(LOG_WARNING, "Failed to reconfigure x264: %d",
|
||||
ret);
|
||||
return ret == 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void load_headers(struct obs_x264 *obsx264)
|
||||
{
|
||||
x264_nal_t *nals;
|
||||
int nal_count;
|
||||
DARRAY(uint8_t) header;
|
||||
DARRAY(uint8_t) sei;
|
||||
|
||||
da_init(header);
|
||||
da_init(sei);
|
||||
|
||||
x264_encoder_headers(obsx264->context, &nals, &nal_count);
|
||||
|
||||
for (int i = 0; i < nal_count; i++) {
|
||||
x264_nal_t *nal = nals+i;
|
||||
|
||||
if (nal->i_type == NAL_SEI)
|
||||
da_push_back_array(sei, nal->p_payload, nal->i_payload);
|
||||
else
|
||||
da_push_back_array(header, nal->p_payload,
|
||||
nal->i_payload);
|
||||
}
|
||||
|
||||
obsx264->extra_data = header.array;
|
||||
obsx264->extra_data_size = header.num;
|
||||
obsx264->sei = sei.array;
|
||||
obsx264->sei_size = sei.num;
|
||||
}
|
||||
|
||||
static bool obs_x264_start(void *data, obs_data_t settings)
|
||||
{
|
||||
struct obs_x264 *obsx264 = data;
|
||||
|
||||
assert(obsx264->context == NULL);
|
||||
obs_x264_stop(data);
|
||||
|
||||
if (update_settings(obsx264, settings)) {
|
||||
obsx264->context = x264_encoder_open(&obsx264->params);
|
||||
|
||||
if (obsx264->context == NULL)
|
||||
blog(LOG_WARNING, "x264 failed to load");
|
||||
else
|
||||
load_headers(obsx264);
|
||||
} else {
|
||||
blog(LOG_WARNING, "bad settings specified for x264");
|
||||
}
|
||||
|
||||
return obsx264->context != NULL;
|
||||
}
|
||||
|
||||
static void obs_x264_stop(void *data)
|
||||
{
|
||||
struct obs_x264 *obsx264 = data;
|
||||
|
||||
if (obsx264->context) {
|
||||
x264_encoder_close(obsx264->context);
|
||||
bfree(obsx264->sei);
|
||||
bfree(obsx264->extra_data);
|
||||
|
||||
obsx264->context = NULL;
|
||||
obsx264->sei = NULL;
|
||||
obsx264->extra_data = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void *obs_x264_create(obs_data_t settings, obs_encoder_t encoder)
|
||||
{
|
||||
struct obs_x264 *data = bzalloc(sizeof(struct obs_x264));
|
||||
data->encoder = encoder;
|
||||
|
||||
obs_x264_defaults(settings);
|
||||
return data;
|
||||
}
|
||||
|
||||
static inline int drop_priority(int priority)
|
||||
{
|
||||
switch (priority) {
|
||||
case NAL_PRIORITY_DISPOSABLE: return NAL_PRIORITY_DISPOSABLE;
|
||||
case NAL_PRIORITY_LOW: return NAL_PRIORITY_LOW;
|
||||
case NAL_PRIORITY_HIGH: return NAL_PRIORITY_HIGHEST;
|
||||
case NAL_PRIORITY_HIGHEST: return NAL_PRIORITY_HIGHEST;
|
||||
}
|
||||
|
||||
return NAL_PRIORITY_HIGHEST;
|
||||
}
|
||||
|
||||
static void parse_packet(struct obs_x264 *obsx264,
|
||||
struct encoder_packet *packet, x264_nal_t *nals,
|
||||
int nal_count, x264_picture_t *pic_out)
|
||||
{
|
||||
if (!nal_count) return;
|
||||
|
||||
da_resize(obsx264->packet_data, 0);
|
||||
|
||||
for (int i = 0; i < nal_count; i++) {
|
||||
x264_nal_t *nal = nals+i;
|
||||
da_push_back_array(obsx264->packet_data, nal->p_payload,
|
||||
nal->i_payload);
|
||||
}
|
||||
|
||||
packet->data = obsx264->packet_data.array;
|
||||
packet->size = obsx264->packet_data.num;
|
||||
packet->type = OBS_ENCODER_VIDEO;
|
||||
packet->pts = pic_out->i_pts;
|
||||
packet->dts = pic_out->i_dts;
|
||||
packet->keyframe = nals[0].i_type == NAL_SLICE_IDR;
|
||||
packet->priority = nals[0].i_ref_idc;
|
||||
packet->drop_priority = drop_priority(nals[0].i_ref_idc);
|
||||
}
|
||||
|
||||
static inline void init_pic_data(struct obs_x264 *obsx264, x264_picture_t *pic,
|
||||
struct encoder_frame *frame)
|
||||
{
|
||||
x264_picture_init(pic);
|
||||
|
||||
pic->i_pts = frame->pts;
|
||||
pic->img.i_csp = obsx264->params.i_csp;
|
||||
|
||||
if (obsx264->params.i_csp == X264_CSP_NV12)
|
||||
pic->img.i_plane = 2;
|
||||
else if (obsx264->params.i_csp == X264_CSP_I420)
|
||||
pic->img.i_plane = 3;
|
||||
|
||||
for (int i = 0; i < pic->img.i_plane; i++) {
|
||||
pic->img.i_stride[i] = (int)frame->linesize[i];
|
||||
pic->img.plane[i] = frame->data[i];
|
||||
}
|
||||
}
|
||||
|
||||
static bool obs_x264_encode(void *data, struct encoder_frame *frame,
|
||||
struct encoder_packet *packet, bool *received_packet)
|
||||
{
|
||||
struct obs_x264 *obsx264 = data;
|
||||
x264_nal_t *nals;
|
||||
int nal_count;
|
||||
int ret;
|
||||
x264_picture_t pic, pic_out;
|
||||
|
||||
if (!frame || !packet || !received_packet)
|
||||
return false;
|
||||
|
||||
init_pic_data(obsx264, &pic, frame);
|
||||
|
||||
ret = x264_encoder_encode(obsx264->context, &nals, &nal_count, &pic,
|
||||
&pic_out);
|
||||
if (ret < 0) {
|
||||
blog(LOG_WARNING, "x264 encode failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
*received_packet = (nal_count != 0);
|
||||
parse_packet(obsx264, packet, nals, nal_count, &pic_out);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool obs_x264_extra_data(void *data, uint8_t **extra_data, size_t *size)
|
||||
{
|
||||
struct obs_x264 *obsx264 = data;
|
||||
|
||||
if (!obsx264->context)
|
||||
return false;
|
||||
|
||||
*extra_data = obsx264->extra_data;
|
||||
*size = obsx264->extra_data_size;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool obs_x264_sei(void *data, uint8_t **sei, size_t *size)
|
||||
{
|
||||
struct obs_x264 *obsx264 = data;
|
||||
|
||||
if (!obsx264->context)
|
||||
return false;
|
||||
|
||||
*sei = obsx264->sei;
|
||||
*size = obsx264->sei_size;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool obs_x264_video_info(void *data, struct video_scale_info *info)
|
||||
{
|
||||
struct obs_x264 *obsx264 = data;
|
||||
video_t video = obs_encoder_video(obsx264->encoder);
|
||||
const struct video_output_info *vid_info = video_output_getinfo(video);
|
||||
|
||||
if (vid_info->format == VIDEO_FORMAT_I420 ||
|
||||
vid_info->format == VIDEO_FORMAT_NV12)
|
||||
return false;
|
||||
|
||||
info->format = VIDEO_FORMAT_NV12;
|
||||
info->width = vid_info->width;
|
||||
info->height = vid_info->height;
|
||||
info->range = VIDEO_RANGE_DEFAULT;
|
||||
info->colorspace = VIDEO_CS_DEFAULT;
|
||||
return true;
|
||||
}
|
||||
|
||||
struct obs_encoder_info obs_x264_encoder = {
|
||||
.id = "obs_x264",
|
||||
.type = OBS_ENCODER_VIDEO,
|
||||
.codec = "h264",
|
||||
.getname = obs_x264_getname,
|
||||
.create = obs_x264_create,
|
||||
.destroy = obs_x264_destroy,
|
||||
.start = obs_x264_start,
|
||||
.stop = obs_x264_stop,
|
||||
.encode = obs_x264_encode,
|
||||
.properties = obs_x264_props,
|
||||
.defaults = obs_x264_defaults,
|
||||
.update = obs_x264_update,
|
||||
.extra_data = obs_x264_extra_data,
|
||||
.sei_data = obs_x264_sei,
|
||||
.video_info = obs_x264_video_info
|
||||
};
|
@ -43,102 +43,162 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "win-wasapi", "win-wasapi\wi
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "win-capture", "win-capture\win-capture.vcxproj", "{AB83E5F0-D76E-45AB-A4C9-4711B1BE6916}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "obs-x264", "obs-x264\obs-x264.vcxproj", "{1798CAF2-531A-4E0E-A722-29F4B1B5D49F}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Mixed Platforms = Debug|Mixed Platforms
|
||||
Debug|Win32 = Debug|Win32
|
||||
Debug|x64 = Debug|x64
|
||||
Release|Mixed Platforms = Release|Mixed Platforms
|
||||
Release|Win32 = Release|Win32
|
||||
Release|x64 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{6F1AC2AE-6424-401A-AF9F-A771E6BEE026}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
|
||||
{6F1AC2AE-6424-401A-AF9F-A771E6BEE026}.Debug|Mixed Platforms.Build.0 = Debug|Win32
|
||||
{6F1AC2AE-6424-401A-AF9F-A771E6BEE026}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{6F1AC2AE-6424-401A-AF9F-A771E6BEE026}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{6F1AC2AE-6424-401A-AF9F-A771E6BEE026}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{6F1AC2AE-6424-401A-AF9F-A771E6BEE026}.Debug|x64.Build.0 = Debug|x64
|
||||
{6F1AC2AE-6424-401A-AF9F-A771E6BEE026}.Release|Mixed Platforms.ActiveCfg = Release|Win32
|
||||
{6F1AC2AE-6424-401A-AF9F-A771E6BEE026}.Release|Mixed Platforms.Build.0 = Release|Win32
|
||||
{6F1AC2AE-6424-401A-AF9F-A771E6BEE026}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{6F1AC2AE-6424-401A-AF9F-A771E6BEE026}.Release|Win32.Build.0 = Release|Win32
|
||||
{6F1AC2AE-6424-401A-AF9F-A771E6BEE026}.Release|x64.ActiveCfg = Release|x64
|
||||
{6F1AC2AE-6424-401A-AF9F-A771E6BEE026}.Release|x64.Build.0 = Release|x64
|
||||
{68A84F9A-5B89-4E7D-8183-87FEA5DC65F6}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
|
||||
{68A84F9A-5B89-4E7D-8183-87FEA5DC65F6}.Debug|Mixed Platforms.Build.0 = Debug|Win32
|
||||
{68A84F9A-5B89-4E7D-8183-87FEA5DC65F6}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{68A84F9A-5B89-4E7D-8183-87FEA5DC65F6}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{68A84F9A-5B89-4E7D-8183-87FEA5DC65F6}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{68A84F9A-5B89-4E7D-8183-87FEA5DC65F6}.Debug|x64.Build.0 = Debug|x64
|
||||
{68A84F9A-5B89-4E7D-8183-87FEA5DC65F6}.Release|Mixed Platforms.ActiveCfg = Release|Win32
|
||||
{68A84F9A-5B89-4E7D-8183-87FEA5DC65F6}.Release|Mixed Platforms.Build.0 = Release|Win32
|
||||
{68A84F9A-5B89-4E7D-8183-87FEA5DC65F6}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{68A84F9A-5B89-4E7D-8183-87FEA5DC65F6}.Release|Win32.Build.0 = Release|Win32
|
||||
{68A84F9A-5B89-4E7D-8183-87FEA5DC65F6}.Release|x64.ActiveCfg = Release|x64
|
||||
{68A84F9A-5B89-4E7D-8183-87FEA5DC65F6}.Release|x64.Build.0 = Release|x64
|
||||
{E11367B7-20CC-4741-B8E0-C20E85302A40}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
|
||||
{E11367B7-20CC-4741-B8E0-C20E85302A40}.Debug|Mixed Platforms.Build.0 = Debug|Win32
|
||||
{E11367B7-20CC-4741-B8E0-C20E85302A40}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{E11367B7-20CC-4741-B8E0-C20E85302A40}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{E11367B7-20CC-4741-B8E0-C20E85302A40}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{E11367B7-20CC-4741-B8E0-C20E85302A40}.Debug|x64.Build.0 = Debug|x64
|
||||
{E11367B7-20CC-4741-B8E0-C20E85302A40}.Release|Mixed Platforms.ActiveCfg = Release|Win32
|
||||
{E11367B7-20CC-4741-B8E0-C20E85302A40}.Release|Mixed Platforms.Build.0 = Release|Win32
|
||||
{E11367B7-20CC-4741-B8E0-C20E85302A40}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{E11367B7-20CC-4741-B8E0-C20E85302A40}.Release|Win32.Build.0 = Release|Win32
|
||||
{E11367B7-20CC-4741-B8E0-C20E85302A40}.Release|x64.ActiveCfg = Release|x64
|
||||
{E11367B7-20CC-4741-B8E0-C20E85302A40}.Release|x64.Build.0 = Release|x64
|
||||
{82C863C3-74C8-43BE-90CF-755EE698F2E8}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
|
||||
{82C863C3-74C8-43BE-90CF-755EE698F2E8}.Debug|Mixed Platforms.Build.0 = Debug|Win32
|
||||
{82C863C3-74C8-43BE-90CF-755EE698F2E8}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{82C863C3-74C8-43BE-90CF-755EE698F2E8}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{82C863C3-74C8-43BE-90CF-755EE698F2E8}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{82C863C3-74C8-43BE-90CF-755EE698F2E8}.Debug|x64.Build.0 = Debug|x64
|
||||
{82C863C3-74C8-43BE-90CF-755EE698F2E8}.Release|Mixed Platforms.ActiveCfg = Release|Win32
|
||||
{82C863C3-74C8-43BE-90CF-755EE698F2E8}.Release|Mixed Platforms.Build.0 = Release|Win32
|
||||
{82C863C3-74C8-43BE-90CF-755EE698F2E8}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{82C863C3-74C8-43BE-90CF-755EE698F2E8}.Release|Win32.Build.0 = Release|Win32
|
||||
{82C863C3-74C8-43BE-90CF-755EE698F2E8}.Release|x64.ActiveCfg = Release|x64
|
||||
{82C863C3-74C8-43BE-90CF-755EE698F2E8}.Release|x64.Build.0 = Release|x64
|
||||
{760ECBBC-EA7C-464A-B60E-945A0BB1B100}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
|
||||
{760ECBBC-EA7C-464A-B60E-945A0BB1B100}.Debug|Mixed Platforms.Build.0 = Debug|Win32
|
||||
{760ECBBC-EA7C-464A-B60E-945A0BB1B100}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{760ECBBC-EA7C-464A-B60E-945A0BB1B100}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{760ECBBC-EA7C-464A-B60E-945A0BB1B100}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{760ECBBC-EA7C-464A-B60E-945A0BB1B100}.Debug|x64.Build.0 = Debug|x64
|
||||
{760ECBBC-EA7C-464A-B60E-945A0BB1B100}.Release|Mixed Platforms.ActiveCfg = Release|Win32
|
||||
{760ECBBC-EA7C-464A-B60E-945A0BB1B100}.Release|Mixed Platforms.Build.0 = Release|Win32
|
||||
{760ECBBC-EA7C-464A-B60E-945A0BB1B100}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{760ECBBC-EA7C-464A-B60E-945A0BB1B100}.Release|Win32.Build.0 = Release|Win32
|
||||
{760ECBBC-EA7C-464A-B60E-945A0BB1B100}.Release|x64.ActiveCfg = Release|x64
|
||||
{760ECBBC-EA7C-464A-B60E-945A0BB1B100}.Release|x64.Build.0 = Release|x64
|
||||
{B6EAE19B-79BF-4F7C-9E66-976D14B9DC6C}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
|
||||
{B6EAE19B-79BF-4F7C-9E66-976D14B9DC6C}.Debug|Mixed Platforms.Build.0 = Debug|Win32
|
||||
{B6EAE19B-79BF-4F7C-9E66-976D14B9DC6C}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{B6EAE19B-79BF-4F7C-9E66-976D14B9DC6C}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{B6EAE19B-79BF-4F7C-9E66-976D14B9DC6C}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{B6EAE19B-79BF-4F7C-9E66-976D14B9DC6C}.Debug|x64.Build.0 = Debug|x64
|
||||
{B6EAE19B-79BF-4F7C-9E66-976D14B9DC6C}.Release|Mixed Platforms.ActiveCfg = Release|Win32
|
||||
{B6EAE19B-79BF-4F7C-9E66-976D14B9DC6C}.Release|Mixed Platforms.Build.0 = Release|Win32
|
||||
{B6EAE19B-79BF-4F7C-9E66-976D14B9DC6C}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{B6EAE19B-79BF-4F7C-9E66-976D14B9DC6C}.Release|Win32.Build.0 = Release|Win32
|
||||
{B6EAE19B-79BF-4F7C-9E66-976D14B9DC6C}.Release|x64.ActiveCfg = Release|x64
|
||||
{B6EAE19B-79BF-4F7C-9E66-976D14B9DC6C}.Release|x64.Build.0 = Release|x64
|
||||
{76226D20-1972-4789-A595-EDACC7A76DC3}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
|
||||
{76226D20-1972-4789-A595-EDACC7A76DC3}.Debug|Mixed Platforms.Build.0 = Debug|Win32
|
||||
{76226D20-1972-4789-A595-EDACC7A76DC3}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{76226D20-1972-4789-A595-EDACC7A76DC3}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{76226D20-1972-4789-A595-EDACC7A76DC3}.Debug|x64.ActiveCfg = Debug|Win32
|
||||
{76226D20-1972-4789-A595-EDACC7A76DC3}.Debug|x64.Build.0 = Debug|Win32
|
||||
{76226D20-1972-4789-A595-EDACC7A76DC3}.Release|Mixed Platforms.ActiveCfg = Release|Win32
|
||||
{76226D20-1972-4789-A595-EDACC7A76DC3}.Release|Mixed Platforms.Build.0 = Release|Win32
|
||||
{76226D20-1972-4789-A595-EDACC7A76DC3}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{76226D20-1972-4789-A595-EDACC7A76DC3}.Release|Win32.Build.0 = Release|Win32
|
||||
{76226D20-1972-4789-A595-EDACC7A76DC3}.Release|x64.ActiveCfg = Release|Win32
|
||||
{76226D20-1972-4789-A595-EDACC7A76DC3}.Release|x64.Build.0 = Release|Win32
|
||||
{36970254-B1E5-4BE6-A561-301B6E7CDCE3}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
|
||||
{36970254-B1E5-4BE6-A561-301B6E7CDCE3}.Debug|Mixed Platforms.Build.0 = Debug|Win32
|
||||
{36970254-B1E5-4BE6-A561-301B6E7CDCE3}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{36970254-B1E5-4BE6-A561-301B6E7CDCE3}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{36970254-B1E5-4BE6-A561-301B6E7CDCE3}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{36970254-B1E5-4BE6-A561-301B6E7CDCE3}.Debug|x64.Build.0 = Debug|x64
|
||||
{36970254-B1E5-4BE6-A561-301B6E7CDCE3}.Release|Mixed Platforms.ActiveCfg = Release|Win32
|
||||
{36970254-B1E5-4BE6-A561-301B6E7CDCE3}.Release|Mixed Platforms.Build.0 = Release|Win32
|
||||
{36970254-B1E5-4BE6-A561-301B6E7CDCE3}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{36970254-B1E5-4BE6-A561-301B6E7CDCE3}.Release|Win32.Build.0 = Release|Win32
|
||||
{36970254-B1E5-4BE6-A561-301B6E7CDCE3}.Release|x64.ActiveCfg = Release|x64
|
||||
{36970254-B1E5-4BE6-A561-301B6E7CDCE3}.Release|x64.Build.0 = Release|x64
|
||||
{B12702AD-ABFB-343A-A199-8E24837244A3}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
|
||||
{B12702AD-ABFB-343A-A199-8E24837244A3}.Debug|Mixed Platforms.Build.0 = Debug|Win32
|
||||
{B12702AD-ABFB-343A-A199-8E24837244A3}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{B12702AD-ABFB-343A-A199-8E24837244A3}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{B12702AD-ABFB-343A-A199-8E24837244A3}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{B12702AD-ABFB-343A-A199-8E24837244A3}.Debug|x64.Build.0 = Debug|x64
|
||||
{B12702AD-ABFB-343A-A199-8E24837244A3}.Release|Mixed Platforms.ActiveCfg = Release|Win32
|
||||
{B12702AD-ABFB-343A-A199-8E24837244A3}.Release|Mixed Platforms.Build.0 = Release|Win32
|
||||
{B12702AD-ABFB-343A-A199-8E24837244A3}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{B12702AD-ABFB-343A-A199-8E24837244A3}.Release|Win32.Build.0 = Release|Win32
|
||||
{B12702AD-ABFB-343A-A199-8E24837244A3}.Release|x64.ActiveCfg = Release|x64
|
||||
{B12702AD-ABFB-343A-A199-8E24837244A3}.Release|x64.Build.0 = Release|x64
|
||||
{A3D24C9D-669D-4DDF-91BA-152D7DDD7C04}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
|
||||
{A3D24C9D-669D-4DDF-91BA-152D7DDD7C04}.Debug|Mixed Platforms.Build.0 = Debug|Win32
|
||||
{A3D24C9D-669D-4DDF-91BA-152D7DDD7C04}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{A3D24C9D-669D-4DDF-91BA-152D7DDD7C04}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{A3D24C9D-669D-4DDF-91BA-152D7DDD7C04}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{A3D24C9D-669D-4DDF-91BA-152D7DDD7C04}.Debug|x64.Build.0 = Debug|x64
|
||||
{A3D24C9D-669D-4DDF-91BA-152D7DDD7C04}.Release|Mixed Platforms.ActiveCfg = Release|Win32
|
||||
{A3D24C9D-669D-4DDF-91BA-152D7DDD7C04}.Release|Mixed Platforms.Build.0 = Release|Win32
|
||||
{A3D24C9D-669D-4DDF-91BA-152D7DDD7C04}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{A3D24C9D-669D-4DDF-91BA-152D7DDD7C04}.Release|Win32.Build.0 = Release|Win32
|
||||
{A3D24C9D-669D-4DDF-91BA-152D7DDD7C04}.Release|x64.ActiveCfg = Release|x64
|
||||
{A3D24C9D-669D-4DDF-91BA-152D7DDD7C04}.Release|x64.Build.0 = Release|x64
|
||||
{AB83E5F0-D76E-45AB-A4C9-4711B1BE6916}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
|
||||
{AB83E5F0-D76E-45AB-A4C9-4711B1BE6916}.Debug|Mixed Platforms.Build.0 = Debug|Win32
|
||||
{AB83E5F0-D76E-45AB-A4C9-4711B1BE6916}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{AB83E5F0-D76E-45AB-A4C9-4711B1BE6916}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{AB83E5F0-D76E-45AB-A4C9-4711B1BE6916}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{AB83E5F0-D76E-45AB-A4C9-4711B1BE6916}.Debug|x64.Build.0 = Debug|x64
|
||||
{AB83E5F0-D76E-45AB-A4C9-4711B1BE6916}.Release|Mixed Platforms.ActiveCfg = Release|Win32
|
||||
{AB83E5F0-D76E-45AB-A4C9-4711B1BE6916}.Release|Mixed Platforms.Build.0 = Release|Win32
|
||||
{AB83E5F0-D76E-45AB-A4C9-4711B1BE6916}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{AB83E5F0-D76E-45AB-A4C9-4711B1BE6916}.Release|Win32.Build.0 = Release|Win32
|
||||
{AB83E5F0-D76E-45AB-A4C9-4711B1BE6916}.Release|x64.ActiveCfg = Release|x64
|
||||
{AB83E5F0-D76E-45AB-A4C9-4711B1BE6916}.Release|x64.Build.0 = Release|x64
|
||||
{1798CAF2-531A-4E0E-A722-29F4B1B5D49F}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
|
||||
{1798CAF2-531A-4E0E-A722-29F4B1B5D49F}.Debug|Mixed Platforms.Build.0 = Debug|Win32
|
||||
{1798CAF2-531A-4E0E-A722-29F4B1B5D49F}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{1798CAF2-531A-4E0E-A722-29F4B1B5D49F}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{1798CAF2-531A-4E0E-A722-29F4B1B5D49F}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{1798CAF2-531A-4E0E-A722-29F4B1B5D49F}.Debug|x64.Build.0 = Debug|x64
|
||||
{1798CAF2-531A-4E0E-A722-29F4B1B5D49F}.Release|Mixed Platforms.ActiveCfg = Release|Win32
|
||||
{1798CAF2-531A-4E0E-A722-29F4B1B5D49F}.Release|Mixed Platforms.Build.0 = Release|Win32
|
||||
{1798CAF2-531A-4E0E-A722-29F4B1B5D49F}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{1798CAF2-531A-4E0E-A722-29F4B1B5D49F}.Release|Win32.Build.0 = Release|Win32
|
||||
{1798CAF2-531A-4E0E-A722-29F4B1B5D49F}.Release|x64.ActiveCfg = Release|x64
|
||||
{1798CAF2-531A-4E0E-A722-29F4B1B5D49F}.Release|x64.Build.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
180
vs/2013/obs-x264/obs-x264.vcxproj
Normal file
180
vs/2013/obs-x264/obs-x264.vcxproj
Normal file
@ -0,0 +1,180 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{1798CAF2-531A-4E0E-A722-29F4B1B5D49F}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>obsx264</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v120</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v120</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v120</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v120</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<IncludePath>$(x264Path);$(IncludePath)</IncludePath>
|
||||
<LibraryPath>$(x264Path)\lib32;$(x264Path);$(LibraryPath)</LibraryPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<IncludePath>$(x264Path);$(IncludePath)</IncludePath>
|
||||
<LibraryPath>$(x264Path)\lib64;$(x264Path);$(LibraryPath)</LibraryPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<IncludePath>$(x264Path);$(IncludePath)</IncludePath>
|
||||
<LibraryPath>$(x264Path)\lib32;$(x264Path);$(LibraryPath)</LibraryPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<IncludePath>$(x264Path);$(IncludePath)</IncludePath>
|
||||
<LibraryPath>$(x264Path)\lib64;$(x264Path);$(LibraryPath)</LibraryPath>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;OBSX264_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>../../../libobs</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<AdditionalDependencies>x264.lib;libobs.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>copy "$(OutDir)$(TargetName)$(TargetExt)" "../../../build/obs-plugins/32bit/$(TargetName)$(TargetExt)"</Command>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;OBSX264_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>../../../libobs</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<AdditionalDependencies>x264.lib;libobs.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>copy "$(OutDir)$(TargetName)$(TargetExt)" "../../../build/obs-plugins/64bit/$(TargetName)$(TargetExt)"</Command>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;OBSX264_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>../../../libobs</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<AdditionalDependencies>x264.lib;libobs.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>copy "$(OutDir)$(TargetName)$(TargetExt)" "../../../build/obs-plugins/32bit/$(TargetName)$(TargetExt)"</Command>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<PrecompiledHeader>
|
||||
</PrecompiledHeader>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;OBSX264_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>../../../libobs</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<AdditionalDependencies>x264.lib;libobs.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>copy "$(OutDir)$(TargetName)$(TargetExt)" "../../../build/obs-plugins/64bit/$(TargetName)$(TargetExt)"</Command>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\..\plugins\obs-x264\obs-x264-plugin-main.c" />
|
||||
<ClCompile Include="..\..\..\plugins\obs-x264\obs-x264.c" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
25
vs/2013/obs-x264/obs-x264.vcxproj.filters
Normal file
25
vs/2013/obs-x264/obs-x264.vcxproj.filters
Normal file
@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\..\plugins\obs-x264\obs-x264.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\plugins\obs-x264\obs-x264-plugin-main.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
Loading…
x
Reference in New Issue
Block a user