Browse Source
The module API allows all "video converter" modules to list their chroma conversions. The user API allows to get a full list of all possibles chromas conversion from an input chroma.pull/177/head
8 changed files with 1123 additions and 0 deletions
@ -0,0 +1,259 @@ |
|||
/*****************************************************************************
|
|||
* vlc_chroma_probe.h: chroma conversion probing |
|||
***************************************************************************** |
|||
* Copyright (C) 2025 VLC authors and VideoLAN |
|||
* |
|||
* This program 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. |
|||
* |
|||
* 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 Lesser General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU Lesser General Public License |
|||
* along with this program; if not, write to the Free Software Foundation, |
|||
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. |
|||
*****************************************************************************/ |
|||
|
|||
#ifndef VLC_CHROMA_PROBE_H |
|||
#define VLC_CHROMA_PROBE_H 1 |
|||
|
|||
#include <vlc_common.h> |
|||
#include <vlc_vector.h> |
|||
|
|||
/**
|
|||
* \defgroup chroma_probe Chroma conversion probing |
|||
* \ingroup filter |
|||
* @{ |
|||
* \file |
|||
* Chroma conversion probing |
|||
* |
|||
* \defgroup chroma_probe_api Chroma probing API |
|||
* \ingroup chroma_probe |
|||
* |
|||
* @{ |
|||
*/ |
|||
|
|||
#define VLC_CHROMA_CONV_MAX_INDIRECT_STEPS 1 |
|||
#define VLC_CHROMA_CONV_CHAIN_COUNT_MAX (2 /* in + out */ + VLC_CHROMA_CONV_MAX_INDIRECT_STEPS) |
|||
|
|||
/**
|
|||
* Chroma conversion result structure |
|||
*/ |
|||
struct vlc_chroma_conv_result |
|||
{ |
|||
/**
|
|||
* Array of chromas used to achieve the conversion |
|||
* |
|||
* 'chain[0]' is always equals to the 'in' argument of the |
|||
* vlc_chroma_conv_Probe() function. |
|||
* |
|||
* if the out argument of the vlc_chroma_conv_Probe() is valid, |
|||
* chain[chain_count - 1] is equals to 'out' |
|||
*/ |
|||
vlc_fourcc_t chain[VLC_CHROMA_CONV_CHAIN_COUNT_MAX]; |
|||
|
|||
/** Number of chromas in the chain */ |
|||
size_t chain_count; |
|||
|
|||
/**
|
|||
* Cost of the full conversion, lower is better. |
|||
*/ |
|||
unsigned cost; |
|||
|
|||
/**
|
|||
* Quality of the conversion, higher is better. |
|||
* |
|||
* A quality of 100 means there are no quality loss: same color size and |
|||
* same vlc_chroma_subtype (or same YUV subsampling for video). |
|||
*/ |
|||
unsigned quality; |
|||
}; |
|||
|
|||
/** Only accept YUV output chromas (the input chroma can be RGB) */ |
|||
#define VLC_CHROMA_CONV_FLAG_ONLY_YUV 0x1 |
|||
/** Only accept RGB output chromas (the input chroma can be YUV) */ |
|||
#define VLC_CHROMA_CONV_FLAG_ONLY_RGB 0x2 |
|||
/** Sort results by cost instead of quality */ |
|||
#define VLC_CHROMA_CONV_FLAG_SORT_COST 0x4 |
|||
|
|||
/**
|
|||
* Probe possible chroma conversions |
|||
|
|||
* Results are sorted by quality, unless VLC_CHROMA_CONV_FLAG_SORT_COST is |
|||
* specified in flags. |
|||
|
|||
* @param in the input chroma to convert from, must be valid |
|||
* @param out the output chroma to convert to, if 0, the function will find all |
|||
* possible conversion from in to x |
|||
* @param width video width, used for finer cost calculation, can be 0 |
|||
* @param height video height, used for finer cost calculation, can be 0 |
|||
* @param max_indirect_steps maximum number of indirect conversion steps, must |
|||
* be lower or equal to @ref VLC_CHROMA_CONV_MAX_INDIRECT_STEPS, if in and out |
|||
* chromas are CPU chromas, the steps will be automatically lowered to 0 |
|||
* @param flags bitwise flags, cf. VLC_CHROMA_CONV_FLAG_* |
|||
* @param count pointer to the number of results, must be valid |
|||
* @return a pointer to an array of results, must be released with free(), can |
|||
* be NULL |
|||
*/ |
|||
VLC_API struct vlc_chroma_conv_result * |
|||
vlc_chroma_conv_Probe(vlc_fourcc_t in, vlc_fourcc_t out, |
|||
unsigned width, unsigned height, |
|||
unsigned max_indirect_steps, int flags, size_t *count); |
|||
|
|||
/**
|
|||
* Get a string representing the result |
|||
* |
|||
* @param res pointer to a valid result |
|||
* @return a string or NULL, must be released with free() |
|||
*/ |
|||
VLC_API char * |
|||
vlc_chroma_conv_result_ToString(const struct vlc_chroma_conv_result *res); |
|||
|
|||
/**
|
|||
* @} |
|||
* |
|||
* \defgroup chroma_probe_module Chroma probing module implementation |
|||
* \ingroup chroma_probe |
|||
* |
|||
* @{ |
|||
*/ |
|||
|
|||
/**
|
|||
* Chroma conversion entry structure |
|||
*/ |
|||
struct vlc_chroma_conv_entry |
|||
{ |
|||
/** Cost factor, 0.25 for GPU<->GPU conversions, 0.75 for SIMD, 1 for CPU */ |
|||
float cost_factor; |
|||
/** input chroma */ |
|||
vlc_fourcc_t in; |
|||
/** output chroma */ |
|||
vlc_fourcc_t out; |
|||
}; |
|||
typedef struct VLC_VECTOR(struct vlc_chroma_conv_entry) vlc_chroma_conv_vec; |
|||
|
|||
/**
|
|||
* Module probe function signature |
|||
* |
|||
* @param vec pointer to an allocated vector |
|||
* @return a VLC error code |
|||
*/ |
|||
typedef void (*vlc_chroma_conv_probe)(vlc_chroma_conv_vec *vec); |
|||
|
|||
#define set_callback_chroma_conv_probe(activate) \ |
|||
{ \ |
|||
vlc_chroma_conv_probe activate__ = activate; \ |
|||
(void) activate__; \ |
|||
set_callback(activate) \ |
|||
} \ |
|||
set_capability("chroma probe", 100) |
|||
|
|||
/**
|
|||
* Helper that add a chroma conversion |
|||
* |
|||
* Must be called inside vlc_chroma_conv_probe() |
|||
* |
|||
* @param vec pointer to the vector of chromas |
|||
* @param cost_factor cf. vlc_chroma_conv_entry.cost_factor |
|||
* @param in cf. vlc_chroma_conv_entry.in |
|||
* @param out cf. vlc_chroma_conv_entry.out |
|||
* @param twoway if true, 'out' can also be converted to 'in' |
|||
*/ |
|||
static inline void |
|||
vlc_chroma_conv_add(vlc_chroma_conv_vec *vec, float cost_factor, |
|||
vlc_fourcc_t in, vlc_fourcc_t out, bool twoway) |
|||
{ |
|||
{ |
|||
const struct vlc_chroma_conv_entry entry = { |
|||
cost_factor, in, out |
|||
}; |
|||
vlc_vector_push(vec, entry); |
|||
} |
|||
|
|||
if (twoway) |
|||
{ |
|||
const struct vlc_chroma_conv_entry entry = { |
|||
cost_factor, out, in |
|||
}; |
|||
vlc_vector_push(vec, entry); |
|||
} |
|||
} |
|||
|
|||
/**
|
|||
* Helper that add an array of out chroma conversions |
|||
* |
|||
* Must be called inside vlc_chroma_conv_probe() |
|||
* |
|||
* @param vec pointer to the vector of chromas |
|||
* @param cost_factor cf. vlc_chroma_conv_entry.cost_factor |
|||
* @param in cf. vlc_chroma_conv_entry.in |
|||
* @param out_array a list of out chromas |
|||
* @param out_count number of elements in the out_array |
|||
*/ |
|||
static inline void |
|||
vlc_chroma_conv_add_in_outarray(vlc_chroma_conv_vec *vec, float cost_factor, |
|||
vlc_fourcc_t in, |
|||
const vlc_fourcc_t *out_array, size_t out_count) |
|||
{ |
|||
for (size_t i = 0; i < out_count; i++) |
|||
{ |
|||
const struct vlc_chroma_conv_entry entry = { |
|||
cost_factor, in, out_array[i], |
|||
}; |
|||
vlc_vector_push(vec, entry); |
|||
} |
|||
} |
|||
|
|||
/**
|
|||
* Helper that add a list of out chroma conversions |
|||
*/ |
|||
#define vlc_chroma_conv_add_in_outlist(vec, cost_factor, in, ...) do { \ |
|||
static const vlc_fourcc_t out_array[] = { __VA_ARGS__ }; \ |
|||
size_t count = ARRAY_SIZE(out_array); \ |
|||
vlc_chroma_conv_add_in_outarray(vec, cost_factor, in, out_array, count); \ |
|||
} while(0) |
|||
|
|||
/**
|
|||
* Helper that add an array of in chroma conversions |
|||
* |
|||
* Must be called inside vlc_chroma_conv_probe() |
|||
* |
|||
* @param vec pointer to the vector of chromas |
|||
* @param cost_factor cf. vlc_chroma_conv_entry.cost_factor |
|||
* @param out cf. vlc_chroma_conv_entry.out |
|||
* @param in_array a list of out chromas |
|||
* @param in_count number of elements in the in_array |
|||
*/ |
|||
static inline void |
|||
vlc_chroma_conv_add_out_inarray(vlc_chroma_conv_vec *vec, float cost_factor, |
|||
vlc_fourcc_t out, |
|||
const vlc_fourcc_t *in_array, size_t in_count) |
|||
{ |
|||
for (size_t i = 0; i < in_count; i++) |
|||
{ |
|||
const struct vlc_chroma_conv_entry entry = { |
|||
cost_factor, in_array[i], out, |
|||
}; |
|||
vlc_vector_push(vec, entry); |
|||
} |
|||
} |
|||
|
|||
/**
|
|||
* Helper that add a list of in chroma conversions |
|||
*/ |
|||
#define vlc_chroma_conv_add_out_inlist(vec, cost_factor, out, ...) do { \ |
|||
static const vlc_fourcc_t in_array[] = { __VA_ARGS__ }; \ |
|||
size_t count = ARRAY_SIZE(in_array); \ |
|||
vlc_chroma_conv_add_out_inarray(vec, cost_factor, out, in_array, count); \ |
|||
} while(0) |
|||
|
|||
/**
|
|||
* @} |
|||
* @} |
|||
*/ |
|||
|
|||
#endif /* VLC_CHROMA_PROBE_H */ |
|||
@ -0,0 +1,475 @@ |
|||
/*****************************************************************************
|
|||
* chroma_probe.c: chroma conversion probing |
|||
***************************************************************************** |
|||
* Copyright (C) 2025 VLC authors and VideoLAN |
|||
* |
|||
* This program 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. |
|||
* |
|||
* 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 Lesser General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU Lesser General Public License |
|||
* along with this program; if not, write to the Free Software Foundation, |
|||
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. |
|||
*****************************************************************************/ |
|||
|
|||
#ifdef HAVE_CONFIG_H |
|||
# include "config.h" |
|||
#endif |
|||
|
|||
#include <vlc_chroma_probe.h> |
|||
#include <vlc_fourcc.h> |
|||
#include <vlc_threads.h> |
|||
#include <vlc_modules.h> |
|||
#include <vlc_sort.h> |
|||
#include <vlc_memstream.h> |
|||
|
|||
static int |
|||
modules_Probe(vlc_chroma_conv_vec *chroma_table) |
|||
{ |
|||
module_t **mods; |
|||
ssize_t total = vlc_module_match("chroma probe", NULL, false, &mods, NULL); |
|||
if (total == -1) |
|||
return -ENOENT; |
|||
|
|||
for (ssize_t i = 0; i < total; ++i) |
|||
{ |
|||
vlc_chroma_conv_probe fn = vlc_module_map(NULL, mods[i]); |
|||
if (fn == NULL) |
|||
continue; |
|||
fn(chroma_table); |
|||
} |
|||
free(mods); |
|||
return 0; |
|||
} |
|||
|
|||
/* Breadth First Search (BFS) node */ |
|||
struct bfs_node |
|||
{ |
|||
vlc_fourcc_t chain[VLC_CHROMA_CONV_CHAIN_COUNT_MAX]; |
|||
unsigned depth; /* Max deep is VLC_CHROMA_CONV_CHAIN_COUNT_MAX -1 */ |
|||
float cost_factor; |
|||
}; |
|||
typedef struct VLC_VECTOR(struct bfs_node) bfs_queue_vec; |
|||
|
|||
static int |
|||
bfs_Run(vlc_fourcc_t chroma_from, vlc_fourcc_t chroma_to, unsigned max_depth, |
|||
const vlc_chroma_conv_vec *chroma_table, int flags, |
|||
bfs_queue_vec *queue) |
|||
{ |
|||
struct bfs_node start = { |
|||
.chain[0] = chroma_from, |
|||
.cost_factor = 1, |
|||
.depth = 0, |
|||
}; |
|||
bool success = vlc_vector_push(queue, start); |
|||
if (!success) |
|||
return -ENOMEM; |
|||
|
|||
for (size_t queue_idx = 0; queue_idx < queue->size; queue_idx++) |
|||
{ |
|||
const struct bfs_node current = queue->data[queue_idx]; |
|||
vlc_fourcc_t current_chroma = current.chain[current.depth]; |
|||
|
|||
if (chroma_to != 0 && current_chroma == chroma_to) |
|||
continue; /* Found a path to 'chroma_to' */ |
|||
|
|||
if (current.depth == max_depth) |
|||
continue; |
|||
|
|||
/* Enqueue neighbors */ |
|||
for (size_t chroma_idx = 0; chroma_idx < chroma_table->size; chroma_idx++) |
|||
{ |
|||
struct vlc_chroma_conv_entry *entry = &chroma_table->data[chroma_idx]; |
|||
vlc_fourcc_t from = entry->in; |
|||
vlc_fourcc_t to = entry->out; |
|||
float cost_factor = entry->cost_factor; |
|||
|
|||
if (from == current_chroma) |
|||
{ |
|||
vlc_fourcc_t next_chroma = to; |
|||
|
|||
/* Apply filters from flags */ |
|||
if (flags & VLC_CHROMA_CONV_FLAG_ONLY_YUV) |
|||
{ |
|||
if (!vlc_fourcc_IsYUV(next_chroma)) |
|||
continue; |
|||
} |
|||
else if (flags & VLC_CHROMA_CONV_FLAG_ONLY_RGB) |
|||
{ |
|||
const vlc_chroma_description_t *desc = |
|||
vlc_fourcc_GetChromaDescription(next_chroma); |
|||
if (desc == NULL || desc->subtype != VLC_CHROMA_SUBTYPE_RGB) |
|||
continue; |
|||
} |
|||
|
|||
/* If next_chroma is already in the chain at any previous step,
|
|||
* we've encountered a cycle or a duplicate. */ |
|||
bool already_visited = false; |
|||
for (size_t i = 0; i < current.depth; ++i) |
|||
if (current.chain[i] == next_chroma) |
|||
{ |
|||
already_visited = true; |
|||
break; |
|||
} |
|||
if (already_visited) |
|||
continue; |
|||
|
|||
struct bfs_node next = current; |
|||
next.depth = current.depth + 1; |
|||
next.cost_factor = current.cost_factor * cost_factor; |
|||
|
|||
next.chain[next.depth] = next_chroma; |
|||
success = vlc_vector_push(queue, next); |
|||
if (!success) |
|||
return -ENOMEM; |
|||
} |
|||
} |
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
static uint64_t |
|||
GetChromaBits(const vlc_chroma_description_t *desc, |
|||
unsigned width, unsigned height) |
|||
{ |
|||
if (desc->plane_count == 0) |
|||
{ |
|||
/* Fallback to the size of the subtype */ |
|||
switch (desc->subtype) |
|||
{ |
|||
case VLC_CHROMA_SUBTYPE_OTHER: |
|||
return 0; |
|||
case VLC_CHROMA_SUBTYPE_YUV444: |
|||
return width * height * 3 * desc->color_bits; |
|||
case VLC_CHROMA_SUBTYPE_YUV440: |
|||
case VLC_CHROMA_SUBTYPE_YUV422: |
|||
return width * height * 2 * desc->color_bits; |
|||
case VLC_CHROMA_SUBTYPE_YUV420: |
|||
case VLC_CHROMA_SUBTYPE_YUV411: |
|||
return width * height * 1.5 * desc->color_bits; |
|||
case VLC_CHROMA_SUBTYPE_YUV410: |
|||
return width * height * 1.125 * desc->color_bits; |
|||
case VLC_CHROMA_SUBTYPE_YUV211: |
|||
case VLC_CHROMA_SUBTYPE_GREY: |
|||
return width * height * desc->color_bits; |
|||
case VLC_CHROMA_SUBTYPE_RGB: |
|||
return width * height * 4 * desc->color_bits; |
|||
default: |
|||
vlc_assert_unreachable(); |
|||
} |
|||
} |
|||
|
|||
uint64_t total_bits = 0; |
|||
for (unsigned i = 0; i < desc->plane_count; i++) |
|||
{ |
|||
const vlc_rational_t rw = desc->p[i].w; |
|||
const vlc_rational_t rh = desc->p[i].h; |
|||
|
|||
unsigned plane_width = width * rw.num / rw.den; |
|||
unsigned plane_height = height * rh.num / rh.den; |
|||
|
|||
uint64_t plane_pixels = plane_width * plane_height; |
|||
uint64_t plane_bits = plane_pixels * desc->pixel_bits; |
|||
|
|||
total_bits += plane_bits; |
|||
} |
|||
|
|||
return total_bits; |
|||
} |
|||
|
|||
static float |
|||
GetColorRatio(enum vlc_chroma_subtype subtype) |
|||
{ |
|||
switch (subtype) |
|||
{ |
|||
case VLC_CHROMA_SUBTYPE_YUV444: |
|||
case VLC_CHROMA_SUBTYPE_RGB: |
|||
return 1.f; |
|||
case VLC_CHROMA_SUBTYPE_YUV422: |
|||
return 0.67; |
|||
case VLC_CHROMA_SUBTYPE_YUV440: |
|||
return 0.5; /* should be like YUV422, but it is less common */ |
|||
case VLC_CHROMA_SUBTYPE_YUV420: |
|||
return 0.5; |
|||
case VLC_CHROMA_SUBTYPE_YUV411: |
|||
return 0.33; |
|||
case VLC_CHROMA_SUBTYPE_YUV410: |
|||
return 0.25; |
|||
case VLC_CHROMA_SUBTYPE_YUV211: |
|||
case VLC_CHROMA_SUBTYPE_OTHER: |
|||
return 0.2; |
|||
case VLC_CHROMA_SUBTYPE_GREY: |
|||
return 0.1; |
|||
default: |
|||
vlc_assert_unreachable(); |
|||
} |
|||
} |
|||
|
|||
static float |
|||
CompareDescs(const vlc_chroma_description_t *in_desc, |
|||
const vlc_chroma_description_t *out_desc) |
|||
{ |
|||
/* Compare color bits */ |
|||
float bits_ratio; |
|||
if (in_desc->color_bits == 0 || out_desc->color_bits == 0) |
|||
bits_ratio = 1.f; |
|||
else |
|||
{ |
|||
bits_ratio = out_desc->color_bits / in_desc->color_bits; |
|||
if (bits_ratio > 1.f) |
|||
bits_ratio = 1.f; |
|||
} |
|||
|
|||
/* Compare color ratios, favor same or near subtype */ |
|||
if (in_desc->subtype == out_desc->subtype) |
|||
return bits_ratio; |
|||
|
|||
float color_ratio = GetColorRatio(out_desc->subtype) |
|||
/ GetColorRatio(in_desc->subtype); |
|||
if (color_ratio > 1.f) |
|||
color_ratio = 1.f; |
|||
|
|||
/* Malus for CPU YUV <-> Other. Favor staying in the same color model. */ |
|||
bool in_is_yuv = vlc_chroma_description_IsYUV(in_desc); |
|||
bool out_is_yuv = vlc_chroma_description_IsYUV(out_desc); |
|||
if ((in_desc->plane_count != 0 && out_desc->plane_count != 0) |
|||
&& (in_is_yuv || out_is_yuv) && (in_is_yuv != out_is_yuv)) |
|||
color_ratio *= 0.9; |
|||
|
|||
return color_ratio * bits_ratio; |
|||
} |
|||
|
|||
static void |
|||
vlc_chroma_conv_result_FromNode(struct vlc_chroma_conv_result *res, |
|||
const struct bfs_node *node, |
|||
unsigned width, unsigned height) |
|||
{ |
|||
res->chain_count = node->depth + 1; |
|||
res->cost = 0; |
|||
|
|||
uint64_t total_cost = 0; |
|||
float total_quality = 1.f; |
|||
for (size_t i = 0; i < res->chain_count; ++i) |
|||
{ |
|||
res->chain[i] = node->chain[i]; |
|||
|
|||
if (i > 0) |
|||
{ |
|||
const vlc_chroma_description_t *from_desc = |
|||
vlc_fourcc_GetChromaDescription(res->chain[i - 1]); |
|||
const vlc_chroma_description_t *to_desc = |
|||
vlc_fourcc_GetChromaDescription(res->chain[i]); |
|||
|
|||
if (from_desc == NULL || to_desc == NULL) |
|||
{ |
|||
/* Unlikely, fallback for a big cost */ |
|||
total_cost += width * height * 4 * 8 * node->cost_factor; |
|||
continue; |
|||
} |
|||
|
|||
uint64_t from_bits = GetChromaBits(from_desc, width, height); |
|||
uint64_t to_bits = GetChromaBits(to_desc, width, height); |
|||
|
|||
/* Unlikely case */ |
|||
if (from_bits == 0) /* OTHER -> ANY */ |
|||
from_bits = to_bits; |
|||
else if (to_bits == 0) /* ANY -> OTHER */ |
|||
to_bits = from_bits; |
|||
|
|||
total_cost += (from_bits + to_bits) * node->cost_factor; |
|||
|
|||
float quality = CompareDescs(from_desc, to_desc); |
|||
assert(quality > 0.f && quality <= 1.f); |
|||
|
|||
total_quality *= quality; |
|||
} |
|||
} |
|||
res->cost = total_cost / width / height; |
|||
res->quality = 100 * total_quality; |
|||
} |
|||
|
|||
static int |
|||
SortResults(const void *a, const void *b, void *arg) |
|||
{ |
|||
const struct vlc_chroma_conv_result *ra = a; |
|||
const struct vlc_chroma_conv_result *rb = b; |
|||
bool *sort_by_quality = arg; |
|||
|
|||
int cost_score = 0, quality_score = 0; |
|||
|
|||
/* Lower cost is better */ |
|||
if (ra->cost < rb->cost) |
|||
cost_score = -1; |
|||
else if (ra->cost > rb->cost) |
|||
cost_score = 1; |
|||
|
|||
/* Higher Quality is better */ |
|||
if (ra->quality > rb->quality) |
|||
quality_score = -1; |
|||
else if (ra->quality < rb->quality) |
|||
quality_score = 1; |
|||
|
|||
/* Fallback to secondary score in same score */ |
|||
if (*sort_by_quality) |
|||
return quality_score != 0 ? quality_score : cost_score; |
|||
else |
|||
return cost_score != 0 ? cost_score : quality_score; |
|||
} |
|||
|
|||
static bool |
|||
bfs_node_IsResult(const struct bfs_node *node, vlc_fourcc_t to) |
|||
{ |
|||
vlc_fourcc_t current_chroma = node->chain[node->depth]; |
|||
return to == 0 || current_chroma == to; |
|||
} |
|||
|
|||
static bool |
|||
vlc_chroma_conv_result_Equals(struct vlc_chroma_conv_result *a, |
|||
struct vlc_chroma_conv_result *b) |
|||
{ |
|||
if (a->chain_count != b->chain_count) |
|||
return false; |
|||
if (a->quality != b->quality) |
|||
return false; |
|||
/* Don't check cost since we want to merge results with different costs */ |
|||
for (size_t i = 0; i < a->chain_count; ++i) |
|||
if (a->chain[i] != b->chain[i]) |
|||
return false; |
|||
return true; |
|||
} |
|||
|
|||
struct vlc_chroma_conv_result * |
|||
vlc_chroma_conv_Probe(vlc_fourcc_t from, vlc_fourcc_t to, |
|||
unsigned width, unsigned height, |
|||
unsigned max_indirect_steps, int flags, size_t *count) |
|||
{ |
|||
assert(from != 0); |
|||
assert(max_indirect_steps <= VLC_CHROMA_CONV_MAX_INDIRECT_STEPS); |
|||
vlc_chroma_conv_vec chroma_table; |
|||
vlc_vector_init(&chroma_table); |
|||
|
|||
if (width == 0 || height == 0) |
|||
{ |
|||
width = 3840; |
|||
height = 2160; |
|||
} |
|||
|
|||
if (max_indirect_steps > 0) |
|||
{ |
|||
/* Allow indirect steps only when converting from/to a GPU chroma */ |
|||
bool from_cpu = vlc_fourcc_GetChromaBPP(from) != 0; |
|||
bool to_cpu = to == 0 ? true : vlc_fourcc_GetChromaBPP(to) != 0; |
|||
if (from_cpu && to_cpu) |
|||
max_indirect_steps--; |
|||
} |
|||
|
|||
/* Probe modules */ |
|||
int ret = modules_Probe(&chroma_table); |
|||
if (ret != 0 || chroma_table.size == 0) |
|||
{ |
|||
vlc_vector_destroy(&chroma_table); |
|||
return NULL; |
|||
} |
|||
|
|||
/* Run tree search */ |
|||
bfs_queue_vec bfs_queue; |
|||
vlc_vector_init(&bfs_queue); |
|||
ret = bfs_Run(from, to, max_indirect_steps + 1 , &chroma_table, flags, |
|||
&bfs_queue); |
|||
|
|||
vlc_vector_destroy(&chroma_table); |
|||
|
|||
size_t result_count = 0; |
|||
for (size_t i = 1 /* skip start node */; i < bfs_queue.size; ++i) |
|||
if (bfs_node_IsResult(&bfs_queue.data[i], to)) |
|||
result_count++; |
|||
|
|||
if (unlikely(ret != 0) || result_count == 0) |
|||
{ |
|||
vlc_vector_destroy(&bfs_queue); |
|||
return NULL; |
|||
} |
|||
|
|||
/* Allocate the result array */ |
|||
struct VLC_VECTOR(struct vlc_chroma_conv_result) result_vec = |
|||
VLC_VECTOR_INITIALIZER; |
|||
bool success = vlc_vector_push_hole(&result_vec, result_count); |
|||
if (!success) |
|||
{ |
|||
vlc_vector_destroy(&bfs_queue); |
|||
return NULL; |
|||
} |
|||
|
|||
/* Fill the result from the tree search */ |
|||
size_t res_idx = 0; |
|||
for (size_t i = 1 /* skip start node */; i < bfs_queue.size; ++i) |
|||
{ |
|||
const struct bfs_node *node = &bfs_queue.data[i]; |
|||
if (!bfs_node_IsResult(node, to)) |
|||
continue; |
|||
|
|||
assert(res_idx < result_count); |
|||
struct vlc_chroma_conv_result *res = &result_vec.data[res_idx++]; |
|||
vlc_chroma_conv_result_FromNode(res, node, width, height); |
|||
} |
|||
assert(res_idx == result_count); |
|||
|
|||
vlc_vector_destroy(&bfs_queue); |
|||
|
|||
/* Sort */ |
|||
bool sort_by_quality = (flags & VLC_CHROMA_CONV_FLAG_SORT_COST) == 0; |
|||
vlc_qsort(result_vec.data, result_count, |
|||
sizeof(struct vlc_chroma_conv_result), SortResults, |
|||
&sort_by_quality); |
|||
|
|||
/* Remove duplicate entries, it can happen when more the 2 modules probe
|
|||
* the same conversion. They are not necessarily one after the other as |
|||
* they might have different quality. */ |
|||
for (size_t i = 0; i < result_vec.size - 1; ++ i) |
|||
{ |
|||
struct vlc_chroma_conv_result *cur = &result_vec.data[i]; |
|||
|
|||
size_t j = i + 1; |
|||
while (j < result_vec.size) |
|||
{ |
|||
struct vlc_chroma_conv_result *next = &result_vec.data[j]; |
|||
if (vlc_chroma_conv_result_Equals(cur, next)) |
|||
{ |
|||
/* Keep the lowest cost */ |
|||
if (next->cost < cur->cost) |
|||
cur->cost = next->cost; |
|||
vlc_vector_remove(&result_vec, j); |
|||
} |
|||
else |
|||
j++; |
|||
} |
|||
} |
|||
|
|||
*count = result_vec.size; |
|||
return result_vec.data; |
|||
} |
|||
|
|||
char * |
|||
vlc_chroma_conv_result_ToString(const struct vlc_chroma_conv_result *res) |
|||
{ |
|||
struct vlc_memstream ms; |
|||
int ret = vlc_memstream_open(&ms); |
|||
if (ret != 0) |
|||
return NULL; |
|||
vlc_memstream_printf(&ms, "[c=%u|q=%u] ", res->cost, res->quality); |
|||
|
|||
for (size_t i = 0; i < res->chain_count; ++i) |
|||
{ |
|||
vlc_memstream_printf(&ms, "%4.4s", (const char *) &res->chain[i]); |
|||
if (i != res->chain_count - 1) |
|||
vlc_memstream_puts(&ms, " -> "); |
|||
} |
|||
ret = vlc_memstream_close(&ms); |
|||
return ret == 0 ? ms.ptr : NULL; |
|||
} |
|||
@ -0,0 +1,379 @@ |
|||
/*****************************************************************************
|
|||
* chroma_probe.c: test for chroma_probe |
|||
***************************************************************************** |
|||
* Copyright (C) 2025 VLC authors and VideoLAN |
|||
* |
|||
* 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, write to the Free Software |
|||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. |
|||
*****************************************************************************/ |
|||
|
|||
#ifdef HAVE_CONFIG_H |
|||
# include "config.h" |
|||
#endif |
|||
|
|||
/* Define a builtin module for mocked parts */ |
|||
#define MODULE_NAME test_chroma_probe |
|||
#undef VLC_DYNAMIC_PLUGIN |
|||
#include "../../libvlc/test.h" |
|||
|
|||
#include <vlc/vlc.h> |
|||
|
|||
#include <vlc_common.h> |
|||
#include <vlc_plugin.h> |
|||
#include <vlc_chroma_probe.h> |
|||
#include <vlc_fourcc.h> |
|||
|
|||
#include <assert.h> |
|||
|
|||
const char vlc_module_name[] = MODULE_STRING; |
|||
|
|||
#define RESULT_MAX 6 |
|||
struct scenario_result |
|||
{ |
|||
unsigned cost; |
|||
unsigned quality; |
|||
vlc_fourcc_t chain[VLC_CHROMA_CONV_CHAIN_COUNT_MAX - 1 /* exclude 'from' */]; |
|||
}; |
|||
|
|||
struct scenario |
|||
{ |
|||
unsigned max_indirect_steps; |
|||
int flags; |
|||
vlc_fourcc_t in; |
|||
vlc_fourcc_t out; |
|||
struct scenario_result results[RESULT_MAX]; |
|||
size_t result_count; |
|||
}; |
|||
|
|||
static const struct scenario scenario_array[] = |
|||
{ |
|||
#define COST VLC_CHROMA_CONV_FLAG_SORT_COST |
|||
#define ONLY_YUV VLC_CHROMA_CONV_FLAG_ONLY_YUV |
|||
#define ONLY_RGB VLC_CHROMA_CONV_FLAG_ONLY_RGB |
|||
#define RESULT(cost, quality, chain0, chain1 ) \ |
|||
{ cost, quality, { chain0, chain1 } } |
|||
|
|||
#define SCENARIO0(max_indirect_steps_, from, to) { \ |
|||
.max_indirect_steps = max_indirect_steps_, \ |
|||
.flags = 0, \ |
|||
.in = from, .out = to, \ |
|||
.result_count = 0, \ |
|||
} |
|||
|
|||
#define SCENARIO1(max_indirect_steps_, sort_, from, to, cost, quality, chain0) { \ |
|||
.max_indirect_steps = max_indirect_steps_, \ |
|||
.flags = sort_, \ |
|||
.in = from, .out = to, \ |
|||
.results = { RESULT(cost, quality, chain0, 0) }, \ |
|||
.result_count = 1, \ |
|||
} |
|||
|
|||
#define SCENARIO2(max_indirect_steps_, sort_, from, to, \ |
|||
result0_cost, result0_quality, result0_chain0, \ |
|||
result1_cost, result1_quality, result1_chain0) { \ |
|||
.max_indirect_steps = max_indirect_steps_, \ |
|||
.flags = sort_, \ |
|||
.in = from, .out = to, \ |
|||
.results = { RESULT(result0_cost, result0_quality, result0_chain0, 0), \ |
|||
RESULT(result1_cost, result1_quality, result1_chain0, 0), }, \ |
|||
.result_count = 2, \ |
|||
} |
|||
|
|||
#define SCENARIOX(max_indirect_steps_, sort_, from, to, count, ...) { \ |
|||
.max_indirect_steps = max_indirect_steps_, \ |
|||
.flags = sort_, \ |
|||
.in = from, .out = to, \ |
|||
.results = { __VA_ARGS__ }, \ |
|||
.result_count = count, \ |
|||
} |
|||
|
|||
/* Success with a depth of 0 (Direct conversion) */ |
|||
SCENARIO1(0, 0, VLC_CODEC_VAAPI_420, VLC_CODEC_I420, 26, 100, 0), |
|||
|
|||
/* Success with a depth of 1 */ |
|||
SCENARIO1(1, 0, VLC_CODEC_VAAPI_420_10BPP, VLC_CODEC_I420, |
|||
47, 80, VLC_CODEC_P010), |
|||
|
|||
/* Fail because it require a depth of 1 */ |
|||
SCENARIO0(0, VLC_CODEC_VAAPI_420_10BPP, VLC_CODEC_I420), |
|||
|
|||
/* Check duplicated entries are removed and that we keep the lowest cost */ |
|||
SCENARIO1(1, 0, VLC_CODEC_NV12, VLC_CODEC_I420, |
|||
18, 100, 0), |
|||
|
|||
/* Check two_way is doing as expected */ |
|||
SCENARIO1(1, 0, VLC_CODEC_I420, VLC_CODEC_VAAPI_420_10BPP, |
|||
47, 100, VLC_CODEC_P010), |
|||
|
|||
/* Fail because it requires a depth of 2 */ |
|||
SCENARIO0(1, VLC_CODEC_CVPX_P010, VLC_CODEC_P010), |
|||
|
|||
/* Fail because conversion is not two-way */ |
|||
SCENARIO0(1, VLC_CODEC_P010, VLC_CODEC_CVPX_P010), |
|||
|
|||
/* Check low cost of GPU <-> GPU */ |
|||
SCENARIO1(1, 0, VLC_CODEC_CVPX_P010, VLC_CODEC_CVPX_BGRA, 11, 80, 0), |
|||
|
|||
/* Check cost and quality of direct conversion */ |
|||
SCENARIO1(0, 0, VLC_CODEC_YUVA_444_12L, VLC_CODEC_I420, 60, 33, 0), |
|||
|
|||
/* Check 1 depth conversions are correctly sorted */ |
|||
SCENARIOX(1, 0, VLC_CODEC_VAAPI_420_10BPP, 0, 6, |
|||
RESULT(33, 100, VLC_CODEC_P010, 0), |
|||
RESULT(33, 100, VLC_CODEC_I420_10L, 0), |
|||
RESULT(66, 100, VLC_CODEC_P010, VLC_CODEC_I420_10L), |
|||
RESULT(66, 100, VLC_CODEC_I420_10L, VLC_CODEC_P010), |
|||
RESULT(47, 80, VLC_CODEC_P010, VLC_CODEC_I420), |
|||
RESULT(84, 72, VLC_CODEC_P010, VLC_CODEC_RGBA)), |
|||
|
|||
/* Check default QUALITY order */ |
|||
SCENARIOX(0, 0, VLC_CODEC_YUVA_444_12L, 0, 4, |
|||
RESULT(112, 90, VLC_CODEC_RGBA64, 0), |
|||
RESULT(88, 83, VLC_CODEC_YUVA_444_10L, 0), |
|||
RESULT(80, 60, VLC_CODEC_RGBA, 0), |
|||
RESULT(60, 33, VLC_CODEC_I420, 0)), |
|||
|
|||
/* Check ONLY_YUV */ |
|||
SCENARIOX(0, ONLY_YUV, VLC_CODEC_YUVA_444_12L, 0, 2, |
|||
RESULT(88, 83, VLC_CODEC_YUVA_444_10L, 0), |
|||
RESULT(60, 33, VLC_CODEC_I420, 0)), |
|||
|
|||
/* Check ONLY_RGB */ |
|||
SCENARIOX(0, ONLY_RGB, VLC_CODEC_YUVA_444_12L, 0, 2, |
|||
RESULT(112, 90, VLC_CODEC_RGBA64, 0), |
|||
RESULT(80, 60, VLC_CODEC_RGBA, 0)), |
|||
|
|||
/* Check COST order */ |
|||
SCENARIOX(0, COST, VLC_CODEC_YUVA_444_12L, 0, 4, |
|||
RESULT(60, 33, VLC_CODEC_I420, 0), |
|||
RESULT(80, 60, VLC_CODEC_RGBA, 0), |
|||
RESULT(88, 83, VLC_CODEC_YUVA_444_10L, 0), |
|||
RESULT(112, 90, VLC_CODEC_RGBA64, 0)), |
|||
|
|||
/* Check VLC_CHROMA_CONV_ADD_IN_OUTTLIST, and quality order with smaller
|
|||
* RGB chromas */ |
|||
SCENARIOX(0, 0, VLC_CODEC_YV12, 0, 5, |
|||
RESULT(36, 90, VLC_CODEC_XRGB, 0), |
|||
RESULT(28, 59, VLC_CODEC_RGB565, 0), |
|||
RESULT(28, 59, VLC_CODEC_BGR565, 0), |
|||
RESULT(27, 56, VLC_CODEC_RGB555, 0), |
|||
RESULT(27, 56, VLC_CODEC_BGR555, 0)), |
|||
|
|||
/* Check VLC_CHROMA_CONV_ADD_OUT_INLIST */ |
|||
SCENARIO1(0, 0, VLC_CODEC_NV16, VLC_CODEC_I422, 32, 100, 0), |
|||
SCENARIO1(0, 0, VLC_CODEC_YUYV, VLC_CODEC_I422, 32, 100, 0), |
|||
SCENARIO1(0, 0, VLC_CODEC_UYVY, VLC_CODEC_I422, 32, 100, 0), |
|||
|
|||
/* Check VLC_CHROMA_CONV_ADD_ALL */ |
|||
SCENARIO1(1, 0, VLC_CODEC_I444_12L, VLC_CODEC_I444, 60, 66, 0), |
|||
SCENARIO1(1, 0, VLC_CODEC_I444, VLC_CODEC_I444_12L, 60, 100, 0), |
|||
SCENARIO1(1, 0, VLC_CODEC_I444_12L, VLC_CODEC_I444_10L, 66, 83, 0), |
|||
SCENARIO1(1, 0, VLC_CODEC_I444_10L, VLC_CODEC_I444_12L, 66, 100, 0), |
|||
}; |
|||
|
|||
static void ProbeChroma(vlc_chroma_conv_vec *vec) |
|||
{ |
|||
vlc_chroma_conv_add(vec, 1.1, VLC_CODEC_VAAPI_420, VLC_CODEC_I420, true); |
|||
vlc_chroma_conv_add(vec, 1.1, VLC_CODEC_VAAPI_420_10BPP, VLC_CODEC_P010, true); |
|||
vlc_chroma_conv_add(vec, 1.1, VLC_CODEC_VAAPI_420_10BPP, VLC_CODEC_I420_10L, true); |
|||
|
|||
vlc_chroma_conv_add(vec, 0.75, VLC_CODEC_I420, VLC_CODEC_NV12, true); |
|||
vlc_chroma_conv_add(vec, 0.75, VLC_CODEC_I420, VLC_CODEC_P010, true); |
|||
vlc_chroma_conv_add(vec, 1, VLC_CODEC_I420_10L, VLC_CODEC_P010, true); |
|||
vlc_chroma_conv_add(vec, 1, VLC_CODEC_RGBA, VLC_CODEC_P010, true); |
|||
vlc_chroma_conv_add(vec, 1, VLC_CODEC_RGBA, VLC_CODEC_NV12, true); |
|||
|
|||
/* Test duplicated entries are removed */ |
|||
vlc_chroma_conv_add(vec, 1.0, VLC_CODEC_I420, VLC_CODEC_NV12, true); |
|||
|
|||
/* Don't change this order as this is used to test to cost sort
|
|||
* (we don't want the result to be naturally sorted) */ |
|||
vlc_chroma_conv_add(vec, 1, VLC_CODEC_YUVA_444_12L, VLC_CODEC_RGBA, true); |
|||
vlc_chroma_conv_add(vec, 1, VLC_CODEC_YUVA_444_12L, VLC_CODEC_I420, true); |
|||
|
|||
vlc_chroma_conv_add(vec, 1, VLC_CODEC_YUVA_444_12L, VLC_CODEC_YUVA_444_10L, true); |
|||
vlc_chroma_conv_add(vec, 1, VLC_CODEC_YUVA_444_12L, VLC_CODEC_RGBA64, true); |
|||
|
|||
vlc_chroma_conv_add(vec, 0.25, VLC_CODEC_CVPX_NV12, VLC_CODEC_CVPX_BGRA, false); |
|||
vlc_chroma_conv_add(vec, 1.1, VLC_CODEC_CVPX_NV12, VLC_CODEC_NV12, false); |
|||
vlc_chroma_conv_add(vec, 0.25, VLC_CODEC_CVPX_P010, VLC_CODEC_CVPX_BGRA, false); |
|||
vlc_chroma_conv_add(vec, 1.1, VLC_CODEC_CVPX_BGRA, VLC_CODEC_RGBA, false); |
|||
|
|||
vlc_chroma_conv_add_in_outlist(vec, 1, VLC_CODEC_YV12, VLC_CODEC_XRGB, |
|||
VLC_CODEC_RGB565, VLC_CODEC_BGR565, |
|||
VLC_CODEC_RGB555, VLC_CODEC_BGR555); |
|||
|
|||
vlc_chroma_conv_add_out_inlist(vec, 1, VLC_CODEC_I422, VLC_CODEC_NV16, |
|||
VLC_CODEC_YUYV, VLC_CODEC_UYVY); |
|||
|
|||
vlc_chroma_conv_add(vec, 1, VLC_CODEC_I444, VLC_CODEC_I444_10L, true); |
|||
vlc_chroma_conv_add(vec, 1, VLC_CODEC_I444, VLC_CODEC_I444_12L, true); |
|||
vlc_chroma_conv_add(vec, 1, VLC_CODEC_I444_10L, VLC_CODEC_I444_12L, true); |
|||
|
|||
/* Test duplicated entries are removed */ |
|||
vlc_chroma_conv_add(vec, 1.0, VLC_CODEC_I420, VLC_CODEC_NV12, true); |
|||
} |
|||
|
|||
vlc_module_begin() |
|||
set_callback_chroma_conv_probe(ProbeChroma) |
|||
vlc_module_end() |
|||
|
|||
VLC_EXPORT vlc_plugin_cb vlc_static_modules[] = { |
|||
VLC_SYMBOL(vlc_entry), |
|||
NULL |
|||
}; |
|||
|
|||
static void |
|||
print_results(const struct vlc_chroma_conv_result *array, size_t count) |
|||
{ |
|||
for (size_t i = 0; i < count; ++i) |
|||
{ |
|||
const struct vlc_chroma_conv_result *res = &array[i]; |
|||
char *res_str = vlc_chroma_conv_result_ToString(res); |
|||
assert(res_str != NULL); |
|||
fprintf(stderr, "\tres[%zu]: %s\n", i, res_str); |
|||
free(res_str); |
|||
} |
|||
} |
|||
|
|||
static void |
|||
check_results(const struct scenario *scr, |
|||
const struct vlc_chroma_conv_result *results) |
|||
{ |
|||
for (size_t i = 0; i < scr->result_count; ++i) |
|||
{ |
|||
const struct vlc_chroma_conv_result *result = &results[i]; |
|||
const struct scenario_result *scr_result = &scr->results[i]; |
|||
|
|||
assert(result->chain_count > 1); |
|||
assert(result->chain_count <= VLC_CHROMA_CONV_CHAIN_COUNT_MAX); |
|||
|
|||
/* Reconstruct the expected fourcc array from the scenario */ |
|||
vlc_fourcc_t scr_chain[VLC_CHROMA_CONV_CHAIN_COUNT_MAX]; |
|||
bool end_reached = false; |
|||
size_t scr_count = 1; |
|||
scr_chain[0] = scr->in; |
|||
for (size_t j = 1; j < VLC_CHROMA_CONV_CHAIN_COUNT_MAX; ++j) |
|||
{ |
|||
if (end_reached) |
|||
{ |
|||
scr_chain[j] = 0; |
|||
continue; |
|||
} |
|||
|
|||
if (scr_result->chain[j - 1] != 0) |
|||
{ |
|||
scr_chain[j] = scr_result->chain[j - 1]; |
|||
scr_count++; |
|||
} |
|||
else |
|||
{ |
|||
if (scr->out != 0) |
|||
{ |
|||
scr_chain[j] = scr->out; |
|||
scr_count++; |
|||
} |
|||
end_reached = true; |
|||
} |
|||
} |
|||
|
|||
assert(scr_count == result->chain_count); |
|||
size_t j; |
|||
for (j = 0; j < result->chain_count; ++j) |
|||
assert(result->chain[j] == scr_chain[j]); |
|||
for (; j < VLC_CHROMA_CONV_CHAIN_COUNT_MAX - 1; ++j) |
|||
assert(scr_result->chain[j - 1] == 0); |
|||
|
|||
assert(result->cost == scr_result->cost); |
|||
assert(result->quality == scr_result->quality); |
|||
} |
|||
} |
|||
|
|||
int main(int argc, const char *argv[]) |
|||
{ |
|||
test_init(); |
|||
|
|||
if (argc > 1 && strlen(argv[1]) >= 4) |
|||
{ |
|||
/* Disable test module (use all VLC modules) */ |
|||
vlc_static_modules[0] = NULL; |
|||
|
|||
unsigned max_indirect_steps = 1; |
|||
if (argc > 2) |
|||
max_indirect_steps = atoi(argv[2]); |
|||
|
|||
int flags = 0; |
|||
if (argc > 3) |
|||
flags = atoi(argv[3]); |
|||
|
|||
const char *f = argv[1]; |
|||
vlc_fourcc_t from_fourcc = VLC_FOURCC(f[0], f[1], f[2], f[3]); |
|||
vlc_fourcc_t to_fourcc = 0; |
|||
if (f[4] == '-' && strlen(f) >= 9) |
|||
to_fourcc = VLC_FOURCC(f[5], f[6], f[7], f[8]); |
|||
libvlc_instance_t *vlc = libvlc_new(0, NULL); |
|||
assert(vlc != NULL); |
|||
|
|||
size_t count; |
|||
struct vlc_chroma_conv_result *results = |
|||
vlc_chroma_conv_Probe(from_fourcc, to_fourcc, 0, 0, |
|||
max_indirect_steps, flags, &count); |
|||
assert(results != NULL); |
|||
print_results(results, count); |
|||
free(results); |
|||
|
|||
libvlc_release(vlc); |
|||
return 0; |
|||
} |
|||
|
|||
/* Disable all modules except the one from this test */ |
|||
const char *libvlc_argv[] = { |
|||
"--no-plugins-cache", |
|||
"--no-plugins-scan", |
|||
}; |
|||
int libvlc_argc = ARRAY_SIZE(libvlc_argv); |
|||
|
|||
libvlc_instance_t *vlc = libvlc_new(libvlc_argc, libvlc_argv); |
|||
assert(vlc != NULL); |
|||
|
|||
size_t scenario_count = ARRAY_SIZE(scenario_array); |
|||
for (size_t i = 0; i < scenario_count; i++) |
|||
{ |
|||
const struct scenario *scr = &scenario_array[i]; |
|||
|
|||
fprintf(stderr, "scenario: %4.4s -> %4.4s flags: 0x%x, " |
|||
"max_indirect_steps: %u result_count: %zu\n", |
|||
(const char *)&scr->in, (const char *)&scr->out, |
|||
scr->flags, |
|||
scr->max_indirect_steps, scr->result_count); |
|||
|
|||
size_t count; |
|||
struct vlc_chroma_conv_result *results = |
|||
vlc_chroma_conv_Probe(scr->in, scr->out, 0, 0, |
|||
scr->max_indirect_steps, scr->flags, &count); |
|||
if (results == NULL) |
|||
{ |
|||
assert(scr->result_count == 0); |
|||
continue; |
|||
} |
|||
print_results(results, count); |
|||
assert(count == scr->result_count); |
|||
check_results(scr, results); |
|||
free(results); |
|||
} |
|||
|
|||
libvlc_release(vlc); |
|||
|
|||
return 0; |
|||
} |
|||
Loading…
Reference in new issue