#!/bin/bash
# IMPORTANT comments below to change the configure line
-# For python version 12, add --without-lv2
# For newer operating system versions, add --enable-libsvtav1
# For really old versions, such as ubuntu 16, add --enable-libaom=no
+# To add OpenCV plugins, add --with-opencv=sta,tar=http://cinelerra-gg.org/download/opencv/opencv-20200306.tgz
( ./autogen.sh
./configure --with-single-user --with-booby
make && make install ) 2>&1 | tee log
break; }
case AVMEDIA_TYPE_AUDIO: {
s->coding_type = bd_coding_type(codec_id);
- s->format = bd_audio_format(st->codecpar->channels);
+ s->format = bd_audio_format(st->codecpar->ch_layout.nb_channels);
s->rate = bd_audio_rate(st->codecpar->sample_rate);
strcpy((char*)s->lang, "eng");
break; }
swr_ichs = ichs; swr_ifmt = ifmt; swr_irate = irate;
if( ichs == channels && ifmt == AV_SAMPLE_FMT_FLT && irate == sample_rate )
return;
- uint64_t ilayout = av_get_default_channel_layout(ichs);
- if( !ilayout ) ilayout = ((uint64_t)1<<ichs) - 1;
- uint64_t olayout = av_get_default_channel_layout(channels);
- if( !olayout ) olayout = ((uint64_t)1<<channels) - 1;
- resample_context = swr_alloc_set_opts(NULL,
- olayout, AV_SAMPLE_FMT_FLT, sample_rate,
- ilayout, (AVSampleFormat)ifmt, irate,
+ //uint64_t ilayout = av_get_default_channel_layout(ichs);
+ AVChannelLayout ilayout, olayout;
+ av_channel_layout_default(&ilayout, ichs);
+ //if( !ilayout ) ilayout = ((uint64_t)1<<ichs) - 1;
+ //uint64_t olayout = av_get_default_channel_layout(channels);
+ av_channel_layout_default(&olayout, channels);
+ //if( !olayout ) olayout = ((uint64_t)1<<channels) - 1;
+
+ swr_alloc_set_opts2(&resample_context,
+ &olayout, AV_SAMPLE_FMT_FLT, sample_rate,
+ &ilayout, (AVSampleFormat)ifmt, irate,
0, NULL);
if( resample_context )
swr_init(resample_context);
int64_t FFAudioStream::load_buffer(double ** const sp, int len)
{
+#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(61,3,100)
+ reserve(len+1, st->codecpar->ch_layout.nb_channels);
+#else
reserve(len+1, st->codecpar->channels);
+#endif
for( int ch=0; ch<nch; ++ch )
write(sp[ch], len, ch);
return put_inp(len);
{
frame->nb_samples = frame_sz;
frame->format = avctx->sample_fmt;
+#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(61,3,100)
+ frame->ch_layout.u.mask = avctx->ch_layout.u.mask;
+ av_channel_layout_copy(&frame->ch_layout, &avctx->ch_layout);
+#else
frame->channel_layout = avctx->channel_layout;
+#endif
frame->sample_rate = avctx->sample_rate;
int ret = av_frame_get_buffer(frame, 0);
if (ret < 0)
while( ret>=0 && !flushed && curr_pos<end_pos && --i>=0 ) {
ret = read_frame(frame);
if( ret > 0 && frame->nb_samples > 0 ) {
- init_swr(frame->channels, frame->format, frame->sample_rate);
+ init_swr(frame->ch_layout.nb_channels, frame->format, frame->sample_rate);
load_history(&frame->extended_data[0], frame->nb_samples);
curr_pos += frame->nb_samples;
}
const char *rg = av_color_range_name(range);
report("/ range:%s\n", rg ? rg : unkn);
+ enum AVColorPrimaries primaries = st->codecpar->color_primaries;
+ const char *pr = av_color_primaries_name(primaries);
+ report(" color primaries:%s", pr ? pr : unkn);
+ enum AVColorTransferCharacteristic trc = st->codecpar->color_trc;
+ const char *transfer = av_color_transfer_name(trc);
+ report("/ transfer characteristics:%s\n", transfer ? transfer : unkn);
+
+
AVRational sar = av_guess_sample_aspect_ratio(fmt_ctx, st, NULL);
AVRational display_aspect_ratio;
if(sar.num) {
ret = vid->create_filter(opt_video_filter);
break; }
case AVMEDIA_TYPE_AUDIO: {
- if( avpar->channels < 1 ) continue;
+ if( avpar->ch_layout.nb_channels < 1 ) continue;
if( avpar->sample_rate < 1 ) continue;
has_audio = 1;
int aidx = ffaudio.size();
FFAudioStream *aud = new FFAudioStream(this, st, aidx, i);
ffaudio.append(aud);
aud->channel0 = astrm_index.size();
- aud->channels = avpar->channels;
+ aud->channels = avpar->ch_layout.nb_channels;
for( int ch=0; ch<aud->channels; ++ch )
astrm_index.append(ffidx(aidx, ch));
aud->sample_rate = avpar->sample_rate;
FFAudioStream *aud = new FFAudioStream(this, st, aidx, fidx);
aud->avctx = ctx; ffaudio.append(aud); fst = aud;
aud->sample_rate = asset->sample_rate;
- ctx->channels = aud->channels = asset->channels;
+ ctx->ch_layout.nb_channels = aud->channels = asset->channels;
for( int ch=0; ch<aud->channels; ++ch )
astrm_index.append(ffidx(aidx, ch));
- ctx->channel_layout = av_get_default_channel_layout(ctx->channels);
+ AVChannelLayout ch_layout;
+ av_channel_layout_default(&ch_layout, ctx->ch_layout.nb_channels);
+ ctx->ch_layout.u.mask = ch_layout.u.mask;
+ av_channel_layout_copy(&ctx->ch_layout, &ch_layout);
ctx->sample_rate = check_sample_rate(codec, asset->sample_rate);
if( !ctx->sample_rate ) {
eprintf(_("check_sample_rate failed %s\n"), filename);
if( sample_fmt == AV_SAMPLE_FMT_NONE )
sample_fmt = codec->sample_fmts ? codec->sample_fmts[0] : AV_SAMPLE_FMT_S16;
ctx->sample_fmt = sample_fmt;
- uint64_t layout = av_get_default_channel_layout(ctx->channels);
- aud->resample_context = swr_alloc_set_opts(NULL,
- layout, ctx->sample_fmt, aud->sample_rate,
- layout, AV_SAMPLE_FMT_FLT, ctx->sample_rate,
+ //uint64_t layout = av_get_default_channel_layout(ctx->ch_layout.nb_channels);
+ AVChannelLayout layout;
+ av_channel_layout_default(&layout, ctx->ch_layout.nb_channels);
+ swr_alloc_set_opts2(&aud->resample_context,
+ &layout, ctx->sample_fmt, aud->sample_rate,
+ &layout, AV_SAMPLE_FMT_FLT, ctx->sample_rate,
0, NULL);
swr_init(aud->resample_context);
aud->writing = -1;
snprintf(args, sizeof(args),
"time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x%jx",
st->time_base.num, st->time_base.den, avpar->sample_rate,
- av_get_sample_fmt_name(sample_fmt), avpar->channel_layout);
+ av_get_sample_fmt_name(sample_fmt), avpar->ch_layout.u.mask);
if( ret >= 0 ) {
filt_ctx = 0;
ret = insert_filter("abuffer", args, "in");
AV_OPT_SEARCH_CHILDREN);
if( ret >= 0 )
ret = av_opt_set_bin(buffersink_ctx, "channel_layouts",
- (uint8_t*)&avpar->channel_layout,
- sizeof(avpar->channel_layout), AV_OPT_SEARCH_CHILDREN);
+ (uint8_t*)&avpar->ch_layout.u.mask,
+ sizeof(avpar->ch_layout.u.mask), AV_OPT_SEARCH_CHILDREN);
if( ret >= 0 )
ret = av_opt_set_bin(buffersink_ctx, "sample_rates",
(uint8_t*)&sample_rate, sizeof(sample_rate),
}
while( (ret=aud->decode_frame(frame)) > 0 ) {
//if( frame->channels != nch ) break;
- aud->init_swr(frame->channels, frame->format, frame->sample_rate);
+ aud->init_swr(frame->ch_layout.nb_channels, frame->format, frame->sample_rate);
float *samples;
int len = aud->get_samples(samples,
&frame->extended_data[0], frame->nb_samples);
int channels = asset->channels;
int sample_rate = asset->sample_rate;
int64_t layout = get_channel_layout(channels);
+ AVChannelLayout ch_layout;
+ av_channel_layout_default(&ch_layout, channels);
+ if(!ch_layout.nb_channels) {
+ printf ("av_ch_layut_default failed! \n"); }
int bitrate = asset->ac3_bitrate * 1000;
av_init_packet(&avpkt);
codec_context = avcodec_alloc_context3(codec);
codec_context->bit_rate = bitrate;
codec_context->sample_rate = sample_rate;
- codec_context->channels = channels;
- codec_context->channel_layout = layout;
+ codec_context->ch_layout.nb_channels = channels;
+ codec_context->ch_layout.u.mask = layout;
+ av_channel_layout_copy(&codec_context->ch_layout, &ch_layout);
codec_context->sample_fmt = codec->sample_fmts[0];
- resample_context = swr_alloc_set_opts(NULL,
- layout, codec_context->sample_fmt, sample_rate,
- layout, AV_SAMPLE_FMT_S16, sample_rate,
+ SwrContext *tmp_resample_context = NULL;
+ int ret = swr_alloc_set_opts2(&tmp_resample_context,
+ &ch_layout, codec_context->sample_fmt, sample_rate,
+ &ch_layout, AV_SAMPLE_FMT_S16, sample_rate,
0, NULL);
- swr_init(resample_context);
+ if(ret <0) printf("swr_alloc eror: %i \n", ret );
+ if(tmp_resample_context){
+ resample_context = tmp_resample_context;
+ if(swr_init(resample_context))
+ {
+ eprintf(_("FileAC3::open_file failed to init swr.\n"));
+ result = 1;
+ }
+ }
if(avcodec_open2(codec_context, codec, 0))
{
eprintf(_("FileAC3::open_file failed to open codec.\n"));
result = 1;
}
+ av_channel_layout_uninit(&ch_layout);
}
}
AVFrame *frame = av_frame_alloc();
frame->nb_samples = frame_size;
frame->format = avctx->sample_fmt;
- frame->channel_layout = avctx->channel_layout;
+ av_channel_layout_copy(&frame->ch_layout, &avctx->ch_layout);
frame->sample_rate = avctx->sample_rate;
+
ret = av_frame_get_buffer(frame, 0);
if( ret >= 0 ) {
const uint8_t *samples = (uint8_t *)temp_raw +
ret = swr_convert(resample_context,
(uint8_t **)frame->extended_data, frame_size,
&samples, frame_size);
- }
+ } else { printf("fileAC3: av_frame_get_buffer failed!\n"); }
if( ret >= 0 ) {
frame->pts = avctx->sample_rate && avctx->time_base.num ?
file->get_audio_position() : AV_NOPTS_VALUE ;
case AV_OPT_TYPE_SAMPLE_FMT: cp = N_("<sample_fmt>"); break;
case AV_OPT_TYPE_DURATION: cp = N_("<duration>"); break;
case AV_OPT_TYPE_COLOR: cp = N_("<color>"); break;
- case AV_OPT_TYPE_CHANNEL_LAYOUT: cp = N_("<channel_layout>"); break;
+ case AV_OPT_TYPE_CHLAYOUT: cp = N_("<channel_layout>"); break;
case AV_OPT_TYPE_BOOL: cp = N_("<bool>"); break;
default: cp = N_("<undef>"); break;
}
RenderPackage* PackagingEngineDefault::get_package_single_farm(double frames_per_second,
int client_number, int use_local_rate)
{
- RenderPackage *result = 0;
- float avg_frames_per_second = preferences->get_avg_rate(use_local_rate);
- double length = package_len;
- int scaled_length = 0;
-
- if( (default_asset->audio_data &&
- (audio_position < audio_end && !EQUIV(audio_position, audio_end))) ||
- (default_asset->video_data &&
- (video_position < video_end && !EQUIV(video_position, video_end))) ) {
+
+//printf("PackageDispatcher::get_package %ld %ld %ld %ld\n", audio_position, video_position, audio_end, video_end);
+
+ RenderPackage *result = 0;
+ float avg_frames_per_second = preferences->get_avg_rate(use_local_rate);
+
+ if(audio_position < audio_end ||
+ video_position < video_end)
+ {
// Last package
- result = packages[current_package];
- result->audio_start = audio_position;
- result->video_start = video_position;
- result->video_do = default_asset->video_data;
- result->audio_do = default_asset->audio_data;
-
- if( current_package >= total_allocated-1 ) {
- result->audio_end = audio_end;
- result->video_end = video_end;
- audio_position = result->audio_end;
- video_position = result->video_end;
- }
- else {
- if( frames_per_second > 0 &&
- !EQUIV(frames_per_second, 0) && !EQUIV(avg_frames_per_second, 0) ) {
-// package size to fit the requestor.
- length *= frames_per_second / avg_frames_per_second;
- scaled_length = 1;
+ double scaled_len;
+ result = packages[current_package];
+ result->audio_start = audio_position;
+ result->video_start = video_position;
+ result->video_do = default_asset->video_data;
+ result->audio_do = default_asset->audio_data;
+
+ if(current_package >= total_allocated - 1)
+ {
+ result->audio_end = audio_end;
+ result->video_end = video_end;
+ audio_position = result->audio_end;
+ video_position = result->video_end;
}
- if( length < min_package_len )
- length = min_package_len;
- double end_position = current_position + length;
-
- if( result->video_do ) {
- int64_t video_end = end_position * default_asset->frame_rate;
- result->video_end = MIN(this->video_end, video_end);
- end_position = video_end / default_asset->frame_rate;
- }
- if( result->audio_do ) {
- int64_t audio_end = end_position * default_asset->sample_rate;
- result->audio_end = MIN(this->audio_end, audio_end);
+ else
+// No useful speed data. May get infinity for real fast jobs.
+ if(frames_per_second > 0x7fffff || frames_per_second < 0 ||
+ EQUIV(frames_per_second, 0) ||
+ EQUIV(avg_frames_per_second, 0))
+ {
+ scaled_len = MAX(package_len, min_package_len);
+
+ result->audio_end = audio_position +
+ Units::round(scaled_len * default_asset->sample_rate);
+ result->video_end = video_position +
+ Units::round(scaled_len * default_asset->frame_rate);
+
+// If we get here without any useful speed data render the whole thing.
+ if(current_package >= total_packages - 1)
+ {
+ result->audio_end = audio_end;
+ result->video_end = video_end;
+ }
+ else
+ {
+ result->audio_end = MIN(audio_end, result->audio_end);
+ result->video_end = MIN(video_end, result->video_end);
+ }
+
+ audio_position = result->audio_end;
+ video_position = result->video_end;
}
- audio_position = result->audio_end;
- video_position = result->video_end;
- current_position = end_position;
+ else
+// Useful speed data and future packages exist. Scale the
+// package size to fit the requestor.
+ {
+ scaled_len = package_len *
+ frames_per_second /
+ avg_frames_per_second;
+ scaled_len = MAX(scaled_len, min_package_len);
+
+ result->audio_end = result->audio_start +
+ Units::to_int64(scaled_len * default_asset->sample_rate);
+ result->video_end = result->video_start +
+ Units::to_int64(scaled_len * default_asset->frame_rate);
+
+ result->audio_end = MIN(audio_end, result->audio_end);
+ result->video_end = MIN(video_end, result->video_end);
+
+ audio_position = result->audio_end;
+ video_position = result->video_end;
// Package size is no longer touched between total_packages and total_allocated
- if( scaled_length && current_package < total_packages-1 ) {
- double remaining =
- result->audio_do ? (double)(audio_end - audio_position) /
- default_asset->sample_rate :
- result->video_do ? (double)(video_end - video_position) /
- default_asset->frame_rate : 0;
- if( remaining > 0 ) {
- int jobs = total_packages - current_package;
- package_len = remaining / jobs;
+ if(current_package < total_packages - 1)
+ {
+ package_len = (double)(audio_end - audio_position) /
+ (double)default_asset->sample_rate /
+ (double)(total_packages - current_package);
}
+
}
- }
- current_package++;
+ current_package++;
//printf("Dispatcher::get_package 50 %lld %lld %lld %lld\n",
// result->audio_start, result->video_start, result->audio_end, result->video_end);
- }
- return result;
+ }
+ return result;
+
}
void PackagingEngineDefault::get_package_paths(ArrayList<char*> *path_list)
case AV_OPT_TYPE_SAMPLE_FMT: cp = "<sample_fmt>"; break;
case AV_OPT_TYPE_DURATION: cp = "<duration>"; break;
case AV_OPT_TYPE_COLOR: cp = "<color>"; break;
- case AV_OPT_TYPE_CHANNEL_LAYOUT: cp = "<channel_layout>"; break;
+ case AV_OPT_TYPE_CHLAYOUT: cp = "<channel_layout>"; break;
default: cp = "<undef>"; break;
}
return sprintf(rp, "%s", cp);
bool PluginFClient::is_audio(const AVFilter *fp)
{
if( !fp->outputs ) return 0;
-#if LIBAVFILTER_VERSION_MINOR > 2 && LIBAVFILTER_VERSION_MAJOR > 7
+#if LIBAVFILTER_VERSION_MAJOR > 8
if( avfilter_filter_pad_count(fp, 1) > 1 ) return 0;
#else
if( avfilter_pad_count(fp->outputs) > 1 ) return 0;
if( !avfilter_pad_get_name(fp->outputs, 0) ) return 0;
if( avfilter_pad_get_type(fp->outputs, 0) != AVMEDIA_TYPE_AUDIO ) return 0;
if( !fp->inputs ) return 1;
-#if LIBAVFILTER_VERSION_MINOR > 2 && LIBAVFILTER_VERSION_MAJOR > 7
+#if LIBAVFILTER_VERSION_MAJOR > 8
if( avfilter_filter_pad_count(fp, 0) > 1 ) return 0;
#else
if( avfilter_pad_count(fp->inputs) > 1 ) return 0;
bool PluginFClient::is_video(const AVFilter *fp)
{
if( !fp->outputs ) return 0;
-#if LIBAVFILTER_VERSION_MINOR > 2 && LIBAVFILTER_VERSION_MAJOR > 7
+#if LIBAVFILTER_VERSION_MAJOR > 8
if( avfilter_filter_pad_count(fp, 1) > 1 ) return 0;
#else
if( avfilter_pad_count(fp->outputs) > 1 ) return 0;
if( !avfilter_pad_get_name(fp->outputs, 0) ) return 0;
if( avfilter_pad_get_type(fp->outputs, 0) != AVMEDIA_TYPE_VIDEO ) return 0;
if( !fp->inputs ) return 1;
-#if LIBAVFILTER_VERSION_MINOR > 2 && LIBAVFILTER_VERSION_MAJOR > 7
+#if LIBAVFILTER_VERSION_MAJOR > 8
if( avfilter_filter_pad_count(fp, 0) > 1 ) return 0;
#else
if( avfilter_pad_count(fp->inputs) > 1 ) return 0;
}
AVSampleFormat sample_fmt = AV_SAMPLE_FMT_FLTP;
int channels = PluginClient::total_in_buffers;
- uint64_t layout = (((uint64_t)1)<<channels) - 1;
+ //uint64_t layout = (((uint64_t)1)<<channels) - 1;
+ AVChannelLayout layout;
+
+ av_channel_layout_default(&layout, channels);
+
+ char chLayoutDescription[128];
+ av_channel_layout_describe(&layout, chLayoutDescription, sizeof(chLayoutDescription));
+
AVFilterGraph *graph = !ffilt ? 0 : ffilt->graph;
int ret = !graph ? -1 : 0;
if( ret >= 0 && ffilt->filter->inputs ) {
char args[BCTEXTLEN];
snprintf(args, sizeof(args),
- "time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x%jx",
- 1, sample_rate, sample_rate, av_get_sample_fmt_name(sample_fmt), layout);
+ "time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=%s:channels=%i",
+ 1, sample_rate, sample_rate, av_get_sample_fmt_name(sample_fmt), chLayoutDescription, channels);
ret = avfilter_graph_create_filter(&fsrc, avfilter_get_by_name("abuffer"),
"in", args, NULL, graph);
+ if(ret <0) printf("abuffer failed!\n");
}
if( ret >= 0 )
ret = avfilter_graph_create_filter(&fsink, avfilter_get_by_name("abuffersink"),
ret = av_opt_set_bin(fsink, "sample_fmts",
(uint8_t*)&sample_fmt, sizeof(sample_fmt), AV_OPT_SEARCH_CHILDREN);
if( ret >= 0 )
- ret = av_opt_set_bin(fsink, "channel_layouts",
- (uint8_t*)&layout, sizeof(layout), AV_OPT_SEARCH_CHILDREN);
+ ret = av_opt_set(fsink, "ch_layouts",
+ chLayoutDescription, AV_OPT_SEARCH_CHILDREN);
if( ret >= 0 )
ret = av_opt_set_bin(fsink, "sample_rates",
(uint8_t*)&sample_rate, sizeof(sample_rate), AV_OPT_SEARCH_CHILDREN);
if( ret >= 0 ) {
in_channels = get_inchannels();
out_channels = get_outchannels();
+#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(59,24,100)
+ AVChannelLayout in_ch_layout;
+ AVFilterContext *fctx = ffilt->fctx;
+ AVFilterLink **links = !fctx->nb_outputs ? 0 : fctx->outputs;
+ av_channel_layout_copy(&in_ch_layout, &links[0]->ch_layout);
+#endif
frame->nb_samples = size;
frame->format = AV_SAMPLE_FMT_FLTP;
+#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(61,3,100)
+ av_channel_layout_copy(&frame->ch_layout, &in_ch_layout);
+#else
frame->channel_layout = (1<<in_channels)-1;
+#endif
frame->sample_rate = sample_rate;
frame->pts = filter_position;
}
[ . ])
PKG_3RD([ffmpeg],[yes],
- [ffmpeg-6.1],
+ [ffmpeg-7.0],
[ libavutil/libavutil.a \
libavcodec/libavcodec.a \
libpostproc/libpostproc.a \
[ api ])
PKG_3RD([flac],[auto],
- [flac-1.4.2],
+ [flac-1.4.3],
[ src/libFLAC/.libs/libFLAC.a \
src/libFLAC++/.libs/libFLAC++.a \
src/share/replaygain_analysis/.libs/libreplaygain_analysis.a \
[ include ])
PKG_3RD([giflib],[yes],
- [giflib-5.2.1],
+ [giflib-5.2.2],
[ libgif.a \
libutil.a ],
[ . ])
[ include ])
PKG_3RD([openjpeg],[auto],
- [openjpeg-2.5.0],
+ [openjpeg-2.5.2],
[ bin/libopenjp2.a ],
[ src/lib/openjp2 ])
# [])
#
PKG_3RD([tiff],[auto],
- [tiff-4.6.0],
+ [tiff-4.6.0t],
[ libtiff/.libs/libtiff.a \
libtiff/.libs/libtiffxx.a \
port/.libs/libport.a ],[
[ libtwolame ])
PKG_3RD([x264],[auto],
- [x264-stable],
+ [x264-stableR3191],
[ libx264.a ],
[ . ])
[ usr/local/include ])
PKG_3RD([libsvtav1],[no],
- [libsvtav1-v1.8.0],
+ [libsvtav1-v2.2.1],
[ usr/local/lib*/libSvtAv1Enc.a ],
[ usr/local/include/svt-av1 ])
# Several important definitions
# ContextManual.pl script API version. Must not be changed !
-$cin_cm_api = 1;
+$cin_cm_api = 3;
# Web browser executable, can be redefined on user's demand
$cin_browser = $ENV{'CIN_BROWSER'};
# The node with the manual contents
$contents_node = 'Contents.html';
-# The node with the manual index
-$index_node = 'Index.html';
+# The node with the actual index if needed will be found after parsing contents
+$index_node = '';
+$index = '';
# Several special plugin names necessary to rewrite
%rewrite = (
"AgingTV" => "Aging TV",
"Brightness/Contrast" => "Brightness\\/Contrast",
"Chroma key (HSV)" => "Chroma Key \\(HSV\\)",
+ "Chroma key (Avid)" => "Chroma Key \\(Avid\\)",
"Crop & Position" => "Crop & Position",
"FindObj" => "Find Object",
"RGB - 601" => "RGB-601",
# Cinelerra HTML manual must reside here
$cin_man = "$cin_dat/doc/CinelerraGG_Manual";
$contents = $cin_man.'/'.$contents_node;
-$index = $cin_man.'/'.$index_node;
#print "ContextManual: using contents $contents\n";
# Cinelerra user's config directory
# Show index on this special request
if ($help_key eq 'IDX')
{
+ # Index node will be needed now, find it
+ if ($index_node eq '')
+ {
+ $node = '';
+ open CONTENTS, $contents or die "Cannot open $contents: $!";
+ while (<CONTENTS>)
+ {
+ chomp;
+ last if ($node) = /^\s*HREF=\"(.+?\.html)\">\s*Index\s*<\/A>(?:<\/B>)?$/;
+ }
+ close CONTENTS;
+ $index_node = $node if $node ne '';
+ $index_node = 'Index.html' if $index_node eq '';
+ $index = $cin_man.'/'.$index_node;
+ }
system "$cin_browser \"file://$index\" &";
exit 0;
}
# Show index on this special request
if ($help_key eq 'IDX')
{
+ # Index node will be needed now, find it
+ if ($index_node eq '')
+ {
+ $node = '';
+ open CONTENTS, $contents or die "Cannot open $contents: $!";
+ while (<CONTENTS>)
+ {
+ chomp;
+ last if ($node) = /^\s*HREF=\"(.+?\.html)\">\s*Index\s*<\/A>(?:<\/B>)?$/;
+ }
+ close CONTENTS;
+ $index_node = $node if $node ne '';
+ $index_node = 'Index.html' if $index_node eq '';
+ $index = $cin_man.'/'.$index_node;
+ }
system "$cin_browser \"file://$index\" &";
exit 0;
}
}
}
+# Index node will be needed now, find it
+if ($node eq '' && $index_node eq '')
+{
+ seek CONTENTS, 0, 0;
+ while (<CONTENTS>)
+ {
+ chomp;
+ last if ($node) = /^\s*HREF=\"(.+?\.html)\">\s*Index\s*<\/A>(?:<\/B>)?$/;
+ }
+ $index_node = $node if $node ne '';
+ $index_node = 'Index.html' if $index_node eq '';
+ $index = $cin_man.'/'.$index_node;
+ $node = '';
+}
+
# If not found, search index for the exact key
if ($node eq '')
{
Blue Banana
Chroma key
Chroma key (HSV)
+ Chroma key (Avid)
+ Color Swatch
CriKey
Difference key
F_chromakey
Blue Banana
Chroma key
Chroma key (HSV)
+ Chroma key (Avid)
+ Color Swatch
CriKey
Difference key
F_chromakey
Blue Banana
Chroma key
Chroma key (HSV)
+ Chroma key (Avid)
+ Color Swatch
CriKey
Difference key
F_chromakey
#bilateral_cuda ###Function not implemented
#bwdif_cuda ###Function not implemented
#colorspace_cuda ###Function not implemented
+; do not work in 7.0
+#aap ###Input/output error
+#fsync ###No such file or directory
+#tiltandshift ###Resource temporarily available
# this codec codes less than one frame per sec
# and so even a few seconds of video can take
# a very long time to encode
+# First 2 lines needed for seeks to put in keyframes
+g=30
+keyint_min=30
cpu-used 5
row-mt 1
tiles 2x2
cin_pix_fmt=yuv420p10le
# use framerate for 1 keyframe/sec, needed for seeks
keyint_min=25
-profile=high10
+#profile=high10
x264-params keyint=25
channel and transparency if there is alpha.
Chroma key (HSV): Replaces a color with another color or
transparency using HSV variables.
+Chroma key (Avid): ChromkayHSV version based on the Avid
+ SpectraMatte algorithm. First attach the Color
+ Swatch plugin to help visualize the effect.
Color 3 Way: Modify color of Shadows, Midtones, and Highlights
as you specify.
Color Balance: Modify RGB colors or white balance to compensate
for errors in video such as low lighting.
ColorSpace: Tweak colors from one color space/range to another
- space=BT601/BT709/BT2020 range=MPEG/JPEG
+ space=BT601/BT709/BT2020 range=MPEG/JPEG.
+Color Swatch: Draws the color space for saturation or brightness.
+ Provides an input to assist in ChromaKey adjustments.
CriKey: Regionally based chroma key with interpolation;
useful when you only want a specific zone defined.
.
https://cinelerra-gg.org/download/CinelerraGG_Manual.pdf
http://g-raffa.eu/Cinelerra/HOWTO/basics.html
.
+2024 July changes of note:
+ All of the AppImages are now being built with x265
+ 8/10/12 bit encode options (multibit).
+2024 June changes of note:
+ ChromaKeyAVID new plugin to use instead of ChromKeyHSV
+ with an improved algorithm based on Avid SpectraMatte.
+2024 May changes of note:
+ FFmpeg has been updated from 6.1 to 7.0.
+ ChromaKey and ChromaKeyHSV have improved menus/options.
+ Mjpegtools upgraded from 2.1.0 to 2.2.1.
2024 January changes of note:
X265 library has been updated to snapshot of 17-12-2023.
Libsndfile is now at version 1.2.2.
C41 \
chromakey \
chromakeyhsv \
+ chromakeyavid \
color3way \
colorbalance \
colorspace \
svg \
swapchannels \
swapframes \
+ swatch \
threshold \
timeavg \
timeblur \
chorus \
chromakey \
chromakeyhsv \
+ chromakeyavid \
color3way \
colorbalance \
colorspace \
svg \
swapchannels \
swapframes \
+ swatch \
synthesizer \
threshold \
timeavg \
ChromaKeyColor *color;
- ChromaKeyThreshold *threshold;
ChromaKeyFText *threshold_text;
ChromaKeyFSlider *threshold_slider;
ChromaKeyClr *threshold_Clr;
ChromaKeyUseValue *use_value;
ChromaKeyUseColorPicker *use_colorpicker;
- ChromaKeySlope *slope;
ChromaKeyFText *slope_text;
ChromaKeyFSlider *slope_slider;
ChromaKeyClr *slope_Clr;
--- /dev/null
+include ../../plugin_defs
+
+PLUGIN = chromakeyavid
+OUTPUT_BINS := $(OBJDIR)/chromakeyavid_sl.o
+OBJS := $(OBJDIR)/chromakey.o
+
+include ../../plugin_config
+
+$(OBJDIR)/chromakey.o: chromakey.C
+$(OUTPUT_BINS): chromakeyavid.sl
+ cd $(OBJDIR) && \
+ ../../../guicast/$(OBJDIR)/bootstrap -s chromakeyavid_sl.o ../chromakeyavid.sl
+
--- /dev/null
+/*
+ * CINELERRA
+ * Copyright (C) 2012-2024 Adam Williams <broadcast at earthling dot net>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+// original chromakey HSV author:
+// https://www.mail-archive.com/cinelerra@skolelinux.no/msg00379.html
+// http://jcornet.free.fr/linux/chromakey.html
+
+// He was trying to replicate the Avid SpectraMatte:
+// https://resources.avid.com/SupportFiles/attach/Symphony_Effects_and_CC_Guide_v5.5.pdf
+// page 691
+// but the problem seems to be harder than he thought
+
+// A rewrite got a slightly different spill light processing
+// but still not very useful:
+// https://github.com/vanakala/cinelerra-cve/blob/master/plugins/chromakeyhsv/chromakey.C
+
+// The current implementation is most of the Spectramatte algorithm.
+// The only way to test it is to use the color swatch plugin.
+// Fix brightness to test the saturation wedge.
+// Fix saturation to test the brightness wedge.
+
+// There are boundary artifacts caused by the color swatch showing a slice
+// of a cube.
+// If out slope > 0, constant 0% saturation or 0% brightness is all wedge.
+// If out slope == 0, constant 0% saturation or constant 0% brightness has no wedge
+// The general idea is if it's acting weird, switch between constant brightness
+// & constant saturation.
+
+// TODO: integrated color swatch & alpha blur, but it takes a lot of space in the GUI.
+
+#include "bcdisplayinfo.h"
+#include "bcsignals.h"
+#include "chromakey.h"
+#include "clip.h"
+#include "bchash.h"
+#include "filexml.h"
+#include "guicast.h"
+#include "keyframe.h"
+#include "language.h"
+#include "loadbalance.h"
+#include "playback3d.h"
+#include "bccolors.h"
+#include "pluginvclient.h"
+#include "vframe.h"
+
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+
+#define WINDOW_W xS(550)
+#define WINDOW_H yS(600)
+#define SLIDER_W xS(250)
+#define MAX_SLOPE 100.0
+#define MAX_VALUE 100.0
+
+ChromaKeyConfig::ChromaKeyConfig ()
+{
+ red = 0.0;
+ green = 1.0;
+ blue = 0.0;
+
+ min_brightness = 50.0;
+ max_brightness = 100.0;
+ tolerance = 15.0;
+ saturation_start = 0.0;
+ saturation_line = 50.0;
+
+ in_slope = 2;
+ out_slope = 2;
+ alpha_offset = 0;
+
+ spill_saturation = 0.0;
+ spill_angle = 0.0;
+ desaturate_only = 0;
+
+ show_mask = 0;
+
+ undo_red = red;
+ undo_green = green;
+ undo_blue = blue;
+ undo_min_brightness = min_brightness;
+ undo_max_brightness = max_brightness;
+ undo_tolerance = tolerance;
+ undo_saturation_start = saturation_start;
+ undo_saturation_line = saturation_line;
+ undo_in_slope = in_slope;
+ undo_out_slope = out_slope;
+ undo_alpha_offset = alpha_offset;
+ undo_spill_saturation = spill_saturation;
+ undo_spill_angle = spill_angle;
+ undo_desaturate_only = desaturate_only;
+ undo_show_mask = show_mask;
+
+ store_red = red;
+ store_green = green;
+ store_blue = blue;
+ store_min_brightness = min_brightness;
+ store_max_brightness = max_brightness;
+ store_tolerance = tolerance;
+ store_saturation_start = saturation_start;
+ store_saturation_line = saturation_line;
+ store_in_slope = in_slope;
+ store_out_slope = out_slope;
+ store_alpha_offset = alpha_offset;
+ store_spill_saturation = spill_saturation;
+ store_spill_angle = spill_angle;
+ store_desaturate_only = desaturate_only;
+ store_show_mask = show_mask;
+}
+
+void ChromaKeyConfig::reset(int clear)
+{
+ switch(clear)
+ {
+ case RESET_RGB:
+ red = 0.0;
+ green = 1.0;
+ blue = 0.0;
+ break;
+ case RESET_MIN_BRIGHTNESS:
+ min_brightness = 50.0;
+ break;
+ case RESET_MAX_BRIGHTNESS:
+ max_brightness = 100.0;
+ break;
+ case RESET_TOLERANCE:
+ tolerance = 15.0;
+ break;
+ case RESET_SATURATION_START:
+ saturation_start = 0.0;
+ break;
+ case RESET_SATURATION_LINE:
+ saturation_line = 50.0;
+ break;
+ case RESET_IN_SLOPE:
+ in_slope = 2;
+ break;
+ case RESET_OUT_SLOPE:
+ out_slope = 2;
+ break;
+ case RESET_ALPHA_OFFSET:
+ alpha_offset = 0;
+ break;
+ case RESET_SPILL_SATURATION:
+ spill_saturation = 0.0;
+ break;
+ case RESET_SPILL_ANGLE:
+ spill_angle = 0.0;
+ break;
+ case RESET_ALL:
+ case RESET_DEFAULT_SETTINGS:
+ default:
+ undo_red = red;
+ undo_green = green;
+ undo_blue = blue;
+ undo_min_brightness = min_brightness;
+ undo_max_brightness = max_brightness;
+ undo_tolerance = tolerance;
+ undo_saturation_start = saturation_start;
+ undo_saturation_line = saturation_line;
+ undo_in_slope = in_slope;
+ undo_out_slope = out_slope;
+ undo_alpha_offset = alpha_offset;
+ undo_spill_saturation = spill_saturation;
+ undo_spill_angle = spill_angle;
+ undo_desaturate_only = desaturate_only;
+ undo_show_mask = show_mask;
+
+ red = 0.0;
+ green = 1.0;
+ blue = 0.0;
+
+ min_brightness = 50.0;
+ max_brightness = 100.0;
+ tolerance = 15.0;
+ saturation_start = 0.0;
+ saturation_line = 50.0;
+
+ in_slope = 2;
+ out_slope = 2;
+ alpha_offset = 0;
+
+ spill_saturation = 0.0;
+ spill_angle = 0.0;
+ desaturate_only = 0;
+
+ show_mask = 0;
+ break;
+ }
+}
+
+
+void
+ChromaKeyConfig::copy_from (ChromaKeyConfig & src)
+{
+ red = src.red;
+ green = src.green;
+ blue = src.blue;
+ spill_saturation = src.spill_saturation;
+ spill_angle = src.spill_angle;
+ min_brightness = src.min_brightness;
+ max_brightness = src.max_brightness;
+ saturation_start = src.saturation_start;
+ saturation_line = src.saturation_line;
+ tolerance = src.tolerance;
+ in_slope = src.in_slope;
+ out_slope = src.out_slope;
+ alpha_offset = src.alpha_offset;
+ show_mask = src.show_mask;
+ desaturate_only = src.desaturate_only;
+
+ undo_red = src.red;
+ undo_green = src.green;
+ undo_blue = src.blue;
+ undo_min_brightness = src.min_brightness;
+ undo_max_brightness = src.max_brightness;
+ undo_tolerance = src.tolerance;
+ undo_saturation_start = src.saturation_start;
+ undo_saturation_line = src.saturation_line;
+ undo_in_slope = src.in_slope;
+ undo_out_slope = src.out_slope;
+ undo_alpha_offset = src.alpha_offset;
+ undo_spill_saturation = src.spill_saturation;
+ undo_spill_angle = src.spill_angle;
+ undo_desaturate_only = src.desaturate_only;
+ undo_show_mask = src.show_mask;
+}
+
+int
+ChromaKeyConfig::equivalent (ChromaKeyConfig & src)
+{
+ return (EQUIV (red, src.red) &&
+ EQUIV (green, src.green) &&
+ EQUIV (blue, src.blue) &&
+ EQUIV (spill_saturation, src.spill_saturation) &&
+ EQUIV (spill_angle, src.spill_angle) &&
+ EQUIV (min_brightness, src.min_brightness) &&
+ EQUIV (max_brightness, src.max_brightness) &&
+ EQUIV (saturation_start, src.saturation_start) &&
+ EQUIV (saturation_line, src.saturation_line) &&
+ EQUIV (tolerance, src.tolerance) &&
+ EQUIV (in_slope, src.in_slope) &&
+ EQUIV (out_slope, src.out_slope) &&
+ EQUIV (alpha_offset, src.alpha_offset) &&
+ show_mask == src.show_mask &&
+ desaturate_only == src.desaturate_only);
+}
+
+void
+ChromaKeyConfig::interpolate (ChromaKeyConfig & prev,
+ ChromaKeyConfig & next,
+ int64_t prev_frame,
+ int64_t next_frame,
+ int64_t current_frame)
+{
+ double next_scale =
+ (double) (current_frame - prev_frame) / (next_frame - prev_frame);
+ double prev_scale =
+ (double) (next_frame - current_frame) / (next_frame - prev_frame);
+
+ this->red = prev.red * prev_scale + next.red * next_scale;
+ this->green = prev.green * prev_scale + next.green * next_scale;
+ this->blue = prev.blue * prev_scale + next.blue * next_scale;
+ this->spill_saturation =
+ prev.spill_saturation * prev_scale + next.spill_saturation * next_scale;
+ this->spill_angle =
+ prev.spill_angle * prev_scale + next.tolerance * next_scale;
+ this->min_brightness =
+ prev.min_brightness * prev_scale + next.min_brightness * next_scale;
+ this->max_brightness =
+ prev.max_brightness * prev_scale + next.max_brightness * next_scale;
+ this->saturation_start =
+ prev.saturation_start * prev_scale + next.saturation_start * next_scale;
+ this->saturation_line =
+ prev.saturation_line * prev_scale + next.saturation_line * next_scale;
+ this->tolerance = prev.tolerance * prev_scale + next.tolerance * next_scale;
+ this->in_slope = prev.in_slope * prev_scale + next.in_slope * next_scale;
+ this->out_slope = prev.out_slope * prev_scale + next.out_slope * next_scale;
+ this->alpha_offset =
+ prev.alpha_offset * prev_scale + next.alpha_offset * next_scale;
+ this->show_mask = prev.show_mask;
+ this->desaturate_only = prev.desaturate_only;
+
+ this->undo_red = this->red;
+ this->undo_green = this->green;
+ this->undo_blue = this->blue;
+ this->undo_min_brightness = this->min_brightness;
+ this->undo_max_brightness = this->max_brightness;
+ this->undo_tolerance = this->tolerance;
+ this->undo_saturation_start = this->saturation_start;
+ this->undo_saturation_line = this->saturation_line;
+ this->undo_in_slope = this->in_slope;
+ this->undo_out_slope = this->out_slope;
+ this->undo_alpha_offset = this->alpha_offset;
+ this->undo_spill_saturation = this->spill_saturation;
+ this->undo_spill_angle = this->spill_angle;
+ this->undo_desaturate_only = this->desaturate_only;
+ this->undo_show_mask = this->show_mask;
+}
+
+int
+ChromaKeyConfig::get_color ()
+{
+ int red = (int) (CLIP (this->red, 0, 1) * 0xff);
+ int green = (int) (CLIP (this->green, 0, 1) * 0xff);
+ int blue = (int) (CLIP (this->blue, 0, 1) * 0xff);
+ return (red << 16) | (green << 8) | blue;
+}
+
+
+
+ChromaKeyWindow::ChromaKeyWindow (ChromaKeyAvid * plugin)
+ : PluginClientWindow(plugin,
+ WINDOW_W,
+ WINDOW_H,
+ WINDOW_W,
+ WINDOW_H,
+ 0)
+{
+ this->plugin = plugin;
+ color_thread = 0;
+}
+
+ChromaKeyWindow::~ChromaKeyWindow ()
+{
+ delete color_thread;
+}
+
+void ChromaKeyWindow::create_objects ()
+{
+ int xs10 = xS(10), xs20 = xS(20), xs100 = xS(100), wslid = SLIDER_W;
+ int ys10 = yS(10), ys20 = yS(20), ys30 = yS(30), ys40 = yS(40), ys50 = yS(50);
+ int y = ys10, x2 = xS(160), x3 = xS(260), xs5 = xS(5);
+ int x = xs10;
+ int clr_x = get_w()-x - xS(22); // note: clrBtn_w = 22
+
+ BC_Title *title;
+ BC_Bar *bar;
+ BC_TitleBar *title_bar;
+
+// Color section
+ add_subwindow (title_bar =
+ new BC_TitleBar(x, y, get_w()-2*x, xs20, xs10, _("Color")));
+ y += ys20;
+
+ add_subwindow (color = new ChromaKeyColor (plugin, this, x, y));
+ // Info for Sample rectangle: x_slider w_slider w_sample
+ // \ | / y, w, h
+ add_subwindow (sample = new BC_SubWindow (x3 + wslid - xs100, y, xs100, ys50));
+ y += ys30;
+ add_subwindow (use_colorpicker = new ChromaKeyUseColorPicker (plugin, this, x, y));
+ y += ys30;
+ add_subwindow (show_mask =
+ new ChromaKeyToggle (plugin, x, y, &plugin->config.show_mask,
+ _("Show Mask")));
+
+// Key parameters section
+ y += ys30;
+ add_subwindow (title_bar =
+ new BC_TitleBar (x, y, get_w()-2*x, xs20, xs10,
+ _("Key parameters")));
+ y += ys20;
+ add_subwindow (title = new BC_Title (x, y, _("Hue Tolerance:")));
+ tolerance_text = new ChromaKeyText (plugin, this, 0, x+x2, y, 0, MAX_VALUE,
+ &plugin->config.tolerance);
+ tolerance_text->create_objects();
+ tolerance = new ChromaKeySlider (plugin, tolerance_text, x3, y, wslid,
+ 0, MAX_VALUE, &plugin->config.tolerance);
+ add_subwindow (tolerance);
+ tolerance_text->slider = tolerance;
+ clr_x = x3 + tolerance->get_w() + x;
+ add_subwindow (tolerance_clr =
+ new ChromaKeyClr (plugin, this, clr_x, y, RESET_TOLERANCE));
+ y += ys30;
+
+ add_subwindow (title = new BC_Title (x, y, _("Min. Brightness:")));
+ min_brightness_text = new ChromaKeyText (plugin, this, 0, x+x2, y,
+ 0, MAX_VALUE,
+ &plugin->config.min_brightness);
+ min_brightness_text->create_objects();
+ min_brightness = new ChromaKeySlider (plugin, min_brightness_text,
+ x3, y, wslid, 0, MAX_VALUE,
+ &plugin->config.min_brightness);
+ add_subwindow(min_brightness);
+ min_brightness_text->slider = min_brightness;
+ add_subwindow (min_brightness_clr =
+ new ChromaKeyClr (plugin, this, clr_x, y,
+ RESET_MIN_BRIGHTNESS));
+ y += ys30;
+
+ add_subwindow (title = new BC_Title (x, y, _("Max. Brightness:")));
+ max_brightness_text = new ChromaKeyText (plugin, this, 0, x+x2, y,
+ 0, MAX_VALUE,
+ &plugin->config.max_brightness);
+ max_brightness_text->create_objects();
+ max_brightness = new ChromaKeySlider (plugin, max_brightness_text,
+ x3, y, wslid, 0, MAX_VALUE,
+ &plugin->config.max_brightness);
+ add_subwindow(max_brightness);
+ max_brightness_text->slider = max_brightness;
+ add_subwindow (max_brightness_clr =
+ new ChromaKeyClr (plugin, this, clr_x, y,
+ RESET_MAX_BRIGHTNESS));
+ y += ys30;
+
+ add_subwindow (title = new BC_Title (x, y, _("Saturation Start:")));
+ saturation_start_text = new ChromaKeyText (plugin, this, 0, x+x2, y,
+ 0, MAX_VALUE,
+ &plugin->config.saturation_start);
+ saturation_start_text->create_objects();
+ saturation_start = new ChromaKeySlider (plugin, saturation_start_text,
+ x3, y, wslid, 0, MAX_VALUE,
+ &plugin->config.saturation_start);
+ add_subwindow(saturation_start);
+ saturation_start_text->slider = saturation_start;
+ add_subwindow (saturation_start_clr =
+ new ChromaKeyClr (plugin, this, clr_x, y,
+ RESET_SATURATION_START));
+ y += ys30;
+
+ add_subwindow (title = new BC_Title (x, y, _("Saturation Line:")));
+ saturation_line_text = new ChromaKeyText (plugin, this, 0, x+x2, y,
+ 0, MAX_VALUE,
+ &plugin->config.saturation_line);
+ saturation_line_text->create_objects();
+ saturation_line = new ChromaKeySlider (plugin, saturation_line_text,
+ x3, y, wslid, 0, MAX_VALUE,
+ &plugin->config.saturation_line);
+ add_subwindow(saturation_line);
+ saturation_line_text->slider = saturation_line;
+ add_subwindow (saturation_line_clr =
+ new ChromaKeyClr (plugin, this, clr_x, y,
+ RESET_SATURATION_LINE));
+ y += ys40;
+
+// Mask tweaking section
+ add_subwindow (title_bar =
+ new BC_TitleBar (x, y, get_w()-2*x, xs20, xs10,
+ _("Mask tweaking")));
+ y += ys20;
+ add_subwindow (title = new BC_Title (x, y, _("In Slope:")));
+ in_slope_text = new ChromaKeyText (plugin, this, 0, x+x2, y, 0, MAX_SLOPE,
+ &plugin->config.in_slope);
+ in_slope_text->create_objects();
+ in_slope = new ChromaKeySlider (plugin, in_slope_text, x3, y, wslid,
+ 0, MAX_SLOPE, &plugin->config.in_slope);
+ add_subwindow(in_slope);
+ in_slope_text->slider = in_slope;
+ add_subwindow (in_slope_clr =
+ new ChromaKeyClr (plugin, this, clr_x, y, RESET_IN_SLOPE));
+ y += ys30;
+
+ add_subwindow (title = new BC_Title (x, y, _("Out Slope:")));
+ out_slope_text = new ChromaKeyText (plugin, this, 0, x+x2, y, 0, MAX_SLOPE,
+ &plugin->config.out_slope);
+ out_slope_text->create_objects();
+ out_slope = new ChromaKeySlider (plugin, out_slope_text, x3, y, wslid,
+ 0, MAX_SLOPE, &plugin->config.out_slope);
+ add_subwindow(out_slope);
+ out_slope_text->slider = out_slope;
+ add_subwindow (out_slope_clr =
+ new ChromaKeyClr (plugin, this, clr_x, y, RESET_OUT_SLOPE));
+ y += ys30;
+
+ add_subwindow (title = new BC_Title (x, y, _("Alpha Offset:")));
+ alpha_offset_text = new ChromaKeyText (plugin, this, 0, x+x2, y,
+ -MAX_VALUE, MAX_VALUE,
+ &plugin->config.alpha_offset);
+ alpha_offset_text->create_objects();
+ alpha_offset = new ChromaKeySlider (plugin, alpha_offset_text, x3, y, wslid,
+ -MAX_VALUE, MAX_VALUE,
+ &plugin->config.alpha_offset);
+ add_subwindow(alpha_offset);
+ alpha_offset_text->slider = alpha_offset;
+ add_subwindow (alpha_offset_clr =
+ new ChromaKeyClr (plugin, this, clr_x, y, RESET_ALPHA_OFFSET));
+ y += ys40;
+
+// Spill light control section
+ add_subwindow (title_bar =
+ new BC_TitleBar (x, y, get_w()-2*x, xs20, xs10,
+ _("Spill light control")));
+ y += ys20;
+ add_subwindow (title = new BC_Title (x, y, _("Spill Saturation:")));
+ spill_saturation_text = new ChromaKeyText (plugin, this, 0, x+x2, y,
+ 0, MAX_VALUE,
+ &plugin->config.spill_saturation);
+ spill_saturation_text->create_objects();
+ spill_saturation = new ChromaKeySlider (plugin, spill_saturation_text,
+ x3, y, wslid, 0, MAX_VALUE,
+ &plugin->config.spill_saturation);
+ add_subwindow(spill_saturation);
+ spill_saturation_text->slider = spill_saturation;
+ add_subwindow (spill_saturation_clr =
+ new ChromaKeyClr (plugin, this, clr_x, y,
+ RESET_SPILL_SATURATION));
+ y += ys30;
+
+ add_subwindow (title = new BC_Title (x, y, _("Spill Angle:")));
+ spill_angle_text = new ChromaKeyText (plugin, this, 0, x+x2, y, 0, MAX_VALUE,
+ &plugin->config.spill_angle);
+ spill_angle_text->create_objects();
+ spill_angle = new ChromaKeySlider (plugin, spill_angle_text, x3, y, wslid,
+ 0, MAX_VALUE, &plugin->config.spill_angle);
+ add_subwindow(spill_angle);
+ spill_angle_text->slider = spill_angle;
+ add_subwindow (spill_angle_clr =
+ new ChromaKeyClr (plugin, this, clr_x, y, RESET_SPILL_ANGLE));
+ y += ys30;
+
+ add_subwindow(desaturate_only =
+ new ChromaKeyToggle(plugin, x, y,
+ &plugin->config.desaturate_only,
+ _("Desaturate Only")));
+ y += ys40;
+
+// Global buttons section
+ add_subwindow (bar = new BC_Bar(x, y, get_w()-2*x));
+ y += ys20;
+ add_subwindow (store = new ChromaKeyStore (plugin, this, x, y));
+ x += store->get_w() + xs5;
+ add_subwindow (recall = new ChromaKeyRecall (plugin, this, x, y));
+ x += recall->get_w() + xs5;
+ add_subwindow (exchange = new ChromaKeyExchange (plugin, this, x, y));
+ x += exchange->get_w() + xs5;
+ add_subwindow (undo = new ChromaKeyUndo (plugin, this, x, y));
+ x += undo->get_w() + xs5;
+ add_subwindow (reset = new ChromaKeyReset (plugin, this, x, y));
+
+ color_thread = new ChromaKeyColorThread (plugin, this);
+
+ update_sample ();
+ show_window ();
+}
+
+void
+ChromaKeyWindow::update_sample ()
+{
+ sample->set_color (plugin->config.get_color ());
+ sample->draw_box (0, 0, sample->get_w (), sample->get_h ());
+ sample->set_color (BLACK);
+ sample->draw_rectangle (0, 0, sample->get_w (), sample->get_h ());
+ sample->flash ();
+}
+
+void ChromaKeyWindow::done_event(int result)
+{
+ color_thread->close_window();
+}
+
+
+ChromaKeyColor::ChromaKeyColor (ChromaKeyAvid * plugin,
+ ChromaKeyWindow * gui, int x, int y)
+ : BC_GenericButton (x, y, _("Color..."))
+{
+ this->plugin = plugin;
+ this->gui = gui;
+}
+
+int
+ChromaKeyColor::handle_event ()
+{
+ gui->color_thread->start_window (plugin->config.get_color (), 0xff);
+ return 1;
+}
+
+
+
+ChromaKeyText::ChromaKeyText(ChromaKeyAvid *plugin, ChromaKeyWindow *gui,
+ ChromaKeySlider *slider, int x, int y,
+ float min, float max, float *output)
+ : BC_TumbleTextBox(gui, *output, min, max, x, y, xS(60), 2)
+{
+ this->plugin = plugin;
+ this->gui = gui;
+ this->slider = slider;
+ this->min = min;
+ this->max = max;
+ this->output = output;
+ set_increment(0.01);
+}
+
+ChromaKeyText::~ChromaKeyText()
+{
+}
+
+int ChromaKeyText::handle_event()
+{
+ *output = atof(get_text());
+ if(*output > max) *output = max;
+ if(*output < min) *output = min;
+ slider->update(*output);
+ plugin->send_configure_change();
+ return 1;
+}
+
+ChromaKeySlider::ChromaKeySlider(ChromaKeyAvid *plugin, ChromaKeyText *text,
+ int x, int y, int w,
+ float min, float max, float *output)
+ : BC_FSlider(x, y, 0, w, w, min, max, *output)
+{
+ this->plugin = plugin;
+ this->text = text;
+ this->output = output;
+ set_precision(0.01);
+ enable_show_value(0); // Hide caption
+}
+
+ChromaKeySlider::~ChromaKeySlider()
+{
+}
+
+int ChromaKeySlider::handle_event()
+{
+ *output = get_value();
+ text->update(*output);
+ plugin->send_configure_change();
+ return 1;
+}
+
+ChromaKeyClr::ChromaKeyClr(ChromaKeyAvid *plugin, ChromaKeyWindow *gui,
+ int x, int y, int clear)
+ : BC_Button(x, y, plugin->get_theme()->get_image_set("reset_button"))
+{
+ this->plugin = plugin;
+ this->gui = gui;
+ this->clear = clear;
+}
+
+ChromaKeyClr::~ChromaKeyClr()
+{
+}
+
+int ChromaKeyClr::handle_event()
+{
+ plugin->config.reset(clear);
+ gui->update_gui(clear);
+ plugin->send_configure_change();
+ return 1;
+}
+
+ChromaKeyToggle::ChromaKeyToggle (ChromaKeyAvid * plugin,
+ int x,
+ int y,
+ bool *output,
+ const char *caption)
+ : BC_CheckBox (x,
+ y,
+ *output ? 1 : 0,
+ caption)
+{
+ this->plugin = plugin;
+ this->output = output;
+}
+
+int
+ChromaKeyToggle::handle_event ()
+{
+ *output = get_value() ? true : false;
+ plugin->send_configure_change ();
+ return 1;
+}
+
+
+ChromaKeyReset::ChromaKeyReset (ChromaKeyAvid *plugin, ChromaKeyWindow *gui,
+ int x, int y)
+ : BC_GenericButton(x, y, _("Reset"))
+{
+ this->plugin = plugin;
+ this->gui = gui;
+}
+
+int ChromaKeyReset::handle_event ()
+{
+ plugin->config.reset(RESET_DEFAULT_SETTINGS);
+ gui->update_gui(RESET_DEFAULT_SETTINGS);
+ plugin->send_configure_change();
+ return 1;
+}
+
+ChromaKeyStore::ChromaKeyStore (ChromaKeyAvid *plugin, ChromaKeyWindow *gui,
+ int x, int y)
+ : BC_GenericButton(x, y, _("Store"))
+{
+ this->plugin = plugin;
+ this->gui = gui;
+}
+
+int ChromaKeyStore::handle_event ()
+{
+ plugin->config.store_red = plugin->config.red;
+ plugin->config.store_green = plugin->config.green;
+ plugin->config.store_blue = plugin->config.blue;
+ plugin->config.store_min_brightness = plugin->config.min_brightness;
+ plugin->config.store_max_brightness = plugin->config.max_brightness;
+ plugin->config.store_tolerance = plugin->config.tolerance;
+ plugin->config.store_saturation_start = plugin->config.saturation_start;
+ plugin->config.store_saturation_line = plugin->config.saturation_line;
+ plugin->config.store_in_slope = plugin->config.in_slope;
+ plugin->config.store_out_slope = plugin->config.out_slope;
+ plugin->config.store_alpha_offset = plugin->config.alpha_offset;
+ plugin->config.store_spill_saturation = plugin->config.spill_saturation;
+ plugin->config.store_spill_angle = plugin->config.spill_angle;
+ plugin->config.store_desaturate_only = plugin->config.desaturate_only;
+ plugin->config.store_show_mask = plugin->config.show_mask;
+ return 1;
+}
+
+ChromaKeyRecall::ChromaKeyRecall (ChromaKeyAvid *plugin, ChromaKeyWindow *gui,
+ int x, int y)
+ : BC_GenericButton(x, y, _("Recall"))
+{
+ this->plugin = plugin;
+ this->gui = gui;
+}
+
+int ChromaKeyRecall::handle_event ()
+{
+ plugin->config.undo_red = plugin->config.red;
+ plugin->config.undo_green = plugin->config.green;
+ plugin->config.undo_blue = plugin->config.blue;
+ plugin->config.undo_min_brightness = plugin->config.min_brightness;
+ plugin->config.undo_max_brightness = plugin->config.max_brightness;
+ plugin->config.undo_tolerance = plugin->config.tolerance;
+ plugin->config.undo_saturation_start = plugin->config.saturation_start;
+ plugin->config.undo_saturation_line = plugin->config.saturation_line;
+ plugin->config.undo_in_slope = plugin->config.in_slope;
+ plugin->config.undo_out_slope = plugin->config.out_slope;
+ plugin->config.undo_alpha_offset = plugin->config.alpha_offset;
+ plugin->config.undo_spill_saturation = plugin->config.spill_saturation;
+ plugin->config.undo_spill_angle = plugin->config.spill_angle;
+ plugin->config.undo_desaturate_only = plugin->config.desaturate_only;
+ plugin->config.undo_show_mask = plugin->config.show_mask;
+
+ plugin->config.red = plugin->config.store_red;
+ plugin->config.green = plugin->config.store_green;
+ plugin->config.blue = plugin->config.store_blue;
+ plugin->config.min_brightness = plugin->config.store_min_brightness;
+ plugin->config.max_brightness = plugin->config.store_max_brightness;
+ plugin->config.tolerance = plugin->config.store_tolerance;
+ plugin->config.saturation_start = plugin->config.store_saturation_start;
+ plugin->config.saturation_line = plugin->config.store_saturation_line;
+ plugin->config.in_slope = plugin->config.store_in_slope;
+ plugin->config.out_slope = plugin->config.store_out_slope;
+ plugin->config.alpha_offset = plugin->config.store_alpha_offset;
+ plugin->config.spill_saturation = plugin->config.store_spill_saturation;
+ plugin->config.spill_angle = plugin->config.store_spill_angle;
+ plugin->config.desaturate_only = plugin->config.store_desaturate_only;
+ plugin->config.show_mask = plugin->config.store_show_mask;
+
+ gui->update_gui(RESET_DEFAULT_SETTINGS);
+ plugin->send_configure_change();
+ return 1;
+}
+
+ChromaKeyExchange::ChromaKeyExchange (ChromaKeyAvid *plugin,
+ ChromaKeyWindow *gui, int x, int y)
+ : BC_GenericButton(x, y, _("Exchange"))
+{
+ this->plugin = plugin;
+ this->gui = gui;
+}
+
+#define CHROMAKEY_SWAP(what,typ,elem) \
+tmp_##typ = plugin->config.what##_##elem; \
+plugin->config.what##_##elem = plugin->config.elem; \
+plugin->config.elem = tmp_##typ;
+
+int ChromaKeyExchange::handle_event ()
+{
+ float tmp_flt;
+ bool tmp_bool;
+
+ plugin->config.undo_red = plugin->config.red;
+ plugin->config.undo_green = plugin->config.green;
+ plugin->config.undo_blue = plugin->config.blue;
+ plugin->config.undo_min_brightness = plugin->config.min_brightness;
+ plugin->config.undo_max_brightness = plugin->config.max_brightness;
+ plugin->config.undo_tolerance = plugin->config.tolerance;
+ plugin->config.undo_saturation_start = plugin->config.saturation_start;
+ plugin->config.undo_saturation_line = plugin->config.saturation_line;
+ plugin->config.undo_in_slope = plugin->config.in_slope;
+ plugin->config.undo_out_slope = plugin->config.out_slope;
+ plugin->config.undo_alpha_offset = plugin->config.alpha_offset;
+ plugin->config.undo_spill_saturation = plugin->config.spill_saturation;
+ plugin->config.undo_spill_angle = plugin->config.spill_angle;
+ plugin->config.undo_desaturate_only = plugin->config.desaturate_only;
+ plugin->config.undo_show_mask = plugin->config.show_mask;
+
+ CHROMAKEY_SWAP (store, flt, red)
+ CHROMAKEY_SWAP (store, flt, green)
+ CHROMAKEY_SWAP (store, flt, blue)
+ CHROMAKEY_SWAP (store, flt, min_brightness)
+ CHROMAKEY_SWAP (store, flt, max_brightness)
+ CHROMAKEY_SWAP (store, flt, tolerance)
+ CHROMAKEY_SWAP (store, flt, saturation_start)
+ CHROMAKEY_SWAP (store, flt, saturation_line)
+ CHROMAKEY_SWAP (store, flt, in_slope)
+ CHROMAKEY_SWAP (store, flt, out_slope)
+ CHROMAKEY_SWAP (store, flt, alpha_offset)
+ CHROMAKEY_SWAP (store, flt, spill_saturation)
+ CHROMAKEY_SWAP (store, flt, spill_angle)
+ CHROMAKEY_SWAP (store, bool, desaturate_only)
+ CHROMAKEY_SWAP (store, bool, show_mask)
+
+ gui->update_gui(RESET_DEFAULT_SETTINGS);
+ plugin->send_configure_change();
+ return 1;
+}
+
+ChromaKeyUndo::ChromaKeyUndo (ChromaKeyAvid *plugin, ChromaKeyWindow *gui,
+ int x, int y)
+ : BC_GenericButton(x, y, _("Undo"))
+{
+ this->plugin = plugin;
+ this->gui = gui;
+}
+
+int ChromaKeyUndo::handle_event ()
+{
+ //float tmp_flt;
+ //bool tmp_bool;
+
+ //CHROMAKEY_SWAP (undo, flt, red)
+ //CHROMAKEY_SWAP (undo, flt, green)
+ //CHROMAKEY_SWAP (undo, flt, blue)
+ //CHROMAKEY_SWAP (undo, flt, min_brightness)
+ //CHROMAKEY_SWAP (undo, flt, max_brightness)
+ //CHROMAKEY_SWAP (undo, flt, tolerance)
+ //CHROMAKEY_SWAP (undo, flt, saturation_start)
+ //CHROMAKEY_SWAP (undo, flt, saturation_line)
+ //CHROMAKEY_SWAP (undo, flt, in_slope)
+ //CHROMAKEY_SWAP (undo, flt, out_slope)
+ //CHROMAKEY_SWAP (undo, flt, alpha_offset)
+ //CHROMAKEY_SWAP (undo, flt, spill_saturation)
+ //CHROMAKEY_SWAP (undo, flt, spill_angle)
+ //CHROMAKEY_SWAP (undo, bool, desaturate_only)
+ //CHROMAKEY_SWAP (undo, bool, show_mask)
+
+ // better not to swap but restore undo values
+
+ plugin->config.red = plugin->config.undo_red;
+ plugin->config.green = plugin->config.undo_green;
+ plugin->config.blue = plugin->config.undo_blue;
+ plugin->config.min_brightness = plugin->config.undo_min_brightness;
+ plugin->config.max_brightness = plugin->config.undo_max_brightness;
+ plugin->config.tolerance = plugin->config.undo_tolerance;
+ plugin->config.saturation_start = plugin->config.undo_saturation_start;
+ plugin->config.saturation_line = plugin->config.undo_saturation_line;
+ plugin->config.in_slope = plugin->config.undo_in_slope;
+ plugin->config.out_slope = plugin->config.undo_out_slope;
+ plugin->config.alpha_offset = plugin->config.undo_alpha_offset;
+ plugin->config.spill_saturation = plugin->config.undo_spill_saturation;
+ plugin->config.spill_angle = plugin->config.undo_spill_angle;
+ plugin->config.desaturate_only = plugin->config.undo_desaturate_only;
+ plugin->config.show_mask = plugin->config.undo_show_mask;
+
+ gui->update_gui(RESET_DEFAULT_SETTINGS);
+ plugin->send_configure_change();
+ return 1;
+}
+
+ChromaKeyUseColorPicker::ChromaKeyUseColorPicker (ChromaKeyAvid * plugin,
+ ChromaKeyWindow * gui,
+ int x, int y)
+ : BC_GenericButton (x, y, _("Use color picker"))
+{
+ this->plugin = plugin;
+ this->gui = gui;
+}
+
+int
+ChromaKeyUseColorPicker::handle_event ()
+{
+ plugin->config.red = plugin->get_red ();
+ plugin->config.green = plugin->get_green ();
+ plugin->config.blue = plugin->get_blue ();
+
+ gui->update_sample ();
+
+ plugin->send_configure_change ();
+ return 1;
+}
+
+ChromaKeyColorThread::ChromaKeyColorThread (ChromaKeyAvid * plugin,
+ ChromaKeyWindow * gui)
+ : ColorPicker (0, _("Color"))
+{
+ this->plugin = plugin;
+ this->gui = gui;
+}
+
+int
+ChromaKeyColorThread::handle_new_color (int output, int alpha)
+{
+ plugin->config.red = (float) (output & 0xff0000) / 0xff0000;
+ plugin->config.green = (float) (output & 0xff00) / 0xff00;
+ plugin->config.blue = (float) (output & 0xff) / 0xff;
+
+ get_gui()->unlock_window();
+ gui->lock_window("ChromaKeyColorThread::handle_new_color");
+ gui->update_sample();
+ gui->unlock_window();
+ get_gui()->lock_window("ChromaKeyColorThread::handle_new_color");
+
+ plugin->send_configure_change ();
+ return 1;
+}
+
+
+
+ChromaKeyServer::ChromaKeyServer (ChromaKeyAvid * plugin)
+ : LoadServer (plugin->PluginClient::smp + 1,
+ plugin->PluginClient::smp + 1)
+// DEBUG
+// : LoadServer (1, 1)
+{
+ this->plugin = plugin;
+}
+
+void
+ChromaKeyServer::init_packages ()
+{
+ for (int i = 0; i < get_total_packages (); i++)
+ {
+ ChromaKeyPackage *pkg = (ChromaKeyPackage *) get_package (i);
+ pkg->y1 = plugin->input->get_h () * i / get_total_packages ();
+ pkg->y2 = plugin->input->get_h () * (i + 1) / get_total_packages ();
+ }
+}
+
+LoadClient *
+ChromaKeyServer::new_client ()
+{
+ return new ChromaKeyUnit (plugin, this);
+}
+
+LoadPackage *
+ChromaKeyServer::new_package ()
+{
+ return new ChromaKeyPackage;
+}
+
+ChromaKeyPackage::ChromaKeyPackage ():LoadPackage ()
+{
+}
+
+ChromaKeyUnit::ChromaKeyUnit (ChromaKeyAvid * plugin, ChromaKeyServer * server)
+ : LoadClient (server)
+{
+ this->plugin = plugin;
+}
+
+
+
+#define ABS(a) (((a) < 0) ? -(a) : (a))
+
+
+
+// Compute the same values in the opengl version
+#define COMMON_VARIABLES \
+ float red = plugin->config.red; \
+ float green = plugin->config.green; \
+ float blue = plugin->config.blue; \
+ \
+ float in_slope = plugin->config.in_slope / MAX_SLOPE; \
+ float out_slope = plugin->config.out_slope / MAX_SLOPE; \
+ \
+/* Convert RGB key to HSV key */ \
+ float hue_key, saturation_key, value_key; \
+ HSV::rgb_to_hsv(red, \
+ green, \
+ blue, \
+ hue_key, \
+ saturation_key, \
+ value_key); \
+ \
+/* hue range */ \
+ float tolerance = plugin->config.tolerance * 180 / MAX_VALUE; \
+ float tolerance_in = tolerance - in_slope * 180; \
+ tolerance_in = MAX(tolerance_in, 0); \
+ float tolerance_out = tolerance + out_slope * 180; \
+ tolerance_out = MIN(tolerance_out, 180); \
+ \
+/* distance of wedge point from center */ \
+ float sat_distance = plugin->config.saturation_start / MAX_VALUE; \
+/* XY shift of input color to get wedge point */ \
+ float sat_x = -cos(TO_RAD(hue_key)) * sat_distance; \
+ float sat_y = -sin(TO_RAD(hue_key)) * sat_distance; \
+/* minimum saturation after wedge point */ \
+ float min_s = plugin->config.saturation_line / MAX_VALUE; \
+ float min_s_in = min_s + in_slope; \
+ float min_s_out = min_s - out_slope; \
+ \
+ float min_v = plugin->config.min_brightness / MAX_VALUE; \
+ float min_v_in = min_v + in_slope; \
+ float min_v_out = min_v - out_slope; \
+/* ignore min_brightness 0 */ \
+ if(plugin->config.min_brightness == 0) min_v_in = 0; \
+ \
+ float max_v = plugin->config.max_brightness / MAX_VALUE; \
+ float max_v_in = max_v - in_slope; \
+ float max_v_out = max_v + out_slope; \
+/* handle wedge boundaries crossing over */ \
+ if(max_v_in < min_v_in) max_v_in = min_v_in = (max_v_in + min_v_in) / 2; \
+/* ignore max_brightness 100% */ \
+ if(plugin->config.max_brightness == MAX_VALUE) max_v_in = 1.0; \
+ \
+/* distance of spill wedge point from center */ \
+ float spill_distance = sat_distance * (1.0 - plugin->config.spill_saturation / MAX_VALUE); \
+/* XY shift of input color to get spill wedge point */ \
+ float spill_x = -cos(TO_RAD(hue_key)) * spill_distance; \
+ float spill_y = -sin(TO_RAD(hue_key)) * spill_distance; \
+/* tolerance of the spill wedge */ \
+ float spill_tolerance = tolerance + plugin->config.spill_angle * 90.0 / MAX_VALUE; \
+/* borders of the spill wedge */ \
+ float min_h = hue_key - spill_tolerance; \
+ float max_h = hue_key + spill_tolerance; \
+ int desaturate_only = plugin->config.desaturate_only; \
+ \
+ float alpha_offset = plugin->config.alpha_offset / MAX_VALUE;
+
+// shortest distance between 2 hues
+static float hue_distance(float h1, float h2)
+{
+ float result = h1 - h2;
+ if(result < -180) result += 360;
+ else if(result > 180) result -= 360;
+ return result;
+}
+
+// shift H & S based on an X & Y offset
+static void shift_hs(float &h_shifted,
+ float &s_shifted,
+ float h,
+ float s,
+ float x_offset,
+ float y_offset)
+{
+ float h_rad = TO_RAD(h);
+ float x = cos(h_rad) * s;
+ float y = sin(h_rad) * s;
+ x += x_offset;
+ y += y_offset;
+ h_shifted = TO_DEG(atan2(y, x));
+ s_shifted = hypot(x, y);
+}
+
+template <typename component_type>
+void ChromaKeyUnit::process_chromakey(int components,
+ component_type max,
+ bool use_yuv,
+ ChromaKeyPackage *pkg)
+{
+ COMMON_VARIABLES
+
+ int w = plugin->input->get_w();
+
+// printf("ChromaKeyUnit::process_chromakey %d hue_key=%f min_h=%f max_h=%f\n",
+// __LINE__,
+// hue_key,
+// min_h,
+// max_h);
+// printf("ChromaKeyUnit::process_chromakey %d tolerance_in=%f tolerance=%f tolerance_out=%f\n",
+// __LINE__,
+// tolerance_in,
+// tolerance,
+// tolerance_out);
+// printf("ChromaKeyUnit::process_chromakey %d min_s_in=%f min_s=%f min_s_out=%f\n",
+// __LINE__,
+// min_s_in,
+// min_s,
+// min_s_out);
+// printf("ChromaKeyUnit::process_chromakey %d min_v_in=%f min_v=%f min_v_out=%f\n",
+// __LINE__,
+// min_v_in,
+// min_v,
+// min_v_out);
+// printf("ChromaKeyUnit::process_chromakey %d max_v_in=%f max_v=%f max_v_out=%f\n",
+// __LINE__,
+// max_v_in,
+// max_v,
+// max_v_out);
+
+ for (int i = pkg->y1; i < pkg->y2; i++)
+ {
+ component_type *row = (component_type *) plugin->input->get_rows ()[i];
+
+ for (int j = 0; j < w; j++)
+ {
+ float r, g, b, a = 1;
+ if (!use_yuv)
+ {
+ r = (float) row[0] / max;
+ g = (float) row[1] / max;
+ b = (float) row[2] / max;
+ }
+ else /* Convert pixel to RGB float */
+ YUV::yuv.yuv_to_rgb_f (r, g, b, row[0], row[1], row[2]);
+
+// the input color
+ float h, s, v;
+
+// alpha contribution of each component of the HSV space
+ float ah = 1, as = 1, av = 1;
+
+ HSV::rgb_to_hsv (r, g, b, h, s, v);
+
+// shift the input color in XY to get the wedge point
+ float h_shifted, s_shifted;
+ if(!EQUIV(plugin->config.saturation_start, 0))
+ {
+ shift_hs(h_shifted,
+ s_shifted,
+ h,
+ s,
+ sat_x,
+ sat_y);
+ }
+ else
+ {
+ h_shifted = h;
+ s_shifted = s;
+ }
+
+/* Get the difference between the shifted input color & the unshifted wedge */
+ float h_diff = hue_distance(h_shifted, hue_key);
+ h_diff = ABS(h_diff);
+
+// alpha contribution from hue difference
+// outside wedge < tolerance_out < tolerance_in < inside wedge < tolerance_in < tolerance_out < outside wedge
+ if (tolerance_out > 0)
+ {
+// completely inside the wedge
+ if (h_diff < tolerance_in)
+ ah = 0;
+ else
+// between the outer & inner slope
+ if(h_diff < tolerance_out)
+ ah = (h_diff - tolerance_in) / (tolerance_out - tolerance_in);
+ if(ah > 1) ah = 1;
+ }
+
+// alpha contribution from saturation
+// outside wedge < min_s_out < min_s_in < inside wedge
+ if(s_shifted > min_s_out)
+ {
+// saturation with offset applied
+// completely inside the wedge
+ if(s_shifted > min_s_in)
+ as = 0;
+// inside the gradient
+ if(s_shifted >= min_s_out)
+ as = (min_s_in - s_shifted) / (min_s_in - min_s_out);
+ }
+
+// alpha contribution from brightness range
+// brightness range is defined by 4 in/out variables
+// outside wedge < min_v_out < min_v_in < inside wedge < max_v_in < max_v_out < outside wedge
+ if(v > min_v_out)
+ {
+ if(v < min_v_in || max_v_in >= 1.0)
+ av = (min_v_in - v) / (min_v_in - min_v_out);
+ else if(v <= max_v_in)
+ av = 0;
+ else if(v <= max_v_out)
+ av = (v - max_v_in) / (max_v_out - max_v_in);
+ }
+
+// combine the alpha contribution of every component into a single alpha
+ a = MAX(as, ah);
+ a = MAX(a, av);
+
+// Spill light processing
+ if(spill_tolerance > 0)
+ {
+// get the difference between the shifted input color to the unshifted spill wedge
+ if(!EQUIV(spill_distance, 0))
+ {
+ shift_hs(h_shifted,
+ s_shifted,
+ h,
+ s,
+ spill_x,
+ spill_y);
+ }
+ else
+ {
+ h_shifted = h;
+ s_shifted = s;
+ }
+
+// Difference between the shifted hue & the unshifted hue key
+ h_diff = hue_distance(h_shifted, hue_key);
+
+// inside the wedge
+ if(ABS(h_diff) < spill_tolerance)
+ {
+ if(!desaturate_only)
+ {
+// the shifted input color in the unshifted wedge
+// gives 2 unshifted border colors & the weighting
+ float blend = 0.5 + h_diff / spill_tolerance / 2;
+// shift the 2 border colors to the output wedge
+ float min_h_shifted;
+ float min_s_shifted;
+ shift_hs(min_h_shifted,
+ min_s_shifted,
+ min_h,
+ s_shifted,
+ -spill_x,
+ -spill_y);
+ float max_h_shifted;
+ float max_s_shifted;
+ shift_hs(max_h_shifted,
+ max_s_shifted,
+ max_h,
+ s_shifted,
+ -spill_x,
+ -spill_y);
+
+// blend the shifted border colors using the unshifted weighting
+// the only thing which doesn't restore the key color & doesn't make an edge is
+// fading the saturation to 0 in the middle
+ if(blend > 0.5)
+ {
+ h = max_h_shifted;
+ s = max_s_shifted;
+ // s = max_s_shifted * (blend - 0.5) / 0.5;
+ // DEBUG
+ //h = 0;
+ //s = blend;
+ }
+ else
+ {
+ h = min_h_shifted;
+ s = min_s_shifted;
+ // s = min_s_shifted * (0.5 - blend) / 0.5;
+ // DEBUG
+ //h = 0;
+ //s = blend;
+ }
+ }
+ else // !desaturate_only
+ {
+
+// fade the saturation to 0 in the middle
+ s *= ABS(h_diff) / spill_tolerance;
+ }
+
+ if(h < 0) h += 360;
+ HSV::hsv_to_rgb (r, g, b, h, s, v);
+
+// store new color
+ if (!use_yuv)
+ {
+ row[0] = (component_type) ((float) r * max);
+ row[1] = (component_type) ((float) g * max);
+ row[2] = (component_type) ((float) b * max);
+ }
+ else
+ YUV::yuv.rgb_to_yuv_f(r, g, b, row[0], row[1], row[2]);
+ }
+ }
+
+ a += alpha_offset;
+ CLAMP (a, 0.0, 1.0);
+
+ if (plugin->config.show_mask)
+ {
+ if (use_yuv)
+ {
+ row[0] = (component_type) ((float) a * max);
+ row[1] = (component_type) ((float) max / 2);
+ row[2] = (component_type) ((float) max / 2);
+ }
+ else
+ {
+ row[0] = (component_type) ((float) a * max);
+ row[1] = (component_type) ((float) a * max);
+ row[2] = (component_type) ((float) a * max);
+ }
+ }
+
+ /* Multiply alpha and put back in frame */
+ if (components == 4)
+ {
+ row[3] = MIN ((component_type) (a * max), row[3]);
+ }
+ else if (use_yuv)
+ {
+ row[0] = (component_type) ((float) a * row[0]);
+ row[1] = (component_type) ((float) a * (row[1] - (max / 2 + 1)) +
+ max / 2 + 1);
+ row[2] = (component_type) ((float) a * (row[2] - (max / 2 + 1)) +
+ max / 2 + 1);
+ }
+ else
+ {
+ row[0] = (component_type) ((float) a * row[0]);
+ row[1] = (component_type) ((float) a * row[1]);
+ row[2] = (component_type) ((float) a * row[2]);
+ }
+
+ row += components;
+ }
+ }
+}
+
+
+
+void ChromaKeyUnit::process_package(LoadPackage *package)
+{
+ ChromaKeyPackage *pkg = (ChromaKeyPackage*)package;
+
+ switch(plugin->input->get_color_model())
+ {
+ case BC_RGB_FLOAT:
+ process_chromakey<float> (3, 1.0, 0, pkg);
+ break;
+ case BC_RGBA_FLOAT:
+ process_chromakey<float> ( 4, 1.0, 0, pkg);
+ break;
+ case BC_RGB888:
+ process_chromakey<unsigned char> ( 3, 0xff, 0, pkg);
+ break;
+ case BC_RGBA8888:
+ process_chromakey<unsigned char> ( 4, 0xff, 0, pkg);
+ break;
+ case BC_YUV888:
+ process_chromakey<unsigned char> ( 3, 0xff, 1, pkg);
+ break;
+ case BC_YUVA8888:
+ process_chromakey<unsigned char> ( 4, 0xff, 1, pkg);
+ break;
+ case BC_YUV161616:
+ process_chromakey<uint16_t> (3, 0xffff, 1, pkg);
+ break;
+ case BC_YUVA16161616:
+ process_chromakey<uint16_t> (4, 0xffff, 1, pkg);
+ break;
+ }
+}
+
+
+
+REGISTER_PLUGIN(ChromaKeyAvid)
+
+
+
+ChromaKeyAvid::ChromaKeyAvid(PluginServer *server)
+ : PluginVClient(server)
+{
+ engine = 0;
+}
+
+ChromaKeyAvid::~ChromaKeyAvid()
+{
+ if(engine) delete engine;
+}
+
+
+int ChromaKeyAvid::process_buffer(VFrame *frame,
+ int64_t start_position,
+ double frame_rate)
+{
+ load_configuration();
+ this->input = frame;
+ this->output = frame;
+
+ read_frame(frame,
+ 0,
+ start_position,
+ frame_rate,
+ get_use_opengl());
+ if(get_use_opengl()) return run_opengl();
+
+ if(!engine) engine = new ChromaKeyServer(this);
+ engine->process_packages();
+
+ return 0;
+}
+
+const char* ChromaKeyAvid::plugin_title() { return N_("Chroma key (Avid)"); }
+int ChromaKeyAvid::is_realtime() { return 1; }
+
+
+LOAD_CONFIGURATION_MACRO(ChromaKeyAvid, ChromaKeyConfig)
+
+
+void ChromaKeyAvid::save_data(KeyFrame * keyframe)
+{
+ FileXML output;
+ output.set_shared_output(keyframe->xbuf);
+ output.tag.set_title("CHROMAKEY_AVID");
+ output.tag.set_property("RED", config.red);
+ output.tag.set_property("GREEN", config.green);
+ output.tag.set_property("BLUE", config.blue);
+ output.tag.set_property("MIN_BRIGHTNESS", config.min_brightness);
+ output.tag.set_property("MAX_BRIGHTNESS", config.max_brightness);
+ output.tag.set_property("SATURATION_START", config.saturation_start);
+ output.tag.set_property("SATURATION_LINE", config.saturation_line);
+ output.tag.set_property("TOLERANCE", config.tolerance);
+ output.tag.set_property("IN_SLOPE", config.in_slope);
+ output.tag.set_property("OUT_SLOPE", config.out_slope);
+ output.tag.set_property("ALPHA_OFFSET", config.alpha_offset);
+ output.tag.set_property("SPILL_SATURATION", config.spill_saturation);
+ output.tag.set_property("SPILL_ANGLE", config.spill_angle);
+ output.tag.set_property("DESATURATE_ONLY", config.desaturate_only);
+ output.tag.set_property("SHOW_MASK", config.show_mask);
+ output.append_tag();
+ output.tag.set_title("/CHROMAKEY_AVID");
+ output.append_tag();
+ output.append_newline();
+ output.terminate_string();
+}
+
+void ChromaKeyAvid::read_data(KeyFrame * keyframe)
+{
+ FileXML input;
+
+ input.set_shared_input(keyframe->xbuf);
+
+ while( !input.read_tag() )
+ {
+ if( input.tag.title_is("CHROMAKEY_AVID") )
+ {
+ config.red = input.tag.get_property("RED", config.red);
+ config.green = input.tag.get_property("GREEN", config.green);
+ config.blue = input.tag.get_property("BLUE", config.blue);
+ config.min_brightness =
+ input.tag.get_property("MIN_BRIGHTNESS", config.min_brightness);
+ config.max_brightness =
+ input.tag.get_property("MAX_BRIGHTNESS", config.max_brightness);
+ config.saturation_start =
+ input.tag.get_property("SATURATION_START", config.saturation_start);
+ config.saturation_line =
+ input.tag.get_property("SATURATION_LINE", config.saturation_line);
+ config.tolerance =
+ input.tag.get_property("TOLERANCE", config.tolerance);
+ config.in_slope =
+ input.tag.get_property("IN_SLOPE", config.in_slope);
+ config.out_slope =
+ input.tag.get_property("OUT_SLOPE", config.out_slope);
+ config.alpha_offset =
+ input.tag.get_property("ALPHA_OFFSET", config.alpha_offset);
+ config.spill_saturation =
+ input.tag.get_property("SPILL_SATURATION", config.spill_saturation);
+ config.spill_angle =
+ input.tag.get_property("SPILL_ANGLE", config.spill_angle);
+ config.desaturate_only =
+ input.tag.get_property("DESATURATE_ONLY", config.desaturate_only);
+ config.show_mask =
+ input.tag.get_property("SHOW_MASK", config.show_mask);
+
+ config.undo_red = config.red;
+ config.undo_green = config.green;
+ config.undo_blue = config.blue;
+ config.undo_min_brightness = config.min_brightness;
+ config.undo_max_brightness = config.max_brightness;
+ config.undo_tolerance = config.tolerance;
+ config.undo_saturation_start = config.saturation_start;
+ config.undo_saturation_line = config.saturation_line;
+ config.undo_in_slope = config.in_slope;
+ config.undo_out_slope = config.out_slope;
+ config.undo_alpha_offset = config.alpha_offset;
+ config.undo_spill_saturation = config.spill_saturation;
+ config.undo_spill_angle = config.spill_angle;
+ config.undo_desaturate_only = config.desaturate_only;
+ config.undo_show_mask = config.show_mask;
+ }
+ }
+}
+
+
+NEW_WINDOW_MACRO(ChromaKeyAvid, ChromaKeyWindow)
+
+
+void ChromaKeyAvid::update_gui()
+{
+ if( thread )
+ {
+ load_configuration();
+ ChromaKeyWindow *window = (ChromaKeyWindow*)thread->window;
+ window->lock_window();
+ window->update_gui(RESET_ALL);
+ window->unlock_window();
+ }
+}
+
+void ChromaKeyWindow::update_gui(int clear)
+{
+ ChromaKeyConfig &config = plugin->config;
+ switch(clear)
+ {
+ case RESET_RGB:
+ update_sample();
+ break;
+ case RESET_MIN_BRIGHTNESS:
+ min_brightness->update(config.min_brightness);
+ min_brightness_text->update(config.min_brightness);
+ break;
+ case RESET_MAX_BRIGHTNESS:
+ max_brightness->update(config.max_brightness);
+ max_brightness_text->update(config.max_brightness);
+ break;
+ case RESET_TOLERANCE:
+ tolerance->update(config.tolerance);
+ tolerance_text->update(config.tolerance);
+ break;
+ case RESET_SATURATION_START:
+ saturation_start->update(config.saturation_start);
+ saturation_start_text->update(config.saturation_start);
+ break;
+ case RESET_SATURATION_LINE:
+ saturation_line->update(config.saturation_line);
+ saturation_line_text->update(config.saturation_line);
+ break;
+ case RESET_IN_SLOPE:
+ in_slope->update(config.in_slope);
+ in_slope_text->update(config.in_slope);
+ break;
+ case RESET_OUT_SLOPE:
+ out_slope->update(config.out_slope);
+ out_slope_text->update(config.out_slope);
+ break;
+ case RESET_ALPHA_OFFSET:
+ alpha_offset->update(config.alpha_offset);
+ alpha_offset_text->update(config.alpha_offset);
+ break;
+ case RESET_SPILL_SATURATION:
+ spill_saturation->update(config.spill_saturation);
+ spill_saturation_text->update(config.spill_saturation);
+ break;
+ case RESET_SPILL_ANGLE:
+ spill_angle->update(config.spill_angle);
+ spill_angle_text->update(config.spill_angle);
+ break;
+ case RESET_ALL:
+ case RESET_DEFAULT_SETTINGS:
+ default:
+ min_brightness->update(config.min_brightness);
+ min_brightness_text->update(config.min_brightness);
+ max_brightness->update(config.max_brightness);
+ max_brightness_text->update(config.max_brightness);
+ saturation_start->update(config.saturation_start);
+ saturation_start_text->update(config.saturation_start);
+ saturation_line->update(config.saturation_line);
+ saturation_line_text->update(config.saturation_line);
+ tolerance->update(config.tolerance);
+ tolerance_text->update(config.tolerance);
+ in_slope->update(config.in_slope);
+ in_slope_text->update(config.in_slope);
+ out_slope->update(config.out_slope);
+ out_slope_text->update(config.out_slope);
+ alpha_offset->update(config.alpha_offset);
+ alpha_offset_text->update(config.alpha_offset);
+ spill_saturation->update(config.spill_saturation);
+ spill_saturation_text->update(config.spill_saturation);
+ spill_angle->update(config.spill_angle);
+ spill_angle_text->update(config.spill_angle);
+ desaturate_only->update(config.desaturate_only);
+ show_mask->update(config.show_mask);
+ update_sample();
+ break;
+ }
+}
+
+
+
+int ChromaKeyAvid::handle_opengl()
+{
+#ifdef HAVE_GL
+// For macro
+ ChromaKeyAvid *plugin = this;
+ COMMON_VARIABLES
+
+ static const char *yuv_shader =
+ "const vec3 black = vec3(0.0, 0.5, 0.5);\n"
+ "uniform mat3 yuv_to_rgb_matrix;\n"
+ "uniform mat3 rgb_to_yuv_matrix;\n"
+ "uniform float yminf;\n"
+ "\n"
+ "vec4 yuv_to_rgb(vec4 color)\n"
+ "{\n"
+ " color.rgb -= vec3(yminf, 0.5, 0.5);\n"
+ " color.rgb = yuv_to_rgb_matrix * color.rgb;\n"
+ " return color;\n"
+ "}\n"
+ "\n"
+ "vec4 rgb_to_yuv(vec4 color)\n"
+ "{\n"
+ " color.rgb = rgb_to_yuv_matrix * color.rgb;\n"
+ " color.rgb += vec3(yminf, 0.5, 0.5);\n"
+ " return color;\n"
+ "}\n";
+
+ static const char *rgb_shader =
+ "const vec3 black = vec3(0.0, 0.0, 0.0);\n"
+ "\n"
+ "vec4 yuv_to_rgb(vec4 color)\n"
+ "{\n"
+ " return color;\n"
+ "}\n"
+ "vec4 rgb_to_yuv(vec4 color)\n"
+ "{\n"
+ " return color;\n"
+ "}\n";
+
+ static const char *hsv_shader =
+ "vec4 rgb_to_hsv(vec4 color)\n"
+ "{\n"
+ RGB_TO_HSV_FRAG("color")
+ " return color;\n"
+ "}\n"
+ "\n"
+ "vec4 hsv_to_rgb(vec4 color)\n"
+ "{\n"
+ HSV_TO_RGB_FRAG("color")
+ " return color;\n"
+ "}\n"
+ "\n";
+
+ static const char *show_rgbmask_shader =
+ "vec4 show_mask(vec4 color, vec4 color2)\n"
+ "{\n"
+ " return vec4(1.0, 1.0, 1.0, min(color.a, color2.a));"
+ "}\n";
+
+ static const char *show_yuvmask_shader =
+ "vec4 show_mask(vec4 color, vec4 color2)\n"
+ "{\n"
+ " return vec4(1.0, 0.5, 0.5, min(color.a, color2.a));"
+ "}\n";
+
+ static const char *nomask_shader =
+ "vec4 show_mask(vec4 color, vec4 color2)\n"
+ "{\n"
+ " return vec4(color.rgb, min(color.a, color2.a));"
+ "}\n";
+
+ get_output()->to_texture();
+ get_output()->enable_opengl();
+ get_output()->init_screen();
+
+ const char *shader_stack[16];
+ memset(shader_stack,0, sizeof(shader_stack));
+ int current_shader = 0;
+
+ shader_stack[current_shader++] = \
+ !BC_CModels::is_yuv(get_output()->get_color_model()) ?
+ rgb_shader : yuv_shader;
+ shader_stack[current_shader++] = hsv_shader;
+ shader_stack[current_shader++] = !config.show_mask ? nomask_shader :
+ !BC_CModels::is_yuv(get_output()->get_color_model()) ?
+ show_rgbmask_shader : show_yuvmask_shader ;
+ extern unsigned char _binary_chromakeyavid_sl_start[];
+ static const char *shader_frag = (char*)_binary_chromakeyavid_sl_start;
+ shader_stack[current_shader++] = shader_frag;
+
+ shader_stack[current_shader] = 0;
+ unsigned int shader = VFrame::make_shader(shader_stack);
+ if( shader > 0 ) {
+ glUseProgram(shader);
+ glUniform1i(glGetUniformLocation(shader, "tex"), 0);
+ glUniform1f(glGetUniformLocation(shader, "red"), red);
+ glUniform1f(glGetUniformLocation(shader, "green"), green);
+ glUniform1f(glGetUniformLocation(shader, "blue"), blue);
+ glUniform1f(glGetUniformLocation(shader, "in_slope"), in_slope);
+ glUniform1f(glGetUniformLocation(shader, "out_slope"), out_slope);
+ glUniform1f(glGetUniformLocation(shader, "tolerance"), tolerance);
+ glUniform1f(glGetUniformLocation(shader, "tolerance_in"), tolerance_in);
+ glUniform1f(glGetUniformLocation(shader, "tolerance_out"), tolerance_out);
+ glUniform1f(glGetUniformLocation(shader, "sat_x"), sat_x);
+ glUniform1f(glGetUniformLocation(shader, "sat_y"), sat_y);
+ glUniform1f(glGetUniformLocation(shader, "min_s"), min_s);
+ glUniform1f(glGetUniformLocation(shader, "min_s_in"), min_s_in);
+ glUniform1f(glGetUniformLocation(shader, "min_s_out"), min_s_out);
+ glUniform1f(glGetUniformLocation(shader, "min_v"), min_v);
+ glUniform1f(glGetUniformLocation(shader, "min_v_in"), min_v_in);
+ glUniform1f(glGetUniformLocation(shader, "min_v_out"), min_v_out);
+ glUniform1f(glGetUniformLocation(shader, "max_v"), max_v);
+ glUniform1f(glGetUniformLocation(shader, "max_v_in"), max_v_in);
+ glUniform1f(glGetUniformLocation(shader, "max_v_out"), max_v_out);
+ glUniform1f(glGetUniformLocation(shader, "spill_distance"), spill_distance);
+ glUniform1f(glGetUniformLocation(shader, "spill_x"), spill_x);
+ glUniform1f(glGetUniformLocation(shader, "spill_y"), spill_y);
+ glUniform1f(glGetUniformLocation(shader, "spill_tolerance"), spill_tolerance);
+ glUniform1f(glGetUniformLocation(shader, "min_h"), min_h);
+ glUniform1f(glGetUniformLocation(shader, "max_h"), max_h);
+ glUniform1i(glGetUniformLocation(shader, "desaturate_only"), desaturate_only);
+ glUniform1f(glGetUniformLocation(shader, "alpha_offset"), alpha_offset);
+ glUniform1f(glGetUniformLocation(shader, "hue_key"), hue_key);
+ glUniform1f(glGetUniformLocation(shader, "saturation_key"), saturation_key);
+ glUniform1f(glGetUniformLocation(shader, "value_key"), value_key);
+ if( BC_CModels::is_yuv(get_output()->get_color_model()) )
+ BC_GL_COLORS(shader);
+ }
+
+ get_output()->bind_texture(0);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+
+ if(BC_CModels::components(get_output()->get_color_model()) == 3)
+ {
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ get_output()->clear_pbuffer();
+ }
+ get_output()->draw_texture();
+
+ glUseProgram(0);
+ get_output()->set_opengl_state(VFrame::SCREEN);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glDisable(GL_BLEND);
+
+
+#endif
+ return 0;
+}
--- /dev/null
+
+/*
+ * CINELERRA
+ * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef CHROMAKEY_H
+#define CHROMAKEY_H
+
+#include "colorpicker.h"
+#include "guicast.h"
+#include "theme.h"
+#include "loadbalance.h"
+#include "pluginvclient.h"
+
+#define RESET_DEFAULT_SETTINGS 20
+#define RESET_ALL 0
+#define RESET_RGB 1
+#define RESET_MIN_BRIGHTNESS 2
+#define RESET_MAX_BRIGHTNESS 3
+#define RESET_TOLERANCE 4
+#define RESET_SATURATION_START 5
+#define RESET_SATURATION_LINE 6
+#define RESET_IN_SLOPE 7
+#define RESET_OUT_SLOPE 8
+#define RESET_ALPHA_OFFSET 9
+#define RESET_SPILL_SATURATION 10
+#define RESET_SPILL_ANGLE 11
+
+class ChromaKeyAvid;
+class ChromaKeyWindow;
+class ChromaKeyText;
+class ChromaKeySlider;
+class ChromaKeyClr;
+class ChromaKeyReset;
+class ChromaKeyStore;
+class ChromaKeyRecall;
+class ChromaKeyExchange;
+class ChromaKeyUndo;
+
+enum {
+ CHROMAKEY_POSTPROCESS_NONE,
+ CHROMAKEY_POSTPROCESS_BLUR,
+ CHROMAKEY_POSTPROCESS_DILATE
+};
+
+
+class ChromaKeyConfig
+{
+public:
+ ChromaKeyConfig();
+ void reset(int clear);
+ void copy_from(ChromaKeyConfig &src);
+ int equivalent(ChromaKeyConfig &src);
+ void interpolate(ChromaKeyConfig &prev,
+ ChromaKeyConfig &next,
+ int64_t prev_frame,
+ int64_t next_frame,
+ int64_t current_frame);
+ int get_color();
+
+ // Output mode
+ bool show_mask;
+ // Key color definition
+ float red;
+ float green;
+ float blue;
+ // Key shade definition
+ float min_brightness;
+ float max_brightness;
+ // distance of wedge point from the center
+ float saturation_start;
+ // minimum saturation after wedge point
+ float saturation_line;
+ // hue range
+ float tolerance;
+ // Mask feathering
+ float in_slope;
+ float out_slope;
+ float alpha_offset;
+ // Spill light compensation
+ float spill_saturation;
+ float spill_angle;
+ bool desaturate_only;
+ // Instances for Store
+ bool store_show_mask;
+ float store_red;
+ float store_green;
+ float store_blue;
+ float store_min_brightness;
+ float store_max_brightness;
+ float store_saturation_start;
+ float store_saturation_line;
+ float store_tolerance;
+ float store_in_slope;
+ float store_out_slope;
+ float store_alpha_offset;
+ float store_spill_saturation;
+ float store_spill_angle;
+ bool store_desaturate_only;
+ // Instances for Undo
+ bool undo_show_mask;
+ float undo_red;
+ float undo_green;
+ float undo_blue;
+ float undo_min_brightness;
+ float undo_max_brightness;
+ float undo_saturation_start;
+ float undo_saturation_line;
+ float undo_tolerance;
+ float undo_in_slope;
+ float undo_out_slope;
+ float undo_alpha_offset;
+ float undo_spill_saturation;
+ float undo_spill_angle;
+ bool undo_desaturate_only;
+};
+
+
+class ChromaKeyColor : public BC_GenericButton
+{
+public:
+ ChromaKeyColor(ChromaKeyAvid *plugin,
+ ChromaKeyWindow *gui,
+ int x,
+ int y);
+
+ int handle_event();
+
+ ChromaKeyAvid *plugin;
+ ChromaKeyWindow *gui;
+};
+
+class ChromaKeyReset : public BC_GenericButton
+{
+public:
+ ChromaKeyReset(ChromaKeyAvid *plugin, ChromaKeyWindow *gui, int x, int y);
+ int handle_event();
+ ChromaKeyAvid *plugin;
+ ChromaKeyWindow *gui;
+};
+
+class ChromaKeyStore : public BC_GenericButton
+{
+public:
+ ChromaKeyStore(ChromaKeyAvid *plugin, ChromaKeyWindow *gui, int x, int y);
+ int handle_event();
+ ChromaKeyAvid *plugin;
+ ChromaKeyWindow *gui;
+};
+
+class ChromaKeyRecall : public BC_GenericButton
+{
+public:
+ ChromaKeyRecall(ChromaKeyAvid *plugin, ChromaKeyWindow *gui, int x, int y);
+ int handle_event();
+ ChromaKeyAvid *plugin;
+ ChromaKeyWindow *gui;
+};
+
+class ChromaKeyExchange : public BC_GenericButton
+{
+public:
+ ChromaKeyExchange(ChromaKeyAvid *plugin, ChromaKeyWindow *gui, int x, int y);
+ int handle_event();
+ ChromaKeyAvid *plugin;
+ ChromaKeyWindow *gui;
+};
+
+class ChromaKeyUndo : public BC_GenericButton
+{
+public:
+ ChromaKeyUndo(ChromaKeyAvid *plugin, ChromaKeyWindow *gui, int x, int y);
+ int handle_event();
+ ChromaKeyAvid *plugin;
+ ChromaKeyWindow *gui;
+};
+
+class ChromaKeyText : public BC_TumbleTextBox
+{
+public:
+ ChromaKeyText(ChromaKeyAvid *plugin, ChromaKeyWindow *gui,
+ ChromaKeySlider *slider, int x, int y,
+ float min, float max, float *output);
+ ~ChromaKeyText();
+ int handle_event();
+ ChromaKeyAvid *plugin;
+ ChromaKeyWindow *gui;
+ ChromaKeySlider *slider;
+ float *output;
+ float min, max;
+};
+
+class ChromaKeySlider : public BC_FSlider
+{
+public:
+ ChromaKeySlider(ChromaKeyAvid *plugin, ChromaKeyText *text,
+ int x, int y, int w, float min, float max, float *output);
+ ~ChromaKeySlider();
+ int handle_event();
+ ChromaKeyAvid *plugin;
+ ChromaKeyText *text;
+ float *output;
+};
+
+class ChromaKeyClr : public BC_Button
+{
+public:
+ ChromaKeyClr(ChromaKeyAvid *plugin, ChromaKeyWindow *gui,
+ int x, int y, int clear);
+ ~ChromaKeyClr();
+ int handle_event();
+ ChromaKeyAvid *plugin;
+ ChromaKeyWindow *gui;
+ int clear;
+};
+
+
+class ChromaKeyUseColorPicker : public BC_GenericButton
+{
+public:
+ ChromaKeyUseColorPicker(ChromaKeyAvid *plugin,
+ ChromaKeyWindow *gui, int x, int y);
+ int handle_event();
+ ChromaKeyAvid *plugin;
+ ChromaKeyWindow *gui;
+};
+
+class ChromaKeyColorThread : public ColorPicker
+{
+public:
+ ChromaKeyColorThread(ChromaKeyAvid *plugin, ChromaKeyWindow *gui);
+ int handle_new_color(int output, int alpha);
+ ChromaKeyAvid *plugin;
+ ChromaKeyWindow *gui;
+};
+
+class ChromaKeyToggle : public BC_CheckBox
+{
+public:
+ ChromaKeyToggle(ChromaKeyAvid *plugin,
+ int x,
+ int y,
+ bool *output,
+ const char *caption);
+ int handle_event();
+ ChromaKeyAvid *plugin;
+ bool *output;
+};
+
+
+
+class ChromaKeyWindow : public PluginClientWindow
+{
+public:
+ ChromaKeyWindow(ChromaKeyAvid *plugin);
+ ~ChromaKeyWindow();
+
+ void create_objects();
+ void update_sample();
+ void update_gui(int clear);
+ void done_event(int result);
+
+ ChromaKeyColor *color;
+ ChromaKeyUseColorPicker *use_colorpicker;
+
+ ChromaKeyText *min_brightness_text;
+ ChromaKeySlider *min_brightness;
+ ChromaKeyClr *min_brightness_clr;
+
+ ChromaKeyText *max_brightness_text;
+ ChromaKeySlider *max_brightness;
+ ChromaKeyClr *max_brightness_clr;
+
+ ChromaKeyText *saturation_start_text;
+ ChromaKeySlider *saturation_start;
+ ChromaKeyClr *saturation_start_clr;
+
+ ChromaKeyText *saturation_line_text;
+ ChromaKeySlider *saturation_line;
+ ChromaKeyClr *saturation_line_clr;
+
+ ChromaKeyText *tolerance_text;
+ ChromaKeySlider *tolerance;
+ ChromaKeyClr *tolerance_clr;
+
+ ChromaKeyText *in_slope_text;
+ ChromaKeySlider *in_slope;
+ ChromaKeyClr *in_slope_clr;
+
+ ChromaKeyText *out_slope_text;
+ ChromaKeySlider *out_slope;
+ ChromaKeyClr *out_slope_clr;
+
+ ChromaKeyText *alpha_offset_text;
+ ChromaKeySlider *alpha_offset;
+ ChromaKeyClr *alpha_offset_clr;
+
+ ChromaKeyText *spill_saturation_text;
+ ChromaKeySlider *spill_saturation;
+ ChromaKeyClr *spill_saturation_clr;
+
+ ChromaKeyText *spill_angle_text;
+ ChromaKeySlider *spill_angle;
+ ChromaKeyClr *spill_angle_clr;
+
+ ChromaKeyToggle *desaturate_only;
+ ChromaKeyToggle *show_mask;
+
+ ChromaKeyReset *reset;
+ ChromaKeyStore *store;
+ ChromaKeyRecall *recall;
+ ChromaKeyExchange *exchange;
+ ChromaKeyUndo *undo;
+
+ BC_SubWindow *sample;
+ ChromaKeyAvid *plugin;
+ ChromaKeyColorThread *color_thread;
+};
+
+class ChromaKeyServer : public LoadServer
+{
+public:
+ ChromaKeyServer(ChromaKeyAvid *plugin);
+ void init_packages();
+ LoadClient* new_client();
+ LoadPackage* new_package();
+
+ ChromaKeyAvid *plugin;
+};
+
+class ChromaKeyPackage : public LoadPackage
+{
+public:
+ ChromaKeyPackage();
+ int y1, y2;
+};
+
+class ChromaKeyUnit : public LoadClient
+{
+public:
+ ChromaKeyUnit(ChromaKeyAvid *plugin, ChromaKeyServer *server);
+ void process_package(LoadPackage *package);
+ template <typename component_type> void process_chromakey(int components, component_type max, bool use_yuv, ChromaKeyPackage *pkg);
+ bool is_same_color(float r, float g, float b, float rk,float bk,float gk,
+ float color_threshold, float light_threshold,
+ int key_main_component);
+
+ ChromaKeyAvid *plugin;
+};
+
+class ChromaKeyAvid : public PluginVClient
+{
+public:
+ ChromaKeyAvid(PluginServer *server);
+ ~ChromaKeyAvid();
+
+ PLUGIN_CLASS_MEMBERS(ChromaKeyConfig);
+ int process_buffer(VFrame *frame, int64_t start_position, double frame_rate);
+ int handle_opengl();
+ int is_realtime();
+ void save_data(KeyFrame *keyframe);
+ void read_data(KeyFrame *keyframe);
+ void update_gui();
+
+ VFrame *input, *output;
+ ChromaKeyServer *engine;
+};
+
+#endif
--- /dev/null
+uniform sampler2D tex;
+uniform float red;
+uniform float green;
+uniform float blue;
+uniform float in_slope;
+uniform float out_slope;
+uniform float tolerance;
+uniform float tolerance_in;
+uniform float tolerance_out;
+uniform float sat_x;
+uniform float sat_y;
+uniform float min_s;
+uniform float min_s_in;
+uniform float min_s_out;
+uniform float min_v;
+uniform float min_v_in;
+uniform float min_v_out;
+uniform float max_v;
+uniform float max_v_in;
+uniform float max_v_out;
+uniform float spill_distance;
+uniform float spill_x;
+uniform float spill_y;
+uniform float spill_tolerance;
+uniform float min_h;
+uniform float max_h;
+uniform bool desaturate_only;
+uniform float alpha_offset;
+uniform float hue_key;
+uniform float saturation_key;
+uniform float value_key;
+
+
+
+// shortest distance between 2 hues
+float hue_distance(float h1, float h2)
+{
+ float result = h1 - h2;
+ if(result < -180.0) result += 360.0;
+ else
+ if(result > 180.0) result -= 360.0;
+ return result;
+}
+
+// shift H & S based on an X & Y offset
+void shift_hs(out float h_shifted,
+ out float s_shifted,
+ float h,
+ float s,
+ float x_offset,
+ float y_offset)
+{
+ float h_rad = radians(h);
+ float x = cos(h_rad) * s;
+ float y = sin(h_rad) * s;
+ x += x_offset;
+ y += y_offset;
+ h_shifted = degrees(atan(y, x));
+ s_shifted = length(vec2(x, y));
+}
+
+
+void main()
+{
+ vec4 color = texture2D(tex, gl_TexCoord[0].st);
+/* Contribution to alpha from each component */
+ float ah = 1.0;
+ float as = 1.0;
+ float av = 1.0;
+ float a = 1.0;
+ vec4 color2;
+
+/* Convert to HSV */
+ color2 = yuv_to_rgb(color);
+ color2 = rgb_to_hsv(color2);
+ float h = color2.r;
+ float s = color2.g;
+ float v = color2.b;
+
+// shift the color in XY to shift the wedge point
+ float h_shifted, s_shifted;
+ shift_hs(h_shifted,
+ s_shifted,
+ h,
+ s,
+ sat_x,
+ sat_y);
+
+/* Get the difference between the current hue & the hue key */
+ float h_diff = abs(hue_distance(h_shifted, hue_key));
+
+// alpha contribution from hue difference
+// outside wedge < tolerance_out < tolerance_in < inside wedge < tolerance_in < tolerance_out < outside wedge
+ if (tolerance_out > 0.0)
+ {
+// completely inside the wedge
+ if (h_diff < tolerance_in)
+ ah = 0.0;
+ else
+// between the outer & inner slope
+ if(h_diff < tolerance_out)
+ ah = (h_diff - tolerance_in) / (tolerance_out - tolerance_in);
+ if(ah > 1.0) ah = 1.0;
+ }
+
+// alpha contribution from saturation
+// outside wedge < min_s_out < min_s_in < inside wedge
+ if(s_shifted > min_s_out)
+ {
+// saturation with offset applied
+// completely inside the wedge
+ if(s_shifted > min_s_in)
+ as = 0.0;
+// inside the gradient
+ if(s_shifted >= min_s_out)
+ as = (min_s_in - s_shifted) / (min_s_in - min_s_out);
+ }
+
+
+// alpha contribution from brightness range
+// brightness range is defined by 4 in/out variables
+// outside wedge < min_v_out < min_v_in < inside wedge < max_v_in < max_v_out < outside wedge
+ if(v > min_v_out)
+ {
+ if(v < min_v_in || max_v_in >= 1.0)
+ av = (min_v_in - v) / (min_v_in - min_v_out);
+ else
+ if(v <= max_v_in)
+ av = 0.0;
+ else
+ if(v <= max_v_out)
+ av = (v - max_v_in) / (max_v_out - max_v_in);
+ }
+
+// combine the alpha contribution of every component into a single alpha
+ a = max(as, ah);
+ a = max(a, av);
+
+// Spill light processing
+ if(spill_tolerance > 0.0)
+ {
+// get the difference between the shifted input color to the unshifted spill wedge
+ if(spill_distance != 0.0)
+ {
+ shift_hs(h_shifted,
+ s_shifted,
+ h,
+ s,
+ spill_x,
+ spill_y);
+ }
+ else
+ {
+ h_shifted = h;
+ s_shifted = s;
+ }
+
+// Difference between the shifted hue & the unshifted hue key
+ h_diff = hue_distance(h_shifted, hue_key);
+
+// inside the wedge
+ if(abs(h_diff) < spill_tolerance)
+ {
+ if(!desaturate_only)
+ {
+// the shifted input color in the unshifted wedge
+// gives 2 unshifted border colors & the weighting
+ float blend = 0.5 + h_diff / spill_tolerance / 2.0;
+// shift the 2 border colors to the output wedge
+ float min_h_shifted;
+ float min_s_shifted;
+ shift_hs(min_h_shifted,
+ min_s_shifted,
+ min_h,
+ s_shifted,
+ -spill_x,
+ -spill_y);
+ float max_h_shifted;
+ float max_s_shifted;
+ shift_hs(max_h_shifted,
+ max_s_shifted,
+ max_h,
+ s_shifted,
+ -spill_x,
+ -spill_y);
+
+
+// blend the shifted border colors using the unshifted weighting
+// the only thing which doesn't restore the key color & doesn't make an edge is
+// fading the saturation to 0 in the middle
+ if(blend > 0.5)
+ {
+ h = max_h_shifted;
+ s = max_s_shifted;
+ }
+ else
+ {
+ h = min_h_shifted;
+ s = min_s_shifted;
+ }
+ }
+ else // !desaturate_only
+ {
+
+// fade the saturation to 0 in the middle
+ s *= abs(h_diff) / spill_tolerance;
+ }
+
+
+ if(h < 0.0) h += 360.0;
+
+// store new color
+ color2.r = h;
+ color2.g = s;
+ color2.b = v;
+ color2 = hsv_to_rgb(color2);
+ color.rgb = rgb_to_yuv(color2).rgb;
+ }
+ }
+
+
+
+ a += alpha_offset;
+ a = min(a, 1.0);
+ color.a = a;
+/* Convert mask into image */
+ gl_FragColor = show_mask(color, color2);
+}
+
+
--- /dev/null
+include ../../plugin_defs
+
+PLUGIN = swatch
+OBJS = $(OBJDIR)/swatch.o
+
+include ../../plugin_config
+
+$(OBJDIR)/swatch.o: swatch.C
--- /dev/null
+/*
+ * CINELERRA
+ * Copyright (C) 2024 Adam Williams <broadcast at earthling dot net>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+
+// draw a color swatch for a given brightness or saturation
+// does not visualize but draws output to be processed
+
+#include <math.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "bcdisplayinfo.h"
+#include "bccolors.h"
+#include "clip.h"
+#include "bchash.h"
+#include "filexml.h"
+#include "keyframe.h"
+#include "language.h"
+#include "playback3d.h"
+#include "swatch.h"
+#include "vframe.h"
+
+
+
+
+REGISTER_PLUGIN(SwatchMain)
+
+
+#define MAX_VALUE 100
+
+
+
+SwatchConfig::SwatchConfig()
+{
+ brightness = MAX_VALUE;
+ saturation = MAX_VALUE;
+ fix_brightness = 0;
+ angle = 0;
+ draw_src = 0;
+}
+
+int SwatchConfig::equivalent(SwatchConfig &that)
+{
+ return brightness == that.brightness &&
+ saturation == that.saturation &&
+ fix_brightness == that.fix_brightness &&
+ angle == that.angle &&
+ draw_src == that.draw_src;
+}
+
+void SwatchConfig::copy_from(SwatchConfig &that)
+{
+ brightness = that.brightness;
+ saturation = that.saturation;
+ fix_brightness = that.fix_brightness;
+ angle = that.angle;
+ draw_src = that.draw_src;
+}
+
+void SwatchConfig::interpolate(SwatchConfig &prev,
+ SwatchConfig &next,
+ long prev_frame,
+ long next_frame,
+ long current_frame)
+{
+ double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
+ double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
+
+
+ this->brightness = (int)(prev.brightness * prev_scale + next.brightness * next_scale);
+ this->saturation = (int)(prev.saturation * prev_scale + next.saturation * next_scale);
+ this->angle = (int)(prev.angle * prev_scale + next.angle * next_scale);
+ fix_brightness = prev.fix_brightness;
+ draw_src = prev.draw_src;
+}
+
+
+
+
+SwatchSlider::SwatchSlider(SwatchMain *plugin,
+ SwatchWindow *gui,
+ int x,
+ int y,
+ int min,
+ int max,
+ int *output)
+ : BC_ISlider(x,
+ y,
+ 0,
+ gui->get_w() - xS(10) - x,
+ gui->get_w() - xS(10) - x,
+ min,
+ max,
+ *output)
+{
+ this->plugin = plugin;
+ this->output = output;
+}
+
+int SwatchSlider::handle_event ()
+{
+ *output = get_value();
+ plugin->send_configure_change();
+ return 1;
+}
+
+SwatchRadial::SwatchRadial(SwatchMain *plugin,
+ SwatchWindow *gui,
+ int x,
+ int y,
+ const char *text,
+ int fix_brightness)
+ : BC_Radial(x,
+ y,
+ (plugin->config.fix_brightness && fix_brightness),
+ text)
+{
+ this->plugin = plugin;
+ this->gui = gui;
+ this->fix_brightness = fix_brightness;
+}
+
+int SwatchRadial::handle_event()
+{
+ plugin->config.fix_brightness = fix_brightness;
+ gui->update_fixed();
+ plugin->send_configure_change();
+ return 1;
+}
+
+
+SwatchCheck::SwatchCheck(SwatchMain *plugin,
+ int x,
+ int y,
+ const char *text,
+ int *output)
+ : BC_CheckBox(x,
+ y,
+ *output,
+ text)
+{
+ this->plugin = plugin;
+ this->output = output;
+}
+
+int SwatchCheck::handle_event()
+{
+ *output = get_value();
+ plugin->send_configure_change();
+ return 1;
+}
+
+
+
+
+SwatchWindow::SwatchWindow(SwatchMain *plugin)
+ : PluginClientWindow(plugin,
+ xS(350),
+ yS(300),
+ xS(350),
+ yS(300),
+ 0)
+{
+ this->plugin = plugin;
+}
+
+SwatchWindow::~SwatchWindow()
+{
+}
+
+void SwatchWindow::create_objects()
+{
+ int margin = yS(10);
+ int x = margin, y = margin;
+ BC_Title *title;
+
+ add_subwindow(brightness_title = new BC_Title(x, y, _("Brightness:")));
+ y += brightness_title->get_h() + margin;
+ add_subwindow (brightness = new SwatchSlider(plugin, this, x, y, 0, MAX_VALUE, &plugin->config.brightness));
+ y += brightness->get_h() + margin;
+
+ add_subwindow(saturation_title = new BC_Title(x, y, _("Saturation:")));
+ y += saturation_title->get_h() + margin;
+ add_subwindow (saturation = new SwatchSlider(plugin, this, x, y, 0, MAX_VALUE, &plugin->config.saturation));
+ y += saturation->get_h() + margin;
+
+ add_subwindow(title = new BC_Title(x, y, _("Angle:")));
+ y += title->get_h() + margin;
+ add_subwindow (angle = new SwatchSlider(plugin, this, x, y, -180, 180, &plugin->config.angle));
+ y += saturation->get_h() + margin;
+
+ add_subwindow(fix_brightness = new SwatchRadial(plugin,
+ this,
+ x,
+ y,
+ _("Constant Brightness"),
+ 1));
+ y += fix_brightness->get_h() + margin;
+ add_subwindow(fix_saturation = new SwatchRadial(plugin,
+ this,
+ x,
+ y,
+ _("Constant Saturation"),
+ 0));
+ y += fix_saturation->get_h() + margin;
+ update_fixed();
+
+ add_subwindow(draw_src = new SwatchCheck(plugin,
+ x,
+ y,
+ _("Draw source"),
+ &plugin->config.draw_src));
+
+
+ show_window();
+}
+
+void SwatchWindow::update_fixed()
+{
+ fix_brightness->update(plugin->config.fix_brightness);
+ fix_saturation->update(!plugin->config.fix_brightness);
+ if(plugin->config.fix_brightness)
+ {
+ saturation_title->set_color(BC_WindowBase::get_resources()->disabled_text_color);
+ brightness_title->set_color(BC_WindowBase::get_resources()->default_text_color);
+ saturation->disable();
+ brightness->enable();
+ }
+ else
+ {
+ saturation_title->set_color(BC_WindowBase::get_resources()->default_text_color);
+ brightness_title->set_color(BC_WindowBase::get_resources()->disabled_text_color);
+ saturation->enable();
+ brightness->disable();
+ }
+}
+
+
+SwatchMain::SwatchMain(PluginServer *server)
+ : PluginVClient(server)
+{
+ need_reconfigure = 1;
+ engine = 0;
+ temp = 0;
+ src_temp = 0;
+}
+
+SwatchMain::~SwatchMain()
+{
+ if(engine) delete engine;
+ if(temp) delete temp;
+ if(src_temp) delete src_temp;
+}
+
+const char* SwatchMain::plugin_title() { return N_("Color Swatch"); }
+int SwatchMain::is_realtime() { return 1; }
+
+
+NEW_WINDOW_MACRO(SwatchMain, SwatchWindow)
+
+LOAD_CONFIGURATION_MACRO(SwatchMain, SwatchConfig)
+
+int SwatchMain::is_synthesis()
+{
+ return 1;
+}
+
+int SwatchMain::process_buffer(VFrame *frame,
+ int64_t start_position,
+ double frame_rate)
+{
+ need_reconfigure |= load_configuration();
+ int use_opengl = get_use_opengl();
+
+// have to draw output pixels out of order
+ if(config.draw_src) use_opengl = 0;
+
+ if(use_opengl) return run_opengl();
+
+ if(!engine) engine = new SwatchEngine(this,
+ get_project_smp() + 1,
+ get_project_smp() + 1);
+ if(config.draw_src)
+ {
+ if(!src_temp)
+ src_temp = new VFrame(0,
+ -1,
+ frame->get_w(),
+ frame->get_h(),
+ frame->get_color_model(),
+ -1);
+
+ read_frame(src_temp,
+ 0,
+ start_position,
+ frame_rate,
+ use_opengl);
+ }
+
+
+ if(!temp) temp = new VFrame(0,
+ -1,
+ frame->get_w(),
+ frame->get_h(),
+ frame->get_color_model(),
+ -1);
+
+// draw pattern once
+ if(need_reconfigure)
+ engine->draw_pattern();
+
+// draw the pattern on the output
+ frame->copy_from(temp);
+// draw input on the pattern
+ if(config.draw_src)
+ engine->draw_src();
+
+//printf("SwatchMain::process_buffer %d %d\n", __LINE__, config.draw_src);
+ return 0;
+}
+
+
+void SwatchMain::update_gui()
+{
+ if(thread)
+ {
+ if(load_configuration())
+ {
+ ((SwatchWindow*)thread->window)->lock_window("SwatchMain::update_gui");
+ ((SwatchWindow*)thread->window)->brightness->update(config.brightness);
+ ((SwatchWindow*)thread->window)->saturation->update(config.saturation);
+ ((SwatchWindow*)thread->window)->angle->update(config.angle);
+ ((SwatchWindow*)thread->window)->draw_src->update(config.draw_src);
+ ((SwatchWindow*)thread->window)->update_fixed();
+ ((SwatchWindow*)thread->window)->unlock_window();
+ }
+ }
+}
+
+
+
+
+
+void SwatchMain::save_data(KeyFrame *keyframe)
+{
+ FileXML output;
+
+// cause data to be stored directly in text
+ output.set_shared_output(keyframe->xbuf);
+ output.tag.set_title("SWATCH");
+
+ output.tag.set_property("BRIGHTNESS", config.brightness);
+ output.tag.set_property("SATURATION", config.saturation);
+ output.tag.set_property("ANGLE", config.angle);
+ output.tag.set_property("FIX_BRIGHTNESS", config.fix_brightness);
+ output.tag.set_property("DRAW_SRC", config.draw_src);
+ output.append_tag();
+ output.tag.set_title("/SWATCH");
+ output.append_tag();
+ output.append_newline();
+ output.terminate_string();
+//printf("SwatchMain::save_data %d %s\n", __LINE__, output.string);
+}
+
+void SwatchMain::read_data(KeyFrame *keyframe)
+{
+ FileXML input;
+
+ input.set_shared_input(keyframe->xbuf);
+
+ while( !input.read_tag() )
+ {
+ if(input.tag.title_is("SWATCH"))
+ {
+ config.brightness =
+ input.tag.get_property("BRIGHTNESS", config.brightness);
+ config.saturation =
+ input.tag.get_property("SATURATION", config.saturation);
+ config.angle =
+ input.tag.get_property("ANGLE", config.angle);
+ config.fix_brightness =
+ input.tag.get_property("FIX_BRIGHTNESS", config.fix_brightness);
+ config.draw_src =
+ input.tag.get_property("DRAW_SRC", config.draw_src);
+ }
+ }
+}
+
+int SwatchMain::handle_opengl()
+{
+#ifdef HAVE_GL
+ const char *head_frag =
+ "uniform mat3 yuv_to_rgb_matrix;\n"
+ "uniform mat3 rgb_to_yuv_matrix;\n"
+ "uniform float yminf;\n"
+ "uniform sampler2D tex;\n"
+ "uniform vec2 texture_extents;\n"
+ "uniform vec2 center_coord;\n"
+ "uniform float value;\n"
+ "uniform float saturation;\n"
+ "uniform float angle;\n"
+ "uniform bool fix_value;\n"
+ "\n"
+ "void main()\n"
+ "{\n"
+ " vec2 out_coord = gl_TexCoord[0].st * texture_extents;\n"
+ " vec2 in_coord = vec2(out_coord.x - center_coord.x, out_coord.y - center_coord.y);\n"
+ " float max_s = center_coord.x;\n"
+ " if(center_coord.y < max_s) max_s = center_coord.y;\n"
+ " vec4 pixel;\n"
+ " pixel.a = 1.0;\n"
+ " pixel.r = atan(in_coord.x, in_coord.y) / 2.0 / 3.14159 * 360.0 + angle; // hue\n"
+ " if(pixel.r < 0.0) pixel.r += 360.0;\n"
+ " if(fix_value)\n"
+ " {\n"
+ " pixel.g = length(vec2(in_coord.x, in_coord.y)) / max_s; // saturation\n"
+ " if(pixel.g > 1.0) pixel.g = 1.0; \n"
+ " pixel.b = value;\n"
+ " }\n"
+ " else\n"
+ " {\n"
+ " pixel.g = saturation;\n"
+ " pixel.b = length(vec2(in_coord.x, in_coord.y)) / max_s; // value\n"
+ " if(pixel.b > 1.0) pixel.b = 1.0; \n"
+ " }\n"
+ HSV_TO_RGB_FRAG("pixel");
+
+ static const char *put_yuv_frag =
+ RGB_TO_YUV_FRAG("pixel")
+ " gl_FragColor = pixel;\n"
+ "}\n";
+
+ static const char *put_rgb_frag =
+ " gl_FragColor = pixel;\n"
+ "}\n";
+
+
+
+
+ const char *shader_stack[5] = { 0, 0, 0, 0, 0 };
+ shader_stack[0] = head_frag;
+ if(BC_CModels::is_yuv(get_output()->get_color_model()))
+ shader_stack[1] = put_yuv_frag;
+ else
+ shader_stack[1] = put_rgb_frag;
+
+ get_output()->set_opengl_state(VFrame::TEXTURE);
+ get_output()->to_texture();
+ get_output()->enable_opengl();
+ get_output()->init_screen();
+ get_output()->bind_texture(0);
+
+ unsigned int frag = VFrame::make_shader(0,
+ shader_stack[0],
+ shader_stack[1],
+ 0);
+
+ if(frag)
+ {
+ glUseProgram(frag);
+ float w = get_output()->get_w();
+ float h = get_output()->get_h();
+ float texture_w = get_output()->get_texture_w();
+ float texture_h = get_output()->get_texture_h();
+ glUniform1i(glGetUniformLocation(frag, "tex"), 0);
+ glUniform2f(glGetUniformLocation(frag, "center_coord"),
+ (GLfloat)w / 2,
+ (GLfloat)h / 2);
+ glUniform2f(glGetUniformLocation(frag, "texture_extents"),
+ (GLfloat)texture_w,
+ (GLfloat)texture_h);
+
+ glUniform1f(glGetUniformLocation(frag, "value"), (float)config.brightness / MAX_VALUE);
+ glUniform1f(glGetUniformLocation(frag, "saturation"), (float)config.saturation / MAX_VALUE);
+ glUniform1f(glGetUniformLocation(frag, "angle"), (float)config.angle);
+ glUniform1i(glGetUniformLocation(frag, "fix_value"), config.fix_brightness);
+ if(BC_CModels::is_yuv(get_output()->get_color_model()))
+ BC_GL_COLORS(frag);
+ }
+
+ get_output()->draw_texture();
+ glUseProgram(0);
+ get_output()->set_opengl_state(VFrame::SCREEN);
+
+#endif
+ return 0;
+}
+
+
+
+
+
+
+
+
+
+
+
+SwatchPackage::SwatchPackage()
+ : LoadPackage()
+{
+}
+
+
+
+
+SwatchUnit::SwatchUnit(SwatchEngine *server, SwatchMain *plugin)
+ : LoadClient(server)
+{
+ this->plugin = plugin;
+ this->server = server;
+}
+
+
+
+
+#define CREATE_SWATCH(type, components, max, is_yuv) \
+{ \
+ for(int i = pkg->y1; i < pkg->y2; i++) \
+ { \
+ type *out_row = (type*)plugin->temp->get_rows()[i]; \
+ for(int j = 0; j < w; j++) \
+ { \
+ float hue = atan2(j - center_x, i - center_y) * 360 / 2 / M_PI + angle; \
+ if(fix_brightness) \
+ { \
+ saturation = hypot(j - center_x, i - center_y) / max_s; \
+ if(saturation > 1) saturation = 1; \
+ } \
+ else \
+ { \
+ value = hypot(j - center_x, i - center_y) / max_s; \
+ if(value > 1) value = 1; \
+ } \
+ if(hue < 0) hue += 360; \
+ if(is_yuv) \
+ { \
+ int y, u, v; \
+ HSV::hsv_to_yuv(y, u, v, hue, saturation, value, max); \
+ out_row[0] = y; \
+ out_row[1] = u; \
+ out_row[2] = v; \
+ } \
+ else \
+ { \
+ float r, g, b; \
+ HSV::hsv_to_rgb(r, g, b, hue, saturation, value); \
+ out_row[0] = (type)(r * max); \
+ out_row[1] = (type)(g * max); \
+ out_row[2] = (type)(b * max); \
+ } \
+ \
+/* the alpha */ \
+ if(components == 4) out_row[3] = max; \
+ out_row += components; \
+ } \
+ } \
+}
+
+
+#define DRAW_SRC(type, components, max, is_yuv) \
+{ \
+ type **dst_rows = (type**)plugin->get_output()->get_rows(); \
+ for(int i = pkg->y1; i < pkg->y2; i++) \
+ { \
+ type *src_row = (type*)plugin->src_temp->get_rows()[i]; \
+ for(int j = 0; j < w; j++) \
+ { \
+/* the source values */ \
+ type r, g, b; \
+ float r2, g2, b2; \
+ if(is_yuv) \
+ { \
+ YUV::yuv.yuv_to_rgb_f (r2, g2, b2, src_row[0], src_row[1], src_row[2]); \
+ } \
+ else \
+ { \
+ r = src_row[0]; \
+ g = src_row[1]; \
+ b = src_row[2]; \
+ r2 = (float)r / max; \
+ g2 = (float)g / max; \
+ b2 = (float)b / max; \
+ } \
+ float hue, s, value; \
+ HSV::rgb_to_hsv(r2, g2, b2, hue, s, value); \
+ float h_rad = TO_RAD(hue - angle); \
+/* get coordinate of color in output */ \
+ int x_out, y_out; \
+ if(fix_brightness) \
+ { \
+ x_out = center_x + (int)(sin(h_rad) * s * max_s); \
+ y_out = center_y + (int)(cos(h_rad) * s * max_s); \
+ } \
+ else \
+ { \
+ x_out = center_x + (int)(sin(h_rad) * value * max_s); \
+ y_out = center_y + (int)(cos(h_rad) * value * max_s); \
+ } \
+ if(x_out >= 0 && x_out < w && y_out >= 0 && y_out < h) \
+ { \
+ type *dst = dst_rows[y_out] + x_out * components; \
+ if(is_yuv) \
+ { \
+ dst[0] = src_row[0]; \
+ dst[1] = src_row[1]; \
+ dst[2] = src_row[2]; \
+ } \
+ else \
+ { \
+ dst[0] = r; \
+ dst[1] = g; \
+ dst[2] = b; \
+ } \
+ } \
+ \
+ src_row += components; \
+ } \
+ } \
+}
+
+#define DRAW_PATTERN_MODE 0
+#define DRAW_SRC_MODE 1
+
+void SwatchUnit::process_package(LoadPackage *package)
+{
+ SwatchPackage *pkg = (SwatchPackage*)package;
+ int h = plugin->temp->get_h();
+ int w = plugin->temp->get_w();
+ int center_x = w / 2;
+ int center_y = h / 2;
+ int cmodel = plugin->temp->get_color_model();
+// maximum saturation
+ float max_s = center_x;
+ if(center_y < max_s) max_s = center_y;
+ int fix_brightness = plugin->config.fix_brightness;
+ float saturation = (float)plugin->config.saturation / MAX_VALUE;
+ float value = (float)plugin->config.brightness / MAX_VALUE;
+ float angle = plugin->config.angle;
+
+ if(server->mode == DRAW_PATTERN_MODE)
+ {
+ switch(cmodel)
+ {
+ case BC_RGB888:
+ CREATE_SWATCH(unsigned char, 3, 0xff, 0)
+ break;
+ case BC_RGBA8888:
+ CREATE_SWATCH(unsigned char, 4, 0xff, 0)
+ break;
+ case BC_RGB_FLOAT:
+ CREATE_SWATCH(float, 3, 1.0, 0)
+ break;
+ case BC_RGBA_FLOAT:
+ CREATE_SWATCH(float, 4, 1.0, 0)
+ break;
+ case BC_YUV888:
+ CREATE_SWATCH(unsigned char, 3, 0xff, 1)
+ break;
+ case BC_YUVA8888:
+ CREATE_SWATCH(unsigned char, 4, 0xff, 1)
+ break;
+ }
+ }
+ else
+ {
+ switch(cmodel)
+ {
+ case BC_RGB888:
+ DRAW_SRC(unsigned char, 3, 0xff, 0)
+ break;
+ case BC_RGBA8888:
+ DRAW_SRC(unsigned char, 4, 0xff, 0)
+ break;
+ case BC_RGB_FLOAT:
+ DRAW_SRC(float, 3, 1.0, 0)
+ break;
+ case BC_RGBA_FLOAT:
+ DRAW_SRC(float, 4, 1.0, 0)
+ break;
+ case BC_YUV888:
+ DRAW_SRC(unsigned char, 3, 0xff, 1)
+ break;
+ case BC_YUVA8888:
+ DRAW_SRC(unsigned char, 4, 0xff, 1)
+ break;
+ }
+ }
+}
+
+
+
+
+
+
+SwatchEngine::SwatchEngine(SwatchMain *plugin,
+ int total_clients,
+ int total_packages)
+ : LoadServer(total_clients, total_packages)
+{
+ this->plugin = plugin;
+}
+
+void SwatchEngine::draw_pattern()
+{
+ mode = DRAW_PATTERN_MODE;
+ process_packages();
+}
+
+void SwatchEngine::draw_src()
+{
+ mode = DRAW_SRC_MODE;
+ process_packages();
+}
+
+void SwatchEngine::init_packages()
+{
+ for(int i = 0; i < get_total_packages(); i++)
+ {
+ SwatchPackage *package = (SwatchPackage*)get_package(i);
+ package->y1 = plugin->temp->get_h() *
+ i /
+ get_total_packages();
+ package->y2 = plugin->temp->get_h() *
+ (i + 1) /
+ get_total_packages();
+ }
+}
+
+LoadClient* SwatchEngine::new_client()
+{
+ return new SwatchUnit(this, plugin);
+}
+
+LoadPackage* SwatchEngine::new_package()
+{
+ return new SwatchPackage;
+}
+
+
+
+
+
--- /dev/null
+/*
+ * CINELERRA
+ * Copyright (C) 2024 Adam Williams <broadcast at earthling dot net>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef SWATCH_H
+#define SWATCH_H
+
+class SwatchMain;
+class SwatchEngine;
+class SwatchWindow;
+class SwatchEngine;
+
+
+
+#include "guicast.h"
+#include "loadbalance.h"
+#include "pluginvclient.h"
+
+class SwatchConfig
+{
+public:
+ SwatchConfig();
+
+ int equivalent(SwatchConfig &that);
+ void copy_from(SwatchConfig &that);
+ void interpolate(SwatchConfig &prev,
+ SwatchConfig &next,
+ long prev_frame,
+ long next_frame,
+ long current_frame);
+
+ int brightness;
+ int saturation;
+ int fix_brightness;
+ int draw_src;
+ int angle;
+};
+
+
+class SwatchSlider : public BC_ISlider
+{
+public:
+ SwatchSlider(SwatchMain *plugin,
+ SwatchWindow *gui,
+ int x,
+ int y,
+ int min,
+ int max,
+ int *output);
+ int handle_event();
+ SwatchMain *plugin;
+ int *output;
+};
+
+class SwatchRadial : public BC_Radial
+{
+public:
+ SwatchRadial(SwatchMain *plugin,
+ SwatchWindow *gui,
+ int x,
+ int y,
+ const char *text,
+ int fix_brightness);
+ int handle_event();
+ SwatchMain *plugin;
+ SwatchWindow *gui;
+ int fix_brightness;
+};
+
+class SwatchCheck : public BC_CheckBox
+{
+public:
+ SwatchCheck(SwatchMain *plugin,
+ int x,
+ int y,
+ const char *text,
+ int *output);
+ int handle_event();
+ SwatchMain *plugin;
+ SwatchWindow *gui;
+ int *output;
+};
+
+class SwatchWindow : public PluginClientWindow
+{
+public:
+ SwatchWindow(SwatchMain *plugin);
+ ~SwatchWindow();
+
+ void create_objects();
+ void update_fixed();
+
+ SwatchMain *plugin;
+ SwatchSlider *brightness;
+ SwatchSlider *saturation;
+ SwatchSlider *angle;
+ SwatchRadial *fix_brightness;
+ SwatchRadial *fix_saturation;
+ SwatchCheck *draw_src;
+ BC_Title *brightness_title;
+ BC_Title *saturation_title;
+};
+
+
+
+
+
+
+
+class SwatchMain : public PluginVClient
+{
+public:
+ SwatchMain(PluginServer *server);
+ ~SwatchMain();
+
+ int process_buffer(VFrame *frame,
+ int64_t start_position,
+ double frame_rate);
+ int is_realtime();
+ void save_data(KeyFrame *keyframe);
+ void read_data(KeyFrame *keyframe);
+ void update_gui();
+ int is_synthesis();
+ int handle_opengl();
+
+ PLUGIN_CLASS_MEMBERS(SwatchConfig)
+
+ int need_reconfigure;
+
+ VFrame *temp;
+ VFrame *src_temp;
+ SwatchEngine *engine;
+};
+
+class SwatchPackage : public LoadPackage
+{
+public:
+ SwatchPackage();
+ int y1;
+ int y2;
+};
+
+class SwatchUnit : public LoadClient
+{
+public:
+ SwatchUnit(SwatchEngine *server, SwatchMain *plugin);
+ void process_package(LoadPackage *package);
+ SwatchEngine *server;
+ SwatchMain *plugin;
+ YUV yuv;
+};
+
+class SwatchEngine : public LoadServer
+{
+public:
+ SwatchEngine(SwatchMain *plugin, int total_clients, int total_packages);
+ void init_packages();
+
+ void draw_pattern();
+ void draw_src();
+
+ LoadClient* new_client();
+ LoadPackage* new_package();
+ SwatchMain *plugin;
+ int mode;
+};
+
+
+
+#endif
$(call if_npkg,libwebp,--disable-webp)
twolame.cfg_params?=--enable-shared=no
x264.cfg_params?= --enable-static --enable-pic
-x265.cfg_vars?=$(call cmake_config,source)
-x265.cfg_params?= -DENABLE_SHARED=no -DENABLE_CLI=no
+x265.cfg_vars?=chmod +x ./configure; chmod +x ./multilib.sh;
+#x265.cfg_vars?=$(call cmake_config,source)
+#x265.cfg_params?= -DENABLE_SHARED=no -DENABLE_CLI=no
libvpx.cfg_params?= --enable-pic --disable-avx512 --enable-vp9-highbitdepth --disable-examples --disable-unit_tests
libdpx.cfg_vars?= libtoolize; aclocal; autoconf; automake -a;
libsndfile.cfg_params+= --disable-shared --enable-static --disable-sqlite --disable-mpeg
#https://www.cybercom.net/~dcoffin/dcraw/dcraw.c (moved to next line for 9.28 2018/06/01)
-# dcraw.c.xz is checked into GIT at version 9.27; should be 9.28 cuz that is what dcraw.C is based on (/mnt0)
+# dcraw.c.xz is at 2018, internal version of 1.478
https://www.dechifro.org/dcraw/dcraw.c
-http://download-mirror.savannah.gnu.org/releases//openexr/ilmbase-2.2.1.tar.gz
-http://gnu.mirrors.pair.com/savannah/savannah//openexr/openexr-2.2.1.tar.gz
+http://download-mirror.savannah.gnu.org/releases//openexr/ilmbase-2.4.1.tar.gz
+http://gnu.mirrors.pair.com/savannah/savannah//openexr/openexr-2.4.1.tar.gz
https://sourceforge.net/projects/opencvlibrary/files/ 4.3.0
https://github.com/opencv/opencv_contrib/releases 4.3.0
#https://sourceforge.net/projects/opencore-amr/files/fdk-aac/fdk-aac-2.2.0.tar.gz/download
https://mirrors.edge.kernel.org/pub/linux/libs/ieee1394/libiec61883-1.2.0.tar.xz
https://sourceforge.net/projects/libavc1394/files/latest/download?source=directory = 0.5.4
https://sourceforge.net/projects/libdv/files/latest/download?source=directory = 0.104
-https://sourceforge.net/projects/giflib/files/latest/download = 5.2.1
-https://github.com/xiph/flac/releases/tag/1.4.2
-https://github.com/uclouvain/openjpeg/ = Releases=sourcefiles openjpeg-2.5.0.tar.gz
+https://sourceforge.net/projects/giflib/files/latest/download = 5.2.2
+https://github.com/xiph/flac/releases/tag/1.4.3
+https://github.com/uclouvain/openjpeg/ = Releases=sourcefiles openjpeg-2.5.2.tar.gz
https://sourceforge.net/projects/libjpeg-turbo/ = Code (GitHub)=sourcefiles/2.1.2/libjpeg-turbo-2.1.5.1.tar.gz
https://sourceforge.net/projects/mjpeg/files/mjpegtools/2.2.1/
http://www.fftw.org/fftw-3.3.10.tar.gz
http://downloads.xiph.org/releases/vorbis/libvorbis-1.3.7.tar.xz
http://downloads.xiph.org/releases/ogg/libogg-1.3.5.tar.gz
http://downloads.xiph.org/releases/theora/libtheora-1.1.1.tar.bz2
+# Added 0.7.4 in at least 2016; no new updates; not in HV or CV
+https://repology.org/project/a52dec/information
https://sourceforge.net/projects/lame/files/latest/download?source=directory = 3.100
-https://download.osgeo.org/libtiff/tiff-4.6.0.tar.xz
+https://download.osgeo.org/libtiff/tiff-4.6.0t.tar.xz
https://sourceforge.net/projects/libuuid/files/latest/download?source=directory - 1.0.3
-https://code.videolan.org/videolan/x264/-/tree/stable/x264-stable.tar.gz (Jan. 2023 version r3106)
+https://www.videolan.org/developers/x264.html (May 2024 version r3191)
https://bitbucket.org/multicoreware/x265_git/downloads/x265_3.5.tar.gz (snapshot 17122023)
-https://ffmpeg.org/releases/ffmpeg-6.1.tar.bz2
+https://ffmpeg.org/releases/ffmpeg-7.0.tar.xz
https://github.com/webmproject/libvpx/archive/v1.13.1.tar.gz
https://code.videolan.org/videolan/dav1d/-/archive/0.5.1/dav1d-0.5.1.tar.gz
+https://gitlab.com/AOMediaCodec/SVT-AV1/-/releases (2.2.1)
https://github.com/swh/ladspa/releases/tag/v0.4.17, plugin.org.uk
https://archive.mozilla.org/pub/opus/opus-1.3.1.tar.gz
#https://github.com/webmproject/libwebp = libwebp-1.3.2
--- /dev/null
+--- a/fftools/cmdutils.c
++++ b/fftools/cmdutils.c
+@@ -60,7 +60,7 @@
+ AVDictionary *swr_opts;
+ AVDictionary *format_opts, *codec_opts;
+
+-int hide_banner = 0;
++int hide_banner = 1;
+
+ void uninit_opts(void)
+ {
--- /dev/null
+--- a/libavutil/hwcontext_cuda.c
++++ b/libavutil/hwcontext_cuda.c
+@@ -363,11 +363,13 @@
+ hwctx->internal->cuda_device));
+ if (ret < 0)
+ return ret;
++#if 0
+ } else if (flags & AV_CUDA_USE_CURRENT_CONTEXT) {
+ ret = CHECK_CU(cu->cuCtxGetCurrent(&hwctx->cuda_ctx));
+ if (ret < 0)
+ return ret;
+ av_log(device_ctx, AV_LOG_INFO, "Using current CUDA context.\n");
++#endif
+ } else {
+ ret = CHECK_CU(cu->cuCtxCreate(&hwctx->cuda_ctx, desired_flags,
+ hwctx->internal->cuda_device));
--- /dev/null
+--- a/libavformat/mpegtsenc.c
++++ b/libavformat/mpegtsenc.c
+@@ -89,9 +89,11 @@
+ int64_t pat_period; /* PAT/PMT period in PCR time base */
+ int64_t nit_period; /* NIT period in PCR time base */
+ int nb_services;
+- int64_t first_pcr;
+ int first_dts_checked;
+- int64_t next_pcr;
++ int64_t pcr_pos, pcr;
++ int64_t first_pcr, next_pcr;
++ int64_t delay;
++ int pcr_stream_pid;
+ int mux_rate; ///< set to 1 when VBR
+ int pes_payload_size;
+ int64_t total_size;
+@@ -258,7 +260,7 @@
+ int data_st_warning;
+
+ int64_t pcr_period; /* PCR period in PCR time base */
+- int64_t last_pcr;
++ int64_t pcr_timer;
+
+ /* For Opus */
+ int opus_queued_samples;
+@@ -959,18 +961,18 @@
+ return 0;
+ }
+
+-static int64_t get_pcr(const MpegTSWrite *ts)
++static int64_t get_pcr(const MpegTSWrite *ts, AVIOContext *pb)
+ {
+- return av_rescale(ts->total_size + 11, 8 * PCR_TIME_BASE, ts->mux_rate) +
+- ts->first_pcr;
++ int64_t pos = avio_tell(pb) + 11;
++ return ts->pcr + (ts->mux_rate == 1 ? (pos - ts->pcr_pos) * 8 :
++ av_rescale(pos - ts->pcr_pos, 8 * PCR_TIME_BASE, ts->mux_rate));
+ }
+
+ static void write_packet(AVFormatContext *s, const uint8_t *packet)
+ {
+ MpegTSWrite *ts = s->priv_data;
+ if (ts->m2ts_mode) {
+- int64_t pcr = get_pcr(s->priv_data);
+- uint32_t tp_extra_header = pcr % 0x3fffffff;
++ uint32_t tp_extra_header = get_pcr(ts, s->pb) % 0x3fffffff;
+ tp_extra_header = AV_RB32(&tp_extra_header);
+ avio_write(s->pb, (unsigned char *) &tp_extra_header,
+ sizeof(tp_extra_header));
+@@ -1056,9 +1058,6 @@
+ else
+ ts_st->pcr_period = 1;
+ }
+-
+- // output a PCR as soon as possible
+- ts_st->last_pcr = ts->first_pcr - ts_st->pcr_period;
+ }
+
+ static void select_pcr_streams(AVFormatContext *s)
+@@ -1121,6 +1120,7 @@
+
+ if (s->max_delay < 0) /* Not set by the caller */
+ s->max_delay = 0;
++ ts->delay = av_rescale(s->max_delay, 90000, AV_TIME_BASE);
+
+ // round up to a whole number of TS packets
+ ts->pes_payload_size = (ts->pes_payload_size + 14 + 183) / 184 * 184 - 14;
+@@ -1180,7 +1180,9 @@
+ /* MPEG pid values < 16 are reserved. Applications which set st->id in
+ * this range are assigned a calculated pid. */
+ if (st->id < 16) {
+- if (ts->m2ts_mode) {
++ if (ts->start_pid >= 0)
++ ts_st->pid = ts->start_pid + i;
++ else if (ts->m2ts_mode) {
+ switch (st->codecpar->codec_type) {
+ case AVMEDIA_TYPE_VIDEO:
+ ts_st->pid = ts->m2ts_video_pid++;
+@@ -1207,9 +1209,9 @@
+ av_log(s, AV_LOG_ERROR, "Cannot automatically assign PID for stream %d\n", st->index);
+ return AVERROR(EINVAL);
+ }
+- } else {
+- ts_st->pid = ts->start_pid + i;
+ }
++ else
++ ts_st->pid = START_PID + i;
+ } else {
+ ts_st->pid = st->id;
+ }
+@@ -1277,9 +1279,14 @@
+ ts->last_pat_ts = AV_NOPTS_VALUE;
+ ts->last_sdt_ts = AV_NOPTS_VALUE;
+ ts->last_nit_ts = AV_NOPTS_VALUE;
+- ts->pat_period = av_rescale(ts->pat_period_us, PCR_TIME_BASE, AV_TIME_BASE);
+- ts->sdt_period = av_rescale(ts->sdt_period_us, PCR_TIME_BASE, AV_TIME_BASE);
+- ts->nit_period = av_rescale(ts->nit_period_us, PCR_TIME_BASE, AV_TIME_BASE);
++ ts->pat_period = ts->pat_period_us < 0 ? -1 :
++ av_rescale(ts->pat_period_us, PCR_TIME_BASE, AV_TIME_BASE);
++ ts->sdt_period = ts->sdt_period_us < 0 ? -1 :
++ av_rescale(ts->sdt_period_us, PCR_TIME_BASE, AV_TIME_BASE);
++ ts->nit_period = ts->nit_period_us < 0 ? -1 :
++ av_rescale(ts->nit_period_us, PCR_TIME_BASE, AV_TIME_BASE);
++ ts->pcr = 0;
++ ts->pcr_pos = 0;
+
+ /* assign provider name */
+ provider = av_dict_get(s->metadata, "service_provider", NULL, 0);
+@@ -1295,8 +1302,8 @@
+ av_log(s, AV_LOG_VERBOSE, "muxrate %d, ", ts->mux_rate);
+ av_log(s, AV_LOG_VERBOSE,
+ "sdt every %"PRId64" ms, pat/pmt every %"PRId64" ms",
+- av_rescale(ts->sdt_period, 1000, PCR_TIME_BASE),
+- av_rescale(ts->pat_period, 1000, PCR_TIME_BASE));
++ ts->sdt_period < 0 ? -1 : av_rescale(ts->sdt_period, 1000, PCR_TIME_BASE),
++ ts->pat_period < 0 ? -1 : av_rescale(ts->pat_period, 1000, PCR_TIME_BASE));
+ if (ts->flags & MPEGTS_FLAG_NIT)
+ av_log(s, AV_LOG_VERBOSE, ", nit every %"PRId64" ms", av_rescale(ts->nit_period, 1000, PCR_TIME_BASE));
+ av_log(s, AV_LOG_VERBOSE, "\n");
+@@ -1305,36 +1312,40 @@
+ }
+
+ /* send SDT, NIT, PAT and PMT tables regularly */
+-static void retransmit_si_info(AVFormatContext *s, int force_pat, int force_sdt, int force_nit, int64_t pcr)
++static void retransmit_si_info(AVFormatContext *s, int force_pat, int force_sdt, int force_nit)
+ {
+ MpegTSWrite *ts = s->priv_data;
+ int i;
+
+- if ((pcr != AV_NOPTS_VALUE && ts->last_sdt_ts == AV_NOPTS_VALUE) ||
+- (pcr != AV_NOPTS_VALUE && pcr - ts->last_sdt_ts >= ts->sdt_period) ||
+- force_sdt
+- ) {
+- if (pcr != AV_NOPTS_VALUE)
+- ts->last_sdt_ts = FFMAX(pcr, ts->last_sdt_ts);
+- mpegts_write_sdt(s);
+- }
+- if ((pcr != AV_NOPTS_VALUE && ts->last_pat_ts == AV_NOPTS_VALUE) ||
+- (pcr != AV_NOPTS_VALUE && pcr - ts->last_pat_ts >= ts->pat_period) ||
+- force_pat) {
+- if (pcr != AV_NOPTS_VALUE)
+- ts->last_pat_ts = FFMAX(pcr, ts->last_pat_ts);
+- mpegts_write_pat(s);
+- for (i = 0; i < ts->nb_services; i++)
+- mpegts_write_pmt(s, ts->services[i]);
+- }
+- if ((pcr != AV_NOPTS_VALUE && ts->last_nit_ts == AV_NOPTS_VALUE) ||
+- (pcr != AV_NOPTS_VALUE && pcr - ts->last_nit_ts >= ts->nit_period) ||
+- force_nit
+- ) {
+- if (pcr != AV_NOPTS_VALUE)
+- ts->last_nit_ts = FFMAX(pcr, ts->last_nit_ts);
++ if (ts->sdt_period >= 0) {
++ int64_t pcr = get_pcr(ts, s->pb);
++ if (ts->last_sdt_ts == AV_NOPTS_VALUE || pcr >= ts->last_sdt_ts + ts->sdt_period)
++ force_sdt = 1;
++ if (force_sdt) {
++ ts->last_sdt_ts = pcr;
++ mpegts_write_sdt(s);
++ }
++ }
++ if (ts->pat_period >= 0) {
++ int64_t pcr = get_pcr(ts, s->pb);
++ if (ts->last_pat_ts == AV_NOPTS_VALUE || pcr >= ts->last_pat_ts + ts->pat_period)
++ force_pat = 1;
++ if (force_pat) {
++ ts->last_pat_ts = pcr;
++ mpegts_write_pat(s);
++ for (i = 0; i < ts->nb_services; i++)
++ mpegts_write_pmt(s, ts->services[i]);
++ }
++ }
++ if (ts->nit_period >= 0) {
++ int64_t pcr = get_pcr(ts, s->pb);
++ if (ts->last_nit_ts == AV_NOPTS_VALUE || pcr >= ts->last_nit_ts + ts->nit_period)
++ force_nit = 1;
++ if (force_nit) {
++ ts->last_nit_ts = pcr;
+ if (ts->flags & MPEGTS_FLAG_NIT)
+ mpegts_write_nit(s);
++ }
+ }
+ }
+
+@@ -1371,25 +1382,29 @@
+ static void mpegts_insert_pcr_only(AVFormatContext *s, AVStream *st)
+ {
+ MpegTSWrite *ts = s->priv_data;
+- MpegTSWriteStream *ts_st = st->priv_data;
++ int64_t pcr = get_pcr(ts, s->pb);
++ MpegTSWriteStream *ts_st = st ? st->priv_data : 0;
++ uint32_t pcr_pid = ts_st ? ts_st->pid : ts->pcr_stream_pid;
+ uint8_t *q;
+ uint8_t buf[TS_PACKET_SIZE];
+
+ q = buf;
+ *q++ = 0x47;
+- *q++ = ts_st->pid >> 8;
+- *q++ = ts_st->pid;
+- *q++ = 0x20 | ts_st->cc; /* Adaptation only */
++ *q++ = pcr_pid >> 8;
++ *q++ = pcr_pid;
++ uint32_t flags = 0x20; /* Adaptation only */
+ /* Continuity Count field does not increment (see 13818-1 section 2.4.3.3) */
++ if(ts_st) flags |= ts_st->cc;
++ *q++ = flags;
+ *q++ = TS_PACKET_SIZE - 5; /* Adaptation Field Length */
+ *q++ = 0x10; /* Adaptation flags: PCR present */
+- if (ts_st->discontinuity) {
++ if (ts_st && ts_st->discontinuity) {
+ q[-1] |= 0x80;
+ ts_st->discontinuity = 0;
+ }
+
+ /* PCR coded into 6 bytes */
+- q += write_pcr_bits(q, get_pcr(ts));
++ q += write_pcr_bits(q, pcr);
+
+ /* stuffing bytes */
+ memset(q, 0xFF, TS_PACKET_SIZE - (q - buf));
+@@ -1490,9 +1505,9 @@
+ int afc_len, stuffing_len;
+ int is_dvb_subtitle = (st->codecpar->codec_id == AV_CODEC_ID_DVB_SUBTITLE);
+ int is_dvb_teletext = (st->codecpar->codec_id == AV_CODEC_ID_DVB_TELETEXT);
+- int64_t delay = av_rescale(s->max_delay, 90000, AV_TIME_BASE);
+ int force_pat = st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && key && !ts_st->prev_payload_key;
+ int force_sdt = 0;
++ int64_t pcr;
+ int force_nit = 0;
+
+ av_assert0(ts_st->payload != buf || st->codecpar->codec_type != AVMEDIA_TYPE_VIDEO);
+@@ -1509,21 +1524,19 @@
+
+ is_start = 1;
+ while (payload_size > 0) {
+- int64_t pcr = AV_NOPTS_VALUE;
+- if (ts->mux_rate > 1)
+- pcr = get_pcr(ts);
+- else if (dts != AV_NOPTS_VALUE)
+- pcr = (dts - delay) * 300;
+-
+- retransmit_si_info(s, force_pat, force_sdt, force_nit, pcr);
+- force_pat = 0;
+- force_sdt = 0;
+- force_nit = 0;
++ // add 11, pcr references the last byte of program clock reference base
++ ts->pcr_pos = avio_tell(s->pb) + 11;
++ pcr = ts->pcr = ts->mux_rate != 1 ?
++ av_rescale(ts->pcr_pos, 8 * PCR_TIME_BASE, ts->mux_rate) :
++ (dts == AV_NOPTS_VALUE ? 0 : (dts - ts->delay) * 300);
++ if (force_pat || force_sdt || force_nit) {
++ retransmit_si_info(s, force_pat, force_sdt, force_nit);
++ force_pat = force_sdt = force_nit = 0;
++ }
+
+ write_pcr = 0;
+ if (ts->mux_rate > 1) {
+ /* Send PCR packets for all PCR streams if needed */
+- pcr = get_pcr(ts);
+ if (pcr >= ts->next_pcr) {
+ int64_t next_pcr = INT64_MAX;
+ for (int i = 0; i < s->nb_streams; i++) {
+@@ -1533,36 +1546,43 @@
+ AVStream *st2 = s->streams[st2_index];
+ MpegTSWriteStream *ts_st2 = st2->priv_data;
+ if (ts_st2->pcr_period) {
+- if (pcr - ts_st2->last_pcr >= ts_st2->pcr_period) {
+- ts_st2->last_pcr = FFMAX(pcr - ts_st2->pcr_period, ts_st2->last_pcr + ts_st2->pcr_period);
+- if (st2 != st) {
++ if (pcr >= ts_st2->pcr_timer) {
++ ts_st2->pcr_timer = pcr + ts_st2->pcr_period;
++ if (st2 != st) {
+ mpegts_insert_pcr_only(s, st2);
+- pcr = get_pcr(ts);
+ } else {
+ write_pcr = 1;
+ }
+ }
+- next_pcr = FFMIN(next_pcr, ts_st2->last_pcr + ts_st2->pcr_period);
++ next_pcr = FFMIN(next_pcr, ts_st2->pcr_timer);
+ }
+ }
+ ts->next_pcr = next_pcr;
+ }
+- if (dts != AV_NOPTS_VALUE && (dts - pcr / 300) > delay) {
+- /* pcr insert gets priority over null packet insert */
+- if (write_pcr)
+- mpegts_insert_pcr_only(s, st);
+- else
+- mpegts_insert_null_packet(s);
+- /* recalculate write_pcr and possibly retransmit si_info */
+- continue;
+- }
+- } else if (ts_st->pcr_period && pcr != AV_NOPTS_VALUE) {
+- if (pcr - ts_st->last_pcr >= ts_st->pcr_period && is_start) {
+- ts_st->last_pcr = FFMAX(pcr - ts_st->pcr_period, ts_st->last_pcr + ts_st->pcr_period);
++ }
++ else if (ts_st->pcr_period) {
++ if (pcr >= ts_st->pcr_timer) {
++ ts_st->pcr_timer = pcr + ts_st->pcr_period;
+ write_pcr = 1;
+ }
+ }
+
++ if (write_pcr && ts->pcr_stream_pid >= 0) {
++ mpegts_insert_pcr_only(s, 0);
++ continue;
++ }
++
++ if (ts->mux_rate > 1 && dts != AV_NOPTS_VALUE &&
++ (dts - pcr / 300) > ts->delay) {
++ /* pcr insert gets priority over null packet insert */
++ if (write_pcr)
++ mpegts_insert_pcr_only(s, st);
++ else
++ mpegts_insert_null_packet(s);
++ /* recalculate write_pcr and possibly retransimit si_info */
++ continue;
++ }
++
+ /* prepare packet header */
+ q = buf;
+ *q++ = 0x47;
+@@ -1592,7 +1612,6 @@
+ if (write_pcr) {
+ set_af_flag(buf, 0x10);
+ q = get_ts_payload_start(buf);
+- // add 11, pcr references the last byte of program clock reference base
+ if (dts != AV_NOPTS_VALUE && dts < pcr / 300)
+ av_log(s, AV_LOG_WARNING, "dts < pcr, TS is invalid\n");
+ extend_af(buf, write_pcr_bits(q, pcr));
+@@ -1864,8 +1883,8 @@
+ uint8_t *data = NULL;
+ MpegTSWrite *ts = s->priv_data;
+ MpegTSWriteStream *ts_st = st->priv_data;
+- const int64_t delay = av_rescale(s->max_delay, 90000, AV_TIME_BASE) * 2;
+- const int64_t max_audio_delay = av_rescale(s->max_delay, 90000, AV_TIME_BASE) / 2;
++ const int64_t delay_ticks2 = ts->delay * 2;
++ const int64_t max_audio_delay = ts->delay / 2;
+ int64_t dts = pkt->dts, pts = pkt->pts;
+ int opus_samples = 0;
+ size_t side_data_size;
+@@ -1885,9 +1904,9 @@
+
+ if (ts->copyts < 1) {
+ if (pts != AV_NOPTS_VALUE)
+- pts += delay;
++ pts += delay_ticks2;
+ if (dts != AV_NOPTS_VALUE)
+- dts += delay;
++ dts += delay_ticks2;
+ }
+
+ if (!ts_st->first_timestamp_checked && (pts == AV_NOPTS_VALUE || dts == AV_NOPTS_VALUE)) {
+@@ -2354,8 +2373,10 @@
+ 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_SERVICE_TYPE_HEVC_DIGITAL_HDTV }, 0x01, 0xff, ENC, .unit = "mpegts_service_type" },
+ { "mpegts_pmt_start_pid", "Set the first pid of the PMT.",
+ OFFSET(pmt_start_pid), AV_OPT_TYPE_INT, { .i64 = 0x1000 }, FIRST_OTHER_PID, LAST_OTHER_PID, ENC },
++ { "mpegts_pcr_stream_pid", "create seperate PCR stream on this pid.",
++ OFFSET(pcr_stream_pid), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, 0x1f00, ENC },
+ { "mpegts_start_pid", "Set the first pid.",
+- OFFSET(start_pid), AV_OPT_TYPE_INT, { .i64 = 0x0100 }, FIRST_OTHER_PID, LAST_OTHER_PID, ENC },
++ OFFSET(start_pid), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, LAST_OTHER_PID, ENC },
+ { "mpegts_m2ts_mode", "Enable m2ts mode.", OFFSET(m2ts_mode), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, ENC },
+ { "muxrate", NULL, OFFSET(mux_rate), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, INT_MAX, ENC },
+ { "pes_payload_size", "Minimum PES packet payload in bytes",
+@@ -2381,10 +2402,10 @@
+ OFFSET(omit_video_pes_length), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, ENC },
+ { "pcr_period", "PCR retransmission time in milliseconds",
+ OFFSET(pcr_period_ms), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, ENC },
+- { "pat_period", "PAT/PMT retransmission time limit in seconds",
++ { "pat_period", "PAT/PMT retransmission time limit in ms, -1 no pat",
+ OFFSET(pat_period_us), AV_OPT_TYPE_DURATION, { .i64 = PAT_RETRANS_TIME * 1000LL }, 0, INT64_MAX, ENC },
+- { "sdt_period", "SDT retransmission time limit in seconds",
+- OFFSET(sdt_period_us), AV_OPT_TYPE_DURATION, { .i64 = SDT_RETRANS_TIME * 1000LL }, 0, INT64_MAX, ENC },
++ { "sdt_period", "SDT retransmission time limit in ms, -1 no sdt",
++ OFFSET(sdt_period_us), AV_OPT_TYPE_INT64, { .i64 = SDT_RETRANS_TIME * 1000LL }, -1, INT64_MAX, ENC },
+ { "nit_period", "NIT retransmission time limit in seconds",
+ OFFSET(nit_period_us), AV_OPT_TYPE_DURATION, { .i64 = NIT_RETRANS_TIME * 1000LL }, 0, INT64_MAX, ENC },
+ { NULL },
+--- a/libavformat/mpegts.h
++++ b/libavformat/mpegts.h
+@@ -64,6 +64,7 @@
+ /* PID from 0x1FFC to 0x1FFE may be assigned as needed to PMT, elementary
+ * streams and other data tables */
+ #define NULL_PID 0x1FFF /* Null packet (used for fixed bandwidth padding) */
++#define START_PID 0x0400
+
+ /* m2ts pids */
+ #define M2TS_PMT_PID 0x0100
+--- a/libavformat/bluray.c
++++ b/libavformat/bluray.c
+@@ -27,7 +27,7 @@
+ #include "libavutil/opt.h"
+
+ #define BLURAY_PROTO_PREFIX "bluray:"
+-#define MIN_PLAYLIST_LENGTH 180 /* 3 min */
++#define MIN_PLAYLIST_LENGTH 0
+
+ typedef struct {
+ const AVClass *class;
+
+--- a/doc/muxers.texi
++++ b/doc/muxers.texi
+@@ -2920,7 +2920,8 @@
+ Maximum time in seconds between PAT/PMT tables. Default is @code{0.1}.
+
+ @item sdt_period @var{duration}
+-Maximum time in seconds between SDT tables. Default is @code{0.5}.
++Maximum time in seconds between SDT tables. Default is @code{0.5}. Regardless
++of this setting no SDT is written in m2ts mode.
+
+ @item nit_period @var{duration}
+ Maximum time in seconds between NIT tables. Default is @code{0.5}.
--- /dev/null
+--- a/libavformat/avformat.h
++++ b/libavformat/avformat.h
+@@ -499,6 +499,9 @@
+ The user or muxer can override this through
+ AVFormatContext.avoid_negative_ts
+ */
++#define AVFMT_SEEK_NOSTREAMS 0x80000 /**< Stream index ignored by seek,
++ or some streams fail to seek
++ */
+
+ #define AVFMT_SEEK_TO_PTS 0x4000000 /**< Seeking is based on PTS */
+
+@@ -562,7 +565,8 @@
+ /**
+ * Can use flags: AVFMT_NOFILE, AVFMT_NEEDNUMBER, AVFMT_SHOW_IDS,
+ * AVFMT_NOTIMESTAMPS, AVFMT_GENERIC_INDEX, AVFMT_TS_DISCONT, AVFMT_NOBINSEARCH,
+- * AVFMT_NOGENSEARCH, AVFMT_NO_BYTE_SEEK, AVFMT_SEEK_TO_PTS.
++ * AVFMT_NOGENSEARCH, AVFMT_NO_BYTE_SEEK, AVFMT_SEEK_TO_PTS,
++ * AVFMT_SEEK_NOSTREAMS
+ */
+ int flags;
+
+--- a/libavformat/dv.c
++++ b/libavformat/dv.c
+@@ -713,6 +713,7 @@
+ const FFInputFormat ff_dv_demuxer = {
+ .p.name = "dv",
+ .p.long_name = NULL_IF_CONFIG_SMALL("DV (Digital Video)"),
++ .p.flags = AVFMT_SEEK_NOSTREAMS,
+ .p.extensions = "dv,dif",
+ .priv_data_size = sizeof(RawDVContext),
+ .read_probe = dv_probe,
+
+--- a/libavformat/matroskadec.c
++++ b/libavformat/matroskadec.c
+@@ -4794,6 +4794,7 @@
+ const FFInputFormat ff_webm_dash_manifest_demuxer = {
+ .p.name = "webm_dash_manifest",
+ .p.long_name = NULL_IF_CONFIG_SMALL("WebM DASH Manifest"),
++ .p.flags = AVFMT_SEEK_NOSTREAMS,
+ .p.priv_class = &webm_dash_class,
+ .priv_data_size = sizeof(MatroskaDemuxContext),
+ .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP,
+@@ -4806,6 +4807,7 @@
+ const FFInputFormat ff_matroska_demuxer = {
+ .p.name = "matroska,webm",
+ .p.long_name = NULL_IF_CONFIG_SMALL("Matroska / WebM"),
++ .p.flags = AVFMT_SEEK_NOSTREAMS,
+ .p.extensions = "mkv,mk3d,mka,mks,webm",
+ .p.mime_type = "audio/webm,audio/x-matroska,video/webm,video/x-matroska",
+ .priv_data_size = sizeof(MatroskaDemuxContext),
+
+--- a/libavformat/seek.c
++++ b/libavformat/seek.c
+@@ -605,6 +605,13 @@
+ return seek_frame_byte(s, stream_index, timestamp, flags);
+ }
+
++ if (stream_index != -1 && (s->iformat->flags & AVFMT_SEEK_NOSTREAMS)) {
++ timestamp = av_rescale_q(timestamp,
++ s->streams[stream_index]->time_base,
++ AV_TIME_BASE_Q);
++ stream_index = -1;
++ }
++
+ if (stream_index < 0) {
+ stream_index = av_find_default_stream_index(s);
+ if (stream_index < 0)
--- /dev/null
+--- a/libavformat/avidec.c
++++ b/libavformat/avidec.c
+@@ -2020,6 +2020,7 @@
+ .p.name = "avi",
+ .p.long_name = NULL_IF_CONFIG_SMALL("AVI (Audio Video Interleaved)"),
+ .p.extensions = "avi",
++ .p.flags = AVFMT_SEEK_NOSTREAMS,
+ .p.priv_class = &demuxer_class,
+ .priv_data_size = sizeof(AVIContext),
+ .flags_internal = FF_INFMT_FLAG_INIT_CLEANUP,
--- /dev/null
+--- a/libavfilter/formats.c
++++ b/libavfilter/formats.c
+@@ -110,11 +110,13 @@
+ possibly causing a lossy conversion elsewhere in the graph.
+ To avoid that, pretend that there are no common formats to force the
+ insertion of a conversion filter. */
+- if (type == AVMEDIA_TYPE_VIDEO)
++ if (type == AVMEDIA_TYPE_VIDEO) {
+ for (i = 0; i < a->nb_formats; i++) {
+ const AVPixFmtDescriptor *const adesc = av_pix_fmt_desc_get(a->formats[i]);
++ if( !adesc ) continue;
+ for (j = 0; j < b->nb_formats; j++) {
+ const AVPixFmtDescriptor *bdesc = av_pix_fmt_desc_get(b->formats[j]);
++ if( !bdesc ) continue;
+ alpha2 |= adesc->flags & bdesc->flags & AV_PIX_FMT_FLAG_ALPHA;
+ chroma2|= adesc->nb_components > 1 && bdesc->nb_components > 1;
+ if (a->formats[i] == b->formats[j]) {
+@@ -123,6 +125,7 @@
+ }
+ }
+ }
++ }
+
+ // If chroma or alpha can be lost through merging then do not merge
+ if (alpha2 > alpha1 || chroma2 > chroma1)
--- /dev/null
+--- a/libavcodec/vdpau_mpeg12.c
++++ b/libavcodec/vdpau_mpeg12.c
+@@ -117,6 +117,7 @@
+ .frame_priv_data_size = sizeof(struct vdpau_picture_context),
+ .init = vdpau_mpeg1_init,
+ .uninit = ff_vdpau_common_uninit,
++ .frame_params = ff_vdpau_common_frame_params,
+ .priv_data_size = sizeof(VDPAUContext),
+ .caps_internal = HWACCEL_CAP_ASYNC_SAFE,
+ };
--- /dev/null
+--- a/libavcodec/h263dec.c
++++ b/libavcodec/h263dec.c
+@@ -623,7 +623,7 @@
+ if (CONFIG_MPEG4_DECODER && avctx->codec_id == AV_CODEC_ID_MPEG4)
+ ff_mpeg4_frame_end(avctx, buf, buf_size);
+
+- if (!s->divx_packed && avctx->hwaccel)
++ if (s->divx_packed && avctx->hwaccel)
+ ff_thread_finish_setup(avctx);
+
+ av_assert1(s->current_picture.f->pict_type == s->current_picture_ptr->f->pict_type);
--- /dev/null
+--- a/libavformat/mpegenc.c
++++ b/libavformat/mpegenc.c
+@@ -987,9 +987,9 @@
+ PacketDesc *pkt_desc;
+
+ while ((pkt_desc = stream->predecode_packet) &&
++ pkt_desc != stream->premux_packet &&
+ scr > pkt_desc->dts) { // FIXME: > vs >=
+- if (stream->buffer_index < pkt_desc->size ||
+- stream->predecode_packet == stream->premux_packet) {
++ if (stream->buffer_index < pkt_desc->size) {
+ av_log(ctx, AV_LOG_ERROR,
+ "buffer underflow st=%d bufi=%d size=%d\n",
+ i, stream->buffer_index, pkt_desc->size);
--- /dev/null
+--- a/libavutil/hwcontext_vdpau.c
++++ b/libavutil/hwcontext_vdpau.c
+@@ -47,6 +47,11 @@
+ { 0, AV_PIX_FMT_NONE, },
+ };
+
++static const VDPAUPixFmtMap pix_fmts_420j[] = {
++ { VDP_YCBCR_FORMAT_YV12, AV_PIX_FMT_YUVJ420P },
++ { 0, AV_PIX_FMT_NONE, },
++};
++
+ static const VDPAUPixFmtMap pix_fmts_422[] = {
+ { VDP_YCBCR_FORMAT_NV12, AV_PIX_FMT_NV16 },
+ { VDP_YCBCR_FORMAT_YV12, AV_PIX_FMT_YUV422P },
+@@ -71,6 +76,7 @@
+ const VDPAUPixFmtMap *map;
+ } vdpau_pix_fmts[] = {
+ { VDP_CHROMA_TYPE_420, AV_PIX_FMT_YUV420P, pix_fmts_420 },
++ { VDP_CHROMA_TYPE_420, AV_PIX_FMT_YUVJ420P, pix_fmts_420j },
+ { VDP_CHROMA_TYPE_422, AV_PIX_FMT_YUV422P, pix_fmts_422 },
+ { VDP_CHROMA_TYPE_444, AV_PIX_FMT_YUV444P, pix_fmts_444 },
+ #ifdef VDP_YCBCR_FORMAT_P016
--- /dev/null
+--- a/libavcodec/encode.c
++++ b/libavcodec/encode.c
+@@ -320,7 +320,7 @@
+ }
+
+ if (!frame->buf[0]) {
+- if (!(avctx->codec->capabilities & AV_CODEC_CAP_DELAY ||
++ if (avci->draining && !(avctx->codec->capabilities & AV_CODEC_CAP_DELAY ||
+ avci->frame_thread_encoder))
+ return AVERROR_EOF;
+
+@@ -339,8 +339,10 @@
+ ret = ff_encode_encode_cb(avctx, avpkt, frame, &got_packet);
+ }
+
+- if (avci->draining && !got_packet)
++ if (avci->draining && !got_packet) {
++ fflush(stderr);
+ avci->draining_done = 1;
++ }
+
+ return ret;
+ }
+@@ -515,10 +517,16 @@
+ if (avci->draining)
+ return AVERROR_EOF;
+
+- if (avci->buffer_frame->buf[0])
++ if (avci->buffer_frame->buf[0]) {
++ if (!frame) {
++ fflush(stderr);
++ av_frame_unref(avci->buffer_frame);
++ }
+ return AVERROR(EAGAIN);
++ }
+
+ if (!frame) {
++ fflush(stderr);
+ avci->draining = 1;
+ } else {
+ ret = encode_send_frame_internal(avctx, frame);
--- /dev/null
+--- a/libavcodec/pcm-dvdenc.c
++++ b/libavcodec/pcm-dvdenc.c
+@@ -38,6 +38,12 @@
+ int quant, freq, frame_size;
+
+ switch (avctx->sample_rate) {
++ case 32000:
++ freq = 3;
++ break;
++ case 44100:
++ freq = 2;
++ break;
+ case 48000:
+ freq = 0;
+ break;
+@@ -181,7 +187,7 @@
+ .priv_data_size = sizeof(PCMDVDContext),
+ .init = pcm_dvd_encode_init,
+ FF_CODEC_ENCODE_CB(pcm_dvd_encode_frame),
+- .p.supported_samplerates = (const int[]) { 48000, 96000, 0},
++ .p.supported_samplerates = (const int[]) { 32000, 44100, 48000, 96000, 0},
+ .p.ch_layouts = (const AVChannelLayout[]) { AV_CHANNEL_LAYOUT_MONO,
+ AV_CHANNEL_LAYOUT_STEREO,
+ AV_CHANNEL_LAYOUT_5POINT1,
--- /dev/null
+--- a/libavcodec/wrapped_avframe.c
++++ b/libavcodec/wrapped_avframe.c
+@@ -33,6 +33,38 @@
+ #include "libavutil/buffer.h"
+ #include "libavutil/pixdesc.h"
+
++
++
++static const enum AVPixelFormat pix_fmts_all[] = {
++ AV_PIX_FMT_YUV411P,
++ AV_PIX_FMT_YUV420P,
++ AV_PIX_FMT_YUVJ420P,
++ AV_PIX_FMT_YUV422P,
++ AV_PIX_FMT_YUVJ422P,
++ AV_PIX_FMT_YUV444P,
++ AV_PIX_FMT_YUVJ444P,
++ AV_PIX_FMT_YUV420P10,
++ AV_PIX_FMT_YUV422P10,
++ AV_PIX_FMT_YUV444P10,
++ AV_PIX_FMT_YUV420P12,
++ AV_PIX_FMT_YUV422P12,
++ AV_PIX_FMT_YUV444P12,
++ AV_PIX_FMT_YUV420P14,
++ AV_PIX_FMT_YUV422P14,
++ AV_PIX_FMT_YUV444P14,
++ AV_PIX_FMT_YUV420P16,
++ AV_PIX_FMT_YUV422P16,
++ AV_PIX_FMT_YUV444P16,
++ AV_PIX_FMT_GRAY8,
++ AV_PIX_FMT_GRAY9,
++ AV_PIX_FMT_GRAY10,
++ AV_PIX_FMT_GRAY12,
++ AV_PIX_FMT_GRAY16,
++ AV_PIX_FMT_NONE
++};
++
++
++
+ static void wrapped_avframe_release_buffer(void *unused, uint8_t *data)
+ {
+ AVFrame *frame = (AVFrame *)data;
+@@ -111,6 +143,7 @@
+ .p.id = AV_CODEC_ID_WRAPPED_AVFRAME,
+ .p.capabilities = AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE,
+ FF_CODEC_ENCODE_CB(wrapped_avframe_encode),
++ .p.pix_fmts = pix_fmts_all,
+ };
+
+ const FFCodec ff_wrapped_avframe_decoder = {
--- /dev/null
+--- a/libavformat/yuv4mpegenc.c
++++ b/libavformat/yuv4mpegenc.c
+@@ -268,7 +268,7 @@
+ av_log(s, AV_LOG_ERROR, "'%s' is not an official yuv4mpegpipe pixel format. "
+ "Use '-strict -1' to encode to this pixel format.\n",
+ av_get_pix_fmt_name(s->streams[0]->codecpar->format));
+- return AVERROR(EINVAL);
++ //return AVERROR(EINVAL);
+ }
+ av_log(s, AV_LOG_WARNING, "Warning: generating non standard YUV stream. "
+ "Mjpegtools will not work.\n");
--- /dev/null
+--- a/Makefile
++++ b/Makefile
+@@ -6,7 +6,7 @@
+ # of code space in the shared library.
+
+ #
+-OFLAGS = -O0 -g
++#OFLAGS = -O0 -g
+ OFLAGS = -O2
+ CFLAGS = -std=gnu99 -fPIC -Wall -Wno-format-truncation $(OFLAGS)
+
+@@ -90,7 +90,7 @@
+ all: $(LIBGIFSO) libgif.a $(LIBUTILSO) libutil.a $(UTILS)
+ ifeq ($(UNAME), Darwin)
+ else
+- $(MAKE) -C doc
++# $(MAKE) -C doc
+ endif
+
+ $(UTILS):: libgif.a libutil.a
--- /dev/null
+--- a/quantize.c
++++ b/quantize.c
+# SortRGBAxis is static and not locked, qsort recoded
+# GAErrorToken is also static and not locked, not fixed
+@@ -25,8 +25,6 @@
+ #define BITS_PER_PRIM_COLOR 5
+ #define MAX_PRIM_COLOR 0x1f
+
+-static int SortRGBAxis;
+-
+ typedef struct QuantizedColorType {
+ GifByteType RGB[3];
+ GifByteType NewColorIndex;
+@@ -34,6 +32,40 @@
+ struct QuantizedColorType *Pnext;
+ } QuantizedColorType;
+
++static int QCmpr(QuantizedColorType *a, QuantizedColorType *b, int i)
++{
++ int i0 = i, i1 = i+1, i2 = i+2;
++ if( i1 >= 3 ) i1 -= 3;
++ if( i2 >= 3 ) i2 -= 3;
++ /* sort on all axes of the color space! */
++ int hash_a = (a->RGB[i0] << 16) | (a->RGB[i1] << 8) | (a->RGB[i2] << 0);
++ int hash_b = (b->RGB[i0] << 16) | (b->RGB[i1] << 8) | (b->RGB[i2] << 0);
++ return hash_a - hash_b;
++}
++
++static int QSplit(QuantizedColorType **q, int l, int r, int i)
++{
++ int m;
++ QuantizedColorType *t;
++ for(;;) {
++ while( QCmpr(q[r],q[l], i) >= 0 ) if( ++l == r ) return r;
++ t = q[l]; q[l] = q[r]; q[r] = t; m = l; l = r; r = m;
++ while( QCmpr(q[l],q[r], i) >= 0 ) if( r == --l ) return r;
++ t = q[l]; q[l] = q[r]; q[r] = t; m = l; l = r; r = m;
++ }
++}
++
++static void QSort(QuantizedColorType **q, int ll, int rr, int i)
++{
++ for(;;) {
++ int l = ll+1; if( l == rr ) return;
++ int r = rr-1; if( l == r ) return;
++ int m = QSplit(q, l, r, i);
++ QSort(q, ll, m, i);
++ ll = m;
++ }
++}
++
+ typedef struct NewColorMapType {
+ GifByteType RGBMin[3], RGBWidth[3];
+ unsigned int
+@@ -45,7 +77,6 @@
+ static int SubdivColorMap(NewColorMapType *NewColorSubdiv,
+ unsigned int ColorMapSize,
+ unsigned int *NewColorMapSize);
+-static int SortCmpRtn(const void *Entry1, const void *Entry2);
+
+ /******************************************************************************
+ Quantize high resolution image into lower one. Input image consists of a
+@@ -215,6 +246,7 @@
+ unsigned int ColorMapSize,
+ unsigned int *NewColorMapSize) {
+
++ int SortRGBAxis = 0;
+ unsigned int i, j, Index = 0;
+ QuantizedColorType *QuantizedColor, **SortArray;
+
+@@ -257,19 +289,7 @@
+ SortArray[j] = QuantizedColor;
+ }
+
+- /*
+- * Because qsort isn't stable, this can produce differing
+- * results for the order of tuples depending on platform
+- * details of how qsort() is implemented.
+- *
+- * We mitigate this problem by sorting on all three axes rather
+- * than only the one specied by SortRGBAxis; that way the
+- * instability can only become an issue if there are multiple
+- * color indices referring to identical RGB tuples. Older
+- * versions of this sorted on only the one axis.
+- */
+- qsort(SortArray, NewColorSubdiv[Index].NumEntries,
+- sizeof(QuantizedColorType *), SortCmpRtn);
++ QSort(SortArray, -1, NewColorSubdiv[Index].NumEntries, SortRGBAxis);
+
+ /* Relink the sorted list into one: */
+ for (j = 0; j < NewColorSubdiv[Index].NumEntries - 1; j++) {
+@@ -339,19 +359,4 @@
+ Routine called by qsort to compare two entries.
+ *****************************************************************************/
+
+-static int SortCmpRtn(const void *Entry1, const void *Entry2) {
+- QuantizedColorType *entry1 = (*((QuantizedColorType **)Entry1));
+- QuantizedColorType *entry2 = (*((QuantizedColorType **)Entry2));
+-
+- /* sort on all axes of the color space! */
+- int hash1 = entry1->RGB[SortRGBAxis] * 256 * 256 +
+- entry1->RGB[(SortRGBAxis + 1) % 3] * 256 +
+- entry1->RGB[(SortRGBAxis + 2) % 3];
+- int hash2 = entry2->RGB[SortRGBAxis] * 256 * 256 +
+- entry2->RGB[(SortRGBAxis + 1) % 3] * 256 +
+- entry2->RGB[(SortRGBAxis + 2) % 3];
+-
+- return hash1 - hash2;
+-}
+-
+ /* end */
--- /dev/null
+--- libsvtav1-v2.2.1/CMakeLists.txt
++++ libsvtav1-v2.2.1/CMakeLists.txt
+@@ -9,7 +9,7 @@
+ # PATENTS file, you can obtain it at https://www.aomedia.org/license/patent-license.
+ #
+
+-cmake_minimum_required(VERSION 3.16)
++cmake_minimum_required(VERSION 3.12)
+
+ if("${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_BINARY_DIR}")
+ message(WARNING "Building in-source is highly not recommended\n"
--- /dev/null
+--- ./Makefile.am
++++ ./Makefile.am
+@@ -60,7 +60,7 @@
+ rm -rf $(distdir)/_build/cmake
+ rm -rf $(distdir)/_inst/cmake
+
+-SUBDIRS = port libtiff tools build contrib test doc
++SUBDIRS = port libtiff build
+
+
+ NEW_LIBTIFF_RELEASE_DATE=$(shell date +"%Y%m%d")
--- /dev/null
+--- ./configure.ac
++++ ./configure.ac
+@@ -873,6 +873,7 @@
+ if test "x$enable_webp" != "xno" ; then
+
+ if test "x$with_webp_lib_dir" != "x" ; then
++ LIBS="-lpthread $LIBS"
+ LDFLAGS="-L$with_webp_lib_dir $LDFLAGS"
+ fi
+
--- /dev/null
+--- a/encoder/encoder.c 2023-04-01 19:08:58.420132123 -0600
++++ b/encoder/encoder.c 2023-04-01 18:52:48.148808059 -0600
+@@ -667,6 +667,7 @@
+ return -1;
+ }
+
++#if 0
+ /* Detect default ffmpeg settings and terminate with an error. */
+ if( b_open )
+ {
+@@ -690,6 +691,7 @@
+ return -1;
+ }
+ }
++#endif
+
+ if( h->param.rc.i_rc_method < 0 || h->param.rc.i_rc_method > 2 )
+ {
--- /dev/null
+--- /dev/null 2020-03-14 06:02:18.586124011 +0300
++++ ./configure 2020-03-18 00:04:59.360807192 +0300
+@@ -0,0 +1 @@
++/bin/true
--- /dev/null
+--- /dev/null 2020-03-14 06:02:18.586124011 +0300
++++ ./Makefile 2020-03-18 00:04:59.388807329 +0300
+@@ -0,0 +1,4 @@
++#$(shell cd build/linux ; ./multilib.sh)
++.NOTPARALLEL:
++all:
++ $(shell ./multilib.sh ; cp 8bit/libx265.a . ; cp 8bit/x265.pc . ; cp 8bit/x265_config.h .)
--- /dev/null
+--- /dev/null 2020-07-19 09:07:01.788494015 +0300
++++ ./multilib.sh 2020-08-02 02:34:58.444933214 +0300
+@@ -0,0 +1,54 @@
++#!/bin/sh
++
++mkdir -p 8bit 10bit 12bit
++
++
++cd 12bit
++if [ $(uname -m) == 'x86_64' ]; then
++ # 64-bit stuff here
++cmake ../source -DHIGH_BIT_DEPTH=ON -DENABLE_ASSEMBLY=ON -DEXPORT_C_API=OFF -DENABLE_SHARED=OFF -DENABLE_CLI=OFF -DMAIN12=ON
++else
++ # 32-bit stuff here
++cmake ../source -DHIGH_BIT_DEPTH=ON -DENABLE_ASSEMBLY=OFF -DEXPORT_C_API=OFF -DENABLE_SHARED=OFF -DENABLE_CLI=OFF -DMAIN12=ON
++fi
++make
++
++cd ../10bit
++if [ $(uname -m) == 'x86_64' ]; then
++ # 64-bit stuff here
++cmake ../source -DHIGH_BIT_DEPTH=ON -DENABLE_ASSEMBLY=ON -DEXPORT_C_API=OFF -DENABLE_SHARED=OFF -DENABLE_CLI=OFF
++else
++ # 32-bit stuff here
++cmake ../source -DHIGH_BIT_DEPTH=ON -DENABLE_ASSEMBLY=OFF -DEXPORT_C_API=OFF -DENABLE_SHARED=OFF -DENABLE_CLI=OFF
++fi
++make
++
++cd ../8bit
++ln -sf ../10bit/libx265.a libx265_main10.a
++ln -sf ../12bit/libx265.a libx265_main12.a
++cmake ../source -DEXTRA_LIB="x265_main10.a;x265_main12.a" -DENABLE_SHARED=OFF -DEXTRA_LINK_FLAGS=-L. -DLINKED_10BIT=ON -DLINKED_12BIT=ON -DENABLE_CLI=OFF
++make
++
++# rename the 8bit library, then combine all three into libx265.a
++mv libx265.a libx265_main.a
++
++uname=`uname`
++if [ "$uname" = "Linux" ]
++then
++
++# On Linux, we use GNU ar to combine the static libraries together
++ar -M <<EOF
++CREATE libx265.a
++ADDLIB libx265_main.a
++ADDLIB libx265_main10.a
++ADDLIB libx265_main12.a
++SAVE
++END
++EOF
++
++else
++
++# Mac/BSD libtool
++libtool -static -o libx265.a libx265_main.a libx265_main10.a libx265_main12.a 2>/dev/null
++
++fi