<commit_before>/*
 * ffapi - Wrapper for pulling/pushing frames with libav*
 * Copyright 2013-2016 0x09.net.
 */

#include "ffapi.h"

#include <libavutil/parseutils.h>
#include <libavutil/opt.h>
#include <libavutil/avstring.h>

#include <sys/stat.h>

#define UNSPECIFIED_COLOR_PROPERTIES \
	.pix_fmt = AV_PIX_FMT_NONE,\
	.color_range = AVCOL_RANGE_UNSPECIFIED,\
	.color_primaries = AVCOL_PRI_UNSPECIFIED,\
	.color_trc = AVCOL_TRC_UNSPECIFIED,\
	.color_space = AVCOL_SPC_UNSPECIFIED,\
	.chroma_location = AVCHROMA_LOC_UNSPECIFIED

const static FFColorProperties ffapi_default_color_properties = {
	UNSPECIFIED_COLOR_PROPERTIES
};

struct FFColorDefaults {
	const char* format;
	FFColorProperties props;
};

// ffmpeg doesn't provide structured info about what color properties are assumed by different formats/codecs,
// so we provide some defaults based on the actual decoder behavior when the container doesn't store this info
// anything set in the options string still takes precedence
const static struct FFColorDefaults ffapi_format_color_defaults[] = {
	{
		.format = "yuv4mpegpipe",
		.props = {
			UNSPECIFIED_COLOR_PROPERTIES,
			.color_range = AVCOL_RANGE_MPEG,
			.color_primaries = AVCOL_PRI_SMPTE170M,
			.color_trc = AVCOL_TRC_SMPTE170M,
			.color_space = AVCOL_SPC_SMPTE170M,
		}
	},{
		.format = "avi",
		.props = {
			UNSPECIFIED_COLOR_PROPERTIES,
			.color_range = AVCOL_RANGE_MPEG,
		}
	},{
		// default to sRGB
		.format = "image2",
		.props = {
			UNSPECIFIED_COLOR_PROPERTIES,
			.color_range = AVCOL_RANGE_JPEG,
			.color_primaries = AVCOL_PRI_BT709,
			.color_trc = AVCOL_TRC_IEC61966_2_1,
			.color_space = AVCOL_SPC_RGB,
		}
	},{0}
};

static void fill_color_defaults(AVOutputFormat* fmt, AVCodecContext* avc) {
	const struct FFColorDefaults* defaults;
	for(defaults = ffapi_format_color_defaults; defaults->format && strcmp(defaults->format,fmt->name); defaults++)
		;
	if(defaults->format) {
		// != isn't a typo, we want to set this only if the input color property is known
		if(defaults->props.color_range != AVCOL_RANGE_UNSPECIFIED && avc->color_range != AVCOL_RANGE_UNSPECIFIED)
			avc->color_range = defaults->props.color_range;
		if(defaults->props.color_space != AVCOL_SPC_UNSPECIFIED && avc->colorspace != AVCOL_SPC_UNSPECIFIED)
			avc->colorspace = defaults->props.color_space;
		if(defaults->props.color_primaries != AVCOL_PRI_UNSPECIFIED && avc->color_primaries != AVCOL_PRI_UNSPECIFIED)
			avc->color_primaries = defaults->props.color_primaries;
		if(defaults->props.color_trc != AVCOL_TRC_UNSPECIFIED && avc->color_trc != AVCOL_TRC_UNSPECIFIED)
			avc->color_trc = defaults->props.color_trc;
		if(defaults->props.chroma_location != AVCHROMA_LOC_UNSPECIFIED && avc->chroma_sample_location != AVCHROMA_LOC_UNSPECIFIED)
			avc->chroma_sample_location = defaults->props.chroma_location;
	}
	// y4m input format infers different chroma locations based on the colorspace header, but stock C420 is taken as center
	if(!strcmp(fmt->name,"yuv4mpegpipe")) {
		if(av_pix_fmt_desc_get(avc->pix_fmt)->flags & AV_PIX_FMT_FLAG_RGB)
			avc->pix_fmt = AV_PIX_FMT_YUV444P;
		else if(avc->pix_fmt == AV_PIX_FMT_YUV420P &&
			avc->chroma_sample_location != AVCHROMA_LOC_UNSPECIFIED &&
			avc->chroma_sample_location != AVCHROMA_LOC_TOPLEFT && //C420paldv
			avc->chroma_sample_location != AVCHROMA_LOC_LEFT) // C420mpeg2
			avc->chroma_sample_location = AVCHROMA_LOC_CENTER;
	}
	// if the input colorspace is RGB but the output pixel format isn't, default to BT601
	if(avc->colorspace == AVCOL_SPC_RGB && !(av_pix_fmt_desc_get(avc->pix_fmt)->flags & AV_PIX_FMT_FLAG_RGB))
		avc->colorspace = AVCOL_SPC_SMPTE170M;
}

