/* Copy YV12 images */ static void copy_image_YV12( GstVaapiImageRaw *dst_image, GstVaapiImageRaw *src_image, const GstVaapiRectangle *rect ) { guchar *dst, *src; guint dst_stride, src_stride; guint i, x, y, w, h; /* Y plane */ dst_stride = dst_image->stride[0]; dst = dst_image->pixels[0] + rect->y * dst_stride + rect->x; src_stride = src_image->stride[0]; src = src_image->pixels[0] + rect->y * src_stride + rect->x; memcpy_pic(dst, dst_stride, src, src_stride, rect->width, rect->height); /* U/V planes */ x = rect->x / 2; y = rect->y / 2; w = rect->width / 2; h = rect->height / 2; for (i = 1; i < dst_image->num_planes; i++) { dst_stride = dst_image->stride[i]; dst = dst_image->pixels[i] + y * dst_stride + x; src_stride = src_image->stride[i]; src = src_image->pixels[i] + y * src_stride + x; memcpy_pic(dst, dst_stride, src, src_stride, w, h); } }
/** @brief libvo Callback: Draw slice * @return 0 on success */ static int draw_slice(uint8_t *src[], int stride[], int w,int h,int x,int y ) { char *my_src; /**< Pointer to the source image */ char *dst; /**< Pointer to the destination image */ int uv_stride; /**< Stride of the U/V planes */ /* If the D3D device is uncooperative (not initialized), return success. The device will be probed for reinitialization in the next flip_page() */ if (!priv->d3d_device) return 0; /* Lock the offscreen surface if it's not already locked. */ if (!priv->locked_rect.pBits) { if (FAILED(IDirect3DSurface9_LockRect(priv->d3d_surface, &priv->locked_rect, NULL, 0))) { mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Surface lock failure.\n"); return VO_FALSE; } } uv_stride = priv->locked_rect.Pitch / 2; /* Copy Y */ dst = priv->locked_rect.pBits; dst = dst + priv->locked_rect.Pitch * y + x; my_src = src[0]; memcpy_pic(dst, my_src, w, h, priv->locked_rect.Pitch, stride[0]); w /= 2; h /= 2; x /= 2; y /= 2; /* Copy U */ dst = priv->locked_rect.pBits; dst = dst + priv->locked_rect.Pitch * priv->src_height + uv_stride * y + x; if (priv->movie_src_fmt == MAKEFOURCC('Y','V','1','2')) my_src = src[2]; else my_src = src[1]; memcpy_pic(dst, my_src, w, h, uv_stride, stride[1]); /* Copy V */ dst = priv->locked_rect.pBits; dst = dst + priv->locked_rect.Pitch * priv->src_height + uv_stride * (priv->src_height / 2) + uv_stride * y + x; if (priv->movie_src_fmt == MAKEFOURCC('Y','V','1','2')) my_src=src[1]; else my_src=src[2]; memcpy_pic(dst, my_src, w, h, uv_stride, stride[2]); return 0; /* Success */ }
void copy_mpi(mp_image_t *dmpi, mp_image_t *mpi) { if(mpi->flags&MP_IMGFLAG_PLANAR){ memcpy_pic(dmpi->planes[0],mpi->planes[0], mpi->w, mpi->h, dmpi->stride[0],mpi->stride[0]); memcpy_pic(dmpi->planes[1],mpi->planes[1], mpi->chroma_width, mpi->chroma_height, dmpi->stride[1],mpi->stride[1]); memcpy_pic(dmpi->planes[2], mpi->planes[2], mpi->chroma_width, mpi->chroma_height, dmpi->stride[2],mpi->stride[2]); } else { memcpy_pic(dmpi->planes[0],mpi->planes[0], mpi->w*(dmpi->bpp/8), mpi->h, dmpi->stride[0],mpi->stride[0]); } }
/** * \brief Removes the logo from a plane (either luma or chroma). * * \param vf Not needed by this function, but needed by the blur function. * \param source The image to have it's logo removed. * \param destination Where the output image will be stored. * \param source_stride How far apart (in memory) two consecutive lines are. * \param destination Same as source_stride, but for the destination image. * \param width Width of the image. This is the same for source and destination. * \param height Height of the image. This is the same for source and destination. * \param is_image_direct If the image is direct, then source and destination are * the same and we can save a lot of time by not copying pixels that * haven't changed. * \param filter The image that stores the distance to the edge of the logo for * each pixel. * \param logo_start_x Smallest x-coordinate that contains at least 1 logo pixel. * \param logo_start_y Smallest y-coordinate that contains at least 1 logo pixel. * \param logo_end_x Largest x-coordinate that contains at least 1 logo pixel. * \param logo_end_y Largest y-coordinate that contains at least 1 logo pixel. * * This function processes an entire plane. Pixels outside of the logo are copied * to the output without change, and pixels inside the logo have the de-blurring * function applied. */ static void convert_yv12(const vf_instance_t * const vf, const char * const source, const int source_stride, const mp_image_t * const source_image, const int width, const int height, char * const destination, const int destination_stride, int is_image_direct, pgm_structure * filter, const int plane, const int logo_start_x, const int logo_start_y, const int logo_end_x, const int logo_end_y) { int y; int x; /* These pointers point to where we are getting our pixel data (inside mpi) and where we are storing it (inside dmpi). */ const unsigned char * source_line; unsigned char * destination_line; if (!is_image_direct) memcpy_pic(destination, source, width, height, destination_stride, source_stride); for (y = logo_start_y; y <= logo_end_y; y++) { source_line = (const unsigned char *) source + (source_stride * y); destination_line = (unsigned char *) destination + (destination_stride * y); for (x = logo_start_x; x <= logo_end_x; x++) { unsigned int output; if (filter->pixel[(y * filter->width) + x]) /* Only process if we are in the logo. */ { get_blur(vf, &output, filter, source_image, x, y, plane); destination_line[x] = output; } else /* Else just copy the data. */ if (!is_image_direct) destination_line[x] = source_line[x]; } } }
static int put_image(struct vf_instance *vf, mp_image_t *mpi, double pts){ mp_image_t *dmpi; if(!(mpi->flags&MP_IMGFLAG_DIRECT)){ // no DR, so get a new image! hope we'll get DR buffer: dmpi=ff_vf_get_image(vf->next,mpi->imgfmt, MP_IMGTYPE_TEMP, MP_IMGFLAG_ACCEPT_STRIDE|MP_IMGFLAG_PREFER_ALIGNED_STRIDE, mpi->width,mpi->height); ff_vf_clone_mpi_attributes(dmpi, mpi); }else{ dmpi=vf->dmpi; } vf->priv->mpeg2= mpi->qscale_type; if(vf->priv->log2_count || !(mpi->flags&MP_IMGFLAG_DIRECT)){ if(mpi->qscale || vf->priv->qp){ filter(vf->priv, dmpi->planes, mpi->planes, dmpi->stride, mpi->stride, mpi->w, mpi->h, mpi->qscale, mpi->qstride); }else{ memcpy_pic(dmpi->planes[0], mpi->planes[0], mpi->w, mpi->h, dmpi->stride[0], mpi->stride[0]); memcpy_pic(dmpi->planes[1], mpi->planes[1], mpi->w>>mpi->chroma_x_shift, mpi->h>>mpi->chroma_y_shift, dmpi->stride[1], mpi->stride[1]); memcpy_pic(dmpi->planes[2], mpi->planes[2], mpi->w>>mpi->chroma_x_shift, mpi->h>>mpi->chroma_y_shift, dmpi->stride[2], mpi->stride[2]); } } #if HAVE_MMX if(ff_gCpuCaps.hasMMX) __asm__ volatile ("emms\n\t"); #endif #if HAVE_MMX2 if(ff_gCpuCaps.hasMMX2) __asm__ volatile ("sfence\n\t"); #endif return ff_vf_next_put_image(vf,dmpi, pts); }
static void flip_page(void) { #ifndef USE_CONVERT2FB int out_offset = 0, in_offset = 0; memcpy_pic(center + out_offset, next_frame + in_offset, in_width * fb_pixel_size, in_height, fb_line_len, in_width * fb_pixel_size); #endif }
//Exact copy of vf.c void ff_vf_next_draw_slice(struct vf_instance *vf,unsigned char** src, int * stride,int w, int h, int x, int y){ if (vf->next->draw_slice) { vf->next->draw_slice(vf->next,src,stride,w,h,x,y); return; } if (!vf->dmpi) { ff_mp_msg(MSGT_VFILTER,MSGL_ERR,"draw_slice: dmpi not stored by vf_%s\n", vf->info->name); return; } if (!(vf->dmpi->flags & MP_IMGFLAG_PLANAR)) { memcpy_pic(vf->dmpi->planes[0]+y*vf->dmpi->stride[0]+vf->dmpi->bpp/8*x, src[0], vf->dmpi->bpp/8*w, h, vf->dmpi->stride[0], stride[0]); return; } memcpy_pic(vf->dmpi->planes[0]+y*vf->dmpi->stride[0]+x, src[0], w, h, vf->dmpi->stride[0], stride[0]); memcpy_pic(vf->dmpi->planes[1]+(y>>vf->dmpi->chroma_y_shift)*vf->dmpi->stride[1]+(x>>vf->dmpi->chroma_x_shift), src[1], w>>vf->dmpi->chroma_x_shift, h>>vf->dmpi->chroma_y_shift, vf->dmpi->stride[1], stride[1]); memcpy_pic(vf->dmpi->planes[2]+(y>>vf->dmpi->chroma_y_shift)*vf->dmpi->stride[2]+(x>>vf->dmpi->chroma_x_shift), src[2], w>>vf->dmpi->chroma_x_shift, h>>vf->dmpi->chroma_y_shift, vf->dmpi->stride[2], stride[2]); }
static int draw_slice(uint8_t *src[], int stride[], int w, int h, int x, int y) { uint8_t *in = src[0]; #ifdef USE_CONVERT2FB uint8_t *dest = center + (fb_line_len * y) + (x * fb_pixel_size); int next = fb_line_len; #else uint8_t *dest = next_frame + (in_width * y + x) * fb_pixel_size; int next = in_width * fb_pixel_size; #endif memcpy_pic(dest, in, w * fb_pixel_size, h, next, stride[0]); return 0; }
/* Copy NV12 images */ static void copy_image_NV12( GstVaapiImageRaw *dst_image, GstVaapiImageRaw *src_image, const GstVaapiRectangle *rect ) { guchar *dst, *src; guint dst_stride, src_stride; /* Y plane */ dst_stride = dst_image->stride[0]; dst = dst_image->pixels[0] + rect->y * dst_stride + rect->x; src_stride = src_image->stride[0]; src = src_image->pixels[0] + rect->y * src_stride + rect->x; memcpy_pic(dst, dst_stride, src, src_stride, rect->width, rect->height); /* UV plane */ dst_stride = dst_image->stride[1]; dst = dst_image->pixels[1] + (rect->y / 2) * dst_stride + (rect->x & -2); src_stride = src_image->stride[1]; src = src_image->pixels[1] + (rect->y / 2) * src_stride + (rect->x & -2); memcpy_pic(dst, dst_stride, src, src_stride, rect->width, rect->height / 2); }
/* Copy RGBA images */ static void copy_image_RGBA( GstVaapiImageRaw *dst_image, GstVaapiImageRaw *src_image, const GstVaapiRectangle *rect ) { guchar *dst, *src; guint dst_stride, src_stride; dst_stride = dst_image->stride[0]; dst = dst_image->pixels[0] + rect->y * dst_stride + rect->x; src_stride = src_image->stride[0]; src = src_image->pixels[0] + rect->y * src_stride + rect->x; memcpy_pic(dst, dst_stride, src, src_stride, 4 * rect->width, rect->height); }
static int put_image(struct vf_instance *vf, mp_image_t* mpi, double pts){ mp_image_t* dmpi; unsigned int bpp = mpi->bpp / 8; int x, y, w, h; dmpi = vf_get_image(vf->next, mpi->imgfmt, MP_IMGTYPE_TEMP, MP_IMGFLAG_ACCEPT_STRIDE | MP_IMGFLAG_PREFER_ALIGNED_STRIDE, mpi->w, mpi->h); memcpy_pic(dmpi->planes[0],mpi->planes[0],mpi->w*bpp, mpi->h, dmpi->stride[0],mpi->stride[0]); if(mpi->flags&MP_IMGFLAG_PLANAR && mpi->flags&MP_IMGFLAG_YUV){ memcpy_pic(dmpi->planes[1],mpi->planes[1], mpi->w>>mpi->chroma_x_shift, mpi->h>>mpi->chroma_y_shift, dmpi->stride[1],mpi->stride[1]); memcpy_pic(dmpi->planes[2],mpi->planes[2], mpi->w>>mpi->chroma_x_shift, mpi->h>>mpi->chroma_y_shift, dmpi->stride[2],mpi->stride[2]); }
static uint32_t draw_image(mp_image_t *mpi){ int buf = 0; tdfx_vid_agp_move_t mov; tdfx_vid_yuv_t yuv; int p; uint8_t* planes[3]; #ifdef VERBOSE printf("Draw image %d\n",buf); #endif if(mpi->flags & MP_IMGFLAG_DIRECT) buf = (int)mpi->priv; switch(mpi->imgfmt) { case IMGFMT_YUY2: case IMGFMT_UYVY: case IMGFMT_BGR8: case IMGFMT_BGR15: case IMGFMT_BGR16: case IMGFMT_BGR24: case IMGFMT_BGR32: if(!(mpi->flags&(MP_IMGFLAG_DIRECT|MP_IMGFLAG_DRAW_CALLBACK))) { // copy to agp_mem #ifdef VERBOSE printf("Memcpy\n"); #endif planes[0] = agp_mem + buf * buffer_size; mem2agpcpy_pic(planes[0],mpi->planes[0],src_bpp*mpi->width,mpi->height, buffer_stride[0],mpi->stride[0]); } else planes[0] = agp_mem + buf * buffer_size; mov.move2 = TDFX_VID_MOVE_2_PACKED; mov.width = mpi->width*((mpi->bpp+7)/8); mov.height = mpi->height; mov.src = planes[0] - agp_mem; mov.src_stride = buffer_stride[0]; mov.dst = back_buffer; mov.dst_stride = src_stride; if(ioctl(tdfx_fd,TDFX_VID_AGP_MOVE,&mov)) mp_msg(MSGT_VO,MSGL_WARN, MSGTR_LIBVO_TDFXVID_AgpMoveFailed); break; case IMGFMT_YV12: case IMGFMT_I420: if(!(mpi->flags&(MP_IMGFLAG_DIRECT|MP_IMGFLAG_DRAW_CALLBACK))) { // Copy to agp mem #ifdef VERBOSE printf("Memcpy\n"); #endif planes[0] = agp_mem + buf * buffer_size; memcpy_pic(planes[0],mpi->planes[0],mpi->width,mpi->height, buffer_stride[0],mpi->stride[0]); planes[1] = planes[0] + (mpi->height*mpi->stride[0]); memcpy_pic(planes[1],mpi->planes[1],mpi->chroma_width,mpi->chroma_height, buffer_stride[1],mpi->stride[1]); planes[2] = planes[1] + (mpi->chroma_height*mpi->stride[1]); memcpy_pic(planes[2],mpi->planes[2],mpi->chroma_width,mpi->chroma_height, buffer_stride[2],mpi->stride[2]); } else { planes[0] = agp_mem + buf * buffer_size; planes[1] = planes[0] + buffer_stride[0] * src_height; planes[2] = planes[1] + buffer_stride[1] * src_height/2; } // Setup the yuv thing yuv.base = back_buffer; yuv.stride = src_stride; if(ioctl(tdfx_fd,TDFX_VID_SET_YUV,&yuv)) { mp_msg(MSGT_VO,MSGL_WARN, MSGTR_LIBVO_TDFXVID_SetYuvFailed); break; } // Now agp move that // Y mov.move2 = TDFX_VID_MOVE_2_YUV; mov.width = mpi->width; mov.height = mpi->height; mov.src = planes[0] - agp_mem; mov.src_stride = buffer_stride[0]; mov.dst = 0x0; mov.dst_stride = TDFX_VID_YUV_STRIDE; if(ioctl(tdfx_fd,TDFX_VID_AGP_MOVE,&mov)) { mp_msg(MSGT_VO,MSGL_WARN, MSGTR_LIBVO_TDFXVID_AgpMoveFailedOnYPlane); break; } //return 0; // U p = mpi->imgfmt == IMGFMT_YV12 ? 1 : 2; mov.width = mpi->chroma_width; mov.height = mpi->chroma_height; mov.src = planes[p] - agp_mem; mov.src_stride = buffer_stride[p]; mov.dst += TDFX_VID_YUV_PLANE_SIZE; if(ioctl(tdfx_fd,TDFX_VID_AGP_MOVE,&mov)) { mp_msg(MSGT_VO,MSGL_WARN, MSGTR_LIBVO_TDFXVID_AgpMoveFailedOnUPlane); break; } // V p = mpi->imgfmt == IMGFMT_YV12 ? 2 : 1; mov.src = planes[p] - agp_mem; mov.src_stride = buffer_stride[p]; mov.dst += TDFX_VID_YUV_PLANE_SIZE; if(ioctl(tdfx_fd,TDFX_VID_AGP_MOVE,&mov)) { mp_msg(MSGT_VO,MSGL_WARN, MSGTR_LIBVO_TDFXVID_AgpMoveFailedOnVPlane); break; } break; default: mp_msg(MSGT_VO,MSGL_WARN, MSGTR_LIBVO_TDFXVID_UnknownFormat,mpi->imgfmt); return VO_TRUE; } return VO_TRUE; }
static int put_image(struct vf_instance *vf, mp_image_t *mpi, double pts) { int ret = 0; mp_image_t *dmpi; switch (vf->priv->mode) { case 0: dmpi = vf->priv->dmpi; if (dmpi == NULL) { dmpi = ff_vf_get_image(vf->next, mpi->imgfmt, MP_IMGTYPE_STATIC, MP_IMGFLAG_ACCEPT_STRIDE | MP_IMGFLAG_PRESERVE, mpi->width, mpi->height*2); vf->priv->dmpi = dmpi; memcpy_pic(dmpi->planes[0], mpi->planes[0], mpi->w, mpi->h, dmpi->stride[0]*2, mpi->stride[0]); if (mpi->flags & MP_IMGFLAG_PLANAR) { memcpy_pic(dmpi->planes[1], mpi->planes[1], mpi->chroma_width, mpi->chroma_height, dmpi->stride[1]*2, mpi->stride[1]); memcpy_pic(dmpi->planes[2], mpi->planes[2], mpi->chroma_width, mpi->chroma_height, dmpi->stride[2]*2, mpi->stride[2]); } } else { vf->priv->dmpi = NULL; memcpy_pic(dmpi->planes[0]+dmpi->stride[0], mpi->planes[0], mpi->w, mpi->h, dmpi->stride[0]*2, mpi->stride[0]); if (mpi->flags & MP_IMGFLAG_PLANAR) { memcpy_pic(dmpi->planes[1]+dmpi->stride[1], mpi->planes[1], mpi->chroma_width, mpi->chroma_height, dmpi->stride[1]*2, mpi->stride[1]); memcpy_pic(dmpi->planes[2]+dmpi->stride[2], mpi->planes[2], mpi->chroma_width, mpi->chroma_height, dmpi->stride[2]*2, mpi->stride[2]); } ret = ff_vf_next_put_image(vf, dmpi, MP_NOPTS_VALUE); } break; case 1: if (vf->priv->frame & 1) ret = ff_vf_next_put_image(vf, mpi, MP_NOPTS_VALUE); break; case 2: if ((vf->priv->frame & 1) == 0) ret = ff_vf_next_put_image(vf, mpi, MP_NOPTS_VALUE); break; case 3: dmpi = ff_vf_get_image(vf->next, mpi->imgfmt, MP_IMGTYPE_TEMP, MP_IMGFLAG_ACCEPT_STRIDE, mpi->width, mpi->height*2); /* fixme, just clear alternate lines */ ff_vf_mpi_clear(dmpi, 0, 0, dmpi->w, dmpi->h); if ((vf->priv->frame & 1) == 0) { memcpy_pic(dmpi->planes[0], mpi->planes[0], mpi->w, mpi->h, dmpi->stride[0]*2, mpi->stride[0]); if (mpi->flags & MP_IMGFLAG_PLANAR) { memcpy_pic(dmpi->planes[1], mpi->planes[1], mpi->chroma_width, mpi->chroma_height, dmpi->stride[1]*2, mpi->stride[1]); memcpy_pic(dmpi->planes[2], mpi->planes[2], mpi->chroma_width, mpi->chroma_height, dmpi->stride[2]*2, mpi->stride[2]); } } else { memcpy_pic(dmpi->planes[0]+dmpi->stride[0], mpi->planes[0], mpi->w, mpi->h, dmpi->stride[0]*2, mpi->stride[0]); if (mpi->flags & MP_IMGFLAG_PLANAR) { memcpy_pic(dmpi->planes[1]+dmpi->stride[1], mpi->planes[1], mpi->chroma_width, mpi->chroma_height, dmpi->stride[1]*2, mpi->stride[1]); memcpy_pic(dmpi->planes[2]+dmpi->stride[2], mpi->planes[2], mpi->chroma_width, mpi->chroma_height, dmpi->stride[2]*2, mpi->stride[2]); } } ret = ff_vf_next_put_image(vf, dmpi, MP_NOPTS_VALUE); break; case 4: // Interleave even lines (only) from Frame 'i' with odd // lines (only) from Frame 'i+1', halving the Frame // rate and preserving image height. dmpi = vf->priv->dmpi; // @@ Need help: Should I set dmpi->fields to indicate // that the (new) frame will be interlaced!? E.g. ... // dmpi->fields |= MP_IMGFIELD_INTERLACED; // dmpi->fields |= MP_IMGFIELD_TOP_FIRST; // etc. if (dmpi == NULL) { dmpi = ff_vf_get_image(vf->next, mpi->imgfmt, MP_IMGTYPE_STATIC, MP_IMGFLAG_ACCEPT_STRIDE | MP_IMGFLAG_PRESERVE, mpi->width, mpi->height); vf->priv->dmpi = dmpi; my_memcpy_pic(dmpi->planes[0], mpi->planes[0], mpi->w, mpi->h/2, dmpi->stride[0]*2, mpi->stride[0]*2); if (mpi->flags & MP_IMGFLAG_PLANAR) { my_memcpy_pic(dmpi->planes[1], mpi->planes[1], mpi->chroma_width, mpi->chroma_height/2, dmpi->stride[1]*2, mpi->stride[1]*2); my_memcpy_pic(dmpi->planes[2], mpi->planes[2], mpi->chroma_width, mpi->chroma_height/2, dmpi->stride[2]*2, mpi->stride[2]*2); } } else { vf->priv->dmpi = NULL; my_memcpy_pic(dmpi->planes[0]+dmpi->stride[0], mpi->planes[0]+mpi->stride[0], mpi->w, mpi->h/2, dmpi->stride[0]*2, mpi->stride[0]*2); if (mpi->flags & MP_IMGFLAG_PLANAR) { my_memcpy_pic(dmpi->planes[1]+dmpi->stride[1], mpi->planes[1]+mpi->stride[1], mpi->chroma_width, mpi->chroma_height/2, dmpi->stride[1]*2, mpi->stride[1]*2); my_memcpy_pic(dmpi->planes[2]+dmpi->stride[2], mpi->planes[2]+mpi->stride[2], mpi->chroma_width, mpi->chroma_height/2, dmpi->stride[2]*2, mpi->stride[2]*2); } ret = ff_vf_next_put_image(vf, dmpi, MP_NOPTS_VALUE); } break; } vf->priv->frame++; return ret; }
static int put_image(struct vf_instance *vf, mp_image_t *mpi, double pts) { struct pullup_context *c = vf->priv->ctx; struct pullup_buffer *b; struct pullup_frame *f; mp_image_t *dmpi; int ret; int p; int i; if (!vf->priv->init) init_pullup(vf, mpi); if (mpi->flags & MP_IMGFLAG_DIRECT) { b = mpi->priv; mpi->priv = 0; } else { b = pullup_get_buffer(c, 2); if (!b) { mp_msg(MSGT_VFILTER,MSGL_ERR,"Could not get buffer from pullup!\n"); f = pullup_get_frame(c); pullup_release_frame(f); return 0; } memcpy_pic(b->planes[0], mpi->planes[0], mpi->w, mpi->h, c->stride[0], mpi->stride[0]); if (mpi->flags & MP_IMGFLAG_PLANAR) { memcpy_pic(b->planes[1], mpi->planes[1], mpi->chroma_width, mpi->chroma_height, c->stride[1], mpi->stride[1]); memcpy_pic(b->planes[2], mpi->planes[2], mpi->chroma_width, mpi->chroma_height, c->stride[2], mpi->stride[2]); } } if (mpi->qscale) { fast_memcpy(b->planes[3], mpi->qscale, c->w[3]); fast_memcpy(b->planes[3]+c->w[3], mpi->qscale, c->w[3]); } p = mpi->fields & MP_IMGFIELD_TOP_FIRST ? 0 : (mpi->fields & MP_IMGFIELD_ORDERED ? 1 : 0); pullup_submit_field(c, b, p); pullup_submit_field(c, b, p^1); if (mpi->fields & MP_IMGFIELD_REPEAT_FIRST) pullup_submit_field(c, b, p); pullup_release_buffer(b, 2); f = pullup_get_frame(c); /* Fake yes for first few frames (buffer depth) to keep from * breaking A/V sync with G1's bad architecture... */ if (!f) return vf->priv->fakecount ? (--vf->priv->fakecount,1) : 0; if (f->length < 2) { pullup_release_frame(f); f = pullup_get_frame(c); if (!f) return 0; if (f->length < 2) { pullup_release_frame(f); if (!(mpi->fields & MP_IMGFIELD_REPEAT_FIRST)) return 0; f = pullup_get_frame(c); if (!f) return 0; if (f->length < 2) { pullup_release_frame(f); return 0; } } } #if 0 /* Average qscale tables from both frames. */ if (mpi->qscale) { for (i=0; i<c->w[3]; i++) { vf->priv->qbuf[i] = (f->ofields[0]->planes[3][i] + f->ofields[1]->planes[3][i+c->w[3]])>>1; } } #else /* Take worst of qscale tables from both frames. */ if (mpi->qscale) { for (i=0; i<c->w[3]; i++) { vf->priv->qbuf[i] = MAX(f->ofields[0]->planes[3][i], f->ofields[1]->planes[3][i+c->w[3]]); } } #endif /* If the frame isn't already exportable... */ while (!f->buffer) { dmpi = vf_get_image(vf->next, mpi->imgfmt, MP_IMGTYPE_TEMP, MP_IMGFLAG_ACCEPT_STRIDE, mpi->width, mpi->height); /* FIXME: Is it ok to discard dmpi if it's not direct? */ if (!(dmpi->flags & MP_IMGFLAG_DIRECT)) { pullup_pack_frame(c, f); break; } /* Direct render fields into output buffer */ my_memcpy_pic(dmpi->planes[0], f->ofields[0]->planes[0], mpi->w, mpi->h/2, dmpi->stride[0]*2, c->stride[0]*2); my_memcpy_pic(dmpi->planes[0] + dmpi->stride[0], f->ofields[1]->planes[0] + c->stride[0], mpi->w, mpi->h/2, dmpi->stride[0]*2, c->stride[0]*2); if (mpi->flags & MP_IMGFLAG_PLANAR) { my_memcpy_pic(dmpi->planes[1], f->ofields[0]->planes[1], mpi->chroma_width, mpi->chroma_height/2, dmpi->stride[1]*2, c->stride[1]*2); my_memcpy_pic(dmpi->planes[1] + dmpi->stride[1], f->ofields[1]->planes[1] + c->stride[1], mpi->chroma_width, mpi->chroma_height/2, dmpi->stride[1]*2, c->stride[1]*2); my_memcpy_pic(dmpi->planes[2], f->ofields[0]->planes[2], mpi->chroma_width, mpi->chroma_height/2, dmpi->stride[2]*2, c->stride[2]*2); my_memcpy_pic(dmpi->planes[2] + dmpi->stride[2], f->ofields[1]->planes[2] + c->stride[2], mpi->chroma_width, mpi->chroma_height/2, dmpi->stride[2]*2, c->stride[2]*2); } pullup_release_frame(f); if (mpi->qscale) { dmpi->qscale = vf->priv->qbuf; dmpi->qstride = mpi->qstride; dmpi->qscale_type = mpi->qscale_type; } return vf_next_put_image(vf, dmpi, MP_NOPTS_VALUE); } dmpi = vf_get_image(vf->next, mpi->imgfmt, MP_IMGTYPE_EXPORT, MP_IMGFLAG_ACCEPT_STRIDE, mpi->width, mpi->height); dmpi->planes[0] = f->buffer->planes[0]; dmpi->planes[1] = f->buffer->planes[1]; dmpi->planes[2] = f->buffer->planes[2]; dmpi->stride[0] = c->stride[0]; dmpi->stride[1] = c->stride[1]; dmpi->stride[2] = c->stride[2]; if (mpi->qscale) { dmpi->qscale = vf->priv->qbuf; dmpi->qstride = mpi->qstride; dmpi->qscale_type = mpi->qscale_type; } ret = vf_next_put_image(vf, dmpi, MP_NOPTS_VALUE); pullup_release_frame(f); return ret; }
static int put_image(struct vf_instance* vf, mp_image_t* mpi, double pts){ int buf_x=0, buf_y=0, buf_pos=0; int have, got, want; int xpos=0, ypos=0, pos=0; unsigned char red=0, green=0, blue=0; int alpha; mp_image_t* dmpi; dmpi = vf_get_image(vf->next, mpi->imgfmt, MP_IMGTYPE_TEMP, MP_IMGFLAG_ACCEPT_STRIDE | MP_IMGFLAG_PREFER_ALIGNED_STRIDE, mpi->w, mpi->h); memcpy_pic( dmpi->planes[0], mpi->planes[0], mpi->width, mpi->height, dmpi->stride[0], mpi->stride[0] ); memcpy_pic( dmpi->planes[1], mpi->planes[1], mpi->chroma_width, mpi->chroma_height, dmpi->stride[1], mpi->stride[1] ); memcpy_pic( dmpi->planes[2], mpi->planes[2], mpi->chroma_width, mpi->chroma_height, dmpi->stride[2], mpi->stride[2] ); if(vf->priv->stream_fd >= 0) { struct timeval tv; int ready; FD_SET( vf->priv->stream_fd, &vf->priv->stream_fdset ); tv.tv_sec=0; tv.tv_usec=0; ready = select( vf->priv->stream_fd+1, &vf->priv->stream_fdset, NULL, NULL, &tv ); if(ready > 0) { // We've got new data from the FIFO char cmd[20], args[100]; int imgw,imgh,imgx,imgy,clear,imgalpha,pxsz=1,command; unsigned char *buffer = NULL; if(! _read_cmd( vf->priv->stream_fd, cmd, args) ) { mp_msg(MSGT_VFILTER, MSGL_ERR, "\nvf_bmovl: Error reading commands: %s\n\n", strerror(errno)); return FALSE; } mp_msg(MSGT_VFILTER, MSGL_DBG2, "\nDEBUG: Got: %s+%s\n", cmd, args); command=NONE; if ( strncmp(cmd,"RGBA32",6)==0 ) { pxsz=4; command = IMG_RGBA32; } else if( strncmp(cmd,"ABGR32",6)==0 ) { pxsz=4; command = IMG_ABGR32; } else if( strncmp(cmd,"RGB24" ,5)==0 ) { pxsz=3; command = IMG_RGB24; } else if( strncmp(cmd,"BGR24" ,5)==0 ) { pxsz=3; command = IMG_BGR24; } else if( strncmp(cmd,"CLEAR" ,5)==0 ) { pxsz=1; command = CMD_CLEAR; } else if( strncmp(cmd,"ALPHA" ,5)==0 ) { pxsz=1; command = CMD_ALPHA; } else if( strncmp(cmd,"OPAQUE",6)==0 ) vf->priv->opaque=TRUE; else if( strncmp(cmd,"SHOW", 4)==0 ) vf->priv->hidden=FALSE; else if( strncmp(cmd,"HIDE", 4)==0 ) vf->priv->hidden=TRUE; else if( strncmp(cmd,"FLUSH" ,5)==0 ) return vf_next_put_image(vf, dmpi, MP_NOPTS_VALUE); else { mp_msg(MSGT_VFILTER, MSGL_WARN, "\nvf_bmovl: Unknown command: '%s'. Ignoring.\n", cmd); return vf_next_put_image(vf, dmpi, MP_NOPTS_VALUE); } if(command == CMD_ALPHA) { sscanf( args, "%d %d %d %d %d", &imgw, &imgh, &imgx, &imgy, &imgalpha); mp_msg(MSGT_VFILTER, MSGL_DBG2, "\nDEBUG: ALPHA: %d %d %d %d %d\n\n", imgw, imgh, imgx, imgy, imgalpha); if(imgw==0 && imgh==0) vf->priv->opaque=FALSE; } if(command & IS_RAWIMG) { sscanf( args, "%d %d %d %d %d %d", &imgw, &imgh, &imgx, &imgy, &imgalpha, &clear); mp_msg(MSGT_VFILTER, MSGL_DBG2, "\nDEBUG: RAWIMG: %d %d %d %d %d %d\n\n", imgw, imgh, imgx, imgy, imgalpha, clear); buffer = malloc(imgw*imgh*pxsz); if(!buffer) { mp_msg(MSGT_VFILTER, MSGL_WARN, "\nvf_bmovl: Couldn't allocate temporary buffer! Skipping...\n\n"); return vf_next_put_image(vf, dmpi, MP_NOPTS_VALUE); } /* pipes/sockets might need multiple calls to read(): */ want = (imgw*imgh*pxsz); have = 0; while (have < want) { got = read( vf->priv->stream_fd, buffer+have, want-have ); if (got == 0) { mp_msg(MSGT_VFILTER, MSGL_WARN, "\nvf_bmovl: premature EOF...\n\n"); break; } if (got < 0) { mp_msg(MSGT_VFILTER, MSGL_WARN, "\nvf_bmovl: read error: %s\n\n", strerror(errno)); break; } have += got; } mp_msg(MSGT_VFILTER, MSGL_DBG2, "Got %d bytes... (wanted %d)\n", have, want ); if(clear) { memset( vf->priv->bitmap.y, 0, vf->priv->w*vf->priv->h ); memset( vf->priv->bitmap.u, 128, vf->priv->w*vf->priv->h/4 ); memset( vf->priv->bitmap.v, 128, vf->priv->w*vf->priv->h/4 ); memset( vf->priv->bitmap.a, 0, vf->priv->w*vf->priv->h ); memset( vf->priv->bitmap.oa, 0, vf->priv->w*vf->priv->h ); vf->priv->x1 = dmpi->width; vf->priv->y1 = dmpi->height; vf->priv->x2 = vf->priv->y2 = 0; } // Define how much of our bitmap that contains graphics! vf->priv->x1 = av_clip(imgx, 0, vf->priv->x1); vf->priv->y1 = av_clip(imgy, 0, vf->priv->y1); vf->priv->x2 = av_clip(imgx + imgw, vf->priv->x2, vf->priv->w); vf->priv->y2 = av_clip(imgy + imgh, vf->priv->y2, vf->priv->h); } if( command == CMD_CLEAR ) { sscanf( args, "%d %d %d %d", &imgw, &imgh, &imgx, &imgy); mp_msg(MSGT_VFILTER, MSGL_DBG2, "\nDEBUG: CLEAR: %d %d %d %d\n\n", imgw, imgh, imgx, imgy); for( ypos=imgy ; (ypos < (imgy+imgh)) && (ypos < vf->priv->y2) ; ypos++ ) { memset( vf->priv->bitmap.y + (ypos*vf->priv->w) + imgx, 0, imgw ); memset( vf->priv->bitmap.a + (ypos*vf->priv->w) + imgx, 0, imgw ); memset( vf->priv->bitmap.oa + (ypos*vf->priv->w) + imgx, 0, imgw ); if(ypos%2) { memset( vf->priv->bitmap.u + ((ypos/2)*dmpi->stride[1]) + (imgx/2), 128, imgw/2 ); memset( vf->priv->bitmap.v + ((ypos/2)*dmpi->stride[2]) + (imgx/2), 128, imgw/2 ); } } // Recalculate area that contains graphics if( (imgx <= vf->priv->x1) && ( (imgw+imgx) >= vf->priv->x2) ) { if( (imgy <= vf->priv->y1) && ( (imgy+imgh) >= vf->priv->y1) ) vf->priv->y1 = imgy+imgh; if( (imgy <= vf->priv->y2) && ( (imgy+imgh) >= vf->priv->y2) ) vf->priv->y2 = imgy; } if( (imgy <= vf->priv->y1) && ( (imgy+imgh) >= vf->priv->y2) ) { if( (imgx <= vf->priv->x1) && ( (imgx+imgw) >= vf->priv->x1) ) vf->priv->x1 = imgx+imgw; if( (imgx <= vf->priv->x2) && ( (imgx+imgw) >= vf->priv->x2) ) vf->priv->x2 = imgx; } return vf_next_put_image(vf, dmpi, MP_NOPTS_VALUE); } for( buf_y=0 ; (buf_y < imgh) && (buf_y < (vf->priv->h-imgy)) ; buf_y++ ) { for( buf_x=0 ; (buf_x < (imgw*pxsz)) && (buf_x < ((vf->priv->w+imgx)*pxsz)) ; buf_x += pxsz ) { if(command & IS_RAWIMG) buf_pos = (buf_y * imgw * pxsz) + buf_x; pos = ((buf_y+imgy) * vf->priv->w) + ((buf_x/pxsz)+imgx); switch(command) { case IMG_RGBA32: red = buffer[buf_pos+0]; green = buffer[buf_pos+1]; blue = buffer[buf_pos+2]; alpha = buffer[buf_pos+3]; break; case IMG_ABGR32: alpha = buffer[buf_pos+0]; blue = buffer[buf_pos+1]; green = buffer[buf_pos+2]; red = buffer[buf_pos+3]; break; case IMG_RGB24: red = buffer[buf_pos+0]; green = buffer[buf_pos+1]; blue = buffer[buf_pos+2]; alpha = 0xFF; break; case IMG_BGR24: blue = buffer[buf_pos+0]; green = buffer[buf_pos+1]; red = buffer[buf_pos+2]; alpha = 0xFF; break; case CMD_ALPHA: vf->priv->bitmap.a[pos] = INRANGE((vf->priv->bitmap.oa[pos]+imgalpha),0,255); break; default: mp_msg(MSGT_VFILTER, MSGL_ERR, "vf_bmovl: Internal error!\n"); return FALSE; } if( command & IS_RAWIMG ) { vf->priv->bitmap.y[pos] = rgb2y(red,green,blue); vf->priv->bitmap.oa[pos] = alpha; vf->priv->bitmap.a[pos] = INRANGE((alpha+imgalpha),0,255); if((buf_y%2) && ((buf_x/pxsz)%2)) { pos = ( ((buf_y+imgy)/2) * dmpi->stride[1] ) + (((buf_x/pxsz)+imgx)/2); vf->priv->bitmap.u[pos] = rgb2u(red,green,blue); vf->priv->bitmap.v[pos] = rgb2v(red,green,blue); } } } // for buf_x } // for buf_y free (buffer); } else if(ready < 0) { mp_msg(MSGT_VFILTER, MSGL_WARN, "\nvf_bmovl: Error %d in fifo: %s\n\n", errno, strerror(errno)); } } if(vf->priv->hidden) return vf_next_put_image(vf, dmpi, MP_NOPTS_VALUE); if(vf->priv->opaque) { // Just copy buffer memory to screen for( ypos=vf->priv->y1 ; ypos < vf->priv->y2 ; ypos++ ) { fast_memcpy( dmpi->planes[0] + (ypos*dmpi->stride[0]) + vf->priv->x1, vf->priv->bitmap.y + (ypos*vf->priv->w) + vf->priv->x1, vf->priv->x2 - vf->priv->x1 ); if(ypos%2) { fast_memcpy( dmpi->planes[1] + ((ypos/2)*dmpi->stride[1]) + (vf->priv->x1/2), vf->priv->bitmap.u + (((ypos/2)*(vf->priv->w)/2)) + (vf->priv->x1/2), (vf->priv->x2 - vf->priv->x1)/2 ); fast_memcpy( dmpi->planes[2] + ((ypos/2)*dmpi->stride[2]) + (vf->priv->x1/2), vf->priv->bitmap.v + (((ypos/2)*(vf->priv->w)/2)) + (vf->priv->x1/2), (vf->priv->x2 - vf->priv->x1)/2 ); } } } else { // Blit the bitmap to the videoscreen, pixel for pixel for( ypos=vf->priv->y1 ; ypos < vf->priv->y2 ; ypos++ ) { for ( xpos=vf->priv->x1 ; xpos < vf->priv->x2 ; xpos++ ) { pos = (ypos * dmpi->stride[0]) + xpos; alpha = vf->priv->bitmap.a[pos]; if (alpha == 0) continue; // Completly transparent pixel if (alpha == 255) { // Opaque pixel dmpi->planes[0][pos] = vf->priv->bitmap.y[pos]; if ((ypos%2) && (xpos%2)) { pos = ( (ypos/2) * dmpi->stride[1] ) + (xpos/2); dmpi->planes[1][pos] = vf->priv->bitmap.u[pos]; dmpi->planes[2][pos] = vf->priv->bitmap.v[pos]; } } else { // Alphablended pixel dmpi->planes[0][pos] = ((255 - alpha) * (int)dmpi->planes[0][pos] + alpha * (int)vf->priv->bitmap.y[pos]) >> 8; if ((ypos%2) && (xpos%2)) { pos = ( (ypos/2) * dmpi->stride[1] ) + (xpos/2); dmpi->planes[1][pos] = ((255 - alpha) * (int)dmpi->planes[1][pos] + alpha * (int)vf->priv->bitmap.u[pos]) >> 8; dmpi->planes[2][pos] = ((255 - alpha) * (int)dmpi->planes[2][pos] + alpha * (int)vf->priv->bitmap.v[pos]) >> 8; } } } // for xpos } // for ypos } // if !opaque
static int demux_avs_fill_buffer(demuxer_t *demuxer, demux_stream_t *ds) { AVS_VideoFrame *curr_frame; demux_packet_t *dp = NULL; AVS_T *AVS = demuxer->priv; if (ds == demuxer->video) { sh_video_t *sh_video = demuxer->video->sh; char *dst; int w, h; if (AVS->video_info->num_frames <= AVS->frameno) return 0; // EOF curr_frame = AVS->avs_get_frame(AVS->clip, AVS->frameno); if (!curr_frame) { mp_msg(MSGT_DEMUX, MSGL_V, "AVS: error getting frame -- EOF??\n"); return 0; } w = curr_frame->row_size; h = curr_frame->height; dp = new_demux_packet(w * h + 2 * (w / 2) * (h / 2)); dp->pts=AVS->frameno / sh_video->fps; dst = dp->buffer; memcpy_pic(dst, curr_frame->vfb->data + curr_frame->offset, w, h, w, curr_frame->pitch); dst += w * h; w /= 2; h /= 2; memcpy_pic(dst, curr_frame->vfb->data + curr_frame->offsetV, w, h, w, curr_frame->pitchUV); dst += w * h; memcpy_pic(dst, curr_frame->vfb->data + curr_frame->offsetU, w, h, w, curr_frame->pitchUV); ds_add_packet(demuxer->video, dp); AVS->frameno++; AVS->avs_release_video_frame(curr_frame); } /* Audio */ if (ds == demuxer->audio) { sh_audio_t *sh_audio = ds->sh; int samples = sh_audio->samplerate; uint64_t l; samples = FFMIN(samples, AVS->video_info->num_audio_samples - AVS->sampleno); if (!samples) return 0; l = samples * sh_audio->channels * sh_audio->samplesize; if (l > INT_MAX) { mp_msg(MSGT_DEMUX, MSGL_FATAL, "AVS: audio packet too big\n"); return 0; } dp = new_demux_packet(l); dp->pts = AVS->sampleno / sh_audio->samplerate; if (AVS->avs_get_audio(AVS->clip, dp->buffer, AVS->sampleno, samples)) { mp_msg(MSGT_DEMUX, MSGL_V, "AVS: avs_get_audio() failed\n"); return 0; } ds_add_packet(demuxer->audio, dp); AVS->sampleno += samples; } return 1; }
static int put_image(struct vf_instance *vf, mp_image_t *mpi, double pts) { mp_image_t *dmpi; int ret; int w = (IMGFMT_IS_YUVP16(mpi->imgfmt) ? 2 : 1) * mpi->w; int chroma_width = (IMGFMT_IS_YUVP16(mpi->imgfmt) ? 2 : 1) * mpi->chroma_width; vf->priv->frame = (vf->priv->frame+1)%4; dmpi = vf_get_image(vf->next, mpi->imgfmt, MP_IMGTYPE_STATIC, MP_IMGFLAG_ACCEPT_STRIDE | MP_IMGFLAG_PRESERVE, mpi->width, mpi->height); ret = 0; // 0/0 1/1 2/2 2/3 3/0 switch (vf->priv->frame) { case 0: my_memcpy_pic(dmpi->planes[0]+dmpi->stride[0], mpi->planes[0]+mpi->stride[0], w, mpi->h/2, dmpi->stride[0]*2, mpi->stride[0]*2); if (mpi->flags & MP_IMGFLAG_PLANAR) { my_memcpy_pic(dmpi->planes[1]+dmpi->stride[1], mpi->planes[1]+mpi->stride[1], chroma_width, mpi->chroma_height/2, dmpi->stride[1]*2, mpi->stride[1]*2); my_memcpy_pic(dmpi->planes[2]+dmpi->stride[2], mpi->planes[2]+mpi->stride[2], chroma_width, mpi->chroma_height/2, dmpi->stride[2]*2, mpi->stride[2]*2); } ret = vf_next_put_image(vf, dmpi, MP_NOPTS_VALUE); /* Fallthrough */ case 1: case 2: memcpy_pic(dmpi->planes[0], mpi->planes[0], w, mpi->h, dmpi->stride[0], mpi->stride[0]); if (mpi->flags & MP_IMGFLAG_PLANAR) { memcpy_pic(dmpi->planes[1], mpi->planes[1], chroma_width, mpi->chroma_height, dmpi->stride[1], mpi->stride[1]); memcpy_pic(dmpi->planes[2], mpi->planes[2], chroma_width, mpi->chroma_height, dmpi->stride[2], mpi->stride[2]); } return vf_next_put_image(vf, dmpi, MP_NOPTS_VALUE) || ret; case 3: my_memcpy_pic(dmpi->planes[0]+dmpi->stride[0], mpi->planes[0]+mpi->stride[0], w, mpi->h/2, dmpi->stride[0]*2, mpi->stride[0]*2); if (mpi->flags & MP_IMGFLAG_PLANAR) { my_memcpy_pic(dmpi->planes[1]+dmpi->stride[1], mpi->planes[1]+mpi->stride[1], chroma_width, mpi->chroma_height/2, dmpi->stride[1]*2, mpi->stride[1]*2); my_memcpy_pic(dmpi->planes[2]+dmpi->stride[2], mpi->planes[2]+mpi->stride[2], chroma_width, mpi->chroma_height/2, dmpi->stride[2]*2, mpi->stride[2]*2); } ret = vf_next_put_image(vf, dmpi, MP_NOPTS_VALUE); my_memcpy_pic(dmpi->planes[0], mpi->planes[0], w, mpi->h/2, dmpi->stride[0]*2, mpi->stride[0]*2); if (mpi->flags & MP_IMGFLAG_PLANAR) { my_memcpy_pic(dmpi->planes[1], mpi->planes[1], chroma_width, mpi->chroma_height/2, dmpi->stride[1]*2, mpi->stride[1]*2); my_memcpy_pic(dmpi->planes[2], mpi->planes[2], chroma_width, mpi->chroma_height/2, dmpi->stride[2]*2, mpi->stride[2]*2); } return ret; } return 0; }
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; }
static void delogo(uint8_t *dst, uint8_t *src, int dstStride, int srcStride, int width, int height, int logo_x, int logo_y, int logo_w, int logo_h, int band, int show, int direct) { int y, x; int interp, dist; uint8_t *xdst, *xsrc; uint8_t *topleft, *botleft, *topright; int xclipl, xclipr, yclipt, yclipb; int logo_x1, logo_x2, logo_y1, logo_y2; xclipl = MAX(-logo_x, 0); xclipr = MAX(logo_x+logo_w-width, 0); yclipt = MAX(-logo_y, 0); yclipb = MAX(logo_y+logo_h-height, 0); logo_x1 = logo_x + xclipl; logo_x2 = logo_x + logo_w - xclipr; logo_y1 = logo_y + yclipt; logo_y2 = logo_y + logo_h - yclipb; topleft = src+logo_y1*srcStride+logo_x1; topright = src+logo_y1*srcStride+logo_x2-1; botleft = src+(logo_y2-1)*srcStride+logo_x1; if (!direct) memcpy_pic(dst, src, width, height, dstStride, srcStride); dst += (logo_y1+1)*dstStride; src += (logo_y1+1)*srcStride; for(y = logo_y1+1; y < logo_y2-1; y++) { for (x = logo_x1+1, xdst = dst+logo_x1+1, xsrc = src+logo_x1+1; x < logo_x2-1; x++, xdst++, xsrc++) { interp = ((topleft[srcStride*(y-logo_y-yclipt)] + topleft[srcStride*(y-logo_y-1-yclipt)] + topleft[srcStride*(y-logo_y+1-yclipt)])*(logo_w-(x-logo_x))/logo_w + (topright[srcStride*(y-logo_y-yclipt)] + topright[srcStride*(y-logo_y-1-yclipt)] + topright[srcStride*(y-logo_y+1-yclipt)])*(x-logo_x)/logo_w + (topleft[x-logo_x-xclipl] + topleft[x-logo_x-1-xclipl] + topleft[x-logo_x+1-xclipl])*(logo_h-(y-logo_y))/logo_h + (botleft[x-logo_x-xclipl] + botleft[x-logo_x-1-xclipl] + botleft[x-logo_x+1-xclipl])*(y-logo_y)/logo_h )/6; /* interp = (topleft[srcStride*(y-logo_y)]*(logo_w-(x-logo_x))/logo_w + topright[srcStride*(y-logo_y)]*(x-logo_x)/logo_w + topleft[x-logo_x]*(logo_h-(y-logo_y))/logo_h + botleft[x-logo_x]*(y-logo_y)/logo_h )/2;*/ if (y >= logo_y+band && y < logo_y+logo_h-band && x >= logo_x+band && x < logo_x+logo_w-band) { *xdst = interp; } else { dist = 0; if (x < logo_x+band) dist = MAX(dist, logo_x-x+band); else if (x >= logo_x+logo_w-band) dist = MAX(dist, x-(logo_x+logo_w-1-band)); if (y < logo_y+band) dist = MAX(dist, logo_y-y+band); else if (y >= logo_y+logo_h-band) dist = MAX(dist, y-(logo_y+logo_h-1-band)); *xdst = (*xsrc*dist + interp*(band-dist))/band; if (show && (dist == band-1)) *xdst = 0; } } dst+= dstStride; src+= srcStride; } }
static struct mp_image *filter(struct vf_instance *vf, struct mp_image *mpi) { struct pullup_context *c = vf->priv->ctx; struct pullup_buffer *b; struct pullup_frame *f; int p; int i; double pts = mpi->pts; struct mp_image *dmpi = NULL; if (!vf->priv->init) init_pullup(vf, mpi); if (1) { b = pullup_get_buffer(c, 2); if (!b) { mp_msg(MSGT_VFILTER,MSGL_ERR,"Could not get buffer from pullup!\n"); f = pullup_get_frame(c); pullup_release_frame(f); goto skip; } memcpy_pic(b->planes[0], mpi->planes[0], mpi->w, mpi->h, c->stride[0], mpi->stride[0]); memcpy_pic(b->planes[1], mpi->planes[1], mpi->chroma_width, mpi->chroma_height, c->stride[1], mpi->stride[1]); memcpy_pic(b->planes[2], mpi->planes[2], mpi->chroma_width, mpi->chroma_height, c->stride[2], mpi->stride[2]); } if (mpi->qscale) { memcpy(b->planes[3], mpi->qscale, c->w[3]); memcpy(b->planes[3]+c->w[3], mpi->qscale, c->w[3]); } p = mpi->fields & MP_IMGFIELD_TOP_FIRST ? 0 : (mpi->fields & MP_IMGFIELD_ORDERED ? 1 : 0); if (pts == MP_NOPTS_VALUE) { pullup_submit_field(c, b, p, MP_NOPTS_VALUE); pullup_submit_field(c, b, p^1, MP_NOPTS_VALUE); if (mpi->fields & MP_IMGFIELD_REPEAT_FIRST) pullup_submit_field(c, b, p, MP_NOPTS_VALUE); } else { double delta; if (vf->priv->lastpts == MP_NOPTS_VALUE) delta = 1001.0/60000.0; // delta = field time distance else delta = (pts - vf->priv->lastpts) / 2; if (delta <= 0.0 || delta >= 0.5) delta = 0.0; vf->priv->lastpts = pts; if (mpi->fields & MP_IMGFIELD_REPEAT_FIRST) { pullup_submit_field(c, b, p, pts - delta); pullup_submit_field(c, b, p^1, pts); pullup_submit_field(c, b, p, pts + delta); } else { pullup_submit_field(c, b, p, pts - delta * 0.5); pullup_submit_field(c, b, p^1, pts + delta * 0.5); } } pullup_release_buffer(b, 2); f = pullup_get_frame(c); /* Fake yes for first few frames (buffer depth) to keep from * breaking A/V sync with G1's bad architecture... */ //if (!f) return vf->priv->fakecount ? (--vf->priv->fakecount,1) : 0; if (!f) goto skip; if (f->length < 2) { pullup_release_frame(f); f = pullup_get_frame(c); if (!f) goto skip; if (f->length < 2) { pullup_release_frame(f); if (!(mpi->fields & MP_IMGFIELD_REPEAT_FIRST)) goto skip; f = pullup_get_frame(c); if (!f) goto skip; if (f->length < 2) { pullup_release_frame(f); goto skip; } } } #if 0 /* Average qscale tables from both frames. */ if (mpi->qscale) { for (i=0; i<c->w[3]; i++) { vf->priv->qbuf[i] = (f->ofields[0]->planes[3][i] + f->ofields[1]->planes[3][i+c->w[3]])>>1; } } #else /* Take worst of qscale tables from both frames. */ if (mpi->qscale) { for (i=0; i<c->w[3]; i++) { vf->priv->qbuf[i] = MAX(f->ofields[0]->planes[3][i], f->ofields[1]->planes[3][i+c->w[3]]); } } #endif /* If the frame isn't already exportable... */ if (!f->buffer) pullup_pack_frame(c, f); // NOTE: the copy could probably be avoided by changing or using the // pullup internal buffer management. But right now just do the // safe thing and always copy. Code outside the filter might // hold a buffer reference even if the filter chain is destroyed. dmpi = vf_alloc_out_image(vf); mp_image_copy_attributes(dmpi, mpi); struct mp_image data = *dmpi; data.planes[0] = f->buffer->planes[0]; data.planes[1] = f->buffer->planes[1]; data.planes[2] = f->buffer->planes[2]; data.stride[0] = c->stride[0]; data.stride[1] = c->stride[1]; data.stride[2] = c->stride[2]; mp_image_copy(dmpi, &data); dmpi->pts = f->pts; // Warning: entirely bogus memory management of qscale if (mpi->qscale) { dmpi->qscale = vf->priv->qbuf; dmpi->qstride = mpi->qstride; dmpi->qscale_type = mpi->qscale_type; } pullup_release_frame(f); skip: talloc_free(mpi); return dmpi; }
static void draw_image(struct vo *vo, mp_image_t *mpi) { memcpy_pic(dither_buffer, mpi->planes[0], image_width * depth, image_height, image_width * depth, mpi->stride[0]); caca_dither_bitmap(canvas, 0, 0, screen_w, screen_h, dither, dither_buffer); }
/** @brief Render a frame on the screen. * @param mpi mpi structure with the decoded frame inside * @return VO_TRUE on success, VO_ERROR on failure */ static uint32_t render_d3d_frame(mp_image_t *mpi) { /* Uncomment when direct rendering is implemented. * if (mpi->flags & MP_IMGFLAG_DIRECT) ... */ /* If the D3D device is uncooperative (not initialized), return success. The device will be probed for reinitialization in the next flip_page() */ if (!priv->d3d_device) return VO_TRUE; if (mpi->flags & MP_IMGFLAG_DRAW_CALLBACK) goto skip_upload; if (mpi->flags & MP_IMGFLAG_PLANAR) { /* Copy a planar frame. */ draw_slice(mpi->planes, mpi->stride, mpi->w, mpi->h, 0, 0); goto skip_upload; } /* If we're here, then we should lock the rect and copy a packed frame */ if (!priv->locked_rect.pBits) { if (FAILED(IDirect3DSurface9_LockRect(priv->d3d_surface, &priv->locked_rect, NULL, 0))) { mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>Surface lock failed.\n"); return VO_ERROR; } } memcpy_pic(priv->locked_rect.pBits, mpi->planes[0], mpi->stride[0], mpi->height, priv->locked_rect.Pitch, mpi->stride[0]); skip_upload: /* This unlock is used for both slice_draw path and render_d3d_frame path. */ if (FAILED(IDirect3DSurface9_UnlockRect(priv->d3d_surface))) { mp_msg(MSGT_VO, MSGL_V, "<vo_direct3d>Surface unlock failed.\n"); return VO_ERROR; } priv->locked_rect.pBits = NULL; if (FAILED(IDirect3DDevice9_BeginScene(priv->d3d_device))) { mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>BeginScene failed.\n"); return VO_ERROR; } if (priv->is_clear_needed) { IDirect3DDevice9_Clear(priv->d3d_device, 0, NULL, D3DCLEAR_TARGET, 0, 0, 0); priv->is_clear_needed = 0; } if (FAILED(IDirect3DDevice9_StretchRect(priv->d3d_device, priv->d3d_surface, &priv->fs_panscan_rect, priv->d3d_backbuf, &priv->fs_movie_rect, D3DTEXF_LINEAR))) { mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>Copying frame to the backbuffer failed.\n"); return VO_ERROR; } if (FAILED(IDirect3DDevice9_EndScene(priv->d3d_device))) { mp_msg(MSGT_VO, MSGL_ERR, "<vo_direct3d>EndScene failed.\n"); return VO_ERROR; } return VO_TRUE; }
static int IvtcFilter (VideoFilter *vf, VideoFrame *frame, int field) { (void)field; ThisFilter *filter = (ThisFilter *) vf; if (!frame->interlaced_frame) { filter->progressive_frame_seen = 1; } if (filter->progressive_frame_seen && frame->interlaced_frame) { filter->interlaced_frame_seen = 1; } if (!frame->interlaced_frame && !filter->apply_filter && filter->interlaced_frame_seen && filter->progressive_frame_seen) { fprintf(stderr,"turning on inverse telecine filter"); filter->apply_filter = 1; } if (!filter->apply_filter) return 1; SetupFilter(filter, frame->width, frame->height, (int*)frame->pitches); struct pullup_buffer *b; struct pullup_frame *f; int ypitch = filter->context->stride[0]; int height = filter->height; int cpitch = filter->context->stride[1]; int cheight = filter->height >> 1; int p = frame->top_field_first ^ 1; struct pullup_context *c = filter->context; if (c->bpp[0] == 0) c->bpp[0] = c->bpp[1] = c->bpp[2] = frame->bpp; b = pullup_get_buffer(c,2); if (!b) { f = pullup_get_frame(c); pullup_release_frame(f); return 0; } memcpy_pic(b->planes[0], frame->buf + frame->offsets[0], height, ypitch, ypitch); memcpy_pic(b->planes[1], frame->buf + frame->offsets[1], cheight, cpitch, cpitch); memcpy_pic(b->planes[2], frame->buf + frame->offsets[2], cheight, cpitch, cpitch); pullup_submit_field(c, b, p); pullup_submit_field(c, b, p^1); if (frame->repeat_pict) pullup_submit_field(c, b, p); pullup_release_buffer(b, 2); f = pullup_get_frame(c); if (!f) return 0; if (f->length < 2) { pullup_release_frame(f); f = pullup_get_frame(c); if (!f) return 0; if (f->length < 2) { pullup_release_frame(f); if (!frame->repeat_pict) return 0; f = pullup_get_frame(c); if (!f) return 0; if (f->length < 2) { pullup_release_frame(f); return 0; } } } if (!f->buffer) { pullup_pack_frame(c, f); } if (!f->buffer) return 0; memcpy_pic(frame->buf + frame->offsets[0], f->buffer->planes[0], height, ypitch, ypitch); memcpy_pic(frame->buf + frame->offsets[1], f->buffer->planes[1], cheight, cpitch, cpitch); memcpy_pic(frame->buf + frame->offsets[2], f->buffer->planes[2], cheight, cpitch, cpitch); pullup_release_frame(f); return 1; }
/** * @brief Read and decode a PNG file into bitmap data. * * @param fname filename (with path) * @param img pointer suitable to store the image data * * @return 0 (ok), 1 (decoding error), 2 (open error), 3 (file too big), * 4 (out of memory), 5 (avcodec alloc error) */ static int pngRead(const char *fname, guiImage *img) { FILE *file; long len; void *data; int decode_ok, bpl; AVCodecContext *avctx; AVFrame *frame; AVPacket pkt; file = fopen(fname, "rb"); if (!file) return 2; fseek(file, 0, SEEK_END); len = ftell(file); if (len > 50 * 1024 * 1024 || len < 8) { fclose(file); return 3; } data = av_malloc(len + FF_INPUT_BUFFER_PADDING_SIZE); if (!data) { fclose(file); return 4; } fseek(file, 0, SEEK_SET); fread(data, len, 1, file); fclose(file); avctx = avcodec_alloc_context3(NULL); frame = av_frame_alloc(); if (!(avctx && frame)) { av_free(frame); av_free(avctx); av_free(data); return 5; } avcodec_register_all(); avcodec_open2(avctx, avcodec_find_decoder(AV_CODEC_ID_PNG), NULL); av_init_packet(&pkt); pkt.data = data; pkt.size = len; // HACK: make PNGs decode normally instead of as CorePNG delta frames pkt.flags = AV_PKT_FLAG_KEY; avcodec_decode_video2(avctx, frame, &decode_ok, &pkt); memset(img, 0, sizeof(*img)); switch (avctx->pix_fmt) { case AV_PIX_FMT_GRAY8: img->Bpp = 8; break; case AV_PIX_FMT_GRAY16BE: img->Bpp = 16; break; case AV_PIX_FMT_RGB24: img->Bpp = 24; break; case AV_PIX_FMT_RGBA: img->Bpp = 32; break; default: img->Bpp = 0; break; } if (decode_ok && img->Bpp) { img->Width = avctx->width; img->Height = avctx->height; bpl = img->Width * (img->Bpp / 8); img->ImageSize = bpl * img->Height; mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[bitmap] file: %s\n", fname); mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[bitmap] size: %lux%lu, color depth: %u\n", img->Width, img->Height, img->Bpp); mp_msg(MSGT_GPLAYER, MSGL_DBG2, "[bitmap] image size: %lu\n", img->ImageSize); img->Image = malloc(img->ImageSize); if (img->Image) memcpy_pic(img->Image, frame->data[0], bpl, img->Height, bpl, frame->linesize[0]); else decode_ok = 0; } avcodec_close(avctx); av_free(frame); av_free(avctx); av_free(data); return !(decode_ok && img->Bpp); }
static void modeset_destroy_fb(int fd, struct modeset_buf *buf) { if (buf->map) { munmap(buf->map, buf->size); } if (buf->fb) { drmModeRmFB(fd, buf->fb); } if (buf->handle) { struct drm_mode_destroy_dumb dreq = { .handle = buf->handle, }; drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dreq); } } static int modeset_create_fb(struct vo *vo, int fd, struct modeset_buf *buf) { int ret = 0; buf->handle = 0; // create dumb buffer struct drm_mode_create_dumb creq = { .width = buf->width, .height = buf->height, .bpp = 32, }; ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &creq); if (ret < 0) { MP_ERR(vo, "Cannot create dumb buffer: %s\n", mp_strerror(errno)); ret = -errno; goto end; } buf->stride = creq.pitch; buf->size = creq.size; buf->handle = creq.handle; // create framebuffer object for the dumb-buffer ret = drmModeAddFB(fd, buf->width, buf->height, 24, 32, buf->stride, buf->handle, &buf->fb); if (ret) { MP_ERR(vo, "Cannot create framebuffer: %s\n", mp_strerror(errno)); ret = -errno; goto end; } // prepare buffer for memory mapping struct drm_mode_map_dumb mreq = { .handle = buf->handle, }; ret = drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &mreq); if (ret) { MP_ERR(vo, "Cannot map dumb buffer: %s\n", mp_strerror(errno)); ret = -errno; goto end; } // perform actual memory mapping buf->map = mmap(0, buf->size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, mreq.offset); if (buf->map == MAP_FAILED) { MP_ERR(vo, "Cannot map dumb buffer: %s\n", mp_strerror(errno)); ret = -errno; goto end; } memset(buf->map, 0, buf->size); end: if (ret == 0) { return 0; } modeset_destroy_fb(fd, buf); return ret; } static int modeset_find_crtc(struct vo *vo, int fd, drmModeRes *res, drmModeConnector *conn, struct modeset_dev *dev) { for (unsigned int i = 0; i < conn->count_encoders; ++i) { drmModeEncoder *enc = drmModeGetEncoder(fd, conn->encoders[i]); if (!enc) { MP_WARN(vo, "Cannot retrieve encoder %u:%u: %s\n", i, conn->encoders[i], mp_strerror(errno)); continue; } // iterate all global CRTCs for (unsigned int j = 0; j < res->count_crtcs; ++j) { // check whether this CRTC works with the encoder if (!(enc->possible_crtcs & (1 << j))) continue; dev->enc = enc; dev->crtc = enc->crtc_id; return 0; } drmModeFreeEncoder(enc); } MP_ERR(vo, "Connector %u has no suitable CRTC\n", conn->connector_id); return -ENOENT; } static bool is_connector_valid(struct vo *vo, int conn_id, drmModeConnector *conn, bool silent) { if (!conn) { if (!silent) { MP_ERR(vo, "Cannot get connector %d: %s\n", conn_id, mp_strerror(errno)); } return false; } if (conn->connection != DRM_MODE_CONNECTED) { if (!silent) { MP_ERR(vo, "Connector %d is disconnected\n", conn_id); } return false; } if (conn->count_modes == 0) { if (!silent) { MP_ERR(vo, "Connector %d has no valid modes\n", conn_id); } return false; } return true; } static int modeset_prepare_dev(struct vo *vo, int fd, int conn_id, struct modeset_dev **out) { struct modeset_dev *dev = NULL; drmModeConnector *conn = NULL; int ret = 0; *out = NULL; drmModeRes *res = drmModeGetResources(fd); if (!res) { MP_ERR(vo, "Cannot retrieve DRM resources: %s\n", mp_strerror(errno)); ret = -errno; goto end; } if (conn_id == -1) { // get the first connected connector for (int i = 0; i < res->count_connectors; i++) { conn = drmModeGetConnector(fd, res->connectors[i]); if (is_connector_valid(vo, i, conn, true)) { conn_id = i; break; } if (conn) { drmModeFreeConnector(conn); conn = NULL; } } if (conn_id == -1) { MP_ERR(vo, "No connected connectors found\n"); ret = -ENODEV; goto end; } } if (conn_id < 0 || conn_id >= res->count_connectors) { MP_ERR(vo, "Bad connector ID. Max valid connector ID = %u\n", res->count_connectors); ret = -ENODEV; goto end; } conn = drmModeGetConnector(fd, res->connectors[conn_id]); if (!is_connector_valid(vo, conn_id, conn, false)) { ret = -ENODEV; goto end; } dev = talloc_zero(vo->priv, struct modeset_dev); dev->conn = conn->connector_id; dev->front_buf = 0; dev->mode = conn->modes[0]; dev->bufs[0].width = conn->modes[0].hdisplay; dev->bufs[0].height = conn->modes[0].vdisplay; dev->bufs[1].width = conn->modes[0].hdisplay; dev->bufs[1].height = conn->modes[0].vdisplay; MP_INFO(vo, "Connector using mode %ux%u\n", dev->bufs[0].width, dev->bufs[0].height); ret = modeset_find_crtc(vo, fd, res, conn, dev); if (ret) { MP_ERR(vo, "Connector %d has no valid CRTC\n", conn_id); goto end; } for (unsigned int i = 0; i < BUF_COUNT; i++) { ret = modeset_create_fb(vo, fd, &dev->bufs[i]); if (ret) { MP_ERR(vo, "Cannot create framebuffer for connector %d\n", conn_id); for (unsigned int j = 0; j < i; j++) { modeset_destroy_fb(fd, &dev->bufs[j]); } goto end; } } end: if (conn) { drmModeFreeConnector(conn); conn = NULL; } if (res) { drmModeFreeResources(res); res = NULL; } if (ret == 0) { *out = dev; } else { talloc_free(dev); } return ret; } static void modeset_page_flipped(int fd, unsigned int frame, unsigned int sec, unsigned int usec, void *data) { struct priv *p = data; p->pflip_happening = false; } static int setup_vo_crtc(struct vo *vo) { struct priv *p = vo->priv; if (p->active) return 0; p->old_crtc = drmModeGetCrtc(p->fd, p->dev->crtc); int ret = drmModeSetCrtc(p->fd, p->dev->crtc, p->dev->bufs[p->dev->front_buf + BUF_COUNT - 1].fb, 0, 0, &p->dev->conn, 1, &p->dev->mode); p->active = true; return ret; } static void release_vo_crtc(struct vo *vo) { struct priv *p = vo->priv; if (!p->active) return; p->active = false; // wait for current page flip while (p->pflip_happening) { int ret = drmHandleEvent(p->fd, &p->ev); if (ret) { MP_ERR(vo, "drmHandleEvent failed: %i\n", ret); break; } } if (p->old_crtc) { drmModeSetCrtc(p->fd, p->old_crtc->crtc_id, p->old_crtc->buffer_id, p->old_crtc->x, p->old_crtc->y, &p->dev->conn, 1, &p->dev->mode); drmModeFreeCrtc(p->old_crtc); p->old_crtc = NULL; } } static void release_vt(void *data) { struct vo *vo = data; release_vo_crtc(vo); if (USE_MASTER) { //this function enables support for switching to x, weston etc. //however, for whatever reason, it can be called only by root users. //until things change, this is commented. struct priv *p = vo->priv; if (drmDropMaster(p->fd)) { MP_WARN(vo, "Failed to drop DRM master: %s\n", mp_strerror(errno)); } } } static void acquire_vt(void *data) { struct vo *vo = data; if (USE_MASTER) { struct priv *p = vo->priv; if (drmSetMaster(p->fd)) { MP_WARN(vo, "Failed to acquire DRM master: %s\n", mp_strerror(errno)); } } setup_vo_crtc(vo); } static int wait_events(struct vo *vo, int64_t until_time_us) { struct priv *p = vo->priv; int64_t wait_us = until_time_us - mp_time_us(); int timeout_ms = MPCLAMP((wait_us + 500) / 1000, 0, 10000); vt_switcher_poll(&p->vt_switcher, timeout_ms); return 0; } static void wakeup(struct vo *vo) { struct priv *p = vo->priv; vt_switcher_interrupt_poll(&p->vt_switcher); } static int reconfig(struct vo *vo, struct mp_image_params *params, int flags) { struct priv *p = vo->priv; vo->dwidth = p->device_w; vo->dheight = p->device_h; vo_get_src_dst_rects(vo, &p->src, &p->dst, &p->osd); int32_t w = p->dst.x1 - p->dst.x0; int32_t h = p->dst.y1 - p->dst.y0; // p->osd contains the parameters assuming OSD rendering in window // coordinates, but OSD can only be rendered in the intersection // between window and video rectangle (i.e. not into panscan borders). p->osd.w = w; p->osd.h = h; p->osd.mt = MPMIN(0, p->osd.mt); p->osd.mb = MPMIN(0, p->osd.mb); p->osd.mr = MPMIN(0, p->osd.mr); p->osd.ml = MPMIN(0, p->osd.ml); p->x = (p->device_w - w) >> 1; p->y = (p->device_h - h) >> 1; mp_sws_set_from_cmdline(p->sws, vo->opts->sws_opts); p->sws->src = *params; p->sws->dst = (struct mp_image_params) { .imgfmt = IMGFMT_BGR0, .w = w, .h = h, .d_w = w, .d_h = h, }; talloc_free(p->cur_frame); p->cur_frame = mp_image_alloc(IMGFMT_BGR0, p->device_w, p->device_h); mp_image_params_guess_csp(&p->sws->dst); mp_image_set_params(p->cur_frame, &p->sws->dst); struct modeset_buf *buf = p->dev->bufs; memset(buf[0].map, 0, buf[0].size); memset(buf[1].map, 0, buf[1].size); if (mp_sws_reinit(p->sws) < 0) return -1; vo->want_redraw = true; return 0; } static void draw_image(struct vo *vo, mp_image_t *mpi) { struct priv *p = vo->priv; if (p->active) { struct mp_image src = *mpi; struct mp_rect src_rc = p->src; src_rc.x0 = MP_ALIGN_DOWN(src_rc.x0, mpi->fmt.align_x); src_rc.y0 = MP_ALIGN_DOWN(src_rc.y0, mpi->fmt.align_y); mp_image_crop_rc(&src, src_rc); mp_sws_scale(p->sws, p->cur_frame, &src); osd_draw_on_image(vo->osd, p->osd, src.pts, 0, p->cur_frame); struct modeset_buf *front_buf = &p->dev->bufs[p->dev->front_buf]; int32_t shift = (p->device_w * p->y + p->x) * 4; memcpy_pic(front_buf->map + shift, p->cur_frame->planes[0], (p->dst.x1 - p->dst.x0) * 4, p->dst.y1 - p->dst.y0, p->device_w * 4, p->cur_frame->stride[0]); } if (mpi != p->last_input) { talloc_free(p->last_input); p->last_input = mpi; } } static void flip_page(struct vo *vo) { struct priv *p = vo->priv; if (!p->active || p->pflip_happening) return; int ret = drmModePageFlip(p->fd, p->dev->crtc, p->dev->bufs[p->dev->front_buf].fb, DRM_MODE_PAGE_FLIP_EVENT, p); if (ret) { MP_WARN(vo, "Cannot flip page for connector\n"); } else { p->dev->front_buf++; p->dev->front_buf %= BUF_COUNT; p->pflip_happening = true; } // poll page flip finish event const int timeout_ms = 3000; struct pollfd fds[1] = { { .events = POLLIN, .fd = p->fd }, }; poll(fds, 1, timeout_ms); if (fds[0].revents & POLLIN) { ret = drmHandleEvent(p->fd, &p->ev); if (ret != 0) { MP_ERR(vo, "drmHandleEvent failed: %i\n", ret); return; } } } static void uninit(struct vo *vo) { struct priv *p = vo->priv; if (p->dev) { release_vo_crtc(vo); modeset_destroy_fb(p->fd, &p->dev->bufs[1]); modeset_destroy_fb(p->fd, &p->dev->bufs[0]); drmModeFreeEncoder(p->dev->enc); } vt_switcher_destroy(&p->vt_switcher); talloc_free(p->last_input); talloc_free(p->cur_frame); talloc_free(p->dev); close(p->fd); } static int preinit(struct vo *vo) { struct priv *p = vo->priv; p->sws = mp_sws_alloc(vo); p->fd = -1; p->ev.version = DRM_EVENT_CONTEXT_VERSION; p->ev.page_flip_handler = modeset_page_flipped; if (vt_switcher_init(&p->vt_switcher, vo->log)) goto err; vt_switcher_acquire(&p->vt_switcher, acquire_vt, vo); vt_switcher_release(&p->vt_switcher, release_vt, vo); if (modeset_open(vo, &p->fd, p->device_path)) goto err; if (modeset_prepare_dev(vo, p->fd, p->connector_id, &p->dev)) goto err; assert(p->dev); p->device_w = p->dev->bufs[0].width; p->device_h = p->dev->bufs[0].height; if (setup_vo_crtc(vo)) { MP_ERR(vo, "Cannot set CRTC for connector %u: %s\n", p->connector_id, mp_strerror(errno)); goto err; } return 0; err: uninit(vo); return -1; } static int query_format(struct vo *vo, int format) { return sws_isSupportedInput(imgfmt2pixfmt(format)); } static int control(struct vo *vo, uint32_t request, void *data) { struct priv *p = vo->priv; switch (request) { case VOCTRL_SCREENSHOT_WIN: *(struct mp_image**)data = mp_image_new_copy(p->cur_frame); return VO_TRUE; case VOCTRL_REDRAW_FRAME: draw_image(vo, p->last_input); return VO_TRUE; case VOCTRL_GET_PANSCAN: return VO_TRUE; case VOCTRL_SET_PANSCAN: if (vo->config_ok) reconfig(vo, vo->params, 0); return VO_TRUE; } return VO_NOTIMPL; } #define OPT_BASE_STRUCT struct priv const struct vo_driver video_out_drm = { .name = "drm", .description = "Direct Rendering Manager", .preinit = preinit, .query_format = query_format, .reconfig = reconfig, .control = control, .draw_image = draw_image, .flip_page = flip_page, .uninit = uninit, .wait_events = wait_events, .wakeup = wakeup, .priv_size = sizeof(struct priv), .options = (const struct m_option[]) { OPT_STRING("devpath", device_path, 0), OPT_INT("connector", connector_id, 0), {0}, }, .priv_defaults = &(const struct priv) {