? libvo/vo_quartz.c.YUV Index: libvo/vo_quartz.c =================================================================== RCS file: /cvsroot/mplayer/main/libvo/vo_quartz.c,v retrieving revision 1.2 diff -u -r1.2 vo_quartz.c --- libvo/vo_quartz.c 26 Apr 2004 12:17:26 -0000 1.2 +++ libvo/vo_quartz.c 26 Apr 2004 15:18:50 -0000 @@ -4,10 +4,12 @@ by Nicolas Plourde Copyright (c) Nicolas Plourde - April 2004 + + YUV support Copyright (C) 2004 Romain Dolbeau MPlayer Mac OSX Quartz video out module. - todo: -YUV support. + todo: -(efficient) YUV support. -Redo event handling. -Choose fullscreen display device. -Fullscreen antialiasing. @@ -15,6 +17,15 @@ -rootwin -non-blocking event -(add sugestion here) + + Currently, direct YUV support is disabled for two reasons: + 1) it's not production-level by any standard (no window rescale, + no full-screen, flickering or non-working OSD...) + 2) it's much slower than going RGB ! With rescaling, + using YV12 can be 60% slower than RGB32 on a G5. + Apparently, QuickTime does a YUV->RGB conversion + itself, and scale in RGB... + Maybe someday someone will figure out what goes wrong ? */ //SYS @@ -22,12 +33,15 @@ //OSX #include +#include //MPLAYER #include "config.h" +#include "fastmemcpy.h" #include "video_out.h" #include "video_out_internal.h" #include "aspect.h" +#include "mp_msg.h" #include "../input/input.h" #include "../input/mouse.h" @@ -38,7 +52,7 @@ { "Mac OSX (Quartz)", "quartz", - "Nicolas Plourde ", + "Nicolas Plourde \nRomain Dolbeau ", "" }; @@ -47,8 +61,20 @@ uint32_t image_width; uint32_t image_height; uint32_t image_depth; -uint32_t image_bytes; uint32_t image_format; +uint32_t image_size; +CodecType image_qtcodec; +PlanarPixmapInfoYUV420 *P; +struct { + ImageSequence seqId; + ImageDescriptionHandle desc; + Handle extension_colr; + Handle extension_fiel; + Handle extension_clap; + Handle extension_pasp; + MatrixRecord matrix; +} yuv_qt_stuff; + char *image_data; extern int vo_ontop; @@ -85,7 +111,21 @@ static void draw_alpha(int x0, int y0, int w, int h, unsigned char *src, unsigned char *srca, int stride) { - vo_draw_alpha_rgb32(w,h,src,srca,stride,image_data+4*(y0*imgRect.right+x0),4*imgRect.right); + switch (image_format) { + case IMGFMT_RGB32: + vo_draw_alpha_rgb32(w,h,src,srca,stride,image_data+4*(y0*imgRect.right+x0),4*imgRect.right); + break; + case IMGFMT_YV12: + case IMGFMT_IYUV: + case IMGFMT_I420: + vo_draw_alpha_yv12(w,h,src,srca,stride,((char*)P) + P->componentInfoY.offset,image_width); + break; + case IMGFMT_UYVY: + vo_draw_alpha_yuy2(w,h,src,srca,stride,((char*)P),image_width*2); + case IMGFMT_YUY2: + vo_draw_alpha_yuy2(w,h,src,srca,stride,((char*)P),image_width*2); + break; + } } //default window event handler @@ -243,6 +283,7 @@ OSStatus result; GDHandle deviceHdl; Rect deviceRect; + OSErr qterr; //Get Main device info/////////////////////////////////////////////////// deviceHdl = GetMainDevice(); @@ -254,9 +295,20 @@ //misc mplayer setup///////////////////////////////////////////////////// image_width = width; image_height = height; - image_depth = IMGFMT_RGB_DEPTH(format); - image_bytes = (IMGFMT_RGB_DEPTH(format)+7)/8; - image_data = malloc(image_width*image_height*4); + switch (image_format) { + case IMGFMT_RGB32: + image_depth = IMGFMT_RGB_DEPTH(format); + break; + case IMGFMT_YV12: + case IMGFMT_IYUV: + case IMGFMT_I420: + image_depth = 12; + case IMGFMT_UYVY: + case IMGFMT_YUY2: + image_depth = 16; + break; + } + image_size = ((image_width*image_height*image_depth)+7)/8; vo_fs = flags & VOFLAG_FULLSCREEN; @@ -296,6 +348,116 @@ //Show window RepositionWindow(theWindow, NULL, kWindowCascadeOnMainScreen); + + if (!(IMGFMT_IS_RGB(image_format))) { + qterr = EnterMovies(); + mp_msg(MSGT_VO, MSGL_INFO, "\nQuartz: EnterMovies\n\n"); + if (qterr) { + mp_msg(MSGT_VO, MSGL_FATAL, "Quartz error: EnterMovies (%d)\n", qterr); + } + yuv_qt_stuff.desc = (ImageDescriptionHandle)NewHandleClear( sizeof(ImageDescription) ); + yuv_qt_stuff.extension_colr = NewHandleClear(sizeof(NCLCColorInfoImageDescriptionExtension)); + ((NCLCColorInfoImageDescriptionExtension*)(*yuv_qt_stuff.extension_colr))->colorParamType = kVideoColorInfoImageDescriptionExtensionType; + ((NCLCColorInfoImageDescriptionExtension*)(*yuv_qt_stuff.extension_colr))->primaries = 2; + ((NCLCColorInfoImageDescriptionExtension*)(*yuv_qt_stuff.extension_colr))->transferFunction = 2; + ((NCLCColorInfoImageDescriptionExtension*)(*yuv_qt_stuff.extension_colr))->matrix = 2; + yuv_qt_stuff.extension_fiel = NewHandleClear(sizeof(FieldInfoImageDescriptionExtension)); + ((FieldInfoImageDescriptionExtension*)(*yuv_qt_stuff.extension_fiel))->fieldCount = 1; + ((FieldInfoImageDescriptionExtension*)(*yuv_qt_stuff.extension_fiel))->fieldOrderings = 0; + yuv_qt_stuff.extension_clap = NewHandleClear(sizeof(CleanApertureImageDescriptionExtension)); + ((CleanApertureImageDescriptionExtension*)(*yuv_qt_stuff.extension_clap))->cleanApertureWidthN = d_width; + ((CleanApertureImageDescriptionExtension*)(*yuv_qt_stuff.extension_clap))->cleanApertureWidthD = 1; + ((CleanApertureImageDescriptionExtension*)(*yuv_qt_stuff.extension_clap))->cleanApertureHeightN = d_height; + ((CleanApertureImageDescriptionExtension*)(*yuv_qt_stuff.extension_clap))->cleanApertureHeightD = 1; + ((CleanApertureImageDescriptionExtension*)(*yuv_qt_stuff.extension_clap))->horizOffN = 0; + ((CleanApertureImageDescriptionExtension*)(*yuv_qt_stuff.extension_clap))->horizOffD = 1; + ((CleanApertureImageDescriptionExtension*)(*yuv_qt_stuff.extension_clap))->vertOffN = 0; + ((CleanApertureImageDescriptionExtension*)(*yuv_qt_stuff.extension_clap))->vertOffD = 1; + yuv_qt_stuff.extension_pasp = NewHandleClear(sizeof(PixelAspectRatioImageDescriptionExtension)); + ((PixelAspectRatioImageDescriptionExtension*)(*yuv_qt_stuff.extension_pasp))->hSpacing = 1; + ((PixelAspectRatioImageDescriptionExtension*)(*yuv_qt_stuff.extension_pasp))->vSpacing = 1; + + (*yuv_qt_stuff.desc)->idSize = sizeof(ImageDescription); + (*yuv_qt_stuff.desc)->cType = image_qtcodec; + (*yuv_qt_stuff.desc)->version = 2; + (*yuv_qt_stuff.desc)->revisionLevel = 0; + (*yuv_qt_stuff.desc)->vendor = 'mpla'; + (*yuv_qt_stuff.desc)->width = image_width; + (*yuv_qt_stuff.desc)->height = image_height; + (*yuv_qt_stuff.desc)->hRes = Long2Fix(72); + (*yuv_qt_stuff.desc)->vRes = Long2Fix(72); + (*yuv_qt_stuff.desc)->temporalQuality = 0; + (*yuv_qt_stuff.desc)->spatialQuality = codecLosslessQuality; + (*yuv_qt_stuff.desc)->frameCount = 1; + (*yuv_qt_stuff.desc)->dataSize = 0; + (*yuv_qt_stuff.desc)->depth = 24; + (*yuv_qt_stuff.desc)->clutID = -1; + + qterr = AddImageDescriptionExtension(yuv_qt_stuff.desc, yuv_qt_stuff.extension_colr, kColorInfoImageDescriptionExtension); + if (qterr) { + mp_msg(MSGT_VO, MSGL_FATAL, "Quartz error: AddImageDescriptionExtension [colr] (%d)\n", qterr); + } + qterr = AddImageDescriptionExtension(yuv_qt_stuff.desc, yuv_qt_stuff.extension_fiel, kFieldInfoImageDescriptionExtension); + if (qterr) { + mp_msg(MSGT_VO, MSGL_FATAL, "Quartz error: AddImageDescriptionExtension [fiel] (%d)\n", qterr); + } + qterr = AddImageDescriptionExtension(yuv_qt_stuff.desc, yuv_qt_stuff.extension_clap, kCleanApertureImageDescriptionExtension); + if (qterr) { + mp_msg(MSGT_VO, MSGL_FATAL, "Quartz error: AddImageDescriptionExtension [clap] (%d)\n", qterr); + } + qterr = AddImageDescriptionExtension(yuv_qt_stuff.desc, yuv_qt_stuff.extension_pasp, kCleanApertureImageDescriptionExtension); + if (qterr) { + mp_msg(MSGT_VO, MSGL_FATAL, "Quartz error: AddImageDescriptionExtension [pasp] (%d)\n", qterr); + } + + mp_msg(MSGT_VO, MSGL_INFO, "Quartz: DecompressSequenceBeginS\n"); + mp_msg(MSGT_VO, MSGL_INFO, "Quartz: width=%d, height=%d, d_width=%d, d_height=%d\n", + width, height, d_width, d_height); + SetPort(GetWindowPort(theWindow)); + SetIdentityMatrix(&yuv_qt_stuff.matrix); + if ((d_width != width) || (d_height != height)) { + ScaleMatrix(&yuv_qt_stuff.matrix, + FixDiv(Long2Fix(d_width),Long2Fix(width)), + FixDiv(Long2Fix(d_height),Long2Fix(height)), + 0, + 0); + } + P = calloc(sizeof(PlanarPixmapInfoYUV420) + image_width * image_height * 2, 1); + switch (image_format) { + case IMGFMT_YV12: + case IMGFMT_IYUV: + case IMGFMT_I420: + P->componentInfoY.offset = sizeof(PlanarPixmapInfoYUV420); + P->componentInfoCb.offset = P->componentInfoY.offset + image_width * image_height; + P->componentInfoCr.offset = P->componentInfoCb.offset + (image_width * image_height) / 4; + P->componentInfoY.rowBytes = image_width; + P->componentInfoCb.rowBytes = image_width / 2; + P->componentInfoCr.rowBytes = image_width / 2; + break; + case IMGFMT_UYVY: + case IMGFMT_YUY2: + break; + } + qterr = DecompressSequenceBeginS(&yuv_qt_stuff.seqId, + yuv_qt_stuff.desc, + P, + image_width * image_height * 2, + NULL, //GetWindowPort(theWindow), + NULL, // GDHandle + NULL, // srcRect + &yuv_qt_stuff.matrix, + srcCopy, + NULL, // mask, + codecFlagUseImageBuffer, + codecLosslessQuality,//codecMinQuality, + bestSpeedCodec); + if (qterr) { + mp_msg(MSGT_VO, MSGL_FATAL, "Quartz error: DecompressSequenceBeginS (%d)\n", qterr); + } + mp_msg(MSGT_VO, MSGL_INFO, "Quartz: DecompressSequenceBeginS done\n"); + } + + ShowWindow (theWindow); if(vo_fs) @@ -345,6 +507,9 @@ static void flip_page(void) { + switch (image_format) { + case IMGFMT_RGB32: + { OSStatus error; CGrafPtr oldPort,deskPort; GDHandle oldGDevice; @@ -380,30 +545,102 @@ } SetGWorld(oldPort, oldGDevice); + } + break; + case IMGFMT_YV12: + case IMGFMT_IYUV: + case IMGFMT_I420: + case IMGFMT_UYVY: + case IMGFMT_YUY2: + { + OSErr qterr; + CodecFlags flags; + //mp_msg(MSGT_VO, MSGL_INFO, "Quartz: DecompressSequenceFrameWhen in flip_page\n"); + qterr = DecompressSequenceFrameWhen(yuv_qt_stuff.seqId, + P, + image_width * image_height * 2, + codecFlagUseImageBuffer, + &flags, + NULL, + NULL); + + if (qterr) { + mp_msg(MSGT_VO, MSGL_FATAL, "Quartz error: DecompressSequenceFrameWhen in flip_page (%d)\n", qterr); + } + } + break; + } } static uint32_t draw_slice(uint8_t *src[], int stride[], int w,int h,int x,int y) { + switch (image_format) { + case IMGFMT_YV12: + case IMGFMT_I420: + memcpy_pic(((char*)P) + P->componentInfoY.offset + x + image_width * y, src[0], + w, h, image_width, stride[0]); + x=x/2;y=y/2;w=w/2;h=h/2; + memcpy_pic(((char*)P) + P->componentInfoCb.offset + x + image_width / 2 * y, src[1], + w, h, image_width / 2, stride[1]); + memcpy_pic(((char*)P) + P->componentInfoCr.offset + x + image_width / 2 * y, src[2], + w, h, image_width / 2, stride[2]); + return 0; + + case IMGFMT_IYUV: + memcpy_pic(((char*)P) + P->componentInfoY.offset + x + image_width * y, src[0], + w, h, image_width, stride[0]); + x=x/2;y=y/2;w=w/2;h=h/2; + memcpy_pic(((char*)P) + P->componentInfoCr.offset + x + image_width / 2 * y, src[1], + w, h, image_width / 2, stride[1]); + memcpy_pic(((char*)P) + P->componentInfoCb.offset + x + image_width / 2 * y, src[2], + w, h, image_width / 2, stride[2]); + return 0; + } return -1; } static uint32_t draw_frame(uint8_t *src[]) { + switch (image_format) { + case IMGFMT_RGB32: image_data = src[0]; - DisposeGWorld(imgGWorld); NewGWorldFromPtr (&imgGWorld, k32ARGBPixelFormat, &imgRect, 0, 0, 0, image_data, image_width * 4); - - return 0; + return 0; + case IMGFMT_UYVY: + case IMGFMT_YUY2: + memcpy_pic(((char*)P), src[0], image_width * 2, image_height, image_width * 2, image_width * 2); + return 0; + } + return -1; } static uint32_t query_format(uint32_t format) { image_format = format; + image_qtcodec = 0; //Curently supporting only rgb32 format. if ((format == IMGFMT_RGB32)) return VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW; + +#if 0 + if ((format == IMGFMT_YV12) || (format == IMGFMT_IYUV) || (format == IMGFMT_I420)) { + image_qtcodec = kYUV420CodecType; + return VFCAP_CSP_SUPPORTED; + } + + if ((format == IMGFMT_YUY2)) { + image_qtcodec = kComponentVideoUnsigned; + return VFCAP_CSP_SUPPORTED; + } + + if ((format == IMGFMT_UYVY)) { + image_qtcodec = k422YpCbCr8CodecType; + return VFCAP_CSP_SUPPORTED; + } +#endif + return 0; } @@ -417,6 +654,54 @@ return 0; } +static uint32_t draw_yuv_image(mp_image_t *mpi) { + OSErr qterr; + CodecFlags flags; + + //mp_msg(MSGT_VO, MSGL_INFO, "Quartz: DecompressSequenceFrameWhen in draw_yuv_image\n"); + qterr = DecompressSequenceFrameWhen(yuv_qt_stuff.seqId, + P, + sizeof(PlanarPixmapInfoYUV420), + codecFlagUseImageBuffer, + &flags, + NULL, + NULL); + + if (qterr) { + mp_msg(MSGT_VO, MSGL_FATAL, "Quartz error: DecompressSequenceFrameWhen in draw_yuv_image (%d)\n", qterr); + } + return VO_TRUE; +} + +static uint32_t get_yuv_image(mp_image_t *mpi) { + if(mpi->type!=MP_IMGTYPE_EXPORT) return VO_FALSE; + + if(mpi->imgfmt!=image_format) return VO_FALSE; + + if(mpi->flags&MP_IMGFLAG_PLANAR){ + mpi->planes[0]=((char*)P) + P->componentInfoY.offset; + mpi->stride[0]=image_width; + mpi->width=image_width; + + if(mpi->flags&MP_IMGFLAG_SWAPPED){ + // I420 + mpi->planes[1]=((char*)P) + P->componentInfoCb.offset; + mpi->planes[2]=((char*)P) + P->componentInfoCr.offset; + mpi->stride[1]=image_width/2; + mpi->stride[2]=image_width/2; + } else { + // YV12 + mpi->planes[1]=((char*)P) + P->componentInfoCr.offset; + mpi->planes[2]=((char*)P) + P->componentInfoCb.offset; + mpi->stride[1]=image_width/2; + mpi->stride[2]=image_width/2; + } + mpi->flags|=MP_IMGFLAG_DIRECT; + return VO_TRUE; + } + return VO_FALSE; +} + static uint32_t control(uint32_t request, void *data, ...) { switch (request) @@ -426,6 +711,26 @@ case VOCTRL_FULLSCREEN: window_fullscreen(); return VO_TRUE; case VOCTRL_ONTOP: window_ontop(); return VO_TRUE; case VOCTRL_QUERY_FORMAT: return query_format(*((uint32_t*)data)); + case VOCTRL_GET_IMAGE: + switch (image_format) { + case IMGFMT_YV12: + case IMGFMT_IYUV: + // case IMGFMT_UYVY: + // case IMGFMT_YUY2: + case IMGFMT_I420: + return get_yuv_image(data); + break; + } + case VOCTRL_DRAW_IMAGE: + switch (image_format) { + case IMGFMT_YV12: + case IMGFMT_IYUV: + // case IMGFMT_UYVY: + // case IMGFMT_YUY2: + case IMGFMT_I420: + return draw_yuv_image(data); + break; + } } return VO_NOTIMPL; }