Honor the color matrix tag from the subtitles file when appropriate
If the script's matrix matches the video's matrix, use that even if Force BT.601 is on, and if it's TV.601, use that even if Force BT.601 is off. This should mostly eliminate the common problems resulting from mixed values of the Force BT.601 option. Closes #1649.
This commit is contained in:
parent
4116f030af
commit
73e1dc1b60
15 changed files with 86 additions and 62 deletions
|
@ -68,8 +68,24 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
template<typename Base, typename Arg1=void>
|
||||
class Factory : public FactoryBase<Base *(*)(Arg1)> {
|
||||
template<typename Base, typename Arg1=void, typename Arg2=void>
|
||||
class Factory : public FactoryBase<Base *(*)(Arg1, Arg2)> {
|
||||
typedef Base *(*func)(Arg1, Arg2);
|
||||
|
||||
public:
|
||||
static std::unique_ptr<Base> Create(std::string const& name, Arg1 a1, Arg2 a2) {
|
||||
auto factory = FactoryBase<func>::Find(name);
|
||||
return factory ? std::unique_ptr<Base>(factory(a1, a2)) : nullptr;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
static void Register(std::string name, bool hide = false, std::vector<std::string> subTypes = std::vector<std::string>()) {
|
||||
FactoryBase<func>::DoRegister([](Arg1 a1, Arg2 a2) -> Base * { return new T(a1, a2); }, name, hide, subTypes);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Base, typename Arg1>
|
||||
class Factory<Base, Arg1, void> : public FactoryBase<Base *(*)(Arg1)> {
|
||||
typedef Base *(*func)(Arg1);
|
||||
|
||||
public:
|
||||
|
@ -85,7 +101,7 @@ public:
|
|||
};
|
||||
|
||||
template<typename Base>
|
||||
class Factory<Base, void> : public FactoryBase<Base *(*)()> {
|
||||
class Factory<Base, void, void> : public FactoryBase<Base *(*)()> {
|
||||
typedef Base *(*func)();
|
||||
|
||||
public:
|
||||
|
|
|
@ -582,6 +582,7 @@ void FrameMain::OnAudioClose() {
|
|||
|
||||
void FrameMain::OnSubtitlesOpen() {
|
||||
UpdateTitle();
|
||||
auto vc = context->videoController;
|
||||
|
||||
/// @todo figure out how to move this to the relevant controllers without
|
||||
/// prompting for each file loaded/unloaded
|
||||
|
@ -592,9 +593,9 @@ void FrameMain::OnSubtitlesOpen() {
|
|||
auto keyframes = config::path->MakeAbsolute(context->ass->GetScriptInfo("Keyframes File"), "?script");
|
||||
auto audio = config::path->MakeAbsolute(context->ass->GetScriptInfo("Audio URI"), "?script");
|
||||
|
||||
bool videoChanged = !blockVideoLoad && video != context->videoController->GetVideoName();
|
||||
bool timecodesChanged = vfr != context->videoController->GetTimecodesName();
|
||||
bool keyframesChanged = keyframes != context->videoController->GetKeyFramesName();
|
||||
bool videoChanged = !blockVideoLoad && video != vc->GetVideoName();
|
||||
bool timecodesChanged = vfr != vc->GetTimecodesName();
|
||||
bool keyframesChanged = keyframes != vc->GetKeyFramesName();
|
||||
bool audioChanged = !blockAudioLoad && audio != context->audioController->GetAudioURL();
|
||||
|
||||
// Check if there is anything to change
|
||||
|
@ -607,6 +608,8 @@ void FrameMain::OnSubtitlesOpen() {
|
|||
if (autoLoadMode == 2) {
|
||||
if (wxMessageBox(_("Do you want to load/unload the associated files?"), _("(Un)Load files?"), wxYES_NO | wxCENTRE, this) != wxYES) {
|
||||
SetDisplayMode(1, 1);
|
||||
if (vc->IsLoaded() && vc->GetProvider()->GetColorSpace() != context->ass->GetScriptInfo("YCbCr Matrix"))
|
||||
vc->Reload();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -616,20 +619,20 @@ void FrameMain::OnSubtitlesOpen() {
|
|||
|
||||
// Video
|
||||
if (videoChanged) {
|
||||
context->videoController->SetVideo(video);
|
||||
if (context->videoController->IsLoaded()) {
|
||||
context->videoController->JumpToFrame(context->ass->GetUIStateAsInt("Video Position"));
|
||||
vc->SetVideo(video);
|
||||
if (vc->IsLoaded()) {
|
||||
vc->JumpToFrame(context->ass->GetUIStateAsInt("Video Position"));
|
||||
|
||||
std::string arString = context->ass->GetUIState("Video Aspect Ratio");
|
||||
if (boost::starts_with(arString, "c")) {
|
||||
double ar = 0.;
|
||||
agi::util::try_parse(arString.substr(1), &ar);
|
||||
context->videoController->SetAspectRatio(ar);
|
||||
vc->SetAspectRatio(ar);
|
||||
}
|
||||
else {
|
||||
int ar = 0;
|
||||
if (agi::util::try_parse(arString, &ar) && ar >= 0 && ar < 4)
|
||||
context->videoController->SetAspectRatio((AspectRatio)ar);
|
||||
vc->SetAspectRatio((AspectRatio)ar);
|
||||
}
|
||||
|
||||
double videoZoom = 0.;
|
||||
|
@ -637,9 +640,11 @@ void FrameMain::OnSubtitlesOpen() {
|
|||
context->videoDisplay->SetZoom(videoZoom);
|
||||
}
|
||||
}
|
||||
else if (vc->IsLoaded() && vc->GetProvider()->GetColorSpace() != context->ass->GetScriptInfo("YCbCr Matrix"))
|
||||
vc->Reload();
|
||||
|
||||
context->videoController->LoadTimecodes(vfr);
|
||||
context->videoController->LoadKeyframes(keyframes);
|
||||
vc->LoadTimecodes(vfr);
|
||||
vc->LoadKeyframes(keyframes);
|
||||
|
||||
// Audio
|
||||
if (audioChanged) {
|
||||
|
|
|
@ -109,10 +109,10 @@ static std::unique_ptr<SubtitlesProvider> get_subs_provider(wxEvtHandler *parent
|
|||
}
|
||||
}
|
||||
|
||||
ThreadedFrameSource::ThreadedFrameSource(agi::fs::path const& video_filename, wxEvtHandler *parent)
|
||||
ThreadedFrameSource::ThreadedFrameSource(agi::fs::path const& video_filename, std::string const& colormatrix, wxEvtHandler *parent)
|
||||
: worker(agi::dispatch::Create())
|
||||
, subs_provider(get_subs_provider(parent))
|
||||
, video_provider(VideoProviderFactory::GetProvider(video_filename))
|
||||
, video_provider(VideoProviderFactory::GetProvider(video_filename, colormatrix))
|
||||
, parent(parent)
|
||||
, frame_number(-1)
|
||||
, time(-1.)
|
||||
|
|
|
@ -106,7 +106,7 @@ public:
|
|||
/// @brief Constructor
|
||||
/// @param videoFileName File to open
|
||||
/// @param parent Event handler to send FrameReady events to
|
||||
ThreadedFrameSource(agi::fs::path const& filename, wxEvtHandler *parent);
|
||||
ThreadedFrameSource(agi::fs::path const& filename, std::string const& colormatrix, wxEvtHandler *parent);
|
||||
~ThreadedFrameSource();
|
||||
};
|
||||
|
||||
|
|
|
@ -129,7 +129,7 @@ void VideoContext::SetVideo(const agi::fs::path &filename) {
|
|||
|
||||
bool commit_subs = false;
|
||||
try {
|
||||
provider.reset(new ThreadedFrameSource(filename, this));
|
||||
provider.reset(new ThreadedFrameSource(filename, context->ass->GetScriptInfo("YCbCr Matrix"), this));
|
||||
video_provider = provider->GetVideoProvider();
|
||||
video_filename = filename;
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@
|
|||
#include <vfw.h>
|
||||
#endif
|
||||
|
||||
AvisynthVideoProvider::AvisynthVideoProvider(agi::fs::path const& filename)
|
||||
AvisynthVideoProvider::AvisynthVideoProvider(agi::fs::path const& filename, std::string const& colormatrix)
|
||||
{
|
||||
agi::acs::CheckFileRead(filename);
|
||||
|
||||
|
@ -146,7 +146,9 @@ file_exit:
|
|||
if (!vi.IsRGB()) {
|
||||
/// @todo maybe read ColorMatrix hints for d2v files?
|
||||
AVSValue args[2] = { script, "Rec601" };
|
||||
if (!OPT_GET("Video/Force BT.601")->GetBool() && (vi.width > 1024 || vi.height >= 600)) {
|
||||
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";
|
||||
}
|
||||
|
|
|
@ -54,7 +54,7 @@ class AvisynthVideoProvider: public VideoProvider {
|
|||
AVSValue Open(agi::fs::path const& filename);
|
||||
|
||||
public:
|
||||
AvisynthVideoProvider(agi::fs::path const& filename);
|
||||
AvisynthVideoProvider(agi::fs::path const& filename, std::string const& colormatrix);
|
||||
|
||||
std::shared_ptr<VideoFrame> GetFrame(int n);
|
||||
|
||||
|
|
|
@ -84,7 +84,7 @@ void DummyVideoProvider::Create(double fps, int frames, int width, int height, u
|
|||
}
|
||||
}
|
||||
|
||||
DummyVideoProvider::DummyVideoProvider(agi::fs::path const& filename) {
|
||||
DummyVideoProvider::DummyVideoProvider(agi::fs::path const& filename, std::string const&) {
|
||||
if (!boost::starts_with(filename.string(), "?dummy"))
|
||||
throw agi::fs::FileNotFound(std::string("Attempted creating dummy video provider with non-dummy filename"));
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@ class DummyVideoProvider : public VideoProvider {
|
|||
|
||||
public:
|
||||
/// Create a dummy video from a string returned from MakeFilename
|
||||
DummyVideoProvider(agi::fs::path const& filename);
|
||||
DummyVideoProvider(agi::fs::path const& filename, std::string const& colormatix);
|
||||
|
||||
/// Create a dummy video from separate parameters
|
||||
/// @param fps Frame rate of the dummy video
|
||||
|
|
|
@ -48,7 +48,31 @@
|
|||
#include <wx/choicdlg.h>
|
||||
#include <wx/msgdlg.h>
|
||||
|
||||
FFmpegSourceVideoProvider::FFmpegSourceVideoProvider(agi::fs::path const& filename) try
|
||||
namespace {
|
||||
std::string colormatrix_description(int cs, int cr) {
|
||||
// Assuming TV for unspecified
|
||||
std::string str = cr == FFMS_CR_JPEG ? "PC" : "TV";
|
||||
|
||||
switch (cs) {
|
||||
case FFMS_CS_RGB:
|
||||
return "None";
|
||||
break;
|
||||
case FFMS_CS_BT709:
|
||||
return str + ".709";
|
||||
case FFMS_CS_FCC:
|
||||
return str + ".FCC";
|
||||
case FFMS_CS_BT470BG:
|
||||
case FFMS_CS_SMPTE170M:
|
||||
return str + ".601";
|
||||
case FFMS_CS_SMPTE240M:
|
||||
return str + ".240M";
|
||||
default:
|
||||
throw VideoOpenError("Unknown video color space");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FFmpegSourceVideoProvider::FFmpegSourceVideoProvider(agi::fs::path const& filename, std::string const& colormatrix) try
|
||||
: VideoSource(nullptr, FFMS_DestroyVideoSource)
|
||||
, VideoInfo(nullptr)
|
||||
, Width(-1)
|
||||
|
@ -61,7 +85,7 @@ FFmpegSourceVideoProvider::FFmpegSourceVideoProvider(agi::fs::path const& filena
|
|||
|
||||
SetLogLevel();
|
||||
|
||||
LoadVideo(filename);
|
||||
LoadVideo(filename, colormatrix);
|
||||
}
|
||||
catch (std::string const& err) {
|
||||
throw VideoOpenError(err);
|
||||
|
@ -70,7 +94,7 @@ catch (const char * err) {
|
|||
throw VideoOpenError(err);
|
||||
}
|
||||
|
||||
void FFmpegSourceVideoProvider::LoadVideo(agi::fs::path const& filename) {
|
||||
void FFmpegSourceVideoProvider::LoadVideo(agi::fs::path const& filename, std::string const& colormatrix) {
|
||||
FFMS_Indexer *Indexer = FFMS_CreateIndexer(filename.string().c_str(), &ErrInfo);
|
||||
if (!Indexer) {
|
||||
if (ErrInfo.SubType == FFMS_ERROR_FILE_READ)
|
||||
|
@ -168,44 +192,21 @@ void FFmpegSourceVideoProvider::LoadVideo(agi::fs::path const& filename) {
|
|||
else
|
||||
DAR = double(Width) / Height;
|
||||
|
||||
// Assuming TV for unspecified
|
||||
ColorSpace = TempFrame->ColorRange == FFMS_CR_JPEG ? "PC" : "TV";
|
||||
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);
|
||||
|
||||
int CS = TempFrame->ColorSpace;
|
||||
#if FFMS_VERSION >= ((2 << 24) | (17 << 16) | (1 << 8) | 0)
|
||||
if (CS != FFMS_CS_RGB && CS != FFMS_CS_BT470BG && OPT_GET("Video/Force BT.601")->GetBool()) {
|
||||
if (CS != FFMS_CS_RGB && CS != FFMS_CS_BT470BG && CS != colormatrix && (colormatrix == "TV.601" || OPT_GET("Video/Force BT.601")->GetBool())) {
|
||||
if (FFMS_SetInputFormatV(VideoSource, FFMS_CS_BT470BG, TempFrame->ColorRange, FFMS_GetPixFmt(""), &ErrInfo))
|
||||
throw VideoOpenError(std::string("Failed to set input format: ") + ErrInfo.Buffer);
|
||||
|
||||
CS = FFMS_CS_BT470BG;
|
||||
ColorSpace = colormatrix_description(CS, TempFrame->ColorRange);
|
||||
}
|
||||
#endif
|
||||
|
||||
switch (CS) {
|
||||
case FFMS_CS_RGB:
|
||||
ColorSpace = "None";
|
||||
break;
|
||||
case FFMS_CS_BT709:
|
||||
ColorSpace += ".709";
|
||||
break;
|
||||
case FFMS_CS_UNSPECIFIED:
|
||||
ColorSpace += Width > 1024 || Height >= 600 ? "709" : "601";
|
||||
break;
|
||||
case FFMS_CS_FCC:
|
||||
ColorSpace += ".FCC";
|
||||
break;
|
||||
case FFMS_CS_BT470BG:
|
||||
case FFMS_CS_SMPTE170M:
|
||||
ColorSpace += ".601";
|
||||
break;
|
||||
case FFMS_CS_SMPTE240M:
|
||||
ColorSpace += ".240M";
|
||||
break;
|
||||
default:
|
||||
throw VideoOpenError("Unknown video color space");
|
||||
break;
|
||||
}
|
||||
|
||||
const int TargetFormat[] = { FFMS_GetPixFmt("bgra"), -1 };
|
||||
if (FFMS_SetOutputFormatV2(VideoSource, TargetFormat, Width, Height, FFMS_RESIZER_BICUBIC, &ErrInfo)) {
|
||||
throw VideoOpenError(std::string("Failed to set output format: ") + ErrInfo.Buffer);
|
||||
|
|
|
@ -53,10 +53,10 @@ class FFmpegSourceVideoProvider : public VideoProvider, FFmpegSourceProvider {
|
|||
char FFMSErrMsg[1024]; ///< FFMS error message
|
||||
FFMS_ErrorInfo ErrInfo; ///< FFMS error codes/messages
|
||||
|
||||
void LoadVideo(agi::fs::path const& filename);
|
||||
void LoadVideo(agi::fs::path const& filename, std::string const& colormatrix);
|
||||
|
||||
public:
|
||||
FFmpegSourceVideoProvider(agi::fs::path const& filename);
|
||||
FFmpegSourceVideoProvider(agi::fs::path const& filename, std::string const& colormatrix);
|
||||
|
||||
std::shared_ptr<VideoFrame> GetFrame(int n);
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@
|
|||
#include <libaegisub/log.h>
|
||||
#include <libaegisub/util.h>
|
||||
|
||||
std::unique_ptr<VideoProvider> VideoProviderFactory::GetProvider(agi::fs::path const& video_file) {
|
||||
std::unique_ptr<VideoProvider> VideoProviderFactory::GetProvider(agi::fs::path const& video_file, std::string const& colormatrix) {
|
||||
std::vector<std::string> factories = GetClasses(OPT_GET("Video/Provider")->GetString());
|
||||
factories.insert(factories.begin(), "YUV4MPEG");
|
||||
factories.insert(factories.begin(), "Dummy");
|
||||
|
@ -60,7 +60,7 @@ std::unique_ptr<VideoProvider> VideoProviderFactory::GetProvider(agi::fs::path c
|
|||
for (auto const& factory : factories) {
|
||||
std::string err;
|
||||
try {
|
||||
auto provider = Create(factory, video_file);
|
||||
auto provider = Create(factory, video_file, colormatrix);
|
||||
LOG_I("manager/video/provider") << factory << ": opened " << video_file;
|
||||
return provider->WantsCaching() ? agi::util::make_unique<VideoProviderCache>(std::move(provider)) : std::move(provider);
|
||||
}
|
||||
|
|
|
@ -19,8 +19,8 @@
|
|||
|
||||
#include <libaegisub/fs_fwd.h>
|
||||
|
||||
class VideoProviderFactory : public Factory<VideoProvider, agi::fs::path> {
|
||||
class VideoProviderFactory : public Factory<VideoProvider, agi::fs::path, std::string> {
|
||||
public:
|
||||
static std::unique_ptr<VideoProvider> GetProvider(agi::fs::path const& video_file);
|
||||
static std::unique_ptr<VideoProvider> GetProvider(agi::fs::path const& video_file, std::string const& colormatrix);
|
||||
static void RegisterProviders();
|
||||
};
|
||||
|
|
|
@ -58,7 +58,7 @@
|
|||
|
||||
/// @brief Constructor
|
||||
/// @param filename The filename to open
|
||||
YUV4MPEGVideoProvider::YUV4MPEGVideoProvider(agi::fs::path const& filename)
|
||||
YUV4MPEGVideoProvider::YUV4MPEGVideoProvider(agi::fs::path const& filename, std::string const&)
|
||||
: sf(nullptr)
|
||||
, inited(false)
|
||||
, w (0)
|
||||
|
|
|
@ -129,7 +129,7 @@ class YUV4MPEGVideoProvider : public VideoProvider {
|
|||
int IndexFile();
|
||||
|
||||
public:
|
||||
YUV4MPEGVideoProvider(agi::fs::path const& filename);
|
||||
YUV4MPEGVideoProvider(agi::fs::path const& filename, std::string const&);
|
||||
~YUV4MPEGVideoProvider();
|
||||
|
||||
std::shared_ptr<VideoFrame> GetFrame(int n);
|
||||
|
|
Loading…
Reference in a new issue