From f2d4de3c0320e4b84122458d098dd4a206927da9 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Tue, 18 Feb 2014 13:37:56 -0700 Subject: [PATCH] Implement automatic video scaling (if requested) Add a scaler interface (defaults to swscale), and if a separate output wants to use a different scale or format than the default output format, allow a scaler instance to be created automatically for that output, which will then receive the new scaled output. --- libobs/CMakeLists.txt | 19 +++- libobs/media-io/audio-io.c | 20 +++- libobs/media-io/audio-io.h | 2 +- libobs/media-io/video-frame.c | 86 +++++++++++++++ libobs/media-io/video-frame.h | 53 +++++++++ libobs/media-io/video-io.c | 131 ++++++++++++++++++---- libobs/media-io/video-io.h | 26 ++--- libobs/media-io/video-scaler-ffmpeg.c | 146 +++++++++++++++++++++++++ libobs/media-io/video-scaler.h | 68 ++++++++++++ libobs/obs-source.c | 69 ++---------- libobs/obs-video.c | 16 +-- plugins/obs-ffmpeg/obs-ffmpeg-output.c | 22 ++-- vs/2013/libobs/libobs.vcxproj | 12 +- vs/2013/libobs/libobs.vcxproj.filters | 12 ++ 14 files changed, 552 insertions(+), 130 deletions(-) create mode 100644 libobs/media-io/video-frame.c create mode 100644 libobs/media-io/video-frame.h create mode 100644 libobs/media-io/video-scaler-ffmpeg.c create mode 100644 libobs/media-io/video-scaler.h diff --git a/libobs/CMakeLists.txt b/libobs/CMakeLists.txt index f2443e9b8..47d07271b 100644 --- a/libobs/CMakeLists.txt +++ b/libobs/CMakeLists.txt @@ -1,5 +1,9 @@ project(libobs) +find_package(Libswscale REQUIRED) +include_directories(${Libswscale_INCLUDE_DIR}) +add_definitions(${Libswscale_DEFINITIONS}) + find_package(Libswresample REQUIRED) include_directories(${Libswresample_INCLUDE_DIR}) add_definitions(${Libswresample_DEFINITIONS}) @@ -96,15 +100,19 @@ set(libobs_graphics_HEADERS set(libobs_mediaio_SOURCES media-io/video-io.c - media-io/audio-resampler-ffmpeg.c + media-io/audio-io.c + media-io/video-frame.c media-io/format-conversion.c - media-io/audio-io.c) + media-io/audio-resampler-ffmpeg.c + media-io/video-scaler-ffmpeg.c) set(libobs_mediaio_HEADERS media-io/media-io-defs.h - media-io/format-conversion.h media-io/video-io.h + media-io/audio-io.h + media-io/video-frame.h + media-io/format-conversion.h media-io/audio-resampler.h - media-io/audio-io.h) + media-io/video-scaler.h) set(libobs_util_SOURCES util/base.c @@ -196,10 +204,11 @@ set_target_properties(libobs PROPERTIES SOVERSION "0") target_link_libraries(libobs ${libobs_PLATFORM_DEPS} + ${Libswscale_LIBRARIES} ${Libswresample_LIBRARIES} ${Libavutil_LIBRARIES}) install_obs_core(libobs) install_obs_data(libobs ../build/data/libobs libobs) -obs_fixup_install_target(libobs PATH ${Libswresample_LIBRARIES} ${Libavutil_LIBRARIES}) +obs_fixup_install_target(libobs PATH ${Libswscale_LIBRARIES} ${Libswresample_LIBRARIES} ${Libavutil_LIBRARIES}) diff --git a/libobs/media-io/audio-io.c b/libobs/media-io/audio-io.c index 400b36600..cc451d80c 100644 --- a/libobs/media-io/audio-io.c +++ b/libobs/media-io/audio-io.c @@ -465,7 +465,7 @@ static size_t audio_get_input_idx(audio_t video, return DARRAY_INVALID; } -static inline void audio_input_init(struct audio_input *input, +static inline bool audio_input_init(struct audio_input *input, struct audio_output *audio) { if (input->conversion.format != audio->info.format || @@ -484,16 +484,25 @@ static inline void audio_input_init(struct audio_input *input, }; input->resampler = audio_resampler_create(&to, &from); + if (!input->resampler) { + blog(LOG_WARNING, "audio_input_init: Failed to " + "create resampler"); + return false; + } } else { input->resampler = NULL; } + + return true; } -void audio_output_connect(audio_t audio, +bool audio_output_connect(audio_t audio, struct audio_convert_info *conversion, void (*callback)(void *param, const struct audio_data *data), void *param) { + bool success = false; + pthread_mutex_lock(&audio->input_mutex); if (audio_get_input_idx(audio, callback, param) == DARRAY_INVALID) { @@ -510,11 +519,14 @@ void audio_output_connect(audio_t audio, audio->info.samples_per_sec; } - audio_input_init(&input, audio); - da_push_back(audio->inputs, &input); + success = audio_input_init(&input, audio); + if (success) + da_push_back(audio->inputs, &input); } pthread_mutex_unlock(&audio->input_mutex); + + return success; } void audio_output_disconnect(audio_t audio, diff --git a/libobs/media-io/audio-io.h b/libobs/media-io/audio-io.h index 525d2831f..7876a39ef 100644 --- a/libobs/media-io/audio-io.h +++ b/libobs/media-io/audio-io.h @@ -164,7 +164,7 @@ static inline size_t get_audio_size(enum audio_format type, EXPORT int audio_output_open(audio_t *audio, struct audio_output_info *info); EXPORT void audio_output_close(audio_t audio); -EXPORT void audio_output_connect(audio_t video, +EXPORT bool audio_output_connect(audio_t video, struct audio_convert_info *conversion, void (*callback)(void *param, const struct audio_data *data), void *param); diff --git a/libobs/media-io/video-frame.c b/libobs/media-io/video-frame.c new file mode 100644 index 000000000..a761d7012 --- /dev/null +++ b/libobs/media-io/video-frame.c @@ -0,0 +1,86 @@ +/****************************************************************************** + Copyright (C) 2013 by Hugh Bailey + + 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 . +******************************************************************************/ + +#include "video-frame.h" + +#define ALIGN_SIZE(size, align) \ + size = (((size)+(align-1)) & (~(align-1))) + +/* messy code alarm */ +void video_frame_init(struct video_frame *frame, enum video_format format, + uint32_t width, uint32_t height) +{ + size_t size; + size_t offsets[MAX_AV_PLANES]; + int alignment = base_get_alignment(); + + memset(frame, 0, sizeof(struct video_frame)); + memset(offsets, 0, sizeof(offsets)); + + switch (format) { + case VIDEO_FORMAT_NONE: + return; + + case VIDEO_FORMAT_I420: + size = width * height; + ALIGN_SIZE(size, alignment); + offsets[0] = size; + size += (width/2) * (height/2); + ALIGN_SIZE(size, alignment); + offsets[1] = size; + size += (width/2) * (height/2); + ALIGN_SIZE(size, alignment); + frame->data[0] = bmalloc(size); + frame->data[1] = (uint8_t*)frame->data[0] + offsets[0]; + frame->data[2] = (uint8_t*)frame->data[0] + offsets[1]; + frame->linesize[0] = width; + frame->linesize[1] = width/2; + frame->linesize[2] = width/2; + break; + + case VIDEO_FORMAT_NV12: + size = width * height; + ALIGN_SIZE(size, alignment); + offsets[0] = size; + size += (width/2) * (height/2) * 2; + ALIGN_SIZE(size, alignment); + frame->data[0] = bmalloc(size); + frame->data[1] = (uint8_t*)frame->data[0] + offsets[0]; + frame->linesize[0] = width; + frame->linesize[1] = width; + break; + + case VIDEO_FORMAT_YVYU: + case VIDEO_FORMAT_YUY2: + case VIDEO_FORMAT_UYVY: + size = width * height * 2; + ALIGN_SIZE(size, alignment); + frame->data[0] = bmalloc(size); + frame->linesize[0] = width*2; + break; + + case VIDEO_FORMAT_RGBA: + case VIDEO_FORMAT_BGRA: + case VIDEO_FORMAT_BGRX: + size = width * height * 4; + ALIGN_SIZE(size, alignment); + frame->data[0] = bmalloc(size); + frame->linesize[0] = width*4; + break; + } +} + diff --git a/libobs/media-io/video-frame.h b/libobs/media-io/video-frame.h new file mode 100644 index 000000000..ac549c81b --- /dev/null +++ b/libobs/media-io/video-frame.h @@ -0,0 +1,53 @@ +/****************************************************************************** + Copyright (C) 2013 by Hugh Bailey + + 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 . +******************************************************************************/ + +#include "../util/bmem.h" +#include "video-io.h" + +struct video_frame { + uint8_t *data[MAX_AV_PLANES]; + uint32_t linesize[MAX_AV_PLANES]; +}; + +EXPORT void video_frame_init(struct video_frame *frame, + enum video_format format, uint32_t width, uint32_t height); + +static inline void video_frame_free(struct video_frame *frame) +{ + if (frame) { + bfree(frame->data[0]); + memset(frame, 0, sizeof(struct video_frame)); + } +} + +static inline struct video_frame *video_frame_create( + enum video_format format, uint32_t width, uint32_t height) +{ + struct video_frame *frame; + + frame = (struct video_frame*)bzalloc(sizeof(struct video_frame)); + video_frame_init(frame, format, width, height); + return frame; +} + +static inline void video_frame_destroy(struct video_frame *frame) +{ + if (frame) { + bfree(frame->data[0]); + bfree(frame); + } +} diff --git a/libobs/media-io/video-io.c b/libobs/media-io/video-io.c index e7e7994d0..083665390 100644 --- a/libobs/media-io/video-io.c +++ b/libobs/media-io/video-io.c @@ -23,13 +23,28 @@ #include "format-conversion.h" #include "video-io.h" +#include "video-frame.h" +#include "video-scaler.h" + +#define MAX_CONVERT_BUFFERS 3 struct video_input { - struct video_convert_info conversion; - void (*callback)(void *param, const struct video_frame *frame); + struct video_scale_info conversion; + video_scaler_t scaler; + struct video_frame frame[MAX_CONVERT_BUFFERS]; + int cur_frame; + + void (*callback)(void *param, const struct video_data *frame); void *param; }; +static inline void video_input_free(struct video_input *input) +{ + for (size_t i = 0; i < MAX_CONVERT_BUFFERS; i++) + video_frame_free(&input->frame[i]); + video_scaler_destroy(input->scaler); +} + struct video_output { struct video_output_info info; @@ -37,8 +52,8 @@ struct video_output { pthread_mutex_t data_mutex; event_t stop_event; - struct video_frame cur_frame; - struct video_frame next_frame; + struct video_data cur_frame; + struct video_data next_frame; bool new_frame; event_t update_event; @@ -61,6 +76,34 @@ static inline void video_swapframes(struct video_output *video) } } +static inline bool scale_video_output(struct video_input *input, + struct video_data *data) +{ + bool success = true; + + if (input->scaler) { + struct video_frame *frame; + + if (++input->cur_frame == MAX_CONVERT_BUFFERS) + input->cur_frame = 0; + + frame = &input->frame[input->cur_frame]; + + success = video_scaler_scale(input->scaler, + frame->data, frame->linesize, + data->data, data->linesize); + + if (success) { + for (size_t i = 0; i < MAX_AV_PLANES; i++) { + data->data[i] = frame->data[i]; + data->linesize[i] = frame->linesize[i]; + } + } + } + + return success; +} + static inline void video_output_cur_frame(struct video_output *video) { if (!video->cur_frame.data[0]) @@ -68,10 +111,10 @@ static inline void video_output_cur_frame(struct video_output *video) pthread_mutex_lock(&video->input_mutex); - /* TODO: conversion */ for (size_t i = 0; i < video->inputs.num; i++) { struct video_input *input = video->inputs.array+i; - input->callback(input->param, &video->cur_frame); + if (scale_video_output(input, &video->cur_frame)) + input->callback(input->param, &video->cur_frame); } pthread_mutex_unlock(&video->input_mutex); @@ -151,7 +194,10 @@ void video_output_close(video_t video) video_output_stop(video); + for (size_t i = 0; i < video->inputs.num; i++) + video_input_free(&video->inputs.array[i]); da_free(video->inputs); + event_destroy(&video->update_event); event_destroy(&video->stop_event); pthread_mutex_destroy(&video->data_mutex); @@ -160,7 +206,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_frame *frame), + void (*callback)(void *param, const struct video_data *frame), void *param) { for (size_t i = 0; i < video->inputs.num; i++) { @@ -172,47 +218,92 @@ static size_t video_get_input_idx(video_t video, return DARRAY_INVALID; } -void video_output_connect(video_t video, - struct video_convert_info *conversion, - void (*callback)(void *param, const struct video_frame *frame), +static inline bool video_input_init(struct video_input *input, + struct video_output *video) +{ + if (input->conversion.width != video->info.width || + input->conversion.height != video->info.height || + input->conversion.format != video->info.format) { + struct video_scale_info from = { + .format = video->info.format, + .width = video->info.width, + .height = video->info.height, + }; + + int ret = video_scaler_create(&input->scaler, + &input->conversion, &from, + VIDEO_SCALE_FAST_BILINEAR); + if (ret != VIDEO_SCALER_SUCCESS) { + if (ret == VIDEO_SCALER_BAD_CONVERSION) + blog(LOG_WARNING, "video_input_init: Bad " + "scale conversion type"); + else + blog(LOG_WARNING, "video_input_init: Failed to " + "create scaler"); + + return false; + } + + for (size_t i = 0; i < MAX_CONVERT_BUFFERS; i++) + video_frame_init(&input->frame[i], + input->conversion.format, + input->conversion.width, + input->conversion.height); + } + + return true; +} + +bool video_output_connect(video_t video, + struct video_scale_info *conversion, + void (*callback)(void *param, const struct video_data *frame), void *param) { + bool success = false; + pthread_mutex_lock(&video->input_mutex); if (video_get_input_idx(video, callback, param) == DARRAY_INVALID) { struct video_input input; + memset(&input, 0, sizeof(input)); + input.callback = callback; input.param = param; - /* TODO: conversion */ if (conversion) { input.conversion = *conversion; - - if (input.conversion.width == 0) - input.conversion.width = video->info.width; - if (input.conversion.height == 0) - input.conversion.height = video->info.height; } else { input.conversion.format = video->info.format; input.conversion.width = video->info.width; input.conversion.height = video->info.height; } - da_push_back(video->inputs, &input); + if (input.conversion.width == 0) + input.conversion.width = video->info.width; + if (input.conversion.height == 0) + input.conversion.height = video->info.height; + + success = video_input_init(&input, video); + if (success) + da_push_back(video->inputs, &input); } pthread_mutex_unlock(&video->input_mutex); + + return success; } void video_output_disconnect(video_t video, - void (*callback)(void *param, const struct video_frame *frame), + void (*callback)(void *param, const struct video_data *frame), void *param) { pthread_mutex_lock(&video->input_mutex); size_t idx = video_get_input_idx(video, callback, param); - if (idx != DARRAY_INVALID) + if (idx != DARRAY_INVALID) { + video_input_free(video->inputs.array+idx); da_erase(video->inputs, idx); + } pthread_mutex_unlock(&video->input_mutex); } @@ -222,7 +313,7 @@ const struct video_output_info *video_output_getinfo(video_t video) return &video->info; } -void video_output_frame(video_t video, struct video_frame *frame) +void video_output_swap_frame(video_t video, struct video_data *frame) { pthread_mutex_lock(&video->data_mutex); video->next_frame = *frame; diff --git a/libobs/media-io/video-io.h b/libobs/media-io/video-io.h index b5aea54ce..a1ad7b976 100644 --- a/libobs/media-io/video-io.h +++ b/libobs/media-io/video-io.h @@ -18,13 +18,13 @@ #pragma once #include "media-io-defs.h" -#include "../util/c99defs.h" +#include "video-scaler.h" #ifdef __cplusplus extern "C" { #endif -/* Base video output component. Use this to create an video output track. */ +/* Base video output component. Use this to create a video output track. */ struct video_output; typedef struct video_output *video_t; @@ -47,7 +47,7 @@ enum video_format { VIDEO_FORMAT_BGRX, }; -struct video_frame { +struct video_data { const uint8_t *data[MAX_AV_PLANES]; uint32_t linesize[MAX_AV_PLANES]; uint64_t timestamp; @@ -63,12 +63,6 @@ struct video_output_info { uint32_t height; }; -struct video_convert_info { - enum video_format format; - uint32_t width; - uint32_t height; -}; - static inline bool format_is_yuv(enum video_format format) { switch (format) { @@ -95,20 +89,20 @@ static inline bool format_is_yuv(enum video_format format) EXPORT int video_output_open(video_t *video, struct video_output_info *info); EXPORT void video_output_close(video_t video); -EXPORT void video_output_connect(video_t video, - struct video_convert_info *conversion, - void (*callback)(void *param, const struct video_frame *frame), +EXPORT bool video_output_connect(video_t video, + struct video_scale_info *conversion, + void (*callback)(void *param, const struct video_data *frame), void *param); EXPORT void video_output_disconnect(video_t video, - void (*callback)(void *param, const struct video_frame *frame), + void (*callback)(void *param, const struct video_data *frame), void *param); EXPORT const struct video_output_info *video_output_getinfo(video_t video); -EXPORT void video_output_frame(video_t video, struct video_frame *frame); -EXPORT bool video_output_wait(video_t video); +EXPORT void video_output_swap_frame(video_t video, struct video_data *frame); +EXPORT bool video_output_wait(video_t video); EXPORT uint64_t video_getframetime(video_t video); EXPORT uint64_t video_gettime(video_t video); -EXPORT void video_output_stop(video_t video); +EXPORT void video_output_stop(video_t video); #ifdef __cplusplus } diff --git a/libobs/media-io/video-scaler-ffmpeg.c b/libobs/media-io/video-scaler-ffmpeg.c new file mode 100644 index 000000000..1e00bab9d --- /dev/null +++ b/libobs/media-io/video-scaler-ffmpeg.c @@ -0,0 +1,146 @@ +/****************************************************************************** + Copyright (C) 2014 by Hugh Bailey + + 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 . +******************************************************************************/ + +#include "../util/bmem.h" +#include "video-scaler.h" + +#include + +struct video_scaler { + struct SwsContext *swscale; + int src_height; +}; + +static inline enum AVPixelFormat get_ffmpeg_video_format( + enum video_format format) +{ + switch (format) { + case VIDEO_FORMAT_NONE: return AV_PIX_FMT_NONE; + case VIDEO_FORMAT_I420: return AV_PIX_FMT_YUV420P; + case VIDEO_FORMAT_NV12: return AV_PIX_FMT_NV12; + case VIDEO_FORMAT_YVYU: return AV_PIX_FMT_NONE; + case VIDEO_FORMAT_YUY2: return AV_PIX_FMT_YUYV422; + case VIDEO_FORMAT_UYVY: return AV_PIX_FMT_UYVY422; + case VIDEO_FORMAT_RGBA: return AV_PIX_FMT_RGBA; + case VIDEO_FORMAT_BGRA: return AV_PIX_FMT_BGRA; + case VIDEO_FORMAT_BGRX: return AV_PIX_FMT_BGRA; + } + + return AV_PIX_FMT_NONE; +} + +static inline int get_ffmpeg_scale_type(enum video_scale_type type) +{ + switch (type) { + 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; + case VIDEO_SCALE_BICUBIC: return SWS_BICUBIC; + } + + return SWS_POINT; +} + +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); + } + + return sws_getCoefficients(SWS_CS_ITU601); +} + +#define FIXED_1_0 (1<<16) + +int video_scaler_create(video_scaler_t *scaler_out, + const struct video_scale_info *dst, + const struct video_scale_info *src, + enum video_scale_type type) +{ + enum AVPixelFormat format_src = get_ffmpeg_video_format(src->format); + enum AVPixelFormat format_dst = get_ffmpeg_video_format(dst->format); + 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); + struct video_scaler *scaler; + int ret; + + if (!scaler_out) + return VIDEO_SCALER_FAILED; + + if (format_src == AV_PIX_FMT_NONE || + format_dst == AV_PIX_FMT_NONE) + return VIDEO_SCALER_BAD_CONVERSION; + + scaler = bzalloc(sizeof(struct video_scaler)); + scaler->src_height = src->height; + + scaler->swscale = sws_getCachedContext(NULL, + src->width, src->height, format_src, + dst->width, dst->height, format_dst, + scale_type, NULL, NULL, NULL); + if (!scaler->swscale) { + blog(LOG_WARNING, "video_scaler_create: Could not create " + "swscale"); + goto fail; + } + + ret = sws_setColorspaceDetails(scaler->swscale, + coeff_src, src->full_range, + coeff_dst, dst->full_range, + 0, FIXED_1_0, FIXED_1_0); + if (ret < 0) { + blog(LOG_DEBUG, "video_scaler_create: " + "sws_setColorspaceDetails failed, ignoring"); + } + + *scaler_out = scaler; + return VIDEO_SCALER_SUCCESS; + +fail: + video_scaler_destroy(scaler); + return VIDEO_SCALER_FAILED; +} + +void video_scaler_destroy(video_scaler_t scaler) +{ + if (scaler) { + sws_freeContext(scaler->swscale); + bfree(scaler); + } +} + +bool video_scaler_scale(video_scaler_t scaler, + uint8_t *output[], const uint32_t out_linesize[], + const uint8_t *const input[], const uint32_t in_linesize[]) +{ + if (!scaler) + return false; + + int ret = sws_scale(scaler->swscale, + input, in_linesize, + 0, scaler->src_height, + output, out_linesize); + if (ret <= 0) { + blog(LOG_DEBUG, "video_scaler_scale: sws_scale failed: %d", + ret); + return false; + } + + return true; +} diff --git a/libobs/media-io/video-scaler.h b/libobs/media-io/video-scaler.h new file mode 100644 index 000000000..ec6be46af --- /dev/null +++ b/libobs/media-io/video-scaler.h @@ -0,0 +1,68 @@ +/****************************************************************************** + Copyright (C) 2014 by Hugh Bailey + + 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 . +******************************************************************************/ + +#pragma once + +#include "../util/c99defs.h" +#include "video-io.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct video_scaler; +typedef struct video_scaler *video_scaler_t; + +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, +}; + +enum video_colorspace { + VIDEO_CS_601 = 0, + VIDEO_CS_DEFAULT = VIDEO_CS_601, + VIDEO_CS_709 = 1, +}; + +struct video_scale_info { + enum video_format format; + uint32_t width; + uint32_t height; + bool full_range; + enum video_colorspace colorspace; +}; + +#define VIDEO_SCALER_SUCCESS 0 +#define VIDEO_SCALER_BAD_CONVERSION -1 +#define VIDEO_SCALER_FAILED -2 + +EXPORT int video_scaler_create(video_scaler_t *scaler, + const struct video_scale_info *dst, + const struct video_scale_info *src, + enum video_scale_type type); +EXPORT void video_scaler_destroy(video_scaler_t scaler); + +EXPORT bool video_scaler_scale(video_scaler_t scaler, + uint8_t *output[], const uint32_t out_linesize[], + const uint8_t *const input[], const uint32_t in_linesize[]); + +#ifdef __cplusplus +} +#endif diff --git a/libobs/obs-source.c b/libobs/obs-source.c index 3f07a1812..229937b89 100644 --- a/libobs/obs-source.c +++ b/libobs/obs-source.c @@ -18,6 +18,7 @@ #include #include "media-io/format-conversion.h" +#include "media-io/video-frame.h" #include "util/platform.h" #include "callback/calldata.h" #include "graphics/matrix3.h" @@ -166,72 +167,18 @@ fail: return NULL; } -#define ALIGN_SIZE(size, align) \ - size = (((size)+(align-1)) & (~(align-1))) - -/* messy code alarm */ -void source_frame_init(struct source_frame *frame, - enum video_format format, uint32_t width, uint32_t height) +void source_frame_init(struct source_frame *frame, enum video_format format, + uint32_t width, uint32_t height) { - size_t size; - size_t offsets[MAX_AV_PLANES]; - int alignment = base_get_alignment(); - - memset(offsets, 0, sizeof(offsets)); + struct video_frame vid_frame; + video_frame_init(&vid_frame, format, width, height); frame->format = format; frame->width = width; frame->height = height; - switch (format) { - case VIDEO_FORMAT_NONE: - return; - - case VIDEO_FORMAT_I420: - size = width * height; - ALIGN_SIZE(size, alignment); - offsets[0] = size; - size += (width/2) * (height/2); - ALIGN_SIZE(size, alignment); - offsets[1] = size; - size += (width/2) * (height/2); - ALIGN_SIZE(size, alignment); - frame->data[0] = bmalloc(size); - frame->data[1] = (uint8_t*)frame->data[0] + offsets[0]; - frame->data[2] = (uint8_t*)frame->data[0] + offsets[1]; - frame->linesize[0] = width; - frame->linesize[1] = width/2; - frame->linesize[2] = width/2; - break; - - case VIDEO_FORMAT_NV12: - size = width * height; - ALIGN_SIZE(size, alignment); - offsets[0] = size; - size += (width/2) * (height/2) * 2; - ALIGN_SIZE(size, alignment); - frame->data[0] = bmalloc(size); - frame->data[1] = (uint8_t*)frame->data[0] + offsets[0]; - frame->linesize[0] = width; - frame->linesize[1] = width; - break; - - case VIDEO_FORMAT_YVYU: - case VIDEO_FORMAT_YUY2: - case VIDEO_FORMAT_UYVY: - size = width * height * 2; - ALIGN_SIZE(size, alignment); - frame->data[0] = bmalloc(size); - frame->linesize[0] = width*2; - break; - - case VIDEO_FORMAT_RGBA: - case VIDEO_FORMAT_BGRA: - case VIDEO_FORMAT_BGRX: - size = width * height * 4; - ALIGN_SIZE(size, alignment); - frame->data[0] = bmalloc(size); - frame->linesize[0] = width*4; - break; + for (size_t i = 0; i < MAX_AV_PLANES; i++) { + frame->data[i] = vid_frame.data[i]; + frame->linesize[i] = vid_frame.linesize[i]; } } diff --git a/libobs/obs-video.c b/libobs/obs-video.c index bcfcf56a3..a934f6c82 100644 --- a/libobs/obs-video.c +++ b/libobs/obs-video.c @@ -238,7 +238,7 @@ static inline void render_video(struct obs_core_video *video, int cur_texture, /* TODO: replace with more optimal conversion */ static inline bool download_frame(struct obs_core_video *video, - int prev_texture, struct video_frame *frame) + int prev_texture, struct video_data *frame) { stagesurf_t surface = video->copy_surfaces[prev_texture]; @@ -290,7 +290,7 @@ static inline uint32_t make_aligned_linesize_offset(uint32_t offset, } static void fix_gpu_converted_alignment(struct obs_core_video *video, - struct video_frame *frame, int cur_texture) + struct video_data *frame, int cur_texture) { struct source_frame *new_frame = &video->convert_frames[cur_texture]; uint32_t src_linesize = frame->linesize[0]; @@ -317,7 +317,7 @@ static void fix_gpu_converted_alignment(struct obs_core_video *video, } static bool set_gpu_converted_data(struct obs_core_video *video, - struct video_frame *frame, int cur_texture) + struct video_data *frame, int cur_texture) { if (frame->linesize[0] == video->output_width*4) { for (size_t i = 0; i < 3; i++) { @@ -337,7 +337,7 @@ static bool set_gpu_converted_data(struct obs_core_video *video, } static bool convert_frame(struct obs_core_video *video, - struct video_frame *frame, + struct video_data *frame, const struct video_output_info *info, int cur_texture) { struct source_frame *new_frame = &video->convert_frames[cur_texture]; @@ -368,7 +368,7 @@ static bool convert_frame(struct obs_core_video *video, } static inline void output_video_data(struct obs_core_video *video, - struct video_frame *frame, int cur_texture) + struct video_data *frame, int cur_texture) { const struct video_output_info *info; info = video_output_getinfo(video->video); @@ -382,7 +382,7 @@ static inline void output_video_data(struct obs_core_video *video, return; } - video_output_frame(video->video, frame); + video_output_swap_frame(video->video, frame); } static inline void output_frame(uint64_t timestamp) @@ -390,10 +390,10 @@ static inline void output_frame(uint64_t timestamp) struct obs_core_video *video = &obs->video; int cur_texture = video->cur_texture; int prev_texture = cur_texture == 0 ? NUM_TEXTURES-1 : cur_texture-1; - struct video_frame frame; + struct video_data frame; bool frame_ready; - memset(&frame, 0, sizeof(struct video_frame)); + memset(&frame, 0, sizeof(struct video_data)); frame.timestamp = timestamp; gs_entercontext(obs_graphics()); diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-output.c b/plugins/obs-ffmpeg/obs-ffmpeg-output.c index 85e4594f9..3ab0e4121 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-output.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg-output.c @@ -399,7 +399,7 @@ static inline int64_t rescale_ts(int64_t val, AVCodecContext *context, #define YUV420_PLANES 3 -static inline void copy_data(AVPicture *pic, const struct video_frame *frame, +static inline void copy_data(AVPicture *pic, const struct video_data *frame, int height) { for (int plane = 0; plane < YUV420_PLANES; plane++) { @@ -420,7 +420,7 @@ static inline void copy_data(AVPicture *pic, const struct video_frame *frame, } } -static void receive_video(void *param, const struct video_frame *frame) +static void receive_video(void *param, const struct video_data *frame) { struct ffmpeg_output *output = param; struct ffmpeg_data *data = &output->ff_data; @@ -574,17 +574,17 @@ static bool ffmpeg_output_start(void *data) if (!ffmpeg_data_init(&output->ff_data, filename_test)) return false; - struct audio_convert_info aci; - aci.samples_per_sec = SPS_TODO; - aci.format = AUDIO_FORMAT_FLOAT_PLANAR; - aci.speakers = SPEAKERS_STEREO; + struct audio_convert_info aci = { + .samples_per_sec = SPS_TODO, + .format = AUDIO_FORMAT_FLOAT_PLANAR, + .speakers = SPEAKERS_STEREO + }; - struct video_convert_info vci; - vci.format = VIDEO_FORMAT_I420; - vci.width = 0; - vci.height = 0; + struct video_scale_info vsi = { + .format = VIDEO_FORMAT_I420 + }; - video_output_connect(video, &vci, receive_video, output); + video_output_connect(video, &vsi, receive_video, output); audio_output_connect(audio, &aci, receive_audio, output); output->active = true; diff --git a/vs/2013/libobs/libobs.vcxproj b/vs/2013/libobs/libobs.vcxproj index 1a487ead6..5a5abd087 100644 --- a/vs/2013/libobs/libobs.vcxproj +++ b/vs/2013/libobs/libobs.vcxproj @@ -42,7 +42,9 @@ + + @@ -92,7 +94,9 @@ + + @@ -200,7 +204,7 @@ Windows true - avutil.lib;swresample.lib;pthreads.lib;%(AdditionalDependencies) + avutil.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies) $(OutDir);%(AdditionalLibraryDirectories) @@ -221,7 +225,7 @@ Windows true - avutil.lib;swresample.lib;pthreads.lib;%(AdditionalDependencies) + avutil.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies) $(OutDir);%(AdditionalLibraryDirectories) @@ -246,7 +250,7 @@ true true true - avutil.lib;swresample.lib;pthreads.lib;%(AdditionalDependencies) + avutil.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies) $(OutDir);%(AdditionalLibraryDirectories) @@ -271,7 +275,7 @@ true true true - avutil.lib;swresample.lib;pthreads.lib;%(AdditionalDependencies) + avutil.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies) $(OutDir);%(AdditionalLibraryDirectories) diff --git a/vs/2013/libobs/libobs.vcxproj.filters b/vs/2013/libobs/libobs.vcxproj.filters index f7a8b9f66..ec188c236 100644 --- a/vs/2013/libobs/libobs.vcxproj.filters +++ b/vs/2013/libobs/libobs.vcxproj.filters @@ -207,6 +207,12 @@ libobs\Header Files + + media-io\Header Files + + + media-io\Header Files + @@ -347,5 +353,11 @@ libobs\Source Files + + media-io\Source Files + + + media-io\Source Files + \ No newline at end of file