/* * softdevice plugin for VDR * * Copyright (C) 2005 Martin Wache * * This code is distributed under the terms and conditions of the * GNU GENERAL PUBLIC LICENSE. See the file COPYING for details. * * $Id: video-shm.c,v 1.20 2007-10-13 11:17:48 lucke Exp $ */ #include "video-shm.h" #include "utils.h" //#define SHMDEB(out...) {printf("SHMSERV[%04d]:",(int)(getTimeMilis() % 10000));printf(out);} #ifndef SHMDEB #define SHMDEB(out...) #endif //#define SIGDEB(out...) {printf("SIGSERV[%04d]:",(int)(getTimeMilis() % 10000));printf(out);} #ifndef SIGDEB #define SIGDEB(out...) #endif cShmRemote::~cShmRemote(){ SHMDEB("~cShmRemote\n"); Stop(); SHMDEB("~cShmRemote returned from cancel\n"); }; void cShmRemote::Action(void) { while (active) { SIGDEB("cShmRemote trying to get a lock\n"); sem_wait_lock(vout->ctl->semid,KEY_MUT); SIGDEB("cShmRemote got lock\n"); if (!vout->ctl->attached) { vout->setupStore->shouldSuspend=1; } else vout->setupStore->shouldSuspend=0; if (vout->ctl->key!=NO_KEY) { SHMDEB("cShmRemote put key\n"); // put and clear Put(vout->ctl->key); vout->ctl->key=NO_KEY; }; // clear signal sem_zero(vout->ctl->semid,KEY_SIG); // release lock sem_sig_unlock(vout->ctl->semid,KEY_MUT); SIGDEB("wait for a new key\n"); sem_wait_lock(vout->ctl->semid,KEY_SIG); }; SHMDEB("ShmRemote thread ended\n"); }; /*----------------------------------------------------------------------------*/ cShmVideoOut::cShmVideoOut(cSetupStore *setupStore, cSetupSoftlog *Softlog) : cVideoOut(setupStore, Softlog) { ctl_key = CTL_KEY; bool Clear_Ctl=false; InitPicBuffer(&privBuf); // first try to get an existing ShmCltBlock if ( (ctl_shmid = shmget(ctl_key,sizeof(ShmCtlBlock), 0666)) >= 0 ) { fprintf(stderr,"cShmVideoOut: Got ctl_shmid %d shm ctl!\n",ctl_shmid); } else if ( (ctl_shmid = shmget(ctl_key,sizeof(ShmCtlBlock), IPC_CREAT | 0666)) >= 0 ) { fprintf(stderr,"cShmVideoOut: Created ctl_shmid %d shm ctl!\n",ctl_shmid); // created ShmCltBlock, clear it Clear_Ctl=true; } else { fprintf(stderr,"cShmVideoOut: Error creating shm ctl!\n"); exit(-1); }; // attach to the control block if ( (ctl = (ShmCtlBlock*)shmat(ctl_shmid,NULL,0)) == (ShmCtlBlock*) -1 ) { fprintf(stderr,"cShmVideoOut: Error attatching shm ctl!\n"); exit(-1); }; if (Clear_Ctl) { ctl->semid=-1; ctl->pict_shmid=-1; ctl->osd_shmid=-1; ctl->attached = 0; ctl->setup_shmid = setupStore->setupStoreShmid; }; if ( ctl->semid == -1 ) { // create semaphores if ( (ctl->semid = semget(IPC_PRIVATE, 4, 0666 | IPC_CREAT) ) == -1) { fprintf(stderr,"cShmVideoOut: Error creating semaphore set!\n"); exit(-1); }; SHMDEB("created semaphores id %d\n",ctl->semid); }; semun sem_val; sem_val.val = 0; // nothing available if ( semctl(ctl->semid,PICT_SIG, SETVAL, sem_val) == -1 ) { fprintf(stderr,"cShmVideoOut: Error resetting semaphore PICT_SIG\n"); exit(-1); }; if ( semctl(ctl->semid,KEY_SIG,SETVAL,sem_val) == -1 ) { fprintf(stderr,"cShmVideoOut: Error resetting semaphore KEY_SIG\n"); exit(-1); }; sem_val.val = 1; // not locked if ( semctl(ctl->semid,PICT_MUT, SETVAL, sem_val) == -1 ) { fprintf(stderr,"cShmVideoOut: Error resetting semaphore PICT_MUT\n"); exit(-1); }; if ( semctl(ctl->semid,KEY_MUT,SETVAL,sem_val) == -1 ) { fprintf(stderr,"cShmVideoOut: Error resetting semaphore KEY_MUT\n"); exit(-1); }; curr_pict_shmid=-1; curr_osd_shmid=-1; //ctl->pict_shmid=-1; curr_pict=NULL; osd_surface=NULL; ctl->key=NO_KEY; #ifdef __APPLE__ // FIXME... should be decided at run time remote = new cShmRemote("softdevice-quartz",this); #else remote = new cShmRemote("softdevice-xv",this); #endif }; cShmVideoOut::~cShmVideoOut() { SHMDEB("cShmVideoOut destructor\n"); // stop&delete remote remote->Stop(); semun sem_val; // remove semaphore if ( semctl(ctl->semid, 0, IPC_RMID, sem_val ) == -1) { fprintf(stderr,"cShmVideoOut: Error removing semaphore set!\n"); exit(1); } ctl->semid=-1; if (osd_surface) shmdt(osd_surface); osd_surface=NULL; if (curr_pict) shmdt(curr_pict); curr_pict=NULL; /* if ( ctl_shmid > 0) shmctl (ctl_shmid, IPC_RMID, 0); */ }; void cShmVideoOut::AdjustOSDMode() { if ( curr_osd_shmid==-1 ) { current_osdMode=OSDMODE_SOFTWARE; return; } current_osdMode = setupStore->osdMode; } void cShmVideoOut::GetOSDDimension(int &OsdWidth,int &OsdHeight, int &xPan, int &yPan) { switch (current_osdMode) { case OSDMODE_PSEUDO : OsdWidth=ctl->osd_width>ctl->osd_max_width? ctl->osd_max_width:ctl->osd_width; OsdHeight=ctl->osd_height>ctl->osd_max_height? ctl->osd_max_height:ctl->osd_height; xPan = yPan = 0; break; case OSDMODE_SOFTWARE: OsdWidth=swidth; OsdHeight=sheight; xPan = sxoff; yPan = syoff; break; }; }; void cShmVideoOut::GetOSDMode(int &Depth, bool &HasAlpha, bool &AlphaInversed, bool &IsYUV) { if ( current_osdMode==OSDMODE_SOFTWARE ) { IsYUV=true; return; }; IsYUV=false; Depth=ctl->osd_depth; HasAlpha=false; AlphaInversed=false; }; void cShmVideoOut::CheckShmIDs() { if ( ctl->pict_shmid != curr_pict_shmid ) { if (curr_pict) { shmdt(curr_pict); curr_pict=NULL; }; if ( ctl->pict_shmid != -1 ) { if ( (curr_pict = (uint8_t *)shmat(ctl->pict_shmid,NULL,0)) == (uint8_t*) -1 ) { fprintf(stderr,"cShmVideoOut: Warning! Could not attatch to shm pict! Assuming no client connected.\n"); ctl->pict_shmid = -1; ctl->attached = 0; curr_pict=NULL; return; } curr_pict_shmid=ctl->pict_shmid; privBuf.format=ctl->format; privBuf.max_width=ctl->max_width; privBuf.max_height=ctl->max_height; switch (privBuf.format) { case PIX_FMT_YUV420P: SHMDEB("new format YUV420P\n"); privBuf.pixel[0]=curr_pict+ctl->offset0; privBuf.pixel[1]=curr_pict+ctl->offset1; privBuf.pixel[2]=curr_pict+ctl->offset2; privBuf.stride[0]=ctl->stride0; privBuf.stride[1]=ctl->stride1; privBuf.stride[2]=ctl->stride2; break; case PIX_FMT_YUV422: SHMDEB("new format YUV422\n"); privBuf.pixel[0]=curr_pict+ctl->offset0; privBuf.pixel[1]=privBuf.pixel[1]=NULL; privBuf.stride[0]=ctl->stride0; privBuf.stride[1]=privBuf.stride[2]=0; break; default: break; } SHMDEB("new pict %p shmid %d\n",curr_pict,curr_pict_shmid); }; }; if ( ctl->osd_shmid != curr_osd_shmid ) { if (osd_surface) { shmdt(osd_surface); osd_surface=NULL; }; if ( ctl->osd_shmid != -1 ) { SHMDEB("get new osd_surface %p\n",osd_surface); if ( (osd_surface = (uint8_t *)shmat(ctl->osd_shmid,NULL,0)) == (uint8_t*) -1 ) { fprintf(stderr,"cShmVideoOut: Warning! Could not attatch to osd pict(%d)! Assuming no client connected.\n", ctl->osd_shmid); ctl->pict_shmid = -1; ctl->osd_shmid = -1; ctl->attached = 0; osd_surface=NULL; return; } curr_osd_shmid=ctl->osd_shmid; }; SHMDEB("got new osd_surface %p\n",osd_surface); }; } void cShmVideoOut::GetLockOsdSurface(uint8_t *&osd, int &stride, bool *&dirtyLines) { SHMDEB("GetLockOsdSurface osd_surface %p osd_shmid %d\n",osd_surface,ctl->osd_shmid); CheckShmIDs(); osd=osd_surface; stride=ctl->osd_stride; dirtyLines=NULL; }; int cShmVideoOut::GetOSDColorkey() { SHMDEB("GetOSDColorkey\n"); CheckShmIDs(); if (!ctl) return 0x000000; return ctl->colorkey; }; void cShmVideoOut::ClearOSD() { SHMDEB("ClearOsd\n"); cVideoOut::ClearOSD(); /* if (current_osdMode == OSDMODE_SOFTWARE) return; */ CheckShmIDs(); if (!ctl) return; SHMDEB("osd_surface %p dim: %d,%d depth %d colorkey %d \n", osd_surface,ctl->osd_max_height,ctl->osd_max_width, ctl->osd_depth,ctl->colorkey); if (osd_surface) { uint32_t *dst32=(uint32_t*)osd_surface; uint16_t *dst16=(uint16_t*)osd_surface; uint8_t *dst8=(uint8_t*)osd_surface; int i=ctl->osd_max_height*ctl->osd_max_width-1; switch (ctl->osd_depth) { case 32: while (i) { dst32[i]=ctl->colorkey; i--; }; break; case 16: while (i) { ARGB_TO_RGB(RGB16,dst16, ctl->colorkey); dst16++; i--; }; break; case 24: while (i) { ARGB_TO_RGB(RGB24,dst8, ctl->colorkey); dst8+=3; i--; }; break; default: memset (osd_surface, 0, ctl->osd_stride *ctl->osd_max_height); }; }; ctl->new_osd++; sem_sig_unlock(ctl->semid,PICT_SIG); }; void cShmVideoOut::CommitUnlockOsdSurface() { SHMDEB("CommitOsd\n"); ctl->new_osd++; sem_sig_unlock(ctl->semid,PICT_SIG); }; void cShmVideoOut::Suspend() { SHMDEB("Suspend shm server\n"); if (osd_surface) { shmdt(osd_surface); osd_surface=NULL; curr_osd_shmid=-1; }; if (curr_pict) { shmdt(curr_pict); curr_pict=NULL; curr_pict_shmid=-1; } }; void cShmVideoOut::YUV(sPicBuffer *buf) { if (!ctl->attached) { setupStore->shouldSuspend=1; return; } else setupStore->shouldSuspend=0; SIGDEB("YUV trying to get a lock\n"); sem_wait_lock(ctl->semid,PICT_MUT,SEM_UNDO); SIGDEB("YUV got a lock\n"); CheckShmIDs(); if ( ctl->pict_shmid==-1 || !curr_pict ) { // unlock picture ctl sem_sig_unlock(ctl->semid,PICT_MUT,SEM_UNDO); SHMDEB(" no pict_shmid or no curr_pict unlock and return\n"); return; }; if ( cutTop != setupStore->cropTopLines || cutBottom != setupStore->cropBottomLines || cutLeft != setupStore->cropLeftCols || cutRight != setupStore->cropRightCols) { cutTop = setupStore->cropTopLines; cutBottom = setupStore->cropBottomLines; cutLeft = setupStore->cropLeftCols; cutRight = setupStore->cropRightCols; ClearPicBuffer(&privBuf); } ctl->width= fwidthmax_width ? fwidth : ctl->max_width; ctl->height= fheightmax_height ? fheight : ctl->max_height; ctl->new_afd=current_afd; ctl->new_asp=GetAspect_F(); if (OSDpresent && current_osdMode==OSDMODE_SOFTWARE) { CopyPicBufAlphaBlend(&privBuf,buf, OsdPy,OsdPu,OsdPv,OsdPAlphaY,OsdPAlphaUV, OSD_FULL_WIDTH, cutTop,cutBottom,cutLeft,cutRight); } else { CopyPicBuf(&privBuf,buf, cutTop,cutBottom,cutLeft,cutRight); }; ctl->new_pict++; if (ctl->new_pict>30) { printf("new_pict > 30; assuming client died!\n"); ctl->attached=0; }; sem_sig_unlock(ctl->semid,PICT_MUT,SEM_UNDO); sem_sig_unlock(ctl->semid,PICT_SIG); SIGDEB("send signal\n"); }; #ifdef USE_SUBPLUGINS /* --------------------------------------------------------------------------- */ extern "C" void * SubPluginCreator(cSetupStore *s, cSetupSoftlog *log) { return new cShmVideoOut(s,log); } #endif