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:
Thomas Goyne 2013-10-17 07:23:45 -07:00
parent 4116f030af
commit 73e1dc1b60
15 changed files with 86 additions and 62 deletions

View file

@ -68,8 +68,24 @@ public:
} }
}; };
template<typename Base, typename Arg1=void> template<typename Base, typename Arg1=void, typename Arg2=void>
class Factory : public FactoryBase<Base *(*)(Arg1)> { 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); typedef Base *(*func)(Arg1);
public: public:
@ -85,7 +101,7 @@ public:
}; };
template<typename Base> template<typename Base>
class Factory<Base, void> : public FactoryBase<Base *(*)()> { class Factory<Base, void, void> : public FactoryBase<Base *(*)()> {
typedef Base *(*func)(); typedef Base *(*func)();
public: public:

View file

@ -582,6 +582,7 @@ void FrameMain::OnAudioClose() {
void FrameMain::OnSubtitlesOpen() { void FrameMain::OnSubtitlesOpen() {
UpdateTitle(); UpdateTitle();
auto vc = context->videoController;
/// @todo figure out how to move this to the relevant controllers without /// @todo figure out how to move this to the relevant controllers without
/// prompting for each file loaded/unloaded /// 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 keyframes = config::path->MakeAbsolute(context->ass->GetScriptInfo("Keyframes File"), "?script");
auto audio = config::path->MakeAbsolute(context->ass->GetScriptInfo("Audio URI"), "?script"); auto audio = config::path->MakeAbsolute(context->ass->GetScriptInfo("Audio URI"), "?script");
bool videoChanged = !blockVideoLoad && video != context->videoController->GetVideoName(); bool videoChanged = !blockVideoLoad && video != vc->GetVideoName();
bool timecodesChanged = vfr != context->videoController->GetTimecodesName(); bool timecodesChanged = vfr != vc->GetTimecodesName();
bool keyframesChanged = keyframes != context->videoController->GetKeyFramesName(); bool keyframesChanged = keyframes != vc->GetKeyFramesName();
bool audioChanged = !blockAudioLoad && audio != context->audioController->GetAudioURL(); bool audioChanged = !blockAudioLoad && audio != context->audioController->GetAudioURL();
// Check if there is anything to change // Check if there is anything to change
@ -607,6 +608,8 @@ void FrameMain::OnSubtitlesOpen() {
if (autoLoadMode == 2) { if (autoLoadMode == 2) {
if (wxMessageBox(_("Do you want to load/unload the associated files?"), _("(Un)Load files?"), wxYES_NO | wxCENTRE, this) != wxYES) { if (wxMessageBox(_("Do you want to load/unload the associated files?"), _("(Un)Load files?"), wxYES_NO | wxCENTRE, this) != wxYES) {
SetDisplayMode(1, 1); SetDisplayMode(1, 1);
if (vc->IsLoaded() && vc->GetProvider()->GetColorSpace() != context->ass->GetScriptInfo("YCbCr Matrix"))
vc->Reload();
return; return;
} }
} }
@ -616,20 +619,20 @@ void FrameMain::OnSubtitlesOpen() {
// Video // Video
if (videoChanged) { if (videoChanged) {
context->videoController->SetVideo(video); vc->SetVideo(video);
if (context->videoController->IsLoaded()) { if (vc->IsLoaded()) {
context->videoController->JumpToFrame(context->ass->GetUIStateAsInt("Video Position")); vc->JumpToFrame(context->ass->GetUIStateAsInt("Video Position"));
std::string arString = context->ass->GetUIState("Video Aspect Ratio"); std::string arString = context->ass->GetUIState("Video Aspect Ratio");
if (boost::starts_with(arString, "c")) { if (boost::starts_with(arString, "c")) {
double ar = 0.; double ar = 0.;
agi::util::try_parse(arString.substr(1), &ar); agi::util::try_parse(arString.substr(1), &ar);
context->videoController->SetAspectRatio(ar); vc->SetAspectRatio(ar);
} }
else { else {
int ar = 0; int ar = 0;
if (agi::util::try_parse(arString, &ar) && ar >= 0 && ar < 4) if (agi::util::try_parse(arString, &ar) && ar >= 0 && ar < 4)
context->videoController->SetAspectRatio((AspectRatio)ar); vc->SetAspectRatio((AspectRatio)ar);
} }
double videoZoom = 0.; double videoZoom = 0.;
@ -637,9 +640,11 @@ void FrameMain::OnSubtitlesOpen() {
context->videoDisplay->SetZoom(videoZoom); context->videoDisplay->SetZoom(videoZoom);
} }
} }
else if (vc->IsLoaded() && vc->GetProvider()->GetColorSpace() != context->ass->GetScriptInfo("YCbCr Matrix"))
vc->Reload();
context->videoController->LoadTimecodes(vfr); vc->LoadTimecodes(vfr);
context->videoController->LoadKeyframes(keyframes); vc->LoadKeyframes(keyframes);
// Audio // Audio
if (audioChanged) { if (audioChanged) {

View file

@ -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()) : worker(agi::dispatch::Create())
, subs_provider(get_subs_provider(parent)) , subs_provider(get_subs_provider(parent))
, video_provider(VideoProviderFactory::GetProvider(video_filename)) , video_provider(VideoProviderFactory::GetProvider(video_filename, colormatrix))
, parent(parent) , parent(parent)
, frame_number(-1) , frame_number(-1)
, time(-1.) , time(-1.)

View file

@ -106,7 +106,7 @@ public:
/// @brief Constructor /// @brief Constructor
/// @param videoFileName File to open /// @param videoFileName File to open
/// @param parent Event handler to send FrameReady events to /// @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(); ~ThreadedFrameSource();
}; };

View file

@ -129,7 +129,7 @@ void VideoContext::SetVideo(const agi::fs::path &filename) {
bool commit_subs = false; bool commit_subs = false;
try { try {
provider.reset(new ThreadedFrameSource(filename, this)); provider.reset(new ThreadedFrameSource(filename, context->ass->GetScriptInfo("YCbCr Matrix"), this));
video_provider = provider->GetVideoProvider(); video_provider = provider->GetVideoProvider();
video_filename = filename; video_filename = filename;

View file

@ -53,7 +53,7 @@
#include <vfw.h> #include <vfw.h>
#endif #endif
AvisynthVideoProvider::AvisynthVideoProvider(agi::fs::path const& filename) AvisynthVideoProvider::AvisynthVideoProvider(agi::fs::path const& filename, std::string const& colormatrix)
{ {
agi::acs::CheckFileRead(filename); agi::acs::CheckFileRead(filename);
@ -146,7 +146,9 @@ file_exit:
if (!vi.IsRGB()) { if (!vi.IsRGB()) {
/// @todo maybe read ColorMatrix hints for d2v files? /// @todo maybe read ColorMatrix hints for d2v files?
AVSValue args[2] = { script, "Rec601" }; 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"; args[1] = "Rec709";
colorspace = "TV.709"; colorspace = "TV.709";
} }

View file

@ -54,7 +54,7 @@ class AvisynthVideoProvider: public VideoProvider {
AVSValue Open(agi::fs::path const& filename); AVSValue Open(agi::fs::path const& filename);
public: public:
AvisynthVideoProvider(agi::fs::path const& filename); AvisynthVideoProvider(agi::fs::path const& filename, std::string const& colormatrix);
std::shared_ptr<VideoFrame> GetFrame(int n); std::shared_ptr<VideoFrame> GetFrame(int n);

View file

@ -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")) if (!boost::starts_with(filename.string(), "?dummy"))
throw agi::fs::FileNotFound(std::string("Attempted creating dummy video provider with non-dummy filename")); throw agi::fs::FileNotFound(std::string("Attempted creating dummy video provider with non-dummy filename"));

View file

@ -63,7 +63,7 @@ class DummyVideoProvider : public VideoProvider {
public: public:
/// Create a dummy video from a string returned from MakeFilename /// 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 /// Create a dummy video from separate parameters
/// @param fps Frame rate of the dummy video /// @param fps Frame rate of the dummy video

View file

@ -48,7 +48,31 @@
#include <wx/choicdlg.h> #include <wx/choicdlg.h>
#include <wx/msgdlg.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) : VideoSource(nullptr, FFMS_DestroyVideoSource)
, VideoInfo(nullptr) , VideoInfo(nullptr)
, Width(-1) , Width(-1)
@ -61,7 +85,7 @@ FFmpegSourceVideoProvider::FFmpegSourceVideoProvider(agi::fs::path const& filena
SetLogLevel(); SetLogLevel();
LoadVideo(filename); LoadVideo(filename, colormatrix);
} }
catch (std::string const& err) { catch (std::string const& err) {
throw VideoOpenError(err); throw VideoOpenError(err);
@ -70,7 +94,7 @@ catch (const char * err) {
throw VideoOpenError(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); FFMS_Indexer *Indexer = FFMS_CreateIndexer(filename.string().c_str(), &ErrInfo);
if (!Indexer) { if (!Indexer) {
if (ErrInfo.SubType == FFMS_ERROR_FILE_READ) if (ErrInfo.SubType == FFMS_ERROR_FILE_READ)
@ -168,44 +192,21 @@ void FFmpegSourceVideoProvider::LoadVideo(agi::fs::path const& filename) {
else else
DAR = double(Width) / Height; DAR = double(Width) / Height;
// Assuming TV for unspecified auto CS = TempFrame->ColorSpace;
ColorSpace = TempFrame->ColorRange == FFMS_CR_JPEG ? "PC" : "TV"; 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 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)) if (FFMS_SetInputFormatV(VideoSource, FFMS_CS_BT470BG, TempFrame->ColorRange, FFMS_GetPixFmt(""), &ErrInfo))
throw VideoOpenError(std::string("Failed to set input format: ") + ErrInfo.Buffer); throw VideoOpenError(std::string("Failed to set input format: ") + ErrInfo.Buffer);
CS = FFMS_CS_BT470BG; CS = FFMS_CS_BT470BG;
ColorSpace = colormatrix_description(CS, TempFrame->ColorRange);
} }
#endif #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 }; const int TargetFormat[] = { FFMS_GetPixFmt("bgra"), -1 };
if (FFMS_SetOutputFormatV2(VideoSource, TargetFormat, Width, Height, FFMS_RESIZER_BICUBIC, &ErrInfo)) { if (FFMS_SetOutputFormatV2(VideoSource, TargetFormat, Width, Height, FFMS_RESIZER_BICUBIC, &ErrInfo)) {
throw VideoOpenError(std::string("Failed to set output format: ") + ErrInfo.Buffer); throw VideoOpenError(std::string("Failed to set output format: ") + ErrInfo.Buffer);

View file

@ -53,10 +53,10 @@ class FFmpegSourceVideoProvider : public VideoProvider, FFmpegSourceProvider {
char FFMSErrMsg[1024]; ///< FFMS error message char FFMSErrMsg[1024]; ///< FFMS error message
FFMS_ErrorInfo ErrInfo; ///< FFMS error codes/messages 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: public:
FFmpegSourceVideoProvider(agi::fs::path const& filename); FFmpegSourceVideoProvider(agi::fs::path const& filename, std::string const& colormatrix);
std::shared_ptr<VideoFrame> GetFrame(int n); std::shared_ptr<VideoFrame> GetFrame(int n);

View file

@ -47,7 +47,7 @@
#include <libaegisub/log.h> #include <libaegisub/log.h>
#include <libaegisub/util.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()); std::vector<std::string> factories = GetClasses(OPT_GET("Video/Provider")->GetString());
factories.insert(factories.begin(), "YUV4MPEG"); factories.insert(factories.begin(), "YUV4MPEG");
factories.insert(factories.begin(), "Dummy"); factories.insert(factories.begin(), "Dummy");
@ -60,7 +60,7 @@ std::unique_ptr<VideoProvider> VideoProviderFactory::GetProvider(agi::fs::path c
for (auto const& factory : factories) { for (auto const& factory : factories) {
std::string err; std::string err;
try { try {
auto provider = Create(factory, video_file); auto provider = Create(factory, video_file, colormatrix);
LOG_I("manager/video/provider") << factory << ": opened " << video_file; LOG_I("manager/video/provider") << factory << ": opened " << video_file;
return provider->WantsCaching() ? agi::util::make_unique<VideoProviderCache>(std::move(provider)) : std::move(provider); return provider->WantsCaching() ? agi::util::make_unique<VideoProviderCache>(std::move(provider)) : std::move(provider);
} }

View file

@ -19,8 +19,8 @@
#include <libaegisub/fs_fwd.h> #include <libaegisub/fs_fwd.h>
class VideoProviderFactory : public Factory<VideoProvider, agi::fs::path> { class VideoProviderFactory : public Factory<VideoProvider, agi::fs::path, std::string> {
public: 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(); static void RegisterProviders();
}; };

View file

@ -58,7 +58,7 @@
/// @brief Constructor /// @brief Constructor
/// @param filename The filename to open /// @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) : sf(nullptr)
, inited(false) , inited(false)
, w (0) , w (0)

View file

@ -129,7 +129,7 @@ class YUV4MPEGVideoProvider : public VideoProvider {
int IndexFile(); int IndexFile();
public: public:
YUV4MPEGVideoProvider(agi::fs::path const& filename); YUV4MPEGVideoProvider(agi::fs::path const& filename, std::string const&);
~YUV4MPEGVideoProvider(); ~YUV4MPEGVideoProvider();
std::shared_ptr<VideoFrame> GetFrame(int n); std::shared_ptr<VideoFrame> GetFrame(int n);