{"id":75,"date":"2016-01-02T17:35:18","date_gmt":"2016-01-02T17:35:18","guid":{"rendered":"http:\/\/www.ferzkopp.org\/wordpress\/?p=75"},"modified":"2016-01-08T07:44:29","modified_gmt":"2016-01-08T15:44:29","slug":"smplayer-scriptable-mediaplayer-v2-0","status":"publish","type":"post","link":"https:\/\/www.ferzkopp.net\/wordpress\/2016\/01\/02\/smplayer-scriptable-mediaplayer-v2-0\/","title":{"rendered":"smplayer"},"content":{"rendered":"<p>This is the source archive for <em>smplayer<\/em> a Linux+OpenGL based LUA scriptable mediaplayer that supports HD video playback and custom digital IO controls; it was used as <em>Digital Retina<\/em> media art installation by Jill Scott in Version 2.0.<\/p>\n<p><!--more--><\/p>\n<p>Index with all the ZIP files is here: <a href=\"http:\/\/www.ferzkopp.net\/Software\/smplayer2\/\">http:\/\/www.ferzkopp.net\/Software\/smplayer2\/<\/a><\/p>\n<p>The software is \u00a9 Andreas Schiffler, aschiffler at ferzkopp.net, 4 Jan 2009 but released under a liberal OSS license.<\/p>\n<p>&nbsp;<\/p>\n<h4>Documentation<\/h4>\n<p>&nbsp;<\/p>\n<h6>Introduction<\/h6>\n<p>The smplayer2 software was created as a frontend driver for digital signage project and has since been used in some media-art installations.<br \/>\nIt is based on a Linux PC with OpenGL graphics acceleration.<\/p>\n<p>Features:<br \/>\n&#8211; allows for flat-field calibrated synchronized playback on up to 2 screens<br \/>\n&#8211; can handle several SD and HD streams concurrently<br \/>\n&#8211; contains many commands for drawing graphics and text to screen<br \/>\n&#8211; contains many utility commands for controlling display script (i.e. parallel port IO, http interface)<br \/>\n&#8211; many image sources (images, video clips, webcam, VNC)<br \/>\n&#8211; based on the LUA scripting language<br \/>\n&#8211; runs stable for long times<br \/>\nLimitations (as per Ver 2.0 released 4 Jan 2009):<br \/>\n&#8211; video must be converted into proprietary file format (stream of compressed textures) before playback. This is done using a custom mplayer binary and the files may consume a lot of disk space.<br \/>\n&#8211; Only partially documented LUA API<br \/>\n&#8211; Scripts are not easy to program and debug; lack of IDE.<br \/>\n&#8211; Currently no (or very limited) audio support.<br \/>\n&#8211; \u2026 probably more issues \u263a \u2026<\/p>\n<p>Other than that, it is a very unique and capable piece of software that can help solve unique media display problems in a neat way.<\/p>\n<p>&nbsp;<\/p>\n<h6>General Software Design<\/h6>\n<p>The software implements a multithreaded scriptable \u201cgame loop\u201d. The graphics thread controls the drawing of OpenGL textures at a fixed rate to the physical screen. The drawing algorithm allows for flatfield calibration (keystone). The program thread executes the media script and performs all \u201cwork\u201d in controller- and visualizer-coroutines (not real threads). These make callbacks to scripts wich control visualizers.<br \/>\n[Diagram Here]<\/p>\n<h6>LUA Scripting Engine<\/h6>\n<p>smplayer2 uses the LUA programming engine for screen and media setup and to control the display while the player is running.<br \/>\nLua is a very compact, relatively fast and easily extensible functional programming language. Smplayer2 extends it with the following APIs:<br \/>\n&#8211; smplayer2 specific objects and functions<br \/>\n&#8211; utility APIs (byte buffer, bit manupulation, compression, etc.)<br \/>\n&#8211; curl API for web\/internet access<br \/>\n&#8211; various utility functions written in LUA code<\/p>\n<p>&nbsp;<\/p>\n<h6>Compiling smplayer2<\/h6>\n<p>The smplayer2 code is designed to compile and run on a 32bit Linux installation (i.e. last used version was Mandriva Linux 2008.1). Since the player requires hardware accelerated OpenGL support, the proprietary drivers (i.e. from Nvidia) are typically required.<br \/>\nThe core mediaplayer depends on many secondary libraries common to a Linux distribution. These should be installed as development packages prior to compiling smplayer2:<br \/>\n&#8211; SDL : simple direct media lib, core for graphics<br \/>\n&#8211; SDL_gfx : graphics primitives<br \/>\n&#8211; SDL_image : image loading<br \/>\n&#8211; SDL_net : network primitives<br \/>\n&#8211; SDL_ttf : font support and text drawing<br \/>\n&#8211; curl : http access, used by lcurl<br \/>\n&#8211; expat : XMl parser<br \/>\n&#8211; ffmpeg : video codecs, used by custom mplayer<br \/>\n&#8211; freetype : TTF rendering, used by SDL_ttf<br \/>\n&#8211; jpeg : JPG codec, used by SDL_image<br \/>\n&#8211; libbgrab : v4l interface, used by SDL_bgrab<br \/>\n&#8211; liblzf : fast compression, used by lbuffer and custom mplayer<br \/>\n&#8211; png : PNG codec, used by SDL_image<br \/>\n&#8211; openssl : SSL support, used by curl<br \/>\n&#8211; zlib : ZIP codec, used by SDL_image<br \/>\nThe following locally provided custom libraries need to be also compiled and installed:<br \/>\n&#8211; SDL_bgrab : libbgrab interface for SDL, multithreaded v4l-based framegrabber interface<br \/>\n&#8211; SDL_vnc : simple multithreaded VNC client for SDL<br \/>\nThe LUA scripting engine is provided and extended using these libraries which need to be compiled and installed:<br \/>\n&#8211; lua-5.0.2 : custom lua interpreter<br \/>\n&#8211; liblzf : fast compression, used by lbuffer<br \/>\n&#8211; lbuffer : LUA memory buffers<br \/>\n&#8211; lcurl : LUA interface to libcurl<br \/>\n&#8211; lthread : LUA threading interface<br \/>\n&#8211; lutils : collection of LUA utilities<br \/>\nThe video compression format is based on a custom mplayer output mode \u201cglstream\u201d. The source archive contains a custom mplayer version \u201cMPlayer-1.0rc1-glstream\u201d which should be compiled separately.<br \/>\nData Flow<br \/>\nThe following diagram shows the typical data flow and relevant commands between the main API objects:<br \/>\nglobals : physical screen calibration<br \/>\nsmplayer : logical screen information<br \/>\nft : frame table, maps of logical frames to the logical screen, corresponds to OpenGL textures<br \/>\nmt : media table, store of in-memory display surfaces<br \/>\n[Diagram Here]<\/p>\n<p>When smplayer is initially started, the frametable and mediatable are initially empty. The script\u2019s Init() callback is executed which generally sets up the physical screen, the calibration and ALL frames and media used during the execution of the program. The the display is opened and the \u201cgame loop\u201d entered. Repeated callbacks are made for each frame in the frametable to see if their textures need to be updated. On Mouse or Keyboard events, optional callbacks are executed. If the game loop is terminated the Done() callback is executed.<\/p>\n<h6>Script Callback Interface<\/h6>\n<p>The smplayer2 executable is started with a LUA script as input. The script needs to provide several callback functions. The following script is the default that needs to be extended by the user:<br \/>\n&#8212; Default smplayer2 script<br \/>\ndofile(&#8220;utility50.lua&#8221;)<br \/>\nfunction Config()<br \/>\n&#8212; Setup display configuration<br \/>\nend<br \/>\nfunction Init()<br \/>\n&#8212; Script initialization<br \/>\nend<br \/>\nfunction Done()<br \/>\n&#8212; Script cleanup<br \/>\nend<br \/>\nfunction Redraw(frame)<br \/>\n&#8212; Redraw of frame &#8216;frame&#8217; requested<br \/>\nend<br \/>\nfunction Key(c,s)<br \/>\n&#8212; Key c (string), sym s (int) pressed<br \/>\nend<br \/>\nfunction Mouse(s,x,y,t)<br \/>\n&#8212; Mouse clicked (t=1) or<br \/>\n&#8212; dragged (t=2) on screen s at coordinate x,y<br \/>\nend<\/p>\n<p>&nbsp;<\/p>\n<h6>Example Program (Code)<\/h6>\n<p>&#8212; Single frame showing single static image from file<br \/>\nfunction Init()<br \/>\n&#8212; create a frame which will display at 1Hz<br \/>\nmyframe=ft.start(1, 1024, 768, 1.00);<br \/>\n&#8212; create an empty output pane used to assemble graphics<br \/>\nmypane=mt.create(1024,768);<br \/>\n&#8212; load an image into (also creates a pane)<br \/>\nmyimg=mt.image(&#8220;Media\/column.jpg&#8221;);<br \/>\n&#8212; draw the image into the output pane<br \/>\nmt.blit(mypane, myimg, 0.25*1024,0,0);<br \/>\n&#8212; create a text pane<br \/>\nmytext = mt.text(\u201cHello\u201d, \u201carial.ttf\u201d,18,0, &#8220;white&#8221;);<br \/>\n&#8212; draw the text into the output pane<br \/>\nmt.blit(mypane, mytext, 10, 10, 1);<br \/>\n&#8212; update frame with pane<br \/>\nft.blit(myframe, mypane, 0, 0, 0);<br \/>\nend<br \/>\nfunction Redraw(frame)<br \/>\n&#8212; update screen with frame<br \/>\nft.update(myframe);<br \/>\nend<br \/>\nfunction Done()<br \/>\n&#8212; cleanup all objects<br \/>\nmyframe=nil<br \/>\nmypane=nil<br \/>\nmyimg=nil<br \/>\nmytext=nil<br \/>\ncollectgarbage();<br \/>\nend<\/p>\n<p>&nbsp;<\/p>\n<h6>Screen Setup<\/h6>\n<p>&nbsp;<\/p>\n<p>The system supports one or two physical screens.<br \/>\nThe application assumes that the X-server has been correctly configured beforehand (i.e. Nvidia driver with OpenGL and TWINVIEW enabled).<br \/>\nSmplayer2 will first parse the Init() section of the input script and then open a window of size [width * screens, height].<\/p>\n<p><strong>Physical to Logical Mapping<\/strong><br \/>\nOnce the physical window has been created, the media script always uses the logical screen coordinates of [0,0 \u2013 1,0] for all frame positioning and drawing commands. This means:<br \/>\n&#8211; screen size changes do not affect the playback<br \/>\n&#8211; the script must manage all aspect ratios and changes<br \/>\n<strong>Debugging of Setup<\/strong><br \/>\nAdditional settings can be specified to assist in the debugging of the application:<br \/>\nsmplayer.cursor=1 \u2013 enables the mouse cursor<br \/>\nsmplayer.outline=1 \u2013 enables the display of texture outlines<\/p>\n<p><strong>Flatfield Calibration<\/strong><br \/>\nTo allow for matching the full-screen output of the display device to the actual screen, the system supports a full flatfield calibration (4-point keystone correction). This can be used to:<br \/>\n&#8211; pixel match the output of two projectors displaying the same image<br \/>\n&#8211; make the images of two adjacent projectors to appear seamless<br \/>\n&#8211; fit a screen area to an object<br \/>\nTo do that the setCorners command receives an array 8 numbers corresponding to the 4 corners of the screen in the unit coordinate system [0,0 \u2013 1,1].<br \/>\n&#8212; default corners<br \/>\ndata_screen_one =<br \/>\n{[1]=0,[2]=0,[3]=1,[4]=0,<br \/>\n[5]=1,[6]=1,[7]=0,[8]=1,}<br \/>\nglobals.setCorners(1,data_screen_one)<br \/>\n&#8212; half size centered corners<br \/>\ndata_screen_two =<br \/>\n{[1]=0.25,[2]=0.25,[3]=0.75,[4]=0.25,<br \/>\n[5]=0.75,[6]=0.75,[7]=0.25,[8]=0.75,}<br \/>\nglobals.setCorners(2,data_screen_two)<\/p>\n<p><strong>Interactive Screen Setup (Code)<\/strong><br \/>\nThe following Mouse callback function fragment can be used to interactively change the screen calibration on the fly.<br \/>\n&#8212; Assumes the variable edit_screen was setup<br \/>\n&#8212; and is 1 or 2 if in edit mode or 0 otherwise<br \/>\n&#8212; TODO: toggle edit_screen from the keyboard<br \/>\nfunction Mouse(screen,x,y,t)<br \/>\n&#8212; warp screen<br \/>\nif (edit_screen&gt;0) then<br \/>\nif (edit_screen==screen) then<br \/>\ncorners = smplayer.getCorners(edit_screen)<br \/>\nif (edit_corner==1) then<br \/>\ncorners[1]=x<br \/>\ncorners[2]=y<br \/>\nelseif (edit_corner==2) then<br \/>\ncorners[3]=x<br \/>\ncorners[4]=y<br \/>\nelseif (edit_corner==3) then<br \/>\ncorners[5]=x<br \/>\ncorners[6]=y<br \/>\nelseif (edit_corner==4) then<br \/>\ncorners[7]=x<br \/>\ncorners[8]=y<br \/>\nelseif (edit_corner==5) then<br \/>\n&#8212; no shift<br \/>\nend<br \/>\nsmplayer.setCorners(edit_screen, corners)<br \/>\n&#8212; TODO: save the current configuration stored<br \/>\n&#8212; in the \u2018corners\u2019 variable<br \/>\nend<br \/>\nend<br \/>\nend<\/p>\n<h6>Video Compression<\/h6>\n<p>&nbsp;<\/p>\n<p>The smplayer2 video playback uses a stream of compressed textures which are generated by the \u201cglstream\u201d output plugin of a custom mplayer version. The glstream writer supports several GL pixel modes, S3TC compressed textures, alpha channel creation and LZF compression of frames.<br \/>\nThe source to the driver can be found in the libvo\\vo_glstream.c file.<\/p>\n<p><strong>Mplayer Usage<\/strong><br \/>\nExample:<br \/>\nmplayer -vo glstream:verbose:dxt5:file=output.dat input.avi<br \/>\nCompression Options:<br \/>\nnone (no compression, no output &#8211; for benchmarking)<br \/>\nrgba (uncompressed texture, 32bpp)<br \/>\nlum (uncompressed texture, luminance, 8bpp)<br \/>\nluma (uncompressed texture, luminance-alpha, 16bpp)<br \/>\ndxt1 (6bpp)<br \/>\ndxt3 (default, 8bpp)<br \/>\ndxt5 (8bpp)<br \/>\nOther options:<br \/>\nfile=filename Save to file &lt;filename&gt; (default: texture.dat)<br \/>\nchromakey Chromakey blue for alpha (default: off)<br \/>\nlzf LZF compress frame (default: off)<br \/>\nverbose Turn on verbose output (default: off)<br \/>\n<strong><br \/>\nglstream Fileformat<\/strong><br \/>\nFileformat is:<br \/>\n[ file ] = [ fragment 0 ] [ fragment 1 ] . . .<br \/>\n[ fragment X ] = [ header X ] [ frame X ]<br \/>\nwhere<br \/>\n[ header (22 bytes) ] =<br \/>\n[ magic (4 char) ]<br \/>\n[ flags (uint16) ]<br \/>\n[ width (uint16) ]<br \/>\n[ height (uint16) ]<br \/>\n[ OpenGL format (uint32) ]<br \/>\n[ x uncompressed bytes per frame (uint32) ]<br \/>\n[ y compressed bytes per frame (uint32) ]<br \/>\n[ frame ] =<br \/>\n[ data (x or y bytes per frame depending on flags) ]<br \/>\n[ magic ] =<br \/>\nGLST<br \/>\n[ OpenGL format ] =<br \/>\nGL_RGBA |<br \/>\nGL_LUMINANCE |<br \/>\nGL_LUMINANCE_ALPHA |<br \/>\nGL_COMPRESSED_RGBA_S3TC_DXT1_EXT |<br \/>\nGL_COMPRESSED_RGBA_S3TC_DXT3_EXT |<br \/>\nGL_COMPRESSED_RGBA_S3TC_DXT5_EXT<br \/>\n[ flags ] =<br \/>\n0x0001 &#8211; LZF compressed frame<br \/>\nNote: Headers are always repeated before each frame to make reading streams easier.<\/p>\n<p><strong>Sample Conversion Script<\/strong><br \/>\nThe following bash script will convert all input AVI files into \u201csquare pixel\u201d SD quality output texture streams cropping the center 480&#215;480 square from the source video frame.<br \/>\n#!\/bin\/sh<br \/>\nMPLAYER=&#8221;\/usr\/local\/bin\/mplayer&#8221;<br \/>\nPARAMS=&#8221;-nosound -noaspect -vop scale=256:256,crop=480:480:80:0 -vo glstream:dxt3:lzf:verbose:file=&#8221;<br \/>\nfor i in *.avi; do<br \/>\nINPUT=$i<br \/>\nOUTPUT=${i%%.avi}_box.dxt3_lzf<br \/>\n$MPLAYER $PARAMS$OUTPUT $INPUT<br \/>\ndone<br \/>\nNote: The target frame size (set with ft.start() in the LUA script) and the texture size (set by the scale option of mplayer) should match for best performance during playback.<\/p>\n<h6>Smplayer2 LUA API<\/h6>\n<p><u>Global API<\/u><br \/>\n<strong>Namespace<\/strong><br \/>\nglobals.xyz<br \/>\nsmplayer.xyz<br \/>\n<strong>Properties<\/strong><br \/>\nwidth (Get, Set)<br \/>\nheight (Get, Set)<br \/>\nscreens (Get, Set)<br \/>\ncursor (Get, Set)<br \/>\noutline (Get, Set)<br \/>\ndraw (Get, Set)<br \/>\n<strong>Functions<\/strong><br \/>\ncreate() : create object and make it global (automatically called by smplayer2)<br \/>\nlist() : displays current smplayer configuration for debugging purposes<br \/>\nfloat[8] = getCorners(screen) : returns current display positions of screens<br \/>\nsetCorners(screen, float[8]) : set display positions of screens for flatfield calibration<br \/>\nquit() : quit game loop and exit smplayer<\/p>\n<p><u>Frametable API<\/u><br \/>\n<strong>Namespace<\/strong><br \/>\nft.xyz<br \/>\n<strong>Properties<\/strong><br \/>\nscreens (get, set) : the screen to place the frame onto<br \/>\ntw (get) : the texture width; power of two<br \/>\nth (get) : the texture height; power of two<br \/>\nr (get, set) : red filter, range=[0.0 \u2013 1.0], default=1.0<br \/>\ng (get, set) : green filter, range=[0.0 \u2013 1.0], default=1.0<br \/>\nb (get, set) : blue filter, range=[0.0 \u2013 1.0], default=1.0<br \/>\na (get, set) : transparency of frame, range=[0.0 \u2013 1.0], default=1.0<br \/>\nrate (get, set) : update framerate in Hz<br \/>\nx (get, set) : top right X position on logical screen, range=[0.0 \u2013 1.0]<br \/>\ny (get, set) : top right Y position on logical screen, range=[0.0 \u2013 1.0]<br \/>\nw (get, set) : width on logical screen, range=[0.0 \u2013 1.0]<br \/>\nh (get, set) : height on logical screen, range=[0.0 \u2013 1.0]<br \/>\n<strong>Functions<\/strong><br \/>\nstart(frame_id, texture_width, texture_height, framerate_hz) : creates new frame object in frametable<br \/>\nlist() : lists all frames in frametable for debugging purposes<\/p>\n<p>stop(frame_id) : deletes frame from frametable<br \/>\nclone(source_frame_id, target_frame_id) : sets the content to be cloned from existing frame<br \/>\nunclone(frame_id) : disables cloning for a frame<br \/>\ndelay(delay_milliseconds) : do a millisecond delay<br \/>\ncolor(frame_id, r, g, b, a) : sets the color filters and transparency<br \/>\nrate(frame_id, rate) : sets the frame rate for updates<br \/>\nfloat[8] = getCorners(frame_id) gets the current corner coordinates in the logical coordinate system [0,0 \u2013 1,1]<br \/>\nsetCorners(frame_id, float[8]) : sets the current corner coordinates coordinates in the logical coordinate system [0,0 \u2013 1,1]<br \/>\nwarp(frame_id, x1,y1,x2,y2,x3,y3,x4,y4) : sets the current corner coordinates coordinates in the logical coordinate system [0,0 \u2013 1,1]; equivalent to setCorners but different call interface<br \/>\ncrop(frame_id, float[8]) : : sets new source texture coordinates; used for cropping or enlarging textures<br \/>\nblit(frame_id, media_id, x, y, blendFlag) : transfer mediatable content into frame<br \/>\ns3t_read(frame_id, fileHandle) : read texture into frame; sync IO<br \/>\ns3tc_tioread(frame_id, tioFileHandle) : read texture into frame; multithreaded async IO<br \/>\nsave(frame_id, filename) : save current frame to a file for debugging<br \/>\nupdate(frame_id) : mark frame for texture update when rate interval expires<br \/>\nvncopen(frame_id, serverHost, serverPort, serverEncoding, serverPassword) : open connection to VNC server<br \/>\nvncupdate(frame_id) : update frame from vnc connection<br \/>\nvncclose(frame_id) : close vnc connection<br \/>\ngrabopen(frame_id, device, width, height, channel, mode, frequencyRegion, frequencyIndex) : open framegrabber device<\/p>\n<p>grabupdate(frame_id, deinterlaceFlag) : update frame from framegrabber device<br \/>\ngrabclose(frame_id) : close framegrabber connection<\/p>\n<p><u>Mediatable API<br \/>\n<\/u><strong>Namespace<\/strong><br \/>\nmt.xyz<br \/>\n<strong>Properties<\/strong><br \/>\nw (Get) : width of media surface<br \/>\nh (Get) : heigh of media surface<br \/>\ncx (Get, Set) : top right X position of clipping rectangle of media surface<br \/>\ncy (Get, Set) : top right Y position of clipping rectangle of media surface<br \/>\ncw (Get, Set) : width of clipping rectangle of media surface<br \/>\nch (Get, Set) : height of clipping rectangle of media surface<br \/>\n<strong>Functions<\/strong><br \/>\nimage(filename) : create new media surface by load image from file<br \/>\nclone(media_id) : create new media surface from existing media<br \/>\ncreate(width, height) : create new blank media surface<br \/>\ntext(text, fontfile, fontsize, fontstyle, color) : create new media surface from text<br \/>\nrotozoom(media_id, angle, zoomFactor) : create new media surface by rotating and zooming existing media surface<br \/>\nlist() : display all media surfaces<br \/>\nclear(media_id, alpha) : clear media surface to particular alpha value<br \/>\nfill(media_id, color) : fill media surface with color<br \/>\nwidth(media_id, w) : set width of media surface<br \/>\nheight(media_id, h) : set height of media surface<\/p>\n<p>blit(source_media_id, target_media_id, x, y, useAlphaFlag) : blit source media surface to target media surface at specified position optionally using alpha blending<\/p>\n<p>clip(media_id, x,y,w,h) : set clipping rectangle for drawing operations on media surface<\/p>\n<p>unclip(media_id) : clear clipping rectangle of media surface<\/p>\n<p><strong>SDL_gfx Mapped Functions<\/strong><\/p>\n<p>pixel(media_id, x, y, color) : draw pixel<br \/>\nhline(media_id, x1, x2, y, color) : draw horizontal line<br \/>\nvline(media_id, x, y1, y2, color) : draw vertical line<br \/>\nrectangle(media_id, x1,y1,x2,y2, color) : draw rectangle<br \/>\nbox(media_id, x1,y1,x2,y2, color) : draw filled rectangle<br \/>\nline(media_id, x1,y1,x2,y2, color) : draw line<br \/>\naaline(media_id, x1,y1,x2,y2, color) : draw antialiased line<br \/>\ncircle(media_id, x,y,r, color) : draw circle<br \/>\naacircle(media_id, x,y,r, color) : draw antialiased circle<br \/>\nfilledcircle(media_id, x,y,r, color) : draw filled circle<br \/>\nellipse(media_id, x,y,rx,ry, color) : draw ellipse<br \/>\naaellipse(media_id, x,y,rx,ry, color) : draw antialiased ellipse<br \/>\nfilledellipse(media_id, x,y,rx,ry, color) : draw filled ellipse<br \/>\npie(media_id, x,y,rad,start,end, color) : draw pie<br \/>\nfilledpie(media_id, x,y,rad,start,end, color) : draw filled pie<br \/>\ntrigon(media_id, x1,y1,x2,y2,x3,y3, color) : draw triangle<br \/>\naatrigon(media_id, x1,y1,x2,y2,x3,y3, color) : draw antialiased triangle<br \/>\nfilledtrigon(media_id, x1,y1,x2,y2,x3,y3, color) : draw filled triangle<br \/>\npolygon() : not implemented<br \/>\naapolygon() : not implemented<br \/>\nfilledpolygon() : not implemented<br \/>\nbezier() : not implemented<br \/>\nstring(media_id, x,y, text, color) : draw text in 8&#215;8 fixed width font to media surface<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This is the source archive for smplayer a Linux+OpenGL based LUA scriptable mediaplayer that supports HD video playback and custom digital IO controls; it was used as Digital Retina media art installation by Jill Scott in Version 2.0.<\/p>\n","protected":false},"author":1,"featured_media":122,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"ngg_post_thumbnail":0,"footnotes":""},"categories":[22],"tags":[36,61,62,31,35],"class_list":["post-75","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-software","tag-c","tag-digital-retina","tag-jill-scott","tag-media-art","tag-opengl"],"_links":{"self":[{"href":"https:\/\/www.ferzkopp.net\/wordpress\/wp-json\/wp\/v2\/posts\/75","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.ferzkopp.net\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.ferzkopp.net\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.ferzkopp.net\/wordpress\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.ferzkopp.net\/wordpress\/wp-json\/wp\/v2\/comments?post=75"}],"version-history":[{"count":5,"href":"https:\/\/www.ferzkopp.net\/wordpress\/wp-json\/wp\/v2\/posts\/75\/revisions"}],"predecessor-version":[{"id":121,"href":"https:\/\/www.ferzkopp.net\/wordpress\/wp-json\/wp\/v2\/posts\/75\/revisions\/121"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.ferzkopp.net\/wordpress\/wp-json\/wp\/v2\/media\/122"}],"wp:attachment":[{"href":"https:\/\/www.ferzkopp.net\/wordpress\/wp-json\/wp\/v2\/media?parent=75"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.ferzkopp.net\/wordpress\/wp-json\/wp\/v2\/categories?post=75"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.ferzkopp.net\/wordpress\/wp-json\/wp\/v2\/tags?post=75"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}