diff --git a/app/codec/ffmpeg/ffmpegdecoder.cpp b/app/codec/ffmpeg/ffmpegdecoder.cpp index db5c4d588..a412f7dd5 100644 --- a/app/codec/ffmpeg/ffmpegdecoder.cpp +++ b/app/codec/ffmpeg/ffmpegdecoder.cpp @@ -28,6 +28,7 @@ extern "C" { #include #include #include +#include } #include @@ -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(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( - 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) { diff --git a/app/codec/ffmpeg/ffmpegdecoder.h b/app/codec/ffmpeg/ffmpegdecoder.h index e904ef2f9..f99f88b0b 100644 --- a/app/codec/ffmpeg/ffmpegdecoder.h +++ b/app/codec/ffmpeg/ffmpegdecoder.h @@ -42,6 +42,20 @@ extern "C" { namespace olive { +// 头文件里先备好枚举映射 +static VideoParams::Interlacing FFmpegFieldOrderToOlive(AVFieldOrder fo) +{ + switch (fo) { + case AV_FIELD_TT: // 隔行,顶场在前 + return VideoParams::kInterlacedTopFirst; + case AV_FIELD_BB: // 隔行,底场在前 + return VideoParams::kInterlacedBottomFirst; + case AV_FIELD_PROGRESSIVE: + default: + return VideoParams::kInterlaceNone; + } +} + /** * @brief A Decoder derivative that wraps FFmpeg functions as on Olive decoder */ @@ -124,6 +138,10 @@ private: { return avstream_; } + AVCodecContext *codec_ctx() + { + return codec_ctx_; + } private: AVFormatContext *fmt_ctx_; diff --git a/app/codec/ffmpeg/ffmpegencoder.cpp b/app/codec/ffmpeg/ffmpegencoder.cpp index 5f7ea1d87..951392114 100644 --- a/app/codec/ffmpeg/ffmpegencoder.cpp +++ b/app/codec/ffmpeg/ffmpegencoder.cpp @@ -24,6 +24,7 @@ extern "C" { #include #include #include +#include } #include @@ -868,7 +869,7 @@ void FFmpegEncoder::FlushEncoders() } if (fmt_ctx_) { - if (fmt_ctx_->oformat->flags & AVFMT_ALLOW_FLUSH) { + if (fmt_ctx_->oformat->flags) { int r = av_interleaved_write_frame(fmt_ctx_, nullptr); if (r < 0) { FFmpegError(tr("Failed to write interleaved packet"), r); diff --git a/ext/KDDockWidgets b/ext/KDDockWidgets index 8d2d0a576..f24a04252 160000 --- a/ext/KDDockWidgets +++ b/ext/KDDockWidgets @@ -1 +1 @@ -Subproject commit 8d2d0a5764f8393cc148a2296d511276a8ffe559 +Subproject commit f24a04252ba319ef323769e766b3ba5252c15c4c