Embed rework

This commit is contained in:
Cat 2018-01-23 10:37:05 +01:00
parent 98054f268f
commit ac7659614a
58 changed files with 8278 additions and 49187 deletions

View file

@ -370,22 +370,19 @@ add_executable(MistOutHTTP
src/output/output_http.cpp src/output/output_http.cpp
src/output/output_http_internal.cpp src/output/output_http_internal.cpp
src/io.cpp src/io.cpp
generated/silverlight.js.h generated/player.js.h
generated/embed.js.h
generated/html5.js.h generated/html5.js.h
generated/flash_strobe.js.h generated/flash_strobe.js.h
generated/theoplayer.js.h
generated/jwplayer.js.h
generated/polytrope.js.h
generated/dashjs.js.h generated/dashjs.js.h
generated/videojs.js.h generated/videojs.js.h
generated/img.js.h generated/webrtc.js.h
generated/playerdash.js.h generated/player_dash.js.h
generated/playerdashlic.js.h generated/player_dash_lic.js.h
generated/playervideo.js.h generated/player_video.js.h
generated/playerhlsvideo.js.h generated/player_webrtc.js.h
generated/core.js.h generated/skin_default.css.h
generated/mist.css.h generated/skin_dev.css.h
generated/skin_videojs.css.h
) )
set_target_properties(MistOutHTTP set_target_properties(MistOutHTTP
PROPERTIES COMPILE_DEFINITIONS "OUTPUTTYPE=\"output_http_internal.h\"" PROPERTIES COMPILE_DEFINITIONS "OUTPUTTYPE=\"output_http_internal.h\""
@ -445,69 +442,61 @@ endif()
######################################## ########################################
# Embed Code # # Embed Code #
######################################## ########################################
add_custom_command(OUTPUT generated/silverlight.js.h # main
COMMAND ./sourcery ${SOURCE_DIR}/embed/wrappers/silverlight.js silverlight_js generated/silverlight.js.h add_custom_command(OUTPUT generated/player.js.h
DEPENDS sourcery ${SOURCE_DIR}/embed/wrappers/silverlight.js COMMAND ./sourcery ${SOURCE_DIR}/embed/min/player.js player_js generated/player.js.h
) DEPENDS sourcery ${SOURCE_DIR}/embed/min/player.js
add_custom_command(OUTPUT generated/embed.js.h
COMMAND ./sourcery ${SOURCE_DIR}/src/embed.js embed_js generated/embed.js.h
DEPENDS sourcery ${SOURCE_DIR}/src/embed.js
) )
# wrappers
add_custom_command(OUTPUT generated/html5.js.h add_custom_command(OUTPUT generated/html5.js.h
COMMAND ./sourcery ${SOURCE_DIR}/embed/wrappers/html5.js html5_js generated/html5.js.h COMMAND ./sourcery ${SOURCE_DIR}/embed/min/wrappers/html5.js html5_js generated/html5.js.h
DEPENDS sourcery ${SOURCE_DIR}/embed/wrappers/html5.js DEPENDS sourcery ${SOURCE_DIR}/embed/min/wrappers/html5.js
) )
add_custom_command(OUTPUT generated/flash_strobe.js.h add_custom_command(OUTPUT generated/flash_strobe.js.h
COMMAND ./sourcery ${SOURCE_DIR}/embed/wrappers/flash_strobe.js flash_strobe_js generated/flash_strobe.js.h COMMAND ./sourcery ${SOURCE_DIR}/embed/min/wrappers/flash_strobe.js flash_strobe_js generated/flash_strobe.js.h
DEPENDS sourcery ${SOURCE_DIR}/embed/wrappers/flash_strobe.js DEPENDS sourcery ${SOURCE_DIR}/embed/min/wrappers/flash_strobe.js
)
add_custom_command(OUTPUT generated/theoplayer.js.h
COMMAND ./sourcery ${SOURCE_DIR}/embed/wrappers/theoplayer.js theoplayer_js generated/theoplayer.js.h
DEPENDS sourcery ${SOURCE_DIR}/embed/wrappers/theoplayer.js
)
add_custom_command(OUTPUT generated/jwplayer.js.h
COMMAND ./sourcery ${SOURCE_DIR}/embed/wrappers/jwplayer.js jwplayer_js generated/jwplayer.js.h
DEPENDS sourcery ${SOURCE_DIR}/embed/wrappers/jwplayer.js
)
add_custom_command(OUTPUT generated/polytrope.js.h
COMMAND ./sourcery ${SOURCE_DIR}/embed/wrappers/polytrope.js polytrope_js generated/polytrope.js.h
DEPENDS sourcery ${SOURCE_DIR}/embed/wrappers/polytrope.js
) )
add_custom_command(OUTPUT generated/dashjs.js.h add_custom_command(OUTPUT generated/dashjs.js.h
COMMAND ./sourcery ${SOURCE_DIR}/embed/wrappers/dashjs.js dash_js generated/dashjs.js.h COMMAND ./sourcery ${SOURCE_DIR}/embed/min/wrappers/dashjs.js dash_js generated/dashjs.js.h
DEPENDS sourcery ${SOURCE_DIR}/embed/wrappers/dashjs.js DEPENDS sourcery ${SOURCE_DIR}/embed/min/wrappers/dashjs.js
) )
add_custom_command(OUTPUT generated/videojs.js.h add_custom_command(OUTPUT generated/videojs.js.h
COMMAND ./sourcery ${SOURCE_DIR}/embed/wrappers/videojs.js video_js generated/videojs.js.h COMMAND ./sourcery ${SOURCE_DIR}/embed/min/wrappers/videojs.js video_js generated/videojs.js.h
DEPENDS sourcery ${SOURCE_DIR}/embed/wrappers/videojs.js DEPENDS sourcery ${SOURCE_DIR}/embed/min/wrappers/videojs.js
) )
add_custom_command(OUTPUT generated/img.js.h add_custom_command(OUTPUT generated/webrtc.js.h
COMMAND ./sourcery ${SOURCE_DIR}/embed/wrappers/img.js img_js generated/img.js.h COMMAND ./sourcery ${SOURCE_DIR}/embed/min/wrappers/webrtc.js webrtc_js generated/webrtc.js.h
DEPENDS sourcery ${SOURCE_DIR}/embed/wrappers/img.js DEPENDS sourcery ${SOURCE_DIR}/embed/min/wrappers/webrtc.js
) )
add_custom_command(OUTPUT generated/playerdashlic.js.h # players
COMMAND ./sourcery ${SOURCE_DIR}/embed/players/dash.js.license.js playerdashlic_js generated/playerdashlic.js.h add_custom_command(OUTPUT generated/player_dash_lic.js.h
COMMAND ./sourcery ${SOURCE_DIR}/embed/players/dash.js.license.js player_dash_lic_js generated/player_dash_lic.js.h
DEPENDS sourcery ${SOURCE_DIR}/embed/players/dash.js.license.js DEPENDS sourcery ${SOURCE_DIR}/embed/players/dash.js.license.js
) )
add_custom_command(OUTPUT generated/playerdash.js.h add_custom_command(OUTPUT generated/player_dash.js.h
COMMAND ./sourcery ${SOURCE_DIR}/embed/players/dash.all.min.js playerdash_js generated/playerdash.js.h COMMAND ./sourcery ${SOURCE_DIR}/embed/players/dash.all.min.js player_dash_js generated/player_dash.js.h
DEPENDS sourcery ${SOURCE_DIR}/embed/players/dash.all.min.js DEPENDS sourcery ${SOURCE_DIR}/embed/players/dash.all.min.js
) )
add_custom_command(OUTPUT generated/playervideo.js.h add_custom_command(OUTPUT generated/player_video.js.h
COMMAND ./sourcery ${SOURCE_DIR}/embed/players/video.min.js playervideo_js generated/playervideo.js.h COMMAND ./sourcery ${SOURCE_DIR}/embed/players/video.min.js player_video_js generated/player_video.js.h
DEPENDS sourcery ${SOURCE_DIR}/embed/players/video.min.js DEPENDS sourcery ${SOURCE_DIR}/embed/players/video.min.js
) )
add_custom_command(OUTPUT generated/playerhlsvideo.js.h add_custom_command(OUTPUT generated/player_webrtc.js.h
COMMAND ./sourcery ${SOURCE_DIR}/embed/players/videojs-contrib-hls.min.js playerhlsvideo_js generated/playerhlsvideo.js.h COMMAND ./sourcery ${SOURCE_DIR}/embed/players/webrtc.js player_webrtc_js generated/player_webrtc.js.h
DEPENDS sourcery ${SOURCE_DIR}/embed/players/videojs-contrib-hls.min.js DEPENDS sourcery ${SOURCE_DIR}/embed/players/webrtc.js
) )
add_custom_command(OUTPUT generated/core.js.h # css
COMMAND ./sourcery ${SOURCE_DIR}/embed/core.js core_js generated/core.js.h add_custom_command(OUTPUT generated/skin_default.css.h
DEPENDS sourcery ${SOURCE_DIR}/embed/core.js COMMAND ./sourcery ${SOURCE_DIR}/embed/min/skins/default.css skin_default_css generated/skin_default.css.h
DEPENDS sourcery ${SOURCE_DIR}/embed/min/skins/default.css
) )
add_custom_command(OUTPUT generated/mist.css.h add_custom_command(OUTPUT generated/skin_dev.css.h
COMMAND ./sourcery ${SOURCE_DIR}/embed/mist.css mist_css generated/mist.css.h COMMAND ./sourcery ${SOURCE_DIR}/embed/min/skins/dev.css skin_dev_css generated/skin_dev.css.h
DEPENDS sourcery ${SOURCE_DIR}/embed/mist.css DEPENDS sourcery ${SOURCE_DIR}/embed/min/skins/dev.css
)
add_custom_command(OUTPUT generated/skin_videojs.css.h
COMMAND ./sourcery ${SOURCE_DIR}/embed/skins/video-js.css skin_videojs_css generated/skin_videojs.css.h
DEPENDS sourcery ${SOURCE_DIR}/embed/skins/video-js.css
) )
######################################## ########################################

122
embed/controls.js vendored Normal file
View file

@ -0,0 +1,122 @@
function MistUI(MistVideo,structure) {
MistVideo.UI = this;
this.elements = [];
this.buildStructure = function(structure){
if (typeof structure == "function") { structure = structure.call(MistVideo); }
if ("if" in structure) {
var result = false;
if (structure.if.call(MistVideo,structure)) {
result = structure.then;
}
else if ("else" in structure) {
result = structure.else;
}
if (!result) { return; }
//append the result with structure options
for (var i in structure) {
if (["if","then","else"].indexOf(i) < 0) {
if (i in result) {
if (!(result[i] instanceof Array)) {
result[i] = [result[i]];
}
result[i] = result[i].concat(structure[i]);
}
else {
result[i] = structure[i];
}
}
}
return this.buildStructure(result);
}
if ("type" in structure) {
if (structure.type in MistVideo.skin.blueprints) {
//create the element; making sure to pass "this" to blueprint function
var container = MistVideo.skin.blueprints[structure.type].call(MistVideo,structure);
if (!container) { return; }
MistUtil.class.add(container,"mistvideo-"+structure.type);
if ("css" in structure) {
var uid = MistUtil.createUnique();
structure.css = [].concat(structure.css); //convert to array; should be in string format with colors already applied
for (var i in structure.css) {
var style = MistUtil.css.createStyle(structure.css[i],uid);
container.appendChild(style);
}
MistUtil.class.add(container,uid);
container.uid = uid;
}
if ("classes" in structure) {
for (var i in structure.classes) {
MistUtil.class.add(container,structure.classes[i]);
}
}
if ("title" in structure) {
container.title = structure.title;
}
if ("style" in structure) {
for (var i in structure.style) {
container.style[i] = structure.style[i];
}
}
if ("children" in structure) {
for (var i in structure.children) {
var child = this.buildStructure(structure.children[i]);
if (child) {
container.appendChild(child);
}
}
}
//save the returned element so they can be killed on unload
MistVideo.UI.elements.push(container);
return container;
}
}
return false;
};
this.build = function(){
return this.buildStructure(structure ? structure : MistVideo.skin.structure.main);
};
var container = this.build();
//apply skin CSS
var uid = MistUtil.createUnique();
var toload = MistVideo.skin.css.length;
if (toload) { container.style.opacity = "0"; }
for (var i in MistVideo.skin.css) {
var style = MistVideo.skin.css[i];
style.callback = function(css) {
this.textContent = MistUtil.css.prependClass(css,uid,true);
toload--;
if (toload <= 0) {
container.style.opacity = "";
}
};
if (style.textContent != "") {
//it has already loaded
style.callback(style.textContent);
}
container.appendChild(style);
}
MistUtil.class.add(container,uid);
//add browser class
var browser = MistUtil.getBrowser();
if (browser) {
MistUtil.class.add(container,"browser-"+browser);
}
return container;
}

File diff suppressed because it is too large Load diff

View file

@ -1 +1,13 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.1" id="svg3937" height="45" width="45"><defs id="defs3939" /><metadata id="metadata3942"><rdf:RDF><cc:Work rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><g transform="translate(0,-1007.3622)" id="layer1"><g transform="translate(0,-1.109375)" id="g4563"><g id="g4558"><path id="rect3945" transform="translate(0,1007.3622)" d="M 5.15625,10 C 3.6913461,10 2.5,11.191346 2.5,12.65625 l 0,19.6875 C 2.5,33.808654 3.6913461,35 5.15625,35 l 34.6875,0 C 41.308654,35 42.5,33.808654 42.5,32.34375 l 0,-19.6875 C 42.5,11.191346 41.308654,10 39.84375,10 L 5.15625,10 z M 5,12.53125 l 35,0 0,20 -35,0 0,-20 z" style="fill:#fff;fill-opacity:1;stroke:none" /><rect ry="0" y="1019.8622" x="5" height="20" width="35" id="rect3947" style="fill:#fff;fill-opacity:0.39215686;stroke:none" /><path id="path3949" transform="translate(0,1007.3622)" d="m 18.78125,35.40625 c -1.53661,0.379809 -2.971465,0.991557 -4.28125,1.8125 l 15.65625,0 c -1.313005,-0.822961 -2.762825,-1.432953 -4.3125,-1.8125 l -7.0625,0 z" style="fill:#fff;fill-opacity:1;stroke:none" /></g><g id="g4007" transform="matrix(2.0353985,0,0,1.1630828,-99.321734,-141.54581)" style="fill:#000"><path id="rect3958" d="m 65.533646,1001.4758 -2.032932,0 0.662913,0.6629 -2.253903,2.2539 0.707107,0.7071 2.253903,-2.2539 0.662912,0.6629 0,-2.0329 z" style="fill:#fff;fill-opacity:1;stroke:none" /><path id="rect3958-5" d="m 65.533646,1012.84 0,-2.033 -0.662836,0.6629 -2.253901,-2.2539 -0.707104,0.7071 2.253902,2.2539 -0.662906,0.6629 2.032845,1e-4 z" style="fill:#fff;fill-opacity:1;stroke:none" /><path id="rect3958-51" d="m 54.16943,1001.4758 2.032932,0 -0.662913,0.6629 2.253903,2.2539 -0.707107,0.7071 -2.253903,-2.2539 -0.662912,0.6629 0,-2.0329 z" style="fill:#fff;fill-opacity:1;stroke:none" /><path id="rect3958-5-7" d="m 54.16943,1012.84 0,-2.033 0.662836,0.6629 2.253901,-2.2539 0.707104,0.7071 -2.253902,2.2539 0.662906,0.6629 -2.032845,1e-4 z" style="fill:#fff;fill-opacity:1;stroke:none" /></g></g></g></svg> <svg width="45" height="45" version="1.1" xmlns="http://www.w3.org/2000/svg">
<g fill="#fff">
<path d="m5.1562 8.8906c-1.4649 0-2.6562 1.1913-2.6562 2.6562v19.688c0 1.4649 1.1913 2.6562 2.6562 2.6562h34.688c1.4649 0 2.6562-1.1913 2.6562-2.6562v-19.688c0-1.4649-1.1913-2.6562-2.6562-2.6562h-34.688m-0.15625 2.5312h35v20h-35v-20"/>
<rect transform="translate(0 -1008.5)" x="5" y="1019.9" width="35" height="20" ry="0" fill-opacity=".39216"/>
<path d="m18.781 34.297c-1.5366 0.37981-2.9715 0.99156-4.2812 1.8125h15.656c-1.313-0.82296-2.7628-1.433-4.3125-1.8125h-7.0625"/>
<g>
<path d="m34.065 14.782h-4.1378l1.3493 0.77101-4.5876 2.6215 1.4392 0.82242 4.5876-2.6215s1.3493 0.77101 1.3493 0.77101v-2.3644"/>
<path d="m34.065 27.999v-2.3645s-1.3491 0.77101-1.3491 0.77101l-4.5876-2.6215-1.4392 0.82242 4.5876 2.6215-1.3493 0.77101 4.1376 1.1631e-4"/>
<path d="m10.935 14.782h4.1378l-1.3493 0.77101 4.5876 2.6215-1.4392 0.82242-4.5876-2.6215s-1.3493 0.77101-1.3493 0.77101v-2.3644"/>
<path d="m10.935 27.999v-2.3645s1.3491 0.77101 1.3491 0.77101l4.5876-2.6215s1.4392 0.82242 1.4392 0.82242l-4.5876 2.6215 1.3493 0.77101-4.1376 1.1631e-4"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -0,0 +1,3 @@
<svg width="45" height="45" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path d="m2.5 10.928v8.5898l4.9023-2.8008 9.6172 5.7832-9.6172 5.7832-4.9023-2.8008v8.5898h15.031l-4.9004-2.8008 9.8691-5.6387 9.8691 5.6387-4.9004 2.8008h15.031v-8.5898l-4.9023 2.8008-9.6172-5.7832 9.6172-5.7832 4.9023 2.8008v-8.5898h-15.033l4.9023 2.8008-9.8691 5.6387-9.8691-5.6387 4.9023-2.8008z" fill="#fff"/>
</svg>

After

Width:  |  Height:  |  Size: 402 B

61
embed/imgs/gear.svg Normal file
View file

@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="svg4659"
height="45"
width="45"
sodipodi:docname="gear.svg"
inkscape:version="0.92.2 5c3e80d, 2017-08-06">
<sodipodi:namedview
pagecolor="#4cc5a0"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1868"
inkscape:window-height="1049"
id="namedview8"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:zoom="6.675088"
inkscape:cx="62.439695"
inkscape:cy="43.399157"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="svg4659"
inkscape:snap-page="true" />
<defs
id="defs4661" />
<metadata
id="metadata4664">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<path
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:2.50001693;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="m 24.139007,3.834512 -1.477875,4.3213185 c -1.101822,0.0088 -2.274313,0.1330197 -3.204775,0.3369163 L 17.128868,4.5441522 C 15.631516,4.9971895 14.196746,5.635837 12.858006,6.4452068 l 1.34441,4.3668912 c -0.878085,0.62225 -1.683392,1.341293 -2.400733,2.14357 L 7.6056909,11.132739 c -0.9476032,1.24558 -1.7355288,2.604902 -2.3453935,4.046251 l 3.6523686,2.739277 c -0.3489484,1.021477 -0.5815406,2.079062 -0.6933641,3.15269 L 3.7661466,22.05892 c -0.00716,0.146959 -0.012582,0.293997 -0.016276,0.441084 0.00628,1.417863 0.173364,2.830439 0.4980503,4.21064 l 4.5703436,0.06998 c 0.3217092,1.027112 0.7579402,2.014774 1.3004645,2.944356 l -3.0387575,3.435896 c 0.8950189,1.282782 1.947035,2.448529 3.1315315,3.470075 l 3.735377,-2.628599 c 0.86307,0.64582 1.796153,1.192348 2.781594,1.629243 l -0.436201,4.539419 c 1.475447,0.52082 3.010282,0.854908 4.568716,0.994474 l 1.477875,-4.321319 c 0.05369,0.003 0.107408,0.0057 0.161134,0.0082 1.023951,-0.0061 2.044309,-0.121786 3.043641,-0.345054 l 2.327489,3.948594 c 1.497352,-0.453037 2.932122,-1.091685 4.270862,-1.901055 l -1.34441,-4.366891 c 0.878085,-0.622249 1.683392,-1.341292 2.400733,-2.14357 l 4.195992,1.82293 c 0.947603,-1.245581 1.735529,-2.604903 2.345394,-4.046252 l -3.652368,-2.739276 c 0.348948,-1.021478 0.58154,-2.079063 0.693363,-3.152691 l 4.453156,-0.987963 c 0.0072,-0.146984 0.01258,-0.294022 0.01628,-0.441108 -0.0062,-1.417863 -0.173364,-2.83044 -0.49805,-4.210641 l -4.570344,-0.06998 c -0.321709,-1.027116 -0.75794,-2.014777 -1.300465,-2.94436 l 3.038754,-3.435928 C 37.025006,10.556342 35.97299,9.3905941 34.788493,8.3690481 L 31.053116,10.997647 C 30.190046,10.351826 29.256963,9.8052988 28.271521,9.3684038 L 28.707723,4.8289849 C 27.232276,4.308166 25.697441,3.9740781 24.139007,3.834512 Z"
id="path4541"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccccccccccccccccccccccccccccccccccccccc" />
</svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

View file

@ -0,0 +1,3 @@
<svg width="45" height="45" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path d="m24.139 3.834-1.4785 4.3223c-1.1018 0.0088-2.2727 0.13204-3.2031 0.33594l-2.3281-3.9473c-1.4974 0.45304-2.9327 1.091-4.2715 1.9004l1.3457 4.3672c-0.87808 0.62225-1.685 1.3403-2.4023 2.1426l-4.1953-1.8223c-0.9476 1.2456-1.7358 2.6055-2.3457 4.0469l3.6523 2.7383c-0.34895 1.0215-0.58154 2.0787-0.69336 3.1523l-4.4531 0.98828c-0.00716 0.14696-0.011931 0.29432-0.015625 0.44141 0.00628 1.4179 0.17336 2.8307 0.49805 4.2109l4.5703 0.070312c0.32171 1.0271 0.75826 2.0138 1.3008 2.9434l-3.0391 3.4355c0.89502 1.2828 1.9464 2.4492 3.1309 3.4707l3.7363-2.6289c0.86307 0.64582 1.7958 1.192 2.7812 1.6289l-0.43555 4.541c1.4754 0.52082 3.0099 0.85458 4.5684 0.99414l1.4766-4.3223c0.05369 3e-3 0.10838 0.005313 0.16211 0.007812 1.024-0.0061 2.0436-0.12048 3.043-0.34375l2.3281 3.9473c1.4974-0.45304 2.9327-1.091 4.2715-1.9004l-1.3457-4.3672c0.87808-0.62225 1.685-1.3403 2.4023-2.1426l4.1953 1.8223c0.9476-1.2456 1.7358-2.6055 2.3457-4.0469l-3.6523-2.7383c0.34895-1.0215 0.58154-2.0787 0.69336-3.1523l4.4531-0.98828c0.0072-0.14698 0.011925-0.29432 0.015625-0.44141-0.0062-1.4179-0.17336-2.8307-0.49805-4.2109l-4.5703-0.070312c-0.32171-1.0271-0.75826-2.0138-1.3008-2.9434l3.0391-3.4355c-0.89502-1.2828-1.9464-2.4492-3.1309-3.4707l-3.7363 2.6289c-0.86307-0.64582-1.7958-1.192-2.7812-1.6289l0.43555-4.541c-1.4754-0.52082-3.0099-0.85457-4.5684-0.99414zm-1.6387 7.8789a10.786 10.786 0 0 1 10.787 10.787 10.786 10.786 0 0 1-10.787 10.787 10.786 10.786 0 0 1-10.787-10.787 10.786 10.786 0 0 1 10.787-10.787z" fill="#fff"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

1
embed/imgs/loading.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" height="100%" width="100%"><path d="m49.998 8.7797e-4c-0.060547 0.0018431-0.12109 0.0037961-0.18164 0.0058593-0.1251 0.0015881-0.25012 0.0061465-0.375 0.013672h-0.001954c-27.388 0.30599-49.432 22.59-49.439 49.98 0.020074 2.6488 0.25061 5.292 0.68945 7.904 3.8792-24.231 24.77-42.065 49.311-42.096v-0.0058582h0.001954c4.3638 3.0803e-4 7.9013-3.5366 7.9021-7.9002 1.474e-4 -2.0958-0.83235-4.106-2.3144-5.5879-1.482-1.482-3.492-2.3145-5.5879-2.3144-6.5007e-4 -7.9369e-8 -0.0013001-7.9369e-8 -0.001954 0" class="semiFill"/></svg>

After

Width:  |  Height:  |  Size: 580 B

View file

@ -11,8 +11,9 @@
id="svg3937" id="svg3937"
height="45" height="45"
width="45" width="45"
inkscape:version="0.91 r13725" inkscape:version="0.92.2 5c3e80d, 2017-08-06"
sodipodi:docname="loop.svg"> sodipodi:docname="loop.svg"
viewBox="0 0 45 45">
<sodipodi:namedview <sodipodi:namedview
pagecolor="#8bff39" pagecolor="#8bff39"
bordercolor="#666666" bordercolor="#666666"
@ -22,8 +23,8 @@
guidetolerance="10" guidetolerance="10"
inkscape:pageopacity="1" inkscape:pageopacity="1"
inkscape:pageshadow="2" inkscape:pageshadow="2"
inkscape:window-width="1920" inkscape:window-width="1868"
inkscape:window-height="1055" inkscape:window-height="1049"
id="namedview3591" id="namedview3591"
showgrid="false" showgrid="false"
showguides="true" showguides="true"
@ -40,37 +41,17 @@
inkscape:snap-smooth-nodes="true" inkscape:snap-smooth-nodes="true"
inkscape:snap-midpoints="true" inkscape:snap-midpoints="true"
inkscape:zoom="14.833529" inkscape:zoom="14.833529"
inkscape:cx="30.216329" inkscape:cx="21.722684"
inkscape:cy="21.135445" inkscape:cy="25.7616"
inkscape:window-x="0" inkscape:window-x="0"
inkscape:window-y="0" inkscape:window-y="0"
inkscape:window-maximized="1" inkscape:window-maximized="1"
inkscape:current-layer="svg3937"> inkscape:current-layer="svg3937"
<sodipodi:guide fit-margin-top="0"
position="0,0" fit-margin-left="0"
orientation="0,45" fit-margin-right="0"
id="guide3615" /> fit-margin-bottom="0"
<sodipodi:guide scale-x="1" />
position="45,0"
orientation="-45,0"
id="guide3617" />
<sodipodi:guide
position="45,45"
orientation="0,-45"
id="guide3619" />
<sodipodi:guide
position="0,45"
orientation="45,0"
id="guide3621" />
<sodipodi:guide
position="0,0"
orientation="-0.70710678,0.70710678"
id="guide3624" />
<sodipodi:guide
position="0,45"
orientation="0.70710678,0.70710678"
id="guide4491" />
</sodipodi:namedview>
<defs <defs
id="defs3939" /> id="defs3939" />
<metadata <metadata
@ -86,11 +67,8 @@
</rdf:RDF> </rdf:RDF>
</metadata> </metadata>
<path <path
style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.5;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" style="opacity:1;fill:none;fill-opacity:0.8584475;fill-rule:evenodd;stroke:#ffffff;stroke-width:2.50002718;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="M 0 0 L 0 45 L 45 45 L 45 0 L 0 0 z M 22.5 11.25 A 11.25 11.25 0 0 1 33.75 22.5 A 11.25 11.25 0 0 1 22.5 33.75 A 11.25 11.25 0 0 1 14.550781 30.449219 L 12.714844 32.285156 L 12.714844 25.785156 L 19.214844 25.785156 L 17.376953 27.623047 A 7.25 7.25 0 0 0 22.5 29.75 A 7.25 7.25 0 0 0 29.75 22.5 A 7.25 7.25 0 0 0 22.5 15.25 A 7.25 7.25 0 0 0 17.376953 17.376953 L 14.550781 14.550781 A 11.25 11.25 0 0 1 22.5 11.25 z " d="M 21.279283,3.749797 A 18.750203,18.750203 0 0 0 8.0304417,9.2511582 L 12.740779,13.961496 A 12.083464,12.083464 0 0 1 21.279283,10.416536 12.083464,12.083464 0 0 1 33.362748,22.5 12.083464,12.083464 0 0 1 21.279283,34.583464 12.083464,12.083464 0 0 1 12.740779,31.038504 l 3.063185,-3.063185 H 4.9705135 V 38.80877 L 8.0304417,35.748842 A 18.750203,18.750203 0 0 0 21.279283,41.250203 18.750203,18.750203 0 0 0 40.029486,22.5 18.750203,18.750203 0 0 0 21.279283,3.749797 Z"
id="rect4511" /> id="path4495"
<path inkscape:connector-curvature="0" />
style="opacity:1;fill:none;fill-opacity:0.8584475;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.5;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="M 22.5 11.25 A 11.25 11.25 0 0 0 14.550781 14.550781 L 17.376953 17.376953 A 7.25 7.25 0 0 1 22.5 15.25 A 7.25 7.25 0 0 1 29.75 22.5 A 7.25 7.25 0 0 1 22.5 29.75 A 7.25 7.25 0 0 1 17.376953 27.623047 L 19.214844 25.785156 L 12.714844 25.785156 L 12.714844 32.285156 L 14.550781 30.449219 A 11.25 11.25 0 0 0 22.5 33.75 A 11.25 11.25 0 0 0 33.75 22.5 A 11.25 11.25 0 0 0 22.5 11.25 z "
id="path4495" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View file

@ -0,0 +1,7 @@
<svg width="45" height="45" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path class="stroke semiFill toggle" d="m25.587 5.2036c-1.8324-1.1012-4.2007-0.8622-5.7719 0.77112 0 0-7.7388 8.0444-7.7388 8.0444h-3.418c-1.9537 0-3.547 1.6562-3.547 3.6869v9.5864c0 2.0309 1.5933 3.6871 3.547 3.6871h3.418s7.7388 8.0447 7.7388 8.0447c1.5721 1.634 3.9389 1.8747 5.7719 0.77076v-34.591" fill="none" stroke="#000"/>
<g fill="none" stroke="#000" stroke-linecap="round" stroke-width="1.4142">
<path d="m30.032 27.86 9.8517-9.8517"/>
<path d="m30.032 18.008 9.8517 9.8517"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 590 B

View file

@ -1 +1 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.1" id="svg2" height="45" width="45"><defs id="defs4" /><metadata id="metadata7"><rdf:RDF><cc:Work rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><g transform="translate(0,-1007.3622)" id="layer1"><g style="fill:#fff" transform="translate(3.0304575,47.729705)" id="g3779"><path id="path3823-7" d="m 4.4695429,998.16377 a 4.0011916,4.0011916 0 0 0 3.749999,3.96873 l 2.2812501,0 a 4.0011916,4.0011916 0 0 0 3.96875,-3.75003 l 0,-32.28123 a 4.0011916,4.0011916 0 0 0 -3.75,-3.96875 l -2.2812501,0 a 4.0011916,4.0011916 0 0 0 -3.968749,3.75 l 0,32.28128 z" style="fill:#fff;fill-opacity:1;stroke:none" /><path id="path3823-7-4" d="m 24.469542,998.1638 a 4.0011916,4.0011916 0 0 0 3.75,3.9687 l 2.28125,0 a 4.0011916,4.0011916 0 0 0 3.96875,-3.75 l 0,-32.28126 a 4.0011916,4.0011916 0 0 0 -3.75,-3.96875 l -2.28125,0 a 4.0011916,4.0011916 0 0 0 -3.96875,3.75 l 0,32.28131 z" style="fill:#fff;fill-opacity:1;stroke:none" /></g></g></svg> <svg width="25" height="25" version="1.1" viewBox="0 0 25 25" xmlns="http://www.w3.org/2000/svg"><g transform="matrix(.55556 0 0 .55556 0 -559.65)"><g transform="translate(3.0305 47.73)" fill="#fff"><path d="m4.4695 998.16a4.0012 4.0012 0 0 0 3.75 3.9687h2.2813a4.0012 4.0012 0 0 0 3.9688-3.75v-32.281a4.0012 4.0012 0 0 0-3.75-3.9688h-2.2813a4.0012 4.0012 0 0 0-3.9687 3.75z"/><path d="m24.47 998.16a4.0012 4.0012 0 0 0 3.75 3.9687h2.2812a4.0012 4.0012 0 0 0 3.9688-3.75v-32.281a4.0012 4.0012 0 0 0-3.75-3.9688h-2.2812a4.0012 4.0012 0 0 0-3.9688 3.75z"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 569 B

View file

@ -13,10 +13,8 @@
height="45" height="45"
id="svg2" id="svg2"
version="1.1" version="1.1"
inkscape:version="0.48.4 r9939" inkscape:version="0.92.2 5c3e80d, 2017-08-06"
sodipodi:docname="New document 1"> sodipodi:docname="play.svg">
<defs
id="defs4" />
<sodipodi:namedview <sodipodi:namedview
id="base" id="base"
pagecolor="#000000" pagecolor="#000000"
@ -39,28 +37,34 @@
inkscape:snap-page="true" inkscape:snap-page="true"
inkscape:object-nodes="true" inkscape:object-nodes="true"
inkscape:snap-nodes="false" inkscape:snap-nodes="false"
inkscape:window-width="981" inkscape:window-width="1868"
inkscape:window-height="709" inkscape:window-height="1049"
inkscape:window-x="856" inkscape:window-x="0"
inkscape:window-y="240" inkscape:window-y="0"
inkscape:window-maximized="0"> inkscape:window-maximized="0">
<sodipodi:guide <sodipodi:guide
position="0,0" inkscape:locked="false"
id="guide3785"
orientation="0,45" orientation="0,45"
id="guide3785" /> position="0,0" />
<sodipodi:guide <sodipodi:guide
position="45,0" inkscape:locked="false"
id="guide3787"
orientation="-45,0" orientation="-45,0"
id="guide3787" /> position="45,0" />
<sodipodi:guide <sodipodi:guide
position="45,45" inkscape:locked="false"
id="guide3789"
orientation="0,-45" orientation="0,-45"
id="guide3789" /> position="45,45" />
<sodipodi:guide <sodipodi:guide
position="0,45" inkscape:locked="false"
id="guide3791"
orientation="45,0" orientation="45,0"
id="guide3791" /> position="0,45" />
</sodipodi:namedview> </sodipodi:namedview>
<defs
id="defs4" />
<metadata <metadata
id="metadata7"> id="metadata7">
<rdf:RDF> <rdf:RDF>
@ -69,22 +73,17 @@
<dc:format>image/svg+xml</dc:format> <dc:format>image/svg+xml</dc:format>
<dc:type <dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title> <dc:title />
</cc:Work> </cc:Work>
</rdf:RDF> </rdf:RDF>
</metadata> </metadata>
<g <g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1" id="layer1"
transform="translate(0,-1007.3622)"> inkscape:groupmode="layer"
inkscape:label="Layer 1">
<path <path
sodipodi:type="inkscape:offset" d="M6.26004984594 3.0550109625C5.27445051914 3.68940862462 4.67905105702 4.78142391497 4.67968264562 5.95354422781C4.67968264562 5.95354422781 4.70004942312 39.0717540916 4.70004942312 39.0717540916C4.70302341604 40.3033886636 5.36331656075 41.439734231 6.43188211452 42.0521884912C7.50044766829 42.6646427515 8.81469531629 42.6600161659 9.87892235656 42.0400537716C9.87892235656 42.0400537716 38.5612768409 25.4802882606 38.5612768409 25.4802882606C39.6181165777 24.8606067582 40.2663250096 23.7262617523 40.2636734301 22.5011460995C40.2610218505 21.2760304467 39.6079092743 20.1445019555 38.5483970356 19.5294009803C38.5483970356 19.5294009803 9.84567577375 2.9709566275 9.84567577375 2.9709566275C8.72898008118 2.32550764609 7.34527425735 2.35794451351 6.26004984594 3.0550109625C6.26004984594 3.0550109625 6.26004984594 3.0550109625 6.26004984594 3.0550109625"
inkscape:radius="0"
inkscape:original="M 10.3125 -6.34375 A 2.9416186 2.9416186 0 0 0 7.90625 -4.875 L -6.21875 19.625 A 2.9416186 2.9416186 0 0 0 -3.65625 24.03125 L 24.625 24.03125 A 2.9416186 2.9416186 0 0 0 27.15625 19.625 L 13 -4.875 A 2.9416186 2.9416186 0 0 0 10.3125 -6.34375 z "
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none"
id="path3809" id="path3809"
d="M 10.3125,-6.34375 A 2.9416186,2.9416186 0 0 0 7.90625,-4.875 l -14.125,24.5 a 2.9416186,2.9416186 0 0 0 2.5625,4.40625 l 28.28125,0 A 2.9416186,2.9416186 0 0 0 27.15625,19.625 L 13,-4.875 a 2.9416186,2.9416186 0 0 0 -2.6875,-1.46875 z" style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none" />
transform="matrix(1.0141827,-0.58553867,0.58553867,1.0141827,-0.48419831,1022.8893)" />
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

3
embed/imgs/popout.svg Normal file
View file

@ -0,0 +1,3 @@
<svg width="45" height="45" version="1.1" viewBox="0 0 45 45" xmlns="http://www.w3.org/2000/svg">
<path d="m24.721 11.075c-12.96 0.049575-32.113 15.432-10.336 28.834-7.6763-7.9825-2.4795-21.824 10.336-22.19v5.5368l15.276-8.862-15.276-8.86v5.5419" fill="none" stroke="#000" stroke-width="1.4142"/>
</svg>

After

Width:  |  Height:  |  Size: 306 B

View file

@ -1,6 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg <svg
xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#" xmlns:cc="http://creativecommons.org/ns#"
@ -9,49 +7,38 @@
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="25"
height="25"
id="svg4659"
version="1.1" version="1.1"
inkscape:version="0.91 r13725" id="svg4659"
sodipodi:docname="speaker.svg"> height="45"
<defs width="45"
id="defs4661" /> sodipodi:docname="speaker.svg"
inkscape:version="0.92.2 5c3e80d, 2017-08-06">
<sodipodi:namedview <sodipodi:namedview
id="base" pagecolor="#4cc5a0"
pagecolor="#de43da"
bordercolor="#666666" bordercolor="#666666"
borderopacity="1.0" borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0" inkscape:pageopacity="0"
inkscape:pageshadow="2" inkscape:pageshadow="2"
inkscape:zoom="19.289873" inkscape:window-width="1868"
inkscape:cx="-2.0603844" inkscape:window-height="1049"
inkscape:cy="14.079113" id="namedview8"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false" showgrid="false"
inkscape:window-width="1918" fit-margin-top="0"
inkscape:window-height="1040" fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:zoom="13.350176"
inkscape:cx="0.054475841"
inkscape:cy="20.133193"
inkscape:window-x="0" inkscape:window-x="0"
inkscape:window-y="19" inkscape:window-y="0"
inkscape:window-maximized="0"> inkscape:window-maximized="0"
<sodipodi:guide inkscape:current-layer="svg4659" />
position="0,0" <defs
orientation="0,25" id="defs4661" />
id="guide4151" />
<sodipodi:guide
position="25,0"
orientation="-25,0"
id="guide4153" />
<sodipodi:guide
position="25,25"
orientation="0,-25"
id="guide4155" />
<sodipodi:guide
position="0,25"
orientation="25,0"
id="guide4157" />
</sodipodi:namedview>
<metadata <metadata
id="metadata4664"> id="metadata4664">
<rdf:RDF> <rdf:RDF>
@ -65,19 +52,18 @@
</rdf:RDF> </rdf:RDF>
</metadata> </metadata>
<g <g
inkscape:label="Layer 1" transform="matrix(1.8,0,0,1.8,0,-1849.252)"
inkscape:groupmode="layer" id="layer1">
id="layer1"
transform="translate(0,-1027.3622)">
<path <path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" id="rect4139"
d="M 0 0 L 0 25 L 25 25 L 25 0 L 0 0 z M 16.955078 2.5175781 C 17.381507 2.5360023 17.80574 2.6612 18.1875 2.890625 L 18.1875 22.109375 C 17.169206 22.722675 15.85386 22.587487 14.980469 21.679688 L 10.681641 17.210938 L 8.7832031 17.210938 C 7.6978331 17.210938 6.8125 16.290409 6.8125 15.162109 L 6.8125 9.8359375 C 6.8125 8.7077375 7.6978331 7.7890625 8.7832031 7.7890625 L 10.681641 7.7890625 L 14.980469 3.3183594 C 15.526019 2.7512344 16.244363 2.4868711 16.955078 2.5175781 z "
transform="translate(0,1027.3622)" transform="translate(0,1027.3622)"
id="rect4139" /> d="M 0,0 V 25 H 25 V 0 Z M 16.955078,2.5175781 C 17.381507,2.5360023 17.80574,2.6612 18.1875,2.890625 v 19.21875 c -1.018294,0.6133 -2.33364,0.478112 -3.207031,-0.429687 l -4.298828,-4.46875 H 8.7832031 c -1.08537,0 -1.9707031,-0.920529 -1.9707031,-2.048829 V 9.8359375 c 0,-1.1282 0.8853331,-2.046875 1.9707031,-2.046875 h 1.8984379 l 4.298828,-4.4707031 c 0.54555,-0.567125 1.263894,-0.8314883 1.974609,-0.8007813 z"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
inkscape:connector-curvature="0" />
<path <path
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:1.5;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 18.187674,1030.2531 c -1.018026,-0.6118 -2.333715,-0.479 -3.206595,0.4284 l -4.299344,4.4691 -1.8988757,0 c -1.0853708,0 -1.9705331,0.9201 -1.9705331,2.0483 l 0,5.3258 c 0,1.1283 0.8851623,2.0484 1.9705331,2.0484 l 1.8988757,0 4.299344,4.4693 c 0.873391,0.9078 2.188301,1.0415 3.206595,0.4282 l 0,-19.2175 z"
id="rect4574" id="rect4574"
d="m 18.187674,1030.2531 c -1.018026,-0.6118 -2.333715,-0.479 -3.206595,0.4284 l -4.299344,4.4691 H 8.7828593 c -1.0853708,0 -1.9705331,0.9201 -1.9705331,2.0483 v 5.3258 c 0,1.1283 0.8851623,2.0484 1.9705331,2.0484 h 1.8988757 l 4.299344,4.4693 c 0.873391,0.9078 2.188301,1.0415 3.206595,0.4282 z"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:1.5;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0" /> inkscape:connector-curvature="0" />
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

59
embed/imgs/speaker_.svg Normal file
View file

@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="svg4659"
height="45"
width="45"
sodipodi:docname="speaker_.svg"
inkscape:version="0.92.2 5c3e80d, 2017-08-06">
<sodipodi:namedview
pagecolor="#4cc5a0"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1868"
inkscape:window-height="1049"
id="namedview8"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:zoom="6.675088"
inkscape:cx="-34.245466"
inkscape:cy="20.740429"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="svg4659" />
<defs
id="defs4661" />
<metadata
id="metadata4664">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<path
inkscape:connector-curvature="0"
style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:3.81837654;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 32.737813,5.2037363 c -1.832447,-1.10124 -4.200687,-0.8622 -5.771871,0.77112 0,0 -7.738819,8.0443797 -7.738819,8.0443797 0,0 -3.417976,0 -3.417976,0 -1.953668,0 -3.54696,1.65618 -3.54696,3.68694 0,0 0,9.58644 0,9.58644 0,2.03094 1.593292,3.68712 3.54696,3.68712 0,0 3.417976,0 3.417976,0 0,0 7.738819,8.04474 7.738819,8.04474 1.572104,1.63404 3.938942,1.8747 5.771871,0.77076 0,0 0,-34.5914997 0,-34.5914997 z"
id="rect4574-3" />
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View file

@ -1 +1 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.1" id="svg4659" height="25" width="25"> <defs id="defs4661" /> <metadata id="metadata4664"> <rdf:RDF> <cc:Work rdf:about=""> <dc:format>image/svg+xml</dc:format> <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> <dc:title></dc:title> </cc:Work> </rdf:RDF> </metadata> <g transform="translate(0,-1027.3622)" id="layer1"> <path id="rect4139" transform="translate(0,1027.3622)" d="M 0 0 L 0 25 L 25 25 L 25 0 L 0 0 z M 16.955078 2.5175781 C 17.381507 2.5360023 17.80574 2.6612 18.1875 2.890625 L 18.1875 22.109375 C 17.169206 22.722675 15.85386 22.587487 14.980469 21.679688 L 10.681641 17.210938 L 8.7832031 17.210938 C 7.6978331 17.210938 6.8125 16.290409 6.8125 15.162109 L 6.8125 9.8359375 C 6.8125 8.7077375 7.6978331 7.7890625 8.7832031 7.7890625 L 10.681641 7.7890625 L 14.980469 3.3183594 C 15.526019 2.7512344 16.244363 2.4868711 16.955078 2.5175781 z " style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" /> <path id="rect4574" d="m 18.187674,1030.2531 c -1.018026,-0.6118 -2.333715,-0.479 -3.206595,0.4284 l -4.299344,4.4691 -1.8988757,0 c -1.0853708,0 -1.9705331,0.9201 -1.9705331,2.0483 l 0,5.3258 c 0,1.1283 0.8851623,2.0484 1.9705331,2.0484 l 1.8988757,0 4.299344,4.4693 c 0.873391,0.9078 2.188301,1.0415 3.206595,0.4282 l 0,-19.2175 z" style="fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:1.5;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> </g></svg> <?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.1" id="svg4659" height="25" width="25"> <defs id="defs4661" /> <metadata id="metadata4664"> <rdf:RDF> <cc:Work rdf:about=""> <dc:format>image/svg+xml</dc:format> <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> <dc:title></dc:title> </cc:Work> </rdf:RDF> </metadata> <path d="m 32.737813,5.2037363 c -1.832447,-1.10124 -4.200687,-0.8622 -5.771871,0.77112 0,0 -7.738819,8.0443797 -7.738819,8.0443797 0,0 -3.417976,0 -3.417976,0 -1.953668,0 -3.54696,1.65618 -3.54696,3.68694 0,0 0,9.58644 0,9.58644 0,2.03094 1.593292,3.68712 3.54696,3.68712 0,0 3.417976,0 3.417976,0 0,0 7.738819,8.04474 7.738819,8.04474 1.572104,1.63404 3.938942,1.8747 5.771871,0.77076 0,0 0,-34.5914997 0,-34.5914997 z" class="stroke semiFill toggle"></path></svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 1 KiB

View file

@ -0,0 +1,4 @@
<svg width="45" height="45" version="1.1" viewBox="0 0 45 45" xmlns="http://www.w3.org/2000/svg">
<path d="m8.4925 18.786c-3.9578 1.504-6.4432 3.632-6.4434 5.9982 2.183e-4 4.1354 7.5562 7.5509 17.399 8.1467v4.7777l10.718-6.2573-10.718-6.2529v4.5717c-6.9764-0.4712-12.229-2.5226-12.227-4.9859 6.693e-4 -0.72127 0.45868-1.4051 1.2714-2.0267zm28.015 0v3.9715c0.81164 0.62126 1.2685 1.3059 1.2692 2.0267-0.0014 1.4217-1.791 2.75-4.8021 3.6968-2.0515 0.82484-0.93693 3.7696 1.2249 2.9659 5.3088-1.8593 8.7426-3.8616 8.7514-6.6627-1.26e-4 -2.3662-2.4856-4.4942-6.4434-5.9982z"/>
<rect x="10.166" y="7.7911" width="24.668" height="15.432" fill="none" stroke="#000" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 694 B

19
embed/imgs/timeout.svg Normal file
View file

@ -0,0 +1,19 @@
<svg width="25" height="25" version="1.1" viewBox="0 0 25 25" xmlns="http://www.w3.org/2000/svg">
<defs>
<mask id="a">
<rect x="0" y="0" width="25" height="25" fill="#fff"/>
<rect x="-5" y="-5" width="17.5" height="35" fill="#000" transform="rotate(180,12.5,12.5)">
<animateTransform attributeName="transform" type="rotate" from="0,12.5,12.5" to="180,12.5,12.5" dur="5" repeatCount="1"/>
</rect>
<rect x="0" y="0" width="12.5" height="25" fill="#fff"/>
<rect x="-5" y="-5" width="17.5" height="35" fill="#000" transform="rotate(360,12.5,12.5)">
<animate attributeType="CSS" attributeName="opacity" from="0" to="1" dur="10" calcMode="discrete" repeatCount="1" />
<animateTransform attributeName="transform" type="rotate" from="180,12.5,12.5" to="360,12.5,12.5" begin="5" dur="5" repeatCount="1"/>
</rect>
<circle cx="12.5" cy="12.5" r="8" fill="#000"/>
</mask>
</defs>
<g mask="url(#a)">
<circle cx="12.5" cy="12.5" r="12.5" class="fill"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1 KiB

View file

@ -9,14 +9,25 @@
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="30" width="65.000015"
height="65" height="30"
id="svg3937" id="svg3937"
version="1.1" version="1.1"
inkscape:version="0.91 r13725" inkscape:version="0.92.2 5c3e80d, 2017-08-06"
sodipodi:docname="volume.svg"> sodipodi:docname="volume.svg">
<defs <defs
id="defs3939" /> id="defs3939">
<mask
maskUnits="userSpaceOnUse"
id="mask4516">
<path
inkscape:connector-curvature="0"
id="path4518"
d="m -14.830002,1027.1553 58.614316,-18.8626 c 1.795162,-0.5777 3.231854,0.5139 3.231854,2.1982 v 19.0111 c 0,1.3165 -1.098401,2.3764 -2.462782,2.3764 h -59.106775 c -2.946621,0 -3.449835,-3.6727 -0.276613,-4.7231 z"
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.96767992;stroke-opacity:1"
sodipodi:nodetypes="cccsscc" />
</mask>
</defs>
<sodipodi:namedview <sodipodi:namedview
id="base" id="base"
pagecolor="#dc39f7" pagecolor="#dc39f7"
@ -24,16 +35,16 @@
borderopacity="1.0" borderopacity="1.0"
inkscape:pageopacity="0" inkscape:pageopacity="0"
inkscape:pageshadow="2" inkscape:pageshadow="2"
inkscape:zoom="4" inkscape:zoom="14.580651"
inkscape:cx="-36.653402" inkscape:cx="30.208524"
inkscape:cy="38.029112" inkscape:cy="17.533448"
inkscape:document-units="px" inkscape:document-units="px"
inkscape:current-layer="layer1" inkscape:current-layer="layer1"
showgrid="false" showgrid="false"
inkscape:snap-bbox="true" inkscape:snap-bbox="true"
inkscape:bbox-paths="false" inkscape:bbox-paths="false"
inkscape:window-width="1920" inkscape:window-width="1868"
inkscape:window-height="1055" inkscape:window-height="1049"
inkscape:window-x="0" inkscape:window-x="0"
inkscape:window-y="0" inkscape:window-y="0"
inkscape:window-maximized="1" inkscape:window-maximized="1"
@ -41,7 +52,13 @@
inkscape:object-paths="true" inkscape:object-paths="true"
inkscape:snap-page="true" inkscape:snap-page="true"
inkscape:snap-global="true" inkscape:snap-global="true"
borderlayer="true" /> borderlayer="true"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
showguides="true"
inkscape:guide-bbox="true" />
<metadata <metadata
id="metadata3942"> id="metadata3942">
<rdf:RDF> <rdf:RDF>
@ -50,7 +67,7 @@
<dc:format>image/svg+xml</dc:format> <dc:format>image/svg+xml</dc:format>
<dc:type <dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title /> <dc:title></dc:title>
</cc:Work> </cc:Work>
</rdf:RDF> </rdf:RDF>
</metadata> </metadata>
@ -58,16 +75,20 @@
inkscape:label="Layer 1" inkscape:label="Layer 1"
inkscape:groupmode="layer" inkscape:groupmode="layer"
id="layer1" id="layer1"
transform="translate(0,-987.3622)"> transform="translate(17.50001,-1004.8622)">
<path <path
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;marker:none;enable-background:accumulate" inkscape:connector-curvature="0"
d="m 0,1052.3622 0,-65 5,0 c -0.172588,0 -0.337256,0.0295 -0.5,0.0625 -0.489111,0.1 -0.942345,0.31725 -1.28125,0.65625 -0.226206,0.2262 -0.404743,0.5135 -0.53125,0.8125 -0.126507,0.2991 -0.1875,0.62365 -0.1875,0.96875 0,0 0.075978,0.44705 0.1875,0.78125 l 19.84375,59.49995 c 0.142777,1.2451 1.185414,2.2188 2.46875,2.2188 l -25,0 z m 25,0 c 0.877549,0 1.647663,-0.441 2.09375,-1.125 0.06381,-0.098 0.1062,-0.2046 0.15625,-0.3125 0.02962,-0.062 0.06926,-0.1225 0.09375,-0.1875 0.04501,-0.1212 0.06741,-0.2459 0.09375,-0.375 0.009,-0.044 0.02457,-0.08 0.03125,-0.125 0.01878,-0.1235 0.03125,-0.2462 0.03125,-0.375 l 0,-60 c 0,-1.385 -1.114999,-2.5 -2.5,-2.5 l 5,0 0,65 -5,0 z" id="path4505"
id="rect4674" d="m -14.830002,1027.1553 58.614316,-18.8626 c 1.795162,-0.5777 3.231854,0.5139 3.231854,2.1982 v 19.0111 c 0,1.3165 -1.098401,2.3764 -2.462782,2.3764 h -59.106775 c -2.946621,0 -3.449835,-3.6727 -0.276613,-4.7231 z"
inkscape:connector-curvature="0" /> style="fill:none;fill-opacity:1;stroke:#fdfdfd;stroke-width:0.96767992;stroke-opacity:1"
<path sodipodi:nodetypes="cccsscc" />
style="fill:#ffffff;fill-opacity:1;stroke:none" <rect
d="m 25,1052.3617 c -1.283336,0 -2.325973,-0.9737 -2.46875,-2.2187 L 2.6875,990.6429 C 2.575978,990.3087 2.5,989.8617 2.5,989.8617 c 0,-0.3451 0.060993,-0.6697 0.1875,-0.9688 0.126507,-0.299 0.305044,-0.5863 0.53125,-0.8125 0.338905,-0.339 0.792139,-0.5562 1.28125,-0.6562 0.162744,-0.033 0.327412,-0.062 0.5,-0.062 l 20,0 c 1.385001,0 2.5,1.115 2.5,2.5 l 0,60 c 0,0.1288 -0.01247,0.2515 -0.03125,0.375 -0.0067,0.045 -0.02225,0.081 -0.03125,0.125 -0.02634,0.1292 -0.04874,0.2538 -0.09375,0.375 -0.02449,0.065 -0.06413,0.1252 -0.09375,0.1875 -0.05005,0.1079 -0.09244,0.2145 -0.15625,0.3125 -0.446087,0.684 -1.216201,1.125 -2.09375,1.125 z m 0,-1.2187 c 0.474106,0 0.864734,-0.2114 1.09375,-0.5625 -0.02112,0.032 -0.0059,-0.01 0.0625,-0.1563 a 1.204452,1.204452 0 0 1 0,-0.031 c 0.0235,-0.049 0.05198,-0.052 0.0625,-0.062 0.0055,-0.016 0.0094,-0.035 0,-0.031 0.0017,-0.01 0.0055,-0.061 0.03125,-0.1875 0.008,-0.039 0.02555,-0.039 0.03125,-0.062 0.0098,-0.066 0.0055,-0.1027 0,-0.094 -0.0016,-0.03 0,-0.068 0,-0.094 l 0,-60 c 0,-0.7386 -0.542617,-1.2813 -1.28125,-1.2813 l -20,0 c -0.035353,0 -0.105322,0 -0.25,0.031 -0.296863,0.061 -0.546343,0.1713 -0.6875,0.3125 -0.089394,0.089 -0.205263,0.258 -0.28125,0.4375 -0.055315,0.1308 -0.058661,0.2832 -0.0625,0.4687 -2.52e-4,0.012 0,0.019 0,0.031 0.027982,0.1353 0.082499,0.2789 0.125,0.4062 l 19.84375,59.5005 a 1.204452,1.204452 0 0 1 0.03125,0.25 c 0.07527,0.6564 0.607054,1.125 1.28125,1.125 z" style="opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:0.60986549;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="path4697-6" id="rect4507"
inkscape:connector-curvature="0" /> width="33.400429"
height="30"
x="-17.50001"
y="1004.8622"
mask="url(#mask4516)" />
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View file

@ -1,38 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg width="100" height="45" version="1.1" xmlns="http://www.w3.org/2000/svg">
<svg <defs>
xmlns:dc="http://purl.org/dc/elements/1.1/" <mask id="a" maskUnits="userSpaceOnUse">
xmlns:cc="http://creativecommons.org/ns#" <rect width="57.288" height="30" fill="#fff" fill-opacity=".74439"/>
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" </mask>
xmlns:svg="http://www.w3.org/2000/svg" </defs>
xmlns="http://www.w3.org/2000/svg" <path d="m6.202 33.254 86.029-28.394c2.6348-0.86966 4.7433 0.77359 4.7433 3.3092v28.617c0 1.9819-1.6122 3.5773-3.6147 3.5773h-86.75c-4.3249 0-5.0634-5.5287-0.40598-7.1098" fill="none" stroke="#fdfdfd" stroke-width="6.0417"/>
version="1.1" <g transform="translate(16.609 9.9991)" fill="#fff" mask="url(#a)">
id="svg3937" <path d="m4.2605 21.935 55.47-18.308c1.6989-0.56074 3.0584 0.4988 3.0584 2.1337v18.452c0 1.2779-1.0395 2.3066-2.3307 2.3066h-55.935c-2.7886 0-3.2648-3.5648-0.26177-4.5843" fill="#fff"/>
height="65"
width="30">
<defs
id="defs3939" />
<metadata
id="metadata3942">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
transform="translate(0,-987.3622)"
id="layer1">
<path
id="rect4674"
d="m 0,1052.3622 0,-65 5,0 c -0.172588,0 -0.337256,0.0295 -0.5,0.0625 -0.489111,0.1 -0.942345,0.31725 -1.28125,0.65625 -0.226206,0.2262 -0.404743,0.5135 -0.53125,0.8125 -0.126507,0.2991 -0.1875,0.62365 -0.1875,0.96875 0,0 0.075978,0.44705 0.1875,0.78125 l 19.84375,59.49995 c 0.142777,1.2451 1.185414,2.2188 2.46875,2.2188 l -25,0 z m 25,0 c 0.877549,0 1.647663,-0.441 2.09375,-1.125 0.06381,-0.098 0.1062,-0.2046 0.15625,-0.3125 0.02962,-0.062 0.06926,-0.1225 0.09375,-0.1875 0.04501,-0.1212 0.06741,-0.2459 0.09375,-0.375 0.009,-0.044 0.02457,-0.08 0.03125,-0.125 0.01878,-0.1235 0.03125,-0.2462 0.03125,-0.375 l 0,-60 c 0,-1.385 -1.114999,-2.5 -2.5,-2.5 l 5,0 0,65 -5,0 z"
style="color:#000000;display:inline;overflow:visible;visibility:visible;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;marker:none;enable-background:accumulate" />
<path
id="path4697-6"
d="m 25,1052.3617 c -1.283336,0 -2.325973,-0.9737 -2.46875,-2.2187 L 2.6875,990.6429 C 2.575978,990.3087 2.5,989.8617 2.5,989.8617 c 0,-0.3451 0.060993,-0.6697 0.1875,-0.9688 0.126507,-0.299 0.305044,-0.5863 0.53125,-0.8125 0.338905,-0.339 0.792139,-0.5562 1.28125,-0.6562 0.162744,-0.033 0.327412,-0.062 0.5,-0.062 l 20,0 c 1.385001,0 2.5,1.115 2.5,2.5 l 0,60 c 0,0.1288 -0.01247,0.2515 -0.03125,0.375 -0.0067,0.045 -0.02225,0.081 -0.03125,0.125 -0.02634,0.1292 -0.04874,0.2538 -0.09375,0.375 -0.02449,0.065 -0.06413,0.1252 -0.09375,0.1875 -0.05005,0.1079 -0.09244,0.2145 -0.15625,0.3125 -0.446087,0.684 -1.216201,1.125 -2.09375,1.125 z m 0,-1.2187 c 0.474106,0 0.864734,-0.2114 1.09375,-0.5625 -0.02112,0.032 -0.0059,-0.01 0.0625,-0.1563 a 1.204452,1.204452 0 0 1 0,-0.031 c 0.0235,-0.049 0.05198,-0.052 0.0625,-0.062 0.0055,-0.016 0.0094,-0.035 0,-0.031 0.0017,-0.01 0.0055,-0.061 0.03125,-0.1875 0.008,-0.039 0.02555,-0.039 0.03125,-0.062 0.0098,-0.066 0.0055,-0.1027 0,-0.094 -0.0016,-0.03 0,-0.068 0,-0.094 l 0,-60 c 0,-0.7386 -0.542617,-1.2813 -1.28125,-1.2813 l -20,0 c -0.035353,0 -0.105322,0 -0.25,0.031 -0.296863,0.061 -0.546343,0.1713 -0.6875,0.3125 -0.089394,0.089 -0.205263,0.258 -0.28125,0.4375 -0.055315,0.1308 -0.058661,0.2832 -0.0625,0.4687 -2.52e-4,0.012 0,0.019 0,0.031 0.027982,0.1353 0.082499,0.2789 0.125,0.4062 l 19.84375,59.5005 a 1.204452,1.204452 0 0 1 0.03125,0.25 c 0.07527,0.6564 0.607054,1.125 1.28125,1.125 z"
style="fill:#ffffff;fill-opacity:1;stroke:none" />
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 731 B

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="100" height="50" version="1.1" viewBox="0 0 100 50" xmlns="http://www.w3.org/2000/svg">
<rect y="21" width="100" height="8" fill="#fff"/>
<ellipse cx="50" cy="25" rx="10" ry="10" fill="#008000"/>
<rect y="21" width="50" height="8" fill="#008000"/>
</svg>

After

Width:  |  Height:  |  Size: 312 B

1
embed/min/player.js Normal file

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,92 @@
.mistvideo{display:inline-block;position:relative;color:$stroke;font-family:sans-serif;background-color:#000;justify-content:center;align-items:center}
.mistvideo-controls{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}
.mistvideo.novideo{overflow:visible}
.mistvideo-video{overflow:hidden}
svg.icon.loading{z-index:-1;position:absolute;top:0;left:0;right:0;bottom:0;margin:auto;opacity:0}
[data-loading]{position:relative}
[data-loading=stalled] svg.icon.loading{transition:opacity 0s 3s}
[data-loading] svg.icon.loading{z-index:2;opacity:1}
[data-loading-css] .mistvideo-controls{display:none}
[data-hidecursor],[data-hidecursor] *,[data-hidecursor] .mistvideo-pointer{cursor:none}
.mistvideo-error{display:none;position:absolute;top:0;left:0;right:0;bottom:0;background-color:$background;align-items:center;justify-content:center;text-align:center;z-index:2;cursor:default;min-height:fit-content;min-width:fit-content}
.mistvideo-error.show{display:flex}
.mistvideo-error .message{max-width:80%}
.mistvideo-error .message .details table{text-align:left}
.mistvideo-controls button,.mistvideo-error button,.mistvideo-video:not(.video-js) button{color:$stroke;border:1px solid $semiFill;background-color:$background;margin:.25em;padding:.5em 1em;opacity:.5;cursor:pointer}
button:hover{opacity:1}
select{background-color:transparent;color:$stroke;border:none;margin:0 .5em;font-size:inherit;cursor:pointer;-ms-background-color:red}
select>option{background-color:$background}
.browser-edge select,.browser-safari select{border:1px solid $semiFill;border-top:none;border-left:none;margin-top:2px}
@keyframes spin{
0%{transform:rotate(0)}
100%{transform:rotate(360deg)}
}
[data-fullscreen]{position:fixed;top:0;left:0;right:0;bottom:0;width:100%!important;height:100%!important}
video{display:block;flex-shrink:0}
table{color:inherit;font-size:inherit;font-style:inherit}
audio:not([controls]){display:block!important}
.mistvideo-padding{padding:5px 10px}
.mistvideo-pointer{cursor:pointer}
.description{color:$semiFill;font-size:.9em}
.mistvideo-container{display:flex;flex-wrap:nowrap}
.mistvideo-container.mistvideo{display:inline-flex;max-width:100%}
.mistvideo-container.mistvideo-row{flex-direction:row}
.mistvideo-container.mistvideo-column{flex-direction:column}
.mistvideo-container.mistvideo-center{justify-content:center}
.mistvideo-align-right{margin-left:auto;margin-right:0}
.hover_window_container.pos>.outer_window{position:absolute;pointer-events:none;overflow:hidden}
.hover_window_container.pos>.outer_window>.inner_window{position:absolute;pointer-events:all}
.hover_window_container:not(:hover)>.outer_window>.inner_window,.hover_window_container>.outer_window[data-hidecursor]>.inner_window{transition:all .5s ease-in .5s}
.outer_window>.inner_window{flex-grow:1}
.hover_window_container>.outer_window>.inner_window>*{flex-shrink:0}
.mistvideo-draggable{overflow:hidden}
.mistvideo-tooltip{position:absolute;background-color:$background;padding:.5em 1em;z-index:2}
:not(:hover)>.mistvideo-tooltip{transition:opacity .25s ease-in}
.mistvideo-tooltip .triangle{border:10px solid $background;position:absolute}
.mistvideo-tracks label{display:block}
.mistvideo-tracks label>span{margin-right:1em}
a{color:$accent}
.mistvideo-log .logs{-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}
.mistvideo-placeholder{max-width:100%;max-height:100%}
.mistvideo-topright{position:absolute;top:0;right:0}
.mistvideo-topleft{position:absolute;top:0;left:0}
.mistvideo-delay-display{animation:appear 1s;animation-iteration-count:1;animation-timing-function:steps(1,end)}
@keyframes appear{
from{opacity:0}
to{opacity:1}
}
svg.icon{display:block;stroke-width:$strokeWidth;fill:none;stroke:none}
svg.icon .fill,svg.icon.fill{fill:$fill}
svg.icon .semiFill,svg.icon.semiFill{fill:$semiFill}
svg.icon .stroke,svg.icon.stroke{stroke:$stroke;vector-effect:non-scaling-stroke}
svg.icon.off .toggle .fill,svg.icon.off .toggle .semiFill,svg.icon.off .toggle.fill,svg.icon.off .toggle.semiFill{fill:none}
svg.icon .spin,svg.icon.spin{animation:spin 1.5s infinite linear;transform-origin:50% 50%}
.mistvideo{line-height:1.2;font-size:14.5px}
.mistvideo svg{margin:2.5px}
.mistvideo-background{background-color:$background}
.mistvideo-totalTime:before{content:'/';margin:.2em}
.mistvideo-progress{padding:10px 0;margin:-10px 0;z-index:2}
.mistvideo-progress>*{height:2px;background-color:$progressBackground;opacity:.95;position:relative}
.mistvideo-novideo .mistvideo-progress>*,.mistvideo-progress:hover>*{height:10px}
.mistvideo-progress:not(:hover)>*{transition:height .25s ease-in .5s}
.mistvideo-progress .bar{height:inherit;width:0;position:absolute;border-right:inherit;background-color:$accent;z-index:2}
.mistvideo-progress .buffer{height:inherit;width:0;position:absolute;background-color:$semiFill}
.mistvideo-progress .bar:after{content:'';border:5px solid $accent;border-radius:5px;position:absolute;right:-5px;top:50%;transform:translateY(-50%)}
.mistvideo-play[data-state=playing] svg.play{display:none}
.mistvideo-play[data-state=paused] svg.pause{display:none}
.mistvideo-main{align-items:center}
svg.icon.timeout{display:inline-block;height:1em;width:1em;margin:0;margin-right:.25em;vertical-align:top}
.mist.largeplay,.mist.muted{position:absolute;opacity:.5}
.mist.largeplay{top:50%;left:0;right:0;margin:auto;transform:translateY(-50%)}
.mist.muted{top:0;right:0;margin:1em}
.mistvideo-secondaryVideo{z-index:1;width:50%;height:50%}
.mistvideo-polling{display:inline-block;position:relative;width:25px;height:25px}
.mistvideo-polling svg.icon.loading{z-index:0;opacity:1}
.mistvideo[data-show-submenu] .mistvideo-submenu{right:5px}
.mistvideo[data-show-submenu] .mistvideo-controls{bottom:0}
.mistvideo-error[data-passive]{bottom:auto;left:auto;margin:.5em;padding:.5em}
.mistvideo-error[data-passive] .message{max-width:none}
.mistvideo-error .mistvideo-buttoncontainer{display:flex;flex-flow:row nowrap;justify-content:center}
.mistvideo-error .mistvideo-buttoncontainer .mistvideo-button{white-space:nowrap}
.browser-ie .mist.icon.loading{animation:spin 1.5s infinite linear;transform-origin:50% 50%}
.browser-ie .mist.icon.loading .spin{animation:none}

116
embed/min/skins/dev.css Normal file
View file

@ -0,0 +1,116 @@
.mistvideo{display:inline-block;position:relative;color:$stroke;font-family:sans-serif;background-color:#000;justify-content:center;align-items:center}
.mistvideo-controls{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}
.mistvideo.novideo{overflow:visible}
.mistvideo-video{overflow:hidden}
svg.icon.loading{z-index:-1;position:absolute;top:0;left:0;right:0;bottom:0;margin:auto;opacity:0}
[data-loading]{position:relative}
[data-loading=stalled] svg.icon.loading{transition:opacity 0s 3s}
[data-loading] svg.icon.loading{z-index:2;opacity:1}
[data-loading-css] .mistvideo-controls{display:none}
[data-hidecursor],[data-hidecursor] *,[data-hidecursor] .mistvideo-pointer{cursor:none}
.mistvideo-error{display:none;position:absolute;top:0;left:0;right:0;bottom:0;background-color:$background;align-items:center;justify-content:center;text-align:center;z-index:2;cursor:default;min-height:fit-content;min-width:fit-content}
.mistvideo-error.show{display:flex}
.mistvideo-error .message{max-width:80%}
.mistvideo-error .message .details table{text-align:left}
.mistvideo-controls button,.mistvideo-error button,.mistvideo-video:not(.video-js) button{color:$stroke;border:1px solid $semiFill;background-color:$background;margin:.25em;padding:.5em 1em;opacity:.5;cursor:pointer}
button:hover{opacity:1}
select{background-color:transparent;color:$stroke;border:none;margin:0 .5em;font-size:inherit;cursor:pointer;-ms-background-color:red}
select>option{background-color:$background}
.browser-edge select,.browser-safari select{border:1px solid $semiFill;border-top:none;border-left:none;margin-top:2px}
@keyframes spin{
0%{transform:rotate(0)}
100%{transform:rotate(360deg)}
}
[data-fullscreen]{position:fixed;top:0;left:0;right:0;bottom:0;width:100%!important;height:100%!important}
video{display:block;flex-shrink:0}
table{color:inherit;font-size:inherit;font-style:inherit}
audio:not([controls]){display:block!important}
.mistvideo-padding{padding:5px 10px}
.mistvideo-pointer{cursor:pointer}
.description{color:$semiFill;font-size:.9em}
.mistvideo-container{display:flex;flex-wrap:nowrap}
.mistvideo-container.mistvideo{display:inline-flex;max-width:100%}
.mistvideo-container.mistvideo-row{flex-direction:row}
.mistvideo-container.mistvideo-column{flex-direction:column}
.mistvideo-container.mistvideo-center{justify-content:center}
.mistvideo-align-right{margin-left:auto;margin-right:0}
.hover_window_container.pos>.outer_window{position:absolute;pointer-events:none;overflow:hidden}
.hover_window_container.pos>.outer_window>.inner_window{position:absolute;pointer-events:all}
.hover_window_container:not(:hover)>.outer_window>.inner_window,.hover_window_container>.outer_window[data-hidecursor]>.inner_window{transition:all .5s ease-in .5s}
.outer_window>.inner_window{flex-grow:1}
.hover_window_container>.outer_window>.inner_window>*{flex-shrink:0}
.mistvideo-draggable{overflow:hidden}
.mistvideo-tooltip{position:absolute;background-color:$background;padding:.5em 1em;z-index:2}
:not(:hover)>.mistvideo-tooltip{transition:opacity .25s ease-in}
.mistvideo-tooltip .triangle{border:10px solid $background;position:absolute}
.mistvideo-tracks label{display:block}
.mistvideo-tracks label>span{margin-right:1em}
a{color:$accent}
.mistvideo-log .logs{-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text}
.mistvideo-placeholder{max-width:100%;max-height:100%}
.mistvideo-topright{position:absolute;top:0;right:0}
.mistvideo-topleft{position:absolute;top:0;left:0}
.mistvideo-delay-display{animation:appear 1s;animation-iteration-count:1;animation-timing-function:steps(1,end)}
@keyframes appear{
from{opacity:0}
to{opacity:1}
}
svg.icon{display:block;stroke-width:$strokeWidth;fill:none;stroke:none}
svg.icon .fill,svg.icon.fill{fill:$fill}
svg.icon .semiFill,svg.icon.semiFill{fill:$semiFill}
svg.icon .stroke,svg.icon.stroke{stroke:$stroke;vector-effect:non-scaling-stroke}
svg.icon.off .toggle .fill,svg.icon.off .toggle .semiFill,svg.icon.off .toggle.fill,svg.icon.off .toggle.semiFill{fill:none}
svg.icon .spin,svg.icon.spin{animation:spin 1.5s infinite linear;transform-origin:50% 50%}
.mistvideo{line-height:1.2;font-size:14.5px}
.mistvideo svg{margin:2.5px}
.mistvideo-background{background-color:$background}
.mistvideo-totalTime:before{content:'/';margin:.2em}
.mistvideo-progress{padding:10px 0;margin:-10px 0;z-index:2}
.mistvideo-progress>*{height:2px;background-color:$progressBackground;opacity:.95;position:relative}
.mistvideo-novideo .mistvideo-progress>*,.mistvideo-progress:hover>*{height:10px}
.mistvideo-progress:not(:hover)>*{transition:height .25s ease-in .5s}
.mistvideo-progress .bar{height:inherit;width:0;position:absolute;border-right:inherit;background-color:$accent;z-index:2}
.mistvideo-progress .buffer{height:inherit;width:0;position:absolute;background-color:$semiFill}
.mistvideo-progress .bar:after{content:'';border:5px solid $accent;border-radius:5px;position:absolute;right:-5px;top:50%;transform:translateY(-50%)}
.mistvideo-play[data-state=playing] svg.play{display:none}
.mistvideo-play[data-state=paused] svg.pause{display:none}
.mistvideo-main{align-items:center}
svg.icon.timeout{display:inline-block;height:1em;width:1em;margin:0;margin-right:.25em;vertical-align:top}
.mist.largeplay,.mist.muted{position:absolute;opacity:.5}
.mist.largeplay{top:50%;left:0;right:0;margin:auto;transform:translateY(-50%)}
.mist.muted{top:0;right:0;margin:1em}
.mistvideo-secondaryVideo{z-index:1;width:50%;height:50%}
.mistvideo-polling{display:inline-block;position:relative;width:25px;height:25px}
.mistvideo-polling svg.icon.loading{z-index:0;opacity:1}
.mistvideo[data-show-submenu] .mistvideo-submenu{right:5px}
.mistvideo[data-show-submenu] .mistvideo-controls{bottom:0}
.mistvideo-error[data-passive]{bottom:auto;left:auto;margin:.5em;padding:.5em}
.mistvideo-error[data-passive] .message{max-width:none}
.mistvideo-error .mistvideo-buttoncontainer{display:flex;flex-flow:row nowrap;justify-content:center}
.mistvideo-error .mistvideo-buttoncontainer .mistvideo-button{white-space:nowrap}
.browser-ie .mist.icon.loading{animation:spin 1.5s infinite linear;transform-origin:50% 50%}
.browser-ie .mist.icon.loading .spin{animation:none}
.mistvideo-log{margin:.5em 0}
.mistvideo-log .logs{max-height:10em;min-height:5em;width:100%;padding:.2em 0;padding-right:1em;overflow-y:auto;overflow-x:hidden;font-size:.9em}
.mistvideo-log .logs table td{vertical-align:top;padding:0}
.mistvideo-log .logs .entry .message{margin:0 .2em}
.mistvideo-log .logs .entry.type-error{color:$accent}
.mistvideo-log .logs .counter,.mistvideo-log .logs .timestamp{color:$semiFill}
.mistvideo-log .logs .timestamp:before{content:'['}
.mistvideo-log .logs .timestamp:after{content:']'}
.mistvideo-log .logs .counter:before{content:'('}
.mistvideo-log .logs .counter:after{content:'\00d7)'}
.mistvideo-devbuttons{font-size:.9em}
.mistvideo-devbuttons button{font-size:.8em;margin:.1em;padding:.2em .4em}
.mistvideo-forcePlayer>span,.mistvideo-forceSource>span,.mistvideo-forceType>span{display:inline-block;width:7em}
.mistvideo-forcePlayer>select,.mistvideo-forceSource>select,.mistvideo-forceType>select{width:15em;min-width:auto}
.mistvideo-devcontrols{margin:.5em 0}
.mistvideo-decodingIssues>*{display:flex;width:50%;max-width:20em;flex-flow:row nowrap;align-items:flex-end;justify-content:space-between}
.mistvideo-decodingIssues>*>:last-child{margin-right:.5em}
.mistvideo-decodingIssues{display:flex;flex-flow:row wrap;justify-content:space-between}
.mistvideo-decodingIssues label{position:relative}
.mistvideo-decodingIssues label .value{font-size:.8em}
svg.icon.graph{position:absolute;right:0;top:0;bottom:0;width:6em}
select{border-radius:0}
input[type=checkbox]{margin:0;margin-right:.2em;border:1px solid $semiFill;border-radius:0;-webkit-appearance:none;-moz-appearance:none;-ms-appearance:none;appearance:none;width:.8em;height:.8em;color:inherit;position:relative;cursor:pointer}
input[type=checkbox]:checked:after{content:"\2713";position:absolute;bottom:-.2em;left:0;font-size:1.2em}

View file

@ -0,0 +1 @@
mistplayers.dashjs={name:"Dash.js player",mimes:["dash/video/mp4"],priority:MistUtil.object.keys(mistplayers).length+1,isMimeSupported:function(t){return MistUtil.array.indexOf(this.mimes,t)==-1?false:true},isBrowserSupported:function(t,e,i){if(location.protocol!=MistUtil.http.url.split(e.url).protocol){i.log("HTTP/HTTPS mismatch for this source");return false}if(location.protocol=="file:"){i.log("This source ("+t+") won't load if the page is run via file://");return false}return"MediaSource"in window},player:function(){this.onreadylist=[]},scriptsrc:function(t){return t+"/dashjs.js"}};var p=mistplayers.dashjs.player;p.prototype=new MistPlayer;p.prototype.build=function(t,e){var i=this;this.onDashLoad=function(){if(t.destroyed){return}t.log("Building DashJS player..");var r=document.createElement("video");if("Proxy"in window){var a={get:{},set:{}};t.player.api=new Proxy(r,{get:function(t,e,i){if(e in a.get){return a.get[e].apply(t,arguments)}var r=t[e];if(typeof r==="function"){return function(){return r.apply(t,arguments)}}return r},set:function(t,e,i){if(e in a.set){return a.set[e].call(t,i)}return t[e]=i}});if(t.info.type=="live"){a.get.duration=function(){var e=0;if(this.buffered.length){e=this.buffered.end(this.buffered.length-1)}var i=((new Date).getTime()-t.player.api.lastProgress.getTime())*.001;return e+i+-1*t.player.api.liveOffset+45};a.set.currentTime=function(e){var i=e-t.player.api.duration;t.log("Seeking to "+MistUtil.format.time(e)+" ("+Math.round(i*-10)/10+"s from live)");t.video.currentTime=e};MistUtil.event.addListener(r,"progress",function(){t.player.api.lastProgress=new Date});t.player.api.lastProgress=new Date;t.player.api.liveOffset=0}}else{i.api=r}if(t.options.autoplay){r.setAttribute("autoplay","")}if(t.options.loop&&t.info.type!="live"){r.setAttribute("loop","")}if(t.options.poster){r.setAttribute("poster",t.options.poster)}if(t.options.controls=="stock"){r.setAttribute("controls","")}var s=dashjs.MediaPlayer().create();s.initialize(r,t.source.url,t.options.autoplay);i.dash=s;var o=["METRIC_ADDED","METRIC_CHANGED","METRICS_CHANGED","FRAGMENT_LOADING_STARTED","FRAGMENT_LOADING_COMPLETED","LOG","PLAYBACK_TIME_UPDATED","PLAYBACK_PROGRESS"];for(var n in dashjs.MediaPlayer.events){if(o.indexOf(n)<0){i.dash.on(dashjs.MediaPlayer.events[n],function(e){t.log("Player event fired: "+e.type)})}}t.player.setSize=function(t){this.api.style.width=t.width+"px";this.api.style.height=t.height+"px"};t.player.api.setSource=function(e){t.player.dash.attachSource(e)};t.player.api.setTrack=function(e,r){var a=MistUtil.tracks.parse(t.info.meta.tracks);if(!(e in a)||!(r in a[e])&&r!=0){t.log("Skipping trackselection of "+e+" track "+r+" because it does not exist");return}var s=i.dash.getBitrateInfoListFor("video").length-1;for(var o in t.info.meta.tracks){var n=t.info.meta.tracks[o];if(n.type==e){if(n.trackid==r){break}s--}}i.dash.setAutoSwitchQualityFor(e,false);i.dash.setFastSwitchEnabled(true);i.dash.setQualityFor(e,s)};i.dash.on("qualityChangeRendered",function(e){var r=i.dash.getBitrateInfoListFor("video").length-1;var a;for(var s in t.info.meta.tracks){var o=t.info.meta.tracks[s];if(o.type==e.mediaType){if(e.newQuality==r){a=o.trackid;break}r--}}MistUtil.event.send("playerUpdate_trackChanged",{type:e.mediaType,trackid:a},t.video)});MistUtil.event.addListener(r,"progress",function(e){if(t.container.getAttribute("data-loading")=="stalled"){t.container.removeAttribute("data-loading")}});i.api.unload=function(){i.dash.reset()};t.log("Built html");e(r)};if("dashjs"in window){this.onDashLoad()}else{var r=MistUtil.scripts.insert(t.urlappend(mistplayers.dashjs.scriptsrc(t.options.host)),{onerror:function(e){var i="Failed to load dashjs.js";if(e.message){i+=": "+e.message}t.showError(i)},onload:i.onDashLoad},t)}};

View file

@ -0,0 +1 @@
mistplayers.flash_strobe={name:"Strobe Flash media playback",mimes:["flash/10","flash/11","flash/7"],priority:MistUtil.object.keys(mistplayers).length+1,isMimeSupported:function(t){return this.mimes.indexOf(t)==-1?false:true},isBrowserSupported:function(t,e,i){if(MistUtil.http.url.split(e.url).protocol.slice(0,4)=="http"&&location.protocol!=MistUtil.http.url.split(e.url).protocol){i.log("HTTP/HTTPS mismatch for this source");return false}var r=0;try{var a=navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin;if(a.version){r=a.version.split(".")[0]}else{r=a.description.replace(/([^0-9\.])/g,"").split(".")[0]}}catch(t){}try{r=new ActiveXObject("ShockwaveFlash.ShockwaveFlash").GetVariable("$version").replace(/([^0-9\,])/g,"").split(",")[0]}catch(t){}if(!r){return false}var l=t.split("/");return Number(r)>=Number(l[l.length-1])},player:function(){this.onreadylist=[]}};var p=mistplayers.flash_strobe.player;p.prototype=new MistPlayer;p.prototype.build=function(t,e){var i=document.createElement("object");var r=document.createElement("embed");i.appendChild(r);function a(e){var a=t.options;function l(t,e){var i=document.createElement("param");i.setAttribute("name",t);i.setAttribute("value",e);return i}MistUtil.empty(i);i.appendChild(l("movie",t.urlappend(a.host+t.source.player_url)));var s="src="+encodeURIComponent(e)+"&controlBarMode="+(a.controls?"floating":"none")+"&initialBufferTime=0.5&expandedBufferTime=5&minContinuousPlaybackTime=3"+(a.live?"&streamType=live":"")+(a.autoplay?"&autoPlay=true":"");i.appendChild(l("flashvars",s));i.appendChild(l("allowFullScreen","true"));i.appendChild(l("wmode","direct"));if(a.autoplay){i.appendChild(l("autoPlay","true"))}r.setAttribute("src",t.urlappend(t.source.player_url));r.setAttribute("type","application/x-shockwave-flash");r.setAttribute("allowfullscreen","true");r.setAttribute("flashvars",s)}a(t.source.url);this.api={};this.setSize=function(t){i.setAttribute("width",t.width);i.setAttribute("height",t.height);r.setAttribute("width",t.width);r.setAttribute("height",t.height)};this.setSize(t.calcSize());this.onready(function(){if(t.container){t.container.removeAttribute("data-loading")}});this.api.setSource=function(t){a(t)};t.log("Built html");e(i)};

View file

@ -0,0 +1 @@
mistplayers.html5={name:"HTML5 video player",mimes:["html5/application/vnd.apple.mpegurl","html5/video/mp4","html5/video/ogg","html5/video/webm","html5/audio/mp3","html5/audio/webm","html5/audio/ogg","html5/audio/wav"],priority:MistUtil.object.keys(mistplayers).length+1,isMimeSupported:function(e){return MistUtil.array.indexOf(this.mimes,e)==-1?false:true},isBrowserSupported:function(e,t,i){if(location.protocol!=MistUtil.http.url.split(t.url).protocol){if(location.protocol=="file:"&&MistUtil.http.url.split(t.url).protocol=="http:"){i.log("This page was loaded over file://, the player might not behave as intended.")}else{i.log("HTTP/HTTPS mismatch for this source");return false}}var r=false;var a=e.split("/");a.shift();try{a=a.join("/");function n(e){var t=document.createElement("video");if(t&&t.canPlayType(e)!=""){r=t.canPlayType(e)}return r}if(a=="video/mp4"){function o(e){function t(t){return("0"+e.init.charCodeAt(t).toString(16)).slice(-2)}switch(e.codec){case"AAC":return"mp4a.40.2";case"MP3":return"mp3";case"AC3":return"ec-3";case"H264":return"avc1."+t(1)+t(2)+t(3);case"HEVC":return"hev1."+t(1)+t(6)+t(7)+t(8)+t(9)+t(10)+t(11)+t(12);default:return e.codec.toLowerCase()}}var s={};for(var l in i.info.meta.tracks){if(i.info.meta.tracks[l].type!="meta"){s[o(i.info.meta.tracks[l])]=1}}s=MistUtil.object.keys(s);if(s.length){if(s.length>t.simul_tracks){var p=0;for(var l in s){var u=n(a+';codecs="'+s[l]+'"');if(u){p++}}return p>=t.simul_tracks}a+=';codecs="'+s.join(",")+'"'}}r=n(a)}catch(e){}return r},player:function(){this.onreadylist=[]},mistControls:true};var p=mistplayers.html5.player;p.prototype=new MistPlayer;p.prototype.build=function(e,t){var i=e.source.type.split("/");i.shift();var r=document.createElement("video");r.setAttribute("crossorigin","anonymous");var a=document.createElement("source");a.setAttribute("src",e.source.url);r.source=a;r.appendChild(a);a.type=i.join("/");var n=["autoplay","loop","poster"];for(var o in n){var s=n[o];if(e.options[s]){r.setAttribute(s,e.options[s]===true?"":e.options[s])}}if(e.options.controls=="stock"){r.setAttribute("controls","")}if(e.info.type=="live"){r.loop=false}if("Proxy"in window&&"Reflect"in window){var l={get:{},set:{}};e.player.api=new Proxy(r,{get:function(e,t,i){if(t in l.get){return l.get[t].apply(e,arguments)}var r=e[t];if(typeof r==="function"){return function(){return r.apply(e,arguments)}}return r},set:function(e,t,i){if(t in l.set){return l.set[t].call(e,i)}return e[t]=i}});if(e.source.type=="html5/audio/mp3"){l.set.currentTime=function(){e.log("Seek attempted, but MistServer does not currently support seeking in MP3.");return false}}if(e.info.type=="live"){l.get.duration=function(){var t=0;if(this.buffered.length){t=this.buffered.end(this.buffered.length-1)}var i=((new Date).getTime()-e.player.api.lastProgress.getTime())*.001;return t+i-e.player.api.liveOffset};l.set.currentTime=function(t){var i=t-e.player.api.duration;e.player.api.liveOffset=i;e.log("Seeking to "+MistUtil.format.time(t)+" ("+Math.round(i*-10)/10+"s from live)");e.player.api.setSource(MistUtil.http.url.addParam(e.source.url,{startunix:i}))};MistUtil.event.addListener(r,"progress",function(){e.player.api.lastProgress=new Date});e.player.api.lastProgress=new Date;e.player.api.liveOffset=0;MistUtil.event.addListener(r,"pause",function(){e.player.api.pausedAt=new Date});l.get.play=function(){return function(){if(e.player.api.paused&&e.player.api.pausedAt&&new Date-e.player.api.pausedAt>5e3){r.load();e.log("Reloading source..")}return r.play.apply(r,arguments)}};if(e.source.type=="html5/video/mp4"){l.get.currentTime=function(){return this.currentTime-e.player.api.liveOffset+e.info.lastms*.001}}}else{if(!isFinite(r.duration)){var p=0;for(var o in e.info.meta.tracks){p=Math.max(p,e.info.meta.tracks[o].lastms)}l.get.duration=function(){if(isFinite(this.duration)){return this.duration}return p*.001}}}}else{e.player.api=r}e.player.api.setSource=function(e){if(e!=this.source.src){this.source.src=e;this.load()}};e.player.api.setSubtitle=function(e){var t=r.getElementsByTagName("track");for(var i=t.length-1;i>=0;i--){r.removeChild(t[i])}if(e){var a=document.createElement("track");r.appendChild(a);a.kind="subtitles";a.label=e.label;a.srclang=e.lang;a.src=e.src;a.setAttribute("default","")}};e.player.setSize=function(e){this.api.style.width=e.width+"px";this.api.style.height=e.height+"px"};t(r)};

View file

@ -0,0 +1 @@
mistplayers.videojs={name:"VideoJS player",mimes:["html5/application/vnd.apple.mpegurl"],priority:MistUtil.object.keys(mistplayers).length+1,isMimeSupported:function(e){return this.mimes.indexOf(e)==-1?false:true},isBrowserSupported:function(e,t,i){if(location.protocol!=MistUtil.http.url.split(t.url).protocol){i.log("HTTP/HTTPS mismatch for this source");return false}if(location.protocol=="file:"&&e=="html5/application/vnd.apple"){i.log("This source ("+e+") won't load if the page is run via file://");return false}return"MediaSource"in window},player:function(){},scriptsrc:function(e){return e+"/videojs.js"}};var p=mistplayers.videojs.player;p.prototype=new MistPlayer;p.prototype.build=function(e,t){var i=this;function r(){if(e.destroyed){return}e.log("Building VideoJS player..");var r=document.createElement("video");if(e.source.type!="html5/video/ogg"){r.crossOrigin="anonymous"}var o=e.source.type.split("/");o.shift();var n=document.createElement("source");n.setAttribute("src",e.source.url);i.source=n;r.appendChild(n);n.type=o.join("/");e.log("Adding "+n.type+" source @ "+e.source.url);if(n.type=="application/vnd.apple.mpegurl"){n.type="application/x-mpegURL"}MistUtil.class.add(r,"video-js");var s={};if(e.options.autoplay){s.autoplay=true}if(e.options.loop&&e.info.type!="live"){s.loop=true;r.loop=true}if(e.options.poster){s.poster=e.options.poster}if(e.options.controls=="stock"){r.setAttribute("controls","");if(!document.getElementById("videojs-css")){var l=document.createElement("link");l.rel="stylesheet";l.href=e.options.host+"/skins/videojs.css";l.id="videojs-css";document.head.appendChild(l)}}i.onready(function(){i.videojs=videojs(r,s,function(){e.log("Videojs initialized")});i.api.unload=function(){videojs(r).dispose()}});e.log("Built html");if("Proxy"in window&&"Reflect"in window){var a={get:{},set:{}};e.player.api=new Proxy(r,{get:function(e,t,i){if(t in a.get){return a.get[t].apply(e,arguments)}var r=e[t];if(typeof r==="function"){return function(){return r.apply(e,arguments)}}return r},set:function(e,t,i){if(t in a.set){return a.set[t].call(e,i)}return e[t]=i}});if(e.info.type=="live"){function p(e){var t=0;if(e.buffered.length){t=e.buffered.end(e.buffered.length-1)}return t}var u=90;a.get.duration=function(){return(e.info.lastms+(new Date).getTime()-e.info.updated.getTime())*.001};e.player.api.lastProgress=new Date;e.player.api.liveOffset=0;MistUtil.event.addListener(r,"progress",function(){e.player.api.lastProgress=new Date});a.set.currentTime=function(t){var i=e.player.api.currentTime-t;var r=t-e.player.api.duration;e.log("Seeking to "+MistUtil.format.time(t)+" ("+Math.round(r*-10)/10+"s from live)");e.video.currentTime-=i};a.get.currentTime=function(){return this.currentTime+e.info.lastms*.001-e.player.api.liveOffset-u}}}else{i.api=r}e.player.setSize=function(t){if("videojs"in e.player){e.player.videojs.dimensions(t.width,t.height);r.parentNode.style.width=t.width+"px";r.parentNode.style.height=t.height+"px"}this.api.style.width=t.width+"px";this.api.style.height=t.height+"px"};e.player.api.setSource=function(t){if(!e.player.videojs){return}if(e.player.videojs.src()!=t){e.player.videojs.src({type:e.player.videojs.currentSource().type,src:t})}};e.player.api.setSubtitle=function(e){var t=r.getElementsByTagName("track");for(var i=t.length-1;i>=0;i--){r.removeChild(t[i])}if(e){var o=document.createElement("track");r.appendChild(o);o.kind="subtitles";o.label=e.label;o.srclang=e.lang;o.src=e.src;o.setAttribute("default","")}};t(r)}if("videojs"in window){r()}else{var o=MistUtil.scripts.insert(e.urlappend(mistplayers.videojs.scriptsrc(e.options.host)),{onerror:function(t){var i="Failed to load videojs.js";if(t.message){i+=": "+t.message}e.showError(i)},onload:r},e)}};

File diff suppressed because one or more lines are too long

48
embed/minimize.sh Executable file
View file

@ -0,0 +1,48 @@
#!/bin/bash
echo "Minimizing player code..";
echo " Minimizing JS..";
if [ "min/player.js" -ot "util.js" ] || [ "min/player.js" -ot "skins.js" ] || [ "min/player.js" -ot "controls.js" ] || [ "min/player.js" -ot "player.js" ]; then
echo " Minimizing 'util.js skins.js controls.js player.js' into 'min/player.js'..";
terser -mc -o min/player.js -- util.js skins.js controls.js player.js
fi
echo " Done.";
echo " Minimizing wrappers.."
if [ "min/wrappers/dashjs.js" -ot "wrappers/dashjs.js" ]; then
echo " Minimizing dashjs";
terser -mn -o min/wrappers/dashjs.js -- wrappers/dashjs.js
fi
if [ "min/wrappers/flash_strobe.js" -ot "wrappers/flash_strobe.js" ]; then
echo " Minimizing flash_strobe";
terser -mn -o min/wrappers/flash_strobe.js -- wrappers/flash_strobe.js
fi
if [ "min/wrappers/html5.js" -ot "wrappers/html5.js" ]; then
echo " Minimizing html5";
terser -mn -o min/wrappers/html5.js -- wrappers/html5.js
fi
if [ "min/wrappers/videojs.js" -ot "wrappers/videojs.js" ]; then
echo " Minimizing videojs";
terser -mn -o min/wrappers/videojs.js -- wrappers/videojs.js
fi
if [ "min/wrappers/webrtc.js" -ot "wrappers/webrtc.js" ]; then
echo " Minimizing webrtc";
terser -mn -o min/wrappers/webrtc.js -- wrappers/webrtc.js
fi
echo " Done.";
echo " Minimizing CSS..";
if [ "min/skins/default.css" -ot "skins/default.css" ] || [ "min/skins/default.css" -ot "skins/general.css" ]; then
echo " Minimizing default";
cleancss --format keep-breaks -o min/skins/default.css skins/general.css skins/default.css
fi
if [ "min/skins/dev.css" -ot "skins/default.css" ] || [ "min/skins/dev.css" -ot "skins/general.css" ] || [ "min/skins/dev.css" -ot "skins/dev.css" ]; then
echo " Minimizing dev";
cleancss --format keep-breaks -o min/skins/dev.css skins/general.css skins/default.css skins/dev.css
fi
echo " Done.";
echo "Done.";

File diff suppressed because one or more lines are too long

1127
embed/player.js Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

8
embed/players/update.sh Executable file
View file

@ -0,0 +1,8 @@
#!/bin/bash
echo "Dashjs"
wget https://cdn.dashjs.org/latest/dash.all.min.js -O dash.all.min.js
echo "Videojs"
echo "You'll want to check for the latest version at https://videojs.com/getting-started/#download-cdn"
wget https://vjs.zencdn.net/7.3.0/video.min.js -O video.min.js

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

1
embed/players/webrtc.js Normal file

File diff suppressed because one or more lines are too long

2559
embed/skins.js Normal file

File diff suppressed because it is too large Load diff

131
embed/skins/default.css Normal file
View file

@ -0,0 +1,131 @@
.mistvideo {
line-height: 1.2;
font-size: 14.5px;
}
.mistvideo svg {
margin: 2.5px;
}
.mistvideo-background { background-color: $background; }
.mistvideo-totalTime:before {
content: '/';
margin: 0.2em;
}
.mistvideo-progress {
padding: 10px 0;
margin: -10px 0;
z-index: 2; /*keep above control bar*/
}
.mistvideo-progress > * {
height: 2px;
background-color: $progressBackground;
opacity: 0.95;
position: relative;
}
.mistvideo-novideo .mistvideo-progress > *,
.mistvideo-progress:hover > * {
height: 10px;
}
.mistvideo-progress:not(:hover) > * {
transition: height 0.25s ease-in 0.5s;
}
.mistvideo-progress .bar {
height: inherit;
width: 0;
position: absolute;
border-right: inherit;
background-color: $accent;
z-index: 2; /*keep above buffer bar(s)*/
}
.mistvideo-progress .buffer {
height: inherit;
width: 0;
position: absolute;
background-color: $semiFill;
}
.mistvideo-progress .bar:after {
content: '';
border: 5px solid $accent;
border-radius: 5px;
position: absolute;
right: -5px;
top: 50%;
transform: translateY(-50%);
}
.mistvideo-play[data-state="playing"] svg.play {
display: none;
}
.mistvideo-play[data-state="paused"] svg.pause {
display: none;
}
.mistvideo-main { align-items: center; }
svg.icon.timeout {
display: inline-block;
height: 1em;
width: 1em;
margin: 0;
margin-right: 0.25em;
vertical-align: top;
}
.mist.largeplay, .mist.muted {
position: absolute;
opacity: 0.5;
}
.mist.largeplay {
top: 50%;
left: 0;
right: 0;
margin: auto;
transform: translateY(-50%);
}
.mist.muted {
top: 0;
right: 0;
margin: 1em;
}
.mistvideo-secondaryVideo {
z-index: 1;
width: 50%;
height: 50%;
}
.mistvideo-polling {
display: inline-block;
position: relative;
width: 25px;
height: 25px;
}
.mistvideo-polling svg.icon.loading {
z-index: 0;
opacity: 1;
}
.mistvideo[data-show-submenu] .mistvideo-submenu {
right: 5px;
}
.mistvideo[data-show-submenu] .mistvideo-controls {
bottom: 0;
}
.mistvideo-error[data-passive] {
bottom: auto;
left: auto;
margin: 0.5em;
padding: 0.5em;
}
.mistvideo-error[data-passive] .message {
max-width: none;
}
.mistvideo-error .mistvideo-buttoncontainer {
display: flex;
flex-flow: row nowrap;
justify-content: center;
}
.mistvideo-error .mistvideo-buttoncontainer .mistvideo-button {
white-space: nowrap;
}
.browser-ie .mist.icon.loading {
animation: spin 1.5s infinite linear;
transform-origin: 50% 50%;
}
.browser-ie .mist.icon.loading .spin { animation: none; }

105
embed/skins/dev.css Normal file
View file

@ -0,0 +1,105 @@
.mistvideo-log {
margin: 0.5em 0;
}
.mistvideo-log .logs {
max-height: 10em;
min-height: 5em;
width: 100%;
padding: 0.2em 0;
padding-right: 1em; /*for scrollbar*/
overflow-y: auto;
overflow-x: hidden;
font-size: 0.9em;
}
.mistvideo-log .logs table td {
vertical-align: top;
padding: 0;
}
.mistvideo-log .logs .entry .message {
margin: 0 0.2em;
}
.mistvideo-log .logs .entry.type-error {
color: $accent;
}
.mistvideo-log .logs .timestamp,
.mistvideo-log .logs .counter {
color: $semiFill;
}
.mistvideo-log .logs .timestamp:before { content: '['; }
.mistvideo-log .logs .timestamp:after { content: ']'; }
.mistvideo-log .logs .counter:before { content: '('; }
.mistvideo-log .logs .counter:after { content: '\00d7)'; }
.mistvideo-devbuttons { font-size: 0.9em; }
.mistvideo-devbuttons button {
font-size: 0.8em;
margin: 0.1em;
padding: 0.2em 0.4em;
}
.mistvideo-forceSource > span,
.mistvideo-forceType > span,
.mistvideo-forcePlayer > span { display: inline-block; width: 7em; }
.mistvideo-forceSource > select,
.mistvideo-forceType > select,
.mistvideo-forcePlayer > select {
width: 15em;
min-width: auto;
}
.mistvideo-devcontrols {
margin: 0.5em 0;
}
.mistvideo-decodingIssues > * {
display: flex;
width: 50%;
max-width: 20em;
flex-flow: row nowrap;
align-items: flex-end;
justify-content: space-between;
}
.mistvideo-decodingIssues > * > :last-child {
margin-right: 0.5em;
}
.mistvideo-decodingIssues {
display: flex;
flex-flow: row wrap;
justify-content: space-between;
}
.mistvideo-decodingIssues label {
position: relative;
}
.mistvideo-decodingIssues label .value {
font-size: 0.8em;
}
svg.icon.graph {
position: absolute;
right: 0;
top: 0;
bottom: 0;
width: 6em;
}
select {
border-radius: 0;
}
input[type="checkbox"] {
margin: 0;
margin-right: 0.2em;
border: 1px solid $semiFill;
border-radius: 0;
-webkit-appearance: none;
-moz-appearance: none;
-ms-appearance: none;
appearance: none;
width: 0.8em;
height: 0.8em;
color: inherit;
position: relative;
cursor: pointer;
}
input[type="checkbox"]:checked:after {
content: "\2713";
position: absolute;
bottom: -0.2em;
left: 0;
font-size: 1.2em;
}

224
embed/skins/general.css Normal file
View file

@ -0,0 +1,224 @@
.mistvideo {
display: inline-block;
position: relative;
color: $stroke;
font-family: sans-serif;
background-color: #000;
justify-content: center;
align-items: center;
}
.mistvideo-controls {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.mistvideo.novideo {
overflow: visible;
}
.mistvideo-video {
overflow: hidden;
}
svg.icon.loading {
z-index: -1; /* don't use display: none because of transition for [data-loading=stalled] */
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
opacity: 0;
}
[data-loading] {
position: relative;
}
/*wait before showing icon when stalled*/
[data-loading="stalled"] svg.icon.loading {
transition: opacity 0s 3s;
}
[data-loading] svg.icon.loading {
z-index: 2;
opacity: 1;
}
[data-loading-css] .mistvideo-controls {
display: none;
}
[data-hidecursor],
[data-hidecursor] .mistvideo-pointer,
[data-hidecursor] *
{ cursor: none }
.mistvideo-error {
display: none;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: $background;
align-items: center;
justify-content: center;
text-align: center;
z-index: 2; /*above progress bar*/
cursor: default; /*cursor won't show up if it was hidden because the mousemove event can no longer reach the video element*/
min-height: fit-content; /*overflow if needed*/
min-width: fit-content;
}
.mistvideo-error.show { display: flex; }
.mistvideo-error .message { max-width: 80%; }
.mistvideo-error .message .details table { text-align: left; }
.mistvideo-video:not(.video-js) button,
.mistvideo-error button,
.mistvideo-controls button
{
color: $stroke;
border: 1px solid $semiFill;
background-color: $background;
margin: 0.25em;
padding: 0.5em 1em;
opacity: 0.5;
cursor: pointer;
}
button:hover { opacity: 1; }
select {
background-color: transparent;
color: $stroke;
border: none;
margin: 0 0.5em;
font-size: inherit;
cursor: pointer;
-ms-background-color: red;
}
select > option {
background-color: $background;
}
.browser-edge select, .browser-safari select {
/*These browsers don't show an arrow for select boxes, causing them to be invisible when a border is not applied */
border: 1px solid $semiFill;
border-top: none;
border-left: none;
margin-top: 2px;
}
@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
[data-fullscreen] {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100% !important;
height: 100% !important;
}
video {
display: block;
flex-shrink: 0;
}
table { color: inherit; font-size: inherit; font-style: inherit; }
audio:not([controls]) { display: block !important; }
.mistvideo-padding { padding: 5px 10px; }
.mistvideo-pointer { cursor: pointer; }
.description {
color: $semiFill;
font-size: 0.9em;
}
.mistvideo-container {
display: flex;
flex-wrap: nowrap;
}
.mistvideo-container.mistvideo { display: inline-flex; max-width: 100%; }
.mistvideo-container.mistvideo-row { flex-direction: row; }
.mistvideo-container.mistvideo-column { flex-direction: column; }
.mistvideo-container.mistvideo-center { justify-content: center; }
.mistvideo-align-right {
margin-left: auto;
margin-right: 0;
}
.hover_window_container.pos > .outer_window {
position: absolute;
pointer-events: none;
overflow: hidden;
}
.hover_window_container.pos > .outer_window > .inner_window {
position: absolute;
pointer-events: all;
}
.hover_window_container:not(:hover) > .outer_window > .inner_window,
.hover_window_container > .outer_window[data-hidecursor] > .inner_window
{ transition: all 0.5s ease-in 0.5s; }
.outer_window > .inner_window { flex-grow: 1; }
.hover_window_container > .outer_window > .inner_window > * { flex-shrink: 0; }
.mistvideo-draggable {
overflow: hidden;
}
.mistvideo-tooltip {
position: absolute;
background-color: $background;
padding: 0.5em 1em;
z-index: 2;
}
*:not(:hover) > .mistvideo-tooltip { transition: opacity 0.25s ease-in; }
.mistvideo-tooltip .triangle {
border: 10px solid $background;
position: absolute;
}
.mistvideo-tracks label {
display: block;
}
.mistvideo-tracks label > span {
margin-right: 1em;
}
a {
color: $accent;
}
.mistvideo-log .logs {
-webkit-user-select: text;
-moz-user-select: text;
-ms-user-select: text;
user-select: text;
}
.mistvideo-placeholder {
max-width: 100%;
max-height: 100%;
}
.mistvideo-topright {
position: absolute;
top: 0;
right: 0;
}
.mistvideo-topleft {
position: absolute;
top: 0;
left: 0;
}
.mistvideo-delay-display {
animation: appear 1s;
animation-iteration-count: 1;
animation-timing-function: steps(1,end);
}
@keyframes appear { from { opacity: 0; } to { opacity: 1; } }
svg.icon {
display: block;
stroke-width: $strokeWidth;
fill: none;
stroke: none;
}
svg.icon.fill, svg.icon .fill { fill: $fill; }
svg.icon.semiFill, svg.icon .semiFill { fill: $semiFill; }
svg.icon.stroke, svg.icon .stroke { stroke: $stroke; vector-effect: non-scaling-stroke; }
svg.icon.off .toggle.fill,
svg.icon.off .toggle.semiFill,
svg.icon.off .toggle .fill,
svg.icon.off .toggle .semiFill { fill: none; }
svg.icon.spin, svg.icon .spin {
animation: spin 1.5s infinite linear;
transform-origin: 50% 50%;
}

1279
embed/skins/video-js.css Normal file

File diff suppressed because one or more lines are too long

954
embed/util.js Normal file
View file

@ -0,0 +1,954 @@
var MistUtil = {
format: {
time: function(secs,options){
if (isNaN(secs) || !isFinite(secs)) { return secs; }
if (!options) { options = {}; }
var ago = (secs < 0 ? " ago" : "");
secs = Math.abs(secs);
var days = Math.floor(secs / 86400)
secs = secs - days * 86400;
var hours = Math.floor(secs / 3600);
secs = secs - hours * 3600;
var mins = Math.floor(secs / 60);
var ms = Math.round((secs % 1)*1e3);
secs = Math.floor(secs - mins * 60);
var str = [];
if (days) {
days = days+" day"+(days > 1 ? "s" : "")+", ";
}
if ((hours) || (days)) {
str.push(hours);
str.push(("0"+mins).slice(-2));
}
else {
str.push(mins); //don't use 0 padding if there are no hours in front
}
str.push(("0"+Math.floor(secs)).slice(-2));
if (options.ms) {
str[str.length-1] += "."+("000"+ms).slice(-3);
}
return (days ? days : "")+str.join(":")+ago;
},
ucFirst: function(string){
return string.charAt(0).toUpperCase()+string.slice(1);
},
number: function(num) {
if ((isNaN(Number(num))) || (num == 0)) { return num; }
//rounding
//use a significance of three, but don't round "visible" digits
var sig = Math.max(3,Math.ceil(Math.log(num)/Math.LN10));
var mult = Math.pow(10,sig - Math.floor(Math.log(num)/Math.LN10) - 1);
num = Math.round(num * mult) / mult;
//thousand seperation
if (num >= 1e4) {
var seperator = "";
number = num.toString().split(".");
var regex = /(\d+)(\d{3})/;
while (regex.test(number[0])) {
number[0] = number[0].replace(regex,"$1"+seperator+"$2");
}
num = number.join(".");
}
return num;
},
bytes: function(val){
if (isNaN(Number(val))) { return val; }
var suffix = ["bytes","KB","MB","GB","TB","PB"];
if (val == 0) {
unit = suffix[0];
}
else {
var exponent = Math.floor(Math.log(Math.abs(val)) / Math.log(1024));
if (exponent < 0) {
unit = suffix[0];
}
else {
val = val / Math.pow(1024,exponent);
unit = suffix[exponent];
}
}
return this.number(val)+unit;
},
mime2human: function(mime){
switch (mime) {
case "html5/video/webm": {
return "WebM";
break;
}
case "html5/application/vnd.apple.mpegurl": {
return "HLS";
break;
}
case "flash/10": {
return "Flash (RTMP)";
break;
}
case "flash/11": {
return "Flash (HDS)";
break;
}
case "flash/7": {
return "Flash (Progressive)";
break;
}
case "html5/video/mpeg": {
return "TS";
break;
}
case "html5/application/vnd.ms-ss": {
return "Smooth streaming";
break;
}
case "dash/video/mp4": {
return "DASH";
break;
}
case "webrtc": {
return "WebRTC";
break;
}
case "silverlight": {
return "Smooth streaming (Silverlight)";
break;
}
case "html5/text/vtt": {
return "VTT subtitles";
break;
}
case "html5/text/plain": {
return "SRT subtitles";
break;
}
default: {
return mime.replace("html5/","").replace("video/","").replace("audio/","").toLocaleUpperCase();
}
}
}
},
class: {
//reroute classList functionalities if not supported; also avoid indexOf
add: function(DOMelement,item){
if ("classList" in DOMelement) {
DOMelement.classList.add(item);
}
else {
var classes = this.get(DOMelement);
classes.push(item);
this.set(DOMelement,classes);
}
},
remove: function(DOMelement,item){
if ("classList" in DOMelement) {
DOMelement.classList.remove(item);
}
else {
var classes = this.get(DOMelement);
for (var i = classes.length-1; i >= 0; i--) {
if (classes[i] == item) {
classes.splice(i);
}
}
this.set(DOMelement,classes);
}
},
get: function(DOMelement) {
var classes;
var className = DOMelement.getAttribute("class"); //DOMelement.className does not work on svg elements
if ((!className) || (className == "")) { classes = []; }
else { classes = className.split(" "); }
return classes;
},
set: function(DOMelement,classes) {
DOMelement.setAttribute("class",classes.join(" "));
},
has: function(DOMelement,hasClass){
return (DOMelement.className.split(" ").indexOf(hasClass) >= 0)
}
},
object: {
//extend object1 with object2
extend: function(object1,object2,deep) {
for (var i in object2) {
if (deep && (typeof object2[i] == "object") && (!("nodeType" in object2[i]))) {
if (!(i in object1)) {
if (MistUtil.array.is(object2[i])) {
object1[i] = [];
}
else {
object1[i] = {};
}
}
this.extend(object1[i],object2[i],true);
}
else {
object1[i] = object2[i];
}
}
return object1;
},
//replace Object.keys
//if sorting: sort the keys alphabetically or use passed sorting function
//sorting gets these arguments: keya,keyb,valuea,valueb
keys: function(obj,sorting){
var keys = [];
for (var i in obj) {
keys.push(i);
}
if (sorting) {
if (typeof sorting != "function") {
sorting = function(keya,keyb){
if (keya > keyb) { return 1; }
if (keya < keyb) { return -1; }
return 0;
};
}
keys.sort(function(keya,keyb){
return sorting(keya,keyb,obj[keya],obj[keyb]);
});
}
return keys;
},
//replace Object.values
//if sorting: sort the keys alphabetically or use passed sorting function
//sorting gets these arguments: keya,keyb,valuea,valueb
values: function(obj,sorting){
var keys = this.keys(obj,sorting);
values = [];
for (var i in keys) {
values.push(obj[keys[i]]);
}
return values;
}
},
array: {
//replace [].indexOf
indexOf: function(array,entry) {
if (!(array instanceof Array)) { throw "Tried to use indexOf on something that is not an array"; }
if ("indexOf" in array) { return array.indexOf(entry); }
for (var i; i < array.length; i++) {
if (array[i] == entry) {
return i;
}
}
return -1;
},
//replace isArray
is: function(array) {
if ("isArray" in Array) {
return Array.isArray(array);
}
return Object.prototype.toString.call(array) === '[object Array]';
},
multiSort: function (array,sortby) {
/*
MistUtil.array.multiSort([].concat(video.info.source),[
{type: ["html5/video/webm","silverlight"]} or ["type",["html5/video/webm","silverlight"]]
,{simul_tracks:-1} or ["simul_tracks",-1]
,function(a){ return a.priority * -1; }
,"url"
]);
*/
var sortfunc = function(a,b){
if (isNaN(a) || isNaN(b)) {
return a.localeCompare(b);
}
return Math.sign(a-b);
};
if (!sortby.length) { return array.sort(sortfunc); }
function getValue(key,a) {
function parseIt(item,key,sortvalue){
if (!(key in item)) {
throw "Invalid sorting rule: "+JSON.stringify([key,sortvalue])+". \""+key+"\" is not a key of "+JSON.stringify(item);
}
if (typeof sortvalue == "number") {
//deals with something like {priority: -1}
if (key in item) {
return item[key] * sortvalue;
}
}
//deals with something like {type:["webrtc"]}
var i = sortvalue.indexOf(item[key])
return (i >= 0 ? i : sortvalue.length);
}
//deals with something like function(a){ return a.foo + a.bar; }
if (typeof key == "function") { return key(a); }
if (typeof key == "object") {
if (key instanceof Array) {
//it's an array
return parseIt(a,key[0],key[1]);
}
//it's an object
for (var j in key) { //only listen to a single key
return parseIt(a,j,key[j]);
}
}
if (key in a) {
return a[key];
}
throw "Invalid sorting rule: "+key+". This should be a function, object or key of "+JSON.stringify(a)+".";
}
array.sort(function(a,b){
var output = 0;
for (var i in sortby) {
var key = sortby[i];
output = sortfunc(getValue(key,a),getValue(key,b));
if (output != 0) {
break;
}
}
return output;
});
return array;
}
},
createUnique: function() {
var i = "uid"+Math.random().toString().replace("0.","");
if (document.querySelector("."+i)) {
//if this is already used, try again
return createUnique();
}
return i;
},
http: {
getpost: function(type,url,data,callback,errorCallback) {
var xhr = new XMLHttpRequest();
xhr.open(type, url, true);
if (type == "POST") { xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); }
if (errorCallback) { xhr.timeout = 8e3; } //go to timeout function after 8 seconds
xhr.onload = function() {
var status = xhr.status;
if ((status >= 200) && (status < 300)) {
callback(xhr.response);
}
else if (errorCallback) {
xhr.onerror = function() {
errorCallback(xhr);
}
}
};
if (errorCallback) {
xhr.onerror = function() {
errorCallback(xhr);
}
xhr.ontimeout = xhr.onerror;
}
if (type == "POST") {
var poststr;
var post = [];
for (var i in data) {
post.push(i+"="+encodeURIComponent(data[i]));
}
if (post.length) { poststr = post.join("&"); }
xhr.send(poststr);
}
else {
xhr.send();
}
},
get: function(url,callback,errorCallback){
this.getpost("GET",url,null,callback,errorCallback);
},
post: function(url,data,callback,errorCallback){
this.getpost("POST",url,data,callback,errorCallback);
},
url: {
addParam: function(url,params){
var spliturl = url.split("?");
var ret = [spliturl.shift()];
var splitparams = [];
if (spliturl.length) {
splitparams = spliturl[0].split("&");
}
for (var i in params) {
splitparams.push(i+"="+params[i]);
}
if (splitparams.length) { ret.push(splitparams.join("&")); }
return ret.join("?");
},
split: function(url){
var a = document.createElement("a");
a.href = url;
return {
protocol: a.protocol,
host: a.hostname,
hash: a.hash,
port: a.port,
path: a.pathname.replace(/\/*$/,"")
};
},
sanitizeHost: function(host){
var split = MistUtil.http.url.split(host);
return split.protocol + "//" + split.host + (split.port && (split.port != "") ? ":"+split.port : "") + (split.hash && (split.hash != "") ? "#"+split.hash : "") + (split.path ? split.path : "");
}
}
},
css: {
cache: {},
load: function(url,colors,callback){
var style = document.createElement("style");
style.type = "text/css";
style.setAttribute("data-source",url);
if (callback) { style.callback = callback; }
var cache = this.cache;
function onCSSLoad(d) {
//parse rules and replace variables; expected syntax $abc[.abc]
var css = MistUtil.css.applyColors(d,colors);
if ("callback" in style) { style.callback(css); }
else { style.textContent = css; }
}
if (url in cache) {
if (cache[url] instanceof Array) {
cache[url].push(onCSSLoad);
}
else {
onCSSLoad(cache[url]);
}
}
else {
//retrieve file contents
cache[url] = [onCSSLoad];
MistUtil.http.get(url,function(d){
for (var i in cache[url]) {
cache[url][i](d);
}
cache[url] = d;
},function(){
throw "Failed to load CSS from "+url;
});
}
return style; //its empty now, but will be filled on load
},
applyColors: function(css,colors) {
return css.replace(/\$([^\s^;^}]*)/g,function(str,variable){
var index = variable.split(".");
var val = colors;
for (var j in index) {
val = val[index[j]];
}
return val;
});
},
createStyle: function(css,prepend,applyToChildren){
var style = document.createElement("style");
style.type = "text/css";
if (css) {
if (prepend) {
css = this.prependClass(css,prepend,applyToChildren);
}
style.textContent = css;
}
return style;
},
prependClass: function (css,prepend,applyToChildren) {
var style = false;
if (typeof css != "string") {
style = css;
if (!("unprepended" in style)) {
style.unprepended = style.textContent;
}
css = style.unprepended;
}
//remove all block comments
css = css.replace(/\/\*.*?\*\//g,"");
//save all @{} blocks
var save = css.match(/@.*?{.*}/g);
for (var i in save) {
css = css.replace(save[i],"@@@@");
}
//find and replace selectors
css = css.replace(/[^@]*?{[^]*?}/g,function(match){
var split = match.split("{")
var selectors = split[0].split(",");
var properties = "{"+split.slice(1).join("}");
for (var i in selectors) {
selectors[i] = selectors[i].trim();
var str = "."+prepend+selectors[i];
if (applyToChildren) {
str += ",\n."+prepend+" "+selectors[i];
}
selectors[i] = str;
}
return "\n"+selectors+" "+properties;
});
//reinsert saved blocks
for (var i in save) {
css = css.replace(/@@@@/,save[i]);
}
if (style) {
style.textContent = css;
return;
}
return css;
}
},
empty: function(DOMelement) {
while (DOMelement.lastChild){
if (DOMelement.lastChild.lastChild) {
//also empty this child
this.empty(DOMelement.lastChild);
}
if ("attachedListeners" in DOMelement.lastChild) {
//remove attached event listeners
for (var i in DOMelement.lastChild.attachedListeners) {
MistUtil.event.removeListener(DOMelement.lastChild.attachedListeners[i]);
}
}
DOMelement.removeChild(DOMelement.lastChild);
}
},
event: {
send: function(type,message,target){
try {
var event = new Event(type,{
bubbles: true,
cancelable: true
});
event.message = message;
target.dispatchEvent(event);
}
catch (e) {
try {
var event = document.createEvent('Event');
event.initEvent(type,true,true);
event.message = message;
target.dispatchEvent(event);
}
catch (e) { return false; }
}
return true;
},
addListener: function(DOMelement,type,callback,storeOnElement) {
//add an event listener and store the handles, so they can be cleared
DOMelement.addEventListener(type,callback);
if (!storeOnElement) { storeOnElement = DOMelement; }
if (!("attachedListeners" in storeOnElement)) {
storeOnElement.attachedListeners = [];
}
var output = {
element: DOMelement,
type: type,
callback: callback
};
storeOnElement.attachedListeners.push(output);
return output;
},
removeListener: function(data) {
data.element.removeEventListener(data.type, data.callback);
}
},
scripts: {
list: {},
insert: function(src,onevent,MistVideo){
var scripts = this;
if (MistVideo) {
//register so we can remove it on unload
MistVideo.errorListeners.push({
src: src,
onevent: onevent
});
}
if (src in this.list) {
//already present
//register to error listening
this.list[src].subscribers.push(onevent.onerror);
//execute onload
if ("onload" in onevent) {
if (this.hasLoaded) {
onevent.onload();
}
else {
MistUtil.event.addListener(this.list[src].tag,"load",onevent.onload);
}
}
return;
}
var scripttag = document.createElement("script");
scripttag.hasLoaded = false;
scripttag.setAttribute("src",src);
scripttag.setAttribute("crossorigin","anonymous"); //must be set to get info about errors thrown
document.head.appendChild(scripttag);
scripttag.onerror = function(e){
onevent.onerror(e);
}
scripttag.onload = function(e){
this.hasLoaded = true;
if (!MistVideo.destroyed) { onevent.onload(e); }
}
scripttag.addEventListener("error",function(e){
onevent.onerror(e);
});
//error catching
var oldonerror = false;
if (window.onerror) {
oldonerror = window.onerror;
}
window.onerror = function(message,source,line,column,error){
if (oldonerror) {
oldonerror.apply(this,arguments);
}
if (source == src) {
onevent.onerror(error);
for (var i in scripts.list[src].subscribers) {
scripts.list[src].subscribers[i](error);
}
}
};
this.list[src] = {
subscribers: [onevent.onerror],
tag: scripttag
};
return scripttag;
}
},
tracks: {
parse: function(metaTracks){
var output = {};
for (var i in metaTracks) {
var track = MistUtil.object.extend({},metaTracks[i]);
if (track.type == "meta") {
track.type = track.codec;
track.codec = "meta";
}
if (!(track.type in output)) { output[track.type] = {}; }
output[track.type][track.trackid] = track;
//make up something logical for the track displayname
var name = {};
for (var j in track) {
switch (j) {
case "width":
name[j] = track.width+"×"+track.height;
break;
case "bps":
if (track.codec == "meta") { continue; }
if (track.bps > 0) {
var val;
if (track.bps > 1024*1024/8) {
val = Math.round(track.bps/1024/1024*8)+"mbps";
}
else {
val = Math.round(track.bps/1024*8)+"kbps";
}
name[j] = val;
}
break;
case "fpks":
if (track.fpks > 0) {
name[j] = track.fpks/1e3+"fps";
}
break;
case "channels":
if (track.channels > 0) {
name[j] = (track.channels == 1 ? "Mono" : (track.channels == 2 ? "Stereo" : "Surround ("+track.channels+"ch)"));
}
break;
case "rate":
name[j] = Math.round(track.rate)+"Khz";
break;
case "language":
if (track[j] != "Undetermined") { name[j] = track[j]; }
break;
case "codec":
if (track.codec == "meta") { continue; }
name[j] = track[j];
break;
}
}
track.describe = name;
}
//filter what to display based on what is different
for (var type in output) {
var equal = false;
for (var i in output[type]) {
if (!equal) {
//fill equal with all the keys and values of the first track of this type
equal = MistUtil.object.extend({},output[type][i].describe);
continue;
}
if (MistUtil.object.keys(output[type]).length > 1) {
//if there is more than one track of this type
for (var j in output[type][i].describe) {
if (equal[j] != output[type][i].describe[j]) {
//remove key from equal if not equal
delete equal[j];
}
}
}
}
//apply
for (var i in output[type]) {
var different = {};
var same = {};
for (var j in output[type][i].describe) {
if (!(j in equal)){
different[j] = output[type][i].describe[j];
}
else {
same[j] = output[type][i].describe[j];
}
}
output[type][i].different = different;
output[type][i].same = same;
var d = MistUtil.object.values(different);
output[type][i].displayName = (d.length ? d.join(", ") : MistUtil.object.values(output[type][i].describe).join(" "));
}
}
return output;
}
},
isTouchDevice: function(){
return (('ontouchstart' in window) || (navigator.msMaxTouchPoints > 0));
//return true;
},
getPos: function(element,cursorLocation){
var style = element.currentStyle || window.getComputedStyle(element, null);
var zoom = 1;
var node = element;
while (node) {
if ((node.style.zoom) && (node.style.zoom != "")) {
zoom *= parseFloat(node.style.zoom,10);
}
node = node.parentElement;
}
var pos0 = element.getBoundingClientRect().left - (parseInt(element.borderLeftWidth,10) || 0);
var width = element.getBoundingClientRect().width;;
var perc = Math.max(0,((cursorLocation.clientX/zoom) - pos0) / width);
perc = Math.min(perc,1);
return perc;
},
createGraph: function(data,options){
var ns = "http://www.w3.org/2000/svg";
var svg = document.createElementNS(ns,"svg");
svg.setAttributeNS(null,"height","100%");
svg.setAttributeNS(null,"width","100%");
svg.setAttributeNS(null,"class","mist icon graph");
svg.setAttributeNS(null,"preserveAspectRatio","none");
var x_correction = data.x[0];
var lasty = data.y[0];
if (options.differentiate) {
for (var i = 1; i < data.y.length; i++) {
var diff = data.y[i] - lasty;
lasty = data.y[i];
data.y[i] = diff;
}
}
var path = [];
var area = {
x: {
min: data.x[0] - x_correction,
max: data.x[0] - x_correction
},
y: {
min: data.y[0]*-1,
max: data.y[0]*-1
}
};
function updateMinMax(x,y) {
if (arguments.length) {
area.x.min = Math.min(area.x.min,x);
area.x.max = Math.max(area.x.max,x);
area.y.min = Math.min(area.y.min,y*-1);
area.y.max = Math.max(area.y.max,y*-1);
}
else {
//reprocess the entire path
var d = path[0].split(",");
area = {
x: {
min: d[0],
max: d[0]
},
y: {
min: d[1],
max: d[1]
}
};
for (var i = 1; i < path.length; i++) {
var d = path[i].split(",");
updateMinMax(d[0],d[1]*-1);
}
}
}
path.push([data.x[0] - x_correction,data.y[0]*-1].join(","));
for (var i = 1; i < data.y.length; i++) {
updateMinMax(data.x[i] - x_correction,data.y[i]*-1);
path.push("L "+[data.x[i] - x_correction,data.y[i]*-1].join(","));
}
//define gradient
var defs = document.createElementNS(ns,"defs");
svg.appendChild(defs);
var gradient = document.createElementNS(ns,"linearGradient");
defs.appendChild(gradient);
gradient.setAttributeNS(null,"id",MistUtil.createUnique());
gradient.setAttributeNS(null,"gradientUnits","userSpaceOnUse");
gradient.innerHTML += '<stop offset="0" stop-color="green"/>';
gradient.innerHTML += '<stop offset="0.33" stop-color="yellow"/>';
gradient.innerHTML += '<stop offset="0.66" stop-color="orange"/>';
gradient.innerHTML += '<stop offset="1" stop-color="red"/>';
function updateViewBox() {
if ("x" in options) {
if ("min" in options.x) { area.x.min = options.x.min; }
if ("max" in options.x) { area.x.max = options.x.max; }
if ("count" in options.x) { area.x.min = area.x.max - options.x.count; }
}
if ("y" in options) {
if ("min" in options.y) { area.y.min = options.y.max*-1; }
if ("max" in options.y) { area.y.max = options.y.min*-1; }
}
svg.setAttributeNS(null,"viewBox",[area.x.min,area.y.min,area.x.max - area.x.min,area.y.max - area.y.min].join(" "));
gradient.setAttributeNS(null,"x1",0);
gradient.setAttributeNS(null,"x2",0);
if (options.reverseGradient) {
gradient.setAttributeNS(null,"y1",area.y.max);
gradient.setAttributeNS(null,"y2",area.y.min);
}
else {
gradient.setAttributeNS(null,"y1",area.y.min);
gradient.setAttributeNS(null,"y2",area.y.max);
}
}
updateViewBox();
var line = document.createElementNS(ns,"path");
svg.appendChild(line);
//line.setAttributeNS(null,"vector-effect","non-scaling-stroke");
line.setAttributeNS(null,"stroke-width","0.1");
line.setAttributeNS(null,"fill","none");
line.setAttributeNS(null,"stroke","url(#"+gradient.getAttribute("id")+")");
line.setAttributeNS(null,"d","M"+path.join(" L"));
line.addData = function(newData) {
if (options.differentiate) {
var diff = newData.y - lasty;
lasty = newData.y;
newData.y = diff;
}
path.push([newData.x - x_correction,newData.y*-1].join(","));
if (options.x && options.x.count) {
if (path.length > options.x.count) {
path.shift();
updateMinMax();
}
}
updateMinMax(newData.x - x_correction,newData.y*-1);
this.setAttributeNS(null,"d","M"+path.join(" L"));
updateViewBox();
};
svg.addData = function(newData){
line.addData(newData);
};
return svg;
},
getBrowser: function(){
var ua = window.navigator.userAgent;
if ((ua.indexOf("MSIE ") >= 0) || (ua.indexOf("Trident/") >= 0)) {
return "ie";
}
if (ua.indexOf("Edge/") >= 0) {
return "edge";
}
if ((ua.indexOf("Opera") >= 0) || (ua.indexOf('OPR') >= 0)) {
return "opera";
}
if (ua.indexOf("Chrome") >= 0) {
return "chrome";
}
if (ua.indexOf("Safari") >= 0) {
return "safari";
}
if (ua.indexOf("Firefox") >= 0) {
return "firefox";
}
return false; //unknown
}
};

View file

@ -1,162 +1,216 @@
mistplayers.dashjs = { mistplayers.dashjs = {
name: 'Dash.js Player', name: "Dash.js player",
version: '1.2', mimes: ["dash/video/mp4"/*,"html5/application/vnd.ms-ss"*/],
mimes: ['dash/video/mp4'], priority: MistUtil.object.keys(mistplayers).length + 1,
priority: Object.keys(mistplayers).length + 1,
isMimeSupported: function (mimetype) { isMimeSupported: function (mimetype) {
return (this.mimes.indexOf(mimetype) == -1 ? false : true); return (MistUtil.array.indexOf(this.mimes,mimetype) == -1 ? false : true);
}, },
isBrowserSupported: function (mimetype,source,options) { isBrowserSupported: function (mimetype,source,MistVideo) {
if ((options.host.substr(0,7) == 'http://') && (source.url.substr(0,8) == 'https://')) { return false; }
return (('MediaSource' in window) && (location.protocol != 'file:')); //check for http/https mismatch
if (location.protocol != MistUtil.http.url.split(source.url).protocol) {
MistVideo.log("HTTP/HTTPS mismatch for this source");
return false;
}
//don't use dashjs if this location is loaded over file://
if (location.protocol == "file:") {
MistVideo.log("This source ("+mimetype+") won't load if the page is run via file://");
return false;
}
return ("MediaSource" in window);
}, },
player: function(){this.onreadylist = [];} player: function(){this.onreadylist = [];},
scriptsrc: function(host) { return host+"/dashjs.js"; }
}; };
var p = mistplayers.dashjs.player; var p = mistplayers.dashjs.player;
p.prototype = new MistPlayer(); p.prototype = new MistPlayer();
p.prototype.build = function (options,callback) { p.prototype.build = function (MistVideo,callback) {
var cont = document.createElement('div');
cont.className = 'mistplayer';
var me = this; var me = this;
function onplayerload () { this.onDashLoad = function() {
var ele = me.getElement('video'); if (MistVideo.destroyed) { return; }
ele.className = '';
cont.appendChild(ele);
ele.width = options.width;
ele.height = options.height;
if (options.autoplay) { MistVideo.log("Building DashJS player..");
ele.setAttribute('autoplay','');
}
if (options.loop) {
ele.setAttribute('loop','');
}
if (options.poster) {
ele.setAttribute('poster',options.poster);
}
if (options.controls) {
if (options.controls == 'stock') {
ele.setAttribute('controls','');
}
else {
me.buildMistControls();
}
}
ele.addEventListener('error',function(e){ var ele = document.createElement("video");
var msg;
if ('message' in e) { if ("Proxy" in window) {
msg = e.message; var overrides = {
get: {},
set: {}
};
MistVideo.player.api = new Proxy(ele,{
get: function(target, key, receiver){
if (key in overrides.get) {
return overrides.get[key].apply(target, arguments);
} }
else { var method = target[key];
msg = 'readyState: '; if (typeof method === "function"){
switch (me.element.readyState) { return function () {
case 0: return method.apply(target, arguments);
msg += 'HAVE_NOTHING';
break;
case 1:
msg += 'HAVE_METADATA';
break;
case 2:
msg += 'HAVE_CURRENT_DATA';
break;
case 3:
msg += 'HAVE_FUTURE_DATA';
break;
case 4:
msg += 'HAVE_ENOUGH_DATA';
break;
}
msg += ' networkState: ';
switch (me.element.networkState) {
case 0:
msg += 'NETWORK_EMPTY';
break;
case 1:
msg += 'NETWORK_IDLE';
break;
case 2:
msg += 'NETWORK_LOADING';
break;
case 3:
msg += 'NETWORK_NO_SOURCE';
break;
} }
} }
//prevent onerror loops return method;
if (e.target == me.element) { },
e.message = msg; set: function(target, key, value) {
if (key in overrides.set) {
return overrides.set[key].call(target,value);
} }
else { return target[key] = value;
me.adderror(msg);
} }
}); });
var events = ['abort','canplay','canplaythrough','durationchange','emptied','ended','interruptbegin','interruptend','loadeddata','loadedmetadata','loadstart','pause','play','playing','ratechange','seeked','seeking','stalled','volumechange','waiting'];
for (var i in events) { if (MistVideo.info.type == "live") {
ele.addEventListener(events[i],function(e){ overrides.get.duration = function(){
me.addlog('Player event fired: '+e.type); //this should indicate the end of Mist's buffer
var buffer_end = 0;
if (this.buffered.length) {
buffer_end = this.buffered.end(this.buffered.length-1)
}
var time_since_buffer = (new Date().getTime() - MistVideo.player.api.lastProgress.getTime())*1e-3;
return buffer_end + time_since_buffer + -1*MistVideo.player.api.liveOffset + 45;
};
overrides.set.currentTime = function(value){
var offset = value - MistVideo.player.api.duration;
//MistVideo.player.api.liveOffset = offset;
MistVideo.log("Seeking to "+MistUtil.format.time(value)+" ("+Math.round(offset*-10)/10+"s from live)");
MistVideo.video.currentTime = value;
//.player.api.setSource(MistUtil.http.url.addParam(MistVideo.source.url,{startunix:offset}));
}
MistUtil.event.addListener(ele,"progress",function(){
MistVideo.player.api.lastProgress = new Date();
}); });
MistVideo.player.api.lastProgress = new Date();
MistVideo.player.api.liveOffset = 0;
}
}
else {
me.api = ele;
}
if (MistVideo.options.autoplay) {
ele.setAttribute("autoplay","");
}
if ((MistVideo.options.loop) && (MistVideo.info.type != "live")) {
ele.setAttribute("loop","");
}
if (MistVideo.options.poster) {
ele.setAttribute("poster",MistVideo.options.poster);
}
if (MistVideo.options.controls == "stock") {
ele.setAttribute("controls","");
} }
var player = dashjs.MediaPlayer().create(); var player = dashjs.MediaPlayer().create();
player.getDebug().setLogToBrowserConsole(false); //player.getDebug().setLogToBrowserConsole(false);
player.initialize(ele,options.src,true); player.initialize(ele,MistVideo.source.url,MistVideo.options.autoplay);
me.dash = player; me.dash = player;
me.src = options.src;
//add listeners for events that we can log
me.addlog('Built html'); var skipEvents = ["METRIC_ADDED","METRIC_CHANGED","METRICS_CHANGED","FRAGMENT_LOADING_STARTED","FRAGMENT_LOADING_COMPLETED","LOG","PLAYBACK_TIME_UPDATED","PLAYBACK_PROGRESS"];
callback(cont); for (var i in dashjs.MediaPlayer.events) {
if (skipEvents.indexOf(i) < 0) {
me.dash.on(dashjs.MediaPlayer.events[i],function(e){
MistVideo.log("Player event fired: "+e.type);
});
}
} }
if ('dash' in window) { MistVideo.player.setSize = function(size){
onplayerload(); this.api.style.width = size.width+"px";
} this.api.style.height = size.height+"px";
else {
//load the dashjs player
var scripttag = document.createElement('script');
scripttag.src = options.host+'/dashjs.js';
me.addlog('Retrieving dashjs player code from '+scripttag.src);
document.head.appendChild(scripttag);
scripttag.onerror = function(){
me.askNextCombo('Failed to load dashjs.js');
}
scripttag.onload = function(){
onplayerload();
}
}
}
p.prototype.play = function(){ return this.element.play(); };
p.prototype.pause = function(){ return this.element.pause(); };
p.prototype.volume = function(level){
if (typeof level == 'undefined' ) { return this.element.volume; }
return this.element.volume = level;
}; };
p.prototype.play = function(){ return this.element.play(); }; MistVideo.player.api.setSource = function(url) {
p.prototype.load = function(){ return this.element.load(); }; MistVideo.player.dash.attachSource(url);
if (document.fullscreenEnabled || document.webkitFullscreenEnabled || document.mozFullScreenEnabled || document.msFullscreenEnabled) {
p.prototype.fullscreen = function(){
if(this.element.requestFullscreen) {
return this.element.requestFullscreen();
} else if(this.element.mozRequestFullScreen) {
return this.element.mozRequestFullScreen();
} else if(this.element.webkitRequestFullscreen) {
return this.element.webkitRequestFullscreen();
} else if(this.element.msRequestFullscreen) {
return this.element.msRequestFullscreen();
}
}; };
}
p.prototype.resize = function(size){ //trackswitching
this.element.width = size.width; MistVideo.player.api.setTrack = function(type,id){
this.element.height = size.height; var meta = MistUtil.tracks.parse(MistVideo.info.meta.tracks);
}; if ((!(type in meta)) || ((!(id in meta[type]) && (id != 0)))) {
p.prototype.updateSrc = function(src){ MistVideo.log("Skipping trackselection of "+type+" track "+id+" because it does not exist");
if (src == '') {
this.dash.reset();
return; return;
} }
this.dash.attachSource(src);
return true; //figure out what the track number is
//whyyyy did it have to be reverse order
var n = me.dash.getBitrateInfoListFor("video").length - 1;
for (var i in MistVideo.info.meta.tracks) {
var t = MistVideo.info.meta.tracks[i];
if (t.type == type) {
if (t.trackid == id) { break; }
n--;
}
}
me.dash.setAutoSwitchQualityFor(type,false); //turn off ABR rules //TODO do we want this by default?
me.dash.setFastSwitchEnabled(true); //show the new track asap
me.dash.setQualityFor(type,n);
//dash does change the track, but is appended to the buffer, so it seems to take a while..
}
//react to automatic trackswitching
me.dash.on("qualityChangeRendered",function(e){
//the newQuality-th track of type mediaType is being selected
//figure out the track id
//whyyyy did it have to be reverse order
var n = me.dash.getBitrateInfoListFor("video").length - 1;
var id;
for (var i in MistVideo.info.meta.tracks) {
var t = MistVideo.info.meta.tracks[i];
if (t.type == e.mediaType) {
if (e.newQuality == n) {
id = t.trackid;
break;
}
n--;
}
}
//create an event to pass this to the skin
MistUtil.event.send("playerUpdate_trackChanged",{
type: e.mediaType,
trackid: id
},MistVideo.video);
});
//dashjs keeps on spamming the stalled icon >_>
MistUtil.event.addListener(ele,"progress",function(e){
if (MistVideo.container.getAttribute("data-loading") == "stalled") {
MistVideo.container.removeAttribute("data-loading");
}
});
me.api.unload = function(){
me.dash.reset();
}; };
MistVideo.log("Built html");
callback(ele);
}
if ("dashjs" in window) {
this.onDashLoad();
}
else {
var scripttag = MistUtil.scripts.insert(MistVideo.urlappend(mistplayers.dashjs.scriptsrc(MistVideo.options.host)),{
onerror: function(e){
var msg = "Failed to load dashjs.js";
if (e.message) { msg += ": "+e.message; }
MistVideo.showError(msg);
},
onload: me.onDashLoad
},MistVideo);
}
}

View file

@ -1,26 +1,37 @@
mistplayers.flash_strobe = { mistplayers.flash_strobe = {
name: 'Strobe Flash Media Playback', name: "Strobe Flash media playback",
version: '1.1', mimes: ["flash/10","flash/11","flash/7"],
mimes: ['flash/10','flash/11','flash/7'], priority: MistUtil.object.keys(mistplayers).length + 1,
priority: Object.keys(mistplayers).length + 1,
isMimeSupported: function (mimetype) { isMimeSupported: function (mimetype) {
return (this.mimes.indexOf(mimetype) == -1 ? false : true); return (this.mimes.indexOf(mimetype) == -1 ? false : true);
}, },
isBrowserSupported: function (mimetype,source,options) { isBrowserSupported: function (mimetype,source,MistVideo) {
//check for http / https crossovers
if ((options.host.substr(0,7) == 'http://') && (source.url.substr(0,8) == 'https://')) { return false; } //check for http/https mismatch
if ((MistUtil.http.url.split(source.url).protocol.slice(0,4) == "http") && (location.protocol != MistUtil.http.url.split(source.url).protocol)) {
MistVideo.log("HTTP/HTTPS mismatch for this source");
return false;
}
var version = 0; var version = 0;
try { try {
// check in the mimeTypes // check in the mimeTypes
version = navigator.mimeTypes['application/x-shockwave-flash'].enabledPlugin.description.replace(/([^0-9\.])/g, '').split('.')[0]; var plugin = navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin;
if (plugin.version) { version = plugin.version.split(".")[0]; }
else { version = plugin.description.replace(/([^0-9\.])/g, "").split(".")[0]; }
} catch(e){} } catch(e){}
try { try {
// for our special friend IE // for our special friend IE
version = new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable("$version").replace(/([^0-9\,])/g, '').split(',')[0]; version = new ActiveXObject("ShockwaveFlash.ShockwaveFlash").GetVariable("$version").replace(/([^0-9\,])/g, "").split(",")[0];
} catch(e){} } catch(e){}
var mimesplit = mimetype.split('/'); if (!version) {
//flash is not enabled? Might need to ask permission first.
//TODO how? just let it build?
return false;
}
var mimesplit = mimetype.split("/");
return Number(version) >= Number(mimesplit[mimesplit.length-1]); return Number(version) >= Number(mimesplit[mimesplit.length-1]);
}, },
@ -28,39 +39,53 @@ mistplayers.flash_strobe = {
}; };
var p = mistplayers.flash_strobe.player; var p = mistplayers.flash_strobe.player;
p.prototype = new MistPlayer(); p.prototype = new MistPlayer();
p.prototype.build = function (options,callback) { p.prototype.build = function (MistVideo,callback) {
var ele = document.createElement("object");
var e = document.createElement("embed");
ele.appendChild(e);
function build(source) {
var options = MistVideo.options;
function createParam(name,value) { function createParam(name,value) {
var p = document.createElement('param'); var p = document.createElement("param");
p.setAttribute('name',name); p.setAttribute("name",name);
p.setAttribute('value',value); p.setAttribute("value",value);
return p; return p;
} }
MistUtil.empty(ele);
var ele = this.getElement('object'); ele.appendChild(createParam("movie",MistVideo.urlappend(options.host+MistVideo.source.player_url)));
var flashvars = "src="+encodeURIComponent(source)+"&controlBarMode="+(options.controls ? "floating" : "none")+"&initialBufferTime=0.5&expandedBufferTime=5&minContinuousPlaybackTime=3"+(options.live ? "&streamType=live" : "")+(options.autoplay ? "&autoPlay=true" : "" );
ele.setAttribute('width',options.width); ele.appendChild(createParam("flashvars",flashvars));
ele.setAttribute('height',options.height); ele.appendChild(createParam("allowFullScreen","true"));
ele.appendChild(createParam("wmode","direct"));
ele.appendChild(createParam('movie',options.host+options.source.player_url));
var flashvars = 'src='+encodeURIComponent(options.src)+'&controlBarMode='+(options.controls ? 'floating' : 'none')+'&initialBufferTime=0.5&expandedBufferTime=5&minContinuousPlaybackTime=3'+(options.live ? '&streamType=live' : '')+(options.autoplay ? '&autoPlay=true' : '' );
ele.appendChild(createParam('flashvars',flashvars));
ele.appendChild(createParam('allowFullScreen','true'));
ele.appendChild(createParam('wmode','direct'));
if (options.autoplay) { if (options.autoplay) {
ele.appendChild(createParam('autoPlay','true')); ele.appendChild(createParam("autoPlay","true"));
} }
var e = document.createElement('embed'); e.setAttribute("src",MistVideo.urlappend(MistVideo.source.player_url));
ele.appendChild(e); e.setAttribute("type","application/x-shockwave-flash");
e.setAttribute('src',options.source.player_url); e.setAttribute("allowfullscreen","true");
e.setAttribute('type','application/x-shockwave-flash'); e.setAttribute("flashvars",flashvars);
e.setAttribute('allowfullscreen','true'); }
e.setAttribute('width',options.width); build(MistVideo.source.url);
e.setAttribute('height',options.height);
e.setAttribute('flashvars',flashvars);
this.api = {};
this.setSize = function(size){
ele.setAttribute("width",size.width);
ele.setAttribute("height",size.height);
e.setAttribute("width",size.width);
e.setAttribute("height",size.height);
};
this.setSize(MistVideo.calcSize());
this.onready(function(){
if (MistVideo.container) { MistVideo.container.removeAttribute("data-loading"); }
});
this.api.setSource = function(url){
build(url);
}
this.addlog('Built html'); MistVideo.log("Built html");
callback(ele); callback(ele);
} }

View file

@ -1,29 +1,87 @@
mistplayers.html5 = { mistplayers.html5 = {
name: 'HTML5 video player', name: "HTML5 video player",
version: '1.1', mimes: ["html5/application/vnd.apple.mpegurl","html5/video/mp4","html5/video/ogg","html5/video/webm","html5/audio/mp3","html5/audio/webm","html5/audio/ogg","html5/audio/wav"],
mimes: ['html5/application/vnd.apple.mpegurl','html5/video/mp4','html5/video/ogg','html5/video/webm','html5/audio/mp3','html5/audio/webm','html5/audio/ogg','html5/audio/wav'], priority: MistUtil.object.keys(mistplayers).length + 1,
priority: Object.keys(mistplayers).length + 1,
isMimeSupported: function (mimetype) { isMimeSupported: function (mimetype) {
return (this.mimes.indexOf(mimetype) == -1 ? false : true); return (MistUtil.array.indexOf(this.mimes,mimetype) == -1 ? false : true);
}, },
isBrowserSupported: function (mimetype,source,options,streaminfo) { isBrowserSupported: function (mimetype,source,MistVideo) {
if ((['iPad','iPhone','iPod','MacIntel'].indexOf(navigator.platform) != -1) && (mimetype == 'html5/video/mp4')) { return false; }
var support = false; //check for http/https mismatch
var shortmime = mimetype.split('/'); if (location.protocol != MistUtil.http.url.split(source.url).protocol) {
shortmime.shift(); if ((location.protocol == "file:") && (MistUtil.http.url.split(source.url).protocol == "http:")) {
MistVideo.log("This page was loaded over file://, the player might not behave as intended.");
if ((shortmime[0] == 'audio') && (streaminfo.height) && (!options.forceType) && (!options.forceSource)) { }
//claim you don't support audio-only playback if there is video data, unless this mime is being forced else {
MistVideo.log("HTTP/HTTPS mismatch for this source");
return false; return false;
} }
}
var support = false;
var shortmime = mimetype.split("/");
shortmime.shift();
try { try {
var v = document.createElement((shortmime[0] == 'audio' ? 'audio' : 'video')); shortmime = shortmime.join("/");
shortmime = shortmime.join('/')
if ((v) && (v.canPlayType(shortmime) != "")) { function test(mime) {
support = v.canPlayType(shortmime); var v = document.createElement("video");
if ((v) && (v.canPlayType(mime) != "")) {
support = v.canPlayType(mime);
} }
return support;
}
if (shortmime == "video/mp4") {
function translateCodec(track) {
function bin2hex(index) {
return ("0"+track.init.charCodeAt(index).toString(16)).slice(-2);
}
switch (track.codec) {
case "AAC":
return "mp4a.40.2";
case "MP3":
return "mp3";
//return "mp4a.40.34";
case "AC3":
return "ec-3";
case "H264":
return "avc1."+bin2hex(1)+bin2hex(2)+bin2hex(3);
case "HEVC":
return "hev1."+bin2hex(1)+bin2hex(6)+bin2hex(7)+bin2hex(8)+bin2hex(9)+bin2hex(10)+bin2hex(11)+bin2hex(12);
default:
return track.codec.toLowerCase();
}
}
var codecs = {};
for (var i in MistVideo.info.meta.tracks) {
if (MistVideo.info.meta.tracks[i].type != "meta") {
codecs[translateCodec(MistVideo.info.meta.tracks[i])] = 1;
}
}
codecs = MistUtil.object.keys(codecs);
if (codecs.length) {
if (codecs.length > source.simul_tracks) {
//not all of the tracks have to work
var working = 0;
for (var i in codecs) {
var s = test(shortmime+";codecs=\""+codecs[i]+"\"");
if (s) {
working++;
}
}
return (working >= source.simul_tracks);
}
shortmime += ";codecs=\""+codecs.join(",")+"\"";
}
}
support = test(shortmime);
} catch(e){} } catch(e){}
return support; return support;
}, },
@ -34,206 +92,159 @@ mistplayers.html5 = {
}; };
var p = mistplayers.html5.player; var p = mistplayers.html5.player;
p.prototype = new MistPlayer(); p.prototype = new MistPlayer();
p.prototype.build = function (options,callback) { p.prototype.build = function (MistVideo,callback) {
var cont = document.createElement('div'); var shortmime = MistVideo.source.type.split("/");
cont.className = 'mistplayer';
var me = this; //to allow nested functions to access the player class itself
var shortmime = options.source.type.split('/');
shortmime.shift(); shortmime.shift();
var video = document.createElement("video");
var ele = this.getElement((shortmime[0] == 'audio' ? 'audio' : 'video')); //TODO verify: not required if player is loaded from same domain as it should always be when not in dev mode?
ele.className = ''; video.setAttribute("crossorigin","anonymous");//required for subs, breaks ogg?
cont.appendChild(ele);
if (options.source.type != "html5/video/ogg") { var source = document.createElement("source");
ele.crossOrigin = 'anonymous'; //required for subtitles, but if ogg, the video won't load source.setAttribute("src",MistVideo.source.url);
} video.source = source;
video.appendChild(source);
source.type = shortmime.join("/");
if (shortmime[0] == 'audio') { //apply options
this.setTracks = function() { return false; } var attrs = ["autoplay","loop","poster"];
this.fullscreen = false; for (var i in attrs) {
cont.className += ' audio'; var attr = attrs[i];
} if (MistVideo.options[attr]) {
video.setAttribute(attr,(MistVideo.options[attr] === true ? "" : MistVideo.options[attr]));
this.addlog('Building HTML5 player..');
var source = document.createElement('source');
source.setAttribute('src',options.src);
this.source = source;
ele.appendChild(source);
source.type = shortmime.join('/');
this.addlog('Adding '+source.type+' source @ '+options.src);
if ((this.tracks.subtitle.length) && (this.subtitle)) {
for (var i in this.tracks.subtitle) {
var t = document.createElement('track');
ele.appendChild(t);
t.kind = 'subtitles';
t.label = this.tracks.subtitle[i].desc;
t.srclang = this.tracks.subtitle[i].lang;
t.src = this.subtitle+'?track='+this.tracks.subtitle[i].trackid;
} }
} }
if (MistVideo.options.controls == "stock") {
ele.width = options.width; video.setAttribute("controls","");
ele.height = options.height;
ele.style.width = options.width+'px';
ele.style.height = options.height+'px';
if (options.autoplay) {
ele.setAttribute('autoplay','');
}
if (options.loop) {
ele.setAttribute('loop','');
}
if (options.poster) {
ele.setAttribute('poster',options.poster);
}
if (options.controls) {
if ((options.controls == 'stock') || (!this.buildMistControls())) {
//MistControls have failed to build in the if condition
ele.setAttribute('controls','');
} }
if (MistVideo.info.type == "live") {
video.loop = false;
} }
cont.onclick = function(){ if (("Proxy" in window) && ("Reflect" in window)) {
if (ele.paused) { ele.play(); } var overrides = {
else { ele.pause(); } get: {},
set: {}
}; };
this.addlog('Built html'); MistVideo.player.api = new Proxy(video,{
get: function(target, key, receiver){
//forward events if (key in overrides.get) {
ele.addEventListener('error',function(e){ return overrides.get[key].apply(target, arguments);
if (!e.isTrusted) { return; } //don't trigger on errors we have thrown ourselves }
var method = target[key];
if (options.live) { if (typeof method === "function"){
if ((ele.error) && (ele.error.code == 3)) { return function () {
e.stopPropagation(); //dont let this error continue to prevent the core from trying to handle the error return method.apply(target, arguments);
me.load(); }
me.cancelAskNextCombo(); }
e.message = 'Handled decoding error'; return method;
me.addlog('Decoding error: reloading..'); },
me.report({ set: function(target, key, value) {
type: 'playback', if (key in overrides.set) {
warn: 'A decoding error was encountered, but handled' return overrides.set[key].call(target,value);
}
return target[key] = value;
}
}); });
return;
} if (MistVideo.source.type == "html5/audio/mp3") {
overrides.set.currentTime = function(){
MistVideo.log("Seek attempted, but MistServer does not currently support seeking in MP3.");
return false;
} }
var msg;
if ('message' in e) {
msg = e.message;
} }
else if ((e.target.tagName == 'SOURCE') && (e.target.getAttribute('src') == '')) { if (MistVideo.info.type == "live") {
e.stopPropagation();
//this error is triggered because the unload function was fired overrides.get.duration = function(){
return; //this should indicate the end of Mist's buffer
var buffer_end = 0;
if (this.buffered.length) {
buffer_end = this.buffered.end(this.buffered.length-1)
}
var time_since_buffer = (new Date().getTime() - MistVideo.player.api.lastProgress.getTime())*1e-3;
return buffer_end + time_since_buffer - MistVideo.player.api.liveOffset;
};
overrides.set.currentTime = function(value){
var offset = value - MistVideo.player.api.duration;
MistVideo.player.api.liveOffset = offset;
MistVideo.log("Seeking to "+MistUtil.format.time(value)+" ("+Math.round(offset*-10)/10+"s from live)");
MistVideo.player.api.setSource(MistUtil.http.url.addParam(MistVideo.source.url,{startunix:offset}));
}
MistUtil.event.addListener(video,"progress",function(){
MistVideo.player.api.lastProgress = new Date();
});
MistVideo.player.api.lastProgress = new Date();
MistVideo.player.api.liveOffset = 0;
MistUtil.event.addListener(video,"pause",function(){
MistVideo.player.api.pausedAt = new Date();
});
overrides.get.play = function(){
return function(){
if ((MistVideo.player.api.paused) && (MistVideo.player.api.pausedAt) && ((new Date()) - MistVideo.player.api.pausedAt > 5e3)) {
video.load();
MistVideo.log("Reloading source..");
}
return video.play.apply(video, arguments);
}
};
if (MistVideo.source.type == "html5/video/mp4") {
overrides.get.currentTime = function(){
return this.currentTime - MistVideo.player.api.liveOffset + MistVideo.info.lastms * 1e-3;
}
}
} }
else { else {
msg = 'readyState: '; if (!isFinite(video.duration)) {
switch (me.element.readyState) { var duration = 0;
case 0: for (var i in MistVideo.info.meta.tracks) {
msg += 'HAVE_NOTHING'; duration = Math.max(duration,MistVideo.info.meta.tracks[i].lastms);
break; }
case 1: overrides.get.duration = function(){
msg += 'HAVE_METADATA'; if (isFinite(this.duration)) { return this.duration; }
break; return duration * 1e-3;
case 2:
msg += 'HAVE_CURRENT_DATA';
break;
case 3:
msg += 'HAVE_FUTURE_DATA';
break;
case 4:
msg += 'HAVE_ENOUGH_DATA';
break;
} }
msg += ' networkState: ';
switch (me.element.networkState) {
case 0:
msg += 'NETWORK_EMPTY';
break;
case 1:
msg += 'NETWORK_IDLE';
break;
case 2:
msg += 'NETWORK_LOADING';
break;
case 3:
msg += 'NETWORK_NO_SOURCE';
break;
} }
} }
me.adderror(msg);
});
var events = ['abort','canplay','canplaythrough','durationchange','emptied','ended','interruptbegin','interruptend','loadeddata','loadedmetadata','loadstart','pause','play','playing','ratechange','seeked','seeking','stalled','volumechange','waiting','progress'];
for (var i in events) {
ele.addEventListener(events[i],function(e){
me.addlog('Player event fired: '+e.type);
});
}
callback(cont);
}
p.prototype.play = function(){ return this.element.play(); };
p.prototype.pause = function(){ return this.element.pause(); };
p.prototype.volume = function(level){
if (typeof level == 'undefined' ) { return this.element.volume; }
return this.element.volume = level;
};
p.prototype.loop = function(bool){
if (typeof bool == 'undefined') {
return this.element.loop;
}
return this.element.loop = bool;
};
p.prototype.load = function(){
var load;
if (this.element.paused) {
load = this.element.load();
} }
else { else {
//sometimes there is a play / pause interrupt: try again MistVideo.player.api = video;
//TODO figure out if this happens on paused or on playing }
MistVideo.player.api.setSource = function(url) {
if (url != this.source.src) {
this.source.src = url;
this.load(); this.load();
return;
} }
};
MistVideo.player.api.setSubtitle = function(trackmeta) {
//remove previous subtitles
var tracks = video.getElementsByTagName("track");
for (var i = tracks.length - 1; i >= 0; i--) {
video.removeChild(tracks[i]);
}
if (trackmeta) { //if the chosen track exists
//add the new one
var track = document.createElement("track");
video.appendChild(track);
track.kind = "subtitles";
track.label = trackmeta.label;
track.srclang = trackmeta.lang;
track.src = trackmeta.src;
track.setAttribute("default","");
}
};
MistVideo.player.setSize = function(size){
this.api.style.width = size.width+"px";
this.api.style.height = size.height+"px";
};
//this helps to prevent the player from just showing a black screen after a reload callback(video);
if (this.element.paused) {
var me = this;
var unpause = function(){
if (me.element.paused) {
me.element.play();
} }
me.element.removeEventListener('progress',unpause);
}
this.element.addEventListener('progress',unpause);
}
return load;
};
if (document.fullscreenEnabled || document.webkitFullscreenEnabled || document.mozFullScreenEnabled || document.msFullscreenEnabled) {
p.prototype.fullscreen = function(){
if(this.element.requestFullscreen) {
return this.element.requestFullscreen();
} else if(this.element.mozRequestFullScreen) {
return this.element.mozRequestFullScreen();
} else if(this.element.webkitRequestFullscreen) {
return this.element.webkitRequestFullscreen();
} else if(this.element.msRequestFullscreen) {
return this.element.msRequestFullscreen();
}
};
}
p.prototype.updateSrc = function(src){
this.source.setAttribute('src',src);
return true;
};
p.prototype.resize = function(size){
this.element.width = size.width;
this.element.height = size.height;
};

View file

@ -1,23 +0,0 @@
mistplayers.img = {
name: 'HTML img tag',
version: '1.1',
mimes: ['html5/image/jpeg'],
priority: Object.keys(mistplayers).length + 1,
isMimeSupported: function (mimetype) {
return (this.mimes.indexOf(mimetype) == -1 ? false : true);
},
isBrowserSupported: function (mimetype,source,options,streaminfo) {
//only use this if we are sure we just want an image
if ((options.forceType) || (options.forceSource) || (options.forcePlayer)) { return true; }
return false;
},
player: function(){this.onreadylist = [];}
};
var p = mistplayers.img.player;
p.prototype = new MistPlayer();
p.prototype.build = function (options,callback) {
var ele = this.getElement('img');
ele.src = options.src;
ele.style.display = 'block';
callback(ele);
}

View file

@ -1,40 +0,0 @@
mistplayers.jwplayer = {
name: 'JWPlayer',
version: '0.2',
mimes: ['html5/video/mp4','html5/video/webm','dash/video/mp4','flash/10','flash/7','html5/application/vnd.apple.mpegurl','html5/audio/mp3','html5/audio/aac'],
priority: Object.keys(mistplayers).length + 1,
isMimeSupported: function (mimetype) {
return (this.mimes.indexOf(mimetype) == -1 ? false : true);
},
isBrowserSupported: function (mimetype) {
//TODO like, actually check the browser or something?
if (typeof jwplayer == 'function') {
return true;
}
return false;
},
player: function(){}
};
var p = mistplayers.jwplayer.player;
p.prototype = new MistPlayer();
p.prototype.build = function (options,callback) {
var ele = this.getElement('div');
this.jw = jwplayer(ele).setup({
file: options.src,
width: options.width,
height: options.height,
autostart: options.autoplay,
image: options.poster,
controls: options.controls
});
this.addlog('Built html');
callback(ele);
}
p.prototype.play = function(){ return this.jw.play(); };
p.prototype.pause = function(){ return this.jw.pause(); };
p.prototype.volume = function(level){
if (typeof level == 'undefined' ) { return this.jw.getVolume/100; }
return this.jw.setVolume(level*100);
};

View file

@ -1,101 +0,0 @@
mistplayers.polytrope = {
name: 'Polytrope Flash Player',
version: '0.2',
mimes: ['flash/11','flash/10','flash/7'],
priority: Object.keys(mistplayers).length + 1,
isMimeSupported: function (mimetype) {
return (this.mimes.indexOf(mimetype) == -1 ? false : true);
},
isBrowserSupported: function (mimetype) {
return false;
var version = 0;
try {
// check in the mimeTypes
version = navigator.mimeTypes['application/x-shockwave-flash'].enabledPlugin.description.replace(/([^0-9\.])/g, '').split('.')[0];
} catch(e){}
try {
// for our special friend IE
version = new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable("$version").replace(/([^0-9\,])/g, '').split(',')[0];
} catch(e){}
var mimesplit = mimetype.split('/');
return Number(version) >= Number(mimesplit[mimesplit.length-1]);
},
player: function(){}
};
var p = mistplayers.polytrope.player;
p.prototype = new MistPlayer();
p.prototype.build = function (options,callback) {
function createParam(name,value) {
var p = document.createElement('param');
p.setAttribute('name',name);
p.setAttribute('value',value);
return p;
}
//TODO its not working.
/*
this.swf = this.video_instance_el.flash({
swf: "/shared/swf/videoplayer.swf?" + (new Date).getTime(),
width: "100%",
height: parseInt(this.options.element.height()),
wmode: "opaque",
menu: "false",
allowFullScreen: "true",
allowFullScreenInteractive: "true",
allowScriptAccess: "always",
id: "cucumbertv-swf-" + this.guid,
expressInstall: "/shared/swf/expressInstall.swf",
flashvars: {
rtmp_url: "rtmp://" + this.options.stream_host + "/play/",
stream_name: this.options.stream_name,
poster: this.options.poster,
autoplay: this.options.autoplay,
color_1: "0x1d1d1d",
color_2: "0xffffff",
buffer_time: .1,
is_streaming_url: "/api/user/is_streaming",
username: this.options.username,
mode: "v" == this.options.type ? "archive" : "live",
guid: this.guid
}
})
<div>
<object data="/shared/swf/videoplayer.swf?1468312898591" type="application/x-shockwave-flash" id="cucumbertv-swf-4dc64c18-59af-91a2-d0c5-ab8df4f45c65" width="100%" height="660">
<param name="wmode" value="opaque">
<param name="menu" value="false">
<param name="allowFullScreen" value="true">
<param name="allowFullScreenInteractive" value="true">
<param name="allowScriptAccess" value="always">
<param name="expressInstall" value="/shared/swf/expressInstall.swf">
<param name="flashvars" value="rtmp_url=rtmp://www.stickystage.com/play/&amp;stream_name=stickystage_archive+SrA-2016.07.08.23.54.08&amp;poster=/stickystage/users/SrA/archive/SrA-2016.07.08.23.54.08.jpg&amp;autoplay=true&amp;color_1=0x1d1d1d&amp;color_2=0xffffff&amp;buffer_time=0.1&amp;is_streaming_url=/api/user/is_streaming&amp;username=SrA&amp;mode=archive&amp;guid=4dc64c18-59af-91a2-d0c5-ab8df4f45c65">
<param name="movie" value="/shared/swf/videoplayer.swf?1468312898591">
</object>
</div>
*/
var ele = this.element('object');
ele.data = 'players/polytrope.swf';
ele.type = 'application/x-shockwave-flash';
ele.width = options.width;
ele.height = options.height;
/*
ele.appendChild(createParam('allowFullScreen','true'));
ele.appendChild(createParam('allowScriptAccess','always'));
var flashvars = 'rtmp_url=rtmp://www.stickystage.com/play/&amp;stream_name=stickystage_archive+SrA-2016.07.08.23.54.08&amp;poster=/stickystage/users/SrA/archive/SrA-2016.07.08.23.54.08.jpg&amp;autoplay=true&amp;color_1=0x1d1d1d&amp;color_2=0xffffff&amp;buffer_time=0.1&amp;is_streaming_url=/api/user/is_streaming&amp;username=SrA&amp;mode=archive&amp;guid=4dc64c18-59af-91a2-d0c5-ab8df4f45c65';
ele.appendChild(createParam('flashvars',flashvars));
ele.appendChild(createParam('movie','players/polytrope.swf'));
*/
ele.innerHTML = '<param name="wmode" value="opaque"> <param name="menu" value="false"> <param name="allowFullScreen" value="true"> <param name="allowFullScreenInteractive" value="true"> <param name="allowScriptAccess" value="always"> <param name="expressInstall" value="/shared/swf/expressInstall.swf"> <param name="flashvars" value="rtmp_url=rtmp://www.stickystage.com/play/&amp;stream_name=stickystage_archive+SrA-2016.07.08.23.54.08&amp;poster=http://stickystage.com/stickystage/users/SrA/archive/SrA-2016.07.08.23.54.08.jpg&amp;autoplay=true&amp;color_1=0x1d1d1d&amp;color_2=0xffffff&amp;buffer_time=0.1&amp;is_streaming_url=/api/user/is_streaming&amp;username=SrA&amp;mode=archive&amp;guid=4dc64c18-59af-91a2-d0c5-ab8df4f45c65"> <param name="movie" value="players/polytrope.swf">';
this.addlog('Built html');
callback(ele);
}

View file

@ -1,56 +0,0 @@
mistplayers.silverlight = {
name: 'Silverlight',
version: '1.1',
mimes: ['silverlight'],
priority: Object.keys(mistplayers).length + 1,
isMimeSupported: function (mimetype) {
return (this.mimes.indexOf(mimetype) == -1 ? false : true);
},
isBrowserSupported: function (mimetype) {
var plugin;
try {
// check in the mimeTypes
plugin = navigator.plugins["Silverlight Plug-In"];
return !!plugin;
} catch(e){}
try {
// for our special friend IE
plugin = new ActiveXObject('AgControl.AgControl');
return true;
} catch(e){}
return false;
},
player: function(){this.onreadylist = [];}
};
var p = mistplayers.silverlight.player;
p.prototype = new MistPlayer();
p.prototype.build = function (options,callback) {
function createParam(name,value) {
var p = document.createElement('param');
p.setAttribute('name',name);
p.setAttribute('value',value);
return p;
}
var ele = this.getElement('object');
ele.setAttribute('data','data:application/x-silverlight,');
ele.setAttribute('type','application/x-silverlight');
ele.setAttribute('width',options.width);
ele.setAttribute('height',options.height);
ele.appendChild(createParam('source',encodeURI(options.src)+'/player.xap'));
ele.appendChild(createParam('initparams','autoload=false,'+(options.autoplay ? 'autoplay=true' : 'autoplay=false')+',displaytimecode=false,enablecaptions=true,joinLive=true,muted=false'));
var a = document.createElement('a');
ele.appendChild(a);
a.setAttribute('href','http://go.microsoft.com/fwlink/?LinkID=124807');
a.setAttribute('style','text-decoration: none;');
var img = document.createElement('img');
a.appendChild(img);
img.setAttribute('src','http://go.microsoft.com/fwlink/?LinkId=108181');
img.setAttribute('alt','Get Microsoft Silverlight');
img.setAttribute('style','border-style: none;')
this.addlog('Built html');
callback(ele);
}

View file

@ -1,24 +0,0 @@
mistplayers.myplayer = {
name: 'My video player',
version: '0.1',
mimes: ['my/mime/types'],
priority: Object.keys(mistplayers).length + 1,
isMimeSupported: function (mimetype) {
return (this.mimes.indexOf(mimetype) == -1 ? false : true);
},
isBrowserSupported: function (mimetype) {
//TODO your code here
return false;
},
player: function(){}
};
var p = mistplayers.myplayer.player;
p.prototype = new MistPlayer();
p.prototype.build = function (options,callback) {
var ele = this.element('object');
//TODO your code here
this.addlog('Built html');
callback(ele);
}

View file

@ -1,53 +0,0 @@
mistplayers.theoplayer = {
name: 'TheoPlayer',
version: '0.2',
mimes: ['html5/application/vnd.apple.mpegurl','dash/video/mp4'],
priority: Object.keys(mistplayers).length + 1,
isMimeSupported: function (mimetype) {
return (this.mimes.indexOf(mimetype) == -1 ? false : true);
},
isBrowserSupported: function (mimetype) {
//TODO like, actually check the browser or something?
if (typeof theoplayer == 'function') {
return true;
}
return false;
},
player: function(){}
};
var p = mistplayers.theoplayer.player;
p.prototype = new MistPlayer();
p.prototype.build = function (options,callback) {
var ele = this.getElement('video');
ele.src = options.src;
ele.width = options.width;
ele.height = options.height;
if (options.controls) {
ele.setAttribute('controls','');
}
if (options.autoplay) {
ele.setAttribute('autoplay','');
}
if (options.loop) {
ele.setAttribute('loop','');
}
if (options.poster) {
ele.setAttribute('poster',options.poster);
}
this.theoplayer = theoplayer(ele);
this.addlog('Built html');
callback(ele);
}
p.prototype.play = function(){ return this.theoplayer.play(); };
p.prototype.pause = function(){ return this.theoplayer.pause(); };
p.prototype.volume = function(level){
if (typeof level == 'undefined' ) { return this.theoplayer.volume; }
return this.theoplayer.volume = level;
};
p.prototype.fullscreen = function(){
return this.theoplayer.requestFullscreen();
};

View file

@ -1,221 +1,208 @@
mistplayers.videojs = { mistplayers.videojs = {
name: 'VideoJS player', name: "VideoJS player",
version: '1.1', mimes: ["html5/application/vnd.apple.mpegurl"],
mimes: ['html5/video/mp4','html5/application/vnd.apple.mpegurl','html5/video/ogg','html5/video/webm'], priority: MistUtil.object.keys(mistplayers).length + 1,
priority: Object.keys(mistplayers).length + 1,
isMimeSupported: function (mimetype) { isMimeSupported: function (mimetype) {
return (this.mimes.indexOf(mimetype) == -1 ? false : true); return (this.mimes.indexOf(mimetype) == -1 ? false : true);
}, },
isBrowserSupported: function (mimetype,source,options,streaminfo,logfunc) { isBrowserSupported: function (mimetype,source,MistVideo) {
//dont use https if the player is loaded over http //check for http/https mismatch
if ((options.host.substr(0,7) == 'http://') && (source.url.substr(0,8) == 'https://')) { if (location.protocol != MistUtil.http.url.split(source.url).protocol) {
if (logfunc) { logfunc('HTTP/HTTPS mismatch for this source'); } MistVideo.log("HTTP/HTTPS mismatch for this source");
return false; return false;
} }
//dont use videojs if this location is loaded over file:// //don't use videojs if this location is loaded over file://
if ((location.protocol == 'file:') && (mimetype == 'html5/application/vnd.apple.mpegurl')) { if ((location.protocol == "file:") && (mimetype == "html5/application/vnd.apple")) {
if (logfunc) { logfunc('This source ('+mimetype+') won\'t work if the page is run via file://'); } MistVideo.log("This source ("+mimetype+") won't load if the page is run via file://");
return false; return false;
} }
//dont use HLS if there is an MP3 audio track, unless we're on apple or edge return ("MediaSource" in window);
if ((mimetype == 'html5/application/vnd.apple.mpegurl') && (['iPad','iPhone','iPod','MacIntel'].indexOf(navigator.platform) == -1) && (navigator.userAgent.indexOf('Edge') == -1)) {
var audio = false;
var nonmp3 = false;
for (var i in streaminfo.meta.tracks) {
var t = streaminfo.meta.tracks[i];
if (t.type == 'audio') {
audio = true;
if (t.codec != 'MP3') {
nonmp3 = true;
}
}
}
if ((audio) && (!nonmp3)) {
if (logfunc) { logfunc('This source has audio, but only MP3, and this browser can\'t play MP3 via HLS'); }
return false;
}
}
return ('MediaSource' in window);
}, },
player: function(){this.onreadylist = [];} player: function(){},
scriptsrc: function(host) { return host+"/videojs.js"; }
}; };
var p = mistplayers.videojs.player; var p = mistplayers.videojs.player;
p.prototype = new MistPlayer(); p.prototype = new MistPlayer();
p.prototype.build = function (options,callback) { p.prototype.build = function (MistVideo,callback) {
var me = this; //to allow nested functions to access the player class itself var me = this; //to allow nested functions to access the player class itself
function onplayerload () { function onVideoJSLoad () {
me.addlog('Building VideoJS player..'); if (MistVideo.destroyed) { return;}
var cont = document.createElement('div'); MistVideo.log("Building VideoJS player..");
cont.className = 'mistplayer';
var ele = me.getElement('video'); var ele = document.createElement("video");
cont.appendChild(ele); if (MistVideo.source.type != "html5/video/ogg") {
ele.className = ''; ele.crossOrigin = "anonymous"; //required for subtitles, but if ogg, the video won"t load
if (options.source.type != "html5/video/ogg") {
ele.crossOrigin = 'anonymous'; //required for subtitles, but if ogg, the video won't load
} }
var shortmime = options.source.type.split('/'); var shortmime = MistVideo.source.type.split("/");
shortmime.shift(); shortmime.shift();
var source = document.createElement('source'); var source = document.createElement("source");
source.setAttribute('src',options.src); source.setAttribute("src",MistVideo.source.url);
me.source = source; me.source = source;
ele.appendChild(source); ele.appendChild(source);
source.type = shortmime.join('/'); source.type = shortmime.join("/");
me.addlog('Adding '+source.type+' source @ '+options.src); MistVideo.log("Adding "+source.type+" source @ "+MistVideo.source.url);
if (source.type == 'application/vnd.apple.mpegurl') { source.type = 'application/x-mpegURL'; } if (source.type == "application/vnd.apple.mpegurl") { source.type = "application/x-mpegURL"; }
ele.className += ' video-js'; MistUtil.class.add(ele,"video-js");
ele.width = options.width;
ele.height = options.height;
ele.style.width = options.width+'px';
ele.style.height = options.height+'px';
var vjsopts = { var vjsopts = {};
preload: 'auto'
};
if (options.autoplay) { vjsopts.autoplay = true; } if (MistVideo.options.autoplay) { vjsopts.autoplay = true; }
if (options.loop) { if ((MistVideo.options.loop) && (MistVideo.info.type != "live")) {
vjsopts.loop = true; vjsopts.loop = true;
ele.loop = true; ele.loop = true;
} }
if (options.poster) { vjsopts.poster = options.poster; } if (MistVideo.options.poster) { vjsopts.poster = MistVideo.options.poster; }
if (options.controls) { if (MistVideo.options.controls == "stock") {
if ((options.controls == 'stock') || (!me.buildMistControls())) { ele.setAttribute("controls","");
//MistControls have failed to build in the if condition if (!document.getElementById("videojs-css")) {
ele.setAttribute('controls',true); var style = document.createElement("link");
style.rel = "stylesheet";
style.href = MistVideo.options.host+"/skins/videojs.css";
style.id = "videojs-css";
document.head.appendChild(style);
} }
} }
me.onready(function(){ me.onready(function(){
me.videojs = videojs(ele,vjsopts,function(){ me.videojs = videojs(ele,vjsopts,function(){
me.addlog('Videojs initialized'); MistVideo.log("Videojs initialized");
});
}); });
me.addlog('Built html'); me.api.unload = function(){
videojs(ele).dispose();
};
});
//forward events MistVideo.log("Built html");
ele.addEventListener('error',function(e){
if (!e.isTrusted) { return; } //don't trigger on errors we have thrown ourselves
var msg; if (("Proxy" in window) && ("Reflect" in window)) {
if ('message' in e) { var overrides = {
msg = e.message; get: {},
set: {}
};
MistVideo.player.api = new Proxy(ele,{
get: function(target, key, receiver){
if (key in overrides.get) {
return overrides.get[key].apply(target, arguments);
}
var method = target[key];
if (typeof method === "function"){
return function () {
return method.apply(target, arguments);
}
}
return method;
},
set: function(target, key, value) {
if (key in overrides.set) {
return overrides.set[key].call(target,value);
}
return target[key] = value;
}
});
if (MistVideo.info.type == "live") {
function getLastBuffer(video) {
var buffer_end = 0;
if (video.buffered.length) {
buffer_end = video.buffered.end(video.buffered.length-1)
}
return buffer_end;
}
var HLSlatency = 90; //best guess..
overrides.get.duration = function(){
return (MistVideo.info.lastms + (new Date()).getTime() - MistVideo.info.updated.getTime())*1e-3;
};
MistVideo.player.api.lastProgress = new Date();
MistVideo.player.api.liveOffset = 0;
MistUtil.event.addListener(ele,"progress",function(){
MistVideo.player.api.lastProgress = new Date();
});
overrides.set.currentTime = function(value){
var diff = MistVideo.player.api.currentTime - value;
var offset = value - MistVideo.player.api.duration;
//MistVideo.player.api.liveOffset = offset;
MistVideo.log("Seeking to "+MistUtil.format.time(value)+" ("+Math.round(offset*-10)/10+"s from live)");
MistVideo.video.currentTime -= diff;
}
overrides.get.currentTime = function(){
return this.currentTime + MistVideo.info.lastms*1e-3 - MistVideo.player.api.liveOffset - HLSlatency;
}
}
} }
else { else {
msg = 'readyState: '; me.api = ele;
switch (me.element.readyState) {
case 0:
msg += 'HAVE_NOTHING';
break;
case 1:
msg += 'HAVE_METADATA';
break;
case 2:
msg += 'HAVE_CURRENT_DATA';
break;
case 3:
msg += 'HAVE_FUTURE_DATA';
break;
case 4:
msg += 'HAVE_ENOUGH_DATA';
break;
}
msg += ' networkState: ';
switch (me.element.networkState) {
case 0:
msg += 'NETWORK_EMPTY';
break;
case 1:
msg += 'NETWORK_IDLE';
break;
case 2:
msg += 'NETWORK_LOADING';
break;
case 3:
msg += 'NETWORK_NO_SOURCE';
break;
}
} }
me.adderror(msg); MistVideo.player.setSize = function(size){
if ("videojs" in MistVideo.player) {
MistVideo.player.videojs.dimensions(size.width,size.height);
}); //for some reason, the videojs' container won't be resized with the method above.
var events = ['abort','canplay','canplaythrough','durationchange','emptied','ended','interruptbegin','interruptend','loadeddata','loadedmetadata','loadstart','pause','play','playing','ratechange','seeked','seeking','stalled','volumechange','waiting','progress']; //so let's cheat and do it ourselves
for (var i in events) { ele.parentNode.style.width = size.width+"px";
ele.addEventListener(events[i],function(e){ ele.parentNode.style.height = size.height+"px";
me.addlog('Player event fired: '+e.type); }
this.api.style.width = size.width+"px";
this.api.style.height = size.height+"px";
};
MistVideo.player.api.setSource = function(url) {
if (!MistVideo.player.videojs) { return; }
if (MistVideo.player.videojs.src() != url) {
MistVideo.player.videojs.src({
type: MistVideo.player.videojs.currentSource().type,
src: url
}); });
} }
};
MistVideo.player.api.setSubtitle = function(trackmeta) {
//remove previous subtitles
var tracks = ele.getElementsByTagName("track");
for (var i = tracks.length - 1; i >= 0; i--) {
ele.removeChild(tracks[i]);
}
if (trackmeta) { //if the chosen track exists
//add the new one
var track = document.createElement("track");
ele.appendChild(track);
track.kind = "subtitles";
track.label = trackmeta.label;
track.srclang = trackmeta.lang;
track.src = trackmeta.src;
track.setAttribute("default","");
}
};
callback(cont); callback(ele);
} }
if ('videojs' in window) { if ("videojs" in window) {
onplayerload(); onVideoJSLoad();
} }
else { else {
//load the videojs player //load the videojs player
var scripttag = document.createElement('script');
scripttag.src = options.host+'/videojs.js'; var scripttag = MistUtil.scripts.insert(MistVideo.urlappend(mistplayers.videojs.scriptsrc(MistVideo.options.host)),{
me.addlog('Retrieving videojs player code from '+scripttag.src); onerror: function(e){
document.head.appendChild(scripttag); var msg = "Failed to load videojs.js";
scripttag.onerror = function(){ if (e.message) { msg += ": "+e.message; }
me.askNextCombo('Failed to load videojs.js'); MistVideo.showError(msg);
} },
scripttag.onload = function(){ onload: onVideoJSLoad
onplayerload(); },MistVideo);
} }
} }
}
p.prototype.play = function(){ return this.element.play(); };
p.prototype.pause = function(){ return this.element.pause(); };
p.prototype.volume = function(level){
if (typeof level == 'undefined' ) { return this.element.volume; }
return this.element.volume = level;
};
p.prototype.loop = function(bool){
if (typeof bool == 'undefined') {
return this.element.loop;
}
return this.element.loop = bool;
};
p.prototype.load = function(){ return this.element.load(); };
if (document.fullscreenEnabled || document.webkitFullscreenEnabled || document.mozFullScreenEnabled || document.msFullscreenEnabled) {
p.prototype.fullscreen = function(){
if(this.element.requestFullscreen) {
return this.element.requestFullscreen();
} else if(this.element.mozRequestFullScreen) {
return this.element.mozRequestFullScreen();
} else if(this.element.webkitRequestFullscreen) {
return this.element.webkitRequestFullscreen();
} else if(this.element.msRequestFullscreen) {
return this.element.msRequestFullscreen();
}
};
}
p.prototype.updateSrc = function(src){
if ("videojs" in this) {
if (src == '') {
this.videojs.dispose();
return;
}
this.videojs.src({
src: src,
type: this.source.type
});
return true;
}
return false;
};

429
embed/wrappers/webrtc.js Normal file
View file

@ -0,0 +1,429 @@
mistplayers.webrtc = {
name: "WebRTC player",
mimes: ["webrtc"],
priority: MistUtil.object.keys(mistplayers).length + 1,
isMimeSupported: function (mimetype) {
return (this.mimes.indexOf(mimetype) == -1 ? false : true);
},
isBrowserSupported: function (mimetype,source,MistVideo) {
if ((!("WebSocket" in window)) || (!("RTCPeerConnection" in window))) { return false; }
//check for http/https mismatch
if (location.protocol.replace(/^http/,"ws") != MistUtil.http.url.split(source.url.replace(/^http/,"ws")).protocol) {
MistVideo.log("HTTP/HTTPS mismatch for this source");
return false;
}
return true;
},
player: function(){}
};
var p = mistplayers.webrtc.player;
p.prototype = new MistPlayer();
p.prototype.build = function (MistVideo,callback) {
var me = this;
if ((typeof WebRTCBrowserEqualizerLoaded == "undefined") || (!WebRTCBrowserEqualizerLoaded)) {
//load it
var scripttag = document.createElement("script");
scripttag.src = MistVideo.urlappend(MistVideo.options.host+"/webrtc.js");
MistVideo.log("Retrieving webRTC browser equalizer code from "+scripttag.src);
document.head.appendChild(scripttag);
scripttag.onerror = function(){
MistVideo.showError("Failed to load webrtc browser equalizer",{nextCombo:5});
}
scripttag.onload = function(){
me.build(MistVideo,callback);
}
return;
}
var video = document.createElement("video");
//apply options
var attrs = ["autoplay","loop","poster"];
for (var i in attrs) {
var attr = attrs[i];
if (MistVideo.options[attr]) {
video.setAttribute(attr,(MistVideo.options[attr] === true ? "" : MistVideo.options[attr]));
}
}
if (MistVideo.info.type == "live") {
video.loop = false;
}
if (MistVideo.options.controls == "stock") {
video.setAttribute("controls","");
}
video.setAttribute("crossorigin","anonymous");
this.setSize = function(size){
video.style.width = size.width+"px";
video.style.height = size.height+"px";
};
MistUtil.event.addListener(video,"loadeddata",correctSubtitleSync);
MistUtil.event.addListener(video,"seeked",correctSubtitleSync);
var seekoffset = 0;
var hasended = false;
this.listeners = {
on_connected: function() {
seekoffset = 0;
hasended = false;
this.webrtc.play();
},
on_disconnected: function() {
MistVideo.log("Websocket sent on_disconnect");
/*
If a VoD file ends, we receive an on_stop, but no on_disconnect
If a live stream ends, we receive an on_disconnect, but no on_stop
If MistOutWebRTC crashes, we receive an on_stop and then an on_disconnect
*/
if (hasended) {
MistVideo.showError("Connection to media server ended unexpectedly.");
}
video.pause();
},
on_answer_sdp: function (ev) {
if (!ev.result) {
MistVideo.showError("Failed to open stream.");
this.on_disconnected();
return;
}
MistVideo.log("SDP answer received");
},
on_time: function(ev) {
//timeupdate
var oldoffset = seekoffset;
seekoffset = ev.current*1e-3 - video.currentTime;
if (Math.abs(oldoffset - seekoffset) > 1) { correctSubtitleSync(); }
var d = (ev.end == 0 ? Infinity : ev.end*1e-3);
if (d != duration) {
duration = d;
MistUtil.event.send("durationchange",d,video);
}
},
on_seek: function(){
MistUtil.event.send("seeked",seekoffset,video);
video.play();
},
on_stop: function(){
MistVideo.log("Websocket sent on_stop");
MistUtil.event.send("ended",null,video);
hasended = true;
}
};
function WebRTCPlayer(){
this.peerConn = null;
this.localOffer = null;
this.isConnected = false;
var thisWebRTCPlayer = this;
this.on_event = function(ev) {
//if (ev.type != "on_time") { console.log(ev); }
switch (ev.type) {
case "on_connected": {
thisWebRTCPlayer.isConnected = true;
break;
}
case "on_answer_sdp": {
thisWebRTCPlayer.peerConn
.setRemoteDescription({ type: "answer", sdp: ev.answer_sdp })
.then(function(){}, function(err) {
console.error(err);
});
break;
}
case "on_disconnected": {
thisWebRTCPlayer.isConnected = false;
break;
}
}
if (ev.type in me.listeners) {
return me.listeners[ev.type].call(me,ev);
}
MistVideo.log("Unhandled WebRTC event "+ev.type+": "+JSON.stringify(ev));
return false;
};
this.connect = function(callback){
thisWebRTCPlayer.signaling = new WebRTCSignaling(thisWebRTCPlayer.on_event);
thisWebRTCPlayer.peerConn = new RTCPeerConnection();
thisWebRTCPlayer.peerConn.ontrack = function(ev) {
video.srcObject = ev.streams[0];
if (callback) { callback(); }
};
};
this.play = function(){
if (!this.isConnected) {
throw "Not connected, cannot play";
}
this.peerConn
.createOffer({
offerToReceiveAudio: true,
offerToReceiveVideo: true,
})
.then(function(offer){
thisWebRTCPlayer.localOffer = offer;
thisWebRTCPlayer.peerConn
.setLocalDescription(offer)
.then(function(){
thisWebRTCPlayer.signaling.sendOfferSDP(thisWebRTCPlayer.localOffer.sdp);
}, function(err){console.error(err)});
}, function(err){ throw err; });
};
this.stop = function(){
if (!this.isConnected) { throw "Not connected, cannot stop." }
this.signaling.send({type: "stop"});
};
this.seek = function(seekTime){
if (!this.isConnected) { return; }
this.signaling.send({type: "seek", "seek_time": seekTime*1e3});
};
this.pause = function(){
if (!this.isConnected) { throw "Not connected, cannot pause." }
this.signaling.send({type: "pause"});
};
this.setTrack = function(obj){
if (!this.isConnected) { throw "Not connected, cannot set track." }
obj.type = "tracks";
this.signaling.send(obj);
};
this.getStats = function(callback){
this.peerConn.getStats().then(function(d){
var output = {};
var entries = Array.from(d.entries());
for (var i in entries) {
var value = entries[i];
if (value[1].type == "inbound-rtp") {
output[value[0]] = value[1];
}
}
callback(output);
});
};
//input only
/*
this.sendVideoBitrate = function(bitrate) {
this.send({type: "video_bitrate", video_bitrate: bitrate});
};
*/
this.connect();
}
function WebRTCSignaling(onEvent){
this.ws = null;
this.ws = new WebSocket(MistVideo.source.url.replace(/^http/,"ws"));
this.ws.onopen = function() {
onEvent({type: "on_connected"});
};
this.ws.onmessage = function(e) {
try {
var cmd = JSON.parse(e.data);
onEvent(cmd);
}
catch (err) {
console.error("Failed to parse a response from MistServer",err,e.data);
}
};
/* See http://tools.ietf.org/html/rfc6455#section-7.4.1 */
this.ws.onclose = function(ev) {
switch (ev.code) {
default: {
onEvent({type: "on_disconnected"});
break;
}
}
}
this.sendOfferSDP = function(sdp) {
this.send({type: "offer_sdp", offer_sdp: sdp});
};
this.send = function(cmd) {
if (!this.ws) {
throw "Not initialized, cannot send "+JSON.stringify(cmd);
}
this.ws.send(JSON.stringify(cmd));
}
};
this.webrtc = new WebRTCPlayer();
this.api = {};
//override video duration
var duration;
Object.defineProperty(this.api,"duration",{
get: function(){ return duration; }
});
//override seeking
Object.defineProperty(this.api,"currentTime",{
get: function(){
return seekoffset + video.currentTime;
},
set: function(value){
seekoffset = value - video.currentTime;
video.pause();
me.webrtc.seek(value);
MistUtil.event.send("seeking",value,video);
}
});
//redirect properties
//using a function to make sure the "item" is in the correct scope
function reroute(item) {
Object.defineProperty(me.api,item,{
get: function(){ return video[item]; },
set: function(value){
return video[item] = value;
}
});
}
var list = [
"volume"
,"muted"
,"loop"
,"paused",
,"error"
,"textTracks"
,"webkitDroppedFrameCount"
,"webkitDecodedFrameCount"
];
for (var i in list) {
reroute(list[i]);
}
//redirect methods
function redirect(item) {
if (item in video) {
me.api[item] = function(){
return video[item].call(video,arguments);
};
}
}
var list = ["load","getVideoPlaybackQuality"];
for (var i in list) {
redirect(list[i]);
}
//redirect play
me.api.play = function(){
if (me.api.currentTime) {
if ((!me.webrtc.isConnected) || (me.webrtc.peerConn.iceConnectionState != "completed")) {
me.webrtc.connect(function(){
me.webrtc.seek(me.api.currentTime);
});
}
else {
me.webrtc.seek(me.api.currentTime);
}
}
else {
video.play();
}
};
//redirect pause
me.api.pause = function(){
video.pause();
try {
me.webrtc.pause();
}
catch (e) {}
MistUtil.event.send("paused",null,video);
};
me.api.setTracks = function(obj){
me.webrtc.setTrack(obj);
};
function correctSubtitleSync() {
if (!me.api.textTracks[0]) { return; }
var currentoffset = me.api.textTracks[0].currentOffset || 0;
if (Math.abs(seekoffset - currentoffset) < 1) { return; } //don't bother if the change is small
var newCues = [];
for (var i = me.api.textTracks[0].cues.length-1; i >= 0; i--) {
var cue = me.api.textTracks[0].cues[i];
me.api.textTracks[0].removeCue(cue);
if (!("orig" in cue)) {
cue.orig = {start:cue.startTime,end:cue.endTime};
}
cue.startTime = cue.orig.start - seekoffset;
cue.endTime = cue.orig.end - seekoffset;
newCues.push(cue);
}
for (var i in newCues) {
me.api.textTracks[0].addCue(newCues[i]);
}
me.api.textTracks[0].currentOffset = seekoffset;
}
me.api.setSubtitle = function(trackmeta) {
//remove previous subtitles
var tracks = video.getElementsByTagName("track");
for (var i = tracks.length - 1; i >= 0; i--) {
video.removeChild(tracks[i]);
}
if (trackmeta) { //if the chosen track exists
//add the new one
var track = document.createElement("track");
video.appendChild(track);
track.kind = "subtitles";
track.label = trackmeta.label;
track.srclang = trackmeta.lang;
track.src = trackmeta.src;
track.setAttribute("default","");
//correct timesync
track.onload = correctSubtitleSync;
}
};
//loop
MistUtil.event.addListener(video,"ended",function(){
if (me.api.loop) {
me.webrtc.connect();
}
});
if ("decodingIssues" in MistVideo.skin.blueprints) {
//get additional dev stats
var vars = ["nackCount","pliCount","packetsLost","packetsReceived","bytesReceived"];
for (var j in vars) {
me.api[vars[j]] = 0;
}
var f = function() {
MistVideo.timers.start(function(){
me.webrtc.getStats(function(d){
for (var i in d) {
for (var j in vars) {
if (vars[j] in d[i]) {
me.api[vars[j]] = d[i][vars[j]];
}
}
break;
}
});
f();
},1e3);
};
f();
}
me.api.unload = function(){
try {
me.webrtc.stop();
} catch (e) {}
};
callback(video);
};

View file

@ -1,327 +0,0 @@
function mistembed(streamname) {
//find the current script
var me;
if (('currentScript' in document) && (document.currentScript)) {
me = document.currentScript;
//not supported in old browsers :(
}
else {
var scripts = document.getElementsByTagName('script');
me = scripts[scripts.length - 1];
//not correct if the script is inserted dynamically, but this is how it used to be
}
// return the current flash version
function flash_version() {
var version = 0;
try {
// check in the mimeTypes
version = navigator.mimeTypes['application/x-shockwave-flash'].enabledPlugin.description.replace(/([^0-9\.])/g, '').split('.')[0];
} catch(e){}
try {
// for our special friend IE
version = new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable("$version").replace(/([^0-9\,])/g, '').split(',')[0];
} catch(e){}
return parseInt(version, 10);
};
// return true if silverlight is installed
function silverlight_installed() {
var plugin;
try {
// check in the mimeTypes
plugin = navigator.plugins["Silverlight Plug-In"];
return !!plugin;
} catch(e){}
try {
// for our special friend IE
plugin = new ActiveXObject('AgControl.AgControl');
return true;
} catch(e){}
return false;
};
// return true if the browser thinks it can play the mimetype
function html5_video_type(type) {
var support = false;
if (type == 'video/mp4') {
if ((navigator.userAgent.indexOf('MSIE') > -1) && (parseInt(navigator.userAgent.split('MSIE')[1]) <= 9)) {
//IE <= 9 doesn't support MP4, Firefox seems to correctly see it now.
return false;
}
if (navigator.userAgent.indexOf('Firefox') > -1) {
//firefox claims to support MP4 but doesn't when:
// - under win xp
// - the stream is live
if (video.type == 'live') {
return false;
}
//find "Windows NT X;" in user userAgent
//sorry, I don't like regexes.. I avoid them when I can :$
var s = navigator.userAgent.split('Windows NT ');
if (s.length > 1) {
s = s[1].split(';');
s = s[0];
if (Number(s) <= 5.1) {
return false;
}
}
}
}
try {
var v = document.createElement('video');
if( v && v.canPlayType(type) != "" )
{
support = true; // true-ish, anyway
}
} catch(e){}
return support;
}
//return true if rtsp is supported
function rtsp_support() {
var plugin;
try {
// check in the mimeTypes
plugin = navigator.mimeTypes["application/x-google-vlc-plugin"];
return !!plugin;
} catch(e){}
try {
// for our special friend IE
plugin = new ActiveXObject('VideoLAN.Vlcplugin.1');
return true;
} catch(e){}
return false;
}
// parse a "type" string from the controller. Format:
// xxx/# (e.g. flash/3) or xxx/xxx/xxx (e.g. html5/application/ogg)
function parseType(type) {
var split = type.split('/');
if( split.length > 2 ) {
split[1] += '/' + split[2];
}
return split;
}
// return true if a type is supported
function hasSupport(type) {
var typemime = parseType(type);
switch(typemime[0]) {
case 'flash': return flash_version() >= parseInt(typemime[1], 10); break;
case 'html5': return html5_video_type(typemime[1]); break;
case 'rtsp': return rtsp_support(); break;
case 'silverlight': return silverlight_installed(); break;
default: return false; break;
}
}
// build HTML for certain kinds of types
function buildPlayer(src, container, videowidth, videoheight, vtype) {
// used to recalculate the width/height
var ratio;
// get the container's width/height
var containerwidth = parseInt(container.clientWidth, 10);
var containerheight = parseInt(container.clientHeight, 10);
if(videowidth > containerwidth && containerwidth > 0) {
ratio = videowidth / containerwidth;
videowidth /= ratio;
videoheight /= ratio;
}
if(videoheight > containerheight && containerheight > 0) {
ratio = videoheight / containerheight;
videowidth /= ratio;
videoheight /= ratio;
}
var maintype = parseType(src.type);
mistvideo[streamname].embedded = src;
switch(maintype[0]) {
case 'flash':
// maintype[1] is already checked (i.e. user has version > maintype[1])
var flashplayer,
url = encodeURIComponent(src.url) + '&controlBarMode=floating&initialBufferTime=0.5&expandedBufferTime=5&minContinuousPlaybackTime=3' + (vtype == 'live' ? "&streamType=live" : "") + (autoplay ? '&autoPlay=true' : '');
/*
if( parseInt(maintype[1], 10) >= 10 ) {
flashplayer = 'http://fpdownload.adobe.com/strobe/FlashMediaPlayback_101.swf';
}
else {
flashplayer = 'http://fpdownload.adobe.com/strobe/FlashMediaPlayback.swf';
}
*/
flashplayer = src.player_url;
container.innerHTML += '<object width="' + videowidth + '" height="' + videoheight + '">' +
'<param name="movie" value="' + flashplayer + '"></param>' +
'<param name="flashvars" value="src=' + url + '"></param>' +
'<param name="allowFullScreen" value="true"></param>' +
'<param name="allowscriptaccess" value="always"></param>' +
'<param name="wmode" value="direct"></param>' +
(autoplay ? '<param name="autoPlay" value="true">' : '') +
'<embed src="' + flashplayer + '" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="' + videowidth + '" height="' + videoheight + '" flashvars="src=' + url + '"></embed>' +
'</object>';
break;
case 'html5':
container.innerHTML += '<video width="' + videowidth + '" height="' + videoheight + '" src="' + src.url + '" controls="controls" '+(autoplay ? 'autoplay="autoplay"' : '')+'><strong>No HTML5 video support</strong></video>';
break;
case 'rtsp':
/*container.innerHTML += '<object classid="clsid:CFCDAA03-8BE4-11cf-B84B-0020AFBBCCFA" width="'+videowidth+'" height="'+videoheight+'">'+
'<param name="src" value="'+encodeURI(src.url)+'">'+
'<param name="console" value="video1">'+
'<param name="controls" value="All">'+
'<param name="autostart" value="false">'+
'<param name="loop" value="false">'+
'<embed name="myMovie" src="'+encodeURI(src.url)+'" width="'+videowidth+'" height="'+videoheight+'" autostart="false" loop="false" nojava="true" console="video1" controls="All"></embed>'+
'<noembed>Something went wrong.</noembed>'+
'</object>'; //realplayer, doesnt work */
container.innerHTML += '<embed type="application/x-google-vlc-plugin"'+
'pluginspage="http://www.videolan.org"'+
'width="'+videowidth+'"'+
'height="'+videoheight+'"'+
'target="'+encodeURI(src.url)+'"'+
'autoplay="'+(autoplay ? 'yes' : 'no')+'"'+
'>'+
'</embed>'+
'<object classid="clsid:9BE31822-FDAD-461B-AD51-BE1D1C159921" codebase="http://downloads.videolan.org/pub/videolan/vlc/latest/win32/axvlc.cab">'+
'</object>'; //vlc, seems to work, sort of. it's trying anyway
break;
case 'silverlight':
container.innerHTML += '<object data="data:application/x-silverlight," type="application/x-silverlight" width="' + videowidth + '" height="' + videoheight + '">'+
'<param name="source" value="' + encodeURI(src.url) + '/player.xap"/>'+
'<param name="onerror" value="onSilverlightError" />'+
'<param name="autoUpgrade" value="true" />'+
'<param name="background" value="white" />'+
'<param name="enableHtmlAccess" value="true" />'+
'<param name="minRuntimeVersion" value="3.0.40624.0" />'+
'<param name="initparams" value =\'autoload=false,'+(autoplay ? 'autoplay=true' : 'autoplay=false')+',displaytimecode=false,enablecaptions=true,joinLive=true,muted=false,playlist=<playList><playListItems><playListItem title="Test" description="testing" mediaSource="' + encodeURI(src.url) + '" adaptiveStreaming="true" thumbSource="" frameRate="25.0" width="" height=""></playListItem></playListItems></playList>\' />'+
'<a href="http://go.microsoft.com/fwlink/?LinkID=124807" style="text-decoration: none;"> <img src="http://go.microsoft.com/fwlink/?LinkId=108181" alt="Get Microsoft Silverlight" style="border-style: none" /></a>'+
'</object>';
break;
default:
container.innerHTML += '<strong>Missing embed code for output type "'+src.type+'"</strong>';
video.error = 'Missing embed code for output type "'+src.type;
}
}
var video = mistvideo[streamname],
container = document.createElement('div'),
forceType = false,
forceSupportCheck = false,
autoplay = true,
urlappend = false;
if (me.parentNode.hasAttribute('data-forcetype')) {
forceType = me.parentNode.getAttribute('data-forcetype');
}
if (me.parentNode.hasAttribute('data-forcesupportcheck')) {
forceSupportCheck = true;
}
if (me.parentNode.hasAttribute('data-noautoplay')) {
autoplay = false;
}
if (me.parentNode.hasAttribute('data-urlappend')) {
urlappend = me.parentNode.getAttribute('data-urlappend');
}
if (video.width == 0) { video.width = 250; }
if (video.height == 0) { video.height = 250; }
// create the container
me.parentNode.insertBefore(container, me);
// set the class to 'mistvideo'
container.setAttribute('class', 'mistvideo');
// remove script tag
me.parentNode.removeChild(me);
if(video.error) {
// there was an error; display it
if (video.on_error){
container.innerHTML = video.on_error;
}else{
container.innerHTML = ['<strong>Error: ', video.error, '</strong>'].join('');
}
}
else if ((typeof video.source == 'undefined') || (video.source.length < 1)) {
// no stream sources
if (video.on_error){
container.innerHTML = video.on_error;
}else{
container.innerHTML = '<strong>Error: no active source or compatible protocols for this stream</strong>';
}
}
else {
// no error, and sources found. Check the video types and output the best
// available video player.
var i,
vtype = (video.type ? video.type : 'unknown'),
foundPlayer = false,
len = video.source.length;
for (var i in video.source) {
var support = hasSupport(video.source[i].type);
video.source[i].browser_support = support;
if ((support) || (forceType)) {
if ((!forceType) || ((forceType) && (video.source[i].type.indexOf(forceType) >= 0))) {
if (foundPlayer === false) {
foundPlayer = i;
if (!forceSupportCheck) {
break;
}
}
}
}
}
if (foundPlayer === false) {
// of all the streams given, none was supported (eg. no flash and HTML5 video). Display error
container.innerHTML = '<strong>No support for any player found</strong>';
}
else {
// we support this kind of video, so build it.
var source = video.source[foundPlayer];
if (urlappend) {
source.url += urlappend;
source.relurl += urlappend;
}
else if (me.src.indexOf('?') != -1) {
var params = me.src.split('?');
params.shift();
params.join('?');
source.url += '?'+params;
source.relurl += '?'+params;
}
buildPlayer(source, container, video.width, video.height, vtype);
}
}
return (mistvideo[streamname].embedded ? mistvideo[streamname].embedded.type : false);
//keep empty line at end of file
}

View file

@ -114,9 +114,12 @@ namespace Mist {
capa["url_match"].append("/info_$.js"); capa["url_match"].append("/info_$.js");
capa["url_match"].append("/json_$.js"); capa["url_match"].append("/json_$.js");
capa["url_match"].append("/player.js"); capa["url_match"].append("/player.js");
capa["url_match"].append("/player.css");
capa["url_match"].append("/videojs.js"); capa["url_match"].append("/videojs.js");
capa["url_match"].append("/dashjs.js"); capa["url_match"].append("/dashjs.js");
capa["url_match"].append("/webrtc.js");
capa["url_match"].append("/skins/default.css");
capa["url_match"].append("/skins/dev.css");
capa["url_match"].append("/skins/videojs.css");
capa["url_match"].append("/embed_$.js"); capa["url_match"].append("/embed_$.js");
capa["url_match"].append("/flashplayer.swf"); capa["url_match"].append("/flashplayer.swf");
capa["url_match"].append("/oldflashplayer.swf"); capa["url_match"].append("/oldflashplayer.swf");
@ -124,15 +127,11 @@ namespace Mist {
capa["optional"]["wrappers"]["help"] = "Which players are attempted and in what order."; capa["optional"]["wrappers"]["help"] = "Which players are attempted and in what order.";
capa["optional"]["wrappers"]["default"] = ""; capa["optional"]["wrappers"]["default"] = "";
capa["optional"]["wrappers"]["type"] = "ord_multi_sel"; capa["optional"]["wrappers"]["type"] = "ord_multi_sel";
/*capa["optional"]["wrappers"]["allowed"].append("theoplayer");
capa["optional"]["wrappers"]["allowed"].append("jwplayer");*/
capa["optional"]["wrappers"]["allowed"].append("html5"); capa["optional"]["wrappers"]["allowed"].append("html5");
capa["optional"]["wrappers"]["allowed"].append("videojs"); capa["optional"]["wrappers"]["allowed"].append("videojs");
capa["optional"]["wrappers"]["allowed"].append("dashjs"); capa["optional"]["wrappers"]["allowed"].append("dashjs");
//capa["optional"]["wrappers"]["allowed"].append("polytrope"); //currently borked capa["optional"]["wrappers"]["allowed"].append("webrtc");
capa["optional"]["wrappers"]["allowed"].append("flash_strobe"); capa["optional"]["wrappers"]["allowed"].append("flash_strobe");
capa["optional"]["wrappers"]["allowed"].append("silverlight");
capa["optional"]["wrappers"]["allowed"].append("img");
capa["optional"]["wrappers"]["option"] = "--wrappers"; capa["optional"]["wrappers"]["option"] = "--wrappers";
capa["optional"]["wrappers"]["short"] = "w"; capa["optional"]["wrappers"]["short"] = "w";
cfg->addConnectorOptions(8080, capa); cfg->addConnectorOptions(8080, capa);
@ -294,6 +293,10 @@ namespace Mist {
fullURL.protocol = getProtocolForPort(fullURL.getPort()); fullURL.protocol = getProtocolForPort(fullURL.getPort());
} }
std::string uAgent = H.GetHeader("User-Agent"); std::string uAgent = H.GetHeader("User-Agent");
std::string devSkin = "";
if (H.GetVar("dev").size()) { devSkin = ",skin:\"dev\""; }
H.Clean(); H.Clean();
H.SetHeader("Content-Type", "text/html"); H.SetHeader("Content-Type", "text/html");
H.SetHeader("Server", "MistServer/" PACKAGE_VERSION); H.SetHeader("Server", "MistServer/" PACKAGE_VERSION);
@ -307,7 +310,7 @@ namespace Mist {
std::string hlsUrl = "/hls/"+streamName+"/index.m3u8"; std::string hlsUrl = "/hls/"+streamName+"/index.m3u8";
std::string mp4Url = "/"+streamName+".mp4"; std::string mp4Url = "/"+streamName+".mp4";
H.SetBody("<!DOCTYPE html><html><head><title>"+streamName+"</title><style>body{color:white;background:black;}</style></head><body><div class=mistvideo id=\""+streamName+"\"><noscript><video controls autoplay><source src=\""+hlsUrl+"\" type=\"application/vnd.apple.mpegurl\"><source src=\""+mp4Url+"\" type=\"video/mp4\"><a href=\""+hlsUrl+"\">Click here to play the video [Apple]</a><br><a href=\""+mp4Url+"\">Click here to play the video [MP4]</a></video></noscript><script src=\"player.js\"></script><script>mistPlay('"+streamName+"',{host:'"+fullURL.getUrl()+"',target:document.getElementById('"+streamName+"')})</script></div></body></html>"); H.SetBody("<!DOCTYPE html><html><head><title>"+streamName+"</title><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><style>html{margin:0;padding:0;display:table;width:100%;height:100%;}body{color:white;background:#0f0f0f;margin:0;padding:0;display:table-cell;vertical-align:middle;text-align:center}body>div>div{text-align:left;}</style></head><body><div class=mistvideo id=\""+streamName+"\"><noscript><video controls autoplay><source src=\""+hlsUrl+"\" type=\"application/vnd.apple.mpegurl\"><source src=\""+mp4Url+"\" type=\"video/mp4\"><a href=\""+hlsUrl+"\">Click here to play the video [Apple]</a><br><a href=\""+mp4Url+"\">Click here to play the video [MP4]</a></video></noscript><script src=\"player.js\"></script><script>mistPlay('"+streamName+"',{host:'"+fullURL.getUrl()+"',target:document.getElementById('"+streamName+"')"+devSkin+"})</script></div></body></html>");
if ((uAgent.find("iPad") != std::string::npos) || (uAgent.find("iPod") != std::string::npos) || (uAgent.find("iPhone") != std::string::npos)) { if ((uAgent.find("iPad") != std::string::npos) || (uAgent.find("iPod") != std::string::npos) || (uAgent.find("iPhone") != std::string::npos)) {
H.SetHeader("Location",hlsUrl); H.SetHeader("Location",hlsUrl);
H.SendResponse("307", "HLS redirect", myConn); H.SendResponse("307", "HLS redirect", myConn);
@ -568,7 +571,7 @@ namespace Mist {
return; return;
} }
if ((H.url.length() > 9 && H.url.substr(0, 6) == "/info_" && H.url.substr(H.url.length() - 3, 3) == ".js") || (H.url.length() > 10 && H.url.substr(0, 7) == "/embed_" && H.url.substr(H.url.length() - 3, 3) == ".js") || (H.url.length() > 9 && H.url.substr(0, 6) == "/json_" && H.url.substr(H.url.length() - 3, 3) == ".js")){ if ((H.url.length() > 9 && H.url.substr(0, 6) == "/info_" && H.url.substr(H.url.length() - 3, 3) == ".js") || (H.url.length() > 9 && H.url.substr(0, 6) == "/json_" && H.url.substr(H.url.length() - 3, 3) == ".js")){
if (websocketHandler()){return;} if (websocketHandler()){return;}
std::string reqHost = HTTP::URL(H.GetHeader("Host")).host; std::string reqHost = HTTP::URL(H.GetHeader("Host")).host;
std::string useragent = H.GetVar("ua"); std::string useragent = H.GetVar("ua");
@ -600,24 +603,13 @@ namespace Mist {
}else{ }else{
response = json_resp.toString(); response = json_resp.toString();
} }
if (rURL.substr(0, 7) == "/embed_" && !json_resp.isMember("error")){
#include "embed.js.h"
response.append("\n(");
if (embed_js[embed_js_len - 2] == ';'){//check if we have a trailing ;\n or just \n
response.append((char*)embed_js, (size_t)embed_js_len - 2); //remove trailing ";\n" from xxd conversion
}else{
response.append((char*)embed_js, (size_t)embed_js_len - 1); //remove trailing "\n" from xxd conversion
}
response.append("(\"" + streamName + "\"));\n");
}
H.SetBody(response); H.SetBody(response);
H.SendResponse("200", "OK", myConn); H.SendResponse("200", "OK", myConn);
H.Clean(); H.Clean();
return; return;
} //embed code generator } //embed code generator
if ((H.url == "/player.js") || ((H.url.substr(0, 7) == "/embed_") && (H.url.length() > 10) && (H.url.substr(H.url.length() - 3, 3) == ".js"))){
if (H.url == "/player.js"){
HTTP::URL fullURL(H.GetHeader("Host")); HTTP::URL fullURL(H.GetHeader("Host"));
if (!fullURL.protocol.size()){ if (!fullURL.protocol.size()){
fullURL.protocol = getProtocolForPort(fullURL.getPort()); fullURL.protocol = getProtocolForPort(fullURL.getPort());
@ -627,7 +619,7 @@ namespace Mist {
H.Clean(); H.Clean();
H.SetHeader("Server", "MistServer/" PACKAGE_VERSION); H.SetHeader("Server", "MistServer/" PACKAGE_VERSION);
H.setCORSHeaders(); H.setCORSHeaders();
H.SetHeader("Content-Type", "application/javascript"); H.SetHeader("Content-Type", "application/javascript; charset=utf-8");
if(method == "OPTIONS" || method == "HEAD"){ if(method == "OPTIONS" || method == "HEAD"){
H.SendResponse("200", "OK", myConn); H.SendResponse("200", "OK", myConn);
H.Clean(); H.Clean();
@ -635,8 +627,10 @@ namespace Mist {
} }
response.append("if (typeof mistoptions == 'undefined') { mistoptions = {}; }\nif (!('host' in mistoptions)) { mistoptions.host = '"+fullURL.getUrl()+"'; }\n"); response.append("if (typeof mistoptions == 'undefined') { mistoptions = {}; }\nif (!('host' in mistoptions)) { mistoptions.host = '"+fullURL.getUrl()+"'; }\n");
#include "core.js.h"
response.append((char*)core_js, (size_t)core_js_len); #include "player.js.h"
response.append((char*)player_js, (size_t)player_js_len);
jsonForEach(config->getOption("wrappers",true),it){ jsonForEach(config->getOption("wrappers",true),it){
bool used = false; bool used = false;
if (it->asStringRef() == "html5"){ if (it->asStringRef() == "html5"){
@ -649,26 +643,6 @@ namespace Mist {
response.append((char*)flash_strobe_js, (size_t)flash_strobe_js_len); response.append((char*)flash_strobe_js, (size_t)flash_strobe_js_len);
used = true; used = true;
} }
if (it->asStringRef() == "silverlight"){
#include "silverlight.js.h"
response.append((char*)silverlight_js, (size_t)silverlight_js_len);
used = true;
}
if (it->asStringRef() == "theoplayer"){
#include "theoplayer.js.h"
response.append((char*)theoplayer_js, (size_t)theoplayer_js_len);
used = true;
}
if (it->asStringRef() == "jwplayer"){
#include "jwplayer.js.h"
response.append((char*)jwplayer_js, (size_t)jwplayer_js_len);
used = true;
}
if (it->asStringRef() == "polytrope"){
#include "polytrope.js.h"
response.append((char*)polytrope_js, (size_t)polytrope_js_len);
used = true;
}
if (it->asStringRef() == "dashjs"){ if (it->asStringRef() == "dashjs"){
#include "dashjs.js.h" #include "dashjs.js.h"
response.append((char*)dash_js, (size_t)dash_js_len); response.append((char*)dash_js, (size_t)dash_js_len);
@ -679,9 +653,9 @@ namespace Mist {
response.append((char*)video_js, (size_t)video_js_len); response.append((char*)video_js, (size_t)video_js_len);
used = true; used = true;
} }
if (it->asStringRef() == "img"){ if (it->asStringRef() == "webrtc"){
#include "img.js.h" #include "webrtc.js.h"
response.append((char*)img_js, (size_t)img_js_len); response.append((char*)webrtc_js, (size_t)webrtc_js_len);
used = true; used = true;
} }
if (!used) { if (!used) {
@ -689,6 +663,10 @@ namespace Mist {
} }
} }
if ((rURL.substr(0, 7) == "/embed_") && (rURL.length() > 10) && (rURL.substr(rURL.length() - 3, 3) == ".js")){
response.append("var container = document.createElement(\"div\");\ncontainer.id = \""+streamName+"\";\ndocument.write(container.outerHTML);\nmistPlay(\""+streamName+"\",{target:document.getElementById(\""+streamName+"\")});");
}
H.SetBody(response); H.SetBody(response);
H.SendResponse("200", "OK", myConn); H.SendResponse("200", "OK", myConn);
H.Clean(); H.Clean();
@ -696,8 +674,9 @@ namespace Mist {
} }
if (H.url == "/player.css"){ if (H.url.substr(0, 7) == "/skins/"){
std::string response; std::string response;
std::string url = H.url;
H.Clean(); H.Clean();
H.SetHeader("Server", "MistServer/" PACKAGE_VERSION); H.SetHeader("Server", "MistServer/" PACKAGE_VERSION);
H.setCORSHeaders(); H.setCORSHeaders();
@ -708,8 +687,24 @@ namespace Mist {
return; return;
} }
#include "mist.css.h" if (url == "/skins/default.css") {
response.append((char*)mist_css, (size_t)mist_css_len); #include "skin_default.css.h"
response.append((char*)skin_default_css, (size_t)skin_default_css_len);
}
else if (url == "/skins/dev.css") {
#include "skin_dev.css.h"
response.append((char*)skin_dev_css, (size_t)skin_dev_css_len);
}
else if (url == "/skins/videojs.css") {
#include "skin_videojs.css.h"
response.append((char*)skin_videojs_css, (size_t)skin_videojs_css_len);
}
else {
H.SetBody("Unknown stylesheet: "+url);
H.SendResponse("404", "Unknown stylesheet", myConn);
H.Clean();
return;
}
H.SetBody(response); H.SetBody(response);
H.SendResponse("200", "OK", myConn); H.SendResponse("200", "OK", myConn);
@ -728,10 +723,8 @@ namespace Mist {
return; return;
} }
#include "playervideo.js.h" #include "player_video.js.h"
response.append((char*)playervideo_js, (size_t)playervideo_js_len); response.append((char*)player_video_js, (size_t)player_video_js_len);
#include "playerhlsvideo.js.h"
response.append((char*)playerhlsvideo_js, (size_t)playerhlsvideo_js_len);
H.SetBody(response); H.SetBody(response);
H.SendResponse("200", "OK", myConn); H.SendResponse("200", "OK", myConn);
@ -750,10 +743,30 @@ namespace Mist {
return; return;
} }
#include "playerdashlic.js.h" #include "player_dash_lic.js.h"
response.append((char*)playerdashlic_js, (size_t)playerdashlic_js_len); response.append((char*)player_dash_lic_js, (size_t)player_dash_lic_js_len);
#include "playerdash.js.h" #include "player_dash.js.h"
response.append((char*)playerdash_js, (size_t)playerdash_js_len); response.append((char*)player_dash_js, (size_t)player_dash_js_len);
H.SetBody(response);
H.SendResponse("200", "OK", myConn);
H.Clean();
return;
}
if (H.url == "/webrtc.js"){
std::string response;
H.Clean();
H.SetHeader("Server", "MistServer/" PACKAGE_VERSION);
H.setCORSHeaders();
H.SetHeader("Content-Type", "application/javascript");
if (method == "OPTIONS" || method == "HEAD"){
H.SendResponse("200", "OK", myConn);
H.Clean();
return;
}
#include "player_webrtc.js.h"
response.append((char*)player_webrtc_js, (size_t)player_webrtc_js_len);
H.SetBody(response); H.SetBody(response);
H.SendResponse("200", "OK", myConn); H.SendResponse("200", "OK", myConn);