From: Good Guy Date: Mon, 17 Jun 2024 20:29:06 +0000 (-0600) Subject: Credit Andrew - fix bug in render farm usage when using in/out pointers or selection X-Git-Url: https://cinelerra-gg.org/git/?a=commitdiff_plain;h=HEAD;hp=24a693bee267965065f80d8b96409aedb6477165;p=goodguy%2Fcinelerra.git Credit Andrew - fix bug in render farm usage when using in/out pointers or selection --- diff --git a/cinelerra-5.1/cinelerra/bdwrite.C b/cinelerra-5.1/cinelerra/bdwrite.C index 1b864bb9..1f7f027e 100644 --- a/cinelerra-5.1/cinelerra/bdwrite.C +++ b/cinelerra-5.1/cinelerra/bdwrite.C @@ -2675,7 +2675,7 @@ int media_info::scan() 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; } diff --git a/cinelerra-5.1/cinelerra/ffmpeg.C b/cinelerra-5.1/cinelerra/ffmpeg.C index 6685f468..9b8832dd 100644 --- a/cinelerra-5.1/cinelerra/ffmpeg.C +++ b/cinelerra-5.1/cinelerra/ffmpeg.C @@ -880,13 +880,17 @@ void FFAudioStream::init_swr(int ichs, int ifmt, int irate) 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<= 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; chnb_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) @@ -1004,7 +1017,7 @@ int FFAudioStream::load(int64_t pos, int len) while( ret>=0 && !flushed && curr_pos=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; } @@ -2780,14 +2793,14 @@ int FFMPEG::open_decoder() 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; chchannels; ++ch ) astrm_index.append(ffidx(aidx, ch)); aud->sample_rate = avpar->sample_rate; @@ -2943,10 +2956,13 @@ int FFMPEG::open_encoder(const char *type, const char *spec) 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; chchannels; ++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); @@ -2958,10 +2974,12 @@ int FFMPEG::open_encoder(const char *type, const char *spec) 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; @@ -3929,7 +3947,7 @@ int FFAudioStream::create_filter(const char *filter_spec) 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"); @@ -3947,8 +3965,8 @@ int FFAudioStream::create_filter(const char *filter_spec) 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), @@ -4200,7 +4218,7 @@ printf("audio%d pad %jd %jd (%jd)\n", aud->idx, pos, aud->curr_pos, pos-aud->cur } 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); diff --git a/cinelerra-5.1/cinelerra/fileac3.C b/cinelerra-5.1/cinelerra/fileac3.C index dd3b4536..63654f32 100644 --- a/cinelerra-5.1/cinelerra/fileac3.C +++ b/cinelerra-5.1/cinelerra/fileac3.C @@ -142,24 +142,39 @@ int FileAC3::open_file(int rd, int wr) 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); } } @@ -314,8 +329,9 @@ int FileAC3::write_samples(double **buffer, int64_t len) 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 + @@ -323,7 +339,7 @@ int FileAC3::write_samples(double **buffer, int64_t len) 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 ; diff --git a/cinelerra-5.1/cinelerra/fileffmpeg.C b/cinelerra-5.1/cinelerra/fileffmpeg.C index 2fbf6c86..abed22da 100644 --- a/cinelerra-5.1/cinelerra/fileffmpeg.C +++ b/cinelerra-5.1/cinelerra/fileffmpeg.C @@ -1493,7 +1493,7 @@ int FFOptions_Opt::types(char *rp) case AV_OPT_TYPE_SAMPLE_FMT: cp = N_(""); break; case AV_OPT_TYPE_DURATION: cp = N_(""); break; case AV_OPT_TYPE_COLOR: cp = N_(""); break; - case AV_OPT_TYPE_CHANNEL_LAYOUT: cp = N_(""); break; + case AV_OPT_TYPE_CHLAYOUT: cp = N_(""); break; case AV_OPT_TYPE_BOOL: cp = N_(""); break; default: cp = N_(""); break; } diff --git a/cinelerra-5.1/cinelerra/packagingengine.C b/cinelerra-5.1/cinelerra/packagingengine.C index 222f8d30..7b3336da 100644 --- a/cinelerra-5.1/cinelerra/packagingengine.C +++ b/cinelerra-5.1/cinelerra/packagingengine.C @@ -89,71 +89,94 @@ int PackagingEngineDefault::create_packages_single_farm(EDL *edl, 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 *path_list) diff --git a/cinelerra-5.1/cinelerra/pluginfclient.C b/cinelerra-5.1/cinelerra/pluginfclient.C index 2d57743c..b8694b12 100644 --- a/cinelerra-5.1/cinelerra/pluginfclient.C +++ b/cinelerra-5.1/cinelerra/pluginfclient.C @@ -453,7 +453,7 @@ int PluginFClient_Opt::types(char *rp) case AV_OPT_TYPE_SAMPLE_FMT: cp = ""; break; case AV_OPT_TYPE_DURATION: cp = ""; break; case AV_OPT_TYPE_COLOR: cp = ""; break; - case AV_OPT_TYPE_CHANNEL_LAYOUT: cp = ""; break; + case AV_OPT_TYPE_CHLAYOUT: cp = ""; break; default: cp = ""; break; } return sprintf(rp, "%s", cp); @@ -664,7 +664,7 @@ PluginFClient::~PluginFClient() 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; @@ -672,7 +672,7 @@ bool PluginFClient::is_audio(const AVFilter *fp) 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; @@ -684,7 +684,7 @@ bool PluginFClient::is_audio(const AVFilter *fp) 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; @@ -692,7 +692,7 @@ bool PluginFClient::is_video(const AVFilter *fp) 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; @@ -845,16 +845,24 @@ int PluginFAClient::activate() } AVSampleFormat sample_fmt = AV_SAMPLE_FMT_FLTP; int channels = PluginClient::total_in_buffers; - uint64_t layout = (((uint64_t)1)<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"), @@ -863,8 +871,8 @@ int PluginFAClient::activate() 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); @@ -990,9 +998,19 @@ int PluginFAClient::process_buffer(int64_t size, Samples **buffer, int64_t start 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<sample_rate = sample_rate; frame->pts = filter_position; } diff --git a/cinelerra-5.1/configure.ac b/cinelerra-5.1/configure.ac index 984019e8..8159da8f 100644 --- a/cinelerra-5.1/configure.ac +++ b/cinelerra-5.1/configure.ac @@ -223,7 +223,7 @@ PKG_3RD([esound],[no], [ . ]) PKG_3RD([ffmpeg],[yes], - [ffmpeg-6.1], + [ffmpeg-7.0], [ libavutil/libavutil.a \ libavcodec/libavcodec.a \ libpostproc/libpostproc.a \ diff --git a/cinelerra-5.1/doc/ContextManual.pl b/cinelerra-5.1/doc/ContextManual.pl index f3e25cdb..45d00dc8 100755 --- a/cinelerra-5.1/doc/ContextManual.pl +++ b/cinelerra-5.1/doc/ContextManual.pl @@ -17,7 +17,7 @@ # Several important definitions # ContextManual.pl script API version. Must not be changed ! -$cin_cm_api = 1; +$cin_cm_api = 2; # Web browser executable, can be redefined on user's demand $cin_browser = $ENV{'CIN_BROWSER'}; @@ -34,8 +34,9 @@ $cin_browser = 'firefox' if $cin_browser eq ''; # 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 = ( @@ -80,7 +81,6 @@ $cin_dat = '.' if $cin_dat eq ''; # 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 @@ -134,6 +134,21 @@ if ($help_key eq 'TOC') # 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 () + { + 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; } @@ -158,6 +173,21 @@ if ($help_key eq 'TOC') # 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 () + { + 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; } @@ -190,6 +220,21 @@ if ($node eq '') } } +# Index node will be needed now, find it +if ($node eq '' && $index_node eq '') +{ + seek CONTENTS, 0, 0; + while () + { + 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 '') { diff --git a/cinelerra-5.1/expanders.es b/cinelerra-5.1/expanders.es index 99c58389..5e5ada63 100644 --- a/cinelerra-5.1/expanders.es +++ b/cinelerra-5.1/expanders.es @@ -123,6 +123,8 @@ Video Effects Blue Banana Chroma key Chroma key (HSV) + Chroma key (Avid) + Color Swatch CriKey Difference key F_chromakey diff --git a/cinelerra-5.1/expanders.fr b/cinelerra-5.1/expanders.fr index 25ed8883..fa092c21 100644 --- a/cinelerra-5.1/expanders.fr +++ b/cinelerra-5.1/expanders.fr @@ -123,6 +123,8 @@ Video Effects Blue Banana Chroma key Chroma key (HSV) + Chroma key (Avid) + Color Swatch CriKey Difference key F_chromakey diff --git a/cinelerra-5.1/expanders.txt b/cinelerra-5.1/expanders.txt index 0e3ae841..898ade19 100644 --- a/cinelerra-5.1/expanders.txt +++ b/cinelerra-5.1/expanders.txt @@ -124,6 +124,8 @@ Video Effects Blue Banana Chroma key Chroma key (HSV) + Chroma key (Avid) + Color Swatch CriKey Difference key F_chromakey diff --git a/cinelerra-5.1/ffmpeg/plugin.opts b/cinelerra-5.1/ffmpeg/plugin.opts index 9fa5e12c..e9c35825 100644 --- a/cinelerra-5.1/ffmpeg/plugin.opts +++ b/cinelerra-5.1/ffmpeg/plugin.opts @@ -500,3 +500,7 @@ afdelaysrc #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 diff --git a/cinelerra-5.1/info/plugins.txt b/cinelerra-5.1/info/plugins.txt index daf71caf..145924a0 100644 --- a/cinelerra-5.1/info/plugins.txt +++ b/cinelerra-5.1/info/plugins.txt @@ -42,12 +42,17 @@ Chroma key: Erases pixels which match chosen color; 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. . diff --git a/cinelerra-5.1/msg/txt b/cinelerra-5.1/msg/txt index 48577fb9..053c04a4 100644 --- a/cinelerra-5.1/msg/txt +++ b/cinelerra-5.1/msg/txt @@ -3,6 +3,10 @@ For usage help, refer to the following: https://cinelerra-gg.org/download/CinelerraGG_Manual.pdf http://g-raffa.eu/Cinelerra/HOWTO/basics.html . +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. diff --git a/cinelerra-5.1/plugin_defs b/cinelerra-5.1/plugin_defs index cb3c700a..ded3fd70 100644 --- a/cinelerra-5.1/plugin_defs +++ b/cinelerra-5.1/plugin_defs @@ -34,6 +34,7 @@ video := \ C41 \ chromakey \ chromakeyhsv \ + chromakeyavid \ color3way \ colorbalance \ colorspace \ @@ -112,6 +113,7 @@ video := \ svg \ swapchannels \ swapframes \ + swatch \ threshold \ timeavg \ timeblur \ diff --git a/cinelerra-5.1/plugins/Makefile b/cinelerra-5.1/plugins/Makefile index 22aaf433..89f16466 100644 --- a/cinelerra-5.1/plugins/Makefile +++ b/cinelerra-5.1/plugins/Makefile @@ -40,6 +40,7 @@ DIRS = $(OPENCV_OBJS) \ chorus \ chromakey \ chromakeyhsv \ + chromakeyavid \ color3way \ colorbalance \ colorspace \ @@ -139,6 +140,7 @@ DIRS = $(OPENCV_OBJS) \ svg \ swapchannels \ swapframes \ + swatch \ synthesizer \ threshold \ timeavg \ diff --git a/cinelerra-5.1/plugins/chromakey/chromakey.h b/cinelerra-5.1/plugins/chromakey/chromakey.h index 9eacd284..b8a2cc22 100644 --- a/cinelerra-5.1/plugins/chromakey/chromakey.h +++ b/cinelerra-5.1/plugins/chromakey/chromakey.h @@ -197,7 +197,6 @@ public: ChromaKeyColor *color; - ChromaKeyThreshold *threshold; ChromaKeyFText *threshold_text; ChromaKeyFSlider *threshold_slider; ChromaKeyClr *threshold_Clr; @@ -205,7 +204,6 @@ public: ChromaKeyUseValue *use_value; ChromaKeyUseColorPicker *use_colorpicker; - ChromaKeySlope *slope; ChromaKeyFText *slope_text; ChromaKeyFSlider *slope_slider; ChromaKeyClr *slope_Clr; diff --git a/cinelerra-5.1/plugins/chromakeyavid/Makefile b/cinelerra-5.1/plugins/chromakeyavid/Makefile new file mode 100644 index 00000000..934f5fda --- /dev/null +++ b/cinelerra-5.1/plugins/chromakeyavid/Makefile @@ -0,0 +1,13 @@ +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 + diff --git a/cinelerra-5.1/plugins/chromakeyavid/chromakey.C b/cinelerra-5.1/plugins/chromakeyavid/chromakey.C new file mode 100644 index 00000000..a1324bd7 --- /dev/null +++ b/cinelerra-5.1/plugins/chromakeyavid/chromakey.C @@ -0,0 +1,1738 @@ +/* + * CINELERRA + * Copyright (C) 2012-2024 Adam Williams + * + * 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 +#include +#include + +#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 +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 (3, 1.0, 0, pkg); + break; + case BC_RGBA_FLOAT: + process_chromakey ( 4, 1.0, 0, pkg); + break; + case BC_RGB888: + process_chromakey ( 3, 0xff, 0, pkg); + break; + case BC_RGBA8888: + process_chromakey ( 4, 0xff, 0, pkg); + break; + case BC_YUV888: + process_chromakey ( 3, 0xff, 1, pkg); + break; + case BC_YUVA8888: + process_chromakey ( 4, 0xff, 1, pkg); + break; + case BC_YUV161616: + process_chromakey (3, 0xffff, 1, pkg); + break; + case BC_YUVA16161616: + process_chromakey (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; +} diff --git a/cinelerra-5.1/plugins/chromakeyavid/chromakey.h b/cinelerra-5.1/plugins/chromakeyavid/chromakey.h new file mode 100644 index 00000000..e39426ed --- /dev/null +++ b/cinelerra-5.1/plugins/chromakeyavid/chromakey.h @@ -0,0 +1,385 @@ + +/* + * CINELERRA + * Copyright (C) 2008 Adam Williams + * + * 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 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 diff --git a/cinelerra-5.1/plugins/chromakeyavid/chromakeyavid.sl b/cinelerra-5.1/plugins/chromakeyavid/chromakeyavid.sl new file mode 100644 index 00000000..2fd86da4 --- /dev/null +++ b/cinelerra-5.1/plugins/chromakeyavid/chromakeyavid.sl @@ -0,0 +1,230 @@ +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); +} + + diff --git a/cinelerra-5.1/plugins/swatch/Makefile b/cinelerra-5.1/plugins/swatch/Makefile new file mode 100644 index 00000000..4f1cb99b --- /dev/null +++ b/cinelerra-5.1/plugins/swatch/Makefile @@ -0,0 +1,8 @@ +include ../../plugin_defs + +PLUGIN = swatch +OBJS = $(OBJDIR)/swatch.o + +include ../../plugin_config + +$(OBJDIR)/swatch.o: swatch.C diff --git a/cinelerra-5.1/plugins/swatch/swatch.C b/cinelerra-5.1/plugins/swatch/swatch.C new file mode 100644 index 00000000..2e9142eb --- /dev/null +++ b/cinelerra-5.1/plugins/swatch/swatch.C @@ -0,0 +1,760 @@ +/* + * CINELERRA + * Copyright (C) 2024 Adam Williams + * + * 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 +#include +#include + +#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; +} + + + + + diff --git a/cinelerra-5.1/plugins/swatch/swatch.h b/cinelerra-5.1/plugins/swatch/swatch.h new file mode 100644 index 00000000..d4551c99 --- /dev/null +++ b/cinelerra-5.1/plugins/swatch/swatch.h @@ -0,0 +1,186 @@ +/* + * CINELERRA + * Copyright (C) 2024 Adam Williams + * + * 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 diff --git a/cinelerra-5.1/thirdparty/downloads.txt b/cinelerra-5.1/thirdparty/downloads.txt index 1d89f8f3..40b579ed 100644 --- a/cinelerra-5.1/thirdparty/downloads.txt +++ b/cinelerra-5.1/thirdparty/downloads.txt @@ -30,12 +30,14 @@ https://sourceforge.net/projects/twolame/ = GitHub / Releases = Source Code=twol 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://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://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://github.com/swh/ladspa/releases/tag/v0.4.17, plugin.org.uk diff --git a/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch1 b/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch1 new file mode 100644 index 00000000..c2cd10b7 --- /dev/null +++ b/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch1 @@ -0,0 +1,11 @@ +--- 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) + { diff --git a/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch10 b/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch10 new file mode 100644 index 00000000..a790fdd7 --- /dev/null +++ b/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch10 @@ -0,0 +1,16 @@ +--- 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)); diff --git a/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch2 b/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch2 new file mode 100644 index 00000000..fc9c74b7 --- /dev/null +++ b/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch2 @@ -0,0 +1,414 @@ +--- 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}. diff --git a/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch3 b/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch3 new file mode 100644 index 00000000..25b062bc --- /dev/null +++ b/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch3 @@ -0,0 +1,68 @@ +--- 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) diff --git a/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch4 b/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch4 new file mode 100644 index 00000000..9e472ce1 --- /dev/null +++ b/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch4 @@ -0,0 +1,10 @@ +--- 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, diff --git a/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch5 b/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch5 new file mode 100644 index 00000000..64ee262c --- /dev/null +++ b/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch5 @@ -0,0 +1,25 @@ +--- 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) diff --git a/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch7 b/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch7 new file mode 100644 index 00000000..379ddb62 --- /dev/null +++ b/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch7 @@ -0,0 +1,10 @@ +--- 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, + }; diff --git a/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch8 b/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch8 new file mode 100644 index 00000000..fcafebb8 --- /dev/null +++ b/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch8 @@ -0,0 +1,11 @@ +--- 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); diff --git a/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch9 b/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch9 new file mode 100644 index 00000000..721fd09d --- /dev/null +++ b/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patch9 @@ -0,0 +1,14 @@ +--- 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); diff --git a/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patchB b/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patchB new file mode 100644 index 00000000..89e99896 --- /dev/null +++ b/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patchB @@ -0,0 +1,22 @@ +--- 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 diff --git a/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patchC b/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patchC new file mode 100644 index 00000000..b89b3afb --- /dev/null +++ b/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patchC @@ -0,0 +1,41 @@ +--- 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); diff --git a/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patchD b/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patchD new file mode 100644 index 00000000..9cbef883 --- /dev/null +++ b/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patchD @@ -0,0 +1,24 @@ +--- 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, diff --git a/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patchZ1 b/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patchZ1 new file mode 100644 index 00000000..29cfb628 --- /dev/null +++ b/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patchZ1 @@ -0,0 +1,49 @@ +--- 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 = { diff --git a/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patchZ2 b/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patchZ2 new file mode 100644 index 00000000..a883b14d --- /dev/null +++ b/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.patchZ2 @@ -0,0 +1,11 @@ +--- 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"); diff --git a/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.tar.xz b/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.tar.xz new file mode 100644 index 00000000..5eff2741 Binary files /dev/null and b/cinelerra-5.1/thirdparty/src/ffmpeg-7.0.tar.xz differ