Commit 3830eba3 authored by Niklas Haas's avatar Niklas Haas

(WIP) utils/libav: add helpers for mangling av pixfmt into libplacebo

These structs are designed in not-quite-compatible ways, and the mapping
is not at all intuitive or obvious.

TODO:
- more testing, make sure it actually does the right thing
- more helper functions (cf. colorspace conversions etc.)
- pin down minimum version requirement?
- figure out how to handle yuv420p10 etc. properly
parent f9e58750
Pipeline #10388 passed with stages
in 2 minutes and 31 seconds
......@@ -2,7 +2,7 @@ project('libplacebo', ['c', 'cpp'],
license: 'LGPL2.1+',
default_options: ['c_std=c99'],
meson_version: '>=0.47',
version: '1.23.0',
version: '1.24.0',
)
# Version number
......
......@@ -11,6 +11,9 @@ option('shaderc', type: 'feature', value: 'auto',
option('lcms', type: 'feature', value: 'auto',
description: 'LittleCMS 2 support')
option('libav', type: 'feature', value: 'auto',
description: 'libav* support')
# Miscellaneous
option('tests', type: 'boolean', value: false,
description: 'Enable building the test cases')
......
......@@ -55,6 +55,10 @@
#include "include/libplacebo/vulkan.h"
#endif
#if PL_HAVE_LIBAVUTIL
#include "include/libplacebo/utils/libav.h"
#endif
#pragma GCC visibility pop
// Align up to the nearest multiple of an arbitrary alignment, which may also
......
/*
* This file is part of libplacebo.
*
* libplacebo is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* libplacebo 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with libplacebo. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LIBPLACEBO_LIBAV_H_
#define LIBPLACEBO_LIBAV_H_
#include "config.h"
#if !PL_HAVE_LIBAVUTIL
#error Included <libplacebo/utils/libav.h> but libplacebo built without libavutil!
#endif
#include <libplacebo/utils/upload.h>
#include <libavutil/pixdesc.h>
// Map an AVPixelFormat to an array of pl_plane_data structs. The array must
// have at least `av_pix_fmt_count_planes(fmt)` elements, but never more than 4.
// This function leaves `width`, `height` and `row_stride`, as well as the
// data pointers, uninitialized.
//
// Returns the number of plane structs written to, or 0 on error.
//
// Note: For formats like P010, this returns `component_depth = 16`
// (corresponding to `depth = 10, shift = 6` in avutil's structs). In cases
// like these, the correct depth/shift must be provided by the user as part of
// `pl_color_repr.bits` when actually rendering from the plane.
int pl_plane_data_from_pixfmt(struct pl_context *ctx,
struct pl_plane_data *data,
enum AVPixelFormat pix_fmt);
#endif // LIBPLACEBO_LIBAV_H_
......@@ -135,6 +135,11 @@ components = [
'name': 'lcms',
'deps': dependency('lcms2', version: '>=2.6', required: get_option('lcms')),
'srcs': 'lcms.c',
}, {
'name': 'libavutil',
'deps': dependency('libavutil', required: get_option('libav')),
'srcs': 'utils/libav.c',
'test': 'libav.c',
}, {
'name': 'glslang',
'deps': glslang_combined,
......
#include "tests.h"
#include <libavutil/pixdesc.h>
int main()
{
struct pl_context *ctx = pl_test_context();
struct pl_plane_data data[4] = {0};
#define TEST(pixfmt, reference) \
do { \
int planes = pl_plane_data_from_pixfmt(ctx, data, pixfmt); \
REQUIRE(planes == sizeof(reference) / sizeof(*reference)); \
REQUIRE(memcmp(data, reference, sizeof(reference)) == 0); \
} while (0)
// Test typical planar and semiplanar formats
static const struct pl_plane_data yuvp8[3] = {
{
.type = PL_FMT_UNORM,
.component_size = {8},
.component_map = {0},
.pixel_stride = 1,
}, {
.type = PL_FMT_UNORM,
.component_size = {8},
.component_map = {1},
.pixel_stride = 1,
}, {
.type = PL_FMT_UNORM,
.component_size = {8},
.component_map = {2},
.pixel_stride = 1,
}
};
TEST(AV_PIX_FMT_YUV420P, yuvp8);
TEST(AV_PIX_FMT_YUV422P, yuvp8);
TEST(AV_PIX_FMT_YUV444P, yuvp8);
static const struct pl_plane_data yuvp16[3] = {
{
.type = PL_FMT_UNORM,
.component_size = {16},
.component_map = {0},
.pixel_stride = 2,
}, {
.type = PL_FMT_UNORM,
.component_size = {16},
.component_map = {1},
.pixel_stride = 2,
}, {
.type = PL_FMT_UNORM,
.component_size = {16},
.component_map = {2},
.pixel_stride = 2,
}
};
TEST(AV_PIX_FMT_YUV420P16, yuvp16);
static const struct pl_plane_data nv12[2] = {
{
.type = PL_FMT_UNORM,
.component_size = {8},
.component_map = {0},
.pixel_stride = 1,
}, {
.type = PL_FMT_UNORM,
.component_size = {8, 8},
.component_map = {1, 2},
.pixel_stride = 2,
}
};
TEST(AV_PIX_FMT_NV12, nv12);
static const struct pl_plane_data nv21[2] = {
{
.type = PL_FMT_UNORM,
.component_size = {8},
.component_map = {0},
.pixel_stride = 1,
}, {
.type = PL_FMT_UNORM,
.component_size = {8, 8},
.component_map = {2, 1},
.pixel_stride = 2,
}
};
TEST(AV_PIX_FMT_NV21, nv21);
static const struct pl_plane_data p016[2] = {
{
.type = PL_FMT_UNORM,
.component_size = {16},
.component_map = {0},
.pixel_stride = 2,
}, {
.type = PL_FMT_UNORM,
.component_size = {16, 16},
.component_map = {1, 2},
.pixel_stride = 4,
}
};
TEST(AV_PIX_FMT_P010, p016);
TEST(AV_PIX_FMT_P016, p016);
// Test typical packed formats
static const struct pl_plane_data rgb24[1] = {
{
.type = PL_FMT_UNORM,
.component_size = {8, 8, 8},
.component_map = {0, 1, 2},
.pixel_stride = 3,
}
};
TEST(AV_PIX_FMT_RGB24, rgb24);
static const struct pl_plane_data bgr24[1] = {
{
.type = PL_FMT_UNORM,
.component_size = {8, 8, 8},
.component_map = {2, 1, 0},
.pixel_stride = 3,
}
};
TEST(AV_PIX_FMT_BGR24, bgr24);
static const struct pl_plane_data rgbx[1] = {
{
.type = PL_FMT_UNORM,
.component_size = {8, 8, 8},
.component_map = {0, 1, 2},
.pixel_stride = 4,
}
};
TEST(AV_PIX_FMT_RGB0, rgbx);
static const struct pl_plane_data xrgb[1] = {
{
.type = PL_FMT_UNORM,
.component_size = {8, 8, 8},
.component_map = {0, 1, 2},
.component_pad = {8, 0, 0},
.pixel_stride = 4,
}
};
TEST(AV_PIX_FMT_0RGB, xrgb);
}
/*
* This file is part of libplacebo.
*
* libplacebo is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* libplacebo 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with libplacebo. If not, see <http://www.gnu.org/licenses/>.
*/
#include "common.h"
#include "context.h"
int pl_plane_data_from_pixfmt(struct pl_context *ctx,
struct pl_plane_data *out_data,
enum AVPixelFormat pix_fmt)
{
const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);
if (!desc) {
pl_err(ctx, "Unknown AVPixelFormat: %d", (int) pix_fmt);
return 0;
}
if (desc->flags & AV_PIX_FMT_FLAG_BE) {
pl_err(ctx, "Big endian formats are most likely not supported "
"in any reasonable manner, erroring as a safety precaution...");
return 0;
}
if (desc->flags & AV_PIX_FMT_FLAG_BITSTREAM) {
pl_err(ctx, "Bitstream formats are not supported! (Components must "
"be byte-aligned)");
return 0;
}
if (desc->flags & AV_PIX_FMT_FLAG_PAL) {
pl_err(ctx, "Palette formats are (currently) not supported.");
return 0;
}
if (desc->nb_components == 0) {
pl_err(ctx, "Pixel format descriptor contains no components, possibly "
"dealing with a fake/virtual/hwaccel format?");
return 0;
}
int planes = av_pix_fmt_count_planes(pix_fmt);
pl_assert(planes <= 4);
// Construct a mapping from planes to components in that plane
int map[4][4] = {0}, map_idx[4] = {0};
for (int c = 0; c < desc->nb_components; c++) {
int plane = desc->comp[c].plane;
map[plane][map_idx[plane]++] = c;
}
// Sort this map by `comp.offset`
for (int p = 0; p < planes; p++) {
for (int c = 0; c < map_idx[p]; c++) {
for (int o = c+1; o < map_idx[p]; o++) {
if (desc->comp[map[p][o]].offset < desc->comp[map[p][c]].offset) {
int tmp = map[p][c];
map[p][c] = map[p][o];
map[p][o] = tmp;
}
}
}
}
// Fill in the details for each plane by iterating through components
// in memory order and keeping track of the current padding
int plane_offset[4] = {0};
for (int p = 0; p < planes; p++) {
struct pl_plane_data *data = &out_data[p];
data->type = (desc->flags & AV_PIX_FMT_FLAG_FLOAT)
? PL_FMT_FLOAT
: PL_FMT_UNORM;
for (int c = 0; c < map_idx[p]; c++) {
const AVComponentDescriptor *comp = &desc->comp[map[p][c]];
int comp_offset = comp->offset * 8;
data->component_size[c] = comp->depth + comp->shift;
data->component_map[c] = map[p][c];
data->component_pad[c] = comp_offset - plane_offset[p];
pl_assert(data->component_pad[c] >= 0);
plane_offset[p] = comp_offset + data->component_size[c];
// Basic sanity checking
pl_assert(c == 0 || data->pixel_stride == comp->step);
data->pixel_stride = comp->step;
}
// Explicitly clear the remaining components to make these defined
for (int c = map_idx[p]; c < 4; c++) {
data->component_size[c] = 0;
data->component_map[c] = 0;
data->component_pad[c] = 0;
}
}
return planes;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment