diff options
Diffstat (limited to 'video')
-rw-r--r-- | video/decode/vd_lavc.c | 10 | ||||
-rw-r--r-- | video/filter/vf.c | 7 | ||||
-rw-r--r-- | video/filter/vf.h | 1 | ||||
-rw-r--r-- | video/filter/vf_rotate.c | 11 | ||||
-rw-r--r-- | video/out/aspect.c | 10 | ||||
-rw-r--r-- | video/out/opengl/context.h | 1 | ||||
-rw-r--r-- | video/out/opengl/context_angle.c | 9 | ||||
-rw-r--r-- | video/out/vo.c | 51 | ||||
-rw-r--r-- | video/out/vo.h | 3 | ||||
-rw-r--r-- | video/out/vo_opengl.c | 5 | ||||
-rw-r--r-- | video/out/w32_common.c | 152 | ||||
-rw-r--r-- | video/out/x11_common.c | 14 |
12 files changed, 205 insertions, 69 deletions
diff --git a/video/decode/vd_lavc.c b/video/decode/vd_lavc.c index eb63e58e92..8cc5d4eca4 100644 --- a/video/decode/vd_lavc.c +++ b/video/decode/vd_lavc.c @@ -449,10 +449,7 @@ static void init_avctx(struct dec_video *vd, const char *decoder, if (!lavc_codec) return; - ctx->codec_timebase = (AVRational){0}; - if (strstr(decoder, "_mmal") || strstr(decoder, "_mediacodec")) - ctx->codec_timebase = (AVRational){1, 1000000}; - + ctx->codec_timebase = mp_get_codec_timebase(vd->codec); ctx->pix_fmt = AV_PIX_FMT_NONE; ctx->hwdec = hwdec; ctx->hwdec_fmt = 0; @@ -463,9 +460,7 @@ static void init_avctx(struct dec_video *vd, const char *decoder, avctx->opaque = vd; avctx->codec_type = AVMEDIA_TYPE_VIDEO; avctx->codec_id = lavc_codec->id; - - if (ctx->codec_timebase.num) - avctx->time_base = ctx->codec_timebase; + avctx->time_base = ctx->codec_timebase; avctx->refcounted_frames = 1; ctx->pic = av_frame_alloc(); @@ -828,7 +823,6 @@ static void decode(struct dec_video *vd, struct demux_packet *packet, struct demux_packet *cc = new_demux_packet_from(sd->data, sd->size); cc->pts = vd->codec_pts; cc->dts = vd->codec_dts; - cc->pos = -1; demuxer_feed_caption(vd->header, cc); } diff --git a/video/filter/vf.c b/video/filter/vf.c index 274ca945a2..b632314426 100644 --- a/video/filter/vf.c +++ b/video/filter/vf.c @@ -458,6 +458,13 @@ struct mp_image *vf_read_output_frame(struct vf_chain *c) return vf_dequeue_output_frame(c->last); } +// Undo the previous vf_read_output_frame(). +void vf_unread_output_frame(struct vf_chain *c, struct mp_image *img) +{ + struct vf_instance *vf = c->last; + MP_TARRAY_INSERT_AT(vf, vf->out_queued, vf->num_out_queued, 0, img); +} + // Some filters (vf_vapoursynth) filter on separate threads, and may need new // input from the decoder, even though the core does not need a new output image // yet (this is required to get proper pipelining in the filter). If the filter diff --git a/video/filter/vf.h b/video/filter/vf.h index 49296fb9b2..901ccead95 100644 --- a/video/filter/vf.h +++ b/video/filter/vf.h @@ -157,6 +157,7 @@ int vf_filter_frame(struct vf_chain *c, struct mp_image *img); int vf_output_frame(struct vf_chain *c, bool eof); int vf_needs_input(struct vf_chain *c); struct mp_image *vf_read_output_frame(struct vf_chain *c); +void vf_unread_output_frame(struct vf_chain *c, struct mp_image *img); void vf_seek_reset(struct vf_chain *c); struct vf_instance *vf_append_filter(struct vf_chain *c, const char *name, char **args); diff --git a/video/filter/vf_rotate.c b/video/filter/vf_rotate.c index 60e52a00bd..dcaba53c6a 100644 --- a/video/filter/vf_rotate.c +++ b/video/filter/vf_rotate.c @@ -18,6 +18,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <math.h> #include <inttypes.h> #include "common/msg.h" @@ -46,11 +47,17 @@ static int lavfi_reconfig(struct vf_instance *vf, struct vf_priv_s *p = vf_lw_old_priv(vf); if (p->angle == 4) { // "auto" int r = in->rotate; - if (r < 0 || r >= 360 || (r % 90) != 0) { + if (r < 0 || r >= 360) { MP_ERR(vf, "Can't apply rotation of %d degrees.\n", r); return -1; } - vf_lw_update_graph(vf, NULL, "%s", rot[(r / 90) % 360]); + if (r % 90) { + double a = r / 180.0 * M_PI; + vf_lw_update_graph(vf, NULL, "rotate=%f:ow=rotw(%f):oh=roth(%f)", + a, a, a); + } else { + vf_lw_update_graph(vf, NULL, "%s", rot[(r / 90) % 360]); + } out->rotate = 0; } return 0; diff --git a/video/out/aspect.c b/video/out/aspect.c index 205ee3b89f..5cd5b32573 100644 --- a/video/out/aspect.c +++ b/video/out/aspect.c @@ -27,10 +27,12 @@ #include "sub/osd.h" static void aspect_calc_panscan(struct mp_vo_opts *opts, - int w, int h, int d_w, int d_h, bool unscaled, + int w, int h, int d_w, int d_h, int unscaled, int window_w, int window_h, double monitor_par, int *out_w, int *out_h) { + w *= monitor_par; + int fwidth = window_w; int fheight = (float)window_w / d_w * d_h / monitor_par; if (fheight > window_h || fheight < h) { @@ -51,9 +53,11 @@ static void aspect_calc_panscan(struct mp_vo_opts *opts, } if (unscaled) { - fwidth = w * monitor_par; - fheight = h; vo_panscan_area = 0; + if (unscaled != 2 || (w <= window_w && h <= window_h)) { + fwidth = w; + fheight = h; + } } *out_w = fwidth + vo_panscan_area * opts->panscan * f_w; diff --git a/video/out/opengl/context.h b/video/out/opengl/context.h index a546e00be0..505c51168a 100644 --- a/video/out/opengl/context.h +++ b/video/out/opengl/context.h @@ -32,6 +32,7 @@ enum { VOFLAG_GL_DEBUG = 1 << 2, // Hint to request debug OpenGL context VOFLAG_ALPHA = 1 << 3, // Hint to request alpha framebuffer VOFLAG_SW = 1 << 4, // Hint to accept a software GL renderer + VOFLAG_ANGLE_DCOMP = 1 << 5, // Whether DirectComposition is allowed }; extern const int mpgl_preferred_gl_versions[]; diff --git a/video/out/opengl/context_angle.c b/video/out/opengl/context_angle.c index ebc803fdb1..b0d637cebd 100644 --- a/video/out/opengl/context_angle.c +++ b/video/out/opengl/context_angle.c @@ -306,8 +306,13 @@ static int angle_init(struct MPGLContext *ctx, int flags) // EGL_DIRECT_COMPOSITION_ANGLE enables the use of flip-mode present, which // avoids a copy of the video image and lowers vsync jitter, though the - // extension is only present on Windows 8 and up. - if (strstr(exts, "EGL_ANGLE_direct_composition")) { + // extension is only present on Windows 8 and up, and might have subpar + // behavior with some drivers (Intel? symptom - whole desktop is black for + // some seconds after spending some minutes in fullscreen and then leaving + // fullscreen). + if ((flags & VOFLAG_ANGLE_DCOMP) && + strstr(exts, "EGL_ANGLE_direct_composition")) + { MP_TARRAY_APPEND(NULL, window_attribs, window_attribs_len, EGL_DIRECT_COMPOSITION_ANGLE); MP_TARRAY_APPEND(NULL, window_attribs, window_attribs_len, EGL_TRUE); diff --git a/video/out/vo.c b/video/out/vo.c index aa92d349e5..139b840c23 100644 --- a/video/out/vo.c +++ b/video/out/vo.c @@ -297,12 +297,17 @@ autoprobe: return NULL; } +static void terminate_vo(void *p) +{ + struct vo *vo = p; + struct vo_internal *in = vo->in; + in->terminate = true; +} + void vo_destroy(struct vo *vo) { struct vo_internal *in = vo->in; - mp_dispatch_lock(in->dispatch); - vo->in->terminate = true; - mp_dispatch_unlock(in->dispatch); + mp_dispatch_run(in->dispatch, terminate_vo, vo); pthread_join(vo->in->thread, NULL); dealloc_vo(vo); } @@ -339,9 +344,7 @@ static void check_estimated_display_fps(struct vo *vo) struct vo_internal *in = vo->in; bool use_estimated = false; - if (in->num_total_vsync_samples >= MAX_VSYNC_SAMPLES * 2 && - fabs((in->nominal_vsync_interval - in->estimated_vsync_interval)) - >= 0.01 * in->nominal_vsync_interval && + if (in->num_total_vsync_samples >= MAX_VSYNC_SAMPLES / 2 && in->estimated_vsync_interval <= 1e6 / 20.0 && in->estimated_vsync_interval >= 1e6 / 99.0) { @@ -358,12 +361,11 @@ static void check_estimated_display_fps(struct vo *vo) } if (use_estimated == (in->vsync_interval == in->nominal_vsync_interval)) { if (use_estimated) { - MP_WARN(vo, "Reported display FPS seems incorrect.\n" - "Assuming a value closer to %.3f Hz.\n", - 1e6 / in->estimated_vsync_interval); + MP_VERBOSE(vo, "adjusting display FPS to a value closer to %.3f Hz\n", + 1e6 / in->estimated_vsync_interval); } else { - MP_WARN(vo, "Switching back to assuming %.3f Hz.\n", - 1e6 / in->nominal_vsync_interval); + MP_VERBOSE(vo, "switching back to assuming display fps = %.3f Hz\n", + 1e6 / in->nominal_vsync_interval); } } in->vsync_interval = use_estimated ? (int64_t)in->estimated_vsync_interval @@ -537,20 +539,39 @@ static void run_control(void *p) { void **pp = p; struct vo *vo = pp[0]; - uint32_t request = *(int *)pp[1]; + int request = (intptr_t)pp[1]; void *data = pp[2]; int ret = vo->driver->control(vo, request, data); - *(int *)pp[3] = ret; + if (pp[3]) + *(int *)pp[3] = ret; } -int vo_control(struct vo *vo, uint32_t request, void *data) +int vo_control(struct vo *vo, int request, void *data) { int ret; - void *p[] = {vo, &request, data, &ret}; + void *p[] = {vo, (void *)(intptr_t)request, data, &ret}; mp_dispatch_run(vo->in->dispatch, run_control, p); return ret; } +// Run vo_control() without waiting for a reply. +// (Only works for some VOCTRLs.) +void vo_control_async(struct vo *vo, int request, void *data) +{ + void *p[4] = {vo, (void *)(intptr_t)request, NULL, NULL}; + void **d = talloc_memdup(NULL, p, sizeof(p)); + + switch (request) { + case VOCTRL_UPDATE_PLAYBACK_STATE: + d[2] = ta_xdup_ptrtype(d, (struct voctrl_playback_state *)data); + break; + default: + abort(); // requires explicit support + } + + mp_dispatch_enqueue_autofree(vo->in->dispatch, run_control, d); +} + // must be called locked static void forget_frames(struct vo *vo) { diff --git a/video/out/vo.h b/video/out/vo.h index a5280e5611..ed2fe94e37 100644 --- a/video/out/vo.h +++ b/video/out/vo.h @@ -341,7 +341,8 @@ struct mpv_global; struct vo *init_best_video_out(struct mpv_global *global, struct vo_extra *ex); int vo_reconfig(struct vo *vo, struct mp_image_params *p); -int vo_control(struct vo *vo, uint32_t request, void *data); +int vo_control(struct vo *vo, int request, void *data); +void vo_control_async(struct vo *vo, int request, void *data); bool vo_is_ready_for_frame(struct vo *vo, int64_t next_pts); void vo_queue_frame(struct vo *vo, struct vo_frame *frame); void vo_wait_frame(struct vo *vo); diff --git a/video/out/vo_opengl.c b/video/out/vo_opengl.c index 8a7c797ea8..0e4c117352 100644 --- a/video/out/vo_opengl.c +++ b/video/out/vo_opengl.c @@ -70,6 +70,7 @@ struct gl_priv { int allow_sw; int swap_interval; int dwm_flush; + int allow_direct_composition; int opt_vsync_fences; char *backend; @@ -409,6 +410,9 @@ static int preinit(struct vo *vo) if (p->allow_sw) vo_flags |= VOFLAG_SW; + if (p->allow_direct_composition) + vo_flags |= VOFLAG_ANGLE_DCOMP; + p->glctx = mpgl_init(vo, p->backend, vo_flags); if (!p->glctx) goto err_out; @@ -460,6 +464,7 @@ static const struct m_option options[] = { OPT_INT("swapinterval", swap_interval, 0, OPTDEF_INT(1)), OPT_CHOICE("dwmflush", dwm_flush, 0, ({"no", -1}, {"auto", 0}, {"windowed", 1}, {"yes", 2})), + OPT_FLAG("dcomposition", allow_direct_composition, 0, OPTDEF_INT(1)), OPT_FLAG("debug", use_gl_debug, 0), OPT_STRING_VALIDATE("backend", backend, 0, mpgl_validate_backend_opt), OPT_FLAG("sw", allow_sw, 0), diff --git a/video/out/w32_common.c b/video/out/w32_common.c index b1c95f78d1..dea0061b71 100644 --- a/video/out/w32_common.c +++ b/video/out/w32_common.c @@ -48,8 +48,6 @@ EXTERN_C IMAGE_DOS_HEADER __ImageBase; #define HINST_THISCOMPONENT ((HINSTANCE)&__ImageBase) -static const wchar_t classname[] = L"mpv"; - static __thread struct vo_w32_state *w32_thread_context; struct vo_w32_state { @@ -64,6 +62,8 @@ struct vo_w32_state { HWND window; HWND parent; // 0 normally, set in embedding mode + HHOOK parent_win_hook; + HWINEVENTHOOK parent_evt_hook; HMONITOR monitor; // Handle of the current screen struct mp_rect screenrc; // Size and virtual position of the current screen @@ -747,11 +747,19 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, // is that will make us lose WM_USER wakeups. mp_input_put_key(w32->input_ctx, MP_KEY_CLOSE_WIN); return 0; + case WM_NCDESTROY: // Sometimes only WM_NCDESTROY is received in --wid mode case WM_DESTROY: + if (w32->destroyed) + break; + // If terminate is not set, something else destroyed the window. This + // can also happen in --wid mode when the parent window is destroyed. + if (!w32->terminate) + mp_input_put_key(w32->input_ctx, MP_KEY_CLOSE_WIN); + RevokeDragDrop(w32->window); w32->destroyed = true; w32->window = NULL; PostQuitMessage(0); - return 0; + break; case WM_SYSCOMMAND: switch (wParam) { case SC_SCREENSAVE: @@ -901,27 +909,109 @@ static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, return DefWindowProcW(hWnd, message, wParam, lParam); } +static pthread_once_t window_class_init_once = PTHREAD_ONCE_INIT; +static ATOM window_class; +static void register_window_class(void) +{ + window_class = RegisterClassExW(&(WNDCLASSEXW) { + .cbSize = sizeof(WNDCLASSEXW), + .style = CS_HREDRAW | CS_VREDRAW, + .lpfnWndProc = WndProc, + .hInstance = HINST_THISCOMPONENT, + .hIcon = LoadIconW(HINST_THISCOMPONENT, L"IDI_ICON1"), + .hCursor = LoadCursor(NULL, IDC_ARROW), + .lpszClassName = L"mpv", + }); +} + +static ATOM get_window_class(void) +{ + pthread_once(&window_class_init_once, register_window_class); + return window_class; +} + +static void resize_child_win(HWND parent) +{ + // Check if an mpv window is a child of this window. This will not + // necessarily be the case because the hook functions will run for all + // windows on the parent window's thread. + ATOM cls = get_window_class(); + HWND child = FindWindowExW(parent, NULL, (LPWSTR)MAKEINTATOM(cls), NULL); + if (!child) + return; + // Make sure the window was created by this instance + if (GetWindowLongPtrW(child, GWLP_HINSTANCE) != (LONG_PTR)HINST_THISCOMPONENT) + return; + + // Resize the mpv window to match its parent window's size + RECT rm, rp; + if (!GetClientRect(child, &rm)) + return; + if (!GetClientRect(parent, &rp)) + return; + if (EqualRect(&rm, &rp)) + return; + SetWindowPos(child, NULL, 0, 0, rp.right, rp.bottom, SWP_ASYNCWINDOWPOS | + SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER); +} + +static LRESULT CALLBACK parent_win_hook(int nCode, WPARAM wParam, LPARAM lParam) +{ + if (nCode != HC_ACTION) + goto done; + CWPSTRUCT *cwp = (CWPSTRUCT*)lParam; + if (cwp->message != WM_WINDOWPOSCHANGED) + goto done; + resize_child_win(cwp->hwnd); +done: + return CallNextHookEx(NULL, nCode, wParam, lParam); +} + +static void CALLBACK parent_evt_hook(HWINEVENTHOOK hWinEventHook, DWORD event, + HWND hwnd, LONG idObject, LONG idChild, DWORD dwEventThread, + DWORD dwmsEventTime) +{ + if (event != EVENT_OBJECT_LOCATIONCHANGE) + return; + if (!hwnd || idObject != OBJID_WINDOW || idChild != CHILDID_SELF) + return; + resize_child_win(hwnd); +} + +static void install_parent_hook(struct vo_w32_state *w32) +{ + DWORD pid; + DWORD tid = GetWindowThreadProcessId(w32->parent, &pid); + + // If the parent lives inside the current process, install a Windows hook + if (pid == GetCurrentProcessId()) { + w32->parent_win_hook = SetWindowsHookExW(WH_CALLWNDPROC, + parent_win_hook, NULL, tid); + } else { + // Otherwise, use a WinEvent hook. These don't seem to be as smooth as + // Windows hooks, but they can be delivered across process boundaries. + w32->parent_evt_hook = SetWinEventHook( + EVENT_OBJECT_LOCATIONCHANGE, EVENT_OBJECT_LOCATIONCHANGE, + NULL, parent_evt_hook, pid, tid, WINEVENT_OUTOFCONTEXT); + } +} + +static void remove_parent_hook(struct vo_w32_state *w32) +{ + if (w32->parent_win_hook) + UnhookWindowsHookEx(w32->parent_win_hook); + if (w32->parent_evt_hook) + UnhookWinEvent(w32->parent_evt_hook); +} + // Dispatch incoming window events and handle them. // This returns only when the thread is asked to terminate. static void run_message_loop(struct vo_w32_state *w32) { MSG msg; - while (GetMessageW(&msg, 0, 0, 0) > 0) { + while (GetMessageW(&msg, 0, 0, 0) > 0) DispatchMessageW(&msg); - if (w32->parent) { - RECT r, rp; - BOOL res = GetClientRect(w32->window, &r); - res = res && GetClientRect(w32->parent, &rp); - if (res && (r.right != rp.right || r.bottom != rp.bottom)) - MoveWindow(w32->window, 0, 0, rp.right, rp.bottom, FALSE); - - // Window has probably been closed, e.g. due to parent program crash - if (!IsWindow(w32->parent)) - mp_input_put_key(w32->input_ctx, MP_KEY_CLOSE_WIN); - } - } - // Even if the message loop somehow exits, we still have to respond to // external requests until termination is requested. while (!w32->terminate) @@ -1222,33 +1312,26 @@ static void *gui_thread(void *ptr) thread_disable_ime(); - WNDCLASSEXW wcex = { - .cbSize = sizeof wcex, - .style = CS_HREDRAW | CS_VREDRAW, - .lpfnWndProc = WndProc, - .hInstance = HINST_THISCOMPONENT, - .hIcon = LoadIconW(HINST_THISCOMPONENT, L"IDI_ICON1"), - .hCursor = LoadCursor(NULL, IDC_ARROW), - .lpszClassName = classname, - }; - RegisterClassExW(&wcex); - w32_thread_context = w32; if (w32->opts->WinID >= 0) w32->parent = (HWND)(intptr_t)(w32->opts->WinID); + ATOM cls = get_window_class(); if (w32->parent) { RECT r; GetClientRect(w32->parent, &r); - w32->window = CreateWindowExW(WS_EX_NOPARENTNOTIFY, classname, - classname, + w32->window = CreateWindowExW(WS_EX_NOPARENTNOTIFY, + (LPWSTR)MAKEINTATOM(cls), L"mpv", WS_CHILD | WS_VISIBLE, 0, 0, r.right, r.bottom, w32->parent, 0, HINST_THISCOMPONENT, NULL); + + // Install a hook to get notifications when the parent changes size + if (w32->window) + install_parent_hook(w32); } else { - w32->window = CreateWindowExW(0, classname, - classname, + w32->window = CreateWindowExW(0, (LPWSTR)MAKEINTATOM(cls), L"mpv", update_style(w32, 0), CW_USEDEFAULT, SW_HIDE, 100, 100, 0, 0, HINST_THISCOMPONENT, NULL); @@ -1323,10 +1406,9 @@ done: MP_VERBOSE(w32, "uninit\n"); - if (w32->window) { - RevokeDragDrop(w32->window); + remove_parent_hook(w32); + if (w32->window && !w32->destroyed) DestroyWindow(w32->window); - } if (w32->taskbar_list) ITaskbarList2_Release(w32->taskbar_list); if (w32->taskbar_list3) diff --git a/video/out/x11_common.c b/video/out/x11_common.c index a9d6a2a632..1b780598af 100644 --- a/video/out/x11_common.c +++ b/video/out/x11_common.c @@ -1748,17 +1748,25 @@ static void vo_x11_fullscreen(struct vo *vo) x11->nofsrc = x11->winrc; } + struct mp_rect rc = x11->nofsrc; + if (x11->wm_type & vo_wm_FULLSCREEN) { x11_set_ewmh_state(x11, "_NET_WM_STATE_FULLSCREEN", x11->fs); if (!x11->fs && (x11->pos_changed_during_fs || x11->size_changed_during_fs)) { + if (x11->screenrc.x0 == rc.x0 && x11->screenrc.x1 == rc.x1 && + x11->screenrc.y0 == rc.y0 && x11->screenrc.y1 == rc.y1) + { + // Workaround for some WMs switching back to FS in this case. + MP_VERBOSE(x11, "avoiding triggering old-style fullscreen\n"); + rc.x1 -= 1; + rc.y1 -= 1; + } vo_x11_move_resize(vo, x11->pos_changed_during_fs, - x11->size_changed_during_fs, - x11->nofsrc); + x11->size_changed_during_fs, rc); } } else { - struct mp_rect rc = x11->nofsrc; if (x11->fs) { vo_x11_update_screeninfo(vo); rc = x11->screenrc; |