From patchwork Mon Sep 12 18:58:15 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Josh Dekker X-Patchwork-Id: 555 Delivered-To: ffmpegpatchwork@gmail.com Received: by 10.103.140.134 with SMTP id o128csp1763614vsd; Mon, 12 Sep 2016 11:58:46 -0700 (PDT) X-Received: by 10.194.87.227 with SMTP id bb3mr11124021wjb.62.1473706726104; Mon, 12 Sep 2016 11:58:46 -0700 (PDT) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id a18si16579160wjx.13.2016.09.12.11.58.45; Mon, 12 Sep 2016 11:58:46 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@itanimul.li; dkim=neutral (body hash did not verify) header.i=@messagingengine.com; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id AFDA1689BFD; Mon, 12 Sep 2016 21:58:30 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from out4-smtp.messagingengine.com (out4-smtp.messagingengine.com [66.111.4.28]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id C85A2689BFD for ; Mon, 12 Sep 2016 21:58:23 +0300 (EEST) Received: from compute7.internal (compute7.nyi.internal [10.202.2.47]) by mailout.nyi.internal (Postfix) with ESMTP id C017A2083C for ; Mon, 12 Sep 2016 14:58:34 -0400 (EDT) Received: from frontend2 ([10.202.2.161]) by compute7.internal (MEProxy); Mon, 12 Sep 2016 14:58:34 -0400 DKIM-Signature: v=1; a=rsa-sha1; c=relaxed/relaxed; d=itanimul.li; h= date:from:message-id:subject:to:x-sasl-enc:x-sasl-enc; s=mesmtp; bh=d5OY3XKMQ5lALMXyZXbla3szQBs=; b=QzojJDvyv6mCaIyf3uWqO0pTNoH2 5AB0YmS0/+iim3IKy2WmuojiHUU9IvHx8HHIAaw9PVxwCUue+zfCnGoD9z1n6Lpj itZ94oJ9GIayEWawenjtBG7Sy87pxUbCJokun7NtDZ9k+HW+JXYXrEkaI0rt7d2L YYODEiZpMrQsMRY= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed/relaxed; d= messagingengine.com; h=date:from:message-id:subject:to :x-sasl-enc:x-sasl-enc; s=smtpout; bh=d5OY3XKMQ5lALMXyZXbla3szQB s=; b=izyu11iEKpnBWfsvD/ZYEmgtL5S3h36ktcF9tAnxe9MSxwZ7QS/AbudW3W Zs1cbQAR/b3y+1NLZ9gn8G5fqo5cROCH54DlEWZhssMhYhBuTCIkWO8pC18J/bUJ lZDpuuIx3JihG5wVR8pb5Oj+PY7P0BIZ0T48LJt74Fetj3LWY= X-Sasl-enc: 0r4LpX7gQaLLUVUZti0lJ7yZmZCy5fehbtRYZ2LDu1ym 1473706713 Received: from localhost (cpc75394-sotn16-2-0-cust168.15-1.cable.virginm.net [82.22.8.169]) by mail.messagingengine.com (Postfix) with ESMTPA id CEFCDCCE99 for ; Mon, 12 Sep 2016 14:58:33 -0400 (EDT) From: Josh de Kock To: ffmpeg-devel@ffmpeg.org Date: Mon, 12 Sep 2016 19:58:15 +0100 Message-Id: <1473706695-77571-1-git-send-email-josh@itanimul.li> X-Mailer: git-send-email 2.7.4 (Apple Git-66) Subject: [FFmpeg-devel] [PATCH] ffplay: convert ffplay to use SDL2 X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" From: Marton Balint Depends on the other SDL patches. Signed-off-by: Josh de Kock Tested-by: James Almer (Windows, mingw-w64) --- configure | 6 +- ffplay.c | 594 ++++++++++++++++++++++++++------------------------------------ 2 files changed, 249 insertions(+), 351 deletions(-) diff --git a/configure b/configure index 6ef2410..c24784d 100755 --- a/configure +++ b/configure @@ -3138,8 +3138,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" @@ -6665,7 +6665,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 adbe9cb..e64909a 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 { @@ -152,17 +154,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 { @@ -272,6 +274,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; @@ -284,11 +288,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; @@ -313,8 +314,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; @@ -362,7 +361,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) @@ -650,12 +650,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); } @@ -798,113 +792,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; ydata[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; ydata[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) @@ -936,12 +864,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_last(&is->pictq); if (vp->bmp) { @@ -950,36 +910,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 } } } @@ -992,7 +987,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; @@ -1042,13 +1037,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; @@ -1065,25 +1055,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); @@ -1097,6 +1085,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; @@ -1111,21 +1102,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) @@ -1215,11 +1208,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); } @@ -1228,6 +1223,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 @@ -1254,42 +1253,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; } @@ -1297,12 +1302,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) @@ -1587,6 +1596,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; @@ -1652,19 +1675,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, @@ -1680,24 +1702,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; @@ -1711,17 +1715,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. */ @@ -1735,7 +1741,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); } @@ -1748,58 +1754,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; @@ -1887,7 +1847,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; @@ -1950,10 +1910,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); @@ -2148,7 +2104,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); @@ -2268,10 +2224,10 @@ static int video_thread(void *arg) static int subtitle_thread(void *arg) { VideoState *is = arg; + AVCodecParameters *codecpar = is->subtitle_st->codecpar; Frame *sp; int got_subtitle; double pts; - int i; for (;;) { if (!(sp = frame_queue_peek_writable(&is->subpq))) @@ -2287,42 +2243,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 = codecpar->width; + sp->height = codecpar->height; + sp->uploaded = 0; /* now we can update the picture count */ frame_queue_push(&is->subpq); @@ -3185,7 +3108,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: @@ -3276,27 +3199,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; } @@ -3305,7 +3218,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; @@ -3469,9 +3382,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); @@ -3527,16 +3437,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: @@ -3773,8 +3685,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"; init_dynload(); @@ -3818,31 +3728,19 @@ int main(int argc, char **argv) /* 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); + SDL_setenv("SDL_AUDIO_ALSA_SET_BUFFER_SIZE","1", 1); } 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);