@@ -93,7 +93,7 @@ bool Decoder::Open(const CodecStream &stream)
|
||||
}
|
||||
}
|
||||
|
||||
TexturePtr Decoder::RetrieveVideo(Renderer *renderer, const rational &timecode, const RetrieveVideoParams ÷r, 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 ÷r, CancelAtom *cancelled)
|
||||
TexturePtr Decoder::RetrieveVideoInternal(const RetrieveVideoParams &p)
|
||||
{
|
||||
Q_UNUSED(timecode)
|
||||
Q_UNUSED(divider)
|
||||
Q_UNUSED(cancelled)
|
||||
Q_UNUSED(p)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
+8
-25
@@ -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 ¶ms, CancelAtom *cancelled);
|
||||
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -141,28 +141,32 @@ bool FFmpegDecoder::OpenInternal()
|
||||
return output_frame;
|
||||
}*/
|
||||
|
||||
TexturePtr FFmpegDecoder::RetrieveVideoInternal(Renderer *renderer, const rational &timecode, const RetrieveVideoParams ¶ms, 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;
|
||||
}
|
||||
|
||||
@@ -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 ¶ms, CancelAtom *cancelled) override;
|
||||
virtual void CloseInternal() override;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -119,22 +119,20 @@ bool OIIODecoder::OpenInternal()
|
||||
return OpenImageHandler(stream().filename(), stream().stream());
|
||||
}
|
||||
|
||||
TexturePtr OIIODecoder::RetrieveVideoInternal(Renderer *renderer, const rational &timecode, const RetrieveVideoParams ¶ms, 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()
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -133,6 +133,21 @@ int FFmpegUtils::GetSwsColorspaceFromAVColorSpace(AVColorSpace cs)
|
||||
return SWS_CS_DEFAULT;
|
||||
}
|
||||
|
||||
AVPixelFormat FFmpegUtils::ConvertJPEGSpaceToRegularSpace(AVPixelFormat f)
|
||||
{
|
||||
switch (f) {
|
||||
case AV_PIX_FMT_YUVJ420P: return AV_PIX_FMT_YUV420P;
|
||||
case AV_PIX_FMT_YUVJ422P: return AV_PIX_FMT_YUV422P;
|
||||
case AV_PIX_FMT_YUVJ444P: return AV_PIX_FMT_YUV444P;
|
||||
case AV_PIX_FMT_YUVJ440P: return AV_PIX_FMT_YUV440P;
|
||||
case AV_PIX_FMT_YUVJ411P: return AV_PIX_FMT_YUV411P;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
AVPixelFormat FFmpegUtils::GetFFmpegPixelFormat(const VideoParams::Format &pix_fmt, int channel_layout)
|
||||
{
|
||||
if (channel_layout == VideoParams::kRGBChannelCount) {
|
||||
|
||||
@@ -66,6 +66,15 @@ public:
|
||||
* convenience function to do this conversion for us? Who knows, but here we are.
|
||||
*/
|
||||
static int GetSwsColorspaceFromAVColorSpace(AVColorSpace cs);
|
||||
|
||||
/**
|
||||
* @brief Convert "JPEG"/full-range colorspace to its regular counterpart
|
||||
*
|
||||
* "JPEG "spaces are deprecated in favor of the regular space and setting `color_range`. For the
|
||||
* time being, FFmpeg still uses these JPEG spaces, so for simplicity (since we *are* color_range
|
||||
* aware), we use this function.
|
||||
*/
|
||||
static AVPixelFormat ConvertJPEGSpaceToRegularSpace(AVPixelFormat f);
|
||||
};
|
||||
|
||||
using AVFramePtr = std::shared_ptr<AVFrame>;
|
||||
|
||||
@@ -557,6 +557,9 @@ ExportParams ExportDialog::GenerateParams() const
|
||||
|
||||
if (video_enabled_->isChecked()) {
|
||||
ExportCodec::Codec video_codec = video_tab_->GetSelectedCodec();
|
||||
|
||||
video_render_params.set_color_range(video_tab_->color_range());
|
||||
|
||||
params.EnableVideo(video_render_params, video_codec);
|
||||
|
||||
params.set_video_threads(video_tab_->threads());
|
||||
@@ -568,7 +571,6 @@ ExportParams ExportDialog::GenerateParams() const
|
||||
params.set_color_transform(video_tab_->CurrentOCIOColorSpace());
|
||||
|
||||
params.set_video_pix_fmt(video_tab_->pix_fmt());
|
||||
params.set_video_color_range(video_tab_->yuv_range());
|
||||
|
||||
params.set_video_is_image_sequence(video_tab_->IsImageSequenceSet());
|
||||
}
|
||||
|
||||
@@ -36,12 +36,12 @@ public:
|
||||
pixel_format_combobox_->setCurrentText(s);
|
||||
}
|
||||
|
||||
EncodingParams::YUVRange yuv_range() const
|
||||
VideoParams::ColorRange yuv_range() const
|
||||
{
|
||||
return static_cast<EncodingParams::YUVRange>(yuv_color_range_combobox_->currentIndex());
|
||||
return static_cast<VideoParams::ColorRange>(yuv_color_range_combobox_->currentIndex());
|
||||
}
|
||||
|
||||
void set_yuv_range(EncodingParams::YUVRange i)
|
||||
void set_yuv_range(VideoParams::ColorRange i)
|
||||
{
|
||||
yuv_color_range_combobox_->setCurrentIndex(i);
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ ExportVideoTab::ExportVideoTab(ColorManager* color_manager, QWidget *parent) :
|
||||
QWidget(parent),
|
||||
color_manager_(color_manager),
|
||||
threads_(0),
|
||||
yuv_range_(EncodingParams::kYUVDefault)
|
||||
color_range_(VideoParams::kColorRangeDefault)
|
||||
{
|
||||
QVBoxLayout* outer_layout = new QVBoxLayout(this);
|
||||
|
||||
@@ -212,12 +212,12 @@ void ExportVideoTab::OpenAdvancedDialog()
|
||||
|
||||
d.set_threads(threads_);
|
||||
d.set_pix_fmt(pix_fmt_);
|
||||
d.set_yuv_range(yuv_range_);
|
||||
d.set_yuv_range(color_range_);
|
||||
|
||||
if (d.exec() == QDialog::Accepted) {
|
||||
threads_ = d.threads();
|
||||
pix_fmt_ = d.pix_fmt();
|
||||
yuv_range_ = d.yuv_range();
|
||||
color_range_ = d.yuv_range();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -138,9 +138,9 @@ public:
|
||||
return pix_fmt_;
|
||||
}
|
||||
|
||||
EncodingParams::YUVRange yuv_range() const
|
||||
VideoParams::ColorRange color_range() const
|
||||
{
|
||||
return yuv_range_;
|
||||
return color_range_;
|
||||
}
|
||||
|
||||
public slots:
|
||||
@@ -183,7 +183,7 @@ private:
|
||||
int threads_;
|
||||
|
||||
QString pix_fmt_;
|
||||
EncodingParams::YUVRange yuv_range_;
|
||||
VideoParams::ColorRange color_range_;
|
||||
|
||||
ExportFormat::Format format_;
|
||||
|
||||
|
||||
@@ -83,11 +83,6 @@ private:
|
||||
*/
|
||||
QStackedWidget* stacked_widget_;
|
||||
|
||||
/**
|
||||
* @brief ComboBox for interlacing setting
|
||||
*/
|
||||
QComboBox* interlacing_box;
|
||||
|
||||
/**
|
||||
* @brief Media name text field
|
||||
*/
|
||||
|
||||
@@ -79,6 +79,17 @@ VideoStreamProperties::VideoStreamProperties(Footage *footage, int video_index)
|
||||
|
||||
video_layout->addWidget(video_color_space_, row, 1);
|
||||
|
||||
row++;
|
||||
|
||||
video_layout->addWidget(new QLabel(tr("Color Range:")), row, 0);
|
||||
|
||||
color_range_combo_ = new QComboBox();
|
||||
color_range_combo_->addItem(tr("Limited (16-235)"), VideoParams::kColorRangeLimited);
|
||||
color_range_combo_->addItem(tr("Full (0-255)"), VideoParams::kColorRangeFull);
|
||||
color_range_combo_->setCurrentIndex(vp.color_range());
|
||||
|
||||
video_layout->addWidget(color_range_combo_, row, 1);
|
||||
|
||||
if (vp.channel_count() == VideoParams::kRGBAChannelCount) {
|
||||
row++;
|
||||
|
||||
@@ -136,14 +147,16 @@ void VideoStreamProperties::Accept(MultiUndoCommand *parent)
|
||||
if ((video_premultiply_alpha_ && video_premultiply_alpha_->isChecked() != vp.premultiplied_alpha())
|
||||
|| set_colorspace != vp.colorspace()
|
||||
|| static_cast<VideoParams::Interlacing>(video_interlace_combo_->currentIndex()) != vp.interlacing()
|
||||
|| pixel_aspect_combo_->GetPixelAspectRatio() != vp.pixel_aspect_ratio()) {
|
||||
|| pixel_aspect_combo_->GetPixelAspectRatio() != vp.pixel_aspect_ratio()
|
||||
|| color_range_combo_->currentData().toInt() != vp.color_range()) {
|
||||
|
||||
parent->add_child(new VideoStreamChangeCommand(footage_,
|
||||
video_index_,
|
||||
video_premultiply_alpha_ ? video_premultiply_alpha_->isChecked() : vp.premultiplied_alpha(),
|
||||
set_colorspace,
|
||||
static_cast<VideoParams::Interlacing>(video_interlace_combo_->currentIndex()),
|
||||
pixel_aspect_combo_->GetPixelAspectRatio()));
|
||||
pixel_aspect_combo_->GetPixelAspectRatio(),
|
||||
static_cast<VideoParams::ColorRange>(color_range_combo_->currentData().toInt())));
|
||||
}
|
||||
|
||||
if (vp.video_type() == VideoParams::kVideoTypeImageSequence) {
|
||||
@@ -181,13 +194,14 @@ VideoStreamProperties::VideoStreamChangeCommand::VideoStreamChangeCommand(Footag
|
||||
bool premultiplied,
|
||||
QString colorspace,
|
||||
VideoParams::Interlacing interlacing,
|
||||
const rational &pixel_ar) :
|
||||
const rational &pixel_ar, VideoParams::ColorRange range) :
|
||||
footage_(footage),
|
||||
video_index_(video_index),
|
||||
new_premultiplied_(premultiplied),
|
||||
new_colorspace_(colorspace),
|
||||
new_interlacing_(interlacing),
|
||||
new_pixel_ar_(pixel_ar)
|
||||
new_pixel_ar_(pixel_ar),
|
||||
new_range_(range)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -204,11 +218,13 @@ void VideoStreamProperties::VideoStreamChangeCommand::redo()
|
||||
old_colorspace_ = vp.colorspace();
|
||||
old_interlacing_ = vp.interlacing();
|
||||
old_pixel_ar_ = vp.pixel_aspect_ratio();
|
||||
old_range_ = vp.color_range();
|
||||
|
||||
vp.set_premultiplied_alpha(new_premultiplied_);
|
||||
vp.set_colorspace(new_colorspace_);
|
||||
vp.set_interlacing(new_interlacing_);
|
||||
vp.set_pixel_aspect_ratio(new_pixel_ar_);
|
||||
vp.set_color_range(new_range_);
|
||||
|
||||
footage_->SetVideoParams(vp, video_index_);
|
||||
}
|
||||
@@ -221,6 +237,7 @@ void VideoStreamProperties::VideoStreamChangeCommand::undo()
|
||||
vp.set_colorspace(old_colorspace_);
|
||||
vp.set_interlacing(old_interlacing_);
|
||||
vp.set_pixel_aspect_ratio(old_pixel_ar_);
|
||||
vp.set_color_range(old_range_);
|
||||
|
||||
footage_->SetVideoParams(vp, video_index_);
|
||||
}
|
||||
|
||||
@@ -56,6 +56,11 @@ private:
|
||||
*/
|
||||
QComboBox* video_color_space_;
|
||||
|
||||
/**
|
||||
* @brief Setting for this streams's color range
|
||||
*/
|
||||
QComboBox *color_range_combo_;
|
||||
|
||||
/**
|
||||
* @brief Setting for video interlacing
|
||||
*/
|
||||
@@ -88,7 +93,8 @@ private:
|
||||
bool premultiplied,
|
||||
QString colorspace,
|
||||
VideoParams::Interlacing interlacing,
|
||||
const rational& pixel_ar);
|
||||
const rational& pixel_ar,
|
||||
VideoParams::ColorRange range);
|
||||
|
||||
virtual Project* GetRelevantProject() const override;
|
||||
|
||||
@@ -104,11 +110,13 @@ private:
|
||||
QString new_colorspace_;
|
||||
VideoParams::Interlacing new_interlacing_;
|
||||
rational new_pixel_ar_;
|
||||
VideoParams::ColorRange new_range_;
|
||||
|
||||
bool old_premultiplied_;
|
||||
QString old_colorspace_;
|
||||
VideoParams::Interlacing old_interlacing_;
|
||||
rational old_pixel_ar_;
|
||||
VideoParams::ColorRange old_range_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -519,6 +519,7 @@ VideoParams Footage::MergeVideoStream(const VideoParams &base, const VideoParams
|
||||
merged.set_colorspace(over.colorspace());
|
||||
merged.set_premultiplied_alpha(over.premultiplied_alpha());
|
||||
merged.set_video_type(over.video_type());
|
||||
merged.set_color_range(over.color_range());
|
||||
if (merged.video_type() == VideoParams::kVideoTypeImageSequence) {
|
||||
merged.set_start_time(over.start_time());
|
||||
merged.set_duration(over.duration());
|
||||
|
||||
@@ -142,7 +142,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr unsigned kFootageMetaVersion = 4;
|
||||
static constexpr unsigned kFootageMetaVersion = 5;
|
||||
|
||||
QString decoder_;
|
||||
|
||||
|
||||
@@ -475,7 +475,14 @@ void RenderProcessor::ProcessVideoFootage(TexturePtr destination, const FootageJ
|
||||
VideoParams tex_params = stream.video_params();
|
||||
|
||||
if (tex_params.is_valid()) {
|
||||
TexturePtr unmanaged_texture = decoder->RetrieveVideo(render_ctx_, (stream_data.video_type() == VideoParams::kVideoTypeVideo) ? input_time : Decoder::kAnyTimecode, p, GetCancelPointer());
|
||||
TexturePtr unmanaged_texture;
|
||||
|
||||
p.renderer = render_ctx_;
|
||||
p.time = (stream_data.video_type() == VideoParams::kVideoTypeVideo) ? input_time : Decoder::kAnyTimecode;
|
||||
p.cancelled = GetCancelPointer();
|
||||
p.force_range = stream_data.color_range();
|
||||
|
||||
unmanaged_texture = decoder->RetrieveVideo(p);
|
||||
|
||||
if (unmanaged_texture) {
|
||||
// We convert to our rendering pixel format, since that will always be float-based which
|
||||
|
||||
@@ -270,6 +270,7 @@ void VideoParams::set_defaults_for_footage()
|
||||
premultiplied_alpha_ = false;
|
||||
x_ = 0;
|
||||
y_ = 0;
|
||||
color_range_ = kColorRangeDefault;
|
||||
}
|
||||
|
||||
void VideoParams::calculate_square_pixel_width()
|
||||
@@ -374,6 +375,8 @@ void VideoParams::Load(QXmlStreamReader *reader)
|
||||
set_premultiplied_alpha(reader->readElementText().toInt());
|
||||
} else if (reader->name() == QStringLiteral("colorspace")) {
|
||||
set_colorspace(reader->readElementText());
|
||||
} else if (reader->name() == QStringLiteral("colorrange")) {
|
||||
set_color_range(static_cast<ColorRange>(reader->readElementText().toInt()));
|
||||
} else {
|
||||
reader->skipCurrentElement();
|
||||
}
|
||||
@@ -401,6 +404,7 @@ void VideoParams::Save(QXmlStreamWriter *writer) const
|
||||
writer->writeTextElement(QStringLiteral("duration"), QString::number(duration_));
|
||||
writer->writeTextElement(QStringLiteral("premultipliedalpha"), QString::number(premultiplied_alpha_));
|
||||
writer->writeTextElement(QStringLiteral("colorspace"), colorspace_);
|
||||
writer->writeTextElement(QStringLiteral("colorrange"), QString::number(color_range_));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -66,6 +66,14 @@ public:
|
||||
kVideoTypeStill,
|
||||
kVideoTypeImageSequence
|
||||
};
|
||||
enum ColorRange
|
||||
{
|
||||
kColorRangeLimited, // 16_235
|
||||
kColorRangeFull, // 0-255
|
||||
|
||||
kColorRangeDefault = kColorRangeLimited
|
||||
};
|
||||
|
||||
|
||||
VideoParams();
|
||||
VideoParams(int width, int height, Format format, int nb_channels,
|
||||
@@ -352,6 +360,9 @@ public:
|
||||
colorspace_ = c;
|
||||
}
|
||||
|
||||
const ColorRange &color_range() const { return color_range_; }
|
||||
void set_color_range(const ColorRange &color_range) { color_range_ = color_range; }
|
||||
|
||||
int64_t get_time_in_timebase_units(const rational& time) const;
|
||||
|
||||
void Load(QXmlStreamReader* reader);
|
||||
@@ -398,6 +409,7 @@ private:
|
||||
QString colorspace_;
|
||||
float x_;
|
||||
float y_;
|
||||
ColorRange color_range_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user