diff --git a/res/vcpkg/ffmpeg/patch/0013-fix-qsv-encode-timeouts.patch b/res/vcpkg/ffmpeg/patch/0013-fix-qsv-encode-timeouts.patch new file mode 100644 index 000000000..33c035bdb --- /dev/null +++ b/res/vcpkg/ffmpeg/patch/0013-fix-qsv-encode-timeouts.patch @@ -0,0 +1,67 @@ +diff --git a/libavcodec/qsvenc.c b/libavcodec/qsvenc.c +--- a/libavcodec/qsvenc.c ++++ b/libavcodec/qsvenc.c +@@ -43,6 +43,9 @@ + #include "qsv_internal.h" + #include "qsvenc.h" + #include "refstruct.h" ++ ++#define QSV_MAX_ENCODE_RETRIES 100 ++#define QSV_SYNC_TIMEOUT_US 2000000 + + struct profile_names { + mfxU16 profile; +@@ -2468,6 +2471,7 @@ + QSVFrame *qsv_frame = NULL; + mfxEncodeCtrl* enc_ctrl = NULL; + int ret; ++ int retries = 0; + + if (frame) { + ret = submit_frame(q, frame, &qsv_frame); +@@ -2538,6 +2542,18 @@ + + do { + ret = MFXVideoENCODE_EncodeFrameAsync(q->session, enc_ctrl, surf, pkt.bs, pkt.sync); ++ if ((ret == MFX_WRN_DEVICE_BUSY || ret == MFX_WRN_IN_EXECUTION) && ++ retries++ >= QSV_MAX_ENCODE_RETRIES) { ++ av_log(avctx, AV_LOG_ERROR, "QSV encoder submission retry limit exceeded\n"); ++ ret = AVERROR_EXTERNAL; ++ goto free; ++ } ++#ifdef _WIN32 ++ // Sub-millisecond waits become Sleep(0) on Windows. ++ if (ret == MFX_WRN_DEVICE_BUSY || ret == MFX_WRN_IN_EXECUTION) ++ av_usleep(1000); ++#else + if (ret == MFX_WRN_DEVICE_BUSY) + av_usleep(500); ++#endif + } while (ret == MFX_WRN_DEVICE_BUSY || ret == MFX_WRN_IN_EXECUTION); +@@ -2656,12 +2672,23 @@ + mfxExtAVCEncodedFrameInfo *enc_info; + mfxExtBuffer **enc_buf; + enum AVPictureType pict_type; +- +- av_fifo_read(q->async_fifo, &qpkt, 1); ++ int64_t sync_deadline = av_gettime_relative() + QSV_SYNC_TIMEOUT_US; ++ ++ // The SDK may still use these buffers after a synchronization timeout. ++ av_fifo_peek(q->async_fifo, &qpkt, 1, 0); + + do { +- ret = MFXVideoCORE_SyncOperation(q->session, *qpkt.sync, 1000); ++ int64_t remaining = sync_deadline - av_gettime_relative(); ++ if (remaining <= 0) { ++ av_log(avctx, AV_LOG_ERROR, "QSV encoder synchronization timed out\n"); ++ return AVERROR_EXTERNAL; ++ } ++ ret = MFXVideoCORE_SyncOperation(q->session, *qpkt.sync, ++ FFMIN(1000, (remaining + 999) / 1000)); ++ if (ret == MFX_WRN_IN_EXECUTION) ++ av_usleep(1000); + } while (ret == MFX_WRN_IN_EXECUTION); ++ av_fifo_drain2(q->async_fifo, 1); + + qpkt.pkt.dts = av_rescale_q(qpkt.bs->DecodeTimeStamp, (AVRational){1, 90000}, avctx->time_base); + qpkt.pkt.pts = av_rescale_q(qpkt.bs->TimeStamp, (AVRational){1, 90000}, avctx->time_base); diff --git a/res/vcpkg/ffmpeg/patch/0014-fix-qsv-encode-buffer.patch b/res/vcpkg/ffmpeg/patch/0014-fix-qsv-encode-buffer.patch new file mode 100644 index 000000000..655722ce7 --- /dev/null +++ b/res/vcpkg/ffmpeg/patch/0014-fix-qsv-encode-buffer.patch @@ -0,0 +1,64 @@ +diff --git a/libavcodec/qsvenc.c b/libavcodec/qsvenc.c +--- a/libavcodec/qsvenc.c ++++ b/libavcodec/qsvenc.c +@@ -1419,6 +1419,18 @@ + return 0; + } + ++static int qsv_update_packet_size(QSVEncContext *q, const mfxInfoMFX *mfx) ++{ ++ // Intel documents a larger buffer for complex CQP frames. ++ // https://github.com/Intel-Media-SDK/MediaSDK/blob/master/doc/mediasdk_release_notes.md ++ int64_t packet_size = (int64_t)mfx->BufferSizeInKB * mfx->BRCParamMultiplier * 1000 * 4; ++ ++ if (packet_size > INT_MAX - AV_INPUT_BUFFER_PADDING_SIZE) ++ return AVERROR(ENOMEM); ++ q->packet_size = FFMAX(q->packet_size, packet_size); ++ return 0; ++} ++ + static int qsv_retrieve_enc_params(AVCodecContext *avctx, QSVEncContext *q) + { + AVCPBProperties *cpb_props; +@@ -1508,7 +1520,13 @@ + return ff_qsv_print_error(avctx, ret, + "Error calling GetVideoParam"); + +- q->packet_size = q->param.mfx.BufferSizeInKB * q->param.mfx.BRCParamMultiplier * 1000; ++ if (avctx->codec_id == AV_CODEC_ID_H264 || avctx->codec_id == AV_CODEC_ID_HEVC) { ++ ret = qsv_update_packet_size(q, &q->param.mfx); ++ if (ret < 0) ++ return ret; ++ } else { ++ q->packet_size = q->param.mfx.BufferSizeInKB * q->param.mfx.BRCParamMultiplier * 1000; ++ } + + if (!extradata.SPSBufSize || (need_pps && !extradata.PPSBufSize) + || (q->hevc_vps && !extradata_vps.VPSBufSize) +@@ -2650,6 +2668,17 @@ + if (ret < 0) + return ff_qsv_print_error(avctx, ret, "Error during resetting"); + ++ if (avctx->codec_id == AV_CODEC_ID_H264 || avctx->codec_id == AV_CODEC_ID_HEVC) { ++ mfxVideoParam param = { 0 }; ++ ++ ret = MFXVideoENCODE_GetVideoParam(q->session, ¶m); ++ if (ret < 0) ++ return ff_qsv_print_error(avctx, ret, "Error querying parameters after reset"); ++ ret = qsv_update_packet_size(q, ¶m.mfx); ++ if (ret < 0) ++ return ret; ++ } ++ + return 0; + } + +@@ -2688,6 +2717,8 @@ + if (ret == MFX_WRN_IN_EXECUTION) + av_usleep(1000); + } while (ret == MFX_WRN_IN_EXECUTION); ++ if (ret < 0) ++ return ff_qsv_print_error(avctx, ret, "Error synchronizing encoded frame"); + av_fifo_drain2(q->async_fifo, 1); + + qpkt.pkt.dts = av_rescale_q(qpkt.bs->DecodeTimeStamp, (AVRational){1, 90000}, avctx->time_base); diff --git a/res/vcpkg/ffmpeg/patch/0015-fix-hybrid-mp4-finalization.patch b/res/vcpkg/ffmpeg/patch/0015-fix-hybrid-mp4-finalization.patch new file mode 100644 index 000000000..2d82e407e --- /dev/null +++ b/res/vcpkg/ffmpeg/patch/0015-fix-hybrid-mp4-finalization.patch @@ -0,0 +1,89 @@ +From: Martin Storsjö +Subject: movenc: Make the hybrid_fragmented mode more robust +Source: https://github.com/FFmpeg/FFmpeg/commit/36896af64a9ecf6835758186e7161a2e954c364d + +movenc: Make the hybrid_fragmented mode more robust + +Write the moov tag at the end first, before overwriting the mdat size +at the start of the file. + +In case writing the final moov box fails (e.g. due to being out +of disk), we haven't broken the initial moov box yet. + +Thus if writing stops between these steps, we could end up with +a file with two moov boxes - which arguably is more feasible to +recover from, than from a file with no moov boxes at all. + +diff --git a/libavformat/movenc.c b/libavformat/movenc.c +--- a/libavformat/movenc.c ++++ b/libavformat/movenc.c +@@ -8546,6 +8546,28 @@ static int shift_data(AVFormatContext *s) + return ff_format_shift_data(s, mov->reserved_header_pos, moov_size); + } + ++static void mov_write_mdat_size(AVFormatContext *s) ++{ ++ MOVMuxContext *mov = s->priv_data; ++ AVIOContext *pb = s->pb; ++ ++ /* Write size of mdat tag */ ++ if (mov->mdat_size + 8 <= UINT32_MAX) { ++ avio_seek(pb, mov->mdat_pos, SEEK_SET); ++ avio_wb32(pb, mov->mdat_size + 8); ++ if (mov->flags & FF_MOV_FLAG_HYBRID_FRAGMENTED) ++ ffio_wfourcc(pb, "mdat"); // overwrite the original moov into a mdat ++ } else { ++ /* overwrite 'wide' placeholder atom */ ++ avio_seek(pb, mov->mdat_pos - 8, SEEK_SET); ++ /* special value: real atom size will be 64 bit value after ++ * tag field */ ++ avio_wb32(pb, 1); ++ ffio_wfourcc(pb, "mdat"); ++ avio_wb64(pb, mov->mdat_size + 16); ++ } ++} ++ + static int mov_write_trailer(AVFormatContext *s) + { + MOVMuxContext *mov = s->priv_data; +@@ -8605,21 +8627,9 @@ static int mov_write_trailer(AVFormatContext *s) + + moov_pos = avio_tell(pb); + +- /* Write size of mdat tag */ +- if (mov->mdat_size + 8 <= UINT32_MAX) { +- avio_seek(pb, mov->mdat_pos, SEEK_SET); +- avio_wb32(pb, mov->mdat_size + 8); +- if (mov->flags & FF_MOV_FLAG_HYBRID_FRAGMENTED) +- ffio_wfourcc(pb, "mdat"); // overwrite the original moov into a mdat +- } else { +- /* overwrite 'wide' placeholder atom */ +- avio_seek(pb, mov->mdat_pos - 8, SEEK_SET); +- /* special value: real atom size will be 64 bit value after +- * tag field */ +- avio_wb32(pb, 1); +- ffio_wfourcc(pb, "mdat"); +- avio_wb64(pb, mov->mdat_size + 16); +- } ++ if (!(mov->flags & FF_MOV_FLAG_HYBRID_FRAGMENTED)) ++ mov_write_mdat_size(s); ++ + avio_seek(pb, mov->reserved_moov_size > 0 ? mov->reserved_header_pos : moov_pos, SEEK_SET); + + if (mov->flags & FF_MOV_FLAG_FASTSTART) { +@@ -8647,6 +8657,15 @@ static int mov_write_trailer(AVFormatContext *s) + if ((res = mov_write_moov_tag(pb, mov, s)) < 0) + return res; + } ++ ++ if (mov->flags & FF_MOV_FLAG_HYBRID_FRAGMENTED) { ++ // With hybrid fragmentation, only write the mdat size (hiding ++ // the original moov and all the fragments within the mdat) ++ // after we've successfully written the complete moov, to avoid ++ // risk for an unreadable file if writing the final moov fails. ++ mov_write_mdat_size(s); ++ } ++ + res = 0; + } else { + mov_auto_flush_fragment(s, 1); diff --git a/res/vcpkg/ffmpeg/portfile.cmake b/res/vcpkg/ffmpeg/portfile.cmake index c0856480b..1b923dd35 100644 --- a/res/vcpkg/ffmpeg/portfile.cmake +++ b/res/vcpkg/ffmpeg/portfile.cmake @@ -30,6 +30,9 @@ vcpkg_from_github( patch/0010.disable-loading-DLLs-from-app-dir.patch patch/0011-android-mediacodec-encode-align-64.patch patch/0012-fix-macos-big-sur-CVBufferCopyAttachments.patch + patch/0013-fix-qsv-encode-timeouts.patch + patch/0014-fix-qsv-encode-buffer.patch + patch/0015-fix-hybrid-mp4-finalization.patch ) if(SOURCE_PATH MATCHES " ")