X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=ffplay.c;h=79dc76889e00dfdb2746723c3fded1a107d5f4b2;hb=f53c26c694c94bb93fb49d72b6439b792b125fe8;hp=b0702ebeb0fa36f7a8e57a945874defbb8fd53fa;hpb=ecf65c30cf7545c72c0e27b32283fc9c55b03544;p=ffmpeg diff --git a/ffplay.c b/ffplay.c index b0702ebeb0f..79dc76889e0 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 { @@ -228,9 +230,6 @@ typedef struct VideoState { Decoder viddec; Decoder subdec; - int viddec_width; - int viddec_height; - int audio_stream; int av_sync_type; @@ -272,6 +271,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 +285,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 +311,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 +358,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) @@ -591,9 +588,7 @@ static int decoder_decode_frame(Decoder *d, AVFrame *frame, AVSubtitle *sub) { if (got_frame) { if (decoder_reorder_pts == -1) { frame->pts = av_frame_get_best_effort_timestamp(frame); - } else if (decoder_reorder_pts) { - frame->pts = frame->pkt_pts; - } else { + } else if (!decoder_reorder_pts) { frame->pts = frame->pkt_dts; } } @@ -603,9 +598,7 @@ static int decoder_decode_frame(Decoder *d, AVFrame *frame, AVSubtitle *sub) { if (got_frame) { AVRational tb = (AVRational){1, frame->sample_rate}; if (frame->pts != AV_NOPTS_VALUE) - frame->pts = av_rescale_q(frame->pts, d->avctx->time_base, tb); - else if (frame->pkt_pts != AV_NOPTS_VALUE) - frame->pts = av_rescale_q(frame->pkt_pts, av_codec_get_pkt_timebase(d->avctx), tb); + frame->pts = av_rescale_q(frame->pts, av_codec_get_pkt_timebase(d->avctx), tb); else if (d->next_pts != AV_NOPTS_VALUE) frame->pts = av_rescale_q(d->next_pts, d->next_pts_tb, tb); if (frame->pts != AV_NOPTS_VALUE) { @@ -650,12 +643,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 +785,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 +857,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 +903,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 +980,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 +1030,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 +1048,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 +1078,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 +1095,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 +1201,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 +1216,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 +1246,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 +1295,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 +1589,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 +1668,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 +1695,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 +1708,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 +1734,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 +1747,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; @@ -1820,9 +1773,6 @@ static int get_video_frame(VideoState *is, AVFrame *frame) frame->sample_aspect_ratio = av_guess_sample_aspect_ratio(is->ic, is->video_st, frame); - is->viddec_width = frame->width; - is->viddec_height = frame->height; - if (framedrop>0 || (framedrop && get_master_sync_type(is) != AV_SYNC_VIDEO_MASTER)) { if (frame->pts != AV_NOPTS_VALUE) { double diff = dpts - get_master_clock(is); @@ -1887,7 +1837,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 +1900,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 +2094,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); @@ -2271,7 +2217,6 @@ static int subtitle_thread(void *arg) Frame *sp; int got_subtitle; double pts; - int i; for (;;) { if (!(sp = frame_queue_peek_writable(&is->subpq))) @@ -2287,42 +2232,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 = is->subdec.avctx->width; + sp->height = is->subdec.avctx->height; + sp->uploaded = 0; /* now we can update the picture count */ frame_queue_push(&is->subpq); @@ -2765,9 +2677,6 @@ static int stream_component_open(VideoState *is, int stream_index) is->video_stream = stream_index; is->video_st = ic->streams[stream_index]; - is->viddec_width = avctx->width; - is->viddec_height = avctx->height; - decoder_init(&is->viddec, avctx, &is->videoq, is->continue_read_thread); if ((ret = decoder_start(&is->viddec, video_thread, is)) < 0) goto out; @@ -2936,7 +2845,7 @@ static int read_thread(void *arg) AVStream *st = ic->streams[i]; enum AVMediaType type = st->codecpar->codec_type; st->discard = AVDISCARD_ALL; - if (wanted_stream_spec[type] && st_index[type] == -1) + if (type >= 0 && wanted_stream_spec[type] && st_index[type] == -1) if (avformat_match_stream_specifier(ic, st, wanted_stream_spec[type]) > 0) st_index[type] = i; } @@ -3185,7 +3094,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 +3185,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 +3204,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 +3368,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 +3423,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 +3671,8 @@ 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(); av_log_set_flags(AV_LOG_SKIP_REPEATED); parse_loglevel(argc, argv, options); @@ -3816,31 +3714,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);