Skip to content

Instantly share code, notes, and snippets.

Created May 21, 2016 22:58
Show Gist options
  • Save anonymous/adc405000493a9f26e568ea3ae0468ef to your computer and use it in GitHub Desktop.
Save anonymous/adc405000493a9f26e568ea3ae0468ef to your computer and use it in GitHub Desktop.
From 76becac9ecab97e8a33c34dae865626d2aa1642b Mon Sep 17 00:00:00 2001
From: Marton Balint <cus@passwd.hu>
Date: Mon, 16 May 2016 01:41:07 +0100
Subject: [PATCH 1/2] ffplay: convert ffplay to use SDL2
---
configure | 27 ++-
ffplay.c | 595 ++++++++++++++++++++++++++------------------------------------
2 files changed, 270 insertions(+), 352 deletions(-)
diff --git a/configure b/configure
index 2dede36..f30f536 100755
--- a/configure
+++ b/configure
@@ -2007,6 +2007,7 @@ HAVE_LIST="
perl
pod2man
sdl
+ sdl2
section_data_rel_ro
texi2html
threads
@@ -3075,8 +3076,8 @@ ffmpeg_deps="avcodec avfilter avformat swresample"
ffmpeg_select="aformat_filter anull_filter atrim_filter format_filter
null_filter
setpts_filter trim_filter"
-ffplay_deps="avcodec avformat swscale swresample sdl"
-ffplay_libs='$sdl_libs'
+ffplay_deps="avcodec avformat swscale swresample sdl2"
+ffplay_libs='$sdl2_libs'
ffplay_select="rdft crop_filter transpose_filter hflip_filter vflip_filter rotate_filter"
ffprobe_deps="avcodec avformat"
ffserver_deps="avformat fork sarestart"
@@ -5767,7 +5768,24 @@ if enabled gcrypt; then
fi
fi
-if ! disabled sdl; then
+
+SDL2_CONFIG="${cross_prefix}sdl2-config"
+if check_pkg_config sdl2 SDL_events.h SDL_PollEvent; then
+ check_cpp_condition SDL.h "(SDL_MAJOR_VERSION<<16 | SDL_MINOR_VERSION<<8 | SDL_PATCHLEVEL) >= 0x020001" $sdl2_cflags &&
+ check_cpp_condition SDL.h "(SDL_MAJOR_VERSION<<16 | SDL_MINOR_VERSION<<8 | SDL_PATCHLEVEL) < 0x020100" $sdl2_cflags &&
+ enable sdl2
+else
+ if "${SDL2_CONFIG}" --version > /dev/null 2>&1; then
+ sdl2_cflags=$("${SDL2_CONFIG}" --cflags)
+ sdl2_libs=$("${SDL2_CONFIG}" --libs)
+ check_cpp_condition SDL.h "(SDL_MAJOR_VERSION<<16 | SDL_MINOR_VERSION<<8 | SDL_PATCHLEVEL) >= 0x020001" $sdl2_cflags &&
+ check_cpp_condition SDL.h "(SDL_MAJOR_VERSION<<16 | SDL_MINOR_VERSION<<8 | SDL_PATCHLEVEL) < 0x020100" $sdl2_cflags &&
+ enable sdl2
+ fi
+fi
+enabled sdl2 && add_cflags $sdl2_cflags && add_extralibs $sdl2_libs
+
+if ! disabled sdl and ! enabled sdl2; then
SDL_CONFIG="${cross_prefix}sdl-config"
if check_pkg_config sdl SDL_events.h SDL_PollEvent; then
check_cpp_condition SDL.h "(SDL_MAJOR_VERSION<<16 | SDL_MINOR_VERSION<<8 | SDL_PATCHLEVEL) >= 0x010201" $sdl_cflags &&
@@ -6369,6 +6387,7 @@ echo "network support ${network-no}"
echo "threading support ${thread_type-no}"
echo "safe bitstream reader ${safe_bitstream_reader-no}"
echo "SDL support ${sdl-no}"
+echo "SDL2 support ${sdl2-no}"
echo "opencl enabled ${opencl-no}"
echo "JNI support ${jni-no}"
echo "texi2html enabled ${texi2html-no}"
@@ -6528,7 +6547,7 @@ HOSTLD_O=$HOSTLD_O
TARGET_EXEC=$target_exec $target_exec_args
TARGET_PATH=$target_path
TARGET_SAMPLES=${target_samples:-\$(SAMPLES)}
-CFLAGS-ffplay=$sdl_cflags
+CFLAGS-ffplay=$sdl2_cflags
ZLIB=$($ldflags_filter -lz)
LIB_INSTALL_EXTRA_CMD=$LIB_INSTALL_EXTRA_CMD
EXTRALIBS=$extralibs
diff --git a/ffplay.c b/ffplay.c
index d5fcde8..451ad50 100644
--- a/ffplay.c
+++ b/ffplay.c
@@ -105,6 +105,8 @@ const int program_birth_year = 2003;
#define CURSOR_HIDE_DELAY 1000000
+#define USE_ONEPASS_SUBTITLE_RENDER 1
+
static unsigned sws_flags = SWS_BICUBIC;
typedef struct MyAVPacketList {
@@ -151,17 +153,17 @@ typedef struct Clock {
typedef struct Frame {
AVFrame *frame;
AVSubtitle sub;
- AVSubtitleRect **subrects; /* rescaled subtitle rectangles in yuva */
int serial;
double pts; /* presentation timestamp for the frame */
double duration; /* estimated duration of the frame */
int64_t pos; /* byte position of the frame in the input file */
- SDL_Overlay *bmp;
+ SDL_Texture *bmp;
int allocated;
- int reallocate;
int width;
int height;
+ int format;
AVRational sar;
+ int uploaded;
} Frame;
typedef struct FrameQueue {
@@ -271,6 +273,8 @@ typedef struct VideoState {
FFTSample *rdft_data;
int xpos;
double last_vis_time;
+ SDL_Texture *vis_texture;
+ SDL_Texture *sub_texture;
int subtitle_stream;
AVStream *subtitle_st;
@@ -283,11 +287,8 @@ typedef struct VideoState {
AVStream *video_st;
PacketQueue videoq;
double max_frame_duration; // maximum duration of a frame - above this, we consider the jump a timestamp discontinuity
-#if !CONFIG_AVFILTER
struct SwsContext *img_convert_ctx;
-#endif
struct SwsContext *sub_convert_ctx;
- SDL_Rect last_display_rect;
int eof;
char *filename;
@@ -312,8 +313,6 @@ typedef struct VideoState {
static AVInputFormat *file_iformat;
static const char *input_filename;
static const char *window_title;
-static int fs_screen_width;
-static int fs_screen_height;
static int default_width = 640;
static int default_height = 480;
static int screen_width = 0;
@@ -361,7 +360,8 @@ static AVPacket flush_pkt;
#define FF_ALLOC_EVENT (SDL_USEREVENT)
#define FF_QUIT_EVENT (SDL_USEREVENT + 2)
-static SDL_Surface *screen;
+static SDL_Window *window;
+static SDL_Renderer *renderer;
#if CONFIG_AVFILTER
static int opt_add_vfilter(void *optctx, const char *opt, const char *arg)
@@ -646,12 +646,6 @@ static void decoder_destroy(Decoder *d) {
static void frame_queue_unref_item(Frame *vp)
{
- int i;
- for (i = 0; i < vp->sub.num_rects; i++) {
- av_freep(&vp->subrects[i]->data[0]);
- av_freep(&vp->subrects[i]);
- }
- av_freep(&vp->subrects);
av_frame_unref(vp->frame);
avsubtitle_free(&vp->sub);
}
@@ -802,113 +796,47 @@ static void decoder_abort(Decoder *d, FrameQueue *fq)
packet_queue_flush(d->queue);
}
-static inline void fill_rectangle(SDL_Surface *screen,
- int x, int y, int w, int h, int color, int update)
+static inline void fill_rectangle(int x, int y, int w, int h)
{
SDL_Rect rect;
rect.x = x;
rect.y = y;
rect.w = w;
rect.h = h;
- SDL_FillRect(screen, &rect, color);
- if (update && w > 0 && h > 0)
- SDL_UpdateRect(screen, x, y, w, h);
-}
-
-/* draw only the border of a rectangle */
-static void fill_border(int xleft, int ytop, int width, int height, int x, int y, int w, int h, int color, int update)
-{
- int w1, w2, h1, h2;
-
- /* fill the background */
- w1 = x;
- if (w1 < 0)
- w1 = 0;
- w2 = width - (x + w);
- if (w2 < 0)
- w2 = 0;
- h1 = y;
- if (h1 < 0)
- h1 = 0;
- h2 = height - (y + h);
- if (h2 < 0)
- h2 = 0;
- fill_rectangle(screen,
- xleft, ytop,
- w1, height,
- color, update);
- fill_rectangle(screen,
- xleft + width - w2, ytop,
- w2, height,
- color, update);
- fill_rectangle(screen,
- xleft + w1, ytop,
- width - w1 - w2, h1,
- color, update);
- fill_rectangle(screen,
- xleft + w1, ytop + height - h2,
- width - w1 - w2, h2,
- color, update);
-}
-
-#define ALPHA_BLEND(a, oldp, newp, s)\
-((((oldp << s) * (255 - (a))) + (newp * (a))) / (255 << s))
-
-
-
-#define BPP 1
-
-static void blend_subrect(uint8_t **data, int *linesize, const AVSubtitleRect *rect, int imgw, int imgh)
-{
- int x, y, Y, U, V, A;
- uint8_t *lum, *cb, *cr;
- int dstx, dsty, dstw, dsth;
- const AVSubtitleRect *src = rect;
-
- dstw = av_clip(rect->w, 0, imgw);
- dsth = av_clip(rect->h, 0, imgh);
- dstx = av_clip(rect->x, 0, imgw - dstw);
- dsty = av_clip(rect->y, 0, imgh - dsth);
- lum = data[0] + dstx + dsty * linesize[0];
- cb = data[1] + dstx/2 + (dsty >> 1) * linesize[1];
- cr = data[2] + dstx/2 + (dsty >> 1) * linesize[2];
-
- for (y = 0; y<dsth; y++) {
- for (x = 0; x<dstw; x++) {
- Y = src->data[0][x + y*src->linesize[0]];
- A = src->data[3][x + y*src->linesize[3]];
- lum[0] = ALPHA_BLEND(A, lum[0], Y, 0);
- lum++;
- }
- lum += linesize[0] - dstw;
- }
-
- for (y = 0; y<dsth/2; y++) {
- for (x = 0; x<dstw/2; x++) {
- U = src->data[1][x + y*src->linesize[1]];
- V = src->data[2][x + y*src->linesize[2]];
- A = src->data[3][2*x + 2*y *src->linesize[3]]
- + src->data[3][2*x + 1 + 2*y *src->linesize[3]]
- + src->data[3][2*x + 1 + (2*y+1)*src->linesize[3]]
- + src->data[3][2*x + (2*y+1)*src->linesize[3]];
- cb[0] = ALPHA_BLEND(A>>2, cb[0], U, 0);
- cr[0] = ALPHA_BLEND(A>>2, cr[0], V, 0);
- cb++;
- cr++;
- }
- cb += linesize[1] - dstw/2;
- cr += linesize[2] - dstw/2;
- }
+ if (w && h)
+ SDL_RenderFillRect(renderer, &rect);
}
static void free_picture(Frame *vp)
{
if (vp->bmp) {
- SDL_FreeYUVOverlay(vp->bmp);
+ SDL_DestroyTexture(vp->bmp);
vp->bmp = NULL;
}
}
+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 (SDL_QueryTexture(*texture, &format, &access, &w, &h) < 0 || new_width != w || new_height != h || new_format != format) {
+ void *pixels;
+ int pitch;
+ 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)
+ return -1;
+ if (init_texture) {
+ if (SDL_LockTexture(*texture, NULL, &pixels, &pitch) < 0)
+ return -1;
+ memset(pixels, 0, pitch * new_height);
+ SDL_UnlockTexture(*texture);
+ }
+ }
+ return 0;
+}
+
static void calculate_display_rect(SDL_Rect *rect,
int scr_xleft, int scr_ytop, int scr_width, int scr_height,
int pic_width, int pic_height, AVRational pic_sar)
@@ -940,12 +868,44 @@ static void calculate_display_rect(SDL_Rect *rect,
rect->h = FFMAX(height, 1);
}
+static int upload_texture(SDL_Texture *tex, AVFrame *frame, struct SwsContext **img_convert_ctx) {
+ int ret = 0;
+ switch (frame->format) {
+ case AV_PIX_FMT_YUV420P:
+ ret = SDL_UpdateYUVTexture(tex, NULL, frame->data[0], frame->linesize[0],
+ frame->data[1], frame->linesize[1],
+ frame->data[2], frame->linesize[2]);
+ break;
+ case AV_PIX_FMT_BGRA:
+ ret = SDL_UpdateTexture(tex, NULL, frame->data[0], frame->linesize[0]);
+ break;
+ default:
+ /* This should only happen if we are not using avfilter... */
+ *img_convert_ctx = sws_getCachedContext(*img_convert_ctx,
+ frame->width, frame->height, frame->format, frame->width, frame->height,
+ AV_PIX_FMT_BGRA, sws_flags, NULL, NULL, NULL);
+ if (*img_convert_ctx != NULL) {
+ uint8_t *pixels;
+ int pitch;
+ if (!SDL_LockTexture(tex, NULL, (void **)&pixels, &pitch)) {
+ sws_scale(*img_convert_ctx, (const uint8_t * const *)frame->data, frame->linesize,
+ 0, frame->height, &pixels, &pitch);
+ SDL_UnlockTexture(tex);
+ }
+ } else {
+ av_log(NULL, AV_LOG_FATAL, "Cannot initialize the conversion context\n");
+ ret = -1;
+ }
+ break;
+ }
+ return ret;
+}
+
static void video_image_display(VideoState *is)
{
Frame *vp;
- Frame *sp;
+ Frame *sp = NULL;
SDL_Rect rect;
- int i;
vp = frame_queue_peek(&is->pictq);
if (vp->bmp) {
@@ -954,36 +914,71 @@ static void video_image_display(VideoState *is)
sp = frame_queue_peek(&is->subpq);
if (vp->pts >= sp->pts + ((float) sp->sub.start_display_time / 1000)) {
- uint8_t *data[4];
- int linesize[4];
-
- SDL_LockYUVOverlay (vp->bmp);
-
- data[0] = vp->bmp->pixels[0];
- data[1] = vp->bmp->pixels[2];
- data[2] = vp->bmp->pixels[1];
-
- linesize[0] = vp->bmp->pitches[0];
- linesize[1] = vp->bmp->pitches[2];
- linesize[2] = vp->bmp->pitches[1];
-
- for (i = 0; i < sp->sub.num_rects; i++)
- blend_subrect(data, linesize, sp->subrects[i],
- vp->bmp->w, vp->bmp->h);
-
- SDL_UnlockYUVOverlay (vp->bmp);
- }
+ if (!sp->uploaded) {
+ uint8_t *pixels;
+ int pitch;
+ int i;
+ if (!sp->width || !sp->height) {
+ sp->width = vp->width;
+ sp->height = vp->height;
+ }
+ if (realloc_texture(&is->sub_texture, SDL_PIXELFORMAT_ARGB8888, sp->width, sp->height, SDL_BLENDMODE_BLEND, 1) < 0)
+ return;
+
+ for (i = 0; i < sp->sub.num_rects; i++) {
+ AVSubtitleRect *sub_rect = sp->sub.rects[i];
+
+ sub_rect->x = av_clip(sub_rect->x, 0, sp->width );
+ sub_rect->y = av_clip(sub_rect->y, 0, sp->height);
+ sub_rect->w = av_clip(sub_rect->w, 0, sp->width - sub_rect->x);
+ sub_rect->h = av_clip(sub_rect->h, 0, sp->height - sub_rect->y);
+
+ is->sub_convert_ctx = sws_getCachedContext(is->sub_convert_ctx,
+ sub_rect->w, sub_rect->h, AV_PIX_FMT_PAL8,
+ sub_rect->w, sub_rect->h, AV_PIX_FMT_BGRA,
+ 0, NULL, NULL, NULL);
+ if (!is->sub_convert_ctx) {
+ av_log(NULL, AV_LOG_FATAL, "Cannot initialize the conversion context\n");
+ return;
+ }
+ if (!SDL_LockTexture(is->sub_texture, (SDL_Rect *)sub_rect, (void **)&pixels, &pitch)) {
+ sws_scale(is->sub_convert_ctx, (const uint8_t * const *)sub_rect->data, sub_rect->linesize,
+ 0, sub_rect->h, &pixels, &pitch);
+ SDL_UnlockTexture(is->sub_texture);
+ }
+ }
+ sp->uploaded = 1;
+ }
+ } else
+ sp = NULL;
}
}
calculate_display_rect(&rect, is->xleft, is->ytop, is->width, is->height, vp->width, vp->height, vp->sar);
- SDL_DisplayYUVOverlay(vp->bmp, &rect);
+ if (!vp->uploaded) {
+ if (upload_texture(vp->bmp, vp->frame, &is->img_convert_ctx) < 0)
+ return;
+ vp->uploaded = 1;
+ }
- if (rect.x != is->last_display_rect.x || rect.y != is->last_display_rect.y || rect.w != is->last_display_rect.w || rect.h != is->last_display_rect.h || is->force_refresh) {
- int bgcolor = SDL_MapRGB(screen->format, 0x00, 0x00, 0x00);
- fill_border(is->xleft, is->ytop, is->width, is->height, rect.x, rect.y, rect.w, rect.h, bgcolor, 1);
- is->last_display_rect = rect;
+ SDL_RenderCopy(renderer, vp->bmp, NULL, &rect);
+ if (sp) {
+#if USE_ONEPASS_SUBTITLE_RENDER
+ SDL_RenderCopy(renderer, is->sub_texture, NULL, &rect);
+#else
+ int i;
+ double xratio = (double)rect.w / (double)sp->width;
+ double yratio = (double)rect.h / (double)sp->height;
+ for (i = 0; i < sp->sub.num_rects; i++) {
+ SDL_Rect *sub_rect = (SDL_Rect*)sp->sub.rects[i];
+ SDL_Rect target = {.x = rect.x + sub_rect->x * xratio,
+ .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);
+ }
+#endif
}
}
}
@@ -996,7 +991,7 @@ static inline int compute_mod(int a, int b)
static void video_audio_display(VideoState *s)
{
int i, i_start, x, y1, y, ys, delay, n, nb_display_channels;
- int ch, channels, h, h2, bgcolor, fgcolor;
+ int ch, channels, h, h2;
int64_t time_diff;
int rdft_bits, nb_freq;
@@ -1046,13 +1041,8 @@ static void video_audio_display(VideoState *s)
i_start = s->last_i_start;
}
- bgcolor = SDL_MapRGB(screen->format, 0x00, 0x00, 0x00);
if (s->show_mode == SHOW_MODE_WAVES) {
- fill_rectangle(screen,
- s->xleft, s->ytop, s->width, s->height,
- bgcolor, 0);
-
- fgcolor = SDL_MapRGB(screen->format, 0xff, 0xff, 0xff);
+ SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
/* total height for one channel */
h = s->height / nb_display_channels;
@@ -1069,25 +1059,23 @@ static void video_audio_display(VideoState *s)
} else {
ys = y1;
}
- fill_rectangle(screen,
- s->xleft + x, ys, 1, y,
- fgcolor, 0);
+ fill_rectangle(s->xleft + x, ys, 1, y);
i += channels;
if (i >= SAMPLE_ARRAY_SIZE)
i -= SAMPLE_ARRAY_SIZE;
}
}
- fgcolor = SDL_MapRGB(screen->format, 0x00, 0x00, 0xff);
+ SDL_SetRenderDrawColor(renderer, 0, 0, 255, 255);
for (ch = 1; ch < nb_display_channels; ch++) {
y = s->ytop + ch * h;
- fill_rectangle(screen,
- s->xleft, y, s->width, 1,
- fgcolor, 0);
+ fill_rectangle(s->xleft, y, s->width, 1);
}
- SDL_UpdateRect(screen, s->xleft, s->ytop, s->width, s->height);
} else {
+ if (realloc_texture(&s->vis_texture, SDL_PIXELFORMAT_ARGB8888, s->width, s->height, SDL_BLENDMODE_NONE, 1) < 0)
+ return;
+
nb_display_channels= FFMIN(nb_display_channels, 2);
if (rdft_bits != s->rdft_bits) {
av_rdft_end(s->rdft);
@@ -1101,6 +1089,9 @@ static void video_audio_display(VideoState *s)
s->show_mode = SHOW_MODE_WAVES;
} else {
FFTSample *data[2];
+ SDL_Rect rect = {.x = s->xpos, .y = 0, .w = 1, .h = s->height};
+ uint32_t *pixels;
+ int pitch;
for (ch = 0; ch < nb_display_channels; ch++) {
data[ch] = s->rdft_data + 2 * nb_freq * ch;
i = i_start + ch;
@@ -1115,21 +1106,23 @@ static void video_audio_display(VideoState *s)
}
/* Least efficient way to do this, we should of course
* directly access it but it is more than fast enough. */
- for (y = 0; y < s->height; y++) {
- double w = 1 / sqrt(nb_freq);
- int a = sqrt(w * hypot(data[0][2 * y + 0], data[0][2 * y + 1]));
- int b = (nb_display_channels == 2 ) ? sqrt(w * hypot(data[1][2 * y + 0], data[1][2 * y + 1]))
- : a;
- a = FFMIN(a, 255);
- b = FFMIN(b, 255);
- fgcolor = SDL_MapRGB(screen->format, a, b, (a + b) / 2);
-
- fill_rectangle(screen,
- s->xpos, s->height-y, 1, 1,
- fgcolor, 0);
+ if (!SDL_LockTexture(s->vis_texture, &rect, (void **)&pixels, &pitch)) {
+ pitch >>= 2;
+ pixels += pitch * s->height;
+ for (y = 0; y < s->height; y++) {
+ double w = 1 / sqrt(nb_freq);
+ int a = sqrt(w * sqrt(data[0][2 * y + 0] * data[0][2 * y + 0] + data[0][2 * y + 1] * data[0][2 * y + 1]));
+ int b = (nb_display_channels == 2 ) ? sqrt(w * hypot(data[1][2 * y + 0], data[1][2 * y + 1]))
+ : a;
+ a = FFMIN(a, 255);
+ b = FFMIN(b, 255);
+ pixels -= pitch;
+ *pixels = (a << 16) + (b << 8) + ((a+b) >> 1);
+ }
+ SDL_UnlockTexture(s->vis_texture);
}
+ SDL_RenderCopy(renderer, s->vis_texture, NULL, NULL);
}
- SDL_UpdateRect(screen, s->xpos, s->ytop, 1, s->height);
if (!s->paused)
s->xpos++;
if (s->xpos >= s->width)
@@ -1219,11 +1212,13 @@ static void stream_close(VideoState *is)
frame_queue_destory(&is->sampq);
frame_queue_destory(&is->subpq);
SDL_DestroyCond(is->continue_read_thread);
-#if !CONFIG_AVFILTER
sws_freeContext(is->img_convert_ctx);
-#endif
sws_freeContext(is->sub_convert_ctx);
av_free(is->filename);
+ if (is->vis_texture)
+ SDL_DestroyTexture(is->vis_texture);
+ if (is->sub_texture)
+ SDL_DestroyTexture(is->sub_texture);
av_free(is);
}
@@ -1232,6 +1227,10 @@ static void do_exit(VideoState *is)
if (is) {
stream_close(is);
}
+ if (renderer)
+ SDL_DestroyRenderer(renderer);
+ if (window)
+ SDL_DestroyWindow(window);
av_lockmgr_register(NULL);
uninit_opts();
#if CONFIG_AVFILTER
@@ -1258,42 +1257,48 @@ static void set_default_window_size(int width, int height, AVRational sar)
default_height = rect.h;
}
-static int video_open(VideoState *is, int force_set_video_mode, Frame *vp)
+static int video_open(VideoState *is, Frame *vp)
{
- int flags = SDL_HWSURFACE | SDL_ASYNCBLIT | SDL_HWACCEL;
int w,h;
- if (is_full_screen) flags |= SDL_FULLSCREEN;
- else flags |= SDL_RESIZABLE;
-
if (vp && vp->width)
set_default_window_size(vp->width, vp->height, vp->sar);
- if (is_full_screen && fs_screen_width) {
- w = fs_screen_width;
- h = fs_screen_height;
- } else if (!is_full_screen && screen_width) {
+ if (screen_width) {
w = screen_width;
h = screen_height;
} else {
w = default_width;
h = default_height;
}
- w = FFMIN(16383, w);
- if (screen && is->width == screen->w && screen->w == w
- && is->height== screen->h && screen->h == h && !force_set_video_mode)
- return 0;
- screen = SDL_SetVideoMode(w, h, 0, flags);
- if (!screen) {
+
+ if (!window) {
+ int flags = SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE;
+ if (!window_title)
+ window_title = input_filename;
+ if (is_full_screen)
+ flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
+ window = SDL_CreateWindow(window_title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, w, h, flags);
+ SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
+ if (window) {
+ SDL_RendererInfo info;
+ renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
+ if (renderer) {
+ if (!SDL_GetRendererInfo(renderer, &info))
+ av_log(NULL, AV_LOG_VERBOSE, "Initialized %s renderer.\n", info.name);
+ }
+ }
+ } else {
+ SDL_SetWindowSize(window, w, h);
+ }
+
+ if (!window || !renderer) {
av_log(NULL, AV_LOG_FATAL, "SDL: could not set video mode - exiting\n");
do_exit(is);
}
- if (!window_title)
- window_title = input_filename;
- SDL_WM_SetCaption(window_title, window_title);
- is->width = screen->w;
- is->height = screen->h;
+ is->width = w;
+ is->height = h;
return 0;
}
@@ -1301,12 +1306,16 @@ static int video_open(VideoState *is, int force_set_video_mode, Frame *vp)
/* display the current picture, if any */
static void video_display(VideoState *is)
{
- if (!screen)
- video_open(is, 0, NULL);
+ if (!window)
+ video_open(is, NULL);
+
+ SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
+ SDL_RenderClear(renderer);
if (is->audio_st && is->show_mode != SHOW_MODE_VIDEO)
video_audio_display(is);
else if (is->video_st)
video_image_display(is);
+ SDL_RenderPresent(renderer);
}
static double get_clock(Clock *c)
@@ -1600,6 +1609,20 @@ retry:
|| (is->vidclk.pts > (sp->pts + ((float) sp->sub.end_display_time / 1000)))
|| (sp2 && is->vidclk.pts > (sp2->pts + ((float) sp2->sub.start_display_time / 1000))))
{
+ if (sp->uploaded) {
+ int i;
+ for (i = 0; i < sp->sub.num_rects; i++) {
+ AVSubtitleRect *sub_rect = sp->sub.rects[i];
+ uint8_t *pixels;
+ int pitch, j;
+
+ if (!SDL_LockTexture(is->sub_texture, (SDL_Rect *)sub_rect, (void **)&pixels, &pitch)) {
+ for (j = 0; j < sub_rect->h; j++, pixels += pitch)
+ memset(pixels, 0, sub_rect->w << 2);
+ SDL_UnlockTexture(is->sub_texture);
+ }
+ }
+ }
frame_queue_next(&is->subpq);
} else {
break;
@@ -1665,19 +1688,18 @@ display:
static void alloc_picture(VideoState *is)
{
Frame *vp;
- int64_t bufferdiff;
+ int sdl_format;
vp = &is->pictq.queue[is->pictq.windex];
- free_picture(vp);
+ video_open(is, vp);
- video_open(is, 0, vp);
+ if (vp->format == AV_PIX_FMT_YUV420P)
+ sdl_format = SDL_PIXELFORMAT_YV12;
+ else
+ sdl_format = SDL_PIXELFORMAT_ARGB8888;
- vp->bmp = SDL_CreateYUVOverlay(vp->width, vp->height,
- SDL_YV12_OVERLAY,
- screen);
- bufferdiff = vp->bmp ? FFMAX(vp->bmp->pixels[0], vp->bmp->pixels[1]) - FFMIN(vp->bmp->pixels[0], vp->bmp->pixels[1]) : 0;
- if (!vp->bmp || vp->bmp->pitches[0] < vp->width || bufferdiff < (int64_t)vp->height * vp->bmp->pitches[0]) {
+ if (realloc_texture(&vp->bmp, sdl_format, vp->width, vp->height, SDL_BLENDMODE_NONE, 0) < 0) {
/* SDL allocates a buffer smaller than requested if the video
* overlay hardware is unable to support the requested size. */
av_log(NULL, AV_LOG_FATAL,
@@ -1693,24 +1715,6 @@ static void alloc_picture(VideoState *is)
SDL_UnlockMutex(is->pictq.mutex);
}
-static void duplicate_right_border_pixels(SDL_Overlay *bmp) {
- int i, width, height;
- Uint8 *p, *maxp;
- for (i = 0; i < 3; i++) {
- width = bmp->w;
- height = bmp->h;
- if (i > 0) {
- width >>= 1;
- height >>= 1;
- }
- if (bmp->pitches[i] > width) {
- maxp = bmp->pixels[i] + bmp->pitches[i] * height - 1;
- for (p = bmp->pixels[i] + width - 1; p < maxp; p += bmp->pitches[i])
- *(p+1) = *p;
- }
- }
-}
-
static int queue_picture(VideoState *is, AVFrame *src_frame, double pts, double duration, int64_t pos, int serial)
{
Frame *vp;
@@ -1724,17 +1728,19 @@ static int queue_picture(VideoState *is, AVFrame *src_frame, double pts, double
return -1;
vp->sar = src_frame->sample_aspect_ratio;
+ vp->uploaded = 0;
/* alloc or resize hardware picture buffer */
- if (!vp->bmp || vp->reallocate || !vp->allocated ||
+ if (!vp->bmp || !vp->allocated ||
vp->width != src_frame->width ||
- vp->height != src_frame->height) {
+ vp->height != src_frame->height ||
+ vp->format != src_frame->format) {
SDL_Event event;
- vp->allocated = 0;
- vp->reallocate = 0;
+ vp->allocated = 0;
vp->width = src_frame->width;
vp->height = src_frame->height;
+ vp->format = src_frame->format;
/* the allocation must be done in the main thread to avoid
locking problems. */
@@ -1748,7 +1754,7 @@ static int queue_picture(VideoState *is, AVFrame *src_frame, double pts, double
SDL_CondWait(is->pictq.cond, is->pictq.mutex);
}
/* if the queue is aborted, we have to pop the pending ALLOC event or wait for the allocation to complete */
- if (is->videoq.abort_request && SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_EVENTMASK(FF_ALLOC_EVENT)) != 1) {
+ if (is->videoq.abort_request && SDL_PeepEvents(&event, 1, SDL_GETEVENT, FF_ALLOC_EVENT, FF_ALLOC_EVENT) != 1) {
while (!vp->allocated && !is->abort_request) {
SDL_CondWait(is->pictq.cond, is->pictq.mutex);
}
@@ -1761,58 +1767,12 @@ static int queue_picture(VideoState *is, AVFrame *src_frame, double pts, double
/* if the frame is not skipped, then display it */
if (vp->bmp) {
- uint8_t *data[4];
- int linesize[4];
-
- /* get a pointer on the bitmap */
- SDL_LockYUVOverlay (vp->bmp);
-
- data[0] = vp->bmp->pixels[0];
- data[1] = vp->bmp->pixels[2];
- data[2] = vp->bmp->pixels[1];
-
- linesize[0] = vp->bmp->pitches[0];
- linesize[1] = vp->bmp->pitches[2];
- linesize[2] = vp->bmp->pitches[1];
-
-#if CONFIG_AVFILTER
- // FIXME use direct rendering
- av_image_copy(data, linesize, (const uint8_t **)src_frame->data, src_frame->linesize,
- src_frame->format, vp->width, vp->height);
-#else
- {
- AVDictionaryEntry *e = av_dict_get(sws_dict, "sws_flags", NULL, 0);
- if (e) {
- const AVClass *class = sws_get_class();
- const AVOption *o = av_opt_find(&class, "sws_flags", NULL, 0,
- AV_OPT_SEARCH_FAKE_OBJ);
- int ret = av_opt_eval_flags(&class, o, e->value, &sws_flags);
- if (ret < 0)
- exit(1);
- }
- }
-
- is->img_convert_ctx = sws_getCachedContext(is->img_convert_ctx,
- vp->width, vp->height, src_frame->format, vp->width, vp->height,
- AV_PIX_FMT_YUV420P, sws_flags, NULL, NULL, NULL);
- if (!is->img_convert_ctx) {
- av_log(NULL, AV_LOG_FATAL, "Cannot initialize the conversion context\n");
- exit(1);
- }
- sws_scale(is->img_convert_ctx, src_frame->data, src_frame->linesize,
- 0, vp->height, data, linesize);
-#endif
- /* workaround SDL PITCH_WORKAROUND */
- duplicate_right_border_pixels(vp->bmp);
- /* update the bitmap content */
- SDL_UnlockYUVOverlay(vp->bmp);
-
vp->pts = pts;
vp->duration = duration;
vp->pos = pos;
vp->serial = serial;
- /* now we can update the picture count */
+ av_frame_move_ref(vp->frame, src_frame);
frame_queue_push(&is->pictq);
}
return 0;
@@ -1900,7 +1860,7 @@ fail:
static int configure_video_filters(AVFilterGraph *graph, VideoState *is, const char *vfilters, AVFrame *frame)
{
- static const enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE };
+ static const enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_YUV420P, AV_PIX_FMT_BGRA, AV_PIX_FMT_NONE };
char sws_flags_str[512] = "";
char buffersrc_args[256];
int ret;
@@ -1963,10 +1923,6 @@ static int configure_video_filters(AVFilterGraph *graph, VideoState *is, const c
last_filter = filt_ctx; \
} while (0)
- /* SDL YUV code is not handling odd width/height for some driver
- * combinations, therefore we crop the picture to an even width/height. */
- INSERT_FILT("crop", "floor(in_w/2)*2:floor(in_h/2)*2");
-
if (autorotate) {
double theta = get_rotation(is->video_st);
@@ -2161,7 +2117,7 @@ static int audio_thread(void *arg)
static int decoder_start(Decoder *d, int (*fn)(void *), void *arg)
{
packet_queue_start(d->queue);
- d->decoder_tid = SDL_CreateThread(fn, arg);
+ d->decoder_tid = SDL_CreateThread(fn, "decoder", arg);
if (!d->decoder_tid) {
av_log(NULL, AV_LOG_ERROR, "SDL_CreateThread(): %s\n", SDL_GetError());
return AVERROR(ENOMEM);
@@ -2281,10 +2237,10 @@ static int video_thread(void *arg)
static int subtitle_thread(void *arg)
{
VideoState *is = arg;
+ AVCodecContext *dec = is->subtitle_st->codec;
Frame *sp;
int got_subtitle;
double pts;
- int i;
for (;;) {
if (!(sp = frame_queue_peek_writable(&is->subpq)))
@@ -2300,42 +2256,9 @@ static int subtitle_thread(void *arg)
pts = sp->sub.pts / (double)AV_TIME_BASE;
sp->pts = pts;
sp->serial = is->subdec.pkt_serial;
- if (!(sp->subrects = av_mallocz_array(sp->sub.num_rects, sizeof(AVSubtitleRect*)))) {
- av_log(NULL, AV_LOG_FATAL, "Cannot allocate subrects\n");
- exit(1);
- }
-
- for (i = 0; i < sp->sub.num_rects; i++)
- {
- int in_w = sp->sub.rects[i]->w;
- int in_h = sp->sub.rects[i]->h;
- int subw = is->subdec.avctx->width ? is->subdec.avctx->width : is->viddec_width;
- int subh = is->subdec.avctx->height ? is->subdec.avctx->height : is->viddec_height;
- int out_w = is->viddec_width ? in_w * is->viddec_width / subw : in_w;
- int out_h = is->viddec_height ? in_h * is->viddec_height / subh : in_h;
-
- if (!(sp->subrects[i] = av_mallocz(sizeof(AVSubtitleRect))) ||
- av_image_alloc(sp->subrects[i]->data, sp->subrects[i]->linesize, out_w, out_h, AV_PIX_FMT_YUVA420P, 16) < 0) {
- av_log(NULL, AV_LOG_FATAL, "Cannot allocate subtitle data\n");
- exit(1);
- }
-
- is->sub_convert_ctx = sws_getCachedContext(is->sub_convert_ctx,
- in_w, in_h, AV_PIX_FMT_PAL8, out_w, out_h,
- AV_PIX_FMT_YUVA420P, sws_flags, NULL, NULL, NULL);
- if (!is->sub_convert_ctx) {
- av_log(NULL, AV_LOG_FATAL, "Cannot initialize the sub conversion context\n");
- exit(1);
- }
- sws_scale(is->sub_convert_ctx,
- (void*)sp->sub.rects[i]->data, sp->sub.rects[i]->linesize,
- 0, in_h, sp->subrects[i]->data, sp->subrects[i]->linesize);
-
- sp->subrects[i]->w = out_w;
- sp->subrects[i]->h = out_h;
- sp->subrects[i]->x = sp->sub.rects[i]->x * out_w / in_w;
- sp->subrects[i]->y = sp->sub.rects[i]->y * out_h / in_h;
- }
+ sp->width = dec->width;
+ sp->height = dec->height;
+ sp->uploaded = 0;
/* now we can update the picture count */
frame_queue_push(&is->subpq);
@@ -3192,7 +3115,7 @@ static VideoState *stream_open(const char *filename, AVInputFormat *iformat)
is->audio_volume = SDL_MIX_MAXVOLUME;
is->muted = 0;
is->av_sync_type = av_sync_type;
- is->read_tid = SDL_CreateThread(read_thread, is);
+ is->read_tid = SDL_CreateThread(read_thread, "read_thread", is);
if (!is->read_tid) {
av_log(NULL, AV_LOG_FATAL, "SDL_CreateThread(): %s\n", SDL_GetError());
fail:
@@ -3283,27 +3206,17 @@ static void stream_cycle_channel(VideoState *is, int codec_type)
static void toggle_full_screen(VideoState *is)
{
-#if defined(__APPLE__) && SDL_VERSION_ATLEAST(1, 2, 14)
- /* OS X needs to reallocate the SDL overlays */
- int i;
- for (i = 0; i < VIDEO_PICTURE_QUEUE_SIZE; i++)
- is->pictq.queue[i].reallocate = 1;
-#endif
is_full_screen = !is_full_screen;
- video_open(is, 1, NULL);
+ SDL_SetWindowFullscreen(window, is_full_screen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
}
static void toggle_audio_display(VideoState *is)
{
- int bgcolor = SDL_MapRGB(screen->format, 0x00, 0x00, 0x00);
int next = is->show_mode;
do {
next = (next + 1) % SHOW_MODE_NB;
} while (next != is->show_mode && (next == SHOW_MODE_VIDEO && !is->video_st || next != SHOW_MODE_VIDEO && !is->audio_st));
if (is->show_mode != next) {
- fill_rectangle(screen,
- is->xleft, is->ytop, is->width, is->height,
- bgcolor, 1);
is->force_refresh = 1;
is->show_mode = next;
}
@@ -3312,7 +3225,7 @@ 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_ALLEVENTS)) {
+ while (!SDL_PeepEvents(event, 1, SDL_GETEVENT, SDL_FIRSTEVENT, SDL_LASTEVENT)) {
if (!cursor_hidden && av_gettime_relative() - cursor_last_shown > CURSOR_HIDE_DELAY) {
SDL_ShowCursor(0);
cursor_hidden = 1;
@@ -3476,9 +3389,6 @@ static void event_loop(VideoState *cur_stream)
break;
}
break;
- case SDL_VIDEOEXPOSE:
- cur_stream->force_refresh = 1;
- break;
case SDL_MOUSEBUTTONDOWN:
if (exit_on_mousedown) {
do_exit(cur_stream);
@@ -3534,16 +3444,18 @@ static void event_loop(VideoState *cur_stream)
stream_seek(cur_stream, ts, 0, 0);
}
break;
- case SDL_VIDEORESIZE:
- screen = SDL_SetVideoMode(FFMIN(16383, event.resize.w), event.resize.h, 0,
- SDL_HWSURFACE|(is_full_screen?SDL_FULLSCREEN:SDL_RESIZABLE)|SDL_ASYNCBLIT|SDL_HWACCEL);
- if (!screen) {
- av_log(NULL, AV_LOG_FATAL, "Failed to set video mode\n");
- do_exit(cur_stream);
- }
- screen_width = cur_stream->width = screen->w;
- screen_height = cur_stream->height = screen->h;
- cur_stream->force_refresh = 1;
+ case SDL_WINDOWEVENT:
+ switch (event.window.event) {
+ case SDL_WINDOWEVENT_RESIZED:
+ 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;
+ }
+ case SDL_WINDOWEVENT_EXPOSED:
+ cur_stream->force_refresh = 1;
+ }
break;
case SDL_QUIT:
case FF_QUIT_EVENT:
@@ -3780,7 +3692,6 @@ int main(int argc, char **argv)
{
int flags;
VideoState *is;
- char dummy_videodriver[] = "SDL_VIDEODRIVER=dummy";
char alsa_bufsize[] = "SDL_AUDIO_ALSA_SET_BUFFER_SIZE=1";
av_log_set_flags(AV_LOG_SKIP_REPEATED);
@@ -3822,32 +3733,20 @@ int main(int argc, char **argv)
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_putenv(alsa_bufsize);
+ //if (!SDL_getenv("SDL_AUDIO_ALSA_SET_BUFFER_SIZE"))
+ // SDL_putenv(alsa_bufsize);
}
if (display_disable)
- SDL_putenv(dummy_videodriver); /* For the event queue, we always need a video driver. */
-#if !defined(_WIN32) && !defined(__APPLE__)
- flags |= SDL_INIT_EVENTTHREAD; /* Not supported on Windows or Mac OS X */
-#endif
+ flags &= ~SDL_INIT_VIDEO;
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);
}
- if (!display_disable) {
- const SDL_VideoInfo *vi = SDL_GetVideoInfo();
- fs_screen_width = vi->current_w;
- fs_screen_height = vi->current_h;
- }
-
- SDL_EventState(SDL_ACTIVEEVENT, SDL_IGNORE);
SDL_EventState(SDL_SYSWMEVENT, SDL_IGNORE);
SDL_EventState(SDL_USEREVENT, SDL_IGNORE);
- SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
-
if (av_lockmgr_register(lockmgr)) {
av_log(NULL, AV_LOG_FATAL, "Could not initialize lock manager!\n");
do_exit(NULL);
--
2.6.4 (Apple Git-63)
From d90a24a03adc775a2902da50431f9f39c89357ba Mon Sep 17 00:00:00 2001
From: Josh de Kock <josh@itanimul.li>
Date: Mon, 16 May 2016 02:30:17 +0100
Subject: [PATCH 2/2] avdev: add sdl2 device
---
configure | 2 +
libavdevice/Makefile | 1 +
libavdevice/alldevices.c | 1 +
libavdevice/sdl2.c | 458 +++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 462 insertions(+)
create mode 100644 libavdevice/sdl2.c
diff --git a/configure b/configure
index f30f536..3cd7aa7 100755
--- a/configure
+++ b/configure
@@ -1534,6 +1534,7 @@ EXTERNAL_LIBRARY_LIST="
openssl
schannel
sdl
+ sdl2
securetransport
videotoolbox
x11grab
@@ -2889,6 +2890,7 @@ pulse_outdev_deps="libpulse"
qtkit_indev_extralibs="-framework QTKit -framework Foundation -framework QuartzCore"
qtkit_indev_select="qtkit"
sdl_outdev_deps="sdl"
+sdl2_outdev_deps="sdl2"
sndio_indev_deps="sndio_h"
sndio_outdev_deps="sndio_h"
v4l_indev_deps="linux_videodev_h"
diff --git a/libavdevice/Makefile b/libavdevice/Makefile
index 585827b..1c4b4d3 100644
--- a/libavdevice/Makefile
+++ b/libavdevice/Makefile
@@ -41,6 +41,7 @@ OBJS-$(CONFIG_PULSE_OUTDEV) += pulse_audio_enc.o \
pulse_audio_common.o
OBJS-$(CONFIG_QTKIT_INDEV) += qtkit.o
OBJS-$(CONFIG_SDL_OUTDEV) += sdl.o
+OBJS-$(CONFIG_SDL2_OUTDEV) += sdl2.o
OBJS-$(CONFIG_SNDIO_INDEV) += sndio_dec.o sndio.o
OBJS-$(CONFIG_SNDIO_OUTDEV) += sndio_enc.o sndio.o
OBJS-$(CONFIG_V4L2_INDEV) += v4l2.o v4l2-common.o timefilter.o
diff --git a/libavdevice/alldevices.c b/libavdevice/alldevices.c
index 26aecf2..c0a9d9a 100644
--- a/libavdevice/alldevices.c
+++ b/libavdevice/alldevices.c
@@ -64,6 +64,7 @@ void avdevice_register_all(void)
REGISTER_INOUTDEV(PULSE, pulse);
REGISTER_INDEV (QTKIT, qtkit);
REGISTER_OUTDEV (SDL, sdl);
+ REGISTER_OUTDEV (SDL2, sdl2);
REGISTER_INOUTDEV(SNDIO, sndio);
REGISTER_INOUTDEV(V4L2, v4l2);
// REGISTER_INDEV (V4L, v4l
diff --git a/libavdevice/sdl2.c b/libavdevice/sdl2.c
new file mode 100644
index 0000000..8c3d49e
--- /dev/null
+++ b/libavdevice/sdl2.c
@@ -0,0 +1,458 @@
+/*
+ * Copyright (c) 2011 Stefano Sabatini
+ * Copyright (c) 2016 Josh de Kock
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * @file
+ * libSDL output device
+ */
+
+#include <SDL.h>
+#include <SDL_thread.h>
+
+#include "libavutil/avstring.h"
+#include "libavutil/imgutils.h"
+#include "libavutil/opt.h"
+#include "libavutil/parseutils.h"
+#include "libavutil/pixdesc.h"
+#include "libavutil/time.h"
+#include "avdevice.h"
+
+typedef struct {
+ AVClass *class;
+ SDL_Window *window;
+ SDL_Renderer *renderer;
+ char *window_title;
+ int window_width, window_height; /**< size of the window */
+ int window_fullscreen;
+ int window_borderless;
+
+ SDL_Texture *texture;
+ int texture_fmt;
+ SDL_Rect texture_rect;
+
+ int sdl_was_already_inited;
+ SDL_Thread *event_thread;
+ SDL_mutex *mutex;
+ SDL_cond *condition;
+ int init_ret; /* return code used to signal initialization errors */
+ int inited;
+ int quit;
+ int pkt_sent;
+} SDLContext;
+
+static const struct sdl_texture_pix_fmt_entry {
+ enum AVPixelFormat pix_fmt; int texture_fmt;
+} sdl_texture_pix_fmt_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 },
+ /* Not implemented in FFmpeg.
+ { AV_PIX_FMT_ARGB4444, SDL_PIXELFORMAT_ARGB4444 },
+ { AV_PIX_FMT_RGBA4444, SDL_PIXELFORMAT_RGBA4444 },
+ { AV_PIX_FMT_ABGR4444, SDL_PIXELFORMAT_ABGR4444 },
+ { AV_PIX_FMT_BGRA4444, SDL_PIXELFORMAT_BGRA4444 },
+ { AV_PIX_FMT_ARGB1555, SDL_PIXELFORMAT_ARGB1555 },
+ { AV_PIX_FMT_RGBA5551, SDL_PIXELFORMAT_RGBA5551 },
+ { AV_PIX_FMT_ABGR1555, SDL_PIXELFORMAT_ABGR1555 },
+ { AV_PIX_FMT_BGRA5551, SDL_PIXELFORMAT_BGRA5551 },
+ */
+ { 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_RGB24, SDL_PIXELFORMAT_RGB888 },
+ { AV_PIX_FMT_RGB0, SDL_PIXELFORMAT_RGBX8888 },
+ { AV_PIX_FMT_BGR24, SDL_PIXELFORMAT_BGR888 },
+ { AV_PIX_FMT_BGR0, SDL_PIXELFORMAT_BGRX8888 },
+ { AV_PIX_FMT_ARGB, SDL_PIXELFORMAT_ARGB8888 },
+ { AV_PIX_FMT_RGBA, SDL_PIXELFORMAT_RGBA8888 },
+ { AV_PIX_FMT_ABGR, SDL_PIXELFORMAT_ABGR8888 },
+ { AV_PIX_FMT_BGRA, SDL_PIXELFORMAT_BGRA8888 },
+ /* Not implemented
+ { AV_PIX_FMT_ARGB2101010, SDL_PIXELFORMAT_ARGB2101010 },
+ */
+ { AV_PIX_FMT_YUV420P, SDL_PIXELFORMAT_IYUV },
+ { AV_PIX_FMT_YUYV422, SDL_PIXELFORMAT_YUY2 },
+ { AV_PIX_FMT_UYVY422, SDL_PIXELFORMAT_UYVY },
+ { AV_PIX_FMT_NONE, 0 },
+};
+
+#define SDL_BASE_FLAGS (SDL_SWSURFACE|SDL_WINDOW_RESIZABLE)
+
+#define FF_PACKET_EVENT (SDL_USEREVENT)
+
+static void compute_texture_rect(AVFormatContext *s)
+{
+ AVRational sar, dar; /* sample and display aspect ratios */
+ SDLContext *sdl = s->priv_data;
+ AVStream *st = s->streams[0];
+ AVCodecParameters *par = s->streams[0]->codecpar;
+ SDL_Rect *texture_rect = &sdl->texture_rect;
+
+ /* compute texture width and height from the codec context information */
+ sar = st->sample_aspect_ratio.num ? st->sample_aspect_ratio : (AVRational){ 1, 1 };
+ dar = av_mul_q(sar, (AVRational){ par->width, par->height });
+
+ /* we suppose the screen has a 1/1 sample aspect ratio */
+ if (sdl->window_width && sdl->window_height) {
+ /* fit in the window */
+ if (av_cmp_q(dar, (AVRational){ sdl->window_width, sdl->window_height }) > 0) {
+ /* fit in width */
+ texture_rect->w = sdl->window_width;
+ texture_rect->h = av_rescale(texture_rect->w, dar.den, dar.num);
+ } else {
+ /* fit in height */
+ texture_rect->h = sdl->window_height;
+ texture_rect->w = av_rescale(texture_rect->h, dar.num, dar.den);
+ }
+ } else {
+ if (sar.num > sar.den) {
+ texture_rect->w = par->width;
+ texture_rect->h = av_rescale(texture_rect->w, dar.den, dar.num);
+ } else {
+ texture_rect->h = par->height;
+ texture_rect->w = av_rescale(texture_rect->h, dar.num, dar.den);
+ }
+ sdl->window_width = texture_rect->w;
+ sdl->window_height = texture_rect->h;
+ }
+
+ texture_rect->x = (sdl->window_width - texture_rect->w) / 2;
+ texture_rect->y = (sdl->window_height - texture_rect->h) / 2;
+}
+
+static int sdl2_write_trailer(AVFormatContext *s)
+{
+ SDLContext *sdl = s->priv_data;
+ sdl->quit = 1;
+
+ if (sdl->event_thread)
+ SDL_WaitThread(sdl->event_thread, NULL);
+ sdl->event_thread = NULL;
+ if (sdl->mutex)
+ SDL_DestroyMutex(sdl->mutex);
+ sdl->mutex = NULL;
+ if (sdl->condition)
+ SDL_DestroyCond(sdl->condition);
+ sdl->condition = NULL;
+
+ return 0;
+}
+
+static int event_thread(void *arg)
+{
+ AVFormatContext *s = arg;
+ SDLContext *sdl = s->priv_data;
+ AVCodecParameters *par = s->streams[0]->codecpar;
+
+ int flags = SDL_BASE_FLAGS | (sdl->window_fullscreen ? SDL_WINDOW_FULLSCREEN : 0) |
+ (sdl->window_borderless ? SDL_WINDOW_BORDERLESS : 0);
+
+ /* Initialization */
+ if (!sdl->inited){
+ if (SDL_Init(SDL_INIT_VIDEO) != 0) {
+ av_log(s, AV_LOG_ERROR, "Unable to initialize SDL: %s\n", SDL_GetError());
+ sdl->init_ret = AVERROR(EINVAL);
+ goto init_end;
+ }
+ }
+
+ compute_texture_rect(s);
+
+ if (SDL_CreateWindowAndRenderer(sdl->window_width, sdl->window_height,
+ flags, &sdl->window, &sdl->renderer) != 0){
+ av_log(sdl, AV_LOG_ERROR, "Couldn't create window and renderer: %s\n", SDL_GetError());
+ sdl->init_ret = AVERROR(EINVAL);
+ goto init_end;
+ }
+
+ SDL_SetWindowTitle(sdl->window, sdl->window_title);
+
+ sdl->texture = SDL_CreateTexture(sdl->renderer, sdl->texture_fmt, SDL_TEXTUREACCESS_STREAMING,
+ sdl->window_width, sdl->window_height);
+
+ if (!sdl->texture) {
+ av_log(sdl, AV_LOG_ERROR, "Unable to set create mode: %s\n", SDL_GetError());
+ sdl->init_ret = AVERROR(EINVAL);
+ goto init_end;
+ }
+
+ av_log(s, AV_LOG_VERBOSE, "w:%d h:%d fmt:%s -> w:%d h:%d\n",
+ par->width, par->height, av_get_pix_fmt_name(par->format),
+ sdl->window_width, sdl->window_height);
+
+init_end:
+
+ SDL_LockMutex(sdl->mutex);
+ sdl->inited = 1;
+ sdl->pkt_sent = 0;
+ SDL_UnlockMutex(sdl->mutex);
+ SDL_CondSignal(sdl->condition);
+
+ if (sdl->init_ret < 0)
+ return sdl->init_ret;
+
+ /* Main event loop. */
+ while (!sdl->quit) {
+ SDL_Event event;
+ if (!SDL_WaitEvent(&event)) {
+ av_log(s, AV_LOG_ERROR, "Error when getting SDL event: %s\n", SDL_GetError());
+ continue;
+ }
+ switch (event.type) {
+ case SDL_KEYDOWN:
+ switch (event.key.keysym.sym) {
+ case SDLK_ESCAPE:
+ case SDLK_q:
+ sdl->quit = 1;
+ break;
+ default:
+ break;
+ }
+ break;
+ case SDL_QUIT:
+ sdl->quit = 1;
+ break;
+ case SDL_WINDOWEVENT:
+ switch(event.window.event){
+ case SDL_WINDOWEVENT_RESIZED:
+ case SDL_WINDOWEVENT_SIZE_CHANGED:
+ sdl->window_width = event.window.data1;
+ sdl->window_height = event.window.data2;
+ SDL_LockMutex(sdl->mutex);
+ compute_texture_rect(s);
+ SDL_UnlockMutex(sdl->mutex);
+ break;
+ default:
+ break;
+ }
+ break;
+ case FF_PACKET_EVENT:
+ {
+ AVPacket *pkt = event.user.data1;
+ sdl->pkt_sent = 1;
+ SDL_CondSignal(sdl->condition);
+
+ uint8_t *data[4];
+ int linesize[4];
+ av_image_fill_arrays(data, linesize, pkt->data, par->format, par->width, par->height, 1);
+ switch (sdl->texture_fmt) {
+ case SDL_PIXELFORMAT_IYUV:
+ case SDL_PIXELFORMAT_YUY2:
+ case SDL_PIXELFORMAT_UYVY:
+ SDL_UpdateYUVTexture(sdl->texture, NULL,
+ data[0], linesize[0],
+ data[1], linesize[1],
+ data[2], linesize[2]);
+ break;
+ case SDL_PIXELFORMAT_RGB332:
+ case SDL_PIXELFORMAT_RGB444:
+ case SDL_PIXELFORMAT_RGB555:
+ case SDL_PIXELFORMAT_BGR555:
+ /* Not implemented in FFmpeg.
+ case SDL_PIXELFORMAT_ARGB4444:
+ case SDL_PIXELFORMAT_RGBA4444:
+ case SDL_PIXELFORMAT_ABGR4444:
+ case SDL_PIXELFORMAT_BGRA4444:
+ case SDL_PIXELFORMAT_ARGB1555:
+ case SDL_PIXELFORMAT_RGBA5551:
+ case SDL_PIXELFORMAT_ABGR1555:
+ case SDL_PIXELFORMAT_BGRA5551:
+ */
+ case SDL_PIXELFORMAT_RGB565:
+ case SDL_PIXELFORMAT_BGR565:
+ case SDL_PIXELFORMAT_RGB24:
+ case SDL_PIXELFORMAT_BGR24:
+ case SDL_PIXELFORMAT_RGB888:
+ case SDL_PIXELFORMAT_RGBX8888:
+ case SDL_PIXELFORMAT_BGR888:
+ case SDL_PIXELFORMAT_BGRX8888:
+ case SDL_PIXELFORMAT_ARGB8888:
+ case SDL_PIXELFORMAT_RGBA8888:
+ case SDL_PIXELFORMAT_ABGR8888:
+ case SDL_PIXELFORMAT_BGRA8888:
+ /* Not implemented.
+ case SDL_PIXELFORMAT_ARGB2101010:
+ */
+ SDL_UpdateTexture(sdl->texture, NULL, data[0], linesize[0]);
+ break;
+ default:
+ av_log(NULL, AV_LOG_FATAL, "Unsupported pixel format\n");
+ sdl->quit = 1;
+ break;
+ }
+ SDL_RenderClear(sdl->renderer);
+ SDL_RenderCopy(sdl->renderer, sdl->texture, NULL, &sdl->texture_rect);
+ SDL_RenderPresent(sdl->renderer);
+ av_packet_free(&pkt);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ if (sdl->texture)
+ SDL_DestroyTexture(sdl->texture);
+ sdl->texture = NULL;
+
+ if (sdl->renderer)
+ SDL_DestroyRenderer(sdl->renderer);
+ sdl->renderer = NULL;
+
+ if (sdl->window)
+ SDL_DestroyWindow(sdl->window);
+ sdl->window = NULL;
+
+ if (!sdl->inited)
+ SDL_Quit();
+
+ return sdl->init_ret;
+}
+
+static int sdl2_write_header(AVFormatContext *s)
+{
+ SDLContext *sdl = s->priv_data;
+ AVCodecParameters *par = s->streams[0]->codecpar;
+ int i, ret = 0;
+
+ if (!sdl->window_title)
+ sdl->window_title = av_strdup(s->filename);
+
+ if (SDL_WasInit(SDL_INIT_VIDEO)) {
+ av_log(s, AV_LOG_WARNING,
+ "SDL video subsystem was already inited, you could have multiple SDL outputs. This may cause unknown behaviour.\n");
+ sdl->init_ret = AVERROR(EINVAL);
+ sdl->inited = 1;
+ }
+
+ if ( s->nb_streams > 1
+ || par->codec_type != AVMEDIA_TYPE_VIDEO
+ || par->codec_id != AV_CODEC_ID_RAWVIDEO) {
+ av_log(s, AV_LOG_ERROR, "Only supports one rawvideo stream\n");
+ sdl->init_ret = AVERROR(EINVAL);
+ goto fail;
+ }
+
+ for (i = 0; sdl_texture_pix_fmt_map[i].pix_fmt != AV_PIX_FMT_NONE; i++) {
+ if (sdl_texture_pix_fmt_map[i].pix_fmt == par->format) {
+ sdl->texture_fmt = sdl_texture_pix_fmt_map[i].texture_fmt;
+ break;
+ }
+ }
+
+ if (!sdl->texture_fmt) {
+ av_log(s, AV_LOG_ERROR,
+ "Unsupported pixel format '%s'\n",
+ av_get_pix_fmt_name(par->format));
+ sdl->init_ret = AVERROR(EINVAL);
+ goto fail;
+ }
+
+ sdl->condition = SDL_CreateCond();
+ if (!sdl->condition) {
+ av_log(s, AV_LOG_ERROR, "Could not create SDL condition variable: %s\n", SDL_GetError());
+ ret = AVERROR_EXTERNAL;
+ goto fail;
+ }
+ sdl->mutex = SDL_CreateMutex();
+ if (!sdl->mutex) {
+ av_log(s, AV_LOG_ERROR, "Could not create SDL mutex: %s\n", SDL_GetError());
+ ret = AVERROR_EXTERNAL;
+ goto fail;
+ }
+ sdl->event_thread = SDL_CreateThread(event_thread, "event_thread", s);
+ if (!sdl->event_thread) {
+ av_log(s, AV_LOG_ERROR, "Could not create SDL event thread: %s\n", SDL_GetError());
+ ret = AVERROR_EXTERNAL;
+ goto fail;
+ }
+
+ /* Wait until the video system has been initiated. */
+ SDL_LockMutex(sdl->mutex);
+ while (!sdl->inited) {
+ SDL_CondWait(sdl->condition, sdl->mutex);
+ }
+
+ SDL_UnlockMutex(sdl->mutex);
+ if (sdl->init_ret < 0) {
+ ret = sdl->init_ret;
+ goto fail;
+ }
+ return 0;
+
+fail:
+ sdl2_write_trailer(s);
+ return ret;
+}
+
+static int sdl2_write_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ SDLContext *sdl = s->priv_data;
+ if(sdl->quit)
+ return sdl2_write_trailer(s);
+
+ AVPacket *ref_pkt = av_packet_alloc();
+ av_packet_ref(ref_pkt, pkt);
+ SDL_Event event;
+ event.type = FF_PACKET_EVENT;
+ event.user.data1 = ref_pkt;
+ SDL_PushEvent(&event);
+
+ SDL_LockMutex(sdl->mutex);
+ while(!sdl->pkt_sent)
+ SDL_CondWait(sdl->condition, sdl->mutex);
+ sdl->pkt_sent = 0;
+ SDL_UnlockMutex(sdl->mutex);
+ return 0;
+}
+
+#define OFFSET(x) offsetof(SDLContext,x)
+
+static const AVOption options[] = {
+ { "window_title", "set SDL window title", OFFSET(window_title), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, AV_OPT_FLAG_ENCODING_PARAM },
+ { "window_size", "set SDL window forced size", OFFSET(window_width), AV_OPT_TYPE_IMAGE_SIZE, { .str = NULL }, 0, 0, AV_OPT_FLAG_ENCODING_PARAM },
+ { "window_fullscreen", "set SDL window fullscreen", OFFSET(window_fullscreen), AV_OPT_TYPE_INT, { .i64 = 0 }, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
+ { "window_borderless", "set SDL window border off", OFFSET(window_fullscreen), AV_OPT_TYPE_INT, { .i64 = 0 }, INT_MIN, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
+ { NULL },
+};
+
+static const AVClass sdl2_class = {
+ .class_name = "sdl2 outdev",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+ .category = AV_CLASS_CATEGORY_DEVICE_VIDEO_OUTPUT,
+};
+
+AVOutputFormat ff_sdl2_muxer = {
+ .name = "sdl2",
+ .long_name = NULL_IF_CONFIG_SMALL("SDL2 output device"),
+ .priv_data_size = sizeof(SDLContext),
+ .audio_codec = AV_CODEC_ID_NONE,
+ .video_codec = AV_CODEC_ID_RAWVIDEO,
+ .write_header = sdl2_write_header,
+ .write_packet = sdl2_write_packet,
+ .write_trailer = sdl2_write_trailer,
+ .flags = AVFMT_NOFILE | AVFMT_VARIABLE_FPS | AVFMT_NOTIMESTAMPS,
+ .priv_class = &sdl2_class,
+};
\ No newline at end of file
--
2.6.4 (Apple Git-63)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment