| Playmodes 2 v0.01 |
|
|
|
| Written by arturo castro |
| Wednesday, 31 December 2008 15:41 |
|
Here's the first public release of the new Playmodes architecture. Playmodes is developed with openframeworks. The new Playmodes is based in the openframeworks event system, that is still under development and for what this software is being also a test platform as it has diferent kinds of events and is also threaded. You will need a modified openframeworks version that you can find in the links at the end of this post. This openframeworks version is not oficial and will be changed for sure for 0.06 so don't rely on it for any development. Also expect some changes in the event system in Playmodes. Apart from that, the public interface should remain more or less unchanged in the future. Here's a brief explanation of the object model and it's use... + download
pipeline: in this directory you will find the base classes from what most of the Playmodes classes inherit. There's an object model for video and another one for audio but the syntax is really similar between them.
event notification: a buffer receives frames from a grabber whenever there's a new frame available so the grabber notifies a new frame and the buffer stores it. To add and remove listeners to an specific source you can call: void addListener(VideoFrameListener * listener)void removeListener(VideoFrameListener * listener)Also most of the objects that receive data in Playmodes have can be created by passing a source in it's constructor, so most of the times you won't need this methods. Sources have an event named newFrameEvent so if you implement you're own sources you will need to notify this method whenever new data is available: newFrameEvent.notify(this, frame); method call: a renderer needs a new frame in the draw call so it calls it source getNextFrame method to get the frame and draw it.VideoFrame * getNextVideoFrame()
grabbers : in this directory you can find the audio and video grabbers. This classes are responsible for grabbing data from a physical device or file and provide that data to other objects. In the current implementation these are the only classes that are only sources. Most of the rest of the classes are both sources and sinks (buffers, headers and filters) or just sins (renderers).
buffers: audio and video buffers store a limited amount of data. Whenever a buffer is full, it begins to remove the first frames to be able to add the new ones. The removal of a frame is delayed till it's not used anymore so there's no problem with threaded applications. Take a look to the frame description to know how it works, but related to buffers the only thing you need to know is they automatically retain frames when you ask for them, so if you recover a frame directly from a buffer you just need to release it when you finish using it. Buffers are prepared to work in threaded environments. Apart from the delayed removal of frames they also provide lock and unlock methods. Most of the times you won't need to use them, they're used from headers so position calculations based on buffer size are thread safe. To recover data from a buffer you can use the following methods: VideoFrame * getVideoFrame(int position); // frame number in the buffer VideoFrame * getVideoFrame(ofTimeDiff time); // frame at n microseconds from the end of the buffer VideoFrame * getVideoFrame(float pct); // % of the buffer VideoFrame * getNextVideoFrame(); // the last video frame in the buffer ofTimeDiff is indeed poco's TimeDiff, and you can just use an integer number to call that version of getFrame. Most of the times you won't need this methods. instead you will create a header, that can be set to provide frames from a buffer at diferent speeds, delays, loop modes... Buffers are 'position safe' so if you try to access a position that doesn't exists you will get the nearest frame (the beginning or the end of the buffer) Buffers extend Source and Sink. They provide a constructor that receives a Source and register to it's newFrame event so they receive the data as soon as it's avalable. You can freeze a buffer by calling it's stop method so although it's asociated source continues generating data, the buffer won't store it. When you want to continue storing data, just call resume: void stop(); // stop receiving new frames The size of a buffer is determined from a constant defined in it's header, so it's hardcoded andby now cannot be changed at runtime. This has been a design decision as we think when you're running Playmodes you will adjust this variable to the maimum ram you have available in your computer. At some point this will be changed to an xml config file, but this won't affect your current code.
AVHeaders: This classes are usually the prefered way of accessing a buffer. They have methods for setting their speed, delay from the end of the buffer, loop and in/out position. The position can be also setted from another class, in the example app that ships with the current version, this is used to set it with osc messages from other applications. This is one of the key concepts in the new architecture, separating the position control from the previous core object we gain in flexibility in the whole system as from here you can connect all kind of filters, renderers and in general any sink with different timing over the same buffer. By now headers doesn't notify the new frame event as in most implementations, from here the timing will be controlled by the opengl draw cycle or the audioRequested event. This can be limiting if you need to connect certain kind of sinks to a header like filters or another buffer. In those cases renderers will be responsible for calling that sinks before drawing or sending audio data to the sound card. This is the most important methods in the public interface of a video header (the audio one is exactly the same): VideoHeader(VideoBuffer *buffer) The constructor, although most sinks in Playmodes have a constructor to receive data from any kind of source, headers are meant to be connected only to a buffer. void draw() This method doesn't draw the frame pointed by the header but the state of it, it's position in the buffer, the in and out position and in the case of the audio header the density of the returned frame. VideoFrame * getNextVideoFrame() Returns the frame in the buffer at the current position of the header. int getNextPosition() Although this method is mainly used inside of the same header to know what frame it has to return, you can call it to know what frame of the buffer the header is pointing at. float speed; int delay; float in, out; bool loopMode; This variables are used to set the different parameters of the header, they determine what frame will be returned when you call the getNextFrame method. void setPct(float & pct) void incrPct(float & incr) This methods are used to set the position of the buffer in percentage(0 to 1) of the current buffer size. In the future we will provide similar methods to set the position in ms. In the example application provided with the current version this are used to set the position from external applications with osc messages.
renderers: this classes are ment to draw video frames or send audio frames to the soundcard. Currently there's only renderers for video as the current audio implementation is really simple so we are just using the main app (or any other audioRequested listener) to 'render' audio.
void draw()
The controls provide the same properties as a simple renderer, when you set some of those properties, in the next update cycle, the first renderer is set with that property and the rest of the properties copy the property of the next from the last one giving a kind of trailing effect. It also provides some offsets in the rotation, delay, speed.. from the first renderer to the next.
You need to pass a buffered video source (usually a header) and a live input (usually a grabber) and it will modify the original frames in the buffer adding to them the live input using a max or min blend equation depending on how you set the mimaxBlend property. filters : This folder contains the basic VideoFilter that you'll need to extend to provide video filter effects. A filter is both a source and a sink, it receives frames from any video source, process them and notifies the next element in the pipeline passing the modified frame. A basic video filter has this public interface: VideoFrame * getNextVideoFrame() Returns the last processed videoFrame, if you rely on event notification in your filter, you can just store as a property the last processed frame and return it on this method call. void newVideoFrame(VideoFrame & frame)This method is not meant to be directly called, it's just the method called on the newFrame notification from the source that sends data to the filter. Whenever a new frame arrives, the filter process it and notifies its own newFrameEvent passing the modifed frame. The current implementation has the problem that if you don't connect the filter to a sink buffer, you won't be able to use the event system as the renderers needs to use they're own timing because of the thread unsafeness of the opengl system. A posible solution to this should be implementing a new kind of buffer that only stores frames till they're required, this way a filter can notify the newFrame event whenever a new processed frame is available, this temporary buffer will store that frame till a renderer (or any other sink) asks for it by calling the getNextVideoFrame metho. This buffer can be implemented in the VideoFilter base class although having it in a separate class should be the way to go.
frames : The classes in this directory are the most basic data store in Playmodes. We've used the same paradigm and naming both from video and audio so the syntax is more uniform.
It's also prepared to be deleted only when no object is using it anymore. For that it extends ofxObjCPointer. This super useful openframeworks addon from Memo Atken provides a simple way of controlling when a class is not used anymore and only then delete it. For that it provides retain and release method. In Playmodes, buffers automatically retain frames when you ask for them so if you directly read them from a buffer or a header you don't need to retain them, only call they're release method when you don't use them anymore. Also to be thread safe, the VideoFrame has to delete it's texture on deletion so instead of deleting the object just when the use counter arrives to 0, it registers to the openframeworks update event and only then it deletes itself. Frames has an static counter that is updated on every frame creation and deletion for debugging purposes. This allows to know how many frames exists currently in memory.
oscControl: in this directory you can find a class that allows to map simple osc messages to variable or setters, so whenever a new message arrives, it automatically modifies the value of the mapped variable or call a setter with the arriving value. To map a variable to a message you need to call: oscInterface->mapMessage("speed",&speed);This way when a new message with the format /setspeed arrives, the first parameter of the message is assigned to the speed variable. The type of the variable and the message parameter is verified so the message doesn't crash the application.To map a message to a setter you need to use a poco delegate, the syntax is a bit complex and perhaps will change in the future as the last poco version provides a simpler way to do this: oscInterface.mapMessage("speed",With this when a message with the format /setspeed arrives, the first paramenter of the message is passed to the setSpeed method of the videoHeader object. This class uses a modified version of the ofxOsc addon by Damian Frey that is able to cast events whenever a new message arrives. The modified addon is also in the package, you will need to substitute your osc addon in the openframeworks addons folder with this one. It won't break any previous code.
Example application: The current version ships with an example app that provides some gui controls with Todd Vanderlin's ofxSimpleGui. As it is in the download, it shows a MultixRenderer with it's parameters controllable through the gui. There's also a perlin noise generator class that affects the x rotation of the renderer as an example of how to use an external class to modify the diferent position (or even timing) parameters. Comented in the testApp setup code, you can find how to setup a recloop renderer instead of the multix one. In future implementations we'll separate the gui from the rendering screen by sending osc messages. You can download Playmodes 2 v0.01 from here (it includes the preRelease 0.6 of Openframeworks, not final version!!). If you're under Linux rename the libsLinux folder as libs !! http://www.playmodes.com/pm/of_preRelease_Playmodes_0.06.rar For any question regarding this experimental (work in progress) version of Playmodes, send us an This e-mail address is being protected from spambots. You need JavaScript enabled to view it . Enjoy it and have a happy 2009!! |
| Last Updated ( Thursday, 09 April 2009 21:23 ) |



