diff --git a/libaegisub/include/libaegisub/ycbcr_conv.h b/libaegisub/include/libaegisub/ycbcr_conv.h index 9eeb29343..73b44b3f4 100644 --- a/libaegisub/include/libaegisub/ycbcr_conv.h +++ b/libaegisub/include/libaegisub/ycbcr_conv.h @@ -17,6 +17,8 @@ #include #include +#include + namespace agi { enum class ycbcr_matrix { bt601, @@ -84,6 +86,11 @@ public: return to_uint8_t(prod(from_ycbcr, add(add(prod(to_ycbcr, input), shift_to), shift_from))); } + + Color rgb_to_rgb(Color c) const { + auto arr = rgb_to_rgb(std::array{{c.r, c.g, c.b}}); + return Color{arr[0], arr[1], arr[2]}; + } }; } diff --git a/src/ass_override.cpp b/src/ass_override.cpp index 59414b35d..2491ff6c7 100644 --- a/src/ass_override.cpp +++ b/src/ass_override.cpp @@ -294,10 +294,10 @@ static void load_protos() { proto[++i].Set("\\fr", VariableDataType::FLOAT); // \fr proto[++i].Set("\\fax", VariableDataType::FLOAT); // \fax proto[++i].Set("\\fay", VariableDataType::FLOAT); // \fay - proto[++i].Set("\\1c", VariableDataType::TEXT); // \1c&H& - proto[++i].Set("\\2c", VariableDataType::TEXT); // \2c&H& - proto[++i].Set("\\3c", VariableDataType::TEXT); // \3c&H& - proto[++i].Set("\\4c", VariableDataType::TEXT); // \4c&H& + proto[++i].Set("\\1c", VariableDataType::TEXT, AssParameterClass::COLOR); // \1c&H& + proto[++i].Set("\\2c", VariableDataType::TEXT, AssParameterClass::COLOR); // \2c&H& + proto[++i].Set("\\3c", VariableDataType::TEXT, AssParameterClass::COLOR); // \3c&H& + proto[++i].Set("\\4c", VariableDataType::TEXT, AssParameterClass::COLOR); // \4c&H& proto[++i].Set("\\1a", VariableDataType::TEXT, AssParameterClass::ALPHA); // \1a&H& proto[++i].Set("\\2a", VariableDataType::TEXT, AssParameterClass::ALPHA); // \2a&H& proto[++i].Set("\\3a", VariableDataType::TEXT, AssParameterClass::ALPHA); // \3a&H& @@ -312,7 +312,7 @@ static void load_protos() { proto[++i].Set("\\fs-", VariableDataType::FLOAT); // \fs- proto[++i].Set("\\fs", VariableDataType::FLOAT, AssParameterClass::ABSOLUTE_SIZE); // \fs proto[++i].Set("\\an", VariableDataType::INT); // \an - proto[++i].Set("\\c", VariableDataType::TEXT); // \c&H& + proto[++i].Set("\\c", VariableDataType::TEXT, AssParameterClass::COLOR); // \c&H& proto[++i].Set("\\b", VariableDataType::INT); // \b<0/1/weight> proto[++i].Set("\\i", VariableDataType::BOOL); // \i<0/1> proto[++i].Set("\\u", VariableDataType::BOOL); // \u<0/1> diff --git a/src/ass_override.h b/src/ass_override.h index d10b3b1d3..703bfa24d 100644 --- a/src/ass_override.h +++ b/src/ass_override.h @@ -49,7 +49,8 @@ enum class AssParameterClass { RELATIVE_TIME_END, KARAOKE, DRAWING, - ALPHA + ALPHA, + COLOR }; enum class VariableDataType { diff --git a/src/dialog_properties.cpp b/src/dialog_properties.cpp index c61f7619e..7518c2258 100644 --- a/src/dialog_properties.cpp +++ b/src/dialog_properties.cpp @@ -39,6 +39,7 @@ #include "help_button.h" #include "include/aegisub/context.h" #include "libresrc/libresrc.h" +#include "resolution_resampler.h" #include "validators.h" #include "video_context.h" @@ -90,15 +91,8 @@ DialogProperties::DialogProperties(agi::Context *c) res_sizer->Add(ResY, 1, wxRIGHT | wxALIGN_CENTER_VERTICAL, 5); res_sizer->Add(FromVideo, 1, 0, 0); - wxString matricies[] = { - "None", - "TV.601", "PC.601", - "TV.709", "PC.709", - "TV.FCC", "PC.FCC", - "TV.240M", "PC.240M" - }; YCbCrMatrix = new wxComboBox(this, -1, c->ass->GetScriptInfo("YCbCr Matrix"), - wxDefaultPosition, wxDefaultSize, boost::size(matricies), matricies, wxCB_READONLY); + wxDefaultPosition, wxDefaultSize, to_wx(MatrixNames()), wxCB_READONLY); auto matrix_sizer = new wxBoxSizer(wxHORIZONTAL); matrix_sizer->Add(new wxStaticText(this, -1, "YCbCr Matrix:"), wxSizerFlags().Center()); diff --git a/src/dialog_resample.cpp b/src/dialog_resample.cpp index 16f8e3c88..dc5049d38 100644 --- a/src/dialog_resample.cpp +++ b/src/dialog_resample.cpp @@ -20,8 +20,10 @@ #include "dialog_resample.h" #include "ass_file.h" -#include "include/aegisub/context.h" +#include "compat.h" #include "help_button.h" +#include "include/aegisub/context.h" +#include "include/aegisub/video_provider.h" #include "libresrc/libresrc.h" #include "resolution_resampler.h" #include "validators.h" @@ -29,6 +31,7 @@ #include #include +#include #include #include #include @@ -52,14 +55,18 @@ DialogResample::DialogResample(agi::Context *c, ResampleSettings &settings) c->ass->GetResolution(script_w, script_h); settings.source_x = script_w; settings.source_y = script_h; + settings.source_matrix = script_mat = MatrixFromString(c->ass->GetScriptInfo("YCbCr Matrix")); if (c->videoController->IsLoaded()) { settings.dest_x = video_w = c->videoController->GetWidth(); settings.dest_y = video_h = c->videoController->GetHeight(); + settings.dest_matrix = video_mat = MatrixFromString(c->videoController->GetProvider()->GetRealColorSpace()); } else { settings.dest_x = script_w; settings.dest_y = script_h; + settings.dest_matrix = script_mat; + video_mat = YCbCrMatrix::rgb; } // Create all controls and set validators @@ -76,13 +83,19 @@ DialogResample::DialogResample(agi::Context *c, ResampleSettings &settings) source_x = new wxSpinCtrl(this, -1, "", wxDefaultPosition, wxSize(50, -1), wxSP_ARROW_KEYS, 1, INT_MAX); source_y = new wxSpinCtrl(this, -1, "", wxDefaultPosition, wxSize(50, -1), wxSP_ARROW_KEYS, 1, INT_MAX); + source_matrix = new wxComboBox(this, -1, "", wxDefaultPosition, + wxDefaultSize, to_wx(MatrixNames()), wxCB_READONLY); dest_x = new wxSpinCtrl(this, -1, "", wxDefaultPosition, wxSize(50, -1), wxSP_ARROW_KEYS, 1, INT_MAX); dest_y = new wxSpinCtrl(this, -1, "", wxDefaultPosition, wxSize(50, -1), wxSP_ARROW_KEYS, 1, INT_MAX); + dest_matrix = new wxComboBox(this, -1, "", wxDefaultPosition, wxDefaultSize, + to_wx(MatrixNames()), wxCB_READONLY); source_x->SetValidator(wxGenericValidator(&settings.source_x)); source_y->SetValidator(wxGenericValidator(&settings.source_y)); + source_matrix->SetValidator(MakeEnumBinder(&settings.source_matrix)); dest_x->SetValidator(wxGenericValidator(&settings.dest_x)); dest_y->SetValidator(wxGenericValidator(&settings.dest_y)); + dest_matrix->SetValidator(MakeEnumBinder(&settings.dest_matrix)); from_video = new wxButton(this, -1, _("From &video")); from_video->Enable(false); @@ -108,25 +121,38 @@ DialogResample::DialogResample(agi::Context *c, ResampleSettings &settings) auto margin_box = new wxStaticBoxSizer(wxVERTICAL, this, _("Margin offset")); margin_box->Add(margin_sizer, wxSizerFlags(1).Expand().Border(wxBOTTOM)); - auto source_res_sizer = new wxStaticBoxSizer(wxHORIZONTAL, this, _("Source Resolution")); + auto source_res_sizer = new wxBoxSizer(wxHORIZONTAL); source_res_sizer->Add(source_x, wxSizerFlags(1).Border(wxRIGHT).Align(wxALIGN_CENTER_VERTICAL)); source_res_sizer->Add(new wxStaticText(this, -1, _("x")), wxSizerFlags().Center().Border(wxRIGHT)); source_res_sizer->Add(source_y, wxSizerFlags(1).Border(wxRIGHT).Align(wxALIGN_CENTER_VERTICAL)); source_res_sizer->Add(from_script, wxSizerFlags(1)); + auto source_matrix_sizer = new wxBoxSizer(wxHORIZONTAL); + source_matrix_sizer->Add(new wxStaticText(this, -1, _("YCbCr Matrix:")), wxSizerFlags().Border(wxRIGHT).Center()); + source_matrix_sizer->Add(source_matrix, wxSizerFlags(1).Center().Right()); + + auto source_res_box = new wxStaticBoxSizer(wxVERTICAL, this, _("Source Resolution")); + source_res_box->Add(source_res_sizer, wxSizerFlags(1).Expand().Border(wxBOTTOM)); + source_res_box->Add(source_matrix_sizer, wxSizerFlags(1).Expand()); + auto dest_res_sizer = new wxBoxSizer(wxHORIZONTAL); dest_res_sizer->Add(dest_x, wxSizerFlags(1).Border(wxRIGHT).Align(wxALIGN_CENTER_VERTICAL)); dest_res_sizer->Add(new wxStaticText(this, -1, _("x")), wxSizerFlags().Center().Border(wxRIGHT)); dest_res_sizer->Add(dest_y, wxSizerFlags(1).Border(wxRIGHT).Align(wxALIGN_CENTER_VERTICAL)); dest_res_sizer->Add(from_video, wxSizerFlags(1)); + auto dest_matrix_sizer = new wxBoxSizer(wxHORIZONTAL); + dest_matrix_sizer->Add(new wxStaticText(this, -1, _("YCbCr Matrix:")), wxSizerFlags().Border(wxRIGHT).Center()); + dest_matrix_sizer->Add(dest_matrix, wxSizerFlags(1).Center().Right()); + auto dest_res_box = new wxStaticBoxSizer(wxVERTICAL, this, _("Destination Resolution")); dest_res_box->Add(dest_res_sizer, wxSizerFlags(1).Expand().Border(wxBOTTOM)); + dest_res_box->Add(dest_matrix_sizer, wxSizerFlags(1).Expand()); auto main_sizer = new wxBoxSizer(wxVERTICAL); - main_sizer->Add(source_res_sizer, wxSizerFlags(0).Expand().Border()); - main_sizer->Add(dest_res_box, wxSizerFlags(0).Expand().Border()); - main_sizer->Add(ar_mode, wxSizerFlags(0).Expand().Border()); + main_sizer->Add(source_res_box, wxSizerFlags().Expand().Border()); + main_sizer->Add(dest_res_box, wxSizerFlags().Expand().Border()); + main_sizer->Add(ar_mode, wxSizerFlags().Expand().Border()); main_sizer->Add(margin_box, wxSizerFlags(1).Expand().Border()); main_sizer->Add(CreateStdDialogButtonSizer(wxOK | wxCANCEL | wxHELP), wxSizerFlags().Expand().Border(wxALL & ~wxTOP)); SetSizerAndFit(main_sizer); @@ -150,11 +176,13 @@ DialogResample::DialogResample(agi::Context *c, ResampleSettings &settings) void DialogResample::SetDestFromVideo(wxCommandEvent &) { dest_x->SetValue(video_w); dest_y->SetValue(video_h); + dest_matrix->SetSelection((int)video_mat); } void DialogResample::SetSourceFromScript(wxCommandEvent&) { source_x->SetValue(script_w); source_y->SetValue(script_h); + source_matrix->SetSelection((int)script_mat); } void DialogResample::UpdateButtons() { diff --git a/src/dialog_resample.h b/src/dialog_resample.h index 1f8f084c9..74ac43164 100644 --- a/src/dialog_resample.h +++ b/src/dialog_resample.h @@ -22,8 +22,10 @@ namespace agi { struct Context; } class AssFile; class wxCheckBox; +class wxComboBox; class wxRadioBox; class wxSpinCtrl; +enum class YCbCrMatrix : int; struct ResampleSettings; /// @class DialogResample @@ -35,13 +37,17 @@ class DialogResample final : public wxDialog { int script_w; int script_h; + YCbCrMatrix script_mat; int video_w = 0; int video_h = 0; + YCbCrMatrix video_mat; wxSpinCtrl *source_x; wxSpinCtrl *source_y; wxSpinCtrl *dest_x; wxSpinCtrl *dest_y; + wxComboBox *source_matrix; + wxComboBox *dest_matrix; wxCheckBox *symmetrical; wxRadioBox *ar_mode; wxSpinCtrl *margin_ctrl[4]; diff --git a/src/dialog_video_properties.cpp b/src/dialog_video_properties.cpp index b0a687881..b9dc872d6 100644 --- a/src/dialog_video_properties.cpp +++ b/src/dialog_video_properties.cpp @@ -134,9 +134,10 @@ bool UpdateVideoProperties(AssFile *file, const VideoProvider *new_provider, wxW // Fallthrough to prompt if the AR changed if (!ar_changed) { ResampleResolution(file, { - {0, 0, 0, 0}, - sx, sy, vx, vy, - ResampleARMode::Stretch + {0, 0, 0, 0}, + sx, sy, vx, vy, + ResampleARMode::Stretch, + YCbCrMatrix::rgb, YCbCrMatrix::rgb }); return true; } @@ -147,9 +148,10 @@ bool UpdateVideoProperties(AssFile *file, const VideoProvider *new_provider, wxW OPT_SET("Video/Last Script Resolution Mismatch Choice")->SetInt(res); ResampleResolution(file, { - {0, 0, 0, 0}, - sx, sy, vx, vy, - static_cast(res - FIX_RESAMPLE) + {0, 0, 0, 0}, + sx, sy, vx, vy, + static_cast(res - FIX_RESAMPLE), + YCbCrMatrix::rgb, YCbCrMatrix::rgb }); return true; } diff --git a/src/include/aegisub/video_provider.h b/src/include/aegisub/video_provider.h index 4265afdd7..8e6d05332 100644 --- a/src/include/aegisub/video_provider.h +++ b/src/include/aegisub/video_provider.h @@ -61,6 +61,7 @@ public: /// @return A string describing the source colorspace or "None" if it is /// unknown or meaningless virtual std::string GetColorSpace() const = 0; + virtual std::string GetRealColorSpace() const { return GetColorSpace(); } /// @brief Use this to set any post-loading warnings, such as "being loaded with unreliable seeking" virtual std::string GetWarning() const { return ""; } diff --git a/src/resolution_resampler.cpp b/src/resolution_resampler.cpp index 0d730e49a..e6a71ed84 100644 --- a/src/resolution_resampler.cpp +++ b/src/resolution_resampler.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -38,6 +39,30 @@ enum { BOTTOM = 3 }; +static const std::string names[] = { + "None", + "TV.601", "PC.601", + "TV.709", "PC.709", + "TV.FCC", "PC.FCC", + "TV.240M", "PC.240M" +}; + +YCbCrMatrix MatrixFromString(std::string const& str) { + if (str.empty()) return YCbCrMatrix::tv_601; + auto pos = std::find(std::begin(names), std::end(names), str); + if (pos == std::end(names)) + return YCbCrMatrix::rgb; + return static_cast(std::distance(std::begin(names), pos)); +} + +std::string MatrixToString(YCbCrMatrix mat) { + return names[static_cast(mat)]; +} + +std::vector MatrixNames() { + return {std::begin(names), std::end(names)}; +} + namespace { std::string transform_drawing(std::string const& drawing, int shift_x, int shift_y, double scale_x, double scale_y) { bool is_x = true; @@ -76,6 +101,8 @@ namespace { double rx; double ry; double ar; + agi::ycbcr_converter conv; + bool convert_colors; }; void resample_tags(std::string const& name, AssOverrideParameter *cur, void *ud) { @@ -113,6 +140,11 @@ namespace { return; } + case AssParameterClass::COLOR: + if (state->convert_colors) + cur->Set(state->conv.rgb_to_rgb(agi::Color{cur->Get()}).GetAssOverrideFormatted()); + return; + default: return; } @@ -152,8 +184,42 @@ namespace { style.scalex *= state->ar; for (int i = 0; i < 3; i++) style.Margin[i] = int((style.Margin[i] + state->margin[i]) * (i < 2 ? state->rx : state->ry) + 0.5); + if (state->convert_colors) { + style.primary = state->conv.rgb_to_rgb(style.primary); + style.secondary = state->conv.rgb_to_rgb(style.secondary); + style.outline = state->conv.rgb_to_rgb(style.outline); + style.shadow = state->conv.rgb_to_rgb(style.shadow); + } style.UpdateData(); } + + agi::ycbcr_matrix matrix(YCbCrMatrix mat) { + switch (mat) { + case YCbCrMatrix::rgb: return agi::ycbcr_matrix::bt601; + case YCbCrMatrix::tv_601: case YCbCrMatrix::pc_601: return agi::ycbcr_matrix::bt601; + case YCbCrMatrix::tv_709: case YCbCrMatrix::pc_709: return agi::ycbcr_matrix::bt709; + case YCbCrMatrix::tv_fcc: case YCbCrMatrix::pc_fcc: return agi::ycbcr_matrix::fcc; + case YCbCrMatrix::tv_240m: case YCbCrMatrix::pc_240m: return agi::ycbcr_matrix::smpte_240m; + } + throw agi::InternalError("Invalid matrix", nullptr); + } + + agi::ycbcr_range range(YCbCrMatrix mat) { + switch (mat) { + case YCbCrMatrix::rgb: + case YCbCrMatrix::tv_601: + case YCbCrMatrix::tv_709: + case YCbCrMatrix::tv_fcc: + case YCbCrMatrix::tv_240m: + return agi::ycbcr_range::tv; + case YCbCrMatrix::pc_601: + case YCbCrMatrix::pc_709: + case YCbCrMatrix::pc_fcc: + case YCbCrMatrix::pc_240m: + return agi::ycbcr_range::pc; + } + throw agi::InternalError("Invalid matrix", nullptr); + } } void ResampleResolution(AssFile *ass, ResampleSettings settings) { @@ -191,11 +257,23 @@ void ResampleResolution(AssFile *ass, ResampleSettings settings) { settings.source_x += settings.margin[LEFT] + settings.margin[RIGHT]; settings.source_y += settings.margin[TOP] + settings.margin[BOTTOM]; + bool resample_colors = + settings.source_matrix != settings.dest_matrix && + settings.source_matrix != YCbCrMatrix::rgb && + settings.dest_matrix != YCbCrMatrix::rgb; + resample_state state = { settings.margin, double(settings.dest_x) / double(settings.source_x), double(settings.dest_y) / double(settings.source_y), - horizontal_stretch + horizontal_stretch, + agi::ycbcr_converter{ + matrix(settings.source_matrix), + range(settings.source_matrix), + matrix(settings.dest_matrix), + range(settings.dest_matrix), + }, + resample_colors }; for (auto& line : ass->Styles) @@ -205,6 +283,7 @@ void ResampleResolution(AssFile *ass, ResampleSettings settings) { ass->SetScriptInfo("PlayResX", std::to_string(settings.dest_x)); ass->SetScriptInfo("PlayResY", std::to_string(settings.dest_y)); + ass->SetScriptInfo("YCbCr Matrix", MatrixToString(settings.dest_matrix)); ass->Commit(_("resolution resampling"), AssFile::COMMIT_SCRIPTINFO | AssFile::COMMIT_DIAG_FULL); } diff --git a/src/resolution_resampler.h b/src/resolution_resampler.h index 43b7211c7..f2975e87a 100644 --- a/src/resolution_resampler.h +++ b/src/resolution_resampler.h @@ -14,6 +14,9 @@ // // Aegisub Project http://www.aegisub.org/ +#include +#include + class AssFile; enum class ResampleARMode { @@ -23,6 +26,22 @@ enum class ResampleARMode { Manual }; +enum class YCbCrMatrix : int { + rgb, + tv_601, + pc_601, + tv_709, + pc_709, + tv_fcc, + pc_fcc, + tv_240m, + pc_240m +}; + +YCbCrMatrix MatrixFromString(std::string const& str); +std::string MatrixToString(YCbCrMatrix mat); +std::vector MatrixNames(); + /// Configuration parameters for a resample struct ResampleSettings { int margin[4]; ///< Amount to add to each margin @@ -31,6 +50,8 @@ struct ResampleSettings { int dest_x; ///< New X resolution int dest_y; ///< New Y resolution ResampleARMode ar_mode; ///< What to do if the old AR and new AR don't match + YCbCrMatrix source_matrix; + YCbCrMatrix dest_matrix; }; /// Resample the subtitles in the project diff --git a/src/validators.h b/src/validators.h index 9031909b5..64aacf020 100644 --- a/src/validators.h +++ b/src/validators.h @@ -79,7 +79,9 @@ class EnumBinder final : public wxValidator { bool Validate(wxWindow *) override { return true; } bool TransferFromWindow() override { - if (wxRadioBox *rb = dynamic_cast(GetWindow())) + if (auto rb = dynamic_cast(GetWindow())) + *value = static_cast(rb->GetSelection()); + else if (auto rb = dynamic_cast(GetWindow())) *value = static_cast(rb->GetSelection()); else throw agi::InternalError("Control type not supported by EnumBinder", nullptr); @@ -87,8 +89,10 @@ class EnumBinder final : public wxValidator { } bool TransferToWindow() override { - if (wxRadioBox *rb = dynamic_cast(GetWindow())) + if (auto rb = dynamic_cast(GetWindow())) rb->SetSelection(static_cast(*value)); + else if (auto cb = dynamic_cast(GetWindow())) + cb->SetSelection(static_cast(*value)); else throw agi::InternalError("Control type not supported by EnumBinder", nullptr); return true; diff --git a/src/video_provider_avs.cpp b/src/video_provider_avs.cpp index 6e548b926..9afeeb3c2 100644 --- a/src/video_provider_avs.cpp +++ b/src/video_provider_avs.cpp @@ -65,6 +65,7 @@ class AvisynthVideoProvider: public VideoProvider { std::vector keyframes; std::string warning; std::string colorspace; + std::string real_colorspace; PClip RGB32Video; VideoInfo vi; @@ -85,6 +86,7 @@ public: std::string GetWarning() const override { return warning; } std::string GetDecoderName() const override { return decoder_name; } std::string GetColorSpace() const override { return colorspace; } + std::string GetRealColorSpace() const override { return real_colorspace; } }; AvisynthVideoProvider::AvisynthVideoProvider(agi::fs::path const& filename, std::string const& colormatrix) { @@ -176,22 +178,24 @@ file_exit: throw VideoNotSupported("No usable video found"); vi = script.AsClip()->GetVideoInfo(); - if (!vi.IsRGB()) { + if (vi.IsRGB()) + real_colorspace = colorspace = "None"; + else { /// @todo maybe read ColorMatrix hints for d2v files? AVSValue args[2] = { script, "Rec601" }; bool force_bt601 = OPT_GET("Video/Force BT.601")->GetBool() || colormatrix == "TV.601"; bool bt709 = vi.width > 1024 || vi.height >= 600; if (bt709 && (!force_bt601 || colormatrix == "TV.709")) { args[1] = "Rec709"; - colorspace = "TV.709"; + real_colorspace = colorspace = "TV.709"; } - else + else { colorspace = "TV.601"; + real_colorspace = bt709 ? "TV.709" : "TV.601"; + } const char *argnames[2] = { 0, "matrix" }; script = avs.GetEnv()->Invoke("ConvertToRGB32", AVSValue(args, 2), argnames); } - else - colorspace = "None"; RGB32Video = avs.GetEnv()->Invoke("Cache", script).AsClip(); vi = RGB32Video->GetVideoInfo(); diff --git a/src/video_provider_cache.cpp b/src/video_provider_cache.cpp index 466a4e937..0633b98a9 100644 --- a/src/video_provider_cache.cpp +++ b/src/video_provider_cache.cpp @@ -68,6 +68,7 @@ public: std::string GetWarning() const override { return master->GetWarning(); } std::string GetDecoderName() const override { return master->GetDecoderName(); } std::string GetColorSpace() const override { return master->GetColorSpace(); } + std::string GetRealColorSpace() const override { return master->GetRealColorSpace(); } }; std::shared_ptr VideoProviderCache::GetFrame(int n) { diff --git a/src/video_provider_ffmpegsource.cpp b/src/video_provider_ffmpegsource.cpp index 6a03b1dc1..49f0fc9a5 100644 --- a/src/video_provider_ffmpegsource.cpp +++ b/src/video_provider_ffmpegsource.cpp @@ -62,6 +62,7 @@ class FFmpegSourceVideoProvider final : public VideoProvider, FFmpegSourceProvid std::vector KeyFramesList; ///< list of keyframes agi::vfr::Framerate Timecodes; ///< vfr object std::string ColorSpace; ///< Colorspace name + std::string RealColorSpace; ///< Colorspace name char FFMSErrMsg[1024]; ///< FFMS error message FFMS_ErrorInfo ErrInfo; ///< FFMS error codes/messages @@ -79,6 +80,7 @@ public: double GetDAR() const override { return DAR; } agi::vfr::Framerate GetFPS() const override { return Timecodes; } std::string GetColorSpace() const override { return ColorSpace; } + std::string GetRealColorSpace() const override { return RealColorSpace; } std::vector GetKeyFrames() const override { return KeyFramesList; }; std::string GetDecoderName() const override { return "FFmpegSource"; } bool WantsCaching() const override { return true; } @@ -91,7 +93,6 @@ std::string colormatrix_description(int cs, int cr) { switch (cs) { case FFMS_CS_RGB: return "None"; - break; case FFMS_CS_BT709: return str + ".709"; case FFMS_CS_FCC: @@ -227,7 +228,7 @@ void FFmpegSourceVideoProvider::LoadVideo(agi::fs::path const& filename, std::st auto CS = TempFrame->ColorSpace; if (CS == FFMS_CS_UNSPECIFIED) CS = Width > 1024 || Height >= 600 ? FFMS_CS_BT709 : FFMS_CS_BT470BG; - ColorSpace = colormatrix_description(CS, TempFrame->ColorRange); + RealColorSpace = ColorSpace = colormatrix_description(CS, TempFrame->ColorRange); #if FFMS_VERSION >= ((2 << 24) | (17 << 16) | (1 << 8) | 0) if (CS != FFMS_CS_RGB && CS != FFMS_CS_BT470BG && ColorSpace != colormatrix && (colormatrix == "TV.601" || OPT_GET("Video/Force BT.601")->GetBool())) {