WIP: Video: 10/12 BPP and HDR video output support #120033
|
@ -460,6 +460,14 @@ class RENDER_PT_encoding_video(RenderOutputButtonsPanel, Panel):
|
|||
if ffmpeg.codec == 'DNXHD':
|
||||
layout.prop(ffmpeg, "use_lossless_output")
|
||||
|
||||
has_10_bit = ffmpeg.codec in {'H264', 'AV1'}
|
||||
has_12_bit = ffmpeg.codec in {'AV1'}
|
||||
has_hdr = ffmpeg.codec in {'H264', 'AV1'}
|
||||
if has_10_bit or has_12_bit:
|
||||
layout.prop(ffmpeg, "video_bpp")
|
||||
if has_10_bit:
|
||||
layout.prop(ffmpeg, "video_hdr")
|
||||
|
||||
# Output quality
|
||||
use_crf = needs_codec and ffmpeg.codec in {
|
||||
'H264',
|
||||
|
|
|
@ -907,4 +907,18 @@ void BKE_image_format_init_for_write(ImageFormatData *imf,
|
|||
STRNCPY(imf->linear_colorspace_settings.name,
|
||||
IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_SCENE_LINEAR));
|
||||
}
|
||||
|
||||
if (imf_src == nullptr && scene_src->r.im_format.imtype == R_IMF_IMTYPE_FFMPEG) {
|
||||
if (scene_src->r.ffcodecdata.video_bpp == FFM_VIDEO_BPP_10) {
|
||||
imf->depth = R_IMF_CHAN_DEPTH_10;
|
||||
}
|
||||
if (scene_src->r.ffcodecdata.video_bpp == FFM_VIDEO_BPP_12) {
|
||||
imf->depth = R_IMF_CHAN_DEPTH_12;
|
||||
}
|
||||
if (scene_src->r.ffcodecdata.video_hdr == FFM_VIDEO_HDR_REC2020_HLG) {
|
||||
/* @TODO: until we get proper OCIO config to do full HLG stuff, use this and to HLG math
|
||||
* manually. */
|
||||
STRNCPY(imf->linear_colorspace_settings.name, "Linear Rec.2020");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ extern "C" {
|
|||
# include <libavutil/channel_layout.h>
|
||||
# include <libavutil/cpu.h>
|
||||
# include <libavutil/imgutils.h>
|
||||
# include <libavutil/mastering_display_metadata.h>
|
||||
# include <libavutil/opt.h>
|
||||
# include <libavutil/rational.h>
|
||||
# include <libavutil/samplefmt.h>
|
||||
|
@ -398,9 +399,12 @@ static bool write_video_frame(FFMpegContext *context, AVFrame *frame, ReportList
|
|||
/* read and encode a frame of video from the buffer */
|
||||
static AVFrame *generate_video_frame(FFMpegContext *context, const ImBuf *image)
|
||||
{
|
||||
/* For now only 8-bit/channel images are supported. */
|
||||
const uint8_t *pixels = image->byte_buffer.data;
|
||||
if (pixels == nullptr) {
|
||||
const float *pixels_fl = image->float_buffer.data;
|
||||
/* Use float input if needed. */
|
||||
const bool use_float = context->img_convert_frame != nullptr &&
|
||||
context->img_convert_frame->format != AV_PIX_FMT_RGBA;
|
||||
if ((!use_float && (pixels == nullptr)) || (use_float && (pixels_fl == nullptr))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -417,31 +421,56 @@ static AVFrame *generate_video_frame(FFMpegContext *context, const ImBuf *image)
|
|||
rgb_frame = context->current_frame;
|
||||
}
|
||||
|
||||
/* Copy the Blender pixels into the FFMPEG data-structure, taking care of endianness and flipping
|
||||
* the image vertically. */
|
||||
int linesize = rgb_frame->linesize[0];
|
||||
int linesize_src = rgb_frame->width * 4;
|
||||
for (int y = 0; y < height; y++) {
|
||||
uint8_t *target = rgb_frame->data[0] + linesize * (height - y - 1);
|
||||
const uint8_t *src = pixels + linesize_src * y;
|
||||
const size_t linesize_dst = rgb_frame->linesize[0];
|
||||
if (use_float) {
|
||||
/* Float image: need to split up the image into a planar format,
|
||||
* because libswscale does not support RGBA->YUV conversions from
|
||||
* packed float formats. */
|
||||
BLI_assert_msg(rgb_frame->linesize[1] == linesize_dst && rgb_frame->linesize[2] == linesize_dst &&
|
||||
rgb_frame->linesize[3] == linesize_dst,
|
||||
"ffmpeg frame should be 4 same size planes for a floating point image case");
|
||||
for (int y = 0; y < height; y++) {
|
||||
size_t dst_offset = linesize_dst * (height - y - 1);
|
||||
float *dst_g = reinterpret_cast<float *>(rgb_frame->data[0] + dst_offset);
|
||||
float *dst_b = reinterpret_cast<float *>(rgb_frame->data[1] + dst_offset);
|
||||
float *dst_r = reinterpret_cast<float *>(rgb_frame->data[2] + dst_offset);
|
||||
float *dst_a = reinterpret_cast<float *>(rgb_frame->data[3] + dst_offset);
|
||||
const float *src = pixels_fl + image->x * y * 4;
|
||||
for (int x = 0; x < image->x; x++) {
|
||||
*dst_g++ = src[1];
|
||||
*dst_b++ = src[2];
|
||||
*dst_r++ = src[0];
|
||||
*dst_a++ = src[3];
|
||||
src += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Byte image: flip the image vertically, possibly with endian
|
||||
* conversion. */
|
||||
const size_t linesize_src = rgb_frame->width * 4;
|
||||
for (int y = 0; y < height; y++) {
|
||||
uint8_t *target = rgb_frame->data[0] + linesize_dst * (height - y - 1);
|
||||
const uint8_t *src = pixels + linesize_src * y;
|
||||
|
||||
# if ENDIAN_ORDER == L_ENDIAN
|
||||
memcpy(target, src, linesize_src);
|
||||
memcpy(target, src, linesize_src);
|
||||
|
||||
# elif ENDIAN_ORDER == B_ENDIAN
|
||||
const uint8_t *end = src + linesize_src;
|
||||
while (src != end) {
|
||||
target[3] = src[0];
|
||||
target[2] = src[1];
|
||||
target[1] = src[2];
|
||||
target[0] = src[3];
|
||||
const uint8_t *end = src + linesize_src;
|
||||
while (src != end) {
|
||||
target[3] = src[0];
|
||||
target[2] = src[1];
|
||||
target[1] = src[2];
|
||||
target[0] = src[3];
|
||||
|
||||
target += 4;
|
||||
src += 4;
|
||||
}
|
||||
target += 4;
|
||||
src += 4;
|
||||
}
|
||||
# else
|
||||
# error ENDIAN_ORDER should either be L_ENDIAN or B_ENDIAN.
|
||||
# endif
|
||||
}
|
||||
}
|
||||
|
||||
/* Convert to the output pixel format, if it's different that Blender's internal one. */
|
||||
|
@ -1004,6 +1033,16 @@ static AVStream *alloc_video_stream(FFMpegContext *context,
|
|||
c->pix_fmt = AV_PIX_FMT_YUV422P;
|
||||
}
|
||||
|
||||
const bool bpp10 = rd->ffcodecdata.video_bpp == FFM_VIDEO_BPP_10;
|
||||
const bool bpp12 = rd->ffcodecdata.video_bpp == FFM_VIDEO_BPP_12;
|
||||
const bool hdrHLG = rd->ffcodecdata.video_hdr == FFM_VIDEO_HDR_REC2020_HLG;
|
||||
if (bpp10) {
|
||||
c->pix_fmt = AV_PIX_FMT_YUV420P10LE;
|
||||
}
|
||||
if (bpp12) {
|
||||
c->pix_fmt = AV_PIX_FMT_YUV420P12LE;
|
||||
}
|
||||
|
||||
if (context->ffmpeg_type == FFMPEG_XVID) {
|
||||
/* Alas! */
|
||||
c->pix_fmt = AV_PIX_FMT_YUV420P;
|
||||
|
@ -1043,6 +1082,12 @@ static AVStream *alloc_video_stream(FFMpegContext *context,
|
|||
else if (ELEM(codec_id, AV_CODEC_ID_H264, AV_CODEC_ID_VP9) && (context->ffmpeg_crf == 0)) {
|
||||
/* Use 4:4:4 instead of 4:2:0 pixel format for lossless rendering. */
|
||||
c->pix_fmt = AV_PIX_FMT_YUV444P;
|
||||
if (bpp10) {
|
||||
c->pix_fmt = AV_PIX_FMT_YUV444P10LE;
|
||||
}
|
||||
if (bpp12) {
|
||||
c->pix_fmt = AV_PIX_FMT_YUV444P12LE;
|
||||
}
|
||||
}
|
||||
|
||||
if (codec_id == AV_CODEC_ID_PNG) {
|
||||
|
@ -1056,6 +1101,13 @@ static AVStream *alloc_video_stream(FFMpegContext *context,
|
|||
c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
|
||||
}
|
||||
|
||||
if (hdrHLG) {
|
||||
c->color_range = AVCOL_RANGE_JPEG; //@TODO: this or MPEG? or configurable?
|
||||
c->color_primaries = AVCOL_PRI_BT2020;
|
||||
c->color_trc = AVCOL_TRC_ARIB_STD_B67;
|
||||
c->colorspace = AVCOL_SPC_BT2020_NCL;
|
||||
}
|
||||
|
||||
/* xasp & yasp got float lately... */
|
||||
|
||||
st->sample_aspect_ratio = c->sample_aspect_ratio = av_d2q((double(rd->xasp) / double(rd->yasp)),
|
||||
|
@ -1100,13 +1152,61 @@ static AVStream *alloc_video_stream(FFMpegContext *context,
|
|||
}
|
||||
else {
|
||||
/* Output pixel format is different, allocate frame for conversion. */
|
||||
context->img_convert_frame = alloc_picture(AV_PIX_FMT_RGBA, c->width, c->height);
|
||||
AVPixelFormat src_format = bpp10 || bpp12 ? AV_PIX_FMT_GBRAPF32LE : AV_PIX_FMT_RGBA;
|
||||
context->img_convert_frame = alloc_picture(src_format, c->width, c->height);
|
||||
context->img_convert_ctx = BKE_ffmpeg_sws_get_context(
|
||||
c->width, c->height, AV_PIX_FMT_RGBA, c->pix_fmt, SWS_BICUBIC);
|
||||
c->width, c->height, src_format, c->pix_fmt, SWS_BICUBIC);
|
||||
}
|
||||
|
||||
avcodec_parameters_from_context(st->codecpar, c);
|
||||
|
||||
/* Add side data indicating light levels and things. @TODO: not quite sure if this is really
|
||||
* needed. */
|
||||
if (hdrHLG) {
|
||||
constexpr int hdr_peak_level = 1000;
|
||||
|
||||
size_t light_meta_size;
|
||||
AVContentLightMetadata *light_meta = av_content_light_metadata_alloc(&light_meta_size);
|
||||
light_meta->MaxCLL = hdr_peak_level;
|
||||
light_meta->MaxFALL = hdr_peak_level;
|
||||
# if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(60, 31, 102)
|
||||
av_stream_add_side_data(
|
||||
st, AV_PKT_DATA_CONTENT_LIGHT_LEVEL, (uint8_t *)light_meta, light_meta_size);
|
||||
# else
|
||||
av_packet_side_data_add(&st->codecpar->coded_side_data,
|
||||
&st->codecpar->nb_coded_side_data,
|
||||
AV_PKT_DATA_CONTENT_LIGHT_LEVEL,
|
||||
light_meta,
|
||||
light_meta_size,
|
||||
0);
|
||||
# endif
|
||||
|
||||
AVMasteringDisplayMetadata *mastering = av_mastering_display_metadata_alloc();
|
||||
mastering->display_primaries[0][0] = av_make_q(17, 25);
|
||||
mastering->display_primaries[0][1] = av_make_q(8, 25);
|
||||
mastering->display_primaries[1][0] = av_make_q(53, 200);
|
||||
mastering->display_primaries[1][1] = av_make_q(69, 100);
|
||||
mastering->display_primaries[2][0] = av_make_q(3, 20);
|
||||
mastering->display_primaries[2][1] = av_make_q(3, 50);
|
||||
mastering->white_point[0] = av_make_q(3127, 10000);
|
||||
mastering->white_point[1] = av_make_q(329, 1000);
|
||||
mastering->min_luminance = av_make_q(0, 1);
|
||||
mastering->max_luminance = av_make_q(hdr_peak_level, 1);
|
||||
mastering->has_primaries = 1;
|
||||
mastering->has_luminance = 1;
|
||||
# if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(60, 31, 102)
|
||||
av_stream_add_side_data(
|
||||
st, AV_PKT_DATA_MASTERING_DISPLAY_METADATA, (uint8_t *)mastering, sizeof(*mastering));
|
||||
# else
|
||||
av_packet_side_data_add(&st->codecpar->coded_side_data,
|
||||
&st->codecpar->nb_coded_side_data,
|
||||
AV_PKT_DATA_MASTERING_DISPLAY_METADATA,
|
||||
mastering,
|
||||
sizeof(*mastering),
|
||||
0);
|
||||
# endif
|
||||
}
|
||||
|
||||
context->video_time = 0.0f;
|
||||
|
||||
return st;
|
||||
|
@ -1839,6 +1939,7 @@ void BKE_ffmpeg_end(void *context_v)
|
|||
void BKE_ffmpeg_preset_set(RenderData *rd, int preset)
|
||||
{
|
||||
bool is_ntsc = (rd->frs_sec != 25);
|
||||
rd->ffcodecdata.video_bpp = FFM_VIDEO_BPP_8;
|
||||
|
||||
switch (preset) {
|
||||
case FFMPEG_PRESET_H264:
|
||||
|
|
|
@ -248,7 +248,8 @@ void IMB_colormanagement_imbuf_make_display_space(
|
|||
ImBuf *IMB_colormanagement_imbuf_for_write(ImBuf *ibuf,
|
||||
bool save_as_render,
|
||||
bool allocate_result,
|
||||
const ImageFormatData *image_format);
|
||||
const ImageFormatData *image_format,
|
||||
bool force_linear = false);
|
||||
|
||||
/** \} */
|
||||
|
||||
|
|
|
@ -2581,7 +2581,8 @@ static ImBuf *imbuf_ensure_editable(ImBuf *ibuf, ImBuf *colormanaged_ibuf, bool
|
|||
ImBuf *IMB_colormanagement_imbuf_for_write(ImBuf *ibuf,
|
||||
bool save_as_render,
|
||||
bool allocate_result,
|
||||
const ImageFormatData *image_format)
|
||||
const ImageFormatData *image_format,
|
||||
bool force_linear)
|
||||
{
|
||||
ImBuf *colormanaged_ibuf = ibuf;
|
||||
|
||||
|
@ -2594,7 +2595,8 @@ ImBuf *IMB_colormanagement_imbuf_for_write(ImBuf *ibuf,
|
|||
}
|
||||
|
||||
/* Detect if we are writing to a file format that needs a linear float buffer. */
|
||||
const bool linear_float_output = BKE_imtype_requires_linear_float(image_format->imtype);
|
||||
bool linear_float_output = force_linear ||
|
||||
BKE_imtype_requires_linear_float(image_format->imtype);
|
||||
|
||||
/* Detect if we are writing output a byte buffer, which we would need to create
|
||||
* with color management conversions applied. This may be for either applying the
|
||||
|
|
|
@ -112,6 +112,17 @@ typedef enum eFFMpegAudioChannels {
|
|||
FFM_CHANNELS_SURROUND71 = 8,
|
||||
} eFFMpegAudioChannels;
|
||||
|
||||
typedef enum eFFMpegVideoBpp {
|
||||
FFM_VIDEO_BPP_8 = 0,
|
||||
FFM_VIDEO_BPP_10 = 1,
|
||||
FFM_VIDEO_BPP_12 = 2,
|
||||
} eFFMpegVideoBpp;
|
||||
|
||||
typedef enum eFFMpegVideoHdr {
|
||||
FFM_VIDEO_HDR_NONE = 0,
|
||||
FFM_VIDEO_HDR_REC2020_HLG = 1,
|
||||
} eFFMpegVideoHdr;
|
||||
|
||||
typedef struct FFMpegCodecData {
|
||||
int type;
|
||||
int codec;
|
||||
|
@ -134,7 +145,8 @@ typedef struct FFMpegCodecData {
|
|||
int rc_buffer_size;
|
||||
int mux_packet_size;
|
||||
int mux_rate;
|
||||
void *_pad1;
|
||||
int video_bpp; /* eFFMpegVideoBpp */
|
||||
int video_hdr; /* eFFMpegVideoHdr */
|
||||
} FFMpegCodecData;
|
||||
|
||||
/** \} */
|
||||
|
@ -654,8 +666,6 @@ typedef enum eBakePassFilter {
|
|||
|
||||
typedef struct RenderData {
|
||||
struct ImageFormatData im_format;
|
||||
|
||||
void *_pad;
|
||||
struct FFMpegCodecData ffcodecdata;
|
||||
|
||||
/** Frames as in 'images'. */
|
||||
|
|
|
@ -6384,6 +6384,22 @@ static void rna_def_scene_ffmpeg_settings(BlenderRNA *brna)
|
|||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
|
||||
static const EnumPropertyItem ffmpeg_bpp_items[] = {
|
||||
{FFM_VIDEO_BPP_8, "8", 0, "8", "8-bit color channels"},
|
||||
{FFM_VIDEO_BPP_10, "10", 0, "10", "10-bit color channels"},
|
||||
{FFM_VIDEO_BPP_12, "12", 0, "12", "12-bit color channels"},
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
static const EnumPropertyItem ffmpeg_hdr_items[] = {
|
||||
{FFM_VIDEO_HDR_NONE, "NONE", 0, "SDR", "Standard Dynamic Range (no HDR)"},
|
||||
{FFM_VIDEO_HDR_REC2020_HLG,
|
||||
"HLG",
|
||||
0,
|
||||
"Rec.2020 HLG",
|
||||
"Rec.2020 color space with Hybrid-Log Gamma HDR encoding"},
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
|
||||
static const EnumPropertyItem ffmpeg_audio_codec_items[] = {
|
||||
{AV_CODEC_ID_NONE, "NONE", 0, "No Audio", "Disables audio output, for video-only renders"},
|
||||
{AV_CODEC_ID_AAC, "AAC", 0, "AAC", ""},
|
||||
|
@ -6442,6 +6458,22 @@ static void rna_def_scene_ffmpeg_settings(BlenderRNA *brna)
|
|||
RNA_def_property_ui_text(prop, "Bitrate", "Video bitrate (kbit/s)");
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, nullptr);
|
||||
|
||||
prop = RNA_def_property(srna, "video_bpp", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_sdna(prop, nullptr, "video_bpp");
|
||||
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
|
||||
RNA_def_property_enum_items(prop, ffmpeg_bpp_items);
|
||||
RNA_def_property_enum_default(prop, FFM_VIDEO_BPP_8);
|
||||
RNA_def_property_ui_text(prop, "Bits/Pixel", "Video bits per pixel color channel");
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, nullptr);
|
||||
|
||||
prop = RNA_def_property(srna, "video_hdr", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_sdna(prop, nullptr, "video_hdr");
|
||||
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
|
||||
RNA_def_property_enum_items(prop, ffmpeg_hdr_items);
|
||||
RNA_def_property_enum_default(prop, FFM_VIDEO_HDR_NONE);
|
||||
RNA_def_property_ui_text(prop, "HDR", "High Dynamic Range options");
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, nullptr);
|
||||
|
||||
prop = RNA_def_property(srna, "minrate", PROP_INT, PROP_NONE);
|
||||
RNA_def_property_int_sdna(prop, nullptr, "rc_min_rate");
|
||||
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
|
||||
|
|
|
@ -2077,6 +2077,20 @@ void RE_RenderFreestyleExternal(Render *re)
|
|||
/** \name Read/Write Render Result (Images & Movies)
|
||||
* \{ */
|
||||
|
||||
/* @TODO: this should be done by OCIO, but until we get the correct configuration, do Linear
|
||||
* Rec.2020 -> HLG transform manually. */
|
||||
static float do_hlg(float v)
|
||||
{
|
||||
if (v <= 0.0f)
|
||||
return 0.0f;
|
||||
if (v <= 1.0f)
|
||||
return 0.5f * sqrtf(v);
|
||||
const float ca = 0.17883277f;
|
||||
const float cb = 0.28466892f;
|
||||
const float cc = 0.55991073f;
|
||||
return ca * logf(v - cb) + cc;
|
||||
}
|
||||
|
||||
bool RE_WriteRenderViewsMovie(ReportList *reports,
|
||||
RenderResult *rr,
|
||||
Scene *scene,
|
||||
|
@ -2097,6 +2111,7 @@ bool RE_WriteRenderViewsMovie(ReportList *reports,
|
|||
|
||||
const bool is_mono = BLI_listbase_count_at_most(&rr->views, 2) < 2;
|
||||
const float dither = scene->r.dither_intensity;
|
||||
const bool is_hdr = scene->r.ffcodecdata.video_hdr == FFM_VIDEO_HDR_REC2020_HLG;
|
||||
|
||||
if (is_mono || (image_format.views_format == R_IMF_VIEWS_INDIVIDUAL)) {
|
||||
int view_id;
|
||||
|
@ -2104,7 +2119,17 @@ bool RE_WriteRenderViewsMovie(ReportList *reports,
|
|||
const char *suffix = BKE_scene_multiview_view_id_suffix_get(&scene->r, view_id);
|
||||
ImBuf *ibuf = RE_render_result_rect_to_ibuf(rr, &rd->im_format, dither, view_id);
|
||||
|
||||
IMB_colormanagement_imbuf_for_write(ibuf, true, false, &image_format);
|
||||
IMB_colormanagement_imbuf_for_write(ibuf, true, false, &image_format, is_hdr);
|
||||
|
||||
/* @TODO: this should be done by OCIO, but until we get the correct configuration, do Linear
|
||||
* Rec.2020 -> HLG transform manually. */
|
||||
if (ibuf->float_buffer.data && is_hdr) {
|
||||
for (int idx = 0; idx < ibuf->x * ibuf->y * 4; idx += 4) {
|
||||
ibuf->float_buffer.data[idx + 0] = do_hlg(ibuf->float_buffer.data[idx + 0]);
|
||||
ibuf->float_buffer.data[idx + 1] = do_hlg(ibuf->float_buffer.data[idx + 1]);
|
||||
ibuf->float_buffer.data[idx + 2] = do_hlg(ibuf->float_buffer.data[idx + 2]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!mh->append_movie(movie_ctx_arr[view_id],
|
||||
rd,
|
||||
|
@ -2133,7 +2158,7 @@ bool RE_WriteRenderViewsMovie(ReportList *reports,
|
|||
int view_id = BLI_findstringindex(&rr->views, names[i], offsetof(RenderView, name));
|
||||
ibuf_arr[i] = RE_render_result_rect_to_ibuf(rr, &rd->im_format, dither, view_id);
|
||||
|
||||
IMB_colormanagement_imbuf_for_write(ibuf_arr[i], true, false, &image_format);
|
||||
IMB_colormanagement_imbuf_for_write(ibuf_arr[i], true, false, &image_format, is_hdr);
|
||||
}
|
||||
|
||||
ibuf_arr[2] = IMB_stereo3d_ImBuf(&image_format, ibuf_arr[0], ibuf_arr[1]);
|
||||
|
|
Loading…
Reference in New Issue