static int write_lavc(struct image_writer_ctx *ctx, mp_image_t *image, FILE *fp) { int success = 0; AVFrame *pic = NULL; AVPacket pkt = {0}; int got_output = 0; av_init_packet(&pkt); struct AVCodec *codec = avcodec_find_encoder(ctx->writer->lavc_codec); AVCodecContext *avctx = NULL; if (!codec) goto print_open_fail; avctx = avcodec_alloc_context3(codec); if (!avctx) goto print_open_fail; avctx->time_base = AV_TIME_BASE_Q; avctx->width = image->w; avctx->height = image->h; avctx->pix_fmt = imgfmt2pixfmt(image->imgfmt); if (avctx->pix_fmt == AV_PIX_FMT_NONE) { MP_ERR(ctx, "Image format %s not supported by lavc.\n", mp_imgfmt_to_name(image->imgfmt)); goto error_exit; } if (ctx->writer->lavc_codec == AV_CODEC_ID_PNG) { avctx->compression_level = ctx->opts->png_compression; avctx->prediction_method = ctx->opts->png_filter; } if (avcodec_open2(avctx, codec, NULL) < 0) { print_open_fail: MP_ERR(ctx, "Could not open libavcodec encoder for saving images\n"); goto error_exit; } pic = av_frame_alloc(); if (!pic) goto error_exit; for (int n = 0; n < 4; n++) { pic->data[n] = image->planes[n]; pic->linesize[n] = image->stride[n]; } int ret = avcodec_encode_video2(avctx, &pkt, pic, &got_output); if (ret < 0) goto error_exit; fwrite(pkt.data, pkt.size, 1, fp); success = !!got_output; error_exit: if (avctx) avcodec_close(avctx); av_free(avctx); av_frame_free(&pic); av_free_packet(&pkt); return success; }
static const char *imgfmt_to_name(int fmt) { for (int n = 0; format_names[n].name; n++) { if (format_names[n].fmt == fmt) return format_names[n].name; } return mp_imgfmt_to_name(fmt); }
static struct mp_image *d3d11va_retrieve_image(struct lavc_ctx *s, struct mp_image *img) { HRESULT hr; struct priv *p = s->hwdec_priv; ID3D11Texture2D *staging = p->decoder->staging; if (img->imgfmt != IMGFMT_D3D11VA) return img; ID3D11Texture2D *texture = (void *)img->planes[1]; int subindex = (intptr_t)img->planes[2]; if (!texture) { MP_ERR(p, "Failed to get Direct3D texture and surface from mp_image\n"); return img; } D3D11_TEXTURE2D_DESC texture_desc; ID3D11Texture2D_GetDesc(texture, &texture_desc); if (texture_desc.Width < img->w || texture_desc.Height < img->h) { MP_ERR(p, "Direct3D11 texture smaller than mp_image dimensions\n"); return img; } // copy to the staging texture ID3D11DeviceContext_CopySubresourceRegion( p->device_ctx, (ID3D11Resource *)staging, 0, 0, 0, 0, (ID3D11Resource *)texture, subindex, NULL); struct mp_image *sw_img = mp_image_pool_get(p->sw_pool, p->decoder->mpfmt_decoded, texture_desc.Width, texture_desc.Height); if (!sw_img) { MP_ERR(p, "Failed to get %s surface from CPU pool\n", mp_imgfmt_to_name(p->decoder->mpfmt_decoded)); return img; } // copy staging texture to the cpu mp_image D3D11_MAPPED_SUBRESOURCE lock; hr = ID3D11DeviceContext_Map(p->device_ctx, (ID3D11Resource *)staging, 0, D3D11_MAP_READ, 0, &lock); if (FAILED(hr)) { MP_ERR(p, "Failed to map D3D11 surface: %s\n", mp_HRESULT_to_str(hr)); talloc_free(sw_img); return img; } copy_nv12(sw_img, lock.pData, lock.RowPitch, texture_desc.Height); ID3D11DeviceContext_Unmap(p->device_ctx, (ID3D11Resource *)staging, 0); mp_image_set_size(sw_img, img->w, img->h); mp_image_copy_attributes(sw_img, img); talloc_free(img); return sw_img; }
static struct mp_image *d3d11va_retrieve_image(struct lavc_ctx *s, struct mp_image *img) { HRESULT hr; struct priv *p = s->hwdec_priv; ID3D11Texture2D *staging = p->decoder->staging; ID3D11Texture2D *texture = d3d11_texture_in_mp_image(img); ID3D11VideoDecoderOutputView *surface = d3d11_surface_in_mp_image(img); if (!texture || !surface) { MP_ERR(p, "Failed to get Direct3D texture and surface from mp_image\n"); return img; } D3D11_TEXTURE2D_DESC texture_desc; ID3D11Texture2D_GetDesc(texture, &texture_desc); if (texture_desc.Width < img->w || texture_desc.Height < img->h) { MP_ERR(p, "Direct3D11 texture smaller than mp_image dimensions\n"); return img; } // copy to the staging texture D3D11_VIDEO_DECODER_OUTPUT_VIEW_DESC surface_desc; ID3D11VideoDecoderOutputView_GetDesc(surface, &surface_desc); ID3D11DeviceContext_CopySubresourceRegion( p->device_ctx, (ID3D11Resource *)staging, 0, 0, 0, 0, (ID3D11Resource *)texture, surface_desc.Texture2D.ArraySlice, NULL); struct mp_image *sw_img = mp_image_pool_get(p->sw_pool, p->decoder->mpfmt_decoded, texture_desc.Width, texture_desc.Height); if (!sw_img) { MP_ERR(p, "Failed to get %s surface from CPU pool\n", mp_imgfmt_to_name(p->decoder->mpfmt_decoded)); return img; } // copy staging texture to the cpu mp_image D3D11_MAPPED_SUBRESOURCE lock; hr = ID3D11DeviceContext_Map(p->device_ctx, (ID3D11Resource *)staging, 0, D3D11_MAP_READ, 0, &lock); if (FAILED(hr)) { MP_ERR(p, "Failed to map D3D11 surface: %s\n", mp_HRESULT_to_str(hr)); talloc_free(sw_img); return img; } copy_nv12(sw_img, lock.pData, lock.RowPitch, texture_desc.Height); ID3D11DeviceContext_Unmap(p->device_ctx, (ID3D11Resource *)staging, 0); mp_image_set_size(sw_img, img->w, img->h); mp_image_copy_attributes(sw_img, img); talloc_free(img); return sw_img; }
static void print_fmt(int msglevel, struct vf_format *fmt) { if (fmt && fmt->configured) { mp_msg(MSGT_VFILTER, msglevel, "%dx%d", fmt->w, fmt->h); if (fmt->w != fmt->dw || fmt->h != fmt->dh) mp_msg(MSGT_VFILTER, msglevel, "->%dx%d", fmt->dw, fmt->dh); mp_msg(MSGT_VFILTER, msglevel, " %s %#x", mp_imgfmt_to_name(fmt->fmt), fmt->flags); } else { mp_msg(MSGT_VFILTER, msglevel, "???"); } }
static struct mp_image *dxva2_retrieve_image(struct lavc_ctx *s, struct mp_image *img) { HRESULT hr; struct priv *p = s->hwdec_priv; IDirect3DSurface9 *surface = img->imgfmt == IMGFMT_DXVA2 ? (IDirect3DSurface9 *)img->planes[3] : NULL; if (!surface) { MP_ERR(p, "Failed to get Direct3D surface from mp_image\n"); return img; } D3DSURFACE_DESC surface_desc; IDirect3DSurface9_GetDesc(surface, &surface_desc); if (surface_desc.Width < img->w || surface_desc.Height < img->h) { MP_ERR(p, "Direct3D11 texture smaller than mp_image dimensions\n"); return img; } struct mp_image *sw_img = mp_image_pool_get(p->sw_pool, p->mpfmt_decoded, surface_desc.Width, surface_desc.Height); if (!sw_img) { MP_ERR(p, "Failed to get %s surface from CPU pool\n", mp_imgfmt_to_name(p->mpfmt_decoded)); return img; } D3DLOCKED_RECT lock; hr = IDirect3DSurface9_LockRect(surface, &lock, NULL, D3DLOCK_READONLY); if (FAILED(hr)) { MP_ERR(p, "Unable to lock DXVA2 surface: %s\n", mp_HRESULT_to_str(hr)); talloc_free(sw_img); return img; } copy_nv12(sw_img, lock.pBits, lock.Pitch, surface_desc.Height); IDirect3DSurface9_UnlockRect(surface); mp_image_set_size(sw_img, img->w, img->h); mp_image_copy_attributes(sw_img, img); talloc_free(img); return sw_img; }
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; }
struct ra_layout ra_renderpass_input_layout(struct ra_renderpass_input *input) { size_t el_size = ra_vartype_size(input->type); if (!el_size) return (struct ra_layout){0}; // host data is always tightly packed return (struct ra_layout) { .align = 1, .stride = el_size * input->dim_v, .size = el_size * input->dim_v * input->dim_m, }; } static struct ra_renderpass_input *dup_inputs(void *ta_parent, const struct ra_renderpass_input *inputs, int num_inputs) { struct ra_renderpass_input *res = talloc_memdup(ta_parent, (void *)inputs, num_inputs * sizeof(inputs[0])); for (int n = 0; n < num_inputs; n++) res[n].name = talloc_strdup(res, res[n].name); return res; } // Return a newly allocated deep-copy of params. struct ra_renderpass_params *ra_renderpass_params_copy(void *ta_parent, const struct ra_renderpass_params *params) { struct ra_renderpass_params *res = talloc_ptrtype(ta_parent, res); *res = *params; res->inputs = dup_inputs(res, res->inputs, res->num_inputs); res->vertex_attribs = dup_inputs(res, res->vertex_attribs, res->num_vertex_attribs); res->cached_program = bstrdup(res, res->cached_program); res->vertex_shader = talloc_strdup(res, res->vertex_shader); res->frag_shader = talloc_strdup(res, res->frag_shader); res->compute_shader = talloc_strdup(res, res->compute_shader); return res; }; struct glsl_fmt { enum ra_ctype ctype; int num_components; int component_depth[4]; const char *glsl_format; }; // List taken from the GLSL specification, sans snorm and sint formats static const struct glsl_fmt ra_glsl_fmts[] = { {RA_CTYPE_FLOAT, 1, {16}, "r16f"}, {RA_CTYPE_FLOAT, 1, {32}, "r32f"}, {RA_CTYPE_FLOAT, 2, {16, 16}, "rg16f"}, {RA_CTYPE_FLOAT, 2, {32, 32}, "rg32f"}, {RA_CTYPE_FLOAT, 4, {16, 16, 16, 16}, "rgba16f"}, {RA_CTYPE_FLOAT, 4, {32, 32, 32, 32}, "rgba32f"}, {RA_CTYPE_FLOAT, 3, {11, 11, 10}, "r11f_g11f_b10f"}, {RA_CTYPE_UNORM, 1, {8}, "r8"}, {RA_CTYPE_UNORM, 1, {16}, "r16"}, {RA_CTYPE_UNORM, 2, {8, 8}, "rg8"}, {RA_CTYPE_UNORM, 2, {16, 16}, "rg16"}, {RA_CTYPE_UNORM, 4, {8, 8, 8, 8}, "rgba8"}, {RA_CTYPE_UNORM, 4, {16, 16, 16, 16}, "rgba16"}, {RA_CTYPE_UNORM, 4, {10, 10, 10, 2}, "rgb10_a2"}, {RA_CTYPE_UINT, 1, {8}, "r8ui"}, {RA_CTYPE_UINT, 1, {16}, "r16ui"}, {RA_CTYPE_UINT, 1, {32}, "r32ui"}, {RA_CTYPE_UINT, 2, {8, 8}, "rg8ui"}, {RA_CTYPE_UINT, 2, {16, 16}, "rg16ui"}, {RA_CTYPE_UINT, 2, {32, 32}, "rg32ui"}, {RA_CTYPE_UINT, 4, {8, 8, 8, 8}, "rgba8ui"}, {RA_CTYPE_UINT, 4, {16, 16, 16, 16}, "rgba16ui"}, {RA_CTYPE_UINT, 4, {32, 32, 32, 32}, "rgba32ui"}, {RA_CTYPE_UINT, 4, {10, 10, 10, 2}, "rgb10_a2ui"}, }; const char *ra_fmt_glsl_format(const struct ra_format *fmt) { for (int n = 0; n < MP_ARRAY_SIZE(ra_glsl_fmts); n++) { const struct glsl_fmt *gfmt = &ra_glsl_fmts[n]; if (fmt->ctype != gfmt->ctype) continue; if (fmt->num_components != gfmt->num_components) continue; for (int i = 0; i < fmt->num_components; i++) { if (fmt->component_depth[i] != gfmt->component_depth[i]) goto next_fmt; } return gfmt->glsl_format; next_fmt: ; // equivalent to `continue` } return NULL; } // Return whether this is a tightly packed format with no external padding and // with the same bit size/depth in all components, and the shader returns // components in the same order as in memory. static bool ra_format_is_regular(const struct ra_format *fmt) { if (!fmt->pixel_size || !fmt->num_components || !fmt->ordered) return false; for (int n = 1; n < fmt->num_components; n++) { if (fmt->component_size[n] != fmt->component_size[0] || fmt->component_depth[n] != fmt->component_depth[0]) return false; } if (fmt->component_size[0] * fmt->num_components != fmt->pixel_size * 8) return false; return true; } // Return a regular filterable format using RA_CTYPE_UNORM. const struct ra_format *ra_find_unorm_format(struct ra *ra, int bytes_per_component, int n_components) { for (int n = 0; n < ra->num_formats; n++) { const struct ra_format *fmt = ra->formats[n]; if (fmt->ctype == RA_CTYPE_UNORM && fmt->num_components == n_components && fmt->pixel_size == bytes_per_component * n_components && fmt->component_depth[0] == bytes_per_component * 8 && fmt->linear_filter && ra_format_is_regular(fmt)) return fmt; } return NULL; } // Return a regular format using RA_CTYPE_UINT. const struct ra_format *ra_find_uint_format(struct ra *ra, int bytes_per_component, int n_components) { for (int n = 0; n < ra->num_formats; n++) { const struct ra_format *fmt = ra->formats[n]; if (fmt->ctype == RA_CTYPE_UINT && fmt->num_components == n_components && fmt->pixel_size == bytes_per_component * n_components && fmt->component_depth[0] == bytes_per_component * 8 && ra_format_is_regular(fmt)) return fmt; } return NULL; } // Find a float format of any precision that matches the C type of the same // size for upload. // May drop bits from the mantissa (such as selecting float16 even if // bytes_per_component == 32); prefers possibly faster formats first. static const struct ra_format *ra_find_float_format(struct ra *ra, int bytes_per_component, int n_components) { // Assumes ra_format are ordered by performance. // The >=16 check is to avoid catching fringe formats. for (int n = 0; n < ra->num_formats; n++) { const struct ra_format *fmt = ra->formats[n]; if (fmt->ctype == RA_CTYPE_FLOAT && fmt->num_components == n_components && fmt->pixel_size == bytes_per_component * n_components && fmt->component_depth[0] >= 16 && fmt->linear_filter && ra_format_is_regular(fmt)) return fmt; } return NULL; } // Return a filterable regular format that uses at least float16 internally, and // uses a normal C float for transfer on the CPU side. (This is just so we don't // need 32->16 bit conversion on CPU, which would be messy.) const struct ra_format *ra_find_float16_format(struct ra *ra, int n_components) { return ra_find_float_format(ra, sizeof(float), n_components); } const struct ra_format *ra_find_named_format(struct ra *ra, const char *name) { for (int n = 0; n < ra->num_formats; n++) { const struct ra_format *fmt = ra->formats[n]; if (strcmp(fmt->name, name) == 0) return fmt; } return NULL; } // Like ra_find_unorm_format(), but if no fixed point format is available, // return an unsigned integer format. static const struct ra_format *find_plane_format(struct ra *ra, int bytes, int n_channels, enum mp_component_type ctype) { switch (ctype) { case MP_COMPONENT_TYPE_UINT: { const struct ra_format *f = ra_find_unorm_format(ra, bytes, n_channels); if (f) return f; return ra_find_uint_format(ra, bytes, n_channels); } case MP_COMPONENT_TYPE_FLOAT: return ra_find_float_format(ra, bytes, n_channels); default: return NULL; } } // Put a mapping of imgfmt to texture formats into *out. Basically it selects // the correct texture formats needed to represent an imgfmt in a shader, with // textures using the same memory organization as on the CPU. // Each plane is represented by a texture, and each texture has a RGBA // component order. out->components describes the meaning of them. // May return integer formats for >8 bit formats, if the driver has no // normalized 16 bit formats. // Returns false (and *out is not touched) if no format found. bool ra_get_imgfmt_desc(struct ra *ra, int imgfmt, struct ra_imgfmt_desc *out) { struct ra_imgfmt_desc res = {0}; struct mp_regular_imgfmt regfmt; if (mp_get_regular_imgfmt(®fmt, imgfmt)) { enum ra_ctype ctype = RA_CTYPE_UNKNOWN; res.num_planes = regfmt.num_planes; res.component_bits = regfmt.component_size * 8; res.component_pad = regfmt.component_pad; for (int n = 0; n < regfmt.num_planes; n++) { struct mp_regular_imgfmt_plane *plane = ®fmt.planes[n]; res.planes[n] = find_plane_format(ra, regfmt.component_size, plane->num_components, regfmt.component_type); if (!res.planes[n]) return false; for (int i = 0; i < plane->num_components; i++) res.components[n][i] = plane->components[i]; // Dropping LSBs when shifting will lead to dropped MSBs. if (res.component_bits > res.planes[n]->component_depth[0] && res.component_pad < 0) return false; // Renderer restriction, but actually an unwanted corner case. if (ctype != RA_CTYPE_UNKNOWN && ctype != res.planes[n]->ctype) return false; ctype = res.planes[n]->ctype; } res.chroma_w = regfmt.chroma_w; res.chroma_h = regfmt.chroma_h; goto supported; } for (int n = 0; n < ra->num_formats; n++) { if (imgfmt && ra->formats[n]->special_imgfmt == imgfmt) { res = *ra->formats[n]->special_imgfmt_desc; goto supported; } } // Unsupported format return false; supported: *out = res; return true; } void ra_dump_tex_formats(struct ra *ra, int msgl) { if (!mp_msg_test(ra->log, msgl)) return; MP_MSG(ra, msgl, "Texture formats:\n"); MP_MSG(ra, msgl, " NAME COMP*TYPE SIZE DEPTH PER COMP.\n"); for (int n = 0; n < ra->num_formats; n++) { const struct ra_format *fmt = ra->formats[n]; const char *ctype = "unknown"; switch (fmt->ctype) { case RA_CTYPE_UNORM: ctype = "unorm"; break; case RA_CTYPE_UINT: ctype = "uint "; break; case RA_CTYPE_FLOAT: ctype = "float"; break; } char cl[40] = ""; for (int i = 0; i < fmt->num_components; i++) { mp_snprintf_cat(cl, sizeof(cl), "%s%d", i ? " " : "", fmt->component_size[i]); if (fmt->component_size[i] != fmt->component_depth[i]) mp_snprintf_cat(cl, sizeof(cl), "/%d", fmt->component_depth[i]); } MP_MSG(ra, msgl, " %-10s %d*%s %3dB %s %s %s {%s}\n", fmt->name, fmt->num_components, ctype, fmt->pixel_size, fmt->luminance_alpha ? "LA" : " ", fmt->linear_filter ? "LF" : " ", fmt->renderable ? "CR" : " ", cl); } MP_MSG(ra, msgl, " LA = LUMINANCE_ALPHA hack format\n"); MP_MSG(ra, msgl, " LF = linear filterable\n"); MP_MSG(ra, msgl, " CR = can be used for render targets\n"); } void ra_dump_imgfmt_desc(struct ra *ra, const struct ra_imgfmt_desc *desc, int msgl) { char pl[80] = ""; char pf[80] = ""; for (int n = 0; n < desc->num_planes; n++) { if (n > 0) { mp_snprintf_cat(pl, sizeof(pl), "/"); mp_snprintf_cat(pf, sizeof(pf), "/"); } char t[5] = {0}; for (int i = 0; i < 4; i++) t[i] = "_rgba"[desc->components[n][i]]; for (int i = 3; i > 0 && t[i] == '_'; i--) t[i] = '\0'; mp_snprintf_cat(pl, sizeof(pl), "%s", t); mp_snprintf_cat(pf, sizeof(pf), "%s", desc->planes[n]->name); } MP_MSG(ra, msgl, "%d planes %dx%d %d/%d [%s] (%s)\n", desc->num_planes, desc->chroma_w, desc->chroma_h, desc->component_bits, desc->component_pad, pf, pl); } void ra_dump_img_formats(struct ra *ra, int msgl) { if (!mp_msg_test(ra->log, msgl)) return; MP_MSG(ra, msgl, "Image formats:\n"); for (int imgfmt = IMGFMT_START; imgfmt < IMGFMT_END; imgfmt++) { const char *name = mp_imgfmt_to_name(imgfmt); if (strcmp(name, "unknown") == 0) continue; MP_MSG(ra, msgl, " %s", name); struct ra_imgfmt_desc desc; if (ra_get_imgfmt_desc(ra, imgfmt, &desc)) { MP_MSG(ra, msgl, " => "); ra_dump_imgfmt_desc(ra, &desc, msgl); } else { MP_MSG(ra, msgl, "\n"); } } }
static bool write_lavc(struct image_writer_ctx *ctx, mp_image_t *image, FILE *fp) { bool success = 0; AVFrame *pic = NULL; AVPacket pkt = {0}; int got_output = 0; av_init_packet(&pkt); struct AVCodec *codec = avcodec_find_encoder(ctx->opts->format); AVCodecContext *avctx = NULL; if (!codec) goto print_open_fail; avctx = avcodec_alloc_context3(codec); if (!avctx) goto print_open_fail; avctx->time_base = AV_TIME_BASE_Q; avctx->width = image->w; avctx->height = image->h; avctx->color_range = mp_csp_levels_to_avcol_range(image->params.color.levels); avctx->pix_fmt = imgfmt2pixfmt(image->imgfmt); // Annoying deprecated garbage for the jpg encoder. if (image->params.color.levels == MP_CSP_LEVELS_PC) avctx->pix_fmt = replace_j_format(avctx->pix_fmt); if (avctx->pix_fmt == AV_PIX_FMT_NONE) { MP_ERR(ctx, "Image format %s not supported by lavc.\n", mp_imgfmt_to_name(image->imgfmt)); goto error_exit; } if (codec->id == AV_CODEC_ID_PNG) { avctx->compression_level = ctx->opts->png_compression; av_opt_set_int(avctx, "pred", ctx->opts->png_filter, AV_OPT_SEARCH_CHILDREN); } if (avcodec_open2(avctx, codec, NULL) < 0) { print_open_fail: MP_ERR(ctx, "Could not open libavcodec encoder for saving images\n"); goto error_exit; } pic = av_frame_alloc(); if (!pic) goto error_exit; for (int n = 0; n < 4; n++) { pic->data[n] = image->planes[n]; pic->linesize[n] = image->stride[n]; } pic->format = avctx->pix_fmt; pic->width = avctx->width; pic->height = avctx->height; pic->color_range = avctx->color_range; if (ctx->opts->tag_csp) { pic->color_primaries = mp_csp_prim_to_avcol_pri(image->params.color.primaries); pic->color_trc = mp_csp_trc_to_avcol_trc(image->params.color.gamma); } int ret = avcodec_send_frame(avctx, pic); if (ret < 0) goto error_exit; ret = avcodec_send_frame(avctx, NULL); // send EOF if (ret < 0) goto error_exit; ret = avcodec_receive_packet(avctx, &pkt); if (ret < 0) goto error_exit; got_output = 1; fwrite(pkt.data, pkt.size, 1, fp); success = !!got_output; error_exit: if (avctx) avcodec_close(avctx); av_free(avctx); av_frame_free(&pic); av_packet_unref(&pkt); return success; }