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.
This commit is contained in:
parent
1044fa0e86
commit
f2d4de3c03
@ -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})
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
86
libobs/media-io/video-frame.c
Normal file
86
libobs/media-io/video-frame.c
Normal file
@ -0,0 +1,86 @@
|
||||
/******************************************************************************
|
||||
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 "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;
|
||||
}
|
||||
}
|
||||
|
53
libobs/media-io/video-frame.h
Normal file
53
libobs/media-io/video-frame.h
Normal file
@ -0,0 +1,53 @@
|
||||
/******************************************************************************
|
||||
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 "../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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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
|
||||
}
|
||||
|
146
libobs/media-io/video-scaler-ffmpeg.c
Normal file
146
libobs/media-io/video-scaler-ffmpeg.c
Normal file
@ -0,0 +1,146 @@
|
||||
/******************************************************************************
|
||||
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/bmem.h"
|
||||
#include "video-scaler.h"
|
||||
|
||||
#include <libswscale/swscale.h>
|
||||
|
||||
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;
|
||||
}
|
68
libobs/media-io/video-scaler.h
Normal file
68
libobs/media-io/video-scaler.h
Normal file
@ -0,0 +1,68 @@
|
||||
/******************************************************************************
|
||||
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/>.
|
||||
******************************************************************************/
|
||||
|
||||
#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
|
@ -18,6 +18,7 @@
|
||||
#include <inttypes.h>
|
||||
|
||||
#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];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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());
|
||||
|
@ -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;
|
||||
|
||||
|
@ -42,7 +42,9 @@
|
||||
<ClInclude Include="..\..\..\libobs\media-io\audio-io.h" />
|
||||
<ClInclude Include="..\..\..\libobs\media-io\audio-resampler.h" />
|
||||
<ClInclude Include="..\..\..\libobs\media-io\format-conversion.h" />
|
||||
<ClInclude Include="..\..\..\libobs\media-io\video-frame.h" />
|
||||
<ClInclude Include="..\..\..\libobs\media-io\video-io.h" />
|
||||
<ClInclude Include="..\..\..\libobs\media-io\video-scaler.h" />
|
||||
<ClInclude Include="..\..\..\libobs\obs-data.h" />
|
||||
<ClInclude Include="..\..\..\libobs\obs-defs.h" />
|
||||
<ClInclude Include="..\..\..\libobs\obs-encoder.h" />
|
||||
@ -92,7 +94,9 @@
|
||||
<ClCompile Include="..\..\..\libobs\media-io\audio-io.c" />
|
||||
<ClCompile Include="..\..\..\libobs\media-io\audio-resampler-ffmpeg.c" />
|
||||
<ClCompile Include="..\..\..\libobs\media-io\format-conversion.c" />
|
||||
<ClCompile Include="..\..\..\libobs\media-io\video-frame.c" />
|
||||
<ClCompile Include="..\..\..\libobs\media-io\video-io.c" />
|
||||
<ClCompile Include="..\..\..\libobs\media-io\video-scaler-ffmpeg.c" />
|
||||
<ClCompile Include="..\..\..\libobs\obs-data.c" />
|
||||
<ClCompile Include="..\..\..\libobs\obs-display.c" />
|
||||
<ClCompile Include="..\..\..\libobs\obs-encoder.c" />
|
||||
@ -200,7 +204,7 @@
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>avutil.lib;swresample.lib;pthreads.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>avutil.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
@ -221,7 +225,7 @@
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>avutil.lib;swresample.lib;pthreads.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>avutil.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
@ -246,7 +250,7 @@
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>avutil.lib;swresample.lib;pthreads.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>avutil.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
@ -271,7 +275,7 @@
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>avutil.lib;swresample.lib;pthreads.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>avutil.lib;swresample.lib;swscale.lib;pthreads.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
|
@ -207,6 +207,12 @@
|
||||
<ClInclude Include="..\..\..\libobs\obs-properties.h">
|
||||
<Filter>libobs\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\libobs\media-io\video-scaler.h">
|
||||
<Filter>media-io\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\libobs\media-io\video-frame.h">
|
||||
<Filter>media-io\Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\..\libobs\obs-output.c">
|
||||
@ -347,5 +353,11 @@
|
||||
<ClCompile Include="..\..\..\libobs\obs-view.c">
|
||||
<Filter>libobs\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\libobs\media-io\video-scaler-ffmpeg.c">
|
||||
<Filter>media-io\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\libobs\media-io\video-frame.c">
|
||||
<Filter>media-io\Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
Loading…
x
Reference in New Issue
Block a user