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:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user