various: implement sequence pixel aspect ratios and interlacing settings

Implements the following:
- Sequences have pixel aspect ratios that work in tandem with footage PARs
  to render footage correctly. Viewer and export also acknowledge PARs
- Sequences can have interlacing settings. This doesn't do anything yet,
  eventually the renderer will need to interlace/deinterlace/reinterlace
  appropriately in order to conform all the footage to the sequence. Export
  acknowledges interlacing, but this only affects metadata, not the image.
This commit is contained in:
itsmattkc
2020-08-10 01:47:26 +10:00
parent 901f1001af
commit 28dcff10c0
51 changed files with 1201 additions and 605 deletions
+11 -5
View File
@@ -463,8 +463,9 @@ bool FFmpegDecoder::Probe(Footage *f, const QAtomicInt* cancelled)
if (avstream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
bool image_is_still = false;
ImageStream::Interlacing interlacing = ImageStream::kInterlaceNone;
rational pixel_aspect_ratio;
rational frame_rate;
VideoParams::Interlacing interlacing = VideoParams::kInterlaceNone;
{
// Read at least two frames to get more information about this video stream
@@ -479,15 +480,19 @@ bool FFmpegDecoder::Probe(Footage *f, const QAtomicInt* cancelled)
// Check if video is interlaced and what field dominance it has if so
if (frame->interlaced_frame) {
if (frame->top_field_first) {
interlacing = ImageStream::kInterlacedTopFirst;
interlacing = VideoParams::kInterlacedTopFirst;
} else {
interlacing = ImageStream::kInterlacedBottomFirst;
interlacing = VideoParams::kInterlacedBottomFirst;
}
}
pixel_aspect_ratio = av_guess_sample_aspect_ratio(instance.fmt_ctx(),
instance.stream(),
frame);
frame_rate = av_guess_frame_rate(instance.fmt_ctx(),
instance.stream(),
frame);
}
// Read second frame
@@ -521,7 +526,7 @@ bool FFmpegDecoder::Probe(Footage *f, const QAtomicInt* cancelled)
} else {
VideoStreamPtr video_stream = std::make_shared<VideoStream>();
video_stream->set_frame_rate(av_guess_frame_rate(fmt_ctx, avstream, nullptr));
video_stream->set_frame_rate(frame_rate);
video_stream->set_start_time(avstream->start_time);
image_stream = video_stream;
@@ -783,9 +788,10 @@ FramePtr FFmpegDecoder::BuffersToNativeFrame(int divider, int width, int height,
copy->set_video_params(VideoParams(width,
height,
native_pix_fmt_,
std::static_pointer_cast<ImageStream>(stream())->pixel_aspect_ratio(),
std::static_pointer_cast<ImageStream>(stream())->interlacing(),
divider));
copy->set_timestamp(Timecode::timestamp_to_time(ts, time_base_));
copy->set_sample_aspect_ratio(std::static_pointer_cast<ImageStream>(stream())->pixel_aspect_ratio());
copy->allocate();
// Convert frame to RGB/A for the rest of the pipeline
+30 -1
View File
@@ -138,6 +138,17 @@ bool FFmpegEncoder::WriteFrame(FramePtr frame, rational time)
encoded_frame->height = frame->height();
encoded_frame->format = video_codec_ctx_->pix_fmt;
// Set interlacing
if (frame->video_params().interlacing() != VideoParams::kInterlaceNone) {
encoded_frame->interlaced_frame = 1;
if (frame->video_params().interlacing() == VideoParams::kInterlacedTopFirst) {
encoded_frame->top_field_first = 1;
} else {
encoded_frame->top_field_first = 0;
}
}
error_code = av_frame_get_buffer(encoded_frame, 0);
if (error_code < 0) {
FFmpegError("Failed to create AVFrame buffer", error_code);
@@ -443,10 +454,28 @@ bool FFmpegEncoder::InitializeStream(AVMediaType type, AVStream** stream_ptr, AV
if (type == AVMEDIA_TYPE_VIDEO) {
codec_ctx->width = params().video_params().width();
codec_ctx->height = params().video_params().height();
codec_ctx->sample_aspect_ratio = {1, 1};
codec_ctx->sample_aspect_ratio = params().video_params().pixel_aspect_ratio().toAVRational();
codec_ctx->time_base = params().video_params().time_base().toAVRational();
codec_ctx->pix_fmt = av_get_pix_fmt(params().video_pix_fmt().toUtf8());
if (params().video_params().interlacing() != VideoParams::kInterlaceNone) {
// FIXME: I actually don't know what these flags do, the documentation helpfully doesn't
// explain them at all. I hope using both of them is the right thing to do.
codec_ctx->flags |= AV_CODEC_FLAG_INTERLACED_DCT | AV_CODEC_FLAG_INTERLACED_ME;
if (params().video_params().interlacing() == VideoParams::kInterlacedTopFirst) {
codec_ctx->field_order = AV_FIELD_TT;
} else {
codec_ctx->field_order = AV_FIELD_BB;
if (codec_id == AV_CODEC_ID_H264) {
// For some reason, FFmpeg doesn't set libx264's bff flag so we have to do it ourselves
av_opt_set(video_codec_ctx_->priv_data, "x264opts", "bff=1", AV_OPT_SEARCH_CHILDREN);
}
}
}
// Set custom options
{
QHash<QString, QString>::const_iterator i;
+1 -16
View File
@@ -27,8 +27,7 @@
OLIVE_NAMESPACE_ENTER
Frame::Frame() :
timestamp_(0),
sample_aspect_ratio_(1)
timestamp_(0)
{
}
@@ -106,20 +105,6 @@ void Frame::set_pixel(int x, int y, const Color &c)
c.toData(data_.data() + byte_offset, video_params().format());
}
const rational &Frame::sample_aspect_ratio() const
{
return sample_aspect_ratio_;
}
void Frame::set_sample_aspect_ratio(const rational &aspect_ratio)
{
if (aspect_ratio.isNull()) {
sample_aspect_ratio_ = 1;
} else {
sample_aspect_ratio_ = aspect_ratio;
}
}
const rational &Frame::timestamp() const
{
return timestamp_;
-5
View File
@@ -57,9 +57,6 @@ public:
bool contains_pixel(int x, int y) const;
void set_pixel(int x, int y, const Color& c);
const rational& sample_aspect_ratio() const;
void set_sample_aspect_ratio(const rational& sample_aspect_ratio);
/**
* @brief Get frame's timestamp.
*
@@ -116,8 +113,6 @@ private:
int64_t native_timestamp_;
rational sample_aspect_ratio_;
int linesize_;
};
+10 -5
View File
@@ -118,16 +118,14 @@ bool OIIODecoder::Probe(Footage *f, const QAtomicInt *cancelled)
image_stream->set_width(in->spec().width);
image_stream->set_height(in->spec().height);
image_stream->set_format(GetFormatFromOIIOBasetype(in->spec()));
// FIXME: Haven't looked, does OIIO report pixel aspect ratio somewhere?
image_stream->set_pixel_aspect_ratio(1);
image_stream->set_pixel_aspect_ratio(GetPixelAspectRatioFromOIIO(in->spec()));
// Images will always have just one stream
image_stream->set_index(0);
// OIIO automatically premultiplies alpha
// FIXME: We usually disassociate the alpha for the color management later, for 8-bit images this likely reduces the
// fidelity?
// FIXME: We usually disassociate the alpha for the color management later, for 8-bit images this
// likely reduces the fidelity?
image_stream->set_premultiplied_alpha(true);
// Get stats for this image and dump them into the Footage file
@@ -181,6 +179,8 @@ FramePtr OIIODecoder::RetrieveVideo(const rational &timecode, const int& divider
frame->set_video_params(VideoParams(buffer_->spec().width,
buffer_->spec().height,
pix_fmt_,
GetPixelAspectRatioFromOIIO(buffer_->spec()),
VideoParams::kInterlaceNone, // FIXME: Does OIIO deinterlace for us?
divider));
frame->allocate();
@@ -294,6 +294,11 @@ PixelFormat::Format OIIODecoder::GetFormatFromOIIOBasetype(const OIIO::ImageSpec
}
}
rational OIIODecoder::GetPixelAspectRatioFromOIIO(const OpenImageIO_v2_1::ImageSpec &spec)
{
return rational::fromDouble(spec.extra_attribs.get_float("PixelAspectRatio", 1));
}
bool OIIODecoder::FileTypeIsSupported(const QString& fn)
{
// We prioritize OIIO over FFmpeg to pick up still images more effectively, but some OIIO decoders (notably OpenJPEG)
+2
View File
@@ -57,6 +57,8 @@ public:
static PixelFormat::Format GetFormatFromOIIOBasetype(const OIIO::ImageSpec& spec);
static rational GetPixelAspectRatioFromOIIO(const OIIO::ImageSpec& spec);
private:
#if OIIO_VERSION < 10903
OIIO::ImageInput* image_;