mirror of
https://github.com/ossrs/srs.git
synced 2025-02-15 04:42:04 +00:00
merge upstream
This commit is contained in:
commit
3753e8c5f1
47 changed files with 3285 additions and 1178 deletions
44
README.md
44
README.md
|
@ -43,13 +43,13 @@ python ./research/api-server/server.py 8085
|
|||
</pre>
|
||||
<strong>step 6:</strong> publish live stream <br/>
|
||||
<pre>
|
||||
FMS URL: rtmp://127.0.0.1:1935/live
|
||||
FMS URL: rtmp://127.0.0.1/live
|
||||
Stream: livestream
|
||||
For example, use ffmpeg to publish:
|
||||
for((;;)); do \
|
||||
./objs/ffmpeg/bin/ffmpeg -re -i ./doc/source.200kbps.768x320.flv \
|
||||
-vcodec copy -acodec copy \
|
||||
-f flv -y rtmp://127.0.0.1:1935/live/livestream; \
|
||||
-f flv -y rtmp://127.0.0.1/live?vhost=demo.srs.com/livestream; \
|
||||
sleep 1; \
|
||||
done
|
||||
</pre>
|
||||
|
@ -59,35 +59,35 @@ For example, use ffmpeg to publish:
|
|||
# linux: /etc/hosts
|
||||
# windows: C:\Windows\System32\drivers\etc\hosts
|
||||
# where server ip is 192.168.2.111
|
||||
192.168.2.111 demo
|
||||
192.168.2.111 demo.srs.com
|
||||
</pre>
|
||||
<strong>step 8:</strong> play live stream. <br/>
|
||||
<pre>
|
||||
players: http://demo:80/players
|
||||
rtmp url: rtmp://demo:1935/live/livestream
|
||||
m3u8 url: http://demo:80/live/livestream.m3u8
|
||||
for android: http://demo:80/live/livestream.html
|
||||
players: [http://demo.srs.com/players](http://demo.srs.com/players)
|
||||
rtmp url: rtmp://demo.srs.com/live/livestream
|
||||
m3u8 url: http://demo.srs.com/live/livestream.m3u8
|
||||
for android: http://demo.srs.com/live/livestream.html
|
||||
</pre>
|
||||
<strong>step 9(optinal):</strong> play live stream auto transcoded<br/>
|
||||
<pre>
|
||||
rtmp url: rtmp://demo:1935/live/livestream_ld
|
||||
m3u8 url: http://demo:80/live/livestream_ld.m3u8
|
||||
for android: http://demo:80/live/livestream_ld.html
|
||||
rtmp url: rtmp://demo:1935/live/livestream_sd
|
||||
m3u8 url: http://demo:80/live/livestream_sd.m3u8
|
||||
for android: http://demo:80/live/livestream_sd.html
|
||||
rtmp url: rtmp://demo.srs.com/live/livestream_ld
|
||||
m3u8 url: http://demo.srs.com/live/livestream_ld.m3u8
|
||||
for android: [http://demo.srs.com/live/livestream_ld.html](http://demo.srs.com/live/livestream_ld.html)
|
||||
rtmp url: rtmp://demo.srs.com/live/livestream_sd
|
||||
m3u8 url: http://demo.srs.com/live/livestream_sd.m3u8
|
||||
for android: [http://demo.srs.com/live/livestream_sd.html](http://demo.srs.com/live/livestream_sd.html)
|
||||
</pre>
|
||||
<strong>step 10(optinal):</strong> play live stream auto forwarded, the hls dir change to /forward<br/>
|
||||
<pre>
|
||||
rtmp url: rtmp://demo:19350/live/livestream
|
||||
m3u8 url: http://demo:80/forward/live/livestream.m3u8
|
||||
for android: http://demo:80/forward/live/livestream.html
|
||||
rtmp url: rtmp://demo:19350/live/livestream_ld
|
||||
m3u8 url: http://demo:80/forward/live/livestream_ld.m3u8
|
||||
for android: http://demo:80/forward/live/livestream_ld.html
|
||||
rtmp url: rtmp://demo:19350/live/livestream_sd
|
||||
m3u8 url: http://demo:80/forward/live/livestream_sd.m3u8
|
||||
for android: http://demo:80/forward/live/livestream_sd.html
|
||||
rtmp url: rtmp://demo.srs.com:19350/live/livestream
|
||||
m3u8 url: http://demo.srs.com/forward/live/livestream.m3u8
|
||||
for android: [http://demo.srs.com/forward/live/livestream.html](http://demo.srs.com/forward/live/livestream.html)
|
||||
rtmp url: rtmp://demo.srs.com:19350/live/livestream_ld
|
||||
m3u8 url: http://demo.srs.com/forward/live/livestream_ld.m3u8
|
||||
for android: [http://demo.srs.com/forward/live/livestream_ld.html](http://demo.srs.com/forward/live/livestream_ld.html)
|
||||
rtmp url: rtmp://demo.srs.com:19350/live/livestream_sd
|
||||
m3u8 url: http://demo.srs.com/forward/live/livestream_sd.m3u8
|
||||
for android: [http://demo.srs.com/forward/live/livestream_sd.html](http://demo.srs.com/forward/live/livestream_sd.html)
|
||||
</pre>
|
||||
<strong>step 11(optinal):</strong> modify the config and reload it (all features support reload)<br/>
|
||||
<pre>
|
||||
|
|
|
@ -243,7 +243,8 @@ if [ $SRS_HLS = YES ]; then
|
|||
sed -i "s/^.user nobody;/user `whoami`;/g" ${SRS_OBJS}/nginx/conf/nginx.conf
|
||||
|
||||
# create forward dir
|
||||
mkdir -p ${SRS_OBJS}/nginx/html/forward
|
||||
mkdir -p ${SRS_OBJS}/nginx/html/live &&
|
||||
mkdir -p ${SRS_OBJS}/nginx/html/forward/live
|
||||
|
||||
# generate default html pages for android.
|
||||
html_file=${SRS_OBJS}/nginx/html/live/livestream.html && hls_stream=livestream.m3u8 && write_nginx_html5
|
||||
|
@ -254,7 +255,17 @@ if [ $SRS_HLS = YES ]; then
|
|||
html_file=${SRS_OBJS}/nginx/html/forward/live/livestream_sd.html && hls_stream=livestream_sd.m3u8 && write_nginx_html5
|
||||
|
||||
# copy players to nginx html dir.
|
||||
cp research/players ${SRS_OBJS}/nginx/html/ -r
|
||||
rm -rf ${SRS_OBJS}/nginx/html/players &&
|
||||
ln -sf `pwd`/research/players ${SRS_OBJS}/nginx/html/players &&
|
||||
rm -f ${SRS_OBJS}/nginx/crossdomain.xml &&
|
||||
ln -sf `pwd`/research/players/crossdomain.xml ${SRS_OBJS}/nginx/html/crossdomain.xml
|
||||
|
||||
# override the default index.
|
||||
cat <<END > ${SRS_OBJS}/nginx/html/index.html
|
||||
<script type="text/javascript">
|
||||
window.location.href = "players/index.html";
|
||||
</script>
|
||||
END
|
||||
fi
|
||||
|
||||
if [ $SRS_HLS = YES ]; then
|
||||
|
@ -327,8 +338,10 @@ fi
|
|||
#####################################################################################
|
||||
# build research code
|
||||
#####################################################################################
|
||||
(cd research/hls && make)
|
||||
mkdir -p ${SRS_OBJS}/research
|
||||
|
||||
(cd research/hls && make && mv ts_info ../../${SRS_OBJS}/research)
|
||||
ret=$?; if [[ $ret -ne 0 ]]; then echo "build research/hls failed, ret=$ret"; exit $ret; fi
|
||||
|
||||
(cd research/ffempty && make)
|
||||
(cd research/ffempty && make && mv ffempty ../../${SRS_OBJS}/research)
|
||||
ret=$?; if [[ $ret -ne 0 ]]; then echo "build research/ffempty failed, ret=$ret"; exit $ret; fi
|
||||
|
|
|
@ -3,9 +3,11 @@ vhost __defaultVhost__ {
|
|||
chunk_size 65000;
|
||||
enabled on;
|
||||
gop_cache on;
|
||||
hls on;
|
||||
hls_path ./objs/nginx/html/forward;
|
||||
hls_fragment 5;
|
||||
hls_window 30;
|
||||
hls {
|
||||
enabled on;
|
||||
hls_path ./objs/nginx/html/forward;
|
||||
hls_fragment 5;
|
||||
hls_window 30;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,11 +18,18 @@ chunk_size 65000;
|
|||
# vhost list, the __defaultVhost__ is the default vhost
|
||||
# for example, user use ip to access the stream: rtmp://192.168.1.2/live/livestream.
|
||||
# for which cannot identify the required vhost.
|
||||
# for default demo.
|
||||
vhost __defaultVhost__ {
|
||||
chunk_size 65000;
|
||||
enabled on;
|
||||
gop_cache on;
|
||||
}
|
||||
# vhost list, the __defaultVhost__ is the default vhost
|
||||
# for example, user use ip to access the stream: rtmp://192.168.1.2/live/livestream.
|
||||
# for which cannot identify the required vhost.
|
||||
# for default demo.
|
||||
vhost demo.srs.com {
|
||||
enabled on;
|
||||
gop_cache on;
|
||||
queue_length 30;
|
||||
forward 127.0.0.1:19350;
|
||||
hls {
|
||||
|
@ -32,7 +39,7 @@ vhost __defaultVhost__ {
|
|||
hls_window 30;
|
||||
}
|
||||
http_hooks {
|
||||
enabled off;
|
||||
enabled on;
|
||||
on_connect http://127.0.0.1:8085/api/v1/clients;
|
||||
on_close http://127.0.0.1:8085/api/v1/clients;
|
||||
on_publish http://127.0.0.1:8085/api/v1/streams;
|
||||
|
@ -46,7 +53,7 @@ vhost __defaultVhost__ {
|
|||
engine ld {
|
||||
enabled on;
|
||||
vfilter {
|
||||
vf 'drawtext=text=SimpleRtmpServer(SRS):x=10:y=10:fontcolor=#cccccc:fontfile=./doc/FreeSerifBold.ttf';
|
||||
vf 'drawtext=text=SimpleRtmpServer(SRS):x=10:y=10:fontsize=30:fontcolor=#cccccc:fontfile=./doc/FreeSerifBold.ttf';
|
||||
}
|
||||
vcodec libx264;
|
||||
vbitrate 300;
|
||||
|
@ -91,15 +98,57 @@ vhost __defaultVhost__ {
|
|||
}
|
||||
}
|
||||
}
|
||||
# for the players site, to play or publish.
|
||||
# the flash player publisher need to transcode to support hls,
|
||||
# we add players_hls vhost to support it.
|
||||
vhost players {
|
||||
enabled on;
|
||||
gop_cache on;
|
||||
transcode {
|
||||
enabled on;
|
||||
ffmpeg ./objs/ffmpeg/bin/ffmpeg;
|
||||
engine hls {
|
||||
enabled on;
|
||||
vfilter {
|
||||
vf 'drawtext=text=SRS(SimpleRtmpServer):x=10:y=10:fontcolor=#cccccc:fontfile=./doc/FreeSerifBold.ttf';
|
||||
}
|
||||
vcodec libx264;
|
||||
vbitrate 300;
|
||||
vfps 20;
|
||||
vwidth 768;
|
||||
vheight 320;
|
||||
vthreads 1;
|
||||
vprofile baseline;
|
||||
vpreset superfast;
|
||||
vparams {
|
||||
}
|
||||
acodec libaacplus;
|
||||
abitrate 30;
|
||||
asample_rate 44100;
|
||||
achannels 2;
|
||||
aparams {
|
||||
}
|
||||
output rtmp://127.0.0.1:[port]/[app]?vhost=players_pub/[stream];
|
||||
}
|
||||
}
|
||||
}
|
||||
vhost players_pub {
|
||||
hls {
|
||||
enabled on;
|
||||
hls_path ./objs/nginx/html;
|
||||
hls_fragment 5;
|
||||
hls_window 30;
|
||||
}
|
||||
}
|
||||
# for development
|
||||
vhost dev {
|
||||
chunk_size 65000;
|
||||
enabled on;
|
||||
gop_cache on;
|
||||
queue_length 10;
|
||||
forward 127.0.0.1:19350;
|
||||
#forward 127.0.0.1:19350;
|
||||
hls {
|
||||
enabled on;
|
||||
enabled off;
|
||||
hls_path ./objs/nginx/html;
|
||||
hls_fragment 5;
|
||||
hls_window 30;
|
||||
|
@ -587,7 +636,7 @@ vhost all.transcode.vhost.com {
|
|||
vhost ffempty.transcode.vhost.com {
|
||||
transcode {
|
||||
enabled on;
|
||||
ffmpeg ./research/ffempty/ffempty;
|
||||
ffmpeg ./objs/research/ffempty;
|
||||
engine empty {
|
||||
enabled on;
|
||||
vcodec libx264;
|
||||
|
|
3
trunk/research/players/crossdomain.xml
Normal file
3
trunk/research/players/crossdomain.xml
Normal file
|
@ -0,0 +1,3 @@
|
|||
<cross-domain-policy>
|
||||
<allow-access-from domain="*"/>
|
||||
</cross-domain-policy>
|
9
trunk/research/players/css/bootstrap.min.css
vendored
Executable file
9
trunk/research/players/css/bootstrap.min.css
vendored
Executable file
File diff suppressed because one or more lines are too long
BIN
trunk/research/players/img/tooltip.png
Normal file
BIN
trunk/research/players/img/tooltip.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 783 B |
|
@ -1,13 +1,50 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Players</title>
|
||||
<meta http-equiv=Content-Type content="text/html;charset=utf-8">
|
||||
<title>SRS</title>
|
||||
<meta charset="utf-8">
|
||||
<link rel="stylesheet" type="text/css" href="css/bootstrap.min.css"/>
|
||||
<script type="text/javascript" src="js/jquery-1.10.2.min.js"></script>
|
||||
<script type="text/javascript" src="js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="js/swfobject.js"></script>
|
||||
<script type="text/javascript" src="js/srs.js"></script>
|
||||
<style>
|
||||
body{
|
||||
padding-top: 55px;
|
||||
}
|
||||
</style>
|
||||
<script type="text/javascript">
|
||||
$(function(){
|
||||
update_nav();
|
||||
|
||||
// direct to the default vhost for players.
|
||||
window.location.href = "srs_player.html?vhost=" + srs_get_player_vhost();
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
welcome!</p>
|
||||
<hr/>
|
||||
<p><a href="rtmp/index.html">Rtmp流播放器</a></p>
|
||||
<p><a href="osmf/index.html">OSMF播放器</a></p>
|
||||
<p><a href="jwplayer5/index.html">JWPlayer5</a></p>
|
||||
<p><a href="jwplayer6/index.html">JWPlayer6</a></p>
|
||||
<p><a href="http://www.videolan.org/vlc/">VLC for HLS/rtmp/rtsp/http....</a></p>
|
||||
<div class="navbar navbar-fixed-top">
|
||||
<div class="navbar-inner">
|
||||
<div class="container">
|
||||
<a class="brand" href="index.html">SRS</a>
|
||||
<div class="nav-collapse collapse">
|
||||
<ul class="nav">
|
||||
<li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li>
|
||||
<li><a id="nav_srs_publisher" href="srs_publisher.html">SRS编码器</a></li>
|
||||
<li><a id="nav_srs_bwt" href="srs_bwt.html">SRS测网速</a></li>
|
||||
<li><a id="nav_jwplayer6" href="jwplayer6.html">JWPlayer6播放器</a></li>
|
||||
<li><a id="nav_osmf" href="osmf.html">AdobeOSMF播放器</a></li>
|
||||
<li><a id="nav_vlc" href="vlc.html">VLC播放器</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<hr>
|
||||
<footer>
|
||||
<p><a href="https://github.com/winlinvip/simple-rtmp-server">SRS Team © 2013</a></p>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
|
|
0
trunk/research/players/osmf/AdobeFlashPlayerInstall.swf → trunk/research/players/js/AdobeFlashPlayerInstall.swf
Normal file → Executable file
0
trunk/research/players/osmf/AdobeFlashPlayerInstall.swf → trunk/research/players/js/AdobeFlashPlayerInstall.swf
Normal file → Executable file
6
trunk/research/players/js/bootstrap.min.js
vendored
Executable file
6
trunk/research/players/js/bootstrap.min.js
vendored
Executable file
File diff suppressed because one or more lines are too long
6
trunk/research/players/js/jquery-1.10.2.min.js
vendored
Executable file
6
trunk/research/players/js/jquery-1.10.2.min.js
vendored
Executable file
File diff suppressed because one or more lines are too long
1
trunk/research/players/js/jquery-1.10.2.min.map
Executable file
1
trunk/research/players/js/jquery-1.10.2.min.map
Executable file
File diff suppressed because one or more lines are too long
BIN
trunk/research/players/js/jwplayer.flash.swf
Normal file
BIN
trunk/research/players/js/jwplayer.flash.swf
Normal file
Binary file not shown.
194
trunk/research/players/js/jwplayer.html5.js
Normal file
194
trunk/research/players/js/jwplayer.html5.js
Normal file
File diff suppressed because one or more lines are too long
76
trunk/research/players/js/jwplayer.js
Normal file
76
trunk/research/players/js/jwplayer.js
Normal file
|
@ -0,0 +1,76 @@
|
|||
"undefined"==typeof jwplayer&&(jwplayer=function(f){if(jwplayer.api)return jwplayer.api.selectPlayer(f)},jwplayer.version="6.4.3359",jwplayer.vid=document.createElement("video"),jwplayer.audio=document.createElement("audio"),jwplayer.source=document.createElement("source"),function(f){function a(g){return function(){return c(g)}}var l=document,e=window,j=navigator,b=f.utils=function(){};b.exists=function(g){switch(typeof g){case "string":return 0<g.length;case "object":return null!==g;case "undefined":return!1}return!0};
|
||||
b.styleDimension=function(g){return g+(0<g.toString().indexOf("%")?"":"px")};b.getAbsolutePath=function(g,a){b.exists(a)||(a=l.location.href);if(b.exists(g)){var c;if(b.exists(g)){c=g.indexOf("://");var j=g.indexOf("?");c=0<c&&(0>j||j>c)}else c=void 0;if(c)return g;c=a.substring(0,a.indexOf("://")+3);var j=a.substring(c.length,a.indexOf("/",c.length+1)),d;0===g.indexOf("/")?d=g.split("/"):(d=a.split("?")[0],d=d.substring(c.length+j.length+1,d.lastIndexOf("/")),d=d.split("/").concat(g.split("/")));
|
||||
for(var h=[],e=0;e<d.length;e++)d[e]&&(b.exists(d[e])&&"."!=d[e])&&(".."==d[e]?h.pop():h.push(d[e]));return c+j+"/"+h.join("/")}};b.extend=function(){var a=b.extend.arguments;if(1<a.length){for(var c=1;c<a.length;c++)b.foreach(a[c],function(c,d){try{b.exists(d)&&(a[0][c]=d)}catch(j){}});return a[0]}return null};b.log=function(a,b){"undefined"!=typeof console&&"undefined"!=typeof console.log&&(b?console.log(a,b):console.log(a))};var c=b.userAgentMatch=function(a){return null!==j.userAgent.toLowerCase().match(a)};
|
||||
b.isIE=a(/msie/i);b.isFF=a(/firefox/i);b.isChrome=a(/chrome/i);b.isIOS=a(/iP(hone|ad|od)/i);b.isIPod=a(/iP(hone|od)/i);b.isIPad=a(/iPad/i);b.isSafari602=a(/Macintosh.*Mac OS X 10_8.*6\.0\.\d* Safari/i);b.isAndroid=function(a){return a?c(RegExp("android.*"+a,"i")):c(/android/i)};b.isMobile=function(){return b.isIOS()||b.isAndroid()};b.saveCookie=function(a,b){l.cookie="jwplayer."+a+"\x3d"+b+"; path\x3d/"};b.getCookies=function(){for(var a={},b=l.cookie.split("; "),c=0;c<b.length;c++){var d=b[c].split("\x3d");
|
||||
0==d[0].indexOf("jwplayer.")&&(a[d[0].substring(9,d[0].length)]=d[1])}return a};b.typeOf=function(a){var b=typeof a;return"object"===b?!a?"null":a instanceof Array?"array":b:b};b.translateEventResponse=function(a,c){var d=b.extend({},c);a==f.events.JWPLAYER_FULLSCREEN&&!d.fullscreen?(d.fullscreen="true"==d.message?!0:!1,delete d.message):"object"==typeof d.data?(d=b.extend(d,d.data),delete d.data):"object"==typeof d.metadata&&b.deepReplaceKeyName(d.metadata,["__dot__","__spc__","__dsh__","__default__"],
|
||||
["."," ","-","default"]);b.foreach(["position","duration","offset"],function(a,b){d[b]&&(d[b]=Math.round(1E3*d[b])/1E3)});return d};b.flashVersion=function(){if(b.isAndroid())return 0;var a=j.plugins,d;try{if("undefined"!==a&&(d=a["Shockwave Flash"]))return parseInt(d.description.replace(/\D+(\d+)\..*/,"$1"))}catch(c){}if("undefined"!=typeof e.ActiveXObject)try{if(d=new ActiveXObject("ShockwaveFlash.ShockwaveFlash"))return parseInt(d.GetVariable("$version").split(" ")[1].split(",")[0])}catch(f){}return 0};
|
||||
b.getScriptPath=function(a){for(var b=l.getElementsByTagName("script"),d=0;d<b.length;d++){var c=b[d].src;if(c&&0<=c.indexOf(a))return c.substr(0,c.indexOf(a))}return""};b.deepReplaceKeyName=function(a,d,c){switch(f.utils.typeOf(a)){case "array":for(var j=0;j<a.length;j++)a[j]=f.utils.deepReplaceKeyName(a[j],d,c);break;case "object":b.foreach(a,function(b,h){var j;if(d instanceof Array&&c instanceof Array){if(d.length!=c.length)return;j=d}else j=[d];for(var e=b,l=0;l<j.length;l++)e=e.replace(RegExp(d[l],
|
||||
"g"),c[l]);a[e]=f.utils.deepReplaceKeyName(h,d,c);b!=e&&delete a[b]})}return a};var d=b.pluginPathType={ABSOLUTE:0,RELATIVE:1,CDN:2};b.getPluginPathType=function(a){if("string"==typeof a){a=a.split("?")[0];var c=a.indexOf("://");if(0<c)return d.ABSOLUTE;var j=a.indexOf("/");a=b.extension(a);return 0>c&&0>j&&(!a||!isNaN(a))?d.CDN:d.RELATIVE}};b.getPluginName=function(a){return a.replace(/^(.*\/)?([^-]*)-?.*\.(swf|js)$/,"$2")};b.getPluginVersion=function(a){return a.replace(/[^-]*-?([^\.]*).*$/,"$1")};
|
||||
b.isYouTube=function(a){return-1<a.indexOf("youtube.com")||-1<a.indexOf("youtu.be")};b.isRtmp=function(a,b){return 0==a.indexOf("rtmp")||"rtmp"==b};b.foreach=function(a,b){var d,c;for(d in a)a.hasOwnProperty(d)&&(c=a[d],b(d,c))};b.isHTTPS=function(){return 0==e.location.href.indexOf("https")};b.repo=function(){var a=""+f.version.split(/\W/).splice(0,2).join("/")+"/";try{b.isHTTPS()&&(a=a.replace("http://","https://ssl."))}catch(d){}return a}}(jwplayer),function(f){var a="video/",
|
||||
l=f.foreach,e={mp4:a+"mp4",vorbis:"audio/ogg",ogg:a+"ogg",webm:a+"webm",aac:"audio/mp4",mp3:"audio/mpeg",hls:"application/vnd.apple.mpegurl"},j={mp4:e.mp4,f4v:e.mp4,m4v:e.mp4,mov:e.mp4,m4a:e.aac,f4a:e.aac,aac:e.aac,mp3:e.mp3,ogv:e.ogg,ogg:e.vorbis,oga:e.vorbis,webm:e.webm,m3u8:e.hls,hls:e.hls},a="video",a={flv:a,f4v:a,mov:a,m4a:a,m4v:a,mp4:a,aac:a,f4a:a,mp3:"sound",smil:"rtmp",m3u8:"hls",hls:"hls"},b=f.extensionmap={};l(j,function(a,d){b[a]={html5:d}});l(a,function(a,d){b[a]||(b[a]={});b[a].flash=
|
||||
d});b.types=e;b.mimeType=function(a){var b;l(e,function(j,e){!b&&e==a&&(b=j)});return b};b.extType=function(a){return b.mimeType(j[a])}}(jwplayer.utils),function(f){var a=f.loaderstatus={NEW:0,LOADING:1,ERROR:2,COMPLETE:3},l=document;f.scriptloader=function(e){function j(){c=a.ERROR;g.sendEvent(d.ERROR)}function b(){c=a.COMPLETE;g.sendEvent(d.COMPLETE)}var c=a.NEW,d=jwplayer.events,g=new d.eventdispatcher;f.extend(this,g);this.load=function(){var g=f.scriptloader.loaders[e];if(g&&(g.getStatus()==
|
||||
a.NEW||g.getStatus()==a.LOADING))g.addEventListener(d.ERROR,j),g.addEventListener(d.COMPLETE,b);else if(f.scriptloader.loaders[e]=this,c==a.NEW){c=a.LOADING;var m=l.createElement("script");m.addEventListener?(m.onload=b,m.onerror=j):m.readyState&&(m.onreadystatechange=function(){("loaded"==m.readyState||"complete"==m.readyState)&&b()});l.getElementsByTagName("head")[0].appendChild(m);m.src=e}};this.getStatus=function(){return c}};f.scriptloader.loaders={}}(jwplayer.utils),function(f){f.trim=function(a){return a.replace(/^\s*/,
|
||||
"").replace(/\s*$/,"")};f.pad=function(a,f,e){for(e||(e="0");a.length<f;)a=e+a;return a};f.xmlAttribute=function(a,f){for(var e=0;e<a.attributes.length;e++)if(a.attributes[e].name&&a.attributes[e].name.toLowerCase()==f.toLowerCase())return a.attributes[e].value.toString();return""};f.extension=function(a){if(!a||"rtmp"==a.substr(0,4))return"";a=a.substring(a.lastIndexOf("/")+1,a.length).split("?")[0].split("#")[0];if(-1<a.lastIndexOf("."))return a.substr(a.lastIndexOf(".")+1,a.length).toLowerCase()};
|
||||
f.stringToColor=function(a){a=a.replace(/(#|0x)?([0-9A-F]{3,6})$/gi,"$2");3==a.length&&(a=a.charAt(0)+a.charAt(0)+a.charAt(1)+a.charAt(1)+a.charAt(2)+a.charAt(2));return parseInt(a,16)}}(jwplayer.utils),function(f){f.key=function(a){var l,e,j;this.edition=function(){return j&&j.getTime()<(new Date).getTime()?"invalid":l};this.token=function(){return e};f.exists(a)||(a="");try{a=f.tea.decrypt(a,"36QXq4W@GSBV^teR");var b=a.split("/");(l=b[0])||(l="free");e=b[1];b[2]&&0<parseInt(b[2])&&(j=new Date,j.setTime(String(b[2])))}catch(c){l=
|
||||
"invalid"}}}(jwplayer.utils),function(f){var a=f.tea={};a.encrypt=function(j,b){if(0==j.length)return"";var c=a.strToLongs(e.encode(j));1>=c.length&&(c[1]=0);for(var d=a.strToLongs(e.encode(b).slice(0,16)),g=c.length,f=c[g-1],m=c[0],n,k=Math.floor(6+52/g),h=0;0<k--;){h+=2654435769;n=h>>>2&3;for(var r=0;r<g;r++)m=c[(r+1)%g],f=(f>>>5^m<<2)+(m>>>3^f<<4)^(h^m)+(d[r&3^n]^f),f=c[r]+=f}c=a.longsToStr(c);return l.encode(c)};a.decrypt=function(j,b){if(0==j.length)return"";for(var c=a.strToLongs(l.decode(j)),
|
||||
d=a.strToLongs(e.encode(b).slice(0,16)),g=c.length,f=c[g-1],m=c[0],n,k=2654435769*Math.floor(6+52/g);0!=k;){n=k>>>2&3;for(var h=g-1;0<=h;h--)f=c[0<h?h-1:g-1],f=(f>>>5^m<<2)+(m>>>3^f<<4)^(k^m)+(d[h&3^n]^f),m=c[h]-=f;k-=2654435769}c=a.longsToStr(c);c=c.replace(/\0+$/,"");return e.decode(c)};a.strToLongs=function(a){for(var b=Array(Math.ceil(a.length/4)),c=0;c<b.length;c++)b[c]=a.charCodeAt(4*c)+(a.charCodeAt(4*c+1)<<8)+(a.charCodeAt(4*c+2)<<16)+(a.charCodeAt(4*c+3)<<24);return b};a.longsToStr=function(a){for(var b=
|
||||
Array(a.length),c=0;c<a.length;c++)b[c]=String.fromCharCode(a[c]&255,a[c]>>>8&255,a[c]>>>16&255,a[c]>>>24&255);return b.join("")};var l={code:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\x3d",encode:function(a,b){var c,d,g,f,m=[],n="",k,h,r=l.code;h=("undefined"==typeof b?0:b)?e.encode(a):a;k=h.length%3;if(0<k)for(;3>k++;)n+="\x3d",h+="\x00";for(k=0;k<h.length;k+=3)c=h.charCodeAt(k),d=h.charCodeAt(k+1),g=h.charCodeAt(k+2),f=c<<16|d<<8|g,c=f>>18&63,d=f>>12&63,g=f>>6&63,f&=63,m[k/
|
||||
3]=r.charAt(c)+r.charAt(d)+r.charAt(g)+r.charAt(f);m=m.join("");return m=m.slice(0,m.length-n.length)+n},decode:function(a,b){b="undefined"==typeof b?!1:b;var c,d,g,f,m,n=[],k,h=l.code;k=b?e.decode(a):a;for(var r=0;r<k.length;r+=4)c=h.indexOf(k.charAt(r)),d=h.indexOf(k.charAt(r+1)),f=h.indexOf(k.charAt(r+2)),m=h.indexOf(k.charAt(r+3)),g=c<<18|d<<12|f<<6|m,c=g>>>16&255,d=g>>>8&255,g&=255,n[r/4]=String.fromCharCode(c,d,g),64==m&&(n[r/4]=String.fromCharCode(c,d)),64==f&&(n[r/4]=String.fromCharCode(c));
|
||||
f=n.join("");return b?e.decode(f):f}},e={encode:function(a){a=a.replace(/[\u0080-\u07ff]/g,function(a){a=a.charCodeAt(0);return String.fromCharCode(192|a>>6,128|a&63)});return a=a.replace(/[\u0800-\uffff]/g,function(a){a=a.charCodeAt(0);return String.fromCharCode(224|a>>12,128|a>>6&63,128|a&63)})},decode:function(a){a=a.replace(/[\u00e0-\u00ef][\u0080-\u00bf][\u0080-\u00bf]/g,function(a){a=(a.charCodeAt(0)&15)<<12|(a.charCodeAt(1)&63)<<6|a.charCodeAt(2)&63;return String.fromCharCode(a)});return a=
|
||||
a.replace(/[\u00c0-\u00df][\u0080-\u00bf]/g,function(a){a=(a.charCodeAt(0)&31)<<6|a.charCodeAt(1)&63;return String.fromCharCode(a)})}}}(jwplayer.utils),function(f){f.events={COMPLETE:"COMPLETE",ERROR:"ERROR",API_READY:"jwplayerAPIReady",JWPLAYER_READY:"jwplayerReady",JWPLAYER_FULLSCREEN:"jwplayerFullscreen",JWPLAYER_RESIZE:"jwplayerResize",JWPLAYER_ERROR:"jwplayerError",JWPLAYER_MEDIA_BEFOREPLAY:"jwplayerMediaBeforePlay",JWPLAYER_MEDIA_BEFORECOMPLETE:"jwplayerMediaBeforeComplete",JWPLAYER_COMPONENT_SHOW:"jwplayerComponentShow",
|
||||
JWPLAYER_COMPONENT_HIDE:"jwplayerComponentHide",JWPLAYER_MEDIA_BUFFER:"jwplayerMediaBuffer",JWPLAYER_MEDIA_BUFFER_FULL:"jwplayerMediaBufferFull",JWPLAYER_MEDIA_ERROR:"jwplayerMediaError",JWPLAYER_MEDIA_LOADED:"jwplayerMediaLoaded",JWPLAYER_MEDIA_COMPLETE:"jwplayerMediaComplete",JWPLAYER_MEDIA_SEEK:"jwplayerMediaSeek",JWPLAYER_MEDIA_TIME:"jwplayerMediaTime",JWPLAYER_MEDIA_VOLUME:"jwplayerMediaVolume",JWPLAYER_MEDIA_META:"jwplayerMediaMeta",JWPLAYER_MEDIA_MUTE:"jwplayerMediaMute",JWPLAYER_MEDIA_LEVELS:"jwplayerMediaLevels",
|
||||
JWPLAYER_MEDIA_LEVEL_CHANGED:"jwplayerMediaLevelChanged",JWPLAYER_CAPTIONS_CHANGED:"jwplayerCaptionsChanged",JWPLAYER_CAPTIONS_LIST:"jwplayerCaptionsList",JWPLAYER_PLAYER_STATE:"jwplayerPlayerState",state:{BUFFERING:"BUFFERING",IDLE:"IDLE",PAUSED:"PAUSED",PLAYING:"PLAYING"},JWPLAYER_PLAYLIST_LOADED:"jwplayerPlaylistLoaded",JWPLAYER_PLAYLIST_ITEM:"jwplayerPlaylistItem",JWPLAYER_PLAYLIST_COMPLETE:"jwplayerPlaylistComplete",JWPLAYER_DISPLAY_CLICK:"jwplayerViewClick",JWPLAYER_CONTROLS:"jwplayerViewControls",
|
||||
JWPLAYER_INSTREAM_CLICK:"jwplayerInstreamClicked",JWPLAYER_INSTREAM_DESTROYED:"jwplayerInstreamDestroyed"}}(jwplayer),function(f){var a=jwplayer.utils;f.eventdispatcher=function(f,e){var j,b;this.resetEventListeners=function(){j={};b=[]};this.resetEventListeners();this.addEventListener=function(b,d,g){try{a.exists(j[b])||(j[b]=[]),"string"==a.typeOf(d)&&(d=(new Function("return "+d))()),j[b].push({listener:d,count:g})}catch(e){a.log("error",e)}return!1};this.removeEventListener=function(b,d){if(j[b]){try{for(var g=
|
||||
0;g<j[b].length;g++)if(j[b][g].listener.toString()==d.toString()){j[b].splice(g,1);break}}catch(e){a.log("error",e)}return!1}};this.addGlobalListener=function(c,d){try{"string"==a.typeOf(c)&&(c=(new Function("return "+c))()),b.push({listener:c,count:d})}catch(g){a.log("error",g)}return!1};this.removeGlobalListener=function(c){if(c){try{for(var d=0;d<b.length;d++)if(b[d].listener.toString()==c.toString()){b.splice(d,1);break}}catch(g){a.log("error",g)}return!1}};this.sendEvent=function(c,d){a.exists(d)||
|
||||
(d={});a.extend(d,{id:f,version:jwplayer.version,type:c});e&&a.log(c,d);if("undefined"!=a.typeOf(j[c]))for(var g=0;g<j[c].length;g++){try{j[c][g].listener(d)}catch(q){a.log("There was an error while handling a listener: "+q.toString(),j[c][g].listener)}j[c][g]&&(1===j[c][g].count?delete j[c][g]:0<j[c][g].count&&(j[c][g].count-=1))}for(g=0;g<b.length;g++){try{b[g].listener(d)}catch(m){a.log("There was an error while handling a listener: "+m.toString(),b[g].listener)}b[g]&&(1===b[g].count?delete b[g]:
|
||||
0<b[g].count&&(b[g].count-=1))}}}}(jwplayer.events),function(f){var a={},l={};f.plugins=function(){};f.plugins.loadPlugins=function(e,j){l[e]=new f.plugins.pluginloader(new f.plugins.model(a),j);return l[e]};f.plugins.registerPlugin=function(e,j,b,c){var d=f.utils.getPluginName(e);a[d]||(a[d]=new f.plugins.plugin(e));a[d].registerPlugin(e,j,b,c)}}(jwplayer),function(f){f.plugins.model=function(a){this.addPlugin=function(l){var e=f.utils.getPluginName(l);a[e]||(a[e]=new f.plugins.plugin(l));return a[e]};
|
||||
this.getPlugins=function(){return a}}}(jwplayer),function(f){var a=jwplayer.utils,l=jwplayer.events;f.pluginmodes={FLASH:0,JAVASCRIPT:1,HYBRID:2};f.plugin=function(e){function j(){switch(a.getPluginPathType(e)){case a.pluginPathType.ABSOLUTE:return e;case a.pluginPathType.RELATIVE:return a.getAbsolutePath(e,window.location.href)}}function b(){n=setTimeout(function(){d=a.loaderstatus.COMPLETE;k.sendEvent(l.COMPLETE)},1E3)}function c(){d=a.loaderstatus.ERROR;k.sendEvent(l.ERROR)}var d=a.loaderstatus.NEW,
|
||||
g,q,m,n,k=new l.eventdispatcher;a.extend(this,k);this.load=function(){if(d==a.loaderstatus.NEW)if(0<e.lastIndexOf(".swf"))g=e,d=a.loaderstatus.COMPLETE,k.sendEvent(l.COMPLETE);else if(a.getPluginPathType(e)==a.pluginPathType.CDN)d=a.loaderstatus.COMPLETE,k.sendEvent(l.COMPLETE);else{d=a.loaderstatus.LOADING;var h=new a.scriptloader(j());h.addEventListener(l.COMPLETE,b);h.addEventListener(l.ERROR,c);h.load()}};this.registerPlugin=function(b,c,e,j){n&&(clearTimeout(n),n=void 0);m=c;e&&j?(g=j,q=e):"string"==
|
||||
typeof e?g=e:"function"==typeof e?q=e:!e&&!j&&(g=b);d=a.loaderstatus.COMPLETE;k.sendEvent(l.COMPLETE)};this.getStatus=function(){return d};this.getPluginName=function(){return a.getPluginName(e)};this.getFlashPath=function(){if(g)switch(a.getPluginPathType(g)){case a.pluginPathType.ABSOLUTE:return g;case a.pluginPathType.RELATIVE:return 0<e.lastIndexOf(".swf")?a.getAbsolutePath(g,window.location.href):a.getAbsolutePath(g,j())}return null};this.getJS=function(){return q};this.getTarget=function(){return m};
|
||||
this.getPluginmode=function(){if("undefined"!=typeof g&&"undefined"!=typeof q)return f.pluginmodes.HYBRID;if("undefined"!=typeof g)return f.pluginmodes.FLASH;if("undefined"!=typeof q)return f.pluginmodes.JAVASCRIPT};this.getNewInstance=function(a,b,d){return new q(a,b,d)};this.getURL=function(){return e}}}(jwplayer.plugins),function(f){var a=f.utils,l=f.events,e=a.foreach;f.plugins.pluginloader=function(j,b){function c(){m?h.sendEvent(l.ERROR,{message:n}):q||(q=!0,g=a.loaderstatus.COMPLETE,h.sendEvent(l.COMPLETE))}
|
||||
function d(){k||c();if(!q&&!m){var b=0,d=j.getPlugins();a.foreach(k,function(g){g=a.getPluginName(g);var e=d[g];g=e.getJS();var h=e.getTarget(),e=e.getStatus();if(e==a.loaderstatus.LOADING||e==a.loaderstatus.NEW)b++;else if(g&&(!h||parseFloat(h)>parseFloat(f.version)))m=!0,n="Incompatible player version",c()});0==b&&c()}}var g=a.loaderstatus.NEW,q=!1,m=!1,n,k=b,h=new l.eventdispatcher;a.extend(this,h);this.setupPlugins=function(b,d,c){var g={length:0,plugins:{}},h=0,f={},r=j.getPlugins();e(d.plugins,
|
||||
function(e,j){var k=a.getPluginName(e),l=r[k],m=l.getFlashPath(),n=l.getJS(),q=l.getURL();m&&(g.plugins[m]=a.extend({},j),g.plugins[m].pluginmode=l.getPluginmode(),g.length++);try{if(n&&d.plugins&&d.plugins[q]){var v=document.createElement("div");v.id=b.id+"_"+k;v.style.position="absolute";v.style.top=0;v.style.zIndex=h+10;f[k]=l.getNewInstance(b,a.extend({},d.plugins[q]),v);h++;b.onReady(c(f[k],v,!0));b.onResize(c(f[k],v))}}catch(B){a.log("ERROR: Failed to load "+k+".")}});b.plugins=f;return g};
|
||||
this.load=function(){if(!(a.exists(b)&&"object"!=a.typeOf(b))){g=a.loaderstatus.LOADING;e(b,function(b){a.exists(b)&&(b=j.addPlugin(b),b.addEventListener(l.COMPLETE,d),b.addEventListener(l.ERROR,r))});var c=j.getPlugins();e(c,function(a,b){b.load()})}d()};var r=this.pluginFailed=function(){m||(m=!0,n="File not found",c())};this.getStatus=function(){return g}}}(jwplayer),function(f){f.playlist=function(a){var l=[];if("array"==f.utils.typeOf(a))for(var e=0;e<a.length;e++)l.push(new f.playlist.item(a[e]));
|
||||
else l.push(new f.playlist.item(a));return l}}(jwplayer),function(f){var a=f.item=function(l){var e=jwplayer.utils,j=e.extend({},a.defaults,l);j.tracks=e.exists(l.tracks)?l.tracks:[];0==j.sources.length&&(j.sources=[new f.source(j)]);for(var b=0;b<j.sources.length;b++){var c=j.sources[b]["default"];j.sources[b]["default"]=c?"true"==c.toString():!1;j.sources[b]=new f.source(j.sources[b])}if(j.captions&&!e.exists(l.tracks)){for(l=0;l<j.captions.length;l++)j.tracks.push(j.captions[l]);delete j.captions}for(b=
|
||||
0;b<j.tracks.length;b++)j.tracks[b]=new f.track(j.tracks[b]);return j};a.defaults={description:"",image:"",mediaid:"",title:"",sources:[],tracks:[]}}(jwplayer.playlist),function(f){var a=jwplayer.utils,l={file:void 0,label:void 0,type:void 0,"default":void 0};f.source=function(e){var j=a.extend({},l);a.foreach(l,function(b){a.exists(e[b])&&(j[b]=e[b],delete e[b])});j.type&&0<j.type.indexOf("/")&&(j.type=a.extensionmap.mimeType(j.type));"m3u8"==j.type&&(j.type="hls");"smil"==j.type&&(j.type="rtmp");
|
||||
return j}}(jwplayer.playlist),function(f){var a=jwplayer.utils,l={file:void 0,label:void 0,kind:"captions","default":!1};f.track=function(e){var j=a.extend({},l);e||(e={});a.foreach(l,function(b){a.exists(e[b])&&(j[b]=e[b],delete e[b])});return j}}(jwplayer.playlist),function(f){var a=f.utils,l=f.events,e=document,j=f.embed=function(b){function c(b,d){a.foreach(d,function(a,d){"function"==typeof b[a]&&b[a].call(b,d)})}function d(a){q(n,t+a.message)}function g(){q(n,t+"No playable sources found")}
|
||||
function q(b,d){if(m.fallback){var c=b.style;c.backgroundColor="#000";c.color="#FFF";c.width=a.styleDimension(m.width);c.height=a.styleDimension(m.height);c.display="table";c.opacity=1;var c=document.createElement("p"),g=c.style;g.verticalAlign="middle";g.textAlign="center";g.display="table-cell";g.font="15px/20px Arial, Helvetica, sans-serif";c.innerHTML=d.replace(":",":\x3cbr\x3e");b.innerHTML="";b.appendChild(c)}}var m=new j.config(b.config),n,k,h,r=m.width,p=m.height,t="Error loading player: ",
|
||||
s=f.plugins.loadPlugins(b.id,m.plugins);m.fallbackDiv&&(h=m.fallbackDiv,delete m.fallbackDiv);m.id=b.id;k=e.getElementById(b.id);m.aspectratio?b.config.aspectratio=m.aspectratio:delete b.config.aspectratio;n=e.createElement("div");n.id=k.id;n.style.width=0<r.toString().indexOf("%")?r:r+"px";n.style.height=0<p.toString().indexOf("%")?p:p+"px";k.parentNode.replaceChild(n,k);f.embed.errorScreen=q;s.addEventListener(l.COMPLETE,function(){if("array"==a.typeOf(m.playlist)&&2>m.playlist.length&&(0==m.playlist.length||
|
||||
!m.playlist[0].sources||0==m.playlist[0].sources.length))g();else if(s.getStatus()==a.loaderstatus.COMPLETE){for(var e=0;e<m.modes.length;e++)if(m.modes[e].type&&j[m.modes[e].type]){var f=a.extend({},m),r=new j[m.modes[e].type](n,m.modes[e],f,s,b);if(r.supportsConfig())return r.addEventListener(l.ERROR,d),r.embed(),c(b,f.events),b}m.fallback?(a.log("No suitable players found and fallback enabled"),new j.download(n,m,g)):(a.log("No suitable players found and fallback disabled"),n.parentNode.replaceChild(h,
|
||||
n))}});s.addEventListener(l.ERROR,function(a){q(n,"Could not load plugins: "+a.message)});s.load();return b}}(jwplayer),function(f){function a(a){if(a.playlist)for(var c=0;c<a.playlist.length;c++)a.playlist[c]=new j(a.playlist[c]);else{var d={};e.foreach(j.defaults,function(c){l(a,d,c)});d.sources||(a.levels?(d.sources=a.levels,delete a.levels):(c={},l(a,c,"file"),l(a,c,"type"),d.sources=c.file?[c]:[]));a.playlist=[new j(d)]}}function l(a,c,d){e.exists(a[d])&&(c[d]=a[d],delete a[d])}var e=f.utils,
|
||||
j=f.playlist.item;(f.embed.config=function(b){var c={fallback:!0,height:270,primary:"html5",width:480,base:b.base?b.base:e.getScriptPath("jwplayer.js"),aspectratio:""};b=e.extend(c,f.defaults,b);var c={type:"html5",src:b.base+"jwplayer.html5.js"},d={type:"flash",src:b.base+"jwplayer.flash.swf"};b.modes="flash"==b.primary?[d,c]:[c,d];b.listbar&&(b.playlistsize=b.listbar.size,b.playlistposition=b.listbar.position);b.flashplayer&&(d.src=b.flashplayer);b.html5player&&(c.src=b.html5player);a(b);d=b.aspectratio;
|
||||
if("string"!=typeof d||!e.exists(d))c=0;else{var g=d.indexOf(":");-1==g?c=0:(c=parseFloat(d.substr(0,g)),d=parseFloat(d.substr(g+1)),c=0>=c||0>=d?0:100*(d/c)+"%")}-1==b.width.toString().indexOf("%")?delete b.aspectratio:c?b.aspectratio=c:delete b.aspectratio;return b}).addConfig=function(b,c){a(c);return e.extend(b,c)}}(jwplayer),function(f){var a=f.utils,l=document;f.embed.download=function(e,f,b){function c(b,d){for(var c=l.querySelectorAll(b),g=0;g<c.length;g++)a.foreach(d,function(a,b){c[g].style[a]=
|
||||
b})}function d(a,b,d){a=l.createElement(a);b&&(a.className="jwdownload"+b);d&&d.appendChild(a);return a}var g=a.extend({},f),q=g.width?g.width:480,m=g.height?g.height:320,n;f=f.logo?f.logo:{prefix:a.repo(),file:"logo.png",margin:10};var k,h,r,g=g.playlist,p,t=["mp4","aac","mp3"];if(g&&g.length){p=g[0];n=p.sources;for(g=0;g<n.length;g++){var s=n[g],u=s.type?s.type:a.extensionmap.extType(a.extension(s.file));s.file&&a.foreach(t,function(b){u==t[b]?(k=s.file,h=p.image):a.isYouTube(s.file)&&(r=s.file)})}k?
|
||||
(n=k,b=h,e&&(g=d("a","display",e),d("div","icon",g),d("div","logo",g),n&&g.setAttribute("href",a.getAbsolutePath(n))),g="#"+e.id+" .jwdownload",e.style.width="",e.style.height="",c(g+"display",{width:a.styleDimension(Math.max(320,q)),height:a.styleDimension(Math.max(180,m)),background:"black center no-repeat "+(b?"url("+b+")":""),backgroundSize:"contain",position:"relative",border:"none",display:"block"}),c(g+"display div",{position:"absolute",width:"100%",height:"100%"}),c(g+"logo",{top:f.margin+
|
||||
"px",right:f.margin+"px",background:"top right no-repeat url("+f.prefix+f.file+")"}),c(g+"icon",{background:"center no-repeat url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAYAAAA6/NlyAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAgNJREFUeNrs28lqwkAYB/CZqNVDDj2r6FN41QeIy8Fe+gj6BL275Q08u9FbT8ZdwVfotSBYEPUkxFOoks4EKiJdaDuTjMn3wWBO0V/+sySR8SNSqVRKIR8qaXHkzlqS9jCfzzWcTCYp9hF5o+59sVjsiRzcegSckFzcjT+ruN80TeSlAjCAAXzdJSGPFXRpAAMYwACGZQkSdhG4WCzehMNhqV6vG6vVSrirKVEw66YoSqDb7cqlUilE8JjHd/y1MQefVzqdDmiaJpfLZWHgXMHn8F6vJ1cqlVAkEsGuAn83J4gAd2RZymQygX6/L1erVQt+9ZPWb+CDwcCC2zXGJaewl/DhcHhK3DVj+KfKZrMWvFarcYNLomAv4aPRSFZVlTlcSPA5fDweW/BoNIqFnKV53JvncjkLns/n/cLdS+92O7RYLLgsKfv9/t8XlDn4eDyiw+HA9Jyz2eyt0+kY2+3WFC5hluej0Ha7zQQq9PPwdDq1Et1sNsx/nFBgCqWJ8oAK1aUptNVqcYWewE4nahfU0YQnk4ntUEfGMIU2m01HoLaCKbTRaDgKtaVLk9tBYaBcE/6Artdr4RZ5TB6/dC+9iIe/WgAMYADDpAUJAxjAAAYwgGFZgoS/AtNNTF7Z2bL0BYPBV3Jw5xFwwWcYxgtBP5OkE8i9G7aWGOOCruvauwADALMLMEbKf4SdAAAAAElFTkSuQmCC)"})):
|
||||
r?(f=r,e=d("embed","",e),e.src="http://www.youtube.com/v/"+/v[=\/](\w*)|\/(\w+)$|^(\w+)$/i.exec(f).slice(1).join(""),e.type="application/x-shockwave-flash",e.width=q,e.height=m):b()}}}(jwplayer),function(f){var a=f.utils,l=f.events,e={};(f.embed.flash=function(j,b,c,d,g){function q(a,b,d){var c=document.createElement("param");c.setAttribute("name",b);c.setAttribute("value",d);a.appendChild(c)}function m(a,b,d){return function(){try{d&&document.getElementById(g.id+"_wrapper").appendChild(b);var c=
|
||||
document.getElementById(g.id).getPluginConfig("display");"function"==typeof a.resize&&a.resize(c.width,c.height);b.style.left=c.x;b.style.top=c.h}catch(e){}}}function n(b){if(!b)return{};var d={},c=[];a.foreach(b,function(b,g){var e=a.getPluginName(b);c.push(b);a.foreach(g,function(a,b){d[e+"."+a]=b})});d.plugins=c.join(",");return d}var k=new f.events.eventdispatcher,h=a.flashVersion();a.extend(this,k);this.embed=function(){c.id=g.id;if(10>h)return k.sendEvent(l.ERROR,{message:"Flash version must be 10.0 or greater"}),
|
||||
!1;var f,p,t=g.config.listbar,s=a.extend({},c);if(j.id+"_wrapper"==j.parentNode.id)f=document.getElementById(j.id+"_wrapper");else{f=document.createElement("div");p=document.createElement("div");p.style.display="none";p.id=j.id+"_aspect";f.id=j.id+"_wrapper";f.style.position="relative";f.style.display="block";f.style.width=a.styleDimension(s.width);f.style.height=a.styleDimension(s.height);if(g.config.aspectratio){var u=parseFloat(g.config.aspectratio);p.style.display="block";p.style.marginTop=g.config.aspectratio;
|
||||
f.style.height="auto";f.style.display="inline-block";t&&("bottom"==t.position?p.style.paddingBottom=t.size+"px":"right"==t.position&&(p.style.marginBottom=-1*t.size*(u/100)+"px"))}j.parentNode.replaceChild(f,j);f.appendChild(j);f.appendChild(p)}f=d.setupPlugins(g,s,m);0<f.length?a.extend(s,n(f.plugins)):delete s.plugins;"undefined"!=typeof s["dock.position"]&&"false"==s["dock.position"].toString().toLowerCase()&&(s.dock=s["dock.position"],delete s["dock.position"]);f=s.wmode?s.wmode:s.height&&40>=
|
||||
s.height?"transparent":"opaque";p="height width modes events primary base fallback volume".split(" ");for(t=0;t<p.length;t++)delete s[p[t]];p=a.getCookies();a.foreach(p,function(a,b){"undefined"==typeof s[a]&&(s[a]=b)});p=window.location.href.split("/");p.splice(p.length-1,1);p=p.join("/");s.base=p+"/";e[j.id]=s;a.isIE()?(p='\x3cobject classid\x3d"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" " width\x3d"100%" height\x3d"100%"id\x3d"'+j.id+'" name\x3d"'+j.id+'" tabindex\x3d0""\x3e',p+='\x3cparam name\x3d"movie" value\x3d"'+
|
||||
b.src+'"\x3e',p+='\x3cparam name\x3d"allowfullscreen" value\x3d"true"\x3e\x3cparam name\x3d"allowscriptaccess" value\x3d"always"\x3e',p+='\x3cparam name\x3d"seamlesstabbing" value\x3d"true"\x3e',p+='\x3cparam name\x3d"wmode" value\x3d"'+f+'"\x3e',p+='\x3cparam name\x3d"bgcolor" value\x3d"#000000"\x3e',p+="\x3c/object\x3e",j.outerHTML=p,f=document.getElementById(j.id)):(p=document.createElement("object"),p.setAttribute("type","application/x-shockwave-flash"),p.setAttribute("data",b.src),p.setAttribute("width",
|
||||
"100%"),p.setAttribute("height","100%"),p.setAttribute("bgcolor","#000000"),p.setAttribute("id",j.id),p.setAttribute("name",j.id),p.setAttribute("tabindex",0),q(p,"allowfullscreen","true"),q(p,"allowscriptaccess","always"),q(p,"seamlesstabbing","true"),q(p,"wmode",f),j.parentNode.replaceChild(p,j),f=p);g.config.aspectratio&&(f.style.position="absolute");g.container=f;g.setPlayer(f,"flash")};this.supportsConfig=function(){if(h)if(c){if("string"==a.typeOf(c.playlist))return!0;try{var b=c.playlist[0].sources;
|
||||
if("undefined"==typeof b)return!0;for(var d=0;d<b.length;d++){var g;if(g=b[d].file){var e=b[d].file,f=b[d].type;if(a.isYouTube(e)||a.isRtmp(e,f)||"hls"==f)g=!0;else{var j=a.extensionmap[f?f:a.extension(e)];g=!j?!1:!!j.flash}}if(g)return!0}}catch(k){}}else return!0;return!1}}).getVars=function(a){return e[a]}}(jwplayer),function(f){var a=f.utils,l=a.extensionmap,e=f.events;f.embed.html5=function(j,b,c,d,g){function q(a,b,d){return function(){try{var c=document.querySelector("#"+j.id+" .jwmain");d&&
|
||||
c.appendChild(b);"function"==typeof a.resize&&(a.resize(c.clientWidth,c.clientHeight),setTimeout(function(){a.resize(c.clientWidth,c.clientHeight)},400));b.left=c.style.left;b.top=c.style.top}catch(g){}}}function m(a){n.sendEvent(a.type,{message:"HTML5 player not found"})}var n=this,k=new e.eventdispatcher;a.extend(n,k);n.embed=function(){if(f.html5){d.setupPlugins(g,c,q);j.innerHTML="";var h=f.utils.extend({},c);delete h.volume;h=new f.html5.player(h);g.container=document.getElementById(g.id);g.setPlayer(h,
|
||||
"html5")}else h=new a.scriptloader(b.src),h.addEventListener(e.ERROR,m),h.addEventListener(e.COMPLETE,n.embed),h.load()};n.supportsConfig=function(){if(f.vid.canPlayType)try{if("string"==a.typeOf(c.playlist))return!0;for(var b=c.playlist[0].sources,d=0;d<b.length;d++){var g;var e=b[d].file,j=b[d].type;if(null!==navigator.userAgent.match(/BlackBerry/i)||a.isAndroid()&&("m3u"==a.extension(e)||"m3u8"==a.extension(e))||a.isRtmp(e,j))g=!1;else{var k=l[j?j:a.extension(e)],m;if(!k||k.flash&&!k.html5)m=!1;
|
||||
else{var n=k.html5,q=f.vid;if(n)try{m=q.canPlayType(n)?!0:!1}catch(z){m=!1}else m=!0}g=m}if(g)return!0}}catch(A){}return!1}}}(jwplayer),function(f){var a=f.embed,l=f.utils,e=l.extend(function(e){var b=l.repo(),c=l.extend({},f.defaults),d=l.extend({},c,e.config),g=e.config,q=d.plugins,m=d.analytics,n=b+"jwpsrv.js",k=b+"sharing.js",h=b+"related.js",r=b+"gapro.js",c=f.key?f.key:c.key,p="premium"/*(new f.utils.key(c)).edition()*/,q=q?q:{}; /*alert(c);*/ "ads"==p&&d.advertising&&(d.advertising.client.match(".js$|.swf$")?q[d.advertising.client]=
|
||||
d.advertising:q[b+d.advertising.client+".js"]=d.advertising);delete g.advertising;g.key=c;d.analytics&&(d.analytics.client&&d.analytics.client.match(".js$|.swf$"))&&(n=d.analytics.client);delete g.analytics;if("free"==p||!m||!1!==m.enabled)q[n]=m?m:{};delete q.sharing;delete q.related;if("premium"==p||"ads"==p)d.sharing&&(d.sharing.client&&d.sharing.client.match(".js$|.swf$")&&(k=d.sharing.client),q[k]=d.sharing),d.related&&(d.related.client&&d.related.client.match(".js$|.swf$")&&(h=d.related.client),
|
||||
q[h]=d.related),d.ga&&(d.ga.client&&d.ga.client.match(".js$|.swf$")&&(r=d.ga.client),q[r]=d.ga),d.skin&&(g.skin=d.skin.replace(/^(beelden|bekle|five|glow|modieus|roundster|stormtrooper|vapor)$/i,l.repo()+"skins/$1.xml"));g.plugins=q;return new a(e)},a);f.embed=e}(jwplayer),function(f){var a=[],l=f.utils,e=f.events,j=e.state,b=document,c=f.api=function(a){function g(a,b){return function(d){return b(a,d)}}function q(a,b){p[a]||(p[a]=[],n(e.JWPLAYER_PLAYER_STATE,function(b){var d=b.newstate;b=b.oldstate;
|
||||
if(d==a){var c=p[d];if(c)for(var g=0;g<c.length;g++)"function"==typeof c[g]&&c[g].call(this,{oldstate:b,newstate:d})}}));p[a].push(b);return h}function m(a,b){try{a.jwAddEventListener(b,'function(dat) { jwplayer("'+h.id+'").dispatchEvent("'+b+'", dat); }')}catch(d){l.log("Could not add internal listener")}}function n(a,b){r[a]||(r[a]=[],t&&s&&m(t,a));r[a].push(b);return h}function k(){if(s){for(var a=arguments[0],b=[],d=1;d<arguments.length;d++)b.push(arguments[d]);if("undefined"!=typeof t&&"function"==
|
||||
typeof t[a])switch(b.length){case 4:return t[a](b[0],b[1],b[2],b[3]);case 3:return t[a](b[0],b[1],b[2]);case 2:return t[a](b[0],b[1]);case 1:return t[a](b[0]);default:return t[a]()}return null}u.push(arguments)}var h=this,r={},p={},t=void 0,s=!1,u=[],w=void 0,x={},y={};h.container=a;h.id=a.id;h.getBuffer=function(){return k("jwGetBuffer")};h.getContainer=function(){return h.container};h.addButton=function(a,b,d,c){try{y[c]=d,k("jwDockAddButton",a,b,"jwplayer('"+h.id+"').callback('"+c+"')",c)}catch(g){l.log("Could not add dock button"+
|
||||
g.message)}};h.removeButton=function(a){k("jwDockRemoveButton",a)};h.callback=function(a){if(y[a])y[a]()};h.forceState=function(a){k("jwForceState",a);return h};h.releaseState=function(){return k("jwReleaseState")};h.getDuration=function(){return k("jwGetDuration")};h.getFullscreen=function(){return k("jwGetFullscreen")};h.getStretching=function(){return k("jwGetStretching")};h.getHeight=function(){return k("jwGetHeight")};h.getLockState=function(){return k("jwGetLockState")};h.getMeta=function(){return h.getItemMeta()};
|
||||
h.getMute=function(){return k("jwGetMute")};h.getPlaylist=function(){var a=k("jwGetPlaylist");"flash"==h.renderingMode&&l.deepReplaceKeyName(a,["__dot__","__spc__","__dsh__","__default__"],["."," ","-","default"]);return a};h.getPlaylistItem=function(a){l.exists(a)||(a=h.getCurrentItem());return h.getPlaylist()[a]};h.getPosition=function(){return k("jwGetPosition")};h.getRenderingMode=function(){return h.renderingMode};h.getState=function(){return k("jwGetState")};h.getVolume=function(){return k("jwGetVolume")};
|
||||
h.getWidth=function(){return k("jwGetWidth")};h.setFullscreen=function(a){l.exists(a)?k("jwSetFullscreen",a):k("jwSetFullscreen",!k("jwGetFullscreen"));return h};h.setStretching=function(a){k("jwSetStretching",a);return h};h.setMute=function(a){l.exists(a)?k("jwSetMute",a):k("jwSetMute",!k("jwGetMute"));return h};h.lock=function(){return h};h.unlock=function(){return h};h.load=function(a){k("jwLoad",a);return h};h.playlistItem=function(a){k("jwPlaylistItem",parseInt(a));return h};h.playlistPrev=function(){k("jwPlaylistPrev");
|
||||
return h};h.playlistNext=function(){k("jwPlaylistNext");return h};h.resize=function(a,d){if("flash"!=h.renderingMode){var c=document.getElementById(h.id);c.className=c.className.replace(/\s+aspectMode/,"");c.style.display="block";k("jwResize",a,d)}else{var c=b.getElementById(h.id+"_wrapper"),g=b.getElementById(h.id+"_aspect");g&&(g.style.display="none");c&&(c.style.display="block",c.style.width=l.styleDimension(a),c.style.height=l.styleDimension(d))}return h};h.play=function(a){"undefined"==typeof a?
|
||||
(a=h.getState(),a==j.PLAYING||a==j.BUFFERING?k("jwPause"):k("jwPlay")):k("jwPlay",a);return h};h.pause=function(a){"undefined"==typeof a?(a=h.getState(),a==j.PLAYING||a==j.BUFFERING?k("jwPause"):k("jwPlay")):k("jwPause",a);return h};h.stop=function(){k("jwStop");return h};h.seek=function(a){k("jwSeek",a);return h};h.setVolume=function(a){k("jwSetVolume",a);return h};h.loadInstream=function(a,b){return w=new c.instream(this,t,a,b)};h.getQualityLevels=function(){return k("jwGetQualityLevels")};h.getCurrentQuality=
|
||||
function(){return k("jwGetCurrentQuality")};h.setCurrentQuality=function(a){k("jwSetCurrentQuality",a)};h.getCaptionsList=function(){return k("jwGetCaptionsList")};h.getCurrentCaptions=function(){return k("jwGetCurrentCaptions")};h.setCurrentCaptions=function(a){k("jwSetCurrentCaptions",a)};h.getControls=function(){return k("jwGetControls")};h.getSafeRegion=function(){return k("jwGetSafeRegion")};h.setControls=function(a){k("jwSetControls",a)};h.destroyPlayer=function(){k("jwPlayerDestroy")};var z=
|
||||
{onBufferChange:e.JWPLAYER_MEDIA_BUFFER,onBufferFull:e.JWPLAYER_MEDIA_BUFFER_FULL,onError:e.JWPLAYER_ERROR,onFullscreen:e.JWPLAYER_FULLSCREEN,onMeta:e.JWPLAYER_MEDIA_META,onMute:e.JWPLAYER_MEDIA_MUTE,onPlaylist:e.JWPLAYER_PLAYLIST_LOADED,onPlaylistItem:e.JWPLAYER_PLAYLIST_ITEM,onPlaylistComplete:e.JWPLAYER_PLAYLIST_COMPLETE,onReady:e.API_READY,onResize:e.JWPLAYER_RESIZE,onComplete:e.JWPLAYER_MEDIA_COMPLETE,onSeek:e.JWPLAYER_MEDIA_SEEK,onTime:e.JWPLAYER_MEDIA_TIME,onVolume:e.JWPLAYER_MEDIA_VOLUME,
|
||||
onBeforePlay:e.JWPLAYER_MEDIA_BEFOREPLAY,onBeforeComplete:e.JWPLAYER_MEDIA_BEFORECOMPLETE,onDisplayClick:e.JWPLAYER_DISPLAY_CLICK,onControls:e.JWPLAYER_CONTROLS,onQualityLevels:e.JWPLAYER_MEDIA_LEVELS,onQualityChange:e.JWPLAYER_MEDIA_LEVEL_CHANGED,onCaptionsList:e.JWPLAYER_CAPTIONS_LIST,onCaptionsChange:e.JWPLAYER_CAPTIONS_CHANGED};l.foreach(z,function(a){h[a]=g(z[a],n)});var A={onBuffer:j.BUFFERING,onPause:j.PAUSED,onPlay:j.PLAYING,onIdle:j.IDLE};l.foreach(A,function(a){h[a]=g(A[a],q)});h.remove=
|
||||
function(){if(!s)throw"Cannot call remove() before player is ready";u=[];c.destroyPlayer(this.id)};h.setup=function(a){if(f.embed){var d=b.getElementById(h.id);d&&(a.fallbackDiv=d);d=h;u=[];c.destroyPlayer(d.id);d=f(h.id);d.config=a;return new f.embed(d)}return h};h.registerPlugin=function(a,b,d,c){f.plugins.registerPlugin(a,b,d,c)};h.setPlayer=function(a,b){t=a;h.renderingMode=b};h.detachMedia=function(){if("html5"==h.renderingMode)return k("jwDetachMedia")};h.attachMedia=function(a){if("html5"==
|
||||
h.renderingMode)return k("jwAttachMedia",a)};h.dispatchEvent=function(a,b){if(r[a])for(var d=l.translateEventResponse(a,b),c=0;c<r[a].length;c++)if("function"==typeof r[a][c])try{a==e.JWPLAYER_PLAYLIST_LOADED&&l.deepReplaceKeyName(d.playlist,["__dot__","__spc__","__dsh__","__default__"],["."," ","-","default"]),r[a][c].call(this,d)}catch(g){l.log("There was an error calling back an event handler")}};h.dispatchInstreamEvent=function(a){w&&w.dispatchEvent(a,arguments)};h.callInternal=k;h.playerReady=
|
||||
function(a){s=!0;t||h.setPlayer(b.getElementById(a.id));h.container=b.getElementById(h.id);l.foreach(r,function(a){m(t,a)});n(e.JWPLAYER_PLAYLIST_ITEM,function(){x={}});n(e.JWPLAYER_MEDIA_META,function(a){l.extend(x,a.metadata)});for(h.dispatchEvent(e.API_READY);0<u.length;)k.apply(this,u.shift())};h.getItemMeta=function(){return x};h.getCurrentItem=function(){return k("jwGetPlaylistIndex")};return h};c.selectPlayer=function(d){var g;l.exists(d)||(d=0);d.nodeType?g=d:"string"==typeof d&&(g=b.getElementById(d));
|
||||
return g?(d=c.playerById(g.id))?d:c.addPlayer(new c(g)):"number"==typeof d?a[d]:null};c.playerById=function(b){for(var c=0;c<a.length;c++)if(a[c].id==b)return a[c];return null};c.addPlayer=function(b){for(var c=0;c<a.length;c++)if(a[c]==b)return b;a.push(b);return b};c.destroyPlayer=function(d){for(var c=-1,e,f=0;f<a.length;f++)a[f].id==d&&(c=f,e=a[f]);0<=c&&(d=e.id,f=b.getElementById(d+("flash"==e.renderingMode?"_wrapper":"")),l.clearCss&&l.clearCss("#"+d),f&&("html5"==e.renderingMode&&e.destroyPlayer(),
|
||||
e=b.createElement("div"),e.id=d,f.parentNode.replaceChild(e,f)),a.splice(c,1));return null};f.playerReady=function(a){var b=f.api.playerById(a.id);b?b.playerReady(a):f.api.selectPlayer(a.id).playerReady(a)}}(jwplayer),function(f){var a=f.events,l=f.utils,e=a.state;f.api.instream=function(f,b,c,d){function g(a,b){k[a]||(k[a]=[],n.jwInstreamAddEventListener(a,'function(dat) { jwplayer("'+m.id+'").dispatchInstreamEvent("'+a+'", dat); }'));k[a].push(b);return this}function q(b,c){h[b]||(h[b]=[],g(a.JWPLAYER_PLAYER_STATE,
|
||||
function(a){var c=a.newstate,d=a.oldstate;if(c==b){var e=h[c];if(e)for(var f=0;f<e.length;f++)"function"==typeof e[f]&&e[f].call(this,{oldstate:d,newstate:c,type:a.type})}}));h[b].push(c);return this}var m=f,n=b,k={},h={};this.dispatchEvent=function(a,b){if(k[a])for(var c=l.translateEventResponse(a,b[1]),d=0;d<k[a].length;d++)"function"==typeof k[a][d]&&k[a][d].call(this,c)};this.onError=function(b){return g(a.JWPLAYER_ERROR,b)};this.onFullscreen=function(b){return g(a.JWPLAYER_FULLSCREEN,b)};this.onMeta=
|
||||
function(b){return g(a.JWPLAYER_MEDIA_META,b)};this.onMute=function(b){return g(a.JWPLAYER_MEDIA_MUTE,b)};this.onComplete=function(b){return g(a.JWPLAYER_MEDIA_COMPLETE,b)};this.onTime=function(b){return g(a.JWPLAYER_MEDIA_TIME,b)};this.onBuffer=function(a){return q(e.BUFFERING,a)};this.onPause=function(a){return q(e.PAUSED,a)};this.onPlay=function(a){return q(e.PLAYING,a)};this.onIdle=function(a){return q(e.IDLE,a)};this.onClick=function(b){return g(a.JWPLAYER_INSTREAM_CLICK,b)};this.onInstreamDestroyed=
|
||||
function(b){return g(a.JWPLAYER_INSTREAM_DESTROYED,b)};this.play=function(a){n.jwInstreamPlay(a)};this.pause=function(a){n.jwInstreamPause(a)};this.destroy=function(){n.jwInstreamDestroy()};m.callInternal("jwLoadInstream",c,d?d:{})}}(jwplayer),function(f){var a=f.api,l=a.selectPlayer;a.selectPlayer=function(a){return(a=l(a))?a:{registerPlugin:function(a,b,c){f.plugins.registerPlugin(a,b,c)}}}}(jwplayer));
|
516
trunk/research/players/js/srs.js
Executable file
516
trunk/research/players/js/srs.js
Executable file
|
@ -0,0 +1,516 @@
|
|||
//////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
* padding the output.
|
||||
* padding(3, 5, '0') is 00003
|
||||
* padding(3, 5, 'x') is xxxx3
|
||||
* @see http://blog.csdn.net/win_lin/article/details/12065413
|
||||
*/
|
||||
function padding(number, length, prefix) {
|
||||
if(String(number).length >= length){
|
||||
return String(number);
|
||||
}
|
||||
return padding(prefix+number, length, prefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* update the navigator, add same query string.
|
||||
*/
|
||||
function update_nav() {
|
||||
$("#nav_srs_player").attr("href", "srs_player.html" + window.location.search);
|
||||
$("#nav_srs_publisher").attr("href", "srs_publisher.html" + window.location.search);
|
||||
$("#nav_srs_bwt").attr("href", "srs_bwt.html" + window.location.search);
|
||||
$("#nav_jwplayer6").attr("href", "jwplayer6.html" + window.location.search);
|
||||
$("#nav_osmf").attr("href", "osmf.html" + window.location.search);
|
||||
$("#nav_vlc").attr("href", "vlc.html" + window.location.search);
|
||||
}
|
||||
|
||||
/**
|
||||
* parse the query string to object.
|
||||
*/
|
||||
function parse_query_string(){
|
||||
var obj = {};
|
||||
|
||||
// parse the host(hostname:http_port), pathname(dir/filename)
|
||||
obj.host = window.location.host;
|
||||
obj.hostname = window.location.hostname;
|
||||
obj.http_port = (window.location.port == "")? 80:window.location.port;
|
||||
obj.pathname = window.location.pathname;
|
||||
if (obj.pathname.lastIndexOf("/") <= 0) {
|
||||
obj.dir = "/";
|
||||
obj.filename = "";
|
||||
} else {
|
||||
obj.dir = obj.pathname.substr(0, obj.pathname.lastIndexOf("/"));
|
||||
obj.filename = obj.pathname.substr(obj.pathname.lastIndexOf("/"));
|
||||
}
|
||||
|
||||
// parse the query string.
|
||||
var query_string = String(window.location.search).replace(" ", "").split("?")[1];
|
||||
if(query_string == undefined){
|
||||
return obj;
|
||||
}
|
||||
|
||||
var queries = query_string.split("&");
|
||||
$(queries).each(function(){
|
||||
var query = this.split("=");
|
||||
obj[query[0]] = query[1];
|
||||
});
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
@param server the ip of server. default to window.location.hostname
|
||||
@param vhost the vhost of rtmp. default to window.location.hostname
|
||||
@param port the port of rtmp. default to 1935
|
||||
@param app the app of rtmp. default to live.
|
||||
@param stream the stream of rtmp. default to livestream.
|
||||
*/
|
||||
function build_default_rtmp_url() {
|
||||
var query = parse_query_string();
|
||||
|
||||
var server = (query.server == undefined)? window.location.hostname:query.server;
|
||||
var port = (query.port == undefined)? 1935:query.port;
|
||||
var vhost = (query.vhost == undefined)? window.location.hostname:query.vhost;
|
||||
var app = (query.app == undefined)? "live":query.app;
|
||||
var stream = (query.stream == undefined)? "livestream":query.stream;
|
||||
|
||||
if (server == vhost || vhost == "") {
|
||||
return "rtmp://" + server + ":" + port + "/" + app + "/" + stream;
|
||||
} else {
|
||||
return "rtmp://" + server + ":" + port + "/" + app + "...vhost..." + vhost + "/" + stream;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@param server the ip of server. default to window.location.hostname
|
||||
@param vhost the vhost of hls. default to window.location.hostname
|
||||
@param hls_vhost the vhost of hls. override the server if specified.
|
||||
@param hls_port the port of hls. default to window.location.port
|
||||
@param app the app of hls. default to live.
|
||||
@param stream the stream of hls. default to livestream.
|
||||
*/
|
||||
function build_default_hls_url() {
|
||||
var query = parse_query_string();
|
||||
|
||||
// for http, use hls_vhost to override server if specified.
|
||||
var server = window.location.hostname;
|
||||
if (query.server != undefined) {
|
||||
server = query.server;
|
||||
} else if (query.hls_vhost != undefined) {
|
||||
server = query.hls_vhost;
|
||||
}
|
||||
|
||||
var port = (query.hls_port == undefined)? window.location.port:query.hls_port;
|
||||
var app = (query.app == undefined)? "live":query.app;
|
||||
var stream = (query.stream == undefined)? "livestream":query.stream;
|
||||
|
||||
if (port == "" || port == null || port == undefined) {
|
||||
port = 80;
|
||||
}
|
||||
|
||||
return "http://" + server + ":" + port + "/" + app + "/" + stream + ".m3u8";
|
||||
}
|
||||
|
||||
/**
|
||||
* parse the rtmp url,
|
||||
* for example: rtmp://demo.srs.com:1935/live...vhost...players/livestream
|
||||
* @return object {server, port, vhost, app, stream}
|
||||
*/
|
||||
function srs_parse_rtmp_url(rtmp_url) {
|
||||
// @see: http://stackoverflow.com/questions/10469575/how-to-use-location-object-to-parse-url-without-redirecting-the-page-in-javascri
|
||||
var a = document.createElement("a");
|
||||
a.href = rtmp_url.replace("rtmp://", "http://");
|
||||
|
||||
var vhost = a.hostname;
|
||||
var port = (a.port == "")? "1935":a.port;
|
||||
var app = a.pathname.substr(1, a.pathname.lastIndexOf("/") - 1);
|
||||
var stream = a.pathname.substr(a.pathname.lastIndexOf("/") + 1);
|
||||
|
||||
// parse the vhost in the params of app, that srs supports.
|
||||
app = app.replace("...vhost...", "?vhost=");
|
||||
if (app.indexOf("?") >= 0) {
|
||||
var params = app.substr(app.indexOf("?"));
|
||||
app = app.substr(0, app.indexOf("?"));
|
||||
|
||||
if (params.indexOf("vhost=") > 0) {
|
||||
vhost = params.substr(params.indexOf("vhost=") + "vhost=".length);
|
||||
if (vhost.indexOf("&") > 0) {
|
||||
vhost = vhost.substr(0, vhost.indexOf("&"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var ret = {
|
||||
server: a.hostname, port: port,
|
||||
vhost: vhost, app: app, stream: stream
|
||||
};
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* player specified size.
|
||||
*/
|
||||
function srs_get_player_modal() { return 740; }
|
||||
function srs_get_player_width() { return srs_get_player_modal() - 30; }
|
||||
function srs_get_player_height() { return srs_get_player_width() * 9 / 19; }
|
||||
|
||||
// to query the swf anti cache.
|
||||
function srs_get_version_code() { return "1.5"; }
|
||||
// get the default vhost for players.
|
||||
function srs_get_player_vhost() { return "players"; }
|
||||
// get the stream published to vhost,
|
||||
// generally we need to transcode the stream to support HLS and filters.
|
||||
// for example, src_vhost is "players", we transcode stream to vhost "players_pub".
|
||||
// if not equals to the player vhost, return the orignal vhost.
|
||||
function srs_get_player_publish_vhost(src_vhost) { return (src_vhost != srs_get_player_vhost())? src_vhost:(src_vhost + "_pub"); }
|
||||
|
||||
/**
|
||||
* initialize the page.
|
||||
* @param rtmp_url the div id contains the rtmp stream url to play
|
||||
* @param hls_url the div id contains the hls stream url to play
|
||||
* @param modal_player the div id contains the modal player
|
||||
*/
|
||||
function srs_init(rtmp_url, hls_url, modal_player) {
|
||||
update_nav();
|
||||
|
||||
if (rtmp_url) {
|
||||
$(rtmp_url).val(build_default_rtmp_url());
|
||||
}
|
||||
if (hls_url) {
|
||||
$(hls_url).val(build_default_hls_url());
|
||||
}
|
||||
if (modal_player) {
|
||||
$(modal_player).width(srs_get_player_modal() + "px");
|
||||
$(modal_player).css("margin-left", "-" + srs_get_player_modal() / 2 +"px");
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
* the SrsPlayer object.
|
||||
* @param container the html container id.
|
||||
* @param width a float value specifies the width of player.
|
||||
* @param height a float value specifies the height of player.
|
||||
*/
|
||||
function SrsPlayer(container, width, height) {
|
||||
if (!SrsPlayer.__id) {
|
||||
SrsPlayer.__id = 100;
|
||||
}
|
||||
if (!SrsPlayer.__players) {
|
||||
SrsPlayer.__players = [];
|
||||
}
|
||||
|
||||
SrsPlayer.__players.push(this);
|
||||
|
||||
this.container = container;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.id = SrsPlayer.__id++;
|
||||
this.stream_url = null;
|
||||
this.buffer_time = 0.8; // default to 0.8
|
||||
this.callbackObj = null;
|
||||
|
||||
// callback set the following values.
|
||||
this.meatadata = {}; // for on_player_metadata
|
||||
this.time = 0; // current stream time.
|
||||
this.buffer_length = 0; // current stream buffer length.
|
||||
}
|
||||
/**
|
||||
* user can set some callback, then start the player.
|
||||
* callbacks:
|
||||
* on_player_ready():int, when srs player ready, user can play.
|
||||
* on_player_metadata(metadata:Object):int, when srs player get metadata.
|
||||
*/
|
||||
SrsPlayer.prototype.start = function() {
|
||||
// embed the flash.
|
||||
var flashvars = {};
|
||||
flashvars.id = this.id;
|
||||
flashvars.on_player_ready = "__srs_on_player_ready";
|
||||
flashvars.on_player_metadata = "__srs_on_player_metadata";
|
||||
flashvars.on_player_timer = "__srs_on_player_timer";
|
||||
|
||||
var params = {};
|
||||
params.wmode = "opaque";
|
||||
params.allowFullScreen = "true";
|
||||
params.allowScriptAccess = "always";
|
||||
|
||||
var attributes = {};
|
||||
|
||||
var self = this;
|
||||
|
||||
swfobject.embedSWF(
|
||||
"srs_player/release/srs_player.swf?_version="+srs_get_version_code(),
|
||||
this.container,
|
||||
this.width, this.height,
|
||||
"11.1", "js/AdobeFlashPlayerInstall.swf",
|
||||
flashvars, params, attributes,
|
||||
function(callbackObj){
|
||||
self.callbackObj = callbackObj;
|
||||
}
|
||||
);
|
||||
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* play the stream.
|
||||
* @param stream_url the url of stream, rtmp or http.
|
||||
*/
|
||||
SrsPlayer.prototype.play = function(url) {
|
||||
this.stream_url = url;
|
||||
this.callbackObj.ref.__play(this.stream_url, this.width, this.height, this.buffer_time);
|
||||
}
|
||||
SrsPlayer.prototype.stop = function() {
|
||||
for (var i = 0; i < SrsPlayer.__players.length; i++) {
|
||||
var player = SrsPlayer.__players[i];
|
||||
|
||||
if (player.id != this.id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SrsPlayer.__players.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
this.callbackObj.ref.__stop();
|
||||
}
|
||||
SrsPlayer.prototype.pause = function() {
|
||||
this.callbackObj.ref.__pause();
|
||||
}
|
||||
SrsPlayer.prototype.resume = function() {
|
||||
this.callbackObj.ref.__resume();
|
||||
}
|
||||
/**
|
||||
* to set the DAR, for example, DAR=16:9
|
||||
* @param num, for example, 9.
|
||||
* use metadata height if 0.
|
||||
* use user specified height if -1.
|
||||
* @param den, for example, 16.
|
||||
* use metadata width if 0.
|
||||
* use user specified width if -1.
|
||||
*/
|
||||
SrsPlayer.prototype.dar = function(num, den) {
|
||||
this.callbackObj.ref.__dar(num, den);
|
||||
}
|
||||
/**
|
||||
* set the fullscreen size data.
|
||||
* @refer the refer fullscreen mode. it can be:
|
||||
* video: use video orignal size.
|
||||
* screen: use screen size to rescale video.
|
||||
* @param percent, the rescale percent, where
|
||||
* 100 means 100%.
|
||||
*/
|
||||
SrsPlayer.prototype.set_fs = function(refer, percent) {
|
||||
this.callbackObj.ref.__set_fs(refer, percent);
|
||||
}
|
||||
/**
|
||||
* set the stream buffer time in seconds.
|
||||
* @buffer_time the buffer time in seconds.
|
||||
*/
|
||||
SrsPlayer.prototype.set_bt = function(buffer_time) {
|
||||
this.buffer_time = buffer_time;
|
||||
this.callbackObj.ref.__set_bt(buffer_time);
|
||||
}
|
||||
SrsPlayer.prototype.on_player_ready = function() {
|
||||
}
|
||||
SrsPlayer.prototype.on_player_metadata = function(metadata) {
|
||||
// ignore.
|
||||
}
|
||||
SrsPlayer.prototype.on_player_timer = function(time, buffer_length) {
|
||||
// ignore.
|
||||
}
|
||||
function __srs_find_player(id) {
|
||||
for (var i = 0; i < SrsPlayer.__players.length; i++) {
|
||||
var player = SrsPlayer.__players[i];
|
||||
|
||||
if (player.id != id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return player;
|
||||
}
|
||||
|
||||
throw new Error("player not found. id=" + id);
|
||||
}
|
||||
function __srs_on_player_ready(id) {
|
||||
var player = __srs_find_player(id);
|
||||
player.on_player_ready();
|
||||
}
|
||||
function __srs_on_player_metadata(id, metadata) {
|
||||
var player = __srs_find_player(id);
|
||||
|
||||
// user may override the on_player_metadata,
|
||||
// so set the data before invoke it.
|
||||
player.metadata = metadata;
|
||||
|
||||
player.on_player_metadata(metadata);
|
||||
}
|
||||
function __srs_on_player_timer(id, time, buffer_length) {
|
||||
var player = __srs_find_player(id);
|
||||
|
||||
buffer_length = Math.max(0, buffer_length);
|
||||
buffer_length = Math.min(player.buffer_time, buffer_length);
|
||||
|
||||
time = Math.max(0, time);
|
||||
|
||||
// user may override the on_player_timer,
|
||||
// so set the data before invoke it.
|
||||
player.time = time;
|
||||
player.buffer_length = buffer_length;
|
||||
|
||||
player.on_player_timer(time, buffer_length);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
* the SrsPublisher object.
|
||||
* @param container the html container id.
|
||||
* @param width a float value specifies the width of publisher.
|
||||
* @param height a float value specifies the height of publisher.
|
||||
*/
|
||||
function SrsPublisher(container, width, height) {
|
||||
if (!SrsPublisher.__id) {
|
||||
SrsPublisher.__id = 100;
|
||||
}
|
||||
if (!SrsPublisher.__publishers) {
|
||||
SrsPublisher.__publishers = [];
|
||||
}
|
||||
|
||||
SrsPublisher.__publishers.push(this);
|
||||
|
||||
this.container = container;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.id = SrsPublisher.__id++;
|
||||
this.callbackObj = null;
|
||||
|
||||
// set the values when publish.
|
||||
this.url = null;
|
||||
this.vcodec = {};
|
||||
this.acodec = {};
|
||||
|
||||
// callback set the following values.
|
||||
this.cameras = [];
|
||||
this.microphones = [];
|
||||
this.code = 0;
|
||||
|
||||
// error code defines.
|
||||
this.errors = {
|
||||
"100": "无法获取指定的摄像头", //error_camera_get
|
||||
"101": "无法获取指定的麦克风", //error_microphone_get
|
||||
"102": "摄像头为禁用状态,推流时请允许flash访问摄像头", //error_camera_muted
|
||||
};
|
||||
}
|
||||
/**
|
||||
* user can set some callback, then start the publisher.
|
||||
* callbacks:
|
||||
* on_publisher_ready(cameras, microphones):int, when srs publisher ready, user can publish.
|
||||
* on_publisher_error(code, desc):int, when srs publisher error, callback this method.
|
||||
* on_publisher_warn(code, desc):int, when srs publisher warn, callback this method.
|
||||
*/
|
||||
SrsPublisher.prototype.start = function() {
|
||||
// embed the flash.
|
||||
var flashvars = {};
|
||||
flashvars.id = this.id;
|
||||
flashvars.on_publisher_ready = "__srs_on_publisher_ready";
|
||||
flashvars.on_publisher_error = "__srs_on_publisher_error";
|
||||
flashvars.on_publisher_warn = "__srs_on_publisher_warn";
|
||||
|
||||
var params = {};
|
||||
params.wmode = "opaque";
|
||||
params.allowFullScreen = "true";
|
||||
params.allowScriptAccess = "always";
|
||||
|
||||
var attributes = {};
|
||||
|
||||
var self = this;
|
||||
|
||||
swfobject.embedSWF(
|
||||
"srs_publisher/release/srs_publisher.swf?_version="+srs_get_version_code(),
|
||||
this.container,
|
||||
this.width, this.height,
|
||||
"11.1", "js/AdobeFlashPlayerInstall.swf",
|
||||
flashvars, params, attributes,
|
||||
function(callbackObj){
|
||||
self.callbackObj = callbackObj;
|
||||
}
|
||||
);
|
||||
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* publish stream to server.
|
||||
* @param url a string indicates the rtmp url to publish.
|
||||
* @param vcodec an object contains the video codec info.
|
||||
* @param acodec an object contains the audio codec info.
|
||||
*/
|
||||
SrsPublisher.prototype.publish = function(url, vcodec, acodec) {
|
||||
this.url = url;
|
||||
this.vcodec = vcodec;
|
||||
this.acodec = acodec;
|
||||
|
||||
this.callbackObj.ref.__publish(url, this.width, this.height, vcodec, acodec);
|
||||
}
|
||||
SrsPublisher.prototype.stop = function() {
|
||||
this.callbackObj.ref.__stop();
|
||||
}
|
||||
/**
|
||||
* when publisher ready.
|
||||
* @param cameras a string array contains the names of cameras.
|
||||
* @param microphones a string array contains the names of microphones.
|
||||
*/
|
||||
SrsPublisher.prototype.on_publisher_ready = function(cameras, microphones) {
|
||||
}
|
||||
/**
|
||||
* when publisher error.
|
||||
* @code the error code.
|
||||
* @desc the error desc message.
|
||||
*/
|
||||
SrsPublisher.prototype.on_publisher_error = function(code, desc) {
|
||||
throw new Error("publisher error. code=" + code + ", desc=" + desc);
|
||||
}
|
||||
SrsPublisher.prototype.on_publisher_warn = function(code, desc) {
|
||||
throw new Error("publisher warn. code=" + code + ", desc=" + desc);
|
||||
}
|
||||
function __srs_find_publisher(id) {
|
||||
for (var i = 0; i < SrsPublisher.__publishers.length; i++) {
|
||||
var publisher = SrsPublisher.__publishers[i];
|
||||
|
||||
if (publisher.id != id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return publisher;
|
||||
}
|
||||
|
||||
throw new Error("publisher not found. id=" + id);
|
||||
}
|
||||
function __srs_on_publisher_ready(id, cameras, microphones) {
|
||||
var publisher = __srs_find_publisher(id);
|
||||
|
||||
publisher.cameras = cameras;
|
||||
publisher.microphones = microphones;
|
||||
|
||||
publisher.on_publisher_ready(cameras, microphones);
|
||||
}
|
||||
function __srs_on_publisher_error(id, code) {
|
||||
var publisher = __srs_find_publisher(id);
|
||||
|
||||
publisher.code = code;
|
||||
|
||||
publisher.on_publisher_error(code, publisher.errors[""+code]);
|
||||
}
|
||||
function __srs_on_publisher_warn(id, code) {
|
||||
var publisher = __srs_find_publisher(id);
|
||||
|
||||
publisher.code = code;
|
||||
|
||||
publisher.on_publisher_warn(code, publisher.errors[""+code]);
|
||||
}
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
<head>
|
||||
<title>JWPlayer5</title>
|
||||
<meta http-equiv=Content-Type content="text/html;charset=utf-8">
|
||||
<style>
|
||||
body{margin:0; padding:0; color:#EEEEEE;}
|
||||
input.url{width:400px; height:20px;}
|
||||
input.size{width:40px; height:20px;}
|
||||
input.buffer{width:20px; height:20px;}
|
||||
input.play{width:60px; height: 25px;}
|
||||
select.type{width:50px; }
|
||||
span.size{padding-left:10px; padding-right:10px;}
|
||||
div.main{font-size:12px; padding:5px 10px 0px 5px; background-color:#333333; width: 780px;}
|
||||
div.player{padding-top:3px; padding-bottom:10px;}
|
||||
div.control{padding-bottom:10px; background-color:#333333; margin-top:5px;}
|
||||
</style>
|
||||
</head>
|
||||
<div class="main">
|
||||
<div id="player"></div>
|
||||
<div class="control" id="control">
|
||||
Url(RTMP/HTTP): <input id="url" type="text" class="url" value="rtmp://demo:1935/live/livestream"></input>
|
||||
<input type="button" class="play" value="Play" onclick="play()"></input>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="jwplayer.js" ></script>
|
||||
<script>jwplayer.key="L1P3Ig76mGOK94gZ9WAAGD+Fb1VCVhoZ/Dm0fg=="</script>
|
||||
<script type='text/javascript'>
|
||||
function play(){
|
||||
var player = document.getElementById("player");
|
||||
player.innerHTML = "";
|
||||
|
||||
var div = document.createElement("div");
|
||||
div.id = "player_div";
|
||||
player.appendChild(div);
|
||||
|
||||
var url = document.getElementById("url").value;
|
||||
var provider = (url.indexOf("rtmp://") == 0) ? "rtmp":"http";
|
||||
var conf = {
|
||||
file: url.substr(url.lastIndexOf("/") + 1),
|
||||
streamer: url.substr(0, url.lastIndexOf("/")),
|
||||
provider: 'rtmp',
|
||||
width: "720",
|
||||
height: "576",
|
||||
autostart: true,
|
||||
};
|
||||
//console.log(conf);
|
||||
jwplayer('player_div').setup(conf);
|
||||
}
|
||||
play();
|
||||
</script>
|
||||
|
File diff suppressed because one or more lines are too long
Binary file not shown.
128
trunk/research/players/jwplayer6.html
Executable file
128
trunk/research/players/jwplayer6.html
Executable file
|
@ -0,0 +1,128 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>SRS</title>
|
||||
<meta charset="utf-8">
|
||||
<link rel="stylesheet" type="text/css" href="css/bootstrap.min.css"/>
|
||||
<script type="text/javascript" src="js/jquery-1.10.2.min.js"></script>
|
||||
<script type="text/javascript" src="js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="js/srs.js"></script>
|
||||
<style>
|
||||
body{
|
||||
padding-top: 55px;
|
||||
}
|
||||
#main_modal {
|
||||
width: 700px;
|
||||
margin-left: -350px;
|
||||
}
|
||||
</style>
|
||||
<script type="text/javascript" src="js/jwplayer.js" ></script>
|
||||
<script type='text/javascript'>jwplayer.key = 'N8zhkmYvvRwOhz4aTGkySoEri4x+9pQwR7GHIQ=='; </script>
|
||||
<script type="text/javascript">
|
||||
var _player = null;
|
||||
var _url = null;
|
||||
$(function(){
|
||||
// get the vhost and port to set the default url.
|
||||
// for example: http://192.168.1.213/players/jwplayer6.html?port=1935&vhost=demo
|
||||
// url set to: rtmp://demo:1935/live/livestream
|
||||
srs_init("#txt_rtmp_url", "#txt_hls_url", "#main_modal");
|
||||
|
||||
$("#main_modal").on("hide", function(){
|
||||
$("#div_container").remove();
|
||||
_player.stop();
|
||||
});
|
||||
|
||||
$("#main_modal").on("show", function(){
|
||||
$("#div_container").remove();
|
||||
|
||||
var div_container = $("<div/>");
|
||||
$(div_container).attr("id", "div_container");
|
||||
$("#player").append(div_container);
|
||||
|
||||
var player = $("<div/>");
|
||||
$(player).attr("id", "player_id");
|
||||
$(div_container).append(player);
|
||||
|
||||
var conf = {
|
||||
file: _url,
|
||||
width: srs_get_player_width(),
|
||||
height: srs_get_player_height(),
|
||||
autostart: true,
|
||||
analytics: { enabled: false}
|
||||
};
|
||||
_player = jwplayer('player_id').setup(conf);
|
||||
});
|
||||
|
||||
$("#btn_play_rtmp").click(function(){
|
||||
_url = $("#txt_rtmp_url").val();
|
||||
$("#main_modal").modal({show:true, keyboard:false});
|
||||
});
|
||||
|
||||
$("#btn_play_hls").click(function(){
|
||||
_url = $("#txt_hls_url").val();
|
||||
$("#main_modal").modal({show:true, keyboard:false});
|
||||
});
|
||||
|
||||
var query = parse_query_string();
|
||||
if (query.hls_autostart == "true") {
|
||||
_url = $("#txt_hls_url").val();
|
||||
$("#main_modal").modal({show:true, keyboard:false});
|
||||
} else if (query.rtmp_autostart == "true") {
|
||||
_url = $("#txt_rtmp_url").val();
|
||||
$("#main_modal").modal({show:true, keyboard:false});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="navbar navbar-fixed-top">
|
||||
<div class="navbar-inner">
|
||||
<div class="container">
|
||||
<a class="brand" href="index.html">SRS</a>
|
||||
<div class="nav-collapse collapse">
|
||||
<ul class="nav">
|
||||
<li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li>
|
||||
<li><a id="nav_srs_publisher" href="srs_publisher.html">SRS编码器</a></li>
|
||||
<li><a id="nav_srs_bwt" href="srs_bwt.html">SRS测网速</a></li>
|
||||
<li class="active"><a id="nav_jwplayer6" href="jwplayer6.html">JWPlayer6播放器</a></li>
|
||||
<li><a id="nav_osmf" href="osmf.html">AdobeOSMF播放器</a></li>
|
||||
<li><a id="nav_vlc" href="vlc.html">VLC播放器</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="alert alert-info fade in">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
<strong><span>Usage:</span></strong> <span>输入地址后点击播放按钮</span>
|
||||
</div>
|
||||
<div class="form-inline">
|
||||
URL:
|
||||
<input type="text" id="txt_rtmp_url" class="input-xxlarge" value=""></input>
|
||||
<button class="btn btn-primary" id="btn_play_rtmp">播放RTMP</button>
|
||||
</div>
|
||||
<hr/>
|
||||
<div class="form-inline">
|
||||
URL:
|
||||
<input type="text" id="txt_hls_url" class="input-xxlarge" value=""></input>
|
||||
<button class="btn btn-primary" id="btn_play_hls"> 播放HLS </button>
|
||||
</div>
|
||||
<div id="main_modal" class="modal hide fade">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3>JWPlayer6</h3>
|
||||
</div>
|
||||
<div class="modal-body" id="player">
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-primary" data-dismiss="modal" aria-hidden="true"> 关闭 </button>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<footer>
|
||||
<p><a href="https://github.com/winlinvip/simple-rtmp-server">SRS Team © 2013</a></p>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
<head>
|
||||
<title>JWPlayer6</title>
|
||||
<meta http-equiv=Content-Type content="text/html;charset=utf-8">
|
||||
<style>
|
||||
body{margin:0; padding:0; color:#EEEEEE;}
|
||||
input.url{width:400px; height:20px;}
|
||||
input.size{width:40px; height:20px;}
|
||||
input.buffer{width:20px; height:20px;}
|
||||
input.play{width:60px; height: 25px;}
|
||||
select.type{width:50px; }
|
||||
span.size{padding-left:10px; padding-right:10px;}
|
||||
div.main{font-size:12px; padding:5px 10px 0px 5px; background-color:#333333; width: 780px;}
|
||||
div.player{padding-top:3px; padding-bottom:10px;}
|
||||
div.control{padding-bottom:10px; background-color:#333333; margin-top:5px;}
|
||||
</style>
|
||||
</head>
|
||||
<div class="main">
|
||||
<div id="player"></div>
|
||||
<div class="control" id="control">
|
||||
Url(RTMP/HTTP): <input id="url" type="text" class="url" value="rtmp://demo:1935/live/livestream"></input>
|
||||
<input type="button" class="play" value="Play" onclick="play()"></input>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="jwplayer.js" ></script>
|
||||
<script>jwplayer.key="L1P3Ig76mGOK94gZ9WAAGD+Fb1VCVhoZ/Dm0fg=="</script>
|
||||
<script type='text/javascript'>
|
||||
function play(){
|
||||
var player = document.getElementById("player");
|
||||
player.innerHTML = "";
|
||||
|
||||
var div = document.createElement("div");
|
||||
div.id = "player_div";
|
||||
player.appendChild(div);
|
||||
|
||||
var url = document.getElementById("url").value;
|
||||
var conf = {
|
||||
file: url,
|
||||
width: "720",
|
||||
height: "576",
|
||||
autostart: true,
|
||||
};
|
||||
//console.log(conf);
|
||||
jwplayer('player_div').setup(conf);
|
||||
}
|
||||
play();
|
||||
</script>
|
||||
|
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
117
trunk/research/players/osmf.html
Executable file
117
trunk/research/players/osmf.html
Executable file
|
@ -0,0 +1,117 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>SRS</title>
|
||||
<meta charset="utf-8">
|
||||
<link rel="stylesheet" type="text/css" href="css/bootstrap.min.css"/>
|
||||
<script type="text/javascript" src="js/jquery-1.10.2.min.js"></script>
|
||||
<script type="text/javascript" src="js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="js/swfobject.js"></script>
|
||||
<script type="text/javascript" src="js/srs.js"></script>
|
||||
<style>
|
||||
body{
|
||||
padding-top: 55px;
|
||||
}
|
||||
#main_modal {
|
||||
width: 700px;
|
||||
margin-left: -350px;
|
||||
}
|
||||
</style>
|
||||
<script type="text/javascript">
|
||||
function osmf_play(url) {
|
||||
$("#div_container").remove();
|
||||
|
||||
var div_container = $("<div/>");
|
||||
$(div_container).attr("id", "div_container");
|
||||
$("#player").append(div_container);
|
||||
|
||||
var player = $("<div/>");
|
||||
$(player).attr("id", "player_id");
|
||||
$(div_container).append(player);
|
||||
|
||||
var flashvars = {};
|
||||
flashvars.src = url;
|
||||
flashvars.streamType = "live"; // live or recorded
|
||||
flashvars.autoPlay = true;
|
||||
flashvars.controlBarAutoHide = false;
|
||||
flashvars.scaleMode = "stretch";
|
||||
flashvars.bufferTime = 0.8;
|
||||
|
||||
var params = {};
|
||||
params.allowFullScreen = true;
|
||||
|
||||
var attributes = {};
|
||||
|
||||
swfobject.embedSWF(
|
||||
"js/StrobeMediaPlayback.swf", "player_id",
|
||||
srs_get_player_width(), srs_get_player_height(),
|
||||
"11.1", "js/AdobeFlashPlayerInstall.swf",
|
||||
flashvars, params, attributes
|
||||
);
|
||||
}
|
||||
$(function(){
|
||||
// get the vhost and port to set the default url.
|
||||
// for example: http://192.168.1.213/players/jwplayer6.html?port=1935&vhost=demo
|
||||
// url set to: rtmp://demo:1935/live/livestream
|
||||
srs_init("#txt_url", null, "#main_modal");
|
||||
|
||||
$("#main_modal").on("hide", function(){
|
||||
osmf_play("http://localhost");
|
||||
$("#div_container").remove();
|
||||
});
|
||||
$("#main_modal").on("show", function(){
|
||||
var url = $("#txt_url").val();
|
||||
osmf_play(url);
|
||||
});
|
||||
|
||||
$("#btn_play").click(function(){
|
||||
$("#main_modal").modal({show:true, keyboard:false});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="navbar navbar-fixed-top">
|
||||
<div class="navbar-inner">
|
||||
<div class="container">
|
||||
<a class="brand" href="index.html">SRS</a>
|
||||
<div class="nav-collapse collapse">
|
||||
<ul class="nav">
|
||||
<li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li>
|
||||
<li><a id="nav_srs_publisher" href="srs_publisher.html">SRS编码器</a></li>
|
||||
<li><a id="nav_srs_bwt" href="srs_bwt.html">SRS测网速</a></li>
|
||||
<li><a id="nav_jwplayer6" href="jwplayer6.html">JWPlayer6播放器</a></li>
|
||||
<li class="active"><a id="nav_osmf" href="osmf.html">AdobeOSMF播放器</a></li>
|
||||
<li><a id="nav_vlc" href="vlc.html">VLC播放器</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="alert alert-info fade in">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
<strong><span>Usage:</span></strong> <span>输入地址后点击播放按钮</span>
|
||||
</div>
|
||||
<div class="form-inline">
|
||||
URL:
|
||||
<input type="text" id="txt_url" class="input-xxlarge" value=""></input>
|
||||
<button class="btn btn-primary" id="btn_play">播放视频</button>
|
||||
</div>
|
||||
<div id="main_modal" class="modal hide fade">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3>AdobeOSMF</h3>
|
||||
</div>
|
||||
<div class="modal-body" id="player">
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-primary" data-dismiss="modal" aria-hidden="true"> 关闭 </button>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<footer>
|
||||
<p><a href="https://github.com/winlinvip/simple-rtmp-server">SRS Team © 2013</a></p>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
|
@ -1,87 +0,0 @@
|
|||
<head>
|
||||
<meta http-equiv=Content-Type content="text/html;charset=utf-8">
|
||||
<title>OSMFPlayer</title>
|
||||
</head>
|
||||
|
||||
<style>
|
||||
body{margin:0; padding:0; color:#EEEEEE;}
|
||||
input.url{width:300px; height:20px;}
|
||||
input.size{width:40px; height:20px;}
|
||||
input.buffer{width:20px; height:20px;}
|
||||
input.play{width:60px; height: 25px;}
|
||||
select.type{width:50px; }
|
||||
span.size{padding-left:10px; padding-right:10px;}
|
||||
div.main{font-size:12px; padding:5px 10px 0px 5px; background-color:#333333;}
|
||||
div.player{padding-top:3px; padding-bottom:10px;}
|
||||
div.control{padding-bottom:10px; background-color:#333333; }
|
||||
</style>
|
||||
|
||||
<div class="main" id="main">
|
||||
<div id="player" class="player"></div>
|
||||
<div class="control" id="control">
|
||||
Url(RTMP/HTTP): <input id="url" type="text" class="url" value="rtmp://demo:1935/live/livestream"></input>
|
||||
<select class="type" id="type">
|
||||
<option value="live" selected>live</option>
|
||||
<option value="recorded">vod</option>
|
||||
</select>
|
||||
<span class="size">
|
||||
Width: <input id="width" type="text" class="size" value="720"></input>
|
||||
Height: <input id="height" type="text" class="size" value="576"></input>
|
||||
Buffer: <input id="buffer" type="text" class="buffer" value="2"></input>(s)
|
||||
</span>
|
||||
<input type="button" class="play" value="Play" onclick="play()"></input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="swfobject.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
player = document.getElementById("player");
|
||||
player_div = null;
|
||||
|
||||
function play(){
|
||||
// remove old player
|
||||
if(player_div != null){
|
||||
player.innerHTML = "";
|
||||
}
|
||||
|
||||
// create new div
|
||||
player_div = document.createElement("div");
|
||||
player.appendChild(player_div);
|
||||
|
||||
// set id to swfobject to create player.
|
||||
player_div.id = "player_div";
|
||||
|
||||
// generate player.
|
||||
var width = document.getElementById("width").value;
|
||||
var height = document.getElementById("height").value;
|
||||
|
||||
// set new style
|
||||
var main = document.getElementById("main");
|
||||
var min_width = 830;
|
||||
main.style.width = Math.max(min_width, width);
|
||||
|
||||
var flashvars = {};
|
||||
flashvars.src = document.getElementById("url").value;
|
||||
flashvars.streamType = document.getElementById("type").value; // live or recorded
|
||||
flashvars.autoPlay = true;
|
||||
flashvars.controlBarAutoHide = false;
|
||||
flashvars.scaleMode = "stretch";
|
||||
flashvars.bufferTime = document.getElementById("buffer").value;
|
||||
|
||||
var params = {};
|
||||
params.allowFullScreen = true;
|
||||
|
||||
var attributes = {};
|
||||
|
||||
swfobject.embedSWF(
|
||||
"StrobeMediaPlayback.swf", "player_div",
|
||||
width, height,
|
||||
"11.1", "AdobeFlashPlayerInstall.swf",
|
||||
flashvars, params, attributes
|
||||
);
|
||||
}
|
||||
|
||||
// play the default stream.
|
||||
play();
|
||||
</script>
|
Binary file not shown.
Binary file not shown.
|
@ -1,33 +0,0 @@
|
|||
<head>
|
||||
<meta http-equiv=Content-Type content="text/html;charset=utf-8">
|
||||
<title>RtmpPlayer</title>
|
||||
<style>
|
||||
body{margin:0; padding:0; color:#EEEEEE;}
|
||||
input.url{width:400px; height:20px;}
|
||||
input.size{width:40px; height:20px;}
|
||||
input.buffer{width:20px; height:20px;}
|
||||
input.play{width:60px; height: 25px;}
|
||||
select.type{width:50px; }
|
||||
span.size{padding-left:10px; padding-right:10px;}
|
||||
div.main{font-size:12px; padding:5px 10px 0px 5px; background-color:#333333; width: 980px;}
|
||||
div.player{padding-top:3px; padding-bottom:10px;}
|
||||
div.control{padding-bottom:10px; background-color:#333333; }
|
||||
</style>
|
||||
</head>
|
||||
<script type="text/javascript" src="rtmp.player.js"></script>
|
||||
<div>
|
||||
<div id="player"></div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
var o = new RtmpPlayer("player", "RtmpPlayer.swf", 1350, 1050);
|
||||
|
||||
o.setRtmpUrl("rtmp://demo:1935/live/livestream");
|
||||
o.admin = "admin";
|
||||
o.password = "123456";
|
||||
o.loop = false;
|
||||
o.cansave = true;
|
||||
o.islive = true;
|
||||
o.autostart = true;
|
||||
|
||||
o.run();
|
||||
</script>
|
|
@ -1,909 +0,0 @@
|
|||
// china cache RTMP player:
|
||||
// run() to run player.
|
||||
// server: the RTMP server, e.g. 192.168.1.5
|
||||
// port: the RTMP port, default is 1935.
|
||||
// adminPort: the admin port, default is 1111.
|
||||
// user: the admin login user name, default is admin.
|
||||
// password: the admin login user password, default is null.
|
||||
// app: the application of fms to play. e.g vod
|
||||
// stream: the stream of fms to play. e.g mp4:sample1_1500kbps.f4v
|
||||
// islive: if true, live mode.
|
||||
// autostart:Boolean a property which specify auto play.
|
||||
// loop:Boolean a property which specify auto loop.
|
||||
//
|
||||
// generate the release js version using GC:
|
||||
// java -jar google_closure_compiler/compiler.jar --js chinacache.src.js --js_output_file chinacache.js
|
||||
function RtmpPlayer(div_container, swf_url, width, height){
|
||||
this.swf_url = swf_url;
|
||||
this.div_container = div_container;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
|
||||
this.flashvars = {};
|
||||
this.flashvars.width = width;
|
||||
this.flashvars.height = height;
|
||||
|
||||
this.params = {};
|
||||
this.params.quality = "high";
|
||||
this.params.bgcolor = "#EEEEEE";
|
||||
this.params.allowscriptaccess = "always";
|
||||
this.params.allowfullscreen = "true";
|
||||
this.params.wmode = "Window";
|
||||
|
||||
this.attributes = {};
|
||||
var html_element_id = "RtmpPlayer";
|
||||
this.attributes.id = html_element_id;
|
||||
this.attributes.name = html_element_id;
|
||||
this.attributes.align = "middle";
|
||||
|
||||
this.autostart = false;
|
||||
this.appLevel = 1;
|
||||
this.loop = false;
|
||||
this.port = 1935;
|
||||
this.adminPort = 1111;
|
||||
this.server = null;
|
||||
this.app = null;
|
||||
this.stream = null;
|
||||
this.islive = false;
|
||||
this.cansave = true;
|
||||
this.user = "admin";
|
||||
this.password = null;
|
||||
}
|
||||
|
||||
RtmpPlayer.prototype.setRtmpUrl = function(url) {
|
||||
var s = this.trim(url.replace("rtmp://", ""), "/");
|
||||
|
||||
if(s.indexOf(":") < 0 || s.indexOf(":") > s.indexOf("/")){
|
||||
this.server = s.substr(0, s.indexOf("/"));
|
||||
s = this.trim(s.substr(this.server.length), "/");
|
||||
}
|
||||
else{
|
||||
this.server = s.substr(0, s.indexOf(":"));
|
||||
s = this.trim(s.substr(this.server.length), ":");
|
||||
|
||||
this.port = s.substr(0, s.indexOf("/"));
|
||||
s = this.trim(s.substr(this.port.length), "/");
|
||||
}
|
||||
|
||||
this.app = s.substr(0, s.indexOf("/"));
|
||||
s = this.trim(s.substr(this.app.length), "/");
|
||||
|
||||
this.stream = s;
|
||||
}
|
||||
|
||||
RtmpPlayer.prototype.trim = function(s, str) {
|
||||
var r = s;
|
||||
|
||||
while(r.indexOf(str) == 0){
|
||||
r = r.substr(str.length);
|
||||
}
|
||||
while(r.lastIndexOf(str) == r.length - str.length){
|
||||
r = r.substr(0, r.length - str.length);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
RtmpPlayer.prototype.ck = function(v, pro) {
|
||||
if(v == null || v == undefined){
|
||||
alert(pro + "不能为空!");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
RtmpPlayer.prototype.run = function() {
|
||||
// const values
|
||||
swfVersionStr = "11.1";
|
||||
xiSwfUrlStr = "AdobeFlashPlayerInstall.swf";
|
||||
|
||||
// check
|
||||
if(this.ck(this.autostart, "autostart(是否自动播放)") || this.ck(this.port, "port(FMS服务器端口)")
|
||||
|| this.ck(this.adminPort, "adminPort(FMS服务器管理端口)") || this.ck(this.server, "server(FMS服务器地址)")
|
||||
|| this.ck(this.app, "app(FMS application)") || this.ck(this.stream, "stream(流名称)")
|
||||
|| this.ck(this.user, "user(FMS登录用户名)") || this.ck(this.password, "password(FMS登录用户密码)")){
|
||||
return;
|
||||
}
|
||||
|
||||
this.flashvars.autostart = this.autostart;
|
||||
this.flashvars.appLevel = this.appLevel;
|
||||
this.flashvars.loop = this.loop;
|
||||
this.flashvars.port = this.port;
|
||||
this.flashvars.adminPort = this.adminPort;
|
||||
this.flashvars.server = this.server;
|
||||
this.flashvars.app = this.app;
|
||||
this.flashvars.stream = this.stream;
|
||||
this.flashvars.islive = this.islive;
|
||||
this.flashvars.cansave = this.cansave;
|
||||
this.flashvars.user = this.user;
|
||||
this.flashvars.password = this.password;
|
||||
|
||||
var chnvideoPlayerHost = this;
|
||||
swfobject.embedSWF(
|
||||
this.swf_url, this.div_container,
|
||||
this.width, this.height,
|
||||
swfVersionStr, xiSwfUrlStr,
|
||||
this.flashvars, this.params, this.attributes,
|
||||
function(callbackObj){
|
||||
chnvideoPlayerHost.callbackObj = callbackObj;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/*! SWFObject v2.2 <http://code.google.com/p/swfobject/>
|
||||
is released under the MIT License <http://www.opensource.org/licenses/mit-license.php>
|
||||
*/
|
||||
|
||||
var swfobject = function() {
|
||||
|
||||
var UNDEF = "undefined",
|
||||
OBJECT = "object",
|
||||
SHOCKWAVE_FLASH = "Shockwave Flash",
|
||||
SHOCKWAVE_FLASH_AX = "ShockwaveFlash.ShockwaveFlash",
|
||||
FLASH_MIME_TYPE = "application/x-shockwave-flash",
|
||||
EXPRESS_INSTALL_ID = "SWFObjectExprInst",
|
||||
ON_READY_STATE_CHANGE = "onreadystatechange",
|
||||
|
||||
win = window,
|
||||
doc = document,
|
||||
nav = navigator,
|
||||
|
||||
plugin = false,
|
||||
domLoadFnArr = [main],
|
||||
regObjArr = [],
|
||||
objIdArr = [],
|
||||
listenersArr = [],
|
||||
storedAltContent,
|
||||
storedAltContentId,
|
||||
storedCallbackFn,
|
||||
storedCallbackObj,
|
||||
isDomLoaded = false,
|
||||
isExpressInstallActive = false,
|
||||
dynamicStylesheet,
|
||||
dynamicStylesheetMedia,
|
||||
autoHideShow = true,
|
||||
|
||||
/* Centralized function for browser feature detection
|
||||
- User agent string detection is only used when no good alternative is possible
|
||||
- Is executed directly for optimal performance
|
||||
*/
|
||||
ua = function() {
|
||||
var w3cdom = typeof doc.getElementById != UNDEF && typeof doc.getElementsByTagName != UNDEF && typeof doc.createElement != UNDEF,
|
||||
u = nav.userAgent.toLowerCase(),
|
||||
p = nav.platform.toLowerCase(),
|
||||
windows = p ? /win/.test(p) : /win/.test(u),
|
||||
mac = p ? /mac/.test(p) : /mac/.test(u),
|
||||
webkit = /webkit/.test(u) ? parseFloat(u.replace(/^.*webkit\/(\d+(\.\d+)?).*$/, "$1")) : false, // returns either the webkit version or false if not webkit
|
||||
ie = !+"\v1", // feature detection based on Andrea Giammarchi's solution: http://webreflection.blogspot.com/2009/01/32-bytes-to-know-if-your-browser-is-ie.html
|
||||
playerVersion = [0,0,0],
|
||||
d = null;
|
||||
if (typeof nav.plugins != UNDEF && typeof nav.plugins[SHOCKWAVE_FLASH] == OBJECT) {
|
||||
d = nav.plugins[SHOCKWAVE_FLASH].description;
|
||||
if (d && !(typeof nav.mimeTypes != UNDEF && nav.mimeTypes[FLASH_MIME_TYPE] && !nav.mimeTypes[FLASH_MIME_TYPE].enabledPlugin)) { // navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin indicates whether plug-ins are enabled or disabled in Safari 3+
|
||||
plugin = true;
|
||||
ie = false; // cascaded feature detection for Internet Explorer
|
||||
d = d.replace(/^.*\s+(\S+\s+\S+$)/, "$1");
|
||||
playerVersion[0] = parseInt(d.replace(/^(.*)\..*$/, "$1"), 10);
|
||||
playerVersion[1] = parseInt(d.replace(/^.*\.(.*)\s.*$/, "$1"), 10);
|
||||
playerVersion[2] = /[a-zA-Z]/.test(d) ? parseInt(d.replace(/^.*[a-zA-Z]+(.*)$/, "$1"), 10) : 0;
|
||||
}
|
||||
}
|
||||
else if (typeof win.ActiveXObject != UNDEF) {
|
||||
try {
|
||||
var a = new ActiveXObject(SHOCKWAVE_FLASH_AX);
|
||||
if (a) { // a will return null when ActiveX is disabled
|
||||
d = a.GetVariable("$version");
|
||||
if (d) {
|
||||
ie = true; // cascaded feature detection for Internet Explorer
|
||||
d = d.split(" ")[1].split(",");
|
||||
playerVersion = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(e) {}
|
||||
}
|
||||
return { w3:w3cdom, pv:playerVersion, wk:webkit, ie:ie, win:windows, mac:mac };
|
||||
}(),
|
||||
|
||||
/* Cross-browser onDomLoad
|
||||
- Will fire an event as soon as the DOM of a web page is loaded
|
||||
- Internet Explorer workaround based on Diego Perini's solution: http://javascript.nwbox.com/IEContentLoaded/
|
||||
- Regular onload serves as fallback
|
||||
*/
|
||||
onDomLoad = function() {
|
||||
if (!ua.w3) { return; }
|
||||
if ((typeof doc.readyState != UNDEF && doc.readyState == "complete") || (typeof doc.readyState == UNDEF && (doc.getElementsByTagName("body")[0] || doc.body))) { // function is fired after onload, e.g. when script is inserted dynamically
|
||||
callDomLoadFunctions();
|
||||
}
|
||||
if (!isDomLoaded) {
|
||||
if (typeof doc.addEventListener != UNDEF) {
|
||||
doc.addEventListener("DOMContentLoaded", callDomLoadFunctions, false);
|
||||
}
|
||||
if (ua.ie && ua.win) {
|
||||
doc.attachEvent(ON_READY_STATE_CHANGE, function() {
|
||||
if (doc.readyState == "complete") {
|
||||
doc.detachEvent(ON_READY_STATE_CHANGE, arguments.callee);
|
||||
callDomLoadFunctions();
|
||||
}
|
||||
});
|
||||
if (win == top) { // if not inside an iframe
|
||||
(function(){
|
||||
if (isDomLoaded) { return; }
|
||||
try {
|
||||
doc.documentElement.doScroll("left");
|
||||
}
|
||||
catch(e) {
|
||||
setTimeout(arguments.callee, 0);
|
||||
return;
|
||||
}
|
||||
callDomLoadFunctions();
|
||||
})();
|
||||
}
|
||||
}
|
||||
if (ua.wk) {
|
||||
(function(){
|
||||
if (isDomLoaded) { return; }
|
||||
if (!/loaded|complete/.test(doc.readyState)) {
|
||||
setTimeout(arguments.callee, 0);
|
||||
return;
|
||||
}
|
||||
callDomLoadFunctions();
|
||||
})();
|
||||
}
|
||||
addLoadEvent(callDomLoadFunctions);
|
||||
}
|
||||
}();
|
||||
|
||||
function callDomLoadFunctions() {
|
||||
if (isDomLoaded) { return; }
|
||||
try { // test if we can really add/remove elements to/from the DOM; we don't want to fire it too early
|
||||
var t = doc.getElementsByTagName("body")[0].appendChild(createElement("span"));
|
||||
t.parentNode.removeChild(t);
|
||||
}
|
||||
catch (e) { return; }
|
||||
isDomLoaded = true;
|
||||
var dl = domLoadFnArr.length;
|
||||
for (var i = 0; i < dl; i++) {
|
||||
domLoadFnArr[i]();
|
||||
}
|
||||
}
|
||||
|
||||
function addDomLoadEvent(fn) {
|
||||
if (isDomLoaded) {
|
||||
fn();
|
||||
}
|
||||
else {
|
||||
domLoadFnArr[domLoadFnArr.length] = fn; // Array.push() is only available in IE5.5+
|
||||
}
|
||||
}
|
||||
|
||||
/* Cross-browser onload
|
||||
- Based on James Edwards' solution: http://brothercake.com/site/resources/scripts/onload/
|
||||
- Will fire an event as soon as a web page including all of its assets are loaded
|
||||
*/
|
||||
function addLoadEvent(fn) {
|
||||
if (typeof win.addEventListener != UNDEF) {
|
||||
win.addEventListener("load", fn, false);
|
||||
}
|
||||
else if (typeof doc.addEventListener != UNDEF) {
|
||||
doc.addEventListener("load", fn, false);
|
||||
}
|
||||
else if (typeof win.attachEvent != UNDEF) {
|
||||
addListener(win, "onload", fn);
|
||||
}
|
||||
else if (typeof win.onload == "function") {
|
||||
var fnOld = win.onload;
|
||||
win.onload = function() {
|
||||
fnOld();
|
||||
fn();
|
||||
};
|
||||
}
|
||||
else {
|
||||
win.onload = fn;
|
||||
}
|
||||
}
|
||||
|
||||
/* Main function
|
||||
- Will preferably execute onDomLoad, otherwise onload (as a fallback)
|
||||
*/
|
||||
function main() {
|
||||
if (plugin) {
|
||||
testPlayerVersion();
|
||||
}
|
||||
else {
|
||||
matchVersions();
|
||||
}
|
||||
}
|
||||
|
||||
/* Detect the Flash Player version for non-Internet Explorer browsers
|
||||
- Detecting the plug-in version via the object element is more precise than using the plugins collection item's description:
|
||||
a. Both release and build numbers can be detected
|
||||
b. Avoid wrong descriptions by corrupt installers provided by Adobe
|
||||
c. Avoid wrong descriptions by multiple Flash Player entries in the plugin Array, caused by incorrect browser imports
|
||||
- Disadvantage of this method is that it depends on the availability of the DOM, while the plugins collection is immediately available
|
||||
*/
|
||||
function testPlayerVersion() {
|
||||
var b = doc.getElementsByTagName("body")[0];
|
||||
var o = createElement(OBJECT);
|
||||
o.setAttribute("type", FLASH_MIME_TYPE);
|
||||
var t = b.appendChild(o);
|
||||
if (t) {
|
||||
var counter = 0;
|
||||
(function(){
|
||||
if (typeof t.GetVariable != UNDEF) {
|
||||
var d = t.GetVariable("$version");
|
||||
if (d) {
|
||||
d = d.split(" ")[1].split(",");
|
||||
ua.pv = [parseInt(d[0], 10), parseInt(d[1], 10), parseInt(d[2], 10)];
|
||||
}
|
||||
}
|
||||
else if (counter < 10) {
|
||||
counter++;
|
||||
setTimeout(arguments.callee, 10);
|
||||
return;
|
||||
}
|
||||
b.removeChild(o);
|
||||
t = null;
|
||||
matchVersions();
|
||||
})();
|
||||
}
|
||||
else {
|
||||
matchVersions();
|
||||
}
|
||||
}
|
||||
|
||||
/* Perform Flash Player and SWF version matching; static publishing only
|
||||
*/
|
||||
function matchVersions() {
|
||||
var rl = regObjArr.length;
|
||||
if (rl > 0) {
|
||||
for (var i = 0; i < rl; i++) { // for each registered object element
|
||||
var id = regObjArr[i].id;
|
||||
var cb = regObjArr[i].callbackFn;
|
||||
var cbObj = {success:false, id:id};
|
||||
if (ua.pv[0] > 0) {
|
||||
var obj = getElementById(id);
|
||||
if (obj) {
|
||||
if (hasPlayerVersion(regObjArr[i].swfVersion) && !(ua.wk && ua.wk < 312)) { // Flash Player version >= published SWF version: Houston, we have a match!
|
||||
setVisibility(id, true);
|
||||
if (cb) {
|
||||
cbObj.success = true;
|
||||
cbObj.ref = getObjectById(id);
|
||||
cb(cbObj);
|
||||
}
|
||||
}
|
||||
else if (regObjArr[i].expressInstall && canExpressInstall()) { // show the Adobe Express Install dialog if set by the web page author and if supported
|
||||
var att = {};
|
||||
att.data = regObjArr[i].expressInstall;
|
||||
att.width = obj.getAttribute("width") || "0";
|
||||
att.height = obj.getAttribute("height") || "0";
|
||||
if (obj.getAttribute("class")) { att.styleclass = obj.getAttribute("class"); }
|
||||
if (obj.getAttribute("align")) { att.align = obj.getAttribute("align"); }
|
||||
// parse HTML object param element's name-value pairs
|
||||
var par = {};
|
||||
var p = obj.getElementsByTagName("param");
|
||||
var pl = p.length;
|
||||
for (var j = 0; j < pl; j++) {
|
||||
if (p[j].getAttribute("name").toLowerCase() != "movie") {
|
||||
par[p[j].getAttribute("name")] = p[j].getAttribute("value");
|
||||
}
|
||||
}
|
||||
showExpressInstall(att, par, id, cb);
|
||||
}
|
||||
else { // Flash Player and SWF version mismatch or an older Webkit engine that ignores the HTML object element's nested param elements: display alternative content instead of SWF
|
||||
displayAltContent(obj);
|
||||
if (cb) { cb(cbObj); }
|
||||
}
|
||||
}
|
||||
}
|
||||
else { // if no Flash Player is installed or the fp version cannot be detected we let the HTML object element do its job (either show a SWF or alternative content)
|
||||
setVisibility(id, true);
|
||||
if (cb) {
|
||||
var o = getObjectById(id); // test whether there is an HTML object element or not
|
||||
if (o && typeof o.SetVariable != UNDEF) {
|
||||
cbObj.success = true;
|
||||
cbObj.ref = o;
|
||||
}
|
||||
cb(cbObj);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getObjectById(objectIdStr) {
|
||||
var r = null;
|
||||
var o = getElementById(objectIdStr);
|
||||
if (o && o.nodeName == "OBJECT") {
|
||||
if (typeof o.SetVariable != UNDEF) {
|
||||
r = o;
|
||||
}
|
||||
else {
|
||||
var n = o.getElementsByTagName(OBJECT)[0];
|
||||
if (n) {
|
||||
r = n;
|
||||
}
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Requirements for Adobe Express Install
|
||||
- only one instance can be active at a time
|
||||
- fp 6.0.65 or higher
|
||||
- Win/Mac OS only
|
||||
- no Webkit engines older than version 312
|
||||
*/
|
||||
function canExpressInstall() {
|
||||
return !isExpressInstallActive && hasPlayerVersion("6.0.65") && (ua.win || ua.mac) && !(ua.wk && ua.wk < 312);
|
||||
}
|
||||
|
||||
/* Show the Adobe Express Install dialog
|
||||
- Reference: http://www.adobe.com/cfusion/knowledgebase/index.cfm?id=6a253b75
|
||||
*/
|
||||
function showExpressInstall(att, par, replaceElemIdStr, callbackFn) {
|
||||
isExpressInstallActive = true;
|
||||
storedCallbackFn = callbackFn || null;
|
||||
storedCallbackObj = {success:false, id:replaceElemIdStr};
|
||||
var obj = getElementById(replaceElemIdStr);
|
||||
if (obj) {
|
||||
if (obj.nodeName == "OBJECT") { // static publishing
|
||||
storedAltContent = abstractAltContent(obj);
|
||||
storedAltContentId = null;
|
||||
}
|
||||
else { // dynamic publishing
|
||||
storedAltContent = obj;
|
||||
storedAltContentId = replaceElemIdStr;
|
||||
}
|
||||
att.id = EXPRESS_INSTALL_ID;
|
||||
if (typeof att.width == UNDEF || (!/%$/.test(att.width) && parseInt(att.width, 10) < 310)) { att.width = "310"; }
|
||||
if (typeof att.height == UNDEF || (!/%$/.test(att.height) && parseInt(att.height, 10) < 137)) { att.height = "137"; }
|
||||
doc.title = doc.title.slice(0, 47) + " - Flash Player Installation";
|
||||
var pt = ua.ie && ua.win ? "ActiveX" : "PlugIn",
|
||||
fv = "MMredirectURL=" + encodeURI(window.location).toString().replace(/&/g,"%26") + "&MMplayerType=" + pt + "&MMdoctitle=" + doc.title;
|
||||
if (typeof par.flashvars != UNDEF) {
|
||||
par.flashvars += "&" + fv;
|
||||
}
|
||||
else {
|
||||
par.flashvars = fv;
|
||||
}
|
||||
// IE only: when a SWF is loading (AND: not available in cache) wait for the readyState of the object element to become 4 before removing it,
|
||||
// because you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work
|
||||
if (ua.ie && ua.win && obj.readyState != 4) {
|
||||
var newObj = createElement("div");
|
||||
replaceElemIdStr += "SWFObjectNew";
|
||||
newObj.setAttribute("id", replaceElemIdStr);
|
||||
obj.parentNode.insertBefore(newObj, obj); // insert placeholder div that will be replaced by the object element that loads expressinstall.swf
|
||||
obj.style.display = "none";
|
||||
(function(){
|
||||
if (obj.readyState == 4) {
|
||||
obj.parentNode.removeChild(obj);
|
||||
}
|
||||
else {
|
||||
setTimeout(arguments.callee, 10);
|
||||
}
|
||||
})();
|
||||
}
|
||||
createSWF(att, par, replaceElemIdStr);
|
||||
}
|
||||
}
|
||||
|
||||
/* Functions to abstract and display alternative content
|
||||
*/
|
||||
function displayAltContent(obj) {
|
||||
if (ua.ie && ua.win && obj.readyState != 4) {
|
||||
// IE only: when a SWF is loading (AND: not available in cache) wait for the readyState of the object element to become 4 before removing it,
|
||||
// because you cannot properly cancel a loading SWF file without breaking browser load references, also obj.onreadystatechange doesn't work
|
||||
var el = createElement("div");
|
||||
obj.parentNode.insertBefore(el, obj); // insert placeholder div that will be replaced by the alternative content
|
||||
el.parentNode.replaceChild(abstractAltContent(obj), el);
|
||||
obj.style.display = "none";
|
||||
(function(){
|
||||
if (obj.readyState == 4) {
|
||||
obj.parentNode.removeChild(obj);
|
||||
}
|
||||
else {
|
||||
setTimeout(arguments.callee, 10);
|
||||
}
|
||||
})();
|
||||
}
|
||||
else {
|
||||
obj.parentNode.replaceChild(abstractAltContent(obj), obj);
|
||||
}
|
||||
}
|
||||
|
||||
function abstractAltContent(obj) {
|
||||
var ac = createElement("div");
|
||||
if (ua.win && ua.ie) {
|
||||
ac.innerHTML = obj.innerHTML;
|
||||
}
|
||||
else {
|
||||
var nestedObj = obj.getElementsByTagName(OBJECT)[0];
|
||||
if (nestedObj) {
|
||||
var c = nestedObj.childNodes;
|
||||
if (c) {
|
||||
var cl = c.length;
|
||||
for (var i = 0; i < cl; i++) {
|
||||
if (!(c[i].nodeType == 1 && c[i].nodeName == "PARAM") && !(c[i].nodeType == 8)) {
|
||||
ac.appendChild(c[i].cloneNode(true));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ac;
|
||||
}
|
||||
|
||||
/* Cross-browser dynamic SWF creation
|
||||
*/
|
||||
function createSWF(attObj, parObj, id) {
|
||||
var r, el = getElementById(id);
|
||||
if (ua.wk && ua.wk < 312) { return r; }
|
||||
if (el) {
|
||||
if (typeof attObj.id == UNDEF) { // if no 'id' is defined for the object element, it will inherit the 'id' from the alternative content
|
||||
attObj.id = id;
|
||||
}
|
||||
if (ua.ie && ua.win) { // Internet Explorer + the HTML object element + W3C DOM methods do not combine: fall back to outerHTML
|
||||
var att = "";
|
||||
for (var i in attObj) {
|
||||
if (attObj[i] != Object.prototype[i]) { // filter out prototype additions from other potential libraries
|
||||
if (i.toLowerCase() == "data") {
|
||||
parObj.movie = attObj[i];
|
||||
}
|
||||
else if (i.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword
|
||||
att += ' class="' + attObj[i] + '"';
|
||||
}
|
||||
else if (i.toLowerCase() != "classid") {
|
||||
att += ' ' + i + '="' + attObj[i] + '"';
|
||||
}
|
||||
}
|
||||
}
|
||||
var par = "";
|
||||
for (var j in parObj) {
|
||||
if (parObj[j] != Object.prototype[j]) { // filter out prototype additions from other potential libraries
|
||||
par += '<param name="' + j + '" value="' + parObj[j] + '" />';
|
||||
}
|
||||
}
|
||||
el.outerHTML = '<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"' + att + '>' + par + '</object>';
|
||||
objIdArr[objIdArr.length] = attObj.id; // stored to fix object 'leaks' on unload (dynamic publishing only)
|
||||
r = getElementById(attObj.id);
|
||||
}
|
||||
else { // well-behaving browsers
|
||||
var o = createElement(OBJECT);
|
||||
o.setAttribute("type", FLASH_MIME_TYPE);
|
||||
for (var m in attObj) {
|
||||
if (attObj[m] != Object.prototype[m]) { // filter out prototype additions from other potential libraries
|
||||
if (m.toLowerCase() == "styleclass") { // 'class' is an ECMA4 reserved keyword
|
||||
o.setAttribute("class", attObj[m]);
|
||||
}
|
||||
else if (m.toLowerCase() != "classid") { // filter out IE specific attribute
|
||||
o.setAttribute(m, attObj[m]);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (var n in parObj) {
|
||||
if (parObj[n] != Object.prototype[n] && n.toLowerCase() != "movie") { // filter out prototype additions from other potential libraries and IE specific param element
|
||||
createObjParam(o, n, parObj[n]);
|
||||
}
|
||||
}
|
||||
el.parentNode.replaceChild(o, el);
|
||||
r = o;
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
function createObjParam(el, pName, pValue) {
|
||||
var p = createElement("param");
|
||||
p.setAttribute("name", pName);
|
||||
p.setAttribute("value", pValue);
|
||||
el.appendChild(p);
|
||||
}
|
||||
|
||||
/* Cross-browser SWF removal
|
||||
- Especially needed to safely and completely remove a SWF in Internet Explorer
|
||||
*/
|
||||
function removeSWF(id) {
|
||||
var obj = getElementById(id);
|
||||
if (obj && obj.nodeName == "OBJECT") {
|
||||
if (ua.ie && ua.win) {
|
||||
obj.style.display = "none";
|
||||
(function(){
|
||||
if (obj.readyState == 4) {
|
||||
removeObjectInIE(id);
|
||||
}
|
||||
else {
|
||||
setTimeout(arguments.callee, 10);
|
||||
}
|
||||
})();
|
||||
}
|
||||
else {
|
||||
obj.parentNode.removeChild(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function removeObjectInIE(id) {
|
||||
var obj = getElementById(id);
|
||||
if (obj) {
|
||||
for (var i in obj) {
|
||||
if (typeof obj[i] == "function") {
|
||||
obj[i] = null;
|
||||
}
|
||||
}
|
||||
obj.parentNode.removeChild(obj);
|
||||
}
|
||||
}
|
||||
|
||||
/* Functions to optimize JavaScript compression
|
||||
*/
|
||||
function getElementById(id) {
|
||||
var el = null;
|
||||
try {
|
||||
el = doc.getElementById(id);
|
||||
}
|
||||
catch (e) {}
|
||||
return el;
|
||||
}
|
||||
|
||||
function createElement(el) {
|
||||
return doc.createElement(el);
|
||||
}
|
||||
|
||||
/* Updated attachEvent function for Internet Explorer
|
||||
- Stores attachEvent information in an Array, so on unload the detachEvent functions can be called to avoid memory leaks
|
||||
*/
|
||||
function addListener(target, eventType, fn) {
|
||||
target.attachEvent(eventType, fn);
|
||||
listenersArr[listenersArr.length] = [target, eventType, fn];
|
||||
}
|
||||
|
||||
/* Flash Player and SWF content version matching
|
||||
*/
|
||||
function hasPlayerVersion(rv) {
|
||||
var pv = ua.pv, v = rv.split(".");
|
||||
v[0] = parseInt(v[0], 10);
|
||||
v[1] = parseInt(v[1], 10) || 0; // supports short notation, e.g. "9" instead of "9.0.0"
|
||||
v[2] = parseInt(v[2], 10) || 0;
|
||||
return (pv[0] > v[0] || (pv[0] == v[0] && pv[1] > v[1]) || (pv[0] == v[0] && pv[1] == v[1] && pv[2] >= v[2])) ? true : false;
|
||||
}
|
||||
|
||||
/* Cross-browser dynamic CSS creation
|
||||
- Based on Bobby van der Sluis' solution: http://www.bobbyvandersluis.com/articles/dynamicCSS.php
|
||||
*/
|
||||
function createCSS(sel, decl, media, newStyle) {
|
||||
if (ua.ie && ua.mac) { return; }
|
||||
var h = doc.getElementsByTagName("head")[0];
|
||||
if (!h) { return; } // to also support badly authored HTML pages that lack a head element
|
||||
var m = (media && typeof media == "string") ? media : "screen";
|
||||
if (newStyle) {
|
||||
dynamicStylesheet = null;
|
||||
dynamicStylesheetMedia = null;
|
||||
}
|
||||
if (!dynamicStylesheet || dynamicStylesheetMedia != m) {
|
||||
// create dynamic stylesheet + get a global reference to it
|
||||
var s = createElement("style");
|
||||
s.setAttribute("type", "text/css");
|
||||
s.setAttribute("media", m);
|
||||
dynamicStylesheet = h.appendChild(s);
|
||||
if (ua.ie && ua.win && typeof doc.styleSheets != UNDEF && doc.styleSheets.length > 0) {
|
||||
dynamicStylesheet = doc.styleSheets[doc.styleSheets.length - 1];
|
||||
}
|
||||
dynamicStylesheetMedia = m;
|
||||
}
|
||||
// add style rule
|
||||
if (ua.ie && ua.win) {
|
||||
if (dynamicStylesheet && typeof dynamicStylesheet.addRule == OBJECT) {
|
||||
dynamicStylesheet.addRule(sel, decl);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (dynamicStylesheet && typeof doc.createTextNode != UNDEF) {
|
||||
dynamicStylesheet.appendChild(doc.createTextNode(sel + " {" + decl + "}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setVisibility(id, isVisible) {
|
||||
if (!autoHideShow) { return; }
|
||||
var v = isVisible ? "visible" : "hidden";
|
||||
if (isDomLoaded && getElementById(id)) {
|
||||
getElementById(id).style.visibility = v;
|
||||
}
|
||||
else {
|
||||
createCSS("#" + id, "visibility:" + v);
|
||||
}
|
||||
}
|
||||
|
||||
/* Filter to avoid XSS attacks
|
||||
*/
|
||||
function urlEncodeIfNecessary(s) {
|
||||
var regex = /[\\\"<>\.;]/;
|
||||
var hasBadChars = regex.exec(s) != null;
|
||||
return hasBadChars && typeof encodeURIComponent != UNDEF ? encodeURIComponent(s) : s;
|
||||
}
|
||||
|
||||
/* Release memory to avoid memory leaks caused by closures, fix hanging audio/video threads and force open sockets/NetConnections to disconnect (Internet Explorer only)
|
||||
*/
|
||||
var cleanup = function() {
|
||||
if (ua.ie && ua.win) {
|
||||
window.attachEvent("onunload", function() {
|
||||
// remove listeners to avoid memory leaks
|
||||
var ll = listenersArr.length;
|
||||
for (var i = 0; i < ll; i++) {
|
||||
listenersArr[i][0].detachEvent(listenersArr[i][1], listenersArr[i][2]);
|
||||
}
|
||||
// cleanup dynamically embedded objects to fix audio/video threads and force open sockets and NetConnections to disconnect
|
||||
var il = objIdArr.length;
|
||||
for (var j = 0; j < il; j++) {
|
||||
removeSWF(objIdArr[j]);
|
||||
}
|
||||
// cleanup library's main closures to avoid memory leaks
|
||||
for (var k in ua) {
|
||||
ua[k] = null;
|
||||
}
|
||||
ua = null;
|
||||
for (var l in swfobject) {
|
||||
swfobject[l] = null;
|
||||
}
|
||||
swfobject = null;
|
||||
});
|
||||
}
|
||||
}();
|
||||
|
||||
return {
|
||||
/* Public API
|
||||
- Reference: http://code.google.com/p/swfobject/wiki/documentation
|
||||
*/
|
||||
registerObject: function(objectIdStr, swfVersionStr, xiSwfUrlStr, callbackFn) {
|
||||
if (ua.w3 && objectIdStr && swfVersionStr) {
|
||||
var regObj = {};
|
||||
regObj.id = objectIdStr;
|
||||
regObj.swfVersion = swfVersionStr;
|
||||
regObj.expressInstall = xiSwfUrlStr;
|
||||
regObj.callbackFn = callbackFn;
|
||||
regObjArr[regObjArr.length] = regObj;
|
||||
setVisibility(objectIdStr, false);
|
||||
}
|
||||
else if (callbackFn) {
|
||||
callbackFn({success:false, id:objectIdStr});
|
||||
}
|
||||
},
|
||||
|
||||
getObjectById: function(objectIdStr) {
|
||||
if (ua.w3) {
|
||||
return getObjectById(objectIdStr);
|
||||
}
|
||||
},
|
||||
|
||||
embedSWF: function(swfUrlStr, replaceElemIdStr, widthStr, heightStr, swfVersionStr, xiSwfUrlStr, flashvarsObj, parObj, attObj, callbackFn) {
|
||||
var callbackObj = {success:false, id:replaceElemIdStr};
|
||||
if (ua.w3 && !(ua.wk && ua.wk < 312) && swfUrlStr && replaceElemIdStr && widthStr && heightStr && swfVersionStr) {
|
||||
setVisibility(replaceElemIdStr, false);
|
||||
addDomLoadEvent(function() {
|
||||
widthStr += ""; // auto-convert to string
|
||||
heightStr += "";
|
||||
var att = {};
|
||||
if (attObj && typeof attObj === OBJECT) {
|
||||
for (var i in attObj) { // copy object to avoid the use of references, because web authors often reuse attObj for multiple SWFs
|
||||
att[i] = attObj[i];
|
||||
}
|
||||
}
|
||||
att.data = swfUrlStr;
|
||||
att.width = widthStr;
|
||||
att.height = heightStr;
|
||||
var par = {};
|
||||
if (parObj && typeof parObj === OBJECT) {
|
||||
for (var j in parObj) { // copy object to avoid the use of references, because web authors often reuse parObj for multiple SWFs
|
||||
par[j] = parObj[j];
|
||||
}
|
||||
}
|
||||
if (flashvarsObj && typeof flashvarsObj === OBJECT) {
|
||||
for (var k in flashvarsObj) { // copy object to avoid the use of references, because web authors often reuse flashvarsObj for multiple SWFs
|
||||
if (typeof par.flashvars != UNDEF) {
|
||||
par.flashvars += "&" + k + "=" + flashvarsObj[k];
|
||||
}
|
||||
else {
|
||||
par.flashvars = k + "=" + flashvarsObj[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hasPlayerVersion(swfVersionStr)) { // create SWF
|
||||
var obj = createSWF(att, par, replaceElemIdStr);
|
||||
if (att.id == replaceElemIdStr) {
|
||||
setVisibility(replaceElemIdStr, true);
|
||||
}
|
||||
callbackObj.success = true;
|
||||
callbackObj.ref = obj;
|
||||
}
|
||||
else if (xiSwfUrlStr && canExpressInstall()) { // show Adobe Express Install
|
||||
att.data = xiSwfUrlStr;
|
||||
showExpressInstall(att, par, replaceElemIdStr, callbackFn);
|
||||
return;
|
||||
}
|
||||
else { // show alternative content
|
||||
setVisibility(replaceElemIdStr, true);
|
||||
}
|
||||
if (callbackFn) { callbackFn(callbackObj); }
|
||||
});
|
||||
}
|
||||
else if (callbackFn) { callbackFn(callbackObj); }
|
||||
},
|
||||
|
||||
switchOffAutoHideShow: function() {
|
||||
autoHideShow = false;
|
||||
},
|
||||
|
||||
ua: ua,
|
||||
|
||||
getFlashPlayerVersion: function() {
|
||||
return { major:ua.pv[0], minor:ua.pv[1], release:ua.pv[2] };
|
||||
},
|
||||
|
||||
hasFlashPlayerVersion: hasPlayerVersion,
|
||||
|
||||
createSWF: function(attObj, parObj, replaceElemIdStr) {
|
||||
if (ua.w3) {
|
||||
return createSWF(attObj, parObj, replaceElemIdStr);
|
||||
}
|
||||
else {
|
||||
return undefined;
|
||||
}
|
||||
},
|
||||
|
||||
showExpressInstall: function(att, par, replaceElemIdStr, callbackFn) {
|
||||
if (ua.w3 && canExpressInstall()) {
|
||||
showExpressInstall(att, par, replaceElemIdStr, callbackFn);
|
||||
}
|
||||
},
|
||||
|
||||
removeSWF: function(objElemIdStr) {
|
||||
if (ua.w3) {
|
||||
removeSWF(objElemIdStr);
|
||||
}
|
||||
},
|
||||
|
||||
createCSS: function(selStr, declStr, mediaStr, newStyleBoolean) {
|
||||
if (ua.w3) {
|
||||
createCSS(selStr, declStr, mediaStr, newStyleBoolean);
|
||||
}
|
||||
},
|
||||
|
||||
addDomLoadEvent: addDomLoadEvent,
|
||||
|
||||
addLoadEvent: addLoadEvent,
|
||||
|
||||
getQueryParamValue: function(param) {
|
||||
var q = doc.location.search || doc.location.hash;
|
||||
if (q) {
|
||||
if (/\?/.test(q)) { q = q.split("?")[1]; } // strip question mark
|
||||
if (param == null) {
|
||||
return urlEncodeIfNecessary(q);
|
||||
}
|
||||
var pairs = q.split("&");
|
||||
for (var i = 0; i < pairs.length; i++) {
|
||||
if (pairs[i].substring(0, pairs[i].indexOf("=")) == param) {
|
||||
return urlEncodeIfNecessary(pairs[i].substring((pairs[i].indexOf("=") + 1)));
|
||||
}
|
||||
}
|
||||
}
|
||||
return "";
|
||||
},
|
||||
|
||||
// For internal usage only
|
||||
expressInstallCallback: function() {
|
||||
if (isExpressInstallActive) {
|
||||
var obj = getElementById(EXPRESS_INSTALL_ID);
|
||||
if (obj && storedAltContent) {
|
||||
obj.parentNode.replaceChild(storedAltContent, obj);
|
||||
if (storedAltContentId) {
|
||||
setVisibility(storedAltContentId, true);
|
||||
if (ua.ie && ua.win) { storedAltContent.style.display = "block"; }
|
||||
}
|
||||
if (storedCallbackFn) { storedCallbackFn(storedCallbackObj); }
|
||||
}
|
||||
isExpressInstallActive = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
}();
|
47
trunk/research/players/srs_bwt.html
Executable file
47
trunk/research/players/srs_bwt.html
Executable file
|
@ -0,0 +1,47 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>SRS</title>
|
||||
<meta charset="utf-8">
|
||||
<link rel="stylesheet" type="text/css" href="css/bootstrap.min.css"/>
|
||||
<script type="text/javascript" src="js/jquery-1.10.2.min.js"></script>
|
||||
<script type="text/javascript" src="js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="js/swfobject.js"></script>
|
||||
<script type="text/javascript" src="js/srs.js"></script>
|
||||
<style>
|
||||
body{
|
||||
padding-top: 55px;
|
||||
}
|
||||
</style>
|
||||
<script type="text/javascript">
|
||||
$(function(){
|
||||
update_nav();
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="navbar navbar-fixed-top">
|
||||
<div class="navbar-inner">
|
||||
<div class="container">
|
||||
<a class="brand" href="index.html">SRS</a>
|
||||
<div class="nav-collapse collapse">
|
||||
<ul class="nav">
|
||||
<li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li>
|
||||
<li><a id="nav_srs_publisher" href="srs_publisher.html">SRS编码器</a></li>
|
||||
<li class="active"><a id="nav_srs_bwt" href="srs_bwt.html">SRS测网速</a></li>
|
||||
<li><a id="nav_jwplayer6" href="jwplayer6.html">JWPlayer6播放器</a></li>
|
||||
<li><a id="nav_osmf" href="osmf.html">AdobeOSMF播放器</a></li>
|
||||
<li><a id="nav_vlc" href="vlc.html">VLC播放器</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<hr>
|
||||
<footer>
|
||||
<p><a href="https://github.com/winlinvip/simple-rtmp-server">SRS Team © 2013</a></p>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
|
523
trunk/research/players/srs_player.html
Executable file
523
trunk/research/players/srs_player.html
Executable file
|
@ -0,0 +1,523 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>SRS</title>
|
||||
<meta charset="utf-8">
|
||||
<link rel="stylesheet" type="text/css" href="css/bootstrap.min.css"/>
|
||||
<script type="text/javascript" src="js/jquery-1.10.2.min.js"></script>
|
||||
<script type="text/javascript" src="js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="js/swfobject.js"></script>
|
||||
<script type="text/javascript" src="js/srs.js"></script>
|
||||
<style>
|
||||
body{
|
||||
padding-top: 55px;
|
||||
}
|
||||
#my_modal_footer {
|
||||
margin-top: -20px;
|
||||
padding-top: 3px;
|
||||
}
|
||||
#div_play_time {
|
||||
margin-top: 10px;
|
||||
}
|
||||
#pb_buffer_bg {
|
||||
margin-top: -4px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
<script type="text/javascript">
|
||||
var srs_player = null;
|
||||
var url = null;
|
||||
|
||||
var __active_dar = null;
|
||||
function select_dar(dar_id, num, den) {
|
||||
srs_player.dar(num, den);
|
||||
|
||||
if (__active_dar) {
|
||||
__active_dar.removeClass("active");
|
||||
}
|
||||
|
||||
__active_dar = $(dar_id).parent();
|
||||
__active_dar.addClass("active");
|
||||
}
|
||||
|
||||
var __active_size = null;
|
||||
function select_fs_size(size_id, refer, percent) {
|
||||
srs_player.set_fs(refer, percent);
|
||||
|
||||
if (__active_size) {
|
||||
__active_size.removeClass("active");
|
||||
}
|
||||
|
||||
__active_size = $(size_id).parent();
|
||||
__active_size.addClass("active");
|
||||
}
|
||||
|
||||
var __active_bt = null;
|
||||
function select_buffer_time(bt_id, buffer_time) {
|
||||
srs_player.set_bt(buffer_time);
|
||||
|
||||
if (__active_bt) {
|
||||
__active_bt.removeClass("active");
|
||||
}
|
||||
|
||||
__active_bt = $(bt_id).parent();
|
||||
__active_bt.addClass("active");
|
||||
}
|
||||
|
||||
$(function(){
|
||||
// get the vhost and port to set the default url.
|
||||
// for example: http://192.168.1.213/players/jwplayer6.html?port=1935&vhost=demo
|
||||
// url set to: rtmp://demo:1935/live/livestream
|
||||
srs_init("#txt_url", null, "#main_modal");
|
||||
|
||||
$("#fs_tips").tooltip({
|
||||
title: "点击视频进入或退出全屏"
|
||||
});
|
||||
|
||||
$("#main_modal").on("show", function(){
|
||||
if (srs_player) {
|
||||
return;
|
||||
}
|
||||
|
||||
$("#div_container").remove();
|
||||
|
||||
var div_container = $("<div/>");
|
||||
$(div_container).attr("id", "div_container");
|
||||
$("#player").append(div_container);
|
||||
|
||||
var player = $("<div/>");
|
||||
$(player).attr("id", "player_id");
|
||||
$(div_container).append(player);
|
||||
|
||||
srs_player = new SrsPlayer("player_id", srs_get_player_width(), srs_get_player_height());
|
||||
srs_player.on_player_ready = function() {
|
||||
select_buffer_time("#btn_bt_0_8", 0.8);
|
||||
srs_player.play(url);
|
||||
};
|
||||
srs_player.on_player_metadata = function(metadata) {
|
||||
$("#btn_dar_original").text("视频原始比例" + "(" + metadata.width + ":" + metadata.height + ")");
|
||||
select_dar("#btn_dar_original", 0, 0);
|
||||
select_fs_size("#btn_fs_size_screen_100", "screen", 100);
|
||||
};
|
||||
srs_player.on_player_timer = function(time, buffer_length) {
|
||||
var buffer = buffer_length / srs_player.buffer_time * 100;
|
||||
$("#pb_buffer").width(Number(buffer).toFixed(1) + "%");
|
||||
|
||||
$("#pb_buffer_bg").attr("title",
|
||||
"缓冲区长度:" + Number(buffer_length).toFixed(1) + "秒("
|
||||
+ Number(buffer).toFixed(1) + "%)");
|
||||
|
||||
var time_str = "";
|
||||
// day
|
||||
time_str = padding(parseInt(time / 24 / 3600), 2, '0') + " ";
|
||||
// hour
|
||||
time = time % (24 * 3600);
|
||||
time_str += padding(parseInt(time / 3600), 2, '0') + ":";
|
||||
// minute
|
||||
time = time % (3600);
|
||||
time_str += padding(parseInt(time / 60), 2, '0') + ":";
|
||||
// seconds
|
||||
time = time % (60);
|
||||
time_str += padding(parseInt(time), 2, '0');
|
||||
// show
|
||||
$("#txt_time").val(time_str);
|
||||
};
|
||||
srs_player.start();
|
||||
});
|
||||
|
||||
$("#main_modal").on("hide", function(){
|
||||
if ($("#main_modal").is(":visible")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (srs_player) {
|
||||
srs_player.stop();
|
||||
srs_player = null;
|
||||
}
|
||||
});
|
||||
|
||||
$("#btn_play").click(function(){
|
||||
url = $("#txt_url").val();
|
||||
$("#main_modal").modal({show:true, keyboard:false});
|
||||
});
|
||||
|
||||
$("#btn_pause").click(function(){
|
||||
if ($("#btn_pause").text() == "暂停") {
|
||||
$("#btn_pause").text("继续");
|
||||
srs_player.pause();
|
||||
} else {
|
||||
$("#btn_pause").text("暂停");
|
||||
srs_player.resume();
|
||||
}
|
||||
});
|
||||
|
||||
$("#srs_publish").click(function(){
|
||||
url = $("#srs_publish").text();
|
||||
$("#main_modal").modal({show:true, keyboard:false});
|
||||
});
|
||||
$("#srs_publish_ld").click(function(){
|
||||
url = $("#srs_publish_ld").text();
|
||||
$("#main_modal").modal({show:true, keyboard:false});
|
||||
});
|
||||
$("#srs_publish_sd").click(function(){
|
||||
url = $("#srs_publish_sd").text();
|
||||
$("#main_modal").modal({show:true, keyboard:false});
|
||||
});
|
||||
$("#srs_publish_fw").click(function(){
|
||||
url = $("#srs_publish_fw").text();
|
||||
$("#main_modal").modal({show:true, keyboard:false});
|
||||
});
|
||||
$("#srs_publish_fw_ld").click(function(){
|
||||
url = $("#srs_publish_fw_ld").text();
|
||||
$("#main_modal").modal({show:true, keyboard:false});
|
||||
});
|
||||
$("#srs_publish_fw_sd").click(function(){
|
||||
url = $("#srs_publish_fw_sd").text();
|
||||
$("#main_modal").modal({show:true, keyboard:false});
|
||||
});
|
||||
|
||||
var query = parse_query_string();
|
||||
var jwplayer_url = "http://" + query.host + query.dir + "/jwplayer6.html?vhost=demo.srs.com&app=live&hls_autostart=true";
|
||||
$("#srs_publish_hls").attr("href", jwplayer_url + "&stream=livestream");
|
||||
$("#srs_publish_ld_hls").attr("href", jwplayer_url + "&stream=livestream_ld");
|
||||
$("#srs_publish_sd_hls").attr("href", jwplayer_url + "&stream=livestream_sd");
|
||||
var jwplayer_url = "http://" + query.host + query.dir + "/jwplayer6.html?vhost=demo.srs.com&app=forward/live&hls_autostart=true";
|
||||
$("#srs_publish_fw_hls").attr("href", jwplayer_url + "&stream=livestream");
|
||||
$("#srs_publish_fw_ld_hls").attr("href", jwplayer_url + "&stream=livestream_ld");
|
||||
$("#srs_publish_fw_sd_hls").attr("href", jwplayer_url + "&stream=livestream_sd");
|
||||
|
||||
if (true) {
|
||||
$("#btn_dar_original").click(function(){
|
||||
select_dar("#btn_dar_original", 0, 0);
|
||||
});
|
||||
$("#btn_dar_21_9").click(function(){
|
||||
select_dar("#btn_dar_21_9", 9, 21);
|
||||
});
|
||||
$("#btn_dar_16_9").click(function(){
|
||||
select_dar("#btn_dar_16_9", 9, 16);
|
||||
});
|
||||
$("#btn_dar_4_3").click(function(){
|
||||
select_dar("#btn_dar_4_3", 3, 4);
|
||||
});
|
||||
$("#btn_dar_fill").click(function(){
|
||||
select_dar("#btn_dar_fill", -1, -1);
|
||||
});
|
||||
}
|
||||
|
||||
if (true) {
|
||||
$("#btn_fs_size_video_100").click(function(){
|
||||
select_fs_size("#btn_fs_size_video_100", "video", 100);
|
||||
});
|
||||
$("#btn_fs_size_video_75").click(function(){
|
||||
select_fs_size("#btn_fs_size_video_75", "video", 75);
|
||||
});
|
||||
$("#btn_fs_size_video_50").click(function(){
|
||||
select_fs_size("#btn_fs_size_video_50", "video", 50);
|
||||
});
|
||||
$("#btn_fs_size_screen_100").click(function(){
|
||||
select_fs_size("#btn_fs_size_screen_100", "screen", 100);
|
||||
});
|
||||
$("#btn_fs_size_screen_75").click(function(){
|
||||
select_fs_size("#btn_fs_size_screen_75", "screen", 75);
|
||||
});
|
||||
$("#btn_fs_size_screen_50").click(function(){
|
||||
select_fs_size("#btn_fs_size_screen_50", "screen", 50);
|
||||
});
|
||||
}
|
||||
|
||||
if (true) {
|
||||
$("#btn_bt_0_5").click(function(){
|
||||
select_buffer_time("#btn_bt_0_5", 0.5);
|
||||
});
|
||||
$("#btn_bt_0_8").click(function(){
|
||||
select_buffer_time("#btn_bt_0_8", 0.8);
|
||||
});
|
||||
$("#btn_bt_1").click(function(){
|
||||
select_buffer_time("#btn_bt_1", 1);
|
||||
});
|
||||
$("#btn_bt_2").click(function(){
|
||||
select_buffer_time("#btn_bt_2", 2);
|
||||
});
|
||||
$("#btn_bt_3").click(function(){
|
||||
select_buffer_time("#btn_bt_3", 3);
|
||||
});
|
||||
$("#btn_bt_5").click(function(){
|
||||
select_buffer_time("#btn_bt_5", 5);
|
||||
});
|
||||
$("#btn_bt_10").click(function(){
|
||||
select_buffer_time("#btn_bt_10", 10);
|
||||
});
|
||||
$("#btn_bt_30").click(function(){
|
||||
select_buffer_time("#btn_bt_30", 30);
|
||||
});
|
||||
}
|
||||
|
||||
var query = parse_query_string();
|
||||
if (query.autostart == "true") {
|
||||
url = $("#txt_url").val();
|
||||
$("#main_modal").modal({show:true, keyboard:false});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="navbar navbar-fixed-top">
|
||||
<div class="navbar-inner">
|
||||
<div class="container">
|
||||
<a class="brand" href="index.html">SRS</a>
|
||||
<div class="nav-collapse collapse">
|
||||
<ul class="nav">
|
||||
<li class="active"><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li>
|
||||
<li><a id="nav_srs_publisher" href="srs_publisher.html">SRS编码器</a></li>
|
||||
<li><a id="nav_srs_bwt" href="srs_bwt.html">SRS测网速</a></li>
|
||||
<li><a id="nav_jwplayer6" href="jwplayer6.html">JWPlayer6播放器</a></li>
|
||||
<li><a id="nav_osmf" href="osmf.html">AdobeOSMF播放器</a></li>
|
||||
<li><a id="nav_vlc" href="vlc.html">VLC播放器</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="alert alert-info fade in">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
<strong><span>Usage:</span></strong> <span>输入RTMP/HTTP地址后点击“播放视频”即可播放视频</span>
|
||||
</div>
|
||||
<div class="form-inline">
|
||||
URL:
|
||||
<input type="text" id="txt_url" class="input-xxlarge" value=""></input>
|
||||
<button class="btn btn-primary" id="btn_play">播放视频</button>
|
||||
</div>
|
||||
<div class="container">
|
||||
<hr/>
|
||||
<span>
|
||||
注意:必须按照<a href="https://github.com/winlinvip/simple-rtmp-server/blob/master/README.md">SRS README.md</a>
|
||||
中的11个Step做完,下面所有的链接才能观看。
|
||||
</span>
|
||||
<div class="accordion" id="main_accordion">
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<span class="accordion-toggle" data-toggle="collapse" data-parent="#main_accordion" href="#collapse1">
|
||||
<strong>[1] SRS示例播放流: 原始流</strong>
|
||||
</span>
|
||||
</div>
|
||||
<div id="collapse1" class="accordion-body collapse">
|
||||
<div class="accordion-inner">
|
||||
<a href="#" id="srs_publish">rtmp://demo.srs.com/live/livestream</a> <br/>
|
||||
<span>用户推送过来的唯一一路流,经过服务器的多种变换和再转发。</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<span class="accordion-toggle" data-toggle="collapse" data-parent="#main_accordion" href="#collapse10">
|
||||
<strong>[2] SRS示例播放流: 原始流HLS</strong>
|
||||
</span>
|
||||
</div>
|
||||
<div id="collapse10" class="accordion-body collapse">
|
||||
<div class="accordion-inner">
|
||||
<a href="#" id="srs_publish_hls">http://demo.srs.com/live/livestream.m3u8</a> <br/>
|
||||
<span>对用户的流进行HLS切片(若编码为非H264/AAC,HLS流会自动禁用)。</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<span class="accordion-toggle" data-toggle="collapse" data-parent="#main_accordion" href="#collapse2">
|
||||
<strong>[3] SRS示例播放流: 转码配置LD流</strong>
|
||||
</span>
|
||||
</div>
|
||||
<div id="collapse2" class="accordion-body collapse">
|
||||
<div class="accordion-inner">
|
||||
<a href="#" id="srs_publish_ld">rtmp://demo.srs.com/live/livestream_ld</a> <br/>
|
||||
<span>对原始流加了<a href="http://ffmpeg.org/ffmpeg-filters.html#drawtext-1">FFMPEG文字水印</a></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<span class="accordion-toggle" data-toggle="collapse" data-parent="#main_accordion" href="#collapse11">
|
||||
<strong>[4] SRS示例播放流: 转码配置LD流HLS</strong>
|
||||
</span>
|
||||
</div>
|
||||
<div id="collapse11" class="accordion-body collapse">
|
||||
<div class="accordion-inner">
|
||||
<a href="#" id="srs_publish_ld_hls">http://demo.srs.com/live/livestream_ld.m3u8</a> <br/>
|
||||
<span>对转码配置LD流进行HLS切片。</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<span class="accordion-toggle" data-toggle="collapse" data-parent="#main_accordion" href="#collapse3">
|
||||
<strong>[5] SRS示例播放流: 转码配置SD流</strong>
|
||||
</span>
|
||||
</div>
|
||||
<div id="collapse3" class="accordion-body collapse">
|
||||
<div class="accordion-inner">
|
||||
<a href="#" id="srs_publish_sd">rtmp://demo.srs.com/live/livestream_sd</a> <br/>
|
||||
<span>对原始流应用了<a href="http://ffmpeg.org/ffmpeg-filters.html#Filtering-Introduction">FFMPEG翻转滤镜</a></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<span class="accordion-toggle" data-toggle="collapse" data-parent="#main_accordion" href="#collapse12">
|
||||
<strong>[6] SRS示例播放流: 转码配置SD流HLS</strong>
|
||||
</span>
|
||||
</div>
|
||||
<div id="collapse12" class="accordion-body collapse">
|
||||
<div class="accordion-inner">
|
||||
<a href="#" id="srs_publish_sd_hls">http://demo.srs.com/live/livestream_sd.m3u8</a> <br/>
|
||||
<span>对转码配置SD流进行HLS切片。</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<span class="accordion-toggle" data-toggle="collapse" data-parent="#main_accordion" href="#collapse4">
|
||||
<strong>[7] SRS示例播放流: 转发原始流</strong>
|
||||
</span>
|
||||
</div>
|
||||
<div id="collapse4" class="accordion-body collapse">
|
||||
<div class="accordion-inner">
|
||||
<a href="#" id="srs_publish_fw">rtmp://demo.srs.com:19350/live/livestream</a> <br/>
|
||||
<span>将用户推送的流转发到另外的vhost或服务器,做热备用。</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<span class="accordion-toggle" data-toggle="collapse" data-parent="#main_accordion" href="#collapse13">
|
||||
<strong>[8] SRS示例播放流: 转发原始流HLS</strong>
|
||||
</span>
|
||||
</div>
|
||||
<div id="collapse13" class="accordion-body collapse">
|
||||
<div class="accordion-inner">
|
||||
<a href="#" id="srs_publish_fw_hls">http://demo.srs.com/forward/live/livestream.m3u8</a> <br/>
|
||||
<span>对转发原始流进行HLS切片(若编码为非H264/AAC,HLS流会自动禁用)。</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<span class="accordion-toggle" data-toggle="collapse" data-parent="#main_accordion" href="#collapse5">
|
||||
<strong>[9] SRS示例播放流: 转发转码配置LD流</strong>
|
||||
</span>
|
||||
</div>
|
||||
<div id="collapse5" class="accordion-body collapse">
|
||||
<div class="accordion-inner">
|
||||
<a href="#" id="srs_publish_fw_ld">rtmp://demo.srs.com:19350/live/livestream_ld</a> <br/>
|
||||
<span>FFMPEG加水印后的流也会自动转发。</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<span class="accordion-toggle" data-toggle="collapse" data-parent="#main_accordion" href="#collapse14">
|
||||
<strong>[10] SRS示例播放流: 转发转码配置LD流HLS</strong>
|
||||
</span>
|
||||
</div>
|
||||
<div id="collapse14" class="accordion-body collapse">
|
||||
<div class="accordion-inner">
|
||||
<a href="#" id="srs_publish_fw_ld_hls">http://demo.srs.com/forward/live/livestream_ld.m3u8</a> <br/>
|
||||
<span>对转发转码配置LD流进行HLS切片,所有转发的流会自动支持HLS。</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<span class="accordion-toggle" data-toggle="collapse" data-parent="#main_accordion" href="#collapse6">
|
||||
<strong>[11] SRS示例播放流: 转发转码配置SD流</strong>
|
||||
</span>
|
||||
</div>
|
||||
<div id="collapse6" class="accordion-body collapse">
|
||||
<div class="accordion-inner">
|
||||
<a href="#" id="srs_publish_fw_sd">rtmp://demo.srs.com:19350/live/livestream_sd</a> <br/>
|
||||
<span>FFMPEG翻转后的流也会自动转发。</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<span class="accordion-toggle" data-toggle="collapse" data-parent="#main_accordion" href="#collapse15">
|
||||
<strong>[12] SRS示例播放流: 转发转码配置SD流HLS</strong>
|
||||
</span>
|
||||
</div>
|
||||
<div id="collapse15" class="accordion-body collapse">
|
||||
<div class="accordion-inner">
|
||||
<a href="#" id="srs_publish_fw_sd_hls">http://demo.srs.com/forward/live/livestream_sd.m3u8</a> <br/>
|
||||
<span>对转发转码配置SD流进行HLS切片,所有转发的流会自动支持HLS。</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="main_modal" class="modal hide fade">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3><a href="https://github.com/winlinvip/simple-rtmp-server">SrsPlayer</a></h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div id="player"></div>
|
||||
<div class="progress progress-striped active" id="pb_buffer_bg">
|
||||
<div class="bar" style="width: 0%;" id="pb_buffer"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer" id="my_modal_footer">
|
||||
<div class="input-prepend" id="div_play_time">
|
||||
<span class="add-on" title="播放时长">@T</span>
|
||||
<input class="span2" style="width:85px" id="txt_time" type="text" placeholder="天 时:分:秒">
|
||||
</div>
|
||||
<div class="btn-group dropup">
|
||||
<button class="btn dropdown-toggle" data-toggle="dropdown">
|
||||
<a id="fs_tips" href="#" data-toggle="tooltip" data-placement="top" title="">
|
||||
<img src="img/tooltip.png"/>
|
||||
</a>
|
||||
全屏大小<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a id="btn_fs_size_screen_100" href="#">屏幕大小(100%)</a></li>
|
||||
<li><a id="btn_fs_size_screen_75" href="#">屏幕大小(75%)</a></li>
|
||||
<li><a id="btn_fs_size_screen_50" href="#">屏幕大小(50%)</a></li>
|
||||
<li><a id="btn_fs_size_video_100" href="#">视频大小(100%)</a></li>
|
||||
<li><a id="btn_fs_size_video_75" href="#">视频大小(75%)</a></li>
|
||||
<li><a id="btn_fs_size_video_50" href="#">视频大小(50%)</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="btn-group dropup">
|
||||
<button class="btn dropdown-toggle" data-toggle="dropdown">显示比例<span class="caret"></span></button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a id="btn_dar_original" href="#">视频原始比例</a></li>
|
||||
<li><a id="btn_dar_21_9" href="#">宽屏影院(21:9)</a></li>
|
||||
<li><a id="btn_dar_16_9" href="#">宽屏电视(16:9)</a></li>
|
||||
<li><a id="btn_dar_4_3" href="#">窄屏(4:3)</a></li>
|
||||
<li><a id="btn_dar_fill" href="#">填充(容器比例)</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="btn-group dropup">
|
||||
<button class="btn dropdown-toggle" data-toggle="dropdown">缓冲区<span class="caret"></span></button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a id="btn_bt_0_5" href="#">0.5秒(实时)</a></li>
|
||||
<li><a id="btn_bt_0_8" href="#">0.8秒(会议)</a></li>
|
||||
<li><a id="btn_bt_1" href="#">1秒(低延迟)</a></li>
|
||||
<li><a id="btn_bt_2" href="#">2秒(较低延时)</a></li>
|
||||
<li><a id="btn_bt_3" href="#">3秒(流畅播放)</a></li>
|
||||
<li><a id="btn_bt_5" href="#">5秒(网速较低)</a></li>
|
||||
<li><a id="btn_bt_10" href="#">10秒(无所谓延迟)</a></li>
|
||||
<li><a id="btn_bt_30" href="#">30秒(流畅第一)</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="btn-group dropup">
|
||||
<button id="btn_pause" class="btn">暂停</button>
|
||||
</div>
|
||||
<div class="btn-group dropup">
|
||||
<button class="btn btn-primary" data-dismiss="modal" aria-hidden="true">关闭</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>
|
||||
<p><a href="https://github.com/winlinvip/simple-rtmp-server">SRS Team © 2013</a></p>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
40
trunk/research/players/srs_player/.actionScriptProperties
Executable file
40
trunk/research/players/srs_player/.actionScriptProperties
Executable file
|
@ -0,0 +1,40 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<actionScriptProperties analytics="false" mainApplicationPath="srs_player.as" projectUUID="1bb41a0e-6b1f-49b5-8603-219442f9f9b3" version="10">
|
||||
<compiler additionalCompilerArguments="-locale en_US" autoRSLOrdering="true" copyDependentFiles="false" fteInMXComponents="false" generateAccessible="true" htmlExpressInstall="true" htmlGenerate="false" htmlHistoryManagement="true" htmlPlayerVersionCheck="true" includeNetmonSwc="false" outputFolderPath="release" removeUnusedRSL="true" sourceFolderPath="src" strict="true" targetPlayerVersion="0.0.0" useApolloConfig="false" useDebugRSLSwfs="true" verifyDigests="true" warn="true">
|
||||
<compilerSourcePath/>
|
||||
<libraryPath defaultLinkType="0">
|
||||
<libraryPathEntry kind="4" path="">
|
||||
<excludedEntries>
|
||||
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/automation_charts.swc" useDefaultLinkType="false"/>
|
||||
<libraryPathEntry kind="1" linkType="1" path="${PROJECT_FRAMEWORKS}/locale/{locale}"/>
|
||||
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/advancedgrids.swc" useDefaultLinkType="false"/>
|
||||
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/qtp.swc" useDefaultLinkType="false"/>
|
||||
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/automation_air.swc" useDefaultLinkType="false"/>
|
||||
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/charts.swc" useDefaultLinkType="false"/>
|
||||
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/framework.swc" useDefaultLinkType="false"/>
|
||||
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/mx/mx.swc" useDefaultLinkType="false"/>
|
||||
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/netmon.swc" useDefaultLinkType="false"/>
|
||||
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/spark.swc" useDefaultLinkType="false"/>
|
||||
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/sparkskins.swc" useDefaultLinkType="false"/>
|
||||
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/rpc.swc" useDefaultLinkType="false"/>
|
||||
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/videoPlayer.swc" useDefaultLinkType="false"/>
|
||||
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/qtp_air.swc" useDefaultLinkType="false"/>
|
||||
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/datavisualization.swc" useDefaultLinkType="false"/>
|
||||
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/spark_dmv.swc" useDefaultLinkType="false"/>
|
||||
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/automation.swc" useDefaultLinkType="false"/>
|
||||
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/flash-integration.swc" useDefaultLinkType="false"/>
|
||||
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/automation_dmv.swc" useDefaultLinkType="false"/>
|
||||
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/automation_flashflexkit.swc" useDefaultLinkType="false"/>
|
||||
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/automation_agent.swc" useDefaultLinkType="false"/>
|
||||
</excludedEntries>
|
||||
</libraryPathEntry>
|
||||
</libraryPath>
|
||||
<sourceAttachmentPath/>
|
||||
</compiler>
|
||||
<applications>
|
||||
<application path="srs_player.as"/>
|
||||
</applications>
|
||||
<modules/>
|
||||
<buildCSSFiles/>
|
||||
<flashCatalyst validateFlashCatalystCompatibility="false"/>
|
||||
</actionScriptProperties>
|
17
trunk/research/players/srs_player/.project
Executable file
17
trunk/research/players/srs_player/.project
Executable file
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>srs_player</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>com.adobe.flexbuilder.project.flexbuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>com.adobe.flexbuilder.project.actionscriptnature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
|
@ -0,0 +1,3 @@
|
|||
#Wed Dec 18 10:07:19 CST 2013
|
||||
eclipse.preferences.version=1
|
||||
encoding/<project>=utf-8
|
BIN
trunk/research/players/srs_player/release/srs_player.swf
Executable file
BIN
trunk/research/players/srs_player/release/srs_player.swf
Executable file
Binary file not shown.
506
trunk/research/players/srs_player/src/srs_player.as
Executable file
506
trunk/research/players/srs_player/src/srs_player.as
Executable file
|
@ -0,0 +1,506 @@
|
|||
package
|
||||
{
|
||||
import flash.display.Sprite;
|
||||
import flash.display.StageAlign;
|
||||
import flash.display.StageDisplayState;
|
||||
import flash.display.StageScaleMode;
|
||||
import flash.events.Event;
|
||||
import flash.events.FullScreenEvent;
|
||||
import flash.events.MouseEvent;
|
||||
import flash.events.NetStatusEvent;
|
||||
import flash.events.TimerEvent;
|
||||
import flash.external.ExternalInterface;
|
||||
import flash.media.Video;
|
||||
import flash.net.NetConnection;
|
||||
import flash.net.NetStream;
|
||||
import flash.system.Security;
|
||||
import flash.ui.ContextMenu;
|
||||
import flash.ui.ContextMenuItem;
|
||||
import flash.utils.Timer;
|
||||
import flash.utils.setTimeout;
|
||||
|
||||
public class srs_player extends Sprite
|
||||
{
|
||||
// user set id.
|
||||
private var js_id:String = null;
|
||||
// user set callback
|
||||
private var js_on_player_ready:String = null;
|
||||
private var js_on_player_metadata:String = null;
|
||||
private var js_on_player_timer:String = null;
|
||||
|
||||
// play param url.
|
||||
private var user_url:String = null;
|
||||
// play param, user set width and height
|
||||
private var user_w:int = 0;
|
||||
private var user_h:int = 0;
|
||||
// user set dar den:num
|
||||
private var user_dar_num:int = 0;
|
||||
private var user_dar_den:int = 0;
|
||||
// user set fs(fullscreen) refer and percent.
|
||||
private var user_fs_refer:String = null;
|
||||
private var user_fs_percent:int = 0;
|
||||
|
||||
// media specified.
|
||||
private var media_conn:NetConnection = null;
|
||||
private var media_stream:NetStream = null;
|
||||
private var media_video:Video = null;
|
||||
private var media_metadata:Object = {};
|
||||
private var media_timer:Timer = new Timer(300);
|
||||
|
||||
// controls.
|
||||
// flash donot allow js to set to fullscreen,
|
||||
// only allow user click to enter fullscreen.
|
||||
private var control_fs_mask:Sprite = new Sprite();
|
||||
|
||||
public function srs_player()
|
||||
{
|
||||
if (!this.stage) {
|
||||
this.addEventListener(Event.ADDED_TO_STAGE, this.system_on_add_to_stage);
|
||||
} else {
|
||||
this.system_on_add_to_stage(null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* system event callback, when this control added to stage.
|
||||
* the main function.
|
||||
*/
|
||||
private function system_on_add_to_stage(evt:Event):void {
|
||||
this.removeEventListener(Event.ADDED_TO_STAGE, this.system_on_add_to_stage);
|
||||
|
||||
this.stage.align = StageAlign.TOP_LEFT;
|
||||
this.stage.scaleMode = StageScaleMode.NO_SCALE;
|
||||
|
||||
this.stage.addEventListener(FullScreenEvent.FULL_SCREEN, this.user_on_stage_fullscreen);
|
||||
|
||||
this.addChild(this.control_fs_mask);
|
||||
this.control_fs_mask.buttonMode = true;
|
||||
this.control_fs_mask.addEventListener(MouseEvent.CLICK, user_on_click_video);
|
||||
|
||||
this.contextMenu = new ContextMenu();
|
||||
this.contextMenu.hideBuiltInItems();
|
||||
|
||||
var flashvars:Object = this.root.loaderInfo.parameters;
|
||||
|
||||
if (!flashvars.hasOwnProperty("id")) {
|
||||
throw new Error("must specifies the id");
|
||||
}
|
||||
|
||||
this.js_id = flashvars.id;
|
||||
this.js_on_player_ready = flashvars.on_player_ready;
|
||||
this.js_on_player_metadata = flashvars.on_player_metadata;
|
||||
this.js_on_player_timer = flashvars.on_player_timer;
|
||||
|
||||
this.media_timer.addEventListener(TimerEvent.TIMER, this.system_on_timer);
|
||||
this.media_timer.start();
|
||||
|
||||
flash.utils.setTimeout(this.system_on_js_ready, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* system callack event, when js ready, register callback for js.
|
||||
* the actual main function.
|
||||
*/
|
||||
private function system_on_js_ready():void {
|
||||
if (!flash.external.ExternalInterface.available) {
|
||||
trace("js not ready, try later.");
|
||||
flash.utils.setTimeout(this.system_on_js_ready, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
flash.external.ExternalInterface.addCallback("__play", this.js_call_play);
|
||||
flash.external.ExternalInterface.addCallback("__stop", this.js_call_stop);
|
||||
flash.external.ExternalInterface.addCallback("__pause", this.js_call_pause);
|
||||
flash.external.ExternalInterface.addCallback("__resume", this.js_call_resume);
|
||||
flash.external.ExternalInterface.addCallback("__dar", this.js_call_dar);
|
||||
flash.external.ExternalInterface.addCallback("__set_fs", this.js_call_set_fs_size);
|
||||
flash.external.ExternalInterface.addCallback("__set_bt", this.js_call_set_bt);
|
||||
|
||||
flash.external.ExternalInterface.call(this.js_on_player_ready, this.js_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* system callack event, timer to do some regular tasks.
|
||||
*/
|
||||
private function system_on_timer(evt:TimerEvent):void {
|
||||
if (!this.media_stream) {
|
||||
trace("stream is null, ignore timer event.");
|
||||
return;
|
||||
}
|
||||
|
||||
trace("notify js the timer event.");
|
||||
flash.external.ExternalInterface.call(
|
||||
this.js_on_player_timer, this.js_id, this.media_stream.time, this.media_stream.bufferLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* system callack event, when got metadata from stream.
|
||||
* or got video dimension change event(the DAR notification), to update the metadata manually.
|
||||
*/
|
||||
private function system_on_metadata(metadata:Object):void {
|
||||
this.media_metadata = metadata;
|
||||
|
||||
if (metadata.hasOwnProperty("server")) {
|
||||
// for context menu
|
||||
var customItems:Array = [new ContextMenuItem("SrsPlayer")];
|
||||
if (metadata.hasOwnProperty("server")) {
|
||||
customItems.push(new ContextMenuItem("Server: " + metadata.server));
|
||||
}
|
||||
if (metadata.hasOwnProperty("contributor")) {
|
||||
customItems.push(new ContextMenuItem("Contributor: " + metadata.contributor));
|
||||
}
|
||||
contextMenu.customItems = customItems;
|
||||
}
|
||||
|
||||
// for js.
|
||||
var obj:Object = __get_video_size_object();
|
||||
|
||||
obj.server = 'srs';
|
||||
obj.contributor = 'winlin';
|
||||
|
||||
if (metadata.hasOwnProperty("server")) {
|
||||
obj.server = metadata.server;
|
||||
}
|
||||
if (metadata.hasOwnProperty("contributor")) {
|
||||
obj.contributor = metadata.contributor;
|
||||
}
|
||||
|
||||
var code:int = flash.external.ExternalInterface.call(js_on_player_metadata, js_id, obj);
|
||||
if (code != 0) {
|
||||
throw new Error("callback on_player_metadata failed. code=" + code);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* player callack event, when user click video to enter or leave fullscreen.
|
||||
*/
|
||||
private function user_on_stage_fullscreen(evt:FullScreenEvent):void {
|
||||
if (!evt.fullScreen) {
|
||||
__execute_user_set_dar();
|
||||
} else {
|
||||
__execute_user_enter_fullscreen();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* user event callback, js cannot enter the fullscreen mode, user must click to.
|
||||
*/
|
||||
private function user_on_click_video(evt:MouseEvent):void {
|
||||
if (!this.stage.allowsFullScreen) {
|
||||
trace("donot allow fullscreen.");
|
||||
return;
|
||||
}
|
||||
|
||||
// enter fullscreen to get the fullscreen size correctly.
|
||||
if (this.stage.displayState == StageDisplayState.FULL_SCREEN) {
|
||||
this.stage.displayState = StageDisplayState.NORMAL;
|
||||
} else {
|
||||
this.stage.displayState = StageDisplayState.FULL_SCREEN;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* function for js to call: to pause the stream. ignore if not play.
|
||||
*/
|
||||
private function js_call_pause():void {
|
||||
if (this.media_stream) {
|
||||
this.media_stream.pause();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* function for js to call: to resume the stream. ignore if not play.
|
||||
*/
|
||||
private function js_call_resume():void {
|
||||
if (this.media_stream) {
|
||||
this.media_stream.resume();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* to set the DAR, for example, DAR=16:9
|
||||
* @param num, for example, 9.
|
||||
* use metadata height if 0.
|
||||
* use user specified height if -1.
|
||||
* @param den, for example, 16.
|
||||
* use metadata width if 0.
|
||||
* use user specified width if -1.
|
||||
*/
|
||||
private function js_call_dar(num:int, den:int):void {
|
||||
user_dar_num = num;
|
||||
user_dar_den = den;
|
||||
|
||||
flash.utils.setTimeout(__execute_user_set_dar, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* set the fullscreen size data.
|
||||
* @refer the refer fullscreen mode. it can be:
|
||||
* video: use video orignal size.
|
||||
* screen: use screen size to rescale video.
|
||||
* @param percent, the rescale percent, where
|
||||
* 100 means 100%.
|
||||
*/
|
||||
private function js_call_set_fs_size(refer:String, percent:int):void {
|
||||
user_fs_refer = refer;
|
||||
user_fs_percent = percent;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the stream buffer time in seconds.
|
||||
* @buffer_time the buffer time in seconds.
|
||||
*/
|
||||
private function js_call_set_bt(buffer_time:Number):void {
|
||||
if (this.media_stream) {
|
||||
this.media_stream.bufferTime = buffer_time;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* function for js to call: to stop the stream. ignore if not play.
|
||||
*/
|
||||
private function js_call_stop():void {
|
||||
if (this.media_video) {
|
||||
this.removeChild(this.media_video);
|
||||
this.media_video = null;
|
||||
}
|
||||
if (this.media_stream) {
|
||||
this.media_stream.close();
|
||||
this.media_stream = null;
|
||||
}
|
||||
if (this.media_conn) {
|
||||
this.media_conn.close();
|
||||
this.media_conn = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* function for js to call: to play the stream. stop then play.
|
||||
* @param url, the rtmp/http url to play.
|
||||
* @param _width, the player width.
|
||||
* @param _height, the player height.
|
||||
* @param buffer_time, the buffer time in seconds. recommend to >=0.5
|
||||
*/
|
||||
private function js_call_play(url:String, _width:int, _height:int, buffer_time:Number):void {
|
||||
this.user_url = url;
|
||||
this.user_w = _width;
|
||||
this.user_h = _height;
|
||||
trace("start to play url: " + this.user_url + ", w=" + this.user_w + ", h=" + this.user_h);
|
||||
|
||||
js_call_stop();
|
||||
|
||||
this.media_conn = new NetConnection();
|
||||
this.media_conn.client = {};
|
||||
this.media_conn.client.onBWDone = function():void {};
|
||||
this.media_conn.addEventListener(NetStatusEvent.NET_STATUS, function(evt:NetStatusEvent):void {
|
||||
trace ("NetConnection: code=" + evt.info.code);
|
||||
|
||||
if (evt.info.hasOwnProperty("data") && evt.info.data) {
|
||||
// for context menu
|
||||
var customItems:Array = [new ContextMenuItem("SrsPlayer")];
|
||||
if (evt.info.data.hasOwnProperty("srs_server")) {
|
||||
customItems.push(new ContextMenuItem("Server: " + evt.info.data.srs_server));
|
||||
}
|
||||
if (evt.info.data.hasOwnProperty("srs_contributor")) {
|
||||
customItems.push(new ContextMenuItem("Contributor: " + evt.info.data.srs_contributor));
|
||||
}
|
||||
contextMenu.customItems = customItems;
|
||||
}
|
||||
|
||||
// TODO: FIXME: failed event.
|
||||
if (evt.info.code != "NetConnection.Connect.Success") {
|
||||
return;
|
||||
}
|
||||
|
||||
media_stream = new NetStream(media_conn);
|
||||
media_stream.bufferTime = buffer_time;
|
||||
media_stream.client = {};
|
||||
media_stream.client.onMetaData = system_on_metadata;
|
||||
media_stream.addEventListener(NetStatusEvent.NET_STATUS, function(evt:NetStatusEvent):void {
|
||||
trace ("NetStream: code=" + evt.info.code);
|
||||
|
||||
if (evt.info.code == "NetStream.Video.DimensionChange") {
|
||||
system_on_metadata(media_metadata);
|
||||
}
|
||||
|
||||
// TODO: FIXME: failed event.
|
||||
});
|
||||
|
||||
if (url.indexOf("http") == 0) {
|
||||
media_stream.play(url);
|
||||
} else {
|
||||
var streamName:String = url.substr(url.lastIndexOf("/"));
|
||||
media_stream.play(streamName);
|
||||
}
|
||||
|
||||
media_video = new Video();
|
||||
media_video.width = _width;
|
||||
media_video.height = _height;
|
||||
media_video.attachNetStream(media_stream);
|
||||
media_video.smoothing = true;
|
||||
addChild(media_video);
|
||||
|
||||
__draw_black_background(_width, _height);
|
||||
|
||||
// lowest layer, for mask to cover it.
|
||||
setChildIndex(media_video, 0);
|
||||
});
|
||||
|
||||
if (url.indexOf("http") == 0) {
|
||||
this.media_conn.connect(null);
|
||||
} else {
|
||||
var tcUrl:String = this.user_url.substr(0, this.user_url.lastIndexOf("/"));
|
||||
this.media_conn.connect(tcUrl);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get the "right" size of video,
|
||||
* 1. initialize with the original video object size.
|
||||
* 2. override with metadata size if specified.
|
||||
* 3. override with codec size if specified.
|
||||
*/
|
||||
private function __get_video_size_object():Object {
|
||||
var obj:Object = {
|
||||
width: media_video.width,
|
||||
height: media_video.height
|
||||
};
|
||||
|
||||
// override with metadata size.
|
||||
if (this.media_metadata.hasOwnProperty("width")) {
|
||||
obj.width = this.media_metadata.width;
|
||||
}
|
||||
if (this.media_metadata.hasOwnProperty("height")) {
|
||||
obj.height = this.media_metadata.height;
|
||||
}
|
||||
|
||||
// override with codec size.
|
||||
if (media_video.videoWidth > 0) {
|
||||
obj.width = media_video.videoWidth;
|
||||
}
|
||||
if (media_video.videoHeight > 0) {
|
||||
obj.height = media_video.videoHeight;
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* execute the enter fullscreen action.
|
||||
*/
|
||||
private function __execute_user_enter_fullscreen():void {
|
||||
if (!user_fs_refer || user_fs_percent <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// change to video size if refer to video.
|
||||
var obj:Object = __get_video_size_object();
|
||||
|
||||
// get the DAR
|
||||
var num:int = user_dar_num;
|
||||
var den:int = user_dar_den;
|
||||
|
||||
if (num == 0) {
|
||||
num = obj.height;
|
||||
}
|
||||
if (num == -1) {
|
||||
num = this.stage.fullScreenHeight;
|
||||
}
|
||||
|
||||
if (den == 0) {
|
||||
den = obj.width;
|
||||
}
|
||||
if (den == -1) {
|
||||
den = this.stage.fullScreenWidth;
|
||||
}
|
||||
|
||||
// for refer is screen.
|
||||
if (user_fs_refer == "screen") {
|
||||
obj = {
|
||||
width: this.stage.fullScreenWidth,
|
||||
height: this.stage.fullScreenHeight
|
||||
};
|
||||
}
|
||||
|
||||
// rescale to fs
|
||||
__update_video_size(num, den, obj.width * user_fs_percent / 100, obj.height * user_fs_percent / 100, this.stage.fullScreenWidth, this.stage.fullScreenHeight);
|
||||
}
|
||||
|
||||
/**
|
||||
* for user set dar, or leave fullscreen to recover the dar.
|
||||
*/
|
||||
private function __execute_user_set_dar():void {
|
||||
// get the DAR
|
||||
var num:int = user_dar_num;
|
||||
var den:int = user_dar_den;
|
||||
|
||||
var obj:Object = __get_video_size_object();
|
||||
|
||||
if (num == 0) {
|
||||
num = obj.height;
|
||||
}
|
||||
if (num == -1) {
|
||||
num = this.user_h;
|
||||
}
|
||||
|
||||
if (den == 0) {
|
||||
den = obj.width;
|
||||
}
|
||||
if (den == -1) {
|
||||
den = this.user_w;
|
||||
}
|
||||
|
||||
__update_video_size(num, den, this.user_w, this.user_h, this.user_w, this.user_h);
|
||||
}
|
||||
|
||||
/**
|
||||
* update the video width and height,
|
||||
* according to the specifies DAR(den:num) and max size(w:h).
|
||||
* set the position of video(x,y) specifies by size(sw:sh),
|
||||
* and update the bg to size(sw:sh).
|
||||
* @param _num/_den the DAR. use to rescale the player together with paper size.
|
||||
* @param _w/_h the video draw paper size. used to rescale the player together with DAR.
|
||||
* @param _sw/_wh the stage size, >= paper size. used to center the player.
|
||||
*/
|
||||
private function __update_video_size(_num:int, _den:int, _w:int, _h:int, _sw:int, _sh:int):void {
|
||||
if (!this.media_video || _num <= 0 || _den <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// set DAR.
|
||||
// calc the height by DAR
|
||||
var _height:int = _w * _num / _den;
|
||||
if (_height <= _h) {
|
||||
this.media_video.width = _w;
|
||||
this.media_video.height = _height;
|
||||
} else {
|
||||
// height overflow, calc the width by DAR
|
||||
var _width:int = _h * _den / _num;
|
||||
|
||||
this.media_video.width = _width;
|
||||
this.media_video.height = _h;
|
||||
}
|
||||
|
||||
// align center.
|
||||
this.media_video.x = (_sw - this.media_video.width) / 2;
|
||||
this.media_video.y = (_sh - this.media_video.height) / 2;
|
||||
|
||||
__draw_black_background(_sw, _sh);
|
||||
}
|
||||
|
||||
/**
|
||||
* draw black background and draw the fullscreen mask.
|
||||
*/
|
||||
private function __draw_black_background(_width:int, _height:int):void {
|
||||
// draw black bg.
|
||||
this.graphics.beginFill(0x00, 1.0);
|
||||
this.graphics.drawRect(0, 0, _width, _height);
|
||||
this.graphics.endFill();
|
||||
|
||||
// draw the fs mask.
|
||||
this.control_fs_mask.graphics.beginFill(0xff0000, 0);
|
||||
this.control_fs_mask.graphics.drawRect(0, 0, _width, _height);
|
||||
this.control_fs_mask.graphics.endFill();
|
||||
}
|
||||
}
|
||||
}
|
485
trunk/research/players/srs_publisher.html
Executable file
485
trunk/research/players/srs_publisher.html
Executable file
|
@ -0,0 +1,485 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>SRS</title>
|
||||
<meta charset="utf-8">
|
||||
<link rel="stylesheet" type="text/css" href="css/bootstrap.min.css"/>
|
||||
<script type="text/javascript" src="js/jquery-1.10.2.min.js"></script>
|
||||
<script type="text/javascript" src="js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="js/swfobject.js"></script>
|
||||
<script type="text/javascript" src="js/srs.js"></script>
|
||||
<style>
|
||||
body{
|
||||
padding-top: 55px;
|
||||
}
|
||||
</style>
|
||||
<script type="text/javascript">
|
||||
var srs_publisher = null;
|
||||
var remote_player = null;
|
||||
var realtime_player = null;
|
||||
|
||||
$(function(){
|
||||
// get the vhost and port to set the default url.
|
||||
// for example: http://192.168.1.213/players/jwplayer6.html?port=1935&vhost=demo
|
||||
// url set to: rtmp://demo:1935/live/livestream
|
||||
srs_init("#txt_url", null, null);
|
||||
|
||||
$("#btn_video_settings").click(function(){
|
||||
$("#video_modal").modal({show:true});
|
||||
});
|
||||
$("#btn_audio_settings").click(function(){
|
||||
$("#audio_modal").modal({show:true});
|
||||
});
|
||||
|
||||
$("#remote_tips").tooltip({
|
||||
title: "为了支持HLS输出,FLASH编码器输出的流需要经过转码(VP6=>H264,MP3=>aac),所以会黑屏较长时间,请耐心等待"
|
||||
});
|
||||
$("#low_latecy_tips").tooltip({
|
||||
title: "服务器不转码直接转发FLASH编码器的流,所以延迟比支持HLS的流要低很多"
|
||||
});
|
||||
|
||||
$("#btn_publish").click(on_user_publish);
|
||||
|
||||
// for publish, we use randome stream name.
|
||||
$("#txt_url").val($("#txt_url").val() + "." + new Date().getTime());
|
||||
|
||||
// start the publisher.
|
||||
srs_publisher = new SrsPublisher("local_publisher", 430, 185);
|
||||
srs_publisher.on_publisher_ready = function(cameras, microphones) {
|
||||
$("#sl_cameras").empty();
|
||||
for (var i = 0; i < cameras.length; i++) {
|
||||
$("#sl_cameras").append("<option value='" + i + "'>" + cameras[i] + "</option");
|
||||
}
|
||||
// optional: select the first no "virtual" signed.
|
||||
for (var i = 0; i < cameras.length; i++) {
|
||||
if (cameras[i].toLowerCase().indexOf("virtual") == -1) {
|
||||
$("#sl_cameras option[value='" + i + "']").attr("selected", true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$("#sl_microphones").empty();
|
||||
for (var i = 0; i < microphones.length; i++) {
|
||||
$("#sl_microphones").append("<option value='" + i + "'>" + microphones[i] + "</option");
|
||||
}
|
||||
|
||||
$("#sl_vcodec").empty();
|
||||
var vcodecs = ["h264", "vp6"];
|
||||
for (var i = 0; i < vcodecs.length; i++) {
|
||||
$("#sl_vcodec").append("<option value='" + vcodecs[i] + "'>" + vcodecs[i] + "</option");
|
||||
}
|
||||
|
||||
$("#sl_profile").empty();
|
||||
var profiles = ["baseline", "main"];
|
||||
for (var i = 0; i < profiles.length; i++) {
|
||||
$("#sl_profile").append("<option value='" + profiles[i] + "'>" + profiles[i] + "</option");
|
||||
}
|
||||
|
||||
$("#sl_level").empty();
|
||||
var levels = ["1", "1b", "1.1", "1.2", "1.3",
|
||||
"2", "2.1", "2.2", "3", "3.1", "3.2", "4", "4.1", "4.2", "5", "5.1"];
|
||||
for (var i = 0; i < levels.length; i++) {
|
||||
$("#sl_level").append("<option value='" + levels[i] + "'>" + levels[i] + "</option");
|
||||
}
|
||||
$("#sl_level option[value='4.1']").attr("selected", true);
|
||||
|
||||
$("#sl_gop").empty();
|
||||
var gops = ["0.3", "0.5", "1", "2", "3", "4",
|
||||
"5", "6", "7", "8", "9", "10", "15", "20"];
|
||||
for (var i = 0; i < gops.length; i++) {
|
||||
$("#sl_gop").append("<option value='" + gops[i] + "'>" + gops[i] + "秒</option");
|
||||
}
|
||||
$("#sl_gop option[value='5']").attr("selected", true);
|
||||
|
||||
$("#sl_size").empty();
|
||||
var sizes = ["176x144", "320x240", "352x240",
|
||||
"352x288", "460x240", "640x480", "720x480", "720x576", "800x600",
|
||||
"1024x768", "1280x720", "1360x768", "1920x1080"];
|
||||
for (i = 0; i < sizes.length; i++) {
|
||||
$("#sl_size").append("<option value='" + sizes[i] + "'>" + sizes[i] + "</option");
|
||||
}
|
||||
$("#sl_size option[value='460x240']").attr("selected", true);
|
||||
|
||||
$("#sl_fps").empty();
|
||||
var fpses = ["5", "10", "15", "20", "24", "25", "29.97", "30"];
|
||||
for (i = 0; i < fpses.length; i++) {
|
||||
$("#sl_fps").append("<option value='" + fpses[i] + "'>" + Number(fpses[i]).toFixed(2) + " 帧/秒</option");
|
||||
}
|
||||
$("#sl_fps option[value='15']").attr("selected", true);
|
||||
|
||||
$("#sl_bitrate").empty();
|
||||
var bitrates = ["50", "200", "350", "500", "650", "800",
|
||||
"950", "1000", "1200", "1500", "1800", "2000", "3000", "5000"];
|
||||
for (i = 0; i < bitrates.length; i++) {
|
||||
$("#sl_bitrate").append("<option value='" + bitrates[i] + "'>" + bitrates[i] + " kbps</option");
|
||||
}
|
||||
$("#sl_bitrate option[value='350']").attr("selected", true);
|
||||
};
|
||||
srs_publisher.on_publisher_error = function(code, desc) {
|
||||
error(code, desc);
|
||||
};
|
||||
srs_publisher.on_publisher_warn = function(code, desc) {
|
||||
warn(code, desc);
|
||||
};
|
||||
srs_publisher.start();
|
||||
|
||||
// if no play specified, donot show the player, for debug the publisher.
|
||||
var query = parse_query_string();
|
||||
if (query.no_play != "true") {
|
||||
// start the normal player with HLS supported.
|
||||
remote_player = new SrsPlayer("remote_player", 430, 185);
|
||||
remote_player.on_player_ready = function() {
|
||||
remote_player.set_bt(0.8);
|
||||
remote_player.set_fs("screen", 100);
|
||||
};
|
||||
remote_player.start();
|
||||
|
||||
// start the realtime player.
|
||||
realtime_player = new SrsPlayer("realtime_player", 430, 185);
|
||||
realtime_player.on_player_ready = function() {
|
||||
realtime_player.set_bt(0.8);
|
||||
realtime_player.set_fs("screen", 100);
|
||||
};
|
||||
realtime_player.start();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* we generate the transcoded stream url for flash publish donot support HLS
|
||||
* which requires aac, so the publish vhost maybe players for example, we
|
||||
* use players_pub vhost(transcoded stream to which) for all clients,
|
||||
* both players and players_pub are write HLS to the sample dir,
|
||||
* it's ok for the players vhost disabled the HLS, only the
|
||||
* players_pub enalbed HLS.
|
||||
*/
|
||||
function update_play_url() {
|
||||
var url = $("#txt_url").val();
|
||||
var ret = srs_parse_rtmp_url(url);
|
||||
var query = parse_query_string();
|
||||
|
||||
var srs_player_url = "http://" + query.host + query.dir + "/srs_player.html?";
|
||||
srs_player_url += "vhost=" + srs_get_player_publish_vhost(ret.vhost) + "&port=" + ret.port + "&app=" + ret.app + "&stream=" + ret.stream;
|
||||
srs_player_url += "&autostart=true";
|
||||
|
||||
var srs_player_rt_url = "http://" + query.host + query.dir + "/srs_player.html?";
|
||||
srs_player_rt_url += "vhost=" + ret.vhost + "&port=" + ret.port + "&app=" + ret.app + "&stream=" + ret.stream;
|
||||
srs_player_rt_url += "&autostart=true";
|
||||
|
||||
var jwplayer_url = "http://" + query.host + query.dir + "/jwplayer6.html?";
|
||||
jwplayer_url += "vhost=" + srs_get_player_publish_vhost(ret.vhost) + "&port=" + ret.port + "&app=" + ret.app + "&stream=" + ret.stream;
|
||||
jwplayer_url += "&hls_autostart=true";
|
||||
|
||||
var hls_url = "http://" + ret.server + ":" + query.http_port + "/" + ret.app + "/" + ret.stream + ".m3u8";
|
||||
|
||||
$("#txt_play_realtime").text("RTMP低延时(点击打开)").attr("href", srs_player_rt_url).attr("target", "_blank");
|
||||
$("#txt_play_url").text("RTMP已转码(点击打开)").attr("href", srs_player_url).attr("target", "_blank");
|
||||
$("#txt_play_hls").text("HLS-m3u8(点击打开或右键复制)").attr("href", hls_url).attr("target", "_blank");
|
||||
$("#txt_play_jwplayer").text("HLS-JWPlayer(点击打开)").attr("href", jwplayer_url).attr("target", "_blank");
|
||||
}
|
||||
function on_user_publish() {
|
||||
if ($("#btn_publish").text() == "停止发布") {
|
||||
srs_publisher.stop();
|
||||
$("#btn_publish").text("发布视频");
|
||||
$("#txt_play_realtime").text("RTMP低延时(请发布视频)").attr("href", "#").attr("target", "_self");
|
||||
$("#txt_play_url").text("RTMP已转码(请发布视频)").attr("href", "#").attr("target", "_self");
|
||||
$("#txt_play_hls").text("HLS-m3u8(请发布视频)").attr("href", "#").attr("target", "_self");
|
||||
$("#txt_play_jwplayer").text("HLS-JWPlayer(请发布视频)").attr("href", "#").attr("target", "_self");
|
||||
return;
|
||||
}
|
||||
|
||||
$("#btn_publish").text("停止发布");
|
||||
|
||||
update_play_url();
|
||||
|
||||
var url = $("#txt_url").val();
|
||||
var vcodec = {};
|
||||
var acodec = {};
|
||||
|
||||
acodec.device_code = $("#sl_microphones").val();
|
||||
acodec.device_name = $("#sl_microphones").text();
|
||||
|
||||
vcodec.device_code = $("#sl_cameras").find("option:selected").val();
|
||||
vcodec.device_name = $("#sl_cameras").find("option:selected").text();
|
||||
|
||||
vcodec.codec = $("#sl_vcodec").find("option:selected").val();
|
||||
vcodec.profile = $("#sl_profile").find("option:selected").val();
|
||||
vcodec.level = $("#sl_level").find("option:selected").val();
|
||||
vcodec.fps = $("#sl_fps").find("option:selected").val();
|
||||
vcodec.gop = $("#sl_gop").find("option:selected").val();
|
||||
vcodec.size = $("#sl_size").find("option:selected").val();
|
||||
vcodec.bitrate = $("#sl_bitrate").find("option:selected").val();
|
||||
|
||||
info("开始推流到服务器");
|
||||
srs_publisher.publish(url, vcodec, acodec);
|
||||
|
||||
if (realtime_player) {
|
||||
// directly play the url for the realtime player.
|
||||
realtime_player.stop();
|
||||
realtime_player.play(url);
|
||||
}
|
||||
|
||||
if (remote_player) {
|
||||
// the normal player should play the transcoded stream in another vhost.
|
||||
// for example, publish stream to vhost players,
|
||||
// the realtime player play the vhost players, which may donot support HLS,
|
||||
// the normal player play the vhost players_pub, which transcoded to h264/aac with HLS.
|
||||
var ret = srs_parse_rtmp_url(url);
|
||||
var pub_url = "rtmp://" + ret.server + ":" + ret.port + "/" + ret.app;
|
||||
pub_url += "?vhost=" + srs_get_player_publish_vhost(ret.vhost) + "/" + ret.stream;
|
||||
remote_player.stop();
|
||||
remote_player.play(pub_url);
|
||||
}
|
||||
}
|
||||
|
||||
function info(desc) {
|
||||
$("#txt_log").addClass("alert-info").removeClass("alert-error").removeClass("alert-warn");
|
||||
$("#txt_log_title").text("Info:");
|
||||
$("#txt_log_msg").text(desc);
|
||||
}
|
||||
function warn(code, desc) {
|
||||
$("#txt_log").removeClass("alert-info").removeClass("alert-error").addClass("alert-warn");
|
||||
$("#txt_log_title").text("Warn:");
|
||||
$("#txt_log_msg").text("code: " + code + ", " + desc);
|
||||
}
|
||||
function error(code, desc) {
|
||||
$("#txt_log").removeClass("alert-info").addClass("alert-error").removeClass("alert-warn");
|
||||
$("#txt_log_title").text("Error:");
|
||||
$("#txt_log_msg").text("code: " + code + ", " + desc);
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="navbar navbar-fixed-top">
|
||||
<div class="navbar-inner">
|
||||
<div class="container">
|
||||
<a class="brand" href="index.html">SRS</a>
|
||||
<div class="nav-collapse collapse">
|
||||
<ul class="nav">
|
||||
<li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li>
|
||||
<li class="active"><a id="nav_srs_publisher" href="srs_publisher.html">SRS编码器</a></li>
|
||||
<li><a id="nav_srs_bwt" href="srs_bwt.html">SRS测网速</a></li>
|
||||
<li><a id="nav_jwplayer6" href="jwplayer6.html">JWPlayer6播放器</a></li>
|
||||
<li><a id="nav_osmf" href="osmf.html">AdobeOSMF播放器</a></li>
|
||||
<li><a id="nav_vlc" href="vlc.html">VLC播放器</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="alert alert-info fade in" id="txt_log">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
<strong><span id="txt_log_title">Usage:</span></strong>
|
||||
<span id="txt_log_msg">设置编码参数,点“发布视频”,允许Flash访问摄像头即可推流</span>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<div class="form-inline">
|
||||
<button class="btn" id="btn_video_settings">视频编码配置</button>
|
||||
<button class="btn" id="btn_audio_settings">音频编码配置</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<div class="form-inline">
|
||||
发布地址:
|
||||
<input type="text" id="txt_url" class="input-xxlarge" value=""></input>
|
||||
<button class="btn btn-primary" id="btn_publish">发布视频</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<div class="form-inline">
|
||||
播放地址
|
||||
1.<a id="txt_play_realtime" class="input-xxlarge" href="#">RTMP低延时(请发布视频)</a>
|
||||
2.<a id="txt_play_url" class="input-xxlarge" href="#">RTMP已转码(请发布视频)</a>
|
||||
3.<a id="txt_play_hls" class="input-xxlarge" href="#">HLS-m3u8(请发布视频)</a>
|
||||
4.<a id="txt_play_jwplayer" class="input-xxlarge" href="#">HLS-JWPlayer(请发布视频)</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="video_modal" class="modal hide fade">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3>视频编码</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-horizontal">
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="sl_cameras">
|
||||
摄像头
|
||||
<a id="sl_cameras_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
|
||||
<img src="img/tooltip.png"/>
|
||||
</a>
|
||||
</label>
|
||||
<div class="controls">
|
||||
<select class="span4" id="sl_cameras"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="sl_vcodec">
|
||||
Codec
|
||||
<a id="sl_cameras_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
|
||||
<img src="img/tooltip.png"/>
|
||||
</a>
|
||||
</label>
|
||||
<div class="controls">
|
||||
<select class="span2" id="sl_vcodec"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="sl_profile">
|
||||
Profile
|
||||
<a id="sl_profile_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
|
||||
<img src="img/tooltip.png"/>
|
||||
</a>
|
||||
</label>
|
||||
<div class="controls">
|
||||
<select class="span2" id="sl_profile"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="sl_level">
|
||||
Level
|
||||
<a id="sl_level_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
|
||||
<img src="img/tooltip.png"/>
|
||||
</a>
|
||||
</label>
|
||||
<div class="controls">
|
||||
<select class="span2" id="sl_level"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="sl_gop">
|
||||
GOP
|
||||
<a id="sl_gop_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
|
||||
<img src="img/tooltip.png"/>
|
||||
</a>
|
||||
</label>
|
||||
<div class="controls">
|
||||
<select class="span2" id="sl_gop"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="sl_size">
|
||||
尺寸
|
||||
<a id="sl_size_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
|
||||
<img src="img/tooltip.png"/>
|
||||
</a>
|
||||
</label>
|
||||
<div class="controls">
|
||||
<select class="span2" id="sl_size"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="sl_fps">
|
||||
帧率
|
||||
<a id="sl_fps_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
|
||||
<img src="img/tooltip.png"/>
|
||||
</a>
|
||||
</label>
|
||||
<div class="controls">
|
||||
<select class="span2" id="sl_fps"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="sl_bitrate">
|
||||
码率
|
||||
<a id="sl_bitrate_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
|
||||
<img src="img/tooltip.png"/>
|
||||
</a>
|
||||
</label>
|
||||
<div class="controls">
|
||||
<select class="span2" id="sl_bitrate"></select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-primary" data-dismiss="modal" aria-hidden="true">设置</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="audio_modal" class="modal hide fade">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3>音频编码</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-horizontal">
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="sl_microphones">
|
||||
麦克风
|
||||
<a id="worker_id_tips" href="#" data-toggle="tooltip" data-placement="right" title="">
|
||||
<img src="img/tooltip.png"/>
|
||||
</a>
|
||||
</label>
|
||||
<div class="controls">
|
||||
<select class="span4" id="sl_microphones"></select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-primary" data-dismiss="modal" aria-hidden="true">设置</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="row-fluid">
|
||||
<div class="span6">
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<span class="accordion-toggle" data-toggle="collapse" href="#collapse1">
|
||||
<strong>本地摄像头</strong>
|
||||
</span>
|
||||
</div>
|
||||
<div id="collapse1" class="accordion-body collapse in">
|
||||
<div class="accordion-inner">
|
||||
<div id="local_publisher"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="span6">
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<span class="accordion-toggle" data-toggle="collapse" href="#collapse2">
|
||||
<strong>远程服务器</strong>
|
||||
<a id="remote_tips" href="#" data-toggle="tooltip" data-placement="top" title="">
|
||||
黑屏<img src="img/tooltip.png"/>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
<div id="collapse2" class="accordion-body collapse in">
|
||||
<div class="accordion-inner">
|
||||
<div id="remote_player"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="row-fluid">
|
||||
<div class="span6">
|
||||
<div class="accordion-group">
|
||||
<div class="accordion-heading">
|
||||
<span class="accordion-toggle" data-toggle="collapse" href="#collapse3">
|
||||
<strong>远程服务器</strong>
|
||||
<a id="low_latecy_tips" href="#" data-toggle="tooltip" data-placement="top" title="">
|
||||
低延时<img src="img/tooltip.png"/>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
<div id="collapse3" class="accordion-body collapse in">
|
||||
<div class="accordion-inner">
|
||||
<div id="realtime_player"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="span6">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>
|
||||
<p><a href="https://github.com/winlinvip/simple-rtmp-server">SRS Team © 2013</a></p>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
|
40
trunk/research/players/srs_publisher/.actionScriptProperties
Executable file
40
trunk/research/players/srs_publisher/.actionScriptProperties
Executable file
|
@ -0,0 +1,40 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<actionScriptProperties analytics="false" mainApplicationPath="srs_publisher.as" projectUUID="f002791f-ee2e-41b7-aa3a-a7af6c65aa58" version="10">
|
||||
<compiler additionalCompilerArguments="-locale en_US" autoRSLOrdering="true" copyDependentFiles="false" fteInMXComponents="false" generateAccessible="true" htmlExpressInstall="true" htmlGenerate="false" htmlHistoryManagement="true" htmlPlayerVersionCheck="true" includeNetmonSwc="false" outputFolderPath="release" removeUnusedRSL="true" sourceFolderPath="src" strict="true" targetPlayerVersion="0.0.0" useApolloConfig="false" useDebugRSLSwfs="true" verifyDigests="true" warn="true">
|
||||
<compilerSourcePath/>
|
||||
<libraryPath defaultLinkType="0">
|
||||
<libraryPathEntry kind="4" path="">
|
||||
<excludedEntries>
|
||||
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/automation_charts.swc" useDefaultLinkType="false"/>
|
||||
<libraryPathEntry kind="1" linkType="1" path="${PROJECT_FRAMEWORKS}/locale/{locale}"/>
|
||||
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/advancedgrids.swc" useDefaultLinkType="false"/>
|
||||
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/qtp.swc" useDefaultLinkType="false"/>
|
||||
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/automation_air.swc" useDefaultLinkType="false"/>
|
||||
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/charts.swc" useDefaultLinkType="false"/>
|
||||
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/framework.swc" useDefaultLinkType="false"/>
|
||||
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/mx/mx.swc" useDefaultLinkType="false"/>
|
||||
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/netmon.swc" useDefaultLinkType="false"/>
|
||||
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/spark.swc" useDefaultLinkType="false"/>
|
||||
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/sparkskins.swc" useDefaultLinkType="false"/>
|
||||
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/rpc.swc" useDefaultLinkType="false"/>
|
||||
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/videoPlayer.swc" useDefaultLinkType="false"/>
|
||||
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/qtp_air.swc" useDefaultLinkType="false"/>
|
||||
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/datavisualization.swc" useDefaultLinkType="false"/>
|
||||
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/spark_dmv.swc" useDefaultLinkType="false"/>
|
||||
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/automation.swc" useDefaultLinkType="false"/>
|
||||
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/flash-integration.swc" useDefaultLinkType="false"/>
|
||||
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/automation_dmv.swc" useDefaultLinkType="false"/>
|
||||
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/automation_flashflexkit.swc" useDefaultLinkType="false"/>
|
||||
<libraryPathEntry kind="3" linkType="1" path="${PROJECT_FRAMEWORKS}/libs/automation_agent.swc" useDefaultLinkType="false"/>
|
||||
</excludedEntries>
|
||||
</libraryPathEntry>
|
||||
</libraryPath>
|
||||
<sourceAttachmentPath/>
|
||||
</compiler>
|
||||
<applications>
|
||||
<application path="srs_publisher.as"/>
|
||||
</applications>
|
||||
<modules/>
|
||||
<buildCSSFiles/>
|
||||
<flashCatalyst validateFlashCatalystCompatibility="false"/>
|
||||
</actionScriptProperties>
|
17
trunk/research/players/srs_publisher/.project
Executable file
17
trunk/research/players/srs_publisher/.project
Executable file
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>srs_publisher</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>com.adobe.flexbuilder.project.flexbuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>com.adobe.flexbuilder.project.actionscriptnature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
|
@ -0,0 +1,3 @@
|
|||
#Wed Dec 18 10:10:45 CST 2013
|
||||
eclipse.preferences.version=1
|
||||
encoding/<project>=utf-8
|
BIN
trunk/research/players/srs_publisher/release/srs_publisher.swf
Executable file
BIN
trunk/research/players/srs_publisher/release/srs_publisher.swf
Executable file
Binary file not shown.
340
trunk/research/players/srs_publisher/src/srs_publisher.as
Executable file
340
trunk/research/players/srs_publisher/src/srs_publisher.as
Executable file
|
@ -0,0 +1,340 @@
|
|||
package
|
||||
{
|
||||
import flash.display.Sprite;
|
||||
import flash.display.StageAlign;
|
||||
import flash.display.StageScaleMode;
|
||||
import flash.events.Event;
|
||||
import flash.events.NetStatusEvent;
|
||||
import flash.external.ExternalInterface;
|
||||
import flash.media.Camera;
|
||||
import flash.media.H264Profile;
|
||||
import flash.media.H264VideoStreamSettings;
|
||||
import flash.media.Microphone;
|
||||
import flash.media.Video;
|
||||
import flash.net.NetConnection;
|
||||
import flash.net.NetStream;
|
||||
import flash.ui.ContextMenu;
|
||||
import flash.ui.ContextMenuItem;
|
||||
import flash.utils.setTimeout;
|
||||
|
||||
public class srs_publisher extends Sprite
|
||||
{
|
||||
// user set id.
|
||||
private var js_id:String = null;
|
||||
// user set callback
|
||||
private var js_on_publisher_ready:String = null;
|
||||
private var js_on_publisher_error:String = null;
|
||||
private var js_on_publisher_warn:String = null;
|
||||
|
||||
// publish param url.
|
||||
private var user_url:String = null;
|
||||
// play param, user set width and height
|
||||
private var user_w:int = 0;
|
||||
private var user_h:int = 0;
|
||||
private var user_vcodec:Object = {};
|
||||
private var user_acodec:Object = {};
|
||||
|
||||
// media specified.
|
||||
private var media_conn:NetConnection = null;
|
||||
private var media_stream:NetStream = null;
|
||||
private var media_video:Video = null;
|
||||
private var media_camera:Camera = null;
|
||||
private var media_microphone:Microphone = null;
|
||||
|
||||
// error code.
|
||||
private const error_camera_get:int = 100;
|
||||
private const error_microphone_get:int = 101;
|
||||
private const error_camera_muted:int = 102;
|
||||
|
||||
public function srs_publisher()
|
||||
{
|
||||
if (!this.stage) {
|
||||
this.addEventListener(Event.ADDED_TO_STAGE, this.system_on_add_to_stage);
|
||||
} else {
|
||||
this.system_on_add_to_stage(null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* system event callback, when this control added to stage.
|
||||
* the main function.
|
||||
*/
|
||||
private function system_on_add_to_stage(evt:Event):void {
|
||||
this.removeEventListener(Event.ADDED_TO_STAGE, this.system_on_add_to_stage);
|
||||
|
||||
this.stage.align = StageAlign.TOP_LEFT;
|
||||
this.stage.scaleMode = StageScaleMode.NO_SCALE;
|
||||
|
||||
this.contextMenu = new ContextMenu();
|
||||
this.contextMenu.hideBuiltInItems();
|
||||
|
||||
var flashvars:Object = this.root.loaderInfo.parameters;
|
||||
|
||||
if (!flashvars.hasOwnProperty("id")) {
|
||||
throw new Error("must specifies the id");
|
||||
}
|
||||
|
||||
this.js_id = flashvars.id;
|
||||
this.js_on_publisher_ready = flashvars.on_publisher_ready;
|
||||
this.js_on_publisher_error = flashvars.on_publisher_error;
|
||||
this.js_on_publisher_warn = flashvars.on_publisher_warn;
|
||||
|
||||
flash.utils.setTimeout(this.system_on_js_ready, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* system callack event, when js ready, register callback for js.
|
||||
* the actual main function.
|
||||
*/
|
||||
private function system_on_js_ready():void {
|
||||
if (!flash.external.ExternalInterface.available) {
|
||||
trace("js not ready, try later.");
|
||||
flash.utils.setTimeout(this.system_on_js_ready, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
flash.external.ExternalInterface.addCallback("__publish", this.js_call_publish);
|
||||
flash.external.ExternalInterface.addCallback("__stop", this.js_call_stop);
|
||||
|
||||
var cameras:Array = Camera.names;
|
||||
var microphones:Array = Microphone.names;
|
||||
trace("retrieve system cameras(" + cameras + ") and microphones(" + microphones + ")");
|
||||
|
||||
flash.external.ExternalInterface.call(this.js_on_publisher_ready, this.js_id, cameras, microphones);
|
||||
}
|
||||
|
||||
/**
|
||||
* notify the js an error occur.
|
||||
*/
|
||||
private function system_error(code:int, desc:String):void {
|
||||
trace("system error, code=" + code + ", error=" + desc);
|
||||
flash.external.ExternalInterface.call(this.js_on_publisher_error, this.js_id, code);
|
||||
}
|
||||
private function system_warn(code:int, desc:String):void {
|
||||
trace("system warn, code=" + code + ", error=" + desc);
|
||||
flash.external.ExternalInterface.call(this.js_on_publisher_warn, this.js_id, code);
|
||||
}
|
||||
|
||||
/**
|
||||
* publish stream to server.
|
||||
* @param url a string indicates the rtmp url to publish.
|
||||
* @param _width, the player width.
|
||||
* @param _height, the player height.
|
||||
* @param vcodec an object contains the video codec info.
|
||||
* @param acodec an object contains the audio codec info.
|
||||
*/
|
||||
private function js_call_publish(url:String, _width:int, _height:int, vcodec:Object, acodec:Object):void {
|
||||
trace("start to publish to " + url + ", vcodec " + JSON.stringify(vcodec) + ", acodec " + JSON.stringify(acodec));
|
||||
|
||||
this.user_url = url;
|
||||
this.user_w = _width;
|
||||
this.user_h = _height;
|
||||
this.user_vcodec = vcodec;
|
||||
this.user_acodec = acodec;
|
||||
|
||||
this.js_call_stop();
|
||||
|
||||
// microphone and camera
|
||||
var m:Microphone = Microphone.getMicrophone(acodec.device_code);
|
||||
if(m == null){
|
||||
this.system_error(this.error_microphone_get, "failed to open microphone " + acodec.device_code + "(" + acodec.device_name + ")");
|
||||
return;
|
||||
}
|
||||
// ignore muted, for flash will require user to access it.
|
||||
|
||||
// Remark: the name is the index!
|
||||
var c:Camera = Camera.getCamera(vcodec.device_code);
|
||||
if(c == null){
|
||||
this.system_error(this.error_camera_get, "failed to open camera " + vcodec.device_code + "(" + vcodec.device_name + ")");
|
||||
return;
|
||||
}
|
||||
// ignore muted, for flash will require user to access it.
|
||||
// but we still warn user.
|
||||
if(c && c.muted){
|
||||
this.system_warn(this.error_camera_muted, "Access Denied, camera " + vcodec.device_code + "(" + vcodec.device_name + ") is muted");
|
||||
}
|
||||
|
||||
this.media_camera = c;
|
||||
this.media_microphone = m;
|
||||
|
||||
this.media_conn = new NetConnection();
|
||||
this.media_conn.client = {};
|
||||
this.media_conn.client.onBWDone = function():void {};
|
||||
this.media_conn.addEventListener(NetStatusEvent.NET_STATUS, function(evt:NetStatusEvent):void {
|
||||
trace ("NetConnection: code=" + evt.info.code);
|
||||
|
||||
if (evt.info.hasOwnProperty("data") && evt.info.data) {
|
||||
// for context menu
|
||||
var customItems:Array = [new ContextMenuItem("SrsPlayer")];
|
||||
if (evt.info.data.hasOwnProperty("srs_server")) {
|
||||
customItems.push(new ContextMenuItem("Server: " + evt.info.data.srs_server));
|
||||
}
|
||||
if (evt.info.data.hasOwnProperty("srs_contributor")) {
|
||||
customItems.push(new ContextMenuItem("Contributor: " + evt.info.data.srs_contributor));
|
||||
}
|
||||
contextMenu.customItems = customItems;
|
||||
}
|
||||
|
||||
// TODO: FIXME: failed event.
|
||||
if (evt.info.code != "NetConnection.Connect.Success") {
|
||||
return;
|
||||
}
|
||||
|
||||
media_stream = new NetStream(media_conn);
|
||||
media_stream.client = {};
|
||||
media_stream.addEventListener(NetStatusEvent.NET_STATUS, function(evt:NetStatusEvent):void {
|
||||
trace ("NetStream: code=" + evt.info.code);
|
||||
|
||||
// TODO: FIXME: failed event.
|
||||
});
|
||||
|
||||
__build_video_codec(media_stream, c, vcodec);
|
||||
__build_audio_codec(media_stream, m, acodec);
|
||||
|
||||
if (media_microphone) {
|
||||
media_stream.attachAudio(m);
|
||||
}
|
||||
if (media_camera) {
|
||||
media_stream.attachCamera(c);
|
||||
}
|
||||
|
||||
var streamName:String = url.substr(url.lastIndexOf("/"));
|
||||
media_stream.publish(streamName);
|
||||
|
||||
media_video = new Video();
|
||||
media_video.width = _width;
|
||||
media_video.height = _height;
|
||||
media_video.attachCamera(media_camera);
|
||||
media_video.smoothing = true;
|
||||
addChild(media_video);
|
||||
|
||||
//__draw_black_background(_width, _height);
|
||||
|
||||
// lowest layer, for mask to cover it.
|
||||
setChildIndex(media_video, 0);
|
||||
});
|
||||
|
||||
var tcUrl:String = this.user_url.substr(0, this.user_url.lastIndexOf("/"));
|
||||
this.media_conn.connect(tcUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* function for js to call: to stop the stream. ignore if not publish.
|
||||
*/
|
||||
private function js_call_stop():void {
|
||||
if (this.media_video) {
|
||||
this.removeChild(this.media_video);
|
||||
this.media_video = null;
|
||||
}
|
||||
if (this.media_stream) {
|
||||
this.media_stream.close();
|
||||
this.media_stream = null;
|
||||
}
|
||||
if (this.media_conn) {
|
||||
this.media_conn.close();
|
||||
this.media_conn = null;
|
||||
}
|
||||
}
|
||||
|
||||
private function __build_audio_codec(stream:NetStream, m:Microphone, acodec:Object):void {
|
||||
if (!m) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if no microphone, donot set the params.
|
||||
if(m == null){
|
||||
return;
|
||||
}
|
||||
|
||||
// use default values.
|
||||
var microEncodeQuality:int = 8;
|
||||
var microRate:int = 22; // 22 === 22050 Hz
|
||||
|
||||
trace("[Publish] audio encoding parameters: "
|
||||
+ "audio(microphone) encodeQuality=" + microEncodeQuality
|
||||
+ ", rate=" + microRate + "(22050Hz)"
|
||||
);
|
||||
|
||||
// The encoded speech quality when using the Speex codec. Possible values are from 0 to 10. The default value is 6. Higher numbers
|
||||
// represent higher quality but require more bandwidth, as shown in the following table. The bit rate values that are listed represent
|
||||
// net bit rates and do not include packetization overhead.
|
||||
m.encodeQuality = microEncodeQuality;
|
||||
|
||||
// The rate at which the microphone is capturing sound, in kHz. Acceptable values are 5, 8, 11, 22, and 44. The default value is 8 kHz
|
||||
// if your sound capture device supports this value. Otherwise, the default value is the next available capture level above 8 kHz that
|
||||
// your sound capture device supports, usually 11 kHz.
|
||||
m.rate = microRate;
|
||||
}
|
||||
private function __build_video_codec(stream:NetStream, c:Camera, vcodec:Object):void {
|
||||
if (!c) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(vcodec.codec == "vp6"){
|
||||
trace("use VP6, donot use H.264 publish encoding.");
|
||||
return;
|
||||
}
|
||||
|
||||
var x264profile:String = (vcodec.profile == "main") ? H264Profile.MAIN : H264Profile.BASELINE;
|
||||
var x264level:String = vcodec.level;
|
||||
var cameraFps:Number = Number(vcodec.fps);
|
||||
var x264KeyFrameInterval:int = int(vcodec.gop * cameraFps);
|
||||
var cameraWidth:int = String(vcodec.size).split("x")[0];
|
||||
var cameraHeight:int = String(vcodec.size).split("x")[1];
|
||||
var cameraBitrate:int = int(vcodec.bitrate);
|
||||
|
||||
// use default values.
|
||||
var cameraQuality:int = 85;
|
||||
|
||||
trace("[Publish] video h.264(x264) encoding parameters: "
|
||||
+ "profile=" + x264profile
|
||||
+ ", level=" + x264level
|
||||
+ ", keyFrameInterval(gop)=" + x264KeyFrameInterval
|
||||
+ "; video(camera) width=" + cameraWidth
|
||||
+ ", height=" + cameraHeight
|
||||
+ ", fps=" + cameraFps
|
||||
+ ", bitrate=" + cameraBitrate
|
||||
+ ", quality=" + cameraQuality
|
||||
);
|
||||
|
||||
var h264Settings:H264VideoStreamSettings = new H264VideoStreamSettings();
|
||||
// we MUST set its values first, then set the NetStream.videoStreamSettings, or it will keep the origin values.
|
||||
h264Settings.setProfileLevel(x264profile, x264level);
|
||||
stream.videoStreamSettings = h264Settings;
|
||||
// the setKeyFrameInterval/setMode/setQuality use the camera settings.
|
||||
// http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/media/VideoStreamSettings.html
|
||||
// Note This feature will be supported in future releases of Flash Player and AIR, for now, Camera parameters are used.
|
||||
//
|
||||
//h264Settings.setKeyFrameInterval(4);
|
||||
//h264Settings.setMode(800, 600, 15);
|
||||
//h264Settings.setQuality(500, 0);
|
||||
|
||||
// set the camera and microphone.
|
||||
|
||||
// setKeyFrameInterval(keyFrameInterval:int):void
|
||||
// keyFrameInterval:int — A value that specifies which video frames are transmitted in full (as keyframes) instead of being
|
||||
// interpolated by the video compression algorithm. A value of 1 means that every frame is a keyframe, a value of 3 means
|
||||
// that every third frame is a keyframe, and so on. Acceptable values are 1 through 48.
|
||||
c.setKeyFrameInterval(x264KeyFrameInterval);
|
||||
|
||||
// setMode(width:int, height:int, fps:Number, favorArea:Boolean = true):void
|
||||
// width:int — The requested capture width, in pixels. The default value is 160.
|
||||
// height:int — The requested capture height, in pixels. The default value is 120.
|
||||
// fps:Number — The requested rate at which the camera should capture data, in frames per second. The default value is 15.
|
||||
c.setMode(cameraWidth, cameraHeight, cameraFps);
|
||||
|
||||
// setQuality(bandwidth:int, quality:int):void
|
||||
// bandwidth:int — Specifies the maximum amount of bandwidth that the current outgoing video feed can use, in bytes per second.
|
||||
// To specify that the video can use as much bandwidth as needed to maintain the value of quality, pass 0 for bandwidth.
|
||||
// The default value is 16384.
|
||||
// quality:int — An integer that specifies the required level of picture quality, as determined by the amount of compression
|
||||
// being applied to each video frame. Acceptable values range from 1 (lowest quality, maximum compression) to 100
|
||||
// (highest quality, no compression). To specify that picture quality can vary as needed to avoid exceeding bandwidth,
|
||||
// pass 0 for quality.
|
||||
// winlin:
|
||||
// bandwidth is in bps not kbps. 500*1000 = 500kbps.
|
||||
// quality=1 is lowest quality, 100 is highest quality.
|
||||
c.setQuality(cameraBitrate * 1000, cameraQuality);
|
||||
}
|
||||
}
|
||||
}
|
51
trunk/research/players/vlc.html
Executable file
51
trunk/research/players/vlc.html
Executable file
|
@ -0,0 +1,51 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>SRS</title>
|
||||
<meta charset="utf-8">
|
||||
<link rel="stylesheet" type="text/css" href="css/bootstrap.min.css"/>
|
||||
<script type="text/javascript" src="js/jquery-1.10.2.min.js"></script>
|
||||
<script type="text/javascript" src="js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="js/swfobject.js"></script>
|
||||
<script type="text/javascript" src="js/srs.js"></script>
|
||||
<style>
|
||||
body{
|
||||
padding-top: 55px;
|
||||
}
|
||||
</style>
|
||||
<script type="text/javascript">
|
||||
$(function(){
|
||||
update_nav();
|
||||
$("#main_frame").attr("src", "http://www.videolan.org/vlc/");
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="navbar navbar-fixed-top">
|
||||
<div class="navbar-inner">
|
||||
<div class="container">
|
||||
<a class="brand" href="index.html">SRS</a>
|
||||
<div class="nav-collapse collapse">
|
||||
<ul class="nav">
|
||||
<li><a id="nav_srs_player" href="srs_player.html">SRS播放器</a></li>
|
||||
<li><a id="nav_srs_publisher" href="srs_publisher.html">SRS编码器</a></li>
|
||||
<li><a id="nav_srs_bwt" href="srs_bwt.html">SRS测网速</a></li>
|
||||
<li><a id="nav_jwplayer6" href="jwplayer6.html">JWPlayer6播放器</a></li>
|
||||
<li><a id="nav_osmf" href="osmf.html">AdobeOSMF播放器</a></li>
|
||||
<li class="active"><a id="nav_vlc" href="vlc.html">VLC播放器</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<iframe id="main_frame" width="100%" height="800" frameBorder="0"></iframe>
|
||||
</div>
|
||||
<div class="container">
|
||||
<hr>
|
||||
<footer>
|
||||
<p><a href="https://github.com/winlinvip/simple-rtmp-server">SRS Team © 2013</a></p>
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
|
|
@ -139,6 +139,8 @@ int SrsRequest::discovery_app()
|
|||
}
|
||||
}
|
||||
|
||||
strip();
|
||||
|
||||
// resolve the vhost from config
|
||||
SrsConfDirective* parsed_vhost = config->get_vhost(vhost);
|
||||
if (parsed_vhost) {
|
||||
|
@ -611,7 +613,7 @@ int SrsRtmp::response_connect_app(SrsRequest *req, const char *ip)
|
|||
pkt->info->set("data", data);
|
||||
|
||||
data->set("srs_version", new SrsAmf0String(RTMP_SIG_FMS_VER));
|
||||
data->set("srs_server", new SrsAmf0String(RTMP_SIG_SRS_NAME));
|
||||
data->set("srs_server", new SrsAmf0String(RTMP_SIG_SRS_KEY" "RTMP_SIG_SRS_VERSION" ("RTMP_SIG_SRS_URL_SHORT")"));
|
||||
data->set("srs_license", new SrsAmf0String(RTMP_SIG_SRS_LICENSE));
|
||||
data->set("srs_role", new SrsAmf0String(RTMP_SIG_SRS_ROLE));
|
||||
data->set("srs_url", new SrsAmf0String(RTMP_SIG_SRS_URL));
|
||||
|
@ -620,9 +622,12 @@ int SrsRtmp::response_connect_app(SrsRequest *req, const char *ip)
|
|||
data->set("srs_email", new SrsAmf0String(RTMP_SIG_SRS_EMAIL));
|
||||
data->set("srs_copyright", new SrsAmf0String(RTMP_SIG_SRS_COPYRIGHT));
|
||||
|
||||
if(ip)
|
||||
if (ip) {
|
||||
data->set("srs_server_ip", new SrsAmf0String(ip));
|
||||
|
||||
}
|
||||
|
||||
data->set("srs_contributor", new SrsAmf0String(RTMP_SIG_SRS_CONTRIBUTOR));
|
||||
|
||||
msg->set_packet(pkt, 0);
|
||||
|
||||
if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
|
||||
|
@ -1297,11 +1302,13 @@ int SrsRtmp::bandwidth_check_play(int duration_ms, int interval_ms, int &actual_
|
|||
SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket;
|
||||
pkt->command_name = SRS_BW_CHECK_PLAYING;
|
||||
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
int object_num = 1;
|
||||
for (int i = 0; i < object_num; ++i) {
|
||||
char buf[32];
|
||||
sprintf(buf, "%d", i);
|
||||
pkt->data->set(buf, new SrsAmf0String(random_data));
|
||||
}
|
||||
object_num += 1;
|
||||
msg->set_packet(pkt, 0);
|
||||
|
||||
play_bytes += pkt->get_payload_length();
|
||||
|
|
Loading…
Reference in a new issue