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>
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:

View File

@ -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) {

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())
, 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.)

View File

@ -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();
};

View File

@ -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;

View File

@ -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";
}

View File

@ -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);

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"))
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:
/// 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

View File

@ -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);

View File

@ -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);

View File

@ -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);
}

View File

@ -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();
};

View File

@ -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)

View File

@ -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);