int write_image(struct mp_image *image, const struct image_writer_opts *opts, const char *filename) { struct mp_image *allocated_image = NULL; struct image_writer_opts defs = image_writer_opts_defaults; int d_w = image->display_w ? image->display_w : image->w; int d_h = image->display_h ? image->display_h : image->h; bool is_anamorphic = image->w != d_w || image->h != d_h; if (!opts) opts = &defs; const struct img_writer *writer = get_writer(opts); struct image_writer_ctx ctx = { opts, writer }; int destfmt = IMGFMT_RGB24; if (writer->pixfmts) { destfmt = writer->pixfmts[0]; // default to first pixel format for (int *fmt = writer->pixfmts; *fmt; fmt++) { if (*fmt == image->imgfmt) { destfmt = *fmt; break; } } } // Caveat: - no colorspace/levels conversion done if pixel formats equal // - RGB->YUV assumes BT.601 // - color levels broken in various ways thanks to libswscale if (image->imgfmt != destfmt || is_anamorphic) { struct mp_image *dst = alloc_mpi(d_w, d_h, destfmt); vf_clone_mpi_attributes(dst, image); int flags = SWS_LANCZOS | SWS_FULL_CHR_H_INT | SWS_FULL_CHR_H_INP | SWS_ACCURATE_RND | SWS_BITEXACT; mp_image_swscale(dst, image, flags); allocated_image = dst; image = dst; } FILE *fp = fopen(filename, "wb"); int success = 0; if (fp == NULL) { mp_msg(MSGT_CPLAYER, MSGL_ERR, "Error opening '%s' for writing!\n", filename); } else { success = writer->write(&ctx, image, fp); success = !fclose(fp) && success; if (!success) mp_msg(MSGT_CPLAYER, MSGL_ERR, "Error writing file '%s'!\n", filename); } free_mp_image(allocated_image); return success; }
static void draw_osd(struct vo *vo, struct osd_state *osd) { struct priv *p = vo->priv; struct mp_image img = get_x_buffer(p); struct mp_osd_res res = { .w = img.w, .h = img.h, .display_par = vo->monitor_par, .video_par = vo->aspdat.par, }; osd_draw_on_image_bk(osd, res, osd->vo_pts, 0, p->osd_backup, &img); } static mp_image_t *get_screenshot(struct vo *vo) { struct priv *p = vo->priv; struct mp_image img = get_x_buffer(p); struct mp_image *res = alloc_mpi(img.w, img.h, img.imgfmt); copy_mpi(res, &img); mp_draw_sub_backup_restore(p->osd_backup, res); return res; } static int redraw_frame(struct vo *vo) { struct priv *p = vo->priv; struct mp_image img = get_x_buffer(p); mp_draw_sub_backup_restore(p->osd_backup, &img); return true; } static void flip_page(struct vo *vo) { struct priv *p = vo->priv; Display_Image(p, p->myximage, p->ImageData); XSync(vo->x11->display, False); } static int draw_slice(struct vo *vo, uint8_t *src[], int stride[], int w, int h, int x, int y) { struct priv *p = vo->priv; uint8_t *dst[MP_MAX_PLANES] = {NULL}; int dstStride[MP_MAX_PLANES] = {0}; if ((p->old_vo_dwidth != vo->dwidth || p->old_vo_dheight != vo->dheight) /*&& y==0 */ && p->zoomFlag) { int newW = vo->dwidth; int newH = vo->dheight; struct SwsContext *oldContext = p->swsContext; p->old_vo_dwidth = vo->dwidth; p->old_vo_dheight = vo->dheight; if (vo_fs) aspect(vo, &newW, &newH, A_ZOOM); if (sws_flags == 0) newW &= (~31); // not needed but, if the user wants the FAST_BILINEAR SCALER, then its needed p->swsContext = sws_getContextFromCmdLine(p->srcW, p->srcH, p->in_format, newW, newH, p->out_format); if (p->swsContext) { p->image_width = (newW + 7) & (~7); p->image_height = newH; freeMyXImage(p); getMyXImage(p); sws_freeContext(oldContext); } else p->swsContext = oldContext; p->dst_width = newW; } dstStride[0] = p->image_width * ((p->bpp + 7) / 8); dst[0] = p->ImageData; if (p->Flip_Flag) { dst[0] += dstStride[0] * (p->image_height - 1); dstStride[0] = -dstStride[0]; } sws_scale(p->swsContext, (const uint8_t **)src, stride, y, h, dst, dstStride); mp_draw_sub_backup_reset(p->osd_backup); return 0; } static int query_format(struct vo *vo, uint32_t format) { mp_msg(MSGT_VO, MSGL_DBG2, "vo_x11: query_format was called: %x (%s)\n", format, vo_format_name(format)); if (IMGFMT_IS_BGR(format)) { if (IMGFMT_BGR_DEPTH(format) <= 8) return 0; // TODO 8bpp not yet fully implemented if (IMGFMT_BGR_DEPTH(format) == vo->x11->depthonscreen) return VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW | VFCAP_OSD | VFCAP_FLIP | VFCAP_ACCEPT_STRIDE; else return VFCAP_CSP_SUPPORTED | VFCAP_OSD | VFCAP_FLIP | VFCAP_ACCEPT_STRIDE; } switch (format) { case IMGFMT_I420: case IMGFMT_IYUV: case IMGFMT_YV12: return VFCAP_CSP_SUPPORTED | VFCAP_OSD | VFCAP_ACCEPT_STRIDE; } return 0; }
static int put_image(struct vf_instance_s* vf, mp_image_t *mpi, double pts){ mp_image_t *dmpi = NULL; if (vf->priv->passthrough) { dmpi=vf_get_image(vf->next, IMGFMT_MPEGPES, MP_IMGTYPE_EXPORT, 0, mpi->w, mpi->h); dmpi->planes[0]=mpi->planes[0]; return vf_next_put_image(vf,dmpi, pts); } if(vf->priv->current->show || (vf->priv->current->parent && vf->priv->current->parent->show)) { // Close all menu who requested it while(vf->priv->current->cl && vf->priv->current != vf->priv->root) { menu_t* m = vf->priv->current; vf->priv->current = m->parent ? m->parent : vf->priv->root; menu_close(m); } // Step 1 : save the picture while(go2pause == 1) { static char delay = 0; // Hack : wait the 2 frame to be sure to show the right picture delay ^= 1; // after a seek if(!delay) break; if(pause_mpi && (mpi->w != pause_mpi->w || mpi->h != pause_mpi->h || mpi->imgfmt != pause_mpi->imgfmt)) { free_mp_image(pause_mpi); pause_mpi = NULL; } if(!pause_mpi) pause_mpi = alloc_mpi(mpi->w,mpi->h,mpi->imgfmt); copy_mpi(pause_mpi,mpi); mp_input_queue_cmd(mp_input_parse_cmd("pause")); go2pause = 2; break; } // Grab // Ungrab the keys if(!mp_input_key_cb && vf->priv->current->show) mp_input_key_cb = key_cb; if(mp_input_key_cb && !vf->priv->current->show) mp_input_key_cb = NULL; if(mpi->flags&MP_IMGFLAG_DIRECT) dmpi = mpi->priv; else { dmpi = vf_get_image(vf->next,mpi->imgfmt, MP_IMGTYPE_TEMP, MP_IMGFLAG_ACCEPT_STRIDE, mpi->w,mpi->h); copy_mpi(dmpi,mpi); } menu_draw(vf->priv->current,dmpi); } else { if(mp_input_key_cb) mp_input_key_cb = NULL; if(mpi->flags&MP_IMGFLAG_DIRECT) dmpi = mpi->priv; else { dmpi = vf_get_image(vf->next,mpi->imgfmt, MP_IMGTYPE_EXPORT, MP_IMGFLAG_ACCEPT_STRIDE, mpi->w,mpi->h); dmpi->stride[0] = mpi->stride[0]; dmpi->stride[1] = mpi->stride[1]; dmpi->stride[2] = mpi->stride[2]; dmpi->planes[0] = mpi->planes[0]; dmpi->planes[1] = mpi->planes[1]; dmpi->planes[2] = mpi->planes[2]; dmpi->priv = mpi->priv; } } return vf_next_put_image(vf,dmpi, pts); }
static int put_image(struct vf_instance *vf, mp_image_t *mpi, double pts){ mp_image_t *dmpi = NULL; if (vf->priv->passthrough) { dmpi=vf_get_image(vf->next, IMGFMT_MPEGPES, MP_IMGTYPE_EXPORT, 0, mpi->w, mpi->h); dmpi->planes[0]=mpi->planes[0]; return vf_next_put_image(vf,dmpi, pts); } // Close all menu who requested it while(vf->priv->current->cl && vf->priv->current != vf->priv->root) { menu_t* m = vf->priv->current; vf->priv->current = m->parent ? m->parent : vf->priv->root; menu_close(m); } // Try to capture the last frame before pause, or fallback to use // last captured frame. if(pause_mpi && (mpi->w != pause_mpi->w || mpi->h != pause_mpi->h || mpi->imgfmt != pause_mpi->imgfmt)) { free_mp_image(pause_mpi); pause_mpi = NULL; } if (!pause_mpi) { pause_mpi = alloc_mpi(mpi->w,mpi->h,mpi->imgfmt); copy_mpi(pause_mpi,mpi); } else if (mpctx_get_osd_function(vf->priv->root->ctx) == OSD_PAUSE) copy_mpi(pause_mpi,mpi); if (vf->priv->current->show) { if (!mp_input_key_cb) mp_input_key_cb = key_cb; if(mpi->flags&MP_IMGFLAG_DIRECT) dmpi = mpi->priv; else { dmpi = vf_get_image(vf->next,mpi->imgfmt, MP_IMGTYPE_TEMP, MP_IMGFLAG_ACCEPT_STRIDE, mpi->w,mpi->h); copy_mpi(dmpi,mpi); } menu_draw(vf->priv->current,dmpi); } else { if(mp_input_key_cb) mp_input_key_cb = NULL; if(mpi->flags&MP_IMGFLAG_DIRECT) dmpi = mpi->priv; else { dmpi = vf_get_image(vf->next,mpi->imgfmt, MP_IMGTYPE_EXPORT, MP_IMGFLAG_ACCEPT_STRIDE, mpi->w,mpi->h); dmpi->stride[0] = mpi->stride[0]; dmpi->stride[1] = mpi->stride[1]; dmpi->stride[2] = mpi->stride[2]; dmpi->planes[0] = mpi->planes[0]; dmpi->planes[1] = mpi->planes[1]; dmpi->planes[2] = mpi->planes[2]; dmpi->priv = mpi->priv; } } return vf_next_put_image(vf,dmpi, pts); }
int write_image(struct mp_image *image, const struct mp_csp_details *csp, const struct image_writer_opts *opts, const char *filename) { struct mp_image *allocated_image = NULL; struct image_writer_opts defs = image_writer_opts_defaults; bool is_anamorphic = image->w != image->width || image->h != image->height; if (!opts) opts = &defs; const struct img_writer *writer = get_writer(opts); struct image_writer_ctx ctx = { opts, writer }; int destfmt = IMGFMT_RGB24; if (writer->pixfmts) { destfmt = writer->pixfmts[0]; // default to first pixel format for (int *fmt = writer->pixfmts; *fmt; fmt++) { if (*fmt == image->imgfmt) { destfmt = *fmt; break; } } } if (image->imgfmt != destfmt || is_anamorphic) { struct mp_image *dst = alloc_mpi(image->w, image->h, destfmt); struct SwsContext *sws = sws_getContextFromCmdLine_hq(image->width, image->height, image->imgfmt, dst->width, dst->height, dst->imgfmt); struct mp_csp_details colorspace = MP_CSP_DETAILS_DEFAULTS; if (csp) colorspace = *csp; // This is a property of the output device; images always use // full-range RGB. colorspace.levels_out = MP_CSP_LEVELS_PC; mp_sws_set_colorspace(sws, &colorspace); sws_scale(sws, (const uint8_t **)image->planes, image->stride, 0, image->height, dst->planes, dst->stride); sws_freeContext(sws); allocated_image = dst; image = dst; } FILE *fp = fopen(filename, "wb"); int success = 0; if (fp == NULL) { mp_msg(MSGT_CPLAYER, MSGL_ERR, "Error opening '%s' for writing!\n", filename); } else { success = writer->write(&ctx, image, fp); success = !fclose(fp) && success; if (!success) mp_msg(MSGT_CPLAYER, MSGL_ERR, "Error writing file '%s'!\n", filename); } free_mp_image(allocated_image); return success; }
static void draw_osd(struct vo *vo, struct osd_state *osd) { struct xvctx *ctx = vo->priv; struct mp_image img = get_xv_buffer(vo, ctx->current_buf); struct mp_rect *src = &ctx->src_rect; struct mp_rect *dst = &ctx->dst_rect; int dw = dst->x1 - dst->x0, dh = dst->y1 - dst->y0; int sw = src->x1 - src->x0, sh = src->y1 - src->y0; double xvpar = (double)dw / dh * sh / sw; struct mp_osd_res res = { .w = ctx->image_width, .h = ctx->image_height, .display_par = vo->monitor_par / xvpar, .video_par = vo->aspdat.par, }; osd_draw_on_image_bk(osd, res, osd->vo_pts, 0, ctx->osd_backup, &img); } static int redraw_frame(struct vo *vo) { struct xvctx *ctx = vo->priv; struct mp_image img = get_xv_buffer(vo, ctx->visible_buf); mp_draw_sub_backup_restore(ctx->osd_backup, &img); ctx->current_buf = ctx->visible_buf; return true; } static void flip_page(struct vo *vo) { struct xvctx *ctx = vo->priv; put_xvimage(vo, ctx->xvimage[ctx->current_buf]); /* remember the currently visible buffer */ ctx->visible_buf = ctx->current_buf; ctx->current_buf = (ctx->current_buf + 1) % ctx->num_buffers; XFlush(vo->x11->display); return; } static int draw_slice(struct vo *vo, uint8_t *image[], int stride[], int w, int h, int x, int y) { struct xvctx *ctx = vo->priv; uint8_t *dst; XvImage *current_image = ctx->xvimage[ctx->current_buf]; dst = current_image->data + current_image->offsets[0] + current_image->pitches[0] * y + x; memcpy_pic(dst, image[0], w, h, current_image->pitches[0], stride[0]); x /= 2; y /= 2; w /= 2; h /= 2; dst = current_image->data + current_image->offsets[1] + current_image->pitches[1] * y + x; if (ctx->image_format != IMGFMT_YV12) memcpy_pic(dst, image[1], w, h, current_image->pitches[1], stride[1]); else memcpy_pic(dst, image[2], w, h, current_image->pitches[1], stride[2]); dst = current_image->data + current_image->offsets[2] + current_image->pitches[2] * y + x; if (ctx->image_format == IMGFMT_YV12) memcpy_pic(dst, image[1], w, h, current_image->pitches[1], stride[1]); else memcpy_pic(dst, image[2], w, h, current_image->pitches[1], stride[2]); mp_draw_sub_backup_reset(ctx->osd_backup); return 0; } static mp_image_t *get_screenshot(struct vo *vo) { struct xvctx *ctx = vo->priv; struct mp_image img = get_xv_buffer(vo, ctx->visible_buf); struct mp_image *res = alloc_mpi(img.w, img.h, img.imgfmt); copy_mpi(res, &img); vf_clone_mpi_attributes(res, &img); // try to get an image without OSD mp_draw_sub_backup_restore(ctx->osd_backup, res); res->display_w = vo->aspdat.prew; res->display_h = vo->aspdat.preh; return res; } static uint32_t draw_image(struct vo *vo, mp_image_t *mpi) { struct xvctx *ctx = vo->priv; if (mpi->flags & MP_IMGFLAG_DRAW_CALLBACK) ; // done else if (mpi->flags & MP_IMGFLAG_PLANAR) draw_slice(vo, mpi->planes, mpi->stride, mpi->w, mpi->h, 0, 0); else if (mpi->flags & MP_IMGFLAG_YUV) // packed YUV: memcpy_pic(ctx->xvimage[ctx->current_buf]->data + ctx->xvimage[ctx->current_buf]->offsets[0], mpi->planes[0], mpi->w * (mpi->bpp / 8), mpi->h, ctx->xvimage[ctx->current_buf]->pitches[0], mpi->stride[0]); else return false; mp_draw_sub_backup_reset(ctx->osd_backup); return true; } static int query_format(struct xvctx *ctx, uint32_t format) { uint32_t i; int flag = VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW | VFCAP_OSD | VFCAP_ACCEPT_STRIDE; // FIXME! check for DOWN /* check image formats */ for (i = 0; i < ctx->formats; i++) { if (ctx->fo[i].id == format) return flag; //xv_format = fo[i].id; } return 0; }