Refactor FFmpeg decoder for improved readability and efficiency

Simplify the frame reading process and metadata extraction in `ffmpegdecoder.cpp` by reducing redundant code and improving variable initialization. Add a helper function in `ffmpegdecoder.h` to map FFmpeg field orders to Olive interlacing types, enhancing code clarity. Adjust the condition for interleaved write in `ffmpegencoder.cpp` for better handling.
This commit is contained in:
2025-11-10 21:06:35 +08:00
parent c1b8618391
commit 2ea53667c0
4 changed files with 82 additions and 101 deletions
+61 -99
View File
@@ -28,6 +28,7 @@ extern "C" {
#include <libavutil/imgutils.h>
#include <libavutil/opt.h>
#include <libavutil/pixdesc.h>
#include <libavutil/frame.h>
}
#include <OpenImageIO/imagebuf.h>
@@ -382,116 +383,77 @@ FootageDescription FFmpegDecoder::Probe(const QString &filename,
VideoParams::kInterlaceNone;
{
// Read at least two frames to get more information about this video stream
AVPacket *pkt = av_packet_alloc();
AVFrame *frame = av_frame_alloc();
// Read at least two frames to get more information about this video stream
AVPacket *pkt = av_packet_alloc();
AVFrame *frame = av_frame_alloc();
{
Instance instance;
instance.Open(filename_c, avstream->index);
VideoParams::Interlacing interlacing = VideoParams::kInterlaceNone;
AVRational pixel_aspect_ratio = {1, 1};
AVRational frame_rate = avstream->avg_frame_rate;
AVPixelFormat compatible_pix_fmt =
FFmpegUtils::GetCompatiblePixelFormat(
static_cast<AVPixelFormat>(avstream->codecpar->format));
bool image_is_still = false;
// Read first frame and retrieve some metadata
if (instance.GetFrame(pkt, frame) >= 0) {
// Check if video is interlaced and what field dominance it has if so
if (frame->interlaced_frame) {
if (frame->top_field_first) {
interlacing =
VideoParams::kInterlacedTopFirst;
} else {
interlacing =
VideoParams::kInterlacedBottomFirst;
}
}
{
Instance instance;
if (instance.Open(filename_c, avstream->index) != 0)
goto cleanup;
pixel_aspect_ratio =
av_guess_sample_aspect_ratio(
instance.fmt_ctx(), instance.avstream(),
frame);
AVCodecContext *avctx = instance.codec_ctx();
interlacing = FFmpegFieldOrderToOlive(avctx->field_order);
frame_rate = av_guess_frame_rate(
instance.fmt_ctx(), instance.avstream(),
frame);
if (instance.GetFrame(pkt, frame) >= 0) {
pixel_aspect_ratio =
av_guess_sample_aspect_ratio(instance.fmt_ctx(),
instance.avstream(), frame);
frame_rate =
av_guess_frame_rate(instance.fmt_ctx(),
instance.avstream(), frame);
}
compatible_pix_fmt =
FFmpegUtils::GetCompatiblePixelFormat(
static_cast<AVPixelFormat>(
avstream->codecpar->format));
}
int ret = instance.GetFrame(pkt, frame);
if (ret == AVERROR_EOF) {
image_is_still = true;
} else if (avstream->duration == AV_NOPTS_VALUE ||
duration_guessed_from_bitrate) {
int64_t last_ts = frame->best_effort_timestamp;
while (instance.GetFrame(pkt, frame) >= 0 &&
(!cancelled || !cancelled->IsCancelled()))
last_ts = frame->best_effort_timestamp;
avstream->duration = last_ts;
}
// Read second frame
int ret = instance.GetFrame(pkt, frame);
instance.Close();
}
if (ret >= 0) {
// Check if we need a manual duration
if (avstream->duration == AV_NOPTS_VALUE ||
duration_guessed_from_bitrate) {
if (footage_duration == AV_NOPTS_VALUE ||
duration_guessed_from_bitrate) {
// Manually read through file for duration
int64_t new_dur;
cleanup:
av_frame_free(&frame);
av_packet_free(&pkt);
do {
new_dur =
frame->best_effort_timestamp;
} while (instance.GetFrame(
pkt, frame) >= 0 &&
(!cancelled ||
!cancelled->IsCancelled()));
VideoParams stream;
stream.set_stream_index(i);
stream.set_width(avstream->codecpar->width);
stream.set_height(avstream->codecpar->height);
stream.set_video_type(image_is_still ? VideoParams::kVideoTypeStill
: VideoParams::kVideoTypeVideo);
stream.set_format(GetNativePixelFormat(compatible_pix_fmt));
stream.set_channel_count(GetNativeChannelCount(compatible_pix_fmt));
stream.set_interlacing(interlacing); // <-- 已正确填充
stream.set_pixel_aspect_ratio(pixel_aspect_ratio);
stream.set_frame_rate(frame_rate);
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);
stream.set_premultiplied_alpha(false);
avstream->duration = new_dur;
} else {
// Fallback to footage duration
avstream->duration =
Timecode::rescale_timestamp_ceil(
footage_duration,
rational(1, AV_TIME_BASE),
avstream->time_base);
}
}
} else if (ret == AVERROR_EOF) {
// Video has only one frame in it, treat it like a still image
image_is_still = true;
}
instance.Close();
}
av_frame_free(&frame);
av_packet_free(&pkt);
desc.AddVideoStream(stream);
image_is_still ? still_streams++ : video_streams++;
}
VideoParams stream;
stream.set_stream_index(i);
stream.set_width(avstream->codecpar->width);
stream.set_height(avstream->codecpar->height);
stream.set_video_type((image_is_still) ?
VideoParams::kVideoTypeStill :
VideoParams::kVideoTypeVideo);
stream.set_format(GetNativePixelFormat(compatible_pix_fmt));
stream.set_channel_count(
GetNativeChannelCount(compatible_pix_fmt));
stream.set_interlacing(interlacing);
stream.set_pixel_aspect_ratio(pixel_aspect_ratio);
stream.set_frame_rate(frame_rate);
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);
desc.AddVideoStream(stream);
if (image_is_still) {
still_streams++;
} else {
video_streams++;
}
} else if (avstream->codecpar->codec_type ==
AVMEDIA_TYPE_AUDIO) {