2
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4
* Movie_HelioviewerMovie Class Definition
10
* @author Jaclyn Beck <jabeck@nmu.edu>
11
* @license http://www.mozilla.org/MPL/MPL-1.1.html Mozilla Public License 1.1
12
* @link http://launchpad.net/helioviewer.org
14
require_once 'HelioviewerMovieFrame.php';
15
require_once 'src/Database/DbConnection.php';
16
require_once 'lib/phpvideotoolkit/config.php';
17
require_once 'lib/phpvideotoolkit/phpvideotoolkit.php5.php';
19
* Represents a static (e.g. ogv/mp4) movie generated by Helioviewer
21
* Note: For movies, it is easiest to work with Unix timestamps since that is what is returned
22
* from the database. To get from a javascript Date object to a Unix timestamp, simply
23
* use "date.getTime() * 1000." (getTime returns the number of miliseconds)
26
* @package Helioviewer
27
* @author Jaclyn Beck <jabeck@nmu.edu>
28
* @license http://www.mozilla.org/MPL/MPL-1.1.html Mozilla Public License 1.1
29
* @link http://launchpad.net/helioviewer.org
31
class Movie_HelioviewerMovie
33
private $_images = array ();
40
private $_baseScale = 2.63;
41
private $_baseZoom = 10;
42
private $_tileSize = 512;
43
private $_filetype = "flv";
44
private $_highQualityLevel = 100;
45
private $_watermarkOptions = "-x 720 -y 965 ";
48
* HelioviewerMovie Constructor
50
* @param array $layers Layers to use for movie generation. Each layer is a string of the form:
51
* OBS_INST_DET_MEAS,xstart,xsize,ystart,ysize,hcOffsetx,hcOffsety,opacity
52
* @param int $startTime Requested movie start time (unix timestamp)
53
* @param int $zoomLevel Zoom-level for which the movie was requested at
54
* @param int $numFrames Number of frames to include
55
* @param int $frameRate Number of frames per second
56
* @param string $hqFormat Format to use for high-quality version of the movie
57
* @param array $options An array with ["edges"] => true/false, ["sharpen"] => true/false
58
* @param int $timeStep Desired timestep between movie frames in seconds. Default is 86400 seconds, or 1 day.
59
* @param array $imageSize Width and height of each movie frame
60
* @param string $filename Desired filename for the movie
61
* @param int $quality Movie quality
63
public function __construct(
64
$layers, $startTime, $zoomLevel, $numFrames, $frameRate, $hqFormat,
65
$options, $timeStep, $imageSize, $filename, $quality
67
// date_default_timezone_set('UTC');
68
// $layers is an array of layer information arrays, identified by their layer names.
69
// Each layer information array has values for "name", "xRange", "yRange", "hcOffset", and "opacityValue"
70
$this->layers = $layers;
73
$this->tmpdir = substr(getcwd(), 0, -3) + "tmp";
75
// _startTime is a Unix timestamp in seconds.
76
$this->_startTime = $startTime;
77
$this->zoomLevel = $zoomLevel;
78
$this->numFrames = $numFrames;
79
$this->frameRate = $frameRate;
80
$this->quality = $quality;
81
$this->options = $options;
83
// _timeStep is in seconds
84
$this->_timeStep = $timeStep;
85
$this->_imageSize = $imageSize;
86
$this->filename = $filename;
88
$this->_endTime = $startTime + ($numFrames * $timeStep);
90
$this->padDimensions = $this->_setAspectRatios();
91
$this->highQualityFiletype = $hqFormat;
92
$this->_db = new Database_DbConnection();
100
public function toMovie()
110
public function toArchive()
120
public function getNumFrames()
126
* Builds the requested movie
128
* Makes a temporary directory to store frames in, calculates a timestamp for every frame, gets the closest
129
* image to each timestamp for each layer. Then takes all layers belonging to one timestamp and makes a movie frame
130
* out of it. When done with all movie frames, phpvideotoolkit is used to compile all the frames into a movie.
134
public function buildMovie()
136
// Make a temporary directory to store the movie in.
138
$movieName = "Helioviewer-Movie-" . $this->filename;
139
$tmpdir = HV_TMP_DIR . "/$now/";
140
$tmpurl = HV_TMP_ROOT_URL . "/$now/$movieName." . $this->_filetype;
142
chmod($tmpdir, 0777);
144
// Build an array with all timestamps needed when requesting images
145
$timeStamps = array();
147
// Calculates unix time stamps, successively increasing by the time step
148
// (default step is 86400 seconds, or 1 day)
149
for ($time = $this->_startTime; $time < $this->_endTime; $time += $this->_timeStep) {
150
array_push($timeStamps, $time);
153
// Array that holds $closestImage array for each layer
154
$layerImages = array();
156
// Array to hold timestamps corresponding to each image, and each image's uri
157
$closestImage = array();
159
foreach ($this->layers as $layer) {
160
// $layerInfo will have values Array
161
// ("obs_inst_det_meas", xStart, xSize, yStart, ySize, offsetx, offsety, opacity)
162
$layerInfo = explode(",", $layer);
164
// name is now: 'obs_inst_det_meas'
165
$name = $layerInfo[0];
167
// closestImage is an associative array the size of numFrames with each entry having:
168
// Array('timestamp', 'unix_timestamp', 'timediff', 'timediffAbs', 'uri', 'opacityGrp')
169
$closestImage = $this->_getImageTimestamps($name, $timeStamps);
171
// layerImages is an associative array the size of the number of layers. An example entry would be:
172
// layerImages['SOH_EIT_EIT_304'] = closestImage array.
173
// So each entry has an array of the closest images to each timestamp.
174
$layerImages[$name] = $closestImage;
177
// For each frame, make a composite image of all layers at that timestamp
178
for ($frameNum = 0; $frameNum < $this->numFrames; $frameNum++) {
179
// images array holds one image from each layer (the closest images to a specific timestamp)
181
$realTimestamps = array();
183
foreach ($this->layers as $layer) {
184
$layerInfo = explode(",", $layer);
186
// name is 'SOH_EIT_EIT_304'
187
$name = $layerInfo[0];
189
// Chop the name off the array but keep the rest of the information.
190
// ranges is an array: [xStart, xSize, yStart, ySize, offsetX, offsetY, opacity]
191
$ranges = array_slice($layerInfo, 1);
193
$closestImage = $layerImages[$name][$frameNum];
195
// $image is now: "uri,xStart,xSize,yStart,ySize,opacity,opacityGrp"
196
$image = $closestImage['uri'] . "," . implode(",", $ranges) . "," .$closestImage['opacityGrp'];
197
$images[$name] = $image;
198
$realTimestamps[$name] = $closestImage['timestamp'];
201
// All frames will be put in cache/movies/$now
202
$movieFrame = new Movie_HelioviewerMovieFrame(
203
$this->zoomLevel, $this->options, $images, $frameNum, $now,
204
$this->_imageSize, $realTimestamps, $this->quality
206
$frameFile = $movieFrame->getComposite();
208
array_push($this->_images, $frameFile);
211
// Pad to a 16:9 aspect ratio by adding a black border around the image.
212
// This is set up so that width CAN be padded if it's uncommented. Currently it is not padded.
213
foreach ($this->_images as $image) {
214
//$imgWidth = $this->_imageSize["width"];
215
//$width = $this->padDimensions["width"];
216
//$widthDiff = ($width - $imgWidth) / 2;
218
$imgHeight = $this->_imageSize["height"];
219
$height = $this->padDimensions["height"];
220
$heightDiff = ($height - $imgHeight) / 2;
222
if (/*$widthDiff > 0 || */ $heightDiff > 0) {
223
$padCmd = ' && convert -bordercolor black -border 0x' . $heightDiff . " " . $image . " " . $image;
224
exec(HV_PATH_CMD . escapeshellcmd($padCmd));
228
// Use phpvideotoolkit to compile them
229
$toolkit = new PHPVideoToolkit($tmpdir);
231
// compile the image to the tmp dir
232
$ok = $toolkit->prepareImagesForConversionToVideo($this->_images, $this->frameRate);
235
// if there was an error then get it
236
logErrorMsg("PHPVideoToolkit: {$toolkit->getLastError()}");
239
$toolkit->setVideoOutputDimensions($this->_imageSize['width'], $this->_imageSize['height']);
241
// set the output parameters (Flash video)
242
$output_filename = "$movieName." . $this->_filetype;
243
$ok = $toolkit->setOutput($tmpdir, $output_filename, PHPVideoToolkit::OVERWRITE_EXISTING);
246
// if there was an error then get it
247
logErrorMsg("PHPVideoToolkit: {$toolkit->getLastError()}");
250
// execute the ffmpeg command
251
$movie = $toolkit->execute(false, true);
253
// check the return value in-case of error
254
if ($movie !== PHPVideoToolkit::RESULT_OK) {
255
// if there was an error then get it
256
logErrorMsg("PHPVideoToolkit: {$toolkit->getLastError()}");
259
// Create a high-quality version as well
260
$hq_filename = "$movieName." . $this->highQualityFiletype;
261
$toolkit->setConstantQuality($this->_highQualityLevel);
263
// Use ASF for Windows
264
if ($this->highQualityFiletype == "avi") {
265
$toolkit->setFormat(PHPVideoToolkit::FORMAT_ASF);
268
// Use MPEG-4 for Mac
269
if ($this->highQualityFiletype == "mov") {
270
$toolkit->setVideoCodec(PHPVideoToolkit::FORMAT_MPEG4);
274
//$watermark = HV_ROOT_DIR . "/images/logos/watermark_small_gs.png";
275
//$toolkit->addWatermark($watermark, PHPVIDEOTOOLKIT_FFMPEG_IMLIB2_VHOOK, $this->_watermarkOptions);
277
$ok = $toolkit->setOutput($tmpdir, $hq_filename, PHPVideoToolkit::OVERWRITE_EXISTING);
280
// if there was an error then get it
281
logErrorMsg("PHPVideoToolkit: {$toolkit->getLastError()}");
284
// execute the ffmpeg command
285
$mp4 = $toolkit->execute(false, true);
287
if ($mp4 !== PHPVideoToolkit::RESULT_OK) {
288
// if there was an error then get it
289
logErrorMsg("PHPVideoToolkit: {$toolkit->getLastError()}");
292
// Clean up png/tif images that are no longer needed
293
foreach ($this->_images as $image) {
297
// $this->showMovie($tmpurl, 512, 512);
299
header('Content-type: application/json');
300
echo json_encode($tmpurl);
304
* Adds black border to movie frames if neccessary to guarantee a 16:9 aspect ratio
306
* Checks the ratio of width to height and adjusts each dimension so that the
307
* ratio is 16:9. The movie will be padded with a black background in JP2Image.php
308
* using the new width and height.
310
* @return array Width and Height of padded movie frames
312
private function _setAspectRatios()
314
$width = $this->_imageSize["width"];
315
$height = $this->_imageSize["height"];
317
$ratio = $width / $height;
319
// Commented out because padding the width looks funny.
321
// If width needs to be adjusted but height is fine
323
$adjust = (16/9) * $height / $width;
327
// Adjust height if necessary
329
$adjust = (9/16) * $width / $height;
333
$dimensions = array("width" => $width, "height" => $height);
338
* Find closest times for each frame of the movie for a given layer
340
* Queries the database to find the exact timestamps for images nearest each time in $timeStamps.
341
* Returns an array the size of numFrames that has:
342
* 'timestamp', 'unix_timestamp', 'timediff', 'timediffAbs', 'uri', and 'opacityGrp'
345
* @param string $name JP2 filename
346
* @param array $timeStamps Array containing the requested timeStamps to match against
348
* @return array Matched times
350
private function _getImageTimestamps($name, $timeStamps) //($obs, $inst, $det, $meas, $timeStamps)
352
$resultArray = array ();
354
// Go through the array and find the closest image in the database to the given timeStamp
356
foreach ($timeStamps as $time) {
357
// sprintf takes too long, especially when it is called 40+ times.
360
UNIX_TIMESTAMP(timestamp) AS unix_timestamp,
361
UNIX_TIMESTAMP(timestamp) - $time AS timediff,
362
ABS(UNIX_TIMESTAMP(timestamp) - $time) AS timediffAbs,
370
'%_%_%_%_" . mysqli_real_escape_string($this->_db->link, $name) . ".jp2'
375
$result = $this->_db->query($sql);
376
$row = mysqli_fetch_array($result, MYSQL_ASSOC);
378
throw new Exception("Could not find the requested image.");
381
catch (Exception $e) {
382
logErrorMsg($e->getMessage);
385
array_push($resultArray, $row);
393
* Displays movie in a Flash player along with a link to the high-quality version
395
* @param string $url The URL for the movie to be displayed
396
* @param int $width Movie width
397
* @param int $height Movie Height
401
public function showMovie($url, $width, $height)
404
<!-- MC Media Player -->
405
<script type="text/javascript">
406
playerFile = "http://www.mcmediaplayer.com/public/mcmp_0.8.swf";
407
fpFileURL = "<?php print $url?>";
408
playerSize = "<?php print $width . 'x' . $height?>";
410
<script type="text/javascript" src="http://www.mcmediaplayer.com/public/mcmp_0.8.js">
412
<!-- / MC Media Player -->