1 <?php
2
3 class FLV extends File {
4
5 public static $allowed_file_types = array(
6 'flv', 'avi', 'mov', 'mpeg', 'mpg', 'm4a'
7 );
8 private static $has_ffmpeg = null;
9 private $allow_full_screen = true;
10 private static $ffmpeg_root = "";
11 private static $termination_code;
12 public static $player_count = 0;
13 public static $video_width = 840;
14 public static $video_height = 525;
15 public static $default_thumbnail_width = 640;
16 public static $default_thumbnail_height = 480;
17 public static $thumbnail_folder = "video_thumbnails";
18 public static $log_file_path = "dataobject_manager/code/flv/ffmpeg_log.txt";
19 public static = 840;
20 public static = 525;
21 public static $thumbnail_seconds = 10;
22 public static $audio_sampling_rate = 22050;
23 public static $audio_bit_rate = 32;
24 public static $play_button_overlay = true;
25
26 public static $default_video_icon_path = "dataobject_manager/code/flv/images/default_video.png";
27
28 public static function set_ffmpeg_root($path) {
29 if (substr($path, -1) != "/")
30 $path .= "/";
31 self::$ffmpeg_root = $path;
32 }
33
34 public static function has_ffmpeg() {
35
36 if (self::$has_ffmpeg !== null)
37 return self::$has_ffmpeg;
38
39 $success = false;
40 if (extension_loaded('ffmpeg'))
41 $success = true;
42 else {
43 $output = self::ffmpeg("");
44 if (self::$termination_code == 1)
45 $success = true;
46 }
47 self::$has_ffmpeg = $success;
48 return self::$has_ffmpeg;
49 }
50
51 public static function echo_ffmpeg_test() {
52
53 echo self::has_ffmpeg() ? "<span style='color:green'>FFMPEG is installed on your server and working properly. Code: " . self::$termination_code . "</span>" :
54 "<span class='color:red'>FFMPEG does not appear to be installed on your server. Code: " . self::$termination_code . "</span>";
55 }
56
57 protected static function ffmpeg($args) {
58 $descriptorspec = array(
59 0 => array("pipe", "r"),
60 1 => array("pipe", "w"),
61 2 => array("pipe", "w")
62 );
63
64 $pipes = array();
65 $cmd = self::$ffmpeg_root . "ffmpeg " . $args;
66 self::log_command($cmd);
67 $process = proc_open($cmd, $descriptorspec, $pipes);
68
69 $output = "";
70
71 if (!is_resource($process))
72 return false;
73
74
75 fclose($pipes[0]);
76
77 stream_set_blocking($pipes[1], false);
78 stream_set_blocking($pipes[2], false);
79
80 $todo = array($pipes[1], $pipes[2]);
81
82 while (true) {
83 $read = array();
84 if (!feof($pipes[1]))
85 $read[] = $pipes[1];
86 if (!feof($pipes[2]))
87 $read[] = $pipes[2];
88
89 if (!$read)
90 break;
91
92 $ready = stream_select($read, $write = NULL, $ex = NULL, 2);
93
94 if ($ready === false) {
95 break;
96 }
97
98 foreach ($read as $r) {
99 $s = fread($r, 1024);
100 $output.= $s;
101 }
102 }
103
104 fclose($pipes[1]);
105 fclose($pipes[2]);
106
107 self::$termination_code = proc_close($process);
108 self::log_command($output);
109 return $output;
110 }
111
112 private static function log_command($cmd) {
113 if (self::$log_file_path) {
114 $log = Director::baseFolder() . "/" . self::$log_file_path;
115 $f = @fopen($log, 'a');
116 $entry = "[" . date('Y-m-d H:i:s') . "] " . $cmd . "\n";
117 @fwrite($f, $entry);
118 @fclose($f);
119 }
120 }
121
122 private function default_thumbnail() {
123 $img = new Image_Cached(self::$default_video_icon_path);
124 $img->ID = $this->ID;
125 return $img;
126 }
127
128 private function SWFLink() {
129 return Director::absoluteURL('dataobject_manager/code/flv/shadowbox/libraries/mediaplayer/player.swf');
130 }
131
132 private function AllowFullScreen() {
133 return $this->allow_full_screen ? "true" : "false";
134 }
135
136 private static function remove_file_extension($filename) {
137 $ext = strrchr($filename, '.');
138 if ($ext !== false)
139 $filename = substr($filename, 0, -strlen($ext));
140 return $filename;
141 }
142
143 private static function clean_file($str) {
144 $t = strtolower($str);
145 $t = str_replace('&', '-and-', $t);
146 $t = str_replace('&', '-and-', $t);
147 $t = preg_replace('![^A-Za-z0-9]+!', '-', $t);
148 $t = preg_replace('!-+!', '-', $t);
149 return $t;
150 }
151
152 public function Icon() {
153 return SAPPHIRE_DIR . "/images/app_icons/mov_32.gif";
154 }
155
156 public function FLVPath() {
157 return self::remove_file_extension($this->Filename) . ".flv";
158 }
159
160 public function FLVLink() {
161 return Director::absoluteURL($this->FLVPath());
162 }
163
164 private function absoluteRawVideoLink() {
165 return Director::baseFolder() . "/" . $this->Filename;
166 }
167
168 private function absoluteFLVPath() {
169 return Director::baseFolder() . "/" . $this->FLVPath();
170 }
171
172 private function hasFLV() {
173 return Director::fileExists($this->FLVPath());
174 }
175
176 public function getThumbnail() {
177 if ($img = DataObject::get_one("Image", "Title = 'flv_thumb_{$this->ID}'"))
178 return Director::fileExists($img->Filename) ? $img : false;
179 return false;
180 }
181
182 private function createFLV() {
183 $args = sprintf("-i %s -ar %d -ab %d -f flv %s",
184 $this->absoluteRawVideoLink(),
185 self::$audio_sampling_rate,
186 self::$audio_bit_rate,
187 $this->absoluteFLVPath()
188 );
189
190 $output = self::ffmpeg($args);
191 }
192
193 private function createThumbnail() {
194 $img_title = "flv_thumb_" . $this->ID;
195 if ($existing = DataObject::get("Image", "Title = '$img_title'")) {
196 foreach ($existing as $file)
197 $file->delete();
198 }
199 $folder = Folder::findOrMake(self::$thumbnail_folder);
200 $img_filename = self::clean_file(self::remove_file_extension($this->Title)) . ".jpg";
201 $abs_thumb = Director::baseFolder() . "/" . $folder->Filename . $img_filename;
202 $args = sprintf("-y -i %s -an -s %s -ss %d -an -r 1 -vframes 1 -y -vcodec mjpeg -f mjpeg %s",
203 str_replace(' ', '\ ', $this->absoluteFLVPath()),
204 self::$default_thumbnail_width . "x" . self::$default_thumbnail_height,
205 self::$thumbnail_seconds,
206 $abs_thumb
207 );
208 self::ffmpeg($args);
209
210 $img = new Image();
211 $img->setField('ParentID', $folder->ID);
212 $img->Filename = $folder->Filename . $img_filename;
213 $img->Title = $img_title;
214 $img->write();
215 }
216
217 public function onBeforeWrite() {
218 parent::onBeforeWrite();
219 if (!$this->hasFLV())
220 $this->createFLV();
221 if (!$this->getThumbnail())
222 $this->createThumbnail();
223 }
224
225 public function Player($width = null, $height = null) {
226 if ($width === null)
227 $width = self::$video_width;
228 if ($height === null)
229 $height = self::$video_height;
230 $image = ($thumb = $this->VideoThumbnail()) ? $thumb->CroppedImage($width, $height)->URL : "";
231 self::$player_count++;
232 Requirements::javascript('dataobject_manager/code/flv/swfobject.js');
233 Requirements::customScript(sprintf(
234 "swfobject.embedSWF('%s','player-%s','%d','%d','9.0.0','expressInstall.swf',{file : '%s',image : '%s'},{allowscriptaccess : 'true', allowfullscreen : '%s'})",
235 $this->SWFLink(),
236 self::$player_count,
237 $width,
238 $height,
239 $this->FLVLink(),
240 $image,
241 $this->AllowFullScreen()
242 )
243 );
244 return "<div id='player-" . self::$player_count . "'>Loading...</div>";
245 }
246
247 public function forTemplate() {
248 return $this->Player();
249 }
250
251 public function VideoThumbnail() {
252 if (self::has_ffmpeg() && !$img = $this->getThumbnail())
253 $this->createThumbnail();
254 $img = $this->getThumbnail();
255 return $img ? $img : $this->default_thumbnail();
256 }
257
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
274 public function ($arg1 = null, $arg2 = null) {
275 $popup_width = null;
276 $popup_height = null;
277 if ($arg1 !== null && stristr($arg1, "x"))
278 list($thumb_width, $thumb_height) = explode("x", $arg1);
279 else
280 $thumb_width = $arg1;
281
282 if ($arg2 !== null && stristr($arg2, "x"))
283 list($popup_width, $popup_height) = explode("x", $arg2);
284 else
285 $thumb_height = $arg2;
286
287 if ($popup_width === null)
288 $popup_width = self::$default_popup_width;
289 if ($popup_height === null)
290 $popup_height = self::$default_popup_height;
291
292 return $this->customise(array(
293 'PopupWidth' => $popup_width,
294 'PopupHeight' => $popup_height,
295 'ThumbWidth' => $thumb_width,
296 'ThumbHeight' => $thumb_height,
297 'Title' => $this->Title,
298 'Link' => $this->FLVLink(),
299 'Thumbnail' => $this->VideoThumbnail()->CroppedImage($thumb_width, $thumb_height),
300 'PlayButton' => self::$play_button_overlay
301 ))->renderWith(array('FLVpopup'));
302 }
303
304 }
305
306 ?>
[Raise a SilverStripe Framework issue/bug](https://github.com/silverstripe/silverstripe-framework/issues/new)
- [Raise a SilverStripe CMS issue/bug](https://github.com/silverstripe/silverstripe-cms/issues/new)
- Please use the
Silverstripe Forums to ask development related questions.
-