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
|
||||
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:
|
||||
virtual int GetFrameCount() const=0; ///< Get total number of frames
|
||||
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> ret;
|
||||
worker->Sync([&]{
|
||||
ret = ProcFrame(frame, time, raw);
|
||||
});
|
||||
worker->Sync([&]{ ret = ProcFrame(frame, time, raw); });
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ThreadedFrameSource::SetColorSpace(std::string const& matrix) {
|
||||
worker->Async([=] { video_provider->SetColorSpace(matrix); });
|
||||
}
|
||||
|
||||
wxDEFINE_EVENT(EVT_FRAME_READY, FrameReadyEvent);
|
||||
wxDEFINE_EVENT(EVT_VIDEO_ERROR, VideoProviderErrorEvent);
|
||||
wxDEFINE_EVENT(EVT_SUBTITLES_ERROR, SubtitlesProviderErrorEvent);
|
||||
|
|
|
@ -106,6 +106,9 @@ public:
|
|||
/// Get a reference to the video provider this is using
|
||||
VideoProvider *GetVideoProvider() const { return video_provider.get(); }
|
||||
|
||||
/// Ask the video provider to change YCbCr matricies
|
||||
void SetColorSpace(std::string const& matrix);
|
||||
|
||||
/// @brief Constructor
|
||||
/// @param videoFileName File to open
|
||||
/// @param parent Event handler to send FrameReady events to
|
||||
|
|
|
@ -94,6 +94,7 @@ void VideoContext::Reset() {
|
|||
|
||||
// Clean up video data
|
||||
video_filename.clear();
|
||||
color_matrix.clear();
|
||||
|
||||
// Remove provider
|
||||
provider.reset();
|
||||
|
@ -121,13 +122,13 @@ void VideoContext::SetVideo(const agi::fs::path &filename) {
|
|||
provider = agi::make_unique<ThreadedFrameSource>(filename, old_matrix, this, progress);
|
||||
video_provider = provider->GetVideoProvider();
|
||||
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);
|
||||
|
||||
keyframes = video_provider->GetKeyFrames();
|
||||
|
||||
// Set frame rate
|
||||
video_fps = video_provider->GetFPS();
|
||||
if (ovr_fps.IsLoaded()) {
|
||||
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);
|
||||
|
@ -186,6 +187,14 @@ void VideoContext::Reload() {
|
|||
void VideoContext::OnSubtitlesCommit(int type, std::set<const AssDialogue *> const& changed) {
|
||||
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)
|
||||
provider->LoadSubtitles(context->ass.get());
|
||||
else
|
||||
|
|
|
@ -98,6 +98,9 @@ class VideoContext final : public wxEvtHandler {
|
|||
/// Filename of currently open video
|
||||
agi::fs::path video_filename;
|
||||
|
||||
/// Last seen script color matrix
|
||||
std::string color_matrix;
|
||||
|
||||
/// List of frame numbers which are keyframes
|
||||
std::vector<int> keyframes;
|
||||
|
||||
|
|
|
@ -67,16 +67,23 @@ class AvisynthVideoProvider: public VideoProvider {
|
|||
std::string colorspace;
|
||||
std::string real_colorspace;
|
||||
|
||||
AVSValue source_clip;
|
||||
PClip RGB32Video;
|
||||
VideoInfo vi;
|
||||
|
||||
AVSValue Open(agi::fs::path const& filename);
|
||||
void Init(std::string const& matrix);
|
||||
|
||||
public:
|
||||
AvisynthVideoProvider(agi::fs::path const& filename, std::string const& colormatrix);
|
||||
|
||||
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; }
|
||||
agi::vfr::Framerate GetFPS() const override { return fps; }
|
||||
int GetWidth() const override { return vi.width; }
|
||||
|
@ -171,41 +178,41 @@ file_exit:
|
|||
#endif
|
||||
|
||||
try {
|
||||
auto script = Open(filename);
|
||||
|
||||
// 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;
|
||||
source_clip = Open(filename);
|
||||
Init(colormatrix);
|
||||
}
|
||||
catch (AvisynthError const& err) {
|
||||
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) {
|
||||
IScriptEnvironment *env = avs.GetEnv();
|
||||
char *videoFilename = env->SaveString(agi::fs::ShortName(filename).c_str());
|
||||
|
|
|
@ -59,6 +59,11 @@ public:
|
|||
|
||||
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 GetWidth() const override { return master->GetWidth(); }
|
||||
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);
|
||||
|
||||
std::shared_ptr<VideoFrame> GetFrame(int n) override;
|
||||
void SetColorSpace(std::string const&) override { }
|
||||
|
||||
int GetFrameCount() const override { return framecount; }
|
||||
int GetWidth() const override { return width; }
|
||||
|
|
|
@ -58,6 +58,8 @@ class FFmpegSourceVideoProvider final : public VideoProvider, FFmpegSourceProvid
|
|||
|
||||
int Width = -1; ///< width 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
|
||||
std::vector<int> KeyFramesList; ///< list of keyframes
|
||||
agi::vfr::Framerate Timecodes; ///< vfr object
|
||||
|
@ -74,6 +76,19 @@ public:
|
|||
|
||||
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 GetWidth() const override { return Width; }
|
||||
int GetHeight() const override { return Height; }
|
||||
|
@ -225,25 +240,24 @@ void FFmpegSourceVideoProvider::LoadVideo(agi::fs::path const& filename, std::st
|
|||
else
|
||||
DAR = double(Width) / Height;
|
||||
|
||||
auto CS = TempFrame->ColorSpace;
|
||||
CS = TempFrame->ColorSpace;
|
||||
CR = TempFrame->ColorRange;
|
||||
|
||||
if (CS == FFMS_CS_UNSPECIFIED)
|
||||
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 (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);
|
||||
|
||||
CS = FFMS_CS_BT470BG;
|
||||
ColorSpace = colormatrix_description(CS, TempFrame->ColorRange);
|
||||
ColorSpace = colormatrix_description(FFMS_CS_BT470BG, CR);
|
||||
}
|
||||
#endif
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// get frame info data
|
||||
FFMS_Track *FrameData = FFMS_GetTrackFromVideo(VideoSource);
|
||||
|
|
|
@ -147,6 +147,7 @@ public:
|
|||
YUV4MPEGVideoProvider(agi::fs::path const& filename);
|
||||
|
||||
std::shared_ptr<VideoFrame> GetFrame(int n) override;
|
||||
void SetColorSpace(std::string const&) override { }
|
||||
|
||||
int GetFrameCount() const override { return num_frames; }
|
||||
int GetWidth() const override { return w; }
|
||||
|
|
Loading…
Reference in a new issue