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