static mp_image_t* alloc_mpi(int w, int h, uint32_t fmt) { mp_image_t* mpi = new_mp_image(w,h); mp_image_setfmt(mpi,fmt); // IF09 - allocate space for 4. plane delta info - unused if (mpi->imgfmt == IMGFMT_IF09) { mpi->planes[0]=memalign(64, mpi->bpp*mpi->width*(mpi->height+2)/8+ mpi->chroma_width*mpi->chroma_height); /* delta table, just for fun ;) */ mpi->planes[3]=mpi->planes[0]+2*(mpi->chroma_width*mpi->chroma_height); } else mpi->planes[0]=memalign(64, mpi->bpp*mpi->width*(mpi->height+2)/8); if(mpi->flags&MP_IMGFLAG_PLANAR){ // YV12/I420/YVU9/IF09. feel free to add other planar formats here... if(!mpi->stride[0]) mpi->stride[0]=mpi->width; if(!mpi->stride[1]) mpi->stride[1]=mpi->stride[2]=mpi->chroma_width; if(mpi->flags&MP_IMGFLAG_SWAPPED){ // I420/IYUV (Y,U,V) mpi->planes[1]=mpi->planes[0]+mpi->width*mpi->height; mpi->planes[2]=mpi->planes[1]+mpi->chroma_width*mpi->chroma_height; } else { // YV12,YVU9,IF09 (Y,V,U) mpi->planes[2]=mpi->planes[0]+mpi->width*mpi->height; mpi->planes[1]=mpi->planes[2]+mpi->chroma_width*mpi->chroma_height; } } else { if(!mpi->stride[0]) mpi->stride[0]=mpi->width*mpi->bpp/8; } mpi->flags|=MP_IMGFLAG_ALLOCATED; return mpi; }
static struct mp_image get_xv_buffer(struct vo *vo, int buf_index) { struct xvctx *ctx = vo->priv; XvImage *xv_image = ctx->xvimage[buf_index]; struct mp_image img = {0}; mp_image_set_size(&img, ctx->image_width, ctx->image_height); mp_image_setfmt(&img, ctx->image_format); bool swapuv = ctx->xv_format == MP_FOURCC_YV12; for (int n = 0; n < img.num_planes; n++) { int sn = n > 0 && swapuv ? (n == 1 ? 2 : 1) : n; img.planes[n] = xv_image->data + xv_image->offsets[sn]; img.stride[n] = xv_image->pitches[sn]; } if (vo->params) { struct mp_image_params params = *vo->params; if (ctx->cached_csp) params.colorspace = ctx->cached_csp; mp_image_set_attributes(&img, ¶ms); } return img; }
static struct mp_image *alloc_pool(void *pctx, int fmt, int w, int h) { struct vf_instance *vf = pctx; struct vf_priv_s *p = vf->priv; HRESULT hr; ID3D11Texture2D *texture = NULL; D3D11_TEXTURE2D_DESC texdesc = { .Width = w, .Height = h, .Format = p->out_format, .MipLevels = 1, .ArraySize = 1, .SampleDesc = { .Count = 1 }, .Usage = D3D11_USAGE_DEFAULT, .BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE, .MiscFlags = p->out_shared ? D3D11_RESOURCE_MISC_SHARED : 0, }; hr = ID3D11Device_CreateTexture2D(p->vo_dev, &texdesc, NULL, &texture); if (FAILED(hr)) return NULL; struct mp_image *mpi = mp_image_new_custom_ref(NULL, texture, release_tex); if (!mpi) abort(); mp_image_setfmt(mpi, p->out_params.imgfmt); mp_image_set_size(mpi, w, h); mpi->params.hw_subfmt = p->out_params.hw_subfmt; mpi->planes[1] = (void *)texture; mpi->planes[2] = (void *)(intptr_t)0; return mpi; }
static struct mp_image *filter(struct vf_instance *vf, struct mp_image *mpi) { // As documented in the manpage, the user can easily provoke crashes if (vf->priv->outfmt) mp_image_setfmt(mpi, vf->priv->outfmt); return mpi; }
static struct mp_image *d3d11va_new_ref(ID3D11VideoDecoderOutputView *view, int w, int h) { if (!view) return NULL; struct d3d11va_surface *surface = talloc_zero(NULL, struct d3d11va_surface); surface->surface = view; ID3D11VideoDecoderOutputView_AddRef(surface->surface); ID3D11VideoDecoderOutputView_GetResource( surface->surface, (ID3D11Resource **)&surface->texture); D3D11_VIDEO_DECODER_OUTPUT_VIEW_DESC surface_desc; ID3D11VideoDecoderOutputView_GetDesc(surface->surface, &surface_desc); surface->subindex = surface_desc.Texture2D.ArraySlice; struct mp_image *mpi = mp_image_new_custom_ref(NULL, surface, d3d11va_release_img); if (!mpi) abort(); mp_image_setfmt(mpi, IMGFMT_D3D11VA); mp_image_set_size(mpi, w, h); mpi->planes[0] = NULL; mpi->planes[1] = (void *)surface->texture; mpi->planes[2] = (void *)(intptr_t)surface->subindex; mpi->planes[3] = (void *)surface->surface; return mpi; }
mp_image_t* alloc_mpi(int w, int h, unsigned long int fmt) { mp_image_t* mpi = new_mp_image(w,h); mp_image_setfmt(mpi,fmt); mp_image_alloc_planes(mpi); return mpi; }
static struct mp_image get_x_buffer(struct priv *p) { struct mp_image img = {0}; img.w = img.width = p->image_width; img.h = img.height = p->image_height; mp_image_setfmt(&img, p->out_format); img.planes[0] = p->ImageData; img.stride[0] = p->image_width * ((p->bpp + 7) / 8); return img; }
static void copy_nv12(struct mp_image *dest, uint8_t *src_bits, unsigned src_pitch, unsigned surf_height) { struct mp_image buf = {0}; mp_image_setfmt(&buf, IMGFMT_NV12); mp_image_set_size(&buf, dest->w, dest->h); buf.planes[0] = src_bits; buf.stride[0] = src_pitch; buf.planes[1] = src_bits + src_pitch * surf_height; buf.stride[1] = src_pitch; mp_image_copy_gpu(dest, &buf); }
void mp_blur_rgba_sub_bitmap(struct sub_bitmap *d, double gblur) { struct mp_image *tmp1 = mp_image_alloc(IMGFMT_BGRA, d->w, d->h); if (tmp1) { // on OOM, skip region struct mp_image s = {0}; mp_image_setfmt(&s, IMGFMT_BGRA); mp_image_set_size(&s, d->w, d->h); s.stride[0] = d->stride; s.planes[0] = d->bitmap; mp_image_copy(tmp1, &s); mp_image_sw_blur_scale(&s, tmp1, gblur); } talloc_free(tmp1); }
static struct mp_image get_xv_buffer(struct vo *vo, int buf_index) { struct xvctx *ctx = vo->priv; XvImage *xv_image = ctx->xvimage[buf_index]; struct mp_image img = {0}; img.w = img.width = ctx->image_width; img.h = img.height = ctx->image_height; mp_image_setfmt(&img, ctx->image_format); bool swapuv = ctx->image_format == IMGFMT_YV12; for (int n = 0; n < img.num_planes; n++) { int sn = n > 0 && swapuv ? (n == 1 ? 2 : 1) : n; img.planes[n] = xv_image->data + xv_image->offsets[sn]; img.stride[n] = xv_image->pitches[sn]; } mp_image_set_colorspace_details(&img, &ctx->cached_csp); return img; }
static struct mp_image map_vs_frame(struct vf_priv_s *p, const VSFrameRef *ref, bool w) { const VSFormat *fmt = p->vsapi->getFrameFormat(ref); struct mp_image img = {0}; mp_image_setfmt(&img, mp_from_vs(fmt->id)); mp_image_set_size(&img, p->vsapi->getFrameWidth(ref, 0), p->vsapi->getFrameHeight(ref, 0)); for (int n = 0; n < img.num_planes; n++) { if (w) { img.planes[n] = p->vsapi->getWritePtr((VSFrameRef *)ref, n); } else { img.planes[n] = (uint8_t *)p->vsapi->getReadPtr(ref, n); } img.stride[n] = p->vsapi->getStride(ref, n); } return img; }
// This actually returns dummy images, since vda_264 creates it's own AVFrames // to wrap CVPixelBuffers in planes[3]. static struct mp_image *allocate_image(struct lavc_ctx *ctx, int fmt, int w, int h) { struct priv *p = ctx->hwdec_priv; if (fmt != IMGFMT_VDA) return NULL; if (w != p->vda_ctx.width || h != p->vda_ctx.height) init_vda_decoder(ctx); struct mp_image img = {0}; mp_image_setfmt(&img, fmt); mp_image_set_size(&img, w, h); // There is an `assert(!dst->f.buf[0])` in libavcodec/h264.c // Setting the first plane to some dummy value allows to satisfy it img.planes[0] = (void*)"dummy"; return mp_image_new_custom_ref(&img, NULL, NULL); }
// Update hw_subfmt to the underlying format. Needed because AVFrame does not // have such an attribute, so it can't be passed through, and is updated here // instead. (But in the future, AVHWFramesContext could be used.) static struct mp_image *d3d11va_update_image_attribs(struct lavc_ctx *s, struct mp_image *img) { ID3D11Texture2D *texture = (void *)img->planes[1]; if (!texture) return img; D3D11_TEXTURE2D_DESC texture_desc; ID3D11Texture2D_GetDesc(texture, &texture_desc); for (int n = 0; n < MP_ARRAY_SIZE(d3d11_formats); n++) { if (d3d11_formats[n].dxfmt == texture_desc.Format) { img->params.hw_subfmt = d3d11_formats[n].mpfmt; break; } } if (img->params.hw_subfmt == IMGFMT_NV12) mp_image_setfmt(img, IMGFMT_D3D11NV12); return img; }
static struct mp_image *dxva2_new_ref(IDirectXVideoDecoder *decoder, IDirect3DSurface9 *d3d9_surface, int w, int h) { if (!decoder || !d3d9_surface) return NULL; struct dxva2_surface *surface = talloc_zero(NULL, struct dxva2_surface); surface->surface = d3d9_surface; IDirect3DSurface9_AddRef(surface->surface); surface->decoder = decoder; IDirectXVideoDecoder_AddRef(surface->decoder); struct mp_image *mpi = mp_image_new_custom_ref(NULL, surface, dxva2_release_img); if (!mpi) abort(); mp_image_setfmt(mpi, IMGFMT_DXVA2); mp_image_set_size(mpi, w, h); mpi->planes[3] = (void *)surface->surface; return mpi; }
static struct mp_image *dxva2_allocate_image(struct lavc_ctx *s, int img_w, int img_h) { DXVA2Context *ctx = s->hwdec_priv; int i, old_unused = -1; for (i = 0; i < ctx->num_surfaces; i++) { surface_info *info = &ctx->surface_infos[i]; if (!info->used && (old_unused == -1 || info->age < ctx->surface_infos[old_unused].age)) old_unused = i; } if (old_unused == -1) { MP_ERR(ctx, "No free DXVA2 surface!\n"); return NULL; } i = old_unused; DXVA2SurfaceWrapper *w = av_mallocz(sizeof(*w)); if (!w) return NULL; w->ctx = ctx; w->surface = ctx->surfaces[i];; IDirect3DSurface9_AddRef(w->surface); w->decoder = ctx->decoder; IDirectXVideoDecoder_AddRef(w->decoder); ctx->surface_infos[i].used = 1; ctx->surface_infos[i].age = ctx->surface_age++; struct mp_image mpi = {0}; mp_image_setfmt(&mpi, IMGFMT_DXVA2); mp_image_set_size(&mpi, img_w, img_h); mpi.planes[3] = (void *)w->surface; return mp_image_new_custom_ref(&mpi, w, dxva2_release_img); }
static struct mp_image *filter(struct vf_instance *vf, struct mp_image *mpi) { if (vf->priv->outfmt) mp_image_setfmt(mpi, vf->priv->outfmt); return mpi; }
mp_image *null_mp_image(uint imgfmt, int width, int height, void *arg, void(*free)(void*)) { auto mpi = null_mp_image(arg, free); mp_image_setfmt(mpi, imgfmt); mp_image_set_size(mpi, width, height); return mpi; }
// Attempt to initialize all pads. Return true if all are initialized, or // false if more data is needed (or on error). static bool init_pads(struct lavfi *c) { if (!c->graph) goto error; for (int n = 0; n < c->num_out_pads; n++) { struct lavfi_pad *pad = c->out_pads[n]; if (pad->buffer) continue; const AVFilter *dst_filter = NULL; if (pad->type == MP_FRAME_AUDIO) { dst_filter = avfilter_get_by_name("abuffersink"); } else if (pad->type == MP_FRAME_VIDEO) { dst_filter = avfilter_get_by_name("buffersink"); } else { assert(0); } if (!dst_filter) goto error; char name[256]; snprintf(name, sizeof(name), "mpv_sink_%s", pad->name); if (avfilter_graph_create_filter(&pad->buffer, dst_filter, name, NULL, NULL, c->graph) < 0) goto error; if (avfilter_link(pad->filter, pad->filter_pad, pad->buffer, 0) < 0) goto error; } for (int n = 0; n < c->num_in_pads; n++) { struct lavfi_pad *pad = c->in_pads[n]; if (pad->buffer) continue; mp_frame_unref(&pad->in_fmt); read_pad_input(c, pad); // no input data, format unknown, can't init, wait longer. if (!pad->pending.type) return false; if (mp_frame_is_data(pad->pending)) { assert(pad->pending.type == pad->type); pad->in_fmt = mp_frame_ref(pad->pending); if (!pad->in_fmt.type) goto error; if (pad->in_fmt.type == MP_FRAME_VIDEO) mp_image_unref_data(pad->in_fmt.data); if (pad->in_fmt.type == MP_FRAME_AUDIO) mp_aframe_unref_data(pad->in_fmt.data); } if (pad->pending.type == MP_FRAME_EOF && !pad->in_fmt.type) { // libavfilter makes this painful. Init it with a dummy config, // just so we can tell it the stream is EOF. if (pad->type == MP_FRAME_AUDIO) { struct mp_aframe *fmt = mp_aframe_create(); mp_aframe_set_format(fmt, AF_FORMAT_FLOAT); mp_aframe_set_chmap(fmt, &(struct mp_chmap)MP_CHMAP_INIT_STEREO); mp_aframe_set_rate(fmt, 48000); pad->in_fmt = (struct mp_frame){MP_FRAME_AUDIO, fmt}; } if (pad->type == MP_FRAME_VIDEO) { struct mp_image *fmt = talloc_zero(NULL, struct mp_image); mp_image_setfmt(fmt, IMGFMT_420P); mp_image_set_size(fmt, 64, 64); pad->in_fmt = (struct mp_frame){MP_FRAME_VIDEO, fmt}; } } if (pad->in_fmt.type != pad->type) goto error; AVBufferSrcParameters *params = av_buffersrc_parameters_alloc(); if (!params) goto error; pad->timebase = AV_TIME_BASE_Q; char *filter_name = NULL; if (pad->type == MP_FRAME_AUDIO) { struct mp_aframe *fmt = pad->in_fmt.data; params->format = af_to_avformat(mp_aframe_get_format(fmt)); params->sample_rate = mp_aframe_get_rate(fmt); struct mp_chmap chmap = {0}; mp_aframe_get_chmap(fmt, &chmap); params->channel_layout = mp_chmap_to_lavc(&chmap); pad->timebase = (AVRational){1, mp_aframe_get_rate(fmt)}; filter_name = "abuffer"; } else if (pad->type == MP_FRAME_VIDEO) { struct mp_image *fmt = pad->in_fmt.data; params->format = imgfmt2pixfmt(fmt->imgfmt); params->width = fmt->w; params->height = fmt->h; params->sample_aspect_ratio.num = fmt->params.p_w; params->sample_aspect_ratio.den = fmt->params.p_h; params->hw_frames_ctx = fmt->hwctx; params->frame_rate = av_d2q(fmt->nominal_fps, 1000000); filter_name = "buffer"; } else { assert(0); } params->time_base = pad->timebase; const AVFilter *filter = avfilter_get_by_name(filter_name); if (filter) { char name[256]; snprintf(name, sizeof(name), "mpv_src_%s", pad->name); pad->buffer = avfilter_graph_alloc_filter(c->graph, filter, name); } if (!pad->buffer) { av_free(params); goto error; } int ret = av_buffersrc_parameters_set(pad->buffer, params); av_free(params); if (ret < 0) goto error; if (avfilter_init_str(pad->buffer, NULL) < 0) goto error; if (avfilter_link(pad->buffer, 0, pad->filter, pad->filter_pad) < 0) goto error; } return true; error: MP_FATAL(c, "could not initialize filter pads\n"); c->failed = true; mp_filter_internal_mark_failed(c->f); return false; }
static int map_image(struct gl_hwdec *hw, struct mp_image *hw_image, GLuint *out_textures) { struct priv *p = hw->priv; GL *gl = hw->gl; VAStatus status; VAImage *va_image = &p->current_image; unref_image(hw); mp_image_setrefp(&p->current_ref, hw_image); va_lock(p->ctx); status = vaDeriveImage(p->display, va_surface_id(hw_image), va_image); if (!CHECK_VA_STATUS(p, "vaDeriveImage()")) goto err; int mpfmt = va_fourcc_to_imgfmt(va_image->format.fourcc); if (mpfmt != IMGFMT_NV12 && mpfmt != IMGFMT_420P) { MP_FATAL(p, "unsupported VA image format %s\n", VA_STR_FOURCC(va_image->format.fourcc)); goto err; } if (!hw->converted_imgfmt) { MP_VERBOSE(p, "format: %s %s\n", VA_STR_FOURCC(va_image->format.fourcc), mp_imgfmt_to_name(mpfmt)); hw->converted_imgfmt = mpfmt; } if (hw->converted_imgfmt != mpfmt) { MP_FATAL(p, "mid-stream hwdec format change (%s -> %s) not supported\n", mp_imgfmt_to_name(hw->converted_imgfmt), mp_imgfmt_to_name(mpfmt)); goto err; } VABufferInfo buffer_info = {.mem_type = VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME}; status = vaAcquireBufferHandle(p->display, va_image->buf, &buffer_info); if (!CHECK_VA_STATUS(p, "vaAcquireBufferHandle()")) goto err; p->buffer_acquired = true; struct mp_image layout = {0}; mp_image_set_params(&layout, &hw_image->params); mp_image_setfmt(&layout, mpfmt); // (it would be nice if we could use EGL_IMAGE_INTERNAL_FORMAT_EXT) int drm_fmts[4] = {MP_FOURCC('R', '8', ' ', ' '), // DRM_FORMAT_R8 MP_FOURCC('G', 'R', '8', '8'), // DRM_FORMAT_GR88 MP_FOURCC('R', 'G', '2', '4'), // DRM_FORMAT_RGB888 MP_FOURCC('R', 'A', '2', '4')}; // DRM_FORMAT_RGBA8888 for (int n = 0; n < layout.num_planes; n++) { int attribs[20] = {EGL_NONE}; int num_attribs = 0; ADD_ATTRIB(EGL_LINUX_DRM_FOURCC_EXT, drm_fmts[layout.fmt.bytes[n] - 1]); ADD_ATTRIB(EGL_WIDTH, mp_image_plane_w(&layout, n)); ADD_ATTRIB(EGL_HEIGHT, mp_image_plane_h(&layout, n)); ADD_ATTRIB(EGL_DMA_BUF_PLANE0_FD_EXT, buffer_info.handle); ADD_ATTRIB(EGL_DMA_BUF_PLANE0_OFFSET_EXT, va_image->offsets[n]); ADD_ATTRIB(EGL_DMA_BUF_PLANE0_PITCH_EXT, va_image->pitches[n]); p->images[n] = p->CreateImageKHR(eglGetCurrentDisplay(), EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, NULL, attribs); if (!p->images[n]) goto err; gl->BindTexture(GL_TEXTURE_2D, p->gl_textures[n]); p->EGLImageTargetTexture2DOES(GL_TEXTURE_2D, p->images[n]); out_textures[n] = p->gl_textures[n]; } gl->BindTexture(GL_TEXTURE_2D, 0); if (va_image->format.fourcc == VA_FOURCC_YV12) MPSWAP(GLuint, out_textures[1], out_textures[2]); va_unlock(p->ctx); return 0; err: va_unlock(p->ctx); MP_FATAL(p, "mapping VAAPI EGL image failed\n"); unref_image(hw); return -1; }