summaryrefslogtreecommitdiffstats
path: root/video
diff options
context:
space:
mode:
Diffstat (limited to 'video')
-rw-r--r--video/decode/vd_lavc.c10
-rw-r--r--video/filter/vf.c7
-rw-r--r--video/filter/vf.h1
-rw-r--r--video/filter/vf_rotate.c11
-rw-r--r--video/out/aspect.c10
-rw-r--r--video/out/opengl/context.h1
-rw-r--r--video/out/opengl/context_angle.c9
-rw-r--r--video/out/vo.c51
-rw-r--r--video/out/vo.h3
-rw-r--r--video/out/vo_opengl.c5
-rw-r--r--video/out/w32_common.c152
-rw-r--r--video/out/x11_common.c14
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;