Blame | Last modification | View Log | RSS feed
<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" /><title>4.9. Streaming Plots</title><link rel="stylesheet" type="text/css" href="phplotdoc.css" /><meta name="generator" content="DocBook XSL Stylesheets V1.78.1" /><link rel="home" href="index.html" title="PHPlot Reference Manual" /><link rel="up" href="advanced.html" title="Chapter 4. PHPlot Advanced Topics" /><link rel="prev" href="adv-multiplot.html" title="4.8. Multiple Plots Per Image" /><link rel="next" href="adv-imgmap.html" title="4.10. Image Maps for Plot Data" /></head><body><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">4.9. Streaming Plots</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="adv-multiplot.html">Prev</a> </td><th width="60%" align="center">Chapter 4. PHPlot Advanced Topics</th><td width="20%" align="right"> <a accesskey="n" href="adv-imgmap.html">Next</a></td></tr></table><hr /></div><div class="sect1"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="adv-streaming"></a>4.9. Streaming Plots</h2></div></div></div><div class="abstract"><p class="title"><strong></strong></p><p>This section contains information about producing streaming plots.The end of this section contains a complete example.</p></div><p>A script can use PHPlot to produce a series of plots that are streamed to abrowser or other viewing application. The result is a movie, or video,consisting of a plot with changing data. This might be used to displayreal-time data, to replay historical data, or for graphical display ofany data where adding a time dimension improves the presentation.This feature was added in PHPlot-5.8.0.</p><p>This feature is intended for use when you want to update a plot one or moretimes per second. If instead you want each plot to be displayed for one ormore seconds, consider using a refreshing page instead, for exampleusing using a "Refresh" meta-tag.</p><div class="warning" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Warning</h3><p>Producing streaming plots will place a significant load on your server.See <a class="xref" href="adv-streaming.html#adv-streaming-performance" title="4.9.2. Streaming Plots - Performance Considerations">Section 4.9.2, “Streaming Plots - Performance Considerations”</a> below for more information.</p></div><p>PHPlot produces streaming plots using<a class="ulink" href="http://en.wikipedia.org/wiki/Motion_Jpeg" target="_top">Motion JPEG</a>(M-JPEG), specifically Streaming M-JPEG over HTTP.This method (which is not a standard) sends a series of JPEG images,with appropriate MIME headers, in a stream to the browser or viewer.PHPlot produces each plot as usual, and sends it out as part of the stream.Your script is responsible for changing the data (or other plotelements) between frames, and for the overall frame timing.</p><p>Browsers and viewers which have been found to be capable of displayinga Motion-JPEG Stream over HTTP include recent versions of:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p>Mozilla Firefox</p></li><li class="listitem"><p>Mozilla Seamonkey</p></li><li class="listitem"><p>VLC Media Player</p></li></ul></div><p>Note: Microsoft Internet Explorer is <span class="emphasis"><em>not</em></span> able todisplay these streams without an add-on.Google Chrome and Apple Safari are reported to be capable of displaying thesestreams, but they have not been tested with PHPlot.</p><p>Although only JPEG images are used in this section, the same methodworks in theory for other image types such as PNG, and PHPlot does notforce the use of JPEG with streaming plots. Mozilla Firefox andSeamonkey have been found to be able to display "Motion-PNG" streams - asequence of PNG images using the same MIME structure as Motion-JPEG.(VLC Media Player cannot display them.) Since plot images using JPEGcompression are of poorer quality than PNG images, you might want toconsider using another format such as PNG, however compatibility withviewers is a bigger issue than with JPEG.</p><div class="sect2"><div class="titlepage"><div><div><h3 class="title"><a id="adv-streaming-using"></a>4.9.1. Streaming Plots - Creating Moving Plots</h3></div></div></div><p>There are generally 3 parts to a script that produces streaming plots:</p><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><p>Creating a PHPlot object, and configuring your plot. This is the same asfor single image plots.</p></li><li class="listitem"><p>You will need an incremental way to produce data for the plot. Typically,it will produce one new row of a PHPlot data array for each plot frame.</p></li><li class="listitem"><p>Your script will have a loop that produces frames and includes frame timing.(If your data is produced at fixed intervals, your loop may not need anyadditional timing.) You may choose to produce a fixed number of frames (orequivalently, run for a fixed length of time), or run forever. The user canalways end the stream by stopping their browser or viewer, and the scriptwill terminate on the server.</p></li></ol></div><p></p><p>You will use these PHPlot functions to produce streaming plots, in additionto the functions used for static plots.</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p>Use <a class="xref" href="SetPrintImage.html" title="SetPrintImage"><span class="refentrytitle">SetPrintImage</span></a>(False) to disable automatic printingfrom <a class="xref" href="DrawGraph.html" title="DrawGraph"><span class="refentrytitle">DrawGraph</span></a>.</p></li><li class="listitem"><p>Use <a class="xref" href="SetFileFormat.html" title="SetFileFormat"><span class="refentrytitle">SetFileFormat</span></a>('jpg') to select JPEG format. This isthe only format that is 'legal' with Motion-JPEG Streaming, although otherformats work with some browsers.</p></li><li class="listitem"><p>Call <a class="xref" href="StartStream.html" title="StartStream"><span class="refentrytitle">StartStream</span></a> outside your main loop to begin the plotstream.</p></li><li class="listitem"><p>Within your main loop, use <a class="xref" href="SetDataValues.html" title="SetDataValues"><span class="refentrytitle">SetDataValues</span></a> to reload thedata array after addition the new row(s). This is necessary because PHPlotcreates a copy (rather than a reference) of your data.</p></li><li class="listitem"><p>Use <a class="xref" href="DrawGraph.html" title="DrawGraph"><span class="refentrytitle">DrawGraph</span></a> to produce the plot (but not output it).</p></li><li class="listitem"><p>Still within your main loop, use <a class="xref" href="PrintImageFrame.html" title="PrintImageFrame"><span class="refentrytitle">PrintImageFrame</span></a> tooutput the plot as a single frame within the plot stream.</p></li><li class="listitem"><p>If your plot stream ends at some point (rather than running until stoppedby the user), call <a class="xref" href="EndStream.html" title="EndStream"><span class="refentrytitle">EndStream</span></a> to cleanly end the plot stream.</p></li></ul></div><p></p><p>Your PHP script should be referenced from an HTML page using an<code class="literal"><img></code> tag, just like when creating a single plot.The MIME type returned by PHPlot (<code class="literal">multipart/x-mixed-replace</code>rather than <code class="literal">image/jpeg</code> for example) tells the browser orviewer to expect a stream rather than a single image.</p><p>Be aware that PHP is usually configured to time out scripts that run toolong, and will terminate your streaming plot script. To prevent this, usethe PHP function <code class="literal">set_time_limit($seconds)</code>. If you knowthe total number of frames and frame rate, you can set the timeout to a bitmore than the total expected runtime.Alternatively, you can call <code class="function">set_time_limit</code> withinyour main loop, so it is called when each frame is produced. Because thisfunction resets the PHP timer, your script will not time out and canproduce frames forever.</p><p>For frame timing, you can use the PHP functions <code class="function">microtime()</code>and <code class="function">time_sleep_until()</code>.Call <code class="literal">microtime(TRUE)</code> once, to get aprecise timestamp as a floating point number. Then, within your main loop,use <code class="literal">time_sleep_until($timestamp)</code> to put your script tosleep until the time to start of the next frame.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><h3 class="title">Note</h3><p>Be sure your PHP script does not leak memory during the loop that producesframes, especially if the script is designed to runs until stopped (ratherthan producing a fixed number of frames). Appending to an array inside theloop is an example of something to avoid.</p></div></div><div class="sect2"><div class="titlepage"><div><div><h3 class="title"><a id="adv-streaming-performance"></a>4.9.2. Streaming Plots - Performance Considerations</h3></div></div></div><p>This section discusses performance considerations for streaming plots,starting with some definitions.</p><div class="variablelist"><dl class="variablelist"><dt><span class="term">Frame Rate</span></dt><dd><p>The number of plots (frames) produced per second.</p></dd><dt><span class="term">Frame Time</span></dt><dd><p>The total time per frame, equal to the reciprocal of the frame rate.</p></dd><dt><span class="term">Plot Time, Output Time, Idle Time</span></dt><dd><p>These are the 3 parts of the Frame Time. The Plot Time is the time it takesto prepare your data for each frame and draw the plot graphics. The OutputTime is the time to send the completed plot to the browser or viewer.Idle time is when your script is sleeping, waiting for the next frame's timeinterval.</p></dd><dt><span class="term">Frame Slippage, Missed Frames</span></dt><dd><p>If your script and processor cannot keep up with the plot stream requirements,the idle time will drop to zero. Frame slippage is when the frame time exceedsthe desired goal, so the frame rate drops (but no frames are lost).A different approach is to enforce the frame time and drop frames to catchup, resulting in missed frames.</p></dd></dl></div><p></p><p>To produce a good plot stream, your script and server must be able toconsistently produce and output frames at the desired rate.The frame rate you want will depend on your data and application, while therate you can achieve depends on the complexity of your script and youravailable processing power. Although typical video runs at between 24 and60 frames per second, those rates are likely too fast to be useful with plotdata.A more realistic starting point for streaming plots is 10 frames per second.This will provide the appearance of continuous motion of the graph(s),but without too much blurring of the data.</p><p>Here are some real performance numbers, using a relatively simple plot, andhardware that is old but was considered high performance when introducedseveral year ago (circa 2007).With a PHPlot script producing 10 frames per second, the Apache serverprocess was found to be using about 24% of one processor core's availableCPU time. (Keep in mind that this is not a short-term load, but means 24%for the duration of the plot stream.) On the same hardware, 15 frames persecond used 35% CPU time, and 30 frames per second used 70% CPU time (ofone core).</p><p>You can measure the performance of your script with a small change, if youare using a main loop like the one shown in the full example in the nextsection (repeated briefly here).</p><pre class="programlisting">$timestamp = microtime(TRUE);$frame_time = 1 / $frame_rate;$slip = 0; // Number of slipped frames$frame = 0; // Current frame numberwhile(1) {$frame++;...$plot->DrawGraph();$plot->PrintImageFrame();if (!@time_sleep_until($timestamp += $frame_time)) $slip++;}</pre><p>The PHP function <code class="function">time_sleep_until()</code> returns FALSE if thedesired time already passed. (We use @ to suppress the message which wouldbe logged in that case.) The variable $slip counts the number of slippedframes.The ratio of $slip to $frame should be as low as possible, ideally zero.If <code class="literal">($slip/$frame)</code> gets too high, you may need to use alower frame rate, because either your frame rate is too high, your plot istoo complex, or your server is too slow to keep up.However, optimizing your plot may help.</p><p>To optimize performance of your streaming plot, you should avoid these moreexpensive features:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p>Plots that use area fills are slower. This especially includes pie chartswith shading (because PHPlot redraws the filled pie segments for eachlevel of shading). Area plots and similar types have a lot of area fill.Bar charts have some area fill, with shaded bar charts having more shading(but not nearly as much as shaded pie charts).The fastest plot types are line plots.</p></li><li class="listitem"><p>TrueType fonts are slower than GD fonts. Consider using text in the HTMLpage containing the image reference, rather than in the image itself.</p></li><li class="listitem"><p>Some operations on truecolor images are slow (for example, gamma adjustmentor anti-aliasing) and should be avoided.</p></li><li class="listitem"><p>Avoid using a background image, especially one which needs to be scaled.</p></li><li class="listitem"><p>Avoid external factors that can affect performance. An obvious example isdatabase access, since a database server can take a variable amount of timeto respond to a query.</p></li></ul></div><p></p><p>Note that PHPlot redraws the complete plot image for each frame, regardlessof which functions are used inside or outside your loop. For example, ifyou use <a class="xref" href="SetTitle.html" title="SetTitle"><span class="refentrytitle">SetTitle</span></a> to set title text outside the frame loop,the same title will be drawn into each frame. If it is inside the loop,you can change the text for each frame. Either way, the time to draw the titlecounts for each frame. The same is true for legend, labels, etc.Although you should keep invariant PHPlot "Set...()" function calls outsideyour frame loop, this does not significantly affect the performance.</p></div><div class="sect2"><div class="titlepage"><div><div><h3 class="title"><a id="adv-streaming-example"></a>4.9.3. Streaming Plots - Example</h3></div></div></div><p>This is a complete example that produces a streaming plot sequence showingsin() and cos(). The frame rate and run time are set with variables at thetop.</p><p></p><pre class="programlisting"><?php# Example of Streaming Plots with PHPlot# This simply plots sin(x) and cos(x), updating at the rate given below.# Replace the function next_row() to plot something else.# This must run using a web server, not CLI.require_once 'phplot.php';# Configuration:# This is the fixed number of points along the X axis:$n_rows = 40;# Data range for Y:$max_y = 1;$min_y = -1;# Frames per second:$frame_rate = 10;# Total runtime in seconds. Use 0 to run 'forever':$run_for = 0;# Derived:$run_forever = $run_for == 0;$frame_time = 1 / $frame_rate;$n_frames = $frame_rate * $run_for;# Return the next data row (per PHPlot text-data data type):function next_row($x){global $frame_rate;# Map 8 seconds of frames into 360 degrees (360/8 = 45 degrees/second)$theta = deg2rad(45 * $x / $frame_rate);return array('', sin($theta), cos($theta));}# Create an initial data array with no values. New values will be# shifted in to the end. This is text-data format; the X values# are implicit and ignored (not plotted).for ($i = 0; $i < $n_rows; $i++) $data[$i] = array('', '', '');# Create and configure the PHPlot object:$plot = new PHPlot(640, 480);$plot->SetDataType('text-data');$plot->SetPlotType('lines');$plot->SetFileFormat('jpg');$plot->SetXTickLabelPos('none');$plot->SetXTickPos('none');$plot->SetXDataLabelPos('none');# Don't draw the initial, empty values:$plot->SetDrawBrokenLines(True);# Force the Y range, or it will use the first frame to calculate:$plot->SetPlotAreaWorld(NULL, $min_y, NULL, $max_y);$plot->SetPrintImage(False);# Main loop:$plot->StartStream();$timestamp = microtime(TRUE);for ($frame = 0; $run_forever || $frame < $n_frames; $frame++) {# Set PHP timeout so it won't terminate the script early.set_time_limit(60);# Discard the oldest data row, and shift in the new row:array_shift($data);$data[] = next_row($frame);# Set a plot title that includes the frame number:$plot->SetTitle(sprintf("Moving Plot Test (Frame %4d)", $frame));# Reload the data array:$plot->SetDataValues($data);# Draw and output the plot:$plot->DrawGraph();$plot->PrintImageFrame();# Sleep until it is time to start the next frame:time_sleep_until($timestamp += $frame_time);}# End the stream:$plot->EndStream();</pre><p></p></div></div><div class="navfooter"><hr /><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="adv-multiplot.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="advanced.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="adv-imgmap.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">4.8. Multiple Plots Per Image </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> 4.10. Image Maps for Plot Data</td></tr></table></div></body></html>