void ffapi_parse_color_props(FFColorProperties* c, const char* props) {
	*c = ffapi_default_color_properties;
	if(!props || !*props)
		return;
	AVDictionary* d = NULL;
	av_dict_parse_string(&d, props, "=", ":", 0);
	AVDictionaryEntry* e;
	if((e = av_dict_get(d,"pixel_format",NULL,0)))
		c->pix_fmt = av_get_pix_fmt(e->value);
	if((e = av_dict_get(d,"color_range",NULL,0)))
		c->color_range = av_color_range_from_name(e->value);
	if((e = av_dict_get(d,"color_primaries",NULL,0)))
		c->color_primaries = av_color_primaries_from_name(e->value);
	if((e = av_dict_get(d,"color_trc",NULL,0)))
		c->color_trc = av_color_transfer_from_name(e->value);
	if((e = av_dict_get(d,"colorspace",NULL,0)))
		c->color_space = av_color_space_from_name(e->value);
	if((e = av_dict_get(d,"chroma_sample_location",NULL,0)))
		c->chroma_location = av_chroma_location_from_name(e->value);
	av_dict_free(&d);
}

#define validate_color_prop(c,ret,prop,strprop,getter) do {\
	if(!getter(c->prop)) {\
		av_log(NULL,AV_LOG_ERROR,"ffapi: Invalid " strprop "\n");\
		ret = false;\
	}\
} while(0)

