diff --git a/libobs/media-io/video-io.h b/libobs/media-io/video-io.h index 5a0a4a84d..77acf8f7a 100644 --- a/libobs/media-io/video-io.h +++ b/libobs/media-io/video-io.h @@ -135,15 +135,23 @@ static inline const char *get_video_colorspace_name(enum video_colorspace cs) return "601"; } -static inline const char *get_video_range_name(enum video_range_type range) +static inline enum video_range_type resolve_video_range( + enum video_format format, enum video_range_type range) { - switch (range) { - case VIDEO_RANGE_FULL: return "Full"; - case VIDEO_RANGE_PARTIAL: - case VIDEO_RANGE_DEFAULT:; + if (range == VIDEO_RANGE_DEFAULT) { + range = format_is_yuv(format) + ? VIDEO_RANGE_PARTIAL + : VIDEO_RANGE_FULL; } - return "Partial"; + return range; +} + +static inline const char *get_video_range_name(enum video_format format, + enum video_range_type range) +{ + range = resolve_video_range(format, range); + return range == VIDEO_RANGE_FULL ? "Full" : "Partial"; } enum video_scale_type { diff --git a/libobs/obs-source.c b/libobs/obs-source.c index 3ebeb6112..409140423 100644 --- a/libobs/obs-source.c +++ b/libobs/obs-source.c @@ -2436,7 +2436,7 @@ static inline struct obs_source_frame *cache_video(struct obs_source *source, return new_frame; } -void obs_source_output_video(obs_source_t *source, +static void obs_source_output_video_internal(obs_source_t *source, const struct obs_source_frame *frame) { if (!obs_source_valid(source, "obs_source_output_video")) @@ -2464,6 +2464,56 @@ void obs_source_output_video(obs_source_t *source, pthread_mutex_unlock(&source->async_mutex); } +void obs_source_output_video(obs_source_t *source, + const struct obs_source_frame *frame) +{ + if (!frame) { + obs_source_output_video_internal(source, NULL); + return; + } + + struct obs_source_frame new_frame = *frame; + new_frame.full_range = format_is_yuv(frame->format) + ? new_frame.full_range + : true; + + obs_source_output_video_internal(source, &new_frame); +} + +void obs_source_output_video2(obs_source_t *source, + const struct obs_source_frame2 *frame) +{ + if (!frame) { + obs_source_output_video_internal(source, NULL); + return; + } + + struct obs_source_frame new_frame; + enum video_range_type range = resolve_video_range(frame->format, + frame->range); + + for (size_t i = 0; i < MAX_AV_PLANES; i++) { + new_frame.data[i] = frame->data[i]; + new_frame.linesize[i] = frame->linesize[i]; + } + + new_frame.width = frame->width; + new_frame.height = frame->height; + new_frame.timestamp = frame->timestamp; + new_frame.format = frame->format; + new_frame.full_range = range == VIDEO_RANGE_FULL; + new_frame.flip = frame->flip; + + memcpy(&new_frame.color_matrix, &frame->color_matrix, + sizeof(frame->color_matrix)); + memcpy(&new_frame.color_range_min, &frame->color_range_min, + sizeof(frame->color_range_min)); + memcpy(&new_frame.color_range_max, &frame->color_range_max, + sizeof(frame->color_range_max)); + + obs_source_output_video_internal(source, &new_frame); +} + static inline bool preload_frame_changed(obs_source_t *source, const struct obs_source_frame *in) { @@ -2475,7 +2525,7 @@ static inline bool preload_frame_changed(obs_source_t *source, in->format != source->async_preload_frame->format; } -void obs_source_preload_video(obs_source_t *source, +static void obs_source_preload_video_internal(obs_source_t *source, const struct obs_source_frame *frame) { if (!obs_source_valid(source, "obs_source_preload_video")) @@ -2504,6 +2554,56 @@ void obs_source_preload_video(obs_source_t *source, obs_leave_graphics(); } +void obs_source_preload_video(obs_source_t *source, + const struct obs_source_frame *frame) +{ + if (!frame) { + obs_source_preload_video_internal(source, NULL); + return; + } + + struct obs_source_frame new_frame = *frame; + new_frame.full_range = format_is_yuv(frame->format) + ? new_frame.full_range + : true; + + obs_source_preload_video_internal(source, &new_frame); +} + +void obs_source_preload_video2(obs_source_t *source, + const struct obs_source_frame2 *frame) +{ + if (!frame) { + obs_source_preload_video_internal(source, NULL); + return; + } + + struct obs_source_frame new_frame; + enum video_range_type range = resolve_video_range(frame->format, + frame->range); + + for (size_t i = 0; i < MAX_AV_PLANES; i++) { + new_frame.data[i] = frame->data[i]; + new_frame.linesize[i] = frame->linesize[i]; + } + + new_frame.width = frame->width; + new_frame.height = frame->height; + new_frame.timestamp = frame->timestamp; + new_frame.format = frame->format; + new_frame.full_range = range == VIDEO_RANGE_FULL; + new_frame.flip = frame->flip; + + memcpy(&new_frame.color_matrix, &frame->color_matrix, + sizeof(frame->color_matrix)); + memcpy(&new_frame.color_range_min, &frame->color_range_min, + sizeof(frame->color_range_min)); + memcpy(&new_frame.color_range_max, &frame->color_range_max, + sizeof(frame->color_range_max)); + + obs_source_preload_video_internal(source, &new_frame); +} + void obs_source_show_preloaded_video(obs_source_t *source) { uint64_t sys_ts; diff --git a/libobs/obs.c b/libobs/obs.c index 6d98ebb61..9fe7e6447 100644 --- a/libobs/obs.c +++ b/libobs/obs.c @@ -1129,7 +1129,8 @@ int obs_reset_video(struct obs_video_info *ovi) bool yuv = format_is_yuv(ovi->output_format); const char *yuv_format = get_video_colorspace_name(ovi->colorspace); - const char *yuv_range = get_video_range_name(ovi->range); + const char *yuv_range = get_video_range_name(ovi->output_format, + ovi->range); blog(LOG_INFO, "---------------------------------"); blog(LOG_INFO, "video settings reset:\n" diff --git a/libobs/obs.h b/libobs/obs.h index 5863087eb..fcf5e0e57 100644 --- a/libobs/obs.h +++ b/libobs/obs.h @@ -220,6 +220,10 @@ struct obs_source_audio { * * If a YUV format is specified, it will be automatically upsampled and * converted to RGB via shader on the graphics processor. + * + * NOTE: Non-YUV formats will always be treated as full range with this + * structure! Use obs_source_frame2 along with obs_source_output_video2 + * instead if partial range support is desired for non-YUV video formats. */ struct obs_source_frame { uint8_t *data[MAX_AV_PLANES]; @@ -240,6 +244,21 @@ struct obs_source_frame { bool prev_frame; }; +struct obs_source_frame2 { + uint8_t *data[MAX_AV_PLANES]; + uint32_t linesize[MAX_AV_PLANES]; + uint32_t width; + uint32_t height; + uint64_t timestamp; + + enum video_format format; + enum video_range_type range; + float color_matrix[16]; + float color_range_min[3]; + float color_range_max[3]; + bool flip; +}; + /** Access to the argc/argv used to start OBS. What you see is what you get. */ struct obs_cmdline_args { int argc; @@ -1117,13 +1136,29 @@ EXPORT void obs_source_draw_set_color_matrix( EXPORT void obs_source_draw(gs_texture_t *image, int x, int y, uint32_t cx, uint32_t cy, bool flip); -/** Outputs asynchronous video data. Set to NULL to deactivate the texture */ +/** + * Outputs asynchronous video data. Set to NULL to deactivate the texture + * + * NOTE: Non-YUV formats will always be treated as full range with this + * function! Use obs_source_output_video2 instead if partial range support is + * desired for non-YUV video formats. + */ EXPORT void obs_source_output_video(obs_source_t *source, const struct obs_source_frame *frame); +EXPORT void obs_source_output_video2(obs_source_t *source, + const struct obs_source_frame2 *frame); -/** Preloads asynchronous video data to allow instantaneous playback */ +/** + * Preloads asynchronous video data to allow instantaneous playback + * + * NOTE: Non-YUV formats will always be treated as full range with this + * function! Use obs_source_preload_video2 instead if partial range support is + * desired for non-YUV video formats. + */ EXPORT void obs_source_preload_video(obs_source_t *source, const struct obs_source_frame *frame); +EXPORT void obs_source_preload_video2(obs_source_t *source, + const struct obs_source_frame2 *frame); /** Shows any preloaded video data */ EXPORT void obs_source_show_preloaded_video(obs_source_t *source);