forked from mia/Aegisub
Update the YCbCr matrix used by the video display when the header is changed
This commit is contained in:
parent
ad33fdb109
commit
6edb38501b
10 changed files with 94 additions and 43 deletions
|
@ -49,6 +49,12 @@ public:
|
||||||
/// Override this method to actually get frames
|
/// Override this method to actually get frames
|
||||||
virtual std::shared_ptr<VideoFrame> GetFrame(int n)=0;
|
virtual std::shared_ptr<VideoFrame> GetFrame(int n)=0;
|
||||||
|
|
||||||
|
/// Set the YCbCr matrix to the specified one
|
||||||
|
///
|
||||||
|
/// Providers are free to disregard this, and should if the requested
|
||||||
|
/// matrix makes no sense or the input isn't YCbCr.
|
||||||
|
virtual void SetColorSpace(std::string const& matrix)=0;
|
||||||
|
|
||||||
// Override the following methods to get video information:
|
// Override the following methods to get video information:
|
||||||
virtual int GetFrameCount() const=0; ///< Get total number of frames
|
virtual int GetFrameCount() const=0; ///< Get total number of frames
|
||||||
virtual int GetWidth() const=0; ///< Returns the video width in pixels
|
virtual int GetWidth() const=0; ///< Returns the video width in pixels
|
||||||
|
|
|
@ -168,12 +168,14 @@ void ThreadedFrameSource::ProcAsync(uint_fast32_t req_version) {
|
||||||
|
|
||||||
std::shared_ptr<VideoFrame> ThreadedFrameSource::GetFrame(int frame, double time, bool raw) {
|
std::shared_ptr<VideoFrame> ThreadedFrameSource::GetFrame(int frame, double time, bool raw) {
|
||||||
std::shared_ptr<VideoFrame> ret;
|
std::shared_ptr<VideoFrame> ret;
|
||||||
worker->Sync([&]{
|
worker->Sync([&]{ ret = ProcFrame(frame, time, raw); });
|
||||||
ret = ProcFrame(frame, time, raw);
|
|
||||||
});
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ThreadedFrameSource::SetColorSpace(std::string const& matrix) {
|
||||||
|
worker->Async([=] { video_provider->SetColorSpace(matrix); });
|
||||||
|
}
|
||||||
|
|
||||||
wxDEFINE_EVENT(EVT_FRAME_READY, FrameReadyEvent);
|
wxDEFINE_EVENT(EVT_FRAME_READY, FrameReadyEvent);
|
||||||
wxDEFINE_EVENT(EVT_VIDEO_ERROR, VideoProviderErrorEvent);
|
wxDEFINE_EVENT(EVT_VIDEO_ERROR, VideoProviderErrorEvent);
|
||||||
wxDEFINE_EVENT(EVT_SUBTITLES_ERROR, SubtitlesProviderErrorEvent);
|
wxDEFINE_EVENT(EVT_SUBTITLES_ERROR, SubtitlesProviderErrorEvent);
|
||||||
|
|
|
@ -106,6 +106,9 @@ public:
|
||||||
/// Get a reference to the video provider this is using
|
/// Get a reference to the video provider this is using
|
||||||
VideoProvider *GetVideoProvider() const { return video_provider.get(); }
|
VideoProvider *GetVideoProvider() const { return video_provider.get(); }
|
||||||
|
|
||||||
|
/// Ask the video provider to change YCbCr matricies
|
||||||
|
void SetColorSpace(std::string const& matrix);
|
||||||
|
|
||||||
/// @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
|
||||||
|
|
|
@ -94,6 +94,7 @@ void VideoContext::Reset() {
|
||||||
|
|
||||||
// Clean up video data
|
// Clean up video data
|
||||||
video_filename.clear();
|
video_filename.clear();
|
||||||
|
color_matrix.clear();
|
||||||
|
|
||||||
// Remove provider
|
// Remove provider
|
||||||
provider.reset();
|
provider.reset();
|
||||||
|
@ -121,13 +122,13 @@ void VideoContext::SetVideo(const agi::fs::path &filename) {
|
||||||
provider = agi::make_unique<ThreadedFrameSource>(filename, old_matrix, this, progress);
|
provider = agi::make_unique<ThreadedFrameSource>(filename, old_matrix, this, progress);
|
||||||
video_provider = provider->GetVideoProvider();
|
video_provider = provider->GetVideoProvider();
|
||||||
video_filename = filename;
|
video_filename = filename;
|
||||||
|
color_matrix = video_provider->GetColorSpace();
|
||||||
|
keyframes = video_provider->GetKeyFrames();
|
||||||
|
video_fps = video_provider->GetFPS();
|
||||||
|
|
||||||
bool needs_commit = UpdateVideoProperties(context->ass.get(), video_provider, context->parent);
|
bool needs_commit = UpdateVideoProperties(context->ass.get(), video_provider, context->parent);
|
||||||
|
|
||||||
keyframes = video_provider->GetKeyFrames();
|
|
||||||
|
|
||||||
// Set frame rate
|
// Set frame rate
|
||||||
video_fps = video_provider->GetFPS();
|
|
||||||
if (ovr_fps.IsLoaded()) {
|
if (ovr_fps.IsLoaded()) {
|
||||||
int ovr = wxMessageBox(_("You already have timecodes loaded. Would you like to replace them with timecodes from the video file?"),
|
int ovr = wxMessageBox(_("You already have timecodes loaded. Would you like to replace them with timecodes from the video file?"),
|
||||||
_("Replace timecodes?"), wxYES_NO | wxICON_QUESTION);
|
_("Replace timecodes?"), wxYES_NO | wxICON_QUESTION);
|
||||||
|
@ -186,6 +187,14 @@ void VideoContext::Reload() {
|
||||||
void VideoContext::OnSubtitlesCommit(int type, std::set<const AssDialogue *> const& changed) {
|
void VideoContext::OnSubtitlesCommit(int type, std::set<const AssDialogue *> const& changed) {
|
||||||
if (!IsLoaded()) return;
|
if (!IsLoaded()) return;
|
||||||
|
|
||||||
|
if ((type & AssFile::COMMIT_SCRIPTINFO) || type == AssFile::COMMIT_NEW) {
|
||||||
|
auto new_matrix = context->ass->GetScriptInfo("YCbCr Matrix");
|
||||||
|
if (!new_matrix.empty() && new_matrix != color_matrix) {
|
||||||
|
color_matrix = new_matrix;
|
||||||
|
provider->SetColorSpace(new_matrix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (changed.empty() || no_amend)
|
if (changed.empty() || no_amend)
|
||||||
provider->LoadSubtitles(context->ass.get());
|
provider->LoadSubtitles(context->ass.get());
|
||||||
else
|
else
|
||||||
|
|
|
@ -98,6 +98,9 @@ class VideoContext final : public wxEvtHandler {
|
||||||
/// Filename of currently open video
|
/// Filename of currently open video
|
||||||
agi::fs::path video_filename;
|
agi::fs::path video_filename;
|
||||||
|
|
||||||
|
/// Last seen script color matrix
|
||||||
|
std::string color_matrix;
|
||||||
|
|
||||||
/// List of frame numbers which are keyframes
|
/// List of frame numbers which are keyframes
|
||||||
std::vector<int> keyframes;
|
std::vector<int> keyframes;
|
||||||
|
|
||||||
|
|
|
@ -67,16 +67,23 @@ class AvisynthVideoProvider: public VideoProvider {
|
||||||
std::string colorspace;
|
std::string colorspace;
|
||||||
std::string real_colorspace;
|
std::string real_colorspace;
|
||||||
|
|
||||||
|
AVSValue source_clip;
|
||||||
PClip RGB32Video;
|
PClip RGB32Video;
|
||||||
VideoInfo vi;
|
VideoInfo vi;
|
||||||
|
|
||||||
AVSValue Open(agi::fs::path const& filename);
|
AVSValue Open(agi::fs::path const& filename);
|
||||||
|
void Init(std::string const& matrix);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AvisynthVideoProvider(agi::fs::path const& filename, std::string const& colormatrix);
|
AvisynthVideoProvider(agi::fs::path const& filename, std::string const& colormatrix);
|
||||||
|
|
||||||
std::shared_ptr<VideoFrame> GetFrame(int n);
|
std::shared_ptr<VideoFrame> GetFrame(int n);
|
||||||
|
|
||||||
|
void SetColorSpace(std::string const& matrix) override {
|
||||||
|
// Can't really do anything if this fails
|
||||||
|
try { Init(matrix); } catch (AvisynthError const&) { }
|
||||||
|
}
|
||||||
|
|
||||||
int GetFrameCount() const override { return vi.num_frames; }
|
int GetFrameCount() const override { return vi.num_frames; }
|
||||||
agi::vfr::Framerate GetFPS() const override { return fps; }
|
agi::vfr::Framerate GetFPS() const override { return fps; }
|
||||||
int GetWidth() const override { return vi.width; }
|
int GetWidth() const override { return vi.width; }
|
||||||
|
@ -171,41 +178,41 @@ file_exit:
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto script = Open(filename);
|
source_clip = Open(filename);
|
||||||
|
Init(colormatrix);
|
||||||
// Check if video was loaded properly
|
|
||||||
if (!script.IsClip() || !script.AsClip()->GetVideoInfo().HasVideo())
|
|
||||||
throw VideoNotSupported("No usable video found");
|
|
||||||
|
|
||||||
vi = script.AsClip()->GetVideoInfo();
|
|
||||||
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";
|
|
||||||
real_colorspace = colorspace = "TV.709";
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
RGB32Video = avs.GetEnv()->Invoke("Cache", script).AsClip();
|
|
||||||
vi = RGB32Video->GetVideoInfo();
|
|
||||||
fps = (double)vi.fps_numerator / vi.fps_denominator;
|
|
||||||
}
|
}
|
||||||
catch (AvisynthError const& err) {
|
catch (AvisynthError const& err) {
|
||||||
throw VideoOpenError("Avisynth error: " + std::string(err.msg));
|
throw VideoOpenError("Avisynth error: " + std::string(err.msg));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AvisynthVideoProvider::Init(std::string const& colormatrix) {
|
||||||
|
auto script = source_clip;
|
||||||
|
vi = script.AsClip()->GetVideoInfo();
|
||||||
|
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";
|
||||||
|
real_colorspace = colorspace = "TV.709";
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
RGB32Video = avs.GetEnv()->Invoke("Cache", script).AsClip();
|
||||||
|
vi = RGB32Video->GetVideoInfo();
|
||||||
|
fps = (double)vi.fps_numerator / vi.fps_denominator;
|
||||||
|
}
|
||||||
|
|
||||||
AVSValue AvisynthVideoProvider::Open(agi::fs::path const& filename) {
|
AVSValue AvisynthVideoProvider::Open(agi::fs::path const& filename) {
|
||||||
IScriptEnvironment *env = avs.GetEnv();
|
IScriptEnvironment *env = avs.GetEnv();
|
||||||
char *videoFilename = env->SaveString(agi::fs::ShortName(filename).c_str());
|
char *videoFilename = env->SaveString(agi::fs::ShortName(filename).c_str());
|
||||||
|
|
|
@ -59,6 +59,11 @@ public:
|
||||||
|
|
||||||
std::shared_ptr<VideoFrame> GetFrame(int n) override;
|
std::shared_ptr<VideoFrame> GetFrame(int n) override;
|
||||||
|
|
||||||
|
void SetColorSpace(std::string const& m) override {
|
||||||
|
cache.clear();
|
||||||
|
return master->SetColorSpace(m);
|
||||||
|
}
|
||||||
|
|
||||||
int GetFrameCount() const override { return master->GetFrameCount(); }
|
int GetFrameCount() const override { return master->GetFrameCount(); }
|
||||||
int GetWidth() const override { return master->GetWidth(); }
|
int GetWidth() const override { return master->GetWidth(); }
|
||||||
int GetHeight() const override { return master->GetHeight(); }
|
int GetHeight() const override { return master->GetHeight(); }
|
||||||
|
|
|
@ -65,6 +65,7 @@ public:
|
||||||
static std::string MakeFilename(double fps, int frames, int width, int height, agi::Color colour, bool pattern);
|
static std::string MakeFilename(double fps, int frames, int width, int height, agi::Color colour, bool pattern);
|
||||||
|
|
||||||
std::shared_ptr<VideoFrame> GetFrame(int n) override;
|
std::shared_ptr<VideoFrame> GetFrame(int n) override;
|
||||||
|
void SetColorSpace(std::string const&) override { }
|
||||||
|
|
||||||
int GetFrameCount() const override { return framecount; }
|
int GetFrameCount() const override { return framecount; }
|
||||||
int GetWidth() const override { return width; }
|
int GetWidth() const override { return width; }
|
||||||
|
|
|
@ -58,6 +58,8 @@ class FFmpegSourceVideoProvider final : public VideoProvider, FFmpegSourceProvid
|
||||||
|
|
||||||
int Width = -1; ///< width in pixels
|
int Width = -1; ///< width in pixels
|
||||||
int Height = -1; ///< height in pixels
|
int Height = -1; ///< height in pixels
|
||||||
|
int CS = -1; ///< Reported colorspace of first frame
|
||||||
|
int CR = -1; ///< Reported colorrange of first frame
|
||||||
double DAR; ///< display aspect ratio
|
double DAR; ///< display aspect ratio
|
||||||
std::vector<int> KeyFramesList; ///< list of keyframes
|
std::vector<int> KeyFramesList; ///< list of keyframes
|
||||||
agi::vfr::Framerate Timecodes; ///< vfr object
|
agi::vfr::Framerate Timecodes; ///< vfr object
|
||||||
|
@ -74,6 +76,19 @@ public:
|
||||||
|
|
||||||
std::shared_ptr<VideoFrame> GetFrame(int n) override;
|
std::shared_ptr<VideoFrame> GetFrame(int n) override;
|
||||||
|
|
||||||
|
void SetColorSpace(std::string const& matrix) override {
|
||||||
|
#if FFMS_VERSION >= ((2 << 24) | (17 << 16) | (1 << 8) | 0)
|
||||||
|
if (matrix == ColorSpace) return;
|
||||||
|
if (matrix == RealColorSpace)
|
||||||
|
FFMS_SetInputFormatV(VideoSource, CS, CR, FFMS_GetPixFmt(""), nullptr);
|
||||||
|
else if (matrix == "TV.601")
|
||||||
|
FFMS_SetInputFormatV(VideoSource, FFMS_CS_BT470BG, CR, FFMS_GetPixFmt(""), nullptr);
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
ColorSpace = matrix;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
int GetFrameCount() const override { return VideoInfo->NumFrames; }
|
int GetFrameCount() const override { return VideoInfo->NumFrames; }
|
||||||
int GetWidth() const override { return Width; }
|
int GetWidth() const override { return Width; }
|
||||||
int GetHeight() const override { return Height; }
|
int GetHeight() const override { return Height; }
|
||||||
|
@ -225,25 +240,24 @@ void FFmpegSourceVideoProvider::LoadVideo(agi::fs::path const& filename, std::st
|
||||||
else
|
else
|
||||||
DAR = double(Width) / Height;
|
DAR = double(Width) / Height;
|
||||||
|
|
||||||
auto CS = TempFrame->ColorSpace;
|
CS = TempFrame->ColorSpace;
|
||||||
|
CR = TempFrame->ColorRange;
|
||||||
|
|
||||||
if (CS == FFMS_CS_UNSPECIFIED)
|
if (CS == FFMS_CS_UNSPECIFIED)
|
||||||
CS = Width > 1024 || Height >= 600 ? FFMS_CS_BT709 : FFMS_CS_BT470BG;
|
CS = Width > 1024 || Height >= 600 ? FFMS_CS_BT709 : FFMS_CS_BT470BG;
|
||||||
RealColorSpace = ColorSpace = colormatrix_description(CS, TempFrame->ColorRange);
|
RealColorSpace = ColorSpace = colormatrix_description(CS, CR);
|
||||||
|
|
||||||
#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 && ColorSpace != colormatrix && (colormatrix == "TV.601" || OPT_GET("Video/Force BT.601")->GetBool())) {
|
if (CS != FFMS_CS_RGB && CS != FFMS_CS_BT470BG && ColorSpace != 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, CR, 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);
|
||||||
|
ColorSpace = colormatrix_description(FFMS_CS_BT470BG, CR);
|
||||||
CS = FFMS_CS_BT470BG;
|
|
||||||
ColorSpace = colormatrix_description(CS, TempFrame->ColorRange);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
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);
|
||||||
}
|
|
||||||
|
|
||||||
// get frame info data
|
// get frame info data
|
||||||
FFMS_Track *FrameData = FFMS_GetTrackFromVideo(VideoSource);
|
FFMS_Track *FrameData = FFMS_GetTrackFromVideo(VideoSource);
|
||||||
|
|
|
@ -147,6 +147,7 @@ public:
|
||||||
YUV4MPEGVideoProvider(agi::fs::path const& filename);
|
YUV4MPEGVideoProvider(agi::fs::path const& filename);
|
||||||
|
|
||||||
std::shared_ptr<VideoFrame> GetFrame(int n) override;
|
std::shared_ptr<VideoFrame> GetFrame(int n) override;
|
||||||
|
void SetColorSpace(std::string const&) override { }
|
||||||
|
|
||||||
int GetFrameCount() const override { return num_frames; }
|
int GetFrameCount() const override { return num_frames; }
|
||||||
int GetWidth() const override { return w; }
|
int GetWidth() const override { return w; }
|
||||||
|
|
Loading…
Reference in a new issue