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:
jp9000 2014-03-16 16:21:34 -07:00
parent 04d07831cc
commit fd37d9e9a8
36 changed files with 1423 additions and 310 deletions

View file

@ -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);

View file

@ -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,

View file

@ -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);

View file

@ -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;

View file

@ -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);

View file

@ -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;

View file

@ -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);

View file

@ -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;

View file

@ -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);

View file

@ -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)

View file

@ -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);

View file

@ -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: "

View file

@ -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;
}

View file

@ -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);
};
/**

View file

@ -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;
};

View file

@ -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,

View file

@ -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;
}

View file

@ -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 */

View file

@ -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));

View file

@ -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);

View file

@ -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);

View file

@ -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)

View file

@ -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;

View file

@ -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})

View file

@ -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;
}

View file

@ -1,5 +0,0 @@
#pragma once
#include <util/c99defs.h>
EXPORT const char *enum_outputs(size_t idx);

View file

@ -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)
{
}

View file

@ -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);

View file

@ -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)
{
}

View file

@ -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);

View 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})

View 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
View 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
};

View file

@ -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

View 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>

View 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>