Update the YCbCr matrix used by the video display when the header is changed

This commit is contained in:
Thomas Goyne 2014-05-19 19:21:50 -07:00
parent ad33fdb109
commit 6edb38501b
10 changed files with 94 additions and 43 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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