bool ffapi_validate_color_props(const FFColorProperties* c) {
	bool ret = true;
	validate_color_prop(c,ret,pix_fmt,"pixel_format",av_pix_fmt_desc_get);<commit_msg>ffapi: fix color property validation for AV_PIX_FMT_NONE<commit_after>/*
 * ffapi - Wrapper for pulling/pushing frames with libav*
 * Copyright 2013-2016 0x09.net.
 */

#include "ffapi.h"

#include <libavutil/parseutils.h>
#include <libavutil/opt.h>
#include <libavutil/avstring.h>

#include <sys/stat.h>

#define UNSPECIFIED_COLOR_PROPERTIES \
	.pix_fmt = AV_PIX_FMT_NONE,\
	.color_range = AVCOL_RANGE_UNSPECIFIED,\
	.color_primaries = AVCOL_PRI_UNSPECIFIED,\
	.color_trc = AVCOL_TRC_UNSPECIFIED,\
	.color_space = AVCOL_SPC_UNSPECIFIED,\
	.chroma_location = AVCHROMA_LOC_UNSPECIFIED

const static FFColorProperties ffapi_default_color_properties = {
	UNSPECIFIED_COLOR_PROPERTIES
};

struct FFColorDefaults {
	const char* format;
	FFColorProperties props;
};

// ffmpeg doesn't provide structured info about what color properties are assumed by different formats/codecs,
// so we provide some defaults based on the actual decoder behavior when the container doesn't store this info
// anything set in the options string still takes precedence
const static struct FFColorDefaults ffapi_format_color_defaults[] = {
	{
		.format = "yuv4mpegpipe",
		.props = {
			UNSPECIFIED_COLOR_PROPERTIES,
			.color_range = AVCOL_RANGE_MPEG,
			.color_primaries = AVCOL_PRI_SMPTE170M,
			.color_trc = AVCOL_TRC_SMPTE170M,
			.color_space = AVCOL_SPC_SMPTE170M,
		}
	},{
		.format = "avi",
		.props = {
			UNSPECIFIED_COLOR_PROPERTIES,
			.color_range = AVCOL_RANGE_MPEG,
		}
	},{
		// default to sRGB
		.format = "image2",
		.props = {
			UNSPECIFIED_COLOR_PROPERTIES,
			.color_range = AVCOL_RANGE_JPEG,
			.color_primaries = AVCOL_PRI_BT709,
			.color_trc = AVCOL_TRC_IEC61966_2_1,
			.color_space = AVCOL_SPC_RGB,
		}
	},{0}
};

static void fill_color_defaults(AVOutputFormat* fmt, AVCodecContext* avc) {
	const struct FFColorDefaults* defaults;
	for(defaults = ffapi_format_color_defaults; defaults->format && strcmp(defaults->format,fmt->name); defaults++)
		;
	if(defaults->format) {
		// != isn't a typo, we want to set this only if the input color property is known
		if(defaults->props.color_range != AVCOL_RANGE_UNSPECIFIED && avc->color_range != AVCOL_RANGE_UNSPECIFIED)
			avc->color_range = defaults->props.color_range;
		if(defaults->props.color_space != AVCOL_SPC_UNSPECIFIED && avc->colorspace != AVCOL_SPC_UNSPECIFIED)
			avc->colorspace = defaults->props.color_space;
		if(defaults->props.color_primaries != AVCOL_PRI_UNSPECIFIED && avc->color_primaries != AVCOL_PRI_UNSPECIFIED)
			avc->color_primaries = defaults->props.color_primaries;
		if(defaults->props.color_trc != AVCOL_TRC_UNSPECIFIED && avc->color_trc != AVCOL_TRC_UNSPECIFIED)
			avc->color_trc = defaults->props.color_trc;
		if(defaults->props.chroma_location != AVCHROMA_LOC_UNSPECIFIED && avc->chroma_sample_location != AVCHROMA_LOC_UNSPECIFIED)
			avc->chroma_sample_location = defaults->props.chroma_location;
	}
	// y4m input format infers different chroma locations based on the colorspace header, but stock C420 is taken as center
	if(!strcmp(fmt->name,"yuv4mpegpipe")) {
		if(av_pix_fmt_desc_get(avc->pix_fmt)->flags & AV_PIX_FMT_FLAG_RGB)
			avc->pix_fmt = AV_PIX_FMT_YUV444P;
		else if(avc->pix_fmt == AV_PIX_FMT_YUV420P &&
			avc->chroma_sample_location != AVCHROMA_LOC_UNSPECIFIED &&
			avc->chroma_sample_location != AVCHROMA_LOC_TOPLEFT && //C420paldv
			avc->chroma_sample_location != AVCHROMA_LOC_LEFT) // C420mpeg2
			avc->chroma_sample_location = AVCHROMA_LOC_CENTER;
	}
	// if the input colorspace is RGB but the output pixel format isn't, default to BT601
	if(avc->colorspace == AVCOL_SPC_RGB && !(av_pix_fmt_desc_get(avc->pix_fmt)->flags & AV_PIX_FMT_FLAG_RGB))
		avc->colorspace = AVCOL_SPC_SMPTE170M;
}

void ffapi_parse_color_props(FFColorProperties* c, const char* props) {
	*c = ffapi_default_color_properties;
	if(!props || !*props)
		return;
	AVDictionary* d = NULL;
	av_dict_parse_string(&d, props, "=", ":", 0);
	AVDictionaryEntry* e;
	if((e = av_dict_get(d,"pixel_format",NULL,0)))
		c->pix_fmt = av_get_pix_fmt(e->value);
	if((e = av_dict_get(d,"color_range",NULL,0)))
		c->color_range = av_color_range_from_name(e->value);
	if((e = av_dict_get(d,"color_primaries",NULL,0)))
		c->color_primaries = av_color_primaries_from_name(e->value);
	if((e = av_dict_get(d,"color_trc",NULL,0)))
		c->color_trc = av_color_transfer_from_name(e->value);
	if((e = av_dict_get(d,"colorspace",NULL,0)))
		c->color_space = av_color_space_from_name(e->value);
	if((e = av_dict_get(d,"chroma_sample_location",NULL,0)))
		c->chroma_location = av_chroma_location_from_name(e->value);
	av_dict_free(&d);
}

#define validate_color_prop(c,ret,prop,strprop,getter) do {\
	if(!getter(c->prop)) {\
		av_log(NULL,AV_LOG_ERROR,"ffapi: Invalid " strprop "\n");\
		ret = false;\
	}\
} while(0)

bool ffapi_validate_color_props(const FFColorProperties* c) {
	bool ret = true;
	if(c->pix_fmt != AV_PIX_FMT_NONE)
		validate_color_prop(c,ret,pix_fmt,"pixel_format",av_pix_fmt_desc_get);
{'fraction_non_alphanumeric': 0.12277126846663271, 'fraction_numerical': 0.013245033112582781, 'mean_word_length': 5.6190155091031695, 'pattern_counts': {'":': 2, '<': 11, '<?xml version=': 0, '>': 112, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2796881', 'n_tokens_mistral': 4086, 'n_tokens_neox': 3844, 'n_words': 627}