Subversion Repositories cheapmusic

Rev

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 a
browser or other viewing application. The result is a movie, or video,
consisting of a plot with changing data. This might be used to display
real-time data, to replay historical data, or for graphical display of
any 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 more
times per second. If instead you want each plot to be displayed for one or
more seconds, consider using a refreshing page instead, for example
using 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, &#8220;Streaming Plots - Performance Considerations&#8221;</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 plot
elements) between frames, and for the overall frame timing.
</p><p>
Browsers and viewers which have been found to be capable of displaying
a 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 to
display these streams without an add-on.
Google Chrome and Apple Safari are reported to be capable of displaying these
streams, but they have not been tested with PHPlot.
</p><p>
Although only JPEG images are used in this section, the same method
works in theory for other image types such as PNG, and PHPlot does not
force the use of JPEG with streaming plots.  Mozilla Firefox and
Seamonkey have been found to be able to display "Motion-PNG" streams - a
sequence of PNG images using the same MIME structure as Motion-JPEG.
(VLC Media Player cannot display them.) Since plot images using JPEG
compression are of poorer quality than PNG images, you might want to
consider using another format such as PNG, however compatibility with
viewers 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 as
for 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 any
additional timing.) You may choose to produce a fixed number of frames (or
equivalently, run for a fixed length of time), or run forever. The user can
always end the stream by stopping their browser or viewer, and the script
will terminate on the server.
      </p></li></ol></div><p>
</p><p>
You will use these PHPlot functions to produce streaming plots, in addition
to 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 printing
from <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 is
the only format that is 'legal' with Motion-JPEG Streaming, although other
formats 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 plot
stream.
      </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 the
data array after addition the new row(s). This is necessary because PHPlot
creates 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> to
output 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 stopped
by 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">&lt;img&gt;</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 or
viewer to expect a stream rather than a single image.
</p><p>
Be aware that PHP is usually configured to time out scripts that run too
long, and will terminate your streaming plot script. To prevent this, use
the PHP function <code class="literal">set_time_limit($seconds)</code>. If you know
the total number of frames and frame rate, you can set the timeout to a bit
more than the total expected runtime.
Alternatively, you can call <code class="function">set_time_limit</code> within
your main loop, so it is called when each frame is produced. Because this
function resets the PHP timer, your script will not time out and can
produce 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 a
precise timestamp as a floating point number. Then, within your main loop,
use <code class="literal">time_sleep_until($timestamp)</code> to put your script to 
sleep 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 produces
frames, especially if the script is designed to runs until stopped (rather
than producing a fixed number of frames). Appending to an array inside the
loop 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 takes
to prepare your data for each frame and draw the plot graphics. The Output
Time 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 time
interval.
        </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 exceeds
the 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 catch
up, resulting in missed frames.
        </p></dd></dl></div><p>
</p><p>
To produce a good plot stream, your script and server must be able to
consistently produce and output frames at the desired rate.
The frame rate you want will depend on your data and application, while the
rate you can achieve depends on the complexity of your script and your
available processing power. Although typical video runs at between 24 and
60 frames per second, those rates are likely too fast to be useful with plot
data.
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, and
hardware that is old but was considered high performance when introduced
several year ago (circa 2007).
With a PHPlot script producing 10 frames per second, the Apache server
process was found to be using about 24% of one processor core's available
CPU 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 per
second used 35% CPU time, and 30 frames per second used 70% CPU time (of
one core).
</p><p>
You can measure the performance of your script with a small change, if you
are using a main loop like the one shown in the full example in the next
section (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 number
while(1) {
    $frame++;
    ...
    $plot-&gt;DrawGraph();
    $plot-&gt;PrintImageFrame();
    if (!@time_sleep_until($timestamp += $frame_time)) $slip++;
}</pre><p>
The PHP function <code class="function">time_sleep_until()</code> returns FALSE if the
desired time already passed. (We use @ to suppress the message which would
be logged in that case.) The variable $slip counts the number of slipped
frames.
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 a
lower frame rate, because either your frame rate is too high, your plot is
too 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 more
expensive 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 charts
with shading (because PHPlot redraws the filled pie segments for each
level 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 HTML
page 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 adjustment
or 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 is
database access, since a database server can take a variable amount of time
to respond to a query.
      </p></li></ul></div><p>
</p><p>
Note that PHPlot redraws the complete plot image for each frame, regardless
of which functions are used inside or outside your loop. For example, if
you 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 title
counts for each frame. The same is true for legend, labels, etc.
Although you should keep invariant PHPlot "Set...()" function calls outside
your 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 showing
sin() and cos(). The frame rate and run time are set with variables at the
top.
</p><p>
  </p><pre class="programlisting">&lt;?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 &lt; $n_rows; $i++) $data[$i] = array('', '', '');

# Create and configure the PHPlot object:
$plot = new PHPlot(640, 480);
$plot-&gt;SetDataType('text-data');
$plot-&gt;SetPlotType('lines');
$plot-&gt;SetFileFormat('jpg');
$plot-&gt;SetXTickLabelPos('none');
$plot-&gt;SetXTickPos('none');
$plot-&gt;SetXDataLabelPos('none');
# Don't draw the initial, empty values:
$plot-&gt;SetDrawBrokenLines(True);
# Force the Y range, or it will use the first frame to calculate:
$plot-&gt;SetPlotAreaWorld(NULL, $min_y, NULL, $max_y);
$plot-&gt;SetPrintImage(False);

# Main loop:
$plot-&gt;StartStream();
$timestamp = microtime(TRUE);
for ($frame = 0; $run_forever || $frame &lt; $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-&gt;SetTitle(sprintf("Moving Plot Test (Frame %4d)", $frame));
    # Reload the data array:
    $plot-&gt;SetDataValues($data);
    # Draw and output the plot:
    $plot-&gt;DrawGraph();
    $plot-&gt;PrintImageFrame();
    # Sleep until it is time to start the next frame:
    time_sleep_until($timestamp += $frame_time);
}
# End the stream:
$plot-&gt;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>