diff --git a/.forgejo/CODEOWNERS b/.forgejo/CODEOWNERS index 0d310fb71f..a4ea6dd50f 100644 --- a/.forgejo/CODEOWNERS +++ b/.forgejo/CODEOWNERS @@ -20,10 +20,9 @@ libavcodec/.*ffv1.* @lynne @michaelni libavcodec/golomb.* @michaelni libavcodec/.*h266.* @frankplow @NuoMi @jianhuaw libavcodec/h26x/.* @frankplow @NuoMi @jianhuaw -libavcodec/.*jpegxl.* @lynne @Traneptora -libavcodec/.*jxl.* @lynne @Traneptora +libavcodec/.*jpegxl.* @lynne +libavcodec/.*jxl.* @lynne libavcodec/.*opus.* @lynne -libavcodec/.*png.* @Traneptora libavcodec/.*prores.* @lynne libavcodec/rangecoder.* @michaelni libavcodec/ratecontrol.* @michaelni @@ -46,8 +45,6 @@ libavfilter/vsrc_mandelbrot.* @michaelni # avformat # ======= libavformat/iamf.* @jamrial -libavformat/.*jpegxl.* @Traneptora -libavformat/.*jxl.* @Traneptora # avutil # ====== diff --git a/Changelog b/Changelog index 98b259f17f..0b0e6ecbf4 100644 --- a/Changelog +++ b/Changelog @@ -8,7 +8,6 @@ version 8.0: - Whisper filter - Drop support for OpenSSL < 1.1.0 - Enable TLS peer certificate verification by default (on next major version bump) -- Drop support for OpenSSL < 1.1.1 - yasm support dropped, users need to use nasm - VVC VAAPI decoder - RealVideo 6.0 decoder diff --git a/compat/w32pthreads.h b/compat/w32pthreads.h index 6f2734b470..fd6428e29f 100644 --- a/compat/w32pthreads.h +++ b/compat/w32pthreads.h @@ -44,7 +44,6 @@ #include "libavutil/internal.h" #include "libavutil/mem.h" #include "libavutil/time.h" -#include "libavutil/wchar_filename.h" typedef struct pthread_t { void *handle; @@ -210,38 +209,4 @@ static inline int pthread_setcancelstate(int state, int *oldstate) return 0; } -static inline int win32_thread_setname(const char *name) -{ -#if !HAVE_UWP - typedef HRESULT (WINAPI *SetThreadDescriptionFn)(HANDLE, PCWSTR); - - // Although SetThreadDescription lives in kernel32.dll, on Windows Server 2016, - // Windows 10 LTSB 2016 and Windows 10 version 1607, it was only available in - // kernelbase.dll. So, load it from there for maximum coverage. - HMODULE kernelbase = GetModuleHandleW(L"kernelbase.dll"); - if (!kernelbase) - return AVERROR(ENOSYS); - - SetThreadDescriptionFn pSetThreadDescription = - (SetThreadDescriptionFn)GetProcAddress(kernelbase, "SetThreadDescription"); - if (!pSetThreadDescription) - return AVERROR(ENOSYS); - - wchar_t *wname; - if (utf8towchar(name, &wname) < 0) - return AVERROR(ENOMEM); - - HRESULT hr = pSetThreadDescription(GetCurrentThread(), wname); - av_free(wname); - return SUCCEEDED(hr) ? 0 : AVERROR(EINVAL); -#else - // UWP is not supported because we cannot use LoadLibrary/GetProcAddress to - // detect the availability of the SetThreadDescription API. There is a small - // gap in Windows builds 1507-1607 where it was not available. UWP allows - // querying the availability of APIs with QueryOptionalDelayLoadedAPI, but it - // requires /DELAYLOAD:kernel32.dll during linking, and we cannot enforce that. - return AVERROR(ENOSYS); -#endif -} - #endif /* COMPAT_W32PTHREADS_H */ diff --git a/configure b/configure index e1809a3e58..416cd2d1d6 100755 --- a/configure +++ b/configure @@ -5174,7 +5174,7 @@ probe_cc(){ # but we can force it back to gnu mode and get the version from there. _ident=$($_cc -flavor gnu --version 2>/dev/null) _ld_o='-out:$@' - _flags_filter=msvc_flags_link + _flags_filter=msvc_flags _ld_lib='%.lib' _ld_path='-libpath:' elif VSLANG=1033 $_cc -nologo- 2>&1 | grep -q ^Microsoft || { $_cc -v 2>&1 | grep -q clang && $_cc -? > /dev/null 2>&1; }; then @@ -7260,10 +7260,10 @@ enabled omx && require_headers OMX_Core.h && \ enabled openssl && { { check_pkg_config openssl "openssl >= 3.0.0" openssl/ssl.h OPENSSL_init_ssl && { enabled gplv3 || ! enabled gpl || enabled nonfree || die "ERROR: OpenSSL >=3.0.0 requires --enable-version3"; }; } || { enabled gpl && ! enabled nonfree && die "ERROR: OpenSSL <3.0.0 is incompatible with the gpl"; } || - check_pkg_config openssl "openssl >= 1.1.1" openssl/ssl.h OPENSSL_init_ssl || + check_pkg_config openssl "openssl >= 1.1.0" openssl/ssl.h OPENSSL_init_ssl || check_lib openssl openssl/ssl.h OPENSSL_init_ssl -lssl -lcrypto || check_lib openssl openssl/ssl.h OPENSSL_init_ssl -lssl -lcrypto -lws2_32 -lgdi32 || - die "ERROR: openssl (>= 1.1.1) not found"; } + die "ERROR: openssl (>= 1.1.0) not found"; } enabled pocketsphinx && require_pkg_config pocketsphinx pocketsphinx pocketsphinx/pocketsphinx.h ps_init enabled rkmpp && { require_pkg_config rkmpp rockchip_mpp rockchip/rk_mpi.h mpp_create && require_pkg_config rockchip_mpp "rockchip_mpp >= 1.3.7" rockchip/rk_mpi.h mpp_create && diff --git a/libavcodec/d3d12va_encode_hevc.c b/libavcodec/d3d12va_encode_hevc.c index 0aa1a1d3a4..ce5d1bf110 100644 --- a/libavcodec/d3d12va_encode_hevc.c +++ b/libavcodec/d3d12va_encode_hevc.c @@ -762,7 +762,6 @@ static const FFCodecDefault d3d12va_encode_hevc_defaults[] = { { "b_qoffset", "0" }, { "qmin", "-1" }, { "qmax", "-1" }, - { "refs", "0" }, { NULL }, }; diff --git a/libavcodec/hw_base_encode.c b/libavcodec/hw_base_encode.c index 927aeb4bad..33a30c8d10 100644 --- a/libavcodec/hw_base_encode.c +++ b/libavcodec/hw_base_encode.c @@ -666,11 +666,6 @@ int ff_hw_base_init_gop_structure(FFHWBaseEncodeContext *ctx, AVCodecContext *av ctx->ref_l0 = FFMIN(ref_l0, MAX_PICTURE_REFERENCES); ctx->ref_l1 = FFMIN(ref_l1, MAX_PICTURE_REFERENCES); - if (avctx->refs > 0) { - ctx->ref_l0 = FFMIN(ctx->ref_l0, avctx->refs); - ctx->ref_l1 = FFMIN(ctx->ref_l1, avctx->refs); - } - if (flags & FF_HW_FLAG_INTRA_ONLY || avctx->gop_size <= 1) { av_log(avctx, AV_LOG_VERBOSE, "Using intra frames only.\n"); ctx->gop_size = 1; diff --git a/libavcodec/mfenc.c b/libavcodec/mfenc.c index 942c75cb9d..3569562b58 100644 --- a/libavcodec/mfenc.c +++ b/libavcodec/mfenc.c @@ -29,6 +29,7 @@ #include "libavutil/opt.h" #include "libavutil/time.h" #include "codec_internal.h" +#include "profiles.h" #include "internal.h" #include "compat/w32dlfcn.h" #if CONFIG_D3D11VA @@ -711,6 +712,26 @@ static int64_t mf_enca_output_score(AVCodecContext *avctx, IMFMediaType *type) score |= 4LL << 32; } + if (avctx->codec_id == AV_CODEC_ID_AAC) { + hr = IMFAttributes_GetUINT32(type, &MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION, &t); + switch (avctx->profile) { + case AV_PROFILE_AAC_LOW: + if (!FAILED(hr) && !(t >= 0x28 && t < 0x2c) && t != 0x50 && t != 0x51) + return -1; + break; + + case AV_PROFILE_AAC_HE: + if (!FAILED(hr) && !(t >= 0x2c && t < 0x30) && t != 0x52 && t != 0x53) + return -1; + break; + + case AV_PROFILE_AAC_HE_V2: + if (!FAILED(hr) && !(t >= 0x30 && t < 0x34)) + return -1; + break; + } + } + // Select the bitrate (lowest priority). hr = IMFAttributes_GetUINT32(type, &MF_MT_AUDIO_AVG_BYTES_PER_SECOND, &t); if (!FAILED(hr)) { @@ -986,12 +1007,32 @@ static int mf_choose_output_type(AVCodecContext *avctx) } if (ret >= 0) { + unsigned int profile_set; + unsigned int profile_negotiated; av_log(avctx, AV_LOG_VERBOSE, "setting output type:\n"); ff_media_type_dump(avctx, out_type); + IMFAttributes_GetUINT32(out_type, &MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION, &profile_set); + hr = IMFTransform_SetOutputType(c->mft, c->out_stream_id, out_type, 0); if (!FAILED(hr)) { - ret = 1; + unsigned int bitrate; + IMFAttributes_GetUINT32(out_type, &MF_MT_AUDIO_AVG_BYTES_PER_SECOND, &bitrate); + + if (avctx->codec_id == AV_CODEC_ID_AAC) { + IMFAttributes_GetUINT32(out_type, &MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION, &profile_negotiated); + + if (profile_set != profile_negotiated) { // Setting PROFILE_LEVEL_INDICATION in AAC Encoder is only supported starting from Win11 24H2, fail if reported a mismatch in profile level indication. + av_log(avctx, AV_LOG_ERROR, "profile level indication mismatch: (out_type profile_level) %d != (out_type_negotiated profile_level) %d\n", profile_set, profile_negotiated); + ret = AVERROR_EXTERNAL; + } else { + av_log(avctx, AV_LOG_INFO, "using bitrate %dkbps, profile level: 0x%02x\n", bitrate / 125, profile_set); + ret = 1; + } + } else { + av_log(avctx, AV_LOG_INFO, "using bitrate %dkbps\n", bitrate / 125); + ret = 1; + } } else if (hr == MF_E_TRANSFORM_TYPE_NOT_SET) { av_log(avctx, AV_LOG_VERBOSE, "rejected - need to set input type\n"); ret = 0; @@ -1427,7 +1468,13 @@ static av_cold int mf_init(AVCodecContext *avctx) .p.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_HYBRID | \ AV_CODEC_CAP_DR1 | AV_CODEC_CAP_VARIABLE_FRAME_SIZE, -MF_ENCODER(AUDIO, aac, AAC, NULL, AFMTS, ACAPS, NULL); +#define AE AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM +static const AVOption aac_opts[] = { + FF_AAC_PROFILE_OPTS + {NULL} +}; + +MF_ENCODER(AUDIO, aac, AAC, aac_opts, AFMTS, ACAPS, NULL); MF_ENCODER(AUDIO, ac3, AC3, NULL, AFMTS, ACAPS, NULL); MF_ENCODER(AUDIO, mp3, MP3, NULL, AFMTS, ACAPS, NULL); diff --git a/libavcodec/prores_raw.c b/libavcodec/prores_raw.c index b2aa97ddda..748e176815 100644 --- a/libavcodec/prores_raw.c +++ b/libavcodec/prores_raw.c @@ -367,6 +367,9 @@ static int decode_frame(AVCodecContext *avctx, if ((w & 1) || (h & 1)) return AVERROR_INVALIDDATA; + avctx->coded_width = FFALIGN(w, 16); + avctx->coded_height = FFALIGN(h, 16); + if (w != avctx->width || h != avctx->height) { av_log(avctx, AV_LOG_WARNING, "picture resolution change: %ix%i -> %ix%i\n", avctx->width, avctx->height, w, h); @@ -374,9 +377,6 @@ static int decode_frame(AVCodecContext *avctx, return ret; } - avctx->coded_width = FFALIGN(w, 16); - avctx->coded_height = FFALIGN(h, 16); - enum AVPixelFormat pix_fmt = AV_PIX_FMT_BAYER_RGGB16; if (pix_fmt != s->pix_fmt) { s->pix_fmt = pix_fmt; diff --git a/libavcodec/rv60dec.c b/libavcodec/rv60dec.c index 208fbc68f7..4a3d9067db 100644 --- a/libavcodec/rv60dec.c +++ b/libavcodec/rv60dec.c @@ -308,8 +308,6 @@ static int update_dimensions_clear_info(RV60Context *s, int width, int height) if ((ret = av_reallocp_array(&s->blk_info, s->blk_stride * (s->cu_height << 4), sizeof(s->blk_info[0]))) < 0) return ret; - memset(s->pu_info, 0, s->pu_stride * (s->cu_height << 3) * sizeof(s->pu_info[0])); - for (int j = 0; j < s->cu_height << 4; j++) for (int i = 0; i < s->cu_width << 4; i++) s->blk_info[j*s->blk_stride + i].mv.mvref = MVREF_NONE; diff --git a/libavcodec/sanm.c b/libavcodec/sanm.c index e4308af647..a066a864eb 100644 --- a/libavcodec/sanm.c +++ b/libavcodec/sanm.c @@ -1757,11 +1757,6 @@ static int process_frame_obj(SANMVideoContext *ctx, GetByteContext *gb) memset(ctx->fbuf, 0, ctx->frm0_size); } - if (w + FFMAX(left, 0) > ctx->avctx->width || h + FFMAX(top, 0) > ctx->avctx->height) { - avpriv_request_sample(ctx->avctx, "overly large frame\n"); - return AVERROR_PATCHWELCOME; - } - switch (codec) { case 1: case 3: @@ -2074,20 +2069,6 @@ static int codec2subblock(SANMVideoContext *ctx, int cx, int cy, int blk_size) mx = motion_vectors[opcode][0]; my = motion_vectors[opcode][1]; - /* The original implementation of this codec precomputes a table - * of int16_t all motion vectors for given image width. - * For larger widths, starting with 762 pixels, the calculation of - * mv table indices 1+ and 255- overflow the int16_t, inverting the - * sign of the offset. This is actively exploited in e.g. the - * "jonesopn_8.snm" video of "Indiana Jones and the Infernal Machine". - * Therefore let the overflow happen and extract x/y components from - * the new value. - */ - if (ctx->width > 761) { - index = (int16_t)(my * ctx->width + mx); - mx = index % ctx->width; - my = index / ctx->width; - } if (good_mvec(ctx, cx, cy, mx, my, blk_size)) { copy_block(ctx->frm0 + cx + ctx->pitch * cy, ctx->frm2 + cx + mx + ctx->pitch * (cy + my), diff --git a/libavcodec/utvideodec.c b/libavcodec/utvideodec.c index bc02ac44d5..934945c1be 100644 --- a/libavcodec/utvideodec.c +++ b/libavcodec/utvideodec.c @@ -585,7 +585,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *frame, int buf_size = avpkt->size; UtvideoContext *c = avctx->priv_data; int i, j; - const uint8_t *plane_start[5] = {NULL}; + const uint8_t *plane_start[5]; int plane_size, max_slice_size = 0, slice_start, slice_end, slice_size; int ret; GetByteContext gb; diff --git a/libavcodec/vaapi_encode_av1.c b/libavcodec/vaapi_encode_av1.c index 9d837f5c6b..3d8f29d599 100644 --- a/libavcodec/vaapi_encode_av1.c +++ b/libavcodec/vaapi_encode_av1.c @@ -1027,7 +1027,6 @@ static const FFCodecDefault vaapi_encode_av1_defaults[] = { { "g", "120" }, { "qmin", "1" }, { "qmax", "255" }, - { "refs", "0" }, { NULL }, }; diff --git a/libavcodec/vaapi_encode_h264.c b/libavcodec/vaapi_encode_h264.c index 27f6551719..0cd5b0ac50 100644 --- a/libavcodec/vaapi_encode_h264.c +++ b/libavcodec/vaapi_encode_h264.c @@ -1153,7 +1153,6 @@ static const FFCodecDefault vaapi_encode_h264_defaults[] = { { "b_qoffset", "0" }, { "qmin", "-1" }, { "qmax", "-1" }, - { "refs", "0" }, { NULL }, }; diff --git a/libavcodec/vaapi_encode_h265.c b/libavcodec/vaapi_encode_h265.c index baf0d77c8a..1539bfbf79 100644 --- a/libavcodec/vaapi_encode_h265.c +++ b/libavcodec/vaapi_encode_h265.c @@ -1184,7 +1184,6 @@ static const FFCodecDefault vaapi_encode_h265_defaults[] = { { "b_qoffset", "0" }, { "qmin", "-1" }, { "qmax", "-1" }, - { "refs", "0" }, { NULL }, }; diff --git a/libavcodec/vaapi_encode_vp8.c b/libavcodec/vaapi_encode_vp8.c index 69a56a17af..125d760b9b 100644 --- a/libavcodec/vaapi_encode_vp8.c +++ b/libavcodec/vaapi_encode_vp8.c @@ -235,7 +235,6 @@ static const FFCodecDefault vaapi_encode_vp8_defaults[] = { { "g", "120" }, { "qmin", "-1" }, { "qmax", "-1" }, - { "refs", "0" }, { NULL }, }; diff --git a/libavcodec/vaapi_encode_vp9.c b/libavcodec/vaapi_encode_vp9.c index ca8de541d9..e88967a053 100644 --- a/libavcodec/vaapi_encode_vp9.c +++ b/libavcodec/vaapi_encode_vp9.c @@ -292,7 +292,6 @@ static const FFCodecDefault vaapi_encode_vp9_defaults[] = { { "g", "250" }, { "qmin", "-1" }, { "qmax", "-1" }, - { "refs", "0" }, { NULL }, }; diff --git a/libavcodec/vulkan_encode_av1.c b/libavcodec/vulkan_encode_av1.c index 08ffbfa393..840092cf6d 100644 --- a/libavcodec/vulkan_encode_av1.c +++ b/libavcodec/vulkan_encode_av1.c @@ -1367,7 +1367,6 @@ static const FFCodecDefault vulkan_encode_av1_defaults[] = { { "g", "300" }, { "qmin", "1" }, { "qmax", "255" }, - { "refs", "0" }, { NULL }, }; diff --git a/libavcodec/vulkan_encode_h264.c b/libavcodec/vulkan_encode_h264.c index 942e911fb7..8bbb4639e6 100644 --- a/libavcodec/vulkan_encode_h264.c +++ b/libavcodec/vulkan_encode_h264.c @@ -1634,7 +1634,6 @@ static const FFCodecDefault vulkan_encode_h264_defaults[] = { { "b_qoffset", "0" }, { "qmin", "-1" }, { "qmax", "-1" }, - { "refs", "0" }, { NULL }, }; diff --git a/libavcodec/vulkan_encode_h265.c b/libavcodec/vulkan_encode_h265.c index c30b7e8f93..f0ec852557 100644 --- a/libavcodec/vulkan_encode_h265.c +++ b/libavcodec/vulkan_encode_h265.c @@ -1761,7 +1761,6 @@ static const FFCodecDefault vulkan_encode_h265_defaults[] = { { "b_qoffset", "0" }, { "qmin", "-1" }, { "qmax", "-1" }, - { "refs", "0" }, { NULL }, }; diff --git a/libavcodec/x86/Makefile b/libavcodec/x86/Makefile index ebd2bdb310..ebf458453e 100644 --- a/libavcodec/x86/Makefile +++ b/libavcodec/x86/Makefile @@ -170,7 +170,6 @@ X86ASM-OBJS-$(CONFIG_MLP_DECODER) += x86/mlpdsp.o X86ASM-OBJS-$(CONFIG_MPEG4_DECODER) += x86/xvididct.o X86ASM-OBJS-$(CONFIG_PNG_DECODER) += x86/pngdsp.o X86ASM-OBJS-$(CONFIG_PRORES_DECODER) += x86/proresdsp.o -X86ASM-OBJS-$(CONFIG_PRORES_RAW_DECODER) += x86/proresdsp.o X86ASM-OBJS-$(CONFIG_RV40_DECODER) += x86/rv40dsp.o X86ASM-OBJS-$(CONFIG_SBC_ENCODER) += x86/sbcdsp.o X86ASM-OBJS-$(CONFIG_SVQ1_ENCODER) += x86/svq1enc.o diff --git a/libavfilter/af_whisper.c b/libavfilter/af_whisper.c index 385180b4ed..186b624504 100644 --- a/libavfilter/af_whisper.c +++ b/libavfilter/af_whisper.c @@ -215,9 +215,7 @@ static void run_transcription(AVFilterContext *ctx, AVFrame *frame, int samples) for (int i = 0; i < n_segments; ++i) { const char *text = whisper_full_get_segment_text(wctx->ctx_wsp, i); - if (av_isspace(text[0])) - text++; - char *text_cleaned = av_strireplace(text, "[BLANK_AUDIO]", ""); + char *text_cleaned = av_strireplace(text + 1, "[BLANK_AUDIO]", ""); if (av_strnlen(text_cleaned, 1) == 0) { av_freep(&text_cleaned); @@ -430,8 +428,7 @@ static int query_formats(const AVFilterContext *ctx, #define HOURS 3600000000 static const AVOption whisper_options[] = { - { "model", "Path to the whisper.cpp model file", OFFSET(model_path), AV_OPT_TYPE_STRING,.flags = FLAGS }, - { "language", "Language for transcription ('auto' for auto-detect)", OFFSET(language), AV_OPT_TYPE_STRING, {.str = "auto"}, .flags = FLAGS }, + { "model", "Path to the whisper.cpp model file", OFFSET(model_path), AV_OPT_TYPE_STRING,.flags = FLAGS }, { "language", "Language for transcription ('auto' for auto-detect)", OFFSET(language), AV_OPT_TYPE_STRING, {.str = "auto"}, .flags = FLAGS }, { "queue", "Audio queue size", OFFSET(queue), AV_OPT_TYPE_DURATION, {.i64 = 3000000}, 20000, HOURS, .flags = FLAGS }, { "use_gpu", "Use GPU for processing", OFFSET(use_gpu), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, .flags = FLAGS }, { "gpu_device", "GPU device to use", OFFSET(gpu_device), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, .flags = FLAGS }, diff --git a/libavformat/avio.h b/libavformat/avio.h index fbcb7dbfc3..ebf611187d 100644 --- a/libavformat/avio.h +++ b/libavformat/avio.h @@ -461,17 +461,17 @@ int avio_put_str16be(AVIOContext *s, const char *str); void avio_write_marker(AVIOContext *s, int64_t time, enum AVIODataMarkerType type); /** - * Passing this as the "whence" parameter to a seek function causes it to + * ORing this as the "whence" parameter to a seek function causes it to * return the filesize without seeking anywhere. Supporting this is optional. * If it is not supported then the seek function will return <0. */ #define AVSEEK_SIZE 0x10000 /** - * OR'ing this flag into the "whence" parameter to a seek function causes it to + * Passing this flag as the "whence" parameter to a seek function causes it to * seek by any means (like reopening and linear reading) or other normally unreasonable * means that can be extremely slow. - * This is the default and therefore ignored by the seek code since 2010. + * This may be ignored by the seek code. */ #define AVSEEK_FORCE 0x20000 diff --git a/libavformat/aviobuf.c b/libavformat/aviobuf.c index 2e65f50006..34743556ae 100644 --- a/libavformat/aviobuf.c +++ b/libavformat/aviobuf.c @@ -238,9 +238,10 @@ int64_t avio_seek(AVIOContext *s, int64_t offset, int whence) FFIOContext *const ctx = ffiocontext(s); int64_t offset1; int64_t pos; + int force = whence & AVSEEK_FORCE; int buffer_size; int short_seek; - whence &= ~AVSEEK_FORCE; // force flag does nothing + whence &= ~AVSEEK_FORCE; if(!s) return AVERROR(EINVAL); @@ -281,7 +282,8 @@ int64_t avio_seek(AVIOContext *s, int64_t offset, int whence) } else if ((!(s->seekable & AVIO_SEEKABLE_NORMAL) || offset1 <= buffer_size + short_seek) && !s->write_flag && offset1 >= 0 && - (!s->direct || !s->seek)) { + (!s->direct || !s->seek) && + (whence != SEEK_END || force)) { while(s->pos < offset && !s->eof_reached) fill_buffer(s); if (s->eof_reached) @@ -298,7 +300,7 @@ int64_t avio_seek(AVIOContext *s, int64_t offset, int whence) s->pos = pos; s->eof_reached = 0; fill_buffer(s); - return avio_seek(s, offset, SEEK_SET); + return avio_seek(s, offset, SEEK_SET | force); } else { int64_t res; if (s->write_flag) { diff --git a/libavformat/mov.c b/libavformat/mov.c index e9a582e5aa..86037c6712 100644 --- a/libavformat/mov.c +++ b/libavformat/mov.c @@ -5456,6 +5456,10 @@ static int heif_add_stream(MOVContext *c, HEIFItem *item) if (!sc->chunk_offsets) goto fail; sc->chunk_count = 1; + sc->sample_sizes = av_malloc_array(1, sizeof(*sc->sample_sizes)); + if (!sc->sample_sizes) + goto fail; + sc->sample_count = 1; sc->stts_data = av_malloc_array(1, sizeof(*sc->stts_data)); if (!sc->stts_data) goto fail; @@ -10467,13 +10471,11 @@ static int mov_parse_heif_items(AVFormatContext *s) st->codecpar->width = item->width; st->codecpar->height = item->height; - sc->sample_size = sc->stsz_sample_size = item->extent_length; - sc->sample_count = 1; - - err = sanity_checks(s, sc, st->index); - if (err) + err = sanity_checks(s, sc, item->item_id); + if (err || !sc->sample_count) return AVERROR_INVALIDDATA; + sc->sample_sizes[0] = item->extent_length; sc->chunk_offsets[0] = item->extent_offset + offset; if (item->item_id == mov->primary_item_id) diff --git a/libavformat/tls.h b/libavformat/tls.h index 2afa248f4c..4d4999aa7c 100644 --- a/libavformat/tls.h +++ b/libavformat/tls.h @@ -34,6 +34,17 @@ */ #define MAX_CERTIFICATE_SIZE 8192 +enum DTLSState { + DTLS_STATE_NONE, + + /* Whether DTLS handshake is finished. */ + DTLS_STATE_FINISHED, + /* Whether DTLS session is closed. */ + DTLS_STATE_CLOSED, + /* Whether DTLS handshake is failed. */ + DTLS_STATE_FAILED, +}; + typedef struct TLSShared { char *ca_file; int verify; @@ -54,6 +65,8 @@ typedef struct TLSShared { int is_dtls; int use_srtp; + enum DTLSState state; + /* The certificate and private key content used for DTLS handshake */ char* cert_buf; char* key_buf; @@ -99,6 +112,8 @@ int ff_tls_set_external_socket(URLContext *h, URLContext *sock); int ff_dtls_export_materials(URLContext *h, char *dtls_srtp_materials, size_t materials_sz); +int ff_dtls_state(URLContext *h); + int ff_ssl_read_key_cert(char *key_url, char *cert_url, char *key_buf, size_t key_sz, char *cert_buf, size_t cert_sz, char **fingerprint); int ff_ssl_gen_key_cert(char *key_buf, size_t key_sz, char *cert_buf, size_t cert_sz, char **fingerprint); diff --git a/libavformat/tls_openssl.c b/libavformat/tls_openssl.c index db7147e491..bab2e711c6 100644 --- a/libavformat/tls_openssl.c +++ b/libavformat/tls_openssl.c @@ -35,82 +35,127 @@ #include /** - * Convert an EVP_PKEY to a PEM string. + * Returns a heap‐allocated null‐terminated string containing + * the PEM‐encoded public key. Caller must free. */ -static int pkey_to_pem_string(EVP_PKEY *pkey, char *out, size_t out_sz) -{ - BIO *mem = NULL; - size_t read_bytes = 0; - - if (!pkey || !out || !out_sz) - goto done; +static char *pkey_to_pem_string(EVP_PKEY *pkey) { + BIO *mem = NULL; + BUF_MEM *bptr = NULL; + char *pem_str = NULL; + // Create a memory BIO if (!(mem = BIO_new(BIO_s_mem()))) - goto done; + goto err; + // Write public key in PEM form if (!PEM_write_bio_PrivateKey(mem, pkey, NULL, NULL, 0, NULL, NULL)) - goto done; + goto err; - if (!BIO_read_ex(mem, out, out_sz - 1, &read_bytes)) - goto done; + // Extract pointer/length + BIO_get_mem_ptr(mem, &bptr); + if (!bptr || !bptr->length) + goto err; -done: + // Allocate string (+1 for NUL) + pem_str = av_malloc(bptr->length + 1); + if (!pem_str) + goto err; + + // Copy data & NUL‐terminate + memcpy(pem_str, bptr->data, bptr->length); + pem_str[bptr->length] = '\0'; + +cleanup: BIO_free(mem); - if (out && out_sz) - out[read_bytes] = '\0'; - return read_bytes; + return pem_str; + +err: + // error path: free and return NULL + free(pem_str); + pem_str = NULL; + goto cleanup; } /** - * Convert an X509 certificate to a PEM string. + * Serialize an X509 certificate to a av_malloc’d PEM string. + * Caller must free the returned pointer. */ -static int cert_to_pem_string(X509 *cert, char *out, size_t out_sz) +static char *cert_to_pem_string(X509 *cert) { - BIO *mem = NULL; - size_t read_bytes = 0; + BIO *mem = BIO_new(BIO_s_mem()); + BUF_MEM *bptr = NULL; + char *out = NULL; - if (!cert || !out || !out_sz) - goto done; - - if (!(mem = BIO_new(BIO_s_mem()))) - goto done; + if (!mem) goto err; + /* Write the PEM certificate */ if (!PEM_write_bio_X509(mem, cert)) - goto done; + goto err; - if (!BIO_read_ex(mem, out, out_sz - 1, &read_bytes)) - goto done; + BIO_get_mem_ptr(mem, &bptr); + if (!bptr || !bptr->length) goto err; -done: + out = av_malloc(bptr->length + 1); + if (!out) goto err; + + memcpy(out, bptr->data, bptr->length); + out[bptr->length] = '\0'; + +cleanup: BIO_free(mem); - if (out && out_sz) - out[read_bytes] = '\0'; - return read_bytes; + return out; + +err: + free(out); + out = NULL; + goto cleanup; } /** * Generate a SHA-256 fingerprint of an X.509 certificate. + * + * @param ctx AVFormatContext for logging (can be NULL) + * @param cert X509 certificate to fingerprint + * @return Newly allocated fingerprint string in "AA:BB:CC:…" format, + * or NULL on error (logs via av_log if ctx is not NULL). + * Caller must free() the returned string. */ -static int x509_fingerprint(X509 *cert, char **fingerprint) +static char *generate_fingerprint(X509 *cert) { unsigned char md[EVP_MAX_MD_SIZE]; int n = 0; - AVBPrint buf; + AVBPrint fingerprint; + char *result = NULL; + int i; + + /* To prevent a crash during cleanup, always initialize it. */ + av_bprint_init(&fingerprint, 0, AV_BPRINT_SIZE_UNLIMITED); if (X509_digest(cert, EVP_sha256(), md, &n) != 1) { - av_log(NULL, AV_LOG_ERROR, "TLS: Failed to generate fingerprint, %s\n", - ERR_error_string(ERR_get_error(), NULL)); - return AVERROR(ENOMEM); + av_log(NULL, AV_LOG_ERROR, "TLS: Failed to generate fingerprint, %s\n", ERR_error_string(ERR_get_error(), NULL)); + goto end; } - av_bprint_init(&buf, n*3, n*3); + for (i = 0; i < n; i++) { + av_bprintf(&fingerprint, "%02X", md[i]); + if (i + 1 < n) + av_bprintf(&fingerprint, ":"); + } - for (int i = 0; i < n - 1; i++) - av_bprintf(&buf, "%02X:", md[i]); - av_bprintf(&buf, "%02X", md[n - 1]); + if (!fingerprint.str || !strlen(fingerprint.str)) { + av_log(NULL, AV_LOG_ERROR, "TLS: Fingerprint is empty\n"); + goto end; + } - return av_bprint_finalize(&buf, fingerprint); + result = av_strdup(fingerprint.str); + if (!result) { + av_log(NULL, AV_LOG_ERROR, "TLS: Out of memory generating fingerprint\n"); + } + +end: + av_bprint_finalize(&fingerprint, NULL); + return result; } int ff_ssl_read_key_cert(char *key_url, char *cert_url, char *key_buf, size_t key_sz, char *cert_buf, size_t cert_sz, char **fingerprint) @@ -120,6 +165,7 @@ int ff_ssl_read_key_cert(char *key_url, char *cert_url, char *key_buf, size_t ke AVBPrint key_bp, cert_bp; EVP_PKEY *pkey = NULL; X509 *cert = NULL; + char *key_tem = NULL, *cert_tem = NULL; /* To prevent a crash during cleanup, always initialize it. */ av_bprint_init(&key_bp, 1, MAX_CERTIFICATE_SIZE); @@ -165,18 +211,29 @@ int ff_ssl_read_key_cert(char *key_url, char *cert_url, char *key_buf, size_t ke goto end; } - pkey_to_pem_string(pkey, key_buf, key_sz); - cert_to_pem_string(cert, cert_buf, cert_sz); + key_tem = pkey_to_pem_string(pkey); + cert_tem = cert_to_pem_string(cert); - ret = x509_fingerprint(cert, fingerprint); - if (ret < 0) - av_log(NULL, AV_LOG_ERROR, "TLS: Failed to generate fingerprint from %s\n", cert_url); + snprintf(key_buf, key_sz, "%s", key_tem); + snprintf(cert_buf, cert_sz, "%s", cert_tem); + + /* Generate fingerprint. */ + if (fingerprint) { + *fingerprint = generate_fingerprint(cert); + if (!*fingerprint) { + av_log(NULL, AV_LOG_ERROR, "TLS: Failed to generate fingerprint from %s\n", cert_url); + ret = AVERROR(EIO); + goto end; + } + } end: BIO_free(key_b); av_bprint_finalize(&key_bp, NULL); BIO_free(cert_b); av_bprint_finalize(&cert_bp, NULL); + av_free(key_tem); + av_free(cert_tem); EVP_PKEY_free(pkey); X509_free(cert); return ret; @@ -319,9 +376,12 @@ static int openssl_gen_certificate(EVP_PKEY *pkey, X509 **cert, char **fingerpri goto einval_end; } - ret = x509_fingerprint(*cert, fingerprint); - if (ret < 0) - goto end; + if (fingerprint) { + *fingerprint = generate_fingerprint(*cert); + if (!*fingerprint) { + goto enomem_end; + } + } goto end; enomem_end: @@ -343,6 +403,7 @@ int ff_ssl_gen_key_cert(char *key_buf, size_t key_sz, char *cert_buf, size_t cer int ret = 0; EVP_PKEY *pkey = NULL; X509 *cert = NULL; + char *key_tem = NULL, *cert_tem = NULL; ret = openssl_gen_private_key(&pkey); if (ret < 0) goto error; @@ -350,9 +411,14 @@ int ff_ssl_gen_key_cert(char *key_buf, size_t key_sz, char *cert_buf, size_t cer ret = openssl_gen_certificate(pkey, &cert, fingerprint); if (ret < 0) goto error; - pkey_to_pem_string(pkey, key_buf, key_sz); - cert_to_pem_string(cert, cert_buf, cert_sz); + key_tem = pkey_to_pem_string(pkey); + cert_tem = cert_to_pem_string(cert); + snprintf(key_buf, key_sz, "%s", key_tem); + snprintf(cert_buf, cert_sz, "%s", cert_tem); + + av_free(key_tem); + av_free(cert_tem); error: X509_free(cert); EVP_PKEY_free(pkey); @@ -361,7 +427,7 @@ error: /** - * Deserialize a PEM-encoded private or public key from a NUL-terminated C string. + * Deserialize a PEM‐encoded private or public key from a NUL-terminated C string. * * @param pem_str The PEM text, e.g. * "-----BEGIN PRIVATE KEY-----\n…\n-----END PRIVATE KEY-----\n" @@ -392,7 +458,7 @@ static EVP_PKEY *pkey_from_pem_string(const char *pem_str, int is_priv) } /** - * Deserialize a PEM-encoded certificate from a NUL-terminated C string. + * Deserialize a PEM‐encoded certificate from a NUL-terminated C string. * * @param pem_str The PEM text, e.g. * "-----BEGIN CERTIFICATE-----\n…\n-----END CERTIFICATE-----\n" @@ -476,6 +542,12 @@ int ff_dtls_export_materials(URLContext *h, char *dtls_srtp_materials, size_t ma return 0; } +int ff_dtls_state(URLContext *h) +{ + TLSContext *c = h->priv_data; + return c->tls_shared.state; +} + static int print_ssl_error(URLContext *h, int ret) { TLSContext *c = h->priv_data; @@ -650,6 +722,7 @@ static int dtls_handshake(URLContext *h) goto end; ret = 0; + c->tls_shared.state = DTLS_STATE_FINISHED; end: return ret; } diff --git a/libavformat/tls_schannel.c b/libavformat/tls_schannel.c index b854f484fa..b60e3100be 100644 --- a/libavformat/tls_schannel.c +++ b/libavformat/tls_schannel.c @@ -681,6 +681,12 @@ int ff_dtls_export_materials(URLContext *h, char *dtls_srtp_materials, size_t ma #endif } +int ff_dtls_state(URLContext *h) +{ + TLSContext *c = h->priv_data; + return c->tls_shared.state; +} + static void init_sec_buffer(SecBuffer *buffer, unsigned long type, void *data, unsigned long size) { @@ -1105,6 +1111,7 @@ static int tls_handshake(URLContext *h) #endif c->connected = 1; + s->state = DTLS_STATE_FINISHED; fail: return ret; diff --git a/libavformat/whip.c b/libavformat/whip.c index e2fa774c6f..65fd3b39b2 100644 --- a/libavformat/whip.c +++ b/libavformat/whip.c @@ -198,6 +198,9 @@ typedef struct WHIPContext { /* The state of the RTC connection. */ enum WHIPState state; + /* The callback return value for DTLS. */ + int dtls_ret; + int dtls_closed; /* Parameters for the input audio and video codecs. */ AVCodecParameters *audio_par; @@ -345,6 +348,41 @@ static av_cold int certificate_key_init(AVFormatContext *s) return ret; } +/** + * When DTLS state change. + */ +static int dtls_context_on_state(AVFormatContext *s, const char* type, const char* desc) +{ + int ret = 0; + WHIPContext *whip = s->priv_data; + int state = ff_dtls_state(whip->dtls_uc); + + if (state == DTLS_STATE_CLOSED) { + whip->dtls_closed = 1; + av_log(whip, AV_LOG_VERBOSE, "DTLS session closed, type=%s, desc=%s, elapsed=%dms\n", + type ? type : "", desc ? desc : "", ELAPSED(whip->whip_starttime, av_gettime())); + goto error; + } + + if (state == DTLS_STATE_FAILED) { + whip->state = WHIP_STATE_FAILED; + av_log(whip, AV_LOG_ERROR, "DTLS session failed, type=%s, desc=%s\n", + type ? type : "", desc ? desc : ""); + whip->dtls_ret = AVERROR(EIO); + goto error; + } + + if (state == DTLS_STATE_FINISHED && whip->state < WHIP_STATE_DTLS_FINISHED) { + whip->state = WHIP_STATE_DTLS_FINISHED; + whip->whip_dtls_time = av_gettime(); + av_log(whip, AV_LOG_VERBOSE, "DTLS handshake is done, elapsed=%dms\n", + ELAPSED(whip->whip_starttime, av_gettime())); + return ret; + } +error: + return -1; +} + static av_cold int dtls_initialize(AVFormatContext *s) { WHIPContext *whip = s->priv_data; @@ -1288,18 +1326,9 @@ next_packet: /* If got any DTLS messages, handle it. */ if (is_dtls_packet(whip->buf, ret) && whip->state >= WHIP_STATE_ICE_CONNECTED || whip->state == WHIP_STATE_DTLS_CONNECTING) { whip->state = WHIP_STATE_DTLS_CONNECTING; - ret = ffurl_handshake(whip->dtls_uc); - if (ret < 0) { - whip->state = WHIP_STATE_FAILED; - av_log(whip, AV_LOG_VERBOSE, "DTLS session failed\n"); + if ((ret = ffurl_handshake(whip->dtls_uc)) < 0) goto end; - } - if (!ret) { - whip->state = WHIP_STATE_DTLS_FINISHED; - whip->whip_dtls_time = av_gettime(); - av_log(whip, AV_LOG_VERBOSE, "DTLS handshake is done, elapsed=%dms\n", - ELAPSED(whip->whip_starttime, whip->whip_dtls_time)); - } + dtls_context_on_state(s, NULL, NULL); goto next_packet; } } @@ -1742,8 +1771,10 @@ static av_cold int whip_init(AVFormatContext *s) goto end; end: - if (ret < 0) + if (ret < 0 && whip->state < WHIP_STATE_FAILED) whip->state = WHIP_STATE_FAILED; + if (ret >= 0 && whip->state >= WHIP_STATE_FAILED && whip->dtls_ret < 0) + ret = whip->dtls_ret; return ret; } @@ -1791,8 +1822,12 @@ static int whip_write_packet(AVFormatContext *s, AVPacket *pkt) } end: - if (ret < 0) + if (ret < 0 && whip->state < WHIP_STATE_FAILED) whip->state = WHIP_STATE_FAILED; + if (ret >= 0 && whip->state >= WHIP_STATE_FAILED && whip->dtls_ret < 0) + ret = whip->dtls_ret; + if (ret >= 0 && whip->dtls_closed) + ret = AVERROR(EIO); return ret; } diff --git a/libavutil/thread.h b/libavutil/thread.h index 184e2d8c5f..2c00c7cc35 100644 --- a/libavutil/thread.h +++ b/libavutil/thread.h @@ -229,8 +229,6 @@ static inline int ff_thread_setname(const char *name) #endif #elif HAVE_PTHREAD_SET_NAME_NP pthread_set_name_np(pthread_self(), name); -#elif HAVE_W32THREADS - ret = win32_thread_setname(name); #else ret = AVERROR(ENOSYS); #endif