From: Marcin Serwin via ffmpeg-devel <ffmpeg-devel@ffmpeg.org>
To: ffmpeg-devel@ffmpeg.org
Cc: Marcin Serwin <marcin@serwin.dev>
Subject: [FFmpeg-devel] [PATCH] fftools/ffplay: migrate to SDL3
Date: Fri, 2 Jan 2026 23:41:01 +0100
Message-ID: <20260102224334.734271-1-marcin@serwin.dev> (raw)
Migrates the FFplay media player to the new SDL version using the
official tutorial at https://wiki.libsdl.org/SDL3/README-migration.
Signed-off-by: Marcin Serwin <marcin@serwin.dev>
---
This is just first iteration but I wanted to make sure if you're interested in
doing the migration and gather some early feedback. With this path I am able to
play some basic video and audio files on my Linux system but I haven't tested it
with other platforms.
configure | 26 +---
fftools/ffplay.c | 283 ++++++++++++++++++--------------------
fftools/ffplay_renderer.c | 12 +-
fftools/ffplay_renderer.h | 2 +-
4 files changed, 150 insertions(+), 173 deletions(-)
diff --git a/configure b/configure
index 301a3e5e3e..e3927fe4ab 100755
--- a/configure
+++ b/configure
@@ -338,7 +338,7 @@ External library support:
--disable-sndio disable sndio support [autodetect]
--disable-schannel disable SChannel SSP, needed for TLS support on
Windows if openssl and gnutls are not used [autodetect]
- --disable-sdl2 disable sdl2 [autodetect]
+ --disable-sdl3 disable sdl3 [autodetect]
--disable-securetransport disable Secure Transport, needed for TLS support
on OSX if openssl and gnutls are not used [autodetect]
--enable-vapoursynth enable VapourSynth demuxer [no]
@@ -1904,7 +1904,7 @@ EXTERNAL_AUTODETECT_LIBRARY_LIST="
mediafoundation
metal
schannel
- sdl2
+ sdl3
securetransport
sndio
xlib
@@ -4217,7 +4217,7 @@ ffmpeg_select="aformat_filter anull_filter atrim_filter crop_filter
format_filter hflip_filter null_filter rotate_filter
transpose_filter trim_filter vflip_filter"
ffmpeg_suggest="ole32 psapi shell32"
-ffplay_deps="avcodec avformat avfilter swscale swresample sdl2"
+ffplay_deps="avcodec avformat avfilter swscale swresample sdl3"
ffplay_select="crop_filter transpose_filter hflip_filter vflip_filter rotate_filter"
ffplay_suggest="shell32 libplacebo vulkan"
ffprobe_deps="avcodec avformat"
@@ -4562,7 +4562,7 @@ for opt do
do_random ${action#--} $optval
;;
--enable-sdl)
- enable sdl2
+ enable sdl3
;;
--enable-lto*)
lto=-f${opt#--enable-}
@@ -7404,20 +7404,8 @@ if enabled gcrypt; then
fi
fi
-if enabled sdl2; then
- SDL2_CONFIG="${cross_prefix}sdl2-config"
- test_pkg_config sdl2 "sdl2 >= 2.0.1 sdl2 < 3.0.0" SDL_events.h SDL_PollEvent
- if disabled sdl2 && "${SDL2_CONFIG}" --version > /dev/null 2>&1; then
- sdl2_cflags=$("${SDL2_CONFIG}" --cflags)
- sdl2_extralibs=$("${SDL2_CONFIG}" --libs)
- test_cpp_condition SDL.h "(SDL_MAJOR_VERSION<<16 | SDL_MINOR_VERSION<<8 | SDL_PATCHLEVEL) >= 0x020001" $sdl2_cflags &&
- test_cpp_condition SDL.h "(SDL_MAJOR_VERSION<<16 | SDL_MINOR_VERSION<<8 | SDL_PATCHLEVEL) < 0x030000" $sdl2_cflags &&
- check_func_headers SDL_events.h SDL_PollEvent $sdl2_extralibs $sdl2_cflags &&
- enable sdl2
- fi
- if test $target_os = "mingw32"; then
- sdl2_extralibs=$(filter_out '-mwindows' $sdl2_extralibs)
- fi
+if enabled sdl3; then
+ test_pkg_config sdl3 "sdl3 >= 3.2.0 sdl3 < 4.0.0" SDL3/SDL_events.h SDL_PollEvent
fi
if enabled decklink; then
@@ -8467,7 +8455,7 @@ HOSTLD_O=$HOSTLD_O
TARGET_EXEC=$target_exec $target_exec_args
TARGET_PATH=$target_path
TARGET_SAMPLES=${target_samples:-\$(SAMPLES)}
-CFLAGS-ffplay=${sdl2_cflags}
+CFLAGS-ffplay=${sdl3_cflags}
CFLAGS_HEADERS=$CFLAGS_HEADERS
LIB_INSTALL_EXTRA_CMD=$LIB_INSTALL_EXTRA_CMD
EXTRALIBS=$extralibs
diff --git a/fftools/ffplay.c b/fftools/ffplay.c
index dcd20e70bc..52d842083c 100644
--- a/fftools/ffplay.c
+++ b/fftools/ffplay.c
@@ -51,8 +51,9 @@
#include "libavfilter/buffersink.h"
#include "libavfilter/buffersrc.h"
-#include <SDL.h>
-#include <SDL_thread.h>
+#include <SDL3/SDL.h>
+#include <SDL3/SDL_main.h>
+#include <SDL3/SDL_thread.h>
#include "cmdutils.h"
#include "ffplay_renderer.h"
@@ -117,8 +118,8 @@ typedef struct PacketQueue {
int64_t duration;
int abort_request;
int serial;
- SDL_mutex *mutex;
- SDL_cond *cond;
+ SDL_Mutex *mutex;
+ SDL_Condition *cond;
} PacketQueue;
#define VIDEO_PICTURE_QUEUE_SIZE 3
@@ -172,8 +173,8 @@ typedef struct FrameQueue {
int max_size;
int keep_last;
int rindex_shown;
- SDL_mutex *mutex;
- SDL_cond *cond;
+ SDL_Mutex *mutex;
+ SDL_Condition *cond;
PacketQueue *pktq;
} FrameQueue;
@@ -190,7 +191,7 @@ typedef struct Decoder {
int pkt_serial;
int finished;
int packet_pending;
- SDL_cond *empty_queue_cond;
+ SDL_Condition *empty_queue_cond;
int64_t start_pts;
AVRational start_pts_tb;
int64_t next_pts;
@@ -245,7 +246,7 @@ typedef struct VideoState {
unsigned int audio_buf1_size;
int audio_buf_index; /* in bytes */
int audio_write_buf_size;
- int audio_volume;
+ float audio_volume;
int muted;
struct AudioParams audio_src;
struct AudioParams audio_filter_src;
@@ -283,7 +284,8 @@ typedef struct VideoState {
PacketQueue videoq;
double max_frame_duration; // maximum duration of a frame - above this, we consider the jump a timestamp discontinuity
struct SwsContext *sub_convert_ctx;
- int eof;
+ bool shown;
+ bool eof;
char *filename;
int width, height, xleft, ytop;
@@ -298,7 +300,7 @@ typedef struct VideoState {
int last_video_stream, last_audio_stream, last_subtitle_stream;
- SDL_cond *continue_read_thread;
+ SDL_Condition *continue_read_thread;
} VideoState;
/* options specified by the user */
@@ -356,12 +358,12 @@ static const char *hwaccel = NULL;
static int is_full_screen;
static int64_t audio_callback_time;
-#define FF_QUIT_EVENT (SDL_USEREVENT + 2)
+#define FF_QUIT_EVENT (SDL_EVENT_USER + 2)
static SDL_Window *window;
static SDL_Renderer *renderer;
-static SDL_RendererInfo renderer_info = {0};
-static SDL_AudioDeviceID audio_dev;
+static const SDL_PixelFormat *renderer_texture_formats;
+static SDL_AudioStream *audio_stream;
static VkRenderer *vk_renderer;
@@ -370,15 +372,15 @@ static const struct TextureFormatEntry {
int texture_fmt;
} sdl_texture_format_map[] = {
{ AV_PIX_FMT_RGB8, SDL_PIXELFORMAT_RGB332 },
- { AV_PIX_FMT_RGB444, SDL_PIXELFORMAT_RGB444 },
- { AV_PIX_FMT_RGB555, SDL_PIXELFORMAT_RGB555 },
- { AV_PIX_FMT_BGR555, SDL_PIXELFORMAT_BGR555 },
+ { AV_PIX_FMT_RGB444, SDL_PIXELFORMAT_XRGB4444 },
+ { AV_PIX_FMT_RGB555, SDL_PIXELFORMAT_XRGB1555 },
+ { AV_PIX_FMT_BGR555, SDL_PIXELFORMAT_XBGR1555 },
{ AV_PIX_FMT_RGB565, SDL_PIXELFORMAT_RGB565 },
{ AV_PIX_FMT_BGR565, SDL_PIXELFORMAT_BGR565 },
{ AV_PIX_FMT_RGB24, SDL_PIXELFORMAT_RGB24 },
{ AV_PIX_FMT_BGR24, SDL_PIXELFORMAT_BGR24 },
- { AV_PIX_FMT_0RGB32, SDL_PIXELFORMAT_RGB888 },
- { AV_PIX_FMT_0BGR32, SDL_PIXELFORMAT_BGR888 },
+ { AV_PIX_FMT_0RGB32, SDL_PIXELFORMAT_XRGB8888 },
+ { AV_PIX_FMT_0BGR32, SDL_PIXELFORMAT_XBGR8888 },
{ AV_PIX_FMT_NE(RGB0, 0BGR), SDL_PIXELFORMAT_RGBX8888 },
{ AV_PIX_FMT_NE(BGR0, 0RGB), SDL_PIXELFORMAT_BGRX8888 },
{ AV_PIX_FMT_RGB32, SDL_PIXELFORMAT_ARGB8888 },
@@ -433,7 +435,7 @@ static int packet_queue_put_private(PacketQueue *q, AVPacket *pkt)
q->size += pkt1.pkt->size + sizeof(pkt1);
q->duration += pkt1.pkt->duration;
/* XXX: should duplicate packet data in DV case */
- SDL_CondSignal(q->cond);
+ SDL_SignalCondition(q->cond);
return 0;
}
@@ -477,7 +479,7 @@ static int packet_queue_init(PacketQueue *q)
av_log(NULL, AV_LOG_FATAL, "SDL_CreateMutex(): %s\n", SDL_GetError());
return AVERROR(ENOMEM);
}
- q->cond = SDL_CreateCond();
+ q->cond = SDL_CreateCondition();
if (!q->cond) {
av_log(NULL, AV_LOG_FATAL, "SDL_CreateCond(): %s\n", SDL_GetError());
return AVERROR(ENOMEM);
@@ -505,7 +507,7 @@ static void packet_queue_destroy(PacketQueue *q)
packet_queue_flush(q);
av_fifo_freep2(&q->pkt_list);
SDL_DestroyMutex(q->mutex);
- SDL_DestroyCond(q->cond);
+ SDL_DestroyCondition(q->cond);
}
static void packet_queue_abort(PacketQueue *q)
@@ -514,7 +516,7 @@ static void packet_queue_abort(PacketQueue *q)
q->abort_request = 1;
- SDL_CondSignal(q->cond);
+ SDL_SignalCondition(q->cond);
SDL_UnlockMutex(q->mutex);
}
@@ -555,14 +557,14 @@ static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block, int *seria
ret = 0;
break;
} else {
- SDL_CondWait(q->cond, q->mutex);
+ SDL_WaitCondition(q->cond, q->mutex);
}
}
SDL_UnlockMutex(q->mutex);
return ret;
}
-static int decoder_init(Decoder *d, AVCodecContext *avctx, PacketQueue *queue, SDL_cond *empty_queue_cond) {
+static int decoder_init(Decoder *d, AVCodecContext *avctx, PacketQueue *queue, SDL_Condition *empty_queue_cond) {
memset(d, 0, sizeof(Decoder));
d->pkt = av_packet_alloc();
if (!d->pkt)
@@ -622,7 +624,7 @@ static int decoder_decode_frame(Decoder *d, AVFrame *frame, AVSubtitle *sub) {
do {
if (d->queue->nb_packets == 0)
- SDL_CondSignal(d->empty_queue_cond);
+ SDL_SignalCondition(d->empty_queue_cond);
if (d->packet_pending) {
d->packet_pending = 0;
} else {
@@ -693,7 +695,7 @@ static int frame_queue_init(FrameQueue *f, PacketQueue *pktq, int max_size, int
av_log(NULL, AV_LOG_FATAL, "SDL_CreateMutex(): %s\n", SDL_GetError());
return AVERROR(ENOMEM);
}
- if (!(f->cond = SDL_CreateCond())) {
+ if (!(f->cond = SDL_CreateCondition())) {
av_log(NULL, AV_LOG_FATAL, "SDL_CreateCond(): %s\n", SDL_GetError());
return AVERROR(ENOMEM);
}
@@ -715,13 +717,13 @@ static void frame_queue_destroy(FrameQueue *f)
av_frame_free(&vp->frame);
}
SDL_DestroyMutex(f->mutex);
- SDL_DestroyCond(f->cond);
+ SDL_DestroyCondition(f->cond);
}
static void frame_queue_signal(FrameQueue *f)
{
SDL_LockMutex(f->mutex);
- SDL_CondSignal(f->cond);
+ SDL_SignalCondition(f->cond);
SDL_UnlockMutex(f->mutex);
}
@@ -746,7 +748,7 @@ static Frame *frame_queue_peek_writable(FrameQueue *f)
SDL_LockMutex(f->mutex);
while (f->size >= f->max_size &&
!f->pktq->abort_request) {
- SDL_CondWait(f->cond, f->mutex);
+ SDL_WaitCondition(f->cond, f->mutex);
}
SDL_UnlockMutex(f->mutex);
@@ -762,7 +764,7 @@ static Frame *frame_queue_peek_readable(FrameQueue *f)
SDL_LockMutex(f->mutex);
while (f->size - f->rindex_shown <= 0 &&
!f->pktq->abort_request) {
- SDL_CondWait(f->cond, f->mutex);
+ SDL_WaitCondition(f->cond, f->mutex);
}
SDL_UnlockMutex(f->mutex);
@@ -778,7 +780,7 @@ static void frame_queue_push(FrameQueue *f)
f->windex = 0;
SDL_LockMutex(f->mutex);
f->size++;
- SDL_CondSignal(f->cond);
+ SDL_SignalCondition(f->cond);
SDL_UnlockMutex(f->mutex);
}
@@ -793,7 +795,7 @@ static void frame_queue_next(FrameQueue *f)
f->rindex = 0;
SDL_LockMutex(f->mutex);
f->size--;
- SDL_CondSignal(f->cond);
+ SDL_SignalCondition(f->cond);
SDL_UnlockMutex(f->mutex);
}
@@ -824,7 +826,7 @@ static void decoder_abort(Decoder *d, FrameQueue *fq)
static inline void fill_rectangle(int x, int y, int w, int h)
{
- SDL_Rect rect;
+ SDL_FRect rect;
rect.x = x;
rect.y = y;
rect.w = w;
@@ -835,19 +837,21 @@ static inline void fill_rectangle(int x, int y, int w, int h)
static int realloc_texture(SDL_Texture **texture, Uint32 new_format, int new_width, int new_height, SDL_BlendMode blendmode, int init_texture)
{
- Uint32 format;
- int access, w, h;
- if (!*texture || SDL_QueryTexture(*texture, &format, &access, &w, &h) < 0 || new_width != w || new_height != h || new_format != format) {
+ SDL_PropertiesID props = SDL_GetTextureProperties(*texture);
+ if (!*texture || !props ||
+ SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_WIDTH_NUMBER, 0) != new_width ||
+ SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_HEIGHT_NUMBER, 0) != new_height ||
+ SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_FORMAT_NUMBER, 0) != new_format) {
void *pixels;
int pitch;
if (*texture)
SDL_DestroyTexture(*texture);
if (!(*texture = SDL_CreateTexture(renderer, new_format, SDL_TEXTUREACCESS_STREAMING, new_width, new_height)))
return -1;
- if (SDL_SetTextureBlendMode(*texture, blendmode) < 0)
+ if (!SDL_SetTextureBlendMode(*texture, blendmode))
return -1;
if (init_texture) {
- if (SDL_LockTexture(*texture, NULL, &pixels, &pitch) < 0)
+ if (!SDL_LockTexture(*texture, NULL, &pixels, &pitch))
return -1;
memset(pixels, 0, pitch * new_height);
SDL_UnlockTexture(*texture);
@@ -857,7 +861,7 @@ static int realloc_texture(SDL_Texture **texture, Uint32 new_format, int new_wid
return 0;
}
-static void calculate_display_rect(SDL_Rect *rect,
+static void calculate_display_rect(SDL_FRect *rect,
int scr_xleft, int scr_ytop, int scr_width, int scr_height,
int pic_width, int pic_height, AVRational pic_sar)
{
@@ -949,25 +953,14 @@ static enum AVAlphaMode sdl_supported_alpha_modes[] = {
static void set_sdl_yuv_conversion_mode(AVFrame *frame)
{
-#if SDL_VERSION_ATLEAST(2,0,8)
- SDL_YUV_CONVERSION_MODE mode = SDL_YUV_CONVERSION_AUTOMATIC;
- if (frame && (frame->format == AV_PIX_FMT_YUV420P || frame->format == AV_PIX_FMT_YUYV422 || frame->format == AV_PIX_FMT_UYVY422)) {
- if (frame->color_range == AVCOL_RANGE_JPEG)
- mode = SDL_YUV_CONVERSION_JPEG;
- else if (frame->colorspace == AVCOL_SPC_BT709)
- mode = SDL_YUV_CONVERSION_BT709;
- else if (frame->colorspace == AVCOL_SPC_BT470BG || frame->colorspace == AVCOL_SPC_SMPTE170M)
- mode = SDL_YUV_CONVERSION_BT601;
- }
- SDL_SetYUVConversionMode(mode); /* FIXME: no support for linear transfer */
-#endif
+ /* TODO */
}
static void video_image_display(VideoState *is)
{
Frame *vp;
Frame *sp = NULL;
- SDL_Rect rect;
+ SDL_FRect rect;
vp = frame_queue_peek_last(&is->pictq);
if (vk_renderer) {
@@ -1032,11 +1025,11 @@ static void video_image_display(VideoState *is)
vp->flip_v = vp->frame->linesize[0] < 0;
}
- SDL_RenderCopyEx(renderer, is->vid_texture, NULL, &rect, 0, NULL, vp->flip_v ? SDL_FLIP_VERTICAL : 0);
+ SDL_RenderTextureRotated(renderer, is->vid_texture, NULL, &rect, 0, NULL, vp->flip_v ? SDL_FLIP_VERTICAL : 0);
set_sdl_yuv_conversion_mode(NULL);
if (sp) {
#if USE_ONEPASS_SUBTITLE_RENDER
- SDL_RenderCopy(renderer, is->sub_texture, NULL, &rect);
+ SDL_RenderTexture(renderer, is->sub_texture, NULL, &rect);
#else
int i;
double xratio = (double)rect.w / (double)sp->width;
@@ -1047,7 +1040,7 @@ static void video_image_display(VideoState *is)
.y = rect.y + sub_rect->y * yratio,
.w = sub_rect->w * xratio,
.h = sub_rect->h * yratio};
- SDL_RenderCopy(renderer, is->sub_texture, sub_rect, &target);
+ SDL_RenderTexture(renderer, is->sub_texture, sub_rect, &target);
}
#endif
}
@@ -1202,7 +1195,7 @@ static void video_audio_display(VideoState *s)
}
SDL_UnlockTexture(s->vis_texture);
}
- SDL_RenderCopy(renderer, s->vis_texture, NULL, NULL);
+ SDL_RenderTexture(renderer, s->vis_texture, NULL, NULL);
}
if (!s->paused)
s->xpos++;
@@ -1221,7 +1214,7 @@ static void stream_component_close(VideoState *is, int stream_index)
switch (codecpar->codec_type) {
case AVMEDIA_TYPE_AUDIO:
decoder_abort(&is->auddec, &is->sampq);
- SDL_CloseAudioDevice(audio_dev);
+ SDL_DestroyAudioStream(audio_stream);
decoder_destroy(&is->auddec);
swr_free(&is->swr_ctx);
av_freep(&is->audio_buf1);
@@ -1291,7 +1284,7 @@ static void stream_close(VideoState *is)
frame_queue_destroy(&is->pictq);
frame_queue_destroy(&is->sampq);
frame_queue_destroy(&is->subpq);
- SDL_DestroyCond(is->continue_read_thread);
+ SDL_DestroyCondition(is->continue_read_thread);
sws_freeContext(is->sub_convert_ctx);
av_free(is->filename);
if (is->vis_texture)
@@ -1337,7 +1330,7 @@ static void sigterm_handler(int sig)
static void set_default_window_size(int width, int height, AVRational sar)
{
- SDL_Rect rect;
+ SDL_FRect rect;
int max_width = screen_width ? screen_width : INT_MAX;
int max_height = screen_height ? screen_height : INT_MAX;
if (max_width == INT_MAX && max_height == INT_MAX)
@@ -1361,11 +1354,12 @@ static int video_open(VideoState *is)
SDL_SetWindowSize(window, w, h);
SDL_SetWindowPosition(window, screen_left, screen_top);
if (is_full_screen)
- SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP);
+ SDL_SetWindowFullscreen(window, true);
SDL_ShowWindow(window);
is->width = w;
is->height = h;
+ is->shown = true;
return 0;
}
@@ -1373,7 +1367,7 @@ static int video_open(VideoState *is)
/* display the current picture, if any */
static void video_display(VideoState *is)
{
- if (!is->width)
+ if (!is->shown)
video_open(is);
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
@@ -1492,7 +1486,7 @@ static void stream_seek(VideoState *is, int64_t pos, int64_t rel, int by_bytes)
if (by_bytes)
is->seek_flags |= AVSEEK_FLAG_BYTE;
is->seek_req = 1;
- SDL_CondSignal(is->continue_read_thread);
+ SDL_SignalCondition(is->continue_read_thread);
}
}
@@ -1523,9 +1517,9 @@ static void toggle_mute(VideoState *is)
static void update_volume(VideoState *is, int sign, double step)
{
- double volume_level = is->audio_volume ? (20 * log(is->audio_volume / (double)SDL_MIX_MAXVOLUME) / log(10)) : -1000.0;
- int new_volume = lrint(SDL_MIX_MAXVOLUME * pow(10.0, (volume_level + sign * step) / 20.0));
- is->audio_volume = av_clip(is->audio_volume == new_volume ? (is->audio_volume + sign) : new_volume, 0, SDL_MIX_MAXVOLUME);
+ float volume_level = is->audio_volume ? (20 * log(is->audio_volume - 10)) : -1000.0;
+ float new_volume = pow(10.0, (volume_level + sign * step) / 20.0);
+ is->audio_volume = SDL_max(0.0, SDL_min(new_volume, 1.0));
}
static void step_to_next_frame(VideoState *is)
@@ -1876,9 +1870,9 @@ static int configure_video_filters(AVFilterGraph *graph, VideoState *is, const c
if (!par)
return AVERROR(ENOMEM);
- for (i = 0; i < renderer_info.num_texture_formats; i++) {
+ for (i = 0; renderer_texture_formats[i] != SDL_PIXELFORMAT_UNKNOWN; i++) {
for (j = 0; j < FF_ARRAY_ELEMS(sdl_texture_format_map); j++) {
- if (renderer_info.texture_formats[i] == sdl_texture_format_map[j].texture_fmt) {
+ if (renderer_texture_formats[i] == sdl_texture_format_map[j].texture_fmt) {
pix_fmts[nb_pix_fmts++] = sdl_texture_format_map[j].format;
break;
}
@@ -2513,12 +2507,13 @@ static void sdl_audio_callback(void *opaque, Uint8 *stream, int len)
len1 = is->audio_buf_size - is->audio_buf_index;
if (len1 > len)
len1 = len;
- if (!is->muted && is->audio_buf && is->audio_volume == SDL_MIX_MAXVOLUME)
+ if (!is->muted && is->audio_buf && is->audio_volume == 1.0)
memcpy(stream, (uint8_t *)is->audio_buf + is->audio_buf_index, len1);
else {
memset(stream, 0, len1);
if (!is->muted && is->audio_buf)
- SDL_MixAudioFormat(stream, (uint8_t *)is->audio_buf + is->audio_buf_index, AUDIO_S16SYS, len1, is->audio_volume);
+ SDL_MixAudio(stream, (uint8_t *)is->audio_buf + is->audio_buf_index,
+ SDL_AUDIO_S16, len1, is->audio_volume);
}
len -= len1;
stream += len1;
@@ -2532,9 +2527,22 @@ static void sdl_audio_callback(void *opaque, Uint8 *stream, int len)
}
}
+static void SDLCALL sdl3_audio_callback(void *userdata, SDL_AudioStream *stream, int additional_amount, int total_amount)
+{
+ if (additional_amount > 0) {
+ Uint8 *data = SDL_stack_alloc(Uint8, additional_amount);
+ if (data) {
+ /* TODO: inline and refactor */
+ sdl_audio_callback(userdata, data, additional_amount);
+ SDL_PutAudioStreamData(stream, data, additional_amount);
+ SDL_stack_free(data);
+ }
+ }
+}
+
static int audio_open(void *opaque, AVChannelLayout *wanted_channel_layout, int wanted_sample_rate, struct AudioParams *audio_hw_params)
{
- SDL_AudioSpec wanted_spec, spec;
+ SDL_AudioSpec wanted_spec;
const char *env;
static const int next_nb_channels[] = {0, 0, 1, 6, 2, 6, 4, 6};
static const int next_sample_rates[] = {0, 44100, 48000, 96000, 192000};
@@ -2560,12 +2568,8 @@ static int audio_open(void *opaque, AVChannelLayout *wanted_channel_layout, int
}
while (next_sample_rate_idx && next_sample_rates[next_sample_rate_idx] >= wanted_spec.freq)
next_sample_rate_idx--;
- wanted_spec.format = AUDIO_S16SYS;
- wanted_spec.silence = 0;
- wanted_spec.samples = FFMAX(SDL_AUDIO_MIN_BUFFER_SIZE, 2 << av_log2(wanted_spec.freq / SDL_AUDIO_MAX_CALLBACKS_PER_SEC));
- wanted_spec.callback = sdl_audio_callback;
- wanted_spec.userdata = opaque;
- while (!(audio_dev = SDL_OpenAudioDevice(NULL, 0, &wanted_spec, &spec, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE | SDL_AUDIO_ALLOW_CHANNELS_CHANGE))) {
+ wanted_spec.format = SDL_AUDIO_S16;
+ while (!(audio_stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &wanted_spec, sdl3_audio_callback, opaque))) {
av_log(NULL, AV_LOG_WARNING, "SDL_OpenAudio (%d channels, %d Hz): %s\n",
wanted_spec.channels, wanted_spec.freq, SDL_GetError());
wanted_spec.channels = next_nb_channels[FFMIN(7, wanted_spec.channels)];
@@ -2580,23 +2584,9 @@ static int audio_open(void *opaque, AVChannelLayout *wanted_channel_layout, int
}
av_channel_layout_default(wanted_channel_layout, wanted_spec.channels);
}
- if (spec.format != AUDIO_S16SYS) {
- av_log(NULL, AV_LOG_ERROR,
- "SDL advised audio format %d is not supported!\n", spec.format);
- return -1;
- }
- if (spec.channels != wanted_spec.channels) {
- av_channel_layout_uninit(wanted_channel_layout);
- av_channel_layout_default(wanted_channel_layout, spec.channels);
- if (wanted_channel_layout->order != AV_CHANNEL_ORDER_NATIVE) {
- av_log(NULL, AV_LOG_ERROR,
- "SDL advised channel count %d is not supported!\n", spec.channels);
- return -1;
- }
- }
audio_hw_params->fmt = AV_SAMPLE_FMT_S16;
- audio_hw_params->freq = spec.freq;
+ audio_hw_params->freq = wanted_spec.freq;
if (av_channel_layout_copy(&audio_hw_params->ch_layout, wanted_channel_layout) < 0)
return -1;
audio_hw_params->frame_size = av_samples_get_buffer_size(NULL, audio_hw_params->ch_layout.nb_channels, 1, audio_hw_params->fmt, 1);
@@ -2605,7 +2595,7 @@ static int audio_open(void *opaque, AVChannelLayout *wanted_channel_layout, int
av_log(NULL, AV_LOG_ERROR, "av_samples_get_buffer_size failed\n");
return -1;
}
- return spec.size;
+ return 0; /* TODO: is zero here ok? */
}
static int create_hwaccel(AVBufferRef **device_ctx)
@@ -2770,7 +2760,7 @@ static int stream_component_open(VideoState *is, int stream_index)
}
if ((ret = decoder_start(&is->auddec, audio_thread, "audio_decoder", is)) < 0)
goto out;
- SDL_PauseAudioDevice(audio_dev, 0);
+ SDL_ResumeAudioStreamDevice(audio_stream);
break;
case AVMEDIA_TYPE_VIDEO:
is->video_stream = stream_index;
@@ -2846,7 +2836,7 @@ static int read_thread(void *arg)
char metadata_description[96];
int pkt_in_play_range = 0;
const AVDictionaryEntry *t;
- SDL_mutex *wait_mutex = SDL_CreateMutex();
+ SDL_Mutex *wait_mutex = SDL_CreateMutex();
int scan_all_pmts_set = 0;
int64_t pkt_ts;
@@ -3096,7 +3086,7 @@ static int read_thread(void *arg)
stream_has_enough_packets(is->subtitle_st, is->subtitle_stream, &is->subtitleq)))) {
/* wait 10 ms */
SDL_LockMutex(wait_mutex);
- SDL_CondWaitTimeout(is->continue_read_thread, wait_mutex, 10);
+ SDL_WaitConditionTimeout(is->continue_read_thread, wait_mutex, 10);
SDL_UnlockMutex(wait_mutex);
continue;
}
@@ -3128,7 +3118,7 @@ static int read_thread(void *arg)
break;
}
SDL_LockMutex(wait_mutex);
- SDL_CondWaitTimeout(is->continue_read_thread, wait_mutex, 10);
+ SDL_WaitConditionTimeout(is->continue_read_thread, wait_mutex, 10);
SDL_UnlockMutex(wait_mutex);
continue;
} else {
@@ -3215,7 +3205,7 @@ static VideoState *stream_open(const char *filename,
packet_queue_init(&is->subtitleq) < 0)
goto fail;
- if (!(is->continue_read_thread = SDL_CreateCond())) {
+ if (!(is->continue_read_thread = SDL_CreateCondition())) {
av_log(NULL, AV_LOG_FATAL, "SDL_CreateCond(): %s\n", SDL_GetError());
goto fail;
}
@@ -3229,8 +3219,7 @@ static VideoState *stream_open(const char *filename,
if (startup_volume > 100)
av_log(NULL, AV_LOG_WARNING, "-volume=%d > 100, setting to 100\n", startup_volume);
startup_volume = av_clip(startup_volume, 0, 100);
- startup_volume = av_clip(SDL_MIX_MAXVOLUME * startup_volume / 100, 0, SDL_MIX_MAXVOLUME);
- is->audio_volume = startup_volume;
+ is->audio_volume = startup_volume / 100.0;
is->muted = 0;
is->av_sync_type = av_sync_type;
is->read_tid = SDL_CreateThread(read_thread, "read_thread", is);
@@ -3325,7 +3314,7 @@ static void stream_cycle_channel(VideoState *is, int codec_type)
static void toggle_full_screen(VideoState *is)
{
is_full_screen = !is_full_screen;
- SDL_SetWindowFullscreen(window, is_full_screen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
+ SDL_SetWindowFullscreen(window, is_full_screen);
}
static void toggle_audio_display(VideoState *is)
@@ -3343,9 +3332,9 @@ static void toggle_audio_display(VideoState *is)
static void refresh_loop_wait_event(VideoState *is, SDL_Event *event) {
double remaining_time = 0.0;
SDL_PumpEvents();
- while (!SDL_PeepEvents(event, 1, SDL_GETEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT)) {
+ while (!SDL_PeepEvents(event, 1, SDL_GETEVENT, SDL_EVENT_FIRST, SDL_EVENT_LAST)) {
if (!cursor_hidden && av_gettime_relative() - cursor_last_shown > CURSOR_HIDE_DELAY) {
- SDL_ShowCursor(0);
+ SDL_HideCursor();
cursor_hidden = 1;
}
if (remaining_time > 0.0)
@@ -3394,24 +3383,24 @@ static void event_loop(VideoState *cur_stream)
double x;
refresh_loop_wait_event(cur_stream, &event);
switch (event.type) {
- case SDL_KEYDOWN:
- if (exit_on_keydown || event.key.keysym.sym == SDLK_ESCAPE || event.key.keysym.sym == SDLK_q) {
+ case SDL_EVENT_KEY_DOWN:
+ if (exit_on_keydown || event.key.key == SDLK_ESCAPE || event.key.key == SDLK_Q) {
do_exit(cur_stream);
break;
}
// If we don't yet have a window, skip all key events, because read_thread might still be initializing...
if (!cur_stream->width)
continue;
- switch (event.key.keysym.sym) {
- case SDLK_f:
+ switch (event.key.key) {
+ case SDLK_F:
toggle_full_screen(cur_stream);
cur_stream->force_refresh = 1;
break;
- case SDLK_p:
+ case SDLK_P:
case SDLK_SPACE:
toggle_pause(cur_stream);
break;
- case SDLK_m:
+ case SDLK_M:
toggle_mute(cur_stream);
break;
case SDLK_KP_MULTIPLY:
@@ -3422,24 +3411,24 @@ static void event_loop(VideoState *cur_stream)
case SDLK_9:
update_volume(cur_stream, -1, SDL_VOLUME_STEP);
break;
- case SDLK_s: // S: Step to next frame
+ case SDLK_S: // S: Step to next frame
step_to_next_frame(cur_stream);
break;
- case SDLK_a:
+ case SDLK_A:
stream_cycle_channel(cur_stream, AVMEDIA_TYPE_AUDIO);
break;
- case SDLK_v:
+ case SDLK_V:
stream_cycle_channel(cur_stream, AVMEDIA_TYPE_VIDEO);
break;
- case SDLK_c:
+ case SDLK_C:
stream_cycle_channel(cur_stream, AVMEDIA_TYPE_VIDEO);
stream_cycle_channel(cur_stream, AVMEDIA_TYPE_AUDIO);
stream_cycle_channel(cur_stream, AVMEDIA_TYPE_SUBTITLE);
break;
- case SDLK_t:
+ case SDLK_T:
stream_cycle_channel(cur_stream, AVMEDIA_TYPE_SUBTITLE);
break;
- case SDLK_w:
+ case SDLK_W:
if (cur_stream->show_mode == SHOW_MODE_VIDEO && cur_stream->vfilter_idx < nb_vfilters - 1) {
if (++cur_stream->vfilter_idx >= nb_vfilters)
cur_stream->vfilter_idx = 0;
@@ -3502,7 +3491,7 @@ static void event_loop(VideoState *cur_stream)
break;
}
break;
- case SDL_MOUSEBUTTONDOWN:
+ case SDL_EVENT_MOUSE_BUTTON_DOWN:
if (exit_on_mousedown) {
do_exit(cur_stream);
break;
@@ -3517,13 +3506,13 @@ static void event_loop(VideoState *cur_stream)
last_mouse_left_click = av_gettime_relative();
}
}
- case SDL_MOUSEMOTION:
+ case SDL_EVENT_MOUSE_MOTION:
if (cursor_hidden) {
- SDL_ShowCursor(1);
+ SDL_ShowCursor();
cursor_hidden = 0;
}
cursor_last_shown = av_gettime_relative();
- if (event.type == SDL_MOUSEBUTTONDOWN) {
+ if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN) {
if (event.button.button != SDL_BUTTON_RIGHT)
break;
x = event.button.x;
@@ -3557,22 +3546,19 @@ static void event_loop(VideoState *cur_stream)
stream_seek(cur_stream, ts, 0, 0);
}
break;
- case SDL_WINDOWEVENT:
- switch (event.window.event) {
- case SDL_WINDOWEVENT_SIZE_CHANGED:
- screen_width = cur_stream->width = event.window.data1;
- screen_height = cur_stream->height = event.window.data2;
- if (cur_stream->vis_texture) {
- SDL_DestroyTexture(cur_stream->vis_texture);
- cur_stream->vis_texture = NULL;
- }
- if (vk_renderer)
- vk_renderer_resize(vk_renderer, screen_width, screen_height);
- case SDL_WINDOWEVENT_EXPOSED:
- cur_stream->force_refresh = 1;
+ case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
+ screen_width = cur_stream->width = event.window.data1;
+ screen_height = cur_stream->height = event.window.data2;
+ if (cur_stream->vis_texture) {
+ SDL_DestroyTexture(cur_stream->vis_texture);
+ cur_stream->vis_texture = NULL;
}
+ if (vk_renderer)
+ vk_renderer_resize(vk_renderer, screen_width, screen_height);
+ case SDL_EVENT_WINDOW_EXPOSED:
+ cur_stream->force_refresh = 1;
break;
- case SDL_QUIT:
+ case SDL_EVENT_QUIT:
case FF_QUIT_EVENT:
do_exit(cur_stream);
break;
@@ -3820,25 +3806,24 @@ int main(int argc, char **argv)
if (display_disable) {
video_disable = 1;
}
- flags = SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER;
+ flags = SDL_INIT_VIDEO | SDL_INIT_AUDIO;
if (audio_disable)
flags &= ~SDL_INIT_AUDIO;
else {
/* Try to work around an occasional ALSA buffer underflow issue when the
* period size is NPOT due to ALSA resampling by forcing the buffer size. */
if (!SDL_getenv("SDL_AUDIO_ALSA_SET_BUFFER_SIZE"))
- SDL_setenv("SDL_AUDIO_ALSA_SET_BUFFER_SIZE","1", 1);
+ SDL_setenv_unsafe("SDL_AUDIO_ALSA_SET_BUFFER_SIZE","1", 1);
}
if (display_disable)
flags &= ~SDL_INIT_VIDEO;
- if (SDL_Init (flags)) {
+ if (!SDL_Init (flags)) {
av_log(NULL, AV_LOG_FATAL, "Could not initialize SDL - %s\n", SDL_GetError());
av_log(NULL, AV_LOG_FATAL, "(Did you set the DISPLAY variable?)\n");
exit(1);
}
- SDL_EventState(SDL_SYSWMEVENT, SDL_IGNORE);
- SDL_EventState(SDL_USEREVENT, SDL_IGNORE);
+ SDL_SetEventEnabled(SDL_EVENT_USER, false);
if (!display_disable) {
int flags = SDL_WINDOW_HIDDEN;
@@ -3871,8 +3856,7 @@ int main(int argc, char **argv)
enable_vulkan = 0;
}
}
- window = SDL_CreateWindow(program_name, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, default_width, default_height, flags);
- SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
+ window = SDL_CreateWindow(program_name, default_width, default_height, flags);
if (!window) {
av_log(NULL, AV_LOG_FATAL, "Failed to create window: %s", SDL_GetError());
do_exit(NULL);
@@ -3895,19 +3879,24 @@ int main(int argc, char **argv)
do_exit(NULL);
}
} else {
- renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
+ SDL_SetHint(SDL_HINT_RENDER_VSYNC, "1");
+ renderer = SDL_CreateRenderer(window, NULL);
if (!renderer) {
- av_log(NULL, AV_LOG_WARNING, "Failed to initialize a hardware accelerated renderer: %s\n", SDL_GetError());
- renderer = SDL_CreateRenderer(window, -1, 0);
+ av_log(NULL, AV_LOG_FATAL, "Failed to create renderer: %s", SDL_GetError());
+ do_exit(NULL);
}
- if (renderer) {
- if (!SDL_GetRendererInfo(renderer, &renderer_info))
- av_log(NULL, AV_LOG_VERBOSE, "Initialized %s renderer.\n", renderer_info.name);
+ if (!strcmp(SDL_GetRendererName(renderer), SDL_SOFTWARE_RENDERER)) {
+ av_log(NULL, AV_LOG_WARNING, "Created renderer is not hardware accelerated\n");
}
- if (!renderer || !renderer_info.num_texture_formats) {
- av_log(NULL, AV_LOG_FATAL, "Failed to create window or renderer: %s", SDL_GetError());
+ SDL_PropertiesID props = SDL_GetRendererProperties(renderer);
+ renderer_texture_formats = SDL_GetPointerProperty(props, SDL_PROP_RENDERER_TEXTURE_FORMATS_POINTER, NULL);
+ if (!renderer_texture_formats ||
+ *renderer_texture_formats == SDL_PIXELFORMAT_UNKNOWN) {
+ av_log(NULL, AV_LOG_FATAL, "Created renderer supports no texture formats");
do_exit(NULL);
}
+
+ av_log(NULL, AV_LOG_VERBOSE, "Initialized %s renderer.\n",SDL_GetRendererName(renderer));
}
}
diff --git a/fftools/ffplay_renderer.c b/fftools/ffplay_renderer.c
index 1321452ad8..83f8849e44 100644
--- a/fftools/ffplay_renderer.c
+++ b/fftools/ffplay_renderer.c
@@ -220,7 +220,7 @@ static int create_vk_by_hwcontext(VkRenderer *renderer,
// There is no way to pass SDL GetInstanceProcAddr to hwdevice.
// Check the result and return error if they don't match.
- if (hwctx->get_proc_addr != SDL_Vulkan_GetVkGetInstanceProcAddr()) {
+ if (hwctx->get_proc_addr != (PFN_vkGetInstanceProcAddr) SDL_Vulkan_GetVkGetInstanceProcAddr()) {
av_log(renderer, AV_LOG_ERROR,
"hwdevice and SDL use different get_proc_addr. "
"Try -vulkan_params create_by_placebo=1\n");
@@ -347,7 +347,7 @@ static int create_vk_by_placebo(VkRenderer *renderer,
const char **dev_exts;
int num_dev_exts;
- ctx->get_proc_addr = SDL_Vulkan_GetVkGetInstanceProcAddr();
+ ctx->get_proc_addr = (PFN_vkGetInstanceProcAddr) SDL_Vulkan_GetVkGetInstanceProcAddr();
ctx->placebo_instance = pl_vk_inst_create(ctx->vk_log, pl_vk_inst_params(
.get_proc_addr = ctx->get_proc_addr,
@@ -459,7 +459,7 @@ static int create(VkRenderer *renderer, SDL_Window *window, AVDictionary *opt)
ctx->vk_log = pl_log_create(PL_API_VER, &vk_log_params);
- if (!SDL_Vulkan_GetInstanceExtensions(window, &num_ext, NULL)) {
+ if (!SDL_Vulkan_GetInstanceExtensions(&num_ext, NULL)) {
av_log(NULL, AV_LOG_FATAL, "Failed to get vulkan extensions: %s\n",
SDL_GetError());
return AVERROR_EXTERNAL;
@@ -471,7 +471,7 @@ static int create(VkRenderer *renderer, SDL_Window *window, AVDictionary *opt)
goto out;
}
- SDL_Vulkan_GetInstanceExtensions(window, &num_ext, ext);
+ SDL_Vulkan_GetInstanceExtensions(&num_ext, ext);
entry = av_dict_get(opt, "create_by_placebo", NULL, 0);
if (entry && strtol(entry->value, NULL, 10))
@@ -481,7 +481,7 @@ static int create(VkRenderer *renderer, SDL_Window *window, AVDictionary *opt)
if (ret < 0)
goto out;
- if (!SDL_Vulkan_CreateSurface(window, ctx->inst, &ctx->vk_surface)) {
+ if (!SDL_Vulkan_CreateSurface(window, ctx->inst, NULL, &ctx->vk_surface)) {
ret = AVERROR_EXTERNAL;
goto out;
}
@@ -496,7 +496,7 @@ static int create(VkRenderer *renderer, SDL_Window *window, AVDictionary *opt)
goto out;
}
- SDL_Vulkan_GetDrawableSize(window, &w, &h);
+ SDL_GetWindowSizeInPixels(window, &w, &h);
pl_swapchain_resize(ctx->swapchain, &w, &h);
ctx->renderer = pl_renderer_create(ctx->vk_log, ctx->placebo_vulkan->gpu);
diff --git a/fftools/ffplay_renderer.h b/fftools/ffplay_renderer.h
index dd8e9c06e7..f026f42692 100644
--- a/fftools/ffplay_renderer.h
+++ b/fftools/ffplay_renderer.h
@@ -19,7 +19,7 @@
#ifndef FFTOOLS_FFPLAY_RENDERER_H
#define FFTOOLS_FFPLAY_RENDERER_H
-#include <SDL.h>
+#include <SDL3/SDL.h>
#include "libavutil/frame.h"
--
2.51.2
_______________________________________________
ffmpeg-devel mailing list -- ffmpeg-devel@ffmpeg.org
To unsubscribe send an email to ffmpeg-devel-leave@ffmpeg.org
next reply other threads:[~2026-01-02 22:45 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-01-02 22:41 Marcin Serwin via ffmpeg-devel [this message]
2026-01-03 4:41 ` [FFmpeg-devel] " Michael Niedermayer via ffmpeg-devel
2026-01-03 8:46 ` Marcin Serwin via ffmpeg-devel
2026-01-03 4:50 ` Gyan Doshi via ffmpeg-devel
2026-01-03 8:48 ` Marcin Serwin via ffmpeg-devel
2026-01-03 13:13 ` Nicolas George via ffmpeg-devel
2026-01-03 13:59 ` Neal Gompa via ffmpeg-devel
2026-01-03 14:29 ` Nicolas George via ffmpeg-devel
2026-01-03 5:40 ` Zhao Zhili via ffmpeg-devel
2026-01-03 7:12 ` Gyan Doshi via ffmpeg-devel
2026-01-03 8:59 ` Marcin Serwin via ffmpeg-devel
2026-01-03 12:59 ` Zhao Zhili via ffmpeg-devel
2026-01-03 13:12 ` Nicolas George via ffmpeg-devel
2026-01-03 15:14 ` Zhao Zhili via ffmpeg-devel
2026-01-03 15:16 ` Nicolas George via ffmpeg-devel
2026-01-03 15:39 ` Zhao Zhili via ffmpeg-devel
2026-01-03 15:50 ` Nicolas George via ffmpeg-devel
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260102224334.734271-1-marcin@serwin.dev \
--to=ffmpeg-devel@ffmpeg.org \
--cc=marcin@serwin.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
This inbox may be cloned and mirrored by anyone:
git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git
# If you have public-inbox 1.1+ installed, you may
# initialize and index your mirror using the following commands:
public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
ffmpegdev@gitmailbox.com
public-inbox-index ffmpegdev
Example config snippet for mirrors.
AGPL code for this site: git clone https://public-inbox.org/public-inbox.git