Compare commits

..

12 commits

Author SHA1 Message Date
wrapper
7ab9e56401 fix bitrate issues 2025-08-17 23:17:41 +07:00
wrapper
eb6bb062f7 bitrate again 2025-08-17 22:43:01 +07:00
wrapper
ede3c5b102 log crash 2025-08-17 22:32:39 +07:00
wrapper
3b2fe4b52c info bitrate 2025-08-17 22:23:59 +07:00
wrapper
28c8e9b7aa 24h2 he-aac bitrate bug 2025-08-17 21:12:20 +07:00
wrapper
60223b3a0b simple 2025-08-17 15:39:40 +07:00
wrapper
ec65dd4d79 again 2025-08-17 15:31:14 +07:00
wrapper
af417a0790 2 2025-08-17 15:16:58 +07:00
wrapper
8180ebe904 fail if not 24h2 2025-08-17 15:15:46 +07:00
wrapper
90b835fb6c level 1 aac 2025-08-16 15:09:33 +07:00
wrapper
b547df22a2 output score profile 2025-08-16 14:53:56 +07:00
wrapper
04fadf51e1 aac profiles test 2025-08-16 12:52:06 +07:00
29 changed files with 272 additions and 171 deletions

View file

@ -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
# ======

View file

@ -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

View file

@ -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 */

6
configure vendored
View file

@ -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 &&

View file

@ -762,7 +762,6 @@ static const FFCodecDefault d3d12va_encode_hevc_defaults[] = {
{ "b_qoffset", "0" },
{ "qmin", "-1" },
{ "qmax", "-1" },
{ "refs", "0" },
{ NULL },
};

View file

@ -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;

View file

@ -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);

View file

@ -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;

View file

@ -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;

View file

@ -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),

View file

@ -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;

View file

@ -1027,7 +1027,6 @@ static const FFCodecDefault vaapi_encode_av1_defaults[] = {
{ "g", "120" },
{ "qmin", "1" },
{ "qmax", "255" },
{ "refs", "0" },
{ NULL },
};

View file

@ -1153,7 +1153,6 @@ static const FFCodecDefault vaapi_encode_h264_defaults[] = {
{ "b_qoffset", "0" },
{ "qmin", "-1" },
{ "qmax", "-1" },
{ "refs", "0" },
{ NULL },
};

View file

@ -1184,7 +1184,6 @@ static const FFCodecDefault vaapi_encode_h265_defaults[] = {
{ "b_qoffset", "0" },
{ "qmin", "-1" },
{ "qmax", "-1" },
{ "refs", "0" },
{ NULL },
};

View file

@ -235,7 +235,6 @@ static const FFCodecDefault vaapi_encode_vp8_defaults[] = {
{ "g", "120" },
{ "qmin", "-1" },
{ "qmax", "-1" },
{ "refs", "0" },
{ NULL },
};

View file

@ -292,7 +292,6 @@ static const FFCodecDefault vaapi_encode_vp9_defaults[] = {
{ "g", "250" },
{ "qmin", "-1" },
{ "qmax", "-1" },
{ "refs", "0" },
{ NULL },
};

View file

@ -1367,7 +1367,6 @@ static const FFCodecDefault vulkan_encode_av1_defaults[] = {
{ "g", "300" },
{ "qmin", "1" },
{ "qmax", "255" },
{ "refs", "0" },
{ NULL },
};

View file

@ -1634,7 +1634,6 @@ static const FFCodecDefault vulkan_encode_h264_defaults[] = {
{ "b_qoffset", "0" },
{ "qmin", "-1" },
{ "qmax", "-1" },
{ "refs", "0" },
{ NULL },
};

View file

@ -1761,7 +1761,6 @@ static const FFCodecDefault vulkan_encode_h265_defaults[] = {
{ "b_qoffset", "0" },
{ "qmin", "-1" },
{ "qmax", "-1" },
{ "refs", "0" },
{ NULL },
};

View file

@ -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

View file

@ -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 },

View file

@ -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

View file

@ -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) {

View file

@ -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)

View file

@ -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);

View file

@ -35,82 +35,127 @@
#include <openssl/x509v3.h>
/**
* Convert an EVP_PKEY to a PEM string.
* Returns a heapallocated nullterminated string containing
* the PEMencoded 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 & NULterminate
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_mallocd 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 PEMencoded 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 PEMencoded 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;
}

View file

@ -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;

View file

@ -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;
}

View file

@ -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