[BUGFIX] Allow Video Text Tracks files
[Packages/TYPO3.CMS.git] / typo3 / sysext / frontend / Classes / ContentObject / FlowPlayerContentObject.php
1 <?php
2 namespace TYPO3\CMS\Frontend\ContentObject;
3
4 /**
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16 /**
17 * Contains FlowPlayer class object.
18 *
19 * @author Stanislas Rolland
20 */
21 class FlowPlayerContentObject extends \TYPO3\CMS\Frontend\ContentObject\AbstractContentObject {
22
23 /**
24 * File extension to mime type
25 */
26 public $mimeTypes = array(
27 'aif' => array(
28 'audio' => 'audio/aiff'
29 ),
30 'au' => array(
31 'audio' => 'audio/x-au'
32 ),
33 'avi' => array(
34 'audio' => 'video/x-msvideo'
35 ),
36 'asf' => array(
37 'video' => 'video/x-ms-asf'
38 ),
39 'class' => array(
40 'audio' => 'application/java',
41 'video' => 'application/java'
42 ),
43 'dcr' => array(
44 'video' => 'application/x-director'
45 ),
46 'flac' => array(
47 'audio' => 'audio/flac'
48 ),
49 'flv' => array(
50 'video' => 'video/x-flv'
51 ),
52 'mp3' => array(
53 'audio' => 'audio/mpeg'
54 ),
55 'mp4' => array(
56 'video' => 'video/mp4'
57 ),
58 'oga' => array(
59 'audio' => 'audio/ogg'
60 ),
61 'ogg' => array(
62 'audio' => 'audio/ogg',
63 'video' => 'video/ogg'
64 ),
65 'ogv' => array(
66 'video' => 'video/ogg'
67 ),
68 'swa' => array(
69 'audio' => 'audio/x-m4a'
70 ),
71 'mov' => array(
72 'video' => 'video/quicktime'
73 ),
74 'm4a' => array(
75 'audio' => 'audio/mp4a-latm'
76 ),
77 'm4v' => array(
78 'video' => 'video/x-m4v'
79 ),
80 'qt' => array(
81 'video' => 'video/quicktime'
82 ),
83 'swa' => array(
84 'audio' => 'application/x-director'
85 ),
86 'swf' => array(
87 'audio' => 'application/x-shockwave-flash',
88 'video' => 'application/x-shockwave-flash'
89 ),
90 'wav' => array(
91 'audio' => 'audio/wave'
92 ),
93 'webm' => array(
94 'audio' => 'audio/webm',
95 'video' => 'video/webm'
96 ),
97 'wmv' => array(
98 'audio' => 'audio/x-ms-wmv'
99 )
100 );
101
102 /**
103 * VideoJS options
104 */
105 public $videoJsOptions = array(
106 // Use the browser's controls (iPhone)
107 'useBuiltInControls',
108 // Display control bar below video vs. in front of
109 'controlsBelow',
110 // Make controls visible when page loads
111 'controlsAtStart',
112 // Hide controls when not over the video
113 'controlsHiding',
114 // Will be overridden by localStorage volume if available
115 'defaultVolume',
116 // Players and order to use them
117 'playerFallbackOrder'
118 );
119
120 /**
121 * html5 tag attributes
122 */
123 public $html5TagAttributes = array(
124 'autoPlay',
125 'controls',
126 'loop',
127 'preload'
128 );
129
130 /**
131 * Flowplayer captions plugin configuration
132 */
133 public $flowplayerCaptionsConfig = array(
134 'plugins' => array(
135 // The captions plugin
136 'captions' => array(
137 'url' => 'plugins/flowplayer.captions-3.2.9.swf',
138 // Pointer to a content plugin (see below)
139 'captionTarget' => 'content'
140 ),
141 // Configure a content plugin so that it looks good for showing captions
142 'content' => array(
143 'url' => 'plugins/flowplayer.content-3.2.8.swf',
144 'bottom' => 5,
145 'height' => 40,
146 'backgroundColor' => 'transparent',
147 'backgroundGradient' => 'none',
148 'border' => 0,
149 'textDecoration' => 'outline',
150 'style' => array(
151 'body' => array(
152 'fontSize' => 14,
153 'fontFamily' => 'Arial',
154 'textAlign' => 'center',
155 'color' => '#ffffff'
156 )
157 )
158 )
159 )
160 );
161
162 /**
163 * Flowplayer audio configuration
164 */
165 public $flowplayerAudioConfig = array(
166 'provider' => 'audio',
167 'plugins' => array(
168 'audio' => array(
169 'url' => 'plugins/flowplayer.audio-3.2.10.swf'
170 ),
171 'controls' => array(
172 'autoHide' => FALSE,
173 'fullscreen' => FALSE
174 )
175 )
176 );
177
178 /**
179 * Flowplayer configuration for the audio description
180 */
181 public $flowplayerAudioDescriptionConfig = array(
182 // The controls plugin
183 'plugins' => array(
184 'controls' => NULL
185 )
186 );
187
188 /**
189 * Rendering the cObject, SWFOBJECT
190 *
191 * @param array $conf Array of TypoScript properties
192 * @return string Output
193 */
194 public function render($conf = array()) {
195 /** @var $pageRenderer \TYPO3\CMS\Core\Page\PageRenderer */
196 $pageRenderer = $GLOBALS['TSFE']->getPageRenderer();
197 $prefix = '';
198 if ($GLOBALS['TSFE']->baseUrl) {
199 $prefix = $GLOBALS['TSFE']->baseUrl;
200 }
201 if ($GLOBALS['TSFE']->absRefPrefix) {
202 $prefix = $GLOBALS['TSFE']->absRefPrefix;
203 }
204 // Initialize content
205 $replaceElementIdString = str_replace('.', '', uniqid('mmswf', TRUE));
206 $GLOBALS['TSFE']->register['MMSWFID'] = $replaceElementIdString;
207 $layout = isset($conf['layout.']) ? $this->cObj->stdWrap($conf['layout'], $conf['layout.']) : $conf['layout'];
208 $content = str_replace('###ID###', $replaceElementIdString, $layout);
209 $type = isset($conf['type.']) ? $this->cObj->stdWrap($conf['type'], $conf['type.']) : $conf['type'];
210 $typeConf = $conf[$type . '.'];
211 // Add Flowplayer js-file
212 $pageRenderer->addJsFile(TYPO3_mainDir . 'contrib/flowplayer/flowplayer-3.2.12.min.js');
213 // Add Flowpayer css for exprss install
214 $pageRenderer->addCssFile(TYPO3_mainDir . 'contrib/flowplayer/express-install/express-install.css');
215 // Add videoJS js-file
216 $pageRenderer->addJsFile(TYPO3_mainDir . 'contrib/videojs/video-js/video.js');
217 // Add videoJS js-file
218 $pageRenderer->addJsFile(TYPO3_mainDir . 'contrib/videojs/video-js/video.js');
219 // Add videoJS css-file
220 $pageRenderer->addCssFile(TYPO3_mainDir . 'contrib/videojs/video-js/video-js.css');
221 // Add extended videoJS control bar
222 $pageRenderer->addJsFile(TYPO3_mainDir . 'contrib/videojs/video-js/controls/control-bar.js');
223 $pageRenderer->addCssFile(TYPO3_mainDir . 'contrib/videojs/video-js/controls/control-bar.css');
224 // Build Flash configuration
225 $player = isset($typeConf['player.']) ? $this->cObj->stdWrap($typeConf['player'], $typeConf['player.']) : $typeConf['player'];
226 if (!$player) {
227 $player = $prefix . TYPO3_mainDir . 'contrib/flowplayer/flowplayer-3.2.16.swf';
228 }
229 $installUrl = isset($conf['installUrl.']) ? $this->cObj->stdWrap($conf['installUrl'], $conf['installUrl.']) : $conf['installUrl'];
230 if (!$installUrl) {
231 $installUrl = $prefix . TYPO3_mainDir . 'contrib/flowplayer/expressinstall.swf';
232 }
233 $flashVersion = isset($conf['flashVersion.']) ? $this->cObj->stdWrap($conf['flashVersion'], $conf['flashVersion.']) : $conf['flashVersion'];
234 if (!$flashVersion) {
235 $flashVersion = array(9, 115);
236 }
237 $flashConfiguration = array(
238 // Flowplayer component
239 'src' => $player,
240 // Express install url
241 'expressInstall' => $installUrl,
242 // Require at least this Flash version
243 'version' => $flashVersion,
244 // Older versions will see a message
245 'onFail' => '###ONFAIL###'
246 );
247 $flashDownloadUrl = 'http://www.adobe.com/go/getflashplayer';
248 $onFail = 'function() {
249 if (!(flashembed.getVersion()[0] > 0)) {
250 var message = "<p>" + "' . $GLOBALS['TSFE']->sL('LLL:EXT:cms/locallang_ttc.xlf:media.needFlashPlugin') . '" + "</p>" + "<p>" + "<a href=\\"' . $flashDownloadUrl . '\\">' . $GLOBALS['TSFE']->sL('LLL:EXT:cms/locallang_ttc.xlf:media.downloadFlash') . '</a>" + "</p>";
251 document.getElementById("' . $replaceElementIdString . '_flash_install_info").innerHTML = "<div class=\\"message\\">" + message + "</div>";
252 }
253 }';
254 $flashConfiguration = json_encode($flashConfiguration);
255 $flashConfiguration = str_replace('"###ONFAIL###"', $onFail, $flashConfiguration);
256 $filename = isset($conf['file.']) ? $this->cObj->stdWrap($conf['file'], $conf['file.']) : $conf['file'];
257 if ($filename) {
258 if (strpos($filename, '://') !== FALSE) {
259 $conf['flashvars.']['url'] = $filename;
260 } else {
261 if ($prefix) {
262 $conf['flashvars.']['url'] = $prefix . $filename;
263 } else {
264 $conf['flashvars.']['url'] = str_repeat('../', substr_count($player, '/')) . $filename;
265 }
266 }
267 }
268 if (is_array($conf['sources'])) {
269 foreach ($conf['sources'] as $key => $source) {
270 if (strpos($source, '://') === FALSE) {
271 $conf['sources'][$key] = $prefix . $source;
272 }
273 }
274 }
275 if (is_array($conf['audioSources'])) {
276 foreach ($conf['audioSources'] as $key => $source) {
277 if (strpos($source, '://') === FALSE) {
278 $conf['audioSources'][$key] = $prefix . $source;
279 }
280 }
281 }
282 if (isset($conf['audioFallback']) && strpos($conf['audioFallback'], '://') === FALSE) {
283 $conf['audioFallback'] = $prefix . $conf['audioFallback'];
284 }
285 if (isset($conf['caption']) && strpos($conf['caption'], '://') === FALSE) {
286 $conf['caption'] = $prefix . $conf['caption'];
287 }
288 // Write calculated values in conf for the hook
289 $conf['player'] = $player ?: $filename;
290 $conf['installUrl'] = $installUrl;
291 $conf['filename'] = $conf['flashvars.']['url'];
292 $conf['prefix'] = $prefix;
293 // merge with default parameters
294 $conf['flashvars.'] = array_merge((array) $typeConf['default.']['flashvars.'], (array) $conf['flashvars.']);
295 $conf['params.'] = array_merge((array) $typeConf['default.']['params.'], (array) $conf['params.']);
296 $conf['attributes.'] = array_merge((array) $typeConf['default.']['attributes.'], (array) $conf['attributes.']);
297 $conf['embedParams'] = 'flashvars, params, attributes';
298 // Hook for manipulating the conf array, it's needed for some players like flowplayer
299 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/hooks/class.tx_cms_mediaitems.php']['swfParamTransform'])) {
300 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/hooks/class.tx_cms_mediaitems.php']['swfParamTransform'] as $classRef) {
301 \TYPO3\CMS\Core\Utility\GeneralUtility::callUserFunction($classRef, $conf, $this);
302 }
303 }
304 // Flowplayer config
305 $flowplayerVideoConfig = array();
306 $flowplayerAudioConfig = array();
307 if (is_array($conf['flashvars.'])) {
308 \TYPO3\CMS\Core\Utility\GeneralUtility::remapArrayKeys($conf['flashvars.'], $typeConf['mapping.']['flashvars.']);
309 } else {
310 $conf['flashvars.'] = array();
311 }
312 $conf['videoflashvars'] = $conf['flashvars.'];
313 $conf['audioflashvars'] = $conf['flashvars.'];
314 $conf['audioflashvars']['url'] = $conf['audioFallback'];
315 // Render video sources
316 $videoSources = '';
317 if (is_array($conf['sources'])) {
318 foreach ($conf['sources'] as $source) {
319 $fileinfo = \TYPO3\CMS\Core\Utility\GeneralUtility::split_fileref($source);
320 $mimeType = $this->mimeTypes[$fileinfo['fileext']]['video'];
321 $videoSources .= '<source src="' . $source . '"' . ($mimeType ? ' type="' . $mimeType . '"' : '') . ' />' . LF;
322 }
323 }
324 // Render audio sources
325 $audioSources = '';
326 if (is_array($conf['audioSources'])) {
327 foreach ($conf['audioSources'] as $source) {
328 $fileinfo = \TYPO3\CMS\Core\Utility\GeneralUtility::split_fileref($source);
329 $mimeType = $this->mimeTypes[$fileinfo['fileext']]['audio'];
330 $audioSources .= '<source src="' . $source . '"' . ($mimeType ? ' type="' . $mimeType . '"' : '') . ' />' . LF;
331 }
332 }
333 // Configure captions
334 if ($conf['type'] === 'video' && isset($conf['caption'])) {
335 // Assemble captions track tag
336 $videoCaptions = '<track id="' . $replaceElementIdString . '_captions_track" kind="captions" src="' . $conf['caption'] . '" default="default" />' . LF;
337 // Add videoJS extension for captions
338 $pageRenderer->addJsFile(TYPO3_mainDir . 'contrib/videojs/video-js/controls/captions.js');
339 // Flowplayer captions
340 $conf['videoflashvars']['captionUrl'] = $conf['caption'];
341 // Flowplayer captions plugin configuration
342 $flowplayerVideoConfig = array_merge_recursive($flowplayerVideoConfig, $this->flowplayerCaptionsConfig);
343 }
344 // Configure flowplayer audio fallback
345 if (isset($conf['audioFallback'])) {
346 $flowplayerAudioConfig = array_merge_recursive($flowplayerAudioConfig, $this->flowplayerAudioConfig);
347 }
348 // Configure audio description
349 if ($conf['type'] == 'video') {
350 if (is_array($conf['audioSources']) && count($conf['audioSources'])) {
351 // Add videoJS audio description toggle
352 $pageRenderer->addJsFile(TYPO3_mainDir . 'contrib/videojs/video-js/controls/audio-description.js');
353 }
354 if (isset($conf['audioFallback'])) {
355 // Audio description flowplayer config (remove controls)
356 $flowplayerAudioConfig = array_merge_recursive($flowplayerAudioConfig, $this->flowplayerAudioDescriptionConfig);
357 }
358 }
359 // Assemble Flowplayer configuration
360 if (count($conf['videoflashvars'])) {
361 $flowplayerVideoConfig = array_merge_recursive($flowplayerVideoConfig, array('clip' => $conf['videoflashvars']));
362 }
363 $flowplayerVideoJsonConfig = str_replace(array('"true"', '"false"'), array('true', 'false'), json_encode($flowplayerVideoConfig));
364 if (count($conf['audioflashvars'])) {
365 $flowplayerAudioConfig = array_merge_recursive($flowplayerAudioConfig, array('clip' => $conf['audioflashvars']));
366 }
367 $flowplayerAudioJsonConfig = str_replace(array('"true"', '"false"'), array('true', 'false'), json_encode($flowplayerAudioConfig));
368 // Assemble param tags (required?)
369 if (is_array($conf['params.'])) {
370 \TYPO3\CMS\Core\Utility\GeneralUtility::remapArrayKeys($conf['params.'], $typeConf['mapping.']['params.']);
371 }
372 $videoFlashParams = '';
373 if (is_array($conf['params.'])) {
374 foreach ($conf['params.'] as $name => $value) {
375 $videoFlashParams .= '<param name="' . $name . '" value="' . $value . '" />' . LF;
376 }
377 }
378 $audioFlashParams = $videoFlashParams;
379 // Required param tags
380 $videoFlashParams .= '<param name="movie" value="' . $player . '" />' . LF;
381 $videoFlashParams .= '<param name="flashvars" value=\'config=' . $flowplayerVideoJsonConfig . '\' />' . LF;
382 $audioFlashParams .= '<param name="movie" value="' . $player . '" />' . LF;
383 $audioFlashParams .= '<param name="flashvars" value=\'config=' . $flowplayerAudioJsonConfig . '\' />' . LF;
384 // Assemble audio/video tag attributes
385 $attributes = '';
386 if (is_array($conf['attributes.'])) {
387 \TYPO3\CMS\Core\Utility\GeneralUtility::remapArrayKeys($conf['attributes.'], $typeConf['attributes.']['params.']);
388 }
389 foreach ($this->html5TagAttributes as $attribute) {
390 if ($conf['attributes.'][$attribute] === 'true' || $conf['attributes.'][$attribute] === strToLower($attribute) || $conf['attributes.'][$attribute] === $attribute) {
391 $attributes .= strToLower($attribute) . '="' . strToLower($attribute) . '" ';
392 }
393 }
394 // Media dimensions
395 $width = isset($conf['width.']) ? $this->cObj->stdWrap($conf['width'], $conf['width.']) : $conf['width'];
396 if (!$width) {
397 $width = $conf[$type . '.']['defaultWidth'];
398 }
399 $height = isset($conf['height.']) ? $this->cObj->stdWrap($conf['height'], $conf['height.']) : $conf['height'];
400 if (!$height) {
401 $height = $conf[$type . '.']['defaultHeight'];
402 }
403 // Alternate content
404 $alternativeContent = isset($conf['alternativeContent.']) ? $this->cObj->stdWrap($conf['alternativeContent'], $conf['alternativeContent.']) : $conf['alternativeContent'];
405 // Render video
406 if ($conf['type'] === 'video') {
407 if ($conf['preferFlashOverHtml5']) {
408 // Flash with video tag fallback
409 $conf['params.']['playerFallbackOrder'] = array('flash', 'html5');
410 $flashDivContent = $videoFlashParams . LF . '<video id="' . $replaceElementIdString . '_video_js" class="video-js" ' . $attributes . 'controls="controls" mediagroup="' . $replaceElementIdString . '" width="' . $width . '" height="' . $height . '">' . LF . $videoSources . $videoCaptions . $alternativeContent . LF . '</video>' . LF;
411 $divContent = '
412 <div id="' . $replaceElementIdString . '_flash_install_info" class="flash-install-info"></div>' . LF . '<noscript>' . LF . '<object id="' . $replaceElementIdString . '_vjs_flash" type="application/x-shockwave-flash" data="' . $player . '" width="' . $width . '" height="' . $height . '">' . LF . $flashDivContent . '</object>' . LF . '</noscript>' . LF;
413 $content = str_replace('###SWFOBJECT###', '<div id="' . $replaceElementIdString . '_video" class="flashcontainer" style="width:' . $width . 'px; height:' . $height . 'px;">' . LF . $divContent . '</div>', $content);
414 } else {
415 // Video tag with Flash fallback
416 $conf['params.']['playerFallbackOrder'] = array('html5', 'flash');
417 $videoTagContent = $videoSources . $videoCaptions;
418 if (isset($conf['videoflashvars']['url'])) {
419 $videoTagContent .= '
420 <noscript>' . LF . '<object class="vjs-flash-fallback" id="' . $replaceElementIdString . '_vjs_flash_fallback" type="application/x-shockwave-flash" data="' . $player . '" width="' . $width . '" height="' . $height . '">' . LF . $videoFlashParams . LF . $alternativeContent . LF . '</object>' . LF . '</noscript>';
421 }
422 $divContent = '
423 <div id="' . $replaceElementIdString . '_flash_install_info" class="flash-install-info"></div>' . LF . '<video id="' . $replaceElementIdString . '_video_js" class="video-js" ' . $attributes . 'controls="controls" mediagroup="' . $replaceElementIdString . '" width="' . $width . '" height="' . $height . '">' . LF . $videoTagContent . '</video>';
424 $content = str_replace('###SWFOBJECT###', '<div id="' . $replaceElementIdString . '_video" class="video-js-box" style="width:' . $width . 'px; height:' . $height . 'px;">' . LF . $divContent . '</div>', $content);
425 }
426 }
427 // Render audio
428 if ($conf['type'] === 'audio' || $audioSources || isset($conf['audioFallback'])) {
429 if ($conf['preferFlashOverHtml5']) {
430 // Flash with audio tag fallback
431 $flashDivContent = $audioFlashParams . LF . '<audio id="' . $replaceElementIdString . '_audio_element"' . $attributes . ($conf['type'] === 'video' ? ' mediagroup="' . $replaceElementIdString . 'style="position:absolute;left:-10000px;"' : ' controls="controls"') . ' style="width:' . $width . 'px; height:' . $height . 'px;">' . LF . $audioSources . $alternativeContent . LF . '</audio>' . LF;
432 $divContent = ($conf['type'] === 'video' ? '' : '<div id="' . $replaceElementIdString . '_flash_install_info" class="flash-install-info"></div>' . LF) . '<noscript>' . LF . '<object id="' . $replaceElementIdString . '_audio_flash" type="application/x-shockwave-flash" data="' . $player . '" width="' . ($conf['type'] === 'video' ? 0 : $width) . '" height="' . ($conf['type'] === 'video' ? 0 : $height) . '">' . LF . $flashDivContent . '</object>' . LF . '</noscript>' . LF;
433 $audioContent = '<div id="' . $replaceElementIdString . '_audio_box" class="audio-flash-container" style="width:' . ($conf['type'] === 'video' ? 0 : $width) . 'px; height:' . ($conf['type'] === 'video' ? 0 : $height) . 'px;">' . LF . $divContent . '</div>';
434 } else {
435 // Audio tag with Flash fallback
436 $audioTagContent = $audioSources;
437 if (isset($conf['audioflashvars']['url'])) {
438 $audioTagContent .= '
439 <noscript>' . LF . '<object class="audio-flash-fallback" id="' . $replaceElementIdString . '_audio_flash" type="application/x-shockwave-flash" data="' . $player . '" width="' . $width . '" height="' . $height . '">' . LF . $audioFlashParams . LF . $alternativeContent . LF . '</object>' . LF . '</noscript>';
440 }
441 $divContent = ($conf['type'] === 'video' ? '' : '<div id="' . $replaceElementIdString . '_flash_install_info" class="flash-install-info"></div>' . LF) . '<audio id="' . $replaceElementIdString . '_audio_element" class="audio-element"' . $attributes . ($conf['type'] === 'video' ? ' mediagroup="' . $replaceElementIdString . '" style="position:absolute;left:-10000px;"' : ' controls="controls"') . '>' . LF . $audioTagContent . '</audio>' . LF . $audioSourcesEmbeddingJsScript;
442 $audioContent = '<div id="' . $replaceElementIdString . '_audio_box" class="audio-box" style="width:' . ($conf['type'] === 'video' ? 0 : $width) . 'px; height:' . ($conf['type'] === 'video' ? 0 : $height) . 'px;">' . LF . $divContent . '</div>';
443 }
444 if ($conf['type'] === 'audio') {
445 $content = str_replace('###SWFOBJECT###', $audioContent, $content);
446 } else {
447 $content .= LF . $audioContent;
448 }
449 }
450 // Assemble inline JS code
451 $videoJsSetup = '';
452 $flowplayerHandlers = '';
453 if ($conf['type'] === 'video') {
454 // Assemble videoJS options
455 $videoJsOptions = array();
456 foreach ($this->videoJsOptions as $videoJsOption) {
457 if (isset($conf['params.'][$videoJsOption])) {
458 $videoJsOptions[$videoJsOption] = $conf['params.'][$videoJsOption];
459 }
460 }
461 $videoJsOptions = count($videoJsOptions) ? json_encode($videoJsOptions) : '{}';
462 // videoJS setup and videoJS listeners for audio description synchronisation
463 if ($audioSources || isset($conf['audioFallback'])) {
464 $videoJsSetup = '
465 var ' . $replaceElementIdString . '_video = VideoJS.setup("' . $replaceElementIdString . '_video_js", ' . $videoJsOptions . ');
466 var ' . $replaceElementIdString . '_video_element = document.getElementById("' . $replaceElementIdString . '_video_js");
467 var ' . $replaceElementIdString . '_audio_element = document.getElementById("' . $replaceElementIdString . '_audio_element");
468 if (!!' . $replaceElementIdString . '_video_element && !!' . $replaceElementIdString . '_audio_element) {
469 ' . $replaceElementIdString . '_audio_element.muted = true;
470 VideoJS.addListener(' . $replaceElementIdString . '_video_element, "pause", function () { document.getElementById("' . $replaceElementIdString . '_audio_element").pause(); });
471 VideoJS.addListener(' . $replaceElementIdString . '_video_element, "play", function () { try {document.getElementById("' . $replaceElementIdString . '_audio_element").currentTime = document.getElementById("' . $replaceElementIdString . '_video_js").currentTime} catch(e) {}; document.getElementById("' . $replaceElementIdString . '_audio_element").play(); });
472 VideoJS.addListener(' . $replaceElementIdString . '_video_element, "seeked", function () { document.getElementById("' . $replaceElementIdString . '_audio_element").currentTime = document.getElementById("' . $replaceElementIdString . '_video_js").currentTime; });
473 VideoJS.addListener(' . $replaceElementIdString . '_video_element, "volumechange", function () { document.getElementById("' . $replaceElementIdString . '_audio_element").volume = document.getElementById("' . $replaceElementIdString . '_video_js").volume; });
474 }';
475 } else {
476 $videoJsSetup = '
477 var ' . $replaceElementIdString . '_video = VideoJS.setup("' . $replaceElementIdString . '_video_js", ' . $videoJsOptions . ');
478 ';
479 }
480 // Prefer Flash or fallback to Flash
481 $videoSourcesEmbedding = '';
482 // If we have a video file for Flash
483 if (isset($conf['filename'])) {
484 // If we prefer Flash
485 if ($conf['preferFlashOverHtml5']) {
486 $videoTagAssembly = '';
487 // Create "source" elements
488 if (is_array($conf['sources']) && count($conf['sources'])) {
489 foreach ($conf['sources'] as $source) {
490 $fileinfo = \TYPO3\CMS\Core\Utility\GeneralUtility::split_fileref($source);
491 $mimeType = $this->mimeTypes[$fileinfo['fileext']]['video'];
492 $videoTagAssembly .= '
493 ' . $replaceElementIdString . '_video_js.appendChild($f.extend(document.createElement("source"), {
494 src: "' . $source . '",
495 type: "' . $mimeType . '"
496 }));';
497 }
498 // Create "track" elements
499 if (isset($conf['caption'])) {
500 // Assemble captions track tag
501 // It will take a while before the captions are loaded and parsed...
502 $videoTagAssembly .= '
503 var track = document.createElement("track");
504 track.setAttribute("src", "' . $conf['caption'] . '");
505 track.setAttribute("id", "' . $replaceElementIdString . '_captions_track");
506 track.setAttribute("kind", "captions");
507 track.setAttribute("default", "default");
508 ' . $replaceElementIdString . '_video_js.appendChild(track);';
509 }
510 $videoTagAssembly .= '
511 $f.extend(' . $replaceElementIdString . '_video_js, {
512 id: "' . $replaceElementIdString . '_video_js",
513 className: "video-js",
514 controls: "controls",
515 mediagroup: "' . $replaceElementIdString . '",
516 preload: "none",
517 width: "' . $width . '",
518 height: "' . $height . '"
519 });
520 ' . $replaceElementIdString . '_video.appendChild(' . $replaceElementIdString . '_video_js);
521 ' . $replaceElementIdString . '_video.className = "video-js-box";';
522 $videoTagAssembly .= $videoJsSetup;
523 }
524 $videoSourcesEmbedding = '
525 var ' . $replaceElementIdString . '_video = document.getElementById("' . $replaceElementIdString . '_video");
526 var ' . $replaceElementIdString . '_video_js = document.createElement("video");
527 if (flashembed.getVersion()[0] > 0) {
528 // Flash is available
529 var videoPlayer = flowplayer("' . $replaceElementIdString . '_video", ' . $flashConfiguration . ', ' . $flowplayerVideoJsonConfig . ').load();
530 videoPlayer.onBeforeUnload(function () { return false; });
531 } else if (!!' . $replaceElementIdString . '_video_js.canPlayType) {
532 // Flash is not available: fallback to videoJS if video tag is supported
533 ' . $videoTagAssembly . '
534 } else {
535 // Neither Flash nor video is available: offer to install Flash
536 flashembed("' . $replaceElementIdString . '_video", ' . $flashConfiguration . ');
537 }';
538 } elseif (is_array($conf['sources'])) {
539 // HTML5 is the preferred rendering method
540 // Test whether the browser supports any of types of the provided sources
541 $supported = array();
542 foreach ($conf['sources'] as $source) {
543 $fileinfo = \TYPO3\CMS\Core\Utility\GeneralUtility::split_fileref($source);
544 $mimeType = $this->mimeTypes[$fileinfo['fileext']]['video'];
545 $supported[] = $replaceElementIdString . '_videoTag.canPlayType("' . $mimeType . '") != ""';
546 }
547 // Testing whether the browser supports the video tag with any of the provided source types
548 // If no support, embed flowplayer
549 $videoSourcesEmbedding = '
550 var ' . $replaceElementIdString . '_videoTag = document.createElement(\'video\');
551 var ' . $replaceElementIdString . '_video_box = document.getElementById("' . $replaceElementIdString . '_video");
552 if (' . $replaceElementIdString . '_video_box) {
553 if (!' . $replaceElementIdString . '_videoTag || !' . $replaceElementIdString . '_videoTag.canPlayType || !(' . (count($supported) ? implode(' || ', $supported) : 'false') . ')) {
554 // Avoid showing an empty video element
555 if (document.getElementById("' . $replaceElementIdString . '_video_js")) {
556 document.getElementById("' . $replaceElementIdString . '_video_js").style.display = "none";
557 }
558 if (flashembed.getVersion()[0] > 0) {
559 // Flash is available
560 var videoPlayer = flowplayer("' . $replaceElementIdString . '_video", ' . $flashConfiguration . ', ' . $flowplayerVideoJsonConfig . ').load();
561 videoPlayer.onBeforeUnload(function () { return false; });
562 } else {
563 // Neither Flash nor video is available: offer to install Flash
564 flashembed("' . $replaceElementIdString . '_video", ' . $flashConfiguration . ');
565 }
566 } else {' . $videoJsSetup . '
567 }
568 }';
569 }
570 }
571 }
572 // Audio fallback to Flash
573 $audioSourcesEmbedding = '';
574 // If we have an audio file for Flash
575 if (isset($conf['audioFallback'])) {
576 // If we prefer Flash in
577 if ($conf['preferFlashOverHtml5']) {
578 $audioTagAssembly = '';
579 // Create "source" elements
580 if (is_array($conf['audioSources']) && count($conf['audioSources'])) {
581 foreach ($conf['audioSources'] as $source) {
582 $fileinfo = \TYPO3\CMS\Core\Utility\GeneralUtility::split_fileref($source);
583 $mimeType = $this->mimeTypes[$fileinfo['fileext']]['audio'];
584 $audioTagAssembly .= '
585 ' . $replaceElementIdString . '_audio_element.appendChild($f.extend(document.createElement("source"), {
586 src: "' . $source . '",
587 type: "' . $mimeType . '"
588 }));';
589 }
590 $audioTagAssembly .= '
591 $f.extend(' . $replaceElementIdString . '_audio_element, {
592 id: "' . $replaceElementIdString . '_audio_element",
593 className: "audio-element",
594 controls: "' . ($conf['type'] === 'video' ? '' : 'controls') . '",
595 mediagroup: "' . $replaceElementIdString . '",
596 preload: "none",
597 width: "' . ($conf['type'] === 'video' ? 0 : $width) . 'px",
598 height: "' . ($conf['type'] === 'video' ? 0 : $height) . 'px"
599 });
600 ' . $replaceElementIdString . '_audio_box.appendChild(' . $replaceElementIdString . '_audio_element);
601 ' . $replaceElementIdString . '_audio_box.className = "audio-box";';
602 }
603 $audioSourcesEmbedding = '
604 var ' . $replaceElementIdString . '_audio_box = document.getElementById("' . $replaceElementIdString . '_audio_box");
605 var ' . $replaceElementIdString . '_audio_element = document.createElement("audio");
606 if (flashembed.getVersion()[0] > 0) {
607 // Flash is available
608 var audioPlayer = flowplayer("' . $replaceElementIdString . '_audio_box", ' . $flashConfiguration . ', ' . $flowplayerAudioJsonConfig . ').load();
609 audioPlayer.onBeforeUnload(function () { return false; });
610 ' . ($conf['type'] === 'video' ? 'audioPlayer.mute();' : '') . '
611 } else if (!!' . $replaceElementIdString . '_audio_element.canPlayType) {
612 // Flash is not available: fallback to audio element if audio tag is supported
613 ' . $audioTagAssembly . '
614 } else {
615 // Neither Flash nor audio is available: offer to install Flash if this is not an audio description of a video
616 ' . ($conf['type'] === 'video' ? '' : 'flashembed("' . $replaceElementIdString . '_audio_box", ' . $flashConfiguration . ');') . '
617 }';
618 } elseif (is_array($conf['audioSources'])) {
619 // HTML5 is the preferred rendering method
620 // Test whether the browser supports any of types of the provided sources
621 $supported = array();
622 foreach ($conf['audioSources'] as $source) {
623 $fileinfo = \TYPO3\CMS\Core\Utility\GeneralUtility::split_fileref($source);
624 $mimeType = $this->mimeTypes[$fileinfo['fileext']]['audio'];
625 $supported[] = $replaceElementIdString . '_audioTag.canPlayType("' . $mimeType . '") != ""';
626 }
627 // Testing whether the browser supports the audio tag with any of the provided source types
628 // If no support, embed flowplayer
629 $audioSourcesEmbedding = '
630 var ' . $replaceElementIdString . '_audioTag = document.createElement(\'audio\');
631 var ' . $replaceElementIdString . '_audio_box = document.getElementById("' . $replaceElementIdString . '_audio_box");
632 if (' . $replaceElementIdString . '_audio_box) {
633 if (!' . $replaceElementIdString . '_audioTag || !' . $replaceElementIdString . '_audioTag.canPlayType || !(' . (count($supported) ? implode(' || ', $supported) : 'false') . ')) {
634 // Avoid showing an empty audio element
635 if (document.getElementById("' . $replaceElementIdString . '_audio_element")) {
636 document.getElementById("' . $replaceElementIdString . '_audio_element").style.display = "none";
637 }
638 if (flashembed.getVersion()[0] > 0) {
639 var audioPlayer = flowplayer("' . $replaceElementIdString . '_audio_box", ' . $flashConfiguration . ', ' . $flowplayerAudioJsonConfig . ').load();
640 audioPlayer.onBeforeUnload(function () { return false; });
641 ' . ($conf['type'] === 'video' ? 'audioPlayer.mute()' : '') . '
642 } else {
643 // Neither Flash nor audio is available: offer to install Flash if this is not an audio description of a video
644 ' . ($conf['type'] === 'video' ? '' : 'flashembed("' . $replaceElementIdString . '_audio_box", ' . $flashConfiguration . ');') . '
645 }
646 }
647 }';
648 }
649 // Flowplayer eventHandlers for audio description synchronisation
650 $flowplayerHandlers = '';
651 if ($conf['type'] === 'video') {
652 $flowplayerHandlers = '
653 if (flashembed.getVersion()[0] > 0) {
654 // Flash is available
655 var videoPlayer = flowplayer("' . $replaceElementIdString . '_video");
656 if (videoPlayer) {
657 // Control audio description through video control bar
658 videoPlayer.onVolume(function (volume) { flowplayer("' . $replaceElementIdString . '_audio_box").setVolume(volume); });
659 videoPlayer.onMute(function () { flowplayer("' . $replaceElementIdString . '_audio_box").mute(); });
660 videoPlayer.onUnmute(function () { flowplayer("' . $replaceElementIdString . '_audio_box").unmute(); });
661 videoPlayer.onPause(function () { flowplayer("' . $replaceElementIdString . '_audio_box").pause(); });
662 videoPlayer.onResume(function () { flowplayer("' . $replaceElementIdString . '_audio_box").resume(); });
663 videoPlayer.onStart(function () { flowplayer("' . $replaceElementIdString . '_audio_box").play(); });
664 videoPlayer.onStop(function () { flowplayer("' . $replaceElementIdString . '_audio_box").stop(); });
665 videoPlayer.onSeek(function (clip, seconds) { flowplayer("' . $replaceElementIdString . '_audio_box").seek(seconds); });
666 // Mute audio description on start
667 flowplayer("' . $replaceElementIdString . '_audio_box").onStart(function () { this.mute()});
668 // Audio description toggle
669 var videoContainer = document.getElementById("' . $replaceElementIdString . '_video");
670 var buttonContainer = document.createElement("div");
671 $f.extend(buttonContainer, {
672 id: "' . $replaceElementIdString . '_audio_description_toggle",
673 className: "vjs-audio-description-control"
674 });
675 var button = document.createElement("div");
676 buttonContainer.appendChild(button);
677 buttonContainer.style.position = "relative";
678 buttonContainer.style.left = (parseInt(' . $width . ', 10)-27) + "px";
679 videoContainer.parentNode.insertBefore(buttonContainer, videoContainer.nextSibling);
680 VideoJS.addListener(buttonContainer, "click", function () {
681 var buttonContainer = document.getElementById("' . $replaceElementIdString . '_audio_description_toggle");
682 var state = buttonContainer.getAttribute("data-state");
683 if (state == "enabled") {
684 buttonContainer.setAttribute("data-state", "disabled");
685 flowplayer("' . $replaceElementIdString . '_audio_box").mute();
686 } else {
687 buttonContainer.setAttribute("data-state", "enabled");
688 flowplayer("' . $replaceElementIdString . '_audio_box").unmute();
689 }
690 });
691 }
692 }';
693 }
694 }
695 // Wrap up inline JS code
696 $jsInlineCode = $audioSourcesEmbedding . $videoSourcesEmbedding . $flowplayerHandlers;
697 if ($jsInlineCode) {
698 $jsInlineCode = 'VideoJS.DOMReady(function(){' . $jsInlineCode . LF . '});';
699 }
700 $pageRenderer->addJsInlineCode($replaceElementIdString, $jsInlineCode);
701 if (isset($conf['stdWrap.'])) {
702 $content = $this->cObj->stdWrap($content, $conf['stdWrap.']);
703 }
704 return $content;
705 }
706
707 }