diff options
Diffstat (limited to 'video')
58 files changed, 2550 insertions, 1170 deletions
diff --git a/video/decode/dec_video.c b/video/decode/dec_video.c index d9dbf0f326..23aba81709 100644 --- a/video/decode/dec_video.c +++ b/video/decode/dec_video.c @@ -33,6 +33,7 @@ #include "demux/packet.h" #include "common/codecs.h" +#include "common/recorder.h" #include "video/out/vo.h" #include "video/csputils.h" @@ -89,7 +90,6 @@ void video_uninit(struct dec_video *d_video) if (!d_video) return; mp_image_unrefp(&d_video->current_mpi); - mp_image_unrefp(&d_video->cover_art_mpi); if (d_video->vd_driver) { MP_VERBOSE(d_video, "Uninit video.\n"); d_video->vd_driver->uninit(d_video); @@ -252,36 +252,53 @@ static void fix_image_params(struct dec_video *d_video, d_video->fixed_format = p; } -static struct mp_image *decode_packet(struct dec_video *d_video, - struct demux_packet *packet, - int drop_frame) +static bool send_packet(struct dec_video *d_video, struct demux_packet *packet) { - struct MPOpts *opts = d_video->opts; - - if (!d_video->vd_driver) - return NULL; - double pkt_pts = packet ? packet->pts : MP_NOPTS_VALUE; double pkt_dts = packet ? packet->dts : MP_NOPTS_VALUE; if (pkt_pts == MP_NOPTS_VALUE) d_video->has_broken_packet_pts = 1; + bool dts_replaced = false; + if (packet && packet->dts == MP_NOPTS_VALUE && !d_video->codec->avi_dts) { + packet->dts = packet->pts; + dts_replaced = true; + } + double pkt_pdts = pkt_pts == MP_NOPTS_VALUE ? pkt_dts : pkt_pts; if (pkt_pdts != MP_NOPTS_VALUE && d_video->first_packet_pdts == MP_NOPTS_VALUE) d_video->first_packet_pdts = pkt_pdts; MP_STATS(d_video, "start decode video"); - struct mp_image *mpi = d_video->vd_driver->decode(d_video, packet, drop_frame); + bool res = d_video->vd_driver->send_packet(d_video, packet); MP_STATS(d_video, "end decode video"); - // Error, discarded frame, dropped frame, or initial codec delay. - if (!mpi || drop_frame) { - talloc_free(mpi); - return NULL; - } + // Stream recording can't deal with almost surely wrong fake DTS. + if (dts_replaced) + packet->dts = MP_NOPTS_VALUE; + + return res; +} + +static bool receive_frame(struct dec_video *d_video, struct mp_image **out_image) +{ + struct MPOpts *opts = d_video->opts; + struct mp_image *mpi = NULL; + + assert(!*out_image); + + MP_STATS(d_video, "start decode video"); + + bool progress = d_video->vd_driver->receive_frame(d_video, &mpi); + + MP_STATS(d_video, "end decode video"); + + // Error, EOF, discarded frame, dropped frame, or initial codec delay. + if (!mpi) + return progress; if (opts->field_dominance == 0) { mpi->fields |= MP_IMGFIELD_TOP_FIRST | MP_IMGFIELD_INTERLACED; @@ -353,7 +370,8 @@ static struct mp_image *decode_packet(struct dec_video *d_video, mpi->pts -= MPMAX(delay, 0) / d_video->fps; } - return mpi; + *out_image = mpi; + return true; } void video_reset_params(struct dec_video *d_video) @@ -379,24 +397,8 @@ void video_set_start(struct dec_video *d_video, double start_pts) void video_work(struct dec_video *d_video) { - if (d_video->current_mpi) - return; - - if (d_video->header->attached_picture) { - if (d_video->current_state == DATA_AGAIN && !d_video->cover_art_mpi) { - struct demux_packet *packet = - demux_copy_packet(d_video->header->attached_picture); - d_video->cover_art_mpi = decode_packet(d_video, packet, 0); - // Might need flush. - if (!d_video->cover_art_mpi) - d_video->cover_art_mpi = decode_packet(d_video, NULL, 0); - talloc_free(packet); - } - if (d_video->current_state != DATA_EOF) - d_video->current_mpi = mp_image_new_ref(d_video->cover_art_mpi); - d_video->current_state = DATA_EOF; + if (d_video->current_mpi || !d_video->vd_driver) return; - } if (!d_video->packet && !d_video->new_segment && demux_read_packet_async(d_video->header, &d_video->packet) == 0) @@ -405,20 +407,12 @@ void video_work(struct dec_video *d_video) return; } - if (d_video->packet) { - if (d_video->packet->dts == MP_NOPTS_VALUE && !d_video->codec->avi_dts) - d_video->packet->dts = d_video->packet->pts; - } - if (d_video->packet && d_video->packet->new_segment) { assert(!d_video->new_segment); d_video->new_segment = d_video->packet; d_video->packet = NULL; } - bool had_input_packet = !!d_video->packet; - bool had_packet = had_input_packet || d_video->new_segment; - double start_pts = d_video->start_pts; if (d_video->start != MP_NOPTS_VALUE && (start_pts == MP_NOPTS_VALUE || d_video->start > start_pts)) @@ -431,23 +425,29 @@ void video_work(struct dec_video *d_video) { framedrop_type = 2; } - d_video->current_mpi = decode_packet(d_video, d_video->packet, framedrop_type); - if (d_video->packet && d_video->packet->len == 0) { + + d_video->vd_driver->control(d_video, VDCTRL_SET_FRAMEDROP, &framedrop_type); + + if (send_packet(d_video, d_video->packet)) { + if (d_video->recorder_sink) + mp_recorder_feed_packet(d_video->recorder_sink, d_video->packet); + talloc_free(d_video->packet); d_video->packet = NULL; } + bool progress = receive_frame(d_video, &d_video->current_mpi); + d_video->current_state = DATA_OK; - if (!d_video->current_mpi) { + if (!progress) { d_video->current_state = DATA_EOF; - if (had_packet) { - if (framedrop_type == 1) - d_video->dropped_frames += 1; - d_video->current_state = DATA_AGAIN; - } + } else if (!d_video->current_mpi) { + if (framedrop_type == 1) + d_video->dropped_frames += 1; + d_video->current_state = DATA_AGAIN; } - bool segment_ended = !d_video->current_mpi && !had_input_packet; + bool segment_ended = d_video->current_state == DATA_EOF; if (d_video->current_mpi && d_video->current_mpi->pts != MP_NOPTS_VALUE) { double vpts = d_video->current_mpi->pts; diff --git a/video/decode/dec_video.h b/video/decode/dec_video.h index 1d2b3f087e..5ef1f9252a 100644 --- a/video/decode/dec_video.h +++ b/video/decode/dec_video.h @@ -42,6 +42,8 @@ struct dec_video { int dropped_frames; + struct mp_recorder_sink *recorder_sink; + // Internal (shared with vd_lavc.c). void *priv; // for free use by vd_driver @@ -75,7 +77,6 @@ struct dec_video { struct demux_packet *new_segment; struct demux_packet *packet; bool framedrop_enabled; - struct mp_image *cover_art_mpi; struct mp_image *current_mpi; int current_state; }; diff --git a/video/decode/cuda.c b/video/decode/hw_cuda.c index cad02b2353..92ba0772c4 100644 --- a/video/decode/cuda.c +++ b/video/decode/hw_cuda.c @@ -38,54 +38,45 @@ static int probe(struct lavc_ctx *ctx, struct vd_lavc_hwdec *hwdec, static int init(struct lavc_ctx *ctx) { - ctx->hwdec_priv = hwdec_devices_get(ctx->hwdec_devs, HWDEC_CUDA)->ctx; + ctx->hwdec_priv = hwdec_devices_get(ctx->hwdec_devs, HWDEC_CUDA); return 0; } static int init_decoder(struct lavc_ctx *ctx, int w, int h) { AVCodecContext *avctx = ctx->avctx; - AVCUDADeviceContext *device_hwctx; - AVHWDeviceContext *device_ctx; - AVHWFramesContext *hwframe_ctx; - int ret = 0; + struct mp_hwdec_ctx *hwctx = ctx->hwdec_priv; if (avctx->hw_frames_ctx) { MP_ERR(ctx, "hw_frames_ctx already initialised!\n"); return -1; } - AVBufferRef *hw_device_ctx = av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_CUDA); - if (!hw_device_ctx) { - MP_WARN(ctx, "av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_CUDA) failed\n"); - goto error; - } - - device_ctx = (AVHWDeviceContext*)hw_device_ctx->data; - - device_hwctx = device_ctx->hwctx; - device_hwctx->cuda_ctx = ctx->hwdec_priv; - - ret = av_hwdevice_ctx_init(hw_device_ctx); - if (ret < 0) { - MP_ERR(ctx, "av_hwdevice_ctx_init failed\n"); - goto error; - } - - avctx->hw_frames_ctx = av_hwframe_ctx_alloc(hw_device_ctx); + avctx->hw_frames_ctx = av_hwframe_ctx_alloc(hwctx->av_device_ref); if (!avctx->hw_frames_ctx) { MP_ERR(ctx, "av_hwframe_ctx_alloc failed\n"); goto error; } - hwframe_ctx = (AVHWFramesContext*)avctx->hw_frames_ctx->data; + AVHWFramesContext *hwframe_ctx = (void* )avctx->hw_frames_ctx->data; hwframe_ctx->format = AV_PIX_FMT_CUDA; + // This is proper use of the hw_frames_ctx API, but it does not work + // (appaears to work but fails e.g. with 10 bit). The cuvid wrapper + // does non-standard things, and it's a meesy situation. + /* + hwframe_ctx->width = w; + hwframe_ctx->height = h; + hwframe_ctx->sw_format = avctx->sw_pix_fmt; + + if (av_hwframe_ctx_init(avctx->hw_frames_ctx) < 0) + goto error; + */ + return 0; error: av_buffer_unref(&avctx->hw_frames_ctx); - av_buffer_unref(&hw_device_ctx); return -1; } diff --git a/video/decode/d3d11va.c b/video/decode/hw_d3d11va.c index e31582d37c..a69a3890bd 100644 --- a/video/decode/d3d11va.c +++ b/video/decode/hw_d3d11va.c @@ -27,7 +27,7 @@ #include "d3d.h" -#define ADDITIONAL_SURFACES (4 + HWDEC_DELAY_QUEUE_COUNT) +#define ADDITIONAL_SURFACES (HWDEC_EXTRA_SURFACES + HWDEC_DELAY_QUEUE_COUNT) struct d3d11va_decoder { ID3D11VideoDecoder *decoder; diff --git a/video/decode/dxva2.c b/video/decode/hw_dxva2.c index ab91c186df..7b1a6b4bc7 100644 --- a/video/decode/dxva2.c +++ b/video/decode/hw_dxva2.c @@ -32,7 +32,7 @@ #include "d3d.h" -#define ADDITIONAL_SURFACES (4 + HWDEC_DELAY_QUEUE_COUNT) +#define ADDITIONAL_SURFACES (HWDEC_EXTRA_SURFACES + HWDEC_DELAY_QUEUE_COUNT) struct priv { struct mp_log *log; @@ -357,44 +357,53 @@ static bool create_device(struct lavc_ctx *s) return false; } - IDirect3D9* (WINAPI *Direct3DCreate9)(UINT) = - (void *)GetProcAddress(d3d9_dll, "Direct3DCreate9"); - if (!Direct3DCreate9) { - MP_ERR(p, "Failed to locate Direct3DCreate9\n"); + HRESULT (WINAPI *Direct3DCreate9Ex)(UINT, IDirect3D9Ex **) = + (void *)GetProcAddress(d3d9_dll, "Direct3DCreate9Ex"); + if (!Direct3DCreate9Ex) { + MP_ERR(p, "Failed to locate Direct3DCreate9Ex\n"); return false; } - p->d3d9 = Direct3DCreate9(D3D_SDK_VERSION); - if (!p->d3d9) { - MP_ERR(p, "Failed to create IDirect3D object\n"); + IDirect3D9Ex *d3d9ex = NULL; + HRESULT hr = Direct3DCreate9Ex(D3D_SDK_VERSION, &d3d9ex); + if (FAILED(hr)) { + MP_ERR(p, "Failed to create IDirect3D9Ex object\n"); return false; } UINT adapter = D3DADAPTER_DEFAULT; - D3DDISPLAYMODE display_mode; - IDirect3D9_GetAdapterDisplayMode(p->d3d9, adapter, &display_mode); + D3DDISPLAYMODEEX modeex = {0}; + IDirect3D9Ex_GetAdapterDisplayModeEx(d3d9ex, adapter, &modeex, NULL); + D3DPRESENT_PARAMETERS present_params = { .Windowed = TRUE, .BackBufferWidth = 640, .BackBufferHeight = 480, .BackBufferCount = 0, - .BackBufferFormat = display_mode.Format, + .BackBufferFormat = modeex.Format, .SwapEffect = D3DSWAPEFFECT_DISCARD, .Flags = D3DPRESENTFLAG_VIDEO, }; - HRESULT hr = IDirect3D9_CreateDevice(p->d3d9, adapter, - D3DDEVTYPE_HAL, - GetShellWindow(), - D3DCREATE_SOFTWARE_VERTEXPROCESSING | - D3DCREATE_MULTITHREADED | - D3DCREATE_FPU_PRESERVE, - &present_params, - &p->device); + + IDirect3DDevice9Ex *exdev = NULL; + hr = IDirect3D9Ex_CreateDeviceEx(d3d9ex, adapter, + D3DDEVTYPE_HAL, + GetShellWindow(), + D3DCREATE_SOFTWARE_VERTEXPROCESSING | + D3DCREATE_MULTITHREADED | + D3DCREATE_FPU_PRESERVE, + &present_params, + NULL, + &exdev); if (FAILED(hr)) { MP_ERR(p, "Failed to create Direct3D device: %s\n", mp_HRESULT_to_str(hr)); + IDirect3D9_Release(d3d9ex); return false; } + + p->d3d9 = (IDirect3D9 *)d3d9ex; + p->device = (IDirect3DDevice9 *)exdev; return true; } diff --git a/video/decode/hw_vaapi.c b/video/decode/hw_vaapi.c new file mode 100644 index 0000000000..99c23f48c4 --- /dev/null +++ b/video/decode/hw_vaapi.c @@ -0,0 +1,171 @@ +/* + * This file is part of mpv. + * + * mpv is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * mpv is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with mpv. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stddef.h> +#include <assert.h> + +#include <libavcodec/avcodec.h> +#include <libavutil/common.h> +#include <libavutil/hwcontext.h> +#include <libavutil/hwcontext_vaapi.h> + +#include "config.h" + +#include "lavc.h" +#include "common/common.h" +#include "common/av_common.h" +#include "video/fmt-conversion.h" +#include "video/vaapi.h" +#include "video/mp_image_pool.h" +#include "video/hwdec.h" +#include "video/filter/vf.h" + +#define ADDITIONAL_SURFACES (HWDEC_EXTRA_SURFACES + HWDEC_DELAY_QUEUE_COUNT) + +struct priv { + struct mp_log *log; + struct mp_vaapi_ctx *ctx; + struct mp_hwdec_ctx *hwdev; +}; + +static int init_decoder(struct lavc_ctx *ctx, int w, int h) +{ + struct priv *p = ctx->hwdec_priv; + // libavcodec has no way yet to communicate the exact surface format needed + // for the frame pool, or the required minimum size of the frame pool. + // Hopefully, this weakness in the libavcodec API will be fixed in the + // future. + // For the pixel format, we try to second-guess from what the libavcodec + // software decoder would require (sw_pix_fmt). It could break and require + // adjustment if new VAAPI surface formats are added. + int sw_format = ctx->avctx->sw_pix_fmt == AV_PIX_FMT_YUV420P10 ? + AV_PIX_FMT_P010 : AV_PIX_FMT_NV12; + + // The video output might not support all formats. + // Note that supported_formats==NULL means any are accepted. + if (p->hwdev && p->hwdev->supported_formats) { + int mp_format = pixfmt2imgfmt(sw_format); + bool found = false; + for (int n = 0; p->hwdev->supported_formats[n]; n++) { + if (p->hwdev->supported_formats[n] == mp_format) { + found = true; + break; + } + } + if (!found) { + MP_WARN(ctx, "Surface format %s not supported for direct rendering.\n", + mp_imgfmt_to_name(mp_format)); + return -1; + } + } + + return hwdec_setup_hw_frames_ctx(ctx, p->ctx->av_device_ref, sw_format, + hwdec_get_max_refs(ctx) + ADDITIONAL_SURFACES); +} + +static void uninit(struct lavc_ctx *ctx) +{ + struct priv *p = ctx->hwdec_priv; + + if (!p) + return; + + if (!p->hwdev) + va_destroy(p->ctx); + + talloc_free(p); + ctx->hwdec_priv = NULL; +} + +static int init(struct lavc_ctx *ctx, bool direct) +{ + struct priv *p = talloc_ptrtype(NULL, p); + *p = (struct priv) { + .log = mp_log_new(p, ctx->log, "vaapi"), + }; + + if (direct) { + p->hwdev = hwdec_devices_get(ctx->hwdec_devs, HWDEC_VAAPI); + p->ctx = p->hwdev->ctx; + } else { + p->ctx = va_create_standalone(ctx->log, false); + if (!p->ctx) { + talloc_free(p); + return -1; + } + } + + ctx->hwdec_priv = p; + + if (!p->ctx->av_device_ref) + return -1; + + return 0; +} + +static int init_direct(struct lavc_ctx *ctx) +{ + return init(ctx, true); +} + +static int probe(struct lavc_ctx *ctx, struct vd_lavc_hwdec *hwdec, + const char *codec) +{ + if (!hwdec_devices_load(ctx->hwdec_devs, HWDEC_VAAPI)) + return HWDEC_ERR_NO_CTX; + return 0; +} + +static int probe_copy(struct lavc_ctx *ctx, struct vd_lavc_hwdec *hwdec, + const char *codec) +{ + struct mp_vaapi_ctx *dummy = va_create_standalone(ctx->log, true); + if (!dummy) + return HWDEC_ERR_NO_CTX; + bool emulated = va_guess_if_emulated(dummy); + va_destroy(dummy); + if (emulated) + return HWDEC_ERR_EMULATED; + return 0; +} + +static int init_copy(struct lavc_ctx *ctx) +{ + return init(ctx, false); +} + +const struct vd_lavc_hwdec mp_vd_lavc_vaapi = { + .type = HWDEC_VAAPI, + .image_format = IMGFMT_VAAPI, + .volatile_context = true, + .probe = probe, + .init = init_direct, + .uninit = uninit, + .init_decoder = init_decoder, +}; + +const struct vd_lavc_hwdec mp_vd_lavc_vaapi_copy = { + .type = HWDEC_VAAPI_COPY, + .copying = true, + .image_format = IMGFMT_VAAPI, + .volatile_context = true, + .probe = probe_copy, + .init = init_copy, + .uninit = uninit, + .init_decoder = init_decoder, + .delay_queue = HWDEC_DELAY_QUEUE_COUNT, +}; diff --git a/video/decode/vaapi.c b/video/decode/hw_vaapi_old.c index c095868816..88379dfed8 100644 --- a/video/decode/vaapi.c +++ b/video/decode/hw_vaapi_old.c @@ -52,11 +52,9 @@ struct priv { struct mp_log *log; struct mp_vaapi_ctx *ctx; + bool own_ctx; VADisplay display; - const struct va_native_display *native_display_fns; - void *native_display; - // libavcodec shared struct struct vaapi_context *va_context; struct vaapi_context va_context_storage; @@ -67,98 +65,6 @@ struct priv { struct mp_image_pool *sw_pool; }; -struct va_native_display { - void (*create)(struct priv *p); - void (*destroy)(struct priv *p); -}; - -#if HAVE_VAAPI_X11 -#include <X11/Xlib.h> -#include <va/va_x11.h> - -static void x11_destroy(struct priv *p) -{ - if (p->native_display) - XCloseDisplay(p->native_display); - p->native_display = NULL; -} - -static void x11_create(struct priv *p) -{ - p->native_display = XOpenDisplay(NULL); - if (!p->native_display) - return; - p->display = vaGetDisplay(p->native_display); - if (!p->display) - x11_destroy(p); -} - -static const struct va_native_display disp_x11 = { - .create = x11_create, - .destroy = x11_destroy, -}; -#endif - -#if HAVE_VAAPI_DRM |