footage: allow overriding YCbCr range

Fixes #1354
This commit is contained in:
itsmattkc
2022-08-08 12:49:50 -07:00
parent a3d6ecc4fd
commit 1ac205413c
23 changed files with 150 additions and 111 deletions
+7 -9
View File
@@ -93,7 +93,7 @@ bool Decoder::Open(const CodecStream &stream)
}
}
TexturePtr Decoder::RetrieveVideo(Renderer *renderer, const rational &timecode, const RetrieveVideoParams &divider, CancelAtom *cancelled)
TexturePtr Decoder::RetrieveVideo(const RetrieveVideoParams &p)
{
QMutexLocker locker(&mutex_);
@@ -109,16 +109,16 @@ TexturePtr Decoder::RetrieveVideo(Renderer *renderer, const rational &timecode,
return nullptr;
}
if (cancelled && cancelled->IsCancelled()) {
if (p.cancelled && p.cancelled->IsCancelled()) {
return nullptr;
}
if (cached_texture_ && cached_time_ == timecode) {
if (cached_texture_ && cached_time_ == p.time) {
return cached_texture_;
}
cached_texture_ = RetrieveVideoInternal(renderer, timecode, divider, cancelled);
cached_time_ = timecode;
cached_texture_ = RetrieveVideoInternal(p);
cached_time_ = p.time;
return cached_texture_;
}
@@ -280,11 +280,9 @@ int64_t Decoder::GetImageSequenceIndex(const QString &filename)
return number_only.toLongLong();
}
TexturePtr Decoder::RetrieveVideoInternal(Renderer *renderer, const rational &timecode, const RetrieveVideoParams &divider, CancelAtom *cancelled)
TexturePtr Decoder::RetrieveVideoInternal(const RetrieveVideoParams &p)
{
Q_UNUSED(timecode)
Q_UNUSED(divider)
Q_UNUSED(cancelled)
Q_UNUSED(p)
return nullptr;
}
+8 -25
View File
@@ -164,29 +164,12 @@ public:
struct RetrieveVideoParams
{
RetrieveVideoParams()
{
divider = 1;
maximum_format = VideoParams::kFormatInvalid;
}
int divider;
VideoParams::Format maximum_format;
void reset()
{
*this = RetrieveVideoParams();
}
bool operator==(const RetrieveVideoParams& rhs) const
{
return divider == rhs.divider && maximum_format == rhs.maximum_format;
}
bool operator!=(const RetrieveVideoParams& rhs) const
{
return !(*this == rhs);
}
Renderer *renderer = nullptr;
rational time;
int divider = 1;
VideoParams::Format maximum_format = VideoParams::kFormatInvalid;
CancelAtom *cancelled = nullptr;
VideoParams::ColorRange force_range = VideoParams::kColorRangeDefault;
};
/**
@@ -199,7 +182,7 @@ public:
*
* This function is thread safe and can only run while the decoder is open. \see Open()
*/
TexturePtr RetrieveVideo(Renderer *renderer, const rational& timecode, const RetrieveVideoParams& params, CancelAtom *cancelled = nullptr);
TexturePtr RetrieveVideo(const RetrieveVideoParams& p);
enum RetrieveAudioStatus {
kInvalid = -1,
@@ -294,7 +277,7 @@ protected:
* Sub-classes must override this function IF they support video. Function is already mutexed
* so sub-classes don't need to worry about thread safety.
*/
virtual TexturePtr RetrieveVideoInternal(Renderer *renderer, const rational& timecode, const RetrieveVideoParams& params, CancelAtom *cancelled);
virtual TexturePtr RetrieveVideoInternal(const RetrieveVideoParams& p);
virtual bool ConformAudioInternal(const QVector<QString>& filenames, const AudioParams &params, CancelAtom *cancelled);
-1
View File
@@ -90,7 +90,6 @@ EncodingParams::EncodingParams() :
video_buffer_size_(0),
video_threads_(0),
video_is_image_sequence_(false),
video_color_range_(kYUVDefault),
audio_enabled_(false),
audio_bit_rate_(0),
subtitles_enabled_(false),
-11
View File
@@ -43,14 +43,6 @@ using EncoderPtr = std::shared_ptr<Encoder>;
class EncodingParams {
public:
enum YUVRange
{
kYUVMPEG16_235,
kYUVJPEG0_255,
kYUVDefault = kYUVMPEG16_235
};
EncodingParams();
void SetFilename(const QString& filename) { filename_ = filename; }
@@ -75,7 +67,6 @@ public:
void set_video_threads(const int& threads) { video_threads_ = threads; }
void set_video_pix_fmt(const QString& s) { video_pix_fmt_ = s; }
void set_video_is_image_sequence(bool s) { video_is_image_sequence_ = s; }
void set_video_color_range(YUVRange r) { video_color_range_ = r; }
void set_color_transform(const ColorTransform& color_transform) { color_transform_ = color_transform; }
const QString& filename() const { return filename_; }
@@ -91,7 +82,6 @@ public:
const int& video_threads() const { return video_threads_; }
const QString& video_pix_fmt() const { return video_pix_fmt_; }
bool video_is_image_sequence() const { return video_is_image_sequence_; }
YUVRange video_color_range() const { return video_color_range_; }
const ColorTransform& color_transform() const { return color_transform_; }
bool audio_enabled() const { return audio_enabled_; }
@@ -126,7 +116,6 @@ private:
int video_threads_;
QString video_pix_fmt_;
bool video_is_image_sequence_;
YUVRange video_color_range_;
ColorTransform color_transform_;
bool audio_enabled_;
+27 -30
View File
@@ -141,28 +141,32 @@ bool FFmpegDecoder::OpenInternal()
return output_frame;
}*/
TexturePtr FFmpegDecoder::RetrieveVideoInternal(Renderer *renderer, const rational &timecode, const RetrieveVideoParams &params, CancelAtom *cancelled)
TexturePtr FFmpegDecoder::RetrieveVideoInternal(const RetrieveVideoParams &p)
{
if (AVFramePtr f = RetrieveFrame(timecode, cancelled)) {
if (cancelled && cancelled->IsCancelled()) {
if (AVFramePtr f = RetrieveFrame(p.time, p.cancelled)) {
if (p.cancelled && p.cancelled->IsCancelled()) {
return nullptr;
}
if (InitScaler(f.get(), params)) {
int &src_fmt = f.get()->format;
src_fmt = FFmpegUtils::ConvertJPEGSpaceToRegularSpace(static_cast<AVPixelFormat>(src_fmt));
f->color_range = p.force_range == VideoParams::kColorRangeFull ? AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG;
if (InitScaler(f.get(), p)) {
VideoParams vp(instance_.avstream()->codecpar->width,
instance_.avstream()->codecpar->height,
native_output_pix_fmt_,
native_channel_count_,
av_guess_sample_aspect_ratio(instance_.fmt_ctx(), instance_.avstream(), nullptr),
VideoParams::kInterlaceNone,
params.divider);
p.divider);
TexturePtr tex = nullptr;
const bool hwscale = true;
const bool hwscale = false;
// Attempt to use GLSL shader for faster YUV to RGB conversion
if (hwscale) {
AVPixelFormat src_fmt = AVPixelFormat(f.get()->format);
if (src_fmt == AV_PIX_FMT_YUV420P
|| src_fmt == AV_PIX_FMT_YUV422P
|| src_fmt == AV_PIX_FMT_YUV444P
@@ -171,13 +175,10 @@ TexturePtr FFmpegDecoder::RetrieveVideoInternal(Renderer *renderer, const ration
|| src_fmt == AV_PIX_FMT_YUV444P10LE
|| src_fmt == AV_PIX_FMT_YUV420P12LE
|| src_fmt == AV_PIX_FMT_YUV422P12LE
|| src_fmt == AV_PIX_FMT_YUV444P12LE
|| src_fmt == AV_PIX_FMT_YUVJ420P
|| src_fmt == AV_PIX_FMT_YUVJ422P
|| src_fmt == AV_PIX_FMT_YUVJ444P) {
|| src_fmt == AV_PIX_FMT_YUV444P12LE) {
if (Yuv2RgbShader.isNull()) {
// Compile shader
Yuv2RgbShader = renderer->CreateNativeShader(ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/yuv2rgb.frag"))));
Yuv2RgbShader = p.renderer->CreateNativeShader(ShaderCode(FileFunctions::ReadFileAsString(QStringLiteral(":/shaders/yuv2rgb.frag"))));
}
if (!Yuv2RgbShader.isNull()) {
@@ -187,9 +188,6 @@ TexturePtr FFmpegDecoder::RetrieveVideoInternal(Renderer *renderer, const ration
case AV_PIX_FMT_YUV420P:
case AV_PIX_FMT_YUV422P:
case AV_PIX_FMT_YUV444P:
case AV_PIX_FMT_YUVJ420P:
case AV_PIX_FMT_YUVJ422P:
case AV_PIX_FMT_YUVJ444P:
default:
px_size = 1;
bits_per_pixel = 8;
@@ -208,20 +206,14 @@ TexturePtr FFmpegDecoder::RetrieveVideoInternal(Renderer *renderer, const ration
break;
}
bool full_range = src_fmt == AV_PIX_FMT_YUVJ420P
|| src_fmt == AV_PIX_FMT_YUVJ422P
|| src_fmt == AV_PIX_FMT_YUVJ444P;
VideoParams plane_params = vp;
plane_params.set_channel_count(1);
plane_params.set_divider(1);
plane_params.set_format(native_internal_pix_fmt_);
TexturePtr y_plane = renderer->CreateTexture(plane_params, f->data[0], f->linesize[0] / px_size);
TexturePtr y_plane = p.renderer->CreateTexture(plane_params, f->data[0], f->linesize[0] / px_size);
if (src_fmt == AV_PIX_FMT_YUV420P
|| src_fmt == AV_PIX_FMT_YUV422P
|| src_fmt == AV_PIX_FMT_YUVJ420P
|| src_fmt == AV_PIX_FMT_YUVJ422P
|| src_fmt == AV_PIX_FMT_YUV420P10LE
|| src_fmt == AV_PIX_FMT_YUV422P10LE
|| src_fmt == AV_PIX_FMT_YUV420P12LE
@@ -230,21 +222,20 @@ TexturePtr FFmpegDecoder::RetrieveVideoInternal(Renderer *renderer, const ration
}
if (src_fmt == AV_PIX_FMT_YUV420P
|| src_fmt == AV_PIX_FMT_YUVJ420P
|| src_fmt == AV_PIX_FMT_YUV420P10LE
|| src_fmt == AV_PIX_FMT_YUV420P12LE) {
plane_params.set_height(plane_params.height()/2);
}
TexturePtr u_plane = renderer->CreateTexture(plane_params, f->data[1], f->linesize[1] / px_size);
TexturePtr v_plane = renderer->CreateTexture(plane_params, f->data[2], f->linesize[2] / px_size);
TexturePtr u_plane = p.renderer->CreateTexture(plane_params, f->data[1], f->linesize[1] / px_size);
TexturePtr v_plane = p.renderer->CreateTexture(plane_params, f->data[2], f->linesize[2] / px_size);
ShaderJob job;
job.Insert(QStringLiteral("y_channel"), NodeValue(NodeValue::kTexture, QVariant::fromValue(y_plane)));
job.Insert(QStringLiteral("u_channel"), NodeValue(NodeValue::kTexture, QVariant::fromValue(u_plane)));
job.Insert(QStringLiteral("v_channel"), NodeValue(NodeValue::kTexture, QVariant::fromValue(v_plane)));
job.Insert(QStringLiteral("bits_per_pixel"), NodeValue(NodeValue::kInt, bits_per_pixel));
job.Insert(QStringLiteral("full_range"), NodeValue(NodeValue::kBoolean, full_range));
job.Insert(QStringLiteral("full_range"), NodeValue(NodeValue::kBoolean, f->color_range == AVCOL_RANGE_JPEG));
const int *yuv_coeffs = sws_getCoefficients(FFmpegUtils::GetSwsColorspaceFromAVColorSpace(f.get()->colorspace));
job.Insert(QStringLiteral("yuv_crv"), NodeValue(NodeValue::kInt, yuv_coeffs[0]));
@@ -252,8 +243,8 @@ TexturePtr FFmpegDecoder::RetrieveVideoInternal(Renderer *renderer, const ration
job.Insert(QStringLiteral("yuv_cgv"), NodeValue(NodeValue::kInt, yuv_coeffs[3]));
job.Insert(QStringLiteral("yuv_cbu"), NodeValue(NodeValue::kInt, yuv_coeffs[1]));
tex = renderer->CreateTexture(vp);
renderer->BlitToTexture(Yuv2RgbShader, job, tex.get(), false);
tex = p.renderer->CreateTexture(vp);
p.renderer->BlitToTexture(Yuv2RgbShader, job, tex.get(), false);
}
}
}
@@ -261,6 +252,7 @@ TexturePtr FFmpegDecoder::RetrieveVideoInternal(Renderer *renderer, const ration
if (!tex) {
// Fallback to software pixel format conversion
int r;
r = av_buffersrc_add_frame_flags(buffersrc_ctx_, f.get(), AV_BUFFERSRC_FLAG_KEEP_REF);
if (r < 0) {
return nullptr;
@@ -270,7 +262,7 @@ TexturePtr FFmpegDecoder::RetrieveVideoInternal(Renderer *renderer, const ration
return nullptr;
}
tex = renderer->CreateTexture(vp, working_frame_->data[0], working_frame_->linesize[0] / vp.GetBytesPerPixel());
tex = p.renderer->CreateTexture(vp, working_frame_->data[0], working_frame_->linesize[0] / vp.GetBytesPerPixel());
av_frame_unref(working_frame_);
}
@@ -435,6 +427,7 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename, CancelAtom *can
stream.set_start_time(avstream->start_time);
stream.set_time_base(avstream->time_base);
stream.set_duration(avstream->duration);
stream.set_color_range(avstream->codecpar->color_range == AVCOL_RANGE_JPEG ? VideoParams::kColorRangeFull : VideoParams::kColorRangeLimited);
// Defaults to false, requires user intervention if incorrect
stream.set_premultiplied_alpha(false);
@@ -900,7 +893,11 @@ AVFramePtr FFmpegDecoder::RetrieveFrame(const rational& time, CancelAtom *cancel
bool FFmpegDecoder::InitScaler(AVFrame *input, const RetrieveVideoParams& params)
{
if (params == filter_params_ && filter_graph_ && input_fmt_ == input->format) {
if (params.divider == filter_params_.divider
&& params.force_range == filter_params_.force_range
&& params.maximum_format == filter_params_.maximum_format
&& filter_graph_
&& input_fmt_ == input->format) {
// We have an appropriate filter for these parameters, just return true
return true;
}
+1 -1
View File
@@ -62,7 +62,7 @@ public:
protected:
virtual bool OpenInternal() override;
virtual TexturePtr RetrieveVideoInternal(Renderer *renderer, const rational& timecode, const RetrieveVideoParams& params, CancelAtom *cancelled) override;
virtual TexturePtr RetrieveVideoInternal(const RetrieveVideoParams& p) override;
virtual bool ConformAudioInternal(const QVector<QString>& filenames, const AudioParams &params, CancelAtom *cancelled) override;
virtual void CloseInternal() override;
+7 -2
View File
@@ -57,6 +57,11 @@ QStringList FFmpegEncoder::GetPixelFormatsForCodec(ExportCodec::Codec c) const
if (codec_info) {
for (int i=0; codec_info->pix_fmts[i]!=-1; i++) {
if (FFmpegUtils::ConvertJPEGSpaceToRegularSpace(codec_info->pix_fmts[i]) != codec_info->pix_fmts[i]) {
// This is a deprecated "JPEG" space, skip it
continue;
}
const char* pix_fmt_name = av_get_pix_fmt_name(codec_info->pix_fmts[i]);
pix_fmts.append(pix_fmt_name);
}
@@ -172,7 +177,7 @@ bool FFmpegEncoder::Open()
AVFilterContext* range_filter;
snprintf(filter_args, FILTER_ARG_SZ, "in_range=full:out_range=%s",
params().video_color_range() == EncodingParams::kYUVJPEG0_255 ? "full" : "limited");
params().video_params().color_range() == VideoParams::kColorRangeFull ? "full" : "limited");
avfilter_graph_create_filter(&range_filter, avfilter_get_by_name("scale"), "range", filter_args, nullptr, video_scale_ctx_);
@@ -610,7 +615,7 @@ bool FFmpegEncoder::InitializeStream(AVMediaType type, AVStream** stream_ptr, AV
codec_ctx->time_base = params().video_params().frame_rate_as_time_base().toAVRational();
codec_ctx->framerate = params().video_params().frame_rate().toAVRational();
codec_ctx->pix_fmt = av_get_pix_fmt(params().video_pix_fmt().toUtf8());
codec_ctx->color_range = params().video_color_range() == EncodingParams::kYUVJPEG0_255 ? AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG;
codec_ctx->color_range = params().video_params().color_range() == VideoParams::kColorRangeFull ? AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG;
if (params().video_params().interlacing() != VideoParams::kInterlaceNone) {
// FIXME: I actually don't know what these flags do, the documentation helpfully doesn't
+7 -9
View File
@@ -119,22 +119,20 @@ bool OIIODecoder::OpenInternal()
return OpenImageHandler(stream().filename(), stream().stream());
}
TexturePtr OIIODecoder::RetrieveVideoInternal(Renderer *renderer, const rational &timecode, const RetrieveVideoParams &params, CancelAtom *cancelled)
TexturePtr OIIODecoder::RetrieveVideoInternal(const RetrieveVideoParams &p)
{
Q_UNUSED(timecode)
Q_UNUSED(cancelled)
VideoParams vp = GetVideoParamsFromImageSpec(image_->spec());
vp.set_divider(params.divider);
vp.set_divider(p.divider);
if (!buffer_.is_allocated() || last_params_ != params) {
last_params_ = params;
if (!buffer_.is_allocated()
|| last_params_.divider != p.divider) {
last_params_ = p;
buffer_.destroy();
buffer_.set_video_params(vp);
buffer_.allocate();
if (params.divider == 1) {
if (p.divider == 1) {
// Just upload straight to the buffer
image_->read_image(oiio_pix_fmt_, buffer_.data(), OIIO::AutoStride, buffer_.linesize_bytes());
} else {
@@ -156,7 +154,7 @@ TexturePtr OIIODecoder::RetrieveVideoInternal(Renderer *renderer, const rational
}
}
return renderer->CreateTexture(vp, buffer_.data(), buffer_.linesize_pixels());
return p.renderer->CreateTexture(vp, buffer_.data(), buffer_.linesize_pixels());
}
void OIIODecoder::CloseInternal()
+1 -1
View File
@@ -44,7 +44,7 @@ public:
protected:
virtual bool OpenInternal() override;
virtual TexturePtr RetrieveVideoInternal(Renderer *renderer, const rational& timecode, const RetrieveVideoParams& params, CancelAtom *cancelled) override;
virtual TexturePtr RetrieveVideoInternal(const RetrieveVideoParams& p) override;
virtual void CloseInternal() override;
private: